1 /*- 2 * Copyright (c) 1997, Stefan Esser <se@kfreebsd.org> 3 * Copyright (c) 2000, Michael Smith <msmith@kfreebsd.org> 4 * Copyright (c) 2000, BSDi 5 * Copyright (c) 2004, Scott Long <scottl@kfreebsd.org> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice unmodified, this list of conditions, and the following 13 * disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 * 29 * $FreeBSD: src/sys/i386/pci/pci_cfgreg.c,v 1.124.2.3 2009/05/04 21:04:29 jhb 30 */ 31 32 #include <sys/param.h> 33 #include <sys/systm.h> 34 #include <sys/bus.h> 35 #include <sys/kernel.h> 36 #include <sys/lock.h> 37 #include <sys/malloc.h> 38 #include <sys/thread2.h> 39 #include <sys/spinlock.h> 40 #include <sys/spinlock2.h> 41 #include <sys/queue.h> 42 #include <bus/pci/pcivar.h> 43 #include <bus/pci/pcireg.h> 44 #include "pci_cfgreg.h" 45 #include <machine/pc/bios.h> 46 47 #include <vm/vm.h> 48 #include <vm/vm_param.h> 49 #include <vm/vm_kern.h> 50 #include <vm/vm_extern.h> 51 #include <vm/pmap.h> 52 #include <machine/pmap.h> 53 54 enum { 55 CFGMECH_NONE = 0, 56 CFGMECH_1, 57 CFGMECH_PCIE, 58 }; 59 60 static vm_offset_t pcie_base; 61 static int pcie_minbus, pcie_maxbus; 62 static uint32_t pcie_badslots; 63 static int cfgmech; 64 static struct spinlock pcicfg_spin; 65 66 static int mcfg_enable = 1; 67 TUNABLE_INT("hw.pci.mcfg", &mcfg_enable); 68 69 static uint32_t pci_docfgregread(int bus, int slot, int func, int reg, int bytes); 70 71 static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 72 static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, 73 int bytes); 74 75 static int pciereg_cfgread(int bus, unsigned slot, unsigned func, 76 unsigned reg, unsigned bytes); 77 static void pciereg_cfgwrite(int bus, unsigned slot, unsigned func, 78 unsigned reg, int data, unsigned bytes); 79 80 /* 81 * Initialise access to PCI configuration space 82 */ 83 int 84 pci_cfgregopen(void) 85 { 86 static int inited = 0; 87 uint64_t pciebar; 88 uint16_t vid, did; 89 90 if (!inited) { 91 inited = 1; 92 spin_init(&pcicfg_spin, "pcicfg"); 93 } 94 95 if (cfgmech != CFGMECH_NONE) 96 return 1; 97 cfgmech = CFGMECH_1; 98 99 /* 100 * Grope around in the PCI config space to see if this is a 101 * chipset that is capable of doing memory-mapped config cycles. 102 * This also implies that it can do PCIe extended config cycles. 103 */ 104 105 /* Check for supported chipsets */ 106 vid = pci_cfgregread(0, 0, 0, PCIR_VENDOR, 2); 107 did = pci_cfgregread(0, 0, 0, PCIR_DEVICE, 2); 108 switch (vid) { 109 case 0x8086: 110 switch (did) { 111 case 0x3590: 112 case 0x3592: 113 /* Intel 7520 or 7320 */ 114 pciebar = pci_cfgregread(0, 0, 0, 0xce, 2) << 16; 115 pcie_cfgregopen(pciebar, 0, 255); 116 break; 117 case 0x2580: 118 case 0x2584: 119 case 0x2590: 120 /* Intel 915, 925, or 915GM */ 121 pciebar = pci_cfgregread(0, 0, 0, 0x48, 4); 122 pcie_cfgregopen(pciebar, 0, 255); 123 break; 124 } 125 } 126 return 1; 127 } 128 129 static uint32_t 130 pci_docfgregread(int bus, int slot, int func, int reg, int bytes) 131 { 132 if (cfgmech == CFGMECH_PCIE && 133 (bus >= pcie_minbus && bus <= pcie_maxbus) && 134 (bus != 0 || !(1 << slot & pcie_badslots))) 135 return pciereg_cfgread(bus, slot, func, reg, bytes); 136 else 137 return pcireg_cfgread(bus, slot, func, reg, bytes); 138 } 139 140 /* 141 * Read configuration space register 142 */ 143 uint32_t 144 pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 145 { 146 /* 147 * Some BIOS writers seem to want to ignore the spec and put 148 * 0 in the intline rather than 255 to indicate none. Some use 149 * numbers in the range 128-254 to indicate something strange and 150 * apparently undocumented anywhere. Assume these are completely 151 * bogus and map them to 255, which means "none". 152 */ 153 if (reg == PCIR_INTLINE && bytes == 1) { 154 uint32_t line; 155 156 line = pci_docfgregread(bus, slot, func, PCIR_INTLINE, 1); 157 if (line == 0 || line >= 128) 158 return (PCI_INVALID_IRQ); 159 return line; 160 } 161 return pci_docfgregread(bus, slot, func, reg, bytes); 162 } 163 164 /* 165 * Write configuration space register 166 */ 167 void 168 pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 169 { 170 if (cfgmech == CFGMECH_PCIE && 171 (bus >= pcie_minbus && bus <= pcie_maxbus) && 172 (bus != 0 || !(1 << slot & pcie_badslots))) 173 pciereg_cfgwrite(bus, slot, func, reg, data, bytes); 174 else 175 pcireg_cfgwrite(bus, slot, func, reg, data, bytes); 176 } 177 178 /* 179 * Configuration space access using direct register operations 180 */ 181 182 /* enable configuration space accesses and return data port address */ 183 static int 184 pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 185 { 186 int dataport = 0; 187 188 if (bus <= PCI_BUSMAX && 189 slot <= PCI_SLOTMAX && 190 func <= PCI_FUNCMAX && 191 (unsigned)reg <= PCI_REGMAX && 192 bytes != 3 && 193 (unsigned)bytes <= 4 && 194 (reg & (bytes - 1)) == 0) { 195 outl(CONF1_ADDR_PORT, (1 << 31) | (bus << 16) | (slot << 11) | 196 (func << 8) | (reg & ~0x03)); 197 dataport = CONF1_DATA_PORT + (reg & 0x03); 198 } 199 return dataport; 200 } 201 202 /* disable configuration space accesses */ 203 static void 204 pci_cfgdisable(void) 205 { 206 /* 207 * Do nothing. Writing a 0 to the address port can apparently 208 * confuse some bridges and cause spurious access failures. 209 */ 210 } 211 212 static int 213 pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 214 { 215 int data = -1; 216 int port; 217 218 spin_lock(&pcicfg_spin); 219 port = pci_cfgenable(bus, slot, func, reg, bytes); 220 if (port != 0) { 221 switch (bytes) { 222 case 1: 223 data = inb(port); 224 break; 225 case 2: 226 data = inw(port); 227 break; 228 case 4: 229 data = inl(port); 230 break; 231 } 232 pci_cfgdisable(); 233 } 234 spin_unlock(&pcicfg_spin); 235 return data; 236 } 237 238 static void 239 pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 240 { 241 int port; 242 243 spin_lock(&pcicfg_spin); 244 port = pci_cfgenable(bus, slot, func, reg, bytes); 245 if (port != 0) { 246 switch (bytes) { 247 case 1: 248 outb(port, data); 249 break; 250 case 2: 251 outw(port, data); 252 break; 253 case 4: 254 outl(port, data); 255 break; 256 } 257 pci_cfgdisable(); 258 } 259 spin_unlock(&pcicfg_spin); 260 } 261 262 int 263 pcie_cfgregopen(uint64_t base, uint8_t minbus, uint8_t maxbus) 264 { 265 if (bootverbose) { 266 kprintf("PCIe: Memory Mapped configuration base @ 0x%jx, " 267 "bus [%d, %d]\n", (uintmax_t)base, minbus, maxbus); 268 } 269 270 if (!mcfg_enable) 271 return 0; 272 273 if (minbus != 0) 274 return 0; 275 276 if (bootverbose) 277 kprintf("PCIe: Using Memory Mapped configuration\n"); 278 279 pcie_base = (vm_offset_t)pmap_mapdev_uncacheable(base, 280 ((unsigned)maxbus + 1) << 20); 281 pcie_minbus = minbus; 282 pcie_maxbus = maxbus; 283 cfgmech = CFGMECH_PCIE; 284 285 /* 286 * On some AMD systems, some of the devices on bus 0 are 287 * inaccessible using memory-mapped PCI config access. Walk 288 * bus 0 looking for such devices. For these devices, we will 289 * fall back to using type 1 config access instead. 290 */ 291 if (pci_cfgregopen() != 0) { 292 int slot; 293 294 for (slot = 0; slot <= PCI_SLOTMAX; slot++) { 295 uint32_t val1, val2; 296 297 val1 = pcireg_cfgread(0, slot, 0, 0, 4); 298 if (val1 == 0xffffffff) 299 continue; 300 301 val2 = pciereg_cfgread(0, slot, 0, 0, 4); 302 if (val2 != val1) 303 pcie_badslots |= (1 << slot); 304 } 305 } 306 return 1; 307 } 308 309 #define PCIE_VADDR(base, reg, bus, slot, func) \ 310 ((base) + \ 311 ((((bus) & 0xff) << 20) | \ 312 (((slot) & 0x1f) << 15) | \ 313 (((func) & 0x7) << 12) | \ 314 ((reg) & 0xfff))) 315 316 static int 317 pciereg_cfgread(int bus, unsigned slot, unsigned func, unsigned reg, 318 unsigned bytes) 319 { 320 volatile vm_offset_t va; 321 int data = -1; 322 323 if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 324 func > PCI_FUNCMAX || reg > PCIE_REGMAX) 325 return -1; 326 327 va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 328 329 switch (bytes) { 330 case 4: 331 data = *(volatile uint32_t *)(va); 332 break; 333 case 2: 334 data = *(volatile uint16_t *)(va); 335 break; 336 case 1: 337 data = *(volatile uint8_t *)(va); 338 break; 339 } 340 return data; 341 } 342 343 static void 344 pciereg_cfgwrite(int bus, unsigned slot, unsigned func, unsigned reg, int data, 345 unsigned bytes) 346 { 347 volatile vm_offset_t va; 348 349 if (bus < pcie_minbus || bus > pcie_maxbus || slot > PCI_SLOTMAX || 350 func > PCI_FUNCMAX || reg > PCIE_REGMAX) 351 return; 352 353 va = PCIE_VADDR(pcie_base, reg, bus, slot, func); 354 355 switch (bytes) { 356 case 4: 357 *(volatile uint32_t *)(va) = data; 358 break; 359 case 2: 360 *(volatile uint16_t *)(va) = data; 361 break; 362 case 1: 363 *(volatile uint8_t *)(va) = data; 364 break; 365 } 366 } 367