git: b8f94506f2d4 - main - sdhci: Provide devmethod for software reset

From: Wojciech Macek <wma_at_FreeBSD.org>
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;