diff -ruNp mmc.HEAD/mmc.c mmc/mmc.c --- mmc.HEAD/mmc.c 2008-09-27 15:50:50.000000000 +0300 +++ mmc/mmc.c 2008-09-29 00:14:10.000000000 +0300 @@ -61,6 +61,7 @@ __FBSDID("$FreeBSD: src/sys/dev/mmc/mmc. #include #include #include +#include #include #include @@ -82,10 +83,14 @@ struct mmc_softc { struct mmc_ivars { uint32_t raw_cid[4]; /* Raw bits of the CID */ uint32_t raw_csd[4]; /* Raw bits of the CSD */ + uint32_t raw_scr[2]; /* Raw bits of the SCR */ uint16_t rca; enum mmc_card_mode mode; struct mmc_cid cid; /* cid decoded */ struct mmc_csd csd; /* csd decoded */ + struct mmc_scr scr; /* scr decoded */ + u_char has_scr; + u_char read_only; }; #define CMD_RETRIES 3 @@ -97,8 +102,8 @@ static int mmc_detach(device_t dev); #define MMC_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) #define MMC_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define MMC_LOCK_INIT(_sc) \ - mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ +#define MMC_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->dev), \ "mmc", MTX_DEF) #define MMC_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); #define MMC_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); @@ -109,6 +114,10 @@ static int mmc_wait_for_cmd(struct mmc_s int retries); static int mmc_wait_for_command(struct mmc_softc *sc, uint32_t opcode, uint32_t arg, uint32_t flags, uint32_t *resp, int retries); +static int mmc_app_set_bus_width(struct mmc_softc *sc, uint16_t rca, int width); +static int mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr); +static void mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr); +static void mmc_power_down(struct mmc_softc *sc); static void mmc_ms_delay(int ms) @@ -120,7 +129,7 @@ static int mmc_probe(device_t dev) { - device_set_desc(dev, "mmc/sd bus"); + device_set_desc(dev, "MMC/SD bus"); return (0); } @@ -160,6 +169,8 @@ mmc_detach(device_t dev) free(ivar, M_DEVBUF); } free(kids, M_TEMP); + + mmc_power_down(sc); MMC_LOCK_DESTROY(sc); @@ -170,10 +181,11 @@ static int mmc_acquire_bus(device_t busdev, device_t dev) { struct mmc_softc *sc; + struct mmc_ivars *ivar; int err; int rca; - err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), dev); + err = MMCBR_ACQUIRE_HOST(device_get_parent(busdev), busdev); if (err) return (err); sc = device_get_softc(busdev); @@ -192,8 +204,30 @@ mmc_acquire_bus(device_t busdev, device_ mmc_wait_for_command(sc, MMC_SELECT_CARD, rca << 16, MMC_RSP_R1 | MMC_CMD_AC, NULL, CMD_RETRIES); sc->last_rca = rca; + if (mmcbr_get_caps(busdev) & MMC_CAP_4_BIT_DATA) { + ivar = device_get_ivars(dev); + if (!ivar->has_scr) { + if (mmc_app_send_scr(sc, ivar->rca, ivar->raw_scr) + == MMC_ERR_NONE) { + mmc_app_decode_scr(ivar->raw_scr, &ivar->scr); + ivar->has_scr = 1; + } + } + if (ivar->has_scr && + (ivar->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { + device_printf(busdev, + "setting bus width to 4 bits\n"); + mmc_app_set_bus_width(sc, rca, bus_width_4); + mmcbr_set_bus_width(busdev, bus_width_4); + } else { + device_printf(busdev, + "setting bus width to 1 bit\n"); + mmc_app_set_bus_width(sc, rca, bus_width_1); + mmcbr_set_bus_width(busdev, bus_width_1); + } + mmcbr_update_ios(busdev); + } } - // XXX should set bus width here? } else { // If there's a card selected, stand down. if (sc->last_rca != 0) { @@ -201,7 +235,6 @@ mmc_acquire_bus(device_t busdev, device_ MMC_RSP_R1 | MMC_CMD_AC, NULL, CMD_RETRIES); sc->last_rca = 0; } - // XXX should set bus width here? } return (0); @@ -221,7 +254,7 @@ mmc_release_bus(device_t busdev, device_ if (sc->owner != dev) panic("mmc: you don't own the bus. game over."); MMC_UNLOCK(sc); - err = MMCBR_RELEASE_HOST(device_get_parent(busdev), dev); + err = MMCBR_RELEASE_HOST(device_get_parent(busdev), busdev); if (err) return (err); MMC_LOCK(sc); @@ -240,7 +273,7 @@ static uint32_t mmc_select_vdd(struct mmc_softc *sc, uint32_t ocr) { // XXX - return ocr; + return (ocr & MMC_OCR_VOLTAGE); } static int @@ -302,10 +335,10 @@ mmc_wait_for_cmd(struct mmc_softc *sc, s memset(&mreq, 0, sizeof(mreq)); memset(cmd->resp, 0, sizeof(cmd->resp)); cmd->retries = retries; - cmd->data = NULL; mreq.cmd = cmd; // printf("CMD: %x ARG %x\n", cmd->opcode, cmd->arg); mmc_wait_for_req(sc, &mreq); +// printf("ERR: %x\n", cmd->error); return (cmd->error); } @@ -320,6 +353,7 @@ mmc_wait_for_app_cmd(struct mmc_softc *s appcmd.opcode = MMC_APP_CMD; appcmd.arg = rca << 16; appcmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + appcmd.data = NULL; mmc_wait_for_cmd(sc, &appcmd, 0); err = appcmd.error; if (err != MMC_ERR_NONE) @@ -345,6 +379,7 @@ mmc_wait_for_command(struct mmc_softc *s cmd.opcode = opcode; cmd.arg = arg; cmd.flags = flags; + cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, retries); if (err) return (err); @@ -374,6 +409,7 @@ mmc_idle_cards(struct mmc_softc *sc) cmd.opcode = MMC_GO_IDLE_STATE; cmd.arg = 0; cmd.flags = MMC_RSP_NONE | MMC_CMD_BC; + cmd.data = NULL; mmc_wait_for_cmd(sc, &cmd, 0); mmc_ms_delay(1); @@ -417,6 +453,7 @@ mmc_send_op_cond(struct mmc_softc *sc, u cmd.opcode = MMC_SEND_OP_COND; cmd.arg = ocr; cmd.flags = MMC_RSP_R3 | MMC_CMD_BCR; + cmd.data = NULL; for (i = 0; i < 100; i++) { err = mmc_wait_for_cmd(sc, &cmd, CMD_RETRIES); @@ -453,6 +490,45 @@ mmc_power_up(struct mmc_softc *sc) mmc_ms_delay(2); } +static void +mmc_power_down(struct mmc_softc *sc) +{ + device_t dev = sc->dev; + + mmcbr_set_bus_mode(dev, opendrain); + mmcbr_set_chip_select(dev, cs_dontcare); + mmcbr_set_bus_width(dev, bus_width_1); + mmcbr_set_power_mode(dev, power_off); + mmcbr_set_clock(dev, 0); + mmcbr_update_ios(dev); +} + +static int +mmc_app_set_bus_width(struct mmc_softc *sc, uint16_t rca, int width) +{ + int err; + struct mmc_command cmd; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + cmd.opcode = ACMD_SET_BUS_WIDTH; + cmd.flags = MMC_RSP_R1 | MMC_CMD_AC; + + switch (width) { + case bus_width_1: + cmd.arg = SD_BUS_WIDTH_1; + break; + case bus_width_4: + cmd.arg = SD_BUS_WIDTH_4; + break; + default: + return (MMC_ERR_INVALID); + } + + err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + return (err); +} + // I wonder if the following is endian safe. static uint32_t mmc_get_bits(uint32_t *bits, int start, int size) @@ -547,6 +623,27 @@ mmc_decode_csd(int is_sd, uint32_t *raw_ } } +static void +mmc_app_decode_scr(uint32_t *raw_scr, struct mmc_scr *scr) +{ + unsigned int scr_struct; + uint32_t tmp[4]; + + tmp[3] = raw_scr[1]; + tmp[2] = raw_scr[0]; + + memset(scr, 0, sizeof(*scr)); + + scr_struct = mmc_get_bits(tmp, 60, 4); + if (scr_struct != 0) { + printf("Unrecognised SCR structure version %d\n", + scr_struct); + return; + } + scr->sda_vsn = mmc_get_bits(tmp, 56, 4); + scr->bus_widths = mmc_get_bits(tmp, 48, 4); +} + static int mmc_all_send_cid(struct mmc_softc *sc, uint32_t *rawcid) { @@ -556,6 +653,7 @@ mmc_all_send_cid(struct mmc_softc *sc, u cmd.opcode = MMC_ALL_SEND_CID; cmd.arg = 0; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, 0); memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t)); return (err); @@ -570,12 +668,38 @@ mmc_send_csd(struct mmc_softc *sc, uint1 cmd.opcode = MMC_SEND_CSD; cmd.arg = rca << 16; cmd.flags = MMC_RSP_R2 | MMC_CMD_BCR; + cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, 0); memcpy(rawcid, cmd.resp, 4 * sizeof(uint32_t)); return (err); } static int +mmc_app_send_scr(struct mmc_softc *sc, uint16_t rca, uint32_t *rawscr) +{ + int err; + struct mmc_command cmd; + struct mmc_data data; + + memset(&cmd, 0, sizeof(struct mmc_command)); + + memset(rawscr, 0, 8); + cmd.opcode = ACMD_SEND_SCR; + cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; + cmd.arg = 0; + cmd.data = &data; + + data.data = rawscr; + data.len = 8; + data.flags = MMC_DATA_READ; + + err = mmc_wait_for_app_cmd(sc, rca, &cmd, CMD_RETRIES); + rawscr[0] = be32toh(rawscr[0]); + rawscr[1] = be32toh(rawscr[1]); + return (err); +} + +static int mmc_send_relative_addr(struct mmc_softc *sc, uint32_t *resp) { struct mmc_command cmd; @@ -584,6 +708,7 @@ mmc_send_relative_addr(struct mmc_softc cmd.opcode = SD_SEND_RELATIVE_ADDR; cmd.arg = 0; cmd.flags = MMC_RSP_R6 | MMC_CMD_BCR; + cmd.data = NULL; err = mmc_wait_for_cmd(sc, &cmd, 0); *resp = cmd.resp[0]; return (err); @@ -598,14 +723,15 @@ mmc_discover_cards(struct mmc_softc *sc) device_t child; while (1) { - ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF, M_WAITOK); + ivar = malloc(sizeof(struct mmc_ivars), M_DEVBUF, + M_WAITOK | M_ZERO); if (!ivar) return; err = mmc_all_send_cid(sc, ivar->raw_cid); if (err == MMC_ERR_TIMEOUT) break; if (err != MMC_ERR_NONE) { - printf("Error reading CID %d\n", err); + device_printf(sc->dev, "Error reading CID %d\n", err); break; } if (mmcbr_get_mode(sc->dev) == mode_sd) { @@ -613,11 +739,10 @@ mmc_discover_cards(struct mmc_softc *sc) mmc_decode_cid(1, ivar->raw_cid, &ivar->cid); mmc_send_relative_addr(sc, &resp); ivar->rca = resp >> 16; - // RO check + if (mmcbr_get_ro(sc->dev)) + ivar->read_only = 1; mmc_send_csd(sc, ivar->rca, ivar->raw_csd); mmc_decode_csd(1, ivar->raw_csd, &ivar->csd); - printf("SD CARD: %lld bytes\n", (long long) - ivar->csd.capacity); child = device_add_child(sc->dev, NULL, -1); device_set_ivars(child, ivar); return; @@ -727,7 +852,7 @@ mmc_read_ivar(device_t bus, device_t chi *(int *)result = ivar->csd.dsr_imp; break; case MMC_IVAR_MEDIA_SIZE: - *(int *)result = ivar->csd.capacity; + *(int *)result = ivar->csd.capacity/512; break; case MMC_IVAR_RCA: *(int *)result = ivar->rca; @@ -738,6 +863,9 @@ mmc_read_ivar(device_t bus, device_t chi case MMC_IVAR_TRAN_SPEED: *(int *)result = ivar->csd.tran_speed; break; + case MMC_IVAR_READ_ONLY: + *(int *)result = ivar->read_only; + break; } return (0); } @@ -790,4 +918,4 @@ static devclass_t mmc_devclass; DRIVER_MODULE(mmc, at91_mci, mmc_driver, mmc_devclass, 0, 0); -DRIVER_MODULE(mmc, sdh, mmc_driver, mmc_devclass, 0, 0); +DRIVER_MODULE(mmc, sdhci, mmc_driver, mmc_devclass, 0, 0); diff -ruNp mmc.HEAD/mmcbrvar.h mmc/mmcbrvar.h --- mmc.HEAD/mmcbrvar.h 2008-09-27 15:50:50.000000000 +0300 +++ mmc/mmcbrvar.h 2008-09-27 22:10:32.000000000 +0300 @@ -70,6 +70,7 @@ enum mmcbr_device_ivars { MMCBR_IVAR_OCR, MMCBR_IVAR_POWER_MODE, MMCBR_IVAR_VDD, + MMCBR_IVAR_CAPS, // MMCBR_IVAR_, }; @@ -90,6 +91,7 @@ MMCBR_ACCESSOR(mode, MODE, int) MMCBR_ACCESSOR(ocr, OCR, int) MMCBR_ACCESSOR(power_mode, POWER_MODE, int) MMCBR_ACCESSOR(vdd, VDD, int) +MMCBR_ACCESSOR(caps, CAPS, int) static int __inline mmcbr_update_ios(device_t dev) @@ -97,4 +99,10 @@ mmcbr_update_ios(device_t dev) return (MMCBR_UPDATE_IOS(device_get_parent(dev), dev)); } +static int __inline +mmcbr_get_ro(device_t dev) +{ + return (MMCBR_GET_RO(device_get_parent(dev), dev)); +} + #endif /* DEV_MMC_MMCBRVAR_H */ diff -ruNp mmc.HEAD/mmcreg.h mmc/mmcreg.h --- mmc.HEAD/mmcreg.h 2008-09-27 15:50:50.000000000 +0300 +++ mmc/mmcreg.h 2008-09-29 00:13:11.000000000 +0300 @@ -277,6 +277,12 @@ struct mmc_request { #define ACMD_SET_CLR_CARD_DETECT 42 #define ACMD_SEND_SCR 51 +/* + * SD bus widths + */ +#define SD_BUS_WIDTH_1 0 +#define SD_BUS_WIDTH_4 2 + /* OCR bits */ /* @@ -292,6 +298,7 @@ struct mmc_request { * * The MMC_OCR_CCS appears to be valid for only SD cards. */ +#define MMC_OCR_VOLTAGE 0x3fffffff /* Vdd voltage mask */ #define MMC_OCR_LOW_VOLTAGE (1u << 7) /* Low Voltage Range -- tbd */ #define MMC_OCR_200_210 (1U << 8) /* Vdd voltage 2.00 ~ 2.10 */ #define MMC_OCR_210_220 (1U << 9) /* Vdd voltage 2.10 ~ 2.20 */ @@ -350,4 +357,12 @@ struct mmc_csd wp_grp_enable:1; }; +struct mmc_scr +{ + unsigned char sda_vsn; + unsigned char bus_widths; +#define SD_SCR_BUS_WIDTH_1 (1<<0) +#define SD_SCR_BUS_WIDTH_4 (1<<2) +}; + #endif /* DEV_MMCREG_H */ diff -ruNp mmc.HEAD/mmcsd.c mmc/mmcsd.c --- mmc.HEAD/mmcsd.c 2008-09-27 22:21:26.000000000 +0300 +++ mmc/mmcsd.c 2008-09-29 00:43:28.000000000 +0300 @@ -80,7 +80,7 @@ struct mmcsd_softc { int running; }; -#define MULTI_BLOCK_READ_BROKEN +#define MULTI_BLOCK /* bus entry points */ static int mmcsd_probe(device_t dev); @@ -106,7 +106,8 @@ static int mmcsd_probe(device_t dev) { - device_set_desc(dev, "mmc or sd flash card"); + device_quiet(dev); + device_set_desc(dev, "MMC/SD Memory Card"); return (0); } @@ -126,10 +127,16 @@ mmcsd_attach(device_t dev) // sc->disk->d_dump = mmcsd_dump; Need polling mmc layer sc->disk->d_name = "mmcsd"; sc->disk->d_drv1 = sc; - sc->disk->d_maxsize = DFLTPHYS; + sc->disk->d_maxsize = 256*1024; /* This is completely empirical */ sc->disk->d_sectorsize = mmc_get_sector_size(dev); - sc->disk->d_mediasize = mmc_get_media_size(dev); + sc->disk->d_mediasize = ((off_t)mmc_get_media_size(dev)) * + mmc_get_sector_size(dev); sc->disk->d_unit = device_get_unit(dev); + + device_printf(dev, "%juMB %s at %s\n", + sc->disk->d_mediasize / 1048576, + mmc_get_read_only(dev)?" (read-only)":"", + device_get_nameunit(device_get_parent(sc->dev))); disk_create(sc->disk, DISK_VERSION); bioq_init(&sc->bio_queue); @@ -215,66 +222,71 @@ mmcsd_task(void *arg) MMCSD_UNLOCK(sc); if (!sc->running) break; +// printf("mmc_task: request %p for block %ju\n", bp, bp->bio_pblkno); + if (bp->bio_cmd != BIO_READ && mmc_get_read_only(dev)) { + bp->bio_error = EROFS; + bp->bio_resid = bp->bio_bcount; + bp->bio_flags |= BIO_ERROR; + biodone(bp); + continue; + } MMCBUS_ACQUIRE_BUS(device_get_parent(dev), dev); -// printf("mmc_task: request %p for block %lld\n", bp, bp->bio_pblkno); sz = sc->disk->d_sectorsize; end = bp->bio_pblkno + (bp->bio_bcount / sz); - // XXX should use read/write_mulit for (block = bp->bio_pblkno; block < end;) { char *vaddr = bp->bio_data + (block - bp->bio_pblkno) * sz; + int numblocks; +#ifdef MULTI_BLOCK + numblocks = end - block; +#else + numblocks = 1; +#endif memset(&req, 0, sizeof(req)); memset(&cmd, 0, sizeof(cmd)); memset(&stop, 0, sizeof(stop)); req.cmd = &cmd; cmd.data = &data; if (bp->bio_cmd == BIO_READ) { -#ifdef MULTI_BLOCK_READ - if (end - block > 1) + if (numblocks > 1) cmd.opcode = MMC_READ_MULTIPLE_BLOCK; else cmd.opcode = MMC_READ_SINGLE_BLOCK; -#else - cmd.opcode = MMC_READ_SINGLE_BLOCK; -#endif - } else - cmd.opcode = MMC_WRITE_BLOCK; + } else { + if (numblocks > 1) + cmd.opcode = MMC_WRITE_MULTIPLE_BLOCK; + else + cmd.opcode = MMC_WRITE_BLOCK; + } cmd.arg = block << 9; cmd.flags = MMC_RSP_R1 | MMC_CMD_ADTC; - // data.timeout_ns = ; - // data.timeout_clks = ; data.data = vaddr; data.mrq = &req; - if (bp->bio_cmd == BIO_READ) { + if (bp->bio_cmd == BIO_READ) data.flags = MMC_DATA_READ; -#ifdef MULTI_BLOCK_READ - data.len = bp->bio_bcount; - if (end - block > 1) { - req.stop = &stop; - data.flags |= MMC_DATA_MULTI; - } - printf("Len %d %lld-%lld flags %#x sz %d\n", - data.len, block, end, data.flags, sz); - block = end; -#else - data.len = sz; - block++; -#endif - } else { + else data.flags = MMC_DATA_WRITE; - data.len = sz; - block++; + data.len = numblocks * sz; + if (numblocks > 1) { + data.flags |= MMC_DATA_MULTI; + stop.opcode = MMC_STOP_TRANSMISSION; + stop.arg = 0; + stop.flags = MMC_RSP_R1B | MMC_CMD_AC; + req.stop = &stop; } - stop.opcode = MMC_STOP_TRANSMISSION; - stop.arg = 0; - stop.flags = MMC_RSP_R1B | MMC_CMD_AC; +// printf("Len %d %lld-%lld flags %#x sz %d\n", +// (int)data.len, (long long)block, (long long)end, data.flags, sz); MMCBUS_WAIT_FOR_REQUEST(device_get_parent(dev), dev, &req); - // XXX error handling -//XXX while (!(mmc_send_status(dev) & R1_READY_FOR_DATA)) -// continue; - // XXX decode mmc status + if (req.cmd->error != MMC_ERR_NONE) + break; + block += numblocks; } MMCBUS_RELEASE_BUS(device_get_parent(dev), dev); + if (block < end) { + bp->bio_error = EIO; + bp->bio_resid = (end - block) * sz; + bp->bio_flags |= BIO_ERROR; + } biodone(bp); } diff -ruNp mmc.HEAD/mmcvar.h mmc/mmcvar.h --- mmc.HEAD/mmcvar.h 2008-09-27 15:50:50.000000000 +0300 +++ mmc/mmcvar.h 2008-09-27 22:21:19.000000000 +0300 @@ -61,6 +61,7 @@ enum mmc_device_ivars { MMC_IVAR_RCA, MMC_IVAR_SECTOR_SIZE, MMC_IVAR_TRAN_SPEED, + MMC_IVAR_READ_ONLY, // MMC_IVAR_, }; @@ -75,5 +76,6 @@ MMC_ACCESSOR(media_size, MEDIA_SIZE, int MMC_ACCESSOR(rca, RCA, int) MMC_ACCESSOR(sector_size, SECTOR_SIZE, int) MMC_ACCESSOR(tran_speed, TRAN_SPEED, int) +MMC_ACCESSOR(read_only, READ_ONLY, int) #endif /* DEV_MMC_MMCVAR_H */