pci_cfgreg.c revision 67311
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 * $FreeBSD: head/sys/amd64/pci/pci_cfgreg.c 67311 2000-10-19 08:06:50Z msmith $ 29 * 30 */ 31 32#include <sys/param.h> /* XXX trim includes */ 33#include <sys/systm.h> 34#include <sys/bus.h> 35#include <sys/kernel.h> 36#include <sys/module.h> 37#include <sys/malloc.h> 38#include <vm/vm.h> 39#include <vm/pmap.h> 40#include <machine/md_var.h> 41#include <pci/pcivar.h> 42#include <pci/pcireg.h> 43#include <isa/isavar.h> 44#include <machine/nexusvar.h> 45#include <machine/pci_cfgreg.h> 46#include <machine/segments.h> 47#include <machine/pc/bios.h> 48 49#include "pcib_if.h" 50 51static int cfgmech; 52static int devmax; 53static int usebios; 54 55static int pcibios_cfgread(int bus, int slot, int func, int reg, int bytes); 56static void pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 57static int pcibios_cfgopen(void); 58static int pcireg_cfgread(int bus, int slot, int func, int reg, int bytes); 59static void pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes); 60static int pcireg_cfgopen(void); 61 62static struct PIR_entry *pci_route_table; 63static int pci_route_count; 64 65/* 66 * Initialise access to PCI configuration space 67 */ 68int 69pci_cfgregopen(void) 70{ 71 static int opened = 0; 72 u_long sigaddr; 73 static struct PIR_table *pt; 74 u_int8_t ck, *cv; 75 int i; 76 77 if (opened) 78 return(1); 79 80 if (pcibios_cfgopen() != 0) { 81 usebios = 1; 82 } else if (pcireg_cfgopen() != 0) { 83 usebios = 0; 84 } else { 85 return(0); 86 } 87 88 /* 89 * Look for the interrupt routing table. 90 */ 91 /* XXX use PCI BIOS if it's available */ 92 93 if ((pt == NULL) && ((sigaddr = bios_sigsearch(0, "$PIR", 4, 16, 0)) != 0)) { 94 pt = (struct PIR_table *)(uintptr_t)BIOS_PADDRTOVADDR(sigaddr); 95 for (cv = (u_int8_t *)pt, ck = 0, i = 0; i < (pt->pt_header.ph_length); i++) { 96 ck += cv[i]; 97 } 98 if (ck == 0) { 99 pci_route_table = &pt->pt_entry[0]; 100 pci_route_count = (pt->pt_header.ph_length - sizeof(struct PIR_header)) / sizeof(struct PIR_entry); 101 printf("Using $PIR table, %d entries at %p\n", pci_route_count, pci_route_table); 102 } 103 } 104 105 opened = 1; 106 return(1); 107} 108 109/* 110 * Read configuration space register 111 */ 112u_int32_t 113pci_cfgregread(int bus, int slot, int func, int reg, int bytes) 114{ 115 return(usebios ? 116 pcibios_cfgread(bus, slot, func, reg, bytes) : 117 pcireg_cfgread(bus, slot, func, reg, bytes)); 118} 119 120/* 121 * Write configuration space register 122 */ 123void 124pci_cfgregwrite(int bus, int slot, int func, int reg, u_int32_t data, int bytes) 125{ 126 return(usebios ? 127 pcibios_cfgwrite(bus, slot, func, reg, data, bytes) : 128 pcireg_cfgwrite(bus, slot, func, reg, data, bytes)); 129} 130 131/* 132 * Route a PCI interrupt 133 * 134 * XXX this needs to learn to actually route uninitialised interrupts as well 135 * as just returning interrupts for stuff that's already initialised. 136 * 137 * XXX we don't do anything "right" with the function number in the PIR table 138 * (because the consumer isn't currently passing it in). 139 */ 140int 141pci_cfgintr(int bus, int device, int pin) 142{ 143 struct PIR_entry *pe; 144 int i, irq; 145 struct bios_regs args; 146 147 if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) || 148 (pin < 1) || (pin > 4)) 149 return(255); 150 151 /* 152 * Scan the entry table for a contender 153 */ 154 for (i = 0, pe = pci_route_table; i < pci_route_count; i++, pe++) { 155 if ((bus != pe->pe_bus) || (device != pe->pe_device)) 156 continue; 157 if (!powerof2(pe->pe_intpin[pin - 1].irqs)) { 158 printf("pci_cfgintr: %d:%d:%c is not routed to a unique interrupt\n", 159 bus, device, 'A' + pin - 1); 160 break; 161 } 162 irq = ffs(pe->pe_intpin[pin - 1].irqs) - 1; 163 printf("pci_cfgintr: %d:%d:%c routed to irq %d\n", 164 bus, device, 'A' + pin - 1, irq); 165 166 /* 167 * Ask the BIOS to route the interrupt 168 */ 169 args.eax = PCIBIOS_ROUTE_INTERRUPT; 170 args.ebx = (bus << 8) | (device << 3); 171 args.ecx = (irq << 8) | (0xa + pin - 1); /* pin value is 0xa - 0xd */ 172 bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); 173 174 /* XXX if it fails, we should smack the router hardware directly */ 175 176 return(irq); 177 } 178 return(255); 179} 180 181 182/* 183 * Config space access using BIOS functions 184 */ 185static int 186pcibios_cfgread(int bus, int slot, int func, int reg, int bytes) 187{ 188 struct bios_regs args; 189 u_int mask; 190 191 switch(bytes) { 192 case 1: 193 args.eax = PCIBIOS_READ_CONFIG_BYTE; 194 mask = 0xff; 195 break; 196 case 2: 197 args.eax = PCIBIOS_READ_CONFIG_WORD; 198 mask = 0xffff; 199 break; 200 case 4: 201 args.eax = PCIBIOS_READ_CONFIG_DWORD; 202 mask = 0xffffffff; 203 break; 204 default: 205 return(-1); 206 } 207 args.ebx = (bus << 8) | (slot << 3) | func; 208 args.edi = reg; 209 bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); 210 /* check call results? */ 211 return(args.ecx & mask); 212} 213 214static void 215pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 216{ 217 struct bios_regs args; 218 219 switch(bytes) { 220 case 1: 221 args.eax = PCIBIOS_WRITE_CONFIG_BYTE; 222 break; 223 case 2: 224 args.eax = PCIBIOS_WRITE_CONFIG_WORD; 225 break; 226 case 4: 227 args.eax = PCIBIOS_WRITE_CONFIG_DWORD; 228 break; 229 default: 230 return; 231 } 232 args.ebx = (bus << 8) | (slot << 3) | func; 233 args.ecx = data; 234 args.edi = reg; 235 bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); 236} 237 238/* 239 * Determine whether there is a PCI BIOS present 240 */ 241static int 242pcibios_cfgopen(void) 243{ 244 /* check for a found entrypoint */ 245 return(PCIbios.entry != 0); 246} 247 248/* 249 * Configuration space access using direct register operations 250 */ 251 252/* enable configuration space accesses and return data port address */ 253static int 254pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 255{ 256 int dataport = 0; 257 258 if (bus <= PCI_BUSMAX 259 && slot < devmax 260 && func <= PCI_FUNCMAX 261 && reg <= PCI_REGMAX 262 && bytes != 3 263 && (unsigned) bytes <= 4 264 && (reg & (bytes -1)) == 0) { 265 switch (cfgmech) { 266 case 1: 267 outl(CONF1_ADDR_PORT, (1 << 31) 268 | (bus << 16) | (slot << 11) 269 | (func << 8) | (reg & ~0x03)); 270 dataport = CONF1_DATA_PORT + (reg & 0x03); 271 break; 272 case 2: 273 outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 274 outb(CONF2_FORWARD_PORT, bus); 275 dataport = 0xc000 | (slot << 8) | reg; 276 break; 277 } 278 } 279 return (dataport); 280} 281 282/* disable configuration space accesses */ 283static void 284pci_cfgdisable(void) 285{ 286 switch (cfgmech) { 287 case 1: 288 outl(CONF1_ADDR_PORT, 0); 289 break; 290 case 2: 291 outb(CONF2_ENABLE_PORT, 0); 292 outb(CONF2_FORWARD_PORT, 0); 293 break; 294 } 295} 296 297static int 298pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 299{ 300 int data = -1; 301 int port; 302 303 port = pci_cfgenable(bus, slot, func, reg, bytes); 304 305 if (port != 0) { 306 switch (bytes) { 307 case 1: 308 data = inb(port); 309 break; 310 case 2: 311 data = inw(port); 312 break; 313 case 4: 314 data = inl(port); 315 break; 316 } 317 pci_cfgdisable(); 318 } 319 return (data); 320} 321 322static void 323pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 324{ 325 int port; 326 327 port = pci_cfgenable(bus, slot, func, reg, bytes); 328 if (port != 0) { 329 switch (bytes) { 330 case 1: 331 outb(port, data); 332 break; 333 case 2: 334 outw(port, data); 335 break; 336 case 4: 337 outl(port, data); 338 break; 339 } 340 pci_cfgdisable(); 341 } 342} 343 344/* check whether the configuration mechanism has been correctly identified */ 345static int 346pci_cfgcheck(int maxdev) 347{ 348 u_char device; 349 350 if (bootverbose) 351 printf("pci_cfgcheck:\tdevice "); 352 353 for (device = 0; device < maxdev; device++) { 354 unsigned id, class, header; 355 if (bootverbose) 356 printf("%d ", device); 357 358 id = inl(pci_cfgenable(0, device, 0, 0, 4)); 359 if (id == 0 || id == -1) 360 continue; 361 362 class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; 363 if (bootverbose) 364 printf("[class=%06x] ", class); 365 if (class == 0 || (class & 0xf870ff) != 0) 366 continue; 367 368 header = inb(pci_cfgenable(0, device, 0, 14, 1)); 369 if (bootverbose) 370 printf("[hdr=%02x] ", header); 371 if ((header & 0x7e) != 0) 372 continue; 373 374 if (bootverbose) 375 printf("is there (id=%08x)\n", id); 376 377 pci_cfgdisable(); 378 return (1); 379 } 380 if (bootverbose) 381 printf("-- nothing found\n"); 382 383 pci_cfgdisable(); 384 return (0); 385} 386 387static int 388pcireg_cfgopen(void) 389{ 390 unsigned long mode1res,oldval1; 391 unsigned char mode2res,oldval2; 392 393 oldval1 = inl(CONF1_ADDR_PORT); 394 395 if (bootverbose) { 396 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", 397 oldval1); 398 } 399 400 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 401 402 cfgmech = 1; 403 devmax = 32; 404 405 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 406 outb(CONF1_ADDR_PORT +3, 0); 407 mode1res = inl(CONF1_ADDR_PORT); 408 outl(CONF1_ADDR_PORT, oldval1); 409 410 if (bootverbose) 411 printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", 412 mode1res, CONF1_ENABLE_CHK); 413 414 if (mode1res) { 415 if (pci_cfgcheck(32)) 416 return (cfgmech); 417 } 418 419 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 420 mode1res = inl(CONF1_ADDR_PORT); 421 outl(CONF1_ADDR_PORT, oldval1); 422 423 if (bootverbose) 424 printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", 425 mode1res, CONF1_ENABLE_CHK1); 426 427 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 428 if (pci_cfgcheck(32)) 429 return (cfgmech); 430 } 431 } 432 433 oldval2 = inb(CONF2_ENABLE_PORT); 434 435 if (bootverbose) { 436 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 437 oldval2); 438 } 439 440 if ((oldval2 & 0xf0) == 0) { 441 442 cfgmech = 2; 443 devmax = 16; 444 445 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 446 mode2res = inb(CONF2_ENABLE_PORT); 447 outb(CONF2_ENABLE_PORT, oldval2); 448 449 if (bootverbose) 450 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 451 mode2res, CONF2_ENABLE_CHK); 452 453 if (mode2res == CONF2_ENABLE_RES) { 454 if (bootverbose) 455 printf("pci_open(2a):\tnow trying mechanism 2\n"); 456 457 if (pci_cfgcheck(16)) 458 return (cfgmech); 459 } 460 } 461 462 cfgmech = 0; 463 devmax = 0; 464 return (cfgmech); 465} 466 467