Message ID | 20181119125307.22486-1-adrian.hunter@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | [V3] mmc: sdhci-pci: Workaround GLK firmware failing to restore the tuning value | expand |
On 19 November 2018 at 13:53, Adrian Hunter <adrian.hunter@intel.com> wrote: > GLK firmware can indicate that the tuning value will be restored after > runtime suspend, but not actually do that. Add a workaround that detects > such cases, and lets the driver do re-tuning instead. > > Reported-by: Anisse Astier <anisse@astier.eu> > Tested-by: Anisse Astier <anisse@astier.eu> > Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> > Cc: stable@vger.kernel.org Thanks, applied for fixes! Kind regards Uffe > --- > > > Changes in V3: > > Added Reported-by/Tested-by: Anisse Astier > Added Cc: stable > > Changes in V2: > > Use bitfield macros > Tweak the rx delay value > > > drivers/mmc/host/sdhci-pci-core.c | 79 ++++++++++++++++++++++++++++++- > 1 file changed, 77 insertions(+), 2 deletions(-) > > diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c > index e53333c695b3..c4115bae5db1 100644 > --- a/drivers/mmc/host/sdhci-pci-core.c > +++ b/drivers/mmc/host/sdhci-pci-core.c > @@ -12,6 +12,7 @@ > * - JMicron (hardware and technical support) > */ > > +#include <linux/bitfield.h> > #include <linux/string.h> > #include <linux/delay.h> > #include <linux/highmem.h> > @@ -462,6 +463,9 @@ struct intel_host { > u32 dsm_fns; > int drv_strength; > bool d3_retune; > + bool rpm_retune_ok; > + u32 glk_rx_ctrl1; > + u32 glk_tun_val; > }; > > static const guid_t intel_dsm_guid = > @@ -791,6 +795,77 @@ static int glk_emmc_add_host(struct sdhci_pci_slot *slot) > return ret; > } > > +#ifdef CONFIG_PM > +#define GLK_RX_CTRL1 0x834 > +#define GLK_TUN_VAL 0x840 > +#define GLK_PATH_PLL GENMASK(13, 8) > +#define GLK_DLY GENMASK(6, 0) > +/* Workaround firmware failing to restore the tuning value */ > +static void glk_rpm_retune_wa(struct sdhci_pci_chip *chip, bool susp) > +{ > + struct sdhci_pci_slot *slot = chip->slots[0]; > + struct intel_host *intel_host = sdhci_pci_priv(slot); > + struct sdhci_host *host = slot->host; > + u32 glk_rx_ctrl1; > + u32 glk_tun_val; > + u32 dly; > + > + if (intel_host->rpm_retune_ok || !mmc_can_retune(host->mmc)) > + return; > + > + glk_rx_ctrl1 = sdhci_readl(host, GLK_RX_CTRL1); > + glk_tun_val = sdhci_readl(host, GLK_TUN_VAL); > + > + if (susp) { > + intel_host->glk_rx_ctrl1 = glk_rx_ctrl1; > + intel_host->glk_tun_val = glk_tun_val; > + return; > + } > + > + if (!intel_host->glk_tun_val) > + return; > + > + if (glk_rx_ctrl1 != intel_host->glk_rx_ctrl1) { > + intel_host->rpm_retune_ok = true; > + return; > + } > + > + dly = FIELD_PREP(GLK_DLY, FIELD_GET(GLK_PATH_PLL, glk_rx_ctrl1) + > + (intel_host->glk_tun_val << 1)); > + if (dly == FIELD_GET(GLK_DLY, glk_rx_ctrl1)) > + return; > + > + glk_rx_ctrl1 = (glk_rx_ctrl1 & ~GLK_DLY) | dly; > + sdhci_writel(host, glk_rx_ctrl1, GLK_RX_CTRL1); > + > + intel_host->rpm_retune_ok = true; > + chip->rpm_retune = true; > + mmc_retune_needed(host->mmc); > + pr_info("%s: Requiring re-tune after rpm resume", mmc_hostname(host->mmc)); > +} > + > +static void glk_rpm_retune_chk(struct sdhci_pci_chip *chip, bool susp) > +{ > + if (chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC && > + !chip->rpm_retune) > + glk_rpm_retune_wa(chip, susp); > +} > + > +static int glk_runtime_suspend(struct sdhci_pci_chip *chip) > +{ > + glk_rpm_retune_chk(chip, true); > + > + return sdhci_cqhci_runtime_suspend(chip); > +} > + > +static int glk_runtime_resume(struct sdhci_pci_chip *chip) > +{ > + glk_rpm_retune_chk(chip, false); > + > + return sdhci_cqhci_runtime_resume(chip); > +} > +#endif > + > #ifdef CONFIG_ACPI > static int ni_set_max_freq(struct sdhci_pci_slot *slot) > { > @@ -879,8 +954,8 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = { > .resume = sdhci_cqhci_resume, > #endif > #ifdef CONFIG_PM > - .runtime_suspend = sdhci_cqhci_runtime_suspend, > - .runtime_resume = sdhci_cqhci_runtime_resume, > + .runtime_suspend = glk_runtime_suspend, > + .runtime_resume = glk_runtime_resume, > #endif > .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, > .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | > -- > 2.17.1 >
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c index e53333c695b3..c4115bae5db1 100644 --- a/drivers/mmc/host/sdhci-pci-core.c +++ b/drivers/mmc/host/sdhci-pci-core.c @@ -12,6 +12,7 @@ * - JMicron (hardware and technical support) */ +#include <linux/bitfield.h> #include <linux/string.h> #include <linux/delay.h> #include <linux/highmem.h> @@ -462,6 +463,9 @@ struct intel_host { u32 dsm_fns; int drv_strength; bool d3_retune; + bool rpm_retune_ok; + u32 glk_rx_ctrl1; + u32 glk_tun_val; }; static const guid_t intel_dsm_guid = @@ -791,6 +795,77 @@ static int glk_emmc_add_host(struct sdhci_pci_slot *slot) return ret; } +#ifdef CONFIG_PM +#define GLK_RX_CTRL1 0x834 +#define GLK_TUN_VAL 0x840 +#define GLK_PATH_PLL GENMASK(13, 8) +#define GLK_DLY GENMASK(6, 0) +/* Workaround firmware failing to restore the tuning value */ +static void glk_rpm_retune_wa(struct sdhci_pci_chip *chip, bool susp) +{ + struct sdhci_pci_slot *slot = chip->slots[0]; + struct intel_host *intel_host = sdhci_pci_priv(slot); + struct sdhci_host *host = slot->host; + u32 glk_rx_ctrl1; + u32 glk_tun_val; + u32 dly; + + if (intel_host->rpm_retune_ok || !mmc_can_retune(host->mmc)) + return; + + glk_rx_ctrl1 = sdhci_readl(host, GLK_RX_CTRL1); + glk_tun_val = sdhci_readl(host, GLK_TUN_VAL); + + if (susp) { + intel_host->glk_rx_ctrl1 = glk_rx_ctrl1; + intel_host->glk_tun_val = glk_tun_val; + return; + } + + if (!intel_host->glk_tun_val) + return; + + if (glk_rx_ctrl1 != intel_host->glk_rx_ctrl1) { + intel_host->rpm_retune_ok = true; + return; + } + + dly = FIELD_PREP(GLK_DLY, FIELD_GET(GLK_PATH_PLL, glk_rx_ctrl1) + + (intel_host->glk_tun_val << 1)); + if (dly == FIELD_GET(GLK_DLY, glk_rx_ctrl1)) + return; + + glk_rx_ctrl1 = (glk_rx_ctrl1 & ~GLK_DLY) | dly; + sdhci_writel(host, glk_rx_ctrl1, GLK_RX_CTRL1); + + intel_host->rpm_retune_ok = true; + chip->rpm_retune = true; + mmc_retune_needed(host->mmc); + pr_info("%s: Requiring re-tune after rpm resume", mmc_hostname(host->mmc)); +} + +static void glk_rpm_retune_chk(struct sdhci_pci_chip *chip, bool susp) +{ + if (chip->pdev->device == PCI_DEVICE_ID_INTEL_GLK_EMMC && + !chip->rpm_retune) + glk_rpm_retune_wa(chip, susp); +} + +static int glk_runtime_suspend(struct sdhci_pci_chip *chip) +{ + glk_rpm_retune_chk(chip, true); + + return sdhci_cqhci_runtime_suspend(chip); +} + +static int glk_runtime_resume(struct sdhci_pci_chip *chip) +{ + glk_rpm_retune_chk(chip, false); + + return sdhci_cqhci_runtime_resume(chip); +} +#endif + #ifdef CONFIG_ACPI static int ni_set_max_freq(struct sdhci_pci_slot *slot) { @@ -879,8 +954,8 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = { .resume = sdhci_cqhci_resume, #endif #ifdef CONFIG_PM - .runtime_suspend = sdhci_cqhci_runtime_suspend, - .runtime_resume = sdhci_cqhci_runtime_resume, + .runtime_suspend = glk_runtime_suspend, + .runtime_resume = glk_runtime_resume, #endif .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC, .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN |