This mailbox driver implements a mailbox which signals transmitted data
via an ARM smc (secure monitor call) instruction. The mailbox receiver
is implemented in firmware and can synchronously return data when it
returns execution to the non-secure world again.
An asynchronous receive path is not implemented.
This allows the usage of a mailbox to trigger firmware actions on SoCs
which either don't have a separate management processor or on which such
a core is not available. A user of this mailbox could be the SCP
interface.
Signed-off-by: Andre Przywara <andre.p...@xxxxxxx>
---
Âdrivers/mailbox/Kconfig      |  8 ++
Âdrivers/mailbox/Makefile     Â|  2 +
Âdrivers/mailbox/arm-smc-mailbox.c | 155 ++++++++++++++++++++++++++++++ ++++++++
Â3 files changed, 165 insertions(+)
Âcreate mode 100644 drivers/mailbox/arm-smc-mailbox.c
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index c5731e5..5664b7f 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -170,4 +170,12 @@ config BCM_FLEXRM_MBOX
ÂÂÂÂÂÂÂÂÂ ÂMailbox implementation of the Broadcom FlexRM ring manager,
ÂÂÂÂÂÂÂÂÂ Âwhich provides access to various offload engines on Broadcom
ÂÂÂÂÂÂÂÂÂ ÂSoCs. Say Y here if you want to use the Broadcom FlexRM.
+
+config ARM_SMC_MBOX
+ÂÂÂÂÂÂÂÂtristate "Generic ARM smc mailbox"
+ÂÂÂÂÂÂÂÂdepends on OF && HAVE_ARM_SMCCC
+ÂÂÂÂÂÂÂÂhelp
+ÂÂÂÂÂÂÂÂ ÂGeneric mailbox driver which uses ARM smc calls to call into
+ÂÂÂÂÂÂÂÂ Âfirmware for triggering mailboxes.
+
Âendif
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index d54e412..8ec6869 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -35,3 +35,5 @@ obj-$(CONFIG_BCM_FLEXRM_MBOX)ÂÂÂÂÂÂÂÂ+= bcm-flexrm-mailbox.o
Âobj-$(CONFIG_QCOM_APCS_IPC)ÂÂÂÂÂÂÂÂ+= qcom-apcs-ipc-mailbox.o
Â
Âobj-$(CONFIG_TEGRA_HSP_MBOX)ÂÂÂÂÂÂÂÂ+= tegra-hsp.o
+
+obj-$(CONFIG_ARM_SMC_MBOX)ÂÂÂÂÂÂÂÂ+= arm-smc-mailbox.o
diff --git a/drivers/mailbox/arm-smc-mailbox.c b/drivers/mailbox/arm-smc-mail box.c
new file mode 100644
index 0000000..d7b61a7
--- /dev/null
+++ b/drivers/mailbox/arm-smc-mailbox.c
@@ -0,0 +1,155 @@
+/*
+ * ÂCopyright (C) 2016,2017 ARM Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This device provides a mechanism for emulating a mailbox by using
+ * smc calls, allowing a "mailbox" consumer to sit in firmware running
+ * on the same core.
+ */
+
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/mailbox_controller.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/arm-smccc.h>
+
+#define ARM_SMC_MBOX_USE_HVCÂÂÂÂÂÂÂÂBIT(0)
+
+struct arm_smc_chan_data {
+ÂÂÂÂÂÂÂÂu32 function_id;
+ÂÂÂÂÂÂÂÂu32 flags;
+};
+
+static int arm_smc_send_data(struct mbox_chan *link, void *data)
+{
+ÂÂÂÂÂÂÂÂstruct arm_smc_chan_data *chan_data = link->con_priv;
+ÂÂÂÂÂÂÂÂu32 function_id = chan_data->function_id;
+ÂÂÂÂÂÂÂÂstruct arm_smccc_res res;
+ÂÂÂÂÂÂÂÂu32 msg = *(u32 *)data;
+
+ÂÂÂÂÂÂÂÂif (chan_data->flags & ARM_SMC_MBOX_USE_HVC)
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂarm_smccc_hvc(function_id, msg, 0, 0, 0, 0, 0, 0, &res);
+ÂÂÂÂÂÂÂÂelse
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂarm_smccc_smc(function_id, msg, 0, 0, 0, 0, 0, 0, &res);
+
+ÂÂÂÂÂÂÂÂmbox_chan_received_data(link, (void *)res.a0);
+
+ÂÂÂÂÂÂÂÂreturn 0;
+}
+
+/* This mailbox is synchronous, so we are always done. */
+static bool arm_smc_last_tx_done(struct mbox_chan *link)
+{
+ÂÂÂÂÂÂÂÂreturn true;
+}
+
+static const struct mbox_chan_ops arm_smc_mbox_chan_ops = {
+ÂÂÂÂÂÂÂÂ.send_dataÂÂÂÂÂÂÂÂ= arm_smc_send_data,
+ÂÂÂÂÂÂÂÂ.last_tx_doneÂÂÂÂÂÂÂÂ= arm_smc_last_tx_done
+};
+
+static int arm_smc_mbox_probe(struct platform_device *pdev)
+{
+ÂÂÂÂÂÂÂÂstruct device *dev = &pdev->dev;
+ÂÂÂÂÂÂÂÂstruct mbox_controller *mbox;
+ÂÂÂÂÂÂÂÂstruct arm_smc_chan_data *chan_data;
+ÂÂÂÂÂÂÂÂconst char *method;
+ÂÂÂÂÂÂÂÂbool use_hvc = false;
+ÂÂÂÂÂÂÂÂint ret, i;
+
+ÂÂÂÂÂÂÂÂret = of_property_count_elems_of_size(dev->of_node, "arm,func-ids",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ Â Â Âsizeof(u32));
+ÂÂÂÂÂÂÂÂif (ret < 0)
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
+
+ÂÂÂÂÂÂÂÂif (!of_property_read_string(dev->of_node, "method", &method)) {
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (!strcmp("hvc", method)) {
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂuse_hvc = true;
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ} else if (!strcmp("smc", method)) {
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂuse_hvc = false;
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ} else {
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂdev_warn(dev, "invalid \"method\" property: %s\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ method);
+
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -EINVAL;
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ}
+ÂÂÂÂÂÂÂÂ}
+
+ÂÂÂÂÂÂÂÂmbox = devm_kzalloc(dev, sizeof(*mbox), GFP_KERNEL);
+ÂÂÂÂÂÂÂÂif (!mbox)
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -ENOMEM;
+
+ÂÂÂÂÂÂÂÂmbox->num_chans = ret;
+ÂÂÂÂÂÂÂÂmbox->chans = devm_kcalloc(dev, mbox->num_chans, sizeof(*mbox->chans),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ Â GFP_KERNEL);
+ÂÂÂÂÂÂÂÂif (!mbox->chans)
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -ENOMEM;
+
+ÂÂÂÂÂÂÂÂchan_data = devm_kcalloc(dev, mbox->num_chans, sizeof(*chan_data),
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ GFP_KERNEL);
+ÂÂÂÂÂÂÂÂif (!chan_data)
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn -ENOMEM;
+
+ÂÂÂÂÂÂÂÂfor (i = 0; i < mbox->num_chans; i++) {
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂu32 function_id;
+
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂret = of_property_read_u32_index(dev->of_node,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ "arm,func-ids", i,
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ &function_id);
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (ret)
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
+
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂchan_data[i].function_id = function_id;
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂif (use_hvc)
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂchan_data[i].flags |= ARM_SMC_MBOX_USE_HVC;
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂmbox->chans[i].con_priv = &chan_data[i];
+ÂÂÂÂÂÂÂÂ}
+
+ÂÂÂÂÂÂÂÂmbox->txdone_poll = true;
+ÂÂÂÂÂÂÂÂmbox->txdone_irq = false;
+ÂÂÂÂÂÂÂÂmbox->txpoll_period = 1;
+ÂÂÂÂÂÂÂÂmbox->ops = &arm_smc_mbox_chan_ops;
+ÂÂÂÂÂÂÂÂmbox->dev = dev;
+
+ÂÂÂÂÂÂÂÂret = mbox_controller_register(mbox);
+ÂÂÂÂÂÂÂÂif (ret)
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂreturn ret;
+
+ÂÂÂÂÂÂÂÂplatform_set_drvdata(pdev, mbox);
+ÂÂÂÂÂÂÂÂdev_info(dev, "ARM SMC mailbox enabled with %d chan%s.\n",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ mbox->num_chans, mbox->num_chans == 1 ? "" : "s");
+
+ÂÂÂÂÂÂÂÂreturn ret;
+}
+
+static int arm_smc_mbox_remove(struct platform_device *pdev)
+{
+ÂÂÂÂÂÂÂÂstruct mbox_controller *mbox = platform_get_drvdata(pdev);
+
+ÂÂÂÂÂÂÂÂmbox_controller_unregister(mbox);
+ÂÂÂÂÂÂÂÂreturn 0;
+}
+
+static const struct of_device_id arm_smc_mbox_of_match[] = {
+ÂÂÂÂÂÂÂÂ{ .compatible = "arm,smc-mbox", },
+ÂÂÂÂÂÂÂÂ{},
+};
+MODULE_DEVICE_TABLE(of, arm_smc_mbox_of_match);
+
+static struct platform_driver arm_smc_mbox_driver = {
+ÂÂÂÂÂÂÂÂ.driver = {
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ.name = "arm-smc-mbox",
+ÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ.of_match_table = arm_smc_mbox_of_match,
+ÂÂÂÂÂÂÂÂ},
+ÂÂÂÂÂÂÂÂ.probeÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ= arm_smc_mbox_probe,
+ÂÂÂÂÂÂÂÂ.removeÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂÂ= arm_smc_mbox_remove,
+};
+module_platform_driver(arm_smc_mbox_driver);
+
+MODULE_AUTHOR("Andre Przywara <andre.p...@xxxxxxx>");
+MODULE_DESCRIPTION("Generic ARM smc mailbox driver");
+MODULE_LICENSE("GPL v2");
--
2.8.2