152436007SJustin Chen // SPDX-License-Identifier: GPL-2.0 252436007SJustin Chen /* 352436007SJustin Chen * Broadcom BCM74110 Mailbox Driver 452436007SJustin Chen * 552436007SJustin Chen * Copyright (c) 2025 Broadcom 652436007SJustin Chen */ 752436007SJustin Chen #include <linux/list.h> 852436007SJustin Chen #include <linux/types.h> 952436007SJustin Chen #include <linux/workqueue.h> 1052436007SJustin Chen #include <linux/io-64-nonatomic-hi-lo.h> 1152436007SJustin Chen #include <linux/interrupt.h> 1252436007SJustin Chen #include <linux/module.h> 1352436007SJustin Chen #include <linux/platform_device.h> 1452436007SJustin Chen #include <linux/of.h> 1552436007SJustin Chen #include <linux/delay.h> 1652436007SJustin Chen #include <linux/mailbox_controller.h> 1752436007SJustin Chen #include <linux/bitfield.h> 1852436007SJustin Chen #include <linux/slab.h> 1952436007SJustin Chen 2052436007SJustin Chen #define BCM_MBOX_BASE(sel) ((sel) * 0x40) 2152436007SJustin Chen #define BCM_MBOX_IRQ_BASE(sel) (((sel) * 0x20) + 0x800) 2252436007SJustin Chen 2352436007SJustin Chen #define BCM_MBOX_CFGA 0x0 2452436007SJustin Chen #define BCM_MBOX_CFGB 0x4 2552436007SJustin Chen #define BCM_MBOX_CFGC 0x8 2652436007SJustin Chen #define BCM_MBOX_CFGD 0xc 2752436007SJustin Chen #define BCM_MBOX_CTRL 0x10 2852436007SJustin Chen #define BCM_MBOX_CTRL_EN BIT(0) 2952436007SJustin Chen #define BCM_MBOX_CTRL_CLR BIT(1) 3052436007SJustin Chen #define BCM_MBOX_STATUS0 0x14 3152436007SJustin Chen #define BCM_MBOX_STATUS0_NOT_EMPTY BIT(28) 3252436007SJustin Chen #define BCM_MBOX_STATUS0_FULL BIT(29) 3352436007SJustin Chen #define BCM_MBOX_STATUS1 0x18 3452436007SJustin Chen #define BCM_MBOX_STATUS2 0x1c 3552436007SJustin Chen #define BCM_MBOX_WDATA 0x20 3652436007SJustin Chen #define BCM_MBOX_RDATA 0x28 3752436007SJustin Chen 3852436007SJustin Chen #define BCM_MBOX_IRQ_STATUS 0x0 3952436007SJustin Chen #define BCM_MBOX_IRQ_SET 0x4 4052436007SJustin Chen #define BCM_MBOX_IRQ_CLEAR 0x8 4152436007SJustin Chen #define BCM_MBOX_IRQ_MASK_STATUS 0xc 4252436007SJustin Chen #define BCM_MBOX_IRQ_MASK_SET 0x10 4352436007SJustin Chen #define BCM_MBOX_IRQ_MASK_CLEAR 0x14 4452436007SJustin Chen #define BCM_MBOX_IRQ_TIMEOUT BIT(0) 4552436007SJustin Chen #define BCM_MBOX_IRQ_NOT_EMPTY BIT(1) 4652436007SJustin Chen #define BCM_MBOX_IRQ_FULL BIT(2) 4752436007SJustin Chen #define BCM_MBOX_IRQ_LOW_WM BIT(3) 4852436007SJustin Chen #define BCM_MBOX_IRQ_HIGH_WM BIT(4) 4952436007SJustin Chen 5052436007SJustin Chen #define BCM_LINK_CODE0 0xbe0 5152436007SJustin Chen #define BCM_LINK_CODE1 0xbe1 5252436007SJustin Chen #define BCM_LINK_CODE2 0xbe2 5352436007SJustin Chen 5452436007SJustin Chen enum { 5552436007SJustin Chen BCM_MSG_FUNC_LINK_START = 0, 5652436007SJustin Chen BCM_MSG_FUNC_LINK_STOP, 5752436007SJustin Chen BCM_MSG_FUNC_SHMEM_TX, 5852436007SJustin Chen BCM_MSG_FUNC_SHMEM_RX, 5952436007SJustin Chen BCM_MSG_FUNC_SHMEM_STOP, 6052436007SJustin Chen BCM_MSG_FUNC_MAX, 6152436007SJustin Chen }; 6252436007SJustin Chen 6352436007SJustin Chen enum { 6452436007SJustin Chen BCM_MSG_SVC_INIT = 0, 6552436007SJustin Chen BCM_MSG_SVC_PMC, 6652436007SJustin Chen BCM_MSG_SVC_SCMI, 6752436007SJustin Chen BCM_MSG_SVC_DPFE, 6852436007SJustin Chen BCM_MSG_SVC_MAX, 6952436007SJustin Chen }; 7052436007SJustin Chen 7152436007SJustin Chen struct bcm74110_mbox_msg { 7252436007SJustin Chen struct list_head list_entry; 7352436007SJustin Chen #define BCM_MSG_VERSION_MASK GENMASK(31, 29) 7452436007SJustin Chen #define BCM_MSG_VERSION 0x1 7552436007SJustin Chen #define BCM_MSG_REQ_MASK BIT(28) 7652436007SJustin Chen #define BCM_MSG_RPLY_MASK BIT(27) 7752436007SJustin Chen #define BCM_MSG_SVC_MASK GENMASK(26, 24) 7852436007SJustin Chen #define BCM_MSG_FUNC_MASK GENMASK(23, 16) 7952436007SJustin Chen #define BCM_MSG_LENGTH_MASK GENMASK(15, 4) 8052436007SJustin Chen #define BCM_MSG_SLOT_MASK GENMASK(3, 0) 8152436007SJustin Chen 8252436007SJustin Chen #define BCM_MSG_SET_FIELD(hdr, field, val) \ 8352436007SJustin Chen do { \ 8452436007SJustin Chen hdr &= ~BCM_MSG_##field##_MASK; \ 8552436007SJustin Chen hdr |= FIELD_PREP(BCM_MSG_##field##_MASK, val); \ 8652436007SJustin Chen } while (0) 8752436007SJustin Chen 8852436007SJustin Chen #define BCM_MSG_GET_FIELD(hdr, field) \ 8952436007SJustin Chen FIELD_GET(BCM_MSG_##field##_MASK, hdr) 9052436007SJustin Chen u32 msg; 9152436007SJustin Chen }; 9252436007SJustin Chen 9352436007SJustin Chen struct bcm74110_mbox_chan { 9452436007SJustin Chen struct bcm74110_mbox *mbox; 9552436007SJustin Chen bool en; 9652436007SJustin Chen int slot; 9752436007SJustin Chen int type; 9852436007SJustin Chen }; 9952436007SJustin Chen 10052436007SJustin Chen struct bcm74110_mbox { 10152436007SJustin Chen struct platform_device *pdev; 10252436007SJustin Chen void __iomem *base; 10352436007SJustin Chen 10452436007SJustin Chen int tx_chan; 10552436007SJustin Chen int rx_chan; 10652436007SJustin Chen int rx_irq; 10752436007SJustin Chen struct list_head rx_svc_init_list; 10852436007SJustin Chen spinlock_t rx_svc_list_lock; 10952436007SJustin Chen 11052436007SJustin Chen struct mbox_controller controller; 11152436007SJustin Chen struct bcm74110_mbox_chan *mbox_chan; 11252436007SJustin Chen }; 11352436007SJustin Chen 11452436007SJustin Chen #define BCM74110_OFFSET_IO_WRITEL_MACRO(name, offset_base) \ 11552436007SJustin Chen static void bcm74110_##name##_writel(struct bcm74110_mbox *mbox,\ 11652436007SJustin Chen u32 val, u32 off) \ 11752436007SJustin Chen { \ 11852436007SJustin Chen writel_relaxed(val, mbox->base + offset_base + off); \ 11952436007SJustin Chen } 12052436007SJustin Chen BCM74110_OFFSET_IO_WRITEL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan)); 12152436007SJustin Chen BCM74110_OFFSET_IO_WRITEL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan)); 12252436007SJustin Chen 12352436007SJustin Chen #define BCM74110_OFFSET_IO_READL_MACRO(name, offset_base) \ 12452436007SJustin Chen static u32 bcm74110_##name##_readl(struct bcm74110_mbox *mbox, \ 12552436007SJustin Chen u32 off) \ 12652436007SJustin Chen { \ 12752436007SJustin Chen return readl_relaxed(mbox->base + offset_base + off); \ 12852436007SJustin Chen } 12952436007SJustin Chen BCM74110_OFFSET_IO_READL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan)); 13052436007SJustin Chen BCM74110_OFFSET_IO_READL_MACRO(rx, BCM_MBOX_BASE(mbox->rx_chan)); 13152436007SJustin Chen BCM74110_OFFSET_IO_READL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan)); 13252436007SJustin Chen 13352436007SJustin Chen static inline struct bcm74110_mbox *bcm74110_mbox_from_cntrl( 13452436007SJustin Chen struct mbox_controller *cntrl) 13552436007SJustin Chen { 13652436007SJustin Chen return container_of(cntrl, struct bcm74110_mbox, controller); 13752436007SJustin Chen } 13852436007SJustin Chen 13952436007SJustin Chen static void bcm74110_rx_push_init_msg(struct bcm74110_mbox *mbox, u32 val) 14052436007SJustin Chen { 14152436007SJustin Chen struct bcm74110_mbox_msg *msg; 14252436007SJustin Chen 14352436007SJustin Chen msg = kzalloc(sizeof(*msg), GFP_ATOMIC); 14452436007SJustin Chen if (!msg) 14552436007SJustin Chen return; 14652436007SJustin Chen 14752436007SJustin Chen INIT_LIST_HEAD(&msg->list_entry); 14852436007SJustin Chen msg->msg = val; 14952436007SJustin Chen 15052436007SJustin Chen spin_lock(&mbox->rx_svc_list_lock); 15152436007SJustin Chen list_add_tail(&msg->list_entry, &mbox->rx_svc_init_list); 15252436007SJustin Chen spin_unlock(&mbox->rx_svc_list_lock); 15352436007SJustin Chen } 15452436007SJustin Chen 15552436007SJustin Chen static void bcm74110_rx_process_msg(struct bcm74110_mbox *mbox) 15652436007SJustin Chen { 15752436007SJustin Chen struct device *dev = &mbox->pdev->dev; 15852436007SJustin Chen struct bcm74110_mbox_chan *chan_priv; 15952436007SJustin Chen struct mbox_chan *chan; 16052436007SJustin Chen u32 msg, status; 16152436007SJustin Chen int type; 16252436007SJustin Chen 16352436007SJustin Chen do { 16452436007SJustin Chen msg = bcm74110_rx_readl(mbox, BCM_MBOX_RDATA); 16552436007SJustin Chen status = bcm74110_rx_readl(mbox, BCM_MBOX_STATUS0); 16652436007SJustin Chen 16752436007SJustin Chen dev_dbg(dev, "rx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n", 16852436007SJustin Chen BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY), 16952436007SJustin Chen BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC), 17052436007SJustin Chen BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT)); 17152436007SJustin Chen 17252436007SJustin Chen type = BCM_MSG_GET_FIELD(msg, SVC); 17352436007SJustin Chen switch (type) { 17452436007SJustin Chen case BCM_MSG_SVC_INIT: 17552436007SJustin Chen bcm74110_rx_push_init_msg(mbox, msg); 17652436007SJustin Chen break; 17752436007SJustin Chen case BCM_MSG_SVC_PMC: 17852436007SJustin Chen case BCM_MSG_SVC_SCMI: 17952436007SJustin Chen case BCM_MSG_SVC_DPFE: 18052436007SJustin Chen chan = &mbox->controller.chans[type]; 18152436007SJustin Chen chan_priv = chan->con_priv; 18252436007SJustin Chen if (chan_priv->en) 18352436007SJustin Chen mbox_chan_received_data(chan, NULL); 18452436007SJustin Chen else 18552436007SJustin Chen dev_warn(dev, "Channel not enabled\n"); 18652436007SJustin Chen break; 18752436007SJustin Chen default: 18852436007SJustin Chen dev_warn(dev, "Unsupported msg received\n"); 18952436007SJustin Chen } 19052436007SJustin Chen } while (status & BCM_MBOX_STATUS0_NOT_EMPTY); 19152436007SJustin Chen } 19252436007SJustin Chen 19352436007SJustin Chen static irqreturn_t bcm74110_mbox_isr(int irq, void *data) 19452436007SJustin Chen { 19552436007SJustin Chen struct bcm74110_mbox *mbox = data; 19652436007SJustin Chen u32 status; 19752436007SJustin Chen 19852436007SJustin Chen status = bcm74110_irq_readl(mbox, BCM_MBOX_IRQ_STATUS); 19952436007SJustin Chen 20052436007SJustin Chen bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR); 20152436007SJustin Chen 20252436007SJustin Chen if (status & BCM_MBOX_IRQ_NOT_EMPTY) 20352436007SJustin Chen bcm74110_rx_process_msg(mbox); 20452436007SJustin Chen else 20552436007SJustin Chen dev_warn(&mbox->pdev->dev, "Spurious interrupt\n"); 20652436007SJustin Chen 20752436007SJustin Chen return IRQ_HANDLED; 20852436007SJustin Chen } 20952436007SJustin Chen 21052436007SJustin Chen static void bcm74110_mbox_mask_and_clear(struct bcm74110_mbox *mbox) 21152436007SJustin Chen { 21252436007SJustin Chen bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_MASK_SET); 21352436007SJustin Chen bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR); 21452436007SJustin Chen } 21552436007SJustin Chen 21652436007SJustin Chen static int bcm74110_rx_pop_init_msg(struct bcm74110_mbox *mbox, u32 func_type, 21752436007SJustin Chen u32 *val) 21852436007SJustin Chen { 21952436007SJustin Chen struct bcm74110_mbox_msg *msg, *msg_tmp; 22052436007SJustin Chen unsigned long flags; 22152436007SJustin Chen bool found = false; 22252436007SJustin Chen 22352436007SJustin Chen spin_lock_irqsave(&mbox->rx_svc_list_lock, flags); 22452436007SJustin Chen list_for_each_entry_safe(msg, msg_tmp, &mbox->rx_svc_init_list, 22552436007SJustin Chen list_entry) { 22652436007SJustin Chen if (BCM_MSG_GET_FIELD(msg->msg, FUNC) == func_type) { 22752436007SJustin Chen list_del(&msg->list_entry); 22852436007SJustin Chen found = true; 22952436007SJustin Chen break; 23052436007SJustin Chen } 23152436007SJustin Chen } 23252436007SJustin Chen spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags); 23352436007SJustin Chen 23452436007SJustin Chen if (!found) 23552436007SJustin Chen return -EINVAL; 23652436007SJustin Chen 23752436007SJustin Chen *val = msg->msg; 23852436007SJustin Chen kfree(msg); 23952436007SJustin Chen 24052436007SJustin Chen return 0; 24152436007SJustin Chen } 24252436007SJustin Chen 24352436007SJustin Chen static void bcm74110_rx_flush_msg(struct bcm74110_mbox *mbox) 24452436007SJustin Chen { 24552436007SJustin Chen struct bcm74110_mbox_msg *msg, *msg_tmp; 24652436007SJustin Chen LIST_HEAD(list_temp); 24752436007SJustin Chen unsigned long flags; 24852436007SJustin Chen 24952436007SJustin Chen spin_lock_irqsave(&mbox->rx_svc_list_lock, flags); 25052436007SJustin Chen list_splice_init(&mbox->rx_svc_init_list, &list_temp); 25152436007SJustin Chen spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags); 25252436007SJustin Chen 25352436007SJustin Chen list_for_each_entry_safe(msg, msg_tmp, &list_temp, list_entry) { 25452436007SJustin Chen list_del(&msg->list_entry); 25552436007SJustin Chen kfree(msg); 25652436007SJustin Chen } 25752436007SJustin Chen } 25852436007SJustin Chen 25952436007SJustin Chen #define BCM_DEQUEUE_TIMEOUT_MS 30 26052436007SJustin Chen static int bcm74110_rx_pop_init_msg_block(struct bcm74110_mbox *mbox, u32 func_type, 26152436007SJustin Chen u32 *val) 26252436007SJustin Chen { 26352436007SJustin Chen int ret, timeout = 0; 26452436007SJustin Chen 26552436007SJustin Chen do { 26652436007SJustin Chen ret = bcm74110_rx_pop_init_msg(mbox, func_type, val); 26752436007SJustin Chen 26852436007SJustin Chen if (!ret) 26952436007SJustin Chen return 0; 27052436007SJustin Chen 27152436007SJustin Chen /* TODO: Figure out what is a good sleep here. */ 27252436007SJustin Chen usleep_range(1000, 2000); 27352436007SJustin Chen timeout++; 27452436007SJustin Chen } while (timeout < BCM_DEQUEUE_TIMEOUT_MS); 27552436007SJustin Chen 27652436007SJustin Chen dev_warn(&mbox->pdev->dev, "Timeout waiting for service init response\n"); 27752436007SJustin Chen return -ETIMEDOUT; 27852436007SJustin Chen } 27952436007SJustin Chen 28052436007SJustin Chen static int bcm74110_mbox_create_msg(int req, int rply, int svc, int func, 28152436007SJustin Chen int length, int slot) 28252436007SJustin Chen { 28352436007SJustin Chen u32 msg = 0; 28452436007SJustin Chen 28552436007SJustin Chen BCM_MSG_SET_FIELD(msg, REQ, req); 28652436007SJustin Chen BCM_MSG_SET_FIELD(msg, RPLY, rply); 28752436007SJustin Chen BCM_MSG_SET_FIELD(msg, SVC, svc); 28852436007SJustin Chen BCM_MSG_SET_FIELD(msg, FUNC, func); 28952436007SJustin Chen BCM_MSG_SET_FIELD(msg, LENGTH, length); 29052436007SJustin Chen BCM_MSG_SET_FIELD(msg, SLOT, slot); 29152436007SJustin Chen 29252436007SJustin Chen return msg; 29352436007SJustin Chen } 29452436007SJustin Chen 29552436007SJustin Chen static int bcm74110_mbox_tx_msg(struct bcm74110_mbox *mbox, u32 msg) 29652436007SJustin Chen { 29752436007SJustin Chen int val; 29852436007SJustin Chen 29952436007SJustin Chen /* We can potentially poll with timeout here instead */ 30052436007SJustin Chen val = bcm74110_tx_readl(mbox, BCM_MBOX_STATUS0); 30152436007SJustin Chen if (val & BCM_MBOX_STATUS0_FULL) { 30252436007SJustin Chen dev_err(&mbox->pdev->dev, "Mailbox full\n"); 30352436007SJustin Chen return -EINVAL; 30452436007SJustin Chen } 30552436007SJustin Chen 30652436007SJustin Chen dev_dbg(&mbox->pdev->dev, "tx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n", 30752436007SJustin Chen BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY), 30852436007SJustin Chen BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC), 30952436007SJustin Chen BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT)); 31052436007SJustin Chen 31152436007SJustin Chen bcm74110_tx_writel(mbox, msg, BCM_MBOX_WDATA); 31252436007SJustin Chen 31352436007SJustin Chen return 0; 31452436007SJustin Chen } 31552436007SJustin Chen 31652436007SJustin Chen #define BCM_MBOX_LINK_TRAINING_RETRIES 5 31752436007SJustin Chen static int bcm74110_mbox_link_training(struct bcm74110_mbox *mbox) 31852436007SJustin Chen { 31952436007SJustin Chen int ret, retries = 0; 32052436007SJustin Chen u32 msg = 0, orig_len = 0, len = BCM_LINK_CODE0; 32152436007SJustin Chen 32252436007SJustin Chen do { 32352436007SJustin Chen switch (len) { 32452436007SJustin Chen case 0: 32552436007SJustin Chen retries++; 32652436007SJustin Chen dev_warn(&mbox->pdev->dev, 32752436007SJustin Chen "Link train failed, trying again... %d\n", 32852436007SJustin Chen retries); 32952436007SJustin Chen if (retries > BCM_MBOX_LINK_TRAINING_RETRIES) 33052436007SJustin Chen return -EINVAL; 33152436007SJustin Chen len = BCM_LINK_CODE0; 33252436007SJustin Chen fallthrough; 33352436007SJustin Chen case BCM_LINK_CODE0: 33452436007SJustin Chen case BCM_LINK_CODE1: 33552436007SJustin Chen case BCM_LINK_CODE2: 33652436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 33752436007SJustin Chen BCM_MSG_FUNC_LINK_START, 33852436007SJustin Chen len, BCM_MSG_SVC_INIT); 33952436007SJustin Chen break; 34052436007SJustin Chen default: 34152436007SJustin Chen break; 34252436007SJustin Chen } 34352436007SJustin Chen 34452436007SJustin Chen bcm74110_mbox_tx_msg(mbox, msg); 34552436007SJustin Chen 34652436007SJustin Chen /* No response expected for LINK_CODE2 */ 34752436007SJustin Chen if (len == BCM_LINK_CODE2) 34852436007SJustin Chen return 0; 34952436007SJustin Chen 35052436007SJustin Chen orig_len = len; 35152436007SJustin Chen 35252436007SJustin Chen ret = bcm74110_rx_pop_init_msg_block(mbox, 35352436007SJustin Chen BCM_MSG_GET_FIELD(msg, FUNC), 35452436007SJustin Chen &msg); 35552436007SJustin Chen if (ret) { 35652436007SJustin Chen len = 0; 35752436007SJustin Chen continue; 35852436007SJustin Chen } 35952436007SJustin Chen 36052436007SJustin Chen if ((BCM_MSG_GET_FIELD(msg, SVC) != BCM_MSG_SVC_INIT) || 36152436007SJustin Chen (BCM_MSG_GET_FIELD(msg, FUNC) != BCM_MSG_FUNC_LINK_START) || 36252436007SJustin Chen (BCM_MSG_GET_FIELD(msg, SLOT) != 0) || 36352436007SJustin Chen (BCM_MSG_GET_FIELD(msg, RPLY) != 1) || 36452436007SJustin Chen (BCM_MSG_GET_FIELD(msg, REQ) != 0)) { 36552436007SJustin Chen len = 0; 36652436007SJustin Chen continue; 36752436007SJustin Chen } 36852436007SJustin Chen 36952436007SJustin Chen len = BCM_MSG_GET_FIELD(msg, LENGTH); 37052436007SJustin Chen 37152436007SJustin Chen /* Make sure sequence is good */ 37252436007SJustin Chen if (len != (orig_len + 1)) { 37352436007SJustin Chen len = 0; 37452436007SJustin Chen continue; 37552436007SJustin Chen } 37652436007SJustin Chen } while (1); 37752436007SJustin Chen 37852436007SJustin Chen return -EINVAL; 37952436007SJustin Chen } 38052436007SJustin Chen 38152436007SJustin Chen static int bcm74110_mbox_tx_msg_and_wait_ack(struct bcm74110_mbox *mbox, u32 msg) 38252436007SJustin Chen { 38352436007SJustin Chen int ret; 38452436007SJustin Chen u32 recv_msg; 38552436007SJustin Chen 38652436007SJustin Chen ret = bcm74110_mbox_tx_msg(mbox, msg); 38752436007SJustin Chen if (ret) 38852436007SJustin Chen return ret; 38952436007SJustin Chen 39052436007SJustin Chen ret = bcm74110_rx_pop_init_msg_block(mbox, BCM_MSG_GET_FIELD(msg, FUNC), 39152436007SJustin Chen &recv_msg); 39252436007SJustin Chen if (ret) 39352436007SJustin Chen return ret; 39452436007SJustin Chen 39552436007SJustin Chen /* 39652436007SJustin Chen * Modify tx message to verify rx ack. 39752436007SJustin Chen * Flip RPLY/REQ for synchronous messages 39852436007SJustin Chen */ 39952436007SJustin Chen if (BCM_MSG_GET_FIELD(msg, REQ) == 1) { 40052436007SJustin Chen BCM_MSG_SET_FIELD(msg, RPLY, 1); 40152436007SJustin Chen BCM_MSG_SET_FIELD(msg, REQ, 0); 40252436007SJustin Chen } 40352436007SJustin Chen 40452436007SJustin Chen if (msg != recv_msg) { 40552436007SJustin Chen dev_err(&mbox->pdev->dev, "Found ack, but ack is invalid\n"); 40652436007SJustin Chen return -EINVAL; 40752436007SJustin Chen } 40852436007SJustin Chen 40952436007SJustin Chen return 0; 41052436007SJustin Chen } 41152436007SJustin Chen 41252436007SJustin Chen /* Each index points to 0x100 of HAB MEM. IDX size counts from 0 */ 41352436007SJustin Chen #define BCM_MBOX_HAB_MEM_IDX_START 0x30 41452436007SJustin Chen #define BCM_MBOX_HAB_MEM_IDX_SIZE 0x0 41552436007SJustin Chen static int bcm74110_mbox_shmem_init(struct bcm74110_mbox *mbox) 41652436007SJustin Chen { 41752436007SJustin Chen u32 msg = 0; 41852436007SJustin Chen int ret; 41952436007SJustin Chen 42052436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 42152436007SJustin Chen BCM_MSG_FUNC_SHMEM_STOP, 42252436007SJustin Chen 0, BCM_MSG_SVC_INIT); 42352436007SJustin Chen ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 42452436007SJustin Chen if (ret) 42552436007SJustin Chen return -EINVAL; 42652436007SJustin Chen 42752436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 42852436007SJustin Chen BCM_MSG_FUNC_SHMEM_TX, 42952436007SJustin Chen BCM_MBOX_HAB_MEM_IDX_START, 43052436007SJustin Chen BCM_MBOX_HAB_MEM_IDX_SIZE); 43152436007SJustin Chen ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 43252436007SJustin Chen if (ret) 43352436007SJustin Chen return -EINVAL; 43452436007SJustin Chen 43552436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 43652436007SJustin Chen BCM_MSG_FUNC_SHMEM_RX, 43752436007SJustin Chen BCM_MBOX_HAB_MEM_IDX_START, 43852436007SJustin Chen BCM_MBOX_HAB_MEM_IDX_SIZE); 43952436007SJustin Chen ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 44052436007SJustin Chen if (ret) 44152436007SJustin Chen return -EINVAL; 44252436007SJustin Chen 44352436007SJustin Chen return 0; 44452436007SJustin Chen } 44552436007SJustin Chen 44652436007SJustin Chen static int bcm74110_mbox_init(struct bcm74110_mbox *mbox) 44752436007SJustin Chen { 44852436007SJustin Chen int ret = 0; 44952436007SJustin Chen 45052436007SJustin Chen /* Disable queues tx/rx */ 45152436007SJustin Chen bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL); 45252436007SJustin Chen 45352436007SJustin Chen /* Clear status & restart tx/rx*/ 45452436007SJustin Chen bcm74110_tx_writel(mbox, BCM_MBOX_CTRL_EN | BCM_MBOX_CTRL_CLR, 45552436007SJustin Chen BCM_MBOX_CTRL); 45652436007SJustin Chen 45752436007SJustin Chen /* Unmask irq */ 45852436007SJustin Chen bcm74110_irq_writel(mbox, BCM_MBOX_IRQ_NOT_EMPTY, BCM_MBOX_IRQ_MASK_CLEAR); 45952436007SJustin Chen 46052436007SJustin Chen ret = bcm74110_mbox_link_training(mbox); 46152436007SJustin Chen if (ret) { 46252436007SJustin Chen dev_err(&mbox->pdev->dev, "Training failed\n"); 46352436007SJustin Chen return ret; 46452436007SJustin Chen } 46552436007SJustin Chen 46652436007SJustin Chen return bcm74110_mbox_shmem_init(mbox); 46752436007SJustin Chen } 46852436007SJustin Chen 46952436007SJustin Chen static int bcm74110_mbox_send_data(struct mbox_chan *chan, void *data) 47052436007SJustin Chen { 47152436007SJustin Chen struct bcm74110_mbox_chan *chan_priv = chan->con_priv; 47252436007SJustin Chen u32 msg; 47352436007SJustin Chen 47452436007SJustin Chen switch (chan_priv->type) { 47552436007SJustin Chen case BCM_MSG_SVC_PMC: 47652436007SJustin Chen case BCM_MSG_SVC_SCMI: 47752436007SJustin Chen case BCM_MSG_SVC_DPFE: 47852436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, chan_priv->type, 0, 47952436007SJustin Chen 128 + 28, chan_priv->slot); 48052436007SJustin Chen break; 48152436007SJustin Chen default: 48252436007SJustin Chen return -EINVAL; 483*7fbb5a56SJiapeng Chong } 48452436007SJustin Chen 48552436007SJustin Chen return bcm74110_mbox_tx_msg(chan_priv->mbox, msg); 48652436007SJustin Chen } 48752436007SJustin Chen 48852436007SJustin Chen static int bcm74110_mbox_chan_startup(struct mbox_chan *chan) 48952436007SJustin Chen { 49052436007SJustin Chen struct bcm74110_mbox_chan *chan_priv = chan->con_priv; 49152436007SJustin Chen 49252436007SJustin Chen chan_priv->en = true; 49352436007SJustin Chen 49452436007SJustin Chen return 0; 49552436007SJustin Chen } 49652436007SJustin Chen 49752436007SJustin Chen static void bcm74110_mbox_chan_shutdown(struct mbox_chan *chan) 49852436007SJustin Chen { 49952436007SJustin Chen struct bcm74110_mbox_chan *chan_priv = chan->con_priv; 50052436007SJustin Chen 50152436007SJustin Chen chan_priv->en = false; 50252436007SJustin Chen } 50352436007SJustin Chen 50452436007SJustin Chen static const struct mbox_chan_ops bcm74110_mbox_chan_ops = { 50552436007SJustin Chen .send_data = bcm74110_mbox_send_data, 50652436007SJustin Chen .startup = bcm74110_mbox_chan_startup, 50752436007SJustin Chen .shutdown = bcm74110_mbox_chan_shutdown, 50852436007SJustin Chen }; 50952436007SJustin Chen 51052436007SJustin Chen static void bcm74110_mbox_shutdown(struct platform_device *pdev) 51152436007SJustin Chen { 51252436007SJustin Chen struct bcm74110_mbox *mbox = dev_get_drvdata(&pdev->dev); 51352436007SJustin Chen u32 msg; 51452436007SJustin Chen 51552436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 51652436007SJustin Chen BCM_MSG_FUNC_LINK_STOP, 51752436007SJustin Chen 0, 0); 51852436007SJustin Chen 51952436007SJustin Chen bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 52052436007SJustin Chen 52152436007SJustin Chen /* Even if we don't receive ACK, lets shut it down */ 52252436007SJustin Chen 52352436007SJustin Chen bcm74110_mbox_mask_and_clear(mbox); 52452436007SJustin Chen 52552436007SJustin Chen /* Disable queues tx/rx */ 52652436007SJustin Chen bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL); 52752436007SJustin Chen 52852436007SJustin Chen /* Flush queues */ 52952436007SJustin Chen bcm74110_rx_flush_msg(mbox); 53052436007SJustin Chen } 53152436007SJustin Chen 53252436007SJustin Chen static struct mbox_chan *bcm74110_mbox_of_xlate(struct mbox_controller *cntrl, 53352436007SJustin Chen const struct of_phandle_args *p) 53452436007SJustin Chen { 53552436007SJustin Chen struct bcm74110_mbox *mbox = bcm74110_mbox_from_cntrl(cntrl); 53652436007SJustin Chen struct device *dev = &mbox->pdev->dev; 53752436007SJustin Chen struct bcm74110_mbox_chan *chan_priv; 53852436007SJustin Chen int slot, type; 53952436007SJustin Chen 54052436007SJustin Chen if (p->args_count != 2) { 54152436007SJustin Chen dev_err(dev, "Invalid arguments\n"); 54252436007SJustin Chen return ERR_PTR(-EINVAL); 54352436007SJustin Chen } 54452436007SJustin Chen 54552436007SJustin Chen type = p->args[0]; 54652436007SJustin Chen slot = p->args[1]; 54752436007SJustin Chen 54852436007SJustin Chen switch (type) { 54952436007SJustin Chen case BCM_MSG_SVC_PMC: 55052436007SJustin Chen case BCM_MSG_SVC_SCMI: 55152436007SJustin Chen case BCM_MSG_SVC_DPFE: 55252436007SJustin Chen if (slot > BCM_MBOX_HAB_MEM_IDX_SIZE) { 55352436007SJustin Chen dev_err(dev, "Not enough shared memory\n"); 55452436007SJustin Chen return ERR_PTR(-EINVAL); 55552436007SJustin Chen } 55652436007SJustin Chen chan_priv = cntrl->chans[type].con_priv; 55752436007SJustin Chen chan_priv->slot = slot; 55852436007SJustin Chen chan_priv->type = type; 55952436007SJustin Chen break; 56052436007SJustin Chen default: 56152436007SJustin Chen dev_err(dev, "Invalid channel type: %d\n", type); 56252436007SJustin Chen return ERR_PTR(-EINVAL); 563*7fbb5a56SJiapeng Chong } 56452436007SJustin Chen 56552436007SJustin Chen return &cntrl->chans[type]; 56652436007SJustin Chen } 56752436007SJustin Chen 56852436007SJustin Chen static int bcm74110_mbox_probe(struct platform_device *pdev) 56952436007SJustin Chen { 57052436007SJustin Chen struct device *dev = &pdev->dev; 57152436007SJustin Chen struct bcm74110_mbox *mbox; 57252436007SJustin Chen int i, ret; 57352436007SJustin Chen 57452436007SJustin Chen mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); 57552436007SJustin Chen if (!mbox) 57652436007SJustin Chen return -ENOMEM; 57752436007SJustin Chen 57852436007SJustin Chen mbox->pdev = pdev; 57952436007SJustin Chen platform_set_drvdata(pdev, mbox); 58052436007SJustin Chen 58152436007SJustin Chen mbox->base = devm_platform_ioremap_resource(pdev, 0); 58252436007SJustin Chen if (IS_ERR(mbox->base)) 58352436007SJustin Chen return dev_err_probe(dev, PTR_ERR(mbox->base), "Failed to iomap\n"); 58452436007SJustin Chen 58552436007SJustin Chen ret = of_property_read_u32(dev->of_node, "brcm,tx", &mbox->tx_chan); 58652436007SJustin Chen if (ret) 58752436007SJustin Chen return dev_err_probe(dev, ret, "Failed to find tx channel\n"); 58852436007SJustin Chen 58952436007SJustin Chen ret = of_property_read_u32(dev->of_node, "brcm,rx", &mbox->rx_chan); 59052436007SJustin Chen if (ret) 59152436007SJustin Chen return dev_err_probe(dev, ret, "Failed to find rx channel\n"); 59252436007SJustin Chen 59352436007SJustin Chen mbox->rx_irq = platform_get_irq(pdev, 0); 59452436007SJustin Chen if (mbox->rx_irq < 0) 59552436007SJustin Chen return mbox->rx_irq; 59652436007SJustin Chen 59752436007SJustin Chen INIT_LIST_HEAD(&mbox->rx_svc_init_list); 59852436007SJustin Chen spin_lock_init(&mbox->rx_svc_list_lock); 59952436007SJustin Chen bcm74110_mbox_mask_and_clear(mbox); 60052436007SJustin Chen 60152436007SJustin Chen ret = devm_request_irq(dev, mbox->rx_irq, bcm74110_mbox_isr, 60252436007SJustin Chen IRQF_NO_SUSPEND, pdev->name, mbox); 60352436007SJustin Chen if (ret) 60452436007SJustin Chen return dev_err_probe(dev, ret, "Failed to request irq\n"); 60552436007SJustin Chen 60652436007SJustin Chen mbox->controller.ops = &bcm74110_mbox_chan_ops; 60752436007SJustin Chen mbox->controller.dev = dev; 60852436007SJustin Chen mbox->controller.num_chans = BCM_MSG_SVC_MAX; 60952436007SJustin Chen mbox->controller.of_xlate = &bcm74110_mbox_of_xlate; 61052436007SJustin Chen mbox->controller.chans = devm_kcalloc(dev, BCM_MSG_SVC_MAX, 61152436007SJustin Chen sizeof(*mbox->controller.chans), 61252436007SJustin Chen GFP_KERNEL); 61352436007SJustin Chen if (!mbox->controller.chans) 61452436007SJustin Chen return -ENOMEM; 61552436007SJustin Chen 61652436007SJustin Chen mbox->mbox_chan = devm_kcalloc(dev, BCM_MSG_SVC_MAX, 61752436007SJustin Chen sizeof(*mbox->mbox_chan), 61852436007SJustin Chen GFP_KERNEL); 61952436007SJustin Chen if (!mbox->mbox_chan) 62052436007SJustin Chen return -ENOMEM; 62152436007SJustin Chen 62252436007SJustin Chen for (i = 0; i < BCM_MSG_SVC_MAX; i++) { 62352436007SJustin Chen mbox->mbox_chan[i].mbox = mbox; 62452436007SJustin Chen mbox->controller.chans[i].con_priv = &mbox->mbox_chan[i]; 62552436007SJustin Chen } 62652436007SJustin Chen 62752436007SJustin Chen ret = devm_mbox_controller_register(dev, &mbox->controller); 62852436007SJustin Chen if (ret) 62952436007SJustin Chen return ret; 63052436007SJustin Chen 63152436007SJustin Chen ret = bcm74110_mbox_init(mbox); 63252436007SJustin Chen if (ret) 63352436007SJustin Chen return ret; 63452436007SJustin Chen 63552436007SJustin Chen return 0; 63652436007SJustin Chen } 63752436007SJustin Chen 63852436007SJustin Chen static const struct of_device_id bcm74110_mbox_of_match[] = { 63952436007SJustin Chen { .compatible = "brcm,bcm74110-mbox", }, 64052436007SJustin Chen { /* sentinel */ }, 64152436007SJustin Chen }; 64252436007SJustin Chen MODULE_DEVICE_TABLE(of, bcm74110_mbox_of_match); 64352436007SJustin Chen 64452436007SJustin Chen static struct platform_driver bcm74110_mbox_driver = { 64552436007SJustin Chen .driver = { 64652436007SJustin Chen .name = "bcm74110-mbox", 64752436007SJustin Chen .of_match_table = bcm74110_mbox_of_match, 64852436007SJustin Chen }, 64952436007SJustin Chen .probe = bcm74110_mbox_probe, 65052436007SJustin Chen .shutdown = bcm74110_mbox_shutdown, 65152436007SJustin Chen }; 65252436007SJustin Chen module_platform_driver(bcm74110_mbox_driver); 65352436007SJustin Chen 65452436007SJustin Chen MODULE_AUTHOR("Justin Chen <justin.chen@braodcom.com>"); 65552436007SJustin Chen MODULE_DESCRIPTION("BCM74110 mailbox driver"); 65652436007SJustin Chen MODULE_LICENSE("GPL"); 657