diff options
author | Terry Lv <r65388@freescale.com> | 2009-05-14 16:38:57 +0800 |
---|---|---|
committer | Fred Fan <r01011@freescale.com> | 2009-09-10 16:56:36 +0800 |
commit | 30e188a23150345a74f5a83aab344ac8510d38d9 (patch) | |
tree | f99cd9f1e14732e09ef66fc3a58dd2c875ef18ff /drivers/mmc/fsl_esdhc.c | |
parent | 31594f542d3c7b1db6b8404e608e452a604bd6d8 (diff) | |
download | u-boot-imx-30e188a23150345a74f5a83aab344ac8510d38d9.zip u-boot-imx-30e188a23150345a74f5a83aab344ac8510d38d9.tar.gz u-boot-imx-30e188a23150345a74f5a83aab344ac8510d38d9.tar.bz2 |
ENGR00112273 BBG2: MMC boot support.
BBG2: MMC boot support.
Signed-off-by: Terry Lv <r65388@freescale.com>
Diffstat (limited to 'drivers/mmc/fsl_esdhc.c')
-rw-r--r-- | drivers/mmc/fsl_esdhc.c | 687 |
1 files changed, 629 insertions, 58 deletions
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index c6e9e6e..9fcce5b 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -1,6 +1,5 @@ /* - * Copyright 2007, Freescale Semiconductor, Inc - * Andy Fleming + * Copyright 2008-2009 Freescale Semiconductor, Inc. * * Based vaguely on the pxa mmc code: * (C) Copyright 2003 @@ -25,7 +24,18 @@ * MA 02111-1307 USA */ +/*! + * @file esdhc.c + * + * @brief source code for the mmc card operation + * + * @ingroup mmc + */ + #include <config.h> +#include <asm/arch/sdhc.h> +#include <linux/mmc/sdhci.h> +#include <asm/errno.h> #include <common.h> #include <command.h> #include <hwconfig.h> @@ -35,9 +45,9 @@ #include <mmc.h> #include <fsl_esdhc.h> #include <fdt_support.h> +#include <linux/types.h> #include <asm/io.h> - DECLARE_GLOBAL_DATA_PTR; struct fsl_esdhc { @@ -67,6 +77,41 @@ struct fsl_esdhc { uint scr; }; +#define RETRIES_TIMES 100 + +#define REG_WRITE_OR(val, reg) { \ + u32 temp = 0; \ + temp = readl(reg); \ + (temp) |= (val); \ + writel((temp), (reg)); \ + } + +#define REG_WRITE_AND(val, reg) { \ + u32 temp = 0; \ + temp = readl(reg); \ + (temp) &= (val); \ + writel((temp), (reg)); \ + } + +#define SDHC_DELAY_BY_100(x) { \ + u32 i; \ + for (i = 0; i < x; ++i) \ + udelay(100); \ + } + +extern volatile u32 esdhc_base_pointer; + +static void esdhc_cmd_config(esdhc_cmd_t *); +static u32 esdhc_check_response(void); +static u32 esdhc_wait_buf_rdy_intr(u32, u32); +static void esdhc_wait_op_done_intr(void); +static u32 esdhc_check_data(void); +static void esdhc_set_data_transfer_width(u32 data_transfer_width); +static u32 esdhc_poll_cihb_cdihb(data_present_select data_present); +static void esdhc_set_endianness(u32 endian_mode); +static void esdhc_clear_buf_rdy_intr(u32 mask); +static u32 esdhc_check_data_crc_status(void); + /* Return the XFERTYP flags for a given command and data packet */ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) { @@ -74,12 +119,9 @@ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) if (data) { xfertyp |= XFERTYP_DPSEL | XFERTYP_DMAEN; - if (data->blocks > 1) { xfertyp |= XFERTYP_MSBSEL; xfertyp |= XFERTYP_BCEN; - } - if (data->flags & MMC_DATA_READ) xfertyp |= XFERTYP_DTDSEL; } @@ -98,20 +140,30 @@ uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) return XFERTYP_CMD(cmd->cmdidx) | xfertyp; } +/*! + * Send 80 SD clock to card and wait for INITA bit to get cleared. + */ +void interface_initialization_active(void) +{ + /* Send 80 clock ticks for card to power up */ + REG_WRITE_OR(ESDHC_SYSCTL_INITA, \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + + /* Start a general purpose timer */ + udelay(ESDHC_CARD_INIT_TIMEOUT); +} + static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) { uint wml_value; int timeout; struct fsl_esdhc *regs = mmc->priv; - wml_value = data->blocksize/4; if (data->flags & MMC_DATA_READ) { if (wml_value > 0x10) wml_value = 0x10; - wml_value = 0x100000 | wml_value; - out_be32(®s->dsaddr, (u32)data->dest); } else { if (wml_value > 0x80) @@ -125,24 +177,158 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) } out_be32(®s->wml, wml_value); - out_be32(®s->blkattr, data->blocks << 16 | data->blocksize); - /* Calculate the timeout period for data transactions */ timeout = __ilog2(mmc->tran_speed/10); timeout -= 13; - if (timeout > 14) timeout = 14; - if (timeout < 0) timeout = 0; - clrsetbits_be32(®s->sysctl, SYSCTL_TIMEOUT_MASK, timeout << 16); - return 0; } +/*! + * Execute a software reset and set data bus width for eSDHC. + */ +u32 interface_reset(void) +{ + u32 reset_status = 0; + u32 u32Retries = 0; + u32 u32Temp = 0; + + debug("Entry: interface_reset"); + + /* Reset the entire host controller by writing + 1 to RSTA bit of SYSCTRL Register */ + REG_WRITE_OR(ESDHC_SOFTWARE_RESET, \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + + /* Start a general purpose timer (3 millsec delay) */ + /* udelay(ESDHC_OPER_TIMEOUT); */ + + /* Wait for clearance of CIHB and CDIHB Bits */ + for (u32Retries = RETRIES_TIMES; u32Retries > 0; --u32Retries) { + if (!is_soc_rev(CHIP_REV_1_0)) { + if (readl(esdhc_base_pointer + SDHCI_PRESENT_STATE) \ + & ESDHC_CMD_INHIBIT) { + reset_status = 1; + } else { + reset_status = 0; + break; + } + } else if (!is_soc_rev(CHIP_REV_2_0)) { + if (readl(esdhc_base_pointer + SDHCI_SYSTEM_CONTROL) \ + & ESDHC_SOFTWARE_RESET) { + reset_status = 1; + } else { + reset_status = 0; + break; + } + } + } + + if (!is_soc_rev(CHIP_REV_1_0)) { + /* send 80 clock ticks for card to power up */ + REG_WRITE_OR(ESDHC_SYSCTL_INITA, \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + } + + /* Set data bus width of ESDCH */ + esdhc_set_data_transfer_width(0x00000000); + /* Set Endianness of ESDHC */ + esdhc_set_endianness(0x00000020); + + /* set data timeout delay to max */ + u32Temp = (readl(esdhc_base_pointer + SDHCI_SYSTEM_CONTROL) & \ + 0xfff0ffff) | 0x000e0000; + writel(u32Temp, esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + + return reset_status; +} + +/*! + * Clear interrupts at eSDHC level. + */ +void interface_clear_interrupt(void) +{ + /* Clear Interrupt status register */ + writel(ESDHC_CLEAR_INTERRUPT, esdhc_base_pointer + SDHCI_INT_STATUS); +} + +/*! + * Enable Clock and set operating frequency. + */ +void interface_configure_clock(sdhc_freq_t frequency) +{ + u32 ident_freq = 0; + u32 oper_freq = 0; + + if (!is_soc_rev(CHIP_REV_1_0)) { + /* Enable ipg_perclk, HCLK enable and IPG Clock enable. */ + REG_WRITE_OR(ESDHC_CLOCK_ENABLE, \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + /* Clear DTOCV SDCLKFS bits */ + REG_WRITE_OR(ESDHC_FREQ_MASK, \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + ident_freq = ESDHC_SYSCTL_IDENT_FREQ_TO1; + oper_freq = ESDHC_SYSCTL_OPERT_FREQ_TO1; + } else if (!is_soc_rev(CHIP_REV_2_0)) { + /* Clear SDCLKEN bit */ + REG_WRITE_OR((~ESDHC_SYSCTL_SDCLKEN_MASK), \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + + /* Clear DTOCV, SDCLKFS, DVFS bits */ + REG_WRITE_OR((~ESDHC_SYSCTL_FREQ_MASK), \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + ident_freq = ESDHC_SYSCTL_IDENT_FREQ_TO2; + oper_freq = ESDHC_SYSCTL_OPERT_FREQ_TO2; + } + + if (!is_soc_rev(CHIP_REV_2_0)) { + /* Disable the PEREN, HCKEN and IPGEN */ + REG_WRITE_OR((~ESDHC_SYSCTL_INPUT_CLOCK_MASK), \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + } + + if (frequency == IDENTIFICATION_FREQ) { + /* Input frequecy to eSDHC is 36 MHZ */ + /* PLL3 is the source of input frequency*/ + /*Set DTOCV and SDCLKFS bit to get SD_CLK + of frequency below 400 KHZ (70.31 KHZ) */ + REG_WRITE_OR(ident_freq, \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + } else if (frequency == OPERATING_FREQ) { + /*Set DTOCV and SDCLKFS bit to get SD_CLK + of frequency around 25 MHz.(18 MHz)*/ + REG_WRITE_OR(oper_freq, \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + } + + if (!is_soc_rev(CHIP_REV_2_0)) { + /* Start a general purpose timer */ + /* Wait for clock to be stable */ + SDHC_DELAY_BY_100(96); + + /* Set SDCLKEN bit to enable clock */ + REG_WRITE_OR(ESDHC_SYSCTL_SDCLKEN_MASK, \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + + /* Mask Data Timeout Error Status Enable Interrupt (DTOESEN) */ + REG_WRITE_AND((~ESDHC_IRQSTATEN_DTOESEN), \ + esdhc_base_pointer + SDHCI_INT_ENABLE); + + /* Set the Data Timeout Counter Value(DTOCV) */ + REG_WRITE_OR(ESDHC_SYSCTL_DTOCV_VAL, \ + esdhc_base_pointer + SDHCI_SYSTEM_CONTROL); + + /* Enable Data Timeout Error Status + Enable Interrupt (DTOESEN) */ + REG_WRITE_OR(ESDHC_IRQSTATEN_DTOESEN, \ + esdhc_base_pointer + SDHCI_INT_ENABLE); + } +} /* * Sends a command out on the bus. Takes the mmc pointer, @@ -154,24 +340,18 @@ 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; - out_be32(®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 (in_be32(®s->prsstat) & PRSSTAT_DLA); - /* Wait at least 8 SD clock cycles before the next command */ /* * Note: This is way more than 8 cycles, but 1ms seems to * resolve timing issues with some cards */ udelay(1000); - /* Set up for a data transfer if we have one */ if (data) { int err; @@ -180,30 +360,23 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) if(err) return err; } - /* Figure out the transfer arguments */ xfertyp = esdhc_xfertyp(cmd, data); - /* Send the command */ out_be32(®s->cmdarg, cmd->cmdarg); out_be32(®s->xfertyp, xfertyp); - /* Wait for the command to complete */ while (!(in_be32(®s->irqstat) & IRQSTAT_CC)); irqstat = in_be32(®s->irqstat); out_be32(®s->irqstat, irqstat); - if (irqstat & CMD_ERR) return COMM_ERR; - if (irqstat & IRQSTAT_CTOE) return TIMEOUT; - /* Copy the response to the response buffer */ 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); @@ -214,15 +387,12 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) cmd->response[3] = (cmdrsp0 << 8); } else cmd->response[0] = in_be32(®s->cmdrsp0); - /* Wait until all of the blocks are transferred */ if (data) { do { irqstat = in_be32(®s->irqstat); - if (irqstat & DATA_ERR) return COMM_ERR; - if (irqstat & IRQSTAT_DTOE) return TIMEOUT; } while (!(irqstat & IRQSTAT_TC) && @@ -230,10 +400,132 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) } out_be32(®s->irqstat, -1); + return 0; +} + +/*! + * Set data transfer width for e-SDHC. + */ +static void esdhc_set_data_transfer_width(u32 data_transfer_width) +{ + /* Set DWT bit of protocol control register according to bus_width */ + if (!is_soc_rev(CHIP_REV_2_0)) + REG_WRITE_AND((~ESDHC_BUS_WIDTH_MASK), \ + esdhc_base_pointer + SDHCI_HOST_CONTROL); + + REG_WRITE_OR((data_transfer_width), \ + esdhc_base_pointer + SDHCI_HOST_CONTROL); +} + +/*! + * Set endianness mode for e-SDHC. + */ +static void esdhc_set_endianness(u32 endian_mode) +{ + if (!is_soc_rev(CHIP_REV_2_0)) { + REG_WRITE_AND((~ESDHC_ENDIAN_MODE_MASK), \ + esdhc_base_pointer + SDHCI_HOST_CONTROL); + } + /* Set DWT bit of protocol control register according to bus_width */ + REG_WRITE_OR((endian_mode), \ + esdhc_base_pointer + SDHCI_HOST_CONTROL); +} + +/*! + * Poll the CIHB & CDIHB bits of the present + * state register and wait until it goes low. + */ +static u32 esdhc_poll_cihb_cdihb(data_present_select data_present) +{ + u32 init_status = 0; + u32 u32Retries = 0; + + /* Start a general purpose timer */ + for (u32Retries = RETRIES_TIMES; u32Retries > 0; u32Retries--) { + if (!(readl(esdhc_base_pointer + SDHCI_PRESENT_STATE) & \ + ESDHC_PRESENT_STATE_CIHB)) { + init_status = 0; + break; + } + SDHC_DELAY_BY_100(10); + } + + /* + * Wait for the data line to be free (poll the CDIHB bit of + * the present state register). + */ + if ((0 == init_status) && (data_present == DATA_PRESENT)) { + /* Start a general purpose timer */ + SDHC_DELAY_BY_100(32); + + if (readl(esdhc_base_pointer + SDHCI_PRESENT_STATE) & \ + ESDHC_PRESENT_STATE_CDIHB) { + init_status = 1; + } + } + + return init_status; +} + +/*! + * Wait until the command and data lines are free. + */ +u32 interface_wait_cmd_data_lines(data_present_select data_present) +{ + u32 cmd_status = 0; + + cmd_status = esdhc_poll_cihb_cdihb(data_present); + + return cmd_status; +} + +u32 interface_set_bus_width(u32 bus_width) +{ + u32 tmp; + + tmp = readl(esdhc_base_pointer + SDHCI_HOST_CONTROL); + tmp &= ~SDHCI_CTRL_8BITBUS; + tmp |= SDHCI_CTRL_4BITBUS; + + writel(tmp, esdhc_base_pointer + SDHCI_HOST_CONTROL); return 0; } +/*! + * Execute a command and wait for the response. + */ +u32 interface_send_cmd_wait_resp(esdhc_cmd_t *cmd) +{ + u32 cmd_status = 0; + + /* Clear Interrupt status register */ + writel(ESDHC_CLEAR_INTERRUPT, \ + esdhc_base_pointer + SDHCI_INT_STATUS); + + /* Enable Interrupt */ + REG_WRITE_OR(ESDHC_INTERRUPT_ENABLE, \ + esdhc_base_pointer + SDHCI_INT_ENABLE); + if (!is_soc_rev(CHIP_REV_2_0)) { + cmd_status = interface_wait_cmd_data_lines(cmd->data_present); + + if (cmd_status == 1) + return 1; + } + + /* Configure Command */ + esdhc_cmd_config(cmd); + + /* Wait for interrupt CTOE or CC */ + SDHC_DELAY_BY_100(96); + + /* Mask all interrupts */ + writel(0, esdhc_base_pointer + SDHCI_SIGNAL_ENABLE); + + /* Check if an error occured */ + return esdhc_check_response(); +} + void set_sysctl(struct mmc *mmc, uint clock) { int sdhc_clk = gd->sdhc_clk; @@ -247,39 +539,178 @@ void set_sysctl(struct mmc *mmc, uint clock) break; } else pre_div = 2; - for (div = 1; div <= 16; div++) if ((sdhc_clk / (div * pre_div)) <= clock) break; - pre_div >>= 1; div -= 1; - clk = (pre_div << 8) | (div << 4); - clrsetbits_be32(®s->sysctl, SYSCTL_CLOCK_MASK, clk); - udelay(10000); - setbits_be32(®s->sysctl, SYSCTL_PEREN); } +/*! + * Configure ESDHC registers for sending a command to MMC. + */ +static void esdhc_cmd_config(esdhc_cmd_t *cmd) +{ + u32 u32Temp = 0; + + /* Write Command Argument in Command Argument Register */ + writel(cmd->arg, esdhc_base_pointer + SDHCI_ARGUMENT); + + /* + *Configure e-SDHC Register value according to Command + */ + u32Temp = \ + (((cmd->data_transfer)<<ESDHC_DATA_TRANSFER_SHIFT) | + ((cmd->response_format)<<ESDHC_RESPONSE_FORMAT_SHIFT) | + ((cmd->data_present)<<ESDHC_DATA_PRESENT_SHIFT) | + ((cmd->crc_check) << ESDHC_CRC_CHECK_SHIFT) | + ((cmd->cmdindex_check) << ESDHC_CMD_INDEX_CHECK_SHIFT) | + ((cmd->command) << ESDHC_CMD_INDEX_SHIFT) | + ((cmd->block_count_enable_check) << \ + ESDHC_BLOCK_COUNT_ENABLE_SHIFT) | + ((cmd->multi_single_block) << \ + ESDHC_MULTI_SINGLE_BLOCK_SELECT_SHIFT)); + + writel(u32Temp, esdhc_base_pointer + SDHCI_TRANSFER_MODE); +} + +/*! + * Wait a END_CMD_RESP interrupt by interrupt status register. + * e-SDHC sets this bit after receving command response. + */ +static u32 esdhc_check_response(void) +{ + u32 status = 1; + + /* Check whether the interrupt is an END_CMD_RESP + * or a response time out or a CRC error + */ + if ((readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_END_CMD_RESP_MSK) && + !(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_TIME_OUT_RESP_MSK) && + !(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_RESP_CRC_ERR_MSK) && + !(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_RESP_CMD_INDEX_ERR_MSK)) + status = 0; + + return status; +} + +/*! + * This function will read response from e-SDHC + * register according to reponse format. + */ +void interface_read_response(esdhc_resp_t *cmd_resp) +{ + /* get response values from e-SDHC CMDRSP registers.*/ + cmd_resp->cmd_rsp0 = (u32)readl(esdhc_base_pointer + SDHCI_RESPONSE); + cmd_resp->cmd_rsp1 = (u32)readl(esdhc_base_pointer + \ + SDHCI_RESPONSE + 4); + cmd_resp->cmd_rsp2 = (u32)readl(esdhc_base_pointer + \ + SDHCI_RESPONSE + 8); + cmd_resp->cmd_rsp3 = (u32)readl(esdhc_base_pointer + \ + SDHCI_RESPONSE + 12); +} + +/*! + * This function will read response from e-SDHC register + * according to reponse format. + */ +u32 interface_data_read(u32 *dest_ptr, u32 blk_len) +{ + u32 i = 0; + u32 j = 0; + u32 status = 1; + u32 *tmp_ptr = dest_ptr; + + debug("Entry: interface_data_read()\n"); + + /* Enable Interrupt */ + REG_WRITE_OR(ESDHC_INTERRUPT_ENABLE, \ + esdhc_base_pointer + SDHCI_INT_ENABLE); + + for (i = 0; i < (blk_len) / (ESDHC_FIFO_SIZE * 4); ++i) { + /* Wait for BRR bit to be set */ + status = esdhc_wait_buf_rdy_intr(ESDHC_STATUS_BUF_READ_RDY_MSK, + ESDHC_READ_DATA_TIME_OUT); + + debug("esdhc_wait_buf_rdy_intr: %d\n", status); + + if (!status) { + for (j = 0; j < ESDHC_FIFO_SIZE; ++j) { + *tmp_ptr++ = \ + readl(esdhc_base_pointer + SDHCI_BUFFER); + } + if (!is_soc_rev(CHIP_REV_2_0)) { + /* Clear the BRR */ + esdhc_clear_buf_rdy_intr(ESDHC_STATUS_BUF_READ_RDY_MSK); + } + } else { + debug("esdhc_wait_buf_rdy_intr failed\n"); + break; + } + } + + esdhc_wait_op_done_intr(); + + status = esdhc_check_data(); + + if (!is_soc_rev(CHIP_REV_2_0) && !status) + status = 0; + + debug("esdhc_check_data: %d\n", status); + debug("Exit: interface_data_read()\n"); + + return status; +} + static void esdhc_set_ios(struct mmc *mmc) { struct fsl_esdhc *regs = mmc->priv; - /* Set the clock speed */ set_sysctl(mmc, mmc->clock); - /* Set the bus width */ clrbits_be32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); - if (mmc->bus_width == 4) setbits_be32(®s->proctl, PROCTL_DTW_4); else if (mmc->bus_width == 8) setbits_be32(®s->proctl, PROCTL_DTW_8); } +/*! + * Wait a BUF_READ_READY interrupt by pooling STATUS register. + */ +static u32 esdhc_wait_buf_rdy_intr(u32 mask, u32 multi_single_block) +{ + u32 status = 0; + u32 u32Retries = 0; + + /* Wait interrupt (BUF_READ_RDY) + */ + + for (u32Retries = RETRIES_TIMES; u32Retries > 0; --u32Retries) { + if (!(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & mask)) { + status = 1; + } else { + status = 0; + break; + } + SDHC_DELAY_BY_100(10); + } + + if (multi_single_block == MULTIPLE && \ + readl(esdhc_base_pointer + SDHCI_INT_STATUS) & mask) + REG_WRITE_OR(mask, (esdhc_base_pointer + SDHCI_INT_STATUS)); + + return status; +} + static int esdhc_init(struct mmc *mmc) { struct fsl_esdhc *regs = mmc->priv; @@ -287,63 +718,170 @@ static int esdhc_init(struct mmc *mmc) /* Enable cache snooping */ out_be32(®s->scr, 0x00000040); - out_be32(®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); - /* Put the PROCTL reg back to the default */ out_be32(®s->proctl, PROCTL_INIT); - while (!(in_be32(®s->prsstat) & PRSSTAT_CINS) && --timeout) udelay(1000); - if (timeout <= 0) return NO_CARD_ERR; - return 0; } +/*! + * Clear BUF_READ_READY/BUF_WRITE_READY interrupt + * by writing 1 to STATUS register. + */ +static void esdhc_clear_buf_rdy_intr(u32 mask) +{ + writel(mask, (esdhc_base_pointer + SDHCI_INT_STATUS)); +} + +/*! + * Wait for TC, DEBE, DCE or DTOE by polling Interrupt STATUS register. + */ +static void esdhc_wait_op_done_intr(void) +{ + while (!(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_TRANSFER_COMPLETE_MSK)) + ; +} + +/*! + * If READ_OP_DONE occured check ESDHC_STATUS_TIME_OUT_READ + * and RD_CRC_ERR_CODE and + * to determine if an error occured + */ +static u32 esdhc_check_data(void) +{ + u32 status = 1; + + debug("Entry: esdhc_check_data()\n"); + + /* Check whether the interrupt is an OP_DONE + * or a data time out or a CRC error + */ + if ((readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_TRANSFER_COMPLETE_MSK) && + !(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_TIME_OUT_READ_MASK) && + !(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_READ_CRC_ERR_MSK)) { + if (!is_soc_rev(CHIP_REV_2_0)) { + writel(ESDHC_STATUS_TRANSFER_COMPLETE_MSK, \ + (esdhc_base_pointer + SDHCI_INT_STATUS)); + } + status = 0; + } else { + status = 1; + } + + debug("Exit: esdhc_check_data()\n"); + return status; +} + +/*! + * Check for Data timeout error, data CRC error and data end bit error + * to determine if an error occured. + */ +static u32 esdhc_check_data_crc_status(void) +{ + u32 status = 1; + + /* Check whether the interrupt is DTOE/DCE/DEBE */ + if (!(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_TIME_OUT_READ_MASK) && + !(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_READ_CRC_ERR_MSK) && + !(readl(esdhc_base_pointer + SDHCI_INT_STATUS) & \ + ESDHC_STATUS_RW_DATA_END_BIT_ERR_MSK)) { + status = 0; + } else { + status = 1; + } + + return status; +} static int esdhc_initialize(bd_t *bis) { struct fsl_esdhc *regs = (struct fsl_esdhc *)CONFIG_SYS_FSL_ESDHC_ADDR; struct mmc *mmc; u32 caps; - mmc = malloc(sizeof(struct mmc)); - sprintf(mmc->name, "FSL_ESDHC"); mmc->priv = regs; mmc->send_cmd = esdhc_send_cmd; mmc->set_ios = esdhc_set_ios; mmc->init = esdhc_init; - caps = regs->hostcapblt; - if (caps & ESDHC_HOSTCAPBLT_VS18) mmc->voltages |= MMC_VDD_165_195; if (caps & ESDHC_HOSTCAPBLT_VS30) mmc->voltages |= MMC_VDD_29_30 | MMC_VDD_30_31; if (caps & ESDHC_HOSTCAPBLT_VS33) mmc->voltages |= MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; - if (caps & ESDHC_HOSTCAPBLT_HSS) mmc->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; - mmc->f_min = 400000; mmc->f_max = MIN(gd->sdhc_clk, 50000000); - mmc_register(mmc); - return 0; } +/*! + * Set Block length. + */ +void interface_config_block_info(u32 blk_len, u32 nob, u32 wml) +{ + /* Configre block Attributes register */ + writel(((nob << ESDHC_BLOCK_SHIFT) | blk_len), \ + (esdhc_base_pointer + SDHCI_BLOCK_SIZE)); + /* Set Read Water MArk Level register */ + writel(wml, esdhc_base_pointer + SDHCI_WML_LEV); +} + +/*! + * This function will write data to device attached to interface. + */ +u32 interface_data_write(u32 *dest_ptr, u32 blk_len) +{ + u32 i = 0; + u32 j = 0; + u32 status = 1; + u32 *tmp_ptr = dest_ptr; + + debug("Entry: interface_data_write()\n"); + + /* Enable Interrupt */ + REG_WRITE_OR(ESDHC_INTERRUPT_ENABLE, \ + (esdhc_base_pointer + SDHCI_INT_ENABLE)); + + for (i = 0; i < (blk_len) / (ESDHC_FIFO_SIZE * 4); ++i) { + /* Wait for BWR bit to be set */ + esdhc_wait_buf_rdy_intr(ESDHC_STATUS_BUF_WRITE_RDY_MSK, \ + SINGLE); + for (j = 0; j < ESDHC_FIFO_SIZE; ++j) { + writel((*tmp_ptr), esdhc_base_pointer + SDHCI_BUFFER); + ++tmp_ptr; + } + esdhc_clear_buf_rdy_intr(ESDHC_STATUS_BUF_WRITE_RDY_MSK); + } + + /* Wait for transfer complete operation interrupt */ + esdhc_wait_op_done_intr(); + + /* Check for status errors */ + status = esdhc_check_data(); + + debug("Exit: interface_data_write()\n"); + return status; +} + int fsl_esdhc_mmc_init(bd_t *bis) { return esdhc_initialize(bis); @@ -353,15 +891,48 @@ 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); } + +/*! + * Configure the CMD line PAD configuration for strong or weak pull-up. + */ +/* +void esdhc_set_cmd_pullup(esdhc_pullup_t pull_up) +{ + u32 interface_esdhc = 0; + u32 pad_val = 0; + + interface_esdhc = (readl(0x53ff080c)) & (0x000000C0) >> 6; + + if (pull_up == STRONG) { + pad_val = PAD_CTL_PUE_PUD | PAD_CTL_PKE_ENABLE | + PAD_CTL_HYS_SCHMITZ | PAD_CTL_DRV_HIGH | + PAD_CTL_22K_PU | PAD_CTL_SRE_FAST; + } else { + pad_val = PAD_CTL_PUE_PUD | PAD_CTL_PKE_ENABLE | + PAD_CTL_HYS_SCHMITZ | PAD_CTL_DRV_MAX | + PAD_CTL_100K_PU | PAD_CTL_SRE_FAST; + } + + switch (interface_esdhc) { + case ESDHC1: + mxc_iomux_set_pad(MX51_PIN_SD1_CMD, pad_val); + break; + case ESDHC2: + mxc_iomux_set_pad(MX51_PIN_SD2_CMD, pad_val); + break; + case ESDHC3: + default: + break; + } +} +*/ |