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
This commit is contained in:
Marcin Wojtas 2021-04-22 13:01:06 +02:00
parent f0a9d7d799
commit 1f84b3a247

View File

@ -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