aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcin Wojtas <mw@FreeBSD.org>2021-04-22 11:01:06 +0000
committerMarcin Wojtas <mw@FreeBSD.org>2021-05-14 08:34:37 +0000
commit1f84b3a247bf4c0e584e36576a20dc120def1214 (patch)
tree4c09dccda625510c917cd203b371722a8f322a68
parentf0a9d7d7995641901e3132242d1d9bf22dc333f6 (diff)
downloadsrc-1f84b3a247bf4c0e584e36576a20dc120def1214.tar.gz
src-1f84b3a247bf4c0e584e36576a20dc120def1214.zip
sdhci_fsl_fdt.c: Read supported voltages from dts.
We shouldn't overwrite capability register. Instead, voltages supported by the controller have to be read from dts, as the hardware doesn't report correct values. Submitted by: Lukasz Hajec <lha@semihalf.com> Reviewed by: manu Obtained from: Semihalf Sponsored by: Alstom Group Differential Revision: https://reviews.freebsd.org/D30123
-rw-r--r--sys/dev/sdhci/sdhci_fsl_fdt.c73
1 files changed, 65 insertions, 8 deletions
diff --git a/sys/dev/sdhci/sdhci_fsl_fdt.c b/sys/dev/sdhci/sdhci_fsl_fdt.c
index c2330a679963..26092f662406 100644
--- a/sys/dev/sdhci/sdhci_fsl_fdt.c
+++ b/sys/dev/sdhci/sdhci_fsl_fdt.c
@@ -90,6 +90,9 @@ __FBSDID("$FreeBSD$");
#define SDHCI_FSL_ESDHC_CTRL_SNOOP (1 << 6)
#define SDHCI_FSL_ESDHC_CTRL_CLK_DIV2 (1 << 19)
+#define SDHCI_FSL_CAN_VDD_MASK \
+ (SDHCI_CAN_VDD_180 | SDHCI_CAN_VDD_300 | SDHCI_CAN_VDD_330)
+
struct sdhci_fsl_fdt_softc {
device_t dev;
const struct sdhci_fsl_fdt_soc_data *soc_data;
@@ -295,18 +298,11 @@ sdhci_fsl_fdt_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
val32 = RD4(sc, off);
- switch (off) {
- case SDHCI_CAPABILITIES:
- val32 &= ~(SDHCI_CAN_DO_SUSPEND | SDHCI_CAN_VDD_180);
- break;
- case SDHCI_PRESENT_STATE:
+ if (off == SDHCI_PRESENT_STATE) {
wrk32 = val32;
val32 &= SDHCI_FSL_PRES_COMPAT_MASK;
val32 |= (wrk32 >> 4) & SDHCI_STATE_DAT_MASK;
val32 |= (wrk32 << 1) & SDHCI_STATE_CMD;
- break;
- default:
- break;
}
return (val32);
@@ -546,17 +542,78 @@ sdhci_fsl_fdt_get_card_present(device_t dev, struct sdhci_slot *slot)
return (sdhci_fdt_gpio_get_present(sc->gpio));
}
+static uint32_t
+sdhci_fsl_fdt_vddrange_to_mask(device_t dev, uint32_t *vdd_ranges, int len)
+{
+ uint32_t vdd_min, vdd_max;
+ uint32_t vdd_mask = 0;
+ int i;
+
+ /* Ranges are organized as pairs of values. */
+ if ((len % 2) != 0) {
+ device_printf(dev, "Invalid voltage range\n");
+ return (0);
+ }
+ len = len / 2;
+
+ for (i = 0; i < len; i++) {
+ vdd_min = vdd_ranges[2 * i];
+ vdd_max = vdd_ranges[2 * i + 1];
+
+ if (vdd_min > vdd_max || vdd_min < 1650 || vdd_min > 3600 ||
+ vdd_max < 1650 || vdd_max > 3600) {
+ device_printf(dev, "Voltage range %d - %d is out of bounds\n",
+ vdd_min, vdd_max);
+ return (0);
+ }
+
+ if (vdd_min <= 1800 && vdd_max >= 1800)
+ vdd_mask |= SDHCI_CAN_VDD_180;
+ if (vdd_min <= 3000 && vdd_max >= 3000)
+ vdd_mask |= SDHCI_CAN_VDD_300;
+ if (vdd_min <= 3300 && vdd_max >= 3300)
+ vdd_mask |= SDHCI_CAN_VDD_330;
+ }
+
+ return (vdd_mask);
+}
+
static void
sdhci_fsl_fdt_of_parse(device_t dev)
{
struct sdhci_fsl_fdt_softc *sc;
phandle_t node;
+ pcell_t *voltage_ranges;
+ uint32_t vdd_mask = 0;
+ ssize_t num_ranges;
sc = device_get_softc(dev);
node = ofw_bus_get_node(dev);
/* Call mmc_fdt_parse in order to get mmc related properties. */
mmc_fdt_parse(dev, node, &sc->fdt_helper, &sc->slot.host);
+
+ sc->slot.caps = sdhci_fsl_fdt_read_4(dev, &sc->slot,
+ SDHCI_CAPABILITIES) & ~(SDHCI_CAN_DO_SUSPEND);
+ sc->slot.caps2 = sdhci_fsl_fdt_read_4(dev, &sc->slot,
+ SDHCI_CAPABILITIES2);
+
+ /* Parse the "voltage-ranges" dts property. */
+ num_ranges = OF_getencprop_alloc(node, "voltage-ranges",
+ (void **) &voltage_ranges);
+ if (num_ranges <= 0)
+ return;
+ vdd_mask = sdhci_fsl_fdt_vddrange_to_mask(dev, voltage_ranges,
+ num_ranges / sizeof(uint32_t));
+ OF_prop_free(voltage_ranges);
+
+ /* Overwrite voltage caps only if we got something from dts. */
+ if (vdd_mask != 0 &&
+ (vdd_mask != (sc->slot.caps & SDHCI_FSL_CAN_VDD_MASK))) {
+ sc->slot.caps &= ~(SDHCI_FSL_CAN_VDD_MASK);
+ sc->slot.caps |= vdd_mask;
+ sc->slot.quirks |= SDHCI_QUIRK_MISSING_CAPS;
+ }
}
static int