1 /************************************************************************** 2 ** 3 ** $Id: pcibus.c,v 1.26 1996/06/18 01:22:28 bde Exp $ 4 ** 5 ** pci bus subroutines for i386 architecture. 6 ** 7 ** FreeBSD 8 ** 9 **------------------------------------------------------------------------- 10 ** 11 ** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved. 12 ** 13 ** Redistribution and use in source and binary forms, with or without 14 ** modification, are permitted provided that the following conditions 15 ** are met: 16 ** 1. Redistributions of source code must retain the above copyright 17 ** notice, this list of conditions and the following disclaimer. 18 ** 2. Redistributions in binary form must reproduce the above copyright 19 ** notice, this list of conditions and the following disclaimer in the 20 ** documentation and/or other materials provided with the distribution. 21 ** 3. The name of the author may not be used to endorse or promote products 22 ** derived from this software without specific prior written permission. 23 ** 24 ** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 25 ** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 26 ** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 27 ** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 28 ** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 29 ** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 30 ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 31 ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 32 ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 33 ** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 34 ** 35 *************************************************************************** 36 */ 37 38 #include "vector.h" 39 40 #include <sys/param.h> 41 #include <sys/systm.h> 42 #include <sys/kernel.h> 43 44 #include <i386/isa/icu.h> 45 #include <i386/isa/isa_device.h> 46 47 #include <pci/pcivar.h> 48 #include <pci/pcireg.h> 49 #include <pci/pcibus.h> 50 51 /*----------------------------------------------------------------- 52 ** 53 ** The following functions are provided by the pci bios. 54 ** They are used only by the pci configuration. 55 ** 56 ** pcibus_setup(): 57 ** Probes for a pci system. 58 ** Sets pci_maxdevice and pci_mechanism. 59 ** 60 ** pcibus_tag(): 61 ** Creates a handle for pci configuration space access. 62 ** This handle is given to the read/write functions. 63 ** 64 ** pcibus_ftag(): 65 ** Creates a modified handle. 66 ** 67 ** pcibus_read(): 68 ** Read a long word from the pci configuration space. 69 ** Requires a tag (from pcitag) and the register 70 ** number (should be a long word alligned one). 71 ** 72 ** pcibus_write(): 73 ** Writes a long word to the pci configuration space. 74 ** Requires a tag (from pcitag), the register number 75 ** (should be a long word alligned one), and a value. 76 ** 77 ** pcibus_regirq(): 78 ** Register an interupt handler for a pci device. 79 ** Requires a tag (from pcitag), the register number 80 ** (should be a long word alligned one), and a value. 81 ** 82 **----------------------------------------------------------------- 83 */ 84 85 static int 86 pcibus_check (void); 87 88 static void 89 pcibus_setup (void); 90 91 static pcici_t 92 pcibus_tag (u_char bus, u_char device, u_char func); 93 94 static pcici_t 95 pcibus_ftag (pcici_t tag, u_char func); 96 97 static u_long 98 pcibus_read (pcici_t tag, u_long reg); 99 100 static void 101 pcibus_write (pcici_t tag, u_long reg, u_long data); 102 103 static int 104 pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr); 105 106 static int 107 pcibus_ihandler_detach (int irq, inthand2_t *func); 108 109 static int 110 pcibus_imask_include (int irq, unsigned* maskptr); 111 112 static int 113 pcibus_imask_exclude (int irq, unsigned* maskptr); 114 115 static struct pcibus i386pci = { 116 "pci", 117 pcibus_setup, 118 pcibus_tag, 119 pcibus_ftag, 120 pcibus_read, 121 pcibus_write, 122 ICU_LEN, 123 pcibus_ihandler_attach, 124 pcibus_ihandler_detach, 125 pcibus_imask_include, 126 pcibus_imask_exclude, 127 }; 128 129 /* 130 ** Announce structure to generic driver 131 */ 132 133 DATA_SET (pcibus_set, i386pci); 134 135 /*-------------------------------------------------------------------- 136 ** 137 ** Determine configuration mode 138 ** 139 **-------------------------------------------------------------------- 140 */ 141 142 143 #define CONF1_ADDR_PORT 0x0cf8 144 #define CONF1_DATA_PORT 0x0cfc 145 146 #define CONF1_ENABLE 0x80000000ul 147 #define CONF1_ENABLE_CHK 0x80000000ul 148 #define CONF1_ENABLE_MSK 0x7ff00000ul 149 #define CONF1_ENABLE_CHK1 0xff000001ul 150 #define CONF1_ENABLE_MSK1 0x80000001ul 151 #define CONF1_ENABLE_RES1 0x80000000ul 152 153 #define CONF2_ENABLE_PORT 0x0cf8 154 #ifdef PC98 155 #define CONF2_FORWARD_PORT 0x0cf9 156 #else 157 #define CONF2_FORWARD_PORT 0x0cfa 158 #endif 159 160 #define CONF2_ENABLE_CHK 0x0e 161 #define CONF2_ENABLE_RES 0x0e 162 163 static int 164 pcibus_check (void) 165 { 166 u_char device; 167 168 if (bootverbose) printf ("pcibus_check:\tdevice "); 169 170 for (device = 0; device < pci_maxdevice; device++) { 171 unsigned long id; 172 if (bootverbose) 173 printf ("%d ", device); 174 id = pcibus_read (pcibus_tag (0,device,0), 0); 175 if (id && id != 0xfffffffful) { 176 if (bootverbose) printf ("is there (id=%08lx)\n", id); 177 return 1; 178 } 179 } 180 if (bootverbose) 181 printf ("-- nothing found\n"); 182 return 0; 183 } 184 185 static void 186 pcibus_setup (void) 187 { 188 unsigned long mode1res,oldval1; 189 unsigned char mode2res,oldval2; 190 191 oldval1 = inl (CONF1_ADDR_PORT); 192 193 if (bootverbose) { 194 printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); 195 } 196 197 /*--------------------------------------- 198 ** Assume configuration mechanism 1 for now ... 199 **--------------------------------------- 200 */ 201 202 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 203 204 pci_mechanism = 1; 205 pci_maxdevice = 32; 206 207 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 208 outb (CONF1_ADDR_PORT +3, 0); 209 mode1res = inl (CONF1_ADDR_PORT); 210 outl (CONF1_ADDR_PORT, oldval1); 211 212 if (bootverbose) 213 printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n", 214 mode1res, CONF1_ENABLE_CHK); 215 216 if (mode1res) { 217 if (pcibus_check()) 218 return; 219 }; 220 221 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 222 mode1res = inl(CONF1_ADDR_PORT); 223 outl (CONF1_ADDR_PORT, oldval1); 224 225 if (bootverbose) 226 printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n", 227 mode1res, CONF1_ENABLE_CHK1); 228 229 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 230 if (pcibus_check()) 231 return; 232 }; 233 } 234 235 /*--------------------------------------- 236 ** Try configuration mechanism 2 ... 237 **--------------------------------------- 238 */ 239 240 oldval2 = inb (CONF2_ENABLE_PORT); 241 242 if (bootverbose) { 243 printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); 244 } 245 246 if ((oldval2 & 0xf0) == 0) { 247 248 pci_mechanism = 2; 249 pci_maxdevice = 16; 250 251 outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 252 mode2res = inb(CONF2_ENABLE_PORT); 253 outb (CONF2_ENABLE_PORT, oldval2); 254 255 if (bootverbose) 256 printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n", 257 mode2res, CONF2_ENABLE_CHK); 258 259 if (mode2res == CONF2_ENABLE_RES) { 260 if (bootverbose) 261 printf ("pcibus_setup(2a):\tnow trying mechanism 2\n"); 262 263 if (pcibus_check()) 264 return; 265 } 266 } 267 268 /*--------------------------------------- 269 ** No PCI bus host bridge found 270 **--------------------------------------- 271 */ 272 273 pci_mechanism = 0; 274 pci_maxdevice = 0; 275 } 276 277 /*-------------------------------------------------------------------- 278 ** 279 ** Build a pcitag from bus, device and function number 280 ** 281 **-------------------------------------------------------------------- 282 */ 283 284 static pcici_t 285 pcibus_tag (unsigned char bus, unsigned char device, unsigned char func) 286 { 287 pcici_t tag; 288 289 tag.cfg1 = 0; 290 if (func >= 8) return tag; 291 292 switch (pci_mechanism) { 293 294 case 1: 295 if (device < 32) { 296 tag.cfg1 = CONF1_ENABLE 297 | (((u_long) bus ) << 16ul) 298 | (((u_long) device) << 11ul) 299 | (((u_long) func ) << 8ul); 300 } 301 break; 302 case 2: 303 if (device < 16) { 304 tag.cfg2.port = 0xc000 | (device << 8ul); 305 tag.cfg2.enable = 0xf0 | (func << 1ul); 306 tag.cfg2.forward = bus; 307 } 308 break; 309 }; 310 return tag; 311 } 312 313 static pcici_t 314 pcibus_ftag (pcici_t tag, u_char func) 315 { 316 switch (pci_mechanism) { 317 318 case 1: 319 tag.cfg1 &= ~0x700ul; 320 tag.cfg1 |= (((u_long) func) << 8ul); 321 break; 322 case 2: 323 tag.cfg2.enable = 0xf0 | (func << 1ul); 324 break; 325 }; 326 return tag; 327 } 328 329 /*-------------------------------------------------------------------- 330 ** 331 ** Read register from configuration space. 332 ** 333 **-------------------------------------------------------------------- 334 */ 335 336 static u_long 337 pcibus_read (pcici_t tag, u_long reg) 338 { 339 u_long addr, data = 0; 340 341 if (!tag.cfg1) return (0xfffffffful); 342 343 switch (pci_mechanism) { 344 345 case 1: 346 addr = tag.cfg1 | (reg & 0xfc); 347 #ifdef PCI_DEBUG 348 printf ("pci_conf_read(1): addr=%x ", addr); 349 #endif 350 outl (CONF1_ADDR_PORT, addr); 351 data = inl (CONF1_DATA_PORT); 352 outl (CONF1_ADDR_PORT, 0 ); 353 break; 354 355 case 2: 356 addr = tag.cfg2.port | (reg & 0xfc); 357 #ifdef PCI_DEBUG 358 printf ("pci_conf_read(2): addr=%x ", addr); 359 #endif 360 outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); 361 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 362 363 data = inl ((u_short) addr); 364 365 outb (CONF2_ENABLE_PORT, 0); 366 outb (CONF2_FORWARD_PORT, 0); 367 break; 368 }; 369 370 #ifdef PCI_DEBUG 371 printf ("data=%x\n", data); 372 #endif 373 374 return (data); 375 } 376 377 /*-------------------------------------------------------------------- 378 ** 379 ** Write register into configuration space. 380 ** 381 **-------------------------------------------------------------------- 382 */ 383 384 static void 385 pcibus_write (pcici_t tag, u_long reg, u_long data) 386 { 387 u_long addr; 388 389 if (!tag.cfg1) return; 390 391 switch (pci_mechanism) { 392 393 case 1: 394 addr = tag.cfg1 | (reg & 0xfc); 395 #ifdef PCI_DEBUG 396 printf ("pci_conf_write(1): addr=%x data=%x\n", 397 addr, data); 398 #endif 399 outl (CONF1_ADDR_PORT, addr); 400 outl (CONF1_DATA_PORT, data); 401 outl (CONF1_ADDR_PORT, 0 ); 402 break; 403 404 case 2: 405 addr = tag.cfg2.port | (reg & 0xfc); 406 #ifdef PCI_DEBUG 407 printf ("pci_conf_write(2): addr=%x data=%x\n", 408 addr, data); 409 #endif 410 outb (CONF2_ENABLE_PORT, tag.cfg2.enable); 411 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 412 413 outl ((u_short) addr, data); 414 415 outb (CONF2_ENABLE_PORT, 0); 416 outb (CONF2_FORWARD_PORT, 0); 417 break; 418 }; 419 } 420 421 /*----------------------------------------------------------------------- 422 ** 423 ** Register an interupt handler for a pci device. 424 ** 425 **----------------------------------------------------------------------- 426 */ 427 428 static int 429 pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr) 430 { 431 char buf[16]; 432 char *cp; 433 int free_id, id, result; 434 435 sprintf(buf, "pci irq%d", irq); 436 for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) { 437 if (strcmp(cp, buf) == 0) 438 break; 439 if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0) 440 free_id = id; 441 while (*cp++ != '\0') 442 ; 443 } 444 if (id == NR_DEVICES) { 445 id = free_id; 446 if (id == 0) { 447 /* 448 * All pci irq counters are in use, perhaps because 449 * config is old so there aren't any. Abuse the 450 * clk0 counter. 451 */ 452 printf ( 453 "pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n", 454 irq); 455 } 456 } 457 result = register_intr( 458 irq, /* isa irq */ 459 id, /* device id */ 460 0, /* flags? */ 461 func, /* handler */ 462 maskptr, /* mask pointer */ 463 arg); /* handler arg */ 464 465 if (result) { 466 printf ("@@@ pcibus_ihandler_attach: result=%d\n", result); 467 return (result); 468 }; 469 update_intr_masks(); 470 471 INTREN ((1ul<<irq)); 472 return (0); 473 } 474 475 static int 476 pcibus_ihandler_detach (int irq, inthand2_t *func) 477 { 478 int result; 479 480 INTRDIS ((1ul<<irq)); 481 482 result = unregister_intr (irq, func); 483 484 if (result) 485 printf ("@@@ pcibus_ihandler_detach: result=%d\n", result); 486 487 update_intr_masks(); 488 489 return (result); 490 } 491 492 static int 493 pcibus_imask_include (int irq, unsigned* maskptr) 494 { 495 unsigned mask; 496 497 if (!maskptr) return (0); 498 499 mask = 1ul << irq; 500 501 if (*maskptr & mask) 502 return (-1); 503 504 INTRMASK (*maskptr, mask); 505 update_intr_masks(); 506 507 return (0); 508 } 509 510 static int 511 pcibus_imask_exclude (int irq, unsigned* maskptr) 512 { 513 unsigned mask; 514 515 if (!maskptr) return (0); 516 517 mask = 1ul << irq; 518 519 if (! (*maskptr & mask)) 520 return (-1); 521 522 *maskptr &= ~mask; 523 update_intr_masks(); 524 525 return (0); 526 } 527