Annotation of sys/dev/sdmmc/sdmmc_io.c, Revision 1.1
1.1 ! nbrk 1: /* $OpenBSD: sdmmc_io.c,v 1.9 2007/06/02 01:48:37 uwe Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: /* Routines for SD I/O cards. */
! 20:
! 21: #include <sys/param.h>
! 22: #include <sys/kernel.h>
! 23: #include <sys/malloc.h>
! 24: #include <sys/proc.h>
! 25: #include <sys/systm.h>
! 26:
! 27: #include <dev/sdmmc/sdmmc_ioreg.h>
! 28: #include <dev/sdmmc/sdmmcchip.h>
! 29: #include <dev/sdmmc/sdmmcreg.h>
! 30: #include <dev/sdmmc/sdmmcvar.h>
! 31:
! 32: struct sdmmc_intr_handler {
! 33: struct sdmmc_softc *ih_softc;
! 34: char *ih_name;
! 35: int (*ih_fun)(void *);
! 36: void *ih_arg;
! 37: TAILQ_ENTRY(sdmmc_intr_handler) entry;
! 38: };
! 39:
! 40: int sdmmc_submatch(struct device *, void *, void *);
! 41: int sdmmc_print(void *, const char *);
! 42: int sdmmc_io_rw_direct(struct sdmmc_softc *, struct sdmmc_function *,
! 43: int, u_char *, int);
! 44: int sdmmc_io_rw_extended(struct sdmmc_softc *, struct sdmmc_function *,
! 45: int, u_char *, int, int);
! 46: int sdmmc_io_xchg(struct sdmmc_softc *, struct sdmmc_function *,
! 47: int, u_char *);
! 48: void sdmmc_io_reset(struct sdmmc_softc *);
! 49: int sdmmc_io_send_op_cond(struct sdmmc_softc *, u_int32_t, u_int32_t *);
! 50:
! 51: #ifdef SDMMC_DEBUG
! 52: #define DPRINTF(s) printf s
! 53: #else
! 54: #define DPRINTF(s) /**/
! 55: #endif
! 56:
! 57: #ifdef SDMMC_DEBUG
! 58: int sdmmc_verbose = 1;
! 59: #else
! 60: int sdmmc_verbose = 0;
! 61: #endif
! 62:
! 63: /*
! 64: * Initialize SD I/O card functions (before memory cards). The host
! 65: * system and controller must support card interrupts in order to use
! 66: * I/O functions.
! 67: */
! 68: int
! 69: sdmmc_io_enable(struct sdmmc_softc *sc)
! 70: {
! 71: u_int32_t host_ocr;
! 72: u_int32_t card_ocr;
! 73:
! 74: /* Set host mode to SD "combo" card. */
! 75: SET(sc->sc_flags, SMF_SD_MODE|SMF_IO_MODE|SMF_MEM_MODE);
! 76:
! 77: /* Reset I/O functions. */
! 78: sdmmc_io_reset(sc);
! 79:
! 80: /*
! 81: * Read the I/O OCR value, determine the number of I/O
! 82: * functions and whether memory is also present (a "combo
! 83: * card") by issuing CMD5. SD memory-only and MMC cards
! 84: * do not respond to CMD5.
! 85: */
! 86: if (sdmmc_io_send_op_cond(sc, 0, &card_ocr) != 0) {
! 87: /* No SDIO card; switch to SD memory-only mode. */
! 88: CLR(sc->sc_flags, SMF_IO_MODE);
! 89: return 0;
! 90: }
! 91:
! 92: /* Parse the additional bits in the I/O OCR value. */
! 93: if (!ISSET(card_ocr, SD_IO_OCR_MEM_PRESENT)) {
! 94: /* SDIO card without memory (not a "combo card"). */
! 95: DPRINTF(("%s: no memory present\n", SDMMCDEVNAME(sc)));
! 96: CLR(sc->sc_flags, SMF_MEM_MODE);
! 97: }
! 98: sc->sc_function_count = SD_IO_OCR_NUM_FUNCTIONS(card_ocr);
! 99: if (sc->sc_function_count == 0) {
! 100: /* Useless SDIO card without any I/O functions. */
! 101: DPRINTF(("%s: no I/O functions\n", SDMMCDEVNAME(sc)));
! 102: CLR(sc->sc_flags, SMF_IO_MODE);
! 103: return 0;
! 104: }
! 105: card_ocr &= SD_IO_OCR_MASK;
! 106:
! 107: /* Set the lowest voltage supported by the card and host. */
! 108: host_ocr = sdmmc_chip_host_ocr(sc->sct, sc->sch);
! 109: if (sdmmc_set_bus_power(sc, host_ocr, card_ocr) != 0) {
! 110: printf("%s: can't supply voltage requested by card\n",
! 111: SDMMCDEVNAME(sc));
! 112: return 1;
! 113: }
! 114:
! 115: /* Reset I/O functions (again). */
! 116: sdmmc_io_reset(sc);
! 117:
! 118: /* Send the new OCR value until all cards are ready. */
! 119: if (sdmmc_io_send_op_cond(sc, host_ocr, NULL) != 0) {
! 120: printf("%s: can't send I/O OCR\n", SDMMCDEVNAME(sc));
! 121: return 1;
! 122: }
! 123: return 0;
! 124: }
! 125:
! 126: /*
! 127: * Allocate sdmmc_function structures for SD card I/O function
! 128: * (including function 0).
! 129: */
! 130: void
! 131: sdmmc_io_scan(struct sdmmc_softc *sc)
! 132: {
! 133: struct sdmmc_function *sf0, *sf;
! 134: int i;
! 135:
! 136: sf0 = sdmmc_function_alloc(sc);
! 137: sf0->number = 0;
! 138: if (sdmmc_set_relative_addr(sc, sf0) != 0) {
! 139: printf("%s: can't set I/O RCA\n", SDMMCDEVNAME(sc));
! 140: SET(sf0->flags, SFF_ERROR);
! 141: return;
! 142: }
! 143: sc->sc_fn0 = sf0;
! 144: SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf0, sf_list);
! 145:
! 146: /* Verify that the RCA has been set by selecting the card. */
! 147: if (sdmmc_select_card(sc, sf0) != 0) {
! 148: printf("%s: can't select I/O RCA %d\n", SDMMCDEVNAME(sc),
! 149: sf0->rca);
! 150: SET(sf0->flags, SFF_ERROR);
! 151: return;
! 152: }
! 153:
! 154: for (i = 1; i <= sc->sc_function_count; i++) {
! 155: sf = sdmmc_function_alloc(sc);
! 156: sf->number = i;
! 157: sf->rca = sf0->rca;
! 158:
! 159: SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf, sf_list);
! 160: }
! 161: }
! 162:
! 163: /*
! 164: * Initialize SDIO card functions.
! 165: */
! 166: int
! 167: sdmmc_io_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
! 168: {
! 169: if (sf->number == 0) {
! 170: sdmmc_io_write_1(sf, SD_IO_CCCR_BUS_WIDTH,
! 171: CCCR_BUS_WIDTH_1);
! 172:
! 173: if (sdmmc_read_cis(sf, &sf->cis) != 0) {
! 174: printf("%s: can't read CIS\n", SDMMCDEVNAME(sc));
! 175: SET(sf->flags, SFF_ERROR);
! 176: return 1;
! 177: }
! 178:
! 179: sdmmc_check_cis_quirks(sf);
! 180:
! 181: if (sdmmc_verbose)
! 182: sdmmc_print_cis(sf);
! 183: }
! 184: return 0;
! 185: }
! 186:
! 187: /*
! 188: * Indicate whether the function is ready to operate.
! 189: */
! 190: int
! 191: sdmmc_io_function_ready(struct sdmmc_function *sf)
! 192: {
! 193: struct sdmmc_softc *sc = sf->sc;
! 194: struct sdmmc_function *sf0 = sc->sc_fn0;
! 195: u_int8_t rv;
! 196:
! 197: if (sf->number == 0)
! 198: return 1; /* FN0 is always ready */
! 199:
! 200: rv = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_READY);
! 201: return (rv & (1 << sf->number)) != 0;
! 202: }
! 203:
! 204: /*
! 205: * Enable the I/O function. Return zero if the function was
! 206: * enabled successfully.
! 207: */
! 208: int
! 209: sdmmc_io_function_enable(struct sdmmc_function *sf)
! 210: {
! 211: struct sdmmc_softc *sc = sf->sc;
! 212: struct sdmmc_function *sf0 = sc->sc_fn0;
! 213: u_int8_t rv;
! 214: int retry = 5;
! 215:
! 216: if (sf->number == 0)
! 217: return 0; /* FN0 is always enabled */
! 218:
! 219: SDMMC_LOCK(sc);
! 220: rv = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_ENABLE);
! 221: rv |= (1<<sf->number);
! 222: sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_ENABLE, rv);
! 223: SDMMC_UNLOCK(sc);
! 224:
! 225: while (!sdmmc_io_function_ready(sf) && retry-- > 0)
! 226: tsleep(&lbolt, PPAUSE, "pause", 0);
! 227: return (retry >= 0) ? 0 : ETIMEDOUT;
! 228: }
! 229:
! 230: /*
! 231: * Disable the I/O function. Return zero if the function was
! 232: * disabled successfully.
! 233: */
! 234: void
! 235: sdmmc_io_function_disable(struct sdmmc_function *sf)
! 236: {
! 237: struct sdmmc_softc *sc = sf->sc;
! 238: struct sdmmc_function *sf0 = sc->sc_fn0;
! 239: u_int8_t rv;
! 240:
! 241: if (sf->number == 0)
! 242: return; /* FN0 is always enabled */
! 243:
! 244: SDMMC_LOCK(sc);
! 245: rv = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_ENABLE);
! 246: rv &= ~(1<<sf->number);
! 247: sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_ENABLE, rv);
! 248: SDMMC_UNLOCK(sc);
! 249: }
! 250:
! 251: void
! 252: sdmmc_io_attach(struct sdmmc_softc *sc)
! 253: {
! 254: struct sdmmc_function *sf;
! 255: struct sdmmc_attach_args saa;
! 256:
! 257: SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
! 258: if (sf->number < 1)
! 259: continue;
! 260:
! 261: bzero(&saa, sizeof saa);
! 262: saa.sf = sf;
! 263:
! 264: sf->child = config_found_sm(&sc->sc_dev, &saa, sdmmc_print,
! 265: sdmmc_submatch);
! 266: }
! 267: }
! 268:
! 269: int
! 270: sdmmc_submatch(struct device *parent, void *match, void *aux)
! 271: {
! 272: struct cfdata *cf = match;
! 273:
! 274: /* Skip the scsibus, it is configured directly. */
! 275: if (strcmp(cf->cf_driver->cd_name, "scsibus") == 0)
! 276: return 0;
! 277:
! 278: return cf->cf_attach->ca_match(parent, cf, aux);
! 279: }
! 280:
! 281: int
! 282: sdmmc_print(void *aux, const char *pnp)
! 283: {
! 284: struct sdmmc_attach_args *sa = aux;
! 285: struct sdmmc_function *sf = sa->sf;
! 286: struct sdmmc_cis *cis = &sf->sc->sc_fn0->cis;
! 287: int i;
! 288:
! 289: if (pnp) {
! 290: if (sf->number == 0)
! 291: return QUIET;
! 292:
! 293: for (i = 0; i < 4 && cis->cis1_info[i]; i++)
! 294: printf("%s%s", i ? ", " : "\"", cis->cis1_info[i]);
! 295: if (i != 0)
! 296: printf("\"");
! 297:
! 298: if (cis->manufacturer != SDMMC_VENDOR_INVALID &&
! 299: cis->product != SDMMC_PRODUCT_INVALID) {
! 300: printf("%s(", i ? " " : "");
! 301: if (cis->manufacturer != SDMMC_VENDOR_INVALID)
! 302: printf("manufacturer 0x%x%s",
! 303: cis->manufacturer,
! 304: cis->product == SDMMC_PRODUCT_INVALID ?
! 305: "" : ", ");
! 306: if (cis->product != SDMMC_PRODUCT_INVALID)
! 307: printf("product 0x%x", cis->product);
! 308: printf(")");
! 309: }
! 310: printf("%sat %s", i ? " " : "", pnp);
! 311: }
! 312: printf(" function %d", sf->number);
! 313:
! 314: if (!pnp) {
! 315: for (i = 0; i < 3 && cis->cis1_info[i]; i++)
! 316: printf("%s%s", i ? ", " : " \"", cis->cis1_info[i]);
! 317: if (i != 0)
! 318: printf("\"");
! 319: }
! 320: return UNCONF;
! 321: }
! 322:
! 323: void
! 324: sdmmc_io_detach(struct sdmmc_softc *sc)
! 325: {
! 326: struct sdmmc_function *sf;
! 327:
! 328: SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
! 329: if (sf->child != NULL) {
! 330: config_detach(sf->child, DETACH_FORCE);
! 331: sf->child = NULL;
! 332: }
! 333: }
! 334:
! 335: KASSERT(TAILQ_EMPTY(&sc->sc_intrq));
! 336: }
! 337:
! 338: int
! 339: sdmmc_io_rw_direct(struct sdmmc_softc *sc, struct sdmmc_function *sf,
! 340: int reg, u_char *datap, int arg)
! 341: {
! 342: struct sdmmc_command cmd;
! 343: int error;
! 344:
! 345: SDMMC_LOCK(sc);
! 346:
! 347: /* Make sure the card is selected. */
! 348: if ((error = sdmmc_select_card(sc, sf)) != 0) {
! 349: SDMMC_UNLOCK(sc);
! 350: return error;
! 351: }
! 352:
! 353: arg |= ((sf == NULL ? 0 : sf->number) & SD_ARG_CMD52_FUNC_MASK) <<
! 354: SD_ARG_CMD52_FUNC_SHIFT;
! 355: arg |= (reg & SD_ARG_CMD52_REG_MASK) <<
! 356: SD_ARG_CMD52_REG_SHIFT;
! 357: arg |= (*datap & SD_ARG_CMD52_DATA_MASK) <<
! 358: SD_ARG_CMD52_DATA_SHIFT;
! 359:
! 360: bzero(&cmd, sizeof cmd);
! 361: cmd.c_opcode = SD_IO_RW_DIRECT;
! 362: cmd.c_arg = arg;
! 363: cmd.c_flags = SCF_CMD_AC | SCF_RSP_R5;
! 364:
! 365: error = sdmmc_mmc_command(sc, &cmd);
! 366: *datap = SD_R5_DATA(cmd.c_resp);
! 367:
! 368: SDMMC_UNLOCK(sc);
! 369: return error;
! 370: }
! 371:
! 372: /*
! 373: * Useful values of `arg' to pass in are either SD_ARG_CMD53_READ or
! 374: * SD_ARG_CMD53_WRITE. SD_ARG_CMD53_INCREMENT may be ORed into `arg'
! 375: * to access successive register locations instead of accessing the
! 376: * same register many times.
! 377: */
! 378: int
! 379: sdmmc_io_rw_extended(struct sdmmc_softc *sc, struct sdmmc_function *sf,
! 380: int reg, u_char *datap, int datalen, int arg)
! 381: {
! 382: struct sdmmc_command cmd;
! 383: int error;
! 384:
! 385: SDMMC_LOCK(sc);
! 386:
! 387: #if 0
! 388: /* Make sure the card is selected. */
! 389: if ((error = sdmmc_select_card(sc, sf)) != 0) {
! 390: SDMMC_UNLOCK(sc);
! 391: return error;
! 392: }
! 393: #endif
! 394:
! 395: arg |= ((sf == NULL ? 0 : sf->number) & SD_ARG_CMD53_FUNC_MASK) <<
! 396: SD_ARG_CMD53_FUNC_SHIFT;
! 397: arg |= (reg & SD_ARG_CMD53_REG_MASK) <<
! 398: SD_ARG_CMD53_REG_SHIFT;
! 399: arg |= (datalen & SD_ARG_CMD53_LENGTH_MASK) <<
! 400: SD_ARG_CMD53_LENGTH_SHIFT;
! 401:
! 402: bzero(&cmd, sizeof cmd);
! 403: cmd.c_opcode = SD_IO_RW_EXTENDED;
! 404: cmd.c_arg = arg;
! 405: cmd.c_flags = SCF_CMD_AC | SCF_RSP_R5;
! 406: cmd.c_data = datap;
! 407: cmd.c_datalen = datalen;
! 408: cmd.c_blklen = MIN(datalen, sdmmc_chip_host_maxblklen(sc->sct, sc->sch));
! 409:
! 410: if (!ISSET(arg, SD_ARG_CMD53_WRITE))
! 411: cmd.c_flags |= SCF_CMD_READ;
! 412:
! 413: error = sdmmc_mmc_command(sc, &cmd);
! 414: SDMMC_UNLOCK(sc);
! 415: return error;
! 416: }
! 417:
! 418: u_int8_t
! 419: sdmmc_io_read_1(struct sdmmc_function *sf, int reg)
! 420: {
! 421: u_int8_t data = 0;
! 422:
! 423: (void)sdmmc_io_rw_direct(sf->sc, sf, reg, (u_char *)&data,
! 424: SD_ARG_CMD52_READ);
! 425: return data;
! 426: }
! 427:
! 428: void
! 429: sdmmc_io_write_1(struct sdmmc_function *sf, int reg, u_int8_t data)
! 430: {
! 431: (void)sdmmc_io_rw_direct(sf->sc, sf, reg, (u_char *)&data,
! 432: SD_ARG_CMD52_WRITE);
! 433: }
! 434:
! 435: u_int16_t
! 436: sdmmc_io_read_2(struct sdmmc_function *sf, int reg)
! 437: {
! 438: u_int16_t data = 0;
! 439:
! 440: (void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 2,
! 441: SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT);
! 442: return data;
! 443: }
! 444:
! 445: void
! 446: sdmmc_io_write_2(struct sdmmc_function *sf, int reg, u_int16_t data)
! 447: {
! 448: (void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 2,
! 449: SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT);
! 450: }
! 451:
! 452: u_int32_t
! 453: sdmmc_io_read_4(struct sdmmc_function *sf, int reg)
! 454: {
! 455: u_int32_t data = 0;
! 456:
! 457: (void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 4,
! 458: SD_ARG_CMD53_READ | SD_ARG_CMD53_INCREMENT);
! 459: return data;
! 460: }
! 461:
! 462: void
! 463: sdmmc_io_write_4(struct sdmmc_function *sf, int reg, u_int32_t data)
! 464: {
! 465: (void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 4,
! 466: SD_ARG_CMD53_WRITE | SD_ARG_CMD53_INCREMENT);
! 467: }
! 468:
! 469: int
! 470: sdmmc_io_read_multi_1(struct sdmmc_function *sf, int reg, u_char *data,
! 471: int datalen)
! 472: {
! 473: int error;
! 474:
! 475: while (datalen > SD_ARG_CMD53_LENGTH_MAX) {
! 476: error = sdmmc_io_rw_extended(sf->sc, sf, reg, data,
! 477: SD_ARG_CMD53_LENGTH_MAX, SD_ARG_CMD53_READ);
! 478: if (error)
! 479: return error;
! 480: data += SD_ARG_CMD53_LENGTH_MAX;
! 481: datalen -= SD_ARG_CMD53_LENGTH_MAX;
! 482: }
! 483:
! 484: return sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen,
! 485: SD_ARG_CMD53_READ);
! 486: }
! 487:
! 488: int
! 489: sdmmc_io_write_multi_1(struct sdmmc_function *sf, int reg, u_char *data,
! 490: int datalen)
! 491: {
! 492: int error;
! 493:
! 494: while (datalen > SD_ARG_CMD53_LENGTH_MAX) {
! 495: error = sdmmc_io_rw_extended(sf->sc, sf, reg, data,
! 496: SD_ARG_CMD53_LENGTH_MAX, SD_ARG_CMD53_WRITE);
! 497: if (error)
! 498: return error;
! 499: data += SD_ARG_CMD53_LENGTH_MAX;
! 500: datalen -= SD_ARG_CMD53_LENGTH_MAX;
! 501: }
! 502:
! 503: return sdmmc_io_rw_extended(sf->sc, sf, reg, data, datalen,
! 504: SD_ARG_CMD53_WRITE);
! 505: }
! 506:
! 507: int
! 508: sdmmc_io_xchg(struct sdmmc_softc *sc, struct sdmmc_function *sf,
! 509: int reg, u_char *datap)
! 510: {
! 511: return sdmmc_io_rw_direct(sc, sf, reg, datap,
! 512: SD_ARG_CMD52_WRITE|SD_ARG_CMD52_EXCHANGE);
! 513: }
! 514:
! 515: /*
! 516: * Reset the I/O functions of the card.
! 517: */
! 518: void
! 519: sdmmc_io_reset(struct sdmmc_softc *sc)
! 520: {
! 521: #if 0 /* XXX command fails */
! 522: (void)sdmmc_io_write(sc, NULL, SD_IO_REG_CCCR_CTL, CCCR_CTL_RES);
! 523: sdmmc_delay(100000);
! 524: #endif
! 525: }
! 526:
! 527: /*
! 528: * Get or set the card's I/O OCR value (SDIO).
! 529: */
! 530: int
! 531: sdmmc_io_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, u_int32_t *ocrp)
! 532: {
! 533: struct sdmmc_command cmd;
! 534: int error;
! 535: int i;
! 536:
! 537: SDMMC_LOCK(sc);
! 538:
! 539: /*
! 540: * If we change the OCR value, retry the command until the OCR
! 541: * we receive in response has the "CARD BUSY" bit set, meaning
! 542: * that all cards are ready for identification.
! 543: */
! 544: for (i = 0; i < 100; i++) {
! 545: bzero(&cmd, sizeof cmd);
! 546: cmd.c_opcode = SD_IO_SEND_OP_COND;
! 547: cmd.c_arg = ocr;
! 548: cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R4;
! 549:
! 550: error = sdmmc_mmc_command(sc, &cmd);
! 551: if (error != 0)
! 552: break;
! 553: if (ISSET(MMC_R4(cmd.c_resp), SD_IO_OCR_MEM_READY) ||
! 554: ocr == 0)
! 555: break;
! 556: error = ETIMEDOUT;
! 557: sdmmc_delay(10000);
! 558: }
! 559: if (error == 0 && ocrp != NULL)
! 560: *ocrp = MMC_R4(cmd.c_resp);
! 561:
! 562: SDMMC_UNLOCK(sc);
! 563: return error;
! 564: }
! 565:
! 566: /*
! 567: * Card interrupt handling
! 568: */
! 569:
! 570: void
! 571: sdmmc_intr_enable(struct sdmmc_function *sf)
! 572: {
! 573: struct sdmmc_softc *sc = sf->sc;
! 574: struct sdmmc_function *sf0 = sc->sc_fn0;
! 575: u_int8_t imask;
! 576:
! 577: SDMMC_LOCK(sc);
! 578: imask = sdmmc_io_read_1(sf0, SD_IO_CCCR_INT_ENABLE);
! 579: imask |= 1 << sf->number;
! 580: sdmmc_io_write_1(sf0, SD_IO_CCCR_INT_ENABLE, imask);
! 581: SDMMC_UNLOCK(sc);
! 582: }
! 583:
! 584: void
! 585: sdmmc_intr_disable(struct sdmmc_function *sf)
! 586: {
! 587: struct sdmmc_softc *sc = sf->sc;
! 588: struct sdmmc_function *sf0 = sc->sc_fn0;
! 589: u_int8_t imask;
! 590:
! 591: SDMMC_LOCK(sc);
! 592: imask = sdmmc_io_read_1(sf0, SD_IO_CCCR_INT_ENABLE);
! 593: imask &= ~(1 << sf->number);
! 594: sdmmc_io_write_1(sf0, SD_IO_CCCR_INT_ENABLE, imask);
! 595: SDMMC_UNLOCK(sc);
! 596: }
! 597:
! 598: /*
! 599: * Establish a handler for the SDIO card interrupt. Because the
! 600: * interrupt may be shared with different SDIO functions, multiple
! 601: * handlers can be established.
! 602: */
! 603: void *
! 604: sdmmc_intr_establish(struct device *sdmmc, int (*fun)(void *),
! 605: void *arg, const char *name)
! 606: {
! 607: struct sdmmc_softc *sc = (struct sdmmc_softc *)sdmmc;
! 608: struct sdmmc_intr_handler *ih;
! 609: int s;
! 610:
! 611: if (sc->sct->card_intr_mask == NULL)
! 612: return NULL;
! 613:
! 614: ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK | M_CANFAIL);
! 615: if (ih == NULL)
! 616: return NULL;
! 617:
! 618: bzero(ih, sizeof *ih);
! 619: ih->ih_name = malloc(strlen(name), M_DEVBUF, M_WAITOK | M_CANFAIL);
! 620: if (ih->ih_name == NULL) {
! 621: free(ih, M_DEVBUF);
! 622: return NULL;
! 623: }
! 624: strlcpy(ih->ih_name, name, strlen(name));
! 625: ih->ih_softc = sc;
! 626: ih->ih_fun = fun;
! 627: ih->ih_arg = arg;
! 628:
! 629: s = splhigh();
! 630: if (TAILQ_EMPTY(&sc->sc_intrq)) {
! 631: sdmmc_intr_enable(sc->sc_fn0);
! 632: sdmmc_chip_card_intr_mask(sc->sct, sc->sch, 1);
! 633: }
! 634: TAILQ_INSERT_TAIL(&sc->sc_intrq, ih, entry);
! 635: splx(s);
! 636: return ih;
! 637: }
! 638:
! 639: /*
! 640: * Disestablish the given handler.
! 641: */
! 642: void
! 643: sdmmc_intr_disestablish(void *cookie)
! 644: {
! 645: struct sdmmc_intr_handler *ih = cookie;
! 646: struct sdmmc_softc *sc = ih->ih_softc;
! 647: int s;
! 648:
! 649: if (sc->sct->card_intr_mask == NULL)
! 650: return;
! 651:
! 652: s = splhigh();
! 653: TAILQ_REMOVE(&sc->sc_intrq, ih, entry);
! 654: if (TAILQ_EMPTY(&sc->sc_intrq)) {
! 655: sdmmc_chip_card_intr_mask(sc->sct, sc->sch, 0);
! 656: sdmmc_intr_disable(sc->sc_fn0);
! 657: }
! 658: splx(s);
! 659:
! 660: free(ih->ih_name, M_DEVBUF);
! 661: free(ih, M_DEVBUF);
! 662: }
! 663:
! 664: /*
! 665: * Call established SDIO card interrupt handlers. The host controller
! 666: * must call this function from its own interrupt handler to handle an
! 667: * SDIO interrupt from the card.
! 668: */
! 669: void
! 670: sdmmc_card_intr(struct device *sdmmc)
! 671: {
! 672: struct sdmmc_softc *sc = (struct sdmmc_softc *)sdmmc;
! 673:
! 674: if (sc->sct->card_intr_mask == NULL)
! 675: return;
! 676:
! 677: if (!sdmmc_task_pending(&sc->sc_intr_task))
! 678: sdmmc_add_task(sc, &sc->sc_intr_task);
! 679: }
! 680:
! 681: void
! 682: sdmmc_intr_task(void *arg)
! 683: {
! 684: struct sdmmc_softc *sc = arg;
! 685: struct sdmmc_intr_handler *ih;
! 686: int s;
! 687:
! 688: s = splhigh();
! 689: TAILQ_FOREACH(ih, &sc->sc_intrq, entry) {
! 690: splx(s);
! 691:
! 692: /* XXX examine return value and do evcount stuff*/
! 693: (void)ih->ih_fun(ih->ih_arg);
! 694:
! 695: s = splhigh();
! 696: }
! 697: sdmmc_chip_card_intr_ack(sc->sct, sc->sch);
! 698: splx(s);
! 699: }
CVSweb