Commit 46f4a69e authored by Adrian Hunter's avatar Adrian Hunter Committed by Ulf Hansson
Browse files

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.

Signed-off-by: default avatarAdrian Hunter <adrian.hunter@intel.com>
Fixes: cb3a7d4a ("mmc: sdhci-pci: Add support for Intel EHL")
Cc: stable@vger.kernel.org
Link: https://lore.kernel.org/r/20200818104508.7149-1-adrian.hunter@intel.com


Signed-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent c92a6af6
Loading
Loading
Loading
Loading
+154 −0
Original line number Diff line number Diff line
@@ -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>
@@ -516,6 +518,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 =
@@ -760,6 +764,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;
@@ -774,6 +880,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)
@@ -854,6 +997,8 @@ static int glk_emmc_add_host(struct sdhci_pci_slot *slot)
	if (ret)
		goto cleanup;

	byt_add_debugfs(slot);

	return 0;

cleanup:
@@ -1031,6 +1176,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 |
@@ -1044,6 +1191,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,
@@ -1074,6 +1222,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),
};
@@ -1091,6 +1241,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),
};
@@ -1110,6 +1262,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),
};