Module Name: src
Committed By: jmcneill
Date: Fri Oct 18 11:03:53 UTC 2024
Modified Files:
src/sys/dev/sdmmc: ld_sdmmc.c sdmmc_mem.c sdmmcreg.h sdmmcvar.h
Log Message:
sdmmc: Add support for SD card caches.
SD physical 6.0 specification introduced Application Performance Class 2
(A2), which adds support for drive caches and command queueing.
Add support for enabling and flushing the cache when this feature is
present.
To generate a diff of this commit:
cvs rdiff -u -r1.43 -r1.44 src/sys/dev/sdmmc/ld_sdmmc.c
cvs rdiff -u -r1.75 -r1.76 src/sys/dev/sdmmc/sdmmc_mem.c
cvs rdiff -u -r1.34 -r1.35 src/sys/dev/sdmmc/sdmmcreg.h
cvs rdiff -u -r1.36 -r1.37 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.43 src/sys/dev/sdmmc/ld_sdmmc.c:1.44
--- src/sys/dev/sdmmc/ld_sdmmc.c:1.43 Tue Jan 23 23:13:05 2024
+++ src/sys/dev/sdmmc/ld_sdmmc.c Fri Oct 18 11:03:52 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: ld_sdmmc.c,v 1.43 2024/01/23 23:13:05 riastradh Exp $ */
+/* $NetBSD: ld_sdmmc.c,v 1.44 2024/10/18 11:03:52 jmcneill Exp $ */
/*
* Copyright (c) 2008 KIYOHARA Takashi
@@ -28,7 +28,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: ld_sdmmc.c,v 1.43 2024/01/23 23:13:05 riastradh Exp $");
+__KERNEL_RCSID(0, "$NetBSD: ld_sdmmc.c,v 1.44 2024/10/18 11:03:52 jmcneill Exp $");
#ifdef _KERNEL_OPT
#include "opt_sdmmc.h"
@@ -322,18 +322,23 @@ 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;
+ const u_int emmc_cache_size = sc->sc_sf->ext_csd.cache_size;
+ const bool sd_cache = sc->sc_sf->ssr.cache;
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);
+ if (emmc_cache_size > 0) {
+ format_bytes(buf, sizeof(buf), emmc_cache_size);
aprint_normal(" %s cache%s,", buf,
ISSET(sc->sc_sf->flags, SFF_CACHE_ENABLED) ? "" :
" (disabled)");
+ } else if (sd_cache) {
+ aprint_normal(" Cache%s,",
+ ISSET(sc->sc_sf->flags, SFF_CACHE_ENABLED) ? "" :
+ " (disabled)");
}
if ((ssc->sc_busclk / 1000) != 0)
aprint_normal(" %u.%03u MHz\n",
Index: src/sys/dev/sdmmc/sdmmc_mem.c
diff -u src/sys/dev/sdmmc/sdmmc_mem.c:1.75 src/sys/dev/sdmmc/sdmmc_mem.c:1.76
--- src/sys/dev/sdmmc/sdmmc_mem.c:1.75 Sat Apr 29 13:21:31 2023
+++ src/sys/dev/sdmmc/sdmmc_mem.c Fri Oct 18 11:03:52 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: sdmmc_mem.c,v 1.75 2023/04/29 13:21:31 jmcneill Exp $ */
+/* $NetBSD: sdmmc_mem.c,v 1.76 2024/10/18 11:03:52 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.75 2023/04/29 13:21:31 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.76 2024/10/18 11:03:52 jmcneill Exp $");
#ifdef _KERNEL_OPT
#include "opt_sdmmc.h"
@@ -83,7 +83,15 @@ static int sdmmc_mem_send_ssr(struct sdm
sdmmc_bitfield512_t *);
static int sdmmc_mem_decode_ssr(struct sdmmc_softc *, struct sdmmc_function *,
sdmmc_bitfield512_t *);
+static int sdmmc_mem_decode_general_info(struct sdmmc_softc *,
+ struct sdmmc_function * ,const uint8_t *);
+static int sdmmc_mem_pef_enable_cache(struct sdmmc_softc *,
+ struct sdmmc_function *);
static int sdmmc_mem_send_cxd_data(struct sdmmc_softc *, int, void *, size_t);
+static int sdmmc_mem_read_extr_single(struct sdmmc_softc *, struct sdmmc_function *,
+ uint8_t, uint8_t, uint32_t, uint16_t, void *);
+static int sdmmc_mem_write_extr_single(struct sdmmc_softc *, struct sdmmc_function *,
+ uint8_t, uint8_t, uint32_t, uint8_t, bool);
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,
@@ -926,6 +934,27 @@ skipswitchfuncs:
return error;
}
+ /* detect extended functions */
+ if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE) && sf->scr.support_cmd48) {
+ uint8_t ginfo[512];
+ error = sdmmc_mem_read_extr_single(sc, sf, SD_EXTR_MIO_MEM, 0, 0,
+ sizeof(ginfo), ginfo);
+ if (error == 0) {
+ sdmmc_mem_decode_general_info(sc, sf, ginfo);
+ }
+ }
+
+ /* enable card cache if supported */
+ if (sf->ssr.cache && sf->ext_sd.pef.valid) {
+ error = sdmmc_mem_pef_enable_cache(sc, sf);
+ if (error != 0) {
+ aprint_error_dev(sc->sc_dev,
+ "can't enable cache: %d", error);
+ } else {
+ SET(sf->flags, SFF_CACHE_ENABLED);
+ }
+ }
+
return 0;
}
@@ -1308,11 +1337,20 @@ sdmmc_mem_decode_scr(struct sdmmc_softc
ver = SCR_STRUCTURE(resp);
sf->scr.sd_spec = SCR_SD_SPEC(resp);
+ if (sf->scr.sd_spec == 2) {
+ sf->scr.sd_spec3 = SCR_SD_SPEC3(resp);
+ if (sf->scr.sd_spec3) {
+ sf->scr.sd_spec4 = SCR_SD_SPEC4(resp);
+ }
+ }
sf->scr.bus_width = SCR_SD_BUS_WIDTHS(resp);
+ if (sf->scr.sd_spec4) {
+ sf->scr.support_cmd48 = SCR_CMD_SUPPORT_CMD48(resp);
+ }
- DPRINTF(("%s: sdmmc_mem_decode_scr: %08x%08x ver=%d, spec=%d, bus width=%d\n",
+ DPRINTF(("%s: sdmmc_mem_decode_scr: %08x%08x ver=%d, spec=%d,%d,%d, bus width=%d\n",
SDMMCDEVNAME(sc), resp[1], resp[0],
- ver, sf->scr.sd_spec, sf->scr.bus_width));
+ ver, sf->scr.sd_spec, sf->scr.sd_spec3, sf->scr.sd_spec4, sf->scr.bus_width));
if (ver != 0 && ver != 1) {
DPRINTF(("%s: unknown structure version: %d\n",
@@ -1413,6 +1451,7 @@ sdmmc_mem_decode_ssr(struct sdmmc_softc
const int uhs_speed_grade = SSR_UHS_SPEED_GRADE(ssr);
const int video_speed_class = SSR_VIDEO_SPEED_CLASS(ssr);
const int app_perf_class = SSR_APP_PERF_CLASS(ssr);
+ const uint64_t perf_enhance = SSR_PERFORMANCE_ENHANCE(ssr);
switch (speed_class) {
case SSR_SPEED_CLASS_0: speed_class_val = 0; break;
@@ -1429,6 +1468,10 @@ sdmmc_mem_decode_ssr(struct sdmmc_softc
default: bus_width_val = -1;
}
+ if (ISSET(perf_enhance, SSR_PERFORMANCE_ENHANCE_CACHE)) {
+ sf->ssr.cache = true;
+ }
+
/*
* Log card status
*/
@@ -1445,12 +1488,104 @@ sdmmc_mem_decode_ssr(struct sdmmc_softc
printf(", V%d", video_speed_class);
if (app_perf_class)
printf(", A%d", app_perf_class);
+ if (ISSET(perf_enhance, SSR_PERFORMANCE_ENHANCE_CACHE))
+ printf(", Cache");
+ if (ISSET(perf_enhance, SSR_PERFORMANCE_ENHANCE_HOST_MAINT|
+ SSR_PERFORMANCE_ENHANCE_CARD_MAINT)) {
+ printf(", %s self-maintenance",
+ perf_enhance == SSR_PERFORMANCE_ENHANCE_HOST_MAINT ? "Host" :
+ perf_enhance == SSR_PERFORMANCE_ENHANCE_CARD_MAINT ? "Card" :
+ "Host/Card");
+ }
printf("\n");
return 0;
}
static int
+sdmmc_mem_decode_general_info(struct sdmmc_softc *sc, struct sdmmc_function *sf,
+ const uint8_t *ginfo)
+{
+ uint16_t len = SD_GENERAL_INFO_HDR_LENGTH(ginfo);
+ unsigned num_ext = SD_GENERAL_INFO_HDR_NUM_EXT(ginfo);
+ unsigned index = SD_GENERAL_INFO_EXT_FIRST;
+ unsigned ext;
+
+ DPRINTF(("%s: sdmmc_mem_decode_general_info: rev=%u, len=%u, num_ext=%u\n",
+ SDMMCDEVNAME(sc), SD_GENERAL_INFO_HDR_REVISION(ginfo),
+ len, num_ext));
+
+ /*
+ * General Information Length can span more than one page, but for
+ * now just parse the first one.
+ */
+ len = uimin(SDMMC_SECTOR_SIZE, len);
+
+ for (ext = 0; ext < num_ext && index < len && index != 0; ext++) {
+ uint16_t sfc = SD_EXTENSION_INFO_SFC(ginfo, index);
+ unsigned num_reg = SD_EXTENSION_INFO_NUM_REG(ginfo, index);
+ uint32_t reg;
+
+ if (num_reg == 0) {
+ goto next_ext;
+ }
+ reg = SD_EXTENSION_INFO_REG(ginfo, index, 0);
+
+ DPRINTF(("%s: sdmmc_mem_decode_general_info: sfc=0x%04x, reg=0x%08x\n",
+ SDMMCDEVNAME(sc), sfc, reg));
+
+ switch (sfc) {
+ case SD_SFC_PEF:
+ sf->ext_sd.pef.valid = true;
+ sf->ext_sd.pef.fno =
+ SD_EXTENSION_INFO_REG_FNO(reg);
+ sf->ext_sd.pef.start_addr =
+ SD_EXTENSION_INFO_REG_START_ADDR(reg);
+ break;
+ }
+
+next_ext:
+ index = SD_EXTENSION_INFO_NEXT(ginfo, index);
+ }
+
+ return 0;
+}
+
+static int
+sdmmc_mem_pef_enable_cache(struct sdmmc_softc *sc,
+ struct sdmmc_function *sf)
+{
+ uint8_t data[512];
+ int error;
+
+ error = sdmmc_mem_read_extr_single(sc, sf, SD_EXTR_MIO_MEM,
+ sf->ext_sd.pef.fno, sf->ext_sd.pef.start_addr,
+ sizeof(data), data);
+ if (error != 0) {
+ return error;
+ }
+
+ if (SD_PEF_CACHE_ENABLE(data)) {
+ /* Cache is already enabled. */
+ return 0;
+ }
+
+ error = sdmmc_mem_write_extr_single(sc, sf, SD_EXTR_MIO_MEM,
+ sf->ext_sd.pef.fno,
+ sf->ext_sd.pef.start_addr + SD_PEF_CACHE_ENABLE_OFFSET, 1,
+ false);
+ if (error != 0) {
+ device_printf(sc->sc_dev,
+ "setting cache enable failed: %d\n", error);
+ return error;
+ }
+
+ device_printf(sc->sc_dev, "cache enabled\n");
+
+ return 0;
+}
+
+static int
sdmmc_mem_send_cxd_data(struct sdmmc_softc *sc, int opcode, void *data,
size_t datalen)
{
@@ -1524,6 +1659,168 @@ dmamem_free:
}
static int
+sdmmc_mem_read_extr_single(struct sdmmc_softc *sc, struct sdmmc_function *sf,
+ uint8_t mio, uint8_t fno, uint32_t addr, uint16_t datalen, void *data)
+{
+ struct sdmmc_command cmd;
+ bus_dma_segment_t ds[1];
+ void *ptr = NULL;
+ int rseg;
+ int error = 0;
+
+ if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+ error = bus_dmamem_alloc(sc->sc_dmat, datalen, PAGE_SIZE, 0, ds,
+ 1, &rseg, BUS_DMA_NOWAIT);
+ if (error)
+ goto out;
+ error = bus_dmamem_map(sc->sc_dmat, ds, 1, datalen, &ptr,
+ BUS_DMA_NOWAIT);
+ if (error)
+ goto dmamem_free;
+ error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, ptr, datalen,
+ NULL, BUS_DMA_NOWAIT|BUS_DMA_STREAMING|BUS_DMA_READ);
+ if (error)
+ goto dmamem_unmap;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
+ BUS_DMASYNC_PREREAD);
+ } else {
+ ptr = data;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.c_data = ptr;
+ cmd.c_datalen = datalen;
+ cmd.c_blklen = SDMMC_SECTOR_SIZE;
+ cmd.c_opcode = SD_READ_EXTR_SINGLE;
+ cmd.c_arg = __SHIFTIN((uint32_t)mio, SD_EXTR_MIO) |
+ __SHIFTIN((uint32_t)fno, SD_EXTR_FNO) |
+ __SHIFTIN(addr, SD_EXTR_ADDR) |
+ __SHIFTIN(datalen - 1, SD_EXTR_LEN);
+ cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1;
+ if (ISSET(sc->sc_caps, SMC_CAPS_DMA))
+ cmd.c_dmamap = sc->sc_dmap;
+
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error == 0) {
+ if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen,
+ BUS_DMASYNC_POSTREAD);
+ memcpy(data, ptr, datalen);
+ }
+#ifdef SDMMC_DEBUG
+ sdmmc_dump_data("EXT", data, datalen);
+#endif
+ }
+
+out:
+ if (ptr != NULL) {
+ if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+ bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
+dmamem_unmap:
+ bus_dmamem_unmap(sc->sc_dmat, ptr, datalen);
+dmamem_free:
+ bus_dmamem_free(sc->sc_dmat, ds, rseg);
+ }
+ }
+ return error;
+}
+
+static int
+sdmmc_mem_write_extr_single(struct sdmmc_softc *sc, struct sdmmc_function *sf,
+ uint8_t mio, uint8_t fno, uint32_t addr, uint8_t value, bool poll)
+{
+ struct sdmmc_command cmd;
+ bus_dma_segment_t ds[1];
+ uint8_t buf[512];
+ uint16_t buflen = sizeof(buf);
+ void *ptr = NULL;
+ int rseg;
+ int error = 0;
+
+ if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+ error = bus_dmamem_alloc(sc->sc_dmat, buflen, PAGE_SIZE, 0, ds,
+ 1, &rseg, BUS_DMA_NOWAIT);
+ if (error)
+ goto out;
+ error = bus_dmamem_map(sc->sc_dmat, ds, 1, buflen, &ptr,
+ BUS_DMA_NOWAIT);
+ if (error)
+ goto dmamem_free;
+ error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, ptr, buflen,
+ NULL, BUS_DMA_NOWAIT|BUS_DMA_STREAMING|BUS_DMA_WRITE);
+ if (error)
+ goto dmamem_unmap;
+
+ memset(ptr, 0, buflen);
+ *(uint8_t *)ptr = value;
+
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, buflen,
+ BUS_DMASYNC_PREWRITE);
+ } else {
+ buf[0] = value;
+ ptr = buf;
+ }
+
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.c_data = ptr;
+ cmd.c_datalen = buflen;
+ cmd.c_blklen = SDMMC_SECTOR_SIZE;
+ cmd.c_opcode = SD_WRITE_EXTR_SINGLE;
+ cmd.c_arg = __SHIFTIN((uint32_t)mio, SD_EXTR_MIO) |
+ __SHIFTIN((uint32_t)fno, SD_EXTR_FNO) |
+ __SHIFTIN(addr, SD_EXTR_ADDR) |
+ __SHIFTIN(0, SD_EXTR_LEN);
+ cmd.c_flags = SCF_CMD_ADTC | SCF_RSP_R1;
+ if (ISSET(sc->sc_caps, SMC_CAPS_DMA))
+ cmd.c_dmamap = sc->sc_dmap;
+
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error == 0) {
+ if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+ bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, buflen,
+ BUS_DMASYNC_POSTWRITE);
+ }
+ }
+
+out:
+ if (ptr != NULL) {
+ if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+ bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap);
+dmamem_unmap:
+ bus_dmamem_unmap(sc->sc_dmat, ptr, buflen);
+dmamem_free:
+ bus_dmamem_free(sc->sc_dmat, ds, rseg);
+ }
+ }
+
+ if (!error) {
+ do {
+ memset(&cmd, 0, sizeof(cmd));
+ cmd.c_opcode = MMC_SEND_STATUS;
+ cmd.c_arg = MMC_ARG_RCA(sf->rca);
+ cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_RSP_SPI_R2 |
+ SCF_TOUT_OK;
+ if (poll) {
+ cmd.c_flags |= SCF_POLL;
+ }
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error)
+ break;
+ /* XXX time out */
+ } while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA));
+
+ if (error) {
+ aprint_error_dev(sc->sc_dev,
+ "error waiting for data ready after ext write : %d\n",
+ error);
+ }
+ }
+
+ return error;
+}
+
+static int
sdmmc_set_bus_width(struct sdmmc_function *sf, int width)
{
struct sdmmc_softc *sc = sf->sc;
@@ -2259,15 +2556,33 @@ sdmmc_mem_flush_cache(struct sdmmc_funct
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);
+ if (ISSET(sc->sc_flags, SMF_SD_MODE)) {
+ KASSERT(sf->ext_sd.pef.valid);
+ error = sdmmc_mem_write_extr_single(sc, sf, SD_EXTR_MIO_MEM,
+ sf->ext_sd.pef.fno,
+ sf->ext_sd.pef.start_addr + SD_PEF_CACHE_FLUSH_OFFSET, 1,
+ poll);
+ if (error == 0) {
+ uint8_t data[512];
+
+ error = sdmmc_mem_read_extr_single(sc, sf, SD_EXTR_MIO_MEM,
+ sf->ext_sd.pef.fno, sf->ext_sd.pef.start_addr,
+ sizeof(data), data);
+ if (error == 0 && SD_PEF_CACHE_FLUSH(data) != 0) {
+ device_printf(sc->sc_dev, "cache flush failed\n");
+ }
+ }
+ } else {
+ 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);
+ device_printf(sc->sc_dev, "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.34 src/sys/dev/sdmmc/sdmmcreg.h:1.35
--- src/sys/dev/sdmmc/sdmmcreg.h:1.34 Thu Apr 19 21:50:09 2018
+++ src/sys/dev/sdmmc/sdmmcreg.h Fri Oct 18 11:03:52 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: sdmmcreg.h,v 1.34 2018/04/19 21:50:09 christos Exp $ */
+/* $NetBSD: sdmmcreg.h,v 1.35 2024/10/18 11:03:52 jmcneill Exp $ */
/* $OpenBSD: sdmmcreg.h,v 1.4 2009/01/09 10:55:22 jsg Exp $ */
/*
@@ -63,6 +63,8 @@
#define SD_VOLTAGE_SWITCH 11 /* R1 */
#define SD_ERASE_WR_BLK_START 32 /* R1 */
#define SD_ERASE_WR_BLK_END 33 /* R1 */
+#define SD_READ_EXTR_SINGLE 48 /* R1 */
+#define SD_WRITE_EXTR_SINGLE 49 /* R1 */
/* SD application commands */ /* response type */
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
@@ -71,6 +73,15 @@
#define SD_APP_OP_COND 41 /* R3 */
#define SD_APP_SEND_SCR 51 /* R1 */
+/* SD extended register argument fields */
+#define SD_EXTR_MIO (0x1U << 31)
+#define SD_EXTR_MIO_MEM 0
+#define SD_EXTR_MIO_IO 1
+#define SD_EXTR_FNO (0xfU << 27)
+#define SD_EXTR_MW (1U << 26) /* write only */
+#define SD_EXTR_ADDR (0x1ffff << 9)
+#define SD_EXTR_LEN 0x1ff
+
/* SD erase arguments */
#define SD_ERASE_DISCARD 0x00000001
#define SD_ERASE_FULE 0x00000002
@@ -355,7 +366,9 @@
#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, 9)
+#define SCR_CMD_SUPPORT_CMD48(scr) MMC_RSP_BITS((scr), 34, 1)
#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)
@@ -393,6 +406,10 @@
#define SSR_APP_PERF_CLASS_A1 1
#define SSR_APP_PERF_CLASS_A2 2
#define SSR_PERFORMANCE_ENHANCE(resp) __bitfield((resp), 328, 8)
+#define SSR_PERFORMANCE_ENHANCE_CACHE __BIT(0)
+#define SSR_PERFORMANCE_ENHANCE_HOST_MAINT __BIT(1)
+#define SSR_PERFORMANCE_ENHANCE_CARD_MAINT __BIT(2)
+#define SSR_PERFORMANCE_ENHANCE_COMMAND_QUEUE __BITS(7,3)
#define SSR_DISCARD_SUPPORT(resp) __bitfield((resp), 313, 1)
#define SSR_FULE_SUPPORT(resp) __bitfield((resp), 312, 1)
@@ -406,6 +423,29 @@
#define SD_ACCESS_MODE_SDR104 3
#define SD_ACCESS_MODE_DDR50 4
+/* SD extension data */
+#define SD_GENERAL_INFO_HDR_REVISION(data) le16dec(&(data)[0])
+#define SD_GENERAL_INFO_HDR_LENGTH(data) le16dec(&(data)[2])
+#define SD_GENERAL_INFO_HDR_NUM_EXT(data) ((data)[4])
+#define SD_GENERAL_INFO_EXT_FIRST 16
+#define SD_EXTENSION_INFO_SFC(data, index) le16dec(&(data)[(index) + 0])
+#define SD_EXTENSION_INFO_NEXT(data, index) le16dec(&(data)[(index) + 40])
+#define SD_EXTENSION_INFO_NUM_REG(data, index) ((data)[(index) + 42])
+#define SD_EXTENSION_INFO_REG(data, index, num) le32dec(&(data)[(index) + 44 + (num) * 4])
+
+#define SD_EXTENSION_INFO_REG_FNO(addr) (((addr) >> 18) & 0xf)
+#define SD_EXTENSION_INFO_REG_START_ADDR(addr) ((addr) & 0x1ffff)
+
+/* SD extension standard function codes */
+#define SD_SFC_PEF 0x0002
+
+/* Performance Enhancement Function */
+#define SD_PEF_CACHE_SUPPORT(data) ((data)[4] & 0x01)
+#define SD_PEF_CACHE_ENABLE(data) ((data)[260] & 0x01)
+#define SD_PEF_CACHE_FLUSH(data) ((data)[261] & 0x01)
+#define SD_PEF_CACHE_ENABLE_OFFSET 260
+#define SD_PEF_CACHE_FLUSH_OFFSET 261
+
/* This assumes the response fields are in host byte order in 32-bit units. */
#define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start)-8, (len))
static __inline uint32_t
Index: src/sys/dev/sdmmc/sdmmcvar.h
diff -u src/sys/dev/sdmmc/sdmmcvar.h:1.36 src/sys/dev/sdmmc/sdmmcvar.h:1.37
--- src/sys/dev/sdmmc/sdmmcvar.h:1.36 Sat Mar 13 23:22:44 2021
+++ src/sys/dev/sdmmc/sdmmcvar.h Fri Oct 18 11:03:52 2024
@@ -1,4 +1,4 @@
-/* $NetBSD: sdmmcvar.h,v 1.36 2021/03/13 23:22:44 mlelstv Exp $ */
+/* $NetBSD: sdmmcvar.h,v 1.37 2024/10/18 11:03:52 jmcneill Exp $ */
/* $OpenBSD: sdmmcvar.h,v 1.13 2009/01/09 10:55:22 jsg Exp $ */
/*
@@ -66,9 +66,27 @@ struct sdmmc_cid {
struct sdmmc_scr {
int sd_spec;
+ int sd_spec3;
+ int sd_spec4;
int bus_width;
+ bool support_cmd48;
};
+struct sdmmc_ssr {
+ bool cache; /* cache supported */
+};
+
+struct sdmmc_ext_regset {
+ bool valid;
+ uint8_t fno;
+ uint32_t start_addr;
+};
+
+struct sdmmc_ext_sd {
+ struct sdmmc_ext_regset pef; /* Performance Enhancement */
+};
+
+
typedef uint32_t sdmmc_response[4];
struct sdmmc_softc;
@@ -201,6 +219,8 @@ struct sdmmc_function {
sdmmc_response raw_cid; /* temp. storage for decoding */
uint32_t raw_scr[2];
struct sdmmc_scr scr; /* decoded SCR value */
+ struct sdmmc_ssr ssr; /* decoded SSR value */
+ struct sdmmc_ext_sd ext_sd; /* decoded SD extension value */
void *bbuf; /* bounce buffer */
bus_dmamap_t bbuf_dmap; /* DMA map for bounce buffer */