summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorUwe Stuehler <uwe@cvs.openbsd.org>2006-06-01 21:53:42 +0000
committerUwe Stuehler <uwe@cvs.openbsd.org>2006-06-01 21:53:42 +0000
commitb89f9d40f1fe1476b0c5078e2a2e451157066db2 (patch)
tree9e87d5289bdf55af01bdec6676ba02079a9c38af /sys
parent6b37ef9f877f52e97b18ea962dbb1c3aa7458dfc (diff)
SDIO card identification
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/sdmmc/files.sdmmc4
-rw-r--r--sys/dev/sdmmc/sdmmc.c301
-rw-r--r--sys/dev/sdmmc/sdmmc_cis.c194
-rw-r--r--sys/dev/sdmmc/sdmmc_io.c333
-rw-r--r--sys/dev/sdmmc/sdmmc_ioreg.h90
-rw-r--r--sys/dev/sdmmc/sdmmc_mem.c150
-rw-r--r--sys/dev/sdmmc/sdmmc_scsi.c15
-rw-r--r--sys/dev/sdmmc/sdmmc_scsi.h4
-rw-r--r--sys/dev/sdmmc/sdmmcreg.h28
-rw-r--r--sys/dev/sdmmc/sdmmcvar.h112
10 files changed, 994 insertions, 237 deletions
diff --git a/sys/dev/sdmmc/files.sdmmc b/sys/dev/sdmmc/files.sdmmc
index 6462e6e0a77..db96764fb5f 100644
--- a/sys/dev/sdmmc/files.sdmmc
+++ b/sys/dev/sdmmc/files.sdmmc
@@ -1,11 +1,13 @@
-# $OpenBSD: files.sdmmc,v 1.1 2006/05/28 17:21:14 uwe Exp $
+# $OpenBSD: files.sdmmc,v 1.2 2006/06/01 21:53:41 uwe Exp $
#
# Config file and device description for machine-independent SD/MMC code.
# Included by ports that need it.
+define sdmmc {}
device sdmmc: scsi
attach sdmmc at sdmmcbus
file dev/sdmmc/sdmmc.c sdmmc
+file dev/sdmmc/sdmmc_cis.c sdmmc
file dev/sdmmc/sdmmc_io.c sdmmc
file dev/sdmmc/sdmmc_mem.c sdmmc
file dev/sdmmc/sdmmc_scsi.c sdmmc
diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c
index 7bafe907fa5..15bb8ca1930 100644
--- a/sys/dev/sdmmc/sdmmc.c
+++ b/sys/dev/sdmmc/sdmmc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmc.c,v 1.2 2006/05/28 18:45:23 uwe Exp $ */
+/* $OpenBSD: sdmmc.c,v 1.3 2006/06/01 21:53:41 uwe Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -32,27 +32,20 @@
#include <scsi/scsi_all.h>
#include <scsi/scsiconf.h>
+#include <dev/sdmmc/sdmmc_ioreg.h>
#include <dev/sdmmc/sdmmc_scsi.h>
#include <dev/sdmmc/sdmmcchip.h>
-#ifdef notyet
-#include <dev/sdmmc/sdmmcdevs.h>
-#endif
#include <dev/sdmmc/sdmmcreg.h>
#include <dev/sdmmc/sdmmcvar.h>
int sdmmc_match(struct device *, void *, void *);
void sdmmc_attach(struct device *, struct device *, void *);
-
-int sdmmc_set_relative_addr(struct sdmmc_softc *, struct sdmmc_card *);
-int sdmmc_set_bus_width(struct sdmmc_softc *, struct sdmmc_card *);
int sdmmc_enable(struct sdmmc_softc *);
void sdmmc_disable(struct sdmmc_softc *);
-int sdmmc_decode_csd(struct sdmmc_softc *, sdmmc_response,
- struct sdmmc_card *);
-int sdmmc_decode_cid(struct sdmmc_softc *, sdmmc_response,
- struct sdmmc_card *);
-void sdmmc_print_cid(struct sdmmc_cid *);
-void sdmmc_identify_all(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 *);
#ifdef SDMMC_DEBUG
#define DPRINTF(s) printf s
@@ -88,7 +81,7 @@ sdmmc_attach(struct device *parent, struct device *self, void *aux)
sc->sct = saa->sct;
sc->sch = saa->sch;
- SIMPLEQ_INIT(&sc->cs_head);
+ SIMPLEQ_INIT(&sc->sf_head);
}
/*
@@ -99,41 +92,44 @@ int
sdmmc_card_attach(struct device *dev)
{
struct sdmmc_softc *sc = (struct sdmmc_softc *)dev;
- struct sdmmc_card *cs;
DPRINTF(("%s: attach card\n", SDMMCDEVNAME(sc)));
- /* Power up the card (or card stack). */
+ /*
+ * Power up the card (or card stack).
+ */
if (sdmmc_enable(sc) != 0) {
printf("%s: can't enable card\n", SDMMCDEVNAME(sc));
return 1;
}
- /* Scan for cards and allocate a card structure for each. */
- sdmmc_identify_all(sc);
-
- /* There should be at least one card now; otherwise, bail out. */
- if (SIMPLEQ_EMPTY(&sc->cs_head)) {
- printf("%s: can't identify card\n", SDMMCDEVNAME(sc));
- sdmmc_disable(sc);
+ /*
+ * Scan for I/O functions and memory cards on the bus,
+ * 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;
}
- /* Initialize all identified cards. */
- SIMPLEQ_FOREACH(cs, &sc->cs_head, cs_list) {
- /* Boost the bus width. */
- (void)sdmmc_set_bus_width(sc, cs); /* XXX */
-
- if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
- sdmmc_mem_init(sc, cs) != 0)
- printf("%s: init failed\n", SDMMCDEVNAME(sc));
+ /*
+ * 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;
}
- /* XXX attach SDIO driver(s) */
-
/* Attach SCSI emulation for memory cards. */
if (ISSET(sc->sc_flags, SMF_MEM_MODE))
sdmmc_scsi_attach(sc);
+
+ /* Attach I/O function drivers. */
+ if (ISSET(sc->sc_flags, SMF_IO_MODE))
+ sdmmc_io_attach(sc);
+
return 0;
}
@@ -145,25 +141,28 @@ void
sdmmc_card_detach(struct device *dev, int flags)
{
struct sdmmc_softc *sc = (struct sdmmc_softc *)dev;
- struct sdmmc_card *cs, *csnext;
+ struct sdmmc_function *sf, *sfnext;
DPRINTF(("%s: detach card\n", SDMMCDEVNAME(sc)));
/* Power down. */
sdmmc_disable(sc);
- /* Detach SCSI emulation. */
+ /* 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);
- /* XXX detach SDIO driver(s) */
-
- /* Free all card structures. */
- for (cs = SIMPLEQ_FIRST(&sc->cs_head); cs != NULL; cs = csnext) {
- csnext = SIMPLEQ_NEXT(cs, cs_list);
- FREE(cs, M_DEVBUF);
+ /* Free all sdmmc_function structures. */
+ for (sf = SIMPLEQ_FIRST(&sc->sf_head); sf != NULL; sf = sfnext) {
+ sfnext = SIMPLEQ_NEXT(sf, sf_list);
+ sdmmc_function_free(sf);
}
- SIMPLEQ_INIT(&sc->cs_head);
+ SIMPLEQ_INIT(&sc->sf_head);
+ sc->sc_function_count = 0;
}
int
@@ -192,15 +191,19 @@ sdmmc_enable(struct sdmmc_softc *sc)
goto err;
}
+ /* XXX wait for card to power up */
+ sdmmc_delay(100000);
+
/* Initialize SD I/O card function(s). */
if ((error = sdmmc_io_enable(sc)) != 0)
goto err;
/* Initialize SD/MMC memory card(s). */
- if ((error = sdmmc_mem_enable(sc)) != 0)
+ if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
+ (error = sdmmc_mem_enable(sc)) != 0)
goto err;
- /* XXX */
+ /* XXX respect host and card capabilities */
if (ISSET(sc->sc_flags, SMF_SD_MODE))
(void)sdmmc_chip_bus_clock(sc->sct, sc->sch,
SDMMC_SDCLK_25MHZ);
@@ -216,7 +219,7 @@ sdmmc_disable(struct sdmmc_softc *sc)
{
/* XXX complete commands if card is still present. */
- /* Deselect all cards. */
+ /* Make sure no card is still selected. */
(void)sdmmc_select_card(sc, NULL);
/* Turn off bus power and clock. */
@@ -224,6 +227,80 @@ sdmmc_disable(struct sdmmc_softc *sc)
(void)sdmmc_chip_bus_power(sc->sct, sc->sch, 0);
}
+struct sdmmc_function *
+sdmmc_function_alloc(struct sdmmc_softc *sc)
+{
+ struct sdmmc_function *sf;
+
+ MALLOC(sf, struct sdmmc_function *, sizeof *sf, M_DEVBUF,
+ M_WAITOK);
+ bzero(sf, sizeof *sf);
+ sf->sc = sc;
+ sf->number = -1;
+ sf->cis.manufacturer = SDMMC_VENDOR_INVALID;
+ sf->cis.product = SDMMC_PRODUCT_INVALID;
+ sf->cis.function = SDMMC_FUNCTION_INVALID;
+ return sf;
+}
+
+void
+sdmmc_function_free(struct sdmmc_function *sf)
+{
+ FREE(sf, M_DEVBUF);
+}
+
+/*
+ * Scan for I/O functions and memory cards on the bus, allocating a
+ * sdmmc_function structure for each.
+ */
+int
+sdmmc_scan(struct sdmmc_softc *sc)
+{
+ /* Scan for I/O functions. */
+ if (ISSET(sc->sc_flags, SMF_IO_MODE))
+ sdmmc_io_scan(sc);
+
+ /* Scan for memory cards on the bus. */
+ if (ISSET(sc->sc_flags, SMF_MEM_MODE))
+ sdmmc_mem_scan(sc);
+
+ /* There should be at least one function now. */
+ if (SIMPLEQ_EMPTY(&sc->sf_head)) {
+ printf("%s: can't identify card\n", SDMMCDEVNAME(sc));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Initialize all the distinguished functions of the card, be it I/O
+ * or memory functions.
+ */
+int
+sdmmc_init(struct sdmmc_softc *sc)
+{
+ struct sdmmc_function *sf;
+
+ /* Initialize all identified card functions. */
+ 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));
+
+ if (ISSET(sc->sc_flags, SMF_MEM_MODE) &&
+ sdmmc_mem_init(sc, sf) != 0)
+ printf("%s: mem init failed\n", SDMMCDEVNAME(sc));
+ }
+
+ /* Any good functions left after initialization? */
+ SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
+ if (!ISSET(sf->flags, SFF_ERROR))
+ return 0;
+ }
+ /* No, we should probably power down the card. */
+ return 1;
+}
+
void
sdmmc_delay(u_int usecs)
{
@@ -261,99 +338,6 @@ sdmmc_set_bus_power(struct sdmmc_softc *sc, u_int32_t host_ocr,
return 0;
}
-/*
- * Read the CSD and CID from all cards and assign each card a unique
- * relative card address (RCA).
- */
-void
-sdmmc_identify_all(struct sdmmc_softc *sc)
-{
- struct sdmmc_command cmd;
- struct sdmmc_card *cs;
- u_int16_t next_rca;
- int error;
- int i;
-
- /*
- * CMD2 is a broadcast command understood by SD cards and MMC
- * cards. All cards begin to respond to the command, but back
- * off if another card drives the CMD line to a different level.
- * Only one card will get its entire response through. That
- * card remains silent once it has been assigned a RCA.
- */
- for (i = 0; i < 100; i++) {
- bzero(&cmd, sizeof cmd);
- cmd.c_opcode = MMC_ALL_SEND_CID;
- cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R2;
-
- error = sdmmc_mmc_command(sc, &cmd);
- if (error == ETIMEDOUT) {
- /* No more cards there. */
- break;
- } else if (error != 0) {
- DPRINTF(("%s: can't read CID\n", SDMMCDEVNAME(sc)));
- break;
- }
-
- /* In MMC mode, find the next available RCA. */
- next_rca = 0;
- if (!ISSET(sc->sc_flags, SMF_SD_MODE))
- SIMPLEQ_FOREACH(cs, &sc->cs_head, cs_list)
- next_rca++;
-
- /* Allocate a card structure. */
- MALLOC(cs, struct sdmmc_card *, sizeof *cs, M_DEVBUF,
- M_WAITOK);
- bzero(cs, sizeof *cs);
- cs->rca = next_rca;
-
- /*
- * Remember the CID returned in the CMD2 response for
- * later decoding.
- */
- bcopy(cmd.c_resp, cs->raw_cid, sizeof cs->raw_cid);
-
- /*
- * Silence the card by assigning it a unique RCA, or
- * querying it for its RCA in case of SD.
- */
- if (sdmmc_set_relative_addr(sc, cs) != 0) {
- printf("%s: can't set RCA\n", SDMMCDEVNAME(sc));
- FREE(cs, M_DEVBUF);
- break;
- }
-
- SIMPLEQ_INSERT_TAIL(&sc->cs_head, cs, cs_list);
- }
-
- /*
- * All cards are either inactive or awaiting further commands.
- * Read the CSDs and decode the raw CID for each card.
- */
- SIMPLEQ_FOREACH(cs, &sc->cs_head, cs_list) {
- bzero(&cmd, sizeof cmd);
- cmd.c_opcode = MMC_SEND_CSD;
- cmd.c_arg = MMC_ARG_RCA(cs->rca);
- cmd.c_flags = SCF_CMD_AC | SCF_RSP_R2;
-
- if (sdmmc_mmc_command(sc, &cmd) != 0) {
- SET(cs->flags, SDMMCF_CARD_ERROR);
- continue;
- }
-
- if (sdmmc_decode_csd(sc, cmd.c_resp, cs) != 0 ||
- sdmmc_decode_cid(sc, cs->raw_cid, cs) != 0) {
- SET(cs->flags, SDMMCF_CARD_ERROR);
- continue;
- }
-
-#ifdef SDMMC_DEBUG
- printf("%s: CID: ", SDMMCDEVNAME(sc));
- sdmmc_print_cid(&cs->cid);
-#endif
- }
-}
-
int
sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd)
{
@@ -401,7 +385,8 @@ sdmmc_go_idle_state(struct sdmmc_softc *sc)
* Retrieve (SD) or set (MMC) the relative card address (RCA).
*/
int
-sdmmc_set_relative_addr(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+sdmmc_set_relative_addr(struct sdmmc_softc *sc,
+ struct sdmmc_function *sf)
{
struct sdmmc_command cmd;
@@ -412,7 +397,7 @@ sdmmc_set_relative_addr(struct sdmmc_softc *sc, struct sdmmc_card *cs)
cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R6;
} else {
cmd.c_opcode = MMC_SET_RELATIVE_ADDR;
- cmd.c_arg = MMC_ARG_RCA(cs->rca);
+ cmd.c_arg = MMC_ARG_RCA(sf->rca);
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
}
@@ -420,15 +405,15 @@ sdmmc_set_relative_addr(struct sdmmc_softc *sc, struct sdmmc_card *cs)
return 1;
if (ISSET(sc->sc_flags, SMF_SD_MODE))
- cs->rca = SD_R6_RCA(cmd.c_resp);
+ sf->rca = SD_R6_RCA(cmd.c_resp);
return 0;
}
/*
- * Set the maximum supported bus width.
+ * Switch card and host to the maximum supported bus width.
*/
int
-sdmmc_set_bus_width(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+sdmmc_set_bus_width(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
struct sdmmc_command cmd;
int error;
@@ -436,7 +421,7 @@ sdmmc_set_bus_width(struct sdmmc_softc *sc, struct sdmmc_card *cs)
if (!ISSET(sc->sc_flags, SMF_SD_MODE))
return EOPNOTSUPP;
- if ((error = sdmmc_select_card(sc, cs)) != 0)
+ if ((error = sdmmc_select_card(sc, sf)) != 0)
return error;
bzero(&cmd, sizeof cmd);
@@ -448,29 +433,33 @@ sdmmc_set_bus_width(struct sdmmc_softc *sc, struct sdmmc_card *cs)
}
int
-sdmmc_select_card(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+sdmmc_select_card(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
struct sdmmc_command cmd;
int error;
- if (sc->sc_card == cs)
+ if (sc->sc_card == sf ||
+ (sc->sc_card != NULL && sf != NULL &&
+ sc->sc_card->rca == sf->rca)) {
+ sc->sc_card = sf;
return 0;
+ }
bzero(&cmd, sizeof cmd);
cmd.c_opcode = MMC_SELECT_CARD;
- cmd.c_arg = cs == NULL ? 0 : MMC_ARG_RCA(cs->rca);
- cmd.c_flags = SCF_CMD_AC | (cs == NULL ? SCF_RSP_R0 : SCF_RSP_R1);
+ cmd.c_arg = sf == NULL ? 0 : MMC_ARG_RCA(sf->rca);
+ cmd.c_flags = SCF_CMD_AC | (sf == NULL ? SCF_RSP_R0 : SCF_RSP_R1);
error = sdmmc_mmc_command(sc, &cmd);
- if (error == 0 || cs == NULL)
- sc->sc_card = cs;
+ if (error == 0 || sf == NULL)
+ sc->sc_card = sf;
return error;
}
int
sdmmc_decode_csd(struct sdmmc_softc *sc, sdmmc_response resp,
- struct sdmmc_card *cs)
+ struct sdmmc_function *sf)
{
- struct sdmmc_csd *csd = &cs->csd;
+ struct sdmmc_csd *csd = &sf->csd;
if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
/*
@@ -507,9 +496,9 @@ sdmmc_decode_csd(struct sdmmc_softc *sc, sdmmc_response resp,
int
sdmmc_decode_cid(struct sdmmc_softc *sc, sdmmc_response resp,
- struct sdmmc_card *cs)
+ struct sdmmc_function *sf)
{
- struct sdmmc_cid *cid = &cs->cid;
+ struct sdmmc_cid *cid = &sf->cid;
if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
cid->mid = SD_CID_MID(resp);
@@ -519,7 +508,7 @@ sdmmc_decode_cid(struct sdmmc_softc *sc, sdmmc_response resp,
cid->psn = SD_CID_PSN(resp);
cid->mdt = SD_CID_MDT(resp);
} else {
- switch(cs->csd.mmcver) {
+ switch(sf->csd.mmcver) {
case MMC_CSD_MMCVER_1_0:
case MMC_CSD_MMCVER_1_4:
cid->mid = MMC_CID_MID_V1(resp);
@@ -538,7 +527,7 @@ sdmmc_decode_cid(struct sdmmc_softc *sc, sdmmc_response resp,
break;
default:
printf("%s: unknown MMC version %d\n",
- SDMMCDEVNAME(sc), cs->csd.mmcver);
+ SDMMCDEVNAME(sc), sf->csd.mmcver);
return 1;
}
}
diff --git a/sys/dev/sdmmc/sdmmc_cis.c b/sys/dev/sdmmc/sdmmc_cis.c
new file mode 100644
index 00000000000..47c21ccc01f
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmc_cis.c
@@ -0,0 +1,194 @@
+/* $OpenBSD: sdmmc_cis.c,v 1.1 2006/06/01 21:53:41 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* Routines to decode the Card Information Structure of SD I/O cards */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+
+#include <dev/sdmmc/sdmmc_ioreg.h>
+#include <dev/sdmmc/sdmmcdevs.h>
+#include <dev/sdmmc/sdmmcvar.h>
+
+u_int32_t sdmmc_cisptr(struct sdmmc_function *);
+
+#ifdef SDMMC_DEBUG
+#define DPRINTF(s) printf s
+#else
+#define DPRINTF(s) /**/
+#endif
+
+u_int32_t
+sdmmc_cisptr(struct sdmmc_function *sf)
+{
+ u_int32_t cisptr = 0;
+
+ /* XXX where is the per-function CIS pointer register? */
+ if (sf->number != 0)
+ return SD_IO_CIS_START;
+
+ /* XXX is the CIS pointer stored in little-endian format? */
+ cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR+0) << 0;
+ cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR+1) << 8;
+ cisptr |= sdmmc_io_read_1(sf, SD_IO_CCCR_CISPTR+2) << 16;
+ return cisptr;
+}
+
+int
+sdmmc_read_cis(struct sdmmc_function *sf, struct sdmmc_cis *cis)
+{
+ int reg;
+ u_int8_t tplcode;
+ u_int8_t tpllen;
+
+ bzero(cis, sizeof *cis);
+
+ /* XXX read per-function CIS */
+ if (sf->number != 0)
+ return 1;
+
+ reg = (int)sdmmc_cisptr(sf);
+ if (reg < SD_IO_CIS_START ||
+ reg >= (SD_IO_CIS_START+SD_IO_CIS_SIZE-16)) {
+ printf("%s: bad CIS ptr %#x\n", SDMMCDEVNAME(sf->sc), reg);
+ return 1;
+ }
+
+ for (;;) {
+ tplcode = sdmmc_io_read_1(sf, reg++);
+ tpllen = sdmmc_io_read_1(sf, reg++);
+
+ if (tplcode == 0xff || tpllen == 0) {
+ if (tplcode != 0xff)
+ printf("%s: CIS parse error at %d, "
+ "tuple code %#x, length %d\n",
+ SDMMCDEVNAME(sf->sc), reg, tplcode, tpllen);
+ break;
+ }
+
+ switch (tplcode) {
+ case SD_IO_CISTPL_FUNCID:
+ if (tpllen < 2) {
+ printf("%s: bad CISTPL_FUNCID length\n",
+ SDMMCDEVNAME(sf->sc));
+ reg += tpllen;
+ break;
+ }
+ cis->function = sdmmc_io_read_1(sf, reg);
+ reg += tpllen;
+ break;
+ case SD_IO_CISTPL_MANFID:
+ if (tpllen < 4) {
+ printf("%s: bad CISTPL_MANFID length\n",
+ SDMMCDEVNAME(sf->sc));
+ reg += tpllen;
+ break;
+ }
+ cis->manufacturer = sdmmc_io_read_1(sf, reg++);
+ cis->manufacturer |= sdmmc_io_read_1(sf, reg++) << 8;
+ cis->product = sdmmc_io_read_1(sf, reg++);
+ cis->product |= sdmmc_io_read_1(sf, reg++) << 8;
+ break;
+ case SD_IO_CISTPL_VERS_1:
+ if (tpllen < 2) {
+ printf("%s: CISTPL_VERS_1 too short\n",
+ SDMMCDEVNAME(sf->sc));
+ reg += tpllen;
+ break;
+ }
+ {
+ int start, i, ch, count;
+
+ cis->cis1_major = sdmmc_io_read_1(sf, reg++);
+ cis->cis1_minor = sdmmc_io_read_1(sf, reg++);
+
+ for (count = 0, start = 0, i = 0;
+ (count < 4) && ((i + 4) < 256); i++) {
+ ch = sdmmc_io_read_1(sf, reg + i);
+ if (ch == 0xff)
+ break;
+ cis->cis1_info_buf[i] = ch;
+ if (ch == 0) {
+ cis->cis1_info[count] =
+ cis->cis1_info_buf + start;
+ start = i + 1;
+ count++;
+ }
+ }
+
+ reg += tpllen - 2;
+ }
+ break;
+ default:
+ DPRINTF(("%s: unknown tuple code %#x, length %d\n",
+ SDMMCDEVNAME(sf->sc), tplcode, tpllen));
+ reg += tpllen;
+ break;
+ }
+ }
+ return 0;
+}
+
+void
+sdmmc_print_cis(struct sdmmc_function *sf)
+{
+ struct sdmmc_cis *cis = &sf->cis;
+ int i;
+
+ printf("%s: CIS version %d.%d\n", SDMMCDEVNAME(sf->sc),
+ cis->cis1_major, cis->cis1_minor);
+
+ printf("%s: CIS info: ", SDMMCDEVNAME(sf->sc));
+ for (i = 0; i < 4; i++) {
+ if (cis->cis1_info[i] == NULL)
+ break;
+ if (i)
+ printf(", ");
+ printf("%s", cis->cis1_info[i]);
+ }
+ printf("\n");
+
+ printf("%s: Manufacturer code 0x%x, product 0x%x\n",
+ SDMMCDEVNAME(sf->sc), cis->manufacturer, cis->product);
+
+ printf("%s: function %d: ", SDMMCDEVNAME(sf->sc), sf->number);
+ switch (sf->cis.function) {
+ case SDMMC_FUNCTION_WLAN:
+ printf("wireless network adapter");
+ break;
+ default:
+ printf("unknown (%d)", sf->cis.function);
+ break;
+ }
+ printf("\n");
+}
+
+void
+sdmmc_check_cis_quirks(struct sdmmc_function *sf)
+{
+ if (sf->cis.manufacturer == SDMMC_VENDOR_SPECTEC &&
+ sf->cis.product == SDMMC_PRODUCT_SPECTEC_SDW820) {
+ /* This card lacks the VERS_1 tuple. */
+ sf->cis.cis1_major = 0x01;
+ sf->cis.cis1_minor = 0x00;
+ sf->cis.cis1_info[0] = "Spectec";
+ sf->cis.cis1_info[1] = "SDIO WLAN Card";
+ sf->cis.cis1_info[2] = "SDW-820";
+ sf->cis.cis1_info[3] = "";
+ }
+}
diff --git a/sys/dev/sdmmc/sdmmc_io.c b/sys/dev/sdmmc/sdmmc_io.c
index f739b210721..f811eb0e7e3 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.2 2006/05/28 18:45:23 uwe Exp $ */
+/* $OpenBSD: sdmmc_io.c,v 1.3 2006/06/01 21:53:41 uwe Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -21,16 +21,36 @@
#include <sys/param.h>
#include <sys/systm.h>
+#include <dev/sdmmc/sdmmc_ioreg.h>
#include <dev/sdmmc/sdmmcchip.h>
#include <dev/sdmmc/sdmmcreg.h>
#include <dev/sdmmc/sdmmcvar.h>
+int sdmmc_submatch(struct device *, void *, void *);
+int sdmmc_print(void *, const char *);
+int sdmmc_io_rw_direct(struct sdmmc_softc *, struct sdmmc_function *,
+ int, u_char *, int);
+int sdmmc_io_rw_extended(struct sdmmc_softc *, struct sdmmc_function *,
+ int, u_char *, int, int);
+int sdmmc_io_write(struct sdmmc_softc *, struct sdmmc_function *,
+ int, u_char);
+int sdmmc_io_xchg(struct sdmmc_softc *, struct sdmmc_function *,
+ int, u_char *);
+void sdmmc_io_reset(struct sdmmc_softc *);
+int sdmmc_io_send_op_cond(struct sdmmc_softc *, u_int32_t, u_int32_t *);
+
#ifdef SDMMC_DEBUG
#define DPRINTF(s) printf s
#else
#define DPRINTF(s) /**/
#endif
+#ifdef SDMMC_DEBUG
+int sdmmc_verbose = 1;
+#else
+int sdmmc_verbose = 0;
+#endif
+
/*
* Initialize SD I/O card functions (before memory cards). The host
* system and controller must support card interrupts in order to use
@@ -45,7 +65,7 @@ sdmmc_io_enable(struct sdmmc_softc *sc)
/* Set host mode to SD "combo" card. */
SET(sc->sc_flags, SMF_SD_MODE|SMF_IO_MODE|SMF_MEM_MODE);
- /* Reset I/O functions (*must* do that before CMD5). */
+ /* Reset I/O functions. */
sdmmc_io_reset(sc);
/*
@@ -60,11 +80,20 @@ sdmmc_io_enable(struct sdmmc_softc *sc)
return 0;
}
- if (SD_IO_OCR_NF(card_ocr) == 0) {
- /* No I/O functions. */
+ /* Parse the additional bits in the I/O OCR value. */
+ if (!ISSET(card_ocr, SD_IO_OCR_MEM_PRESENT)) {
+ /* SDIO card without memory (not a "combo card"). */
+ DPRINTF(("%s: no memory present\n", SDMMCDEVNAME(sc)));
+ CLR(sc->sc_flags, SMF_MEM_MODE);
+ }
+ sc->sc_function_count = SD_IO_OCR_NUM_FUNCTIONS(card_ocr);
+ if (sc->sc_function_count == 0) {
+ /* Useless SDIO card without any I/O functions. */
DPRINTF(("%s: no I/O functions\n", SDMMCDEVNAME(sc)));
+ CLR(sc->sc_flags, SMF_IO_MODE);
return 0;
}
+ card_ocr &= SD_IO_OCR_MASK;
/* Set the lowest voltage supported by the card and host. */
host_ocr = sdmmc_chip_host_ocr(sc->sct, sc->sch);
@@ -86,18 +115,300 @@ sdmmc_io_enable(struct sdmmc_softc *sc)
}
/*
- * Send the "I/O RESET" command.
+ * Allocate sdmmc_function structures for SD card I/O function
+ * (including function 0).
*/
void
-sdmmc_io_reset(struct sdmmc_softc *sc)
+sdmmc_io_scan(struct sdmmc_softc *sc)
+{
+ struct sdmmc_function *sf0, *sf;
+ int i;
+
+ sf0 = sdmmc_function_alloc(sc);
+ sf0->number = 0;
+ if (sdmmc_set_relative_addr(sc, sf0) != 0) {
+ printf("%s: can't set I/O RCA\n", SDMMCDEVNAME(sc));
+ SET(sf0->flags, SFF_ERROR);
+ return;
+ }
+ sc->sc_fn0 = sf0;
+ SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf0, sf_list);
+
+ /* Verify that the RCA has been set by selecting the card. */
+ if (sdmmc_select_card(sc, sf0) != 0) {
+ printf("%s: can't select I/O RCA %d\n", SDMMCDEVNAME(sc),
+ sf0->rca);
+ SET(sf0->flags, SFF_ERROR);
+ return;
+ }
+
+ for (i = 1; i <= sc->sc_function_count; i++) {
+ sf = sdmmc_function_alloc(sc);
+ sf->number = i;
+ sf->rca = sf0->rca;
+
+ SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf, sf_list);
+ }
+}
+
+/*
+ * Initialize SDIO card functions.
+ */
+int
+sdmmc_io_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
+{
+ if (sf->number == 0) {
+ (void)sdmmc_io_write(sc, sf, SD_IO_CCCR_BUS_WIDTH,
+ CCCR_BUS_WIDTH_1);
+
+ if (sdmmc_read_cis(sf, &sf->cis) != 0) {
+ printf("%s: can't read CIS\n");
+ SET(sf->flags, SFF_ERROR);
+ return 1;
+ }
+
+ sdmmc_check_cis_quirks(sf);
+
+ if (sdmmc_verbose)
+ sdmmc_print_cis(sf);
+ }
+ return 0;
+}
+
+void
+sdmmc_io_function_enable(struct sdmmc_function *sf)
+{
+ struct sdmmc_function *sf0 = sf->sc->sc_fn0;
+ u_int8_t rv;
+
+ 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);
+}
+
+void
+sdmmc_io_function_disable(struct sdmmc_function *sf)
+{
+ struct sdmmc_function *sf0 = sf->sc->sc_fn0;
+ u_int8_t rv;
+
+ 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);
+}
+
+void
+sdmmc_io_attach(struct sdmmc_softc *sc)
+{
+ struct sdmmc_function *sf;
+ struct sdmmc_attach_args saa;
+
+ SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
+ bzero(&saa, sizeof saa);
+ saa.sf = sf;
+
+ sf->child = config_found_sm(&sc->sc_dev, &saa, sdmmc_print,
+ sdmmc_submatch);
+ }
+}
+
+int
+sdmmc_submatch(struct device *parent, void *match, void *aux)
+{
+ struct cfdata *cf = match;
+
+ /* Skip the scsibus, it is configured directly. */
+ if (strcmp(cf->cf_driver->cd_name, "scsibus") == 0)
+ return 0;
+
+ return cf->cf_attach->ca_match(parent, cf, aux);
+}
+
+int
+sdmmc_print(void *aux, const char *pnp)
+{
+ struct sdmmc_attach_args *sa = aux;
+ struct sdmmc_function *sf = sa->sf;
+ struct sdmmc_cis *cis = &sf->sc->sc_fn0->cis;
+ int i;
+
+ if (pnp) {
+ if (sf->number == 0)
+ return QUIET;
+
+ for (i = 0; i < 4 && cis->cis1_info[i]; i++)
+ printf("%s%s", i ? ", " : "\"", cis->cis1_info[i]);
+ if (i != 0)
+ printf("\"");
+
+ if (cis->manufacturer != SDMMC_VENDOR_INVALID &&
+ cis->product != SDMMC_PRODUCT_INVALID) {
+ printf("%s(", i ? " " : "");
+ if (cis->manufacturer != SDMMC_VENDOR_INVALID)
+ printf("manufacturer 0x%x%s",
+ cis->manufacturer,
+ cis->product == SDMMC_PRODUCT_INVALID ?
+ "" : ", ");
+ if (cis->product != SDMMC_PRODUCT_INVALID)
+ printf("product 0x%x", cis->product);
+ printf(")");
+ }
+ printf("%sat %s", i ? " " : "", pnp);
+ }
+ printf(" function %d", sf->number);
+ return UNCONF;
+}
+
+void
+sdmmc_io_detach(struct sdmmc_softc *sc)
+{
+ struct sdmmc_function *sf;
+
+ SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
+ if (sf->child != NULL) {
+ config_detach(sf->child, DETACH_FORCE);
+ sf->child = NULL;
+ }
+ }
+}
+
+int
+sdmmc_io_rw_direct(struct sdmmc_softc *sc, struct sdmmc_function *sf,
+ int reg, u_char *datap, int arg)
{
struct sdmmc_command cmd;
+ int error;
+
+ /* Make sure the card is selected. */
+ if ((error = sdmmc_select_card(sc, sf)) != 0)
+ return error;
+
+ arg |= ((sf == NULL ? 0 : sf->number) & SD_ARG_CMD52_FUNC_MASK) <<
+ SD_ARG_CMD52_FUNC_SHIFT;
+ arg |= (reg & SD_ARG_CMD52_REG_MASK) <<
+ SD_ARG_CMD52_REG_SHIFT;
+ arg |= (*datap & SD_ARG_CMD52_DATA_MASK) <<
+ SD_ARG_CMD52_DATA_SHIFT;
bzero(&cmd, sizeof cmd);
- cmd.c_opcode = SD_IO_RESET;
- cmd.c_flags = SCF_CMD_BC | SCF_RSP_R0;
+ cmd.c_opcode = SD_IO_RW_DIRECT;
+ cmd.c_arg = arg;
+ cmd.c_flags = SCF_CMD_BC/* XXX */ | SCF_RSP_R5;
- (void)sdmmc_mmc_command(sc, &cmd);
+ error = sdmmc_mmc_command(sc, &cmd);
+ *datap = SD_R5_DATA(cmd.c_resp);
+ return error;
+}
+
+int
+sdmmc_io_rw_extended(struct sdmmc_softc *sc, struct sdmmc_function *sf,
+ int reg, u_char *datap, int datalen, int arg)
+{
+ struct sdmmc_command cmd;
+ int error;
+
+ /* Make sure the card is selected. */
+ if ((error = sdmmc_select_card(sc, sf)) != 0)
+ return error;
+
+ arg |= ((sf == NULL ? 0 : sf->number) & SD_ARG_CMD53_FUNC_MASK) <<
+ SD_ARG_CMD53_FUNC_SHIFT;
+ arg |= SD_ARG_CMD53_INCREMENT;
+ arg |= (reg & SD_ARG_CMD53_REG_MASK) <<
+ SD_ARG_CMD53_REG_SHIFT;
+ arg |= (datalen & SD_ARG_CMD53_LENGTH_MASK) <<
+ SD_ARG_CMD53_LENGTH_SHIFT;
+
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = SD_IO_RW_EXTENDED;
+ cmd.c_arg = arg;
+ cmd.c_flags = SCF_CMD_BC/* XXX */ | SCF_RSP_R5;
+ cmd.c_data = datap;
+ cmd.c_datalen = datalen;
+ cmd.c_blklen = datalen;
+ if (!ISSET(arg, SD_ARG_CMD53_WRITE))
+ cmd.c_flags |= SCF_CMD_READ;
+
+ return sdmmc_mmc_command(sc, &cmd);
+}
+
+u_int8_t
+sdmmc_io_read_1(struct sdmmc_function *sf, int reg)
+{
+ u_int8_t data = 0;
+
+ (void)sdmmc_io_rw_direct(sf->sc, sf, reg, (u_char *)&data,
+ SD_ARG_CMD52_READ);
+ return data;
+}
+
+void
+sdmmc_io_write_1(struct sdmmc_function *sf, int reg, u_int8_t data)
+{
+ (void)sdmmc_io_rw_direct(sf->sc, sf, reg, (u_char *)&data,
+ SD_ARG_CMD52_WRITE);
+}
+
+u_int16_t
+sdmmc_io_read_2(struct sdmmc_function *sf, int reg)
+{
+ u_int16_t data = 0;
+
+ (void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 2,
+ SD_ARG_CMD53_READ);
+ return data;
+}
+
+void
+sdmmc_io_write_2(struct sdmmc_function *sf, int reg, u_int16_t data)
+{
+ (void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 2,
+ SD_ARG_CMD53_WRITE);
+}
+
+u_int32_t
+sdmmc_io_read_4(struct sdmmc_function *sf, int reg)
+{
+ u_int32_t data = 0;
+
+ (void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 4,
+ SD_ARG_CMD53_READ);
+ return data;
+}
+
+void
+sdmmc_io_write_4(struct sdmmc_function *sf, int reg, u_int32_t data)
+{
+ (void)sdmmc_io_rw_extended(sf->sc, sf, reg, (u_char *)&data, 4,
+ SD_ARG_CMD53_WRITE);
+}
+
+int
+sdmmc_io_write(struct sdmmc_softc *sc, struct sdmmc_function *sf,
+ int reg, u_char data)
+{
+ return sdmmc_io_rw_direct(sc, sf, reg, &data,
+ SD_ARG_CMD53_WRITE);
+}
+
+int
+sdmmc_io_xchg(struct sdmmc_softc *sc, struct sdmmc_function *sf,
+ int reg, u_char *datap)
+{
+ return sdmmc_io_rw_direct(sc, sf, reg, datap,
+ SD_ARG_CMD52_WRITE|SD_ARG_CMD52_EXCHANGE);
+}
+
+/*
+ * Reset the I/O functions of the card.
+ */
+void
+sdmmc_io_reset(struct sdmmc_softc *sc)
+{
+#if 0 /* XXX command fails */
+ (void)sdmmc_io_write(sc, NULL, SD_IO_REG_CCCR_CTL, CCCR_CTL_RES);
+ sdmmc_delay(100000);
+#endif
}
/*
@@ -113,7 +424,7 @@ sdmmc_io_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, u_int32_t *ocrp)
/*
* If we change the OCR value, retry the command until the OCR
* we receive in response has the "CARD BUSY" bit set, meaning
- * that all cards are ready for card identification.
+ * that all cards are ready for identification.
*/
for (i = 0; i < 100; i++) {
bzero(&cmd, sizeof cmd);
@@ -130,8 +441,6 @@ sdmmc_io_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr, u_int32_t *ocrp)
error = ETIMEDOUT;
sdmmc_delay(10000);
}
- if (error == 0)
- printf("ocr: %x\n", MMC_R4(cmd.c_resp));
if (error == 0 && ocrp != NULL)
*ocrp = MMC_R4(cmd.c_resp);
return error;
diff --git a/sys/dev/sdmmc/sdmmc_ioreg.h b/sys/dev/sdmmc/sdmmc_ioreg.h
new file mode 100644
index 00000000000..7c36d941583
--- /dev/null
+++ b/sys/dev/sdmmc/sdmmc_ioreg.h
@@ -0,0 +1,90 @@
+/* $OpenBSD: sdmmc_ioreg.h,v 1.1 2006/06/01 21:53:41 uwe Exp $ */
+
+/*
+ * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _SDMMC_IOREG_H
+#define _SDMMC_IOREG_H
+
+/* SDIO commands */ /* response type */
+#define SD_IO_SEND_OP_COND 5 /* R4 */
+#define SD_IO_RW_DIRECT 52 /* R5 */
+#define SD_IO_RW_EXTENDED 53 /* R5? */
+
+/* CMD52 arguments */
+#define SD_ARG_CMD52_READ (0<<31)
+#define SD_ARG_CMD52_WRITE (1<<31)
+#define SD_ARG_CMD52_FUNC_SHIFT 28
+#define SD_ARG_CMD52_FUNC_MASK 0x7
+#define SD_ARG_CMD52_EXCHANGE (1<<27)
+#define SD_ARG_CMD52_REG_SHIFT 9
+#define SD_ARG_CMD52_REG_MASK 0x1ffff
+#define SD_ARG_CMD52_DATA_SHIFT 0
+#define SD_ARG_CMD52_DATA_MASK 0xff
+#define SD_R5_DATA(resp) ((resp)[0] & 0xff)
+
+/* CMD53 arguments */
+#define SD_ARG_CMD53_READ (0<<31)
+#define SD_ARG_CMD53_WRITE (1<<31)
+#define SD_ARG_CMD53_FUNC_SHIFT 28
+#define SD_ARG_CMD53_FUNC_MASK 0x7
+#define SD_ARG_CMD53_BLOCK_MODE (1<<27)
+#define SD_ARG_CMD53_INCREMENT (1<<26)
+#define SD_ARG_CMD53_REG_SHIFT 9
+#define SD_ARG_CMD53_REG_MASK 0x1ffff
+#define SD_ARG_CMD53_LENGTH_SHIFT 0
+#define SD_ARG_CMD53_LENGTH_MASK 0x1ff
+
+/* 48-bit response decoding (32 bits w/o CRC) */
+#define MMC_R4(resp) ((resp)[0])
+#define MMC_R5(resp) ((resp)[0])
+
+/* SD R4 response (IO OCR) */
+#define SD_IO_OCR_MEM_READY (1<<31)
+#define SD_IO_OCR_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x3)
+/* XXX big fat memory present "flag" because we don't know better */
+#define SD_IO_OCR_MEM_PRESENT (0xf<<24)
+#define SD_IO_OCR_MASK 0x00fffff0
+
+/* Card Common Control Registers (CCCR) */
+#define SD_IO_CCCR_START 0x00000
+#define SD_IO_CCCR_SIZE 0x100
+#define SD_IO_CCCR_FN_ENABLE 0x02
+#define SD_IO_CCCR_CTL 0x06
+#define CCCR_CTL_RES (1<<3)
+#define SD_IO_CCCR_BUS_WIDTH 0x07
+#define CCCR_BUS_WIDTH_4 (1<<1)
+#define CCCR_BUS_WIDTH_1 (1<<0)
+#define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */
+
+/* Function Basic Registers (FBR) */
+#define SD_IO_FBR_START 0x00100
+#define SD_IO_FBR_SIZE 0x00700
+
+/* Card Information Structure (CIS) */
+#define SD_IO_CIS_START 0x01000
+#define SD_IO_CIS_SIZE 0x17000
+
+/* CIS tuple codes (based on PC Card 16) */
+#define SD_IO_CISTPL_VERS_1 0x15
+#define SD_IO_CISTPL_MANFID 0x20
+#define SD_IO_CISTPL_FUNCID 0x21
+#define SD_IO_CISTPL_FUNCE 0x22
+
+/* CISTPL_FUNCID codes */
+#define SDMMC_FUNCTION_WLAN 0x0c
+
+#endif
diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c
index 0399b9e016d..916385123d4 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.2 2006/05/28 18:45:23 uwe Exp $ */
+/* $OpenBSD: sdmmc_mem.c,v 1.3 2006/06/01 21:53:41 uwe Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -19,6 +19,8 @@
/* Routines for SD/MMC memory cards. */
#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
#include <sys/systm.h>
#include <dev/sdmmc/sdmmcchip.h>
@@ -26,7 +28,7 @@
#include <dev/sdmmc/sdmmcvar.h>
int sdmmc_mem_send_op_cond(struct sdmmc_softc *, u_int32_t, u_int32_t *);
-int sdmmc_mem_set_blocklen(struct sdmmc_softc *, struct sdmmc_card *);
+int sdmmc_mem_set_blocklen(struct sdmmc_softc *, struct sdmmc_function *);
#ifdef SDMMC_DEBUG
#define DPRINTF(s) printf s
@@ -65,8 +67,8 @@ sdmmc_mem_enable(struct sdmmc_softc *sc)
goto mmc_mode;
}
if (!ISSET(sc->sc_flags, SMF_SD_MODE)) {
- printf("%s: can't read memory OCR\n",
- SDMMCDEVNAME(sc));
+ DPRINTF(("%s: can't read memory OCR\n",
+ SDMMCDEVNAME(sc)));
return 1;
} else {
/* Not a "combo" card. */
@@ -78,8 +80,8 @@ sdmmc_mem_enable(struct sdmmc_softc *sc)
/* Set the lowest voltage supported by the card and host. */
host_ocr = sdmmc_chip_host_ocr(sc->sct, sc->sch);
if (sdmmc_set_bus_power(sc, host_ocr, card_ocr) != 0) {
- printf("%s: can't supply voltage requested by card\n",
- SDMMCDEVNAME(sc));
+ DPRINTF(("%s: can't supply voltage requested by card\n",
+ SDMMCDEVNAME(sc)));
return 1;
}
@@ -88,20 +90,124 @@ sdmmc_mem_enable(struct sdmmc_softc *sc)
/* Send the new OCR value until all cards are ready. */
if (sdmmc_mem_send_op_cond(sc, host_ocr, NULL) != 0) {
- printf("%s: can't send memory OCR\n", SDMMCDEVNAME(sc));
+ DPRINTF(("%s: can't send memory OCR\n", SDMMCDEVNAME(sc)));
return 1;
}
return 0;
}
/*
+ * Read the CSD and CID from all cards and assign each card a unique
+ * relative card address (RCA). CMD2 is ignored by SDIO-only cards.
+ */
+void
+sdmmc_mem_scan(struct sdmmc_softc *sc)
+{
+ struct sdmmc_command cmd;
+ struct sdmmc_function *sf;
+ u_int16_t next_rca;
+ int error;
+ int i;
+
+ /*
+ * CMD2 is a broadcast command understood by SD cards and MMC
+ * cards. All cards begin to respond to the command, but back
+ * off if another card drives the CMD line to a different level.
+ * Only one card will get its entire response through. That
+ * card remains silent once it has been assigned a RCA.
+ */
+ for (i = 0; i < 100; i++) {
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = MMC_ALL_SEND_CID;
+ cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R2;
+
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error == ETIMEDOUT) {
+ /* No more cards there. */
+ break;
+ } else if (error != 0) {
+ DPRINTF(("%s: can't read CID\n", SDMMCDEVNAME(sc)));
+ break;
+ }
+
+ /* In MMC mode, find the next available RCA. */
+ next_rca = 0;
+ if (!ISSET(sc->sc_flags, SMF_SD_MODE))
+ SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list)
+ next_rca++;
+
+ /* Allocate a sdmmc_function structure. */
+ sf = sdmmc_function_alloc(sc);
+ sf->rca = next_rca;
+
+ /*
+ * Remember the CID returned in the CMD2 response for
+ * later decoding.
+ */
+ bcopy(cmd.c_resp, sf->raw_cid, sizeof sf->raw_cid);
+
+ /*
+ * Silence the card by assigning it a unique RCA, or
+ * querying it for its RCA in the case of SD.
+ */
+ if (sdmmc_set_relative_addr(sc, sf) != 0) {
+ printf("%s: can't set mem RCA\n", SDMMCDEVNAME(sc));
+ sdmmc_function_free(sf);
+ break;
+ }
+
+#if 0
+ /* Verify that the RCA has been set by selecting the card. */
+ if (sdmmc_select_card(sc, sf) != 0) {
+ printf("%s: can't select mem RCA %d\n",
+ SDMMCDEVNAME(sc), sf->rca);
+ sdmmc_function_free(sf);
+ break;
+ }
+
+ /* Deselect. */
+ (void)sdmmc_select_card(sc, NULL);
+#endif
+
+ SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf, sf_list);
+ }
+
+ /*
+ * All cards are either inactive or awaiting further commands.
+ * Read the CSDs and decode the raw CID for each card.
+ */
+ SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
+ bzero(&cmd, sizeof cmd);
+ cmd.c_opcode = MMC_SEND_CSD;
+ cmd.c_arg = MMC_ARG_RCA(sf->rca);
+ cmd.c_flags = SCF_CMD_AC | SCF_RSP_R2;
+
+ if (sdmmc_mmc_command(sc, &cmd) != 0) {
+ SET(sf->flags, SFF_ERROR);
+ continue;
+ }
+
+ if (sdmmc_decode_csd(sc, cmd.c_resp, sf) != 0 ||
+ sdmmc_decode_cid(sc, sf->raw_cid, sf) != 0) {
+ SET(sf->flags, SFF_ERROR);
+ continue;
+ }
+
+#ifdef SDMMC_DEBUG
+ printf("%s: CID: ", SDMMCDEVNAME(sc));
+ sdmmc_print_cid(&sf->cid);
+#endif
+ }
+}
+
+/*
* Initialize a SD/MMC memory card.
*/
int
-sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
- if (sdmmc_select_card(sc, cs) != 0 ||
- sdmmc_mem_set_blocklen(sc, cs) != 0)
+ if (sdmmc_select_card(sc, sf) != 0 ||
+ sdmmc_mem_set_blocklen(sc, sf) != 0)
return 1;
return 0;
}
@@ -120,7 +226,7 @@ sdmmc_mem_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr,
/*
* If we change the OCR value, retry the command until the OCR
* we receive in response has the "CARD BUSY" bit set, meaning
- * that all cards are ready for card identification.
+ * that all cards are ready for identification.
*/
for (i = 0; i < 100; i++) {
bzero(&cmd, sizeof cmd);
@@ -152,34 +258,34 @@ sdmmc_mem_send_op_cond(struct sdmmc_softc *sc, u_int32_t ocr,
* the card CSD register value.
*/
int
-sdmmc_mem_set_blocklen(struct sdmmc_softc *sc, struct sdmmc_card *cs)
+sdmmc_mem_set_blocklen(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
struct sdmmc_command cmd;
bzero(&cmd, sizeof cmd);
cmd.c_opcode = MMC_SET_BLOCKLEN;
- cmd.c_arg = cs->csd.sector_size;
+ cmd.c_arg = sf->csd.sector_size;
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
DPRINTF(("%s: read_bl_len=%d sector_size=%d\n", SDMMCDEVNAME(sc),
- 1 << cs->csd.read_bl_len, cs->csd.sector_size));
+ 1 << sf->csd.read_bl_len, sf->csd.sector_size));
return sdmmc_mmc_command(sc, &cmd);
}
int
-sdmmc_mem_read_block(struct sdmmc_softc *sc, struct sdmmc_card *cs,
+sdmmc_mem_read_block(struct sdmmc_softc *sc, struct sdmmc_function *sf,
int blkno, u_char *data, size_t datalen)
{
struct sdmmc_command cmd;
int error;
- if ((error = sdmmc_select_card(sc, cs)) != 0)
+ if ((error = sdmmc_select_card(sc, sf)) != 0)
return error;
bzero(&cmd, sizeof cmd);
cmd.c_data = data;
cmd.c_datalen = datalen;
- cmd.c_blklen = cs->csd.sector_size;
+ cmd.c_blklen = sf->csd.sector_size;
cmd.c_opcode = (datalen / cmd.c_blklen) > 1 ?
MMC_READ_BLOCK_MULTIPLE : MMC_READ_BLOCK_SINGLE;
cmd.c_arg = blkno << 9;
@@ -192,7 +298,7 @@ sdmmc_mem_read_block(struct sdmmc_softc *sc, struct sdmmc_card *cs,
do {
bzero(&cmd, sizeof cmd);
cmd.c_opcode = MMC_SEND_STATUS;
- cmd.c_arg = MMC_ARG_RCA(cs->rca);
+ cmd.c_arg = MMC_ARG_RCA(sf->rca);
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
error = sdmmc_mmc_command(sc, &cmd);
if (error != 0)
@@ -204,19 +310,19 @@ sdmmc_mem_read_block(struct sdmmc_softc *sc, struct sdmmc_card *cs,
}
int
-sdmmc_mem_write_block(struct sdmmc_softc *sc, struct sdmmc_card *cs,
+sdmmc_mem_write_block(struct sdmmc_softc *sc, struct sdmmc_function *sf,
int blkno, u_char *data, size_t datalen)
{
struct sdmmc_command cmd;
int error;
- if ((error = sdmmc_select_card(sc, cs)) != 0)
+ if ((error = sdmmc_select_card(sc, sf)) != 0)
return error;
bzero(&cmd, sizeof cmd);
cmd.c_data = data;
cmd.c_datalen = datalen;
- cmd.c_blklen = cs->csd.sector_size;
+ cmd.c_blklen = sf->csd.sector_size;
cmd.c_opcode = (datalen / cmd.c_blklen) > 1 ?
MMC_WRITE_BLOCK_MULTIPLE : MMC_WRITE_BLOCK_SINGLE;
cmd.c_arg = blkno << 9;
@@ -229,7 +335,7 @@ sdmmc_mem_write_block(struct sdmmc_softc *sc, struct sdmmc_card *cs,
do {
bzero(&cmd, sizeof cmd);
cmd.c_opcode = MMC_SEND_STATUS;
- cmd.c_arg = MMC_ARG_RCA(cs->rca);
+ cmd.c_arg = MMC_ARG_RCA(sf->rca);
cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
error = sdmmc_mmc_command(sc, &cmd);
if (error != 0)
diff --git a/sys/dev/sdmmc/sdmmc_scsi.c b/sys/dev/sdmmc/sdmmc_scsi.c
index 5f75c07e5e7..447617112ef 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.2 2006/05/28 18:45:23 uwe Exp $ */
+/* $OpenBSD: sdmmc_scsi.c,v 1.3 2006/06/01 21:53:41 uwe Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -49,7 +49,8 @@ void
sdmmc_scsi_attach(struct sdmmc_softc *sc)
{
struct sdmmc_scsi_softc *scbus;
- struct sdmmc_card *cs;
+ struct sdmmc_function *sf;
+ struct sdmmc_attach_args saa;
MALLOC(scbus, struct sdmmc_scsi_softc *,
sizeof *scbus, M_DEVBUF, M_WAITOK);
@@ -65,10 +66,10 @@ sdmmc_scsi_attach(struct sdmmc_softc *sc)
* gets a SCSI ID > 0, whether it is a memory card or not.
*/
scbus->sc_ntargets = 1;
- SIMPLEQ_FOREACH(cs, &sc->cs_head, cs_list) {
+ SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
if (scbus->sc_ntargets >= SDMMC_SCSIID_MAX+1)
break;
- scbus->sc_tgt[scbus->sc_ntargets].card = cs;
+ scbus->sc_tgt[scbus->sc_ntargets].card = sf;
scbus->sc_ntargets++;
}
@@ -84,8 +85,10 @@ sdmmc_scsi_attach(struct sdmmc_softc *sc)
scbus->sc_link.openings = 1;
scbus->sc_link.adapter = &scbus->sc_adapter;
- scbus->sc_child = config_found(&sc->sc_dev, &scbus->sc_link,
- scsiprint);
+ bzero(&saa, sizeof saa);
+ bcopy(&scbus->sc_link, &saa.scsi_link, sizeof saa.scsi_link);
+
+ scbus->sc_child = config_found(&sc->sc_dev, &saa, scsiprint);
}
void
diff --git a/sys/dev/sdmmc/sdmmc_scsi.h b/sys/dev/sdmmc/sdmmc_scsi.h
index 4dd12e32953..1bf29badbdf 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.1 2006/05/28 17:21:14 uwe Exp $ */
+/* $OpenBSD: sdmmc_scsi.h,v 1.2 2006/06/01 21:53:41 uwe Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -22,7 +22,7 @@
struct sdmmc_softc;
struct sdmmc_scsi_target {
- struct sdmmc_card *card;
+ struct sdmmc_function *card;
};
struct sdmmc_scsi_softc {
diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h
index 5f0f8c34a38..66b054501aa 100644
--- a/sys/dev/sdmmc/sdmmcreg.h
+++ b/sys/dev/sdmmc/sdmmcreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcreg.h,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+/* $OpenBSD: sdmmcreg.h,v 1.2 2006/06/01 21:53:41 uwe Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -37,8 +37,6 @@
/* SD commands */ /* response type */
#define SD_SEND_RELATIVE_ADDR 3 /* R6 */
-#define SD_IO_SEND_OP_COND 5 /* R4 */
-#define SD_IO_RESET 52 /* R0 */
/* SD application commands */ /* response type */
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
@@ -71,19 +69,19 @@
#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */
#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */
+/* 48-bit response decoding (32 bits w/o CRC) */
+#define MMC_R1(resp) ((resp)[0])
+#define MMC_R3(resp) ((resp)[0])
+#define SD_R6(resp) ((resp)[0])
+
/* RCA argument and response */
#define MMC_ARG_RCA(rca) ((rca) << 16)
-#define SD_R6_RCA(resp) ((resp)[0] >> 16)
+#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16)
/* bus width argument */
#define SD_ARG_BUS_WIDTH_1 0
#define SD_ARG_BUS_WIDTH_4 2
-/* 48-bit response decoding (32 bits w/o CRC) */
-#define MMC_R1(resp) ((resp)[0])
-#define MMC_R3(resp) ((resp)[0])
-#define MMC_R4(resp) ((resp)[0])
-
/* MMC R2 response (CSD) */
#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
#define MMC_CSD_CSDVER_1_0 1
@@ -189,16 +187,8 @@
#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32)
#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12)
-/* SD R4 response (IO OCR) */
-#define SD_IO_OCR_MEM_READY (1<<31) /* all memory cards ready */
-#define SD_IO_OCR_MP /* memory present flag */
-#define SD_IO_OCR_NF_SHIFT 28 /* number of functions */
-#define SD_IO_OCR_NF_MASK 0x3 /* 7 functions max. */
-#define SD_IO_OCR_NF(ocr) (((ocr) >> SD_IO_OCR_NF_SHIFT) & \
- SD_IO_OCR_NF_MASK)
-
-/* XXX slow but should work on big and little endian systems. */
-#define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start)-8, (len))
+/* Might be slow, but it should work on big and little endian systems. */
+#define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start)-8, (len))
static __inline int
__bitfield(u_int32_t *src, int start, int len)
{
diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h
index 16ea091b88e..3f69068750c 100644
--- a/sys/dev/sdmmc/sdmmcvar.h
+++ b/sys/dev/sdmmc/sdmmcvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcvar.h,v 1.1 2006/05/28 17:21:14 uwe Exp $ */
+/* $OpenBSD: sdmmcvar.h,v 1.2 2006/06/01 21:53:41 uwe Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -20,6 +20,10 @@
#define _SDMMCVAR_H_
#include <sys/queue.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
#include <dev/sdmmc/sdmmcchip.h>
#include <dev/sdmmc/sdmmcreg.h>
@@ -74,21 +78,57 @@ struct sdmmc_command {
#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136)
#define SCF_RSP_R3 (SCF_RSP_PRESENT)
#define SCF_RSP_R4 (SCF_RSP_PRESENT)
+#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 */
};
-struct sdmmc_card {
- u_int16_t rca;
+/*
+ * Decoded PC Card 16 based Card Information Structure (CIS),
+ * per card (function 0) and per function (1 and greater).
+ */
+struct sdmmc_cis {
+ u_int16_t manufacturer;
+#define SDMMC_VENDOR_INVALID 0xffff
+ u_int16_t product;
+#define SDMMC_PRODUCT_INVALID 0xffff
+ u_int8_t function;
+#define SDMMC_FUNCTION_INVALID 0xff
+ u_char cis1_major;
+ u_char cis1_minor;
+ char cis1_info_buf[256];
+ char *cis1_info[4];
+};
+
+/*
+ * Structure describing either an SD card I/O function or a SD/MMC
+ * memory card from a "stack of cards" that responded to CMD2. For a
+ * combo card with one I/O function and one memory card, there will be
+ * two of these structures allocated. Each card slot has such a list
+ * of sdmmc_function structures.
+ */
+struct sdmmc_function {
+ /* common members */
+ struct sdmmc_softc *sc; /* card slot softc */
+ u_int16_t rca; /* relative card address */
int flags;
-#define SDMMCF_CARD_ERROR 0x0010 /* card in error state */
- sdmmc_response raw_cid;
- struct sdmmc_cid cid;
- struct sdmmc_csd csd;
- SIMPLEQ_ENTRY(sdmmc_card) cs_list;
+#define SFF_ERROR 0x0001 /* function is poo; ignore it */
+ SIMPLEQ_ENTRY(sdmmc_function) sf_list;
+ /* SD card I/O function members */
+ int number; /* I/O function number or -1 */
+ struct device *child; /* function driver */
+ struct sdmmc_cis cis; /* decoded CIS */
+ /* SD/MMC memory card members */
+ struct sdmmc_csd csd; /* decoded CSD value */
+ struct sdmmc_cid cid; /* decoded CID value */
+ sdmmc_response raw_cid; /* temp. storage for decoding */
};
+/*
+ * Structure describing a single SD/MMC/SDIO card slot.
+ */
struct sdmmc_softc {
struct device sc_dev; /* base device */
#define SDMMCDEVNAME(sc) ((sc)->sc_dev.dv_xname)
@@ -96,32 +136,66 @@ struct sdmmc_softc {
sdmmc_chipset_handle_t sch; /* host controller chipset handle */
int sc_flags;
#define SMF_SD_MODE 0x0001 /* host in SD mode (MMC otherwise) */
-#define SMF_IO_MODE 0x0002 /* host in I/O mode (SD only) */
+#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) */
- SIMPLEQ_HEAD(, sdmmc_card) cs_head;
- struct sdmmc_card *sc_card; /* selected card */
+ 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;
+};
+
+/*
+ * Attach devices at the sdmmc bus.
+ */
+struct sdmmc_attach_args {
+ struct scsi_link scsi_link; /* XXX */
+ struct sdmmc_function *sf;
};
#define IPL_SDMMC IPL_BIO
#define splsdmmc() splbio()
+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 *);
void sdmmc_go_idle_state(struct sdmmc_softc *);
-int sdmmc_select_card(struct sdmmc_softc *, struct sdmmc_card *);
+int sdmmc_select_card(struct sdmmc_softc *, struct sdmmc_function *);
+int sdmmc_set_relative_addr(struct sdmmc_softc *,
+ struct sdmmc_function *);
+int sdmmc_decode_csd(struct sdmmc_softc *, sdmmc_response,
+ struct sdmmc_function *);
+int sdmmc_decode_cid(struct sdmmc_softc *, sdmmc_response,
+ struct sdmmc_function *);
+void sdmmc_print_cid(struct sdmmc_cid *);
int sdmmc_io_enable(struct sdmmc_softc *);
-void sdmmc_io_reset(struct sdmmc_softc *);
-int sdmmc_io_send_op_cond(struct sdmmc_softc *, u_int32_t, u_int32_t *);
+void sdmmc_io_scan(struct sdmmc_softc *);
+int sdmmc_io_init(struct sdmmc_softc *, struct sdmmc_function *);
+void sdmmc_io_attach(struct sdmmc_softc *);
+void sdmmc_io_detach(struct sdmmc_softc *);
+u_int8_t sdmmc_io_read_1(struct sdmmc_function *, int);
+u_int16_t sdmmc_io_read_2(struct sdmmc_function *, int);
+u_int32_t sdmmc_io_read_4(struct sdmmc_function *, int);
+void sdmmc_io_write_1(struct sdmmc_function *, int, u_int8_t);
+void sdmmc_io_write_2(struct sdmmc_function *, int, u_int16_t);
+void sdmmc_io_write_4(struct sdmmc_function *, int, u_int32_t);
+void sdmmc_io_function_enable(struct sdmmc_function *);
+void sdmmc_io_function_disable(struct sdmmc_function *);
+
+int sdmmc_read_cis(struct sdmmc_function *, struct sdmmc_cis *);
+void sdmmc_print_cis(struct sdmmc_function *);
+void sdmmc_check_cis_quirks(struct sdmmc_function *);
int sdmmc_mem_enable(struct sdmmc_softc *);
-int sdmmc_mem_init(struct sdmmc_softc *, struct sdmmc_card *);
-int sdmmc_mem_read_block(struct sdmmc_softc *, struct sdmmc_card *,
- int, u_char *, size_t);
-int sdmmc_mem_write_block(struct sdmmc_softc *, struct sdmmc_card *,
- int, u_char *, size_t);
+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);
#endif