diff options
author | Uwe Stuehler <uwe@cvs.openbsd.org> | 2006-07-18 04:10:36 +0000 |
---|---|---|
committer | Uwe Stuehler <uwe@cvs.openbsd.org> | 2006-07-18 04:10:36 +0000 |
commit | 8b5e60da0ed541ea61113a1a3ceb5bf9d6da74b2 (patch) | |
tree | f5ea5823833927e220f2e244ce61c5bcff254c7d /sys/dev | |
parent | 7188f4169d0b60dba8f97ef2305a65737b31d6df (diff) |
First and foremost, avoid an obious race between two or more processes
trying to get MMC commands through to the SD/MMC host controller via the
emulated SCSI layer. This is achieved by ensuring exclusive access to
the host controller for one process during any MMC command and during a
sequence of commands at the sdmmc(4) layer.
While at it, the command processing thread has been moved to sdmmc(4),
so as to simplify the implementation of future host controller drivers.
This should also pave the way for further cleanup of the code and for
new drivers.
Minor cleanups are included in this commit, as well.
Diffstat (limited to 'sys/dev')
-rw-r--r-- | sys/dev/sdmmc/sdhc.c | 452 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc.c | 326 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_io.c | 34 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_mem.c | 41 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_scsi.c | 336 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_scsi.h | 14 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcchip.h | 9 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcvar.h | 55 |
8 files changed, 805 insertions, 462 deletions
diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c index 53e111164fe..847dcc06c4c 100644 --- a/sys/dev/sdmmc/sdhc.c +++ b/sys/dev/sdmmc/sdhc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdhc.c,v 1.8 2006/07/17 20:50:58 fgsch Exp $ */ +/* $OpenBSD: sdhc.c,v 1.9 2006/07/18 04:10:35 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -27,7 +27,6 @@ #include <sys/kthread.h> #include <sys/malloc.h> #include <sys/systm.h> -#include <sys/timeout.h> #include <dev/sdmmc/sdhcreg.h> #include <dev/sdmmc/sdhcvar.h> @@ -35,8 +34,9 @@ #include <dev/sdmmc/sdmmcreg.h> #include <dev/sdmmc/sdmmcvar.h> -#define SDHC_COMMAND_TIMEOUT (hz*2) /* 2 seconds */ -#define SDHC_DATA_TIMEOUT (hz*2) /* 2 seconds */ +#define SDHC_COMMAND_TIMEOUT hz +#define SDHC_BUFFER_TIMEOUT hz +#define SDHC_TRANSFER_TIMEOUT hz struct sdhc_host { struct sdhc_softc *sc; /* host controller device */ @@ -47,18 +47,15 @@ struct sdhc_host { int maxblklen; /* maximum block length */ int flags; /* flags for this host */ u_int32_t ocr; /* OCR value from capabilities */ - struct proc *event_thread; /* event processing thread */ - struct sdmmc_command *cmd; /* current command or NULL */ - struct timeout cmd_to; /* command timeout */ u_int8_t regs[14]; /* host controller state */ + u_int16_t intr_status; /* soft interrupt status */ + u_int16_t intr_error_status; /* soft error status */ }; -#define HDEVNAME(hp) ((hp)->sdmmc->dv_xname) +#define HDEVNAME(hp) ((hp)->sc->sc_dev.dv_xname) /* flag values */ #define SHF_USE_DMA 0x0001 -#define SHF_CARD_PRESENT 0x0002 -#define SHF_CARD_ATTACHED 0x0004 #define HREAD1(hp, reg) \ (bus_space_read_1((hp)->iot, (hp)->ioh, (reg))) @@ -81,25 +78,20 @@ struct sdhc_host { #define HSET2(hp, reg, bits) \ HWRITE2((hp), (reg), HREAD2((hp), (reg)) | (bits)) -void sdhc_create_event_thread(void *); -void sdhc_event_thread(void *); -void sdhc_event_process(struct sdhc_host *); - int sdhc_host_reset(sdmmc_chipset_handle_t); u_int32_t sdhc_host_ocr(sdmmc_chipset_handle_t); 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); -int sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); -int sdhc_wait_state(struct sdhc_host *, u_int32_t, u_int32_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_command(struct sdhc_host *, int); -int sdhc_finish_command(struct sdhc_host *); -void sdhc_transfer_data(struct sdhc_host *); +int sdhc_wait_state(struct sdhc_host *, u_int32_t, u_int32_t); +void sdhc_soft_reset(struct sdhc_host *); +int sdhc_wait_intr(struct sdhc_host *, int, int); +void sdhc_transfer_data(struct sdhc_host *, struct sdmmc_command *); void sdhc_read_data(struct sdhc_host *, u_char *, int); void sdhc_write_data(struct sdhc_host *, u_char *, int); -void sdhc_command_timeout(void *); #ifdef SDHC_DEBUG void sdhc_dump_regs(struct sdhc_host *); @@ -166,7 +158,6 @@ sdhc_host_found(struct sdhc_softc *sc, bus_space_tag_t iot, hp->sc = sc; hp->iot = iot; hp->ioh = ioh; - timeout_set(&hp->cmd_to, sdhc_command_timeout, hp); /* * Reset the host controller and enable interrupts. @@ -245,85 +236,16 @@ sdhc_host_found(struct sdhc_softc *sc, bus_space_tag_t iot, error = 0; goto err; } - - /* - * Create the event thread that will attach and detach cards - * and perform other lengthy operations. - */ -#ifdef DO_CONFIG_PENDING - config_pending_incr(); -#endif - kthread_create_deferred(sdhc_create_event_thread, hp); return 0; err: - timeout_del(&hp->cmd_to); FREE(hp, M_DEVBUF); sc->sc_host[sc->sc_nhosts - 1] = NULL; sc->sc_nhosts--; return (error); } -void -sdhc_create_event_thread(void *arg) -{ - struct sdhc_host *hp = arg; - - /* If there's a card, attach it. */ - sdhc_event_process(hp); - - if (kthread_create(sdhc_event_thread, hp, &hp->event_thread, - HDEVNAME(hp)) != 0) - printf("%s: can't create event thread\n", HDEVNAME(hp)); - -#ifdef DO_CONFIG_PENDING - config_pending_decr(); -#endif -} - -void -sdhc_event_thread(void *arg) -{ - struct sdhc_host *hp = arg; - - for (;;) { - (void)tsleep((caddr_t)hp, PWAIT, "sdhcev", 0); - sdhc_event_process(hp); - } -} - -void -sdhc_event_process(struct sdhc_host *hp) -{ - /* If there's a card, attach it, if it went away, detach it. */ - if (sdhc_card_detect(hp)) { - if (!ISSET(hp->flags, SHF_CARD_PRESENT)) { - SET(hp->flags, SHF_CARD_PRESENT); - if (sdmmc_card_attach(hp->sdmmc) == 0) - SET(hp->flags, SHF_CARD_ATTACHED); - } - } else { - /* XXX If a command was in progress, abort it. */ - int s = splsdmmc(); - if (hp->cmd != NULL) { - timeout_del(&hp->cmd_to); - printf("%s: interrupted command %u\n", - HDEVNAME(hp), hp->cmd->c_opcode); - hp->cmd = NULL; - } - splx(s); - - if (ISSET(hp->flags, SHF_CARD_PRESENT)) { - CLR(hp->flags, SHF_CARD_PRESENT); - if (ISSET(hp->flags, SHF_CARD_ATTACHED)) { - sdmmc_card_detach(hp->sdmmc, DETACH_FORCE); - CLR(hp->flags, SHF_CARD_ATTACHED); - } - } - } -} - /* * Power hook established by or called from attachment driver. */ @@ -597,39 +519,6 @@ ret: return error; } -/* - * Send a command and data to the card and return the command response - * and data from the card. - * - * If no callback function is specified, execute the command - * synchronously; otherwise, return immediately and call the function - * from the event thread after the command has completed. - */ -int -sdhc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) -{ - struct sdhc_host *hp = sch; - int error; - - if (hp->cmd != NULL) - return EBUSY; - - error = sdhc_start_command(hp, cmd); - if (error != 0) - goto err; - if (cmd->c_done != NULL) - /* Execute this command asynchronously. */ - return error; - - error = sdhc_wait_command(hp, SCF_DONE|SCF_CMD_DONE); - if (error != 0) - goto err; - return sdhc_finish_command(hp); - err: - cmd->c_error = error; - SET(cmd->c_flags, SCF_DONE); - return sdhc_finish_command(hp); -} int sdhc_wait_state(struct sdhc_host *hp, u_int32_t mask, u_int32_t value) @@ -648,6 +537,64 @@ sdhc_wait_state(struct sdhc_host *hp, u_int32_t mask, u_int32_t value) return ETIMEDOUT; } +void +sdhc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct sdhc_host *hp = sch; + int error; + + /* + * Start the MMC command, or mark `cmd' as failed and return. + */ + error = sdhc_start_command(hp, cmd); + if (error != 0) { + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); + return; + } + + /* + * Wait until the command phase is done, or until the command + * is marked done for any other reason. + */ + if (!sdhc_wait_intr(hp, SDHC_COMMAND_COMPLETE, + SDHC_COMMAND_TIMEOUT)) { + cmd->c_error = ETIMEDOUT; + SET(cmd->c_flags, SCF_ITSDONE); + return; + } + + /* + * The host controller removes bits [0:7] from the response + * data (CRC) and we pass the data up unchanged to the bus + * driver (without padding). + */ + if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { + if (ISSET(cmd->c_flags, SCF_RSP_136)) { + u_char *p = (u_char *)cmd->c_resp; + int i; + + for (i = 0; i < 15; i++) + *p++ = HREAD1(hp, SDHC_RESPONSE + i); + } else + cmd->c_resp[0] = HREAD4(hp, SDHC_RESPONSE); + } + + /* + * If the command has data to transfer in any direction, + * execute the transfer now. + */ + if (cmd->c_error == 0 && cmd->c_data != NULL) + sdhc_transfer_data(hp, cmd); + + /* Turn off the LED. */ + HCLR1(hp, SDHC_HOST_CTL, SDHC_LED_ON); + + DPRINTF(("%s: cmd %u done (flags=%#x error=%d)\n", + HDEVNAME(hp), cmd->c_opcode, cmd->c_flags, cmd->c_error)); + SET(cmd->c_flags, SCF_ITSDONE); +} + int sdhc_start_command(struct sdhc_host *hp, struct sdmmc_command *cmd) { @@ -658,13 +605,9 @@ sdhc_start_command(struct sdhc_host *hp, struct sdmmc_command *cmd) int error; int s; - DPRINTF(("%s: start cmd %u arg=%#x\n", HDEVNAME(hp), cmd->c_opcode, - cmd->c_arg)); - hp->cmd = cmd; - - /* If the card went away, finish the command immediately. */ - if (!ISSET(hp->flags, SHF_CARD_PRESENT)) - return ETIMEDOUT; + DPRINTF(("%s: start cmd %u arg=%#x proc=%#x\"%s\"\n", + HDEVNAME(hp), cmd->c_opcode, cmd->c_arg, curproc, + curproc ? curproc->p_comm : "")); /* * The maximum block length for commands should be the minimum @@ -748,97 +691,13 @@ sdhc_start_command(struct sdhc_host *hp, struct sdmmc_command *cmd) HWRITE2(hp, SDHC_TRANSFER_MODE, mode); HWRITE2(hp, SDHC_COMMAND, command); - /* - * Start a software timeout. In the unlikely event that the - * controller's own timeout detection mechanism fails we will - * abort the transfer in software. - */ - timeout_add(&hp->cmd_to, SDHC_COMMAND_TIMEOUT); - splx(s); return 0; } -int -sdhc_wait_command(struct sdhc_host *hp, int flags) -{ - int s; - - for (;;) { - /* Return if the command was aborted. */ - if (hp->cmd == NULL) - return EIO; - - s = splsdmmc(); - - /* Return if the command has reached the awaited state. */ - if (ISSET(hp->cmd->c_flags, flags)) { - splx(s); - return 0; - } - - (void)tsleep((caddr_t)hp, PWAIT, "sdhccmd", 0); - - /* Process card events. */ - sdhc_event_process(hp); - - splx(s); - } -} - -int -sdhc_finish_command(struct sdhc_host *hp) -{ - struct sdmmc_command *cmd = hp->cmd; - int error; - - if (cmd == NULL) { - DPRINTF(("%s: finish NULL cmd\n", HDEVNAME(hp))); - return 0; - } - - /* Cancel command timeout. */ - timeout_del(&hp->cmd_to); - - /* - * The host controller removes bits [0:7] from the response - * data (CRC) and we pass the data up unchanged to the bus - * driver (without padding). - */ - if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { - if (ISSET(cmd->c_flags, SCF_RSP_136)) { - u_char *p = (u_char *)cmd->c_resp; - int i; - - for (i = 0; i < 15; i++) - *p++ = HREAD1(hp, SDHC_RESPONSE + i); - } else - cmd->c_resp[0] = HREAD4(hp, SDHC_RESPONSE); - } - - if (cmd->c_error == 0 && cmd->c_data != NULL) { - timeout_add(&hp->cmd_to, SDHC_DATA_TIMEOUT); - sdhc_transfer_data(hp); - } - - /* Turn off the LED. */ - HCLR1(hp, SDHC_HOST_CTL, SDHC_LED_ON); - - error = cmd->c_error; - hp->cmd = NULL; - - SET(cmd->c_flags, SCF_DONE); - DPRINTF(("%s: cmd %u done (flags=%#x error=%d)\n", - HDEVNAME(hp), cmd->c_opcode, cmd->c_flags, error)); - if (cmd->c_done != NULL) - cmd->c_done(hp->sdmmc, cmd); - return error; -} - void -sdhc_transfer_data(struct sdhc_host *hp) +sdhc_transfer_data(struct sdhc_host *hp, struct sdmmc_command *cmd) { - struct sdmmc_command *cmd = hp->cmd; u_char *datap = cmd->c_data; int i, datalen; int mask; @@ -849,15 +708,17 @@ sdhc_transfer_data(struct sdhc_host *hp) error = 0; datalen = cmd->c_datalen; - DPRINTF(("%s: resp=%#x ", HDEVNAME(hp), MMC_R1(cmd->c_resp))); - DPRINTF(("datalen %u\n", datalen)); + DPRINTF(("%s: resp=%#x datalen %u\n", HDEVNAME(hp), + MMC_R1(cmd->c_resp), datalen)); + while (datalen > 0) { - error = sdhc_wait_command(hp, SCF_DONE|SCF_BUF_READY); - if (error != 0) + if (!sdhc_wait_intr(hp, SDHC_BUFFER_READ_READY| + SDHC_BUFFER_WRITE_READY, SDHC_BUFFER_TIMEOUT)) { + error = ETIMEDOUT; break; + } - error = sdhc_wait_state(hp, mask, mask); - if (error != 0) + if ((error = sdhc_wait_state(hp, mask, mask)) != 0) break; i = MIN(datalen, cmd->c_blklen); @@ -868,18 +729,15 @@ sdhc_transfer_data(struct sdhc_host *hp) datap += i; datalen -= i; - CLR(cmd->c_flags, SCF_BUF_READY); } - if (error == 0) - error = sdhc_wait_command(hp, SCF_DONE|SCF_XFR_DONE); - - timeout_del(&hp->cmd_to); + if (error == 0 && !sdhc_wait_intr(hp, SDHC_TRANSFER_COMPLETE, + SDHC_TRANSFER_TIMEOUT)) + error = ETIMEDOUT; - if (cmd->c_error == 0) { + if (error != 0) cmd->c_error = error; - SET(cmd->c_flags, SCF_DONE); - } + SET(cmd->c_flags, SCF_ITSDONE); DPRINTF(("%s: data transfer done (error=%d)\n", HDEVNAME(hp), cmd->c_error)); @@ -922,31 +780,47 @@ sdhc_write_data(struct sdhc_host *hp, u_char *datap, int datalen) } } +/* Prepare for another command. */ void -sdhc_command_timeout(void *arg) +sdhc_soft_reset(struct sdhc_host *hp) { - struct sdhc_host *hp = arg; - struct sdmmc_command *cmd = hp->cmd; + DPRINTF(("%s: software reset\n", HDEVNAME(hp))); + HWRITE1(hp, SDHC_SOFTWARE_RESET, SDHC_RESET_DAT|SDHC_RESET_CMD); + sdmmc_delay(10000); +} + +int +sdhc_wait_intr(struct sdhc_host *hp, int mask, int timo) +{ + int status; int s; - if (cmd == NULL) - return; + mask |= SDHC_ERROR_INTERRUPT; s = splsdmmc(); - if (!ISSET(cmd->c_flags, SCF_DONE)) { - DPRINTF(("%s: timeout cmd %u, resetting...\n", - HDEVNAME(hp), cmd->c_opcode)); - cmd->c_error = ETIMEDOUT; - SET(cmd->c_flags, SCF_DONE); - HWRITE1(hp, SDHC_SOFTWARE_RESET, SDHC_RESET_DAT| - SDHC_RESET_CMD); - timeout_add(&hp->cmd_to, hz/2); - } else { - DPRINTF(("%s: timeout cmd %u, resetting...done\n", - HDEVNAME(hp), cmd->c_opcode)); - wakeup(hp); + status = hp->intr_status & mask; + while (status == 0) { + if (tsleep(&hp->intr_status, PWAIT, "hcintr", timo) + == EWOULDBLOCK) { + status |= SDHC_ERROR_INTERRUPT; + break; + } + status = hp->intr_status & mask; + } + hp->intr_status &= ~status; + + DPRINTF(("%s: intr status %#x error %#x\n", HDEVNAME(hp), status, + hp->intr_error_status)); + + /* Command timeout has higher priority than command complete. */ + if (ISSET(status, SDHC_ERROR_INTERRUPT)) { + hp->intr_error_status = 0; + sdhc_soft_reset(hp); + status = 0; } + splx(s); + return status; } /* @@ -956,8 +830,8 @@ int sdhc_intr(void *arg) { struct sdhc_softc *sc = arg; - int done = 0; int host; + int done = 0; /* We got an interrupt, but we don't know from which slot. */ for (host = 0; host < sc->sc_nhosts; host++) { @@ -967,78 +841,62 @@ sdhc_intr(void *arg) if (hp == NULL) continue; + /* Acknowledge interrupts we are about to handle. */ status = HREAD2(hp, SDHC_NINTR_STATUS); + HWRITE2(hp, SDHC_NINTR_STATUS, status); + DPRINTF(("%s: interrupt status=%b\n", HDEVNAME(hp), + status, SDHC_NINTR_STATUS_BITS)); + if (!ISSET(status, SDHC_NINTR_STATUS_MASK)) continue; - /* Clear interrupts we are about to handle. */ - HWRITE2(hp, SDHC_NINTR_STATUS, status); -#ifdef SDHC_DEBUG - printf("%s: interrupt status=%b\n", HDEVNAME(hp), - status, SDHC_NINTR_STATUS_BITS); -#endif + /* Claim this interrupt. */ + done = 1; /* - * Wake up the event thread to service the interrupt(s). + * Service error interrupts. */ - if (ISSET(status, SDHC_BUFFER_READ_READY| - SDHC_BUFFER_WRITE_READY)) { - if (hp->cmd != NULL && - !ISSET(hp->cmd->c_flags, SCF_DONE)) { - SET(hp->cmd->c_flags, SCF_BUF_READY); - wakeup(hp); - } - done++; - } - if (ISSET(status, SDHC_COMMAND_COMPLETE)) { - if (hp->cmd != NULL && - !ISSET(hp->cmd->c_flags, SCF_DONE)) { - SET(hp->cmd->c_flags, SCF_CMD_DONE); - wakeup(hp); - } - done++; - } - if (ISSET(status, SDHC_TRANSFER_COMPLETE)) { - if (hp->cmd != NULL && - !ISSET(hp->cmd->c_flags, SCF_DONE)) { - SET(hp->cmd->c_flags, SCF_XFR_DONE); - wakeup(hp); - } - done++; - } - if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)) { - wakeup(hp); - done++; - } - if (ISSET(status, SDHC_ERROR_INTERRUPT)) { u_int16_t error; + /* Acknowledge error interrupts. */ error = HREAD2(hp, SDHC_EINTR_STATUS); HWRITE2(hp, SDHC_EINTR_STATUS, error); - DPRINTF(("%s: error interrupt, status=%b\n", HDEVNAME(hp), error, SDHC_EINTR_STATUS_BITS)); - /* XXX command timeout has higher priority - * than command complete */ if (ISSET(error, SDHC_CMD_TIMEOUT_ERROR| - SDHC_DATA_TIMEOUT_ERROR) && hp->cmd != NULL && - !ISSET(hp->cmd->c_flags, SCF_DONE)) { - hp->cmd->c_error = ETIMEDOUT; - SET(hp->cmd->c_flags, SCF_DONE); - /* XXX can this reset be avoided? */ - HWRITE1(hp, SDHC_SOFTWARE_RESET, - SDHC_RESET_DAT|SDHC_RESET_CMD); - timeout_add(&hp->cmd_to, hz/2); + SDHC_DATA_TIMEOUT_ERROR)) { + hp->intr_error_status |= error; + hp->intr_status |= status; + wakeup(&hp->intr_status); } - done++; } + /* + * Wake up the sdmmc event thread to scan for cards. + */ + if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)) + sdmmc_needs_discover(hp->sdmmc); + + /* + * Wake up the blocking process to service command + * related interrupt(s). + */ + if (ISSET(status, SDHC_BUFFER_READ_READY| + SDHC_BUFFER_WRITE_READY|SDHC_COMMAND_COMPLETE| + SDHC_TRANSFER_COMPLETE)) { + hp->intr_status |= status; + wakeup(&hp->intr_status); + } + + /* + * Service SD card interrupts. + */ if (ISSET(status, SDHC_CARD_INTERRUPT)) { + printf("%s: card interrupt\n", HDEVNAME(hp)); HCLR2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); /* XXX service card interrupt */ - printf("%s: card interrupt\n", HDEVNAME(hp)); HSET2(hp, SDHC_NINTR_STATUS_EN, SDHC_CARD_INTERRUPT); } } diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c index 3b89507cb08..fa8890e7c3f 100644 --- a/sys/dev/sdmmc/sdmmc.c +++ b/sys/dev/sdmmc/sdmmc.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc.c,v 1.6 2006/06/29 01:35:37 uwe Exp $ */ +/* $OpenBSD: sdmmc.c,v 1.7 2006/07/18 04:10:35 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -25,6 +25,7 @@ #include <sys/param.h> #include <sys/device.h> #include <sys/kernel.h> +#include <sys/kthread.h> #include <sys/malloc.h> #include <sys/proc.h> #include <sys/systm.h> @@ -40,11 +41,19 @@ int sdmmc_match(struct device *, void *, void *); void sdmmc_attach(struct device *, struct device *, void *); +int sdmmc_detach(struct device *, int); +void sdmmc_create_thread(void *); +void sdmmc_task_thread(void *); +void sdmmc_discover_task(void *); +void sdmmc_card_attach(struct sdmmc_softc *); +void sdmmc_card_detach(struct sdmmc_softc *, int); int sdmmc_enable(struct sdmmc_softc *); void sdmmc_disable(struct sdmmc_softc *); int sdmmc_scan(struct sdmmc_softc *); int sdmmc_init(struct sdmmc_softc *); -int sdmmc_set_bus_width(struct sdmmc_softc *, struct sdmmc_function *); +int sdmmc_set_bus_width(struct sdmmc_function *); + +#define DEVNAME(sc) SDMMCDEVNAME(sc) #ifdef SDMMC_DEBUG #define DPRINTF(s) printf s @@ -53,7 +62,7 @@ int sdmmc_set_bus_width(struct sdmmc_softc *, struct sdmmc_function *); #endif struct cfattach sdmmc_ca = { - sizeof(struct sdmmc_softc), sdmmc_match, sdmmc_attach + sizeof(struct sdmmc_softc), sdmmc_match, sdmmc_attach, sdmmc_detach }; struct cfdriver sdmmc_cd = { @@ -81,25 +90,150 @@ sdmmc_attach(struct device *parent, struct device *self, void *aux) sc->sch = saa->sch; SIMPLEQ_INIT(&sc->sf_head); + TAILQ_INIT(&sc->sc_tskq); + sdmmc_init_task(&sc->sc_discover_task, sdmmc_discover_task, sc); + lockinit(&sc->sc_lock, PRIBIO, DEVNAME(sc), 0, LK_CANRECURSE); + + /* + * Create the event thread that will attach and detach cards + * and perform other lengthy operations. + */ +#ifdef DO_CONFIG_PENDING + config_pending_incr(); +#endif + kthread_create_deferred(sdmmc_create_thread, sc); +} + +int +sdmmc_detach(struct device *self, int flags) +{ + struct sdmmc_softc *sc = (struct sdmmc_softc *)self; + + sc->sc_dying = 1; + while (sc->sc_task_thread != NULL) { + wakeup(&sc->sc_tskq); + tsleep(sc, PWAIT, "mmcdie", 0); + } + return 0; +} + +void +sdmmc_create_thread(void *arg) +{ + struct sdmmc_softc *sc = arg; + + if (kthread_create(sdmmc_task_thread, sc, &sc->sc_task_thread, + "%s", DEVNAME(sc)) != 0) + printf("%s: can't create task thread\n", DEVNAME(sc)); + +#ifdef DO_CONFIG_PENDING + config_pending_decr(); +#endif +} + +void +sdmmc_task_thread(void *arg) +{ + struct sdmmc_softc *sc = arg; + struct sdmmc_task *task; + int s; + + sdmmc_needs_discover(&sc->sc_dev); + + s = splsdmmc(); + while (!sc->sc_dying) { + for (task = TAILQ_FIRST(&sc->sc_tskq); task != NULL; + task = TAILQ_FIRST(&sc->sc_tskq)) { + splx(s); + sdmmc_del_task(task); + task->func(task->arg); + s = splsdmmc(); + } + tsleep(&sc->sc_tskq, PWAIT, "mmctsk", 0); + } + splx(s); + + if (ISSET(sc->sc_flags, SMF_CARD_PRESENT)) + sdmmc_card_detach(sc, DETACH_FORCE); + + sc->sc_task_thread = NULL; + wakeup(sc); + kthread_exit(0); +} + +void +sdmmc_add_task(struct sdmmc_softc *sc, struct sdmmc_task *task) +{ + int s; + + s = splsdmmc(); + TAILQ_INSERT_TAIL(&sc->sc_tskq, task, next); + task->onqueue = 1; + task->sc = sc; + wakeup(&sc->sc_tskq); + splx(s); +} + +void +sdmmc_del_task(struct sdmmc_task *task) +{ + struct sdmmc_softc *sc = task->sc; + int s; + + if (sc == NULL) + return; + + s = splsdmmc(); + task->sc = NULL; + task->onqueue = 0; + TAILQ_REMOVE(&sc->sc_tskq, task, next); + splx(s); +} + +void +sdmmc_needs_discover(struct device *self) +{ + struct sdmmc_softc *sc = (struct sdmmc_softc *)self; + + if (!sdmmc_task_pending(&sc->sc_discover_task)) + sdmmc_add_task(sc, &sc->sc_discover_task); +} + +void +sdmmc_discover_task(void *arg) +{ + struct sdmmc_softc *sc = arg; + + if (sdmmc_chip_card_detect(sc->sct, sc->sch)) { + if (!ISSET(sc->sc_flags, SMF_CARD_PRESENT)) { + SET(sc->sc_flags, SMF_CARD_PRESENT); + sdmmc_card_attach(sc); + } + } else { + if (ISSET(sc->sc_flags, SMF_CARD_PRESENT)) { + CLR(sc->sc_flags, SMF_CARD_PRESENT); + sdmmc_card_detach(sc, DETACH_FORCE); + } + } } /* - * Called from the host driver when a card, or a stack of cards are - * inserted. Return zero if any card drivers have been attached. + * Called from process context when a card is present. */ -int -sdmmc_card_attach(struct device *dev) +void +sdmmc_card_attach(struct sdmmc_softc *sc) { - struct sdmmc_softc *sc = (struct sdmmc_softc *)dev; + DPRINTF(("%s: attach card\n", DEVNAME(sc))); - DPRINTF(("%s: attach card\n", SDMMCDEVNAME(sc))); + SDMMC_LOCK(sc); + CLR(sc->sc_flags, SMF_CARD_ATTACHED); /* * Power up the card (or card stack). */ if (sdmmc_enable(sc) != 0) { - printf("%s: can't enable card\n", SDMMCDEVNAME(sc)); - return 1; + printf("%s: can't enable card\n", DEVNAME(sc)); + goto err; } /* @@ -107,18 +241,16 @@ sdmmc_card_attach(struct device *dev) * allocating a sdmmc_function structure for each. */ if (sdmmc_scan(sc) != 0) { - printf("%s: no functions\n", SDMMCDEVNAME(sc)); - sdmmc_card_detach(dev, DETACH_FORCE); - return 1; + printf("%s: no functions\n", DEVNAME(sc)); + goto err; } /* * Initialize the I/O functions and memory cards. */ if (sdmmc_init(sc) != 0) { - printf("%s: init failed\n", SDMMCDEVNAME(sc)); - sdmmc_card_detach(dev, DETACH_FORCE); - return 1; + printf("%s: init failed\n", DEVNAME(sc)); + goto err; } /* Attach SCSI emulation for memory cards. */ @@ -129,31 +261,39 @@ sdmmc_card_attach(struct device *dev) if (ISSET(sc->sc_flags, SMF_IO_MODE)) sdmmc_io_attach(sc); - return 0; + SET(sc->sc_flags, SMF_CARD_ATTACHED); + SDMMC_UNLOCK(sc); + return; +err: + sdmmc_card_detach(sc, DETACH_FORCE); + SDMMC_UNLOCK(sc); } /* - * Called from host driver with DETACH_* flags from <sys/device.h> + * Called from process context with DETACH_* flags from <sys/device.h> * when cards are gone. */ void -sdmmc_card_detach(struct device *dev, int flags) +sdmmc_card_detach(struct sdmmc_softc *sc, int flags) { - struct sdmmc_softc *sc = (struct sdmmc_softc *)dev; struct sdmmc_function *sf, *sfnext; - DPRINTF(("%s: detach card\n", SDMMCDEVNAME(sc))); + DPRINTF(("%s: detach card\n", DEVNAME(sc))); - /* Power down. */ - sdmmc_disable(sc); + if (ISSET(sc->sc_flags, SMF_CARD_ATTACHED)) { + /* Detach I/O function drivers. */ + if (ISSET(sc->sc_flags, SMF_IO_MODE)) + sdmmc_io_detach(sc); - /* Detach I/O function drivers. */ - if (ISSET(sc->sc_flags, SMF_IO_MODE)) - sdmmc_io_detach(sc); + /* Detach the SCSI emulation for memory cards. */ + if (ISSET(sc->sc_flags, SMF_MEM_MODE)) + sdmmc_scsi_detach(sc); - /* Detach the SCSI emulation for memory cards. */ - if (ISSET(sc->sc_flags, SMF_MEM_MODE)) - sdmmc_scsi_detach(sc); + CLR(sc->sc_flags, SMF_CARD_ATTACHED); + } + + /* Power down. */ + sdmmc_disable(sc); /* Free all sdmmc_function structures. */ for (sf = SIMPLEQ_FIRST(&sc->sf_head); sf != NULL; sf = sfnext) { @@ -178,7 +318,7 @@ sdmmc_enable(struct sdmmc_softc *sc) host_ocr = sdmmc_chip_host_ocr(sc->sct, sc->sch); error = sdmmc_chip_bus_power(sc->sct, sc->sch, host_ocr); if (error != 0) { - printf("%s: can't supply bus power\n", SDMMCDEVNAME(sc)); + printf("%s: can't supply bus power\n", DEVNAME(sc)); goto err; } @@ -187,7 +327,7 @@ sdmmc_enable(struct sdmmc_softc *sc) */ error = sdmmc_chip_bus_clock(sc->sct, sc->sch, SDMMC_SDCLK_400KHZ); if (error != 0) { - printf("%s: can't supply clock\n", SDMMCDEVNAME(sc)); + printf("%s: can't supply clock\n", DEVNAME(sc)); goto err; } @@ -227,6 +367,32 @@ sdmmc_disable(struct sdmmc_softc *sc) (void)sdmmc_chip_bus_power(sc->sct, sc->sch, 0); } +/* + * Set the lowest bus voltage supported by the card and the host. + */ +int +sdmmc_set_bus_power(struct sdmmc_softc *sc, u_int32_t host_ocr, + u_int32_t card_ocr) +{ + u_int32_t bit; + + /* Mask off unsupported voltage levels and select the lowest. */ + DPRINTF(("%s: host_ocr=%x ", DEVNAME(sc), host_ocr)); + host_ocr &= card_ocr; + for (bit = 4; bit < 23; bit++) { + if (ISSET(host_ocr, 1<<bit)) { + host_ocr &= 3<<bit; + break; + } + } + DPRINTF(("card_ocr=%x new_ocr=%x\n", card_ocr, host_ocr)); + + if (host_ocr == 0 || + sdmmc_chip_bus_power(sc->sct, sc->sch, host_ocr) != 0) + return 1; + return 0; +} + struct sdmmc_function * sdmmc_function_alloc(struct sdmmc_softc *sc) { @@ -266,7 +432,7 @@ sdmmc_scan(struct sdmmc_softc *sc) /* There should be at least one function now. */ if (SIMPLEQ_EMPTY(&sc->sf_head)) { - printf("%s: can't identify card\n", SDMMCDEVNAME(sc)); + printf("%s: can't identify card\n", DEVNAME(sc)); return 1; } return 0; @@ -285,11 +451,11 @@ sdmmc_init(struct sdmmc_softc *sc) SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) { if (ISSET(sc->sc_flags, SMF_IO_MODE) && sdmmc_io_init(sc, sf) != 0) - printf("%s: i/o init failed\n", SDMMCDEVNAME(sc)); + printf("%s: i/o init failed\n", DEVNAME(sc)); if (ISSET(sc->sc_flags, SMF_MEM_MODE) && sdmmc_mem_init(sc, sf) != 0) - printf("%s: mem init failed\n", SDMMCDEVNAME(sc)); + printf("%s: mem init failed\n", DEVNAME(sc)); } /* Any good functions left after initialization? */ @@ -307,63 +473,64 @@ sdmmc_delay(u_int usecs) int ticks = usecs / (1000000 / hz); if (ticks > 0) - (void)tsleep(&sdmmc_delay, PWAIT, "sdwait", ticks); + tsleep(&sdmmc_delay, PWAIT, "mmcdly", ticks); else delay(usecs); } -/* - * Set the lowest bus voltage supported by the card and the host. - */ -int -sdmmc_set_bus_power(struct sdmmc_softc *sc, u_int32_t host_ocr, - u_int32_t card_ocr) -{ - u_int32_t bit; - - /* Mask off unsupported voltage levels and select the lowest. */ - DPRINTF(("%s: host_ocr=%x ", SDMMCDEVNAME(sc), host_ocr)); - host_ocr &= card_ocr; - for (bit = 4; bit < 23; bit++) { - if (ISSET(host_ocr, 1<<bit)) { - host_ocr &= 3<<bit; - break; - } - } - DPRINTF(("card_ocr=%x new_ocr=%x\n", card_ocr, host_ocr)); - - if (host_ocr == 0 || - sdmmc_chip_bus_power(sc->sct, sc->sch, host_ocr) != 0) - return 1; - return 0; -} - int sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd) { struct sdmmc_command acmd; int error; + SDMMC_LOCK(sc); + bzero(&acmd, sizeof acmd); acmd.c_opcode = MMC_APP_CMD; acmd.c_arg = 0; acmd.c_flags = SCF_CMD_AC | SCF_RSP_R1; error = sdmmc_mmc_command(sc, &acmd); - if (error != 0) + if (error != 0) { + SDMMC_UNLOCK(sc); return error; + } - if (!ISSET(MMC_R1(acmd.c_resp), MMC_R1_APP_CMD)) + if (!ISSET(MMC_R1(acmd.c_resp), MMC_R1_APP_CMD)) { /* Card does not support application commands. */ + SDMMC_UNLOCK(sc); return ENODEV; + } - return sdmmc_mmc_command(sc, cmd); + error = sdmmc_mmc_command(sc, cmd); + SDMMC_UNLOCK(sc); + return error; } +/* + * Execute MMC command and data transfers. All interactions with the + * host controller to complete the command happen in the context of + * the current process. + */ int sdmmc_mmc_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd) { - return sdmmc_chip_exec_command(sc->sct, sc->sch, cmd); + int error; + + SDMMC_LOCK(sc); + + sdmmc_chip_exec_command(sc->sct, sc->sch, cmd); + + DPRINTF(("%s: mmc cmd=%p opcode=%d proc=\"%s\" (error %d)\n", + DEVNAME(sc), cmd, cmd->c_opcode, curproc ? curproc->p_comm : + "", cmd->c_error)); + + error = cmd->c_error; + wakeup(cmd); + + SDMMC_UNLOCK(sc); + return error; } /* @@ -413,23 +580,31 @@ sdmmc_set_relative_addr(struct sdmmc_softc *sc, * Switch card and host to the maximum supported bus width. */ int -sdmmc_set_bus_width(struct sdmmc_softc *sc, struct sdmmc_function *sf) +sdmmc_set_bus_width(struct sdmmc_function *sf) { + struct sdmmc_softc *sc = sf->sc; struct sdmmc_command cmd; int error; - if (!ISSET(sc->sc_flags, SMF_SD_MODE)) + SDMMC_LOCK(sc); + + if (!ISSET(sc->sc_flags, SMF_SD_MODE)) { + SDMMC_UNLOCK(sc); return EOPNOTSUPP; + } - if ((error = sdmmc_select_card(sc, sf)) != 0) + if ((error = sdmmc_select_card(sc, sf)) != 0) { + SDMMC_UNLOCK(sc); return error; + } bzero(&cmd, sizeof cmd); cmd.c_opcode = SD_APP_SET_BUS_WIDTH; cmd.c_arg = SD_ARG_BUS_WIDTH_4; cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1; - - return sdmmc_app_command(sc, &cmd); + error = sdmmc_app_command(sc, &cmd); + SDMMC_UNLOCK(sc); + return error; } int @@ -438,8 +613,7 @@ sdmmc_select_card(struct sdmmc_softc *sc, struct sdmmc_function *sf) struct sdmmc_command cmd; int error; - if (sc->sc_card == sf || - (sc->sc_card != NULL && sf != NULL && + if (sc->sc_card == sf || (sf && sc->sc_card && sc->sc_card->rca == sf->rca)) { sc->sc_card = sf; return 0; @@ -469,7 +643,7 @@ sdmmc_decode_csd(struct sdmmc_softc *sc, sdmmc_response resp, csd->csdver = SD_CSD_CSDVER(resp); if (csd->csdver != SD_CSD_CSDVER_1_0) { printf("%s: unknown SD CSD structure version 0x%x\n", - SDMMCDEVNAME(sc), csd->csdver); + DEVNAME(sc), csd->csdver); return 1; } @@ -480,7 +654,7 @@ sdmmc_decode_csd(struct sdmmc_softc *sc, sdmmc_response resp, if (csd->csdver != MMC_CSD_CSDVER_1_0 && csd->csdver != MMC_CSD_CSDVER_2_0) { printf("%s: unknown MMC CSD structure version 0x%x\n", - SDMMCDEVNAME(sc), csd->csdver); + DEVNAME(sc), csd->csdver); return 1; } @@ -530,7 +704,7 @@ sdmmc_decode_cid(struct sdmmc_softc *sc, sdmmc_response resp, break; default: printf("%s: unknown MMC version %d\n", - SDMMCDEVNAME(sc), sf->csd.mmcver); + DEVNAME(sc), sf->csd.mmcver); return 1; } } diff --git a/sys/dev/sdmmc/sdmmc_io.c b/sys/dev/sdmmc/sdmmc_io.c index 1ba852bb4f2..50fdf5034a8 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.5 2006/06/19 21:14:30 miod Exp $ */ +/* $OpenBSD: sdmmc_io.c,v 1.6 2006/07/18 04:10:35 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -178,23 +178,29 @@ sdmmc_io_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) void sdmmc_io_function_enable(struct sdmmc_function *sf) { - struct sdmmc_function *sf0 = sf->sc->sc_fn0; + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_function *sf0 = sc->sc_fn0; u_int8_t rv; + SDMMC_LOCK(sc); rv = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_ENABLE); rv |= (1<<sf->number); sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_ENABLE, rv); + SDMMC_UNLOCK(sc); } void sdmmc_io_function_disable(struct sdmmc_function *sf) { - struct sdmmc_function *sf0 = sf->sc->sc_fn0; + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_function *sf0 = sc->sc_fn0; u_int8_t rv; + SDMMC_LOCK(sc); rv = sdmmc_io_read_1(sf0, SD_IO_CCCR_FN_ENABLE); rv &= ~(1<<sf->number); sdmmc_io_write_1(sf0, SD_IO_CCCR_FN_ENABLE, rv); + SDMMC_UNLOCK(sc); } void @@ -289,9 +295,13 @@ sdmmc_io_rw_direct(struct sdmmc_softc *sc, struct sdmmc_function *sf, struct sdmmc_command cmd; int error; + SDMMC_LOCK(sc); + /* Make sure the card is selected. */ - if ((error = sdmmc_select_card(sc, sf)) != 0) + if ((error = sdmmc_select_card(sc, sf)) != 0) { + SDMMC_UNLOCK(sc); return error; + } arg |= ((sf == NULL ? 0 : sf->number) & SD_ARG_CMD52_FUNC_MASK) << SD_ARG_CMD52_FUNC_SHIFT; @@ -307,6 +317,8 @@ sdmmc_io_rw_direct(struct sdmmc_softc *sc, struct sdmmc_function *sf, error = sdmmc_mmc_command(sc, &cmd); *datap = SD_R5_DATA(cmd.c_resp); + + SDMMC_UNLOCK(sc); return error; } @@ -317,9 +329,13 @@ sdmmc_io_rw_extended(struct sdmmc_softc *sc, struct sdmmc_function *sf, struct sdmmc_command cmd; int error; + SDMMC_LOCK(sc); + /* Make sure the card is selected. */ - if ((error = sdmmc_select_card(sc, sf)) != 0) + if ((error = sdmmc_select_card(sc, sf)) != 0) { + SDMMC_UNLOCK(sc); return error; + } arg |= ((sf == NULL ? 0 : sf->number) & SD_ARG_CMD53_FUNC_MASK) << SD_ARG_CMD53_FUNC_SHIFT; @@ -339,7 +355,9 @@ sdmmc_io_rw_extended(struct sdmmc_softc *sc, struct sdmmc_function *sf, if (!ISSET(arg, SD_ARG_CMD53_WRITE)) cmd.c_flags |= SCF_CMD_READ; - return sdmmc_mmc_command(sc, &cmd); + error = sdmmc_mmc_command(sc, &cmd); + SDMMC_UNLOCK(sc); + return error; } u_int8_t @@ -431,6 +449,8 @@ sdmmc_io_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, u_int32_t *ocrp) int error; int i; + SDMMC_LOCK(sc); + /* * If we change the OCR value, retry the command until the OCR * we receive in response has the "CARD BUSY" bit set, meaning @@ -453,5 +473,7 @@ sdmmc_io_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, u_int32_t *ocrp) } if (error == 0 && ocrp != NULL) *ocrp = MMC_R4(cmd.c_resp); + + SDMMC_UNLOCK(sc); return error; } diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c index c8cdaaa1e5b..a3daba6411d 100644 --- a/sys/dev/sdmmc/sdmmc_mem.c +++ b/sys/dev/sdmmc/sdmmc_mem.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc_mem.c,v 1.4 2006/06/29 01:40:51 uwe Exp $ */ +/* $OpenBSD: sdmmc_mem.c,v 1.5 2006/07/18 04:10:35 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -59,7 +59,6 @@ sdmmc_mem_enable(struct sdmmc_softc *sc) */ mmc_mode: if (sdmmc_mem_send_op_cond(sc, 0, &card_ocr) != 0) { - DPRINTF(("flags %x\n", sc->sc_flags)); if (ISSET(sc->sc_flags, SMF_SD_MODE) && !ISSET(sc->sc_flags, SMF_IO_MODE)) { /* Not a SD card, switch to MMC mode. */ @@ -213,10 +212,14 @@ sdmmc_mem_scan(struct sdmmc_softc *sc) int sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) { + int error = 0; + + SDMMC_LOCK(sc); if (sdmmc_select_card(sc, sf) != 0 || sdmmc_mem_set_blocklen(sc, sf) != 0) - return 1; - return 0; + error = 1; + SDMMC_UNLOCK(sc); + return error; } /* @@ -230,6 +233,8 @@ sdmmc_mem_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, int error; int i; + SDMMC_LOCK(sc); + /* * If we change the OCR value, retry the command until the OCR * we receive in response has the "CARD BUSY" bit set, meaning @@ -257,6 +262,8 @@ sdmmc_mem_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, } if (error == 0 && ocrp != NULL) *ocrp = MMC_R3(cmd.c_resp); + + SDMMC_UNLOCK(sc); return error; } @@ -280,14 +287,17 @@ sdmmc_mem_set_blocklen(struct sdmmc_softc *sc, struct sdmmc_function *sf) } int -sdmmc_mem_read_block(struct sdmmc_softc *sc, struct sdmmc_function *sf, - int blkno, u_char *data, size_t datalen) +sdmmc_mem_read_block(struct sdmmc_function *sf, int blkno, u_char *data, + size_t datalen) { + struct sdmmc_softc *sc = sf->sc; struct sdmmc_command cmd; int error; + SDMMC_LOCK(sc); + if ((error = sdmmc_select_card(sc, sf)) != 0) - return error; + goto err; bzero(&cmd, sizeof cmd); cmd.c_data = data; @@ -300,7 +310,7 @@ sdmmc_mem_read_block(struct sdmmc_softc *sc, struct sdmmc_function *sf, error = sdmmc_mmc_command(sc, &cmd); if (error != 0) - return error; + goto err; do { bzero(&cmd, sizeof cmd); @@ -313,18 +323,23 @@ sdmmc_mem_read_block(struct sdmmc_softc *sc, struct sdmmc_function *sf, /* XXX time out */ } while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA)); +err: + SDMMC_UNLOCK(sc); return error; } int -sdmmc_mem_write_block(struct sdmmc_softc *sc, struct sdmmc_function *sf, - int blkno, u_char *data, size_t datalen) +sdmmc_mem_write_block(struct sdmmc_function *sf, int blkno, u_char *data, + size_t datalen) { + struct sdmmc_softc *sc = sf->sc; struct sdmmc_command cmd; int error; + SDMMC_LOCK(sc); + if ((error = sdmmc_select_card(sc, sf)) != 0) - return error; + goto err; bzero(&cmd, sizeof cmd); cmd.c_data = data; @@ -337,7 +352,7 @@ sdmmc_mem_write_block(struct sdmmc_softc *sc, struct sdmmc_function *sf, error = sdmmc_mmc_command(sc, &cmd); if (error != 0) - return error; + goto err; do { bzero(&cmd, sizeof cmd); @@ -350,5 +365,7 @@ sdmmc_mem_write_block(struct sdmmc_softc *sc, struct sdmmc_function *sf, /* XXX time out */ } while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA)); +err: + SDMMC_UNLOCK(sc); return error; } diff --git a/sys/dev/sdmmc/sdmmc_scsi.c b/sys/dev/sdmmc/sdmmc_scsi.c index 447617112ef..4b8a57ab404 100644 --- a/sys/dev/sdmmc/sdmmc_scsi.c +++ b/sys/dev/sdmmc/sdmmc_scsi.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc_scsi.c,v 1.3 2006/06/01 21:53:41 uwe Exp $ */ +/* $OpenBSD: sdmmc_scsi.c,v 1.4 2006/07/18 04:10:35 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -16,11 +16,13 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -/* A SCSI bus emulation to access SD/MMC memory cards. */ +/* A SCSI adapter emulation to access SD/MMC memory cards */ #include <sys/param.h> #include <sys/buf.h> +#include <sys/kernel.h> #include <sys/malloc.h> +#include <sys/proc.h> #include <sys/systm.h> #include <scsi/scsi_all.h> @@ -33,12 +35,57 @@ #define SDMMC_SCSIID_HOST 0x00 #define SDMMC_SCSIID_MAX 0x0f +#define SDMMC_SCSI_MAXCMDS 8 + +struct sdmmc_scsi_target { + struct sdmmc_function *card; +}; + +struct sdmmc_ccb { + struct sdmmc_scsi_softc *ccb_scbus; + struct scsi_xfer *ccb_xs; + int ccb_flags; +#define SDMMC_CCB_F_ERR 0x0001 + void (*ccb_done)(struct sdmmc_ccb *); + u_int32_t ccb_blockno; + u_int32_t ccb_blockcnt; + volatile enum { + SDMMC_CCB_FREE, + SDMMC_CCB_READY, + SDMMC_CCB_QUEUED + } ccb_state; + struct sdmmc_command ccb_cmd; + struct sdmmc_task ccb_task; + TAILQ_ENTRY(sdmmc_ccb) ccb_link; +}; + +TAILQ_HEAD(sdmmc_ccb_list, sdmmc_ccb); + +struct sdmmc_scsi_softc { + struct scsi_adapter sc_adapter; + struct scsi_link sc_link; + struct device *sc_child; + struct sdmmc_scsi_target *sc_tgt; + int sc_ntargets; + struct sdmmc_ccb *sc_ccbs; /* allocated ccbs */ + struct sdmmc_ccb_list sc_ccb_freeq; /* free ccbs */ + struct sdmmc_ccb_list sc_ccb_runq; /* queued ccbs */ +}; + +int sdmmc_alloc_ccbs(struct sdmmc_scsi_softc *, int); +void sdmmc_free_ccbs(struct sdmmc_scsi_softc *); +struct sdmmc_ccb *sdmmc_get_ccb(struct sdmmc_scsi_softc *, int); +void sdmmc_put_ccb(struct sdmmc_ccb *); + int sdmmc_scsi_cmd(struct scsi_xfer *); -int sdmmc_start_xs(struct sdmmc_softc *, struct scsi_xfer *); -int sdmmc_done_xs(struct sdmmc_softc *, struct scsi_xfer *); -void sdmmc_complete(struct sdmmc_softc *, struct scsi_xfer *); +int sdmmc_start_xs(struct sdmmc_softc *, struct sdmmc_ccb *); +void sdmmc_complete_xs(void *); +void sdmmc_done_xs(struct sdmmc_ccb *); +void sdmmc_stimeout(void *); void sdmmc_scsi_minphys(struct buf *); +#define DEVNAME(sc) SDMMCDEVNAME(sc) + #ifdef SDMMC_DEBUG #define DPRINTF(s) printf s #else @@ -73,6 +120,12 @@ sdmmc_scsi_attach(struct sdmmc_softc *sc) scbus->sc_ntargets++; } + /* Preallocate some CCBs and initialize the CCB lists. */ + if (sdmmc_alloc_ccbs(scbus, SDMMC_SCSI_MAXCMDS) != 0) { + printf("%s: can't allocate ccbs\n", sc->sc_dev.dv_xname); + goto free_sctgt; + } + sc->sc_scsibus = scbus; scbus->sc_adapter.scsi_cmd = sdmmc_scsi_cmd; @@ -88,15 +141,45 @@ sdmmc_scsi_attach(struct sdmmc_softc *sc) bzero(&saa, sizeof saa); bcopy(&scbus->sc_link, &saa.scsi_link, sizeof saa.scsi_link); + /* + * Set saa.sf to something, so that SDIO drivers don't need a + * special case to weed out memory cards. + */ + saa.sf = sc->sc_fn0 != NULL ? sc->sc_fn0 : + SIMPLEQ_FIRST(&sc->sf_head); + scbus->sc_child = config_found(&sc->sc_dev, &saa, scsiprint); + if (scbus->sc_child == NULL) { + printf("%s: can't attach scsibus\n", sc->sc_dev.dv_xname); + goto free_ccbs; + } + return; + + free_ccbs: + sc->sc_scsibus = NULL; + sdmmc_free_ccbs(scbus); + free_sctgt: + free(scbus->sc_tgt, M_DEVBUF); + free(scbus, M_DEVBUF); } void sdmmc_scsi_detach(struct sdmmc_softc *sc) { struct sdmmc_scsi_softc *scbus; + struct sdmmc_ccb *ccb; + int s; scbus = sc->sc_scsibus; + if (scbus == NULL) + return; + + /* Complete all open scsi xfers. */ + s = splbio(); + for (ccb = TAILQ_FIRST(&scbus->sc_ccb_runq); ccb != NULL; + ccb = TAILQ_FIRST(&scbus->sc_ccb_runq)) + sdmmc_stimeout(ccb); + splx(s); if (scbus->sc_child != NULL) config_detach(scbus->sc_child, DETACH_FORCE); @@ -104,10 +187,111 @@ sdmmc_scsi_detach(struct sdmmc_softc *sc) if (scbus->sc_tgt != NULL) FREE(scbus->sc_tgt, M_DEVBUF); + sdmmc_free_ccbs(scbus); FREE(scbus, M_DEVBUF); sc->sc_scsibus = NULL; } +/* + * CCB management + */ + +int +sdmmc_alloc_ccbs(struct sdmmc_scsi_softc *scbus, int nccbs) +{ + struct sdmmc_ccb *ccb; + int i; + + scbus->sc_ccbs = malloc(sizeof(struct sdmmc_ccb) * nccbs, + M_DEVBUF, M_NOWAIT); + if (scbus->sc_ccbs == NULL) + return 1; + + TAILQ_INIT(&scbus->sc_ccb_freeq); + TAILQ_INIT(&scbus->sc_ccb_runq); + + for (i = 0; i < nccbs; i++) { + ccb = &scbus->sc_ccbs[i]; + ccb->ccb_scbus = scbus; + ccb->ccb_state = SDMMC_CCB_FREE; + ccb->ccb_flags = 0; + ccb->ccb_xs = NULL; + ccb->ccb_done = NULL; + + TAILQ_INSERT_TAIL(&scbus->sc_ccb_freeq, ccb, ccb_link); + } + return 0; +} + +void +sdmmc_free_ccbs(struct sdmmc_scsi_softc *scbus) +{ + if (scbus->sc_ccbs != NULL) { + free(scbus->sc_ccbs, M_DEVBUF); + scbus->sc_ccbs = NULL; + } +} + +struct sdmmc_ccb * +sdmmc_get_ccb(struct sdmmc_scsi_softc *scbus, int flags) +{ + struct sdmmc_ccb *ccb; + int s; + + s = splbio(); + while ((ccb = TAILQ_FIRST(&scbus->sc_ccb_freeq)) == NULL && + !ISSET(flags, SCSI_NOSLEEP)) + tsleep(&scbus->sc_ccb_freeq, PRIBIO, "getccb", 0); + if (ccb != NULL) { + TAILQ_REMOVE(&scbus->sc_ccb_freeq, ccb, ccb_link); + ccb->ccb_state = SDMMC_CCB_READY; + } + splx(s); + return ccb; +} + +void +sdmmc_put_ccb(struct sdmmc_ccb *ccb) +{ + struct sdmmc_scsi_softc *scbus = ccb->ccb_scbus; + int s; + + s = splbio(); + if (ccb->ccb_state == SDMMC_CCB_QUEUED) + TAILQ_REMOVE(&scbus->sc_ccb_runq, ccb, ccb_link); + ccb->ccb_state = SDMMC_CCB_FREE; + ccb->ccb_flags = 0; + ccb->ccb_xs = NULL; + ccb->ccb_done = NULL; + TAILQ_INSERT_TAIL(&scbus->sc_ccb_freeq, ccb, ccb_link); + if (TAILQ_NEXT(ccb, ccb_link) == NULL) + wakeup(&scbus->sc_ccb_freeq); + splx(s); +} + +/* + * SCSI command emulation + */ + +/* XXX move to some sort of "scsi emulation layer". */ +static void +sdmmc_scsi_decode_rw(struct scsi_xfer *xs, u_int32_t *blocknop, + u_int32_t *blockcntp) +{ + struct scsi_rw *rw; + struct scsi_rw_big *rwb; + + if (xs->cmdlen == 6) { + rw = (struct scsi_rw *)xs->cmd; + *blocknop = _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff); + *blockcntp = rw->length ? rw->length : 0x100; + } else { + rwb = (struct scsi_rw_big *)xs->cmd; + *blocknop = _4btol(rwb->addr); + *blockcntp = _2btol(rwb->length); + } +} + int sdmmc_scsi_cmd(struct scsi_xfer *xs) { @@ -117,11 +301,14 @@ sdmmc_scsi_cmd(struct scsi_xfer *xs) struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target]; struct scsi_inquiry_data inq; struct scsi_read_cap_data rcd; + u_int32_t blockno; + u_int32_t blockcnt; + struct sdmmc_ccb *ccb; if (link->target >= scbus->sc_ntargets || tgt->card == NULL || link->lun != 0) { DPRINTF(("%s: sdmmc_scsi_cmd: no target %d\n", - SDMMCDEVNAME(sc), link->target)); + DEVNAME(sc), link->target)); /* XXX should be XS_SENSE and sense filled out */ xs->error = XS_DRIVER_STUFFUP; xs->flags |= ITSDONE; @@ -129,9 +316,9 @@ sdmmc_scsi_cmd(struct scsi_xfer *xs) return COMPLETE; } - DPRINTF(("%s: sdmmc_scsi_cmd: target=%d xs=%p cmd=%#x " - "datalen=%d (poll=%d)\n", SDMMCDEVNAME(sc), link->target, - xs, xs->cmd->opcode, xs->datalen, xs->flags & SCSI_POLL)); + DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (poll=%#x)\n", + DEVNAME(sc), link->target, xs->cmd->opcode, curproc ? + curproc->p_comm : "", xs->flags & SCSI_POLL)); xs->error = XS_NOERROR; @@ -170,66 +357,141 @@ sdmmc_scsi_cmd(struct scsi_xfer *xs) scsi_done(xs); return COMPLETE; - default: DPRINTF(("%s: unsupported scsi command %#x\n", - SDMMCDEVNAME(sc), xs->cmd->opcode)); + DEVNAME(sc), xs->cmd->opcode)); + xs->error = XS_DRIVER_STUFFUP; + scsi_done(xs); + return COMPLETE; + } + + /* A read or write operation. */ + sdmmc_scsi_decode_rw(xs, &blockno, &blockcnt); + + if (blockno >= tgt->card->csd.capacity || + blockno + blockcnt > tgt->card->csd.capacity) { + DPRINTF(("%s: out of bounds %u-%u >= %u\n", DEVNAME(sc), + blockno, blockcnt, tgt->card->csd.capacity)); xs->error = XS_DRIVER_STUFFUP; scsi_done(xs); return COMPLETE; } - /* XXX check bounds */ + ccb = sdmmc_get_ccb(sc->sc_scsibus, xs->flags); + if (ccb == NULL) { + printf("%s: out of ccbs\n", DEVNAME(sc)); + xs->error = XS_DRIVER_STUFFUP; + scsi_done(xs); + return COMPLETE; + } + + ccb->ccb_xs = xs; + ccb->ccb_done = sdmmc_done_xs; + + ccb->ccb_blockcnt = blockcnt; + ccb->ccb_blockno = blockno; - return sdmmc_start_xs(sc, xs); + return sdmmc_start_xs(sc, ccb); } int -sdmmc_start_xs(struct sdmmc_softc *sc, struct scsi_xfer *xs) +sdmmc_start_xs(struct sdmmc_softc *sc, struct sdmmc_ccb *ccb) { - sdmmc_complete(sc, xs); - return COMPLETE; + struct sdmmc_scsi_softc *scbus = sc->sc_scsibus; + struct scsi_xfer *xs = ccb->ccb_xs; + int s; + + timeout_set(&xs->stimeout, sdmmc_stimeout, ccb); + sdmmc_init_task(&ccb->ccb_task, sdmmc_complete_xs, ccb); + + s = splbio(); + TAILQ_INSERT_TAIL(&scbus->sc_ccb_runq, ccb, ccb_link); + ccb->ccb_state = SDMMC_CCB_QUEUED; + splx(s); + + if (ISSET(xs->flags, SCSI_POLL)) { + sdmmc_complete_xs(ccb); + return COMPLETE; + } + + timeout_add(&xs->stimeout, (xs->timeout * hz) / 1000); + sdmmc_add_task(sc, &ccb->ccb_task); + return SUCCESSFULLY_QUEUED; } void -sdmmc_complete(struct sdmmc_softc *sc, struct scsi_xfer *xs) +sdmmc_complete_xs(void *arg) { + struct sdmmc_ccb *ccb = arg; + struct scsi_xfer *xs = ccb->ccb_xs; struct scsi_link *link = xs->sc_link; + struct sdmmc_softc *sc = link->adapter_softc; struct sdmmc_scsi_softc *scbus = sc->sc_scsibus; struct sdmmc_scsi_target *tgt = &scbus->sc_tgt[link->target]; - struct scsi_rw *rw; - struct scsi_rw_big *rwb; - u_int32_t blockno; - u_int32_t blockcnt; int error; + int s; - /* A read or write operation. */ - /* XXX move to some sort of "scsi emulation layer". */ - if (xs->cmdlen == 6) { - rw = (struct scsi_rw *)xs->cmd; - blockno = _3btol(rw->addr) & (SRW_TOPADDR << 16 | 0xffff); - blockcnt = rw->length ? rw->length : 0x100; - } else { - rwb = (struct scsi_rw_big *)xs->cmd; - blockno = _4btol(rwb->addr); - blockcnt = _2btol(rwb->length); - } + DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (poll=%#x)" + " complete\n", DEVNAME(sc), link->target, xs->cmd->opcode, + curproc ? curproc->p_comm : "", xs->flags & SCSI_POLL)); + + s = splbio(); if (ISSET(xs->flags, SCSI_DATA_IN)) - error = sdmmc_mem_read_block(sc, tgt->card, blockno, - xs->data, blockcnt * DEV_BSIZE); + error = sdmmc_mem_read_block(tgt->card, ccb->ccb_blockno, + xs->data, ccb->ccb_blockcnt * DEV_BSIZE); else - error = sdmmc_mem_write_block(sc, tgt->card, blockno, - xs->data, blockcnt * DEV_BSIZE); + error = sdmmc_mem_write_block(tgt->card, ccb->ccb_blockno, + xs->data, ccb->ccb_blockcnt * DEV_BSIZE); + if (error != 0) xs->error = XS_DRIVER_STUFFUP; - xs->flags |= ITSDONE; + ccb->ccb_done(ccb); + splx(s); +} + +void +sdmmc_done_xs(struct sdmmc_ccb *ccb) +{ + struct scsi_xfer *xs = ccb->ccb_xs; +#ifdef SDMMC_DEBUG + struct scsi_link *link = xs->sc_link; + struct sdmmc_softc *sc = link->adapter_softc; +#endif + + timeout_del(&xs->stimeout); + + DPRINTF(("%s: scsi cmd target=%d opcode=%#x proc=\"%s\" (error=%#x)" + " done\n", DEVNAME(sc), link->target, xs->cmd->opcode, + curproc ? curproc->p_comm : "", xs->error)); + xs->resid = 0; + xs->flags |= ITSDONE; + + if (ISSET(ccb->ccb_flags, SDMMC_CCB_F_ERR)) + xs->error = XS_DRIVER_STUFFUP; + + sdmmc_put_ccb(ccb); scsi_done(xs); } void +sdmmc_stimeout(void *arg) +{ + struct sdmmc_ccb *ccb = arg; + int s; + + s = splbio(); + ccb->ccb_flags |= SDMMC_CCB_F_ERR; + if (sdmmc_task_pending(&ccb->ccb_task)) { + sdmmc_del_task(&ccb->ccb_task); + ccb->ccb_done(ccb); + } + splx(s); +} + +void sdmmc_scsi_minphys(struct buf *bp) { /* XXX limit to max. transfer size supported by card/host? */ diff --git a/sys/dev/sdmmc/sdmmc_scsi.h b/sys/dev/sdmmc/sdmmc_scsi.h index 1bf29badbdf..da92079b3e8 100644 --- a/sys/dev/sdmmc/sdmmc_scsi.h +++ b/sys/dev/sdmmc/sdmmc_scsi.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmc_scsi.h,v 1.2 2006/06/01 21:53:41 uwe Exp $ */ +/* $OpenBSD: sdmmc_scsi.h,v 1.3 2006/07/18 04:10:35 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -21,18 +21,6 @@ struct sdmmc_softc; -struct sdmmc_scsi_target { - struct sdmmc_function *card; -}; - -struct sdmmc_scsi_softc { - struct scsi_adapter sc_adapter; - struct scsi_link sc_link; - struct device *sc_child; - struct sdmmc_scsi_target *sc_tgt; - int sc_ntargets; -}; - void sdmmc_scsi_attach(struct sdmmc_softc *); void sdmmc_scsi_detach(struct sdmmc_softc *); diff --git a/sys/dev/sdmmc/sdmmcchip.h b/sys/dev/sdmmc/sdmmcchip.h index 2ac3881dd32..9cd05c0bf3e 100644 --- a/sys/dev/sdmmc/sdmmcchip.h +++ b/sys/dev/sdmmc/sdmmcchip.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmcchip.h,v 1.1 2006/05/28 17:21:14 uwe Exp $ */ +/* $OpenBSD: sdmmcchip.h,v 1.2 2006/07/18 04:10:35 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -36,7 +36,7 @@ struct sdmmc_chip_functions { int (*bus_power)(sdmmc_chipset_handle_t, u_int32_t); int (*bus_clock)(sdmmc_chipset_handle_t, int); /* command execution */ - int (*exec_command)(sdmmc_chipset_handle_t, + void (*exec_command)(sdmmc_chipset_handle_t, struct sdmmc_command *); }; @@ -71,8 +71,7 @@ struct sdmmcbus_attach_args { sdmmc_chipset_handle_t sch; }; -/* host controller calls to sdmmc */ -int sdmmc_card_attach(struct device *); -void sdmmc_card_detach(struct device *, int); +void sdmmc_needs_discover(struct device *); +void sdmmc_delay(u_int); #endif diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h index 3f69068750c..e3718990363 100644 --- a/sys/dev/sdmmc/sdmmcvar.h +++ b/sys/dev/sdmmc/sdmmcvar.h @@ -1,4 +1,4 @@ -/* $OpenBSD: sdmmcvar.h,v 1.2 2006/06/01 21:53:41 uwe Exp $ */ +/* $OpenBSD: sdmmcvar.h,v 1.3 2006/07/18 04:10:35 uwe Exp $ */ /* * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> @@ -45,12 +45,29 @@ struct sdmmc_cid { int mdt; /* manufacturing date */ }; -struct sdmmc_command; - typedef u_int32_t sdmmc_response[4]; -typedef void (*sdmmc_callback)(struct device *, struct sdmmc_command *); + +struct sdmmc_softc; + +struct sdmmc_task { + void (*func)(void *arg); + void *arg; + int onqueue; + struct sdmmc_softc *sc; + TAILQ_ENTRY(sdmmc_task) next; +}; + +#define sdmmc_init_task(xtask, xfunc, xarg) do { \ + (xtask)->func = (xfunc); \ + (xtask)->arg = (xarg); \ + (xtask)->onqueue = 0; \ + (xtask)->sc = NULL; \ +} while (0) + +#define sdmmc_task_pending(xtask) ((xtask)->onqueue) struct sdmmc_command { + struct sdmmc_task c_task; /* task queue entry */ u_int16_t c_opcode; /* SD or MMC command index */ u_int32_t c_arg; /* SD/MMC command argument */ sdmmc_response c_resp; /* response buffer */ @@ -58,10 +75,7 @@ struct sdmmc_command { int c_datalen; /* length of data buffer */ int c_blklen; /* block length */ int c_flags; /* see below */ -#define SCF_DONE 0x0001 /* command is finished */ -#define SCF_BUF_READY 0x0002 /* buffer ready int occurred */ -#define SCF_CMD_DONE 0x0004 /* cmd complete int occurred */ -#define SCF_XFR_DONE 0x0008 /* transfer complete int occurred */ +#define SCF_ITSDONE 0x0001 /* command is complete */ #define SCF_CMD_AC 0x0000 #define SCF_CMD_ADTC 0x0010 #define SCF_CMD_BC 0x0020 @@ -81,7 +95,6 @@ struct sdmmc_command { #define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) #define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) #define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC) - sdmmc_callback c_done; /* callback function */ int c_error; /* errno value on completion */ }; @@ -138,11 +151,18 @@ struct sdmmc_softc { #define SMF_SD_MODE 0x0001 /* host in SD mode (MMC otherwise) */ #define SMF_IO_MODE 0x0002 /* host in I/O mode (SD mode only) */ #define SMF_MEM_MODE 0x0004 /* host in memory mode (SD or MMC) */ +#define SMF_CARD_PRESENT 0x0010 /* card presence noticed */ +#define SMF_CARD_ATTACHED 0x0020 /* card driver(s) attached */ int sc_function_count; /* number of I/O functions (SDIO) */ - void *sc_scsibus; /* SCSI bus emulation softc */ struct sdmmc_function *sc_card; /* selected card */ struct sdmmc_function *sc_fn0; /* function 0, the card itself */ - SIMPLEQ_HEAD(, sdmmc_function) sf_head; + SIMPLEQ_HEAD(, sdmmc_function) sf_head; /* list of card functions */ + int sc_dying; /* bus driver is shutting down */ + 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 lock sc_lock; /* lock around host controller */ + void *sc_scsibus; /* SCSI bus emulation softc */ }; /* @@ -156,9 +176,14 @@ struct sdmmc_attach_args { #define IPL_SDMMC IPL_BIO #define splsdmmc() splbio() +#define SDMMC_LOCK(sc) lockmgr(&(sc)->sc_lock, LK_EXCLUSIVE, NULL) +#define SDMMC_UNLOCK(sc) lockmgr(&(sc)->sc_lock, LK_RELEASE, NULL) + +void sdmmc_add_task(struct sdmmc_softc *, struct sdmmc_task *); +void sdmmc_del_task(struct sdmmc_task *); + struct sdmmc_function *sdmmc_function_alloc(struct sdmmc_softc *); void sdmmc_function_free(struct sdmmc_function *); -void sdmmc_delay(u_int); int sdmmc_set_bus_power(struct sdmmc_softc *, u_int32_t, u_int32_t); int sdmmc_mmc_command(struct sdmmc_softc *, struct sdmmc_command *); int sdmmc_app_command(struct sdmmc_softc *, struct sdmmc_command *); @@ -193,9 +218,7 @@ void sdmmc_check_cis_quirks(struct sdmmc_function *); int sdmmc_mem_enable(struct sdmmc_softc *); void sdmmc_mem_scan(struct sdmmc_softc *); int sdmmc_mem_init(struct sdmmc_softc *, struct sdmmc_function *); -int sdmmc_mem_read_block(struct sdmmc_softc *, - struct sdmmc_function *, int, u_char *, size_t); -int sdmmc_mem_write_block(struct sdmmc_softc *, - struct sdmmc_function *, int, u_char *, size_t); +int sdmmc_mem_read_block(struct sdmmc_function *, int, u_char *, size_t); +int sdmmc_mem_write_block(struct sdmmc_function *, int, u_char *, size_t); #endif |