From 835998c2107e8c5388dd8ac81baa9aaee49ea2df Mon Sep 17 00:00:00 2001 From: Marius Strobl Date: Sun, 18 Nov 2018 00:52:27 +0000 Subject: [PATCH] Add a quirk handling for AMDI0040 controllers allowing them to do HS400. Submitted by: Shreyank Amartya (original version) --- sys/dev/sdhci/sdhci.c | 3 ++ sys/dev/sdhci/sdhci.h | 2 + sys/dev/sdhci/sdhci_acpi.c | 76 +++++++++++++++++++++++++++++++------- 3 files changed, 68 insertions(+), 13 deletions(-) diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c index f117fb49c88d..5b347499e031 100644 --- a/sys/dev/sdhci/sdhci.c +++ b/sys/dev/sdhci/sdhci.c @@ -898,6 +898,9 @@ sdhci_init_slot(device_t dev, struct sdhci_slot *slot, int num) if (slot->quirks & SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 && caps2 & SDHCI_CAN_MMC_HS400) host_caps |= MMC_CAP_MMC_HS400; + if (slot->quirks & SDHCI_QUIRK_MMC_HS400_IF_CAN_SDR104 && + caps2 & SDHCI_CAN_SDR104) + host_caps |= MMC_CAP_MMC_HS400; /* * Disable UHS-I and eMMC modes if the set_uhs_timing method is the diff --git a/sys/dev/sdhci/sdhci.h b/sys/dev/sdhci/sdhci.h index 760672800892..cdb9b7343c08 100644 --- a/sys/dev/sdhci/sdhci.h +++ b/sys/dev/sdhci/sdhci.h @@ -93,6 +93,8 @@ #define SDHCI_QUIRK_PRESET_VALUE_BROKEN (1 << 27) /* Controller does not support or the support for ACMD12 is broken. */ #define SDHCI_QUIRK_BROKEN_AUTO_STOP (1 << 28) +/* Controller supports eMMC HS400 mode if SDHCI_CAN_SDR104 is set. */ +#define SDHCI_QUIRK_MMC_HS400_IF_CAN_SDR104 (1 << 29) /* * Controller registers diff --git a/sys/dev/sdhci/sdhci_acpi.c b/sys/dev/sdhci/sdhci_acpi.c index 39dac3e5d890..0d7333b9f820 100644 --- a/sys/dev/sdhci/sdhci_acpi.c +++ b/sys/dev/sdhci/sdhci_acpi.c @@ -45,12 +45,15 @@ __FBSDID("$FreeBSD$"); #include #include +#include #include #include "mmcbr_if.h" #include "sdhci_if.h" +#define SDHCI_AMD_RESET_DLL_REG 0x908 + static const struct sdhci_acpi_device { const char* hid; int uid; @@ -80,7 +83,8 @@ static const struct sdhci_acpi_device { SDHCI_QUIRK_CAPS_BIT63_FOR_MMC_HS400 | SDHCI_QUIRK_PRESET_VALUE_BROKEN }, { "AMDI0040", 0, "AMD eMMC 5.0 Controller", - SDHCI_QUIRK_32BIT_DMA_SIZE }, + SDHCI_QUIRK_32BIT_DMA_SIZE | + SDHCI_QUIRK_MMC_HS400_IF_CAN_SDR104 }, { NULL, 0, NULL, 0} }; @@ -94,12 +98,11 @@ static char *sdhci_ids[] = { }; struct sdhci_acpi_softc { - u_int quirks; /* Chip specific quirks */ - struct resource *irq_res; /* IRQ resource */ - void *intrhand; /* Interrupt handle */ - struct sdhci_slot slot; struct resource *mem_res; /* Memory resource */ + struct resource *irq_res; /* IRQ resource */ + void *intrhand; /* Interrupt handle */ + const struct sdhci_acpi_device *acpi_dev; }; static void sdhci_acpi_intr(void *arg); @@ -189,6 +192,52 @@ sdhci_acpi_write_multi_4(device_t dev, struct sdhci_slot *slot __unused, bus_write_multi_stream_4(sc->mem_res, off, data, count); } +static void +sdhci_acpi_set_uhs_timing(device_t dev, struct sdhci_slot *slot) +{ + const struct sdhci_acpi_softc *sc; + const struct sdhci_acpi_device *acpi_dev; + const struct mmc_ios *ios; + device_t bus; + uint16_t old_timing; + enum mmc_bus_timing timing; + + bus = slot->bus; + old_timing = sdhci_acpi_read_2(bus, slot, SDHCI_HOST_CONTROL2); + old_timing &= SDHCI_CTRL2_UHS_MASK; + sdhci_generic_set_uhs_timing(dev, slot); + + sc = device_get_softc(dev); + acpi_dev = sc->acpi_dev; + /* + * AMDI0040 controllers require SDHCI_CTRL2_SAMPLING_CLOCK to be + * disabled when switching from HS200 to high speed and to always + * be turned on again when tuning for HS400. In the later case, + * an AMD-specific DLL reset additionally is needed. + */ + if (strcmp(acpi_dev->hid, "AMDI0040") == 0 && acpi_dev->uid == 0) { + ios = &slot->host.ios; + timing = ios->timing; + if (old_timing == SDHCI_CTRL2_UHS_SDR104 && + timing == bus_timing_hs) + sdhci_acpi_write_2(bus, slot, SDHCI_HOST_CONTROL2, + sdhci_acpi_read_2(bus, slot, SDHCI_HOST_CONTROL2) & + ~SDHCI_CTRL2_SAMPLING_CLOCK); + if (ios->clock > SD_SDR50_MAX && + old_timing != SDHCI_CTRL2_MMC_HS400 && + timing == bus_timing_mmc_hs400) { + sdhci_acpi_write_2(bus, slot, SDHCI_HOST_CONTROL2, + sdhci_acpi_read_2(bus, slot, SDHCI_HOST_CONTROL2) | + SDHCI_CTRL2_SAMPLING_CLOCK); + sdhci_acpi_write_4(bus, slot, SDHCI_AMD_RESET_DLL_REG, + 0x40003210); + DELAY(20); + sdhci_acpi_write_4(bus, slot, SDHCI_AMD_RESET_DLL_REG, + 0x40033210); + } + } +} + static const struct sdhci_acpi_device * sdhci_acpi_find_device(device_t dev) { @@ -198,7 +247,7 @@ sdhci_acpi_find_device(device_t dev) ACPI_STATUS status; int rv; - rv = ACPI_ID_PROBE(device_get_parent(dev), dev, sdhci_ids, &hid); + rv = ACPI_ID_PROBE(device_get_parent(dev), dev, sdhci_ids, &hid); if (rv > 0) return (NULL); @@ -238,13 +287,15 @@ sdhci_acpi_attach(device_t dev) { struct sdhci_acpi_softc *sc = device_get_softc(dev); int rid, err; + u_int quirks; const struct sdhci_acpi_device *acpi_dev; acpi_dev = sdhci_acpi_find_device(dev); if (acpi_dev == NULL) return (ENXIO); - sc->quirks = acpi_dev->quirks; + sc->acpi_dev = acpi_dev; + quirks = acpi_dev->quirks; /* Allocate IRQ. */ rid = 0; @@ -272,11 +323,10 @@ sdhci_acpi_attach(device_t dev) if (strcmp(acpi_dev->hid, "80860F14") == 0 && acpi_dev->uid == 1 && SDHCI_READ_4(dev, &sc->slot, SDHCI_CAPABILITIES) == 0x446cc8b2 && SDHCI_READ_4(dev, &sc->slot, SDHCI_CAPABILITIES2) == 0x00000807) - sc->quirks |= SDHCI_QUIRK_MMC_DDR52 | - SDHCI_QUIRK_DATA_TIMEOUT_1MHZ; - sc->quirks &= ~sdhci_quirk_clear; - sc->quirks |= sdhci_quirk_set; - sc->slot.quirks = sc->quirks; + quirks |= SDHCI_QUIRK_MMC_DDR52 | SDHCI_QUIRK_DATA_TIMEOUT_1MHZ; + quirks &= ~sdhci_quirk_clear; + quirks |= sdhci_quirk_set; + sc->slot.quirks = quirks; err = sdhci_init_slot(dev, &sc->slot, 0); if (err) { @@ -393,7 +443,7 @@ static device_method_t sdhci_methods[] = { DEVMETHOD(sdhci_write_2, sdhci_acpi_write_2), DEVMETHOD(sdhci_write_4, sdhci_acpi_write_4), DEVMETHOD(sdhci_write_multi_4, sdhci_acpi_write_multi_4), - DEVMETHOD(sdhci_set_uhs_timing, sdhci_generic_set_uhs_timing), + DEVMETHOD(sdhci_set_uhs_timing, sdhci_acpi_set_uhs_timing), DEVMETHOD_END };