summaryrefslogtreecommitdiffstats
path: root/sys/i386/isa/pcibus.c
diff options
context:
space:
mode:
authorse <se@FreeBSD.org>1995-02-01 23:06:58 +0000
committerse <se@FreeBSD.org>1995-02-01 23:06:58 +0000
commitdeb6352a2b8144b30e0405dd7a8fbd0df5e1b5d0 (patch)
treea85ec60c733945c9624a9e88dc2b4796ad1ca932 /sys/i386/isa/pcibus.c
parentf3d5354cdddcda3a4ee802329d43d06480be08b0 (diff)
downloadFreeBSD-src-deb6352a2b8144b30e0405dd7a8fbd0df5e1b5d0.zip
FreeBSD-src-deb6352a2b8144b30e0405dd7a8fbd0df5e1b5d0.tar.gz
Reviewed by: se
Submitted by: wolf (Wolfgang Stanglmeier) New ISA dependend file for PCI bus support. Replaces sys/i386/pci/pcibios.c.
Diffstat (limited to 'sys/i386/isa/pcibus.c')
-rw-r--r--sys/i386/isa/pcibus.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/sys/i386/isa/pcibus.c b/sys/i386/isa/pcibus.c
new file mode 100644
index 0000000..5b7f8dd
--- /dev/null
+++ b/sys/i386/isa/pcibus.c
@@ -0,0 +1,442 @@
+/**************************************************************************
+**
+** $Id: pcibus.c,v 1.5 1994/10/12 02:33:21 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.
+**
+***************************************************************************
+*/
+
+#ifndef __FreeBSD2__
+#if __FreeBSD__ >= 2
+#define __FreeBSD2__
+#endif
+#endif
+
+#ifdef __FreeBSD2__
+#define HAS_CPUFUNC_H
+#endif
+
+#include <types.h>
+#include <param.h>
+#include <kernel.h>
+#include <i386/isa/isa.h>
+#include <i386/isa/isa_device.h>
+#include <i386/isa/icu.h>
+
+#ifdef HAS_CPUFUNC_H
+#include <i386/include/cpufunc.h>
+#endif
+
+#include <pci/pcivar.h>
+#include <pci/pcireg.h>
+#include <pci/pcibus.h>
+
+extern int printf();
+
+static char pci_mode;
+
+/*-----------------------------------------------------------------
+**
+** The following functions are provided by the pci bios.
+** They are used only by the pci configuration.
+**
+** pcibus_mode():
+** Probes for a pci system.
+** Returns 1 or 2 for pci configuration mechanism.
+** Returns 0 if no pci system.
+**
+** pcibus_tag():
+** Gets a handle for accessing the pci configuration
+** space.
+** This handle is given to the mapping functions (see
+** above) or to the read/write functions.
+**
+** 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 alligned 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 alligned one), and a value.
+**
+** pcibus_regirq():
+** Register an interupt handler for a pci device.
+** Requires a tag (from pcitag), the register number
+** (should be a long word alligned one), and a value.
+**
+**-----------------------------------------------------------------
+*/
+
+static int
+pcibus_mode (void);
+
+static pcici_t
+pcibus_tag (u_char bus, u_char device, u_char func);
+
+static u_long
+pcibus_read (pcici_t tag, u_long reg);
+
+static void
+pcibus_write (pcici_t tag, u_long reg, u_long data);
+
+static int
+pcibus_regint (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr);
+
+struct pcibus i386pci = {
+ "pci",
+ pcibus_mode,
+ pcibus_tag,
+ pcibus_read,
+ pcibus_write,
+ pcibus_regint,
+};
+
+/*
+** Announce structure to generic driver
+*/
+
+DATA_SET (pcibus_set, i386pci);
+
+/*--------------------------------------------------------------------
+**
+** Port access
+**
+**--------------------------------------------------------------------
+*/
+
+#ifndef HAS_CPUFUNC_H
+
+#undef inl
+#define inl(port) \
+({ u_long data; \
+ __asm __volatile("inl %1, %0": "=a" (data): "d" ((u_short)(port))); \
+ data; })
+
+
+#undef outl
+#define outl(port, data) \
+{__asm __volatile("outl %0, %1"::"a" ((u_long)(data)), "d" ((u_short)(port)));}
+
+
+#undef inb
+#define inb(port) \
+({ u_char data; \
+ __asm __volatile("inb %1, %0": "=a" (data): "d" ((u_short)(port))); \
+ data; })
+
+
+#undef outb
+#define outb(port, data) \
+{__asm __volatile("outb %0, %1"::"a" ((u_char)(data)), "d" ((u_short)(port)));}
+
+#endif /* HAS_CPUFUNC_H */
+
+/*--------------------------------------------------------------------
+**
+** Determine configuration mode
+**
+**--------------------------------------------------------------------
+*/
+
+
+#define CONF1_ENABLE 0x80000000ul
+#define CONF1_ADDR_PORT 0x0cf8
+#define CONF1_DATA_PORT 0x0cfc
+
+
+#define CONF2_ENABLE_PORT 0x0cf8
+#define CONF2_FORWARD_PORT 0x0cfa
+
+
+static int
+pcibus_mode (void)
+{
+#ifdef PCI_CONF_MODE
+ return (PCI_CONF_MODE)
+#else /* PCI_CONF_MODE */
+ u_long result, oldval;
+
+ /*---------------------------------------
+ ** Configuration mode 2 ?
+ **---------------------------------------
+ */
+
+ outb (CONF2_ENABLE_PORT, 0);
+ outb (CONF2_FORWARD_PORT, 0);
+ if (!inb (CONF2_ENABLE_PORT) && !inb (CONF2_FORWARD_PORT)) {
+ pci_mode = 2;
+ return (2);
+ };
+
+ /*---------------------------------------
+ ** Configuration mode 1 ?
+ **---------------------------------------
+ */
+
+ oldval = inl (CONF1_ADDR_PORT);
+ outl (CONF1_ADDR_PORT, CONF1_ENABLE);
+ result = inl (CONF1_ADDR_PORT);
+ outl (CONF1_ADDR_PORT, oldval);
+
+ if (result == CONF1_ENABLE) {
+ pci_mode = 1;
+ return (1);
+ };
+
+ /*---------------------------------------
+ ** No PCI bus available.
+ **---------------------------------------
+ */
+ return (0);
+#endif /* PCI_CONF_MODE */
+}
+
+/*--------------------------------------------------------------------
+**
+** 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 (device >= 32) return tag;
+ if (func >= 8) return tag;
+
+ switch (pci_mode) {
+
+ case 1:
+ tag.cfg1 = CONF1_ENABLE
+ | (((u_long) bus ) << 16ul)
+ | (((u_long) device) << 11ul)
+ | (((u_long) func ) << 8ul);
+ break;
+ case 2:
+ if (device >= 16) break;
+ tag.cfg2.port = 0xc000 | (device << 8ul);
+ tag.cfg2.enable = 0xf1 | (func << 1ul);
+ tag.cfg2.forward = bus;
+ 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_mode) {
+
+ 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_mode) {
+
+ 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 interupt handler for a pci device.
+**
+**-----------------------------------------------------------------------
+*/
+
+#ifndef __FreeBSD2__
+/*
+ * Type of the first (asm) part of an interrupt handler.
+ */
+typedef void inthand_t __P((u_int cs, u_int ef, u_int esp, u_int ss));
+
+/*
+ * Usual type of the second (C) part of an interrupt handler. Some bogus
+ * ones need the arg to be the interrupt frame (and not a copy of it, which
+ * is all that is possible in C).
+ */
+typedef void inthand2_t __P((int unit));
+
+/*
+** XXX @FreeBSD2@
+**
+** Unfortunately, the mptr argument is _no_ pointer in 2.0 FreeBSD.
+** We would prefer a pointer because it enables us to install
+** new interrupt handlers at any time.
+** (This is just going to be changed ... <se> :)
+** In 2.0 FreeBSD later installed interrupt handlers may change
+** the xyz_imask, but this would not be recognized by handlers
+** which are installed before.
+*/
+
+static int
+register_intr __P((int intr, int device_id, unsigned int flags,
+ inthand2_t *handler, unsigned int * mptr, int unit));
+extern unsigned intr_mask[ICU_LEN];
+
+#endif /* !__FreeBSD2__ */
+static unsigned int pci_int_mask [16];
+
+int pcibus_regint (pcici_t tag, int(*func)(), void* arg, unsigned* maskptr)
+{
+ int irq;
+ unsigned mask;
+
+ irq = PCI_INTERRUPT_LINE_EXTRACT(
+ pci_conf_read (tag, PCI_INTERRUPT_REG));
+
+ mask = 1ul << irq;
+
+ if (!maskptr)
+ maskptr = &pci_int_mask[irq];
+
+ INTRMASK (*maskptr, mask);
+
+ register_intr(
+ irq, /* isa irq */
+ 0, /* deviced?? */
+ 0, /* flags? */
+ (inthand2_t*) func, /* handler */
+ maskptr, /* mask pointer */
+ (int) arg); /* handler arg */
+
+#ifdef __FreeBSD2__
+ /*
+ ** XXX See comment at beginning of file.
+ **
+ ** Have to update all the interrupt masks ... Grrrrr!!!
+ */
+ {
+ unsigned * mp = &intr_mask[0];
+ /*
+ ** update the isa interrupt masks.
+ */
+ for (mp=&intr_mask[0]; mp<&intr_mask[ICU_LEN]; mp++)
+ if (*mp & *maskptr)
+ *mp |= mask;
+ /*
+ ** update the pci interrupt masks.
+ */
+ for (mp=&pci_int_mask[0]; mp<&pci_int_mask[16]; mp++)
+ if (*mp & *maskptr)
+ *mp |= mask;
+ };
+#endif
+
+ INTREN (mask);
+
+ return (1);
+}
OpenPOWER on IntegriCloud