Commit db847adb authored by Bjorn Helgaas's avatar Bjorn Helgaas
Browse files

Merge branch 'pci/ptm-debugfs'

- Add debugfs support for exposing DWC device-specific PTM context
  (Manivannan Sadhasivam)

* pci/ptm-debugfs:
  PCI: qcom-ep: Mask PTM_UPDATING interrupt
  PCI: dwc: Add debugfs support for PTM context
  PCI: dwc: Pass DWC PCIe mode to dwc_pcie_debugfs_init()
  PCI: Add debugfs support for exposing PTM context
parents df52b639 5fbfae69
Loading
Loading
Loading
Loading
+70 −0
Original line number Diff line number Diff line
What:		/sys/kernel/debug/pcie_ptm_*/local_clock
Date:		May 2025
Contact:	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Description:
		(RO) PTM local clock in nanoseconds. Applicable for both Root
		Complex and Endpoint controllers.

What:		/sys/kernel/debug/pcie_ptm_*/master_clock
Date:		May 2025
Contact:	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Description:
		(RO) PTM master clock in nanoseconds. Applicable only for
		Endpoint controllers.

What:		/sys/kernel/debug/pcie_ptm_*/t1
Date:		May 2025
Contact:	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Description:
		(RO) PTM T1 timestamp in nanoseconds. Applicable only for
		Endpoint controllers.

What:		/sys/kernel/debug/pcie_ptm_*/t2
Date:		May 2025
Contact:	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Description:
		(RO) PTM T2 timestamp in nanoseconds. Applicable only for
		Root Complex controllers.

What:		/sys/kernel/debug/pcie_ptm_*/t3
Date:		May 2025
Contact:	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Description:
		(RO) PTM T3 timestamp in nanoseconds. Applicable only for
		Root Complex controllers.

What:		/sys/kernel/debug/pcie_ptm_*/t4
Date:		May 2025
Contact:	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Description:
		(RO) PTM T4 timestamp in nanoseconds. Applicable only for
		Endpoint controllers.

What:		/sys/kernel/debug/pcie_ptm_*/context_update
Date:		May 2025
Contact:	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Description:
		(RW) Control the PTM context update mode. Applicable only for
		Endpoint controllers.

		Following values are supported:

		* auto = PTM context auto update trigger for every 10ms

		* manual = PTM context manual update. Writing 'manual' to this
			   file triggers PTM context update (default)

What:		/sys/kernel/debug/pcie_ptm_*/context_valid
Date:		May 2025
Contact:	Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
Description:
		(RW) Control the PTM context validity (local clock timing).
		Applicable only for Root Complex controllers. PTM context is
		invalidated by hardware if the Root Complex enters low power
		mode or changes link frequency.

		Following values are supported:

		* 0 = PTM context invalid (default)

		* 1 = PTM context valid
+1 −0
Original line number Diff line number Diff line
@@ -18642,6 +18642,7 @@ Q: https://patchwork.kernel.org/project/linux-pci/list/
B:	https://bugzilla.kernel.org
C:	irc://irc.oftc.net/linux-pci
T:	git git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci.git
F:	Documentation/ABI/testing/debugfs-pcie-ptm
F:	Documentation/devicetree/bindings/pci/
F:	drivers/pci/controller/
F:	drivers/pci/pci-bridge-emul.c
+251 −1
Original line number Diff line number Diff line
@@ -642,16 +642,262 @@ static void dwc_pcie_ltssm_debugfs_init(struct dw_pcie *pci, struct dentry *dir)
			    &dwc_pcie_ltssm_status_ops);
}

static int dw_pcie_ptm_check_capability(void *drvdata)
{
	struct dw_pcie *pci = drvdata;

	pci->ptm_vsec_offset = dw_pcie_find_ptm_capability(pci);

	return pci->ptm_vsec_offset;
}

static int dw_pcie_ptm_context_update_write(void *drvdata, u8 mode)
{
	struct dw_pcie *pci = drvdata;
	u32 val;

	if (mode == PCIE_PTM_CONTEXT_UPDATE_AUTO) {
		val = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL);
		val |= PTM_REQ_AUTO_UPDATE_ENABLED;
		dw_pcie_writel_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL, val);
	} else if (mode == PCIE_PTM_CONTEXT_UPDATE_MANUAL) {
		val = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL);
		val &= ~PTM_REQ_AUTO_UPDATE_ENABLED;
		val |= PTM_REQ_START_UPDATE;
		dw_pcie_writel_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL, val);
	} else {
		return -EINVAL;
	}

	return 0;
}

static int dw_pcie_ptm_context_update_read(void *drvdata, u8 *mode)
{
	struct dw_pcie *pci = drvdata;
	u32 val;

	val = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL);
	if (FIELD_GET(PTM_REQ_AUTO_UPDATE_ENABLED, val))
		*mode = PCIE_PTM_CONTEXT_UPDATE_AUTO;
	else
		/*
		 * PTM_REQ_START_UPDATE is a self clearing register bit. So if
		 * PTM_REQ_AUTO_UPDATE_ENABLED is not set, then it implies that
		 * manual update is used.
		 */
		*mode = PCIE_PTM_CONTEXT_UPDATE_MANUAL;

	return 0;
}

static int dw_pcie_ptm_context_valid_write(void *drvdata, bool valid)
{
	struct dw_pcie *pci = drvdata;
	u32 val;

	if (valid) {
		val = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL);
		val |= PTM_RES_CCONTEXT_VALID;
		dw_pcie_writel_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL, val);
	} else {
		val = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL);
		val &= ~PTM_RES_CCONTEXT_VALID;
		dw_pcie_writel_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL, val);
	}

	return 0;
}

static int dw_pcie_ptm_context_valid_read(void *drvdata, bool *valid)
{
	struct dw_pcie *pci = drvdata;
	u32 val;

	val = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_RES_REQ_CTRL);
	*valid = !!FIELD_GET(PTM_RES_CCONTEXT_VALID, val);

	return 0;
}

static int dw_pcie_ptm_local_clock_read(void *drvdata, u64 *clock)
{
	struct dw_pcie *pci = drvdata;
	u32 msb, lsb;

	do {
		msb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_LOCAL_MSB);
		lsb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_LOCAL_LSB);
	} while (msb != dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_LOCAL_MSB));

	*clock = ((u64) msb) << 32 | lsb;

	return 0;
}

static int dw_pcie_ptm_master_clock_read(void *drvdata, u64 *clock)
{
	struct dw_pcie *pci = drvdata;
	u32 msb, lsb;

	do {
		msb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_MASTER_MSB);
		lsb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_MASTER_LSB);
	} while (msb != dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_MASTER_MSB));

	*clock = ((u64) msb) << 32 | lsb;

	return 0;
}

static int dw_pcie_ptm_t1_read(void *drvdata, u64 *clock)
{
	struct dw_pcie *pci = drvdata;
	u32 msb, lsb;

	do {
		msb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T1_T2_MSB);
		lsb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T1_T2_LSB);
	} while (msb != dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T1_T2_MSB));

	*clock = ((u64) msb) << 32 | lsb;

	return 0;
}

static int dw_pcie_ptm_t2_read(void *drvdata, u64 *clock)
{
	struct dw_pcie *pci = drvdata;
	u32 msb, lsb;

	do {
		msb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T1_T2_MSB);
		lsb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T1_T2_LSB);
	} while (msb != dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T1_T2_MSB));

	*clock = ((u64) msb) << 32 | lsb;

	return 0;
}

static int dw_pcie_ptm_t3_read(void *drvdata, u64 *clock)
{
	struct dw_pcie *pci = drvdata;
	u32 msb, lsb;

	do {
		msb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T3_T4_MSB);
		lsb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T3_T4_LSB);
	} while (msb != dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T3_T4_MSB));

	*clock = ((u64) msb) << 32 | lsb;

	return 0;
}

static int dw_pcie_ptm_t4_read(void *drvdata, u64 *clock)
{
	struct dw_pcie *pci = drvdata;
	u32 msb, lsb;

	do {
		msb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T3_T4_MSB);
		lsb = dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T3_T4_LSB);
	} while (msb != dw_pcie_readl_dbi(pci, pci->ptm_vsec_offset + PTM_T3_T4_MSB));

	*clock = ((u64) msb) << 32 | lsb;

	return 0;
}

static bool dw_pcie_ptm_context_update_visible(void *drvdata)
{
	struct dw_pcie *pci = drvdata;

	return (pci->mode == DW_PCIE_EP_TYPE) ? true : false;
}

static bool dw_pcie_ptm_context_valid_visible(void *drvdata)
{
	struct dw_pcie *pci = drvdata;

	return (pci->mode == DW_PCIE_RC_TYPE) ? true : false;
}

static bool dw_pcie_ptm_local_clock_visible(void *drvdata)
{
	/* PTM local clock is always visible */
	return true;
}

static bool dw_pcie_ptm_master_clock_visible(void *drvdata)
{
	struct dw_pcie *pci = drvdata;

	return (pci->mode == DW_PCIE_EP_TYPE) ? true : false;
}

static bool dw_pcie_ptm_t1_visible(void *drvdata)
{
	struct dw_pcie *pci = drvdata;

	return (pci->mode == DW_PCIE_EP_TYPE) ? true : false;
}

static bool dw_pcie_ptm_t2_visible(void *drvdata)
{
	struct dw_pcie *pci = drvdata;

	return (pci->mode == DW_PCIE_RC_TYPE) ? true : false;
}

static bool dw_pcie_ptm_t3_visible(void *drvdata)
{
	struct dw_pcie *pci = drvdata;

	return (pci->mode == DW_PCIE_RC_TYPE) ? true : false;
}

static bool dw_pcie_ptm_t4_visible(void *drvdata)
{
	struct dw_pcie *pci = drvdata;

	return (pci->mode == DW_PCIE_EP_TYPE) ? true : false;
}

const struct pcie_ptm_ops dw_pcie_ptm_ops = {
	.check_capability = dw_pcie_ptm_check_capability,
	.context_update_write = dw_pcie_ptm_context_update_write,
	.context_update_read = dw_pcie_ptm_context_update_read,
	.context_valid_write = dw_pcie_ptm_context_valid_write,
	.context_valid_read = dw_pcie_ptm_context_valid_read,
	.local_clock_read = dw_pcie_ptm_local_clock_read,
	.master_clock_read = dw_pcie_ptm_master_clock_read,
	.t1_read = dw_pcie_ptm_t1_read,
	.t2_read = dw_pcie_ptm_t2_read,
	.t3_read = dw_pcie_ptm_t3_read,
	.t4_read = dw_pcie_ptm_t4_read,
	.context_update_visible = dw_pcie_ptm_context_update_visible,
	.context_valid_visible = dw_pcie_ptm_context_valid_visible,
	.local_clock_visible = dw_pcie_ptm_local_clock_visible,
	.master_clock_visible = dw_pcie_ptm_master_clock_visible,
	.t1_visible = dw_pcie_ptm_t1_visible,
	.t2_visible = dw_pcie_ptm_t2_visible,
	.t3_visible = dw_pcie_ptm_t3_visible,
	.t4_visible = dw_pcie_ptm_t4_visible,
};

void dwc_pcie_debugfs_deinit(struct dw_pcie *pci)
{
	if (!pci->debugfs)
		return;

	pcie_ptm_destroy_debugfs(pci->ptm_debugfs);
	dwc_pcie_rasdes_debugfs_deinit(pci);
	debugfs_remove_recursive(pci->debugfs->debug_dir);
}

void dwc_pcie_debugfs_init(struct dw_pcie *pci)
void dwc_pcie_debugfs_init(struct dw_pcie *pci, enum dw_pcie_device_mode mode)
{
	char dirname[DWC_DEBUGFS_BUF_MAX];
	struct device *dev = pci->dev;
@@ -674,4 +920,8 @@ void dwc_pcie_debugfs_init(struct dw_pcie *pci)
			err);

	dwc_pcie_ltssm_debugfs_init(pci, dir);

	pci->mode = mode;
	pci->ptm_debugfs = pcie_ptm_create_debugfs(pci->dev, pci,
						   &dw_pcie_ptm_ops);
}
+1 −1
Original line number Diff line number Diff line
@@ -1013,7 +1013,7 @@ int dw_pcie_ep_init_registers(struct dw_pcie_ep *ep)

	dw_pcie_ep_init_non_sticky_registers(pci);

	dwc_pcie_debugfs_init(pci);
	dwc_pcie_debugfs_init(pci, DW_PCIE_EP_TYPE);

	return 0;

+1 −1
Original line number Diff line number Diff line
@@ -574,7 +574,7 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp)
	if (pp->ops->post_init)
		pp->ops->post_init(pp);

	dwc_pcie_debugfs_init(pci);
	dwc_pcie_debugfs_init(pci, DW_PCIE_RC_TYPE);

	return 0;

Loading