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 15f68c0654SThomas Gleixner static char __iomem *get_virt(unsigned int seg, unsigned bus) 16f68c0654SThomas Gleixner { 17*3f0f5503SBjorn Helgaas int i; 18d215a9c8SBjorn Helgaas struct pci_mmcfg_region *cfg; 19f68c0654SThomas Gleixner 20*3f0f5503SBjorn Helgaas for (i = 0; i < pci_mmcfg_config_num; ++i) { 21*3f0f5503SBjorn Helgaas cfg = &pci_mmcfg_config[i]; 22d7e6b66fSBjorn Helgaas if (cfg->segment == seg && 23d7e6b66fSBjorn Helgaas (cfg->start_bus <= bus) && 24d7e6b66fSBjorn Helgaas (cfg->end_bus >= bus)) 25*3f0f5503SBjorn Helgaas return cfg->virt; 26f68c0654SThomas Gleixner } 27f68c0654SThomas Gleixner 28f68c0654SThomas Gleixner /* Fall back to type 0 */ 29f68c0654SThomas Gleixner return NULL; 30f68c0654SThomas Gleixner } 31f68c0654SThomas Gleixner 32f68c0654SThomas Gleixner static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) 33f68c0654SThomas Gleixner { 34f68c0654SThomas Gleixner char __iomem *addr; 35a0ca9909SIvan Kokshaysky 36f68c0654SThomas Gleixner addr = get_virt(seg, bus); 37f68c0654SThomas Gleixner if (!addr) 38f68c0654SThomas Gleixner return NULL; 39df5eb1d6SBjorn Helgaas return addr + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12)); 40f68c0654SThomas Gleixner } 41f68c0654SThomas Gleixner 42f68c0654SThomas Gleixner static int pci_mmcfg_read(unsigned int seg, unsigned int bus, 43f68c0654SThomas Gleixner unsigned int devfn, int reg, int len, u32 *value) 44f68c0654SThomas Gleixner { 45f68c0654SThomas Gleixner char __iomem *addr; 46f68c0654SThomas Gleixner 47f68c0654SThomas Gleixner /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ 48f68c0654SThomas Gleixner if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) { 49a0ca9909SIvan Kokshaysky err: *value = -1; 50f68c0654SThomas Gleixner return -EINVAL; 51f68c0654SThomas Gleixner } 52f68c0654SThomas Gleixner 53f68c0654SThomas Gleixner addr = pci_dev_base(seg, bus, devfn); 54f68c0654SThomas Gleixner if (!addr) 55a0ca9909SIvan Kokshaysky goto err; 56f68c0654SThomas Gleixner 57f68c0654SThomas Gleixner switch (len) { 58f68c0654SThomas Gleixner case 1: 59f68c0654SThomas Gleixner *value = mmio_config_readb(addr + reg); 60f68c0654SThomas Gleixner break; 61f68c0654SThomas Gleixner case 2: 62f68c0654SThomas Gleixner *value = mmio_config_readw(addr + reg); 63f68c0654SThomas Gleixner break; 64f68c0654SThomas Gleixner case 4: 65f68c0654SThomas Gleixner *value = mmio_config_readl(addr + reg); 66f68c0654SThomas Gleixner break; 67f68c0654SThomas Gleixner } 68f68c0654SThomas Gleixner 69f68c0654SThomas Gleixner return 0; 70f68c0654SThomas Gleixner } 71f68c0654SThomas Gleixner 72f68c0654SThomas Gleixner static int pci_mmcfg_write(unsigned int seg, unsigned int bus, 73f68c0654SThomas Gleixner unsigned int devfn, int reg, int len, u32 value) 74f68c0654SThomas Gleixner { 75f68c0654SThomas Gleixner char __iomem *addr; 76f68c0654SThomas Gleixner 77f68c0654SThomas Gleixner /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ 78f68c0654SThomas Gleixner if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) 79f68c0654SThomas Gleixner return -EINVAL; 80f68c0654SThomas Gleixner 81f68c0654SThomas Gleixner addr = pci_dev_base(seg, bus, devfn); 82f68c0654SThomas Gleixner if (!addr) 83a0ca9909SIvan Kokshaysky return -EINVAL; 84f68c0654SThomas Gleixner 85f68c0654SThomas Gleixner switch (len) { 86f68c0654SThomas Gleixner case 1: 87f68c0654SThomas Gleixner mmio_config_writeb(addr + reg, value); 88f68c0654SThomas Gleixner break; 89f68c0654SThomas Gleixner case 2: 90f68c0654SThomas Gleixner mmio_config_writew(addr + reg, value); 91f68c0654SThomas Gleixner break; 92f68c0654SThomas Gleixner case 4: 93f68c0654SThomas Gleixner mmio_config_writel(addr + reg, value); 94f68c0654SThomas Gleixner break; 95f68c0654SThomas Gleixner } 96f68c0654SThomas Gleixner 97f68c0654SThomas Gleixner return 0; 98f68c0654SThomas Gleixner } 99f68c0654SThomas Gleixner 100f68c0654SThomas Gleixner static struct pci_raw_ops pci_mmcfg = { 101f68c0654SThomas Gleixner .read = pci_mmcfg_read, 102f68c0654SThomas Gleixner .write = pci_mmcfg_write, 103f68c0654SThomas Gleixner }; 104f68c0654SThomas Gleixner 105d215a9c8SBjorn Helgaas static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg) 106f68c0654SThomas Gleixner { 107f68c0654SThomas Gleixner void __iomem *addr; 108068258bcSYinghai Lu u64 start, size; 109df5eb1d6SBjorn Helgaas int num_buses; 110f68c0654SThomas Gleixner 111d7e6b66fSBjorn Helgaas start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus); 112d7e6b66fSBjorn Helgaas num_buses = cfg->end_bus - cfg->start_bus + 1; 113df5eb1d6SBjorn Helgaas size = PCI_MMCFG_BUS_OFFSET(num_buses); 114068258bcSYinghai Lu addr = ioremap_nocache(start, size); 115f68c0654SThomas Gleixner if (addr) { 116f68c0654SThomas Gleixner printk(KERN_INFO "PCI: Using MMCONFIG at %Lx - %Lx\n", 117068258bcSYinghai Lu start, start + size - 1); 118d7e6b66fSBjorn Helgaas addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus); 119f68c0654SThomas Gleixner } 120f68c0654SThomas Gleixner return addr; 121f68c0654SThomas Gleixner } 122f68c0654SThomas Gleixner 123f68c0654SThomas Gleixner int __init pci_mmcfg_arch_init(void) 124f68c0654SThomas Gleixner { 125f68c0654SThomas Gleixner int i; 126*3f0f5503SBjorn Helgaas struct pci_mmcfg_region *cfg; 127f68c0654SThomas Gleixner 128f68c0654SThomas Gleixner for (i = 0; i < pci_mmcfg_config_num; ++i) { 129*3f0f5503SBjorn Helgaas cfg = &pci_mmcfg_config[i]; 130*3f0f5503SBjorn Helgaas cfg->virt = mcfg_ioremap(cfg); 131*3f0f5503SBjorn Helgaas if (!cfg->virt) { 132f68c0654SThomas Gleixner printk(KERN_ERR "PCI: Cannot map mmconfig aperture for " 133f68c0654SThomas Gleixner "segment %d\n", 134*3f0f5503SBjorn Helgaas cfg->segment); 1350b64ad71SYinghai Lu pci_mmcfg_arch_free(); 136f68c0654SThomas Gleixner return 0; 137f68c0654SThomas Gleixner } 138f68c0654SThomas Gleixner } 139b6ce068aSMatthew Wilcox raw_pci_ext_ops = &pci_mmcfg; 140f68c0654SThomas Gleixner return 1; 141f68c0654SThomas Gleixner } 1420b64ad71SYinghai Lu 1430b64ad71SYinghai Lu void __init pci_mmcfg_arch_free(void) 1440b64ad71SYinghai Lu { 1450b64ad71SYinghai Lu int i; 146*3f0f5503SBjorn Helgaas struct pci_mmcfg_region *cfg; 1470b64ad71SYinghai Lu 1480b64ad71SYinghai Lu for (i = 0; i < pci_mmcfg_config_num; ++i) { 149*3f0f5503SBjorn Helgaas cfg = &pci_mmcfg_config[i]; 150*3f0f5503SBjorn Helgaas if (cfg->virt) { 151*3f0f5503SBjorn Helgaas iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); 152*3f0f5503SBjorn Helgaas cfg->virt = NULL; 1530b64ad71SYinghai Lu } 1540b64ad71SYinghai Lu } 1550b64ad71SYinghai Lu } 156