[PATCH] PCI: cadence: LTSSM Detect Quiet state minimum delay setting.
Adding a quirk flag "quirk_detect_quiet_flag" to program the minimum time that LTSSM waits on entering Detect.Quiet state. Setting this to 2ms for TI j721e SOC as a workaround to resolve a bug in IP. In future revisions this setting will not be required. As per PCIe specification, all Receivers must meet the Z-RX-DC specification for 2.5 GT/s within 1ms of entering Detect.Quiet LTSSM substate. The LTSSM must stay in this substate until the ZRXDC specification for 2.5 GT/s is met. 00 : 0us minimum wait time in Detect.Quiet state. 01 : 100us minimum wait time in Detect.Quiet state. 10 : 1ms minimum wait time in Detect.Quiet state. 11 : 2ms minimum wait time in Detect.Quiet state. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pci-j721e.c | 6 ++ drivers/pci/controller/cadence/pcie-cadence-ep.c | 21 + drivers/pci/controller/cadence/pcie-cadence-host.c | 21 + drivers/pci/controller/cadence/pcie-cadence.h | 12 4 files changed, 60 insertions(+) diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index 35e61048e133..40ebe698e179 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -67,6 +67,7 @@ enum j721e_pcie_mode { struct j721e_pcie_data { enum j721e_pcie_modemode; bool quirk_retrain_flag; + bool quirk_detect_quiet_flag; }; static inline u32 j721e_pcie_user_readl(struct j721e_pcie *pcie, u32 offset) @@ -284,10 +285,12 @@ static struct pci_ops cdns_ti_pcie_host_ops = { static const struct j721e_pcie_data j721e_pcie_rc_data = { .mode = PCI_MODE_RC, .quirk_retrain_flag = true, + .quirk_detect_quiet_flag = true, }; static const struct j721e_pcie_data j721e_pcie_ep_data = { .mode = PCI_MODE_EP, + .quirk_detect_quiet_flag = true, }; static const struct of_device_id of_j721e_pcie_match[] = { @@ -394,6 +397,7 @@ static int j721e_pcie_probe(struct platform_device *pdev) bridge->ops = &cdns_ti_pcie_host_ops; rc = pci_host_bridge_priv(bridge); rc->quirk_retrain_flag = data->quirk_retrain_flag; + rc->quirk_detect_quiet_flag = data->quirk_detect_quiet_flag; cdns_pcie = &rc->pcie; cdns_pcie->dev = dev; @@ -460,6 +464,8 @@ static int j721e_pcie_probe(struct platform_device *pdev) goto err_get_sync; } + ep->quirk_detect_quiet_flag = data->quirk_detect_quiet_flag; + cdns_pcie = &ep->pcie; cdns_pcie->dev = dev; cdns_pcie->ops = &j721e_pcie_ops; diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index 897cdde02bd8..245771f03c21 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -552,6 +552,23 @@ static const struct pci_epc_ops cdns_pcie_epc_ops = { .get_features = cdns_pcie_ep_get_features, }; +static void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie_ep *ep) +{ + struct cdns_pcie *pcie = &ep->pcie; + u32 delay = 0x3; + u32 ltssm_control_cap; + + /* +* Set the LTSSM Detect Quiet state min. delay to 2ms. +*/ + + ltssm_control_cap = cdns_pcie_readl(pcie, CDNS_PCIE_LTSSM_CONTROL_CAP); + ltssm_control_cap = ((ltssm_control_cap & + ~CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK) | + CDNS_PCIE_DETECT_QUIET_MIN_DELAY(delay)); + + cdns_pcie_writel(pcie, CDNS_PCIE_LTSSM_CONTROL_CAP, ltssm_control_cap); +} int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) { @@ -623,6 +640,10 @@ int cdns_pcie_ep_setup(struct cdns_pcie_ep *ep) ep->irq_pci_addr = CDNS_PCIE_EP_IRQ_PCI_ADDR_NONE; /* Reserve region 0 for IRQs */ set_bit(0, &ep->ob_region_map); + + if (ep->quirk_detect_quiet_flag) + cdns_pcie_detect_quiet_min_delay_set(ep); + spin_lock_init(&ep->lock); return 0; diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 73dcf8cf98fb..0ed2bfac4855 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -461,6 +461,24 @@ static int cdns_pcie_host_init(struct device *dev, return cdns_pcie_host_init_address_translation(rc); } +static void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie_rc *rc) +{ + struct cdns_pcie *pcie = &rc->pcie; + u32 delay = 0x3; + u32 ltssm_control_cap; + + /* +* Set the LTSSM Detect Quiet state min. delay to 2ms. +*/ + + ltssm_control_cap =
[PATCH 1/2] dt-bindings:pci: Set LTSSM Detect.Quiet state delay.
The parameter detect-quiet-min-delay can be used to program the minimum time that LTSSM waits on entering Detect.Quiet state. 00 : 0us minimum wait time in Detect.Quiet state. 01 : 100us minimum wait time in Detect.Quiet state. 10 : 1000us minimum wait time in Detect.Quiet state. 11 : 2000us minimum wait time in Detect.Quiet state. Signed-off-by: Nadeem Athani --- .../devicetree/bindings/pci/cdns,cdns-pcie-host.yaml| 13 + 1 file changed, 13 insertions(+) diff --git a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml index 293b8ec318bc..a1d56e0be419 100644 --- a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml @@ -27,6 +27,18 @@ properties: msi-parent: true + detect-quiet-min-delay: +description: + LTSSM Detect.Quiet state minimum delay. + 00 : 0us minimum wait time + 01 : 100us minimum wait time + 10 : 1000us minimum wait time + 11 : 2000us minimum wait time +$ref: /schemas/types.yaml#/definitions/uint32 +minimum: 0 +maximum: 3 +default: 0 + required: - reg - reg-names @@ -48,6 +60,7 @@ examples: linux,pci-domain = <0>; vendor-id = <0x17cd>; device-id = <0x0200>; +detect-quiet-min-delay = <0>; reg = <0x0 0xfb00 0x0 0x0100>, <0x0 0x4100 0x0 0x1000>; -- 2.15.0
[PATCH 2/2] PCI: cadence: Set LTSSM Detect.Quiet state delay.
The parameter detect_quiet_min_delay can be used to program the minimum time that LTSSM waits on entering Detect.Quiet state. 00 : 0us minimum wait time in Detect.Quiet state. 01 : 100us minimum wait time in Detect.Quiet state. 10 : 1000us minimum wait time in Detect.Quiet state. 11 : 2000us minimum wait time in Detect.Quiet state. As per PCIe specification, all Receivers must meet the Z-RX-DC specification for 2.5 GT/s within 1000us of entering Detect.Quiet LTSSM substate. The LTSSM must stay in this substate until the ZRXDC specification for 2.5 GT/s is met. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pcie-cadence-host.c | 22 ++ drivers/pci/controller/cadence/pcie-cadence.h | 10 ++ 2 files changed, 32 insertions(+) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 73dcf8cf98fb..056161b3fe65 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -461,6 +461,20 @@ static int cdns_pcie_host_init(struct device *dev, return cdns_pcie_host_init_address_translation(rc); } +static void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie_rc *rc) +{ + struct cdns_pcie *pcie = &rc->pcie; + u32 delay = rc->detect_quiet_min_delay; + u32 ltssm_control_cap; + + ltssm_control_cap = cdns_pcie_readl(pcie, CDNS_PCIE_LTSSM_CONTROL_CAP); + ltssm_control_cap = ((ltssm_control_cap & +~CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK) | + CDNS_PCIE_DETECT_QUIET_MIN_DELAY(delay)); + + cdns_pcie_writel(pcie, CDNS_PCIE_LTSSM_CONTROL_CAP, ltssm_control_cap); +} + int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) { struct device *dev = rc->pcie.dev; @@ -485,6 +499,10 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) rc->device_id = 0x; of_property_read_u32(np, "device-id", &rc->device_id); + rc->detect_quiet_min_delay = 0; + of_property_read_u32(np, "detect-quiet-min-delay", +&rc->detect_quiet_min_delay); + pcie->reg_base = devm_platform_ioremap_resource_byname(pdev, "reg"); if (IS_ERR(pcie->reg_base)) { dev_err(dev, "missing \"reg\"\n"); @@ -497,6 +515,10 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) return PTR_ERR(rc->cfg_base); rc->cfg_res = res; + /* Default Detect.Quiet state delay is 0 */ + if (rc->detect_quiet_min_delay) + cdns_pcie_detect_quiet_min_delay_set(rc); + ret = cdns_pcie_start_link(pcie); if (ret) { dev_err(dev, "Failed to start link\n"); diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 254d2570f8c9..f2d3cca2c707 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -189,6 +189,14 @@ /* AXI link down register */ #define CDNS_PCIE_AT_LINKDOWN (CDNS_PCIE_AT_BASE + 0x0824) +/* LTSSM Capabilities register */ +#define CDNS_PCIE_LTSSM_CONTROL_CAP (CDNS_PCIE_LM_BASE + 0x0054) +#define CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK GENMASK(2, 1) +#define CDNS_PCIE_DETECT_QUIET_MIN_DELAY_SHIFT 1 +#define CDNS_PCIE_DETECT_QUIET_MIN_DELAY(delay) \ + (((delay) << CDNS_PCIE_DETECT_QUIET_MIN_DELAY_SHIFT) & \ + CDNS_PCIE_DETECT_QUIET_MIN_DELAY_MASK) + enum cdns_pcie_rp_bar { RP_BAR_UNDEFINED = -1, RP_BAR0, @@ -289,6 +297,7 @@ struct cdns_pcie { *single function at a time * @vendor_id: PCI vendor ID * @device_id: PCI device ID + * @detect_quiet_min_delay: LTSSM Detect Quite state min. delay * @avail_ib_bar: Satus of RP_BAR0, RP_BAR1 andRP_NO_BAR if it's free or *available * @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2 @@ -299,6 +308,7 @@ struct cdns_pcie_rc { void __iomem*cfg_base; u32 vendor_id; u32 device_id; + u32 detect_quiet_min_delay; boolavail_ib_bar[CDNS_PCIE_RP_MAX_IB]; boolquirk_retrain_flag; }; -- 2.15.0
[PATCH 0/2] PCI: cadence: Set LTSSM Detect.Quiet state delay.
This patch includes a set of two patches. First patch for adding a new property detect-quiet-min-delay in yaml file. Second patch programs the delay value in host pcie driver. The parameter detect-quiet-min-delay can be used to program the minimum time that LTSSM waits on entering Detect.Quiet state. 00 : 0us minimum wait time in Detect.Quiet state. 01 : 100us minimum wait time in Detect.Quiet state. 10 : 1000us minimum wait time in Detect.Quiet state. 11 : 2000us minimum wait time in Detect.Quiet state. Nadeem Athani (2): dt-bindings:pci: Set LTSSM Detect.Quiet state delay. PCI: cadence: Set LTSSM Detect.Quiet state delay. .../bindings/pci/cdns,cdns-pcie-host.yaml | 13 + drivers/pci/controller/cadence/pcie-cadence-host.c | 22 ++ drivers/pci/controller/cadence/pcie-cadence.h | 10 ++ 3 files changed, 45 insertions(+) -- 2.15.0
[PATCH v8 2/2] PCI: cadence: Retrain Link to work around Gen2 training defect.
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pci-j721e.c | 3 ++ drivers/pci/controller/cadence/pcie-cadence-host.c | 48 +- drivers/pci/controller/cadence/pcie-cadence.h | 11 - 3 files changed, 60 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index dac1ac8a7615..849f1e416ea5 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -64,6 +64,7 @@ enum j721e_pcie_mode { struct j721e_pcie_data { enum j721e_pcie_modemode; + bool quirk_retrain_flag; }; static inline u32 j721e_pcie_user_readl(struct j721e_pcie *pcie, u32 offset) @@ -280,6 +281,7 @@ static struct pci_ops cdns_ti_pcie_host_ops = { static const struct j721e_pcie_data j721e_pcie_rc_data = { .mode = PCI_MODE_RC, + .quirk_retrain_flag = true, }; static const struct j721e_pcie_data j721e_pcie_ep_data = { @@ -388,6 +390,7 @@ static int j721e_pcie_probe(struct platform_device *pdev) bridge->ops = &cdns_ti_pcie_host_ops; rc = pci_host_bridge_priv(bridge); + rc->quirk_retrain_flag = data->quirk_retrain_flag; cdns_pcie = &rc->pcie; cdns_pcie->dev = dev; diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 9f7aa718c8d4..6f591d382578 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -94,6 +94,52 @@ static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) return -ETIMEDOUT; } +static int cdns_pcie_retrain(struct cdns_pcie *pcie) +{ + u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET; + u16 lnk_stat, lnk_ctl; + int ret = 0; + + /* +* Set retrain bit if current speed is 2.5 GB/s, +* but the PCIe root port support is > 2.5 GB/s. +*/ + + lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off + +PCI_EXP_LNKCAP)); + if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) + return ret; + + lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA); + if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { + lnk_ctl = cdns_pcie_rp_readw(pcie, +pcie_cap_off + PCI_EXP_LNKCTL); + lnk_ctl |= PCI_EXP_LNKCTL_RL; + cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL, + lnk_ctl); + + ret = cdns_pcie_host_wait_for_link(pcie); + } + return ret; +} + +static int cdns_pcie_host_start_link(struct cdns_pcie_rc *rc) +{ + struct cdns_pcie *pcie = &rc->pcie; + int ret; + + ret = cdns_pcie_host_wait_for_link(pcie); + + /* +* Retrain link for Gen2 training defect +* if quirk flag is set. +*/ + if (!ret && rc->quirk_retrain_flag) + ret = cdns_pcie_retrain(pcie); + + return ret; +} + static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { struct cdns_pcie *pcie = &rc->pcie; @@ -456,7 +502,7 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) return ret; } - ret = cdns_pcie_host_wait_for_link(pcie); + ret = cdns_pcie_host_start_link(rc); if (ret) dev_dbg(dev, "PCIe link never came up\n"); diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 30eba6cafe2c..254d2570f8c9 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -119,7 +119,7 @@ * Root Port Registers (PCI configuration space for the root port function) */ #define CDNS_PCIE_RP_BASE 0x0020 - +#define CDNS_PCIE_RP_CAP_OFFSET 0xc0 /* * Address Translation Registers @@ -291,6 +291,7 @@ struct cdns_pcie { * @device_id: PCI device ID * @avail_ib_bar: Satus of RP_BAR0, RP_BAR1 andRP_NO_BAR if it's free or *available + * @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2 */ struct cdns_pcie_rc { struct cdns_pciepcie; @@ -299,6 +300,7 @@ struct cdns_pcie_rc { u32 vendor_id; u32 device_id; boolavail_ib_bar[CDNS_PCIE_RP_MAX_IB]; + boolquirk_retrain_flag; }; /** @@ -414,6 +416,13 @@ static inline void cdns_pcie_rp_writew(struct cdns_pcie
[PATCH v8 1/2] PCI: cadence: Shifting of a function to support new code.
Moving the function cdns_pcie_host_wait_for_link() further up in the file, as it's going to be used by upcoming additional code in the driver. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pcie-cadence-host.c | 33 +++--- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 811c1cb2e8de..9f7aa718c8d4 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -77,6 +77,22 @@ static struct pci_ops cdns_pcie_host_ops = { .write = pci_generic_config_write, }; +static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) +{ + struct device *dev = pcie->dev; + int retries; + + /* Check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (cdns_pcie_link_up(pcie)) { + dev_info(dev, "Link up\n"); + return 0; + } + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + return -ETIMEDOUT; +} static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { @@ -398,23 +414,6 @@ static int cdns_pcie_host_init(struct device *dev, return cdns_pcie_host_init_address_translation(rc); } -static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) -{ - struct device *dev = pcie->dev; - int retries; - - /* Check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (cdns_pcie_link_up(pcie)) { - dev_info(dev, "Link up\n"); - return 0; - } - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); - } - - return -ETIMEDOUT; -} - int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) { struct device *dev = rc->pcie.dev; -- 2.15.0
[PATCH v8 0/2] PCI: cadence: Retrain Link to work around Gen2
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Adding a quirk flag for defective IP. In future IP revisions this will not be applicable. Version history: Changes in v8: - Adding a new function cdns_pcie_host_start_link(). Changes in v7: - Changing the commit title of patch 1 in this series. - Added a return value for function cdns_pcie_retrain(). Changes in v6: - Move the position of function cdns_pcie_host_wait_for_link to remove compilation error. No changes in code. Separate patch for this. Changes in v5: - Remove the compatible string based setting of quirk flag. - Removed additional Link Up Check - Removed quirk from pcie-cadence-plat.c and added in pci-j721e.c Changes in v4: - Added a quirk flag based on a new compatible string. - Change of api for link up: cdns_pcie_host_wait_for_link(). Changes in v3: - To set retrain link bit,checking device capability & link status. - 32bit read in place of 8bit. - Minor correction in patch comment. - Change in variable & macro name. Changes in v2: - 16bit read in place of 8bit. Nadeem Athani (2): PCI: cadence: Shifting of a function to support new code. PCI: cadence: Retrain Link to work around Gen2 training defect. drivers/pci/controller/cadence/pci-j721e.c | 3 + drivers/pci/controller/cadence/pcie-cadence-host.c | 81 +- drivers/pci/controller/cadence/pcie-cadence.h | 11 ++- 3 files changed, 76 insertions(+), 19 deletions(-) -- 2.15.0
[PATCH v7 0/2] PCI: cadence: Retrain Link to work around Gen2
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Adding a quirk flag for defective IP. In future IP revisions this will not be applicable. Version history: Changes in v7: - Changing the commit title of patch 1 in this series. - Added a return value for function cdns_pcie_retrain(). Changes in v6: - Move the position of function cdns_pcie_host_wait_for_link to remove compilation error. No changes in code. Separate patch for this. Changes in v5: - Remove the compatible string based setting of quirk flag. - Removed additional Link Up Check - Removed quirk from pcie-cadence-plat.c and added in pci-j721e.c Changes in v4: - Added a quirk flag based on a new compatible string. - Change of api for link up: cdns_pcie_host_wait_for_link(). Changes in v3: - To set retrain link bit,checking device capability & link status. - 32bit read in place of 8bit. - Minor correction in patch comment. - Change in variable & macro name. Changes in v2: - 16bit read in place of 8bit. Nadeem Athani (2): PCI: cadence: Shifting of a function to support new code. PCI: cadence: Retrain Link to work around Gen2 training defect. drivers/pci/controller/cadence/pci-j721e.c | 3 + drivers/pci/controller/cadence/pcie-cadence-host.c | 70 -- drivers/pci/controller/cadence/pcie-cadence.h | 11 +++- 3 files changed, 65 insertions(+), 19 deletions(-) -- 2.15.0
[PATCH v7 2/2] PCI: cadence: Retrain Link to work around Gen2 training defect.
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pci-j721e.c | 3 ++ drivers/pci/controller/cadence/pcie-cadence-host.c | 37 +- drivers/pci/controller/cadence/pcie-cadence.h | 11 ++- 3 files changed, 49 insertions(+), 2 deletions(-) diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index dac1ac8a7615..849f1e416ea5 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -64,6 +64,7 @@ enum j721e_pcie_mode { struct j721e_pcie_data { enum j721e_pcie_modemode; + bool quirk_retrain_flag; }; static inline u32 j721e_pcie_user_readl(struct j721e_pcie *pcie, u32 offset) @@ -280,6 +281,7 @@ static struct pci_ops cdns_ti_pcie_host_ops = { static const struct j721e_pcie_data j721e_pcie_rc_data = { .mode = PCI_MODE_RC, + .quirk_retrain_flag = true, }; static const struct j721e_pcie_data j721e_pcie_ep_data = { @@ -388,6 +390,7 @@ static int j721e_pcie_probe(struct platform_device *pdev) bridge->ops = &cdns_ti_pcie_host_ops; rc = pci_host_bridge_priv(bridge); + rc->quirk_retrain_flag = data->quirk_retrain_flag; cdns_pcie = &rc->pcie; cdns_pcie->dev = dev; diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 9f7aa718c8d4..f3496588862d 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -94,6 +94,35 @@ static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) return -ETIMEDOUT; } +static int cdns_pcie_retrain(struct cdns_pcie *pcie) +{ + u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET; + u16 lnk_stat, lnk_ctl; + int ret = 0; + + /* +* Set retrain bit if current speed is 2.5 GB/s, +* but the PCIe root port support is > 2.5 GB/s. +*/ + + lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off + +PCI_EXP_LNKCAP)); + if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) + return ret; + + lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA); + if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { + lnk_ctl = cdns_pcie_rp_readw(pcie, +pcie_cap_off + PCI_EXP_LNKCTL); + lnk_ctl |= PCI_EXP_LNKCTL_RL; + cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL, + lnk_ctl); + + ret = cdns_pcie_host_wait_for_link(pcie); + } + return ret; +} + static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { struct cdns_pcie *pcie = &rc->pcie; @@ -457,8 +486,14 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) } ret = cdns_pcie_host_wait_for_link(pcie); - if (ret) + if (ret) { dev_dbg(dev, "PCIe link never came up\n"); + } else { + if (rc->quirk_retrain_flag) { + if (cdns_pcie_retrain(pcie)) + dev_dbg(dev, "PCIe link never came up\n"); + } + } for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) rc->avail_ib_bar[bar] = true; diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 30eba6cafe2c..0f29128a5d0a 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -119,7 +119,7 @@ * Root Port Registers (PCI configuration space for the root port function) */ #define CDNS_PCIE_RP_BASE 0x0020 - +#define CDNS_PCIE_RP_CAP_OFFSET 0xc0 /* * Address Translation Registers @@ -291,6 +291,7 @@ struct cdns_pcie { * @device_id: PCI device ID * @avail_ib_bar: Satus of RP_BAR0, RP_BAR1 andRP_NO_BAR if it's free or *available + * @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2 */ struct cdns_pcie_rc { struct cdns_pciepcie; @@ -299,6 +300,7 @@ struct cdns_pcie_rc { u32 vendor_id; u32 device_id; boolavail_ib_bar[CDNS_PCIE_RP_MAX_IB]; + boolquirk_retrain_flag; }; /** @@ -414,6 +416,13 @@ static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie, cdns_pcie_write_sz(addr, 0x2, value); } +static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg) +{ + v
[PATCH v7 1/2] PCI: cadence: Shifting of a function to support new code.
Move the function cdns_pcie_host_wait_for_link() further up in the file, as it's going to be used by upcoming additional code in the driver. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pcie-cadence-host.c | 33 +++--- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 811c1cb2e8de..9f7aa718c8d4 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -77,6 +77,22 @@ static struct pci_ops cdns_pcie_host_ops = { .write = pci_generic_config_write, }; +static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) +{ + struct device *dev = pcie->dev; + int retries; + + /* Check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (cdns_pcie_link_up(pcie)) { + dev_info(dev, "Link up\n"); + return 0; + } + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + return -ETIMEDOUT; +} static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { @@ -398,23 +414,6 @@ static int cdns_pcie_host_init(struct device *dev, return cdns_pcie_host_init_address_translation(rc); } -static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) -{ - struct device *dev = pcie->dev; - int retries; - - /* Check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (cdns_pcie_link_up(pcie)) { - dev_info(dev, "Link up\n"); - return 0; - } - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); - } - - return -ETIMEDOUT; -} - int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) { struct device *dev = rc->pcie.dev; -- 2.15.0
[PATCH v6 1/2] PCI: cadence: Retrain Link to work around Gen2 training defect.
Moving the function above to remove compilation error. No changes in function. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pcie-cadence-host.c | 33 +++--- 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 811c1cb2e8de..9f7aa718c8d4 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -77,6 +77,22 @@ static struct pci_ops cdns_pcie_host_ops = { .write = pci_generic_config_write, }; +static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) +{ + struct device *dev = pcie->dev; + int retries; + + /* Check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (cdns_pcie_link_up(pcie)) { + dev_info(dev, "Link up\n"); + return 0; + } + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + return -ETIMEDOUT; +} static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { @@ -398,23 +414,6 @@ static int cdns_pcie_host_init(struct device *dev, return cdns_pcie_host_init_address_translation(rc); } -static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) -{ - struct device *dev = pcie->dev; - int retries; - - /* Check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (cdns_pcie_link_up(pcie)) { - dev_info(dev, "Link up\n"); - return 0; - } - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); - } - - return -ETIMEDOUT; -} - int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) { struct device *dev = rc->pcie.dev; -- 2.15.0
[PATCH v6 2/2] PCI: cadence: Retrain Link to work around Gen2 training defect.
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pci-j721e.c | 3 ++ drivers/pci/controller/cadence/pcie-cadence-host.c | 32 ++ drivers/pci/controller/cadence/pcie-cadence.h | 11 +++- 3 files changed, 45 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index dac1ac8a7615..23a30be207a5 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -64,6 +64,7 @@ enum j721e_pcie_mode { struct j721e_pcie_data { enum j721e_pcie_modemode; + boolquirk_retrain_flag; }; static inline u32 j721e_pcie_user_readl(struct j721e_pcie *pcie, u32 offset) @@ -280,6 +281,7 @@ static struct pci_ops cdns_ti_pcie_host_ops = { static const struct j721e_pcie_data j721e_pcie_rc_data = { .mode = PCI_MODE_RC, + .quirk_retrain_flag = true, }; static const struct j721e_pcie_data j721e_pcie_ep_data = { @@ -388,6 +390,7 @@ static int j721e_pcie_probe(struct platform_device *pdev) bridge->ops = &cdns_ti_pcie_host_ops; rc = pci_host_bridge_priv(bridge); + rc->quirk_retrain_flag = data->quirk_retrain_flag; cdns_pcie = &rc->pcie; cdns_pcie->dev = dev; diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 9f7aa718c8d4..9d730c10083b 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -94,6 +94,34 @@ static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) return -ETIMEDOUT; } +static void cdns_pcie_retrain(struct cdns_pcie *pcie) +{ + u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET; + u16 lnk_stat, lnk_ctl; + + /* +* Set retrain bit if current speed is 2.5 GB/s, +* but the PCIe root port support is > 2.5 GB/s. +*/ + + lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off + +PCI_EXP_LNKCAP)); + if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) + return; + + lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA); + if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { + lnk_ctl = cdns_pcie_rp_readw(pcie, +pcie_cap_off + PCI_EXP_LNKCTL); + lnk_ctl |= PCI_EXP_LNKCTL_RL; + cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL, + lnk_ctl); + + if (cdns_pcie_host_wait_for_link(pcie)) + return; + } +} + static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { struct cdns_pcie *pcie = &rc->pcie; @@ -459,6 +487,10 @@ int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) ret = cdns_pcie_host_wait_for_link(pcie); if (ret) dev_dbg(dev, "PCIe link never came up\n"); + else { + if (rc->quirk_retrain_flag) + cdns_pcie_retrain(pcie); + } for (bar = RP_BAR0; bar <= RP_NO_BAR; bar++) rc->avail_ib_bar[bar] = true; diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 30eba6cafe2c..0f29128a5d0a 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -119,7 +119,7 @@ * Root Port Registers (PCI configuration space for the root port function) */ #define CDNS_PCIE_RP_BASE 0x0020 - +#define CDNS_PCIE_RP_CAP_OFFSET 0xc0 /* * Address Translation Registers @@ -291,6 +291,7 @@ struct cdns_pcie { * @device_id: PCI device ID * @avail_ib_bar: Satus of RP_BAR0, RP_BAR1 andRP_NO_BAR if it's free or *available + * @quirk_retrain_flag: Retrain link as quirk for PCIe Gen2 */ struct cdns_pcie_rc { struct cdns_pciepcie; @@ -299,6 +300,7 @@ struct cdns_pcie_rc { u32 vendor_id; u32 device_id; boolavail_ib_bar[CDNS_PCIE_RP_MAX_IB]; + boolquirk_retrain_flag; }; /** @@ -414,6 +416,13 @@ static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie, cdns_pcie_write_sz(addr, 0x2, value); } +static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg) +{ + void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg; + + return cdns_pcie_read_sz(addr, 0x2); +} + /* Endpoint Function register access */
[PATCH v6 0/2] PCI: cadence: Retrain Link to work around Gen2
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Adding a quirk flag for defective IP. In future IP revisions this will not be applicable. Version history: Changes in v6: - Move the position of function cdns_pcie_host_wait_for_link to remove compilation error. No changes in code. Separate patch for this. Changes in v5: - Remove the compatible string based setting of quirk flag. - Removed additional Link Up Check - Removed quirk from pcie-cadence-plat.c and added in pci-j721e.c Changes in v4: - Added a quirk flag based on a new compatible string. - Change of api for link up: cdns_pcie_host_wait_for_link(). Changes in v3: - To set retrain link bit,checking device capability & link status. - 32bit read in place of 8bit. - Minor correction in patch comment. - Change in variable & macro name. Changes in v2: - 16bit read in place of 8bit. Nadeem Athani (2): PCI: cadence: Retrain Link to work around Gen2 training defect. PCI: cadence: Retrain Link to work around Gen2 training defect. drivers/pci/controller/cadence/pci-j721e.c | 3 + drivers/pci/controller/cadence/pcie-cadence-host.c | 65 -- drivers/pci/controller/cadence/pcie-cadence.h | 11 +++- 3 files changed, 61 insertions(+), 18 deletions(-) -- 2.15.0
[PATCH v4 0/2] PCI: cadence: Retrain Link to work around Gen2
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Adding a quirk flag based on a new compatible string. In future IP revisions this will not be applicable. Version history: Changes in v4: - Added a quirk flag based on a new compatible string. - Change of api for link up: cdns_pcie_host_wait_for_link(). Changes in v3: - To set retrain link bit,checking device capability & link status. - 32bit read in place of 8bit. - Minor correction in patch comment. - Change in variable & macro name. Changes in v2: - 16bit read in place of 8bit. Nadeem Athani (2): dt-bindings: pci: Retrain Link to work around Gen2 training defect. PCI: cadence: Retrain Link to work around Gen2 training defect. .../bindings/pci/cdns,cdns-pcie-host.yaml | 4 +- drivers/pci/controller/cadence/pcie-cadence-host.c | 67 -- drivers/pci/controller/cadence/pcie-cadence-plat.c | 13 + drivers/pci/controller/cadence/pcie-cadence.h | 11 +++- 4 files changed, 76 insertions(+), 19 deletions(-) -- 2.15.0
[PATCH v4 2/2] PCI: cadence: Retrain Link to work around Gen2 training defect.
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pcie-cadence-host.c | 67 -- drivers/pci/controller/cadence/pcie-cadence-plat.c | 13 + drivers/pci/controller/cadence/pcie-cadence.h | 11 +++- 3 files changed, 73 insertions(+), 18 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 811c1cb2e8de..36dccf7241fe 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -77,6 +77,53 @@ static struct pci_ops cdns_pcie_host_ops = { .write = pci_generic_config_write, }; +static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) +{ + struct device *dev = pcie->dev; + int retries; + + /* Check if the link is up or not */ + for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { + if (cdns_pcie_link_up(pcie)) { + dev_info(dev, "Link up\n"); + return 0; + } + usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); + } + + return -ETIMEDOUT; +} + +static void cdns_pcie_retrain(struct cdns_pcie *pcie) +{ + u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET; + u16 lnk_stat, lnk_ctl; + + if (cdns_pcie_host_wait_for_link(pcie)) + return; + + /* +* Set retrain bit if current speed is 2.5 GB/s, +* but the PCIe root port support is > 2.5 GB/s. +*/ + + lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off + +PCI_EXP_LNKCAP)); + if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) + return; + + lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA); + if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { + lnk_ctl = cdns_pcie_rp_readw(pcie, +pcie_cap_off + PCI_EXP_LNKCTL); + lnk_ctl |= PCI_EXP_LNKCTL_RL; + cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL, + lnk_ctl); + + if (cdns_pcie_host_wait_for_link(pcie)) + return; + } +} static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { @@ -115,6 +162,9 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0); cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); + if (rc->quirk_retrain_flag) + cdns_pcie_retrain(pcie); + return 0; } @@ -398,23 +448,6 @@ static int cdns_pcie_host_init(struct device *dev, return cdns_pcie_host_init_address_translation(rc); } -static int cdns_pcie_host_wait_for_link(struct cdns_pcie *pcie) -{ - struct device *dev = pcie->dev; - int retries; - - /* Check if the link is up or not */ - for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) { - if (cdns_pcie_link_up(pcie)) { - dev_info(dev, "Link up\n"); - return 0; - } - usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX); - } - - return -ETIMEDOUT; -} - int cdns_pcie_host_setup(struct cdns_pcie_rc *rc) { struct device *dev = rc->pcie.dev; diff --git a/drivers/pci/controller/cadence/pcie-cadence-plat.c b/drivers/pci/controller/cadence/pcie-cadence-plat.c index 5fee0f89ab59..97b4b4f98fa4 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-plat.c +++ b/drivers/pci/controller/cadence/pcie-cadence-plat.c @@ -28,6 +28,7 @@ struct cdns_plat_pcie { struct cdns_plat_pcie_of_data { bool is_rc; + bool quirk_retrain_flag; }; static const struct of_device_id cdns_plat_pcie_of_match[]; @@ -78,6 +79,7 @@ static int cdns_plat_pcie_probe(struct platform_device *pdev) rc = pci_host_bridge_priv(bridge); rc->pcie.dev = dev; rc->pcie.ops = &cdns_plat_ops; + rc->quirk_retrain_flag = data->quirk_retrain_flag; cdns_plat_pcie->pcie = &rc->pcie; cdns_plat_pcie->is_rc = is_rc; @@ -156,6 +158,13 @@ static void cdns_plat_pcie_shutdown(struct platform_device *pdev) static const struct cdns_plat_pcie_of_data cdns_plat_pcie_host_of_data = { .is_rc = true, + .quirk_retrain_flag = false, +}; + +static const struct cdns_plat_pcie_of_data + cdns_plat_pcie_host_quirk_retrain_of_data = { + .is_rc = true, + .quirk_retrain_flag = true, }; s
[PATCH v4 1/2] dt-bindings: pci: Retrain Link to work around Gen2 training defect.
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Adding a quirk flag based on a new compatible string. Signed-off-by: Nadeem Athani --- Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml index 293b8ec318bc..204d78f9efe3 100644 --- a/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml +++ b/Documentation/devicetree/bindings/pci/cdns,cdns-pcie-host.yaml @@ -15,7 +15,9 @@ allOf: properties: compatible: -const: cdns,cdns-pcie-host +enum: +- cdns,cdns-pcie-host +- cdns,cdns-pcie-host-quirk-retrain reg: maxItems: 2 -- 2.15.0
[PATCH v3] PCI: cadence: Retrain Link to work around Gen2 training defect.
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain Link bit is set as quirk to enable this speed change. Signed-off-by: Nadeem Athani --- Changes in v3: - To set retrain link bit,checking device capability & link status. - 32bit read in place of 8bit. - Minor correction in patch comment. - Change in variable & macro name. Changes in v2: - 16bit read in place of 8bit. drivers/pci/controller/cadence/pcie-cadence-host.c | 31 ++ drivers/pci/controller/cadence/pcie-cadence.h | 9 ++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 4550e0d469ca..2b2ae4e18032 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -77,6 +77,36 @@ static struct pci_ops cdns_pcie_host_ops = { .write = pci_generic_config_write, }; +static void cdns_pcie_retrain(struct cdns_pcie *pcie) +{ + u32 lnk_cap_sls, pcie_cap_off = CDNS_PCIE_RP_CAP_OFFSET; + u16 lnk_stat, lnk_ctl; + + if (!cdns_pcie_link_up(pcie)) + return; + + /* +* Set retrain bit if current speed is 2.5 GB/s, +* but the PCIe root port support is > 2.5 GB/s. +*/ + + lnk_cap_sls = cdns_pcie_readl(pcie, (CDNS_PCIE_RP_BASE + pcie_cap_off + + PCI_EXP_LNKCAP)); + if ((lnk_cap_sls & PCI_EXP_LNKCAP_SLS) <= PCI_EXP_LNKCAP_SLS_2_5GB) + return; + + lnk_stat = cdns_pcie_rp_readw(pcie, pcie_cap_off + PCI_EXP_LNKSTA); + if ((lnk_stat & PCI_EXP_LNKSTA_CLS) == PCI_EXP_LNKSTA_CLS_2_5GB) { + lnk_ctl = cdns_pcie_rp_readw(pcie, +pcie_cap_off + PCI_EXP_LNKCTL); + lnk_ctl |= PCI_EXP_LNKCTL_RL; + cdns_pcie_rp_writew(pcie, pcie_cap_off + PCI_EXP_LNKCTL, + lnk_ctl); + + if (!cdns_pcie_link_up(pcie)) + return; + } +} static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) { @@ -115,6 +145,7 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0); cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); + cdns_pcie_retrain(pcie); return 0; } diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index feed1e3038f4..5f1cf032ae15 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -119,7 +119,7 @@ * Root Port Registers (PCI configuration space for the root port function) */ #define CDNS_PCIE_RP_BASE 0x0020 - +#define CDNS_PCIE_RP_CAP_OFFSET 0xc0 /* * Address Translation Registers @@ -413,6 +413,13 @@ static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie, cdns_pcie_write_sz(addr, 0x2, value); } +static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg) +{ + void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg; + + return cdns_pcie_read_sz(addr, 0x2); +} + /* Endpoint Function register access */ static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn, u32 reg, u8 value) -- 2.15.0
[PATCH v2] PCI: Cadence: Add quirk for Gen2 controller to do autonomous speed change.
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain bit is set as a quirk to trigger this speed change. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pcie-cadence-host.c | 14 ++ drivers/pci/controller/cadence/pcie-cadence.h | 15 +++ 2 files changed, 29 insertions(+) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 4550e0d469ca..a2317614268d 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -83,6 +83,9 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) struct cdns_pcie *pcie = &rc->pcie; u32 value, ctrl; u32 id; + u32 link_cap = CDNS_PCIE_LINK_CAP_OFFSET; + u8 sls; + u16 lnk_ctl; /* * Set the root complex BAR configuration register: @@ -111,6 +114,17 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) if (rc->device_id != 0x) cdns_pcie_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id); + /* Quirk to enable autonomous speed change for GEN2 controller */ + /* Reading Supported Link Speed value */ + sls = PCI_EXP_LNKCAP_SLS & + cdns_pcie_rp_readb(pcie, link_cap + PCI_EXP_LNKCAP); + if (sls == PCI_EXP_LNKCAP_SLS_5_0GB) { + /* Since this a Gen2 controller, set Retrain Link(RL) bit */ + lnk_ctl = cdns_pcie_rp_readw(pcie, link_cap + PCI_EXP_LNKCTL); + lnk_ctl |= PCI_EXP_LNKCTL_RL; + cdns_pcie_rp_writew(pcie, link_cap + PCI_EXP_LNKCTL, lnk_ctl); + } + cdns_pcie_rp_writeb(pcie, PCI_CLASS_REVISION, 0); cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0); cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index feed1e3038f4..fe560480c573 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -120,6 +120,7 @@ */ #define CDNS_PCIE_RP_BASE 0x0020 +#define CDNS_PCIE_LINK_CAP_OFFSET 0xC0 /* * Address Translation Registers @@ -413,6 +414,20 @@ static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie, cdns_pcie_write_sz(addr, 0x2, value); } +static inline u8 cdns_pcie_rp_readb(struct cdns_pcie *pcie, u32 reg) +{ + void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg; + + return cdns_pcie_read_sz(addr, 0x1); +} + +static inline u16 cdns_pcie_rp_readw(struct cdns_pcie *pcie, u32 reg) +{ + void __iomem *addr = pcie->reg_base + CDNS_PCIE_RP_BASE + reg; + + return cdns_pcie_read_sz(addr, 0x2); +} + /* Endpoint Function register access */ static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn, u32 reg, u8 value) -- 2.15.0
[PATCH] PCI: Cadence: Add quirk for Gen2 controller to do autonomous speed change.
Cadence controller will not initiate autonomous speed change if strapped as Gen2. The Retrain bit is set as a quirk to trigger this speed change. Signed-off-by: Nadeem Athani --- drivers/pci/controller/cadence/pcie-cadence-host.c | 13 + drivers/pci/controller/cadence/pcie-cadence.h |6 ++ 2 files changed, 19 insertions(+), 0 deletions(-) diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 4550e0d..4cb7f29 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -83,6 +83,8 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) struct cdns_pcie *pcie = &rc->pcie; u32 value, ctrl; u32 id; + u32 link_cap = CDNS_PCIE_LINK_CAP_OFFSET; + u8 sls, lnk_ctl; /* * Set the root complex BAR configuration register: @@ -111,6 +113,17 @@ static int cdns_pcie_host_init_root_port(struct cdns_pcie_rc *rc) if (rc->device_id != 0x) cdns_pcie_rp_writew(pcie, PCI_DEVICE_ID, rc->device_id); + /* Quirk to enable autonomous speed change for GEN2 controller */ + /* Reading Supported Link Speed value */ + sls = PCI_EXP_LNKCAP_SLS & + cdns_pcie_rp_readb(pcie, link_cap + PCI_EXP_LNKCAP); + if (sls == PCI_EXP_LNKCAP_SLS_5_0GB) { + /* Since this a Gen2 controller, set Retrain Link(RL) bit */ + lnk_ctl = cdns_pcie_rp_readb(pcie, link_cap + PCI_EXP_LNKCTL); + lnk_ctl |= PCI_EXP_LNKCTL_RL; + cdns_pcie_rp_writeb(pcie, link_cap + PCI_EXP_LNKCTL, lnk_ctl); + } + cdns_pcie_rp_writeb(pcie, PCI_CLASS_REVISION, 0); cdns_pcie_rp_writeb(pcie, PCI_CLASS_PROG, 0); cdns_pcie_rp_writew(pcie, PCI_CLASS_DEVICE, PCI_CLASS_BRIDGE_PCI); diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index feed1e3..075c263 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -120,6 +120,7 @@ */ #define CDNS_PCIE_RP_BASE 0x0020 +#define CDNS_PCIE_LINK_CAP_OFFSET 0xC0 /* * Address Translation Registers @@ -413,6 +414,11 @@ static inline void cdns_pcie_rp_writew(struct cdns_pcie *pcie, cdns_pcie_write_sz(addr, 0x2, value); } +static inline u8 cdns_pcie_rp_readb(struct cdns_pcie *pcie, u32 reg) +{ + return readb(pcie->reg_base + CDNS_PCIE_RP_BASE + reg); +} + /* Endpoint Function register access */ static inline void cdns_pcie_ep_fn_writeb(struct cdns_pcie *pcie, u8 fn, u32 reg, u8 value) -- 1.7.1