Merge tag 'rpmsg-for-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remot...
authorArnd Bergmann <arnd@arndb.de>
Thu, 23 Feb 2012 14:11:12 +0000 (14:11 +0000)
committerArnd Bergmann <arnd@arndb.de>
Thu, 23 Feb 2012 14:11:12 +0000 (14:11 +0000)
* tag 'rpmsg-for-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc:
  remoteproc: s/big switch/lookup table/
  remoteproc: bail out if firmware has different endianess
  remoteproc: don't use virtio's weak barriers
  rpmsg: rename virtqueue_add_buf_gfp to virtqueue_add_buf
  rpmsg: depend on EXPERIMENTAL
  remoteproc: depend on EXPERIMENTAL
  rpmsg: add Kconfig menu
  remoteproc: add Kconfig menu
  remoteproc: look for truncated firmware images
  remoteproc/omap: utilize module_platform_driver
  remoteproc: remove unused resource type
  remoteproc: avoid registering a virtio device if not supported
  remoteproc: do not require an iommu
  samples/rpmsg: add an rpmsg driver sample
  rpmsg: add virtio-based remote processor messaging bus
  remoteproc/omap: add a remoteproc driver for OMAP4
  remoteproc: create rpmsg virtio device
  remoteproc: add debugfs entries
  remoteproc: add framework for controlling remote processors

26 files changed:
Documentation/ABI/testing/sysfs-bus-rpmsg [new file with mode: 0644]
Documentation/remoteproc.txt [new file with mode: 0644]
Documentation/rpmsg.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/plat-omap/include/plat/remoteproc.h [new file with mode: 0644]
drivers/Kconfig
drivers/Makefile
drivers/remoteproc/Kconfig [new file with mode: 0644]
drivers/remoteproc/Makefile [new file with mode: 0644]
drivers/remoteproc/omap_remoteproc.c [new file with mode: 0644]
drivers/remoteproc/omap_remoteproc.h [new file with mode: 0644]
drivers/remoteproc/remoteproc_core.c [new file with mode: 0644]
drivers/remoteproc/remoteproc_debugfs.c [new file with mode: 0644]
drivers/remoteproc/remoteproc_internal.h [new file with mode: 0644]
drivers/remoteproc/remoteproc_rpmsg.c [new file with mode: 0644]
drivers/rpmsg/Kconfig [new file with mode: 0644]
drivers/rpmsg/Makefile [new file with mode: 0644]
drivers/rpmsg/virtio_rpmsg_bus.c [new file with mode: 0644]
include/linux/mod_devicetable.h
include/linux/remoteproc.h [new file with mode: 0644]
include/linux/rpmsg.h [new file with mode: 0644]
include/linux/virtio_ids.h
samples/Kconfig
samples/Makefile
samples/rpmsg/Makefile [new file with mode: 0644]
samples/rpmsg/rpmsg_client_sample.c [new file with mode: 0644]

diff --git a/Documentation/ABI/testing/sysfs-bus-rpmsg b/Documentation/ABI/testing/sysfs-bus-rpmsg
new file mode 100644 (file)
index 0000000..189e419
--- /dev/null
@@ -0,0 +1,75 @@
+What:          /sys/bus/rpmsg/devices/.../name
+Date:          June 2011
+KernelVersion: 3.3
+Contact:       Ohad Ben-Cohen <ohad@wizery.com>
+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.3
+Contact:       Ohad Ben-Cohen <ohad@wizery.com>
+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.3
+Contact:       Ohad Ben-Cohen <ohad@wizery.com>
+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.3
+Contact:       Ohad Ben-Cohen <ohad@wizery.com>
+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/remoteproc.txt b/Documentation/remoteproc.txt
new file mode 100644 (file)
index 0000000..23ff734
--- /dev/null
@@ -0,0 +1,324 @@
+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 remoteproc framework 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. In addition, this framework also adds rpmsg virtio devices
+for remote processors that supports this kind of communication. This way,
+platform-specific remoteproc drivers only need to provide a few low-level
+handlers, and then all rpmsg drivers will then just work
+(for more information about the virtio-based rpmsg bus and its drivers,
+please read Documentation/rpmsg.txt).
+
+2. User API
+
+  int rproc_boot(struct rproc *rproc)
+    - Boot a remote processor (i.e. load its firmware, power it on, ...).
+      If the remote processor is already powered on, this function immediately
+      returns (successfully).
+      Returns 0 on success, and an appropriate error value otherwise.
+      Note: to use this function you should already have a valid rproc
+      handle. There are several ways to achieve that cleanly (devres, pdata,
+      the way remoteproc_rpmsg.c does this, or, if this becomes prevalent, we
+      might also consider using dev_archdata for this). See also
+      rproc_get_by_name() below.
+
+  void rproc_shutdown(struct rproc *rproc)
+    - Power off a remote processor (previously booted with rproc_boot()).
+      In case @rproc is still being used by an additional user(s), then
+      this function will just decrement the power refcount and exit,
+      without really powering off the device.
+      Every call to rproc_boot() must (eventually) be accompanied by a call
+      to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug.
+      Notes:
+      - we're not decrementing the rproc's refcount, only the power refcount.
+        which means that the @rproc handle stays valid even after
+        rproc_shutdown() returns, and users can still use it with a subsequent
+        rproc_boot(), if needed.
+      - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly
+        because rproc_shutdown() _does not_ decrement the refcount of @rproc.
+        To decrement the refcount of @rproc, use rproc_put() (but _only_ if
+        you acquired @rproc using rproc_get_by_name()).
+
+  struct rproc *rproc_get_by_name(const char *name)
+    - Find an rproc handle using the remote processor's name, and then
+      boot it. If it's already powered on, then just immediately return
+      (successfully). Returns the rproc handle on success, and NULL on failure.
+      This function increments the remote processor's refcount, so always
+      use rproc_put() to decrement it back once rproc isn't needed anymore.
+      Note: currently rproc_get_by_name() and rproc_put() are not used anymore
+      by the rpmsg bus and its drivers. We need to scrutinize the use cases
+      that still need them, and see if we can migrate them to use the non
+      name-based boot/shutdown interface.
+
+  void rproc_put(struct rproc *rproc)
+    - Decrement @rproc's power refcount and shut it down if it reaches zero
+      (essentially by just calling rproc_shutdown), and then decrement @rproc's
+      validity refcount too.
+      After this function returns, @rproc may _not_ be used anymore, and its
+      handle should be considered invalid.
+      This function should be called _iff_ the @rproc handle was grabbed by
+      calling rproc_get_by_name().
+
+3. Typical usage
+
+#include <linux/remoteproc.h>
+
+/* in case we were given a valid 'rproc' handle */
+int dummy_rproc_example(struct rproc *my_rproc)
+{
+       int ret;
+
+       /* let's power on and boot our remote processor */
+       ret = rproc_boot(my_rproc);
+       if (ret) {
+               /*
+                * something went wrong. handle it and leave.
+                */
+       }
+
+       /*
+        * our remote processor is now powered on... give it some work
+        */
+
+       /* let's shut it down now */
+       rproc_shutdown(my_rproc);
+}
+
+4. API for implementors
+
+  struct rproc *rproc_alloc(struct device *dev, const char *name,
+                               const struct rproc_ops *ops,
+                               const char *firmware, int len)
+    - Allocate a new remote processor handle, but don't register
+      it yet. Required parameters are the underlying device, the
+      name of this remote processor, platform-specific ops handlers,
+      the name of the firmware to boot this rproc with, and the
+      length of private data needed by the allocating rproc driver (in bytes).
+
+      This function should be used by rproc implementations during
+      initialization of the remote processor.
+      After creating an rproc handle using this function, and when ready,
+      implementations should then call rproc_register() to complete
+      the registration of the remote processor.
+      On success, the new rproc is returned, and on failure, NULL.
+
+      Note: _never_ directly deallocate @rproc, even if it was not registered
+      yet. Instead, if you just need to unroll rproc_alloc(), use rproc_free().
+
+  void rproc_free(struct rproc *rproc)
+    - Free an rproc handle that was allocated by rproc_alloc.
+      This function should _only_ be used if @rproc was only allocated,
+      but not registered yet.
+      If @rproc was already successfully registered (by calling
+      rproc_register()), then use rproc_unregister() instead.
+
+  int rproc_register(struct rproc *rproc)
+    - Register @rproc with the remoteproc framework, after it has been
+      allocated with rproc_alloc().
+      This is called by the platform-specific rproc implementation, whenever
+      a new remote processor device is probed.
+      Returns 0 on success and an appropriate error code otherwise.
+      Note: this function initiates an asynchronous firmware loading
+      context, which will look for virtio devices supported by the rproc's
+      firmware.
+      If found, those virtio devices will be created and added, so as a result
+      of registering this remote processor, additional virtio drivers might get
+      probed.
+      Currently, though, we only support a single RPMSG virtio vdev per remote
+      processor.
+
+  int rproc_unregister(struct rproc *rproc)
+    - Unregister a remote processor, and decrement its refcount.
+      If its refcount drops to zero, then @rproc will be freed. If not,
+      it will be freed later once the last reference is dropped.
+
+      This function should be called when the platform specific rproc
+      implementation decides to remove the rproc device. it should
+      _only_ be called if a previous invocation of rproc_register()
+      has completed successfully.
+
+      After rproc_unregister() returns, @rproc is _not_ valid anymore and
+      it shouldn't be used. More specifically, don't call rproc_free()
+      or try to directly free @rproc after rproc_unregister() returns;
+      none of these are needed, and calling them is a bug.
+
+      Returns 0 on success and -EINVAL if @rproc isn't valid.
+
+5. Implementation callbacks
+
+These callbacks should be provided by platform-specific remoteproc
+drivers:
+
+/**
+ * struct rproc_ops - platform-specific device handlers
+ * @start:     power on the device and boot it
+ * @stop:      power off the device
+ * @kick:      kick a virtqueue (virtqueue id given as a parameter)
+ */
+struct rproc_ops {
+       int (*start)(struct rproc *rproc);
+       int (*stop)(struct rproc *rproc);
+       void (*kick)(struct rproc *rproc, int vqid);
+};
+
+Every remoteproc implementation should at least provide the ->start and ->stop
+handlers. If rpmsg functionality is also desired, then the ->kick handler
+should be provided as well.
+
+The ->start() handler takes an rproc handle and should then power on the
+device and boot it (use rproc->priv to access platform-specific private data).
+The boot address, in case needed, can be found in rproc->bootaddr (remoteproc
+core puts there the ELF entry point).
+On success, 0 should be returned, and on failure, an appropriate error code.
+
+The ->stop() handler takes an rproc handle and powers the device down.
+On success, 0 is returned, and on failure, an appropriate error code.
+
+The ->kick() handler takes an rproc handle, and an index of a virtqueue
+where new message was placed in. Implementations should interrupt the remote
+processor and let it know it has pending messages. Notifying remote processors
+the exact virtqueue index to look in is optional: it is easy (and not
+too expensive) to go through the existing virtqueues and look for new buffers
+in the used rings.
+
+6. Binary Firmware Structure
+
+At this point remoteproc only supports ELF32 firmware binaries. However,
+it is quite expected that other platforms/devices which we'd want to
+support with this framework will be based on different binary formats.
+
+When those use cases show up, we will have to decouple the binary format
+from the framework core, so we can support several binary formats without
+duplicating common code.
+
+When the firmware is parsed, its various segments are loaded to memory
+according to the specified device address (might be a physical address
+if the remote processor is accessing memory directly).
+
+In addition to the standard ELF segments, most remote processors would
+also include a special section which we call "the resource table".
+
+The resource table contains system resources that the remote processor
+requires before it should be powered on, such as allocation of physically
+contiguous memory, or iommu mapping of certain on-chip peripherals.
+Remotecore will only power up the device after all the resource table's
+requirement are met.
+
+In addition to system resources, the resource table may also contain
+resource entries that publish the existence of supported features
+or configurations by the remote processor, such as trace buffers and
+supported virtio devices (and their configurations).
+
+Currently the resource table is just an array of:
+
+/**
+ * struct fw_resource - describes an entry from the resource section
+ * @type: resource type
+ * @id: index number of the resource
+ * @da: device address of the resource
+ * @pa: physical address of the resource
+ * @len: size, in bytes, of the resource
+ * @flags: properties of the resource, e.g. iommu protection required
+ * @reserved: must be 0 atm
+ * @name: name of resource
+ */
+struct fw_resource {
+       u32 type;
+       u32 id;
+       u64 da;
+       u64 pa;
+       u32 len;
+       u32 flags;
+       u8 reserved[16];
+       u8 name[48];
+} __packed;
+
+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 possibly also reply
+by overwriting a member inside 'struct fw_resource' with info about the
+allocated resource.
+
+Different resource entries use different members of this struct,
+with different meanings. This is pretty limiting and error-prone,
+so the plan is to move to variable-length TLV-based resource entries,
+where each resource will begin with a type and length fields, followed by
+its own specific structure.
+
+Here are the resource types that are currently being used:
+
+/**
+ * enum fw_resource_type - types of resource entries
+ *
+ * @RSC_CARVEOUT:   request for allocation of a physically contiguous
+ *                 memory region.
+ * @RSC_DEVMEM:     request to iommu_map a memory-based peripheral.
+ * @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.
+ * @RSC_VRING:     request for allocation of a virtio vring (address should
+ *                 be indicated in 'da', and 'len' should contain the number
+ *                 of buffers supported by the vring).
+ * @RSC_VIRTIO_DEV: announces support for a virtio device, and serves as
+ *                 the virtio header. 'da' contains the virtio device
+ *                 features, 'pa' holds the virtio guest features (host
+ *                 will write them here after they're negotiated), 'len'
+ *                 holds the virtio status, and 'flags' holds the virtio
+ *                 device id (currently only VIRTIO_ID_RPMSG is supported).
+ */
+enum fw_resource_type {
+       RSC_CARVEOUT    = 0,
+       RSC_DEVMEM      = 1,
+       RSC_TRACE       = 2,
+       RSC_VRING       = 3,
+       RSC_VIRTIO_DEV  = 4,
+       RSC_VIRTIO_CFG  = 5,
+};
+
+Most of the resource entries share the basic idea of address/length
+negotiation with the host: the firmware usually asks for memory
+of size 'len' bytes, and the host needs to allocate it and provide
+the device/physical address (when relevant) in 'da'/'pa' respectively.
+
+If the firmware is compiled with hard coded device addresses, and
+can't handle dynamically allocated 'da' values, then the 'da' field
+will contain the expected device addresses (today we actually only support
+this scheme, as there aren't yet any use cases for dynamically allocated
+device addresses).
+
+We also expect that platform-specific resource entries will show up
+at some point. When that happens, we could easily add a new RSC_PLAFORM
+type, and hand those resources to the platform-specific rproc driver to handle.
+
+7. Virtio and remoteproc
+
+The firmware should provide remoteproc information about virtio devices
+that it supports, and their configurations: a RSC_VIRTIO_DEV resource entry
+should specify the virtio device id, and subsequent RSC_VRING resource entries
+should indicate the vring size (i.e. how many buffers do they support) and
+where should they be mapped (i.e. which device address). Note: the alignment
+between the consumer and producer parts of the vring is assumed to be 4096.
+
+At this point we only support a single virtio rpmsg device per remote
+processor, but the plan is to remove this limitation. In addition, once we
+move to TLV-based resource table, the plan is to have a single RSC_VIRTIO
+entry per supported virtio device, which will include the virtio header,
+the vrings information and the virtio config space.
+
+Of course, RSC_VIRTIO resource entries are only good enough for static
+allocation of virtio devices. Dynamic allocations will also be made possible
+using the rpmsg bus (similar to how we already do dynamic allocations of
+rpmsg channels; read more about it in rpmsg.txt).
diff --git a/Documentation/rpmsg.txt b/Documentation/rpmsg.txt
new file mode 100644 (file)
index 0000000..409d9f9
--- /dev/null
@@ -0,0 +1,293 @@
+Remote Processor Messaging (rpmsg) Framework
+
+Note: this document describes the rpmsg bus and how to write rpmsg drivers.
+To learn how to add rpmsg support for new platforms, check out remoteproc.txt
+(also a resident of Documentation/).
+
+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 other sensitive hardware resources (e.g. on
+OMAP4, remote cores and 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 (or buggy) userland code
+exploiting remote bugs, and by that taking over the system, it is often
+desired to limit userland to specific rpmsg channels (see definition below)
+it can send messages on, and if possible, minimize how much 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, its rx callback is bound with
+a unique rpmsg local address (a 32-bit integer). This way when inbound messages
+arrive, the rpmsg core dispatches them to the appropriate driver according
+to their destination address (this is done by invoking the driver's rx handler
+with the payload of the inbound 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 consumes
+     a tx buffer and puts 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, 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 consumes
+     a tx buffer and puts 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, 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 consumes
+     a tx buffer and puts 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, 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, 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.
+     Drivers 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-registered 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);
+}
+module_init(init);
+
+static void __exit fini(void)
+{
+       unregister_rpmsg_driver(&rpmsg_sample_client);
+}
+module_exit(fini);
+
+Note: a similar sample which can be built and loaded can be found
+in samples/rpmsg/.
+
+4. Allocations of rpmsg channels:
+
+At this point we only support dynamic allocations of rpmsg channels.
+
+This is possible only with remote processors that have the VIRTIO_RPMSG_F_NS
+virtio device feature set. This feature bit means that the remote
+processor supports dynamic name service announcement messages.
+
+When this feature is enabled, creation of rpmsg devices (i.e. channels)
+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.
+
+The plan is also to add static creation of rpmsg channels via the virtio
+config space, but it's not implemented yet.
index 55ca0bea142c58e3eb8574084c770059de5fb779..50a62ad6d2ee518ea98f75bd149a4e130c79d8fb 100644 (file)
@@ -5550,6 +5550,13 @@ S:       Supported
 F:     drivers/base/regmap/
 F:     include/linux/regmap.h
 
+REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM
+M:     Ohad Ben-Cohen <ohad@wizery.com>
+S:     Maintained
+F:     drivers/remoteproc/
+F:     Documentation/remoteproc.txt
+F:     include/linux/remoteproc.txt
+
 RFKILL
 M:     Johannes Berg <johannes@sipsolutions.net>
 L:     linux-wireless@vger.kernel.org
diff --git a/arch/arm/plat-omap/include/plat/remoteproc.h b/arch/arm/plat-omap/include/plat/remoteproc.h
new file mode 100644 (file)
index 0000000..b10eac8
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Remote Processor - omap-specific bits
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, 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
+
+struct rproc_ops;
+struct platform_device;
+
+/*
+ * struct omap_rproc_pdata - omap remoteproc's platform data
+ * @name: the remoteproc's name
+ * @oh_name: omap hwmod device
+ * @oh_name_opt: optional, secondary omap hwmod device
+ * @firmware: name of firmware file to load
+ * @mbox_name: name of omap mailbox device to use with this rproc
+ * @ops: start/stop rproc handlers
+ * @device_enable: omap-specific handler for enabling a device
+ * @device_shutdown: omap-specific handler for shutting down a device
+ */
+struct omap_rproc_pdata {
+       const char *name;
+       const char *oh_name;
+       const char *oh_name_opt;
+       const char *firmware;
+       const char *mbox_name;
+       const struct rproc_ops *ops;
+       int (*device_enable) (struct platform_device *pdev);
+       int (*device_shutdown) (struct platform_device *pdev);
+};
+
+#if defined(CONFIG_OMAP_REMOTEPROC) || defined(CONFIG_OMAP_REMOTEPROC_MODULE)
+
+void __init omap_rproc_reserve_cma(void);
+
+#else
+
+void __init omap_rproc_reserve_cma(void)
+{
+}
+
+#endif
+
+#endif /* _PLAT_REMOTEPROC_H */
index 5afe5d1f199b83747e8f7a6b0a6d89cb0400e231..516faf6d88bac8d4cbb04fadae46b6fd1618730f 100644 (file)
@@ -132,6 +132,10 @@ source "drivers/clocksource/Kconfig"
 
 source "drivers/iommu/Kconfig"
 
+source "drivers/remoteproc/Kconfig"
+
+source "drivers/rpmsg/Kconfig"
+
 source "drivers/virt/Kconfig"
 
 source "drivers/devfreq/Kconfig"
index c07be024b962aa9ad8cce1ef635f37acd0a18fae..3fdc17709d36b26425600721f69928ac759dd860 100644 (file)
@@ -126,6 +126,8 @@ obj-y                               += clk/
 obj-$(CONFIG_HWSPINLOCK)       += hwspinlock/
 obj-$(CONFIG_NFC)              += nfc/
 obj-$(CONFIG_IOMMU_SUPPORT)    += iommu/
+obj-$(CONFIG_REMOTEPROC)       += remoteproc/
+obj-$(CONFIG_RPMSG)            += rpmsg/
 
 # Virtualization drivers
 obj-$(CONFIG_VIRT_DRIVERS)     += virt/
diff --git a/drivers/remoteproc/Kconfig b/drivers/remoteproc/Kconfig
new file mode 100644 (file)
index 0000000..25fc4cc
--- /dev/null
@@ -0,0 +1,29 @@
+menu "Remoteproc drivers (EXPERIMENTAL)"
+
+# REMOTEPROC gets selected by whoever wants it
+config REMOTEPROC
+       tristate
+       depends on EXPERIMENTAL
+
+config OMAP_REMOTEPROC
+       tristate "OMAP remoteproc support"
+       depends on ARCH_OMAP4
+       select OMAP_IOMMU
+       select REMOTEPROC
+       select OMAP_MBOX_FWK
+       select RPMSG
+       default m
+       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.
+
+endmenu
diff --git a/drivers/remoteproc/Makefile b/drivers/remoteproc/Makefile
new file mode 100644 (file)
index 0000000..df0897f
--- /dev/null
@@ -0,0 +1,9 @@
+#
+# Generic framework for controlling remote processors
+#
+
+obj-$(CONFIG_REMOTEPROC)               += remoteproc.o
+remoteproc-y                           := remoteproc_core.o
+remoteproc-y                           += remoteproc_debugfs.o
+remoteproc-y                           += remoteproc_rpmsg.o
+obj-$(CONFIG_OMAP_REMOTEPROC)          += omap_remoteproc.o
diff --git a/drivers/remoteproc/omap_remoteproc.c b/drivers/remoteproc/omap_remoteproc.c
new file mode 100644 (file)
index 0000000..aa3ce52
--- /dev/null
@@ -0,0 +1,238 @@
+/*
+ * OMAP Remote Processor driver
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ * Fernando Guzman Lugo <fernando.lugo@ti.com>
+ * Mark Grosen <mgrosen@ti.com>
+ * Suman Anna <s-anna@ti.com>
+ * Hari Kanigeri <h-kanigeri2@ti.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/remoteproc.h>
+
+#include <plat/mailbox.h>
+#include <plat/remoteproc.h>
+
+#include "omap_remoteproc.h"
+#include "remoteproc_internal.h"
+
+/**
+ * struct omap_rproc - omap remote processor state
+ * @mbox: omap mailbox handle
+ * @nb: notifier block that will be invoked on inbound mailbox messages
+ * @rproc: rproc handle
+ */
+struct omap_rproc {
+       struct omap_mbox *mbox;
+       struct notifier_block nb;
+       struct rproc *rproc;
+};
+
+/**
+ * omap_rproc_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 remoteproc core 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.
+ */
+static int omap_rproc_mbox_callback(struct notifier_block *this,
+                                       unsigned long index, void *data)
+{
+       mbox_msg_t msg = (mbox_msg_t) data;
+       struct omap_rproc *oproc = container_of(this, struct omap_rproc, nb);
+       struct device *dev = oproc->rproc->dev;
+       const char *name = oproc->rproc->name;
+
+       dev_dbg(dev, "mbox msg: 0x%x\n", msg);
+
+       switch (msg) {
+       case RP_MBOX_CRASH:
+               /* just log this for now. later, we'll also do recovery */
+               dev_err(dev, "omap rproc %s crashed\n", name);
+               break;
+       case RP_MBOX_ECHO_REPLY:
+               dev_info(dev, "received echo reply from %s\n", name);
+               break;
+       default:
+               /* ignore vq indices which are too large to be valid */
+               if (msg >= 2) {
+                       dev_warn(dev, "invalid mbox msg: 0x%x\n", msg);
+                       break;
+               }
+
+               /*
+                * At this point, 'msg' contains the index of the vring
+                * which was just triggered.
+                */
+               if (rproc_vq_interrupt(oproc->rproc, msg) == IRQ_NONE)
+                       dev_dbg(dev, "no message was found in vqid %d\n", msg);
+       }
+
+       return NOTIFY_DONE;
+}
+
+/* kick a virtqueue */
+static void omap_rproc_kick(struct rproc *rproc, int vqid)
+{
+       struct omap_rproc *oproc = rproc->priv;
+       int ret;
+
+       /* send the index of the triggered virtqueue in the mailbox payload */
+       ret = omap_mbox_msg_send(oproc->mbox, vqid);
+       if (ret)
+               dev_err(rproc->dev, "omap_mbox_msg_send failed: %d\n", ret);
+}
+
+/*
+ * Power up the remote processor.
+ *
+ * This function will be invoked only after the firmware for this rproc
+ * was loaded, parsed successfully, and all of its resource requirements
+ * were met.
+ */
+static int omap_rproc_start(struct rproc *rproc)
+{
+       struct omap_rproc *oproc = rproc->priv;
+       struct platform_device *pdev = to_platform_device(rproc->dev);
+       struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+       int ret;
+
+       oproc->nb.notifier_call = omap_rproc_mbox_callback;
+
+       /* every omap rproc is assigned a mailbox instance for messaging */
+       oproc->mbox = omap_mbox_get(pdata->mbox_name, &oproc->nb);
+       if (IS_ERR(oproc->mbox)) {
+               ret = PTR_ERR(oproc->mbox);
+               dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Ping the remote processor. this is only for sanity-sake;
+        * there is no functional effect whatsoever.
+        *
+        * Note that the reply will _not_ arrive immediately: this message
+        * will wait in the mailbox fifo until the remote processor is booted.
+        */
+       ret = omap_mbox_msg_send(oproc->mbox, RP_MBOX_ECHO_REQUEST);
+       if (ret) {
+               dev_err(rproc->dev, "omap_mbox_get failed: %d\n", ret);
+               goto put_mbox;
+       }
+
+       ret = pdata->device_enable(pdev);
+       if (ret) {
+               dev_err(rproc->dev, "omap_device_enable failed: %d\n", ret);
+               goto put_mbox;
+       }
+
+       return 0;
+
+put_mbox:
+       omap_mbox_put(oproc->mbox, &oproc->nb);
+       return ret;
+}
+
+/* power off the remote processor */
+static int omap_rproc_stop(struct rproc *rproc)
+{
+       struct platform_device *pdev = to_platform_device(rproc->dev);
+       struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+       struct omap_rproc *oproc = rproc->priv;
+       int ret;
+
+       ret = pdata->device_shutdown(pdev);
+       if (ret)
+               return ret;
+
+       omap_mbox_put(oproc->mbox, &oproc->nb);
+
+       return 0;
+}
+
+static struct rproc_ops omap_rproc_ops = {
+       .start          = omap_rproc_start,
+       .stop           = omap_rproc_stop,
+       .kick           = omap_rproc_kick,
+};
+
+static int __devinit omap_rproc_probe(struct platform_device *pdev)
+{
+       struct omap_rproc_pdata *pdata = pdev->dev.platform_data;
+       struct omap_rproc *oproc;
+       struct rproc *rproc;
+       int ret;
+
+       ret = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
+       if (ret) {
+               dev_err(pdev->dev.parent, "dma_set_coherent_mask: %d\n", ret);
+               return ret;
+       }
+
+       rproc = rproc_alloc(&pdev->dev, pdata->name, &omap_rproc_ops,
+                               pdata->firmware, sizeof(*oproc));
+       if (!rproc)
+               return -ENOMEM;
+
+       oproc = rproc->priv;
+       oproc->rproc = rproc;
+
+       platform_set_drvdata(pdev, rproc);
+
+       ret = rproc_register(rproc);
+       if (ret)
+               goto free_rproc;
+
+       return 0;
+
+free_rproc:
+       rproc_free(rproc);
+       return ret;
+}
+
+static int __devexit omap_rproc_remove(struct platform_device *pdev)
+{
+       struct rproc *rproc = platform_get_drvdata(pdev);
+
+       return rproc_unregister(rproc);
+}
+
+static struct platform_driver omap_rproc_driver = {
+       .probe = omap_rproc_probe,
+       .remove = __devexit_p(omap_rproc_remove),
+       .driver = {
+               .name = "omap-rproc",
+               .owner = THIS_MODULE,
+       },
+};
+
+module_platform_driver(omap_rproc_driver);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("OMAP Remote Processor control driver");
diff --git a/drivers/remoteproc/omap_remoteproc.h b/drivers/remoteproc/omap_remoteproc.h
new file mode 100644 (file)
index 0000000..f6d2036
--- /dev/null
@@ -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 */
diff --git a/drivers/remoteproc/remoteproc_core.c b/drivers/remoteproc/remoteproc_core.c
new file mode 100644 (file)
index 0000000..729911b
--- /dev/null
@@ -0,0 +1,1450 @@
+/*
+ * Remote Processor Framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ * Mark Grosen <mgrosen@ti.com>
+ * Fernando Guzman Lugo <fernando.lugo@ti.com>
+ * Suman Anna <s-anna@ti.com>
+ * Robert Tivy <rtivy@ti.com>
+ * Armando Uribe De Leon <x0095078@ti.com>
+ *
+ * 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/slab.h>
+#include <linux/mutex.h>
+#include <linux/dma-mapping.h>
+#include <linux/firmware.h>
+#include <linux/string.h>
+#include <linux/debugfs.h>
+#include <linux/remoteproc.h>
+#include <linux/iommu.h>
+#include <linux/klist.h>
+#include <linux/elf.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_ring.h>
+#include <asm/byteorder.h>
+
+#include "remoteproc_internal.h"
+
+static void klist_rproc_get(struct klist_node *n);
+static void klist_rproc_put(struct klist_node *n);
+
+/*
+ * klist of the available remote processors.
+ *
+ * We need this in order to support name-based lookups (needed by the
+ * rproc_get_by_name()).
+ *
+ * That said, we don't use rproc_get_by_name() anymore within the rpmsg
+ * framework. The use cases that do require its existence should be
+ * scrutinized, and hopefully migrated to rproc_boot() using device-based
+ * binding.
+ *
+ * If/when this materializes, we could drop the klist (and the by_name
+ * API).
+ */
+static DEFINE_KLIST(rprocs, klist_rproc_get, klist_rproc_put);
+
+typedef int (*rproc_handle_resources_t)(struct rproc *rproc,
+                               struct fw_resource *rsc, int len);
+typedef int (*rproc_handle_resource_t)(struct rproc *rproc,
+                               struct fw_resource *rsc);
+
+/*
+ * This is the IOMMU fault handler we register with the IOMMU API
+ * (when relevant; not all remote processors access memory through
+ * an IOMMU).
+ *
+ * IOMMU core will invoke this handler whenever the remote processor
+ * will try to access an unmapped device address.
+ *
+ * Currently this is mostly a stub, but it will be later used to trigger
+ * the recovery of the remote processor.
+ */
+static int rproc_iommu_fault(struct iommu_domain *domain, struct device *dev,
+               unsigned long iova, int flags)
+{
+       dev_err(dev, "iommu fault: da 0x%lx flags 0x%x\n", iova, flags);
+
+       /*
+        * Let the iommu core know we're not really handling this fault;
+        * we just plan to use this as a recovery trigger.
+        */
+       return -ENOSYS;
+}
+
+static int rproc_enable_iommu(struct rproc *rproc)
+{
+       struct iommu_domain *domain;
+       struct device *dev = rproc->dev;
+       int ret;
+
+       /*
+        * We currently use iommu_present() to decide if an IOMMU
+        * setup is needed.
+        *
+        * This works for simple cases, but will easily fail with
+        * platforms that do have an IOMMU, but not for this specific
+        * rproc.
+        *
+        * This will be easily solved by introducing hw capabilities
+        * that will be set by the remoteproc driver.
+        */
+       if (!iommu_present(dev->bus)) {
+               dev_dbg(dev, "iommu not found\n");
+               return 0;
+       }
+
+       domain = iommu_domain_alloc(dev->bus);
+       if (!domain) {
+               dev_err(dev, "can't alloc iommu domain\n");
+               return -ENOMEM;
+       }
+
+       iommu_set_fault_handler(domain, rproc_iommu_fault);
+
+       ret = iommu_attach_device(domain, dev);
+       if (ret) {
+               dev_err(dev, "can't attach iommu device: %d\n", ret);
+               goto free_domain;
+       }
+
+       rproc->domain = domain;
+
+       return 0;
+
+free_domain:
+       iommu_domain_free(domain);
+       return ret;
+}
+
+static void rproc_disable_iommu(struct rproc *rproc)
+{
+       struct iommu_domain *domain = rproc->domain;
+       struct device *dev = rproc->dev;
+
+       if (!domain)
+               return;
+
+       iommu_detach_device(domain, dev);
+       iommu_domain_free(domain);
+
+       return;
+}
+
+/*
+ * Some remote processors will ask us to allocate them physically contiguous
+ * memory regions (which we call "carveouts"), and map them to specific
+ * device addresses (which are hardcoded in the firmware).
+ *
+ * They may then ask us to copy objects into specific device addresses (e.g.
+ * code/data sections) or expose us certain symbols in other device address
+ * (e.g. their trace buffer).
+ *
+ * This function is an internal helper with which we can go over the allocated
+ * carveouts and translate specific device address to kernel virtual addresses
+ * so we can access the referenced memory.
+ *
+ * Note: phys_to_virt(iommu_iova_to_phys(rproc->domain, da)) will work too,
+ * but only on kernel direct mapped RAM memory. Instead, we're just using
+ * here the output of the DMA API, which should be more correct.
+ */
+static void *rproc_da_to_va(struct rproc *rproc, u64 da, int len)
+{
+       struct rproc_mem_entry *carveout;
+       void *ptr = NULL;
+
+       list_for_each_entry(carveout, &rproc->carveouts, node) {
+               int offset = da - carveout->da;
+
+               /* try next carveout if da is too small */
+               if (offset < 0)
+                       continue;
+
+               /* try next carveout if da is too large */
+               if (offset + len > carveout->len)
+                       continue;
+
+               ptr = carveout->va + offset;
+
+               break;
+       }
+
+       return ptr;
+}
+
+/**
+ * rproc_load_segments() - load firmware segments to memory
+ * @rproc: remote processor which will be booted using these fw segments
+ * @elf_data: the content of the ELF firmware image
+ * @len: firmware size (in bytes)
+ *
+ * This function loads the firmware segments to memory, where the remote
+ * processor expects them.
+ *
+ * Some remote processors will expect their code and data to be placed
+ * in specific device addresses, and can't have them dynamically assigned.
+ *
+ * We currently support only those kind of remote processors, and expect
+ * the program header's paddr member to contain those addresses. We then go
+ * through the physically contiguous "carveout" memory regions which we
+ * allocated (and mapped) earlier on behalf of the remote processor,
+ * and "translate" device address to kernel addresses, so we can copy the
+ * segments where they are expected.
+ *
+ * Currently we only support remote processors that required carveout
+ * allocations and got them mapped onto their iommus. Some processors
+ * might be different: they might not have iommus, and would prefer to
+ * directly allocate memory for every segment/resource. This is not yet
+ * supported, though.
+ */
+static int
+rproc_load_segments(struct rproc *rproc, const u8 *elf_data, size_t len)
+{
+       struct device *dev = rproc->dev;
+       struct elf32_hdr *ehdr;
+       struct elf32_phdr *phdr;
+       int i, ret = 0;
+
+       ehdr = (struct elf32_hdr *)elf_data;
+       phdr = (struct elf32_phdr *)(elf_data + ehdr->e_phoff);
+
+       /* go through the available ELF segments */
+       for (i = 0; i < ehdr->e_phnum; i++, phdr++) {
+               u32 da = phdr->p_paddr;
+               u32 memsz = phdr->p_memsz;
+               u32 filesz = phdr->p_filesz;
+               u32 offset = phdr->p_offset;
+               void *ptr;
+
+               if (phdr->p_type != PT_LOAD)
+                       continue;
+
+               dev_dbg(dev, "phdr: type %d da 0x%x memsz 0x%x filesz 0x%x\n",
+                                       phdr->p_type, da, memsz, filesz);
+
+               if (filesz > memsz) {
+                       dev_err(dev, "bad phdr filesz 0x%x memsz 0x%x\n",
+                                                       filesz, memsz);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               if (offset + filesz > len) {
+                       dev_err(dev, "truncated fw: need 0x%x avail 0x%x\n",
+                                       offset + filesz, len);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               /* grab the kernel address for this device address */
+               ptr = rproc_da_to_va(rproc, da, memsz);
+               if (!ptr) {
+                       dev_err(dev, "bad phdr da 0x%x mem 0x%x\n", da, memsz);
+                       ret = -EINVAL;
+                       break;
+               }
+
+               /* put the segment where the remote processor expects it */
+               if (phdr->p_filesz)
+                       memcpy(ptr, elf_data + phdr->p_offset, filesz);
+
+               /*
+                * Zero out remaining memory for this segment.
+                *
+                * This isn't strictly required since dma_alloc_coherent already
+                * did this for us. albeit harmless, we may consider removing
+                * this.
+                */
+               if (memsz > filesz)
+                       memset(ptr + filesz, 0, memsz - filesz);
+       }
+
+       return ret;
+}
+
+/**
+ * rproc_handle_virtio_hdr() - handle a virtio header resource
+ * @rproc: the remote processor
+ * @rsc: the resource descriptor
+ *
+ * The existence of this virtio hdr resource entry means that the firmware
+ * of this @rproc supports this virtio device.
+ *
+ * Currently we support only a single virtio device of type VIRTIO_ID_RPMSG,
+ * but the plan is to remove this limitation and support any number
+ * of virtio devices (and of any type). We'll also add support for dynamically
+ * adding (and removing) virtio devices over the rpmsg bus, but small
+ * firmwares that doesn't want to get involved with rpmsg will be able
+ * to simple use the resource table for this.
+ *
+ * At this point this virtio header entry is rather simple: it just
+ * announces the virtio device id and the supported virtio device features.
+ * The plan though is to extend this to include the vring information and
+ * the virtio config space, too (but first, some resource table overhaul
+ * is needed: move from fixed-sized to variable-length TLV entries).
+ *
+ * For now, the 'flags' member of the resource entry contains the virtio
+ * device id, the 'da' member contains the device features, and 'pa' is
+ * where we need to store the guest features once negotiation completes.
+ * As usual, the 'id' member of this resource contains the index of this
+ * resource type (i.e. is this the first virtio hdr entry, the 2nd, ...).
+ *
+ * Returns 0 on success, or an appropriate error code otherwise
+ */
+static int rproc_handle_virtio_hdr(struct rproc *rproc, struct fw_resource *rsc)
+{
+       struct rproc_vdev *rvdev;
+
+       /* we only support VIRTIO_ID_RPMSG devices for now */
+       if (rsc->flags != VIRTIO_ID_RPMSG) {
+               dev_warn(rproc->dev, "unsupported vdev: %d\n", rsc->flags);
+               return -EINVAL;
+       }
+
+       /* we only support a single vdev per rproc for now */
+       if (rsc->id || rproc->rvdev) {
+               dev_warn(rproc->dev, "redundant vdev entry: %s\n", rsc->name);
+               return -EINVAL;
+       }
+
+       rvdev = kzalloc(sizeof(struct rproc_vdev), GFP_KERNEL);
+       if (!rvdev)
+               return -ENOMEM;
+
+       /* remember the device features */
+       rvdev->dfeatures = rsc->da;
+
+       rproc->rvdev = rvdev;
+       rvdev->rproc = rproc;
+
+       return 0;
+}
+
+/**
+ * rproc_handle_vring() - handle a vring fw resource
+ * @rproc: the remote processor
+ * @rsc: the vring resource descriptor
+ *
+ * This resource entry requires allocation of non-cacheable memory
+ * for a virtio vring. Currently we only support two vrings per remote
+ * processor, required for the virtio rpmsg device.
+ *
+ * The 'len' member of @rsc should contain the number of buffers this vring
+ * support and 'da' should either contain the device address where
+ * the remote processor is expecting the vring, or indicate that
+ * dynamically allocation of the vring's device address is supported.
+ *
+ * Note: 'da' is currently not handled. This will be revised when the generic
+ * iommu-based DMA API will arrive, or a dynanic & non-iommu use case show
+ * up. Meanwhile, statically-addressed iommu-based images should use
+ * RSC_DEVMEM resource entries to map their require 'da' to the physical
+ * address of their base CMA region.
+ *
+ * Returns 0 on success, or an appropriate error code otherwise
+ */
+static int rproc_handle_vring(struct rproc *rproc, struct fw_resource *rsc)
+{
+       struct device *dev = rproc->dev;
+       struct rproc_vdev *rvdev = rproc->rvdev;
+       dma_addr_t dma;
+       int size, id = rsc->id;
+       void *va;
+
+       /* no vdev is in place ? */
+       if (!rvdev) {
+               dev_err(dev, "vring requested without a virtio dev entry\n");
+               return -EINVAL;
+       }
+
+       /* the firmware must provide the expected queue size */
+       if (!rsc->len) {
+               dev_err(dev, "missing expected queue size\n");
+               return -EINVAL;
+       }
+
+       /* we currently support two vrings per rproc (for rx and tx) */
+       if (id >= ARRAY_SIZE(rvdev->vring)) {
+               dev_err(dev, "%s: invalid vring id %d\n", rsc->name, id);
+               return -EINVAL;
+       }
+
+       /* have we already allocated this vring id ? */
+       if (rvdev->vring[id].len) {
+               dev_err(dev, "%s: duplicated id %d\n", rsc->name, id);
+               return -EINVAL;
+       }
+
+       /* actual size of vring (in bytes) */
+       size = PAGE_ALIGN(vring_size(rsc->len, AMP_VRING_ALIGN));
+
+       /*
+        * Allocate non-cacheable memory for the vring. In the future
+        * this call will also configure the IOMMU for us
+        */
+       va = dma_alloc_coherent(dev, size, &dma, GFP_KERNEL);
+       if (!va) {
+               dev_err(dev, "dma_alloc_coherent failed\n");
+               return -ENOMEM;
+       }
+
+       dev_dbg(dev, "vring%d: va %p dma %x qsz %d ring size %x\n", id, va,
+                                       dma, rsc->len, size);
+
+       rvdev->vring[id].len = rsc->len;
+       rvdev->vring[id].va = va;
+       rvdev->vring[id].dma = dma;
+
+       return 0;
+}
+
+/**
+ * rproc_handle_trace() - 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,
+ * export it via debugfs.
+ *
+ * Currently, the 'da' member of @rsc should contain the device address
+ * where the remote processor is dumping the traces. Later we could also
+ * support dynamically allocating this address using the generic
+ * DMA API (but currently there isn't a use case for that).
+ *
+ * Returns 0 on success, or an appropriate error code otherwise
+ */
+static int rproc_handle_trace(struct rproc *rproc, struct fw_resource *rsc)
+{
+       struct rproc_mem_entry *trace;
+       struct device *dev = rproc->dev;
+       void *ptr;
+       char name[15];
+
+       /* what's the kernel address of this resource ? */
+       ptr = rproc_da_to_va(rproc, rsc->da, rsc->len);
+       if (!ptr) {
+               dev_err(dev, "erroneous trace resource entry\n");
+               return -EINVAL;
+       }
+
+       trace = kzalloc(sizeof(*trace), GFP_KERNEL);
+       if (!trace) {
+               dev_err(dev, "kzalloc trace failed\n");
+               return -ENOMEM;
+       }
+
+       /* set the trace buffer dma properties */
+       trace->len = rsc->len;
+       trace->va = ptr;
+
+       /* make sure snprintf always null terminates, even if truncating */
+       snprintf(name, sizeof(name), "trace%d", rproc->num_traces);
+
+       /* create the debugfs entry */
+       trace->priv = rproc_create_trace_file(name, rproc, trace);
+       if (!trace->priv) {
+               trace->va = NULL;
+               kfree(trace);
+               return -EINVAL;
+       }
+
+       list_add_tail(&trace->node, &rproc->traces);
+
+       rproc->num_traces++;
+
+       dev_dbg(dev, "%s added: va %p, da 0x%llx, len 0x%x\n", name, ptr,
+                                               rsc->da, rsc->len);
+
+       return 0;
+}
+
+/**
+ * rproc_handle_devmem() - handle devmem resource entry
+ * @rproc: remote processor handle
+ * @rsc: the devmem resource entry
+ *
+ * Remote processors commonly need to access certain on-chip peripherals.
+ *
+ * Some of these remote processors access memory via an iommu device,
+ * and might require us to configure their iommu before they can access
+ * the on-chip peripherals they need.
+ *
+ * This resource entry is a request to map such a peripheral device.
+ *
+ * These devmem entries will contain the physical address of the device in
+ * the 'pa' member. If a specific device address is expected, then 'da' will
+ * contain it (currently this is the only use case supported). 'len' will
+ * contain the size of the physical region we need to map.
+ *
+ * Currently we just "trust" those devmem entries to contain valid physical
+ * addresses, but this is going to change: we want the implementations to
+ * tell us ranges of physical addresses the firmware is allowed to request,
+ * and not allow firmwares to request access to physical addresses that
+ * are outside those ranges.
+ */
+static int rproc_handle_devmem(struct rproc *rproc, struct fw_resource *rsc)
+{
+       struct rproc_mem_entry *mapping;
+       int ret;
+
+       /* no point in handling this resource without a valid iommu domain */
+       if (!rproc->domain)
+               return -EINVAL;
+
+       mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
+       if (!mapping) {
+               dev_err(rproc->dev, "kzalloc mapping failed\n");
+               return -ENOMEM;
+       }
+
+       ret = iommu_map(rproc->domain, rsc->da, rsc->pa, rsc->len, rsc->flags);
+       if (ret) {
+               dev_err(rproc->dev, "failed to map devmem: %d\n", ret);
+               goto out;
+       }
+
+       /*
+        * We'll need this info later when we'll want to unmap everything
+        * (e.g. on shutdown).
+        *
+        * We can't trust the remote processor not to change the resource
+        * table, so we must maintain this info independently.
+        */
+       mapping->da = rsc->da;
+       mapping->len = rsc->len;
+       list_add_tail(&mapping->node, &rproc->mappings);
+
+       dev_dbg(rproc->dev, "mapped devmem pa 0x%llx, da 0x%llx, len 0x%x\n",
+                                       rsc->pa, rsc->da, rsc->len);
+
+       return 0;
+
+out:
+       kfree(mapping);
+       return ret;
+}
+
+/**
+ * rproc_handle_carveout() - handle phys contig memory allocation requests
+ * @rproc: rproc handle
+ * @rsc: the resource entry
+ *
+ * This function will handle firmware requests for allocation of physically
+ * contiguous memory regions.
+ *
+ * These request entries should come first in the firmware's resource table,
+ * as other firmware entries might request placing other data objects inside
+ * these memory regions (e.g. data/code segments, trace resource entries, ...).
+ *
+ * Allocating memory this way helps utilizing the reserved physical memory
+ * (e.g. CMA) more efficiently, and also minimizes the number of TLB entries
+ * needed to map it (in case @rproc is using an IOMMU). Reducing the TLB
+ * pressure is important; it may have a substantial impact on performance.
+ */
+static int rproc_handle_carveout(struct rproc *rproc, struct fw_resource *rsc)
+{
+       struct rproc_mem_entry *carveout, *mapping;
+       struct device *dev = rproc->dev;
+       dma_addr_t dma;
+       void *va;
+       int ret;
+
+       mapping = kzalloc(sizeof(*mapping), GFP_KERNEL);
+       if (!mapping) {
+               dev_err(dev, "kzalloc mapping failed\n");
+               return -ENOMEM;
+       }
+
+       carveout = kzalloc(sizeof(*carveout), GFP_KERNEL);
+       if (!carveout) {
+               dev_err(dev, "kzalloc carveout failed\n");
+               ret = -ENOMEM;
+               goto free_mapping;
+       }
+
+       va = dma_alloc_coherent(dev, rsc->len, &dma, GFP_KERNEL);
+       if (!va) {
+               dev_err(dev, "failed to dma alloc carveout: %d\n", rsc->len);
+               ret = -ENOMEM;
+               goto free_carv;
+       }
+
+       dev_dbg(dev, "carveout va %p, dma %x, len 0x%x\n", va, dma, rsc->len);
+
+       /*
+        * Ok, this is non-standard.
+        *
+        * Sometimes we can't rely on the generic iommu-based DMA API
+        * to dynamically allocate the device address and then set the IOMMU
+        * tables accordingly, because some remote processors might
+        * _require_ us to use hard coded device addresses that their
+        * firmware was compiled with.
+        *
+        * In this case, we must use the IOMMU API directly and map
+        * the memory to the device address as expected by the remote
+        * processor.
+        *
+        * Obviously such remote processor devices should not be configured
+        * to use the iommu-based DMA API: we expect 'dma' to contain the
+        * physical address in this case.
+        */
+       if (rproc->domain) {
+               ret = iommu_map(rproc->domain, rsc->da, dma, rsc->len,
+                                                               rsc->flags);
+               if (ret) {
+                       dev_err(dev, "iommu_map failed: %d\n", ret);
+                       goto dma_free;
+               }
+
+               /*
+                * We'll need this info later when we'll want to unmap
+                * everything (e.g. on shutdown).
+                *
+                * We can't trust the remote processor not to change the
+                * resource table, so we must maintain this info independently.
+                */
+               mapping->da = rsc->da;
+               mapping->len = rsc->len;
+               list_add_tail(&mapping->node, &rproc->mappings);
+
+               dev_dbg(dev, "carveout mapped 0x%llx to 0x%x\n", rsc->da, dma);
+
+               /*
+                * Some remote processors might need to know the pa
+                * even though they are behind an IOMMU. E.g., OMAP4's
+                * remote M3 processor needs this so it can control
+                * on-chip hardware accelerators that are not behind
+                * the IOMMU, and therefor must know the pa.
+                *
+                * Generally we don't want to expose physical addresses
+                * if we don't have to (remote processors are generally
+                * _not_ trusted), so we might want to do this only for
+                * remote processor that _must_ have this (e.g. OMAP4's
+                * dual M3 subsystem).
+                */
+               rsc->pa = dma;
+       }
+
+       carveout->va = va;
+       carveout->len = rsc->len;
+       carveout->dma = dma;
+       carveout->da = rsc->da;
+
+       list_add_tail(&carveout->node, &rproc->carveouts);
+
+       return 0;
+
+dma_free:
+       dma_free_coherent(dev, rsc->len, va, dma);
+free_carv:
+       kfree(carveout);
+free_mapping:
+       kfree(mapping);
+       return ret;
+}
+
+/*
+ * A lookup table for resource handlers. The indices are defined in
+ * enum fw_resource_type.
+ */
+static rproc_handle_resource_t rproc_handle_rsc[] = {
+       [RSC_CARVEOUT] = rproc_handle_carveout,
+       [RSC_DEVMEM] = rproc_handle_devmem,
+       [RSC_TRACE] = rproc_handle_trace,
+       [RSC_VRING] = rproc_handle_vring,
+       [RSC_VIRTIO_DEV] = NULL, /* handled early upon registration */
+};
+
+/* handle firmware resource entries before booting the remote processor */
+static int
+rproc_handle_boot_rsc(struct rproc *rproc, struct fw_resource *rsc, int len)
+{
+       struct device *dev = rproc->dev;
+       rproc_handle_resource_t handler;
+       int ret = 0;
+
+       for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc)) {
+               dev_dbg(dev, "rsc: type %d, da 0x%llx, pa 0x%llx, len 0x%x, "
+                       "id %d, name %s, flags %x\n", rsc->type, rsc->da,
+                       rsc->pa, rsc->len, rsc->id, rsc->name, rsc->flags);
+
+               if (rsc->type >= RSC_LAST) {
+                       dev_warn(dev, "unsupported resource %d\n", rsc->type);
+                       continue;
+               }
+
+               handler = rproc_handle_rsc[rsc->type];
+               if (!handler)
+                       continue;
+
+               ret = handler(rproc, rsc);
+               if (ret)
+                       break;
+       }
+
+       return ret;
+}
+
+/* handle firmware resource entries while registering the remote processor */
+static int
+rproc_handle_virtio_rsc(struct rproc *rproc, struct fw_resource *rsc, int len)
+{
+       struct device *dev = rproc->dev;
+       int ret = -ENODEV;
+
+       for (; len >= sizeof(*rsc); rsc++, len -= sizeof(*rsc))
+               if (rsc->type == RSC_VIRTIO_DEV) {
+                       dev_dbg(dev, "found vdev %d/%s features %llx\n",
+                                       rsc->flags, rsc->name, rsc->da);
+                       ret = rproc_handle_virtio_hdr(rproc, rsc);
+                       break;
+               }
+
+       return ret;
+}
+
+/**
+ * rproc_handle_resources() - find and handle the resource table
+ * @rproc: the rproc handle
+ * @elf_data: the content of the ELF firmware image
+ * @len: firmware size (in bytes)
+ * @handler: function that should be used to handle the resource table
+ *
+ * This function finds the resource table inside the remote processor's
+ * firmware, and invoke a user-supplied handler with it (we have two
+ * possible handlers: one is invoked upon registration of @rproc,
+ * in order to register the supported virito devices, and the other is
+ * invoked when @rproc is actually booted).
+ *
+ * Currently this function fails if a resource table doesn't exist.
+ * This restriction will be removed when we'll start supporting remote
+ * processors that don't need a resource table.
+ */
+static int rproc_handle_resources(struct rproc *rproc, const u8 *elf_data,
+                               size_t len, rproc_handle_resources_t handler)
+
+{
+       struct elf32_hdr *ehdr;
+       struct elf32_shdr *shdr;
+       const char *name_table;
+       int i, ret = -EINVAL;
+
+       ehdr = (struct elf32_hdr *)elf_data;
+       shdr = (struct elf32_shdr *)(elf_data + ehdr->e_shoff);
+       name_table = elf_data + shdr[ehdr->e_shstrndx].sh_offset;
+
+       /* look for the resource table and handle it */
+       for (i = 0; i < ehdr->e_shnum; i++, shdr++) {
+               if (!strcmp(name_table + shdr->sh_name, ".resource_table")) {
+                       struct fw_resource *table = (struct fw_resource *)
+                                               (elf_data + shdr->sh_offset);
+
+                       if (shdr->sh_offset + shdr->sh_size > len) {
+                               dev_err(rproc->dev,
+                                       "truncated fw: need 0x%x avail 0x%x\n",
+                                       shdr->sh_offset + shdr->sh_size, len);
+                               ret = -EINVAL;
+                       }
+
+                       ret = handler(rproc, table, shdr->sh_size);
+
+                       break;
+               }
+       }
+
+       return ret;
+}
+
+/**
+ * rproc_resource_cleanup() - clean up and free all acquired resources
+ * @rproc: rproc handle
+ *
+ * This function will free all resources acquired for @rproc, and it
+ * is called when @rproc shuts down, or just failed booting.
+ */
+static void rproc_resource_cleanup(struct rproc *rproc)
+{
+       struct rproc_mem_entry *entry, *tmp;
+       struct device *dev = rproc->dev;
+       struct rproc_vdev *rvdev = rproc->rvdev;
+       int i;
+
+       /* clean up debugfs trace entries */
+       list_for_each_entry_safe(entry, tmp, &rproc->traces, node) {
+               rproc_remove_trace_file(entry->priv);
+               rproc->num_traces--;
+               list_del(&entry->node);
+               kfree(entry);
+       }
+
+       /* free the coherent memory allocated for the vrings */
+       for (i = 0; rvdev && i < ARRAY_SIZE(rvdev->vring); i++) {
+               int qsz = rvdev->vring[i].len;
+               void *va = rvdev->vring[i].va;
+               int dma = rvdev->vring[i].dma;
+
+               /* virtqueue size is expressed in number of buffers supported */
+               if (qsz) {
+                       /* how many bytes does this vring really occupy ? */
+                       int size = PAGE_ALIGN(vring_size(qsz, AMP_VRING_ALIGN));
+
+                       dma_free_coherent(rproc->dev, size, va, dma);
+
+                       rvdev->vring[i].len = 0;
+               }
+       }
+
+       /* clean up carveout allocations */
+       list_for_each_entry_safe(entry, tmp, &rproc->carveouts, node) {
+               dma_free_coherent(dev, entry->len, entry->va, entry->dma);
+               list_del(&entry->node);
+               kfree(entry);
+       }
+
+       /* clean up iommu mapping entries */
+       list_for_each_entry_safe(entry, tmp, &rproc->mappings, node) {
+               size_t unmapped;
+
+               unmapped = iommu_unmap(rproc->domain, entry->da, entry->len);
+               if (unmapped != entry->len) {
+                       /* nothing much to do besides complaining */
+                       dev_err(dev, "failed to unmap %u/%u\n", entry->len,
+                                                               unmapped);
+               }
+
+               list_del(&entry->node);
+               kfree(entry);
+       }
+}
+
+/* make sure this fw image is sane */
+static int rproc_fw_sanity_check(struct rproc *rproc, const struct firmware *fw)
+{
+       const char *name = rproc->firmware;
+       struct device *dev = rproc->dev;
+       struct elf32_hdr *ehdr;
+
+       if (!fw) {
+               dev_err(dev, "failed to load %s\n", name);
+               return -EINVAL;
+       }
+
+       if (fw->size < sizeof(struct elf32_hdr)) {
+               dev_err(dev, "Image is too small\n");
+               return -EINVAL;
+       }
+
+       ehdr = (struct elf32_hdr *)fw->data;
+
+       /* We assume the firmware has the same endianess as the host */
+# ifdef __LITTLE_ENDIAN
+       if (ehdr->e_ident[EI_DATA] != ELFDATA2LSB) {
+# else /* BIG ENDIAN */
+       if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB) {
+# endif
+               dev_err(dev, "Unsupported firmware endianess\n");
+               return -EINVAL;
+       }
+
+       if (fw->size < ehdr->e_shoff + sizeof(struct elf32_shdr)) {
+               dev_err(dev, "Image is too small\n");
+               return -EINVAL;
+       }
+
+       if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG)) {
+               dev_err(dev, "Image is corrupted (bad magic)\n");
+               return -EINVAL;
+       }
+
+       if (ehdr->e_phnum == 0) {
+               dev_err(dev, "No loadable segments\n");
+               return -EINVAL;
+       }
+
+       if (ehdr->e_phoff > fw->size) {
+               dev_err(dev, "Firmware size is too small\n");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+/*
+ * take a firmware and boot a remote processor with it.
+ */
+static int rproc_fw_boot(struct rproc *rproc, const struct firmware *fw)
+{
+       struct device *dev = rproc->dev;
+       const char *name = rproc->firmware;
+       struct elf32_hdr *ehdr;
+       int ret;
+
+       ret = rproc_fw_sanity_check(rproc, fw);
+       if (ret)
+               return ret;
+
+       ehdr = (struct elf32_hdr *)fw->data;
+
+       dev_info(dev, "Booting fw image %s, size %d\n", name, fw->size);
+
+       /*
+        * if enabling an IOMMU isn't relevant for this rproc, this is
+        * just a nop
+        */
+       ret = rproc_enable_iommu(rproc);
+       if (ret) {
+               dev_err(dev, "can't enable iommu: %d\n", ret);
+               return ret;
+       }
+
+       /*
+        * The ELF entry point is the rproc's boot addr (though this is not
+        * a configurable property of all remote processors: some will always
+        * boot at a specific hardcoded address).
+        */
+       rproc->bootaddr = ehdr->e_entry;
+
+       /* handle fw resources which are required to boot rproc */
+       ret = rproc_handle_resources(rproc, fw->data, fw->size,
+                                               rproc_handle_boot_rsc);
+       if (ret) {
+               dev_err(dev, "Failed to process resources: %d\n", ret);
+               goto clean_up;
+       }
+
+       /* load the ELF segments to memory */
+       ret = rproc_load_segments(rproc, fw->data, fw->size);
+       if (ret) {
+               dev_err(dev, "Failed to load program segments: %d\n", ret);
+               goto clean_up;
+       }
+
+       /* power up the remote processor */
+       ret = rproc->ops->start(rproc);
+       if (ret) {
+               dev_err(dev, "can't start rproc %s: %d\n", rproc->name, ret);
+               goto clean_up;
+       }
+
+       rproc->state = RPROC_RUNNING;
+
+       dev_info(dev, "remote processor %s is now up\n", rproc->name);
+
+       return 0;
+
+clean_up:
+       rproc_resource_cleanup(rproc);
+       rproc_disable_iommu(rproc);
+       return ret;
+}
+
+/*
+ * take a firmware and look for virtio devices to register.
+ *
+ * Note: this function is called asynchronously upon registration of the
+ * remote processor (so we must wait until it completes before we try
+ * to unregister the device. one other option is just to use kref here,
+ * that might be cleaner).
+ */
+static void rproc_fw_config_virtio(const struct firmware *fw, void *context)
+{
+       struct rproc *rproc = context;
+       struct device *dev = rproc->dev;
+       int ret;
+
+       if (rproc_fw_sanity_check(rproc, fw) < 0)
+               goto out;
+
+       /* does the fw supports any virtio devices ? */
+       ret = rproc_handle_resources(rproc, fw->data, fw->size,
+                                               rproc_handle_virtio_rsc);
+       if (ret) {
+               dev_info(dev, "No fw virtio device was found\n");
+               goto out;
+       }
+
+       /* add the virtio device (currently only rpmsg vdevs are supported) */
+       ret = rproc_add_rpmsg_vdev(rproc);
+       if (ret)
+               goto out;
+
+out:
+       if (fw)
+               release_firmware(fw);
+       /* allow rproc_unregister() contexts, if any, to proceed */
+       complete_all(&rproc->firmware_loading_complete);
+}
+
+/**
+ * rproc_boot() - boot a remote processor
+ * @rproc: handle of a remote processor
+ *
+ * Boot a remote processor (i.e. load its firmware, power it on, ...).
+ *
+ * If the remote processor is already powered on, this function immediately
+ * returns (successfully).
+ *
+ * Returns 0 on success, and an appropriate error value otherwise.
+ */
+int rproc_boot(struct rproc *rproc)
+{
+       const struct firmware *firmware_p;
+       struct device *dev;
+       int ret;
+
+       if (!rproc) {
+               pr_err("invalid rproc handle\n");
+               return -EINVAL;
+       }
+
+       dev = rproc->dev;
+
+       ret = mutex_lock_interruptible(&rproc->lock);
+       if (ret) {
+               dev_err(dev, "can't lock rproc %s: %d\n", rproc->name, ret);
+               return ret;
+       }
+
+       /* loading a firmware is required */
+       if (!rproc->firmware) {
+               dev_err(dev, "%s: no firmware to load\n", __func__);
+               ret = -EINVAL;
+               goto unlock_mutex;
+       }
+
+       /* prevent underlying implementation from being removed */
+       if (!try_module_get(dev->driver->owner)) {
+               dev_err(dev, "%s: can't get owner\n", __func__);
+               ret = -EINVAL;
+               goto unlock_mutex;
+       }
+
+       /* skip the boot process if rproc is already powered up */
+       if (atomic_inc_return(&rproc->power) > 1) {
+               ret = 0;
+               goto unlock_mutex;
+       }
+
+       dev_info(dev, "powering up %s\n", rproc->name);
+
+       /* load firmware */
+       ret = request_firmware(&firmware_p, rproc->firmware, dev);
+       if (ret < 0) {
+               dev_err(dev, "request_firmware failed: %d\n", ret);
+               goto downref_rproc;
+       }
+
+       ret = rproc_fw_boot(rproc, firmware_p);
+
+       release_firmware(firmware_p);
+
+downref_rproc:
+       if (ret) {
+               module_put(dev->driver->owner);
+               atomic_dec(&rproc->power);
+       }
+unlock_mutex:
+       mutex_unlock(&rproc->lock);
+       return ret;
+}
+EXPORT_SYMBOL(rproc_boot);
+
+/**
+ * rproc_shutdown() - power off the remote processor
+ * @rproc: the remote processor
+ *
+ * Power off a remote processor (previously booted with rproc_boot()).
+ *
+ * In case @rproc is still being used by an additional user(s), then
+ * this function will just decrement the power refcount and exit,
+ * without really powering off the device.
+ *
+ * Every call to rproc_boot() must (eventually) be accompanied by a call
+ * to rproc_shutdown(). Calling rproc_shutdown() redundantly is a bug.
+ *
+ * Notes:
+ * - we're not decrementing the rproc's refcount, only the power refcount.
+ *   which means that the @rproc handle stays valid even after rproc_shutdown()
+ *   returns, and users can still use it with a subsequent rproc_boot(), if
+ *   needed.
+ * - don't call rproc_shutdown() to unroll rproc_get_by_name(), exactly
+ *   because rproc_shutdown() _does not_ decrement the refcount of @rproc.
+ *   To decrement the refcount of @rproc, use rproc_put() (but _only_ if
+ *   you acquired @rproc using rproc_get_by_name()).
+ */
+void rproc_shutdown(struct rproc *rproc)
+{
+       struct device *dev = rproc->dev;
+       int ret;
+
+       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 (!atomic_dec_and_test(&rproc->power))
+               goto out;
+
+       /* power off the remote processor */
+       ret = rproc->ops->stop(rproc);
+       if (ret) {
+               atomic_inc(&rproc->power);
+               dev_err(dev, "can't stop rproc: %d\n", ret);
+               goto out;
+       }
+
+       /* clean up all acquired resources */
+       rproc_resource_cleanup(rproc);
+
+       rproc_disable_iommu(rproc);
+
+       rproc->state = RPROC_OFFLINE;
+
+       dev_info(dev, "stopped remote processor %s\n", rproc->name);
+
+out:
+       mutex_unlock(&rproc->lock);
+       if (!ret)
+               module_put(dev->driver->owner);
+}
+EXPORT_SYMBOL(rproc_shutdown);
+
+/**
+ * rproc_release() - completely deletes the existence of a remote processor
+ * @kref: the rproc's kref
+ *
+ * This function should _never_ be called directly.
+ *
+ * The only reasonable location to use it is as an argument when kref_put'ing
+ * @rproc's refcount.
+ *
+ * This way it will be called when no one holds a valid pointer to this @rproc
+ * anymore (and obviously after it is removed from the rprocs klist).
+ *
+ * Note: this function is not static because rproc_vdev_release() needs it when
+ * it decrements @rproc's refcount.
+ */
+void rproc_release(struct kref *kref)
+{
+       struct rproc *rproc = container_of(kref, struct rproc, refcount);
+
+       dev_info(rproc->dev, "removing %s\n", rproc->name);
+
+       rproc_delete_debug_dir(rproc);
+
+       /* at this point no one holds a reference to rproc anymore */
+       kfree(rproc);
+}
+
+/* will be called when an rproc is added to the rprocs klist */
+static void klist_rproc_get(struct klist_node *n)
+{
+       struct rproc *rproc = container_of(n, struct rproc, node);
+
+       kref_get(&rproc->refcount);
+}
+
+/* will be called when an rproc is removed from the rprocs klist */
+static void klist_rproc_put(struct klist_node *n)
+{
+       struct rproc *rproc = container_of(n, struct rproc, node);
+
+       kref_put(&rproc->refcount, rproc_release);
+}
+
+static struct rproc *next_rproc(struct klist_iter *i)
+{
+       struct klist_node *n;
+
+       n = klist_next(i);
+       if (!n)
+               return NULL;
+
+       return container_of(n, struct rproc, node);
+}
+
+/**
+ * rproc_get_by_name() - find a remote processor by name and boot it
+ * @name: name of the remote processor
+ *
+ * Finds an rproc handle using the remote processor's name, and then
+ * boot it. If it's already powered on, then just immediately return
+ * (successfully).
+ *
+ * Returns the rproc handle on success, and NULL on failure.
+ *
+ * This function increments the remote processor's refcount, so always
+ * use rproc_put() to decrement it back once rproc isn't needed anymore.
+ *
+ * Note: currently this function (and its counterpart rproc_put()) are not
+ * used anymore by the rpmsg subsystem. We need to scrutinize the use cases
+ * that still need them, and see if we can migrate them to use the non
+ * name-based boot/shutdown interface.
+ */
+struct rproc *rproc_get_by_name(const char *name)
+{
+       struct rproc *rproc;
+       struct klist_iter i;
+       int ret;
+
+       /* find the remote processor, and upref its refcount */
+       klist_iter_init(&rprocs, &i);
+       while ((rproc = next_rproc(&i)) != NULL)
+               if (!strcmp(rproc->name, name)) {
+                       kref_get(&rproc->refcount);
+                       break;
+               }
+       klist_iter_exit(&i);
+
+       /* can't find this rproc ? */
+       if (!rproc) {
+               pr_err("can't find remote processor %s\n", name);
+               return NULL;
+       }
+
+       ret = rproc_boot(rproc);
+       if (ret < 0) {
+               kref_put(&rproc->refcount, rproc_release);
+               return NULL;
+       }
+
+       return rproc;
+}
+EXPORT_SYMBOL(rproc_get_by_name);
+
+/**
+ * rproc_put() - decrement the refcount of a remote processor, and shut it down
+ * @rproc: the remote processor
+ *
+ * This function tries to shutdown @rproc, and it then decrements its
+ * refcount.
+ *
+ * After this function returns, @rproc may _not_ be used anymore, and its
+ * handle should be considered invalid.
+ *
+ * This function should be called _iff_ the @rproc handle was grabbed by
+ * calling rproc_get_by_name().
+ */
+void rproc_put(struct rproc *rproc)
+{
+       /* try to power off the remote processor */
+       rproc_shutdown(rproc);
+
+       /* downref rproc's refcount */
+       kref_put(&rproc->refcount, rproc_release);
+}
+EXPORT_SYMBOL(rproc_put);
+
+/**
+ * rproc_register() - register a remote processor
+ * @rproc: the remote processor handle to register
+ *
+ * Registers @rproc with the remoteproc framework, after it has been
+ * allocated with rproc_alloc().
+ *
+ * This is called by the platform-specific rproc implementation, whenever
+ * a new remote processor device is probed.
+ *
+ * Returns 0 on success and an appropriate error code otherwise.
+ *
+ * Note: this function initiates an asynchronous firmware loading
+ * context, which will look for virtio devices supported by the rproc's
+ * firmware.
+ *
+ * If found, those virtio devices will be created and added, so as a result
+ * of registering this remote processor, additional virtio drivers will be
+ * probed.
+ *
+ * Currently, though, we only support a single RPMSG virtio vdev per remote
+ * processor.
+ */
+int rproc_register(struct rproc *rproc)
+{
+       struct device *dev = rproc->dev;
+       int ret = 0;
+
+       /* expose to rproc_get_by_name users */
+       klist_add_tail(&rproc->node, &rprocs);
+
+       dev_info(rproc->dev, "%s is available\n", rproc->name);
+
+       dev_info(dev, "Note: remoteproc is still under development and considered experimental.\n");
+       dev_info(dev, "THE BINARY FORMAT IS NOT YET FINALIZED, and backward compatibility isn't yet guaranteed.\n");
+
+       /* create debugfs entries */
+       rproc_create_debug_dir(rproc);
+
+       /* rproc_unregister() calls must wait until async loader completes */
+       init_completion(&rproc->firmware_loading_complete);
+
+       /*
+        * We must retrieve early virtio configuration info from
+        * the firmware (e.g. whether to register a virtio rpmsg device,
+        * what virtio features does it support, ...).
+        *
+        * We're initiating an asynchronous firmware loading, so we can
+        * be built-in kernel code, without hanging the boot process.
+        */
+       ret = request_firmware_nowait(THIS_MODULE, FW_ACTION_HOTPLUG,
+                                       rproc->firmware, dev, GFP_KERNEL,
+                                       rproc, rproc_fw_config_virtio);
+       if (ret < 0) {
+               dev_err(dev, "request_firmware_nowait failed: %d\n", ret);
+               complete_all(&rproc->firmware_loading_complete);
+               klist_remove(&rproc->node);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL(rproc_register);
+
+/**
+ * rproc_alloc() - allocate a remote processor handle
+ * @dev: the underlying device
+ * @name: name of this remote processor
+ * @ops: platform-specific handlers (mainly start/stop)
+ * @firmware: name of firmware file to load
+ * @len: length of private data needed by the rproc driver (in bytes)
+ *
+ * Allocates a new remote processor handle, but does not register
+ * it yet.
+ *
+ * This function should be used by rproc implementations during initialization
+ * of the remote processor.
+ *
+ * After creating an rproc handle using this function, and when ready,
+ * implementations should then call rproc_register() to complete
+ * the registration of the remote processor.
+ *
+ * On success the new rproc is returned, and on failure, NULL.
+ *
+ * Note: _never_ directly deallocate @rproc, even if it was not registered
+ * yet. Instead, if you just need to unroll rproc_alloc(), use rproc_free().
+ */
+struct rproc *rproc_alloc(struct device *dev, const char *name,
+                               const struct rproc_ops *ops,
+                               const char *firmware, int len)
+{
+       struct rproc *rproc;
+
+       if (!dev || !name || !ops)
+               return NULL;
+
+       rproc = kzalloc(sizeof(struct rproc) + len, GFP_KERNEL);
+       if (!rproc) {
+               dev_err(dev, "%s: kzalloc failed\n", __func__);
+               return NULL;
+       }
+
+       rproc->dev = dev;
+       rproc->name = name;
+       rproc->ops = ops;
+       rproc->firmware = firmware;
+       rproc->priv = &rproc[1];
+
+       atomic_set(&rproc->power, 0);
+
+       kref_init(&rproc->refcount);
+
+       mutex_init(&rproc->lock);
+
+       INIT_LIST_HEAD(&rproc->carveouts);
+       INIT_LIST_HEAD(&rproc->mappings);
+       INIT_LIST_HEAD(&rproc->traces);
+
+       rproc->state = RPROC_OFFLINE;
+
+       return rproc;
+}
+EXPORT_SYMBOL(rproc_alloc);
+
+/**
+ * rproc_free() - free an rproc handle that was allocated by rproc_alloc
+ * @rproc: the remote processor handle
+ *
+ * This function should _only_ be used if @rproc was only allocated,
+ * but not registered yet.
+ *
+ * If @rproc was already successfully registered (by calling rproc_register()),
+ * then use rproc_unregister() instead.
+ */
+void rproc_free(struct rproc *rproc)
+{
+       kfree(rproc);
+}
+EXPORT_SYMBOL(rproc_free);
+
+/**
+ * rproc_unregister() - unregister a remote processor
+ * @rproc: rproc handle to unregister
+ *
+ * Unregisters a remote processor, and decrements its refcount.
+ * If its refcount drops to zero, then @rproc will be freed. If not,
+ * it will be freed later once the last reference is dropped.
+ *
+ * This function should be called when the platform specific rproc
+ * implementation decides to remove the rproc device. it should
+ * _only_ be called if a previous invocation of rproc_register()
+ * has completed successfully.
+ *
+ * After rproc_unregister() returns, @rproc is _not_ valid anymore and
+ * it shouldn't be used. More specifically, don't call rproc_free()
+ * or try to directly free @rproc after rproc_unregister() returns;
+ * none of these are needed, and calling them is a bug.
+ *
+ * Returns 0 on success and -EINVAL if @rproc isn't valid.
+ */
+int rproc_unregister(struct rproc *rproc)
+{
+       if (!rproc)
+               return -EINVAL;
+
+       /* if rproc is just being registered, wait */
+       wait_for_completion(&rproc->firmware_loading_complete);
+
+       /* was an rpmsg vdev created ? */
+       if (rproc->rvdev)
+               rproc_remove_rpmsg_vdev(rproc);
+
+       klist_remove(&rproc->node);
+
+       kref_put(&rproc->refcount, rproc_release);
+
+       return 0;
+}
+EXPORT_SYMBOL(rproc_unregister);
+
+static int __init remoteproc_init(void)
+{
+       rproc_init_debugfs();
+       return 0;
+}
+module_init(remoteproc_init);
+
+static void __exit remoteproc_exit(void)
+{
+       rproc_exit_debugfs();
+}
+module_exit(remoteproc_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Generic Remote Processor Framework");
diff --git a/drivers/remoteproc/remoteproc_debugfs.c b/drivers/remoteproc/remoteproc_debugfs.c
new file mode 100644 (file)
index 0000000..70277a5
--- /dev/null
@@ -0,0 +1,179 @@
+/*
+ * Remote Processor Framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Mark Grosen <mgrosen@ti.com>
+ * Brian Swetland <swetland@google.com>
+ * Fernando Guzman Lugo <fernando.lugo@ti.com>
+ * Suman Anna <s-anna@ti.com>
+ * Robert Tivy <rtivy@ti.com>
+ * Armando Uribe De Leon <x0095078@ti.com>
+ *
+ * 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/debugfs.h>
+#include <linux/remoteproc.h>
+#include <linux/device.h>
+
+/* remoteproc 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.
+ *
+ * We will most probably improve the rproc tracing facilities later on,
+ * but this kind of lightweight and simple mechanism is always good to have,
+ * as it provides very early tracing with little to no dependencies at all.
+ */
+static ssize_t rproc_trace_read(struct file *filp, char __user *userbuf,
+                                               size_t count, loff_t *ppos)
+{
+       struct rproc_mem_entry *trace = filp->private_data;
+       int len = strnlen(trace->va, trace->len);
+
+       return simple_read_from_buffer(userbuf, count, ppos, trace->va, len);
+}
+
+static int rproc_open_generic(struct inode *inode, struct file *file)
+{
+       file->private_data = inode->i_private;
+
+       return 0;
+}
+
+static const struct file_operations trace_rproc_ops = {
+       .read = rproc_trace_read,
+       .open = rproc_open_generic,
+       .llseek = generic_file_llseek,
+};
+
+/*
+ * A state-to-string lookup table, for exposing a human readable state
+ * via debugfs. Always keep in sync with enum rproc_state
+ */
+static const char * const rproc_state_string[] = {
+       "offline",
+       "suspended",
+       "running",
+       "crashed",
+       "invalid",
+};
+
+/* expose the state of the remote processor via debugfs */
+static ssize_t rproc_state_read(struct file *filp, char __user *userbuf,
+                                               size_t count, loff_t *ppos)
+{
+       struct rproc *rproc = filp->private_data;
+       unsigned int state;
+       char buf[30];
+       int i;
+
+       state = rproc->state > RPROC_LAST ? RPROC_LAST : rproc->state;
+
+       i = snprintf(buf, 30, "%.28s (%d)\n", rproc_state_string[state],
+                                                       rproc->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,
+};
+
+/* expose the name of the remote processor via debugfs */
+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[100];
+       int i;
+
+       i = snprintf(buf, sizeof(buf), "%.98s\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,
+};
+
+void rproc_remove_trace_file(struct dentry *tfile)
+{
+       debugfs_remove(tfile);
+}
+
+struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
+                                       struct rproc_mem_entry *trace)
+{
+       struct dentry *tfile;
+
+       tfile = debugfs_create_file(name, 0400, rproc->dbg_dir,
+                                               trace, &trace_rproc_ops);
+       if (!tfile) {
+               dev_err(rproc->dev, "failed to create debugfs trace entry\n");
+               return NULL;
+       }
+
+       return tfile;
+}
+
+void rproc_delete_debug_dir(struct rproc *rproc)
+{
+       if (!rproc->dbg_dir)
+               return;
+
+       debugfs_remove_recursive(rproc->dbg_dir);
+}
+
+void rproc_create_debug_dir(struct rproc *rproc)
+{
+       struct device *dev = rproc->dev;
+
+       if (!rproc_dbg)
+               return;
+
+       rproc->dbg_dir = debugfs_create_dir(dev_name(dev), rproc_dbg);
+       if (!rproc->dbg_dir)
+               return;
+
+       debugfs_create_file("name", 0400, rproc->dbg_dir,
+                                       rproc, &rproc_name_ops);
+       debugfs_create_file("state", 0400, rproc->dbg_dir,
+                                       rproc, &rproc_state_ops);
+}
+
+void __init rproc_init_debugfs(void)
+{
+       if (debugfs_initialized()) {
+               rproc_dbg = debugfs_create_dir(KBUILD_MODNAME, NULL);
+               if (!rproc_dbg)
+                       pr_err("can't create debugfs dir\n");
+       }
+}
+
+void __exit rproc_exit_debugfs(void)
+{
+       if (rproc_dbg)
+               debugfs_remove(rproc_dbg);
+}
diff --git a/drivers/remoteproc/remoteproc_internal.h b/drivers/remoteproc/remoteproc_internal.h
new file mode 100644 (file)
index 0000000..8b2fc40
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * Remote processor framework
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ *
+ * 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.
+ */
+
+#ifndef REMOTEPROC_INTERNAL_H
+#define REMOTEPROC_INTERNAL_H
+
+#include <linux/irqreturn.h>
+
+struct rproc;
+
+/* from remoteproc_core.c */
+void rproc_release(struct kref *kref);
+irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id);
+
+/* from remoteproc_rpmsg.c */
+int rproc_add_rpmsg_vdev(struct rproc *);
+void rproc_remove_rpmsg_vdev(struct rproc *rproc);
+
+/* from remoteproc_debugfs.c */
+void rproc_remove_trace_file(struct dentry *tfile);
+struct dentry *rproc_create_trace_file(const char *name, struct rproc *rproc,
+                                       struct rproc_mem_entry *trace);
+void rproc_delete_debug_dir(struct rproc *rproc);
+void rproc_create_debug_dir(struct rproc *rproc);
+void rproc_init_debugfs(void);
+void rproc_exit_debugfs(void);
+
+#endif /* REMOTEPROC_INTERNAL_H */
diff --git a/drivers/remoteproc/remoteproc_rpmsg.c b/drivers/remoteproc/remoteproc_rpmsg.c
new file mode 100644 (file)
index 0000000..4f73e81
--- /dev/null
@@ -0,0 +1,299 @@
+/*
+ * Remote processor messaging transport (OMAP platform-specific bits)
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ *
+ * 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.
+ */
+
+#include <linux/export.h>
+#include <linux/remoteproc.h>
+#include <linux/rpmsg.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/virtio_ids.h>
+#include <linux/virtio_ring.h>
+#include <linux/err.h>
+#include <linux/kref.h>
+#include <linux/slab.h>
+
+#include "remoteproc_internal.h"
+
+/**
+ * struct rproc_virtio_vq_info - virtqueue state
+ * @vq_id: a unique index of this virtqueue (unique for this @rproc)
+ * @rproc: handle to the remote processor
+ *
+ * Such a struct will be maintained for every virtqueue we're
+ * using to communicate with the remote processor
+ */
+struct rproc_virtio_vq_info {
+       __u16 vq_id;
+       struct rproc *rproc;
+};
+
+/* kick the remote processor, and let it know which virtqueue to poke at */
+static void rproc_virtio_notify(struct virtqueue *vq)
+{
+       struct rproc_virtio_vq_info *rpvq = vq->priv;
+       struct rproc *rproc = rpvq->rproc;
+
+       dev_dbg(rproc->dev, "kicking vq id: %d\n", rpvq->vq_id);
+
+       rproc->ops->kick(rproc, rpvq->vq_id);
+}
+
+/**
+ * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
+ * @rproc: handle to the remote processor
+ * @vq_id: index of the signalled virtqueue
+ *
+ * This function should be called by the platform-specific rproc driver,
+ * when the remote processor signals that a specific virtqueue has pending
+ * messages available.
+ *
+ * Returns IRQ_NONE if no message was found in the @vq_id virtqueue,
+ * and otherwise returns IRQ_HANDLED.
+ */
+irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id)
+{
+       return vring_interrupt(0, rproc->rvdev->vq[vq_id]);
+}
+EXPORT_SYMBOL(rproc_vq_interrupt);
+
+static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
+                                   unsigned id,
+                                   void (*callback)(struct virtqueue *vq),
+                                   const char *name)
+{
+       struct rproc *rproc = vdev_to_rproc(vdev);
+       struct rproc_vdev *rvdev = rproc->rvdev;
+       struct rproc_virtio_vq_info *rpvq;
+       struct virtqueue *vq;
+       void *addr;
+       int ret, len;
+
+       rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL);
+       if (!rpvq)
+               return ERR_PTR(-ENOMEM);
+
+       rpvq->rproc = rproc;
+       rpvq->vq_id = id;
+
+       addr = rvdev->vring[id].va;
+       len = rvdev->vring[id].len;
+
+       dev_dbg(rproc->dev, "vring%d: va %p qsz %d\n", id, addr, len);
+
+       /*
+        * Create the new vq, and tell virtio we're not interested in
+        * the 'weak' smp barriers, since we're talking with a real device.
+        */
+       vq = vring_new_virtqueue(len, AMP_VRING_ALIGN, vdev, false, addr,
+                                       rproc_virtio_notify, callback, name);
+       if (!vq) {
+               dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name);
+               ret = -ENOMEM;
+               goto free_rpvq;
+       }
+
+       rvdev->vq[id] = vq;
+       vq->priv = rpvq;
+
+       return vq;
+
+free_rpvq:
+       kfree(rpvq);
+       return ERR_PTR(ret);
+}
+
+static void rproc_virtio_del_vqs(struct virtio_device *vdev)
+{
+       struct virtqueue *vq, *n;
+       struct rproc *rproc = vdev_to_rproc(vdev);
+
+       list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
+               struct rproc_virtio_vq_info *rpvq = vq->priv;
+               vring_del_virtqueue(vq);
+               kfree(rpvq);
+       }
+
+       /* power down the remote processor */
+       rproc_shutdown(rproc);
+}
+
+static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
+                      struct virtqueue *vqs[],
+                      vq_callback_t *callbacks[],
+                      const char *names[])
+{
+       struct rproc *rproc = vdev_to_rproc(vdev);
+       int i, ret;
+
+       /* we maintain two virtqueues per remote processor (for RX and TX) */
+       if (nvqs != 2)
+               return -EINVAL;
+
+       /* boot the remote processor */
+       ret = rproc_boot(rproc);
+       if (ret) {
+               dev_err(rproc->dev, "rproc_boot() failed %d\n", ret);
+               goto error;
+       }
+
+       for (i = 0; i < nvqs; ++i) {
+               vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
+               if (IS_ERR(vqs[i])) {
+                       ret = PTR_ERR(vqs[i]);
+                       goto error;
+               }
+       }
+
+       return 0;
+
+error:
+       rproc_virtio_del_vqs(vdev);
+       return ret;
+}
+
+/*
+ * We don't support yet real virtio status semantics.
+ *
+ * The plan is to provide this via the VIRTIO HDR resource entry
+ * which is part of the firmware: this way the remote processor
+ * will be able to access the status values as set by us.
+ */
+static u8 rproc_virtio_get_status(struct virtio_device *vdev)
+{
+       return 0;
+}
+
+static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
+{
+       dev_dbg(&vdev->dev, "new status: %d\n", status);
+}
+
+static void rproc_virtio_reset(struct virtio_device *vdev)
+{
+       dev_dbg(&vdev->dev, "reset !\n");
+}
+
+/* provide the vdev features as retrieved from the firmware */
+static u32 rproc_virtio_get_features(struct virtio_device *vdev)
+{
+       struct rproc *rproc = vdev_to_rproc(vdev);
+
+       /* we only support a single vdev device for now */
+       return rproc->rvdev->dfeatures;
+}
+
+static void rproc_virtio_finalize_features(struct virtio_device *vdev)
+{
+       struct rproc *rproc = vdev_to_rproc(vdev);
+
+       /* Give virtio_ring a chance to accept features */
+       vring_transport_features(vdev);
+
+       /*
+        * Remember the finalized features of our vdev, and provide it
+        * to the remote processor once it is powered on.
+        *
+        * Similarly to the status field, we don't expose yet the negotiated
+        * features to the remote processors at this point. This will be
+        * fixed as part of a small resource table overhaul and then an
+        * extension of the virtio resource entries.
+        */
+       rproc->rvdev->gfeatures = vdev->features[0];
+}
+
+static struct virtio_config_ops rproc_virtio_config_ops = {
+       .get_features   = rproc_virtio_get_features,
+       .finalize_features = rproc_virtio_finalize_features,
+       .find_vqs       = rproc_virtio_find_vqs,
+       .del_vqs        = rproc_virtio_del_vqs,
+       .reset          = rproc_virtio_reset,
+       .set_status     = rproc_virtio_set_status,
+       .get_status     = rproc_virtio_get_status,
+};
+
+/*
+ * This function is called whenever vdev is released, and is responsible
+ * to decrement the remote processor's refcount taken when vdev was
+ * added.
+ *
+ * Never call this function directly; it will be called by the driver
+ * core when needed.
+ */
+static void rproc_vdev_release(struct device *dev)
+{
+       struct virtio_device *vdev = dev_to_virtio(dev);
+       struct rproc *rproc = vdev_to_rproc(vdev);
+
+       kref_put(&rproc->refcount, rproc_release);
+}
+
+/**
+ * rproc_add_rpmsg_vdev() - create an rpmsg virtio device
+ * @rproc: the rproc handle
+ *
+ * This function is called if virtio rpmsg support was found in the
+ * firmware of the remote processor.
+ *
+ * Today we only support creating a single rpmsg vdev (virtio device),
+ * but the plan is to remove this limitation. At that point this interface
+ * will be revised/extended.
+ */
+int rproc_add_rpmsg_vdev(struct rproc *rproc)
+{
+       struct device *dev = rproc->dev;
+       struct rproc_vdev *rvdev = rproc->rvdev;
+       int ret;
+
+       rvdev->vdev.id.device   = VIRTIO_ID_RPMSG,
+       rvdev->vdev.config      = &rproc_virtio_config_ops,
+       rvdev->vdev.dev.parent  = dev;
+       rvdev->vdev.dev.release = rproc_vdev_release;
+
+       /*
+        * We're indirectly making a non-temporary copy of the rproc pointer
+        * here, because drivers probed with this vdev will indirectly
+        * access the wrapping rproc.
+        *
+        * Therefore we must increment the rproc refcount here, and decrement
+        * it _only_ when the vdev is released.
+        */
+       kref_get(&rproc->refcount);
+
+       ret = register_virtio_device(&rvdev->vdev);
+       if (ret) {
+               kref_put(&rproc->refcount, rproc_release);
+               dev_err(dev, "failed to register vdev: %d\n", ret);
+       }
+
+       return ret;
+}
+
+/**
+ * rproc_remove_rpmsg_vdev() - remove an rpmsg vdev device
+ * @rproc: the rproc handle
+ *
+ * This function is called whenever @rproc is removed _iff_ an rpmsg
+ * vdev was created beforehand.
+ */
+void rproc_remove_rpmsg_vdev(struct rproc *rproc)
+{
+       struct rproc_vdev *rvdev = rproc->rvdev;
+
+       unregister_virtio_device(&rvdev->vdev);
+}
diff --git a/drivers/rpmsg/Kconfig b/drivers/rpmsg/Kconfig
new file mode 100644 (file)
index 0000000..32aead6
--- /dev/null
@@ -0,0 +1,10 @@
+menu "Rpmsg drivers (EXPERIMENTAL)"
+
+# RPMSG always gets selected by whoever wants it
+config RPMSG
+       tristate
+       select VIRTIO
+       select VIRTIO_RING
+       depends on EXPERIMENTAL
+
+endmenu
diff --git a/drivers/rpmsg/Makefile b/drivers/rpmsg/Makefile
new file mode 100644 (file)
index 0000000..7617fcb
--- /dev/null
@@ -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 (file)
index 0000000..8980ac2
--- /dev/null
@@ -0,0 +1,1026 @@
+/*
+ * Virtio-based remote processor messaging bus
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ *
+ * 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/dma-mapping.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>
+#include <linux/mutex.h>
+
+/**
+ * struct virtproc_info - virtual remote processor state
+ * @vdev:      the virtio device
+ * @rvq:       rx virtqueue
+ * @svq:       tx virtqueue
+ * @rbufs:     kernel address of rx buffers
+ * @sbufs:     kernel address of tx buffers
+ * @last_sbuf: index of last tx buffer used
+ * @bufs_dma:  dma base addr of the buffers
+ * @tx_lock:   protects svq, sbufs and sleepers, to allow concurrent senders.
+ *             sending a message might require waking up a dozing remote
+ *             processor, which involves sleeping, hence the mutex.
+ * @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;
+       dma_addr_t bufs_dma;
+       struct mutex tx_lock;
+       struct idr endpoints;
+       struct mutex endpoints_lock;
+       wait_queue_head_t sendq;
+       atomic_t sleepers;
+       struct rpmsg_endpoint *ns_ept;
+};
+
+/**
+ * struct rpmsg_channel_info - internal channel info representation
+ * @name: name of service
+ * @src: local address
+ * @dst: destination address
+ */
+struct rpmsg_channel_info {
+       char name[RPMSG_NAME_SIZE];
+       u32 src;
+       u32 dst;
+};
+
+#define to_rpmsg_channel(d) container_of(d, struct rpmsg_channel, dev)
+#define to_rpmsg_driver(d) container_of(d, struct rpmsg_driver, drv)
+
+/*
+ * We're allocating 512 buffers of 512 bytes for communications, and then
+ * using the first 256 buffers for RX, and the last 256 buffers for TX.
+ *
+ * Each buffer will have 16 bytes for the msg header and 496 bytes for
+ * the payload.
+ *
+ * This will require a total space of 256KB for the buffers.
+ *
+ * We might also want to add support for user-provided buffers in time.
+ * This will allow bigger buffer size flexibility, and can also be used
+ * to achieve zero-copy messaging.
+ *
+ * Note that these numbers are purely a decision of this driver - we
+ * can change this without changing anything in the firmware of the remote
+ * processor.
+ */
+#define RPMSG_NUM_BUFS         (512)
+#define RPMSG_BUF_SIZE         (512)
+#define RPMSG_TOTAL_BUF_SPACE  (RPMSG_NUM_BUFS * RPMSG_BUF_SIZE)
+
+/*
+ * 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)
+
+/* sysfs 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 (and free running) index for rpmsg devices.
+ *
+ * Yeah, we're not recycling those numbers (yet?). will be easy
+ * to change if/when we want to.
+ */
+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)
+{
+       return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;
+}
+
+/* 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, rpmsg_rx_cb_t cb,
+               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;
+
+       mutex_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;
+
+       mutex_unlock(&vrp->endpoints_lock);
+
+       return ept;
+
+rem_idr:
+       idr_remove(&vrp->endpoints, request);
+free_ept:
+       mutex_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,
+                               rpmsg_rx_cb_t cb, 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;
+
+       mutex_lock(&vrp->endpoints_lock);
+       idr_remove(&vrp->endpoints, ept->addr);
+       mutex_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);
+               put_device(&rpdev->dev);
+               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);
+
+       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 */
+       mutex_lock(&vrp->tx_lock);
+
+       /*
+        * either pick the next unused tx buffer
+        * (half of our buffers are used for sending messages)
+        */
+       if (vrp->last_sbuf < RPMSG_NUM_BUFS / 2)
+               ret = vrp->sbufs + RPMSG_BUF_SIZE * vrp->last_sbuf++;
+       /* or recycle a used one */
+       else
+               ret = virtqueue_get_buf(vrp->svq, &len);
+
+       mutex_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 */
+       mutex_lock(&vrp->tx_lock);
+
+       /* are we the first sleeping context waiting for tx buffers ? */
+       if (atomic_inc_return(&vrp->sleepers) == 1)
+               /* enable "tx-complete" interrupts before dozing off */
+               virtqueue_enable_cb(vrp->svq);
+
+       mutex_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 */
+       mutex_lock(&vrp->tx_lock);
+
+       /* are we the last sleeping context waiting for tx buffers ? */
+       if (atomic_dec_and_test(&vrp->sleepers))
+               /* disable "tx-complete" interrupts */
+               virtqueue_disable_cb(vrp->svq);
+
+       mutex_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;
+
+       /* bcasting isn't allowed */
+       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;
+       }
+
+       /*
+        * We currently use fixed-sized buffers, and therefore the payload
+        * length is limited.
+        *
+        * 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 > RPMSG_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);
+
+       sg_init_one(&sg, msg, sizeof(*msg) + len);
+
+       mutex_lock(&vrp->tx_lock);
+
+       /* add message to the remote processor's virtqueue */
+       err = virtqueue_add_buf(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 failed: %d\n", err);
+               goto out;
+       }
+
+       /* tell the remote processor it has a pending message to read */
+       virtqueue_kick(vrp->svq);
+
+       err = 0;
+out:
+       mutex_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;
+       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 */
+       mutex_lock(&vrp->endpoints_lock);
+       ept = idr_find(&vrp->endpoints, msg->dst);
+       mutex_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");
+
+       sg_init_one(&sg, msg, sizeof(*msg) + len);
+
+       /* add the buffer back to the remote processor's virtqueue */
+       err = virtqueue_add_buf(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 *bufs_va;
+       int err = 0, i;
+
+       vrp = kzalloc(sizeof(*vrp), GFP_KERNEL);
+       if (!vrp)
+               return -ENOMEM;
+
+       vrp->vdev = vdev;
+
+       idr_init(&vrp->endpoints);
+       mutex_init(&vrp->endpoints_lock);
+       mutex_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_vrp;
+
+       vrp->rvq = vqs[0];
+       vrp->svq = vqs[1];
+
+       /* allocate coherent memory for the buffers */
+       bufs_va = dma_alloc_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE,
+                               &vrp->bufs_dma, GFP_KERNEL);
+       if (!bufs_va)
+               goto vqs_del;
+
+       dev_dbg(&vdev->dev, "buffers: va %p, dma 0x%x\n", bufs_va,
+                                               vrp->bufs_dma);
+
+       /* half of the buffers is dedicated for RX */
+       vrp->rbufs = bufs_va;
+
+       /* and half is dedicated for TX */
+       vrp->sbufs = bufs_va + RPMSG_TOTAL_BUF_SPACE / 2;
+
+       /* set up the receive buffers */
+       for (i = 0; i < RPMSG_NUM_BUFS / 2; i++) {
+               struct scatterlist sg;
+               void *cpu_addr = vrp->rbufs + i * RPMSG_BUF_SIZE;
+
+               sg_init_one(&sg, cpu_addr, RPMSG_BUF_SIZE);
+
+               err = virtqueue_add_buf(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 free_coherent;
+               }
+       }
+
+       /* tell the remote processor it can start sending messages */
+       virtqueue_kick(vrp->rvq);
+
+       dev_info(&vdev->dev, "rpmsg host is online\n");
+
+       return 0;
+
+free_coherent:
+       dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE, bufs_va,
+                                       vrp->bufs_dma);
+vqs_del:
+       vdev->config->del_vqs(vrp->vdev);
+free_vrp:
+       kfree(vrp);
+       return err;
+}
+
+static int rpmsg_remove_device(struct device *dev, void *data)
+{
+       device_unregister(dev);
+
+       return 0;
+}
+
+static void __devexit rpmsg_remove(struct virtio_device *vdev)
+{
+       struct virtproc_info *vrp = vdev->priv;
+       int ret;
+
+       vdev->config->reset(vdev);
+
+       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);
+
+       dma_free_coherent(vdev->dev.parent, RPMSG_TOTAL_BUF_SPACE,
+                                       vrp->rbufs, vrp->bufs_dma);
+
+       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 rpmsg_init(void)
+{
+       int ret;
+
+       ret = bus_register(&rpmsg_bus);
+       if (ret) {
+               pr_err("failed to register rpmsg bus: %d\n", ret);
+               return ret;
+       }
+
+       ret = register_virtio_driver(&virtio_ipc_driver);
+       if (ret) {
+               pr_err("failed to register virtio driver: %d\n", ret);
+               bus_unregister(&rpmsg_bus);
+       }
+
+       return ret;
+}
+module_init(rpmsg_init);
+
+static void __exit rpmsg_fini(void)
+{
+       unregister_virtio_driver(&virtio_ipc_driver);
+       bus_unregister(&rpmsg_bus);
+}
+module_exit(rpmsg_fini);
+
+MODULE_DEVICE_TABLE(virtio, id_table);
+MODULE_DESCRIPTION("Virtio-based remote processor messaging bus");
+MODULE_LICENSE("GPL v2");
index 83ac0713ed0aa9a2a0c79b95013c5bfd42137d9c..88046b622052256b2b9849656fa293eb90cbf0ae 100644 (file)
@@ -414,6 +414,15 @@ struct hv_vmbus_device_id {
                        __attribute__((aligned(sizeof(kernel_ulong_t))));
 };
 
+/* rpmsg */
+
+#define RPMSG_NAME_SIZE                        32
+#define RPMSG_DEVICE_MODALIAS_FMT      "rpmsg:%s"
+
+struct rpmsg_device_id {
+       char name[RPMSG_NAME_SIZE];
+};
+
 /* i2c */
 
 #define I2C_NAME_SIZE  20
diff --git a/include/linux/remoteproc.h b/include/linux/remoteproc.h
new file mode 100644 (file)
index 0000000..ada4cb0
--- /dev/null
@@ -0,0 +1,271 @@
+/*
+ * 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/types.h>
+#include <linux/kref.h>
+#include <linux/klist.h>
+#include <linux/mutex.h>
+#include <linux/virtio.h>
+#include <linux/completion.h>
+
+/*
+ * 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 peers too.
+ */
+#define AMP_VRING_ALIGN        (4096)
+
+/**
+ * struct fw_resource - describes an entry from the resource section
+ * @type: resource type
+ * @id: index number of the resource
+ * @da: device address of the resource
+ * @pa: physical address of the resource
+ * @len: size, in bytes, of the resource
+ * @flags: properties of the resource, e.g. iommu protection required
+ * @reserved: must be 0 atm
+ * @name: name of resource
+ *
+ * The remote processor firmware should contain a "resource table":
+ * array of 'struct fw_resource' entries.
+ *
+ * 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 possibly also reply
+ * by overwriting a member inside 'struct fw_resource' with info about the
+ * allocated resource.
+ *
+ * Different resource entries use different members of this struct,
+ * with different meanings. This is pretty limiting and error-prone,
+ * so the plan is to move to variable-length TLV-based resource entries,
+ * where each resource type will have its own structure.
+ */
+struct fw_resource {
+       u32 type;
+       u32 id;
+       u64 da;
+       u64 pa;
+       u32 len;
+       u32 flags;
+       u8 reserved[16];
+       u8 name[48];
+} __packed;
+
+/**
+ * enum fw_resource_type - types of resource entries
+ *
+ * @RSC_CARVEOUT:   request for allocation of a physically contiguous
+ *                 memory region.
+ * @RSC_DEVMEM:     request to iommu_map a memory-based peripheral.
+ * @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.
+ * @RSC_VRING:     request for allocation of a virtio vring (address should
+ *                 be indicated in 'da', and 'len' should contain the number
+ *                 of buffers supported by the vring).
+ * @RSC_VIRTIO_DEV: this entry declares about support for a virtio device,
+ *                 and serves as the virtio header. 'da' holds the
+ *                 the virtio device features, 'pa' holds the virtio guest
+ *                 features, 'len' holds the virtio status, and 'flags' holds
+ *                 the virtio id (currently only VIRTIO_ID_RPMSG is supported).
+ * @RSC_LAST:       just keep this one at the end
+ *
+ * Most of the resource entries share the basic idea of address/length
+ * negotiation with the host: the firmware usually asks (on behalf of the
+ * remote processor that will soon be booted with it) for memory
+ * of size 'len' bytes, and the host needs to allocate it and provide
+ * the device/physical address (when relevant) in 'da'/'pa' respectively.
+ *
+ * If the firmware is compiled with hard coded device addresses, and
+ * can't handle dynamically allocated 'da' values, then the 'da' field
+ * will contain the expected device addresses (today we actually only support
+ * this scheme, as there aren't yet any use cases for dynamically allocated
+ * device addresses).
+ *
+ * Please note that these values are used as indices to the rproc_handle_rsc
+ * lookup table, so please keep them sane. Moreover, @RSC_LAST is used to
+ * check the validity of an index before the lookup table is accessed, so
+ * please update it as needed.
+ */
+enum fw_resource_type {
+       RSC_CARVEOUT    = 0,
+       RSC_DEVMEM      = 1,
+       RSC_TRACE       = 2,
+       RSC_VRING       = 3,
+       RSC_VIRTIO_DEV  = 4,
+       RSC_LAST        = 5,
+};
+
+/**
+ * struct rproc_mem_entry - memory entry descriptor
+ * @va:        virtual address
+ * @dma: dma address
+ * @len: length, in bytes
+ * @da: device address
+ * @priv: associated data
+ * @node: list node
+ */
+struct rproc_mem_entry {
+       void *va;
+       dma_addr_t dma;
+       int len;
+       u64 da;
+       void *priv;
+       struct list_head node;
+};
+
+struct rproc;
+
+/**
+ * struct rproc_ops - platform-specific device handlers
+ * @start:     power on the device and boot it
+ * @stop:      power off the device
+ * @kick:      kick a virtqueue (virtqueue id given as a parameter)
+ */
+struct rproc_ops {
+       int (*start)(struct rproc *rproc);
+       int (*stop)(struct rproc *rproc);
+       void (*kick)(struct rproc *rproc, int vqid);
+};
+
+/**
+ * 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_CRASHED:     device has crashed; need to start recovery
+ * @RPROC_LAST:                just keep this one at the end
+ *
+ * Please note that the values of these states are used as indices
+ * to rproc_state_string, a state-to-name lookup table,
+ * so please keep the two synchronized. @RPROC_LAST is used to check
+ * the validity of an index before the lookup table is accessed, so
+ * please update it as needed too.
+ */
+enum rproc_state {
+       RPROC_OFFLINE   = 0,
+       RPROC_SUSPENDED = 1,
+       RPROC_RUNNING   = 2,
+       RPROC_CRASHED   = 3,
+       RPROC_LAST      = 4,
+};
+
+/**
+ * struct rproc - represents a physical remote processor device
+ * @node: klist node of this rproc object
+ * @domain: iommu domain
+ * @name: human readable name of the rproc
+ * @firmware: name of firmware file to be loaded
+ * @priv: private data which belongs to the platform-specific rproc module
+ * @ops: platform-specific start/stop rproc handlers
+ * @dev: underlying device
+ * @refcount: refcount of users that have a valid pointer to this rproc
+ * @power: refcount of users who need this rproc powered up
+ * @state: state of the device
+ * @lock: lock which protects concurrent manipulations of the rproc
+ * @dbg_dir: debugfs directory of this rproc device
+ * @traces: list of trace buffers
+ * @num_traces: number of trace buffers
+ * @carveouts: list of physically contiguous memory allocations
+ * @mappings: list of iommu mappings we initiated, needed on shutdown
+ * @firmware_loading_complete: marks e/o asynchronous firmware loading
+ * @bootaddr: address of first instruction to boot rproc with (optional)
+ * @rvdev: virtio device (we only support a single rpmsg virtio device for now)
+ */
+struct rproc {
+       struct klist_node node;
+       struct iommu_domain *domain;
+       const char *name;
+       const char *firmware;
+       void *priv;
+       const struct rproc_ops *ops;
+       struct device *dev;
+       struct kref refcount;
+       atomic_t power;
+       unsigned int state;
+       struct mutex lock;
+       struct dentry *dbg_dir;
+       struct list_head traces;
+       int num_traces;
+       struct list_head carveouts;
+       struct list_head mappings;
+       struct completion firmware_loading_complete;
+       u64 bootaddr;
+       struct rproc_vdev *rvdev;
+};
+
+/**
+ * struct rproc_vdev - remoteproc state for a supported virtio device
+ * @rproc: the rproc handle
+ * @vdev: the virio device
+ * @vq: the virtqueues for this vdev
+ * @vring: the vrings for this vdev
+ * @dfeatures: virtio device features
+ * @gfeatures: virtio guest features
+ */
+struct rproc_vdev {
+       struct rproc *rproc;
+       struct virtio_device vdev;
+       struct virtqueue *vq[2];
+       struct rproc_mem_entry vring[2];
+       unsigned long dfeatures;
+       unsigned long gfeatures;
+};
+
+struct rproc *rproc_get_by_name(const char *name);
+void rproc_put(struct rproc *rproc);
+
+struct rproc *rproc_alloc(struct device *dev, const char *name,
+                               const struct rproc_ops *ops,
+                               const char *firmware, int len);
+void rproc_free(struct rproc *rproc);
+int rproc_register(struct rproc *rproc);
+int rproc_unregister(struct rproc *rproc);
+
+int rproc_boot(struct rproc *rproc);
+void rproc_shutdown(struct rproc *rproc);
+
+static inline struct rproc *vdev_to_rproc(struct virtio_device *vdev)
+{
+       struct rproc_vdev *rvdev = container_of(vdev, struct rproc_vdev, vdev);
+
+       return rvdev->rproc;
+}
+
+#endif /* REMOTEPROC_H */
diff --git a/include/linux/rpmsg.h b/include/linux/rpmsg.h
new file mode 100644 (file)
index 0000000..a8e50e4
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * 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 we receive 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;
+
+/**
+ * enum 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,
+};
+
+#define RPMSG_ADDR_ANY         0xFFFFFFFF
+
+struct virtproc_info;
+
+/**
+ * rpmsg_channel - devices that belong to the rpmsg bus 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;
+};
+
+typedef void (*rpmsg_rx_cb_t)(struct rpmsg_channel *, void *, int, void *, u32);
+
+/**
+ * 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 an rpmsg address with an rx callback handler.
+ *
+ * Simple rpmsg drivers shouldn't use this struct directly, 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 additional endpoints by themselves (see rpmsg_create_ept()).
+ */
+struct rpmsg_endpoint {
+       struct rpmsg_channel *rpdev;
+       rpmsg_rx_cb_t cb;
+       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 *,
+                               rpmsg_rx_cb_t cb, 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 */
index 85bb0bb66ffc5c33b985194975466bb30189657b..b37c5212265ed004ea48b71078809d00a4c7262a 100644 (file)
@@ -34,6 +34,7 @@
 #define VIRTIO_ID_CONSOLE      3 /* virtio console */
 #define VIRTIO_ID_RNG          4 /* virtio ring */
 #define VIRTIO_ID_BALLOON      5 /* virtio balloon */
+#define VIRTIO_ID_RPMSG                7 /* virtio remote processor messaging */
 #define VIRTIO_ID_9P           9 /* 9p virtio console */
 
 #endif /* _LINUX_VIRTIO_IDS_H */
index 41063e7592d2392b231c42cf86b4144d5f7a34b4..7b6792a18c051a94438493c55513f0c47c4d3483 100644 (file)
@@ -61,4 +61,12 @@ config SAMPLE_KDB
          Build an example of how to dynamically add the hello
          command to the kdb shell.
 
+config SAMPLE_RPMSG_CLIENT
+       tristate "Build rpmsg client sample -- loadable modules only"
+       depends on RPMSG && m
+       help
+         Build an rpmsg client sample driver, which demonstrates how
+         to communicate with an AMP-configured remote processor over
+         the rpmsg bus.
+
 endif # SAMPLES
index 6280817c2b7e84594a40650cb19753b50c255840..2f75851ec6294a2a694931336458057856a94094 100644 (file)
@@ -1,4 +1,4 @@
 # Makefile for Linux samples code
 
 obj-$(CONFIG_SAMPLES)  += kobject/ kprobes/ tracepoints/ trace_events/ \
-                          hw_breakpoint/ kfifo/ kdb/ hidraw/
+                          hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/
diff --git a/samples/rpmsg/Makefile b/samples/rpmsg/Makefile
new file mode 100644 (file)
index 0000000..2d4973c
--- /dev/null
@@ -0,0 +1 @@
+obj-$(CONFIG_SAMPLE_RPMSG_CLIENT) += rpmsg_client_sample.o
diff --git a/samples/rpmsg/rpmsg_client_sample.c b/samples/rpmsg/rpmsg_client_sample.c
new file mode 100644 (file)
index 0000000..23ea9f2
--- /dev/null
@@ -0,0 +1,100 @@
+/*
+ * Remote processor messaging - sample client driver
+ *
+ * Copyright (C) 2011 Texas Instruments, Inc.
+ * Copyright (C) 2011 Google, Inc.
+ *
+ * Ohad Ben-Cohen <ohad@wizery.com>
+ * Brian Swetland <swetland@google.com>
+ *
+ * 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.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/rpmsg.h>
+
+#define MSG            "hello world!"
+#define MSG_LIMIT      100
+
+static void rpmsg_sample_cb(struct rpmsg_channel *rpdev, void *data, int len,
+                                               void *priv, u32 src)
+{
+       int ret;
+       static int rx_count;
+
+       dev_info(&rpdev->dev, "incoming msg %d (src: 0x%x)\n", ++rx_count, src);
+
+       print_hex_dump(KERN_DEBUG, __func__, DUMP_PREFIX_NONE, 16, 1,
+                      data, len,  true);
+
+       /* samples should not live forever */
+       if (rx_count >= MSG_LIMIT) {
+               dev_info(&rpdev->dev, "goodbye!\n");
+               return;
+       }
+
+       /* send a new message now */
+       ret = rpmsg_send(rpdev, MSG, strlen(MSG));
+       if (ret)
+               dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
+}
+
+static int rpmsg_sample_probe(struct rpmsg_channel *rpdev)
+{
+       int ret;
+
+       dev_info(&rpdev->dev, "new channel: 0x%x -> 0x%x!\n",
+                                       rpdev->src, rpdev->dst);
+
+       /* send a message to our remote processor */
+       ret = rpmsg_send(rpdev, MSG, strlen(MSG));
+       if (ret) {
+               dev_err(&rpdev->dev, "rpmsg_send failed: %d\n", ret);
+               return ret;
+       }
+
+       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 rpmsg_client_sample_init(void)
+{
+       return register_rpmsg_driver(&rpmsg_sample_client);
+}
+module_init(rpmsg_client_sample_init);
+
+static void __exit rpmsg_client_sample_fini(void)
+{
+       unregister_rpmsg_driver(&rpmsg_sample_client);
+}
+module_exit(rpmsg_client_sample_fini);
+
+MODULE_DESCRIPTION("Remote processor messaging sample client driver");
+MODULE_LICENSE("GPL v2");
This page took 0.235114 seconds and 5 git commands to generate.