forked from Minki/linux
scsi: ufs-pci: Add LTR support for Intel controllers
Intel 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. Link: https://lore.kernel.org/r/20200827072030.24655-1-adrian.hunter@intel.com Fixes:8c09d75276
("scsi: ufshdc-pci: Add Intel PCI IDs for EHL") Fixes:1ab27c9cf8
("ufs: Add support for clock gating") Reviewed-by: Avri Altman <avri.altman@wdc.com> Acked-by: Stanley Chu <stanley.chu@mediatek.com> Signed-off-by: Adrian Hunter <adrian.hunter@intel.com> Signed-off-by: Martin K. Petersen <martin.petersen@oracle.com>
This commit is contained in:
parent
02f7415054
commit
247f994459
@ -13,6 +13,14 @@
|
||||
#include "ufshcd.h"
|
||||
#include <linux/pci.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/pm_qos.h>
|
||||
#include <linux/debugfs.h>
|
||||
|
||||
struct intel_host {
|
||||
u32 active_ltr;
|
||||
u32 idle_ltr;
|
||||
struct dentry *debugfs_root;
|
||||
};
|
||||
|
||||
static int ufs_intel_disable_lcc(struct ufs_hba *hba)
|
||||
{
|
||||
@ -44,20 +52,134 @@ static int ufs_intel_link_startup_notify(struct ufs_hba *hba,
|
||||
return err;
|
||||
}
|
||||
|
||||
#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 ufs_hba *hba)
|
||||
{
|
||||
struct intel_host *host = ufshcd_get_variant(hba);
|
||||
|
||||
host->active_ltr = readl(hba->mmio_base + INTEL_ACTIVELTR);
|
||||
host->idle_ltr = readl(hba->mmio_base + INTEL_IDLELTR);
|
||||
}
|
||||
|
||||
static void intel_ltr_set(struct device *dev, s32 val)
|
||||
{
|
||||
struct ufs_hba *hba = dev_get_drvdata(dev);
|
||||
struct intel_host *host = ufshcd_get_variant(hba);
|
||||
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(hba->mmio_base + 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 == host->active_ltr)
|
||||
goto out;
|
||||
|
||||
writel(ltr, hba->mmio_base + INTEL_ACTIVELTR);
|
||||
writel(ltr, hba->mmio_base + INTEL_IDLELTR);
|
||||
|
||||
/* Cache the values into intel_host structure */
|
||||
intel_cache_ltr(hba);
|
||||
out:
|
||||
pm_runtime_put(dev);
|
||||
}
|
||||
|
||||
static void intel_ltr_expose(struct device *dev)
|
||||
{
|
||||
dev->power.set_latency_tolerance = intel_ltr_set;
|
||||
dev_pm_qos_expose_latency_tolerance(dev);
|
||||
}
|
||||
|
||||
static void intel_ltr_hide(struct device *dev)
|
||||
{
|
||||
dev_pm_qos_hide_latency_tolerance(dev);
|
||||
dev->power.set_latency_tolerance = NULL;
|
||||
}
|
||||
|
||||
static void intel_add_debugfs(struct ufs_hba *hba)
|
||||
{
|
||||
struct dentry *dir = debugfs_create_dir(dev_name(hba->dev), NULL);
|
||||
struct intel_host *host = ufshcd_get_variant(hba);
|
||||
|
||||
intel_cache_ltr(hba);
|
||||
|
||||
host->debugfs_root = dir;
|
||||
debugfs_create_x32("active_ltr", 0444, dir, &host->active_ltr);
|
||||
debugfs_create_x32("idle_ltr", 0444, dir, &host->idle_ltr);
|
||||
}
|
||||
|
||||
static void intel_remove_debugfs(struct ufs_hba *hba)
|
||||
{
|
||||
struct intel_host *host = ufshcd_get_variant(hba);
|
||||
|
||||
debugfs_remove_recursive(host->debugfs_root);
|
||||
}
|
||||
|
||||
static int ufs_intel_common_init(struct ufs_hba *hba)
|
||||
{
|
||||
struct intel_host *host;
|
||||
|
||||
host = devm_kzalloc(hba->dev, sizeof(*host), GFP_KERNEL);
|
||||
if (!host)
|
||||
return -ENOMEM;
|
||||
ufshcd_set_variant(hba, host);
|
||||
intel_ltr_expose(hba->dev);
|
||||
intel_add_debugfs(hba);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ufs_intel_common_exit(struct ufs_hba *hba)
|
||||
{
|
||||
intel_remove_debugfs(hba);
|
||||
intel_ltr_hide(hba->dev);
|
||||
}
|
||||
|
||||
static int ufs_intel_ehl_init(struct ufs_hba *hba)
|
||||
{
|
||||
hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
|
||||
return 0;
|
||||
return ufs_intel_common_init(hba);
|
||||
}
|
||||
|
||||
static struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = {
|
||||
.name = "intel-pci",
|
||||
.init = ufs_intel_common_init,
|
||||
.exit = ufs_intel_common_exit,
|
||||
.link_startup_notify = ufs_intel_link_startup_notify,
|
||||
};
|
||||
|
||||
static struct ufs_hba_variant_ops ufs_intel_ehl_hba_vops = {
|
||||
.name = "intel-pci",
|
||||
.init = ufs_intel_ehl_init,
|
||||
.exit = ufs_intel_common_exit,
|
||||
.link_startup_notify = ufs_intel_link_startup_notify,
|
||||
};
|
||||
|
||||
@ -162,6 +284,8 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
return err;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, hba);
|
||||
|
||||
hba->vops = (struct ufs_hba_variant_ops *)id->driver_data;
|
||||
|
||||
err = ufshcd_init(hba, mmio_base, pdev->irq);
|
||||
@ -171,7 +295,6 @@ ufshcd_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
|
||||
return err;
|
||||
}
|
||||
|
||||
pci_set_drvdata(pdev, hba);
|
||||
pm_runtime_put_noidle(&pdev->dev);
|
||||
pm_runtime_allow(&pdev->dev);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user