| /* Copyright (c) 2013-2016, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include <platform/iomap.h> |
| #include <platform/irqs.h> |
| #include <platform/interrupts.h> |
| #include <platform/timer.h> |
| #include <kernel/event.h> |
| #include <target.h> |
| #include <string.h> |
| #include <stdlib.h> |
| #include <bits.h> |
| #include <debug.h> |
| #include <sdhci.h> |
| #include <sdhci_msm.h> |
| |
| static void sdhci_dumpregs(struct sdhci_host *host) |
| { |
| DBG("****************** SDHC REG DUMP START ********************\n"); |
| |
| DBG("Version: 0x%08x\n", REG_READ32(host, SDHCI_ARG2_REG)); |
| DBG("Arg2: 0x%08x\t Blk Cnt: 0x%08x\n", |
| REG_READ32(host, SDHCI_ARG2_REG), |
| REG_READ16(host, SDHCI_BLK_CNT_REG)); |
| DBG("Arg1: 0x%08x\t Blk Sz : 0x%08x\n", |
| REG_READ32(host, SDHCI_ARGUMENT_REG), |
| REG_READ16(host, SDHCI_BLKSZ_REG)); |
| DBG("Command: 0x%08x\t Trans mode: 0x%08x\n", |
| REG_READ16(host, SDHCI_CMD_REG), |
| REG_READ16(host, SDHCI_TRANS_MODE_REG)); |
| DBG("Resp0: 0x%08x\t Resp1: 0x%08x\n", |
| REG_READ32(host, SDHCI_RESP_REG), |
| REG_READ32(host, SDHCI_RESP_REG + 0x4)); |
| DBG("Resp2: 0x%08x\t Resp3: 0x%08x\n", |
| REG_READ32(host, SDHCI_RESP_REG + 0x8), |
| REG_READ32(host, SDHCI_RESP_REG + 0xC)); |
| DBG("Prsnt State: 0x%08x\t Host Ctrl1: 0x%08x\n", |
| REG_READ32(host, SDHCI_PRESENT_STATE_REG), |
| REG_READ8(host, SDHCI_HOST_CTRL1_REG)); |
| DBG("Timeout ctrl: 0x%08x\t Power Ctrl: 0x%08x\n", |
| REG_READ8(host, SDHCI_TIMEOUT_REG), |
| REG_READ8(host, SDHCI_PWR_CTRL_REG)); |
| DBG("Error stat: 0x%08x\t Int Status: 0x%08x\n", |
| REG_READ16(host, SDHCI_ERR_INT_STS_REG), |
| REG_READ16(host, SDHCI_NRML_INT_STS_REG)); |
| DBG("Host Ctrl2: 0x%08x\t Clock ctrl: 0x%08x\n", |
| REG_READ16(host, SDHCI_HOST_CTRL2_REG), |
| REG_READ16(host, SDHCI_CLK_CTRL_REG)); |
| DBG("Caps1: 0x%08x\t Caps2: 0x%08x\n", |
| REG_READ32(host, SDHCI_CAPS_REG1), |
| REG_READ32(host, SDHCI_CAPS_REG1)); |
| DBG("Adma Err: 0x%08x\t Auto Cmd err: 0x%08x\n", |
| REG_READ8(host, SDHCI_ADM_ERR_REG), |
| REG_READ16(host, SDHCI_AUTO_CMD_ERR)); |
| DBG("Adma addr1: 0x%08x\t Adma addr2: 0x%08x\n", |
| REG_READ32(host, SDHCI_ADM_ADDR_REG), |
| REG_READ32(host, SDHCI_ADM_ADDR_REG + 0x4)); |
| |
| DBG("****************** SDHC REG DUMP END ********************\n"); |
| |
| DBG("************* SDHC VENDOR REG DUMPS START ***************\n"); |
| DBG("SDCC_DLL_CONFIG_REG: 0x%08x\n", REG_READ32(host, SDCC_DLL_CONFIG_REG)); |
| DBG("SDCC_VENDOR_SPECIFIC_FUNC: 0x%08x\n", REG_READ32(host, SDCC_VENDOR_SPECIFIC_FUNC)); |
| DBG("SDCC_REG_DLL_STATUS: 0x%08x\n", REG_READ32(host, SDCC_REG_DLL_STATUS)); |
| DBG("************* SDHC VENDOR REG DUMPS END ***************\n"); |
| } |
| |
| /* |
| * Function: sdhci reset |
| * Arg : Host structure & mask to write to reset register |
| * Return : None |
| * Flow: : Reset the host controller |
| */ |
| void sdhci_reset(struct sdhci_host *host, uint8_t mask) |
| { |
| uint32_t reg; |
| uint32_t timeout = SDHCI_RESET_MAX_TIMEOUT; |
| |
| REG_WRITE8(host, mask, SDHCI_RESET_REG); |
| |
| /* Wait for the reset to complete */ |
| do { |
| reg = REG_READ8(host, SDHCI_RESET_REG); |
| reg &= mask; |
| |
| if (!reg) |
| break; |
| if (!timeout) |
| { |
| dprintf(CRITICAL, "Error: sdhci reset failed for: %x\n", mask); |
| break; |
| } |
| |
| timeout--; |
| mdelay(1); |
| |
| } while(1); |
| } |
| |
| /* |
| * Function: sdhci error status enable |
| * Arg : Host structure |
| * Return : None |
| * Flow: : Enable command error status |
| */ |
| static void sdhci_error_status_enable(struct sdhci_host *host) |
| { |
| /* Enable all interrupt status */ |
| REG_WRITE16(host, SDHCI_NRML_INT_STS_EN, SDHCI_NRML_INT_STS_EN_REG); |
| REG_WRITE16(host, SDHCI_ERR_INT_STS_EN, SDHCI_ERR_INT_STS_EN_REG); |
| /* Enable all interrupt signal */ |
| REG_WRITE16(host, SDHCI_NRML_INT_SIG_EN, SDHCI_NRML_INT_SIG_EN_REG); |
| REG_WRITE16(host, SDHCI_ERR_INT_SIG_EN, SDHCI_ERR_INT_SIG_EN_REG); |
| } |
| |
| /* |
| * Function: sdhci clock supply |
| * Arg : Host structure |
| * Return : 0 on Success, 1 on Failure |
| * Flow: : 1. Calculate the clock divider |
| * 2. Set the clock divider |
| * 3. Check if clock stable |
| * 4. Enable Clock |
| */ |
| uint32_t sdhci_clk_supply(struct sdhci_host *host, uint32_t clk) |
| { |
| uint32_t div = 0; |
| uint32_t freq = 0; |
| uint16_t clk_val = 0; |
| |
| if (clk >= host->caps.base_clk_rate) |
| goto clk_ctrl; |
| |
| /* As per the sd spec div should be a multiplier of 2 */ |
| for (div = 2; div < SDHCI_CLK_MAX_DIV; div += 2) { |
| freq = host->caps.base_clk_rate / div; |
| if (freq <= clk) |
| break; |
| } |
| |
| div >>= 1; |
| |
| clk_ctrl: |
| /* As per the sdhci spec 3.0, bits 6-7 of the clock |
| * control registers will be mapped to bit 8-9, to |
| * support a 10 bit divider value. |
| * This is needed when the divider value overflows |
| * the 8 bit range. |
| */ |
| clk_val = ((div & SDHCI_SDCLK_FREQ_MASK) << SDHCI_SDCLK_FREQ_SEL); |
| clk_val |= ((div & SDHC_SDCLK_UP_BIT_MASK) >> SDHCI_SDCLK_FREQ_SEL) |
| << SDHCI_SDCLK_UP_BIT_SEL; |
| |
| clk_val |= SDHCI_INT_CLK_EN; |
| REG_WRITE16(host, clk_val, SDHCI_CLK_CTRL_REG); |
| |
| /* Check for clock stable */ |
| while (!(REG_READ16(host, SDHCI_CLK_CTRL_REG) & SDHCI_CLK_STABLE)); |
| |
| /* Now clock is stable, enable it */ |
| clk_val = REG_READ16(host, SDHCI_CLK_CTRL_REG); |
| clk_val |= SDHCI_CLK_EN; |
| REG_WRITE16(host, clk_val, SDHCI_CLK_CTRL_REG); |
| |
| host->cur_clk_rate = clk; |
| |
| DBG("\n %s: clock_rate: %d clock_div:0x%08x\n", __func__, clk, div); |
| |
| return 0; |
| } |
| |
| /* |
| * Function: sdhci stop sdcc clock |
| * Arg : Host structure |
| * Return : 0 on Success, 1 on Failure |
| * Flow: : 1. Stop the clock |
| */ |
| static uint32_t sdhci_stop_sdcc_clk(struct sdhci_host *host) |
| { |
| uint32_t reg; |
| |
| reg = REG_READ32(host, SDHCI_PRESENT_STATE_REG); |
| |
| if (reg & (SDHCI_CMD_ACT | SDHCI_DAT_ACT)) { |
| dprintf(CRITICAL, "Error: SDCC command & data line are active\n"); |
| return 1; |
| } |
| |
| REG_WRITE16(host, SDHCI_CLK_DIS, SDHCI_CLK_CTRL_REG); |
| |
| return 0; |
| } |
| |
| /* |
| * Function: sdhci change frequency |
| * Arg : Host structure & clock value |
| * Return : 0 on Success, 1 on Failure |
| * Flow: : 1. Stop the clock |
| * 2. Star the clock with new frequency |
| */ |
| static uint32_t sdhci_change_freq_clk(struct sdhci_host *host, uint32_t clk) |
| { |
| if (sdhci_stop_sdcc_clk(host)) { |
| dprintf(CRITICAL, "Error: Card is busy, cannot change frequency\n"); |
| return 1; |
| } |
| |
| if (sdhci_clk_supply(host, clk)) { |
| dprintf(CRITICAL, "Error: cannot change frequency\n"); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Function: sdhci set bus power |
| * Arg : Host structure |
| * Return : None |
| * Flow: : 1. Set the voltage |
| * 2. Set the sd power control register |
| */ |
| static void sdhci_set_bus_power_on(struct sdhci_host *host) |
| { |
| uint8_t voltage; |
| |
| voltage = host->caps.voltage; |
| |
| voltage <<= SDHCI_BUS_VOL_SEL; |
| REG_WRITE8(host, voltage, SDHCI_PWR_CTRL_REG); |
| |
| voltage |= SDHCI_BUS_PWR_EN; |
| |
| DBG("\n %s: voltage: 0x%02x\n", __func__, voltage); |
| |
| REG_WRITE8(host, voltage, SDHCI_PWR_CTRL_REG); |
| |
| } |
| |
| |
| /* |
| * Function: sdhci set SDR mode |
| * Arg : Host structure, UHS mode |
| * Return : None |
| * Flow: : 1. Disable the clock |
| * 2. Enable UHS mode |
| * 3. Enable the clock |
| * Details : SDR50/SDR104 mode is nothing but HS200 |
| * mode SDCC spec refers to it as SDR mode |
| * & emmc spec refers as HS200 mode. |
| */ |
| void sdhci_set_uhs_mode(struct sdhci_host *host, uint32_t mode) |
| { |
| uint16_t clk; |
| uint16_t ctrl = 0; |
| uint32_t clk_val = 0; |
| |
| /* Disable the clock */ |
| clk = REG_READ16(host, SDHCI_CLK_CTRL_REG); |
| clk &= ~SDHCI_CLK_EN; |
| REG_WRITE16(host, clk, SDHCI_CLK_CTRL_REG); |
| |
| ctrl = REG_READ16(host, SDHCI_HOST_CTRL2_REG); |
| |
| ctrl &= ~SDHCI_UHS_MODE_MASK; |
| |
| /* Enable SDR50/SDR104/DDR50 mode */ |
| switch (mode) |
| { |
| case SDHCI_SDR104_MODE: |
| ctrl |= SDHCI_SDR104_MODE_EN; |
| clk_val = SDHCI_CLK_200MHZ; |
| break; |
| case SDHCI_SDR50_MODE: |
| ctrl |= SDHCI_SDR50_MODE_EN; |
| clk_val = SDHCI_CLK_100MHZ; |
| break; |
| case SDHCI_DDR50_MODE: |
| ctrl |= SDHCI_DDR50_MODE_EN; |
| clk_val = SDHCI_CLK_50MHZ; |
| break; |
| case SDHCI_SDR25_MODE: |
| ctrl |= SDHCI_SDR25_MODE_EN; |
| clk_val = SDHCI_CLK_50MHZ; |
| break; |
| case SDHCI_SDR12_MODE_EN: |
| ctrl |= SDHCI_SDR12_MODE_EN; |
| clk_val = SDHCI_CLK_25MHZ; |
| break; |
| default: |
| dprintf(CRITICAL, "Error: Invalid UHS mode: %x\n", mode); |
| ASSERT(0); |
| }; |
| |
| REG_WRITE16(host, ctrl, SDHCI_HOST_CTRL2_REG); |
| |
| /* Run the clock back */ |
| sdhci_clk_supply(host, clk_val); |
| } |
| |
| /* |
| * Function: sdhci set adma mode |
| * Arg : Host structure |
| * Return : None |
| * Flow: : Set adma mode |
| */ |
| static void sdhci_set_adma_mode(struct sdhci_host *host) |
| { |
| /* Select 32 Bit ADMA2 type */ |
| REG_WRITE8(host, SDHCI_ADMA_32BIT, SDHCI_HOST_CTRL1_REG); |
| } |
| |
| /* |
| * Function: sdhci set bus width |
| * Arg : Host & width |
| * Return : 0 on Sucess, 1 on Failure |
| * Flow: : Set the bus width for controller |
| */ |
| uint8_t sdhci_set_bus_width(struct sdhci_host *host, uint16_t width) |
| { |
| uint16_t reg = 0; |
| |
| reg = REG_READ8(host, SDHCI_HOST_CTRL1_REG); |
| |
| switch(width) { |
| case DATA_BUS_WIDTH_8BIT: |
| width = SDHCI_BUS_WITDH_8BIT; |
| break; |
| case DATA_BUS_WIDTH_4BIT: |
| width = SDHCI_BUS_WITDH_4BIT; |
| break; |
| case DATA_BUS_WIDTH_1BIT: |
| width = SDHCI_BUS_WITDH_1BIT; |
| break; |
| default: |
| dprintf(CRITICAL, "Bus width is invalid: %u\n", width); |
| return 1; |
| } |
| |
| DBG("\n %s: bus width:0x%04x\n", __func__, width); |
| |
| REG_WRITE8(host, (reg | width), SDHCI_HOST_CTRL1_REG); |
| |
| return 0; |
| } |
| |
| /* |
| * Function: sdhci command err status |
| * Arg : Host structure |
| * Return : 0 on Sucess, 1 on Failure |
| * Flow: : Look for error status |
| */ |
| static uint8_t sdhci_cmd_err_status(struct sdhci_host *host) |
| { |
| uint32_t err; |
| |
| err = REG_READ16(host, SDHCI_ERR_INT_STS_REG); |
| |
| if (err & SDHCI_CMD_TIMEOUT_MASK) { |
| dprintf(CRITICAL, "Error: Command timeout error\n"); |
| return 1; |
| } else if (err & SDHCI_CMD_CRC_MASK) { |
| dprintf(CRITICAL, "Error: Command CRC error\n"); |
| return 1; |
| } else if (err & SDHCI_CMD_END_BIT_MASK) { |
| dprintf(CRITICAL, "Error: CMD end bit error\n"); |
| return 1; |
| } else if (err & SDHCI_CMD_IDX_MASK) { |
| dprintf(CRITICAL, "Error: Command Index error\n"); |
| return 1; |
| } else if (err & SDHCI_DAT_TIMEOUT_MASK) { |
| dprintf(CRITICAL, "Error: DATA time out error\n"); |
| return 1; |
| } else if (err & SDHCI_DAT_CRC_MASK) { |
| dprintf(CRITICAL, "Error: DATA CRC error\n"); |
| return 1; |
| } else if (err & SDHCI_DAT_END_BIT_MASK) { |
| dprintf(CRITICAL, "Error: DATA end bit error\n"); |
| return 1; |
| } else if (err & SDHCI_CUR_LIM_MASK) { |
| dprintf(CRITICAL, "Error: Current limit error\n"); |
| return 1; |
| } else if (err & SDHCI_AUTO_CMD12_MASK) { |
| dprintf(CRITICAL, "Error: Auto CMD12 error\n"); |
| return 1; |
| } else if (err & SDHCI_ADMA_MASK) { |
| dprintf(CRITICAL, "Error: ADMA error\n"); |
| return 1; |
| } |
| |
| return 0; |
| } |
| |
| /* |
| * Function: sdhci command complete |
| * Arg : Host & command structure |
| * Return : 0 on Sucess, 1 on Failure |
| * Flow: : 1. Check for command complete |
| * 2. Check for transfer complete |
| * 3. Get the command response |
| * 4. Check for errors |
| */ |
| static uint8_t sdhci_cmd_complete(struct sdhci_host *host, struct mmc_command *cmd) |
| { |
| uint8_t i; |
| uint8_t ret = 0; |
| uint8_t need_reset = 0; |
| uint64_t retry = 0; |
| uint32_t int_status; |
| uint32_t trans_complete = 0; |
| uint32_t err_status; |
| uint64_t max_trans_retry = (cmd->cmd_timeout ? cmd->cmd_timeout : SDHCI_MAX_TRANS_RETRY); |
| |
| do { |
| int_status = REG_READ16(host, SDHCI_NRML_INT_STS_REG); |
| |
| if (int_status & SDHCI_INT_STS_CMD_COMPLETE) |
| break; |
| /* |
| * Some controllers set the data timout first on issuing an erase & take time |
| * to set data complete interrupt. We need to wait hoping the controller would |
| * set data complete |
| */ |
| else if (int_status & SDHCI_ERR_INT_STAT_MASK && !host->tuning_in_progress && |
| !((REG_READ16(host, SDHCI_ERR_INT_STS_REG) & SDHCI_DAT_TIMEOUT_MASK))) |
| goto err; |
| |
| /* |
| * If Tuning is in progress ignore cmd crc, cmd timeout & cmd end bit errors |
| */ |
| if (host->tuning_in_progress) |
| { |
| err_status = REG_READ16(host, SDHCI_ERR_INT_STS_REG); |
| if ((err_status & SDHCI_CMD_CRC_MASK) || (err_status & SDHCI_CMD_END_BIT_MASK) |
| || (err_status & SDHCI_CMD_TIMEOUT_MASK) |
| || (err_status & SDHCI_CMD_IDX_MASK)) |
| { |
| sdhci_reset(host, (SOFT_RESET_CMD | SOFT_RESET_DATA)); |
| return 1; |
| } |
| } |
| |
| retry++; |
| udelay(1); |
| if (retry == SDHCI_MAX_CMD_RETRY) { |
| dprintf(CRITICAL, "Error: Command never completed\n"); |
| ret = 1; |
| goto err; |
| } |
| } while(1); |
| |
| /* Command is complete, clear the interrupt bit */ |
| REG_WRITE16(host, SDHCI_INT_STS_CMD_COMPLETE, SDHCI_NRML_INT_STS_REG); |
| |
| /* Copy the command response, |
| * The valid bits for R2 response are 0-119, & but the actual response |
| * is stored in bits 8-128. We need to move 8 bits of MSB of each |
| * response to register 8 bits of LSB of next response register. |
| * As: |
| * MSB 8 bits of RESP0 --> LSB 8 bits of RESP1 |
| * MSB 8 bits of RESP1 --> LSB 8 bits of RESP2 |
| * MSB 8 bits of RESP2 --> LSB 8 bits of RESP3 |
| */ |
| if (cmd->resp_type == SDHCI_CMD_RESP_R2) { |
| for (i = 0; i < 4; i++) { |
| cmd->resp[i] = REG_READ32(host, SDHCI_RESP_REG + (i * 4)); |
| cmd->resp[i] <<= SDHCI_RESP_LSHIFT; |
| |
| if (i != 0) |
| cmd->resp[i] |= (REG_READ32(host, SDHCI_RESP_REG + ((i-1) * 4)) >> SDHCI_RESP_RSHIFT); |
| } |
| } else |
| cmd->resp[0] = REG_READ32(host, SDHCI_RESP_REG); |
| |
| retry = 0; |
| |
| /* |
| * Clear the transfer complete interrupt |
| */ |
| if (cmd->data_present || cmd->resp_type == SDHCI_CMD_RESP_R1B) { |
| do { |
| int_status = REG_READ16(host, SDHCI_NRML_INT_STS_REG); |
| |
| if (int_status & SDHCI_INT_STS_TRANS_COMPLETE) |
| { |
| trans_complete = 1; |
| break; |
| } |
| /* |
| * Some controllers set the data timout first on issuing an erase & take time |
| * to set data complete interrupt. We need to wait hoping the controller would |
| * set data complete |
| */ |
| else if (int_status & SDHCI_ERR_INT_STAT_MASK && !host->tuning_in_progress && |
| !((REG_READ16(host, SDHCI_ERR_INT_STS_REG) & SDHCI_DAT_TIMEOUT_MASK))) |
| goto err; |
| |
| /* |
| * If we are in tuning then we need to wait until Data timeout , Data end |
| * or Data CRC error |
| */ |
| if (host->tuning_in_progress) |
| { |
| err_status = REG_READ16(host, SDHCI_ERR_INT_STS_REG); |
| if ((err_status & SDHCI_DAT_TIMEOUT_MASK) || (err_status & SDHCI_DAT_CRC_MASK)) |
| { |
| sdhci_reset(host, (SOFT_RESET_CMD | SOFT_RESET_DATA)); |
| return 1; |
| } |
| } |
| |
| retry++; |
| udelay(1); |
| if (retry == max_trans_retry) { |
| dprintf(CRITICAL, "Error: Transfer never completed\n"); |
| ret = 1; |
| goto err; |
| } |
| } while(1); |
| |
| /* Transfer is complete, clear the interrupt bit */ |
| REG_WRITE16(host, SDHCI_INT_STS_TRANS_COMPLETE, SDHCI_NRML_INT_STS_REG); |
| } |
| |
| err: |
| /* Look for errors */ |
| int_status = REG_READ16(host, SDHCI_NRML_INT_STS_REG); |
| |
| if (int_status & SDHCI_ERR_INT_STAT_MASK) |
| { |
| /* |
| * As per SDHC spec transfer complete has higher priority than data timeout |
| * If both transfer complete & data timeout are set then we should ignore |
| * data timeout error. |
| * --------------------------------------------------------------------------- |
| * | Transfer complete | Data timeout error | Meaning of the Status | |
| * |--------------------------------------------------------------------------| |
| * | 0 | 0 | Interrupted by another factor | |
| * |--------------------------------------------------------------------------| |
| * | 0 | 1 | Time out occured during transfer| |
| * |--------------------------------------------------------------------------| |
| * | 1 | Don't Care | Command execution complete | |
| * -------------------------------------------------------------------------- |
| */ |
| if ((REG_READ16(host, SDHCI_ERR_INT_STS_REG) & SDHCI_DAT_TIMEOUT_MASK) && trans_complete) |
| { |
| ret = 0; |
| } |
| else if (sdhci_cmd_err_status(host)) |
| { |
| ret = 1; |
| /* Dump sdhc registers on error */ |
| sdhci_dumpregs(host); |
| } |
| /* Reset Command & Dat lines on error */ |
| need_reset = 1; |
| } |
| |
| /* Reset data & command line */ |
| if (need_reset) |
| sdhci_reset(host, (SOFT_RESET_CMD | SOFT_RESET_DATA)); |
| |
| return ret; |
| } |
| |
| /* |
| * Function: sdhci prep desc table |
| * Arg : Pointer data & length |
| * Return : Pointer to desc table |
| * Flow: : Prepare the adma table as per the sd spec v 3.0 |
| */ |
| static struct desc_entry *sdhci_prep_desc_table(void *data, uint32_t len) |
| { |
| struct desc_entry *sg_list; |
| uint32_t sg_len = 0; |
| uint32_t remain = 0; |
| uint32_t i; |
| uint32_t table_len = 0; |
| |
| if (len <= SDHCI_ADMA_DESC_LINE_SZ) { |
| /* Allocate only one descriptor */ |
| sg_list = (struct desc_entry *) memalign(lcm(4, CACHE_LINE), ROUNDUP(sizeof(struct desc_entry), CACHE_LINE)); |
| |
| if (!sg_list) { |
| dprintf(CRITICAL, "Error allocating memory\n"); |
| ASSERT(0); |
| } |
| |
| sg_list[0].addr = (uint32_t)data; |
| sg_list[0].len = (len < SDHCI_ADMA_DESC_LINE_SZ) ? len : (SDHCI_ADMA_DESC_LINE_SZ & 0xffff); |
| sg_list[0].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA |
| | SDHCI_ADMA_TRANS_END; |
| |
| sg_len = 1; |
| table_len = sizeof(struct desc_entry); |
| } else { |
| /* Calculate the number of entries in desc table */ |
| sg_len = len / SDHCI_ADMA_DESC_LINE_SZ; |
| remain = len - (sg_len * SDHCI_ADMA_DESC_LINE_SZ); |
| |
| /* Allocate sg_len + 1 entries if there are remaining bytes at the end */ |
| if (remain) |
| sg_len++; |
| |
| table_len = (sg_len * sizeof(struct desc_entry)); |
| |
| sg_list = (struct desc_entry *) memalign(lcm(4, CACHE_LINE), ROUNDUP(table_len, CACHE_LINE)); |
| |
| if (!sg_list) { |
| dprintf(CRITICAL, "Error allocating memory\n"); |
| ASSERT(0); |
| } |
| |
| memset((void *) sg_list, 0, table_len); |
| |
| /* |
| * Prepare sglist in the format: |
| * ___________________________________________________ |
| * |Transfer Len | Transfer ATTR | Data Address | |
| * | (16 bit) | (16 bit) | (32 bit) | |
| * |_____________|_______________|_____________________| |
| */ |
| for (i = 0; i < (sg_len - 1); i++) { |
| sg_list[i].addr = (uint32_t)data; |
| /* |
| * Length attribute is 16 bit value & max transfer size for one |
| * descriptor line is 65536 bytes, As per SD Spec3.0 'len = 0' |
| * implies 65536 bytes. Truncate the length to limit to 16 bit |
| * range. |
| */ |
| sg_list[i].len = (SDHCI_ADMA_DESC_LINE_SZ & 0xffff); |
| sg_list[i].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA; |
| data += SDHCI_ADMA_DESC_LINE_SZ; |
| len -= SDHCI_ADMA_DESC_LINE_SZ; |
| } |
| |
| /* Fill the last entry of the table with Valid & End |
| * attributes |
| */ |
| sg_list[sg_len - 1].addr = (uint32_t)data; |
| sg_list[sg_len - 1].len = (len < SDHCI_ADMA_DESC_LINE_SZ) ? len : (SDHCI_ADMA_DESC_LINE_SZ & 0xffff); |
| sg_list[sg_len - 1].tran_att = SDHCI_ADMA_TRANS_VALID | SDHCI_ADMA_TRANS_DATA | |
| SDHCI_ADMA_TRANS_END; |
| } |
| |
| arch_clean_invalidate_cache_range((addr_t)sg_list, table_len); |
| |
| for (i = 0; i < sg_len; i++) |
| { |
| DBG("\n %s: sg_list: addr: 0x%08x len: 0x%04x attr: 0x%04x\n", __func__, sg_list[i].addr, |
| (sg_list[i].len ? sg_list[i].len : SDHCI_ADMA_DESC_LINE_SZ), sg_list[i].tran_att); |
| } |
| |
| return sg_list; |
| } |
| |
| /* |
| * Function: sdhci adma transfer |
| * Arg : Host structure & command stucture |
| * Return : Pointer to desc table |
| * Flow : 1. Prepare descriptor table |
| * 2. Write adma register |
| * 3. Write block size & block count register |
| */ |
| static struct desc_entry *sdhci_adma_transfer(struct sdhci_host *host, |
| struct mmc_command *cmd) |
| { |
| uint32_t num_blks = 0; |
| uint32_t sz; |
| void *data; |
| struct desc_entry *adma_addr; |
| |
| |
| num_blks = cmd->data.num_blocks; |
| data = cmd->data.data_ptr; |
| |
| /* |
| * Some commands send data on DAT lines which is less |
| * than SDHCI_MMC_BLK_SZ, in that case trying to read |
| * more than the data sent by the card results in data |
| * CRC errors. To avoid such errors allow data to pass |
| * the required block size, if the block size is not |
| * passed use the default value |
| */ |
| if (cmd->data.blk_sz) |
| sz = num_blks * cmd->data.blk_sz; |
| else |
| sz = num_blks * SDHCI_MMC_BLK_SZ; |
| |
| /* Prepare adma descriptor table */ |
| adma_addr = sdhci_prep_desc_table(data, sz); |
| |
| /* Write adma address to adma register */ |
| REG_WRITE32(host, (uint32_t) adma_addr, SDHCI_ADM_ADDR_REG); |
| |
| /* Write the block size */ |
| if (cmd->data.blk_sz) |
| REG_WRITE16(host, cmd->data.blk_sz, SDHCI_BLKSZ_REG); |
| else |
| REG_WRITE16(host, SDHCI_MMC_BLK_SZ, SDHCI_BLKSZ_REG); |
| |
| /* |
| * Set block count in block count register |
| */ |
| REG_WRITE16(host, num_blks, SDHCI_BLK_CNT_REG); |
| |
| return adma_addr; |
| } |
| |
| /* |
| * Function: sdhci send command |
| * Arg : Host structure & command stucture |
| * Return : 0 on Success, 1 on Failure |
| * Flow: : 1. Prepare the command register |
| * 2. If data is present, prepare adma table |
| * 3. Run the command |
| * 4. Check for command results & take action |
| */ |
| uint32_t sdhci_send_command(struct sdhci_host *host, struct mmc_command *cmd) |
| { |
| uint32_t ret = 0; |
| uint8_t retry = 0; |
| uint32_t resp_type = 0; |
| uint16_t trans_mode = 0; |
| uint16_t present_state; |
| uint32_t flags; |
| struct desc_entry *sg_list = NULL; |
| |
| DBG("\n %s: START: cmd:%04d, arg:0x%08x, resp_type:0x%04x, data_present:%d\n", |
| __func__, cmd->cmd_index, cmd->argument, cmd->resp_type, cmd->data_present); |
| |
| if (cmd->data_present) |
| ASSERT(cmd->data.data_ptr); |
| |
| /* |
| * Assert if the data buffer is not aligned to cache |
| * line size for read operations. |
| * For write operations this function assumes that |
| * the cache is already flushed by the caller. As |
| * the data buffer we receive for write operation |
| * may not be aligned to cache boundary due to |
| * certain image formats like sparse image. |
| */ |
| if (cmd->trans_mode == SDHCI_READ_MODE) |
| ASSERT(IS_CACHE_LINE_ALIGNED(cmd->data.data_ptr)); |
| |
| do { |
| present_state = REG_READ32(host, SDHCI_PRESENT_STATE_REG); |
| /* check if CMD & DAT lines are free */ |
| present_state &= SDHCI_STATE_CMD_DAT_MASK; |
| |
| if (!present_state) |
| break; |
| udelay(1000); |
| retry++; |
| if (retry == 10) { |
| dprintf(CRITICAL, "Error: CMD or DAT lines were never freed\n"); |
| return 1; |
| } |
| } while(1); |
| |
| switch(cmd->resp_type) { |
| case SDHCI_CMD_RESP_R1: |
| case SDHCI_CMD_RESP_R3: |
| case SDHCI_CMD_RESP_R6: |
| case SDHCI_CMD_RESP_R7: |
| /* Response of length 48 have 32 bits |
| * of response data stored in RESP0[0:31] |
| */ |
| resp_type = SDHCI_CMD_RESP_48; |
| break; |
| |
| case SDHCI_CMD_RESP_R2: |
| /* Response of length 136 have 120 bits |
| * of response data stored in RESP0[0:119] |
| */ |
| resp_type = SDHCI_CMD_RESP_136; |
| break; |
| |
| case SDHCI_CMD_RESP_R1B: |
| /* Response of length 48 have 32 bits |
| * of response data stored in RESP0[0:31] |
| * & set CARD_BUSY status if card is busy |
| */ |
| resp_type = SDHCI_CMD_RESP_48_BUSY; |
| break; |
| |
| case SDHCI_CMD_RESP_NONE: |
| resp_type = SDHCI_CMD_RESP_NONE; |
| break; |
| |
| default: |
| dprintf(CRITICAL, "Invalid response type for the command\n"); |
| return 1; |
| }; |
| |
| flags = (resp_type << SDHCI_CMD_RESP_TYPE_SEL_BIT); |
| flags |= (cmd->data_present << SDHCI_CMD_DATA_PRESENT_BIT); |
| flags |= (cmd->cmd_type << SDHCI_CMD_CMD_TYPE_BIT); |
| |
| /* Enable Command CRC & Index check for commands with response |
| * R1, R6, R7 & R1B. Also only CRC check for R2 response |
| */ |
| switch(cmd->resp_type) { |
| case SDHCI_CMD_RESP_R1: |
| case SDHCI_CMD_RESP_R6: |
| case SDHCI_CMD_RESP_R7: |
| case SDHCI_CMD_RESP_R1B: |
| flags |= (1 << SDHCI_CMD_CRC_CHECK_BIT) | (1 << SDHCI_CMD_IDX_CHECK_BIT); |
| break; |
| case SDHCI_CMD_RESP_R2: |
| flags |= (1 << SDHCI_CMD_CRC_CHECK_BIT); |
| break; |
| default: |
| break; |
| }; |
| |
| /* Set the timeout value */ |
| REG_WRITE8(host, SDHCI_CMD_TIMEOUT, SDHCI_TIMEOUT_REG); |
| |
| /* Check if data needs to be processed */ |
| if (cmd->data_present) |
| sg_list = sdhci_adma_transfer(host, cmd); |
| |
| /* Write the argument 1 */ |
| REG_WRITE32(host, cmd->argument, SDHCI_ARGUMENT_REG); |
| |
| /* Set the Transfer mode */ |
| if (cmd->data_present) |
| { |
| /* Enable DMA */ |
| trans_mode |= SDHCI_DMA_EN; |
| |
| if (cmd->trans_mode == SDHCI_MMC_READ) |
| { |
| trans_mode |= SDHCI_READ_MODE; |
| if(cmd->cmd_index == CMD21_SEND_TUNING_BLOCK) |
| sdhci_msm_toggle_cdr(host, false); |
| else |
| sdhci_msm_toggle_cdr(host, true); |
| } |
| else |
| { |
| sdhci_msm_toggle_cdr(host, false); |
| } |
| |
| /* Enable auto cmd23 or cmd12 for multi block transfer |
| * based on what command card supports |
| */ |
| if ((cmd->data.num_blocks > 1) && !cmd->rel_write) { |
| if (cmd->cmd23_support) { |
| trans_mode |= SDHCI_TRANS_MULTI | SDHCI_AUTO_CMD23_EN | SDHCI_BLK_CNT_EN; |
| REG_WRITE32(host, cmd->data.num_blocks, SDHCI_ARG2_REG); |
| } |
| else |
| trans_mode |= SDHCI_TRANS_MULTI | SDHCI_AUTO_CMD12_EN | SDHCI_BLK_CNT_EN; |
| } |
| else if ((cmd->data.num_blocks > 1) && cmd->rel_write) { |
| trans_mode |= SDHCI_TRANS_MULTI | SDHCI_BLK_CNT_EN; |
| } |
| } |
| |
| /* Write to transfer mode register */ |
| REG_WRITE16(host, trans_mode, SDHCI_TRANS_MODE_REG); |
| |
| /* Write the command register */ |
| REG_WRITE16(host, SDHCI_PREP_CMD(cmd->cmd_index, flags), SDHCI_CMD_REG); |
| |
| /* Command complete sequence */ |
| if (sdhci_cmd_complete(host, cmd)) |
| { |
| ret = 1; |
| goto err; |
| } |
| |
| /* Invalidate the cache only for read operations */ |
| if (cmd->trans_mode == SDHCI_MMC_READ) |
| { |
| /* Read can be performed on block size < SDHCI_MMC_BLK_SZ, make sure to flush |
| * the data only for the read size instead |
| */ |
| arch_invalidate_cache_range((addr_t)cmd->data.data_ptr, (cmd->data.blk_sz) ? \ |
| (cmd->data.num_blocks * cmd->data.blk_sz) : \ |
| (cmd->data.num_blocks * SDHCI_MMC_BLK_SZ)); |
| } |
| |
| DBG("\n %s: END: cmd:%04d, arg:0x%08x, resp:0x%08x 0x%08x 0x%08x 0x%08x\n", |
| __func__, cmd->cmd_index, cmd->argument, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); |
| err: |
| /* Free the scatter/gather list */ |
| if (sg_list) |
| free(sg_list); |
| |
| return ret; |
| } |
| |
| /* |
| * Function: sdhci init |
| * Arg : Host structure |
| * Return : None |
| * Flow: : 1. Reset the controller |
| * 2. Read the capabilities register & populate the host |
| * controller capabilities for use by other functions |
| * 3. Enable the power control |
| * 4. Set initial bus width |
| * 5. Set Adma mode |
| * 6. Enable the error status |
| */ |
| void sdhci_init(struct sdhci_host *host) |
| { |
| uint32_t caps[2]; |
| uint32_t version; |
| |
| /* Read the capabilities register & store the info */ |
| caps[0] = REG_READ32(host, SDHCI_CAPS_REG1); |
| caps[1] = REG_READ32(host, SDHCI_CAPS_REG2); |
| |
| |
| DBG("\n %s: Host capability: cap1:0x%08x, cap2: 0x%08x\n", __func__, caps[0], caps[1]); |
| |
| host->caps.base_clk_rate = (caps[0] & SDHCI_CLK_RATE_MASK) >> SDHCI_CLK_RATE_BIT; |
| host->caps.base_clk_rate *= 1000000; |
| |
| /* Get the max block length for mmc */ |
| host->caps.max_blk_len = (caps[0] & SDHCI_BLK_LEN_MASK) >> SDHCI_BLK_LEN_BIT; |
| |
| /* 8 bit Bus width */ |
| if (caps[0] & SDHCI_8BIT_WIDTH_MASK) |
| host->caps.bus_width_8bit = 1; |
| |
| /* Adma support */ |
| if (caps[0] & SDHCI_BLK_ADMA_MASK) |
| host->caps.adma_support = 1; |
| |
| /* Supported voltage */ |
| if (caps[0] & SDHCI_3_3_VOL_MASK) |
| host->caps.voltage = SDHCI_VOL_3_3; |
| else if (caps[0] & SDHCI_3_0_VOL_MASK) |
| host->caps.voltage = SDHCI_VOL_3_0; |
| else if (caps[0] & SDHCI_1_8_VOL_MASK) |
| host->caps.voltage = SDHCI_VOL_1_8; |
| |
| /* DDR mode support */ |
| host->caps.ddr_support = (caps[1] & SDHCI_DDR50_MODE_MASK) ? 1 : 0; |
| |
| /* SDR50 mode support */ |
| host->caps.sdr50_support = (caps[1] & SDHCI_SDR50_MODE_MASK) ? 1 : 0; |
| |
| /* SDR104 mode support */ |
| host->caps.sdr104_support = (caps[1] & SDHCI_SDR104_MODE_MASK) ? 1 : 0; |
| |
| version = readl(host->msm_host->pwrctl_base + MCI_VERSION); |
| |
| host->major = (version & CORE_VERSION_MAJOR_MASK) >> CORE_VERSION_MAJOR_SHIFT; |
| host->minor = (version & CORE_VERSION_MINOR_MASK); |
| |
| if (host->major == 0x1 && host->minor < 0x34) |
| host->use_cdclp533 = true; |
| else |
| host->use_cdclp533 = false; |
| |
| /* Set bus power on */ |
| sdhci_set_bus_power_on(host); |
| |
| /* Wait for power interrupt to be handled */ |
| event_wait(host->sdhc_event); |
| |
| /* Set bus width */ |
| sdhci_set_bus_width(host, SDHCI_BUS_WITDH_1BIT); |
| |
| /* Set Adma mode */ |
| sdhci_set_adma_mode(host); |
| |
| /* |
| * Enable error status |
| */ |
| sdhci_error_status_enable(host); |
| } |