1 /************************************************************************** 2 ** 3 ** $Id: pcibus.c,v 1.18 1995/10/17 15:23:14 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 <sys/param.h> 39 #include <sys/systm.h> 40 #include <sys/kernel.h> 41 42 #include <machine/cpu.h> /* bootverbose */ 43 44 #include <i386/isa/icu.h> 45 #include <i386/isa/isa.h> 46 #include <i386/isa/isa_device.h> 47 48 #include <pci/pcivar.h> 49 #include <pci/pcireg.h> 50 #include <pci/pcibus.h> 51 52 /*----------------------------------------------------------------- 53 ** 54 ** The following functions are provided by the pci bios. 55 ** They are used only by the pci configuration. 56 ** 57 ** pcibus_setup(): 58 ** Probes for a pci system. 59 ** Sets pci_maxdevice and pci_mechanism. 60 ** 61 ** pcibus_tag(): 62 ** Creates a handle for pci configuration space access. 63 ** This handle is given to the read/write functions. 64 ** 65 ** pcibus_ftag(): 66 ** Creates a modified handle. 67 ** 68 ** pcibus_read(): 69 ** Read a long word from the pci configuration space. 70 ** Requires a tag (from pcitag) and the register 71 ** number (should be a long word alligned one). 72 ** 73 ** pcibus_write(): 74 ** Writes a long word to the pci configuration space. 75 ** Requires a tag (from pcitag), the register number 76 ** (should be a long word alligned one), and a value. 77 ** 78 ** pcibus_regirq(): 79 ** Register an interupt handler for a pci device. 80 ** Requires a tag (from pcitag), the register number 81 ** (should be a long word alligned one), and a value. 82 ** 83 **----------------------------------------------------------------- 84 */ 85 86 static int 87 pcibus_check (void); 88 89 static void 90 pcibus_setup (void); 91 92 static pcici_t 93 pcibus_tag (u_char bus, u_char device, u_char func); 94 95 static pcici_t 96 pcibus_ftag (pcici_t tag, u_char func); 97 98 static u_long 99 pcibus_read (pcici_t tag, u_long reg); 100 101 static void 102 pcibus_write (pcici_t tag, u_long reg, u_long data); 103 104 static int 105 pcibus_ihandler_attach (int irq, void(*ihandler)(), int arg, unsigned* maskp); 106 107 static int 108 pcibus_ihandler_detach (int irq, void(*handler)()); 109 110 static int 111 pcibus_imask_include (int irq, unsigned* maskptr); 112 113 static int 114 pcibus_imask_exclude (int irq, unsigned* maskptr); 115 116 struct pcibus i386pci = { 117 "pci", 118 pcibus_setup, 119 pcibus_tag, 120 pcibus_ftag, 121 pcibus_read, 122 pcibus_write, 123 ICU_LEN, 124 pcibus_ihandler_attach, 125 pcibus_ihandler_detach, 126 pcibus_imask_include, 127 pcibus_imask_exclude, 128 }; 129 130 /* 131 ** Announce structure to generic driver 132 */ 133 134 DATA_SET (pcibus_set, i386pci); 135 136 /*-------------------------------------------------------------------- 137 ** 138 ** Determine configuration mode 139 ** 140 **-------------------------------------------------------------------- 141 */ 142 143 144 #define CONF1_ADDR_PORT 0x0cf8 145 #define CONF1_DATA_PORT 0x0cfc 146 147 #define CONF1_ENABLE 0x80000000ul 148 #define CONF1_ENABLE_CHK 0x80000000ul 149 #define CONF1_ENABLE_MSK 0x00ff0700ul 150 #define CONF1_ENABLE_CHK1 0xff000001ul 151 #define CONF1_ENABLE_MSK1 0x80000001ul 152 #define CONF1_ENABLE_RES1 0x80000000ul 153 154 #define CONF2_ENABLE_PORT 0x0cf8 155 #define CONF2_FORWARD_PORT 0x0cfa 156 157 #define CONF2_ENABLE_CHK 0x0e 158 #define CONF2_ENABLE_RES 0x0e 159 160 static int 161 pcibus_check (void) 162 { 163 u_char device; 164 165 if (bootverbose) printf ("pcibus_check:\tdevice "); 166 167 for (device = 0; device < pci_maxdevice; device++) { 168 unsigned long id; 169 if (bootverbose) 170 printf ("%d ", device); 171 id = pcibus_read (pcibus_tag (0,device,0), 0); 172 if (id != 0xfffffffful) { 173 if (bootverbose) printf ("is there (id=%08lx)\n", id); 174 return 1; 175 } 176 } 177 if (bootverbose) 178 printf ("-- nothing found\n"); 179 return 0; 180 } 181 182 static void 183 pcibus_setup (void) 184 { 185 unsigned long mode1res,oldval1; 186 unsigned char mode2res,oldval2; 187 188 oldval1 = inl (CONF1_ADDR_PORT); 189 190 if (bootverbose) { 191 printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); 192 } 193 194 /*--------------------------------------- 195 ** Assume configuration mechanism 1 for now ... 196 **--------------------------------------- 197 */ 198 199 if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 200 201 pci_mechanism = 1; 202 pci_maxdevice = 32; 203 204 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 205 outb (CONF1_ADDR_PORT +3, 0); 206 mode1res = inl (CONF1_ADDR_PORT); 207 outl (CONF1_ADDR_PORT, oldval1); 208 209 if (bootverbose) 210 printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n", 211 mode1res, CONF1_ENABLE_CHK); 212 213 if (mode1res) { 214 if (pcibus_check()) 215 return; 216 }; 217 218 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 219 mode1res = inl(CONF1_ADDR_PORT); 220 outl (CONF1_ADDR_PORT, oldval1); 221 222 if (bootverbose) 223 printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n", 224 mode1res, CONF1_ENABLE_CHK1); 225 226 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 227 if (pcibus_check()) 228 return; 229 }; 230 } 231 232 /*--------------------------------------- 233 ** Try configuration mechanism 2 ... 234 **--------------------------------------- 235 */ 236 237 oldval2 = inb (CONF2_ENABLE_PORT); 238 239 if (bootverbose) { 240 printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); 241 } 242 243 if ((oldval2 & 0xf0) == 0) { 244 245 pci_mechanism = 2; 246 pci_maxdevice = 16; 247 248 outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 249 mode2res = inb(CONF2_ENABLE_PORT); 250 outb (CONF2_ENABLE_PORT, oldval2); 251 252 if (bootverbose) 253 printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n", 254 mode2res, CONF2_ENABLE_CHK); 255 256 if (mode2res == CONF2_ENABLE_RES) { 257 if (bootverbose) 258 printf ("pcibus_setup(2a):\tnow trying mechanism 2\n"); 259 260 if (pcibus_check()) 261 return; 262 } 263 } 264 265 /*--------------------------------------- 266 ** No PCI bus host bridge found 267 **--------------------------------------- 268 */ 269 270 pci_mechanism = 0; 271 pci_maxdevice = 0; 272 } 273 274 /*-------------------------------------------------------------------- 275 ** 276 ** Build a pcitag from bus, device and function number 277 ** 278 **-------------------------------------------------------------------- 279 */ 280 281 static pcici_t 282 pcibus_tag (unsigned char bus, unsigned char device, unsigned char func) 283 { 284 pcici_t tag; 285 286 tag.cfg1 = 0; 287 if (func >= 8) return tag; 288 289 switch (pci_mechanism) { 290 291 case 1: 292 if (device < 32) { 293 tag.cfg1 = CONF1_ENABLE 294 | (((u_long) bus ) << 16ul) 295 | (((u_long) device) << 11ul) 296 | (((u_long) func ) << 8ul); 297 } 298 break; 299 case 2: 300 if (device < 16) { 301 tag.cfg2.port = 0xc000 | (device << 8ul); 302 tag.cfg2.enable = 0xf0 | (func << 1ul); 303 tag.cfg2.forward = bus; 304 } 305 break; 306 }; 307 return tag; 308 } 309 310 static pcici_t 311 pcibus_ftag (pcici_t tag, u_char func) 312 { 313 switch (pci_mechanism) { 314 315 case 1: 316 tag.cfg1 &= ~0x700ul; 317 tag.cfg1 |= (((u_long) func) << 8ul); 318 break; 319 case 2: 320 tag.cfg2.enable = 0xf0 | (func << 1ul); 321 break; 322 }; 323 return tag; 324 } 325 326 /*-------------------------------------------------------------------- 327 ** 328 ** Read register from configuration space. 329 ** 330 **-------------------------------------------------------------------- 331 */ 332 333 static u_long 334 pcibus_read (pcici_t tag, u_long reg) 335 { 336 u_long addr, data = 0; 337 338 if (!tag.cfg1) return (0xfffffffful); 339 340 switch (pci_mechanism) { 341 342 case 1: 343 addr = tag.cfg1 | (reg & 0xfc); 344 #ifdef PCI_DEBUG 345 printf ("pci_conf_read(1): addr=%x ", addr); 346 #endif 347 outl (CONF1_ADDR_PORT, addr); 348 data = inl (CONF1_DATA_PORT); 349 outl (CONF1_ADDR_PORT, 0 ); 350 break; 351 352 case 2: 353 addr = tag.cfg2.port | (reg & 0xfc); 354 #ifdef PCI_DEBUG 355 printf ("pci_conf_read(2): addr=%x ", addr); 356 #endif 357 outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); 358 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 359 360 data = inl ((u_short) addr); 361 362 outb (CONF2_ENABLE_PORT, 0); 363 outb (CONF2_FORWARD_PORT, 0); 364 break; 365 }; 366 367 #ifdef PCI_DEBUG 368 printf ("data=%x\n", data); 369 #endif 370 371 return (data); 372 } 373 374 /*-------------------------------------------------------------------- 375 ** 376 ** Write register into configuration space. 377 ** 378 **-------------------------------------------------------------------- 379 */ 380 381 static void 382 pcibus_write (pcici_t tag, u_long reg, u_long data) 383 { 384 u_long addr; 385 386 if (!tag.cfg1) return; 387 388 switch (pci_mechanism) { 389 390 case 1: 391 addr = tag.cfg1 | (reg & 0xfc); 392 #ifdef PCI_DEBUG 393 printf ("pci_conf_write(1): addr=%x data=%x\n", 394 addr, data); 395 #endif 396 outl (CONF1_ADDR_PORT, addr); 397 outl (CONF1_DATA_PORT, data); 398 outl (CONF1_ADDR_PORT, 0 ); 399 break; 400 401 case 2: 402 addr = tag.cfg2.port | (reg & 0xfc); 403 #ifdef PCI_DEBUG 404 printf ("pci_conf_write(2): addr=%x data=%x\n", 405 addr, data); 406 #endif 407 outb (CONF2_ENABLE_PORT, tag.cfg2.enable); 408 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 409 410 outl ((u_short) addr, data); 411 412 outb (CONF2_ENABLE_PORT, 0); 413 outb (CONF2_FORWARD_PORT, 0); 414 break; 415 }; 416 } 417 418 /*----------------------------------------------------------------------- 419 ** 420 ** Register an interupt handler for a pci device. 421 ** 422 **----------------------------------------------------------------------- 423 */ 424 425 static int 426 pcibus_ihandler_attach (int irq, void(*func)(), int arg, unsigned * maskptr) 427 { 428 int result; 429 result = register_intr( 430 irq, /* isa irq */ 431 0, /* deviced?? */ 432 0, /* flags? */ 433 (inthand2_t*) func, /* handler */ 434 maskptr, /* mask pointer */ 435 arg); /* handler arg */ 436 437 if (result) { 438 printf ("@@@ pcibus_ihandler_attach: result=%d\n", result); 439 return (result); 440 }; 441 update_intr_masks(); 442 443 INTREN ((1ul<<irq)); 444 return (0); 445 } 446 447 static int 448 pcibus_ihandler_detach (int irq, void(*func)()) 449 { 450 int result; 451 452 INTRDIS ((1ul<<irq)); 453 454 result = unregister_intr (irq, (inthand2_t*) func); 455 456 if (result) 457 printf ("@@@ pcibus_ihandler_detach: result=%d\n", result); 458 459 update_intr_masks(); 460 461 return (result); 462 } 463 464 static int 465 pcibus_imask_include (int irq, unsigned* maskptr) 466 { 467 unsigned mask; 468 469 if (!maskptr) return (0); 470 471 mask = 1ul << irq; 472 473 if (*maskptr & mask) 474 return (-1); 475 476 INTRMASK (*maskptr, mask); 477 update_intr_masks(); 478 479 return (0); 480 } 481 482 static int 483 pcibus_imask_exclude (int irq, unsigned* maskptr) 484 { 485 unsigned mask; 486 487 if (!maskptr) return (0); 488 489 mask = 1ul << irq; 490 491 if (! (*maskptr & mask)) 492 return (-1); 493 494 *maskptr &= ~mask; 495 update_intr_masks(); 496 497 return (0); 498 } 499