Message ID | 20200818104508.7149-1-adrian.hunter@intel.com (mailing list archive) |
---|---|
State | New, archived |
Headers | show |
Series | mmc: sdhci: Add LTR support for some Intel BYT based controllers | expand |
On Tue, 18 Aug 2020 at 12:45, Adrian Hunter <adrian.hunter@intel.com> 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@intel.com> > Fixes: cb3a7d4a0aec4e ("mmc: sdhci-pci: Add support for Intel EHL") > Cc: stable@vger.kernel.org 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 >
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), };
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@intel.com> Fixes: cb3a7d4a0aec4e ("mmc: sdhci-pci: Add support for Intel EHL") Cc: stable@vger.kernel.org --- drivers/mmc/host/sdhci-pci-core.c | 154 ++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+)