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: drivers/base/regmap/
|
||||||
F: include/linux/regmap.h
|
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
|
RFKILL
|
||||||
M: Johannes Berg <johannes@sipsolutions.net>
|
M: Johannes Berg <johannes@sipsolutions.net>
|
||||||
L: linux-wireless@vger.kernel.org
|
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/iommu/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/remoteproc/Kconfig"
|
||||||
|
|
||||||
|
source "drivers/rpmsg/Kconfig"
|
||||||
|
|
||||||
source "drivers/virt/Kconfig"
|
source "drivers/virt/Kconfig"
|
||||||
|
|
||||||
source "drivers/devfreq/Kconfig"
|
source "drivers/devfreq/Kconfig"
|
||||||
|
@ -126,6 +126,8 @@ obj-y += clk/
|
|||||||
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
|
obj-$(CONFIG_HWSPINLOCK) += hwspinlock/
|
||||||
obj-$(CONFIG_NFC) += nfc/
|
obj-$(CONFIG_NFC) += nfc/
|
||||||
obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
|
obj-$(CONFIG_IOMMU_SUPPORT) += iommu/
|
||||||
|
obj-$(CONFIG_REMOTEPROC) += remoteproc/
|
||||||
|
obj-$(CONFIG_RPMSG) += rpmsg/
|
||||||
|
|
||||||
# Virtualization drivers
|
# Virtualization drivers
|
||||||
obj-$(CONFIG_VIRT_DRIVERS) += virt/
|
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))));
|
__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 */
|
/* i2c */
|
||||||
|
|
||||||
#define I2C_NAME_SIZE 20
|
#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_CONSOLE 3 /* virtio console */
|
||||||
#define VIRTIO_ID_RNG 4 /* virtio ring */
|
#define VIRTIO_ID_RNG 4 /* virtio ring */
|
||||||
#define VIRTIO_ID_BALLOON 5 /* virtio balloon */
|
#define VIRTIO_ID_BALLOON 5 /* virtio balloon */
|
||||||
|
#define VIRTIO_ID_RPMSG 7 /* virtio remote processor messaging */
|
||||||
#define VIRTIO_ID_9P 9 /* 9p virtio console */
|
#define VIRTIO_ID_9P 9 /* 9p virtio console */
|
||||||
|
|
||||||
#endif /* _LINUX_VIRTIO_IDS_H */
|
#endif /* _LINUX_VIRTIO_IDS_H */
|
||||||
|
@ -61,4 +61,12 @@ config SAMPLE_KDB
|
|||||||
Build an example of how to dynamically add the hello
|
Build an example of how to dynamically add the hello
|
||||||
command to the kdb shell.
|
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
|
endif # SAMPLES
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# Makefile for Linux samples code
|
# Makefile for Linux samples code
|
||||||
|
|
||||||
obj-$(CONFIG_SAMPLES) += kobject/ kprobes/ tracepoints/ trace_events/ \
|
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