On Tue, 18 Aug 2020 at 12:45, Adrian Hunter <adrian.hunter@xxxxxxxxx> wrote: > > Some Intel BYT based host controllers support the setting of latency > tolerance. Accordingly, implement the PM QoS ->set_latency_tolerance() > callback. The raw register values are also exposed via debugfs. > > Intel EHL controllers require this support. > > Signed-off-by: Adrian Hunter <adrian.hunter@xxxxxxxxx> > Fixes: cb3a7d4a0aec4e ("mmc: sdhci-pci: Add support for Intel EHL") > Cc: stable@xxxxxxxxxxxxxxx Applied for next, thanks! Kind regards Uffe > --- > drivers/mmc/host/sdhci-pci-core.c | 154 ++++++++++++++++++++++++++++++ > 1 file changed, 154 insertions(+) > > diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c > index bb6802448b2f..c0e081e48d3f 100644 > --- a/drivers/mmc/host/sdhci-pci-core.c > +++ b/drivers/mmc/host/sdhci-pci-core.c > @@ -24,6 +24,8 @@ > #include <linux/iopoll.h> > #include <linux/gpio.h> > #include <linux/pm_runtime.h> > +#include <linux/pm_qos.h> > +#include <linux/debugfs.h> > #include <linux/mmc/slot-gpio.h> > #include <linux/mmc/sdhci-pci-data.h> > #include <linux/acpi.h> > @@ -508,6 +510,8 @@ struct intel_host { > bool rpm_retune_ok; > u32 glk_rx_ctrl1; > u32 glk_tun_val; > + u32 active_ltr; > + u32 idle_ltr; > }; > > static const guid_t intel_dsm_guid = > @@ -752,6 +756,108 @@ static int intel_execute_tuning(struct mmc_host *mmc, u32 opcode) > return 0; > } > > +#define INTEL_ACTIVELTR 0x804 > +#define INTEL_IDLELTR 0x808 > + > +#define INTEL_LTR_REQ BIT(15) > +#define INTEL_LTR_SCALE_MASK GENMASK(11, 10) > +#define INTEL_LTR_SCALE_1US (2 << 10) > +#define INTEL_LTR_SCALE_32US (3 << 10) > +#define INTEL_LTR_VALUE_MASK GENMASK(9, 0) > + > +static void intel_cache_ltr(struct sdhci_pci_slot *slot) > +{ > + struct intel_host *intel_host = sdhci_pci_priv(slot); > + struct sdhci_host *host = slot->host; > + > + intel_host->active_ltr = readl(host->ioaddr + INTEL_ACTIVELTR); > + intel_host->idle_ltr = readl(host->ioaddr + INTEL_IDLELTR); > +} > + > +static void intel_ltr_set(struct device *dev, s32 val) > +{ > + struct sdhci_pci_chip *chip = dev_get_drvdata(dev); > + struct sdhci_pci_slot *slot = chip->slots[0]; > + struct intel_host *intel_host = sdhci_pci_priv(slot); > + struct sdhci_host *host = slot->host; > + u32 ltr; > + > + pm_runtime_get_sync(dev); > + > + /* > + * Program latency tolerance (LTR) accordingly what has been asked > + * by the PM QoS layer or disable it in case we were passed > + * negative value or PM_QOS_LATENCY_ANY. > + */ > + ltr = readl(host->ioaddr + INTEL_ACTIVELTR); > + > + if (val == PM_QOS_LATENCY_ANY || val < 0) { > + ltr &= ~INTEL_LTR_REQ; > + } else { > + ltr |= INTEL_LTR_REQ; > + ltr &= ~INTEL_LTR_SCALE_MASK; > + ltr &= ~INTEL_LTR_VALUE_MASK; > + > + if (val > INTEL_LTR_VALUE_MASK) { > + val >>= 5; > + if (val > INTEL_LTR_VALUE_MASK) > + val = INTEL_LTR_VALUE_MASK; > + ltr |= INTEL_LTR_SCALE_32US | val; > + } else { > + ltr |= INTEL_LTR_SCALE_1US | val; > + } > + } > + > + if (ltr == intel_host->active_ltr) > + goto out; > + > + writel(ltr, host->ioaddr + INTEL_ACTIVELTR); > + writel(ltr, host->ioaddr + INTEL_IDLELTR); > + > + /* Cache the values into lpss structure */ > + intel_cache_ltr(slot); > +out: > + pm_runtime_put_autosuspend(dev); > +} > + > +static bool intel_use_ltr(struct sdhci_pci_chip *chip) > +{ > + switch (chip->pdev->device) { > + case PCI_DEVICE_ID_INTEL_BYT_EMMC: > + case PCI_DEVICE_ID_INTEL_BYT_EMMC2: > + case PCI_DEVICE_ID_INTEL_BYT_SDIO: > + case PCI_DEVICE_ID_INTEL_BYT_SD: > + case PCI_DEVICE_ID_INTEL_BSW_EMMC: > + case PCI_DEVICE_ID_INTEL_BSW_SDIO: > + case PCI_DEVICE_ID_INTEL_BSW_SD: > + return false; > + default: > + return true; > + } > +} > + > +static void intel_ltr_expose(struct sdhci_pci_chip *chip) > +{ > + struct device *dev = &chip->pdev->dev; > + > + if (!intel_use_ltr(chip)) > + return; > + > + dev->power.set_latency_tolerance = intel_ltr_set; > + dev_pm_qos_expose_latency_tolerance(dev); > +} > + > +static void intel_ltr_hide(struct sdhci_pci_chip *chip) > +{ > + struct device *dev = &chip->pdev->dev; > + > + if (!intel_use_ltr(chip)) > + return; > + > + dev_pm_qos_hide_latency_tolerance(dev); > + dev->power.set_latency_tolerance = NULL; > +} > + > static void byt_probe_slot(struct sdhci_pci_slot *slot) > { > struct mmc_host_ops *ops = &slot->host->mmc_host_ops; > @@ -766,6 +872,43 @@ static void byt_probe_slot(struct sdhci_pci_slot *slot) > ops->start_signal_voltage_switch = intel_start_signal_voltage_switch; > > device_property_read_u32(dev, "max-frequency", &mmc->f_max); > + > + if (!mmc->slotno) { > + slot->chip->slots[mmc->slotno] = slot; > + intel_ltr_expose(slot->chip); > + } > +} > + > +static void byt_add_debugfs(struct sdhci_pci_slot *slot) > +{ > + struct intel_host *intel_host = sdhci_pci_priv(slot); > + struct mmc_host *mmc = slot->host->mmc; > + struct dentry *dir = mmc->debugfs_root; > + > + if (!intel_use_ltr(slot->chip)) > + return; > + > + debugfs_create_x32("active_ltr", 0444, dir, &intel_host->active_ltr); > + debugfs_create_x32("idle_ltr", 0444, dir, &intel_host->idle_ltr); > + > + intel_cache_ltr(slot); > +} > + > +static int byt_add_host(struct sdhci_pci_slot *slot) > +{ > + int ret = sdhci_add_host(slot->host); > + > + if (!ret) > + byt_add_debugfs(slot); > + return ret; > +} > + > +static void byt_remove_slot(struct sdhci_pci_slot *slot, int dead) > +{ > + struct mmc_host *mmc = slot->host->mmc; > + > + if (!mmc->slotno) > + intel_ltr_hide(slot->chip); > } > > static int byt_emmc_probe_slot(struct sdhci_pci_slot *slot) > @@ -846,6 +989,8 @@ static int glk_emmc_add_host(struct sdhci_pci_slot *slot) > if (ret) > goto cleanup; > > + byt_add_debugfs(slot); > + > return 0; > > cleanup: > @@ -1023,6 +1168,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_emmc = { > #endif > .allow_runtime_pm = true, > .probe_slot = byt_emmc_probe_slot, > + .add_host = byt_add_host, > + .remove_slot = byt_remove_slot, > .quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC | > SDHCI_QUIRK_NO_LED, > .quirks2 = SDHCI_QUIRK2_PRESET_VALUE_BROKEN | > @@ -1036,6 +1183,7 @@ static const struct sdhci_pci_fixes sdhci_intel_glk_emmc = { > .allow_runtime_pm = true, > .probe_slot = glk_emmc_probe_slot, > .add_host = glk_emmc_add_host, > + .remove_slot = byt_remove_slot, > #ifdef CONFIG_PM_SLEEP > .suspend = sdhci_cqhci_suspend, > .resume = sdhci_cqhci_resume, > @@ -1066,6 +1214,8 @@ static const struct sdhci_pci_fixes sdhci_ni_byt_sdio = { > SDHCI_QUIRK2_PRESET_VALUE_BROKEN, > .allow_runtime_pm = true, > .probe_slot = ni_byt_sdio_probe_slot, > + .add_host = byt_add_host, > + .remove_slot = byt_remove_slot, > .ops = &sdhci_intel_byt_ops, > .priv_size = sizeof(struct intel_host), > }; > @@ -1083,6 +1233,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sdio = { > SDHCI_QUIRK2_PRESET_VALUE_BROKEN, > .allow_runtime_pm = true, > .probe_slot = byt_sdio_probe_slot, > + .add_host = byt_add_host, > + .remove_slot = byt_remove_slot, > .ops = &sdhci_intel_byt_ops, > .priv_size = sizeof(struct intel_host), > }; > @@ -1102,6 +1254,8 @@ static const struct sdhci_pci_fixes sdhci_intel_byt_sd = { > .allow_runtime_pm = true, > .own_cd_for_runtime_pm = true, > .probe_slot = byt_sd_probe_slot, > + .add_host = byt_add_host, > + .remove_slot = byt_remove_slot, > .ops = &sdhci_intel_byt_ops, > .priv_size = sizeof(struct intel_host), > }; > -- > 2.17.1 >