143402ba5SAndrew Jones /* 2d73e4521SAndrew Jones * virtqueue support adapted from the Linux kernel. 3d73e4521SAndrew Jones * 4*49f758b8SAndrew Jones * Copyright (C) 2017, Red Hat Inc, Andrew Jones <drjones@redhat.com> 543402ba5SAndrew Jones * 6*49f758b8SAndrew Jones * This work is licensed under the terms of the GNU GPL, version 2. 743402ba5SAndrew Jones */ 843402ba5SAndrew Jones #include "libcflat.h" 943402ba5SAndrew Jones #include "devicetree.h" 1043402ba5SAndrew Jones #include "alloc.h" 11d73e4521SAndrew Jones #include "asm/page.h" 1243402ba5SAndrew Jones #include "asm/io.h" 1343402ba5SAndrew Jones #include "virtio.h" 1443402ba5SAndrew Jones #include "virtio-mmio.h" 1543402ba5SAndrew Jones 1643402ba5SAndrew Jones static void vm_get(struct virtio_device *vdev, unsigned offset, 1743402ba5SAndrew Jones void *buf, unsigned len) 1843402ba5SAndrew Jones { 1943402ba5SAndrew Jones struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 2043402ba5SAndrew Jones u8 *p = buf; 2143402ba5SAndrew Jones unsigned i; 2243402ba5SAndrew Jones 2343402ba5SAndrew Jones for (i = 0; i < len; ++i) 2443402ba5SAndrew Jones p[i] = readb(vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i); 2543402ba5SAndrew Jones } 2643402ba5SAndrew Jones 2743402ba5SAndrew Jones static void vm_set(struct virtio_device *vdev, unsigned offset, 2843402ba5SAndrew Jones const void *buf, unsigned len) 2943402ba5SAndrew Jones { 3043402ba5SAndrew Jones struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 3143402ba5SAndrew Jones const u8 *p = buf; 3243402ba5SAndrew Jones unsigned i; 3343402ba5SAndrew Jones 3443402ba5SAndrew Jones for (i = 0; i < len; ++i) 3543402ba5SAndrew Jones writeb(p[i], vm_dev->base + VIRTIO_MMIO_CONFIG + offset + i); 3643402ba5SAndrew Jones } 3743402ba5SAndrew Jones 38d73e4521SAndrew Jones static bool vm_notify(struct virtqueue *vq) 39d73e4521SAndrew Jones { 40d73e4521SAndrew Jones struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev); 41d73e4521SAndrew Jones writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY); 42d73e4521SAndrew Jones return true; 43d73e4521SAndrew Jones } 44d73e4521SAndrew Jones 45d73e4521SAndrew Jones static struct virtqueue *vm_setup_vq(struct virtio_device *vdev, 46d73e4521SAndrew Jones unsigned index, 47d73e4521SAndrew Jones void (*callback)(struct virtqueue *vq), 48d73e4521SAndrew Jones const char *name) 49d73e4521SAndrew Jones { 50d73e4521SAndrew Jones struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 51d73e4521SAndrew Jones struct vring_virtqueue *vq; 52d73e4521SAndrew Jones void *queue; 53d73e4521SAndrew Jones unsigned num = VIRTIO_MMIO_QUEUE_NUM_MIN; 54d73e4521SAndrew Jones 55d73e4521SAndrew Jones vq = calloc(1, sizeof(*vq)); 56d73e4521SAndrew Jones queue = memalign(PAGE_SIZE, VIRTIO_MMIO_QUEUE_SIZE_MIN); 57d14aa7cfSAndrew Jones assert(vq && queue); 58d73e4521SAndrew Jones 59d73e4521SAndrew Jones writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); 60d73e4521SAndrew Jones 61d73e4521SAndrew Jones assert(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM_MAX) >= num); 62d73e4521SAndrew Jones 63d73e4521SAndrew Jones if (readl(vm_dev->base + VIRTIO_MMIO_QUEUE_PFN) != 0) { 64d73e4521SAndrew Jones printf("%s: virtqueue %d already setup! base=%p\n", 65d73e4521SAndrew Jones __func__, index, vm_dev->base); 66d73e4521SAndrew Jones return NULL; 67d73e4521SAndrew Jones } 68d73e4521SAndrew Jones 69d73e4521SAndrew Jones writel(num, vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); 70d73e4521SAndrew Jones writel(VIRTIO_MMIO_VRING_ALIGN, 71d73e4521SAndrew Jones vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); 72d73e4521SAndrew Jones writel(virt_to_pfn(queue), vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); 73d73e4521SAndrew Jones 74d73e4521SAndrew Jones vring_init_virtqueue(vq, index, num, VIRTIO_MMIO_VRING_ALIGN, 75d73e4521SAndrew Jones vdev, queue, vm_notify, callback, name); 76d73e4521SAndrew Jones 77d73e4521SAndrew Jones return &vq->vq; 78d73e4521SAndrew Jones } 79d73e4521SAndrew Jones 80d73e4521SAndrew Jones static int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs, 81d73e4521SAndrew Jones struct virtqueue *vqs[], vq_callback_t *callbacks[], 82d73e4521SAndrew Jones const char *names[]) 83d73e4521SAndrew Jones { 84d73e4521SAndrew Jones unsigned i; 85d73e4521SAndrew Jones 86d73e4521SAndrew Jones for (i = 0; i < nvqs; ++i) { 87355e4f48SAndrew Jones vqs[i] = vm_setup_vq(vdev, i, 88355e4f48SAndrew Jones callbacks ? callbacks[i] : NULL, 89355e4f48SAndrew Jones names ? names[i] : ""); 90d73e4521SAndrew Jones if (vqs[i] == NULL) 91d73e4521SAndrew Jones return -1; 92d73e4521SAndrew Jones } 93d73e4521SAndrew Jones 94d73e4521SAndrew Jones return 0; 95d73e4521SAndrew Jones } 96d73e4521SAndrew Jones 9743402ba5SAndrew Jones static const struct virtio_config_ops vm_config_ops = { 9843402ba5SAndrew Jones .get = vm_get, 9943402ba5SAndrew Jones .set = vm_set, 100d73e4521SAndrew Jones .find_vqs = vm_find_vqs, 10143402ba5SAndrew Jones }; 10243402ba5SAndrew Jones 10343402ba5SAndrew Jones static void vm_device_init(struct virtio_mmio_device *vm_dev) 10443402ba5SAndrew Jones { 10543402ba5SAndrew Jones vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID); 10643402ba5SAndrew Jones vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID); 10743402ba5SAndrew Jones vm_dev->vdev.config = &vm_config_ops; 108d73e4521SAndrew Jones 109d73e4521SAndrew Jones writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); 11043402ba5SAndrew Jones } 11143402ba5SAndrew Jones 11243402ba5SAndrew Jones /****************************************************** 11343402ba5SAndrew Jones * virtio-mmio device tree support 11443402ba5SAndrew Jones ******************************************************/ 11543402ba5SAndrew Jones 11643402ba5SAndrew Jones struct vm_dt_info { 11743402ba5SAndrew Jones u32 devid; 11843402ba5SAndrew Jones void *base; 11943402ba5SAndrew Jones }; 12043402ba5SAndrew Jones 12143402ba5SAndrew Jones static int vm_dt_match(const struct dt_device *dev, int fdtnode) 12243402ba5SAndrew Jones { 12343402ba5SAndrew Jones struct vm_dt_info *info = (struct vm_dt_info *)dev->info; 12443402ba5SAndrew Jones struct dt_pbus_reg base; 12543402ba5SAndrew Jones u32 magic; 12618ab6cadSAndrew Jones int ret; 12743402ba5SAndrew Jones 12843402ba5SAndrew Jones dt_device_bind_node((struct dt_device *)dev, fdtnode); 12943402ba5SAndrew Jones 13018ab6cadSAndrew Jones ret = dt_pbus_get_base(dev, &base); 13118ab6cadSAndrew Jones assert(ret == 0); 13243402ba5SAndrew Jones info->base = ioremap(base.addr, base.size); 13343402ba5SAndrew Jones 13443402ba5SAndrew Jones magic = readl(info->base + VIRTIO_MMIO_MAGIC_VALUE); 13543402ba5SAndrew Jones if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) 13643402ba5SAndrew Jones return false; 13743402ba5SAndrew Jones 13843402ba5SAndrew Jones return readl(info->base + VIRTIO_MMIO_DEVICE_ID) == info->devid; 13943402ba5SAndrew Jones } 14043402ba5SAndrew Jones 14143402ba5SAndrew Jones static struct virtio_device *virtio_mmio_dt_bind(u32 devid) 14243402ba5SAndrew Jones { 14343402ba5SAndrew Jones struct virtio_mmio_device *vm_dev; 14443402ba5SAndrew Jones struct dt_device dt_dev; 14543402ba5SAndrew Jones struct dt_bus dt_bus; 14643402ba5SAndrew Jones struct vm_dt_info info; 14743402ba5SAndrew Jones int node; 14843402ba5SAndrew Jones 14943402ba5SAndrew Jones if (!dt_available()) 15043402ba5SAndrew Jones return NULL; 15143402ba5SAndrew Jones 15243402ba5SAndrew Jones dt_bus_init_defaults(&dt_bus); 15343402ba5SAndrew Jones dt_bus.match = vm_dt_match; 15443402ba5SAndrew Jones 15543402ba5SAndrew Jones info.devid = devid; 15643402ba5SAndrew Jones 15743402ba5SAndrew Jones dt_device_init(&dt_dev, &dt_bus, &info); 15843402ba5SAndrew Jones 15943402ba5SAndrew Jones node = dt_device_find_compatible(&dt_dev, "virtio,mmio"); 16043402ba5SAndrew Jones assert(node >= 0 || node == -FDT_ERR_NOTFOUND); 16143402ba5SAndrew Jones 16243402ba5SAndrew Jones if (node == -FDT_ERR_NOTFOUND) 16343402ba5SAndrew Jones return NULL; 16443402ba5SAndrew Jones 16543402ba5SAndrew Jones vm_dev = calloc(1, sizeof(*vm_dev)); 166d14aa7cfSAndrew Jones assert(vm_dev != NULL); 16743402ba5SAndrew Jones 16843402ba5SAndrew Jones vm_dev->base = info.base; 16943402ba5SAndrew Jones vm_device_init(vm_dev); 17043402ba5SAndrew Jones 17143402ba5SAndrew Jones return &vm_dev->vdev; 17243402ba5SAndrew Jones } 17343402ba5SAndrew Jones 17443402ba5SAndrew Jones struct virtio_device *virtio_mmio_bind(u32 devid) 17543402ba5SAndrew Jones { 17643402ba5SAndrew Jones return virtio_mmio_dt_bind(devid); 17743402ba5SAndrew Jones } 178