diff --git a/sys/conf/files b/sys/conf/files index 67be183dee9..c1c90928c7c 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -174,6 +174,9 @@ dev/mlx/mlx.c optional mlx dev/pccard/pccard.c optional pccard dev/pccard/pccard_cis.c optional pccard dev/pccard/pccard_cis_quirks.c optional pccard +dev/pcic/i82365.c optional pcic pccard +dev/pcic/i82365_isa.c optional pcic pccard +dev/pcic/i82365_isasubr.c optional pcic pccard dev/pdq/pdq.c optional fea dev/pdq/pdq_ifsubr.c optional fea dev/pdq/pdq.c optional fpa @@ -305,7 +308,6 @@ i4b/layer4/i4b_i4bdrv.c optional i4b i4b/layer4/i4b_l4.c optional i4b i4b/layer4/i4b_l4mgmt.c optional i4b i4b/layer4/i4b_l4timer.c optional i4b -isa/pcicx.c optional pcicx pccard isofs/cd9660/cd9660_bmap.c optional cd9660 isofs/cd9660/cd9660_lookup.c optional cd9660 isofs/cd9660/cd9660_node.c optional cd9660 diff --git a/sys/dev/pcic/i82365.c b/sys/dev/pcic/i82365.c new file mode 100644 index 00000000000..71d6a02609b --- /dev/null +++ b/sys/dev/pcic/i82365.c @@ -0,0 +1,1358 @@ +/* $NetBSD: i82365.c,v 1.23 1999/02/19 03:14:00 mycroft Exp $ */ +/* $FreeBSD$ */ + +#define PCICDEBUG + +/* + * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. + * 4. 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include +#include + +#ifdef __FreeBSD__ +#define delay(x) DELAY(x) +#endif + +#ifdef PCICDEBUG +int pcic_debug = 0; +#define DPRINTF(arg) if (pcic_debug) printf arg; +#else +#define DPRINTF(arg) +#endif + +#define PCIC_VENDOR_UNKNOWN 0 +#define PCIC_VENDOR_I82365SLR0 1 +#define PCIC_VENDOR_I82365SLR1 2 +#define PCIC_VENDOR_CIRRUS_PD6710 3 +#define PCIC_VENDOR_CIRRUS_PD672X 4 + +/* + * Individual drivers will allocate their own memory and io regions. Memory + * regions must be a multiple of 4k, aligned on a 4k boundary. + */ + +#define PCIC_MEM_ALIGN PCIC_MEM_PAGESIZE + +void pcic_attach_socket __P((struct pcic_handle *)); +void pcic_init_socket __P((struct pcic_handle *)); + +int pcic_submatch __P((struct device *, struct cfdata *, void *)); +int pcic_print __P((void *arg, const char *pnp)); +int pcic_intr_socket __P((struct pcic_handle *)); + +void pcic_attach_card __P((struct pcic_handle *)); +void pcic_detach_card __P((struct pcic_handle *, int)); +void pcic_deactivate_card __P((struct pcic_handle *)); + +void pcic_chip_do_mem_map __P((struct pcic_handle *, int)); +void pcic_chip_do_io_map __P((struct pcic_handle *, int)); + +void pcic_create_event_thread __P((void *)); +void pcic_event_thread __P((void *)); + +void pcic_queue_event __P((struct pcic_handle *, int)); + +static void pcic_wait_ready __P((struct pcic_handle *)); + +int +pcic_ident_ok(ident) + int ident; +{ + /* this is very empirical and heuristic */ + + if ((ident == 0) || (ident == 0xff) || (ident & PCIC_IDENT_ZERO)) + return (0); + + if ((ident & PCIC_IDENT_IFTYPE_MASK) != PCIC_IDENT_IFTYPE_MEM_AND_IO) { +#ifdef DIAGNOSTIC + printf("pcic: does not support memory and I/O cards, " + "ignored (ident=%0x)\n", ident); +#endif + return (0); + } + return (1); +} + +int +pcic_vendor(h) + struct pcic_handle *h; +{ + int reg; + + /* + * the chip_id of the cirrus toggles between 11 and 00 after a write. + * weird. + */ + + pcic_write(h, PCIC_CIRRUS_CHIP_INFO, 0); + reg = pcic_read(h, -1); + + if ((reg & PCIC_CIRRUS_CHIP_INFO_CHIP_ID) == + PCIC_CIRRUS_CHIP_INFO_CHIP_ID) { + reg = pcic_read(h, -1); + if ((reg & PCIC_CIRRUS_CHIP_INFO_CHIP_ID) == 0) { + if (reg & PCIC_CIRRUS_CHIP_INFO_SLOTS) + return (PCIC_VENDOR_CIRRUS_PD672X); + else + return (PCIC_VENDOR_CIRRUS_PD6710); + } + } + + reg = pcic_read(h, PCIC_IDENT); + + if ((reg & PCIC_IDENT_REV_MASK) == PCIC_IDENT_REV_I82365SLR0) + return (PCIC_VENDOR_I82365SLR0); + else + return (PCIC_VENDOR_I82365SLR1); + + return (PCIC_VENDOR_UNKNOWN); +} + +char * +pcic_vendor_to_string(vendor) + int vendor; +{ + switch (vendor) { + case PCIC_VENDOR_I82365SLR0: + return ("Intel 82365SL Revision 0"); + case PCIC_VENDOR_I82365SLR1: + return ("Intel 82365SL Revision 1"); + case PCIC_VENDOR_CIRRUS_PD6710: + return ("Cirrus PD6710"); + case PCIC_VENDOR_CIRRUS_PD672X: + return ("Cirrus PD672X"); + } + + return ("Unknown controller"); +} + +void +pcic_attach(sc) + struct pcic_softc *sc; +{ + int vendor, count, i, reg; + + /* now check for each controller/socket */ + + /* + * this could be done with a loop, but it would violate the + * abstraction + */ + + count = 0; + + DPRINTF(("pcic ident regs:")); + + sc->handle[0].sc = sc; + sc->handle[0].sock = C0SA; + if (pcic_ident_ok(reg = pcic_read(&sc->handle[0], PCIC_IDENT))) { + sc->handle[0].flags = PCIC_FLAG_SOCKETP; + count++; + } else { + sc->handle[0].flags = 0; + } + sc->handle[0].laststate = PCIC_LASTSTATE_EMPTY; + + DPRINTF((" 0x%02x", reg)); + + sc->handle[1].sc = sc; + sc->handle[1].sock = C0SB; + if (pcic_ident_ok(reg = pcic_read(&sc->handle[1], PCIC_IDENT))) { + sc->handle[1].flags = PCIC_FLAG_SOCKETP; + count++; + } else { + sc->handle[1].flags = 0; + } + sc->handle[1].laststate = PCIC_LASTSTATE_EMPTY; + + DPRINTF((" 0x%02x", reg)); + + /* + * The CL-PD6729 has only one controller and always returns 0 + * if you try to read from the second one. Maybe pcic_ident_ok + * shouldn't accept 0? + */ + sc->handle[2].sc = sc; + sc->handle[2].sock = C1SA; + if (pcic_vendor(&sc->handle[0]) != PCIC_VENDOR_CIRRUS_PD672X || + pcic_read(&sc->handle[2], PCIC_IDENT) != 0) { + if (pcic_ident_ok(reg = pcic_read(&sc->handle[2], + PCIC_IDENT))) { + sc->handle[2].flags = PCIC_FLAG_SOCKETP; + count++; + } else { + sc->handle[2].flags = 0; + } + sc->handle[2].laststate = PCIC_LASTSTATE_EMPTY; + + DPRINTF((" 0x%02x", reg)); + + sc->handle[3].sc = sc; + sc->handle[3].sock = C1SB; + if (pcic_ident_ok(reg = pcic_read(&sc->handle[3], + PCIC_IDENT))) { + sc->handle[3].flags = PCIC_FLAG_SOCKETP; + count++; + } else { + sc->handle[3].flags = 0; + } + sc->handle[3].laststate = PCIC_LASTSTATE_EMPTY; + + DPRINTF((" 0x%02x\n", reg)); + } else { + sc->handle[2].flags = 0; + sc->handle[3].flags = 0; + } + + if (count == 0) + panic("pcic_attach: attach found no sockets"); + + /* establish the interrupt */ + + /* XXX block interrupts? */ + + for (i = 0; i < PCIC_NSLOTS; i++) { + /* + * this should work, but w/o it, setting tty flags hangs at + * boot time. + */ + if (sc->handle[i].flags & PCIC_FLAG_SOCKETP) + { + SIMPLEQ_INIT(&sc->handle[i].events); + pcic_write(&sc->handle[i], PCIC_CSC_INTR, 0); + pcic_read(&sc->handle[i], PCIC_CSC); + } + } + + if ((sc->handle[0].flags & PCIC_FLAG_SOCKETP) || + (sc->handle[1].flags & PCIC_FLAG_SOCKETP)) { + vendor = pcic_vendor(&sc->handle[0]); + + printf("%s: controller 0 (%s) has ", sc->dev.dv_xname, + pcic_vendor_to_string(vendor)); + + if ((sc->handle[0].flags & PCIC_FLAG_SOCKETP) && + (sc->handle[1].flags & PCIC_FLAG_SOCKETP)) + printf("sockets A and B\n"); + else if (sc->handle[0].flags & PCIC_FLAG_SOCKETP) + printf("socket A only\n"); + else + printf("socket B only\n"); + + if (sc->handle[0].flags & PCIC_FLAG_SOCKETP) + sc->handle[0].vendor = vendor; + if (sc->handle[1].flags & PCIC_FLAG_SOCKETP) + sc->handle[1].vendor = vendor; + } + if ((sc->handle[2].flags & PCIC_FLAG_SOCKETP) || + (sc->handle[3].flags & PCIC_FLAG_SOCKETP)) { + vendor = pcic_vendor(&sc->handle[2]); + + printf("%s: controller 1 (%s) has ", sc->dev.dv_xname, + pcic_vendor_to_string(vendor)); + + if ((sc->handle[2].flags & PCIC_FLAG_SOCKETP) && + (sc->handle[3].flags & PCIC_FLAG_SOCKETP)) + printf("sockets A and B\n"); + else if (sc->handle[2].flags & PCIC_FLAG_SOCKETP) + printf("socket A only\n"); + else + printf("socket B only\n"); + + if (sc->handle[2].flags & PCIC_FLAG_SOCKETP) + sc->handle[2].vendor = vendor; + if (sc->handle[3].flags & PCIC_FLAG_SOCKETP) + sc->handle[3].vendor = vendor; + } +} + +void +pcic_attach_sockets(sc) + struct pcic_softc *sc; +{ + int i; + + for (i = 0; i < PCIC_NSLOTS; i++) + if (sc->handle[i].flags & PCIC_FLAG_SOCKETP) + pcic_attach_socket(&sc->handle[i]); +} + +void +pcic_attach_socket(h) + struct pcic_handle *h; +{ + struct pccardbus_attach_args paa; + + /* initialize the rest of the handle */ + + h->shutdown = 0; + h->memalloc = 0; + h->ioalloc = 0; + h->ih_irq = 0; + + /* now, config one pccard device per socket */ + paa.paa_busname = "pccard"; + paa.pct = (pccard_chipset_tag_t) h->sc->pct; + paa.pch = (pccard_chipset_handle_t) h; + paa.iobase = h->sc->iobase; + paa.iosize = h->sc->iosize; + + h->pccard = config_found_sm(&h->sc->dev, &paa, pcic_print, + pcic_submatch); + + /* if there's actually a pccard device attached, initialize the slot */ + + if (h->pccard) + pcic_init_socket(h); +} + +void +pcic_create_event_thread(arg) + void *arg; +{ + struct pcic_handle *h = arg; + const char *cs; + + switch (h->sock) { + case C0SA: + cs = "0,0"; + break; + case C0SB: + cs = "0,1"; + break; + case C1SA: + cs = "1,0"; + break; + case C1SB: + cs = "1,1"; + break; + default: + panic("pcic_create_event_thread: unknown pcic socket"); + } + + if (kthread_create1(pcic_event_thread, h, &h->event_thread, + "%s,%s", h->sc->dev.dv_xname, cs)) { + printf("%s: unable to create event thread for sock 0x%02x\n", + h->sc->dev.dv_xname, h->sock); + panic("pcic_create_event_thread"); + } else + printf("%s: create event thread for sock 0x%02x\n", + h->sc->dev.dv_xname, h->sock); + +} + +void +pcic_event_thread(arg) + void *arg; +{ + struct pcic_handle *h = arg; + struct pcic_event *pe; + int s; + + while (h->shutdown == 0) { + s = splhigh(); + if ((pe = SIMPLEQ_FIRST(&h->events)) == NULL) { + splx(s); + (void) tsleep(&h->events, PWAIT, "pcicev", 0); + continue; + } else { + splx(s); + /* sleep .25s to be enqueued chatterling interrupts */ + (void) tsleep((caddr_t)pcic_event_thread, PWAIT, "pcicss", hz/4); + } + s = splhigh(); + SIMPLEQ_REMOVE_HEAD(&h->events, pe, pe_q); + splx(s); + + switch (pe->pe_type) { + case PCIC_EVENT_INSERTION: + s = splhigh(); + while (1) { + struct pcic_event *pe1, *pe2; + + if ((pe1 = SIMPLEQ_FIRST(&h->events)) == NULL) + break; + if (pe1->pe_type != PCIC_EVENT_REMOVAL) + break; + if ((pe2 = SIMPLEQ_NEXT(pe1, pe_q)) == NULL) + break; + if (pe2->pe_type == PCIC_EVENT_INSERTION) { + SIMPLEQ_REMOVE_HEAD(&h->events, pe1, pe_q); + free(pe1, M_TEMP); + SIMPLEQ_REMOVE_HEAD(&h->events, pe2, pe_q); + free(pe2, M_TEMP); + } + } + splx(s); + + DPRINTF(("%s: insertion event\n", h->sc->dev.dv_xname)); + pcic_attach_card(h); + break; + + case PCIC_EVENT_REMOVAL: + s = splhigh(); + while (1) { + struct pcic_event *pe1, *pe2; + + if ((pe1 = SIMPLEQ_FIRST(&h->events)) == NULL) + break; + if (pe1->pe_type != PCIC_EVENT_INSERTION) + break; + if ((pe2 = SIMPLEQ_NEXT(pe1, pe_q)) == NULL) + break; + if (pe2->pe_type == PCIC_EVENT_REMOVAL) { + SIMPLEQ_REMOVE_HEAD(&h->events, pe1, pe_q); + free(pe1, M_TEMP); + SIMPLEQ_REMOVE_HEAD(&h->events, pe2, pe_q); + free(pe2, M_TEMP); + } + } + splx(s); + + DPRINTF(("%s: removal event\n", h->sc->dev.dv_xname)); + pcic_detach_card(h, DETACH_FORCE); + break; + + default: + panic("pcic_event_thread: unknown event %d", + pe->pe_type); + } + free(pe, M_TEMP); + } + + h->event_thread = NULL; + + /* In case parent is waiting for us to exit. */ + wakeup(h->sc); + + kthread_exit(0); +} + +void +pcic_init_socket(h) + struct pcic_handle *h; +{ + int reg; + + /* + * queue creation of a kernel thread to handle insert/removal events. + */ +#ifdef DIAGNOSTIC + if (h->event_thread != NULL) + panic("pcic_attach_socket: event thread"); +#endif + kthread_create(pcic_create_event_thread, h); + + /* set up the card to interrupt on card detect */ + + pcic_write(h, PCIC_CSC_INTR, (h->sc->irq << PCIC_CSC_INTR_IRQ_SHIFT) | + PCIC_CSC_INTR_CD_ENABLE); + pcic_write(h, PCIC_INTR, 0); + pcic_read(h, PCIC_CSC); + + /* unsleep the cirrus controller */ + + if ((h->vendor == PCIC_VENDOR_CIRRUS_PD6710) || + (h->vendor == PCIC_VENDOR_CIRRUS_PD672X)) { + reg = pcic_read(h, PCIC_CIRRUS_MISC_CTL_2); + if (reg & PCIC_CIRRUS_MISC_CTL_2_SUSPEND) { + DPRINTF(("%s: socket %02x was suspended\n", + h->sc->dev.dv_xname, h->sock)); + reg &= ~PCIC_CIRRUS_MISC_CTL_2_SUSPEND; + pcic_write(h, PCIC_CIRRUS_MISC_CTL_2, reg); + } + } + /* if there's a card there, then attach it. */ + + reg = pcic_read(h, PCIC_IF_STATUS); + + if ((reg & PCIC_IF_STATUS_CARDDETECT_MASK) == + PCIC_IF_STATUS_CARDDETECT_PRESENT) { + pcic_attach_card(h); + h->laststate = PCIC_LASTSTATE_PRESENT; + } else { + h->laststate = PCIC_LASTSTATE_EMPTY; + } +} + +int +pcic_submatch(parent, cf, aux) + struct device *parent; + struct cfdata *cf; + void *aux; +{ + + struct pccardbus_attach_args *paa = aux; + struct pcic_handle *h = (struct pcic_handle *) paa->pch; + + switch (h->sock) { + case C0SA: + if (cf->cf_loc[PCCARDBUSCF_CONTROLLER] != + PCCARDBUSCF_CONTROLLER_DEFAULT && + cf->cf_loc[PCCARDBUSCF_CONTROLLER] != 0) + return 0; + if (cf->cf_loc[PCCARDBUSCF_SOCKET] != + PCCARDBUSCF_SOCKET_DEFAULT && + cf->cf_loc[PCCARDBUSCF_SOCKET] != 0) + return 0; + + break; + case C0SB: + if (cf->cf_loc[PCCARDBUSCF_CONTROLLER] != + PCCARDBUSCF_CONTROLLER_DEFAULT && + cf->cf_loc[PCCARDBUSCF_CONTROLLER] != 0) + return 0; + if (cf->cf_loc[PCCARDBUSCF_SOCKET] != + PCCARDBUSCF_SOCKET_DEFAULT && + cf->cf_loc[PCCARDBUSCF_SOCKET] != 1) + return 0; + + break; + case C1SA: + if (cf->cf_loc[PCCARDBUSCF_CONTROLLER] != + PCCARDBUSCF_CONTROLLER_DEFAULT && + cf->cf_loc[PCCARDBUSCF_CONTROLLER] != 1) + return 0; + if (cf->cf_loc[PCCARDBUSCF_SOCKET] != + PCCARDBUSCF_SOCKET_DEFAULT && + cf->cf_loc[PCCARDBUSCF_SOCKET] != 0) + return 0; + + break; + case C1SB: + if (cf->cf_loc[PCCARDBUSCF_CONTROLLER] != + PCCARDBUSCF_CONTROLLER_DEFAULT && + cf->cf_loc[PCCARDBUSCF_CONTROLLER] != 1) + return 0; + if (cf->cf_loc[PCCARDBUSCF_SOCKET] != + PCCARDBUSCF_SOCKET_DEFAULT && + cf->cf_loc[PCCARDBUSCF_SOCKET] != 1) + return 0; + + break; + default: + panic("unknown pcic socket"); + } + + return ((*cf->cf_attach->ca_match)(parent, cf, aux)); +} + +int +pcic_print(arg, pnp) + void *arg; + const char *pnp; +{ + struct pccardbus_attach_args *paa = arg; + struct pcic_handle *h = (struct pcic_handle *) paa->pch; + + /* Only "pccard"s can attach to "pcic"s... easy. */ + if (pnp) + printf("pccard at %s", pnp); + + switch (h->sock) { + case C0SA: + printf(" controller 0 socket 0"); + break; + case C0SB: + printf(" controller 0 socket 1"); + break; + case C1SA: + printf(" controller 1 socket 0"); + break; + case C1SB: + printf(" controller 1 socket 1"); + break; + default: + panic("unknown pcic socket"); + } + + return (UNCONF); +} + +int +pcic_intr(arg) + void *arg; +{ + struct pcic_softc *sc = arg; + int i, ret = 0; + + DPRINTF(("%s: intr\n", sc->dev.dv_xname)); + + for (i = 0; i < PCIC_NSLOTS; i++) + if (sc->handle[i].flags & PCIC_FLAG_SOCKETP) + ret += pcic_intr_socket(&sc->handle[i]); + + return (ret ? 1 : 0); +} + +int +pcic_intr_socket(h) + struct pcic_handle *h; +{ + int cscreg; + + cscreg = pcic_read(h, PCIC_CSC); + + cscreg &= (PCIC_CSC_GPI | + PCIC_CSC_CD | + PCIC_CSC_READY | + PCIC_CSC_BATTWARN | + PCIC_CSC_BATTDEAD); + + if (cscreg & PCIC_CSC_GPI) { + DPRINTF(("%s: %02x GPI\n", h->sc->dev.dv_xname, h->sock)); + } + if (cscreg & PCIC_CSC_CD) { + int statreg; + + statreg = pcic_read(h, PCIC_IF_STATUS); + + DPRINTF(("%s: %02x CD %x\n", h->sc->dev.dv_xname, h->sock, + statreg)); + + if ((statreg & PCIC_IF_STATUS_CARDDETECT_MASK) == + PCIC_IF_STATUS_CARDDETECT_PRESENT) { + if (h->laststate != PCIC_LASTSTATE_PRESENT) { + DPRINTF(("%s: enqueing INSERTION event\n", + h->sc->dev.dv_xname)); + pcic_queue_event(h, PCIC_EVENT_INSERTION); + } + h->laststate = PCIC_LASTSTATE_PRESENT; + } else { + if (h->laststate == PCIC_LASTSTATE_PRESENT) { + /* Deactivate the card now. */ + DPRINTF(("%s: deactivating card\n", + h->sc->dev.dv_xname)); + pcic_deactivate_card(h); + + DPRINTF(("%s: enqueing REMOVAL event\n", + h->sc->dev.dv_xname)); + pcic_queue_event(h, PCIC_EVENT_REMOVAL); + } + h->laststate = ((statreg & PCIC_IF_STATUS_CARDDETECT_MASK) == 0) + ? PCIC_LASTSTATE_EMPTY : PCIC_LASTSTATE_HALF; + } + } + if (cscreg & PCIC_CSC_READY) { + DPRINTF(("%s: %02x READY\n", h->sc->dev.dv_xname, h->sock)); + /* shouldn't happen */ + } + if (cscreg & PCIC_CSC_BATTWARN) { + DPRINTF(("%s: %02x BATTWARN\n", h->sc->dev.dv_xname, h->sock)); + } + if (cscreg & PCIC_CSC_BATTDEAD) { + DPRINTF(("%s: %02x BATTDEAD\n", h->sc->dev.dv_xname, h->sock)); + } + return (cscreg ? 1 : 0); +} + +void +pcic_queue_event(h, event) + struct pcic_handle *h; + int event; +{ + struct pcic_event *pe; + int s; + + pe = malloc(sizeof(*pe), M_TEMP, M_NOWAIT); + if (pe == NULL) + panic("pcic_queue_event: can't allocate event"); + + pe->pe_type = event; + s = splhigh(); + SIMPLEQ_INSERT_TAIL(&h->events, pe, pe_q); + splx(s); + wakeup(&h->events); +} + +void +pcic_attach_card(h) + struct pcic_handle *h; +{ + struct pccard_softc *psc = (void*)h->pccard; + if (!(h->flags & PCIC_FLAG_CARDP)) { + /* call the MI attach function */ + psc->sc_if.if_card_attach (psc); + + h->flags |= PCIC_FLAG_CARDP; + } else { + DPRINTF(("pcic_attach_card: already attached")); + } +} + +void +pcic_detach_card(h, flags) + struct pcic_handle *h; + int flags; /* DETACH_* */ +{ + struct pccard_softc *psc = (void*)h->pccard; + if (h->flags & PCIC_FLAG_CARDP) { + h->flags &= ~PCIC_FLAG_CARDP; + + /* call the MI detach function */ + psc->sc_if.if_card_detach (psc, flags); + } else { + DPRINTF(("pcic_detach_card: already detached")); + } +} + +void +pcic_deactivate_card(h) + struct pcic_handle *h; +{ + struct pccard_softc *psc = (void*)h->pccard; + /* call the MI deactivate function */ + psc->sc_if.if_card_deactivate (psc); + + /* power down the socket */ + pcic_write(h, PCIC_PWRCTL, 0); + + /* reset the socket */ + pcic_write(h, PCIC_INTR, 0); +} + +int +pcic_chip_mem_alloc(pch, size, pcmhp) + pccard_chipset_handle_t pch; + bus_size_t size; + struct pccard_mem_handle *pcmhp; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + bus_space_handle_t memh; + bus_addr_t addr; + bus_size_t sizepg; + int i, mask, mhandle; + + /* out of sc->memh, allocate as many pages as necessary */ + + /* convert size to PCIC pages */ + sizepg = (size + (PCIC_MEM_ALIGN - 1)) / PCIC_MEM_ALIGN; + if (sizepg > PCIC_MAX_MEM_PAGES) + return (1); + + mask = (1 << sizepg) - 1; + + addr = 0; /* XXX gcc -Wuninitialized */ + mhandle = 0; /* XXX gcc -Wuninitialized */ + + for (i = 0; i <= PCIC_MAX_MEM_PAGES - sizepg; i++) { + if ((h->sc->subregionmask & (mask << i)) == (mask << i)) { + if (bus_space_subregion(h->sc->memt, h->sc->memh, + i * PCIC_MEM_PAGESIZE, + sizepg * PCIC_MEM_PAGESIZE, &memh)) + return (1); + mhandle = mask << i; + addr = h->sc->membase + (i * PCIC_MEM_PAGESIZE); + h->sc->subregionmask &= ~(mhandle); + pcmhp->memt = h->sc->memt; + pcmhp->memh = memh; + pcmhp->addr = addr; + pcmhp->size = size; + pcmhp->mhandle = mhandle; + pcmhp->realsize = sizepg * PCIC_MEM_PAGESIZE; + return (0); + } + } + + return (1); +} + +void +pcic_chip_mem_free(pch, pcmhp) + pccard_chipset_handle_t pch; + struct pccard_mem_handle *pcmhp; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + + h->sc->subregionmask |= pcmhp->mhandle; +} + +static struct mem_map_index_st { + int sysmem_start_lsb; + int sysmem_start_msb; + int sysmem_stop_lsb; + int sysmem_stop_msb; + int cardmem_lsb; + int cardmem_msb; + int memenable; +} mem_map_index[] = { + { + PCIC_SYSMEM_ADDR0_START_LSB, + PCIC_SYSMEM_ADDR0_START_MSB, + PCIC_SYSMEM_ADDR0_STOP_LSB, + PCIC_SYSMEM_ADDR0_STOP_MSB, + PCIC_CARDMEM_ADDR0_LSB, + PCIC_CARDMEM_ADDR0_MSB, + PCIC_ADDRWIN_ENABLE_MEM0, + }, + { + PCIC_SYSMEM_ADDR1_START_LSB, + PCIC_SYSMEM_ADDR1_START_MSB, + PCIC_SYSMEM_ADDR1_STOP_LSB, + PCIC_SYSMEM_ADDR1_STOP_MSB, + PCIC_CARDMEM_ADDR1_LSB, + PCIC_CARDMEM_ADDR1_MSB, + PCIC_ADDRWIN_ENABLE_MEM1, + }, + { + PCIC_SYSMEM_ADDR2_START_LSB, + PCIC_SYSMEM_ADDR2_START_MSB, + PCIC_SYSMEM_ADDR2_STOP_LSB, + PCIC_SYSMEM_ADDR2_STOP_MSB, + PCIC_CARDMEM_ADDR2_LSB, + PCIC_CARDMEM_ADDR2_MSB, + PCIC_ADDRWIN_ENABLE_MEM2, + }, + { + PCIC_SYSMEM_ADDR3_START_LSB, + PCIC_SYSMEM_ADDR3_START_MSB, + PCIC_SYSMEM_ADDR3_STOP_LSB, + PCIC_SYSMEM_ADDR3_STOP_MSB, + PCIC_CARDMEM_ADDR3_LSB, + PCIC_CARDMEM_ADDR3_MSB, + PCIC_ADDRWIN_ENABLE_MEM3, + }, + { + PCIC_SYSMEM_ADDR4_START_LSB, + PCIC_SYSMEM_ADDR4_START_MSB, + PCIC_SYSMEM_ADDR4_STOP_LSB, + PCIC_SYSMEM_ADDR4_STOP_MSB, + PCIC_CARDMEM_ADDR4_LSB, + PCIC_CARDMEM_ADDR4_MSB, + PCIC_ADDRWIN_ENABLE_MEM4, + }, +}; + +void +pcic_chip_do_mem_map(h, win) + struct pcic_handle *h; + int win; +{ + int reg; + + pcic_write(h, mem_map_index[win].sysmem_start_lsb, + (h->mem[win].addr >> PCIC_SYSMEM_ADDRX_SHIFT) & 0xff); + pcic_write(h, mem_map_index[win].sysmem_start_msb, + ((h->mem[win].addr >> (PCIC_SYSMEM_ADDRX_SHIFT + 8)) & + PCIC_SYSMEM_ADDRX_START_MSB_ADDR_MASK)); + +#if 0 + /* XXX do I want 16 bit all the time? */ + PCIC_SYSMEM_ADDRX_START_MSB_DATASIZE_16BIT; +#endif + + pcic_write(h, mem_map_index[win].sysmem_stop_lsb, + ((h->mem[win].addr + h->mem[win].size) >> + PCIC_SYSMEM_ADDRX_SHIFT) & 0xff); + pcic_write(h, mem_map_index[win].sysmem_stop_msb, + (((h->mem[win].addr + h->mem[win].size) >> + (PCIC_SYSMEM_ADDRX_SHIFT + 8)) & + PCIC_SYSMEM_ADDRX_STOP_MSB_ADDR_MASK) | + PCIC_SYSMEM_ADDRX_STOP_MSB_WAIT2); + + pcic_write(h, mem_map_index[win].cardmem_lsb, + (h->mem[win].offset >> PCIC_CARDMEM_ADDRX_SHIFT) & 0xff); + pcic_write(h, mem_map_index[win].cardmem_msb, + ((h->mem[win].offset >> (PCIC_CARDMEM_ADDRX_SHIFT + 8)) & + PCIC_CARDMEM_ADDRX_MSB_ADDR_MASK) | + ((h->mem[win].kind == PCCARD_MEM_ATTR) ? + PCIC_CARDMEM_ADDRX_MSB_REGACTIVE_ATTR : 0)); + + reg = pcic_read(h, PCIC_ADDRWIN_ENABLE); + reg |= (mem_map_index[win].memenable | PCIC_ADDRWIN_ENABLE_MEMCS16); + pcic_write(h, PCIC_ADDRWIN_ENABLE, reg); + + delay(100); + +#ifdef PCICDEBUG + { + int r1, r2, r3, r4, r5, r6; + + r1 = pcic_read(h, mem_map_index[win].sysmem_start_msb); + r2 = pcic_read(h, mem_map_index[win].sysmem_start_lsb); + r3 = pcic_read(h, mem_map_index[win].sysmem_stop_msb); + r4 = pcic_read(h, mem_map_index[win].sysmem_stop_lsb); + r5 = pcic_read(h, mem_map_index[win].cardmem_msb); + r6 = pcic_read(h, mem_map_index[win].cardmem_lsb); + + DPRINTF(("pcic_chip_do_mem_map window %d: %02x%02x %02x%02x " + "%02x%02x\n", win, r1, r2, r3, r4, r5, r6)); + } +#endif +} + +int +pcic_chip_mem_map(pch, kind, card_addr, size, pcmhp, offsetp, windowp) + pccard_chipset_handle_t pch; + int kind; + bus_addr_t card_addr; + bus_size_t size; + struct pccard_mem_handle *pcmhp; + bus_addr_t *offsetp; + int *windowp; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + bus_addr_t busaddr; + long card_offset; + int i, win; + + win = -1; + for (i = 0; i < (sizeof(mem_map_index) / sizeof(mem_map_index[0])); + i++) { + if ((h->memalloc & (1 << i)) == 0) { + win = i; + h->memalloc |= (1 << i); + break; + } + } + + if (win == -1) + return (1); + + *windowp = win; + + /* XXX this is pretty gross */ + + if (h->sc->memt != pcmhp->memt) + panic("pcic_chip_mem_map memt is bogus"); + + busaddr = pcmhp->addr; + + /* + * compute the address offset to the pccard address space for the + * pcic. this is intentionally signed. The masks and shifts below + * will cause TRT to happen in the pcic registers. Deal with making + * sure the address is aligned, and return the alignment offset. + */ + + *offsetp = card_addr % PCIC_MEM_ALIGN; + card_addr -= *offsetp; + + DPRINTF(("pcic_chip_mem_map window %d bus %lx+%lx+%lx at card addr " + "%lx\n", win, (u_long) busaddr, (u_long) * offsetp, (u_long) size, + (u_long) card_addr)); + + /* + * include the offset in the size, and decrement size by one, since + * the hw wants start/stop + */ + size += *offsetp - 1; + + card_offset = (((long) card_addr) - ((long) busaddr)); + + h->mem[win].addr = busaddr; + h->mem[win].size = size; + h->mem[win].offset = card_offset; + h->mem[win].kind = kind; + + pcic_chip_do_mem_map(h, win); + + return (0); +} + +void +pcic_chip_mem_unmap(pch, window) + pccard_chipset_handle_t pch; + int window; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + int reg; + + if (window >= (sizeof(mem_map_index) / sizeof(mem_map_index[0]))) + panic("pcic_chip_mem_unmap: window out of range"); + + reg = pcic_read(h, PCIC_ADDRWIN_ENABLE); + reg &= ~mem_map_index[window].memenable; + pcic_write(h, PCIC_ADDRWIN_ENABLE, reg); + + h->memalloc &= ~(1 << window); +} + +int +pcic_chip_io_alloc(pch, start, size, align, pcihp) + pccard_chipset_handle_t pch; + bus_addr_t start; + bus_size_t size; + bus_size_t align; + struct pccard_io_handle *pcihp; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + bus_space_tag_t iot; + bus_space_handle_t ioh; + bus_addr_t ioaddr; + int flags = 0; + + /* + * Allocate some arbitrary I/O space. + */ + + iot = h->sc->iot; + + if (start) { + ioaddr = start; + if (bus_space_map(iot, start, size, 0, &ioh)) + return (1); + DPRINTF(("pcic_chip_io_alloc map port %lx+%lx\n", + (u_long) ioaddr, (u_long) size)); + } else { + flags |= PCCARD_IO_ALLOCATED; + if (bus_space_alloc(iot, h->sc->iobase, + h->sc->iobase + h->sc->iosize, size, align, 0, 0, + &ioaddr, &ioh)) + return (1); + DPRINTF(("pcic_chip_io_alloc alloc port %lx+%lx\n", + (u_long) ioaddr, (u_long) size)); + } + + pcihp->iot = iot; + pcihp->ioh = ioh; + pcihp->addr = ioaddr; + pcihp->size = size; + pcihp->flags = flags; + + return (0); +} + +void +pcic_chip_io_free(pch, pcihp) + pccard_chipset_handle_t pch; + struct pccard_io_handle *pcihp; +{ + bus_space_tag_t iot = pcihp->iot; + bus_space_handle_t ioh = pcihp->ioh; + bus_size_t size = pcihp->size; + + if (pcihp->flags & PCCARD_IO_ALLOCATED) + bus_space_free(iot, ioh, size); + else + bus_space_unmap(iot, ioh, size); +} + + +static struct io_map_index_st { + int start_lsb; + int start_msb; + int stop_lsb; + int stop_msb; + int ioenable; + int ioctlmask; + int ioctlbits[3]; /* indexed by PCCARD_WIDTH_* */ +} io_map_index[] = { + { + PCIC_IOADDR0_START_LSB, + PCIC_IOADDR0_START_MSB, + PCIC_IOADDR0_STOP_LSB, + PCIC_IOADDR0_STOP_MSB, + PCIC_ADDRWIN_ENABLE_IO0, + PCIC_IOCTL_IO0_WAITSTATE | PCIC_IOCTL_IO0_ZEROWAIT | + PCIC_IOCTL_IO0_IOCS16SRC_MASK | PCIC_IOCTL_IO0_DATASIZE_MASK, + { + PCIC_IOCTL_IO0_IOCS16SRC_CARD, + PCIC_IOCTL_IO0_IOCS16SRC_DATASIZE | + PCIC_IOCTL_IO0_DATASIZE_8BIT, + PCIC_IOCTL_IO0_IOCS16SRC_DATASIZE | + PCIC_IOCTL_IO0_DATASIZE_16BIT, + }, + }, + { + PCIC_IOADDR1_START_LSB, + PCIC_IOADDR1_START_MSB, + PCIC_IOADDR1_STOP_LSB, + PCIC_IOADDR1_STOP_MSB, + PCIC_ADDRWIN_ENABLE_IO1, + PCIC_IOCTL_IO1_WAITSTATE | PCIC_IOCTL_IO1_ZEROWAIT | + PCIC_IOCTL_IO1_IOCS16SRC_MASK | PCIC_IOCTL_IO1_DATASIZE_MASK, + { + PCIC_IOCTL_IO1_IOCS16SRC_CARD, + PCIC_IOCTL_IO1_IOCS16SRC_DATASIZE | + PCIC_IOCTL_IO1_DATASIZE_8BIT, + PCIC_IOCTL_IO1_IOCS16SRC_DATASIZE | + PCIC_IOCTL_IO1_DATASIZE_16BIT, + }, + }, +}; + +void +pcic_chip_do_io_map(h, win) + struct pcic_handle *h; + int win; +{ + int reg; + + DPRINTF(("pcic_chip_do_io_map win %d addr %lx size %lx width %d\n", + win, (long) h->io[win].addr, (long) h->io[win].size, + h->io[win].width * 8)); + + pcic_write(h, io_map_index[win].start_lsb, h->io[win].addr & 0xff); + pcic_write(h, io_map_index[win].start_msb, + (h->io[win].addr >> 8) & 0xff); + + pcic_write(h, io_map_index[win].stop_lsb, + (h->io[win].addr + h->io[win].size - 1) & 0xff); + pcic_write(h, io_map_index[win].stop_msb, + ((h->io[win].addr + h->io[win].size - 1) >> 8) & 0xff); + + reg = pcic_read(h, PCIC_IOCTL); + reg &= ~io_map_index[win].ioctlmask; + reg |= io_map_index[win].ioctlbits[h->io[win].width]; + pcic_write(h, PCIC_IOCTL, reg); + + reg = pcic_read(h, PCIC_ADDRWIN_ENABLE); + reg |= io_map_index[win].ioenable; + pcic_write(h, PCIC_ADDRWIN_ENABLE, reg); +} + +int +pcic_chip_io_map(pch, width, offset, size, pcihp, windowp) + pccard_chipset_handle_t pch; + int width; + bus_addr_t offset; + bus_size_t size; + struct pccard_io_handle *pcihp; + int *windowp; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + bus_addr_t ioaddr = pcihp->addr + offset; + int i, win; +#ifdef PCICDEBUG + static char *width_names[] = { "auto", "io8", "io16" }; +#endif + + /* XXX Sanity check offset/size. */ + + win = -1; + for (i = 0; i < (sizeof(io_map_index) / sizeof(io_map_index[0])); i++) { + if ((h->ioalloc & (1 << i)) == 0) { + win = i; + h->ioalloc |= (1 << i); + break; + } + } + + if (win == -1) + return (1); + + *windowp = win; + + /* XXX this is pretty gross */ + + if (h->sc->iot != pcihp->iot) + panic("pcic_chip_io_map iot is bogus"); + + DPRINTF(("pcic_chip_io_map window %d %s port %lx+%lx\n", + win, width_names[width], (u_long) ioaddr, (u_long) size)); + + /* XXX wtf is this doing here? */ + + printf(" port 0x%lx", (u_long) ioaddr); + if (size > 1) + printf("-0x%lx", (u_long) ioaddr + (u_long) size - 1); + + h->io[win].addr = ioaddr; + h->io[win].size = size; + h->io[win].width = width; + + pcic_chip_do_io_map(h, win); + + return (0); +} + +void +pcic_chip_io_unmap(pch, window) + pccard_chipset_handle_t pch; + int window; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + int reg; + + if (window >= (sizeof(io_map_index) / sizeof(io_map_index[0]))) + panic("pcic_chip_io_unmap: window out of range"); + + reg = pcic_read(h, PCIC_ADDRWIN_ENABLE); + reg &= ~io_map_index[window].ioenable; + pcic_write(h, PCIC_ADDRWIN_ENABLE, reg); + + h->ioalloc &= ~(1 << window); +} + +static void +pcic_wait_ready(h) + struct pcic_handle *h; +{ + int i; + + for (i = 0; i < 10000; i++) { + if (pcic_read(h, PCIC_IF_STATUS) & PCIC_IF_STATUS_READY) + return; + delay(500); +#ifdef PCICDEBUG + if (pcic_debug) { + if ((i>5000) && (i%100 == 99)) + printf("."); + } +#endif + } + +#ifdef DIAGNOSTIC + printf("pcic_wait_ready: ready never happened, status = %02x\n", + pcic_read(h, PCIC_IF_STATUS)); +#endif +} + +void +pcic_chip_socket_enable(pch) + pccard_chipset_handle_t pch; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + struct pccard_softc *psc = (void*)h->pccard; + int cardtype, reg, win; + + /* this bit is mostly stolen from pcic_attach_card */ + + /* power down the socket to reset it, clear the card reset pin */ + + pcic_write(h, PCIC_PWRCTL, 0); + + /* + * wait 300ms until power fails (Tpf). Then, wait 100ms since + * we are changing Vcc (Toff). + */ + delay((300 + 100) * 1000); + + /* power up the socket */ + + pcic_write(h, PCIC_PWRCTL, PCIC_PWRCTL_DISABLE_RESETDRV + | PCIC_PWRCTL_PWR_ENABLE); + + /* + * wait 100ms until power raise (Tpr) and 20ms to become + * stable (Tsu(Vcc)). + * + * some machines require some more time to be settled + * (300ms is added here). + */ + delay((100 + 20 + 300) * 1000); + + pcic_write(h, PCIC_PWRCTL, PCIC_PWRCTL_DISABLE_RESETDRV | PCIC_PWRCTL_OE + | PCIC_PWRCTL_PWR_ENABLE); + pcic_write(h, PCIC_INTR, 0); + + /* + * hold RESET at least 10us. + */ + delay(10); + + /* clear the reset flag */ + + pcic_write(h, PCIC_INTR, PCIC_INTR_RESET); + + /* wait 20ms as per pc card standard (r2.01) section 4.3.6 */ + + delay(20000); + + /* wait for the chip to finish initializing */ + +#ifdef DIAGNOSTIC + reg = pcic_read(h, PCIC_IF_STATUS); + if (!(reg & PCIC_IF_STATUS_POWERACTIVE)) { + printf("pcic_chip_socket_enable: status %x", reg); + } +#endif + + pcic_wait_ready(h); + + /* zero out the address windows */ + + pcic_write(h, PCIC_ADDRWIN_ENABLE, 0); + + /* set the card type */ + + cardtype = psc->sc_if.if_card_gettype (psc); + + reg = pcic_read(h, PCIC_INTR); + reg &= ~(PCIC_INTR_CARDTYPE_MASK | PCIC_INTR_IRQ_MASK | PCIC_INTR_ENABLE); + reg |= ((cardtype == PCCARD_IFTYPE_IO) ? + PCIC_INTR_CARDTYPE_IO : + PCIC_INTR_CARDTYPE_MEM); + reg |= h->ih_irq; + pcic_write(h, PCIC_INTR, reg); + + DPRINTF(("%s: pcic_chip_socket_enable %02x cardtype %s %02x\n", + h->sc->dev.dv_xname, h->sock, + ((cardtype == PCCARD_IFTYPE_IO) ? "io" : "mem"), reg)); + + /* reinstall all the memory and io mappings */ + + for (win = 0; win < PCIC_MEM_WINS; win++) + if (h->memalloc & (1 << win)) + pcic_chip_do_mem_map(h, win); + + for (win = 0; win < PCIC_IO_WINS; win++) + if (h->ioalloc & (1 << win)) + pcic_chip_do_io_map(h, win); +} + +void +pcic_chip_socket_disable(pch) + pccard_chipset_handle_t pch; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + + DPRINTF(("pcic_chip_socket_disable\n")); + + /* power down the socket */ + + pcic_write(h, PCIC_PWRCTL, 0); + + /* + * wait 300ms until power fails (Tpf). + */ + delay(300 * 1000); +} diff --git a/sys/dev/pcic/i82365_isa.c b/sys/dev/pcic/i82365_isa.c new file mode 100644 index 00000000000..6e41f3cc3bf --- /dev/null +++ b/sys/dev/pcic/i82365_isa.c @@ -0,0 +1,262 @@ +/* $NetBSD: i82365_isa.c,v 1.11 1998/06/09 07:25:00 thorpej Exp $ */ +/* $FreeBSD$ */ + +#define PCICISADEBUG + +/* + * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. + * 4. 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include +typedef int isa_chipset_tag_t; +#define ia_msize ia_memsize +#define delay(x) DELAY(x) +#else +#include +#include +#endif + +#include +#include +#include + +#include +#include +#include + +#ifdef PCICISADEBUG +int pcicisa_debug = 0 /* XXX */ ; +#define DPRINTF(arg) if (pcicisa_debug) printf arg; +#else +#define DPRINTF(arg) +#endif + +int pcic_isa_probe __P((struct device *, struct cfdata *, void *)); +void pcic_isa_attach __P((struct device *, struct device *, void *)); + +void *pcic_isa_chip_intr_establish __P((pccard_chipset_handle_t, + struct pccard_function *, int, int (*) (void *), void *)); +void pcic_isa_chip_intr_disestablish __P((pccard_chipset_handle_t, void *)); + +#ifdef __FreeBSD__ +struct cfattach pcic_isa_ca = { + sizeof(struct pcic_softc), pcic_isa_probe, pcic_isa_attach +}; +#else +struct cfattach pcic_isa_ca = { + sizeof(struct pcic_softc), pcic_isa_probe, pcic_isa_attach +}; +#endif +static struct pccard_chip_functions pcic_isa_functions = { + pcic_chip_mem_alloc, + pcic_chip_mem_free, + pcic_chip_mem_map, + pcic_chip_mem_unmap, + + pcic_chip_io_alloc, + pcic_chip_io_free, + pcic_chip_io_map, + pcic_chip_io_unmap, + + pcic_isa_chip_intr_establish, + pcic_isa_chip_intr_disestablish, + + pcic_chip_socket_enable, + pcic_chip_socket_disable, +}; + +int +pcic_isa_probe(parent, match, aux) + struct device *parent; + struct cfdata *match; + void *aux; +{ + struct isa_attach_args *ia = aux; + bus_space_tag_t iot = ia->ia_iot; + bus_space_handle_t ioh, memh; + int val, found; + + /* Disallow wildcarded i/o address. */ +#ifdef __FreeBSD__ + if (ia->ia_iobase == ISACF_IOBASE_DEFAULT) +#else + if (ia->ia_iobase == ISACF_PORT_DEFAULT) +#endif + return (0); + + if (bus_space_map(iot, ia->ia_iobase, PCIC_IOSIZE, 0, &ioh)) + return (0); + + if (ia->ia_msize == -1) + ia->ia_msize = PCIC_MEMSIZE; +#ifdef __FreeBSD__ + if (bus_space_map(ia->ia_memt, kvtop(ia->ia_membase), ia->ia_msize, 0, &memh)) +#else + if (bus_space_map(ia->ia_memt, ia->ia_maddr, ia->ia_msize, 0, &memh)) +#endif + return (0); + + found = 0; + + /* + * this could be done with a loop, but it would violate the + * abstraction + */ + + bus_space_write_1(iot, ioh, PCIC_REG_INDEX, C0SA + PCIC_IDENT); + + val = bus_space_read_1(iot, ioh, PCIC_REG_DATA); + + if (pcic_ident_ok(val)) + found++; + + + bus_space_write_1(iot, ioh, PCIC_REG_INDEX, C0SB + PCIC_IDENT); + + val = bus_space_read_1(iot, ioh, PCIC_REG_DATA); + + if (pcic_ident_ok(val)) + found++; + + + bus_space_write_1(iot, ioh, PCIC_REG_INDEX, C1SA + PCIC_IDENT); + + val = bus_space_read_1(iot, ioh, PCIC_REG_DATA); + + if (pcic_ident_ok(val)) + found++; + + + bus_space_write_1(iot, ioh, PCIC_REG_INDEX, C1SB + PCIC_IDENT); + + val = bus_space_read_1(iot, ioh, PCIC_REG_DATA); + + if (pcic_ident_ok(val)) + found++; + + + bus_space_unmap(iot, ioh, PCIC_IOSIZE); + + bus_space_unmap(ia->ia_memt, memh, ia->ia_msize); + + if (!found) + return (0); + + ia->ia_iosize = PCIC_IOSIZE; + + return (1); +} + +void +pcic_isa_attach(parent, self, aux) + struct device *parent, *self; + void *aux; +{ + struct pcic_softc *sc = (void *) self; + struct isa_attach_args *ia = aux; + isa_chipset_tag_t ic = ia->ia_ic; + bus_space_tag_t iot = ia->ia_iot; + bus_space_tag_t memt = ia->ia_memt; + bus_space_handle_t ioh; + bus_space_handle_t memh; + + /* Map i/o space. */ + if (bus_space_map(iot, ia->ia_iobase, ia->ia_iosize, 0, &ioh)) { + printf(": can't map i/o space\n"); + return; + } + + /* Map mem space. */ +#ifdef __FreeBSD__ + if (bus_space_map(memt, kvtop(ia->ia_membase), ia->ia_memsize, 0, &memh)) { +#else + if (bus_space_map(memt, ia->ia_maddr, ia->ia_msize, 0, &memh)) { +#endif + printf(": can't map mem space\n"); + return; + } +#ifdef __FreeBSD__ + sc->membase = kvtop(ia->ia_membase); +#else + sc->membase = ia->ia_maddr; +#endif + sc->subregionmask = (1 << (ia->ia_msize / PCIC_MEM_PAGESIZE)) - 1; + + sc->intr_est = ic; + sc->pct = (pccard_chipset_tag_t) & pcic_isa_functions; + + sc->iot = iot; + sc->ioh = ioh; + sc->memt = memt; + sc->memh = memh; + + /* + * allocate an irq. it will be used by both controllers. I could + * use two different interrupts, but interrupts are relatively + * scarce, shareable, and for PCIC controllers, very infrequent. + */ + + if ((sc->irq = ia->ia_irq) == IRQUNK) { + if (isa_intr_alloc(ic, + PCIC_CSC_INTR_IRQ_VALIDMASK & pcic_isa_intr_alloc_mask, + IST_EDGE, &sc->irq)) { + printf("\n%s: can't allocate interrupt\n", + sc->dev.dv_xname); + return; + } + printf(": using irq %d", sc->irq); + } + printf("\n"); + + pcic_attach(sc); + + pcic_isa_bus_width_probe (sc, iot, ioh, ia->ia_iobase, ia->ia_iosize); + + sc->ih = isa_intr_establish(ic, sc->irq, IST_EDGE, IPL_TTY, + pcic_intr, sc); + if (sc->ih == NULL) { + printf("%s: can't establish interrupt\n", sc->dev.dv_xname); + return; + } + + pcic_attach_sockets(sc); +} diff --git a/sys/dev/pcic/i82365_isasubr.c b/sys/dev/pcic/i82365_isasubr.c new file mode 100644 index 00000000000..7741fa078a9 --- /dev/null +++ b/sys/dev/pcic/i82365_isasubr.c @@ -0,0 +1,278 @@ +/* $NetBSD: i82365_isasubr.c,v 1.1 1998/06/07 18:28:31 sommerfe Exp $ */ +/* $FreeBSD$ */ + +#define PCICISADEBUG + +/* + * Copyright (c) 1998 Bill Sommerfeld. All rights reserved. + * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. + * 4. 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ +#include +typedef int isa_chipset_tag_t; +#define delay(x) DELAY(x) +#else +#include +#include +#endif + +#include +#include +#include + +#include +#include +#include + +/***************************************************************************** + * Configurable parameters. + *****************************************************************************/ + +#if 0 +#include "opt_pcic_isa_alloc_iobase.h" +#include "opt_pcic_isa_alloc_iosize.h" +#include "opt_pcic_isa_intr_alloc_mask.h" +#endif + +/* + * Default I/O allocation range. If both are set to non-zero, these + * values will be used instead. Otherwise, the code attempts to probe + * the bus width. Systems with 10 address bits should use 0x300 and 0xff. + * Systems with 12 address bits (most) should use 0x400 and 0xbff. + */ + +#ifndef PCIC_ISA_ALLOC_IOBASE +#define PCIC_ISA_ALLOC_IOBASE 0 +#endif + +#ifndef PCIC_ISA_ALLOC_IOSIZE +#define PCIC_ISA_ALLOC_IOSIZE 0 +#endif + +int pcic_isa_alloc_iobase = PCIC_ISA_ALLOC_IOBASE; +int pcic_isa_alloc_iosize = PCIC_ISA_ALLOC_IOSIZE; + + +/* + * Default IRQ allocation bitmask. This defines the range of allowable + * IRQs for PCCARD slots. Useful if order of probing would screw up other + * devices, or if PCIC hardware/cards have trouble with certain interrupt + * lines. + * + * We disable IRQ 10 by default, since some common laptops (namely, the + * NEC Versa series) reserve IRQ 10 for the docking station SCSI interface. + */ + +#ifndef PCIC_ISA_INTR_ALLOC_MASK +#define PCIC_ISA_INTR_ALLOC_MASK 0xfbff +#endif + +int pcic_isa_intr_alloc_mask = PCIC_ISA_INTR_ALLOC_MASK; + +/***************************************************************************** + * End of configurable parameters. + *****************************************************************************/ + +#ifdef PCICISADEBUG +int pcicsubr_debug = 0 /* XXX */ ; +#define DPRINTF(arg) if (pcicsubr_debug) printf arg; +#else +#define DPRINTF(arg) +#endif + +void pcic_isa_bus_width_probe (sc, iot, ioh, base, length) + struct pcic_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; + bus_addr_t base; + u_int32_t length; +{ + bus_space_handle_t ioh_high; + int i, iobuswidth, tmp1, tmp2; + + /* + * figure out how wide the isa bus is. Do this by checking if the + * pcic controller is mirrored 0x400 above where we expect it to be. + */ + + iobuswidth = 12; + + /* Map i/o space. */ + if (bus_space_map(iot, base + 0x400, length, 0, &ioh_high)) { + printf("%s: can't map high i/o space\n", sc->dev.dv_xname); + return; + } + + for (i = 0; i < PCIC_NSLOTS; i++) { + if (sc->handle[i].flags & PCIC_FLAG_SOCKETP) { + /* + * read the ident flags from the normal space and + * from the mirror, and compare them + */ + + bus_space_write_1(iot, ioh, PCIC_REG_INDEX, + sc->handle[i].sock + PCIC_IDENT); + tmp1 = bus_space_read_1(iot, ioh, PCIC_REG_DATA); + + bus_space_write_1(iot, ioh_high, PCIC_REG_INDEX, + sc->handle[i].sock + PCIC_IDENT); + tmp2 = bus_space_read_1(iot, ioh_high, PCIC_REG_DATA); + + if (tmp1 == tmp2) + iobuswidth = 10; + } + } + + bus_space_free(iot, ioh_high, length); + + /* + * XXX mycroft recommends I/O space range 0x400-0xfff . I should put + * this in a header somewhere + */ + + /* + * XXX some hardware doesn't seem to grok addresses in 0x400 range-- + * apparently missing a bit or more of address lines. (e.g. + * CIRRUS_PD672X with Linksys EthernetCard ne2000 clone in TI + * TravelMate 5000--not clear which is at fault) + * + * Add a kludge to detect 10 bit wide buses and deal with them, + * and also a config file option to override the probe. + */ + + if (iobuswidth == 10) { + sc->iobase = 0x300; + sc->iosize = 0x0ff; + } else { +#if 0 + /* + * This is what we'd like to use, but... + */ + sc->iobase = 0x400; + sc->iosize = 0xbff; +#else + /* + * ...the above bus width probe doesn't always work. + * So, experimentation has shown the following range + * to not lose on systems that 0x300-0x3ff loses on + * (e.g. the NEC Versa 6030X). + */ + sc->iobase = 0x330; + sc->iosize = 0x0cf; +#endif + } + + DPRINTF(("%s: bus_space_alloc range 0x%04lx-0x%04lx (probed)\n", + sc->dev.dv_xname, (long) sc->iobase, + + (long) sc->iobase + sc->iosize)); + + if (pcic_isa_alloc_iobase && pcic_isa_alloc_iosize) { + sc->iobase = pcic_isa_alloc_iobase; + sc->iosize = pcic_isa_alloc_iosize; + + DPRINTF(("%s: bus_space_alloc range 0x%04lx-0x%04lx " + "(config override)\n", sc->dev.dv_xname, (long) sc->iobase, + (long) sc->iobase + sc->iosize)); + } +} + + +void * +pcic_isa_chip_intr_establish(pch, pf, ipl, fct, arg) + pccard_chipset_handle_t pch; + struct pccard_function *pf; + int ipl; + int (*fct) __P((void *)); + void *arg; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + isa_chipset_tag_t ic = h->sc->intr_est; + int irq, ist; + void *ih; + int reg; + + if (pf->cfe->flags & PCCARD_CFE_IRQLEVEL) + ist = IST_LEVEL; + else if (pf->cfe->flags & PCCARD_CFE_IRQPULSE) + ist = IST_PULSE; + else + ist = IST_LEVEL; + + if (isa_intr_alloc(ic, + PCIC_INTR_IRQ_VALIDMASK & pcic_isa_intr_alloc_mask, ist, &irq)) + return (NULL); + if ((ih = isa_intr_establish(ic, irq, ist, ipl, + fct, arg)) == NULL) + return (NULL); + + reg = pcic_read(h, PCIC_INTR); + reg &= ~PCIC_INTR_IRQ_MASK; + reg |= irq; + pcic_write(h, PCIC_INTR, reg); + + h->ih_irq = irq; + + printf("%s: card irq %d\n", h->pccard->dv_xname, irq); + + return (ih); +} + +void +pcic_isa_chip_intr_disestablish(pch, ih) + pccard_chipset_handle_t pch; + void *ih; +{ + struct pcic_handle *h = (struct pcic_handle *) pch; + isa_chipset_tag_t ic = h->sc->intr_est; + int reg; + + h->ih_irq = 0; + + reg = pcic_read(h, PCIC_INTR); + reg &= ~(PCIC_INTR_IRQ_MASK | PCIC_INTR_ENABLE); + pcic_write(h, PCIC_INTR, reg); + + isa_intr_disestablish(ic, ih); +} diff --git a/sys/dev/pcic/i82365_isavar.h b/sys/dev/pcic/i82365_isavar.h new file mode 100644 index 00000000000..8e4ea914a11 --- /dev/null +++ b/sys/dev/pcic/i82365_isavar.h @@ -0,0 +1,50 @@ +/* $NetBSD: i82365_isavar.h,v 1.1 1998/06/07 18:28:31 sommerfe Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1998 Bill Sommerfeld. All rights reserved. + * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. + * 4. 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. + */ + +extern int pcic_isa_intr_alloc_mask; + +/* + * Establish/disestablish interrupts for PCMCIA functions. + */ + +void *pcic_isa_chip_intr_establish __P((pcmcia_chipset_handle_t, + struct pcmcia_function *, int, int (*) (void *), void *)); +void pcic_isa_chip_intr_disestablish __P((pcmcia_chipset_handle_t, void *)); + +/* + * Figure out how wide the ISA bus is... + */ + +void pcic_isa_bus_width_probe __P((struct pcic_softc *, bus_space_tag_t, + bus_space_handle_t, bus_addr_t, u_int32_t)); + diff --git a/sys/dev/pcic/i82365reg.h b/sys/dev/pcic/i82365reg.h new file mode 100644 index 00000000000..b6fc39fc0f5 --- /dev/null +++ b/sys/dev/pcic/i82365reg.h @@ -0,0 +1,338 @@ +/* $NetBSD: i82365reg.h,v 1.3 1998/12/20 17:53:28 nathanw Exp $ */ +/* $FreeBSD$ */ + +/* + * Copyright (c) 1997 Marc Horowitz. 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 Marc Horowitz. + * 4. 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. + */ + +/* + * All information is from the intel 82365sl PC Card Interface Controller + * (PCIC) data sheet, marked "preliminary". Order number 290423-002, January + * 1993. + */ + +#define PCIC_IOSIZE 2 + +#define PCIC_REG_INDEX 0 +#define PCIC_REG_DATA 1 + +/* + * The PCIC allows two chips to share the same address. In order not to run + * afoul of the netbsd device model, this driver will treat those chips as + * the same device. + */ + +#define PCIC_CHIP0_BASE 0x00 +#define PCIC_CHIP1_BASE 0x80 + +/* Each PCIC chip can drive two sockets */ + +#define PCIC_SOCKETA_INDEX 0x00 +#define PCIC_SOCKETB_INDEX 0x40 + +/* general setup registers */ + +#define PCIC_IDENT 0x00 /* RO */ +#define PCIC_IDENT_IFTYPE_MASK 0xC0 +#define PCIC_IDENT_IFTYPE_IO_ONLY 0x00 +#define PCIC_IDENT_IFTYPE_MEM_ONLY 0x40 +#define PCIC_IDENT_IFTYPE_MEM_AND_IO 0x80 +#define PCIC_IDENT_IFTYPE_RESERVED 0xC0 +#define PCIC_IDENT_ZERO 0x30 +#define PCIC_IDENT_REV_MASK 0x0F +#define PCIC_IDENT_REV_I82365SLR0 0x02 +#define PCIC_IDENT_REV_I82365SLR1 0x03 + +#define PCIC_IF_STATUS 0x01 /* RO */ +#define PCIC_IF_STATUS_GPI 0x80 /* General Purpose Input */ +#define PCIC_IF_STATUS_POWERACTIVE 0x40 +#define PCIC_IF_STATUS_READY 0x20 /* really READY/!BUSY */ +#define PCIC_IF_STATUS_MEM_WP 0x10 +#define PCIC_IF_STATUS_CARDDETECT_MASK 0x0C +#define PCIC_IF_STATUS_CARDDETECT_PRESENT 0x0C +#define PCIC_IF_STATUS_BATTERY_MASK 0x03 +#define PCIC_IF_STATUS_BATTERY_DEAD1 0x00 +#define PCIC_IF_STATUS_BATTERY_DEAD2 0x01 +#define PCIC_IF_STATUS_BATTERY_WARNING 0x02 +#define PCIC_IF_STATUS_BATTERY_GOOD 0x03 + +#define PCIC_PWRCTL 0x02 /* RW */ +#define PCIC_PWRCTL_OE 0x80 /* output enable */ +#define PCIC_PWRCTL_DISABLE_RESETDRV 0x40 +#define PCIC_PWRCTL_AUTOSWITCH_ENABLE 0x20 +#define PCIC_PWRCTL_PWR_ENABLE 0x10 +#define PCIC_PWRCTL_VPP2_MASK 0x0C +/* XXX these are a little unclear from the data sheet */ +#define PCIC_PWRCTL_VPP2_RESERVED 0x0C +#define PCIC_PWRCTL_VPP2_EN1 0x08 +#define PCIC_PWRCTL_VPP2_EN0 0x04 +#define PCIC_PWRCTL_VPP2_ENX 0x00 +#define PCIC_PWRCTL_VPP1_MASK 0x03 +/* XXX these are a little unclear from the data sheet */ +#define PCIC_PWRCTL_VPP1_RESERVED 0x03 +#define PCIC_PWRCTL_VPP1_EN1 0x02 +#define PCIC_PWRCTL_VPP1_EN0 0x01 +#define PCIC_PWRCTL_VPP1_ENX 0x00 + +#define PCIC_CSC 0x04 /* RW */ +#define PCIC_CSC_ZERO 0xE0 +#define PCIC_CSC_GPI 0x10 +#define PCIC_CSC_CD 0x08 /* Card Detect Change */ +#define PCIC_CSC_READY 0x04 +#define PCIC_CSC_BATTWARN 0x02 +#define PCIC_CSC_BATTDEAD 0x01 /* for memory cards */ +#define PCIC_CSC_RI 0x01 /* for i/o cards */ + +#define PCIC_ADDRWIN_ENABLE 0x06 /* RW */ +#define PCIC_ADDRWIN_ENABLE_IO1 0x80 +#define PCIC_ADDRWIN_ENABLE_IO0 0x40 +#define PCIC_ADDRWIN_ENABLE_MEMCS16 0x20 /* rtfds if you care */ +#define PCIC_ADDRWIN_ENABLE_MEM4 0x10 +#define PCIC_ADDRWIN_ENABLE_MEM3 0x08 +#define PCIC_ADDRWIN_ENABLE_MEM2 0x04 +#define PCIC_ADDRWIN_ENABLE_MEM1 0x02 +#define PCIC_ADDRWIN_ENABLE_MEM0 0x01 + +#define PCIC_CARD_DETECT 0x16 /* RW */ +#define PCIC_CARD_DETECT_RESERVED 0xC0 +#define PCIC_CARD_DETECT_SW_INTR 0x20 +#define PCIC_CARD_DETECT_RESUME_ENABLE 0x10 +#define PCIC_CARD_DETECT_GPI_TRANSCTL 0x08 +#define PCIC_CARD_DETECT_GPI_ENABLE 0x04 +#define PCIC_CARD_DETECT_CFGRST_ENABLE 0x02 +#define PCIC_CARD_DETECT_MEMDLY_INHIBIT 0x01 + +/* interrupt registers */ + +#define PCIC_INTR 0x03 /* RW */ +#define PCIC_INTR_RI_ENABLE 0x80 +#define PCIC_INTR_RESET 0x40 /* active low (zero) */ +#define PCIC_INTR_CARDTYPE_MASK 0x20 +#define PCIC_INTR_CARDTYPE_IO 0x20 +#define PCIC_INTR_CARDTYPE_MEM 0x00 +#define PCIC_INTR_ENABLE 0x10 +#define PCIC_INTR_IRQ_MASK 0x0F +#define PCIC_INTR_IRQ_SHIFT 0 +#define PCIC_INTR_IRQ_NONE 0x00 +#define PCIC_INTR_IRQ_RESERVED1 0x01 +#define PCIC_INTR_IRQ_RESERVED2 0x02 +#define PCIC_INTR_IRQ3 0x03 +#define PCIC_INTR_IRQ4 0x04 +#define PCIC_INTR_IRQ5 0x05 +#define PCIC_INTR_IRQ_RESERVED6 0x06 +#define PCIC_INTR_IRQ7 0x07 +#define PCIC_INTR_IRQ_RESERVED8 0x08 +#define PCIC_INTR_IRQ9 0x09 +#define PCIC_INTR_IRQ10 0x0A +#define PCIC_INTR_IRQ11 0x0B +#define PCIC_INTR_IRQ12 0x0C +#define PCIC_INTR_IRQ_RESERVED13 0x0D +#define PCIC_INTR_IRQ14 0x0E +#define PCIC_INTR_IRQ15 0x0F + +#define PCIC_INTR_IRQ_VALIDMASK 0xDEB8 /* 1101 1110 1011 1000 */ + +#define PCIC_CSC_INTR 0x05 /* RW */ +#define PCIC_CSC_INTR_IRQ_MASK 0xF0 +#define PCIC_CSC_INTR_IRQ_SHIFT 4 +#define PCIC_CSC_INTR_IRQ_NONE 0x00 +#define PCIC_CSC_INTR_IRQ_RESERVED1 0x10 +#define PCIC_CSC_INTR_IRQ_RESERVED2 0x20 +#define PCIC_CSC_INTR_IRQ3 0x30 +#define PCIC_CSC_INTR_IRQ4 0x40 +#define PCIC_CSC_INTR_IRQ5 0x50 +#define PCIC_CSC_INTR_IRQ_RESERVED6 0x60 +#define PCIC_CSC_INTR_IRQ7 0x70 +#define PCIC_CSC_INTR_IRQ_RESERVED8 0x80 +#define PCIC_CSC_INTR_IRQ9 0x90 +#define PCIC_CSC_INTR_IRQ10 0xA0 +#define PCIC_CSC_INTR_IRQ11 0xB0 +#define PCIC_CSC_INTR_IRQ12 0xC0 +#define PCIC_CSC_INTR_IRQ_RESERVED13 0xD0 +#define PCIC_CSC_INTR_IRQ14 0xE0 +#define PCIC_CSC_INTR_IRQ15 0xF0 +#define PCIC_CSC_INTR_CD_ENABLE 0x08 +#define PCIC_CSC_INTR_READY_ENABLE 0x04 +#define PCIC_CSC_INTR_BATTWARN_ENABLE 0x02 +#define PCIC_CSC_INTR_BATTDEAD_ENABLE 0x01 /* for memory cards */ +#define PCIC_CSC_INTR_RI_ENABLE 0x01 /* for I/O cards */ + +#define PCIC_CSC_INTR_IRQ_VALIDMASK 0xDEB8 /* 1101 1110 1011 1000 */ + +/* I/O registers */ + +#define PCIC_IO_WINS 2 + +#define PCIC_IOCTL 0x07 /* RW */ +#define PCIC_IOCTL_IO1_WAITSTATE 0x80 +#define PCIC_IOCTL_IO1_ZEROWAIT 0x40 +#define PCIC_IOCTL_IO1_IOCS16SRC_MASK 0x20 +#define PCIC_IOCTL_IO1_IOCS16SRC_CARD 0x20 +#define PCIC_IOCTL_IO1_IOCS16SRC_DATASIZE 0x00 +#define PCIC_IOCTL_IO1_DATASIZE_MASK 0x10 +#define PCIC_IOCTL_IO1_DATASIZE_16BIT 0x10 +#define PCIC_IOCTL_IO1_DATASIZE_8BIT 0x00 +#define PCIC_IOCTL_IO0_WAITSTATE 0x08 +#define PCIC_IOCTL_IO0_ZEROWAIT 0x04 +#define PCIC_IOCTL_IO0_IOCS16SRC_MASK 0x02 +#define PCIC_IOCTL_IO0_IOCS16SRC_CARD 0x02 +#define PCIC_IOCTL_IO0_IOCS16SRC_DATASIZE 0x00 +#define PCIC_IOCTL_IO0_DATASIZE_MASK 0x01 +#define PCIC_IOCTL_IO0_DATASIZE_16BIT 0x01 +#define PCIC_IOCTL_IO0_DATASIZE_8BIT 0x00 + +#define PCIC_IOADDR0_START_LSB 0x08 +#define PCIC_IOADDR0_START_MSB 0x09 +#define PCIC_IOADDR0_STOP_LSB 0x0A +#define PCIC_IOADDR0_STOP_MSB 0x0B +#define PCIC_IOADDR1_START_LSB 0x0C +#define PCIC_IOADDR1_START_MSB 0x0D +#define PCIC_IOADDR1_STOP_LSB 0x0E +#define PCIC_IOADDR1_STOP_MSB 0x0F + +/* memory registers */ + +/* + * memory window addresses refer to bits A23-A12 of the ISA system memory + * address. This is a shift of 12 bits. The LSB contains A19-A12, and the + * MSB contains A23-A20, plus some other bits. + */ + +#define PCIC_MEM_WINS 5 + +#define PCIC_MEM_SHIFT 12 +#define PCIC_MEM_PAGESIZE (1< +#include + +#include + +struct proc; + +struct pcic_event { + SIMPLEQ_ENTRY(pcic_event) pe_q; + int pe_type; +}; + +/* pe_type */ +#define PCIC_EVENT_INSERTION 0 +#define PCIC_EVENT_REMOVAL 1 + +struct pcic_handle { + struct pcic_softc *sc; + int vendor; + int sock; + int flags; + int laststate; + int memalloc; + struct { + bus_addr_t addr; + bus_size_t size; + long offset; + int kind; + } mem[PCIC_MEM_WINS]; + int ioalloc; + struct { + bus_addr_t addr; + bus_size_t size; + int width; + } io[PCIC_IO_WINS]; + int ih_irq; + struct device *pccard; + + int shutdown; + struct proc *event_thread; + SIMPLEQ_HEAD(, pcic_event) events; +}; + +#define PCIC_FLAG_SOCKETP 0x0001 +#define PCIC_FLAG_CARDP 0x0002 + +#define PCIC_LASTSTATE_PRESENT 0x0002 +#define PCIC_LASTSTATE_HALF 0x0001 +#define PCIC_LASTSTATE_EMPTY 0x0000 + +#define C0SA PCIC_CHIP0_BASE+PCIC_SOCKETA_INDEX +#define C0SB PCIC_CHIP0_BASE+PCIC_SOCKETB_INDEX +#define C1SA PCIC_CHIP1_BASE+PCIC_SOCKETA_INDEX +#define C1SB PCIC_CHIP1_BASE+PCIC_SOCKETB_INDEX + +/* + * This is sort of arbitrary. It merely needs to be "enough". It can be + * overridden in the conf file, anyway. + */ + +#define PCIC_MEM_PAGES 4 +#define PCIC_MEMSIZE PCIC_MEM_PAGES*PCIC_MEM_PAGESIZE + +#define PCIC_NSLOTS 4 + +struct pcic_softc { + struct device dev; + + bus_space_tag_t memt; + bus_space_handle_t memh; + bus_space_tag_t iot; + bus_space_handle_t ioh; + + /* XXX isa_chipset_tag_t, pci_chipset_tag_t, etc. */ + void *intr_est; + + pccard_chipset_tag_t pct; + + /* this needs to be large enough to hold PCIC_MEM_PAGES bits */ + int subregionmask; +#define PCIC_MAX_MEM_PAGES (8 * sizeof(int)) + + /* used by memory window mapping functions */ + bus_addr_t membase; + + /* + * used by io window mapping functions. These can actually overlap + * with another pcic, since the underlying extent mapper will deal + * with individual allocations. This is here to deal with the fact + * that different busses have different real widths (different pc + * hardware seems to use 10 or 12 bits for the I/O bus). + */ + bus_addr_t iobase; + bus_addr_t iosize; + + int irq; + void *ih; + + struct pcic_handle handle[PCIC_NSLOTS]; +}; + + +int pcic_ident_ok __P((int)); +int pcic_vendor __P((struct pcic_handle *)); +char *pcic_vendor_to_string __P((int)); + +void pcic_attach __P((struct pcic_softc *)); +void pcic_attach_sockets __P((struct pcic_softc *)); +int pcic_intr __P((void *arg)); + +static __inline int pcic_read __P((struct pcic_handle *, int)); +static __inline void pcic_write __P((struct pcic_handle *, int, int)); + +int pcic_chip_mem_alloc __P((pccard_chipset_handle_t, bus_size_t, + struct pccard_mem_handle *)); +void pcic_chip_mem_free __P((pccard_chipset_handle_t, + struct pccard_mem_handle *)); +int pcic_chip_mem_map __P((pccard_chipset_handle_t, int, bus_addr_t, + bus_size_t, struct pccard_mem_handle *, bus_addr_t *, int *)); +void pcic_chip_mem_unmap __P((pccard_chipset_handle_t, int)); + +int pcic_chip_io_alloc __P((pccard_chipset_handle_t, bus_addr_t, + bus_size_t, bus_size_t, struct pccard_io_handle *)); +void pcic_chip_io_free __P((pccard_chipset_handle_t, + struct pccard_io_handle *)); +int pcic_chip_io_map __P((pccard_chipset_handle_t, int, bus_addr_t, + bus_size_t, struct pccard_io_handle *, int *)); +void pcic_chip_io_unmap __P((pccard_chipset_handle_t, int)); + +void pcic_chip_socket_enable __P((pccard_chipset_handle_t)); +void pcic_chip_socket_disable __P((pccard_chipset_handle_t)); + +static __inline int pcic_read __P((struct pcic_handle *, int)); +static __inline int +pcic_read(h, idx) + struct pcic_handle *h; + int idx; +{ + if (idx != -1) + bus_space_write_1(h->sc->iot, h->sc->ioh, PCIC_REG_INDEX, + h->sock + idx); + return (bus_space_read_1(h->sc->iot, h->sc->ioh, PCIC_REG_DATA)); +} + +static __inline void pcic_write __P((struct pcic_handle *, int, int)); +static __inline void +pcic_write(h, idx, data) + struct pcic_handle *h; + int idx; + int data; +{ + if (idx != -1) + bus_space_write_1(h->sc->iot, h->sc->ioh, PCIC_REG_INDEX, + h->sock + idx); + bus_space_write_1(h->sc->iot, h->sc->ioh, PCIC_REG_DATA, (data)); +} diff --git a/sys/i386/conf/NEWCARD b/sys/i386/conf/NEWCARD index 0bfcf2259cd..5ac33ee8d1f 100644 --- a/sys/i386/conf/NEWCARD +++ b/sys/i386/conf/NEWCARD @@ -146,8 +146,8 @@ device npx0 at nexus? port IO_NPX irq 13 device apm0 at nexus? disable flags 0x31 # Advanced Power Management # PCCARD (PCMCIA) support -controller pcicx0 at isa? -controller pcicx1 at isa? +controller pcic0 at isa? +controller pcic1 at isa? # controller pccbb0 # Serial (COM) ports