Skip to contentSkip to conversation

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

PATCH In review Adrian Hunter 2 participants 2 messages Aug 18, 2020 – Aug 24, 2020

Subsystems (2)
lore
Share & tools
lore

Adrian Hunter

author maintainer
#0 lore raw

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

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.

Fixes cb3a7d4a0aec4e ("mmc: sdhci-pci: Add support for Intel EHL")
---
 drivers/mmc/host/sdhci-pci-core.c | 154 ++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)
drivers/mmc/host/sdhci-pci-core.c +154 -0
index bb6802448b2f..c0e081e48d3f 100644
@@ -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),
 };
Signature · 1 line
2.17.1
#1 lore raw
Quoted 10 lines · Adrian Hunter

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.

Fixes cb3a7d4a0aec4e ("mmc: sdhci-pci: Add support for Intel EHL")

Applied for next, thanks!

Kind regards
Uffe

Quoted 235 lines
---
 drivers/mmc/host/sdhci-pci-core.c | 154 ++++++++++++++++++++++++++++++
 1 file changed, 154 insertions(+)
drivers/mmc/host/sdhci-pci-core.c +154 -1
index bb6802448b2f..c0e081e48d3f 100644
@@ -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