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/api.h>
  16#include <asm/pci_x86.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        rcu_read_lock();
  64        base = get_base_addr(seg, bus, devfn);
  65        if (!base) {
  66                rcu_read_unlock();
  67                goto err;
  68        }
  69
  70        raw_spin_lock_irqsave(&pci_config_lock, flags);
  71
  72        pci_exp_set_dev_base(base, bus, devfn);
  73
  74        switch (len) {
  75        case 1:
  76                *value = mmio_config_readb(mmcfg_virt_addr + reg);
  77                break;
  78        case 2:
  79                *value = mmio_config_readw(mmcfg_virt_addr + reg);
  80                break;
  81        case 4:
  82                *value = mmio_config_readl(mmcfg_virt_addr + reg);
  83                break;
  84        }
  85        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
  86        rcu_read_unlock();
  87
  88        return 0;
  89}
  90
  91static int pci_mmcfg_write(unsigned int seg, unsigned int bus,
  92                           unsigned int devfn, int reg, int len, u32 value)
  93{
  94        unsigned long flags;
  95        u32 base;
  96
  97        if ((bus > 255) || (devfn > 255) || (reg > 4095))
  98                return -EINVAL;
  99
 100        rcu_read_lock();
 101        base = get_base_addr(seg, bus, devfn);
 102        if (!base) {
 103                rcu_read_unlock();
 104                return -EINVAL;
 105        }
 106
 107        raw_spin_lock_irqsave(&pci_config_lock, flags);
 108
 109        pci_exp_set_dev_base(base, bus, devfn);
 110
 111        switch (len) {
 112        case 1:
 113                mmio_config_writeb(mmcfg_virt_addr + reg, value);
 114                break;
 115        case 2:
 116                mmio_config_writew(mmcfg_virt_addr + reg, value);
 117                break;
 118        case 4:
 119                mmio_config_writel(mmcfg_virt_addr + reg, value);
 120                break;
 121        }
 122        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
 123        rcu_read_unlock();
 124
 125        return 0;
 126}
 127
 128const struct pci_raw_ops pci_mmcfg = {
 129        .read =         pci_mmcfg_read,
 130        .write =        pci_mmcfg_write,
 131};
 132
 133int __init pci_mmcfg_arch_init(void)
 134{
 135        printk(KERN_INFO "PCI: Using MMCONFIG for extended config space\n");
 136        raw_pci_ext_ops = &pci_mmcfg;
 137        return 1;
 138}
 139
 140void __init pci_mmcfg_arch_free(void)
 141{
 142}
 143
 144int pci_mmcfg_arch_map(struct pci_mmcfg_region *cfg)
 145{
 146        return 0;
 147}
 148
 149void pci_mmcfg_arch_unmap(struct pci_mmcfg_region *cfg)
 150{
 151        unsigned long flags;
 152
 153        /* Invalidate the cached mmcfg map entry. */
 154        raw_spin_lock_irqsave(&pci_config_lock, flags);
 155        mmcfg_last_accessed_device = 0;
 156        raw_spin_unlock_irqrestore(&pci_config_lock, flags);
 157}
 158