PCI: rockchip-host: Support quirky devices
Subsystems (5)
v2 cover PCI: rockchip-host: Support quirky devices
Hi folks,
while I understand there are lots of already-working PCIe devices on RK3399 there are also many quirky devices which fail link training and refuse to enumerate. This RFC series is meant to alleviate this problem and has been tested on my Rock Pi N10.
Note that with these patches, link will train for quirky devices but with Gen1 only and only one lane (x1). I have separate patches for improving to Gen2 and all four lanes (x4). They don't depend on this fix however and since I predict the present patches are bound to be controversial, I decided to send the quality improvements separately.
Hopefully this time the series will be threaded. Thanks Heiko!
Geraldo Nascimento (2):
PCI: rockchip-host: Retry link training on failure without PERST#
arm64: dts: rockchip: drop PCIe 3v3 always-on and boot-on
.../dts/rockchip/rk3399pro-vmarc-som.dtsi | 2 - drivers/pci/controller/pcie-rockchip-host.c | 141 +++++++++++------- 2 files changed, 87 insertions(+), 56 deletions(-)
Signature · 1 line
2.49.0
RFC PATCH v2 1/2 PCI: rockchip-host: Retry link training on failure without PERST#
After almost 30 days of battling with RK3399 buggy PCIe on my Rock Pi N10 through trial-and-error debugging, I finally got positive results with enumeration on the PCI bus for both a Realtek 8111E NIC and a Samsung PM981a SSD.
The NIC was connected to a M.2->PCIe x4 riser card and it would get stuck on Polling.Compliance, without breaking electrical idle on the Host RX side. The Samsung PM981a SSD is directly connected to M.2 connector and that SSD is known to be quirky (OEM... no support) and non-functional on the RK3399 platform.
The Samsung SSD was even worse than the NIC - it would get stuck on Detect.Active like a bricked card, even though it was fully functional via USB adapter.
It seems both devices benefit from retrying Link Training if - big if here - PERST# is not toggled during retry.
For retry to work, flow must be exactly as handled by present patch, that is, we must cut 3.3V power, disable the clocks, then re-enable both clocks and power regulator and go through initialization without touching PERST#. Then quirky devices are able to sucessfully enumerate.
No functional change for already working devices.
--- drivers/pci/controller/pcie-rockchip-host.c | 141 ++++++++++++-------- 1 file changed, 87 insertions(+), 54 deletions(-)
drivers/pci/controller/pcie-rockchip-host.c +87 -54
@@ -284,6 +284,53 @@ static void rockchip_pcie_set_power_limit(struct rockchip_pcie *rockchip)rockchip_pcie_write (rockchip, status,PCIE_RC_CONFIG_DCR ); } +static int rockchip_pcie_set_vpcie (struct rockchip_pcie *rockchip) +{ +struct device *dev = rockchip->dev; +int err; + +if (!IS_ERR (rockchip->vpcie12v)) { + err =regulator_enable (rockchip->vpcie12v); +if (err) { +dev_err (dev,"fail to enable vpcie12v regulator\n" ); + goto err_out; + } + } + +if (!IS_ERR (rockchip->vpcie3v3)) { + err =regulator_enable (rockchip->vpcie3v3); +if (err) { +dev_err (dev,"fail to enable vpcie3v3 regulator\n" ); + goto err_disable_12v; + } + } + + err =regulator_enable (rockchip->vpcie1v8); +if (err) { +dev_err (dev,"fail to enable vpcie1v8 regulator\n" ); + goto err_disable_3v3; + } + + err =regulator_enable (rockchip->vpcie0v9); +if (err) { +dev_err (dev,"fail to enable vpcie0v9 regulator\n" ); + goto err_disable_1v8; + } + +return 0 ; + +err_disable_1v8: +regulator_disable (rockchip->vpcie1v8); +err_disable_3v3: +if (!IS_ERR (rockchip->vpcie3v3)) +regulator_disable (rockchip->vpcie3v3); +err_disable_12v: +if (!IS_ERR (rockchip->vpcie12v)) +regulator_disable (rockchip->vpcie12v); +err_out: +return err; +} +/** * rockchip_pcie_host_init_port - Initialize hardware * @rockchip: PCIe port information
@@ -291,11 +338,14 @@ static void rockchip_pcie_set_power_limit(struct rockchip_pcie *rockchip)static int rockchip_pcie_host_init_port (struct rockchip_pcie *rockchip) {struct device *dev = rockchip->dev; -int err, i =MAX_LANE_NUM ; +int err, i =MAX_LANE_NUM , is_reinit =0 ;u32 status; -gpiod_set_value_cansleep (rockchip->perst_gpio,0 ); +if (!is_reinit) { +gpiod_set_value_cansleep (rockchip->perst_gpio,0 ); + } +reinit: err =rockchip_pcie_init_port (rockchip);if (err)return err;
@@ -322,16 +372,46 @@ static int rockchip_pcie_host_init_port(struct rockchip_pcie *rockchip)rockchip_pcie_write (rockchip,PCIE_CLIENT_LINK_TRAIN_ENABLE ,PCIE_CLIENT_CONFIG ); -msleep (PCIE_T_PVPERL_MS ); -gpiod_set_value_cansleep (rockchip->perst_gpio,1 ); - -msleep (PCIE_T_RRS_READY_MS ); +if (!is_reinit) { +msleep (PCIE_T_PVPERL_MS ); +gpiod_set_value_cansleep (rockchip->perst_gpio,1 ); +msleep (PCIE_T_RRS_READY_MS ); + }/* 500ms timeout value should be enough for Gen1/2 training */ err =readl_poll_timeout (rockchip->apb_base +PCIE_CLIENT_BASIC_STATUS1 , status,PCIE_LINK_UP (status),20 ,500 *USEC_PER_MSEC ); -if (err) { + +if (err && !is_reinit) { +while (i--) +phy_power_off (rockchip->phys[i]); + i =MAX_LANE_NUM ; +while (i--) +phy_exit (rockchip->phys[i]); + i =MAX_LANE_NUM ; + is_reinit =1 ; +dev_dbg (dev,"Will reinit PCIe without toggling PERST#" ); +if (!IS_ERR (rockchip->vpcie12v)) +regulator_disable (rockchip->vpcie12v); +if (!IS_ERR (rockchip->vpcie3v3)) +regulator_disable (rockchip->vpcie3v3); +regulator_disable (rockchip->vpcie1v8); +regulator_disable (rockchip->vpcie0v9); +rockchip_pcie_disable_clocks (rockchip); + err =rockchip_pcie_enable_clocks (rockchip); +if (err) +return err; + err =rockchip_pcie_set_vpcie (rockchip); +if (err) { +dev_err (dev,"failed to set vpcie regulator\n" ); +rockchip_pcie_disable_clocks (rockchip); +return err; + } + goto reinit; + } + +else if (err) {dev_err (dev,"PCIe link training gen1 timeout!\n" ); goto err_power_off_phy; }
@@ -613,53 +693,6 @@ static int rockchip_pcie_parse_host_dt(struct rockchip_pcie *rockchip)return 0 ; } -static int rockchip_pcie_set_vpcie (struct rockchip_pcie *rockchip) -{ -struct device *dev = rockchip->dev; -int err; - -if (!IS_ERR (rockchip->vpcie12v)) { - err =regulator_enable (rockchip->vpcie12v); -if (err) { -dev_err (dev,"fail to enable vpcie12v regulator\n" ); - goto err_out; - } - } - -if (!IS_ERR (rockchip->vpcie3v3)) { - err =regulator_enable (rockchip->vpcie3v3); -if (err) { -dev_err (dev,"fail to enable vpcie3v3 regulator\n" ); - goto err_disable_12v; - } - } - - err =regulator_enable (rockchip->vpcie1v8); -if (err) { -dev_err (dev,"fail to enable vpcie1v8 regulator\n" ); - goto err_disable_3v3; - } - - err =regulator_enable (rockchip->vpcie0v9); -if (err) { -dev_err (dev,"fail to enable vpcie0v9 regulator\n" ); - goto err_disable_1v8; - } - -return 0 ; - -err_disable_1v8: -regulator_disable (rockchip->vpcie1v8); -err_disable_3v3: -if (!IS_ERR (rockchip->vpcie3v3)) -regulator_disable (rockchip->vpcie3v3); -err_disable_12v: -if (!IS_ERR (rockchip->vpcie12v)) -regulator_disable (rockchip->vpcie12v); -err_out: -return err; -} -static void rockchip_pcie_enable_interrupts (struct rockchip_pcie *rockchip) { rockchip_pcie_write(rockchip, (PCIE_CLIENT_INT_CLI << 16) &
Signature · 1 line
2.49.0
Re: [RFC PATCH v2 1/2] PCI: rockchip-host: Retry link training on failure without PERST#
Quoted 41 lines · Geraldo Nascimento
After almost 30 days of battling with RK3399 buggy PCIe on my Rock Pi N10 through trial-and-error debugging, I finally got positive results with enumeration on the PCI bus for both a Realtek 8111E NIC and a Samsung PM981a SSD.
drivers/pci/controller/pcie-rockchip-host.c +36
-0+static int rockchip_pcie_set_vpcie (struct rockchip_pcie *rockchip) +{ +struct device *dev = rockchip->dev; +int err; + +if (!IS_ERR (rockchip->vpcie12v)) { + err =regulator_enable (rockchip->vpcie12v); +if (err) { +dev_err (dev,"fail to enable vpcie12v regulator\n" ); + goto err_out; + } + } + +if (!IS_ERR (rockchip->vpcie3v3)) { + err =regulator_enable (rockchip->vpcie3v3); +if (err) { +dev_err (dev,"fail to enable vpcie3v3 regulator\n" ); + goto err_disable_12v; + } + } + + err =regulator_enable (rockchip->vpcie1v8); +if (err) { +dev_err (dev,"fail to enable vpcie1v8 regulator\n" ); + goto err_disable_3v3; + } + + err =regulator_enable (rockchip->vpcie0v9); +if (err) { +dev_err (dev,"fail to enable vpcie0v9 regulator\n" ); + goto err_disable_1v8; + } + +return 0 ; + +err_disable_1v8:
drivers/pci/controller/pcie-rockchip-host.c static void rockchip_pcie_set_power_limit(struct rockchip_pcie *rockchip) · line 332
Quoted 10 lines
drivers/pci/controller/pcie-rockchip-host.c +10
-0+regulator_disable (rockchip->vpcie1v8); +err_disable_3v3: +if (!IS_ERR (rockchip->vpcie3v3)) +regulator_disable (rockchip->vpcie3v3); +err_disable_12v: +if (!IS_ERR (rockchip->vpcie12v)) +regulator_disable (rockchip->vpcie12v); +err_out: +return err; +}
This *looks* like it could be strictly a move of rockchip_pcie_set_vpcie(), without changing it at all.
If that's the case, please make the move a separate patch so it's more obvious what the interesting changes that actually make a difference are.
Re: [RFC PATCH v2 1/2] PCI: rockchip-host: Retry link training on failure without PERST#
Quoted 2 lines · Bjorn Helgaas
This *looks* like it could be strictly a move of
rockchip_pcie_set_vpcie(), without changing it at all.
Hi Bjorn,
yes, a move of rockchip_pcie_set_vpcie() is needed for re-enabling power rails, considering patch 2/2 was also applied and the regulator is not always-on anymore.
Quoted 3 lines
If that's the case, please make the move a separate patch so it's more obvious what the interesting changes that actually make a difference are.
Good idea, I'll send v3.
Thank you!
Geraldo Nascimento
RFC PATCH v2 2/2 arm64: dts: rockchip: drop PCIe 3v3 always-on and boot-on
In reply to Geraldo NascimentoExample commit of needed dropping of regulator always-on/boot-on declarations to make sure quirky devices known to not be working on RK3399 are able to enumerate on second try without assertion/deassertion of PERST# in-band PCIe reset signal.
One example only, to avoid patch-bomb.
--- arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi | 2 -- 1 file changed, 2 deletions(-)
arch/arm64/boot/dts/rockchip/rk3399pro-vmarc-som.dtsi +0 -2
@@ -25,8 +25,6 @@ vcc3v3_pcie: regulator-vcc-pcie { pinctrl-names = "default"; pinctrl-0 = <&pcie_pwr>; regulator-name = "vcc3v3_pcie"; - regulator-always-on; - regulator-boot-on; vin-supply = <&vcc5v0_sys>; };
Signature · 1 line
2.49.0