pci_pir.c revision 22005
1/************************************************************************** 2** 3** $FreeBSD: head/sys/i386/pci/pci_pir.c 22005 1997-01-25 18:51:01Z bde $ 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 85static int 86pcibus_check (void); 87 88static void 89pcibus_setup (void); 90 91static pcici_t 92pcibus_tag (u_char bus, u_char device, u_char func); 93 94static pcici_t 95pcibus_ftag (pcici_t tag, u_char func); 96 97static u_long 98pcibus_read (pcici_t tag, u_long reg); 99 100static void 101pcibus_write (pcici_t tag, u_long reg, u_long data); 102 103static int 104pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr); 105 106static int 107pcibus_ihandler_detach (int irq, inthand2_t *func); 108 109static int 110pcibus_imask_include (int irq, unsigned* maskptr); 111 112static int 113pcibus_imask_exclude (int irq, unsigned* maskptr); 114 115static 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 132DATA_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 162static int 163pcibus_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 184static void 185pcibus_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 283static pcici_t 284pcibus_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 312static pcici_t 313pcibus_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 335static u_long 336pcibus_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 383static void 384pcibus_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 427static int 428pcibus_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 474static int 475pcibus_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 491static int 492pcibus_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 509static int 510pcibus_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