1 /************************************************************************** 2 ** 3 ** $Id: pcibus.c,v 1.25 1996/06/13 21:50:41 se 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 #define CONF2_FORWARD_PORT 0x0cfa 155 156 #define CONF2_ENABLE_CHK 0x0e 157 #define CONF2_ENABLE_RES 0x0e 158 159 static int 160 pcibus_check (void) 161 { 162 u_char device; 163 164 if (bootverbose) printf ("pcibus_check:\tdevice "); 165 166 for (device = 0; device < pci_maxdevice; device++) { 167 unsigned long id; 168 if (bootverbose) 169 printf ("%d ", device); 170 id = pcibus_read (pcibus_tag (0,device,0), 0); 171 if (id && id != 0xfffffffful) { 172 if (bootverbose) printf ("is there (id=%08lx)\n", id); 173 return 1; 174 } 175 } 176 if (bootverbose) 177 printf ("-- nothing found\n"); 178 return 0; 179 } 180 181 static void 182 pcibus_setup (void) 183 { 184 unsigned long mode1res,oldval1; 185 unsigned char mode2res,oldval2; 186 187 oldval1 = inl (CONF1_ADDR_PORT); 188 189 if (bootverbose) { 190 printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); 191 } 192 193 /*--------------------------------------- 194 ** Assume configuration mechanism 1 for now ... 195 **--------------------------------------- 196 */ 197 198 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 199 200 pci_mechanism = 1; 201 pci_maxdevice = 32; 202 203 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 204 outb (CONF1_ADDR_PORT +3, 0); 205 mode1res = inl (CONF1_ADDR_PORT); 206 outl (CONF1_ADDR_PORT, oldval1); 207 208 if (bootverbose) 209 printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n", 210 mode1res, CONF1_ENABLE_CHK); 211 212 if (mode1res) { 213 if (pcibus_check()) 214 return; 215 }; 216 217 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 218 mode1res = inl(CONF1_ADDR_PORT); 219 outl (CONF1_ADDR_PORT, oldval1); 220 221 if (bootverbose) 222 printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n", 223 mode1res, CONF1_ENABLE_CHK1); 224 225 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 226 if (pcibus_check()) 227 return; 228 }; 229 } 230 231 /*--------------------------------------- 232 ** Try configuration mechanism 2 ... 233 **--------------------------------------- 234 */ 235 236 oldval2 = inb (CONF2_ENABLE_PORT); 237 238 if (bootverbose) { 239 printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); 240 } 241 242 if ((oldval2 & 0xf0) == 0) { 243 244 pci_mechanism = 2; 245 pci_maxdevice = 16; 246 247 outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 248 mode2res = inb(CONF2_ENABLE_PORT); 249 outb (CONF2_ENABLE_PORT, oldval2); 250 251 if (bootverbose) 252 printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n", 253 mode2res, CONF2_ENABLE_CHK); 254 255 if (mode2res == CONF2_ENABLE_RES) { 256 if (bootverbose) 257 printf ("pcibus_setup(2a):\tnow trying mechanism 2\n"); 258 259 if (pcibus_check()) 260 return; 261 } 262 } 263 264 /*--------------------------------------- 265 ** No PCI bus host bridge found 266 **--------------------------------------- 267 */ 268 269 pci_mechanism = 0; 270 pci_maxdevice = 0; 271 } 272 273 /*-------------------------------------------------------------------- 274 ** 275 ** Build a pcitag from bus, device and function number 276 ** 277 **-------------------------------------------------------------------- 278 */ 279 280 static pcici_t 281 pcibus_tag (unsigned char bus, unsigned char device, unsigned char func) 282 { 283 pcici_t tag; 284 285 tag.cfg1 = 0; 286 if (func >= 8) return tag; 287 288 switch (pci_mechanism) { 289 290 case 1: 291 if (device < 32) { 292 tag.cfg1 = CONF1_ENABLE 293 | (((u_long) bus ) << 16ul) 294 | (((u_long) device) << 11ul) 295 | (((u_long) func ) << 8ul); 296 } 297 break; 298 case 2: 299 if (device < 16) { 300 tag.cfg2.port = 0xc000 | (device << 8ul); 301 tag.cfg2.enable = 0xf0 | (func << 1ul); 302 tag.cfg2.forward = bus; 303 } 304 break; 305 }; 306 return tag; 307 } 308 309 static pcici_t 310 pcibus_ftag (pcici_t tag, u_char func) 311 { 312 switch (pci_mechanism) { 313 314 case 1: 315 tag.cfg1 &= ~0x700ul; 316 tag.cfg1 |= (((u_long) func) << 8ul); 317 break; 318 case 2: 319 tag.cfg2.enable = 0xf0 | (func << 1ul); 320 break; 321 }; 322 return tag; 323 } 324 325 /*-------------------------------------------------------------------- 326 ** 327 ** Read register from configuration space. 328 ** 329 **-------------------------------------------------------------------- 330 */ 331 332 static u_long 333 pcibus_read (pcici_t tag, u_long reg) 334 { 335 u_long addr, data = 0; 336 337 if (!tag.cfg1) return (0xfffffffful); 338 339 switch (pci_mechanism) { 340 341 case 1: 342 addr = tag.cfg1 | (reg & 0xfc); 343 #ifdef PCI_DEBUG 344 printf ("pci_conf_read(1): addr=%x ", addr); 345 #endif 346 outl (CONF1_ADDR_PORT, addr); 347 data = inl (CONF1_DATA_PORT); 348 outl (CONF1_ADDR_PORT, 0 ); 349 break; 350 351 case 2: 352 addr = tag.cfg2.port | (reg & 0xfc); 353 #ifdef PCI_DEBUG 354 printf ("pci_conf_read(2): addr=%x ", addr); 355 #endif 356 outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); 357 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 358 359 data = inl ((u_short) addr); 360 361 outb (CONF2_ENABLE_PORT, 0); 362 outb (CONF2_FORWARD_PORT, 0); 363 break; 364 }; 365 366 #ifdef PCI_DEBUG 367 printf ("data=%x\n", data); 368 #endif 369 370 return (data); 371 } 372 373 /*-------------------------------------------------------------------- 374 ** 375 ** Write register into configuration space. 376 ** 377 **-------------------------------------------------------------------- 378 */ 379 380 static void 381 pcibus_write (pcici_t tag, u_long reg, u_long data) 382 { 383 u_long addr; 384 385 if (!tag.cfg1) return; 386 387 switch (pci_mechanism) { 388 389 case 1: 390 addr = tag.cfg1 | (reg & 0xfc); 391 #ifdef PCI_DEBUG 392 printf ("pci_conf_write(1): addr=%x data=%x\n", 393 addr, data); 394 #endif 395 outl (CONF1_ADDR_PORT, addr); 396 outl (CONF1_DATA_PORT, data); 397 outl (CONF1_ADDR_PORT, 0 ); 398 break; 399 400 case 2: 401 addr = tag.cfg2.port | (reg & 0xfc); 402 #ifdef PCI_DEBUG 403 printf ("pci_conf_write(2): addr=%x data=%x\n", 404 addr, data); 405 #endif 406 outb (CONF2_ENABLE_PORT, tag.cfg2.enable); 407 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 408 409 outl ((u_short) addr, data); 410 411 outb (CONF2_ENABLE_PORT, 0); 412 outb (CONF2_FORWARD_PORT, 0); 413 break; 414 }; 415 } 416 417 /*----------------------------------------------------------------------- 418 ** 419 ** Register an interupt handler for a pci device. 420 ** 421 **----------------------------------------------------------------------- 422 */ 423 424 static int 425 pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr) 426 { 427 char buf[16]; 428 char *cp; 429 int free_id, id, result; 430 431 sprintf(buf, "pci irq%d", irq); 432 for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) { 433 if (strcmp(cp, buf) == 0) 434 break; 435 if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0) 436 free_id = id; 437 while (*cp++ != '\0') 438 ; 439 } 440 if (id == NR_DEVICES) { 441 id = free_id; 442 if (id == 0) { 443 /* 444 * All pci irq counters are in use, perhaps because 445 * config is old so there aren't any. Abuse the 446 * clk0 counter. 447 */ 448 printf ( 449 "pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n", 450 irq); 451 } 452 } 453 result = register_intr( 454 irq, /* isa irq */ 455 id, /* device id */ 456 0, /* flags? */ 457 func, /* handler */ 458 maskptr, /* mask pointer */ 459 arg); /* handler arg */ 460 461 if (result) { 462 printf ("@@@ pcibus_ihandler_attach: result=%d\n", result); 463 return (result); 464 }; 465 update_intr_masks(); 466 467 INTREN ((1ul<<irq)); 468 return (0); 469 } 470 471 static int 472 pcibus_ihandler_detach (int irq, inthand2_t *func) 473 { 474 int result; 475 476 INTRDIS ((1ul<<irq)); 477 478 result = unregister_intr (irq, func); 479 480 if (result) 481 printf ("@@@ pcibus_ihandler_detach: result=%d\n", result); 482 483 update_intr_masks(); 484 485 return (result); 486 } 487 488 static int 489 pcibus_imask_include (int irq, unsigned* maskptr) 490 { 491 unsigned mask; 492 493 if (!maskptr) return (0); 494 495 mask = 1ul << irq; 496 497 if (*maskptr & mask) 498 return (-1); 499 500 INTRMASK (*maskptr, mask); 501 update_intr_masks(); 502 503 return (0); 504 } 505 506 static int 507 pcibus_imask_exclude (int irq, unsigned* maskptr) 508 { 509 unsigned mask; 510 511 if (!maskptr) return (0); 512 513 mask = 1ul << irq; 514 515 if (! (*maskptr & mask)) 516 return (-1); 517 518 *maskptr &= ~mask; 519 update_intr_masks(); 520 521 return (0); 522 } 523