git: f97e7d6e9d3e - main - sdhci_fsl_fdt: Add voltage switching through syscon

From: Wojciech Macek <wma_at_FreeBSD.org>
Date: Tue, 22 Feb 2022 08:58:51 UTC
The branch main has been updated by wma:

URL: https://cgit.FreeBSD.org/src/commit/?id=f97e7d6e9d3ee603a571892053e174bf9ddf4d0b

commit f97e7d6e9d3ee603a571892053e174bf9ddf4d0b
Author:     Hubert Mazur <hum@semihalf.com>
AuthorDate: 2022-01-20 09:56:10 +0000
Commit:     Wojciech Macek <wma@FreeBSD.org>
CommitDate: 2022-02-22 08:58:38 +0000

    sdhci_fsl_fdt: Add voltage switching through syscon
    
    Some SoCs does not have a fixed regulator to handle voltage
    switching automatically. Add support for voltage switching
    through syscon register when necessary. Add new errata flag
    indicating missing regulator. Apply errata to SoCs, which are
    known to be affected, i.e. LS1046 and LS1012.
    
    Obtained from: Semihalf
    Sponsored by: Alstom Group
    Differential revision: https://reviews.freebsd.org/D34029
---
 sys/dev/sdhci/sdhci_fsl_fdt.c | 99 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 90 insertions(+), 9 deletions(-)

diff --git a/sys/dev/sdhci/sdhci_fsl_fdt.c b/sys/dev/sdhci/sdhci_fsl_fdt.c
index 34e7e6fd449f..31ff858c7a14 100644
--- a/sys/dev/sdhci/sdhci_fsl_fdt.c
+++ b/sys/dev/sdhci/sdhci_fsl_fdt.c
@@ -43,6 +43,7 @@ __FBSDID("$FreeBSD$");
 #include <machine/resource.h>
 
 #include <dev/extres/clk/clk.h>
+#include <dev/extres/syscon/syscon.h>
 #include <dev/mmc/bridge.h>
 #include <dev/mmc/mmcbrvar.h>
 #include <dev/mmc/mmc_fdt_helpers.h>
@@ -53,6 +54,7 @@ __FBSDID("$FreeBSD$");
 
 #include "mmcbr_if.h"
 #include "sdhci_if.h"
+#include "syscon_if.h"
 
 #define	RD4	(sc->read)
 #define	WR4	(sc->write)
@@ -142,6 +144,13 @@ __FBSDID("$FreeBSD$");
 #define SDHCI_FSL_ESDHC_CTRL_FAF	(1 << 18)
 #define	SDHCI_FSL_ESDHC_CTRL_CLK_DIV2	(1 << 19)
 
+#define SCFG_SDHCIOVSELCR		0x408
+#define SCFG_SDHCIOVSELCR_TGLEN		(1 << 0)
+#define SCFG_SDHCIOVSELCR_VS		(1 << 31)
+#define SCFG_SDHCIOVSELCR_VSELVAL_MASK	(3 << 1)
+#define SCFG_SDHCIOVSELCR_VSELVAL_1_8	0x0
+#define SCFG_SDHCIOVSELCR_VSELVAL_3_3	0x2
+
 #define SDHCI_FSL_CAN_VDD_MASK		\
     (SDHCI_CAN_VDD_180 | SDHCI_CAN_VDD_300 | SDHCI_CAN_VDD_330)
 
@@ -162,6 +171,12 @@ __FBSDID("$FreeBSD$");
  */
 #define SDHCI_FSL_HS400_LIMITED_CLK_DIV	(1 << 4)
 
+/*
+ * Some SoCs don't have a fixed regulator. Switching voltage
+ * requires special routine including syscon registers.
+ */
+#define SDHCI_FSL_MISSING_VCCQ_REG	(1 << 5)
+
 /*
  * HS400 tuning is done in HS200 mode, but it has to be done using
  * the target frequency. In order to apply the errata above we need to
@@ -197,12 +212,14 @@ struct sdhci_fsl_fdt_soc_data {
 	int quirks;
 	int baseclk_div;
 	uint8_t errata;
+	char *syscon_compat;
 };
 
 static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1012a_soc_data = {
 	.quirks = 0,
 	.baseclk_div = 1,
-	.errata = SDHCI_FSL_UNSUPP_1_8V,
+	.errata = SDHCI_FSL_MISSING_VCCQ_REG | SDHCI_FSL_TUNING_ERRATUM_TYPE2,
+	.syscon_compat = "fsl,ls1012a-scfg",
 };
 
 static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1028a_soc_data = {
@@ -216,7 +233,8 @@ static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1028a_soc_data = {
 static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1046a_soc_data = {
 	.quirks = SDHCI_QUIRK_DONT_SET_HISPD_BIT | SDHCI_QUIRK_BROKEN_AUTO_STOP,
 	.baseclk_div = 2,
-	.errata = SDHCI_FSL_UNSUPP_1_8V | SDHCI_FSL_TUNING_ERRATUM_TYPE2,
+	.errata = SDHCI_FSL_MISSING_VCCQ_REG | SDHCI_FSL_TUNING_ERRATUM_TYPE2,
+	.syscon_compat = "fsl,ls1046a-scfg",
 };
 
 static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_lx2160a_soc_data = {
@@ -621,6 +639,60 @@ sdhci_fsl_fdt_update_ios(device_t brdev, device_t reqdev)
 	return (0);
 }
 
+static int
+sdhci_fsl_fdt_switch_syscon_voltage(device_t dev,
+    struct sdhci_fsl_fdt_softc *sc, enum mmc_vccq vccq)
+{
+	struct syscon *syscon;
+	phandle_t syscon_node;
+	uint32_t reg;
+
+	if (sc->soc_data->syscon_compat == NULL) {
+		device_printf(dev, "Empty syscon compat string.\n");
+		return (ENXIO);
+	}
+
+	syscon_node = ofw_bus_find_compatible(OF_finddevice("/"),
+	    sc->soc_data->syscon_compat);
+
+	if (syscon_get_by_ofw_node(dev, syscon_node, &syscon) != 0) {
+		device_printf(dev, "Could not find syscon node.\n");
+		return (ENXIO);
+	}
+
+	reg = SYSCON_READ_4(syscon, SCFG_SDHCIOVSELCR);
+	reg &= ~SCFG_SDHCIOVSELCR_VSELVAL_MASK;
+	reg |= SCFG_SDHCIOVSELCR_TGLEN;
+
+	switch (vccq) {
+	case vccq_180:
+		reg |= SCFG_SDHCIOVSELCR_VSELVAL_1_8;
+		SYSCON_WRITE_4(syscon, SCFG_SDHCIOVSELCR, reg);
+
+		DELAY(5000);
+
+		reg = SYSCON_READ_4(syscon, SCFG_SDHCIOVSELCR);
+		reg |= SCFG_SDHCIOVSELCR_VS;
+		break;
+	case vccq_330:
+		reg |= SCFG_SDHCIOVSELCR_VSELVAL_3_3;
+		SYSCON_WRITE_4(syscon, SCFG_SDHCIOVSELCR, reg);
+
+		DELAY(5000);
+
+		reg = SYSCON_READ_4(syscon, SCFG_SDHCIOVSELCR);
+		reg &= ~SCFG_SDHCIOVSELCR_VS;
+		break;
+	default:
+		device_printf(dev, "Unsupported voltage requested.\n");
+		return (ENXIO);
+	}
+
+	SYSCON_WRITE_4(syscon, SCFG_SDHCIOVSELCR, reg);
+
+	return (0);
+}
+
 static int
 sdhci_fsl_fdt_switch_vccq(device_t brdev, device_t reqdev)
 {
@@ -628,7 +700,7 @@ sdhci_fsl_fdt_switch_vccq(device_t brdev, device_t reqdev)
 	struct sdhci_slot *slot;
 	regulator_t vqmmc_supply;
 	uint32_t val_old, val;
-	int uvolt, err;
+	int uvolt, err = 0;
 
 	sc = device_get_softc(brdev);
 	slot = device_get_ivars(reqdev);
@@ -653,6 +725,13 @@ sdhci_fsl_fdt_switch_vccq(device_t brdev, device_t reqdev)
 
 	WR4(sc, SDHCI_FSL_PROT_CTRL, val);
 
+	if (sc->soc_data->errata & SDHCI_FSL_MISSING_VCCQ_REG) {
+		err = sdhci_fsl_fdt_switch_syscon_voltage(brdev, sc,
+		    slot->host.ios.vccq);
+		if (err != 0)
+			goto vccq_fail;
+	}
+
 	vqmmc_supply = sc->fdt_helper.vqmmc_supply;
 	/*
 	 * Even though we expect to find a fixed regulator in this controller
@@ -660,15 +739,17 @@ sdhci_fsl_fdt_switch_vccq(device_t brdev, device_t reqdev)
 	 */
 	if (vqmmc_supply != NULL) {
 		err = regulator_set_voltage(vqmmc_supply, uvolt, uvolt);
-		if (err != 0) {
-			device_printf(sc->dev,
-			    "Cannot set vqmmc to %d<->%d\n", uvolt, uvolt);
-			WR4(sc, SDHCI_FSL_PROT_CTRL, val_old);
-			return (err);
-		}
+		if (err != 0)
+			goto vccq_fail;
 	}
 
 	return (0);
+
+vccq_fail:
+	device_printf(sc->dev, "Cannot set vqmmc to %d<->%d\n", uvolt, uvolt);
+	WR4(sc, SDHCI_FSL_PROT_CTRL, val_old);
+
+	return (err);
 }
 
 static int