i3c: mipi-i3c-hci-pci: Add LTR support for Intel controllers

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@intel.com>
Reviewed-by: Frank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20251128064038.55158-12-adrian.hunter@intel.com
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
This commit is contained in:
Adrian Hunter 2025-11-28 08:40:37 +02:00 committed by Alexandre Belloni
parent 040dcd762d
commit 884a33131f

View File

@ -7,17 +7,21 @@
* Author: Jarkko Nikula <jarkko.nikula@linux.intel.com>
*/
#include <linux/acpi.h>
#include <linux/bitfield.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;
struct platform_device *pdev;
const struct mipi_i3c_hci_pci_info *info;
void *private;
};
struct mipi_i3c_hci_pci_info {
@ -34,6 +38,99 @@ static DEFINE_IDA(mipi_i3c_hci_pci_ida);
#define INTEL_RESETS_RESET_DONE BIT(1)
#define INTEL_RESETS_TIMEOUT_US (10 * USEC_PER_MSEC)
#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 FIELD_PREP(INTEL_LTR_SCALE_MASK, 2)
#define INTEL_LTR_SCALE_32US FIELD_PREP(INTEL_LTR_SCALE_MASK, 3)
#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) {
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)
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 void intel_add_debugfs(struct mipi_i3c_hci_pci *hci)
{
struct dentry *dir = debugfs_create_dir(dev_name(&hci->pci->dev), NULL);
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);
}
static void intel_reset(void __iomem *priv)
{
u32 reg;
@ -55,20 +152,34 @@ static void __iomem *intel_priv(struct pci_dev *pci)
static int intel_i3c_init(struct mipi_i3c_hci_pci *hci)
{
struct intel_host *host = devm_kzalloc(&hci->pci->dev, sizeof(*host), GFP_KERNEL);
void __iomem *priv = intel_priv(hci->pci);
if (!priv)
if (!host || !priv)
return -ENOMEM;
dma_set_mask_and_coherent(&hci->pci->dev, DMA_BIT_MASK(64));
hci->private = host;
host->priv = priv;
intel_reset(priv);
intel_ltr_expose(&hci->pci->dev);
intel_add_debugfs(hci);
return 0;
}
static void intel_i3c_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_i3c_init,
.exit = intel_i3c_exit,
};
static int mipi_i3c_hci_pci_probe(struct pci_dev *pci,