Add a mailbox driver framework/uclass
A mailbox is a hardware mechanism for transferring small message and/or
notifications between the CPU on which U-Boot runs and some other device
such as an auxilliary CPU running firmware or a hardware module.
This patch defines a standard API that connects mailbox clients to mailbox
providers (drivers). Initially, DT is the only supported method for
connecting the two.
The DT binding specification (mailbox.txt) was taken from Linux kernel
v4.5's Documentation/devicetree/bindings/mailbox/mailbox.txt.
Signed-off-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Simon Glass <sjg@chromium.org>
diff --git a/doc/device-tree-bindings/mailbox/mailbox.txt b/doc/device-tree-bindings/mailbox/mailbox.txt
new file mode 100644
index 0000000..be05b97
--- /dev/null
+++ b/doc/device-tree-bindings/mailbox/mailbox.txt
@@ -0,0 +1,32 @@
+* Generic Mailbox Controller and client driver bindings
+
+Generic binding to provide a way for Mailbox controller drivers to
+assign appropriate mailbox channel to client drivers.
+
+* Mailbox Controller
+
+Required property:
+- #mbox-cells: Must be at least 1. Number of cells in a mailbox
+ specifier.
+
+Example:
+ mailbox: mailbox {
+ ...
+ #mbox-cells = <1>;
+ };
+
+
+* Mailbox Client
+
+Required property:
+- mboxes: List of phandle and mailbox channel specifiers.
+
+Optional property:
+- mbox-names: List of identifier strings for each mailbox channel.
+
+Example:
+ pwr_cntrl: power {
+ ...
+ mbox-names = "pwr-ctrl", "rpc";
+ mboxes = <&mailbox 0 &mailbox 1>;
+ };
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 118b66e..f2a137a 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -30,6 +30,8 @@
source "drivers/led/Kconfig"
+source "drivers/mailbox/Kconfig"
+
source "drivers/memory/Kconfig"
source "drivers/misc/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 99dd07f..f6295d2 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -64,6 +64,7 @@
obj-y += watchdog/
obj-$(CONFIG_QE) += qe/
obj-$(CONFIG_U_QE) += qe/
+obj-y += mailbox/
obj-y += memory/
obj-y += pwm/
obj-y += input/
diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
new file mode 100644
index 0000000..295e6db
--- /dev/null
+++ b/drivers/mailbox/Kconfig
@@ -0,0 +1,13 @@
+menu "Mailbox Controller Support"
+
+config DM_MAILBOX
+ bool "Enable mailbox controllers using Driver Model"
+ depends on DM && OF_CONTROL
+ help
+ Enable support for the mailbox driver class. Mailboxes provide the
+ ability to transfer small messages and/or notifications from one
+ CPU to another CPU, or sometimes to dedicated HW modules. They form
+ the basis of a variety of inter-process/inter-CPU communication
+ protocols.
+
+endmenu
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
new file mode 100644
index 0000000..a2d96a4
--- /dev/null
+++ b/drivers/mailbox/Makefile
@@ -0,0 +1,5 @@
+# Copyright (c) 2016, NVIDIA CORPORATION.
+#
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_DM_MAILBOX) += mailbox-uclass.o
diff --git a/drivers/mailbox/mailbox-uclass.c b/drivers/mailbox/mailbox-uclass.c
new file mode 100644
index 0000000..73fa328
--- /dev/null
+++ b/drivers/mailbox/mailbox-uclass.c
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <fdtdec.h>
+#include <mailbox_client.h>
+#include <mailbox_uclass.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static inline struct mbox_ops *mbox_dev_ops(struct udevice *dev)
+{
+ return (struct mbox_ops *)dev->driver->ops;
+}
+
+static int mbox_of_xlate_default(struct mbox_chan *chan,
+ struct fdtdec_phandle_args *args)
+{
+ debug("%s(chan=%p)\n", __func__, chan);
+
+ if (args->args_count != 1) {
+ debug("Invaild args_count: %d\n", args->args_count);
+ return -EINVAL;
+ }
+
+ chan->id = args->args[0];
+
+ return 0;
+}
+
+int mbox_get_by_index(struct udevice *dev, int index, struct mbox_chan *chan)
+{
+ struct fdtdec_phandle_args args;
+ int ret;
+ struct udevice *dev_mbox;
+ struct mbox_ops *ops;
+
+ debug("%s(dev=%p, index=%d, chan=%p)\n", __func__, dev, index, chan);
+
+ ret = fdtdec_parse_phandle_with_args(gd->fdt_blob, dev->of_offset,
+ "mboxes", "#mbox-cells", 0,
+ index, &args);
+ if (ret) {
+ debug("%s: fdtdec_parse_phandle_with_args failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_of_offset(UCLASS_MAILBOX, args.node,
+ &dev_mbox);
+ if (ret) {
+ debug("%s: uclass_get_device_by_of_offset failed: %d\n",
+ __func__, ret);
+ return ret;
+ }
+ ops = mbox_dev_ops(dev_mbox);
+
+ chan->dev = dev_mbox;
+ if (ops->of_xlate)
+ ret = ops->of_xlate(chan, &args);
+ else
+ ret = mbox_of_xlate_default(chan, &args);
+ if (ret) {
+ debug("of_xlate() failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = ops->request(chan);
+ if (ret) {
+ debug("ops->request() failed: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+int mbox_get_by_name(struct udevice *dev, const char *name,
+ struct mbox_chan *chan)
+{
+ int index;
+
+ debug("%s(dev=%p, name=%s, chan=%p)\n", __func__, dev, name, chan);
+
+ index = fdt_find_string(gd->fdt_blob, dev->of_offset, "mbox-names",
+ name);
+ if (index < 0) {
+ debug("fdt_find_string() failed: %d\n", index);
+ return index;
+ }
+
+ return mbox_get_by_index(dev, index, chan);
+}
+
+int mbox_free(struct mbox_chan *chan)
+{
+ struct mbox_ops *ops = mbox_dev_ops(chan->dev);
+
+ debug("%s(chan=%p)\n", __func__, chan);
+
+ return ops->free(chan);
+}
+
+int mbox_send(struct mbox_chan *chan, const void *data)
+{
+ struct mbox_ops *ops = mbox_dev_ops(chan->dev);
+
+ debug("%s(chan=%p, data=%p)\n", __func__, chan, data);
+
+ return ops->send(chan, data);
+}
+
+int mbox_recv(struct mbox_chan *chan, void *data, ulong timeout_us)
+{
+ struct mbox_ops *ops = mbox_dev_ops(chan->dev);
+ ulong start_time;
+ int ret;
+
+ debug("%s(chan=%p, data=%p, timeout_us=%ld)\n", __func__, chan, data,
+ timeout_us);
+
+ start_time = timer_get_us();
+ /*
+ * Account for partial us ticks, but if timeout_us is 0, ensure we
+ * still don't wait at all.
+ */
+ if (timeout_us)
+ timeout_us++;
+
+ for (;;) {
+ ret = ops->recv(chan, data);
+ if (ret != -ENODATA)
+ return ret;
+ if ((timer_get_us() - start_time) >= timeout_us)
+ return -ETIMEDOUT;
+ }
+}
+
+UCLASS_DRIVER(mailbox) = {
+ .id = UCLASS_MAILBOX,
+ .name = "mailbox",
+};
diff --git a/include/dm/uclass-id.h b/include/dm/uclass-id.h
index f9ff45e..0777cbe 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -44,6 +44,7 @@
UCLASS_KEYBOARD, /* Keyboard input device */
UCLASS_LED, /* Light-emitting diode (LED) */
UCLASS_LPC, /* x86 'low pin count' interface */
+ UCLASS_MAILBOX, /* Mailbox controller */
UCLASS_MASS_STORAGE, /* Mass storage device */
UCLASS_MISC, /* Miscellaneous device */
UCLASS_MMC, /* SD / MMC card or chip */
diff --git a/include/mailbox_client.h b/include/mailbox_client.h
new file mode 100644
index 0000000..8345ea0
--- /dev/null
+++ b/include/mailbox_client.h
@@ -0,0 +1,149 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef _MAILBOX_CLIENT_H
+#define _MAILBOX_CLIENT_H
+
+/**
+ * A mailbox is a hardware mechanism for transferring small fixed-size messages
+ * and/or notifications between the CPU on which U-Boot runs and some other
+ * device such as an auxiliary CPU running firmware or a hardware module.
+ *
+ * Data transfer is optional; a mailbox may consist solely of a notification
+ * mechanism. When data transfer is implemented, it is via HW registers or
+ * FIFOs, rather than via RAM-based buffers. The mailbox API generally
+ * implements any communication protocol enforced solely by hardware, and
+ * leaves any higher-level protocols to other layers.
+ *
+ * A mailbox channel is a bi-directional mechanism that can send a message or
+ * notification to a single specific remote entity, and receive messages or
+ * notifications from that entity. The size, content, and format of such
+ * messages is defined by the mailbox implementation, or the remote entity with
+ * which it communicates; there is no general standard at this API level.
+ *
+ * A driver that implements UCLASS_MAILBOX is a mailbox provider. A provider
+ * will often implement multiple separate mailbox channels, since the hardware
+ * it manages often has this capability. mailbox_uclass.h describes the
+ * interface which mailbox providers must implement.
+ *
+ * Mailbox consumers/clients generate and send, or receive and process,
+ * messages. This header file describes the API used by clients.
+ */
+
+struct udevice;
+
+/**
+ * struct mbox_chan - A handle to a single mailbox channel.
+ *
+ * Clients provide storage for channels. The content of the channel structure
+ * is managed solely by the mailbox API and mailbox drivers. A mailbox channel
+ * is initialized by "get"ing the mailbox. The channel struct is passed to all
+ * other mailbox APIs to identify which mailbox to operate upon.
+ *
+ * @dev: The device which implements the mailbox.
+ * @id: The mailbox channel ID within the provider.
+ *
+ * Currently, the mailbox API assumes that a single integer ID is enough to
+ * identify and configure any mailbox channel for any mailbox provider. If this
+ * assumption becomes invalid in the future, the struct could be expanded to
+ * either (a) add more fields to allow mailbox providers to store additional
+ * information, or (b) replace the id field with an opaque pointer, which the
+ * provider would dynamically allocated during its .of_xlate op, and process
+ * during is .request op. This may require the addition of an extra op to clean
+ * up the allocation.
+ */
+struct mbox_chan {
+ struct udevice *dev;
+ /*
+ * Written by of_xlate. We assume a single id is enough for now. In the
+ * future, we might add more fields here.
+ */
+ unsigned long id;
+};
+
+/**
+ * mbox_get_by_index - Get/request a mailbox by integer index
+ *
+ * This looks up and requests a mailbox channel. The index is relative to the
+ * client device; each device is assumed to have n mailbox channels associated
+ * with it somehow, and this function finds and requests one of them. The
+ * mapping of client device channel indices to provider channels may be via
+ * device-tree properties, board-provided mapping tables, or some other
+ * mechanism.
+ *
+ * @dev: The client device.
+ * @index: The index of the mailbox channel to request, within the
+ * client's list of channels.
+ * @chan A pointer to a channel object to initialize.
+ * @return 0 if OK, or a negative error code.
+ */
+int mbox_get_by_index(struct udevice *dev, int index, struct mbox_chan *chan);
+
+/**
+ * mbox_get_by_name - Get/request a mailbox by name
+ *
+ * This looks up and requests a mailbox channel. The name is relative to the
+ * client device; each device is assumed to have n mailbox channels associated
+ * with it somehow, and this function finds and requests one of them. The
+ * mapping of client device channel names to provider channels may be via
+ * device-tree properties, board-provided mapping tables, or some other
+ * mechanism.
+ *
+ * @dev: The client device.
+ * @name: The name of the mailbox channel to request, within the client's
+ * list of channels.
+ * @chan A pointer to a channel object to initialize.
+ * @return 0 if OK, or a negative error code.
+ */
+int mbox_get_by_name(struct udevice *dev, const char *name,
+ struct mbox_chan *chan);
+
+/**
+ * mbox_free - Free a previously requested mailbox channel.
+ *
+ * @chan: A channel object that was previously successfully requested by
+ * calling mbox_get_by_*().
+ * @return 0 if OK, or a negative error code.
+ */
+int mbox_free(struct mbox_chan *chan);
+
+/**
+ * mbox_send - Send a message over a mailbox channel
+ *
+ * This function will send a message to the remote entity. It may return before
+ * the remote entity has received and/or processed the message.
+ *
+ * @chan: A channel object that was previously successfully requested by
+ * calling mbox_get_by_*().
+ * @data: A pointer to the message to transfer. The format and size of
+ * the memory region pointed at by @data is determined by the
+ * mailbox provider. Providers that solely transfer notifications
+ * will ignore this parameter.
+ * @return 0 if OK, or a negative error code.
+ */
+int mbox_send(struct mbox_chan *chan, const void *data);
+
+/**
+ * mbox_recv - Receive any available message from a mailbox channel
+ *
+ * This function will wait (up to the specified @timeout_us) for a message to
+ * be sent by the remote entity, and write the content of any such message
+ * into a caller-provided buffer.
+ *
+ * @chan: A channel object that was previously successfully requested by
+ * calling mbox_get_by_*().
+ * @data: A pointer to the buffer to receive the message. The format and
+ * size of the memory region pointed at by @data is determined by
+ * the mailbox provider. Providers that solely transfer
+ * notifications will ignore this parameter.
+ * @timeout_us: The maximum time to wait for a message to be available, in
+ * micro-seconds. A value of 0 does not wait at all.
+ * @return 0 if OK, -ENODATA if no message was available, or a negative error
+ * code.
+ */
+int mbox_recv(struct mbox_chan *chan, void *data, ulong timeout_us);
+
+#endif
diff --git a/include/mailbox_uclass.h b/include/mailbox_uclass.h
new file mode 100644
index 0000000..6a2994c
--- /dev/null
+++ b/include/mailbox_uclass.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.
+ *
+ * SPDX-License-Identifier: GPL-2.0
+ */
+
+#ifndef _MAILBOX_UCLASS_H
+#define _MAILBOX_UCLASS_H
+
+/* See mailbox_client.h for background documentation. */
+
+#include <mailbox_client.h>
+
+struct udevice;
+
+/**
+ * struct mbox_ops - The functions that a mailbox driver must implement.
+ */
+struct mbox_ops {
+ /**
+ * of_xlate - Translate a client's device-tree (OF) mailbox specifier.
+ *
+ * The mailbox core calls this function as the first step in
+ * implementing a client's mbox_get_by_*() call.
+ *
+ * If this function pointer is set to NULL, the mailbox core will use
+ * a default implementation, which assumes #mbox-cells = <1>, and that
+ * the DT cell contains a simple integer channel ID.
+ *
+ * At present, the mailbox API solely supports device-tree. If this
+ * changes, other xxx_xlate() functions may be added to support those
+ * other mechanisms.
+ *
+ * @chan: The channel to hold the translation result.
+ * @args: The mailbox specifier values from device tree.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*of_xlate)(struct mbox_chan *chan,
+ struct fdtdec_phandle_args *args);
+ /**
+ * request - Request a translated channel.
+ *
+ * The mailbox core calls this function as the second step in
+ * implementing a client's mbox_get_by_*() call, following a successful
+ * xxx_xlate() call.
+ *
+ * @chan: The channel to request; this has been filled in by a
+ * previoux xxx_xlate() function call.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*request)(struct mbox_chan *chan);
+ /**
+ * free - Free a previously requested channel.
+ *
+ * This is the implementation of the client mbox_free() API.
+ *
+ * @chan: The channel to free.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*free)(struct mbox_chan *chan);
+ /**
+ * send - Send a message over a mailbox channel
+ *
+ * @chan: The channel to send to the message to.
+ * @data: A pointer to the message to send.
+ * @return 0 if OK, or a negative error code.
+ */
+ int (*send)(struct mbox_chan *chan, const void *data);
+ /**
+ * recv - Receive any available message from the channel.
+ *
+ * This function does not block. If not message is immediately
+ * available, the function should return an error.
+ *
+ * @chan: The channel to receive to the message from.
+ * @data: A pointer to the buffer to hold the received message.
+ * @return 0 if OK, -ENODATA if no message was available, or a negative
+ * error code.
+ */
+ int (*recv)(struct mbox_chan *chan, void *data);
+};
+
+#endif