diff options
author | Uwe Stuehler <uwe@cvs.openbsd.org> | 2007-05-31 10:09:02 +0000 |
---|---|---|
committer | Uwe Stuehler <uwe@cvs.openbsd.org> | 2007-05-31 10:09:02 +0000 |
commit | 9cc2c24e279ad4e26047eb4506b57737b8d3a6c1 (patch) | |
tree | 463f2464f8800783d1ae380c993bb4b0efd6169d /sys/dev | |
parent | ee5b69e63140ed7263ccc94ffbd034557b444d6f (diff) |
Add SDIO card interrupt handling code
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/sdmmc/sdhc.c | 41 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc.c | 4 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_io.c | 152 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_ioreg.h | 3 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcchip.h | 11 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcvar.h | 11 |
6 files changed, 206 insertions, 16 deletions
diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c index 423809e168b..e6183c79c43 100644 --- a/sys/dev/sdmmc/sdhc.c +++ b/sys/dev/sdmmc/sdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdhc.c,v 1.19 2007/05/26 19:04:24 uwe Exp $ */ +/* $OpenBSD: sdhc.c,v 1.20 2007/05/31 10:09:01 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -84,6 +84,8 @@ int sdhc_host_maxblklen(sdmmc_chipset_handle_t); int sdhc_card_detect(sdmmc_chipset_handle_t); int sdhc_bus_power(sdmmc_chipset_handle_t, u_int32_t); int sdhc_bus_clock(sdmmc_chipset_handle_t, int); +void sdhc_card_intr_mask(sdmmc_chipset_handle_t, int); +void sdhc_card_intr_ack(sdmmc_chipset_handle_t); void sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); int sdhc_start_command(struct sdhc_host *, struct sdmmc_command *); int sdhc_wait_state(struct sdhc_host *, u_int32_t, u_int32_t); @@ -113,7 +115,10 @@ struct sdmmc_chip_functions sdhc_functions = { sdhc_bus_power, sdhc_bus_clock, /* command execution */ - sdhc_exec_command + sdhc_exec_command, + /* card interrupt */ + sdhc_card_intr_mask, + sdhc_card_intr_ack }; struct cfdriver sdhc_cd = { @@ -333,8 +338,7 @@ sdhc_host_reset(sdmmc_chipset_handle_t sch) imask = SDHC_CARD_REMOVAL | SDHC_CARD_INSERTION | SDHC_BUFFER_READ_READY | SDHC_BUFFER_WRITE_READY | SDHC_DMA_INTERRUPT | SDHC_BLOCK_GAP_EVENT | - SDHC_TRANSFER_COMPLETE | SDHC_COMMAND_COMPLETE | - SDHC_CARD_INTERRUPT; + SDHC_TRANSFER_COMPLETE | SDHC_COMMAND_COMPLETE; HWRITE2(hp, SDHC_NINTR_STATUS_EN, imask); HWRITE2(hp, SDHC_EINTR_STATUS_EN, SDHC_EINTR_STATUS_MASK); @@ -512,6 +516,28 @@ ret: return error; } +void +sdhc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) +{ + struct sdhc_host *hp = sch; + + printf("sdhc_card_intr_mask enable=%d\n", enable); + if (enable) { + HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); + HSET2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT); + } else { + HCLR2(hp, SDHC_NINTR_SIGNAL_EN, SDHC_CARD_INTERRUPT); + HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); + } +} + +void +sdhc_card_intr_ack(sdmmc_chipset_handle_t sch) +{ + struct sdhc_host *hp = sch; + + HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); +} int sdhc_wait_state(struct sdhc_host *hp, u_int32_t mask, u_int32_t value) @@ -917,12 +943,9 @@ sdhc_intr(void *arg) * Service SD card interrupts. */ if (ISSET(status, SDHC_CARD_INTERRUPT)) { - printf("%s: card interrupt\n", HDEVNAME(hp)); + DPRINTF(0,("%s: card interrupt\n", HDEVNAME(hp))); HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); - /* XXX service card interrupt */ -#ifdef notyet - HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); -#endif + sdmmc_card_intr(hp->sdmmc); } } return done; diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c index 2de1084d913..1c2e5145e09 100644 --- a/sys/dev/sdmmc/sdmmc.c +++ b/sys/dev/sdmmc/sdmmc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc.c,v 1.11 2007/05/26 17:06:36 uwe Exp $ */ +/* $OpenBSD: sdmmc.c,v 1.12 2007/05/31 10:09:01 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -105,7 +105,9 @@ sdmmc_attach(struct device *parent, struct device *self, void *aux) SIMPLEQ_INIT(&sc->sf_head); TAILQ_INIT(&sc->sc_tskq); + TAILQ_INIT(&sc->sc_intrq); sdmmc_init_task(&sc->sc_discover_task, sdmmc_discover_task, sc); + sdmmc_init_task(&sc->sc_intr_task, sdmmc_intr_task, sc); lockinit(&sc->sc_lock, PRIBIO, DEVNAME(sc), 0, LK_CANRECURSE); #ifdef SDMMC_IOCTL diff --git a/sys/dev/sdmmc/sdmmc_io.c b/sys/dev/sdmmc/sdmmc_io.c index cfce95d6d09..ce39c98ce78 100644 --- a/sys/dev/sdmmc/sdmmc_io.c +++ b/sys/dev/sdmmc/sdmmc_io.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc_io.c,v 1.7 2007/05/26 18:37:45 uwe Exp $ */ +/* $OpenBSD: sdmmc_io.c,v 1.8 2007/05/31 10:09:01 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -20,6 +20,7 @@ #include <sys/param.h> #include <sys/kernel.h> +#include <sys/malloc.h> #include <sys/proc.h> #include <sys/systm.h> @@ -28,6 +29,14 @@ #include <dev/sdmmc/sdmmcreg.h> #include <dev/sdmmc/sdmmcvar.h> +struct sdmmc_intr_handler { + struct sdmmc_softc *ih_softc; + char *ih_name; + int (*ih_fun)(void *); + void *ih_arg; + TAILQ_ENTRY(sdmmc_intr_handler) entry; +}; + int sdmmc_submatch(struct device *, void *, void *); int sdmmc_print(void *, const char *); int sdmmc_io_rw_direct(struct sdmmc_softc *, struct sdmmc_function *, @@ -322,6 +331,8 @@ sdmmc_io_detach(struct sdmmc_softc *sc) sf->child = NULL; } } + + KASSERT(TAILQ_EMPTY(&sc->sc_intrq)); } int @@ -349,7 +360,7 @@ sdmmc_io_rw_direct(struct sdmmc_softc *sc, struct sdmmc_function *sf, bzero(&cmd, sizeof cmd); cmd.c_opcode = SD_IO_RW_DIRECT; cmd.c_arg = arg; - cmd.c_flags = SCF_CMD_BC/* XXX */ | SCF_RSP_R5; + cmd.c_flags = SCF_CMD_AC | SCF_RSP_R5; error = sdmmc_mmc_command(sc, &cmd); *datap = SD_R5_DATA(cmd.c_resp); @@ -391,7 +402,7 @@ sdmmc_io_rw_extended(struct sdmmc_softc *sc, struct sdmmc_function *sf, bzero(&cmd, sizeof cmd); cmd.c_opcode = SD_IO_RW_EXTENDED; cmd.c_arg = arg; - cmd.c_flags = SCF_CMD_ADTC/* XXX */ | SCF_RSP_R5; + cmd.c_flags = SCF_CMD_AC | SCF_RSP_R5; cmd.c_data = datap; cmd.c_datalen = datalen; cmd.c_blklen = MIN(datalen, sdmmc_chip_host_maxblklen(sc->sct, sc->sch)); @@ -529,3 +540,138 @@ sdmmc_io_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, u_int32_t *ocrp) SDMMC_UNLOCK(sc); return error; } + +/* + * Card interrupt handling + */ + +void +sdmmc_intr_enable(struct sdmmc_function *sf) +{ + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_function *sf0 = sc->sc_fn0; + u_int8_t imask; + + SDMMC_LOCK(sc); + imask = sdmmc_io_read_1(sf0, SD_IO_CCCR_INT_ENABLE); + imask |= 1 << sf->number; + sdmmc_io_write_1(sf0, SD_IO_CCCR_INT_ENABLE, imask); + SDMMC_UNLOCK(sc); +} + +void +sdmmc_intr_disable(struct sdmmc_function *sf) +{ + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_function *sf0 = sc->sc_fn0; + u_int8_t imask; + + SDMMC_LOCK(sc); + imask = sdmmc_io_read_1(sf0, SD_IO_CCCR_INT_ENABLE); + imask &= ~(1 << sf->number); + sdmmc_io_write_1(sf0, SD_IO_CCCR_INT_ENABLE, imask); + SDMMC_UNLOCK(sc); +} + +/* + * Establish a handler for the SDIO card interrupt. Because the + * interrupt may be shared with different SDIO functions, multiple + * handlers can be established. + */ +void * +sdmmc_intr_establish(struct device *sdmmc, int (*fun)(void *), + void *arg, const char *name) +{ + struct sdmmc_softc *sc = (struct sdmmc_softc *)sdmmc; + struct sdmmc_intr_handler *ih; + int s; + + if (sc->sct->card_intr_mask == NULL) + return NULL; + + ih = malloc(sizeof *ih, M_DEVBUF, M_WAITOK | M_CANFAIL); + if (ih == NULL) + return NULL; + + bzero(ih, sizeof *ih); + ih->ih_name = malloc(strlen(name), M_DEVBUF, M_WAITOK | M_CANFAIL); + if (ih->ih_name == NULL) { + free(ih, M_DEVBUF); + return NULL; + } + strlcpy(ih->ih_name, name, strlen(name)); + ih->ih_softc = sc; + ih->ih_fun = fun; + ih->ih_arg = arg; + + s = splhigh(); + if (TAILQ_EMPTY(&sc->sc_intrq)) { + sdmmc_intr_enable(sc->sc_fn0); + sdmmc_chip_card_intr_mask(sc->sct, sc->sch, 1); + } + TAILQ_INSERT_TAIL(&sc->sc_intrq, ih, entry); + splx(s); + return ih; +} + +/* + * Disestablish the given handler. + */ +void +sdmmc_intr_disestablish(void *cookie) +{ + struct sdmmc_intr_handler *ih = cookie; + struct sdmmc_softc *sc = ih->ih_softc; + int s; + + if (sc->sct->card_intr_mask == NULL) + return; + + s = splhigh(); + TAILQ_REMOVE(&sc->sc_intrq, ih, entry); + if (TAILQ_EMPTY(&sc->sc_intrq)) { + sdmmc_chip_card_intr_mask(sc->sct, sc->sch, 0); + sdmmc_intr_disable(sc->sc_fn0); + } + splx(s); + + free(ih->ih_name, M_DEVBUF); + free(ih, M_DEVBUF); +} + +/* + * Call established SDIO card interrupt handlers. The host controller + * must call this function from its own interrupt handler to handle an + * SDIO interrupt from the card. + */ +void +sdmmc_card_intr(struct device *sdmmc) +{ + struct sdmmc_softc *sc = (struct sdmmc_softc *)sdmmc; + + if (sc->sct->card_intr_mask == NULL) + return; + + if (!sdmmc_task_pending(&sc->sc_intr_task)) + sdmmc_add_task(sc, &sc->sc_intr_task); +} + +void +sdmmc_intr_task(void *arg) +{ + struct sdmmc_softc *sc = arg; + struct sdmmc_intr_handler *ih; + int s; + + s = splhigh(); + TAILQ_FOREACH(ih, &sc->sc_intrq, entry) { + splx(s); + + /* XXX examine return value and do evcount stuff*/ + (void)ih->ih_fun(ih->ih_arg); + + s = splhigh(); + } + sdmmc_chip_card_intr_ack(sc->sct, sc->sch); + splx(s); +} diff --git a/sys/dev/sdmmc/sdmmc_ioreg.h b/sys/dev/sdmmc/sdmmc_ioreg.h index 481b8d56230..689d0596c6e 100644 --- a/sys/dev/sdmmc/sdmmc_ioreg.h +++ b/sys/dev/sdmmc/sdmmc_ioreg.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc_ioreg.h,v 1.2 2007/05/26 18:37:44 uwe Exp $ */ +/* $OpenBSD: sdmmc_ioreg.h,v 1.3 2007/05/31 10:09:01 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -64,6 +64,7 @@ #define SD_IO_CCCR_SIZE 0x100 #define SD_IO_CCCR_FN_ENABLE 0x02 #define SD_IO_CCCR_FN_READY 0x03 +#define SD_IO_CCCR_INT_ENABLE 0x04 #define SD_IO_CCCR_CTL 0x06 #define CCCR_CTL_RES (1<<3) #define SD_IO_CCCR_BUS_WIDTH 0x07 diff --git a/sys/dev/sdmmc/sdmmcchip.h b/sys/dev/sdmmc/sdmmcchip.h index 9cd05c0bf3e..93969149674 100644 --- a/sys/dev/sdmmc/sdmmcchip.h +++ b/sys/dev/sdmmc/sdmmcchip.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmcchip.h,v 1.2 2006/07/18 04:10:35 uwe Exp $ */ +/* $OpenBSD: sdmmcchip.h,v 1.3 2007/05/31 10:09:01 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -38,6 +38,9 @@ struct sdmmc_chip_functions { /* command execution */ void (*exec_command)(sdmmc_chipset_handle_t, struct sdmmc_command *); + /* card interrupt */ + void (*card_intr_mask)(sdmmc_chipset_handle_t, int); + void (*card_intr_ack)(sdmmc_chipset_handle_t); }; /* host controller reset */ @@ -59,6 +62,11 @@ struct sdmmc_chip_functions { /* command execution */ #define sdmmc_chip_exec_command(tag, handle, cmdp) \ ((tag)->exec_command((handle), (cmdp))) +/* card interrupt */ +#define sdmmc_chip_card_intr_mask(tag, handle, enable) \ + ((tag)->card_intr_mask((handle), (enable))) +#define sdmmc_chip_card_intr_ack(tag, handle) \ + ((tag)->card_intr_ack((handle))) /* clock frequencies for sdmmc_chip_bus_clock() */ #define SDMMC_SDCLK_OFF 0 @@ -72,6 +80,7 @@ struct sdmmcbus_attach_args { }; void sdmmc_needs_discover(struct device *); +void sdmmc_card_intr(struct device *); void sdmmc_delay(u_int); #endif diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h index 960fe68daa4..6e7610fe89a 100644 --- a/sys/dev/sdmmc/sdmmcvar.h +++ b/sys/dev/sdmmc/sdmmcvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmcvar.h,v 1.8 2007/05/26 18:37:45 uwe Exp $ */ +/* $OpenBSD: sdmmcvar.h,v 1.9 2007/05/31 10:09:01 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -167,8 +167,10 @@ struct sdmmc_softc { struct proc *sc_task_thread; /* asynchronous tasks */ TAILQ_HEAD(, sdmmc_task) sc_tskq; /* task thread work queue */ struct sdmmc_task sc_discover_task; /* card attach/detach task */ + struct sdmmc_task sc_intr_task; /* card interrupt task */ struct lock sc_lock; /* lock around host controller */ void *sc_scsibus; /* SCSI bus emulation softc */ + TAILQ_HEAD(, sdmmc_intr_handler) sc_intrq; /* interrupt handlers */ }; /* @@ -198,6 +200,13 @@ int sdmmc_select_card(struct sdmmc_softc *, struct sdmmc_function *); int sdmmc_set_relative_addr(struct sdmmc_softc *, struct sdmmc_function *); +void sdmmc_intr_enable(struct sdmmc_function *); +void sdmmc_intr_disable(struct sdmmc_function *); +void *sdmmc_intr_establish(struct device *, int (*)(void *), + void *, const char *); +void sdmmc_intr_disestablish(void *); +void sdmmc_intr_task(void *); + int sdmmc_io_enable(struct sdmmc_softc *); void sdmmc_io_scan(struct sdmmc_softc *); int sdmmc_io_init(struct sdmmc_softc *, struct sdmmc_function *); |