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

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.comSigned-off-by: default avatarUlf Hansson <ulf.hansson@linaro.org>
parent c92a6af6
......@@ -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),
};
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment