Annotation of sys/arch/sparc/dev/ts102.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: ts102.c,v 1.18 2006/08/11 18:57:35 miod Exp $ */
! 2: /*
! 3: * Copyright (c) 2003, 2004, Miodrag Vallat.
! 4: *
! 5: * Redistribution and use in source and binary forms, with or without
! 6: * modification, are permitted provided that the following conditions
! 7: * are met:
! 8: * 1. Redistributions of source code must retain the above copyright
! 9: * notice, this list of conditions and the following disclaimer.
! 10: * 2. Redistributions in binary form must reproduce the above copyright
! 11: * notice, this list of conditions and the following disclaimer in the
! 12: * documentation and/or other materials provided with the distribution.
! 13: *
! 14: * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
! 15: * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
! 16: * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
! 17: * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
! 18: * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
! 19: * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
! 20: * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
! 21: * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
! 22: * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
! 23: * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
! 24: * POSSIBILITY OF SUCH DAMAGE.
! 25: */
! 26:
! 27: /*
! 28: * Driver for the PCMCIA controller found in Tadpole SPARCbook 3 series
! 29: * notebooks.
! 30: *
! 31: * Based on the information provided in the SPARCbook 3 Technical Reference
! 32: * Manual (s3gxtrmb.pdf), chapter 7. A few ramblings against this document
! 33: * and/or the chip itself are scattered across this file.
! 34: *
! 35: * Implementation notes:
! 36: *
! 37: * - The TS102 exports its PCMCIA windows as SBus memory ranges: 64MB for
! 38: * the common memory window, and 16MB for the attribute and I/O windows.
! 39: *
! 40: * Mapping the whole windows would consume 192MB of address space, which
! 41: * is much more that what the iospace can offer.
! 42: *
! 43: * A best-effort solution would be to map the windows on demand. However,
! 44: * due to the way mapdev() works, the va used for the mappings would be
! 45: * lost after unmapping (although using an extent to register iospace memory
! 46: * usage would fix this). So, instead, we will do a fixed mapping of a subset
! 47: * of each window upon attach - this is similar to what the stp4020 driver
! 48: * does.
! 49: *
! 50: * - IPL for the cards interrupt handlers are not respected. See the stp4020
! 51: * driver source for comments about this.
! 52: *
! 53: * Endianness farce:
! 54: *
! 55: * - The documentation pretends that the endianness settings only affect the
! 56: * common memory window. Gee, thanks a lot. What about other windows, then?
! 57: * As a result, this driver runs with endianness conversions turned off.
! 58: *
! 59: * - One of the little-endian SBus and big-endian PCMCIA flags has the reverse
! 60: * meaning, actually. To achieve a ``no endianness conversion'' status,
! 61: * one has to be set and the other unset. It does not matter which one,
! 62: * though.
! 63: */
! 64:
! 65: #include <sys/param.h>
! 66: #include <sys/systm.h>
! 67: #include <sys/conf.h>
! 68: #include <sys/device.h>
! 69: #include <sys/kthread.h>
! 70: #include <sys/proc.h>
! 71: #include <sys/queue.h>
! 72:
! 73: #include <machine/bus.h>
! 74:
! 75: #include <uvm/uvm_extern.h>
! 76:
! 77: #include <dev/pcmcia/pcmciareg.h>
! 78: #include <dev/pcmcia/pcmciavar.h>
! 79: #include <dev/pcmcia/pcmciachip.h>
! 80:
! 81: #include <sparc/dev/sbusvar.h>
! 82: #include <sparc/dev/tctrlvar.h>
! 83: #include <sparc/dev/ts102reg.h>
! 84:
! 85: #define TS102_NUM_SLOTS 2
! 86:
! 87: /*
! 88: * Memory ranges
! 89: */
! 90: #define TS102_RANGE_COMMON 0
! 91: #define TS102_RANGE_ATTR 1
! 92: #define TS102_RANGE_IO 2
! 93:
! 94: #define TS102_RANGE_CNT 3
! 95: #define TS102_NUM_RANGES (TS102_RANGE_CNT * TS102_NUM_SLOTS)
! 96:
! 97: #define TS102_ARBITRARY_MAP_SIZE (1 * 1024 * 1024)
! 98:
! 99: struct tslot_softc;
! 100:
! 101: /*
! 102: * Per-slot data
! 103: */
! 104: struct tslot_data {
! 105: struct tslot_softc *td_parent;
! 106: struct device *td_pcmcia;
! 107:
! 108: volatile u_int8_t *td_regs;
! 109: struct rom_reg td_rr;
! 110: vaddr_t td_space[TS102_RANGE_CNT];
! 111:
! 112: /* Interrupt handler */
! 113: int (*td_intr)(void *);
! 114: void *td_intrarg;
! 115:
! 116: /* Socket status */
! 117: int td_slot;
! 118: int td_status;
! 119: #define TS_CARD 0x0001
! 120: };
! 121:
! 122: struct tslot_softc {
! 123: struct device sc_dev;
! 124:
! 125: struct intrhand sc_ih;
! 126:
! 127: pcmcia_chipset_tag_t sc_pct;
! 128:
! 129: struct proc *sc_thread; /* event thread */
! 130: unsigned int sc_events; /* sockets with pending events */
! 131:
! 132: struct tslot_data sc_slot[TS102_NUM_SLOTS];
! 133: };
! 134:
! 135: void tslot_attach(struct device *, struct device *, void *);
! 136: void tslot_create_event_thread(void *);
! 137: void tslot_event_thread(void *);
! 138: int tslot_intr(void *);
! 139: void tslot_intr_disestablish(pcmcia_chipset_handle_t, void *);
! 140: void *tslot_intr_establish(pcmcia_chipset_handle_t, struct pcmcia_function *,
! 141: int, int (*)(void *), void *, char *);
! 142: const char *tslot_intr_string(pcmcia_chipset_handle_t, void *);
! 143: int tslot_io_alloc(pcmcia_chipset_handle_t, bus_addr_t, bus_size_t,
! 144: bus_size_t, struct pcmcia_io_handle *);
! 145: void tslot_io_free(pcmcia_chipset_handle_t, struct pcmcia_io_handle *);
! 146: int tslot_io_map(pcmcia_chipset_handle_t, int, bus_addr_t, bus_size_t,
! 147: struct pcmcia_io_handle *, int *);
! 148: void tslot_io_unmap(pcmcia_chipset_handle_t, int);
! 149: int tslot_match(struct device *, void *, void *);
! 150: int tslot_mem_alloc(pcmcia_chipset_handle_t, bus_size_t,
! 151: struct pcmcia_mem_handle *);
! 152: void tslot_mem_free(pcmcia_chipset_handle_t, struct pcmcia_mem_handle *);
! 153: int tslot_mem_map(pcmcia_chipset_handle_t, int, bus_addr_t, bus_size_t,
! 154: struct pcmcia_mem_handle *, bus_size_t *, int *);
! 155: void tslot_mem_unmap(pcmcia_chipset_handle_t, int);
! 156: int tslot_print(void *, const char *);
! 157: void tslot_queue_event(struct tslot_softc *, int);
! 158: void tslot_reset(struct tslot_data *, u_int32_t);
! 159: void tslot_slot_disable(pcmcia_chipset_handle_t);
! 160: void tslot_slot_enable(pcmcia_chipset_handle_t);
! 161: void tslot_slot_intr(struct tslot_data *, int);
! 162:
! 163: struct cfattach tslot_ca = {
! 164: sizeof(struct tslot_softc), tslot_match, tslot_attach
! 165: };
! 166:
! 167: struct cfdriver tslot_cd = {
! 168: NULL, "tslot", DV_DULL
! 169: };
! 170:
! 171: /*
! 172: * PCMCIA chipset methods
! 173: */
! 174: struct pcmcia_chip_functions tslot_functions = {
! 175: tslot_mem_alloc,
! 176: tslot_mem_free,
! 177: tslot_mem_map,
! 178: tslot_mem_unmap,
! 179:
! 180: tslot_io_alloc,
! 181: tslot_io_free,
! 182: tslot_io_map,
! 183: tslot_io_unmap,
! 184:
! 185: tslot_intr_establish,
! 186: tslot_intr_disestablish,
! 187: tslot_intr_string,
! 188:
! 189: tslot_slot_enable,
! 190: tslot_slot_disable
! 191: };
! 192:
! 193: #define TSLOT_READ(slot, offset) \
! 194: *(volatile u_int16_t *)((slot)->td_regs + (offset))
! 195: #define TSLOT_WRITE(slot, offset, value) \
! 196: *(volatile u_int16_t *)((slot)->td_regs + (offset)) = (value)
! 197:
! 198: /*
! 199: * Attachment and initialization
! 200: */
! 201:
! 202: int
! 203: tslot_match(struct device *parent, void *vcf, void *aux)
! 204: {
! 205: struct confargs *ca = aux;
! 206:
! 207: return (strcmp("ts102", ca->ca_ra.ra_name) == 0);
! 208: }
! 209:
! 210: void
! 211: tslot_attach(struct device *parent, struct device *self, void *args)
! 212: {
! 213: struct confargs *ca = args;
! 214: struct tslot_softc *sc = (struct tslot_softc *)self;
! 215: struct romaux *ra;
! 216: struct rom_range ranges[TS102_NUM_RANGES], *range;
! 217: struct tslot_data *td;
! 218: volatile u_int8_t *regs;
! 219: int node, nranges, slot, rnum;
! 220:
! 221: ra = &ca->ca_ra;
! 222: node = ra->ra_node;
! 223: regs = mapiodev(&ra->ra_reg[0], 0, ra->ra_len);
! 224:
! 225: /*
! 226: * Find memory ranges
! 227: */
! 228: nranges = getproplen(node, "ranges") / sizeof(struct rom_range);
! 229: if (nranges < TS102_NUM_RANGES) {
! 230: printf(": expected %d memory ranges, got %d\n",
! 231: TS102_NUM_RANGES, nranges);
! 232: return;
! 233: }
! 234: getprop(node, "ranges", ranges, sizeof ranges);
! 235:
! 236: /*
! 237: * Ranges being relative to this sbus slot, turn them into absolute
! 238: * addresses.
! 239: */
! 240: for (rnum = 0; rnum < TS102_NUM_RANGES; rnum++) {
! 241: ranges[rnum].poffset -= TS102_OFFSET_REGISTERS;
! 242: }
! 243:
! 244: sc->sc_ih.ih_fun = tslot_intr;
! 245: sc->sc_ih.ih_arg = sc;
! 246: intr_establish(ra->ra_intr[0].int_pri, &sc->sc_ih, -1, self->dv_xname);
! 247: printf(" pri %d", ra->ra_intr[0].int_pri);
! 248:
! 249: printf(": %d slots\n", TS102_NUM_SLOTS);
! 250:
! 251: /*
! 252: * Setup asynchronous event handler
! 253: */
! 254: sc->sc_events = 0;
! 255: kthread_create_deferred(tslot_create_event_thread, sc);
! 256:
! 257: sc->sc_pct = (pcmcia_chipset_tag_t)&tslot_functions;
! 258:
! 259: /*
! 260: * Setup slots
! 261: */
! 262: for (slot = 0; slot < TS102_NUM_SLOTS; slot++) {
! 263: td = &sc->sc_slot[slot];
! 264: for (rnum = 0; rnum < TS102_RANGE_CNT; rnum++) {
! 265: range = ranges + (slot * TS102_RANGE_CNT + rnum);
! 266: td->td_rr = ra->ra_reg[0];
! 267: td->td_rr.rr_iospace = range->pspace;
! 268: td->td_rr.rr_paddr = (void *)
! 269: ((u_int32_t)td->td_rr.rr_paddr + range->poffset);
! 270: td->td_space[rnum] = (vaddr_t)mapiodev(&td->td_rr, 0,
! 271: TS102_ARBITRARY_MAP_SIZE);
! 272: }
! 273: td->td_parent = sc;
! 274: td->td_regs = regs +
! 275: slot * (TS102_REG_CARD_B_INT - TS102_REG_CARD_A_INT);
! 276: td->td_slot = slot;
! 277: SET_TAG_LITTLE_ENDIAN(&td->td_rr);
! 278: tslot_reset(td, TS102_ARBITRARY_MAP_SIZE);
! 279: }
! 280: }
! 281:
! 282: void
! 283: tslot_reset(struct tslot_data *td, u_int32_t iosize)
! 284: {
! 285: struct pcmciabus_attach_args paa;
! 286: int ctl, status;
! 287:
! 288: paa.paa_busname = "pcmcia";
! 289: paa.pct = (pcmcia_chipset_tag_t)td->td_parent->sc_pct;
! 290: paa.pch = (pcmcia_chipset_handle_t)td;
! 291: paa.iobase = 0;
! 292: paa.iosize = iosize;
! 293:
! 294: td->td_pcmcia = config_found(&td->td_parent->sc_dev, &paa, tslot_print);
! 295:
! 296: if (td->td_pcmcia == NULL) {
! 297: /*
! 298: * If no pcmcia attachment, power down the slot.
! 299: */
! 300: tslot_slot_disable((pcmcia_chipset_handle_t)td);
! 301: return;
! 302: }
! 303:
! 304: /*
! 305: * Initialize the slot
! 306: */
! 307:
! 308: ctl = TSLOT_READ(td, TS102_REG_CARD_A_CTL);
! 309: /* force low addresses */
! 310: ctl &= ~(TS102_CARD_CTL_AA_MASK | TS102_CARD_CTL_IA_MASK);
! 311: /* Put SBus and PCMCIA in their respective endian mode */
! 312: ctl |= TS102_CARD_CTL_SBLE; /* this is not what it looks like! */
! 313: ctl &= ~TS102_CARD_CTL_PCMBE;
! 314: /* disable read ahead and address increment */
! 315: ctl &= ~TS102_CARD_CTL_RAHD;
! 316: ctl |= TS102_CARD_CTL_INCDIS;
! 317: /* power on */
! 318: ctl &= ~TS102_CARD_CTL_PWRD;
! 319: TSLOT_WRITE(td, TS102_REG_CARD_A_CTL, ctl);
! 320:
! 321: /*
! 322: * Enable interrupt upon insertion/removal
! 323: */
! 324:
! 325: TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
! 326: TS102_CARD_INT_MASK_CARDDETECT_STATUS);
! 327:
! 328: status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
! 329: if (status & TS102_CARD_STS_PRES) {
! 330: tadpole_set_pcmcia(td->td_slot, 1);
! 331: td->td_status = TS_CARD;
! 332: pcmcia_card_attach(td->td_pcmcia);
! 333: } else {
! 334: tadpole_set_pcmcia(td->td_slot, 0);
! 335: td->td_status = 0;
! 336: }
! 337: }
! 338:
! 339: /* XXX there ought to be a common function for this... */
! 340: int
! 341: tslot_print(void *aux, const char *description)
! 342: {
! 343: struct pcmciabus_attach_args *paa = aux;
! 344: struct tslot_data *td = (struct tslot_data *)paa->pch;
! 345:
! 346: printf(" socket %d", td->td_slot);
! 347: return (UNCONF);
! 348: }
! 349:
! 350: /*
! 351: * PCMCIA Helpers
! 352: */
! 353:
! 354: int
! 355: tslot_io_alloc(pcmcia_chipset_handle_t pch, bus_addr_t start, bus_size_t size,
! 356: bus_size_t align, struct pcmcia_io_handle *pih)
! 357: {
! 358: struct tslot_data *td = (struct tslot_data *)pch;
! 359:
! 360: #ifdef TSLOT_DEBUG
! 361: printf("[io alloc %x-%x]", start, size);
! 362: #endif
! 363:
! 364: pih->iot = &td->td_rr;
! 365: pih->ioh = (bus_space_handle_t)(td->td_space[TS102_RANGE_IO]);
! 366: pih->addr = start;
! 367: pih->size = size;
! 368: pih->flags = 0;
! 369:
! 370: return (0);
! 371: }
! 372:
! 373: void
! 374: tslot_io_free(pcmcia_chipset_handle_t pch, struct pcmcia_io_handle *pih)
! 375: {
! 376: #ifdef TSLOT_DEBUG
! 377: printf("[io free %x-%x]", pih->start, pih->size);
! 378: #endif
! 379: }
! 380:
! 381: int
! 382: tslot_io_map(pcmcia_chipset_handle_t pch, int width, bus_addr_t offset,
! 383: bus_size_t size, struct pcmcia_io_handle *pih, int *windowp)
! 384: {
! 385: struct tslot_data *td = (struct tslot_data *)pch;
! 386:
! 387: #ifdef TSLOT_DEBUG
! 388: printf("[io map %x-%x", offset, size);
! 389: #endif
! 390:
! 391: pih->iot = &td->td_rr;
! 392: bus_space_subregion(&td->td_rr, td->td_space[TS102_RANGE_IO],
! 393: offset, size, &pih->ioh);
! 394: *windowp = TS102_RANGE_IO;
! 395:
! 396: #ifdef TSLOT_DEBUG
! 397: printf("->%p/%x]", pih->ioh, size);
! 398: #endif
! 399:
! 400: return (0);
! 401: }
! 402:
! 403: void
! 404: tslot_io_unmap(pcmcia_chipset_handle_t pch, int win)
! 405: {
! 406: #ifdef TSLOT_DEBUG
! 407: printf("[io unmap]");
! 408: #endif
! 409: }
! 410:
! 411: int
! 412: tslot_mem_alloc(pcmcia_chipset_handle_t pch, bus_size_t size,
! 413: struct pcmcia_mem_handle *pmh)
! 414: {
! 415: struct tslot_data *td = (struct tslot_data *)pch;
! 416:
! 417: #ifdef TSLOT_DEBUG
! 418: printf("[mem alloc %x]", size);
! 419: #endif
! 420: pmh->memt = &td->td_rr;
! 421: pmh->size = round_page(size);
! 422: pmh->addr = 0;
! 423: pmh->mhandle = 0;
! 424: pmh->realsize = 0; /* nothing so far! */
! 425:
! 426: return (0);
! 427: }
! 428:
! 429: void
! 430: tslot_mem_free(pcmcia_chipset_handle_t pch, struct pcmcia_mem_handle *pmh)
! 431: {
! 432: #ifdef TSLOT_DEBUG
! 433: printf("[mem free %x]", pmh->size);
! 434: #endif
! 435: }
! 436:
! 437: int
! 438: tslot_mem_map(pcmcia_chipset_handle_t pch, int kind, bus_addr_t addr,
! 439: bus_size_t size, struct pcmcia_mem_handle *pmh, bus_size_t *offsetp,
! 440: int *windowp)
! 441: {
! 442: struct tslot_data *td = (struct tslot_data *)pch;
! 443: int slot;
! 444:
! 445: slot = kind & PCMCIA_MEM_ATTR ? TS102_RANGE_ATTR : TS102_RANGE_COMMON;
! 446: #ifdef TSLOT_DEBUG
! 447: printf("[mem map %d %x-%x", slot, addr, size);
! 448: #endif
! 449:
! 450: addr += pmh->addr;
! 451:
! 452: pmh->memt = &td->td_rr;
! 453: bus_space_subregion(&td->td_rr, td->td_space[slot],
! 454: addr, size, &pmh->memh);
! 455: pmh->realsize = TS102_ARBITRARY_MAP_SIZE - addr;
! 456: *offsetp = 0;
! 457: *windowp = slot;
! 458:
! 459: #ifdef TSLOT_DEBUG
! 460: printf("->%p/%x]", pmh->memh, size);
! 461: #endif
! 462:
! 463: return (0);
! 464: }
! 465:
! 466: void
! 467: tslot_mem_unmap(pcmcia_chipset_handle_t pch, int win)
! 468: {
! 469: #ifdef TSLOT_DEBUG
! 470: printf("[mem unmap %d]", win);
! 471: #endif
! 472: }
! 473:
! 474: void
! 475: tslot_slot_disable(pcmcia_chipset_handle_t pch)
! 476: {
! 477: struct tslot_data *td = (struct tslot_data *)pch;
! 478:
! 479: #ifdef TSLOT_DEBUG
! 480: printf("%s: disable slot %d\n",
! 481: td->td_parent->sc_dev.dv_xname, td->td_slot);
! 482: #endif
! 483:
! 484: /*
! 485: * Disable card access.
! 486: */
! 487: TSLOT_WRITE(td, TS102_REG_CARD_A_STS,
! 488: TSLOT_READ(td, TS102_REG_CARD_A_STS) & ~TS102_CARD_STS_ACEN);
! 489:
! 490: /*
! 491: * Disable interrupts, except for insertion.
! 492: */
! 493: TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
! 494: TS102_CARD_INT_MASK_CARDDETECT_STATUS);
! 495: }
! 496:
! 497: void
! 498: tslot_slot_enable(pcmcia_chipset_handle_t pch)
! 499: {
! 500: struct tslot_data *td = (struct tslot_data *)pch;
! 501: int status, intr, i;
! 502:
! 503: #ifdef TSLOT_DEBUG
! 504: printf("%s: enable slot %d\n",
! 505: td->td_parent->sc_dev.dv_xname, td->td_slot);
! 506: #endif
! 507:
! 508: /* Power down the socket to reset it */
! 509: status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
! 510: TSLOT_WRITE(td, TS102_REG_CARD_A_STS, status | TS102_CARD_STS_VCCEN);
! 511:
! 512: /*
! 513: * wait 300ms until power fails (Tpf). Then, wait 100ms since we
! 514: * are changing Vcc (Toff).
! 515: */
! 516: DELAY((300 + 100) * 1000);
! 517:
! 518: /*
! 519: * Power on the card if not already done, and enable card access
! 520: */
! 521: status |= TS102_CARD_STS_ACEN;
! 522: status &= ~TS102_CARD_STS_VCCEN;
! 523: TSLOT_WRITE(td, TS102_REG_CARD_A_STS, status);
! 524:
! 525: /*
! 526: * wait 100ms until power raise (Tpr) and 20ms to become
! 527: * stable (Tsu(Vcc)).
! 528: */
! 529: DELAY((100 + 20) * 1000);
! 530:
! 531: status &= ~TS102_CARD_STS_VPP1_MASK;
! 532: status |= TS102_CARD_STS_VPP1_VCC;
! 533: TSLOT_WRITE(td, TS102_REG_CARD_A_STS, status);
! 534:
! 535: /*
! 536: * hold RESET at least 20us.
! 537: */
! 538: intr = TSLOT_READ(td, TS102_REG_CARD_A_INT);
! 539: TSLOT_WRITE(td, TS102_REG_CARD_A_INT, TS102_CARD_INT_SOFT_RESET);
! 540: DELAY(20);
! 541: TSLOT_WRITE(td, TS102_REG_CARD_A_INT, intr);
! 542:
! 543: /* wait 20ms as per pc card standard (r2.01) section 4.3.6 */
! 544: DELAY(20 * 1000);
! 545:
! 546: /* We need level-triggered interrupts for PC Card hardware */
! 547: TSLOT_WRITE(td, TS102_REG_CARD_A_STS,
! 548: TSLOT_READ(td, TS102_REG_CARD_A_STS) | TS102_CARD_STS_LVL);
! 549:
! 550: /*
! 551: * Wait until the card is unbusy. If it is still busy after 3 seconds,
! 552: * give up. We could enable card interrupts and wait for the interrupt
! 553: * to happen when BUSY is released, but the interrupt could also be
! 554: * triggered by the card itself if it's an I/O card, so better poll
! 555: * here.
! 556: */
! 557: for (i = 30000; i != 0; i--) {
! 558: status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
! 559: /* If the card has been removed, abort */
! 560: if ((status & TS102_CARD_STS_PRES) == 0) {
! 561: tslot_slot_disable(pch);
! 562: return;
! 563: }
! 564: if (status & TS102_CARD_STS_RDY)
! 565: break;
! 566: else
! 567: DELAY(100);
! 568: }
! 569:
! 570: if (i == 0) {
! 571: printf("%s: slot %d still busy after 3 seconds, status 0x%x\n",
! 572: td->td_parent->sc_dev.dv_xname, td->td_slot,
! 573: TSLOT_READ(td, TS102_REG_CARD_A_STS));
! 574: return;
! 575: }
! 576:
! 577: /*
! 578: * Enable the card interrupts if this is an I/O card.
! 579: * Note that the TS102_CARD_STS_IO bit in the status register will
! 580: * never get set, despite what the documentation says!
! 581: */
! 582: if (pcmcia_card_gettype(td->td_pcmcia) == PCMCIA_IFTYPE_IO) {
! 583: TSLOT_WRITE(td, TS102_REG_CARD_A_STS,
! 584: TSLOT_READ(td, TS102_REG_CARD_A_STS) | TS102_CARD_STS_IO);
! 585: TSLOT_WRITE(td, TS102_REG_CARD_A_INT,
! 586: TS102_CARD_INT_MASK_CARDDETECT_STATUS |
! 587: TS102_CARD_INT_MASK_IRQ);
! 588: }
! 589: }
! 590:
! 591: /*
! 592: * Event management
! 593: */
! 594: void
! 595: tslot_create_event_thread(void *v)
! 596: {
! 597: struct tslot_softc *sc = v;
! 598: const char *name = sc->sc_dev.dv_xname;
! 599:
! 600: if (kthread_create(tslot_event_thread, sc, &sc->sc_thread, "%s",
! 601: name) != 0) {
! 602: panic("%s: unable to create event kthread", name);
! 603: }
! 604: }
! 605:
! 606: void
! 607: tslot_event_thread(void *v)
! 608: {
! 609: struct tslot_softc *sc = v;
! 610: struct tslot_data *td;
! 611: int s, status;
! 612: unsigned int socket;
! 613:
! 614: for (;;) {
! 615: s = splhigh();
! 616:
! 617: if ((socket = ffs(sc->sc_events)) == 0) {
! 618: splx(s);
! 619: tsleep(&sc->sc_events, PWAIT, "tslot_event", 0);
! 620: continue;
! 621: }
! 622: socket--;
! 623: sc->sc_events &= ~(1 << socket);
! 624: splx(s);
! 625:
! 626: if (socket >= TS102_NUM_SLOTS) {
! 627: #ifdef DEBUG
! 628: printf("%s: invalid slot number %d\n",
! 629: sc->sc_dev.dv_xname, te->te_slot);
! 630: #endif
! 631: continue;
! 632: }
! 633:
! 634: td = &sc->sc_slot[socket];
! 635: status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
! 636:
! 637: if (status & TS102_CARD_STS_PRES) {
! 638: /* Card insertion */
! 639: if ((td->td_status & TS_CARD) == 0) {
! 640: td->td_status |= TS_CARD;
! 641: tadpole_set_pcmcia(td->td_slot, 1);
! 642: pcmcia_card_attach(td->td_pcmcia);
! 643: }
! 644: } else {
! 645: /* Card removal */
! 646: if ((td->td_status & TS_CARD) != 0) {
! 647: td->td_status &= ~TS_CARD;
! 648: tadpole_set_pcmcia(td->td_slot, 0);
! 649: pcmcia_card_detach(td->td_pcmcia,
! 650: DETACH_FORCE);
! 651: }
! 652: }
! 653: }
! 654: }
! 655:
! 656: /*
! 657: * Interrupt handling
! 658: */
! 659:
! 660: int
! 661: tslot_intr(void *v)
! 662: {
! 663: struct tslot_softc *sc = v;
! 664: struct tslot_data *td;
! 665: int intregs[TS102_NUM_SLOTS], *intreg;
! 666: int i, rc = 0;
! 667:
! 668: /*
! 669: * Scan slots, and acknowledge the interrupt if necessary first
! 670: */
! 671: for (i = 0; i < TS102_NUM_SLOTS; i++) {
! 672: td = &sc->sc_slot[i];
! 673: intreg = &intregs[i];
! 674: *intreg = TSLOT_READ(td, TS102_REG_CARD_A_INT);
! 675:
! 676: /*
! 677: * Acknowledge all interrupt situations at once, even if they
! 678: * did not occur.
! 679: */
! 680: if ((*intreg & (TS102_CARD_INT_STATUS_IRQ |
! 681: TS102_CARD_INT_STATUS_WP_STATUS_CHANGED |
! 682: TS102_CARD_INT_STATUS_BATTERY_STATUS_CHANGED |
! 683: TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED)) != 0) {
! 684: rc = 1;
! 685: TSLOT_WRITE(td, TS102_REG_CARD_A_INT, *intreg |
! 686: TS102_CARD_INT_RQST_IRQ |
! 687: TS102_CARD_INT_RQST_WP_STATUS_CHANGED |
! 688: TS102_CARD_INT_RQST_BATTERY_STATUS_CHANGED |
! 689: TS102_CARD_INT_RQST_CARDDETECT_STATUS_CHANGED);
! 690: }
! 691: }
! 692:
! 693: /*
! 694: * Invoke the interrupt handler for each slot
! 695: */
! 696: for (i = 0; i < TS102_NUM_SLOTS; i++) {
! 697: td = &sc->sc_slot[i];
! 698: intreg = &intregs[i];
! 699:
! 700: if ((*intreg & (TS102_CARD_INT_STATUS_IRQ |
! 701: TS102_CARD_INT_STATUS_WP_STATUS_CHANGED |
! 702: TS102_CARD_INT_STATUS_BATTERY_STATUS_CHANGED |
! 703: TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED)) != 0)
! 704: tslot_slot_intr(td, *intreg);
! 705: }
! 706:
! 707: return (rc);
! 708: }
! 709:
! 710: void
! 711: tslot_queue_event(struct tslot_softc *sc, int slot)
! 712: {
! 713: int s;
! 714:
! 715: s = splhigh();
! 716: sc->sc_events |= (1 << slot);
! 717: splx(s);
! 718: wakeup(&sc->sc_events);
! 719: }
! 720:
! 721: void
! 722: tslot_slot_intr(struct tslot_data *td, int intreg)
! 723: {
! 724: struct tslot_softc *sc = td->td_parent;
! 725: int status, sockstat;
! 726:
! 727: status = TSLOT_READ(td, TS102_REG_CARD_A_STS);
! 728: #ifdef TSLOT_DEBUG
! 729: printf("%s: interrupt on socket %d ir %x sts %x\n",
! 730: sc->sc_dev.dv_xname, td->td_slot, intreg, status);
! 731: #endif
! 732:
! 733: sockstat = td->td_status;
! 734:
! 735: /*
! 736: * The TS102 queues interrupt requests, and may trigger an interrupt
! 737: * for a condition the driver does not want to receive anymore (for
! 738: * example, after a card gets removed).
! 739: * Thus, only proceed if the driver is currently allowing a particular
! 740: * condition.
! 741: */
! 742:
! 743: if ((intreg & TS102_CARD_INT_STATUS_CARDDETECT_STATUS_CHANGED) != 0 &&
! 744: (intreg & TS102_CARD_INT_MASK_CARDDETECT_STATUS) != 0) {
! 745: tslot_queue_event(sc, td->td_slot);
! 746: #ifdef TSLOT_DEBUG
! 747: printf("%s: slot %d status changed from %d to %d\n",
! 748: sc->sc_dev.dv_xname, td->td_slot, sockstat, td->td_status);
! 749: #endif
! 750: /*
! 751: * Ignore extra interrupt bits, they are part of the change.
! 752: */
! 753: return;
! 754: }
! 755:
! 756: if ((intreg & TS102_CARD_INT_STATUS_IRQ) != 0 &&
! 757: (intreg & TS102_CARD_INT_MASK_IRQ) != 0) {
! 758: /* ignore interrupts if we have a pending state change */
! 759: if (sc->sc_events & (1 << td->td_slot))
! 760: return;
! 761:
! 762: if ((sockstat & TS_CARD) == 0) {
! 763: printf("%s: spurious interrupt on slot %d isr %x\n",
! 764: sc->sc_dev.dv_xname, td->td_slot, intreg);
! 765: return;
! 766: }
! 767:
! 768: if (td->td_intr != NULL) {
! 769: /*
! 770: * XXX There is no way to honour the interrupt handler
! 771: * requested IPL level...
! 772: */
! 773: (*td->td_intr)(td->td_intrarg);
! 774: }
! 775: }
! 776: }
! 777:
! 778: void
! 779: tslot_intr_disestablish(pcmcia_chipset_handle_t pch, void *ih)
! 780: {
! 781: struct tslot_data *td = (struct tslot_data *)pch;
! 782:
! 783: td->td_intr = NULL;
! 784: td->td_intrarg = NULL;
! 785: }
! 786:
! 787: const char *
! 788: tslot_intr_string(pcmcia_chipset_handle_t pch, void *ih)
! 789: {
! 790: if (ih == NULL)
! 791: return ("couldn't establish interrupt");
! 792: else
! 793: return (""); /* nothing for now */
! 794: }
! 795:
! 796:
! 797: void *
! 798: tslot_intr_establish(pcmcia_chipset_handle_t pch, struct pcmcia_function *pf,
! 799: int ipl, int (*handler)(void *), void *arg, char *xname)
! 800: {
! 801: struct tslot_data *td = (struct tslot_data *)pch;
! 802:
! 803: td->td_intr = handler;
! 804: td->td_intrarg = arg;
! 805:
! 806: return (td);
! 807: }
CVSweb