pci.c revision 3870
1/************************************************************************** 2** 3** $Id: pci.c,v 1.7 1994/10/12 02:33:21 se Exp $ 4** 5** General subroutines for the PCI bus on 80*86 systems. 6** pci_configure () 7** 8** 386bsd / FreeBSD 9** 10**------------------------------------------------------------------------- 11** 12** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved. 13** 14** Redistribution and use in source and binary forms, with or without 15** modification, are permitted provided that the following conditions 16** are met: 17** 1. Redistributions of source code must retain the above copyright 18** notice, this list of conditions and the following disclaimer. 19** 2. Redistributions in binary form must reproduce the above copyright 20** notice, this list of conditions and the following disclaimer in the 21** documentation and/or other materials provided with the distribution. 22** 3. The name of the author may not be used to endorse or promote products 23** derived from this software without specific prior written permission. 24** 25** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 26** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 27** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 28** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 29** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 30** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 31** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 32** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 34** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 35** 36*************************************************************************** 37*/ 38 39#include <pci.h> 40#if NPCI > 0 41 42#ifndef __FreeBSD2__ 43#if __FreeBSD__ >= 2 44#define __FreeBSD2__ 45#endif 46#endif 47 48/*======================================================== 49** 50** #includes and declarations 51** 52**======================================================== 53*/ 54 55#include <sys/param.h> 56#include <sys/systm.h> 57#include <sys/malloc.h> 58#include <sys/errno.h> 59 60#include <vm/vm.h> 61#include <vm/vm_param.h> 62 63#include <i386/isa/isa.h> 64#include <i386/isa/isa_device.h> 65#include <i386/isa/icu.h> 66#include <i386/pci/pcireg.h> 67 68/* 69** Function prototypes missing in system headers 70*/ 71 72#ifndef __FreeBSD2__ 73extern pmap_t pmap_kernel(void); 74static vm_offset_t pmap_mapdev (vm_offset_t paddr, vm_size_t vsize); 75 76/* 77 * Type of the first (asm) part of an interrupt handler. 78 */ 79typedef void inthand_t __P((u_int cs, u_int ef, u_int esp, u_int ss)); 80 81/* 82 * Usual type of the second (C) part of an interrupt handler. Some bogus 83 * ones need the arg to be the interrupt frame (and not a copy of it, which 84 * is all that is possible in C). 85 */ 86typedef void inthand2_t __P((int unit)); 87 88/* 89** XXX @FreeBSD2@ 90** 91** Unfortunately, the mptr argument is _no_ pointer in 2.0 FreeBSD. 92** We would prefer a pointer because it enables us to install 93** new interrupt handlers at any time. 94** (This is just going to be changed ... <se> :) 95** In 2.0 FreeBSD later installed interrupt handlers may change 96** the xyz_imask, but this would not be recognized by handlers 97** which are installed before. 98*/ 99 100static int 101register_intr __P((int intr, int device_id, unsigned int flags, 102 inthand2_t *handler, unsigned int * mptr, int unit)); 103extern unsigned intr_mask[ICU_LEN]; 104 105#endif /* !__FreeBSD2__ */ 106 107/*======================================================== 108** 109** Autoconfiguration of pci devices. 110** 111** This is reverse to the isa configuration. 112** (1) find a pci device. 113** (2) look for a driver. 114** 115**======================================================== 116*/ 117 118/*-------------------------------------------------------- 119** 120** The pci devices can be mapped to any address. 121** As default we start at the last gigabyte. 122** 123**-------------------------------------------------------- 124*/ 125 126#ifndef PCI_PMEM_START 127#define PCI_PMEM_START 0xc0000000 128#endif 129 130static vm_offset_t pci_paddr = PCI_PMEM_START; 131 132/*-------------------------------------------------------- 133** 134** The pci device interrupt lines should have been 135** assigned by the bios. But if the bios failed to 136** to it, we set it. 137** 138**-------------------------------------------------------- 139*/ 140 141#ifndef PCI_IRQ 142#define PCI_IRQ 0 143#endif 144 145static u_long pci_irq = PCI_IRQ; 146 147/*--------------------------------------------------------- 148** 149** pci_configure () 150** 151** Probe all devices on pci bus and attach them. 152** 153** May be called more than once. 154** Any device is attached only once. 155** (Attached devices are remembered in pci_seen.) 156** 157**--------------------------------------------------------- 158*/ 159 160static void not_supported (pcici_t tag, u_long type); 161 162static unsigned long pci_seen[NPCI]; 163 164static int pci_conf_count; 165 166void pci_configure() 167{ 168 u_char device,last_device; 169 u_short bus; 170 pcici_t tag; 171 pcidi_t type; 172 u_long data; 173 int unit; 174 int pci_mechanism; 175 int pciint; 176 int irq; 177 char* name=0; 178 int newdev=0; 179 180 struct pci_driver *drp=0; 181 struct pci_device *dvp; 182 183 /* 184 ** check pci bus present 185 */ 186 187 pci_mechanism = pci_conf_mode (); 188 if (!pci_mechanism) return; 189 last_device = pci_mechanism==1 ? 31 : 15; 190 191 /* 192 ** hello world .. 193 */ 194 195 196 for (bus=0;bus<NPCI;bus++) { 197#ifndef PCI_QUIET 198 printf ("pci%d: scanning device 0..%d, mechanism=%d.\n", 199 bus, last_device, pci_mechanism); 200#endif 201 for (device=0; device<=last_device; device ++) { 202 203 if (pci_seen[bus] & (1ul << device)) 204 continue; 205 206 tag = pcitag (bus, device, 0); 207 type = pci_conf_read (tag, PCI_ID_REG); 208 209 if ((!type) || (type==0xfffffffful)) continue; 210 211 /* 212 ** lookup device in ioconfiguration: 213 */ 214 215 for (dvp = pci_devtab; dvp->pd_name; dvp++) { 216 drp = dvp->pd_driver; 217 if (!drp) 218 continue; 219 if ((name=(*drp->probe)(tag, type))) 220 break; 221 }; 222 223 if (!dvp->pd_name) { 224#ifndef PCI_QUIET 225 if (pci_conf_count) 226 continue; 227 printf("pci%d:%d: ", bus, device); 228 not_supported (tag, type); 229#endif 230 continue; 231 }; 232 233 pci_seen[bus] |= (1ul << device); 234 /* 235 ** Get and increment the unit. 236 */ 237 238 unit = (*drp->count)++; 239 240 /* 241 ** ignore device ? 242 */ 243 244 if (!*name) continue; 245 246 /* 247 ** Announce this device 248 */ 249 250 newdev++; 251 printf ("%s%d <%s>", dvp->pd_name, unit, name); 252 253 /* 254 ** Get the int pin number (pci interrupt number a-d) 255 ** from the pci configuration space. 256 */ 257 258 data = pci_conf_read (tag, PCI_INTERRUPT_REG); 259 pciint = PCI_INTERRUPT_PIN_EXTRACT(data); 260 261 if (pciint) { 262 263 printf (" int %c", 0x60+pciint); 264 265 /* 266 ** If the interrupt line register is not set, 267 ** set it now from PCI_IRQ. 268 */ 269 270 if (!(PCI_INTERRUPT_LINE_EXTRACT(data))) { 271 272 irq = pci_irq & 0x0f; 273 pci_irq >>= 4; 274 275 data = PCI_INTERRUPT_LINE_INSERT(data, irq); 276 printf (" (config)"); 277 pci_conf_write (tag, PCI_INTERRUPT_REG, data); 278 }; 279 280 irq = PCI_INTERRUPT_LINE_EXTRACT(data); 281 282 /* 283 ** If it's zero, the isa irq number is unknown, 284 ** and we cannot bind the pci interrupt to isa. 285 */ 286 287 if (irq) 288 printf (" irq %d", irq); 289 else 290 printf (" not bound"); 291 }; 292 293 /* 294 ** enable memory access 295 */ 296 297 data = (pci_conf_read (tag, PCI_COMMAND_STATUS_REG) 298 & 0xffff) | PCI_COMMAND_MEM_ENABLE; 299 300 pci_conf_write (tag, (u_char) PCI_COMMAND_STATUS_REG, data); 301 302 /* 303 ** attach device 304 ** may produce additional log messages, 305 ** i.e. when installing subdevices. 306 */ 307 308 printf (" on pci%d:%d\n", bus, device); 309 310 (*drp->attach) (tag, unit); 311 }; 312 }; 313 314#ifndef PCI_QUIET 315 if (newdev) 316 printf ("pci uses physical addresses from 0x%lx to 0x%lx\n", 317 (u_long)PCI_PMEM_START, (u_long)pci_paddr); 318#endif 319 pci_conf_count++; 320} 321 322/*----------------------------------------------------------------------- 323** 324** Map device into port space. 325** 326** PCI-Specification: 6.2.5.1: address maps 327** 328**----------------------------------------------------------------------- 329*/ 330 331int pci_map_port (pcici_t tag, u_long reg, u_short* pa) 332{ 333 /* 334 ** @MAPIO@ not yet implemented. 335 */ 336 printf ("pci_map_port failed: not yet implemented\n"); 337 return (0); 338} 339 340/*----------------------------------------------------------------------- 341** 342** Map device into virtual and physical space 343** 344** PCI-Specification: 6.2.5.1: address maps 345** 346**----------------------------------------------------------------------- 347*/ 348 349int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa) 350{ 351 u_long data; 352 vm_size_t vsize; 353 vm_offset_t vaddr; 354 355 /* 356 ** sanity check 357 */ 358 359 if (reg < PCI_MAP_REG_START || reg >= PCI_MAP_REG_END || (reg & 3)) { 360 printf ("pci_map_mem failed: bad register=0x%x\n", 361 (unsigned)reg); 362 return (0); 363 }; 364 365 /* 366 ** get size and type of memory 367 ** 368 ** type is in the lowest four bits. 369 ** If device requires 2^n bytes, the next 370 ** n-4 bits are read as 0. 371 */ 372 373 pci_conf_write (tag, reg, 0xfffffffful); 374 data = pci_conf_read (tag, reg); 375 376 switch (data & 0x0f) { 377 378 case PCI_MAP_MEMORY_TYPE_32BIT: /* 32 bit non cachable */ 379 break; 380 381 default: /* unknown */ 382 printf ("pci_map_mem failed: bad memory type=0x%x\n", 383 (unsigned) data); 384 return (0); 385 }; 386 387 /* 388 ** mask out the type, 389 ** and round up to a page size 390 */ 391 392 vsize = round_page (-(data & PCI_MAP_MEMORY_ADDRESS_MASK)); 393 394 if (!vsize) return (0); 395 396 /* 397 ** align physical address to virtual size 398 */ 399 400 if ((data = pci_paddr % vsize)) 401 pci_paddr += vsize - data; 402 403 vaddr = (vm_offset_t) pmap_mapdev (pci_paddr, vsize); 404 405 406 if (!vaddr) return (0); 407 408#ifndef PCI_QUIET 409 /* 410 ** display values. 411 */ 412 413 printf ("\treg%d: virtual=0x%lx physical=0x%lx\n", 414 (unsigned) reg, (u_long)vaddr, (u_long)pci_paddr); 415#endif 416 417 /* 418 ** return them to the driver 419 */ 420 421 *va = vaddr; 422 *pa = pci_paddr; 423 424 /* 425 ** set device address 426 */ 427 428 pci_conf_write (tag, reg, pci_paddr); 429 430 /* 431 ** and don't forget to increment pci_paddr 432 */ 433 434 pci_paddr += vsize; 435 436 return (1); 437} 438 439/*----------------------------------------------------------------------- 440** 441** Map pci interrupts to isa interrupts. 442** 443**----------------------------------------------------------------------- 444*/ 445 446static unsigned int pci_int_mask [16]; 447 448int pci_map_int (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr) 449{ 450 int irq; 451 unsigned mask; 452 453 irq = PCI_INTERRUPT_LINE_EXTRACT( 454 pci_conf_read (tag, PCI_INTERRUPT_REG)); 455 456 if (irq >= 16 || irq <= 0) { 457 printf ("pci_map_int failed: no int line set.\n"); 458 return (0); 459 } 460 461 mask = 1ul << irq; 462 463 if (!maskptr) 464 maskptr = &pci_int_mask[irq]; 465 466 INTRMASK (*maskptr, mask); 467 468 register_intr( 469 irq, /* isa irq */ 470 0, /* deviced?? */ 471 0, /* flags? */ 472 (inthand2_t*) func, /* handler */ 473 maskptr, /* mask pointer */ 474 (int) arg); /* handler arg */ 475 476#ifdef __FreeBSD2__ 477 /* 478 ** XXX See comment at beginning of file. 479 ** 480 ** Have to update all the interrupt masks ... Grrrrr!!! 481 */ 482 { 483 unsigned * mp = &intr_mask[0]; 484 /* 485 ** update the isa interrupt masks. 486 */ 487 for (mp=&intr_mask[0]; mp<&intr_mask[ICU_LEN]; mp++) 488 if (*mp & *maskptr) 489 *mp |= mask; 490 /* 491 ** update the pci interrupt masks. 492 */ 493 for (mp=&pci_int_mask[0]; mp<&pci_int_mask[16]; mp++) 494 if (*mp & *maskptr) 495 *mp |= mask; 496 }; 497#endif 498 499 INTREN (mask); 500 501 return (1); 502} 503 504/*----------------------------------------------------------- 505** 506** Display of unknown devices. 507** 508**----------------------------------------------------------- 509*/ 510struct vt { 511 u_short ident; 512 char* name; 513}; 514 515static struct vt VendorTable[] = { 516 {0x1002, "ATI TECHNOLOGIES INC"}, 517 {0x1011, "DIGITAL EQUIPMENT CORPORATION"}, 518 {0x101A, "NCR"}, 519 {0x102B, "MATROX"}, 520 {0x1045, "OPTI"}, 521 {0x5333, "S3 INC."}, 522 {0x8086, "INTEL CORPORATION"}, 523 {0,0} 524}; 525 526static const char *const majclasses[] = { 527 "old", "storage", "network", "display", 528 "multimedia", "memory", "bridge" 529}; 530 531void not_supported (pcici_t tag, u_long type) 532{ 533 u_char reg; 534 u_long data; 535 struct vt * vp; 536 537 /* 538 ** lookup the names. 539 */ 540 541 for (vp=VendorTable; vp->ident; vp++) 542 if (vp->ident == (type & 0xffff)) 543 break; 544 545 /* 546 ** and display them. 547 */ 548 549 if (vp->ident) printf (vp->name); 550 else printf ("vendor=0x%lx", type & 0xffff); 551 552 printf (", device=0x%lx", type >> 16); 553 554 data = (pci_conf_read(tag, PCI_CLASS_REG) >> 24) & 0xff; 555 if (data < sizeof(majclasses) / sizeof(majclasses[0])) 556 printf(", class=%s", majclasses[data]); 557 558 printf (" [not supported]\n"); 559 560 for (reg=PCI_MAP_REG_START; reg<PCI_MAP_REG_END; reg+=4) { 561 data = pci_conf_read (tag, reg); 562 if (!data) continue; 563 switch (data&7) { 564 565 case 1: 566 case 5: 567 printf (" map(%x): io(%lx)\n", 568 reg, data & ~3); 569 break; 570 case 0: 571 printf (" map(%x): mem32(%lx)\n", 572 reg, data & ~7); 573 break; 574 case 2: 575 printf (" map(%x): mem20(%lx)\n", 576 reg, data & ~7); 577 break; 578 case 4: 579 printf (" map(%x): mem64(%lx)\n", 580 reg, data & ~7); 581 break; 582 } 583 } 584} 585 586#ifndef __FreeBSD2__ 587/*----------------------------------------------------------- 588** 589** Mapping of physical to virtual memory 590** 591**----------------------------------------------------------- 592*/ 593 594extern vm_map_t kernel_map; 595 596static vm_offset_t pmap_mapdev (vm_offset_t paddr, vm_size_t vsize) 597{ 598 vm_offset_t vaddr,value; 599 u_long result; 600 601 vaddr = vm_map_min (kernel_map); 602 603 result = vm_map_find (kernel_map, (void*)0, (vm_offset_t) 0, 604 &vaddr, vsize, TRUE); 605 606 if (result != KERN_SUCCESS) { 607 printf (" vm_map_find failed(%d)\n", result); 608 return (0); 609 }; 610 611 /* 612 ** map physical 613 */ 614 615 value = vaddr; 616 while (vsize >= NBPG) { 617 pmap_enter (pmap_kernel(), vaddr, paddr, 618 VM_PROT_READ|VM_PROT_WRITE, TRUE); 619 vaddr += NBPG; 620 paddr += NBPG; 621 vsize -= NBPG; 622 }; 623 return (value); 624} 625 626/*------------------------------------------------------------ 627** 628** Emulate the register_intr() function of FreeBSD 2.0 629** 630** requires a patch: 631** FreeBSD 2.0: "/sys/i386/isa/vector.s" 632** 386bsd0.1: "/sys/i386/isa/icu.s" 633** 386bsd1.0: Please ask Jesus Monroy Jr. 634** 635**------------------------------------------------------------ 636*/ 637 638#include <machine/segments.h> 639 640int pci_int_unit [16]; 641inthand2_t* (pci_int_hdlr [16]); 642unsigned int * pci_int_mptr [16]; 643unsigned int pci_int_count[16]; 644 645extern void 646 Vpci3(), Vpci4(), Vpci5(), Vpci6(), Vpci7(), Vpci8(), Vpci9(), 647 Vpci10(), Vpci11(), Vpci12(), Vpci13(), Vpci14(), Vpci15(); 648 649static inthand_t* pci_int_glue[16] = { 650 0, 0, 0, Vpci3, Vpci4, Vpci5, Vpci6, Vpci7, Vpci8, 651 Vpci9, Vpci10, Vpci11, Vpci12, Vpci13, Vpci14, Vpci15 }; 652 653static int 654register_intr __P((int intr, int device_id, unsigned int flags, 655 inthand2_t *handler, unsigned int* mptr, int unit)) 656{ 657 if (intr >= 16 || intr <= 2) 658 return (EINVAL); 659 if (pci_int_hdlr [intr]) 660 return (EBUSY); 661 662 pci_int_hdlr [intr] = handler; 663 pci_int_unit [intr] = unit; 664 pci_int_mptr [intr] = mptr; 665 666 setidt(NRSVIDT + intr, pci_int_glue[intr], SDT_SYS386IGT, SEL_KPL); 667 return (0); 668} 669#endif /* __FreeBSD2__ */ 670#endif /* NPCI */ 671