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 <asm/e820.h>
  15#include <asm/pci_x86.h>
  16#include <acpi/acpi.h>
  17
  18/* Assume systems with more busses have correct MCFG */
  19#define mmcfg_virt_addr ((void __iomem *) fix_to_virt(FIX_PCIE_MCFG))
  20
  21/* The base address of the last MMCONFIG device accessed */
  22static u32 mmcfg_last_accessed_device;
  23static int mmcfg_last_accessed_cpu;
  24
  25/*
  26 * Functions for accessing PCI configuration space with MMCONFIG accesses
  27 */
  28static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn)
  29{
  30        struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus);
  31
  32        if (cfg)
  33                return cfg->address;
  34        return 0;
  35}
  36
  37/*
  38 * This is always called under pci_config_lock
  39 */
  40static void pci_exp_set_dev_base(unsigned int base, int bus, int devfn)
  41{
  42        u32 dev_base = base | PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12);
  43        int cpu = smp_processor_id();
  44        if (dev_base != mmcfg_last_accessed_device ||
  45            cpu != mmcfg_last_accessed_cpu) {
  46                mmcfg_last_accessed_device = dev_base;
  47                mmcfg_last_accessed_cpu = cpu;
  48                set_fixmap_nocache(FIX_PCIE_MCFG, dev_base);
  49        }
  50}
  51
  52static int pci_mmcfg_read(unsigned int seg, unsigned int bus,
  53                          unsigned int devfn, int reg, int len, u32 *value)
  54{
  55        unsigned long flags;
  56        u32 base;
  57
  58        if ((bus > 255) || (devfn > 255) || (reg > 4095)) {
  59err:            *value = -1;
  60                return -EINVAL;
  61        }
  62
  63        base = get_base_addr(seg, bus, devfn);
  64        if (!base)
  65                goto err;
  66
  67        raw_spin_lock_irqsave(&pci_config_lock, flags);
  68
  69        pci_exp_set_dev_base(base, bus, devfn);
  70
  71        switch (len) {
  72        case 1:
  73                *value = mmio_config_readb(mmcfg_virt_addr + reg);
  74                break;
  75        case 2:
  76                *value = mmio_config_readw(mmcfg_virt_addr + reg);
  77                break;
  78        case 4:
  79                *value = mmio_config_readl(mmcfg_virt_addr + reg);
  80                break;
  81        }
  82        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
  83
  84        return 0;
  85}
  86
  87static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
  88                           unsigned int devfn, int reg, int len, u32 value)
  89{
  90        unsigned long flags;
  91        u32 base;
  92
  93        if ((bus > 255) || (devfn > 255) || (reg > 4095))
  94                return -EINVAL;
  95
  96        base = get_base_addr(seg, bus, devfn);
  97        if (!base)
  98                return -EINVAL;
  99
 100        raw_spin_lock_irqsave(&pci_config_lock, flags);
 101
 102        pci_exp_set_dev_base(base, bus, devfn);
 103
 104        switch (len) {
 105        case 1:
 106                mmio_config_writeb(mmcfg_virt_addr + reg, value);
 107                break;
 108        case 2:
 109                mmio_config_writew(mmcfg_virt_addr + reg, value);
 110                break;
 111        case 4:
 112                mmio_config_writel(mmcfg_virt_addr + reg, value);
 113                break;
 114        }
 115        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
 116
 117        return 0;
 118}
 119
 120static struct pci_raw_ops pci_mmcfg = {
 121        .read =         pci_mmcfg_read,
 122        .write =        pci_mmcfg_write,
 123};
 124
 125int __init pci_mmcfg_arch_init(void)
 126{
 127        printk(KERN_INFO "PCI: Using MMCONFIG for extended config space\n");
 128        raw_pci_ext_ops = &pci_mmcfg;
 129        return 1;
 130}
 131
 132void __init pci_mmcfg_arch_free(void)
 133{
 134}
 135