mirror of
https://github.com/Rockbox/rockbox.git
synced 2026-05-12 11:43:16 -04:00
Compare commits
2 commits
7fdba68524
...
55183c0b2a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
55183c0b2a | ||
|
|
65bf8d4192 |
1 changed files with 37 additions and 11 deletions
|
|
@ -74,6 +74,28 @@ static size_t abs_diff(size_t a, size_t b)
|
|||
return a > b ? a - b : b - a;
|
||||
}
|
||||
|
||||
/*
|
||||
* The CLKCR and CMDR registers must not be written twice
|
||||
* within 7 AHB clock cycles. If this is not respected the
|
||||
* hardware seems to misbehave, for example updating the
|
||||
* clock divider in CLKCR can fail (the register field is
|
||||
* updated, but the actual clock output is not).
|
||||
*
|
||||
* Reading a peripheral register should require at least
|
||||
* two cycles on the AHB bus (1 address + 1 data cycle),
|
||||
* assuming they are single transfers and no pipelining
|
||||
* can take place. Doing four reads of an SDMMC register
|
||||
* should therefore be enough to add the correct number
|
||||
* of wait states.
|
||||
*/
|
||||
static void stm32h7_sdmmc_regwrite_delay(struct stm32h7_sdmmc_controller *ctl)
|
||||
{
|
||||
reg_readl(ctl->regs, SDMMC_STAR);
|
||||
reg_readl(ctl->regs, SDMMC_STAR);
|
||||
reg_readl(ctl->regs, SDMMC_STAR);
|
||||
reg_readl(ctl->regs, SDMMC_STAR);
|
||||
}
|
||||
|
||||
static bool stm32h7_sdmmc_is_powered_off(struct stm32h7_sdmmc_controller *ctl)
|
||||
{
|
||||
uint32_t pwrctrl = reg_readlf(ctl->regs, SDMMC_POWER, PWRCTRL);
|
||||
|
|
@ -131,6 +153,7 @@ void stm32h7_sdmmc_set_power_enabled(void *controller, bool enabled)
|
|||
|
||||
/* Automatically stop clock when bus is not in use */
|
||||
reg_writelf(ctl->regs, SDMMC_CLKCR, PWRSAV(1), HWFC_EN(1));
|
||||
stm32h7_sdmmc_regwrite_delay(ctl);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -173,6 +196,8 @@ void stm32h7_sdmmc_set_bus_width(void *controller, uint32_t width)
|
|||
reg_writelf(ctl->regs, SDMMC_CLKCR, WIDBUS_V(8BIT));
|
||||
else
|
||||
panicf("%s", __func__);
|
||||
|
||||
stm32h7_sdmmc_regwrite_delay(ctl);
|
||||
}
|
||||
|
||||
void stm32h7_sdmmc_set_bus_clock(void *controller, uint32_t clock)
|
||||
|
|
@ -222,6 +247,8 @@ void stm32h7_sdmmc_set_bus_clock(void *controller, uint32_t clock)
|
|||
DDR(0),
|
||||
NEGEDGE(0),
|
||||
CLKDIV(div[idx] / 2));
|
||||
|
||||
stm32h7_sdmmc_regwrite_delay(ctl);
|
||||
}
|
||||
|
||||
int stm32h7_sdmmc_submit_command(void *controller,
|
||||
|
|
@ -233,6 +260,7 @@ int stm32h7_sdmmc_submit_command(void *controller,
|
|||
uint32_t maskr = CMD_ERROR_BITS;
|
||||
uint32_t cmdr = __reg_orf(SDMMC_CMDR, CPSMEN(1), CMDINDEX(cmd->command));
|
||||
uint32_t cmd_wait = WAIT_CMD;
|
||||
uint32_t dctrl = 0, dtimer = 0, dlenr = 0;
|
||||
|
||||
void *buff_addr = cmd->buffer;
|
||||
size_t buff_size = cmd->nr_blocks * cmd->block_len;
|
||||
|
|
@ -290,7 +318,6 @@ int stm32h7_sdmmc_submit_command(void *controller,
|
|||
panicf("%s: buffer too big", __func__);
|
||||
|
||||
/* Set block size */
|
||||
uint32_t dctrl = 0;
|
||||
uint32_t dblocksize = find_first_set_bit(cmd->block_len);
|
||||
if (dblocksize > 14 || (cmd->block_len & (cmd->block_len - 1)))
|
||||
panicf("%s: incorrect block size", __func__);
|
||||
|
|
@ -314,11 +341,8 @@ int stm32h7_sdmmc_submit_command(void *controller,
|
|||
reg_assignlf(ctl->regs, SDMMC_IDMACTRLR, IDMAEN(1));
|
||||
|
||||
/* Use a 10 second timeout (DTIMER is in units of bus clocks) */
|
||||
reg_varl(ctl->regs, SDMMC_DTIMER) = 10 * ctl->bus_freq;
|
||||
reg_varl(ctl->regs, SDMMC_DLENR) = buff_size;
|
||||
|
||||
/* DCTRL must be written last */
|
||||
reg_varl(ctl->regs, SDMMC_DCTRL) = dctrl;
|
||||
dtimer = 10 * ctl->bus_freq;
|
||||
dlenr = buff_size;
|
||||
|
||||
/* Enable data phase */
|
||||
reg_vwritef(cmdr, SDMMC_CMDR, CMDTRANS(1));
|
||||
|
|
@ -329,16 +353,17 @@ int stm32h7_sdmmc_submit_command(void *controller,
|
|||
{
|
||||
/* Disable data transfer */
|
||||
reg_assignlf(ctl->regs, SDMMC_IDMACTRLR, IDMAEN(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;
|
||||
dtimer = 1 * ctl->bus_freq;
|
||||
}
|
||||
|
||||
/* Set data transfer registers */
|
||||
reg_varl(ctl->regs, SDMMC_DLENR) = dlenr;
|
||||
reg_varl(ctl->regs, SDMMC_DTIMER) = dtimer;
|
||||
reg_varl(ctl->regs, SDMMC_DCTRL) = dctrl;
|
||||
|
||||
/*
|
||||
* Set CMDSTOP bit for CMD12 (stop transmission) command;
|
||||
* this is needed to stop the DPSM in case of an error in
|
||||
|
|
@ -360,6 +385,7 @@ int stm32h7_sdmmc_submit_command(void *controller,
|
|||
reg_varl(ctl->regs, SDMMC_MASKR) = maskr;
|
||||
reg_varl(ctl->regs, SDMMC_ARGR) = cmd->argument;
|
||||
reg_varl(ctl->regs, SDMMC_CMDR) = cmdr;
|
||||
stm32h7_sdmmc_regwrite_delay(ctl);
|
||||
|
||||
/* Wait for command completion */
|
||||
semaphore_wait(&ctl->sem, TIMEOUT_BLOCK);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue