1f68c0654SThomas Gleixner /* 2f68c0654SThomas Gleixner * mmconfig.c - Low-level direct PCI config space access via MMCONFIG 3f68c0654SThomas Gleixner * 4f68c0654SThomas Gleixner * This is an 64bit optimized version that always keeps the full mmconfig 5f68c0654SThomas Gleixner * space mapped. This allows lockless config space operation. 6f68c0654SThomas Gleixner */ 7f68c0654SThomas Gleixner 8f68c0654SThomas Gleixner #include <linux/pci.h> 9f68c0654SThomas Gleixner #include <linux/init.h> 10f68c0654SThomas Gleixner #include <linux/acpi.h> 11f68c0654SThomas Gleixner #include <linux/bitmap.h> 12f68c0654SThomas Gleixner #include <asm/e820.h> 1382487711SJaswinder Singh Rajput #include <asm/pci_x86.h> 14f68c0654SThomas Gleixner 158c57786aSBjorn Helgaas #define PREFIX "PCI: " 168c57786aSBjorn Helgaas 17f68c0654SThomas Gleixner static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) 18f68c0654SThomas Gleixner { 19*f6e1d8ccSBjorn Helgaas struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus); 20a0ca9909SIvan Kokshaysky 21*f6e1d8ccSBjorn Helgaas if (cfg && cfg->virt) 22*f6e1d8ccSBjorn Helgaas return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12)); 23f68c0654SThomas Gleixner return NULL; 24f68c0654SThomas Gleixner } 25f68c0654SThomas Gleixner 26f68c0654SThomas Gleixner static int pci_mmcfg_read(unsigned int seg, unsigned int bus, 27f68c0654SThomas Gleixner unsigned int devfn, int reg, int len, u32 *value) 28f68c0654SThomas Gleixner { 29f68c0654SThomas Gleixner char __iomem *addr; 30f68c0654SThomas Gleixner 31f68c0654SThomas Gleixner /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ 32f68c0654SThomas Gleixner if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) { 33a0ca9909SIvan Kokshaysky err: *value = -1; 34f68c0654SThomas Gleixner return -EINVAL; 35f68c0654SThomas Gleixner } 36f68c0654SThomas Gleixner 37f68c0654SThomas Gleixner addr = pci_dev_base(seg, bus, devfn); 38f68c0654SThomas Gleixner if (!addr) 39a0ca9909SIvan Kokshaysky goto err; 40f68c0654SThomas Gleixner 41f68c0654SThomas Gleixner switch (len) { 42f68c0654SThomas Gleixner case 1: 43f68c0654SThomas Gleixner *value = mmio_config_readb(addr + reg); 44f68c0654SThomas Gleixner break; 45f68c0654SThomas Gleixner case 2: 46f68c0654SThomas Gleixner *value = mmio_config_readw(addr + reg); 47f68c0654SThomas Gleixner break; 48f68c0654SThomas Gleixner case 4: 49f68c0654SThomas Gleixner *value = mmio_config_readl(addr + reg); 50f68c0654SThomas Gleixner break; 51f68c0654SThomas Gleixner } 52f68c0654SThomas Gleixner 53f68c0654SThomas Gleixner return 0; 54f68c0654SThomas Gleixner } 55f68c0654SThomas Gleixner 56f68c0654SThomas Gleixner static int pci_mmcfg_write(unsigned int seg, unsigned int bus, 57f68c0654SThomas Gleixner unsigned int devfn, int reg, int len, u32 value) 58f68c0654SThomas Gleixner { 59f68c0654SThomas Gleixner char __iomem *addr; 60f68c0654SThomas Gleixner 61f68c0654SThomas Gleixner /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ 62f68c0654SThomas Gleixner if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) 63f68c0654SThomas Gleixner return -EINVAL; 64f68c0654SThomas Gleixner 65f68c0654SThomas Gleixner addr = pci_dev_base(seg, bus, devfn); 66f68c0654SThomas Gleixner if (!addr) 67a0ca9909SIvan Kokshaysky return -EINVAL; 68f68c0654SThomas Gleixner 69f68c0654SThomas Gleixner switch (len) { 70f68c0654SThomas Gleixner case 1: 71f68c0654SThomas Gleixner mmio_config_writeb(addr + reg, value); 72f68c0654SThomas Gleixner break; 73f68c0654SThomas Gleixner case 2: 74f68c0654SThomas Gleixner mmio_config_writew(addr + reg, value); 75f68c0654SThomas Gleixner break; 76f68c0654SThomas Gleixner case 4: 77f68c0654SThomas Gleixner mmio_config_writel(addr + reg, value); 78f68c0654SThomas Gleixner break; 79f68c0654SThomas Gleixner } 80f68c0654SThomas Gleixner 81f68c0654SThomas Gleixner return 0; 82f68c0654SThomas Gleixner } 83f68c0654SThomas Gleixner 84f68c0654SThomas Gleixner static struct pci_raw_ops pci_mmcfg = { 85f68c0654SThomas Gleixner .read = pci_mmcfg_read, 86f68c0654SThomas Gleixner .write = pci_mmcfg_write, 87f68c0654SThomas Gleixner }; 88f68c0654SThomas Gleixner 89d215a9c8SBjorn Helgaas static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg) 90f68c0654SThomas Gleixner { 91f68c0654SThomas Gleixner void __iomem *addr; 92068258bcSYinghai Lu u64 start, size; 93df5eb1d6SBjorn Helgaas int num_buses; 94f68c0654SThomas Gleixner 95d7e6b66fSBjorn Helgaas start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus); 96d7e6b66fSBjorn Helgaas num_buses = cfg->end_bus - cfg->start_bus + 1; 97df5eb1d6SBjorn Helgaas size = PCI_MMCFG_BUS_OFFSET(num_buses); 98068258bcSYinghai Lu addr = ioremap_nocache(start, size); 998c57786aSBjorn Helgaas if (addr) 100d7e6b66fSBjorn Helgaas addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus); 101f68c0654SThomas Gleixner return addr; 102f68c0654SThomas Gleixner } 103f68c0654SThomas Gleixner 104f68c0654SThomas Gleixner int __init pci_mmcfg_arch_init(void) 105f68c0654SThomas Gleixner { 1063f0f5503SBjorn Helgaas struct pci_mmcfg_region *cfg; 107f68c0654SThomas Gleixner 108ff097dddSBjorn Helgaas list_for_each_entry(cfg, &pci_mmcfg_list, list) { 1093f0f5503SBjorn Helgaas cfg->virt = mcfg_ioremap(cfg); 1103f0f5503SBjorn Helgaas if (!cfg->virt) { 1118c57786aSBjorn Helgaas printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n", 1128c57786aSBjorn Helgaas &cfg->res); 1130b64ad71SYinghai Lu pci_mmcfg_arch_free(); 114f68c0654SThomas Gleixner return 0; 115f68c0654SThomas Gleixner } 116f68c0654SThomas Gleixner } 117b6ce068aSMatthew Wilcox raw_pci_ext_ops = &pci_mmcfg; 118f68c0654SThomas Gleixner return 1; 119f68c0654SThomas Gleixner } 1200b64ad71SYinghai Lu 1210b64ad71SYinghai Lu void __init pci_mmcfg_arch_free(void) 1220b64ad71SYinghai Lu { 1233f0f5503SBjorn Helgaas struct pci_mmcfg_region *cfg; 1240b64ad71SYinghai Lu 125ff097dddSBjorn Helgaas list_for_each_entry(cfg, &pci_mmcfg_list, list) { 1263f0f5503SBjorn Helgaas if (cfg->virt) { 1273f0f5503SBjorn Helgaas iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); 1283f0f5503SBjorn Helgaas cfg->virt = NULL; 1290b64ad71SYinghai Lu } 1300b64ad71SYinghai Lu } 1310b64ad71SYinghai Lu } 132