1fd502729SJason Wang // SPDX-License-Identifier: GPL-2.0-or-later 2fd502729SJason Wang 3fd502729SJason Wang #include <linux/virtio_pci_modern.h> 4fd502729SJason Wang #include <linux/module.h> 5fd502729SJason Wang #include <linux/pci.h> 6fd502729SJason Wang 7fd502729SJason Wang /* 8fd502729SJason Wang * vp_modern_map_capability - map a part of virtio pci capability 9fd502729SJason Wang * @mdev: the modern virtio-pci device 10fd502729SJason Wang * @off: offset of the capability 11fd502729SJason Wang * @minlen: minimal length of the capability 12fd502729SJason Wang * @align: align requirement 13fd502729SJason Wang * @start: start from the capability 14fd502729SJason Wang * @size: map size 15fd502729SJason Wang * @len: the length that is actually mapped 169e311bcaSJason Wang * @pa: physical address of the capability 17fd502729SJason Wang * 18fd502729SJason Wang * Returns the io address of for the part of the capability 19fd502729SJason Wang */ 20fd466b36SJason Wang static void __iomem * 21fd466b36SJason Wang vp_modern_map_capability(struct virtio_pci_modern_device *mdev, int off, 22fd466b36SJason Wang size_t minlen, u32 align, u32 start, u32 size, 239e311bcaSJason Wang size_t *len, resource_size_t *pa) 24fd502729SJason Wang { 25fd502729SJason Wang struct pci_dev *dev = mdev->pci_dev; 26fd502729SJason Wang u8 bar; 27fd502729SJason Wang u32 offset, length; 28fd502729SJason Wang void __iomem *p; 29fd502729SJason Wang 30fd502729SJason Wang pci_read_config_byte(dev, off + offsetof(struct virtio_pci_cap, 31fd502729SJason Wang bar), 32fd502729SJason Wang &bar); 33fd502729SJason Wang pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, offset), 34fd502729SJason Wang &offset); 35fd502729SJason Wang pci_read_config_dword(dev, off + offsetof(struct virtio_pci_cap, length), 36fd502729SJason Wang &length); 37fd502729SJason Wang 38fd502729SJason Wang if (length <= start) { 39fd502729SJason Wang dev_err(&dev->dev, 40fd502729SJason Wang "virtio_pci: bad capability len %u (>%u expected)\n", 41fd502729SJason Wang length, start); 42fd502729SJason Wang return NULL; 43fd502729SJason Wang } 44fd502729SJason Wang 45fd502729SJason Wang if (length - start < minlen) { 46fd502729SJason Wang dev_err(&dev->dev, 47fd502729SJason Wang "virtio_pci: bad capability len %u (>=%zu expected)\n", 48fd502729SJason Wang length, minlen); 49fd502729SJason Wang return NULL; 50fd502729SJason Wang } 51fd502729SJason Wang 52fd502729SJason Wang length -= start; 53fd502729SJason Wang 54fd502729SJason Wang if (start + offset < offset) { 55fd502729SJason Wang dev_err(&dev->dev, 56fd502729SJason Wang "virtio_pci: map wrap-around %u+%u\n", 57fd502729SJason Wang start, offset); 58fd502729SJason Wang return NULL; 59fd502729SJason Wang } 60fd502729SJason Wang 61fd502729SJason Wang offset += start; 62fd502729SJason Wang 63fd502729SJason Wang if (offset & (align - 1)) { 64fd502729SJason Wang dev_err(&dev->dev, 65fd502729SJason Wang "virtio_pci: offset %u not aligned to %u\n", 66fd502729SJason Wang offset, align); 67fd502729SJason Wang return NULL; 68fd502729SJason Wang } 69fd502729SJason Wang 70fd502729SJason Wang if (length > size) 71fd502729SJason Wang length = size; 72fd502729SJason Wang 73fd502729SJason Wang if (len) 74fd502729SJason Wang *len = length; 75fd502729SJason Wang 76fd502729SJason Wang if (minlen + offset < minlen || 77fd502729SJason Wang minlen + offset > pci_resource_len(dev, bar)) { 78fd502729SJason Wang dev_err(&dev->dev, 79fd502729SJason Wang "virtio_pci: map virtio %zu@%u " 80fd502729SJason Wang "out of range on bar %i length %lu\n", 81fd502729SJason Wang minlen, offset, 82fd502729SJason Wang bar, (unsigned long)pci_resource_len(dev, bar)); 83fd502729SJason Wang return NULL; 84fd502729SJason Wang } 85fd502729SJason Wang 86fd502729SJason Wang p = pci_iomap_range(dev, bar, offset, length); 87fd502729SJason Wang if (!p) 88fd502729SJason Wang dev_err(&dev->dev, 89fd502729SJason Wang "virtio_pci: unable to map virtio %u@%u on bar %i\n", 90fd502729SJason Wang length, offset, bar); 919e311bcaSJason Wang else if (pa) 929e311bcaSJason Wang *pa = pci_resource_start(dev, bar) + offset; 939e311bcaSJason Wang 94fd502729SJason Wang return p; 95fd502729SJason Wang } 96fd502729SJason Wang 97fd502729SJason Wang /** 98fd502729SJason Wang * virtio_pci_find_capability - walk capabilities to find device info. 99fd502729SJason Wang * @dev: the pci device 100fd502729SJason Wang * @cfg_type: the VIRTIO_PCI_CAP_* value we seek 101fd502729SJason Wang * @ioresource_types: IORESOURCE_MEM and/or IORESOURCE_IO. 102fd502729SJason Wang * @bars: the bitmask of BARs 103fd502729SJason Wang * 104fd502729SJason Wang * Returns offset of the capability, or 0. 105fd502729SJason Wang */ 106fd502729SJason Wang static inline int virtio_pci_find_capability(struct pci_dev *dev, u8 cfg_type, 107fd502729SJason Wang u32 ioresource_types, int *bars) 108fd502729SJason Wang { 109fd502729SJason Wang int pos; 110fd502729SJason Wang 111fd502729SJason Wang for (pos = pci_find_capability(dev, PCI_CAP_ID_VNDR); 112fd502729SJason Wang pos > 0; 113fd502729SJason Wang pos = pci_find_next_capability(dev, pos, PCI_CAP_ID_VNDR)) { 114fd502729SJason Wang u8 type, bar; 115fd502729SJason Wang pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 116fd502729SJason Wang cfg_type), 117fd502729SJason Wang &type); 118fd502729SJason Wang pci_read_config_byte(dev, pos + offsetof(struct virtio_pci_cap, 119fd502729SJason Wang bar), 120fd502729SJason Wang &bar); 121fd502729SJason Wang 122fd502729SJason Wang /* Ignore structures with reserved BAR values */ 123fd502729SJason Wang if (bar > 0x5) 124fd502729SJason Wang continue; 125fd502729SJason Wang 126fd502729SJason Wang if (type == cfg_type) { 127fd502729SJason Wang if (pci_resource_len(dev, bar) && 128fd502729SJason Wang pci_resource_flags(dev, bar) & ioresource_types) { 129fd502729SJason Wang *bars |= (1 << bar); 130fd502729SJason Wang return pos; 131fd502729SJason Wang } 132fd502729SJason Wang } 133fd502729SJason Wang } 134fd502729SJason Wang return 0; 135fd502729SJason Wang } 136fd502729SJason Wang 137fd502729SJason Wang /* This is part of the ABI. Don't screw with it. */ 138fd502729SJason Wang static inline void check_offsets(void) 139fd502729SJason Wang { 140fd502729SJason Wang /* Note: disk space was harmed in compilation of this function. */ 141fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_CAP_VNDR != 142fd502729SJason Wang offsetof(struct virtio_pci_cap, cap_vndr)); 143fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_CAP_NEXT != 144fd502729SJason Wang offsetof(struct virtio_pci_cap, cap_next)); 145fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_CAP_LEN != 146fd502729SJason Wang offsetof(struct virtio_pci_cap, cap_len)); 147fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_CAP_CFG_TYPE != 148fd502729SJason Wang offsetof(struct virtio_pci_cap, cfg_type)); 149fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_CAP_BAR != 150fd502729SJason Wang offsetof(struct virtio_pci_cap, bar)); 151fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_CAP_OFFSET != 152fd502729SJason Wang offsetof(struct virtio_pci_cap, offset)); 153fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_CAP_LENGTH != 154fd502729SJason Wang offsetof(struct virtio_pci_cap, length)); 155fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_NOTIFY_CAP_MULT != 156fd502729SJason Wang offsetof(struct virtio_pci_notify_cap, 157fd502729SJason Wang notify_off_multiplier)); 158fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_DFSELECT != 159fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, 160fd502729SJason Wang device_feature_select)); 161fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_DF != 162fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, device_feature)); 163fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_GFSELECT != 164fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, 165fd502729SJason Wang guest_feature_select)); 166fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_GF != 167fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, guest_feature)); 168fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_MSIX != 169fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, msix_config)); 170fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_NUMQ != 171fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, num_queues)); 172fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_STATUS != 173fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, device_status)); 174fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_CFGGENERATION != 175fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, config_generation)); 176fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_SELECT != 177fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_select)); 178fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_SIZE != 179fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_size)); 180fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_MSIX != 181fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_msix_vector)); 182fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_ENABLE != 183fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_enable)); 184fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_NOFF != 185fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_notify_off)); 186fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_DESCLO != 187fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_desc_lo)); 188fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_DESCHI != 189fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_desc_hi)); 190fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_AVAILLO != 191fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_avail_lo)); 192fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_AVAILHI != 193fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_avail_hi)); 194fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_USEDLO != 195fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_used_lo)); 196fd502729SJason Wang BUILD_BUG_ON(VIRTIO_PCI_COMMON_Q_USEDHI != 197fd502729SJason Wang offsetof(struct virtio_pci_common_cfg, queue_used_hi)); 198fd502729SJason Wang } 199fd502729SJason Wang 200fd502729SJason Wang /* 201fd502729SJason Wang * vp_modern_probe: probe the modern virtio pci device, note that the 202fd502729SJason Wang * caller is required to enable PCI device before calling this function. 203fd502729SJason Wang * @mdev: the modern virtio-pci device 204fd502729SJason Wang * 205fd502729SJason Wang * Return 0 on succeed otherwise fail 206fd502729SJason Wang */ 207fd502729SJason Wang int vp_modern_probe(struct virtio_pci_modern_device *mdev) 208fd502729SJason Wang { 209fd502729SJason Wang struct pci_dev *pci_dev = mdev->pci_dev; 210fd502729SJason Wang int err, common, isr, notify, device; 211fd502729SJason Wang u32 notify_length; 212fd502729SJason Wang u32 notify_offset; 213fd502729SJason Wang 214fd502729SJason Wang check_offsets(); 215fd502729SJason Wang 216fd502729SJason Wang mdev->pci_dev = pci_dev; 217fd502729SJason Wang 218fd502729SJason Wang /* We only own devices >= 0x1000 and <= 0x107f: leave the rest. */ 219fd502729SJason Wang if (pci_dev->device < 0x1000 || pci_dev->device > 0x107f) 220fd502729SJason Wang return -ENODEV; 221fd502729SJason Wang 222fd502729SJason Wang if (pci_dev->device < 0x1040) { 223fd502729SJason Wang /* Transitional devices: use the PCI subsystem device id as 224fd502729SJason Wang * virtio device id, same as legacy driver always did. 225fd502729SJason Wang */ 226fd502729SJason Wang mdev->id.device = pci_dev->subsystem_device; 227fd502729SJason Wang } else { 228fd502729SJason Wang /* Modern devices: simply use PCI device id, but start from 0x1040. */ 229fd502729SJason Wang mdev->id.device = pci_dev->device - 0x1040; 230fd502729SJason Wang } 231fd502729SJason Wang mdev->id.vendor = pci_dev->subsystem_vendor; 232fd502729SJason Wang 233fd502729SJason Wang /* check for a common config: if not, use legacy mode (bar 0). */ 234fd502729SJason Wang common = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_COMMON_CFG, 235fd502729SJason Wang IORESOURCE_IO | IORESOURCE_MEM, 236fd502729SJason Wang &mdev->modern_bars); 237fd502729SJason Wang if (!common) { 238fd502729SJason Wang dev_info(&pci_dev->dev, 239fd502729SJason Wang "virtio_pci: leaving for legacy driver\n"); 240fd502729SJason Wang return -ENODEV; 241fd502729SJason Wang } 242fd502729SJason Wang 243fd502729SJason Wang /* If common is there, these should be too... */ 244fd502729SJason Wang isr = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_ISR_CFG, 245fd502729SJason Wang IORESOURCE_IO | IORESOURCE_MEM, 246fd502729SJason Wang &mdev->modern_bars); 247fd502729SJason Wang notify = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_NOTIFY_CFG, 248fd502729SJason Wang IORESOURCE_IO | IORESOURCE_MEM, 249fd502729SJason Wang &mdev->modern_bars); 250fd502729SJason Wang if (!isr || !notify) { 251fd502729SJason Wang dev_err(&pci_dev->dev, 252fd502729SJason Wang "virtio_pci: missing capabilities %i/%i/%i\n", 253fd502729SJason Wang common, isr, notify); 254fd502729SJason Wang return -EINVAL; 255fd502729SJason Wang } 256fd502729SJason Wang 257fd502729SJason Wang err = dma_set_mask_and_coherent(&pci_dev->dev, DMA_BIT_MASK(64)); 258fd502729SJason Wang if (err) 259fd502729SJason Wang err = dma_set_mask_and_coherent(&pci_dev->dev, 260fd502729SJason Wang DMA_BIT_MASK(32)); 261fd502729SJason Wang if (err) 262fd502729SJason Wang dev_warn(&pci_dev->dev, "Failed to enable 64-bit or 32-bit DMA. Trying to continue, but this might not work.\n"); 263fd502729SJason Wang 264fd502729SJason Wang /* Device capability is only mandatory for devices that have 265fd502729SJason Wang * device-specific configuration. 266fd502729SJason Wang */ 267fd502729SJason Wang device = virtio_pci_find_capability(pci_dev, VIRTIO_PCI_CAP_DEVICE_CFG, 268fd502729SJason Wang IORESOURCE_IO | IORESOURCE_MEM, 269fd502729SJason Wang &mdev->modern_bars); 270fd502729SJason Wang 271fd502729SJason Wang err = pci_request_selected_regions(pci_dev, mdev->modern_bars, 272fd502729SJason Wang "virtio-pci-modern"); 273fd502729SJason Wang if (err) 274fd502729SJason Wang return err; 275fd502729SJason Wang 276fd502729SJason Wang err = -EINVAL; 277fd502729SJason Wang mdev->common = vp_modern_map_capability(mdev, common, 278fd502729SJason Wang sizeof(struct virtio_pci_common_cfg), 4, 279fd502729SJason Wang 0, sizeof(struct virtio_pci_common_cfg), 2809e311bcaSJason Wang NULL, NULL); 281fd502729SJason Wang if (!mdev->common) 282fd502729SJason Wang goto err_map_common; 283fd502729SJason Wang mdev->isr = vp_modern_map_capability(mdev, isr, sizeof(u8), 1, 284fd502729SJason Wang 0, 1, 2859e311bcaSJason Wang NULL, NULL); 286fd502729SJason Wang if (!mdev->isr) 287fd502729SJason Wang goto err_map_isr; 288fd502729SJason Wang 289fd502729SJason Wang /* Read notify_off_multiplier from config space. */ 290fd502729SJason Wang pci_read_config_dword(pci_dev, 291fd502729SJason Wang notify + offsetof(struct virtio_pci_notify_cap, 292fd502729SJason Wang notify_off_multiplier), 293fd502729SJason Wang &mdev->notify_offset_multiplier); 294fd502729SJason Wang /* Read notify length and offset from config space. */ 295fd502729SJason Wang pci_read_config_dword(pci_dev, 296fd502729SJason Wang notify + offsetof(struct virtio_pci_notify_cap, 297fd502729SJason Wang cap.length), 298fd502729SJason Wang ¬ify_length); 299fd502729SJason Wang 300fd502729SJason Wang pci_read_config_dword(pci_dev, 301fd502729SJason Wang notify + offsetof(struct virtio_pci_notify_cap, 302fd502729SJason Wang cap.offset), 303fd502729SJason Wang ¬ify_offset); 304fd502729SJason Wang 305fd502729SJason Wang /* We don't know how many VQs we'll map, ahead of the time. 306fd502729SJason Wang * If notify length is small, map it all now. 307fd502729SJason Wang * Otherwise, map each VQ individually later. 308fd502729SJason Wang */ 309fd502729SJason Wang if ((u64)notify_length + (notify_offset % PAGE_SIZE) <= PAGE_SIZE) { 310fd502729SJason Wang mdev->notify_base = vp_modern_map_capability(mdev, notify, 311fd502729SJason Wang 2, 2, 312fd502729SJason Wang 0, notify_length, 3139e311bcaSJason Wang &mdev->notify_len, 3149e311bcaSJason Wang &mdev->notify_pa); 315fd502729SJason Wang if (!mdev->notify_base) 316fd502729SJason Wang goto err_map_notify; 317fd502729SJason Wang } else { 318fd502729SJason Wang mdev->notify_map_cap = notify; 319fd502729SJason Wang } 320fd502729SJason Wang 321fd502729SJason Wang /* Again, we don't know how much we should map, but PAGE_SIZE 322fd502729SJason Wang * is more than enough for all existing devices. 323fd502729SJason Wang */ 324fd502729SJason Wang if (device) { 325fd502729SJason Wang mdev->device = vp_modern_map_capability(mdev, device, 0, 4, 326fd502729SJason Wang 0, PAGE_SIZE, 3279e311bcaSJason Wang &mdev->device_len, 3289e311bcaSJason Wang NULL); 329fd502729SJason Wang if (!mdev->device) 330fd502729SJason Wang goto err_map_device; 331fd502729SJason Wang } 332fd502729SJason Wang 333fd502729SJason Wang return 0; 334fd502729SJason Wang 335fd502729SJason Wang err_map_device: 336fd502729SJason Wang if (mdev->notify_base) 337fd502729SJason Wang pci_iounmap(pci_dev, mdev->notify_base); 338fd502729SJason Wang err_map_notify: 339fd502729SJason Wang pci_iounmap(pci_dev, mdev->isr); 340fd502729SJason Wang err_map_isr: 341fd502729SJason Wang pci_iounmap(pci_dev, mdev->common); 342fd502729SJason Wang err_map_common: 343fd502729SJason Wang return err; 344fd502729SJason Wang } 345fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_probe); 346fd502729SJason Wang 347fd502729SJason Wang /* 348*2b68224eSDapeng Mi * vp_modern_remove: remove and cleanup the modern virtio pci device 349fd502729SJason Wang * @mdev: the modern virtio-pci device 350fd502729SJason Wang */ 351fd502729SJason Wang void vp_modern_remove(struct virtio_pci_modern_device *mdev) 352fd502729SJason Wang { 353fd502729SJason Wang struct pci_dev *pci_dev = mdev->pci_dev; 354fd502729SJason Wang 355fd502729SJason Wang if (mdev->device) 356fd502729SJason Wang pci_iounmap(pci_dev, mdev->device); 357fd502729SJason Wang if (mdev->notify_base) 358fd502729SJason Wang pci_iounmap(pci_dev, mdev->notify_base); 359fd502729SJason Wang pci_iounmap(pci_dev, mdev->isr); 360fd502729SJason Wang pci_iounmap(pci_dev, mdev->common); 361fd502729SJason Wang pci_release_selected_regions(pci_dev, mdev->modern_bars); 362fd502729SJason Wang } 363fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_remove); 364fd502729SJason Wang 365fd502729SJason Wang /* 366fd502729SJason Wang * vp_modern_get_features - get features from device 367fd502729SJason Wang * @mdev: the modern virtio-pci device 368fd502729SJason Wang * 369fd502729SJason Wang * Returns the features read from the device 370fd502729SJason Wang */ 371fd502729SJason Wang u64 vp_modern_get_features(struct virtio_pci_modern_device *mdev) 372fd502729SJason Wang { 373fd502729SJason Wang struct virtio_pci_common_cfg __iomem *cfg = mdev->common; 374fd502729SJason Wang 375fd502729SJason Wang u64 features; 376fd502729SJason Wang 377fd502729SJason Wang vp_iowrite32(0, &cfg->device_feature_select); 378fd502729SJason Wang features = vp_ioread32(&cfg->device_feature); 379fd502729SJason Wang vp_iowrite32(1, &cfg->device_feature_select); 380fd502729SJason Wang features |= ((u64)vp_ioread32(&cfg->device_feature) << 32); 381fd502729SJason Wang 382fd502729SJason Wang return features; 383fd502729SJason Wang } 384fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_get_features); 385fd502729SJason Wang 386fd502729SJason Wang /* 3870140b3d0SJason Wang * vp_modern_get_driver_features - get driver features from device 3880140b3d0SJason Wang * @mdev: the modern virtio-pci device 3890140b3d0SJason Wang * 3900140b3d0SJason Wang * Returns the driver features read from the device 3910140b3d0SJason Wang */ 3920140b3d0SJason Wang u64 vp_modern_get_driver_features(struct virtio_pci_modern_device *mdev) 3930140b3d0SJason Wang { 3940140b3d0SJason Wang struct virtio_pci_common_cfg __iomem *cfg = mdev->common; 3950140b3d0SJason Wang 3960140b3d0SJason Wang u64 features; 3970140b3d0SJason Wang 3980140b3d0SJason Wang vp_iowrite32(0, &cfg->guest_feature_select); 3990140b3d0SJason Wang features = vp_ioread32(&cfg->guest_feature); 4000140b3d0SJason Wang vp_iowrite32(1, &cfg->guest_feature_select); 4010140b3d0SJason Wang features |= ((u64)vp_ioread32(&cfg->guest_feature) << 32); 4020140b3d0SJason Wang 4030140b3d0SJason Wang return features; 4040140b3d0SJason Wang } 4050140b3d0SJason Wang EXPORT_SYMBOL_GPL(vp_modern_get_driver_features); 4060140b3d0SJason Wang 4070140b3d0SJason Wang /* 408fd502729SJason Wang * vp_modern_set_features - set features to device 409fd502729SJason Wang * @mdev: the modern virtio-pci device 410fd502729SJason Wang * @features: the features set to device 411fd502729SJason Wang */ 412fd502729SJason Wang void vp_modern_set_features(struct virtio_pci_modern_device *mdev, 413fd502729SJason Wang u64 features) 414fd502729SJason Wang { 415fd502729SJason Wang struct virtio_pci_common_cfg __iomem *cfg = mdev->common; 416fd502729SJason Wang 417fd502729SJason Wang vp_iowrite32(0, &cfg->guest_feature_select); 418fd502729SJason Wang vp_iowrite32((u32)features, &cfg->guest_feature); 419fd502729SJason Wang vp_iowrite32(1, &cfg->guest_feature_select); 420fd502729SJason Wang vp_iowrite32(features >> 32, &cfg->guest_feature); 421fd502729SJason Wang } 422fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_set_features); 423fd502729SJason Wang 424fd502729SJason Wang /* 425fd502729SJason Wang * vp_modern_generation - get the device genreation 426fd502729SJason Wang * @mdev: the modern virtio-pci device 427fd502729SJason Wang * 428fd502729SJason Wang * Returns the genreation read from device 429fd502729SJason Wang */ 430fd502729SJason Wang u32 vp_modern_generation(struct virtio_pci_modern_device *mdev) 431fd502729SJason Wang { 432fd502729SJason Wang struct virtio_pci_common_cfg __iomem *cfg = mdev->common; 433fd502729SJason Wang 434fd502729SJason Wang return vp_ioread8(&cfg->config_generation); 435fd502729SJason Wang } 436fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_generation); 437fd502729SJason Wang 438fd502729SJason Wang /* 439fd502729SJason Wang * vp_modern_get_status - get the device status 440fd502729SJason Wang * @mdev: the modern virtio-pci device 441fd502729SJason Wang * 442fd502729SJason Wang * Returns the status read from device 443fd502729SJason Wang */ 444fd502729SJason Wang u8 vp_modern_get_status(struct virtio_pci_modern_device *mdev) 445fd502729SJason Wang { 446fd502729SJason Wang struct virtio_pci_common_cfg __iomem *cfg = mdev->common; 447fd502729SJason Wang 448fd502729SJason Wang return vp_ioread8(&cfg->device_status); 449fd502729SJason Wang } 450fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_get_status); 451fd502729SJason Wang 452fd502729SJason Wang /* 453fd502729SJason Wang * vp_modern_set_status - set status to device 454fd502729SJason Wang * @mdev: the modern virtio-pci device 455fd502729SJason Wang * @status: the status set to device 456fd502729SJason Wang */ 457fd502729SJason Wang void vp_modern_set_status(struct virtio_pci_modern_device *mdev, 458fd502729SJason Wang u8 status) 459fd502729SJason Wang { 460fd502729SJason Wang struct virtio_pci_common_cfg __iomem *cfg = mdev->common; 461fd502729SJason Wang 462fd502729SJason Wang vp_iowrite8(status, &cfg->device_status); 463fd502729SJason Wang } 464fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_set_status); 465fd502729SJason Wang 466fd502729SJason Wang /* 467fd502729SJason Wang * vp_modern_queue_vector - set the MSIX vector for a specific virtqueue 468fd502729SJason Wang * @mdev: the modern virtio-pci device 469fd502729SJason Wang * @index: queue index 470fd502729SJason Wang * @vector: the config vector 471fd502729SJason Wang * 472fd502729SJason Wang * Returns the config vector read from the device 473fd502729SJason Wang */ 474fd502729SJason Wang u16 vp_modern_queue_vector(struct virtio_pci_modern_device *mdev, 475fd502729SJason Wang u16 index, u16 vector) 476fd502729SJason Wang { 477fd502729SJason Wang struct virtio_pci_common_cfg __iomem *cfg = mdev->common; 478fd502729SJason Wang 479fd502729SJason Wang vp_iowrite16(index, &cfg->queue_select); 480fd502729SJason Wang vp_iowrite16(vector, &cfg->queue_msix_vector); 481fd502729SJason Wang /* Flush the write out to device */ 482fd502729SJason Wang return vp_ioread16(&cfg->queue_msix_vector); 483fd502729SJason Wang } 484fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_queue_vector); 485fd502729SJason Wang 486fd502729SJason Wang /* 487fd502729SJason Wang * vp_modern_config_vector - set the vector for config interrupt 488fd502729SJason Wang * @mdev: the modern virtio-pci device 489fd502729SJason Wang * @vector: the config vector 490fd502729SJason Wang * 491fd502729SJason Wang * Returns the config vector read from the device 492fd502729SJason Wang */ 493fd502729SJason Wang u16 vp_modern_config_vector(struct virtio_pci_modern_device *mdev, 494fd502729SJason Wang u16 vector) 495fd502729SJason Wang { 496fd502729SJason Wang struct virtio_pci_common_cfg __iomem *cfg = mdev->common; 497fd502729SJason Wang 498fd502729SJason Wang /* Setup the vector used for configuration events */ 499fd502729SJason Wang vp_iowrite16(vector, &cfg->msix_config); 500fd502729SJason Wang /* Verify we had enough resources to assign the vector */ 501fd502729SJason Wang /* Will also flush the write out to device */ 502fd502729SJason Wang return vp_ioread16(&cfg->msix_config); 503fd502729SJason Wang } 504fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_config_vector); 505fd502729SJason Wang 506fd502729SJason Wang /* 507fd502729SJason Wang * vp_modern_queue_address - set the virtqueue address 508fd502729SJason Wang * @mdev: the modern virtio-pci device 509fd502729SJason Wang * @index: the queue index 510fd502729SJason Wang * @desc_addr: address of the descriptor area 511fd502729SJason Wang * @driver_addr: address of the driver area 512fd502729SJason Wang * @device_addr: address of the device area 513fd502729SJason Wang */ 514fd502729SJason Wang void vp_modern_queue_address(struct virtio_pci_modern_device *mdev, 515fd502729SJason Wang u16 index, u64 desc_addr, u64 driver_addr, 516fd502729SJason Wang u64 device_addr) 517fd502729SJason Wang { 518fd502729SJason Wang struct virtio_pci_common_cfg __iomem *cfg = mdev->common; 519fd502729SJason Wang 520fd502729SJason Wang vp_iowrite16(index, &cfg->queue_select); 521fd502729SJason Wang 522fd502729SJason Wang vp_iowrite64_twopart(desc_addr, &cfg->queue_desc_lo, 523fd502729SJason Wang &cfg->queue_desc_hi); 524fd502729SJason Wang vp_iowrite64_twopart(driver_addr, &cfg->queue_avail_lo, 525fd502729SJason Wang &cfg->queue_avail_hi); 526fd502729SJason Wang vp_iowrite64_twopart(device_addr, &cfg->queue_used_lo, 527fd502729SJason Wang &cfg->queue_used_hi); 528fd502729SJason Wang } 529fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_queue_address); 530fd502729SJason Wang 531fd502729SJason Wang /* 532fd502729SJason Wang * vp_modern_set_queue_enable - enable a virtqueue 533fd502729SJason Wang * @mdev: the modern virtio-pci device 534fd502729SJason Wang * @index: the queue index 535fd502729SJason Wang * @enable: whether the virtqueue is enable or not 536fd502729SJason Wang */ 537fd502729SJason Wang void vp_modern_set_queue_enable(struct virtio_pci_modern_device *mdev, 538fd502729SJason Wang u16 index, bool enable) 539fd502729SJason Wang { 540fd502729SJason Wang vp_iowrite16(index, &mdev->common->queue_select); 541fd502729SJason Wang vp_iowrite16(enable, &mdev->common->queue_enable); 542fd502729SJason Wang } 543fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_set_queue_enable); 544fd502729SJason Wang 545fd502729SJason Wang /* 546fd502729SJason Wang * vp_modern_get_queue_enable - enable a virtqueue 547fd502729SJason Wang * @mdev: the modern virtio-pci device 548fd502729SJason Wang * @index: the queue index 549fd502729SJason Wang * 550fd502729SJason Wang * Returns whether a virtqueue is enabled or not 551fd502729SJason Wang */ 552fd502729SJason Wang bool vp_modern_get_queue_enable(struct virtio_pci_modern_device *mdev, 553fd502729SJason Wang u16 index) 554fd502729SJason Wang { 555fd502729SJason Wang vp_iowrite16(index, &mdev->common->queue_select); 556fd502729SJason Wang 557fd502729SJason Wang return vp_ioread16(&mdev->common->queue_enable); 558fd502729SJason Wang } 559fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_get_queue_enable); 560fd502729SJason Wang 561fd502729SJason Wang /* 562fd502729SJason Wang * vp_modern_set_queue_size - set size for a virtqueue 563fd502729SJason Wang * @mdev: the modern virtio-pci device 564fd502729SJason Wang * @index: the queue index 565fd502729SJason Wang * @size: the size of the virtqueue 566fd502729SJason Wang */ 567fd502729SJason Wang void vp_modern_set_queue_size(struct virtio_pci_modern_device *mdev, 568fd502729SJason Wang u16 index, u16 size) 569fd502729SJason Wang { 570fd502729SJason Wang vp_iowrite16(index, &mdev->common->queue_select); 571fd502729SJason Wang vp_iowrite16(size, &mdev->common->queue_size); 572fd502729SJason Wang 573fd502729SJason Wang } 574fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_set_queue_size); 575fd502729SJason Wang 576fd502729SJason Wang /* 577fd502729SJason Wang * vp_modern_get_queue_size - get size for a virtqueue 578fd502729SJason Wang * @mdev: the modern virtio-pci device 579fd502729SJason Wang * @index: the queue index 580fd502729SJason Wang * 581fd502729SJason Wang * Returns the size of the virtqueue 582fd502729SJason Wang */ 583fd502729SJason Wang u16 vp_modern_get_queue_size(struct virtio_pci_modern_device *mdev, 584fd502729SJason Wang u16 index) 585fd502729SJason Wang { 586fd502729SJason Wang vp_iowrite16(index, &mdev->common->queue_select); 587fd502729SJason Wang 588fd502729SJason Wang return vp_ioread16(&mdev->common->queue_size); 589fd502729SJason Wang 590fd502729SJason Wang } 591fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_get_queue_size); 592fd502729SJason Wang 593fd502729SJason Wang /* 594fd502729SJason Wang * vp_modern_get_num_queues - get the number of virtqueues 595fd502729SJason Wang * @mdev: the modern virtio-pci device 596fd502729SJason Wang * 597fd502729SJason Wang * Returns the number of virtqueues 598fd502729SJason Wang */ 599fd502729SJason Wang u16 vp_modern_get_num_queues(struct virtio_pci_modern_device *mdev) 600fd502729SJason Wang { 601fd502729SJason Wang return vp_ioread16(&mdev->common->num_queues); 602fd502729SJason Wang } 603fd502729SJason Wang EXPORT_SYMBOL_GPL(vp_modern_get_num_queues); 604fd502729SJason Wang 605fd502729SJason Wang /* 606fd502729SJason Wang * vp_modern_get_queue_notify_off - get notification offset for a virtqueue 607fd502729SJason Wang * @mdev: the modern virtio-pci device 608fd502729SJason Wang * @index: the queue index 609fd502729SJason Wang * 610fd502729SJason Wang * Returns the notification offset for a virtqueue 611fd502729SJason Wang */ 612a5f7a24fSJason Wang static u16 vp_modern_get_queue_notify_off(struct virtio_pci_modern_device *mdev, 613fd502729SJason Wang u16 index) 614fd502729SJason Wang { 615fd502729SJason Wang vp_iowrite16(index, &mdev->common->queue_select); 616fd502729SJason Wang 617fd502729SJason Wang return vp_ioread16(&mdev->common->queue_notify_off); 618fd502729SJason Wang } 619fd502729SJason Wang 6209e3bb9b7SJason Wang /* 6219e3bb9b7SJason Wang * vp_modern_map_vq_notify - map notification area for a 6229e3bb9b7SJason Wang * specific virtqueue 6239e3bb9b7SJason Wang * @mdev: the modern virtio-pci device 6249e3bb9b7SJason Wang * @index: the queue index 6259e311bcaSJason Wang * @pa: the pointer to the physical address of the nofity area 6269e3bb9b7SJason Wang * 6279e3bb9b7SJason Wang * Returns the address of the notification area 6289e3bb9b7SJason Wang */ 629d7bce85aSMichael S. Tsirkin void __iomem *vp_modern_map_vq_notify(struct virtio_pci_modern_device *mdev, 6309e311bcaSJason Wang u16 index, resource_size_t *pa) 6319e3bb9b7SJason Wang { 6329e3bb9b7SJason Wang u16 off = vp_modern_get_queue_notify_off(mdev, index); 6339e3bb9b7SJason Wang 6349e3bb9b7SJason Wang if (mdev->notify_base) { 6359e3bb9b7SJason Wang /* offset should not wrap */ 6369e3bb9b7SJason Wang if ((u64)off * mdev->notify_offset_multiplier + 2 6379e3bb9b7SJason Wang > mdev->notify_len) { 6389e3bb9b7SJason Wang dev_warn(&mdev->pci_dev->dev, 6399e3bb9b7SJason Wang "bad notification offset %u (x %u) " 6409e3bb9b7SJason Wang "for queue %u > %zd", 6419e3bb9b7SJason Wang off, mdev->notify_offset_multiplier, 6429e3bb9b7SJason Wang index, mdev->notify_len); 6439e3bb9b7SJason Wang return NULL; 6449e3bb9b7SJason Wang } 6459e311bcaSJason Wang if (pa) 6469e311bcaSJason Wang *pa = mdev->notify_pa + 6479e311bcaSJason Wang off * mdev->notify_offset_multiplier; 648d7bce85aSMichael S. Tsirkin return mdev->notify_base + off * mdev->notify_offset_multiplier; 6499e3bb9b7SJason Wang } else { 650d7bce85aSMichael S. Tsirkin return vp_modern_map_capability(mdev, 6519e3bb9b7SJason Wang mdev->notify_map_cap, 2, 2, 6529e3bb9b7SJason Wang off * mdev->notify_offset_multiplier, 2, 6539e311bcaSJason Wang NULL, pa); 6549e3bb9b7SJason Wang } 6559e3bb9b7SJason Wang } 6569e3bb9b7SJason Wang EXPORT_SYMBOL_GPL(vp_modern_map_vq_notify); 6579e3bb9b7SJason Wang 658fd502729SJason Wang MODULE_VERSION("0.1"); 659fd502729SJason Wang MODULE_DESCRIPTION("Modern Virtio PCI Device"); 660fd502729SJason Wang MODULE_AUTHOR("Jason Wang <jasowang@redhat.com>"); 661fd502729SJason Wang MODULE_LICENSE("GPL"); 662