The diff below adds support for changing the bus width to the sdmmc
subsystem and the sdhc(4) controller.  By default controllers and card
use a 1-bit bus.  But most SD cards actually have support fora 4-bit
bus.  This can be checked by looking atthe SCR register.  In theory
using the 4-bit bus quadruples the data rate to and from the card.

With this diff the raw disk transferrate of the sdhc(4) controller in
te PC-Engines APU2 goes up from 1.5 MB/s to 5.5 MB/s.

ok?


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 -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        30 Apr 2016 19:34:18 -0000
@@ -90,6 +90,7 @@ struct sdmmc_chip_functions pxammc_funct
        /* bus power and clock frequency */
        pxammc_bus_power,
        pxammc_bus_clock,
+       NULL,
        /* command execution */
        pxammc_exec_command
 };
Index: arch/armv7/exynos/exesdhc.c
===================================================================
RCS file: /cvs/src/sys/arch/armv7/exynos/exesdhc.c,v
retrieving revision 1.4
diff -u -p -r1.4 exesdhc.c
--- arch/armv7/exynos/exesdhc.c 10 Jan 2016 14:11:43 -0000      1.4
+++ arch/armv7/exynos/exesdhc.c 30 Apr 2016 19:34:18 -0000
@@ -230,6 +230,7 @@ struct sdmmc_chip_functions exesdhc_func
        /* bus power and clock frequency */
        exesdhc_bus_power,
        exesdhc_bus_clock,
+       NULL,
        /* command execution */
        exesdhc_exec_command,
        /* card interrupt */
Index: arch/armv7/imx/imxesdhc.c
===================================================================
RCS file: /cvs/src/sys/arch/armv7/imx/imxesdhc.c,v
retrieving revision 1.13
diff -u -p -r1.13 imxesdhc.c
--- arch/armv7/imx/imxesdhc.c   10 Jan 2016 14:11:43 -0000      1.13
+++ arch/armv7/imx/imxesdhc.c   30 Apr 2016 19:34:18 -0000
@@ -226,6 +226,7 @@ struct sdmmc_chip_functions imxesdhc_fun
        /* bus power and clock frequency */
        imxesdhc_bus_power,
        imxesdhc_bus_clock,
+       NULL,
        /* command execution */
        imxesdhc_exec_command,
        /* card interrupt */
Index: arch/armv7/omap/ommmc.c
===================================================================
RCS file: /cvs/src/sys/arch/armv7/omap/ommmc.c,v
retrieving revision 1.15
diff -u -p -r1.15 ommmc.c
--- arch/armv7/omap/ommmc.c     10 Jan 2016 14:11:43 -0000      1.15
+++ arch/armv7/omap/ommmc.c     30 Apr 2016 19:34:18 -0000
@@ -260,6 +260,7 @@ struct sdmmc_chip_functions ommmc_functi
        /* bus power and clock frequency */
        ommmc_bus_power,
        ommmc_bus_clock,
+       NULL,
        /* command execution */
        ommmc_exec_command,
        /* card interrupt */
Index: dev/sdmmc/sdhc.c
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdhc.c,v
retrieving revision 1.45
diff -u -p -r1.45 sdhc.c
--- dev/sdmmc/sdhc.c    30 Apr 2016 13:33:35 -0000      1.45
+++ dev/sdmmc/sdhc.c    30 Apr 2016 19:34:18 -0000
@@ -87,6 +87,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 *);
@@ -117,6 +118,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 */
@@ -289,6 +291,7 @@ sdhc_host_found(struct sdhc_softc *sc, b
        saa.saa_busname = "sdmmc";
        saa.sct = &sdhc_functions;
        saa.sch = hp;
+       saa.caps = SMC_CAPS_4BIT_MODE;
        saa.dmat = sc->sc_dmat;
        if (ISSET(hp->flags, SHF_USE_DMA))
                saa.caps |= SMC_CAPS_DMA;
@@ -597,6 +600,36 @@ sdhc_bus_clock(sdmmc_chipset_handle_t sc
 ret:
        splx(s);
        return error;
+}
+
+int
+sdhc_bus_width(sdmmc_chipset_handle_t sch, int width)
+{
+       struct sdhc_host *hp = (struct sdhc_host *)sch;
+       int reg;
+       int s;
+
+       if (width != 1 && width != 4 && width != 8)
+               return 1;
+
+       s = splsdmmc();
+
+       reg = HREAD1(hp, SDHC_HOST_CTL);
+       reg &= ~SDHC_4BIT_MODE;
+       if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3) {
+               reg &= ~SDHC_8BIT_MODE;
+       }
+       if (width == 4) {
+               reg |= SDHC_4BIT_MODE;
+       } else if (width == 8) {
+               KASSERT(SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3);
+               reg |= SDHC_8BIT_MODE;
+       }
+       HWRITE1(hp, SDHC_HOST_CTL, reg);
+
+       splx(s);
+
+       return 0;
 }
 
 void
Index: dev/sdmmc/sdmmc.c
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmc.c,v
retrieving revision 1.40
diff -u -p -r1.40 sdmmc.c
--- dev/sdmmc/sdmmc.c   30 Apr 2016 11:32:23 -0000      1.40
+++ dev/sdmmc/sdmmc.c   30 Apr 2016 19:34:18 -0000
@@ -60,7 +60,6 @@ int   sdmmc_enable(struct sdmmc_softc *);
 void   sdmmc_disable(struct sdmmc_softc *);
 int    sdmmc_scan(struct sdmmc_softc *);
 int    sdmmc_init(struct sdmmc_softc *);
-int    sdmmc_set_bus_width(struct sdmmc_function *);
 #ifdef SDMMC_IOCTL
 int    sdmmc_ioctl(struct device *, u_long, caddr_t);
 #endif
@@ -698,37 +697,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_mem.c
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmc_mem.c,v
retrieving revision 1.23
diff -u -p -r1.23 sdmmc_mem.c
--- dev/sdmmc/sdmmc_mem.c       30 Apr 2016 11:32:23 -0000      1.23
+++ dev/sdmmc/sdmmc_mem.c       30 Apr 2016 19:34:18 -0000
@@ -37,7 +37,12 @@ void sdmmc_print_cid(struct sdmmc_cid *)
 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_function *);
 
+int    sdmmc_mem_send_scr(struct sdmmc_softc *, uint32_t *);
+int    sdmmc_mem_decode_scr(struct sdmmc_softc *, uint32_t *,
+           struct sdmmc_function *);
+
 int    sdmmc_mem_send_cxd_data(struct sdmmc_softc *, int, void *, size_t);
+int    sdmmc_set_bus_width(struct sdmmc_function *, int);
 int    sdmmc_mem_mmc_switch(struct sdmmc_function *, uint8_t, uint8_t, 
uint8_t);
 
 int    sdmmc_mem_sd_init(struct sdmmc_softc *, struct sdmmc_function *);
@@ -339,6 +344,70 @@ sdmmc_print_cid(struct sdmmc_cid *cid)
 #endif
 
 int
+sdmmc_mem_send_scr(struct sdmmc_softc *sc, uint32_t *scr)
+{
+       struct sdmmc_command cmd;
+       void *ptr = NULL;
+       int datalen = 8;
+       int error = 0;
+
+       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;
+       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, datalen);
+
+       return error;
+}
+
+int
+sdmmc_mem_decode_scr(struct sdmmc_softc *sc, uint32_t *raw_scr,
+    struct sdmmc_function *sf)
+{
+       sdmmc_response resp;
+       int ver;
+
+       memset(resp, 0, sizeof(resp));
+       /*
+        * Change the raw SCR to a response.
+        */
+       resp[0] = be32toh(raw_scr[1]) >> 8;             // LSW
+       resp[1] = be32toh(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: %s: %08x%08x ver=%d, spec=%d, bus width=%d\n",
+           DEVNAME(sc), __func__, resp[1], resp[0],
+           ver, sf->scr.sd_spec, sf->scr.bus_width));
+
+       if (ver != 0) {
+               DPRINTF(("%s: unknown SCR structure version: %d\n",
+                   DEVNAME(sc), ver));
+               return EINVAL;
+       }
+       return 0;
+}
+
+int
 sdmmc_mem_send_cxd_data(struct sdmmc_softc *sc, int opcode, void *data,
     size_t datalen)
 {
@@ -376,6 +445,36 @@ out:
 }
 
 int
+sdmmc_set_bus_width(struct sdmmc_function *sf, int width)
+{
+       struct sdmmc_softc *sc = sf->sc;
+       struct sdmmc_command cmd;
+       int error;
+
+       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)
 {
@@ -416,7 +515,27 @@ sdmmc_mem_init(struct sdmmc_softc *sc, s
 int
 sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
 {
-       /* XXX */
+       uint32_t raw_scr[2];
+       int error;
+
+       error = sdmmc_mem_send_scr(sc, raw_scr);
+       if (error) {
+               printf("%s: SD_SEND_SCR send failed\n", DEVNAME(sc));
+               return error;
+       }
+       error = sdmmc_mem_decode_scr(sc, raw_scr, sf);
+       if (error)
+               return error;
+
+       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", DEVNAME(sc)));
+               error = sdmmc_set_bus_width(sf, 4);
+               if (error) {
+                       printf("%s: can't change bus width\n", DEVNAME(sc));
+                       return error;
+               }
+       }
 
        return 0;
 }
Index: dev/sdmmc/sdmmcchip.h
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmcchip.h,v
retrieving revision 1.6
diff -u -p -r1.6 sdmmcchip.h
--- dev/sdmmc/sdmmcchip.h       30 Apr 2016 11:32:23 -0000      1.6
+++ dev/sdmmc/sdmmcchip.h       30 Apr 2016 19:34:18 -0000
@@ -37,6 +37,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 *);
@@ -61,6 +62,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)))
Index: dev/sdmmc/sdmmcreg.h
===================================================================
RCS file: /cvs/src/sys/dev/sdmmc/sdmmcreg.h,v
retrieving revision 1.8
diff -u -p -r1.8 sdmmcreg.h
--- dev/sdmmc/sdmmcreg.h        10 Jan 2016 14:11:43 -0000      1.8
+++ dev/sdmmc/sdmmcreg.h        30 Apr 2016 19:34:18 -0000
@@ -45,6 +45,7 @@
 /* SD application commands */                  /* response type */
 #define SD_APP_SET_BUS_WIDTH           6       /* 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 */
@@ -235,6 +236,29 @@
 #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_SD_SPEC4(scr)              MMC_RSP_BITS((scr), 42, 1)
+#define SCR_RESERVED(scr)              MMC_RSP_BITS((scr), 34, 8)
+#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)
 
 /* 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.23
diff -u -p -r1.23 sdmmcvar.h
--- dev/sdmmc/sdmmcvar.h        30 Apr 2016 11:32:23 -0000      1.23
+++ dev/sdmmc/sdmmcvar.h        30 Apr 2016 19:34:18 -0000
@@ -48,6 +48,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;
@@ -149,6 +154,7 @@ 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 */
+       struct sdmmc_scr scr;           /* decoded SCR value */
 };
 
 /*
Index: dev/ic/rtsx.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/rtsx.c,v
retrieving revision 1.12
diff -u -p -r1.12 rtsx.c
--- dev/ic/rtsx.c       28 Apr 2015 07:55:13 -0000      1.12
+++ dev/ic/rtsx.c       30 Apr 2016 19:34:18 -0000
@@ -146,6 +146,7 @@ struct sdmmc_chip_functions rtsx_functio
        /* bus power and clock frequency */
        rtsx_bus_power,
        rtsx_bus_clock,
+       NULL,
        /* command execution */
        rtsx_exec_command,
        /* card interrupt */
Index: dev/ic/w83l518d_sdmmc.c
===================================================================
RCS file: /cvs/src/sys/dev/ic/w83l518d_sdmmc.c,v
retrieving revision 1.2
diff -u -p -r1.2 w83l518d_sdmmc.c
--- dev/ic/w83l518d_sdmmc.c     10 May 2014 18:41:55 -0000      1.2
+++ dev/ic/w83l518d_sdmmc.c     30 Apr 2016 19:34:18 -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);
@@ -88,9 +86,7 @@ struct sdmmc_chip_functions wb_sdmmc_chi
        /* bus power 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 */
@@ -304,7 +300,6 @@ wb_sdmmc_bus_clock(sdmmc_chipset_handle_
        return 0;
 }
 
-#if 0
 int
 wb_sdmmc_bus_width(sdmmc_chipset_handle_t sch, int width)
 {
@@ -319,7 +314,6 @@ wb_sdmmc_bus_width(sdmmc_chipset_handle_
 
        return 0;
 }
-#endif
 
 void
 wb_sdmmc_rsp_read_long(struct wb_softc *wb, struct sdmmc_command *cmd)

Reply via email to