pci_pir.c revision 11378
16104Sse/************************************************************************** 26104Sse** 311378Sse** $Id: pcibus.c,v 1.15 1995/09/22 19:10:54 se Exp $ 46104Sse** 56104Sse** pci bus subroutines for i386 architecture. 66104Sse** 76104Sse** FreeBSD 86104Sse** 96104Sse**------------------------------------------------------------------------- 106104Sse** 116104Sse** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved. 126104Sse** 136104Sse** Redistribution and use in source and binary forms, with or without 146104Sse** modification, are permitted provided that the following conditions 156104Sse** are met: 166104Sse** 1. Redistributions of source code must retain the above copyright 176104Sse** notice, this list of conditions and the following disclaimer. 186104Sse** 2. Redistributions in binary form must reproduce the above copyright 196104Sse** notice, this list of conditions and the following disclaimer in the 206104Sse** documentation and/or other materials provided with the distribution. 216104Sse** 3. The name of the author may not be used to endorse or promote products 226104Sse** derived from this software without specific prior written permission. 236104Sse** 246104Sse** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 256104Sse** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 266104Sse** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 276104Sse** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 286104Sse** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 296104Sse** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 306104Sse** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 316104Sse** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 326104Sse** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 336104Sse** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 346104Sse** 356104Sse*************************************************************************** 366104Sse*/ 376104Sse 386734Sbde#include <sys/param.h> 396734Sbde#include <sys/systm.h> 406734Sbde#include <sys/kernel.h> 416734Sbde 4210807Sse#include <machine/cpu.h> /* bootverbose */ 4310807Sse 446734Sbde#include <i386/isa/icu.h> 456104Sse#include <i386/isa/isa.h> 466104Sse#include <i386/isa/isa_device.h> 476104Sse 486104Sse#include <pci/pcivar.h> 496104Sse#include <pci/pcireg.h> 506104Sse#include <pci/pcibus.h> 516104Sse 526104Sse/*----------------------------------------------------------------- 536104Sse** 546104Sse** The following functions are provided by the pci bios. 556104Sse** They are used only by the pci configuration. 566104Sse** 577234Sse** pcibus_setup(): 586104Sse** Probes for a pci system. 597234Sse** Sets pci_maxdevice and pci_mechanism. 606104Sse** 616104Sse** pcibus_tag(): 627234Sse** Creates a handle for pci configuration space access. 637234Sse** This handle is given to the read/write functions. 646104Sse** 657234Sse** pcibus_ftag(): 667234Sse** Creates a modified handle. 677234Sse** 686104Sse** pcibus_read(): 696104Sse** Read a long word from the pci configuration space. 706104Sse** Requires a tag (from pcitag) and the register 716104Sse** number (should be a long word alligned one). 726104Sse** 736104Sse** pcibus_write(): 746104Sse** Writes a long word to the pci configuration space. 756104Sse** Requires a tag (from pcitag), the register number 766104Sse** (should be a long word alligned one), and a value. 776104Sse** 786104Sse** pcibus_regirq(): 796104Sse** Register an interupt handler for a pci device. 806104Sse** Requires a tag (from pcitag), the register number 816104Sse** (should be a long word alligned one), and a value. 826104Sse** 836104Sse**----------------------------------------------------------------- 846104Sse*/ 856104Sse 8610887Ssestatic int 8710887Ssepcibus_check (void); 8810887Sse 897234Ssestatic void 907234Ssepcibus_setup (void); 916104Sse 926104Ssestatic pcici_t 936104Ssepcibus_tag (u_char bus, u_char device, u_char func); 946104Sse 957234Ssestatic pcici_t 967234Ssepcibus_ftag (pcici_t tag, u_char func); 977234Sse 986104Ssestatic u_long 996104Ssepcibus_read (pcici_t tag, u_long reg); 1006104Sse 1016104Ssestatic void 1026104Ssepcibus_write (pcici_t tag, u_long reg, u_long data); 1036104Sse 1046104Ssestatic int 1057234Ssepcibus_ihandler_attach (int irq, void(*ihandler)(), int arg, unsigned* maskp); 1066104Sse 1077234Ssestatic int 1087234Ssepcibus_ihandler_detach (int irq, void(*handler)()); 1097234Sse 1107234Ssestatic int 1117234Ssepcibus_imask_include (int irq, unsigned* maskptr); 1127234Sse 1137234Ssestatic int 1147234Ssepcibus_imask_exclude (int irq, unsigned* maskptr); 1157234Sse 1166104Ssestruct pcibus i386pci = { 1176104Sse "pci", 1187234Sse pcibus_setup, 1196104Sse pcibus_tag, 1207234Sse pcibus_ftag, 1216104Sse pcibus_read, 1226104Sse pcibus_write, 1237234Sse ICU_LEN, 1247234Sse pcibus_ihandler_attach, 1257234Sse pcibus_ihandler_detach, 1267234Sse pcibus_imask_include, 1277234Sse pcibus_imask_exclude, 1286104Sse}; 1296104Sse 1306104Sse/* 1316104Sse** Announce structure to generic driver 1326104Sse*/ 1336104Sse 1346104SseDATA_SET (pcibus_set, i386pci); 1356104Sse 1366104Sse/*-------------------------------------------------------------------- 1376104Sse** 1386104Sse** Determine configuration mode 1396104Sse** 1406104Sse**-------------------------------------------------------------------- 1416104Sse*/ 1426104Sse 1436104Sse 1446104Sse#define CONF1_ADDR_PORT 0x0cf8 1456104Sse#define CONF1_DATA_PORT 0x0cfc 1466104Sse 14710807Sse#define CONF1_ENABLE 0x80000000ul 14810960Sse#define CONF1_ENABLE_CHK 0x80000000ul 14910960Sse#define CONF1_ENABLE_CHK1 0xFF000001ul 15010960Sse#define CONF1_ENABLE_MSK1 0x80000000ul 15110887Sse#define CONF1_ENABLE_RES1 0x80000000ul 1526104Sse 1536104Sse#define CONF2_ENABLE_PORT 0x0cf8 1546104Sse#define CONF2_FORWARD_PORT 0x0cfa 1556104Sse 15610807Sse#define CONF2_ENABLE_CHK 0x0e 15710807Sse#define CONF2_ENABLE_RES 0x0e 1586104Sse 15910887Ssestatic int 16010887Ssepcibus_check (void) 16110887Sse{ 16210887Sse u_char device; 16310735Sse 16410960Sse if (bootverbose) printf ("pcibus_check:\tdevice "); 16510960Sse 16610887Sse for (device = 0; device < pci_maxdevice; device++) { 16711378Sse unsigned long id; 16811378Sse if (bootverbose) 16911378Sse printf ("%d ", device); 17011378Sse id = pcibus_read (pcibus_tag (0,device,0), 0); 17111378Sse if (id != 0xfffffffful) { 17211378Sse if (bootverbose) printf ("is there (id=%08lx)\n", id); 17310887Sse return 1; 17410960Sse } 17510887Sse } 17611378Sse if (bootverbose) 17711378Sse printf ("-- nothing found\n"); 17810887Sse return 0; 17910887Sse} 18010887Sse 1817234Ssestatic void 1827234Ssepcibus_setup (void) 1836104Sse{ 18410960Sse unsigned long mode1res,oldval; 18510960Sse unsigned char mode2res; 1866104Sse 18710960Sse oldval = inl (CONF1_ADDR_PORT); 18810960Sse outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 18910960Sse outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); 19010960Sse mode1res = inl(CONF1_ADDR_PORT); 19110960Sse mode2res = inb(CONF2_ENABLE_PORT); 19211378Sse outb (CONF2_ENABLE_PORT, 0); 19310960Sse outl (CONF1_ADDR_PORT, oldval); 19410960Sse 19510960Sse if (bootverbose) { 19610960Sse printf ("pcibus_setup(1):\tmode1res=0x%08lx (0x%08lx), " 19710960Sse "mode2res=0x%02x (0x%02x)\n", 19810960Sse mode1res,CONF1_ENABLE_CHK, 19910960Sse (int)mode2res,CONF2_ENABLE_CHK); 20010960Sse } 20110960Sse 2026104Sse /*--------------------------------------- 20310960Sse ** No PCI, if neither mode1res nor mode2res could be read back 2046104Sse **--------------------------------------- 2056104Sse */ 2066104Sse 20710960Sse if ((mode1res != CONF1_ENABLE_CHK) && (mode2res != CONF2_ENABLE_CHK)) { 20810960Sse return; 20910960Sse } 21010960Sse 21110960Sse /*--------------------------------------- 21210960Sse ** Assume configuration mechanism 1 for now ... 21310960Sse **--------------------------------------- 21410960Sse */ 21510960Sse 21610960Sse pci_mechanism = 1; 21710960Sse pci_maxdevice = 32; 21810960Sse 21910960Sse outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); 22010887Sse outb (CONF1_ADDR_PORT +3, 0); 22110960Sse mode1res = inl (CONF1_ADDR_PORT); 2226104Sse outl (CONF1_ADDR_PORT, oldval); 2236104Sse 22410960Sse if (bootverbose) 22510960Sse printf ("pcibus_setup(2):\tmode1res=0x%08lx (0x%08lx)\n", 22610960Sse mode1res, CONF1_ENABLE_CHK); 22710960Sse 22810960Sse if (mode1res) { 22910887Sse if (pcibus_check()) 23010887Sse return; 2316104Sse }; 2326104Sse 23310960Sse outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); 23410960Sse outl (CONF1_DATA_PORT, 0); 23510960Sse mode1res = inl(CONF1_ADDR_PORT); 23610960Sse outl (CONF1_ADDR_PORT, oldval); 2379360Sse 23810960Sse if (bootverbose) 23910960Sse printf ("pcibus_setup(3):\tmode1res=0x%08lx (0x%08lx)\n", 24010960Sse mode1res, CONF1_ENABLE_CHK1); 24110807Sse 24211378Sse if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { 24310887Sse if (pcibus_check()) 24410887Sse return; 2459360Sse }; 2469360Sse 24710960Sse /*--------------------------------------- 24810960Sse ** Try configuration mechanism 2 ... 24910960Sse **--------------------------------------- 25010887Sse */ 25110887Sse 25211378Sse if (bootverbose) 25311378Sse printf ("pcibus_setup(4):\tnow trying mechanism 2\n"); 25411378Sse 25510960Sse pci_mechanism = 2; 25610960Sse pci_maxdevice = 16; 25710887Sse 25810960Sse if (pcibus_check()) 25910960Sse return; 26010887Sse 2619360Sse /*--------------------------------------- 26210710Sse ** No PCI bus host bridge found 2636104Sse **--------------------------------------- 2646104Sse */ 26510710Sse 26610710Sse pci_mechanism = 0; 26710710Sse pci_maxdevice = 0; 2686104Sse} 2697234Sse 2706104Sse/*-------------------------------------------------------------------- 2716104Sse** 2726104Sse** Build a pcitag from bus, device and function number 2736104Sse** 2746104Sse**-------------------------------------------------------------------- 2756104Sse*/ 2766104Sse 2776104Ssestatic pcici_t 2786104Ssepcibus_tag (unsigned char bus, unsigned char device, unsigned char func) 2796104Sse{ 2806104Sse pcici_t tag; 2816104Sse 2826104Sse tag.cfg1 = 0; 2836104Sse if (func >= 8) return tag; 2846104Sse 2857234Sse switch (pci_mechanism) { 2866104Sse 2876104Sse case 1: 28810807Sse if (device < 32) { 28910807Sse tag.cfg1 = CONF1_ENABLE 29010807Sse | (((u_long) bus ) << 16ul) 29110807Sse | (((u_long) device) << 11ul) 29210807Sse | (((u_long) func ) << 8ul); 29310807Sse } 2946104Sse break; 2956104Sse case 2: 29610807Sse if (device < 16) { 29710807Sse tag.cfg2.port = 0xc000 | (device << 8ul); 29810807Sse tag.cfg2.enable = 0xf0 | (func << 1ul); 29910807Sse tag.cfg2.forward = bus; 30010807Sse } 3016104Sse break; 3026104Sse }; 3036104Sse return tag; 3046104Sse} 3057234Sse 3067234Ssestatic pcici_t 3077234Ssepcibus_ftag (pcici_t tag, u_char func) 3087234Sse{ 3097234Sse switch (pci_mechanism) { 3107234Sse 3117234Sse case 1: 3127234Sse tag.cfg1 &= ~0x700ul; 3137234Sse tag.cfg1 |= (((u_long) func) << 8ul); 3147234Sse break; 3157234Sse case 2: 31610807Sse tag.cfg2.enable = 0xf0 | (func << 1ul); 3177234Sse break; 3187234Sse }; 3197234Sse return tag; 3207234Sse} 3217234Sse 3226104Sse/*-------------------------------------------------------------------- 3236104Sse** 3246104Sse** Read register from configuration space. 3256104Sse** 3266104Sse**-------------------------------------------------------------------- 3276104Sse*/ 3286104Sse 3296104Ssestatic u_long 3306104Ssepcibus_read (pcici_t tag, u_long reg) 3316104Sse{ 3326104Sse u_long addr, data = 0; 3336104Sse 3346104Sse if (!tag.cfg1) return (0xfffffffful); 3356104Sse 3367234Sse switch (pci_mechanism) { 3376104Sse 3386104Sse case 1: 3396104Sse addr = tag.cfg1 | (reg & 0xfc); 3406104Sse#ifdef PCI_DEBUG 3416104Sse printf ("pci_conf_read(1): addr=%x ", addr); 3426104Sse#endif 3436104Sse outl (CONF1_ADDR_PORT, addr); 3446104Sse data = inl (CONF1_DATA_PORT); 3456104Sse outl (CONF1_ADDR_PORT, 0 ); 3466104Sse break; 3476104Sse 3486104Sse case 2: 3496104Sse addr = tag.cfg2.port | (reg & 0xfc); 3506104Sse#ifdef PCI_DEBUG 3516104Sse printf ("pci_conf_read(2): addr=%x ", addr); 3526104Sse#endif 3536104Sse outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); 3546104Sse outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 3556104Sse 3566104Sse data = inl ((u_short) addr); 3576104Sse 3586104Sse outb (CONF2_ENABLE_PORT, 0); 3596104Sse outb (CONF2_FORWARD_PORT, 0); 3606104Sse break; 3616104Sse }; 3626104Sse 3636104Sse#ifdef PCI_DEBUG 3646104Sse printf ("data=%x\n", data); 3656104Sse#endif 3666104Sse 3676104Sse return (data); 3686104Sse} 3697234Sse 3706104Sse/*-------------------------------------------------------------------- 3716104Sse** 3726104Sse** Write register into configuration space. 3736104Sse** 3746104Sse**-------------------------------------------------------------------- 3756104Sse*/ 3766104Sse 3776104Ssestatic void 3786104Ssepcibus_write (pcici_t tag, u_long reg, u_long data) 3796104Sse{ 3806104Sse u_long addr; 3816104Sse 3826104Sse if (!tag.cfg1) return; 3836104Sse 3847234Sse switch (pci_mechanism) { 3856104Sse 3866104Sse case 1: 3876104Sse addr = tag.cfg1 | (reg & 0xfc); 3886104Sse#ifdef PCI_DEBUG 3896104Sse printf ("pci_conf_write(1): addr=%x data=%x\n", 3906104Sse addr, data); 3916104Sse#endif 3926104Sse outl (CONF1_ADDR_PORT, addr); 3936104Sse outl (CONF1_DATA_PORT, data); 3946104Sse outl (CONF1_ADDR_PORT, 0 ); 3956104Sse break; 3966104Sse 3976104Sse case 2: 3986104Sse addr = tag.cfg2.port | (reg & 0xfc); 3996104Sse#ifdef PCI_DEBUG 4006104Sse printf ("pci_conf_write(2): addr=%x data=%x\n", 4016104Sse addr, data); 4026104Sse#endif 4036104Sse outb (CONF2_ENABLE_PORT, tag.cfg2.enable); 4046104Sse outb (CONF2_FORWARD_PORT, tag.cfg2.forward); 4056104Sse 4066104Sse outl ((u_short) addr, data); 4076104Sse 4086104Sse outb (CONF2_ENABLE_PORT, 0); 4096104Sse outb (CONF2_FORWARD_PORT, 0); 4106104Sse break; 4116104Sse }; 4126104Sse} 4137234Sse 4146104Sse/*----------------------------------------------------------------------- 4156104Sse** 4166104Sse** Register an interupt handler for a pci device. 4176104Sse** 4186104Sse**----------------------------------------------------------------------- 4196104Sse*/ 4206104Sse 4217234Ssestatic int 4227234Ssepcibus_ihandler_attach (int irq, void(*func)(), int arg, unsigned * maskptr) 4237234Sse{ 4247234Sse int result; 4257234Sse result = register_intr( 4267234Sse irq, /* isa irq */ 4277234Sse 0, /* deviced?? */ 4287234Sse 0, /* flags? */ 4297234Sse (inthand2_t*) func, /* handler */ 4307234Sse maskptr, /* mask pointer */ 4317234Sse arg); /* handler arg */ 4326104Sse 4337234Sse if (result) { 4347234Sse printf ("@@@ pcibus_ihandler_attach: result=%d\n", result); 4357234Sse return (result); 4367234Sse }; 4377234Sse update_intr_masks(); 4386104Sse 4397234Sse INTREN ((1ul<<irq)); 4407234Sse return (0); 4417234Sse} 4426104Sse 4436104Ssestatic int 4447234Ssepcibus_ihandler_detach (int irq, void(*func)()) 4457234Sse{ 4467234Sse int result; 4476104Sse 4487234Sse INTRDIS ((1ul<<irq)); 4496104Sse 4507234Sse result = unregister_intr (irq, (inthand2_t*) func); 4517234Sse 4527234Sse if (result) 4537234Sse printf ("@@@ pcibus_ihandler_detach: result=%d\n", result); 4547234Sse 4557234Sse update_intr_masks(); 4567234Sse 4577234Sse return (result); 4587234Sse} 4597234Sse 4607234Ssestatic int 4617234Ssepcibus_imask_include (int irq, unsigned* maskptr) 4626104Sse{ 4637234Sse unsigned mask; 4646104Sse 4657234Sse if (!maskptr) return (0); 4666104Sse 4676104Sse mask = 1ul << irq; 4686104Sse 4697234Sse if (*maskptr & mask) 4707234Sse return (-1); 4716104Sse 4726104Sse INTRMASK (*maskptr, mask); 4737234Sse update_intr_masks(); 4746104Sse 4757234Sse return (0); 4767234Sse} 4776104Sse 4787234Ssestatic int 4797234Ssepcibus_imask_exclude (int irq, unsigned* maskptr) 4807234Sse{ 4817234Sse unsigned mask; 4826104Sse 4837234Sse if (!maskptr) return (0); 4846104Sse 4857234Sse mask = 1ul << irq; 4867234Sse 4877234Sse if (! (*maskptr & mask)) 4887234Sse return (-1); 4897234Sse 4907234Sse *maskptr &= ~mask; 4917234Sse update_intr_masks(); 4927234Sse 4937234Sse return (0); 4946104Sse} 495