18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Spreadtrum mailbox driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2020 Spreadtrum Communications Inc. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/delay.h> 98c2ecf20Sopenharmony_ci#include <linux/err.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/io.h> 128c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 158c2ecf20Sopenharmony_ci#include <linux/clk.h> 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define SPRD_MBOX_ID 0x0 188c2ecf20Sopenharmony_ci#define SPRD_MBOX_MSG_LOW 0x4 198c2ecf20Sopenharmony_ci#define SPRD_MBOX_MSG_HIGH 0x8 208c2ecf20Sopenharmony_ci#define SPRD_MBOX_TRIGGER 0xc 218c2ecf20Sopenharmony_ci#define SPRD_MBOX_FIFO_RST 0x10 228c2ecf20Sopenharmony_ci#define SPRD_MBOX_FIFO_STS 0x14 238c2ecf20Sopenharmony_ci#define SPRD_MBOX_IRQ_STS 0x18 248c2ecf20Sopenharmony_ci#define SPRD_MBOX_IRQ_MSK 0x1c 258c2ecf20Sopenharmony_ci#define SPRD_MBOX_LOCK 0x20 268c2ecf20Sopenharmony_ci#define SPRD_MBOX_FIFO_DEPTH 0x24 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* Bit and mask definiation for inbox's SPRD_MBOX_FIFO_STS register */ 298c2ecf20Sopenharmony_ci#define SPRD_INBOX_FIFO_DELIVER_MASK GENMASK(23, 16) 308c2ecf20Sopenharmony_ci#define SPRD_INBOX_FIFO_OVERLOW_MASK GENMASK(15, 8) 318c2ecf20Sopenharmony_ci#define SPRD_INBOX_FIFO_DELIVER_SHIFT 16 328c2ecf20Sopenharmony_ci#define SPRD_INBOX_FIFO_BUSY_MASK GENMASK(7, 0) 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_ci/* Bit and mask definiation for SPRD_MBOX_IRQ_STS register */ 358c2ecf20Sopenharmony_ci#define SPRD_MBOX_IRQ_CLR BIT(0) 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci/* Bit and mask definiation for outbox's SPRD_MBOX_FIFO_STS register */ 388c2ecf20Sopenharmony_ci#define SPRD_OUTBOX_FIFO_FULL BIT(2) 398c2ecf20Sopenharmony_ci#define SPRD_OUTBOX_FIFO_WR_SHIFT 16 408c2ecf20Sopenharmony_ci#define SPRD_OUTBOX_FIFO_RD_SHIFT 24 418c2ecf20Sopenharmony_ci#define SPRD_OUTBOX_FIFO_POS_MASK GENMASK(7, 0) 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_ci/* Bit and mask definiation for inbox's SPRD_MBOX_IRQ_MSK register */ 448c2ecf20Sopenharmony_ci#define SPRD_INBOX_FIFO_BLOCK_IRQ BIT(0) 458c2ecf20Sopenharmony_ci#define SPRD_INBOX_FIFO_OVERFLOW_IRQ BIT(1) 468c2ecf20Sopenharmony_ci#define SPRD_INBOX_FIFO_DELIVER_IRQ BIT(2) 478c2ecf20Sopenharmony_ci#define SPRD_INBOX_FIFO_IRQ_MASK GENMASK(2, 0) 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci/* Bit and mask definiation for outbox's SPRD_MBOX_IRQ_MSK register */ 508c2ecf20Sopenharmony_ci#define SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ BIT(0) 518c2ecf20Sopenharmony_ci#define SPRD_OUTBOX_FIFO_IRQ_MASK GENMASK(4, 0) 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci#define SPRD_MBOX_CHAN_MAX 8 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_cistruct sprd_mbox_priv { 568c2ecf20Sopenharmony_ci struct mbox_controller mbox; 578c2ecf20Sopenharmony_ci struct device *dev; 588c2ecf20Sopenharmony_ci void __iomem *inbox_base; 598c2ecf20Sopenharmony_ci void __iomem *outbox_base; 608c2ecf20Sopenharmony_ci struct clk *clk; 618c2ecf20Sopenharmony_ci u32 outbox_fifo_depth; 628c2ecf20Sopenharmony_ci 638c2ecf20Sopenharmony_ci struct mutex lock; 648c2ecf20Sopenharmony_ci u32 refcnt; 658c2ecf20Sopenharmony_ci struct mbox_chan chan[SPRD_MBOX_CHAN_MAX]; 668c2ecf20Sopenharmony_ci}; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_cistatic struct sprd_mbox_priv *to_sprd_mbox_priv(struct mbox_controller *mbox) 698c2ecf20Sopenharmony_ci{ 708c2ecf20Sopenharmony_ci return container_of(mbox, struct sprd_mbox_priv, mbox); 718c2ecf20Sopenharmony_ci} 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_cistatic u32 sprd_mbox_get_fifo_len(struct sprd_mbox_priv *priv, u32 fifo_sts) 748c2ecf20Sopenharmony_ci{ 758c2ecf20Sopenharmony_ci u32 wr_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_WR_SHIFT) & 768c2ecf20Sopenharmony_ci SPRD_OUTBOX_FIFO_POS_MASK; 778c2ecf20Sopenharmony_ci u32 rd_pos = (fifo_sts >> SPRD_OUTBOX_FIFO_RD_SHIFT) & 788c2ecf20Sopenharmony_ci SPRD_OUTBOX_FIFO_POS_MASK; 798c2ecf20Sopenharmony_ci u32 fifo_len; 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci /* 828c2ecf20Sopenharmony_ci * If the read pointer is equal with write pointer, which means the fifo 838c2ecf20Sopenharmony_ci * is full or empty. 848c2ecf20Sopenharmony_ci */ 858c2ecf20Sopenharmony_ci if (wr_pos == rd_pos) { 868c2ecf20Sopenharmony_ci if (fifo_sts & SPRD_OUTBOX_FIFO_FULL) 878c2ecf20Sopenharmony_ci fifo_len = priv->outbox_fifo_depth; 888c2ecf20Sopenharmony_ci else 898c2ecf20Sopenharmony_ci fifo_len = 0; 908c2ecf20Sopenharmony_ci } else if (wr_pos > rd_pos) { 918c2ecf20Sopenharmony_ci fifo_len = wr_pos - rd_pos; 928c2ecf20Sopenharmony_ci } else { 938c2ecf20Sopenharmony_ci fifo_len = priv->outbox_fifo_depth - rd_pos + wr_pos; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci return fifo_len; 978c2ecf20Sopenharmony_ci} 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_cistatic irqreturn_t sprd_mbox_outbox_isr(int irq, void *data) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci struct sprd_mbox_priv *priv = data; 1028c2ecf20Sopenharmony_ci struct mbox_chan *chan; 1038c2ecf20Sopenharmony_ci u32 fifo_sts, fifo_len, msg[2]; 1048c2ecf20Sopenharmony_ci int i, id; 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci fifo_sts = readl(priv->outbox_base + SPRD_MBOX_FIFO_STS); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci fifo_len = sprd_mbox_get_fifo_len(priv, fifo_sts); 1098c2ecf20Sopenharmony_ci if (!fifo_len) { 1108c2ecf20Sopenharmony_ci dev_warn_ratelimited(priv->dev, "spurious outbox interrupt\n"); 1118c2ecf20Sopenharmony_ci return IRQ_NONE; 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci 1148c2ecf20Sopenharmony_ci for (i = 0; i < fifo_len; i++) { 1158c2ecf20Sopenharmony_ci msg[0] = readl(priv->outbox_base + SPRD_MBOX_MSG_LOW); 1168c2ecf20Sopenharmony_ci msg[1] = readl(priv->outbox_base + SPRD_MBOX_MSG_HIGH); 1178c2ecf20Sopenharmony_ci id = readl(priv->outbox_base + SPRD_MBOX_ID); 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci chan = &priv->chan[id]; 1208c2ecf20Sopenharmony_ci if (chan->cl) 1218c2ecf20Sopenharmony_ci mbox_chan_received_data(chan, (void *)msg); 1228c2ecf20Sopenharmony_ci else 1238c2ecf20Sopenharmony_ci dev_warn_ratelimited(priv->dev, 1248c2ecf20Sopenharmony_ci "message's been dropped at ch[%d]\n", id); 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_ci /* Trigger to update outbox FIFO pointer */ 1278c2ecf20Sopenharmony_ci writel(0x1, priv->outbox_base + SPRD_MBOX_TRIGGER); 1288c2ecf20Sopenharmony_ci } 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Clear irq status after reading all message. */ 1318c2ecf20Sopenharmony_ci writel(SPRD_MBOX_IRQ_CLR, priv->outbox_base + SPRD_MBOX_IRQ_STS); 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cistatic irqreturn_t sprd_mbox_inbox_isr(int irq, void *data) 1378c2ecf20Sopenharmony_ci{ 1388c2ecf20Sopenharmony_ci struct sprd_mbox_priv *priv = data; 1398c2ecf20Sopenharmony_ci struct mbox_chan *chan; 1408c2ecf20Sopenharmony_ci u32 fifo_sts, send_sts, busy, id; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci fifo_sts = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS); 1438c2ecf20Sopenharmony_ci 1448c2ecf20Sopenharmony_ci /* Get the inbox data delivery status */ 1458c2ecf20Sopenharmony_ci send_sts = (fifo_sts & SPRD_INBOX_FIFO_DELIVER_MASK) >> 1468c2ecf20Sopenharmony_ci SPRD_INBOX_FIFO_DELIVER_SHIFT; 1478c2ecf20Sopenharmony_ci if (!send_sts) { 1488c2ecf20Sopenharmony_ci dev_warn_ratelimited(priv->dev, "spurious inbox interrupt\n"); 1498c2ecf20Sopenharmony_ci return IRQ_NONE; 1508c2ecf20Sopenharmony_ci } 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci while (send_sts) { 1538c2ecf20Sopenharmony_ci id = __ffs(send_sts); 1548c2ecf20Sopenharmony_ci send_sts &= (send_sts - 1); 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_ci chan = &priv->chan[id]; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci /* 1598c2ecf20Sopenharmony_ci * Check if the message was fetched by remote traget, if yes, 1608c2ecf20Sopenharmony_ci * that means the transmission has been completed. 1618c2ecf20Sopenharmony_ci */ 1628c2ecf20Sopenharmony_ci busy = fifo_sts & SPRD_INBOX_FIFO_BUSY_MASK; 1638c2ecf20Sopenharmony_ci if (!(busy & BIT(id))) 1648c2ecf20Sopenharmony_ci mbox_chan_txdone(chan, 0); 1658c2ecf20Sopenharmony_ci } 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci /* Clear FIFO delivery and overflow status */ 1688c2ecf20Sopenharmony_ci writel(fifo_sts & 1698c2ecf20Sopenharmony_ci (SPRD_INBOX_FIFO_DELIVER_MASK | SPRD_INBOX_FIFO_OVERLOW_MASK), 1708c2ecf20Sopenharmony_ci priv->inbox_base + SPRD_MBOX_FIFO_RST); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci /* Clear irq status */ 1738c2ecf20Sopenharmony_ci writel(SPRD_MBOX_IRQ_CLR, priv->inbox_base + SPRD_MBOX_IRQ_STS); 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1768c2ecf20Sopenharmony_ci} 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_cistatic int sprd_mbox_send_data(struct mbox_chan *chan, void *msg) 1798c2ecf20Sopenharmony_ci{ 1808c2ecf20Sopenharmony_ci struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 1818c2ecf20Sopenharmony_ci unsigned long id = (unsigned long)chan->con_priv; 1828c2ecf20Sopenharmony_ci u32 *data = msg; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci /* Write data into inbox FIFO, and only support 8 bytes every time */ 1858c2ecf20Sopenharmony_ci writel(data[0], priv->inbox_base + SPRD_MBOX_MSG_LOW); 1868c2ecf20Sopenharmony_ci writel(data[1], priv->inbox_base + SPRD_MBOX_MSG_HIGH); 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci /* Set target core id */ 1898c2ecf20Sopenharmony_ci writel(id, priv->inbox_base + SPRD_MBOX_ID); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci /* Trigger remote request */ 1928c2ecf20Sopenharmony_ci writel(0x1, priv->inbox_base + SPRD_MBOX_TRIGGER); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int sprd_mbox_flush(struct mbox_chan *chan, unsigned long timeout) 1988c2ecf20Sopenharmony_ci{ 1998c2ecf20Sopenharmony_ci struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 2008c2ecf20Sopenharmony_ci unsigned long id = (unsigned long)chan->con_priv; 2018c2ecf20Sopenharmony_ci u32 busy; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci timeout = jiffies + msecs_to_jiffies(timeout); 2048c2ecf20Sopenharmony_ci 2058c2ecf20Sopenharmony_ci while (time_before(jiffies, timeout)) { 2068c2ecf20Sopenharmony_ci busy = readl(priv->inbox_base + SPRD_MBOX_FIFO_STS) & 2078c2ecf20Sopenharmony_ci SPRD_INBOX_FIFO_BUSY_MASK; 2088c2ecf20Sopenharmony_ci if (!(busy & BIT(id))) { 2098c2ecf20Sopenharmony_ci mbox_chan_txdone(chan, 0); 2108c2ecf20Sopenharmony_ci return 0; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci udelay(1); 2148c2ecf20Sopenharmony_ci } 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ci return -ETIME; 2178c2ecf20Sopenharmony_ci} 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_cistatic int sprd_mbox_startup(struct mbox_chan *chan) 2208c2ecf20Sopenharmony_ci{ 2218c2ecf20Sopenharmony_ci struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 2228c2ecf20Sopenharmony_ci u32 val; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci mutex_lock(&priv->lock); 2258c2ecf20Sopenharmony_ci if (priv->refcnt++ == 0) { 2268c2ecf20Sopenharmony_ci /* Select outbox FIFO mode and reset the outbox FIFO status */ 2278c2ecf20Sopenharmony_ci writel(0x0, priv->outbox_base + SPRD_MBOX_FIFO_RST); 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci /* Enable inbox FIFO overflow and delivery interrupt */ 2308c2ecf20Sopenharmony_ci val = readl(priv->inbox_base + SPRD_MBOX_IRQ_MSK); 2318c2ecf20Sopenharmony_ci val &= ~(SPRD_INBOX_FIFO_OVERFLOW_IRQ | SPRD_INBOX_FIFO_DELIVER_IRQ); 2328c2ecf20Sopenharmony_ci writel(val, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci /* Enable outbox FIFO not empty interrupt */ 2358c2ecf20Sopenharmony_ci val = readl(priv->outbox_base + SPRD_MBOX_IRQ_MSK); 2368c2ecf20Sopenharmony_ci val &= ~SPRD_OUTBOX_FIFO_NOT_EMPTY_IRQ; 2378c2ecf20Sopenharmony_ci writel(val, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 2388c2ecf20Sopenharmony_ci } 2398c2ecf20Sopenharmony_ci mutex_unlock(&priv->lock); 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci return 0; 2428c2ecf20Sopenharmony_ci} 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_cistatic void sprd_mbox_shutdown(struct mbox_chan *chan) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci struct sprd_mbox_priv *priv = to_sprd_mbox_priv(chan->mbox); 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci mutex_lock(&priv->lock); 2498c2ecf20Sopenharmony_ci if (--priv->refcnt == 0) { 2508c2ecf20Sopenharmony_ci /* Disable inbox & outbox interrupt */ 2518c2ecf20Sopenharmony_ci writel(SPRD_INBOX_FIFO_IRQ_MASK, priv->inbox_base + SPRD_MBOX_IRQ_MSK); 2528c2ecf20Sopenharmony_ci writel(SPRD_OUTBOX_FIFO_IRQ_MASK, priv->outbox_base + SPRD_MBOX_IRQ_MSK); 2538c2ecf20Sopenharmony_ci } 2548c2ecf20Sopenharmony_ci mutex_unlock(&priv->lock); 2558c2ecf20Sopenharmony_ci} 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops sprd_mbox_ops = { 2588c2ecf20Sopenharmony_ci .send_data = sprd_mbox_send_data, 2598c2ecf20Sopenharmony_ci .flush = sprd_mbox_flush, 2608c2ecf20Sopenharmony_ci .startup = sprd_mbox_startup, 2618c2ecf20Sopenharmony_ci .shutdown = sprd_mbox_shutdown, 2628c2ecf20Sopenharmony_ci}; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_cistatic void sprd_mbox_disable(void *data) 2658c2ecf20Sopenharmony_ci{ 2668c2ecf20Sopenharmony_ci struct sprd_mbox_priv *priv = data; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci clk_disable_unprepare(priv->clk); 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci 2718c2ecf20Sopenharmony_cistatic int sprd_mbox_probe(struct platform_device *pdev) 2728c2ecf20Sopenharmony_ci{ 2738c2ecf20Sopenharmony_ci struct device *dev = &pdev->dev; 2748c2ecf20Sopenharmony_ci struct sprd_mbox_priv *priv; 2758c2ecf20Sopenharmony_ci int ret, inbox_irq, outbox_irq; 2768c2ecf20Sopenharmony_ci unsigned long id; 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); 2798c2ecf20Sopenharmony_ci if (!priv) 2808c2ecf20Sopenharmony_ci return -ENOMEM; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci priv->dev = dev; 2838c2ecf20Sopenharmony_ci mutex_init(&priv->lock); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci /* 2868c2ecf20Sopenharmony_ci * The Spreadtrum mailbox uses an inbox to send messages to the target 2878c2ecf20Sopenharmony_ci * core, and uses an outbox to receive messages from other cores. 2888c2ecf20Sopenharmony_ci * 2898c2ecf20Sopenharmony_ci * Thus the mailbox controller supplies 2 different register addresses 2908c2ecf20Sopenharmony_ci * and IRQ numbers for inbox and outbox. 2918c2ecf20Sopenharmony_ci */ 2928c2ecf20Sopenharmony_ci priv->inbox_base = devm_platform_ioremap_resource(pdev, 0); 2938c2ecf20Sopenharmony_ci if (IS_ERR(priv->inbox_base)) 2948c2ecf20Sopenharmony_ci return PTR_ERR(priv->inbox_base); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci priv->outbox_base = devm_platform_ioremap_resource(pdev, 1); 2978c2ecf20Sopenharmony_ci if (IS_ERR(priv->outbox_base)) 2988c2ecf20Sopenharmony_ci return PTR_ERR(priv->outbox_base); 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_ci priv->clk = devm_clk_get(dev, "enable"); 3018c2ecf20Sopenharmony_ci if (IS_ERR(priv->clk)) { 3028c2ecf20Sopenharmony_ci dev_err(dev, "failed to get mailbox clock\n"); 3038c2ecf20Sopenharmony_ci return PTR_ERR(priv->clk); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci ret = clk_prepare_enable(priv->clk); 3078c2ecf20Sopenharmony_ci if (ret) 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_ci ret = devm_add_action_or_reset(dev, sprd_mbox_disable, priv); 3118c2ecf20Sopenharmony_ci if (ret) { 3128c2ecf20Sopenharmony_ci dev_err(dev, "failed to add mailbox disable action\n"); 3138c2ecf20Sopenharmony_ci return ret; 3148c2ecf20Sopenharmony_ci } 3158c2ecf20Sopenharmony_ci 3168c2ecf20Sopenharmony_ci inbox_irq = platform_get_irq(pdev, 0); 3178c2ecf20Sopenharmony_ci if (inbox_irq < 0) 3188c2ecf20Sopenharmony_ci return inbox_irq; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, inbox_irq, sprd_mbox_inbox_isr, 3218c2ecf20Sopenharmony_ci IRQF_NO_SUSPEND, dev_name(dev), priv); 3228c2ecf20Sopenharmony_ci if (ret) { 3238c2ecf20Sopenharmony_ci dev_err(dev, "failed to request inbox IRQ: %d\n", ret); 3248c2ecf20Sopenharmony_ci return ret; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci outbox_irq = platform_get_irq(pdev, 1); 3288c2ecf20Sopenharmony_ci if (outbox_irq < 0) 3298c2ecf20Sopenharmony_ci return outbox_irq; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci ret = devm_request_irq(dev, outbox_irq, sprd_mbox_outbox_isr, 3328c2ecf20Sopenharmony_ci IRQF_NO_SUSPEND, dev_name(dev), priv); 3338c2ecf20Sopenharmony_ci if (ret) { 3348c2ecf20Sopenharmony_ci dev_err(dev, "failed to request outbox IRQ: %d\n", ret); 3358c2ecf20Sopenharmony_ci return ret; 3368c2ecf20Sopenharmony_ci } 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Get the default outbox FIFO depth */ 3398c2ecf20Sopenharmony_ci priv->outbox_fifo_depth = 3408c2ecf20Sopenharmony_ci readl(priv->outbox_base + SPRD_MBOX_FIFO_DEPTH) + 1; 3418c2ecf20Sopenharmony_ci priv->mbox.dev = dev; 3428c2ecf20Sopenharmony_ci priv->mbox.chans = &priv->chan[0]; 3438c2ecf20Sopenharmony_ci priv->mbox.num_chans = SPRD_MBOX_CHAN_MAX; 3448c2ecf20Sopenharmony_ci priv->mbox.ops = &sprd_mbox_ops; 3458c2ecf20Sopenharmony_ci priv->mbox.txdone_irq = true; 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci for (id = 0; id < SPRD_MBOX_CHAN_MAX; id++) 3488c2ecf20Sopenharmony_ci priv->chan[id].con_priv = (void *)id; 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_ci ret = devm_mbox_controller_register(dev, &priv->mbox); 3518c2ecf20Sopenharmony_ci if (ret) { 3528c2ecf20Sopenharmony_ci dev_err(dev, "failed to register mailbox: %d\n", ret); 3538c2ecf20Sopenharmony_ci return ret; 3548c2ecf20Sopenharmony_ci } 3558c2ecf20Sopenharmony_ci 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_cistatic const struct of_device_id sprd_mbox_of_match[] = { 3608c2ecf20Sopenharmony_ci { .compatible = "sprd,sc9860-mailbox", }, 3618c2ecf20Sopenharmony_ci { }, 3628c2ecf20Sopenharmony_ci}; 3638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, sprd_mbox_of_match); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_cistatic struct platform_driver sprd_mbox_driver = { 3668c2ecf20Sopenharmony_ci .driver = { 3678c2ecf20Sopenharmony_ci .name = "sprd-mailbox", 3688c2ecf20Sopenharmony_ci .of_match_table = sprd_mbox_of_match, 3698c2ecf20Sopenharmony_ci }, 3708c2ecf20Sopenharmony_ci .probe = sprd_mbox_probe, 3718c2ecf20Sopenharmony_ci}; 3728c2ecf20Sopenharmony_cimodule_platform_driver(sprd_mbox_driver); 3738c2ecf20Sopenharmony_ci 3748c2ecf20Sopenharmony_ciMODULE_AUTHOR("Baolin Wang <baolin.wang@unisoc.com>"); 3758c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Spreadtrum mailbox driver"); 3768c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 377