This driver provides a trivial mailbox client that can be used with the
mailbox-demo branch of https://github.com/crust-firmware/crust for
verifying the functionality of the sunxi-msgbox driver.

This is not a "real" driver, nor a "real" firmware protocol. This driver
is not intended to be merged. It is provided only as an example that
won't interfere with any other hardware.

Signed-off-by: Samuel Holland <sam...@sholland.org>
---
 arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi |  24 ++
 arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi  |  24 ++
 drivers/firmware/Kconfig                      |   6 +
 drivers/firmware/Makefile                     |   1 +
 drivers/firmware/sunxi_msgbox_demo.c          | 310 ++++++++++++++++++
 5 files changed, 365 insertions(+)
 create mode 100644 drivers/firmware/sunxi_msgbox_demo.c

diff --git a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
index 428f539a091a..78315d5512db 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-a64.dtsi
@@ -121,6 +121,30 @@
                };
        };
 
+       demo_0 {
+               compatible = "allwinner,sunxi-msgbox-demo";
+               mboxes = <&msgbox 0>, <&msgbox 1>;
+               mbox-names = "tx", "rx";
+       };
+
+       demo_1 {
+               compatible = "allwinner,sunxi-msgbox-demo";
+               mboxes = <&msgbox 2>, <&msgbox 3>;
+               mbox-names = "tx", "rx";
+       };
+
+       demo_2 {
+               compatible = "allwinner,sunxi-msgbox-demo";
+               mboxes = <&msgbox 4>, <&msgbox 5>;
+               mbox-names = "tx", "rx";
+       };
+
+       demo_3 {
+               compatible = "allwinner,sunxi-msgbox-demo";
+               mboxes = <&msgbox 6>, <&msgbox 7>;
+               mbox-names = "tx", "rx";
+       };
+
        de: display-engine {
                compatible = "allwinner,sun50i-a64-display-engine";
                allwinner,pipelines = <&mixer0>,
diff --git a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi 
b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
index f002a496d7cb..5a2d85b7e0a1 100644
--- a/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
+++ b/arch/arm64/boot/dts/allwinner/sun50i-h5.dtsi
@@ -76,6 +76,30 @@
                };
        };
 
+       demo_0 {
+               compatible = "allwinner,sunxi-msgbox-demo";
+               mboxes = <&msgbox 0>, <&msgbox 1>;
+               mbox-names = "tx", "rx";
+       };
+
+       demo_1 {
+               compatible = "allwinner,sunxi-msgbox-demo";
+               mboxes = <&msgbox 2>, <&msgbox 3>;
+               mbox-names = "tx", "rx";
+       };
+
+       demo_2 {
+               compatible = "allwinner,sunxi-msgbox-demo";
+               mboxes = <&msgbox 4>, <&msgbox 5>;
+               mbox-names = "tx", "rx";
+       };
+
+       demo_3 {
+               compatible = "allwinner,sunxi-msgbox-demo";
+               mboxes = <&msgbox 6>, <&msgbox 7>;
+               mbox-names = "tx", "rx";
+       };
+
        psci {
                compatible = "arm,psci-0.2";
                method = "smc";
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index ba8d3d0ef32c..e0f8f3c856c1 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -240,6 +240,12 @@ config QCOM_SCM_DOWNLOAD_MODE_DEFAULT
 
          Say Y here to enable "download mode" by default.
 
+config SUNXI_MSGBOX_DEMO
+       tristate "sunxi msgbox demo"
+       depends on MAILBOX
+       help
+         Demo client for demo firmware to use in mailbox driver validation.
+
 config TI_SCI_PROTOCOL
        tristate "TI System Control Interface (TISCI) Message Protocol"
        depends on TI_MESSAGE_MANAGER
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 3fa0b34eb72f..6f8e17a854b6 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_QCOM_SCM)                += qcom_scm.o
 obj-$(CONFIG_QCOM_SCM_64)      += qcom_scm-64.o
 obj-$(CONFIG_QCOM_SCM_32)      += qcom_scm-32.o
 CFLAGS_qcom_scm-32.o :=$(call as-instr,.arch armv7-a\n.arch_extension 
sec,-DREQUIRES_SEC=1) -march=armv7-a
+obj-$(CONFIG_SUNXI_MSGBOX_DEMO)        += sunxi_msgbox_demo.o
 obj-$(CONFIG_TI_SCI_PROTOCOL)  += ti_sci.o
 obj-$(CONFIG_TRUSTED_FOUNDATIONS) += trusted_foundations.o
 
diff --git a/drivers/firmware/sunxi_msgbox_demo.c 
b/drivers/firmware/sunxi_msgbox_demo.c
new file mode 100644
index 000000000000..9431b1ef1841
--- /dev/null
+++ b/drivers/firmware/sunxi_msgbox_demo.c
@@ -0,0 +1,310 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// Copyright (c) 2018-2019 Samuel Holland <sam...@sholland.org>
+
+#include <linux/completion.h>
+#include <linux/mailbox_client.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/random.h>
+
+enum {
+       OP_MAGIC,
+       OP_VERSION,
+       OP_LOOPBACK,
+       OP_LOOPBACK_INVERTED,
+       OP_TIME_SECONDS,
+       OP_TIME_TICKS,
+       OP_DELAY_MICROS,
+       OP_DELAY_MILLIS,
+       OP_ADDR_SET_LO,
+       OP_ADDR_SET_HI,
+       OP_ADDR_READ,
+       OP_ADDR_WRITE,
+       OP_INVALID_1,
+       OP_INVALID_2,
+       OP_RESET = 16,
+};
+
+struct msgbox_demo {
+       struct mbox_chan *rx_chan;
+       struct mbox_chan *tx_chan;
+       struct mbox_client cl;
+       struct completion completion;
+       uint32_t request;
+       uint32_t response;
+       uint32_t address;
+       uint32_t value;
+};
+
+static void msgbox_demo_rx(struct mbox_client *cl, void *msg)
+{
+       struct msgbox_demo *demo = container_of(cl, struct msgbox_demo, cl);
+
+       demo->response = *(uint32_t *)msg;
+       complete(&demo->completion);
+}
+
+static int msgbox_demo_tx(struct msgbox_demo *demo, uint32_t request)
+{
+       unsigned long timeout = msecs_to_jiffies(10);
+       int ret;
+
+       demo->request  = request;
+       demo->response = 0;
+       reinit_completion(&demo->completion);
+
+       ret = mbox_send_message(demo->tx_chan, &demo->request);
+       if (ret < 0) {
+               dev_err(demo->cl.dev, "Failed to send request: %d\n", ret);
+               return ret;
+       }
+
+       if (wait_for_completion_timeout(&demo->completion, timeout))
+               return 0;
+
+       return -ETIMEDOUT;
+}
+
+static void msgbox_demo_do_operation(struct msgbox_demo *demo, uint16_t op)
+{
+       struct device *dev = demo->cl.dev;
+       uint16_t data = 0;
+       uint32_t resp = 0;
+       int exp = 0;
+       int ret;
+
+       switch (op) {
+       case OP_MAGIC:
+               resp = 0x1a2a3a4a;
+               break;
+       case OP_LOOPBACK:
+               data = get_random_u32();
+               resp = data;
+               break;
+       case OP_LOOPBACK_INVERTED:
+               data = get_random_u32();
+               resp = ~data;
+               break;
+       case OP_DELAY_MICROS:
+               data = 25000;
+               exp  = -ETIMEDOUT;
+               break;
+       case OP_DELAY_MILLIS:
+               data = 500;
+               exp  = -ETIMEDOUT;
+               break;
+       case OP_ADDR_SET_LO:
+               data = demo->address & 0xffff;
+               resp = demo->address;
+               break;
+       case OP_ADDR_SET_HI:
+               data = demo->address >> 16;
+               break;
+       case OP_ADDR_WRITE:
+               data = demo->value;
+               resp = demo->value;
+               break;
+       case OP_INVALID_1:
+       case OP_INVALID_2:
+               resp = -1U;
+               break;
+       case OP_RESET:
+               exp  = -ETIMEDOUT;
+               break;
+       }
+
+       dev_info(demo->cl.dev, "Sending opcode %d, data 0x%08x\n", op, data);
+       ret = msgbox_demo_tx(demo, op << 16 | data);
+
+       if (ret) {
+               /* Nothing was received. */
+               if (exp)
+                       dev_info(dev, "No response received, as expected\n");
+               else
+                       dev_err(dev, "Timeout receiving response\n");
+               return;
+       }
+
+       /* Something was received. */
+       if (exp)
+               dev_err(dev, "Unexpected response 0x%08x\n", demo->response);
+       else if (!resp)
+               dev_info(dev, "Received response 0x%08x\n", demo->response);
+       else if (demo->response == resp)
+               dev_info(dev, "Good response 0x%08x\n", resp);
+       else
+               dev_err(dev, "Expected 0x%08x, received 0x%08x\n",
+                            resp, demo->response);
+}
+
+ssize_t demo_address_show(struct device *dev, struct device_attribute *attr,
+                         char *buf)
+{
+       struct msgbox_demo *demo = dev_get_drvdata(dev);
+
+       return sprintf(buf, "%08x\n", demo->address);
+}
+
+static ssize_t demo_address_store(struct device *dev,
+                                 struct device_attribute *attr,
+                                 const char *buf, size_t count)
+{
+       struct msgbox_demo *demo = dev_get_drvdata(dev);
+       uint32_t val;
+
+       if (sscanf(buf, "%x", &val)) {
+               demo->address = val;
+               msgbox_demo_do_operation(demo, OP_ADDR_SET_HI);
+               msgbox_demo_do_operation(demo, OP_ADDR_SET_LO);
+               return count;
+       }
+
+       return 0;
+}
+
+ssize_t demo_value_show(struct device *dev, struct device_attribute *attr,
+                       char *buf)
+{
+       struct msgbox_demo *demo = dev_get_drvdata(dev);
+
+       msgbox_demo_do_operation(demo, OP_ADDR_READ);
+       demo->value = demo->response;
+
+       return sprintf(buf, "%08x\n", demo->value);
+}
+
+static ssize_t demo_value_store(struct device *dev,
+                               struct device_attribute *attr,
+                               const char *buf, size_t count)
+{
+       struct msgbox_demo *demo = dev_get_drvdata(dev);
+       int16_t val;
+
+       if (sscanf(buf, "%hx", &val)) {
+               demo->value = (int32_t)val;
+               msgbox_demo_do_operation(demo, OP_ADDR_WRITE);
+               return count;
+       }
+
+       return 0;
+}
+
+static ssize_t demo_operation_store(struct device *dev,
+                                   struct device_attribute *attr,
+                                   const char *buf, size_t count)
+{
+       struct msgbox_demo *demo = dev_get_drvdata(dev);
+       uint16_t val;
+
+       if (sscanf(buf, "%hu", &val)) {
+               msgbox_demo_do_operation(demo, val);
+               return count;
+       }
+
+       return 0;
+}
+
+static DEVICE_ATTR(demo_address,   0644, demo_address_show, 
demo_address_store);
+static DEVICE_ATTR(demo_value,     0644, demo_value_show,   demo_value_store);
+static DEVICE_ATTR(demo_operation, 0200, NULL,              
demo_operation_store);
+
+static int msgbox_demo_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct device_attribute *attr;
+       struct msgbox_demo *demo;
+       int ret;
+
+       demo = devm_kzalloc(dev, sizeof(*demo), GFP_KERNEL);
+       if (!demo)
+               return -ENOMEM;
+
+       demo->cl.dev         = dev;
+       demo->cl.rx_callback = msgbox_demo_rx;
+
+       if (of_get_property(dev->of_node, "mbox-names", NULL)) {
+               demo->rx_chan = mbox_request_channel_byname(&demo->cl, "rx");
+               if (IS_ERR(demo->rx_chan)) {
+                       ret = PTR_ERR(demo->rx_chan);
+                       dev_err(dev, "Failed to request rx mailbox channel\n");
+                       goto err;
+               }
+               demo->tx_chan = mbox_request_channel_byname(&demo->cl, "tx");
+               if (IS_ERR(demo->tx_chan)) {
+                       ret = PTR_ERR(demo->tx_chan);
+                       dev_err(dev, "Failed to request tx mailbox channel\n");
+                       goto err_free_rx_chan;
+               }
+       } else {
+               demo->rx_chan = mbox_request_channel(&demo->cl, 0);
+               demo->tx_chan = demo->rx_chan;
+               if (IS_ERR(demo->tx_chan)) {
+                       ret = PTR_ERR(demo->tx_chan);
+                       dev_err(dev, "Failed to request mailbox channel\n");
+                       goto err;
+               }
+       }
+
+       attr = &dev_attr_demo_address;
+       ret = device_create_file(dev, attr);
+       if (ret)
+               goto err_creating_files;
+       attr = &dev_attr_demo_value;
+       ret = device_create_file(dev, attr);
+       if (ret)
+               goto err_creating_files;
+       attr = &dev_attr_demo_operation;
+       ret = device_create_file(dev, attr);
+       if (ret)
+               goto err_creating_files;
+
+       init_completion(&demo->completion);
+
+       platform_set_drvdata(pdev, demo);
+
+       msgbox_demo_do_operation(demo, OP_VERSION);
+
+       return 0;
+
+err_creating_files:
+       dev_err(dev, "Failed to create sysfs attribute %s: %d\n",
+               attr->attr.name, ret);
+       if (demo->tx_chan != demo->rx_chan)
+               mbox_free_channel(demo->tx_chan);
+err_free_rx_chan:
+       mbox_free_channel(demo->rx_chan);
+err:
+       return ret;
+}
+
+static int msgbox_demo_remove(struct platform_device *pdev)
+{
+       struct msgbox_demo *demo = platform_get_drvdata(pdev);
+
+       if (demo->tx_chan != demo->rx_chan)
+               mbox_free_channel(demo->tx_chan);
+       mbox_free_channel(demo->rx_chan);
+
+       return 0;
+}
+
+static const struct of_device_id msgbox_demo_of_match[] = {
+       { .compatible = "allwinner,sunxi-msgbox-demo" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, msgbox_demo_of_match);
+
+static struct platform_driver msgbox_demo_driver = {
+       .driver = {
+               .name = KBUILD_MODNAME,
+               .of_match_table = msgbox_demo_of_match,
+       },
+       .probe  = msgbox_demo_probe,
+       .remove = msgbox_demo_remove,
+};
+module_platform_driver(msgbox_demo_driver);
+
+MODULE_AUTHOR("Samuel Holland <sam...@sholland.org>");
+MODULE_DESCRIPTION("sunxi msgbox demo");
+MODULE_LICENSE("GPL v2");
-- 
2.21.0

Reply via email to