mirror of
https://github.com/torvalds/linux.git
synced 2024-11-18 01:51:53 +00:00
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 "ufshcd.h"
|
||||||
#include <linux/pci.h>
|
#include <linux/pci.h>
|
||||||
#include <linux/pm_runtime.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)
|
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;
|
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)
|
static int ufs_intel_ehl_init(struct ufs_hba *hba)
|
||||||
{
|
{
|
||||||
hba->quirks |= UFSHCD_QUIRK_BROKEN_AUTO_HIBERN8;
|
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 = {
|
static struct ufs_hba_variant_ops ufs_intel_cnl_hba_vops = {
|
||||||
.name = "intel-pci",
|
.name = "intel-pci",
|
||||||
|
.init = ufs_intel_common_init,
|
||||||
|
.exit = ufs_intel_common_exit,
|
||||||
.link_startup_notify = ufs_intel_link_startup_notify,
|
.link_startup_notify = ufs_intel_link_startup_notify,
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct ufs_hba_variant_ops ufs_intel_ehl_hba_vops = {
|
static struct ufs_hba_variant_ops ufs_intel_ehl_hba_vops = {
|
||||||
.name = "intel-pci",
|
.name = "intel-pci",
|
||||||
.init = ufs_intel_ehl_init,
|
.init = ufs_intel_ehl_init,
|
||||||
|
.exit = ufs_intel_common_exit,
|
||||||
.link_startup_notify = ufs_intel_link_startup_notify,
|
.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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pci_set_drvdata(pdev, hba);
|
||||||
|
|
||||||
hba->vops = (struct ufs_hba_variant_ops *)id->driver_data;
|
hba->vops = (struct ufs_hba_variant_ops *)id->driver_data;
|
||||||
|
|
||||||
err = ufshcd_init(hba, mmio_base, pdev->irq);
|
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;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
pci_set_drvdata(pdev, hba);
|
|
||||||
pm_runtime_put_noidle(&pdev->dev);
|
pm_runtime_put_noidle(&pdev->dev);
|
||||||
pm_runtime_allow(&pdev->dev);
|
pm_runtime_allow(&pdev->dev);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user