[PATCH 12/13] i3c: mipi-i3c-hci-pci: Add LTR support for Intel controllers
Adrian Hunter
adrian.hunter at intel.com
Wed Nov 12 02:03:38 PST 2025
Add support for Latency Tolerance Reporting (LTR) for Intel controllers.
Implement PM ->set_latency_tolerance() callback to set LTR register values.
Also expose LTR register values via debugfs.
Signed-off-by: Adrian Hunter <adrian.hunter at intel.com>
---
.../master/mipi-i3c-hci/mipi-i3c-hci-pci.c | 120 ++++++++++++++++++
1 file changed, 120 insertions(+)
diff --git a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
index d53e9dfc5ceb..1d76dc566ab6 100644
--- a/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
+++ b/drivers/i3c/master/mipi-i3c-hci/mipi-i3c-hci-pci.c
@@ -7,12 +7,14 @@
* Author: Jarkko Nikula <jarkko.nikula at linux.intel.com>
*/
#include <linux/acpi.h>
+#include <linux/debugfs.h>
#include <linux/idr.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/platform_device.h>
+#include <linux/pm_qos.h>
struct mipi_i3c_hci_pci {
struct pci_dev *pci;
@@ -35,10 +37,118 @@ static DEFINE_IDA(mipi_i3c_hci_pci_ida);
#define INTEL_RESETS_RESET_DONE BIT(1)
#define INTEL_RESETS_TIMEOUT_US 10000
+#define INTEL_ACTIVELTR 0x0c
+#define INTEL_IDLELTR 0x10
+
+#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)
+
struct intel_host {
void __iomem *priv;
+ u32 active_ltr;
+ u32 idle_ltr;
+ struct dentry *debugfs_root;
};
+static void intel_cache_ltr(struct intel_host *host)
+{
+ host->active_ltr = readl(host->priv + INTEL_ACTIVELTR);
+ host->idle_ltr = readl(host->priv + INTEL_IDLELTR);
+}
+
+static void intel_ltr_set(struct device *dev, s32 val)
+{
+ struct mipi_i3c_hci_pci *hci = dev_get_drvdata(dev);
+ struct intel_host *host = hci->private;
+ u32 ltr;
+
+ /*
+ * 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->priv + 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)
+ ltr |= INTEL_LTR_SCALE_32US | val >> 5;
+ else
+ ltr |= INTEL_LTR_SCALE_1US | val;
+ }
+
+ if (ltr == host->active_ltr)
+ return;
+
+ writel(ltr, host->priv + INTEL_ACTIVELTR);
+ writel(ltr, host->priv + INTEL_IDLELTR);
+
+ /* Cache the values into intel_host structure */
+ intel_cache_ltr(host);
+}
+
+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 struct dentry *intel_actualize_debugfs_root(bool add)
+{
+ static struct dentry *debugfs_root;
+ static DEFINE_MUTEX(lock);
+ static int ref_cnt;
+
+ guard(mutex)(&lock);
+
+ ref_cnt += add ? 1 : -1;
+
+ if (ref_cnt) {
+ if (IS_ERR_OR_NULL(debugfs_root))
+ debugfs_root = debugfs_create_dir("intel_i3c", NULL);
+ } else {
+ debugfs_remove(debugfs_root);
+ debugfs_root = NULL;
+ }
+
+ return debugfs_root;
+}
+
+static void intel_add_debugfs(struct mipi_i3c_hci_pci *hci)
+{
+ struct dentry *debugfs_root = intel_actualize_debugfs_root(true);
+ struct dentry *dir = debugfs_create_dir(dev_name(&hci->pci->dev), debugfs_root);
+ struct intel_host *host = hci->private;
+
+ intel_cache_ltr(host);
+
+ 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 mipi_i3c_hci_pci *hci)
+{
+ struct intel_host *host = hci->private;
+
+ debugfs_remove_recursive(host->debugfs_root);
+ intel_actualize_debugfs_root(false);
+}
+
static void __iomem *intel_priv(struct pci_dev *pci)
{
resource_size_t base = pci_resource_start(pci, 0);
@@ -76,11 +186,21 @@ static int intel_init(struct mipi_i3c_hci_pci *hci)
intel_reset(priv);
+ intel_ltr_expose(&hci->pci->dev);
+ intel_add_debugfs(hci);
+
return 0;
}
+static void intel_exit(struct mipi_i3c_hci_pci *hci)
+{
+ intel_remove_debugfs(hci);
+ intel_ltr_hide(&hci->pci->dev);
+}
+
static const struct mipi_i3c_hci_pci_info intel_info = {
.init = intel_init,
+ .exit = intel_exit,
};
static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,
--
2.51.0
More information about the linux-i3c
mailing list