diff options
Diffstat (limited to 'sys/i386/pci/pci_pir.c')
| -rw-r--r-- | sys/i386/pci/pci_pir.c | 653 |
1 files changed, 190 insertions, 463 deletions
diff --git a/sys/i386/pci/pci_pir.c b/sys/i386/pci/pci_pir.c index 31de543cea12..df8479459099 100644 --- a/sys/i386/pci/pci_pir.c +++ b/sys/i386/pci/pci_pir.c @@ -1,538 +1,265 @@ -/************************************************************************** -** -** $Id: pcibus.c,v 1.34 1997/04/09 09:16:27 se Exp $ -** -** pci bus subroutines for i386 architecture. -** -** FreeBSD -** -**------------------------------------------------------------------------- -** -** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved. -** -** Redistribution and use in source and binary forms, with or without -** modification, are permitted provided that the following conditions -** are met: -** 1. Redistributions of source code must retain the above copyright -** notice, this list of conditions and the following disclaimer. -** 2. Redistributions in binary form must reproduce the above copyright -** notice, this list of conditions and the following disclaimer in the -** documentation and/or other materials provided with the distribution. -** 3. The name of the author may not be used to endorse or promote products -** derived from this software without specific prior written permission. -** -** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -** -*************************************************************************** -*/ - -#include "vector.h" - -#include <sys/param.h> +/* + * Copyright (c) 1997, Stefan Esser <se@freebsd.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id$ + * + */ + +#include <sys/types.h> #include <sys/systm.h> -#include <sys/kernel.h> - -#include <i386/isa/icu.h> -#include <i386/isa/isa_device.h> -#include <pci/pcivar.h> #include <pci/pcireg.h> -#include <pci/pcibus.h> - -/*----------------------------------------------------------------- -** -** The following functions are provided by the pci bios. -** They are used only by the pci configuration. -** -** pcibus_setup(): -** Probes for a pci system. -** Sets pci_maxdevice and pci_mechanism. -** -** pcibus_tag(): -** Creates a handle for pci configuration space access. -** This handle is given to the read/write functions. -** -** pcibus_ftag(): -** Creates a modified handle. -** -** pcibus_read(): -** Read a long word from the pci configuration space. -** Requires a tag (from pcitag) and the register -** number (should be a long word aligned one). -** -** pcibus_write(): -** Writes a long word to the pci configuration space. -** Requires a tag (from pcitag), the register number -** (should be a long word aligned one), and a value. -** -** pcibus_regirq(): -** Register an interrupt handler for a pci device. -** Requires a tag (from pcitag), the register number -** (should be a long word aligned one), and a value. -** -**----------------------------------------------------------------- -*/ - -static int -pcibus_check (void); - -static void -pcibus_setup (void); - -static pcici_t -pcibus_tag (u_char bus, u_char device, u_char func); - -static pcici_t -pcibus_ftag (pcici_t tag, u_char func); - -static u_long -pcibus_read (pcici_t tag, u_long reg); +#include <pci/pcivar.h> +#include <i386/isa/pcibus.h> -static void -pcibus_write (pcici_t tag, u_long reg, u_long data); +#ifdef PCI_COMPAT +/* XXX this is a terrible hack, which keeps the Tekram AMD SCSI driver happy */ +#define cfgmech pci_mechanism +int cfgmech; +#else +static int cfgmech; +#endif /* PCI_COMPAT */ +static int devmax; -static int -pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned* maskptr); +/* enable configuration space accesses and return data port address */ static int -pcibus_ihandler_detach (int irq, inthand2_t *func); +pci_cfgenable(unsigned bus, unsigned slot, unsigned func, int reg, int bytes) +{ + int dataport = 0; + + if (bus <= PCI_BUSMAX + && slot < devmax + && func <= PCI_FUNCMAX + && reg <= PCI_REGMAX + && bytes != 3 + && (unsigned) bytes <= 4 + && (reg & (bytes -1)) == 0) { + switch (cfgmech) { + case 1: + outl(CONF1_ADDR_PORT, + (bus << 16) | (slot << 11) | (func << 8) | reg); + dataport = CONF1_DATA_PORT; + break; + case 2: + outb(CONF2_ENABLE_PORT, 0xf0 | (func << 1)); + outb(CONF2_FORWARD_PORT, bus); + dataport = 0xc000 | (slot << 8) | reg; + break; + } + } + return (dataport); +} -static int -pcibus_imask_include (int irq, unsigned* maskptr); +/* disable configuration space accesses */ -static int -pcibus_imask_exclude (int irq, unsigned* maskptr); - -static struct pcibus i386pci = { - "pci", - pcibus_setup, - pcibus_tag, - pcibus_ftag, - pcibus_read, - pcibus_write, - pcibus_ihandler_attach, - pcibus_ihandler_detach, - pcibus_imask_include, - pcibus_imask_exclude, -}; +static void +pci_cfgdisable(void) +{ + switch (cfgmech) { + case 1: + outl(CONF1_ADDR_PORT, 0); + break; + case 2: + outb(CONF2_ENABLE_PORT, 0); + outb(CONF2_FORWARD_PORT, 0); + break; + } +} -/* -** Announce structure to generic driver -*/ +/* read configuration space register */ -DATA_SET (pcibus_set, i386pci); +int +pci_cfgread(pcicfgregs *cfg, int reg, int bytes) +{ + int data = -1; + int port; -/*-------------------------------------------------------------------- -** -** Determine configuration mode -** -**-------------------------------------------------------------------- -*/ + port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); + if (port != 0) { + switch (bytes) { + case 1: + data = inb(port); + break; + case 2: + data = inw(port); + break; + case 4: + data = inl(port); + break; + } + pci_cfgdisable(); + } + return (data); +} -#define CONF1_ADDR_PORT 0x0cf8 -#define CONF1_DATA_PORT 0x0cfc +/* write configuration space register */ -#define CONF1_ENABLE 0x80000000ul -#define CONF1_ENABLE_CHK 0x80000000ul -#define CONF1_ENABLE_MSK 0x7ff00000ul -#define CONF1_ENABLE_CHK1 0xff000001ul -#define CONF1_ENABLE_MSK1 0x80000001ul -#define CONF1_ENABLE_RES1 0x80000000ul +void +pci_cfgwrite(pcicfgregs *cfg, int reg, int data, int bytes) +{ + int port; -#define CONF2_ENABLE_PORT 0x0cf8 -#ifdef PC98 -#define CONF2_FORWARD_PORT 0x0cf9 -#else -#define CONF2_FORWARD_PORT 0x0cfa -#endif + port = pci_cfgenable(cfg->bus, cfg->slot, cfg->func, reg, bytes); + if (port != 0) { + switch (bytes) { + case 1: + outb(port, data); + break; + case 2: + outw(port, data); + break; + case 4: + outl(port, data); + break; + } + pci_cfgdisable(); + } +} -#define CONF2_ENABLE_CHK 0x0e -#define CONF2_ENABLE_RES 0x0e +/* check whether the configuration mechanism has been correct identified */ static int -pcibus_check (void) +pci_cfgcheck(int maxdev) { u_char device; - if (bootverbose) printf ("pcibus_check:\tdevice "); + if (bootverbose) + printf("pci_cfgcheck:\tdevice "); - for (device = 0; device < pci_maxdevice; device++) { - unsigned long id, class, header; + for (device = 0; device < maxdev; device++) { + unsigned id, class, header; if (bootverbose) - printf ("%d ", device); - id = pcibus_read (pcibus_tag (0,device,0), 0); - if ((id == 0) || (id == 0xfffffffful)) + printf("%d ", device); + + id = inl(pci_cfgenable(0, device, 0, 0, 4)); + if (id == 0 || id == -1) continue; - class = pcibus_read (pcibus_tag (0,device,0), 8); + class = inl(pci_cfgenable(0, device, 0, 8, 4)) >> 8; if (bootverbose) - printf ("[class=%x] ", class >> 8); - if ((class & 0xffffff00) == 0 || (class & 0xf8f0ff00) != 0) + printf("[class=%06x] ", class); + if (class == 0 || (class & 0xf8f0ff) != 0) continue; - header = pcibus_read (pcibus_tag (0,device,0), 12); + header = inb(pci_cfgenable(0, device, 0, 14, 1)); if (bootverbose) - printf ("[hdr=%x] ", (header >> 16) & 0xff); - if ((header & 0x007e0000) != 0) + printf("[hdr=%02x] ", header); + if ((header & 0x7e) != 0) continue; - if (bootverbose) printf ("is there (id=%08lx)\n", id); - return 1; + if (bootverbose) + printf("is there (id=%08x)\n", id); + + pci_cfgdisable(); + return (1); } if (bootverbose) - printf ("-- nothing found\n"); - return 0; + printf("-- nothing found\n"); + + pci_cfgdisable(); + return (0); } -static void -pcibus_setup (void) +int +pci_cfgopen(void) { unsigned long mode1res,oldval1; unsigned char mode2res,oldval2; - oldval1 = inl (CONF1_ADDR_PORT); + oldval1 = inl(CONF1_ADDR_PORT); if (bootverbose) { - printf ("pcibus_setup(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", oldval1); + printf("pci_open(1):\tmode 1 addr port (0x0cf8) is 0x%08lx\n", + oldval1); } - /*--------------------------------------- - ** Assume configuration mechanism 1 for now ... - **--------------------------------------- - */ - if ((oldval1 & CONF1_ENABLE_MSK) == 0) { - pci_mechanism = 1; - pci_maxdevice = 32; + cfgmech = 1; + devmax = 32; - outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK); - outb (CONF1_ADDR_PORT +3, 0); - mode1res = inl (CONF1_ADDR_PORT); - outl (CONF1_ADDR_PORT, oldval1); + outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK); + outb(CONF1_ADDR_PORT +3, 0); + mode1res = inl(CONF1_ADDR_PORT); + outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) - printf ("pcibus_setup(1a):\tmode1res=0x%08lx (0x%08lx)\n", - mode1res, CONF1_ENABLE_CHK); + printf("pci_open(1a):\tmode1res=0x%08lx (0x%08lx)\n", + mode1res, CONF1_ENABLE_CHK); if (mode1res) { - if (pcibus_check()) - return; - }; + if (pci_cfgcheck(32)) + return (cfgmech); + } - outl (CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); + outl(CONF1_ADDR_PORT, CONF1_ENABLE_CHK1); mode1res = inl(CONF1_ADDR_PORT); - outl (CONF1_ADDR_PORT, oldval1); + outl(CONF1_ADDR_PORT, oldval1); if (bootverbose) - printf ("pcibus_setup(1b):\tmode1res=0x%08lx (0x%08lx)\n", - mode1res, CONF1_ENABLE_CHK1); + printf("pci_open(1b):\tmode1res=0x%08lx (0x%08lx)\n", + mode1res, CONF1_ENABLE_CHK1); if ((mode1res & CONF1_ENABLE_MSK1) == CONF1_ENABLE_RES1) { - if (pcibus_check()) - return; - }; + if (pci_cfgcheck(32)) + return (cfgmech); + } } - /*--------------------------------------- - ** Try configuration mechanism 2 ... - **--------------------------------------- - */ - - oldval2 = inb (CONF2_ENABLE_PORT); + oldval2 = inb(CONF2_ENABLE_PORT); if (bootverbose) { - printf ("pcibus_setup(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", oldval2); + printf("pci_open(2):\tmode 2 enable port (0x0cf8) is 0x%02x\n", + oldval2); } if ((oldval2 & 0xf0) == 0) { - pci_mechanism = 2; - pci_maxdevice = 16; - - outb (CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); + cfgmech = 2; + devmax = 16; + + outb(CONF2_ENABLE_PORT, CONF2_ENABLE_CHK); mode2res = inb(CONF2_ENABLE_PORT); - outb (CONF2_ENABLE_PORT, oldval2); + outb(CONF2_ENABLE_PORT, oldval2); if (bootverbose) - printf ("pcibus_setup(2a):\tmode2res=0x%02x (0x%02x)\n", - mode2res, CONF2_ENABLE_CHK); + printf("pci_open(2a):\tmode2res=0x%02x (0x%02x)\n", + mode2res, CONF2_ENABLE_CHK); if (mode2res == CONF2_ENABLE_RES) { - if (bootverbose) - printf ("pcibus_setup(2a):\tnow trying mechanism 2\n"); - - if (pcibus_check()) - return; - } - } - - /*--------------------------------------- - ** No PCI bus host bridge found - **--------------------------------------- - */ - - pci_mechanism = 0; - pci_maxdevice = 0; -} - -/*-------------------------------------------------------------------- -** -** Build a pcitag from bus, device and function number -** -**-------------------------------------------------------------------- -*/ - -static pcici_t -pcibus_tag (unsigned char bus, unsigned char device, unsigned char func) -{ - pcici_t tag; - - tag.cfg1 = 0; - if (func >= 8) return tag; - - switch (pci_mechanism) { - - case 1: - if (device < 32) { - tag.cfg1 = CONF1_ENABLE - | (((u_long) bus ) << 16ul) - | (((u_long) device) << 11ul) - | (((u_long) func ) << 8ul); - } - break; - case 2: - if (device < 16) { - tag.cfg2.port = 0xc000 | (device << 8ul); - tag.cfg2.enable = 0xf0 | (func << 1ul); - tag.cfg2.forward = bus; - } - break; - }; - return tag; -} - -static pcici_t -pcibus_ftag (pcici_t tag, u_char func) -{ - switch (pci_mechanism) { - - case 1: - tag.cfg1 &= ~0x700ul; - tag.cfg1 |= (((u_long) func) << 8ul); - break; - case 2: - tag.cfg2.enable = 0xf0 | (func << 1ul); - break; - }; - return tag; -} - -/*-------------------------------------------------------------------- -** -** Read register from configuration space. -** -**-------------------------------------------------------------------- -*/ - -static u_long -pcibus_read (pcici_t tag, u_long reg) -{ - u_long addr, data = 0; - - if (!tag.cfg1) return (0xfffffffful); - - switch (pci_mechanism) { - - case 1: - addr = tag.cfg1 | (reg & 0xfc); -#ifdef PCI_DEBUG - printf ("pci_conf_read(1): addr=%x ", addr); -#endif - outl (CONF1_ADDR_PORT, addr); - data = inl (CONF1_DATA_PORT); - outl (CONF1_ADDR_PORT, 0 ); - break; - - case 2: - addr = tag.cfg2.port | (reg & 0xfc); -#ifdef PCI_DEBUG - printf ("pci_conf_read(2): addr=%x ", addr); -#endif - outb (CONF2_ENABLE_PORT , tag.cfg2.enable ); - outb (CONF2_FORWARD_PORT, tag.cfg2.forward); - - data = inl ((u_short) addr); - - outb (CONF2_ENABLE_PORT, 0); - outb (CONF2_FORWARD_PORT, 0); - break; - }; - -#ifdef PCI_DEBUG - printf ("data=%x\n", data); -#endif - - return (data); -} - -/*-------------------------------------------------------------------- -** -** Write register into configuration space. -** -**-------------------------------------------------------------------- -*/ - -static void -pcibus_write (pcici_t tag, u_long reg, u_long data) -{ - u_long addr; - - if (!tag.cfg1) return; - - switch (pci_mechanism) { - - case 1: - addr = tag.cfg1 | (reg & 0xfc); -#ifdef PCI_DEBUG - printf ("pci_conf_write(1): addr=%x data=%x\n", - addr, data); -#endif - outl (CONF1_ADDR_PORT, addr); - outl (CONF1_DATA_PORT, data); - outl (CONF1_ADDR_PORT, 0 ); - break; - - case 2: - addr = tag.cfg2.port | (reg & 0xfc); -#ifdef PCI_DEBUG - printf ("pci_conf_write(2): addr=%x data=%x\n", - addr, data); -#endif - outb (CONF2_ENABLE_PORT, tag.cfg2.enable); - outb (CONF2_FORWARD_PORT, tag.cfg2.forward); - - outl ((u_short) addr, data); - - outb (CONF2_ENABLE_PORT, 0); - outb (CONF2_FORWARD_PORT, 0); - break; - }; -} - -/*----------------------------------------------------------------------- -** -** Register an interrupt handler for a pci device. -** -**----------------------------------------------------------------------- -*/ - -static int -pcibus_ihandler_attach (int irq, inthand2_t *func, int arg, unsigned * maskptr) -{ - char buf[16]; - char *cp; - int free_id, id, result; + if (bootverbose) + printf("pci_open(2a):\tnow trying mechanism 2\n"); - sprintf(buf, "pci irq%d", irq); - for (cp = intrnames, free_id = 0, id = 0; id < NR_DEVICES; id++) { - if (strcmp(cp, buf) == 0) - break; - if (free_id <= 0 && strcmp(cp, "pci irqnn") == 0) - free_id = id; - while (*cp++ != '\0') - ; - } - if (id == NR_DEVICES) { - id = free_id; - if (id == 0) { - /* - * All pci irq counters are in use, perhaps because - * config is old so there aren't any. Abuse the - * clk0 counter. - */ - printf ( - "pcibus_ihandler_attach: counting pci irq%d's as clk0 irqs\n", - irq); + if (pci_cfgcheck(16)) + return (cfgmech); } } - result = register_intr( - irq, /* isa irq */ - id, /* device id */ - 0, /* flags? */ - func, /* handler */ - maskptr, /* mask pointer */ - arg); /* handler arg */ - - if (result) { - printf ("@@@ pcibus_ihandler_attach: result=%d\n", result); - return (result); - }; - update_intr_masks(); - - INTREN ((1ul<<irq)); - return (0); -} - -static int -pcibus_ihandler_detach (int irq, inthand2_t *func) -{ - int result; - - INTRDIS ((1ul<<irq)); - - result = unregister_intr (irq, func); - - if (result) - printf ("@@@ pcibus_ihandler_detach: result=%d\n", result); - - update_intr_masks(); - - return (result); -} - -static int -pcibus_imask_include (int irq, unsigned* maskptr) -{ - unsigned mask; - - if (!maskptr) return (0); - mask = 1ul << irq; - - if (*maskptr & mask) - return (-1); - - INTRMASK (*maskptr, mask); - update_intr_masks(); - - return (0); -} - -static int -pcibus_imask_exclude (int irq, unsigned* maskptr) -{ - unsigned mask; - - if (!maskptr) return (0); - - mask = 1ul << irq; - - if (! (*maskptr & mask)) - return (-1); - - INTRUNMASK (*maskptr, mask); - update_intr_masks(); - - return (0); + cfgmech = 0; + devmax = 0; + return (cfgmech); } |
