git: ec9333c3aac3 - main - sdhci_fsl_fdt: Fix vccq reconfiguration

From: Wojciech Macek <wma_at_FreeBSD.org>
Date: Wed, 08 Dec 2021 10:21:33 UTC
The branch main has been updated by wma:

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

commit ec9333c3aac300d15009df9f24fe5cf37852578b
Author:     Artur Rojek <ar@semihalf.com>
AuthorDate: 2021-09-10 16:03:19 +0000
Commit:     Wojciech Macek <wma@FreeBSD.org>
CommitDate: 2021-12-08 10:21:02 +0000

    sdhci_fsl_fdt: Fix vccq reconfiguration
    
    As `vqmmc-supply` is an optional Device Tree property, don't skip vccq
    reconfiguration when the regulator is not specified. Instead, accept
    voltage specified by the `voltage-ranges` property.
    
    The actual voltage switch is done through a hw register in LS1028A.
    Add errata flag for other boards, as they are not supported. Return
    not supported error code when switching to 1.8V on affected platforms.
    
    Fixes: b08bf4c35ca ("sdhci_fsl_fdt: Skip vccq reconfiguration without regulator")
    
    Co-authored-by: Artur Rojek <ar@semihalf.com>
    
    Reviewed by:            manu, mw
    Obtained from:          Semihalf
    Sponsored by:           Alstom Group
    Differential revision:  https://reviews.freebsd.org/D33319
---
 sys/dev/sdhci/sdhci_fsl_fdt.c | 44 +++++++++++++++++++++++++++++--------------
 1 file changed, 30 insertions(+), 14 deletions(-)

diff --git a/sys/dev/sdhci/sdhci_fsl_fdt.c b/sys/dev/sdhci/sdhci_fsl_fdt.c
index 2d6c7115ff1e..d17811634353 100644
--- a/sys/dev/sdhci/sdhci_fsl_fdt.c
+++ b/sys/dev/sdhci/sdhci_fsl_fdt.c
@@ -70,6 +70,7 @@ __FBSDID("$FreeBSD$");
 #define	SDHCI_FSL_PROT_CTRL_BYTE_NATIVE	(2 << 4)
 #define	SDHCI_FSL_PROT_CTRL_BYTE_MASK	(3 << 4)
 #define	SDHCI_FSL_PROT_CTRL_DMA_MASK	(3 << 8)
+#define	SDHCI_FSL_PROT_CTRL_VOLT_SEL	(1 << 10)
 
 #define	SDHCI_FSL_SYS_CTRL		0x2c
 #define	SDHCI_FSL_CLK_IPGEN		(1 << 0)
@@ -102,6 +103,8 @@ __FBSDID("$FreeBSD$");
 
 /* Some platforms do not detect pulse width correctly. */
 #define SDHCI_FSL_UNRELIABLE_PULSE_DET	(1 << 0)
+/* On some platforms switching voltage to 1.8V is not supported */
+#define SDHCI_FSL_UNSUPP_1_8V		(1 << 1)
 
 struct sdhci_fsl_fdt_softc {
 	device_t				dev;
@@ -132,7 +135,7 @@ struct sdhci_fsl_fdt_soc_data {
 static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1012a_soc_data = {
 	.quirks = 0,
 	.baseclk_div = 1,
-	.errata = 0
+	.errata = SDHCI_FSL_UNSUPP_1_8V,
 };
 
 static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1028a_soc_data = {
@@ -145,6 +148,7 @@ 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,
 };
 
 static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_gen_data = {
@@ -517,34 +521,46 @@ sdhci_fsl_fdt_switch_vccq(device_t brdev, device_t reqdev)
 {
 	struct sdhci_fsl_fdt_softc *sc;
 	struct sdhci_slot *slot;
+	regulator_t vqmmc_supply;
+	uint32_t val_old, val;
 	int uvolt, err;
 
 	sc = device_get_softc(brdev);
+	slot = device_get_ivars(reqdev);
 
-	if (sc->fdt_helper.vqmmc_supply == NULL)
-		return EOPNOTSUPP;
-
-	err = sdhci_generic_switch_vccq(brdev, reqdev);
-	if (err != 0)
-		return (err);
+	val_old = val = RD4(sc, SDHCI_FSL_PROT_CTRL);
 
-	slot = device_get_ivars(reqdev);
 	switch (slot->host.ios.vccq) {
 	case vccq_180:
+		if (sc->soc_data->errata & SDHCI_FSL_UNSUPP_1_8V)
+			return (EOPNOTSUPP);
+
+		val |= SDHCI_FSL_PROT_CTRL_VOLT_SEL;
 		uvolt = 1800000;
 		break;
 	case vccq_330:
+		val &= ~SDHCI_FSL_PROT_CTRL_VOLT_SEL;
 		uvolt = 3300000;
 		break;
 	default:
-		return EINVAL;
+		return (EOPNOTSUPP);
 	}
 
-	err = regulator_set_voltage(sc->fdt_helper.vqmmc_supply, uvolt, uvolt);
-	if (err != 0) {
-		device_printf(sc->dev,
-		    "Cannot set vqmmc to %d<->%d\n", uvolt, uvolt);
-		return (err);
+	WR4(sc, SDHCI_FSL_PROT_CTRL, val);
+
+	vqmmc_supply = sc->fdt_helper.vqmmc_supply;
+	/*
+	 * Even though we expect to find a fixed regulator in this controller
+	 * family, let's play safe.
+	 */
+	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);
+		}
 	}
 
 	return (0);