[v2,2/3] mailbox: introduce ARM SMC based mailbox
diff mbox series

Message ID 1500852206-28771-3-git-send-email-andre.przywara@arm.com
State New, archived
Headers show
Series
  • mailbox: arm: introduce smc triggered mailbox
Related show

Commit Message

Andre Przywara July 23, 2017, 11:23 p.m. UTC
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.przywara@arm.com>
---
 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

Comments

Alexey Klimov July 31, 2017, 5:34 p.m. UTC | #1
Hi Andre,

On Mon, Jul 24, 2017 at 12:23 AM, Andre Przywara <andre.przywara@arm.com> wrote:
> 
> This mailbox driver implements a mailbox which signals transmitted data
> via an ARM smc (secure monitor call) instruction. The mailbox receiver

As far as I can see, this driver also supports transmission via hvc.
However, almost everywhere here only smc instruction is mentioned.
Is it okay from your point of view?

> 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.przywara@arm.com>
> ---
>  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-mailbox.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
> +};

How the usage of timer-based polling tx_done method is justified (since it
always returns 'true')?

At the first glance, will it be more efficient to use TXDONE_BY_ACK here?
For instance, a controller will say:

	mbox->txdone_poll = false;
	mbox->txdone_irq = false;

and a client will say:

	cl->tx_block = true;
	cl->knows_txdone = true,

and the client will tick tx machinery with its mbox_client_txdone() immediately
after sending of message (since 'This mailbox is synchronous'). Otherwise,
why framework and client should wait >=1 ms before sending next message?



> +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");


Out of curiosity, did you try to use more than one channel with this driver?


[..]

Best regards,
Alexey
Jassi Brar Aug. 1, 2017, 4:06 p.m. UTC | #2
On Mon, Jul 24, 2017 at 4:53 AM, Andre Przywara <andre.przywara@arm.com> wrote:

> +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);
> +
Sorry, I didn't notice earlier that you cull parameters [R2, R7] to
SMC/HVC call by making them all 0s ...
        arm_smccc_smc(function_id, msg, 0, 0, 0, 0, 0, 0, &res);

I see you have SCMI/SCPI on your mind, however we need to make
controller drivers independent of users.

I think this driver should simply emulate mailbox over the SMC/HVC
calls. That is, respect all parameters.
So maybe we should define a
   struct arm_smccc_mbox_cmd {
         unsigned long a0, a1, a2, a3, a4, a5, a6, a7;
   }

and have
static int arm_smc_send_data(struct mbox_chan *link, void *data)
{
       struct arm_smc_chan_data *chan_data = link->con_priv;
       struct arm_smccc_res res;
       struct arm_smccc_mbox_cmd *cmd = data;
       u32 function_id = cmd->a0;

       ..... and pass cmd.{a1, a2, a3, a4, a5, a6, a7} to the SMC/HVC call.
          ....
}

And BTW, instead of specifying 'func-ids' in DT, we can have
'num-chans' which will tell how many identical channels to populate.
Also because the Function-Identifier seems like belong to the client
driver more than controller.

Cheers!
Jiaying Liang July 31, 2018, 11:43 p.m. UTC | #3
Added missing maintainers from the previous reply

On Sunday, 23 July 2017 16:26:55 UTC-7, Andre Przywara wrote:
>
> 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...@arm.com>
> ---
>  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-mailbox.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;
> +}
>
We have a use case that the message to post to the mailbox is larger
than 32bit. Can we change the SMC request to take the pointer as the
message argument instead of the the value?
But in this case, I am not clear on how the ATF to validate if the pointer
is valid. Any suggestions?

Furthermore, the received response can be larger that smc response
a0, any suggestion to solve this issue? reuse the input data pointer for
ATF to write copy the response data?

In case of asynchronous request, the request can be from remote first.
How to solve this issue to use a generic SMC mailbox driver?
Use mailbox mbox_send_message() for a separate rx request channel?

Thanks,
Wendy

+
> +/* 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...@arm.com>");
> +MODULE_DESCRIPTION("Generic ARM smc mailbox driver");
> +MODULE_LICENSE("GPL v2");
> --
> 2.8.2
>
> --
You received this message because you are subscribed to the Google Groups
"linux-sunxi" group.
To unsubscribe from this group and stop receiving emails from it, send an
email to linux-sunxi+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Patch
diff mbox series

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-mailbox.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.przywara@arm.com>");
+MODULE_DESCRIPTION("Generic ARM smc mailbox driver");
+MODULE_LICENSE("GPL v2");