pci.c revision 2815
1/************************************************************************** 2** 3** $Id: pci.c,v 2.0.0.12 94/09/15 20:49:23 wolf 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/*======================================================== 43** 44** Configuration 45** 46**======================================================== 47*/ 48 49/* 50** maximum number of devices which share one interrupt line 51*/ 52 53#ifndef PCI_MAX_DPI 54#define PCI_MAX_DPI (4) 55#endif /*PCI_MAX_DPI*/ 56 57 58/*======================================================== 59** 60** #includes and declarations 61** 62**======================================================== 63*/ 64 65#include <sys/param.h> 66#include <sys/systm.h> 67#include <sys/errno.h> 68 69#include <vm/vm.h> 70#include <vm/vm_param.h> 71 72#include <i386/isa/icu.h> 73#include <i386/isa/isa.h> 74#include <i386/isa/isa_device.h> 75 76#include <i386/pci/pci.h> 77#include <i386/pci/pci_device.h> 78#include <i386/pci/pcibios.h> 79 80 81char ident_pci_c[] = 82 "\n$Id: pci.c,v 2.0.0.12 94/09/15 20:49:23 wolf Exp $\n"; 83 84/* 85** Function prototypes missing in system headers 86*/ 87 88#if ! (__FreeBSD__ >= 2) 89extern pmap_t pmap_kernel(void); 90static vm_offset_t pmap_mapdev (vm_offset_t paddr, vm_size_t vsize); 91#endif 92 93 94/*======================================================== 95** 96** Autoconfiguration (of isa bus) 97** 98**======================================================== 99*/ 100 101/* 102** per slot data structure for passing interupts.. 103*/ 104 105static struct { 106 u_short number; 107 u_short isanum; 108 struct { 109 int (*proc)(int dev); 110 dev_t unit; 111 } vector[PCI_MAX_DPI]; 112} pcidata [NPCI]; 113 114/* 115** check device ready 116*/ 117static int pciprobe (struct isa_device *dev) 118{ 119 if (dev->id_unit >= NPCI) 120 return (0); 121 122 if (!pci_conf_mode()) 123 return (0); 124 125 return (-1); 126} 127 128/* 129** initialize the driver structure 130*/ 131static int pciattach (struct isa_device *isdp) 132{ 133 pcidata[isdp->id_unit].number = 0; 134 pcidata[isdp->id_unit].isanum = ffs(isdp->id_irq)-1; 135 return (1); 136} 137 138/* 139** ISA driver structure 140*/ 141 142struct isa_driver pcidriver = { 143 pciprobe, 144 pciattach, 145 "pci" 146}; 147 148/*======================================================== 149** 150** Interrupt forward from isa to pci devices. 151** 152**======================================================== 153*/ 154 155 156void pciintr (int unit) 157{ 158 u_short i; 159 if (unit >= NPCI) return; 160 for (i=0; i<pcidata[unit].number; i++) { 161 (void)(*pcidata[unit].vector[i].proc) 162 (pcidata[unit].vector[i].unit); 163 }; 164} 165 166 167/*======================================================== 168** 169** Autoconfiguration of pci devices. 170** 171** This is reverse to the isa configuration. 172** (1) find a pci device. 173** (2) look for a driver. 174** 175**======================================================== 176*/ 177 178/*-------------------------------------------------------- 179** 180** The pci devices can be mapped to any address. 181** As default we start at the last gigabyte. 182** 183**-------------------------------------------------------- 184*/ 185 186#ifndef PCI_PMEM_START 187#define PCI_PMEM_START 0xc0000000 188#endif 189 190static vm_offset_t pci_paddr = PCI_PMEM_START; 191 192/*--------------------------------------------------------- 193** 194** pci_configure () 195** 196**--------------------------------------------------------- 197*/ 198 199static void not_supported (pcici_t tag, u_long type); 200 201void pci_configure() 202{ 203 u_char device,last_device; 204 u_short bus,last_bus; 205 pcici_t tag; 206 pcidi_t type; 207 u_long data; 208 int unit; 209 int intpin; 210 int isanum; 211 int pci_mode; 212 213 struct pci_driver *drp; 214 struct pci_device *dvp; 215 216 /* 217 ** check pci bus present 218 */ 219 220 pci_mode = pci_conf_mode (); 221 if (!pci_mode) return; 222 last_bus = pci_last_bus (); 223 last_device = pci_mode==1 ? 31 : 15; 224 225 /* 226 ** hello world .. 227 */ 228 229#ifndef PCI_QUIET 230 printf ("pci*: mode=%d, scanning bus 0..%d, device 0..%d.\n", 231 pci_mode, last_bus, last_device); 232#endif 233 234 for (bus=0;bus<=last_bus; bus++) 235 for (device=0; device<=last_device; device ++) { 236 tag = pcitag (bus, device, 0); 237 type = pci_conf_read (tag, PCI_ID_REG); 238 239 if ((!type) || (type==0xfffffffful)) continue; 240 241 /* 242 ** lookup device in ioconfiguration: 243 */ 244 245 for (dvp = pci_devtab; dvp->pd_device_id; dvp++) { 246 if (dvp->pd_device_id == type) break; 247 }; 248 drp = dvp->pd_driver; 249 250 if (!dvp->pd_device_id) { 251 252 /* 253 ** not found 254 ** try to dig out some information. 255 ** 256 ** By Garrett Wollman 257 ** <wollman@halloran-eldar.lcs.mit.edu> 258 */ 259 260 int data = pci_conf_read(tag, PCI_CLASS_REG); 261 vm_offset_t va; 262 vm_offset_t pa; 263 int reg; 264 265 switch (data & PCI_CLASS_MASK) { 266 267 case PCI_CLASS_PREHISTORIC: 268 if ((data & PCI_SUBCLASS_MASK) 269 != PCI_SUBCLASS_PREHISTORIC_VGA) 270 break; 271 272 case PCI_CLASS_DISPLAY: 273 for (reg = PCI_MAP_REG_START; 274 reg < PCI_MAP_REG_END; 275 reg += 4) { 276 data = pci_map_mem(tag, reg, &va, &pa); 277 if (data == 0) 278 printf ( 279 "pci%d:%d: mapped VGA-like device at physaddr 0x%lx\n", 280 bus, device, (u_long)pa); 281 282 } 283 continue; 284 }; 285#ifndef PCI_QUIET 286 printf("pci%d:%d: ", bus, device); 287 not_supported (tag, type); 288#endif 289 }; 290 291 if (!drp) { 292 if(dvp->pd_flags & PDF_LOADABLE) { 293 printf("%s: loadable device on pci%d:%d\n", 294 dvp->pd_name, bus, device); 295 } 296 continue; 297 } 298 299 /* 300 ** found it. 301 ** probe returns the device unit. 302 */ 303 304 unit = (*drp->probe) (tag); 305 306 if (unit<0) { 307 printf ("%s <%s>: probe failed on pci%d:%d\n", 308 dvp->pd_name, drp->name, bus, device); 309 continue; 310 }; 311 312 printf ("%s%d <%s>", dvp->pd_name, unit, drp->name); 313 314 /* 315 ** install interrupts 316 */ 317 318 data = pci_conf_read (tag, PCI_INTERRUPT_REG); 319 intpin = PCI_INTERRUPT_PIN_EXTRACT(data); 320 if (intpin) { 321 int idx=NPCI; 322 323 /* 324 ** Usage of int line register (if set by bios) 325 ** Matt Thomas <thomas@lkg.dec.com> 326 */ 327 328 isanum = PCI_INTERRUPT_LINE_EXTRACT(data); 329 if (isanum) { 330 331 /* 332 ** @INT@ FIXME!!! 333 ** 334 ** Should try to use "register_interupt" 335 ** at this point. 336 */ 337 338 printf (" line=%d", isanum); 339 for (idx = 0; idx < NPCI; idx++) { 340 if (pcidata[idx].isanum == isanum) 341 break; 342 }; 343 }; 344 345 /* 346 ** Or take the device number as index ... 347 */ 348 349 if (idx >= NPCI) idx = device; 350 351 /* 352 ** And install the interrupt. 353 */ 354 355 if (idx<NPCI) { 356 u_short entry = pcidata[idx].number; 357 printf (" irq %c", 0x60+intpin); 358 if (entry < PCI_MAX_DPI) { 359 pcidata[idx].vector[entry].proc = drp->intr; 360 pcidata[idx].vector[entry].unit = unit; 361 entry++; 362 }; 363 printf (" isa=%d [%d]",pcidata[idx].isanum, entry); 364 pcidata[idx].number=entry; 365 } else { 366 printf (" no int"); 367 }; 368 }; 369 370 /* 371 ** enable memory access 372 */ 373 374 data = pci_conf_read (tag, PCI_COMMAND_STATUS_REG) 375 & 0xffff | PCI_COMMAND_MEM_ENABLE; 376 377 pci_conf_write (tag, (u_char) PCI_COMMAND_STATUS_REG, data); 378 379 /* 380 ** attach device 381 ** may produce additional log messages, 382 ** i.e. when installing subdevices. 383 */ 384 385 printf (" on pci%d:%d\n", bus, device); 386 387 (void) (*drp->attach) (tag); 388 } 389 390#ifndef PCI_QUIET 391 printf ("pci uses physical addresses from 0x%lx to 0x%lx\n", 392 (u_long)PCI_PMEM_START, (u_long)pci_paddr); 393#endif 394} 395 396/*----------------------------------------------------------------------- 397** 398** Map device into port space. 399** 400** PCI-Specification: 6.2.5.1: address maps 401** 402**----------------------------------------------------------------------- 403*/ 404 405int pci_map_port (pcici_t tag, u_long reg, u_short* pa) 406{ 407 /* 408 ** @MAPIO@ not yet implemented. 409 */ 410 return (ENOSYS); 411} 412 413/*----------------------------------------------------------------------- 414** 415** Map device into virtual and physical space 416** 417** PCI-Specification: 6.2.5.1: address maps 418** 419**----------------------------------------------------------------------- 420*/ 421 422int pci_map_mem (pcici_t tag, u_long reg, vm_offset_t* va, vm_offset_t* pa) 423{ 424 u_long data; 425 vm_size_t vsize; 426 vm_offset_t vaddr; 427 428 /* 429 ** sanity check 430 */ 431 432 if (reg <= PCI_MAP_REG_START || reg >= PCI_MAP_REG_END || (reg & 3)) 433 return (EINVAL); 434 435 /* 436 ** get size and type of memory 437 ** 438 ** type is in the lowest four bits. 439 ** If device requires 2^n bytes, the next 440 ** n-4 bits are read as 0. 441 */ 442 443 pci_conf_write (tag, reg, 0xfffffffful); 444 data = pci_conf_read (tag, reg); 445 446 switch (data & 0x0f) { 447 448 case PCI_MAP_MEMORY_TYPE_32BIT: /* 32 bit non cachable */ 449 break; 450 451 default: /* unknown */ 452 return (EINVAL); 453 }; 454 455 /* 456 ** mask out the type, 457 ** and round up to a page size 458 */ 459 460 vsize = round_page (-(data & PCI_MAP_MEMORY_ADDRESS_MASK)); 461 462 if (!vsize) return (EINVAL); 463 464 /* 465 ** align physical address to virtual size 466 */ 467 468 if (data = pci_paddr % vsize) 469 pci_paddr += vsize - data; 470 471 vaddr = pmap_mapdev (pci_paddr, vsize); 472 473 if (!vaddr) return (EINVAL); 474 475#ifndef PCI_QUIET 476 /* 477 ** display values. 478 */ 479 480 printf ("\treg%d: virtual=0x%lx physical=0x%lx\n", 481 reg, (u_long)vaddr, (u_long)pci_paddr); 482#endif 483 484 /* 485 ** return them to the driver 486 */ 487 488 *va = vaddr; 489 *pa = pci_paddr; 490 491 /* 492 ** set device address 493 */ 494 495 pci_conf_write (tag, reg, pci_paddr); 496 497 /* 498 ** and don't forget to increment pci_paddr 499 */ 500 501 pci_paddr += vsize; 502 503 return (0); 504} 505 506/*----------------------------------------------------------- 507** 508** Mapping of physical to virtual memory 509** 510**----------------------------------------------------------- 511*/ 512 513#if ! (__FreeBSD__ >= 2) 514 515extern vm_map_t kernel_map; 516 517static vm_offset_t pmap_mapdev (vm_offset_t paddr, vm_size_t vsize) 518{ 519 vm_offset_t vaddr,value; 520 u_long result; 521 522 vaddr = vm_map_min (kernel_map); 523 524 result = vm_map_find (kernel_map, (void*)0, (vm_offset_t) 0, 525 &vaddr, vsize, TRUE); 526 527 if (result != KERN_SUCCESS) { 528 printf (" vm_map_find failed(%d)\n", result); 529 return (0); 530 }; 531 532 /* 533 ** map physical 534 */ 535 536 value = vaddr; 537 while (vsize >= NBPG) { 538 pmap_enter (pmap_kernel(), vaddr, paddr, 539 VM_PROT_READ|VM_PROT_WRITE, TRUE); 540 vaddr += NBPG; 541 paddr += NBPG; 542 vsize -= NBPG; 543 }; 544 return (value); 545} 546#endif 547 548/*----------------------------------------------------------- 549** 550** Display of unknown devices. 551** 552**----------------------------------------------------------- 553*/ 554struct vt { 555 u_short ident; 556 char* name; 557}; 558 559static struct vt VendorTable[] = { 560 {0x1002, "ATI TECHNOLOGIES INC"}, 561 {0x1011, "DIGITAL EQUIPMENT COMPANY"}, 562 {0x101A, "NCR"}, 563 {0x102B, "MATROX"}, 564 {0x1045, "OPTI"}, 565 {0x5333, "S3 INC."}, 566 {0x8086, "INTEL CORPORATION"}, 567 {0,0} 568}; 569 570static const char *const majclasses[] = { 571 "old", "storage", "network", "display", 572 "multimedia", "memory", "bridge" 573}; 574 575void not_supported (pcici_t tag, u_long type) 576{ 577 u_char reg; 578 u_long data; 579 struct vt * vp; 580 581 /* 582 ** lookup the names. 583 */ 584 585 for (vp=VendorTable; vp->ident; vp++) 586 if (vp->ident == (type & 0xffff)) 587 break; 588 589 /* 590 ** and display them. 591 */ 592 593 if (vp->ident) printf (vp->name); 594 else printf ("vendor=0x%lx", type & 0xffff); 595 596 printf (", device=0x%lx", type >> 16); 597 598 data = (pci_conf_read(tag, PCI_CLASS_REG) >> 24) & 0xff; 599 if (data < sizeof(majclasses) / sizeof(majclasses[0])) 600 printf(", class=%s", majclasses[data]); 601 602 printf (" [not supported]\n"); 603 604 for (reg=PCI_MAP_REG_START; reg<PCI_MAP_REG_END; reg+=4) { 605 data = pci_conf_read (tag, reg); 606 if (!data) continue; 607 switch (data&7) { 608 609 case 1: 610 case 5: 611 printf (" map(%lx): io(%lx)\n", reg, data & ~3); 612 break; 613 case 0: 614 printf (" map(%lx): mem32(%lx)\n", reg, data & ~7); 615 break; 616 case 2: 617 printf (" map(%lx): mem20(%lx)\n", reg, data & ~7); 618 break; 619 case 4: 620 printf (" map(%lx): mem64(%lx)\n", reg, data & ~7); 621 break; 622 } 623 } 624} 625#endif 626