summaryrefslogtreecommitdiff
path: root/sys/arch/sparc
diff options
context:
space:
mode:
Diffstat (limited to 'sys/arch/sparc')
-rw-r--r--sys/arch/sparc/dev/ts102.c768
1 files changed, 768 insertions, 0 deletions
diff --git a/sys/arch/sparc/dev/ts102.c b/sys/arch/sparc/dev/ts102.c
new file mode 100644
index 00000000000..dfddc79ef96
--- /dev/null
+++ b/sys/arch/sparc/dev/ts102.c
@@ -0,0 +1,768 @@
+/* $OpenBSD: ts102.c,v 1.1 2003/06/23 09:29:55 miod Exp $ */
+/*
+ * Copyright (c) 2003, Miodrag Vallat.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR 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.
+ */
+
+/*
+ * Driver for the PCMCIA controller found in Tadpole SPARCbook 3 series
+ * notebooks.
+ *
+ * Based on the information provided in the SPARCbook 3 Technical Reference
+ * Manual (s3gxtrmb.pdf), chapter 7. A few ramblings against this document
+ * and/or the chip itself are scattered across this file.
+ *
+ * Implementation notes:
+ *
+ * - The TS102 exports its PCMCIA windows as SBus memory ranges: 64MB for
+ * the common memory window, and 16MB for the attribute and I/O windows.
+ *
+ * Mapping the whole windows would consume 192MB of address space, which
+ * is much more that what the iospace can offer.
+ *
+ * A best-effort solution would be to map the windows on demand. However,
+ * due to the wap mapdev() works, the va used for the mappings would be
+ * lost after unmapping (although using an extent to register iospace memory
+ * usage would fix this). So, instead, we will do a fixed mapping of a subset
+ * of each window upon attach - this is similar to what the stp4020 driver
+ * does.
+ *
+ * - IPL for the cards interrupt handles are not respected. See the stp4020
+ * driver source for comments about this.
+ *
+ * Endianness farce:
+ *
+ * - The documentation pretends that the endianness settings only affect the
+ * common memory window. Gee, thanks a lot. What about other windows, then?
+ * As a result, this driver runs with endianness conversions turned off.
+ *
+ * - One of the little-endian SBus and big-endian PCMCIA flags has the reverse
+ * meaning, actually. To achieve a ``no endianness conversion'' status,
+ * one has to be set and the other unset. It does not matter which one,
+ * though.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/kthread.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/queue.h>
+
+#include <machine/bus.h>
+
+#include <uvm/uvm_extern.h>
+
+#include <dev/pcmcia/pcmciareg.h>
+#include <dev/pcmcia/pcmciavar.h>
+#include <dev/pcmcia/pcmciachip.h>
+
+#include <sparc/dev/sbusvar.h>
+#include <sparc/dev/ts102reg.h>
+
+#define TS102_NUM_SLOTS 2
+
+/*
+ * Memory ranges
+ */
+#define TS102_RANGE_COMMON 0
+#define TS102_RANGE_ATTR 1
+#define TS102_RANGE_IO 2
+
+#define TS102_RANGE_CNT 3
+#define TS102_NUM_RANGES (TS102_RANGE_CNT * TS102_NUM_SLOTS)
+
+#define TS102_ARBITRARY_MAP_SIZE (1 * 1024 * 1024)
+
+struct tslot_softc;
+
+/*
+ * Slot event structure
+ */
+struct tslot_event {
+ SIMPLEQ_ENTRY(tslot_event) te_q;
+ int te_what;
+ int te_slot;
+};
+
+#define TSLOT_EVENT_INSERT 0
+#define TSLOT_EVENT_REMOVE 1
+
+const char *tslot_event_descr[] = {
+ "insertion",
+ "removal"
+};
+
+/*
+ * Per-slot data
+ */
+struct tslot_data {
+ struct tslot_softc *td_parent;
+ struct device *td_pcmcia;
+
+ volatile u_int8_t *td_regs;
+ vaddr_t td_space[TS102_RANGE_CNT];
+
+ /* Interrupt handler */
+ int (*td_intr)(void *);
+ void *td_intrarg;
+
+ /* Socket status */
+ int td_slot;
+ int td_status;
+#define TS_UNKNOWN -1
+#define TS_EMPTY 0
+#define TS_CARD 1
+};
+
+struct tslot_softc {
+ struct device sc_dev;
+ struct sbusdev sc_sd;
+
+ struct intrhand sc_ih;
+
+ pcmcia_chipset_tag_t sc_pct;
+
+ struct proc *sc_thread; /* event thread */
+ SIMPLEQ_HEAD(, tslot_event) sc_events;
+
+ struct tslot_data sc_slot[TS102_NUM_SLOTS];
+};
+
+void tslot_attach(struct device *, struct device *, void *);
+void tslot_create_event_thread(void *);
+void tslot_event_thread(void *);
+int tslot_intr(void *);
+void tslot_intr_disestablish(pcmcia_chipset_handle_t, void *);
+void *tslot_intr_establish(pcmcia_chipset_handle_t, struct pcmcia_function *,
+ int, int (*)(void *), void *, char *);
+int tslot_io_alloc(pcmcia_chipset_handle_t, bus_addr_t, bus_size_t,
+ bus_size_t, struct pcmcia_io_handle *);
+void tslot_io_free(pcmcia_chipset_handle_t, struct pcmcia_io_handle *);
+int tslot_io_map(pcmcia_chipset_handle_t, int, bus_addr_t, bus_size_t,
+ struct pcmcia_io_handle *, int *);
+void tslot_io_unmap(pcmcia_chipset_handle_t, int);
+int tslot_match(struct device *, void *, void *);
+int tslot_mem_alloc(pcmcia_chipset_handle_t, bus_size_t,
+ struct pcmcia_mem_handle *);
+void tslot_mem_free(pcmcia_chipset_handle_t, struct pcmcia_mem_handle *);
+int tslot_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t, bus_size_t,
+ struct pcmcia_mem_handle *, bus_addr_t *, int *);
+void tslot_mem_unmap(pcmcia_chipset_handle_t, int);
+int tslot_print(void *, const char *);
+int tslot_queue_event(struct tslot_softc *, int, int);
+void tslot_reset(struct tslot_data *, u_int32_t);
+void tslot_slot_disable(pcmcia_chipset_handle_t);
+void tslot_slot_enable(pcmcia_chipset_handle_t);
+void tslot_slot_intr(struct tslot_data *, int);
+
+struct cfattach tslot_ca = {
+ sizeof(struct tslot_softc), tslot_match, tslot_attach
+};
+
+struct cfdriver tslot_cd = {
+ NULL, "tslot", DV_DULL
+};
+
+/*
+ * PCMCIA chipset methods
+ */
+struct pcmcia_chip_functions tslot_functions = {
+ tslot_mem_alloc,
+ tslot_mem_free,
+ tslot_mem_map,
+ tslot_mem_unmap,
+
+ tslot_io_alloc,
+ tslot_io_free,
+ tslot_io_map,
+ tslot_io_unmap,
+
+ tslot_intr_establish,
+ tslot_intr_disestablish,
+
+ tslot_slot_enable,
+ tslot_slot_disable
+};
+
+#define TSLOT_READ(slot, offset) \
+ *(volatile u_int16_t *)((slot)->td_regs + (offset))
+#define TSLOT_WRITE(slot, offset, value) \
+ *(volatile u_int16_t *)((slot)->td_regs + (offset)) = (value)
+
+/*
+ * Attachment and initialization
+ */
+
+int
+tslot_match(struct device *parent, void *vcf, void *aux)
+{
+ struct confargs *ca = aux;
+
+ return (strcmp("ts102", ca->ca_ra.ra_name) == 0);
+}
+
+void
+tslot_attach(struct device *parent, struct device *self, void *args)
+{
+ struct confargs *ca = args;
+ struct tslot_softc *sc = (struct tslot_softc *)self;
+ struct romaux *ra;
+ struct rom_reg reg;
+ struct rom_range ranges[TS102_NUM_RANGES], *range;
+ struct tslot_data *td;
+ volatile u_int8_t *regs;
+ int node, nranges, slot, rnum;
+
+ ra = &ca->ca_ra;
+ node = ra->ra_node;
+ regs = mapiodev(&ra->ra_reg[0], 0, ra->ra_len);
+
+ /*
+ * Find memory ranges
+ */
+ nranges = getproplen(node, "ranges") / sizeof(struct rom_range);
+ if (nranges < TS102_NUM_RANGES) {
+ printf(": expected %d memory ranges, got %d\n",
+ TS102_NUM_RANGES, nranges);
+ return;
+ }
+ getprop(node, "ranges", ranges, sizeof ranges);
+
+ /*
+ * Ranges being relative to this sbus slot, turn them into absolute
+ * addresses.
+ */
+ for (rnum = 0; rnum < TS102_NUM_RANGES; rnum++) {
+ ranges[rnum].poffset -= TS102_OFFSET_REGISTERS;
+ }
+
+ sc->sc_ih.ih_fun = tslot_intr;
+ sc->sc_ih.ih_arg = sc;
+ intr_establish(ra->ra_intr[0].int_pri, &sc->sc_ih, -1);
+ printf(" pri %d", ra->ra_intr[0].int_pri);
+
+ sbus_establish(&sc->sc_sd, self);
+
+ printf(": %d slots\n", TS102_NUM_SLOTS);
+
+ /*
+ * Setup asynchronous event handler
+ */
+ SIMPLEQ_INIT(&sc->sc_events);
+ kthread_create_deferred(tslot_create_event_thread, sc);
+
+ sc->sc_pct = (pcmcia_chipset_tag_t)&tslot_functions;
+
+ /*
+ * Setup slots
+ */
+ for (slot = 0; slot < TS102_NUM_SLOTS; slot++) {
+ td = &sc->sc_slot[slot];
+ for (rnum = 0; rnum < TS102_RANGE_CNT; rnum++) {
+ range = ranges + (slot * TS102_RANGE_CNT + rnum);
+ reg = ra->ra_reg[0];
+ reg.rr_iospace = range->pspace;
+ reg.rr_paddr = (void *)
+ ((u_int32_t)reg.rr_paddr + range->poffset);
+ td->td_space[rnum] = (vaddr_t)mapiodev(&reg, 0,
+ TS102_ARBITRARY_MAP_SIZE);
+ }
+ td->td_parent = sc;
+ td->td_regs = regs +
+ slot * (TS102_REG_CARD_B_INT - TS102_REG_CARD_A_INT);
+ td->td_slot = slot;
+ tslot_reset(td, TS102_ARBITRARY_MAP_SIZE);
+ }
+}
+
+void
+tslot_reset(struct tslot_data *td, u_int32_t iosize)
+{
+ struct pcmciabus_attach_args paa;
+ int ctl;
+
+ paa.paa_busname = "pcmcia";
+ paa.pct = (pcmcia_chipset_tag_t)td->td_parent->sc_pct;
+ paa.pch = (pcmcia_chipset_handle_t)td;
+ paa.iobase = 0;
+ paa.iosize = iosize;
+
+ td->td_pcmcia = config_found(&td->td_parent->sc_dev, &paa, tslot_print);
+
+ if (td->td_pcmcia == NULL) {
+ /*
+ * If no pcmcia attachment, power down the slot.
+ */
+ tslot_slot_disable((pcmcia_chipset_handle_t)td);
+ return;
+ }
+
+ /*
+ * Reset slot and initialize it
+ */
+
+ TSLOT_WRITE(td, TS102_REG_CARD_A_INT, TS102_CARD_INT_SOFT_RESET);
+
+ ctl = TSLOT_READ(td, TS102_REG_CARD_A_CTL);
+ /* force low addresses */
+ ctl &= ~(TS102_CARD_CTL_AA_MASK | TS102_CARD_CTL_IA_MASK);
+ /* Put SBus and PCMCIA in their respective endian mode */
+ ctl |= TS102_CARD_CTL_SBLE; /* this is not what it looks like! */
+ ctl &= ~TS102_CARD_CTL_PCMBE;
+ /* disable read ahead and address increment */
+ ctl &= ~TS102_CARD_CTL_RAHD;
+ ctl &= ~TS102_CARD_CTL_INCDIS;
+ /* power on */
+ ctl &= ~TS102_CARD_CTL_PWRD;
+ TSLOT_WRITE(td, TS102_REG_CARD_A_CTL, ctl);
+
+ /*
+ * Enable interrupt upon insertion/removal
+ */
+
+ TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
+ TS102_CARD_INT_MASK_CARDDETECT_STATUS);
+
+ /*
+ * Force immediate probe - this will depend on the worker
+ * thread, so will not really happen until interrupts are enabled,
+ * which is exactly what we need.
+ */
+ td->td_status = TS_UNKNOWN;
+ tslot_slot_intr(td, TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED);
+}
+
+/* XXX there ought to be a common function for this... */
+int
+tslot_print(void *aux, const char *description)
+{
+ struct pcmciabus_attach_args *paa = aux;
+ struct tslot_data *td = (struct tslot_data *)paa->pch;
+
+ printf(" socket %d", td->td_slot);
+ return (UNCONF);
+}
+
+/*
+ * PCMCIA Helpers
+ */
+
+int
+tslot_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start, bus_size_t size,
+ bus_size_t align, struct pcmcia_io_handle *pih)
+{
+#ifdef TSLOT_DEBUG
+ printf("[io alloc %x]", size);
+#endif
+
+ pih->iot = 0;
+ pih->ioh = 0;
+ pih->addr = start;
+ pih->size = size;
+ pih->flags = 0;
+
+ return (0);
+}
+
+void
+tslot_io_free(pcmcia_chipset_handle_t pch, struct pcmcia_io_handle *pih)
+{
+#ifdef TSLOT_DEBUG
+ printf("[io free]");
+#endif
+}
+
+int
+tslot_io_map(pcmcia_chipset_handle_t pch, int width, bus_addr_t offset,
+ bus_size_t size, struct pcmcia_io_handle *pih, int *windowp)
+{
+ struct tslot_data *td = (struct tslot_data *)pch;
+
+#ifdef TSLOT_DEBUG
+ printf("[io map %x-%x", offset, size);
+#endif
+
+ pih->iot = 0;
+ pih->ioh = (bus_space_handle_t)(td->td_space[TS102_RANGE_IO]);
+ *windowp = TS102_RANGE_IO;
+
+#ifdef TSLOT_DEBUG
+ printf("->%p/%x]", pih->ioh, size);
+#endif
+
+ return (0);
+}
+
+void
+tslot_io_unmap(pcmcia_chipset_handle_t pch, int win)
+{
+#ifdef TSLOT_DEBUG
+ printf("[io unmap]");
+#endif
+}
+
+int
+tslot_mem_alloc(pcmcia_chipset_handle_t pch, bus_size_t size,
+ struct pcmcia_mem_handle *pmh)
+{
+#ifdef TSLOT_DEBUG
+ printf("[mem alloc %x]", size);
+#endif
+ pmh->memt = 0;
+ pmh->size = round_page(size);
+ pmh->addr = 0;
+ pmh->mhandle = 0;
+ pmh->realsize = 0; /* nothing so far! */
+
+ return (0);
+}
+
+void
+tslot_mem_free(pcmcia_chipset_handle_t pch, struct pcmcia_mem_handle *pmh)
+{
+#ifdef TSLOT_DEBUG
+ printf("[mem free]");
+#endif
+}
+
+int
+tslot_mem_map(pcmcia_chipset_handle_t pch, int kind, bus_addr_t addr,
+ bus_size_t size, struct pcmcia_mem_handle *pmh, bus_addr_t *offsetp,
+ int *windowp)
+{
+ struct tslot_data *td = (struct tslot_data *)pch;
+ int slot;
+
+ slot = kind & PCMCIA_MEM_ATTR ? TS102_RANGE_ATTR : TS102_RANGE_COMMON;
+#ifdef TSLOT_DEBUG
+ printf("[mem map %d %x-%x", slot, addr, size);
+#endif
+
+ addr += pmh->addr;
+
+ pmh->memt = 0;
+ pmh->memh = (bus_space_handle_t)(td->td_space[slot] + addr);
+ pmh->realsize = TS102_ARBITRARY_MAP_SIZE - addr;
+ *offsetp = 0;
+ *windowp = slot;
+
+#ifdef TSLOT_DEBUG
+ printf("->%p/%x]", pmh->memh, size);
+#endif
+
+ return (0);
+}
+
+void
+tslot_mem_unmap(pcmcia_chipset_handle_t pch, int win)
+{
+#ifdef TSLOT_DEBUG
+ printf("[mem unmap %d]", win);
+#endif
+}
+
+void
+tslot_slot_disable(pcmcia_chipset_handle_t pch)
+{
+ struct tslot_data *td = (struct tslot_data *)pch;
+#ifdef TSLOT_DEBUG
+ printf("%s: disable slot %d\n",
+ td->td_parent->sc_dev.dv_xname, td->td_slot);
+#endif
+
+ /*
+ * Disable card access.
+ */
+ TSLOT_WRITE(td, TS102_REG_CARD_A_STS,
+ TSLOT_READ(td, TS102_REG_CARD_A_STS) & ~TS102_CARD_STS_ACEN);
+
+ /*
+ * Disable interrupts, except for insertion.
+ */
+ TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
+ TS102_CARD_INT_MASK_CARDDETECT_STATUS);
+}
+
+void
+tslot_slot_enable(pcmcia_chipset_handle_t pch)
+{
+ struct tslot_data *td = (struct tslot_data *)pch;
+ int status, i;
+#ifdef TSLOT_DEBUG
+ printf("%s: enable slot %d\n",
+ td->td_parent->sc_dev.dv_xname, td->td_slot);
+#endif
+
+ /*
+ * Power on the card if not already done, and enable card access
+ */
+ status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
+ status |= TS102_CARD_STS_ACEN;
+ status &= ~TS102_CARD_STS_VCCEN;
+ TSLOT_WRITE(td, TS102_REG_CARD_A_STS, status);
+ DELAY(200 * 1000);
+
+ /*
+ * Wait until the card is unbusy. If it is still busy after 3 seconds,
+ * give up. We could enable card interrupts and wait for the interrupt
+ * to happen when BUSY is released, but the interrupt could also be
+ * triggered by the card itself if it's an I/O card, so better poll
+ * here.
+ */
+ for (i = 30000; i != 0; i--) {
+ if (TSLOT_READ(td, TS102_REG_CARD_A_STS) & TS102_CARD_STS_RDY)
+ break;
+ else
+ DELAY(100);
+ }
+
+ if (i == 0) {
+ printf("%s: slot %d still busy after 3 seconds, status 0x%x\n",
+ td->td_parent->sc_dev.dv_xname, td->td_slot,
+ TSLOT_READ(td, TS102_REG_CARD_A_STS));
+ return;
+ }
+
+ /*
+ * Enable the card interrupts if this is an I/O card.
+ * Note that the TS102_CARD_STS_IO bit in the status register will
+ * never get set, despite what the documentation says!
+ */
+ if (pcmcia_card_gettype(td->td_pcmcia) == PCMCIA_IFTYPE_IO) {
+ TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
+ TS102_CARD_INT_MASK_CARDDETECT_STATUS |
+ TS102_CARD_INT_MASK_IRQ);
+ }
+}
+
+/*
+ * Event management
+ */
+void
+tslot_create_event_thread(void *v)
+{
+ struct tslot_softc *sc = v;
+ const char *name = sc->sc_dev.dv_xname;
+
+ if (kthread_create(tslot_event_thread, sc, &sc->sc_thread, "%s",
+ name) != 0) {
+ panic("%s: unable to create event kthread", name);
+ }
+}
+
+void
+tslot_event_thread(void *v)
+{
+ struct tslot_softc *sc = v;
+ struct tslot_data *td;
+ struct tslot_event *te;
+ int s;
+
+ for (;;) {
+ s = splhigh();
+
+ if ((te = SIMPLEQ_FIRST(&sc->sc_events)) == NULL) {
+ splx(s);
+ tsleep(&sc->sc_events, PWAIT, "tslot_event", 0);
+ continue;
+ }
+
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_events, te, te_q);
+ splx(s);
+
+ if (te->te_slot >= TS102_NUM_SLOTS) {
+ printf("%s: invalid slot number %d\n",
+ sc->sc_dev.dv_xname, te->te_slot);
+ } else {
+ td = &sc->sc_slot[te->te_slot];
+ switch (te->te_what) {
+ case TSLOT_EVENT_INSERT:
+ pcmcia_card_attach(td->td_pcmcia);
+ break;
+ case TSLOT_EVENT_REMOVE:
+ pcmcia_card_detach(td->td_pcmcia, DETACH_FORCE);
+ break;
+ default:
+ printf("%s: invalid event type %d on slot %d\n",
+ sc->sc_dev.dv_xname,
+ te->te_slot, te->te_what);
+ }
+ }
+ free(te, M_TEMP);
+ }
+}
+
+/*
+ * Interrupt handling
+ */
+
+int
+tslot_intr(void *v)
+{
+ struct tslot_softc *sc = v;
+ struct tslot_data *td;
+ int intregs[TS102_NUM_SLOTS], *intreg;
+ int i, rc = 0;
+
+ /*
+ * Scan slots, and acknowledge the interrupt if necessary first
+ */
+ for (i = 0; i < TS102_NUM_SLOTS; i++) {
+ td = &sc->sc_slot[i];
+ intreg = &intregs[i];
+ *intreg = TSLOT_READ(td, TS102_REG_CARD_A_INT);
+
+ /*
+ * Acknowledge all interrupt situations at once, even if they
+ * did not occur.
+ */
+ if ((*intreg & (TS102_CARD_INT_STATUS_IRQ |
+ TS102_CARD_INT_STATUS_WP_STATUS_CHANGED |
+ TS102_CARD_INT_STATUS_BATTERY_STATUS_CHANGED |
+ TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED)) != 0) {
+ rc = 1;
+ TSLOT_WRITE(td, TS102_REG_CARD_A_INT, *intreg |
+ TS102_CARD_INT_RQST_IRQ |
+ TS102_CARD_INT_RQST_WP_STATUS_CHANGED |
+ TS102_CARD_INT_RQST_BATTERY_STATUS_CHANGED |
+ TS102_CARD_INT_RQST_CARDDETECT_STATUS_CHANGED);
+ }
+ }
+
+ /*
+ * Invoke the interrupt handler for each slot
+ */
+ for (i = 0; i < TS102_NUM_SLOTS; i++) {
+ td = &sc->sc_slot[i];
+ intreg = &intregs[i];
+
+ if ((*intreg & (TS102_CARD_INT_STATUS_IRQ |
+ TS102_CARD_INT_STATUS_WP_STATUS_CHANGED |
+ TS102_CARD_INT_STATUS_BATTERY_STATUS_CHANGED |
+ TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED)) != 0)
+ tslot_slot_intr(td, *intreg);
+ }
+
+ return (rc);
+}
+
+int
+tslot_queue_event(struct tslot_softc *sc, int slot, int what)
+{
+ struct tslot_event *te;
+ int s;
+
+ te = malloc(sizeof(*te), M_TEMP, M_NOWAIT);
+ if (te == NULL) {
+ printf("%s: %s event lost on slot %d\n",
+ sc->sc_dev.dv_xname, tslot_event_descr[what], slot);
+ return (ENOMEM);
+ }
+
+ te->te_what = what;
+ te->te_slot = slot;
+ s = splhigh();
+ SIMPLEQ_INSERT_TAIL(&sc->sc_events, te, te_q);
+ splx(s);
+ wakeup(&sc->sc_events);
+
+ return (0);
+}
+
+void
+tslot_slot_intr(struct tslot_data *td, int intreg)
+{
+ int status, sockstat;
+
+ status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
+#ifdef TSLOT_DEBUG
+ printf("%s: interrupt on socket %d ir %x sts %x\n",
+ td->td_parent->sc_dev.dv_xname, td->td_slot, intreg, status);
+#endif
+
+ sockstat = td->td_status;
+
+ if (intreg & TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED) {
+ if (status & TS102_CARD_STS_PRES) {
+ if (sockstat != TS_CARD) {
+ if (tslot_queue_event(td->td_parent,
+ td->td_slot, TSLOT_EVENT_INSERT) == 0)
+ td->td_status = TS_CARD;
+ }
+ } else {
+ if (sockstat != TS_EMPTY &&
+ tslot_queue_event(td->td_parent, td->td_slot,
+ TSLOT_EVENT_REMOVE) == 0)
+ td->td_status = TS_EMPTY;
+ }
+#ifdef TSLOT_DEBUG
+ printf("%s: slot %d status changed from %d to %d\n",
+ td->td_parent->sc_dev.dv_xname,
+ td->td_slot, sockstat, td->td_status);
+#endif
+ /*
+ * Ignore extra interrupt bits, they are part of the change.
+ */
+ return;
+ }
+
+ if (intreg & TS102_CARD_INT_STATUS_IRQ) {
+ if (sockstat != TS_CARD) {
+ printf("%s: spurious interrupt on slot %d\n",
+ td->td_parent->sc_dev.dv_xname, td->td_slot);
+ return;
+ }
+
+ if (td->td_intr != NULL) {
+ /*
+ * XXX There is no way to honour the interrupt handler
+ * requested IPL level...
+ */
+ (*td->td_intr)(td->td_intrarg);
+ }
+ }
+}
+
+void
+tslot_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih)
+{
+ struct tslot_data *td = (struct tslot_data *)pch;
+
+ td->td_intr = NULL;
+ td->td_intrarg = NULL;
+}
+
+void *
+tslot_intr_establish(pcmcia_chipset_handle_t pch, struct pcmcia_function *pf,
+ int ipl, int (*handler)(void *), void *arg, char *xname)
+{
+ struct tslot_data *td = (struct tslot_data *)pch;
+
+ td->td_intr = handler;
+ td->td_intrarg = arg;
+
+ return (td);
+}