Add a new sdhci quirk, SDHCI_QUIRK_WAITFOR_RESET_ASSERTED, to work around

TI OMAP controllers which will return the reset-in-progress bit as zero if
you read the status register too fast after setting the reset bit.

The zero is apparently from a stale snapshot of the internal state presented
in the interface register, and leads to a false indication that the reset
is complete when it either hasn't started yet or is in-progress.  The
workaround is to first loop until the bit is seen as asserted, then do the
normal loop waiting to see it de-asserted.

Submitted by:	Michal Meloun <meloun@miracle.cz>
This commit is contained in:
Ian Lepore 2014-12-20 01:13:13 +00:00
parent 7e5866432f
commit 61bc42f782
3 changed files with 54 additions and 12 deletions

View File

@ -413,9 +413,27 @@ ti_sdhci_hw_init(device_t dev)
DELAY(100);
}
/* Reset both the command and data state machines */
/*
* Reset the command and data state machines and also other aspects of
* the controller such as bus clock and power.
*
* If we read the software reset register too fast after writing it we
* can get back a zero that means the reset hasn't started yet rather
* than that the reset is complete. Per TI recommendations, work around
* it by reading until we see the reset bit asserted, then read until
* it's clear. We also set the SDHCI_QUIRK_WAITFOR_RESET_ASSERTED quirk
* so that the main sdhci driver uses this same logic in its resets.
*/
ti_sdhci_write_1(dev, NULL, SDHCI_SOFTWARE_RESET, SDHCI_RESET_ALL);
timeout = 1000;
timeout = 10000;
while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) &
SDHCI_RESET_ALL) != SDHCI_RESET_ALL) {
if (--timeout == 0) {
break;
}
DELAY(1);
}
timeout = 10000;
while ((ti_sdhci_read_1(dev, NULL, SDHCI_SOFTWARE_RESET) &
SDHCI_RESET_ALL)) {
if (--timeout == 0) {
@ -582,6 +600,12 @@ ti_sdhci_attach(device_t dev)
*/
sc->slot.quirks |= SDHCI_QUIRK_DONT_SHIFT_RESPONSE;
/*
* Reset bits are broken, have to wait to see the bits asserted
* before waiting to see them de-asserted.
*/
sc->slot.quirks |= SDHCI_QUIRK_WAITFOR_RESET_ASSERTED;
/*
* DMA is not really broken, I just haven't implemented it yet.
*/

View File

@ -149,7 +149,6 @@ static void
sdhci_reset(struct sdhci_slot *slot, uint8_t mask)
{
int timeout;
uint8_t res;
if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) {
if (!(RD4(slot, SDHCI_PRESENT_STATE) &
@ -168,26 +167,43 @@ sdhci_reset(struct sdhci_slot *slot, uint8_t mask)
sdhci_set_clock(slot, clock);
}
WR1(slot, SDHCI_SOFTWARE_RESET, mask);
if (mask & SDHCI_RESET_ALL) {
slot->clock = 0;
slot->power = 0;
}
WR1(slot, SDHCI_SOFTWARE_RESET, mask);
if (slot->quirks & SDHCI_QUIRK_WAITFOR_RESET_ASSERTED) {
/*
* Resets on TI OMAPs and AM335x are incompatible with SDHCI
* specification. The reset bit has internal propagation delay,
* so a fast read after write returns 0 even if reset process is
* in progress. The workaround is to poll for 1 before polling
* for 0. In the worst case, if we miss seeing it asserted the
* time we spent waiting is enough to ensure the reset finishes.
*/
timeout = 10000;
while ((RD1(slot, SDHCI_SOFTWARE_RESET) & mask) != mask) {
if (timeout <= 0)
break;
timeout--;
DELAY(1);
}
}
/* Wait max 100 ms */
timeout = 100;
timeout = 10000;
/* Controller clears the bits when it's done */
while ((res = RD1(slot, SDHCI_SOFTWARE_RESET)) & mask) {
if (timeout == 0) {
slot_printf(slot,
"Reset 0x%x never completed - 0x%x.\n",
(int)mask, (int)res);
while (RD1(slot, SDHCI_SOFTWARE_RESET) & mask) {
if (timeout <= 0) {
slot_printf(slot, "Reset 0x%x never completed.\n",
mask);
sdhci_dumpregs(slot);
return;
}
timeout--;
DELAY(1000);
DELAY(10);
}
}

View File

@ -59,6 +59,8 @@
#define SDHCI_QUIRK_MISSING_CAPS (1<<12)
/* Hardware shifts the 136-bit response, don't do it in software. */
#define SDHCI_QUIRK_DONT_SHIFT_RESPONSE (1<<13)
/* Wait to see reset bit asserted before waiting for de-asserted */
#define SDHCI_QUIRK_WAITFOR_RESET_ASSERTED (1<<14)
/*
* Controller registers