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