1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * rWTM BIU Mailbox driver for Armada 37xx 4 * 5 * Author: Marek Behun <marek.behun@nic.cz> 6 */ 7 8 #include <linux/device.h> 9 #include <linux/interrupt.h> 10 #include <linux/io.h> 11 #include <linux/kernel.h> 12 #include <linux/mailbox_controller.h> 13 #include <linux/module.h> 14 #include <linux/of.h> 15 #include <linux/platform_device.h> 16 #include <linux/armada-37xx-rwtm-mailbox.h> 17 18 #define DRIVER_NAME "armada-37xx-rwtm-mailbox" 19 20 /* relative to rWTM BIU Mailbox Registers */ 21 #define RWTM_MBOX_PARAM(i) (0x0 + ((i) << 2)) 22 #define RWTM_MBOX_COMMAND 0x40 23 #define RWTM_MBOX_RETURN_STATUS 0x80 24 #define RWTM_MBOX_STATUS(i) (0x84 + ((i) << 2)) 25 #define RWTM_MBOX_FIFO_STATUS 0xc4 26 #define FIFO_STS_RDY 0x100 27 #define FIFO_STS_CNTR_MASK 0x7 28 #define FIFO_STS_CNTR_MAX 4 29 30 #define RWTM_HOST_INT_RESET 0xc8 31 #define RWTM_HOST_INT_MASK 0xcc 32 #define SP_CMD_COMPLETE BIT(0) 33 #define SP_CMD_QUEUE_FULL_ACCESS BIT(17) 34 #define SP_CMD_QUEUE_FULL BIT(18) 35 36 struct a37xx_mbox { 37 struct device *dev; 38 struct mbox_controller controller; 39 void __iomem *base; 40 int irq; 41 }; 42 43 static void a37xx_mbox_receive(struct mbox_chan *chan) 44 { 45 struct a37xx_mbox *mbox = chan->con_priv; 46 struct armada_37xx_rwtm_rx_msg rx_msg; 47 int i; 48 49 rx_msg.retval = readl(mbox->base + RWTM_MBOX_RETURN_STATUS); 50 for (i = 0; i < 16; ++i) 51 rx_msg.status[i] = readl(mbox->base + RWTM_MBOX_STATUS(i)); 52 53 mbox_chan_received_data(chan, &rx_msg); 54 } 55 56 static irqreturn_t a37xx_mbox_irq_handler(int irq, void *data) 57 { 58 struct mbox_chan *chan = data; 59 struct a37xx_mbox *mbox = chan->con_priv; 60 u32 reg; 61 62 reg = readl(mbox->base + RWTM_HOST_INT_RESET); 63 64 if (reg & SP_CMD_COMPLETE) 65 a37xx_mbox_receive(chan); 66 67 if (reg & (SP_CMD_QUEUE_FULL_ACCESS | SP_CMD_QUEUE_FULL)) 68 dev_err(mbox->dev, "Secure processor command queue full\n"); 69 70 writel(reg, mbox->base + RWTM_HOST_INT_RESET); 71 if (reg) 72 mbox_chan_txdone(chan, 0); 73 74 return reg ? IRQ_HANDLED : IRQ_NONE; 75 } 76 77 static int a37xx_mbox_send_data(struct mbox_chan *chan, void *data) 78 { 79 struct a37xx_mbox *mbox = chan->con_priv; 80 struct armada_37xx_rwtm_tx_msg *msg = data; 81 int i; 82 u32 reg; 83 84 if (!data) 85 return -EINVAL; 86 87 reg = readl(mbox->base + RWTM_MBOX_FIFO_STATUS); 88 if (!(reg & FIFO_STS_RDY)) 89 dev_warn(mbox->dev, "Secure processor not ready\n"); 90 91 if ((reg & FIFO_STS_CNTR_MASK) >= FIFO_STS_CNTR_MAX) { 92 dev_err(mbox->dev, "Secure processor command queue full\n"); 93 return -EBUSY; 94 } 95 96 for (i = 0; i < 16; ++i) 97 writel(msg->args[i], mbox->base + RWTM_MBOX_PARAM(i)); 98 writel(msg->command, mbox->base + RWTM_MBOX_COMMAND); 99 100 return 0; 101 } 102 103 static int a37xx_mbox_startup(struct mbox_chan *chan) 104 { 105 struct a37xx_mbox *mbox = chan->con_priv; 106 u32 reg; 107 int ret; 108 109 ret = devm_request_irq(mbox->dev, mbox->irq, a37xx_mbox_irq_handler, 0, 110 DRIVER_NAME, chan); 111 if (ret < 0) { 112 dev_err(mbox->dev, "Cannot request irq\n"); 113 return ret; 114 } 115 116 /* enable IRQ generation */ 117 reg = readl(mbox->base + RWTM_HOST_INT_MASK); 118 reg &= ~(SP_CMD_COMPLETE | SP_CMD_QUEUE_FULL_ACCESS | SP_CMD_QUEUE_FULL); 119 writel(reg, mbox->base + RWTM_HOST_INT_MASK); 120 121 return 0; 122 } 123 124 static void a37xx_mbox_shutdown(struct mbox_chan *chan) 125 { 126 u32 reg; 127 struct a37xx_mbox *mbox = chan->con_priv; 128 129 /* disable interrupt generation */ 130 reg = readl(mbox->base + RWTM_HOST_INT_MASK); 131 reg |= SP_CMD_COMPLETE | SP_CMD_QUEUE_FULL_ACCESS | SP_CMD_QUEUE_FULL; 132 writel(reg, mbox->base + RWTM_HOST_INT_MASK); 133 134 devm_free_irq(mbox->dev, mbox->irq, chan); 135 } 136 137 static const struct mbox_chan_ops a37xx_mbox_ops = { 138 .send_data = a37xx_mbox_send_data, 139 .startup = a37xx_mbox_startup, 140 .shutdown = a37xx_mbox_shutdown, 141 }; 142 143 static int armada_37xx_mbox_probe(struct platform_device *pdev) 144 { 145 struct a37xx_mbox *mbox; 146 struct resource *regs; 147 struct mbox_chan *chans; 148 int ret; 149 150 mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); 151 if (!mbox) 152 return -ENOMEM; 153 154 /* Allocated one channel */ 155 chans = devm_kzalloc(&pdev->dev, sizeof(*chans), GFP_KERNEL); 156 if (!chans) 157 return -ENOMEM; 158 159 regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); 160 161 mbox->base = devm_ioremap_resource(&pdev->dev, regs); 162 if (IS_ERR(mbox->base)) { 163 dev_err(&pdev->dev, "ioremap failed\n"); 164 return PTR_ERR(mbox->base); 165 } 166 167 mbox->irq = platform_get_irq(pdev, 0); 168 if (mbox->irq < 0) { 169 dev_err(&pdev->dev, "Cannot get irq\n"); 170 return mbox->irq; 171 } 172 173 mbox->dev = &pdev->dev; 174 175 /* Hardware supports only one channel. */ 176 chans[0].con_priv = mbox; 177 mbox->controller.dev = mbox->dev; 178 mbox->controller.num_chans = 1; 179 mbox->controller.chans = chans; 180 mbox->controller.ops = &a37xx_mbox_ops; 181 mbox->controller.txdone_irq = true; 182 183 ret = mbox_controller_register(&mbox->controller); 184 if (ret) { 185 dev_err(&pdev->dev, "Could not register mailbox controller\n"); 186 return ret; 187 } 188 189 platform_set_drvdata(pdev, mbox); 190 return ret; 191 } 192 193 static int armada_37xx_mbox_remove(struct platform_device *pdev) 194 { 195 struct a37xx_mbox *mbox = platform_get_drvdata(pdev); 196 197 if (!mbox) 198 return -EINVAL; 199 200 mbox_controller_unregister(&mbox->controller); 201 202 return 0; 203 } 204 205 static const struct of_device_id armada_37xx_mbox_match[] = { 206 { .compatible = "marvell,armada-3700-rwtm-mailbox" }, 207 { }, 208 }; 209 210 MODULE_DEVICE_TABLE(of, armada_37xx_mbox_match); 211 212 static struct platform_driver armada_37xx_mbox_driver = { 213 .probe = armada_37xx_mbox_probe, 214 .remove = armada_37xx_mbox_remove, 215 .driver = { 216 .name = DRIVER_NAME, 217 .of_match_table = armada_37xx_mbox_match, 218 }, 219 }; 220 221 module_platform_driver(armada_37xx_mbox_driver); 222 223 MODULE_LICENSE("GPL v2"); 224 MODULE_DESCRIPTION("rWTM BIU Mailbox driver for Armada 37xx"); 225 MODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>"); 226