mail archive of the barebox mailing list
 help / color / mirror / Atom feed
From: Ahmad Fatoum <a.fatoum@pengutronix.de>
To: barebox@lists.infradead.org
Cc: Ahmad Fatoum <a.fatoum@pengutronix.de>
Subject: [PATCH v2 28/30] mci: imx-esdhc: fixup quirks in standard SDHCI registers
Date: Wed,  7 May 2025 10:22:07 +0200	[thread overview]
Message-ID: <20250507082209.3289972-29-a.fatoum@pengutronix.de> (raw)
In-Reply-To: <20250507082209.3289972-1-a.fatoum@pengutronix.de>

The eSDHC implementation differs from the spec sufficiently to break the
generic SDHCI tuning support. Linux works around this by intercepting
some particular register reads and faking the content.

Let's begrudgingly do the same for barebox.

Mildly interesting statistic: As of Linux v6.12, the generic SDHCI driver
defines 51 quirk bits and 32 callbacks to handle differences.

Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de>
---
 drivers/mci/imx-esdhc-common.c | 229 +++++++++++++++++++++++++++++++++
 drivers/mci/imx-esdhc.h        |  30 +++++
 drivers/mci/sdhci.h            |   1 +
 3 files changed, 260 insertions(+)

diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index 5fc9b7b30c76..caa8505ac00e 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -23,6 +23,10 @@
 #define  ESDHC_MIX_CTRL_FBCLK_SEL	(1 << 25)
 #define  ESDHC_MIX_CTRL_HS400_EN	(1 << 26)
 #define  ESDHC_MIX_CTRL_HS400_ES_EN	(1 << 27)
+/* Bits 3 and 6 are not SDHCI standard definitions */
+#define  ESDHC_MIX_CTRL_SDHCI_MASK	0xb7
+/* Tuning bits */
+#define  ESDHC_MIX_CTRL_TUNING_MASK	0x03c00000
 
 static u32 esdhc_op_read32_be(struct sdhci *sdhci, int reg)
 {
@@ -52,6 +56,224 @@ static void esdhc_op_write16_be(struct sdhci *sdhci, int reg, u16 val)
 	out_be16(host->sdhci.base + reg, val);
 }
 
+static u16 esdhc_op_read16_le_tuning(struct sdhci *sdhci, int reg)
+{
+	struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+	u16 ret = 0;
+	u32 val;
+
+	if (unlikely(reg == SDHCI_HOST_VERSION)) {
+		/*
+		 * The usdhc register returns a wrong host version.
+		 * Correct it here.
+		 */
+		return SDHCI_SPEC_300;
+	}
+
+	if (unlikely(reg == SDHCI_HOST_CONTROL2)) {
+		val = readl(sdhci->base + ESDHC_VENDOR_SPEC);
+		if (val & ESDHC_VENDOR_SPEC_VSELECT)
+			ret |= SDHCI_CTRL_VDD_180;
+
+		if (host->socdata->flags & ESDHC_FLAG_MAN_TUNING)
+			val = readl(sdhci->base + ESDHC_MIX_CTRL);
+		else if (host->socdata->flags & ESDHC_FLAG_STD_TUNING)
+			/* the std tuning bits is in ACMD12_ERR for imx6sl */
+			val = readl(sdhci->base + SDHCI_ACMD12_ERR__HOST_CONTROL2);
+
+		if (val & ESDHC_MIX_CTRL_EXE_TUNE)
+			ret |= SDHCI_CTRL_EXEC_TUNING;
+		if (val & ESDHC_MIX_CTRL_SMPCLK_SEL)
+			ret |= SDHCI_CTRL_TUNED_CLK;
+
+		ret &= ~SDHCI_CTRL_PRESET_VAL_ENABLE;
+
+		return ret;
+	}
+
+	if (unlikely(reg == SDHCI_TRANSFER_MODE)) {
+		u32 m = readl(sdhci->base + ESDHC_MIX_CTRL);
+		ret = m & ESDHC_MIX_CTRL_SDHCI_MASK;
+		/* Swap AC23 bit */
+		if (m & ESDHC_MIX_CTRL_AC23EN) {
+			ret &= ~ESDHC_MIX_CTRL_AC23EN;
+			ret |= SDHCI_TRNS_AUTO_CMD23;
+		}
+
+		return ret;
+	}
+
+	return readw(sdhci->base + reg);
+}
+
+static inline void esdhc_wait_for_card_clock_gate_off(struct sdhci *sdhci)
+{
+	struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+	u32 present_state;
+	int ret;
+
+	ret = readl_poll_timeout(sdhci->base + ESDHC_PRSSTAT, present_state,
+				(present_state & ESDHC_CLOCK_GATE_OFF), 100);
+	if (ret == -ETIMEDOUT)
+		dev_warn(host->dev, "%s: card clock still not gate off in 100us!.\n", __func__);
+}
+
+static inline void esdhc_clrset_le(struct sdhci *sdhci, u32 mask, u32 val, int reg)
+{
+	void __iomem *base = sdhci->base + (reg & ~0x3);
+	u32 shift = (reg & 0x3) * 8;
+
+	writel(((readl(base) & ~(mask << shift)) | (val << shift)), base);
+}
+
+static void esdhc_op_write16_le_tuning(struct sdhci *sdhci, int reg, u16 val)
+{
+	struct fsl_esdhc_host *host = sdhci_to_esdhc(sdhci);
+	u32 new_val = 0;
+
+	switch (reg) {
+	case SDHCI_CLOCK_CONTROL:
+		new_val = readl(sdhci->base + ESDHC_VENDOR_SPEC);
+		if (val & SDHCI_CLOCK_CARD_EN)
+			new_val |= ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
+		else
+			new_val &= ~ESDHC_VENDOR_SPEC_FRC_SDCLK_ON;
+		writel(new_val, sdhci->base + ESDHC_VENDOR_SPEC);
+		if (!(new_val & ESDHC_VENDOR_SPEC_FRC_SDCLK_ON))
+			esdhc_wait_for_card_clock_gate_off(sdhci);
+		return;
+	case SDHCI_HOST_CONTROL2:
+		new_val = readl(sdhci->base + ESDHC_VENDOR_SPEC);
+		if (val & SDHCI_CTRL_VDD_180)
+			new_val |= ESDHC_VENDOR_SPEC_VSELECT;
+		else
+			new_val &= ~ESDHC_VENDOR_SPEC_VSELECT;
+		writel(new_val, sdhci->base + ESDHC_VENDOR_SPEC);
+		if (host->socdata->flags & ESDHC_FLAG_STD_TUNING) {
+			u32 v = readl(sdhci->base + SDHCI_ACMD12_ERR__HOST_CONTROL2);
+			u32 m = readl(sdhci->base + ESDHC_MIX_CTRL);
+			if (val & SDHCI_CTRL_TUNED_CLK) {
+				v |= ESDHC_MIX_CTRL_SMPCLK_SEL;
+			} else {
+				v &= ~ESDHC_MIX_CTRL_SMPCLK_SEL;
+				m &= ~ESDHC_MIX_CTRL_FBCLK_SEL;
+			}
+
+			if (val & SDHCI_CTRL_EXEC_TUNING) {
+				v |= ESDHC_MIX_CTRL_EXE_TUNE;
+				m |= ESDHC_MIX_CTRL_FBCLK_SEL;
+			} else {
+				v &= ~ESDHC_MIX_CTRL_EXE_TUNE;
+			}
+
+			writel(v, sdhci->base + SDHCI_ACMD12_ERR__HOST_CONTROL2);
+			writel(m, sdhci->base + ESDHC_MIX_CTRL);
+		}
+		return;
+	case SDHCI_COMMAND:
+		if (host->last_cmd == MMC_CMD_STOP_TRANSMISSION)
+			val |= SDHCI_COMMAND_CMDTYP_ABORT;
+
+		writel(val << 16,
+		       sdhci->base + SDHCI_TRANSFER_MODE);
+		return;
+	case SDHCI_BLOCK_SIZE:
+		val &= ~SDHCI_MAKE_BLKSZ(0x7, 0);
+		break;
+	}
+	esdhc_clrset_le(sdhci, 0xffff, val, reg);
+}
+
+static u8 esdhc_readb_le(struct sdhci *sdhci, int reg)
+{
+	u8 ret;
+	u8 val;
+
+	switch (reg) {
+	case SDHCI_HOST_CONTROL:
+		val = readl(sdhci->base + reg);
+
+		ret = val & SDHCI_CTRL_LED;
+		ret |= (val >> 5) & SDHCI_CTRL_DMA_MASK;
+		ret |= (val & ESDHC_CTRL_4BITBUS);
+		ret |= (val & ESDHC_CTRL_8BITBUS) << 3;
+		return ret;
+	}
+
+	return readb(sdhci->base + reg);
+}
+
+static void esdhc_writeb_le(struct sdhci *sdhci, int reg, u8 val)
+{
+	u32 new_val = 0;
+	u32 mask;
+
+	switch (reg) {
+	case SDHCI_POWER_CONTROL:
+		/*
+		 * FSL put some DMA bits here
+		 * If your board has a regulator, code should be here
+		 */
+		return;
+	case SDHCI_HOST_CONTROL:
+		/* FSL messed up here, so we need to manually compose it. */
+		new_val = val & SDHCI_CTRL_LED;
+		/* ensure the endianness */
+		new_val |= ESDHC_HOST_CONTROL_LE;
+		/* DMA mode bits are shifted */
+		new_val |= (val & SDHCI_CTRL_DMA_MASK) << 5;
+
+		/*
+		 * Do not touch buswidth bits here. This is done in
+		 * esdhc_pltfm_bus_width.
+		 * Do not touch the D3CD bit either which is used for the
+		 * SDIO interrupt erratum workaround.
+		 */
+		mask = 0xffff & ~(ESDHC_CTRL_BUSWIDTH_MASK | ESDHC_CTRL_D3CD);
+
+		esdhc_clrset_le(sdhci, mask, new_val, reg);
+		return;
+	case SDHCI_SOFTWARE_RESET:
+		if (val & SDHCI_RESET_DATA)
+			new_val = readl(sdhci->base + SDHCI_HOST_CONTROL);
+		break;
+	}
+	esdhc_clrset_le(sdhci, 0xff, val, reg);
+
+	if (reg == SDHCI_SOFTWARE_RESET) {
+		if (val & SDHCI_RESET_ALL) {
+			/*
+			 * The esdhc has a design violation to SDHC spec which
+			 * tells that software reset should not affect card
+			 * detection circuit. But esdhc clears its SYSCTL
+			 * register bits [0..2] during the software reset. This
+			 * will stop those clocks that card detection circuit
+			 * relies on. To work around it, we turn the clocks on
+			 * back to keep card detection circuit functional.
+			 */
+			esdhc_clrset_le(sdhci, 0x7, 0x7, ESDHC_SYSTEM_CONTROL);
+			/*
+			 * The reset on usdhc fails to clear MIX_CTRL register.
+			 * Do it manually here.
+			 */
+			/*
+			 * the tuning bits should be kept during reset
+			 */
+			new_val = readl(sdhci->base + ESDHC_MIX_CTRL);
+			writel(new_val & ESDHC_MIX_CTRL_TUNING_MASK,
+					sdhci->base + ESDHC_MIX_CTRL);
+		} else if (val & SDHCI_RESET_DATA) {
+			/*
+			 * The eSDHC DAT line software reset clears at least the
+			 * data transfer width on i.MX25, so make sure that the
+			 * Host Control register is unaffected.
+			 */
+			esdhc_clrset_le(sdhci, 0xff, new_val,
+					SDHCI_HOST_CONTROL);
+		}
+	}
+}
+
 void esdhc_populate_sdhci(struct fsl_esdhc_host *host)
 {
 	if (host->socdata->flags & ESDHC_FLAG_BIGENDIAN) {
@@ -59,6 +281,11 @@ void esdhc_populate_sdhci(struct fsl_esdhc_host *host)
 		host->sdhci.write16 = esdhc_op_write16_be;
 		host->sdhci.read32 = esdhc_op_read32_be;
 		host->sdhci.write32 = esdhc_op_write32_be;
+	} else if (esdhc_is_usdhc(host)){
+		host->sdhci.read8 = esdhc_readb_le;
+		host->sdhci.write8 = esdhc_writeb_le;
+		host->sdhci.read16 = esdhc_op_read16_le_tuning;
+		host->sdhci.write16 = esdhc_op_write16_le_tuning;
 	}
 }
 
@@ -100,6 +327,8 @@ int __esdhc_send_cmd(struct fsl_esdhc_host *host, struct mci_cmd *cmd,
 	dma_addr_t dma = SDHCI_NO_DMA;
 	int ret;
 
+	host->last_cmd = cmd ? cmd->cmdidx : 0;
+
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, -1);
 
 	/* Wait at least 8 SD clock cycles before the next command */
diff --git a/drivers/mci/imx-esdhc.h b/drivers/mci/imx-esdhc.h
index e24d76d0c687..df8aca18ad32 100644
--- a/drivers/mci/imx-esdhc.h
+++ b/drivers/mci/imx-esdhc.h
@@ -66,6 +66,36 @@
 #define  IMX_SDHCI_DLL_CTRL_OVERRIDE_EN_SHIFT	8
 #define IMX_SDHCI_MIX_CTRL_FBCLK_SEL	BIT(25)
 
+/* pltfm-specific */
+#define ESDHC_HOST_CONTROL_LE	0x20
+
+/*
+ * eSDHC register definition
+ */
+
+/* Present State Register */
+#define ESDHC_PRSSTAT			0x24
+#define ESDHC_CLOCK_GATE_OFF		0x00000080
+#define ESDHC_CLOCK_STABLE		0x00000008
+
+/* Protocol Control Register */
+#define ESDHC_PROCTL			0x28
+#define ESDHC_VOLT_SEL			0x00000400
+#define ESDHC_CTRL_4BITBUS		(0x1 << 1)
+#define ESDHC_CTRL_8BITBUS		(0x2 << 1)
+#define ESDHC_CTRL_BUSWIDTH_MASK	(0x3 << 1)
+#define ESDHC_HOST_CONTROL_RES		0x01
+
+/* System Control Register */
+#define ESDHC_SYSTEM_CONTROL		0x2c
+#define ESDHC_CLOCK_MASK		0x0000fff0
+#define ESDHC_PREDIV_SHIFT		8
+#define ESDHC_DIVIDER_SHIFT		4
+#define ESDHC_CLOCK_SDCLKEN		0x00000008
+#define ESDHC_CLOCK_PEREN		0x00000004
+#define ESDHC_CLOCK_HCKEN		0x00000002
+#define ESDHC_CLOCK_IPGEN		0x00000001
+
 /* tune control register */
 #define ESDHC_TUNE_CTRL_STATUS		0x68
 #define  ESDHC_TUNE_CTRL_STEP		1
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index e0436425fbda..038b52cfe619 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -143,6 +143,7 @@
 #define  SDHCI_CTRL_TUNED_CLK			BIT(7)
 #define  SDHCI_CTRL_64BIT_ADDR			BIT(13)
 #define  SDHCI_CTRL_V4_MODE			BIT(12)
+#define  SDHCI_CTRL_PRESET_VAL_ENABLE		BIT(15)
 #define SDHCI_CAPABILITIES					0x40
 #define  SDHCI_TIMEOUT_CLK_MASK			GENMASK(5, 0)
 #define  SDHCI_TIMEOUT_CLK_UNIT			0x00000080
-- 
2.39.5




  parent reply	other threads:[~2025-05-07  9:33 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-05-07  8:21 [PATCH v2 00/30] mci: imx-esdhc: add HS200 support Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 01/30] mci: sdhci: fix SDHCI_TRNS_AUTO_CMD12 definition Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 02/30] mci: move most recent I/O settings into mci_host::ios Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 03/30] mci: use struct mci_host::ios inside mci_set_ios Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 04/30] mci: tuning: fix fallback to DDR52 Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 05/30] mci: sdhci: unmap DMA buffers on timeout Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 06/30] mci: add MMC_CAP_UHS constants Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 07/30] mci: rename MMC_CAP_MMC_x_yV_DDR to MMC_CAP_x_yV_DDR as in Linux Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 08/30] mci: compare host and card caps for supported speeds Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 09/30] mci: print HS200 capabilities in devinfo Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 10/30] mci: respect no-1-8-v OF property Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 11/30] mci: sdhci: add support for struct mci_data::timeout_ns Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 12/30] mci: imx-esdhc: use unsigned types where appropriate Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 13/30] mci: imx-esdhc: implement esdhc_poll using sdhci_read32_poll_timeout Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 14/30] mci: imx-esdhc: drop one extra read of SDHCI_INT_STATUS Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 15/30] mci: sdhci: add cmd parameter to sdhci_transfer_* Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 16/30] mci: arasan: introduce mmc_op_tuning helper Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 17/30] mci: imx-esdhc: flesh out register description Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 18/30] mci: imx-esdhc: add support for delay/tuning properties in DT Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 19/30] mci: add mci_set_timing helper Ahmad Fatoum
2025-05-07  8:21 ` [PATCH v2 20/30] mci: imx-esdhc: add support for setting drive strength Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 21/30] mci: sdhci: move SDHCI_MAKE_BLKSZ definition to header Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 22/30] mci: imx-esdhc: select different pinctrl state depending on frequency Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 23/30] mci: core: retry MMC_CMD_SET_BLOCKLEN up to 4 times Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 24/30] mci: imx-esdhc: don't reconfigure clock unless required Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 25/30] mci: sdhci: fix sdhci_transfer_data MMC_SEND_TUNING compatibility Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 26/30] mci: core: implement mmc_send_tuning Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 27/30] mci: imx-esdhc: set burst_length_enable Ahmad Fatoum
2025-05-07  8:22 ` Ahmad Fatoum [this message]
2025-05-07  8:22 ` [PATCH v2 29/30] mci: sdhci: support Linux SDHCI_QUIRK2_BROKEN_HS200 flag Ahmad Fatoum
2025-05-07  8:22 ` [PATCH v2 30/30] mci: imx-esdhc: implement HS200 support Ahmad Fatoum
2025-05-08  7:30 ` [PATCH v2 00/30] mci: imx-esdhc: add " Sascha Hauer

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20250507082209.3289972-29-a.fatoum@pengutronix.de \
    --to=a.fatoum@pengutronix.de \
    --cc=barebox@lists.infradead.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox