git: b8f94506f2d4 - main - sdhci: Provide devmethod for software reset
- Go to: [ bottom of page ] [ top of archives ] [ this month ]
Date: Fri, 05 Nov 2021 09:19:18 UTC
The branch main has been updated by wma: URL: https://cgit.FreeBSD.org/src/commit/?id=b8f94506f2d4b0ae811f27c244896d044d8780bf commit b8f94506f2d4b0ae811f27c244896d044d8780bf Author: Artur Rojek <ar@semihalf.com> AuthorDate: 2021-11-05 09:14:25 +0000 Commit: Wojciech Macek <wma@FreeBSD.org> CommitDate: 2021-11-05 09:18:57 +0000 sdhci: Provide devmethod for software reset Some sdhci controllers require custom software reset logic. Accommodate this need by introducing a new SDHCI_RESET devmethod. Move the existing reset logic into sdhci_generic_reset and use it as a default for the aforementioned method. Obtained from: Semihalf Sponsored by: Alstom Group Differeential revision: https://reviews.freebsd.org/D32704 --- sys/dev/sdhci/sdhci.c | 157 ++++++++++++++++++++++++----------------------- sys/dev/sdhci/sdhci.h | 1 + sys/dev/sdhci/sdhci_if.m | 6 ++ 3 files changed, 87 insertions(+), 77 deletions(-) diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c index 4a59a73a7e26..22618ca0a822 100644 --- a/sys/dev/sdhci/sdhci.c +++ b/sys/dev/sdhci/sdhci.c @@ -106,7 +106,6 @@ static void sdhci_init(struct sdhci_slot *slot); static void sdhci_read_block_pio(struct sdhci_slot *slot); static void sdhci_req_done(struct sdhci_slot *slot); static void sdhci_req_wakeup(struct mmc_request *req); -static void sdhci_reset(struct sdhci_slot *slot, uint8_t mask); static void sdhci_retune(void *arg); static void sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock); static void sdhci_set_power(struct sdhci_slot *slot, u_char power); @@ -370,66 +369,6 @@ sdhci_syctl_dumpcaps(SYSCTL_HANDLER_ARGS) return (0); } -static void -sdhci_reset(struct sdhci_slot *slot, uint8_t mask) -{ - int timeout; - uint32_t clock; - - if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { - if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot)) - return; - } - - /* Some controllers need this kick or reset won't work. */ - if ((mask & SDHCI_RESET_ALL) == 0 && - (slot->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)) { - /* This is to force an update */ - clock = slot->clock; - slot->clock = 0; - sdhci_set_clock(slot, clock); - } - - 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 = 10000; - /* Controller clears the bits when it's done */ - 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(10); - } -} - static uint32_t sdhci_tuning_intmask(const struct sdhci_slot *slot) { @@ -449,7 +388,7 @@ static void sdhci_init(struct sdhci_slot *slot) { - sdhci_reset(slot, SDHCI_RESET_ALL); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_ALL); /* Enable interrupts. */ slot->intmask = SDHCI_INT_BUS_POWER | SDHCI_INT_DATA_END_BIT | @@ -1256,7 +1195,7 @@ sdhci_cleanup_slot(struct sdhci_slot *slot) device_delete_child(slot->bus, d); SDHCI_LOCK(slot); - sdhci_reset(slot, SDHCI_RESET_ALL); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_ALL); SDHCI_UNLOCK(slot); if (slot->opt & SDHCI_HAVE_DMA) sdhci_dma_free(slot); @@ -1283,7 +1222,7 @@ sdhci_generic_suspend(struct sdhci_slot *slot) callout_drain(&slot->retune_callout); SDHCI_LOCK(slot); slot->opt &= ~SDHCI_TUNING_ENABLED; - sdhci_reset(slot, SDHCI_RESET_ALL); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_ALL); SDHCI_UNLOCK(slot); return (0); @@ -1300,6 +1239,67 @@ sdhci_generic_resume(struct sdhci_slot *slot) return (0); } +void +sdhci_generic_reset(device_t brdev __unused, struct sdhci_slot *slot, + uint8_t mask) +{ + int timeout; + uint32_t clock; + + if (slot->quirks & SDHCI_QUIRK_NO_CARD_NO_RESET) { + if (!SDHCI_GET_CARD_PRESENT(slot->bus, slot)) + return; + } + + /* Some controllers need this kick or reset won't work. */ + if ((mask & SDHCI_RESET_ALL) == 0 && + (slot->quirks & SDHCI_QUIRK_CLOCK_BEFORE_RESET)) { + /* This is to force an update */ + clock = slot->clock; + slot->clock = 0; + sdhci_set_clock(slot, clock); + } + + 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 = 10000; + /* Controller clears the bits when it's done */ + 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(10); + } +} + uint32_t sdhci_generic_min_freq(device_t brdev __unused, struct sdhci_slot *slot) { @@ -1391,7 +1391,8 @@ sdhci_generic_update_ios(device_t brdev, device_t reqdev) SDHCI_SET_UHS_TIMING(brdev, slot); /* Some controllers like reset after bus changes. */ if (slot->quirks & SDHCI_QUIRK_RESET_ON_IOS) - sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + SDHCI_RESET(slot->bus, slot, + SDHCI_RESET_CMD | SDHCI_RESET_DATA); SDHCI_UNLOCK(slot); return (0); @@ -1634,7 +1635,7 @@ sdhci_exec_tuning(struct sdhci_slot *slot, bool reset) slot_printf(slot, "Tuning failed, using fixed sampling clock\n"); WR2(slot, SDHCI_HOST_CONTROL2, hostctrl2 & ~(SDHCI_CTRL2_EXEC_TUNING | SDHCI_CTRL2_SAMPLING_CLOCK)); - sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); return (EIO); } @@ -1703,7 +1704,8 @@ sdhci_timeout(void *arg) if (slot->curcmd != NULL) { slot_printf(slot, "Controller timeout\n"); sdhci_dumpregs(slot); - sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + SDHCI_RESET(slot->bus, slot, + SDHCI_RESET_CMD | SDHCI_RESET_DATA); slot->curcmd->error = MMC_ERR_TIMEOUT; sdhci_req_done(slot); } else { @@ -1881,8 +1883,8 @@ sdhci_finish_command(struct sdhci_slot *slot) if (slot->curcmd->error) { if (slot->curcmd->error == MMC_ERR_BADCRC) slot->retune_req |= SDHCI_RETUNE_REQ_RESET; - sdhci_reset(slot, SDHCI_RESET_CMD); - sdhci_reset(slot, SDHCI_RESET_DATA); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_DATA); sdhci_start(slot); return; } @@ -2041,8 +2043,8 @@ sdhci_finish_data(struct sdhci_slot *slot) if (slot->curcmd->error) { if (slot->curcmd->error == MMC_ERR_BADCRC) slot->retune_req |= SDHCI_RETUNE_REQ_RESET; - sdhci_reset(slot, SDHCI_RESET_CMD); - sdhci_reset(slot, SDHCI_RESET_DATA); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_DATA); sdhci_start(slot); return; } @@ -2084,8 +2086,8 @@ sdhci_start(struct sdhci_slot *slot) slot_printf(slot, "result: %d\n", mmcio->cmd.error); if (mmcio->cmd.error == 0 && (slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST)) { - sdhci_reset(slot, SDHCI_RESET_CMD); - sdhci_reset(slot, SDHCI_RESET_DATA); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_DATA); } sdhci_req_done(slot); @@ -2117,8 +2119,8 @@ sdhci_start(struct sdhci_slot *slot) ((slot->curcmd == req->stop && (slot->quirks & SDHCI_QUIRK_BROKEN_AUTO_STOP)) || (slot->quirks & SDHCI_QUIRK_RESET_AFTER_REQUEST))) { - sdhci_reset(slot, SDHCI_RESET_CMD); - sdhci_reset(slot, SDHCI_RESET_DATA); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_DATA); } sdhci_req_done(slot); @@ -2343,7 +2345,7 @@ sdhci_acmd_irq(struct sdhci_slot *slot, uint16_t acmd_err) return; } slot_printf(slot, "Got AutoCMD12 error 0x%04x\n", acmd_err); - sdhci_reset(slot, SDHCI_RESET_CMD); + SDHCI_RESET(slot->bus, slot, SDHCI_RESET_CMD); } void @@ -2849,7 +2851,8 @@ sdhci_cam_update_ios(struct sdhci_slot *slot) WR1(slot, SDHCI_HOST_CONTROL, slot->hostctrl); /* Some controllers like reset after bus changes. */ if(slot->quirks & SDHCI_QUIRK_RESET_ON_IOS) - sdhci_reset(slot, SDHCI_RESET_CMD | SDHCI_RESET_DATA); + SDHCI_RESET(slot->bus, slot, + SDHCI_RESET_CMD | SDHCI_RESET_DATA); SDHCI_UNLOCK(slot); return (0); diff --git a/sys/dev/sdhci/sdhci.h b/sys/dev/sdhci/sdhci.h index 9d68a14b28a2..4feb272bb359 100644 --- a/sys/dev/sdhci/sdhci.h +++ b/sys/dev/sdhci/sdhci.h @@ -431,6 +431,7 @@ void sdhci_finish_data(struct sdhci_slot *slot); int sdhci_cleanup_slot(struct sdhci_slot *slot); int sdhci_generic_suspend(struct sdhci_slot *slot); int sdhci_generic_resume(struct sdhci_slot *slot); +void sdhci_generic_reset(device_t brdev, struct sdhci_slot *slot, uint8_t mask); int sdhci_generic_update_ios(device_t brdev, device_t reqdev); int sdhci_generic_tune(device_t brdev, device_t reqdev, bool hs400); int sdhci_generic_switch_vccq(device_t brdev, device_t reqdev); diff --git a/sys/dev/sdhci/sdhci_if.m b/sys/dev/sdhci/sdhci_if.m index 93c97a155fb1..c888f35bdaf0 100644 --- a/sys/dev/sdhci/sdhci_if.m +++ b/sys/dev/sdhci/sdhci_if.m @@ -164,3 +164,9 @@ METHOD void set_uhs_timing { device_t brdev; struct sdhci_slot *slot; } DEFAULT null_set_uhs_timing; + +METHOD void reset { + device_t brdev; + struct sdhci_slot *slot; + uint8_t mask; +} DEFAULT sdhci_generic_reset;