Module Name: src Committed By: jmcneill Date: Sun Jul 16 17:11:46 UTC 2017
Modified Files: src/sys/dev/sdmmc: ld_sdmmc.c sdmmc_mem.c sdmmcreg.h sdmmcvar.h Log Message: Add support for eMMC 4.5's optional cache feature. If a cache is present, and the host controller reports the SMC_CAPS_POLLING capability (needed to flush cache at shutdown), it will be automatically enabled and used. To generate a diff of this commit: cvs rdiff -u -r1.30 -r1.31 src/sys/dev/sdmmc/ld_sdmmc.c cvs rdiff -u -r1.60 -r1.61 src/sys/dev/sdmmc/sdmmc_mem.c cvs rdiff -u -r1.31 -r1.32 src/sys/dev/sdmmc/sdmmcreg.h cvs rdiff -u -r1.27 -r1.28 src/sys/dev/sdmmc/sdmmcvar.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/sdmmc/ld_sdmmc.c diff -u src/sys/dev/sdmmc/ld_sdmmc.c:1.30 src/sys/dev/sdmmc/ld_sdmmc.c:1.31 --- src/sys/dev/sdmmc/ld_sdmmc.c:1.30 Mon Jul 10 10:35:07 2017 +++ src/sys/dev/sdmmc/ld_sdmmc.c Sun Jul 16 17:11:46 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: ld_sdmmc.c,v 1.30 2017/07/10 10:35:07 mlelstv Exp $ */ +/* $NetBSD: ld_sdmmc.c,v 1.31 2017/07/16 17:11:46 jmcneill Exp $ */ /* * Copyright (c) 2008 KIYOHARA Takashi @@ -28,7 +28,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: ld_sdmmc.c,v 1.30 2017/07/10 10:35:07 mlelstv Exp $"); +__KERNEL_RCSID(0, "$NetBSD: ld_sdmmc.c,v 1.31 2017/07/16 17:11:46 jmcneill Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -94,6 +94,7 @@ static int ld_sdmmc_dump(struct ld_softc static int ld_sdmmc_start(struct ld_softc *, struct buf *); static void ld_sdmmc_restart(void *); static int ld_sdmmc_discard(struct ld_softc *, off_t, off_t); +static int ld_sdmmc_ioctl(struct ld_softc *, u_long, void *, int32_t, bool); static void ld_sdmmc_doattach(void *); static void ld_sdmmc_dobio(void *); @@ -151,6 +152,7 @@ ld_sdmmc_attach(device_t parent, device_ ld->sc_dump = ld_sdmmc_dump; ld->sc_start = ld_sdmmc_start; ld->sc_discard = ld_sdmmc_discard; + ld->sc_ioctl = ld_sdmmc_ioctl; /* * Defer attachment of ld + disk subsystem to a thread. @@ -174,11 +176,19 @@ ld_sdmmc_doattach(void *arg) struct ld_sdmmc_softc *sc = (struct ld_sdmmc_softc *)arg; struct ld_softc *ld = &sc->sc_ld; struct sdmmc_softc *ssc = device_private(device_parent(ld->sc_dv)); + const u_int cache_size = sc->sc_sf->ext_csd.cache_size; + char buf[sizeof("9999 KB")]; ldattach(ld, BUFQ_DISK_DEFAULT_STRAT); aprint_normal_dev(ld->sc_dv, "%d-bit width,", sc->sc_sf->width); if (ssc->sc_transfer_mode != NULL) aprint_normal(" %s,", ssc->sc_transfer_mode); + if (cache_size > 0) { + format_bytes(buf, sizeof(buf), cache_size); + aprint_normal(" %s cache%s,", buf, + ISSET(sc->sc_sf->flags, SFF_CACHE_ENABLED) ? "" : + " (disabled)"); + } if ((ssc->sc_busclk / 1000) != 0) aprint_normal(" %u.%03u MHz\n", ssc->sc_busclk / 1000, ssc->sc_busclk % 1000); @@ -312,6 +322,20 @@ ld_sdmmc_discard(struct ld_softc *ld, of return sdmmc_mem_discard(sc->sc_sf, pos, len); } +static int +ld_sdmmc_ioctl(struct ld_softc *ld, u_long cmd, void *addr, int32_t flag, + bool poll) +{ + struct ld_sdmmc_softc *sc = device_private(ld->sc_dv); + + switch (cmd) { + case DIOCCACHESYNC: + return sdmmc_mem_flush_cache(sc->sc_sf, poll); + default: + return EPASSTHROUGH; + } +} + MODULE(MODULE_CLASS_DRIVER, ld_sdmmc, "ld"); #ifdef _MODULE Index: src/sys/dev/sdmmc/sdmmc_mem.c diff -u src/sys/dev/sdmmc/sdmmc_mem.c:1.60 src/sys/dev/sdmmc/sdmmc_mem.c:1.61 --- src/sys/dev/sdmmc/sdmmc_mem.c:1.60 Sat Jun 24 23:25:01 2017 +++ src/sys/dev/sdmmc/sdmmc_mem.c Sun Jul 16 17:11:46 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc_mem.c,v 1.60 2017/06/24 23:25:01 jmcneill Exp $ */ +/* $NetBSD: sdmmc_mem.c,v 1.61 2017/07/16 17:11:46 jmcneill Exp $ */ /* $OpenBSD: sdmmc_mem.c,v 1.10 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -45,7 +45,7 @@ /* Routines for SD/MMC memory cards. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.60 2017/06/24 23:25:01 jmcneill Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.61 2017/07/16 17:11:46 jmcneill Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -87,7 +87,7 @@ static int sdmmc_mem_send_cxd_data(struc static int sdmmc_set_bus_width(struct sdmmc_function *, int); static int sdmmc_mem_sd_switch(struct sdmmc_function *, int, int, int, sdmmc_bitfield512_t *); static int sdmmc_mem_mmc_switch(struct sdmmc_function *, uint8_t, uint8_t, - uint8_t); + uint8_t, bool); static int sdmmc_mem_signal_voltage(struct sdmmc_softc *, int); static int sdmmc_mem_spi_read_ocr(struct sdmmc_softc *, uint32_t, uint32_t *); static int sdmmc_mem_single_read_block(struct sdmmc_function *, uint32_t, @@ -981,7 +981,7 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s if (width != 1) { error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, value); + EXT_CSD_BUS_WIDTH, value, false); if (error == 0) error = sdmmc_chip_bus_width(sc->sc_sct, sc->sc_sch, width); @@ -1002,7 +1002,7 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s } if (hs_timing != EXT_CSD_HS_TIMING_LEGACY) { error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_HS_TIMING, hs_timing); + EXT_CSD_HS_TIMING, hs_timing, false); if (error) { aprint_error_dev(sc->sc_dev, "can't change high speed %d, error %d\n", @@ -1048,7 +1048,7 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_BUS_WIDTH, (width == 8) ? EXT_CSD_BUS_WIDTH_8_DDR : - EXT_CSD_BUS_WIDTH_4_DDR); + EXT_CSD_BUS_WIDTH_4_DDR, false); if (error) { DPRINTF(("%s: can't switch to DDR" " (%d bit)\n", SDMMCDEVNAME(sc), width)); @@ -1104,6 +1104,23 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *s sf->ext_csd.rst_n_function = ext_csd[EXT_CSD_RST_N_FUNCTION]; } + + if (sf->ext_csd.rev >= 6) { + sf->ext_csd.cache_size = + le32dec(&ext_csd[EXT_CSD_CACHE_SIZE]) * 1024; + } + if (sf->ext_csd.cache_size > 0) { + /* eMMC cache present, enable it */ + error = sdmmc_mem_mmc_switch(sf, + EXT_CSD_CMD_SET_NORMAL, EXT_CSD_CACHE_CTRL, + EXT_CSD_CACHE_CTRL_CACHE_EN, false); + if (error) { + aprint_error_dev(sc->sc_dev, + "can't enable cache: %d\n", error); + } else { + SET(sf->flags, SFF_CACHE_ENABLED); + } + } } else { if (sc->sc_busclk > sf->csd.tran_speed) sc->sc_busclk = sf->csd.tran_speed; @@ -1600,7 +1617,7 @@ dmamem_free: static int sdmmc_mem_mmc_switch(struct sdmmc_function *sf, uint8_t set, uint8_t index, - uint8_t value) + uint8_t value, bool poll) { struct sdmmc_softc *sc = sf->sc; struct sdmmc_command cmd; @@ -1612,6 +1629,9 @@ sdmmc_mem_mmc_switch(struct sdmmc_functi (index << 16) | (value << 8) | set; cmd.c_flags = SCF_RSP_SPI_R1B | SCF_RSP_R1B | SCF_CMD_AC; + if (poll) + cmd.c_flags |= SCF_POLL; + error = sdmmc_mmc_command(sc, &cmd); if (error) return error; @@ -1623,6 +1643,8 @@ sdmmc_mem_mmc_switch(struct sdmmc_functi if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) cmd.c_arg = MMC_ARG_RCA(sf->rca); cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_RSP_SPI_R2; + if (poll) + cmd.c_flags |= SCF_POLL; error = sdmmc_mmc_command(sc, &cmd); if (error) break; @@ -2193,3 +2215,29 @@ out: return error; } + +int +sdmmc_mem_flush_cache(struct sdmmc_function *sf, bool poll) +{ + struct sdmmc_softc *sc = sf->sc; + int error; + + if (!ISSET(sf->flags, SFF_CACHE_ENABLED)) + return 0; + + SDMMC_LOCK(sc); + mutex_enter(&sc->sc_mtx); + + error = sdmmc_mem_mmc_switch(sf, + EXT_CSD_CMD_SET_NORMAL, EXT_CSD_FLUSH_CACHE, + EXT_CSD_FLUSH_CACHE_FLUSH, poll); + + mutex_exit(&sc->sc_mtx); + SDMMC_UNLOCK(sc); + +#ifdef SDMMC_DEBUG + device_printf(sc->sc_dev, "mmc flush cache error %d\n", error); +#endif + + return error; +} Index: src/sys/dev/sdmmc/sdmmcreg.h diff -u src/sys/dev/sdmmc/sdmmcreg.h:1.31 src/sys/dev/sdmmc/sdmmcreg.h:1.32 --- src/sys/dev/sdmmc/sdmmcreg.h:1.31 Sat Jun 24 23:07:35 2017 +++ src/sys/dev/sdmmc/sdmmcreg.h Sun Jul 16 17:11:46 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmcreg.h,v 1.31 2017/06/24 23:07:35 jmcneill Exp $ */ +/* $NetBSD: sdmmcreg.h,v 1.32 2017/07/16 17:11:46 jmcneill Exp $ */ /* $OpenBSD: sdmmcreg.h,v 1.4 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -121,6 +121,8 @@ #define SD_ARG_BUS_WIDTH_4 2 /* EXT_CSD fields */ +#define EXT_CSD_FLUSH_CACHE 32 /* W/E_P */ +#define EXT_CSD_CACHE_CTRL 33 /* R/W/E_P */ #define EXT_CSD_RST_N_FUNCTION 162 /* R/W */ #define EXT_CSD_BUS_WIDTH 183 /* W/E_P */ #define EXT_CSD_HS_TIMING 185 /* R/W/E_P */ @@ -128,12 +130,20 @@ #define EXT_CSD_STRUCTURE 194 /* R */ #define EXT_CSD_CARD_TYPE 196 /* R */ #define EXT_CSD_SEC_COUNT 212 /* R */ +#define EXT_CSD_CACHE_SIZE 249 /* R (4 bytes) */ /* EXT_CSD field definitions */ #define EXT_CSD_CMD_SET_NORMAL (1U << 0) #define EXT_CSD_CMD_SET_SECURE (1U << 1) #define EXT_CSD_CMD_SET_CPSECURE (1U << 2) +/* EXT_CSD_FLUSH_CACHE */ +#define EXT_CSD_FLUSH_CACHE_FLUSH (1U << 0) +#define EXT_CSD_FLUSH_CACHE_BARRIER (1U << 1) + +/* EXT_CSD_CACHE_CTRL */ +#define EXT_CSD_CACHE_CTRL_CACHE_EN (1U << 0) + /* EXT_CSD_BUS_WIDTH */ #define EXT_CSD_BUS_WIDTH_1 0 /* 1 bit mode */ #define EXT_CSD_BUS_WIDTH_4 1 /* 4 bit mode */ Index: src/sys/dev/sdmmc/sdmmcvar.h diff -u src/sys/dev/sdmmc/sdmmcvar.h:1.27 src/sys/dev/sdmmc/sdmmcvar.h:1.28 --- src/sys/dev/sdmmc/sdmmcvar.h:1.27 Sat Jun 24 23:25:01 2017 +++ src/sys/dev/sdmmc/sdmmcvar.h Sun Jul 16 17:11:46 2017 @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmcvar.h,v 1.27 2017/06/24 23:25:01 jmcneill Exp $ */ +/* $NetBSD: sdmmcvar.h,v 1.28 2017/07/16 17:11:46 jmcneill Exp $ */ /* $OpenBSD: sdmmcvar.h,v 1.13 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -52,6 +52,7 @@ struct sdmmc_csd { struct sdmmc_ext_csd { uint8_t rev; uint8_t rst_n_function; /* RST_n_FUNCTION */ + uint32_t cache_size; }; struct sdmmc_cid { @@ -122,8 +123,9 @@ struct sdmmc_command { #define SCF_RSP_SPI_BSY (1U << 13) /* Probing */ #define SCF_TOUT_OK (1U << 14) /* command timeout expected */ -/* Transfer hints */ +/* Command hints */ #define SCF_XFER_SDHC (1U << 15) /* card is SDHC */ +#define SCF_POLL (1U << 16) /* polling required */ /* response types */ #define SCF_RSP_R0 0 /* none */ #define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) @@ -185,6 +187,7 @@ struct sdmmc_function { int flags; #define SFF_ERROR 0x0001 /* function is poo; ignore it */ #define SFF_SDHC 0x0002 /* SD High Capacity card */ +#define SFF_CACHE_ENABLED 0x0004 /* cache enabled */ SIMPLEQ_ENTRY(sdmmc_function) sf_list; /* SD card I/O function members */ int number; /* I/O function number or -1 */ @@ -249,6 +252,7 @@ struct sdmmc_softc { | SMC_CAPS_UHS_SDR104 \ | SMC_CAPS_UHS_DDR50) #define SMC_CAPS_MMC_HS200 __BIT(15) /* eMMC HS200 timing */ +#define SMC_CAPS_POLLING __BIT(30) /* driver supports cmd polling */ /* function */ int sc_function_count; /* number of I/O functions (SDIO) */ @@ -378,5 +382,6 @@ int sdmmc_mem_read_block(struct sdmmc_fu int sdmmc_mem_write_block(struct sdmmc_function *, uint32_t, u_char *, size_t); int sdmmc_mem_discard(struct sdmmc_function *, off_t, off_t); +int sdmmc_mem_flush_cache(struct sdmmc_function *, bool); #endif /* _SDMMCVAR_H_ */