Hi tech@ Here is a diff that updates our sdmmc stack with support for other frequencies than 25Mhz and for 4 pins transfers. Big chunks of this code comes directly from NetBSD sdmmc stack.
Normally this diff should speedup things for some sdcards and for some controllers. I tried this diff on the only sdmmc controller I have (ommmc). It will be awesome if people could give me feedback for these drivers : rtsx sdhc imxesdhc pxammc w83l518d_sdmmc Any OK/Comments ? Index: arch/arm/xscale/pxa2x0_mmc.c =================================================================== RCS file: /cvs/src/sys/arch/arm/xscale/pxa2x0_mmc.c,v retrieving revision 1.11 diff -u -p -u -p -r1.11 pxa2x0_mmc.c --- arch/arm/xscale/pxa2x0_mmc.c 22 Aug 2012 13:37:04 -0000 1.11 +++ arch/arm/xscale/pxa2x0_mmc.c 6 Nov 2013 16:59:38 -0000 @@ -56,6 +56,7 @@ u_int32_t pxammc_host_ocr(sdmmc_chipset_ int pxammc_host_maxblklen(sdmmc_chipset_handle_t); int pxammc_card_detect(sdmmc_chipset_handle_t); int pxammc_bus_power(sdmmc_chipset_handle_t, u_int32_t); +int pxammc_bus_width(sdmmc_chipset_handle_t, int); int pxammc_bus_clock(sdmmc_chipset_handle_t, int); void pxammc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); void pxammc_clock_stop(struct pxammc_softc *); @@ -90,6 +91,7 @@ struct sdmmc_chip_functions pxammc_funct /* bus power and clock frequency */ pxammc_bus_power, pxammc_bus_clock, + pxammc_bus_width, /* command execution */ pxammc_exec_command }; @@ -179,6 +181,8 @@ pxammc_attach(struct pxammc_softc *sc, v */ bzero(&saa, sizeof saa); saa.saa_busname = "sdmmc"; + saa.saa_clkmax = SDMMC_SDCLK_25MHZ; + saa.sct = &pxammc_functions; saa.sch = sc; saa.flags = SMF_STOP_AFTER_MULTIPLE; @@ -274,6 +278,12 @@ pxammc_bus_power(sdmmc_chipset_handle_t DPRINTF(0,("%s: driver lacks set_power() function\n", sc->sc_dev.dv_xname)); return ENXIO; +} + +int +pxammc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + return (0); } int Index: arch/armv7/imx/imxesdhc.c =================================================================== RCS file: /cvs/src/sys/arch/armv7/imx/imxesdhc.c,v retrieving revision 1.3 diff -u -p -u -p -r1.3 imxesdhc.c --- arch/armv7/imx/imxesdhc.c 27 Oct 2013 20:27:09 -0000 1.3 +++ arch/armv7/imx/imxesdhc.c 6 Nov 2013 16:59:43 -0000 @@ -195,6 +195,7 @@ int imxesdhc_host_maxblklen(sdmmc_chipse int imxesdhc_card_detect(sdmmc_chipset_handle_t); int imxesdhc_bus_power(sdmmc_chipset_handle_t, uint32_t); int imxesdhc_bus_clock(sdmmc_chipset_handle_t, int); +int imxesdhc_bus_width(sdmmc_chipset_handle_t, int); void imxesdhc_card_intr_mask(sdmmc_chipset_handle_t, int); void imxesdhc_card_intr_ack(sdmmc_chipset_handle_t); void imxesdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); @@ -225,6 +226,7 @@ struct sdmmc_chip_functions imxesdhc_fun /* bus power and clock frequency */ imxesdhc_bus_power, imxesdhc_bus_clock, + imxesdhc_bus_width, /* command execution */ imxesdhc_exec_command, /* card interrupt */ @@ -319,6 +321,7 @@ imxesdhc_attach(struct device *parent, s bzero(&saa, sizeof(saa)); saa.saa_busname = "sdmmc"; + saa.saa_clkmax = SDMMC_SDCLK_25MHZ; saa.sct = &imxesdhc_functions; saa.sch = sc; @@ -327,7 +330,7 @@ imxesdhc_attach(struct device *parent, s error = 0; goto err; } - + return; err: @@ -483,6 +486,12 @@ imxesdhc_card_detect(sdmmc_chipset_handl */ int imxesdhc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + return 0; +} + +int +imxesdhc_bus_width(sdmmc_chipset_handle_t sch, int freq) { return 0; } Index: arch/armv7/omap/ommmc.c =================================================================== RCS file: /cvs/src/sys/arch/armv7/omap/ommmc.c,v retrieving revision 1.9 diff -u -p -u -p -r1.9 ommmc.c --- arch/armv7/omap/ommmc.c 28 Oct 2013 20:45:20 -0000 1.9 +++ arch/armv7/omap/ommmc.c 6 Nov 2013 16:59:43 -0000 @@ -228,6 +228,7 @@ int ommmc_host_maxblklen(sdmmc_chipset_h int ommmc_card_detect(sdmmc_chipset_handle_t); int ommmc_bus_power(sdmmc_chipset_handle_t, uint32_t); int ommmc_bus_clock(sdmmc_chipset_handle_t, int); +int ommmc_bus_width(sdmmc_chipset_handle_t, int); void ommmc_card_intr_mask(sdmmc_chipset_handle_t, int); void ommmc_card_intr_ack(sdmmc_chipset_handle_t); void ommmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); @@ -259,6 +260,7 @@ struct sdmmc_chip_functions ommmc_functi /* bus power and clock frequency */ ommmc_bus_power, ommmc_bus_clock, + ommmc_bus_width, /* command execution */ ommmc_exec_command, /* card interrupt */ @@ -386,9 +388,10 @@ ommmc_attach(struct device *parent, stru */ bzero(&saa, sizeof(saa)); saa.saa_busname = "sdmmc"; + saa.saa_clkmax = 96000; saa.sct = &ommmc_functions; saa.sch = sc; - saa.caps = 0; + saa.caps = SMC_CAPS_4BIT_MODE; if (caps & MMCHS_CAPA_HSS) saa.caps |= SMC_CAPS_MMC_HIGHSPEED; @@ -624,6 +627,30 @@ ommmc_clock_divisor(struct ommmc_softc * printf("divisor failure\n"); /* No divisor found. */ return (-1); +} + +int +ommmc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct ommmc_softc *sc = sch; + + switch (width) { + case 1: + printf("%s: BW 1\n", DEVNAME(sc)); + HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_DTW); + HCLR4(sc, MMCHS_CON, MMCHS_CON_DW8); + break; + case 4: + printf("%s: BW 4\n", DEVNAME(sc)); + HSET4(sc, MMCHS_HCTL, MMCHS_HCTL_DTW); + HCLR4(sc, MMCHS_CON, MMCHS_CON_DW8); + break; + default: + printf("%s: bus width %i not supported\n", DEVNAME(sc), width); + break; + } + + return (0); } /* Index: dev/ic/rtsx.c =================================================================== RCS file: /cvs/src/sys/dev/ic/rtsx.c,v retrieving revision 1.5 diff -u -p -u -p -r1.5 rtsx.c --- dev/ic/rtsx.c 6 Nov 2013 13:51:02 -0000 1.5 +++ dev/ic/rtsx.c 6 Nov 2013 16:59:44 -0000 @@ -97,6 +97,7 @@ int rtsx_host_maxblklen(sdmmc_chipset_ha int rtsx_card_detect(sdmmc_chipset_handle_t); int rtsx_bus_power(sdmmc_chipset_handle_t, u_int32_t); int rtsx_bus_clock(sdmmc_chipset_handle_t, int); +int rtsx_bus_width(sdmmc_chipset_handle_t, int); void rtsx_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); int rtsx_init(struct rtsx_softc *, int); void rtsx_soft_reset(struct rtsx_softc *); @@ -147,6 +148,7 @@ struct sdmmc_chip_functions rtsx_functio /* bus power and clock frequency */ rtsx_bus_power, rtsx_bus_clock, + rtsx_bus_width, /* command execution */ rtsx_exec_command, /* card interrupt */ @@ -196,8 +198,10 @@ rtsx_attach(struct rtsx_softc *sc, bus_s */ bzero(&saa, sizeof(saa)); saa.saa_busname = "sdmmc"; + saa.saa_clkmax = SDMMC_SDCLK_25MHZ; saa.sct = &rtsx_functions; saa.sch = sc; + saa.caps = SMC_CAPS_4BIT_MODE; saa.flags = SMF_STOP_AFTER_MULTIPLE; sc->sdmmc = config_found(&sc->sc_dev, &saa, NULL); @@ -207,7 +211,7 @@ rtsx_attach(struct rtsx_softc *sc, bus_s /* Now handle cards discovered during attachment. */ if (ISSET(sc->flags, RTSX_F_CARD_PRESENT)) rtsx_card_insert(sc); - + return 0; } @@ -602,14 +606,19 @@ rtsx_bus_power(sdmmc_chipset_handle_t sc goto ret; } - error = rtsx_set_bus_width(sc, 1); - if (error) - goto ret; error = rtsx_bus_power_on(sc); ret: splx(s); return error; +} + +int +rtsx_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct rtsx_softc *sc = sch; + + return (rtsx_set_bus_width(sc, width)); } /* Index: dev/ic/w83l518d_sdmmc.c =================================================================== RCS file: /cvs/src/sys/dev/ic/w83l518d_sdmmc.c,v retrieving revision 1.1 diff -u -p -u -p -r1.1 w83l518d_sdmmc.c --- dev/ic/w83l518d_sdmmc.c 3 Oct 2009 19:51:53 -0000 1.1 +++ dev/ic/w83l518d_sdmmc.c 6 Nov 2013 16:59:44 -0000 @@ -66,9 +66,7 @@ int wb_sdmmc_write_protect(sdmmc_chipset #endif int wb_sdmmc_bus_power(sdmmc_chipset_handle_t, uint32_t); int wb_sdmmc_bus_clock(sdmmc_chipset_handle_t, int); -#if 0 int wb_sdmmc_bus_width(sdmmc_chipset_handle_t, int); -#endif void wb_sdmmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); void wb_sdmmc_card_intr_mask(sdmmc_chipset_handle_t, int); @@ -85,12 +83,10 @@ struct sdmmc_chip_functions wb_sdmmc_chi #ifdef notyet .write_protect = wb_sdmmc_write_protect, #endif - /* bus power and clock frequency */ + /* bus power, width and clock frequency */ wb_sdmmc_bus_power, wb_sdmmc_bus_clock, -#if 0 - .bus_width = wb_sdmmc_bus_width, -#endif + wb_sdmmc_bus_width, /* command execution */ wb_sdmmc_exec_command, /* card interrupt */ @@ -190,6 +186,7 @@ wb_sdmmc_attach(struct wb_softc *wb) memset(&saa, 0, sizeof(saa)); saa.saa_busname = "sdmmc"; + saa.saa_clkmax = SDMMC_SDCLK_25MHZ; saa.sct = &wb_sdmmc_chip_functions; saa.sch = wb; saa.flags = SMF_STOP_AFTER_MULTIPLE; @@ -279,6 +276,14 @@ wb_sdmmc_bus_power(sdmmc_chipset_handle_ REPORT(sch, "TRACE: sdmmc/bus_power(wb, ocr=%d)\n", ocr); return 0; +} + +int +wb_sdmmc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + REPORT(sch, "TRACE: sdmmc/bus_width(wb, width=%d)\n", width); + + return (0); } int Index: dev/sdmmc/sdhc.c =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdhc.c,v retrieving revision 1.36 diff -u -p -u -p -r1.36 sdhc.c --- dev/sdmmc/sdhc.c 31 May 2013 21:28:32 -0000 1.36 +++ dev/sdmmc/sdhc.c 6 Nov 2013 16:59:44 -0000 @@ -82,6 +82,7 @@ int sdhc_host_maxblklen(sdmmc_chipset_ha int sdhc_card_detect(sdmmc_chipset_handle_t); int sdhc_bus_power(sdmmc_chipset_handle_t, u_int32_t); int sdhc_bus_clock(sdmmc_chipset_handle_t, int); +int sdhc_bus_width(sdmmc_chipset_handle_t, int); void sdhc_card_intr_mask(sdmmc_chipset_handle_t, int); void sdhc_card_intr_ack(sdmmc_chipset_handle_t); void sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); @@ -112,6 +113,7 @@ struct sdmmc_chip_functions sdhc_functio /* bus power and clock frequency */ sdhc_bus_power, sdhc_bus_clock, + sdhc_bus_width, /* command execution */ sdhc_exec_command, /* card interrupt */ @@ -231,6 +233,8 @@ sdhc_host_found(struct sdhc_softc *sc, b bzero(&saa, sizeof(saa)); saa.saa_busname = "sdmmc"; saa.sct = &sdhc_functions; + saa.saa_clkmax = hp->clkbase; + saa.caps = SMC_CAPS_4BIT_MODE; saa.sch = hp; hp->sdmmc = config_found(&sc->sc_dev, &saa, NULL); @@ -238,7 +242,7 @@ sdhc_host_found(struct sdhc_softc *sc, b error = 0; goto err; } - + return 0; err: @@ -457,6 +461,22 @@ sdhc_clock_divisor(struct sdhc_host *hp, * Set or change SDCLK frequency or disable the SD clock. * Return zero on success. */ +int +sdhc_bus_width(sdmmc_chipset_handle_t sch, int width) +{ + struct sdhc_host *hp = (struct sdhc_host *)sch; + int reg; + + reg = HREAD1(hp, SDHC_HOST_CTL); + reg &= ~SDHC_4BIT_MODE; + if (width == 4) + reg |= SDHC_4BIT_MODE; + + HWRITE1(hp, SDHC_HOST_CTL, reg); + + return (0); +} + int sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq) { Index: dev/sdmmc/sdmmc.c =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmc.c,v retrieving revision 1.28 diff -u -p -u -p -r1.28 sdmmc.c --- dev/sdmmc/sdmmc.c 12 Sep 2013 11:54:04 -0000 1.28 +++ dev/sdmmc/sdmmc.c 6 Nov 2013 16:59:44 -0000 @@ -106,6 +106,9 @@ sdmmc_attach(struct device *parent, stru sc->sch = saa->sch; sc->sc_flags = saa->flags; sc->sc_caps = saa->caps; + sc->sc_clkmax = saa->saa_clkmax; + sc->sc_busclk = saa->saa_clkmax; + sc->sc_buswidth = 1; sc->sc_max_xfer = saa->max_xfer; SIMPLEQ_INIT(&sc->sf_head); @@ -417,11 +420,6 @@ sdmmc_enable(struct sdmmc_softc *sc) (error = sdmmc_mem_enable(sc)) != 0) goto err; - /* 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); - err: if (error != 0) sdmmc_disable(sc); @@ -440,8 +438,10 @@ sdmmc_disable(struct sdmmc_softc *sc) (void)sdmmc_select_card(sc, NULL); /* Turn off bus power and clock. */ + (void)sdmmc_chip_bus_width(sc->sct, sc->sch, 1); (void)sdmmc_chip_bus_clock(sc->sct, sc->sch, SDMMC_SDCLK_OFF); (void)sdmmc_chip_bus_power(sc->sct, sc->sch, 0); + sc->sc_busclk = sc->sc_clkmax; } /* @@ -688,37 +688,6 @@ sdmmc_set_relative_addr(struct sdmmc_sof if (ISSET(sc->sc_flags, SMF_SD_MODE)) sf->rca = SD_R6_RCA(cmd.c_resp); return 0; -} - -/* - * Switch card and host to the maximum supported bus width. - */ -int -sdmmc_set_bus_width(struct sdmmc_function *sf) -{ - struct sdmmc_softc *sc = sf->sc; - struct sdmmc_command cmd; - int error; - - rw_enter_write(&sc->sc_lock); - - if (!ISSET(sc->sc_flags, SMF_SD_MODE)) { - rw_exit(&sc->sc_lock); - return EOPNOTSUPP; - } - - if ((error = sdmmc_select_card(sc, sf)) != 0) { - rw_exit(&sc->sc_lock); - return error; - } - - bzero(&cmd, sizeof cmd); - cmd.c_opcode = SD_APP_SET_BUS_WIDTH; - cmd.c_arg = SD_ARG_BUS_WIDTH_4; - cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1; - error = sdmmc_app_command(sc, &cmd); - rw_exit(&sc->sc_lock); - return error; } int Index: dev/sdmmc/sdmmc_io.c =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmc_io.c,v retrieving revision 1.19 diff -u -p -u -p -r1.19 sdmmc_io.c --- dev/sdmmc/sdmmc_io.c 24 Aug 2010 14:52:23 -0000 1.19 +++ dev/sdmmc/sdmmc_io.c 6 Nov 2013 16:59:44 -0000 @@ -171,11 +171,18 @@ sdmmc_io_scan(struct sdmmc_softc *sc) int sdmmc_io_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) { + int error = 0; + uint8_t reg; + rw_assert_wrlock(&sc->sc_lock); if (sf->number == 0) { - sdmmc_io_write_1(sf, SD_IO_CCCR_BUS_WIDTH, - CCCR_BUS_WIDTH_1); + reg = sdmmc_io_read_1(sf, SD_IO_CCCR_CAPABILITY); + if (!(reg & CCCR_CAPS_LSC) || (reg & CCCR_CAPS_4BLS)) { + sdmmc_io_write_1(sf, SD_IO_CCCR_BUS_WIDTH, + CCCR_BUS_WIDTH_4); + sf->width = 4; + } if (sdmmc_read_cis(sf, &sf->cis) != 0) { printf("%s: can't read CIS\n", DEVNAME(sc)); @@ -187,6 +194,23 @@ sdmmc_io_init(struct sdmmc_softc *sc, st if (sdmmc_verbose) sdmmc_print_cis(sf); + + reg = sdmmc_io_read_1(sf, SD_IO_CCCR_HIGH_SPEED); + if (reg & CCCR_HIGH_SPEED_SHS) { + reg |= CCCR_HIGH_SPEED_EHS; + sdmmc_io_write_1(sf, SD_IO_CCCR_HIGH_SPEED, reg); + + sf->csd.tran_speed = SDMMC_SDCLK_50MHZ; + + /* Wait 400KHz x 8 clock */ + delay(1); + } + + if (sc->sc_busclk > sf->csd.tran_speed) + sc->sc_busclk = sf->csd.tran_speed; + error = sdmmc_chip_bus_clock(sc->sct, sc->sch, sc->sc_busclk); + if (error) + printf("%s: can't change bus clock\n", DEVNAME(sc)); } return 0; } Index: dev/sdmmc/sdmmc_ioreg.h =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmc_ioreg.h,v retrieving revision 1.4 diff -u -p -u -p -r1.4 sdmmc_ioreg.h --- dev/sdmmc/sdmmc_ioreg.h 2 Jun 2007 01:48:37 -0000 1.4 +++ dev/sdmmc/sdmmc_ioreg.h 6 Nov 2013 16:59:44 -0000 @@ -71,7 +71,19 @@ #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_CAPABILITY 0x08 +#define CCCR_CAPS_SDC (1<<0) +#define CCCR_CAPS_SMB (1<<1) /* Multi-Block support */ +#define CCCR_CAPS_SRB (1<<2) /* Read Wait support */ +#define CCCR_CAPS_SBS (1<<3) /* Suspend/Resume support */ +#define CCCR_CAPS_S4MI (1<<4) /* intr support in 4-bit mode */ +#define CCCR_CAPS_E4MI (1<<5) /* enable intr in 4-bit mode */ +#define CCCR_CAPS_LSC (1<<6) /* Low speed card */ +#define CCCR_CAPS_4BLS (1<<7) /* 4-bit support for low speed */ #define SD_IO_CCCR_CISPTR 0x09 /* XXX 9-10, 10-11, or 9-12 */ +#define SD_IO_CCCR_HIGH_SPEED 0x13 +#define CCCR_HIGH_SPEED_SHS (1<<0) /* Support High-Speed */ +#define CCCR_HIGH_SPEED_EHS (1<<1) /* Enable High-Speed */ /* Function Basic Registers (FBR) */ #define SD_IO_FBR_START 0x00100 Index: dev/sdmmc/sdmmc_mem.c =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmc_mem.c,v retrieving revision 1.18 diff -u -p -u -p -r1.18 sdmmc_mem.c --- dev/sdmmc/sdmmc_mem.c 22 Oct 2013 16:49:27 -0000 1.18 +++ dev/sdmmc/sdmmc_mem.c 6 Nov 2013 16:59:44 -0000 @@ -24,10 +24,13 @@ #include <sys/malloc.h> #include <sys/systm.h> +#include <sys/endian.h> + #include <dev/sdmmc/sdmmcchip.h> #include <dev/sdmmc/sdmmcreg.h> #include <dev/sdmmc/sdmmcvar.h> +typedef struct { uint32_t _bits[512/32]; } __packed sdmmc_bitfield512_t; int sdmmc_decode_csd(struct sdmmc_softc *, sdmmc_response, struct sdmmc_function *); int sdmmc_decode_cid(struct sdmmc_softc *, sdmmc_response, @@ -240,7 +243,23 @@ int sdmmc_decode_csd(struct sdmmc_softc *sc, sdmmc_response resp, struct sdmmc_function *sf) { + /* TRAN_SPEED(2:0): transfer rate exponent */ + static const int speed_exponent[8] = { + 100 * 1, /* 100 Kbits/s */ + 1 * 1000, /* 1 Mbits/s */ + 10 * 1000, /* 10 Mbits/s */ + 100 * 1000, /* 100 Mbits/s */ + 0, + 0, + 0, + 0, + }; + /* TRAN_SPEED(6:3): time mantissa */ + static const int speed_mantissa[16] = { + 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80, + }; struct sdmmc_csd *csd = &sf->csd; + int e, m; if (ISSET(sc->sc_flags, SMF_SD_MODE)) { /* @@ -265,6 +284,13 @@ sdmmc_decode_csd(struct sdmmc_softc *sc, break; } + csd->mmcver = SD_CSD_MMCVER(resp); + csd->write_bl_len = SD_CSD_WRITE_BL_LEN(resp); + csd->r2w_factor = SD_CSD_R2W_FACTOR(resp); + e = SD_CSD_SPEED_EXP(resp); + m = SD_CSD_SPEED_MANT(resp); + csd->tran_speed = speed_exponent[e] * speed_mantissa[m] / 10; + csd->ccc = SD_CSD_CCC(resp); } else { csd->csdver = MMC_CSD_CSDVER(resp); if (csd->csdver == MMC_CSD_CSDVER_1_0 || @@ -273,6 +299,12 @@ sdmmc_decode_csd(struct sdmmc_softc *sc, csd->mmcver = MMC_CSD_MMCVER(resp); csd->capacity = MMC_CSD_CAPACITY(resp); csd->read_bl_len = MMC_CSD_READ_BL_LEN(resp); + csd->write_bl_len = MMC_CSD_WRITE_BL_LEN(resp); + csd->r2w_factor = MMC_CSD_R2W_FACTOR(resp); + e = MMC_CSD_TRAN_SPEED_EXP(resp); + m = MMC_CSD_TRAN_SPEED_MANT(resp); + csd->tran_speed = speed_exponent[e] * + speed_mantissa[m] / 10; } else { printf("%s: unknown MMC CSD structure version 0x%x\n", DEVNAME(sc), csd->csdver); @@ -375,6 +407,39 @@ out: return error; } +static int +sdmmc_set_bus_width(struct sdmmc_function *sf, int width) +{ + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_command cmd; + int error; + + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) + return ENODEV; + + memset(&cmd, 0, sizeof(cmd)); + cmd.c_opcode = SD_APP_SET_BUS_WIDTH; + cmd.c_flags = SCF_RSP_R1 | SCF_CMD_AC; + + switch (width) { + case 1: + cmd.c_arg = SD_ARG_BUS_WIDTH_1; + break; + + case 4: + cmd.c_arg = SD_ARG_BUS_WIDTH_4; + break; + + default: + return EINVAL; + } + + error = sdmmc_app_command(sc, &cmd); + if (error == 0) + error = sdmmc_chip_bus_width(sc->sct, sc->sch, width); + return error; +} + int sdmmc_mem_mmc_switch(struct sdmmc_function *sf, uint8_t set, uint8_t index, uint8_t value) @@ -413,12 +478,234 @@ sdmmc_mem_init(struct sdmmc_softc *sc, s return error; } +static int +sdmmc_mem_send_scr(struct sdmmc_softc *sc, struct sdmmc_function *sf, + uint32_t scr[2]) +{ + struct sdmmc_command cmd; + void *ptr = NULL; + int datalen = 8; + int error = 0; + + /* Don't lock */ + + ptr = malloc(datalen, M_DEVBUF, M_NOWAIT | M_ZERO); + if (ptr == NULL) + goto out; + + memset(&cmd, 0, sizeof(cmd)); + cmd.c_data = ptr; + cmd.c_datalen = datalen; + cmd.c_blklen = datalen; + cmd.c_arg = 0; + cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1 | SCF_RSP_SPI_R1; + cmd.c_opcode = SD_APP_SEND_SCR; + + error = sdmmc_app_command(sc, &cmd); + if (error == 0) + memcpy(scr, ptr, datalen); + +out: + if (ptr != NULL) + free(ptr, M_DEVBUF); + DPRINTF(("%s: sdmem_mem_send_scr: error = %d\n", SDMMCDEVNAME(sc), + error)); + + return (error); +} + +static int +sdmmc_mem_decode_scr(struct sdmmc_softc *sc, struct sdmmc_function *sf) +{ + sdmmc_response resp; + int ver; + + memset(resp, 0, sizeof(resp)); + /* + * Change the raw-scr received from the DMA stream to resp. + */ + resp[0] = betoh32(sf->raw_scr[1]) >> 8; // LSW + resp[1] = betoh32(sf->raw_scr[0]); // MSW + resp[0] |= (resp[1] & 0xff) << 24; + resp[1] >>= 8; + + ver = SCR_STRUCTURE(resp); + sf->scr.sd_spec = SCR_SD_SPEC(resp); + sf->scr.bus_width = SCR_SD_BUS_WIDTHS(resp); + + DPRINTF(("%s: sdmmc_mem_decode_scr: %08x%08x spec=%d, bus width=%d\n", + SDMMCDEVNAME(sc), resp[1], resp[0], + sf->scr.sd_spec, sf->scr.bus_width)); + + if (ver != 0) { + DPRINTF(("%s: unknown structure version: %d\n", + SDMMCDEVNAME(sc), ver)); + return EINVAL; + } + return 0; +} + +/* make 512-bit BE quantity __bitfield()-compatible */ +static void +sdmmc_be512_to_bitfield512(sdmmc_bitfield512_t *buf) { + size_t i; + uint32_t tmp0, tmp1; + const size_t bitswords = nitems(buf->_bits); + for (i = 0; i < bitswords/2; i++) { + tmp0 = buf->_bits[i]; + tmp1 = buf->_bits[bitswords - 1 - i]; + buf->_bits[i] = betoh32(tmp1); + buf->_bits[bitswords - 1 - i] = betoh32(tmp0); + } +} + +static int +sdmmc_mem_sd_switch(struct sdmmc_function *sf, int mode, int group, + int function, sdmmc_bitfield512_t *status) +{ + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_command cmd; + void *ptr = NULL; + int gsft, error = 0; + const int statlen = 64; + + if (sf->scr.sd_spec >= SCR_SD_SPEC_VER_1_10 && + !ISSET(sf->csd.ccc, SD_CSD_CCC_SWITCH)) + return EINVAL; + + if (group <= 0 || group > 6 || + function < 0 || function > 15) + return EINVAL; + + gsft = (group - 1) << 2; + + ptr = malloc(statlen, M_DEVBUF, M_NOWAIT | M_ZERO); + if (ptr == NULL) + goto out; + + memset(&cmd, 0, sizeof(cmd)); + cmd.c_data = ptr; + cmd.c_datalen = statlen; + cmd.c_blklen = statlen; + cmd.c_opcode = SD_SEND_SWITCH_FUNC; + cmd.c_arg = + (!!mode << 31) | (function << gsft) | (0x00ffffff & ~(0xf << gsft)); + cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1 | SCF_RSP_SPI_R1; + + error = sdmmc_mmc_command(sc, &cmd); + if (error == 0) + memcpy(status, ptr, statlen); + +out: + if (ptr != NULL) + if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) + free(ptr, M_DEVBUF); + + if (error == 0) + sdmmc_be512_to_bitfield512(status); + + return error; +} int sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) { - /* XXX */ + sdmmc_bitfield512_t status; /* Switch Function Status */ + int support_func; + int best_func; + int host_ocr; + int error; + int g; + int i; + struct { + int v; + int freq; + } switch_group0_functions[] = { + /* Default/SDR12 */ + { MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V | + MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V, 25000 }, + + /* High-Speed/SDR25 */ + { MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V | + MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V, 50000 }, + + /* SDR50 */ + { MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V, 100000 }, + + /* SDR104 */ + { MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V, 208000 }, + + /* DDR50 */ + { MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V, 50000 }, + }; + + if ((error = sdmmc_mem_send_scr(sc, sf, sf->raw_scr))) { + printf("%s: SD_SEND_SCR send failed.\n", DEVNAME(sc)); + return (error); + } + if ((error = sdmmc_mem_decode_scr(sc, sf))) + return (error); - return 0; + if (ISSET(sc->sc_caps, SMC_CAPS_4BIT_MODE) && + ISSET(sf->scr.bus_width, SCR_SD_BUS_WIDTHS_4BIT)) { + DPRINTF(("%s: change bus width\n", SDMMCDEVNAME(sc))); + error = sdmmc_set_bus_width(sf, 4); + if (error) { + printf("%s: can't change bus width (%d bit)\n", + DEVNAME(sc), 4); + return error; + } + sf->width = 4; + } + + if (sf->scr.sd_spec >= SCR_SD_SPEC_VER_1_10 && + ISSET(sf->csd.ccc, SD_CSD_CCC_SWITCH)) { + DPRINTF(("%s: switch func mode 0\n", DEVNAME(sc))); + error = sdmmc_mem_sd_switch(sf, 0, 1, 0, &status); + if (error) { + printf("%s: switch func mode 0 failed\n", + DEVNAME(sc)); + return error; + } + + host_ocr = sdmmc_chip_host_ocr(sc->sct, sc->sch); + support_func = SFUNC_STATUS_GROUP(&status, 1); + best_func = 0; + for (i = 0, g = 1; + i < nitems(switch_group0_functions); i++, g <<= 1) { + if (!(switch_group0_functions[i].v & host_ocr)) + continue; + if (g & support_func) + best_func = i; + } + if (ISSET(sc->sc_caps, SMC_CAPS_SD_HIGHSPEED) && + best_func != 0) { + printf("%s: switch func mode 1(func=%d)\n", + DEVNAME(sc), best_func); + error = + sdmmc_mem_sd_switch(sf, 1, 1, best_func, &status); + if (error) { + printf("%s: switch func mode 1 failed:" + " group 1 function %d(0x%2x)\n", + DEVNAME(sc), best_func, support_func); + return error; + } + sf->csd.tran_speed = + switch_group0_functions[best_func].freq; + + /* Wait 400KHz x 8 clock (2.5us * 8 + slop) */ + DELAY(25); + } + } + + /* change bus clock */ + if (sc->sc_busclk > sf->csd.tran_speed) + sc->sc_busclk = sf->csd.tran_speed; + error = sdmmc_chip_bus_clock(sc->sct, sc->sch, sc->sc_busclk); + if (error) { + printf("%s: can't change bus clock\n", DEVNAME(sc)); + return (error); + } + return (0); } int @@ -468,8 +755,9 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s } } - error = - sdmmc_chip_bus_clock(sc->sct, sc->sch, speed); + if (sc->sc_busclk > sf->csd.tran_speed) + sc->sc_busclk = sf->csd.tran_speed; + error = sdmmc_chip_bus_clock(sc->sct, sc->sch, speed); if (error != 0) { printf("%s: can't change bus clock\n", DEVNAME(sc)); return error; @@ -487,6 +775,14 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s printf("%s, HS_TIMING set failed\n", DEVNAME(sc)); return EINVAL; } + } + } else { + if (sc->sc_busclk > sf->csd.tran_speed) + sc->sc_busclk = sf->csd.tran_speed; + error = sdmmc_chip_bus_clock(sc->sct, sc->sch, sc->sc_busclk); + if (error) { + printf("%s: can't change bus clock\n"); + return (error); } } Index: dev/sdmmc/sdmmcchip.h =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmcchip.h,v retrieving revision 1.5 diff -u -p -u -p -r1.5 sdmmcchip.h --- dev/sdmmc/sdmmcchip.h 12 Sep 2013 11:54:04 -0000 1.5 +++ dev/sdmmc/sdmmcchip.h 6 Nov 2013 16:59:44 -0000 @@ -35,6 +35,7 @@ struct sdmmc_chip_functions { /* bus power and clock frequency */ int (*bus_power)(sdmmc_chipset_handle_t, u_int32_t); int (*bus_clock)(sdmmc_chipset_handle_t, int); + int (*bus_width)(sdmmc_chipset_handle_t, int); /* command execution */ void (*exec_command)(sdmmc_chipset_handle_t, struct sdmmc_command *); @@ -59,6 +60,8 @@ struct sdmmc_chip_functions { ((tag)->bus_power((handle), (ocr))) #define sdmmc_chip_bus_clock(tag, handle, freq) \ ((tag)->bus_clock((handle), (freq))) +#define sdmmc_chip_bus_width(tag, handle, width) \ + ((tag)->bus_width((handle), (width))) /* command execution */ #define sdmmc_chip_exec_command(tag, handle, cmdp) \ ((tag)->exec_command((handle), (cmdp))) @@ -72,6 +75,7 @@ struct sdmmc_chip_functions { #define SDMMC_SDCLK_OFF 0 #define SDMMC_SDCLK_400KHZ 400 #define SDMMC_SDCLK_25MHZ 25000 +#define SDMMC_SDCLK_50MHZ 50000 struct sdmmcbus_attach_args { const char *saa_busname; @@ -80,6 +84,7 @@ struct sdmmcbus_attach_args { int flags; int caps; long max_xfer; + long saa_clkmax; }; void sdmmc_needs_discover(struct device *); Index: dev/sdmmc/sdmmcreg.h =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmcreg.h,v retrieving revision 1.5 diff -u -p -u -p -r1.5 sdmmcreg.h --- dev/sdmmc/sdmmcreg.h 12 Sep 2013 11:54:04 -0000 1.5 +++ dev/sdmmc/sdmmcreg.h 6 Nov 2013 16:59:44 -0000 @@ -40,11 +40,14 @@ /* SD commands */ /* response type */ #define SD_SEND_RELATIVE_ADDR 3 /* R6 */ +#define SD_SEND_SWITCH_FUNC 6 /* R1 */ #define SD_SEND_IF_COND 8 /* R7 */ /* SD application commands */ /* response type */ #define SD_APP_SET_BUS_WIDTH 6 /* R1 */ +#define SD_APP_SD_STATUS 13 /* R1 */ #define SD_APP_OP_COND 41 /* R3 */ +#define SD_APP_SEND_SCR 51 /* R1 */ /* OCR bits */ #define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */ @@ -80,6 +83,7 @@ #define MMC_R1(resp) ((resp)[0]) #define MMC_R3(resp) ((resp)[0]) #define SD_R6(resp) ((resp)[0]) +#define MMC_SPI_R1(resp) ((resp)[0]) /* RCA argument and response */ #define MMC_ARG_RCA(rca) ((rca) << 16) @@ -136,11 +140,15 @@ #define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */ #define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */ #define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */ +#define MMC_CSD_TRAN_SPEED_MANT(resp) MMC_RSP_BITS((resp), 99, 4) +#define MMC_CSD_TRAN_SPEED_EXP(resp) MMC_RSP_BITS((resp), 96, 3) #define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) #define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) #define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \ (MMC_CSD_C_SIZE_MULT((resp))+2)) #define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) +#define MMC_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3) +#define MMC_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4) /* MMC v1 R2 response (CID) */ #define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24) @@ -178,13 +186,25 @@ #define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) #define SD_CSD_CSDVER_1_0 0 #define SD_CSD_CSDVER_2_0 1 +#define SD_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4) #define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8) #define SD_CSD_TAAC_1_5_MSEC 0x26 #define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8) #define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8) +#define SD_CSD_SPEED_MANT(resp) MMC_RSP_BITS((resp), 99, 4) +#define SD_CSD_SPEED_EXP(resp) MMC_RSP_BITS((resp), 96, 3) #define SD_CSD_SPEED_25_MHZ 0x32 #define SD_CSD_SPEED_50_MHZ 0x5a #define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12) +#define SD_CSD_CCC_BASIC (1 << 0) /* basic */ +#define SD_CSD_CCC_BR (1 << 2) /* block read */ +#define SD_CSD_CCC_BW (1 << 4) /* block write */ +#define SD_CSD_CCC_ERACE (1 << 5) /* erase */ +#define SD_CSD_CCC_WP (1 << 6) /* write protection */ +#define SD_CSD_CCC_LC (1 << 7) /* lock card */ +#define SD_CSD_CCC_AS (1 << 8) /*application specific*/ +#define SD_CSD_CCC_IOM (1 << 9) /* I/O mode */ +#define SD_CSD_CCC_SWITCH (1 << 10) /* switch */ #define SD_CSD_CCC_ALL 0x5f5 #define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) #define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1) @@ -234,6 +254,32 @@ #define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8) #define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32) #define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12) + +/* SCR (SD Configuration Register) */ +#define SCR_STRUCTURE(scr) MMC_RSP_BITS((scr), 60, 4) +#define SCR_STRUCTURE_VER_1_0 0 /* Version 1.0 */ +#define SCR_SD_SPEC(scr) MMC_RSP_BITS((scr), 56, 4) +#define SCR_SD_SPEC_VER_1_0 0 /* Version 1.0 and 1.01 */ +#define SCR_SD_SPEC_VER_1_10 1 /* Version 1.10 */ +#define SCR_SD_SPEC_VER_2 2 /* Version 2.00 or Version 3.0X */ +#define SCR_DATA_STAT_AFTER_ERASE(scr) MMC_RSP_BITS((scr), 55, 1) +#define SCR_SD_SECURITY(scr) MMC_RSP_BITS((scr), 52, 3) +#define SCR_SD_SECURITY_NONE 0 /* no security */ +#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */ +#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */ +#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4) +#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */ +#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */ +#define SCR_SD_SPEC3(scr) MMC_RSP_BITS((scr), 47, 1) +#define SCR_EX_SECURITY(scr) MMC_RSP_BITS((scr), 43, 4) +#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 34, 9) +#define SCR_CMD_SUPPORT_CMD23(scr) MMC_RSP_BITS((scr), 33, 1) +#define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1) +#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32) + +/* Status of Switch Function */ +#define SFUNC_STATUS_GROUP(status, group) \ + (__bitfield((uint32_t *)(status), 400 + (group - 1) * 16, 16)) /* 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)) Index: dev/sdmmc/sdmmcvar.h =================================================================== RCS file: /cvs/src/sys/dev/sdmmc/sdmmcvar.h,v retrieving revision 1.22 diff -u -p -u -p -r1.22 sdmmcvar.h --- dev/sdmmc/sdmmcvar.h 12 Sep 2013 11:54:04 -0000 1.22 +++ dev/sdmmc/sdmmcvar.h 6 Nov 2013 16:59:44 -0000 @@ -34,6 +34,10 @@ struct sdmmc_csd { int capacity; /* total number of sectors */ int sector_size; /* sector size in bytes */ int read_bl_len; /* block length for reads */ + int write_bl_len; /* block length for writes */ + int r2w_factor; + int tran_speed; /* transfer speed (kbit/s) */ + int ccc; /* Card Command Class for SD */ /* ... */ }; @@ -46,6 +50,11 @@ struct sdmmc_cid { int mdt; /* manufacturing date */ }; +struct sdmmc_scr { + int sd_spec; + int bus_width; +}; + typedef u_int32_t sdmmc_response[4]; struct sdmmc_softc; @@ -88,6 +97,11 @@ struct sdmmc_command { #define SCF_RSP_CRC 0x0400 #define SCF_RSP_IDX 0x0800 #define SCF_RSP_PRESENT 0x1000 +/* SPI */ +#define SCF_RSP_SPI_S1 (1U << 10) +#define SCF_RSP_SPI_S2 (1U << 11) +#define SCF_RSP_SPI_B4 (1U << 12) +#define SCF_RSP_SPI_BSY (1U << 13) /* response types */ #define SCF_RSP_R0 0 /* none */ #define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) @@ -99,6 +113,14 @@ struct sdmmc_command { #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|SCF_RSP_IDX) #define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +/* SPI */ +#define SCF_RSP_SPI_R1 (SCF_RSP_SPI_S1) +#define SCF_RSP_SPI_R1B (SCF_RSP_SPI_S1|SCF_RSP_SPI_BSY) +#define SCF_RSP_SPI_R2 (SCF_RSP_SPI_S1|SCF_RSP_SPI_S2) +#define SCF_RSP_SPI_R3 (SCF_RSP_SPI_S1|SCF_RSP_SPI_B4) +#define SCF_RSP_SPI_R4 (SCF_RSP_SPI_S1|SCF_RSP_SPI_B4) +#define SCF_RSP_SPI_R5 (SCF_RSP_SPI_S1|SCF_RSP_SPI_S2) +#define SCF_RSP_SPI_R7 (SCF_RSP_SPI_S1|SCF_RSP_SPI_B4) int c_error; /* errno value on completion */ /* Host controller owned fields for data xfer in progress */ @@ -134,6 +156,7 @@ struct sdmmc_function { /* common members */ struct sdmmc_softc *sc; /* card slot softc */ u_int16_t rca; /* relative card address */ + int width; /* bus width */ int flags; #define SFF_ERROR 0x0001 /* function is poo; ignore it */ #define SFF_SDHC 0x0002 /* SD High Capacity card */ @@ -146,6 +169,8 @@ struct sdmmc_function { struct sdmmc_csd csd; /* decoded CSD value */ struct sdmmc_cid cid; /* decoded CID value */ sdmmc_response raw_cid; /* temp. storage for decoding */ + uint32_t raw_scr[2]; + struct sdmmc_scr scr; /* decoded SCR value */ }; /* @@ -177,6 +202,9 @@ struct sdmmc_softc { #define SMC_CAPS_SD_HIGHSPEED 0x0100 /* SD high-speed timing */ #define SMC_CAPS_MMC_HIGHSPEED 0x0200 /* MMC high-speed timing */ + long sc_busclk; + int sc_buswidth; /* host bus width */ + long sc_clkmax; int sc_function_count; /* number of I/O functions (SDIO) */ struct sdmmc_function *sc_card; /* selected card */ struct sdmmc_function *sc_fn0; /* function 0, the card itself */