* [PATCH v2 0/3] PCI: qcom: Program T_POWER_ON value for L1.2 exit timing
@ 2026-02-23 11:13 Krishna Chaitanya Chundru
2026-02-23 11:13 ` [PATCH v2 1/3] PCI/ASPM: Add helper to encode L1SS T_POWER_ON fields Krishna Chaitanya Chundru
` (2 more replies)
0 siblings, 3 replies; 12+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-02-23 11:13 UTC (permalink / raw)
To: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han
Cc: linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan, Krishna Chaitanya Chundru
The T_POWER_ON indicates the time (in μs) that a Port requires the port
on the opposite side of Link to wait in L1.2.Exit after sampling CLKREQ#
asserted before actively driving the interface. This value is used by
the ASPM driver to compute the LTR_L1.2_THRESHOLD.
Currently, qcom root port exposes T_POWER_ON value of zero in the L1SS
capability registers, leading to incorrect LTR_L1.2_THRESHOLD calculations,
which can result in improper L1.2 exit behavior and can trigger AER's.
In this series, qcom controller drivers read the devicetree property
"t-power-on" which got merged recently[1], and use that value to over
write default/wrong value.
To convert T_POWER_ON in to T_POWER_ON_SCALE & T_POWER_ON_VALUE created
a pcie_encode_t_power_on() helper in aspm.c and also created
dw_pcie_program_t_power_on() helper for other drivers to use these
helpers.
Link [1]: https://lore.kernel.org/all/20260205093346.667898-1-krishna.chundru@oss.qualcomm.com/
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
Changes in v2:
- Instead of hard coding the values in the driver, created a devicetree
property "t-power-on" to program it (Bjorn & Mani).
- Link to v1: https://lore.kernel.org/r/20251104-t_power_on_fux-v1-1-eb5916e47fd7@oss.qualcomm.com
---
Krishna Chaitanya Chundru (3):
PCI/ASPM: Add helper to encode L1SS T_POWER_ON fields
PCI: dwc: Add helper to Program T_POWER_ON
PCI: qcom: Program T_POWER_ON
drivers/pci/controller/dwc/pcie-designware.c | 27 +++++++++++++++++
drivers/pci/controller/dwc/pcie-designware.h | 1 +
drivers/pci/controller/dwc/pcie-qcom.c | 15 ++++++++++
drivers/pci/pcie/aspm.c | 43 ++++++++++++++++++++++++++++
include/linux/pci.h | 2 ++
5 files changed, 88 insertions(+)
---
base-commit: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
change-id: 20251104-t_power_on_fux-70dc68377941
Best regards,
--
Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
^ permalink raw reply [flat|nested] 12+ messages in thread
* [PATCH v2 1/3] PCI/ASPM: Add helper to encode L1SS T_POWER_ON fields
2026-02-23 11:13 [PATCH v2 0/3] PCI: qcom: Program T_POWER_ON value for L1.2 exit timing Krishna Chaitanya Chundru
@ 2026-02-23 11:13 ` Krishna Chaitanya Chundru
2026-02-23 15:29 ` Bjorn Helgaas
2026-02-23 11:13 ` [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON Krishna Chaitanya Chundru
2026-02-23 11:13 ` [PATCH v2 3/3] PCI: qcom: " Krishna Chaitanya Chundru
2 siblings, 1 reply; 12+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-02-23 11:13 UTC (permalink / raw)
To: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han
Cc: linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan, Krishna Chaitanya Chundru
Add a shared helper to encode the PCIe L1 PM Substates T_POWER_ON
parameter into the T_POWER_ON_Scale and T_POWER_ON_Value fields.
This helper can be used by the controller drivers to change the
default/wrong value of T_POWER_ON in L1ss capability register to
avoid incorrect calculation of LTR_L1.2_THRESHOLD value.
The helper converts a T_POWER_ON time specified in microseconds into
the appropriate scale/value encoding defined by the PCIe spec r7.0,
sec 7.8.3.2. Values that exceed the maximum encodable range are clamped
to the largest representable encoding.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/pci/pcie/aspm.c | 43 +++++++++++++++++++++++++++++++++++++++++++
include/linux/pci.h | 2 ++
2 files changed, 45 insertions(+)
diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
index 21f5d23e0b61bd7e1163cc869fe9356d1ab87b34..d7f9ae9e48c25dbc2d9b4887e2f74623688098e0 100644
--- a/drivers/pci/pcie/aspm.c
+++ b/drivers/pci/pcie/aspm.c
@@ -525,6 +525,49 @@ static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val)
return 0;
}
+/**
+ * pcie_encode_t_power_on - Encode T_POWER_ON into scale and value fields
+ * @t_power_on_us: T_POWER_ON time in microseconds
+ * @scale: Encoded T_POWER_ON_Scale (0..2)
+ * @value: Encoded T_POWER_ON_Value
+ *
+ * T_POWER_ON is encoded as:
+ * T_POWER_ON(us) = scale_unit(us) * value
+ *
+ * where scale_unit is selected by @scale:
+ * 0: 2us
+ * 1: 10us
+ * 2: 100us
+ *
+ * If @t_power_on_us exceeds the maximum representable value, the result
+ * is clamped to the largest encodable T_POWER_ON.
+ *
+ * See PCIe r7.0, sec 7.8.3.2.
+ */
+void pcie_encode_t_power_on(u16 t_power_on_us, u8 *scale, u8 *value)
+{
+ u8 maxv = FIELD_MAX(PCI_L1SS_CTL2_T_PWR_ON_VALUE);
+
+ /*
+ * T_POWER_ON_Value ("value") is a 5-bit field with max
+ * value of 31.
+ */
+ if (t_power_on_us <= 2 * maxv) {
+ *scale = 0; /* Value times 2us */
+ *value = DIV_ROUND_UP(t_power_on_us, 2);
+ } else if (t_power_on_us <= 10 * maxv) {
+ *scale = 1; /* Value times 10us */
+ *value = DIV_ROUND_UP(t_power_on_us, 10);
+ } else if (t_power_on_us <= 100 * maxv) {
+ *scale = 2; /* value times 100us */
+ *value = DIV_ROUND_UP(t_power_on_us, 100);
+ } else {
+ *scale = 2;
+ *value = maxv;
+ }
+}
+EXPORT_SYMBOL(pcie_encode_t_power_on);
+
/*
* Encode an LTR_L1.2_THRESHOLD value for the L1 PM Substates Control 1
* register. Ports enter L1.2 when the most recent LTR value is greater
diff --git a/include/linux/pci.h b/include/linux/pci.h
index 1c270f1d512301de4d462fe7e5097c32af5c6f8d..eec16fdcb9996ab0f663f4587a2367a676a49ce6 100644
--- a/include/linux/pci.h
+++ b/include/linux/pci.h
@@ -1911,6 +1911,7 @@ int pci_enable_link_state_locked(struct pci_dev *pdev, int state);
void pcie_no_aspm(void);
bool pcie_aspm_support_enabled(void);
bool pcie_aspm_enabled(struct pci_dev *pdev);
+void pcie_encode_t_power_on(u16 t_power_on_us, u8 *scale, u8 *value);
#else
static inline int pci_disable_link_state(struct pci_dev *pdev, int state)
{ return 0; }
@@ -1923,6 +1924,7 @@ static inline int pci_enable_link_state_locked(struct pci_dev *pdev, int state)
static inline void pcie_no_aspm(void) { }
static inline bool pcie_aspm_support_enabled(void) { return false; }
static inline bool pcie_aspm_enabled(struct pci_dev *pdev) { return false; }
+static inline void pcie_encode_t_power_on(u16 t_power_on_us, u8 *scale, u8 *value) { }
#endif
#ifdef CONFIG_HOTPLUG_PCI
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON
2026-02-23 11:13 [PATCH v2 0/3] PCI: qcom: Program T_POWER_ON value for L1.2 exit timing Krishna Chaitanya Chundru
2026-02-23 11:13 ` [PATCH v2 1/3] PCI/ASPM: Add helper to encode L1SS T_POWER_ON fields Krishna Chaitanya Chundru
@ 2026-02-23 11:13 ` Krishna Chaitanya Chundru
2026-02-23 15:38 ` Bjorn Helgaas
2026-02-23 23:33 ` kernel test robot
2026-02-23 11:13 ` [PATCH v2 3/3] PCI: qcom: " Krishna Chaitanya Chundru
2 siblings, 2 replies; 12+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-02-23 11:13 UTC (permalink / raw)
To: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han
Cc: linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan, Krishna Chaitanya Chundru
The T_POWER_ON indicates the time (in μs) that a Port requires the port
on the opposite side of Link to wait in L1.2.Exit after sampling CLKREQ#
asserted before actively driving the interface. This value is used by
the ASPM driver to compute the LTR_L1.2_THRESHOLD.
Currently, some controllers exposes T_POWER_ON value of zero in the L1SS
capability registers, leading to incorrect LTR_L1.2_THRESHOLD calculations,
which can result in improper L1.2 exit behavior and can trigger AER's.
Add a helper to override T_POWER_ON value by the DWC controller drivers.
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
---
drivers/pci/controller/dwc/pcie-designware.c | 27 +++++++++++++++++++++++++++
drivers/pci/controller/dwc/pcie-designware.h | 1 +
2 files changed, 28 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
index 5741c09dde7f40487c6da6dfd66f7c8d96a74259..f56e2c07ddc57bd84882c14bebc7d4b4961f601a 100644
--- a/drivers/pci/controller/dwc/pcie-designware.c
+++ b/drivers/pci/controller/dwc/pcie-designware.c
@@ -1249,6 +1249,33 @@ void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)
dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap);
}
+/* TODO: Need to handle multi root ports */
+void dw_pcie_program_t_power_on(struct dw_pcie *pci, u16 t_power_on)
+{
+ u8 scale, value;
+ u16 offset;
+ u32 val;
+
+ if (!t_power_on)
+ return;
+
+ offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
+ if (offset) {
+ pcie_encode_t_power_on(t_power_on, &scale, &value);
+
+ dw_pcie_dbi_ro_wr_en(pci);
+
+ val = readl(pci->dbi_base + offset + PCI_L1SS_CAP);
+ val &= ~(PCI_L1SS_CAP_P_PWR_ON_SCALE | PCI_L1SS_CAP_P_PWR_ON_VALUE);
+ val |= FIELD_PREP(PCI_L1SS_CAP_P_PWR_ON_SCALE, scale);
+ val |= FIELD_PREP(PCI_L1SS_CAP_P_PWR_ON_VALUE, value);
+
+ writel(val, pci->dbi_base + offset + PCI_L1SS_CAP);
+
+ dw_pcie_dbi_ro_wr_dis(pci);
+ }
+}
+
void dw_pcie_setup(struct dw_pcie *pci)
{
u32 val;
diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
index ae6389dd9caa5c27690f998d58729130ea863984..da67beece3f11e33d9a1937fa23d443feea3bbc7 100644
--- a/drivers/pci/controller/dwc/pcie-designware.h
+++ b/drivers/pci/controller/dwc/pcie-designware.h
@@ -602,6 +602,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
u8 bar, size_t size);
void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci);
+void dw_pcie_program_t_power_on(struct dw_pcie *pci, u16 t_power_on);
void dw_pcie_setup(struct dw_pcie *pci);
void dw_pcie_iatu_detect(struct dw_pcie *pci);
int dw_pcie_edma_detect(struct dw_pcie *pci);
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* [PATCH v2 3/3] PCI: qcom: Program T_POWER_ON
2026-02-23 11:13 [PATCH v2 0/3] PCI: qcom: Program T_POWER_ON value for L1.2 exit timing Krishna Chaitanya Chundru
2026-02-23 11:13 ` [PATCH v2 1/3] PCI/ASPM: Add helper to encode L1SS T_POWER_ON fields Krishna Chaitanya Chundru
2026-02-23 11:13 ` [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON Krishna Chaitanya Chundru
@ 2026-02-23 11:13 ` Krishna Chaitanya Chundru
2026-02-23 13:57 ` Shawn Lin
2026-02-23 15:47 ` Bjorn Helgaas
2 siblings, 2 replies; 12+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-02-23 11:13 UTC (permalink / raw)
To: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han
Cc: linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan, Krishna Chaitanya Chundru
Some platforms have incorrect T_POWER_ON value programmed in hardware.
Generally these will be corrected by bootloaders, but not all targets
support bootloaders to program correct values due to that
LTR_L1.2_THRESHOLD value calculated by aspm driver can be wrong, which
can result in improper L1.2 exit behavior and can trigger AER's.
Parse "t-power-on-us" property from each root port node and program them
as part of host initialization using dw_pcie_program_t_power_on() before
link training.
This property in added to the dtschema here[1].
Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
Link[1]: https://lore.kernel.org/all/20260205093346.667898-1-krishna.chundru@oss.qualcomm.com/
---
drivers/pci/controller/dwc/pcie-qcom.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
index 67a16af69ddc75fca1b123e70715e692a91a9135..489ed64c1df0fa3ed9f6b0d4c3e0bb65cfc3308e 100644
--- a/drivers/pci/controller/dwc/pcie-qcom.c
+++ b/drivers/pci/controller/dwc/pcie-qcom.c
@@ -269,6 +269,7 @@ struct qcom_pcie_perst {
struct qcom_pcie_port {
struct list_head list;
struct phy *phy;
+ u32 t_power_on;
struct list_head perst;
};
@@ -1283,6 +1284,16 @@ static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie)
return 0;
}
+static int qcom_pcie_configure_ports(struct qcom_pcie *pcie)
+{
+ struct qcom_pcie_port *port;
+
+ list_for_each_entry(port, &pcie->ports, list)
+ dw_pcie_program_t_power_on(pcie->pci, port->t_power_on);
+
+ return 0;
+}
+
static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
{
struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
@@ -1317,6 +1328,8 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX);
dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC);
+ qcom_pcie_configure_ports(pcie);
+
qcom_pcie_perst_deassert(pcie);
if (pcie->cfg->ops->config_sid) {
@@ -1759,6 +1772,8 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node
if (ret)
return ret;
+ of_property_read_u32(node, "t-power-on-us", &port->t_power_on);
+
port->phy = phy;
INIT_LIST_HEAD(&port->list);
list_add_tail(&port->list, &pcie->ports);
--
2.34.1
^ permalink raw reply related [flat|nested] 12+ messages in thread
* Re: [PATCH v2 3/3] PCI: qcom: Program T_POWER_ON
2026-02-23 11:13 ` [PATCH v2 3/3] PCI: qcom: " Krishna Chaitanya Chundru
@ 2026-02-23 13:57 ` Shawn Lin
2026-02-24 5:33 ` Krishna Chaitanya Chundru
2026-02-23 15:47 ` Bjorn Helgaas
1 sibling, 1 reply; 12+ messages in thread
From: Shawn Lin @ 2026-02-23 13:57 UTC (permalink / raw)
To: Krishna Chaitanya Chundru
Cc: shawn.lin, linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han
Hi Krishna,
在 2026/02/23 星期一 19:13, Krishna Chaitanya Chundru 写道:
> Some platforms have incorrect T_POWER_ON value programmed in hardware.
> Generally these will be corrected by bootloaders, but not all targets
> support bootloaders to program correct values due to that
> LTR_L1.2_THRESHOLD value calculated by aspm driver can be wrong, which
> can result in improper L1.2 exit behavior and can trigger AER's.
>
> Parse "t-power-on-us" property from each root port node and program them
> as part of host initialization using dw_pcie_program_t_power_on() before
> link training.
>
> This property in added to the dtschema here[1].
>
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> Link[1]: https://lore.kernel.org/all/20260205093346.667898-1-krishna.chundru@oss.qualcomm.com/
> ---
> drivers/pci/controller/dwc/pcie-qcom.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
> index 67a16af69ddc75fca1b123e70715e692a91a9135..489ed64c1df0fa3ed9f6b0d4c3e0bb65cfc3308e 100644
> --- a/drivers/pci/controller/dwc/pcie-qcom.c
> +++ b/drivers/pci/controller/dwc/pcie-qcom.c
> @@ -269,6 +269,7 @@ struct qcom_pcie_perst {
> struct qcom_pcie_port {
> struct list_head list;
> struct phy *phy;
> + u32 t_power_on;
> struct list_head perst;
> };
>
> @@ -1283,6 +1284,16 @@ static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie)
> return 0;
> }
>
> +static int qcom_pcie_configure_ports(struct qcom_pcie *pcie)
> +{
> + struct qcom_pcie_port *port;
> +
> + list_for_each_entry(port, &pcie->ports, list)
> + dw_pcie_program_t_power_on(pcie->pci, port->t_power_on);
> +
> + return 0;
> +}
> +
> static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
> {
> struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> @@ -1317,6 +1328,8 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
> dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX);
> dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC);
>
> + qcom_pcie_configure_ports(pcie);
> +
> qcom_pcie_perst_deassert(pcie);
>
> if (pcie->cfg->ops->config_sid) {
> @@ -1759,6 +1772,8 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node
> if (ret)
> return ret;
>
> + of_property_read_u32(node, "t-power-on-us", &port->t_power_on);
Your patch 2 adds a common helper in pcie-designware, but why not add
this parse as well as what the qcom_pcie_configure_ports() does, into to
the pcie-designware core, etc dw_pcie_host_init()/dw_pcie_ep_init()?
Because I don't see anything qcom specific here.
> +
> port->phy = phy;
> INIT_LIST_HEAD(&port->list);
> list_add_tail(&port->list, &pcie->ports);
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] PCI/ASPM: Add helper to encode L1SS T_POWER_ON fields
2026-02-23 11:13 ` [PATCH v2 1/3] PCI/ASPM: Add helper to encode L1SS T_POWER_ON fields Krishna Chaitanya Chundru
@ 2026-02-23 15:29 ` Bjorn Helgaas
2026-02-24 5:21 ` Krishna Chaitanya Chundru
0 siblings, 1 reply; 12+ messages in thread
From: Bjorn Helgaas @ 2026-02-23 15:29 UTC (permalink / raw)
To: Krishna Chaitanya Chundru
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han,
linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan
On Mon, Feb 23, 2026 at 04:43:30PM +0530, Krishna Chaitanya Chundru wrote:
> Add a shared helper to encode the PCIe L1 PM Substates T_POWER_ON
> parameter into the T_POWER_ON_Scale and T_POWER_ON_Value fields.
>
> This helper can be used by the controller drivers to change the
> default/wrong value of T_POWER_ON in L1ss capability register to
> avoid incorrect calculation of LTR_L1.2_THRESHOLD value.
>
> The helper converts a T_POWER_ON time specified in microseconds into
> the appropriate scale/value encoding defined by the PCIe spec r7.0,
> sec 7.8.3.2. Values that exceed the maximum encodable range are clamped
> to the largest representable encoding.
>
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> ---
> drivers/pci/pcie/aspm.c | 43 +++++++++++++++++++++++++++++++++++++++++++
> include/linux/pci.h | 2 ++
> 2 files changed, 45 insertions(+)
>
> diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
> index 21f5d23e0b61bd7e1163cc869fe9356d1ab87b34..d7f9ae9e48c25dbc2d9b4887e2f74623688098e0 100644
> --- a/drivers/pci/pcie/aspm.c
> +++ b/drivers/pci/pcie/aspm.c
> @@ -525,6 +525,49 @@ static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val)
> return 0;
> }
>
> +/**
> + * pcie_encode_t_power_on - Encode T_POWER_ON into scale and value fields
> + * @t_power_on_us: T_POWER_ON time in microseconds
> + * @scale: Encoded T_POWER_ON_Scale (0..2)
> + * @value: Encoded T_POWER_ON_Value
> + *
> + * T_POWER_ON is encoded as:
> + * T_POWER_ON(us) = scale_unit(us) * value
> + *
> + * where scale_unit is selected by @scale:
> + * 0: 2us
> + * 1: 10us
> + * 2: 100us
> + *
> + * If @t_power_on_us exceeds the maximum representable value, the result
> + * is clamped to the largest encodable T_POWER_ON.
> + *
> + * See PCIe r7.0, sec 7.8.3.2.
> + */
> +void pcie_encode_t_power_on(u16 t_power_on_us, u8 *scale, u8 *value)
> +{
> + u8 maxv = FIELD_MAX(PCI_L1SS_CTL2_T_PWR_ON_VALUE);
> +
> + /*
> + * T_POWER_ON_Value ("value") is a 5-bit field with max
> + * value of 31.
> + */
> + if (t_power_on_us <= 2 * maxv) {
> + *scale = 0; /* Value times 2us */
> + *value = DIV_ROUND_UP(t_power_on_us, 2);
> + } else if (t_power_on_us <= 10 * maxv) {
> + *scale = 1; /* Value times 10us */
> + *value = DIV_ROUND_UP(t_power_on_us, 10);
> + } else if (t_power_on_us <= 100 * maxv) {
> + *scale = 2; /* value times 100us */
> + *value = DIV_ROUND_UP(t_power_on_us, 100);
> + } else {
> + *scale = 2;
> + *value = maxv;
> + }
> +}
> +EXPORT_SYMBOL(pcie_encode_t_power_on);
> +
> /*
> * Encode an LTR_L1.2_THRESHOLD value for the L1 PM Substates Control 1
> * register. Ports enter L1.2 when the most recent LTR value is greater
> diff --git a/include/linux/pci.h b/include/linux/pci.h
> index 1c270f1d512301de4d462fe7e5097c32af5c6f8d..eec16fdcb9996ab0f663f4587a2367a676a49ce6 100644
> --- a/include/linux/pci.h
> +++ b/include/linux/pci.h
> @@ -1911,6 +1911,7 @@ int pci_enable_link_state_locked(struct pci_dev *pdev, int state);
> void pcie_no_aspm(void);
> bool pcie_aspm_support_enabled(void);
> bool pcie_aspm_enabled(struct pci_dev *pdev);
> +void pcie_encode_t_power_on(u16 t_power_on_us, u8 *scale, u8 *value);
This looks like it should go in drivers/pci/pci.h. I don't think it's
needed outside drivers/pci/.
> #else
> static inline int pci_disable_link_state(struct pci_dev *pdev, int state)
> { return 0; }
> @@ -1923,6 +1924,7 @@ static inline int pci_enable_link_state_locked(struct pci_dev *pdev, int state)
> static inline void pcie_no_aspm(void) { }
> static inline bool pcie_aspm_support_enabled(void) { return false; }
> static inline bool pcie_aspm_enabled(struct pci_dev *pdev) { return false; }
> +static inline void pcie_encode_t_power_on(u16 t_power_on_us, u8 *scale, u8 *value) { }
> #endif
>
> #ifdef CONFIG_HOTPLUG_PCI
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON
2026-02-23 11:13 ` [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON Krishna Chaitanya Chundru
@ 2026-02-23 15:38 ` Bjorn Helgaas
2026-02-24 5:21 ` Krishna Chaitanya Chundru
2026-02-23 23:33 ` kernel test robot
1 sibling, 1 reply; 12+ messages in thread
From: Bjorn Helgaas @ 2026-02-23 15:38 UTC (permalink / raw)
To: Krishna Chaitanya Chundru
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han,
linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan
PCI: dwc: Add dw_pcie_program_t_power_on() helper for L1SS T_POWER_ON
On Mon, Feb 23, 2026 at 04:43:31PM +0530, Krishna Chaitanya Chundru wrote:
> The T_POWER_ON indicates the time (in μs) that a Port requires the port
> on the opposite side of Link to wait in L1.2.Exit after sampling CLKREQ#
> asserted before actively driving the interface. This value is used by
> the ASPM driver to compute the LTR_L1.2_THRESHOLD.
>
> Currently, some controllers exposes T_POWER_ON value of zero in the L1SS
> capability registers, leading to incorrect LTR_L1.2_THRESHOLD calculations,
> which can result in improper L1.2 exit behavior and can trigger AER's.
s/some controllers exposes/some controllers expose/
> Add a helper to override T_POWER_ON value by the DWC controller drivers.
s/a helper/dw_pcie_program_t_power_on()/
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> ---
> drivers/pci/controller/dwc/pcie-designware.c | 27 +++++++++++++++++++++++++++
> drivers/pci/controller/dwc/pcie-designware.h | 1 +
> 2 files changed, 28 insertions(+)
>
> diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
> index 5741c09dde7f40487c6da6dfd66f7c8d96a74259..f56e2c07ddc57bd84882c14bebc7d4b4961f601a 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.c
> +++ b/drivers/pci/controller/dwc/pcie-designware.c
> @@ -1249,6 +1249,33 @@ void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)
> dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap);
> }
>
> +/* TODO: Need to handle multi root ports */
> +void dw_pcie_program_t_power_on(struct dw_pcie *pci, u16 t_power_on)
> +{
> + u8 scale, value;
> + u16 offset;
> + u32 val;
> +
> + if (!t_power_on)
> + return;
> +
> + offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
> + if (offset) {
if (!offset)
return;
> + pcie_encode_t_power_on(t_power_on, &scale, &value);
> +
> + dw_pcie_dbi_ro_wr_en(pci);
> +
> + val = readl(pci->dbi_base + offset + PCI_L1SS_CAP);
> + val &= ~(PCI_L1SS_CAP_P_PWR_ON_SCALE | PCI_L1SS_CAP_P_PWR_ON_VALUE);
> + val |= FIELD_PREP(PCI_L1SS_CAP_P_PWR_ON_SCALE, scale);
> + val |= FIELD_PREP(PCI_L1SS_CAP_P_PWR_ON_VALUE, value);
FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_SCALE, ®, scale);
FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_VALUE, ®, value);
> + writel(val, pci->dbi_base + offset + PCI_L1SS_CAP);
> +
> + dw_pcie_dbi_ro_wr_dis(pci);
> + }
> +}
> +
> void dw_pcie_setup(struct dw_pcie *pci)
> {
> u32 val;
> diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
> index ae6389dd9caa5c27690f998d58729130ea863984..da67beece3f11e33d9a1937fa23d443feea3bbc7 100644
> --- a/drivers/pci/controller/dwc/pcie-designware.h
> +++ b/drivers/pci/controller/dwc/pcie-designware.h
> @@ -602,6 +602,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
> u8 bar, size_t size);
> void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
> void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci);
> +void dw_pcie_program_t_power_on(struct dw_pcie *pci, u16 t_power_on);
> void dw_pcie_setup(struct dw_pcie *pci);
> void dw_pcie_iatu_detect(struct dw_pcie *pci);
> int dw_pcie_edma_detect(struct dw_pcie *pci);
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 3/3] PCI: qcom: Program T_POWER_ON
2026-02-23 11:13 ` [PATCH v2 3/3] PCI: qcom: " Krishna Chaitanya Chundru
2026-02-23 13:57 ` Shawn Lin
@ 2026-02-23 15:47 ` Bjorn Helgaas
1 sibling, 0 replies; 12+ messages in thread
From: Bjorn Helgaas @ 2026-02-23 15:47 UTC (permalink / raw)
To: Krishna Chaitanya Chundru
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han,
linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan
Mention L1 PM Substates in the subject so we know where to look for
T_POWER_ON.
On Mon, Feb 23, 2026 at 04:43:32PM +0530, Krishna Chaitanya Chundru wrote:
> Some platforms have incorrect T_POWER_ON value programmed in hardware.
> Generally these will be corrected by bootloaders, but not all targets
> support bootloaders to program correct values due to that
> LTR_L1.2_THRESHOLD value calculated by aspm driver can be wrong, which
> can result in improper L1.2 exit behavior and can trigger AER's.
"AER" is a little bit too specific here. The actual behavior is some
PCIe error, e.g., Bad DLLP, Data Link Protol Error, etc (if you know
the actual error, please mention it here), and if AER happens to be
supported and enabled, the error may be *reported* via AER.
> Parse "t-power-on-us" property from each root port node and program them
> as part of host initialization using dw_pcie_program_t_power_on() before
> link training.
>
> This property in added to the dtschema here[1].
>
> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
> Link[1]: https://lore.kernel.org/all/20260205093346.667898-1-krishna.chundru@oss.qualcomm.com/
> ---
> drivers/pci/controller/dwc/pcie-qcom.c | 15 +++++++++++++++
> 1 file changed, 15 insertions(+)
>
> diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c
> index 67a16af69ddc75fca1b123e70715e692a91a9135..489ed64c1df0fa3ed9f6b0d4c3e0bb65cfc3308e 100644
> --- a/drivers/pci/controller/dwc/pcie-qcom.c
> +++ b/drivers/pci/controller/dwc/pcie-qcom.c
> @@ -269,6 +269,7 @@ struct qcom_pcie_perst {
> struct qcom_pcie_port {
> struct list_head list;
> struct phy *phy;
> + u32 t_power_on;
Please mention L1 PM Substates somewhere here (comment, member name,
function name?) Currently there's nothing in the code that a reader
could look up in a spec.
> struct list_head perst;
> };
>
> @@ -1283,6 +1284,16 @@ static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie)
> return 0;
> }
>
> +static int qcom_pcie_configure_ports(struct qcom_pcie *pcie)
> +{
> + struct qcom_pcie_port *port;
> +
> + list_for_each_entry(port, &pcie->ports, list)
> + dw_pcie_program_t_power_on(pcie->pci, port->t_power_on);
> +
> + return 0;
Why return a value if it's never used or checked? If we need a return
value later, we can add it then.
> +}
> +
> static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
> {
> struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
> @@ -1317,6 +1328,8 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
> dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX);
> dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC);
>
> + qcom_pcie_configure_ports(pcie);
> +
> qcom_pcie_perst_deassert(pcie);
>
> if (pcie->cfg->ops->config_sid) {
> @@ -1759,6 +1772,8 @@ static int qcom_pcie_parse_port(struct qcom_pcie *pcie, struct device_node *node
> if (ret)
> return ret;
>
> + of_property_read_u32(node, "t-power-on-us", &port->t_power_on);
> +
> port->phy = phy;
> INIT_LIST_HEAD(&port->list);
> list_add_tail(&port->list, &pcie->ports);
>
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON
2026-02-23 11:13 ` [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON Krishna Chaitanya Chundru
2026-02-23 15:38 ` Bjorn Helgaas
@ 2026-02-23 23:33 ` kernel test robot
1 sibling, 0 replies; 12+ messages in thread
From: kernel test robot @ 2026-02-23 23:33 UTC (permalink / raw)
To: Krishna Chaitanya Chundru, Manivannan Sadhasivam,
Lorenzo Pieralisi, Krzysztof Wilczyński, Rob Herring,
Bjorn Helgaas, Jingoo Han
Cc: llvm, oe-kbuild-all, linux-pci, linux-arm-msm, linux-kernel,
mayank.rana, quic_vbadigan, Krishna Chaitanya Chundru
Hi Krishna,
kernel test robot noticed the following build errors:
[auto build test ERROR on 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f]
url: https://github.com/intel-lab-lkp/linux/commits/Krishna-Chaitanya-Chundru/PCI-ASPM-Add-helper-to-encode-L1SS-T_POWER_ON-fields/20260223-192621
base: 6de23f81a5e08be8fbf5e8d7e9febc72a5b5f27f
patch link: https://lore.kernel.org/r/20260223-t_power_on_fux-v2-2-20c921262709%40oss.qualcomm.com
patch subject: [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON
config: i386-buildonly-randconfig-004-20260223 (https://download.01.org/0day-ci/archive/20260224/202602240705.ujRk6agl-lkp@intel.com/config)
compiler: clang version 20.1.8 (https://github.com/llvm/llvm-project 87f0227cb60147a26a1eeb4fb06e3b505e9c7261)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260224/202602240705.ujRk6agl-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202602240705.ujRk6agl-lkp@intel.com/
All errors (new ones prefixed by >>):
>> ld.lld: error: call to __compiletime_assert_421 marked "dontcall-error": FIELD_PREP: value too large for the field
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 1/3] PCI/ASPM: Add helper to encode L1SS T_POWER_ON fields
2026-02-23 15:29 ` Bjorn Helgaas
@ 2026-02-24 5:21 ` Krishna Chaitanya Chundru
0 siblings, 0 replies; 12+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-02-24 5:21 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han,
linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan
On 2/23/2026 8:59 PM, Bjorn Helgaas wrote:
> On Mon, Feb 23, 2026 at 04:43:30PM +0530, Krishna Chaitanya Chundru wrote:
>> Add a shared helper to encode the PCIe L1 PM Substates T_POWER_ON
>> parameter into the T_POWER_ON_Scale and T_POWER_ON_Value fields.
>>
>> This helper can be used by the controller drivers to change the
>> default/wrong value of T_POWER_ON in L1ss capability register to
>> avoid incorrect calculation of LTR_L1.2_THRESHOLD value.
>>
>> The helper converts a T_POWER_ON time specified in microseconds into
>> the appropriate scale/value encoding defined by the PCIe spec r7.0,
>> sec 7.8.3.2. Values that exceed the maximum encodable range are clamped
>> to the largest representable encoding.
>>
>> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
>> ---
>> drivers/pci/pcie/aspm.c | 43 +++++++++++++++++++++++++++++++++++++++++++
>> include/linux/pci.h | 2 ++
>> 2 files changed, 45 insertions(+)
>>
>> diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c
>> index 21f5d23e0b61bd7e1163cc869fe9356d1ab87b34..d7f9ae9e48c25dbc2d9b4887e2f74623688098e0 100644
>> --- a/drivers/pci/pcie/aspm.c
>> +++ b/drivers/pci/pcie/aspm.c
>> @@ -525,6 +525,49 @@ static u32 calc_l12_pwron(struct pci_dev *pdev, u32 scale, u32 val)
>> return 0;
>> }
>>
>> +/**
>> + * pcie_encode_t_power_on - Encode T_POWER_ON into scale and value fields
>> + * @t_power_on_us: T_POWER_ON time in microseconds
>> + * @scale: Encoded T_POWER_ON_Scale (0..2)
>> + * @value: Encoded T_POWER_ON_Value
>> + *
>> + * T_POWER_ON is encoded as:
>> + * T_POWER_ON(us) = scale_unit(us) * value
>> + *
>> + * where scale_unit is selected by @scale:
>> + * 0: 2us
>> + * 1: 10us
>> + * 2: 100us
>> + *
>> + * If @t_power_on_us exceeds the maximum representable value, the result
>> + * is clamped to the largest encodable T_POWER_ON.
>> + *
>> + * See PCIe r7.0, sec 7.8.3.2.
>> + */
>> +void pcie_encode_t_power_on(u16 t_power_on_us, u8 *scale, u8 *value)
>> +{
>> + u8 maxv = FIELD_MAX(PCI_L1SS_CTL2_T_PWR_ON_VALUE);
>> +
>> + /*
>> + * T_POWER_ON_Value ("value") is a 5-bit field with max
>> + * value of 31.
>> + */
>> + if (t_power_on_us <= 2 * maxv) {
>> + *scale = 0; /* Value times 2us */
>> + *value = DIV_ROUND_UP(t_power_on_us, 2);
>> + } else if (t_power_on_us <= 10 * maxv) {
>> + *scale = 1; /* Value times 10us */
>> + *value = DIV_ROUND_UP(t_power_on_us, 10);
>> + } else if (t_power_on_us <= 100 * maxv) {
>> + *scale = 2; /* value times 100us */
>> + *value = DIV_ROUND_UP(t_power_on_us, 100);
>> + } else {
>> + *scale = 2;
>> + *value = maxv;
>> + }
>> +}
>> +EXPORT_SYMBOL(pcie_encode_t_power_on);
>> +
>> /*
>> * Encode an LTR_L1.2_THRESHOLD value for the L1 PM Substates Control 1
>> * register. Ports enter L1.2 when the most recent LTR value is greater
>> diff --git a/include/linux/pci.h b/include/linux/pci.h
>> index 1c270f1d512301de4d462fe7e5097c32af5c6f8d..eec16fdcb9996ab0f663f4587a2367a676a49ce6 100644
>> --- a/include/linux/pci.h
>> +++ b/include/linux/pci.h
>> @@ -1911,6 +1911,7 @@ int pci_enable_link_state_locked(struct pci_dev *pdev, int state);
>> void pcie_no_aspm(void);
>> bool pcie_aspm_support_enabled(void);
>> bool pcie_aspm_enabled(struct pci_dev *pdev);
>> +void pcie_encode_t_power_on(u16 t_power_on_us, u8 *scale, u8 *value);
> This looks like it should go in drivers/pci/pci.h. I don't think it's
> needed outside drivers/pci/.
ack, I will fix in next patch.
- Krishna Chaitanya.
>> #else
>> static inline int pci_disable_link_state(struct pci_dev *pdev, int state)
>> { return 0; }
>> @@ -1923,6 +1924,7 @@ static inline int pci_enable_link_state_locked(struct pci_dev *pdev, int state)
>> static inline void pcie_no_aspm(void) { }
>> static inline bool pcie_aspm_support_enabled(void) { return false; }
>> static inline bool pcie_aspm_enabled(struct pci_dev *pdev) { return false; }
>> +static inline void pcie_encode_t_power_on(u16 t_power_on_us, u8 *scale, u8 *value) { }
>> #endif
>>
>> #ifdef CONFIG_HOTPLUG_PCI
>>
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON
2026-02-23 15:38 ` Bjorn Helgaas
@ 2026-02-24 5:21 ` Krishna Chaitanya Chundru
0 siblings, 0 replies; 12+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-02-24 5:21 UTC (permalink / raw)
To: Bjorn Helgaas
Cc: Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han,
linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan
On 2/23/2026 9:08 PM, Bjorn Helgaas wrote:
> PCI: dwc: Add dw_pcie_program_t_power_on() helper for L1SS T_POWER_ON
>
> On Mon, Feb 23, 2026 at 04:43:31PM +0530, Krishna Chaitanya Chundru wrote:
>> The T_POWER_ON indicates the time (in μs) that a Port requires the port
>> on the opposite side of Link to wait in L1.2.Exit after sampling CLKREQ#
>> asserted before actively driving the interface. This value is used by
>> the ASPM driver to compute the LTR_L1.2_THRESHOLD.
>>
>> Currently, some controllers exposes T_POWER_ON value of zero in the L1SS
>> capability registers, leading to incorrect LTR_L1.2_THRESHOLD calculations,
>> which can result in improper L1.2 exit behavior and can trigger AER's.
> s/some controllers exposes/some controllers expose/
>
>> Add a helper to override T_POWER_ON value by the DWC controller drivers.
> s/a helper/dw_pcie_program_t_power_on()/
>
>> Signed-off-by: Krishna Chaitanya Chundru <krishna.chundru@oss.qualcomm.com>
>> ---
>> drivers/pci/controller/dwc/pcie-designware.c | 27 +++++++++++++++++++++++++++
>> drivers/pci/controller/dwc/pcie-designware.h | 1 +
>> 2 files changed, 28 insertions(+)
>>
>> diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c
>> index 5741c09dde7f40487c6da6dfd66f7c8d96a74259..f56e2c07ddc57bd84882c14bebc7d4b4961f601a 100644
>> --- a/drivers/pci/controller/dwc/pcie-designware.c
>> +++ b/drivers/pci/controller/dwc/pcie-designware.c
>> @@ -1249,6 +1249,33 @@ void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci)
>> dw_pcie_writel_dbi(pci, l1ss + PCI_L1SS_CAP, l1ss_cap);
>> }
>>
>> +/* TODO: Need to handle multi root ports */
>> +void dw_pcie_program_t_power_on(struct dw_pcie *pci, u16 t_power_on)
>> +{
>> + u8 scale, value;
>> + u16 offset;
>> + u32 val;
>> +
>> + if (!t_power_on)
>> + return;
>> +
>> + offset = dw_pcie_find_ext_capability(pci, PCI_EXT_CAP_ID_L1SS);
>> + if (offset) {
> if (!offset)
> return;
>
>> + pcie_encode_t_power_on(t_power_on, &scale, &value);
>> +
>> + dw_pcie_dbi_ro_wr_en(pci);
>> +
>> + val = readl(pci->dbi_base + offset + PCI_L1SS_CAP);
>> + val &= ~(PCI_L1SS_CAP_P_PWR_ON_SCALE | PCI_L1SS_CAP_P_PWR_ON_VALUE);
>> + val |= FIELD_PREP(PCI_L1SS_CAP_P_PWR_ON_SCALE, scale);
>> + val |= FIELD_PREP(PCI_L1SS_CAP_P_PWR_ON_VALUE, value);
> FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_SCALE, ®, scale);
> FIELD_MODIFY(PCI_L1SS_CAP_P_PWR_ON_VALUE, ®, value);
ack, I will fix all the comments in next series.
- Krishna Chaitanya.
>> + writel(val, pci->dbi_base + offset + PCI_L1SS_CAP);
>> +
>> + dw_pcie_dbi_ro_wr_dis(pci);
>> + }
>> +}
>> +
>> void dw_pcie_setup(struct dw_pcie *pci)
>> {
>> u32 val;
>> diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h
>> index ae6389dd9caa5c27690f998d58729130ea863984..da67beece3f11e33d9a1937fa23d443feea3bbc7 100644
>> --- a/drivers/pci/controller/dwc/pcie-designware.h
>> +++ b/drivers/pci/controller/dwc/pcie-designware.h
>> @@ -602,6 +602,7 @@ int dw_pcie_prog_ep_inbound_atu(struct dw_pcie *pci, u8 func_no, int index,
>> u8 bar, size_t size);
>> void dw_pcie_disable_atu(struct dw_pcie *pci, u32 dir, int index);
>> void dw_pcie_hide_unsupported_l1ss(struct dw_pcie *pci);
>> +void dw_pcie_program_t_power_on(struct dw_pcie *pci, u16 t_power_on);
>> void dw_pcie_setup(struct dw_pcie *pci);
>> void dw_pcie_iatu_detect(struct dw_pcie *pci);
>> int dw_pcie_edma_detect(struct dw_pcie *pci);
>>
>> --
>> 2.34.1
>>
^ permalink raw reply [flat|nested] 12+ messages in thread
* Re: [PATCH v2 3/3] PCI: qcom: Program T_POWER_ON
2026-02-23 13:57 ` Shawn Lin
@ 2026-02-24 5:33 ` Krishna Chaitanya Chundru
0 siblings, 0 replies; 12+ messages in thread
From: Krishna Chaitanya Chundru @ 2026-02-24 5:33 UTC (permalink / raw)
To: Shawn Lin
Cc: linux-pci, linux-arm-msm, linux-kernel, mayank.rana,
quic_vbadigan, Manivannan Sadhasivam, Lorenzo Pieralisi,
Krzysztof Wilczyński, Rob Herring, Bjorn Helgaas, Jingoo Han
On 2/23/2026 7:27 PM, Shawn Lin wrote:
> Hi Krishna,
>
> 在 2026/02/23 星期一 19:13, Krishna Chaitanya Chundru 写道:
>> Some platforms have incorrect T_POWER_ON value programmed in hardware.
>> Generally these will be corrected by bootloaders, but not all targets
>> support bootloaders to program correct values due to that
>> LTR_L1.2_THRESHOLD value calculated by aspm driver can be wrong, which
>> can result in improper L1.2 exit behavior and can trigger AER's.
>>
>> Parse "t-power-on-us" property from each root port node and program them
>> as part of host initialization using dw_pcie_program_t_power_on() before
>> link training.
>>
>> This property in added to the dtschema here[1].
>>
>> Signed-off-by: Krishna Chaitanya Chundru
>> <krishna.chundru@oss.qualcomm.com>
>> Link[1]:
>> https://lore.kernel.org/all/20260205093346.667898-1-krishna.chundru@oss.qualcomm.com/
>> ---
>> drivers/pci/controller/dwc/pcie-qcom.c | 15 +++++++++++++++
>> 1 file changed, 15 insertions(+)
>>
>> diff --git a/drivers/pci/controller/dwc/pcie-qcom.c
>> b/drivers/pci/controller/dwc/pcie-qcom.c
>> index
>> 67a16af69ddc75fca1b123e70715e692a91a9135..489ed64c1df0fa3ed9f6b0d4c3e0bb65cfc3308e
>> 100644
>> --- a/drivers/pci/controller/dwc/pcie-qcom.c
>> +++ b/drivers/pci/controller/dwc/pcie-qcom.c
>> @@ -269,6 +269,7 @@ struct qcom_pcie_perst {
>> struct qcom_pcie_port {
>> struct list_head list;
>> struct phy *phy;
>> + u32 t_power_on;
>> struct list_head perst;
>> };
>> @@ -1283,6 +1284,16 @@ static int qcom_pcie_phy_power_on(struct
>> qcom_pcie *pcie)
>> return 0;
>> }
>> +static int qcom_pcie_configure_ports(struct qcom_pcie *pcie)
>> +{
>> + struct qcom_pcie_port *port;
>> +
>> + list_for_each_entry(port, &pcie->ports, list)
>> + dw_pcie_program_t_power_on(pcie->pci, port->t_power_on);
>> +
>> + return 0;
>> +}
>> +
>> static int qcom_pcie_host_init(struct dw_pcie_rp *pp)
>> {
>> struct dw_pcie *pci = to_dw_pcie_from_pp(pp);
>> @@ -1317,6 +1328,8 @@ static int qcom_pcie_host_init(struct
>> dw_pcie_rp *pp)
>> dw_pcie_remove_capability(pcie->pci, PCI_CAP_ID_MSIX);
>> dw_pcie_remove_ext_capability(pcie->pci, PCI_EXT_CAP_ID_DPC);
>> + qcom_pcie_configure_ports(pcie);
>> +
>> qcom_pcie_perst_deassert(pcie);
>> if (pcie->cfg->ops->config_sid) {
>> @@ -1759,6 +1772,8 @@ static int qcom_pcie_parse_port(struct
>> qcom_pcie *pcie, struct device_node *node
>> if (ret)
>> return ret;
>> + of_property_read_u32(node, "t-power-on-us", &port->t_power_on);
>
> Your patch 2 adds a common helper in pcie-designware, but why not add
> this parse as well as what the qcom_pcie_configure_ports() does, into to
> the pcie-designware core, etc dw_pcie_host_init()/dw_pcie_ep_init()?
> Because I don't see anything qcom specific here.
>
Currently dwc doesn't support to read multiple root ports, but these
properties
needs to be read from root ports. For that reason I kept it in qcom driver
until dwc supports multi root ports parsing which is currently in process.
- Krishna Chaitanya.
>
>> +
>> port->phy = phy;
>> INIT_LIST_HEAD(&port->list);
>> list_add_tail(&port->list, &pcie->ports);
>>
^ permalink raw reply [flat|nested] 12+ messages in thread
end of thread, other threads:[~2026-02-24 5:34 UTC | newest]
Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-23 11:13 [PATCH v2 0/3] PCI: qcom: Program T_POWER_ON value for L1.2 exit timing Krishna Chaitanya Chundru
2026-02-23 11:13 ` [PATCH v2 1/3] PCI/ASPM: Add helper to encode L1SS T_POWER_ON fields Krishna Chaitanya Chundru
2026-02-23 15:29 ` Bjorn Helgaas
2026-02-24 5:21 ` Krishna Chaitanya Chundru
2026-02-23 11:13 ` [PATCH v2 2/3] PCI: dwc: Add helper to Program T_POWER_ON Krishna Chaitanya Chundru
2026-02-23 15:38 ` Bjorn Helgaas
2026-02-24 5:21 ` Krishna Chaitanya Chundru
2026-02-23 23:33 ` kernel test robot
2026-02-23 11:13 ` [PATCH v2 3/3] PCI: qcom: " Krishna Chaitanya Chundru
2026-02-23 13:57 ` Shawn Lin
2026-02-24 5:33 ` Krishna Chaitanya Chundru
2026-02-23 15:47 ` Bjorn Helgaas
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox