Commit 884a3313 authored by Adrian Hunter's avatar Adrian Hunter Committed by Alexandre Belloni
Browse files

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: default avatarAdrian Hunter <adrian.hunter@intel.com>
Reviewed-by: default avatarFrank Li <Frank.Li@nxp.com>
Link: https://patch.msgid.link/20251128064038.55158-12-adrian.hunter@intel.com


Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 040dcd76
Loading
Loading
Loading
Loading
+112 −1
Original line number Diff line number Diff line
@@ -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,