Modern SoCs typically employ a central symmetric multiprocessing (SMP)
application processor running Linux, with several other asymmetric
multiprocessing (AMP) heterogeneous processors running different instances
of operating system, whether Linux or any other flavor of real-time OS.
OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP.
Typically, the dual cortex-A9 is running Linux in a SMP configuration, and
each of the other three cores (two M3 cores and a DSP) is running its own
instance of RTOS in an AMP configuration.
AMP remote processors typically employ dedicated DSP codecs and multimedia
hardware accelerators, and therefore are often used to offload cpu-intensive
multimedia tasks from the main application processor. They could also be
used to control latency-sensitive sensors, drive "random" hardware blocks,
or just perform background tasks while the main CPU is idling.
Users of those remote processors can either be userland apps (e.g.
multimedia frameworks talking with remote OMX components) or kernel drivers
(controlling hardware accessible only by the remote processor, reserving
kernel-controlled resources on behalf of the remote processor, etc..).
This patch set adds a generic AMP/IPC framework which makes it possible to
control (power on, boot, power off) and communicate (simply send and receive
messages) with those remote processors.
Specifically, we're adding:
* Rpmsg: a virtio-based messaging bus that allows kernel drivers to
communicate with remote processors available on the system. In turn,
drivers could then expose appropriate user space interfaces, if needed
(tasks running on remote processors often have direct access to sensitive
resources like the system's physical memory, gpios, i2c buses, dma
controllers, etc.. so one normally wouldn't want to allow userland to
send everything/everywhere it wants).
Every rpmsg device is a communication channel with a service running on a
remote processor (thus rpmsg devices are called channels). Channels are
identified by a textual name (which is used to match drivers to devices)
and have a local ("source") rpmsg address, and remote ("destination") rpmsg
address. When a driver starts listening on a channel (most commonly when it
is probed), the bus assigns the driver a unique rpmsg src address (a 32 bit
integer) and binds it with the driver's rx callback handler. This way
when inbound messages arrive to this src address, the rpmsg core dispatches
them to that driver, by invoking the driver's rx handler with the payload
of the incoming message.
Once probed, rpmsg drivers can also immediately start sending messages to the
remote rpmsg service by using simple sending API; no need even to specify
a destination address, since that's part of the rpmsg channel, and the rpmsg
bus uses the channel's dst address when it constructs the message (for
more demanding use cases, there's also an extended API, which does allow
full control of both the src and dst addresses).
The rpmsg bus is using virtio to send and receive messages: every pair
of processors share two vrings, which are used to send and receive the
messages over shared memory (one vring is used for tx, and the other one
for rx). Kicking the remote processor (i.e. letting it know it has a pending
message on its vring) is accomplished by means available on the platform we
run on (e.g. OMAP is using its mailbox to both interrupt the remote processor
and tell it which vring is kicked at the same time). The header of every
message sent on the rpmsg bus contains src and dst addresses, which make it
possible to multiplex several rpmsg channels on the same vring.
One nice property of the rpmsg bus is that device creation is completely
dynamic: remote processors can announce the existence of remote rpmsg
services by sending a "name service" messages (which contain the name and
rpmsg addr of the remote service). Those messages are picked by the rpmsg
bus, which in turn dynamically creates and registers the rpmsg channels
(i.e devices) which represents the remote services. If/when a relevant rpmsg
driver is registered, it will be immediately probed by the bus, and can then
start "talking" to the remote service.
Similarly, we can use this technique to dynamically create virtio devices
(and new vrings) which would then represent e.g. remote network, console
and block devices that will be driven by the existing virtio drivers
(this is still not implemented though; it requires some RTOS work as we're
not booting Linux on OMAP's remote processors). Creating new vrings might
also be desired by users who just don't want to use the shared rpmsg vrings
(for performance or any other functionality reasons).
In addition to dynamic creation of rpmsg channels, the rpmsg bus also
supports creation of static channels. This is needed in two cases:
- when a certain remote processor doesn't support sending those "name
service" announcements. In that case, a static table of remote rpmsg
services must be used to create the rpmsg channels.
- to support rpmsg server drivers, which aren't bound to a specific remote
rpmsg address. Instead, they just listen on a local address, waiting for
incoming messages. To send a message, those server drivers need to use
the rpmsg_sendto() API, so they can explicitly indicate the dst address
every time.
There are already several immediate use cases for rpmsg drivers: OMX
offloading (already being used on OMAP4), hardware resource manager (remote
processors on OMAP4 need to ask Linux to enable/disable hardware resources
on its behalf), remote display driver on Netra (dm8168), where the display
is controlled by a remote M3 processor (and a Linux v4l2/fbdev driver will
use rpmsg to communicate with that remote display driver).
* Remoteproc: a generic driver that maintains the state of the remote
processor(s). Simple rproc_get() and rproc_put() API is exposed, which
drivers can use when needed (first driver to call get() will load a firmware,
configure an iommu if needed, and boot the remote processor, while last
driver to call put() will power it down).
Hardware differences are abstracted as usual: a platform-specific driver
registers its own start/stop handlers in the generic remoteproc driver,
and those are invoked when its time to power up/down the processor. As a
reference, this patch set include remoteproc support for both OMAP4's
cortex-M3 and Davinci's DSP, tested on the pandaboard and hawkboard,
respectively.
The gory part of remoteproc is the firmware handling. We tried to come up
with a simple binary format that will require minimum kernel code to handle,
but at the same time be generic enough in the hopes that it would prove
useful to others as well. We're not at all hang onto the binary format
we picked: if there's any technical reason to change it to support other
platforms, please let us know. We do realize that a single binary firmware
structure might eventually not work for everyone. it did prove useful for
us though; we adopted both the OMAP and Davinci platforms (and their
completely different remote processor devices) to this simple binary
structure, so we don't have to duplicate the firmware handling code.
It's also not entirely clear whether remoteproc should really be an
independent layer, or if it should just be squashed with the host-specific
component of the rpmsg framework (there isn't really a remoteproc use case
that doesn't need rpmsg anyway). Looking ahead, functionality like error
recovery and power management might require coupling those two together
as well.
Important stuff:
* Thanks Brian Swetland for great design ideas and fruitful meetings and
Arnd Bergmann for pointing us at virtio, which is just awesome.
* Thanks Bhavin Shah, Mark Grosen, Suman Anna, Fernando Guzman Lugo,
Shreyas Prasad, Gilbert Pitney, Armando Uribe De Leon, Robert Tivy and
Alan DeMars for all your help. You know what you did.
* Patches are based on 3.0-rc3. Code was tested on OMAP4 using a panda
board (and remoteproc was also tested on Davinci da850-evm and hawkboard).
* I will be replying to this email with an attachment of the M3 firmware
image I work with, so anyone with a panda board can take the code for a
ride (put the image in /lib/firmware, and tell me if stuff doesn't work
for you: there are a few omap iommu fixes that are circulating but not
merged yet).
* Licensing: definitions that needs to be shared with remote processors
were put in BSD-licensed header files, so anyone can use them to develop
compatible peers.
* The M3 RTOS source code itself is BSD licensed and will be publicly
released too (it's in the works).
* This work is not complete; there are still several things to implement,
improve and clean up, but the intention is to start the review, and not
wait until everything is finished.
Guzman Lugo, Fernando (2):
remoteproc: add omap implementation
omap: add remoteproc devices
Mark Grosen (2):
remoteproc: add davinci implementation
davinci: da850: add remoteproc dsp device
Ohad Ben-Cohen (4):
drivers: add generic remoteproc framework
omap: add carveout memory support for remoteproc
drivers: introduce rpmsg, a remote-processor messaging bus
rpmsg: add omap host backend
Documentation/ABI/testing/sysfs-bus-rpmsg | 75 ++
Documentation/remoteproc.txt | 170 ++++
Documentation/rpmsg.txt | 308 +++++++
arch/arm/mach-davinci/board-da850-evm.c | 4 +
arch/arm/mach-davinci/board-omapl138-hawk.c | 4 +
arch/arm/mach-davinci/da850.c | 14 +
arch/arm/mach-davinci/devices-da8xx.c | 15 +
arch/arm/mach-davinci/include/mach/da8xx.h | 1 +
arch/arm/mach-davinci/include/mach/remoteproc.h | 28 +
arch/arm/mach-omap2/Makefile | 2 +
arch/arm/mach-omap2/remoteproc.c | 159 ++++
arch/arm/plat-omap/Kconfig | 8 +
arch/arm/plat-omap/devices.c | 14 +-
arch/arm/plat-omap/include/plat/dsp.h | 6 +-
arch/arm/plat-omap/include/plat/remoteproc.h | 41 +
drivers/Kconfig | 3 +
drivers/Makefile | 2 +
drivers/remoteproc/Kconfig | 44 +
drivers/remoteproc/Makefile | 7 +
drivers/remoteproc/davinci_remoteproc.c | 147 ++++
drivers/remoteproc/omap_remoteproc.c | 243 ++++++
drivers/remoteproc/remoteproc.c | 780 +++++++++++++++++
drivers/rpmsg/Kconfig | 16 +
drivers/rpmsg/Makefile | 3 +
drivers/rpmsg/host/Kconfig | 11 +
drivers/rpmsg/host/Makefile | 1 +
drivers/rpmsg/host/omap_rpmsg.c | 540 ++++++++++++
drivers/rpmsg/host/omap_rpmsg.h | 69 ++
drivers/rpmsg/virtio_rpmsg_bus.c | 1036 +++++++++++++++++++++++
include/linux/mod_devicetable.h | 10 +
include/linux/remoteproc.h | 273 ++++++
include/linux/rpmsg.h | 421 +++++++++
include/linux/virtio_ids.h | 1 +
33 files changed, 4453 insertions(+), 3 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg
create mode 100644 Documentation/remoteproc.txt
create mode 100644 Documentation/rpmsg.txt
create mode 100644 arch/arm/mach-davinci/include/mach/remoteproc.h
create mode 100644 arch/arm/mach-omap2/remoteproc.c
create mode 100644 arch/arm/plat-omap/include/plat/remoteproc.h
create mode 100644 drivers/remoteproc/Kconfig
create mode 100644 drivers/remoteproc/Makefile
create mode 100644 drivers/remoteproc/davinci_remoteproc.c
create mode 100644 drivers/remoteproc/omap_remoteproc.c
create mode 100644 drivers/remoteproc/remoteproc.c
create mode 100644 drivers/rpmsg/Kconfig
create mode 100644 drivers/rpmsg/Makefile
create mode 100644 drivers/rpmsg/host/Kconfig
create mode 100644 drivers/rpmsg/host/Makefile
create mode 100644 drivers/rpmsg/host/omap_rpmsg.c
create mode 100644 drivers/rpmsg/host/omap_rpmsg.h
create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c
create mode 100644 include/linux/remoteproc.h
create mode 100644 include/linux/rpmsg.h
Some systems have slave heterogeneous remote processor devices,
that are usually used to offload cpu-intensive computations
(e.g. multimedia codec tasks).
Booting a remote processor typically involves:
- Loading a firmware which contains the OS image (mainly text and data)
- If needed, programming an IOMMU
- Powering on the device
This patch introduces a generic remoteproc framework that allows drivers
to start and stop those remote processor devices, load up their firmware
(which might not necessarily be Linux-based), and in the future also
support power management and error recovery.
It's still not clear how much this is really reusable for other
platforms/architectures, especially the part that deals with the
firmware.
Moreover, it's not entirely clear whether this should really be an
independent layer, or if it should just be squashed with the host-specific
component of the rpmsg framework (there isn't really a remoteproc use case
that doesn't require rpmsg).
That said, it did prove useful for us on two completely different
platforms: OMAP and Davinci, each with its different remote
processor (Cortex-M3 and a C674x DSP, respectively). So to avoid
egregious duplication of code, remoteproc must not be omap-only.
Firmware loader is based on code by Mark Grosen <[email protected]>.
TODO:
- drop rproc_da_to_pa(), use iommu_iova_to_phys() instead
(requires completion of omap's iommu migration and some generic iommu
API work)
- instead of ioremapping reserved memory and handling IOMMUs, consider
moving to the generic DMA mapping API (with a CMA backend)
Signed-off-by: Ohad Ben-Cohen <[email protected]>
---
Documentation/remoteproc.txt | 170 +++++++++
drivers/Kconfig | 2 +
drivers/Makefile | 1 +
drivers/remoteproc/Kconfig | 7 +
drivers/remoteproc/Makefile | 5 +
drivers/remoteproc/remoteproc.c | 780 +++++++++++++++++++++++++++++++++++++++
include/linux/remoteproc.h | 273 ++++++++++++++
7 files changed, 1238 insertions(+), 0 deletions(-)
create mode 100644 Documentation/remoteproc.txt
create mode 100644 drivers/remoteproc/Kconfig
create mode 100644 drivers/remoteproc/Makefile
create mode 100644 drivers/remoteproc/remoteproc.c
create mode 100644 include/linux/remoteproc.h
diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt
new file mode 100644
index 0000000..3075813
--- /dev/null
+++ b/Documentation/remoteproc.txt
@@ -0,0 +1,170 @@
+Remote Processor Framework
+
+1. Introduction
+
+Modern SoCs typically have heterogeneous remote processor devices in asymmetric
+multiprocessing (AMP) configurations, which may be running different instances
+of operating system, whether it's Linux or any other flavor of real-time OS.
+
+OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP.
+In a typical configuration, the dual cortex-A9 is running Linux in a SMP
+configuration, and each of the other three cores (two M3 cores and a DSP)
+is running its own instance of RTOS in an AMP configuration.
+
+The generic remoteproc driver allows different platforms/architectures to
+control (power on, load firmware, power off) those remote processors while
+abstracting the hardware differences, so the entire driver doesn't need to be
+duplicated.
+
+2. User API
+
+ struct rproc *rproc_get(const char *name);
+ - power up the remote processor, identified by the 'name' argument,
+ and boot it. If the remote processor is already powered on, the
+ function immediately succeeds.
+ On success, returns the rproc handle. On failure, NULL is returned.
+
+ void rproc_put(struct rproc *rproc);
+ - power off the remote processor, identified by the rproc handle.
+ Every call to rproc_get() must be (eventually) accompanied by a call
+ to rproc_put(). Calling rproc_put() redundantly is a bug.
+ Note: the remote processor will actually be powered off only when the
+ last user calls rproc_put().
+
+3. Typical usage
+
+#include <linux/remoteproc.h>
+
+int dummy_rproc_example(void)
+{
+ struct rproc *my_rproc;
+
+ /* let's power on and boot the image processing unit */
+ my_rproc = rproc_get("ipu");
+ if (!my_rproc) {
+ /*
+ * something went wrong. handle it and leave.
+ */
+ }
+
+ /*
+ * the 'ipu' remote processor is now powered on... let it work !
+ */
+
+ /* if we no longer need ipu's services, power it down */
+ rproc_put(my_rproc);
+}
+
+4. API for implementors
+
+ int rproc_register(struct device *dev, const char *name,
+ const struct rproc_ops *ops,
+ const char *firmware,
+ const struct rproc_mem_entry *memory_maps,
+ struct module *owner);
+ - should be called from the underlying platform-specific implementation, in
+ order to register a new remoteproc device. 'dev' is the underlying
+ device, 'name' is the name of the remote processor, which will be
+ specified by users calling rproc_get(), 'ops' is the platform-specific
+ start/stop handlers, 'firmware' is the name of the firmware file to
+ boot the processor with, 'memory_maps' is a table of da<->pa memory
+ mappings which should be used to configure the IOMMU (if not relevant,
+ just pass NULL here), 'owner' is the underlying module that should
+ not be removed while the remote processor is in use.
+
+ Returns 0 on success, or an appropriate error code on failure.
+
+ int rproc_unregister(const char *name);
+ - should be called from the underlying platform-specific implementation, in
+ order to unregister a remoteproc device that was previously registered
+ with rproc_register().
+
+5. Implementation callbacks
+
+Every remoteproc implementation must provide these handlers:
+
+struct rproc_ops {
+ int (*start)(struct rproc *rproc, u64 bootaddr);
+ int (*stop)(struct rproc *rproc);
+};
+
+The ->start() handler takes a rproc handle and an optional bootaddr argument,
+and should power on the device and boot it (using the bootaddr argument
+if the hardware requires one).
+On success, 0 is returned, and on failure, an appropriate error code.
+
+The ->stop() handler takes a rproc handle and powers the device off.
+On success, 0 is returned, and on failure, an appropriate error code.
+
+6. Binary Firmware Structure
+
+The following enums and structures define the binary format of the images
+remoteproc loads and boot the remote processors with.
+
+The general binary format is as follows:
+
+struct {
+ char magic[4] = { 'R', 'P', 'R', 'C' };
+ u32 version;
+ u32 header_len;
+ char header[...] = { header_len bytes of unformatted, textual header };
+ struct section {
+ u32 type;
+ u64 da;
+ u32 len;
+ u8 content[...] = { len bytes of binary data };
+ } [ no limit on number of sections ];
+} __packed;
+
+The image begins with a 4-bytes "RPRC" magic, a version number, and a
+free-style textual header that users can easily read.
+
+After the header, the firmware contains several sections that should be
+loaded to memory so the remote processor can access them.
+
+Every section begins with its type, device address (da) where the remote
+processor expects to find this section at (exact meaning depends whether
+the device accesses memory through an IOMMU or not. if not, da might just
+be physical addresses), the section length and its content.
+
+Most of the sections are either text or data (which currently are treated
+exactly the same), but there is one special "resource" section that allows
+the remote processor to announce/request certain resources from the host.
+
+A resource section is just a packed array of the following struct:
+
+struct fw_resource {
+ u32 type;
+ u64 da;
+ u64 pa;
+ u32 len;
+ u32 flags;
+ u8 name[48];
+} __packed;
+
+The way a resource is really handled strongly depends on its type.
+Some resources are just one-way announcements, e.g., a RSC_TRACE type means
+that the remote processor will be writing log messages into a trace buffer
+which is located at the address specified in 'da'. In that case, 'len' is
+the size of that buffer. A RSC_BOOTADDR resource type announces the boot
+address (i.e. the first instruction the remote processor should be booted with)
+in 'da'.
+
+Other resources entries might be a two-way request/respond negotiation where
+a certain resource (memory or any other hardware resource) is requested
+by specifying the appropriate type and name. The host should then allocate
+such a resource and "reply" by writing the identifier (physical address
+or any other device id that will be meaningful to the remote processor)
+back into the relevant member of the resource structure. Obviously this
+approach can only be used _before_ booting the remote processor. After
+the remote processor is powered up, the resource section is expected
+to stay static. Runtime resource management (i.e. handling requests after
+the remote processor has booted) will be achieved using a dedicated rpmsg
+driver.
+
+The latter two-way approach is still preliminary and has not been implemented
+yet. It's left to see how this all works out.
+
+Most likely this kind of static allocations of hardware resources for
+remote processors can also use DT, so it's interesting to see how
+this all work out when DT materializes.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 3bb154d..1f6d6d3 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -126,4 +126,6 @@ source "drivers/hwspinlock/Kconfig"
source "drivers/clocksource/Kconfig"
+source "drivers/remoteproc/Kconfig"
+
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 09f3232..4d53a18 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -122,3 +122,4 @@ obj-y += ieee802154/
obj-y += clk/
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
+obj-$(CONFIG_REMOTE_PROC) += remoteproc/
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
new file mode 100644
index 0000000..a60bb12
--- /dev/null
+++ b/drivers/remoteproc/Kconfig
@@ -0,0 +1,7 @@
+#
+# Generic framework for controlling remote processors
+#
+
+# REMOTE_PROC gets selected by whoever wants it
+config REMOTE_PROC
+ tristate
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
new file mode 100644
index 0000000..d0f60c7
--- /dev/null
+++ b/drivers/remoteproc/Makefile
@@ -0,0 +1,5 @@
+#
+# Generic framework for controlling remote processors
+#
+
+obj-$(CONFIG_REMOTE_PROC) += remoteproc.o
diff --git a/drivers/remoteproc/remoteproc.c b/drivers/remoteproc/remoteproc.c
new file mode 100644
index 0000000..2b0514b
--- /dev/null
+++ b/drivers/remoteproc/remoteproc.c
@@ -0,0 +1,780 @@
+/*
+ * Remote Processor Framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <[email protected]>
+ * Mark Grosen <[email protected]>
+ * Brian Swetland <[email protected]>
+ * Fernando Guzman Lugo <[email protected]>
+ * Robert Tivy <[email protected]>
+ * Armando Uribe De Leon <[email protected]>
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/debugfs.h>
+#include <linux/remoteproc.h>
+
+/* list of the available remote processors */
+static LIST_HEAD(rprocs);
+/*
+ * This lock should be taken when the list of rprocs is accessed.
+ * Consider using RCU instead, since remote processors only get registered
+ * once (usually at boot), and then the list is only read accessed.
+ * Though right now the list is pretty short, and only rarely accessed.
+ */
+static DEFINE_SPINLOCK(rprocs_lock);
+
+/* debugfs parent dir */
+static struct dentry *rproc_dbg;
+
+/*
+ * Some remote processors may support dumping trace logs into a shared
+ * memory buffer. We expose this trace buffer using debugfs, so users
+ * can easily tell what's going on remotely.
+ */
+static ssize_t rproc_format_trace_buf(char __user *userbuf, size_t count,
+ loff_t *ppos, const void *src, int size)
+{
+ const char *buf = (const char *) src;
+ int i;
+
+ /*
+ * find the end of trace buffer (does not account for wrapping).
+ * desirable improvement: use a ring buffer instead.
+ */
+ for (i = 0; i < size && buf[i]; i++);
+
+ return simple_read_from_buffer(userbuf, count, ppos, src, i);
+}
+
+static int rproc_open_generic(struct inode *inode, struct file *file)
+{
+ file->private_data = inode->i_private;
+ return 0;
+}
+
+#define DEBUGFS_READONLY_FILE(name, value, len) \
+static ssize_t name## _rproc_read(struct file *filp, \
+ char __user *userbuf, size_t count, loff_t *ppos) \
+{ \
+ struct rproc *rproc = filp->private_data; \
+ return rproc_format_trace_buf(userbuf, count, ppos, value, len);\
+} \
+ \
+static const struct file_operations name ##_rproc_ops = { \
+ .read = name ##_rproc_read, \
+ .open = rproc_open_generic, \
+ .llseek = generic_file_llseek, \
+};
+
+/*
+ * Currently we allow two trace buffers for each remote processor.
+ * This is helpful in case a single remote processor has two independent
+ * cores, each of which is running an independent OS image.
+ * The current implementation is straightforward and simple, and is
+ * rather limited to 2 trace buffers. If, in time, we'd realize we
+ * need additional trace buffers, then the code should be refactored
+ * and generalized.
+ */
+DEBUGFS_READONLY_FILE(trace0, rproc->trace_buf0, rproc->trace_len0);
+DEBUGFS_READONLY_FILE(trace1, rproc->trace_buf1, rproc->trace_len1);
+
+/* The state of the remote processor is exposed via debugfs, too */
+const char *rproc_state_string(int state)
+{
+ const char *result;
+
+ switch (state) {
+ case RPROC_OFFLINE:
+ result = "offline";
+ break;
+ case RPROC_SUSPENDED:
+ result = "suspended";
+ break;
+ case RPROC_RUNNING:
+ result = "running";
+ break;
+ case RPROC_LOADING:
+ result = "loading";
+ break;
+ case RPROC_CRASHED:
+ result = "crashed";
+ break;
+ default:
+ result = "invalid state";
+ break;
+ }
+
+ return result;
+}
+
+static ssize_t rproc_state_read(struct file *filp, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct rproc *rproc = filp->private_data;
+ int state = rproc->state;
+ char buf[100];
+ int i;
+
+ i = snprintf(buf, 100, "%s (%d)\n", rproc_state_string(state), state);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, i);
+}
+
+static const struct file_operations rproc_state_ops = {
+ .read = rproc_state_read,
+ .open = rproc_open_generic,
+ .llseek = generic_file_llseek,
+};
+
+/* The name of the remote processor is exposed via debugfs, too */
+static ssize_t rproc_name_read(struct file *filp, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct rproc *rproc = filp->private_data;
+ /* need room for the name, a newline and a terminating null */
+ char buf[RPROC_MAX_NAME + 2];
+ int i;
+
+ i = snprintf(buf, RPROC_MAX_NAME + 2, "%s\n", rproc->name);
+
+ return simple_read_from_buffer(userbuf, count, ppos, buf, i);
+}
+
+static const struct file_operations rproc_name_ops = {
+ .read = rproc_name_read,
+ .open = rproc_open_generic,
+ .llseek = generic_file_llseek,
+};
+
+#define DEBUGFS_ADD(name) \
+ debugfs_create_file(#name, 0400, rproc->dbg_dir, \
+ rproc, &name## _rproc_ops)
+
+/**
+ * __find_rproc_by_name() - find a registered remote processor by name
+ * @name: name of the remote processor
+ *
+ * Internal function that returns the rproc @name, or NULL if @name does
+ * not exists.
+ */
+static struct rproc *__find_rproc_by_name(const char *name)
+{
+ struct rproc *rproc;
+ struct list_head *tmp;
+
+ spin_lock(&rprocs_lock);
+
+ list_for_each(tmp, &rprocs) {
+ rproc = list_entry(tmp, struct rproc, next);
+ if (!strcmp(rproc->name, name))
+ break;
+ rproc = NULL;
+ }
+
+ spin_unlock(&rprocs_lock);
+
+ return rproc;
+}
+
+/**
+ * __rproc_da_to_pa() - device to physical address conversion
+ * @maps: the remote processor's memory mappings table
+ * @da: a device address (as seen by the remote processor)
+ * @pa: pointer where the physical address result should be stored
+ *
+ * This function converts @da to its physical address (pa) by going through
+ * @maps, looking for a mapping that contains @da, and then calculating the
+ * appropriate pa.
+ *
+ * Not all remote processors are behind an IOMMU, so if @maps is NULL,
+ * we just return @da (after doing a basic sanity check).
+ *
+ * *** This function will be removed. Instead, iommu_iova_to_phys should
+ * *** be used. This will be done once the OMAP IOMMU migration will
+ * *** complete, and the missing parts in the generic IOMMU API will be
+ * *** added.
+ *
+ * On success 0 is returned, and @pa is updated with the result.
+ * Otherwise, -EINVAL is returned.
+ */
+static int
+rproc_da_to_pa(const struct rproc_mem_entry *maps, u64 da, phys_addr_t *pa)
+{
+ int i;
+ u64 offset;
+
+ /*
+ * If we're not being an IOMMU, then the remoteproc is accessing
+ * physical addresses directly
+ */
+ if (!maps) {
+ if (da > ((phys_addr_t)(~0U)))
+ return -EINVAL;
+
+ *pa = (phys_addr_t) da;
+ return 0;
+ }
+
+ for (i = 0; maps[i].size; i++) {
+ const struct rproc_mem_entry *me = &maps[i];
+
+ if (da >= me->da && da < (me->da + me->size)) {
+ offset = da - me->da;
+ pr_debug("%s: matched mem entry no. %d\n", __func__, i);
+ *pa = me->pa + offset;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * rproc_start() - boot the remote processor
+ * @rproc: the remote processor
+ * @bootaddr: address of first instruction to execute (optional)
+ *
+ * Boot a remote processor (i.e. power it on, take it out of reset, etc..)
+ */
+static void rproc_start(struct rproc *rproc, u64 bootaddr)
+{
+ struct device *dev = rproc->dev;
+ int err;
+
+ err = mutex_lock_interruptible(&rproc->lock);
+ if (err) {
+ dev_err(dev, "can't lock remote processor %d\n", err);
+ return;
+ }
+
+ err = rproc->ops->start(rproc, bootaddr);
+ if (err) {
+ dev_err(dev, "can't start rproc %s: %d\n", rproc->name, err);
+ goto unlock_mutex;
+ }
+
+ rproc->state = RPROC_RUNNING;
+
+ dev_info(dev, "remote processor %s is now up\n", rproc->name);
+
+unlock_mutex:
+ mutex_unlock(&rproc->lock);
+}
+
+/**
+ * rproc_handle_trace_rsc() - handle a shared trace buffer resource
+ * @rproc: the remote processor
+ * @rsc: the trace resource descriptor
+ *
+ * In case the remote processor dumps trace logs into memory, ioremap it
+ * and make it available to the user via debugfs.
+ * Consider using the DMA mapping API here.
+ *
+ * Returns 0 on success, or an appropriate error code otherwise
+ */
+static int rproc_handle_trace_rsc(struct rproc *rproc, struct fw_resource *rsc)
+{
+ struct device *dev = rproc->dev;
+ void *ptr;
+ phys_addr_t pa;
+ int ret;
+
+ ret = rproc_da_to_pa(rproc->memory_maps, rsc->da, &pa);
+ if (ret) {
+ dev_err(dev, "invalid device address\n");
+ return -EINVAL;
+ }
+
+ /* allow two trace buffers per rproc (can be extended if needed) */
+ if (rproc->trace_buf0 && rproc->trace_buf1) {
+ dev_warn(dev, "skipping extra trace rsc %s\n", rsc->name);
+ return -EBUSY;
+ }
+
+ /*
+ * trace buffer memory is normal memory, so we cast away the __iomem
+ * to make sparse happy
+ */
+ ptr = (__force void *) ioremap_nocache(pa, rsc->len);
+ if (!ptr) {
+ dev_err(dev, "can't ioremap trace buffer %s\n", rsc->name);
+ return -ENOMEM;
+ }
+
+ /* add the trace0 debugfs entry. If it already exists, add trace1 */
+ if (!rproc->trace_buf0) {
+ rproc->trace_len0 = rsc->len;
+ rproc->trace_buf0 = ptr;
+ DEBUGFS_ADD(trace0);
+ } else {
+ rproc->trace_len1 = rsc->len;
+ rproc->trace_buf1 = ptr;
+ DEBUGFS_ADD(trace1);
+ }
+
+ return 0;
+}
+
+/**
+ * rproc_handle_resources - go over and handle the resource section
+ * @rproc: rproc handle
+ * @rsc: the resource secion
+ * @len: length of the resource section
+ * @bootaddr: if found a boot address, put it here
+ */
+static int rproc_handle_resources(struct rproc *rproc, struct fw_resource *rsc,
+ int len, u64 *bootaddr)
+{
+ struct device *dev = rproc->dev;
+ int ret = 0;
+
+ while (len >= sizeof(*rsc)) {
+ dev_dbg(dev, "resource: type %d, da 0x%llx, pa 0x%llx, len 0x%x"
+ ", flags 0x%x, name %s\n", rsc->type, rsc->da, rsc->pa,
+ rsc->len, rsc->flags, rsc->name);
+
+ switch (rsc->type) {
+ case RSC_TRACE:
+ ret = rproc_handle_trace_rsc(rproc, rsc);
+ if (ret)
+ dev_err(dev, "failed handling rsc\n");
+ break;
+ case RSC_BOOTADDR:
+ if (*bootaddr)
+ dev_warn(dev, "bootaddr already set\n");
+ *bootaddr = rsc->da;
+ break;
+ default:
+ /* we don't support much yet, so don't be noisy */
+ dev_dbg(dev, "unsupported resource %d\n", rsc->type);
+ break;
+ }
+
+ if (ret)
+ break;
+ rsc++;
+ len -= sizeof(*rsc);
+ }
+
+ /* unmap trace buffers on failure */
+ if (ret && rproc->trace_buf0)
+ iounmap((__force void __iomem *) rproc->trace_buf0);
+ if (ret && rproc->trace_buf1)
+ iounmap((__force void __iomem *) rproc->trace_buf1);
+
+ return ret;
+}
+
+static int rproc_process_fw(struct rproc *rproc, struct fw_section *section,
+ int left, u64 *bootaddr)
+{
+ struct device *dev = rproc->dev;
+ phys_addr_t pa;
+ u32 len, type;
+ u64 da;
+ int ret = 0;
+ void *ptr;
+
+ while (left > sizeof(struct fw_section)) {
+ da = section->da;
+ len = section->len;
+ type = section->type;
+
+ dev_dbg(dev, "section: type %d da 0x%llx len 0x%x\n",
+ type, da, len);
+
+ left -= sizeof(struct fw_section);
+ if (left < section->len) {
+ dev_err(dev, "firmware image is truncated\n");
+ ret = -EINVAL;
+ break;
+ }
+
+ ret = rproc_da_to_pa(rproc->memory_maps, da, &pa);
+ if (ret) {
+ dev_err(dev, "rproc_da_to_pa failed: %d\n", ret);
+ break;
+ }
+
+ dev_dbg(dev, "da 0x%llx pa 0x%x len 0x%x\n", da, pa, len);
+
+ /* ioremaping normal memory, so make sparse happy */
+ ptr = (__force void *) ioremap_nocache(pa, len);
+ if (!ptr) {
+ dev_err(dev, "can't ioremap 0x%x\n", pa);
+ ret = -ENOMEM;
+ break;
+ }
+
+ /* put the section where the remoteproc will expect it */
+ memcpy(ptr, section->content, len);
+
+ /* a resource table needs special handling */
+ if (section->type == FW_RESOURCE)
+ ret = rproc_handle_resources(rproc,
+ (struct fw_resource *) ptr,
+ len, bootaddr);
+
+ /* iounmap normal memory; make sparse happy */
+ iounmap((__force void __iomem *) ptr);
+
+ /* rproc_handle_resources may have failed */
+ if (ret)
+ break;
+
+ section = (struct fw_section *)(section->content + len);
+ left -= len;
+ }
+
+ return ret;
+}
+
+static void rproc_load_fw(const struct firmware *fw, void *context)
+{
+ struct rproc *rproc = context;
+ struct device *dev = rproc->dev;
+ const char *fwfile = rproc->firmware;
+ u64 bootaddr = 0;
+ struct fw_header *image;
+ struct fw_section *section;
+ int left, ret;
+
+ if (!fw) {
+ dev_err(dev, "%s: failed to load %s\n", __func__, fwfile);
+ goto complete_fw;
+ }
+
+ dev_info(dev, "Loaded fw image %s, size %d\n", fwfile, fw->size);
+
+ /* make sure this image is sane */
+ if (fw->size < sizeof(struct fw_header)) {
+ dev_err(dev, "Image is too small\n");
+ goto out;
+ }
+
+ image = (struct fw_header *) fw->data;
+
+ if (memcmp(image->magic, "RPRC", 4)) {
+ dev_err(dev, "Image is corrupted (bad magic)\n");
+ goto out;
+ }
+
+ dev_info(dev, "BIOS image version is %d\n", image->version);
+
+ /* now process the image, section by section */
+ section = (struct fw_section *)(image->header + image->header_len);
+
+ left = fw->size - sizeof(struct fw_header) - image->header_len;
+
+ ret = rproc_process_fw(rproc, section, left, &bootaddr);
+ if (ret) {
+ dev_err(dev, "Failed to process the image: %d\n", ret);
+ goto out;
+ }
+
+ rproc_start(rproc, bootaddr);
+
+out:
+ release_firmware(fw);
+complete_fw:
+ /* allow all contexts calling rproc_put() to proceed */
+ complete_all(&rproc->firmware_loading_complete);
+}
+
+/**
+ * rproc_get() - boot the remote processor
+ * @name: name of the remote processor
+ *
+ * Boot a remote processor (i.e. load its firmware, power it on, take it
+ * out of reset, etc..).
+ *
+ * If the remote processor is already powered on, immediately return
+ * its rproc handle.
+ *
+ * On success, returns the rproc handle. On failure, NULL is returned.
+ */
+struct rproc *rproc_get(const char *name)
+{
+ struct rproc *rproc, *ret = NULL;
+ struct device *dev;
+ int err;
+
+ rproc = __find_rproc_by_name(name);
+ if (!rproc) {
+ pr_err("can't find remote processor %s\n", name);
+ return NULL;
+ }
+
+ dev = rproc->dev;
+
+ err = mutex_lock_interruptible(&rproc->lock);
+ if (err) {
+ dev_err(dev, "can't lock remote processor %s\n", name);
+ return NULL;
+ }
+
+ /* prevent underlying implementation from being removed */
+ if (!try_module_get(rproc->owner)) {
+ dev_err(dev, "%s: can't get owner\n", __func__);
+ goto unlock_mutex;
+ }
+
+ /* skip the boot process if rproc is already (being) powered up */
+ if (rproc->count++) {
+ ret = rproc;
+ goto unlock_mutex;
+ }
+
+ /* rproc_put() calls should wait until async loader completes */
+ init_completion(&rproc->firmware_loading_complete);
+
+ dev_info(dev, "powering up %s\n", name);
+
+ /* loading a firmware is required */
+ if (!rproc->firmware) {
+ dev_err(dev, "%s: no firmware to load\n", __func__);
+ goto deref_rproc;
+ }
+
+ /*
+ * Initiate an asynchronous firmware loading, to allow building
+ * remoteproc as built-in kernel code, without hanging the boot process
+ */
+ err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+ rproc->firmware, dev, GFP_KERNEL, rproc, rproc_load_fw);
+ if (err < 0) {
+ dev_err(dev, "request_firmware_nowait failed: %d\n", err);
+ goto deref_rproc;
+ }
+
+ rproc->state = RPROC_LOADING;
+ ret = rproc;
+ goto unlock_mutex;
+
+deref_rproc:
+ complete_all(&rproc->firmware_loading_complete);
+ module_put(rproc->owner);
+ --rproc->count;
+unlock_mutex:
+ mutex_unlock(&rproc->lock);
+ return ret;
+}
+EXPORT_SYMBOL(rproc_get);
+
+/**
+ * rproc_put() - power off the remote processor
+ * @rproc: the remote processor
+ *
+ * Release an rproc handle previously acquired with rproc_get(),
+ * and if we're the last user, power the processor off.
+ *
+ * Every call to rproc_get() must be (eventually) accompanied by a call
+ * to rproc_put(). Calling rproc_put() redundantly is a bug.
+ */
+void rproc_put(struct rproc *rproc)
+{
+ struct device *dev = rproc->dev;
+ int ret;
+
+ /*
+ * make sure rproc_get() was called beforehand.
+ * it should be safe to check for zero without taking the lock.
+ */
+ if (!rproc->count) {
+ dev_err(dev, "asymmetric put (fogot to call rproc_get ?)\n");
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* if rproc is just being loaded now, wait */
+ wait_for_completion(&rproc->firmware_loading_complete);
+
+ ret = mutex_lock_interruptible(&rproc->lock);
+ if (ret) {
+ dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
+ return;
+ }
+
+ /* if the remote proc is still needed, bail out */
+ if (--rproc->count)
+ goto out;
+
+ if (rproc->trace_buf0)
+ /* iounmap normal memory, so make sparse happy */
+ iounmap((__force void __iomem *) rproc->trace_buf0);
+ if (rproc->trace_buf1)
+ /* iounmap normal memory, so make sparse happy */
+ iounmap((__force void __iomem *) rproc->trace_buf1);
+
+ rproc->trace_buf0 = rproc->trace_buf1 = NULL;
+
+ /*
+ * make sure rproc is really running before powering it off.
+ * this is important, because the fw loading might have failed.
+ */
+ if (rproc->state == RPROC_RUNNING) {
+ ret = rproc->ops->stop(rproc);
+ if (ret) {
+ dev_err(dev, "can't stop rproc: %d\n", ret);
+ goto out;
+ }
+ }
+
+ rproc->state = RPROC_OFFLINE;
+
+ dev_info(dev, "stopped remote processor %s\n", rproc->name);
+
+out:
+ mutex_unlock(&rproc->lock);
+ if (!ret)
+ module_put(rproc->owner);
+}
+EXPORT_SYMBOL(rproc_put);
+
+/**
+ * rproc_register() - register a remote processor
+ * @dev: the underlying device
+ * @name: name of this remote processor
+ * @ops: platform-specific handlers (mainly start/stop)
+ * @firmware: name of firmware file to load
+ * @memory_maps: IOMMU settings for this rproc (optional)
+ * @owner: owning module
+ *
+ * Registers a new remote processor in the remoteproc framework.
+ *
+ * This is called by the underlying platform-specific implementation,
+ * whenever a new remote processor device is probed.
+ *
+ * On succes, 0 is return, and on failure an appropriate error code.
+ */
+int rproc_register(struct device *dev, const char *name,
+ const struct rproc_ops *ops,
+ const char *firmware,
+ const struct rproc_mem_entry *memory_maps,
+ struct module *owner)
+{
+ struct rproc *rproc;
+
+ if (!dev || !name || !ops)
+ return -EINVAL;
+
+ rproc = kzalloc(sizeof(struct rproc), GFP_KERNEL);
+ if (!rproc) {
+ dev_err(dev, "%s: kzalloc failed\n", __func__);
+ return -ENOMEM;
+ }
+
+ rproc->dev = dev;
+ rproc->name = name;
+ rproc->ops = ops;
+ rproc->firmware = firmware;
+ rproc->memory_maps = memory_maps;
+ rproc->owner = owner;
+
+ mutex_init(&rproc->lock);
+
+ rproc->state = RPROC_OFFLINE;
+
+ spin_lock(&rprocs_lock);
+ list_add_tail(&rproc->next, &rprocs);
+ spin_unlock(&rprocs_lock);
+
+ dev_info(dev, "%s is available\n", name);
+
+ if (!rproc_dbg)
+ goto out;
+
+ rproc->dbg_dir = debugfs_create_dir(dev_name(dev), rproc_dbg);
+ if (!rproc->dbg_dir) {
+ dev_err(dev, "can't create debugfs dir\n");
+ goto out;
+ }
+
+ debugfs_create_file("name", 0400, rproc->dbg_dir, rproc,
+ &rproc_name_ops);
+ debugfs_create_file("state", 0400, rproc->dbg_dir, rproc,
+ &rproc_state_ops);
+
+out:
+ return 0;
+}
+EXPORT_SYMBOL(rproc_register);
+
+/**
+ * rproc_unregister() - unregister a remote processor
+ * @name: name of this remote processor
+ *
+ * Unregisters a remote processor.
+ *
+ * On succes, 0 is return. If this remote processor isn't found, -EINVAL
+ * is returned.
+ */
+int rproc_unregister(const char *name)
+{
+ struct rproc *rproc;
+
+ rproc = __find_rproc_by_name(name);
+ if (!rproc) {
+ pr_err("can't find remote processor %s\n", name);
+ return -EINVAL;
+ }
+
+ dev_info(rproc->dev, "removing %s\n", name);
+
+ if (rproc->dbg_dir)
+ debugfs_remove_recursive(rproc->dbg_dir);
+
+ spin_lock(&rprocs_lock);
+ list_del(&rproc->next);
+ spin_unlock(&rprocs_lock);
+
+ kfree(rproc);
+
+ return 0;
+}
+EXPORT_SYMBOL(rproc_unregister);
+
+static int __init remoteproc_init(void)
+{
+ if (debugfs_initialized()) {
+ rproc_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
+ if (!rproc_dbg)
+ pr_err("can't create debugfs dir\n");
+ }
+
+ return 0;
+}
+/* must be ready in time for device_initcall users */
+subsys_initcall(remoteproc_init);
+
+static void __exit remoteproc_exit(void)
+{
+ if (rproc_dbg)
+ debugfs_remove(rproc_dbg);
+}
+module_exit(remoteproc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Generic Remote Processor Framework");
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
new file mode 100644
index 0000000..6cdb966
--- /dev/null
+++ b/include/linux/remoteproc.h
@@ -0,0 +1,273 @@
+/*
+ * Remote Processor Framework
+ *
+ * Copyright(c) 2011 Texas Instruments, Inc.
+ * Copyright(c) 2011 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef REMOTEPROC_H
+#define REMOTEPROC_H
+
+#include <linux/mutex.h>
+#include <linux/completion.h>
+
+/**
+ * DOC: The Binary Structure of the Firmware
+ *
+ * The following enums and structures define the binary format of the image
+ * we load and run the remote processors with.
+ *
+ * The binary format is as follows:
+ *
+ * struct {
+ * char magic[4] = { 'R', 'P', 'R', 'C' };
+ * u32 version;
+ * u32 header_len;
+ * char header[...] = { header_len bytes of unformatted, textual header };
+ * struct section {
+ * u32 type;
+ * u64 da;
+ * u32 len;
+ * u8 content[...] = { len bytes of binary data };
+ * } [ no limit on number of sections ];
+ * } __packed;
+ */
+
+/**
+ * struct fw_header - header of the firmware image
+ * @magic: 4-bytes magic (should contain "RPRC")
+ * @version: version number, should be bumped on binary changes
+ * @header_len: length, in bytes, of the following text header
+ * @header: free-style textual header, users can read with 'head'
+ *
+ * This structure defines the header of the remoteproc firmware.
+ */
+struct fw_header {
+ char magic[4];
+ u32 version;
+ u32 header_len;
+ char header[0];
+} __packed;
+
+/**
+ * struct fw_section - header of a firmware section
+ * @type: section type
+ * @da: device address that the rproc expects to find this section at.
+ * @len: length of the section (in bytes)
+ * @content: the section data
+ *
+ * This structure defines the header of a firmware section. All sections
+ * should be loaded to the address specified by @da, so the remote processor
+ * will find them.
+ *
+ * Note: if the remote processor is not behind an IOMMU, then da is a
+ * mere physical address
+ */
+struct fw_section {
+ u32 type;
+ u64 da;
+ u32 len;
+ char content[0];
+} __packed;
+
+/**
+ * enum fw_section_type - section type values
+ *
+ * @FW_RESOURCE: a resource section. this section contains static
+ * resource requests (/announcements) that the remote
+ * processor requires (/supports). Most of these requests
+ * require that the host fulfill them (and usually
+ * "reply" with a result) before the remote processor
+ * is booted. See Documentation/remoteproc.h for more info
+ * @FW_TEXT: a text section
+ * @FW_DATA: a data section
+ *
+ * Note: text and data sections have different types so we can support stuff
+ * like crash dumps (which only requires dumping data sections) or loading
+ * text sections into faster memory. Currently, though, both section types
+ * are treated exactly the same.
+ */
+enum fw_section_type {
+ FW_RESOURCE = 0,
+ FW_TEXT = 1,
+ FW_DATA = 2,
+};
+
+/**
+ * struct fw_resource - describes an entry from the resource section
+ * @type: resource type
+ * @da: depends on the resource type
+ * @pa: depends on the resource type
+ * @len: depends on the resource type
+ * @flags: depends on the resource type
+ * @name: name of resource
+ *
+ * Some resources entries are mere announcements, where the host is informed
+ * of specific remoteproc configuration. Other entries require the host to
+ * do something (e.g. reserve a requested resource) and reply by overwriting
+ * a member inside struct fw_resource with the id of the allocated resource.
+ * There could also be resource entries where the remoteproc's image suggests
+ * a configuration, but the host may overwrite it with its own preference.
+ *
+ * Note: the vast majority of the resource types are not implemented yet,
+ * and this is all very much preliminary.
+ */
+struct fw_resource {
+ u32 type;
+ u64 da;
+ u64 pa;
+ u32 len;
+ u32 flags;
+ u8 name[48];
+} __packed;
+
+/**
+ * enum fw_resource_type - types of resource entries
+ *
+ * @RSC_TRACE: announces the availability of a trace buffer into which
+ * the remote processor will be writing logs. In this case,
+ * 'da' indicates the device address where logs are written to,
+ * and 'len' is the size of the trace buffer.
+ * Currently we support two trace buffers per remote processor,
+ * to support two autonomous cores running in a single rproc
+ * device.
+ * If additional trace buffers are needed, this should be
+ * extended/generalized.
+ * @RSC_BOOTADDR: announces the address of the first instruction the remote
+ * processor should be booted with (address indicated in 'da').
+ *
+ * Note: most of the resource types are not implemented yet, so they are
+ * not documented yet.
+ */
+enum fw_resource_type {
+ RSC_CARVEOUT = 0,
+ RSC_DEVMEM = 1,
+ RSC_DEVICE = 2,
+ RSC_IRQ = 3,
+ RSC_TRACE = 4,
+ RSC_BOOTADDR = 5,
+};
+
+/**
+ * struct rproc_mem_entry - memory mapping descriptor
+ * @da: device address as seen by the remote processor
+ * @pa: physical address
+ * @size: size of this memory region
+ *
+ * Board file will use this struct to define the IOMMU configuration
+ * for this remote processor. If the rproc device accesses physical memory
+ * directly (and not through an IOMMU), this is not needed.
+ */
+struct rproc_mem_entry {
+ u64 da;
+ phys_addr_t pa;
+ u32 size;
+};
+
+struct rproc;
+
+/**
+ * struct rproc_ops - platform-specific device handlers
+ * @start: power on the device and boot it. implementation may require
+ * specifyng a boot address
+ * @stop: power off the device
+ */
+struct rproc_ops {
+ int (*start)(struct rproc *rproc, u64 bootaddr);
+ int (*stop)(struct rproc *rproc);
+};
+
+/*
+ * enum rproc_state - remote processor states
+ *
+ * @RPROC_OFFLINE: device is powered off
+ * @RPROC_SUSPENDED: device is suspended; needs to be woken up to receive
+ * a message.
+ * @RPROC_RUNNING: device is up and running
+ * @RPROC_LOADING: asynchronous firmware loading has started
+ * @RPROC_CRASHED: device has crashed; need to start recovery
+ */
+enum rproc_state {
+ RPROC_OFFLINE,
+ RPROC_SUSPENDED,
+ RPROC_RUNNING,
+ RPROC_LOADING,
+ RPROC_CRASHED,
+};
+
+#define RPROC_MAX_NAME 100
+
+/*
+ * struct rproc - represents a physical remote processor device
+ *
+ * @next: next rproc entry in the list
+ * @name: human readable name of the rproc, cannot exceed RPROC_MAX_NAME bytes
+ * @memory_maps: table of da-to-pa memory maps (relevant if device is behind
+ * an iommu)
+ * @firmware: name of firmware file to be loaded
+ * @owner: reference to the platform-specific rproc module
+ * @priv: private data which belongs to the platform-specific rproc module
+ * @ops: platform-specific start/stop rproc handlers
+ * @dev: underlying device
+ * @count: usage refcount
+ * @state: state of the device
+ * @lock: lock which protects concurrent manipulations of the rproc
+ * @dbg_dir: debugfs directory of this rproc device
+ * @trace_buf0: main trace buffer of the remote processor
+ * @trace_buf1: second, optional, trace buffer of the remote processor
+ * @trace_len0: length of main trace buffer of the remote processor
+ * @trace_len1: length of the second (and optional) trace buffer
+ * @firmware_loading_complete: marks e/o asynchronous firmware loading
+ */
+struct rproc {
+ struct list_head next;
+ const char *name;
+ const struct rproc_mem_entry *memory_maps;
+ const char *firmware;
+ struct module *owner;
+ void *priv;
+ const struct rproc_ops *ops;
+ struct device *dev;
+ int count;
+ int state;
+ struct mutex lock;
+ struct dentry *dbg_dir;
+ char *trace_buf0, *trace_buf1;
+ int trace_len0, trace_len1;
+ struct completion firmware_loading_complete;
+};
+
+struct rproc *rproc_get(const char *);
+void rproc_put(struct rproc *);
+int rproc_register(struct device *, const char *, const struct rproc_ops *,
+ const char *, const struct rproc_mem_entry *, struct module *);
+int rproc_unregister(const char *);
+
+#endif /* REMOTEPROC_H */
--
1.7.1
From: Guzman Lugo, Fernando <[email protected]>
Add remoteproc implementation for OMAP4, so we can load the M3 and DSP
remote processors.
Based on code by Hari Kanigeri <[email protected]>
While this code is functional, and works on OMAP4 & its M3's,
it is still preliminary and going to substantially change:
1. Splitting physically contiguous regions to pages should happen
inside the IOMMU API framework, and not here (so omap_rproc_map_unmap
should go away).
2. IOMMU programming in general should go away too; it should be handled by
generic code (preferably using the DMA mapping API), and not by an OMAP
specific component.
This patch depends on OMAP's IOMMU API migration, as posted here:
https://lkml.org/lkml/2011/6/2/369
[[email protected]: commit log, generic iommu api migration, refactoring, cleanups]
Signed-off-by: Guzman Lugo, Fernando <[email protected]>
Signed-off-by: Ohad Ben-Cohen <[email protected]>
---
arch/arm/plat-omap/include/plat/remoteproc.h | 41 +++++
drivers/remoteproc/Kconfig | 21 +++
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/omap_remoteproc.c | 243 ++++++++++++++++++++++++++
4 files changed, 306 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/plat-omap/include/plat/remoteproc.h
create mode 100644 drivers/remoteproc/omap_remoteproc.c
diff --git a/arch/arm/plat-omap/include/plat/remoteproc.h b/arch/arm/plat-omap/include/plat/remoteproc.h
new file mode 100644
index 0000000..6494570
--- /dev/null
+++ b/arch/arm/plat-omap/include/plat/remoteproc.h
@@ -0,0 +1,41 @@
+/*
+ * Remote Processor - omap-specific bits
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _PLAT_REMOTEPROC_H
+#define _PLAT_REMOTEPROC_H
+
+#include <linux/remoteproc.h>
+
+/*
+ * struct omap_rproc_pdata - omap remoteproc's platform data
+ * @name: the remoteproc's name, cannot exceed RPROC_MAX_NAME bytes
+ * @iommu_name: iommu device we're behind of
+ * @oh_name: omap hwmod device
+ * @oh_name_opt: optional, secondary omap hwmod device
+ * @firmware: name of firmware file to load
+ * @ops: start/stop rproc handlers
+ * @memory_maps: table of da-to-pa iommu memory maps
+ */
+struct omap_rproc_pdata {
+ const char *name;
+ const char *iommu_name;
+ const char *oh_name;
+ const char *oh_name_opt;
+ const char *firmware;
+ const struct rproc_ops *ops;
+ const struct rproc_mem_entry *memory_maps;
+};
+
+#endif /* _PLAT_REMOTEPROC_H */
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index a60bb12..88421bd 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -5,3 +5,24 @@
# REMOTE_PROC gets selected by whoever wants it
config REMOTE_PROC
tristate
+
+# for tristate, either expose omap_device_* or pass it via pdata
+config OMAP_REMOTE_PROC
+ bool "OMAP remoteproc support"
+ depends on ARCH_OMAP4
+ select OMAP_IOMMU
+ select REMOTE_PROC
+ select OMAP_MBOX_FWK
+ default y
+ help
+ Say y here to support OMAP's remote processors (dual M3
+ and DSP on OMAP4) via the remote processor framework.
+
+ Currently only supported on OMAP4.
+
+ Usually you want to say y here, in order to enable multimedia
+ use-cases to run on your platform (multimedia codecs are
+ offloaded to remote DSP processors using this framework).
+
+ It's safe to say n here if you're not interested in multimedia
+ offloading or just want a bare minimum kernel.
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index d0f60c7..0198b1d 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -3,3 +3,4 @@
#
obj-$(CONFIG_REMOTE_PROC) += remoteproc.o
+obj-$(CONFIG_OMAP_REMOTE_PROC) += omap_remoteproc.o
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
new file mode 100644
index 0000000..91f7f11
--- /dev/null
+++ b/drivers/remoteproc/omap_remoteproc.c
@@ -0,0 +1,243 @@
+/*
+ * Remote processor machine-specific module for OMAP4
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/iommu.h>
+#include <linux/slab.h>
+#include <linux/remoteproc.h>
+
+#include <plat/iommu.h>
+#include <plat/omap_device.h>
+#include <plat/remoteproc.h>
+
+struct omap_rproc_priv {
+ struct iommu_domain *domain;
+ struct device *iommu;
+};
+
+/*
+ * Map a physically contiguous memory region using biggest pages possible.
+ * TODO: this code should go away; it belongs in the generic IOMMU layer
+ */
+static int omap_rproc_map_unmap(struct iommu_domain *domain,
+ const struct rproc_mem_entry *me, bool map)
+{
+ u32 all_bits;
+ /* these are the page sizes supported by OMAP's IOMMU */
+ u32 pg_size[] = {SZ_16M, SZ_1M, SZ_64K, SZ_4K};
+ int i, ret, size = me->size;
+ u32 da = me->da;
+ u32 pa = me->pa;
+ int order;
+ int flags;
+
+ /* must be aligned at least with the smallest supported iommu page */
+ if (!IS_ALIGNED(size, SZ_4K) || !IS_ALIGNED(da | pa, SZ_4K)) {
+ pr_err("misaligned: size %x da 0x%x pa 0x%x\n", size, da, pa);
+ return -EINVAL;
+ }
+
+ while (size) {
+ /* find the max page size with which both pa, da are aligned */
+ all_bits = pa | da;
+ for (i = 0; i < ARRAY_SIZE(pg_size); i++) {
+ if ((size >= pg_size[i]) &&
+ ((all_bits & (pg_size[i] - 1)) == 0)) {
+ break;
+ }
+ }
+
+ order = get_order(pg_size[i]);
+
+ /* OMAP4's M3 is little endian, so no need for conversions */
+ flags = MMU_RAM_ENDIAN_LITTLE | MMU_RAM_ELSZ_NONE;
+
+ if (map)
+ ret = iommu_map(domain, da, pa, order, flags);
+ else
+ ret = iommu_unmap(domain, da, order);
+
+ if (ret)
+ return ret;
+
+ size -= pg_size[i];
+ da += pg_size[i];
+ pa += pg_size[i];
+ }
+
+ return 0;
+}
+
+/* bootaddr isn't needed for the dual M3's */
+static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr)
+{
+ struct device *dev = rproc->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_rproc_pdata *pdata = dev->platform_data;
+ struct iommu_domain *domain;
+ struct device *iommu;
+ struct omap_rproc_priv *priv;
+ int ret, i;
+
+ if (!iommu_found()) {
+ dev_err(&pdev->dev, "iommu not found\n");
+ return -ENODEV;
+ }
+
+ priv = kmalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "kzalloc failed\n");
+ return -ENOMEM;
+ }
+
+ /*
+ * Use the specified iommu name to find our iommu device.
+ * TODO: solve this generically so other platforms can use it, too
+ */
+ iommu = omap_find_iommu_device(pdata->iommu_name);
+ if (!iommu) {
+ dev_err(dev, "omap_find_iommu_device failed\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+
+ domain = iommu_domain_alloc();
+ if (!domain) {
+ dev_err(&pdev->dev, "can't alloc iommu domain\n");
+ ret = -ENODEV;
+ goto free_mem;
+ }
+
+ priv->iommu = iommu;
+ priv->domain = domain;
+ rproc->priv = priv;
+
+ ret = iommu_attach_device(domain, iommu);
+ if (ret) {
+ dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
+ goto free_domain;
+ }
+
+ for (i = 0; rproc->memory_maps[i].size; i++) {
+ const struct rproc_mem_entry *me = &rproc->memory_maps[i];
+
+ ret = omap_rproc_map_unmap(domain, me, true);
+ if (ret) {
+ dev_err(&pdev->dev, "iommu_map failed: %d\n", ret);
+ goto unmap_regions;
+ }
+ }
+
+ /* power on the remote processor itself */
+ ret = omap_device_enable(pdev);
+ if (ret)
+ goto unmap_regions;
+
+ return 0;
+
+unmap_regions:
+ for (--i; i >= 0 && rproc->memory_maps[i].size; i--) {
+ const struct rproc_mem_entry *me = &rproc->memory_maps[i];
+ omap_rproc_map_unmap(domain, me, false);
+ }
+free_domain:
+ iommu_domain_free(domain);
+free_mem:
+ kfree(priv);
+ return ret;
+}
+
+static inline int omap_rproc_stop(struct rproc *rproc)
+{
+ struct device *dev = rproc->dev;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_rproc_priv *priv = rproc->priv;
+ struct iommu_domain *domain = priv->domain;
+ struct device *iommu = priv->iommu;
+ int ret, i;
+
+ /* power off the remote processor itself */
+ ret = omap_device_shutdown(pdev);
+ if (ret) {
+ dev_err(dev, "failed to shutdown: %d\n", ret);
+ goto out;
+ }
+
+ for (i = 0; rproc->memory_maps[i].size; i++) {
+ const struct rproc_mem_entry *me = &rproc->memory_maps[i];
+
+ ret = omap_rproc_map_unmap(domain, me, false);
+ if (ret) {
+ dev_err(&pdev->dev, "iommu_unmap failed: %d\n", ret);
+ goto out;
+ }
+ }
+
+ iommu_detach_device(domain, iommu);
+ iommu_domain_free(domain);
+
+out:
+ return ret;
+}
+
+static struct rproc_ops omap_rproc_ops = {
+ .start = omap_rproc_start,
+ .stop = omap_rproc_stop,
+};
+
+static int omap_rproc_probe(struct platform_device *pdev)
+{
+ struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+
+ return rproc_register(&pdev->dev, pdata->name, &omap_rproc_ops,
+ pdata->firmware, pdata->memory_maps,
+ THIS_MODULE);
+}
+
+static int __devexit omap_rproc_remove(struct platform_device *pdev)
+{
+ struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+
+ return rproc_unregister(pdata->name);
+}
+
+static struct platform_driver omap_rproc_driver = {
+ .probe = omap_rproc_probe,
+ .remove = __devexit_p(omap_rproc_remove),
+ .driver = {
+ .name = "omap-rproc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init omap_rproc_init(void)
+{
+ return platform_driver_register(&omap_rproc_driver);
+}
+/* must be ready in time for device_initcall users */
+subsys_initcall(omap_rproc_init);
+
+static void __exit omap_rproc_exit(void)
+{
+ platform_driver_unregister(&omap_rproc_driver);
+}
+module_exit(omap_rproc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("OMAP Remote Processor control driver");
--
1.7.1
This is a temporary patch to get things going, and is by no means
a suggestion for inclusion (read only if interested, but don't waste
much review energies here).
The way to go forward here is to use CMA (together with the generic
DMA API, so we also get IOMMU programming "for free").
This patch also breaks tidspbridge. Use it only if you want to try out
rpmsg/remoteproc on OMAP4, and you don't care too much about multi-board
kernels.
Signed-off-by: Ohad Ben-Cohen <[email protected]>
---
arch/arm/plat-omap/Kconfig | 8 ++++++++
arch/arm/plat-omap/devices.c | 14 ++++++++++++--
arch/arm/plat-omap/include/plat/dsp.h | 6 +++++-
3 files changed, 25 insertions(+), 3 deletions(-)
diff --git a/arch/arm/plat-omap/Kconfig b/arch/arm/plat-omap/Kconfig
index 1c3acb5..09c91d1 100644
--- a/arch/arm/plat-omap/Kconfig
+++ b/arch/arm/plat-omap/Kconfig
@@ -211,6 +211,14 @@ config OMAP_SERIAL_WAKE
to data on the serial RX line. This allows you to wake the
system from serial console.
+config OMAP_CARVEOUT_MEMPOOL_SIZE
+ hex "Physical carveout memory pool size (Byte)"
+ depends on OMAP_REMOTE_PROC
+ default 0x3300000
+ help
+ Allocate specified size of memory at boot time so we can ioremap
+ it safely.
+
choice
prompt "OMAP PM layer selection"
depends on ARCH_OMAP
diff --git a/arch/arm/plat-omap/devices.c b/arch/arm/plat-omap/devices.c
index ea28f98..6922f3b 100644
--- a/arch/arm/plat-omap/devices.c
+++ b/arch/arm/plat-omap/devices.c
@@ -234,13 +234,16 @@ static void omap_init_uwire(void)
static inline void omap_init_uwire(void) {}
#endif
-#if defined(CONFIG_TIDSPBRIDGE) || defined(CONFIG_TIDSPBRIDGE_MODULE)
+#if defined(CONFIG_TIDSPBRIDGE) || defined(CONFIG_TIDSPBRIDGE_MODULE) || \
+ defined(CONFIG_OMAP_REMOTE_PROC)
static phys_addr_t omap_dsp_phys_mempool_base;
+static phys_addr_t omap_dsp_phys_mempool_size;
void __init omap_dsp_reserve_sdram_memblock(void)
{
- phys_addr_t size = CONFIG_TIDSPBRIDGE_MEMPOOL_SIZE;
+ /* ignoring TIDSPBRIDGE for a moment here... */
+ phys_addr_t size = CONFIG_OMAP_CARVEOUT_MEMPOOL_SIZE;
phys_addr_t paddr;
if (!size)
@@ -256,6 +259,7 @@ void __init omap_dsp_reserve_sdram_memblock(void)
memblock_remove(paddr, size);
omap_dsp_phys_mempool_base = paddr;
+ omap_dsp_phys_mempool_size = size;
}
phys_addr_t omap_dsp_get_mempool_base(void)
@@ -263,6 +267,12 @@ phys_addr_t omap_dsp_get_mempool_base(void)
return omap_dsp_phys_mempool_base;
}
EXPORT_SYMBOL(omap_dsp_get_mempool_base);
+
+phys_addr_t omap_dsp_get_mempool_size(void)
+{
+ return omap_dsp_phys_mempool_size;
+}
+EXPORT_SYMBOL(omap_dsp_get_mempool_size);
#endif
/*
diff --git a/arch/arm/plat-omap/include/plat/dsp.h b/arch/arm/plat-omap/include/plat/dsp.h
index 9c604b3..31ee386 100644
--- a/arch/arm/plat-omap/include/plat/dsp.h
+++ b/arch/arm/plat-omap/include/plat/dsp.h
@@ -22,10 +22,14 @@ struct omap_dsp_platform_data {
phys_addr_t phys_mempool_size;
};
-#if defined(CONFIG_TIDSPBRIDGE) || defined(CONFIG_TIDSPBRIDGE_MODULE)
+#if defined(CONFIG_TIDSPBRIDGE) || defined(CONFIG_TIDSPBRIDGE_MODULE) || \
+ defined(CONFIG_OMAP_REMOTE_PROC)
extern void omap_dsp_reserve_sdram_memblock(void);
#else
static inline void omap_dsp_reserve_sdram_memblock(void) { }
#endif
+phys_addr_t omap_dsp_get_mempool_size(void);
+phys_addr_t omap_dsp_get_mempool_base(void);
+
#endif
--
1.7.1
From: Guzman Lugo, Fernando <[email protected]>
Add omap remoteproc devices for the ipu and dsp remote processors.
TODO:
- rework to use CMA instead of reserving memory at boot
[[email protected]: commit log, refactored and simplified, still wip]
Signed-off-by: Guzman Lugo, Fernando <[email protected]>
Signed-off-by: Ohad Ben-Cohen <[email protected]>
---
arch/arm/mach-omap2/Makefile | 2 +
arch/arm/mach-omap2/remoteproc.c | 159 ++++++++++++++++++++++++++++++++++++++
2 files changed, 161 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-omap2/remoteproc.c
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index b148077..2b04fe6 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -270,3 +270,5 @@ disp-$(CONFIG_OMAP2_DSS) := display.o
obj-y += $(disp-m) $(disp-y)
obj-y += common-board-devices.o
+
+obj-$(CONFIG_OMAP_REMOTE_PROC) += remoteproc.o
diff --git a/arch/arm/mach-omap2/remoteproc.c b/arch/arm/mach-omap2/remoteproc.c
new file mode 100644
index 0000000..4f846cb
--- /dev/null
+++ b/arch/arm/mach-omap2/remoteproc.c
@@ -0,0 +1,159 @@
+/*
+ * Remote processor machine-specific module for OMAP4
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/remoteproc.h>
+#include <linux/memblock.h>
+
+#include <plat/omap_device.h>
+#include <plat/omap_hwmod.h>
+#include <plat/remoteproc.h>
+#include <plat/dsp.h>
+#include <plat/io.h>
+
+#define L4_PERIPHERAL_L4CFG (L4_44XX_BASE)
+#define IPU_PERIPHERAL_L4CFG 0xAA000000
+
+#define IPU_MEM_TEXT 0x0
+#define IPU_MEM_DATA 0x80000000
+#define IPU_MEM_IPC 0xA0000000
+
+/*
+ * Memory mappings for the remote M3 subsystem
+ *
+ * Don't change the device addresses (first parameter), otherwise you'd have
+ * to update the firmware (BIOS image) accordingly.
+ *
+ * A 0 physical address (second parameter) means this physical region should
+ * be dynamically carved out at boot time.
+ *
+ * Sizes should be in the form of 2 ^ n * PAGE_SIZE (where n = 0, 1, 2, ...)
+ *
+ * Alignment requirement: at least page-based
+ */
+static struct rproc_mem_entry ipu_memory_maps[] = {
+ {IPU_MEM_IPC, 0, SZ_1M}, /* keep this IPC region first */
+ {IPU_MEM_TEXT, 0, SZ_4M},
+ {IPU_MEM_DATA, 0, SZ_32M},
+ {IPU_PERIPHERAL_L4CFG, L4_PERIPHERAL_L4CFG, SZ_16M},
+ { }
+};
+
+static struct omap_rproc_pdata omap4_rproc_data[] = {
+ {
+ .name = "dsp",
+ .iommu_name = "tesla",
+ .firmware = "tesla-dsp.bin",
+ .oh_name = "dsp_c0",
+ },
+ {
+ .name = "ipu",
+ .iommu_name = "ducati",
+ .firmware = "ducati-m3.bin",
+ .oh_name = "ipu_c0",
+ .oh_name_opt = "ipu_c1",
+ .memory_maps = ipu_memory_maps,
+ },
+};
+
+static struct omap_device_pm_latency omap_rproc_latency[] = {
+ {
+ .deactivate_func = omap_device_idle_hwmods,
+ .activate_func = omap_device_enable_hwmods,
+ .flags = OMAP_DEVICE_LATENCY_AUTO_ADJUST,
+ },
+};
+
+static int __init omap_rproc_init(void)
+{
+ const char *pdev_name = "omap-rproc";
+ struct omap_hwmod *oh[2];
+ struct omap_device *od;
+ int i, ret = 0, oh_count;
+ phys_addr_t paddr, size;
+
+ /* names like ipu_cx/dsp_cx might show up on other OMAPs, too */
+ if (!cpu_is_omap44xx())
+ return 0;
+
+ paddr = omap_dsp_get_mempool_base();
+ size = omap_dsp_get_mempool_size();
+ if (!paddr || !size) {
+ pr_warn("carveout memory is unavailable: 0x%x, 0x%x\n",
+ paddr, size);
+ return -ENOMEM;
+ }
+
+ /* dynamically allocate carveout memory as required by the ipu */
+ for (i = 0; i < ARRAY_SIZE(ipu_memory_maps); i++) {
+ struct rproc_mem_entry *me = &ipu_memory_maps[i];
+
+ if (!me->pa && me->size) {
+ if (me->size > size) {
+ pr_warn("out of carveout memory\n");
+ return -ENOMEM;
+ }
+
+ me->pa = paddr;
+ paddr += me->size;
+ size -= me->size;
+
+ pr_info("0x%x bytes at 0x%x %d\n", me->size, me->pa, i);
+ }
+ }
+
+ /* build the remote proc devices */
+ for (i = 0; i < ARRAY_SIZE(omap4_rproc_data); i++) {
+ const char *oh_name = omap4_rproc_data[i].oh_name;
+ const char *oh_name_opt = omap4_rproc_data[i].oh_name_opt;
+ oh_count = 0;
+
+ oh[0] = omap_hwmod_lookup(oh_name);
+ if (!oh[0]) {
+ pr_err("could not look up %s\n", oh_name);
+ continue;
+ }
+ oh_count++;
+
+ /* ipu has a secondary hwmod entry */
+ if (oh_name_opt) {
+ oh[1] = omap_hwmod_lookup(oh_name_opt);
+ if (!oh[1]) {
+ pr_err("could not look up %s\n", oh_name_opt);
+ continue;
+ }
+ oh_count++;
+ }
+
+ od = omap_device_build_ss(pdev_name, i, oh, oh_count,
+ &omap4_rproc_data[i],
+ sizeof(struct omap_rproc_pdata),
+ omap_rproc_latency,
+ ARRAY_SIZE(omap_rproc_latency),
+ false);
+ if (IS_ERR(od)) {
+ pr_err("Could not build omap_device for %s:%s\n",
+ pdev_name, oh_name);
+ ret = PTR_ERR(od);
+ }
+ }
+
+ return ret;
+}
+/* must be ready in time for device_initcall users */
+subsys_initcall(omap_rproc_init);
--
1.7.1
From: Mark Grosen <[email protected]>
Add remoteproc implementation for da850, so we can boot its DSP.
Signed-off-by: Mark Grosen <[email protected]>
Signed-off-by: Ohad Ben-Cohen <[email protected]>
---
arch/arm/mach-davinci/include/mach/remoteproc.h | 28 +++++
drivers/remoteproc/Kconfig | 16 +++
drivers/remoteproc/Makefile | 1 +
drivers/remoteproc/davinci_remoteproc.c | 147 +++++++++++++++++++++++
4 files changed, 192 insertions(+), 0 deletions(-)
create mode 100644 arch/arm/mach-davinci/include/mach/remoteproc.h
create mode 100644 drivers/remoteproc/davinci_remoteproc.c
diff --git a/arch/arm/mach-davinci/include/mach/remoteproc.h b/arch/arm/mach-davinci/include/mach/remoteproc.h
new file mode 100644
index 0000000..af6e88c
--- /dev/null
+++ b/arch/arm/mach-davinci/include/mach/remoteproc.h
@@ -0,0 +1,28 @@
+/*
+ * Remote Processor
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _DAVINCI_REMOTEPROC_H
+#define _DAVINCI_REMOTEPROC_H
+
+#include <linux/remoteproc.h>
+
+struct davinci_rproc_pdata {
+ struct rproc_ops *ops;
+ char *name;
+ char *clk_name;
+ char *firmware;
+};
+
+#endif /* _DAVINCI_REMOTEPROC_H */
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
index 88421bd..1e594b5 100644
--- a/drivers/remoteproc/Kconfig
+++ b/drivers/remoteproc/Kconfig
@@ -26,3 +26,19 @@ config OMAP_REMOTE_PROC
It's safe to say n here if you're not interested in multimedia
offloading or just want a bare minimum kernel.
+
+config DAVINCI_REMOTE_PROC
+ tristate "Davinci remoteproc support"
+ depends on ARCH_DAVINCI_DA850
+ select REMOTE_PROC
+ default y
+ help
+ Say y here to support Davinci's DSP remote processor via the remote
+ processor framework (currently only da850 is supported).
+
+ Usually you want to say y here, in order to enable multimedia
+ use-cases to run on your platform (multimedia codecs are
+ offloaded to remote DSP processors using this framework).
+
+ It's safe to say n here if you're not interested in multimedia
+ offloading or just want a bare minimum kernel.
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
index 0198b1d..e83dac9 100644
--- a/drivers/remoteproc/Makefile
+++ b/drivers/remoteproc/Makefile
@@ -4,3 +4,4 @@
obj-$(CONFIG_REMOTE_PROC) += remoteproc.o
obj-$(CONFIG_OMAP_REMOTE_PROC) += omap_remoteproc.o
+obj-$(CONFIG_DAVINCI_REMOTE_PROC) += davinci_remoteproc.o
diff --git a/drivers/remoteproc/davinci_remoteproc.c b/drivers/remoteproc/davinci_remoteproc.c
new file mode 100644
index 0000000..0e26fe9
--- /dev/null
+++ b/drivers/remoteproc/davinci_remoteproc.c
@@ -0,0 +1,147 @@
+/*
+ * Remote processor machine-specific module for Davinci
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ *
+ * 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 program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/err.h>
+#include <linux/printk.h>
+#include <linux/bitops.h>
+#include <linux/platform_device.h>
+#include <linux/remoteproc.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+
+#include <mach/da8xx.h>
+#include <mach/cputype.h>
+#include <mach/psc.h>
+#include <mach/remoteproc.h>
+
+/*
+ * Technical Reference:
+ * OMAP-L138 Applications Processor System Reference Guide
+ * http://www.ti.com/litv/pdf/sprugm7d
+ */
+
+/* local reset bit (0 is asserted) in MDCTL15 register (section 9.6.18) */
+#define LRST BIT(8)
+
+/* next state bits in MDCTL15 register (section 9.6.18) */
+#define NEXT_ENABLED 0x3
+
+/* register for DSP boot address in SYSCFG0 module (section 11.5.6) */
+#define HOST1CFG 0x44
+
+static inline int davinci_rproc_start(struct rproc *rproc, u64 bootaddr)
+{
+ struct device *dev = rproc->dev;
+ struct davinci_rproc_pdata *pdata = dev->platform_data;
+ struct davinci_soc_info *soc_info = &davinci_soc_info;
+ void __iomem *psc_base;
+ struct clk *dsp_clk;
+
+ /* hw requires the start (boot) address be on 1KB boundary */
+ if (bootaddr & 0x3ff) {
+ dev_err(dev, "invalid boot address: must be aligned to 1KB\n");
+ return -EINVAL;
+ }
+
+ dsp_clk = clk_get(dev, pdata->clk_name);
+ if (IS_ERR_OR_NULL(dsp_clk)) {
+ dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
+ return PTR_ERR(dsp_clk);
+ }
+
+ clk_enable(dsp_clk);
+ rproc->priv = dsp_clk;
+
+ psc_base = ioremap(soc_info->psc_bases[0], SZ_4K);
+
+ /* insure local reset is asserted before writing start address */
+ __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM);
+
+ __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG));
+
+ /* de-assert local reset to start the dsp running */
+ __raw_writel(LRST | NEXT_ENABLED, psc_base + MDCTL +
+ 4 * DA8XX_LPSC0_GEM);
+
+ iounmap(psc_base);
+
+ return 0;
+}
+
+static inline int davinci_rproc_stop(struct rproc *rproc)
+{
+ struct davinci_soc_info *soc_info = &davinci_soc_info;
+ void __iomem *psc_base;
+ struct clk *dsp_clk = rproc->priv;
+
+ psc_base = ioremap(soc_info->psc_bases[0], SZ_4K);
+
+ /* halt the dsp by asserting local reset */
+ __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM);
+
+ clk_disable(dsp_clk);
+ clk_put(dsp_clk);
+
+ iounmap(psc_base);
+
+ return 0;
+}
+
+static struct rproc_ops davinci_rproc_ops = {
+ .start = davinci_rproc_start,
+ .stop = davinci_rproc_stop,
+};
+
+static int davinci_rproc_probe(struct platform_device *pdev)
+{
+ struct davinci_rproc_pdata *pdata = pdev->dev.platform_data;
+
+ return rproc_register(&pdev->dev, pdata->name, &davinci_rproc_ops,
+ pdata->firmware, NULL, THIS_MODULE);
+}
+
+static int __devexit davinci_rproc_remove(struct platform_device *pdev)
+{
+ struct davinci_rproc_pdata *pdata = pdev->dev.platform_data;
+
+ return rproc_unregister(pdata->name);
+}
+
+static struct platform_driver davinci_rproc_driver = {
+ .probe = davinci_rproc_probe,
+ .remove = __devexit_p(davinci_rproc_remove),
+ .driver = {
+ .name = "davinci-rproc",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init davinci_rproc_init(void)
+{
+ return platform_driver_register(&davinci_rproc_driver);
+}
+module_init(davinci_rproc_init);
+
+static void __exit davinci_rproc_exit(void)
+{
+ platform_driver_unregister(&davinci_rproc_driver);
+}
+module_exit(davinci_rproc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Davinci Remote Processor control driver");
--
1.7.1
From: Mark Grosen <[email protected]>
Add davinci remoteproc device for the da850's C674x dsp remote
processor, and support it on the da850-evm and omapl138-hawk boards.
Signed-off-by: Mark Grosen <[email protected]>
Signed-off-by: Ohad Ben-Cohen <[email protected]>
---
It'd be nice to split this patch to the different components it changes,
but it's probably not so important at this early stage.
arch/arm/mach-davinci/board-da850-evm.c | 4 ++++
arch/arm/mach-davinci/board-omapl138-hawk.c | 4 ++++
arch/arm/mach-davinci/da850.c | 14 ++++++++++++++
arch/arm/mach-davinci/devices-da8xx.c | 15 +++++++++++++++
arch/arm/mach-davinci/include/mach/da8xx.h | 1 +
5 files changed, 38 insertions(+), 0 deletions(-)
diff --git a/arch/arm/mach-davinci/board-da850-evm.c b/arch/arm/mach-davinci/board-da850-evm.c
index a7b41bf..6b35f0a 100644
--- a/arch/arm/mach-davinci/board-da850-evm.c
+++ b/arch/arm/mach-davinci/board-da850-evm.c
@@ -1176,6 +1176,10 @@ static __init void da850_evm_init(void)
i2c_register_board_info(1, da850_evm_i2c_devices,
ARRAY_SIZE(da850_evm_i2c_devices));
+ ret = da850_register_rproc();
+ if (ret)
+ pr_warning("dsp/rproc registration failed: %d\n", ret);
+
/*
* shut down uart 0 and 1; they are not used on the board and
* accessing them causes endless "too much work in irq53" messages
diff --git a/arch/arm/mach-davinci/board-omapl138-hawk.c b/arch/arm/mach-davinci/board-omapl138-hawk.c
index 67c38d0..52c8434 100644
--- a/arch/arm/mach-davinci/board-omapl138-hawk.c
+++ b/arch/arm/mach-davinci/board-omapl138-hawk.c
@@ -319,6 +319,10 @@ static __init void omapl138_hawk_init(void)
pr_warning("omapl138_hawk_init: "
"watchdog registration failed: %d\n",
ret);
+
+ ret = da850_register_rproc();
+ if (ret)
+ pr_warning("dsp/rproc registration failed: %d\n", ret);
}
#ifdef CONFIG_SERIAL_8250_CONSOLE
diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
index 133aac4..9280b1e 100644
--- a/arch/arm/mach-davinci/da850.c
+++ b/arch/arm/mach-davinci/da850.c
@@ -74,6 +74,13 @@ static struct clk pll0_aux_clk = {
.flags = CLK_PLL | PRE_PLL,
};
+static struct clk pll0_sysclk1 = {
+ .name = "pll0_sysclk1",
+ .parent = &pll0_clk,
+ .flags = CLK_PLL,
+ .div_reg = PLLDIV1,
+};
+
static struct clk pll0_sysclk2 = {
.name = "pll0_sysclk2",
.parent = &pll0_clk,
@@ -373,6 +380,12 @@ static struct clk spi1_clk = {
.flags = DA850_CLK_ASYNC3,
};
+static struct clk dsp_clk = {
+ .name = "dsp",
+ .parent = &pll0_sysclk1,
+ .lpsc = DA8XX_LPSC0_GEM,
+};
+
static struct clk_lookup da850_clks[] = {
CLK(NULL, "ref", &ref_clk),
CLK(NULL, "pll0", &pll0_clk),
@@ -419,6 +432,7 @@ static struct clk_lookup da850_clks[] = {
CLK(NULL, "usb20", &usb20_clk),
CLK("spi_davinci.0", NULL, &spi0_clk),
CLK("spi_davinci.1", NULL, &spi1_clk),
+ CLK(NULL, "dsp", &dsp_clk),
CLK(NULL, NULL, NULL),
};
diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c
index fc4e98e..3becfd1 100644
--- a/arch/arm/mach-davinci/devices-da8xx.c
+++ b/arch/arm/mach-davinci/devices-da8xx.c
@@ -20,6 +20,7 @@
#include <mach/time.h>
#include <mach/da8xx.h>
#include <mach/cpuidle.h>
+#include <mach/remoteproc.h>
#include "clock.h"
@@ -647,6 +648,20 @@ int __init da850_register_mmcsd1(struct davinci_mmc_config *config)
da850_mmcsd1_device.dev.platform_data = config;
return platform_device_register(&da850_mmcsd1_device);
}
+
+int __init da850_register_rproc(void)
+{
+ struct platform_device *rproc;
+ struct davinci_rproc_pdata rproc_pdata = {
+ .name = "dsp",
+ .firmware = "davinci-dsp.bin",
+ .clk_name = "dsp",
+ };
+
+ rproc = platform_device_register_data(NULL, "davinci-rproc", 0,
+ &rproc_pdata, sizeof(rproc_pdata));
+ return PTR_RET(rproc);
+};
#endif
static struct resource da8xx_rtc_resources[] = {
diff --git a/arch/arm/mach-davinci/include/mach/da8xx.h b/arch/arm/mach-davinci/include/mach/da8xx.h
index ad64da7..a6f024f 100644
--- a/arch/arm/mach-davinci/include/mach/da8xx.h
+++ b/arch/arm/mach-davinci/include/mach/da8xx.h
@@ -74,6 +74,7 @@ void __init da850_init(void);
int da830_register_edma(struct edma_rsv_info *rsv);
int da850_register_edma(struct edma_rsv_info *rsv[2]);
+int da850_register_rproc(void);
int da8xx_register_i2c(int instance, struct davinci_i2c_platform_data *pdata);
int da8xx_register_spi(int instance, struct spi_board_info *info, unsigned len);
int da8xx_register_watchdog(void);
--
1.7.1
Add a virtio-based IPC bus, which enables kernel users to communicate
with remote processors over shared memory using a simple messaging
protocol.
Assign a local address for every local endpoint that is created,
and bind it to the user's callback. Invoke that callback when the
destination of an inbound message is the user's address.
Signed-off-by: Ohad Ben-Cohen <[email protected]>
---
Documentation/ABI/testing/sysfs-bus-rpmsg | 75 +++
Documentation/rpmsg.txt | 308 +++++++++
drivers/Kconfig | 1 +
drivers/Makefile | 1 +
drivers/rpmsg/Kconfig | 14 +
drivers/rpmsg/Makefile | 1 +
drivers/rpmsg/virtio_rpmsg_bus.c | 1036 +++++++++++++++++++++++++++++
include/linux/mod_devicetable.h | 10 +
include/linux/rpmsg.h | 421 ++++++++++++
include/linux/virtio_ids.h | 1 +
10 files changed, 1868 insertions(+), 0 deletions(-)
create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg
create mode 100644 Documentation/rpmsg.txt
create mode 100644 drivers/rpmsg/Kconfig
create mode 100644 drivers/rpmsg/Makefile
create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c
create mode 100644 include/linux/rpmsg.h
diff --git a/Documentation/ABI/testing/sysfs-bus-rpmsg b/Documentation/ABI/testing/sysfs-bus-rpmsg
new file mode 100644
index 0000000..7bddf70
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-bus-rpmsg
@@ -0,0 +1,75 @@
+What: /sys/bus/rpmsg/devices/.../name
+Date: June 2011
+KernelVersion: 3.2
+Contact: Ohad Ben-Cohen <[email protected]>
+Description:
+ Every rpmsg device is a communication channel with a remote
+ processor. Channels are identified with a (textual) name,
+ which is maximum 32 bytes long (defined as RPMSG_NAME_SIZE in
+ rpmsg.h).
+
+ This sysfs entry contains the name of this channel.
+
+What: /sys/bus/rpmsg/devices/.../src
+Date: June 2011
+KernelVersion: 3.2
+Contact: Ohad Ben-Cohen <[email protected]>
+Description:
+ Every rpmsg device is a communication channel with a remote
+ processor. Channels have a local ("source") rpmsg address,
+ and remote ("destination") rpmsg address. When an entity
+ starts listening on one end of a channel, it assigns it with
+ a unique rpmsg address (a 32 bits integer). This way when
+ inbound messages arrive to this address, the rpmsg core
+ dispatches them to the listening entity (a kernel driver).
+
+ This sysfs entry contains the src (local) rpmsg address
+ of this channel. If it contains 0xffffffff, then an address
+ wasn't assigned (can happen if no driver exists for this
+ channel).
+
+What: /sys/bus/rpmsg/devices/.../dst
+Date: June 2011
+KernelVersion: 3.2
+Contact: Ohad Ben-Cohen <[email protected]>
+Description:
+ Every rpmsg device is a communication channel with a remote
+ processor. Channels have a local ("source") rpmsg address,
+ and remote ("destination") rpmsg address. When an entity
+ starts listening on one end of a channel, it assigns it with
+ a unique rpmsg address (a 32 bits integer). This way when
+ inbound messages arrive to this address, the rpmsg core
+ dispatches them to the listening entity.
+
+ This sysfs entry contains the dst (remote) rpmsg address
+ of this channel. If it contains 0xffffffff, then an address
+ wasn't assigned (can happen if the kernel driver that
+ is attached to this channel is exposing a service to the
+ remote processor. This make it a local rpmsg server,
+ and it is listening for inbound messages that may be sent
+ from any remote rpmsg client; it is not bound to a single
+ remote entity).
+
+What: /sys/bus/rpmsg/devices/.../announce
+Date: June 2011
+KernelVersion: 3.2
+Contact: Ohad Ben-Cohen <[email protected]>
+Description:
+ Every rpmsg device is a communication channel with a remote
+ processor. Channels are identified by a textual name (see
+ /sys/bus/rpmsg/devices/.../name above) and have a local
+ ("source") rpmsg address, and remote ("destination") rpmsg
+ address.
+
+ A channel is first created when an entity, whether local
+ or remote, starts listening on it for messages (and is thus
+ called an rpmsg server).
+
+ When that happens, a "name service" announcement is sent
+ to the other processor, in order to let it know about the
+ creation of the channel (this way remote clients know they
+ can start sending messages).
+
+ This sysfs entry tells us whether the channel is a local
+ server channel that is announced (values are either
+ true or false).
diff --git a/Documentation/rpmsg.txt b/Documentation/rpmsg.txt
new file mode 100644
index 0000000..0a7c820
--- /dev/null
+++ b/Documentation/rpmsg.txt
@@ -0,0 +1,308 @@
+Remote Processor Messaging (rpmsg) Framework
+
+1. Introduction
+
+Modern SoCs typically employ heterogeneous remote processor devices in
+asymmetric multiprocessing (AMP) configurations, which may be running
+different instances of operating system, whether it's Linux or any other
+flavor of real-time OS.
+
+OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP.
+Typically, the dual cortex-A9 is running Linux in a SMP configuration,
+and each of the other three cores (two M3 cores and a DSP) is running
+its own instance of RTOS in an AMP configuration.
+
+Typically AMP remote processors employ dedicated DSP codecs and multimedia
+hardware accelerators, and therefore are often used to offload cpu-intensive
+multimedia tasks from the main application processor.
+
+These remote processors could also be used to control latency-sensitive
+sensors, drive random hardware blocks, or just perform background tasks
+while the main CPU is idling.
+
+Users of those remote processors can either be userland apps (e.g. multimedia
+frameworks talking with remote OMX components) or kernel drivers (controlling
+hardware accessible only by the remote processor, reserving kernel-controlled
+resources on behalf of the remote processor, etc..).
+
+Rpmsg is a virtio-based messaging bus that allows kernel drivers to communicate
+with remote processors available on the system. In turn, drivers could then
+expose appropriate user space interfaces, if needed.
+
+When writing a driver that exposes rpmsg communication to userland, please
+keep in mind that remote processors might have direct access to the
+system's physical memory and/or other sensitive hardware resources (e.g. on
+OMAP4, remote cores (/hardware accelerators) may have direct access to the
+physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox
+devices, hwspinlocks, etc..). Moreover, those remote processors might be
+running RTOS where every task can access the entire memory/devices exposed
+to the processor. To minimize the risks of rogue (/buggy) userland code
+exploiting (/triggering) remote bugs, and by that taking over (/down) the
+system, it is often desired to limit userland to specific rpmsg channels (see
+definition below) it is allowed to send messages on, and if possible/relevant,
+minimize the amount of control it has over the content of the messages.
+
+Every rpmsg device is a communication channel with a remote processor (thus
+rpmsg devices are called channels). Channels are identified by a textual name
+and have a local ("source") rpmsg address, and remote ("destination") rpmsg
+address.
+
+When a driver starts listening on a channel, it binds it with a unique
+rpmsg src address (a 32 bits integer). This way when inbound messages arrive
+to this src address, the rpmsg core dispatches them to that driver (by invoking
+the driver's rx handler with the payload of the incoming message).
+
+
+2. User API
+
+ int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len);
+ - sends a message across to the remote processor on a given channel.
+ The caller should specify the channel, the data it wants to send,
+ and its length (in bytes). The message will be sent on the specified
+ channel, i.e. its source and destination address fields will be
+ set to the channel's src and dst addresses.
+
+ In case there are no TX buffers available, the function will block until
+ one becomes available (i.e. until the remote processor will consume
+ a tx buffer and put it back on virtio's used descriptor ring),
+ or a timeout of 15 seconds elapses. When the latter happens,
+ -ERESTARTSYS is returned.
+ The function can only be called from a process context (for now).
+ Returns 0 on success and an appropriate error value on failure.
+
+ int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst);
+ - sends a message across to the remote processor on a given channel,
+ to a destination address provided by the caller.
+ The caller should specify the channel, the data it wants to send,
+ its length (in bytes), and an explicit destination address.
+ The message will then be sent to the remote processor to which the
+ channel belongs to, using the channel's src address, and the user-provided
+ dst address (thus the channel's dst address will be ignored).
+
+ In case there are no TX buffers available, the function will block until
+ one becomes available (i.e. until the remote processor will consume
+ a tx buffer and put it back on virtio's used descriptor ring),
+ or a timeout of 15 seconds elapses. When the latter happens,
+ -ERESTARTSYS is returned.
+ The function can only be called from a process context (for now).
+ Returns 0 on success and an appropriate error value on failure.
+
+ int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst,
+ void *data, int len);
+ - sends a message across to the remote processor, using the src and dst
+ addresses provided by the user.
+ The caller should specify the channel, the data it wants to send,
+ its length (in bytes), and explicit source and destination addresses.
+ The message will then be sent to the remote processor to which the
+ channel belongs to, but the channel's src and dst addresses will be
+ ignored (and the user-provided addresses will be used instead).
+
+ In case there are no TX buffers available, the function will block until
+ one becomes available (i.e. until the remote processor will consume
+ a tx buffer and put it back on virtio's used descriptor ring),
+ or a timeout of 15 seconds elapses. When the latter happens,
+ -ERESTARTSYS is returned.
+ The function can only be called from a process context (for now).
+ Returns 0 on success and an appropriate error value on failure.
+
+ int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len);
+ - sends a message across to the remote processor on a given channel.
+ The caller should specify the channel, the data it wants to send,
+ and its length (in bytes). The message will be sent on the specified
+ channel, i.e. its source and destination address fields will be
+ set to the channel's src and dst addresses.
+
+ In case there are no TX buffers available, the function will immediately
+ return -ENOMEM without waiting until one becomes available.
+ The function can only be called from a process context (for now).
+ Returns 0 on success and an appropriate error value on failure.
+
+ int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst)
+ - sends a message across to the remote processor on a given channel,
+ to a destination address provided by the user.
+ The user should specify the channel, the data it wants to send,
+ its length (in bytes), and an explicit destination address.
+ The message will then be sent to the remote processor to which the
+ channel belongs to, using the channel's src address, and the user-provided
+ dst address (thus the channel's dst address will be ignored).
+
+ In case there are no TX buffers available, the function will immediately
+ return -ENOMEM without waiting until one becomes available.
+ The function can only be called from a process context (for now).
+ Returns 0 on success and an appropriate error value on failure.
+
+ int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst,
+ void *data, int len);
+ - sends a message across to the remote processor, using source and
+ destination addresses provided by the user.
+ The user should specify the channel, the data it wants to send,
+ its length (in bytes), and explicit source and destination addresses.
+ The message will then be sent to the remote processor to which the
+ channel belongs to, but the channel's src and dst addresses will be
+ ignored (and the user-provided addresses will be used instead).
+
+ In case there are no TX buffers available, the function will immediately
+ return -ENOMEM without waiting until one becomes available.
+ The function can only be called from a process context (for now).
+ Returns 0 on success and an appropriate error value on failure.
+
+ struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev,
+ void (*cb)(struct rpmsg_channel *, void *, int, void *, u32),
+ void *priv, u32 addr);
+ - every rpmsg address in the system is bound to an rx callback (so when
+ inbound messages arrive, they are dispatched by the rpmsg bus using the
+ appropriate callback handler) by means of an rpmsg_endpoint struct.
+
+ This function allows drivers to create such an endpoint, and by that,
+ bind a callback, and possibly some private data too, to an rpmsg address
+ (either one that is known in advance, or one that will be dynamically
+ assigned for them).
+
+ Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint
+ is already created for them when they are probed by the rpmsg bus
+ (using the rx callback they provide when they registered to the rpmsg bus).
+
+ So things should just work for simple drivers: they already have an
+ endpoint, their rx callback is bound to their rpmsg address, and when
+ relevant inbound messages arrive (i.e. messages which their dst address
+ equals to the src address of their rpmsg channel), the driver's handler
+ is invoked to process it.
+
+ That said, more complicated drivers might do need to allocate
+ additional rpmsg addresses, and bind them to different rx callbacks.
+ To accomplish that, those drivers need to call this function.
+ Driver should provide their channel (so the new endpoint would bind
+ to the same remote processor their channel belongs to), an rx callback
+ function, an optional private data (which is provided back when the
+ rx callback is invoked), and an address they want to bind with the
+ callback. If addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will
+ dynamically assign them an available rpmsg address (drivers should have
+ a very good reason why not to always use RPMSG_ADDR_ANY here).
+
+ Returns a pointer to the endpoint on success, or NULL on error.
+
+ void rpmsg_destroy_ept(struct rpmsg_endpoint *ept);
+ - destroys an existing rpmsg endpoint. user should provide a pointer
+ to an rpmsg endpoint that was previously created with rpmsg_create_ept().
+
+ int register_rpmsg_driver(struct rpmsg_driver *rpdrv);
+ - registers an rpmsg driver with the rpmsg bus. user should provide
+ a pointer to an rpmsg_driver struct, which contains the driver's
+ ->probe() and ->remove() functions, an rx callback, and an id_table
+ specifying the names of the channels this driver is interested to
+ be probed with.
+
+ void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv);
+ - unregisters an rpmsg driver from the rpmsg bus. user should provide
+ a pointer to a previously-registerd rpmsg_driver struct.
+ Returns 0 on success, and an appropriate error value on failure.
+
+
+3. Typical usage
+
+The following is a simple rpmsg driver, that sends an "hello!" message
+on probe(), and whenever it receives an incoming message, it dumps its
+content to the console.
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rpmsg.h>
+
+static void rpmsg_sample_cb(struct rpmsg_channel *rpdev, void *data, int len,
+ void *priv, u32 src)
+{
+ print_hex_dump(KERN_INFO, "incoming message:", DUMP_PREFIX_NONE,
+ 16, 1, data, len, true);
+}
+
+static int rpmsg_sample_probe(struct rpmsg_channel *rpdev)
+{
+ int err;
+
+ dev_info(&rpdev->dev, "chnl: 0x%x -> 0x%x\n", rpdev->src, rpdev->dst);
+
+ /* send a message on our channel */
+ err = rpmsg_send(rpdev, "hello!", 6);
+ if (err) {
+ pr_err("rpmsg_send failed: %d\n", err);
+ return err;
+ }
+
+ return 0;
+}
+
+static void __devexit rpmsg_sample_remove(struct rpmsg_channel *rpdev)
+{
+ dev_info(&rpdev->dev, "rpmsg sample client driver is removed\n");
+}
+
+static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = {
+ { .name = "rpmsg-client-sample" },
+ { },
+};
+MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table);
+
+static struct rpmsg_driver rpmsg_sample_client = {
+ .drv.name = KBUILD_MODNAME,
+ .drv.owner = THIS_MODULE,
+ .id_table = rpmsg_driver_sample_id_table,
+ .probe = rpmsg_sample_probe,
+ .callback = rpmsg_sample_cb,
+ .remove = __devexit_p(rpmsg_sample_remove),
+};
+
+static int __init init(void)
+{
+ return register_rpmsg_driver(&rpmsg_sample_client);
+}
+
+static void __exit fini(void)
+{
+ unregister_rpmsg_driver(&rpmsg_sample_client);
+}
+module_init(init);
+module_exit(fini);
+
+
+4. API for implementors
+
+Adding rpmsg support for a new platform is relatively easy; one just needs
+to register a VIRTIO_ID_RPMSG virtio device with the usual virtio_config_ops
+handlers.
+
+For simplicity, it is recommended to register a single virtio device for
+every physical remote processor we have in the system, but there are no
+hard rules here, and this decision largely depends on the use cases,
+platform capabilities, performance requirements, etc.
+
+OMAP4, e.g., registers two virtio devices to communicate with the remote dual
+Cortex-M3 processor, because each of the M3 cores executes its own OS instance
+(see omap_rpmsg.c for reference).
+This way each of the remote cores may have different rpmsg channels, and the
+rpmsg core will treat them as completely independent processors (despite
+the fact that both of are part of the same physical device, and they are
+powered on/off together).
+
+Notable virtio implementation bits:
+
+* virtio features: VIRTIO_RPMSG_F_NS should be enabled if the remote
+ processor supports dynamic name service announcement messages.
+
+ Enabling this means that rpmsg device (i.e. channel) creation is
+ completely dynamic: the remote processor announces the existence of a
+ remote rpmsg service by sending a name service message (which contains
+ the name and rpmsg addr of the remote service, see struct rpmsg_ns_msg).
+
+ This message is then handled by the rpmsg bus, which in turn dynamically
+ creates and registers an rpmsg channel (which represents the remote service).
+ If/when a relevant rpmsg driver is registered, it will be immediately probed
+ by the bus, and can then start sending messages to the remote service.
+
+* virtqueue's notify handler: should inform the remote processor whenever
+ it is kicked by virtio. OMAP4 is using its mailbox device to interrupt
+ the remote processor, and inform it which virtqueue number is kicked
+ (the index of the kicked virtqueue is written to the mailbox register).
+
+* virtio_config_ops's ->get() handler: the rpmsg bus uses this handler
+ to request for platform-specific configuration values.
+ see enum rpmsg_platform_requests for more info.
diff --git a/drivers/Kconfig b/drivers/Kconfig
index 1f6d6d3..840f835 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -128,4 +128,5 @@ source "drivers/clocksource/Kconfig"
source "drivers/remoteproc/Kconfig"
+source "drivers/rpmsg/Kconfig"
endmenu
diff --git a/drivers/Makefile b/drivers/Makefile
index 4d53a18..2980a15 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_ARM_AMBA) += amba/
obj-$(CONFIG_DMA_ENGINE) += dma/
obj-$(CONFIG_VIRTIO) += virtio/
+obj-$(CONFIG_RPMSG) += rpmsg/
obj-$(CONFIG_XEN) += xen/
# regulators early, since some subsystems rely on them to initialize
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
new file mode 100644
index 0000000..41303f5
--- /dev/null
+++ b/drivers/rpmsg/Kconfig
@@ -0,0 +1,14 @@
+# RPMSG always gets selected by whoever wants it
+config RPMSG
+ tristate
+ select VIRTIO
+ select VIRTIO_RING
+
+if RPMSG
+
+# OK, it's a little counter-intuitive to do this, but it puts it neatly under
+# the rpmsg menu (and it's the approach preferred by the virtio folks).
+
+source "drivers/virtio/Kconfig"
+
+endif # RPMSG
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
new file mode 100644
index 0000000..7617fcb
--- /dev/null
+++ b/drivers/rpmsg/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o
diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
new file mode 100644
index 0000000..3e98b02
--- /dev/null
+++ b/drivers/rpmsg/virtio_rpmsg_bus.c
@@ -0,0 +1,1036 @@
+/*
+ * Virtio-based remote processor messaging bus
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <[email protected]>
+ * Brian Swetland <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_config.h>
+#include <linux/scatterlist.h>
+#include <linux/slab.h>
+#include <linux/idr.h>
+#include <linux/jiffies.h>
+#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/rpmsg.h>
+
+/**
+ * struct virtproc_info - virtual remote processor state
+ * @vdev: the virtio device
+ * @rvq: rx virtqueue (from pov of local processor)
+ * @svq: tx virtqueue (from pov of local processor)
+ * @rbufs: address of rx buffers
+ * @sbufs: address of tx buffers
+ * @last_sbuf: index of last tx buffer used
+ * @phys_base: physical base addr of the buffers
+ * @tx_lock: protects svq, sbufs and sleepers, to allow concurrent senders
+ * @num_bufs: total number of buffers allocated for communicating with this
+ * virtual remote processor. half is used for rx and half for tx.
+ * @buf_size: size of buffers allocated for communications
+ * @endpoints: idr of local endpoints, allows fast retrieval
+ * @endpoints_lock: lock of the endpoints set
+ * @sendq: wait queue of sending contexts waiting for a tx buffers
+ * @sleepers: number of senders that are waiting for a tx buffer
+ * @ns_ept: the bus's name service endpoint
+ *
+ * This structure stores the rpmsg state of a given virtio remote processor
+ * device (there might be several virtio proc devices for each physical
+ * remote processor).
+ */
+struct virtproc_info {
+ struct virtio_device *vdev;
+ struct virtqueue *rvq, *svq;
+ void *rbufs, *sbufs;
+ int last_sbuf;
+ phys_addr_t phys_base;
+ spinlock_t tx_lock;
+ int num_bufs;
+ int buf_size;
+ struct idr endpoints;
+ spinlock_t endpoints_lock;
+ wait_queue_head_t sendq;
+ int sleepers;
+ struct rpmsg_endpoint *ns_ept;
+};
+
+#define to_rpmsg_channel(d) container_of(d, struct rpmsg_channel, dev)
+#define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv)
+
+/*
+ * Local addresses are dynamically allocated on-demand.
+ * We do not dynamically assign addresses from the low 1024 range,
+ * in order to reserve that address range for predefined services.
+ */
+#define RPMSG_RESERVED_ADDRESSES (1024)
+
+/* Address 53 is reserved for advertising remote services */
+#define RPMSG_NS_ADDR (53)
+
+/* show configuration fields */
+#define rpmsg_show_attr(field, path, format_string) \
+static ssize_t \
+field##_show(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+{ \
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev); \
+ \
+ return sprintf(buf, format_string, rpdev->path); \
+}
+
+/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */
+rpmsg_show_attr(name, id.name, "%s\n");
+rpmsg_show_attr(src, src, "0x%x\n");
+rpmsg_show_attr(dst, dst, "0x%x\n");
+rpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
+
+/* unique (free running) index for rpmsg devices */
+static unsigned int rpmsg_dev_index;
+
+static ssize_t modalias_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+
+ return sprintf(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name);
+}
+
+static struct device_attribute rpmsg_dev_attrs[] = {
+ __ATTR_RO(name),
+ __ATTR_RO(modalias),
+ __ATTR_RO(dst),
+ __ATTR_RO(src),
+ __ATTR_RO(announce),
+ __ATTR_NULL
+};
+
+/* rpmsg devices and drivers are matched using the service name */
+static inline int rpmsg_id_match(const struct rpmsg_channel *rpdev,
+ const struct rpmsg_device_id *id)
+{
+ if (strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE))
+ return 0;
+
+ return 1;
+}
+
+/* match rpmsg channel and rpmsg driver */
+static int rpmsg_dev_match(struct device *dev, struct device_driver *drv)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+ struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv);
+ const struct rpmsg_device_id *ids = rpdrv->id_table;
+ unsigned int i;
+
+ for (i = 0; ids[i].name[0]; i++) {
+ if (rpmsg_id_match(rpdev, &ids[i]))
+ return 1;
+ }
+
+ return 0;
+}
+
+static int rpmsg_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+
+ return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT,
+ rpdev->id.name);
+}
+
+/* for more info, see below documentation of rpmsg_create_ept() */
+static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
+ struct rpmsg_channel *rpdev,
+ void (*cb)(struct rpmsg_channel *, void *, int, void *, u32),
+ void *priv, u32 addr)
+{
+ int err, tmpaddr, request;
+ struct rpmsg_endpoint *ept;
+ struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev;
+
+ if (!idr_pre_get(&vrp->endpoints, GFP_KERNEL))
+ return NULL;
+
+ ept = kzalloc(sizeof(*ept), GFP_KERNEL);
+ if (!ept) {
+ dev_err(dev, "failed to kzalloc a new ept\n");
+ return NULL;
+ }
+
+ ept->rpdev = rpdev;
+ ept->cb = cb;
+ ept->priv = priv;
+
+ /* do we need to allocate a local address ? */
+ request = addr == RPMSG_ADDR_ANY ? RPMSG_RESERVED_ADDRESSES : addr;
+
+ spin_lock(&vrp->endpoints_lock);
+
+ /* bind the endpoint to an rpmsg address (and allocate one if needed) */
+ err = idr_get_new_above(&vrp->endpoints, ept, request, &tmpaddr);
+ if (err) {
+ dev_err(dev, "idr_get_new_above failed: %d\n", err);
+ goto free_ept;
+ }
+
+ /* make sure the user's address request is fulfilled, if relevant */
+ if (addr != RPMSG_ADDR_ANY && tmpaddr != addr) {
+ dev_err(dev, "address 0x%x already in use\n", addr);
+ goto rem_idr;
+ }
+
+ ept->addr = tmpaddr;
+
+ spin_unlock(&vrp->endpoints_lock);
+
+ return ept;
+
+rem_idr:
+ idr_remove(&vrp->endpoints, request);
+free_ept:
+ spin_unlock(&vrp->endpoints_lock);
+ kfree(ept);
+ return NULL;
+}
+
+/**
+ * rpmsg_create_ept() - create a new rpmsg_endpoint
+ * @rpdev: rpmsg channel device
+ * @cb: rx callback handler
+ * @priv: private data for the driver's use
+ * @addr: local rpmsg address to bind with @cb
+ *
+ * Every rpmsg address in the system is bound to an rx callback (so when
+ * inbound messages arrive, they are dispatched by the rpmsg bus using the
+ * appropriate callback handler) by means of an rpmsg_endpoint struct.
+ *
+ * This function allows drivers to create such an endpoint, and by that,
+ * bind a callback, and possibly some private data too, to an rpmsg address
+ * (either one that is known in advance, or one that will be dynamically
+ * assigned for them).
+ *
+ * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint
+ * is already created for them when they are probed by the rpmsg bus
+ * (using the rx callback provided when they registered to the rpmsg bus).
+ *
+ * So things should just work for simple drivers: they already have an
+ * endpoint, their rx callback is bound to their rpmsg address, and when
+ * relevant inbound messages arrive (i.e. messages which their dst address
+ * equals to the src address of their rpmsg channel), the driver's handler
+ * is invoked to process it.
+ *
+ * That said, more complicated drivers might do need to allocate
+ * additional rpmsg addresses, and bind them to different rx callbacks.
+ * To accomplish that, those drivers need to call this function.
+ *
+ * Drivers should provide their @rpdev channel (so the new endpoint would belong
+ * to the same remote processor their channel belongs to), an rx callback
+ * function, an optional private data (which is provided back when the
+ * rx callback is invoked), and an address they want to bind with the
+ * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will
+ * dynamically assign them an available rpmsg address (drivers should have
+ * a very good reason why not to always use RPMSG_ADDR_ANY here).
+ *
+ * Returns a pointer to the endpoint on success, or NULL on error.
+ */
+struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev,
+ void (*cb)(struct rpmsg_channel *, void *, int, void *, u32),
+ void *priv, u32 addr)
+{
+ return __rpmsg_create_ept(rpdev->vrp, rpdev, cb, priv, addr);
+}
+EXPORT_SYMBOL(rpmsg_create_ept);
+
+/**
+ * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
+ * @ept: endpoing to destroy
+ *
+ * Should be used by drivers to destroy an rpmsg endpoint previously
+ * created with rpmsg_create_ept().
+ */
+void rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
+{
+ struct virtproc_info *vrp = ept->rpdev->vrp;
+
+ spin_lock(&vrp->endpoints_lock);
+ idr_remove(&vrp->endpoints, ept->addr);
+ spin_unlock(&vrp->endpoints_lock);
+
+ kfree(ept);
+}
+EXPORT_SYMBOL(rpmsg_destroy_ept);
+
+/*
+ * when an rpmsg driver is probed with a channel, we seamlessly create
+ * it an endpoint, binding its rx callback to a unique local rpmsg
+ * address.
+ *
+ * if we need to, we also announce about this channel to the remote
+ * processor (needed in case the driver is exposing an rpmsg service).
+ */
+static int rpmsg_dev_probe(struct device *dev)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+ struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
+ struct virtproc_info *vrp = rpdev->vrp;
+ struct rpmsg_endpoint *ept;
+ int err;
+
+ ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, rpdev->src);
+ if (!ept) {
+ dev_err(dev, "failed to create endpoint\n");
+ err = -ENOMEM;
+ goto out;
+ }
+
+ rpdev->ept = ept;
+ rpdev->src = ept->addr;
+
+ err = rpdrv->probe(rpdev);
+ if (err) {
+ dev_err(dev, "%s: failed: %d\n", __func__, err);
+ rpmsg_destroy_ept(ept);
+ goto out;
+ }
+
+ /* need to tell remote processor's name service about this channel ? */
+ if (rpdev->announce &&
+ virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) {
+ struct rpmsg_ns_msg nsm;
+
+ strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
+ nsm.addr = rpdev->src;
+ nsm.flags = RPMSG_NS_CREATE;
+
+ err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR);
+ if (err)
+ dev_err(dev, "failed to announce service %d\n", err);
+ }
+
+out:
+ return err;
+}
+
+static int rpmsg_dev_remove(struct device *dev)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+ struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
+ struct virtproc_info *vrp = rpdev->vrp;
+ int err = 0;
+
+ /* tell remote processor's name service we're removing this channel */
+ if (rpdev->announce &&
+ virtio_has_feature(vrp->vdev, VIRTIO_RPMSG_F_NS)) {
+ struct rpmsg_ns_msg nsm;
+
+ strncpy(nsm.name, rpdev->id.name, RPMSG_NAME_SIZE);
+ nsm.addr = rpdev->src;
+ nsm.flags = RPMSG_NS_DESTROY;
+
+ err = rpmsg_sendto(rpdev, &nsm, sizeof(nsm), RPMSG_NS_ADDR);
+ if (err)
+ dev_err(dev, "failed to announce service %d\n", err);
+ }
+
+ rpdrv->remove(rpdev);
+
+ rpmsg_destroy_ept(rpdev->ept);
+
+ return err;
+}
+
+static struct bus_type rpmsg_bus = {
+ .name = "rpmsg",
+ .match = rpmsg_dev_match,
+ .dev_attrs = rpmsg_dev_attrs,
+ .uevent = rpmsg_uevent,
+ .probe = rpmsg_dev_probe,
+ .remove = rpmsg_dev_remove,
+};
+
+/**
+ * register_rpmsg_driver() - register an rpmsg driver with the rpmsg bus
+ * @rpdrv: pointer to a struct rpmsg_driver
+ *
+ * Returns 0 on success, and an appropriate error value on failure.
+ */
+int register_rpmsg_driver(struct rpmsg_driver *rpdrv)
+{
+ rpdrv->drv.bus = &rpmsg_bus;
+ return driver_register(&rpdrv->drv);
+}
+EXPORT_SYMBOL(register_rpmsg_driver);
+
+/**
+ * unregister_rpmsg_driver() - unregister an rpmsg driver from the rpmsg bus
+ * @rpdrv: pointer to a struct rpmsg_driver
+ *
+ * Returns 0 on success, and an appropriate error value on failure.
+ */
+void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv)
+{
+ driver_unregister(&rpdrv->drv);
+}
+EXPORT_SYMBOL(unregister_rpmsg_driver);
+
+static void rpmsg_release_device(struct device *dev)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+
+ kfree(rpdev);
+}
+
+/*
+ * match an rpmsg channel with a channel info struct.
+ * this is used to make sure we're not creating rpmsg devices for channels
+ * that already exist.
+ */
+static int rpmsg_channel_match(struct device *dev, void *data)
+{
+ struct rpmsg_channel_info *chinfo = data;
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+
+ if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src)
+ return 0;
+
+ if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst)
+ return 0;
+
+ if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE))
+ return 0;
+
+ /* found a match ! */
+ return 1;
+}
+
+/*
+ * create an rpmsg channel using its name and address info.
+ * this function will be used to create both static and dynamic
+ * channels.
+ */
+static struct rpmsg_channel *rpmsg_create_channel(struct virtproc_info *vrp,
+ struct rpmsg_channel_info *chinfo)
+{
+ struct rpmsg_channel *rpdev;
+ struct device *tmp, *dev = &vrp->vdev->dev;
+ int ret;
+
+ /* make sure a similar channel doesn't already exist */
+ tmp = device_find_child(dev, chinfo, rpmsg_channel_match);
+ if (tmp) {
+ /* decrement the matched device's refcount back */
+ put_device(tmp);
+ dev_err(dev, "channel %s:%x:%x already exist\n",
+ chinfo->name, chinfo->src, chinfo->dst);
+ return NULL;
+ }
+
+ rpdev = kzalloc(sizeof(struct rpmsg_channel), GFP_KERNEL);
+ if (!rpdev) {
+ pr_err("kzalloc failed\n");
+ return NULL;
+ }
+
+ rpdev->vrp = vrp;
+ rpdev->src = chinfo->src;
+ rpdev->dst = chinfo->dst;
+
+ /*
+ * rpmsg server channels has predefined local address (for now),
+ * and their existence needs to be announced remotely
+ */
+ rpdev->announce = rpdev->src != RPMSG_ADDR_ANY ? true : false;
+
+ strncpy(rpdev->id.name, chinfo->name, RPMSG_NAME_SIZE);
+
+ /* very simple device indexing plumbing which is enough for now */
+ dev_set_name(&rpdev->dev, "rpmsg%d", rpmsg_dev_index++);
+
+ rpdev->dev.parent = &vrp->vdev->dev;
+ rpdev->dev.bus = &rpmsg_bus;
+ rpdev->dev.release = rpmsg_release_device;
+
+ ret = device_register(&rpdev->dev);
+ if (ret) {
+ dev_err(dev, "device_register failed: %d\n", ret);
+ kfree(rpdev);
+ return NULL;
+ }
+
+ return rpdev;
+}
+
+/*
+ * find an existing channel using its name + address properties,
+ * and destroy it
+ */
+static int rpmsg_destroy_channel(struct virtproc_info *vrp,
+ struct rpmsg_channel_info *chinfo)
+{
+ struct virtio_device *vdev = vrp->vdev;
+ struct device *dev;
+
+ dev = device_find_child(&vdev->dev, chinfo, rpmsg_channel_match);
+ if (!dev)
+ return -EINVAL;
+
+ device_unregister(dev);
+
+ put_device(dev);
+
+ kfree(to_rpmsg_channel(dev));
+
+ return 0;
+}
+
+/* super simple buffer "allocator" that is just enough for now */
+static void *get_a_tx_buf(struct virtproc_info *vrp)
+{
+ unsigned int len;
+ void *ret;
+
+ /* support multiple concurrent senders */
+ spin_lock(&vrp->tx_lock);
+
+ /*
+ * either pick the next unused tx buffer
+ * (half of our buffers are used for sending messages)
+ */
+ if (vrp->last_sbuf < vrp->num_bufs / 2)
+ ret = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++;
+ /* or recycle a used one */
+ else
+ ret = virtqueue_get_buf(vrp->svq, &len);
+
+ spin_unlock(&vrp->tx_lock);
+
+ return ret;
+}
+
+/**
+ * rpmsg_upref_sleepers() - enable "tx-complete" interrupts, if needed
+ * @vrp: virtual remote processor state
+ *
+ * This function is called before a sender is blocked, waiting for
+ * a tx buffer to become available.
+ *
+ * If we already have blocking senders, this function merely increases
+ * the "sleepers" reference count, and exits.
+ *
+ * Otherwise, if this is the first sender to block, we also enable
+ * virtio's tx callbacks, so we'd be immediately notified when a tx
+ * buffer is consumed (we rely on virtio's tx callback in order
+ * to wake up sleeping senders as soon as a tx buffer is used by the
+ * remote processor).
+ */
+static void rpmsg_upref_sleepers(struct virtproc_info *vrp)
+{
+ /* support multiple concurrent senders */
+ spin_lock(&vrp->tx_lock);
+
+ /* are we the first sleeping context waiting for tx buffers ? */
+ if (!vrp->sleepers++)
+ /* enable "tx-complete" interrupts before dozing off */
+ virtqueue_enable_cb(vrp->svq);
+
+ spin_unlock(&vrp->tx_lock);
+}
+
+/**
+ * rpmsg_downref_sleepers() - disable "tx-complete" interrupts, if needed
+ * @vrp: virtual remote processor state
+ *
+ * This function is called after a sender, that waited for a tx buffer
+ * to become available, is unblocked.
+ *
+ * If we still have blocking senders, this function merely decreases
+ * the "sleepers" reference count, and exits.
+ *
+ * Otherwise, if there are no more blocking senders, we also disable
+ * virtio's tx callbacks, to avoid the overhead incurred with handling
+ * those (now redundant) interrupts.
+ */
+static void rpmsg_downref_sleepers(struct virtproc_info *vrp)
+{
+ /* support multiple concurrent senders */
+ spin_lock(&vrp->tx_lock);
+
+ /* are we the last sleeping context waiting for tx buffers ? */
+ if (!--vrp->sleepers)
+ /* disable "tx-complete" interrupts */
+ virtqueue_disable_cb(vrp->svq);
+
+ spin_unlock(&vrp->tx_lock);
+}
+
+/**
+ * rpmsg_send_offchannel_raw() - send a message across to the remote processor
+ * @rpdev: the rpmsg channel
+ * @src: source address
+ * @dst: destination address
+ * @data: payload of message
+ * @len: length of payload
+ * @wait: indicates whether caller should block in case no TX buffers available
+ *
+ * This function is the base implementation for all of the rpmsg sending API.
+ *
+ * It will send @data of length @len to @dst, and say it's from @src. The
+ * message will be sent to the remote processor which the @rpdev channel
+ * belongs to.
+ *
+ * The message is sent using one of the TX buffers that are available for
+ * communication with this remote processor.
+ *
+ * If @wait is true, the caller will be blocked until either a TX buffer is
+ * available, or 15 seconds elapses (we don't want callers to
+ * sleep indefinitely due to misbehaving remote processors), and in that
+ * case -ERESTARTSYS is returned. The number '15' itself was picked
+ * arbitrarily; there's little point in asking drivers to provide a timeout
+ * value themselves.
+ *
+ * Otherwise, if @wait is false, and there are no TX buffers available,
+ * the function will immediately fail, and -ENOMEM will be returned.
+ *
+ * Normally drivers shouldn't use this function directly; instead, drivers
+ * should use the appropriate rpmsg_{try}send{to, _offchannel} API
+ * (see include/linux/rpmsg.h).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+int rpmsg_send_offchannel_raw(struct rpmsg_channel *rpdev, u32 src, u32 dst,
+ void *data, int len, bool wait)
+{
+ struct virtproc_info *vrp = rpdev->vrp;
+ struct device *dev = &rpdev->dev;
+ struct scatterlist sg;
+ struct rpmsg_hdr *msg;
+ int err;
+ unsigned long offset;
+ phys_addr_t phys_addr;
+
+ if (src == RPMSG_ADDR_ANY || dst == RPMSG_ADDR_ANY) {
+ dev_err(dev, "invalid addr (src 0x%x, dst 0x%x)\n", src, dst);
+ return -EINVAL;
+ }
+
+ /*
+ * The size of the payload is currently limited according to the
+ * buffers size picked by the platform.
+ *
+ * One of the possible improvements here is either to support
+ * user-provided buffers (and then we can also support zero-copy
+ * messaging), or to improve the buffer allocator, to support
+ * variable-length buffer sizes.
+ */
+ if (len > vrp->buf_size - sizeof(struct rpmsg_hdr)) {
+ dev_err(dev, "message is too big (%d)\n", len);
+ return -EMSGSIZE;
+ }
+
+ /* grab a buffer */
+ msg = get_a_tx_buf(vrp);
+ if (!msg && !wait)
+ return -ENOMEM;
+
+ /* no free buffer ? wait for one (but bail after 15 seconds) */
+ while (!msg) {
+ /* enable "tx-complete" interrupts, if not already enabled */
+ rpmsg_upref_sleepers(vrp);
+
+ /*
+ * sleep until a free buffer is available or 15 secs elapse.
+ * the timeout period is not configurable because there's
+ * little point in asking drivers to specify that.
+ * if later this happens to be required, it'd be easy to add.
+ */
+ err = wait_event_interruptible_timeout(vrp->sendq,
+ (msg = get_a_tx_buf(vrp)),
+ msecs_to_jiffies(15000));
+
+ /* disable "tx-complete" interrupts if we're the last sleeper */
+ rpmsg_downref_sleepers(vrp);
+
+ /* timeout ? */
+ if (!err) {
+ dev_err(dev, "timeout waiting for a tx buffer\n");
+ return -ERESTARTSYS;
+ }
+ }
+
+ msg->len = len;
+ msg->flags = 0;
+ msg->src = src;
+ msg->dst = dst;
+ msg->reserved = 0;
+ memcpy(msg->data, data, len);
+
+ dev_dbg(dev, "TX From 0x%x, To 0x%x, Len %d, Flags %d, Reserved %d\n",
+ msg->src, msg->dst, msg->len,
+ msg->flags, msg->reserved);
+ print_hex_dump(KERN_DEBUG, "rpmsg_virtio TX: ", DUMP_PREFIX_NONE, 16, 1,
+ msg, sizeof(*msg) + msg->len, true);
+
+ offset = ((unsigned long) msg) - ((unsigned long) vrp->rbufs);
+
+ phys_addr = vrp->phys_base + offset;
+
+ sg_init_table(&sg, 1);
+
+ /*
+ * can't use sg_set_buf because buffers might not be residing
+ * in virt_to_page()-able memory.
+ * revisit this to achieve a cleaner solution (e.g. DMA API).
+ */
+ sg_set_page(&sg, phys_to_page(phys_addr), sizeof(*msg) + len,
+ offset_in_page(phys_addr));
+
+ spin_lock(&vrp->tx_lock);
+
+ /* add message to the remote processor's virtqueue */
+ err = virtqueue_add_buf_gfp(vrp->svq, &sg, 1, 0, msg, GFP_KERNEL);
+ if (err < 0) {
+ /*
+ * need to reclaim the buffer here, otherwise it's lost
+ * (memory won't leak, but rpmsg won't use it again for TX).
+ * this will wait for a buffer management overhaul.
+ */
+ dev_err(dev, "virtqueue_add_buf_gfp failed: %d\n", err);
+ goto out;
+ }
+
+ /* tell the remote processor it has a pending message to read */
+ virtqueue_kick(vrp->svq);
+
+ err = 0;
+out:
+ spin_unlock(&vrp->tx_lock);
+ return err;
+}
+EXPORT_SYMBOL(rpmsg_send_offchannel_raw);
+
+/* called when an rx buffer is used, and it's time to digest a message */
+static void rpmsg_recv_done(struct virtqueue *rvq)
+{
+ struct rpmsg_hdr *msg;
+ unsigned int len;
+ struct rpmsg_endpoint *ept;
+ struct scatterlist sg;
+ unsigned long offset;
+ phys_addr_t phys_addr;
+ struct virtproc_info *vrp = rvq->vdev->priv;
+ struct device *dev = &rvq->vdev->dev;
+ int err;
+
+ msg = virtqueue_get_buf(rvq, &len);
+ if (!msg) {
+ dev_err(dev, "uhm, incoming signal, but no used buffer ?\n");
+ return;
+ }
+
+ dev_dbg(dev, "From: 0x%x, To: 0x%x, Len: %d, Flags: %d, Reserved: %d\n",
+ msg->src, msg->dst, msg->len,
+ msg->flags, msg->reserved);
+ print_hex_dump(KERN_DEBUG, "rpmsg_virtio RX: ", DUMP_PREFIX_NONE, 16, 1,
+ msg, sizeof(*msg) + msg->len, true);
+
+ /* use the dst addr to fetch the callback of the appropriate user */
+ spin_lock(&vrp->endpoints_lock);
+ ept = idr_find(&vrp->endpoints, msg->dst);
+ spin_unlock(&vrp->endpoints_lock);
+
+ if (ept && ept->cb)
+ ept->cb(ept->rpdev, msg->data, msg->len, ept->priv, msg->src);
+ else
+ dev_warn(dev, "msg received with no recepient\n");
+
+ /* add the buffer back to the remote processor's virtqueue */
+ offset = ((unsigned long) msg) - ((unsigned long) vrp->rbufs);
+ phys_addr = vrp->phys_base + offset;
+
+ sg_init_table(&sg, 1);
+
+ /*
+ * can't use sg_set_buf because buffers might not be residing
+ * in virt_to_page()-able memory.
+ * revisit this to achieve a cleaner solution.
+ */
+ sg_set_page(&sg, phys_to_page(phys_addr), sizeof(*msg) + len,
+ offset_in_page(phys_addr));
+
+ err = virtqueue_add_buf_gfp(vrp->rvq, &sg, 0, 1, msg, GFP_KERNEL);
+ if (err < 0) {
+ dev_err(dev, "failed to add a virtqueue buffer: %d\n", err);
+ return;
+ }
+
+ /* tell the remote processor we added another available rx buffer */
+ virtqueue_kick(vrp->rvq);
+}
+
+/*
+ * This is invoked whenever the remote processor completed processing
+ * a TX msg we just sent it, and the buffer is put back to the used ring.
+ *
+ * Normally, though, we suppress this "tx complete" interrupt in order to
+ * avoid the incurred overhead.
+ */
+static void rpmsg_xmit_done(struct virtqueue *svq)
+{
+ struct virtproc_info *vrp = svq->vdev->priv;
+
+ dev_dbg(&svq->vdev->dev, "%s\n", __func__);
+
+ /* wake up potential senders that are waiting for a tx buffer */
+ wake_up_interruptible(&vrp->sendq);
+}
+
+/* invoked when a name service announcement arrives */
+static void rpmsg_ns_cb(struct rpmsg_channel *rpdev, void *data, int len,
+ void *priv, u32 src)
+{
+ struct rpmsg_ns_msg *msg = data;
+ struct rpmsg_channel *newch;
+ struct rpmsg_channel_info chinfo;
+ struct virtproc_info *vrp = priv;
+ struct device *dev = &vrp->vdev->dev;
+ int ret;
+
+ print_hex_dump(KERN_DEBUG, "NS announcement: ",
+ DUMP_PREFIX_NONE, 16, 1,
+ data, len, true);
+
+ if (len != sizeof(*msg)) {
+ dev_err(dev, "malformed ns msg (%d)\n", len);
+ return;
+ }
+
+ /*
+ * the name service ept does _not_ belong to a real rpmsg channel,
+ * and is handled by the rpmsg bus itself.
+ * for sanity reasons, make sure a valid rpdev has _not_ sneaked
+ * in somehow.
+ */
+ if (rpdev) {
+ dev_err(dev, "anomaly: ns ept has an rpdev handle\n");
+ return;
+ }
+
+ /* don't trust the remote processor for null terminating the name */
+ msg->name[RPMSG_NAME_SIZE - 1] = '\0';
+
+ dev_info(dev, "%sing channel %s addr 0x%x\n",
+ msg->flags & RPMSG_NS_DESTROY ? "destroy" : "creat",
+ msg->name, msg->addr);
+
+ strncpy(chinfo.name, msg->name, sizeof(chinfo.name));
+ chinfo.src = RPMSG_ADDR_ANY;
+ chinfo.dst = msg->addr;
+
+ if (msg->flags & RPMSG_NS_DESTROY) {
+ ret = rpmsg_destroy_channel(vrp, &chinfo);
+ if (ret)
+ dev_err(dev, "rpmsg_destroy_channel failed: %d\n", ret);
+ } else {
+ newch = rpmsg_create_channel(vrp, &chinfo);
+ if (!newch)
+ dev_err(dev, "rpmsg_create_channel failed\n");
+ }
+}
+
+static int rpmsg_probe(struct virtio_device *vdev)
+{
+ vq_callback_t *vq_cbs[] = { rpmsg_recv_done, rpmsg_xmit_done };
+ const char *names[] = { "input", "output" };
+ struct virtqueue *vqs[2];
+ struct virtproc_info *vrp;
+ void *addr;
+ int err, i, num_bufs, buf_size, total_buf_size;
+ struct rpmsg_channel_info *ch;
+
+ vrp = kzalloc(sizeof(*vrp), GFP_KERNEL);
+ if (!vrp)
+ return -ENOMEM;
+
+ vrp->vdev = vdev;
+
+ idr_init(&vrp->endpoints);
+ spin_lock_init(&vrp->endpoints_lock);
+ spin_lock_init(&vrp->tx_lock);
+ init_waitqueue_head(&vrp->sendq);
+
+ /* We expect two virtqueues, rx and tx (and in this order) */
+ err = vdev->config->find_vqs(vdev, 2, vqs, vq_cbs, names);
+ if (err)
+ goto free_vi;
+
+ vrp->rvq = vqs[0];
+ vrp->svq = vqs[1];
+
+ /* Platform must supply pre-allocated uncached buffers for now */
+ vdev->config->get(vdev, VPROC_BUF_ADDR, &addr, sizeof(addr));
+ vdev->config->get(vdev, VPROC_BUF_NUM, &num_bufs, sizeof(num_bufs));
+ vdev->config->get(vdev, VPROC_BUF_SZ, &buf_size, sizeof(buf_size));
+ vdev->config->get(vdev, VPROC_BUF_PADDR, &vrp->phys_base,
+ sizeof(vrp->phys_base));
+
+ total_buf_size = num_bufs * buf_size;
+
+ dev_dbg(&vdev->dev, "%d buffers, size %d, addr 0x%x, total 0x%x\n",
+ num_bufs, buf_size, (unsigned int) addr, total_buf_size);
+
+ vrp->num_bufs = num_bufs;
+ vrp->buf_size = buf_size;
+
+ /* the first half of the buffers is dedicated for RX */
+ vrp->rbufs = addr;
+
+ /* the second half of the buffers is dedicated for TX */
+ vrp->sbufs = addr + total_buf_size / 2;
+
+ /* set up the receive buffers */
+ for (i = 0; i < num_bufs / 2; i++) {
+ struct scatterlist sg;
+ void *cpu_addr = vrp->rbufs + i * buf_size;
+ phys_addr_t phys_addr = vrp->phys_base + i * buf_size;
+
+ sg_init_table(&sg, 1);
+
+ /*
+ * can't use sg_set_buf because buffers might not be residing
+ * in virt_to_page()-able memory.
+ * revisit this to achieve a cleaner solution.
+ */
+ sg_set_page(&sg, phys_to_page(phys_addr), buf_size,
+ offset_in_page(phys_addr));
+
+ err = virtqueue_add_buf_gfp(vrp->rvq, &sg, 0, 1, cpu_addr,
+ GFP_KERNEL);
+ WARN_ON(err < 0); /* sanity check; this can't really happen */
+ }
+
+ /* suppress "tx-complete" interrupts */
+ virtqueue_disable_cb(vrp->svq);
+
+ vdev->priv = vrp;
+
+ /* if supported by the remote processor, enable the name service */
+ if (virtio_has_feature(vdev, VIRTIO_RPMSG_F_NS)) {
+ /* a dedicated endpoint handles the name service msgs */
+ vrp->ns_ept = __rpmsg_create_ept(vrp, NULL, rpmsg_ns_cb,
+ vrp, RPMSG_NS_ADDR);
+ if (!vrp->ns_ept) {
+ dev_err(&vdev->dev, "failed to create the ns ept\n");
+ err = -ENOMEM;
+ goto vqs_del;
+ }
+ }
+
+ /* tell the remote processor it can start sending messages */
+ virtqueue_kick(vrp->rvq);
+
+ /* do we have a platform-specific static channels table ? */
+ vdev->config->get(vdev, VPROC_STATIC_CHANNELS, &ch, sizeof(ch));
+
+ /* if we do, create the appropriate channels */
+ for (i = 0; ch && ch[i].name[0]; i++)
+ rpmsg_create_channel(vrp, &ch[i]);
+
+ dev_info(&vdev->dev, "rpmsg backend virtproc probed successfully\n");
+
+ return 0;
+
+vqs_del:
+ vdev->config->del_vqs(vrp->vdev);
+free_vi:
+ kfree(vrp);
+ return err;
+}
+
+static int rpmsg_remove_device(struct device *dev, void *data)
+{
+ struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
+
+ device_unregister(dev);
+
+ kfree(rpdev);
+
+ return 0;
+}
+
+static void __devexit rpmsg_remove(struct virtio_device *vdev)
+{
+ struct virtproc_info *vrp = vdev->priv;
+ int ret;
+
+ ret = device_for_each_child(&vdev->dev, NULL, rpmsg_remove_device);
+ if (ret)
+ dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret);
+
+ idr_remove_all(&vrp->endpoints);
+ idr_destroy(&vrp->endpoints);
+
+ vdev->config->del_vqs(vrp->vdev);
+
+ kfree(vrp);
+}
+
+static struct virtio_device_id id_table[] = {
+ { VIRTIO_ID_RPMSG, VIRTIO_DEV_ANY_ID },
+ { 0 },
+};
+
+static unsigned int features[] = {
+ VIRTIO_RPMSG_F_NS,
+};
+
+static struct virtio_driver virtio_ipc_driver = {
+ .feature_table = features,
+ .feature_table_size = ARRAY_SIZE(features),
+ .driver.name = KBUILD_MODNAME,
+ .driver.owner = THIS_MODULE,
+ .id_table = id_table,
+ .probe = rpmsg_probe,
+ .remove = __devexit_p(rpmsg_remove),
+};
+
+static int __init init(void)
+{
+ int ret;
+
+ ret = bus_register(&rpmsg_bus);
+ if (ret) {
+ pr_err("failed to register rpmsg bus: %d\n", ret);
+ return ret;
+ }
+
+ return register_virtio_driver(&virtio_ipc_driver);
+}
+module_init(init);
+
+static void __exit fini(void)
+{
+ unregister_virtio_driver(&virtio_ipc_driver);
+ bus_unregister(&rpmsg_bus);
+}
+module_exit(fini);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio-based remote processor messaging bus");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
index ae28e93..561567e 100644
--- a/include/linux/mod_devicetable.h
+++ b/include/linux/mod_devicetable.h
@@ -533,4 +533,14 @@ struct isapnp_device_id {
kernel_ulong_t driver_data; /* data private to the driver */
};
+/* rpmsg */
+
+#define RPMSG_NAME_SIZE 32
+#define RPMSG_DEVICE_MODALIAS_FMT "rpmsg:%s"
+
+struct rpmsg_device_id {
+ char name[RPMSG_NAME_SIZE];
+ kernel_ulong_t driver_data /* Data private to the driver */
+ __attribute__((aligned(sizeof(kernel_ulong_t))));
+};
#endif /* LINUX_MOD_DEVICETABLE_H */
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
new file mode 100644
index 0000000..73f9231
--- /dev/null
+++ b/include/linux/rpmsg.h
@@ -0,0 +1,421 @@
+/*
+ * Remote processor messaging
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _LINUX_RPMSG_H
+#define _LINUX_RPMSG_H
+
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/mod_devicetable.h>
+
+/* The feature bitmap for virtio rpmsg */
+#define VIRTIO_RPMSG_F_NS 0 /* RP supports name service notifications */
+
+/**
+ * struct rpmsg_hdr - common header for all rpmsg messages
+ * @src: source address
+ * @dst: destination address
+ * @reserved: reserved for future use
+ * @len: length of payload (in bytes)
+ * @flags: message flags
+ * @data: @len bytes of message payload data
+ *
+ * Every message sent(/received) on the rpmsg bus begins with this header.
+ */
+struct rpmsg_hdr {
+ u32 src;
+ u32 dst;
+ u32 reserved;
+ u16 len;
+ u16 flags;
+ u8 data[0];
+} __packed;
+
+/**
+ * struct rpmsg_ns_msg - dynamic name service announcement message
+ * @name: name of remote service that is published
+ * @addr: address of remote service that is published
+ * @flags: indicates whether service is created or destroyed
+ *
+ * This message is sent across to publish a new service (or announce
+ * about its removal). When Linux receives these messages, an appropriate
+ * rpmsg channel (i.e device) is created/destroyed. In turn, the ->probe()
+ * or ->remove() handler of the appropriate rpmsg driver will be invoked
+ * (if/as-soon-as one is registered).
+ */
+struct rpmsg_ns_msg {
+ char name[RPMSG_NAME_SIZE];
+ u32 addr;
+ u32 flags;
+} __packed;
+
+/**
+ * struct rpmsg_ns_flags - dynamic name service announcement flags
+ *
+ * @RPMSG_NS_CREATE: a new remote service was just created
+ * @RPMSG_NS_DESTROY: a known remote service was just destroyed
+ */
+enum rpmsg_ns_flags {
+ RPMSG_NS_CREATE = 0,
+ RPMSG_NS_DESTROY = 1,
+};
+
+/**
+ * enum rpmsg_platform_requests - platform-specific rpmsg configuration requests
+ *
+ * These configuration requests are required by the rpmsg bus, and must be
+ * implemented by the platform-specific rpmsg backend.
+ *
+ * @VPROC_BUF_ADDR: Kernel virtual address of an uncached memory region,
+ * shared with the remote processor, that will be splitted
+ * to buffers and then used to send messages across to the
+ * remote processor. Those buffers will be added to the
+ * appropriate vrings by the rpmsg bus in order to send and
+ * receive messages.
+ *
+ * @VPROC_BUF_PADDR: Physical address of the above memory region, needed
+ * for the "wire" protocol between the two processors
+ * (i.e. for the vring's buffer descriptors).
+ *
+ * @VPROC_BUF_NUM: Number of buffers to split the shared memory region to
+ * @VPROC_BUF_SZ: Size of each buffer that the shared memory region will be
+ * split into. It is the responsibility of the underlying
+ * implementation to make sure that the size of the memory
+ * region provided by @VPROC_BUF_ADDR is exactly
+ * @VPROC_BUF_NUM * @VPROC_BUF_SZ bytes.
+ *
+ * @VPROC_STATIC_CHANNELS: Table of static channels that this platform
+ * expects to have. See struct rpmsg_channel_info
+ * for additional information.
+ * This configuration is optional: it is perfectly
+ * fine not to have any pre-configured static channels.
+ *
+ * The number and size of buffers to use are considered platform-specific,
+ * because this is strongly tied with the performance/functionality
+ * requirements of the specific use cases that the platform needs rpmsg
+ * for. This should be revisited when we'll have a bigger requirement
+ * picture.
+ *
+ * We might also want to add support for user-provided buffers. This will
+ * allow bigger buffer size flexibility, and might also be used to achieve
+ * zero-copy messaging.
+ */
+enum rpmsg_platform_requests {
+ VPROC_BUF_ADDR,
+ VPROC_BUF_PADDR,
+ VPROC_BUF_NUM,
+ VPROC_BUF_SZ,
+ VPROC_STATIC_CHANNELS,
+};
+
+#define RPMSG_ADDR_ANY 0xFFFFFFFF
+
+struct virtproc_info;
+
+/**
+ * rpmsg_channel - the rpmsg bus devices are called channels
+ * @vrp: the remote processor this channel belongs to
+ * @dev: the device struct
+ * @id: device id (used to match between rpmsg drivers and devices)
+ * @src: local address
+ * @dst: destination address
+ * @ept: the rpmsg endpoint of this channel
+ * @announce: if set, rpmsg will announce the creation/removal of this channel
+ */
+struct rpmsg_channel {
+ struct virtproc_info *vrp;
+ struct device dev;
+ struct rpmsg_device_id id;
+ u32 src;
+ u32 dst;
+ struct rpmsg_endpoint *ept;
+ bool announce;
+};
+
+/**
+ * struct rpmsg_channel_info - static channel info
+ * @name: name of service
+ * @src: local address
+ * @dst: destination address
+ *
+ * This struct is used to define static channel information, namely name
+ * and addresses, which is used by platform-specific code to create static
+ * channels (i.e. channels that always exists).
+ *
+ * Use cases of static channels are:
+ *
+ * 1. In case the remote processor does not support dynamic name service
+ * announcements (i.e. remote channel creation/removal).
+ * In this case, we predefine a static channel table which contains the
+ * remote rpmsg services supported by the remote processor. The rpmsg
+ * bus then iterates through this table, and creates rpmsg channels
+ * (i.e. devices) accordingly. @dst will contain the rpmsg address
+ * of the remote service, and @src will most likely contain RPMSG_ADDR_ANY.
+ *
+ * 2. To define channels which will be probed with local rpmsg "server"
+ * driver (i.e. drivers that expose a service). In this case, @src will
+ * contain the local rpmsg address of the service (but this can made
+ * dynamic too, if remote processor supports listening to our own dynamic
+ * name service announcements), and @dst will most likely be RPMSG_ADDR_ANY.
+ *
+ * Use the RPMSG_REMOTE_CHNL and RMSG_SERVER_CHNL macros (see below)
+ * to populate the static channels table for the above two use cases.
+ */
+struct rpmsg_channel_info {
+ char name[RPMSG_NAME_SIZE];
+ u32 src;
+ u32 dst;
+};
+
+/*
+ * rpmsg server channels have a local predefined address, and accept
+ * any remote address
+ */
+#define RMSG_SERVER_CHNL(name, addr) { name, addr, RPMSG_ADDR_ANY }
+
+/*
+ * rpmsg remote service channels have a remote predefined address, and
+ * don't care about the local address
+ */
+#define RMSG_REMOTE_CHNL(name, addr) { name, RPMSG_ADDR_ANY, addr }
+
+/**
+ * struct rpmsg_endpoint - binds a local rpmsg address to its user
+ * @rpdev: rpmsg channel device
+ * @cb: rx callback handler
+ * @addr: local rpmsg address
+ * @priv: private data for the driver's use
+ *
+ * In essence, an rpmsg endpoint represents a listener on the rpmsg bus, as
+ * it binds together an rpmsg address with an rx callback handler.
+ *
+ * Simple rpmsg drivers shouldn't be aware of this detail, because
+ * things just work: every rpmsg driver provides an rx callback upon
+ * registering to the bus, and that callback is then bound to its rpmsg
+ * address when the driver is probed. When relevant inbound messages arrive
+ * (i.e. messages which their dst address equals to the src address of
+ * the rpmsg channel), the driver's handler is invoked to process it.
+ *
+ * More complicated drivers though, that do need to allocate additional rpmsg
+ * addresses, and bind them to different rx callbacks, must explicitly
+ * create themselves additional endpoints (see rpmsg_create_ept()).
+ */
+struct rpmsg_endpoint {
+ struct rpmsg_channel *rpdev;
+ void (*cb)(struct rpmsg_channel *, void *, int, void *, u32);
+ u32 addr;
+ void *priv;
+};
+
+/**
+ * struct rpmsg_driver - rpmsg driver struct
+ * @drv: underlying device driver
+ * @id_table: rpmsg ids serviced by this driver
+ * @probe: invoked when a matching rpmsg channel (i.e. device) is found
+ * @remove: invoked when the rpmsg channel is removed
+ * @callback: invoked when an inbound message is received on the channel
+ */
+struct rpmsg_driver {
+ struct device_driver drv;
+ const struct rpmsg_device_id *id_table;
+ int (*probe)(struct rpmsg_channel *dev);
+ void (*remove)(struct rpmsg_channel *dev);
+ void (*callback)(struct rpmsg_channel *, void *, int, void *, u32);
+};
+
+int register_rpmsg_device(struct rpmsg_channel *dev);
+void unregister_rpmsg_device(struct rpmsg_channel *dev);
+int register_rpmsg_driver(struct rpmsg_driver *drv);
+void unregister_rpmsg_driver(struct rpmsg_driver *drv);
+void rpmsg_destroy_ept(struct rpmsg_endpoint *);
+struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *,
+ void (*cb)(struct rpmsg_channel *, void *, int, void *, u32),
+ void *priv, u32 addr);
+
+int
+rpmsg_send_offchannel_raw(struct rpmsg_channel *, u32, u32, void *, int, bool);
+
+/**
+ * rpmsg_send() - send a message across to the remote processor
+ * @rpdev: the rpmsg channel
+ * @data: payload of message
+ * @len: length of payload
+ *
+ * This function sends @data of length @len on the @rpdev channel.
+ * The message will be sent to the remote processor which the @rpdev
+ * channel belongs to, using @rpdev's source and destination addresses.
+ * In case there are no TX buffers available, the function will block until
+ * one becomes available, or a timeout of 15 seconds elapses. When the latter
+ * happens, -ERESTARTSYS is returned.
+ *
+ * Can only be called from process context (for now).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+static inline int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len)
+{
+ u32 src = rpdev->src, dst = rpdev->dst;
+
+ return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true);
+}
+
+/**
+ * rpmsg_sendto() - send a message across to the remote processor, specify dst
+ * @rpdev: the rpmsg channel
+ * @data: payload of message
+ * @len: length of payload
+ * @dst: destination address
+ *
+ * This function sends @data of length @len to the remote @dst address.
+ * The message will be sent to the remote processor which the @rpdev
+ * channel belongs to, using @rpdev's source address.
+ * In case there are no TX buffers available, the function will block until
+ * one becomes available, or a timeout of 15 seconds elapses. When the latter
+ * happens, -ERESTARTSYS is returned.
+ *
+ * Can only be called from process context (for now).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+static inline
+int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst)
+{
+ u32 src = rpdev->src;
+
+ return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true);
+}
+
+/**
+ * rpmsg_send_offchannel() - send a message using explicit src/dst addresses
+ * @rpdev: the rpmsg channel
+ * @src: source address
+ * @dst: destination address
+ * @data: payload of message
+ * @len: length of payload
+ *
+ * This function sends @data of length @len to the remote @dst address,
+ * and uses @src as the source address.
+ * The message will be sent to the remote processor which the @rpdev
+ * channel belongs to.
+ * In case there are no TX buffers available, the function will block until
+ * one becomes available, or a timeout of 15 seconds elapses. When the latter
+ * happens, -ERESTARTSYS is returned.
+ *
+ * Can only be called from process context (for now).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+static inline
+int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst,
+ void *data, int len)
+{
+ return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, true);
+}
+
+/**
+ * rpmsg_send() - send a message across to the remote processor
+ * @rpdev: the rpmsg channel
+ * @data: payload of message
+ * @len: length of payload
+ *
+ * This function sends @data of length @len on the @rpdev channel.
+ * The message will be sent to the remote processor which the @rpdev
+ * channel belongs to, using @rpdev's source and destination addresses.
+ * In case there are no TX buffers available, the function will immediately
+ * return -ENOMEM without waiting until one becomes available.
+ *
+ * Can only be called from process context (for now).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+static inline
+int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len)
+{
+ u32 src = rpdev->src, dst = rpdev->dst;
+
+ return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false);
+}
+
+/**
+ * rpmsg_sendto() - send a message across to the remote processor, specify dst
+ * @rpdev: the rpmsg channel
+ * @data: payload of message
+ * @len: length of payload
+ * @dst: destination address
+ *
+ * This function sends @data of length @len to the remote @dst address.
+ * The message will be sent to the remote processor which the @rpdev
+ * channel belongs to, using @rpdev's source address.
+ * In case there are no TX buffers available, the function will immediately
+ * return -ENOMEM without waiting until one becomes available.
+ *
+ * Can only be called from process context (for now).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+static inline
+int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst)
+{
+ u32 src = rpdev->src;
+
+ return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false);
+}
+
+/**
+ * rpmsg_send_offchannel() - send a message using explicit src/dst addresses
+ * @rpdev: the rpmsg channel
+ * @src: source address
+ * @dst: destination address
+ * @data: payload of message
+ * @len: length of payload
+ *
+ * This function sends @data of length @len to the remote @dst address,
+ * and uses @src as the source address.
+ * The message will be sent to the remote processor which the @rpdev
+ * channel belongs to.
+ * In case there are no TX buffers available, the function will immediately
+ * return -ENOMEM without waiting until one becomes available.
+ *
+ * Can only be called from process context (for now).
+ *
+ * Returns 0 on success and an appropriate error value on failure.
+ */
+static inline
+int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst,
+ void *data, int len)
+{
+ return rpmsg_send_offchannel_raw(rpdev, src, dst, data, len, false);
+}
+
+#endif /* _LINUX_RPMSG_H */
diff --git a/include/linux/virtio_ids.h b/include/linux/virtio_ids.h
index 85bb0bb..1520c06 100644
--- a/include/linux/virtio_ids.h
+++ b/include/linux/virtio_ids.h
@@ -35,5 +35,6 @@
#define VIRTIO_ID_RNG 4 /* virtio ring */
#define VIRTIO_ID_BALLOON 5 /* virtio balloon */
#define VIRTIO_ID_9P 9 /* 9p virtio console */
+#define VIRTIO_ID_RPMSG 10 /* virtio remote processor messaging */
#endif /* _LINUX_VIRTIO_IDS_H */
--
1.7.1
Add rpmsg devices that wires virtio to the OMAP mailbox and
enabled A9<->M3 communications on OMAP4.
Each M3 core gets its own virtio device, since it is running its own
independent instance of RTOS, and is therefore treated as a completely
separate processor by rpmsg.
Both M3 cores use the same mailbox device to communicate with the A9,
and the mailbox payload is used to indicate which of the virtqueues is
triggered (each M3 core gets 2 virtqueues, for rx and tx).
Map the two vrings and the IPC buffers as noncacheable; use
dynamically carved-out physical memory for that.
Put this omap implementation in a dedicated drivers/rpmsg/host folder,
with the intention of stacking different rpmsg host implementations
in one place.
Signed-off-by: Ohad Ben-Cohen <[email protected]>
---
drivers/rpmsg/Kconfig | 2 +
drivers/rpmsg/Makefile | 2 +
drivers/rpmsg/host/Kconfig | 11 +
drivers/rpmsg/host/Makefile | 1 +
drivers/rpmsg/host/omap_rpmsg.c | 540 +++++++++++++++++++++++++++++++++++++++
drivers/rpmsg/host/omap_rpmsg.h | 69 +++++
6 files changed, 625 insertions(+), 0 deletions(-)
create mode 100644 drivers/rpmsg/host/Kconfig
create mode 100644 drivers/rpmsg/host/Makefile
create mode 100644 drivers/rpmsg/host/omap_rpmsg.c
create mode 100644 drivers/rpmsg/host/omap_rpmsg.h
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
index 41303f5..9544d62 100644
--- a/drivers/rpmsg/Kconfig
+++ b/drivers/rpmsg/Kconfig
@@ -12,3 +12,5 @@ if RPMSG
source "drivers/virtio/Kconfig"
endif # RPMSG
+
+source "drivers/rpmsg/host/Kconfig"
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
index 7617fcb..368a526 100644
--- a/drivers/rpmsg/Makefile
+++ b/drivers/rpmsg/Makefile
@@ -1 +1,3 @@
obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o
+
+obj-$(CONFIG_RPMSG) += host/
diff --git a/drivers/rpmsg/host/Kconfig b/drivers/rpmsg/host/Kconfig
new file mode 100644
index 0000000..fd2140c
--- /dev/null
+++ b/drivers/rpmsg/host/Kconfig
@@ -0,0 +1,11 @@
+config OMAP_RPMSG
+ tristate "OMAP virtio-based remote processor messaging support"
+ depends on ARCH_OMAP4 && OMAP_REMOTE_PROC
+ select CONFIG_OMAP_MBOX_FWK
+ select RPMSG
+ help
+ Say Y if you want to enable OMAP's virtio-based remote-processor
+ messaging, currently only available on OMAP4. This is required
+ for offloading cpu-intensive and/or latency-sensitive tasks to
+ the remote on-chip M3s or C64x+ dsp, usually used by multimedia
+ frameworks.
diff --git a/drivers/rpmsg/host/Makefile b/drivers/rpmsg/host/Makefile
new file mode 100644
index 0000000..9823745
--- /dev/null
+++ b/drivers/rpmsg/host/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_OMAP_RPMSG) += omap_rpmsg.o
diff --git a/drivers/rpmsg/host/omap_rpmsg.c b/drivers/rpmsg/host/omap_rpmsg.c
new file mode 100644
index 0000000..b8c2305
--- /dev/null
+++ b/drivers/rpmsg/host/omap_rpmsg.c
@@ -0,0 +1,540 @@
+/*
+ * Remote processor messaging transport (OMAP platform-specific bits)
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <[email protected]>
+ * Brian Swetland <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#define pr_fmt(fmt) "%s: " fmt, __func__
+
+#include <linux/init.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_ring.h>
+#include <linux/rpmsg.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/notifier.h>
+#include <linux/remoteproc.h>
+
+#include <plat/mailbox.h>
+#include <plat/dsp.h>
+
+#include "omap_rpmsg.h"
+
+/**
+ * struct omap_rpmsg_vproc - omap's virtio remote processor state
+ * @vdev: virtio device
+ * @vring: phys address of two vrings; first one used for rx, 2nd one for tx
+ * @buf_paddr: physical address of the IPC buffer region
+ * @buf_size: size of IPC buffer region
+ * @buf_mapped: kernel (ioremap'ed) address of IPC buffer region
+ * @mbox_name: name of omap mailbox device to use with this vproc
+ * @rproc_name: name of remote proc device to use with this vproc
+ * @mbox: omap mailbox handle
+ * @rproc: remoteproc handle
+ * @nb: notifier block that will be invoked on inbound mailbox messages
+ * @vq: virtio's virtqueues
+ * @base_vq_id: index of first virtqueue that belongs to this vproc
+ * @num_of_vqs: number of virtqueues this vproc owns
+ * @static_chnls: table of static channels for this vproc
+ */
+struct omap_rpmsg_vproc {
+ struct virtio_device vdev;
+ unsigned int vring[2]; /* mpu owns first vring, ipu owns the 2nd */
+ unsigned int buf_paddr;
+ unsigned int buf_size; /* size must be page-aligned */
+ void *buf_mapped;
+ char *mbox_name;
+ char *rproc_name;
+ struct omap_mbox *mbox;
+ struct rproc *rproc;
+ struct notifier_block nb;
+ struct virtqueue *vq[2];
+ int base_vq_id;
+ int num_of_vqs;
+ struct rpmsg_channel_info *static_chnls;
+};
+
+#define to_omap_vproc(vd) container_of(vd, struct omap_rpmsg_vproc, vdev)
+
+/**
+ * struct omap_rpmsg_vq_info - virtqueue state
+ * @num: number of buffers supported by the vring
+ * @vq_id: a unique index of this virtqueue
+ * @addr: address where the vring is mapped onto
+ * @vproc: the virtual remote processor state
+ *
+ * Such a struct will be maintained for every virtqueue we're
+ * using to communicate with the remote processor
+ */
+struct omap_rpmsg_vq_info {
+ __u16 num;
+ __u16 vq_id;
+ void *addr;
+ struct omap_rpmsg_vproc *vproc;
+};
+
+/*
+ * For now, allocate 256 buffers of 512 bytes for each side. each buffer
+ * will then have 16B for the msg header and 496B for the payload.
+ * This will require a total space of 256KB for the buffers themselves, and
+ * 3 pages for every vring (the size of the vring depends on the number of
+ * buffers it supports).
+ */
+#define RPMSG_NUM_BUFS (512)
+#define RPMSG_BUF_SIZE (512)
+#define RPMSG_BUFS_SPACE (RPMSG_NUM_BUFS * RPMSG_BUF_SIZE)
+
+/*
+ * The alignment between the consumer and producer parts of the vring.
+ * Note: this is part of the "wire" protocol. If you change this, you need
+ * to update your BIOS image as well
+ */
+#define RPMSG_VRING_ALIGN (4096)
+
+/* With 256 buffers, our vring will occupy 3 pages */
+#define RPMSG_RING_SIZE ((DIV_ROUND_UP(vring_size(RPMSG_NUM_BUFS / 2, \
+ RPMSG_VRING_ALIGN), PAGE_SIZE)) * PAGE_SIZE)
+
+/* The total IPC space needed to communicate with a remote processor */
+#define RPMSG_IPC_MEM (RPMSG_BUFS_SPACE + 2 * RPMSG_RING_SIZE)
+
+/*
+ * Provide rpmsg core with platform-specific configuration.
+ * Since user data is at stake here, bugs can't be tolerated. hence
+ * the BUG_ON approach on invalid lengths.
+ *
+ * For more info on these configuration requests, see enum
+ * rpmsg_platform_requests.
+ */
+static void omap_rpmsg_get(struct virtio_device *vdev, unsigned int request,
+ void *buf, unsigned len)
+{
+ struct omap_rpmsg_vproc *vproc = to_omap_vproc(vdev);
+ int tmp;
+
+ switch (request) {
+ case VPROC_BUF_ADDR:
+ BUG_ON(len != sizeof(vproc->buf_mapped));
+ memcpy(buf, &vproc->buf_mapped, len);
+ break;
+ case VPROC_BUF_PADDR:
+ BUG_ON(len != sizeof(vproc->buf_paddr));
+ memcpy(buf, &vproc->buf_paddr, len);
+ break;
+ case VPROC_BUF_NUM:
+ BUG_ON(len != sizeof(tmp));
+ tmp = RPMSG_NUM_BUFS;
+ memcpy(buf, &tmp, len);
+ break;
+ case VPROC_BUF_SZ:
+ BUG_ON(len != sizeof(tmp));
+ tmp = RPMSG_BUF_SIZE;
+ memcpy(buf, &tmp, len);
+ break;
+ case VPROC_STATIC_CHANNELS:
+ BUG_ON(len != sizeof(vproc->static_chnls));
+ memcpy(buf, &vproc->static_chnls, len);
+ break;
+ default:
+ dev_err(&vdev->dev, "invalid request: %d\n", request);
+ }
+}
+
+/* kick the remote processor, and let it know which virtqueue to poke at */
+static void omap_rpmsg_notify(struct virtqueue *vq)
+{
+ struct omap_rpmsg_vq_info *rpvq = vq->priv;
+ int ret;
+
+ pr_debug("sending mailbox msg: %d\n", rpvq->vq_id);
+ /* send the index of the triggered virtqueue in the mailbox payload */
+ ret = omap_mbox_msg_send(rpvq->vproc->mbox, rpvq->vq_id);
+ if (ret)
+ pr_err("ugh, omap_mbox_msg_send() failed: %d\n", ret);
+}
+
+/**
+ * omap_rpmsg_mbox_callback() - inbound mailbox message handler
+ * @this: notifier block
+ * @index: unused
+ * @data: mailbox payload
+ *
+ * This handler is invoked by omap's mailbox driver whenever a mailbox
+ * message is received. Usually, the mailbox payload simply contains
+ * the index of the virtqueue that is kicked by the remote processor,
+ * and we let virtio handle it.
+ *
+ * In addition to virtqueue indices, we also have some out-of-band values
+ * that indicates different events. Those values are deliberately very
+ * big so they don't coincide with virtqueue indices. Moreover,
+ * they are rarely used, if used at all, and their necessity should
+ * be revisited.
+ */
+static int omap_rpmsg_mbox_callback(struct notifier_block *this,
+ unsigned long index, void *data)
+{
+ mbox_msg_t msg = (mbox_msg_t) data;
+ struct omap_rpmsg_vproc *vproc;
+
+ vproc = container_of(this, struct omap_rpmsg_vproc, nb);
+
+ pr_debug("mbox msg: 0x%x\n", msg);
+
+ switch (msg) {
+ case RP_MBOX_CRASH:
+ pr_err("%s has just crashed !\n", vproc->rproc_name);
+ /* todo: smarter error handling here */
+ break;
+ case RP_MBOX_ECHO_REPLY:
+ pr_info("received echo reply from %s !\n", vproc->rproc_name);
+ break;
+ case RP_MBOX_PENDING_MSG:
+ /*
+ * a new inbound message is waiting in our rx vring (1st vring).
+ * Let's pretend the message explicitly contained the rx vring
+ * index number and handle it generically.
+ */
+ msg = vproc->base_vq_id;
+ /* intentional fall-through */
+ default:
+ /* ignore vq indices which are clearly not for us */
+ if (msg < vproc->base_vq_id)
+ break;
+
+ msg -= vproc->base_vq_id;
+
+ /*
+ * Currently both PENDING_MSG and explicit-virtqueue-index
+ * messaging are supported.
+ * Whatever approach is taken, at this point 'msg' contains
+ * the index of the vring which was just triggered.
+ */
+ if (msg < vproc->num_of_vqs)
+ vring_interrupt(msg, vproc->vq[msg]);
+ }
+
+ return NOTIFY_DONE;
+}
+
+/* prepare a virtqueue */
+static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
+ unsigned index,
+ void (*callback)(struct virtqueue *vq),
+ const char *name)
+{
+ struct omap_rpmsg_vproc *vproc = to_omap_vproc(vdev);
+ struct omap_rpmsg_vq_info *rpvq;
+ struct virtqueue *vq;
+ int err;
+
+ rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL);
+ if (!rpvq)
+ return ERR_PTR(-ENOMEM);
+
+ /* ioremap'ing normal memory, so we cast away sparse's complaints */
+ rpvq->addr = (__force void *) ioremap_nocache(vproc->vring[index],
+ RPMSG_RING_SIZE);
+ if (!rpvq->addr) {
+ err = -ENOMEM;
+ goto free_rpvq;
+ }
+
+ memset(rpvq->addr, 0, RPMSG_RING_SIZE);
+
+ pr_debug("vring%d: phys 0x%x, virt 0x%x\n", index, vproc->vring[index],
+ (unsigned int) rpvq->addr);
+
+ vq = vring_new_virtqueue(RPMSG_NUM_BUFS / 2, RPMSG_VRING_ALIGN, vdev,
+ rpvq->addr, omap_rpmsg_notify, callback, name);
+ if (!vq) {
+ pr_err("vring_new_virtqueue failed\n");
+ err = -ENOMEM;
+ goto unmap_vring;
+ }
+
+ vproc->vq[index] = vq;
+ vq->priv = rpvq;
+ /* unique id for this virtqueue */
+ rpvq->vq_id = vproc->base_vq_id + index;
+ rpvq->vproc = vproc;
+
+ return vq;
+
+unmap_vring:
+ /* iounmap normal memory, so make sparse happy */
+ iounmap((__force void __iomem *) rpvq->addr);
+free_rpvq:
+ kfree(rpvq);
+ return ERR_PTR(err);
+}
+
+static void omap_rpmsg_del_vqs(struct virtio_device *vdev)
+{
+ struct virtqueue *vq, *n;
+ struct omap_rpmsg_vproc *vproc = to_omap_vproc(vdev);
+
+ if (vproc->rproc)
+ rproc_put(vproc->rproc);
+
+ if (vproc->mbox)
+ omap_mbox_put(vproc->mbox, &vproc->nb);
+
+ if (vproc->buf_mapped)
+ /* iounmap normal memory, so make sparse happy */
+ iounmap((__force void __iomem *)vproc->buf_mapped);
+
+ list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
+ struct omap_rpmsg_vq_info *rpvq = vq->priv;
+ vring_del_virtqueue(vq);
+ /* iounmap normal memory, so make sparse happy */
+ iounmap((__force void __iomem *) rpvq->addr);
+ kfree(rpvq);
+ }
+}
+
+static int omap_rpmsg_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+ struct virtqueue *vqs[],
+ vq_callback_t *callbacks[],
+ const char *names[])
+{
+ struct omap_rpmsg_vproc *vproc = to_omap_vproc(vdev);
+ int i, err;
+
+ /* we maintain two virtqueues per remote processor (for RX and TX) */
+ if (nvqs != 2)
+ return -EINVAL;
+
+ for (i = 0; i < nvqs; ++i) {
+ vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
+ if (IS_ERR(vqs[i])) {
+ err = PTR_ERR(vqs[i]);
+ goto error;
+ }
+ }
+
+ vproc->num_of_vqs = nvqs;
+
+ /* ioremap'ing normal memory, so we cast away sparse's complaints */
+ vproc->buf_mapped = (__force void *) ioremap_nocache(vproc->buf_paddr,
+ vproc->buf_size);
+ if (!vproc->buf_mapped) {
+ pr_err("ioremap failed\n");
+ err = -ENOMEM;
+ goto error;
+ }
+
+ /* for now, use mailbox's notifiers. later that can be optimized */
+ vproc->nb.notifier_call = omap_rpmsg_mbox_callback;
+ vproc->mbox = omap_mbox_get(vproc->mbox_name, &vproc->nb);
+ if (IS_ERR(vproc->mbox)) {
+ pr_err("failed to get mailbox %s\n", vproc->mbox_name);
+ err = -EINVAL;
+ goto error;
+ }
+
+ pr_debug("buf: phys 0x%x, virt 0x%x\n", vproc->buf_paddr,
+ (unsigned int) vproc->buf_mapped);
+
+ /* tell the M3 we're ready (so M3 will know we're sane) */
+ err = omap_mbox_msg_send(vproc->mbox, RP_MBOX_READY);
+ if (err) {
+ pr_err("ugh, omap_mbox_msg_send() failed: %d\n", err);
+ goto error;
+ }
+
+ /* send it the physical address of the vrings + IPC buffer */
+ err = omap_mbox_msg_send(vproc->mbox, (mbox_msg_t) vproc->buf_paddr);
+ if (err) {
+ pr_err("ugh, omap_mbox_msg_send() failed: %d\n", err);
+ goto error;
+ }
+
+ /* ping the remote processor. this is only for sanity-sake;
+ * there is no functional effect whatsoever */
+ err = omap_mbox_msg_send(vproc->mbox, RP_MBOX_ECHO_REQUEST);
+ if (err) {
+ pr_err("ugh, omap_mbox_msg_send() failed: %d\n", err);
+ goto error;
+ }
+
+ /* now load the firmware, and boot the M3 */
+ vproc->rproc = rproc_get(vproc->rproc_name);
+ if (!vproc->rproc) {
+ pr_err("failed to get rproc %s\n", vproc->rproc_name);
+ err = -EINVAL;
+ goto error;
+ }
+
+ return 0;
+
+error:
+ omap_rpmsg_del_vqs(vdev);
+ return err;
+}
+
+/*
+ * should be nice to add firmware support for these handlers.
+ * for now provide them so virtio doesn't crash
+ */
+static u8 omap_rpmsg_get_status(struct virtio_device *vdev)
+{
+ return 0;
+}
+
+static void omap_rpmsg_set_status(struct virtio_device *vdev, u8 status)
+{
+ dev_dbg(&vdev->dev, "new status: %d\n", status);
+}
+
+static void omap_rpmsg_reset(struct virtio_device *vdev)
+{
+ dev_dbg(&vdev->dev, "reset !\n");
+}
+
+static u32 omap_rpmsg_get_features(struct virtio_device *vdev)
+{
+ /* for now, use hardcoded bitmap. later this should be provided
+ * by the firmware itself */
+ return 1 << VIRTIO_RPMSG_F_NS;
+}
+
+static void omap_rpmsg_finalize_features(struct virtio_device *vdev)
+{
+ /* Give virtio_ring a chance to accept features */
+ vring_transport_features(vdev);
+}
+
+static void omap_rpmsg_vproc_release(struct device *dev)
+{
+ /* this handler is provided so driver core doesn't yell at us */
+}
+
+static struct virtio_config_ops omap_rpmsg_config_ops = {
+ .get_features = omap_rpmsg_get_features,
+ .finalize_features = omap_rpmsg_finalize_features,
+ .get = omap_rpmsg_get,
+ .find_vqs = omap_rpmsg_find_vqs,
+ .del_vqs = omap_rpmsg_del_vqs,
+ .reset = omap_rpmsg_reset,
+ .set_status = omap_rpmsg_set_status,
+ .get_status = omap_rpmsg_get_status,
+};
+
+/*
+ * Populating the static channels table.
+ *
+ * This is not always required, and the example below just demonstrates
+ * how to populate it with a static server channel.
+ *
+ * This example should be moved to Documentation/rpmsh.h before merging.
+ *
+ * For more info, see 'struct rpmsg_channel_info'.
+ */
+static struct rpmsg_channel_info omap_ipuc0_static_chnls[] = {
+ RMSG_SERVER_CHNL("rpmsg-server-sample", 137),
+ { },
+};
+
+static struct rpmsg_channel_info omap_ipuc1_static_chnls[] = {
+ { },
+};
+
+static struct omap_rpmsg_vproc omap_rpmsg_vprocs[] = {
+ /* ipu_c0's rpmsg backend */
+ {
+ .vdev.id.device = VIRTIO_ID_RPMSG,
+ .vdev.config = &omap_rpmsg_config_ops,
+ .mbox_name = "mailbox-1",
+ .rproc_name = "ipu",
+ /* core 0 is using indices 0 + 1 for its vqs */
+ .base_vq_id = 0,
+ .static_chnls = omap_ipuc0_static_chnls,
+ },
+ /* ipu_c1's rpmsg backend */
+ {
+ .vdev.id.device = VIRTIO_ID_RPMSG,
+ .vdev.config = &omap_rpmsg_config_ops,
+ .mbox_name = "mailbox-1",
+ .rproc_name = "ipu",
+ /* core 1 is using indices 2 + 3 for its vqs */
+ .base_vq_id = 2,
+ .static_chnls = omap_ipuc1_static_chnls,
+ },
+};
+
+static int __init omap_rpmsg_ini(void)
+{
+ int i, ret = 0;
+ /*
+ * This whole area generally needs some rework.
+ * E.g, consider using dma_alloc_coherent for the IPC buffers and
+ * vrings, use CMA, etc...
+ */
+ phys_addr_t paddr = omap_dsp_get_mempool_base();
+ phys_addr_t psize = omap_dsp_get_mempool_size();
+
+ /*
+ * allocate carverout memory for the buffers and vring, and
+ * then register the vproc virtio device
+ */
+ for (i = 0; i < ARRAY_SIZE(omap_rpmsg_vprocs); i++) {
+ struct omap_rpmsg_vproc *vproc = &omap_rpmsg_vprocs[i];
+
+ if (psize < RPMSG_IPC_MEM) {
+ pr_err("out of carveout memory: %d (%d)\n", psize, i);
+ return -ENOMEM;
+ }
+
+ vproc->buf_paddr = paddr;
+ vproc->buf_size = RPMSG_BUFS_SPACE;
+ vproc->vring[0] = paddr + RPMSG_BUFS_SPACE;
+ vproc->vring[1] = paddr + RPMSG_BUFS_SPACE + RPMSG_RING_SIZE;
+
+ paddr += RPMSG_IPC_MEM;
+ psize -= RPMSG_IPC_MEM;
+
+ pr_debug("vproc%d: buf 0x%x, vring0 0x%x, vring1 0x%x\n", i,
+ vproc->buf_paddr, vproc->vring[0], vproc->vring[1]);
+
+ vproc->vdev.dev.release = omap_rpmsg_vproc_release;
+
+ ret = register_virtio_device(&vproc->vdev);
+ if (ret) {
+ pr_err("failed to register vproc: %d\n", ret);
+ break;
+ }
+ }
+
+ return ret;
+}
+module_init(omap_rpmsg_ini);
+
+static void __exit omap_rpmsg_fini(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(omap_rpmsg_vprocs); i++) {
+ struct omap_rpmsg_vproc *vproc = &omap_rpmsg_vprocs[i];
+
+ unregister_virtio_device(&vproc->vdev);
+ }
+}
+module_exit(omap_rpmsg_fini);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("OMAP Remote processor messaging virtio device");
diff --git a/drivers/rpmsg/host/omap_rpmsg.h b/drivers/rpmsg/host/omap_rpmsg.h
new file mode 100644
index 0000000..f6d2036
--- /dev/null
+++ b/drivers/rpmsg/host/omap_rpmsg.h
@@ -0,0 +1,69 @@
+/*
+ * Remote processor messaging
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name Texas Instruments nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _OMAP_RPMSG_H
+#define _OMAP_RPMSG_H
+
+/*
+ * enum - Predefined Mailbox Messages
+ *
+ * @RP_MBOX_READY: informs the M3's that we're up and running. this is
+ * part of the init sequence sent that the M3 expects to see immediately
+ * after it is booted.
+ *
+ * @RP_MBOX_PENDING_MSG: informs the receiver that there is an inbound
+ * message waiting in its own receive-side vring. please note that currently
+ * this message is optional: alternatively, one can explicitly send the index
+ * of the triggered virtqueue itself. the preferred approach will be decided
+ * as we progress and experiment with those two different approaches.
+ *
+ * @RP_MBOX_CRASH: this message is sent if BIOS crashes
+ *
+ * @RP_MBOX_ECHO_REQUEST: a mailbox-level "ping" message.
+ *
+ * @RP_MBOX_ECHO_REPLY: a mailbox-level reply to a "ping"
+ *
+ * @RP_MBOX_ABORT_REQUEST: a "please crash" request, used for testing the
+ * recovery mechanism (to some extent).
+ */
+enum omap_rp_mbox_messages {
+ RP_MBOX_READY = 0xFFFFFF00,
+ RP_MBOX_PENDING_MSG = 0xFFFFFF01,
+ RP_MBOX_CRASH = 0xFFFFFF02,
+ RP_MBOX_ECHO_REQUEST = 0xFFFFFF03,
+ RP_MBOX_ECHO_REPLY = 0xFFFFFF04,
+ RP_MBOX_ABORT_REQUEST = 0xFFFFFF05,
+};
+
+#endif /* _OMAP_RPMSG_H */
--
1.7.1
On Tue, Jun 21, 2011 at 10:18 AM, Ohad Ben-Cohen <[email protected]> wrote:
> * I will be replying to this email with an attachment of the M3 firmware
> ?image I work with, so anyone with a panda board can take the code for a
> ?ride (put the image in /lib/firmware, and tell me if stuff doesn't work
> ?for you: there are a few omap iommu fixes that are circulating but not
> ?merged yet).
Attaching an OMAP4 M3 firmware.
Below is a simple session with it:
root@omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/name
ipu
root@omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/state
offline (0)
root@omap4430-panda:~# modprobe omap_rpmsg
[ 222.245758] omap_device: omap-mailbox.-1: new worst case activate
latency 0: 30517
[ 222.254669] omap-rproc omap-rproc.1: powering up ipu
[ 222.263580] virtio_rpmsg_bus virtio0: rpmsg backend virtproc probed
successfully
[ 222.272033] virtio_rpmsg_bus virtio1: rpmsg backend virtproc probed
successfully
[ 222.325317] omap-rproc omap-rproc.1: Loaded fw image ducati-m3.bin,
size 161736
[ 222.333068] omap-rproc omap-rproc.1: BIOS image version is 2
[ 222.346099] omap-iommu omap-iommu.0: ducati: version 2.1
[ 222.352661] omap_device: omap-rproc.1: new worst case activate
latency 0: 30517
[ 222.360351] omap-rproc omap-rproc.1: remote processor ipu is now up
[ 222.709411] omap_rpmsg_mbox_callback: received echo reply from ipu !
[ 222.716156] omap_rpmsg_mbox_callback: received echo reply from ipu !
[ 222.722869] omap_rpmsg_mbox_callback: received echo reply from ipu !
[ 222.729583] omap_rpmsg_mbox_callback: received echo reply from ipu !
[ 222.736419] virtio_rpmsg_bus virtio0: creating channel
rpmsg-client-sample addr 0x32
[ 222.745361] virtio_rpmsg_bus virtio0: creating channel
rpmsg-client-sample addr 0x33
[ 222.753814] virtio_rpmsg_bus virtio0: creating channel rpmsg-omx addr 0x3c
[ 222.761535] virtio_rpmsg_bus virtio1: creating channel
rpmsg-client-sample addr 0x32
[ 222.769989] virtio_rpmsg_bus virtio1: creating channel
rpmsg-client-sample addr 0x33
[ 222.778411] virtio_rpmsg_bus virtio1: creating channel rpmsg-omx addr 0x3c
root@omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/state
running (2)
root@omap4430-panda:~# modprobe rpmsg_server_sample
[ 509.860748] rpmsg_server_sample rpmsg0: new channel: 0x89 -> 0xffffffff!
[ 509.871307] rpmsg_server_sample rpmsg0: incoming msg 1 (src: 0x32)
[ 509.879608] rpmsg_server_sample rpmsg0: incoming msg 2 (src: 0x32)
[ 509.887451] rpmsg_server_sample rpmsg0: incoming msg 3 (src: 0x32)
...
On Tue, Jun 21, 2011 at 10:50 AM, Ohad Ben-Cohen <[email protected]> wrote:
> root@omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/state
> running (2)
At this point, the two remote M3 cores also start dumping trace logs to
shared memory buffers, which are exposed by debugfs entries:
root@omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/trace0
CORE0 starting..
...
root@omap4430-panda:~# cat /debug/remoteproc/omap-rproc.1/trace1
CORE1 starting..
...
Hi Ohad,
On Tuesday 21 June 2011, Ohad Ben-Cohen wrote:
> This patch set adds a generic AMP/IPC framework which makes it possible to
> control (power on, boot, power off) and communicate (simply send and receive
> messages) with those remote processors.
This looks really nice overall, but I don't currently have time for a
more in-depth review. My feeling is that you are definitely on the right
track here, and the plans you list as TODO to continue are all good.
One point I noticed is the use of debugfs, which you should probably
replace at some point with a stable API, e.g. your own debugfs-like
file system, but there is no hurry to do that now.
> The gory part of remoteproc is the firmware handling. We tried to come up
> with a simple binary format that will require minimum kernel code to handle,
> but at the same time be generic enough in the hopes that it would prove
> useful to others as well. We're not at all hang onto the binary format
> we picked: if there's any technical reason to change it to support other
> platforms, please let us know. We do realize that a single binary firmware
> structure might eventually not work for everyone. it did prove useful for
> us though; we adopted both the OMAP and Davinci platforms (and their
> completely different remote processor devices) to this simple binary
> structure, so we don't have to duplicate the firmware handling code.
Regarding the firmware, I think it would be nice to have the option
to stick the firmware blob into the flattened device tree as a bininclude,
so you can build systems using the external processors without special
user space support.
The binary format looks reasonable, but if you need anything more complex,
you should probably go straight for ELF files ;-)
The structures you use like
+struct fw_section {
+ u32 type;
+ u64 da;
+ u32 len;
+ char content[0];
+} __packed;
Unfortunately require __packed. It would be better to sort the members
differently so that each member is naturally aligned in order to
avoid the need for padding or __packed attributes, like:
struct fw_section {
u32 type;
u32 len;
u64 da;
char content[0];
};
Arnd
On Tue, Jun 21, 2011 at 8:20 AM, Arnd Bergmann <[email protected]> wrote:
> Hi Ohad,
>
> On Tuesday 21 June 2011, Ohad Ben-Cohen wrote:
>
>> This patch set adds a generic AMP/IPC framework which makes it possible to
>> control (power on, boot, power off) and communicate (simply send and receive
>> messages) with those remote processors.
>
> This looks really nice overall, but I don't currently have time for a
> more in-depth review. My feeling is that you are definitely on the right
> track here, and the plans you list as TODO to continue are all good.
Second that. I'm happy to see the shift over to virtio, I think that
is the right direction overall. I'll try to carve out some time for a
detailed review.
g.
On Tue, 21 Jun 2011 10:18:33 +0300, Ohad Ben-Cohen <[email protected]> wrote:
> Add a virtio-based IPC bus, which enables kernel users to communicate
> with remote processors over shared memory using a simple messaging
> protocol.
Wow, sometimes one writes a standard and people use it. Thanks!
I'm still digesting exactly what you're doing, but this is a bit
unconventional:
> + /* Platform must supply pre-allocated uncached buffers for now */
> + vdev->config->get(vdev, VPROC_BUF_ADDR, &addr, sizeof(addr));
> + vdev->config->get(vdev, VPROC_BUF_NUM, &num_bufs, sizeof(num_bufs));
> + vdev->config->get(vdev, VPROC_BUF_SZ, &buf_size, sizeof(buf_size));
> + vdev->config->get(vdev, VPROC_BUF_PADDR, &vrp->phys_base,
> + sizeof(vrp->phys_base));
The normal way is to think of the config space as a structure, and use
offsets rather than using an enum value to distinguish the fields.
> +#define RPMSG_NAME_SIZE 32
> +#define RPMSG_DEVICE_MODALIAS_FMT "rpmsg:%s"
> +
> +struct rpmsg_device_id {
> + char name[RPMSG_NAME_SIZE];
> + kernel_ulong_t driver_data /* Data private to the driver */
> + __attribute__((aligned(sizeof(kernel_ulong_t))));
> +};
This alignment directive seems overkill...
> +#define VIRTIO_ID_RPMSG 10 /* virtio remote processor messaging */
I think you want 6. Plan 9 jumped ahead to grab 9 :)
Cheers,
Rusty.
On Wed, 2011-06-22 at 12:12 +0930, Rusty Russell wrote:
> > +#define VIRTIO_ID_RPMSG 10 /* virtio remote processor messaging */
>
> I think you want 6. Plan 9 jumped ahead to grab 9 :)
Maybe it's worth adding an appendix to the virtio spec as part of this
(and really any) patch series which takes a virtio ID.
Also, I understand that virtio itself as an idea isn't Linux specific,
but maybe it's worth including the spec (or at least a variation of it)
as part of the kernel documentation.
--
Sasha.
On Tue, Jun 21, 2011 at 8:18 AM, Ohad Ben-Cohen <[email protected]> wrote:
> +/* bootaddr isn't needed for the dual M3's */
> +static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr)
> +static inline int omap_rproc_stop(struct rproc *rproc)
These two functions don't need to be inline as far as I can see.
> +static struct rproc_ops omap_rproc_ops = {
> + ? ? ? .start = omap_rproc_start,
> + ? ? ? .stop = omap_rproc_stop,
> +};
On Wed, Jun 22, 2011 at 5:42 AM, Rusty Russell <[email protected]> wrote:
> On Tue, 21 Jun 2011 10:18:33 +0300, Ohad Ben-Cohen <[email protected]> wrote:
>> Add a virtio-based IPC bus, which enables kernel users to communicate
>> with remote processors over shared memory using a simple messaging
>> protocol.
>
> Wow, sometimes one writes a standard and people use it. ?Thanks!
And we really liked it: virtio_rpmsg_bus.c, the virtio driver which
does most of the magic here ended up pretty small thanks to virtio.
and the performance numbers are really good, too.
>> + ? ? /* Platform must supply pre-allocated uncached buffers for now */
>> + ? ? vdev->config->get(vdev, VPROC_BUF_ADDR, &addr, sizeof(addr));
>> + ? ? vdev->config->get(vdev, VPROC_BUF_NUM, &num_bufs, sizeof(num_bufs));
>> + ? ? vdev->config->get(vdev, VPROC_BUF_SZ, &buf_size, sizeof(buf_size));
>> + ? ? vdev->config->get(vdev, VPROC_BUF_PADDR, &vrp->phys_base,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? sizeof(vrp->phys_base));
>
> The normal way is to think of the config space as a structure, and use
> offsets rather than using an enum value to distinguish the fields.
Yes, I was (mis-)using the config space for now to talk with
platform-specific code (on the host), and not with the peer, so I
opted for simplicity.
But this is definitely one thing that is going away: I don't see any
reason why not just use dma_alloc_coherent (or even dma_pool_create)
directly from the driver here in order to get those buffers.
>> +#define RPMSG_NAME_SIZE ? ? ? ? ? ? ? ? ? ? ?32
>> +#define RPMSG_DEVICE_MODALIAS_FMT ? ?"rpmsg:%s"
>> +
>> +struct rpmsg_device_id {
>> + ? ? char name[RPMSG_NAME_SIZE];
>> + ? ? kernel_ulong_t driver_data ? ? ?/* Data private to the driver */
>> + ? ? ? ? ? ? ? ? ? ? __attribute__((aligned(sizeof(kernel_ulong_t))));
>> +};
>
> This alignment directive seems overkill...
Yes, looks like I can remove this. thanks.
>> +#define VIRTIO_ID_RPMSG ? ? ? ? ? ? ?10 /* virtio remote processor messaging */
>
> I think you want 6. ?Plan 9 jumped ahead to grab 9 :)
6 it is :)
Thanks,
Ohad.
On Wed, Jun 22, 2011 at 1:05 PM, Will Newton <[email protected]> wrote:
> On Tue, Jun 21, 2011 at 8:18 AM, Ohad Ben-Cohen <[email protected]> wrote:
>
>> +/* bootaddr isn't needed for the dual M3's */
>> +static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr)
>
>> +static inline int omap_rproc_stop(struct rproc *rproc)
>
> These two functions don't need to be inline as far as I can see.
They definitely don't need to. Thanks !
Hi Arnd,
On Tue, Jun 21, 2011 at 5:20 PM, Arnd Bergmann <[email protected]> wrote:
> This looks really nice overall, but I don't currently have time for a
> more in-depth review. My feeling is that you are definitely on the right
> track here, and the plans you list as TODO to continue are all good.
Thanks !
> One point I noticed is the use of debugfs, which you should probably
> replace at some point with a stable API, e.g. your own debugfs-like
> file system, but there is no hurry to do that now.
debugfs is only being used to expose debugging info (namely the power
state of the remote processor and its trace log messages), which is
mostly useful for developers trying to understand what went wrong.
It seems like debugfs fits quite nicely here (e.g. it's perfectly fine
if this is completely compiled out on production systems), but sure,
we can always revisit this later too.
> Regarding the firmware, I think it would be nice to have the option
> to stick the firmware blob into the flattened device tree as a bininclude,
> so you can build systems using the external processors without special
> user space support.
That's interesting. we'll definitely explore this - thanks !
I was also hoping that DT will simplify remote resource allocations as
well (just like any other device, remote processors usually needs some
hw resources) and planned to try it out soon to see how it all works
out together. It might completely eliminate the preliminary "resource
section" thingy we have in the firmware structure currently.
> The binary format looks reasonable, but if you need anything more complex,
> you should probably go straight for ELF files ;-)
Hopefully we won't need to :)
> +struct fw_section {
> + ? ? ? u32 type;
> + ? ? ? u64 da;
> + ? ? ? u32 len;
> + ? ? ? char content[0];
> +} __packed;
>
> Unfortunately require __packed. It would be better to sort the members
> differently so that each member is naturally aligned in order to
> avoid the need for padding or __packed attributes
Definitely. __packed is being used just to be on the safe side; we
didn't intend to introduce unnatural alignment intentionally. will be
fixed.
Thanks!
Ohad.
On Wednesday 22 June 2011, Ohad Ben-Cohen wrote:
> > One point I noticed is the use of debugfs, which you should probably
> > replace at some point with a stable API, e.g. your own debugfs-like
> > file system, but there is no hurry to do that now.
>
> debugfs is only being used to expose debugging info (namely the power
> state of the remote processor and its trace log messages), which is
> mostly useful for developers trying to understand what went wrong.
>
> It seems like debugfs fits quite nicely here (e.g. it's perfectly fine
> if this is completely compiled out on production systems), but sure,
> we can always revisit this later too.
Ok, I see. In that case I agree that using debugfs is fine, but I would
recommend trying to use fewer macros and just open-coding the file
operations for better readability.
> > Unfortunately require __packed. It would be better to sort the members
> > differently so that each member is naturally aligned in order to
> > avoid the need for padding or __packed attributes
>
> Definitely. __packed is being used just to be on the safe side; we
> didn't intend to introduce unnatural alignment intentionally. will be
> fixed.
Ok.
Arnd
On Wed, Jun 22, 2011 at 4:05 PM, Arnd Bergmann <[email protected]> wrote:
> Ok, I see. In that case I agree that using debugfs is fine, but I would
> recommend trying to use fewer macros and just open-coding the file
> operations for better readability.
Sure thing. It didn't end up saving much code eventually, too, so I'll
just remove it.
On Tue, 21 Jun 2011 10:18:27 +0300 Ohad Ben-Cohen wrote:
Hi,
Just a few minor nits inline...
> diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt
> new file mode 100644
> index 0000000..3075813
> --- /dev/null
> +++ b/Documentation/remoteproc.txt
> @@ -0,0 +1,170 @@
> +Remote Processor Framework
> +
> +1. Introduction
> +
> +Modern SoCs typically have heterogeneous remote processor devices in asymmetric
> +multiprocessing (AMP) configurations, which may be running different instances
> +of operating system, whether it's Linux or any other flavor of real-time OS.
> +
> +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP.
> +In a typical configuration, the dual cortex-A9 is running Linux in a SMP
> +configuration, and each of the other three cores (two M3 cores and a DSP)
> +is running its own instance of RTOS in an AMP configuration.
> +
> +The generic remoteproc driver allows different platforms/architectures to
> +control (power on, load firmware, power off) those remote processors while
> +abstracting the hardware differences, so the entire driver doesn't need to be
> +duplicated.
> +
> +2. User API
> +
> + struct rproc *rproc_get(const char *name);
> + - power up the remote processor, identified by the 'name' argument,
> + and boot it. If the remote processor is already powered on, the
> + function immediately succeeds.
> + On success, returns the rproc handle. On failure, NULL is returned.
> +
> + void rproc_put(struct rproc *rproc);
> + - power off the remote processor, identified by the rproc handle.
> + Every call to rproc_get() must be (eventually) accompanied by a call
> + to rproc_put(). Calling rproc_put() redundantly is a bug.
> + Note: the remote processor will actually be powered off only when the
> + last user calls rproc_put().
> +
> +3. Typical usage
> +
> +#include <linux/remoteproc.h>
> +
> +int dummy_rproc_example(void)
> +{
> + struct rproc *my_rproc;
> +
> + /* let's power on and boot the image processing unit */
> + my_rproc = rproc_get("ipu");
> + if (!my_rproc) {
> + /*
> + * something went wrong. handle it and leave.
> + */
> + }
> +
> + /*
> + * the 'ipu' remote processor is now powered on... let it work !
> + */
> +
> + /* if we no longer need ipu's services, power it down */
> + rproc_put(my_rproc);
> +}
> +
> +4. API for implementors
> +
> + int rproc_register(struct device *dev, const char *name,
> + const struct rproc_ops *ops,
> + const char *firmware,
> + const struct rproc_mem_entry *memory_maps,
> + struct module *owner);
> + - should be called from the underlying platform-specific implementation, in
> + order to register a new remoteproc device. 'dev' is the underlying
> + device, 'name' is the name of the remote processor, which will be
> + specified by users calling rproc_get(), 'ops' is the platform-specific
> + start/stop handlers, 'firmware' is the name of the firmware file to
> + boot the processor with, 'memory_maps' is a table of da<->pa memory
> + mappings which should be used to configure the IOMMU (if not relevant,
> + just pass NULL here), 'owner' is the underlying module that should
> + not be removed while the remote processor is in use.
> +
> + Returns 0 on success, or an appropriate error code on failure.
> +
> + int rproc_unregister(const char *name);
> + - should be called from the underlying platform-specific implementation, in
> + order to unregister a remoteproc device that was previously registered
> + with rproc_register().
> +
> +5. Implementation callbacks
> +
> +Every remoteproc implementation must provide these handlers:
> +
> +struct rproc_ops {
> + int (*start)(struct rproc *rproc, u64 bootaddr);
> + int (*stop)(struct rproc *rproc);
> +};
> +
> +The ->start() handler takes a rproc handle and an optional bootaddr argument,
an rproc
> +and should power on the device and boot it (using the bootaddr argument
> +if the hardware requires one).
> +On success, 0 is returned, and on failure, an appropriate error code.
> +
> +The ->stop() handler takes a rproc handle and powers the device off.
an rproc
> +On success, 0 is returned, and on failure, an appropriate error code.
> +
> +6. Binary Firmware Structure
> +
> +The following enums and structures define the binary format of the images
> +remoteproc loads and boot the remote processors with.
boots
> +
> +The general binary format is as follows:
> +
> +struct {
> + char magic[4] = { 'R', 'P', 'R', 'C' };
> + u32 version;
> + u32 header_len;
> + char header[...] = { header_len bytes of unformatted, textual header };
> + struct section {
> + u32 type;
> + u64 da;
> + u32 len;
> + u8 content[...] = { len bytes of binary data };
> + } [ no limit on number of sections ];
> +} __packed;
> +
> +The image begins with a 4-bytes "RPRC" magic, a version number, and a
> +free-style textual header that users can easily read.
> +
> +After the header, the firmware contains several sections that should be
> +loaded to memory so the remote processor can access them.
> +
> +Every section begins with its type, device address (da) where the remote
> +processor expects to find this section at (exact meaning depends whether
drop: at
> +the device accesses memory through an IOMMU or not. if not, da might just
> +be physical addresses), the section length and its content.
> +
> +Most of the sections are either text or data (which currently are treated
> +exactly the same), but there is one special "resource" section that allows
> +the remote processor to announce/request certain resources from the host.
> +
> +A resource section is just a packed array of the following struct:
> +
> +struct fw_resource {
> + u32 type;
> + u64 da;
> + u64 pa;
> + u32 len;
> + u32 flags;
> + u8 name[48];
> +} __packed;
> +
> +The way a resource is really handled strongly depends on its type.
> +Some resources are just one-way announcements, e.g., a RSC_TRACE type means
> +that the remote processor will be writing log messages into a trace buffer
> +which is located at the address specified in 'da'. In that case, 'len' is
> +the size of that buffer. A RSC_BOOTADDR resource type announces the boot
> +address (i.e. the first instruction the remote processor should be booted with)
> +in 'da'.
> +
> +Other resources entries might be a two-way request/respond negotiation where
> +a certain resource (memory or any other hardware resource) is requested
> +by specifying the appropriate type and name. The host should then allocate
> +such a resource and "reply" by writing the identifier (physical address
> +or any other device id that will be meaningful to the remote processor)
> +back into the relevant member of the resource structure. Obviously this
> +approach can only be used _before_ booting the remote processor. After
> +the remote processor is powered up, the resource section is expected
> +to stay static. Runtime resource management (i.e. handling requests after
> +the remote processor has booted) will be achieved using a dedicated rpmsg
> +driver.
> +
> +The latter two-way approach is still preliminary and has not been implemented
> +yet. It's left to see how this all works out.
> +
> +Most likely this kind of static allocations of hardware resources for
> +remote processors can also use DT, so it's interesting to see how
> +this all work out when DT materializes.
works out
thanks,
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
Hi Randy,
On Wed, Jun 22, 2011 at 8:55 PM, Randy Dunlap <[email protected]> wrote:
> On Tue, 21 Jun 2011 10:18:27 +0300 Ohad Ben-Cohen wrote:
>
> Hi,
> Just a few minor nits inline...
Thanks!
Ohad.
On 6/21/2011 3:18 AM, Ohad Ben-Cohen wrote:
> Modern SoCs typically employ a central symmetric multiprocessing (SMP)
> application processor running Linux, with several other asymmetric
> multiprocessing (AMP) heterogeneous processors running different instances
> of operating system, whether Linux or any other flavor of real-time OS.
>
> OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP.
> Typically, the dual cortex-A9 is running Linux in a SMP configuration, and
> each of the other three cores (two M3 cores and a DSP) is running its own
> instance of RTOS in an AMP configuration.
>
> AMP remote processors typically employ dedicated DSP codecs and multimedia
> hardware accelerators, and therefore are often used to offload cpu-intensive
> multimedia tasks from the main application processor. They could also be
> used to control latency-sensitive sensors, drive "random" hardware blocks,
> or just perform background tasks while the main CPU is idling.
>
> Users of those remote processors can either be userland apps (e.g.
> multimedia frameworks talking with remote OMX components) or kernel drivers
> (controlling hardware accessible only by the remote processor, reserving
> kernel-controlled resources on behalf of the remote processor, etc..).
>
> This patch set adds a generic AMP/IPC framework which makes it possible to
> control (power on, boot, power off) and communicate (simply send and receive
> messages) with those remote processors.
>
> Specifically, we're adding:
>
> * Rpmsg: a virtio-based messaging bus that allows kernel drivers to
> communicate with remote processors available on the system. In turn,
> drivers could then expose appropriate user space interfaces, if needed
> (tasks running on remote processors often have direct access to sensitive
> resources like the system's physical memory, gpios, i2c buses, dma
> controllers, etc.. so one normally wouldn't want to allow userland to
> send everything/everywhere it wants).
>
> Every rpmsg device is a communication channel with a service running on a
> remote processor (thus rpmsg devices are called channels). Channels are
> identified by a textual name (which is used to match drivers to devices)
> and have a local ("source") rpmsg address, and remote ("destination") rpmsg
> address. When a driver starts listening on a channel (most commonly when it
> is probed), the bus assigns the driver a unique rpmsg src address (a 32 bit
> integer) and binds it with the driver's rx callback handler. This way
> when inbound messages arrive to this src address, the rpmsg core dispatches
> them to that driver, by invoking the driver's rx handler with the payload
> of the incoming message.
>
> Once probed, rpmsg drivers can also immediately start sending messages to the
> remote rpmsg service by using simple sending API; no need even to specify
> a destination address, since that's part of the rpmsg channel, and the rpmsg
> bus uses the channel's dst address when it constructs the message (for
> more demanding use cases, there's also an extended API, which does allow
> full control of both the src and dst addresses).
>
> The rpmsg bus is using virtio to send and receive messages: every pair
> of processors share two vrings, which are used to send and receive the
> messages over shared memory (one vring is used for tx, and the other one
> for rx). Kicking the remote processor (i.e. letting it know it has a pending
> message on its vring) is accomplished by means available on the platform we
> run on (e.g. OMAP is using its mailbox to both interrupt the remote processor
> and tell it which vring is kicked at the same time). The header of every
> message sent on the rpmsg bus contains src and dst addresses, which make it
> possible to multiplex several rpmsg channels on the same vring.
>
> One nice property of the rpmsg bus is that device creation is completely
> dynamic: remote processors can announce the existence of remote rpmsg
> services by sending a "name service" messages (which contain the name and
> rpmsg addr of the remote service). Those messages are picked by the rpmsg
> bus, which in turn dynamically creates and registers the rpmsg channels
> (i.e devices) which represents the remote services. If/when a relevant rpmsg
> driver is registered, it will be immediately probed by the bus, and can then
> start "talking" to the remote service.
>
> Similarly, we can use this technique to dynamically create virtio devices
> (and new vrings) which would then represent e.g. remote network, console
> and block devices that will be driven by the existing virtio drivers
> (this is still not implemented though; it requires some RTOS work as we're
> not booting Linux on OMAP's remote processors). Creating new vrings might
> also be desired by users who just don't want to use the shared rpmsg vrings
> (for performance or any other functionality reasons).
>
> In addition to dynamic creation of rpmsg channels, the rpmsg bus also
> supports creation of static channels. This is needed in two cases:
> - when a certain remote processor doesn't support sending those "name
> service" announcements. In that case, a static table of remote rpmsg
> services must be used to create the rpmsg channels.
> - to support rpmsg server drivers, which aren't bound to a specific remote
> rpmsg address. Instead, they just listen on a local address, waiting for
> incoming messages. To send a message, those server drivers need to use
> the rpmsg_sendto() API, so they can explicitly indicate the dst address
> every time.
>
> There are already several immediate use cases for rpmsg drivers: OMX
> offloading (already being used on OMAP4), hardware resource manager (remote
> processors on OMAP4 need to ask Linux to enable/disable hardware resources
> on its behalf), remote display driver on Netra (dm8168), where the display
> is controlled by a remote M3 processor (and a Linux v4l2/fbdev driver will
> use rpmsg to communicate with that remote display driver).
>
> * Remoteproc: a generic driver that maintains the state of the remote
> processor(s). Simple rproc_get() and rproc_put() API is exposed, which
> drivers can use when needed (first driver to call get() will load a firmware,
> configure an iommu if needed, and boot the remote processor, while last
> driver to call put() will power it down).
>
> Hardware differences are abstracted as usual: a platform-specific driver
> registers its own start/stop handlers in the generic remoteproc driver,
> and those are invoked when its time to power up/down the processor. As a
> reference, this patch set include remoteproc support for both OMAP4's
> cortex-M3 and Davinci's DSP, tested on the pandaboard and hawkboard,
> respectively.
>
> The gory part of remoteproc is the firmware handling. We tried to come up
> with a simple binary format that will require minimum kernel code to handle,
> but at the same time be generic enough in the hopes that it would prove
> useful to others as well. We're not at all hang onto the binary format
> we picked: if there's any technical reason to change it to support other
> platforms, please let us know. We do realize that a single binary firmware
> structure might eventually not work for everyone. it did prove useful for
> us though; we adopted both the OMAP and Davinci platforms (and their
> completely different remote processor devices) to this simple binary
> structure, so we don't have to duplicate the firmware handling code.
>
I'd like to kick the tires on this with a da850 based platform (MityDSP-L138).
Any chance you might be able to share the stuff you did on the remote side
(DSP/BIOS adaptations for rpmsg, utils for ELF or COFF conversion to
firmware format, etc.) for the DSP side of your tests done with the hawkboard?
It looks like, at least for the da850, this subsumes or obsoletes DSPLINK in order
to drive a more general purpose architecture (which looks great, so far, BTW).
Is that the intent?
-Mike
Hello.
Ohad Ben-Cohen wrote:
> From: Mark Grosen <[email protected]>
> Add remoteproc implementation for da850, so we can boot its DSP.
> Signed-off-by: Mark Grosen <[email protected]>
> Signed-off-by: Ohad Ben-Cohen <[email protected]>
> ---
> arch/arm/mach-davinci/include/mach/remoteproc.h | 28 +++++
> drivers/remoteproc/Kconfig | 16 +++
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/davinci_remoteproc.c | 147 +++++++++++++++++++++++
> 4 files changed, 192 insertions(+), 0 deletions(-)
> create mode 100644 arch/arm/mach-davinci/include/mach/remoteproc.h
> create mode 100644 drivers/remoteproc/davinci_remoteproc.c
> diff --git a/arch/arm/mach-davinci/include/mach/remoteproc.h b/arch/arm/mach-davinci/include/mach/remoteproc.h
> new file mode 100644
> index 0000000..af6e88c
> --- /dev/null
> +++ b/arch/arm/mach-davinci/include/mach/remoteproc.h
> @@ -0,0 +1,28 @@
> +/*
> + * Remote Processor
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _DAVINCI_REMOTEPROC_H
> +#define _DAVINCI_REMOTEPROC_H
> +
> +#include <linux/remoteproc.h>
> +
> +struct davinci_rproc_pdata {
> + struct rproc_ops *ops;
> + char *name;
> + char *clk_name;
> + char *firmware;
> +};
> +
> +#endif /* _DAVINCI_REMOTEPROC_H */
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index 88421bd..1e594b5 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -26,3 +26,19 @@ config OMAP_REMOTE_PROC
>
> It's safe to say n here if you're not interested in multimedia
> offloading or just want a bare minimum kernel.
> +
> +config DAVINCI_REMOTE_PROC
> + tristate "Davinci remoteproc support"
> + depends on ARCH_DAVINCI_DA850
It should work on DA830 as well, but not on real DaVinci, so the name is
misleading...
[...]
> diff --git a/drivers/remoteproc/davinci_remoteproc.c b/drivers/remoteproc/davinci_remoteproc.c
> new file mode 100644
> index 0000000..0e26fe9
> --- /dev/null
> +++ b/drivers/remoteproc/davinci_remoteproc.c
> @@ -0,0 +1,147 @@
> +/*
> + * Remote processor machine-specific module for Davinci
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <linux/printk.h>
> +#include <linux/bitops.h>
> +#include <linux/platform_device.h>
> +#include <linux/remoteproc.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +
> +#include <mach/da8xx.h>
> +#include <mach/cputype.h>
> +#include <mach/psc.h>
> +#include <mach/remoteproc.h>
> +
> +/*
> + * Technical Reference:
> + * OMAP-L138 Applications Processor System Reference Guide
> + * http://www.ti.com/litv/pdf/sprugm7d
> + */
> +
> +/* local reset bit (0 is asserted) in MDCTL15 register (section 9.6.18) */
> +#define LRST BIT(8)
Perhaps this should be named nLRST or something if the sense is inverted?
> +/* next state bits in MDCTL15 register (section 9.6.18) */
> +#define NEXT_ENABLED 0x3
Isn't this already declared in <mach/psc.h> as PSC_STATE_ENABLED?
> +/* register for DSP boot address in SYSCFG0 module (section 11.5.6) */
> +#define HOST1CFG 0x44
Worth declaring in <mach/da8xx.h> instead...
> +static inline int davinci_rproc_start(struct rproc *rproc, u64 bootaddr)
> +{
> + struct device *dev = rproc->dev;
> + struct davinci_rproc_pdata *pdata = dev->platform_data;
> + struct davinci_soc_info *soc_info = &davinci_soc_info;
> + void __iomem *psc_base;
> + struct clk *dsp_clk;
> +
> + /* hw requires the start (boot) address be on 1KB boundary */
> + if (bootaddr & 0x3ff) {
> + dev_err(dev, "invalid boot address: must be aligned to 1KB\n");
> + return -EINVAL;
> + }
> +
> + dsp_clk = clk_get(dev, pdata->clk_name);
We could match using clkdev functionality, but the clock entry would need to
be changed then...
> + if (IS_ERR_OR_NULL(dsp_clk)) {
> + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
> + return PTR_ERR(dsp_clk);
> + }
> +
> + clk_enable(dsp_clk);
This seems rather senseless activity as on DA8xx the DSP core boots the ARM
core, so the DSP clock will be already enabled.
> + rproc->priv = dsp_clk;
> +
> + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K);
> +
> + /* insure local reset is asserted before writing start address */
> + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM);
> +
> + __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG));
DA8XX_SYSCFG0_VIRT() is not supposed to be used outside mach-davinci. The
variable it refers is not exported, so driver module won't work.
> + /* de-assert local reset to start the dsp running */
> + __raw_writel(LRST | NEXT_ENABLED, psc_base + MDCTL +
> + 4 * DA8XX_LPSC0_GEM);
> +
> + iounmap(psc_base);
> +
> + return 0;
> +}
> +
> +static inline int davinci_rproc_stop(struct rproc *rproc)
> +{
> + struct davinci_soc_info *soc_info = &davinci_soc_info;
> + void __iomem *psc_base;
> + struct clk *dsp_clk = rproc->priv;
> +
> + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K);
> +
> + /* halt the dsp by asserting local reset */
> + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 * DA8XX_LPSC0_GEM);
> +
> + clk_disable(dsp_clk);
> + clk_put(dsp_clk);
> +
> + iounmap(psc_base);
> +
> + return 0;
> +}
All this is DA8xx specific code which won't fly on real DaVincis, so I
suggest that you rename the file to da8xx_remoteproc.c for clarity; and rename
the patch as well...
WBR, Sergei
> From: Michael Williamson
> Sent: Thursday, June 23, 2011 5:23 AM
...
> I'd like to kick the tires on this with a da850 based platform (MityDSP-L138).
> Any chance you might be able to share the stuff you did on the remote side
> (DSP/BIOS adaptations for rpmsg, utils for ELF or COFF conversion to
> firmware format, etc.) for the DSP side of your tests done with the
> hawkboard?
We have only implemented the remoteproc (load, start, stop) part for
OMAPL138 so far, i.e., not the rpmsg part (communications). I am not sure
when we will get to the rpmsg part.
We will be publishing a Git tree soon with the "remote processor" code under
a BSD license. This code works with our TI RTOS, SYS/BIOS (also BSD), on
both M3 and DSP CPUs. This will include the utility to generate the RPRC
firmware format from an ELF file. Note that the Linux side does not have any
explicit binding or dependency on the runtime environment on the remote
processor, so alternate RTOS or bare-metal implementations could also be
done. We will post a follow-up to the list with a URL soon.
> It looks like, at least for the da850, this subsumes or obsoletes
> DSPLINK in order to drive a more general purpose architecture
> (which looks great, so far, BTW).
> Is that the intent?
First, we are not abandoning DSPLINK. We have many users of this, even
though it is out-of-tree, and we will continue to support it. That said, we
do intend to make this new design the basis for DSPLINK-like
functionality. It's designed to be done "the right way" for Linux (and we
are looking for feedback to make it better). It is also designed to be more
scalable and extensible in userspace. With a solid kernel foundation, we can
provide lots of functionality in userspace, or users can implement their own
custom solutions. One of the key things to do is map our existing DSPLINK
APIs, like MessageQ, to the new rpmsg transport.
Mark
On Thursday 23 June 2011 18:27:10 Grosen, Mark wrote:
> First, we are not abandoning DSPLINK. We have many users of this, even
> though it is out-of-tree, and we will continue to support it. That said, we
> do intend to make this new design the basis for DSPLINK-like
> functionality. It's designed to be done "the right way" for Linux (and we
> are looking for feedback to make it better). It is also designed to be more
> scalable and extensible in userspace. With a solid kernel foundation, we can
> provide lots of functionality in userspace, or users can implement their own
> custom solutions. One of the key things to do is map our existing DSPLINK
> APIs, like MessageQ, to the new rpmsg transport.
Sounds all good. What about the PRUSS code? Does that fit into the new model
as well?
Arnd
> From: Sergei Shtylyov
> Sent: Thursday, June 23, 2011 8:28 AM
> Subject: Re: [RFC 5/8] remoteproc: add davinci implementation
>
> Hello.
Sergei, thanks for the feedback. Comments below.
Mark
>
> It should work on DA830 as well, but not on real DaVinci, so the
> name is misleading...
Yes, we debated calling it da8xx, but felt that with minor changes it could
accomodate the other SoCs in the davinci family. However, it may be better
to start with just the da8xx/omapl13x parts and then rename if we add the
others.
> [...]
> > +
> > +/*
> > + * Technical Reference:
> > + * OMAP-L138 Applications Processor System Reference Guide
> > + * http://www.ti.com/litv/pdf/sprugm7d
> > + */
> > +
> > +/* local reset bit (0 is asserted) in MDCTL15 register (section
> 9.6.18) */
> > +#define LRST BIT(8)
>
> Perhaps this should be named nLRST or something if the sense is inverted?
If there is an established naming convention for this, I'll adopt it.
>
> > +/* next state bits in MDCTL15 register (section 9.6.18) */
> > +#define NEXT_ENABLED 0x3
>
> Isn't this already declared in <mach/psc.h> as PSC_STATE_ENABLED?
Yes, thanks, I missed it.
> > +/* register for DSP boot address in SYSCFG0 module (section 11.5.6)
> */
> > +#define HOST1CFG 0x44
>
> Worth declaring in <mach/da8xx.h> instead...
Possibly - since it is only used for the DSP, I thought it would be better
to keep local to this implementation. I'll adopt whichever approach is the
convention.
> > +static inline int davinci_rproc_start(struct rproc *rproc, u64
> bootaddr)
> > +{
> > + struct device *dev = rproc->dev;
> > + struct davinci_rproc_pdata *pdata = dev->platform_data;
> > + struct davinci_soc_info *soc_info = &davinci_soc_info;
> > + void __iomem *psc_base;
> > + struct clk *dsp_clk;
> > +
> > + /* hw requires the start (boot) address be on 1KB boundary */
> > + if (bootaddr & 0x3ff) {
> > + dev_err(dev, "invalid boot address: must be aligned to
> 1KB\n");
> > + return -EINVAL;
> > + }
> > +
> > + dsp_clk = clk_get(dev, pdata->clk_name);
>
> We could match using clkdev functionality, but the clock entry
> would need to be changed then...
I followed the existing pattern I saw in other drivers. If there is a new,
better way, please point me to an example.
>
> > + if (IS_ERR_OR_NULL(dsp_clk)) {
> > + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
> > + return PTR_ERR(dsp_clk);
> > + }
> > +
> > + clk_enable(dsp_clk);
>
> This seems rather senseless activity as on DA8xx the DSP core
> boots the ARM core, so the DSP clock will be already enabled.
I think it is needed. It's true that the DSP initiates the boot, but then it is
reset and the clock disabled. See Section 13.2 of
http://focus.ti.com/lit/ug/sprugm7e/sprugm7e.pdf:
13.2 DSP Wake Up
Following deassertion of device reset, the DSP intializes the ARM296 so that
it can execute the ARM ROM bootloader. Upon successful wake up, the ARM
places the DSP in a reset and clock gated (SwRstDisable) state that is
controlled by the LPSC and the SYSCFG modules.
Besides, the boot loader could have disabled it to save power. The ARM and
DSP are clocked independently, so I think it's best to use clock management.
> > + rproc->priv = dsp_clk;
> > +
> > + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K);
> > +
> > + /* insure local reset is asserted before writing start address */
> > + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 *
> DA8XX_LPSC0_GEM);
> > +
> > + __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG));
>
> DA8XX_SYSCFG0_VIRT() is not supposed to be used outside mach-davinci. The
> variable it refers is not exported, so driver module won't work.
Ooops, I clearly did not build this as a module. Suggestion how to fix this?
> > + /* de-assert local reset to start the dsp running */
> > + __raw_writel(LRST | NEXT_ENABLED, psc_base + MDCTL +
> > + 4 * DA8XX_LPSC0_GEM);
> > +
> > + iounmap(psc_base);
> > +
> > + return 0;
> > +}
> > +
> > +static inline int davinci_rproc_stop(struct rproc *rproc)
> > +{
> > + struct davinci_soc_info *soc_info = &davinci_soc_info;
> > + void __iomem *psc_base;
> > + struct clk *dsp_clk = rproc->priv;
> > +
> > + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K);
> > +
> > + /* halt the dsp by asserting local reset */
> > + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 *
> DA8XX_LPSC0_GEM);
> > +
> > + clk_disable(dsp_clk);
> > + clk_put(dsp_clk);
> > +
> > + iounmap(psc_base);
> > +
> > + return 0;
> > +}
>
> All this is DA8xx specific code which won't fly on real DaVincis, so I
> suggest that you rename the file to da8xx_remoteproc.c for clarity; and
> rename the patch as well...
This is probably the right thing to do ...
Mark
> From: Arnd Bergmann
> Sent: Thursday, June 23, 2011 11:47 AM
> Subject: Re: [RFC 0/8] Introducing a generic AMP/IPC framework
>
> On Thursday 23 June 2011 18:27:10 Grosen, Mark wrote:
> > First, we are not abandoning DSPLINK. We have many users of this, even
> > though it is out-of-tree, and we will continue to support it. That said, we
> > do intend to make this new design the basis for DSPLINK-like
> > functionality. It's designed to be done "the right way" for Linux (and we
> > are looking for feedback to make it better). It is also designed to be more
> > scalable and extensible in userspace. With a solid kernel foundation, we can
> > provide lots of functionality in userspace, or users can implement their own
> > custom solutions. One of the key things to do is map our existing DSPLINK
> > APIs, like MessageQ, to the new rpmsg transport.
>
> Sounds all good. What about the PRUSS code? Does that fit into the new
> model as well?
>
> Arnd
Arnd,
Yes, I have been following some of the PRUSS discussion. I think the
remoteproc driver could be used to manage the basic load/start/stop of the
PRUSS processor. I am not sure if the virio/rpmsg part would be a good
fit. The PRUSS processor is pretty limited, so the generality of virtio
might be too much to fit and too much overhead. However, one of the good
things about remoteproc currently is that it is standalone, so other
transports could use it via the rproc_get/put methods.
Mark
Hello.
Grosen, Mark wrote:
>> It should work on DA830 as well,
So please make it dependent on ARCH_DAVINCI_DA8XX.
>> but not on real DaVinci, so the name is misleading...
> Yes, we debated calling it da8xx, but felt that with minor changes it could
> accomodate the other SoCs in the davinci family.
I don't think it's a good idea. Using cpu_is_*() is drivers is bad. Using
#ifdef's is not an option either.
> However, it may be better
> to start with just the da8xx/omapl13x parts and then rename if we add the
> others.
>> [...]
>>> +
>>> +/*
>>> + * Technical Reference:
>>> + * OMAP-L138 Applications Processor System Reference Guide
>>> + * http://www.ti.com/litv/pdf/sprugm7d
>>> + */
>>> +
>>> +/* local reset bit (0 is asserted) in MDCTL15 register (section
>> 9.6.18) */
>>> +#define LRST BIT(8)
>> Perhaps this should be named nLRST or something if the sense is inverted?
> If there is an established naming convention for this, I'll adopt it.
Looking into my old PSC manual (can't get the recent documentation from TI's
site right now), the bit is called LRSTz.
It's worth moving this #define into <mach/psc.h> as well.
>>> +/* register for DSP boot address in SYSCFG0 module (section 11.5.6)
>> */
>>> +#define HOST1CFG 0x44
>> Worth declaring in <mach/da8xx.h> instead...
> Possibly - since it is only used for the DSP, I thought it would be better
> to keep local to this implementation. I'll adopt whichever approach is the
> convention.
Well, the general approach is to keep the #define's where they are used, so
maybe we should keep this #define here.
>>> +static inline int davinci_rproc_start(struct rproc *rproc, u64
>> bootaddr)
>>> +{
>>> + struct device *dev = rproc->dev;
>>> + struct davinci_rproc_pdata *pdata = dev->platform_data;
>>> + struct davinci_soc_info *soc_info = &davinci_soc_info;
>>> + void __iomem *psc_base;
>>> + struct clk *dsp_clk;
>>> +
>>> + /* hw requires the start (boot) address be on 1KB boundary */
>>> + if (bootaddr & 0x3ff) {
>>> + dev_err(dev, "invalid boot address: must be aligned to
>> 1KB\n");
>>> + return -EINVAL;
>>> + }
>>> +
>>> + dsp_clk = clk_get(dev, pdata->clk_name);
>> We could match using clkdev functionality, but the clock entry
>> would need to be changed then...
> I followed the existing pattern I saw in other drivers.
Probably MUSB? We're trying to move away from passing the clock name to thge
drivers, using match by device instead.
> If there is a new, better way, please point me to an example.
Look at the da850_clks[] in mach-davinci/da850.c: if the device name is
specified as a first argument to CLK() macro (instead of clock name the second
argument), the matching is done by device, so you don't need to specify the
clock name to clk_get(), passing NULL instead.
>>> + if (IS_ERR_OR_NULL(dsp_clk)) {
>>> + dev_err(dev, "clk_get error: %ld\n", PTR_ERR(dsp_clk));
>>> + return PTR_ERR(dsp_clk);
>>> + }
>>> +
>>> + clk_enable(dsp_clk);
>> This seems rather senseless activity as on DA8xx the DSP core
>> boots the ARM core, so the DSP clock will be already enabled.
> I think it is needed. It's true that the DSP initiates the boot, but then it is
> reset and the clock disabled. See Section 13.2 of
Hm, didn't know that. Contrarywise, we had to work around the races between
ARM and DSP cores on accessing locked SYSCFG registers -- in kernel. So I was
under impression that DSP code continues running some stuff of its own.
> http://focus.ti.com/lit/ug/sprugm7e/sprugm7e.pdf:
> 13.2 DSP Wake Up
> Following deassertion of device reset, the DSP intializes the ARM296 so that
> it can execute the ARM ROM bootloader. Upon successful wake up, the ARM
> places the DSP in a reset and clock gated (SwRstDisable) state that is
> controlled by the LPSC and the SYSCFG modules.
> Besides, the boot loader could have disabled it to save power. The ARM and
> DSP are clocked independently, so I think it's best to use clock management.
OK, agreed.
>>> + rproc->priv = dsp_clk;
>>> +
>>> + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K);
>>> +
>>> + /* insure local reset is asserted before writing start address */
>>> + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 *
>> DA8XX_LPSC0_GEM);
>>> +
>>> + __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG));
>> DA8XX_SYSCFG0_VIRT() is not supposed to be used outside mach-davinci. The
>> variable it refers is not exported, so driver module won't work.
> Ooops, I clearly did not build this as a module. Suggestion how to fix this?
Using the normal ioremap() of SYSCFG0 space, I suppose.
> Mark
WBR, Sergei
Hi Mark,
On Fri, Jun 24, 2011 at 20:43:50, Sergei Shtylyov wrote:
> >>> + rproc->priv = dsp_clk;
> >>> +
> >>> + psc_base = ioremap(soc_info->psc_bases[0], SZ_4K);
> >>> +
> >>> + /* insure local reset is asserted before writing start address */
> >>> + __raw_writel(NEXT_ENABLED, psc_base + MDCTL + 4 *
> >> DA8XX_LPSC0_GEM);
> >>> +
> >>> + __raw_writel(bootaddr, DA8XX_SYSCFG0_VIRT(HOST1CFG));
>
> >> DA8XX_SYSCFG0_VIRT() is not supposed to be used outside mach-davinci. The
> >> variable it refers is not exported, so driver module won't work.
>
> > Ooops, I clearly did not build this as a module. Suggestion how to fix this?
>
> Using the normal ioremap() of SYSCFG0 space, I suppose.
Since procedure to set the boot address varies across DaVinci
platforms, you could have a callback populated in platform data
which will be implemented differently for original DaVinci and
DA8xx devices.
Also, all PSC accesses are better off going through clock
framework to ensure proper locking and modularity.
To assert/de-assert local reset when enabling or disabling PSC,
you could use a flag in the clock structure to indicate the need
for this. This way, if there is any other module needing a local
reset, it can just define the same flag. Similarly, if the DSP
does not need a local reset on a particular platform, that
platform can simply skip the flag.
This can be done in a manner similar to how the support for
a forced transition PSC was added here:
https://patchwork.kernel.org/patch/662941/
Thanks,
Sekhar
On 06/21/2011 12:18 AM, Ohad Ben-Cohen wrote:
> * Rpmsg: a virtio-based messaging bus that allows kernel drivers to
> communicate with remote processors available on the system. In turn,
> drivers could then expose appropriate user space interfaces, if needed
> (tasks running on remote processors often have direct access to sensitive
> resources like the system's physical memory, gpios, i2c buses, dma
> controllers, etc.. so one normally wouldn't want to allow userland to
> send everything/everywhere it wants).
>
> Every rpmsg device is a communication channel with a service running on a
> remote processor (thus rpmsg devices are called channels). Channels are
> identified by a textual name (which is used to match drivers to devices)
> and have a local ("source") rpmsg address, and remote ("destination") rpmsg
> address. When a driver starts listening on a channel (most commonly when it
> is probed), the bus assigns the driver a unique rpmsg src address (a 32 bit
> integer) and binds it with the driver's rx callback handler. This way
> when inbound messages arrive to this src address, the rpmsg core dispatches
> them to that driver, by invoking the driver's rx handler with the payload
> of the incoming message.
>
[snip]
>
> In addition to dynamic creation of rpmsg channels, the rpmsg bus also
> supports creation of static channels. This is needed in two cases:
> - when a certain remote processor doesn't support sending those "name
> service" announcements. In that case, a static table of remote rpmsg
> services must be used to create the rpmsg channels.
> - to support rpmsg server drivers, which aren't bound to a specific remote
> rpmsg address. Instead, they just listen on a local address, waiting for
> incoming messages. To send a message, those server drivers need to use
> the rpmsg_sendto() API, so they can explicitly indicate the dst address
> every time.
>
This sounds a lot like SMD (shared memory driver) on MSM. The main
difference I see is that SMD uses the platform bus instead of the virtio
bus and it has its own protocol for channel allocation.
>
> * Remoteproc: a generic driver that maintains the state of the remote
> processor(s). Simple rproc_get() and rproc_put() API is exposed, which
> drivers can use when needed (first driver to call get() will load a firmware,
> configure an iommu if needed, and boot the remote processor, while last
> driver to call put() will power it down).
>
> Hardware differences are abstracted as usual: a platform-specific driver
> registers its own start/stop handlers in the generic remoteproc driver,
> and those are invoked when its time to power up/down the processor. As a
> reference, this patch set include remoteproc support for both OMAP4's
> cortex-M3 and Davinci's DSP, tested on the pandaboard and hawkboard,
> respectively.
This remote proc code is eerily similar to PIL (peripheral image loader,
yes we love our acronyms) which I posted a few months back[1]. Was it
inspiration for this patch series?
In terms of API, s/pil/rproc/ and it would be 95% identical. There are
some low-level differences though (see below).
>
> The gory part of remoteproc is the firmware handling. We tried to come up
> with a simple binary format that will require minimum kernel code to handle,
> but at the same time be generic enough in the hopes that it would prove
> useful to others as well. We're not at all hang onto the binary format
> we picked: if there's any technical reason to change it to support other
> platforms, please let us know. We do realize that a single binary firmware
> structure might eventually not work for everyone. it did prove useful for
> us though; we adopted both the OMAP and Davinci platforms (and their
> completely different remote processor devices) to this simple binary
> structure, so we don't have to duplicate the firmware handling code.
This is an important difference between remote proc and PIL. In PIL, we
do image authentication in addition to processor boot. In this case, we
need to give the authentication engine the elf header and program
headers for the firmware so that it knows what parts of memory need to
be authenticated. Instead of devising a new firmware format, we decided
to just stick with elf and parse the headers in the kernel because we
needed them for authentication anyway. Is this reason enough to move to
an ELF format instead?
Another difference is inter-processor dependencies. For example, on
msm8660 the modem can't boot until the dsp has been booted. I suppose we
could hide this detail in the platform specific get() implementation by
calling rproc_get() on the dependent processor (hopefully no locking
issues arise). I'd rather have it built into the core though as it isn't
really specific to the hardware.
If we can resolve these differences I think we can easily support remote
processor boot on MSM via remoteproc.
[1] https://lkml.org/lkml/2011/3/9/490
--
Sent by an employee of the Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.
Hi Stephen,
On Fri, Jun 24, 2011 at 11:16 PM, Stephen Boyd <[email protected]> wrote:
> This sounds a lot like SMD (shared memory driver) on MSM. The main
> difference I see is that SMD uses the platform bus instead of the virtio
> bus and it has its own protocol for channel allocation.
Yeah, virtio is a key factor in this work; it was suggested to us by
Arnd at the AMP plumbers discussions last year, where it was apparent
that many vendors have their own IPC drivers/buses/channels over
shared memory with some vendor-ish binary protocol. I must say we
really liked virtio: it considerably simplified the code (we're not
adding any new binary protocol), it's very nicely optimized and
flexible, and it comes with a set of virtio drivers (e.g. network,
console, block) so we don't have to write our own.
We also cared about adding this functionality as an IPC bus, so the
driver core will help matching drivers to channels - it simplified the
code (in both setup and tear down of channels) and kept it flexible.
It will also facilitate error recovery (on remote crash, we just
remove the virtio device, and then the driver core will in turn start
->remove()ing the rpmsg drivers) and power management (via runtime
PM).
About SMD: I'm not familiar with it too much, but Brian naturally is
(just for the sake of everyone who are not reading headers - Brian
Swetland wrote the Linux SMD driver, and is also an author of this
Google+TI joint work).
Btw, I'm sure SMD is conceptually not MSM-specific, and have wondered
whether you guys would like to use rpmsg/virtio (I know you have
several drivers like network/tty/etc over SMD, somewhat similarly to
virtio). Probably the biggest reason why not to is the pain in
changing the binary protocol with the modem/dsp side. If you ever do
think about it, I'd be happy to work with you to make it happen.
> This remote proc code is eerily similar to PIL (peripheral image loader,
> yes we love our acronyms) which I posted a few months back[1]. Was it
> inspiration for this patch series?
No, we weren't (or at least I wasn't) aware of PIL.
> In terms of API, s/pil/rproc/ and it would be 95% identical. There are
> some low-level differences though (see below).
Indeed, eerily similar :O
I just guess the API is so simple that probably most kernel hackers
would use refcounting get/put semantics here.
> This is an important difference between remote proc and PIL. In PIL, we
> do image authentication in addition to processor boot.
Yes, we have this too (secure code will need to authenticate the image
in some use cases) - but that's not ready yet for submission and we
decided to start off with the basics first and then evolve.
> Instead of devising a new firmware format, we decided
> to just stick with elf and parse the headers in the kernel because we
> needed them for authentication anyway. Is this reason enough to move to
> an ELF format instead?
I think that consolidation of code is enough reason to make an effort.
I know that our firmware format was chosen for simplicity, but I'm not
sure if we have the tools yet to build standard ELF files for the
remote processors (IIRC it's in the works though). I'll let Mark
comment this one.
> Another difference is inter-processor dependencies. For example, on
> msm8660 the modem can't boot until the dsp has been booted. I suppose we
> could hide this detail in the platform specific get() implementation by
> calling rproc_get() on the dependent processor (hopefully no locking
> issues arise). I'd rather have it built into the core though as it isn't
> really specific to the hardware.
No problems, I'm sure we can solve this one easily.
> If we can resolve these differences I think we can easily support remote
> processor boot on MSM via remoteproc.
That'd be very cool, I sure do hope we can work together.
Thanks for your comments !
Ohad.
On Sat, Jun 25, 2011 at 6:11 PM, Ohad Ben-Cohen <[email protected]> wrote:
> Hi Stephen,
>
> On Fri, Jun 24, 2011 at 11:16 PM, Stephen Boyd <[email protected]> wrote:
>> This sounds a lot like SMD (shared memory driver) on MSM. The main
>> difference I see is that SMD uses the platform bus instead of the virtio
>> bus and it has its own protocol for channel allocation.
>
> Yeah, virtio is a key factor in this work; it was suggested to us by
> Arnd at the AMP plumbers discussions last year, where it was apparent
> that many vendors have their own IPC drivers/buses/channels over
> shared memory with some vendor-ish binary protocol. I must say we
> really liked virtio: it considerably simplified the code (we're not
> adding any new binary protocol), it's very nicely optimized and
> flexible, and it comes with a set of virtio drivers (e.g. network,
> console, block) so we don't have to write our own.
>
> We also cared about adding this functionality as an IPC bus, so the
> driver core will help matching drivers to channels - it simplified the
> code (in both setup and tear down of channels) and kept it flexible.
> It will also facilitate error recovery (on remote crash, we just
> remove the virtio device, and then the driver core will in turn start
> ->remove()ing the rpmsg drivers) and power management (via runtime
> PM).
>
> About SMD: I'm not familiar with it too much, but Brian naturally is
> (just for the sake of everyone who are not reading headers - Brian
> Swetland wrote the Linux SMD driver, and is also an author of this
> Google+TI joint work).
rpmsg definitely shares some design features with SMD (given that I
wrote the linux SMD driver and was involved in the design of rpmsg,
this is maybe unsurprising), but whereas in SMD we had to be
compatible with existing AMSS modems (to a degree), we had more
flexibility in the "wire protocol" on rpmsg and virtio looked like a
really nice fit that already was in the kernel.
Ohad had a number of great ideas for making the dynamic discovery,
publishing, and binding of remote services very clean and
straightforward -- I wish I had a chance to pick his brain about this
stuff back when building the SMD interfaces, which started out fairly
static, but then evolved into publishing platform devices, etc. Of
course some of this is the benefit of hindsight. It's easier to get
it right (er?) the second or third time around.
The TI SYS/BIOS folks were quite helpful and patient as we redesigned
the wire protocol and publishing/resource model several times along
the way here.
Brian
> From: Sergei Shtylyov
> Sent: Friday, June 24, 2011 8:14 AM
>
> Grosen, Mark wrote:
>
> >> It should work on DA830 as well,
>
> So please make it dependent on ARCH_DAVINCI_DA8XX.
>
> >> but not on real DaVinci, so the name is misleading...
>
> > Yes, we debated calling it da8xx, but felt that with minor changes it could
> > accomodate the other SoCs in the davinci family.
>
> I don't think it's a good idea. Using cpu_is_*() is drivers is bad. Using
> #ifdef's is not an option either.
>
> > However, it may be better
> > to start with just the da8xx/omapl13x parts and then rename if we add the
> > others.
Sergei, I'll respond more to this in a response to Sekhar's ideas. We may be
able to make this work generically for davinci w/o idef's.
> Looking into my old PSC manual (can't get the recent documentation from TI's
> site right now), the bit is called LRSTz.
> It's worth moving this #define into <mach/psc.h> as well.
Ok, I agree we should try to match the HW names as much as possible
> >>> +/* register for DSP boot address in SYSCFG0 module (section 11.5.6) */
> >>> +#define HOST1CFG 0x44
>
> >> Worth declaring in <mach/da8xx.h> instead...
>
> > Possibly - since it is only used for the DSP, I thought it would be better
> > to keep local to this implementation. I'll adopt whichever approach is the
> > convention.
>
> Well, the general approach is to keep the #define's where they are
> used, so maybe we should keep this #define here.
Will review as part of the general cleanup.
>
> >>> +static inline int davinci_rproc_start(struct rproc *rproc, u64
> >> bootaddr)
> >>> +{
> >>> + struct device *dev = rproc->dev;
> >>> + struct davinci_rproc_pdata *pdata = dev->platform_data;
> >>> + struct davinci_soc_info *soc_info = &davinci_soc_info;
> >>> + void __iomem *psc_base;
> >>> + struct clk *dsp_clk;
> >>> +
> >>> + /* hw requires the start (boot) address be on 1KB boundary */
> >>> + if (bootaddr & 0x3ff) {
> >>> + dev_err(dev, "invalid boot address: must be aligned to
> >> 1KB\n");
> >>> + return -EINVAL;
> >>> + }
> >>> +
> >>> + dsp_clk = clk_get(dev, pdata->clk_name);
>
> >> We could match using clkdev functionality, but the clock entry
> >> would need to be changed then...
>
> > I followed the existing pattern I saw in other drivers.
>
> Probably MUSB? We're trying to move away from passing the clock name to thge
> drivers, using match by device instead.
>
> > If there is a new, better way, please point me to an example.
>
> Look at the da850_clks[] in mach-davinci/da850.c: if the device name is
> specified as a first argument to CLK() macro (instead of clock name the second
> argument), the matching is done by device, so you don't need to specify the
> clock name to clk_get(), passing NULL instead.
Thanks, I'll look at this and ask for Sekhar and Kevin's preferences.
Mark
> From: Nori, Sekhar
> Sent: Friday, June 24, 2011 8:44 AM
>
> Hi Mark,
Sekhar, thanks for your feedback and ideas. Comments below.
Mark
> Since procedure to set the boot address varies across DaVinci
> platforms, you could have a callback populated in platform data
> which will be implemented differently for original DaVinci and
> DA8xx devices.
I looked at DM6467 and it's the same as OMAPL13x, except at a different
address. Rather than a callback, it could be just an address in the
platform data.
>
> Also, all PSC accesses are better off going through clock
> framework to ensure proper locking and modularity.
>
> To assert/de-assert local reset when enabling or disabling PSC,
> you could use a flag in the clock structure to indicate the need
> for this. This way, if there is any other module needing a local
> reset, it can just define the same flag. Similarly, if the DSP
> does not need a local reset on a particular platform, that
> platform can simply skip the flag.
>
> This can be done in a manner similar to how the support for
> a forced transition PSC was added here:
>
> https://patchwork.kernel.org/patch/662941/
Yes, I like this idea - much cleaner. For example, the start() method
becomes (pseudo-code):
start()
{
/* bootaddrreg derived from platform data */
bootaddrreg = boot_address;
clk_enable();
}
Referring to your patch above, would it be better to just pass
the flags into the davinci_psc_config() function rather than breaking out more
arguments for each flag?
Mark
On Tue, Jun 21, 2011 at 10:18:27AM +0300, Ohad Ben-Cohen wrote:
> Some systems have slave heterogeneous remote processor devices,
> that are usually used to offload cpu-intensive computations
> (e.g. multimedia codec tasks).
>
> Booting a remote processor typically involves:
> - Loading a firmware which contains the OS image (mainly text and data)
> - If needed, programming an IOMMU
> - Powering on the device
>
> This patch introduces a generic remoteproc framework that allows drivers
> to start and stop those remote processor devices, load up their firmware
> (which might not necessarily be Linux-based), and in the future also
> support power management and error recovery.
>
> It's still not clear how much this is really reusable for other
> platforms/architectures, especially the part that deals with the
> firmware.
>
> Moreover, it's not entirely clear whether this should really be an
> independent layer, or if it should just be squashed with the host-specific
> component of the rpmsg framework (there isn't really a remoteproc use case
> that doesn't require rpmsg).
>
> That said, it did prove useful for us on two completely different
> platforms: OMAP and Davinci, each with its different remote
> processor (Cortex-M3 and a C674x DSP, respectively). So to avoid
> egregious duplication of code, remoteproc must not be omap-only.
>
> Firmware loader is based on code by Mark Grosen <[email protected]>.
>
> TODO:
> - drop rproc_da_to_pa(), use iommu_iova_to_phys() instead
> (requires completion of omap's iommu migration and some generic iommu
> API work)
> - instead of ioremapping reserved memory and handling IOMMUs, consider
> moving to the generic DMA mapping API (with a CMA backend)
>
> Signed-off-by: Ohad Ben-Cohen <[email protected]>
Hi Ohad,
Overall, looks pretty nice to me. Comments below...
> ---
> Documentation/remoteproc.txt | 170 +++++++++
> drivers/Kconfig | 2 +
> drivers/Makefile | 1 +
> drivers/remoteproc/Kconfig | 7 +
> drivers/remoteproc/Makefile | 5 +
> drivers/remoteproc/remoteproc.c | 780 +++++++++++++++++++++++++++++++++++++++
> include/linux/remoteproc.h | 273 ++++++++++++++
> 7 files changed, 1238 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/remoteproc.txt
> create mode 100644 drivers/remoteproc/Kconfig
> create mode 100644 drivers/remoteproc/Makefile
> create mode 100644 drivers/remoteproc/remoteproc.c
> create mode 100644 include/linux/remoteproc.h
>
> diff --git a/Documentation/remoteproc.txt b/Documentation/remoteproc.txt
> new file mode 100644
> index 0000000..3075813
> --- /dev/null
> +++ b/Documentation/remoteproc.txt
> @@ -0,0 +1,170 @@
> +Remote Processor Framework
> +
> +1. Introduction
> +
> +Modern SoCs typically have heterogeneous remote processor devices in asymmetric
> +multiprocessing (AMP) configurations, which may be running different instances
> +of operating system, whether it's Linux or any other flavor of real-time OS.
> +
> +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP.
> +In a typical configuration, the dual cortex-A9 is running Linux in a SMP
> +configuration, and each of the other three cores (two M3 cores and a DSP)
> +is running its own instance of RTOS in an AMP configuration.
> +
> +The generic remoteproc driver allows different platforms/architectures to
> +control (power on, load firmware, power off) those remote processors while
> +abstracting the hardware differences, so the entire driver doesn't need to be
> +duplicated.
> +
> +2. User API
> +
> + struct rproc *rproc_get(const char *name);
> + - power up the remote processor, identified by the 'name' argument,
> + and boot it. If the remote processor is already powered on, the
> + function immediately succeeds.
> + On success, returns the rproc handle. On failure, NULL is returned.
> +
> + void rproc_put(struct rproc *rproc);
> + - power off the remote processor, identified by the rproc handle.
> + Every call to rproc_get() must be (eventually) accompanied by a call
> + to rproc_put(). Calling rproc_put() redundantly is a bug.
> + Note: the remote processor will actually be powered off only when the
> + last user calls rproc_put().
> +
> +3. Typical usage
> +
> +#include <linux/remoteproc.h>
> +
> +int dummy_rproc_example(void)
> +{
> + struct rproc *my_rproc;
> +
> + /* let's power on and boot the image processing unit */
> + my_rproc = rproc_get("ipu");
I tend to be suspicious of apis whose primary interface is by-name
lookup. It works fine when the system is small, but it can get
unwieldy when the client driver doesn't have a direct relation to the
setup code that chooses the name. At some point I suspect that there
will need to be different lookup mechanism, such as which AMP
processor is currently available (if there are multiple of the same
type).
It also leaves no option for drivers to obtain a reference to the
rproc instance, and bring it up/down as needed (without the name
lookup every time).
That said, it looks like only the rproc_get() api is using by-name
lookup, and everything else is via the structure. Can (should) the
by-name lookup part be factored out into a rproc_get_by_name()
accessor?
> + if (!my_rproc) {
> + /*
> + * something went wrong. handle it and leave.
> + */
> + }
> +
> + /*
> + * the 'ipu' remote processor is now powered on... let it work !
> + */
> +
> + /* if we no longer need ipu's services, power it down */
> + rproc_put(my_rproc);
> +}
> +
> +4. API for implementors
> +
> + int rproc_register(struct device *dev, const char *name,
> + const struct rproc_ops *ops,
> + const char *firmware,
> + const struct rproc_mem_entry *memory_maps,
> + struct module *owner);
> + - should be called from the underlying platform-specific implementation, in
> + order to register a new remoteproc device. 'dev' is the underlying
> + device, 'name' is the name of the remote processor, which will be
> + specified by users calling rproc_get(), 'ops' is the platform-specific
> + start/stop handlers, 'firmware' is the name of the firmware file to
> + boot the processor with, 'memory_maps' is a table of da<->pa memory
> + mappings which should be used to configure the IOMMU (if not relevant,
> + just pass NULL here), 'owner' is the underlying module that should
> + not be removed while the remote processor is in use.
Since rproc_register is allocating a struct rproc instance that
represent the device, shouldn't the pointer to that device be returned
to the caller? Also, consider the use case that at some point someone
will need separate rproc_alloc and rproc_add steps so that it can
modify the structure between allocating and adding. Otherwise you're
stuck in the model of having to modify the function signature to
rproc_register() every time a new feature is added that required
additional data; regardless of whether or not all drivers will use it.
> +
> + Returns 0 on success, or an appropriate error code on failure.
> +
> + int rproc_unregister(const char *name);
I definitely would not do this by name. I think it is better to pass
the actual instance pointer to rproc_unregister.
> + - should be called from the underlying platform-specific implementation, in
> + order to unregister a remoteproc device that was previously registered
> + with rproc_register().
> +
> +5. Implementation callbacks
> +
> +Every remoteproc implementation must provide these handlers:
> +
> +struct rproc_ops {
> + int (*start)(struct rproc *rproc, u64 bootaddr);
> + int (*stop)(struct rproc *rproc);
> +};
> +
> +The ->start() handler takes a rproc handle and an optional bootaddr argument,
> +and should power on the device and boot it (using the bootaddr argument
> +if the hardware requires one).
Naive question: Why is bootaddr an argument? Wouldn't rproc drivers
keep track of the boot address in their driver private data?
> +On success, 0 is returned, and on failure, an appropriate error code.
> +
> +The ->stop() handler takes a rproc handle and powers the device off.
> +On success, 0 is returned, and on failure, an appropriate error code.
> +
> +6. Binary Firmware Structure
> +
> +The following enums and structures define the binary format of the images
> +remoteproc loads and boot the remote processors with.
> +
> +The general binary format is as follows:
> +
> +struct {
> + char magic[4] = { 'R', 'P', 'R', 'C' };
> + u32 version;
> + u32 header_len;
> + char header[...] = { header_len bytes of unformatted, textual header };
> + struct section {
> + u32 type;
> + u64 da;
> + u32 len;
> + u8 content[...] = { len bytes of binary data };
> + } [ no limit on number of sections ];
> +} __packed;
Other have commented on the image format, so I'll skip this bit other
than saying that I agree it would be great to have a common format.
> +Most likely this kind of static allocations of hardware resources for
> +remote processors can also use DT, so it's interesting to see how
> +this all work out when DT materializes.
I imagine that it will be quite straight forward. There will probably
be a node in the tree to represent each slave AMP processor, and other
devices attached to it could be represented using 'phandle' links
between the nodes. Any configuration of the AMP process can be
handled with arbitrary device-specific properties in the AMP
processor's node.
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 3bb154d..1f6d6d3 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -126,4 +126,6 @@ source "drivers/hwspinlock/Kconfig"
>
> source "drivers/clocksource/Kconfig"
>
> +source "drivers/remoteproc/Kconfig"
> +
Hmmm, I wonder if the end of the drivers list is the best place for
this. The drivers menu in kconfig is getting quite unwieldy.
> endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 09f3232..4d53a18 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -122,3 +122,4 @@ obj-y += ieee802154/
> obj-y += clk/
>
> obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
> +obj-$(CONFIG_REMOTE_PROC) += remoteproc/
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> new file mode 100644
> index 0000000..a60bb12
> --- /dev/null
> +++ b/drivers/remoteproc/Kconfig
> @@ -0,0 +1,7 @@
> +#
> +# Generic framework for controlling remote processors
> +#
> +
> +# REMOTE_PROC gets selected by whoever wants it
> +config REMOTE_PROC
> + tristate
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> new file mode 100644
> index 0000000..d0f60c7
> --- /dev/null
> +++ b/drivers/remoteproc/Makefile
> @@ -0,0 +1,5 @@
> +#
> +# Generic framework for controlling remote processors
> +#
> +
> +obj-$(CONFIG_REMOTE_PROC) += remoteproc.o
> diff --git a/drivers/remoteproc/remoteproc.c b/drivers/remoteproc/remoteproc.c
> new file mode 100644
> index 0000000..2b0514b
> --- /dev/null
> +++ b/drivers/remoteproc/remoteproc.c
> @@ -0,0 +1,780 @@
> +/*
> + * Remote Processor Framework
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + * Copyright (C) 2011 Google, Inc.
> + *
> + * Ohad Ben-Cohen <[email protected]>
> + * Mark Grosen <[email protected]>
> + * Brian Swetland <[email protected]>
> + * Fernando Guzman Lugo <[email protected]>
> + * Robert Tivy <[email protected]>
> + * Armando Uribe De Leon <[email protected]>
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/platform_device.h>
> +#include <linux/firmware.h>
> +#include <linux/io.h>
> +#include <linux/list.h>
> +#include <linux/debugfs.h>
> +#include <linux/remoteproc.h>
> +
> +/* list of the available remote processors */
> +static LIST_HEAD(rprocs);
> +/*
> + * This lock should be taken when the list of rprocs is accessed.
> + * Consider using RCU instead, since remote processors only get registered
> + * once (usually at boot), and then the list is only read accessed.
> + * Though right now the list is pretty short, and only rarely accessed.
> + */
> +static DEFINE_SPINLOCK(rprocs_lock);
> +
> +/* debugfs parent dir */
> +static struct dentry *rproc_dbg;
> +
> +/*
> + * Some remote processors may support dumping trace logs into a shared
> + * memory buffer. We expose this trace buffer using debugfs, so users
> + * can easily tell what's going on remotely.
> + */
> +static ssize_t rproc_format_trace_buf(char __user *userbuf, size_t count,
> + loff_t *ppos, const void *src, int size)
> +{
> + const char *buf = (const char *) src;
> + int i;
> +
> + /*
> + * find the end of trace buffer (does not account for wrapping).
> + * desirable improvement: use a ring buffer instead.
> + */
> + for (i = 0; i < size && buf[i]; i++);
Hmmm, I wonder if this could make use of the ftrace ring buffer.
> +
> + return simple_read_from_buffer(userbuf, count, ppos, src, i);
> +}
> +
> +static int rproc_open_generic(struct inode *inode, struct file *file)
> +{
> + file->private_data = inode->i_private;
> + return 0;
> +}
> +
> +#define DEBUGFS_READONLY_FILE(name, value, len) \
> +static ssize_t name## _rproc_read(struct file *filp, \
> + char __user *userbuf, size_t count, loff_t *ppos) \
> +{ \
> + struct rproc *rproc = filp->private_data; \
> + return rproc_format_trace_buf(userbuf, count, ppos, value, len);\
> +} \
> + \
> +static const struct file_operations name ##_rproc_ops = { \
> + .read = name ##_rproc_read, \
> + .open = rproc_open_generic, \
> + .llseek = generic_file_llseek, \
> +};
> +
> +/*
> + * Currently we allow two trace buffers for each remote processor.
> + * This is helpful in case a single remote processor has two independent
> + * cores, each of which is running an independent OS image.
> + * The current implementation is straightforward and simple, and is
> + * rather limited to 2 trace buffers. If, in time, we'd realize we
> + * need additional trace buffers, then the code should be refactored
> + * and generalized.
> + */
> +DEBUGFS_READONLY_FILE(trace0, rproc->trace_buf0, rproc->trace_len0);
> +DEBUGFS_READONLY_FILE(trace1, rproc->trace_buf1, rproc->trace_len1);
> +
> +/* The state of the remote processor is exposed via debugfs, too */
> +const char *rproc_state_string(int state)
> +{
> + const char *result;
> +
> + switch (state) {
> + case RPROC_OFFLINE:
> + result = "offline";
> + break;
> + case RPROC_SUSPENDED:
> + result = "suspended";
> + break;
> + case RPROC_RUNNING:
> + result = "running";
> + break;
> + case RPROC_LOADING:
> + result = "loading";
> + break;
> + case RPROC_CRASHED:
> + result = "crashed";
> + break;
> + default:
> + result = "invalid state";
> + break;
> + }
Me thinks this is asking for a lookup table.
> +
> + return result;
> +}
> +
> +static ssize_t rproc_state_read(struct file *filp, char __user *userbuf,
> + size_t count, loff_t *ppos)
> +{
> + struct rproc *rproc = filp->private_data;
> + int state = rproc->state;
> + char buf[100];
100 bytes? I count at most ~30.
> + int i;
> +
> + i = snprintf(buf, 100, "%s (%d)\n", rproc_state_string(state), state);
> +
> + return simple_read_from_buffer(userbuf, count, ppos, buf, i);
> +}
> +
> +static const struct file_operations rproc_state_ops = {
> + .read = rproc_state_read,
> + .open = rproc_open_generic,
> + .llseek = generic_file_llseek,
> +};
> +
> +/* The name of the remote processor is exposed via debugfs, too */
> +static ssize_t rproc_name_read(struct file *filp, char __user *userbuf,
> + size_t count, loff_t *ppos)
> +{
> + struct rproc *rproc = filp->private_data;
> + /* need room for the name, a newline and a terminating null */
> + char buf[RPROC_MAX_NAME + 2];
> + int i;
> +
> + i = snprintf(buf, RPROC_MAX_NAME + 2, "%s\n", rproc->name);
> +
> + return simple_read_from_buffer(userbuf, count, ppos, buf, i);
> +}
> +
> +static const struct file_operations rproc_name_ops = {
> + .read = rproc_name_read,
> + .open = rproc_open_generic,
> + .llseek = generic_file_llseek,
> +};
> +
> +#define DEBUGFS_ADD(name) \
> + debugfs_create_file(#name, 0400, rproc->dbg_dir, \
> + rproc, &name## _rproc_ops)
You might want to split the debug stuff off into a separate patch,
just to keep the review load down. (up to you though).
> +
> +/**
> + * __find_rproc_by_name() - find a registered remote processor by name
> + * @name: name of the remote processor
> + *
> + * Internal function that returns the rproc @name, or NULL if @name does
> + * not exists.
> + */
> +static struct rproc *__find_rproc_by_name(const char *name)
> +{
> + struct rproc *rproc;
> + struct list_head *tmp;
> +
> + spin_lock(&rprocs_lock);
> +
> + list_for_each(tmp, &rprocs) {
> + rproc = list_entry(tmp, struct rproc, next);
> + if (!strcmp(rproc->name, name))
> + break;
> + rproc = NULL;
> + }
> +
> + spin_unlock(&rprocs_lock);
Unless you're going to be looking up the device at irq time, a mutex
is probably a better choice here.
> +
> + return rproc;
> +}
> +
[ignoring da_to_pa bits because they are subject to change]
> +
> +/**
> + * rproc_start() - boot the remote processor
> + * @rproc: the remote processor
> + * @bootaddr: address of first instruction to execute (optional)
> + *
> + * Boot a remote processor (i.e. power it on, take it out of reset, etc..)
> + */
> +static void rproc_start(struct rproc *rproc, u64 bootaddr)
> +{
> + struct device *dev = rproc->dev;
> + int err;
> +
> + err = mutex_lock_interruptible(&rproc->lock);
> + if (err) {
> + dev_err(dev, "can't lock remote processor %d\n", err);
> + return;
> + }
> +
> + err = rproc->ops->start(rproc, bootaddr);
> + if (err) {
> + dev_err(dev, "can't start rproc %s: %d\n", rproc->name, err);
> + goto unlock_mutex;
> + }
> +
> + rproc->state = RPROC_RUNNING;
> +
> + dev_info(dev, "remote processor %s is now up\n", rproc->name);
How often are remote processors likely to be brought up/down? Do PM
events hard stop remote processors? I only ask because I wonder if
this dev_info() will end up flooding the kernel log.
> +/**
> + * rproc_get() - boot the remote processor
> + * @name: name of the remote processor
> + *
> + * Boot a remote processor (i.e. load its firmware, power it on, take it
> + * out of reset, etc..).
> + *
> + * If the remote processor is already powered on, immediately return
> + * its rproc handle.
> + *
> + * On success, returns the rproc handle. On failure, NULL is returned.
> + */
> +struct rproc *rproc_get(const char *name)
> +{
> + struct rproc *rproc, *ret = NULL;
> + struct device *dev;
> + int err;
> +
> + rproc = __find_rproc_by_name(name);
> + if (!rproc) {
> + pr_err("can't find remote processor %s\n", name);
> + return NULL;
> + }
> +
> + dev = rproc->dev;
> +
> + err = mutex_lock_interruptible(&rproc->lock);
> + if (err) {
> + dev_err(dev, "can't lock remote processor %s\n", name);
> + return NULL;
> + }
> +
> + /* prevent underlying implementation from being removed */
> + if (!try_module_get(rproc->owner)) {
> + dev_err(dev, "%s: can't get owner\n", __func__);
> + goto unlock_mutex;
> + }
> +
> + /* skip the boot process if rproc is already (being) powered up */
> + if (rproc->count++) {
> + ret = rproc;
> + goto unlock_mutex;
> + }
> +
> + /* rproc_put() calls should wait until async loader completes */
> + init_completion(&rproc->firmware_loading_complete);
> +
> + dev_info(dev, "powering up %s\n", name);
> +
> + /* loading a firmware is required */
> + if (!rproc->firmware) {
> + dev_err(dev, "%s: no firmware to load\n", __func__);
> + goto deref_rproc;
> + }
> +
> + /*
> + * Initiate an asynchronous firmware loading, to allow building
> + * remoteproc as built-in kernel code, without hanging the boot process
> + */
> + err = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
> + rproc->firmware, dev, GFP_KERNEL, rproc, rproc_load_fw);
> + if (err < 0) {
> + dev_err(dev, "request_firmware_nowait failed: %d\n", err);
> + goto deref_rproc;
> + }
> +
> + rproc->state = RPROC_LOADING;
> + ret = rproc;
> + goto unlock_mutex;
> +
> +deref_rproc:
> + complete_all(&rproc->firmware_loading_complete);
> + module_put(rproc->owner);
> + --rproc->count;
> +unlock_mutex:
> + mutex_unlock(&rproc->lock);
> + return ret;
> +}
> +EXPORT_SYMBOL(rproc_get);
> +
> +/**
> + * rproc_put() - power off the remote processor
> + * @rproc: the remote processor
> + *
> + * Release an rproc handle previously acquired with rproc_get(),
> + * and if we're the last user, power the processor off.
> + *
> + * Every call to rproc_get() must be (eventually) accompanied by a call
> + * to rproc_put(). Calling rproc_put() redundantly is a bug.
> + */
> +void rproc_put(struct rproc *rproc)
> +{
> + struct device *dev = rproc->dev;
> + int ret;
> +
> + /*
> + * make sure rproc_get() was called beforehand.
> + * it should be safe to check for zero without taking the lock.
> + */
However, it may be non-zero here, but drop to zero by the time you
take the lock. Best be safe and put it inside the mutex. Having it
under the mutex shouldn't be a performance hit since only buggy code
will get this test wrong. In fact, it is probably appropriate to
WARN_ON() on the !rproc->count condition.
Actually, using a hand coded reference count like this shouldn't be
done. Use a kobject or a kref instead. Looking at the code, I
suspect you'll want separate reference counting for object references
and power up/down count so that clients can control power to a device
without giving up the pointer to the rproc instance.
> + if (!rproc->count) {
> + dev_err(dev, "asymmetric put (fogot to call rproc_get ?)\n");
> + ret = -EINVAL;
> + goto out;
> + }
> +
> + /* if rproc is just being loaded now, wait */
> + wait_for_completion(&rproc->firmware_loading_complete);
> +
> + ret = mutex_lock_interruptible(&rproc->lock);
> + if (ret) {
> + dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
> + return;
> + }
> +
> + /* if the remote proc is still needed, bail out */
> + if (--rproc->count)
> + goto out;
> +
> + if (rproc->trace_buf0)
> + /* iounmap normal memory, so make sparse happy */
> + iounmap((__force void __iomem *) rproc->trace_buf0);
> + if (rproc->trace_buf1)
> + /* iounmap normal memory, so make sparse happy */
> + iounmap((__force void __iomem *) rproc->trace_buf1);
Icky casting! That suggests that how the trace buffer pointer is
managed needs work.
> +
> + rproc->trace_buf0 = rproc->trace_buf1 = NULL;
> +
> + /*
> + * make sure rproc is really running before powering it off.
> + * this is important, because the fw loading might have failed.
> + */
> + if (rproc->state == RPROC_RUNNING) {
> + ret = rproc->ops->stop(rproc);
> + if (ret) {
> + dev_err(dev, "can't stop rproc: %d\n", ret);
> + goto out;
> + }
> + }
> +
> + rproc->state = RPROC_OFFLINE;
> +
> + dev_info(dev, "stopped remote processor %s\n", rproc->name);
> +
> +out:
> + mutex_unlock(&rproc->lock);
> + if (!ret)
> + module_put(rproc->owner);
> +}
> +EXPORT_SYMBOL(rproc_put);
> +
> +/**
> + * rproc_register() - register a remote processor
> + * @dev: the underlying device
> + * @name: name of this remote processor
> + * @ops: platform-specific handlers (mainly start/stop)
> + * @firmware: name of firmware file to load
> + * @memory_maps: IOMMU settings for this rproc (optional)
> + * @owner: owning module
> + *
> + * Registers a new remote processor in the remoteproc framework.
> + *
> + * This is called by the underlying platform-specific implementation,
> + * whenever a new remote processor device is probed.
> + *
> + * On succes, 0 is return, and on failure an appropriate error code.
> + */
> +int rproc_register(struct device *dev, const char *name,
> + const struct rproc_ops *ops,
> + const char *firmware,
> + const struct rproc_mem_entry *memory_maps,
> + struct module *owner)
> +{
> + struct rproc *rproc;
> +
> + if (!dev || !name || !ops)
> + return -EINVAL;
> +
> + rproc = kzalloc(sizeof(struct rproc), GFP_KERNEL);
> + if (!rproc) {
> + dev_err(dev, "%s: kzalloc failed\n", __func__);
> + return -ENOMEM;
> + }
> +
> + rproc->dev = dev;
> + rproc->name = name;
> + rproc->ops = ops;
> + rproc->firmware = firmware;
> + rproc->memory_maps = memory_maps;
> + rproc->owner = owner;
> +
> + mutex_init(&rproc->lock);
> +
> + rproc->state = RPROC_OFFLINE;
> +
> + spin_lock(&rprocs_lock);
> + list_add_tail(&rproc->next, &rprocs);
> + spin_unlock(&rprocs_lock);
> +
> + dev_info(dev, "%s is available\n", name);
> +
> + if (!rproc_dbg)
> + goto out;
> +
> + rproc->dbg_dir = debugfs_create_dir(dev_name(dev), rproc_dbg);
> + if (!rproc->dbg_dir) {
> + dev_err(dev, "can't create debugfs dir\n");
> + goto out;
> + }
> +
> + debugfs_create_file("name", 0400, rproc->dbg_dir, rproc,
> + &rproc_name_ops);
> + debugfs_create_file("state", 0400, rproc->dbg_dir, rproc,
> + &rproc_state_ops);
> +
> +out:
> + return 0;
> +}
> +EXPORT_SYMBOL(rproc_register);
> +
> +/**
> + * rproc_unregister() - unregister a remote processor
> + * @name: name of this remote processor
> + *
> + * Unregisters a remote processor.
> + *
> + * On succes, 0 is return. If this remote processor isn't found, -EINVAL
> + * is returned.
> + */
> +int rproc_unregister(const char *name)
> +{
> + struct rproc *rproc;
> +
> + rproc = __find_rproc_by_name(name);
> + if (!rproc) {
> + pr_err("can't find remote processor %s\n", name);
> + return -EINVAL;
> + }
> +
> + dev_info(rproc->dev, "removing %s\n", name);
> +
> + if (rproc->dbg_dir)
> + debugfs_remove_recursive(rproc->dbg_dir);
> +
> + spin_lock(&rprocs_lock);
> + list_del(&rproc->next);
> + spin_unlock(&rprocs_lock);
> +
> + kfree(rproc);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL(rproc_unregister);
> +
> +static int __init remoteproc_init(void)
> +{
> + if (debugfs_initialized()) {
> + rproc_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
> + if (!rproc_dbg)
> + pr_err("can't create debugfs dir\n");
> + }
> +
> + return 0;
> +}
> +/* must be ready in time for device_initcall users */
> +subsys_initcall(remoteproc_init);
> +
> +static void __exit remoteproc_exit(void)
> +{
> + if (rproc_dbg)
> + debugfs_remove(rproc_dbg);
> +}
> +module_exit(remoteproc_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Generic Remote Processor Framework");
> diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
> new file mode 100644
> index 0000000..6cdb966
> --- /dev/null
> +++ b/include/linux/remoteproc.h
> @@ -0,0 +1,273 @@
> +/*
> + * Remote Processor Framework
> + *
> + * Copyright(c) 2011 Texas Instruments, Inc.
> + * Copyright(c) 2011 Google, Inc.
> + * All rights reserved.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + *
> + * * Redistributions of source code must retain the above copyright
> + * notice, this list of conditions and the following disclaimer.
> + * * Redistributions in binary form must reproduce the above copyright
> + * notice, this list of conditions and the following disclaimer in
> + * the documentation and/or other materials provided with the
> + * distribution.
> + * * Neither the name Texas Instruments nor the names of its
> + * contributors may be used to endorse or promote products derived
> + * from this software without specific prior written permission.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
> + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
> + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
> + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
> + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
> + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
> + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
> + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
> + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
> + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
> + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#ifndef REMOTEPROC_H
> +#define REMOTEPROC_H
> +
> +#include <linux/mutex.h>
> +#include <linux/completion.h>
> +
> +/**
> + * DOC: The Binary Structure of the Firmware
> + *
> + * The following enums and structures define the binary format of the image
> + * we load and run the remote processors with.
> + *
> + * The binary format is as follows:
> + *
> + * struct {
> + * char magic[4] = { 'R', 'P', 'R', 'C' };
> + * u32 version;
> + * u32 header_len;
> + * char header[...] = { header_len bytes of unformatted, textual header };
> + * struct section {
> + * u32 type;
> + * u64 da;
> + * u32 len;
> + * u8 content[...] = { len bytes of binary data };
> + * } [ no limit on number of sections ];
> + * } __packed;
> + */
> +
> +/**
> + * struct fw_header - header of the firmware image
> + * @magic: 4-bytes magic (should contain "RPRC")
> + * @version: version number, should be bumped on binary changes
> + * @header_len: length, in bytes, of the following text header
> + * @header: free-style textual header, users can read with 'head'
> + *
> + * This structure defines the header of the remoteproc firmware.
> + */
> +struct fw_header {
> + char magic[4];
> + u32 version;
> + u32 header_len;
> + char header[0];
> +} __packed;
> +
> +/**
> + * struct fw_section - header of a firmware section
> + * @type: section type
> + * @da: device address that the rproc expects to find this section at.
> + * @len: length of the section (in bytes)
> + * @content: the section data
> + *
> + * This structure defines the header of a firmware section. All sections
> + * should be loaded to the address specified by @da, so the remote processor
> + * will find them.
> + *
> + * Note: if the remote processor is not behind an IOMMU, then da is a
> + * mere physical address
> + */
> +struct fw_section {
> + u32 type;
> + u64 da;
> + u32 len;
> + char content[0];
> +} __packed;
> +
> +/**
> + * enum fw_section_type - section type values
> + *
> + * @FW_RESOURCE: a resource section. this section contains static
> + * resource requests (/announcements) that the remote
> + * processor requires (/supports). Most of these requests
> + * require that the host fulfill them (and usually
> + * "reply" with a result) before the remote processor
> + * is booted. See Documentation/remoteproc.h for more info
> + * @FW_TEXT: a text section
> + * @FW_DATA: a data section
> + *
> + * Note: text and data sections have different types so we can support stuff
> + * like crash dumps (which only requires dumping data sections) or loading
> + * text sections into faster memory. Currently, though, both section types
> + * are treated exactly the same.
> + */
> +enum fw_section_type {
> + FW_RESOURCE = 0,
> + FW_TEXT = 1,
> + FW_DATA = 2,
> +};
> +
> +/**
> + * struct fw_resource - describes an entry from the resource section
> + * @type: resource type
> + * @da: depends on the resource type
> + * @pa: depends on the resource type
> + * @len: depends on the resource type
> + * @flags: depends on the resource type
> + * @name: name of resource
> + *
> + * Some resources entries are mere announcements, where the host is informed
> + * of specific remoteproc configuration. Other entries require the host to
> + * do something (e.g. reserve a requested resource) and reply by overwriting
> + * a member inside struct fw_resource with the id of the allocated resource.
> + * There could also be resource entries where the remoteproc's image suggests
> + * a configuration, but the host may overwrite it with its own preference.
> + *
> + * Note: the vast majority of the resource types are not implemented yet,
> + * and this is all very much preliminary.
> + */
> +struct fw_resource {
> + u32 type;
> + u64 da;
> + u64 pa;
> + u32 len;
> + u32 flags;
> + u8 name[48];
> +} __packed;
> +
> +/**
> + * enum fw_resource_type - types of resource entries
> + *
> + * @RSC_TRACE: announces the availability of a trace buffer into which
> + * the remote processor will be writing logs. In this case,
> + * 'da' indicates the device address where logs are written to,
> + * and 'len' is the size of the trace buffer.
> + * Currently we support two trace buffers per remote processor,
> + * to support two autonomous cores running in a single rproc
> + * device.
> + * If additional trace buffers are needed, this should be
> + * extended/generalized.
> + * @RSC_BOOTADDR: announces the address of the first instruction the remote
> + * processor should be booted with (address indicated in 'da').
> + *
> + * Note: most of the resource types are not implemented yet, so they are
> + * not documented yet.
> + */
> +enum fw_resource_type {
> + RSC_CARVEOUT = 0,
> + RSC_DEVMEM = 1,
> + RSC_DEVICE = 2,
> + RSC_IRQ = 3,
> + RSC_TRACE = 4,
> + RSC_BOOTADDR = 5,
> +};
> +
> +/**
> + * struct rproc_mem_entry - memory mapping descriptor
> + * @da: device address as seen by the remote processor
> + * @pa: physical address
> + * @size: size of this memory region
> + *
> + * Board file will use this struct to define the IOMMU configuration
> + * for this remote processor. If the rproc device accesses physical memory
> + * directly (and not through an IOMMU), this is not needed.
> + */
> +struct rproc_mem_entry {
> + u64 da;
> + phys_addr_t pa;
> + u32 size;
> +};
> +
> +struct rproc;
> +
> +/**
> + * struct rproc_ops - platform-specific device handlers
> + * @start: power on the device and boot it. implementation may require
> + * specifyng a boot address
> + * @stop: power off the device
> + */
> +struct rproc_ops {
> + int (*start)(struct rproc *rproc, u64 bootaddr);
> + int (*stop)(struct rproc *rproc);
> +};
> +
> +/*
> + * enum rproc_state - remote processor states
> + *
> + * @RPROC_OFFLINE: device is powered off
> + * @RPROC_SUSPENDED: device is suspended; needs to be woken up to receive
> + * a message.
> + * @RPROC_RUNNING: device is up and running
> + * @RPROC_LOADING: asynchronous firmware loading has started
> + * @RPROC_CRASHED: device has crashed; need to start recovery
> + */
> +enum rproc_state {
> + RPROC_OFFLINE,
> + RPROC_SUSPENDED,
> + RPROC_RUNNING,
> + RPROC_LOADING,
> + RPROC_CRASHED,
> +};
> +
> +#define RPROC_MAX_NAME 100
I wouldn't even bother with this. The only place it is used is in one
of the debugfs files, and you can protect against too large a static
buffer by using %100s (or whatever) in the snprintf().
> +
> +/*
> + * struct rproc - represents a physical remote processor device
> + *
> + * @next: next rproc entry in the list
> + * @name: human readable name of the rproc, cannot exceed RPROC_MAX_NAME bytes
> + * @memory_maps: table of da-to-pa memory maps (relevant if device is behind
> + * an iommu)
> + * @firmware: name of firmware file to be loaded
> + * @owner: reference to the platform-specific rproc module
> + * @priv: private data which belongs to the platform-specific rproc module
> + * @ops: platform-specific start/stop rproc handlers
> + * @dev: underlying device
> + * @count: usage refcount
> + * @state: state of the device
> + * @lock: lock which protects concurrent manipulations of the rproc
> + * @dbg_dir: debugfs directory of this rproc device
> + * @trace_buf0: main trace buffer of the remote processor
> + * @trace_buf1: second, optional, trace buffer of the remote processor
> + * @trace_len0: length of main trace buffer of the remote processor
> + * @trace_len1: length of the second (and optional) trace buffer
> + * @firmware_loading_complete: marks e/o asynchronous firmware loading
> + */
> +struct rproc {
> + struct list_head next;
> + const char *name;
> + const struct rproc_mem_entry *memory_maps;
> + const char *firmware;
> + struct module *owner;
> + void *priv;
> + const struct rproc_ops *ops;
> + struct device *dev;
> + int count;
> + int state;
> + struct mutex lock;
> + struct dentry *dbg_dir;
> + char *trace_buf0, *trace_buf1;
> + int trace_len0, trace_len1;
> + struct completion firmware_loading_complete;
> +};
> +
> +struct rproc *rproc_get(const char *);
> +void rproc_put(struct rproc *);
> +int rproc_register(struct device *, const char *, const struct rproc_ops *,
> + const char *, const struct rproc_mem_entry *, struct module *);
> +int rproc_unregister(const char *);
> +
> +#endif /* REMOTEPROC_H */
> --
> 1.7.1
>
On Tue, Jun 21, 2011 at 10:18:28AM +0300, Ohad Ben-Cohen wrote:
> From: Guzman Lugo, Fernando <[email protected]>
>
> Add remoteproc implementation for OMAP4, so we can load the M3 and DSP
> remote processors.
>
> Based on code by Hari Kanigeri <[email protected]>
>
> While this code is functional, and works on OMAP4 & its M3's,
> it is still preliminary and going to substantially change:
>
> 1. Splitting physically contiguous regions to pages should happen
> inside the IOMMU API framework, and not here (so omap_rproc_map_unmap
> should go away).
> 2. IOMMU programming in general should go away too; it should be handled by
> generic code (preferably using the DMA mapping API), and not by an OMAP
> specific component.
>
> This patch depends on OMAP's IOMMU API migration, as posted here:
> https://lkml.org/lkml/2011/6/2/369
>
> [[email protected]: commit log, generic iommu api migration, refactoring, cleanups]
>
> Signed-off-by: Guzman Lugo, Fernando <[email protected]>
> Signed-off-by: Ohad Ben-Cohen <[email protected]>
> ---
> arch/arm/plat-omap/include/plat/remoteproc.h | 41 +++++
> drivers/remoteproc/Kconfig | 21 +++
> drivers/remoteproc/Makefile | 1 +
> drivers/remoteproc/omap_remoteproc.c | 243 ++++++++++++++++++++++++++
> 4 files changed, 306 insertions(+), 0 deletions(-)
> create mode 100644 arch/arm/plat-omap/include/plat/remoteproc.h
> create mode 100644 drivers/remoteproc/omap_remoteproc.c
>
> diff --git a/arch/arm/plat-omap/include/plat/remoteproc.h b/arch/arm/plat-omap/include/plat/remoteproc.h
> new file mode 100644
> index 0000000..6494570
> --- /dev/null
> +++ b/arch/arm/plat-omap/include/plat/remoteproc.h
> @@ -0,0 +1,41 @@
> +/*
> + * Remote Processor - omap-specific bits
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#ifndef _PLAT_REMOTEPROC_H
> +#define _PLAT_REMOTEPROC_H
> +
> +#include <linux/remoteproc.h>
> +
> +/*
> + * struct omap_rproc_pdata - omap remoteproc's platform data
> + * @name: the remoteproc's name, cannot exceed RPROC_MAX_NAME bytes
> + * @iommu_name: iommu device we're behind of
> + * @oh_name: omap hwmod device
> + * @oh_name_opt: optional, secondary omap hwmod device
> + * @firmware: name of firmware file to load
> + * @ops: start/stop rproc handlers
> + * @memory_maps: table of da-to-pa iommu memory maps
> + */
> +struct omap_rproc_pdata {
> + const char *name;
> + const char *iommu_name;
> + const char *oh_name;
> + const char *oh_name_opt;
> + const char *firmware;
> + const struct rproc_ops *ops;
> + const struct rproc_mem_entry *memory_maps;
> +};
> +
> +#endif /* _PLAT_REMOTEPROC_H */
> diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
> index a60bb12..88421bd 100644
> --- a/drivers/remoteproc/Kconfig
> +++ b/drivers/remoteproc/Kconfig
> @@ -5,3 +5,24 @@
> # REMOTE_PROC gets selected by whoever wants it
> config REMOTE_PROC
> tristate
> +
> +# for tristate, either expose omap_device_* or pass it via pdata
> +config OMAP_REMOTE_PROC
> + bool "OMAP remoteproc support"
> + depends on ARCH_OMAP4
> + select OMAP_IOMMU
> + select REMOTE_PROC
> + select OMAP_MBOX_FWK
> + default y
> + help
> + Say y here to support OMAP's remote processors (dual M3
> + and DSP on OMAP4) via the remote processor framework.
> +
> + Currently only supported on OMAP4.
> +
> + Usually you want to say y here, in order to enable multimedia
> + use-cases to run on your platform (multimedia codecs are
> + offloaded to remote DSP processors using this framework).
> +
> + It's safe to say n here if you're not interested in multimedia
> + offloading or just want a bare minimum kernel.
> diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
> index d0f60c7..0198b1d 100644
> --- a/drivers/remoteproc/Makefile
> +++ b/drivers/remoteproc/Makefile
> @@ -3,3 +3,4 @@
> #
>
> obj-$(CONFIG_REMOTE_PROC) += remoteproc.o
> +obj-$(CONFIG_OMAP_REMOTE_PROC) += omap_remoteproc.o
> diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
> new file mode 100644
> index 0000000..91f7f11
> --- /dev/null
> +++ b/drivers/remoteproc/omap_remoteproc.c
> @@ -0,0 +1,243 @@
> +/*
> + * Remote processor machine-specific module for OMAP4
> + *
> + * Copyright (C) 2011 Texas Instruments, Inc.
> + *
> + * 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 program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + */
> +
> +#define pr_fmt(fmt) "%s: " fmt, __func__
> +
> +#include <linux/kernel.h>
> +#include <linux/err.h>
> +#include <linux/platform_device.h>
> +#include <linux/iommu.h>
> +#include <linux/slab.h>
> +#include <linux/remoteproc.h>
> +
> +#include <plat/iommu.h>
> +#include <plat/omap_device.h>
> +#include <plat/remoteproc.h>
> +
> +struct omap_rproc_priv {
> + struct iommu_domain *domain;
> + struct device *iommu;
> +};
> +
> +/*
> + * Map a physically contiguous memory region using biggest pages possible.
> + * TODO: this code should go away; it belongs in the generic IOMMU layer
> + */
> +static int omap_rproc_map_unmap(struct iommu_domain *domain,
> + const struct rproc_mem_entry *me, bool map)
> +{
> + u32 all_bits;
> + /* these are the page sizes supported by OMAP's IOMMU */
> + u32 pg_size[] = {SZ_16M, SZ_1M, SZ_64K, SZ_4K};
> + int i, ret, size = me->size;
> + u32 da = me->da;
> + u32 pa = me->pa;
> + int order;
> + int flags;
> +
> + /* must be aligned at least with the smallest supported iommu page */
> + if (!IS_ALIGNED(size, SZ_4K) || !IS_ALIGNED(da | pa, SZ_4K)) {
> + pr_err("misaligned: size %x da 0x%x pa 0x%x\n", size, da, pa);
> + return -EINVAL;
> + }
> +
> + while (size) {
> + /* find the max page size with which both pa, da are aligned */
> + all_bits = pa | da;
> + for (i = 0; i < ARRAY_SIZE(pg_size); i++) {
> + if ((size >= pg_size[i]) &&
> + ((all_bits & (pg_size[i] - 1)) == 0)) {
> + break;
> + }
> + }
> +
> + order = get_order(pg_size[i]);
> +
> + /* OMAP4's M3 is little endian, so no need for conversions */
> + flags = MMU_RAM_ENDIAN_LITTLE | MMU_RAM_ELSZ_NONE;
> +
> + if (map)
> + ret = iommu_map(domain, da, pa, order, flags);
> + else
> + ret = iommu_unmap(domain, da, order);
> +
> + if (ret)
> + return ret;
> +
> + size -= pg_size[i];
> + da += pg_size[i];
> + pa += pg_size[i];
> + }
> +
> + return 0;
> +}
> +
> +/* bootaddr isn't needed for the dual M3's */
> +static inline int omap_rproc_start(struct rproc *rproc, u64 bootaddr)
> +{
> + struct device *dev = rproc->dev;
> + struct platform_device *pdev = to_platform_device(dev);
> + struct omap_rproc_pdata *pdata = dev->platform_data;
> + struct iommu_domain *domain;
> + struct device *iommu;
> + struct omap_rproc_priv *priv;
> + int ret, i;
> +
> + if (!iommu_found()) {
> + dev_err(&pdev->dev, "iommu not found\n");
> + return -ENODEV;
> + }
> +
> + priv = kmalloc(sizeof(*priv), GFP_KERNEL);
> + if (!priv) {
> + dev_err(&pdev->dev, "kzalloc failed\n");
> + return -ENOMEM;
> + }
> +
> + /*
> + * Use the specified iommu name to find our iommu device.
> + * TODO: solve this generically so other platforms can use it, too
> + */
> + iommu = omap_find_iommu_device(pdata->iommu_name);
> + if (!iommu) {
> + dev_err(dev, "omap_find_iommu_device failed\n");
> + ret = -ENODEV;
> + goto free_mem;
> + }
> +
> + domain = iommu_domain_alloc();
> + if (!domain) {
> + dev_err(&pdev->dev, "can't alloc iommu domain\n");
> + ret = -ENODEV;
> + goto free_mem;
> + }
> +
> + priv->iommu = iommu;
> + priv->domain = domain;
> + rproc->priv = priv;
> +
> + ret = iommu_attach_device(domain, iommu);
> + if (ret) {
> + dev_err(&pdev->dev, "can't attach iommu device: %d\n", ret);
> + goto free_domain;
> + }
> +
> + for (i = 0; rproc->memory_maps[i].size; i++) {
> + const struct rproc_mem_entry *me = &rproc->memory_maps[i];
> +
> + ret = omap_rproc_map_unmap(domain, me, true);
> + if (ret) {
> + dev_err(&pdev->dev, "iommu_map failed: %d\n", ret);
> + goto unmap_regions;
> + }
> + }
> +
> + /* power on the remote processor itself */
> + ret = omap_device_enable(pdev);
> + if (ret)
> + goto unmap_regions;
> +
> + return 0;
> +
> +unmap_regions:
> + for (--i; i >= 0 && rproc->memory_maps[i].size; i--) {
> + const struct rproc_mem_entry *me = &rproc->memory_maps[i];
> + omap_rproc_map_unmap(domain, me, false);
> + }
> +free_domain:
> + iommu_domain_free(domain);
> +free_mem:
> + kfree(priv);
> + return ret;
> +}
> +
> +static inline int omap_rproc_stop(struct rproc *rproc)
> +{
> + struct device *dev = rproc->dev;
> + struct platform_device *pdev = to_platform_device(dev);
> + struct omap_rproc_priv *priv = rproc->priv;
> + struct iommu_domain *domain = priv->domain;
> + struct device *iommu = priv->iommu;
> + int ret, i;
> +
> + /* power off the remote processor itself */
> + ret = omap_device_shutdown(pdev);
> + if (ret) {
> + dev_err(dev, "failed to shutdown: %d\n", ret);
> + goto out;
> + }
> +
> + for (i = 0; rproc->memory_maps[i].size; i++) {
> + const struct rproc_mem_entry *me = &rproc->memory_maps[i];
> +
> + ret = omap_rproc_map_unmap(domain, me, false);
> + if (ret) {
> + dev_err(&pdev->dev, "iommu_unmap failed: %d\n", ret);
> + goto out;
> + }
> + }
> +
> + iommu_detach_device(domain, iommu);
> + iommu_domain_free(domain);
> +
> +out:
> + return ret;
> +}
> +
> +static struct rproc_ops omap_rproc_ops = {
> + .start = omap_rproc_start,
> + .stop = omap_rproc_stop,
> +};
> +
> +static int omap_rproc_probe(struct platform_device *pdev)
> +{
> + struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
> +
> + return rproc_register(&pdev->dev, pdata->name, &omap_rproc_ops,
> + pdata->firmware, pdata->memory_maps,
> + THIS_MODULE);
Very little for me to comment on here. However, something I just
noticed. Why is it necessary to pass in THIS_MODULE to the
rproc_register function? Having a reference to the pdev gives you the
pointer to the driver, which has the THIS_MODULE value in it. That
should be sufficient.
/me also isn't sure if incrementing the refcount on the module is the
best way to prevent the rproc from going away, but I haven't dug into
the details in the driver code to find out. Drivers can get unbound
from devices without the driver being unloaded, so I imagine there is
an in-use count on the device itself that would prevent driver
unbinding.
> +}
> +
> +static int __devexit omap_rproc_remove(struct platform_device *pdev)
> +{
> + struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
> +
> + return rproc_unregister(pdata->name);
> +}
> +
> +static struct platform_driver omap_rproc_driver = {
> + .probe = omap_rproc_probe,
> + .remove = __devexit_p(omap_rproc_remove),
> + .driver = {
> + .name = "omap-rproc",
> + .owner = THIS_MODULE,
> + },
> +};
> +
> +static int __init omap_rproc_init(void)
> +{
> + return platform_driver_register(&omap_rproc_driver);
> +}
> +/* must be ready in time for device_initcall users */
> +subsys_initcall(omap_rproc_init);
> +
> +static void __exit omap_rproc_exit(void)
> +{
> + platform_driver_unregister(&omap_rproc_driver);
> +}
> +module_exit(omap_rproc_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("OMAP Remote Processor control driver");
> --
> 1.7.1
>
> From: Ohad Ben-Cohen
> Sent: Saturday, June 25, 2011 6:12 PM
>
> Hi Stephen,
>
> On Fri, Jun 24, 2011 at 11:16 PM, Stephen Boyd <[email protected]> wrote:
> > Instead of devising a new firmware format, we decided
> > to just stick with elf and parse the headers in the kernel because we
> > needed them for authentication anyway. Is this reason enough to move to
> > an ELF format instead?
>
> I think that consolidation of code is enough reason to make an effort.
> I know that our firmware format was chosen for simplicity, but I'm not
> sure if we have the tools yet to build standard ELF files for the
> remote processors (IIRC it's in the works though). I'll let Mark
> comment this one.
Yes, we are converting from "standard" ELF to the simple format. I've used the
GNU binutils to work with our ELF files. There were a few motivations:
1. Concern about complexity of parsing ELF files in kernel; however, the PIL
implementation looks pretty clean to me.
2. We added a special section (resource table) that is interpreted as part
of the loading process. The tool that generates our simple format just
recognizes a named section (".resource_table"), so perhaps that could be
done with the PIL ELF loader.
3. Smaller firmware file sizes. Our ELF files are large relative to the
payload, but this might be addressed by a better ELF "strip" utility.
> > Another difference is inter-processor dependencies. For example, on
> > msm8660 the modem can't boot until the dsp has been booted. I suppose we
> > could hide this detail in the platform specific get() implementation by
> > calling rproc_get() on the dependent processor (hopefully no locking
> > issues arise). I'd rather have it built into the core though as it isn't
> > really specific to the hardware.
>
> No problems, I'm sure we can solve this one easily.
>
> > If we can resolve these differences I think we can easily support remote
> > processor boot on MSM via remoteproc.
>
> That'd be very cool, I sure do hope we can work together.
Yes, I hope we can merge our efforts on PIL and remoteproc since they seem
quite close in function and design.
Mark
> From: Grant Likely
> Sent: Monday, June 27, 2011 1:50 PM
Grant, thanks for the feedback. I'll try to answer one of your
questions below and leave the rest for Ohad.
Mark
> > +Every remoteproc implementation must provide these handlers:
> > +
> > +struct rproc_ops {
> > + int (*start)(struct rproc *rproc, u64 bootaddr);
> > + int (*stop)(struct rproc *rproc);
> > +};
> > +
> > +The ->start() handler takes a rproc handle and an optional bootaddr
> argument,
> > +and should power on the device and boot it (using the bootaddr
> argument
> > +if the hardware requires one).
>
> Naive question: Why is bootaddr an argument? Wouldn't rproc drivers
> keep track of the boot address in their driver private data?
Our AMPs (remote processors) have a variety of boot mechanisms that vary
across the different SoCs (yes, TI likes HW diversity). In some cases, the
boot address is more like an entry point and that comes from the firmware,
so it is not a static attribute of a driver. Correct me if I misunderstood
your question.
Mark
On Tue, Jun 21, 2011 at 10:18:33AM +0300, Ohad Ben-Cohen wrote:
> Add a virtio-based IPC bus, which enables kernel users to communicate
> with remote processors over shared memory using a simple messaging
> protocol.
>
> Assign a local address for every local endpoint that is created,
> and bind it to the user's callback. Invoke that callback when the
> destination of an inbound message is the user's address.
>
> Signed-off-by: Ohad Ben-Cohen <[email protected]>
Hey Ohad,
Nice patch. I'm quite thrilled to see this implemented. Some
comments below, but otherwise I think it looks pretty good.
> ---
> Documentation/ABI/testing/sysfs-bus-rpmsg | 75 +++
> Documentation/rpmsg.txt | 308 +++++++++
> drivers/Kconfig | 1 +
> drivers/Makefile | 1 +
> drivers/rpmsg/Kconfig | 14 +
> drivers/rpmsg/Makefile | 1 +
> drivers/rpmsg/virtio_rpmsg_bus.c | 1036 +++++++++++++++++++++++++++++
> include/linux/mod_devicetable.h | 10 +
> include/linux/rpmsg.h | 421 ++++++++++++
> include/linux/virtio_ids.h | 1 +
> 10 files changed, 1868 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg
> create mode 100644 Documentation/rpmsg.txt
> create mode 100644 drivers/rpmsg/Kconfig
> create mode 100644 drivers/rpmsg/Makefile
> create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c
> create mode 100644 include/linux/rpmsg.h
>
> diff --git a/Documentation/rpmsg.txt b/Documentation/rpmsg.txt
> new file mode 100644
> index 0000000..0a7c820
> --- /dev/null
> +++ b/Documentation/rpmsg.txt
[...]
Great documentation! Thanks!
> diff --git a/drivers/Kconfig b/drivers/Kconfig
> index 1f6d6d3..840f835 100644
> --- a/drivers/Kconfig
> +++ b/drivers/Kconfig
> @@ -128,4 +128,5 @@ source "drivers/clocksource/Kconfig"
>
> source "drivers/remoteproc/Kconfig"
>
> +source "drivers/rpmsg/Kconfig"
> endmenu
> diff --git a/drivers/Makefile b/drivers/Makefile
> index 4d53a18..2980a15 100644
> --- a/drivers/Makefile
> +++ b/drivers/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_ARM_AMBA) += amba/
> obj-$(CONFIG_DMA_ENGINE) += dma/
>
> obj-$(CONFIG_VIRTIO) += virtio/
> +obj-$(CONFIG_RPMSG) += rpmsg/
> obj-$(CONFIG_XEN) += xen/
>
> # regulators early, since some subsystems rely on them to initialize
> diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
> new file mode 100644
> index 0000000..41303f5
> --- /dev/null
> +++ b/drivers/rpmsg/Kconfig
> @@ -0,0 +1,14 @@
> +# RPMSG always gets selected by whoever wants it
> +config RPMSG
> + tristate
> + select VIRTIO
> + select VIRTIO_RING
> +
> +if RPMSG
> +
> +# OK, it's a little counter-intuitive to do this, but it puts it neatly under
> +# the rpmsg menu (and it's the approach preferred by the virtio folks).
> +
> +source "drivers/virtio/Kconfig"
What happens when kvm and rpmsg both get enabled on the same kernel.
ARM virtualization is currently being worked on, so it will happen.
Also, I can see this finding use in the x86 world to talk to
coprocessor boards (like the Xilinx FPGA plugin board which can have a
soft core on it).
> +
> +endif # RPMSG
> diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
> new file mode 100644
> index 0000000..7617fcb
> --- /dev/null
> +++ b/drivers/rpmsg/Makefile
> @@ -0,0 +1 @@
> +obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o
> diff --git a/drivers/rpmsg/virtio_rpmsg_bus.c b/drivers/rpmsg/virtio_rpmsg_bus.c
> new file mode 100644
> index 0000000..3e98b02
> --- /dev/null
> +++ b/drivers/rpmsg/virtio_rpmsg_bus.c
[...]
> +/* rpmsg devices and drivers are matched using the service name */
> +static inline int rpmsg_id_match(const struct rpmsg_channel *rpdev,
> + const struct rpmsg_device_id *id)
> +{
> + if (strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE))
> + return 0;
> +
> + return 1;
> +}
or simply: 'return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;'
:-)
> +/* for more info, see below documentation of rpmsg_create_ept() */
> +static struct rpmsg_endpoint *__rpmsg_create_ept(struct virtproc_info *vrp,
> + struct rpmsg_channel *rpdev,
> + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32),
> + void *priv, u32 addr)
> +{
> + int err, tmpaddr, request;
> + struct rpmsg_endpoint *ept;
> + struct device *dev = rpdev ? &rpdev->dev : &vrp->vdev->dev;
> +
> + if (!idr_pre_get(&vrp->endpoints, GFP_KERNEL))
> + return NULL;
> +
> + ept = kzalloc(sizeof(*ept), GFP_KERNEL);
> + if (!ept) {
> + dev_err(dev, "failed to kzalloc a new ept\n");
> + return NULL;
> + }
> +
> + ept->rpdev = rpdev;
> + ept->cb = cb;
> + ept->priv = priv;
> +
> + /* do we need to allocate a local address ? */
> + request = addr == RPMSG_ADDR_ANY ? RPMSG_RESERVED_ADDRESSES : addr;
> +
> + spin_lock(&vrp->endpoints_lock);
Is a spin_lock the right choice for endpoints, or should it be a
mutex (do the endpoints operations need to be atomic)?
> +/*
> + * find an existing channel using its name + address properties,
> + * and destroy it
> + */
> +static int rpmsg_destroy_channel(struct virtproc_info *vrp,
> + struct rpmsg_channel_info *chinfo)
> +{
> + struct virtio_device *vdev = vrp->vdev;
> + struct device *dev;
> +
> + dev = device_find_child(&vdev->dev, chinfo, rpmsg_channel_match);
> + if (!dev)
> + return -EINVAL;
> +
> + device_unregister(dev);
> +
> + put_device(dev);
> +
> + kfree(to_rpmsg_channel(dev));
At put_device time, it is conceivable that the dev pointer is no
longer valid. You'll need to get do the to_rpmsg_channel() before
putting the dev.
> +
> + return 0;
> +}
> +
> +/* super simple buffer "allocator" that is just enough for now */
> +static void *get_a_tx_buf(struct virtproc_info *vrp)
> +{
> + unsigned int len;
> + void *ret;
> +
> + /* support multiple concurrent senders */
> + spin_lock(&vrp->tx_lock);
> +
> + /*
> + * either pick the next unused tx buffer
> + * (half of our buffers are used for sending messages)
> + */
> + if (vrp->last_sbuf < vrp->num_bufs / 2)
> + ret = vrp->sbufs + vrp->buf_size * vrp->last_sbuf++;
> + /* or recycle a used one */
> + else
> + ret = virtqueue_get_buf(vrp->svq, &len);
> +
> + spin_unlock(&vrp->tx_lock);
> +
> + return ret;
> +}
> +
> +/**
> + * rpmsg_upref_sleepers() - enable "tx-complete" interrupts, if needed
> + * @vrp: virtual remote processor state
> + *
> + * This function is called before a sender is blocked, waiting for
> + * a tx buffer to become available.
> + *
> + * If we already have blocking senders, this function merely increases
> + * the "sleepers" reference count, and exits.
> + *
> + * Otherwise, if this is the first sender to block, we also enable
> + * virtio's tx callbacks, so we'd be immediately notified when a tx
> + * buffer is consumed (we rely on virtio's tx callback in order
> + * to wake up sleeping senders as soon as a tx buffer is used by the
> + * remote processor).
> + */
> +static void rpmsg_upref_sleepers(struct virtproc_info *vrp)
> +{
> + /* support multiple concurrent senders */
> + spin_lock(&vrp->tx_lock);
> +
> + /* are we the first sleeping context waiting for tx buffers ? */
> + if (!vrp->sleepers++)
Maybe use a kref? It might be useful to have a kref_get_first() that
takes a callback used for the first increment.
> +static int rpmsg_remove_device(struct device *dev, void *data)
> +{
> + struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
> +
> + device_unregister(dev);
> +
> + kfree(rpdev);
put_device() I think.
> +
> + return 0;
> +}
> +
> +static void __devexit rpmsg_remove(struct virtio_device *vdev)
> +{
> + struct virtproc_info *vrp = vdev->priv;
> + int ret;
> +
> + ret = device_for_each_child(&vdev->dev, NULL, rpmsg_remove_device);
> + if (ret)
> + dev_warn(&vdev->dev, "can't remove rpmsg device: %d\n", ret);
> +
> + idr_remove_all(&vrp->endpoints);
> + idr_destroy(&vrp->endpoints);
> +
> + vdev->config->del_vqs(vrp->vdev);
> +
> + kfree(vrp);
> +}
> +
> +static struct virtio_device_id id_table[] = {
> + { VIRTIO_ID_RPMSG, VIRTIO_DEV_ANY_ID },
> + { 0 },
> +};
> +
> +static unsigned int features[] = {
> + VIRTIO_RPMSG_F_NS,
> +};
> +
> +static struct virtio_driver virtio_ipc_driver = {
> + .feature_table = features,
> + .feature_table_size = ARRAY_SIZE(features),
> + .driver.name = KBUILD_MODNAME,
> + .driver.owner = THIS_MODULE,
> + .id_table = id_table,
> + .probe = rpmsg_probe,
> + .remove = __devexit_p(rpmsg_remove),
> +};
> +
> +static int __init init(void)
Even for static functions, it's a really good idea to prefix all
function names and file scoped symbol with a common prefix like
"rpmsg_". Doing so avoids even the outside chance of a namespace
conflict.
> +{
> + int ret;
> +
> + ret = bus_register(&rpmsg_bus);
> + if (ret) {
> + pr_err("failed to register rpmsg bus: %d\n", ret);
> + return ret;
> + }
> +
> + return register_virtio_driver(&virtio_ipc_driver);
And if register_virtio_driver fails?
> +}
> +module_init(init);
> +
> +static void __exit fini(void)
> +{
> + unregister_virtio_driver(&virtio_ipc_driver);
> + bus_unregister(&rpmsg_bus);
> +}
> +module_exit(fini);
> +
> +MODULE_DEVICE_TABLE(virtio, id_table);
> +MODULE_DESCRIPTION("Virtio-based remote processor messaging bus");
> +MODULE_LICENSE("GPL v2");
> diff --git a/include/linux/mod_devicetable.h b/include/linux/mod_devicetable.h
> index ae28e93..561567e 100644
> --- a/include/linux/mod_devicetable.h
> +++ b/include/linux/mod_devicetable.h
> @@ -533,4 +533,14 @@ struct isapnp_device_id {
> kernel_ulong_t driver_data; /* data private to the driver */
> };
>
> +/* rpmsg */
> +
> +#define RPMSG_NAME_SIZE 32
> +#define RPMSG_DEVICE_MODALIAS_FMT "rpmsg:%s"
> +
> +struct rpmsg_device_id {
> + char name[RPMSG_NAME_SIZE];
> + kernel_ulong_t driver_data /* Data private to the driver */
> + __attribute__((aligned(sizeof(kernel_ulong_t))));
> +};
Should this be co-located with vio_device_id?
It makes it easier to dereference in kernel code if you do:
#ifdef __KERNEL__
void *data;
#else
kernel_ulong_t data;
#endif
On Mon, Jun 27, 2011 at 09:52:30PM +0000, Grosen, Mark wrote:
> > From: Grant Likely
> > Sent: Monday, June 27, 2011 1:50 PM
>
> Grant, thanks for the feedback. I'll try to answer one of your
> questions below and leave the rest for Ohad.
>
> Mark
>
> > > +Every remoteproc implementation must provide these handlers:
> > > +
> > > +struct rproc_ops {
> > > + int (*start)(struct rproc *rproc, u64 bootaddr);
> > > + int (*stop)(struct rproc *rproc);
> > > +};
> > > +
> > > +The ->start() handler takes a rproc handle and an optional bootaddr
> > argument,
> > > +and should power on the device and boot it (using the bootaddr
> > argument
> > > +if the hardware requires one).
> >
> > Naive question: Why is bootaddr an argument? Wouldn't rproc drivers
> > keep track of the boot address in their driver private data?
>
> Our AMPs (remote processors) have a variety of boot mechanisms that vary
> across the different SoCs (yes, TI likes HW diversity). In some cases, the
> boot address is more like an entry point and that comes from the firmware,
> so it is not a static attribute of a driver. Correct me if I misunderstood
> your question.
More to the point, I would expect the boot_address to be a property of
the rproc instance because it represents the configuration of the
remote processor. It seems odd that the caller of ->start would know
better than the rproc driver about the entry point of the processor.
g.
On Mon, Jun 27, 2011 at 02:49:58PM -0600, Grant Likely wrote:
> > +struct {
> > + char magic[4] = { 'R', 'P', 'R', 'C' };
> > + u32 version;
> > + u32 header_len;
> > + char header[...] = { header_len bytes of unformatted, textual header };
> > + struct section {
> > + u32 type;
> > + u64 da;
> > + u32 len;
> > + u8 content[...] = { len bytes of binary data };
> > + } [ no limit on number of sections ];
> > +} __packed;
>
> Other have commented on the image format, so I'll skip this bit other
> than saying that I agree it would be great to have a common format.
(Don't have the original message to reply to...)
Do we really want to end up with header being 5 bytes, header_len set
as 5, and having to load/store all this data using byte loads/stores ?
If we don't want that, then I suggest we get rid of the packed attribute,
and require stuff to be naturally aligned.
First issue is that struct section could be much better layed out:
struct section {
u32 type;
u32 len;
u64 da;
u8 content[];
};
and require sizeof(struct section) % sizeof(u64) == 0 - iow, to find
the next section, round len up to sizeof(u64). Ditto for the header.
On Mon, Jun 27, 2011 at 5:29 PM, Russell King - ARM Linux
<[email protected]> wrote:
> On Mon, Jun 27, 2011 at 02:49:58PM -0600, Grant Likely wrote:
>> > +struct {
>> > + ? ? ?char magic[4] = { 'R', 'P', 'R', 'C' };
>> > + ? ? ?u32 version;
>> > + ? ? ?u32 header_len;
>> > + ? ? ?char header[...] = { header_len bytes of unformatted, textual header };
>> > + ? ? ?struct section {
>> > + ? ? ? ? ?u32 type;
>> > + ? ? ? ? ?u64 da;
>> > + ? ? ? ? ?u32 len;
>> > + ? ? ? ? ?u8 content[...] = { len bytes of binary data };
>> > + ? ? ?} [ no limit on number of sections ];
>> > +} __packed;
>>
>> Other have commented on the image format, so I'll skip this bit other
>> than saying that I agree it would be great to have a common format.
>
> (Don't have the original message to reply to...)
>
> Do we really want to end up with header being 5 bytes, header_len set
> as 5, and having to load/store all this data using byte loads/stores ?
>
> If we don't want that, then I suggest we get rid of the packed attribute,
> and require stuff to be naturally aligned.
>
> First issue is that struct section could be much better layed out:
>
> struct section {
> ? ? ? ?u32 type;
> ? ? ? ?u32 len;
> ? ? ? ?u64 da;
> ? ? ? ?u8 content[];
> };
>
> and require sizeof(struct section) % sizeof(u64) == 0 - iow, to find
> the next section, round len up to sizeof(u64). ?Ditto for the header.
Hopefully this will all be moot since it has been proposed to use elf
images directly.
g.
--
Grant Likely, B.Sc., P.Eng.
Secret Lab Technologies Ltd.
> From: Grant Likely
> Sent: Monday, June 27, 2011 3:24 PM
> > Our AMPs (remote processors) have a variety of boot mechanisms that vary
> > across the different SoCs (yes, TI likes HW diversity). In some cases, the
> > boot address is more like an entry point and that comes from the firmware,
> > so it is not a static attribute of a driver. Correct me if I misunderstood
> > your question.
>
> More to the point, I would expect the boot_address to be a property of
> the rproc instance because it represents the configuration of the
> remote processor. It seems odd that the caller of ->start would know
> better than the rproc driver about the entry point of the processor.
>
> g.
Yes, in many cases the boot_address will be defined by the HW. However, we have
processors that are "soft" - the boot_address comes from the particular firmware
being loaded and can (will) be different with each firmware image. We factored
out the firmware loader to be device-independent (in remoteproc.c) so it's not
repeated in each device-specific implementation like omap_remoteproc.c and
davinci_remoteproc.c. In the cases where the HW dictates what happens, the start()
method should just ignore the boot_address.
Mark
Hi Mark,
On Mon, Jun 27, 2011 at 23:50:25, Grosen, Mark wrote:
> > >>> +static inline int davinci_rproc_start(struct rproc *rproc, u64
> > >> bootaddr)
> > >>> +{
> > >>> + struct device *dev = rproc->dev;
> > >>> + struct davinci_rproc_pdata *pdata = dev->platform_data;
> > >>> + struct davinci_soc_info *soc_info = &davinci_soc_info;
> > >>> + void __iomem *psc_base;
> > >>> + struct clk *dsp_clk;
> > >>> +
> > >>> + /* hw requires the start (boot) address be on 1KB boundary */
> > >>> + if (bootaddr & 0x3ff) {
> > >>> + dev_err(dev, "invalid boot address: must be aligned to
> > >> 1KB\n");
> > >>> + return -EINVAL;
> > >>> + }
> > >>> +
> > >>> + dsp_clk = clk_get(dev, pdata->clk_name);
> >
> > >> We could match using clkdev functionality, but the clock entry
> > >> would need to be changed then...
> >
> > > I followed the existing pattern I saw in other drivers.
> >
> > Probably MUSB? We're trying to move away from passing the clock name to thge
> > drivers, using match by device instead.
> >
> > > If there is a new, better way, please point me to an example.
> >
> > Look at the da850_clks[] in mach-davinci/da850.c: if the device name is
> > specified as a first argument to CLK() macro (instead of clock name the second
> > argument), the matching is done by device, so you don't need to specify the
> > clock name to clk_get(), passing NULL instead.
>
> Thanks, I'll look at this and ask for Sekhar and Kevin's preferences.
The second argument is not a (SoC specific)
clock name, it is the "connection id" which
is specific to the IP (not to the SoC).
For example, the EMAC IP may want to deal
with interface clock, functional clock and
mii clock. So, it passes a connection identifier
to tell the clock framework which particular
clock it is interested in. The names of these
connection identifiers will not change with SoC
as they are IP property. So, it will not be
right to get this through platform data.
If there is no connection identifier in
your case, you can just pass the second
argument as NULL. The clock matching will
happen using device name (which should
remain same across SoCs too).
Of course, this means that any incorrectly defined
clock lookup structures for dsp clock in platform
code needs to be corrected.
Thanks,
Sekhar
Hello.
On 21.06.2011 11:18, Ohad Ben-Cohen wrote:
> From: Mark Grosen <[email protected]>
> Add davinci remoteproc device for the da850's C674x dsp remote
> processor, and support it on the da850-evm and omapl138-hawk boards.
> Signed-off-by: Mark Grosen<[email protected]>
> Signed-off-by: Ohad Ben-Cohen<[email protected]>
[...]
> diff --git a/arch/arm/mach-davinci/da850.c b/arch/arm/mach-davinci/da850.c
> index 133aac4..9280b1e 100644
> --- a/arch/arm/mach-davinci/da850.c
> +++ b/arch/arm/mach-davinci/da850.c
> @@ -74,6 +74,13 @@ static struct clk pll0_aux_clk = {
> .flags = CLK_PLL | PRE_PLL,
> };
>
> +static struct clk pll0_sysclk1 = {
> + .name = "pll0_sysclk1",
> + .parent = &pll0_clk,
> + .flags = CLK_PLL,
> + .div_reg = PLLDIV1,
> +};
> +
> static struct clk pll0_sysclk2 = {
> .name = "pll0_sysclk2",
> .parent = &pll0_clk,
> @@ -373,6 +380,12 @@ static struct clk spi1_clk = {
> .flags = DA850_CLK_ASYNC3,
> };
>
> +static struct clk dsp_clk = {
> + .name = "dsp",
> + .parent = &pll0_sysclk1,
> + .lpsc = DA8XX_LPSC0_GEM,
> +};
> +
> static struct clk_lookup da850_clks[] = {
> CLK(NULL, "ref", &ref_clk),
> CLK(NULL, "pll0", &pll0_clk),
> @@ -419,6 +432,7 @@ static struct clk_lookup da850_clks[] = {
> CLK(NULL, "usb20", &usb20_clk),
> CLK("spi_davinci.0", NULL, &spi0_clk),
> CLK("spi_davinci.1", NULL, &spi1_clk),
> + CLK(NULL, "dsp", &dsp_clk),
> CLK(NULL, NULL, NULL),
> };
How about also adding the clock for DA830?
> diff --git a/arch/arm/mach-davinci/devices-da8xx.c b/arch/arm/mach-davinci/devices-da8xx.c
> index fc4e98e..3becfd1 100644
> --- a/arch/arm/mach-davinci/devices-da8xx.c
> +++ b/arch/arm/mach-davinci/devices-da8xx.c
[...]
> @@ -647,6 +648,20 @@ int __init da850_register_mmcsd1(struct davinci_mmc_config *config)
> da850_mmcsd1_device.dev.platform_data = config;
> return platform_device_register(&da850_mmcsd1_device);
> }
> +
> +int __init da850_register_rproc(void)
Please rename to da8xx_register_rproc() -- there's nothing DA850-specific
here.
> +{
> + struct platform_device *rproc;
> + struct davinci_rproc_pdata rproc_pdata = {
> + .name = "dsp",
> + .firmware = "davinci-dsp.bin",
Isn't the firmware DA8xx-specific?
WBR, Sergei
On Tue, Jun 28, 2011 at 12:22 AM, Grosen, Mark <[email protected]> wrote:
> 2. We added a special section (resource table) that is interpreted as part
> of the loading process. The tool that generates our simple format just
> recognizes a named section (".resource_table"), so perhaps that could be
> done with the PIL ELF loader.
I hope that DT will completely replace that "resource table" section
eventually. We still need to try it out (as soon as DT materializes on
ARM/pandaboard) and see how it all fits together, but in general it
looks like DT could provide all the necessary static resource
configuration, and then we just need to expose it to the remote
processor.
On Tue, Jun 28, 2011 at 4:26 AM, Ohad Ben-Cohen <[email protected]> wrote:
> On Tue, Jun 28, 2011 at 12:22 AM, Grosen, Mark <[email protected]> wrote:
>> 2. We added a special section (resource table) that is interpreted as part
>> of the loading process. The tool that generates our simple format just
>> recognizes a named section (".resource_table"), so perhaps that could be
>> done with the PIL ELF loader.
>
> I hope that DT will completely replace that "resource table" section
> eventually. We still need to try it out (as soon as DT materializes on
> ARM/pandaboard) and see how it all fits together, but in general it
> looks like DT could provide all the necessary static resource
> configuration, and then we just need to expose it to the remote
> processor.
I'm not sure I see the benefit to pulling the resource information out
of the firmware image. The resource information is a description of
what resources the firmware requires to work properly (it needs
certain amounts of working memory, timers, peripheral interfaces like
i2c to control camera hw, etc), which will be specific to a given
firmware build. Pulling that information out into another place adds
new ways for things to break as the firmware and its resource
requirements are now separate and could get out of sync, etc.
Brian
Hi Grant,
Thanks a lot for the exhaustive review and comments !
On Mon, Jun 27, 2011 at 11:49 PM, Grant Likely
<[email protected]> wrote:
>> + ? ? my_rproc = rproc_get("ipu");
>
> I tend to be suspicious of apis whose primary interface is by-name
> lookup. ?It works fine when the system is small, but it can get
> unwieldy when the client driver doesn't have a direct relation to the
> setup code that chooses the name. ?At some point I suspect that there
> will need to be different lookup mechanism, such as which AMP
> processor is currently available (if there are multiple of the same
> type).
Yeah, this might be too limiting on some systems. I gave this a little
thought, but decided to wait until those systems show up first, so I
we can better understand them/their requirements/use-cases. For now,
I've just followed this simple name-based API model (which still seem
a bit popular in several SoC drivers I've looked at, probably due to
the general simplicity of it and its use cases).
> It also leaves no option for drivers to obtain a reference to the
> rproc instance, and bring it up/down as needed (without the name
> lookup every time).
..
> That said, it looks like only the rproc_get() api is using by-name
> lookup, and everything else is via the structure. ?Can (should) the
> by-name lookup part be factored out into a rproc_get_by_name()
> accessor?
I think you are looking for a different set of API here, probably
something that is better implemented using runtime PM.
When a driver calls rproc_get(), not only does it power on the remote
processor, but it also makes sure the underlying implementation cannot
go away (i.e. platform-specific remoteproc module cannot be removed,
and the rproc cannot be unregistered).
After it calls rproc_put(), it cannot rely anymore on the remote
processor to stick around (the rproc can be unregistered at this
point), so the next time it needs it, it must go through the full
get-by-name (or any other get API we will come up with eventually)
getter API.
If drivers need to hold onto the rproc instance, but still explicitly
allow it to power off at times, they should probably call something
like pm_runtime_put(rproc->dev).
(remoteproc runtime PM support is not implemented yet, but is
definitely planned, so we can suspend remote processors on
inactivity).
> Since rproc_register is allocating a struct rproc instance that
> represent the device, shouldn't the pointer to that device be returned
> to the caller?
Yes, it definitely should. We will have the underlying implementation
remember it, and then pass it to rproc_unregister when needed.
>> + ?int rproc_unregister(const char *name);
>
> I definitely would not do this by name. ?I think it is better to pass
> the actual instance pointer to rproc_unregister.
Much better, yeah.
> Naive question: Why is bootaddr an argument? ?Wouldn't rproc drivers
> keep track of the boot address in their driver private data?
Mark already got that one, but basically the boot address comes from
the firmware image: we need to let the implementation know the
physical address where the text section is mapped. This is ignored on
implementations where that address is fixed (e.g. OMAP's M3).
> Other have commented on the image format, so I'll skip this bit other
> than saying that I agree it would be great to have a common format.
We are evaluating now moving to ELF; let's see how it goes. Using a
standard format is an advantage (as long as it's not overly
complicated), but I wonder if achieving a common format is really
feasible and whether eventually different platforms will need
different binary formats anyway, and we'll have to abstract this out
of remoteproc (guess that as usual, we just need to start off with
something, and then evolve as requirements show up).
>> +Most likely this kind of static allocations of hardware resources for
>> +remote processors can also use DT, so it's interesting to see how
>> +this all work out when DT materializes.
>
> I imagine that it will be quite straight forward. ?There will probably
> be a node in the tree to represent each slave AMP processor, and other
> devices attached to it could be represented using 'phandle' links
> between the nodes. ?Any configuration of the AMP process can be
> handled with arbitrary device-specific properties in the AMP
> processor's node.
That sounds good. The dilemma is bigger, though.
The kind of stuff we need to synchronize about are not really
describing the hardware; it's more a runtime policy/configuration than
a hardware description.
As Brian mentioned in the other thread:
> The resource information is a description of
> what resources the firmware requires to work properly (it needs
> certain amounts of working memory, timers, peripheral interfaces like
> i2c to control camera hw, etc), which will be specific to a given
> firmware build.
Some of those resources will be allocated dynamically using an rpmsg
driver (developed by Fernando Guzman Lugo), but some must be supplied
before booting the firmware (memory ?). We're also using the existing
resource table today to announce the boot address and the trace buffer
address.
So the question is whether/if DT can help here.
On one hand, we're not describing the hardware here. it's pure
configuration, but that seem fine, as DT seem to be taking runtime
configuration, too (e.g. bootargs, initrd addresses, etc..). Moreover,
some of those remoteproc configurations should handed early to the
host, too (e.g. we might need to reserve specific physical memory that
must be used by the remote processor, and this can't wait until the
firmware is loaded).
OTOH, as Brian mentioned, it does make sense to couple those
configurations with the specific firmware image, so risk of breaking
stuff when the firmware is changed is minimized. Maybe we can have a
secondary .dts file as part of the firmware sources, and have it
included in the primary .dts (and let the remoteproc access that
respective secondary .dtb) ?
These are just raw ideas - I never tried working with DT yet myself.
>> +source "drivers/remoteproc/Kconfig"
>> +
>
> Hmmm, I wonder if the end of the drivers list is the best place for
> this. ?The drivers menu in kconfig is getting quite unwieldy.
We can arbitrarily choose a better location in that file but I'm not
sure I can objectively justify it :)
(alternatively, we can source that Kconfig from the relevant
platform's Kconfig, like virtio does).
>> + ? ? /*
>> + ? ? ?* find the end of trace buffer (does not account for wrapping).
>> + ? ? ?* desirable improvement: use a ring buffer instead.
>> + ? ? ?*/
>> + ? ? for (i = 0; i < size && buf[i]; i++);
>
> Hmmm, I wonder if this could make use of the ftrace ring buffer.
I thought about it, but I'm not sure we want to.
To do that, we'd need the remote processor to send us a message (via
rpmsg probably...) for every trace log we want to write into that ring
buffer. That would mean significant overhead for every remote trace
message, but would also mean you can't debug low level issues with
rpmsg, because you need it to deliver the debug messages themselves.
Instead, we just use a 'dumb' non-cacheable memory region into which
the remote processor unilaterally writes its trace messages. If/when
we're interested in the last remote log messages, we just read that
shared buffer (e.g. cat /debug/remoteproc/omap-rproc.1/trace0).
This means zero overhead on the host, and the ability to debug very
low level remote issues: all you need is a shared memory buffer and
remote traces work.
Currently this shared buffer is really dumb: we just dump its entire
content when asked. One nice improvement we can do is handling the
inevitable wrapping, by maintaining a shared "head" offset into the
buffer.
>> + ? ? switch (state) {
..
>> + ? ? }
>
> Me thinks this is asking for a lookup table.
sounds good.
>> +static ssize_t rproc_state_read(struct file *filp, char __user *userbuf,
>> + ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? size_t count, loff_t *ppos)
>> +{
>> + ? ? struct rproc *rproc = filp->private_data;
>> + ? ? int state = rproc->state;
>> + ? ? char buf[100];
>
> 100 bytes? ?I count at most ~30.
30 it is.
>> +#define DEBUGFS_ADD(name) ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?\
>> + ? ? debugfs_create_file(#name, 0400, rproc->dbg_dir, ? ? ? ? ? ? ? ?\
>> + ? ? ? ? ? ? ? ? ? ? rproc, &name## _rproc_ops)
>
> You might want to split the debug stuff off into a separate patch,
> just to keep the review load down. ?(up to you though).
Sure. I thought maybe to even split it to a separate file as well.
>> + ? ? spin_unlock(&rprocs_lock);
>
> Unless you're going to be looking up the device at irq time, a mutex
> is probably a better choice here.
mutex it is.
We can also completely remove the lock and just use RCU, as the list
is rarely changed. Since it's so short today, and rarely accessed at
all (even read access is pretty rare), it probably won't matter too
much.
>> + ? ? dev_info(dev, "remote processor %s is now up\n", rproc->name);
>
> How often are remote processors likely to be brought up/down?
Very rarely. Today we bring it up on boot, and keep it loaded (it will
then be suspended on inactivity and won't consume power when we don't
need it to do anything).
> However, it may be non-zero here, but drop to zero by the time you
> take the lock. ?Best be safe and put it inside the mutex. ?Having it
> under the mutex shouldn't be a performance hit since only buggy code
> will get this test wrong. ?In fact, it is probably appropriate to
> WARN_ON() on the !rproc->count condition.
good points, thanks.
> Actually, using a hand coded reference count like this shouldn't be
> done.
yeah, i planned to switch to an atomic variable here.
> Looking at the code, I
> suspect you'll want separate reference counting for object references
> and power up/down count so that clients can control power to a device
> without giving up the pointer to the rproc instance.
Eventually the plan is to use runtime PM for the second refcount, so
we get all this plumbing for free.
>> + ? ? ? ? ? ? /* iounmap normal memory, so make sparse happy */
>> + ? ? ? ? ? ? iounmap((__force void __iomem *) rproc->trace_buf1);
>
> Icky casting! ?That suggests that how the trace buffer pointer is
> managed needs work.
The plan is to replace those ioremaps with dma coherent memory, and
then we don't need no casting. We just need the generic dma API (which
is in the works) to handle omap's iommu transparently (in the works
too), and then tell the remoteproc where to write logs to. It might
take some time, but it sounds very clean.
>> +#define RPROC_MAX_NAME ? ? ? 100
>
> I wouldn't even bother with this. ?The only place it is used is in one
> of the debugfs files, and you can protect against too large a static
> buffer by using %100s (or whatever) in the snprintf().
cool, thanks!
Again, many thanks for the review,
Ohad.
On Tue, Jun 28, 2011 at 2:29 AM, Russell King - ARM Linux
<[email protected]> wrote:
> (Don't have the original message to reply to...)
Sorry about that.
My recent emails to linux-arm-kernel were bounced with a "Message has
a suspicious header" reason. not sure what am I doing wrong..
> Do we really want to end up with header being 5 bytes, header_len set
> as 5, and having to load/store all this data using byte loads/stores ?
As Grant said, we're trying to move to ELF now, but if we do end up
sticking with this custom format eventually, we'll sure do take care
of all the alignment issues.
Thanks!
Hi Grant,
On Tue, Jun 28, 2011 at 1:21 AM, Grant Likely <[email protected]> wrote:
> Nice patch. ?I'm quite thrilled to see this implemented. ?Some
> comments below, but otherwise I think it looks pretty good.
Thanks !
>> +source "drivers/virtio/Kconfig"
>
> What happens when kvm and rpmsg both get enabled on the same kernel.
Sounds to me that eventually we'll have to source virtio's Kconfig in
drivers/Kconfig, and remove all the other locations it is sourced at
(currently it is directly sourced by several arch Kconfigs when
VIRTUALIZATION is selected).
>> + ? ? if (strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE))
>> + ? ? ? ? ? ? return 0;
>> +
>> + ? ? return 1;
>> +}
>
> or simply: 'return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;'
>
> :-)
that works too ;)
>> + ? ? spin_lock(&vrp->endpoints_lock);
>
> Is a spin_lock the right choice for endpoints, or should it be a
> mutex (do the endpoints operations need to be atomic)?
Today it can be a mutex: it protects idr operations invoked either by
users (creating new endpoints, definitely not requiring atomicity) or
by platform code indicating that a new message has arrived (today it's
omap's mailbox driver, which calls from a process context).
I can change that, and if someone requires atomic operations later on,
move to spinlocks again.
But it probably won't matter too much as there's no contention on that
lock today (notifications coming from omap's mailbox are serialized,
and users creating new endpoints show up very infrequently).
> At put_device time, it is conceivable that the dev pointer is no
> longer valid. ?You'll need to get do the to_rpmsg_channel() before
> putting the dev.
sure.
>> +static void rpmsg_upref_sleepers(struct virtproc_info *vrp)
>> +{
>> + ? ? /* support multiple concurrent senders */
>> + ? ? spin_lock(&vrp->tx_lock);
>> +
>> + ? ? /* are we the first sleeping context waiting for tx buffers ? */
>> + ? ? if (!vrp->sleepers++)
>
> Maybe use a kref?
I can change it to an atomic variable, but I don't think kref's memory
barriers are needed here: we already have memory barriers induced by
the spinlock.
>> +static int rpmsg_remove_device(struct device *dev, void *data)
>> +{
>> + ? ? struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
>> +
>> + ? ? device_unregister(dev);
>> +
>> + ? ? kfree(rpdev);
>
> put_device() I think.
Don't think so, we get the device handle from device_for_each_child
here, which doesn't call get_device (unlike device_find_child).
>> +static int __init init(void)
>
> Even for static functions, it's a really good idea to prefix all
> function names and file scoped symbol with a common prefix like
> "rpmsg_". ?Doing so avoids even the outside chance of a namespace
> conflict.
Sure thing.
>> + ? ? ret = bus_register(&rpmsg_bus);
>> + ? ? if (ret) {
>> + ? ? ? ? ? ? pr_err("failed to register rpmsg bus: %d\n", ret);
>> + ? ? ? ? ? ? return ret;
>> + ? ? }
>> +
>> + ? ? return register_virtio_driver(&virtio_ipc_driver);
>
> And if register_virtio_driver fails?
I'll handle that, thanks.
>> +struct rpmsg_device_id {
>> + ? ? char name[RPMSG_NAME_SIZE];
>> + ? ? kernel_ulong_t driver_data ? ? ?/* Data private to the driver */
>> + ? ? ? ? ? ? ? ? ? ? __attribute__((aligned(sizeof(kernel_ulong_t))));
>> +};
>
> Should this be co-located with vio_device_id?
Sure, can't hurt (I assume you meant virtio_device_id here).
> It makes it easier to dereference in kernel code if you do:
>
> #ifdef __KERNEL__
> ? ? ? ?void *data;
> #else
> ? ? ? ?kernel_ulong_t data;
> #endif
Sure thing.
Thanks!
Ohad.
On Tue, Jun 28, 2011 at 4:46 PM, Ohad Ben-Cohen <[email protected]> wrote:
> Hi Grant,
>
> On Tue, Jun 28, 2011 at 1:21 AM, Grant Likely <[email protected]> wrote:
>>> +static int rpmsg_remove_device(struct device *dev, void *data)
>>> +{
>>> + ? ? struct rpmsg_channel *rpdev = to_rpmsg_channel(dev);
>>> +
>>> + ? ? device_unregister(dev);
>>> +
>>> + ? ? kfree(rpdev);
>>
>> put_device() I think.
>
> Don't think so, we get the device handle from device_for_each_child
> here, which doesn't call get_device (unlike device_find_child).
It's not the device_for_each_child() that you're 'putting' back from
here. Its the original kref initialization when the device was
created. Once a device is initialized, it must never be directly
kfree()'d.
g.
On Wed, Jun 29, 2011 at 1:51 AM, Grant Likely <[email protected]> wrote:
> It's not the device_for_each_child() that you're 'putting' back from
> here. ?Its the original kref initialization when the device was
> created.
device_unregister() is already calling put_device(), doesn't that deal
with the original kref init for us ?
On Tue, 21 Jun 2011 10:18:33 +0300 Ohad Ben-Cohen wrote:
> Add a virtio-based IPC bus, which enables kernel users to communicate
> with remote processors over shared memory using a simple messaging
> protocol.
>
> Assign a local address for every local endpoint that is created,
> and bind it to the user's callback. Invoke that callback when the
> destination of an inbound message is the user's address.
>
> Signed-off-by: Ohad Ben-Cohen <[email protected]>
> ---
> Documentation/ABI/testing/sysfs-bus-rpmsg | 75 +++
> Documentation/rpmsg.txt | 308 +++++++++
> drivers/Kconfig | 1 +
> drivers/Makefile | 1 +
> drivers/rpmsg/Kconfig | 14 +
> drivers/rpmsg/Makefile | 1 +
> drivers/rpmsg/virtio_rpmsg_bus.c | 1036 +++++++++++++++++++++++++++++
> include/linux/mod_devicetable.h | 10 +
> include/linux/rpmsg.h | 421 ++++++++++++
> include/linux/virtio_ids.h | 1 +
> 10 files changed, 1868 insertions(+), 0 deletions(-)
> create mode 100644 Documentation/ABI/testing/sysfs-bus-rpmsg
> create mode 100644 Documentation/rpmsg.txt
> create mode 100644 drivers/rpmsg/Kconfig
> create mode 100644 drivers/rpmsg/Makefile
> create mode 100644 drivers/rpmsg/virtio_rpmsg_bus.c
> create mode 100644 include/linux/rpmsg.h
> diff --git a/Documentation/rpmsg.txt b/Documentation/rpmsg.txt
> new file mode 100644
> index 0000000..0a7c820
> --- /dev/null
> +++ b/Documentation/rpmsg.txt
> @@ -0,0 +1,308 @@
> +Remote Processor Messaging (rpmsg) Framework
> +
> +1. Introduction
> +
> +Modern SoCs typically employ heterogeneous remote processor devices in
> +asymmetric multiprocessing (AMP) configurations, which may be running
> +different instances of operating system, whether it's Linux or any other
> +flavor of real-time OS.
> +
> +OMAP4, for example, has dual Cortex-A9, dual Cortex-M3 and a C64x+ DSP.
> +Typically, the dual cortex-A9 is running Linux in a SMP configuration,
> +and each of the other three cores (two M3 cores and a DSP) is running
> +its own instance of RTOS in an AMP configuration.
> +
> +Typically AMP remote processors employ dedicated DSP codecs and multimedia
> +hardware accelerators, and therefore are often used to offload cpu-intensive
prefer: CPU-
throughout.
> +multimedia tasks from the main application processor.
> +
> +These remote processors could also be used to control latency-sensitive
> +sensors, drive random hardware blocks, or just perform background tasks
> +while the main CPU is idling.
> +
> +Users of those remote processors can either be userland apps (e.g. multimedia
> +frameworks talking with remote OMX components) or kernel drivers (controlling
> +hardware accessible only by the remote processor, reserving kernel-controlled
> +resources on behalf of the remote processor, etc..).
> +
> +Rpmsg is a virtio-based messaging bus that allows kernel drivers to communicate
> +with remote processors available on the system. In turn, drivers could then
> +expose appropriate user space interfaces, if needed.
> +
> +When writing a driver that exposes rpmsg communication to userland, please
> +keep in mind that remote processors might have direct access to the
> +system's physical memory and/or other sensitive hardware resources (e.g. on
> +OMAP4, remote cores (/hardware accelerators) may have direct access to the
> +physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox
> +devices, hwspinlocks, etc..). Moreover, those remote processors might be
> +running RTOS where every task can access the entire memory/devices exposed
> +to the processor. To minimize the risks of rogue (/buggy) userland code
What is with the leading / here and above (/hardware) and below?
Looks like they can all be dropped.
> +exploiting (/triggering) remote bugs, and by that taking over (/down) the
> +system, it is often desired to limit userland to specific rpmsg channels (see
> +definition below) it is allowed to send messages on, and if possible/relevant,
> +minimize the amount of control it has over the content of the messages.
> +
> +Every rpmsg device is a communication channel with a remote processor (thus
> +rpmsg devices are called channels). Channels are identified by a textual name
> +and have a local ("source") rpmsg address, and remote ("destination") rpmsg
> +address.
> +
> +When a driver starts listening on a channel, it binds it with a unique
> +rpmsg src address (a 32 bits integer). This way when inbound messages arrive
(a 32-bit integer).
> +to this src address, the rpmsg core dispatches them to that driver (by invoking
> +the driver's rx handler with the payload of the incoming message).
> +
> +
> +2. User API
> +
> + int rpmsg_send(struct rpmsg_channel *rpdev, void *data, int len);
> + - sends a message across to the remote processor on a given channel.
> + The caller should specify the channel, the data it wants to send,
> + and its length (in bytes). The message will be sent on the specified
> + channel, i.e. its source and destination address fields will be
> + set to the channel's src and dst addresses.
> +
> + In case there are no TX buffers available, the function will block until
> + one becomes available (i.e. until the remote processor will consume
prefer: until the remote processor consumes
and puts it back on
> + a tx buffer and put it back on virtio's used descriptor ring),
> + or a timeout of 15 seconds elapses. When the latter happens,
> + -ERESTARTSYS is returned.
> + The function can only be called from a process context (for now).
> + Returns 0 on success and an appropriate error value on failure.
> +
> + int rpmsg_sendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst);
> + - sends a message across to the remote processor on a given channel,
> + to a destination address provided by the caller.
> + The caller should specify the channel, the data it wants to send,
> + its length (in bytes), and an explicit destination address.
> + The message will then be sent to the remote processor to which the
> + channel belongs to, using the channel's src address, and the user-provided
channel belongs,
> + dst address (thus the channel's dst address will be ignored).
> +
> + In case there are no TX buffers available, the function will block until
> + one becomes available (i.e. until the remote processor will consume
until the remote processor consumes
a tx buffer and puts it back on
> + a tx buffer and put it back on virtio's used descriptor ring),
> + or a timeout of 15 seconds elapses. When the latter happens,
> + -ERESTARTSYS is returned.
> + The function can only be called from a process context (for now).
> + Returns 0 on success and an appropriate error value on failure.
> +
> + int rpmsg_send_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst,
> + void *data, int len);
> + - sends a message across to the remote processor, using the src and dst
> + addresses provided by the user.
> + The caller should specify the channel, the data it wants to send,
> + its length (in bytes), and explicit source and destination addresses.
> + The message will then be sent to the remote processor to which the
> + channel belongs to, but the channel's src and dst addresses will be
channel belongs,
> + ignored (and the user-provided addresses will be used instead).
> +
> + In case there are no TX buffers available, the function will block until
> + one becomes available (i.e. until the remote processor will consume
until the remote processor consumes
a tx buffer and puts it back on
> + a tx buffer and put it back on virtio's used descriptor ring),
> + or a timeout of 15 seconds elapses. When the latter happens,
> + -ERESTARTSYS is returned.
> + The function can only be called from a process context (for now).
> + Returns 0 on success and an appropriate error value on failure.
> +
> + int rpmsg_trysend(struct rpmsg_channel *rpdev, void *data, int len);
> + - sends a message across to the remote processor on a given channel.
> + The caller should specify the channel, the data it wants to send,
> + and its length (in bytes). The message will be sent on the specified
> + channel, i.e. its source and destination address fields will be
> + set to the channel's src and dst addresses.
> +
> + In case there are no TX buffers available, the function will immediately
> + return -ENOMEM without waiting until one becomes available.
> + The function can only be called from a process context (for now).
> + Returns 0 on success and an appropriate error value on failure.
> +
> + int rpmsg_trysendto(struct rpmsg_channel *rpdev, void *data, int len, u32 dst)
> + - sends a message across to the remote processor on a given channel,
> + to a destination address provided by the user.
> + The user should specify the channel, the data it wants to send,
> + its length (in bytes), and an explicit destination address.
> + The message will then be sent to the remote processor to which the
> + channel belongs to, using the channel's src address, and the user-provided
channel belongs,
> + dst address (thus the channel's dst address will be ignored).
> +
> + In case there are no TX buffers available, the function will immediately
> + return -ENOMEM without waiting until one becomes available.
> + The function can only be called from a process context (for now).
> + Returns 0 on success and an appropriate error value on failure.
> +
> + int rpmsg_trysend_offchannel(struct rpmsg_channel *rpdev, u32 src, u32 dst,
> + void *data, int len);
> + - sends a message across to the remote processor, using source and
> + destination addresses provided by the user.
> + The user should specify the channel, the data it wants to send,
> + its length (in bytes), and explicit source and destination addresses.
> + The message will then be sent to the remote processor to which the
> + channel belongs to, but the channel's src and dst addresses will be
channel belongs,
> + ignored (and the user-provided addresses will be used instead).
> +
> + In case there are no TX buffers available, the function will immediately
> + return -ENOMEM without waiting until one becomes available.
> + The function can only be called from a process context (for now).
> + Returns 0 on success and an appropriate error value on failure.
> +
> + struct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_channel *rpdev,
> + void (*cb)(struct rpmsg_channel *, void *, int, void *, u32),
> + void *priv, u32 addr);
> + - every rpmsg address in the system is bound to an rx callback (so when
> + inbound messages arrive, they are dispatched by the rpmsg bus using the
> + appropriate callback handler) by means of an rpmsg_endpoint struct.
> +
> + This function allows drivers to create such an endpoint, and by that,
> + bind a callback, and possibly some private data too, to an rpmsg address
> + (either one that is known in advance, or one that will be dynamically
> + assigned for them).
> +
> + Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint
> + is already created for them when they are probed by the rpmsg bus
> + (using the rx callback they provide when they registered to the rpmsg bus).
> +
> + So things should just work for simple drivers: they already have an
> + endpoint, their rx callback is bound to their rpmsg address, and when
> + relevant inbound messages arrive (i.e. messages which their dst address
> + equals to the src address of their rpmsg channel), the driver's handler
> + is invoked to process it.
> +
> + That said, more complicated drivers might do need to allocate
> + additional rpmsg addresses, and bind them to different rx callbacks.
> + To accomplish that, those drivers need to call this function.
> + Driver should provide their channel (so the new endpoint would bind
Drivers
> + to the same remote processor their channel belongs to), an rx callback
> + function, an optional private data (which is provided back when the
> + rx callback is invoked), and an address they want to bind with the
> + callback. If addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will
> + dynamically assign them an available rpmsg address (drivers should have
> + a very good reason why not to always use RPMSG_ADDR_ANY here).
> +
> + Returns a pointer to the endpoint on success, or NULL on error.
> +
> + void rpmsg_destroy_ept(struct rpmsg_endpoint *ept);
> + - destroys an existing rpmsg endpoint. user should provide a pointer
> + to an rpmsg endpoint that was previously created with rpmsg_create_ept().
> +
> + int register_rpmsg_driver(struct rpmsg_driver *rpdrv);
> + - registers an rpmsg driver with the rpmsg bus. user should provide
> + a pointer to an rpmsg_driver struct, which contains the driver's
> + ->probe() and ->remove() functions, an rx callback, and an id_table
> + specifying the names of the channels this driver is interested to
> + be probed with.
> +
> + void unregister_rpmsg_driver(struct rpmsg_driver *rpdrv);
> + - unregisters an rpmsg driver from the rpmsg bus. user should provide
> + a pointer to a previously-registerd rpmsg_driver struct.
registered
> + Returns 0 on success, and an appropriate error value on failure.
> +
> +
> +3. Typical usage
> +
> +The following is a simple rpmsg driver, that sends an "hello!" message
> +on probe(), and whenever it receives an incoming message, it dumps its
> +content to the console.
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/rpmsg.h>
> +
> +static void rpmsg_sample_cb(struct rpmsg_channel *rpdev, void *data, int len,
> + void *priv, u32 src)
> +{
> + print_hex_dump(KERN_INFO, "incoming message:", DUMP_PREFIX_NONE,
> + 16, 1, data, len, true);
> +}
> +
> +static int rpmsg_sample_probe(struct rpmsg_channel *rpdev)
> +{
> + int err;
> +
> + dev_info(&rpdev->dev, "chnl: 0x%x -> 0x%x\n", rpdev->src, rpdev->dst);
> +
> + /* send a message on our channel */
> + err = rpmsg_send(rpdev, "hello!", 6);
> + if (err) {
> + pr_err("rpmsg_send failed: %d\n", err);
> + return err;
> + }
> +
> + return 0;
> +}
> +
> +static void __devexit rpmsg_sample_remove(struct rpmsg_channel *rpdev)
> +{
> + dev_info(&rpdev->dev, "rpmsg sample client driver is removed\n");
> +}
> +
> +static struct rpmsg_device_id rpmsg_driver_sample_id_table[] = {
> + { .name = "rpmsg-client-sample" },
> + { },
> +};
> +MODULE_DEVICE_TABLE(rpmsg, rpmsg_driver_sample_id_table);
> +
> +static struct rpmsg_driver rpmsg_sample_client = {
> + .drv.name = KBUILD_MODNAME,
> + .drv.owner = THIS_MODULE,
> + .id_table = rpmsg_driver_sample_id_table,
> + .probe = rpmsg_sample_probe,
> + .callback = rpmsg_sample_cb,
> + .remove = __devexit_p(rpmsg_sample_remove),
> +};
> +
> +static int __init init(void)
> +{
> + return register_rpmsg_driver(&rpmsg_sample_client);
> +}
> +
> +static void __exit fini(void)
> +{
> + unregister_rpmsg_driver(&rpmsg_sample_client);
> +}
> +module_init(init);
> +module_exit(fini);
> +
> +
> +4. API for implementors
> +
> +Adding rpmsg support for a new platform is relatively easy; one just needs
> +to register a VIRTIO_ID_RPMSG virtio device with the usual virtio_config_ops
> +handlers.
> +
> +For simplicity, it is recommended to register a single virtio device for
> +every physical remote processor we have in the system, but there are no
remote processor in the system,
> +hard rules here, and this decision largely depends on the use cases,
> +platform capabilities, performance requirements, etc.
> +
> +OMAP4, e.g., registers two virtio devices to communicate with the remote dual
> +Cortex-M3 processor, because each of the M3 cores executes its own OS instance
> +(see omap_rpmsg.c for reference).
> +This way each of the remote cores may have different rpmsg channels, and the
> +rpmsg core will treat them as completely independent processors (despite
> +the fact that both of are part of the same physical device, and they are
both of them are part
> +powered on/off together).
> +
> +Notable virtio implementation bits:
> +
> +* virtio features: VIRTIO_RPMSG_F_NS should be enabled if the remote
> + processor supports dynamic name service announcement messages.
> +
> + Enabling this means that rpmsg device (i.e. channel) creation is
> + completely dynamic: the remote processor announces the existence of a
> + remote rpmsg service by sending a name service message (which contains
> + the name and rpmsg addr of the remote service, see struct rpmsg_ns_msg).
> +
> + This message is then handled by the rpmsg bus, which in turn dynamically
> + creates and registers an rpmsg channel (which represents the remote service).
> + If/when a relevant rpmsg driver is registered, it will be immediately probed
> + by the bus, and can then start sending messages to the remote service.
> +
> +* virtqueue's notify handler: should inform the remote processor whenever
> + it is kicked by virtio. OMAP4 is using its mailbox device to interrupt
> + the remote processor, and inform it which virtqueue number is kicked
> + (the index of the kicked virtqueue is written to the mailbox register).
> +
> +* virtio_config_ops's ->get() handler: the rpmsg bus uses this handler
> + to request for platform-specific configuration values.
> + see enum rpmsg_platform_requests for more info.
> diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
> new file mode 100644
> index 0000000..41303f5
> --- /dev/null
> +++ b/drivers/rpmsg/Kconfig
> @@ -0,0 +1,14 @@
> +# RPMSG always gets selected by whoever wants it
> +config RPMSG
> + tristate
> + select VIRTIO
> + select VIRTIO_RING
> +
> +if RPMSG
> +
> +# OK, it's a little counter-intuitive to do this, but it puts it neatly under
> +# the rpmsg menu (and it's the approach preferred by the virtio folks).
> +
> +source "drivers/virtio/Kconfig"
It seems odd to have that Kconfig file sourced in multiple places.
Are the kconfig tools happy with that?
> +
> +endif # RPMSG
Sorry about the delay. I had most of this in my drafts folder and
forgot about it...
HTH.
---
~Randy
*** Remember to use Documentation/SubmitChecklist when testing your code ***
Hi Randy,
Thanks for your comments !
On Wed, Jun 29, 2011 at 2:44 AM, Randy Dunlap <[email protected]> wrote:
>> +hardware accelerators, and therefore are often used to offload cpu-intensive
>
> prefer: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CPU-
> throughout.
Isn't that changing the meaning a bit ? Let's stick with the original
version, I think it's more clear.
>> +system's physical memory and/or other sensitive hardware resources (e.g. on
>> +OMAP4, remote cores (/hardware accelerators) may have direct access to the
>> +physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox
>> +devices, hwspinlocks, etc..). Moreover, those remote processors might be
>> +running RTOS where every task can access the entire memory/devices exposed
>> +to the processor. To minimize the risks of rogue (/buggy) userland code
>
> What is with the leading / here and above (/hardware) and below?
/ here means "or". You can read the sentence twice, either without the
(/ ..) options or with them, and then you get two (different)
examples.
Any idea how to make it more readable ? I prefer not to drop the
second example, as it's adding information.
>> +if RPMSG
>> +
>> +# OK, it's a little counter-intuitive to do this, but it puts it neatly under
>> +# the rpmsg menu (and it's the approach preferred by the virtio folks).
>> +
>> +source "drivers/virtio/Kconfig"
>
> It seems odd to have that Kconfig file sourced in multiple places.
> Are the kconfig tools happy with that?
They are, probably because these places are mutually exclusive today:
$ git grep "drivers/virtio/Kconfig"
arch/ia64/kvm/Kconfig:source drivers/virtio/Kconfig
arch/mips/Kconfig:source drivers/virtio/Kconfig
arch/powerpc/kvm/Kconfig:source drivers/virtio/Kconfig
arch/s390/kvm/Kconfig:source drivers/virtio/Kconfig
arch/sh/Kconfig:source drivers/virtio/Kconfig
arch/tile/kvm/Kconfig:source drivers/virtio/Kconfig
arch/x86/kvm/Kconfig:source drivers/virtio/Kconfig
Now that we start using virtio for inter-processor communications,
too, we might soon bump into a situation where virtio will be sourced
twice.
Probably the solution is to move 'source "drivers/virtio/Kconfig"'
into drivers/Kconfig, and remove all other instances.
Rusty, are you ok with that ?
Thanks,
Ohad.
> Sorry about the delay. ?I had most of this in my drafts folder and
> forgot about it...
Np, thanks a lot !
Ohad.
On Wednesday 29 June 2011, Ohad Ben-Cohen wrote:
> On Wed, Jun 29, 2011 at 2:44 AM, Randy Dunlap <[email protected]> wrote:
> >> +hardware accelerators, and therefore are often used to offload cpu-intensive
> >
> > prefer: CPU-
> > throughout.
>
> Isn't that changing the meaning a bit ? Let's stick with the original
> version, I think it's more clear.
I think you misunderstood Randy, he meant you should do 's/cpu/CPU/' globally,
which does not change the meaning.
> >> +system's physical memory and/or other sensitive hardware resources (e.g. on
> >> +OMAP4, remote cores (/hardware accelerators) may have direct access to the
> >> +physical memory, gpio banks, dma controllers, i2c bus, gptimers, mailbox
> >> +devices, hwspinlocks, etc..). Moreover, those remote processors might be
> >> +running RTOS where every task can access the entire memory/devices exposed
> >> +to the processor. To minimize the risks of rogue (/buggy) userland code
> >
> > What is with the leading / here and above (/hardware) and below?
>
> / here means "or". You can read the sentence twice, either without the
> (/ ..) options or with them, and then you get two (different)
> examples.
>
> Any idea how to make it more readable ? I prefer not to drop the
> second example, as it's adding information.
The easiest way would be to replace it with 'or', as in
... remote cores (or hardware accelerators) may have ...
I would also suggest you drop the parentheses, especially in the first
case where you have two levels of them:
system's physical memory and/or other sensitive hardware resources, e.g. on
OMAP4, remote cores and hardware accelerators may have direct access to the
specific hardware blocks such as physical memory, gpio banks or dma
controllers.
Moreover, those remote processors might be...
> >> +if RPMSG
> >> +
> >> +# OK, it's a little counter-intuitive to do this, but it puts it neatly under
> >> +# the rpmsg menu (and it's the approach preferred by the virtio folks).
> >> +
> >> +source "drivers/virtio/Kconfig"
> >
> > It seems odd to have that Kconfig file sourced in multiple places.
> > Are the kconfig tools happy with that?
>
> They are, probably because these places are mutually exclusive today:
>
> $ git grep "drivers/virtio/Kconfig"
> arch/ia64/kvm/Kconfig:source drivers/virtio/Kconfig
> arch/mips/Kconfig:source drivers/virtio/Kconfig
> arch/powerpc/kvm/Kconfig:source drivers/virtio/Kconfig
> arch/s390/kvm/Kconfig:source drivers/virtio/Kconfig
> arch/sh/Kconfig:source drivers/virtio/Kconfig
> arch/tile/kvm/Kconfig:source drivers/virtio/Kconfig
> arch/x86/kvm/Kconfig:source drivers/virtio/Kconfig
>
> Now that we start using virtio for inter-processor communications,
> too, we might soon bump into a situation where virtio will be sourced
> twice.
>
> Probably the solution is to move 'source "drivers/virtio/Kconfig"'
> into drivers/Kconfig, and remove all other instances.
I think changing that would be good. However, you need to at least
restructure the contents, or they will show up in the main driver menu.
Arnd
On Wed, Jun 29, 2011 at 2:59 PM, Arnd Bergmann <[email protected]> wrote:
> On Wednesday 29 June 2011, Ohad Ben-Cohen wrote:
>> On Wed, Jun 29, 2011 at 2:44 AM, Randy Dunlap <[email protected]> wrote:
>> >> +hardware accelerators, and therefore are often used to offload cpu-intensive
>> >
>> > prefer: ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? CPU-
>> > throughout.
>>
>> Isn't that changing the meaning a bit ? Let's stick with the original
>> version, I think it's more clear.
>
> I think you misunderstood Randy, he meant you should do 's/cpu/CPU/' globally,
Oh, sorry, Randy. For some reason I thought you meant
s/cpu-intensive/CPU-throughout/ which didn't make a lot of sense to me
:)
s/cpu/CPU/ is of course nicer. thanks !
> The easiest way would be to replace it with 'or', as in
>
> ... remote cores (or hardware accelerators) may have ...
yeah, i'll do it, thanks.
It's a bit harder to get rid of the parentheses in the second
sentence, but I'll think of something too.
>> Probably the solution is to move 'source "drivers/virtio/Kconfig"'
>> into drivers/Kconfig, and remove all other instances.
>
> I think changing that would be good. However, you need to at least
> restructure the contents, or they will show up in the main driver menu.
I'll do that.
Thanks,
Ohad.
On Tue, Jun 28, 2011 at 12:00 AM, Grant Likely
<[email protected]> wrote:
> Very little for me to comment on here. ?However, something I just
> noticed. ?Why is it necessary to pass in THIS_MODULE to the
> rproc_register function? ?Having a reference to the pdev gives you the
> pointer to the driver, which has the THIS_MODULE value in it. ?That
> should be sufficient.
Nice one, thanks !
> /me also isn't sure if incrementing the refcount on the module is the
> best way to prevent the rproc from going away, but I haven't dug into
> the details in the driver code to find out. ?Drivers can get unbound
> from devices without the driver being unloaded, so I imagine there is
> an in-use count on the device itself that would prevent driver
> unbinding.
Yes, increasing the module refcount is necessary to prevent the user
from removing the driver when the rproc is used.
If the underlying device goes away while rproc is used, then
rproc_unregister should return -EBUSY, which would fail the underlying
driver's ->remove() handler (gpiolib is doing something very similar).
I have forgotten to add this check, and will add it now.
Thanks !
On Wed, Jun 29, 2011 at 9:04 AM, Ohad Ben-Cohen <[email protected]> wrote:
> On Tue, Jun 28, 2011 at 12:00 AM, Grant Likely
> <[email protected]> wrote:
>> Very little for me to comment on here. ?However, something I just
>> noticed. ?Why is it necessary to pass in THIS_MODULE to the
>> rproc_register function? ?Having a reference to the pdev gives you the
>> pointer to the driver, which has the THIS_MODULE value in it. ?That
>> should be sufficient.
>
> Nice one, thanks !
>
>> /me also isn't sure if incrementing the refcount on the module is the
>> best way to prevent the rproc from going away, but I haven't dug into
>> the details in the driver code to find out. ?Drivers can get unbound
>> from devices without the driver being unloaded, so I imagine there is
>> an in-use count on the device itself that would prevent driver
>> unbinding.
>
> Yes, increasing the module refcount is necessary to prevent the user
> from removing the driver when the rproc is used.
That prevents removing the module which necessitates unbinding the
device. However, I believe it is possible to unbind a driver
/without/ the module being unloaded. My question (for which I don't
have an answer) is whether or not there is a way to increment a
refcount on users of the driver bound to the device..
g.
On Tue, Jun 28, 2011 at 5:00 PM, Ohad Ben-Cohen <[email protected]> wrote:
> On Wed, Jun 29, 2011 at 1:51 AM, Grant Likely <[email protected]> wrote:
>> It's not the device_for_each_child() that you're 'putting' back from
>> here. ?Its the original kref initialization when the device was
>> created.
>
> device_unregister() is already calling put_device(), doesn't that deal
> with the original kref init for us ?
/me digs deeper:
device_register() has 2 parts; device_initialize() and device_add()
device_init() initialized the kref to 1 (via kobject_init()
device_add() calls get_device() to increment it to 2
Then similarly for device_unregister():
device_del() calls put_device() to decrement the kref to 1
a final put_device() call decrements the kref to 0 - which triggers a
call to the release method that kfrees the object.
So you are right that device_unregister drops the refcount to zero,
but the code still needs to be fixed to not call kfree() directly.
It also looks like rpmsg_destroy_channel() needs to be fixed to remove
the kfree call and an extra put_device() call. This is important
because the last put_device() call above might /not/ decrement the
refcount to zero is for some reason something still holds a reference
to the device. But the device will still get freed correctly when the
other holder finally calls device_put().
g.