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