X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fmmc%2Ffsl_esdhc.c;h=a368fe60db4a285bb5b43a8ec16069ab8e436d24;hb=f12d4cb48a1040c25a55148d0eedee1a896364e6;hp=0ba45cd03457ad5107e9c098d06f5117fb6879e3;hpb=156feb90d200f186cdfd856d7f6f1878bb1bec1e;p=u-boot diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 0ba45cd034..a368fe60db 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -1,5 +1,5 @@ /* - * Copyright 2007, Freescale Semiconductor, Inc + * Copyright 2007,2010 Freescale Semiconductor, Inc * Andy Fleming * * Based vaguely on the pxa mmc code: @@ -28,14 +28,15 @@ #include #include #include +#include #include #include #include #include #include +#include #include - DECLARE_GLOBAL_DATA_PTR; struct fsl_esdhc { @@ -71,8 +72,10 @@ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) uint xfertyp = 0; if (data) { - xfertyp |= XFERTYP_DPSEL | XFERTYP_DMAEN; - + xfertyp |= XFERTYP_DPSEL; +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO + xfertyp |= XFERTYP_DMAEN; +#endif if (data->blocks > 1) { xfertyp |= XFERTYP_MSBSEL; xfertyp |= XFERTYP_BCEN; @@ -96,11 +99,78 @@ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) return XFERTYP_CMD(cmd->cmdidx) | xfertyp; } +#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO +/* + * PIO Read/Write Mode reduce the performace as DMA is not used in this mode. + */ +static void +esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data) +{ + struct fsl_esdhc *regs = mmc->priv; + uint blocks; + char *buffer; + uint databuf; + uint size; + uint irqstat; + uint timeout; + + if (data->flags & MMC_DATA_READ) { + blocks = data->blocks; + buffer = data->dest; + while (blocks) { + timeout = PIO_TIMEOUT; + size = data->blocksize; + irqstat = esdhc_read32(®s->irqstat); + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BREN) + && --timeout); + if (timeout <= 0) { + printf("\nData Read Failed in PIO Mode."); + return; + } + while (size && (!(irqstat & IRQSTAT_TC))) { + udelay(100); /* Wait before last byte transfer complete */ + irqstat = esdhc_read32(®s->irqstat); + databuf = in_le32(®s->datport); + *((uint *)buffer) = databuf; + buffer += 4; + size -= 4; + } + blocks--; + } + } else { + blocks = data->blocks; + buffer = (char *)data->src; + while (blocks) { + timeout = PIO_TIMEOUT; + size = data->blocksize; + irqstat = esdhc_read32(®s->irqstat); + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_BWEN) + && --timeout); + if (timeout <= 0) { + printf("\nData Write Failed in PIO Mode."); + return; + } + while (size && (!(irqstat & IRQSTAT_TC))) { + udelay(100); /* Wait before last byte transfer complete */ + databuf = *((uint *)buffer); + buffer += 4; + size -= 4; + irqstat = esdhc_read32(®s->irqstat); + out_le32(®s->datport, databuf); + } + blocks--; + } + } +} +#endif + static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) { - uint wml_value; int timeout; - struct fsl_esdhc *regs = mmc->priv; + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; +#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO + uint wml_value; wml_value = data->blocksize/4; @@ -108,26 +178,36 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) if (wml_value > 0x10) wml_value = 0x10; - wml_value = 0x100000 | wml_value; - - out_be32(®s->dsaddr, (u32)data->dest); + esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); + esdhc_write32(®s->dsaddr, (u32)data->dest); } else { if (wml_value > 0x80) wml_value = 0x80; - if ((in_be32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { + if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); return TIMEOUT; } - wml_value = wml_value << 16 | 0x10; - out_be32(®s->dsaddr, (u32)data->src); - } - out_be32(®s->wml, wml_value); + esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, + wml_value << 16); + esdhc_write32(®s->dsaddr, (u32)data->src); + } +#else /* CONFIG_SYS_FSL_ESDHC_USE_PIO */ + if (!(data->flags & MMC_DATA_READ)) { + if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) { + printf("\nThe SD card is locked. " + "Can not write to a locked card.\n\n"); + return TIMEOUT; + } + esdhc_write32(®s->dsaddr, (u32)data->src); + } else + esdhc_write32(®s->dsaddr, (u32)data->dest); +#endif /* CONFIG_SYS_FSL_ESDHC_USE_PIO */ - out_be32(®s->blkattr, data->blocks << 16 | data->blocksize); + esdhc_write32(®s->blkattr, data->blocks << 16 | data->blocksize); /* Calculate the timeout period for data transactions */ - timeout = __ilog2(mmc->tran_speed/10); + timeout = fls(mmc->tran_speed/10) - 1; timeout -= 13; if (timeout > 14) @@ -136,7 +216,7 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) if (timeout < 0) timeout = 0; - clrsetbits_be32(®s->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); + esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); return 0; } @@ -151,17 +231,20 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { uint xfertyp; uint irqstat; - volatile struct fsl_esdhc *regs = mmc->priv; + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; - out_be32(®s->irqstat, -1); + esdhc_write32(®s->irqstat, -1); sync(); /* Wait for the bus to be idle */ - while ((in_be32(®s->prsstat) & PRSSTAT_CICHB) || - (in_be32(®s->prsstat) & PRSSTAT_CIDHB)); + while ((esdhc_read32(®s->prsstat) & PRSSTAT_CICHB) || + (esdhc_read32(®s->prsstat) & PRSSTAT_CIDHB)) + ; - while (in_be32(®s->prsstat) & PRSSTAT_DLA); + while (esdhc_read32(®s->prsstat) & PRSSTAT_DLA) + ; /* Wait at least 8 SD clock cycles before the next command */ /* @@ -183,14 +266,15 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) xfertyp = esdhc_xfertyp(cmd, data); /* Send the command */ - out_be32(®s->cmdarg, cmd->cmdarg); - out_be32(®s->xfertyp, xfertyp); + esdhc_write32(®s->cmdarg, cmd->cmdarg); + esdhc_write32(®s->xfertyp, xfertyp); /* Wait for the command to complete */ - while (!(in_be32(®s->irqstat) & IRQSTAT_CC)); + while (!(esdhc_read32(®s->irqstat) & IRQSTAT_CC)) + ; - irqstat = in_be32(®s->irqstat); - out_be32(®s->irqstat, irqstat); + irqstat = esdhc_read32(®s->irqstat); + esdhc_write32(®s->irqstat, irqstat); if (irqstat & CMD_ERR) return COMM_ERR; @@ -202,21 +286,24 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) if (cmd->resp_type & MMC_RSP_136) { u32 cmdrsp3, cmdrsp2, cmdrsp1, cmdrsp0; - cmdrsp3 = in_be32(®s->cmdrsp3); - cmdrsp2 = in_be32(®s->cmdrsp2); - cmdrsp1 = in_be32(®s->cmdrsp1); - cmdrsp0 = in_be32(®s->cmdrsp0); - ((uint *)(cmd->response))[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24); - ((uint *)(cmd->response))[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24); - ((uint *)(cmd->response))[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24); - ((uint *)(cmd->response))[3] = (cmdrsp0 << 8); + cmdrsp3 = esdhc_read32(®s->cmdrsp3); + cmdrsp2 = esdhc_read32(®s->cmdrsp2); + cmdrsp1 = esdhc_read32(®s->cmdrsp1); + cmdrsp0 = esdhc_read32(®s->cmdrsp0); + cmd->response[0] = (cmdrsp3 << 8) | (cmdrsp2 >> 24); + cmd->response[1] = (cmdrsp2 << 8) | (cmdrsp1 >> 24); + cmd->response[2] = (cmdrsp1 << 8) | (cmdrsp0 >> 24); + cmd->response[3] = (cmdrsp0 << 8); } else - ((uint *)(cmd->response))[0] = in_be32(®s->cmdrsp0); + cmd->response[0] = esdhc_read32(®s->cmdrsp0); /* Wait until all of the blocks are transferred */ if (data) { +#ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO + esdhc_pio_read_write(mmc, data); +#else do { - irqstat = in_be32(®s->irqstat); + irqstat = esdhc_read32(®s->irqstat); if (irqstat & DATA_ERR) return COMM_ERR; @@ -224,10 +311,11 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) if (irqstat & IRQSTAT_DTOE) return TIMEOUT; } while (!(irqstat & IRQSTAT_TC) && - (in_be32(®s->prsstat) & PRSSTAT_DLA)); + (esdhc_read32(®s->prsstat) & PRSSTAT_DLA)); +#endif } - out_be32(®s->irqstat, -1); + esdhc_write32(®s->irqstat, -1); return 0; } @@ -236,9 +324,13 @@ void set_sysctl(struct mmc *mmc, uint clock) { int sdhc_clk = gd->sdhc_clk; int div, pre_div; - volatile struct fsl_esdhc *regs = mmc->priv; + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; uint clk; + if (clock < mmc->f_min) + clock = mmc->f_min; + if (sdhc_clk / 16 > clock) { for (pre_div = 2; pre_div < 256; pre_div *= 2) if ((sdhc_clk / pre_div) <= (clock * 16)) @@ -255,67 +347,117 @@ void set_sysctl(struct mmc *mmc, uint clock) clk = (pre_div << 8) | (div << 4); - clrsetbits_be32(®s->sysctl, SYSCTL_CLOCK_MASK, clk); + esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN); + + esdhc_clrsetbits32(®s->sysctl, SYSCTL_CLOCK_MASK, clk); udelay(10000); - setbits_be32(®s->sysctl, SYSCTL_PEREN); + clk = SYSCTL_PEREN | SYSCTL_CKEN; + + esdhc_setbits32(®s->sysctl, clk); } static void esdhc_set_ios(struct mmc *mmc) { - struct fsl_esdhc *regs = mmc->priv; + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; /* Set the clock speed */ set_sysctl(mmc, mmc->clock); /* Set the bus width */ - clrbits_be32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); + esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); if (mmc->bus_width == 4) - setbits_be32(®s->proctl, PROCTL_DTW_4); + esdhc_setbits32(®s->proctl, PROCTL_DTW_4); else if (mmc->bus_width == 8) - setbits_be32(®s->proctl, PROCTL_DTW_8); + esdhc_setbits32(®s->proctl, PROCTL_DTW_8); + } static int esdhc_init(struct mmc *mmc) { - struct fsl_esdhc *regs = mmc->priv; + struct fsl_esdhc_cfg *cfg = (struct fsl_esdhc_cfg *)mmc->priv; + struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; int timeout = 1000; + int ret = 0; + u8 card_absent; /* Enable cache snooping */ - out_be32(®s->scr, 0x00000040); + if (cfg && !cfg->no_snoop) + esdhc_write32(®s->scr, 0x00000040); - out_be32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); + /* Reset the entire host controller */ + esdhc_write32(®s->sysctl, SYSCTL_RSTA); + + /* Wait until the controller is available */ + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) + udelay(1000); + + esdhc_write32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN); /* Set the initial clock speed */ set_sysctl(mmc, 400000); /* Disable the BRR and BWR bits in IRQSTAT */ - clrbits_be32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); + esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR); /* Put the PROCTL reg back to the default */ - out_be32(®s->proctl, PROCTL_INIT); + esdhc_write32(®s->proctl, PROCTL_INIT); - while (!(in_be32(®s->prsstat) & PRSSTAT_CINS) && --timeout) - udelay(1000); + /* Set timout to the maximum value */ + esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16); - if (timeout <= 0) - return NO_CARD_ERR; + /* Check if there is a callback for detecting the card */ + if (board_mmc_getcd(&card_absent, mmc)) { + timeout = 1000; + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_CINS) && + --timeout) + udelay(1000); - return 0; + if (timeout <= 0) + ret = NO_CARD_ERR; + } else { + if (card_absent) + ret = NO_CARD_ERR; + } + + return ret; +} + +static void esdhc_reset(struct fsl_esdhc *regs) +{ + unsigned long timeout = 100; /* wait max 100 ms */ + + /* reset the controller */ + esdhc_write32(®s->sysctl, SYSCTL_RSTA); + + /* hardware clears the bit when it is done */ + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) + udelay(1000); + if (!timeout) + printf("MMC/SD: Reset never completed.\n"); } -static int esdhc_initialize(bd_t *bis) +int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) { - struct fsl_esdhc *regs = (struct fsl_esdhc *)CONFIG_SYS_FSL_ESDHC_ADDR; + struct fsl_esdhc *regs; struct mmc *mmc; u32 caps; + if (!cfg) + return -1; + mmc = malloc(sizeof(struct mmc)); sprintf(mmc->name, "FSL_ESDHC"); - mmc->priv = regs; + regs = (struct fsl_esdhc *)cfg->esdhc_base; + + /* First reset the eSDHC controller */ + esdhc_reset(regs); + + mmc->priv = cfg; mmc->send_cmd = esdhc_send_cmd; mmc->set_ios = esdhc_set_ios; mmc->init = esdhc_init; @@ -344,5 +486,29 @@ static int esdhc_initialize(bd_t *bis) int fsl_esdhc_mmc_init(bd_t *bis) { - return esdhc_initialize(bis); + struct fsl_esdhc_cfg *cfg; + + cfg = malloc(sizeof(struct fsl_esdhc_cfg)); + memset(cfg, 0, sizeof(struct fsl_esdhc_cfg)); + cfg->esdhc_base = CONFIG_SYS_FSL_ESDHC_ADDR; + return fsl_esdhc_initialize(bis, cfg); +} + +#ifdef CONFIG_OF_LIBFDT +void fdt_fixup_esdhc(void *blob, bd_t *bd) +{ + const char *compat = "fsl,esdhc"; + const char *status = "okay"; + + if (!hwconfig("esdhc")) { + status = "disabled"; + goto out; + } + + do_fixup_by_compat_u32(blob, compat, "clock-frequency", + gd->sdhc_clk, 1); +out: + do_fixup_by_compat(blob, compat, "status", status, + strlen(status) + 1, 1); } +#endif