1 /************************************************************************** 2 ** 3 ** $Id: pcibus.c,v 1.15 1995/09/22 19:10:54 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_CHK1 0xFF000001ul 150 #define CONF1_ENABLE_MSK1 0x80000000ul 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 != 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,oldval; 185 unsigned char mode2res; 186 187 oldval = inl (CONF1_ADDR_PORT); 188 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 189 outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 190 mode1res = inl(CONF1_ADDR_PORT); 191 mode2res = inb(CONF2_ENABLE_PORT); 192 outb (CONF2_ENABLE_PORT, 0); 193 outl (CONF1_ADDR_PORT, oldval); 194 195 if (bootverbose) { 196 printf ("pcibus_setup(1):\tmode1res=0x%08lx (0x%08lx), " 197 "mode2res=0x%02x (0x%02x)\n", 198 mode1res,CONF1_ENABLE_CHK, 199 (int)mode2res,CONF2_ENABLE_CHK); 200 } 201 202 /*--------------------------------------- 203 ** No PCI, if neither mode1res nor mode2res could be read back 204 **--------------------------------------- 205 */ 206 207 if ((mode1res != CONF1_ENABLE_CHK) && (mode2res != CONF2_ENABLE_CHK)) { 208 return; 209 } 210 211 /*--------------------------------------- 212 ** Assume configuration mechanism 1 for now ... 213 **--------------------------------------- 214 */ 215 216 pci_mechanism = 1; 217 pci_maxdevice = 32; 218 219 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 220 outb (CONF1_ADDR_PORT +3, 0); 221 mode1res = inl (CONF1_ADDR_PORT); 222 outl (CONF1_ADDR_PORT, oldval); 223 224 if (bootverbose) 225 printf ("pcibus_setup(2):\tmode1res=0x%08lx (0x%08lx)\n", 226 mode1res, CONF1_ENABLE_CHK); 227 228 if (mode1res) { 229 if (pcibus_check()) 230 return; 231 }; 232 233 outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 234 outl (CONF1_DATA_PORT, 0); 235 mode1res = inl(CONF1_ADDR_PORT); 236 outl (CONF1_ADDR_PORT, oldval); 237 238 if (bootverbose) 239 printf ("pcibus_setup(3):\tmode1res=0x%08lx (0x%08lx)\n", 240 mode1res, CONF1_ENABLE_CHK1); 241 242 if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 243 if (pcibus_check()) 244 return; 245 }; 246 247 /*--------------------------------------- 248 ** Try configuration mechanism 2 ... 249 **--------------------------------------- 250 */ 251 252 if (bootverbose) 253 printf ("pcibus_setup(4):\tnow trying mechanism 2\n"); 254 255 pci_mechanism = 2; 256 pci_maxdevice = 16; 257 258 if (pcibus_check()) 259 return; 260 261 /*--------------------------------------- 262 ** No PCI bus host bridge found 263 **--------------------------------------- 264 */ 265 266 pci_mechanism = 0; 267 pci_maxdevice = 0; 268 } 269 270 /*-------------------------------------------------------------------- 271 ** 272 ** Build a pcitag from bus, device and function number 273 ** 274 **-------------------------------------------------------------------- 275 */ 276 277 static pcici_t 278 pcibus_tag (unsigned char bus, unsigned char device, unsigned char func) 279 { 280 pcici_t tag; 281 282 tag.cfg1 = 0; 283 if (func >= 8) return tag; 284 285 switch (pci_mechanism) { 286 287 case 1: 288 if (device < 32) { 289 tag.cfg1 = CONF1_ENABLE 290 | (((u_long) bus ) << 16ul) 291 | (((u_long) device) << 11ul) 292 | (((u_long) func ) << 8ul); 293 } 294 break; 295 case 2: 296 if (device < 16) { 297 tag.cfg2.port = 0xc000 | (device << 8ul); 298 tag.cfg2.enable = 0xf0 | (func << 1ul); 299 tag.cfg2.forward = bus; 300 } 301 break; 302 }; 303 return tag; 304 } 305 306 static pcici_t 307 pcibus_ftag (pcici_t tag, u_char func) 308 { 309 switch (pci_mechanism) { 310 311 case 1: 312 tag.cfg1 &= ~0x700ul; 313 tag.cfg1 |= (((u_long) func) << 8ul); 314 break; 315 case 2: 316 tag.cfg2.enable = 0xf0 | (func << 1ul); 317 break; 318 }; 319 return tag; 320 } 321 322 /*-------------------------------------------------------------------- 323 ** 324 ** Read register from configuration space. 325 ** 326 **-------------------------------------------------------------------- 327 */ 328 329 static u_long 330 pcibus_read (pcici_t tag, u_long reg) 331 { 332 u_long addr, data = 0; 333 334 if (!tag.cfg1) return (0xfffffffful); 335 336 switch (pci_mechanism) { 337 338 case 1: 339 addr = tag.cfg1 | (reg & 0xfc); 340 #ifdef PCI_DEBUG 341 printf ("pci_conf_read(1): addr=%x ", addr); 342 #endif 343 outl (CONF1_ADDR_PORT, addr); 344 data = inl (CONF1_DATA_PORT); 345 outl (CONF1_ADDR_PORT, 0 ); 346 break; 347 348 case 2: 349 addr = tag.cfg2.port | (reg & 0xfc); 350 #ifdef PCI_DEBUG 351 printf ("pci_conf_read(2): addr=%x ", addr); 352 #endif 353 outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); 354 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 355 356 data = inl ((u_short) addr); 357 358 outb (CONF2_ENABLE_PORT, 0); 359 outb (CONF2_FORWARD_PORT, 0); 360 break; 361 }; 362 363 #ifdef PCI_DEBUG 364 printf ("data=%x\n", data); 365 #endif 366 367 return (data); 368 } 369 370 /*-------------------------------------------------------------------- 371 ** 372 ** Write register into configuration space. 373 ** 374 **-------------------------------------------------------------------- 375 */ 376 377 static void 378 pcibus_write (pcici_t tag, u_long reg, u_long data) 379 { 380 u_long addr; 381 382 if (!tag.cfg1) return; 383 384 switch (pci_mechanism) { 385 386 case 1: 387 addr = tag.cfg1 | (reg & 0xfc); 388 #ifdef PCI_DEBUG 389 printf ("pci_conf_write(1): addr=%x data=%x\n", 390 addr, data); 391 #endif 392 outl (CONF1_ADDR_PORT, addr); 393 outl (CONF1_DATA_PORT, data); 394 outl (CONF1_ADDR_PORT, 0 ); 395 break; 396 397 case 2: 398 addr = tag.cfg2.port | (reg & 0xfc); 399 #ifdef PCI_DEBUG 400 printf ("pci_conf_write(2): addr=%x data=%x\n", 401 addr, data); 402 #endif 403 outb (CONF2_ENABLE_PORT, tag.cfg2.enable); 404 outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 405 406 outl ((u_short) addr, data); 407 408 outb (CONF2_ENABLE_PORT, 0); 409 outb (CONF2_FORWARD_PORT, 0); 410 break; 411 }; 412 } 413 414 /*----------------------------------------------------------------------- 415 ** 416 ** Register an interupt handler for a pci device. 417 ** 418 **----------------------------------------------------------------------- 419 */ 420 421 static int 422 pcibus_ihandler_attach (int irq, void(*func)(), int arg, unsigned * maskptr) 423 { 424 int result; 425 result = register_intr( 426 irq, /* isa irq */ 427 0, /* deviced?? */ 428 0, /* flags? */ 429 (inthand2_t*) func, /* handler */ 430 maskptr, /* mask pointer */ 431 arg); /* handler arg */ 432 433 if (result) { 434 printf ("@@@ pcibus_ihandler_attach: result=%d\n", result); 435 return (result); 436 }; 437 update_intr_masks(); 438 439 INTREN ((1ul<<irq)); 440 return (0); 441 } 442 443 static int 444 pcibus_ihandler_detach (int irq, void(*func)()) 445 { 446 int result; 447 448 INTRDIS ((1ul<<irq)); 449 450 result = unregister_intr (irq, (inthand2_t*) func); 451 452 if (result) 453 printf ("@@@ pcibus_ihandler_detach: result=%d\n", result); 454 455 update_intr_masks(); 456 457 return (result); 458 } 459 460 static int 461 pcibus_imask_include (int irq, unsigned* maskptr) 462 { 463 unsigned mask; 464 465 if (!maskptr) return (0); 466 467 mask = 1ul << irq; 468 469 if (*maskptr & mask) 470 return (-1); 471 472 INTRMASK (*maskptr, mask); 473 update_intr_masks(); 474 475 return (0); 476 } 477 478 static int 479 pcibus_imask_exclude (int irq, unsigned* maskptr) 480 { 481 unsigned mask; 482 483 if (!maskptr) return (0); 484 485 mask = 1ul << irq; 486 487 if (! (*maskptr & mask)) 488 return (-1); 489 490 *maskptr &= ~mask; 491 update_intr_masks(); 492 493 return (0); 494 } 495