diff options
author | shawn <shawn@cvs.openbsd.org> | 1996-08-14 14:36:18 +0000 |
---|---|---|
committer | shawn <shawn@cvs.openbsd.org> | 1996-08-14 14:36:18 +0000 |
commit | 50875c762a1e05d68dbff7bad1c348f7b166492f (patch) | |
tree | 9b92cfeed7b3226c3580314960b098fc9c71de35 /sys/dev | |
parent | 87ed205c76c2e0b7a7409f1aed484310b5ba7bae (diff) |
rudimentary plug-and-play support, see pnp(4)
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/isa/files.isa | 12 | ||||
-rw-r--r-- | sys/dev/isa/isa.c | 29 | ||||
-rw-r--r-- | sys/dev/isa/isapnp.c | 1037 | ||||
-rw-r--r-- | sys/dev/isa/isapnpreg.h | 93 | ||||
-rw-r--r-- | sys/dev/isa/isapnpvar.h | 98 | ||||
-rw-r--r-- | sys/dev/isa/isavar.h | 27 |
6 files changed, 1291 insertions, 5 deletions
diff --git a/sys/dev/isa/files.isa b/sys/dev/isa/files.isa index f98467cb3f3..56b28effd36 100644 --- a/sys/dev/isa/files.isa +++ b/sys/dev/isa/files.isa @@ -1,4 +1,4 @@ -# $OpenBSD: files.isa,v 1.26 1996/07/27 07:20:06 deraadt Exp $ +# $OpenBSD: files.isa,v 1.27 1996/08/14 14:36:14 shawn Exp $ # $NetBSD: files.isa,v 1.21 1996/05/16 03:45:55 mycroft Exp $ # # Config.new file and device description for machine-independent ISA code. @@ -11,7 +11,8 @@ device isa {[port = -1], [size = 0], [iomem = -1], [iosiz = 0], - [irq = -1], [drq = -1]} + [irq = -1], [drq = -1], + [pnpid = -1]} attach isa at isabus file dev/isa/isa.c isa needs-flag @@ -23,6 +24,13 @@ device isadma attach isadma at isa: isa_dma # +# ISA PnP capability +# +device isapnp +attach isapnp at isa +file dev/isa/isapnp.c isapnp needs-flag + +# # 8250/16[45]50-based multi-port serial boards # diff --git a/sys/dev/isa/isa.c b/sys/dev/isa/isa.c index 73e4607cb77..c3c92971110 100644 --- a/sys/dev/isa/isa.c +++ b/sys/dev/isa/isa.c @@ -1,4 +1,4 @@ -/* $OpenBSD: isa.c,v 1.11 1996/07/02 22:21:15 deraadt Exp $ */ +/* $OpenBSD: isa.c,v 1.12 1996/08/14 14:36:15 shawn Exp $ */ /* $NetBSD: isa.c,v 1.85 1996/05/14 00:31:04 thorpej Exp $ */ /*- @@ -36,12 +36,15 @@ #include <sys/conf.h> #include <sys/malloc.h> #include <sys/device.h> +#include <sys/extent.h> #include <machine/intr.h> #include <dev/isa/isareg.h> #include <dev/isa/isavar.h> +#include "isapnp.h" + int isamatch __P((struct device *, void *, void *)); void isaattach __P((struct device *, struct device *, void *)); int isaprint __P((void *, char *)); @@ -77,6 +80,9 @@ isaattach(parent, self, aux) { struct isa_softc *sc = (struct isa_softc *)self; struct isabus_attach_args *iba = aux; +#if NISAPNP > 0 + void postisapnpattach(struct device *, struct device *, void *); +#endif /* NISAPNP > 0 */ isa_attach_hook(parent, self, iba); printf("\n"); @@ -93,6 +99,10 @@ isaattach(parent, self, aux) TAILQ_INIT(&sc->sc_subdevs); config_scan(isascan, self); + +#if NISAPNP > 0 + postisapnpattach(parent, self, aux); +#endif /* NISAPNP > 0 */ } int @@ -126,6 +136,12 @@ isascan(parent, match) struct device *dev = match; struct cfdata *cf = dev->dv_cfdata; struct isa_attach_args ia; + struct emap *io_map, *mem_map, *irq_map, *drq_map; + + io_map = find_emap("io"); + mem_map = find_emap("mem"); + irq_map = find_emap("irq"); + drq_map = find_emap("drq"); ia.ia_bc = sc->sc_bc; ia.ia_ic = sc->sc_ic; @@ -141,6 +157,10 @@ isascan(parent, match) struct isa_attach_args ia2 = ia; while ((*cf->cf_attach->ca_match)(parent, dev, &ia2) > 0) { + add_extent(io_map, ia.ia_iobase, ia.ia_iosize); + add_extent(mem_map, ia.ia_maddr, ia.ia_msize); + add_extent(irq_map, ia.ia_irq, 1); + add_extent(drq_map, ia.ia_drq, 1); config_attach(parent, dev, &ia2, isaprint); dev = config_make_softc(parent, cf); ia2 = ia; @@ -149,8 +169,13 @@ isascan(parent, match) return; } - if ((*cf->cf_attach->ca_match)(parent, dev, &ia) > 0) + if ((*cf->cf_attach->ca_match)(parent, dev, &ia) > 0) { + add_extent(io_map, ia.ia_iobase, ia.ia_iosize); + add_extent(mem_map, ia.ia_maddr, ia.ia_msize); + add_extent(irq_map, ia.ia_irq, 1); + add_extent(drq_map, ia.ia_drq, 1); config_attach(parent, dev, &ia, isaprint); + } else free(dev, M_DEVBUF); } diff --git a/sys/dev/isa/isapnp.c b/sys/dev/isa/isapnp.c new file mode 100644 index 00000000000..b3b727812b7 --- /dev/null +++ b/sys/dev/isa/isapnp.c @@ -0,0 +1,1037 @@ +/* $OpenBSD: isapnp.c,v 1.1 1996/08/14 14:36:15 shawn Exp $ */ + +/* + * Copyright (c) 1996, Shawn Hsiao <shawn@alpha.secc.fju.edu.tw> + * 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 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. + */ + +/* + * Note: Most of the basic code was originally written by Sujal M. Patel, + * plus some code takes from his pnpinfo(8). + */ +/* + * Copyright (c) 1996, Sujal M. Patel + * 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 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. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/queue.h> +#include <sys/extent.h> +#include <machine/bus.h> + +#include <dev/isa/isavar.h> + +#include "isapnpreg.h" +#include "isapnpvar.h" + +#define SEND(d, r) { bus_io_write_1(sc->bc, sc->addrh, 0, d); \ + bus_io_write_1(sc->bc, sc->wdh, 0, r); } + +int isapnpmatch __P((struct device *, void *, void *)); +void isapnpattach __P((struct device *, struct device *, void *)); +int isapnpprint __P((void *aux, char *pnp)); +int isapnpsubmatch __P((struct device *parent, void *match, void *aux)); + +struct isapnp_softc { + struct device sc_dev; + struct device *parent; + + bus_chipset_tag_t bc; + bus_io_handle_t addrh; + bus_io_handle_t wdh; + bus_io_handle_t rdh; + int rd_offset; + + int rd_port; + + TAILQ_HEAD(, cardinfo) q_card; +}; + +struct cfattach isapnp_ca = { + sizeof(struct isapnp_softc), isapnpmatch, isapnpattach +}; + +struct cfdriver isapnp_cd = { + NULL, "isapnp", DV_DULL, 1 +}; + +static int isapnpquery __P((struct isapnp_softc *sc, + u_int32_t dev_id, struct isa_attach_args *ia)); +static void send_Initiation_LFSR __P((struct isapnp_softc *sc)); +static int get_serial __P((struct isapnp_softc *sc, unsigned char *data)); +static int isolation_protocol __P((struct isapnp_softc *sc)); +static void read_config __P((struct isapnp_softc *sc, + struct cardinfo *card, int csn)); +static int get_resource_info __P((struct isapnp_softc *sc, + char *buffer, int len)); +static void config_device __P((struct isapnp_softc *sc, + struct isa_attach_args *data)); +static int find_free_irq __P((int irq_mask)); +static int find_free_drq __P((int drq_mask)); +static int find_free_io __P((struct isapnp_softc *sc, int desc, + int min_addr, int max_addr, int size, + int alignment, int range_check)); + +int +isapnpmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct isa_attach_args *ia = aux; + + /* sure we exist */ + ia->ia_iosize = 0; + return(1); +} + +void +isapnpattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct isa_softc *isc = (void *)parent; + struct isapnp_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + struct cardinfo *card; + int iobase; + int num_pnp_devs; + + /* + * a reference to corresponding isapnp_softc + */ + isc->pnpsc = sc; + + sc->bc = ia->ia_bc; + sc->parent = parent; + TAILQ_INIT(&sc->q_card); + + /* + * WRITE_DATA port is located at fixed offset (0x0800) + * from ADDRESS port, + * and valid READ_DATA ports are from 0x203 to 0x3ff. + */ + if (bus_io_map(sc->bc, ADDRESS, 1, &(sc->addrh)) || + bus_io_map(sc->bc, ADDRESS+0x0800, 1, &(sc->wdh)) || + bus_io_map(sc->bc, 0x0200, 0x200, &(sc->rdh))) + panic("isapnpattach: io mapping failed"); + + printf("\n%s: Checking for ISA Plug-n-Play devices... ", + sc->sc_dev.dv_xname); + + /* Try various READ_DATA ports from 0x203-0x3ff */ + for (sc->rd_port = 0x80; (sc->rd_port < 0xff); sc->rd_port += 0x10) { + num_pnp_devs = isolation_protocol(sc); + if (num_pnp_devs) { + printf("\n%s: Plug-n-Play Read_Port at 0x%x, ", + sc->sc_dev.dv_xname, + (sc->rd_port << 2) | 0x3); + printf("%d devices were found\n", num_pnp_devs); + break; + } + } + if (!num_pnp_devs) { + printf("\n%s: No Plug-n-Play devices were found\n", + sc->sc_dev.dv_xname); + return; + } +} + +void +postisapnpattach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct isa_softc *isc = (struct isa_softc *)self; + struct isapnp_softc *sc = (struct isapnp_softc *)isc->pnpsc; + struct isabus_attach_args *iba = aux; + struct cardinfo *card; + struct devinfo *dev; + + for (card = sc->q_card.tqh_first; card; + card = card->card_link.tqe_next) { + for (dev = card->q_dev.tqh_first; + dev; + dev = dev->dev_link.tqe_next) { + struct isa_attach_args ia; + + memset(&ia, 0, sizeof(ia)); + ia.ia_bc = iba->iba_bc; + ia.ia_ic = iba->iba_ic; + ia.id = dev->id; + ia.csn = card->csn; + ia.ldn = dev->ldn; + isapnpquery(sc, ia.id, &ia); + if (!config_found_sm(self, &ia, isapnpprint, + isapnpsubmatch)) { + /* + * supplied configuration fails, + * disable the device. + */ + SEND(WAKE, ia.csn); + SEND(SET_LDN, ia.ldn); + SEND(ACTIVATE, 0); + } + } + } +} + +int +isapnpprint(aux, pnp) + void *aux; + char *pnp; +{ + register struct isa_attach_args *ia = aux; + unsigned char info[4]; + struct emap *io_map, *mem_map, *irq_map, *drq_map; + + io_map = find_emap("io"); + mem_map = find_emap("mem"); + irq_map = find_emap("irq"); + drq_map = find_emap("drq"); + + bcopy(&ia->id, info, 4); + if (pnp) { + printf("device <%c%c%c%02x%02x> at %s", + ((info[0] & 0x7c) >> 2) + 64, + (((info[0] & 0x03) << 3) | + ((info[1] & 0xe0) >> 5)) + 64, + (info[1] & 0x1f) + 64, + info[2], info[3], pnp); + } + if (!pnp) { + if (ia->ia_iosize) + printf(" port 0x%x", ia->ia_iobase); + if (ia->ia_iosize > 1) { + printf("-0x%x", + ia->ia_iobase + ia->ia_iosize - 1); + add_extent(io_map, ia->ia_iobase, ia->ia_iosize); + } + if (ia->ia_msize) + printf(" iomem 0x%x", ia->ia_maddr); + if (ia->ia_msize > 1) { + printf("-0x%x", + ia->ia_maddr + ia->ia_msize - 1); + add_extent(mem_map, ia->ia_maddr, ia->ia_msize); + } + if (ia->ia_irq != IRQUNK) { + printf(" irq %d", ia->ia_irq); + add_extent(irq_map, ia->ia_irq, 1); + } + if (ia->ia_drq != DRQUNK) { + printf(" drq %d", ia->ia_drq); + add_extent(drq_map, ia->ia_drq, 1); + } + } + return(UNCONF); +} + +int +isapnpsubmatch(parent, match, aux) + struct device *parent; + void *match, *aux; +{ + struct device *dev = match; + struct cfdata *cf = dev->dv_cfdata; + struct isa_attach_args *ia = aux; + int ret; + + if (cf->cf_pnpid == ia->id) { + ret = (*cf->cf_attach->ca_match)(parent, match, aux); + return(ret); + } + + return(0); +} + +/* + * given the logical device ID, return 1 if found and configured. + */ +int +isapnpquery(sc, dev_id, ipa) + struct isapnp_softc *sc; + u_int32_t dev_id; + struct isa_attach_args *ipa; +{ + struct cardinfo *card; + struct devinfo *dev; + struct confinfo *conf; + struct isa_attach_args *tmp; + int irq, drq, iobase, mbase; + int c, i, j, fail, success; + + for (card = sc->q_card.tqh_first; card; + card = card->card_link.tqe_next) { + for (dev = card->q_dev.tqh_first; dev; + dev = dev->dev_link.tqe_next) { + if (dev_id == dev->id || + dev_id == dev->comp_id) { + tmp = malloc(sizeof(struct isa_attach_args), M_DEVBUF, M_WAITOK); + memset(tmp, 0, sizeof(struct isa_attach_args)); + SEND(WAKE, card->csn); + SEND(SET_LDN, dev->ldn); + + for (conf = dev->q_conf.tqh_first; conf; + conf = conf->conf_link.tqe_next) { + /* + * BASIC CONFIGURATION + */ + if (conf->prio == BASIC_CONFIGURATION) { + if (conf->irq[0]) { + for (c = 0; conf->irq[c] && c < 2; c++) { + i = conf->irq[c]->num; + j = find_free_irq(i); + if (j) { + ipa->irq[c].num = j; + /* + * if the interrupt can not be configured as + * low true level-triggered, + * then set it to high true edge-triggered. + * XXX needs rework + */ + if (conf->irq[c]->info & 0x08) { + ipa->irq[c].type = 0x01; + } + else { + ipa->irq[c].type = 0x10; + } + } + } + } + if (conf->dma[0]) { + for (c = 0; conf->dma[c] && c < 2; c++) { + i = conf->dma[c]->channel; + j = find_free_drq(i); + if (j) { + ipa->drq[c] = j; + } + } + } + if (conf->io[0]){ + for (c = 0; conf->io[c] && c < 8; c++) { + ipa->port[c] = find_free_io(sc, c, + conf->io[c]->min_base, + conf->io[c]->max_base, + conf->io[c]->size, + conf->io[c]->alignment, + dev->io_range_check); + } + } + /* XXX mem */ + if (conf->mem[0]) { + for (c = 0; conf->mem[c] && c < 4; c++) { + ipa->mem[c].base = conf->mem[c]->min_base; + ipa->mem[c].range = conf->mem[c]->size; + } + } + } + + /* + * DEPENDENT FUNCTION + */ + fail = 0; success = 1; + if (conf->irq[0]) { + for (c = 0; conf->irq[c] && c < 2; c++) { + i = conf->irq[c]->num; + j = find_free_irq(i); + if (j) { + tmp->irq[c].num = j; + /* + * if the interrupt can not be + * low true level-triggered, + * then set it to high true edge-triggered. + * XXX rework + */ + if (conf->irq[c]->info & 0x08) { + tmp->irq[c].type = 0x01; + } + else { + tmp->irq[c].type = 0x10; + } + } + else { + fail = 1; + success = 0; + break; + } + } + } + if (conf->dma[0]) { + for (c = 0; conf->dma[c] && c < 2; c++) { + i = conf->dma[c]->channel; + j = find_free_drq(i); + if (j) { + tmp->drq[c] = j; + } + else { + fail = 1; + success = 0; + break; + } + } + } + if (conf->io[0]) { + for (c = 0; conf->io[c] && c < 8; c++) { + tmp->port[c] = find_free_io(sc, c, + conf->io[c]->min_base, + conf->io[c]->max_base, + conf->io[c]->size, + conf->io[c]->alignment, + dev->io_range_check); + if (!tmp->port[c]) { + fail = 1; + success = 0; + break; + } + } + } + if (conf->mem[0]) { + for (c = 0; conf->mem[c] && c < 4; c++) { + tmp->mem[c].base = conf->mem[c]->min_base; + tmp->mem[c].range = conf->mem[c]->size; + + if (!tmp->mem[c].base) { + fail = 1; + success = 0; + break; + } + } + } + + if (fail) { + continue; + } + } + + if (!success) { + return(0); + } + + for (c = 0; c < 2; c++) { + if (tmp->irq[c].num) { + ipa->irq[c].num = tmp->irq[c].num; + ipa->irq[c].type = tmp->irq[c].type; + } + } + for (c = 0; c < 8; c++) { + if (tmp->port[c]) { + ipa->port[c] = tmp->port[c]; + } + } + for (c = 0; c < 4; c++) { + if (tmp->mem[c].base) { + ipa->mem[c].base = tmp->mem[c].base; + } + } + config_device(sc, ipa); + ipa->ia_iobase = ipa->port[0]; + ipa->ia_irq = ipa->irq[0].num; + ipa->ia_drq = ipa->drq[0]; + free(tmp, M_DEVBUF); + return(1); + } + } + } + return(0); +} + +/* + * Send Initiation LFSR as described in "Plug and Play ISA Specification, + * Intel May 94." + */ +static void +send_Initiation_LFSR(sc) + struct isapnp_softc *sc; +{ + bus_chipset_tag_t bc = sc->bc; + bus_io_handle_t addrh = sc->addrh; + int cur, i; + + /* Reset the LSFR */ + bus_io_write_1(bc, addrh, 0, 0); + bus_io_write_1(bc, addrh, 0, 0); + + cur = 0x6a; + bus_io_write_1(bc, addrh, 0, cur); + + for (i = 1; i < 32; i++) { + cur = (cur >> 1) | (((cur ^ (cur >> 1)) << 7) & 0xff); + bus_io_write_1(bc, addrh, 0, cur); + } +} + +/* + * Get the device's serial number. Returns 1 if the serial is valid. + */ +static int +get_serial(sc, data) + struct isapnp_softc *sc; + unsigned char *data; +{ + bus_chipset_tag_t bc = sc->bc; + bus_io_handle_t rdh = sc->rdh; + + int i, bit, valid = 0, sum = 0x6a; + + bzero(data, sizeof(char) * 9); + + sc->rd_offset = ((sc->rd_port - 0x80) << 2) | 0x3; + + for (i = 0; i < 72; i++) { + bit = bus_io_read_1(bc, rdh, sc->rd_offset) == 0x55; + delay(250); /* Delay 250 usec */ + + /* Can't Short Circuit the next evaluation, so 'and' is last */ + bit = (bus_io_read_1(bc, rdh, sc->rd_offset) == 0xaa) && bit; + delay(250); /* Delay 250 usec */ + + valid = valid || bit; + + if (i < 64) + sum = (sum >> 1) | + (((sum ^ (sum >> 1) ^ bit) << 7) & 0xff); + + data[i / 8] = (data[i / 8] >> 1) | (bit ? 0x80 : 0); + } + + valid = valid && (data[8] == sum); + + return valid; +} + +static int +get_resource_info(sc, buffer, len) + struct isapnp_softc *sc; + char *buffer; + int len; +{ + int i, j; + + for (i = 0; i < len; i++) { + bus_io_write_1(sc->bc, sc->addrh, 0, STATUS); + for (j = 0; j < 100; j++) { + if ((bus_io_read_1(sc->bc, sc->rdh, sc->rd_offset)) + & 0x1) + break; + delay(1); + } + if (j == 100) { + printf("%s: failed to report resource data\n", + sc->sc_dev.dv_xname); + return 0; + } + bus_io_write_1(sc->bc, sc->addrh, 0, RESOURCE_DATA); + buffer[i] = bus_io_read_1(sc->bc, sc->rdh, sc->rd_offset); + } + return 1; +} + +/* + * Small Resource Tag Handler + * + * Returns 1 if checksum was valid (and an END_TAG was received). + * Returns -1 if checksum was invalid (and an END_TAG was received). + * Returns 0 for other tags. + * + * XXX checksum is ignored now ... + */ +static int +handle_small_res(resinfo, item, len, card) + unsigned char *resinfo; + int item, len; + struct cardinfo *card; +{ + int i; + + switch (item) { + case PNP_VERSION: + memcpy(card->pnp_version, resinfo, 2); + break; + case LOG_DEVICE_ID: + card->dev = malloc(sizeof(struct devinfo), M_DEVBUF, M_WAITOK); + bzero(card->dev, sizeof(struct devinfo)); + TAILQ_INSERT_TAIL(&card->q_dev, card->dev, dev_link); + card->dev->id = *(u_int32_t *)resinfo; + card->dev->ldn = card->num_ld; + card->dev->io_range_check = resinfo[4] & 0x2 ? 1 : 0; + TAILQ_INIT(&card->dev->q_conf); + + /* + * if the resource data is not enclosed in a START_DEPEND_FUNC and + * a END_DEPEND_FUNC, it's the basic configuration. + * + * we simple treat is as a special case. + */ + card->dev->basic = malloc(sizeof(struct confinfo), M_DEVBUF, M_WAITOK); + TAILQ_INSERT_TAIL(&card->dev->q_conf, card->dev->basic, conf_link); + card->dev->basic->prio = BASIC_CONFIGURATION; + bzero(card->dev->basic->irq, 2*sizeof(void *)); + bzero(card->dev->basic->dma, 2*sizeof(void *)); + bzero(card->dev->basic->io, 8*sizeof(void *)); + bzero(card->dev->basic->mem, 4*sizeof(void *)); + card->dev->conf = card->dev->basic; + + card->num_ld++; + break; + case COMP_DEVICE_ID: + card->dev->comp_id = *(u_int32_t *)resinfo; + break; + case IRQ_FORMAT: + for (i = 0; card->dev->conf->irq[i]; i++) ; + card->dev->conf->irq[i] = malloc(sizeof(struct irq_format), + M_DEVBUF, M_WAITOK); + card->dev->conf->irq[i]->num = resinfo[0] | resinfo[1] << 8; + if (len == 3) { + card->dev->conf->irq[i]->info = resinfo[2]; + } + else { + card->dev->conf->irq[i]->info = 0; + } + break; + case DMA_FORMAT: + for (i = 0; card->dev->conf->dma[i]; i++) ; + card->dev->conf->dma[i] = malloc(sizeof(struct dma_format), + M_DEVBUF, M_WAITOK); + card->dev->conf->dma[i]->channel = resinfo[0]; + card->dev->conf->dma[i]->info = resinfo[1]; + break; + case START_DEPEND_FUNC: + card->dev->conf = malloc(sizeof(struct confinfo), + M_DEVBUF, M_WAITOK); + TAILQ_INSERT_TAIL(&card->dev->q_conf, card->dev->conf, + conf_link); + card->dev->conf->prio = ACCEPTABLE_CONFIGURATION; + bzero(card->dev->conf->irq, 2*sizeof(void *)); + bzero(card->dev->conf->dma, 2*sizeof(void *)); + bzero(card->dev->conf->io, 8*sizeof(void *)); + bzero(card->dev->conf->mem, 4*sizeof(void *)); + + if (len == 1) { + switch (resinfo[0]) { + case GOOD_CONFIGURATION: + card->dev->conf->prio = + GOOD_CONFIGURATION; + break; + case ACCEPTABLE_CONFIGURATION: + card->dev->conf->prio = + ACCEPTABLE_CONFIGURATION; + break; + case SUBOPTIMAL_CONFIGURATION: + card->dev->conf->prio = + SUBOPTIMAL_CONFIGURATION; + break; + default: + card->dev->conf->prio = + RESERVED_CONFIGURATION; + break; + } + } + break; + case END_DEPEND_FUNC: + break; + case IO_PORT_DESC: + for (i = 0; card->dev->conf->io[i]; i++) ; + card->dev->conf->io[i] = malloc(sizeof(struct io_descriptor), + M_DEVBUF, M_WAITOK); + card->dev->conf->io[i]->type = 0; /* 0 for normal I/O desc. */ + card->dev->conf->io[i]->info = resinfo[0]; + card->dev->conf->io[i]->min_base = + resinfo[1] | resinfo[2] << 8; + card->dev->conf->io[i]->max_base = + resinfo[3] | resinfo[4] << 8; + card->dev->conf->io[i]->alignment = resinfo[5]; + card->dev->conf->io[i]->size = resinfo[6]; + break; + case FIXED_IO_PORT_DESC: + for (i = 0; card->dev->conf->io[i]; i++) ; + card->dev->conf->io[i] = malloc(sizeof(struct io_descriptor), + M_DEVBUF, M_WAITOK); + card->dev->conf->io[i]->type = 1; /* 1 for fixed I/O desc. */ + card->dev->conf->io[i]->info = 0; + card->dev->conf->io[i]->min_base = + resinfo[0] | (resinfo[1] & 0x3) << 8; + card->dev->conf->io[i]->max_base = + card->dev->conf->io[i]->min_base; + card->dev->conf->io[i]->alignment = 0; + card->dev->conf->io[i]->size = resinfo[2]; + break; + case END_TAG: + /* + * XXX checksum is ignored + */ + return(1); + } + + return(0); +} + +static void +handle_large_res(resinfo, item, len, card) + unsigned char *resinfo; + int item, len; + struct cardinfo *card; +{ + int i; + + switch (item) { + case MEMORY_RANGE_DESC: + for (i = 0; card->dev->conf->mem[i]; i++) ; + card->dev->conf->mem[i] = malloc(sizeof(struct mem_descriptor), + M_DEVBUF, M_WAITOK); + card->dev->conf->mem[i]->type = 0; /* 0 for 24bit mem desc. */ + card->dev->conf->mem[i]->info = resinfo[0]; + card->dev->conf->mem[i]->min_base = + (resinfo[1] | resinfo[2] << 8) << 8; + card->dev->conf->mem[i]->max_base = + (resinfo[3] | resinfo[4] << 8) << 8; + card->dev->conf->mem[i]->alignment = + (resinfo[5] | resinfo[6] << 8); + if (!card->dev->conf->mem[i]->alignment) + card->dev->conf->mem[i]->alignment = 1 << 16; + card->dev->conf->mem[i]->size = + (resinfo[7] | resinfo[8] << 8) << 8; + break; + case ID_STRING_ANSI: + if (card->dev) { + card->dev->id_string = + (char *)malloc(len+1, M_DEVBUF, M_WAITOK); + strncpy(card->dev->id_string, resinfo, len+1); + card->dev->id_string[len] = '\0'; + } else { + card->id_string = + (char *)malloc(len+1, M_DEVBUF, M_WAITOK); + strncpy(card->id_string, resinfo, len+1); + card->id_string[len] = '\0'; + } + break; + case ID_STRING_UNICODE: + break; + case LG_VENDOR_DEFINED: + break; + case _32BIT_MEM_RANGE_DESC: + break; + case _32BIT_FIXED_LOC_DESC: + break; + case LG_RES_RESERVED: + break; + } +} + +static void +read_config(sc, card, csn) + struct isapnp_softc *sc; + struct cardinfo *card; + int csn; +{ + u_char serial[9], tag, *resinfo; + int i, large_len; + + /* + * set card with csn to Config state + */ + SEND(SET_CSN, csn); + + /* + * since we are in the card isolation process, so ther's no reason + * to rewind and skip the first 9 bytes + */ + + /* + * allow up to 1KB of resource info, should be plenty */ + for (i = 0; i < 1024; i++) { + if (!get_resource_info(sc, &tag, 1)) + return; + +#define TYPE (tag >> 7) +#define S_ITEM (tag >> 3) +#define S_LEN (tag & 0x7) +#define L_ITEM (tag & 0x7f) + + if (TYPE == 0) { + resinfo = malloc(S_LEN, M_TEMP, M_WAITOK); + if (!get_resource_info(sc, resinfo, S_LEN)) + return; + + if (handle_small_res(resinfo, S_ITEM, S_LEN, card) == 1) + return; + free(resinfo, M_TEMP); + } else { + large_len = 0; + if (!get_resource_info(sc, (char *)&large_len, 2)) + return; + + resinfo = malloc(large_len, M_TEMP, M_WAITOK); + if (!get_resource_info(sc, resinfo, large_len)) + return; + + handle_large_res(resinfo, L_ITEM, large_len, card); + free(resinfo, M_TEMP); + } + } +} + +/* + * Run the isolaion protocol. Use rd_port as the READ_DATA port value (caller + * should try multiple READ_DATA locations before giving up). Upon exiting, + * all cards are aware that they should use rd_port as the READ_DATA port; + */ +static int +isolation_protocol(sc) + struct isapnp_softc *sc; +{ + int csn; + unsigned char data[9]; + + /* Reset CSN for All Cards */ + SEND(CONFIG_CONTROL, 0x05); + + send_Initiation_LFSR(sc); + + for (csn = 1; (csn < MAX_CARDS); csn++) { + /* Wake up cards without a CSN */ + SEND(WAKE, 0); + SEND(SET_RD_DATA, sc->rd_port); + bus_io_write_1(sc->bc, sc->addrh, 0, SERIAL_ISOLATION); + delay(1000); /* Delay 1 msec */ + + if (get_serial(sc, data)) { + struct cardinfo *card; + + SEND(SET_CSN, csn); + card = malloc(sizeof(struct cardinfo), + M_DEVBUF, M_WAITOK); + bzero(card, sizeof(struct cardinfo)); + TAILQ_INSERT_TAIL(&sc->q_card, card, card_link); + memcpy(card->serial, data, 9); + card->csn = csn; + TAILQ_INIT(&card->q_dev); + /* + * read card's resource data + */ + read_config(sc, card, csn); + } + else + break; + } + return csn - 1; +} + +/* + * Configure PnP devices, given a set of configuration data + */ +static void +config_device(sc, data) + struct isapnp_softc *sc; + struct isa_attach_args *data; +{ + int i; + + if (data->csn <= 0) { + return; + } + +#if 0 + printf ("%s: Configuring CSN %x (Logical Device %x)\n", + sc->sc_dev.dv_xname, + data->csn, + data->ldn != -1 ? data->ldn : 0); +#endif + + SEND(WAKE, data->csn); + if (data->ldn > 0) + SEND (SET_LDN, data->ldn); + + for (i = 0; i < 8; i++) + if (data->port[i] > 0) { + SEND (IO_CONFIG_BASE + i * 2, + data->port[i] >> 8); + SEND (IO_CONFIG_BASE + i * 2 + 1, + data->port[i] & 0xff); + } + + for (i = 0; i < 2; i++) + if (data->irq[i].num > 0) { + SEND (IRQ_CONFIG + i * 2, data->irq[i].num); + if (data->irq[i].type >= 0) + SEND (IRQ_CONFIG + i * 2 + 1, data->irq[i].type); + } + + for (i = 0; i < 2; i++) + if (data->drq[i] > 0) { + SEND (DRQ_CONFIG + i, data->drq[i]); + } + + for (i = 0; i < 4; i++) + if (data->mem[i].base > 0) { + SEND (MEM_CONFIG + i * 8, + data->mem[i].base >> 16); + SEND (MEM_CONFIG + i * 8 + 1, + (data->mem[i].base >> 8) & + 0xff); + /* + * This needs to be handled better for + * the user's sake. XXX + */ + if (data->mem[i].control >= 0) { + SEND (MEM_CONFIG + i * 8 + 2, + data->mem[i].control); + } + SEND (MEM_CONFIG + i * 8 + 3, + data->mem[i].range >> 16); + SEND (MEM_CONFIG + i * 8 + 4, + (data->mem[i].range >> 8) & + 0xff); + } + SEND (IO_RANGE_CHECK, 0); + SEND (ACTIVATE, 1); +} + +static int +find_free_irq(irq_mask) + int irq_mask; +{ + int i, j; + struct emap *irq_map; + + irq_map = find_emap("irq"); + i = irq_mask; + + while (1) { + j = ffs(i); + if (!j) { + return(0); + } + /* + * irq_mask is starting from 0 + */ + j --; + if (!probe_extent(irq_map, j, 1)) { + return(j); + } + i &= ~(0x1 << j); + } +} + +int +find_free_drq(drq_mask) + int drq_mask; +{ + int i, j; + struct emap *drq_map; + + drq_map = find_emap("drq"); + i = drq_mask; + + while (1) { + j = ffs(i); + if (!j) { + return(0); + } + /* + * drq_mask is starting from 0 + */ + j --; + if (!probe_extent(drq_map, j, 1)) { + return(j); + } + i &= ~(0x1 << j); + } +} + +/* + * find free I/O space. + * if device is capable of doing I/O range check, then use it. + * else, try to find free region from extent map. + * + * assume caller has set csn and ldn properly. + */ +static int +find_free_io(sc, desc, min_addr, max_addr, size, alignment, range_check) + struct isapnp_softc *sc; + int desc, min_addr, max_addr, size, alignment, range_check; +{ + int addr, i, success = 0; + bus_io_handle_t data; + struct emap *io_map; + + if (range_check) { + for (addr = min_addr; addr <= max_addr; addr += alignment) { + SEND(ACTIVATE, 0); + SEND(IO_CONFIG_BASE + desc * 2, addr >> 8); + SEND(IO_CONFIG_BASE + desc * 2 + 1, addr & 0xff); + SEND(IO_RANGE_CHECK, 0x2); + bus_io_map(sc->bc, addr, size, &data); + i = 0; + for (i = 0; i < size; i++) { + if (bus_io_read_1(sc->bc, data, i) != 0xAA) { + bus_io_unmap(sc->bc, data, size); + break; + } + } + if (i == size) { + success = 1; + bus_io_unmap(sc->bc, data, size); + break; + } + } + + if (success) { + return(addr); + } + else { + return(0); + } + } + else { + io_map = find_emap("io"); + addr = min_addr; + if (!probe_extent(io_map, addr, size)) { + return(addr); + } + return(0); + } +} diff --git a/sys/dev/isa/isapnpreg.h b/sys/dev/isa/isapnpreg.h new file mode 100644 index 00000000000..b93f2beab66 --- /dev/null +++ b/sys/dev/isa/isapnpreg.h @@ -0,0 +1,93 @@ +/* $OpenBSD: isapnpreg.h,v 1.1 1996/08/14 14:36:16 shawn Exp $ */ + +/* + * Copyright (c) 1996, Sujal M. Patel + * 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. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Sujal M. Patel + * 4. Neither the name of the author nor the names of any co-contributors + * may 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. + */ + +#ifndef _DEV_ISA_ISAPNPREG_H_ +#define _DEV_ISA_ISAPNPREG_H_ + +/* Maximum Number of PnP Devices. 8 should be plenty */ +#define MAX_CARDS 8 + + +/* Static ports */ +#define ADDRESS 0x279 +#define WRITE_DATA 0xa79 + + +/* PnP Registers. Write to ADDRESS and then use WRITE/READ_DATA */ +#define SET_RD_DATA 0x00 +#define SERIAL_ISOLATION 0x01 +#define CONFIG_CONTROL 0x02 +#define WAKE 0x03 +#define RESOURCE_DATA 0x04 +#define STATUS 0x05 +#define SET_CSN 0x06 +#define SET_LDN 0x07 +#define ACTIVATE 0x30 +#define IO_RANGE_CHECK 0x31 +#define MEM_CONFIG 0x40 +#define IO_CONFIG_BASE 0x60 +#define IRQ_CONFIG 0x70 +#define DRQ_CONFIG 0x74 + +/* Small Resource Item names */ +#define PNP_VERSION 0x1 +#define LOG_DEVICE_ID 0x2 +#define COMP_DEVICE_ID 0x3 +#define IRQ_FORMAT 0x4 +#define DMA_FORMAT 0x5 +#define START_DEPEND_FUNC 0x6 +#define END_DEPEND_FUNC 0x7 +#define IO_PORT_DESC 0x8 +#define FIXED_IO_PORT_DESC 0x9 +#define SM_RES_RESERVED 0xa-0xd +#define SM_VENDOR_DEFINED 0xe +#define END_TAG 0xf + +/* Large Resource Item names */ +#define MEMORY_RANGE_DESC 0x1 +#define ID_STRING_ANSI 0x2 +#define ID_STRING_UNICODE 0x3 +#define LG_VENDOR_DEFINED 0x4 +#define _32BIT_MEM_RANGE_DESC 0x5 +#define _32BIT_FIXED_LOC_DESC 0x6 +#define LG_RES_RESERVED 0x7-0x7f + +/* Priority for Resource Group */ +#define BASIC_CONFIGURATION -1 +#define GOOD_CONFIGURATION 0x0 +#define ACCEPTABLE_CONFIGURATION 0x1 +#define SUBOPTIMAL_CONFIGURATION 0x2 +#define RESERVED_CONFIGURATION 0x3 + +#endif /* !_DEV_ISA_ISAPNPREG_H_ */ diff --git a/sys/dev/isa/isapnpvar.h b/sys/dev/isa/isapnpvar.h new file mode 100644 index 00000000000..49f5da5f5d0 --- /dev/null +++ b/sys/dev/isa/isapnpvar.h @@ -0,0 +1,98 @@ +/* $OpenBSD: isapnpvar.h,v 1.1 1996/08/14 14:36:16 shawn Exp $ */ + +/* + * Copyright (c) 1996, Shawn Hsiao <shawn@alpha.secc.fju.edu.tw> + * 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 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. + */ + +#ifndef _DEV_ISA_ISAPNPVAR_H_ +#define _DEV_ISA_ISAPNPVAR_H_ + +struct irq_format { + int num; /* IRQ number */ + int info; /* IRQ type */ +}; + +struct dma_format { + int channel; /* DMA channel */ + int info; /* DMA type */ +}; + +struct io_descriptor { + int type; /* normal or fixed I/O descriptor */ + int info; /* decoding information */ + int min_base; /* minimum base I/O address */ + int max_base; /* maximum base I/O address */ + int alignment; /* alignment for base address */ + int size; /* number of contiguous ports */ +}; + +struct mem_descriptor { + int type; /* 24 or 32bit memory descriptor */ + int info; /* misc. memory information */ + int min_base; /* minimum base mem address */ + int max_base; /* maximum base mem address */ + int alignment; /* aligment for base address */ + int size; /* number of memory range length */ +}; + +struct confinfo { + int prio; + struct irq_format *irq[2]; + struct dma_format *dma[2]; + struct io_descriptor *io[8]; + struct mem_descriptor *mem[4]; + + TAILQ_ENTRY(confinfo) conf_link; +}; + +struct devinfo { + u_int32_t id; + u_int32_t comp_id; + char *id_string; + + int ldn; + int io_range_check; + + TAILQ_HEAD(, confinfo) q_conf; + struct confinfo *conf; + struct confinfo *basic; + + TAILQ_ENTRY(devinfo) dev_link; +}; + +struct cardinfo { + char serial[9]; + char pnp_version[2]; + int csn; + char *id_string; + + int num_ld; + + TAILQ_HEAD(, devinfo) q_dev; + struct devinfo *dev; + TAILQ_ENTRY(cardinfo) card_link; +}; + +#endif /* !_DEV_ISA_ISAPNPVAR_H_ */ diff --git a/sys/dev/isa/isavar.h b/sys/dev/isa/isavar.h index f0459ea98d2..849c40ead19 100644 --- a/sys/dev/isa/isavar.h +++ b/sys/dev/isa/isavar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: isavar.h,v 1.13 1996/06/22 23:18:43 pefo Exp $ */ +/* $OpenBSD: isavar.h,v 1.14 1996/08/14 14:36:17 shawn Exp $ */ /* $NetBSD: isavar.h,v 1.23 1996/05/08 23:32:31 thorpej Exp $ */ /* @@ -96,6 +96,24 @@ struct isa_attach_args { void *ia_aux; /* driver specific */ bus_io_handle_t ia_delayioh; /* i/o handle for `delay port' */ + + /* XXX need fixes, some are duplicated */ + /* begin isapnp section */ + int id; /* logical device ID */ + int csn; /* card selection number */ + int ldn; /* logical device number */ + struct { + int num; + int type; + } irq[2]; + int drq[2]; + int port[8]; + struct { + int base; + int control; + int range; + } mem[4]; + /* end isapnp stuff */ }; #define IOBASEUNK -1 /* i/o address is unknown */ @@ -130,6 +148,12 @@ struct isa_softc { * via isa_attach_args. */ bus_io_handle_t sc_delayioh; + + /* + * This points to the isapnp_softc structure that holds + * information of PnP devices on the ISA bus. + */ + void *pnpsc; }; #define cf_iobase cf_loc[0] @@ -138,6 +162,7 @@ struct isa_softc { #define cf_msize cf_loc[3] #define cf_irq cf_loc[4] #define cf_drq cf_loc[5] +#define cf_pnpid cf_loc[6] /* * ISA interrupt handler manipulation. |