summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiod Vallat <miod@cvs.openbsd.org>2003-06-23 09:29:56 +0000
committerMiod Vallat <miod@cvs.openbsd.org>2003-06-23 09:29:56 +0000
commit76a938656b0398c0bbd3db3259c0c8c3aa754145 (patch)
tree3fcad42d8c8b7d01ead39a856c9c8071b3949acd
parent506250333c76efa91fdf1b8cc6689e2a27b5274e (diff)
Driver for the pcmcia slots in the Tadpole SPARCbook machines.
Currently only tested with wi(4), and there are still some sporadic "device timeout" issues left - your mileage may vary.
-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);
+}