[PATCH 2/3] PCI: cadence: Use DT bindings to set PHY latencies

From: Dominic Rath
Date: Thu Oct 13 2022 - 02:45:21 EST


From: Alexander Bahle <bahle@xxxxxxxxxxxxxxx>

Use optional "cdns,tx-phy-latency-ps" and "cdns,rx-phy-latency-ps"
DeviceTree bindings to set the CDNS_PCIE_LM_PTM_LAT_PARAM(_IDX)
register(s) during PCIe host and ep setup.
The properties expect a list of uint32 PHY latencies in picoseconds for
every supported speed starting at PCIe Gen1, e.g.:

max-link-speed = <2>;
tx-phy-latency-ps = <100000 200000>; /* Gen1: 100ns, Gen2: 200ns */
rx-phy-latency-ps = <150000 250000>; /* Gen1: 150ns, Gen2: 250ns */

There should be a value for every supported speed but it is not enforced or
necessary. A warning is emitted to let users know that the PTM timestamps
from this PCIe device may not be precise enough for some applications.

Signed-off-by: Alexander Bahle <bahle@xxxxxxxxxxxxxxx>
Signed-off-by: Dominic Rath <rath@xxxxxxxxxxxxxxx>
---
.../pci/controller/cadence/pcie-cadence-ep.c | 2 +
.../controller/cadence/pcie-cadence-host.c | 1 +
drivers/pci/controller/cadence/pcie-cadence.c | 81 +++++++++++++++++++
drivers/pci/controller/cadence/pcie-cadence.h | 23 ++++++
4 files changed, 107 insertions(+)

diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c
index b8b655d4047e..6e39126922d1 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-ep.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c
@@ -664,6 +664,8 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
}
pcie->mem_res = res;

+ cdns_pcie_init_ptm_phy_latency(dev, pcie);
+
ep->max_regions = CDNS_PCIE_MAX_OB;
of_property_read_u32(np, "cdns,max-outbound-regions", &ep->max_regions);

diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c
index 940c7dd701d6..8933002f828e 100644
--- a/drivers/pci/controller/cadence/pcie-cadence-host.c
+++ b/drivers/pci/controller/cadence/pcie-cadence-host.c
@@ -510,6 +510,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc)
cdns_pcie_detect_quiet_min_delay_set(&rc->pcie);

cdns_pcie_host_enable_ptm_response(pcie);
+ cdns_pcie_init_ptm_phy_latency(dev, pcie);

ret = cdns_pcie_start_link(pcie);
if (ret) {
diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c
index 13c4032ca379..f0370a10640b 100644
--- a/drivers/pci/controller/cadence/pcie-cadence.c
+++ b/drivers/pci/controller/cadence/pcie-cadence.c
@@ -5,8 +5,89 @@

#include <linux/kernel.h>

+#include "../../pci.h"
#include "pcie-cadence.h"

+void cdns_pcie_set_ptm_phy_latency_param(struct cdns_pcie *pcie, bool rx,
+ u32 speed_index, u32 latency)
+{
+ u32 val;
+
+ /* Set the speed index */
+ val = cdns_pcie_readl(pcie, CDNS_PCIE_LM_PTM_LAT_PARAM_IDX);
+ val = ((val & ~CDNS_PCIE_LM_PTM_LAT_PARAM_IDX_PTMLATIN_MASK) |
+ CDNS_PCIE_LM_PTM_LAT_PARAM_IDX_PTMLATIN(speed_index));
+ cdns_pcie_writel(pcie, CDNS_PCIE_LM_PTM_LAT_PARAM_IDX, val);
+
+ val = cdns_pcie_readl(pcie, CDNS_PCIE_LM_PTM_LAT_PARAM);
+ if (rx) {
+ /* Set the RX direction latency */
+ val = ((val & ~CDNS_PCIE_LM_PTM_LAT_PARAM_PTMRXLAT_MASK) |
+ CDNS_PCIE_LM_PTM_LAT_PARAM_PTMRXLAT(latency));
+ } else {
+ /* Set TX direction latency */
+ val = ((val & ~CDNS_PCIE_LM_PTM_LAT_PARAM_PTMTXLAT_MASK) |
+ CDNS_PCIE_LM_PTM_LAT_PARAM_PTMTXLAT(latency));
+ }
+ cdns_pcie_writel(pcie, CDNS_PCIE_LM_PTM_LAT_PARAM, val);
+}
+
+static int cdns_pcie_set_ptm_phy_latency(struct device *dev, struct cdns_pcie *pcie,
+ bool rx, const char *key)
+{
+ struct device_node *np = dev->of_node;
+ int max_link_speed;
+ int param_count;
+ u32 latency;
+ int i;
+
+ max_link_speed = of_pci_get_max_link_speed(np);
+ if (max_link_speed < 1)
+ return -EINVAL;
+
+ param_count = of_property_count_u32_elems(np, key);
+ if (param_count < 0 || param_count < max_link_speed) {
+ dev_warn(dev,
+ "no %s set for one or more speeds: %d\n",
+ key, param_count);
+ }
+
+ /* Don't set param for unsupported speed */
+ if (param_count > max_link_speed)
+ param_count = max_link_speed;
+
+ for (i = 0; i < param_count; i++) {
+ if (of_property_read_u32_index(np, key, i,
+ &latency) < 0) {
+ dev_err(dev, "failed to set latency for speed %d. %s\n",
+ i, key);
+ return -EINVAL;
+ }
+
+ /* convert ps to ns */
+ latency /= 1000;
+
+ cdns_pcie_set_ptm_phy_latency_param(pcie, rx,
+ i, latency);
+ }
+
+ return 0;
+}
+
+int cdns_pcie_init_ptm_phy_latency(struct device *dev, struct cdns_pcie *pcie)
+{
+ int ret;
+
+ ret = cdns_pcie_set_ptm_phy_latency(dev, pcie, false,
+ "cdns,tx-phy-latency-ps");
+ if (ret)
+ return ret;
+
+ ret = cdns_pcie_set_ptm_phy_latency(dev, pcie, true,
+ "cdns,rx-phy-latency-ps");
+ return ret;
+}
+
void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie)
{
u32 delay = 0x3;
diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h
index 190786e47df9..483b957a8212 100644
--- a/drivers/pci/controller/cadence/pcie-cadence.h
+++ b/drivers/pci/controller/cadence/pcie-cadence.h
@@ -120,6 +120,26 @@
#define CDNS_PCIE_LM_PTM_CTRL (CDNS_PCIE_LM_BASE + 0x0da8)
#define CDNS_PCIE_LM_TPM_CTRL_PTMRSEN BIT(17)

+/* PTM Latency Parameters Index Register */
+#define CDNS_PCIE_LM_PTM_LAT_PARAM_IDX \
+ (CDNS_PCIE_LM_BASE + 0x0db0)
+#define CDNS_PCIE_LM_PTM_LAT_PARAM_IDX_PTMLATIN_MASK \
+ GENMASK(3, 0)
+#define CDNS_PCIE_LM_PTM_LAT_PARAM_IDX_PTMLATIN(a) \
+ (((a) << 0) & CDNS_PCIE_LM_PTM_LAT_PARAM_IDX_PTMLATIN_MASK)
+
+/* PTM Latency Parameters Register */
+#define CDNS_PCIE_LM_PTM_LAT_PARAM \
+ (CDNS_PCIE_LM_BASE + 0x0db4)
+#define CDNS_PCIE_LM_PTM_LAT_PARAM_PTMTXLAT_MASK \
+ GENMASK(9, 0)
+#define CDNS_PCIE_LM_PTM_LAT_PARAM_PTMTXLAT(a) \
+ (((a) << 0) & CDNS_PCIE_LM_PTM_LAT_PARAM_PTMTXLAT_MASK)
+#define CDNS_PCIE_LM_PTM_LAT_PARAM_PTMRXLAT_MASK \
+ GENMASK(19, 10)
+#define CDNS_PCIE_LM_PTM_LAT_PARAM_PTMRXLAT(b) \
+ (((b) << 10) & CDNS_PCIE_LM_PTM_LAT_PARAM_PTMRXLAT_MASK)
+
/*
* Endpoint Function Registers (PCI configuration space for endpoint functions)
*/
@@ -541,6 +561,9 @@ static inline int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep)
#endif

void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie);
+void cdns_pcie_set_ptm_phy_latency_param(struct cdns_pcie *pcie, bool rx,
+ u32 speed_index, u32 latency);
+int cdns_pcie_init_ptm_phy_latency(struct device *dev, struct cdns_pcie *pcie);

void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn,
u32 r, bool is_io,
--
2.36.0