diff options
Diffstat (limited to 'sys/pci/pci_compat.c')
| -rw-r--r-- | sys/pci/pci_compat.c | 351 |
1 files changed, 351 insertions, 0 deletions
diff --git a/sys/pci/pci_compat.c b/sys/pci/pci_compat.c new file mode 100644 index 000000000000..c14d8641f2a7 --- /dev/null +++ b/sys/pci/pci_compat.c @@ -0,0 +1,351 @@ +/* + * 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 "pci.h" +#if NPCI > 0 + +/* for compatibility to FreeBSD-2.2 version of PCI code */ + +#include <stddef.h> + +#include <sys/types.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/malloc.h> +#include <sys/fcntl.h> +#include <sys/kernel.h> /* for DATA_SET support */ + +#include <vm/vm.h> +#include <vm/pmap.h> +#include <i386/isa/isa_device.h> +#include <sys/interrupt.h> + +#include <pci/pcireg.h> +#include <pci/pcivar.h> + +#ifdef RESOURCE_CHECK +#include <sys/drvresource.h> +#endif + +#ifdef PCI_COMPAT + +/* ------------------------------------------------------------------------- */ + +static int +pci_mapno(pcicfgregs *cfg, int reg) +{ + int map = -1; + + if ((reg & 0x03) == 0) { + map = (reg -0x10) / 4; + if (map < 0 || map >= cfg->nummaps) + map = -1; + } + return (map); +} + +static int +pci_isportmap(pcicfgregs *cfg, int map) +{ + return ((unsigned)map < cfg->nummaps + && (cfg->map[map].type & PCI_MAPPORT) != 0); +} + +static int +pci_ismemmap(pcicfgregs *cfg, int map) +{ + return ((unsigned)map < cfg->nummaps + && (cfg->map[map].type & PCI_MAPMEM) != 0); +} + +/* ------------------------------------------------------------------------- */ + +u_long +pci_conf_read(pcici_t tag, u_long reg) +{ + return (pci_cfgread(tag, reg, 4)); +} + +void +pci_conf_write(pcici_t tag, u_long reg, u_long data) +{ + pci_cfgwrite(tag, reg, data, 4); +} + +int pci_map_port(pcici_t cfg, u_long reg, u_short* pa) +{ + int map; + + map = pci_mapno(cfg, reg); + if (pci_isportmap(cfg, map)) { + u_int32_t iobase; + u_int32_t iosize; + + iobase = cfg->map[map].base; + iosize = 1 << cfg->map[map].ln2size; +#ifdef RESOURCE_CHECK + if (resource_claim(cfg, REST_PORT, RESF_NONE, + iobase, iobase + iosize -1) == 0) +#endif /* RESOURCE_CHECK */ + { + *pa = iobase; + return (1); + } + } + return (0); +} + +int pci_map_mem(pcici_t cfg, u_long reg, vm_offset_t* va, vm_offset_t* pa) +{ + int map; + + map = pci_mapno(cfg, reg); + if (pci_ismemmap(cfg, map)) { + u_int32_t paddr; + u_int32_t psize; + + paddr = cfg->map[map].base; + psize = 1 << cfg->map[map].ln2size; +#ifdef RESOURCE_CHECK + if (resource_claim(cfg, REST_MEM, RESF_NONE, + paddr, paddr + psize -1) == 0) +#endif /* RESOURCE_CHECK */ + { + u_int32_t poffs; + vm_offset_t vaddr; + + poffs = paddr - trunc_page(paddr); + vaddr = (vm_offset_t)pmap_mapdev(paddr-poffs, psize+poffs); + if (vaddr != NULL) { + vaddr += poffs; + *va = vaddr; + *pa = paddr; + return (1); + } + } + } + return (0); +} + +int +pci_map_int(pcici_t cfg, pci_inthand_t *func, void *arg, unsigned *maskptr) +{ + if (cfg->intpin != 0) { + int irq = cfg->intline; + void *dev_instance = (void *)-1; /* XXX use cfg->devdata */ + intrec *idesc; + + idesc = intr_create(dev_instance, irq, func, arg, maskptr, 0); + return (intr_connect(idesc) == 0); + } + return (1); +} + +int +pci_unmap_int(pcici_t cfg) +{ + return (0); /* not supported, yet, since cfg doesn't know about idesc */ +} + +/* ------------------------------------------------------------------------- */ + +/* + * Preliminary support for "wired" PCI devices. + * This code supports currently only devices on PCI bus 0, since the + * mapping from PCI BIOS bus numbers to configuration file bus numbers + * is not yet maintained, whenever a PCI to PCI bridge is found. + * The "bus" field of "pciwirecfg" correlates an PCI bus with the bridge + * it is attached to. The "biosbus" field is to be updated for each bus, + * whose bridge is probed. An entry with bus != 0 and biosbus == 0 is + * invalid and will be skipped in the search for a wired unit, but not + * in the test for a free unit number. + */ + +typedef struct { + char *name; + int unit; + u_int8_t bus; + u_int8_t slot; + u_int8_t func; + u_int8_t biosbus; +} pciwirecfg; + +static pciwirecfg pci_wireddevs[] = { + /* driver, unit, bus, slot, func, BIOS bus */ + { "ncr", 2, 1, 4, 0, 0 }, + { "ed", 2, 1, 5, 0, 0 }, + /* do not delete the end marker that follows this comment !!! */ + { NULL } +}; + +/* return unit number of wired device, or -1 if no match */ + +static int +pci_wiredunit(pcicfgregs *cfg, char *name) +{ + pciwirecfg *p; + + p = pci_wireddevs; + while (p->name != NULL) { + if (p->bus == cfg->bus + && p->slot == cfg->slot + && p->func == cfg->func + && strcmp(p->name, name) == 0) + return (p->unit); + p++; + } + return (-1); +} + +/* return free unit number equal or greater to the one supplied as parameter */ + +static int +pci_freeunit(pcicfgregs *cfg, char *name, int unit) +{ + pciwirecfg *p; + + p = pci_wireddevs; + while (p->name != NULL) { + if (p->unit == unit && strcmp(p->name, name) == 0) { + p = pci_wireddevs; + unit++; + } else { + p++; + } + } + return (unit); +} + +static char *drvname; + +static char* +pci_probedrv(pcicfgregs *cfg, struct pci_device *dvp) +{ + if (dvp && dvp->pd_probe) { + pcidi_t type = (cfg->device << 16) + cfg->vendor; + return (dvp->pd_probe(cfg, type)); + } + return (NULL); +} + +static struct pci_lkm *pci_lkm_head; + +static struct pci_device* +pci_finddrv(pcicfgregs *cfg) +{ + struct pci_device **dvpp; + struct pci_device *dvp = NULL; + struct pci_lkm *lkm; + + drvname = NULL; + lkm = pci_lkm_head; + while (drvname == NULL && lkm != NULL) { + dvp = lkm->dvp; + drvname = pci_probedrv(cfg, dvp); + lkm = lkm->next; + } + + dvpp = (struct pci_device **)pcidevice_set.ls_items; + while (drvname == NULL && (dvp = *dvpp++) != NULL) + drvname = pci_probedrv(cfg, dvp); + return (dvp); +} + +static void +pci_drvmessage(pcicfgregs *cfg, char *name, int unit) +{ + if (drvname == NULL || *drvname == '\0') + return; + printf("%s%d: <%s> rev 0x%02x", name, unit, drvname, cfg->revid); + if (cfg->intpin != 0) + printf(" int %c irq %d", cfg->intpin + 'a' -1, cfg->intline); + printf(" on pci%d.%d.%d\n", cfg->bus, cfg->slot, cfg->func); +} + + +void +pci_drvattach(pcicfgregs *cfg) +{ + struct pci_device *dvp; + + dvp = pci_finddrv(cfg); + if (dvp != NULL) { + int unit; + + unit = pci_wiredunit(cfg, dvp->pd_name); + if (unit < 0) { + unit = pci_freeunit(cfg, dvp->pd_name, *dvp->pd_count); + *dvp->pd_count = unit +1; + } + pci_drvmessage(cfg, dvp->pd_name, unit); + if (dvp->pd_attach) + dvp->pd_attach(cfg, unit); + } +} + +/* ------------------------------------------------------------------------- */ + +static void +pci_rescan(void) +{ + /* XXX do nothing, currently, soon to come ... */ +} + +int pci_register_lkm (struct pci_device *dvp, int if_revision) +{ + struct pci_lkm *lkm; + + if (if_revision != 0) { + return (-1); + } + if (dvp == NULL || dvp->pd_probe == NULL || dvp->pd_attach == NULL) { + return (-1); + } + lkm = malloc (sizeof (*lkm), M_DEVBUF, M_WAITOK); + if (lkm != NULL) { + return (-1); + } + + lkm->dvp = dvp; + lkm->next = pci_lkm_head; + pci_lkm_head = lkm; + pci_rescan(); + return (0); +} + +void +pci_configure(void) +{ + pci_probe(NULL); +} + +/* ------------------------------------------------------------------------- */ + +#endif /* PCI_COMPAT */ +#endif /* NPCI > 0 */ |
