18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+ 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * rWTM BIU Mailbox driver for Armada 37xx 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Author: Marek Behun <marek.behun@nic.cz> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/device.h> 98c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 108c2ecf20Sopenharmony_ci#include <linux/io.h> 118c2ecf20Sopenharmony_ci#include <linux/kernel.h> 128c2ecf20Sopenharmony_ci#include <linux/mailbox_controller.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 168c2ecf20Sopenharmony_ci#include <linux/armada-37xx-rwtm-mailbox.h> 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#define DRIVER_NAME "armada-37xx-rwtm-mailbox" 198c2ecf20Sopenharmony_ci 208c2ecf20Sopenharmony_ci/* relative to rWTM BIU Mailbox Registers */ 218c2ecf20Sopenharmony_ci#define RWTM_MBOX_PARAM(i) (0x0 + ((i) << 2)) 228c2ecf20Sopenharmony_ci#define RWTM_MBOX_COMMAND 0x40 238c2ecf20Sopenharmony_ci#define RWTM_MBOX_RETURN_STATUS 0x80 248c2ecf20Sopenharmony_ci#define RWTM_MBOX_STATUS(i) (0x84 + ((i) << 2)) 258c2ecf20Sopenharmony_ci#define RWTM_MBOX_FIFO_STATUS 0xc4 268c2ecf20Sopenharmony_ci#define FIFO_STS_RDY 0x100 278c2ecf20Sopenharmony_ci#define FIFO_STS_CNTR_MASK 0x7 288c2ecf20Sopenharmony_ci#define FIFO_STS_CNTR_MAX 4 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci#define RWTM_HOST_INT_RESET 0xc8 318c2ecf20Sopenharmony_ci#define RWTM_HOST_INT_MASK 0xcc 328c2ecf20Sopenharmony_ci#define SP_CMD_COMPLETE BIT(0) 338c2ecf20Sopenharmony_ci#define SP_CMD_QUEUE_FULL_ACCESS BIT(17) 348c2ecf20Sopenharmony_ci#define SP_CMD_QUEUE_FULL BIT(18) 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct a37xx_mbox { 378c2ecf20Sopenharmony_ci struct device *dev; 388c2ecf20Sopenharmony_ci struct mbox_controller controller; 398c2ecf20Sopenharmony_ci void __iomem *base; 408c2ecf20Sopenharmony_ci int irq; 418c2ecf20Sopenharmony_ci}; 428c2ecf20Sopenharmony_ci 438c2ecf20Sopenharmony_cistatic void a37xx_mbox_receive(struct mbox_chan *chan) 448c2ecf20Sopenharmony_ci{ 458c2ecf20Sopenharmony_ci struct a37xx_mbox *mbox = chan->con_priv; 468c2ecf20Sopenharmony_ci struct armada_37xx_rwtm_rx_msg rx_msg; 478c2ecf20Sopenharmony_ci int i; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci rx_msg.retval = readl(mbox->base + RWTM_MBOX_RETURN_STATUS); 508c2ecf20Sopenharmony_ci for (i = 0; i < 16; ++i) 518c2ecf20Sopenharmony_ci rx_msg.status[i] = readl(mbox->base + RWTM_MBOX_STATUS(i)); 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci mbox_chan_received_data(chan, &rx_msg); 548c2ecf20Sopenharmony_ci} 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_cistatic irqreturn_t a37xx_mbox_irq_handler(int irq, void *data) 578c2ecf20Sopenharmony_ci{ 588c2ecf20Sopenharmony_ci struct mbox_chan *chan = data; 598c2ecf20Sopenharmony_ci struct a37xx_mbox *mbox = chan->con_priv; 608c2ecf20Sopenharmony_ci u32 reg; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci reg = readl(mbox->base + RWTM_HOST_INT_RESET); 638c2ecf20Sopenharmony_ci 648c2ecf20Sopenharmony_ci if (reg & SP_CMD_COMPLETE) 658c2ecf20Sopenharmony_ci a37xx_mbox_receive(chan); 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci if (reg & (SP_CMD_QUEUE_FULL_ACCESS | SP_CMD_QUEUE_FULL)) 688c2ecf20Sopenharmony_ci dev_err(mbox->dev, "Secure processor command queue full\n"); 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci writel(reg, mbox->base + RWTM_HOST_INT_RESET); 718c2ecf20Sopenharmony_ci if (reg) 728c2ecf20Sopenharmony_ci mbox_chan_txdone(chan, 0); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci return reg ? IRQ_HANDLED : IRQ_NONE; 758c2ecf20Sopenharmony_ci} 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_cistatic int a37xx_mbox_send_data(struct mbox_chan *chan, void *data) 788c2ecf20Sopenharmony_ci{ 798c2ecf20Sopenharmony_ci struct a37xx_mbox *mbox = chan->con_priv; 808c2ecf20Sopenharmony_ci struct armada_37xx_rwtm_tx_msg *msg = data; 818c2ecf20Sopenharmony_ci int i; 828c2ecf20Sopenharmony_ci u32 reg; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci if (!data) 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci reg = readl(mbox->base + RWTM_MBOX_FIFO_STATUS); 888c2ecf20Sopenharmony_ci if (!(reg & FIFO_STS_RDY)) 898c2ecf20Sopenharmony_ci dev_warn(mbox->dev, "Secure processor not ready\n"); 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci if ((reg & FIFO_STS_CNTR_MASK) >= FIFO_STS_CNTR_MAX) { 928c2ecf20Sopenharmony_ci dev_err(mbox->dev, "Secure processor command queue full\n"); 938c2ecf20Sopenharmony_ci return -EBUSY; 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_ci for (i = 0; i < 16; ++i) 978c2ecf20Sopenharmony_ci writel(msg->args[i], mbox->base + RWTM_MBOX_PARAM(i)); 988c2ecf20Sopenharmony_ci writel(msg->command, mbox->base + RWTM_MBOX_COMMAND); 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return 0; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cistatic int a37xx_mbox_startup(struct mbox_chan *chan) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci struct a37xx_mbox *mbox = chan->con_priv; 1068c2ecf20Sopenharmony_ci u32 reg; 1078c2ecf20Sopenharmony_ci int ret; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci ret = devm_request_irq(mbox->dev, mbox->irq, a37xx_mbox_irq_handler, 0, 1108c2ecf20Sopenharmony_ci DRIVER_NAME, chan); 1118c2ecf20Sopenharmony_ci if (ret < 0) { 1128c2ecf20Sopenharmony_ci dev_err(mbox->dev, "Cannot request irq\n"); 1138c2ecf20Sopenharmony_ci return ret; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* enable IRQ generation */ 1178c2ecf20Sopenharmony_ci reg = readl(mbox->base + RWTM_HOST_INT_MASK); 1188c2ecf20Sopenharmony_ci reg &= ~(SP_CMD_COMPLETE | SP_CMD_QUEUE_FULL_ACCESS | SP_CMD_QUEUE_FULL); 1198c2ecf20Sopenharmony_ci writel(reg, mbox->base + RWTM_HOST_INT_MASK); 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_ci return 0; 1228c2ecf20Sopenharmony_ci} 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_cistatic void a37xx_mbox_shutdown(struct mbox_chan *chan) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci u32 reg; 1278c2ecf20Sopenharmony_ci struct a37xx_mbox *mbox = chan->con_priv; 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci /* disable interrupt generation */ 1308c2ecf20Sopenharmony_ci reg = readl(mbox->base + RWTM_HOST_INT_MASK); 1318c2ecf20Sopenharmony_ci reg |= SP_CMD_COMPLETE | SP_CMD_QUEUE_FULL_ACCESS | SP_CMD_QUEUE_FULL; 1328c2ecf20Sopenharmony_ci writel(reg, mbox->base + RWTM_HOST_INT_MASK); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci devm_free_irq(mbox->dev, mbox->irq, chan); 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_cistatic const struct mbox_chan_ops a37xx_mbox_ops = { 1388c2ecf20Sopenharmony_ci .send_data = a37xx_mbox_send_data, 1398c2ecf20Sopenharmony_ci .startup = a37xx_mbox_startup, 1408c2ecf20Sopenharmony_ci .shutdown = a37xx_mbox_shutdown, 1418c2ecf20Sopenharmony_ci}; 1428c2ecf20Sopenharmony_ci 1438c2ecf20Sopenharmony_cistatic int armada_37xx_mbox_probe(struct platform_device *pdev) 1448c2ecf20Sopenharmony_ci{ 1458c2ecf20Sopenharmony_ci struct a37xx_mbox *mbox; 1468c2ecf20Sopenharmony_ci struct mbox_chan *chans; 1478c2ecf20Sopenharmony_ci int ret; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci mbox = devm_kzalloc(&pdev->dev, sizeof(*mbox), GFP_KERNEL); 1508c2ecf20Sopenharmony_ci if (!mbox) 1518c2ecf20Sopenharmony_ci return -ENOMEM; 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci /* Allocated one channel */ 1548c2ecf20Sopenharmony_ci chans = devm_kzalloc(&pdev->dev, sizeof(*chans), GFP_KERNEL); 1558c2ecf20Sopenharmony_ci if (!chans) 1568c2ecf20Sopenharmony_ci return -ENOMEM; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci mbox->base = devm_platform_ioremap_resource(pdev, 0); 1598c2ecf20Sopenharmony_ci if (IS_ERR(mbox->base)) 1608c2ecf20Sopenharmony_ci return PTR_ERR(mbox->base); 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci mbox->irq = platform_get_irq(pdev, 0); 1638c2ecf20Sopenharmony_ci if (mbox->irq < 0) 1648c2ecf20Sopenharmony_ci return mbox->irq; 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci mbox->dev = &pdev->dev; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* Hardware supports only one channel. */ 1698c2ecf20Sopenharmony_ci chans[0].con_priv = mbox; 1708c2ecf20Sopenharmony_ci mbox->controller.dev = mbox->dev; 1718c2ecf20Sopenharmony_ci mbox->controller.num_chans = 1; 1728c2ecf20Sopenharmony_ci mbox->controller.chans = chans; 1738c2ecf20Sopenharmony_ci mbox->controller.ops = &a37xx_mbox_ops; 1748c2ecf20Sopenharmony_ci mbox->controller.txdone_irq = true; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci ret = devm_mbox_controller_register(mbox->dev, &mbox->controller); 1778c2ecf20Sopenharmony_ci if (ret) { 1788c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not register mailbox controller\n"); 1798c2ecf20Sopenharmony_ci return ret; 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, mbox); 1838c2ecf20Sopenharmony_ci return ret; 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_cistatic const struct of_device_id armada_37xx_mbox_match[] = { 1888c2ecf20Sopenharmony_ci { .compatible = "marvell,armada-3700-rwtm-mailbox" }, 1898c2ecf20Sopenharmony_ci { }, 1908c2ecf20Sopenharmony_ci}; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, armada_37xx_mbox_match); 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_cistatic struct platform_driver armada_37xx_mbox_driver = { 1958c2ecf20Sopenharmony_ci .probe = armada_37xx_mbox_probe, 1968c2ecf20Sopenharmony_ci .driver = { 1978c2ecf20Sopenharmony_ci .name = DRIVER_NAME, 1988c2ecf20Sopenharmony_ci .of_match_table = armada_37xx_mbox_match, 1998c2ecf20Sopenharmony_ci }, 2008c2ecf20Sopenharmony_ci}; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_cimodule_platform_driver(armada_37xx_mbox_driver); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2"); 2058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("rWTM BIU Mailbox driver for Armada 37xx"); 2068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Marek Behun <marek.behun@nic.cz>"); 207