mailbox-mpfs.c (6413B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Microchip PolarFire SoC (MPFS) system controller/mailbox controller driver 4 * 5 * Copyright (c) 2020 Microchip Corporation. All rights reserved. 6 * 7 * Author: Conor Dooley <conor.dooley@microchip.com> 8 * 9 */ 10 11#include <linux/io.h> 12#include <linux/err.h> 13#include <linux/init.h> 14#include <linux/module.h> 15#include <linux/kernel.h> 16#include <linux/interrupt.h> 17#include <linux/platform_device.h> 18#include <linux/mailbox_controller.h> 19#include <soc/microchip/mpfs.h> 20 21#define SERVICES_CR_OFFSET 0x50u 22#define SERVICES_SR_OFFSET 0x54u 23#define MAILBOX_REG_OFFSET 0x800u 24#define MSS_SYS_MAILBOX_DATA_OFFSET 0u 25#define SCB_MASK_WIDTH 16u 26 27/* SCBCTRL service control register */ 28 29#define SCB_CTRL_REQ (0) 30#define SCB_CTRL_REQ_MASK BIT(SCB_CTRL_REQ) 31 32#define SCB_CTRL_BUSY (1) 33#define SCB_CTRL_BUSY_MASK BIT(SCB_CTRL_BUSY) 34 35#define SCB_CTRL_ABORT (2) 36#define SCB_CTRL_ABORT_MASK BIT(SCB_CTRL_ABORT) 37 38#define SCB_CTRL_NOTIFY (3) 39#define SCB_CTRL_NOTIFY_MASK BIT(SCB_CTRL_NOTIFY) 40 41#define SCB_CTRL_POS (16) 42#define SCB_CTRL_MASK GENMASK_ULL(SCB_CTRL_POS + SCB_MASK_WIDTH, SCB_CTRL_POS) 43 44/* SCBCTRL service status register */ 45 46#define SCB_STATUS_REQ (0) 47#define SCB_STATUS_REQ_MASK BIT(SCB_STATUS_REQ) 48 49#define SCB_STATUS_BUSY (1) 50#define SCB_STATUS_BUSY_MASK BIT(SCB_STATUS_BUSY) 51 52#define SCB_STATUS_ABORT (2) 53#define SCB_STATUS_ABORT_MASK BIT(SCB_STATUS_ABORT) 54 55#define SCB_STATUS_NOTIFY (3) 56#define SCB_STATUS_NOTIFY_MASK BIT(SCB_STATUS_NOTIFY) 57 58#define SCB_STATUS_POS (16) 59#define SCB_STATUS_MASK GENMASK_ULL(SCB_STATUS_POS + SCB_MASK_WIDTH, SCB_STATUS_POS) 60 61struct mpfs_mbox { 62 struct mbox_controller controller; 63 struct device *dev; 64 int irq; 65 void __iomem *mbox_base; 66 void __iomem *int_reg; 67 struct mbox_chan chans[1]; 68 struct mpfs_mss_response *response; 69 u16 resp_offset; 70}; 71 72static bool mpfs_mbox_busy(struct mpfs_mbox *mbox) 73{ 74 u32 status; 75 76 status = readl_relaxed(mbox->mbox_base + SERVICES_SR_OFFSET); 77 78 return status & SCB_STATUS_BUSY_MASK; 79} 80 81static int mpfs_mbox_send_data(struct mbox_chan *chan, void *data) 82{ 83 struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 84 struct mpfs_mss_msg *msg = data; 85 u32 tx_trigger; 86 u16 opt_sel; 87 u32 val = 0u; 88 89 mbox->response = msg->response; 90 mbox->resp_offset = msg->resp_offset; 91 92 if (mpfs_mbox_busy(mbox)) 93 return -EBUSY; 94 95 if (msg->cmd_data_size) { 96 u32 index; 97 u8 extra_bits = msg->cmd_data_size & 3; 98 u32 *word_buf = (u32 *)msg->cmd_data; 99 100 for (index = 0; index < (msg->cmd_data_size / 4); index++) 101 writel_relaxed(word_buf[index], 102 mbox->mbox_base + MAILBOX_REG_OFFSET + index * 0x4); 103 if (extra_bits) { 104 u8 i; 105 u8 byte_off = ALIGN_DOWN(msg->cmd_data_size, 4); 106 u8 *byte_buf = msg->cmd_data + byte_off; 107 108 val = readl_relaxed(mbox->mbox_base + 109 MAILBOX_REG_OFFSET + index * 0x4); 110 111 for (i = 0u; i < extra_bits; i++) { 112 val &= ~(0xffu << (i * 8u)); 113 val |= (byte_buf[i] << (i * 8u)); 114 } 115 116 writel_relaxed(val, 117 mbox->mbox_base + MAILBOX_REG_OFFSET + index * 0x4); 118 } 119 } 120 121 opt_sel = ((msg->mbox_offset << 7u) | (msg->cmd_opcode & 0x7fu)); 122 tx_trigger = (opt_sel << SCB_CTRL_POS) & SCB_CTRL_MASK; 123 tx_trigger |= SCB_CTRL_REQ_MASK | SCB_STATUS_NOTIFY_MASK; 124 writel_relaxed(tx_trigger, mbox->mbox_base + SERVICES_CR_OFFSET); 125 126 return 0; 127} 128 129static void mpfs_mbox_rx_data(struct mbox_chan *chan) 130{ 131 struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 132 struct mpfs_mss_response *response = mbox->response; 133 u16 num_words = ALIGN((response->resp_size), (4)) / 4U; 134 u32 i; 135 136 if (!response->resp_msg) { 137 dev_err(mbox->dev, "failed to assign memory for response %d\n", -ENOMEM); 138 return; 139 } 140 141 if (!mpfs_mbox_busy(mbox)) { 142 for (i = 0; i < num_words; i++) { 143 response->resp_msg[i] = 144 readl_relaxed(mbox->mbox_base + MAILBOX_REG_OFFSET 145 + mbox->resp_offset + i * 0x4); 146 } 147 } 148 149 mbox_chan_received_data(chan, response); 150} 151 152static irqreturn_t mpfs_mbox_inbox_isr(int irq, void *data) 153{ 154 struct mbox_chan *chan = data; 155 struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 156 157 writel_relaxed(0, mbox->int_reg); 158 159 mpfs_mbox_rx_data(chan); 160 161 mbox_chan_txdone(chan, 0); 162 return IRQ_HANDLED; 163} 164 165static int mpfs_mbox_startup(struct mbox_chan *chan) 166{ 167 struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 168 int ret = 0; 169 170 if (!mbox) 171 return -EINVAL; 172 173 ret = devm_request_irq(mbox->dev, mbox->irq, mpfs_mbox_inbox_isr, 0, "mpfs-mailbox", chan); 174 if (ret) 175 dev_err(mbox->dev, "failed to register mailbox interrupt:%d\n", ret); 176 177 return ret; 178} 179 180static void mpfs_mbox_shutdown(struct mbox_chan *chan) 181{ 182 struct mpfs_mbox *mbox = (struct mpfs_mbox *)chan->con_priv; 183 184 devm_free_irq(mbox->dev, mbox->irq, chan); 185} 186 187static const struct mbox_chan_ops mpfs_mbox_ops = { 188 .send_data = mpfs_mbox_send_data, 189 .startup = mpfs_mbox_startup, 190 .shutdown = mpfs_mbox_shutdown, 191}; 192 193static int mpfs_mbox_probe(struct platform_device *pdev) 194{ 195 struct mpfs_mbox *mbox; 196 struct resource *regs; 197 int ret; 198 199 mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); 200 if (!mbox) 201 return -ENOMEM; 202 203 mbox->mbox_base = devm_platform_get_and_ioremap_resource(pdev, 0, ®s); 204 if (IS_ERR(mbox->mbox_base)) 205 return PTR_ERR(mbox->mbox_base); 206 207 mbox->int_reg = devm_platform_get_and_ioremap_resource(pdev, 1, ®s); 208 if (IS_ERR(mbox->int_reg)) 209 return PTR_ERR(mbox->int_reg); 210 211 mbox->irq = platform_get_irq(pdev, 0); 212 if (mbox->irq < 0) 213 return mbox->irq; 214 215 mbox->dev = &pdev->dev; 216 217 mbox->chans[0].con_priv = mbox; 218 mbox->controller.dev = mbox->dev; 219 mbox->controller.num_chans = 1; 220 mbox->controller.chans = mbox->chans; 221 mbox->controller.ops = &mpfs_mbox_ops; 222 mbox->controller.txdone_irq = true; 223 224 ret = devm_mbox_controller_register(&pdev->dev, &mbox->controller); 225 if (ret) { 226 dev_err(&pdev->dev, "Registering MPFS mailbox controller failed\n"); 227 return ret; 228 } 229 dev_info(&pdev->dev, "Registered MPFS mailbox controller driver\n"); 230 231 return 0; 232} 233 234static const struct of_device_id mpfs_mbox_of_match[] = { 235 {.compatible = "microchip,mpfs-mailbox", }, 236 {}, 237}; 238MODULE_DEVICE_TABLE(of, mpfs_mbox_of_match); 239 240static struct platform_driver mpfs_mbox_driver = { 241 .driver = { 242 .name = "mpfs-mailbox", 243 .of_match_table = mpfs_mbox_of_match, 244 }, 245 .probe = mpfs_mbox_probe, 246}; 247module_platform_driver(mpfs_mbox_driver); 248 249MODULE_LICENSE("GPL v2"); 250MODULE_AUTHOR("Conor Dooley <conor.dooley@microchip.com>"); 251MODULE_DESCRIPTION("MPFS mailbox controller driver");