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