xref: /openbmc/u-boot/drivers/virtio/virtio_mmio.c (revision 1d6edcbfed2af33c748f2beb399810a0441888da)
1*fdc4aca8SBin Meng // SPDX-License-Identifier: GPL-2.0+
2*fdc4aca8SBin Meng /*
3*fdc4aca8SBin Meng  * Copyright (C) 2018, Tuomas Tynkkynen <tuomas.tynkkynen@iki.fi>
4*fdc4aca8SBin Meng  * Copyright (C) 2018, Bin Meng <bmeng.cn@gmail.com>
5*fdc4aca8SBin Meng  *
6*fdc4aca8SBin Meng  * VirtIO memory-maped I/O transport driver
7*fdc4aca8SBin Meng  * Ported from Linux drivers/virtio/virtio_mmio.c
8*fdc4aca8SBin Meng  */
9*fdc4aca8SBin Meng 
10*fdc4aca8SBin Meng #include <common.h>
11*fdc4aca8SBin Meng #include <dm.h>
12*fdc4aca8SBin Meng #include <virtio_types.h>
13*fdc4aca8SBin Meng #include <virtio.h>
14*fdc4aca8SBin Meng #include <virtio_ring.h>
15*fdc4aca8SBin Meng #include <linux/compat.h>
16*fdc4aca8SBin Meng #include <linux/io.h>
17*fdc4aca8SBin Meng #include "virtio_mmio.h"
18*fdc4aca8SBin Meng 
virtio_mmio_get_config(struct udevice * udev,unsigned int offset,void * buf,unsigned int len)19*fdc4aca8SBin Meng static int virtio_mmio_get_config(struct udevice *udev, unsigned int offset,
20*fdc4aca8SBin Meng 				  void *buf, unsigned int len)
21*fdc4aca8SBin Meng {
22*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
23*fdc4aca8SBin Meng 	void __iomem *base = priv->base + VIRTIO_MMIO_CONFIG;
24*fdc4aca8SBin Meng 	u8 b;
25*fdc4aca8SBin Meng 	__le16 w;
26*fdc4aca8SBin Meng 	__le32 l;
27*fdc4aca8SBin Meng 
28*fdc4aca8SBin Meng 	if (priv->version == 1) {
29*fdc4aca8SBin Meng 		u8 *ptr = buf;
30*fdc4aca8SBin Meng 		int i;
31*fdc4aca8SBin Meng 
32*fdc4aca8SBin Meng 		for (i = 0; i < len; i++)
33*fdc4aca8SBin Meng 			ptr[i] = readb(base + offset + i);
34*fdc4aca8SBin Meng 
35*fdc4aca8SBin Meng 		return 0;
36*fdc4aca8SBin Meng 	}
37*fdc4aca8SBin Meng 
38*fdc4aca8SBin Meng 	switch (len) {
39*fdc4aca8SBin Meng 	case 1:
40*fdc4aca8SBin Meng 		b = readb(base + offset);
41*fdc4aca8SBin Meng 		memcpy(buf, &b, sizeof(b));
42*fdc4aca8SBin Meng 		break;
43*fdc4aca8SBin Meng 	case 2:
44*fdc4aca8SBin Meng 		w = cpu_to_le16(readw(base + offset));
45*fdc4aca8SBin Meng 		memcpy(buf, &w, sizeof(w));
46*fdc4aca8SBin Meng 		break;
47*fdc4aca8SBin Meng 	case 4:
48*fdc4aca8SBin Meng 		l = cpu_to_le32(readl(base + offset));
49*fdc4aca8SBin Meng 		memcpy(buf, &l, sizeof(l));
50*fdc4aca8SBin Meng 		break;
51*fdc4aca8SBin Meng 	case 8:
52*fdc4aca8SBin Meng 		l = cpu_to_le32(readl(base + offset));
53*fdc4aca8SBin Meng 		memcpy(buf, &l, sizeof(l));
54*fdc4aca8SBin Meng 		l = cpu_to_le32(readl(base + offset + sizeof(l)));
55*fdc4aca8SBin Meng 		memcpy(buf + sizeof(l), &l, sizeof(l));
56*fdc4aca8SBin Meng 		break;
57*fdc4aca8SBin Meng 	default:
58*fdc4aca8SBin Meng 		WARN_ON(true);
59*fdc4aca8SBin Meng 	}
60*fdc4aca8SBin Meng 
61*fdc4aca8SBin Meng 	return 0;
62*fdc4aca8SBin Meng }
63*fdc4aca8SBin Meng 
virtio_mmio_set_config(struct udevice * udev,unsigned int offset,const void * buf,unsigned int len)64*fdc4aca8SBin Meng static int virtio_mmio_set_config(struct udevice *udev, unsigned int offset,
65*fdc4aca8SBin Meng 				  const void *buf, unsigned int len)
66*fdc4aca8SBin Meng {
67*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
68*fdc4aca8SBin Meng 	void __iomem *base = priv->base + VIRTIO_MMIO_CONFIG;
69*fdc4aca8SBin Meng 	u8 b;
70*fdc4aca8SBin Meng 	__le16 w;
71*fdc4aca8SBin Meng 	__le32 l;
72*fdc4aca8SBin Meng 
73*fdc4aca8SBin Meng 	if (priv->version == 1) {
74*fdc4aca8SBin Meng 		const u8 *ptr = buf;
75*fdc4aca8SBin Meng 		int i;
76*fdc4aca8SBin Meng 
77*fdc4aca8SBin Meng 		for (i = 0; i < len; i++)
78*fdc4aca8SBin Meng 			writeb(ptr[i], base + offset + i);
79*fdc4aca8SBin Meng 
80*fdc4aca8SBin Meng 		return 0;
81*fdc4aca8SBin Meng 	}
82*fdc4aca8SBin Meng 
83*fdc4aca8SBin Meng 	switch (len) {
84*fdc4aca8SBin Meng 	case 1:
85*fdc4aca8SBin Meng 		memcpy(&b, buf, sizeof(b));
86*fdc4aca8SBin Meng 		writeb(b, base + offset);
87*fdc4aca8SBin Meng 		break;
88*fdc4aca8SBin Meng 	case 2:
89*fdc4aca8SBin Meng 		memcpy(&w, buf, sizeof(w));
90*fdc4aca8SBin Meng 		writew(le16_to_cpu(w), base + offset);
91*fdc4aca8SBin Meng 		break;
92*fdc4aca8SBin Meng 	case 4:
93*fdc4aca8SBin Meng 		memcpy(&l, buf, sizeof(l));
94*fdc4aca8SBin Meng 		writel(le32_to_cpu(l), base + offset);
95*fdc4aca8SBin Meng 		break;
96*fdc4aca8SBin Meng 	case 8:
97*fdc4aca8SBin Meng 		memcpy(&l, buf, sizeof(l));
98*fdc4aca8SBin Meng 		writel(le32_to_cpu(l), base + offset);
99*fdc4aca8SBin Meng 		memcpy(&l, buf + sizeof(l), sizeof(l));
100*fdc4aca8SBin Meng 		writel(le32_to_cpu(l), base + offset + sizeof(l));
101*fdc4aca8SBin Meng 		break;
102*fdc4aca8SBin Meng 	default:
103*fdc4aca8SBin Meng 		WARN_ON(true);
104*fdc4aca8SBin Meng 	}
105*fdc4aca8SBin Meng 
106*fdc4aca8SBin Meng 	return 0;
107*fdc4aca8SBin Meng }
108*fdc4aca8SBin Meng 
virtio_mmio_generation(struct udevice * udev,u32 * counter)109*fdc4aca8SBin Meng static int virtio_mmio_generation(struct udevice *udev, u32 *counter)
110*fdc4aca8SBin Meng {
111*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
112*fdc4aca8SBin Meng 
113*fdc4aca8SBin Meng 	if (priv->version == 1)
114*fdc4aca8SBin Meng 		*counter = 0;
115*fdc4aca8SBin Meng 	else
116*fdc4aca8SBin Meng 		*counter = readl(priv->base + VIRTIO_MMIO_CONFIG_GENERATION);
117*fdc4aca8SBin Meng 
118*fdc4aca8SBin Meng 	return 0;
119*fdc4aca8SBin Meng }
120*fdc4aca8SBin Meng 
virtio_mmio_get_status(struct udevice * udev,u8 * status)121*fdc4aca8SBin Meng static int virtio_mmio_get_status(struct udevice *udev, u8 *status)
122*fdc4aca8SBin Meng {
123*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
124*fdc4aca8SBin Meng 
125*fdc4aca8SBin Meng 	*status = readl(priv->base + VIRTIO_MMIO_STATUS) & 0xff;
126*fdc4aca8SBin Meng 
127*fdc4aca8SBin Meng 	return 0;
128*fdc4aca8SBin Meng }
129*fdc4aca8SBin Meng 
virtio_mmio_set_status(struct udevice * udev,u8 status)130*fdc4aca8SBin Meng static int virtio_mmio_set_status(struct udevice *udev, u8 status)
131*fdc4aca8SBin Meng {
132*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
133*fdc4aca8SBin Meng 
134*fdc4aca8SBin Meng 	/* We should never be setting status to 0 */
135*fdc4aca8SBin Meng 	WARN_ON(status == 0);
136*fdc4aca8SBin Meng 
137*fdc4aca8SBin Meng 	writel(status, priv->base + VIRTIO_MMIO_STATUS);
138*fdc4aca8SBin Meng 
139*fdc4aca8SBin Meng 	return 0;
140*fdc4aca8SBin Meng }
141*fdc4aca8SBin Meng 
virtio_mmio_reset(struct udevice * udev)142*fdc4aca8SBin Meng static int virtio_mmio_reset(struct udevice *udev)
143*fdc4aca8SBin Meng {
144*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
145*fdc4aca8SBin Meng 
146*fdc4aca8SBin Meng 	/* 0 status means a reset */
147*fdc4aca8SBin Meng 	writel(0, priv->base + VIRTIO_MMIO_STATUS);
148*fdc4aca8SBin Meng 
149*fdc4aca8SBin Meng 	return 0;
150*fdc4aca8SBin Meng }
151*fdc4aca8SBin Meng 
virtio_mmio_get_features(struct udevice * udev,u64 * features)152*fdc4aca8SBin Meng static int virtio_mmio_get_features(struct udevice *udev, u64 *features)
153*fdc4aca8SBin Meng {
154*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
155*fdc4aca8SBin Meng 
156*fdc4aca8SBin Meng 	writel(1, priv->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
157*fdc4aca8SBin Meng 	*features = readl(priv->base + VIRTIO_MMIO_DEVICE_FEATURES);
158*fdc4aca8SBin Meng 	*features <<= 32;
159*fdc4aca8SBin Meng 
160*fdc4aca8SBin Meng 	writel(0, priv->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL);
161*fdc4aca8SBin Meng 	*features |= readl(priv->base + VIRTIO_MMIO_DEVICE_FEATURES);
162*fdc4aca8SBin Meng 
163*fdc4aca8SBin Meng 	return 0;
164*fdc4aca8SBin Meng }
165*fdc4aca8SBin Meng 
virtio_mmio_set_features(struct udevice * udev)166*fdc4aca8SBin Meng static int virtio_mmio_set_features(struct udevice *udev)
167*fdc4aca8SBin Meng {
168*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
169*fdc4aca8SBin Meng 	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
170*fdc4aca8SBin Meng 
171*fdc4aca8SBin Meng 	/* Make sure there is are no mixed devices */
172*fdc4aca8SBin Meng 	if (priv->version == 2 && uc_priv->legacy) {
173*fdc4aca8SBin Meng 		debug("New virtio-mmio devices (version 2) must provide VIRTIO_F_VERSION_1 feature!\n");
174*fdc4aca8SBin Meng 		return -EINVAL;
175*fdc4aca8SBin Meng 	}
176*fdc4aca8SBin Meng 
177*fdc4aca8SBin Meng 	writel(1, priv->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL);
178*fdc4aca8SBin Meng 	writel((u32)(uc_priv->features >> 32),
179*fdc4aca8SBin Meng 	       priv->base + VIRTIO_MMIO_DRIVER_FEATURES);
180*fdc4aca8SBin Meng 
181*fdc4aca8SBin Meng 	writel(0, priv->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL);
182*fdc4aca8SBin Meng 	writel((u32)uc_priv->features,
183*fdc4aca8SBin Meng 	       priv->base + VIRTIO_MMIO_DRIVER_FEATURES);
184*fdc4aca8SBin Meng 
185*fdc4aca8SBin Meng 	return 0;
186*fdc4aca8SBin Meng }
187*fdc4aca8SBin Meng 
virtio_mmio_setup_vq(struct udevice * udev,unsigned int index)188*fdc4aca8SBin Meng static struct virtqueue *virtio_mmio_setup_vq(struct udevice *udev,
189*fdc4aca8SBin Meng 					      unsigned int index)
190*fdc4aca8SBin Meng {
191*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
192*fdc4aca8SBin Meng 	struct virtqueue *vq;
193*fdc4aca8SBin Meng 	unsigned int num;
194*fdc4aca8SBin Meng 	int err;
195*fdc4aca8SBin Meng 
196*fdc4aca8SBin Meng 	/* Select the queue we're interested in */
197*fdc4aca8SBin Meng 	writel(index, priv->base + VIRTIO_MMIO_QUEUE_SEL);
198*fdc4aca8SBin Meng 
199*fdc4aca8SBin Meng 	/* Queue shouldn't already be set up */
200*fdc4aca8SBin Meng 	if (readl(priv->base + (priv->version == 1 ?
201*fdc4aca8SBin Meng 	    VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY))) {
202*fdc4aca8SBin Meng 		err = -ENOENT;
203*fdc4aca8SBin Meng 		goto error_available;
204*fdc4aca8SBin Meng 	}
205*fdc4aca8SBin Meng 
206*fdc4aca8SBin Meng 	num = readl(priv->base + VIRTIO_MMIO_QUEUE_NUM_MAX);
207*fdc4aca8SBin Meng 	if (num == 0) {
208*fdc4aca8SBin Meng 		err = -ENOENT;
209*fdc4aca8SBin Meng 		goto error_new_virtqueue;
210*fdc4aca8SBin Meng 	}
211*fdc4aca8SBin Meng 
212*fdc4aca8SBin Meng 	/* Create the vring */
213*fdc4aca8SBin Meng 	vq = vring_create_virtqueue(index, num, VIRTIO_MMIO_VRING_ALIGN, udev);
214*fdc4aca8SBin Meng 	if (!vq) {
215*fdc4aca8SBin Meng 		err = -ENOMEM;
216*fdc4aca8SBin Meng 		goto error_new_virtqueue;
217*fdc4aca8SBin Meng 	}
218*fdc4aca8SBin Meng 
219*fdc4aca8SBin Meng 	/* Activate the queue */
220*fdc4aca8SBin Meng 	writel(virtqueue_get_vring_size(vq),
221*fdc4aca8SBin Meng 	       priv->base + VIRTIO_MMIO_QUEUE_NUM);
222*fdc4aca8SBin Meng 	if (priv->version == 1) {
223*fdc4aca8SBin Meng 		u64 q_pfn = virtqueue_get_desc_addr(vq) >> PAGE_SHIFT;
224*fdc4aca8SBin Meng 
225*fdc4aca8SBin Meng 		/*
226*fdc4aca8SBin Meng 		 * virtio-mmio v1 uses a 32bit QUEUE PFN. If we have something
227*fdc4aca8SBin Meng 		 * that doesn't fit in 32bit, fail the setup rather than
228*fdc4aca8SBin Meng 		 * pretending to be successful.
229*fdc4aca8SBin Meng 		 */
230*fdc4aca8SBin Meng 		if (q_pfn >> 32) {
231*fdc4aca8SBin Meng 			debug("platform bug: legacy virtio-mmio must not be used with RAM above 0x%llxGB\n",
232*fdc4aca8SBin Meng 			      0x1ULL << (32 + PAGE_SHIFT - 30));
233*fdc4aca8SBin Meng 			err = -E2BIG;
234*fdc4aca8SBin Meng 			goto error_bad_pfn;
235*fdc4aca8SBin Meng 		}
236*fdc4aca8SBin Meng 
237*fdc4aca8SBin Meng 		writel(PAGE_SIZE, priv->base + VIRTIO_MMIO_QUEUE_ALIGN);
238*fdc4aca8SBin Meng 		writel(q_pfn, priv->base + VIRTIO_MMIO_QUEUE_PFN);
239*fdc4aca8SBin Meng 	} else {
240*fdc4aca8SBin Meng 		u64 addr;
241*fdc4aca8SBin Meng 
242*fdc4aca8SBin Meng 		addr = virtqueue_get_desc_addr(vq);
243*fdc4aca8SBin Meng 		writel((u32)addr, priv->base + VIRTIO_MMIO_QUEUE_DESC_LOW);
244*fdc4aca8SBin Meng 		writel((u32)(addr >> 32),
245*fdc4aca8SBin Meng 		       priv->base + VIRTIO_MMIO_QUEUE_DESC_HIGH);
246*fdc4aca8SBin Meng 
247*fdc4aca8SBin Meng 		addr = virtqueue_get_avail_addr(vq);
248*fdc4aca8SBin Meng 		writel((u32)addr, priv->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW);
249*fdc4aca8SBin Meng 		writel((u32)(addr >> 32),
250*fdc4aca8SBin Meng 		       priv->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH);
251*fdc4aca8SBin Meng 
252*fdc4aca8SBin Meng 		addr = virtqueue_get_used_addr(vq);
253*fdc4aca8SBin Meng 		writel((u32)addr, priv->base + VIRTIO_MMIO_QUEUE_USED_LOW);
254*fdc4aca8SBin Meng 		writel((u32)(addr >> 32),
255*fdc4aca8SBin Meng 		       priv->base + VIRTIO_MMIO_QUEUE_USED_HIGH);
256*fdc4aca8SBin Meng 
257*fdc4aca8SBin Meng 		writel(1, priv->base + VIRTIO_MMIO_QUEUE_READY);
258*fdc4aca8SBin Meng 	}
259*fdc4aca8SBin Meng 
260*fdc4aca8SBin Meng 	return vq;
261*fdc4aca8SBin Meng 
262*fdc4aca8SBin Meng error_bad_pfn:
263*fdc4aca8SBin Meng 	vring_del_virtqueue(vq);
264*fdc4aca8SBin Meng 
265*fdc4aca8SBin Meng error_new_virtqueue:
266*fdc4aca8SBin Meng 	if (priv->version == 1) {
267*fdc4aca8SBin Meng 		writel(0, priv->base + VIRTIO_MMIO_QUEUE_PFN);
268*fdc4aca8SBin Meng 	} else {
269*fdc4aca8SBin Meng 		writel(0, priv->base + VIRTIO_MMIO_QUEUE_READY);
270*fdc4aca8SBin Meng 		WARN_ON(readl(priv->base + VIRTIO_MMIO_QUEUE_READY));
271*fdc4aca8SBin Meng 	}
272*fdc4aca8SBin Meng 
273*fdc4aca8SBin Meng error_available:
274*fdc4aca8SBin Meng 	return ERR_PTR(err);
275*fdc4aca8SBin Meng }
276*fdc4aca8SBin Meng 
virtio_mmio_del_vq(struct virtqueue * vq)277*fdc4aca8SBin Meng static void virtio_mmio_del_vq(struct virtqueue *vq)
278*fdc4aca8SBin Meng {
279*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(vq->vdev);
280*fdc4aca8SBin Meng 	unsigned int index = vq->index;
281*fdc4aca8SBin Meng 
282*fdc4aca8SBin Meng 	/* Select and deactivate the queue */
283*fdc4aca8SBin Meng 	writel(index, priv->base + VIRTIO_MMIO_QUEUE_SEL);
284*fdc4aca8SBin Meng 	if (priv->version == 1) {
285*fdc4aca8SBin Meng 		writel(0, priv->base + VIRTIO_MMIO_QUEUE_PFN);
286*fdc4aca8SBin Meng 	} else {
287*fdc4aca8SBin Meng 		writel(0, priv->base + VIRTIO_MMIO_QUEUE_READY);
288*fdc4aca8SBin Meng 		WARN_ON(readl(priv->base + VIRTIO_MMIO_QUEUE_READY));
289*fdc4aca8SBin Meng 	}
290*fdc4aca8SBin Meng 
291*fdc4aca8SBin Meng 	vring_del_virtqueue(vq);
292*fdc4aca8SBin Meng }
293*fdc4aca8SBin Meng 
virtio_mmio_del_vqs(struct udevice * udev)294*fdc4aca8SBin Meng static int virtio_mmio_del_vqs(struct udevice *udev)
295*fdc4aca8SBin Meng {
296*fdc4aca8SBin Meng 	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
297*fdc4aca8SBin Meng 	struct virtqueue *vq, *n;
298*fdc4aca8SBin Meng 
299*fdc4aca8SBin Meng 	list_for_each_entry_safe(vq, n, &uc_priv->vqs, list)
300*fdc4aca8SBin Meng 		virtio_mmio_del_vq(vq);
301*fdc4aca8SBin Meng 
302*fdc4aca8SBin Meng 	return 0;
303*fdc4aca8SBin Meng }
304*fdc4aca8SBin Meng 
virtio_mmio_find_vqs(struct udevice * udev,unsigned int nvqs,struct virtqueue * vqs[])305*fdc4aca8SBin Meng static int virtio_mmio_find_vqs(struct udevice *udev, unsigned int nvqs,
306*fdc4aca8SBin Meng 				struct virtqueue *vqs[])
307*fdc4aca8SBin Meng {
308*fdc4aca8SBin Meng 	int i;
309*fdc4aca8SBin Meng 
310*fdc4aca8SBin Meng 	for (i = 0; i < nvqs; ++i) {
311*fdc4aca8SBin Meng 		vqs[i] = virtio_mmio_setup_vq(udev, i);
312*fdc4aca8SBin Meng 		if (IS_ERR(vqs[i])) {
313*fdc4aca8SBin Meng 			virtio_mmio_del_vqs(udev);
314*fdc4aca8SBin Meng 			return PTR_ERR(vqs[i]);
315*fdc4aca8SBin Meng 		}
316*fdc4aca8SBin Meng 	}
317*fdc4aca8SBin Meng 
318*fdc4aca8SBin Meng 	return 0;
319*fdc4aca8SBin Meng }
320*fdc4aca8SBin Meng 
virtio_mmio_notify(struct udevice * udev,struct virtqueue * vq)321*fdc4aca8SBin Meng static int virtio_mmio_notify(struct udevice *udev, struct virtqueue *vq)
322*fdc4aca8SBin Meng {
323*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
324*fdc4aca8SBin Meng 
325*fdc4aca8SBin Meng 	/*
326*fdc4aca8SBin Meng 	 * We write the queue's selector into the notification register
327*fdc4aca8SBin Meng 	 * to signal the other end
328*fdc4aca8SBin Meng 	 */
329*fdc4aca8SBin Meng 	writel(vq->index, priv->base + VIRTIO_MMIO_QUEUE_NOTIFY);
330*fdc4aca8SBin Meng 
331*fdc4aca8SBin Meng 	return 0;
332*fdc4aca8SBin Meng }
333*fdc4aca8SBin Meng 
virtio_mmio_ofdata_to_platdata(struct udevice * udev)334*fdc4aca8SBin Meng static int virtio_mmio_ofdata_to_platdata(struct udevice *udev)
335*fdc4aca8SBin Meng {
336*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
337*fdc4aca8SBin Meng 
338*fdc4aca8SBin Meng 	priv->base = (void __iomem *)(ulong)dev_read_addr(udev);
339*fdc4aca8SBin Meng 	if (priv->base == (void __iomem *)FDT_ADDR_T_NONE)
340*fdc4aca8SBin Meng 		return -EINVAL;
341*fdc4aca8SBin Meng 
342*fdc4aca8SBin Meng 	return 0;
343*fdc4aca8SBin Meng }
344*fdc4aca8SBin Meng 
virtio_mmio_probe(struct udevice * udev)345*fdc4aca8SBin Meng static int virtio_mmio_probe(struct udevice *udev)
346*fdc4aca8SBin Meng {
347*fdc4aca8SBin Meng 	struct virtio_mmio_priv *priv = dev_get_priv(udev);
348*fdc4aca8SBin Meng 	struct virtio_dev_priv *uc_priv = dev_get_uclass_priv(udev);
349*fdc4aca8SBin Meng 	u32 magic;
350*fdc4aca8SBin Meng 
351*fdc4aca8SBin Meng 	/* Check magic value */
352*fdc4aca8SBin Meng 	magic = readl(priv->base + VIRTIO_MMIO_MAGIC_VALUE);
353*fdc4aca8SBin Meng 	if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) {
354*fdc4aca8SBin Meng 		debug("(%s): wrong magic value 0x%08x!\n", udev->name, magic);
355*fdc4aca8SBin Meng 		return 0;
356*fdc4aca8SBin Meng 	}
357*fdc4aca8SBin Meng 
358*fdc4aca8SBin Meng 	/* Check device version */
359*fdc4aca8SBin Meng 	priv->version = readl(priv->base + VIRTIO_MMIO_VERSION);
360*fdc4aca8SBin Meng 	if (priv->version < 1 || priv->version > 2) {
361*fdc4aca8SBin Meng 		debug("(%s): version %d not supported!\n",
362*fdc4aca8SBin Meng 		      udev->name, priv->version);
363*fdc4aca8SBin Meng 		return 0;
364*fdc4aca8SBin Meng 	}
365*fdc4aca8SBin Meng 
366*fdc4aca8SBin Meng 	/* Check devicd ID */
367*fdc4aca8SBin Meng 	uc_priv->device = readl(priv->base + VIRTIO_MMIO_DEVICE_ID);
368*fdc4aca8SBin Meng 	if (uc_priv->device == 0) {
369*fdc4aca8SBin Meng 		/*
370*fdc4aca8SBin Meng 		 * virtio-mmio device with an ID 0 is a (dummy) placeholder
371*fdc4aca8SBin Meng 		 * with no function. End probing now with no error reported.
372*fdc4aca8SBin Meng 		 */
373*fdc4aca8SBin Meng 		return 0;
374*fdc4aca8SBin Meng 	}
375*fdc4aca8SBin Meng 	uc_priv->vendor = readl(priv->base + VIRTIO_MMIO_VENDOR_ID);
376*fdc4aca8SBin Meng 
377*fdc4aca8SBin Meng 	if (priv->version == 1)
378*fdc4aca8SBin Meng 		writel(PAGE_SIZE, priv->base + VIRTIO_MMIO_GUEST_PAGE_SIZE);
379*fdc4aca8SBin Meng 
380*fdc4aca8SBin Meng 	debug("(%s): device (%d) vendor (%08x) version (%d)\n", udev->name,
381*fdc4aca8SBin Meng 	      uc_priv->device, uc_priv->vendor, priv->version);
382*fdc4aca8SBin Meng 
383*fdc4aca8SBin Meng 	return 0;
384*fdc4aca8SBin Meng }
385*fdc4aca8SBin Meng 
386*fdc4aca8SBin Meng static const struct dm_virtio_ops virtio_mmio_ops = {
387*fdc4aca8SBin Meng 	.get_config	= virtio_mmio_get_config,
388*fdc4aca8SBin Meng 	.set_config	= virtio_mmio_set_config,
389*fdc4aca8SBin Meng 	.generation	= virtio_mmio_generation,
390*fdc4aca8SBin Meng 	.get_status	= virtio_mmio_get_status,
391*fdc4aca8SBin Meng 	.set_status	= virtio_mmio_set_status,
392*fdc4aca8SBin Meng 	.reset		= virtio_mmio_reset,
393*fdc4aca8SBin Meng 	.get_features	= virtio_mmio_get_features,
394*fdc4aca8SBin Meng 	.set_features	= virtio_mmio_set_features,
395*fdc4aca8SBin Meng 	.find_vqs	= virtio_mmio_find_vqs,
396*fdc4aca8SBin Meng 	.del_vqs	= virtio_mmio_del_vqs,
397*fdc4aca8SBin Meng 	.notify		= virtio_mmio_notify,
398*fdc4aca8SBin Meng };
399*fdc4aca8SBin Meng 
400*fdc4aca8SBin Meng static const struct udevice_id virtio_mmio_ids[] = {
401*fdc4aca8SBin Meng 	{ .compatible = "virtio,mmio" },
402*fdc4aca8SBin Meng 	{ }
403*fdc4aca8SBin Meng };
404*fdc4aca8SBin Meng 
405*fdc4aca8SBin Meng U_BOOT_DRIVER(virtio_mmio) = {
406*fdc4aca8SBin Meng 	.name	= "virtio-mmio",
407*fdc4aca8SBin Meng 	.id	= UCLASS_VIRTIO,
408*fdc4aca8SBin Meng 	.of_match = virtio_mmio_ids,
409*fdc4aca8SBin Meng 	.ops	= &virtio_mmio_ops,
410*fdc4aca8SBin Meng 	.probe	= virtio_mmio_probe,
411*fdc4aca8SBin Meng 	.ofdata_to_platdata = virtio_mmio_ofdata_to_platdata,
412*fdc4aca8SBin Meng 	.priv_auto_alloc_size = sizeof(struct virtio_mmio_priv),
413*fdc4aca8SBin Meng };
414