summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/sdmmc/sdhc.c452
-rw-r--r--sys/dev/sdmmc/sdmmc.c326
-rw-r--r--sys/dev/sdmmc/sdmmc_io.c34
-rw-r--r--sys/dev/sdmmc/sdmmc_mem.c41
-rw-r--r--sys/dev/sdmmc/sdmmc_scsi.c336
-rw-r--r--sys/dev/sdmmc/sdmmc_scsi.h14
-rw-r--r--sys/dev/sdmmc/sdmmcchip.h9
-rw-r--r--sys/dev/sdmmc/sdmmcvar.h55
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