1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 1997, Stefan Esser <se@freebsd.org> 5 * Copyright (c) 2000, Michael Smith <msmith@freebsd.org> 6 * Copyright (c) 2000, BSDi 7 * All rights reserved. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice unmodified, this list of conditions, and the following 14 * disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 21 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 22 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 23 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 24 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 */ 30 31 #include <sys/param.h> 32 #include <sys/systm.h> 33 #include <sys/bus.h> 34 #include <sys/lock.h> 35 #include <sys/kernel.h> 36 #include <sys/mutex.h> 37 #include <sys/sysctl.h> 38 #include <dev/pci/pcivar.h> 39 #include <dev/pci/pcireg.h> 40 #include <vm/vm.h> 41 #include <vm/pmap.h> 42 #include <machine/pci_cfgreg.h> 43 44 static uint32_t pci_docfgregread(int bus, int slot, int func, int reg, 45 int bytes); 46 static int pciereg_cfgread(int bus, unsigned slot, unsigned func, 47 unsigned reg, unsigned bytes); 48 static void pciereg_cfgwrite(int bus, unsigned slot, unsigned func, 49 unsigned reg, int data, unsigned bytes); 50 static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 51 static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 52 53 SYSCTL_DECL(_hw_pci); 54 55 /* 56 * For amd64 we assume that type 1 I/O port-based access always works. 57 * If an ACPI MCFG table exists, pcie_cfgregopen() will be called to 58 * switch to memory-mapped access. 59 */ 60 int cfgmech = CFGMECH_1; 61 62 static vm_offset_t pcie_base; 63 static int pcie_minbus, pcie_maxbus; 64 static uint32_t pcie_badslots; 65 static struct mtx pcicfg_mtx; 66 MTX_SYSINIT(pcicfg_mtx, &pcicfg_mtx, "pcicfg_mtx", MTX_SPIN); 67 static int mcfg_enable = 1; 68 SYSCTL_INT(_hw_pci, OID_AUTO, mcfg, CTLFLAG_RDTUN, &mcfg_enable, 0, 69 "Enable support for PCI-e memory mapped config access"); 70 71 int 72 pci_cfgregopen(void) 73 { 74 75 return (1); 76 } 77 78 static uint32_t 79 pci_docfgregread(int bus, int slot, int func, int reg, int bytes) 80 { 81 82 if (cfgmech == CFGMECH_PCIE && 83 (bus >= pcie_minbus && bus <= pcie_maxbus) && 84 (bus != 0 || !(1 << slot & pcie_badslots))) 85 return (pciereg_cfgread(bus, slot, func, reg, bytes)); 86 else 87 return (pcireg_cfgread(bus, slot, func, reg, bytes)); 88 } 89 90 /* 91 * Read configuration space register 92 */ 93 u_int32_t 94 pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 95 { 96 uint32_t line; 97 98 /* 99 * Some BIOS writers seem to want to ignore the spec and put 100 * 0 in the intline rather than 255 to indicate none. Some use 101 * numbers in the range 128-254 to indicate something strange and 102 * apparently undocumented anywhere. Assume these are completely bogus 103 * and map them to 255, which the rest of the PCI code recognizes as 104 * as an invalid IRQ. 105 */ 106 if (reg == PCIR_INTLINE && bytes == 1) { 107 line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1); 108 if (line == 0 || line >= 128) 109 line = PCI_INVALID_IRQ; 110 return (line); 111 } 112 return (pci_docfgregread(bus, slot, func, reg, bytes)); 113 } 114 115 /* 116 * Write configuration space register 117 */ 118 void 119 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 120 { 121 122 if (cfgmech == CFGMECH_PCIE && 123 (bus >= pcie_minbus && bus <= pcie_maxbus) && 124 (bus != 0 || !(1 << slot & pcie_badslots))) 125 pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 126 else 127 pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 128 } 129 130 /* 131 * Configuration space access using direct register operations 132 */ 133 134 /* enable configuration space accesses and return data port address */ 135 static int 136 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 137 { 138 int dataport = 0; 139 140 if (bus <= PCI_BUSMAX && slot <= PCI_SLOTMAX && func <= PCI_FUNCMAX && 141 (unsigned)reg <= PCI_REGMAX && bytes != 3 && 142 (unsigned)bytes <= 4 && (reg & (bytes - 1)) == 0) { 143 outl(CONF1_ADDR_PORT, (1U << 31) | (bus << 16) | (slot << 11) 144 | (func << 8) | (reg & ~0x03)); 145 dataport = CONF1_DATA_PORT + (reg & 0x03); 146 } 147 return (dataport); 148 } 149 150 /* disable configuration space accesses */ 151 static void 152 pci_cfgdisable(void) 153 { 154 155 /* 156 * Do nothing. Writing a 0 to the address port can apparently 157 * confuse some bridges and cause spurious access failures. 158 */ 159 } 160 161 static int 162 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 163 { 164 int data = -1; 165 int port; 166 167 mtx_lock_spin(&pcicfg_mtx); 168 port = pci_cfgenable(bus, slot, func, reg, bytes); 169 if (port != 0) { 170 switch (bytes) { 171 case 1: 172 data = inb(port); 173 break; 174 case 2: 175 data = inw(port); 176 break; 177 case 4: 178 data = inl(port); 179 break; 180 } 181 pci_cfgdisable(); 182 } 183 mtx_unlock_spin(&pcicfg_mtx); 184 return (data); 185 } 186 187 static void 188 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 189 { 190 int port; 191 192 mtx_lock_spin(&pcicfg_mtx); 193 port = pci_cfgenable(bus, slot, func, reg, bytes); 194 if (port != 0) { 195 switch (bytes) { 196 case 1: 197 outb(port, data); 198 break; 199 case 2: 200 outw(port, data); 201 break; 202 case 4: 203 outl(port, data); 204 break; 205 } 206 pci_cfgdisable(); 207 } 208 mtx_unlock_spin(&pcicfg_mtx); 209 } 210 211 int 212 pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus) 213 { 214 uint32_t val1, val2; 215 int slot; 216 217 if (!mcfg_enable) 218 return (0); 219 220 if (minbus != 0) 221 return (0); 222 223 if (bootverbose) 224 printf("PCIe: Memory Mapped configuration base @ 0x%lx\n", 225 base); 226 227 /* XXX: We should make sure this really fits into the direct map. */ 228 pcie_base = (vm_offset_t)pmap_mapdev_pciecfg(base, (maxbus + 1) << 20); 229 pcie_minbus = minbus; 230 pcie_maxbus = maxbus; 231 cfgmech = CFGMECH_PCIE; 232 233 /* 234 * On some AMD systems, some of the devices on bus 0 are 235 * inaccessible using memory-mapped PCI config access. Walk 236 * bus 0 looking for such devices. For these devices, we will 237 * fall back to using type 1 config access instead. 238 */ 239 if (pci_cfgregopen() != 0) { 240 for (slot = 0; slot <= PCI_SLOTMAX; slot++) { 241 val1 = pcireg_cfgread(0, slot, 0, 0, 4); 242 if (val1 == 0xffffffff) 243 continue; 244 245 val2 = pciereg_cfgread(0, slot, 0, 0, 4); 246 if (val2 != val1) 247 pcie_badslots |= (1 << slot); 248 } 249 } 250 251 return (1); 252 } 253 254 #define PCIE_VADDR(base, reg, bus, slot, func) \ 255 ((base) + \ 256 ((((bus) & 0xff) << 20) | \ 257 (((slot) & 0x1f) << 15) | \ 258 (((func) & 0x7) << 12) | \ 259 ((reg) & 0xfff))) 260 261 /* 262 * AMD BIOS And Kernel Developer's Guides for CPU families starting with 10h 263 * have a requirement that all accesses to the memory mapped PCI configuration 264 * space are done using AX class of registers. 265 * Since other vendors do not currently have any contradicting requirements 266 * the AMD access pattern is applied universally. 267 */ 268 269 static int 270 pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg, 271 unsigned bytes) 272 { 273 vm_offset_t va; 274 int data = -1; 275 276 if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 277 func > PCI_FUNCMAX || reg > PCIE_REGMAX) 278 return (-1); 279 280 va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 281 282 switch (bytes) { 283 case 4: 284 __asm("movl %1, %0" : "=a" (data) 285 : "m" (*(volatile uint32_t *)va)); 286 break; 287 case 2: 288 __asm("movzwl %1, %0" : "=a" (data) 289 : "m" (*(volatile uint16_t *)va)); 290 break; 291 case 1: 292 __asm("movzbl %1, %0" : "=a" (data) 293 : "m" (*(volatile uint8_t *)va)); 294 break; 295 } 296 297 return (data); 298 } 299 300 static void 301 pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data, 302 unsigned bytes) 303 { 304 vm_offset_t va; 305 306 if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 307 func > PCI_FUNCMAX || reg > PCIE_REGMAX) 308 return; 309 310 va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 311 312 switch (bytes) { 313 case 4: 314 __asm("movl %1, %0" : "=m" (*(volatile uint32_t *)va) 315 : "a" (data)); 316 break; 317 case 2: 318 __asm("movw %1, %0" : "=m" (*(volatile uint16_t *)va) 319 : "a" ((uint16_t)data)); 320 break; 321 case 1: 322 __asm("movb %1, %0" : "=m" (*(volatile uint8_t *)va) 323 : "a" ((uint8_t)data)); 324 break; 325 } 326 } 327