linux/arch/x86/pci/mmconfig_64.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
   4 *
   5 * This is an 64bit optimized version that always keeps the full mmconfig
   6 * space mapped. This allows lockless config space operation.
   7 */
   8
   9#include <linux/pci.h>
  10#include <linux/init.h>
  11#include <linux/acpi.h>
  12#include <linux/bitmap.h>
  13#include <linux/rcupdate.h>
  14#include <asm/e820/api.h>
  15#include <asm/pci_x86.h>
  16
  17#define PREFIX "PCI: "
  18
  19static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
  20{
  21        struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
  22
  23        if (cfg && cfg->virt)
  24                return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
  25        return NULL;
  26}
  27
  28static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
  29                          unsigned int devfn, int reg, int len, u32 *value)
  30{
  31        char __iomem *addr;
  32
  33        /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
  34        if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
  35err:            *value = -1;
  36                return -EINVAL;
  37        }
  38
  39        rcu_read_lock();
  40        addr = pci_dev_base(seg, bus, devfn);
  41        if (!addr) {
  42                rcu_read_unlock();
  43                goto err;
  44        }
  45
  46        switch (len) {
  47        case 1:
  48                *value = mmio_config_readb(addr + reg);
  49                break;
  50        case 2:
  51                *value = mmio_config_readw(addr + reg);
  52                break;
  53        case 4:
  54                *value = mmio_config_readl(addr + reg);
  55                break;
  56        }
  57        rcu_read_unlock();
  58
  59        return 0;
  60}
  61
  62static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
  63                           unsigned int devfn, int reg, int len, u32 value)
  64{
  65        char __iomem *addr;
  66
  67        /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
  68        if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
  69                return -EINVAL;
  70
  71        rcu_read_lock();
  72        addr = pci_dev_base(seg, bus, devfn);
  73        if (!addr) {
  74                rcu_read_unlock();
  75                return -EINVAL;
  76        }
  77
  78        switch (len) {
  79        case 1:
  80                mmio_config_writeb(addr + reg, value);
  81                break;
  82        case 2:
  83                mmio_config_writew(addr + reg, value);
  84                break;
  85        case 4:
  86                mmio_config_writel(addr + reg, value);
  87                break;
  88        }
  89        rcu_read_unlock();
  90
  91        return 0;
  92}
  93
  94const struct pci_raw_ops pci_mmcfg = {
  95        .read =         pci_mmcfg_read,
  96        .write =        pci_mmcfg_write,
  97};
  98
  99static void __iomem *mcfg_ioremap(struct pci_mmcfg_region *cfg)
 100{
 101        void __iomem *addr;
 102        u64 start, size;
 103        int num_buses;
 104
 105        start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
 106        num_buses = cfg->end_bus - cfg->start_bus + 1;
 107        size = PCI_MMCFG_BUS_OFFSET(num_buses);
 108        addr = ioremap(start, size);
 109        if (addr)
 110                addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
 111        return addr;
 112}
 113
 114int __init pci_mmcfg_arch_init(void)
 115{
 116        struct pci_mmcfg_region *cfg;
 117
 118        list_for_each_entry(cfg, &pci_mmcfg_list, list)
 119                if (pci_mmcfg_arch_map(cfg)) {
 120                        pci_mmcfg_arch_free();
 121                        return 0;
 122                }
 123
 124        raw_pci_ext_ops = &pci_mmcfg;
 125
 126        return 1;
 127}
 128
 129void __init pci_mmcfg_arch_free(void)
 130{
 131        struct pci_mmcfg_region *cfg;
 132
 133        list_for_each_entry(cfg, &pci_mmcfg_list, list)
 134                pci_mmcfg_arch_unmap(cfg);
 135}
 136
 137int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
 138{
 139        cfg->virt = mcfg_ioremap(cfg);
 140        if (!cfg->virt) {
 141                pr_err(PREFIX "can't map MMCONFIG at %pR\n", &cfg->res);
 142                return -ENOMEM;
 143        }
 144
 145        return 0;
 146}
 147
 148void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
 149{
 150        if (cfg && cfg->virt) {
 151                iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
 152                cfg->virt = NULL;
 153        }
 154}
 155