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