Blame view
drivers/remoteproc/remoteproc_virtio.c
11.3 KB
9c92ab619 treewide: Replace... |
1 |
// SPDX-License-Identifier: GPL-2.0-only |
ac8954a41 remoteproc: creat... |
2 3 4 5 6 7 8 9 |
/* * 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> |
ac8954a41 remoteproc: creat... |
10 |
*/ |
0a0f0d8be dma-mapping: spli... |
11 |
#include <linux/dma-map-ops.h> |
ac8954a41 remoteproc: creat... |
12 |
#include <linux/export.h> |
086d08725 remoteproc: creat... |
13 |
#include <linux/of_reserved_mem.h> |
ac8954a41 remoteproc: creat... |
14 |
#include <linux/remoteproc.h> |
ac8954a41 remoteproc: creat... |
15 16 17 18 19 20 21 22 23 |
#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" |
ac8954a41 remoteproc: creat... |
24 |
/* kick the remote processor, and let it know which virtqueue to poke at */ |
46f9c2b92 virtio_ring: chan... |
25 |
static bool rproc_virtio_notify(struct virtqueue *vq) |
ac8954a41 remoteproc: creat... |
26 |
{ |
7a1869416 remoteproc: remov... |
27 28 29 |
struct rproc_vring *rvring = vq->priv; struct rproc *rproc = rvring->rvdev->rproc; int notifyid = rvring->notifyid; |
ac8954a41 remoteproc: creat... |
30 |
|
b5ab5e24e remoteproc: maint... |
31 32 |
dev_dbg(&rproc->dev, "kicking vq index: %d ", notifyid); |
ac8954a41 remoteproc: creat... |
33 |
|
7a1869416 remoteproc: remov... |
34 |
rproc->ops->kick(rproc, notifyid); |
46f9c2b92 virtio_ring: chan... |
35 |
return true; |
ac8954a41 remoteproc: creat... |
36 37 38 39 40 |
} /** * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted * @rproc: handle to the remote processor |
7a1869416 remoteproc: remov... |
41 |
* @notifyid: index of the signalled virtqueue (unique per this @rproc) |
ac8954a41 remoteproc: creat... |
42 43 44 45 46 |
* * This function should be called by the platform-specific rproc driver, * when the remote processor signals that a specific virtqueue has pending * messages available. * |
7a1869416 remoteproc: remov... |
47 |
* Returns IRQ_NONE if no message was found in the @notifyid virtqueue, |
ac8954a41 remoteproc: creat... |
48 49 |
* and otherwise returns IRQ_HANDLED. */ |
7a1869416 remoteproc: remov... |
50 |
irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid) |
ac8954a41 remoteproc: creat... |
51 |
{ |
7a1869416 remoteproc: remov... |
52 |
struct rproc_vring *rvring; |
b5ab5e24e remoteproc: maint... |
53 54 |
dev_dbg(&rproc->dev, "vq index %d is interrupted ", notifyid); |
7a1869416 remoteproc: remov... |
55 56 57 58 59 60 |
rvring = idr_find(&rproc->notifyids, notifyid); if (!rvring || !rvring->vq) return IRQ_NONE; return vring_interrupt(0, rvring->vq); |
ac8954a41 remoteproc: creat... |
61 62 63 64 |
} EXPORT_SYMBOL(rproc_vq_interrupt); static struct virtqueue *rp_find_vq(struct virtio_device *vdev, |
f145928d4 remoteproc: fix b... |
65 |
unsigned int id, |
ac8954a41 remoteproc: creat... |
66 |
void (*callback)(struct virtqueue *vq), |
f94682dde virtio: add conte... |
67 |
const char *name, bool ctx) |
ac8954a41 remoteproc: creat... |
68 |
{ |
7a1869416 remoteproc: remov... |
69 |
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); |
ac8954a41 remoteproc: creat... |
70 |
struct rproc *rproc = vdev_to_rproc(vdev); |
b5ab5e24e remoteproc: maint... |
71 |
struct device *dev = &rproc->dev; |
c6aed238b remoteproc: modif... |
72 |
struct rproc_mem_entry *mem; |
7a1869416 remoteproc: remov... |
73 |
struct rproc_vring *rvring; |
c6aed238b remoteproc: modif... |
74 |
struct fw_rsc_vdev *rsc; |
ac8954a41 remoteproc: creat... |
75 76 |
struct virtqueue *vq; void *addr; |
a863af5d4 remoteproc: virti... |
77 |
int len, size; |
ac8954a41 remoteproc: creat... |
78 |
|
7a1869416 remoteproc: remov... |
79 80 81 |
/* we're temporarily limited to two virtqueues per rvdev */ if (id >= ARRAY_SIZE(rvdev->vring)) return ERR_PTR(-EINVAL); |
6457f126c virtio: support r... |
82 83 |
if (!name) return NULL; |
c6aed238b remoteproc: modif... |
84 85 86 87 88 |
/* Search allocated memory region by name */ mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index, id); if (!mem || !mem->va) return ERR_PTR(-ENOMEM); |
6db20ea8d remoteproc: alloc... |
89 |
rvring = &rvdev->vring[id]; |
c6aed238b remoteproc: modif... |
90 |
addr = mem->va; |
7a1869416 remoteproc: remov... |
91 |
len = rvring->len; |
ac8954a41 remoteproc: creat... |
92 |
|
7a1869416 remoteproc: remov... |
93 94 95 |
/* zero vring */ size = vring_size(len, rvring->align); memset(addr, 0, size); |
ac8954a41 remoteproc: creat... |
96 |
|
276ec9934 remoteproc: repla... |
97 98 |
dev_dbg(dev, "vring%d: va %pK qsz %d notifyid %d ", |
730f84ce6 remoteproc: align... |
99 |
id, addr, len, rvring->notifyid); |
ac8954a41 remoteproc: creat... |
100 |
|
dd6da1c5e remoteproc: don't... |
101 102 103 104 |
/* * Create the new vq, and tell virtio we're not interested in * the 'weak' smp barriers, since we're talking with a real device. */ |
f94682dde virtio: add conte... |
105 106 |
vq = vring_new_virtqueue(id, len, rvring->align, vdev, false, ctx, addr, rproc_virtio_notify, callback, name); |
ac8954a41 remoteproc: creat... |
107 |
if (!vq) { |
b5ab5e24e remoteproc: maint... |
108 109 |
dev_err(dev, "vring_new_virtqueue %s failed ", name); |
6db20ea8d remoteproc: alloc... |
110 |
rproc_free_vring(rvring); |
7a1869416 remoteproc: remov... |
111 |
return ERR_PTR(-ENOMEM); |
ac8954a41 remoteproc: creat... |
112 |
} |
7a1869416 remoteproc: remov... |
113 114 |
rvring->vq = vq; vq->priv = rvring; |
ac8954a41 remoteproc: creat... |
115 |
|
c6aed238b remoteproc: modif... |
116 117 118 |
/* Update vring in resource table */ rsc = (void *)rproc->table_ptr + rvdev->rsc_offset; rsc->vring[id].da = mem->da; |
ac8954a41 remoteproc: creat... |
119 |
return vq; |
ac8954a41 remoteproc: creat... |
120 |
} |
dab55bbaf remoteproc: fix e... |
121 |
static void __rproc_virtio_del_vqs(struct virtio_device *vdev) |
ac8954a41 remoteproc: creat... |
122 123 |
{ struct virtqueue *vq, *n; |
7a1869416 remoteproc: remov... |
124 |
struct rproc_vring *rvring; |
ac8954a41 remoteproc: creat... |
125 126 |
list_for_each_entry_safe(vq, n, &vdev->vqs, list) { |
7a1869416 remoteproc: remov... |
127 128 |
rvring = vq->priv; rvring->vq = NULL; |
ac8954a41 remoteproc: creat... |
129 |
vring_del_virtqueue(vq); |
ac8954a41 remoteproc: creat... |
130 |
} |
ac8954a41 remoteproc: creat... |
131 |
} |
dab55bbaf remoteproc: fix e... |
132 133 |
static void rproc_virtio_del_vqs(struct virtio_device *vdev) { |
dab55bbaf remoteproc: fix e... |
134 135 |
__rproc_virtio_del_vqs(vdev); } |
f145928d4 remoteproc: fix b... |
136 |
static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs, |
730f84ce6 remoteproc: align... |
137 138 |
struct virtqueue *vqs[], vq_callback_t *callbacks[], |
fb5e31d97 virtio: allow dri... |
139 |
const char * const names[], |
f94682dde virtio: add conte... |
140 |
const bool * ctx, |
fb5e31d97 virtio: allow dri... |
141 |
struct irq_affinity *desc) |
ac8954a41 remoteproc: creat... |
142 |
{ |
a229989d9 virtio: don't all... |
143 |
int i, ret, queue_idx = 0; |
ac8954a41 remoteproc: creat... |
144 |
|
ac8954a41 remoteproc: creat... |
145 |
for (i = 0; i < nvqs; ++i) { |
a229989d9 virtio: don't all... |
146 147 148 149 150 151 |
if (!names[i]) { vqs[i] = NULL; continue; } vqs[i] = rp_find_vq(vdev, queue_idx++, callbacks[i], names[i], |
f94682dde virtio: add conte... |
152 |
ctx ? ctx[i] : false); |
ac8954a41 remoteproc: creat... |
153 154 155 156 157 158 159 160 161 |
if (IS_ERR(vqs[i])) { ret = PTR_ERR(vqs[i]); goto error; } } return 0; error: |
dab55bbaf remoteproc: fix e... |
162 |
__rproc_virtio_del_vqs(vdev); |
ac8954a41 remoteproc: creat... |
163 164 |
return ret; } |
ac8954a41 remoteproc: creat... |
165 166 |
static u8 rproc_virtio_get_status(struct virtio_device *vdev) { |
92b38f851 remoteproc: suppo... |
167 168 169 170 171 172 |
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct fw_rsc_vdev *rsc; rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; return rsc->status; |
ac8954a41 remoteproc: creat... |
173 174 175 176 |
} static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status) { |
92b38f851 remoteproc: suppo... |
177 178 179 180 181 182 |
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct fw_rsc_vdev *rsc; rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; rsc->status = status; |
7a1869416 remoteproc: remov... |
183 184 |
dev_dbg(&vdev->dev, "status: %d ", status); |
ac8954a41 remoteproc: creat... |
185 186 187 188 |
} static void rproc_virtio_reset(struct virtio_device *vdev) { |
92b38f851 remoteproc: suppo... |
189 190 191 192 193 194 |
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct fw_rsc_vdev *rsc; rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; rsc->status = 0; |
ac8954a41 remoteproc: creat... |
195 196 197 198 199 |
dev_dbg(&vdev->dev, "reset ! "); } /* provide the vdev features as retrieved from the firmware */ |
d02547736 virtio: add suppo... |
200 |
static u64 rproc_virtio_get_features(struct virtio_device *vdev) |
ac8954a41 remoteproc: creat... |
201 |
{ |
7a1869416 remoteproc: remov... |
202 |
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); |
92b38f851 remoteproc: suppo... |
203 204 205 |
struct fw_rsc_vdev *rsc; rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; |
ac8954a41 remoteproc: creat... |
206 |
|
92b38f851 remoteproc: suppo... |
207 |
return rsc->dfeatures; |
ac8954a41 remoteproc: creat... |
208 |
} |
3a814fdf2 virtio_ring: disa... |
209 210 211 212 213 214 215 216 217 |
static void rproc_transport_features(struct virtio_device *vdev) { /* * Packed ring isn't enabled on remoteproc for now, * because remoteproc uses vring_new_virtqueue() which * creates virtio rings on preallocated memory. */ __virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED); } |
5c609a5ef virtio: allow fin... |
218 |
static int rproc_virtio_finalize_features(struct virtio_device *vdev) |
ac8954a41 remoteproc: creat... |
219 |
{ |
7a1869416 remoteproc: remov... |
220 |
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); |
92b38f851 remoteproc: suppo... |
221 222 223 |
struct fw_rsc_vdev *rsc; rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; |
ac8954a41 remoteproc: creat... |
224 225 226 |
/* Give virtio_ring a chance to accept features */ vring_transport_features(vdev); |
3a814fdf2 virtio_ring: disa... |
227 228 |
/* Give virtio_rproc a chance to accept features. */ rproc_transport_features(vdev); |
93d389f82 virtio: assert 32... |
229 230 |
/* Make sure we don't have any features > 32 bits! */ BUG_ON((u32)vdev->features != vdev->features); |
ac8954a41 remoteproc: creat... |
231 232 233 |
/* * Remember the finalized features of our vdev, and provide it * to the remote processor once it is powered on. |
ac8954a41 remoteproc: creat... |
234 |
*/ |
e16e12be3 virtio: use u32, ... |
235 |
rsc->gfeatures = vdev->features; |
5c609a5ef virtio: allow fin... |
236 237 |
return 0; |
92b38f851 remoteproc: suppo... |
238 |
} |
f145928d4 remoteproc: fix b... |
239 240 |
static void rproc_virtio_get(struct virtio_device *vdev, unsigned int offset, void *buf, unsigned int len) |
92b38f851 remoteproc: suppo... |
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 |
{ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct fw_rsc_vdev *rsc; void *cfg; rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; cfg = &rsc->vring[rsc->num_of_vrings]; if (offset + len > rsc->config_len || offset + len < len) { dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds "); return; } memcpy(buf, cfg + offset, len); } |
f145928d4 remoteproc: fix b... |
257 258 |
static void rproc_virtio_set(struct virtio_device *vdev, unsigned int offset, const void *buf, unsigned int len) |
92b38f851 remoteproc: suppo... |
259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 |
{ struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); struct fw_rsc_vdev *rsc; void *cfg; rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset; cfg = &rsc->vring[rsc->num_of_vrings]; if (offset + len > rsc->config_len || offset + len < len) { dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds "); return; } memcpy(cfg + offset, buf, len); |
ac8954a41 remoteproc: creat... |
274 |
} |
935039323 virtio: make conf... |
275 |
static const struct virtio_config_ops rproc_virtio_config_ops = { |
ac8954a41 remoteproc: creat... |
276 277 278 279 280 281 282 |
.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, |
92b38f851 remoteproc: suppo... |
283 284 |
.get = rproc_virtio_get, .set = rproc_virtio_set, |
ac8954a41 remoteproc: creat... |
285 286 287 288 |
}; /* * This function is called whenever vdev is released, and is responsible |
7183a2a79 remoteproc: remov... |
289 |
* to decrement the remote processor's refcount which was taken when vdev was |
ac8954a41 remoteproc: creat... |
290 291 292 293 294 |
* added. * * Never call this function directly; it will be called by the driver * core when needed. */ |
aab8d8022 remoteproc: Assig... |
295 |
static void rproc_virtio_dev_release(struct device *dev) |
ac8954a41 remoteproc: creat... |
296 297 |
{ struct virtio_device *vdev = dev_to_virtio(dev); |
6db20ea8d remoteproc: alloc... |
298 |
struct rproc_vdev *rvdev = vdev_to_rvdev(vdev); |
ac8954a41 remoteproc: creat... |
299 |
struct rproc *rproc = vdev_to_rproc(vdev); |
d4c036fec remoteproc: fix r... |
300 |
kfree(vdev); |
aab8d8022 remoteproc: Assig... |
301 |
kref_put(&rvdev->refcount, rproc_vdev_release); |
6db20ea8d remoteproc: alloc... |
302 |
|
7183a2a79 remoteproc: remov... |
303 |
put_device(&rproc->dev); |
ac8954a41 remoteproc: creat... |
304 305 306 |
} /** |
7a1869416 remoteproc: remov... |
307 308 |
* rproc_add_virtio_dev() - register an rproc-induced virtio device * @rvdev: the remote vdev |
2e7d4c2c4 remoteproc: fix k... |
309 |
* @id: the device type identification (used to match it with a driver). |
ac8954a41 remoteproc: creat... |
310 |
* |
7a1869416 remoteproc: remov... |
311 312 |
* This function registers a virtio device. This vdev's partent is * the rproc device. |
ac8954a41 remoteproc: creat... |
313 |
* |
7a1869416 remoteproc: remov... |
314 |
* Returns 0 on success or an appropriate error value otherwise. |
ac8954a41 remoteproc: creat... |
315 |
*/ |
7a1869416 remoteproc: remov... |
316 |
int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id) |
ac8954a41 remoteproc: creat... |
317 |
{ |
7a1869416 remoteproc: remov... |
318 |
struct rproc *rproc = rvdev->rproc; |
086d08725 remoteproc: creat... |
319 |
struct device *dev = &rvdev->dev; |
d4c036fec remoteproc: fix r... |
320 |
struct virtio_device *vdev; |
086d08725 remoteproc: creat... |
321 |
struct rproc_mem_entry *mem; |
ac8954a41 remoteproc: creat... |
322 |
int ret; |
791c13b70 remoteproc: Fix N... |
323 324 |
if (rproc->ops->kick == NULL) { ret = -EINVAL; |
2fb75ceaf remoteproc: Add m... |
325 326 |
dev_err(dev, ".kick method not defined for %s ", rproc->name); |
791c13b70 remoteproc: Fix N... |
327 328 |
goto out; } |
086d08725 remoteproc: creat... |
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 |
/* Try to find dedicated vdev buffer carveout */ mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index); if (mem) { phys_addr_t pa; if (mem->of_resm_idx != -1) { struct device_node *np = rproc->dev.parent->of_node; /* Associate reserved memory to vdev device */ ret = of_reserved_mem_device_init_by_idx(dev, np, mem->of_resm_idx); if (ret) { dev_err(dev, "Can't associate reserved memory "); goto out; } } else { if (mem->va) { dev_warn(dev, "vdev %d buffer already mapped ", rvdev->index); pa = rproc_va_to_pa(mem->va); } else { /* Use dma address as carveout no memmapped yet */ pa = (phys_addr_t)mem->dma; } /* Associate vdev buffer memory pool to vdev subdev */ ret = dma_declare_coherent_memory(dev, pa, mem->da, |
d664ce75a remoteproc: fix f... |
359 |
mem->len); |
086d08725 remoteproc: creat... |
360 361 362 363 364 365 |
if (ret < 0) { dev_err(dev, "Failed to associate buffer "); goto out; } } |
db9178a4f remoteproc: Fall ... |
366 367 368 369 370 371 372 373 374 375 376 377 |
} else { struct device_node *np = rproc->dev.parent->of_node; /* * If we don't have dedicated buffer, just attempt to re-assign * the reserved memory from our parent. A default memory-region * at index 0 from the parent's memory-regions is assigned for * the rvdev dev to allocate from. Failure is non-critical and * the allocations will fall back to global pools, so don't * check return value either. */ of_reserved_mem_device_init_by_idx(dev, np, 0); |
086d08725 remoteproc: creat... |
378 |
} |
d4c036fec remoteproc: fix r... |
379 380 381 382 383 384 |
/* Allocate virtio device */ vdev = kzalloc(sizeof(*vdev), GFP_KERNEL); if (!vdev) { ret = -ENOMEM; goto out; } |
7a1869416 remoteproc: remov... |
385 386 387 |
vdev->id.device = id, vdev->config = &rproc_virtio_config_ops, vdev->dev.parent = dev; |
aab8d8022 remoteproc: Assig... |
388 |
vdev->dev.release = rproc_virtio_dev_release; |
ac8954a41 remoteproc: creat... |
389 390 391 392 393 394 395 396 397 |
/* * 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. */ |
7183a2a79 remoteproc: remov... |
398 |
get_device(&rproc->dev); |
ac8954a41 remoteproc: creat... |
399 |
|
aab8d8022 remoteproc: Assig... |
400 401 |
/* Reference the vdev and vring allocations */ kref_get(&rvdev->refcount); |
7a1869416 remoteproc: remov... |
402 |
ret = register_virtio_device(vdev); |
ac8954a41 remoteproc: creat... |
403 |
if (ret) { |
900a163ed virtio_remoteproc... |
404 |
put_device(&vdev->dev); |
ac8954a41 remoteproc: creat... |
405 406 |
dev_err(dev, "failed to register vdev: %d ", ret); |
7a1869416 remoteproc: remov... |
407 |
goto out; |
ac8954a41 remoteproc: creat... |
408 |
} |
7a1869416 remoteproc: remov... |
409 410 411 412 |
dev_info(dev, "registered %s (type %d) ", dev_name(&vdev->dev), id); out: |
ac8954a41 remoteproc: creat... |
413 414 415 416 |
return ret; } /** |
7a1869416 remoteproc: remov... |
417 |
* rproc_remove_virtio_dev() - remove an rproc-induced virtio device |
d4c036fec remoteproc: fix r... |
418 419 |
* @dev: the virtio device * @data: must be null |
ac8954a41 remoteproc: creat... |
420 |
* |
7a1869416 remoteproc: remov... |
421 |
* This function unregisters an existing virtio device. |
ac8954a41 remoteproc: creat... |
422 |
*/ |
d4c036fec remoteproc: fix r... |
423 |
int rproc_remove_virtio_dev(struct device *dev, void *data) |
ac8954a41 remoteproc: creat... |
424 |
{ |
d4c036fec remoteproc: fix r... |
425 426 427 428 |
struct virtio_device *vdev = dev_to_virtio(dev); unregister_virtio_device(vdev); return 0; |
ac8954a41 remoteproc: creat... |
429 |
} |