xref: /kvm-unit-tests/lib/virtio-mmio.c (revision 43402ba5fa2dc06e12855a04f6003f1b3f838070)
1*43402ba5SAndrew Jones /*
2*43402ba5SAndrew Jones  * Copyright (C) 2014, Red Hat Inc, Andrew Jones <drjones@redhat.com>
3*43402ba5SAndrew Jones  *
4*43402ba5SAndrew Jones  * This work is licensed under the terms of the GNU LGPL, version 2.
5*43402ba5SAndrew Jones  */
6*43402ba5SAndrew Jones #include "libcflat.h"
7*43402ba5SAndrew Jones #include "devicetree.h"
8*43402ba5SAndrew Jones #include "alloc.h"
9*43402ba5SAndrew Jones #include "asm/io.h"
10*43402ba5SAndrew Jones #include "virtio.h"
11*43402ba5SAndrew Jones #include "virtio-mmio.h"
12*43402ba5SAndrew Jones 
13*43402ba5SAndrew Jones static void vm_get(struct virtio_device *vdev, unsigned offset,
14*43402ba5SAndrew Jones 		   void *buf, unsigned len)
15*43402ba5SAndrew Jones {
16*43402ba5SAndrew Jones 	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
17*43402ba5SAndrew Jones 	u8 *p = buf;
18*43402ba5SAndrew Jones 	unsigned i;
19*43402ba5SAndrew Jones 
20*43402ba5SAndrew Jones 	for (i = 0; i < len; ++i)
21*43402ba5SAndrew Jones 		p[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
22*43402ba5SAndrew Jones }
23*43402ba5SAndrew Jones 
24*43402ba5SAndrew Jones static void vm_set(struct virtio_device *vdev, unsigned offset,
25*43402ba5SAndrew Jones 		   const void *buf, unsigned len)
26*43402ba5SAndrew Jones {
27*43402ba5SAndrew Jones 	struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev);
28*43402ba5SAndrew Jones 	const u8 *p = buf;
29*43402ba5SAndrew Jones 	unsigned i;
30*43402ba5SAndrew Jones 
31*43402ba5SAndrew Jones 	for (i = 0; i < len; ++i)
32*43402ba5SAndrew Jones 		writeb(p[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i);
33*43402ba5SAndrew Jones }
34*43402ba5SAndrew Jones 
35*43402ba5SAndrew Jones static const struct virtio_config_ops vm_config_ops = {
36*43402ba5SAndrew Jones 	.get = vm_get,
37*43402ba5SAndrew Jones 	.set = vm_set,
38*43402ba5SAndrew Jones };
39*43402ba5SAndrew Jones 
40*43402ba5SAndrew Jones static void vm_device_init(struct virtio_mmio_device *vm_dev)
41*43402ba5SAndrew Jones {
42*43402ba5SAndrew Jones 	vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID);
43*43402ba5SAndrew Jones 	vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID);
44*43402ba5SAndrew Jones 	vm_dev->vdev.config = &vm_config_ops;
45*43402ba5SAndrew Jones }
46*43402ba5SAndrew Jones 
47*43402ba5SAndrew Jones /******************************************************
48*43402ba5SAndrew Jones  * virtio-mmio device tree support
49*43402ba5SAndrew Jones  ******************************************************/
50*43402ba5SAndrew Jones 
51*43402ba5SAndrew Jones struct vm_dt_info {
52*43402ba5SAndrew Jones 	u32 devid;
53*43402ba5SAndrew Jones 	void *base;
54*43402ba5SAndrew Jones };
55*43402ba5SAndrew Jones 
56*43402ba5SAndrew Jones static int vm_dt_match(const struct dt_device *dev, int fdtnode)
57*43402ba5SAndrew Jones {
58*43402ba5SAndrew Jones 	struct vm_dt_info *info = (struct vm_dt_info *)dev->info;
59*43402ba5SAndrew Jones 	struct dt_pbus_reg base;
60*43402ba5SAndrew Jones 	u32 magic;
61*43402ba5SAndrew Jones 
62*43402ba5SAndrew Jones 	dt_device_bind_node((struct dt_device *)dev, fdtnode);
63*43402ba5SAndrew Jones 
64*43402ba5SAndrew Jones 	assert(dt_pbus_get_base(dev, &base) == 0);
65*43402ba5SAndrew Jones 	info->base = ioremap(base.addr, base.size);
66*43402ba5SAndrew Jones 
67*43402ba5SAndrew Jones 	magic = readl(info->base + VIRTIO_MMIO_MAGIC_VALUE);
68*43402ba5SAndrew Jones 	if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24))
69*43402ba5SAndrew Jones 		return false;
70*43402ba5SAndrew Jones 
71*43402ba5SAndrew Jones 	return readl(info->base + VIRTIO_MMIO_DEVICE_ID) == info->devid;
72*43402ba5SAndrew Jones }
73*43402ba5SAndrew Jones 
74*43402ba5SAndrew Jones static struct virtio_device *virtio_mmio_dt_bind(u32 devid)
75*43402ba5SAndrew Jones {
76*43402ba5SAndrew Jones 	struct virtio_mmio_device *vm_dev;
77*43402ba5SAndrew Jones 	struct dt_device dt_dev;
78*43402ba5SAndrew Jones 	struct dt_bus dt_bus;
79*43402ba5SAndrew Jones 	struct vm_dt_info info;
80*43402ba5SAndrew Jones 	int node;
81*43402ba5SAndrew Jones 
82*43402ba5SAndrew Jones 	if (!dt_available())
83*43402ba5SAndrew Jones 		return NULL;
84*43402ba5SAndrew Jones 
85*43402ba5SAndrew Jones 	dt_bus_init_defaults(&dt_bus);
86*43402ba5SAndrew Jones 	dt_bus.match = vm_dt_match;
87*43402ba5SAndrew Jones 
88*43402ba5SAndrew Jones 	info.devid = devid;
89*43402ba5SAndrew Jones 
90*43402ba5SAndrew Jones 	dt_device_init(&dt_dev, &dt_bus, &info);
91*43402ba5SAndrew Jones 
92*43402ba5SAndrew Jones 	node = dt_device_find_compatible(&dt_dev, "virtio,mmio");
93*43402ba5SAndrew Jones 	assert(node >= 0 || node == -FDT_ERR_NOTFOUND);
94*43402ba5SAndrew Jones 
95*43402ba5SAndrew Jones 	if (node == -FDT_ERR_NOTFOUND)
96*43402ba5SAndrew Jones 		return NULL;
97*43402ba5SAndrew Jones 
98*43402ba5SAndrew Jones 	vm_dev = calloc(1, sizeof(*vm_dev));
99*43402ba5SAndrew Jones 	if (!vm_dev)
100*43402ba5SAndrew Jones 		return NULL;
101*43402ba5SAndrew Jones 
102*43402ba5SAndrew Jones 	vm_dev->base = info.base;
103*43402ba5SAndrew Jones 	vm_device_init(vm_dev);
104*43402ba5SAndrew Jones 
105*43402ba5SAndrew Jones 	return &vm_dev->vdev;
106*43402ba5SAndrew Jones }
107*43402ba5SAndrew Jones 
108*43402ba5SAndrew Jones struct virtio_device *virtio_mmio_bind(u32 devid)
109*43402ba5SAndrew Jones {
110*43402ba5SAndrew Jones 	return virtio_mmio_dt_bind(devid);
111*43402ba5SAndrew Jones }
112