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 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