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