File: [local] / sys / arch / i386 / pci / pcibios.c (download)
Revision 1.1.1.1 (vendor branch), Tue Mar 4 16:06:05 2008 UTC (16 years, 4 months ago) by nbrk
Branch: OPENBSD_4_2_BASE, MAIN
CVS Tags: jornada-partial-support-wip, HEAD Changes since 1.1: +0 -0 lines
Import of OpenBSD 4.2 release kernel tree with initial code to support
Jornada 720/728, StrongARM 1110-based handheld PC.
At this point kernel roots on NFS and boots into vfs_mountroot() and traps.
What is supported:
- glass console, Jornada framebuffer (jfb) works in 16bpp direct color mode
(needs some palette tweaks for non black/white/blue colors, i think)
- saic, SA11x0 interrupt controller (needs cleanup)
- sacom, SA11x0 UART (supported only as boot console for now)
- SA11x0 GPIO controller fully supported (but can't handle multiple interrupt
handlers on one gpio pin)
- sassp, SSP port on SA11x0 that attaches spibus
- Jornada microcontroller (jmcu) to control kbd, battery, etc throught
the SPI bus (wskbd attaches on jmcu, but not tested)
- tod functions seem work
- initial code for SA-1111 (chip companion) : this is TODO
Next important steps, i think:
- gpio and intc on sa1111
- pcmcia support for sa11x0 (and sa1111 help logic)
- REAL root on nfs when we have PCMCIA support (we may use any of supported pccard NICs)
- root on wd0! (using already supported PCMCIA-ATA)
|
/* $OpenBSD: pcibios.c,v 1.37 2007/03/19 05:32:05 deraadt Exp $ */
/* $NetBSD: pcibios.c,v 1.5 2000/08/01 05:23:59 uch Exp $ */
/*
* Copyright (c) 2000 Michael Shalayeff
* 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.
*
* 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 OR HIS RELATIVES 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 MIND, 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.
*/
/*-
* Copyright (c) 1999 The NetBSD Foundation, Inc.
* All rights reserved.
*
* This code is derived from software contributed to The NetBSD Foundation
* by Jason R. Thorpe of the Numerical Aerospace Simulation Facility,
* NASA Ames Research Center.
*
* 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. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the NetBSD
* Foundation, Inc. and its contributors.
* 4. Neither the name of The NetBSD Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
* ``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 FOUNDATION OR CONTRIBUTORS
* 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.
*/
/*
* Copyright (c) 1999, by UCHIYAMA Yasushi
* 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. The name of the developer 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
*/
/*
* Interface to the PCI BIOS and PCI Interrupt Routing table.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <sys/malloc.h>
#include <dev/isa/isareg.h>
#include <i386/isa/isa_machdep.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <dev/pci/pcidevs.h>
#include <i386/pci/pcibiosvar.h>
#include <machine/biosvar.h>
int pcibios_flags;
int pcibios_present;
struct pcibios_pir_header pcibios_pir_header;
struct pcibios_intr_routing *pcibios_pir_table;
int pcibios_pir_table_nentries;
int pcibios_flags = 0;
struct bios32_entry pcibios_entry;
struct bios32_entry_info pcibios_entry_info;
struct pcibios_intr_routing *pcibios_pir_init(struct pcibios_softc *);
int pcibios_get_status(struct pcibios_softc *,
u_int32_t *, u_int32_t *, u_int32_t *,
u_int32_t *, u_int32_t *, u_int32_t *, u_int32_t *);
int pcibios_get_intr_routing(struct pcibios_softc *,
struct pcibios_intr_routing *, int *, u_int16_t *);
int pcibios_return_code(struct pcibios_softc *, u_int16_t, const char *);
void pcibios_print_exclirq(struct pcibios_softc *);
void pcibios_print_pir_table(void);
#define PCI_IRQ_TABLE_START 0xf0000
#define PCI_IRQ_TABLE_END 0xfffff
struct cfdriver pcibios_cd = {
NULL, "pcibios", DV_DULL
};
int pcibiosprobe(struct device *, void *, void *);
void pcibiosattach(struct device *, struct device *, void *);
struct cfattach pcibios_ca = {
sizeof(struct pcibios_softc), pcibiosprobe, pcibiosattach
};
int
pcibiosprobe(struct device *parent, void *match, void *aux)
{
struct bios_attach_args *ba = aux;
u_int32_t rev_maj, rev_min, mech1, mech2, scmech1, scmech2, maxbus;
int rv;
if (strcmp(ba->bios_dev, "pcibios"))
return 0;
rv = bios32_service(PCIBIOS_SIGNATURE, &pcibios_entry,
&pcibios_entry_info);
PCIBIOS_PRINTV(("pcibiosprobe: 0x%lx:0x%lx at 0x%lx[0x%lx]\n",
pcibios_entry.segment, pcibios_entry.offset,
pcibios_entry_info.bei_base, pcibios_entry_info.bei_size));
return rv &&
pcibios_get_status(NULL, &rev_maj, &rev_min, &mech1, &mech2,
&scmech1, &scmech2, &maxbus) == PCIBIOS_SUCCESS;
}
void
pcibiosattach(struct device *parent, struct device *self, void *aux)
{
struct pcibios_softc *sc = (struct pcibios_softc *)self;
u_int32_t rev_maj, rev_min, mech1, mech2, scmech1, scmech2;
pcibios_flags = sc->sc_dev.dv_cfdata->cf_flags;
pcibios_get_status((struct pcibios_softc *)self, &rev_maj,
&rev_min, &mech1, &mech2,
&scmech1, &scmech2, &sc->max_bus);
printf(": rev %d.%d @ 0x%lx/0x%lx\n",
rev_maj, rev_min >> 4, pcibios_entry_info.bei_base,
pcibios_entry_info.bei_size);
PCIBIOS_PRINTV(("%s: config mechanism %s%s, special cycles %s%s, "
"last bus %d\n", sc->sc_dev.dv_xname,
mech1 ? "[1]" : "[x]", mech2 ? "[2]" : "[x]",
scmech1 ? "[1]" : "[x]", scmech2 ? "[2]" : "[x]", sc->max_bus));
/*
* The PCI BIOS tells us the config mechanism; fill it in now
* so that pci_mode_detect() doesn't have to look for it.
*/
pci_mode = mech1 ? 1 : 2;
pcibios_present = 1;
/*
* Find the PCI IRQ Routing table.
*/
if (!(pcibios_flags & PCIBIOS_INTR_FIXUP) &&
pcibios_pir_init((struct pcibios_softc *)self) != NULL) {
int rv;
/*
* Fixup interrupt routing.
*/
rv = pci_intr_fixup(sc, NULL, I386_BUS_SPACE_IO);
switch (rv) {
case -1:
/* Non-fatal error. */
printf("%s: Warning, unable to fix up PCI interrupt "
"routing\n", sc->sc_dev.dv_xname);
break;
case 1:
/* Fatal error. */
printf("%s: interrupt fixup failed\n", sc->sc_dev.dv_xname);
return;
}
/*
* XXX Clear `pciirq' from the ISA interrupt allocation
* XXX mask.
*/
}
if (!(pcibios_flags & PCIBIOS_BUS_FIXUP)) {
sc->max_bus = pci_bus_fixup(NULL, 0);
printf("%s: PCI bus #%d is the last bus\n",
sc->sc_dev.dv_xname, sc->max_bus);
}
if (!(pcibios_flags & PCIBIOS_ADDR_FIXUP))
pci_addr_fixup(sc, NULL, sc->max_bus);
}
struct pcibios_intr_routing *
pcibios_pir_init(struct pcibios_softc *sc)
{
paddr_t pa;
pcibios_pir_table = NULL;
for (pa = PCI_IRQ_TABLE_START; pa < PCI_IRQ_TABLE_END; pa += 16) {
u_int8_t *p, cksum;
struct pcibios_pir_header *pirh;
int i;
p = ISA_HOLE_VADDR(pa);
pirh = (struct pcibios_pir_header *)p;
/*
* Some laptops (such as the Toshiba Libretto L series)
* use _PIR instead of the standard $PIR for the signature
* so we check for that too.
*/
if (pirh->signature != BIOS32_MAKESIG('$', 'P', 'I', 'R') &&
pirh->signature != BIOS32_MAKESIG('_', 'P', 'I', 'R'))
continue;
if (pirh->tablesize < sizeof(*pirh))
continue;
cksum = 0;
for (i = 0; i < pirh->tablesize; i++)
cksum += p[i];
printf("%s: PCI IRQ Routing Table rev %d.%d @ 0x%lx/%d "
"(%d entries)\n", sc->sc_dev.dv_xname,
pirh->version >> 8, pirh->version & 0xff, pa,
pirh->tablesize, (pirh->tablesize - sizeof(*pirh)) / 16);
if (cksum != 0) {
printf("%s: bad IRQ table checksum\n",
sc->sc_dev.dv_xname);
continue;
}
if (pirh->tablesize % 16 != 0) {
printf("%s: bad IRQ table size\n", sc->sc_dev.dv_xname);
continue;
}
if (pirh->version != 0x0100) {
printf("%s: unsupported IRQ table version\n",
sc->sc_dev.dv_xname);
continue;
}
/*
* We can handle this table! Make a copy of it.
*/
pcibios_pir_header = *pirh;
pcibios_pir_table =
malloc(pirh->tablesize - sizeof(*pirh), M_DEVBUF, M_NOWAIT);
if (pcibios_pir_table == NULL) {
printf("%s: no memory for $PIR\n", sc->sc_dev.dv_xname);
return NULL;
}
bcopy(p + sizeof(*pirh), pcibios_pir_table,
pirh->tablesize - sizeof(*pirh));
pcibios_pir_table_nentries =
(pirh->tablesize - sizeof(*pirh)) / 16;
}
/*
* If there was no PIR table found, try using the PCI BIOS
* Get Interrupt Routing call.
*
* XXX The interface to this call sucks; just allocate enough
* XXX room for 32 entries.
*/
if (pcibios_pir_table == NULL) {
pcibios_pir_table_nentries = 32;
pcibios_pir_table = malloc(pcibios_pir_table_nentries *
sizeof(*pcibios_pir_table), M_DEVBUF, M_NOWAIT);
if (pcibios_pir_table == NULL) {
printf("%s: no memory for $PIR\n", sc->sc_dev.dv_xname);
return NULL;
}
if (pcibios_get_intr_routing(sc, pcibios_pir_table,
&pcibios_pir_table_nentries,
&pcibios_pir_header.exclusive_irq) != PCIBIOS_SUCCESS) {
printf("%s: PCI IRQ Routing information unavailable.\n",
sc->sc_dev.dv_xname);
free(pcibios_pir_table, M_DEVBUF);
pcibios_pir_table = NULL;
pcibios_pir_table_nentries = 0;
return NULL;
}
printf("%s: PCI BIOS has %d Interrupt Routing table entries\n",
sc->sc_dev.dv_xname, pcibios_pir_table_nentries);
}
pcibios_print_exclirq(sc);
if (pcibios_flags & PCIBIOS_INTRDEBUG)
pcibios_print_pir_table();
return pcibios_pir_table;
}
int
pcibios_get_status(struct pcibios_softc *sc, u_int32_t *rev_maj,
u_int32_t *rev_min, u_int32_t *mech1, u_int32_t *mech2, u_int32_t *scmech1,
u_int32_t *scmech2, u_int32_t *maxbus)
{
u_int32_t ax, bx, cx, edx;
int rv;
__asm __volatile("pushl %%es\n\t"
"pushl %%ds\n\t"
"movw 4(%%edi), %%cx\n\t"
"movl %%ecx, %%ds\n\t"
"lcall %%cs:*(%%edi)\n\t"
"pop %%ds\n\t"
"pop %%es\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:"
: "=a" (ax), "=b" (bx), "=c" (cx), "=d" (edx)
: "0" (0xb101), "D" (&pcibios_entry)
: "cc", "memory");
rv = pcibios_return_code(sc, ax, "pcibios_get_status");
if (rv != PCIBIOS_SUCCESS)
return (rv);
if (edx != BIOS32_MAKESIG('P', 'C', 'I', ' '))
return (PCIBIOS_SERVICE_NOT_PRESENT); /* XXX */
/*
* Fill in the various pieces of info we're looking for.
*/
*mech1 = ax & 1;
*mech2 = ax & (1 << 1);
*scmech1 = ax & (1 << 4);
*scmech2 = ax & (1 << 5);
*rev_maj = (bx >> 8) & 0xff;
*rev_min = bx & 0xff;
*maxbus = cx & 0xff;
return (PCIBIOS_SUCCESS);
}
int
pcibios_get_intr_routing(struct pcibios_softc *sc,
struct pcibios_intr_routing *table, int *nentries, u_int16_t *exclirq)
{
u_int32_t ax, bx;
int rv;
struct {
u_int16_t size;
u_int32_t offset;
u_int16_t segment;
} __attribute__((__packed__)) args;
args.size = *nentries * sizeof(*table);
args.offset = (u_int32_t)table;
args.segment = GSEL(GDATA_SEL, SEL_KPL);
memset(table, 0, args.size);
__asm __volatile("pushl %%es\n\t"
"pushl %%ds\n\t"
"movw 4(%%esi), %%cx\n\t"
"movl %%ecx, %%ds\n\t"
"lcall %%cs:*(%%esi)\n\t"
"popl %%ds\n\t"
"popl %%es\n\t"
"jc 1f\n\t"
"xor %%ah, %%ah\n"
"1:\n"
: "=a" (ax), "=b" (bx)
: "0" (0xb10e), "1" (0), "D" (&args), "S" (&pcibios_entry)
: "%ecx", "%edx", "cc", "memory");
rv = pcibios_return_code(sc, ax, "pcibios_get_intr_routing");
if (rv != PCIBIOS_SUCCESS)
return (rv);
*nentries = args.size / sizeof(*table);
*exclirq |= bx;
return (PCIBIOS_SUCCESS);
}
int
pcibios_return_code(struct pcibios_softc *sc, u_int16_t ax, const char *func)
{
const char *errstr;
int rv = ax >> 8;
char *nam;
if (sc)
nam = sc->sc_dev.dv_xname;
else
nam = "pcibios0";
switch (rv) {
case PCIBIOS_SUCCESS:
return (PCIBIOS_SUCCESS);
case PCIBIOS_SERVICE_NOT_PRESENT:
errstr = "service not present";
break;
case PCIBIOS_FUNCTION_NOT_SUPPORTED:
errstr = "function not supported";
break;
case PCIBIOS_BAD_VENDOR_ID:
errstr = "bad vendor ID";
break;
case PCIBIOS_DEVICE_NOT_FOUND:
errstr = "device not found";
break;
case PCIBIOS_BAD_REGISTER_NUMBER:
errstr = "bad register number";
break;
case PCIBIOS_SET_FAILED:
errstr = "set failed";
break;
case PCIBIOS_BUFFER_TOO_SMALL:
errstr = "buffer too small";
break;
default:
printf("%s: %s - unknown return code 0x%x\n",
nam, func, rv);
return (rv);
}
printf("%s: %s - %s\n", nam, func, errstr);
return (rv);
}
void
pcibios_print_exclirq(struct pcibios_softc *sc)
{
int i;
if (pcibios_pir_header.exclusive_irq) {
printf("%s: PCI Exclusive IRQs:", sc->sc_dev.dv_xname);
for (i = 0; i < 16; i++) {
if (pcibios_pir_header.exclusive_irq & (1 << i))
printf(" %d", i);
}
printf("\n");
}
}
void
pcibios_print_pir_table(void)
{
int i, j;
for (i = 0; i < pcibios_pir_table_nentries; i++) {
printf("PIR Entry %d:\n", i);
printf("\tBus: %d Device: %d\n",
pcibios_pir_table[i].bus,
PIR_DEVFUNC_DEVICE(pcibios_pir_table[i].device));
for (j = 0; j < 4; j++) {
printf("\t\tINT%c: link 0x%02x bitmap 0x%04x\n",
'A' + j,
pcibios_pir_table[i].linkmap[j].link,
pcibios_pir_table[i].linkmap[j].bitmap);
}
}
}
void
pci_device_foreach(struct pcibios_softc *sc, pci_chipset_tag_t pc, int maxbus,
void (*func)(struct pcibios_softc *, pci_chipset_tag_t, pcitag_t))
{
const struct pci_quirkdata *qd;
int bus, device, function, maxdevs, nfuncs;
pcireg_t id, bhlcr;
pcitag_t tag;
for (bus = 0; bus <= maxbus; bus++) {
maxdevs = pci_bus_maxdevs(pc, bus);
for (device = 0; device < maxdevs; device++) {
tag = pci_make_tag(pc, bus, device, 0);
id = pci_conf_read(pc, tag, PCI_ID_REG);
/* Invalid vendor ID value? */
if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
continue;
/* XXX Not invalid, but we've done this ~forever. */
if (PCI_VENDOR(id) == 0)
continue;
qd = pci_lookup_quirkdata(PCI_VENDOR(id),
PCI_PRODUCT(id));
bhlcr = pci_conf_read(pc, tag, PCI_BHLC_REG);
if (PCI_HDRTYPE_MULTIFN(bhlcr) ||
(qd != NULL &&
(qd->quirks & PCI_QUIRK_MULTIFUNCTION) != 0))
nfuncs = 8;
else
nfuncs = 1;
for (function = 0; function < nfuncs; function++) {
tag = pci_make_tag(pc, bus, device, function);
id = pci_conf_read(pc, tag, PCI_ID_REG);
/* Invalid vendor ID value? */
if (PCI_VENDOR(id) == PCI_VENDOR_INVALID)
continue;
/*
* XXX Not invalid, but we've done this
* ~forever.
*/
if (PCI_VENDOR(id) == 0)
continue;
(*func)(sc, pc, tag);
}
}
}
}