aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/remoteproc/remoteproc_rpmsg.c
diff options
context:
space:
mode:
authorOhad Ben-Cohen <ohad@wizery.com>2011-10-20 12:15:13 -0400
committerOhad Ben-Cohen <ohad@wizery.com>2012-02-08 15:53:39 -0500
commitac8954a413930dae3c53f7e782f09a94e7eae88b (patch)
treed8f6e0ef862d959b4211184c05b559e14214c162 /drivers/remoteproc/remoteproc_rpmsg.c
parent6391a70682b173abb8f2895c03c6b21d764e04e5 (diff)
remoteproc: create rpmsg virtio device
Create an rpmsg virtio device to allow message-based communication with the remote processor (but only if supported by its firmware). There are several advantages to provide this functionality at the remoteproc-level: - to support it, platforms only have to provide their own ->kick() handler; no need to duplicate the rest of the code. - the virtio device is created only when the remote processor is registered and ready to go. No need to depend on initcall magic. moreover, we only add the virtio device if the firmware really supports it, and only after we know the supported virtio device features. - correct device model hierarchy can be set, and that is useful for natural power management and DMA API behavior. - when the remote processor crashes (or removed) we only need to remove the virtio device, and the driver core will take care of the rest. No need to implement any out-of-bound notifiers. - we can now easily bind the virtio device to its rproc handle, and this way we don't need any name-based remoteproc ->get() API. Currently we only support creating a single rpmsg virtio device per remote processor, but later this is going to be extended to support creating numerous virtio devices of other types too (block, net, console...). Designed with Brian Swetland <swetland@google.com>. Signed-off-by: Ohad Ben-Cohen <ohad@wizery.com> Cc: Brian Swetland <swetland@google.com> Cc: Arnd Bergmann <arnd@arndb.de> Cc: Grant Likely <grant.likely@secretlab.ca> Cc: Tony Lindgren <tony@atomide.com> Cc: Russell King <linux@arm.linux.org.uk> Cc: Rusty Russell <rusty@rustcorp.com.au> Cc: Andrew Morton <akpm@linux-foundation.org> Cc: Greg KH <greg@kroah.com> Cc: Stephen Boyd <sboyd@codeaurora.org>
Diffstat (limited to 'drivers/remoteproc/remoteproc_rpmsg.c')
-rw-r--r--drivers/remoteproc/remoteproc_rpmsg.c295
1 files changed, 295 insertions, 0 deletions
diff --git a/drivers/remoteproc/remoteproc_rpmsg.c b/drivers/remoteproc/remoteproc_rpmsg.c
new file mode 100644
index 000000000000..f30be4c193ef
--- /dev/null
+++ b/drivers/remoteproc/remoteproc_rpmsg.c
@@ -0,0 +1,295 @@
1/*
2 * Remote processor messaging transport (OMAP platform-specific bits)
3 *
4 * Copyright (C) 2011 Texas Instruments, Inc.
5 * Copyright (C) 2011 Google, Inc.
6 *
7 * Ohad Ben-Cohen <ohad@wizery.com>
8 * Brian Swetland <swetland@google.com>
9 *
10 * This software is licensed under the terms of the GNU General Public
11 * License version 2, as published by the Free Software Foundation, and
12 * may be copied, distributed, and modified under those terms.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 */
19
20#include <linux/export.h>
21#include <linux/remoteproc.h>
22#include <linux/rpmsg.h>
23#include <linux/virtio.h>
24#include <linux/virtio_config.h>
25#include <linux/virtio_ids.h>
26#include <linux/virtio_ring.h>
27#include <linux/err.h>
28#include <linux/kref.h>
29#include <linux/slab.h>
30
31#include "remoteproc_internal.h"
32
33/**
34 * struct rproc_virtio_vq_info - virtqueue state
35 * @vq_id: a unique index of this virtqueue (unique for this @rproc)
36 * @rproc: handle to the remote processor
37 *
38 * Such a struct will be maintained for every virtqueue we're
39 * using to communicate with the remote processor
40 */
41struct rproc_virtio_vq_info {
42 __u16 vq_id;
43 struct rproc *rproc;
44};
45
46/* kick the remote processor, and let it know which virtqueue to poke at */
47static void rproc_virtio_notify(struct virtqueue *vq)
48{
49 struct rproc_virtio_vq_info *rpvq = vq->priv;
50 struct rproc *rproc = rpvq->rproc;
51
52 dev_dbg(rproc->dev, "kicking vq id: %d\n", rpvq->vq_id);
53
54 rproc->ops->kick(rproc, rpvq->vq_id);
55}
56
57/**
58 * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
59 * @rproc: handle to the remote processor
60 * @vq_id: index of the signalled virtqueue
61 *
62 * This function should be called by the platform-specific rproc driver,
63 * when the remote processor signals that a specific virtqueue has pending
64 * messages available.
65 *
66 * Returns IRQ_NONE if no message was found in the @vq_id virtqueue,
67 * and otherwise returns IRQ_HANDLED.
68 */
69irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int vq_id)
70{
71 return vring_interrupt(0, rproc->rvdev->vq[vq_id]);
72}
73EXPORT_SYMBOL(rproc_vq_interrupt);
74
75static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
76 unsigned id,
77 void (*callback)(struct virtqueue *vq),
78 const char *name)
79{
80 struct rproc *rproc = vdev_to_rproc(vdev);
81 struct rproc_vdev *rvdev = rproc->rvdev;
82 struct rproc_virtio_vq_info *rpvq;
83 struct virtqueue *vq;
84 void *addr;
85 int ret, len;
86
87 rpvq = kmalloc(sizeof(*rpvq), GFP_KERNEL);
88 if (!rpvq)
89 return ERR_PTR(-ENOMEM);
90
91 rpvq->rproc = rproc;
92 rpvq->vq_id = id;
93
94 addr = rvdev->vring[id].va;
95 len = rvdev->vring[id].len;
96
97 dev_dbg(rproc->dev, "vring%d: va %p qsz %d\n", id, addr, len);
98
99 vq = vring_new_virtqueue(len, AMP_VRING_ALIGN, vdev, addr,
100 rproc_virtio_notify, callback, name);
101 if (!vq) {
102 dev_err(rproc->dev, "vring_new_virtqueue %s failed\n", name);
103 ret = -ENOMEM;
104 goto free_rpvq;
105 }
106
107 rvdev->vq[id] = vq;
108 vq->priv = rpvq;
109
110 return vq;
111
112free_rpvq:
113 kfree(rpvq);
114 return ERR_PTR(ret);
115}
116
117static void rproc_virtio_del_vqs(struct virtio_device *vdev)
118{
119 struct virtqueue *vq, *n;
120 struct rproc *rproc = vdev_to_rproc(vdev);
121
122 list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
123 struct rproc_virtio_vq_info *rpvq = vq->priv;
124 vring_del_virtqueue(vq);
125 kfree(rpvq);
126 }
127
128 /* power down the remote processor */
129 rproc_shutdown(rproc);
130}
131
132static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned nvqs,
133 struct virtqueue *vqs[],
134 vq_callback_t *callbacks[],
135 const char *names[])
136{
137 struct rproc *rproc = vdev_to_rproc(vdev);
138 int i, ret;
139
140 /* we maintain two virtqueues per remote processor (for RX and TX) */
141 if (nvqs != 2)
142 return -EINVAL;
143
144 /* boot the remote processor */
145 ret = rproc_boot(rproc);
146 if (ret) {
147 dev_err(rproc->dev, "rproc_boot() failed %d\n", ret);
148 goto error;
149 }
150
151 for (i = 0; i < nvqs; ++i) {
152 vqs[i] = rp_find_vq(vdev, i, callbacks[i], names[i]);
153 if (IS_ERR(vqs[i])) {
154 ret = PTR_ERR(vqs[i]);
155 goto error;
156 }
157 }
158
159 return 0;
160
161error:
162 rproc_virtio_del_vqs(vdev);
163 return ret;
164}
165
166/*
167 * We don't support yet real virtio status semantics.
168 *
169 * The plan is to provide this via the VIRTIO HDR resource entry
170 * which is part of the firmware: this way the remote processor
171 * will be able to access the status values as set by us.
172 */
173static u8 rproc_virtio_get_status(struct virtio_device *vdev)
174{
175 return 0;
176}
177
178static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
179{
180 dev_dbg(&vdev->dev, "new status: %d\n", status);
181}
182
183static void rproc_virtio_reset(struct virtio_device *vdev)
184{
185 dev_dbg(&vdev->dev, "reset !\n");
186}
187
188/* provide the vdev features as retrieved from the firmware */
189static u32 rproc_virtio_get_features(struct virtio_device *vdev)
190{
191 struct rproc *rproc = vdev_to_rproc(vdev);
192
193 /* we only support a single vdev device for now */
194 return rproc->rvdev->dfeatures;
195}
196
197static void rproc_virtio_finalize_features(struct virtio_device *vdev)
198{
199 struct rproc *rproc = vdev_to_rproc(vdev);
200
201 /* Give virtio_ring a chance to accept features */
202 vring_transport_features(vdev);
203
204 /*
205 * Remember the finalized features of our vdev, and provide it
206 * to the remote processor once it is powered on.
207 *
208 * Similarly to the status field, we don't expose yet the negotiated
209 * features to the remote processors at this point. This will be
210 * fixed as part of a small resource table overhaul and then an
211 * extension of the virtio resource entries.
212 */
213 rproc->rvdev->gfeatures = vdev->features[0];
214}
215
216static struct virtio_config_ops rproc_virtio_config_ops = {
217 .get_features = rproc_virtio_get_features,
218 .finalize_features = rproc_virtio_finalize_features,
219 .find_vqs = rproc_virtio_find_vqs,
220 .del_vqs = rproc_virtio_del_vqs,
221 .reset = rproc_virtio_reset,
222 .set_status = rproc_virtio_set_status,
223 .get_status = rproc_virtio_get_status,
224};
225
226/*
227 * This function is called whenever vdev is released, and is responsible
228 * to decrement the remote processor's refcount taken when vdev was
229 * added.
230 *
231 * Never call this function directly; it will be called by the driver
232 * core when needed.
233 */
234static void rproc_vdev_release(struct device *dev)
235{
236 struct virtio_device *vdev = dev_to_virtio(dev);
237 struct rproc *rproc = vdev_to_rproc(vdev);
238
239 kref_put(&rproc->refcount, rproc_release);
240}
241
242/**
243 * rproc_add_rpmsg_vdev() - create an rpmsg virtio device
244 * @rproc: the rproc handle
245 *
246 * This function is called if virtio rpmsg support was found in the
247 * firmware of the remote processor.
248 *
249 * Today we only support creating a single rpmsg vdev (virtio device),
250 * but the plan is to remove this limitation. At that point this interface
251 * will be revised/extended.
252 */
253int rproc_add_rpmsg_vdev(struct rproc *rproc)
254{
255 struct device *dev = rproc->dev;
256 struct rproc_vdev *rvdev = rproc->rvdev;
257 int ret;
258
259 rvdev->vdev.id.device = VIRTIO_ID_RPMSG,
260 rvdev->vdev.config = &rproc_virtio_config_ops,
261 rvdev->vdev.dev.parent = dev;
262 rvdev->vdev.dev.release = rproc_vdev_release;
263
264 /*
265 * We're indirectly making a non-temporary copy of the rproc pointer
266 * here, because drivers probed with this vdev will indirectly
267 * access the wrapping rproc.
268 *
269 * Therefore we must increment the rproc refcount here, and decrement
270 * it _only_ when the vdev is released.
271 */
272 kref_get(&rproc->refcount);
273
274 ret = register_virtio_device(&rvdev->vdev);
275 if (ret) {
276 kref_put(&rproc->refcount, rproc_release);
277 dev_err(dev, "failed to register vdev: %d\n", ret);
278 }
279
280 return ret;
281}
282
283/**
284 * rproc_remove_rpmsg_vdev() - remove an rpmsg vdev device
285 * @rproc: the rproc handle
286 *
287 * This function is called whenever @rproc is removed _iff_ an rpmsg
288 * vdev was created beforehand.
289 */
290void rproc_remove_rpmsg_vdev(struct rproc *rproc)
291{
292 struct rproc_vdev *rvdev = rproc->rvdev;
293
294 unregister_virtio_device(&rvdev->vdev);
295}