From 752fce88196c1083b7faa86fb4c12007445fdf84 Mon Sep 17 00:00:00 2001 From: matt Date: Fri, 20 Jul 2012 02:04:13 +0000 Subject: [PATCH] Add use of watermark register when PIO to an ESDHC. After every kill or drain of watermask words, pause a bit to give time for the fifo to recover. Always the command response in BE byteorder. Rewrite __bitfield to deal with this. --- sys/dev/sdmmc/sdhc.c | 50 ++++++++++++++++++++++++++------------- sys/dev/sdmmc/sdhcreg.h | 7 +++++- sys/dev/sdmmc/sdmmc_mem.c | 8 +++---- sys/dev/sdmmc/sdmmcreg.h | 42 +++++++++++++------------------- 4 files changed, 61 insertions(+), 46 deletions(-) diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c index 467ecc27cef0..cfff78d1036f 100644 --- a/sys/dev/sdmmc/sdhc.c +++ b/sys/dev/sdmmc/sdhc.c @@ -1,4 +1,4 @@ -/* $NetBSD: sdhc.c,v 1.22 2012/07/17 21:35:26 matt Exp $ */ +/* $NetBSD: sdhc.c,v 1.23 2012/07/20 02:04:13 matt Exp $ */ /* $OpenBSD: sdhc.c,v 1.25 2009/01/13 19:44:20 grange Exp $ */ /* @@ -23,7 +23,7 @@ */ #include -__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.22 2012/07/17 21:35:26 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdhc.c,v 1.23 2012/07/20 02:04:13 matt Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -1012,23 +1012,15 @@ sdhc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) */ mutex_enter(&hp->host_mtx); if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { - uint32_t *p = cmd->c_resp; - int i; - - for (i = 0; i < 4; i++) { -#ifdef __BUS_SPACE_HAS_STREAM_METHODS - *p++ = bus_space_read_stream_4(hp->iot, hp->ioh, - SDHC_RESPONSE + i * 4); -#else - *p++ = htole32(bus_space_read_4(hp->iot, hp->ioh, - SDHC_RESPONSE + i * 4)); -#endif - if (!ISSET(cmd->c_flags, SCF_RSP_136)) - break; + cmd->c_resp[0] = HREAD4(hp, SDHC_RESPONSE + 0); + if (ISSET(cmd->c_flags, SCF_RSP_136)) { + cmd->c_resp[1] = HREAD4(hp, SDHC_RESPONSE + 4); + cmd->c_resp[2] = HREAD4(hp, SDHC_RESPONSE + 8); + cmd->c_resp[3] = HREAD4(hp, SDHC_RESPONSE + 12); } } mutex_exit(&hp->host_mtx); - DPRINTF(1,("%s: resp = %08x\n", HDEVNAME(hp), cmd->c_resp[0])); + DPRINTF(1,("%s: resp = %08x\n", HDEVNAME(hp), be32toh(cmd->c_resp[0]))); /* * If the command has data to transfer in any direction, @@ -1403,15 +1395,30 @@ esdhc_read_data_pio(struct sdhc_host *hp, uint8_t *data, u_int datalen) uint16_t status = HREAD2(hp, SDHC_NINTR_STATUS); uint32_t v; + const size_t watermark = (HREAD4(hp, SDHC_WATERMARK_LEVEL) >> SDHC_WATERMARK_READ_SHIFT) & SDHC_WATERMARK_READ_MASK; + size_t count = 0; + while (datalen > 3 && !ISSET(status, SDHC_TRANSFER_COMPLETE)) { + if (count == 0) { + /* + * If we've drained "watermark" words, we need to wait + * a little bit so the read FIFO can refill. + */ + sdmmc_delay(10); + count = watermark; + } v = HREAD4(hp, SDHC_DATA); v = le32toh(v); *(uint32_t *)data = v; data += 4; datalen -= 4; status = HREAD2(hp, SDHC_NINTR_STATUS); + count--; } if (datalen > 0 && !ISSET(status, SDHC_TRANSFER_COMPLETE)) { + if (count == 0) { + sdmmc_delay(10); + } v = HREAD4(hp, SDHC_DATA); v = le32toh(v); do { @@ -1427,15 +1434,26 @@ esdhc_write_data_pio(struct sdhc_host *hp, uint8_t *data, u_int datalen) uint16_t status = HREAD2(hp, SDHC_NINTR_STATUS); uint32_t v; + const size_t watermark = (HREAD4(hp, SDHC_WATERMARK_LEVEL) >> SDHC_WATERMARK_WRITE_SHIFT) & SDHC_WATERMARK_WRITE_MASK; + size_t count = watermark; + while (datalen > 3 && !ISSET(status, SDHC_TRANSFER_COMPLETE)) { + if (count == 0) { + sdmmc_delay(10); + count = watermark; + } v = *(uint32_t *)data; v = htole32(v); HWRITE4(hp, SDHC_DATA, v); data += 4; datalen -= 4; status = HREAD2(hp, SDHC_NINTR_STATUS); + count--; } if (datalen > 0 && !ISSET(status, SDHC_TRANSFER_COMPLETE)) { + if (count == 0) { + sdmmc_delay(10); + } v = *(uint32_t *)data; v = htole32(v); HWRITE4(hp, SDHC_DATA, v); diff --git a/sys/dev/sdmmc/sdhcreg.h b/sys/dev/sdmmc/sdhcreg.h index 34ac29f776cd..f9b54e529f48 100644 --- a/sys/dev/sdmmc/sdhcreg.h +++ b/sys/dev/sdmmc/sdhcreg.h @@ -1,4 +1,4 @@ -/* $NetBSD: sdhcreg.h,v 1.7 2012/07/12 16:34:30 jakllsch Exp $ */ +/* $NetBSD: sdhcreg.h,v 1.8 2012/07/20 02:04:13 matt Exp $ */ /* $OpenBSD: sdhcreg.h,v 1.4 2006/07/30 17:20:40 fgsch Exp $ */ /* @@ -154,6 +154,11 @@ #define SDHC_TIMEOUT_FREQ_UNIT (1<<7) /* 0=KHz, 1=MHz */ #define SDHC_TIMEOUT_FREQ_SHIFT 0 #define SDHC_TIMEOUT_FREQ_MASK 0x1f +#define SDHC_WATERMARK_LEVEL 0x44 /* ESDHC */ +#define SDHC_WATERMARK_WRITE_SHIFT 16 +#define SDHC_WATERMARK_WRITE_MASK 0xff +#define SDHC_WATERMARK_READ_SHIFT 0 +#define SDHC_WATERMARK_READ_MASK 0xff #define SDHC_MAX_CAPABILITIES 0x48 #define SDHC_HOST_VER 0xFC #define SDHC_VVN_MASK 0x0f diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c index 42b5e88a167b..98de19c4610b 100644 --- a/sys/dev/sdmmc/sdmmc_mem.c +++ b/sys/dev/sdmmc/sdmmc_mem.c @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc_mem.c,v 1.20 2012/02/01 22:34:43 matt Exp $ */ +/* $NetBSD: sdmmc_mem.c,v 1.21 2012/07/20 02:04:13 matt 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 -__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.20 2012/02/01 22:34:43 matt Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.21 2012/07/20 02:04:13 matt Exp $"); #ifdef _KERNEL_OPT #include "opt_sdmmc.h" @@ -934,8 +934,8 @@ sdmmc_mem_decode_scr(struct sdmmc_softc *sc, struct sdmmc_function *sf) resp[1] = be32toh(sf->raw_scr[0]); // MSW resp[0] |= (resp[1] & 0xff) << 24; resp[1] >>= 8; - resp[0] = htole32(resp[0]); - resp[1] = htole32(resp[1]); + resp[0] = htobe32(resp[0]); + resp[1] = htobe32(resp[1]); ver = SCR_STRUCTURE(resp); sf->scr.sd_spec = SCR_SD_SPEC(resp); diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h index a293af8fd856..bda98fee741c 100644 --- a/sys/dev/sdmmc/sdmmcreg.h +++ b/sys/dev/sdmmc/sdmmcreg.h @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmcreg.h,v 1.9 2012/07/12 16:03:13 jakllsch Exp $ */ +/* $NetBSD: sdmmcreg.h,v 1.10 2012/07/20 02:04:13 matt Exp $ */ /* $OpenBSD: sdmmcreg.h,v 1.4 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -309,43 +309,35 @@ #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_RESERVED(scr) MMC_RSP_BITS((scr), 32, 16) +#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) \ be16toh(__bitfield((uint32_t *)(status), (7 - (group)) << 4, 16)) -/* Might be slow, but it should work on big and little endian systems. */ +/* This assumes the response fields are in big endian order. */ #define MMC_RSP_BITS(resp, start, len) __bitfield((resp), (start)-8, (len)) -static inline int -__bitfield(uint32_t *src, int start, int len) +static inline uint32_t +__bitfield(const uint32_t *src, size_t start, size_t len) { - uint8_t *sp; - uint32_t dst, mask; - int shift, bs, bc; - - if (start < 0 || len < 0 || len > 32) + if (start + len > 128 || len == 0 || len > 32) return 0; - dst = 0; - mask = len % 32 ? UINT_MAX >> (32 - (len % 32)) : UINT_MAX; - shift = 0; + src += start / 32; + start %= 32; - while (len > 0) { - sp = (uint8_t *)src + start / 8; - bs = start % 8; - bc = 8 - bs; - if (bc > len) - bc = len; - dst |= (*sp >> bs) << shift; - shift += bc; - start += bc; - len -= bc; + uint32_t dst = be32toh(src[0]) >> start; + + if (__predict_false((start + len - 1) / 32 != start / 32)) { + dst |= be32toh(src[1]) << (32 - start); } - dst &= mask; - return (int)dst; + return dst & (__BIT(len) - 1); } #endif /* _SDMMCREG_H_ */