xref: /linux/drivers/mailbox/mailbox-th1520.c (revision b3cc7428a32202936904b5b07cf9f135025bafd6)
15d4d263eSMichal Wilczynski // SPDX-License-Identifier: GPL-2.0
25d4d263eSMichal Wilczynski /*
35d4d263eSMichal Wilczynski  * Copyright (C) 2021 Alibaba Group Holding Limited.
45d4d263eSMichal Wilczynski  */
55d4d263eSMichal Wilczynski 
65d4d263eSMichal Wilczynski #include <linux/clk.h>
75d4d263eSMichal Wilczynski #include <linux/interrupt.h>
85d4d263eSMichal Wilczynski #include <linux/io.h>
95d4d263eSMichal Wilczynski #include <linux/kernel.h>
105d4d263eSMichal Wilczynski #include <linux/mailbox_controller.h>
115d4d263eSMichal Wilczynski #include <linux/module.h>
125d4d263eSMichal Wilczynski #include <linux/of_device.h>
135d4d263eSMichal Wilczynski #include <linux/platform_device.h>
145d4d263eSMichal Wilczynski #include <linux/slab.h>
155d4d263eSMichal Wilczynski 
165d4d263eSMichal Wilczynski /* Status Register */
175d4d263eSMichal Wilczynski #define TH_1520_MBOX_STA 0x0
185d4d263eSMichal Wilczynski #define TH_1520_MBOX_CLR 0x4
195d4d263eSMichal Wilczynski #define TH_1520_MBOX_MASK 0xc
205d4d263eSMichal Wilczynski 
215d4d263eSMichal Wilczynski /* Transmit/receive data register:
225d4d263eSMichal Wilczynski  * INFO0 ~ INFO6
235d4d263eSMichal Wilczynski  */
245d4d263eSMichal Wilczynski #define TH_1520_MBOX_INFO_NUM 8
255d4d263eSMichal Wilczynski #define TH_1520_MBOX_DATA_INFO_NUM 7
265d4d263eSMichal Wilczynski #define TH_1520_MBOX_INFO0 0x14
275d4d263eSMichal Wilczynski /* Transmit ack register: INFO7 */
285d4d263eSMichal Wilczynski #define TH_1520_MBOX_INFO7 0x30
295d4d263eSMichal Wilczynski 
305d4d263eSMichal Wilczynski /* Generate remote icu IRQ Register */
315d4d263eSMichal Wilczynski #define TH_1520_MBOX_GEN 0x10
325d4d263eSMichal Wilczynski #define TH_1520_MBOX_GEN_RX_DATA BIT(6)
335d4d263eSMichal Wilczynski #define TH_1520_MBOX_GEN_TX_ACK BIT(7)
345d4d263eSMichal Wilczynski 
355d4d263eSMichal Wilczynski #define TH_1520_MBOX_CHAN_RES_SIZE 0x1000
365d4d263eSMichal Wilczynski #define TH_1520_MBOX_CHANS 4
375d4d263eSMichal Wilczynski #define TH_1520_MBOX_CHAN_NAME_SIZE 20
385d4d263eSMichal Wilczynski 
395d4d263eSMichal Wilczynski #define TH_1520_MBOX_ACK_MAGIC 0xdeadbeaf
405d4d263eSMichal Wilczynski 
415d4d263eSMichal Wilczynski #ifdef CONFIG_PM_SLEEP
425d4d263eSMichal Wilczynski /* store MBOX context across system-wide suspend/resume transitions */
435d4d263eSMichal Wilczynski struct th1520_mbox_context {
44*db049866SMichal Wilczynski 	u32 intr_mask[TH_1520_MBOX_CHANS];
455d4d263eSMichal Wilczynski };
465d4d263eSMichal Wilczynski #endif
475d4d263eSMichal Wilczynski 
485d4d263eSMichal Wilczynski enum th1520_mbox_icu_cpu_id {
495d4d263eSMichal Wilczynski 	TH_1520_MBOX_ICU_KERNEL_CPU0, /* 910T */
505d4d263eSMichal Wilczynski 	TH_1520_MBOX_ICU_CPU1, /* 902 */
515d4d263eSMichal Wilczynski 	TH_1520_MBOX_ICU_CPU2, /* 906 */
525d4d263eSMichal Wilczynski 	TH_1520_MBOX_ICU_CPU3, /* 910R */
535d4d263eSMichal Wilczynski };
545d4d263eSMichal Wilczynski 
555d4d263eSMichal Wilczynski struct th1520_mbox_con_priv {
565d4d263eSMichal Wilczynski 	enum th1520_mbox_icu_cpu_id idx;
575d4d263eSMichal Wilczynski 	void __iomem *comm_local_base;
585d4d263eSMichal Wilczynski 	void __iomem *comm_remote_base;
595d4d263eSMichal Wilczynski 	char irq_desc[TH_1520_MBOX_CHAN_NAME_SIZE];
605d4d263eSMichal Wilczynski 	struct mbox_chan *chan;
615d4d263eSMichal Wilczynski };
625d4d263eSMichal Wilczynski 
635d4d263eSMichal Wilczynski struct th1520_mbox_priv {
645d4d263eSMichal Wilczynski 	struct device *dev;
655d4d263eSMichal Wilczynski 	void __iomem *local_icu[TH_1520_MBOX_CHANS];
665d4d263eSMichal Wilczynski 	void __iomem *remote_icu[TH_1520_MBOX_CHANS - 1];
675d4d263eSMichal Wilczynski 	void __iomem *cur_cpu_ch_base;
685d4d263eSMichal Wilczynski 	spinlock_t mbox_lock; /* control register lock */
695d4d263eSMichal Wilczynski 
705d4d263eSMichal Wilczynski 	struct mbox_controller mbox;
715d4d263eSMichal Wilczynski 	struct mbox_chan mbox_chans[TH_1520_MBOX_CHANS];
725d4d263eSMichal Wilczynski 	struct clk_bulk_data clocks[TH_1520_MBOX_CHANS];
735d4d263eSMichal Wilczynski 	struct th1520_mbox_con_priv con_priv[TH_1520_MBOX_CHANS];
745d4d263eSMichal Wilczynski 	int irq;
755d4d263eSMichal Wilczynski #ifdef CONFIG_PM_SLEEP
765d4d263eSMichal Wilczynski 	struct th1520_mbox_context *ctx;
775d4d263eSMichal Wilczynski #endif
785d4d263eSMichal Wilczynski };
795d4d263eSMichal Wilczynski 
805d4d263eSMichal Wilczynski static struct th1520_mbox_priv *
815d4d263eSMichal Wilczynski to_th1520_mbox_priv(struct mbox_controller *mbox)
825d4d263eSMichal Wilczynski {
835d4d263eSMichal Wilczynski 	return container_of(mbox, struct th1520_mbox_priv, mbox);
845d4d263eSMichal Wilczynski }
855d4d263eSMichal Wilczynski 
865d4d263eSMichal Wilczynski static void th1520_mbox_write(struct th1520_mbox_priv *priv, u32 val, u32 offs)
875d4d263eSMichal Wilczynski {
885d4d263eSMichal Wilczynski 	iowrite32(val, priv->cur_cpu_ch_base + offs);
895d4d263eSMichal Wilczynski }
905d4d263eSMichal Wilczynski 
915d4d263eSMichal Wilczynski static u32 th1520_mbox_read(struct th1520_mbox_priv *priv, u32 offs)
925d4d263eSMichal Wilczynski {
935d4d263eSMichal Wilczynski 	return ioread32(priv->cur_cpu_ch_base + offs);
945d4d263eSMichal Wilczynski }
955d4d263eSMichal Wilczynski 
965d4d263eSMichal Wilczynski static u32 th1520_mbox_rmw(struct th1520_mbox_priv *priv, u32 off, u32 set,
975d4d263eSMichal Wilczynski 			   u32 clr)
985d4d263eSMichal Wilczynski {
995d4d263eSMichal Wilczynski 	unsigned long flags;
1005d4d263eSMichal Wilczynski 	u32 val;
1015d4d263eSMichal Wilczynski 
1025d4d263eSMichal Wilczynski 	spin_lock_irqsave(&priv->mbox_lock, flags);
1035d4d263eSMichal Wilczynski 	val = th1520_mbox_read(priv, off);
1045d4d263eSMichal Wilczynski 	val &= ~clr;
1055d4d263eSMichal Wilczynski 	val |= set;
1065d4d263eSMichal Wilczynski 	th1520_mbox_write(priv, val, off);
1075d4d263eSMichal Wilczynski 	spin_unlock_irqrestore(&priv->mbox_lock, flags);
1085d4d263eSMichal Wilczynski 
1095d4d263eSMichal Wilczynski 	return val;
1105d4d263eSMichal Wilczynski }
1115d4d263eSMichal Wilczynski 
1125d4d263eSMichal Wilczynski static void th1520_mbox_chan_write(struct th1520_mbox_con_priv *cp, u32 val,
1135d4d263eSMichal Wilczynski 				   u32 offs, bool is_remote)
1145d4d263eSMichal Wilczynski {
1155d4d263eSMichal Wilczynski 	if (is_remote)
1165d4d263eSMichal Wilczynski 		iowrite32(val, cp->comm_remote_base + offs);
1175d4d263eSMichal Wilczynski 	else
1185d4d263eSMichal Wilczynski 		iowrite32(val, cp->comm_local_base + offs);
1195d4d263eSMichal Wilczynski }
1205d4d263eSMichal Wilczynski 
1215d4d263eSMichal Wilczynski static u32 th1520_mbox_chan_read(struct th1520_mbox_con_priv *cp, u32 offs,
1225d4d263eSMichal Wilczynski 				 bool is_remote)
1235d4d263eSMichal Wilczynski {
1245d4d263eSMichal Wilczynski 	if (is_remote)
1255d4d263eSMichal Wilczynski 		return ioread32(cp->comm_remote_base + offs);
1265d4d263eSMichal Wilczynski 	else
1275d4d263eSMichal Wilczynski 		return ioread32(cp->comm_local_base + offs);
1285d4d263eSMichal Wilczynski }
1295d4d263eSMichal Wilczynski 
1305d4d263eSMichal Wilczynski static void th1520_mbox_chan_rmw(struct th1520_mbox_con_priv *cp, u32 off,
1315d4d263eSMichal Wilczynski 				 u32 set, u32 clr, bool is_remote)
1325d4d263eSMichal Wilczynski {
1335d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv = to_th1520_mbox_priv(cp->chan->mbox);
1345d4d263eSMichal Wilczynski 	unsigned long flags;
1355d4d263eSMichal Wilczynski 	u32 val;
1365d4d263eSMichal Wilczynski 
1375d4d263eSMichal Wilczynski 	spin_lock_irqsave(&priv->mbox_lock, flags);
1385d4d263eSMichal Wilczynski 	val = th1520_mbox_chan_read(cp, off, is_remote);
1395d4d263eSMichal Wilczynski 	val &= ~clr;
1405d4d263eSMichal Wilczynski 	val |= set;
1415d4d263eSMichal Wilczynski 	th1520_mbox_chan_write(cp, val, off, is_remote);
1425d4d263eSMichal Wilczynski 	spin_unlock_irqrestore(&priv->mbox_lock, flags);
1435d4d263eSMichal Wilczynski }
1445d4d263eSMichal Wilczynski 
1455d4d263eSMichal Wilczynski static void th1520_mbox_chan_rd_data(struct th1520_mbox_con_priv *cp,
1465d4d263eSMichal Wilczynski 				     void *data, bool is_remote)
1475d4d263eSMichal Wilczynski {
1485d4d263eSMichal Wilczynski 	u32 off = TH_1520_MBOX_INFO0;
1495d4d263eSMichal Wilczynski 	u32 *arg = data;
1505d4d263eSMichal Wilczynski 	u32 i;
1515d4d263eSMichal Wilczynski 
1525d4d263eSMichal Wilczynski 	/* read info0 ~ info6, totally 28 bytes
1535d4d263eSMichal Wilczynski 	 * requires data memory size is 28 bytes
1545d4d263eSMichal Wilczynski 	 */
1555d4d263eSMichal Wilczynski 	for (i = 0; i < TH_1520_MBOX_DATA_INFO_NUM; i++) {
1565d4d263eSMichal Wilczynski 		*arg = th1520_mbox_chan_read(cp, off, is_remote);
1575d4d263eSMichal Wilczynski 		off += 4;
1585d4d263eSMichal Wilczynski 		arg++;
1595d4d263eSMichal Wilczynski 	}
1605d4d263eSMichal Wilczynski }
1615d4d263eSMichal Wilczynski 
1625d4d263eSMichal Wilczynski static void th1520_mbox_chan_wr_data(struct th1520_mbox_con_priv *cp,
1635d4d263eSMichal Wilczynski 				     void *data, bool is_remote)
1645d4d263eSMichal Wilczynski {
1655d4d263eSMichal Wilczynski 	u32 off = TH_1520_MBOX_INFO0;
1665d4d263eSMichal Wilczynski 	u32 *arg = data;
1675d4d263eSMichal Wilczynski 	u32 i;
1685d4d263eSMichal Wilczynski 
1695d4d263eSMichal Wilczynski 	/* write info0 ~ info6, totally 28 bytes
1705d4d263eSMichal Wilczynski 	 * requires data memory is 28 bytes valid data
1715d4d263eSMichal Wilczynski 	 */
1725d4d263eSMichal Wilczynski 	for (i = 0; i < TH_1520_MBOX_DATA_INFO_NUM; i++) {
1735d4d263eSMichal Wilczynski 		th1520_mbox_chan_write(cp, *arg, off, is_remote);
1745d4d263eSMichal Wilczynski 		off += 4;
1755d4d263eSMichal Wilczynski 		arg++;
1765d4d263eSMichal Wilczynski 	}
1775d4d263eSMichal Wilczynski }
1785d4d263eSMichal Wilczynski 
1795d4d263eSMichal Wilczynski static void th1520_mbox_chan_wr_ack(struct th1520_mbox_con_priv *cp, void *data,
1805d4d263eSMichal Wilczynski 				    bool is_remote)
1815d4d263eSMichal Wilczynski {
1825d4d263eSMichal Wilczynski 	u32 off = TH_1520_MBOX_INFO7;
1835d4d263eSMichal Wilczynski 	u32 *arg = data;
1845d4d263eSMichal Wilczynski 
1855d4d263eSMichal Wilczynski 	th1520_mbox_chan_write(cp, *arg, off, is_remote);
1865d4d263eSMichal Wilczynski }
1875d4d263eSMichal Wilczynski 
1885d4d263eSMichal Wilczynski static int th1520_mbox_chan_id_to_mapbit(struct th1520_mbox_con_priv *cp)
1895d4d263eSMichal Wilczynski {
1905d4d263eSMichal Wilczynski 	int mapbit = 0;
1915d4d263eSMichal Wilczynski 	int i;
1925d4d263eSMichal Wilczynski 
1935d4d263eSMichal Wilczynski 	for (i = 0; i < TH_1520_MBOX_CHANS; i++) {
1945d4d263eSMichal Wilczynski 		if (i == cp->idx)
1955d4d263eSMichal Wilczynski 			return mapbit;
1965d4d263eSMichal Wilczynski 
1975d4d263eSMichal Wilczynski 		if (i != TH_1520_MBOX_ICU_KERNEL_CPU0)
1985d4d263eSMichal Wilczynski 			mapbit++;
1995d4d263eSMichal Wilczynski 	}
2005d4d263eSMichal Wilczynski 
2015d4d263eSMichal Wilczynski 	if (i == TH_1520_MBOX_CHANS)
2025d4d263eSMichal Wilczynski 		dev_err(cp->chan->mbox->dev, "convert to mapbit failed\n");
2035d4d263eSMichal Wilczynski 
2045d4d263eSMichal Wilczynski 	return 0;
2055d4d263eSMichal Wilczynski }
2065d4d263eSMichal Wilczynski 
2075d4d263eSMichal Wilczynski static irqreturn_t th1520_mbox_isr(int irq, void *p)
2085d4d263eSMichal Wilczynski {
2095d4d263eSMichal Wilczynski 	struct mbox_chan *chan = p;
2105d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv = to_th1520_mbox_priv(chan->mbox);
2115d4d263eSMichal Wilczynski 	struct th1520_mbox_con_priv *cp = chan->con_priv;
2125d4d263eSMichal Wilczynski 	int mapbit = th1520_mbox_chan_id_to_mapbit(cp);
2135d4d263eSMichal Wilczynski 	u32 sta, dat[TH_1520_MBOX_DATA_INFO_NUM];
2145d4d263eSMichal Wilczynski 	u32 ack_magic = TH_1520_MBOX_ACK_MAGIC;
2155d4d263eSMichal Wilczynski 	u32 info0_data, info7_data;
2165d4d263eSMichal Wilczynski 
2175d4d263eSMichal Wilczynski 	sta = th1520_mbox_read(priv, TH_1520_MBOX_STA);
2185d4d263eSMichal Wilczynski 	if (!(sta & BIT(mapbit)))
2195d4d263eSMichal Wilczynski 		return IRQ_NONE;
2205d4d263eSMichal Wilczynski 
2215d4d263eSMichal Wilczynski 	/* clear chan irq bit in STA register */
2225d4d263eSMichal Wilczynski 	th1520_mbox_rmw(priv, TH_1520_MBOX_CLR, BIT(mapbit), 0);
2235d4d263eSMichal Wilczynski 
2245d4d263eSMichal Wilczynski 	/* info0 is the protocol word, should not be zero! */
2255d4d263eSMichal Wilczynski 	info0_data = th1520_mbox_chan_read(cp, TH_1520_MBOX_INFO0, false);
2265d4d263eSMichal Wilczynski 	if (info0_data) {
2275d4d263eSMichal Wilczynski 		/* read info0~info6 data */
2285d4d263eSMichal Wilczynski 		th1520_mbox_chan_rd_data(cp, dat, false);
2295d4d263eSMichal Wilczynski 
2305d4d263eSMichal Wilczynski 		/* clear local info0 */
2315d4d263eSMichal Wilczynski 		th1520_mbox_chan_write(cp, 0x0, TH_1520_MBOX_INFO0, false);
2325d4d263eSMichal Wilczynski 
2335d4d263eSMichal Wilczynski 		/* notify remote cpu */
2345d4d263eSMichal Wilczynski 		th1520_mbox_chan_wr_ack(cp, &ack_magic, true);
2355d4d263eSMichal Wilczynski 		/* CPU1 902/906 use polling mode to monitor info7 */
2365d4d263eSMichal Wilczynski 		if (cp->idx != TH_1520_MBOX_ICU_CPU1 &&
2375d4d263eSMichal Wilczynski 		    cp->idx != TH_1520_MBOX_ICU_CPU2)
2385d4d263eSMichal Wilczynski 			th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN,
2395d4d263eSMichal Wilczynski 					     TH_1520_MBOX_GEN_TX_ACK, 0, true);
2405d4d263eSMichal Wilczynski 
2415d4d263eSMichal Wilczynski 		/* transfer the data to client */
2425d4d263eSMichal Wilczynski 		mbox_chan_received_data(chan, (void *)dat);
2435d4d263eSMichal Wilczynski 	}
2445d4d263eSMichal Wilczynski 
2455d4d263eSMichal Wilczynski 	/* info7 magic value mean the real ack signal, not generate bit7 */
2465d4d263eSMichal Wilczynski 	info7_data = th1520_mbox_chan_read(cp, TH_1520_MBOX_INFO7, false);
2475d4d263eSMichal Wilczynski 	if (info7_data == TH_1520_MBOX_ACK_MAGIC) {
2485d4d263eSMichal Wilczynski 		/* clear local info7 */
2495d4d263eSMichal Wilczynski 		th1520_mbox_chan_write(cp, 0x0, TH_1520_MBOX_INFO7, false);
2505d4d263eSMichal Wilczynski 
2515d4d263eSMichal Wilczynski 		/* notify framework the last TX has completed */
2525d4d263eSMichal Wilczynski 		mbox_chan_txdone(chan, 0);
2535d4d263eSMichal Wilczynski 	}
2545d4d263eSMichal Wilczynski 
2555d4d263eSMichal Wilczynski 	if (!info0_data && !info7_data)
2565d4d263eSMichal Wilczynski 		return IRQ_NONE;
2575d4d263eSMichal Wilczynski 
2585d4d263eSMichal Wilczynski 	return IRQ_HANDLED;
2595d4d263eSMichal Wilczynski }
2605d4d263eSMichal Wilczynski 
2615d4d263eSMichal Wilczynski static int th1520_mbox_send_data(struct mbox_chan *chan, void *data)
2625d4d263eSMichal Wilczynski {
2635d4d263eSMichal Wilczynski 	struct th1520_mbox_con_priv *cp = chan->con_priv;
2645d4d263eSMichal Wilczynski 
2655d4d263eSMichal Wilczynski 	th1520_mbox_chan_wr_data(cp, data, true);
2665d4d263eSMichal Wilczynski 	th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, TH_1520_MBOX_GEN_RX_DATA, 0,
2675d4d263eSMichal Wilczynski 			     true);
2685d4d263eSMichal Wilczynski 	return 0;
2695d4d263eSMichal Wilczynski }
2705d4d263eSMichal Wilczynski 
2715d4d263eSMichal Wilczynski static int th1520_mbox_startup(struct mbox_chan *chan)
2725d4d263eSMichal Wilczynski {
2735d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv = to_th1520_mbox_priv(chan->mbox);
2745d4d263eSMichal Wilczynski 	struct th1520_mbox_con_priv *cp = chan->con_priv;
2755d4d263eSMichal Wilczynski 	u32 data[8] = {};
2765d4d263eSMichal Wilczynski 	int mask_bit;
2775d4d263eSMichal Wilczynski 	int ret;
2785d4d263eSMichal Wilczynski 
2795d4d263eSMichal Wilczynski 	/* clear local and remote generate and info0~info7 */
2805d4d263eSMichal Wilczynski 	th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, 0x0, 0xff, true);
2815d4d263eSMichal Wilczynski 	th1520_mbox_chan_rmw(cp, TH_1520_MBOX_GEN, 0x0, 0xff, false);
2825d4d263eSMichal Wilczynski 	th1520_mbox_chan_wr_ack(cp, &data[7], true);
2835d4d263eSMichal Wilczynski 	th1520_mbox_chan_wr_ack(cp, &data[7], false);
2845d4d263eSMichal Wilczynski 	th1520_mbox_chan_wr_data(cp, &data[0], true);
2855d4d263eSMichal Wilczynski 	th1520_mbox_chan_wr_data(cp, &data[0], false);
2865d4d263eSMichal Wilczynski 
2875d4d263eSMichal Wilczynski 	/* enable the chan mask */
2885d4d263eSMichal Wilczynski 	mask_bit = th1520_mbox_chan_id_to_mapbit(cp);
2895d4d263eSMichal Wilczynski 	th1520_mbox_rmw(priv, TH_1520_MBOX_MASK, BIT(mask_bit), 0);
2905d4d263eSMichal Wilczynski 
2915d4d263eSMichal Wilczynski 	/*
2925d4d263eSMichal Wilczynski 	 * Mixing devm_ managed resources with manual IRQ handling is generally
2935d4d263eSMichal Wilczynski 	 * discouraged due to potential complexities with resource management,
2945d4d263eSMichal Wilczynski 	 * especially when dealing with shared interrupts. However, in this case,
2955d4d263eSMichal Wilczynski 	 * the approach is safe and effective because:
2965d4d263eSMichal Wilczynski 	 *
2975d4d263eSMichal Wilczynski 	 * 1. Each mailbox channel requests its IRQ within the .startup() callback
2985d4d263eSMichal Wilczynski 	 *    and frees it within the .shutdown() callback.
2995d4d263eSMichal Wilczynski 	 * 2. During device unbinding, the devm_ managed mailbox controller first
3005d4d263eSMichal Wilczynski 	 *    iterates through all channels, ensuring that their IRQs are freed before
3015d4d263eSMichal Wilczynski 	 *    any other devm_ resources are released.
3025d4d263eSMichal Wilczynski 	 *
3035d4d263eSMichal Wilczynski 	 * This ordering guarantees that no interrupts can be triggered from the device
3045d4d263eSMichal Wilczynski 	 * while it is being unbound, preventing race conditions and ensuring system
3055d4d263eSMichal Wilczynski 	 * stability.
3065d4d263eSMichal Wilczynski 	 */
3075d4d263eSMichal Wilczynski 	ret = request_irq(priv->irq, th1520_mbox_isr,
3085d4d263eSMichal Wilczynski 			  IRQF_SHARED | IRQF_NO_SUSPEND, cp->irq_desc, chan);
3095d4d263eSMichal Wilczynski 	if (ret) {
3105d4d263eSMichal Wilczynski 		dev_err(priv->dev, "Unable to acquire IRQ %d\n", priv->irq);
3115d4d263eSMichal Wilczynski 		return ret;
3125d4d263eSMichal Wilczynski 	}
3135d4d263eSMichal Wilczynski 
3145d4d263eSMichal Wilczynski 	return 0;
3155d4d263eSMichal Wilczynski }
3165d4d263eSMichal Wilczynski 
3175d4d263eSMichal Wilczynski static void th1520_mbox_shutdown(struct mbox_chan *chan)
3185d4d263eSMichal Wilczynski {
3195d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv = to_th1520_mbox_priv(chan->mbox);
3205d4d263eSMichal Wilczynski 	struct th1520_mbox_con_priv *cp = chan->con_priv;
3215d4d263eSMichal Wilczynski 	int mask_bit;
3225d4d263eSMichal Wilczynski 
3235d4d263eSMichal Wilczynski 	free_irq(priv->irq, chan);
3245d4d263eSMichal Wilczynski 
3255d4d263eSMichal Wilczynski 	/* clear the chan mask */
3265d4d263eSMichal Wilczynski 	mask_bit = th1520_mbox_chan_id_to_mapbit(cp);
3275d4d263eSMichal Wilczynski 	th1520_mbox_rmw(priv, TH_1520_MBOX_MASK, 0, BIT(mask_bit));
3285d4d263eSMichal Wilczynski }
3295d4d263eSMichal Wilczynski 
3305d4d263eSMichal Wilczynski static const struct mbox_chan_ops th1520_mbox_ops = {
3315d4d263eSMichal Wilczynski 	.send_data = th1520_mbox_send_data,
3325d4d263eSMichal Wilczynski 	.startup = th1520_mbox_startup,
3335d4d263eSMichal Wilczynski 	.shutdown = th1520_mbox_shutdown,
3345d4d263eSMichal Wilczynski };
3355d4d263eSMichal Wilczynski 
3365d4d263eSMichal Wilczynski static int th1520_mbox_init_generic(struct th1520_mbox_priv *priv)
3375d4d263eSMichal Wilczynski {
3385d4d263eSMichal Wilczynski #ifdef CONFIG_PM_SLEEP
3395d4d263eSMichal Wilczynski 	priv->ctx = devm_kzalloc(priv->dev, sizeof(*priv->ctx), GFP_KERNEL);
3405d4d263eSMichal Wilczynski 	if (!priv->ctx)
3415d4d263eSMichal Wilczynski 		return -ENOMEM;
3425d4d263eSMichal Wilczynski #endif
3435d4d263eSMichal Wilczynski 	/* Set default configuration */
3445d4d263eSMichal Wilczynski 	th1520_mbox_write(priv, 0xff, TH_1520_MBOX_CLR);
3455d4d263eSMichal Wilczynski 	th1520_mbox_write(priv, 0x0, TH_1520_MBOX_MASK);
3465d4d263eSMichal Wilczynski 	return 0;
3475d4d263eSMichal Wilczynski }
3485d4d263eSMichal Wilczynski 
3495d4d263eSMichal Wilczynski static struct mbox_chan *th1520_mbox_xlate(struct mbox_controller *mbox,
3505d4d263eSMichal Wilczynski 					   const struct of_phandle_args *sp)
3515d4d263eSMichal Wilczynski {
3525d4d263eSMichal Wilczynski 	u32 chan;
3535d4d263eSMichal Wilczynski 
3545d4d263eSMichal Wilczynski 	if (sp->args_count != 1) {
3555d4d263eSMichal Wilczynski 		dev_err(mbox->dev, "Invalid argument count %d\n",
3565d4d263eSMichal Wilczynski 			sp->args_count);
3575d4d263eSMichal Wilczynski 		return ERR_PTR(-EINVAL);
3585d4d263eSMichal Wilczynski 	}
3595d4d263eSMichal Wilczynski 
3605d4d263eSMichal Wilczynski 	chan = sp->args[0]; /* comm remote channel */
3615d4d263eSMichal Wilczynski 
3625d4d263eSMichal Wilczynski 	if (chan >= mbox->num_chans) {
3635d4d263eSMichal Wilczynski 		dev_err(mbox->dev, "Not supported channel number: %d\n", chan);
3645d4d263eSMichal Wilczynski 		return ERR_PTR(-EINVAL);
3655d4d263eSMichal Wilczynski 	}
3665d4d263eSMichal Wilczynski 
3675d4d263eSMichal Wilczynski 	if (chan == TH_1520_MBOX_ICU_KERNEL_CPU0) {
3685d4d263eSMichal Wilczynski 		dev_err(mbox->dev, "Cannot communicate with yourself\n");
3695d4d263eSMichal Wilczynski 		return ERR_PTR(-EINVAL);
3705d4d263eSMichal Wilczynski 	}
3715d4d263eSMichal Wilczynski 
3725d4d263eSMichal Wilczynski 	return &mbox->chans[chan];
3735d4d263eSMichal Wilczynski }
3745d4d263eSMichal Wilczynski 
3755d4d263eSMichal Wilczynski static void __iomem *th1520_map_mmio(struct platform_device *pdev,
3765d4d263eSMichal Wilczynski 				     char *res_name, size_t offset)
3775d4d263eSMichal Wilczynski {
3785d4d263eSMichal Wilczynski 	void __iomem *mapped;
3795d4d263eSMichal Wilczynski 	struct resource *res;
3805d4d263eSMichal Wilczynski 
3815d4d263eSMichal Wilczynski 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, res_name);
3825d4d263eSMichal Wilczynski 
3835d4d263eSMichal Wilczynski 	if (!res) {
3845d4d263eSMichal Wilczynski 		dev_err(&pdev->dev, "Failed to get resource: %s\n", res_name);
3855d4d263eSMichal Wilczynski 		return ERR_PTR(-EINVAL);
3865d4d263eSMichal Wilczynski 	}
3875d4d263eSMichal Wilczynski 
3885d4d263eSMichal Wilczynski 	mapped = devm_ioremap(&pdev->dev, res->start + offset,
3895d4d263eSMichal Wilczynski 			      resource_size(res) - offset);
390d0f98e14SDan Carpenter 	if (!mapped) {
3915d4d263eSMichal Wilczynski 		dev_err(&pdev->dev, "Failed to map resource: %s\n", res_name);
392d0f98e14SDan Carpenter 		return ERR_PTR(-ENOMEM);
393d0f98e14SDan Carpenter 	}
3945d4d263eSMichal Wilczynski 
3955d4d263eSMichal Wilczynski 	return mapped;
3965d4d263eSMichal Wilczynski }
3975d4d263eSMichal Wilczynski 
3985d4d263eSMichal Wilczynski static void th1520_disable_clk(void *data)
3995d4d263eSMichal Wilczynski {
4005d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv = data;
4015d4d263eSMichal Wilczynski 
4025d4d263eSMichal Wilczynski 	clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks);
4035d4d263eSMichal Wilczynski }
4045d4d263eSMichal Wilczynski 
4055d4d263eSMichal Wilczynski static int th1520_mbox_probe(struct platform_device *pdev)
4065d4d263eSMichal Wilczynski {
4075d4d263eSMichal Wilczynski 	struct device *dev = &pdev->dev;
4085d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv;
4095d4d263eSMichal Wilczynski 	unsigned int remote_idx = 0;
4105d4d263eSMichal Wilczynski 	unsigned int i;
4115d4d263eSMichal Wilczynski 	int ret;
4125d4d263eSMichal Wilczynski 
4135d4d263eSMichal Wilczynski 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
4145d4d263eSMichal Wilczynski 	if (!priv)
4155d4d263eSMichal Wilczynski 		return -ENOMEM;
4165d4d263eSMichal Wilczynski 
4175d4d263eSMichal Wilczynski 	priv->dev = dev;
4185d4d263eSMichal Wilczynski 
4195d4d263eSMichal Wilczynski 	priv->clocks[0].id = "clk-local";
4205d4d263eSMichal Wilczynski 	priv->clocks[1].id = "clk-remote-icu0";
4215d4d263eSMichal Wilczynski 	priv->clocks[2].id = "clk-remote-icu1";
4225d4d263eSMichal Wilczynski 	priv->clocks[3].id = "clk-remote-icu2";
4235d4d263eSMichal Wilczynski 
4245d4d263eSMichal Wilczynski 	ret = devm_clk_bulk_get(dev, ARRAY_SIZE(priv->clocks),
4255d4d263eSMichal Wilczynski 				priv->clocks);
4265d4d263eSMichal Wilczynski 	if (ret) {
4275d4d263eSMichal Wilczynski 		dev_err(dev, "Failed to get clocks\n");
4285d4d263eSMichal Wilczynski 		return ret;
4295d4d263eSMichal Wilczynski 	}
4305d4d263eSMichal Wilczynski 
4315d4d263eSMichal Wilczynski 	ret = clk_bulk_prepare_enable(ARRAY_SIZE(priv->clocks), priv->clocks);
4325d4d263eSMichal Wilczynski 	if (ret) {
4335d4d263eSMichal Wilczynski 		dev_err(dev, "Failed to enable clocks\n");
4345d4d263eSMichal Wilczynski 		return ret;
4355d4d263eSMichal Wilczynski 	}
4365d4d263eSMichal Wilczynski 
4375d4d263eSMichal Wilczynski 	ret = devm_add_action_or_reset(dev, th1520_disable_clk, priv);
4385d4d263eSMichal Wilczynski 	if (ret) {
4395d4d263eSMichal Wilczynski 		clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks);
4405d4d263eSMichal Wilczynski 		return ret;
4415d4d263eSMichal Wilczynski 	}
4425d4d263eSMichal Wilczynski 
4435d4d263eSMichal Wilczynski 	/*
4445d4d263eSMichal Wilczynski 	 * The address mappings in the device tree align precisely with those
4455d4d263eSMichal Wilczynski 	 * outlined in the manual. However, register offsets within these
4465d4d263eSMichal Wilczynski 	 * mapped regions are irregular, particularly for remote-icu0.
4475d4d263eSMichal Wilczynski 	 * Consequently, th1520_map_mmio() requires an additional parameter to
4485d4d263eSMichal Wilczynski 	 * handle this quirk.
4495d4d263eSMichal Wilczynski 	 */
4505d4d263eSMichal Wilczynski 	priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0] =
4515d4d263eSMichal Wilczynski 		th1520_map_mmio(pdev, "local", 0x0);
4525d4d263eSMichal Wilczynski 	if (IS_ERR(priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0]))
4535d4d263eSMichal Wilczynski 		return PTR_ERR(priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0]);
4545d4d263eSMichal Wilczynski 
4555d4d263eSMichal Wilczynski 	priv->remote_icu[0] = th1520_map_mmio(pdev, "remote-icu0", 0x4000);
4565d4d263eSMichal Wilczynski 	if (IS_ERR(priv->remote_icu[0]))
4575d4d263eSMichal Wilczynski 		return PTR_ERR(priv->remote_icu[0]);
4585d4d263eSMichal Wilczynski 
4595d4d263eSMichal Wilczynski 	priv->remote_icu[1] = th1520_map_mmio(pdev, "remote-icu1", 0x0);
4605d4d263eSMichal Wilczynski 	if (IS_ERR(priv->remote_icu[1]))
4615d4d263eSMichal Wilczynski 		return PTR_ERR(priv->remote_icu[1]);
4625d4d263eSMichal Wilczynski 
4635d4d263eSMichal Wilczynski 	priv->remote_icu[2] = th1520_map_mmio(pdev, "remote-icu2", 0x0);
4645d4d263eSMichal Wilczynski 	if (IS_ERR(priv->remote_icu[2]))
4655d4d263eSMichal Wilczynski 		return PTR_ERR(priv->remote_icu[2]);
4665d4d263eSMichal Wilczynski 
4675d4d263eSMichal Wilczynski 	priv->local_icu[TH_1520_MBOX_ICU_CPU1] =
4685d4d263eSMichal Wilczynski 		priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0] +
4695d4d263eSMichal Wilczynski 		TH_1520_MBOX_CHAN_RES_SIZE;
4705d4d263eSMichal Wilczynski 	priv->local_icu[TH_1520_MBOX_ICU_CPU2] =
4715d4d263eSMichal Wilczynski 		priv->local_icu[TH_1520_MBOX_ICU_CPU1] +
4725d4d263eSMichal Wilczynski 		TH_1520_MBOX_CHAN_RES_SIZE;
4735d4d263eSMichal Wilczynski 	priv->local_icu[TH_1520_MBOX_ICU_CPU3] =
4745d4d263eSMichal Wilczynski 		priv->local_icu[TH_1520_MBOX_ICU_CPU2] +
4755d4d263eSMichal Wilczynski 		TH_1520_MBOX_CHAN_RES_SIZE;
4765d4d263eSMichal Wilczynski 
4775d4d263eSMichal Wilczynski 	priv->cur_cpu_ch_base = priv->local_icu[TH_1520_MBOX_ICU_KERNEL_CPU0];
4785d4d263eSMichal Wilczynski 
4795d4d263eSMichal Wilczynski 	priv->irq = platform_get_irq(pdev, 0);
4805d4d263eSMichal Wilczynski 	if (priv->irq < 0)
4815d4d263eSMichal Wilczynski 		return priv->irq;
4825d4d263eSMichal Wilczynski 
4835d4d263eSMichal Wilczynski 	/* init the chans */
4845d4d263eSMichal Wilczynski 	for (i = 0; i < TH_1520_MBOX_CHANS; i++) {
4855d4d263eSMichal Wilczynski 		struct th1520_mbox_con_priv *cp = &priv->con_priv[i];
4865d4d263eSMichal Wilczynski 
4875d4d263eSMichal Wilczynski 		cp->idx = i;
4885d4d263eSMichal Wilczynski 		cp->chan = &priv->mbox_chans[i];
4895d4d263eSMichal Wilczynski 		priv->mbox_chans[i].con_priv = cp;
4905d4d263eSMichal Wilczynski 		snprintf(cp->irq_desc, sizeof(cp->irq_desc),
4915d4d263eSMichal Wilczynski 			 "th1520_mbox_chan[%i]", cp->idx);
4925d4d263eSMichal Wilczynski 
4935d4d263eSMichal Wilczynski 		cp->comm_local_base = priv->local_icu[i];
4945d4d263eSMichal Wilczynski 		if (i != TH_1520_MBOX_ICU_KERNEL_CPU0) {
4955d4d263eSMichal Wilczynski 			cp->comm_remote_base = priv->remote_icu[remote_idx];
4965d4d263eSMichal Wilczynski 			remote_idx++;
4975d4d263eSMichal Wilczynski 		}
4985d4d263eSMichal Wilczynski 	}
4995d4d263eSMichal Wilczynski 
5005d4d263eSMichal Wilczynski 	spin_lock_init(&priv->mbox_lock);
5015d4d263eSMichal Wilczynski 
5025d4d263eSMichal Wilczynski 	priv->mbox.dev = dev;
5035d4d263eSMichal Wilczynski 	priv->mbox.ops = &th1520_mbox_ops;
5045d4d263eSMichal Wilczynski 	priv->mbox.chans = priv->mbox_chans;
5055d4d263eSMichal Wilczynski 	priv->mbox.num_chans = TH_1520_MBOX_CHANS;
5065d4d263eSMichal Wilczynski 	priv->mbox.of_xlate = th1520_mbox_xlate;
5075d4d263eSMichal Wilczynski 	priv->mbox.txdone_irq = true;
5085d4d263eSMichal Wilczynski 
5095d4d263eSMichal Wilczynski 	platform_set_drvdata(pdev, priv);
5105d4d263eSMichal Wilczynski 
5115d4d263eSMichal Wilczynski 	ret = th1520_mbox_init_generic(priv);
5125d4d263eSMichal Wilczynski 	if (ret) {
5135d4d263eSMichal Wilczynski 		dev_err(dev, "Failed to init mailbox context\n");
5145d4d263eSMichal Wilczynski 		return ret;
5155d4d263eSMichal Wilczynski 	}
5165d4d263eSMichal Wilczynski 
5175d4d263eSMichal Wilczynski 	return devm_mbox_controller_register(dev, &priv->mbox);
5185d4d263eSMichal Wilczynski }
5195d4d263eSMichal Wilczynski 
5205d4d263eSMichal Wilczynski static const struct of_device_id th1520_mbox_dt_ids[] = {
5215d4d263eSMichal Wilczynski 	{ .compatible = "thead,th1520-mbox" },
5225d4d263eSMichal Wilczynski 	{}
5235d4d263eSMichal Wilczynski };
5245d4d263eSMichal Wilczynski MODULE_DEVICE_TABLE(of, th1520_mbox_dt_ids);
5255d4d263eSMichal Wilczynski 
5265d4d263eSMichal Wilczynski #ifdef CONFIG_PM_SLEEP
5275d4d263eSMichal Wilczynski static int __maybe_unused th1520_mbox_suspend_noirq(struct device *dev)
5285d4d263eSMichal Wilczynski {
5295d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv = dev_get_drvdata(dev);
5305d4d263eSMichal Wilczynski 	struct th1520_mbox_context *ctx = priv->ctx;
5315d4d263eSMichal Wilczynski 	u32 i;
5325d4d263eSMichal Wilczynski 	/*
5335d4d263eSMichal Wilczynski 	 * ONLY interrupt mask bit should be stored and restores.
5345d4d263eSMichal Wilczynski 	 * INFO data all assumed to be lost.
5355d4d263eSMichal Wilczynski 	 */
5365d4d263eSMichal Wilczynski 	for (i = 0; i < TH_1520_MBOX_CHANS; i++) {
5375d4d263eSMichal Wilczynski 		ctx->intr_mask[i] =
5385d4d263eSMichal Wilczynski 			ioread32(priv->local_icu[i] + TH_1520_MBOX_MASK);
5395d4d263eSMichal Wilczynski 	}
5405d4d263eSMichal Wilczynski 	return 0;
5415d4d263eSMichal Wilczynski }
5425d4d263eSMichal Wilczynski 
5435d4d263eSMichal Wilczynski static int __maybe_unused th1520_mbox_resume_noirq(struct device *dev)
5445d4d263eSMichal Wilczynski {
5455d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv = dev_get_drvdata(dev);
5465d4d263eSMichal Wilczynski 	struct th1520_mbox_context *ctx = priv->ctx;
5475d4d263eSMichal Wilczynski 	u32 i;
5485d4d263eSMichal Wilczynski 
5495d4d263eSMichal Wilczynski 	for (i = 0; i < TH_1520_MBOX_CHANS; i++) {
5505d4d263eSMichal Wilczynski 		iowrite32(ctx->intr_mask[i],
5515d4d263eSMichal Wilczynski 			  priv->local_icu[i] + TH_1520_MBOX_MASK);
5525d4d263eSMichal Wilczynski 	}
5535d4d263eSMichal Wilczynski 
5545d4d263eSMichal Wilczynski 	return 0;
5555d4d263eSMichal Wilczynski }
5565d4d263eSMichal Wilczynski #endif
5575d4d263eSMichal Wilczynski 
5585d4d263eSMichal Wilczynski static int  __maybe_unused th1520_mbox_runtime_suspend(struct device *dev)
5595d4d263eSMichal Wilczynski {
5605d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv = dev_get_drvdata(dev);
5615d4d263eSMichal Wilczynski 
5625d4d263eSMichal Wilczynski 	clk_bulk_disable_unprepare(ARRAY_SIZE(priv->clocks), priv->clocks);
5635d4d263eSMichal Wilczynski 
5645d4d263eSMichal Wilczynski 	return 0;
5655d4d263eSMichal Wilczynski }
5665d4d263eSMichal Wilczynski 
5675d4d263eSMichal Wilczynski static int __maybe_unused th1520_mbox_runtime_resume(struct device *dev)
5685d4d263eSMichal Wilczynski {
5695d4d263eSMichal Wilczynski 	struct th1520_mbox_priv *priv = dev_get_drvdata(dev);
5705d4d263eSMichal Wilczynski 	int ret;
5715d4d263eSMichal Wilczynski 
5725d4d263eSMichal Wilczynski 	ret = clk_bulk_prepare_enable(ARRAY_SIZE(priv->clocks), priv->clocks);
5735d4d263eSMichal Wilczynski 	if (ret)
5745d4d263eSMichal Wilczynski 		dev_err(dev, "Failed to enable clocks in runtime resume\n");
5755d4d263eSMichal Wilczynski 
5765d4d263eSMichal Wilczynski 	return ret;
5775d4d263eSMichal Wilczynski }
5785d4d263eSMichal Wilczynski 
5795d4d263eSMichal Wilczynski static const struct dev_pm_ops th1520_mbox_pm_ops = {
5805d4d263eSMichal Wilczynski #ifdef CONFIG_PM_SLEEP
5815d4d263eSMichal Wilczynski 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(th1520_mbox_suspend_noirq,
5825d4d263eSMichal Wilczynski 				      th1520_mbox_resume_noirq)
5835d4d263eSMichal Wilczynski #endif
5845d4d263eSMichal Wilczynski 	SET_RUNTIME_PM_OPS(th1520_mbox_runtime_suspend,
5855d4d263eSMichal Wilczynski 			   th1520_mbox_runtime_resume, NULL)
5865d4d263eSMichal Wilczynski };
5875d4d263eSMichal Wilczynski 
5885d4d263eSMichal Wilczynski static struct platform_driver th1520_mbox_driver = {
5895d4d263eSMichal Wilczynski 	.probe		= th1520_mbox_probe,
5905d4d263eSMichal Wilczynski 	.driver = {
5915d4d263eSMichal Wilczynski 		.name	= "th1520-mbox",
5925d4d263eSMichal Wilczynski 		.of_match_table = th1520_mbox_dt_ids,
5935d4d263eSMichal Wilczynski 		.pm = &th1520_mbox_pm_ops,
5945d4d263eSMichal Wilczynski 	},
5955d4d263eSMichal Wilczynski };
5965d4d263eSMichal Wilczynski module_platform_driver(th1520_mbox_driver);
5975d4d263eSMichal Wilczynski 
5985d4d263eSMichal Wilczynski MODULE_DESCRIPTION("Thead TH-1520 mailbox IPC driver");
5995d4d263eSMichal Wilczynski MODULE_LICENSE("GPL");
600