1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00

cpu/sam0_common/periph/sdhc: busy wait implementation

This commit is contained in:
Gunar Schorcht 2023-06-18 16:27:15 +02:00 committed by Benjamin Valentin
parent b859da8495
commit 7899e8002e

View File

@ -66,6 +66,10 @@
#define SDHC_CLOCK_SLOW SAM0_GCLK_TIMER
#endif
#ifndef SDHC_ENABLE_HS
#define SDHC_ENABLE_HS 1
#endif
/**
* @brief The board can overwrite this if only a single SDHC instance is used
* to save 80 Bytes of ROM.
@ -126,26 +130,73 @@ static void _delay(unsigned us)
}
}
static void _reset_all(sdhc_state_t *state)
/**
* @brief Reset the entire SDHC peripheral or a part of it
*
* @param state SDHC device context
* @param type Reset type
* [SDHC_SRR_SWRSTALL | SDHC_SRR_SWRSTCMD | SDHC_SRR_SWRSTDAT]
*/
static void _reset_sdhc(sdhc_state_t *state, uint8_t type)
{
SDHC_DEV->SRR.reg = SDHC_SRR_SWRSTALL;
while (SDHC_DEV->SRR.bit.SWRSTALL) {}
state->need_init = true;
state->error = 0;
SDHC_DEV->SRR.reg = type;
while (SDHC_DEV->SRR.reg & type) {}
if (type == SDHC_SRR_SWRSTALL) {
/* trigger card_init */
state->need_init = true;
state->error = 0;
}
}
static uint32_t _wait_for_event(sdhc_state_t *state)
/**
* @brief Wait for a given event while checking for errors
*
* @param state SDHC device context
* @param event Event to wait for [SDHC_NISTR_*]
* @param error_mask Mask of errors to be checked [SDHC_EISTR_*]
* @param reset Reset type in case of errors
* [SDHC_SRR_SWRSTALL | SDHC_SRR_SWRSTCMD | SDHC_SRR_SWRSTDAT]
*
* @return true if event occurred or false on error
*/
static bool _wait_for_event(sdhc_state_t *state,
uint16_t event, uint16_t error_mask,
uint8_t reset)
{
uint32_t res;
/* wait for the event given by event mask */
do {
/* check for any error in error mask */
if (SDHC_DEV->EISTR.reg & error_mask) {
state->error = SDHC_DEV->EISTR.reg;
SDHC_DEV->EISTR.reg = SDHC_EISTR_MASK;
if (IS_USED(ENABLE_DEBUG)) {
DEBUG("sdhc error: %x, ", state->error);
switch (reset) {
case SDHC_SRR_SWRSTCMD:
DEBUG("reset CMD\n");
break;
case SDHC_SRR_SWRSTDAT:
DEBUG("reset DAT\n");
break;
case SDHC_SRR_SWRSTALL:
DEBUG("reset ALL\n");
break;
default:
assert(false);
break;
}
}
_reset_sdhc(state, reset);
return false;
}
} while (!(SDHC_DEV->NISTR.reg & event));
/* SDHC runs off CPU clock - block IDLE so that the clock does not stop */
pm_block(3);
mutex_lock(&state->sync);
pm_unblock(3);
/* clear the event */
SDHC_DEV->NISTR.reg = event;
res = state->error;
state->error = 0;
return res;
return true;
}
static void _init_clocks(sdhc_state_t *state)
@ -220,7 +271,9 @@ int sdhc_init(sdhc_state_t *state)
state->sync = _init_locked;
_init_clocks(state);
_reset_all(state);
_reset_sdhc(state, SDHC_SRR_SWRSTALL);
SDHC_DEV->NISIER.reg = NISTR_CARD_DETECT;
SDHC_DEV->TCR.reg = 14; /* max timeout is 14 or about 1sec */
SDHC_DEV->PCR.reg = SDHC_PCR_SDBPWR | SDHC_PCR_SDBVSEL_3V3;
@ -300,7 +353,7 @@ int sdhc_init(sdhc_state_t *state)
_set_hc(state);
/* if it is high speed capable, (well it is) */
if (SDHC_DEV->CA0R.bit.HSSUP) {
if (IS_USED(SDHC_ENABLE_HS) && SDHC_DEV->CA0R.bit.HSSUP) {
if (!_test_high_speed(state)) {
return -EIO;
}
@ -355,15 +408,10 @@ bool sdhc_send_cmd(sdhc_state_t *state, uint32_t cmd, uint32_t arg)
: SDHC_EISTR_CMDTEO | SDHC_EISTR_CMDEND | SDHC_EISTR_CMDIDX | SDHC_EISTR_DATTEO |
SDHC_EISTR_DATEND | SDHC_EISTR_ADMA | SDHC_EISTR_CMDCRC | SDHC_EISTR_DATCRC;
SDHC_DEV->NISIER.reg = SDHC_NISTR_CMDC | NISTR_CARD_DETECT;
SDHC_DEV->EISIER.reg = eis;
SDHC_DEV->ARG1R.reg = arg; /* setup the argument register */
SDHC_DEV->CR.reg = command; /* send command */
if (_wait_for_event(state)) {
SDHC_DEV->SRR.reg = SDHC_SRR_SWRSTCMD; /* reset command */
while (SDHC_DEV->SRR.bit.SWRSTCMD) {}
if (!_wait_for_event(state, SDHC_NISTR_CMDC, eis, SDHC_SRR_SWRSTCMD)) {
return false;
}
@ -396,12 +444,26 @@ static void _set_speed(sdhc_state_t *state, uint32_t fsdhc)
/* since both examples use divided clock rather than programmable - just use divided here */
SDHC_DEV->CCR.reg &= ~SDHC_CCR_CLKGSEL; /* divided clock */
/* Fsdclk = Fsdhc_core/(2 * div) */
div = (sam0_gclk_freq(SDHC_CLOCK) / fsdhc) / 2;
/* According to the data sheet the divided clock is given by
*
* Fsdclk = Fsdhc_core/(2 * div)
*
* Hovewer, this seems to be wrong since the SD CLK is always exactly the half.
* So it seems that the clock is given by
*
* Fsdclk = Fsdhc_core/(4 * div)
*/
if (SDHC_CLOCK == SAM0_GCLK_100MHZ) {
/* if the FDPLL1 with 100 MHz is used, we can use 25 MHz/50 MHz clocks */
div = (sam0_gclk_freq(SDHC_CLOCK) / fsdhc) / 4;
}
else {
div = (sam0_gclk_freq(SDHC_CLOCK) / fsdhc) / 2;
/* high speed div must not be 0 */
if (SDHC_DEV->HC1R.bit.HSEN && (div == 0)) {
div = 1;
/* high speed div must not be 0 */
if (SDHC_DEV->HC1R.bit.HSEN && (div == 0)) {
div = 1;
}
}
/* write the 10 bit clock divider */
@ -569,10 +631,24 @@ static bool _test_version(sdhc_state_t *state)
return false;
}
/* wait until buffer read ready */
if (!_wait_for_event(state, SDHC_NISTR_BRDRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
for (int words = 0; words < (SD_SCR_REG_BSIZE / 4); words++) {
*p++ = SDHC_DEV->BDPR.reg;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
/* Get SD Memory Card - Spec. Version */
switch (SD_SCR_SD_SPEC(scr)) {
case SD_SCR_SD_SPEC_1_0_01:
@ -606,17 +682,16 @@ static bool _init_transfer(sdhc_state_t *state, uint32_t cmd, uint32_t arg, uint
uint32_t tmr;
uint32_t command;
uint32_t eis;
uint32_t timeout = 0xFFFFFFFF;
/* wait if card is busy */
while (SDHC_DEV->PSR.reg & (SDHC_PSR_CMDINHC | SDHC_PSR_CMDINHD)) {}
if (cmd & MCI_CMD_WRITE) {
tmr = SDHC_TMR_DTDSEL_WRITE;
SDHC_DEV->NISIER.reg = SDHC_NISTR_BWRRDY | NISTR_CARD_DETECT;
}
else {
tmr = SDHC_TMR_DTDSEL_READ;
SDHC_DEV->NISIER.reg = SDHC_NISTR_BRDRDY | NISTR_CARD_DETECT;
}
if (cmd & MCI_CMD_SDIO_BYTE) {
@ -672,17 +747,24 @@ static bool _init_transfer(sdhc_state_t *state, uint32_t cmd, uint32_t arg, uint
DEBUG("sdhc: send cmd %lx\n", command);
SDHC_DEV->EISIER.reg = eis;
SDHC_DEV->SSAR.reg = num_blocks; /* Setup block size for Auto CMD23 */
SDHC_DEV->ARG1R.reg = arg; /* setup the argument register */
SDHC_DEV->CR.reg = command; /* send command */
if (_wait_for_event(state)) {
DEBUG("sdhc error: %x, reset all\n", state->error);
_reset_all(state);
if (!_wait_for_event(state, SDHC_NISTR_CMDC, eis, SDHC_SRR_SWRSTCMD)) {
return false;
}
if (cmd & MCI_RESP_BUSY) {
do {
if (--timeout == 0) {
SDHC_DEV->SRR.reg = SDHC_SRR_SWRSTCMD; /* reset command */
while (SDHC_DEV->SRR.bit.SWRSTCMD) {}
return false;
}
} while (!(SDHC_DEV->PSR.reg & SDHC_PSR_DATLL(1))); /* DAT[0] is busy bit */
}
return true;
}
@ -741,12 +823,28 @@ int sdhc_read_blocks(sdhc_state_t *state, uint32_t address, void *dst, uint16_t
goto out;
}
/* wait until buffer read ready */
if (!_wait_for_event(state, SDHC_NISTR_BRDRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
int num_words = (num_blocks * SD_MMC_BLOCK_SIZE) / 4;
for (int words = 0; words < num_words; words++) {
while (!SDHC_DEV->PSR.bit.BUFRDEN) {}
*p++ = SDHC_DEV->BDPR.reg;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
out:
mutex_unlock(&state->lock);
return res;
@ -808,6 +906,14 @@ int sdhc_write_blocks(sdhc_state_t *state, uint32_t address, const void *src,
goto out;
}
/* wait until buffer write ready */
if (!_wait_for_event(state, SDHC_NISTR_BWRRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
/* Write data */
int num_words = (num_blocks * SD_MMC_BLOCK_SIZE) / 4;
for (int words = 0; words < num_words; words++) {
@ -815,6 +921,14 @@ int sdhc_write_blocks(sdhc_state_t *state, uint32_t address, const void *src,
SDHC_DEV->BDPR.reg = *p++;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
res = -EIO;
goto out;
}
out:
mutex_unlock(&state->lock);
return res;
@ -966,10 +1080,24 @@ static bool _test_high_speed(sdhc_state_t *state)
return false;
}
/* wait until buffer read ready */
if (!_wait_for_event(state, SDHC_NISTR_BRDRDY,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
for (int words = 0; words < (SD_SW_STATUS_BSIZE / 4); words++) {
*p++ = SDHC_DEV->BDPR.reg;
}
/* wait until transfer is complete */
if (!_wait_for_event(state, SDHC_NISTR_TRFC,
SDHC_EISTR_DATTEO | SDHC_EISTR_DATCRC | SDHC_EISTR_DATEND,
SDHC_SRR_SWRSTALL)) {
return false;
}
if (SDHC_DEV->RR[0].reg & CARD_STATUS_SWITCH_ERROR) {
return false;
}
@ -1016,32 +1144,13 @@ static bool _test_bus_width(sdhc_state_t *state)
static void _isr(sdhc_state_t *state)
{
uint16_t events = (SDHC_DEV->NISIER.reg & ~NISTR_CARD_DETECT)
| SDHC_NISTR_ERRINT;
if (SDHC_DEV->EISTR.reg) {
state->error = SDHC_DEV->EISTR.reg;
}
DEBUG("NISTR: %x\n", SDHC_DEV->NISTR.reg);
DEBUG("EISTR: %x\n", SDHC_DEV->EISTR.reg);
DEBUG("ACESR: %x\n", SDHC_DEV->ACESR.reg);
/* we got the awaited event */
if (SDHC_DEV->NISTR.reg & events) {
DEBUG_PUTS("unlock");
mutex_unlock(&state->sync);
}
/* if card got inserted we need to re-init */
if (SDHC_DEV->NISTR.reg & NISTR_CARD_DETECT) {
SDHC_DEV->NISTR.reg = NISTR_CARD_DETECT;
DEBUG_PUTS("card presence changed");
state->need_init = true;
}
SDHC_DEV->EISTR.reg = SDHC_EISTR_MASK;
SDHC_DEV->NISTR.reg = SDHC_NISTR_MASK;
cortexm_isr_end();
}