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:
parent
b859da8495
commit
7899e8002e
@ -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();
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user