diff mbox series

mmc: sdhci: Add LTR support for some Intel BYT based controllers

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

Commit Message

Adrian Hunter Aug. 18, 2020, 10:45 a.m. UTC
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(+)

Comments

Ulf Hansson Aug. 24, 2020, 9:51 a.m. UTC | #1
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 mbox series

Patch

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),
 };