diff options
Diffstat (limited to 'drivers/renesas/common/emmc/emmc_cmd.c')
-rw-r--r-- | drivers/renesas/common/emmc/emmc_cmd.c | 493 |
1 files changed, 493 insertions, 0 deletions
diff --git a/drivers/renesas/common/emmc/emmc_cmd.c b/drivers/renesas/common/emmc/emmc_cmd.c new file mode 100644 index 0000000..d255bff --- /dev/null +++ b/drivers/renesas/common/emmc/emmc_cmd.c @@ -0,0 +1,493 @@ +/* + * Copyright (c) 2015-2020, Renesas Electronics Corporation. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include <common/debug.h> + +#include "emmc_config.h" +#include "emmc_def.h" +#include "emmc_hal.h" +#include "emmc_registers.h" +#include "emmc_std.h" +#include "micro_delay.h" + +static void emmc_little_to_big(uint8_t *p, uint32_t value) +{ + if (p == NULL) + return; + + p[0] = (uint8_t) (value >> 24); + p[1] = (uint8_t) (value >> 16); + p[2] = (uint8_t) (value >> 8); + p[3] = (uint8_t) value; + +} + +static void emmc_softreset(void) +{ + int32_t loop = 10000; + int32_t retry = 1000; + + /* flag clear */ + mmc_drv_obj.during_cmd_processing = FALSE; + mmc_drv_obj.during_transfer = FALSE; + mmc_drv_obj.during_dma_transfer = FALSE; + mmc_drv_obj.state_machine_blocking = FALSE; + mmc_drv_obj.force_terminate = FALSE; + mmc_drv_obj.dma_error_flag = FALSE; + + /* during operation ? */ + if ((GETR_32(SD_INFO2) & SD_INFO2_CBSY) == 0) + goto reset; + + /* wait CMDSEQ = 0 */ + while (loop > 0) { + if ((GETR_32(SD_INFO2) & SD_INFO2_CBSY) == 0) + break; /* ready */ + + loop--; + if ((loop == 0) && (retry > 0)) { + rcar_micro_delay(1000U); /* wait 1ms */ + loop = 10000; + retry--; + } + } + +reset: + /* reset */ + SETR_32(SOFT_RST, (GETR_32(SOFT_RST) & (~SOFT_RST_SDRST))); + SETR_32(SOFT_RST, (GETR_32(SOFT_RST) | SOFT_RST_SDRST)); + + /* initialize */ + SETR_32(SD_INFO1, 0x00000000U); + SETR_32(SD_INFO2, SD_INFO2_CLEAR); + SETR_32(SD_INFO1_MASK, 0x00000000U); /* all interrupt disable */ + SETR_32(SD_INFO2_MASK, SD_INFO2_CLEAR); /* all interrupt disable */ +} + +static void emmc_read_response(uint32_t *response) +{ + uint8_t *p; + + if (response == NULL) + return; + + /* read response */ + if (mmc_drv_obj.response_length != EMMC_MAX_RESPONSE_LENGTH) { + *response = GETR_32(SD_RSP10); /* [39:8] */ + return; + } + + /* CSD or CID */ + p = (uint8_t *) (response); + emmc_little_to_big(p, ((GETR_32(SD_RSP76) << 8) + | (GETR_32(SD_RSP54) >> 24))); /* [127:96] */ + emmc_little_to_big(p + 4, ((GETR_32(SD_RSP54) << 8) + | (GETR_32(SD_RSP32) >> 24))); /* [95:64] */ + emmc_little_to_big(p + 8, ((GETR_32(SD_RSP32) << 8) + | (GETR_32(SD_RSP10) >> 24))); /* [63:32] */ + emmc_little_to_big(p + 12, (GETR_32(SD_RSP10) << 8)); +} + +static EMMC_ERROR_CODE emmc_response_check(uint32_t *response, + uint32_t error_mask) +{ + + HAL_MEMCARD_RESPONSE_TYPE response_type = + ((HAL_MEMCARD_RESPONSE_TYPE)mmc_drv_obj.cmd_info.cmd & HAL_MEMCARD_RESPONSE_TYPE_MASK); + + if (response == NULL) + return EMMC_ERR_PARAM; + + if (response_type == HAL_MEMCARD_RESPONSE_NONE) + return EMMC_SUCCESS; + + + if (response_type <= HAL_MEMCARD_RESPONSE_R1b) { + /* R1 or R1b */ + mmc_drv_obj.current_state = + (EMMC_R1_STATE) ((*response & EMMC_R1_STATE_MASK) >> + EMMC_R1_STATE_SHIFT); + if ((*response & error_mask) != 0) { + if ((0x80 & *response) != 0) { + ERROR("BL2: emmc SWITCH_ERROR\n"); + } + return EMMC_ERR_CARD_STATUS_BIT; + } + return EMMC_SUCCESS; + } + + if (response_type == HAL_MEMCARD_RESPONSE_R4) { + if ((*response & EMMC_R4_STATUS) != 0) + return EMMC_ERR_CARD_STATUS_BIT; + } + + return EMMC_SUCCESS; +} + +static void emmc_WaitCmd2Cmd_8Cycle(void) +{ + uint32_t dataL, wait = 0; + + dataL = GETR_32(SD_CLK_CTRL); + dataL &= 0x000000FF; + + switch (dataL) { + case 0xFF: + case 0x00: + case 0x01: + case 0x02: + case 0x04: + case 0x08: + case 0x10: + case 0x20: + wait = 10U; + break; + case 0x40: + wait = 20U; + break; + case 0x80: + wait = 30U; + break; + } + + rcar_micro_delay(wait); +} + +static void cmdErrSdInfo2Log(void) +{ + ERROR("BL2: emmc ERR SD_INFO2 = 0x%x\n", mmc_drv_obj.error_info.info2); +} + +static void emmc_data_transfer_dma(void) +{ + mmc_drv_obj.during_dma_transfer = TRUE; + mmc_drv_obj.dma_error_flag = FALSE; + + SETR_32(SD_INFO1_MASK, 0x00000000U); + SETR_32(SD_INFO2_MASK, (SD_INFO2_ALL_ERR | SD_INFO2_CLEAR)); + + /* DMAC setting */ + if (mmc_drv_obj.cmd_info.dir == HAL_MEMCARD_WRITE) { + /* transfer complete interrupt enable */ + SETR_32(DM_CM_INFO1_MASK, + (DM_CM_INFO_MASK_CLEAR | DM_CM_INFO_CH0_ENABLE)); + SETR_32(DM_CM_INFO2_MASK, + (DM_CM_INFO_MASK_CLEAR | DM_CM_INFO_CH0_ENABLE)); + /* BUFF --> FIFO */ + SETR_32(DM_CM_DTRAN_MODE, (DM_CM_DTRAN_MODE_CH0 | + DM_CM_DTRAN_MODE_BIT_WIDTH)); + } else { + /* transfer complete interrupt enable */ + SETR_32(DM_CM_INFO1_MASK, + (DM_CM_INFO_MASK_CLEAR | DM_CM_INFO_CH1_ENABLE)); + SETR_32(DM_CM_INFO2_MASK, + (DM_CM_INFO_MASK_CLEAR | DM_CM_INFO_CH1_ENABLE)); + /* FIFO --> BUFF */ + SETR_32(DM_CM_DTRAN_MODE, (DM_CM_DTRAN_MODE_CH1 + | DM_CM_DTRAN_MODE_BIT_WIDTH)); + } + SETR_32(DM_DTRAN_ADDR, (((uintptr_t) mmc_drv_obj.buff_address_virtual & + DM_DTRAN_ADDR_WRITE_MASK))); + + SETR_32(DM_CM_DTRAN_CTRL, DM_CM_DTRAN_CTRL_START); +} + +EMMC_ERROR_CODE emmc_exec_cmd(uint32_t error_mask, uint32_t *response) +{ + EMMC_ERROR_CODE rtn_code = EMMC_SUCCESS; + HAL_MEMCARD_RESPONSE_TYPE response_type; + HAL_MEMCARD_COMMAND_TYPE cmd_type; + EMMC_INT_STATE state; + uint32_t err_not_care_flag = FALSE; + + /* parameter check */ + if (response == NULL) { + emmc_write_error_info(EMMC_FUNCNO_EXEC_CMD, EMMC_ERR_PARAM); + return EMMC_ERR_PARAM; + } + + /* state check */ + if (mmc_drv_obj.clock_enable != TRUE) { + emmc_write_error_info(EMMC_FUNCNO_EXEC_CMD, EMMC_ERR_STATE); + return EMMC_ERR_STATE; + } + + if (mmc_drv_obj.state_machine_blocking == TRUE) { + emmc_write_error_info(EMMC_FUNCNO_EXEC_CMD, EMMC_ERR); + return EMMC_ERR; + } + + state = ESTATE_BEGIN; + response_type = + ((HAL_MEMCARD_RESPONSE_TYPE)mmc_drv_obj.cmd_info.cmd & + HAL_MEMCARD_RESPONSE_TYPE_MASK); + cmd_type = + ((HAL_MEMCARD_COMMAND_TYPE) mmc_drv_obj.cmd_info.cmd & + HAL_MEMCARD_COMMAND_TYPE_MASK); + + /* state machine */ + while ((mmc_drv_obj.force_terminate != TRUE) && (state != ESTATE_END)) { + /* The interrupt factor flag is observed. */ + emmc_interrupt(); + + /* wait interrupt */ + if (mmc_drv_obj.state_machine_blocking == TRUE) + continue; + + switch (state) { + case ESTATE_BEGIN: + /* Busy check */ + if ((mmc_drv_obj.error_info.info2 & SD_INFO2_CBSY) != 0) { + emmc_write_error_info(EMMC_FUNCNO_EXEC_CMD, + EMMC_ERR_CARD_BUSY); + return EMMC_ERR_CARD_BUSY; + } + + /* clear register */ + SETR_32(SD_INFO1, 0x00000000U); + SETR_32(SD_INFO2, SD_INFO2_CLEAR); + SETR_32(SD_INFO1_MASK, SD_INFO1_INFO0); + SETR_32(SD_INFO2_MASK, + (SD_INFO2_ALL_ERR | SD_INFO2_CLEAR)); + + state = ESTATE_ISSUE_CMD; + /* through */ + + case ESTATE_ISSUE_CMD: + /* ARG */ + SETR_32(SD_ARG, mmc_drv_obj.cmd_info.arg); + /* issue cmd */ + SETR_32(SD_CMD, mmc_drv_obj.cmd_info.hw); + /* Set driver flag */ + mmc_drv_obj.during_cmd_processing = TRUE; + mmc_drv_obj.state_machine_blocking = TRUE; + + if (response_type == HAL_MEMCARD_RESPONSE_NONE) { + state = ESTATE_NON_RESP_CMD; + } else { + state = ESTATE_RCV_RESP; + } + + break; + + case ESTATE_NON_RESP_CMD: + /* interrupt disable */ + SETR_32(SD_INFO1_MASK, 0x00000000U); + SETR_32(SD_INFO2_MASK, SD_INFO2_CLEAR); + + /* check interrupt */ + if ((mmc_drv_obj.int_event2 & SD_INFO2_ALL_ERR) != 0) { + /* error interrupt */ + cmdErrSdInfo2Log(); + rtn_code = EMMC_ERR_INFO2; + state = ESTATE_ERROR; + } else if ((mmc_drv_obj.int_event1 & SD_INFO1_INFO0) == + 0) { + /* not receive expected interrupt */ + rtn_code = EMMC_ERR_RESPONSE; + state = ESTATE_ERROR; + } else { + emmc_WaitCmd2Cmd_8Cycle(); + state = ESTATE_END; + } + break; + + case ESTATE_RCV_RESP: + /* interrupt disable */ + SETR_32(SD_INFO1_MASK, 0x00000000U); + SETR_32(SD_INFO2_MASK, SD_INFO2_CLEAR); + + /* check interrupt */ + if ((mmc_drv_obj.int_event2 & SD_INFO2_ALL_ERR) != 0) { + if ((mmc_drv_obj.get_partition_access_flag == + TRUE) + && ((mmc_drv_obj.int_event2 & SD_INFO2_ERR6) + != 0U)) { + err_not_care_flag = TRUE; + rtn_code = EMMC_ERR_CMD_TIMEOUT; + } else { + /* error interrupt */ + cmdErrSdInfo2Log(); + rtn_code = EMMC_ERR_INFO2; + } + state = ESTATE_ERROR; + break; + } else if ((mmc_drv_obj.int_event1 & SD_INFO1_INFO0) == + 0) { + /* not receive expected interrupt */ + rtn_code = EMMC_ERR_RESPONSE; + state = ESTATE_ERROR; + break; + } + + /* read response */ + emmc_read_response(response); + + /* check response */ + rtn_code = emmc_response_check(response, error_mask); + if (rtn_code != EMMC_SUCCESS) { + state = ESTATE_ERROR; + break; + } + + if (response_type == HAL_MEMCARD_RESPONSE_R1b) { + /* R1b */ + SETR_32(SD_INFO2_MASK, + (SD_INFO2_ALL_ERR | SD_INFO2_CLEAR)); + state = ESTATE_RCV_RESPONSE_BUSY; + } else { + state = ESTATE_CHECK_RESPONSE_COMPLETE; + } + break; + + case ESTATE_RCV_RESPONSE_BUSY: + /* check interrupt */ + if ((mmc_drv_obj.int_event2 & SD_INFO2_ALL_ERR) != 0) { + /* error interrupt */ + cmdErrSdInfo2Log(); + rtn_code = EMMC_ERR_INFO2; + state = ESTATE_ERROR; + break; + } + /* DAT0 not Busy */ + if ((SD_INFO2_DAT0 & mmc_drv_obj.error_info.info2) != 0) { + state = ESTATE_CHECK_RESPONSE_COMPLETE; + break; + } + break; + + case ESTATE_CHECK_RESPONSE_COMPLETE: + if (cmd_type >= HAL_MEMCARD_COMMAND_TYPE_ADTC_WRITE) { + state = ESTATE_DATA_TRANSFER; + } else { + emmc_WaitCmd2Cmd_8Cycle(); + state = ESTATE_END; + } + break; + + case ESTATE_DATA_TRANSFER: + /* ADTC command */ + mmc_drv_obj.during_transfer = TRUE; + mmc_drv_obj.state_machine_blocking = TRUE; + + if (mmc_drv_obj.transfer_mode == HAL_MEMCARD_DMA) { + /* DMA */ + emmc_data_transfer_dma(); + } else { + /* PIO */ + /* interrupt enable (FIFO read/write enable) */ + if (mmc_drv_obj.cmd_info.dir == + HAL_MEMCARD_WRITE) { + SETR_32(SD_INFO2_MASK, + (SD_INFO2_BWE | SD_INFO2_ALL_ERR + | SD_INFO2_CLEAR)); + } else { + SETR_32(SD_INFO2_MASK, + (SD_INFO2_BRE | SD_INFO2_ALL_ERR + | SD_INFO2_CLEAR)); + } + } + state = ESTATE_DATA_TRANSFER_COMPLETE; + break; + + case ESTATE_DATA_TRANSFER_COMPLETE: + /* check interrupt */ + if ((mmc_drv_obj.int_event2 & SD_INFO2_ALL_ERR) != 0) { + /* error interrupt */ + cmdErrSdInfo2Log(); + rtn_code = EMMC_ERR_INFO2; + state = ESTATE_TRANSFER_ERROR; + break; + } + + /* DMAC error ? */ + if (mmc_drv_obj.dma_error_flag == TRUE) { + /* Error occurred in DMAC driver. */ + rtn_code = EMMC_ERR_FROM_DMAC_TRANSFER; + state = ESTATE_TRANSFER_ERROR; + } else if (mmc_drv_obj.during_dma_transfer == TRUE) { + /* DMAC not finished. unknown error */ + rtn_code = EMMC_ERR; + state = ESTATE_TRANSFER_ERROR; + } else { + SETR_32(SD_INFO1_MASK, SD_INFO1_INFO2); + SETR_32(SD_INFO2_MASK, + (SD_INFO2_ALL_ERR | SD_INFO2_CLEAR)); + + mmc_drv_obj.state_machine_blocking = TRUE; + + state = ESTATE_ACCESS_END; + } + break; + + case ESTATE_ACCESS_END: + + /* clear flag */ + if (mmc_drv_obj.transfer_mode == HAL_MEMCARD_DMA) { + /* W (CC_EXT_MODE, H'0000_1010) SD_BUF DMA transfer disabled */ + SETR_32(CC_EXT_MODE, CC_EXT_MODE_CLEAR); + SETR_32(SD_STOP, 0x00000000U); + mmc_drv_obj.during_dma_transfer = FALSE; + } + + SETR_32(SD_INFO1_MASK, 0x00000000U); + SETR_32(SD_INFO2_MASK, SD_INFO2_CLEAR); + SETR_32(SD_INFO1, 0x00000000U); + SETR_32(SD_INFO2, SD_INFO2_CLEAR); + + if ((mmc_drv_obj.int_event1 & SD_INFO1_INFO2) != 0) { + emmc_WaitCmd2Cmd_8Cycle(); + state = ESTATE_END; + } else { + state = ESTATE_ERROR; + } + break; + + case ESTATE_TRANSFER_ERROR: + /* The error occurred in the Data transfer. */ + if (mmc_drv_obj.transfer_mode == HAL_MEMCARD_DMA) { + /* W (CC_EXT_MODE, H'0000_1010) SD_BUF DMA transfer disabled */ + SETR_32(CC_EXT_MODE, CC_EXT_MODE_CLEAR); + SETR_32(SD_STOP, 0x00000000U); + mmc_drv_obj.during_dma_transfer = FALSE; + } + /* through */ + + case ESTATE_ERROR: + if (err_not_care_flag == TRUE) { + mmc_drv_obj.during_cmd_processing = FALSE; + } else { + emmc_softreset(); + emmc_write_error_info(EMMC_FUNCNO_EXEC_CMD, + rtn_code); + } + return rtn_code; + + default: + state = ESTATE_END; + break; + } /* switch (state) */ + } /* while ( (mmc_drv_obj.force_terminate != TRUE) && (state != ESTATE_END) ) */ + + /* force terminate */ + if (mmc_drv_obj.force_terminate == TRUE) { + /* timeout timer is expired. Or, PIO data transfer error. */ + /* Timeout occurred in the DMA transfer. */ + if (mmc_drv_obj.during_dma_transfer == TRUE) { + mmc_drv_obj.during_dma_transfer = FALSE; + } + ERROR("BL2: emmc exec_cmd:EMMC_ERR_FORCE_TERMINATE\n"); + emmc_softreset(); + + return EMMC_ERR_FORCE_TERMINATE; /* error information has already been written. */ + } + + /* success */ + mmc_drv_obj.during_cmd_processing = FALSE; + mmc_drv_obj.during_transfer = FALSE; + + return EMMC_SUCCESS; +} |