1 /* 2 * mmconfig.c - Low-level direct PCI config space access via MMCONFIG 3 * 4 * This is an 64bit optimized version that always keeps the full mmconfig 5 * space mapped. This allows lockless config space operation. 6 */ 7 8 #include <linux/pci.h> 9 #include <linux/init.h> 10 #include <linux/acpi.h> 11 #include <linux/bitmap.h> 12 #include <asm/e820.h> 13 #include <asm/pci_x86.h> 14 15 #define PREFIX "PCI: " 16 17 static char __iomem *pci_dev_base(unsigned int seg, unsigned int bus, unsigned int devfn) 18 { 19 struct pci_mmcfg_region *cfg = pci_mmconfig_lookup(seg, bus); 20 21 if (cfg && cfg->virt) 22 return cfg->virt + (PCI_MMCFG_BUS_OFFSET(bus) | (devfn << 12)); 23 return NULL; 24 } 25 26 static int pci_mmcfg_read(unsigned int seg, unsigned int bus, 27 unsigned int devfn, int reg, int len, u32 *value) 28 { 29 char __iomem *addr; 30 31 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ 32 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) { 33 err: *value = -1; 34 return -EINVAL; 35 } 36 37 addr = pci_dev_base(seg, bus, devfn); 38 if (!addr) 39 goto err; 40 41 switch (len) { 42 case 1: 43 *value = mmio_config_readb(addr + reg); 44 break; 45 case 2: 46 *value = mmio_config_readw(addr + reg); 47 break; 48 case 4: 49 *value = mmio_config_readl(addr + reg); 50 break; 51 } 52 53 return 0; 54 } 55 56 static int pci_mmcfg_write(unsigned int seg, unsigned int bus, 57 unsigned int devfn, int reg, int len, u32 value) 58 { 59 char __iomem *addr; 60 61 /* Why do we have this when nobody checks it. How about a BUG()!? -AK */ 62 if (unlikely((bus > 255) || (devfn > 255) || (reg > 4095))) 63 return -EINVAL; 64 65 addr = pci_dev_base(seg, bus, devfn); 66 if (!addr) 67 return -EINVAL; 68 69 switch (len) { 70 case 1: 71 mmio_config_writeb(addr + reg, value); 72 break; 73 case 2: 74 mmio_config_writew(addr + reg, value); 75 break; 76 case 4: 77 mmio_config_writel(addr + reg, value); 78 break; 79 } 80 81 return 0; 82 } 83 84 static struct pci_raw_ops pci_mmcfg = { 85 .read = pci_mmcfg_read, 86 .write = pci_mmcfg_write, 87 }; 88 89 static void __iomem * __init mcfg_ioremap(struct pci_mmcfg_region *cfg) 90 { 91 void __iomem *addr; 92 u64 start, size; 93 int num_buses; 94 95 start = cfg->address + PCI_MMCFG_BUS_OFFSET(cfg->start_bus); 96 num_buses = cfg->end_bus - cfg->start_bus + 1; 97 size = PCI_MMCFG_BUS_OFFSET(num_buses); 98 addr = ioremap_nocache(start, size); 99 if (addr) 100 addr -= PCI_MMCFG_BUS_OFFSET(cfg->start_bus); 101 return addr; 102 } 103 104 int __init pci_mmcfg_arch_init(void) 105 { 106 struct pci_mmcfg_region *cfg; 107 108 list_for_each_entry(cfg, &pci_mmcfg_list, list) { 109 cfg->virt = mcfg_ioremap(cfg); 110 if (!cfg->virt) { 111 printk(KERN_ERR PREFIX "can't map MMCONFIG at %pR\n", 112 &cfg->res); 113 pci_mmcfg_arch_free(); 114 return 0; 115 } 116 } 117 raw_pci_ext_ops = &pci_mmcfg; 118 return 1; 119 } 120 121 void __init pci_mmcfg_arch_free(void) 122 { 123 struct pci_mmcfg_region *cfg; 124 125 list_for_each_entry(cfg, &pci_mmcfg_list, list) { 126 if (cfg->virt) { 127 iounmap(cfg->virt + PCI_MMCFG_BUS_OFFSET(cfg->start_bus)); 128 cfg->virt = NULL; 129 } 130 } 131 } 132