[PATCH v4 10/10] [DO NOT MERGE] drivers: firmware: msgbox demo

From: Samuel Holland
Date: Mon Aug 19 2019 - 23:23:24 EST


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 <samuel@xxxxxxxxxxxx>
---
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 <samuel@xxxxxxxxxxxx>
+
+#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 <samuel@xxxxxxxxxxxx>");
+MODULE_DESCRIPTION("sunxi msgbox demo");
+MODULE_LICENSE("GPL v2");
--
2.21.0