Merge tag 'rpmsg-for-3.4' of git://git.kernel.org/pub/scm/linux/kernel/git/ohad/remoteproc into next/rpmsg
* 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
This commit is contained in:
commit
ab646a24bb
75
Documentation/ABI/testing/sysfs-bus-rpmsg
Normal file
75
Documentation/ABI/testing/sysfs-bus-rpmsg
Normal file
@ -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).
|
324
Documentation/remoteproc.txt
Normal file
324
Documentation/remoteproc.txt
Normal file
@ -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).
|
293
Documentation/rpmsg.txt
Normal file
293
Documentation/rpmsg.txt
Normal file
@ -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.
|
@ -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
|
||||
|
57
arch/arm/plat-omap/include/plat/remoteproc.h
Normal file
57
arch/arm/plat-omap/include/plat/remoteproc.h
Normal file
@ -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 */
|
@ -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"
|
||||
|
@ -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/
|
||||
|
29
drivers/remoteproc/Kconfig
Normal file
29
drivers/remoteproc/Kconfig
Normal file
@ -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
|
9
drivers/remoteproc/Makefile
Normal file
9
drivers/remoteproc/Makefile
Normal file
@ -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
|
238
drivers/remoteproc/omap_remoteproc.c
Normal file
238
drivers/remoteproc/omap_remoteproc.c
Normal file
@ -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");
|
69
drivers/remoteproc/omap_remoteproc.h
Normal file
69
drivers/remoteproc/omap_remoteproc.h
Normal file
@ -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 */
|
1450
drivers/remoteproc/remoteproc_core.c
Normal file
1450
drivers/remoteproc/remoteproc_core.c
Normal file
File diff suppressed because it is too large
Load Diff
179
drivers/remoteproc/remoteproc_debugfs.c
Normal file
179
drivers/remoteproc/remoteproc_debugfs.c
Normal file
@ -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);
|
||||
}
|
44
drivers/remoteproc/remoteproc_internal.h
Normal file
44
drivers/remoteproc/remoteproc_internal.h
Normal file
@ -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 */
|
299
drivers/remoteproc/remoteproc_rpmsg.c
Normal file
299
drivers/remoteproc/remoteproc_rpmsg.c
Normal file
@ -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);
|
||||
}
|
10
drivers/rpmsg/Kconfig
Normal file
10
drivers/rpmsg/Kconfig
Normal file
@ -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
|
1
drivers/rpmsg/Makefile
Normal file
1
drivers/rpmsg/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-$(CONFIG_RPMSG) += virtio_rpmsg_bus.o
|
1026
drivers/rpmsg/virtio_rpmsg_bus.c
Normal file
1026
drivers/rpmsg/virtio_rpmsg_bus.c
Normal file
File diff suppressed because it is too large
Load Diff
@ -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
|
||||
|
271
include/linux/remoteproc.h
Normal file
271
include/linux/remoteproc.h
Normal file
@ -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 */
|
326
include/linux/rpmsg.h
Normal file
326
include/linux/rpmsg.h
Normal file
@ -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 */
|
@ -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 */
|
||||
|
@ -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
|
||||
|
@ -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/
|
||||
|
1
samples/rpmsg/Makefile
Normal file
1
samples/rpmsg/Makefile
Normal file
@ -0,0 +1 @@
|
||||
obj-$(CONFIG_SAMPLE_RPMSG_CLIENT) += rpmsg_client_sample.o
|
100
samples/rpmsg/rpmsg_client_sample.c
Normal file
100
samples/rpmsg/rpmsg_client_sample.c
Normal file
@ -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");
|
Loading…
Reference in New Issue
Block a user