]> git.proxmox.com Git - mirror_ubuntu-jammy-kernel.git/commitdiff
scsi: ufs-pci: Add LTR support for Intel controllers
authorAdrian Hunter <adrian.hunter@intel.com>
Thu, 27 Aug 2020 07:20:30 +0000 (10:20 +0300)
committerMartin K. Petersen <martin.petersen@oracle.com>
Tue, 15 Sep 2020 20:11:47 +0000 (16:11 -0400)
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: 8c09d7527697 ("scsi: ufshdc-pci: Add Intel PCI IDs for EHL")
Fixes: 1ab27c9cf8b6 ("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>
drivers/scsi/ufs/ufshcd-pci.c

index 5a95a7bfbab0c1bc98c49da33f5efb62ac77d4b3..df3a564c3e334875ed9a2eab331344f3468031b1 100644 (file)
 #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);