18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Virtio memory mapped device driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright 2011-2014, ARM Ltd. 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * This module allows virtio devices to be used over a virtual, memory mapped 88c2ecf20Sopenharmony_ci * platform device. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * The guest device(s) may be instantiated in one of three equivalent ways: 118c2ecf20Sopenharmony_ci * 128c2ecf20Sopenharmony_ci * 1. Static platform device in board's code, eg.: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * static struct platform_device v2m_virtio_device = { 158c2ecf20Sopenharmony_ci * .name = "virtio-mmio", 168c2ecf20Sopenharmony_ci * .id = -1, 178c2ecf20Sopenharmony_ci * .num_resources = 2, 188c2ecf20Sopenharmony_ci * .resource = (struct resource []) { 198c2ecf20Sopenharmony_ci * { 208c2ecf20Sopenharmony_ci * .start = 0x1001e000, 218c2ecf20Sopenharmony_ci * .end = 0x1001e0ff, 228c2ecf20Sopenharmony_ci * .flags = IORESOURCE_MEM, 238c2ecf20Sopenharmony_ci * }, { 248c2ecf20Sopenharmony_ci * .start = 42 + 32, 258c2ecf20Sopenharmony_ci * .end = 42 + 32, 268c2ecf20Sopenharmony_ci * .flags = IORESOURCE_IRQ, 278c2ecf20Sopenharmony_ci * }, 288c2ecf20Sopenharmony_ci * } 298c2ecf20Sopenharmony_ci * }; 308c2ecf20Sopenharmony_ci * 318c2ecf20Sopenharmony_ci * 2. Device Tree node, eg.: 328c2ecf20Sopenharmony_ci * 338c2ecf20Sopenharmony_ci * virtio_block@1e000 { 348c2ecf20Sopenharmony_ci * compatible = "virtio,mmio"; 358c2ecf20Sopenharmony_ci * reg = <0x1e000 0x100>; 368c2ecf20Sopenharmony_ci * interrupts = <42>; 378c2ecf20Sopenharmony_ci * } 388c2ecf20Sopenharmony_ci * 398c2ecf20Sopenharmony_ci * 3. Kernel module (or command line) parameter. Can be used more than once - 408c2ecf20Sopenharmony_ci * one device will be created for each one. Syntax: 418c2ecf20Sopenharmony_ci * 428c2ecf20Sopenharmony_ci * [virtio_mmio.]device=<size>@<baseaddr>:<irq>[:<id>] 438c2ecf20Sopenharmony_ci * where: 448c2ecf20Sopenharmony_ci * <size> := size (can use standard suffixes like K, M or G) 458c2ecf20Sopenharmony_ci * <baseaddr> := physical base address 468c2ecf20Sopenharmony_ci * <irq> := interrupt number (as passed to request_irq()) 478c2ecf20Sopenharmony_ci * <id> := (optional) platform device id 488c2ecf20Sopenharmony_ci * eg.: 498c2ecf20Sopenharmony_ci * virtio_mmio.device=0x100@0x100b0000:48 \ 508c2ecf20Sopenharmony_ci * virtio_mmio.device=1K@0x1001e000:74 518c2ecf20Sopenharmony_ci * 528c2ecf20Sopenharmony_ci * Based on Virtio PCI driver by Anthony Liguori, copyright IBM Corp. 2007 538c2ecf20Sopenharmony_ci */ 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "virtio-mmio: " fmt 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci#include <linux/acpi.h> 588c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 598c2ecf20Sopenharmony_ci#include <linux/highmem.h> 608c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 618c2ecf20Sopenharmony_ci#include <linux/io.h> 628c2ecf20Sopenharmony_ci#include <linux/list.h> 638c2ecf20Sopenharmony_ci#include <linux/module.h> 648c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 658c2ecf20Sopenharmony_ci#include <linux/pm.h> 668c2ecf20Sopenharmony_ci#include <linux/slab.h> 678c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 688c2ecf20Sopenharmony_ci#include <linux/virtio.h> 698c2ecf20Sopenharmony_ci#include <linux/virtio_config.h> 708c2ecf20Sopenharmony_ci#include <uapi/linux/virtio_mmio.h> 718c2ecf20Sopenharmony_ci#include <linux/virtio_ring.h> 728c2ecf20Sopenharmony_ci 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci/* The alignment to use between consumer and producer parts of vring. 768c2ecf20Sopenharmony_ci * Currently hardcoded to the page size. */ 778c2ecf20Sopenharmony_ci#define VIRTIO_MMIO_VRING_ALIGN PAGE_SIZE 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci#define to_virtio_mmio_device(_plat_dev) \ 828c2ecf20Sopenharmony_ci container_of(_plat_dev, struct virtio_mmio_device, vdev) 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_cistruct virtio_mmio_device { 858c2ecf20Sopenharmony_ci struct virtio_device vdev; 868c2ecf20Sopenharmony_ci struct platform_device *pdev; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci void __iomem *base; 898c2ecf20Sopenharmony_ci unsigned long version; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci /* a list of queues so we can dispatch IRQs */ 928c2ecf20Sopenharmony_ci spinlock_t lock; 938c2ecf20Sopenharmony_ci struct list_head virtqueues; 948c2ecf20Sopenharmony_ci}; 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistruct virtio_mmio_vq_info { 978c2ecf20Sopenharmony_ci /* the actual virtqueue */ 988c2ecf20Sopenharmony_ci struct virtqueue *vq; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* the list node for the virtqueues list */ 1018c2ecf20Sopenharmony_ci struct list_head node; 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci 1068c2ecf20Sopenharmony_ci/* Configuration interface */ 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_cistatic u64 vm_get_features(struct virtio_device *vdev) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 1118c2ecf20Sopenharmony_ci u64 features; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci writel(1, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); 1148c2ecf20Sopenharmony_ci features = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); 1158c2ecf20Sopenharmony_ci features <<= 32; 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci writel(0, vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES_SEL); 1188c2ecf20Sopenharmony_ci features |= readl(vm_dev->base + VIRTIO_MMIO_DEVICE_FEATURES); 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci return features; 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic int vm_finalize_features(struct virtio_device *vdev) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci /* Give virtio_ring a chance to accept features. */ 1288c2ecf20Sopenharmony_ci vring_transport_features(vdev); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci /* Make sure there is are no mixed devices */ 1318c2ecf20Sopenharmony_ci if (vm_dev->version == 2 && 1328c2ecf20Sopenharmony_ci !__virtio_test_bit(vdev, VIRTIO_F_VERSION_1)) { 1338c2ecf20Sopenharmony_ci dev_err(&vdev->dev, "New virtio-mmio devices (version 2) must provide VIRTIO_F_VERSION_1 feature!\n"); 1348c2ecf20Sopenharmony_ci return -EINVAL; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci writel(1, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); 1388c2ecf20Sopenharmony_ci writel((u32)(vdev->features >> 32), 1398c2ecf20Sopenharmony_ci vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci writel(0, vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES_SEL); 1428c2ecf20Sopenharmony_ci writel((u32)vdev->features, 1438c2ecf20Sopenharmony_ci vm_dev->base + VIRTIO_MMIO_DRIVER_FEATURES); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic void vm_get(struct virtio_device *vdev, unsigned offset, 1498c2ecf20Sopenharmony_ci void *buf, unsigned len) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 1528c2ecf20Sopenharmony_ci void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG; 1538c2ecf20Sopenharmony_ci u8 b; 1548c2ecf20Sopenharmony_ci __le16 w; 1558c2ecf20Sopenharmony_ci __le32 l; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (vm_dev->version == 1) { 1588c2ecf20Sopenharmony_ci u8 *ptr = buf; 1598c2ecf20Sopenharmony_ci int i; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 1628c2ecf20Sopenharmony_ci ptr[i] = readb(base + offset + i); 1638c2ecf20Sopenharmony_ci return; 1648c2ecf20Sopenharmony_ci } 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_ci switch (len) { 1678c2ecf20Sopenharmony_ci case 1: 1688c2ecf20Sopenharmony_ci b = readb(base + offset); 1698c2ecf20Sopenharmony_ci memcpy(buf, &b, sizeof b); 1708c2ecf20Sopenharmony_ci break; 1718c2ecf20Sopenharmony_ci case 2: 1728c2ecf20Sopenharmony_ci w = cpu_to_le16(readw(base + offset)); 1738c2ecf20Sopenharmony_ci memcpy(buf, &w, sizeof w); 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci case 4: 1768c2ecf20Sopenharmony_ci l = cpu_to_le32(readl(base + offset)); 1778c2ecf20Sopenharmony_ci memcpy(buf, &l, sizeof l); 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case 8: 1808c2ecf20Sopenharmony_ci l = cpu_to_le32(readl(base + offset)); 1818c2ecf20Sopenharmony_ci memcpy(buf, &l, sizeof l); 1828c2ecf20Sopenharmony_ci l = cpu_to_le32(ioread32(base + offset + sizeof l)); 1838c2ecf20Sopenharmony_ci memcpy(buf + sizeof l, &l, sizeof l); 1848c2ecf20Sopenharmony_ci break; 1858c2ecf20Sopenharmony_ci default: 1868c2ecf20Sopenharmony_ci BUG(); 1878c2ecf20Sopenharmony_ci } 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void vm_set(struct virtio_device *vdev, unsigned offset, 1918c2ecf20Sopenharmony_ci const void *buf, unsigned len) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 1948c2ecf20Sopenharmony_ci void __iomem *base = vm_dev->base + VIRTIO_MMIO_CONFIG; 1958c2ecf20Sopenharmony_ci u8 b; 1968c2ecf20Sopenharmony_ci __le16 w; 1978c2ecf20Sopenharmony_ci __le32 l; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (vm_dev->version == 1) { 2008c2ecf20Sopenharmony_ci const u8 *ptr = buf; 2018c2ecf20Sopenharmony_ci int i; 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci for (i = 0; i < len; i++) 2048c2ecf20Sopenharmony_ci writeb(ptr[i], base + offset + i); 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci return; 2078c2ecf20Sopenharmony_ci } 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci switch (len) { 2108c2ecf20Sopenharmony_ci case 1: 2118c2ecf20Sopenharmony_ci memcpy(&b, buf, sizeof b); 2128c2ecf20Sopenharmony_ci writeb(b, base + offset); 2138c2ecf20Sopenharmony_ci break; 2148c2ecf20Sopenharmony_ci case 2: 2158c2ecf20Sopenharmony_ci memcpy(&w, buf, sizeof w); 2168c2ecf20Sopenharmony_ci writew(le16_to_cpu(w), base + offset); 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci case 4: 2198c2ecf20Sopenharmony_ci memcpy(&l, buf, sizeof l); 2208c2ecf20Sopenharmony_ci writel(le32_to_cpu(l), base + offset); 2218c2ecf20Sopenharmony_ci break; 2228c2ecf20Sopenharmony_ci case 8: 2238c2ecf20Sopenharmony_ci memcpy(&l, buf, sizeof l); 2248c2ecf20Sopenharmony_ci writel(le32_to_cpu(l), base + offset); 2258c2ecf20Sopenharmony_ci memcpy(&l, buf + sizeof l, sizeof l); 2268c2ecf20Sopenharmony_ci writel(le32_to_cpu(l), base + offset + sizeof l); 2278c2ecf20Sopenharmony_ci break; 2288c2ecf20Sopenharmony_ci default: 2298c2ecf20Sopenharmony_ci BUG(); 2308c2ecf20Sopenharmony_ci } 2318c2ecf20Sopenharmony_ci} 2328c2ecf20Sopenharmony_ci 2338c2ecf20Sopenharmony_cistatic u32 vm_generation(struct virtio_device *vdev) 2348c2ecf20Sopenharmony_ci{ 2358c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci if (vm_dev->version == 1) 2388c2ecf20Sopenharmony_ci return 0; 2398c2ecf20Sopenharmony_ci else 2408c2ecf20Sopenharmony_ci return readl(vm_dev->base + VIRTIO_MMIO_CONFIG_GENERATION); 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ci 2438c2ecf20Sopenharmony_cistatic u8 vm_get_status(struct virtio_device *vdev) 2448c2ecf20Sopenharmony_ci{ 2458c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 2468c2ecf20Sopenharmony_ci 2478c2ecf20Sopenharmony_ci return readl(vm_dev->base + VIRTIO_MMIO_STATUS) & 0xff; 2488c2ecf20Sopenharmony_ci} 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_cistatic void vm_set_status(struct virtio_device *vdev, u8 status) 2518c2ecf20Sopenharmony_ci{ 2528c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_ci /* We should never be setting status to 0. */ 2558c2ecf20Sopenharmony_ci BUG_ON(status == 0); 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci writel(status, vm_dev->base + VIRTIO_MMIO_STATUS); 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void vm_reset(struct virtio_device *vdev) 2618c2ecf20Sopenharmony_ci{ 2628c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci /* 0 status means a reset. */ 2658c2ecf20Sopenharmony_ci writel(0, vm_dev->base + VIRTIO_MMIO_STATUS); 2668c2ecf20Sopenharmony_ci} 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci/* Transport interface */ 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci/* the notify function used when creating a virt queue */ 2738c2ecf20Sopenharmony_cistatic bool vm_notify(struct virtqueue *vq) 2748c2ecf20Sopenharmony_ci{ 2758c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev); 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci /* We write the queue's selector into the notification register to 2788c2ecf20Sopenharmony_ci * signal the other end */ 2798c2ecf20Sopenharmony_ci writel(vq->index, vm_dev->base + VIRTIO_MMIO_QUEUE_NOTIFY); 2808c2ecf20Sopenharmony_ci return true; 2818c2ecf20Sopenharmony_ci} 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci/* Notify all virtqueues on an interrupt. */ 2848c2ecf20Sopenharmony_cistatic irqreturn_t vm_interrupt(int irq, void *opaque) 2858c2ecf20Sopenharmony_ci{ 2868c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = opaque; 2878c2ecf20Sopenharmony_ci struct virtio_mmio_vq_info *info; 2888c2ecf20Sopenharmony_ci unsigned long status; 2898c2ecf20Sopenharmony_ci unsigned long flags; 2908c2ecf20Sopenharmony_ci irqreturn_t ret = IRQ_NONE; 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_ci /* Read and acknowledge interrupts */ 2938c2ecf20Sopenharmony_ci status = readl(vm_dev->base + VIRTIO_MMIO_INTERRUPT_STATUS); 2948c2ecf20Sopenharmony_ci writel(status, vm_dev->base + VIRTIO_MMIO_INTERRUPT_ACK); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (unlikely(status & VIRTIO_MMIO_INT_CONFIG)) { 2978c2ecf20Sopenharmony_ci virtio_config_changed(&vm_dev->vdev); 2988c2ecf20Sopenharmony_ci ret = IRQ_HANDLED; 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (likely(status & VIRTIO_MMIO_INT_VRING)) { 3028c2ecf20Sopenharmony_ci spin_lock_irqsave(&vm_dev->lock, flags); 3038c2ecf20Sopenharmony_ci list_for_each_entry(info, &vm_dev->virtqueues, node) 3048c2ecf20Sopenharmony_ci ret |= vring_interrupt(irq, info->vq); 3058c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vm_dev->lock, flags); 3068c2ecf20Sopenharmony_ci } 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci return ret; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic void vm_del_vq(struct virtqueue *vq) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vq->vdev); 3168c2ecf20Sopenharmony_ci struct virtio_mmio_vq_info *info = vq->priv; 3178c2ecf20Sopenharmony_ci unsigned long flags; 3188c2ecf20Sopenharmony_ci unsigned int index = vq->index; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci spin_lock_irqsave(&vm_dev->lock, flags); 3218c2ecf20Sopenharmony_ci list_del(&info->node); 3228c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vm_dev->lock, flags); 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* Select and deactivate the queue */ 3258c2ecf20Sopenharmony_ci writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); 3268c2ecf20Sopenharmony_ci if (vm_dev->version == 1) { 3278c2ecf20Sopenharmony_ci writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); 3288c2ecf20Sopenharmony_ci } else { 3298c2ecf20Sopenharmony_ci writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); 3308c2ecf20Sopenharmony_ci WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); 3318c2ecf20Sopenharmony_ci } 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci vring_del_virtqueue(vq); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci kfree(info); 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void vm_del_vqs(struct virtio_device *vdev) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 3418c2ecf20Sopenharmony_ci struct virtqueue *vq, *n; 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci list_for_each_entry_safe(vq, n, &vdev->vqs, list) 3448c2ecf20Sopenharmony_ci vm_del_vq(vq); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci free_irq(platform_get_irq(vm_dev->pdev, 0), vm_dev); 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_cistatic struct virtqueue *vm_setup_vq(struct virtio_device *vdev, unsigned index, 3508c2ecf20Sopenharmony_ci void (*callback)(struct virtqueue *vq), 3518c2ecf20Sopenharmony_ci const char *name, bool ctx) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 3548c2ecf20Sopenharmony_ci struct virtio_mmio_vq_info *info; 3558c2ecf20Sopenharmony_ci struct virtqueue *vq; 3568c2ecf20Sopenharmony_ci unsigned long flags; 3578c2ecf20Sopenharmony_ci unsigned int num; 3588c2ecf20Sopenharmony_ci int err; 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_ci if (!name) 3618c2ecf20Sopenharmony_ci return NULL; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci /* Select the queue we're interested in */ 3648c2ecf20Sopenharmony_ci writel(index, vm_dev->base + VIRTIO_MMIO_QUEUE_SEL); 3658c2ecf20Sopenharmony_ci 3668c2ecf20Sopenharmony_ci /* Queue shouldn't already be set up. */ 3678c2ecf20Sopenharmony_ci if (readl(vm_dev->base + (vm_dev->version == 1 ? 3688c2ecf20Sopenharmony_ci VIRTIO_MMIO_QUEUE_PFN : VIRTIO_MMIO_QUEUE_READY))) { 3698c2ecf20Sopenharmony_ci err = -ENOENT; 3708c2ecf20Sopenharmony_ci goto error_available; 3718c2ecf20Sopenharmony_ci } 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ci /* Allocate and fill out our active queue description */ 3748c2ecf20Sopenharmony_ci info = kmalloc(sizeof(*info), GFP_KERNEL); 3758c2ecf20Sopenharmony_ci if (!info) { 3768c2ecf20Sopenharmony_ci err = -ENOMEM; 3778c2ecf20Sopenharmony_ci goto error_kmalloc; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci num = readl(vm_dev->base + VIRTIO_MMIO_QUEUE_NUM_MAX); 3818c2ecf20Sopenharmony_ci if (num == 0) { 3828c2ecf20Sopenharmony_ci err = -ENOENT; 3838c2ecf20Sopenharmony_ci goto error_new_virtqueue; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci /* Create the vring */ 3878c2ecf20Sopenharmony_ci vq = vring_create_virtqueue(index, num, VIRTIO_MMIO_VRING_ALIGN, vdev, 3888c2ecf20Sopenharmony_ci true, true, ctx, vm_notify, callback, name); 3898c2ecf20Sopenharmony_ci if (!vq) { 3908c2ecf20Sopenharmony_ci err = -ENOMEM; 3918c2ecf20Sopenharmony_ci goto error_new_virtqueue; 3928c2ecf20Sopenharmony_ci } 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci /* Activate the queue */ 3958c2ecf20Sopenharmony_ci writel(virtqueue_get_vring_size(vq), vm_dev->base + VIRTIO_MMIO_QUEUE_NUM); 3968c2ecf20Sopenharmony_ci if (vm_dev->version == 1) { 3978c2ecf20Sopenharmony_ci u64 q_pfn = virtqueue_get_desc_addr(vq) >> PAGE_SHIFT; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci /* 4008c2ecf20Sopenharmony_ci * virtio-mmio v1 uses a 32bit QUEUE PFN. If we have something 4018c2ecf20Sopenharmony_ci * that doesn't fit in 32bit, fail the setup rather than 4028c2ecf20Sopenharmony_ci * pretending to be successful. 4038c2ecf20Sopenharmony_ci */ 4048c2ecf20Sopenharmony_ci if (q_pfn >> 32) { 4058c2ecf20Sopenharmony_ci dev_err(&vdev->dev, 4068c2ecf20Sopenharmony_ci "platform bug: legacy virtio-mmio must not be used with RAM above 0x%llxGB\n", 4078c2ecf20Sopenharmony_ci 0x1ULL << (32 + PAGE_SHIFT - 30)); 4088c2ecf20Sopenharmony_ci err = -E2BIG; 4098c2ecf20Sopenharmony_ci goto error_bad_pfn; 4108c2ecf20Sopenharmony_ci } 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_QUEUE_ALIGN); 4138c2ecf20Sopenharmony_ci writel(q_pfn, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); 4148c2ecf20Sopenharmony_ci } else { 4158c2ecf20Sopenharmony_ci u64 addr; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci addr = virtqueue_get_desc_addr(vq); 4188c2ecf20Sopenharmony_ci writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_LOW); 4198c2ecf20Sopenharmony_ci writel((u32)(addr >> 32), 4208c2ecf20Sopenharmony_ci vm_dev->base + VIRTIO_MMIO_QUEUE_DESC_HIGH); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci addr = virtqueue_get_avail_addr(vq); 4238c2ecf20Sopenharmony_ci writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_LOW); 4248c2ecf20Sopenharmony_ci writel((u32)(addr >> 32), 4258c2ecf20Sopenharmony_ci vm_dev->base + VIRTIO_MMIO_QUEUE_AVAIL_HIGH); 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_ci addr = virtqueue_get_used_addr(vq); 4288c2ecf20Sopenharmony_ci writel((u32)addr, vm_dev->base + VIRTIO_MMIO_QUEUE_USED_LOW); 4298c2ecf20Sopenharmony_ci writel((u32)(addr >> 32), 4308c2ecf20Sopenharmony_ci vm_dev->base + VIRTIO_MMIO_QUEUE_USED_HIGH); 4318c2ecf20Sopenharmony_ci 4328c2ecf20Sopenharmony_ci writel(1, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); 4338c2ecf20Sopenharmony_ci } 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci vq->priv = info; 4368c2ecf20Sopenharmony_ci info->vq = vq; 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci spin_lock_irqsave(&vm_dev->lock, flags); 4398c2ecf20Sopenharmony_ci list_add(&info->node, &vm_dev->virtqueues); 4408c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&vm_dev->lock, flags); 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci return vq; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_cierror_bad_pfn: 4458c2ecf20Sopenharmony_ci vring_del_virtqueue(vq); 4468c2ecf20Sopenharmony_cierror_new_virtqueue: 4478c2ecf20Sopenharmony_ci if (vm_dev->version == 1) { 4488c2ecf20Sopenharmony_ci writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_PFN); 4498c2ecf20Sopenharmony_ci } else { 4508c2ecf20Sopenharmony_ci writel(0, vm_dev->base + VIRTIO_MMIO_QUEUE_READY); 4518c2ecf20Sopenharmony_ci WARN_ON(readl(vm_dev->base + VIRTIO_MMIO_QUEUE_READY)); 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci kfree(info); 4548c2ecf20Sopenharmony_cierror_kmalloc: 4558c2ecf20Sopenharmony_cierror_available: 4568c2ecf20Sopenharmony_ci return ERR_PTR(err); 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_cistatic int vm_find_vqs(struct virtio_device *vdev, unsigned nvqs, 4608c2ecf20Sopenharmony_ci struct virtqueue *vqs[], 4618c2ecf20Sopenharmony_ci vq_callback_t *callbacks[], 4628c2ecf20Sopenharmony_ci const char * const names[], 4638c2ecf20Sopenharmony_ci const bool *ctx, 4648c2ecf20Sopenharmony_ci struct irq_affinity *desc) 4658c2ecf20Sopenharmony_ci{ 4668c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 4678c2ecf20Sopenharmony_ci int irq = platform_get_irq(vm_dev->pdev, 0); 4688c2ecf20Sopenharmony_ci int i, err, queue_idx = 0; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci if (irq < 0) 4718c2ecf20Sopenharmony_ci return irq; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci err = request_irq(irq, vm_interrupt, IRQF_SHARED, 4748c2ecf20Sopenharmony_ci dev_name(&vdev->dev), vm_dev); 4758c2ecf20Sopenharmony_ci if (err) 4768c2ecf20Sopenharmony_ci return err; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci for (i = 0; i < nvqs; ++i) { 4798c2ecf20Sopenharmony_ci if (!names[i]) { 4808c2ecf20Sopenharmony_ci vqs[i] = NULL; 4818c2ecf20Sopenharmony_ci continue; 4828c2ecf20Sopenharmony_ci } 4838c2ecf20Sopenharmony_ci 4848c2ecf20Sopenharmony_ci vqs[i] = vm_setup_vq(vdev, queue_idx++, callbacks[i], names[i], 4858c2ecf20Sopenharmony_ci ctx ? ctx[i] : false); 4868c2ecf20Sopenharmony_ci if (IS_ERR(vqs[i])) { 4878c2ecf20Sopenharmony_ci vm_del_vqs(vdev); 4888c2ecf20Sopenharmony_ci return PTR_ERR(vqs[i]); 4898c2ecf20Sopenharmony_ci } 4908c2ecf20Sopenharmony_ci } 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic const char *vm_bus_name(struct virtio_device *vdev) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci return vm_dev->pdev->name; 5008c2ecf20Sopenharmony_ci} 5018c2ecf20Sopenharmony_ci 5028c2ecf20Sopenharmony_cistatic bool vm_get_shm_region(struct virtio_device *vdev, 5038c2ecf20Sopenharmony_ci struct virtio_shm_region *region, u8 id) 5048c2ecf20Sopenharmony_ci{ 5058c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 5068c2ecf20Sopenharmony_ci u64 len, addr; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci /* Select the region we're interested in */ 5098c2ecf20Sopenharmony_ci writel(id, vm_dev->base + VIRTIO_MMIO_SHM_SEL); 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* Read the region size */ 5128c2ecf20Sopenharmony_ci len = (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_LEN_LOW); 5138c2ecf20Sopenharmony_ci len |= (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_LEN_HIGH) << 32; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci region->len = len; 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ci /* Check if region length is -1. If that's the case, the shared memory 5188c2ecf20Sopenharmony_ci * region does not exist and there is no need to proceed further. 5198c2ecf20Sopenharmony_ci */ 5208c2ecf20Sopenharmony_ci if (len == ~(u64)0) 5218c2ecf20Sopenharmony_ci return false; 5228c2ecf20Sopenharmony_ci 5238c2ecf20Sopenharmony_ci /* Read the region base address */ 5248c2ecf20Sopenharmony_ci addr = (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_BASE_LOW); 5258c2ecf20Sopenharmony_ci addr |= (u64) readl(vm_dev->base + VIRTIO_MMIO_SHM_BASE_HIGH) << 32; 5268c2ecf20Sopenharmony_ci 5278c2ecf20Sopenharmony_ci region->addr = addr; 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci return true; 5308c2ecf20Sopenharmony_ci} 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_cistatic const struct virtio_config_ops virtio_mmio_config_ops = { 5338c2ecf20Sopenharmony_ci .get = vm_get, 5348c2ecf20Sopenharmony_ci .set = vm_set, 5358c2ecf20Sopenharmony_ci .generation = vm_generation, 5368c2ecf20Sopenharmony_ci .get_status = vm_get_status, 5378c2ecf20Sopenharmony_ci .set_status = vm_set_status, 5388c2ecf20Sopenharmony_ci .reset = vm_reset, 5398c2ecf20Sopenharmony_ci .find_vqs = vm_find_vqs, 5408c2ecf20Sopenharmony_ci .del_vqs = vm_del_vqs, 5418c2ecf20Sopenharmony_ci .get_features = vm_get_features, 5428c2ecf20Sopenharmony_ci .finalize_features = vm_finalize_features, 5438c2ecf20Sopenharmony_ci .bus_name = vm_bus_name, 5448c2ecf20Sopenharmony_ci .get_shm_region = vm_get_shm_region, 5458c2ecf20Sopenharmony_ci}; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 5488c2ecf20Sopenharmony_cistatic int virtio_mmio_freeze(struct device *dev) 5498c2ecf20Sopenharmony_ci{ 5508c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = dev_get_drvdata(dev); 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_ci return virtio_device_freeze(&vm_dev->vdev); 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ci 5558c2ecf20Sopenharmony_cistatic int virtio_mmio_restore(struct device *dev) 5568c2ecf20Sopenharmony_ci{ 5578c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = dev_get_drvdata(dev); 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci if (vm_dev->version == 1) 5608c2ecf20Sopenharmony_ci writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); 5618c2ecf20Sopenharmony_ci 5628c2ecf20Sopenharmony_ci return virtio_device_restore(&vm_dev->vdev); 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic const struct dev_pm_ops virtio_mmio_pm_ops = { 5668c2ecf20Sopenharmony_ci SET_SYSTEM_SLEEP_PM_OPS(virtio_mmio_freeze, virtio_mmio_restore) 5678c2ecf20Sopenharmony_ci}; 5688c2ecf20Sopenharmony_ci#endif 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_cistatic void virtio_mmio_release_dev(struct device *_d) 5718c2ecf20Sopenharmony_ci{ 5728c2ecf20Sopenharmony_ci struct virtio_device *vdev = 5738c2ecf20Sopenharmony_ci container_of(_d, struct virtio_device, dev); 5748c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = to_virtio_mmio_device(vdev); 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci kfree(vm_dev); 5778c2ecf20Sopenharmony_ci} 5788c2ecf20Sopenharmony_ci 5798c2ecf20Sopenharmony_ci/* Platform device */ 5808c2ecf20Sopenharmony_ci 5818c2ecf20Sopenharmony_cistatic int virtio_mmio_probe(struct platform_device *pdev) 5828c2ecf20Sopenharmony_ci{ 5838c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev; 5848c2ecf20Sopenharmony_ci unsigned long magic; 5858c2ecf20Sopenharmony_ci int rc; 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci vm_dev = kzalloc(sizeof(*vm_dev), GFP_KERNEL); 5888c2ecf20Sopenharmony_ci if (!vm_dev) 5898c2ecf20Sopenharmony_ci return -ENOMEM; 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_ci vm_dev->vdev.dev.parent = &pdev->dev; 5928c2ecf20Sopenharmony_ci vm_dev->vdev.dev.release = virtio_mmio_release_dev; 5938c2ecf20Sopenharmony_ci vm_dev->vdev.config = &virtio_mmio_config_ops; 5948c2ecf20Sopenharmony_ci vm_dev->pdev = pdev; 5958c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&vm_dev->virtqueues); 5968c2ecf20Sopenharmony_ci spin_lock_init(&vm_dev->lock); 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci vm_dev->base = devm_platform_ioremap_resource(pdev, 0); 5998c2ecf20Sopenharmony_ci if (IS_ERR(vm_dev->base)) { 6008c2ecf20Sopenharmony_ci rc = PTR_ERR(vm_dev->base); 6018c2ecf20Sopenharmony_ci goto free_vm_dev; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_ci /* Check magic value */ 6058c2ecf20Sopenharmony_ci magic = readl(vm_dev->base + VIRTIO_MMIO_MAGIC_VALUE); 6068c2ecf20Sopenharmony_ci if (magic != ('v' | 'i' << 8 | 'r' << 16 | 't' << 24)) { 6078c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Wrong magic value 0x%08lx!\n", magic); 6088c2ecf20Sopenharmony_ci rc = -ENODEV; 6098c2ecf20Sopenharmony_ci goto free_vm_dev; 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci 6128c2ecf20Sopenharmony_ci /* Check device version */ 6138c2ecf20Sopenharmony_ci vm_dev->version = readl(vm_dev->base + VIRTIO_MMIO_VERSION); 6148c2ecf20Sopenharmony_ci if (vm_dev->version < 1 || vm_dev->version > 2) { 6158c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Version %ld not supported!\n", 6168c2ecf20Sopenharmony_ci vm_dev->version); 6178c2ecf20Sopenharmony_ci rc = -ENXIO; 6188c2ecf20Sopenharmony_ci goto free_vm_dev; 6198c2ecf20Sopenharmony_ci } 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ci vm_dev->vdev.id.device = readl(vm_dev->base + VIRTIO_MMIO_DEVICE_ID); 6228c2ecf20Sopenharmony_ci if (vm_dev->vdev.id.device == 0) { 6238c2ecf20Sopenharmony_ci /* 6248c2ecf20Sopenharmony_ci * virtio-mmio device with an ID 0 is a (dummy) placeholder 6258c2ecf20Sopenharmony_ci * with no function. End probing now with no error reported. 6268c2ecf20Sopenharmony_ci */ 6278c2ecf20Sopenharmony_ci rc = -ENODEV; 6288c2ecf20Sopenharmony_ci goto free_vm_dev; 6298c2ecf20Sopenharmony_ci } 6308c2ecf20Sopenharmony_ci vm_dev->vdev.id.vendor = readl(vm_dev->base + VIRTIO_MMIO_VENDOR_ID); 6318c2ecf20Sopenharmony_ci 6328c2ecf20Sopenharmony_ci if (vm_dev->version == 1) { 6338c2ecf20Sopenharmony_ci writel(PAGE_SIZE, vm_dev->base + VIRTIO_MMIO_GUEST_PAGE_SIZE); 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci rc = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)); 6368c2ecf20Sopenharmony_ci /* 6378c2ecf20Sopenharmony_ci * In the legacy case, ensure our coherently-allocated virtio 6388c2ecf20Sopenharmony_ci * ring will be at an address expressable as a 32-bit PFN. 6398c2ecf20Sopenharmony_ci */ 6408c2ecf20Sopenharmony_ci if (!rc) 6418c2ecf20Sopenharmony_ci dma_set_coherent_mask(&pdev->dev, 6428c2ecf20Sopenharmony_ci DMA_BIT_MASK(32 + PAGE_SHIFT)); 6438c2ecf20Sopenharmony_ci } else { 6448c2ecf20Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 6458c2ecf20Sopenharmony_ci } 6468c2ecf20Sopenharmony_ci if (rc) 6478c2ecf20Sopenharmony_ci rc = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 6488c2ecf20Sopenharmony_ci if (rc) 6498c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, "Failed to enable 64-bit or 32-bit DMA. Trying to continue, but this might not work.\n"); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci platform_set_drvdata(pdev, vm_dev); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci rc = register_virtio_device(&vm_dev->vdev); 6548c2ecf20Sopenharmony_ci if (rc) 6558c2ecf20Sopenharmony_ci put_device(&vm_dev->vdev.dev); 6568c2ecf20Sopenharmony_ci 6578c2ecf20Sopenharmony_ci return rc; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_cifree_vm_dev: 6608c2ecf20Sopenharmony_ci kfree(vm_dev); 6618c2ecf20Sopenharmony_ci return rc; 6628c2ecf20Sopenharmony_ci} 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_cistatic int virtio_mmio_remove(struct platform_device *pdev) 6658c2ecf20Sopenharmony_ci{ 6668c2ecf20Sopenharmony_ci struct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev); 6678c2ecf20Sopenharmony_ci unregister_virtio_device(&vm_dev->vdev); 6688c2ecf20Sopenharmony_ci 6698c2ecf20Sopenharmony_ci return 0; 6708c2ecf20Sopenharmony_ci} 6718c2ecf20Sopenharmony_ci 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci 6748c2ecf20Sopenharmony_ci/* Devices list parameter */ 6758c2ecf20Sopenharmony_ci 6768c2ecf20Sopenharmony_ci#if defined(CONFIG_VIRTIO_MMIO_CMDLINE_DEVICES) 6778c2ecf20Sopenharmony_ci 6788c2ecf20Sopenharmony_cistatic struct device vm_cmdline_parent = { 6798c2ecf20Sopenharmony_ci .init_name = "virtio-mmio-cmdline", 6808c2ecf20Sopenharmony_ci}; 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_cistatic int vm_cmdline_parent_registered; 6838c2ecf20Sopenharmony_cistatic int vm_cmdline_id; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_cistatic int vm_cmdline_set(const char *device, 6868c2ecf20Sopenharmony_ci const struct kernel_param *kp) 6878c2ecf20Sopenharmony_ci{ 6888c2ecf20Sopenharmony_ci int err; 6898c2ecf20Sopenharmony_ci struct resource resources[2] = {}; 6908c2ecf20Sopenharmony_ci char *str; 6918c2ecf20Sopenharmony_ci long long int base, size; 6928c2ecf20Sopenharmony_ci unsigned int irq; 6938c2ecf20Sopenharmony_ci int processed, consumed = 0; 6948c2ecf20Sopenharmony_ci struct platform_device *pdev; 6958c2ecf20Sopenharmony_ci 6968c2ecf20Sopenharmony_ci /* Consume "size" part of the command line parameter */ 6978c2ecf20Sopenharmony_ci size = memparse(device, &str); 6988c2ecf20Sopenharmony_ci 6998c2ecf20Sopenharmony_ci /* Get "@<base>:<irq>[:<id>]" chunks */ 7008c2ecf20Sopenharmony_ci processed = sscanf(str, "@%lli:%u%n:%d%n", 7018c2ecf20Sopenharmony_ci &base, &irq, &consumed, 7028c2ecf20Sopenharmony_ci &vm_cmdline_id, &consumed); 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci /* 7058c2ecf20Sopenharmony_ci * sscanf() must process at least 2 chunks; also there 7068c2ecf20Sopenharmony_ci * must be no extra characters after the last chunk, so 7078c2ecf20Sopenharmony_ci * str[consumed] must be '\0' 7088c2ecf20Sopenharmony_ci */ 7098c2ecf20Sopenharmony_ci if (processed < 2 || str[consumed] || irq == 0) 7108c2ecf20Sopenharmony_ci return -EINVAL; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci resources[0].flags = IORESOURCE_MEM; 7138c2ecf20Sopenharmony_ci resources[0].start = base; 7148c2ecf20Sopenharmony_ci resources[0].end = base + size - 1; 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci resources[1].flags = IORESOURCE_IRQ; 7178c2ecf20Sopenharmony_ci resources[1].start = resources[1].end = irq; 7188c2ecf20Sopenharmony_ci 7198c2ecf20Sopenharmony_ci if (!vm_cmdline_parent_registered) { 7208c2ecf20Sopenharmony_ci err = device_register(&vm_cmdline_parent); 7218c2ecf20Sopenharmony_ci if (err) { 7228c2ecf20Sopenharmony_ci put_device(&vm_cmdline_parent); 7238c2ecf20Sopenharmony_ci pr_err("Failed to register parent device!\n"); 7248c2ecf20Sopenharmony_ci return err; 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci vm_cmdline_parent_registered = 1; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci pr_info("Registering device virtio-mmio.%d at 0x%llx-0x%llx, IRQ %d.\n", 7308c2ecf20Sopenharmony_ci vm_cmdline_id, 7318c2ecf20Sopenharmony_ci (unsigned long long)resources[0].start, 7328c2ecf20Sopenharmony_ci (unsigned long long)resources[0].end, 7338c2ecf20Sopenharmony_ci (int)resources[1].start); 7348c2ecf20Sopenharmony_ci 7358c2ecf20Sopenharmony_ci pdev = platform_device_register_resndata(&vm_cmdline_parent, 7368c2ecf20Sopenharmony_ci "virtio-mmio", vm_cmdline_id++, 7378c2ecf20Sopenharmony_ci resources, ARRAY_SIZE(resources), NULL, 0); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(pdev); 7408c2ecf20Sopenharmony_ci} 7418c2ecf20Sopenharmony_ci 7428c2ecf20Sopenharmony_cistatic int vm_cmdline_get_device(struct device *dev, void *data) 7438c2ecf20Sopenharmony_ci{ 7448c2ecf20Sopenharmony_ci char *buffer = data; 7458c2ecf20Sopenharmony_ci unsigned int len = strlen(buffer); 7468c2ecf20Sopenharmony_ci struct platform_device *pdev = to_platform_device(dev); 7478c2ecf20Sopenharmony_ci 7488c2ecf20Sopenharmony_ci snprintf(buffer + len, PAGE_SIZE - len, "0x%llx@0x%llx:%llu:%d\n", 7498c2ecf20Sopenharmony_ci pdev->resource[0].end - pdev->resource[0].start + 1ULL, 7508c2ecf20Sopenharmony_ci (unsigned long long)pdev->resource[0].start, 7518c2ecf20Sopenharmony_ci (unsigned long long)pdev->resource[1].start, 7528c2ecf20Sopenharmony_ci pdev->id); 7538c2ecf20Sopenharmony_ci return 0; 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic int vm_cmdline_get(char *buffer, const struct kernel_param *kp) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci buffer[0] = '\0'; 7598c2ecf20Sopenharmony_ci device_for_each_child(&vm_cmdline_parent, buffer, 7608c2ecf20Sopenharmony_ci vm_cmdline_get_device); 7618c2ecf20Sopenharmony_ci return strlen(buffer) + 1; 7628c2ecf20Sopenharmony_ci} 7638c2ecf20Sopenharmony_ci 7648c2ecf20Sopenharmony_cistatic const struct kernel_param_ops vm_cmdline_param_ops = { 7658c2ecf20Sopenharmony_ci .set = vm_cmdline_set, 7668c2ecf20Sopenharmony_ci .get = vm_cmdline_get, 7678c2ecf20Sopenharmony_ci}; 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_cidevice_param_cb(device, &vm_cmdline_param_ops, NULL, S_IRUSR); 7708c2ecf20Sopenharmony_ci 7718c2ecf20Sopenharmony_cistatic int vm_unregister_cmdline_device(struct device *dev, 7728c2ecf20Sopenharmony_ci void *data) 7738c2ecf20Sopenharmony_ci{ 7748c2ecf20Sopenharmony_ci platform_device_unregister(to_platform_device(dev)); 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci return 0; 7778c2ecf20Sopenharmony_ci} 7788c2ecf20Sopenharmony_ci 7798c2ecf20Sopenharmony_cistatic void vm_unregister_cmdline_devices(void) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci if (vm_cmdline_parent_registered) { 7828c2ecf20Sopenharmony_ci device_for_each_child(&vm_cmdline_parent, NULL, 7838c2ecf20Sopenharmony_ci vm_unregister_cmdline_device); 7848c2ecf20Sopenharmony_ci device_unregister(&vm_cmdline_parent); 7858c2ecf20Sopenharmony_ci vm_cmdline_parent_registered = 0; 7868c2ecf20Sopenharmony_ci } 7878c2ecf20Sopenharmony_ci} 7888c2ecf20Sopenharmony_ci 7898c2ecf20Sopenharmony_ci#else 7908c2ecf20Sopenharmony_ci 7918c2ecf20Sopenharmony_cistatic void vm_unregister_cmdline_devices(void) 7928c2ecf20Sopenharmony_ci{ 7938c2ecf20Sopenharmony_ci} 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_ci#endif 7968c2ecf20Sopenharmony_ci 7978c2ecf20Sopenharmony_ci/* Platform driver */ 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic const struct of_device_id virtio_mmio_match[] = { 8008c2ecf20Sopenharmony_ci { .compatible = "virtio,mmio", }, 8018c2ecf20Sopenharmony_ci {}, 8028c2ecf20Sopenharmony_ci}; 8038c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, virtio_mmio_match); 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 8068c2ecf20Sopenharmony_cistatic const struct acpi_device_id virtio_mmio_acpi_match[] = { 8078c2ecf20Sopenharmony_ci { "LNRO0005", }, 8088c2ecf20Sopenharmony_ci { } 8098c2ecf20Sopenharmony_ci}; 8108c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(acpi, virtio_mmio_acpi_match); 8118c2ecf20Sopenharmony_ci#endif 8128c2ecf20Sopenharmony_ci 8138c2ecf20Sopenharmony_cistatic struct platform_driver virtio_mmio_driver = { 8148c2ecf20Sopenharmony_ci .probe = virtio_mmio_probe, 8158c2ecf20Sopenharmony_ci .remove = virtio_mmio_remove, 8168c2ecf20Sopenharmony_ci .driver = { 8178c2ecf20Sopenharmony_ci .name = "virtio-mmio", 8188c2ecf20Sopenharmony_ci .of_match_table = virtio_mmio_match, 8198c2ecf20Sopenharmony_ci .acpi_match_table = ACPI_PTR(virtio_mmio_acpi_match), 8208c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 8218c2ecf20Sopenharmony_ci .pm = &virtio_mmio_pm_ops, 8228c2ecf20Sopenharmony_ci#endif 8238c2ecf20Sopenharmony_ci }, 8248c2ecf20Sopenharmony_ci}; 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic int __init virtio_mmio_init(void) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci return platform_driver_register(&virtio_mmio_driver); 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic void __exit virtio_mmio_exit(void) 8328c2ecf20Sopenharmony_ci{ 8338c2ecf20Sopenharmony_ci platform_driver_unregister(&virtio_mmio_driver); 8348c2ecf20Sopenharmony_ci vm_unregister_cmdline_devices(); 8358c2ecf20Sopenharmony_ci} 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_cimodule_init(virtio_mmio_init); 8388c2ecf20Sopenharmony_cimodule_exit(virtio_mmio_exit); 8398c2ecf20Sopenharmony_ci 8408c2ecf20Sopenharmony_ciMODULE_AUTHOR("Pawel Moll <pawel.moll@arm.com>"); 8418c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Platform bus driver for memory mapped virtio devices"); 8428c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 843