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;