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