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