linux/arch/x86/pci/mmconfig_32.c
<<
>>
Prefs
   1/*
   2 * Copyright (C) 2004 Matthew Wilcox <matthew@wil.cx>
   3 * Copyright (C) 2004 Intel Corp.
   4 *
   5 * This code is released under the GNU General Public License version 2.
   6 */
   7
   8/*
   9 * mmconfig.c - Low-level direct PCI config space access via MMCONFIG
  10 */
  11
  12#include <linux/pci.h>
  13#include <linux/init.h>
  14#include <linux/rcupdate.h>
  15#include <asm/e820.h>
  16#include <asm/pci_x86.h>
  17#include <acpi/acpi.h>
  18
  19/* Assume systems with more busses have correct MCFG */
  20#define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG))
  21
  22/* The base address of the last MMCONFIG device accessed */
  23static u32 mmcfg_last_accessed_device;
  24static int mmcfg_last_accessed_cpu;
  25
  26/*
  27 * Functions for accessing PCI configuration space with MMCONFIG accesses
  28 */
  29static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
  30{
  31        struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
  32
  33        if (cfg)
  34                return cfg->address;
  35        return 0;
  36}
  37
  38/*
  39 * This is always called under pci_config_lock
  40 */
  41static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
  42{
  43        u32 dev_base = base | PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12);
  44        int cpu = smp_processor_id();
  45        if (dev_base != mmcfg_last_accessed_device ||
  46            cpu != mmcfg_last_accessed_cpu) {
  47                mmcfg_last_accessed_device = dev_base;
  48                mmcfg_last_accessed_cpu = cpu;
  49                set_fixmap_nocache(FIX_PCIE_MCFG, dev_base);
  50        }
  51}
  52
  53static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
  54                          unsigned int devfn, int reg, int len, u32 *value)
  55{
  56        unsigned long flags;
  57        u32 base;
  58
  59        if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
  60err:            *value = -1;
  61                return -EINVAL;
  62        }
  63
  64        rcu_read_lock();
  65        base = get_base_addr(seg, bus, devfn);
  66        if (!base) {
  67                rcu_read_unlock();
  68                goto err;
  69        }
  70
  71        raw_spin_lock_irqsave(&pci_config_lock, flags);
  72
  73        pci_exp_set_dev_base(base, bus, devfn);
  74
  75        switch (len) {
  76        case 1:
  77                *value = mmio_config_readb(mmcfg_virt_addr + reg);
  78                break;
  79        case 2:
  80                *value = mmio_config_readw(mmcfg_virt_addr + reg);
  81                break;
  82        case 4:
  83                *value = mmio_config_readl(mmcfg_virt_addr + reg);
  84                break;
  85        }
  86        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
  87        rcu_read_unlock();
  88
  89        return 0;
  90}
  91
  92static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
  93                           unsigned int devfn, int reg, int len, u32 value)
  94{
  95        unsigned long flags;
  96        u32 base;
  97
  98        if ((bus > 255) || (devfn > 255) || (reg > 4095))
  99                return -EINVAL;
 100
 101        rcu_read_lock();
 102        base = get_base_addr(seg, bus, devfn);
 103        if (!base) {
 104                rcu_read_unlock();
 105                return -EINVAL;
 106        }
 107
 108        raw_spin_lock_irqsave(&pci_config_lock, flags);
 109
 110        pci_exp_set_dev_base(base, bus, devfn);
 111
 112        switch (len) {
 113        case 1:
 114                mmio_config_writeb(mmcfg_virt_addr + reg, value);
 115                break;
 116        case 2:
 117                mmio_config_writew(mmcfg_virt_addr + reg, value);
 118                break;
 119        case 4:
 120                mmio_config_writel(mmcfg_virt_addr + reg, value);
 121                break;
 122        }
 123        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
 124        rcu_read_unlock();
 125
 126        return 0;
 127}
 128
 129const struct pci_raw_ops pci_mmcfg = {
 130        .read =         pci_mmcfg_read,
 131        .write =        pci_mmcfg_write,
 132};
 133
 134int __init pci_mmcfg_arch_init(void)
 135{
 136        printk(KERN_INFO "PCI: Using MMCONFIG for extended config space\n");
 137        raw_pci_ext_ops = &pci_mmcfg;
 138        return 1;
 139}
 140
 141void __init pci_mmcfg_arch_free(void)
 142{
 143}
 144
 145int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
 146{
 147        return 0;
 148}
 149
 150void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
 151{
 152        unsigned long flags;
 153
 154        /* Invalidate the cached mmcfg map entry. */
 155        raw_spin_lock_irqsave(&pci_config_lock, flags);
 156        mmcfg_last_accessed_device = 0;
 157        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
 158}
 159