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