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