/****************************************************************************** | |
* | |
* Copyright 2013 Altera Corporation. All Rights Reserved. | |
* | |
* Redistribution and use in source and binary forms, with or without | |
* modification, are permitted provided that the following conditions are met: | |
* | |
* 1. Redistributions of source code must retain the above copyright notice, | |
* this list of conditions and the following disclaimer. | |
* | |
* 2. 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. | |
* | |
* 3. The name of the author may not be used to endorse or promote products | |
* derived from this software without specific prior written permission. | |
* | |
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER "AS IS" AND ANY EXPRESS OR | |
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF | |
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE DISCLAIMED. IN NO | |
* EVENT SHALL THE AUTHOR 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 "alt_sdmmc.h" | |
#include "alt_clock_manager.h" | |
#include "alt_reset_manager.h" | |
#include "socal/alt_sdmmc.h" | |
#include "socal/alt_rstmgr.h" | |
#include "socal/hps.h" | |
#include "socal/socal.h" | |
#include <stdio.h> | |
///// | |
//#define LOGGER | |
// NOTE: To enable debugging output, delete the next line and uncomment the | |
// line after. | |
//#define dprintf(...) | |
#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) | |
#ifdef LOGGER | |
#endif | |
///// | |
#define MIN(a, b) ((a) > (b) ? (b) : (a)) | |
#define ARRAY_COUNT(array) (sizeof(array) / sizeof(array[0])) | |
///// | |
// Timeout for reset manager | |
#define ALT_SDMMC_RESET_TMO_INIT 8192 | |
// Timeout for disable device | |
#define ALT_SDMMC_MAX_T_POLL_COUNT 8192 | |
// Timeout for waiting event | |
#define ALT_SDMMC_TMO_WAITER 1000000 | |
#define ALT_SDMMC_DMA_SEGMENT_SIZE 512 | |
#define ALT_SDMMC_DMA_DESC_COUNT 128 | |
#define ALT_SDMMC_FSM_IDLE 0 | |
#define ALT_SDMMC_DMA_FSM_IDLE 0 | |
#define ALT_SDMMC_CSD_MAX_R_BLK_MSK 0x000F0000 | |
#define ALT_SDMMC_CSD_MAX_W_BLK_MSK 0x03C00000 | |
#define ALT_SDMMC_CSD_PART_R_ALLOW_MSK 0x00008000 | |
#define ALT_SDMMC_CSD_PART_W_ALLOW_MSK 0x00200000 | |
#define ALT_SDMMC_CSD_SPEED_RATE_MSK 0x00000007 | |
#define ALT_SDMMC_CSD_SPEED_TIME_MSK 0x00000078 | |
#define ALT_SDMMC_CSD_MAX_R_BLK_GET(val) ((val & ALT_SDMMC_CSD_MAX_R_BLK_MSK) >> 16) | |
#define ALT_SDMMC_CSD_MAX_W_BLK_GET(val) ((val & ALT_SDMMC_CSD_MAX_W_BLK_MSK) >> 22) | |
#define ALT_SDMMC_CSD_PART_R_ALLOW_GET(val) ((val & ALT_SDMMC_CSD_PART_R_ALLOW_MSK) >> 15) | |
#define ALT_SDMMC_CSD_PART_W_ALLOW_GET(val) ((val & ALT_SDMMC_CSD_PART_W_ALLOW_MSK) >> 21) | |
#define ALT_SDMMC_CSD_SPEED_RATE_GET(val) ((val & ALT_SDMMC_CSD_SPEED_RATE_MSK) >> 0) | |
#define ALT_SDMMC_CSD_SPEED_TIME_GET(val) ((val & ALT_SDMMC_CSD_SPEED_TIME_MSK) >> 3) | |
typedef enum ALT_SDMMC_TMOD_e | |
{ | |
ALT_SDMMC_TMOD_READ = 0, | |
ALT_SDMMC_TMOD_WRITE = 1, | |
} ALT_SDMMC_TMOD_t; | |
#ifdef LOGGER | |
uint32_t log_buffer[1000] = { 0 }; | |
uint32_t log_index = 0; | |
#define D_ADD_VALUE(value)({if (log_index < 1000) log_buffer[log_index++] = value;}) | |
#endif | |
static alt_freq_t clock_freq; | |
// Default configurations of used commands | |
static ALT_SDMMC_CMD_CONFIG_t cmd_default_cfg[20] = | |
{ | |
{ | |
.cmd_index = ALT_SDMMC_WRITE_MULTIPLE_BLOCK, | |
.send_auto_stop = true, | |
.response_expect = true, | |
.data_expected = 1, | |
.write_active = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_READ_MULTIPLE_BLOCK, | |
.send_auto_stop = true, | |
.response_expect = true, | |
.data_expected = 1, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SD_SEND_SCR, | |
.response_expect = true, | |
.data_expected = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_WRITE_BLOCK, | |
.response_expect = true, | |
.data_expected = true, | |
.write_active = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_READ_SINGLE_BLOCK, | |
.response_expect = true, | |
.data_expected = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_SET_BLOCKLEN, | |
.response_expect = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_SEL_DES_CARD, | |
.response_expect = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_APP_CMD, | |
.response_expect = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SD_SET_BUS_WIDTH, | |
.response_expect = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SD_SEND_OP_COND, | |
.response_expect = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_SEND_OP_COND, | |
.response_expect = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_IF_COND, | |
.response_expect = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_SET_RELATIVE_ADDR, | |
.response_expect = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_SEND_STATUS, | |
.response_expect = true, | |
.card_number = 0, | |
.use_hold_reg = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_ALL_SEND_CID, | |
.response_expect = true, | |
.response_length_long = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_SEND_CID, | |
.response_expect = true, | |
.response_length_long = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_SEND_CSD, | |
.response_expect = true, | |
.response_length_long = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_STOP_TRANSMISSION, | |
.stop_abort_cmd = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
}, | |
{ | |
.cmd_index = ALT_SDMMC_GO_IDLE_STATE, | |
.send_initialization = true, | |
.card_number = 0, | |
.use_hold_reg = true, | |
.wait_prvdata_complete = true | |
} | |
}; | |
static ALT_SDMMC_CMD_CONFIG_t cmd_clock_cfg = | |
{ | |
.update_clock_registers_only = true, | |
.wait_prvdata_complete = true | |
}; | |
static uint32_t rca_number; /*!< Relative card address. */ | |
static ALT_SDMMC_DMA_BUF_DESC_t dma_descriptors[ALT_SDMMC_DMA_DESC_COUNT]; | |
/*!< Array of dma descriptors. */ | |
static ALT_SDMMC_DMA_BUF_DESC_t *dma_cur_descr; | |
/*!< Current decriptor. */ | |
// | |
// Reset sdmmc module by reset manager without deassert | |
// | |
static ALT_STATUS_CODE alt_sdmmc_rstmgr_set(void) | |
{ | |
alt_setbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_SDMMC_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Assert reset sdmmc module by reset manager, wait, deasert | |
// | |
static ALT_STATUS_CODE alt_sdmmc_rstmgr_strobe(void) | |
{ | |
alt_setbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_SDMMC_SET_MSK); | |
volatile uint32_t timeout = ALT_SDMMC_RESET_TMO_INIT; | |
// Wait while sdmmc module is reseting | |
while (timeout--) | |
; | |
// Deassert the appropriate sdmmc module reset signal via the Reset Manager Peripheral Reset register. | |
alt_clrbits_word(ALT_RSTMGR_PERMODRST_ADDR, ALT_RSTMGR_PERMODRST_SDMMC_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Initialize descriptor chain for dma | |
// | |
static ALT_STATUS_CODE alt_sdmmc_desc_chain_init() | |
{ | |
ALT_SDMMC_DMA_BUF_DESC_t * dma_desc = dma_descriptors; | |
// Initialising descriptor chain | |
for (uint32_t count = 0; count < ALT_SDMMC_DMA_DESC_COUNT; count++) | |
{ | |
dma_desc[count].des0.fld.own = 0; | |
dma_desc[count].des0.fld.ch = 1; | |
dma_desc[count].des0.fld.er = 0; | |
dma_desc[count].des1.fld.bs1 = 0; | |
dma_desc[count].des2.fld.bap1 = 0; | |
// Create chain description list | |
if (count == (ALT_SDMMC_DMA_DESC_COUNT - 1)) | |
{ | |
// If it is latest element set pointer to the ring head. | |
dma_desc[count].des3.fld.bap2_or_next = (uint32_t) dma_desc; | |
} | |
else | |
{ | |
// Set pointer to the next element in the ring | |
dma_desc[count].des3.fld.bap2_or_next = (uint32_t) (&dma_desc[count + 1]); | |
} | |
} | |
dma_cur_descr = dma_desc; | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Clear descriptors of chain for dma operations | |
// | |
static ALT_STATUS_CODE alt_sdmmc_desc_chain_clear() | |
{ | |
ALT_SDMMC_DMA_BUF_DESC_t * dma_desc = dma_descriptors; | |
// Clean descriptions | |
for (uint32_t count = 0; count < ALT_SDMMC_DMA_DESC_COUNT; count++) | |
{ | |
dma_desc[count].des0.fld.own = 0; | |
dma_desc[count].des0.fld.dic = 0; | |
dma_desc[count].des0.fld.ld = 0; | |
dma_desc[count].des0.fld.fs = 0; | |
dma_desc[count].des1.fld.bs1 = 0; | |
dma_desc[count].des2.fld.bap1 = 0; | |
} | |
dma_cur_descr = dma_desc; | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Initialize the specified sdmmc controller instance for use and return a device | |
// handle referencing it. | |
// | |
ALT_STATUS_CODE alt_sdmmc_init() | |
{ | |
if (alt_clk_is_enabled(ALT_CLK_SDMMC) != ALT_E_TRUE) | |
{ | |
return ALT_E_BAD_CLK; | |
} | |
///// | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
// Query the SDMMC clock. | |
if (status == ALT_E_SUCCESS) | |
{ | |
status = alt_clk_freq_get(ALT_CLK_SDMMC, &clock_freq); | |
} | |
// Reset sdmmc module | |
if (status == ALT_E_SUCCESS) | |
{ | |
status = alt_sdmmc_reset(); | |
} | |
return status; | |
} | |
// | |
//Reset the SD/MMC controller by stopping any data transfers in progress and | |
//putting the controller into reset and reinit it after reset complete. | |
// | |
ALT_STATUS_CODE alt_sdmmc_reset() | |
{ | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
bool already_enabled = (alt_sdmmc_card_pwr_is_on() == ALT_E_TRUE); | |
if (already_enabled) | |
{ | |
// Temporarily power off the card | |
status = alt_sdmmc_card_pwr_off(); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
} | |
// Reset sdmmc module by reset manager | |
alt_sdmmc_rstmgr_strobe(); | |
if (already_enabled) | |
{ | |
// Re-enable card power | |
status = alt_sdmmc_card_pwr_on(); | |
} | |
// Relative card address have not readed yet | |
rca_number = 0; | |
// Init description chain | |
alt_sdmmc_desc_chain_init(); | |
if (status == ALT_E_SUCCESS) | |
{ | |
//Enable default clock for working alt_sdmmc_command_send | |
alt_write_word(ALT_SDMMC_CLKENA_ADDR, ALT_SDMMC_CLKENA_CCLK_EN_SET(true)); | |
status = alt_sdmmc_command_send(ALT_SDMMC_CLK_INDEX, 0x0, NULL); | |
} | |
return status; | |
} | |
// | |
// Uninitialize the sdmmc controller referenced by the sdmmc_dev handle. | |
// | |
ALT_STATUS_CODE alt_sdmmc_uninit(void) | |
{ | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
// Clean descriptor chain | |
alt_sdmmc_desc_chain_clear(); | |
// Card power off | |
if (status == ALT_E_SUCCESS) | |
{ | |
status = alt_sdmmc_card_pwr_off(); | |
} | |
// Reset sdmmc module by reset manager | |
if (status == ALT_E_SUCCESS) | |
{ | |
status = alt_sdmmc_rstmgr_set(); | |
} | |
return status; | |
} | |
// | |
// Power on of the card. | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_pwr_on(void) | |
{ | |
alt_setbits_word(ALT_SDMMC_PWREN_ADDR, | |
ALT_SDMMC_PWREN_POWER_EN_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
// | |
//Power off of the card. | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_pwr_off(void) | |
{ | |
//If sdmmc controller is enabled, return with sucess | |
if (alt_sdmmc_card_pwr_is_on() == ALT_E_FALSE) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
//Else clear enable bit of sdmmc_enable register | |
alt_clrbits_word(ALT_SDMMC_PWREN_ADDR, | |
ALT_SDMMC_PWREN_POWER_EN_SET_MSK); | |
// Clear interrupt status | |
alt_sdmmc_int_clear(ALT_SDMMC_INT_STATUS_ALL); | |
// Relative card address have not readed yet | |
rca_number = 0; | |
// Reset state of card stack | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Check whether sdmmc controller is enable | |
// | |
bool alt_sdmmc_card_pwr_is_on(void) | |
{ | |
if (ALT_SDMMC_PWREN_POWER_EN_GET(alt_read_word(ALT_SDMMC_PWREN_ADDR)) == | |
ALT_SDMMC_PWREN_POWER_EN_E_ON) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
// | |
// Returns ALT_E_TRUE if the sdmmc controller is busy | |
// | |
static ALT_STATUS_CODE alt_sdmmc_is_busy(void) | |
{ | |
if (ALT_SDMMC_STAT_DATA_BUSY_GET(alt_read_word(ALT_SDMMC_STAT_ADDR)) == | |
ALT_SDMMC_STAT_DATA_BUSY_E_CARDBUSY) | |
{ | |
return ALT_E_TRUE; | |
} | |
else | |
{ | |
return ALT_E_FALSE; | |
} | |
} | |
// | |
// Returns ALT_E_TRUE if the sdmmc and iddmac controller is in idle state | |
// | |
static ALT_STATUS_CODE alt_sdmmc_is_idle(void) | |
{ | |
uint32_t mmc_state = ALT_SDMMC_STAT_CMD_FSM_STATES_GET(alt_read_word(ALT_SDMMC_STAT_ADDR)); | |
uint32_t dma_state = ALT_SDMMC_IDSTS_FSM_GET(alt_read_word(ALT_SDMMC_IDSTS_ADDR)); | |
if ((mmc_state != ALT_SDMMC_FSM_IDLE) || (dma_state != ALT_SDMMC_DMA_FSM_IDLE)) | |
{ | |
#ifdef LOGGER | |
dprintf("\nstate %x dma_state %x\n", (int)mmc_state, (int)dma_state); | |
#endif | |
return ALT_E_FALSE; | |
} | |
else | |
{ | |
return ALT_E_TRUE; | |
} | |
} | |
// | |
// Get config clock parameters | |
// | |
uint32_t alt_sdmmc_card_clk_div_get(void) | |
{ | |
return ALT_SDMMC_CLKDIV_CLK_DIVR0_GET(alt_read_word(ALT_SDMMC_CLKDIV_ADDR)); | |
} | |
// | |
// Set config clock parameters (7.2.3 Clock Programming) | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_clk_div_set(const uint32_t clk_div) | |
{ | |
if (alt_sdmmc_is_busy() == ALT_E_TRUE) | |
{ | |
return ALT_E_ERROR; | |
} | |
alt_write_word(ALT_SDMMC_CLKDIV_ADDR, ALT_SDMMC_CLKDIV_CLK_DIVR0_SET(clk_div)); | |
return alt_sdmmc_command_send(ALT_SDMMC_CLK_INDEX, 0x0, NULL); | |
} | |
uint32_t alt_sdmmc_card_speed_get(void) | |
{ | |
uint32_t clk_div = alt_sdmmc_card_clk_div_get(); | |
uint32_t speed_bps = clock_freq / (2 * clk_div); | |
return speed_bps; | |
} | |
ALT_STATUS_CODE alt_sdmmc_card_speed_set(uint32_t xfer_speed) | |
{ | |
uint32_t clk_div = clock_freq / (2 * xfer_speed); | |
return alt_sdmmc_card_clk_div_set(clk_div); | |
} | |
ALT_STATUS_CODE alt_sdmmc_card_clk_disable(void) | |
{ | |
if (alt_sdmmc_is_busy() == ALT_E_TRUE) | |
{ | |
return ALT_E_ERROR; | |
} | |
alt_write_word(ALT_SDMMC_CLKENA_ADDR, ALT_SDMMC_CLKENA_CCLK_EN_SET(false)); | |
return alt_sdmmc_command_send(ALT_SDMMC_CLK_INDEX, 0x0, NULL); | |
} | |
// | |
// Enables the card clock (sdmmc_cclk_out). | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_clk_enable(const bool use_low_pwr_mode) | |
{ | |
if (alt_sdmmc_is_busy() == ALT_E_TRUE) | |
{ | |
return ALT_E_ERROR; | |
} | |
// Enable clock | |
alt_write_word(ALT_SDMMC_CLKENA_ADDR, ALT_SDMMC_CLKENA_CCLK_EN_SET(true) | |
| ALT_SDMMC_CLKENA_CCLK_LOW_POWER_SET(use_low_pwr_mode)); | |
return alt_sdmmc_command_send(ALT_SDMMC_CLK_INDEX, 0x0, NULL); | |
} | |
// | |
// Returns true if the card clock (sdmmc_cclk_out) is enabled otherwise returns | |
// | |
bool alt_sdmmc_card_clk_is_enabled(void) | |
{ | |
return ALT_SDMMC_CLKENA_CCLK_EN_GET(alt_read_word(ALT_SDMMC_CLKENA_ADDR)); | |
} | |
// | |
// Get sdmmc bus width | |
// | |
static ALT_SDMMC_BUS_WIDTH_t alt_sdmmc_bus_width_get(void) | |
{ | |
uint32_t ctype_register = alt_read_word(ALT_SDMMC_CTYPE_ADDR); | |
uint16_t card_width1 = ALT_SDMMC_CTYPE_CARD_WIDTH1_GET(ctype_register); | |
uint16_t card_width2 = ALT_SDMMC_CTYPE_CARD_WIDTH2_GET(ctype_register); | |
if (card_width1 == ALT_SDMMC_CTYPE_CARD_WIDTH1_E_MOD8BIT) | |
{ | |
return ALT_SDMMC_BUS_WIDTH_8; | |
} | |
else if (card_width2 == ALT_SDMMC_CTYPE_CARD_WIDTH2_E_MOD4BIT) | |
{ | |
return ALT_SDMMC_BUS_WIDTH_4; | |
} | |
else | |
{ | |
return ALT_SDMMC_BUS_WIDTH_1; | |
} | |
} | |
// | |
// Set sdmmc bus width | |
// | |
static ALT_STATUS_CODE alt_sdmmc_bus_width_set(const ALT_SDMMC_BUS_WIDTH_t width) | |
{ | |
// Set config parameters to appropriate registers | |
switch (width) | |
{ | |
case ALT_SDMMC_BUS_WIDTH_8: | |
alt_replbits_word(ALT_SDMMC_CTYPE_ADDR, | |
ALT_SDMMC_CTYPE_CARD_WIDTH1_SET_MSK, | |
ALT_SDMMC_CTYPE_CARD_WIDTH1_SET(ALT_SDMMC_CTYPE_CARD_WIDTH1_E_MOD8BIT)); | |
break; | |
case ALT_SDMMC_BUS_WIDTH_4: | |
alt_replbits_word(ALT_SDMMC_CTYPE_ADDR, | |
ALT_SDMMC_CTYPE_CARD_WIDTH1_SET_MSK, | |
ALT_SDMMC_CTYPE_CARD_WIDTH1_SET(ALT_SDMMC_CTYPE_CARD_WIDTH1_E_NON8BIT)); | |
alt_replbits_word(ALT_SDMMC_CTYPE_ADDR, | |
ALT_SDMMC_CTYPE_CARD_WIDTH2_SET_MSK, | |
ALT_SDMMC_CTYPE_CARD_WIDTH2_SET(ALT_SDMMC_CTYPE_CARD_WIDTH2_E_MOD4BIT)); | |
break; | |
case ALT_SDMMC_BUS_WIDTH_1: | |
alt_replbits_word(ALT_SDMMC_CTYPE_ADDR, | |
ALT_SDMMC_CTYPE_CARD_WIDTH1_SET_MSK, | |
ALT_SDMMC_CTYPE_CARD_WIDTH1_SET(ALT_SDMMC_CTYPE_CARD_WIDTH1_E_NON8BIT)); | |
alt_replbits_word(ALT_SDMMC_CTYPE_ADDR, | |
ALT_SDMMC_CTYPE_CARD_WIDTH2_SET_MSK, | |
ALT_SDMMC_CTYPE_CARD_WIDTH2_SET(ALT_SDMMC_CTYPE_CARD_WIDTH2_E_MOD1BIT)); | |
break; | |
default: | |
return ALT_E_BAD_ARG; | |
} | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Get block size | |
// | |
inline static uint16_t alt_sdmmc_block_size_get(void) | |
{ | |
uint32_t blksiz_register = alt_read_word(ALT_SDMMC_BLKSIZ_ADDR); | |
return ALT_SDMMC_BLKSIZ_BLOCK_SIZE_GET(blksiz_register); | |
} | |
// | |
// Set block size | |
// | |
inline static ALT_STATUS_CODE alt_sdmmc_block_size_set(uint16_t block_size) | |
{ | |
alt_replbits_word(ALT_SDMMC_BLKSIZ_ADDR, | |
ALT_SDMMC_BLKSIZ_BLOCK_SIZE_SET_MSK, | |
ALT_SDMMC_BLKSIZ_BLOCK_SIZE_SET(block_size)); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Set byte count | |
// | |
inline static ALT_STATUS_CODE alt_sdmmc_byte_count_set(uint32_t count) | |
{ | |
alt_replbits_word(ALT_SDMMC_BYTCNT_ADDR, | |
ALT_SDMMC_BYTCNT_BYTE_COUNT_SET_MSK, | |
ALT_SDMMC_BYTCNT_BYTE_COUNT_SET(count)); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Get sdmmc timeouts for command response and data sending | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_misc_get(ALT_SDMMC_CARD_MISC_t *card_misc_cfg) | |
{ | |
uint32_t tmout_register = alt_read_word(ALT_SDMMC_TMOUT_ADDR); | |
card_misc_cfg->response_timeout = ALT_SDMMC_TMOUT_RESPONSE_TMO_GET(tmout_register); | |
card_misc_cfg->data_timeout = ALT_SDMMC_TMOUT_DATA_TMO_GET(tmout_register); | |
card_misc_cfg->card_width = alt_sdmmc_bus_width_get(); | |
card_misc_cfg->block_size = alt_sdmmc_block_size_get(); | |
card_misc_cfg->debounce_count = ALT_SDMMC_DEBNCE_DEBOUNCE_COUNT_GET(alt_read_word(ALT_SDMMC_DEBNCE_ADDR)); | |
return ALT_E_SUCCESS; | |
} | |
// | |
//Set sdmmc timeouts for command response and data sending | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_misc_set(const ALT_SDMMC_CARD_MISC_t *card_misc_cfg) | |
{ | |
uint32_t tmout_value = ALT_SDMMC_TMOUT_RESPONSE_TMO_SET(card_misc_cfg->response_timeout) | |
| ALT_SDMMC_TMOUT_DATA_TMO_SET(card_misc_cfg->data_timeout); | |
alt_write_word(ALT_SDMMC_TMOUT_ADDR, tmout_value); | |
alt_replbits_word(ALT_SDMMC_DEBNCE_ADDR, | |
ALT_SDMMC_DEBNCE_DEBOUNCE_COUNT_SET_MSK, | |
ALT_SDMMC_DEBNCE_DEBOUNCE_COUNT_SET(card_misc_cfg->debounce_count)); | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
if (status == ALT_E_SUCCESS) | |
{ | |
status = alt_sdmmc_bus_width_set(card_misc_cfg->card_width); | |
} | |
if (status == ALT_E_SUCCESS) | |
{ | |
status = alt_sdmmc_block_size_set(card_misc_cfg->block_size); | |
} | |
return status; | |
} | |
// | |
// Starts the SD/MMC internal DMA transfer with the specified | |
// descriptor an bus mode transfer configuration. | |
// | |
ALT_STATUS_CODE alt_sdmmc_dma_start(ALT_SDMMC_DMA_BUF_DESC_t *buf_desc_list, | |
const uint32_t desc_skip_len, | |
const ALT_SDMMC_DMA_PBL_t burst_len, | |
const bool use_fixed_burst) | |
{ | |
uint32_t bmod_set_value = ALT_SDMMC_BMOD_PBL_SET(burst_len) | |
| ALT_SDMMC_BMOD_FB_SET(use_fixed_burst) | |
| ALT_SDMMC_BMOD_DSL_SET(desc_skip_len); | |
uint32_t bmod_set_mask = ALT_SDMMC_BMOD_PBL_SET_MSK | |
| ALT_SDMMC_BMOD_FB_SET_MSK | |
| ALT_SDMMC_BMOD_DSL_SET_MSK; | |
alt_replbits_word(ALT_SDMMC_BMOD_ADDR, bmod_set_mask, bmod_set_value); | |
// Set start address of descriptor chain | |
alt_write_word(ALT_SDMMC_DBADDR_ADDR, (uint32_t)buf_desc_list); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Enables the sdmmc write protect. | |
// | |
bool alt_sdmmc_card_is_write_protected(void) | |
{ | |
alt_setbits_word(ALT_SDMMC_WRTPRT_ADDR, | |
ALT_SDMMC_WRTPRT_WR_PROTECT_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// FIFO reset | |
// | |
ALT_STATUS_CODE alt_sdmmc_fifo_reset(void) | |
{ | |
uint32_t timeout = ALT_SDMMC_MAX_T_POLL_COUNT; | |
// Activate fifo reset | |
alt_setbits_word(ALT_SDMMC_CTL_ADDR, ALT_SDMMC_CTL_FIFO_RST_SET_MSK); | |
// Wait to complete reset or timeout | |
while (ALT_SDMMC_CTL_FIFO_RST_GET(alt_read_word(ALT_SDMMC_CTL_ADDR)) | |
&& --timeout) | |
; | |
// If fifo reset still are active, return timeout error | |
if (timeout == 0) | |
{ | |
return ALT_E_TMO; | |
} | |
return ALT_E_SUCCESS; | |
} | |
// | |
// DMA reset | |
// | |
ALT_STATUS_CODE alt_sdmmc_dma_reset(void) | |
{ | |
uint32_t timeout = ALT_SDMMC_MAX_T_POLL_COUNT; | |
//Activate dma reset | |
alt_setbits_word(ALT_SDMMC_CTL_ADDR, ALT_SDMMC_CTL_DMA_RST_SET_MSK); | |
// Wait to complete reset or timeout | |
while (ALT_SDMMC_CTL_DMA_RST_GET(alt_read_word(ALT_SDMMC_CTL_ADDR)) | |
&& --timeout) | |
; | |
// If dma reset still are active, return timeout error | |
if (timeout == 0) | |
{ | |
return ALT_E_TMO; | |
} | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Returns ALT_E_TRUE if the sdmmc controller is present depend on cdata_in. | |
// | |
bool alt_sdmmc_card_is_detected(void) | |
{ | |
if (ALT_SDMMC_STAT_DATA_3_STAT_GET(alt_read_word(ALT_SDMMC_STAT_ADDR)) == | |
ALT_SDMMC_STAT_DATA_3_STAT_E_CARDPRESENT) | |
// if (ALT_SDMMC_CDETECT_CARD_DETECT_N_GET(alt_read_word(ALT_SDMMC_CDETECT_ADDR)) | |
// == ALT_SDMMC_CDETECT_CARD_DETECT_N_E_DETECTED) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
// | |
//Set command configuration | |
// | |
static ALT_STATUS_CODE alt_sdmmc_cmd_set(const ALT_SDMMC_CMD_INDEX_t cmd_index, | |
const ALT_SDMMC_CMD_CONFIG_t *cmd_cfg, | |
bool start_cmd) | |
{ | |
uint32_t cmd_register = ALT_SDMMC_CMD_CMD_INDEX_SET(cmd_index) | |
| ALT_SDMMC_CMD_RESPONSE_EXPECT_SET(cmd_cfg->response_expect) | |
| ALT_SDMMC_CMD_RESPONSE_LEN_SET(cmd_cfg->response_length_long) | |
| ALT_SDMMC_CMD_CHECK_RESPONSE_CRC_SET(cmd_cfg->check_response_crc) | |
| ALT_SDMMC_CMD_DATA_EXPECTED_SET(cmd_cfg->data_expected) | |
| ALT_SDMMC_CMD_RD_WR_SET(cmd_cfg->write_active) | |
| ALT_SDMMC_CMD_TFR_MOD_SET(cmd_cfg->stream_mode_active) | |
| ALT_SDMMC_CMD_SEND_AUTO_STOP_SET(cmd_cfg->send_auto_stop) | |
| ALT_SDMMC_CMD_WAIT_PRVDATA_COMPLETE_SET(cmd_cfg->wait_prvdata_complete) | |
| ALT_SDMMC_CMD_STOP_ABT_CMD_SET(cmd_cfg->stop_abort_cmd) | |
| ALT_SDMMC_CMD_SEND_INITIALIZATION_SET(cmd_cfg->send_initialization) | |
| ALT_SDMMC_CMD_UPDATE_CLK_REGS_ONLY_SET(cmd_cfg->update_clock_registers_only) | |
| ALT_SDMMC_CMD_RD_CEATA_DEVICE_SET(cmd_cfg->read_ceata_device) | |
| ALT_SDMMC_CMD_CCS_EXPECTED_SET(cmd_cfg->ccs_expected) | |
| ALT_SDMMC_CMD_EN_BOOT_SET(cmd_cfg->enable_boot) | |
| ALT_SDMMC_CMD_EXPECT_BOOT_ACK_SET(cmd_cfg->expect_boot_ack) | |
| ALT_SDMMC_CMD_DIS_BOOT_SET(cmd_cfg->disable_boot) | |
| ALT_SDMMC_CMD_BOOT_MOD_SET(cmd_cfg->boot_mode) | |
| ALT_SDMMC_CMD_VOLT_SWITCH_SET(cmd_cfg->volt_switch) | |
| ALT_SDMMC_CMD_USE_HOLD_REG_SET(cmd_cfg->use_hold_reg) | |
| ALT_SDMMC_CMD_START_CMD_SET(start_cmd); | |
alt_write_word(ALT_SDMMC_CMD_ADDR, cmd_register); | |
#ifdef LOGGER | |
dprintf("\ncommand = %X\n", (int)cmd_register); | |
#endif | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Set command argument | |
// | |
inline static ALT_STATUS_CODE alt_sdmmc_cmd_arg_set(uint32_t cmdarg) | |
{ | |
alt_write_word(ALT_SDMMC_CMDARG_ADDR, cmdarg); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Get response previous command. | |
// | |
inline static ALT_STATUS_CODE alt_sdmmc_read_short_response(uint32_t *response) | |
{ | |
uint32_t resp0 = alt_read_word(ALT_SDMMC_RESP0_ADDR); | |
*response = (uint32_t)(ALT_SDMMC_RESP0_RESPONSE0_GET(resp0)); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Get long response of previous command. | |
// | |
ALT_STATUS_CODE alt_sdmmc_read_long_response(ALT_SDMMC_RESPONSE_t *response) | |
{ | |
uint32_t resp0 = alt_read_word(ALT_SDMMC_RESP0_ADDR); | |
uint32_t resp1 = alt_read_word(ALT_SDMMC_RESP1_ADDR); | |
uint32_t resp2 = alt_read_word(ALT_SDMMC_RESP2_ADDR); | |
uint32_t resp3 = alt_read_word(ALT_SDMMC_RESP3_ADDR); | |
response->resp0 = (uint32_t)(ALT_SDMMC_RESP0_RESPONSE0_GET(resp0)); | |
response->resp1 = (uint32_t)(ALT_SDMMC_RESP1_RESPONSE1_GET(resp1)); | |
response->resp2 = (uint32_t)(ALT_SDMMC_RESP2_RESPONSE2_GET(resp2)); | |
response->resp3 = (uint32_t)(ALT_SDMMC_RESP3_RESPONSE3_GET(resp3)); | |
return ALT_E_SUCCESS; | |
} | |
// | |
//This function reads a single data byte from the receive FIFO. | |
// | |
ALT_STATUS_CODE alt_sdmmc_fifo_read(void *dest, const size_t size) | |
{ | |
uint32_t * dest_ptr = dest; | |
for (int counter = 0; counter < size / 4; counter++) | |
{ | |
dest_ptr[counter] = (uint32_t)(ALT_SDMMC_DATA_VALUE_GET(alt_read_word(ALT_SDMMC_DATA_ADDR))); | |
} | |
if (size & 0x3) | |
{ | |
uint8_t * add_dest_ptr = (uint8_t*)dest + (size / 4); | |
uint32_t word_notfull = (uint32_t)(ALT_SDMMC_DATA_VALUE_GET(alt_read_word(ALT_SDMMC_DATA_ADDR))); | |
for (int counter = 0; counter < (size & 0x3); counter++) | |
{ | |
add_dest_ptr[counter] = (uint8_t)word_notfull; | |
word_notfull = word_notfull >> 8; | |
} | |
} | |
return ALT_E_SUCCESS; | |
} | |
// | |
// This function writes a single data byte to the transmit FIFO. | |
// | |
ALT_STATUS_CODE alt_sdmmc_fifo_write(const void *src, const size_t size) | |
{ | |
const uint32_t * src_ptr = src; | |
for (int counter = 0; counter < size / 4; counter++) | |
{ | |
alt_write_word(ALT_SDMMC_DATA_ADDR, ALT_SDMMC_DATA_VALUE_SET(src_ptr[counter])); | |
} | |
if (size & 0x3) | |
{ | |
const uint8_t *add_src_ptr = (uint8_t*)src + (size / 4); | |
uint32_t word_notfull = 0; | |
for (int counter = 0; counter < (size & 0x3); counter++) | |
{ | |
word_notfull |= (uint32_t)add_src_ptr[counter] << (8 * counter); | |
} | |
alt_write_word(ALT_SDMMC_DATA_ADDR, ALT_SDMMC_DATA_VALUE_SET(word_notfull)); | |
} | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Returns the current sdmmc controller interrupt status conditions. | |
// | |
uint32_t alt_sdmmc_int_status_get(void) | |
{ | |
return alt_read_word(ALT_SDMMC_MINTSTS_ADDR); | |
} | |
// | |
// Returns the sdmmc controller raw interrupt status conditions irrespective of | |
// the interrupt status condition enablement state. | |
// | |
uint32_t alt_sdmmc_int_mask_get(void) | |
{ | |
return alt_read_word(ALT_SDMMC_INTMSK_ADDR); | |
} | |
// | |
// Clears the specified sdmmc controller interrupt status conditions identified | |
// in the mask. | |
// | |
ALT_STATUS_CODE alt_sdmmc_int_clear(const uint32_t mask) | |
{ | |
alt_write_word(ALT_SDMMC_RINTSTS_ADDR, mask); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Disable the specified sdmmc controller interrupt status conditions identified in | |
// the mask. | |
// | |
ALT_STATUS_CODE alt_sdmmc_int_disable(const uint32_t mask) | |
{ | |
alt_clrbits_word(ALT_SDMMC_INTMSK_ADDR, mask); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Enable the specified sdmmc controller interrupt status conditions identified in | |
// the mask. | |
// | |
ALT_STATUS_CODE alt_sdmmc_int_enable(const uint32_t mask) | |
{ | |
if (mask & 0x0001ffff) | |
{ | |
alt_setbits_word(ALT_SDMMC_CTL_ADDR, | |
ALT_SDMMC_CTL_INT_EN_SET_MSK); | |
alt_setbits_word(ALT_SDMMC_INTMSK_ADDR, mask); | |
} | |
return ALT_E_SUCCESS; | |
} | |
// | |
//Returns true if SD/MMC controller FIFO has reached the receive watermark level | |
//otherwise returns false. | |
// | |
bool alt_sdmmc_fifo_is_rx_wtrmk_reached(void) | |
{ | |
if (ALT_SDMMC_STAT_FIFO_RX_WATERMARK_GET(alt_read_word(ALT_SDMMC_STAT_ADDR)) == | |
ALT_SDMMC_STAT_FIFO_RX_WATERMARK_E_RXWATERMARK) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
// | |
//Returns true if SD/MMC controller FIFO has reached the transmit watermark level | |
//otherwise returns false. | |
// | |
bool alt_sdmmc_fifo_is_tx_wtrmk_reached(void) | |
{ | |
if (ALT_SDMMC_STAT_FIFO_TX_WATERMARK_GET(alt_read_word(ALT_SDMMC_STAT_ADDR)) == | |
ALT_SDMMC_STAT_FIFO_TX_WATERMARK_E_TXWATERMARK) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
// | |
// Returns ALT_E_TRUE when the receive FIFO is empty. | |
// | |
bool alt_sdmmc_fifo_is_empty(void) | |
{ | |
if (ALT_SDMMC_STAT_FIFO_EMPTY_GET(alt_read_word(ALT_SDMMC_STAT_ADDR)) == | |
ALT_SDMMC_STAT_FIFO_EMPTY_E_FIFOEMPTY) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
// | |
// Returns ALT_E_TRUE when the receive FIFO is completely full. | |
// | |
bool alt_sdmmc_fifo_is_full(void) | |
{ | |
if (ALT_SDMMC_STAT_FIFO_FULL_GET(alt_read_word(ALT_SDMMC_STAT_ADDR)) == | |
ALT_SDMMC_STAT_FIFO_FULL_E_FIFOFULL) | |
{ | |
return true; | |
} | |
else | |
{ | |
return false; | |
} | |
} | |
// | |
// Returns the number of valid entries in the receive FIFO. | |
// | |
int32_t alt_sdmmc_fifo_count(void) | |
{ | |
return (int32_t)ALT_SDMMC_STAT_FIFO_COUNT_GET(alt_read_word(ALT_SDMMC_STAT_ADDR)); | |
} | |
// | |
// Gets the configured FIFO operational parameter values. | |
// | |
ALT_STATUS_CODE alt_sdmmc_fifo_param_get(uint32_t *rx_wtrmk, uint32_t *tx_wtrmk, ALT_SDMMC_MULT_TRANS_t *mult_trans_size) | |
{ | |
uint32_t fifoth = alt_read_word(ALT_SDMMC_FIFOTH_ADDR); | |
*rx_wtrmk = ALT_SDMMC_FIFOTH_RX_WMARK_GET(fifoth); | |
*tx_wtrmk = ALT_SDMMC_FIFOTH_TX_WMARK_GET(fifoth); | |
*mult_trans_size = (ALT_SDMMC_MULT_TRANS_t)ALT_SDMMC_FIFOTH_DW_DMA_MULT_TRANSACTION_SIZE_GET(fifoth); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Sets the configured FIFO operational parameter values. | |
// | |
ALT_STATUS_CODE alt_sdmmc_fifo_param_set(uint32_t rx_wtrmk, uint32_t tx_wtrmk, ALT_SDMMC_MULT_TRANS_t mult_trans_size) | |
{ | |
uint32_t fifoth_set_mask = ALT_SDMMC_FIFOTH_RX_WMARK_SET_MSK | |
| ALT_SDMMC_FIFOTH_TX_WMARK_SET_MSK | |
| ALT_SDMMC_FIFOTH_DW_DMA_MULT_TRANSACTION_SIZE_SET_MSK; | |
uint32_t fifoth_set_value = ALT_SDMMC_FIFOTH_RX_WMARK_SET(rx_wtrmk) | |
| ALT_SDMMC_FIFOTH_TX_WMARK_SET(tx_wtrmk) | |
| ALT_SDMMC_FIFOTH_DW_DMA_MULT_TRANSACTION_SIZE_SET(mult_trans_size); | |
alt_replbits_word(ALT_SDMMC_FIFOTH_ADDR, | |
fifoth_set_mask, | |
fifoth_set_value); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Card reset | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_reset(void) | |
{ | |
// Assert card reset | |
alt_setbits_word(ALT_SDMMC_RST_N_ADDR, | |
ALT_SDMMC_RST_N_CARD_RST_SET_MSK); | |
volatile uint32_t timeout = ALT_SDMMC_RESET_TMO_INIT; | |
// Wait while card reset | |
while (timeout--) | |
; | |
// Deassert the appropriate card reset. | |
alt_clrbits_word(ALT_SDMMC_RST_N_ADDR, | |
ALT_SDMMC_RST_N_CARD_RST_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Enables the sdmmc Internal DMA Controller. | |
// | |
ALT_STATUS_CODE alt_sdmmc_dma_enable(void) | |
{ | |
alt_setbits_word(ALT_SDMMC_CTL_ADDR, | |
ALT_SDMMC_CTL_USE_INTERNAL_DMAC_SET_MSK); | |
alt_setbits_word(ALT_SDMMC_BMOD_ADDR, | |
ALT_SDMMC_BMOD_DE_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Disables the sdmmc Internal DMA Controller | |
// | |
ALT_STATUS_CODE alt_sdmmc_dma_disable(void) | |
{ | |
alt_clrbits_word(ALT_SDMMC_CTL_ADDR, | |
ALT_SDMMC_CTL_USE_INTERNAL_DMAC_SET_MSK); | |
alt_clrbits_word(ALT_SDMMC_BMOD_ADDR, | |
ALT_SDMMC_BMOD_DE_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Enables the sdmmc Internal DMA Controller. | |
// | |
ALT_STATUS_CODE alt_sdmmc_is_dma_enabled(void) | |
{ | |
if ( ALT_SDMMC_CTL_USE_INTERNAL_DMAC_GET(alt_read_word(ALT_SDMMC_CTL_ADDR)) | |
&& ALT_SDMMC_BMOD_DE_GET(alt_read_word(ALT_SDMMC_BMOD_ADDR))) | |
{ | |
return ALT_E_TRUE; | |
} | |
else | |
{ | |
return ALT_E_FALSE; | |
} | |
} | |
// | |
// Returns the current sdmmc controller interrupt IDMAC status conditions. | |
// | |
uint32_t alt_sdmmc_dma_int_status_get(void) | |
{ | |
return alt_read_word(ALT_SDMMC_IDSTS_ADDR); | |
} | |
// | |
// Returns the SD/MMC internal DMA controller interrupt mask value which | |
// reflects the enabled internal DMA controller interrupt status conditions. | |
// | |
uint32_t alt_sdmmc_dma_int_mask_get(void) | |
{ | |
return alt_read_word(ALT_SDMMC_IDINTEN_ADDR); | |
} | |
// | |
// Clears the specified sdmmc controller interrupt status IDMAC conditions identified | |
// in the mask. | |
// | |
ALT_STATUS_CODE alt_sdmmc_dma_int_clear(const uint32_t mask) | |
{ | |
alt_write_word(ALT_SDMMC_IDSTS_ADDR, mask); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Disable the specified sdmmc controller interrupt IDMAC status conditions identified in | |
// the mask. | |
// | |
ALT_STATUS_CODE alt_sdmmc_dma_int_disable(const uint32_t mask) | |
{ | |
alt_clrbits_word(ALT_SDMMC_IDINTEN_ADDR, mask); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Enable the specified sdmmc controller interrupt status conditions identified in | |
// the mask. | |
// | |
ALT_STATUS_CODE alt_sdmmc_dma_int_enable(const uint32_t mask) | |
{ | |
alt_setbits_word(ALT_SDMMC_IDINTEN_ADDR, mask); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Sets value into this register for the IDMAC FSM to resume normal descriptor fetch operation. | |
// | |
ALT_STATUS_CODE alt_sdmmc_poll_demand_set(const uint32_t value) | |
{ | |
alt_replbits_word(ALT_SDMMC_PLDMND_ADDR, | |
ALT_SDMMC_PLDMND_PD_SET_MSK, | |
ALT_SDMMC_PLDMND_PD_SET(value)); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Disable Card Read Threshold . | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_rd_threshold_disable(void) | |
{ | |
alt_clrbits_word(ALT_SDMMC_CARDTHRCTL_ADDR, | |
ALT_SDMMC_CARDTHRCTL_CARDRDTHREN_SET_MSK); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Enable Card Read Threshold . | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_rd_threshold_enable(const uint32_t threshold) | |
{ | |
alt_replbits_word(ALT_SDMMC_CARDTHRCTL_ADDR, | |
ALT_SDMMC_CARDTHRCTL_CARDRDTHRESHOLD_SET_MSK | |
| ALT_SDMMC_CARDTHRCTL_CARDRDTHREN_SET_MSK, | |
ALT_SDMMC_CARDTHRCTL_CARDRDTHRESHOLD_SET(threshold) | |
| ALT_SDMMC_CARDTHRCTL_CARDRDTHREN_SET(ALT_SDMMC_CARDTHRCTL_CARDRDTHREN_E_END)); | |
return ALT_E_SUCCESS; | |
} | |
// | |
// This function return ALT_E_ERROR if interrupt error was detected | |
// | |
static ALT_STATUS_CODE alt_sdmmc_error_status_detect(void) | |
{ | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
uint32_t int_status = 0; | |
// All sdmmc interrupt status caused by an error | |
uint32_t err = ( ALT_SDMMC_INT_STATUS_RE | |
| ALT_SDMMC_INT_STATUS_RCRC | |
| ALT_SDMMC_INT_STATUS_DCRC | |
| ALT_SDMMC_INT_STATUS_RTO | |
| ALT_SDMMC_INT_STATUS_DRTO | |
| ALT_SDMMC_INT_STATUS_FRUN | |
| ALT_SDMMC_INT_STATUS_HLE | |
| ALT_SDMMC_INT_STATUS_SBE | |
| ALT_SDMMC_INT_STATUS_EBE); | |
int_status = alt_sdmmc_int_status_get(); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
// Checking on errors | |
if (int_status & err) | |
{ | |
status = ALT_E_ERROR; | |
} | |
return status; | |
} | |
// | |
// Read/write all data from/to buffer | |
// | |
static ALT_STATUS_CODE alt_sdmmc_transfer_helper(uint32_t * buffer, | |
const size_t size, | |
ALT_SDMMC_TMOD_t transfer_mode) | |
{ | |
#ifdef LOGGER | |
dprintf("\nalt_sdmmc_transfer_helper\n"); | |
#endif | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
uint32_t data_size = size; | |
bool read_freeze = false; | |
bool write_freeze = false; | |
while (data_size > 0) | |
{ | |
#ifdef LOGGER | |
dprintf("\ndata_size = %x\n", (int)data_size); | |
// Error handling | |
#endif | |
// Error checking | |
status = alt_sdmmc_error_status_detect(); | |
if (status != ALT_E_SUCCESS) | |
{ | |
break; | |
} | |
uint32_t timeout = ALT_SDMMC_TMO_WAITER; | |
do | |
{ | |
read_freeze =(transfer_mode == ALT_SDMMC_TMOD_READ) | |
&& alt_sdmmc_fifo_is_empty() == true; | |
write_freeze = transfer_mode == ALT_SDMMC_TMOD_WRITE | |
&& alt_sdmmc_fifo_is_full() == true; | |
#ifdef LOGGER | |
dprintf("\nread_freeze = %x write_freeze = %x\n", (int)read_freeze, (int)write_freeze); | |
#endif | |
if (--timeout == 0) | |
{ | |
status = ALT_E_TMO; | |
return status; | |
} | |
} | |
while (read_freeze || write_freeze); | |
uint32_t level = alt_sdmmc_fifo_count(); | |
#ifdef LOGGER | |
dprintf("\nfifo level = %x\n", (int)level); | |
#endif | |
// Top up the TX FIFO with read issues | |
if (transfer_mode == ALT_SDMMC_TMOD_WRITE) | |
{ | |
uint32_t free_space = ALT_SDMMC_FIFO_NUM_ENTRIES - level; | |
free_space = MIN(data_size / 4, free_space); | |
for (uint32_t i = 0; i < free_space; i++) | |
{ | |
alt_write_word(ALT_SDMMC_DATA_ADDR, *buffer); | |
++buffer; | |
} | |
data_size -= free_space * 4; | |
} | |
// Read out the resulting received data as they come in. | |
if (transfer_mode == ALT_SDMMC_TMOD_READ) | |
{ | |
level = MIN(data_size / 4, level); | |
for (uint32_t i = 0; i < level; i++) | |
{ | |
*buffer = ALT_SDMMC_DATA_VALUE_GET(alt_read_word(ALT_SDMMC_DATA_ADDR)); | |
++buffer; | |
} | |
data_size -= level * 4; | |
} | |
} | |
return status; | |
} | |
// | |
// Fill descriptors | |
// | |
static ALT_STATUS_CODE alt_sdmmc_dma_trans_helper(uint32_t * buffer, | |
size_t buf_len) | |
{ | |
#ifdef LOGGER | |
dprintf("\nalt_sdmmc_dma_trans_helper: buf_len = %d\n", | |
(int)buf_len); | |
#endif | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
//Pointer to current descriptor | |
ALT_SDMMC_DMA_BUF_DESC_t *cur_dma_desc = dma_cur_descr; | |
uint32_t cur_buffer = (uint32_t)buffer; | |
uint32_t len_left = buf_len; | |
while (len_left > 0) | |
{ | |
//Error checking | |
status = alt_sdmmc_error_status_detect(); | |
if (status != ALT_E_SUCCESS) | |
{ | |
status = ALT_E_ERROR; | |
break; | |
} | |
//If current descriptor is free then fill it | |
if (cur_dma_desc->des0.fld.own == 0) | |
{ | |
int set_len = len_left > ALT_SDMMC_DMA_SEGMENT_SIZE ? ALT_SDMMC_DMA_SEGMENT_SIZE : len_left; | |
//Disable interrupt after it will be free | |
cur_dma_desc->des0.fld.dic = 1;//socfpga->dma_cur_pos % 4; | |
//Set If it is first part of buffer for transfer | |
cur_dma_desc->des0.fld.fs = (buf_len == len_left) ? 1 : 0; | |
//Set size of des2 | |
cur_dma_desc->des1.fld.bs1 = set_len; | |
//Set address of buffer in memory | |
cur_dma_desc->des2.fld.bap1 = cur_buffer; | |
#ifdef LOGGER | |
dprintf("socfpga_setup_dma_add: des_adrdr %08X des2_paddr %08X des1_len %08X len_left %08X\n", | |
(int)cur_dma_desc, (int)cur_buffer, (int)set_len, (int)len_left); | |
#endif | |
//Update address buffer and buffer len | |
cur_buffer += set_len; | |
len_left -= set_len; | |
//Set if it is last part of buffer | |
cur_dma_desc->des0.fld.ld = (len_left == 0) ? 1 : 0; | |
//Descriptor could be used | |
cur_dma_desc->des0.fld.own = 1; | |
//Currernt descriptor set sa next element | |
cur_dma_desc = (ALT_SDMMC_DMA_BUF_DESC_t *)cur_dma_desc->des3.fld.bap2_or_next; | |
} | |
uint32_t idmac_status = alt_sdmmc_dma_int_status_get(); | |
// If DMA status is as descriptor unavailable then resume transfer and clean interrupt status | |
if (idmac_status & ALT_SDMMC_DMA_INT_STATUS_DU) | |
{ | |
alt_sdmmc_poll_demand_set(0xFFFF); | |
alt_sdmmc_dma_int_clear(ALT_SDMMC_DMA_INT_STATUS_ALL); | |
} | |
// If DMA status is another abnormal then break with error | |
else if (idmac_status & ALT_SDMMC_DMA_INT_STATUS_AI) | |
{ | |
status = ALT_E_ERROR; | |
break; | |
} | |
} | |
return status; | |
} | |
// | |
// Waiter of data transfer complete | |
// | |
static ALT_STATUS_CODE alt_sdmmc_data_done_waiter(void) | |
{ | |
ALT_STATUS_CODE status = ALT_E_TMO; | |
uint32_t timeout = ALT_SDMMC_TMO_WAITER; | |
while (--timeout) | |
{ | |
uint32_t int_status; | |
int_status = alt_sdmmc_int_status_get(); | |
// Error checking | |
if (alt_sdmmc_error_status_detect() != ALT_E_SUCCESS) | |
{ | |
status = ALT_E_ERROR; | |
break; | |
} | |
uint32_t idmac_status = alt_sdmmc_dma_int_status_get(); | |
// If DMA status is abnormal then transfer complete with error | |
if (idmac_status & ALT_SDMMC_DMA_INT_STATUS_AI) | |
{ | |
status = ALT_E_ERROR; | |
break; | |
} | |
// Data transfer over caused by complete transfer operation | |
if (int_status & ALT_SDMMC_INT_STATUS_DTO) | |
{ | |
alt_sdmmc_int_clear(ALT_SDMMC_INT_STATUS_DTO); | |
status = ALT_E_SUCCESS; | |
break; | |
} | |
} | |
timeout = ALT_SDMMC_TMO_WAITER; | |
while (!alt_sdmmc_is_idle() && timeout--) | |
; | |
if (timeout == 0) | |
{ | |
status = ALT_E_TMO; | |
} | |
return status; | |
} | |
// | |
// Waiter of clock command complete | |
// | |
static ALT_STATUS_CODE alt_sdmmc_clock_waiter(void) | |
{ | |
ALT_STATUS_CODE status = ALT_E_TMO; | |
uint32_t timeout = ALT_SDMMC_TMO_WAITER; | |
while (--timeout) | |
{ | |
uint32_t cmd_register = alt_read_word(ALT_SDMMC_CMD_ADDR); | |
// Error checking | |
if (alt_sdmmc_error_status_detect() != ALT_E_SUCCESS) | |
{ | |
status = ALT_E_ERROR; | |
break; | |
} | |
// Only for clock command detect complete operation by 0 in start_cmd bit of cmd register | |
if (ALT_SDMMC_CMD_START_CMD_GET(cmd_register) == ALT_SDMMC_CMD_START_CMD_E_NOSTART) | |
{ | |
status = ALT_E_SUCCESS; | |
break; | |
} | |
} | |
return status; | |
} | |
// | |
// Waiter of command complete | |
// | |
static ALT_STATUS_CODE alt_sdmmc_cmd_waiter(void) | |
{ | |
ALT_STATUS_CODE status = ALT_E_TMO; | |
uint32_t timeout = ALT_SDMMC_TMO_WAITER; | |
while (--timeout) | |
{ | |
uint32_t int_status; | |
int_status = alt_sdmmc_int_status_get(); | |
// Error checking | |
if (alt_sdmmc_error_status_detect() != ALT_E_SUCCESS) | |
{ | |
status = ALT_E_ERROR; | |
break; | |
} | |
//Check command done | |
if (int_status & ALT_SDMMC_INT_STATUS_CMD) | |
{ | |
alt_sdmmc_int_clear(ALT_SDMMC_INT_STATUS_CMD); | |
status = ALT_E_SUCCESS; | |
break; | |
} | |
} | |
return status; | |
} | |
// | |
// Read SRC register from card and read supported bus width | |
// | |
static ALT_STATUS_CODE alt_sdmmc_card_scr_get(uint64_t *scr_reg) | |
{ | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
#ifdef LOGGER | |
uint32_t response = 0; | |
#endif | |
uint16_t prev_blk_size = 0; | |
// Save current block size and change it | |
prev_blk_size = alt_sdmmc_block_size_get(); | |
alt_sdmmc_block_size_set(8); | |
alt_sdmmc_byte_count_set(8); | |
// Activate ACMD commands | |
status = alt_sdmmc_command_send(ALT_SDMMC_APP_CMD, rca_number, NULL); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_APP_CMD response = %x\n", (int)response); | |
#endif | |
// Send request for read SRC register | |
status = alt_sdmmc_command_send(ALT_SD_SEND_SCR, 0x0, NULL); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SD_SEND_SCR responce = %x\n", (int)response); | |
#endif | |
// Read SRC register | |
status = alt_sdmmc_transfer_helper((uint32_t*)scr_reg, 8, ALT_SDMMC_TMOD_READ); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
// Transfer complete | |
status = alt_sdmmc_data_done_waiter(); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
dprintf("\nALT_SD_SEND_SCR data = "); | |
uint8_t* scr_buf_8 = (uint8_t*)scr_reg; | |
for (int count = 0; count < 8; count++) | |
{ | |
dprintf("%02x", scr_buf_8[count]); | |
} | |
dprintf("\n"); | |
#endif | |
// Re-change block size | |
alt_sdmmc_block_size_set(prev_blk_size); | |
return status; | |
} | |
// | |
// Set sdmmc card width | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_bus_width_set(const ALT_SDMMC_BUS_WIDTH_t width) | |
{ | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
uint64_t scr_reg; | |
// Read SRC register | |
status = alt_sdmmc_card_scr_get(&scr_reg); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
uint8_t * scr_buf_8 = (uint8_t*)&scr_reg; | |
// Get supported bus width | |
uint32_t supported_bus_width = scr_buf_8[1] & 0xF; | |
#ifdef LOGGER | |
dprintf("\nbus_width_supported = %02x\n", (int)supported_bus_width); | |
#endif | |
if ((supported_bus_width & width) == 0) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
uint32_t set_width_arg; | |
switch (width) | |
{ | |
case ALT_SDMMC_BUS_WIDTH_8: | |
set_width_arg = 0x3; | |
break; | |
case ALT_SDMMC_BUS_WIDTH_4: | |
set_width_arg = 0x2; | |
break; | |
case ALT_SDMMC_BUS_WIDTH_1: | |
set_width_arg = 0x0; | |
break; | |
default: | |
return ALT_E_BAD_ARG; | |
} | |
#ifdef LOGGER | |
uint32_t response = 0; | |
#endif | |
// Activate ACMD commands | |
status = alt_sdmmc_command_send(ALT_SDMMC_APP_CMD, rca_number, NULL); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_APP_CMD response = %x\n", (int)response); | |
#endif | |
// Send new card bus width | |
status = alt_sdmmc_command_send(ALT_SD_SET_BUS_WIDTH, set_width_arg, NULL); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SD_SET_BUS_WIDTH responce = %x\n", (int)response); | |
#endif | |
// Set new bus width in controller register | |
alt_sdmmc_bus_width_set(width); | |
return status; | |
} | |
// | |
// Set block size | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_block_size_set(const uint16_t block_size) | |
{ | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
// Send new block size to card | |
status = alt_sdmmc_command_send(ALT_SDMMC_SET_BLOCKLEN, block_size, NULL); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
uint32_t response; | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_SET_BLOCKLEN response = %x\n", (int)response); | |
#endif | |
// Set new block size in controller register | |
alt_sdmmc_block_size_set(block_size); | |
return status; | |
} | |
// | |
// Enumerated Card Stack ident sdio io only | |
// | |
static ALT_STATUS_CODE alt_sdmmc_card_ident_io_only(ALT_SDMMC_CARD_INFO_t *card_info) | |
{ | |
#ifdef LOGGER | |
dprintf("\nalt_sdmmc_card_ident_io_only\n"); | |
#endif | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
uint32_t int_status = 0; | |
uint32_t response = 0; | |
// Enumerated Card Stack p.2a - 2b | |
// Activates the card's initialization process. | |
status = alt_sdmmc_command_send(ALT_SDMMC_SEND_OP_COND, 0x0, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
int_status = alt_sdmmc_int_status_get(); | |
if (int_status & ALT_SDMMC_INT_STATUS_RTO) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
else | |
{ | |
return status; | |
} | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_SEND_OP_COND_1 = %x\n", (int)response); | |
#endif | |
// Enumerated Card Stack p.2c | |
status = alt_sdmmc_command_send(ALT_SDMMC_SEND_OP_COND, 0x100000, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
int_status = alt_sdmmc_int_status_get(); | |
if (int_status & ALT_SDMMC_INT_STATUS_RTO) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
else | |
{ | |
return status; | |
} | |
} | |
#ifdef LOGGER | |
dprintf("\nALT_SDMMC_SEND_OP_COND_2 response = %x\n", (int)response); | |
#endif | |
// Enumerated Card Stack p.2d | |
bool is_sdio_combo = response & (1 << 27); | |
card_info->card_type = (is_sdio_combo) | |
? ALT_SDMMC_CARD_TYPE_NOTDETECT | |
: ALT_SDMMC_CARD_TYPE_SDIOIO; | |
return ALT_E_SUCCESS; | |
} | |
// | |
// Enumerated Card Stack ident sdhc type | |
// | |
static ALT_STATUS_CODE alt_sdmmc_card_ident_sdhc(ALT_SDMMC_CARD_INFO_t *card_info) | |
{ | |
#ifdef LOGGER | |
dprintf("\nalt_sdmmc_card_ident_sdhc\n"); | |
#endif | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
uint32_t int_status = 0; | |
uint32_t response = 0; | |
// Resets all cards to Idle State | |
status = alt_sdmmc_command_send(ALT_SDMMC_GO_IDLE_STATE, 0x0, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
int_status = alt_sdmmc_int_status_get(); | |
if (int_status & ALT_SDMMC_INT_STATUS_RTO) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
else | |
{ | |
return status; | |
} | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_GO_IDLE_STATE response = %x\n", (int)response); | |
#endif | |
// Enumerated Card Stack p.3a | |
// For only SDC V2. Check voltage range. | |
status = alt_sdmmc_command_send(ALT_SDMMC_IF_COND, 0x1AA, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
int_status = alt_sdmmc_int_status_get(); | |
if (int_status & ALT_SDMMC_INT_STATUS_RTO) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
else | |
{ | |
return status; | |
} | |
} | |
alt_sdmmc_read_short_response(&response); | |
#ifdef LOGGER | |
dprintf("\nALT_SDMMC_IF_COND response = %x\n", (int)response); | |
#endif | |
if (response != 0x1AA) | |
{ | |
return ALT_E_ERROR; | |
} | |
// Indicates to the card that the next command is an | |
// application specific command rather than a | |
// standard command | |
status = alt_sdmmc_command_send(ALT_SDMMC_APP_CMD, 0x0, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
int_status = alt_sdmmc_int_status_get(); | |
if (int_status & ALT_SDMMC_INT_STATUS_RTO) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
else | |
{ | |
return status; | |
} | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_APP_CMD response = %x\n", (int)response); | |
#endif | |
// Enumerated Card Stack p.3c | |
// Asks the accessed card to send its operating condition | |
// register (OCR) con tent in the response on the CMD | |
// line. | |
status = alt_sdmmc_command_send(ALT_SD_SEND_OP_COND, 0x40FF8000, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
int_status = alt_sdmmc_int_status_get(); | |
if (int_status & ALT_SDMMC_INT_STATUS_RTO) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
else | |
{ | |
return status; | |
} | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SD_SEND_OP_COND responce = %x\n", (int)response); | |
#endif | |
//Enumerated Card Stack p.3d | |
card_info->card_type = ALT_SDMMC_CARD_TYPE_SDHC; | |
return status; | |
} | |
// | |
//Enumerated Card Stack ident sd type | |
// | |
static ALT_STATUS_CODE alt_sdmmc_card_ident_sd(ALT_SDMMC_CARD_INFO_t *card_info) | |
{ | |
#ifdef LOGGER | |
dprintf("\nalt_sdmmc_card_ident_sd\n"); | |
#endif | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
uint32_t int_status = 0; | |
#ifdef LOGGER | |
uint32_t response = 0; | |
#endif | |
// Enumerated Card Stack p.3e | |
// Indicates to the card that the next command is an | |
// application specific command rather than a | |
// standard command | |
status = alt_sdmmc_command_send(ALT_SDMMC_APP_CMD, 0x0, NULL); | |
if (status != ALT_E_SUCCESS) | |
{ | |
int_status = alt_sdmmc_int_status_get(); | |
if (int_status & ALT_SDMMC_INT_STATUS_RTO) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
else | |
{ | |
return status; | |
} | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_APP_CMD response = %x\n", (int)response); | |
#endif | |
// Asks the accessed card to send its operating condition | |
// register (OCR) con tent in the response on the CMD | |
// line. | |
status = alt_sdmmc_command_send(ALT_SD_SEND_OP_COND, 0x00FF8000, NULL); | |
if (status != ALT_E_SUCCESS) | |
{ | |
int_status = alt_sdmmc_int_status_get(); | |
if (int_status & ALT_SDMMC_INT_STATUS_RTO) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
else | |
{ | |
return status; | |
} | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SD_SEND_OP_COND responce = %x\n", (int)response); | |
#endif | |
// Enumerated Card Stack p.3f | |
card_info->card_type = ALT_SDMMC_CARD_TYPE_SD; | |
return status; | |
} | |
// | |
// Enumerated Card Stack enumarete sd cart type | |
// | |
static ALT_STATUS_CODE alt_sdmmc_card_enum_sd(ALT_SDMMC_CARD_INFO_t *card_info) | |
{ | |
#ifdef LOGGER | |
dprintf("\nalt_sdmmc_card_enum_sd\n"); | |
#endif | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
uint32_t clk_div = clock_freq / (2 * 400000); | |
status = alt_sdmmc_card_clk_div_set(clk_div); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
status = alt_sdmmc_card_clk_enable(false); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
uint32_t response = 0; | |
// Resets all cards to Idle State | |
status = alt_sdmmc_command_send(ALT_SDMMC_GO_IDLE_STATE, 0x0, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_GO_IDLE_STATE response = %x\n", (int)response); | |
#endif | |
// For only SDC V2. Check voltage range. | |
status = alt_sdmmc_command_send(ALT_SDMMC_IF_COND, 0x1AA, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_IF_COND response = %x\n", (int)response); | |
#endif | |
/* OCR Bit VDD Voltage Window | |
0-3 Reserved | |
4 1.6-1.7 | |
5 1.7-1.8 | |
6 1.8-1.9 | |
7 1.9-2.0 | |
8 2.0-2.1 | |
9 2.1-2.2 | |
10 2.2-2.3 | |
11 2.3-2.4 | |
12 2.4-2.5 | |
13 2.5-2.6 | |
14 2.6-2.7 | |
15 2.7-2.8 | |
16 2.8-2.9 | |
17 2.9-3.0 | |
18 3.0-3.1 | |
19 3.1-3.2 | |
20 3.2-3.3 | |
21 3.3-3.4 | |
22 3.4-3.5 | |
23 3.5-3.6 | |
24-29 Reserved | |
30 High capacity card | |
31 Card power up status bit (busy) | |
*/ | |
uint32_t ocr_reg = 0xFF8000; | |
if (card_info->card_type == ALT_SDMMC_CARD_TYPE_SDHC) | |
{ | |
ocr_reg |= (1 << 30); | |
} | |
do | |
{ | |
status = alt_sdmmc_command_send(ALT_SDMMC_APP_CMD, 0x0, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_APP_CMD response = %x\n", (int)response); | |
#endif | |
volatile uint32_t timeout = 1000000; | |
// Wait while sdmmc module is reseting | |
while (timeout--) | |
; | |
status = alt_sdmmc_command_send(ALT_SD_SEND_OP_COND, 0x40FF8000, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
alt_sdmmc_read_short_response(&response); | |
#ifdef LOGGER | |
dprintf("\nALT_SD_SEND_OP_COND response = %x\n", (int)response); | |
#endif | |
} | |
while ((response & (1UL << 31)) == 0); | |
//Asks any card to send their CID numbers on the CMD line. | |
//(Any card that is connected to the host will respond.) | |
status = alt_sdmmc_command_send(ALT_SDMMC_ALL_SEND_CID, 0x0, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_ALL_SEND_CID response = %x\n", (int)response); | |
#endif | |
// Asks the card to publish a new relative address (RCA). | |
status = alt_sdmmc_command_send(ALT_SDMMC_SET_RELATIVE_ADDR, 0x0, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
alt_sdmmc_read_short_response(&response); | |
#ifdef LOGGER | |
dprintf("\nALT_SDMMC_SET_RELATIVE_ADDR responce = %x\n", (int)response); | |
#endif | |
uint32_t RCA_number = response & 0xFFFF0000; | |
rca_number = RCA_number; | |
// Addressed card sends its card identification (CID) on the CMD line. | |
status = alt_sdmmc_command_send(ALT_SDMMC_SEND_CID, rca_number, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_SEND_CID responce = %x\n", (int)response); | |
#endif | |
// Addressed card sends its card-specific data (CSD) | |
// on the CMD line. | |
status = alt_sdmmc_command_send(ALT_SDMMC_SEND_CSD, rca_number, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_SEND_CSD responce = %x\n", (int)response); | |
#endif | |
ALT_SDMMC_RESPONSE_t response_long; | |
alt_sdmmc_read_long_response(&response_long); | |
static const uint32_t tran_speed_mul_x10[] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; | |
static const uint32_t freq_unit[] = { 100000, 1000000, 10000000, 100000000 }; | |
// card_info->max_r_blkln = pow(2, ALT_SDMMC_CSD_MAX_R_BLK_GET(response_long.resp2)); | |
// card_info->max_w_blkln = pow(2, ALT_SDMMC_CSD_MAX_W_BLK_GET(response_long.resp0)); | |
card_info->max_r_blkln = 1 << ALT_SDMMC_CSD_MAX_R_BLK_GET(response_long.resp2); | |
card_info->max_w_blkln = 1 << ALT_SDMMC_CSD_MAX_W_BLK_GET(response_long.resp0); | |
card_info->partial_r_allowed = ALT_SDMMC_CSD_PART_R_ALLOW_GET(response_long.resp2); | |
card_info->partial_w_allowed = ALT_SDMMC_CSD_PART_W_ALLOW_GET(response_long.resp0); | |
uint32_t rate_unit = ALT_SDMMC_CSD_SPEED_RATE_GET(response_long.resp3); | |
uint32_t time_val = ALT_SDMMC_CSD_SPEED_TIME_GET(response_long.resp3); | |
if ((time_val != 0) && (rate_unit <= 3)) | |
{ | |
// uint32_t speed_rate = (rate_unit == 0) ? 100 : pow(10, rate_unit - 1) * 1000; | |
uint32_t speed_rate = freq_unit[rate_unit]; | |
card_info->xfer_speed = speed_rate * tran_speed_mul_x10[time_val] / 10; | |
} | |
else | |
{ | |
return ALT_E_ERROR; | |
} | |
// Command toggles a card between the Stand-by | |
// and Transfer states or between the Programming | |
// and Disconnect state | |
status = alt_sdmmc_command_send(ALT_SDMMC_SEL_DES_CARD, rca_number, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_SEL_DES_CARD responce = %x\n", (int)response); | |
#endif | |
// Addressed card sends its status register | |
status = alt_sdmmc_command_send(ALT_SDMMC_SEND_STATUS, rca_number, &response); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
alt_sdmmc_read_short_response(&response); | |
dprintf("\nALT_SDMMC_SEND_STATUS responce = %x\n", (int)response); | |
#endif | |
return status; | |
} | |
// | |
// Enumerated Card Stack | |
// | |
ALT_STATUS_CODE alt_sdmmc_card_identify(ALT_SDMMC_CARD_INFO_t *card_info) | |
{ | |
#ifdef LOGGER | |
dprintf("\nalt_sdmmc_card_identify\n"); | |
#endif | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
card_info->card_type = ALT_SDMMC_CARD_TYPE_NOTDETECT; | |
//Enumerated Card Stack p.1 | |
alt_sdmmc_bus_width_set(ALT_SDMMC_BUS_WIDTH_1); | |
if (status == ALT_E_SUCCESS) | |
{ | |
status = alt_sdmmc_card_ident_io_only(card_info); | |
// If card is identified as SDIO IO only or SDIO COMBO then prepare it | |
if (card_info->card_type != ALT_SDMMC_CARD_TYPE_NOTDETECT && status == ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
} | |
if (status == ALT_E_SUCCESS) | |
{ | |
status = alt_sdmmc_card_ident_sdhc(card_info); | |
if (card_info->card_type != ALT_SDMMC_CARD_TYPE_NOTDETECT && status == ALT_E_SUCCESS) | |
{ | |
// If card is identified as SDHC then prepare it | |
status = alt_sdmmc_card_enum_sd(card_info); | |
return status; | |
} | |
} | |
if (status != ALT_E_SUCCESS) | |
{ | |
status = alt_sdmmc_card_ident_sd(card_info); | |
if (card_info->card_type != ALT_SDMMC_CARD_TYPE_NOTDETECT && status == ALT_E_SUCCESS) | |
{ | |
// If card is identified as SD card then prepare it | |
status = alt_sdmmc_card_enum_sd(card_info); | |
return status; | |
} | |
} | |
return status; | |
} | |
// | |
// Send the a command and command argument to the card and optionally return the | |
// command response. | |
// | |
ALT_STATUS_CODE alt_sdmmc_command_send(ALT_SDMMC_CMD_INDEX_t command, | |
uint32_t command_arg, uint32_t *response) | |
{ | |
const ALT_SDMMC_CMD_CONFIG_t * cmd_cfg = NULL; | |
bool found = false; | |
if (command == ALT_SDMMC_CLK_INDEX) | |
{ | |
cmd_cfg = &cmd_clock_cfg; | |
found = true; | |
} | |
for (uint32_t counter = 0; counter < ARRAY_COUNT(cmd_default_cfg); counter++) | |
{ | |
if (found == true) | |
{ | |
break; | |
} | |
if (cmd_default_cfg[counter].cmd_index == command) | |
{ | |
cmd_cfg = &cmd_default_cfg[counter]; | |
found = true; | |
} | |
} | |
if (found == false) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
if (cmd_cfg->wait_prvdata_complete) | |
{ | |
uint32_t timeout = ALT_SDMMC_TMO_WAITER; | |
while (alt_sdmmc_is_busy() && timeout--) | |
; | |
} | |
// Create interrupt mask by command configurations | |
uint32_t int_mask = ALT_SDMMC_INT_STATUS_RE // Response error | |
| ALT_SDMMC_INT_STATUS_RTO // Response timeout | |
| ALT_SDMMC_INT_STATUS_CD // Card detect (CD) interrupt | |
| ALT_SDMMC_INT_STATUS_HLE // Hardware Locked Write Error | |
| ALT_SDMMC_INT_STATUS_CMD; // Command done (CD) interrupt | |
if (cmd_cfg->data_expected == true) | |
{ | |
int_mask |= ALT_SDMMC_INT_STATUS_DTO // Data transfer over | |
| ALT_SDMMC_INT_STATUS_RCRC // Response CRC error | |
| ALT_SDMMC_INT_STATUS_DCRC // Data CRC error | |
| ALT_SDMMC_INT_STATUS_HTO // Data starvation by host timeout | |
| ALT_SDMMC_INT_STATUS_FRUN // FIFO underrun/overrun error | |
| ALT_SDMMC_INT_STATUS_EBE; // End-bit error | |
if (cmd_cfg->write_active == ALT_SDMMC_TMOD_WRITE) | |
{ | |
int_mask |= ALT_SDMMC_INT_STATUS_TXDR // Transmit FIFO data request (TXDR) | |
| ALT_SDMMC_INT_STATUS_HLE; // Hardware locked write error (HLE) | |
} | |
else | |
{ | |
int_mask |= ALT_SDMMC_INT_STATUS_RXDR // Receive FIFO data request (RXDR) | |
|ALT_SDMMC_INT_STATUS_SBE; // Start-bit error (SBE) | |
} | |
} | |
alt_sdmmc_int_disable(ALT_SDMMC_INT_STATUS_ALL); | |
// Reset all possible interrupts | |
alt_sdmmc_int_clear(ALT_SDMMC_INT_STATUS_ALL); | |
// Interrupts enable | |
alt_sdmmc_int_enable(int_mask); | |
// Setup the Argument Register and send CMD | |
alt_sdmmc_cmd_arg_set(command_arg); | |
// Set command configuraions | |
alt_sdmmc_cmd_set(command, cmd_cfg, false); | |
// Send commnd | |
alt_sdmmc_cmd_set(command, cmd_cfg, true); | |
#ifdef LOGGER | |
uint32_t state = (uint32_t)ALT_SDMMC_STAT_CMD_FSM_STATES_GET(alt_read_word(ALT_SDMMC_STAT_ADDR)); | |
uint32_t dma_state = (uint32_t)ALT_SDMMC_IDSTS_FSM_GET(alt_read_word(ALT_SDMMC_IDSTS_ADDR)); | |
dprintf("\nstate %x dma_state %x\n", (int)state, (int)dma_state); | |
dprintf("\nCMD = %d ARG = %x\n", (int)command, (int)command_arg); | |
#endif | |
ALT_STATUS_CODE status = 0; | |
if (cmd_cfg->update_clock_registers_only == true) | |
{ | |
//Wait for complete clock update command | |
status = alt_sdmmc_clock_waiter(); | |
#ifdef LOGGER | |
if (status == ALT_E_TMO) | |
{ | |
dprintf("\nTIMEOUT\n"); | |
} | |
#endif | |
return status; | |
} | |
//Wait for complete | |
if ( alt_sdmmc_is_dma_enabled() == ALT_E_FALSE | |
|| cmd_cfg->data_expected == false) | |
{ | |
status = alt_sdmmc_cmd_waiter(); | |
} | |
#ifdef LOGGER | |
if (status == ALT_E_TMO) | |
{ | |
dprintf("\nTIMEOUT\n"); | |
} | |
#endif | |
if (status == ALT_E_SUCCESS) | |
{ | |
alt_sdmmc_read_short_response(response); | |
} | |
return status; | |
} | |
static ALT_STATUS_CODE alt_sdmmc_transfer(uint32_t start_addr, | |
uint32_t buffer[], | |
const size_t buf_len, | |
ALT_SDMMC_TMOD_t transfer_mode) | |
{ | |
if (buf_len == 0) | |
{ | |
return ALT_E_SUCCESS; | |
} | |
if (!alt_sdmmc_is_idle()) | |
{ | |
return ALT_E_ERROR; | |
} | |
uint16_t block_size = alt_sdmmc_block_size_get(); | |
if ( (start_addr % block_size != 0) | |
|| (buf_len % block_size != 0)) | |
{ | |
return ALT_E_BAD_ARG; | |
} | |
ALT_STATUS_CODE status = ALT_E_SUCCESS; | |
// Number of block to transfer | |
uint32_t block_count = buf_len / block_size; | |
// New count of reading byte | |
uint32_t byte_count = block_count * block_size; | |
// reset FIFO | |
status = alt_sdmmc_fifo_reset(); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
// reset DMA | |
status = alt_sdmmc_dma_reset(); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
alt_sdmmc_byte_count_set(byte_count); | |
alt_sdmmc_card_rd_threshold_enable(0x80); | |
uint32_t cmd_index = 0; | |
if (buf_len == block_size) | |
{ | |
cmd_index = (transfer_mode == ALT_SDMMC_TMOD_READ) | |
? ALT_SDMMC_READ_SINGLE_BLOCK | |
: ALT_SDMMC_WRITE_BLOCK; | |
} | |
else | |
{ | |
cmd_index = (transfer_mode == ALT_SDMMC_TMOD_READ) | |
? ALT_SDMMC_READ_MULTIPLE_BLOCK | |
: ALT_SDMMC_WRITE_MULTIPLE_BLOCK; | |
} | |
if (alt_sdmmc_is_dma_enabled()) | |
{ | |
// Clean descriptor chain | |
alt_sdmmc_desc_chain_clear(); | |
alt_sdmmc_dma_start(dma_cur_descr, 0x0, | |
ALT_SDMMC_DMA_PBL_1, false); | |
//Enable all dma interrupt status | |
alt_sdmmc_dma_int_enable(ALT_SDMMC_DMA_INT_STATUS_ALL); | |
} | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
#ifdef LOGGER | |
dprintf("\nstart_addr = %d\n", (int)start_addr); | |
#endif | |
//Send transfer command | |
status = alt_sdmmc_command_send((ALT_SDMMC_CMD_INDEX_t)cmd_index, start_addr / block_size, NULL); | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
//Send or read data | |
if (alt_sdmmc_is_dma_enabled()) | |
{ | |
//Fill descriptors | |
status = alt_sdmmc_dma_trans_helper(buffer, byte_count); | |
} | |
else | |
{ | |
status = alt_sdmmc_transfer_helper(buffer, byte_count, transfer_mode); | |
} | |
if (status != ALT_E_SUCCESS) | |
{ | |
return status; | |
} | |
//Wait for data transfer complete | |
status = alt_sdmmc_data_done_waiter(); | |
return status; | |
} | |
// | |
// This function performs SDMMC write. | |
// | |
ALT_STATUS_CODE alt_sdmmc_write(void *dest, void *src, const size_t size) | |
{ | |
return alt_sdmmc_transfer((uint32_t)dest, src, size, ALT_SDMMC_TMOD_WRITE); | |
} | |
// | |
// This function performs SDMMC read. | |
// | |
ALT_STATUS_CODE alt_sdmmc_read(void *dest, void *src, const size_t size) | |
{ | |
return alt_sdmmc_transfer((uint32_t)src, dest, size, ALT_SDMMC_TMOD_READ); | |
} |