xref: /linux/drivers/remoteproc/remoteproc_virtio.c (revision 9c31255ce5fe8ce61d947ba496a6058e22d2375b)
19c92ab61SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
204a9016eSOhad Ben-Cohen /*
304a9016eSOhad Ben-Cohen  * Remote processor messaging transport (OMAP platform-specific bits)
404a9016eSOhad Ben-Cohen  *
504a9016eSOhad Ben-Cohen  * Copyright (C) 2011 Texas Instruments, Inc.
604a9016eSOhad Ben-Cohen  * Copyright (C) 2011 Google, Inc.
704a9016eSOhad Ben-Cohen  *
804a9016eSOhad Ben-Cohen  * Ohad Ben-Cohen <ohad@wizery.com>
904a9016eSOhad Ben-Cohen  * Brian Swetland <swetland@google.com>
1004a9016eSOhad Ben-Cohen  */
1104a9016eSOhad Ben-Cohen 
12*9c31255cSArnaud Pouliquen #include <linux/dma-direct.h>
130a0f0d8bSChristoph Hellwig #include <linux/dma-map-ops.h>
14*9c31255cSArnaud Pouliquen #include <linux/dma-mapping.h>
1504a9016eSOhad Ben-Cohen #include <linux/export.h>
16086d0872SLoic Pallardy #include <linux/of_reserved_mem.h>
1704a9016eSOhad Ben-Cohen #include <linux/remoteproc.h>
1804a9016eSOhad Ben-Cohen #include <linux/virtio.h>
1904a9016eSOhad Ben-Cohen #include <linux/virtio_config.h>
2004a9016eSOhad Ben-Cohen #include <linux/virtio_ids.h>
2104a9016eSOhad Ben-Cohen #include <linux/virtio_ring.h>
2204a9016eSOhad Ben-Cohen #include <linux/err.h>
2304a9016eSOhad Ben-Cohen #include <linux/kref.h>
2404a9016eSOhad Ben-Cohen #include <linux/slab.h>
2504a9016eSOhad Ben-Cohen 
2604a9016eSOhad Ben-Cohen #include "remoteproc_internal.h"
2704a9016eSOhad Ben-Cohen 
28*9c31255cSArnaud Pouliquen static int copy_dma_range_map(struct device *to, struct device *from)
29*9c31255cSArnaud Pouliquen {
30*9c31255cSArnaud Pouliquen 	const struct bus_dma_region *map = from->dma_range_map, *new_map, *r;
31*9c31255cSArnaud Pouliquen 	int num_ranges = 0;
32*9c31255cSArnaud Pouliquen 
33*9c31255cSArnaud Pouliquen 	if (!map)
34*9c31255cSArnaud Pouliquen 		return 0;
35*9c31255cSArnaud Pouliquen 
36*9c31255cSArnaud Pouliquen 	for (r = map; r->size; r++)
37*9c31255cSArnaud Pouliquen 		num_ranges++;
38*9c31255cSArnaud Pouliquen 
39*9c31255cSArnaud Pouliquen 	new_map = kmemdup(map, array_size(num_ranges + 1, sizeof(*map)),
40*9c31255cSArnaud Pouliquen 			  GFP_KERNEL);
41*9c31255cSArnaud Pouliquen 	if (!new_map)
42*9c31255cSArnaud Pouliquen 		return -ENOMEM;
43*9c31255cSArnaud Pouliquen 	to->dma_range_map = new_map;
44*9c31255cSArnaud Pouliquen 	return 0;
45*9c31255cSArnaud Pouliquen }
46*9c31255cSArnaud Pouliquen 
4799555489SArnaud Pouliquen static struct rproc_vdev *vdev_to_rvdev(struct virtio_device *vdev)
4899555489SArnaud Pouliquen {
4999555489SArnaud Pouliquen 	return container_of(vdev->dev.parent, struct rproc_vdev, dev);
5099555489SArnaud Pouliquen }
5199555489SArnaud Pouliquen 
5299555489SArnaud Pouliquen static  struct rproc *vdev_to_rproc(struct virtio_device *vdev)
5399555489SArnaud Pouliquen {
5499555489SArnaud Pouliquen 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
5599555489SArnaud Pouliquen 
5699555489SArnaud Pouliquen 	return rvdev->rproc;
5799555489SArnaud Pouliquen }
5899555489SArnaud Pouliquen 
5904a9016eSOhad Ben-Cohen /* kick the remote processor, and let it know which virtqueue to poke at */
6046f9c2b9SHeinz Graalfs static bool rproc_virtio_notify(struct virtqueue *vq)
6104a9016eSOhad Ben-Cohen {
627a186941SOhad Ben-Cohen 	struct rproc_vring *rvring = vq->priv;
637a186941SOhad Ben-Cohen 	struct rproc *rproc = rvring->rvdev->rproc;
647a186941SOhad Ben-Cohen 	int notifyid = rvring->notifyid;
6504a9016eSOhad Ben-Cohen 
66b5ab5e24SOhad Ben-Cohen 	dev_dbg(&rproc->dev, "kicking vq index: %d\n", notifyid);
6704a9016eSOhad Ben-Cohen 
687a186941SOhad Ben-Cohen 	rproc->ops->kick(rproc, notifyid);
6946f9c2b9SHeinz Graalfs 	return true;
7004a9016eSOhad Ben-Cohen }
7104a9016eSOhad Ben-Cohen 
7204a9016eSOhad Ben-Cohen /**
7304a9016eSOhad Ben-Cohen  * rproc_vq_interrupt() - tell remoteproc that a virtqueue is interrupted
7404a9016eSOhad Ben-Cohen  * @rproc: handle to the remote processor
757a186941SOhad Ben-Cohen  * @notifyid: index of the signalled virtqueue (unique per this @rproc)
7604a9016eSOhad Ben-Cohen  *
7704a9016eSOhad Ben-Cohen  * This function should be called by the platform-specific rproc driver,
7804a9016eSOhad Ben-Cohen  * when the remote processor signals that a specific virtqueue has pending
7904a9016eSOhad Ben-Cohen  * messages available.
8004a9016eSOhad Ben-Cohen  *
81f2867434SSuman Anna  * Return: IRQ_NONE if no message was found in the @notifyid virtqueue,
8204a9016eSOhad Ben-Cohen  * and otherwise returns IRQ_HANDLED.
8304a9016eSOhad Ben-Cohen  */
847a186941SOhad Ben-Cohen irqreturn_t rproc_vq_interrupt(struct rproc *rproc, int notifyid)
8504a9016eSOhad Ben-Cohen {
867a186941SOhad Ben-Cohen 	struct rproc_vring *rvring;
877a186941SOhad Ben-Cohen 
88b5ab5e24SOhad Ben-Cohen 	dev_dbg(&rproc->dev, "vq index %d is interrupted\n", notifyid);
897a186941SOhad Ben-Cohen 
907a186941SOhad Ben-Cohen 	rvring = idr_find(&rproc->notifyids, notifyid);
917a186941SOhad Ben-Cohen 	if (!rvring || !rvring->vq)
927a186941SOhad Ben-Cohen 		return IRQ_NONE;
937a186941SOhad Ben-Cohen 
947a186941SOhad Ben-Cohen 	return vring_interrupt(0, rvring->vq);
9504a9016eSOhad Ben-Cohen }
9604a9016eSOhad Ben-Cohen EXPORT_SYMBOL(rproc_vq_interrupt);
9704a9016eSOhad Ben-Cohen 
9804a9016eSOhad Ben-Cohen static struct virtqueue *rp_find_vq(struct virtio_device *vdev,
99f145928dSAnna, Suman 				    unsigned int id,
10004a9016eSOhad Ben-Cohen 				    void (*callback)(struct virtqueue *vq),
101f94682ddSMichael S. Tsirkin 				    const char *name, bool ctx)
10204a9016eSOhad Ben-Cohen {
1037a186941SOhad Ben-Cohen 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
10404a9016eSOhad Ben-Cohen 	struct rproc *rproc = vdev_to_rproc(vdev);
105b5ab5e24SOhad Ben-Cohen 	struct device *dev = &rproc->dev;
106c6aed238SLoic Pallardy 	struct rproc_mem_entry *mem;
1077a186941SOhad Ben-Cohen 	struct rproc_vring *rvring;
108c6aed238SLoic Pallardy 	struct fw_rsc_vdev *rsc;
10904a9016eSOhad Ben-Cohen 	struct virtqueue *vq;
11004a9016eSOhad Ben-Cohen 	void *addr;
111c2a052a4SXuan Zhuo 	int num, size;
11204a9016eSOhad Ben-Cohen 
1137a186941SOhad Ben-Cohen 	/* we're temporarily limited to two virtqueues per rvdev */
1147a186941SOhad Ben-Cohen 	if (id >= ARRAY_SIZE(rvdev->vring))
1157a186941SOhad Ben-Cohen 		return ERR_PTR(-EINVAL);
11604a9016eSOhad Ben-Cohen 
1176457f126SMichael S. Tsirkin 	if (!name)
1186457f126SMichael S. Tsirkin 		return NULL;
1196457f126SMichael S. Tsirkin 
120c6aed238SLoic Pallardy 	/* Search allocated memory region by name */
121c6aed238SLoic Pallardy 	mem = rproc_find_carveout_by_name(rproc, "vdev%dvring%d", rvdev->index,
122c6aed238SLoic Pallardy 					  id);
123c6aed238SLoic Pallardy 	if (!mem || !mem->va)
124c6aed238SLoic Pallardy 		return ERR_PTR(-ENOMEM);
125c6aed238SLoic Pallardy 
1266db20ea8SOhad Ben-Cohen 	rvring = &rvdev->vring[id];
127c6aed238SLoic Pallardy 	addr = mem->va;
128c2a052a4SXuan Zhuo 	num = rvring->num;
12904a9016eSOhad Ben-Cohen 
1307a186941SOhad Ben-Cohen 	/* zero vring */
131c2a052a4SXuan Zhuo 	size = vring_size(num, rvring->align);
1327a186941SOhad Ben-Cohen 	memset(addr, 0, size);
1337a186941SOhad Ben-Cohen 
134276ec993SLoic Pallardy 	dev_dbg(dev, "vring%d: va %pK qsz %d notifyid %d\n",
135c2a052a4SXuan Zhuo 		id, addr, num, rvring->notifyid);
13604a9016eSOhad Ben-Cohen 
13704a9016eSOhad Ben-Cohen 	/*
13804a9016eSOhad Ben-Cohen 	 * Create the new vq, and tell virtio we're not interested in
13904a9016eSOhad Ben-Cohen 	 * the 'weak' smp barriers, since we're talking with a real device.
14004a9016eSOhad Ben-Cohen 	 */
141c2a052a4SXuan Zhuo 	vq = vring_new_virtqueue(id, num, rvring->align, vdev, false, ctx,
142f94682ddSMichael S. Tsirkin 				 addr, rproc_virtio_notify, callback, name);
14304a9016eSOhad Ben-Cohen 	if (!vq) {
144b5ab5e24SOhad Ben-Cohen 		dev_err(dev, "vring_new_virtqueue %s failed\n", name);
1456db20ea8SOhad Ben-Cohen 		rproc_free_vring(rvring);
1467a186941SOhad Ben-Cohen 		return ERR_PTR(-ENOMEM);
14704a9016eSOhad Ben-Cohen 	}
14804a9016eSOhad Ben-Cohen 
149da802961SXuan Zhuo 	vq->num_max = num;
150da802961SXuan Zhuo 
1517a186941SOhad Ben-Cohen 	rvring->vq = vq;
1527a186941SOhad Ben-Cohen 	vq->priv = rvring;
15304a9016eSOhad Ben-Cohen 
154c6aed238SLoic Pallardy 	/* Update vring in resource table */
155c6aed238SLoic Pallardy 	rsc = (void *)rproc->table_ptr + rvdev->rsc_offset;
156c6aed238SLoic Pallardy 	rsc->vring[id].da = mem->da;
157c6aed238SLoic Pallardy 
15804a9016eSOhad Ben-Cohen 	return vq;
15904a9016eSOhad Ben-Cohen }
16004a9016eSOhad Ben-Cohen 
161dab55bbaSOhad Ben-Cohen static void __rproc_virtio_del_vqs(struct virtio_device *vdev)
16204a9016eSOhad Ben-Cohen {
16304a9016eSOhad Ben-Cohen 	struct virtqueue *vq, *n;
1647a186941SOhad Ben-Cohen 	struct rproc_vring *rvring;
16504a9016eSOhad Ben-Cohen 
16604a9016eSOhad Ben-Cohen 	list_for_each_entry_safe(vq, n, &vdev->vqs, list) {
1677a186941SOhad Ben-Cohen 		rvring = vq->priv;
1687a186941SOhad Ben-Cohen 		rvring->vq = NULL;
16904a9016eSOhad Ben-Cohen 		vring_del_virtqueue(vq);
17004a9016eSOhad Ben-Cohen 	}
17104a9016eSOhad Ben-Cohen }
17204a9016eSOhad Ben-Cohen 
173dab55bbaSOhad Ben-Cohen static void rproc_virtio_del_vqs(struct virtio_device *vdev)
174dab55bbaSOhad Ben-Cohen {
175dab55bbaSOhad Ben-Cohen 	__rproc_virtio_del_vqs(vdev);
176dab55bbaSOhad Ben-Cohen }
177dab55bbaSOhad Ben-Cohen 
178f145928dSAnna, Suman static int rproc_virtio_find_vqs(struct virtio_device *vdev, unsigned int nvqs,
17904a9016eSOhad Ben-Cohen 				 struct virtqueue *vqs[],
18004a9016eSOhad Ben-Cohen 				 vq_callback_t *callbacks[],
181fb5e31d9SChristoph Hellwig 				 const char * const names[],
182f94682ddSMichael S. Tsirkin 				 const bool * ctx,
183fb5e31d9SChristoph Hellwig 				 struct irq_affinity *desc)
18404a9016eSOhad Ben-Cohen {
185a229989dSWei Wang 	int i, ret, queue_idx = 0;
18604a9016eSOhad Ben-Cohen 
18704a9016eSOhad Ben-Cohen 	for (i = 0; i < nvqs; ++i) {
188a229989dSWei Wang 		if (!names[i]) {
189a229989dSWei Wang 			vqs[i] = NULL;
190a229989dSWei Wang 			continue;
191a229989dSWei Wang 		}
192a229989dSWei Wang 
193a229989dSWei Wang 		vqs[i] = rp_find_vq(vdev, queue_idx++, callbacks[i], names[i],
194f94682ddSMichael S. Tsirkin 				    ctx ? ctx[i] : false);
19504a9016eSOhad Ben-Cohen 		if (IS_ERR(vqs[i])) {
19604a9016eSOhad Ben-Cohen 			ret = PTR_ERR(vqs[i]);
19704a9016eSOhad Ben-Cohen 			goto error;
19804a9016eSOhad Ben-Cohen 		}
19904a9016eSOhad Ben-Cohen 	}
20004a9016eSOhad Ben-Cohen 
20104a9016eSOhad Ben-Cohen 	return 0;
20204a9016eSOhad Ben-Cohen 
20304a9016eSOhad Ben-Cohen error:
204dab55bbaSOhad Ben-Cohen 	__rproc_virtio_del_vqs(vdev);
20504a9016eSOhad Ben-Cohen 	return ret;
20604a9016eSOhad Ben-Cohen }
20704a9016eSOhad Ben-Cohen 
20804a9016eSOhad Ben-Cohen static u8 rproc_virtio_get_status(struct virtio_device *vdev)
20904a9016eSOhad Ben-Cohen {
21092b38f85SSjur Brændeland 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
21192b38f85SSjur Brændeland 	struct fw_rsc_vdev *rsc;
21292b38f85SSjur Brændeland 
21392b38f85SSjur Brændeland 	rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
21492b38f85SSjur Brændeland 
21592b38f85SSjur Brændeland 	return rsc->status;
21604a9016eSOhad Ben-Cohen }
21704a9016eSOhad Ben-Cohen 
21804a9016eSOhad Ben-Cohen static void rproc_virtio_set_status(struct virtio_device *vdev, u8 status)
21904a9016eSOhad Ben-Cohen {
22092b38f85SSjur Brændeland 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
22192b38f85SSjur Brændeland 	struct fw_rsc_vdev *rsc;
22292b38f85SSjur Brændeland 
22392b38f85SSjur Brændeland 	rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
22492b38f85SSjur Brændeland 
22592b38f85SSjur Brændeland 	rsc->status = status;
2267a186941SOhad Ben-Cohen 	dev_dbg(&vdev->dev, "status: %d\n", status);
22704a9016eSOhad Ben-Cohen }
22804a9016eSOhad Ben-Cohen 
22904a9016eSOhad Ben-Cohen static void rproc_virtio_reset(struct virtio_device *vdev)
23004a9016eSOhad Ben-Cohen {
23192b38f85SSjur Brændeland 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
23292b38f85SSjur Brændeland 	struct fw_rsc_vdev *rsc;
23392b38f85SSjur Brændeland 
23492b38f85SSjur Brændeland 	rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
23592b38f85SSjur Brændeland 
23692b38f85SSjur Brændeland 	rsc->status = 0;
23704a9016eSOhad Ben-Cohen 	dev_dbg(&vdev->dev, "reset !\n");
23804a9016eSOhad Ben-Cohen }
23904a9016eSOhad Ben-Cohen 
24004a9016eSOhad Ben-Cohen /* provide the vdev features as retrieved from the firmware */
241d0254773SMichael S. Tsirkin static u64 rproc_virtio_get_features(struct virtio_device *vdev)
24204a9016eSOhad Ben-Cohen {
2437a186941SOhad Ben-Cohen 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
24492b38f85SSjur Brændeland 	struct fw_rsc_vdev *rsc;
24504a9016eSOhad Ben-Cohen 
24692b38f85SSjur Brændeland 	rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
24792b38f85SSjur Brændeland 
24892b38f85SSjur Brændeland 	return rsc->dfeatures;
24904a9016eSOhad Ben-Cohen }
25004a9016eSOhad Ben-Cohen 
2513a814fdfSTiwei Bie static void rproc_transport_features(struct virtio_device *vdev)
2523a814fdfSTiwei Bie {
2533a814fdfSTiwei Bie 	/*
2543a814fdfSTiwei Bie 	 * Packed ring isn't enabled on remoteproc for now,
2553a814fdfSTiwei Bie 	 * because remoteproc uses vring_new_virtqueue() which
2563a814fdfSTiwei Bie 	 * creates virtio rings on preallocated memory.
2573a814fdfSTiwei Bie 	 */
2583a814fdfSTiwei Bie 	__virtio_clear_bit(vdev, VIRTIO_F_RING_PACKED);
2593a814fdfSTiwei Bie }
2603a814fdfSTiwei Bie 
2615c609a5eSMichael S. Tsirkin static int rproc_virtio_finalize_features(struct virtio_device *vdev)
26204a9016eSOhad Ben-Cohen {
2637a186941SOhad Ben-Cohen 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
26492b38f85SSjur Brændeland 	struct fw_rsc_vdev *rsc;
26592b38f85SSjur Brændeland 
26692b38f85SSjur Brændeland 	rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
26704a9016eSOhad Ben-Cohen 
26804a9016eSOhad Ben-Cohen 	/* Give virtio_ring a chance to accept features */
26904a9016eSOhad Ben-Cohen 	vring_transport_features(vdev);
27004a9016eSOhad Ben-Cohen 
2713a814fdfSTiwei Bie 	/* Give virtio_rproc a chance to accept features. */
2723a814fdfSTiwei Bie 	rproc_transport_features(vdev);
2733a814fdfSTiwei Bie 
27493d389f8SMichael S. Tsirkin 	/* Make sure we don't have any features > 32 bits! */
27593d389f8SMichael S. Tsirkin 	BUG_ON((u32)vdev->features != vdev->features);
27693d389f8SMichael S. Tsirkin 
27704a9016eSOhad Ben-Cohen 	/*
27804a9016eSOhad Ben-Cohen 	 * Remember the finalized features of our vdev, and provide it
27904a9016eSOhad Ben-Cohen 	 * to the remote processor once it is powered on.
28004a9016eSOhad Ben-Cohen 	 */
281e16e12beSMichael S. Tsirkin 	rsc->gfeatures = vdev->features;
2825c609a5eSMichael S. Tsirkin 
2835c609a5eSMichael S. Tsirkin 	return 0;
28492b38f85SSjur Brændeland }
28592b38f85SSjur Brændeland 
286f145928dSAnna, Suman static void rproc_virtio_get(struct virtio_device *vdev, unsigned int offset,
287f145928dSAnna, Suman 			     void *buf, unsigned int len)
28892b38f85SSjur Brændeland {
28992b38f85SSjur Brændeland 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
29092b38f85SSjur Brændeland 	struct fw_rsc_vdev *rsc;
29192b38f85SSjur Brændeland 	void *cfg;
29292b38f85SSjur Brændeland 
29392b38f85SSjur Brændeland 	rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
29492b38f85SSjur Brændeland 	cfg = &rsc->vring[rsc->num_of_vrings];
29592b38f85SSjur Brændeland 
29692b38f85SSjur Brændeland 	if (offset + len > rsc->config_len || offset + len < len) {
29792b38f85SSjur Brændeland 		dev_err(&vdev->dev, "rproc_virtio_get: access out of bounds\n");
29892b38f85SSjur Brændeland 		return;
29992b38f85SSjur Brændeland 	}
30092b38f85SSjur Brændeland 
30192b38f85SSjur Brændeland 	memcpy(buf, cfg + offset, len);
30292b38f85SSjur Brændeland }
30392b38f85SSjur Brændeland 
304f145928dSAnna, Suman static void rproc_virtio_set(struct virtio_device *vdev, unsigned int offset,
305f145928dSAnna, Suman 			     const void *buf, unsigned int len)
30692b38f85SSjur Brændeland {
30792b38f85SSjur Brændeland 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
30892b38f85SSjur Brændeland 	struct fw_rsc_vdev *rsc;
30992b38f85SSjur Brændeland 	void *cfg;
31092b38f85SSjur Brændeland 
31192b38f85SSjur Brændeland 	rsc = (void *)rvdev->rproc->table_ptr + rvdev->rsc_offset;
31292b38f85SSjur Brændeland 	cfg = &rsc->vring[rsc->num_of_vrings];
31392b38f85SSjur Brændeland 
31492b38f85SSjur Brændeland 	if (offset + len > rsc->config_len || offset + len < len) {
31592b38f85SSjur Brændeland 		dev_err(&vdev->dev, "rproc_virtio_set: access out of bounds\n");
31692b38f85SSjur Brændeland 		return;
31792b38f85SSjur Brændeland 	}
31892b38f85SSjur Brændeland 
31992b38f85SSjur Brændeland 	memcpy(cfg + offset, buf, len);
32004a9016eSOhad Ben-Cohen }
32104a9016eSOhad Ben-Cohen 
32293503932SStephen Hemminger static const struct virtio_config_ops rproc_virtio_config_ops = {
32304a9016eSOhad Ben-Cohen 	.get_features	= rproc_virtio_get_features,
32404a9016eSOhad Ben-Cohen 	.finalize_features = rproc_virtio_finalize_features,
32504a9016eSOhad Ben-Cohen 	.find_vqs	= rproc_virtio_find_vqs,
32604a9016eSOhad Ben-Cohen 	.del_vqs	= rproc_virtio_del_vqs,
32704a9016eSOhad Ben-Cohen 	.reset		= rproc_virtio_reset,
32804a9016eSOhad Ben-Cohen 	.set_status	= rproc_virtio_set_status,
32904a9016eSOhad Ben-Cohen 	.get_status	= rproc_virtio_get_status,
33092b38f85SSjur Brændeland 	.get		= rproc_virtio_get,
33192b38f85SSjur Brændeland 	.set		= rproc_virtio_set,
33204a9016eSOhad Ben-Cohen };
33304a9016eSOhad Ben-Cohen 
33404a9016eSOhad Ben-Cohen /*
33504a9016eSOhad Ben-Cohen  * This function is called whenever vdev is released, and is responsible
3367183a2a7SOhad Ben-Cohen  * to decrement the remote processor's refcount which was taken when vdev was
33704a9016eSOhad Ben-Cohen  * added.
33804a9016eSOhad Ben-Cohen  *
33904a9016eSOhad Ben-Cohen  * Never call this function directly; it will be called by the driver
34004a9016eSOhad Ben-Cohen  * core when needed.
34104a9016eSOhad Ben-Cohen  */
342aab8d802SBjorn Andersson static void rproc_virtio_dev_release(struct device *dev)
34304a9016eSOhad Ben-Cohen {
34404a9016eSOhad Ben-Cohen 	struct virtio_device *vdev = dev_to_virtio(dev);
3456db20ea8SOhad Ben-Cohen 	struct rproc_vdev *rvdev = vdev_to_rvdev(vdev);
34604a9016eSOhad Ben-Cohen 	struct rproc *rproc = vdev_to_rproc(vdev);
34704a9016eSOhad Ben-Cohen 
348d4c036feSLoic Pallardy 	kfree(vdev);
349d4c036feSLoic Pallardy 
350aab8d802SBjorn Andersson 	kref_put(&rvdev->refcount, rproc_vdev_release);
3516db20ea8SOhad Ben-Cohen 
3527183a2a7SOhad Ben-Cohen 	put_device(&rproc->dev);
35304a9016eSOhad Ben-Cohen }
35404a9016eSOhad Ben-Cohen 
35504a9016eSOhad Ben-Cohen /**
3567a186941SOhad Ben-Cohen  * rproc_add_virtio_dev() - register an rproc-induced virtio device
3577a186941SOhad Ben-Cohen  * @rvdev: the remote vdev
3582e7d4c2cSArnaud Pouliquen  * @id: the device type identification (used to match it with a driver).
35904a9016eSOhad Ben-Cohen  *
3607a186941SOhad Ben-Cohen  * This function registers a virtio device. This vdev's partent is
3617a186941SOhad Ben-Cohen  * the rproc device.
36204a9016eSOhad Ben-Cohen  *
363f2867434SSuman Anna  * Return: 0 on success or an appropriate error value otherwise
36404a9016eSOhad Ben-Cohen  */
365*9c31255cSArnaud Pouliquen static int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)
36604a9016eSOhad Ben-Cohen {
3677a186941SOhad Ben-Cohen 	struct rproc *rproc = rvdev->rproc;
368086d0872SLoic Pallardy 	struct device *dev = &rvdev->dev;
369d4c036feSLoic Pallardy 	struct virtio_device *vdev;
370086d0872SLoic Pallardy 	struct rproc_mem_entry *mem;
37104a9016eSOhad Ben-Cohen 	int ret;
37204a9016eSOhad Ben-Cohen 
373791c13b7SNikita Shubin 	if (rproc->ops->kick == NULL) {
374791c13b7SNikita Shubin 		ret = -EINVAL;
3752fb75ceaSChristophe JAILLET 		dev_err(dev, ".kick method not defined for %s\n", rproc->name);
376791c13b7SNikita Shubin 		goto out;
377791c13b7SNikita Shubin 	}
378791c13b7SNikita Shubin 
379086d0872SLoic Pallardy 	/* Try to find dedicated vdev buffer carveout */
380086d0872SLoic Pallardy 	mem = rproc_find_carveout_by_name(rproc, "vdev%dbuffer", rvdev->index);
381086d0872SLoic Pallardy 	if (mem) {
382086d0872SLoic Pallardy 		phys_addr_t pa;
383086d0872SLoic Pallardy 
384086d0872SLoic Pallardy 		if (mem->of_resm_idx != -1) {
385086d0872SLoic Pallardy 			struct device_node *np = rproc->dev.parent->of_node;
386086d0872SLoic Pallardy 
387086d0872SLoic Pallardy 			/* Associate reserved memory to vdev device */
388086d0872SLoic Pallardy 			ret = of_reserved_mem_device_init_by_idx(dev, np,
389086d0872SLoic Pallardy 								 mem->of_resm_idx);
390086d0872SLoic Pallardy 			if (ret) {
391086d0872SLoic Pallardy 				dev_err(dev, "Can't associate reserved memory\n");
392086d0872SLoic Pallardy 				goto out;
393086d0872SLoic Pallardy 			}
394086d0872SLoic Pallardy 		} else {
395086d0872SLoic Pallardy 			if (mem->va) {
396086d0872SLoic Pallardy 				dev_warn(dev, "vdev %d buffer already mapped\n",
397086d0872SLoic Pallardy 					 rvdev->index);
398086d0872SLoic Pallardy 				pa = rproc_va_to_pa(mem->va);
399086d0872SLoic Pallardy 			} else {
400086d0872SLoic Pallardy 				/* Use dma address as carveout no memmapped yet */
401086d0872SLoic Pallardy 				pa = (phys_addr_t)mem->dma;
402086d0872SLoic Pallardy 			}
403086d0872SLoic Pallardy 
404086d0872SLoic Pallardy 			/* Associate vdev buffer memory pool to vdev subdev */
405086d0872SLoic Pallardy 			ret = dma_declare_coherent_memory(dev, pa,
406086d0872SLoic Pallardy 							   mem->da,
407d664ce75SStephen Rothwell 							   mem->len);
408086d0872SLoic Pallardy 			if (ret < 0) {
409086d0872SLoic Pallardy 				dev_err(dev, "Failed to associate buffer\n");
410086d0872SLoic Pallardy 				goto out;
411086d0872SLoic Pallardy 			}
412086d0872SLoic Pallardy 		}
413db9178a4STero Kristo 	} else {
414db9178a4STero Kristo 		struct device_node *np = rproc->dev.parent->of_node;
415db9178a4STero Kristo 
416db9178a4STero Kristo 		/*
417db9178a4STero Kristo 		 * If we don't have dedicated buffer, just attempt to re-assign
418db9178a4STero Kristo 		 * the reserved memory from our parent. A default memory-region
419db9178a4STero Kristo 		 * at index 0 from the parent's memory-regions is assigned for
420db9178a4STero Kristo 		 * the rvdev dev to allocate from. Failure is non-critical and
421db9178a4STero Kristo 		 * the allocations will fall back to global pools, so don't
422db9178a4STero Kristo 		 * check return value either.
423db9178a4STero Kristo 		 */
424db9178a4STero Kristo 		of_reserved_mem_device_init_by_idx(dev, np, 0);
425086d0872SLoic Pallardy 	}
426086d0872SLoic Pallardy 
427d4c036feSLoic Pallardy 	/* Allocate virtio device */
428d4c036feSLoic Pallardy 	vdev = kzalloc(sizeof(*vdev), GFP_KERNEL);
429d4c036feSLoic Pallardy 	if (!vdev) {
430d4c036feSLoic Pallardy 		ret = -ENOMEM;
431d4c036feSLoic Pallardy 		goto out;
432d4c036feSLoic Pallardy 	}
4337a186941SOhad Ben-Cohen 	vdev->id.device	= id,
4347a186941SOhad Ben-Cohen 	vdev->config = &rproc_virtio_config_ops,
4357a186941SOhad Ben-Cohen 	vdev->dev.parent = dev;
436aab8d802SBjorn Andersson 	vdev->dev.release = rproc_virtio_dev_release;
43704a9016eSOhad Ben-Cohen 
43804a9016eSOhad Ben-Cohen 	/*
43904a9016eSOhad Ben-Cohen 	 * We're indirectly making a non-temporary copy of the rproc pointer
44004a9016eSOhad Ben-Cohen 	 * here, because drivers probed with this vdev will indirectly
44104a9016eSOhad Ben-Cohen 	 * access the wrapping rproc.
44204a9016eSOhad Ben-Cohen 	 *
44304a9016eSOhad Ben-Cohen 	 * Therefore we must increment the rproc refcount here, and decrement
44404a9016eSOhad Ben-Cohen 	 * it _only_ when the vdev is released.
44504a9016eSOhad Ben-Cohen 	 */
4467183a2a7SOhad Ben-Cohen 	get_device(&rproc->dev);
44704a9016eSOhad Ben-Cohen 
448aab8d802SBjorn Andersson 	/* Reference the vdev and vring allocations */
449aab8d802SBjorn Andersson 	kref_get(&rvdev->refcount);
450aab8d802SBjorn Andersson 
4517a186941SOhad Ben-Cohen 	ret = register_virtio_device(vdev);
45204a9016eSOhad Ben-Cohen 	if (ret) {
453900a163eSweiping zhang 		put_device(&vdev->dev);
45404a9016eSOhad Ben-Cohen 		dev_err(dev, "failed to register vdev: %d\n", ret);
4557a186941SOhad Ben-Cohen 		goto out;
45604a9016eSOhad Ben-Cohen 	}
45704a9016eSOhad Ben-Cohen 
4587a186941SOhad Ben-Cohen 	dev_info(dev, "registered %s (type %d)\n", dev_name(&vdev->dev), id);
4597a186941SOhad Ben-Cohen 
4607a186941SOhad Ben-Cohen out:
46104a9016eSOhad Ben-Cohen 	return ret;
46204a9016eSOhad Ben-Cohen }
46304a9016eSOhad Ben-Cohen 
46404a9016eSOhad Ben-Cohen /**
4657a186941SOhad Ben-Cohen  * rproc_remove_virtio_dev() - remove an rproc-induced virtio device
466d4c036feSLoic Pallardy  * @dev: the virtio device
467d4c036feSLoic Pallardy  * @data: must be null
46804a9016eSOhad Ben-Cohen  *
4697a186941SOhad Ben-Cohen  * This function unregisters an existing virtio device.
470f2867434SSuman Anna  *
471f2867434SSuman Anna  * Return: 0
47204a9016eSOhad Ben-Cohen  */
473*9c31255cSArnaud Pouliquen static int rproc_remove_virtio_dev(struct device *dev, void *data)
47404a9016eSOhad Ben-Cohen {
475d4c036feSLoic Pallardy 	struct virtio_device *vdev = dev_to_virtio(dev);
476d4c036feSLoic Pallardy 
477d4c036feSLoic Pallardy 	unregister_virtio_device(vdev);
478d4c036feSLoic Pallardy 	return 0;
47904a9016eSOhad Ben-Cohen }
480*9c31255cSArnaud Pouliquen 
481*9c31255cSArnaud Pouliquen static int rproc_vdev_do_start(struct rproc_subdev *subdev)
482*9c31255cSArnaud Pouliquen {
483*9c31255cSArnaud Pouliquen 	struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev);
484*9c31255cSArnaud Pouliquen 
485*9c31255cSArnaud Pouliquen 	return rproc_add_virtio_dev(rvdev, rvdev->id);
486*9c31255cSArnaud Pouliquen }
487*9c31255cSArnaud Pouliquen 
488*9c31255cSArnaud Pouliquen static void rproc_vdev_do_stop(struct rproc_subdev *subdev, bool crashed)
489*9c31255cSArnaud Pouliquen {
490*9c31255cSArnaud Pouliquen 	struct rproc_vdev *rvdev = container_of(subdev, struct rproc_vdev, subdev);
491*9c31255cSArnaud Pouliquen 	int ret;
492*9c31255cSArnaud Pouliquen 
493*9c31255cSArnaud Pouliquen 	ret = device_for_each_child(&rvdev->dev, NULL, rproc_remove_virtio_dev);
494*9c31255cSArnaud Pouliquen 	if (ret)
495*9c31255cSArnaud Pouliquen 		dev_warn(&rvdev->dev, "can't remove vdev child device: %d\n", ret);
496*9c31255cSArnaud Pouliquen }
497*9c31255cSArnaud Pouliquen 
498*9c31255cSArnaud Pouliquen /**
499*9c31255cSArnaud Pouliquen  * rproc_rvdev_release() - release the existence of a rvdev
500*9c31255cSArnaud Pouliquen  *
501*9c31255cSArnaud Pouliquen  * @dev: the subdevice's dev
502*9c31255cSArnaud Pouliquen  */
503*9c31255cSArnaud Pouliquen static void rproc_rvdev_release(struct device *dev)
504*9c31255cSArnaud Pouliquen {
505*9c31255cSArnaud Pouliquen 	struct rproc_vdev *rvdev = container_of(dev, struct rproc_vdev, dev);
506*9c31255cSArnaud Pouliquen 
507*9c31255cSArnaud Pouliquen 	of_reserved_mem_device_release(dev);
508*9c31255cSArnaud Pouliquen 	dma_release_coherent_memory(dev);
509*9c31255cSArnaud Pouliquen 
510*9c31255cSArnaud Pouliquen 	kfree(rvdev);
511*9c31255cSArnaud Pouliquen }
512*9c31255cSArnaud Pouliquen 
513*9c31255cSArnaud Pouliquen struct rproc_vdev *
514*9c31255cSArnaud Pouliquen rproc_rvdev_add_device(struct rproc *rproc, struct rproc_vdev_data *rvdev_data)
515*9c31255cSArnaud Pouliquen {
516*9c31255cSArnaud Pouliquen 	struct rproc_vdev *rvdev;
517*9c31255cSArnaud Pouliquen 	struct fw_rsc_vdev *rsc = rvdev_data->rsc;
518*9c31255cSArnaud Pouliquen 	char name[16];
519*9c31255cSArnaud Pouliquen 	int i, ret;
520*9c31255cSArnaud Pouliquen 
521*9c31255cSArnaud Pouliquen 	rvdev = kzalloc(sizeof(*rvdev), GFP_KERNEL);
522*9c31255cSArnaud Pouliquen 	if (!rvdev)
523*9c31255cSArnaud Pouliquen 		return ERR_PTR(-ENOMEM);
524*9c31255cSArnaud Pouliquen 
525*9c31255cSArnaud Pouliquen 	kref_init(&rvdev->refcount);
526*9c31255cSArnaud Pouliquen 
527*9c31255cSArnaud Pouliquen 	rvdev->id = rvdev_data->id;
528*9c31255cSArnaud Pouliquen 	rvdev->rproc = rproc;
529*9c31255cSArnaud Pouliquen 	rvdev->index = rvdev_data->index;
530*9c31255cSArnaud Pouliquen 
531*9c31255cSArnaud Pouliquen 	/* Initialise vdev subdevice */
532*9c31255cSArnaud Pouliquen 	snprintf(name, sizeof(name), "vdev%dbuffer", rvdev->index);
533*9c31255cSArnaud Pouliquen 	rvdev->dev.parent = &rproc->dev;
534*9c31255cSArnaud Pouliquen 	rvdev->dev.release = rproc_rvdev_release;
535*9c31255cSArnaud Pouliquen 	dev_set_name(&rvdev->dev, "%s#%s", dev_name(rvdev->dev.parent), name);
536*9c31255cSArnaud Pouliquen 	dev_set_drvdata(&rvdev->dev, rvdev);
537*9c31255cSArnaud Pouliquen 
538*9c31255cSArnaud Pouliquen 	ret = device_register(&rvdev->dev);
539*9c31255cSArnaud Pouliquen 	if (ret) {
540*9c31255cSArnaud Pouliquen 		put_device(&rvdev->dev);
541*9c31255cSArnaud Pouliquen 		return ERR_PTR(ret);
542*9c31255cSArnaud Pouliquen 	}
543*9c31255cSArnaud Pouliquen 
544*9c31255cSArnaud Pouliquen 	ret = copy_dma_range_map(&rvdev->dev, rproc->dev.parent);
545*9c31255cSArnaud Pouliquen 	if (ret)
546*9c31255cSArnaud Pouliquen 		goto free_rvdev;
547*9c31255cSArnaud Pouliquen 
548*9c31255cSArnaud Pouliquen 	/* Make device dma capable by inheriting from parent's capabilities */
549*9c31255cSArnaud Pouliquen 	set_dma_ops(&rvdev->dev, get_dma_ops(rproc->dev.parent));
550*9c31255cSArnaud Pouliquen 
551*9c31255cSArnaud Pouliquen 	ret = dma_coerce_mask_and_coherent(&rvdev->dev,
552*9c31255cSArnaud Pouliquen 					   dma_get_mask(rproc->dev.parent));
553*9c31255cSArnaud Pouliquen 	if (ret) {
554*9c31255cSArnaud Pouliquen 		dev_warn(&rvdev->dev,
555*9c31255cSArnaud Pouliquen 			 "Failed to set DMA mask %llx. Trying to continue... (%pe)\n",
556*9c31255cSArnaud Pouliquen 			 dma_get_mask(rproc->dev.parent), ERR_PTR(ret));
557*9c31255cSArnaud Pouliquen 	}
558*9c31255cSArnaud Pouliquen 
559*9c31255cSArnaud Pouliquen 	/* parse the vrings */
560*9c31255cSArnaud Pouliquen 	for (i = 0; i < rsc->num_of_vrings; i++) {
561*9c31255cSArnaud Pouliquen 		ret = rproc_parse_vring(rvdev, rsc, i);
562*9c31255cSArnaud Pouliquen 		if (ret)
563*9c31255cSArnaud Pouliquen 			goto free_rvdev;
564*9c31255cSArnaud Pouliquen 	}
565*9c31255cSArnaud Pouliquen 
566*9c31255cSArnaud Pouliquen 	/* remember the resource offset*/
567*9c31255cSArnaud Pouliquen 	rvdev->rsc_offset = rvdev_data->rsc_offset;
568*9c31255cSArnaud Pouliquen 
569*9c31255cSArnaud Pouliquen 	/* allocate the vring resources */
570*9c31255cSArnaud Pouliquen 	for (i = 0; i < rsc->num_of_vrings; i++) {
571*9c31255cSArnaud Pouliquen 		ret = rproc_alloc_vring(rvdev, i);
572*9c31255cSArnaud Pouliquen 		if (ret)
573*9c31255cSArnaud Pouliquen 			goto unwind_vring_allocations;
574*9c31255cSArnaud Pouliquen 	}
575*9c31255cSArnaud Pouliquen 
576*9c31255cSArnaud Pouliquen 	rproc_add_rvdev(rproc, rvdev);
577*9c31255cSArnaud Pouliquen 
578*9c31255cSArnaud Pouliquen 	rvdev->subdev.start = rproc_vdev_do_start;
579*9c31255cSArnaud Pouliquen 	rvdev->subdev.stop = rproc_vdev_do_stop;
580*9c31255cSArnaud Pouliquen 
581*9c31255cSArnaud Pouliquen 	rproc_add_subdev(rproc, &rvdev->subdev);
582*9c31255cSArnaud Pouliquen 
583*9c31255cSArnaud Pouliquen 	return rvdev;
584*9c31255cSArnaud Pouliquen 
585*9c31255cSArnaud Pouliquen unwind_vring_allocations:
586*9c31255cSArnaud Pouliquen 	for (i--; i >= 0; i--)
587*9c31255cSArnaud Pouliquen 		rproc_free_vring(&rvdev->vring[i]);
588*9c31255cSArnaud Pouliquen free_rvdev:
589*9c31255cSArnaud Pouliquen 	device_unregister(&rvdev->dev);
590*9c31255cSArnaud Pouliquen 	return ERR_PTR(ret);
591*9c31255cSArnaud Pouliquen }
592*9c31255cSArnaud Pouliquen 
593*9c31255cSArnaud Pouliquen void rproc_vdev_release(struct kref *ref)
594*9c31255cSArnaud Pouliquen {
595*9c31255cSArnaud Pouliquen 	struct rproc_vdev *rvdev = container_of(ref, struct rproc_vdev, refcount);
596*9c31255cSArnaud Pouliquen 	struct rproc_vring *rvring;
597*9c31255cSArnaud Pouliquen 	struct rproc *rproc = rvdev->rproc;
598*9c31255cSArnaud Pouliquen 	int id;
599*9c31255cSArnaud Pouliquen 
600*9c31255cSArnaud Pouliquen 	for (id = 0; id < ARRAY_SIZE(rvdev->vring); id++) {
601*9c31255cSArnaud Pouliquen 		rvring = &rvdev->vring[id];
602*9c31255cSArnaud Pouliquen 		rproc_free_vring(rvring);
603*9c31255cSArnaud Pouliquen 	}
604*9c31255cSArnaud Pouliquen 
605*9c31255cSArnaud Pouliquen 	rproc_remove_subdev(rproc, &rvdev->subdev);
606*9c31255cSArnaud Pouliquen 	rproc_remove_rvdev(rvdev);
607*9c31255cSArnaud Pouliquen 	device_unregister(&rvdev->dev);
608*9c31255cSArnaud Pouliquen }
609