pci_pir.c revision 16354
11590Srgrimes/************************************************************************** 21590Srgrimes** 31590Srgrimes** $Id: pcibus.c,v 1.24 1996/04/30 21:37:21 se Exp $ 41590Srgrimes** 51590Srgrimes** pci bus subroutines for i386 architecture. 61590Srgrimes** 71590Srgrimes** FreeBSD 81590Srgrimes** 91590Srgrimes**------------------------------------------------------------------------- 101590Srgrimes** 111590Srgrimes** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved. 121590Srgrimes** 131590Srgrimes** Redistribution and use in source and binary forms, with or without 141590Srgrimes** modification, are permitted provided that the following conditions 151590Srgrimes** are met: 161590Srgrimes** 1. Redistributions of source code must retain the above copyright 171590Srgrimes** notice, this list of conditions and the following disclaimer. 181590Srgrimes** 2. Redistributions in binary form must reproduce the above copyright 191590Srgrimes** notice, this list of conditions and the following disclaimer in the 201590Srgrimes** documentation and/or other materials provided with the distribution. 211590Srgrimes** 3. The name of the author may not be used to endorse or promote products 221590Srgrimes** derived from this software without specific prior written permission. 231590Srgrimes** 241590Srgrimes** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 251590Srgrimes** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 261590Srgrimes** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 271590Srgrimes** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 281590Srgrimes** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 291590Srgrimes** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 301590Srgrimes** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 311590Srgrimes** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 321590Srgrimes** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 331590Srgrimes** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 341590Srgrimes** 3528564Scharnier*************************************************************************** 361590Srgrimes*/ 371590Srgrimes 381590Srgrimes#include "vector.h" 391590Srgrimes 4096438Smike#include <sys/param.h> 411590Srgrimes#include <sys/systm.h> 421590Srgrimes#include <sys/kernel.h> 4396438Smike 4428564Scharnier#include <i386/isa/icu.h> 451590Srgrimes#include <i386/isa/isa.h> 4696438Smike#include <i386/isa/isa_device.h> 4796438Smike 4896438Smike#include <pci/pcivar.h> 491590Srgrimes#include <pci/pcireg.h> 501590Srgrimes#include <pci/pcibus.h> 511590Srgrimes 521590Srgrimes/*----------------------------------------------------------------- 531590Srgrimes** 541590Srgrimes** The following functions are provided by the pci bios. 551590Srgrimes** They are used only by the pci configuration. 5691661Sjmallett** 571590Srgrimes** pcibus_setup(): 581590Srgrimes** Probes for a pci system. 5991661Sjmallett** Sets pci_maxdevice and pci_mechanism. 6091661Sjmallett** 6128564Scharnier** pcibus_tag(): 621590Srgrimes** Creates a handle for pci configuration space access. 6391661Sjmallett** This handle is given to the read/write functions. 641590Srgrimes** 6528564Scharnier** pcibus_ftag(): 661590Srgrimes** Creates a modified handle. 6728564Scharnier** 681590Srgrimes** pcibus_read(): 6987304Sdwmalone** Read a long word from the pci configuration space. 7089882Smike** Requires a tag (from pcitag) and the register 7189882Smike** number (should be a long word alligned one). 721590Srgrimes** 7392922Simp** pcibus_write(): 7492922Simp** Writes a long word to the pci configuration space. 7592922Simp** Requires a tag (from pcitag), the register number 7692922Simp** (should be a long word alligned one), and a value. 7719078Swosch** 781590Srgrimes** pcibus_regirq(): 7996386Salfred** Register an interupt handler for a pci device. 801590Srgrimes** Requires a tag (from pcitag), the register number 8119078Swosch** (should be a long word alligned one), and a value. 821590Srgrimes** 8389882Smike**----------------------------------------------------------------- 8419078Swosch*/ 8519078Swosch 8689882Smikestatic int 8789882Smikepcibus_check (void); 8819078Swosch 8919078Swoschstatic void 9032780Swoschpcibus_setup (void); 9132780Swosch 9232780Swoschstatic pcici_t 9389882Smikepcibus_tag (u_char bus, u_char device, u_char func); 9489882Smike 9589882Smikestatic pcici_t 9689882Smikepcibus_ftag (pcici_t tag, u_char func); 9789882Smike 9889882Smikestatic u_long 9989882Smikepcibus_read (pcici_t tag, u_long reg); 10019078Swosch 10189882Smikestatic void 10289882Smikepcibus_write (pcici_t tag, u_long reg, u_long data); 10319078Swosch 10419078Swoschstatic int 10532780Swoschpcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr); 10689882Smike 10789882Smikestatic int 10832780Swoschpcibus_ihandler_detach (int irq, inthand2_t *func); 10932780Swosch 11019078Swoschstatic int 11128564Scharnierpcibus_imask_include (int irq, unsigned* maskptr); 11219078Swosch 11319078Swoschstatic int 11419078Swoschpcibus_imask_exclude (int irq, unsigned* maskptr); 11519078Swosch 11619078Swoschstatic struct pcibus i386pci = { 11719078Swosch "pci", 1181590Srgrimes pcibus_setup, 1191590Srgrimes pcibus_tag, 1201590Srgrimes pcibus_ftag, 12128564Scharnier pcibus_read, 1221590Srgrimes pcibus_write, 1231590Srgrimes ICU_LEN, 1241590Srgrimes pcibus_ihandler_attach, 1251590Srgrimes pcibus_ihandler_detach, 1261590Srgrimes pcibus_imask_include, 1271590Srgrimes pcibus_imask_exclude, 1281590Srgrimes}; 1291590Srgrimes 1301590Srgrimes/* 1311590Srgrimes** Announce structure to generic driver 1321590Srgrimes*/ 1331590Srgrimes 13419078SwoschDATA_SET (pcibus_set, i386pci); 13596438Smike 1361590Srgrimes/*-------------------------------------------------------------------- 13719078Swosch** 13819078Swosch** Determine configuration mode 13919078Swosch** 140102890Sfanf**-------------------------------------------------------------------- 14119078Swosch*/ 14219078Swosch 14319078Swosch 14419078Swosch#define CONF1_ADDR_PORT 0x0cf8 14519078Swosch#define CONF1_DATA_PORT 0x0cfc 14619078Swosch 14719078Swosch#define CONF1_ENABLE 0x80000000ul 14819078Swosch#define CONF1_ENABLE_CHK 0x80000000ul 14919078Swosch#define CONF1_ENABLE_MSK 0x7ff00000ul 15019078Swosch#define CONF1_ENABLE_CHK1 0xff000001ul 15119078Swosch#define CONF1_ENABLE_MSK1 0x80000001ul 15219078Swosch#define CONF1_ENABLE_RES1 0x80000000ul 15319078Swosch 15496438Smike#define CONF2_ENABLE_PORT 0x0cf8 15519078Swosch#define CONF2_FORWARD_PORT 0x0cfa 1561590Srgrimes 1571590Srgrimes#define CONF2_ENABLE_CHK 0x0e 15858828Ssheldonh#define CONF2_ENABLE_RES 0x0e 15992623Sjmallett 16092623Sjmallettstatic int 16192623Sjmallettpcibus_check (void) 16292623Sjmallett{ 16392623Sjmallett u_char device; 1641590Srgrimes 16591661Sjmallett if (bootverbose) printf ("pcibus_check:\tdevice "); 1661590Srgrimes 1671590Srgrimes for (device = 0; device < pci_maxdevice; device++) { 1681590Srgrimes unsigned long id; 16919078Swosch if (bootverbose) 17019078Swosch printf ("%d ", device); 17119078Swosch id = pcibus_read (pcibus_tag (0,device,0), 0); 17228564Scharnier if (id && id != 0xfffffffful) { 1731590Srgrimes if (bootverbose) printf ("is there (id=%08lx)\n", id); 1741590Srgrimes return 1; 17591661Sjmallett } 17622887Swosch } 17791661Sjmallett if (bootverbose) 17891661Sjmallett printf ("-- nothing found\n"); 17991661Sjmallett return 0; 18092623Sjmallett} 18192623Sjmallett 18292623Sjmallettstatic void 18392623Sjmallettpcibus_setup (void) 18492623Sjmallett{ 18592623Sjmallett unsigned long mode1res,oldval1; 18692623Sjmallett unsigned char mode2res,oldval2; 18792623Sjmallett 18892623Sjmallett oldval1 = inl (CONF1_ADDR_PORT); 18992623Sjmallett 19092623Sjmallett if (bootverbose) { 19191661Sjmallett printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); 19292623Sjmallett } 19392637Sjmallett 19492623Sjmallett /*--------------------------------------- 19592623Sjmallett ** Assume configuration mechanism 1 for now ... 19692623Sjmallett **--------------------------------------- 1971590Srgrimes */ 19892623Sjmallett 19992623Sjmallett if ((oldval1 & CONF1_ENABLE_MSK) == 0) { 20032780Swosch 201102890Sfanf pci_mechanism = 1; 20232780Swosch pci_maxdevice = 32; 20332780Swosch 20432780Swosch outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 20532780Swosch outb (CONF1_ADDR_PORT +3, 0); 20632780Swosch mode1res = inl (CONF1_ADDR_PORT); 20732780Swosch outl (CONF1_ADDR_PORT, oldval1); 20832780Swosch 20992623Sjmallett if (bootverbose) 21092623Sjmallett printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n", 21192623Sjmallett mode1res, CONF1_ENABLE_CHK); 21292623Sjmallett 21392623Sjmallett if (mode1res) { 21492623Sjmallett if (pcibus_check()) 21592623Sjmallett return; 21692623Sjmallett }; 21792623Sjmallett 21892623Sjmallett outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 21992623Sjmallett mode1res = inl(CONF1_ADDR_PORT); 22092623Sjmallett outl (CONF1_ADDR_PORT, oldval1); 22192623Sjmallett 22292623Sjmallett if (bootverbose) 22392623Sjmallett printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n", 22492623Sjmallett mode1res, CONF1_ENABLE_CHK1); 22592623Sjmallett 22692623Sjmallett if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 22792623Sjmallett if (pcibus_check()) 22892623Sjmallett return; 2291590Srgrimes }; 2301590Srgrimes } 2311590Srgrimes 2321590Srgrimes /*--------------------------------------- 23319078Swosch ** Try configuration mechanism 2 ... 23419078Swosch **--------------------------------------- 23519078Swosch */ 23632780Swosch 23792623Sjmallett oldval2 = inb (CONF2_ENABLE_PORT); 23892623Sjmallett 23992623Sjmallett if (bootverbose) { 24058666Ssheldonh printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); 24132780Swosch } 24258666Ssheldonh 24358666Ssheldonh if ((oldval2 & 0xf0) == 0) { 24492623Sjmallett 24532780Swosch pci_mechanism = 2; 24632780Swosch pci_maxdevice = 16; 24732780Swosch 24892623Sjmallett outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 24992623Sjmallett mode2res = inb(CONF2_ENABLE_PORT); 2501590Srgrimes outb (CONF2_ENABLE_PORT, oldval2); 25122887Swosch 2521590Srgrimes if (bootverbose) 2531590Srgrimes printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n", 25491661Sjmallett mode2res, CONF2_ENABLE_CHK); 2551590Srgrimes 2561590Srgrimes if (mode2res == CONF2_ENABLE_RES) { 25728564Scharnier if (bootverbose) 2581590Srgrimes printf ("pcibus_setup(2a):\tnow trying mechanism 2\n"); 2591590Srgrimes 26091661Sjmallett if (pcibus_check()) 26191661Sjmallett return; 26291661Sjmallett } 26391661Sjmallett } 26491661Sjmallett 26591661Sjmallett /*--------------------------------------- 2661590Srgrimes ** No PCI bus host bridge found 26724263Swosch **--------------------------------------- 26822887Swosch */ 26922887Swosch 27022887Swosch pci_mechanism = 0; 27122887Swosch pci_maxdevice = 0; 27228564Scharnier} 27328564Scharnier 27428564Scharnier/*-------------------------------------------------------------------- 27522887Swosch** 27622887Swosch** Build a pcitag from bus, device and function number 27758666Ssheldonh** 27858666Ssheldonh**-------------------------------------------------------------------- 27958666Ssheldonh*/ 28022887Swosch 28122887Swoschstatic pcici_t 2821590Srgrimespcibus_tag (unsigned char bus, unsigned char device, unsigned char func) 2831590Srgrimes{ 2841590Srgrimes pcici_t tag; 2851590Srgrimes 2861590Srgrimes tag.cfg1 = 0; 2871590Srgrimes if (func >= 8) return tag; 2881590Srgrimes 2891590Srgrimes switch (pci_mechanism) { 290102890Sfanf 29122887Swosch case 1: 29222887Swosch if (device < 32) { 29322887Swosch tag.cfg1 = CONF1_ENABLE 2941590Srgrimes | (((u_long) bus ) << 16ul) 29558666Ssheldonh | (((u_long) device) << 11ul) 2961590Srgrimes | (((u_long) func ) << 8ul); 29758666Ssheldonh } 2981590Srgrimes break; 29958666Ssheldonh case 2: 3001590Srgrimes if (device < 16) { 3011590Srgrimes tag.cfg2.port = 0xc000 | (device << 8ul); 3021590Srgrimes tag.cfg2.enable = 0xf0 | (func << 1ul); 30322887Swosch tag.cfg2.forward = bus; 30422887Swosch } 3051590Srgrimes break; 30658666Ssheldonh }; 3071590Srgrimes return tag; 3081590Srgrimes} 309102890Sfanf 31022887Swoschstatic pcici_t 31122887Swoschpcibus_ftag (pcici_t tag, u_char func) 31222887Swosch{ 3131590Srgrimes switch (pci_mechanism) { 31458666Ssheldonh 3151590Srgrimes case 1: 3161590Srgrimes tag.cfg1 &= ~0x700ul; 317102890Sfanf tag.cfg1 |= (((u_long) func) << 8ul); 31822887Swosch break; 31922887Swosch case 2: 3201590Srgrimes tag.cfg2.enable = 0xf0 | (func << 1ul); 32158666Ssheldonh break; 3221590Srgrimes }; 3231590Srgrimes return tag; 3241590Srgrimes} 325102890Sfanf 32622894Swosch/*-------------------------------------------------------------------- 32722894Swosch** 32828564Scharnier** Read register from configuration space. 3291590Srgrimes** 3301590Srgrimes**-------------------------------------------------------------------- 3311590Srgrimes*/ 3321590Srgrimes 3331590Srgrimesstatic u_long 33491661Sjmallettpcibus_read (pcici_t tag, u_long reg) 33596438Smike{ 33691661Sjmallett u_long addr, data = 0; 33791661Sjmallett 33891661Sjmallett if (!tag.cfg1) return (0xfffffffful); 33991661Sjmallett 34095101Sjmallett switch (pci_mechanism) { 34195101Sjmallett 34295101Sjmallett case 1: 34395101Sjmallett addr = tag.cfg1 | (reg & 0xfc); 34491661Sjmallett#ifdef PCI_DEBUG 34591661Sjmallett printf ("pci_conf_read(1): addr=%x ", addr); 34691661Sjmallett#endif 34795103Sjmallett outl (CONF1_ADDR_PORT, addr); 34891661Sjmallett data = inl (CONF1_DATA_PORT); 34991661Sjmallett outl (CONF1_ADDR_PORT, 0 ); 35028564Scharnier break; 35196438Smike 3521590Srgrimes case 2: 35396943Sjmallett addr = tag.cfg2.port | (reg & 0xfc); 35496943Sjmallett#ifdef PCI_DEBUG 35596943Sjmallett printf ("pci_conf_read(2): addr=%x ", addr); 35696943Sjmallett#endif 35796943Sjmallett outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); 3581590Srgrimes outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 3591590Srgrimes 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 381static void 382pcibus_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 425static int 426pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr) 427{ 428 char buf[16]; 429 char *cp; 430 int free_id, id, result; 431 432 sprintf(buf, "pci irq%d", irq); 433 for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) { 434 if (strcmp(cp, buf) == 0) 435 break; 436 if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0) 437 free_id = id; 438 while (*cp++ != '\0') 439 ; 440 } 441 if (id == NR_DEVICES) { 442 id = free_id; 443 if (id == 0) { 444 /* 445 * All pci irq counters are in use, perhaps because 446 * config is old so there aren't any. Abuse the 447 * clk0 counter. 448 */ 449 printf ( 450 "pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n", 451 irq); 452 } 453 } 454 result = register_intr( 455 irq, /* isa irq */ 456 id, /* device id */ 457 0, /* flags? */ 458 func, /* handler */ 459 maskptr, /* mask pointer */ 460 arg); /* handler arg */ 461 462 if (result) { 463 printf ("@@@ pcibus_ihandler_attach: result=%d\n", result); 464 return (result); 465 }; 466 update_intr_masks(); 467 468 INTREN ((1ul<<irq)); 469 return (0); 470} 471 472static int 473pcibus_ihandler_detach (int irq, inthand2_t *func) 474{ 475 int result; 476 477 INTRDIS ((1ul<<irq)); 478 479 result = unregister_intr (irq, func); 480 481 if (result) 482 printf ("@@@ pcibus_ihandler_detach: result=%d\n", result); 483 484 update_intr_masks(); 485 486 return (result); 487} 488 489static int 490pcibus_imask_include (int irq, unsigned* maskptr) 491{ 492 unsigned mask; 493 494 if (!maskptr) return (0); 495 496 mask = 1ul << irq; 497 498 if (*maskptr & mask) 499 return (-1); 500 501 INTRMASK (*maskptr, mask); 502 update_intr_masks(); 503 504 return (0); 505} 506 507static int 508pcibus_imask_exclude (int irq, unsigned* maskptr) 509{ 510 unsigned mask; 511 512 if (!maskptr) return (0); 513 514 mask = 1ul << irq; 515 516 if (! (*maskptr & mask)) 517 return (-1); 518 519 *maskptr &= ~mask; 520 update_intr_masks(); 521 522 return (0); 523} 524