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