mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-04-11 16:37:45 -04:00
stm32h7: sdmmc: implement missing R1b response handling
The driver didn't handle the busy signal at the end of some command responses (R1b response type), notably the CMD12 (STOP_TRANSMISSION) sent after a multiblock write would time out because DTIMER==0 and leave the DPSM in an incorrect state for the next command, causing a hang. Change-Id: I406337a7612f759418a4872979aa2de5aa2244c7
This commit is contained in:
parent
c390467d19
commit
06c9d87f53
1 changed files with 49 additions and 2 deletions
|
|
@ -28,6 +28,7 @@
|
|||
/* cmd_wait flags */
|
||||
#define WAIT_CMD 0x01
|
||||
#define WAIT_DATA 0x02
|
||||
#define WAIT_BUSY 0x04
|
||||
|
||||
/* Maximum number of bytes for 1 transfer */
|
||||
#define MAX_DATA_LEN \
|
||||
|
|
@ -49,6 +50,14 @@
|
|||
#define DATA_END_BITS \
|
||||
(DATA_SUCCESS_BITS | DATA_ERROR_BITS)
|
||||
|
||||
/* IRQs for busy wait phase */
|
||||
#define BUSY_SUCCESS_BITS \
|
||||
__reg_orm(SDMMC_STAR, BUSYD0END)
|
||||
#define BUSY_ERROR_BITS \
|
||||
__reg_orm(SDMMC_STAR, DTIMEOUT)
|
||||
#define BUSY_END_BITS \
|
||||
(BUSY_SUCCESS_BITS | DATA_ERROR_BITS)
|
||||
|
||||
static size_t get_sdmmc_bus_freq(uint32_t clock)
|
||||
{
|
||||
switch (clock)
|
||||
|
|
@ -262,6 +271,13 @@ int stm32h7_sdmmc_submit_command(void *controller,
|
|||
break;
|
||||
}
|
||||
|
||||
/* For R1b responses we need to wait for the busy signal. */
|
||||
if (cmd->flags & SDMMC_RESP_BUSY)
|
||||
{
|
||||
maskr |= BUSY_END_BITS;
|
||||
cmd_wait |= WAIT_BUSY;
|
||||
}
|
||||
|
||||
/*
|
||||
* Handle commands with data transfers
|
||||
*/
|
||||
|
|
@ -313,9 +329,14 @@ int stm32h7_sdmmc_submit_command(void *controller,
|
|||
{
|
||||
/* Disable data transfer */
|
||||
reg_assignlf(ctl->regs, SDMMC_IDMACTRLR, IDMAEN(0));
|
||||
reg_varl(ctl->regs, SDMMC_DTIMER) = 0;
|
||||
reg_varl(ctl->regs, SDMMC_DLENR) = 0;
|
||||
reg_varl(ctl->regs, SDMMC_DCTRL) = 0;
|
||||
|
||||
/* DTIMER is the wait time for the busy signal */
|
||||
if (cmd->flags & SDMMC_RESP_BUSY)
|
||||
reg_varl(ctl->regs, SDMMC_DTIMER) = 1 * ctl->bus_freq;
|
||||
else
|
||||
reg_varl(ctl->regs, SDMMC_DTIMER) = 0;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -419,7 +440,7 @@ void stm32h7_sdmmc_irq_handler(struct stm32h7_sdmmc_controller *ctl)
|
|||
* to ensure the correct interrupt is used to detect command
|
||||
* completion (CMDSENT or CMDREND).
|
||||
*/
|
||||
star &= maskr;
|
||||
star &= maskr | __reg_orm(SDMMC_STAR, BUSYD0);
|
||||
|
||||
if (ctl->cmd_wait & WAIT_CMD)
|
||||
{
|
||||
|
|
@ -478,6 +499,32 @@ void stm32h7_sdmmc_irq_handler(struct stm32h7_sdmmc_controller *ctl)
|
|||
}
|
||||
}
|
||||
|
||||
if (ctl->cmd_wait & WAIT_BUSY)
|
||||
{
|
||||
if (reg_vreadf(star, SDMMC_STAR, CMDREND) &&
|
||||
!reg_vreadf(star, SDMMC_STAR, BUSYD0))
|
||||
{
|
||||
/*
|
||||
* Skip waiting if the busy signal is not asserted by
|
||||
* the card when the command ends; in this case we'll
|
||||
* never receive a BUSYD0END interrupt because it is
|
||||
* only raised on a transition from busy -> not busy.
|
||||
*/
|
||||
ctl->cmd_wait &= ~WAIT_BUSY;
|
||||
}
|
||||
else if ((star & BUSY_END_BITS) || ctl->cmd_error)
|
||||
{
|
||||
if (!ctl->cmd_error)
|
||||
{
|
||||
if (reg_vreadf(star, SDMMC_STAR, DTIMEOUT))
|
||||
ctl->cmd_error = SDMMC_STATUS_TIMEOUT;
|
||||
}
|
||||
|
||||
ctl->cmd_wait &= ~WAIT_BUSY;
|
||||
icr |= BUSY_END_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Each interrupt should complete at least one phase
|
||||
* but in error cases we can get spurious interrupts.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue