pci_cfgreg.c revision 67185
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 67185 2000-10-16 07:25:08Z imp $ 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 */ 137int 138pci_cfgintr(int bus, int device, int pin) 139{ 140 struct PIR_entry *pe; 141 int i; 142 143 if ((bus < 0) || (bus > 255) || (device < 0) || (device > 255) || (pin < 1) || (pin > 4)) { 144 printf("bus %d pin %d device %d, returning 255\n", bus, pin, device); 145 return(255); 146 } 147 148 /* 149 * Scan the entry table for a contender 150 */ 151 printf("bus %d device %d\n", bus, device); 152 for (i = 0, pe = pci_route_table; i < pci_route_count; i++, pe++) { 153 printf("pe_bus %d pe_device %d\n", pe->pe_bus, pe->pe_device); 154 if ((bus != pe->pe_bus) || (device != pe->pe_device)) 155 continue; 156 if (!powerof2(pe->pe_intpin[pin - 1].irqs)) { 157 printf("pci_cfgintr: %d:%d:%c is not routed to a unique interrupt\n", bus, device, 'A' + pin - 1); 158 break; 159 } 160 printf("pci_cfgintr: %d:%d:%c routed to irq %d\n", bus, device, 'A' + pin - 1, ffs(pe->pe_intpin[pin - 1].irqs)); 161 return(ffs(pe->pe_intpin[pin - 1].irqs)); 162 } 163 return(255); 164} 165 166 167/* 168 * Config space access using BIOS functions 169 */ 170static int 171pcibios_cfgread(int bus, int slot, int func, int reg, int bytes) 172{ 173 struct bios_regs args; 174 u_int mask; 175 176 switch(bytes) { 177 case 1: 178 args.eax = PCIBIOS_READ_CONFIG_BYTE; 179 mask = 0xff; 180 break; 181 case 2: 182 args.eax = PCIBIOS_READ_CONFIG_WORD; 183 mask = 0xffff; 184 break; 185 case 4: 186 args.eax = PCIBIOS_READ_CONFIG_DWORD; 187 mask = 0xffffffff; 188 break; 189 default: 190 return(-1); 191 } 192 args.ebx = (bus << 8) | (slot << 3) | func; 193 args.edi = reg; 194 bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); 195 /* check call results? */ 196 return(args.ecx & mask); 197} 198 199static void 200pcibios_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 201{ 202 struct bios_regs args; 203 204 switch(bytes) { 205 case 1: 206 args.eax = PCIBIOS_WRITE_CONFIG_BYTE; 207 break; 208 case 2: 209 args.eax = PCIBIOS_WRITE_CONFIG_WORD; 210 break; 211 case 4: 212 args.eax = PCIBIOS_WRITE_CONFIG_DWORD; 213 break; 214 default: 215 return; 216 } 217 args.ebx = (bus << 8) | (slot << 3) | func; 218 args.ecx = data; 219 args.edi = reg; 220 bios32(&args, PCIbios.ventry, GSEL(GCODE_SEL, SEL_KPL)); 221} 222 223/* 224 * Determine whether there is a PCI BIOS present 225 */ 226static int 227pcibios_cfgopen(void) 228{ 229 /* check for a found entrypoint */ 230 return(PCIbios.entry != 0); 231} 232 233/* 234 * Configuration space access using direct register operations 235 */ 236 237/* enable configuration space accesses and return data port address */ 238static int 239pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) 240{ 241 int dataport = 0; 242 243 if (bus <= PCI_BUSMAX 244 && slot < devmax 245 && func <= PCI_FUNCMAX 246 && reg <= PCI_REGMAX 247 && bytes != 3 248 && (unsigned) bytes <= 4 249 && (reg & (bytes -1)) == 0) { 250 switch (cfgmech) { 251 case 1: 252 outl(CONF1_ADDR_PORT, (1 << 31) 253 | (bus << 16) | (slot << 11) 254 | (func << 8) | (reg & ~0x03)); 255 dataport = CONF1_DATA_PORT + (reg & 0x03); 256 break; 257 case 2: 258 outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); 259 outb(CONF2_FORWARD_PORT, bus); 260 dataport = 0xc000 | (slot << 8) | reg; 261 break; 262 } 263 } 264 return (dataport); 265} 266 267/* disable configuration space accesses */ 268static void 269pci_cfgdisable(void) 270{ 271 switch (cfgmech) { 272 case 1: 273 outl(CONF1_ADDR_PORT, 0); 274 break; 275 case 2: 276 outb(CONF2_ENABLE_PORT, 0); 277 outb(CONF2_FORWARD_PORT, 0); 278 break; 279 } 280} 281 282static int 283pcireg_cfgread(int bus, int slot, int func, int reg, int bytes) 284{ 285 int data = -1; 286 int port; 287 288 port = pci_cfgenable(bus, slot, func, reg, bytes); 289 290 if (port != 0) { 291 switch (bytes) { 292 case 1: 293 data = inb(port); 294 break; 295 case 2: 296 data = inw(port); 297 break; 298 case 4: 299 data = inl(port); 300 break; 301 } 302 pci_cfgdisable(); 303 } 304 return (data); 305} 306 307static void 308pcireg_cfgwrite(int bus, int slot, int func, int reg, int data, int bytes) 309{ 310 int port; 311 312 port = pci_cfgenable(bus, slot, func, reg, bytes); 313 if (port != 0) { 314 switch (bytes) { 315 case 1: 316 outb(port, data); 317 break; 318 case 2: 319 outw(port, data); 320 break; 321 case 4: 322 outl(port, data); 323 break; 324 } 325 pci_cfgdisable(); 326 } 327} 328 329/* check whether the configuration mechanism has been correctly identified */ 330static int 331pci_cfgcheck(int maxdev) 332{ 333 u_char device; 334 335 if (bootverbose) 336 printf("pci_cfgcheck:\tdevice "); 337 338 for (device = 0; device < maxdev; device++) { 339 unsigned id, class, header; 340 if (bootverbose) 341 printf("%d ", device); 342 343 id = inl(pci_cfgenable(0, device, 0, 0, 4)); 344 if (id == 0 || id == -1) 345 continue; 346 347 class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; 348 if (bootverbose) 349 printf("[class=%06x] ", class); 350 if (class == 0 || (class & 0xf870ff) != 0) 351 continue; 352 353 header = inb(pci_cfgenable(0, device, 0, 14, 1)); 354 if (bootverbose) 355 printf("[hdr=%02x] ", header); 356 if ((header & 0x7e) != 0) 357 continue; 358 359 if (bootverbose) 360 printf("is there (id=%08x)\n", id); 361 362 pci_cfgdisable(); 363 return (1); 364 } 365 if (bootverbose) 366 printf("-- nothing found\n"); 367 368 pci_cfgdisable(); 369 return (0); 370} 371 372static int 373pcireg_cfgopen(void) 374{ 375 unsigned long mode1res,oldval1; 376 unsigned char mode2res,oldval2; 377 378 oldval1 = inl(CONF1_ADDR_PORT); 379 380 if (bootverbose) { 381 printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", 382 oldval1); 383 } 384 385 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 386 387 cfgmech = 1; 388 devmax = 32; 389 390 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 391 outb(CONF1_ADDR_PORT +3, 0); 392 mode1res = inl(CONF1_ADDR_PORT); 393 outl(CONF1_ADDR_PORT, oldval1); 394 395 if (bootverbose) 396 printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", 397 mode1res, CONF1_ENABLE_CHK); 398 399 if (mode1res) { 400 if (pci_cfgcheck(32)) 401 return (cfgmech); 402 } 403 404 outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 405 mode1res = inl(CONF1_ADDR_PORT); 406 outl(CONF1_ADDR_PORT, oldval1); 407 408 if (bootverbose) 409 printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", 410 mode1res, CONF1_ENABLE_CHK1); 411 412 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 413 if (pci_cfgcheck(32)) 414 return (cfgmech); 415 } 416 } 417 418 oldval2 = inb(CONF2_ENABLE_PORT); 419 420 if (bootverbose) { 421 printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", 422 oldval2); 423 } 424 425 if ((oldval2 & 0xf0) == 0) { 426 427 cfgmech = 2; 428 devmax = 16; 429 430 outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 431 mode2res = inb(CONF2_ENABLE_PORT); 432 outb(CONF2_ENABLE_PORT, oldval2); 433 434 if (bootverbose) 435 printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", 436 mode2res, CONF2_ENABLE_CHK); 437 438 if (mode2res == CONF2_ENABLE_RES) { 439 if (bootverbose) 440 printf("pci_open(2a):\tnow trying mechanism 2\n"); 441 442 if (pci_cfgcheck(16)) 443 return (cfgmech); 444 } 445 } 446 447 cfgmech = 0; 448 devmax = 0; 449 return (cfgmech); 450} 451 452