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