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 <asm/e820.h>
  13#include <asm/pci_x86.h>
  14
  15#define PREFIX "PCI: "
  16
  17static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn)
  18{
  19        struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
  20
  21        if (cfg && cfg->virt)
  22                return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12));
  23        return NULL;
  24}
  25
  26static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
  27                          unsigned int devfn, int reg, int len, u32 *value)
  28{
  29        char __iomem *addr;
  30
  31        /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
  32        if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) {
  33err:            *value = -1;
  34                return -EINVAL;
  35        }
  36
  37        addr = pci_dev_base(seg, bus, devfn);
  38        if (!addr)
  39                goto err;
  40
  41        switch (len) {
  42        case 1:
  43                *value = mmio_config_readb(addr + reg);
  44                break;
  45        case 2:
  46                *value = mmio_config_readw(addr + reg);
  47                break;
  48        case 4:
  49                *value = mmio_config_readl(addr + reg);
  50                break;
  51        }
  52
  53        return 0;
  54}
  55
  56static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
  57                           unsigned int devfn, int reg, int len, u32 value)
  58{
  59        char __iomem *addr;
  60
  61        /* Why do we have this when nobody checks it. How about a BUG()!? -AK */
  62        if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095)))
  63                return -EINVAL;
  64
  65        addr = pci_dev_base(seg, bus, devfn);
  66        if (!addr)
  67                return -EINVAL;
  68
  69        switch (len) {
  70        case 1:
  71                mmio_config_writeb(addr + reg, value);
  72                break;
  73        case 2:
  74                mmio_config_writew(addr + reg, value);
  75                break;
  76        case 4:
  77                mmio_config_writel(addr + reg, value);
  78                break;
  79        }
  80
  81        return 0;
  82}
  83
  84static struct pci_raw_ops pci_mmcfg = {
  85        .read =         pci_mmcfg_read,
  86        .write =        pci_mmcfg_write,
  87};
  88
  89static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg)
  90{
  91        void __iomem *addr;
  92        u64 start, size;
  93        int num_buses;
  94
  95        start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
  96        num_buses = cfg->end_bus - cfg->start_bus + 1;
  97        size = PCI_MMCFG_BUS_OFFSET(num_buses);
  98        addr = ioremap_nocache(start, size);
  99        if (addr)
 100                addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus);
 101        return addr;
 102}
 103
 104int __init pci_mmcfg_arch_init(void)
 105{
 106        struct pci_mmcfg_region *cfg;
 107
 108        list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 109                cfg->virt = mcfg_ioremap(cfg);
 110                if (!cfg->virt) {
 111                        printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n",
 112                               &cfg->res);
 113                        pci_mmcfg_arch_free();
 114                        return 0;
 115                }
 116        }
 117        raw_pci_ext_ops = &pci_mmcfg;
 118        return 1;
 119}
 120
 121void __init pci_mmcfg_arch_free(void)
 122{
 123        struct pci_mmcfg_region *cfg;
 124
 125        list_for_each_entry(cfg, &pci_mmcfg_list, list) {
 126                if (cfg->virt) {
 127                        iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus));
 128                        cfg->virt = NULL;
 129                }
 130        }
 131}
 132