diff options
author | Uwe Stuehler <uwe@cvs.openbsd.org> | 2006-06-01 21:53:42 +0000 |
---|---|---|
committer | Uwe Stuehler <uwe@cvs.openbsd.org> | 2006-06-01 21:53:42 +0000 |
commit | b89f9d40f1fe1476b0c5078e2a2e451157066db2 (patch) | |
tree | 9e87d5289bdf55af01bdec6676ba02079a9c38af /sys | |
parent | 6b37ef9f877f52e97b18ea962dbb1c3aa7458dfc (diff) |
SDIO card identification
Diffstat (limited to 'sys')
-rw-r--r-- | sys/dev/sdmmc/files.sdmmc | 4 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc.c | 301 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_cis.c | 194 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_io.c | 333 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_ioreg.h | 90 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_mem.c | 150 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_scsi.c | 15 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_scsi.h | 4 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcreg.h | 28 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcvar.h | 112 |
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 |