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
bcm74110_mbox_from_cntrl(struct mbox_controller * cntrl)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
bcm74110_rx_push_init_msg(struct bcm74110_mbox * mbox,u32 val)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
bcm74110_rx_process_msg(struct bcm74110_mbox * mbox)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
bcm74110_mbox_isr(int irq,void * data)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
bcm74110_mbox_mask_and_clear(struct bcm74110_mbox * mbox)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
bcm74110_rx_pop_init_msg(struct bcm74110_mbox * mbox,u32 func_type,u32 * val)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
bcm74110_rx_flush_msg(struct bcm74110_mbox * mbox)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
bcm74110_rx_pop_init_msg_block(struct bcm74110_mbox * mbox,u32 func_type,u32 * val)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
bcm74110_mbox_create_msg(int req,int rply,int svc,int func,int length,int slot)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
bcm74110_mbox_tx_msg(struct bcm74110_mbox * mbox,u32 msg)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
bcm74110_mbox_link_training(struct bcm74110_mbox * mbox)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
bcm74110_mbox_tx_msg_and_wait_ack(struct bcm74110_mbox * mbox,u32 msg)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
bcm74110_mbox_shmem_init(struct bcm74110_mbox * mbox)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
bcm74110_mbox_init(struct bcm74110_mbox * mbox)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
bcm74110_mbox_send_data(struct mbox_chan * chan,void * data)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;
4837fbb5a56SJiapeng Chong }
48452436007SJustin Chen
48552436007SJustin Chen return bcm74110_mbox_tx_msg(chan_priv->mbox, msg);
48652436007SJustin Chen }
48752436007SJustin Chen
bcm74110_mbox_chan_startup(struct mbox_chan * chan)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
bcm74110_mbox_chan_shutdown(struct mbox_chan * chan)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
bcm74110_mbox_shutdown(struct platform_device * pdev)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
bcm74110_mbox_of_xlate(struct mbox_controller * cntrl,const struct of_phandle_args * p)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);
5637fbb5a56SJiapeng Chong }
56452436007SJustin Chen
56552436007SJustin Chen return &cntrl->chans[type];
56652436007SJustin Chen }
56752436007SJustin Chen
bcm74110_mbox_probe(struct platform_device * pdev)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
654*75f1fbc9SColin Ian King MODULE_AUTHOR("Justin Chen <justin.chen@broadcom.com>");
65552436007SJustin Chen MODULE_DESCRIPTION("BCM74110 mailbox driver");
65652436007SJustin Chen MODULE_LICENSE("GPL");
657