diff mbox series

[V4,27/28] PCI: tegra: Add support for GPIO based PERST#

Message ID 20190516055307.25737-28-mmaddireddy@nvidia.com (mailing list archive)
State Superseded, archived
Headers show
Series Enable Tegra PCIe root port features | expand

Commit Message

Manikanta Maddireddy May 16, 2019, 5:53 a.m. UTC
Add support for GPIO based PERST# signal. GPIO number comes from per port
PCIe device tree node.

Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
---
V4: Using devm_gpiod_get_from_of_node() to get reset-gpios

V3: Using helper function to get reset-gpios

V2: Using standard "reset-gpio" property

 drivers/pci/controller/pci-tegra.c | 41 +++++++++++++++++++++++++-----
 1 file changed, 35 insertions(+), 6 deletions(-)

Comments

Thierry Reding June 4, 2019, 1:22 p.m. UTC | #1
On Thu, May 16, 2019 at 11:23:06AM +0530, Manikanta Maddireddy wrote:
> Add support for GPIO based PERST# signal. GPIO number comes from per port
> PCIe device tree node.
> 
> Signed-off-by: Manikanta Maddireddy <mmaddireddy@nvidia.com>
> ---
> V4: Using devm_gpiod_get_from_of_node() to get reset-gpios
> 
> V3: Using helper function to get reset-gpios
> 
> V2: Using standard "reset-gpio" property
> 
>  drivers/pci/controller/pci-tegra.c | 41 +++++++++++++++++++++++++-----
>  1 file changed, 35 insertions(+), 6 deletions(-)
> 
> diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c
> index 06b99fcbf382..09b4d384ba38 100644
> --- a/drivers/pci/controller/pci-tegra.c
> +++ b/drivers/pci/controller/pci-tegra.c
> @@ -17,6 +17,7 @@
>  #include <linux/debugfs.h>
>  #include <linux/delay.h>
>  #include <linux/export.h>
> +#include <linux/gpio.h>
>  #include <linux/interrupt.h>
>  #include <linux/iopoll.h>
>  #include <linux/irq.h>
> @@ -400,6 +401,8 @@ struct tegra_pcie_port {
>  	unsigned int lanes;
>  
>  	struct phy **phys;
> +
> +	struct gpio_desc *reset_gpiod;

Nit: I'd leave away the "d" at the end there. Or perhaps even the _gpio
suffix entirely. But it's fine either way.

>  };
>  
>  struct tegra_pcie_bus {
> @@ -583,15 +586,23 @@ static void tegra_pcie_port_reset(struct tegra_pcie_port *port)
>  	unsigned long value;
>  
>  	/* pulse reset signal */
> -	value = afi_readl(port->pcie, ctrl);
> -	value &= ~AFI_PEX_CTRL_RST;
> -	afi_writel(port->pcie, value, ctrl);
> +	if (port->reset_gpiod) {
> +		gpiod_set_value(port->reset_gpiod, 0);

So is this actually deasserting the reset pin, or is it asserting a
low-active reset? I think it's the latter, because ...

> +	} else {
> +		value = afi_readl(port->pcie, ctrl);
> +		value &= ~AFI_PEX_CTRL_RST;
> +		afi_writel(port->pcie, value, ctrl);
> +	}
>  
>  	usleep_range(1000, 2000);
>  
> -	value = afi_readl(port->pcie, ctrl);
> -	value |= AFI_PEX_CTRL_RST;
> -	afi_writel(port->pcie, value, ctrl);
> +	if (port->reset_gpiod) {
> +		gpiod_set_value(port->reset_gpiod, 1);

After this the port should be functional, right? I think it'd be better
to reverse the logic here and move the polarity of the GPIO into device
tree. gpiod_set_value() takes care of inverting the level internally if
the GPIO is marked as low-active in DT.

The end result is obviously the same, but it makes the usage much
clearer. If somebody want to write a DT for their board, they will look
at the schematics and see a low-active reset line and may be tempted to
describe it as such in DT, but with your current code that would be
exactly the wrong way around.

> +	} else {
> +		value = afi_readl(port->pcie, ctrl);
> +		value |= AFI_PEX_CTRL_RST;
> +		afi_writel(port->pcie, value, ctrl);
> +	}
>  }
>  
>  static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port)
> @@ -2238,6 +2249,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
>  		struct tegra_pcie_port *rp;
>  		unsigned int index;
>  		u32 value;
> +		char *label;
>  
>  		err = of_pci_get_devfn(port);
>  		if (err < 0) {
> @@ -2296,6 +2308,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
>  		if (IS_ERR(rp->base))
>  			return PTR_ERR(rp->base);
>  
> +		label = kasprintf(GFP_KERNEL, "pex-reset-%u", index);

devm_kasprintf()?

Thierry

> +		if (!label) {
> +			dev_err(dev, "failed to create reset GPIO label\n");
> +			return -ENOMEM;
> +		}
> +
> +		rp->reset_gpiod = devm_gpiod_get_from_of_node(dev, port,
> +							      "reset-gpios", 0,
> +							      GPIOD_OUT_LOW,
> +							      label);
> +		kfree(label);
> +		if (IS_ERR(rp->reset_gpiod)) {
> +			err = PTR_ERR(rp->reset_gpiod);
> +			dev_err(dev, "failed to get reset GPIO: %d\n", err);
> +			return err;
> +		}
> +
>  		list_add_tail(&rp->list, &pcie->ports);
>  	}
>  
> -- 
> 2.17.1
>
Lorenzo Pieralisi June 13, 2019, 3:24 p.m. UTC | #2
On Tue, Jun 04, 2019 at 03:22:33PM +0200, Thierry Reding wrote:

[...]

> > +	} else {
> > +		value = afi_readl(port->pcie, ctrl);
> > +		value &= ~AFI_PEX_CTRL_RST;
> > +		afi_writel(port->pcie, value, ctrl);
> > +	}
> >  
> >  	usleep_range(1000, 2000);
> >  
> > -	value = afi_readl(port->pcie, ctrl);
> > -	value |= AFI_PEX_CTRL_RST;
> > -	afi_writel(port->pcie, value, ctrl);
> > +	if (port->reset_gpiod) {
> > +		gpiod_set_value(port->reset_gpiod, 1);
> 
> After this the port should be functional, right? I think it'd be better
> to reverse the logic here and move the polarity of the GPIO into device
> tree. gpiod_set_value() takes care of inverting the level internally if
> the GPIO is marked as low-active in DT.
> 
> The end result is obviously the same, but it makes the usage much
> clearer. If somebody want to write a DT for their board, they will look
> at the schematics and see a low-active reset line and may be tempted to
> describe it as such in DT, but with your current code that would be
> exactly the wrong way around.

I agree with Thierry here, you should change the logic.

Question: what's the advantage of adding GPIO reset support if that's
architected already in port registers ? I am pretty sure there is a
reason behind it (and forgive me the dumb question) and I would like to
have it written in the commit log.

Thanks,
Lorenzo

> > +	} else {
> > +		value = afi_readl(port->pcie, ctrl);
> > +		value |= AFI_PEX_CTRL_RST;
> > +		afi_writel(port->pcie, value, ctrl);
> > +	}
> >  }
> >  
> >  static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port)
> > @@ -2238,6 +2249,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
> >  		struct tegra_pcie_port *rp;
> >  		unsigned int index;
> >  		u32 value;
> > +		char *label;
> >  
> >  		err = of_pci_get_devfn(port);
> >  		if (err < 0) {
> > @@ -2296,6 +2308,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
> >  		if (IS_ERR(rp->base))
> >  			return PTR_ERR(rp->base);
> >  
> > +		label = kasprintf(GFP_KERNEL, "pex-reset-%u", index);
> 
> devm_kasprintf()?
> 
> Thierry
> 
> > +		if (!label) {
> > +			dev_err(dev, "failed to create reset GPIO label\n");
> > +			return -ENOMEM;
> > +		}
> > +
> > +		rp->reset_gpiod = devm_gpiod_get_from_of_node(dev, port,
> > +							      "reset-gpios", 0,
> > +							      GPIOD_OUT_LOW,
> > +							      label);
> > +		kfree(label);
> > +		if (IS_ERR(rp->reset_gpiod)) {
> > +			err = PTR_ERR(rp->reset_gpiod);
> > +			dev_err(dev, "failed to get reset GPIO: %d\n", err);
> > +			return err;
> > +		}
> > +
> >  		list_add_tail(&rp->list, &pcie->ports);
> >  	}
> >  
> > -- 
> > 2.17.1
> >
Manikanta Maddireddy June 14, 2019, 10:37 a.m. UTC | #3
On 13-Jun-19 8:54 PM, Lorenzo Pieralisi wrote:
> On Tue, Jun 04, 2019 at 03:22:33PM +0200, Thierry Reding wrote:
>
> [...]
>
>>> +	} else {
>>> +		value = afi_readl(port->pcie, ctrl);
>>> +		value &= ~AFI_PEX_CTRL_RST;
>>> +		afi_writel(port->pcie, value, ctrl);
>>> +	}
>>>  
>>>  	usleep_range(1000, 2000);
>>>  
>>> -	value = afi_readl(port->pcie, ctrl);
>>> -	value |= AFI_PEX_CTRL_RST;
>>> -	afi_writel(port->pcie, value, ctrl);
>>> +	if (port->reset_gpiod) {
>>> +		gpiod_set_value(port->reset_gpiod, 1);
>> After this the port should be functional, right? I think it'd be better
>> to reverse the logic here and move the polarity of the GPIO into device
>> tree. gpiod_set_value() takes care of inverting the level internally if
>> the GPIO is marked as low-active in DT.
>>
>> The end result is obviously the same, but it makes the usage much
>> clearer. If somebody want to write a DT for their board, they will look
>> at the schematics and see a low-active reset line and may be tempted to
>> describe it as such in DT, but with your current code that would be
>> exactly the wrong way around.
> I agree with Thierry here, you should change the logic.
>
> Question: what's the advantage of adding GPIO reset support if that's
> architected already in port registers ? I am pretty sure there is a
> reason behind it (and forgive me the dumb question) and I would like to
> have it written in the commit log.
>
> Thanks,
> Lorenzo

Each PCIe controller has a dedicated SFIO pin to support PERST# signal. Port register
can control only this particular SFIO pin. However, in one of the Nvidia platform,
instead of using PCIe SFIO pin, different gpio is routed PCIe slot. This happened
because of a confusion in IO ball naming convention. To support this particular
platform, driver has provide gpio support. I will update the commit log in V5.

Manikanta

>
>>> +	} else {
>>> +		value = afi_readl(port->pcie, ctrl);
>>> +		value |= AFI_PEX_CTRL_RST;
>>> +		afi_writel(port->pcie, value, ctrl);
>>> +	}
>>>  }
>>>  
>>>  static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port)
>>> @@ -2238,6 +2249,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
>>>  		struct tegra_pcie_port *rp;
>>>  		unsigned int index;
>>>  		u32 value;
>>> +		char *label;
>>>  
>>>  		err = of_pci_get_devfn(port);
>>>  		if (err < 0) {
>>> @@ -2296,6 +2308,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
>>>  		if (IS_ERR(rp->base))
>>>  			return PTR_ERR(rp->base);
>>>  
>>> +		label = kasprintf(GFP_KERNEL, "pex-reset-%u", index);
>> devm_kasprintf()?
>>
>> Thierry
>>
>>> +		if (!label) {
>>> +			dev_err(dev, "failed to create reset GPIO label\n");
>>> +			return -ENOMEM;
>>> +		}
>>> +
>>> +		rp->reset_gpiod = devm_gpiod_get_from_of_node(dev, port,
>>> +							      "reset-gpios", 0,
>>> +							      GPIOD_OUT_LOW,
>>> +							      label);
>>> +		kfree(label);
>>> +		if (IS_ERR(rp->reset_gpiod)) {
>>> +			err = PTR_ERR(rp->reset_gpiod);
>>> +			dev_err(dev, "failed to get reset GPIO: %d\n", err);
>>> +			return err;
>>> +		}
>>> +
>>>  		list_add_tail(&rp->list, &pcie->ports);
>>>  	}
>>>  
>>> -- 
>>> 2.17.1
>>>
>
Lorenzo Pieralisi June 14, 2019, 2:32 p.m. UTC | #4
On Fri, Jun 14, 2019 at 04:07:35PM +0530, Manikanta Maddireddy wrote:
> 
> 
> On 13-Jun-19 8:54 PM, Lorenzo Pieralisi wrote:
> > On Tue, Jun 04, 2019 at 03:22:33PM +0200, Thierry Reding wrote:
> >
> > [...]
> >
> >>> +	} else {
> >>> +		value = afi_readl(port->pcie, ctrl);
> >>> +		value &= ~AFI_PEX_CTRL_RST;
> >>> +		afi_writel(port->pcie, value, ctrl);
> >>> +	}
> >>>  
> >>>  	usleep_range(1000, 2000);
> >>>  
> >>> -	value = afi_readl(port->pcie, ctrl);
> >>> -	value |= AFI_PEX_CTRL_RST;
> >>> -	afi_writel(port->pcie, value, ctrl);
> >>> +	if (port->reset_gpiod) {
> >>> +		gpiod_set_value(port->reset_gpiod, 1);
> >> After this the port should be functional, right? I think it'd be better
> >> to reverse the logic here and move the polarity of the GPIO into device
> >> tree. gpiod_set_value() takes care of inverting the level internally if
> >> the GPIO is marked as low-active in DT.
> >>
> >> The end result is obviously the same, but it makes the usage much
> >> clearer. If somebody want to write a DT for their board, they will look
> >> at the schematics and see a low-active reset line and may be tempted to
> >> describe it as such in DT, but with your current code that would be
> >> exactly the wrong way around.
> > I agree with Thierry here, you should change the logic.
> >
> > Question: what's the advantage of adding GPIO reset support if that's
> > architected already in port registers ? I am pretty sure there is a
> > reason behind it (and forgive me the dumb question) and I would like to
> > have it written in the commit log.
> >
> > Thanks,
> > Lorenzo
> 
> Each PCIe controller has a dedicated SFIO pin to support PERST#
> signal. Port register can control only this particular SFIO pin.
> However, in one of the Nvidia platform, instead of using PCIe SFIO
> pin, different gpio is routed PCIe slot. This happened because of a
> confusion in IO ball naming convention. To support this particular
> platform, driver has provide gpio support. I will update the commit
> log in V5.

What happens on that platform where you trigger reset through a port
register with :

value = afi_readl(port->pcie, ctrl);
value |= AFI_PEX_CTRL_RST;
afi_writel(port->pcie, value, ctrl);

(imagine the DT is not updated for instance or on current
mainline) ?

Lorenzo

> Manikanta
> 
> >
> >>> +	} else {
> >>> +		value = afi_readl(port->pcie, ctrl);
> >>> +		value |= AFI_PEX_CTRL_RST;
> >>> +		afi_writel(port->pcie, value, ctrl);
> >>> +	}
> >>>  }
> >>>  
> >>>  static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port)
> >>> @@ -2238,6 +2249,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
> >>>  		struct tegra_pcie_port *rp;
> >>>  		unsigned int index;
> >>>  		u32 value;
> >>> +		char *label;
> >>>  
> >>>  		err = of_pci_get_devfn(port);
> >>>  		if (err < 0) {
> >>> @@ -2296,6 +2308,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
> >>>  		if (IS_ERR(rp->base))
> >>>  			return PTR_ERR(rp->base);
> >>>  
> >>> +		label = kasprintf(GFP_KERNEL, "pex-reset-%u", index);
> >> devm_kasprintf()?
> >>
> >> Thierry
> >>
> >>> +		if (!label) {
> >>> +			dev_err(dev, "failed to create reset GPIO label\n");
> >>> +			return -ENOMEM;
> >>> +		}
> >>> +
> >>> +		rp->reset_gpiod = devm_gpiod_get_from_of_node(dev, port,
> >>> +							      "reset-gpios", 0,
> >>> +							      GPIOD_OUT_LOW,
> >>> +							      label);
> >>> +		kfree(label);
> >>> +		if (IS_ERR(rp->reset_gpiod)) {
> >>> +			err = PTR_ERR(rp->reset_gpiod);
> >>> +			dev_err(dev, "failed to get reset GPIO: %d\n", err);
> >>> +			return err;
> >>> +		}
> >>> +
> >>>  		list_add_tail(&rp->list, &pcie->ports);
> >>>  	}
> >>>  
> >>> -- 
> >>> 2.17.1
> >>>
> >
>
Manikanta Maddireddy June 14, 2019, 2:38 p.m. UTC | #5
On 14-Jun-19 8:02 PM, Lorenzo Pieralisi wrote:
> On Fri, Jun 14, 2019 at 04:07:35PM +0530, Manikanta Maddireddy wrote:
>>
>> On 13-Jun-19 8:54 PM, Lorenzo Pieralisi wrote:
>>> On Tue, Jun 04, 2019 at 03:22:33PM +0200, Thierry Reding wrote:
>>>
>>> [...]
>>>
>>>>> +	} else {
>>>>> +		value = afi_readl(port->pcie, ctrl);
>>>>> +		value &= ~AFI_PEX_CTRL_RST;
>>>>> +		afi_writel(port->pcie, value, ctrl);
>>>>> +	}
>>>>>  
>>>>>  	usleep_range(1000, 2000);
>>>>>  
>>>>> -	value = afi_readl(port->pcie, ctrl);
>>>>> -	value |= AFI_PEX_CTRL_RST;
>>>>> -	afi_writel(port->pcie, value, ctrl);
>>>>> +	if (port->reset_gpiod) {
>>>>> +		gpiod_set_value(port->reset_gpiod, 1);
>>>> After this the port should be functional, right? I think it'd be better
>>>> to reverse the logic here and move the polarity of the GPIO into device
>>>> tree. gpiod_set_value() takes care of inverting the level internally if
>>>> the GPIO is marked as low-active in DT.
>>>>
>>>> The end result is obviously the same, but it makes the usage much
>>>> clearer. If somebody want to write a DT for their board, they will look
>>>> at the schematics and see a low-active reset line and may be tempted to
>>>> describe it as such in DT, but with your current code that would be
>>>> exactly the wrong way around.
>>> I agree with Thierry here, you should change the logic.
>>>
>>> Question: what's the advantage of adding GPIO reset support if that's
>>> architected already in port registers ? I am pretty sure there is a
>>> reason behind it (and forgive me the dumb question) and I would like to
>>> have it written in the commit log.
>>>
>>> Thanks,
>>> Lorenzo
>> Each PCIe controller has a dedicated SFIO pin to support PERST#
>> signal. Port register can control only this particular SFIO pin.
>> However, in one of the Nvidia platform, instead of using PCIe SFIO
>> pin, different gpio is routed PCIe slot. This happened because of a
>> confusion in IO ball naming convention. To support this particular
>> platform, driver has provide gpio support. I will update the commit
>> log in V5.
> What happens on that platform where you trigger reset through a port
> register with :
>
> value = afi_readl(port->pcie, ctrl);
> value |= AFI_PEX_CTRL_RST;
> afi_writel(port->pcie, value, ctrl);
>
> (imagine the DT is not updated for instance or on current
> mainline) ?
>
> Lorenzo

Lets take an example of PCIe controller-0, SFIO ball name which is controlled
by the port-0 register is PEX_L0_RST. It will deassert PEX_L0_RST SFIO line
but it doesn't go to PCIe slot, so fundamental reset(PERST# deassert) is not
applied to the endpoint connected to that slot.

Manikanta

>> Manikanta
>>
>>>>> +	} else {
>>>>> +		value = afi_readl(port->pcie, ctrl);
>>>>> +		value |= AFI_PEX_CTRL_RST;
>>>>> +		afi_writel(port->pcie, value, ctrl);
>>>>> +	}
>>>>>  }
>>>>>  
>>>>>  static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port)
>>>>> @@ -2238,6 +2249,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
>>>>>  		struct tegra_pcie_port *rp;
>>>>>  		unsigned int index;
>>>>>  		u32 value;
>>>>> +		char *label;
>>>>>  
>>>>>  		err = of_pci_get_devfn(port);
>>>>>  		if (err < 0) {
>>>>> @@ -2296,6 +2308,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
>>>>>  		if (IS_ERR(rp->base))
>>>>>  			return PTR_ERR(rp->base);
>>>>>  
>>>>> +		label = kasprintf(GFP_KERNEL, "pex-reset-%u", index);
>>>> devm_kasprintf()?
>>>>
>>>> Thierry
>>>>
>>>>> +		if (!label) {
>>>>> +			dev_err(dev, "failed to create reset GPIO label\n");
>>>>> +			return -ENOMEM;
>>>>> +		}
>>>>> +
>>>>> +		rp->reset_gpiod = devm_gpiod_get_from_of_node(dev, port,
>>>>> +							      "reset-gpios", 0,
>>>>> +							      GPIOD_OUT_LOW,
>>>>> +							      label);
>>>>> +		kfree(label);
>>>>> +		if (IS_ERR(rp->reset_gpiod)) {
>>>>> +			err = PTR_ERR(rp->reset_gpiod);
>>>>> +			dev_err(dev, "failed to get reset GPIO: %d\n", err);
>>>>> +			return err;
>>>>> +		}
>>>>> +
>>>>>  		list_add_tail(&rp->list, &pcie->ports);
>>>>>  	}
>>>>>  
>>>>> -- 
>>>>> 2.17.1
>>>>>
Lorenzo Pieralisi June 14, 2019, 2:50 p.m. UTC | #6
On Fri, Jun 14, 2019 at 08:08:26PM +0530, Manikanta Maddireddy wrote:
> 
> 
> On 14-Jun-19 8:02 PM, Lorenzo Pieralisi wrote:
> > On Fri, Jun 14, 2019 at 04:07:35PM +0530, Manikanta Maddireddy wrote:
> >>
> >> On 13-Jun-19 8:54 PM, Lorenzo Pieralisi wrote:
> >>> On Tue, Jun 04, 2019 at 03:22:33PM +0200, Thierry Reding wrote:
> >>>
> >>> [...]
> >>>
> >>>>> +	} else {
> >>>>> +		value = afi_readl(port->pcie, ctrl);
> >>>>> +		value &= ~AFI_PEX_CTRL_RST;
> >>>>> +		afi_writel(port->pcie, value, ctrl);
> >>>>> +	}
> >>>>>  
> >>>>>  	usleep_range(1000, 2000);
> >>>>>  
> >>>>> -	value = afi_readl(port->pcie, ctrl);
> >>>>> -	value |= AFI_PEX_CTRL_RST;
> >>>>> -	afi_writel(port->pcie, value, ctrl);
> >>>>> +	if (port->reset_gpiod) {
> >>>>> +		gpiod_set_value(port->reset_gpiod, 1);
> >>>> After this the port should be functional, right? I think it'd be better
> >>>> to reverse the logic here and move the polarity of the GPIO into device
> >>>> tree. gpiod_set_value() takes care of inverting the level internally if
> >>>> the GPIO is marked as low-active in DT.
> >>>>
> >>>> The end result is obviously the same, but it makes the usage much
> >>>> clearer. If somebody want to write a DT for their board, they will look
> >>>> at the schematics and see a low-active reset line and may be tempted to
> >>>> describe it as such in DT, but with your current code that would be
> >>>> exactly the wrong way around.
> >>> I agree with Thierry here, you should change the logic.
> >>>
> >>> Question: what's the advantage of adding GPIO reset support if that's
> >>> architected already in port registers ? I am pretty sure there is a
> >>> reason behind it (and forgive me the dumb question) and I would like to
> >>> have it written in the commit log.
> >>>
> >>> Thanks,
> >>> Lorenzo
> >> Each PCIe controller has a dedicated SFIO pin to support PERST#
> >> signal. Port register can control only this particular SFIO pin.
> >> However, in one of the Nvidia platform, instead of using PCIe SFIO
> >> pin, different gpio is routed PCIe slot. This happened because of a
> >> confusion in IO ball naming convention. To support this particular
> >> platform, driver has provide gpio support. I will update the commit
> >> log in V5.
> > What happens on that platform where you trigger reset through a port
> > register with :
> >
> > value = afi_readl(port->pcie, ctrl);
> > value |= AFI_PEX_CTRL_RST;
> > afi_writel(port->pcie, value, ctrl);
> >
> > (imagine the DT is not updated for instance or on current
> > mainline) ?
> >
> > Lorenzo
> 
> Lets take an example of PCIe controller-0, SFIO ball name which is
> controlled by the port-0 register is PEX_L0_RST. It will deassert
> PEX_L0_RST SFIO line but it doesn't go to PCIe slot, so fundamental
> reset(PERST# deassert) is not applied to the endpoint connected to
> that slot.

That's the point I am making, if the reset is not applied nothing
will work (provided PEX_L0_RST does not do any damage either).

For the platform in question you should make reset-gpios mandatory and
fail if not present (instead of toggling the wrong reset line) there is
no chance the driver can work without that property AFAICS.

Lorenzo

> 
> 
> Manikanta
> 
> >> Manikanta
> >>
> >>>>> +	} else {
> >>>>> +		value = afi_readl(port->pcie, ctrl);
> >>>>> +		value |= AFI_PEX_CTRL_RST;
> >>>>> +		afi_writel(port->pcie, value, ctrl);
> >>>>> +	}
> >>>>>  }
> >>>>>  
> >>>>>  static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port)
> >>>>> @@ -2238,6 +2249,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
> >>>>>  		struct tegra_pcie_port *rp;
> >>>>>  		unsigned int index;
> >>>>>  		u32 value;
> >>>>> +		char *label;
> >>>>>  
> >>>>>  		err = of_pci_get_devfn(port);
> >>>>>  		if (err < 0) {
> >>>>> @@ -2296,6 +2308,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
> >>>>>  		if (IS_ERR(rp->base))
> >>>>>  			return PTR_ERR(rp->base);
> >>>>>  
> >>>>> +		label = kasprintf(GFP_KERNEL, "pex-reset-%u", index);
> >>>> devm_kasprintf()?
> >>>>
> >>>> Thierry
> >>>>
> >>>>> +		if (!label) {
> >>>>> +			dev_err(dev, "failed to create reset GPIO label\n");
> >>>>> +			return -ENOMEM;
> >>>>> +		}
> >>>>> +
> >>>>> +		rp->reset_gpiod = devm_gpiod_get_from_of_node(dev, port,
> >>>>> +							      "reset-gpios", 0,
> >>>>> +							      GPIOD_OUT_LOW,
> >>>>> +							      label);
> >>>>> +		kfree(label);
> >>>>> +		if (IS_ERR(rp->reset_gpiod)) {
> >>>>> +			err = PTR_ERR(rp->reset_gpiod);
> >>>>> +			dev_err(dev, "failed to get reset GPIO: %d\n", err);
> >>>>> +			return err;
> >>>>> +		}
> >>>>> +
> >>>>>  		list_add_tail(&rp->list, &pcie->ports);
> >>>>>  	}
> >>>>>  
> >>>>> -- 
> >>>>> 2.17.1
> >>>>>
>
Manikanta Maddireddy June 14, 2019, 2:56 p.m. UTC | #7
On 14-Jun-19 8:20 PM, Lorenzo Pieralisi wrote:
> On Fri, Jun 14, 2019 at 08:08:26PM +0530, Manikanta Maddireddy wrote:
>>
>> On 14-Jun-19 8:02 PM, Lorenzo Pieralisi wrote:
>>> On Fri, Jun 14, 2019 at 04:07:35PM +0530, Manikanta Maddireddy wrote:
>>>> On 13-Jun-19 8:54 PM, Lorenzo Pieralisi wrote:
>>>>> On Tue, Jun 04, 2019 at 03:22:33PM +0200, Thierry Reding wrote:
>>>>>
>>>>> [...]
>>>>>
>>>>>>> +	} else {
>>>>>>> +		value = afi_readl(port->pcie, ctrl);
>>>>>>> +		value &= ~AFI_PEX_CTRL_RST;
>>>>>>> +		afi_writel(port->pcie, value, ctrl);
>>>>>>> +	}
>>>>>>>  
>>>>>>>  	usleep_range(1000, 2000);
>>>>>>>  
>>>>>>> -	value = afi_readl(port->pcie, ctrl);
>>>>>>> -	value |= AFI_PEX_CTRL_RST;
>>>>>>> -	afi_writel(port->pcie, value, ctrl);
>>>>>>> +	if (port->reset_gpiod) {
>>>>>>> +		gpiod_set_value(port->reset_gpiod, 1);
>>>>>> After this the port should be functional, right? I think it'd be better
>>>>>> to reverse the logic here and move the polarity of the GPIO into device
>>>>>> tree. gpiod_set_value() takes care of inverting the level internally if
>>>>>> the GPIO is marked as low-active in DT.
>>>>>>
>>>>>> The end result is obviously the same, but it makes the usage much
>>>>>> clearer. If somebody want to write a DT for their board, they will look
>>>>>> at the schematics and see a low-active reset line and may be tempted to
>>>>>> describe it as such in DT, but with your current code that would be
>>>>>> exactly the wrong way around.
>>>>> I agree with Thierry here, you should change the logic.
>>>>>
>>>>> Question: what's the advantage of adding GPIO reset support if that's
>>>>> architected already in port registers ? I am pretty sure there is a
>>>>> reason behind it (and forgive me the dumb question) and I would like to
>>>>> have it written in the commit log.
>>>>>
>>>>> Thanks,
>>>>> Lorenzo
>>>> Each PCIe controller has a dedicated SFIO pin to support PERST#
>>>> signal. Port register can control only this particular SFIO pin.
>>>> However, in one of the Nvidia platform, instead of using PCIe SFIO
>>>> pin, different gpio is routed PCIe slot. This happened because of a
>>>> confusion in IO ball naming convention. To support this particular
>>>> platform, driver has provide gpio support. I will update the commit
>>>> log in V5.
>>> What happens on that platform where you trigger reset through a port
>>> register with :
>>>
>>> value = afi_readl(port->pcie, ctrl);
>>> value |= AFI_PEX_CTRL_RST;
>>> afi_writel(port->pcie, value, ctrl);
>>>
>>> (imagine the DT is not updated for instance or on current
>>> mainline) ?
>>>
>>> Lorenzo
>> Lets take an example of PCIe controller-0, SFIO ball name which is
>> controlled by the port-0 register is PEX_L0_RST. It will deassert
>> PEX_L0_RST SFIO line but it doesn't go to PCIe slot, so fundamental
>> reset(PERST# deassert) is not applied to the endpoint connected to
>> that slot.
> That's the point I am making, if the reset is not applied nothing
> will work (provided PEX_L0_RST does not do any damage either).
>
> For the platform in question you should make reset-gpios mandatory and
> fail if not present (instead of toggling the wrong reset line) there is
> no chance the driver can work without that property AFAICS.
>
> Lorenzo

In upstream kernel, device tree file is not available for the platform in
question. That is the reason for not send device tree patch as part of 
this series.

Manikanta

>>
>> Manikanta
>>
>>>> Manikanta
>>>>
>>>>>>> +	} else {
>>>>>>> +		value = afi_readl(port->pcie, ctrl);
>>>>>>> +		value |= AFI_PEX_CTRL_RST;
>>>>>>> +		afi_writel(port->pcie, value, ctrl);
>>>>>>> +	}
>>>>>>>  }
>>>>>>>  
>>>>>>>  static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port)
>>>>>>> @@ -2238,6 +2249,7 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
>>>>>>>  		struct tegra_pcie_port *rp;
>>>>>>>  		unsigned int index;
>>>>>>>  		u32 value;
>>>>>>> +		char *label;
>>>>>>>  
>>>>>>>  		err = of_pci_get_devfn(port);
>>>>>>>  		if (err < 0) {
>>>>>>> @@ -2296,6 +2308,23 @@ static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
>>>>>>>  		if (IS_ERR(rp->base))
>>>>>>>  			return PTR_ERR(rp->base);
>>>>>>>  
>>>>>>> +		label = kasprintf(GFP_KERNEL, "pex-reset-%u", index);
>>>>>> devm_kasprintf()?
>>>>>>
>>>>>> Thierry
>>>>>>
>>>>>>> +		if (!label) {
>>>>>>> +			dev_err(dev, "failed to create reset GPIO label\n");
>>>>>>> +			return -ENOMEM;
>>>>>>> +		}
>>>>>>> +
>>>>>>> +		rp->reset_gpiod = devm_gpiod_get_from_of_node(dev, port,
>>>>>>> +							      "reset-gpios", 0,
>>>>>>> +							      GPIOD_OUT_LOW,
>>>>>>> +							      label);
>>>>>>> +		kfree(label);
>>>>>>> +		if (IS_ERR(rp->reset_gpiod)) {
>>>>>>> +			err = PTR_ERR(rp->reset_gpiod);
>>>>>>> +			dev_err(dev, "failed to get reset GPIO: %d\n", err);
>>>>>>> +			return err;
>>>>>>> +		}
>>>>>>> +
>>>>>>>  		list_add_tail(&rp->list, &pcie->ports);
>>>>>>>  	}
>>>>>>>  
>>>>>>> -- 
>>>>>>> 2.17.1
>>>>>>>
Thierry Reding June 14, 2019, 3:23 p.m. UTC | #8
On Fri, Jun 14, 2019 at 03:50:23PM +0100, Lorenzo Pieralisi wrote:
> On Fri, Jun 14, 2019 at 08:08:26PM +0530, Manikanta Maddireddy wrote:
> > 
> > 
> > On 14-Jun-19 8:02 PM, Lorenzo Pieralisi wrote:
> > > On Fri, Jun 14, 2019 at 04:07:35PM +0530, Manikanta Maddireddy wrote:
> > >>
> > >> On 13-Jun-19 8:54 PM, Lorenzo Pieralisi wrote:
> > >>> On Tue, Jun 04, 2019 at 03:22:33PM +0200, Thierry Reding wrote:
> > >>>
> > >>> [...]
> > >>>
> > >>>>> +	} else {
> > >>>>> +		value = afi_readl(port->pcie, ctrl);
> > >>>>> +		value &= ~AFI_PEX_CTRL_RST;
> > >>>>> +		afi_writel(port->pcie, value, ctrl);
> > >>>>> +	}
> > >>>>>  
> > >>>>>  	usleep_range(1000, 2000);
> > >>>>>  
> > >>>>> -	value = afi_readl(port->pcie, ctrl);
> > >>>>> -	value |= AFI_PEX_CTRL_RST;
> > >>>>> -	afi_writel(port->pcie, value, ctrl);
> > >>>>> +	if (port->reset_gpiod) {
> > >>>>> +		gpiod_set_value(port->reset_gpiod, 1);
> > >>>> After this the port should be functional, right? I think it'd be better
> > >>>> to reverse the logic here and move the polarity of the GPIO into device
> > >>>> tree. gpiod_set_value() takes care of inverting the level internally if
> > >>>> the GPIO is marked as low-active in DT.
> > >>>>
> > >>>> The end result is obviously the same, but it makes the usage much
> > >>>> clearer. If somebody want to write a DT for their board, they will look
> > >>>> at the schematics and see a low-active reset line and may be tempted to
> > >>>> describe it as such in DT, but with your current code that would be
> > >>>> exactly the wrong way around.
> > >>> I agree with Thierry here, you should change the logic.
> > >>>
> > >>> Question: what's the advantage of adding GPIO reset support if that's
> > >>> architected already in port registers ? I am pretty sure there is a
> > >>> reason behind it (and forgive me the dumb question) and I would like to
> > >>> have it written in the commit log.
> > >>>
> > >>> Thanks,
> > >>> Lorenzo
> > >> Each PCIe controller has a dedicated SFIO pin to support PERST#
> > >> signal. Port register can control only this particular SFIO pin.
> > >> However, in one of the Nvidia platform, instead of using PCIe SFIO
> > >> pin, different gpio is routed PCIe slot. This happened because of a
> > >> confusion in IO ball naming convention. To support this particular
> > >> platform, driver has provide gpio support. I will update the commit
> > >> log in V5.
> > > What happens on that platform where you trigger reset through a port
> > > register with :
> > >
> > > value = afi_readl(port->pcie, ctrl);
> > > value |= AFI_PEX_CTRL_RST;
> > > afi_writel(port->pcie, value, ctrl);
> > >
> > > (imagine the DT is not updated for instance or on current
> > > mainline) ?
> > >
> > > Lorenzo
> > 
> > Lets take an example of PCIe controller-0, SFIO ball name which is
> > controlled by the port-0 register is PEX_L0_RST. It will deassert
> > PEX_L0_RST SFIO line but it doesn't go to PCIe slot, so fundamental
> > reset(PERST# deassert) is not applied to the endpoint connected to
> > that slot.
> 
> That's the point I am making, if the reset is not applied nothing
> will work (provided PEX_L0_RST does not do any damage either).
> 
> For the platform in question you should make reset-gpios mandatory and
> fail if not present (instead of toggling the wrong reset line) there is
> no chance the driver can work without that property AFAICS.

I'm not sure I understand what you're proposing here. Are you suggesting
that we put a check in the driver to see if we're running on a specific
board and then fail if the reset-gpios are not there?

Thierry
Lorenzo Pieralisi June 14, 2019, 3:59 p.m. UTC | #9
On Fri, Jun 14, 2019 at 05:23:04PM +0200, Thierry Reding wrote:
> On Fri, Jun 14, 2019 at 03:50:23PM +0100, Lorenzo Pieralisi wrote:
> > On Fri, Jun 14, 2019 at 08:08:26PM +0530, Manikanta Maddireddy wrote:
> > > 
> > > 
> > > On 14-Jun-19 8:02 PM, Lorenzo Pieralisi wrote:
> > > > On Fri, Jun 14, 2019 at 04:07:35PM +0530, Manikanta Maddireddy wrote:
> > > >>
> > > >> On 13-Jun-19 8:54 PM, Lorenzo Pieralisi wrote:
> > > >>> On Tue, Jun 04, 2019 at 03:22:33PM +0200, Thierry Reding wrote:
> > > >>>
> > > >>> [...]
> > > >>>
> > > >>>>> +	} else {
> > > >>>>> +		value = afi_readl(port->pcie, ctrl);
> > > >>>>> +		value &= ~AFI_PEX_CTRL_RST;
> > > >>>>> +		afi_writel(port->pcie, value, ctrl);
> > > >>>>> +	}
> > > >>>>>  
> > > >>>>>  	usleep_range(1000, 2000);
> > > >>>>>  
> > > >>>>> -	value = afi_readl(port->pcie, ctrl);
> > > >>>>> -	value |= AFI_PEX_CTRL_RST;
> > > >>>>> -	afi_writel(port->pcie, value, ctrl);
> > > >>>>> +	if (port->reset_gpiod) {
> > > >>>>> +		gpiod_set_value(port->reset_gpiod, 1);
> > > >>>> After this the port should be functional, right? I think it'd be better
> > > >>>> to reverse the logic here and move the polarity of the GPIO into device
> > > >>>> tree. gpiod_set_value() takes care of inverting the level internally if
> > > >>>> the GPIO is marked as low-active in DT.
> > > >>>>
> > > >>>> The end result is obviously the same, but it makes the usage much
> > > >>>> clearer. If somebody want to write a DT for their board, they will look
> > > >>>> at the schematics and see a low-active reset line and may be tempted to
> > > >>>> describe it as such in DT, but with your current code that would be
> > > >>>> exactly the wrong way around.
> > > >>> I agree with Thierry here, you should change the logic.
> > > >>>
> > > >>> Question: what's the advantage of adding GPIO reset support if that's
> > > >>> architected already in port registers ? I am pretty sure there is a
> > > >>> reason behind it (and forgive me the dumb question) and I would like to
> > > >>> have it written in the commit log.
> > > >>>
> > > >>> Thanks,
> > > >>> Lorenzo
> > > >> Each PCIe controller has a dedicated SFIO pin to support PERST#
> > > >> signal. Port register can control only this particular SFIO pin.
> > > >> However, in one of the Nvidia platform, instead of using PCIe SFIO
> > > >> pin, different gpio is routed PCIe slot. This happened because of a
> > > >> confusion in IO ball naming convention. To support this particular
> > > >> platform, driver has provide gpio support. I will update the commit
> > > >> log in V5.
> > > > What happens on that platform where you trigger reset through a port
> > > > register with :
> > > >
> > > > value = afi_readl(port->pcie, ctrl);
> > > > value |= AFI_PEX_CTRL_RST;
> > > > afi_writel(port->pcie, value, ctrl);
> > > >
> > > > (imagine the DT is not updated for instance or on current
> > > > mainline) ?
> > > >
> > > > Lorenzo
> > > 
> > > Lets take an example of PCIe controller-0, SFIO ball name which is
> > > controlled by the port-0 register is PEX_L0_RST. It will deassert
> > > PEX_L0_RST SFIO line but it doesn't go to PCIe slot, so fundamental
> > > reset(PERST# deassert) is not applied to the endpoint connected to
> > > that slot.
> > 
> > That's the point I am making, if the reset is not applied nothing
> > will work (provided PEX_L0_RST does not do any damage either).
> > 
> > For the platform in question you should make reset-gpios mandatory and
> > fail if not present (instead of toggling the wrong reset line) there is
> > no chance the driver can work without that property AFAICS.
> 
> I'm not sure I understand what you're proposing here. Are you suggesting
> that we put a check in the driver to see if we're running on a specific
> board and then fail if the reset-gpios are not there?

I am just trying to understand what this patch does. By reading it again
it looks like it makes GPIO PERST# reset mandatory for all platforms
supported by this driver (because if the driver does not grab an handle
to the GPIO tegra_pcie_parse_dt() fails), if I read the code correctly,
apologies if not.

Which makes me question the check:

	if (port->reset_gpiod) {
		gpiod_set_value(port->reset_gpiod, 0);

in tegra_pcie_port_reset(), if we are there port->reset_gpiod can't be
NULL or I am missing something and also make:

	} else {
		value = afi_readl(port->pcie, ctrl);
		value &= ~AFI_PEX_CTRL_RST;
		afi_writel(port->pcie, value, ctrl);
	}

path dead code.

Is this GPIO based #PERST a per-platform requirement or you want
to update the driver to always use GPIO based #PERST ?

And if it is a per-platform requirement I assume that a missing
DT property describing the GPIO #PERST should cause a probe failure,
not a fallback to port registers reset (which may have unintended
consequences).

From the commit log it is not clear what this patch does and for what
reason it does it but it should be, let's define it here and update the
log accordingly for everyone's benefit.

Lorenzo
Manikanta Maddireddy June 14, 2019, 4:30 p.m. UTC | #10
On 14-Jun-19 9:29 PM, Lorenzo Pieralisi wrote:
> On Fri, Jun 14, 2019 at 05:23:04PM +0200, Thierry Reding wrote:
>> On Fri, Jun 14, 2019 at 03:50:23PM +0100, Lorenzo Pieralisi wrote:
>>> On Fri, Jun 14, 2019 at 08:08:26PM +0530, Manikanta Maddireddy wrote:
>>>>
>>>> On 14-Jun-19 8:02 PM, Lorenzo Pieralisi wrote:
>>>>> On Fri, Jun 14, 2019 at 04:07:35PM +0530, Manikanta Maddireddy wrote:
>>>>>> On 13-Jun-19 8:54 PM, Lorenzo Pieralisi wrote:
>>>>>>> On Tue, Jun 04, 2019 at 03:22:33PM +0200, Thierry Reding wrote:
>>>>>>>
>>>>>>> [...]
>>>>>>>
>>>>>>>>> +	} else {
>>>>>>>>> +		value = afi_readl(port->pcie, ctrl);
>>>>>>>>> +		value &= ~AFI_PEX_CTRL_RST;
>>>>>>>>> +		afi_writel(port->pcie, value, ctrl);
>>>>>>>>> +	}
>>>>>>>>>  
>>>>>>>>>  	usleep_range(1000, 2000);
>>>>>>>>>  
>>>>>>>>> -	value = afi_readl(port->pcie, ctrl);
>>>>>>>>> -	value |= AFI_PEX_CTRL_RST;
>>>>>>>>> -	afi_writel(port->pcie, value, ctrl);
>>>>>>>>> +	if (port->reset_gpiod) {
>>>>>>>>> +		gpiod_set_value(port->reset_gpiod, 1);
>>>>>>>> After this the port should be functional, right? I think it'd be better
>>>>>>>> to reverse the logic here and move the polarity of the GPIO into device
>>>>>>>> tree. gpiod_set_value() takes care of inverting the level internally if
>>>>>>>> the GPIO is marked as low-active in DT.
>>>>>>>>
>>>>>>>> The end result is obviously the same, but it makes the usage much
>>>>>>>> clearer. If somebody want to write a DT for their board, they will look
>>>>>>>> at the schematics and see a low-active reset line and may be tempted to
>>>>>>>> describe it as such in DT, but with your current code that would be
>>>>>>>> exactly the wrong way around.
>>>>>>> I agree with Thierry here, you should change the logic.
>>>>>>>
>>>>>>> Question: what's the advantage of adding GPIO reset support if that's
>>>>>>> architected already in port registers ? I am pretty sure there is a
>>>>>>> reason behind it (and forgive me the dumb question) and I would like to
>>>>>>> have it written in the commit log.
>>>>>>>
>>>>>>> Thanks,
>>>>>>> Lorenzo
>>>>>> Each PCIe controller has a dedicated SFIO pin to support PERST#
>>>>>> signal. Port register can control only this particular SFIO pin.
>>>>>> However, in one of the Nvidia platform, instead of using PCIe SFIO
>>>>>> pin, different gpio is routed PCIe slot. This happened because of a
>>>>>> confusion in IO ball naming convention. To support this particular
>>>>>> platform, driver has provide gpio support. I will update the commit
>>>>>> log in V5.
>>>>> What happens on that platform where you trigger reset through a port
>>>>> register with :
>>>>>
>>>>> value = afi_readl(port->pcie, ctrl);
>>>>> value |= AFI_PEX_CTRL_RST;
>>>>> afi_writel(port->pcie, value, ctrl);
>>>>>
>>>>> (imagine the DT is not updated for instance or on current
>>>>> mainline) ?
>>>>>
>>>>> Lorenzo
>>>> Lets take an example of PCIe controller-0, SFIO ball name which is
>>>> controlled by the port-0 register is PEX_L0_RST. It will deassert
>>>> PEX_L0_RST SFIO line but it doesn't go to PCIe slot, so fundamental
>>>> reset(PERST# deassert) is not applied to the endpoint connected to
>>>> that slot.
>>> That's the point I am making, if the reset is not applied nothing
>>> will work (provided PEX_L0_RST does not do any damage either).
>>>
>>> For the platform in question you should make reset-gpios mandatory and
>>> fail if not present (instead of toggling the wrong reset line) there is
>>> no chance the driver can work without that property AFAICS.
>> I'm not sure I understand what you're proposing here. Are you suggesting
>> that we put a check in the driver to see if we're running on a specific
>> board and then fail if the reset-gpios are not there?
> I am just trying to understand what this patch does. By reading it again
> it looks like it makes GPIO PERST# reset mandatory for all platforms
> supported by this driver (because if the driver does not grab an handle
> to the GPIO tegra_pcie_parse_dt() fails), if I read the code correctly,
> apologies if not.
>
> Which makes me question the check:
>
> 	if (port->reset_gpiod) {
> 		gpiod_set_value(port->reset_gpiod, 0);
>
> in tegra_pcie_port_reset(), if we are there port->reset_gpiod can't be
> NULL or I am missing something and also make:
>
> 	} else {
> 		value = afi_readl(port->pcie, ctrl);
> 		value &= ~AFI_PEX_CTRL_RST;
> 		afi_writel(port->pcie, value, ctrl);
> 	}
>
> path dead code.
>
> Is this GPIO based #PERST a per-platform requirement or you want
> to update the driver to always use GPIO based #PERST ?
>
> And if it is a per-platform requirement I assume that a missing
> DT property describing the GPIO #PERST should cause a probe failure,
> not a fallback to port registers reset (which may have unintended
> consequences).
>
> From the commit log it is not clear what this patch does and for what
> reason it does it but it should be, let's define it here and update the
> log accordingly for everyone's benefit.
>
> Lorenzo

GPIO based PERST# is per-platform requirement.
If DT prop is not present, then devm_gpiod_get_from_of_node() returns
NULL gpio_desc.

struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
                                         const char *propname, int index,
                                         enum gpiod_flags dflags,
                                         const char *label)
{
        struct gpio_desc *desc;
        unsigned long lflags = 0;
        enum of_gpio_flags flags;
        bool active_low = false;
        bool single_ended = false;
        bool open_drain = false;
        bool transitory = false;
        int ret;

        desc = of_get_named_gpiod_flags(node, propname,
                                        index, &flags);

        if (!desc || IS_ERR(desc)) {
*/* If it is not there, just return NULL */****if (PTR_ERR(desc) == -ENOENT)****return NULL;*
                return desc;
        }
	...

}

Manikanta
Lorenzo Pieralisi June 14, 2019, 4:53 p.m. UTC | #11
On Fri, Jun 14, 2019 at 10:00:49PM +0530, Manikanta Maddireddy wrote:

[...]

> GPIO based PERST# is per-platform requirement.
> If DT prop is not present, then devm_gpiod_get_from_of_node() returns
> NULL gpio_desc.
> 
> struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
>                                          const char *propname, int index,
>                                          enum gpiod_flags dflags,
>                                          const char *label)
> {
>         struct gpio_desc *desc;
>         unsigned long lflags = 0;
>         enum of_gpio_flags flags;
>         bool active_low = false;
>         bool single_ended = false;
>         bool open_drain = false;
>         bool transitory = false;
>         int ret;
> 
>         desc = of_get_named_gpiod_flags(node, propname,
>                                         index, &flags);
> 
>         if (!desc || IS_ERR(desc)) {
> */* If it is not there, just return NULL */****if (PTR_ERR(desc) == -ENOENT)****return NULL;*
>                 return desc;
>         }
> 	...
> 
> }

Ok. My point then is that you have no way to enforce this requirement on
platforms that actually need it, I do not even know if there is a
way you can do it (I was thinking along the lines of using a
compatible string to detect whether the GPIO #PERST reset is mandatory)
but maybe this is not even a SOC property.

Maybe what I am asking is overkill, I just wanted to understand.

I was just asking a question to understand how you handle the case
where a GPIO pin definition is missing in DT for a platform that
actually needs it, the driver will probe but nothing will work.

It would be good to describe this and capture it in the commit log.

Thanks,
Lorenzo
Manikanta Maddireddy June 14, 2019, 5:23 p.m. UTC | #12
On 14-Jun-19 10:23 PM, Lorenzo Pieralisi wrote:
> On Fri, Jun 14, 2019 at 10:00:49PM +0530, Manikanta Maddireddy wrote:
>
> [...]
>
>> GPIO based PERST# is per-platform requirement.
>> If DT prop is not present, then devm_gpiod_get_from_of_node() returns
>> NULL gpio_desc.
>>
>> struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
>>                                          const char *propname, int index,
>>                                          enum gpiod_flags dflags,
>>                                          const char *label)
>> {
>>         struct gpio_desc *desc;
>>         unsigned long lflags = 0;
>>         enum of_gpio_flags flags;
>>         bool active_low = false;
>>         bool single_ended = false;
>>         bool open_drain = false;
>>         bool transitory = false;
>>         int ret;
>>
>>         desc = of_get_named_gpiod_flags(node, propname,
>>                                         index, &flags);
>>
>>         if (!desc || IS_ERR(desc)) {
>> */* If it is not there, just return NULL */****if (PTR_ERR(desc) == -ENOENT)****return NULL;*
>>                 return desc;
>>         }
>> 	...
>>
>> }
> Ok. My point then is that you have no way to enforce this requirement on
> platforms that actually need it, I do not even know if there is a
> way you can do it (I was thinking along the lines of using a
> compatible string to detect whether the GPIO #PERST reset is mandatory)
> but maybe this is not even a SOC property.
>
> Maybe what I am asking is overkill, I just wanted to understand.
>
> I was just asking a question to understand how you handle the case
> where a GPIO pin definition is missing in DT for a platform that
> actually needs it, the driver will probe but nothing will work.
>
> It would be good to describe this and capture it in the commit log.
>
> Thanks,
> Lorenzo

I can't think of a easy way to enforce this requirement. As you said
compatible string is per SOC, so we can't use it for a platform.
This issue is present on only one platform, so it is hard to miss the
DT property. That is the reason for publishing this patch with out this
enforcement in driver.

I thought for changing PERST# to GPIO for all platform, but testing is
a tedious job. Also I don't have Tegra20 and Tegra30 platforms.

Do you want me to drop the patch or update the limitation in the commit
log?

Manikanta
Lorenzo Pieralisi June 17, 2019, 9:48 a.m. UTC | #13
On Fri, Jun 14, 2019 at 10:53:13PM +0530, Manikanta Maddireddy wrote:

[...]

> > Ok. My point then is that you have no way to enforce this requirement on
> > platforms that actually need it, I do not even know if there is a
> > way you can do it (I was thinking along the lines of using a
> > compatible string to detect whether the GPIO #PERST reset is mandatory)
> > but maybe this is not even a SOC property.
> >
> > Maybe what I am asking is overkill, I just wanted to understand.
> >
> > I was just asking a question to understand how you handle the case
> > where a GPIO pin definition is missing in DT for a platform that
> > actually needs it, the driver will probe but nothing will work.
> >
> > It would be good to describe this and capture it in the commit log.
> >
> > Thanks,
> > Lorenzo
> 
> I can't think of a easy way to enforce this requirement. As you said
> compatible string is per SOC, so we can't use it for a platform.
> This issue is present on only one platform, so it is hard to miss the
> DT property. That is the reason for publishing this patch with out this
> enforcement in driver.
> 
> I thought for changing PERST# to GPIO for all platform, but testing is
> a tedious job. Also I don't have Tegra20 and Tegra30 platforms.

I can't help with that.

> Do you want me to drop the patch or update the limitation in the commit
> log?

It is Thierry's call, if he is OK with it fine by me, please do
update the commit log, it will help everybody understand.

Lorenzo
Manikanta Maddireddy June 17, 2019, 10:27 a.m. UTC | #14
On 17-Jun-19 3:18 PM, Lorenzo Pieralisi wrote:
> On Fri, Jun 14, 2019 at 10:53:13PM +0530, Manikanta Maddireddy wrote:
>
> [...]
>
>>> Ok. My point then is that you have no way to enforce this requirement on
>>> platforms that actually need it, I do not even know if there is a
>>> way you can do it (I was thinking along the lines of using a
>>> compatible string to detect whether the GPIO #PERST reset is mandatory)
>>> but maybe this is not even a SOC property.
>>>
>>> Maybe what I am asking is overkill, I just wanted to understand.
>>>
>>> I was just asking a question to understand how you handle the case
>>> where a GPIO pin definition is missing in DT for a platform that
>>> actually needs it, the driver will probe but nothing will work.
>>>
>>> It would be good to describe this and capture it in the commit log.
>>>
>>> Thanks,
>>> Lorenzo
>> I can't think of a easy way to enforce this requirement. As you said
>> compatible string is per SOC, so we can't use it for a platform.
>> This issue is present on only one platform, so it is hard to miss the
>> DT property. That is the reason for publishing this patch with out this
>> enforcement in driver.
>>
>> I thought for changing PERST# to GPIO for all platform, but testing is
>> a tedious job. Also I don't have Tegra20 and Tegra30 platforms.
> I can't help with that.
>
>> Do you want me to drop the patch or update the limitation in the commit
>> log?
> It is Thierry's call, if he is OK with it fine by me, please do
> update the commit log, it will help everybody understand.
>
> Lorenzo

Sure, I will update the commit log in V5.
Please let me know if you completed reviewing this series, I will
send V5 addressing review comments in this patch.

Manikanta
Lorenzo Pieralisi June 17, 2019, 10:39 a.m. UTC | #15
On Mon, Jun 17, 2019 at 03:57:09PM +0530, Manikanta Maddireddy wrote:
> 
> 
> On 17-Jun-19 3:18 PM, Lorenzo Pieralisi wrote:
> > On Fri, Jun 14, 2019 at 10:53:13PM +0530, Manikanta Maddireddy wrote:
> >
> > [...]
> >
> >>> Ok. My point then is that you have no way to enforce this requirement on
> >>> platforms that actually need it, I do not even know if there is a
> >>> way you can do it (I was thinking along the lines of using a
> >>> compatible string to detect whether the GPIO #PERST reset is mandatory)
> >>> but maybe this is not even a SOC property.
> >>>
> >>> Maybe what I am asking is overkill, I just wanted to understand.
> >>>
> >>> I was just asking a question to understand how you handle the case
> >>> where a GPIO pin definition is missing in DT for a platform that
> >>> actually needs it, the driver will probe but nothing will work.
> >>>
> >>> It would be good to describe this and capture it in the commit log.
> >>>
> >>> Thanks,
> >>> Lorenzo
> >> I can't think of a easy way to enforce this requirement. As you said
> >> compatible string is per SOC, so we can't use it for a platform.
> >> This issue is present on only one platform, so it is hard to miss the
> >> DT property. That is the reason for publishing this patch with out this
> >> enforcement in driver.
> >>
> >> I thought for changing PERST# to GPIO for all platform, but testing is
> >> a tedious job. Also I don't have Tegra20 and Tegra30 platforms.
> > I can't help with that.
> >
> >> Do you want me to drop the patch or update the limitation in the commit
> >> log?
> > It is Thierry's call, if he is OK with it fine by me, please do
> > update the commit log, it will help everybody understand.
> >
> > Lorenzo
> 
> Sure, I will update the commit log in V5.
> Please let me know if you completed reviewing this series, I will
> send V5 addressing review comments in this patch.

Post v5, we should be able to get it in v5.3, thanks.

Lorenzo
Thierry Reding June 17, 2019, 11:26 a.m. UTC | #16
On Fri, Jun 14, 2019 at 05:53:53PM +0100, Lorenzo Pieralisi wrote:
> On Fri, Jun 14, 2019 at 10:00:49PM +0530, Manikanta Maddireddy wrote:
> 
> [...]
> 
> > GPIO based PERST# is per-platform requirement.
> > If DT prop is not present, then devm_gpiod_get_from_of_node() returns
> > NULL gpio_desc.
> > 
> > struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
> >                                          const char *propname, int index,
> >                                          enum gpiod_flags dflags,
> >                                          const char *label)
> > {
> >         struct gpio_desc *desc;
> >         unsigned long lflags = 0;
> >         enum of_gpio_flags flags;
> >         bool active_low = false;
> >         bool single_ended = false;
> >         bool open_drain = false;
> >         bool transitory = false;
> >         int ret;
> > 
> >         desc = of_get_named_gpiod_flags(node, propname,
> >                                         index, &flags);
> > 
> >         if (!desc || IS_ERR(desc)) {
> > */* If it is not there, just return NULL */****if (PTR_ERR(desc) == -ENOENT)****return NULL;*
> >                 return desc;
> >         }
> > 	...
> > 
> > }
> 
> Ok. My point then is that you have no way to enforce this requirement on
> platforms that actually need it, I do not even know if there is a
> way you can do it (I was thinking along the lines of using a
> compatible string to detect whether the GPIO #PERST reset is mandatory)
> but maybe this is not even a SOC property.

So this is definitely not an SoC property. From what Manikanta said, the
only reason why we need this is because on one particular design (that
we know of), the PCIe #PERST signal was connected to a pin that does not
originate from the PCIe controller. This happened by accident.

> Maybe what I am asking is overkill, I just wanted to understand.
> 
> I was just asking a question to understand how you handle the case
> where a GPIO pin definition is missing in DT for a platform that
> actually needs it, the driver will probe but nothing will work.

I think we should handle this the way we handle other GPIOs as well. If
some device requires a GPIO to be toggled to put it into or take it out
of reset, then that's something that needs to be described in DT. In
this case it just so happens that typically we don't need to worry about
it because the signals are properly connected and then the PCIe
controller will do the right with the reset. For the one design where
it doesn't work the reset GPIO is a workaround and it's board-specific
knowledge that the engineer writing the DT will have to know. I suspect
that if the same accident happened on another board, then as part of
writing the DT somebody would have noticed that we need an external pin
to be hooked up because the SFIO doesn't work.

> It would be good to describe this and capture it in the commit log.

Agreed. Let's be more verbose about the situation where this is required
and make it very clear that this is a workaround for a board design
mistake that shouldn't be necessary if best practices are followed.

Maybe add that to both the commit message and the device tree bindings
to make it difficult for people to miss.

Thierry
Thierry Reding June 17, 2019, 11:29 a.m. UTC | #17
On Fri, Jun 14, 2019 at 10:53:13PM +0530, Manikanta Maddireddy wrote:
> 
> 
> On 14-Jun-19 10:23 PM, Lorenzo Pieralisi wrote:
> > On Fri, Jun 14, 2019 at 10:00:49PM +0530, Manikanta Maddireddy wrote:
> >
> > [...]
> >
> >> GPIO based PERST# is per-platform requirement.
> >> If DT prop is not present, then devm_gpiod_get_from_of_node() returns
> >> NULL gpio_desc.
> >>
> >> struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
> >>                                          const char *propname, int index,
> >>                                          enum gpiod_flags dflags,
> >>                                          const char *label)
> >> {
> >>         struct gpio_desc *desc;
> >>         unsigned long lflags = 0;
> >>         enum of_gpio_flags flags;
> >>         bool active_low = false;
> >>         bool single_ended = false;
> >>         bool open_drain = false;
> >>         bool transitory = false;
> >>         int ret;
> >>
> >>         desc = of_get_named_gpiod_flags(node, propname,
> >>                                         index, &flags);
> >>
> >>         if (!desc || IS_ERR(desc)) {
> >> */* If it is not there, just return NULL */****if (PTR_ERR(desc) == -ENOENT)****return NULL;*
> >>                 return desc;
> >>         }
> >> 	...
> >>
> >> }
> > Ok. My point then is that you have no way to enforce this requirement on
> > platforms that actually need it, I do not even know if there is a
> > way you can do it (I was thinking along the lines of using a
> > compatible string to detect whether the GPIO #PERST reset is mandatory)
> > but maybe this is not even a SOC property.
> >
> > Maybe what I am asking is overkill, I just wanted to understand.
> >
> > I was just asking a question to understand how you handle the case
> > where a GPIO pin definition is missing in DT for a platform that
> > actually needs it, the driver will probe but nothing will work.
> >
> > It would be good to describe this and capture it in the commit log.
> >
> > Thanks,
> > Lorenzo
> 
> I can't think of a easy way to enforce this requirement. As you said
> compatible string is per SOC, so we can't use it for a platform.
> This issue is present on only one platform, so it is hard to miss the
> DT property. That is the reason for publishing this patch with out this
> enforcement in driver.
> 
> I thought for changing PERST# to GPIO for all platform, but testing is
> a tedious job. Also I don't have Tegra20 and Tegra30 platforms.

Yeah, let's not go that way. The standard way to do this is to use the
SFIO and let the PCIe controller and driver handle this. It's working
just fine on all platforms currently supported upstream. Using direct
GPIO for PERST# is a workaround, so let's not proliferate unless it is
absolutely necessary.

With an updated commit message, this is:

Acked-by: Thierry Reding <treding@nvidia.com>
diff mbox series

Patch

diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c
index 06b99fcbf382..09b4d384ba38 100644
--- a/drivers/pci/controller/pci-tegra.c
+++ b/drivers/pci/controller/pci-tegra.c
@@ -17,6 +17,7 @@ 
 #include <linux/debugfs.h>
 #include <linux/delay.h>
 #include <linux/export.h>
+#include <linux/gpio.h>
 #include <linux/interrupt.h>
 #include <linux/iopoll.h>
 #include <linux/irq.h>
@@ -400,6 +401,8 @@  struct tegra_pcie_port {
 	unsigned int lanes;
 
 	struct phy **phys;
+
+	struct gpio_desc *reset_gpiod;
 };
 
 struct tegra_pcie_bus {
@@ -583,15 +586,23 @@  static void tegra_pcie_port_reset(struct tegra_pcie_port *port)
 	unsigned long value;
 
 	/* pulse reset signal */
-	value = afi_readl(port->pcie, ctrl);
-	value &= ~AFI_PEX_CTRL_RST;
-	afi_writel(port->pcie, value, ctrl);
+	if (port->reset_gpiod) {
+		gpiod_set_value(port->reset_gpiod, 0);
+	} else {
+		value = afi_readl(port->pcie, ctrl);
+		value &= ~AFI_PEX_CTRL_RST;
+		afi_writel(port->pcie, value, ctrl);
+	}
 
 	usleep_range(1000, 2000);
 
-	value = afi_readl(port->pcie, ctrl);
-	value |= AFI_PEX_CTRL_RST;
-	afi_writel(port->pcie, value, ctrl);
+	if (port->reset_gpiod) {
+		gpiod_set_value(port->reset_gpiod, 1);
+	} else {
+		value = afi_readl(port->pcie, ctrl);
+		value |= AFI_PEX_CTRL_RST;
+		afi_writel(port->pcie, value, ctrl);
+	}
 }
 
 static void tegra_pcie_enable_rp_features(struct tegra_pcie_port *port)
@@ -2238,6 +2249,7 @@  static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 		struct tegra_pcie_port *rp;
 		unsigned int index;
 		u32 value;
+		char *label;
 
 		err = of_pci_get_devfn(port);
 		if (err < 0) {
@@ -2296,6 +2308,23 @@  static int tegra_pcie_parse_dt(struct tegra_pcie *pcie)
 		if (IS_ERR(rp->base))
 			return PTR_ERR(rp->base);
 
+		label = kasprintf(GFP_KERNEL, "pex-reset-%u", index);
+		if (!label) {
+			dev_err(dev, "failed to create reset GPIO label\n");
+			return -ENOMEM;
+		}
+
+		rp->reset_gpiod = devm_gpiod_get_from_of_node(dev, port,
+							      "reset-gpios", 0,
+							      GPIOD_OUT_LOW,
+							      label);
+		kfree(label);
+		if (IS_ERR(rp->reset_gpiod)) {
+			err = PTR_ERR(rp->reset_gpiod);
+			dev_err(dev, "failed to get reset GPIO: %d\n", err);
+			return err;
+		}
+
 		list_add_tail(&rp->list, &pcie->ports);
 	}