1*52436007SJustin Chen // SPDX-License-Identifier: GPL-2.0 2*52436007SJustin Chen /* 3*52436007SJustin Chen * Broadcom BCM74110 Mailbox Driver 4*52436007SJustin Chen * 5*52436007SJustin Chen * Copyright (c) 2025 Broadcom 6*52436007SJustin Chen */ 7*52436007SJustin Chen #include <linux/list.h> 8*52436007SJustin Chen #include <linux/types.h> 9*52436007SJustin Chen #include <linux/workqueue.h> 10*52436007SJustin Chen #include <linux/io-64-nonatomic-hi-lo.h> 11*52436007SJustin Chen #include <linux/interrupt.h> 12*52436007SJustin Chen #include <linux/module.h> 13*52436007SJustin Chen #include <linux/platform_device.h> 14*52436007SJustin Chen #include <linux/of.h> 15*52436007SJustin Chen #include <linux/delay.h> 16*52436007SJustin Chen #include <linux/mailbox_controller.h> 17*52436007SJustin Chen #include <linux/bitfield.h> 18*52436007SJustin Chen #include <linux/slab.h> 19*52436007SJustin Chen 20*52436007SJustin Chen #define BCM_MBOX_BASE(sel) ((sel) * 0x40) 21*52436007SJustin Chen #define BCM_MBOX_IRQ_BASE(sel) (((sel) * 0x20) + 0x800) 22*52436007SJustin Chen 23*52436007SJustin Chen #define BCM_MBOX_CFGA 0x0 24*52436007SJustin Chen #define BCM_MBOX_CFGB 0x4 25*52436007SJustin Chen #define BCM_MBOX_CFGC 0x8 26*52436007SJustin Chen #define BCM_MBOX_CFGD 0xc 27*52436007SJustin Chen #define BCM_MBOX_CTRL 0x10 28*52436007SJustin Chen #define BCM_MBOX_CTRL_EN BIT(0) 29*52436007SJustin Chen #define BCM_MBOX_CTRL_CLR BIT(1) 30*52436007SJustin Chen #define BCM_MBOX_STATUS0 0x14 31*52436007SJustin Chen #define BCM_MBOX_STATUS0_NOT_EMPTY BIT(28) 32*52436007SJustin Chen #define BCM_MBOX_STATUS0_FULL BIT(29) 33*52436007SJustin Chen #define BCM_MBOX_STATUS1 0x18 34*52436007SJustin Chen #define BCM_MBOX_STATUS2 0x1c 35*52436007SJustin Chen #define BCM_MBOX_WDATA 0x20 36*52436007SJustin Chen #define BCM_MBOX_RDATA 0x28 37*52436007SJustin Chen 38*52436007SJustin Chen #define BCM_MBOX_IRQ_STATUS 0x0 39*52436007SJustin Chen #define BCM_MBOX_IRQ_SET 0x4 40*52436007SJustin Chen #define BCM_MBOX_IRQ_CLEAR 0x8 41*52436007SJustin Chen #define BCM_MBOX_IRQ_MASK_STATUS 0xc 42*52436007SJustin Chen #define BCM_MBOX_IRQ_MASK_SET 0x10 43*52436007SJustin Chen #define BCM_MBOX_IRQ_MASK_CLEAR 0x14 44*52436007SJustin Chen #define BCM_MBOX_IRQ_TIMEOUT BIT(0) 45*52436007SJustin Chen #define BCM_MBOX_IRQ_NOT_EMPTY BIT(1) 46*52436007SJustin Chen #define BCM_MBOX_IRQ_FULL BIT(2) 47*52436007SJustin Chen #define BCM_MBOX_IRQ_LOW_WM BIT(3) 48*52436007SJustin Chen #define BCM_MBOX_IRQ_HIGH_WM BIT(4) 49*52436007SJustin Chen 50*52436007SJustin Chen #define BCM_LINK_CODE0 0xbe0 51*52436007SJustin Chen #define BCM_LINK_CODE1 0xbe1 52*52436007SJustin Chen #define BCM_LINK_CODE2 0xbe2 53*52436007SJustin Chen 54*52436007SJustin Chen enum { 55*52436007SJustin Chen BCM_MSG_FUNC_LINK_START = 0, 56*52436007SJustin Chen BCM_MSG_FUNC_LINK_STOP, 57*52436007SJustin Chen BCM_MSG_FUNC_SHMEM_TX, 58*52436007SJustin Chen BCM_MSG_FUNC_SHMEM_RX, 59*52436007SJustin Chen BCM_MSG_FUNC_SHMEM_STOP, 60*52436007SJustin Chen BCM_MSG_FUNC_MAX, 61*52436007SJustin Chen }; 62*52436007SJustin Chen 63*52436007SJustin Chen enum { 64*52436007SJustin Chen BCM_MSG_SVC_INIT = 0, 65*52436007SJustin Chen BCM_MSG_SVC_PMC, 66*52436007SJustin Chen BCM_MSG_SVC_SCMI, 67*52436007SJustin Chen BCM_MSG_SVC_DPFE, 68*52436007SJustin Chen BCM_MSG_SVC_MAX, 69*52436007SJustin Chen }; 70*52436007SJustin Chen 71*52436007SJustin Chen struct bcm74110_mbox_msg { 72*52436007SJustin Chen struct list_head list_entry; 73*52436007SJustin Chen #define BCM_MSG_VERSION_MASK GENMASK(31, 29) 74*52436007SJustin Chen #define BCM_MSG_VERSION 0x1 75*52436007SJustin Chen #define BCM_MSG_REQ_MASK BIT(28) 76*52436007SJustin Chen #define BCM_MSG_RPLY_MASK BIT(27) 77*52436007SJustin Chen #define BCM_MSG_SVC_MASK GENMASK(26, 24) 78*52436007SJustin Chen #define BCM_MSG_FUNC_MASK GENMASK(23, 16) 79*52436007SJustin Chen #define BCM_MSG_LENGTH_MASK GENMASK(15, 4) 80*52436007SJustin Chen #define BCM_MSG_SLOT_MASK GENMASK(3, 0) 81*52436007SJustin Chen 82*52436007SJustin Chen #define BCM_MSG_SET_FIELD(hdr, field, val) \ 83*52436007SJustin Chen do { \ 84*52436007SJustin Chen hdr &= ~BCM_MSG_##field##_MASK; \ 85*52436007SJustin Chen hdr |= FIELD_PREP(BCM_MSG_##field##_MASK, val); \ 86*52436007SJustin Chen } while (0) 87*52436007SJustin Chen 88*52436007SJustin Chen #define BCM_MSG_GET_FIELD(hdr, field) \ 89*52436007SJustin Chen FIELD_GET(BCM_MSG_##field##_MASK, hdr) 90*52436007SJustin Chen u32 msg; 91*52436007SJustin Chen }; 92*52436007SJustin Chen 93*52436007SJustin Chen struct bcm74110_mbox_chan { 94*52436007SJustin Chen struct bcm74110_mbox *mbox; 95*52436007SJustin Chen bool en; 96*52436007SJustin Chen int slot; 97*52436007SJustin Chen int type; 98*52436007SJustin Chen }; 99*52436007SJustin Chen 100*52436007SJustin Chen struct bcm74110_mbox { 101*52436007SJustin Chen struct platform_device *pdev; 102*52436007SJustin Chen void __iomem *base; 103*52436007SJustin Chen 104*52436007SJustin Chen int tx_chan; 105*52436007SJustin Chen int rx_chan; 106*52436007SJustin Chen int rx_irq; 107*52436007SJustin Chen struct list_head rx_svc_init_list; 108*52436007SJustin Chen spinlock_t rx_svc_list_lock; 109*52436007SJustin Chen 110*52436007SJustin Chen struct mbox_controller controller; 111*52436007SJustin Chen struct bcm74110_mbox_chan *mbox_chan; 112*52436007SJustin Chen }; 113*52436007SJustin Chen 114*52436007SJustin Chen #define BCM74110_OFFSET_IO_WRITEL_MACRO(name, offset_base) \ 115*52436007SJustin Chen static void bcm74110_##name##_writel(struct bcm74110_mbox *mbox,\ 116*52436007SJustin Chen u32 val, u32 off) \ 117*52436007SJustin Chen { \ 118*52436007SJustin Chen writel_relaxed(val, mbox->base + offset_base + off); \ 119*52436007SJustin Chen } 120*52436007SJustin Chen BCM74110_OFFSET_IO_WRITEL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan)); 121*52436007SJustin Chen BCM74110_OFFSET_IO_WRITEL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan)); 122*52436007SJustin Chen 123*52436007SJustin Chen #define BCM74110_OFFSET_IO_READL_MACRO(name, offset_base) \ 124*52436007SJustin Chen static u32 bcm74110_##name##_readl(struct bcm74110_mbox *mbox, \ 125*52436007SJustin Chen u32 off) \ 126*52436007SJustin Chen { \ 127*52436007SJustin Chen return readl_relaxed(mbox->base + offset_base + off); \ 128*52436007SJustin Chen } 129*52436007SJustin Chen BCM74110_OFFSET_IO_READL_MACRO(tx, BCM_MBOX_BASE(mbox->tx_chan)); 130*52436007SJustin Chen BCM74110_OFFSET_IO_READL_MACRO(rx, BCM_MBOX_BASE(mbox->rx_chan)); 131*52436007SJustin Chen BCM74110_OFFSET_IO_READL_MACRO(irq, BCM_MBOX_IRQ_BASE(mbox->rx_chan)); 132*52436007SJustin Chen 133*52436007SJustin Chen static inline struct bcm74110_mbox *bcm74110_mbox_from_cntrl( 134*52436007SJustin Chen struct mbox_controller *cntrl) 135*52436007SJustin Chen { 136*52436007SJustin Chen return container_of(cntrl, struct bcm74110_mbox, controller); 137*52436007SJustin Chen } 138*52436007SJustin Chen 139*52436007SJustin Chen static void bcm74110_rx_push_init_msg(struct bcm74110_mbox *mbox, u32 val) 140*52436007SJustin Chen { 141*52436007SJustin Chen struct bcm74110_mbox_msg *msg; 142*52436007SJustin Chen 143*52436007SJustin Chen msg = kzalloc(sizeof(*msg), GFP_ATOMIC); 144*52436007SJustin Chen if (!msg) 145*52436007SJustin Chen return; 146*52436007SJustin Chen 147*52436007SJustin Chen INIT_LIST_HEAD(&msg->list_entry); 148*52436007SJustin Chen msg->msg = val; 149*52436007SJustin Chen 150*52436007SJustin Chen spin_lock(&mbox->rx_svc_list_lock); 151*52436007SJustin Chen list_add_tail(&msg->list_entry, &mbox->rx_svc_init_list); 152*52436007SJustin Chen spin_unlock(&mbox->rx_svc_list_lock); 153*52436007SJustin Chen } 154*52436007SJustin Chen 155*52436007SJustin Chen static void bcm74110_rx_process_msg(struct bcm74110_mbox *mbox) 156*52436007SJustin Chen { 157*52436007SJustin Chen struct device *dev = &mbox->pdev->dev; 158*52436007SJustin Chen struct bcm74110_mbox_chan *chan_priv; 159*52436007SJustin Chen struct mbox_chan *chan; 160*52436007SJustin Chen u32 msg, status; 161*52436007SJustin Chen int type; 162*52436007SJustin Chen 163*52436007SJustin Chen do { 164*52436007SJustin Chen msg = bcm74110_rx_readl(mbox, BCM_MBOX_RDATA); 165*52436007SJustin Chen status = bcm74110_rx_readl(mbox, BCM_MBOX_STATUS0); 166*52436007SJustin Chen 167*52436007SJustin Chen dev_dbg(dev, "rx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n", 168*52436007SJustin Chen BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY), 169*52436007SJustin Chen BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC), 170*52436007SJustin Chen BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT)); 171*52436007SJustin Chen 172*52436007SJustin Chen type = BCM_MSG_GET_FIELD(msg, SVC); 173*52436007SJustin Chen switch (type) { 174*52436007SJustin Chen case BCM_MSG_SVC_INIT: 175*52436007SJustin Chen bcm74110_rx_push_init_msg(mbox, msg); 176*52436007SJustin Chen break; 177*52436007SJustin Chen case BCM_MSG_SVC_PMC: 178*52436007SJustin Chen case BCM_MSG_SVC_SCMI: 179*52436007SJustin Chen case BCM_MSG_SVC_DPFE: 180*52436007SJustin Chen chan = &mbox->controller.chans[type]; 181*52436007SJustin Chen chan_priv = chan->con_priv; 182*52436007SJustin Chen if (chan_priv->en) 183*52436007SJustin Chen mbox_chan_received_data(chan, NULL); 184*52436007SJustin Chen else 185*52436007SJustin Chen dev_warn(dev, "Channel not enabled\n"); 186*52436007SJustin Chen break; 187*52436007SJustin Chen default: 188*52436007SJustin Chen dev_warn(dev, "Unsupported msg received\n"); 189*52436007SJustin Chen } 190*52436007SJustin Chen } while (status & BCM_MBOX_STATUS0_NOT_EMPTY); 191*52436007SJustin Chen } 192*52436007SJustin Chen 193*52436007SJustin Chen static irqreturn_t bcm74110_mbox_isr(int irq, void *data) 194*52436007SJustin Chen { 195*52436007SJustin Chen struct bcm74110_mbox *mbox = data; 196*52436007SJustin Chen u32 status; 197*52436007SJustin Chen 198*52436007SJustin Chen status = bcm74110_irq_readl(mbox, BCM_MBOX_IRQ_STATUS); 199*52436007SJustin Chen 200*52436007SJustin Chen bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR); 201*52436007SJustin Chen 202*52436007SJustin Chen if (status & BCM_MBOX_IRQ_NOT_EMPTY) 203*52436007SJustin Chen bcm74110_rx_process_msg(mbox); 204*52436007SJustin Chen else 205*52436007SJustin Chen dev_warn(&mbox->pdev->dev, "Spurious interrupt\n"); 206*52436007SJustin Chen 207*52436007SJustin Chen return IRQ_HANDLED; 208*52436007SJustin Chen } 209*52436007SJustin Chen 210*52436007SJustin Chen static void bcm74110_mbox_mask_and_clear(struct bcm74110_mbox *mbox) 211*52436007SJustin Chen { 212*52436007SJustin Chen bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_MASK_SET); 213*52436007SJustin Chen bcm74110_irq_writel(mbox, 0xffffffff, BCM_MBOX_IRQ_CLEAR); 214*52436007SJustin Chen } 215*52436007SJustin Chen 216*52436007SJustin Chen static int bcm74110_rx_pop_init_msg(struct bcm74110_mbox *mbox, u32 func_type, 217*52436007SJustin Chen u32 *val) 218*52436007SJustin Chen { 219*52436007SJustin Chen struct bcm74110_mbox_msg *msg, *msg_tmp; 220*52436007SJustin Chen unsigned long flags; 221*52436007SJustin Chen bool found = false; 222*52436007SJustin Chen 223*52436007SJustin Chen spin_lock_irqsave(&mbox->rx_svc_list_lock, flags); 224*52436007SJustin Chen list_for_each_entry_safe(msg, msg_tmp, &mbox->rx_svc_init_list, 225*52436007SJustin Chen list_entry) { 226*52436007SJustin Chen if (BCM_MSG_GET_FIELD(msg->msg, FUNC) == func_type) { 227*52436007SJustin Chen list_del(&msg->list_entry); 228*52436007SJustin Chen found = true; 229*52436007SJustin Chen break; 230*52436007SJustin Chen } 231*52436007SJustin Chen } 232*52436007SJustin Chen spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags); 233*52436007SJustin Chen 234*52436007SJustin Chen if (!found) 235*52436007SJustin Chen return -EINVAL; 236*52436007SJustin Chen 237*52436007SJustin Chen *val = msg->msg; 238*52436007SJustin Chen kfree(msg); 239*52436007SJustin Chen 240*52436007SJustin Chen return 0; 241*52436007SJustin Chen } 242*52436007SJustin Chen 243*52436007SJustin Chen static void bcm74110_rx_flush_msg(struct bcm74110_mbox *mbox) 244*52436007SJustin Chen { 245*52436007SJustin Chen struct bcm74110_mbox_msg *msg, *msg_tmp; 246*52436007SJustin Chen LIST_HEAD(list_temp); 247*52436007SJustin Chen unsigned long flags; 248*52436007SJustin Chen 249*52436007SJustin Chen spin_lock_irqsave(&mbox->rx_svc_list_lock, flags); 250*52436007SJustin Chen list_splice_init(&mbox->rx_svc_init_list, &list_temp); 251*52436007SJustin Chen spin_unlock_irqrestore(&mbox->rx_svc_list_lock, flags); 252*52436007SJustin Chen 253*52436007SJustin Chen list_for_each_entry_safe(msg, msg_tmp, &list_temp, list_entry) { 254*52436007SJustin Chen list_del(&msg->list_entry); 255*52436007SJustin Chen kfree(msg); 256*52436007SJustin Chen } 257*52436007SJustin Chen } 258*52436007SJustin Chen 259*52436007SJustin Chen #define BCM_DEQUEUE_TIMEOUT_MS 30 260*52436007SJustin Chen static int bcm74110_rx_pop_init_msg_block(struct bcm74110_mbox *mbox, u32 func_type, 261*52436007SJustin Chen u32 *val) 262*52436007SJustin Chen { 263*52436007SJustin Chen int ret, timeout = 0; 264*52436007SJustin Chen 265*52436007SJustin Chen do { 266*52436007SJustin Chen ret = bcm74110_rx_pop_init_msg(mbox, func_type, val); 267*52436007SJustin Chen 268*52436007SJustin Chen if (!ret) 269*52436007SJustin Chen return 0; 270*52436007SJustin Chen 271*52436007SJustin Chen /* TODO: Figure out what is a good sleep here. */ 272*52436007SJustin Chen usleep_range(1000, 2000); 273*52436007SJustin Chen timeout++; 274*52436007SJustin Chen } while (timeout < BCM_DEQUEUE_TIMEOUT_MS); 275*52436007SJustin Chen 276*52436007SJustin Chen dev_warn(&mbox->pdev->dev, "Timeout waiting for service init response\n"); 277*52436007SJustin Chen return -ETIMEDOUT; 278*52436007SJustin Chen } 279*52436007SJustin Chen 280*52436007SJustin Chen static int bcm74110_mbox_create_msg(int req, int rply, int svc, int func, 281*52436007SJustin Chen int length, int slot) 282*52436007SJustin Chen { 283*52436007SJustin Chen u32 msg = 0; 284*52436007SJustin Chen 285*52436007SJustin Chen BCM_MSG_SET_FIELD(msg, REQ, req); 286*52436007SJustin Chen BCM_MSG_SET_FIELD(msg, RPLY, rply); 287*52436007SJustin Chen BCM_MSG_SET_FIELD(msg, SVC, svc); 288*52436007SJustin Chen BCM_MSG_SET_FIELD(msg, FUNC, func); 289*52436007SJustin Chen BCM_MSG_SET_FIELD(msg, LENGTH, length); 290*52436007SJustin Chen BCM_MSG_SET_FIELD(msg, SLOT, slot); 291*52436007SJustin Chen 292*52436007SJustin Chen return msg; 293*52436007SJustin Chen } 294*52436007SJustin Chen 295*52436007SJustin Chen static int bcm74110_mbox_tx_msg(struct bcm74110_mbox *mbox, u32 msg) 296*52436007SJustin Chen { 297*52436007SJustin Chen int val; 298*52436007SJustin Chen 299*52436007SJustin Chen /* We can potentially poll with timeout here instead */ 300*52436007SJustin Chen val = bcm74110_tx_readl(mbox, BCM_MBOX_STATUS0); 301*52436007SJustin Chen if (val & BCM_MBOX_STATUS0_FULL) { 302*52436007SJustin Chen dev_err(&mbox->pdev->dev, "Mailbox full\n"); 303*52436007SJustin Chen return -EINVAL; 304*52436007SJustin Chen } 305*52436007SJustin Chen 306*52436007SJustin Chen dev_dbg(&mbox->pdev->dev, "tx: [{req=%lu|rply=%lu|srv=%lu|fn=%lu|length=%lu|slot=%lu]\n", 307*52436007SJustin Chen BCM_MSG_GET_FIELD(msg, REQ), BCM_MSG_GET_FIELD(msg, RPLY), 308*52436007SJustin Chen BCM_MSG_GET_FIELD(msg, SVC), BCM_MSG_GET_FIELD(msg, FUNC), 309*52436007SJustin Chen BCM_MSG_GET_FIELD(msg, LENGTH), BCM_MSG_GET_FIELD(msg, SLOT)); 310*52436007SJustin Chen 311*52436007SJustin Chen bcm74110_tx_writel(mbox, msg, BCM_MBOX_WDATA); 312*52436007SJustin Chen 313*52436007SJustin Chen return 0; 314*52436007SJustin Chen } 315*52436007SJustin Chen 316*52436007SJustin Chen #define BCM_MBOX_LINK_TRAINING_RETRIES 5 317*52436007SJustin Chen static int bcm74110_mbox_link_training(struct bcm74110_mbox *mbox) 318*52436007SJustin Chen { 319*52436007SJustin Chen int ret, retries = 0; 320*52436007SJustin Chen u32 msg = 0, orig_len = 0, len = BCM_LINK_CODE0; 321*52436007SJustin Chen 322*52436007SJustin Chen do { 323*52436007SJustin Chen switch (len) { 324*52436007SJustin Chen case 0: 325*52436007SJustin Chen retries++; 326*52436007SJustin Chen dev_warn(&mbox->pdev->dev, 327*52436007SJustin Chen "Link train failed, trying again... %d\n", 328*52436007SJustin Chen retries); 329*52436007SJustin Chen if (retries > BCM_MBOX_LINK_TRAINING_RETRIES) 330*52436007SJustin Chen return -EINVAL; 331*52436007SJustin Chen len = BCM_LINK_CODE0; 332*52436007SJustin Chen fallthrough; 333*52436007SJustin Chen case BCM_LINK_CODE0: 334*52436007SJustin Chen case BCM_LINK_CODE1: 335*52436007SJustin Chen case BCM_LINK_CODE2: 336*52436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 337*52436007SJustin Chen BCM_MSG_FUNC_LINK_START, 338*52436007SJustin Chen len, BCM_MSG_SVC_INIT); 339*52436007SJustin Chen break; 340*52436007SJustin Chen default: 341*52436007SJustin Chen break; 342*52436007SJustin Chen } 343*52436007SJustin Chen 344*52436007SJustin Chen bcm74110_mbox_tx_msg(mbox, msg); 345*52436007SJustin Chen 346*52436007SJustin Chen /* No response expected for LINK_CODE2 */ 347*52436007SJustin Chen if (len == BCM_LINK_CODE2) 348*52436007SJustin Chen return 0; 349*52436007SJustin Chen 350*52436007SJustin Chen orig_len = len; 351*52436007SJustin Chen 352*52436007SJustin Chen ret = bcm74110_rx_pop_init_msg_block(mbox, 353*52436007SJustin Chen BCM_MSG_GET_FIELD(msg, FUNC), 354*52436007SJustin Chen &msg); 355*52436007SJustin Chen if (ret) { 356*52436007SJustin Chen len = 0; 357*52436007SJustin Chen continue; 358*52436007SJustin Chen } 359*52436007SJustin Chen 360*52436007SJustin Chen if ((BCM_MSG_GET_FIELD(msg, SVC) != BCM_MSG_SVC_INIT) || 361*52436007SJustin Chen (BCM_MSG_GET_FIELD(msg, FUNC) != BCM_MSG_FUNC_LINK_START) || 362*52436007SJustin Chen (BCM_MSG_GET_FIELD(msg, SLOT) != 0) || 363*52436007SJustin Chen (BCM_MSG_GET_FIELD(msg, RPLY) != 1) || 364*52436007SJustin Chen (BCM_MSG_GET_FIELD(msg, REQ) != 0)) { 365*52436007SJustin Chen len = 0; 366*52436007SJustin Chen continue; 367*52436007SJustin Chen } 368*52436007SJustin Chen 369*52436007SJustin Chen len = BCM_MSG_GET_FIELD(msg, LENGTH); 370*52436007SJustin Chen 371*52436007SJustin Chen /* Make sure sequence is good */ 372*52436007SJustin Chen if (len != (orig_len + 1)) { 373*52436007SJustin Chen len = 0; 374*52436007SJustin Chen continue; 375*52436007SJustin Chen } 376*52436007SJustin Chen } while (1); 377*52436007SJustin Chen 378*52436007SJustin Chen return -EINVAL; 379*52436007SJustin Chen } 380*52436007SJustin Chen 381*52436007SJustin Chen static int bcm74110_mbox_tx_msg_and_wait_ack(struct bcm74110_mbox *mbox, u32 msg) 382*52436007SJustin Chen { 383*52436007SJustin Chen int ret; 384*52436007SJustin Chen u32 recv_msg; 385*52436007SJustin Chen 386*52436007SJustin Chen ret = bcm74110_mbox_tx_msg(mbox, msg); 387*52436007SJustin Chen if (ret) 388*52436007SJustin Chen return ret; 389*52436007SJustin Chen 390*52436007SJustin Chen ret = bcm74110_rx_pop_init_msg_block(mbox, BCM_MSG_GET_FIELD(msg, FUNC), 391*52436007SJustin Chen &recv_msg); 392*52436007SJustin Chen if (ret) 393*52436007SJustin Chen return ret; 394*52436007SJustin Chen 395*52436007SJustin Chen /* 396*52436007SJustin Chen * Modify tx message to verify rx ack. 397*52436007SJustin Chen * Flip RPLY/REQ for synchronous messages 398*52436007SJustin Chen */ 399*52436007SJustin Chen if (BCM_MSG_GET_FIELD(msg, REQ) == 1) { 400*52436007SJustin Chen BCM_MSG_SET_FIELD(msg, RPLY, 1); 401*52436007SJustin Chen BCM_MSG_SET_FIELD(msg, REQ, 0); 402*52436007SJustin Chen } 403*52436007SJustin Chen 404*52436007SJustin Chen if (msg != recv_msg) { 405*52436007SJustin Chen dev_err(&mbox->pdev->dev, "Found ack, but ack is invalid\n"); 406*52436007SJustin Chen return -EINVAL; 407*52436007SJustin Chen } 408*52436007SJustin Chen 409*52436007SJustin Chen return 0; 410*52436007SJustin Chen } 411*52436007SJustin Chen 412*52436007SJustin Chen /* Each index points to 0x100 of HAB MEM. IDX size counts from 0 */ 413*52436007SJustin Chen #define BCM_MBOX_HAB_MEM_IDX_START 0x30 414*52436007SJustin Chen #define BCM_MBOX_HAB_MEM_IDX_SIZE 0x0 415*52436007SJustin Chen static int bcm74110_mbox_shmem_init(struct bcm74110_mbox *mbox) 416*52436007SJustin Chen { 417*52436007SJustin Chen u32 msg = 0; 418*52436007SJustin Chen int ret; 419*52436007SJustin Chen 420*52436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 421*52436007SJustin Chen BCM_MSG_FUNC_SHMEM_STOP, 422*52436007SJustin Chen 0, BCM_MSG_SVC_INIT); 423*52436007SJustin Chen ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 424*52436007SJustin Chen if (ret) 425*52436007SJustin Chen return -EINVAL; 426*52436007SJustin Chen 427*52436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 428*52436007SJustin Chen BCM_MSG_FUNC_SHMEM_TX, 429*52436007SJustin Chen BCM_MBOX_HAB_MEM_IDX_START, 430*52436007SJustin Chen BCM_MBOX_HAB_MEM_IDX_SIZE); 431*52436007SJustin Chen ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 432*52436007SJustin Chen if (ret) 433*52436007SJustin Chen return -EINVAL; 434*52436007SJustin Chen 435*52436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 436*52436007SJustin Chen BCM_MSG_FUNC_SHMEM_RX, 437*52436007SJustin Chen BCM_MBOX_HAB_MEM_IDX_START, 438*52436007SJustin Chen BCM_MBOX_HAB_MEM_IDX_SIZE); 439*52436007SJustin Chen ret = bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 440*52436007SJustin Chen if (ret) 441*52436007SJustin Chen return -EINVAL; 442*52436007SJustin Chen 443*52436007SJustin Chen return 0; 444*52436007SJustin Chen } 445*52436007SJustin Chen 446*52436007SJustin Chen static int bcm74110_mbox_init(struct bcm74110_mbox *mbox) 447*52436007SJustin Chen { 448*52436007SJustin Chen int ret = 0; 449*52436007SJustin Chen 450*52436007SJustin Chen /* Disable queues tx/rx */ 451*52436007SJustin Chen bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL); 452*52436007SJustin Chen 453*52436007SJustin Chen /* Clear status & restart tx/rx*/ 454*52436007SJustin Chen bcm74110_tx_writel(mbox, BCM_MBOX_CTRL_EN | BCM_MBOX_CTRL_CLR, 455*52436007SJustin Chen BCM_MBOX_CTRL); 456*52436007SJustin Chen 457*52436007SJustin Chen /* Unmask irq */ 458*52436007SJustin Chen bcm74110_irq_writel(mbox, BCM_MBOX_IRQ_NOT_EMPTY, BCM_MBOX_IRQ_MASK_CLEAR); 459*52436007SJustin Chen 460*52436007SJustin Chen ret = bcm74110_mbox_link_training(mbox); 461*52436007SJustin Chen if (ret) { 462*52436007SJustin Chen dev_err(&mbox->pdev->dev, "Training failed\n"); 463*52436007SJustin Chen return ret; 464*52436007SJustin Chen } 465*52436007SJustin Chen 466*52436007SJustin Chen return bcm74110_mbox_shmem_init(mbox); 467*52436007SJustin Chen } 468*52436007SJustin Chen 469*52436007SJustin Chen static int bcm74110_mbox_send_data(struct mbox_chan *chan, void *data) 470*52436007SJustin Chen { 471*52436007SJustin Chen struct bcm74110_mbox_chan *chan_priv = chan->con_priv; 472*52436007SJustin Chen u32 msg; 473*52436007SJustin Chen 474*52436007SJustin Chen switch (chan_priv->type) { 475*52436007SJustin Chen case BCM_MSG_SVC_PMC: 476*52436007SJustin Chen case BCM_MSG_SVC_SCMI: 477*52436007SJustin Chen case BCM_MSG_SVC_DPFE: 478*52436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, chan_priv->type, 0, 479*52436007SJustin Chen 128 + 28, chan_priv->slot); 480*52436007SJustin Chen break; 481*52436007SJustin Chen default: 482*52436007SJustin Chen return -EINVAL; 483*52436007SJustin Chen }; 484*52436007SJustin Chen 485*52436007SJustin Chen return bcm74110_mbox_tx_msg(chan_priv->mbox, msg); 486*52436007SJustin Chen } 487*52436007SJustin Chen 488*52436007SJustin Chen static int bcm74110_mbox_chan_startup(struct mbox_chan *chan) 489*52436007SJustin Chen { 490*52436007SJustin Chen struct bcm74110_mbox_chan *chan_priv = chan->con_priv; 491*52436007SJustin Chen 492*52436007SJustin Chen chan_priv->en = true; 493*52436007SJustin Chen 494*52436007SJustin Chen return 0; 495*52436007SJustin Chen } 496*52436007SJustin Chen 497*52436007SJustin Chen static void bcm74110_mbox_chan_shutdown(struct mbox_chan *chan) 498*52436007SJustin Chen { 499*52436007SJustin Chen struct bcm74110_mbox_chan *chan_priv = chan->con_priv; 500*52436007SJustin Chen 501*52436007SJustin Chen chan_priv->en = false; 502*52436007SJustin Chen } 503*52436007SJustin Chen 504*52436007SJustin Chen static const struct mbox_chan_ops bcm74110_mbox_chan_ops = { 505*52436007SJustin Chen .send_data = bcm74110_mbox_send_data, 506*52436007SJustin Chen .startup = bcm74110_mbox_chan_startup, 507*52436007SJustin Chen .shutdown = bcm74110_mbox_chan_shutdown, 508*52436007SJustin Chen }; 509*52436007SJustin Chen 510*52436007SJustin Chen static void bcm74110_mbox_shutdown(struct platform_device *pdev) 511*52436007SJustin Chen { 512*52436007SJustin Chen struct bcm74110_mbox *mbox = dev_get_drvdata(&pdev->dev); 513*52436007SJustin Chen u32 msg; 514*52436007SJustin Chen 515*52436007SJustin Chen msg = bcm74110_mbox_create_msg(1, 0, BCM_MSG_SVC_INIT, 516*52436007SJustin Chen BCM_MSG_FUNC_LINK_STOP, 517*52436007SJustin Chen 0, 0); 518*52436007SJustin Chen 519*52436007SJustin Chen bcm74110_mbox_tx_msg_and_wait_ack(mbox, msg); 520*52436007SJustin Chen 521*52436007SJustin Chen /* Even if we don't receive ACK, lets shut it down */ 522*52436007SJustin Chen 523*52436007SJustin Chen bcm74110_mbox_mask_and_clear(mbox); 524*52436007SJustin Chen 525*52436007SJustin Chen /* Disable queues tx/rx */ 526*52436007SJustin Chen bcm74110_tx_writel(mbox, 0x0, BCM_MBOX_CTRL); 527*52436007SJustin Chen 528*52436007SJustin Chen /* Flush queues */ 529*52436007SJustin Chen bcm74110_rx_flush_msg(mbox); 530*52436007SJustin Chen } 531*52436007SJustin Chen 532*52436007SJustin Chen static struct mbox_chan *bcm74110_mbox_of_xlate(struct mbox_controller *cntrl, 533*52436007SJustin Chen const struct of_phandle_args *p) 534*52436007SJustin Chen { 535*52436007SJustin Chen struct bcm74110_mbox *mbox = bcm74110_mbox_from_cntrl(cntrl); 536*52436007SJustin Chen struct device *dev = &mbox->pdev->dev; 537*52436007SJustin Chen struct bcm74110_mbox_chan *chan_priv; 538*52436007SJustin Chen int slot, type; 539*52436007SJustin Chen 540*52436007SJustin Chen if (p->args_count != 2) { 541*52436007SJustin Chen dev_err(dev, "Invalid arguments\n"); 542*52436007SJustin Chen return ERR_PTR(-EINVAL); 543*52436007SJustin Chen } 544*52436007SJustin Chen 545*52436007SJustin Chen type = p->args[0]; 546*52436007SJustin Chen slot = p->args[1]; 547*52436007SJustin Chen 548*52436007SJustin Chen switch (type) { 549*52436007SJustin Chen case BCM_MSG_SVC_PMC: 550*52436007SJustin Chen case BCM_MSG_SVC_SCMI: 551*52436007SJustin Chen case BCM_MSG_SVC_DPFE: 552*52436007SJustin Chen if (slot > BCM_MBOX_HAB_MEM_IDX_SIZE) { 553*52436007SJustin Chen dev_err(dev, "Not enough shared memory\n"); 554*52436007SJustin Chen return ERR_PTR(-EINVAL); 555*52436007SJustin Chen } 556*52436007SJustin Chen chan_priv = cntrl->chans[type].con_priv; 557*52436007SJustin Chen chan_priv->slot = slot; 558*52436007SJustin Chen chan_priv->type = type; 559*52436007SJustin Chen break; 560*52436007SJustin Chen default: 561*52436007SJustin Chen dev_err(dev, "Invalid channel type: %d\n", type); 562*52436007SJustin Chen return ERR_PTR(-EINVAL); 563*52436007SJustin Chen }; 564*52436007SJustin Chen 565*52436007SJustin Chen return &cntrl->chans[type]; 566*52436007SJustin Chen } 567*52436007SJustin Chen 568*52436007SJustin Chen static int bcm74110_mbox_probe(struct platform_device *pdev) 569*52436007SJustin Chen { 570*52436007SJustin Chen struct device *dev = &pdev->dev; 571*52436007SJustin Chen struct bcm74110_mbox *mbox; 572*52436007SJustin Chen int i, ret; 573*52436007SJustin Chen 574*52436007SJustin Chen mbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL); 575*52436007SJustin Chen if (!mbox) 576*52436007SJustin Chen return -ENOMEM; 577*52436007SJustin Chen 578*52436007SJustin Chen mbox->pdev = pdev; 579*52436007SJustin Chen platform_set_drvdata(pdev, mbox); 580*52436007SJustin Chen 581*52436007SJustin Chen mbox->base = devm_platform_ioremap_resource(pdev, 0); 582*52436007SJustin Chen if (IS_ERR(mbox->base)) 583*52436007SJustin Chen return dev_err_probe(dev, PTR_ERR(mbox->base), "Failed to iomap\n"); 584*52436007SJustin Chen 585*52436007SJustin Chen ret = of_property_read_u32(dev->of_node, "brcm,tx", &mbox->tx_chan); 586*52436007SJustin Chen if (ret) 587*52436007SJustin Chen return dev_err_probe(dev, ret, "Failed to find tx channel\n"); 588*52436007SJustin Chen 589*52436007SJustin Chen ret = of_property_read_u32(dev->of_node, "brcm,rx", &mbox->rx_chan); 590*52436007SJustin Chen if (ret) 591*52436007SJustin Chen return dev_err_probe(dev, ret, "Failed to find rx channel\n"); 592*52436007SJustin Chen 593*52436007SJustin Chen mbox->rx_irq = platform_get_irq(pdev, 0); 594*52436007SJustin Chen if (mbox->rx_irq < 0) 595*52436007SJustin Chen return mbox->rx_irq; 596*52436007SJustin Chen 597*52436007SJustin Chen INIT_LIST_HEAD(&mbox->rx_svc_init_list); 598*52436007SJustin Chen spin_lock_init(&mbox->rx_svc_list_lock); 599*52436007SJustin Chen bcm74110_mbox_mask_and_clear(mbox); 600*52436007SJustin Chen 601*52436007SJustin Chen ret = devm_request_irq(dev, mbox->rx_irq, bcm74110_mbox_isr, 602*52436007SJustin Chen IRQF_NO_SUSPEND, pdev->name, mbox); 603*52436007SJustin Chen if (ret) 604*52436007SJustin Chen return dev_err_probe(dev, ret, "Failed to request irq\n"); 605*52436007SJustin Chen 606*52436007SJustin Chen mbox->controller.ops = &bcm74110_mbox_chan_ops; 607*52436007SJustin Chen mbox->controller.dev = dev; 608*52436007SJustin Chen mbox->controller.num_chans = BCM_MSG_SVC_MAX; 609*52436007SJustin Chen mbox->controller.of_xlate = &bcm74110_mbox_of_xlate; 610*52436007SJustin Chen mbox->controller.chans = devm_kcalloc(dev, BCM_MSG_SVC_MAX, 611*52436007SJustin Chen sizeof(*mbox->controller.chans), 612*52436007SJustin Chen GFP_KERNEL); 613*52436007SJustin Chen if (!mbox->controller.chans) 614*52436007SJustin Chen return -ENOMEM; 615*52436007SJustin Chen 616*52436007SJustin Chen mbox->mbox_chan = devm_kcalloc(dev, BCM_MSG_SVC_MAX, 617*52436007SJustin Chen sizeof(*mbox->mbox_chan), 618*52436007SJustin Chen GFP_KERNEL); 619*52436007SJustin Chen if (!mbox->mbox_chan) 620*52436007SJustin Chen return -ENOMEM; 621*52436007SJustin Chen 622*52436007SJustin Chen for (i = 0; i < BCM_MSG_SVC_MAX; i++) { 623*52436007SJustin Chen mbox->mbox_chan[i].mbox = mbox; 624*52436007SJustin Chen mbox->controller.chans[i].con_priv = &mbox->mbox_chan[i]; 625*52436007SJustin Chen } 626*52436007SJustin Chen 627*52436007SJustin Chen ret = devm_mbox_controller_register(dev, &mbox->controller); 628*52436007SJustin Chen if (ret) 629*52436007SJustin Chen return ret; 630*52436007SJustin Chen 631*52436007SJustin Chen ret = bcm74110_mbox_init(mbox); 632*52436007SJustin Chen if (ret) 633*52436007SJustin Chen return ret; 634*52436007SJustin Chen 635*52436007SJustin Chen return 0; 636*52436007SJustin Chen } 637*52436007SJustin Chen 638*52436007SJustin Chen static const struct of_device_id bcm74110_mbox_of_match[] = { 639*52436007SJustin Chen { .compatible = "brcm,bcm74110-mbox", }, 640*52436007SJustin Chen { /* sentinel */ }, 641*52436007SJustin Chen }; 642*52436007SJustin Chen MODULE_DEVICE_TABLE(of, bcm74110_mbox_of_match); 643*52436007SJustin Chen 644*52436007SJustin Chen static struct platform_driver bcm74110_mbox_driver = { 645*52436007SJustin Chen .driver = { 646*52436007SJustin Chen .name = "bcm74110-mbox", 647*52436007SJustin Chen .of_match_table = bcm74110_mbox_of_match, 648*52436007SJustin Chen }, 649*52436007SJustin Chen .probe = bcm74110_mbox_probe, 650*52436007SJustin Chen .shutdown = bcm74110_mbox_shutdown, 651*52436007SJustin Chen }; 652*52436007SJustin Chen module_platform_driver(bcm74110_mbox_driver); 653*52436007SJustin Chen 654*52436007SJustin Chen MODULE_AUTHOR("Justin Chen <justin.chen@braodcom.com>"); 655*52436007SJustin Chen MODULE_DESCRIPTION("BCM74110 mailbox driver"); 656*52436007SJustin Chen MODULE_LICENSE("GPL"); 657