From 113e5dfcd77cfef4a1719c9a6ba3933c2ef06320 Mon Sep 17 00:00:00 2001 From: Jaehoon Chung Date: Fri, 19 Jul 2013 17:44:49 +0900 Subject: mmc: sdhci: use the SDHCI_QUIRK_USE_WIDE8 for samsung SoC Samsung SoC is supported the WIDE8, even if Controller version is v2.0. So add the SDHCI_QUIRK_USE_WIDE8 for Samsung-SoC. Signed-off-by: Jaehoon Chung Signed-off-by: Kyungmin Park Signed-off-by: Pantelis Antoniou --- drivers/mmc/s5p_sdhci.c | 4 +++- drivers/mmc/sdhci.c | 13 +++++++------ 2 files changed, 10 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/s5p_sdhci.c b/drivers/mmc/s5p_sdhci.c index 7f89403..40ff873 100644 --- a/drivers/mmc/s5p_sdhci.c +++ b/drivers/mmc/s5p_sdhci.c @@ -72,7 +72,7 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width) host->quirks = SDHCI_QUIRK_NO_HISPD_BIT | SDHCI_QUIRK_BROKEN_VOLTAGE | SDHCI_QUIRK_BROKEN_R1B | SDHCI_QUIRK_32BIT_DMA_ADDR | - SDHCI_QUIRK_WAIT_SEND_CMD; + SDHCI_QUIRK_WAIT_SEND_CMD | SDHCI_QUIRK_USE_WIDE8; host->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; host->version = sdhci_readw(host, SDHCI_HOST_VERSION); @@ -81,6 +81,8 @@ int s5p_sdhci_init(u32 regbase, int index, int bus_width) host->index = index; host->host_caps = MMC_MODE_HC; + if (bus_width == 8) + host->host_caps |= MMC_MODE_8BIT; return add_sdhci(host, 52000000, 400000); } diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 4261991..14fe41f 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -254,7 +254,7 @@ static int sdhci_set_clock(struct mmc *mmc, unsigned int clock) if (clock == 0) return 0; - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { /* Version 3.00 divisors must be a multiple of 2. */ if (mmc->f_max <= clock) div = 1; @@ -347,10 +347,11 @@ void sdhci_set_ios(struct mmc *mmc) ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); if (mmc->bus_width == 8) { ctrl &= ~SDHCI_CTRL_4BITBUS; - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if ((SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) || + (host->quirks & SDHCI_QUIRK_USE_WIDE8)) ctrl |= SDHCI_CTRL_8BITBUS; } else { - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) ctrl &= ~SDHCI_CTRL_8BITBUS; if (mmc->bus_width == 4) ctrl |= SDHCI_CTRL_4BITBUS; @@ -437,7 +438,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) if (max_clk) mmc->f_max = max_clk; else { - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) mmc->f_max = (caps & SDHCI_CLOCK_V3_BASE_MASK) >> SDHCI_CLOCK_BASE_SHIFT; else @@ -452,7 +453,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) if (min_clk) mmc->f_min = min_clk; else { - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_300; else mmc->f_min = mmc->f_max / SDHCI_MAX_DIV_SPEC_200; @@ -470,7 +471,7 @@ int add_sdhci(struct sdhci_host *host, u32 max_clk, u32 min_clk) mmc->voltages |= host->voltages; mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT; - if ((host->version & SDHCI_SPEC_VER_MASK) >= SDHCI_SPEC_300) { + if (SDHCI_GET_VERSION(host) >= SDHCI_SPEC_300) { if (caps & SDHCI_CAN_DO_8BIT) mmc->host_caps |= MMC_MODE_8BIT; } -- cgit v1.1 From c30054ba94d58c157c310e064d6ccda398873bcb Mon Sep 17 00:00:00 2001 From: Oleksandr Tyshchenko Date: Tue, 6 Aug 2013 13:50:10 +0300 Subject: mmc: Remove unused variable backup from mmc_send_cmd() Do not call a memset for unused variable backup every time. Remove unused variable from function. Signed-off-by: Oleksandr Tyshchenko Acked-by: Pantelis Antoniou --- drivers/mmc/mmc.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5502675..0b552f5 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -55,11 +55,8 @@ int board_mmc_getcd(struct mmc *mmc)__attribute__((weak, static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { - struct mmc_data backup; int ret; - memset(&backup, 0, sizeof(backup)); - #ifdef CONFIG_MMC_TRACE int i; u8 *ptr; -- cgit v1.1 From 61a6cc27bce36ce2c27a8e4970796caa653c8529 Mon Sep 17 00:00:00 2001 From: Oleksandr Tyshchenko Date: Tue, 6 Aug 2013 13:44:16 +0300 Subject: omap_hsmmc: omap4+/am335x: modify MMC controller internal fsm reset func "mmc_send_cmd: timeout: No status update" error sometimes happens in omap_hsmmc driver func mmc_send_cmd() when the MMC controller card identification and selection sequence is executed for eMMC on OMAP4 boards. It happens due to incorrect execution of CMD line reset procedure for OMAP4. Because CMD(DAT) lines reset procedures are slightly different for OMAP3 and OMAP4(AM335x,OMAP5,DRA7xx). According to OMAP3 TRM: Set SRC(SRD) bit in MMCHS_SYSCTL register to 0x1 and wait until it returns to 0x0. According to OMAP4(AM335x,OMAP5,DRA7xx) TRMs, CMD(DATA) lines reset procedure steps must be as follows: 1. Initiate CMD(DAT) line reset by writing 0x1 to SRC(SRD) bit in MMCHS_SYSCTL register (SD_SYSCTL for AM335x). 2. Poll the SRC(SRD) bit until it is set to 0x1. 3. Wait until the SRC(SRD) bit returns to 0x0 (reset procedure is completed). Unfortunately, at present omap_hsmmc driver has support only for OMAP3. And as result step #2 is missing for OMAP4(AM335x,OMAP5,DRA7xx). This sometimes leads to the fact that the waiting loop which is required in step #3 does not executed, because SRC bit does not set yet (at the moment of checking a condition of a loop execution). And as a result this can cause to timeout error when sending a next command. In the particular case (working with eMMC witch do not respond to some SD specific command) due to incorrect reset sequence after command SD_CMD_SEND_IF_COND which finished with CTO flag within 64 clock cycles, the next command MMC_CMD_APP_CMD leads to a timeout error within 1s. So, extend CMD(DATA) lines reset procedure in func mmc_reset_controller_fsm() by adding the missing step #2 for OMAP4+/AM335x boards. Signed-off-by: Oleksandr Tyshchenko Acked-by: Pantelis Antoniou --- drivers/mmc/omap_hsmmc.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 975b2c5..0e36bf9 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -288,6 +288,30 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit) mmc_reg_out(&mmc_base->sysctl, bit, bit); + /* + * CMD(DAT) lines reset procedures are slightly different + * for OMAP3 and OMAP4(AM335x,OMAP5,DRA7xx). + * According to OMAP3 TRM: + * Set SRC(SRD) bit in MMCHS_SYSCTL register to 0x1 and wait until it + * returns to 0x0. + * According to OMAP4(AM335x,OMAP5,DRA7xx) TRMs, CMD(DATA) lines reset + * procedure steps must be as follows: + * 1. Initiate CMD(DAT) line reset by writing 0x1 to SRC(SRD) bit in + * MMCHS_SYSCTL register (SD_SYSCTL for AM335x). + * 2. Poll the SRC(SRD) bit until it is set to 0x1. + * 3. Wait until the SRC (SRD) bit returns to 0x0 + * (reset procedure is completed). + */ +#if defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ + defined(CONFIG_AM33XX) + if (!(readl(&mmc_base->sysctl) & bit)) { + start = get_timer(0); + while (!(readl(&mmc_base->sysctl) & bit)) { + if (get_timer(0) - start > MAX_RETRY_MS) + return; + } + } +#endif start = get_timer(0); while ((readl(&mmc_base->sysctl) & bit) != 0) { if (get_timer(0) - start > MAX_RETRY_MS) { -- cgit v1.1 From 152ba36362377bd1a7c99e7e50cf53b5a8851e5c Mon Sep 17 00:00:00 2001 From: Lubomir Popov Date: Wed, 14 Aug 2013 18:59:18 +0300 Subject: ARM: OMAP: Enable 8-bit eMMC access for OMAP4/5/DRA7xx Enable 8-bit host capability for HSMMC2 and/or HSMMC3. CONFIG_HSMMC2_8BIT (for OMAP4/5/DRA7xx) and/or CONFIG_HSMMC3_8BIT (for DRA7xx only) must be defined in the board header if an 8-bit eMMC device is connected to the corresponding port. Fix the "No status update" error that appeared for eMMC devices by inserting a 20 us delay between writing arguments and command. This solution has been proposed by Michael Cashwell . A minor cosmetic fix in a comment as well. Signed-off-by: Lubomir Popov --- drivers/mmc/omap_hsmmc.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c index 0e36bf9..d3a8b53 100644 --- a/drivers/mmc/omap_hsmmc.c +++ b/drivers/mmc/omap_hsmmc.c @@ -400,6 +400,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, } writel(cmd->cmdarg, &mmc_base->arg); + udelay(20); /* To fix "No status update" error on eMMC */ writel((cmd->cmdidx << 24) | flags, &mmc_base->cmd); start = get_timer(0); @@ -504,7 +505,7 @@ static int mmc_write_data(struct hsmmc *mmc_base, const char *buf, unsigned int count; /* - * Start Polled Read + * Start Polled Write */ count = (size > MMCSD_SECTOR_SIZE) ? MMCSD_SECTOR_SIZE : size; count /= 4; @@ -610,6 +611,8 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, { struct mmc *mmc = &hsmmc_dev[dev_index]; struct omap_hsmmc_data *priv_data = &hsmmc_dev_data[dev_index]; + uint host_caps_val = MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | + MMC_MODE_HC; sprintf(mmc->name, "OMAP SD/MMC"); mmc->send_cmd = mmc_send_cmd; @@ -624,11 +627,20 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, #ifdef OMAP_HSMMC2_BASE case 1: priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC2_BASE; +#if (defined(CONFIG_OMAP44XX) || defined(CONFIG_OMAP54XX) || \ + defined(CONFIG_DRA7XX)) && defined(CONFIG_HSMMC2_8BIT) + /* Enable 8-bit interface for eMMC on OMAP4/5 or DRA7XX */ + host_caps_val |= MMC_MODE_8BIT; +#endif break; #endif #ifdef OMAP_HSMMC3_BASE case 2: priv_data->base_addr = (struct hsmmc *)OMAP_HSMMC3_BASE; +#if defined(CONFIG_DRA7XX) && defined(CONFIG_HSMMC3_8BIT) + /* Enable 8-bit interface for eMMC on DRA7XX */ + host_caps_val |= MMC_MODE_8BIT; +#endif break; #endif default: @@ -644,8 +656,7 @@ int omap_mmc_init(int dev_index, uint host_caps_mask, uint f_max, int cd_gpio, mmc->getwp = omap_mmc_getwp; mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195; - mmc->host_caps = (MMC_MODE_4BIT | MMC_MODE_HS_52MHz | MMC_MODE_HS | - MMC_MODE_HC) & ~host_caps_mask; + mmc->host_caps = host_caps_val & ~host_caps_mask; mmc->f_min = 400000; -- cgit v1.1 From 561968266431e16790e0d71086341f28e6930800 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Wed, 4 Sep 2013 16:12:25 +0100 Subject: mmc: don't call *printf or puts when SPL & !CONFIG_SPL_LIBCOMMON_SUPPORT If we don't have CONFIG_SPL_LIBCOMMON_SUPPORT defined then stdio & *printf functions are unavailable & calling them will cause a link failure. Signed-off-by: Paul Burton --- drivers/mmc/mmc.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 0b552f5..5550d46 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -132,8 +132,10 @@ static int mmc_send_status(struct mmc *mmc, int timeout) MMC_STATE_PRG) break; else if (cmd.response[0] & MMC_STATUS_MASK) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Status Error: 0x%08X\n", cmd.response[0]); +#endif return COMM_ERR; } } else if (--retries < 0) @@ -148,7 +150,9 @@ static int mmc_send_status(struct mmc *mmc, int timeout) printf("CURR STATE:%d\n", status); #endif if (timeout <= 0) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Timeout waiting card ready\n"); +#endif return TIMEOUT; } @@ -178,7 +182,9 @@ struct mmc *find_mmc_device(int dev_num) return m; } +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC Device %d not found\n", dev_num); +#endif return NULL; } @@ -230,7 +236,9 @@ static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) return 0; err_out: +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) puts("mmc erase failed\n"); +#endif return err; } @@ -245,6 +253,7 @@ mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) if (!mmc) return -1; +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) printf("\n\nCaution! Your devices Erase group is 0x%x\n" "The erase range would be change to " @@ -252,6 +261,7 @@ mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), ((start + blkcnt + mmc->erase_grp_size) & ~(mmc->erase_grp_size - 1)) - 1); +#endif while (blk < blkcnt) { blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? @@ -278,8 +288,10 @@ mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void*sr int timeout = 1000; if ((start + blkcnt) > mmc->block_dev.lba) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", start + blkcnt, mmc->block_dev.lba); +#endif return 0; } @@ -303,7 +315,9 @@ mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void*sr data.flags = MMC_DATA_WRITE; if (mmc_send_cmd(mmc, &cmd, &data)) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("mmc write failed\n"); +#endif return 0; } @@ -315,7 +329,9 @@ mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void*sr cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; if (mmc_send_cmd(mmc, &cmd, NULL)) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("mmc fail to send stop cmd\n"); +#endif return 0; } } @@ -382,7 +398,9 @@ static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, cmd.cmdarg = 0; cmd.resp_type = MMC_RSP_R1b; if (mmc_send_cmd(mmc, &cmd, NULL)) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("mmc fail to send stop cmd\n"); +#endif return 0; } } @@ -402,8 +420,10 @@ static ulong mmc_bread(int dev_num, lbaint_t start, lbaint_t blkcnt, void *dst) return 0; if ((start + blkcnt) > mmc->block_dev.lba) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", start + blkcnt, mmc->block_dev.lba); +#endif return 0; } @@ -1265,6 +1285,7 @@ static int mmc_startup(struct mmc *mmc) mmc->block_dev.blksz = mmc->read_bl_len; mmc->block_dev.log2blksz = LOG2(mmc->block_dev.blksz); mmc->block_dev.lba = lldiv(mmc->capacity, mmc->read_bl_len); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) sprintf(mmc->block_dev.vendor, "Man %06x Snr %04x%04x", mmc->cid[0] >> 24, (mmc->cid[2] & 0xffff), (mmc->cid[3] >> 16) & 0xffff); @@ -1274,6 +1295,11 @@ static int mmc_startup(struct mmc *mmc) (mmc->cid[2] >> 24) & 0xff); sprintf(mmc->block_dev.revision, "%d.%d", (mmc->cid[2] >> 20) & 0xf, (mmc->cid[2] >> 16) & 0xf); +#else + mmc->block_dev.vendor[0] = 0; + mmc->block_dev.product[0] = 0; + mmc->block_dev.revision[0] = 0; +#endif #if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBDISK_SUPPORT) init_part(&mmc->block_dev); #endif @@ -1340,7 +1366,9 @@ int mmc_start_init(struct mmc *mmc) if (mmc_getcd(mmc) == 0) { mmc->has_init = 0; +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("MMC: no card present\n"); +#endif return NO_CARD_ERR; } @@ -1375,7 +1403,9 @@ int mmc_start_init(struct mmc *mmc) err = mmc_send_op_cond(mmc); if (err && err != IN_PROGRESS) { +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) printf("Card did not respond to voltage select!\n"); +#endif return UNUSABLE_ERR; } } @@ -1431,6 +1461,8 @@ static int __def_mmc_init(bd_t *bis) int cpu_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); int board_mmc_init(bd_t *bis) __attribute__((weak, alias("__def_mmc_init"))); +#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) + void print_mmc_devices(char separator) { struct mmc *m; @@ -1448,6 +1480,10 @@ void print_mmc_devices(char separator) printf("\n"); } +#else +void print_mmc_devices(char separator) { } +#endif + int get_mmc_num(void) { return cur_dev_num; -- cgit v1.1 From da61fa5f42133593be51764b55a905330eae5063 Mon Sep 17 00:00:00 2001 From: Paul Burton Date: Mon, 9 Sep 2013 15:30:26 +0100 Subject: mmc: don't support write & erase for SPL builds For SPL builds this is just dead code since we'll only need to read. Eliminating it results in a significant size reduction for the SPL binary, which may be critical for certain platforms where the binary size is highly constrained. Signed-off-by: Paul Burton Acked-by: Pantelis Antoniou --- drivers/mmc/Makefile | 2 + drivers/mmc/mmc.c | 186 +--------------------------------------------- drivers/mmc/mmc_private.h | 45 +++++++++++ drivers/mmc/mmc_write.c | 179 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 230 insertions(+), 182 deletions(-) create mode 100644 drivers/mmc/mmc_private.h create mode 100644 drivers/mmc/mmc_write.c (limited to 'drivers') diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index bedf833..06280d1 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -34,6 +34,8 @@ COBJS-$(CONFIG_EXYNOS_DWMMC) += exynos_dw_mmc.o COBJS-$(CONFIG_ZYNQ_SDHCI) += zynq_sdhci.o ifdef CONFIG_SPL_BUILD COBJS-$(CONFIG_SPL_MMC_BOOT) += fsl_esdhc_spl.o +else +COBJS-$(CONFIG_GENERIC_MMC) += mmc_write.o endif COBJS := $(COBJS-y) diff --git a/drivers/mmc/mmc.c b/drivers/mmc/mmc.c index 5550d46..84dae4d 100644 --- a/drivers/mmc/mmc.c +++ b/drivers/mmc/mmc.c @@ -15,6 +15,7 @@ #include #include #include +#include "mmc_private.h" /* Set block count limit because of 16 bit register limit on some hardware*/ #ifndef CONFIG_SYS_MMC_MAX_BLK_COUNT @@ -52,8 +53,7 @@ int __board_mmc_getcd(struct mmc *mmc) { int board_mmc_getcd(struct mmc *mmc)__attribute__((weak, alias("__board_mmc_getcd"))); -static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, - struct mmc_data *data) +int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { int ret; @@ -111,7 +111,7 @@ static int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, return ret; } -static int mmc_send_status(struct mmc *mmc, int timeout) +int mmc_send_status(struct mmc *mmc, int timeout) { struct mmc_cmd cmd; int err, retries = 5; @@ -159,7 +159,7 @@ static int mmc_send_status(struct mmc *mmc, int timeout) return 0; } -static int mmc_set_blocklen(struct mmc *mmc, int len) +int mmc_set_blocklen(struct mmc *mmc, int len) { struct mmc_cmd cmd; @@ -189,184 +189,6 @@ struct mmc *find_mmc_device(int dev_num) return NULL; } -static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) -{ - struct mmc_cmd cmd; - ulong end; - int err, start_cmd, end_cmd; - - if (mmc->high_capacity) - end = start + blkcnt - 1; - else { - end = (start + blkcnt - 1) * mmc->write_bl_len; - start *= mmc->write_bl_len; - } - - if (IS_SD(mmc)) { - start_cmd = SD_CMD_ERASE_WR_BLK_START; - end_cmd = SD_CMD_ERASE_WR_BLK_END; - } else { - start_cmd = MMC_CMD_ERASE_GROUP_START; - end_cmd = MMC_CMD_ERASE_GROUP_END; - } - - cmd.cmdidx = start_cmd; - cmd.cmdarg = start; - cmd.resp_type = MMC_RSP_R1; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - goto err_out; - - cmd.cmdidx = end_cmd; - cmd.cmdarg = end; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - goto err_out; - - cmd.cmdidx = MMC_CMD_ERASE; - cmd.cmdarg = SECURE_ERASE; - cmd.resp_type = MMC_RSP_R1b; - - err = mmc_send_cmd(mmc, &cmd, NULL); - if (err) - goto err_out; - - return 0; - -err_out: -#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - puts("mmc erase failed\n"); -#endif - return err; -} - -static unsigned long -mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) -{ - int err = 0; - struct mmc *mmc = find_mmc_device(dev_num); - lbaint_t blk = 0, blk_r = 0; - int timeout = 1000; - - if (!mmc) - return -1; - -#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) - printf("\n\nCaution! Your devices Erase group is 0x%x\n" - "The erase range would be change to " - "0x" LBAF "~0x" LBAF "\n\n", - mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), - ((start + blkcnt + mmc->erase_grp_size) - & ~(mmc->erase_grp_size - 1)) - 1); -#endif - - while (blk < blkcnt) { - blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? - mmc->erase_grp_size : (blkcnt - blk); - err = mmc_erase_t(mmc, start + blk, blk_r); - if (err) - break; - - blk += blk_r; - - /* Waiting for the ready status */ - if (mmc_send_status(mmc, timeout)) - return 0; - } - - return blk; -} - -static ulong -mmc_write_blocks(struct mmc *mmc, lbaint_t start, lbaint_t blkcnt, const void*src) -{ - struct mmc_cmd cmd; - struct mmc_data data; - int timeout = 1000; - - if ((start + blkcnt) > mmc->block_dev.lba) { -#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", - start + blkcnt, mmc->block_dev.lba); -#endif - return 0; - } - - if (blkcnt == 0) - return 0; - else if (blkcnt == 1) - cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; - else - cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; - - if (mmc->high_capacity) - cmd.cmdarg = start; - else - cmd.cmdarg = start * mmc->write_bl_len; - - cmd.resp_type = MMC_RSP_R1; - - data.src = src; - data.blocks = blkcnt; - data.blocksize = mmc->write_bl_len; - data.flags = MMC_DATA_WRITE; - - if (mmc_send_cmd(mmc, &cmd, &data)) { -#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("mmc write failed\n"); -#endif - return 0; - } - - /* SPI multiblock writes terminate using a special - * token, not a STOP_TRANSMISSION request. - */ - if (!mmc_host_is_spi(mmc) && blkcnt > 1) { - cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; - cmd.cmdarg = 0; - cmd.resp_type = MMC_RSP_R1b; - if (mmc_send_cmd(mmc, &cmd, NULL)) { -#if !defined(CONFIG_SPL_BUILD) || defined(CONFIG_SPL_LIBCOMMON_SUPPORT) - printf("mmc fail to send stop cmd\n"); -#endif - return 0; - } - } - - /* Waiting for the ready status */ - if (mmc_send_status(mmc, timeout)) - return 0; - - return blkcnt; -} - -static ulong -mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void*src) -{ - lbaint_t cur, blocks_todo = blkcnt; - - struct mmc *mmc = find_mmc_device(dev_num); - if (!mmc) - return 0; - - if (mmc_set_blocklen(mmc, mmc->write_bl_len)) - return 0; - - do { - cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo; - if(mmc_write_blocks(mmc, start, cur, src) != cur) - return 0; - blocks_todo -= cur; - start += cur; - src += cur * mmc->write_bl_len; - } while (blocks_todo > 0); - - return blkcnt; -} - static int mmc_read_blocks(struct mmc *mmc, void *dst, lbaint_t start, lbaint_t blkcnt) { diff --git a/drivers/mmc/mmc_private.h b/drivers/mmc/mmc_private.h new file mode 100644 index 0000000..16dcf9f --- /dev/null +++ b/drivers/mmc/mmc_private.h @@ -0,0 +1,45 @@ +/* + * Copyright 2008,2010 Freescale Semiconductor, Inc + * Andy Fleming + * + * Based (loosely) on the Linux code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef _MMC_PRIVATE_H_ +#define _MMC_PRIVATE_H_ + +#include + +extern int mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data); +extern int mmc_send_status(struct mmc *mmc, int timeout); +extern int mmc_set_blocklen(struct mmc *mmc, int len); + +#ifndef CONFIG_SPL_BUILD + +extern unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt); + +extern ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, + const void *src); + +#else /* CONFIG_SPL_BUILD */ + +/* SPL will never write or erase, declare dummies to reduce code size. */ + +static inline unsigned long mmc_berase(int dev_num, lbaint_t start, + lbaint_t blkcnt) +{ + return 0; +} + +static inline ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, + const void *src) +{ + return 0; +} + +#endif /* CONFIG_SPL_BUILD */ + +#endif /* _MMC_PRIVATE_H_ */ diff --git a/drivers/mmc/mmc_write.c b/drivers/mmc/mmc_write.c new file mode 100644 index 0000000..aa2fdef --- /dev/null +++ b/drivers/mmc/mmc_write.c @@ -0,0 +1,179 @@ +/* + * Copyright 2008, Freescale Semiconductor, Inc + * Andy Fleming + * + * Based vaguely on the Linux code + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include +#include +#include +#include "mmc_private.h" + +static ulong mmc_erase_t(struct mmc *mmc, ulong start, lbaint_t blkcnt) +{ + struct mmc_cmd cmd; + ulong end; + int err, start_cmd, end_cmd; + + if (mmc->high_capacity) { + end = start + blkcnt - 1; + } else { + end = (start + blkcnt - 1) * mmc->write_bl_len; + start *= mmc->write_bl_len; + } + + if (IS_SD(mmc)) { + start_cmd = SD_CMD_ERASE_WR_BLK_START; + end_cmd = SD_CMD_ERASE_WR_BLK_END; + } else { + start_cmd = MMC_CMD_ERASE_GROUP_START; + end_cmd = MMC_CMD_ERASE_GROUP_END; + } + + cmd.cmdidx = start_cmd; + cmd.cmdarg = start; + cmd.resp_type = MMC_RSP_R1; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + cmd.cmdidx = end_cmd; + cmd.cmdarg = end; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + cmd.cmdidx = MMC_CMD_ERASE; + cmd.cmdarg = SECURE_ERASE; + cmd.resp_type = MMC_RSP_R1b; + + err = mmc_send_cmd(mmc, &cmd, NULL); + if (err) + goto err_out; + + return 0; + +err_out: + puts("mmc erase failed\n"); + return err; +} + +unsigned long mmc_berase(int dev_num, lbaint_t start, lbaint_t blkcnt) +{ + int err = 0; + struct mmc *mmc = find_mmc_device(dev_num); + lbaint_t blk = 0, blk_r = 0; + int timeout = 1000; + + if (!mmc) + return -1; + + if ((start % mmc->erase_grp_size) || (blkcnt % mmc->erase_grp_size)) + printf("\n\nCaution! Your devices Erase group is 0x%x\n" + "The erase range would be change to " + "0x" LBAF "~0x" LBAF "\n\n", + mmc->erase_grp_size, start & ~(mmc->erase_grp_size - 1), + ((start + blkcnt + mmc->erase_grp_size) + & ~(mmc->erase_grp_size - 1)) - 1); + + while (blk < blkcnt) { + blk_r = ((blkcnt - blk) > mmc->erase_grp_size) ? + mmc->erase_grp_size : (blkcnt - blk); + err = mmc_erase_t(mmc, start + blk, blk_r); + if (err) + break; + + blk += blk_r; + + /* Waiting for the ready status */ + if (mmc_send_status(mmc, timeout)) + return 0; + } + + return blk; +} + +static ulong mmc_write_blocks(struct mmc *mmc, lbaint_t start, + lbaint_t blkcnt, const void *src) +{ + struct mmc_cmd cmd; + struct mmc_data data; + int timeout = 1000; + + if ((start + blkcnt) > mmc->block_dev.lba) { + printf("MMC: block number 0x" LBAF " exceeds max(0x" LBAF ")\n", + start + blkcnt, mmc->block_dev.lba); + return 0; + } + + if (blkcnt == 0) + return 0; + else if (blkcnt == 1) + cmd.cmdidx = MMC_CMD_WRITE_SINGLE_BLOCK; + else + cmd.cmdidx = MMC_CMD_WRITE_MULTIPLE_BLOCK; + + if (mmc->high_capacity) + cmd.cmdarg = start; + else + cmd.cmdarg = start * mmc->write_bl_len; + + cmd.resp_type = MMC_RSP_R1; + + data.src = src; + data.blocks = blkcnt; + data.blocksize = mmc->write_bl_len; + data.flags = MMC_DATA_WRITE; + + if (mmc_send_cmd(mmc, &cmd, &data)) { + printf("mmc write failed\n"); + return 0; + } + + /* SPI multiblock writes terminate using a special + * token, not a STOP_TRANSMISSION request. + */ + if (!mmc_host_is_spi(mmc) && blkcnt > 1) { + cmd.cmdidx = MMC_CMD_STOP_TRANSMISSION; + cmd.cmdarg = 0; + cmd.resp_type = MMC_RSP_R1b; + if (mmc_send_cmd(mmc, &cmd, NULL)) { + printf("mmc fail to send stop cmd\n"); + return 0; + } + } + + /* Waiting for the ready status */ + if (mmc_send_status(mmc, timeout)) + return 0; + + return blkcnt; +} + +ulong mmc_bwrite(int dev_num, lbaint_t start, lbaint_t blkcnt, const void *src) +{ + lbaint_t cur, blocks_todo = blkcnt; + + struct mmc *mmc = find_mmc_device(dev_num); + if (!mmc) + return 0; + + if (mmc_set_blocklen(mmc, mmc->write_bl_len)) + return 0; + + do { + cur = (blocks_todo > mmc->b_max) ? mmc->b_max : blocks_todo; + if (mmc_write_blocks(mmc, start, cur, src) != cur) + return 0; + blocks_todo -= cur; + start += cur; + src += cur * mmc->write_bl_len; + } while (blocks_todo > 0); + + return blkcnt; +} -- cgit v1.1 From 2136d22630ef5acf36b90c45b6e44e964547b618 Mon Sep 17 00:00:00 2001 From: Mischa Jonker Date: Fri, 26 Jul 2013 14:08:14 +0200 Subject: mmc/dw_mmc: Fix DMA descriptor corruption In dwmci_prepare_data, the descriptors are allocated for DMA transfer. These are allocated using the ALLOC_CACHE_ALIGN_BUFFER. This macro uses the stack to allocate these descriptors. This becomes a problem if the DMA transfer continues after the processor leaves the function in which the descriptors were allocated. Therefore, I have moved the allocated of the buffers up one level, to dwmci_send_cmd(). The DMA transfer should be complete when leaving this function. Signed-off-by: Mischa Jonker Cc: Alexey Brodkin Cc: Jaehoon Chung Cc: Andy Fleming Acked-by: Jaehoon Chung Acked-by: Pantelis Antoniou --- drivers/mmc/dw_mmc.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index a82ee17..796a811 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -41,12 +41,11 @@ static void dwmci_set_idma_desc(struct dwmci_idmac *idmac, } static void dwmci_prepare_data(struct dwmci_host *host, - struct mmc_data *data) + struct mmc_data *data, struct dwmci_idmac *cur_idmac) { unsigned long ctrl; unsigned int i = 0, flags, cnt, blk_cnt; ulong data_start, data_end, start_addr; - ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, data->blocks); blk_cnt = data->blocks; @@ -111,6 +110,8 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct dwmci_host *host = (struct dwmci_host *)mmc->priv; + ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, + data ? data->blocks : 0); int flags = 0, i; unsigned int timeout = 100000; u32 retry = 10000; @@ -127,7 +128,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, dwmci_writel(host, DWMCI_RINTSTS, DWMCI_INTMSK_ALL); if (data) - dwmci_prepare_data(host, data); + dwmci_prepare_data(host, data, cur_idmac); dwmci_writel(host, DWMCI_CMDARG, cmd->cmdarg); -- cgit v1.1 From 21bd5761a6e945eabaf245668868b58c2772fa21 Mon Sep 17 00:00:00 2001 From: Mischa Jonker Date: Fri, 26 Jul 2013 16:18:40 +0200 Subject: mmc/dw_mmc: Allocate the correct amount of descriptors This fixes two issues: * a descriptor was allocated for every block, while a descriptor can take 8 blocks * there was an off-by-one error in the descriptor preparation: there were two last descriptors, one with length==0 Signed-off-by: Mischa Jonker Cc: Alexey Brodkin Cc: Jaehoon Chung Cc: Andy Fleming --- drivers/mmc/dw_mmc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 796a811..9a803a0 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -72,7 +72,7 @@ static void dwmci_prepare_data(struct dwmci_host *host, dwmci_set_idma_desc(cur_idmac, flags, cnt, start_addr + (i * PAGE_SIZE)); - if(blk_cnt < 8) + if (blk_cnt <= 8) break; blk_cnt -= 8; cur_idmac++; @@ -111,7 +111,7 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, { struct dwmci_host *host = (struct dwmci_host *)mmc->priv; ALLOC_CACHE_ALIGN_BUFFER(struct dwmci_idmac, cur_idmac, - data ? data->blocks : 0); + data ? DIV_ROUND_UP(data->blocks, 8) : 0); int flags = 0, i; unsigned int timeout = 100000; u32 retry = 10000; -- cgit v1.1 From 2c011847c129491084a19c753a039a3441b7dce4 Mon Sep 17 00:00:00 2001 From: "Juhyun \\(Justin\\) Oh" Date: Fri, 13 Sep 2013 18:06:00 +0000 Subject: Fix wrong sdhci host control register read and write The patch fixes the improper read and write of sdhci host control register for sdma transfer. The problem comes when reading and writing 1 byte long host control register with the sdhci_readl() and sdhci_writel(). The misuse of these functions overwrite the value of the next registers which are in 4 bytes boundary. This patch replaces four byte register read/write functions with one byte read/write ones. Beside, it eliminates unnecessary bit operation. i.e. or-ing zero against a variable. Signed-off-by: Juhyun (Justin) Oh --- drivers/mmc/sdhci.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index 14fe41f..dfb2eee 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -68,10 +68,9 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, unsigned int stat, rdy, mask, timeout, block = 0; #ifdef CONFIG_MMC_SDMA unsigned char ctrl; - ctrl = sdhci_readl(host, SDHCI_HOST_CONTROL); + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; - ctrl |= SDHCI_CTRL_SDMA; - sdhci_writel(host, ctrl, SDHCI_HOST_CONTROL); + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); #endif timeout = 1000000; -- cgit v1.1