Re: [PATCH] mmc: sdhci: Add LTR support for some Intel BYT based controllers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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
>



[Index of Archives]     [Linux Memonry Technology]     [Linux USB Devel]     [Linux Media]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux