2021-11-23 18:06:44

by Marc Zyngier

[permalink] [raw]
Subject: [PATCH v3 1/3] PCI: apple: Follow the PCIe specifications when resetting the port

While the Apple PCIe driver works correctly when directly booted
from the firmware, it fails to initialise when the kernel is booted
from a bootloader using PCIe such as u-boot.

That's beacuse we're missing a proper reset of the port (we only
clear the reset, but never assert it).

The PCIe spec requirements are two-fold:

- #PERST must be asserted before setting up the clocks, and
stay asserted for at least 100us (Tperst-clk).

- Once #PERST is deasserted, the OS must wait for at least 100ms
"from the end of a Conventional Reset" before we can start talking
to the devices

Implementing this results in a booting system.

Fixes: 1e33888fbe44 ("PCI: apple: Add initial hardware bring-up")
Acked-by: Pali Rohár <[email protected]>
Signed-off-by: Marc Zyngier <[email protected]>
Cc: Alyssa Rosenzweig <[email protected]>
Cc: Lorenzo Pieralisi <[email protected]>
Cc: Bjorn Helgaas <[email protected]>
---
drivers/pci/controller/pcie-apple.c | 10 ++++++++++
1 file changed, 10 insertions(+)

diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
index 1bf4d75b61be..957960a733c4 100644
--- a/drivers/pci/controller/pcie-apple.c
+++ b/drivers/pci/controller/pcie-apple.c
@@ -539,13 +539,23 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,

rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK);

+ /* Engage #PERST before setting up the clock */
+ gpiod_set_value(reset, 0);
+
ret = apple_pcie_setup_refclk(pcie, port);
if (ret < 0)
return ret;

+ /* The minimal Tperst-clk value is 100us (PCIe CMS r2.0, 2.6.2) */
+ usleep_range(100, 200);
+
+ /* Deassert #PERST */
rmw_set(PORT_PERST_OFF, port->base + PORT_PERST);
gpiod_set_value(reset, 1);

+ /* Wait for 100ms after #PERST deassertion (PCIe r2.0, 6.6.1) */
+ msleep(100);
+
ret = readl_relaxed_poll_timeout(port->base + PORT_STATUS, stat,
stat & PORT_STATUS_READY, 100, 250000);
if (ret < 0) {
--
2.30.2



2021-11-24 09:22:31

by Luca Ceresoli

[permalink] [raw]
Subject: Re: [PATCH v3 1/3] PCI: apple: Follow the PCIe specifications when resetting the port

Hi,

On 23/11/21 19:06, Marc Zyngier wrote:
> While the Apple PCIe driver works correctly when directly booted
> from the firmware, it fails to initialise when the kernel is booted
> from a bootloader using PCIe such as u-boot.
>
> That's beacuse we're missing a proper reset of the port (we only
> clear the reset, but never assert it).
>
> The PCIe spec requirements are two-fold:
>
> - #PERST must be asserted before setting up the clocks, and
> stay asserted for at least 100us (Tperst-clk).
>
> - Once #PERST is deasserted, the OS must wait for at least 100ms
> "from the end of a Conventional Reset" before we can start talking
> to the devices
>
> Implementing this results in a booting system.
>
> Fixes: 1e33888fbe44 ("PCI: apple: Add initial hardware bring-up")
> Acked-by: Pali Rohár <[email protected]>
> Signed-off-by: Marc Zyngier <[email protected]>
> Cc: Alyssa Rosenzweig <[email protected]>
> Cc: Lorenzo Pieralisi <[email protected]>
> Cc: Bjorn Helgaas <[email protected]>>
> ---
> drivers/pci/controller/pcie-apple.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
> index 1bf4d75b61be..957960a733c4 100644
> --- a/drivers/pci/controller/pcie-apple.c
> +++ b/drivers/pci/controller/pcie-apple.c
> @@ -539,13 +539,23 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
>
> rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK);
>
> + /* Engage #PERST before setting up the clock */

"Assert" is the verb typically used instead of "Engage".

With this fixed, or even without as this is a pretty urgent fix:

Reviewed-by: Luca Ceresoli <[email protected]>

--
Luca

2021-11-24 12:58:36

by Robin Murphy

[permalink] [raw]
Subject: Re: [PATCH v3 1/3] PCI: apple: Follow the PCIe specifications when resetting the port

On 2021-11-23 18:06, Marc Zyngier wrote:
> While the Apple PCIe driver works correctly when directly booted
> from the firmware, it fails to initialise when the kernel is booted
> from a bootloader using PCIe such as u-boot.
>
> That's beacuse we're missing a proper reset of the port (we only
> clear the reset, but never assert it).
>
> The PCIe spec requirements are two-fold:
>
> - #PERST must be asserted before setting up the clocks, and
> stay asserted for at least 100us (Tperst-clk).
>
> - Once #PERST is deasserted, the OS must wait for at least 100ms
> "from the end of a Conventional Reset" before we can start talking
> to the devices
>
> Implementing this results in a booting system.
>
> Fixes: 1e33888fbe44 ("PCI: apple: Add initial hardware bring-up")
> Acked-by: Pali Rohár <[email protected]>
> Signed-off-by: Marc Zyngier <[email protected]>
> Cc: Alyssa Rosenzweig <[email protected]>
> Cc: Lorenzo Pieralisi <[email protected]>
> Cc: Bjorn Helgaas <[email protected]>
> ---
> drivers/pci/controller/pcie-apple.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
> index 1bf4d75b61be..957960a733c4 100644
> --- a/drivers/pci/controller/pcie-apple.c
> +++ b/drivers/pci/controller/pcie-apple.c
> @@ -539,13 +539,23 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
>
> rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK);
>
> + /* Engage #PERST before setting up the clock */
> + gpiod_set_value(reset, 0);

FWIW, given that getting the GPIO with GPIOD_OUT_LOW should have had
this effect in the first place, if this isn't a no-op at this point then
it would hint at something being more significantly wrong down at the
GPIO/pinctrl end :/

Once you fix the polarity in the later patch, though, adding the
explicit reset assertion here does seem a far nicer option than fiddling
the flags to preserve the implicit assertion earlier.

Cheers,
Robin.

> +
> ret = apple_pcie_setup_refclk(pcie, port);
> if (ret < 0)
> return ret;
>
> + /* The minimal Tperst-clk value is 100us (PCIe CMS r2.0, 2.6.2) */
> + usleep_range(100, 200);
> +
> + /* Deassert #PERST */
> rmw_set(PORT_PERST_OFF, port->base + PORT_PERST);
> gpiod_set_value(reset, 1);
>
> + /* Wait for 100ms after #PERST deassertion (PCIe r2.0, 6.6.1) */
> + msleep(100);
> +
> ret = readl_relaxed_poll_timeout(port->base + PORT_STATUS, stat,
> stat & PORT_STATUS_READY, 100, 250000);
> if (ret < 0) {
>

2021-12-07 16:23:05

by Bjorn Helgaas

[permalink] [raw]
Subject: Re: [PATCH v3 1/3] PCI: apple: Follow the PCIe specifications when resetting the port

On Tue, Nov 23, 2021 at 06:06:34PM +0000, Marc Zyngier wrote:
> While the Apple PCIe driver works correctly when directly booted
> from the firmware, it fails to initialise when the kernel is booted
> from a bootloader using PCIe such as u-boot.
>
> That's beacuse we're missing a proper reset of the port (we only
> clear the reset, but never assert it).
>
> The PCIe spec requirements are two-fold:
>
> - #PERST must be asserted before setting up the clocks, and
> stay asserted for at least 100us (Tperst-clk).
>
> - Once #PERST is deasserted, the OS must wait for at least 100ms
> "from the end of a Conventional Reset" before we can start talking
> to the devices

Unless somebody objects, I'll s/#PERST/PERST#/ to match the spec
usage, both here and in the comments below.

I also notice gpiod_get_from_of_node(..., "#PERST") earlier in
apple_pcie_setup_port(). If it wouldn't break anything, I'd like to
change that, too.

> Implementing this results in a booting system.
>
> Fixes: 1e33888fbe44 ("PCI: apple: Add initial hardware bring-up")
> Acked-by: Pali Roh?r <[email protected]>
> Signed-off-by: Marc Zyngier <[email protected]>
> Cc: Alyssa Rosenzweig <[email protected]>
> Cc: Lorenzo Pieralisi <[email protected]>
> Cc: Bjorn Helgaas <[email protected]>
> ---
> drivers/pci/controller/pcie-apple.c | 10 ++++++++++
> 1 file changed, 10 insertions(+)
>
> diff --git a/drivers/pci/controller/pcie-apple.c b/drivers/pci/controller/pcie-apple.c
> index 1bf4d75b61be..957960a733c4 100644
> --- a/drivers/pci/controller/pcie-apple.c
> +++ b/drivers/pci/controller/pcie-apple.c
> @@ -539,13 +539,23 @@ static int apple_pcie_setup_port(struct apple_pcie *pcie,
>
> rmw_set(PORT_APPCLK_EN, port->base + PORT_APPCLK);
>
> + /* Engage #PERST before setting up the clock */
> + gpiod_set_value(reset, 0);
> +
> ret = apple_pcie_setup_refclk(pcie, port);
> if (ret < 0)
> return ret;
>
> + /* The minimal Tperst-clk value is 100us (PCIe CMS r2.0, 2.6.2) */
> + usleep_range(100, 200);

Spec says "min 100us from REFCLK stable before PERST# inactive". So I
guess when apple_pcie_setup_refclk() returns, we know REFCLK is
already stable?

> + /* Deassert #PERST */
> rmw_set(PORT_PERST_OFF, port->base + PORT_PERST);
> gpiod_set_value(reset, 1);
>
> + /* Wait for 100ms after #PERST deassertion (PCIe r2.0, 6.6.1) */
> + msleep(100);

Does this port support speeds greater than 5 GT/s? If so, 6.6.1 says
we need "100ms after Link training completes," not just after
deasserting PERST#.

I'll update this citation to "PCIe r5.0, 6.6.1" to reference the
current spec.

> ret = readl_relaxed_poll_timeout(port->base + PORT_STATUS, stat,
> stat & PORT_STATUS_READY, 100, 250000);
> if (ret < 0) {
> --
> 2.30.2
>