diff mbox

[v9,2/2] pcie-designware platform driver

Message ID 9069117816a5cd9cc3314b6d584c26d457807b8e.1454952698.git.jpinto@synopsys.com (mailing list archive)
State New, archived
Delegated to: Bjorn Helgaas
Headers show

Commit Message

Joao Pinto Feb. 8, 2016, 5:45 p.m. UTC
The "wait for link" routine was centralised and so all drivers using it
(dra7xx, exynos, imx6 and spear13xx) were updated to include the new
function. The keystone driver was not updated because it had some custom
opreations in the link waiting loop. 

A simple module (pcie-designware-plat) was created to contain the 
specific platform init code for pcie-designware.

Signed-off-by: Joao Pinto <jpinto@synopsys.com>
---
Change v8 -> v9 (Bjorn Helgaas and Arnd Bergmann):
- wait for link routine was moved to pcie-designware, this implicated
  an update in ra7xx, exynos, imx6 and spear13xx pcie drivers
- the proposed synopsys pcie rc platform driver is now a simple module
  that uses pcie-designware functions only and has no custom features
- the designware-pcie.txt was updated with a DT example
Change v7 -> v8 (Bjorn Helgaas and Arnd Bergmann):
- driver name was changed from pcie-synopsys to pcie-dw-pltfm
- mdelay() replaced for msleep() in the new driver
- Devicetree bindings for the new driver was updated (config space removed
from ranges)
- Unnecessary synopsys_pcie_irq_handler() was removed
- Driver compatibility strings updated
Change v6 -> v7 (Bjorn Helgaas):
- driver name was changed from pcie-snpsdev to pcie-synopsys
- driver internals (functions and certain variables) also changed name
accordingly
- devicetree bindings documentation also changed accordingly
- quirk function dw_pcie_link_retrain() was removed (tests were made
successfully without it)
- driver was changed to meet pci standards (link up confirmation routine,
init rc functions, etc.)
- EPROBE_DEFER were removed
Change v5 -> v6:
- Nothing changed (just to keep up with patch set version).
Change v4 -> v5:
- Nothing changed (just to keep up with patch set version).
Changes v3 -> v4 (Bjorn Helgaas):
- ARCH dependencies were added to the drivers/pci/host/kconfig for the
PCIE_SNPSDEV.
Changes v2 -> v3 (Bjorn Helgaas):
- link init stuff was moved to a snpsdev_pcie_establish_link() function in
pcie-snpsdev
- pcie-snpsdev driver declaration was changed to be more 
standard (Bjorn Helgaas)
- pcie-designware' dw_pcie_link_retrain() now use standard registers from
pci-regs.h (Bjorn Helgaas)
- pcie-snpsdev.txt was complemented with more info (Mark Rutland)
Changes v1 -> v2 (Bjorn Helgaas):
- Fixups snpsdev_pcie_fixup_bridge() and snpsdev_pcie_fixup_res() were removed
from the driver (these functions were for specific tests only and not usefull
in mainline)
- Driver' comments were reviewed (fix Typos and excessive comments removal)
- Removed unnecessary definitions in the driver source (PCIE_PHY_CTRL and
PCIE_PHY_STAT)

 .../devicetree/bindings/pci/designware-pcie.txt    |  17 +++
 drivers/pci/host/Kconfig                           |  11 ++
 drivers/pci/host/Makefile                          |   1 +
 drivers/pci/host/pci-dra7xx.c                      |  11 +-
 drivers/pci/host/pci-exynos.c                      |  11 +-
 drivers/pci/host/pci-imx6.c                        |  11 +-
 drivers/pci/host/pcie-designware-plat.c            | 143 +++++++++++++++++++++
 drivers/pci/host/pcie-designware.c                 |  31 ++++-
 drivers/pci/host/pcie-designware.h                 |   6 +
 drivers/pci/host/pcie-spear13xx.c                  |  12 +-
 10 files changed, 217 insertions(+), 37 deletions(-)
 create mode 100644 drivers/pci/host/pcie-designware-plat.c

Comments

kernel test robot Feb. 8, 2016, 9:29 p.m. UTC | #1
Hi Joao,

[auto build test ERROR on arc/for-next]
[cannot apply to pci/next v4.5-rc3 next-20160208]
[if your patch is applied to the wrong git tree, please drop us a note to help improving the system]

url:    https://github.com/0day-ci/linux/commits/Joao-Pinto/PCI-support-added-to-ARC/20160209-014808
base:   https://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc for-next
config: x86_64-allmodconfig (attached as .config)
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All errors (new ones prefixed by >>):

   include/linux/compiler.h:228:8: sparse: attribute 'no_sanitize_address': unknown attribute
   drivers/pci/host/pcie-designware.c:581:14: sparse: undefined identifier 'pci_has_flag'
   drivers/pci/host/pcie-designware.c: In function 'dw_pcie_host_init':
>> drivers/pci/host/pcie-designware.c:581:7: error: implicit declaration of function 'pci_has_flag' [-Werror=implicit-function-declaration]
     if (!pci_has_flag(PCI_PROBE_ONLY)) {
          ^
>> drivers/pci/host/pcie-designware.c:581:20: error: 'PCI_PROBE_ONLY' undeclared (first use in this function)
     if (!pci_has_flag(PCI_PROBE_ONLY)) {
                       ^
   drivers/pci/host/pcie-designware.c:581:20: note: each undeclared identifier is reported only once for each function it appears in
   cc1: some warnings being treated as errors

vim +/pci_has_flag +581 drivers/pci/host/pcie-designware.c

cbce7900 Zhou Wang   2015-10-29  575  
cbce7900 Zhou Wang   2015-10-29  576  #ifdef CONFIG_ARM
cbce7900 Zhou Wang   2015-10-29  577  	/* support old dtbs that incorrectly describe IRQs */
cbce7900 Zhou Wang   2015-10-29  578  	pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
0815f957 Yijing Wang 2014-11-11  579  #endif
0815f957 Yijing Wang 2014-11-11  580  
cbce7900 Zhou Wang   2015-10-29 @581  	if (!pci_has_flag(PCI_PROBE_ONLY)) {
cbce7900 Zhou Wang   2015-10-29  582  		pci_bus_size_bridges(bus);
cbce7900 Zhou Wang   2015-10-29  583  		pci_bus_assign_resources(bus);
4b1ced84 Jingoo Han  2013-07-31  584  

:::::: The code at line 581 was first introduced by commit
:::::: cbce7900598c26a12652f8ca9c41c5b29034c38d PCI: designware: Make driver arch-agnostic

:::::: TO: Zhou Wang <wangzhou1@hisilicon.com>
:::::: CC: Bjorn Helgaas <bhelgaas@google.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
Bjorn Helgaas Feb. 8, 2016, 11:28 p.m. UTC | #2
On Tue, Feb 09, 2016 at 05:29:15AM +0800, kbuild test robot wrote:
> Hi Joao,
> 
> [auto build test ERROR on arc/for-next]
> [cannot apply to pci/next v4.5-rc3 next-20160208]
> [if your patch is applied to the wrong git tree, please drop us a note to help improving the system]
> 
> url:    https://github.com/0day-ci/linux/commits/Joao-Pinto/PCI-support-added-to-ARC/20160209-014808
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/vgupta/arc for-next
> config: x86_64-allmodconfig (attached as .config)
> reproduce:
>         # save the attached .config to linux build tree
>         make ARCH=x86_64 
> 
> All errors (new ones prefixed by >>):
> 
>    include/linux/compiler.h:228:8: sparse: attribute 'no_sanitize_address': unknown attribute
>    drivers/pci/host/pcie-designware.c:581:14: sparse: undefined identifier 'pci_has_flag'
>    drivers/pci/host/pcie-designware.c: In function 'dw_pcie_host_init':
> >> drivers/pci/host/pcie-designware.c:581:7: error: implicit declaration of function 'pci_has_flag' [-Werror=implicit-function-declaration]
>      if (!pci_has_flag(PCI_PROBE_ONLY)) {
>           ^
> >> drivers/pci/host/pcie-designware.c:581:20: error: 'PCI_PROBE_ONLY' undeclared (first use in this function)
>      if (!pci_has_flag(PCI_PROBE_ONLY)) {
>                        ^
>    drivers/pci/host/pcie-designware.c:581:20: note: each undeclared identifier is reported only once for each function it appears in
>    cc1: some warnings being treated as errors

I have fixes for this on my pci/misc branch, so you don't need to
worry about these errors.  Basically I'm moving the PCI_PROBE_ONLY and
pci_has_flag() declarations from the arch-specific asm/pci-bridge.h
files to the generic linux/pci.h file.

> vim +/pci_has_flag +581 drivers/pci/host/pcie-designware.c
> 
> cbce7900 Zhou Wang   2015-10-29  575  
> cbce7900 Zhou Wang   2015-10-29  576  #ifdef CONFIG_ARM
> cbce7900 Zhou Wang   2015-10-29  577  	/* support old dtbs that incorrectly describe IRQs */
> cbce7900 Zhou Wang   2015-10-29  578  	pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
> 0815f957 Yijing Wang 2014-11-11  579  #endif
> 0815f957 Yijing Wang 2014-11-11  580  
> cbce7900 Zhou Wang   2015-10-29 @581  	if (!pci_has_flag(PCI_PROBE_ONLY)) {
> cbce7900 Zhou Wang   2015-10-29  582  		pci_bus_size_bridges(bus);
> cbce7900 Zhou Wang   2015-10-29  583  		pci_bus_assign_resources(bus);
> 4b1ced84 Jingoo Han  2013-07-31  584  
> 
> :::::: The code at line 581 was first introduced by commit
> :::::: cbce7900598c26a12652f8ca9c41c5b29034c38d PCI: designware: Make driver arch-agnostic
> 
> :::::: TO: Zhou Wang <wangzhou1@hisilicon.com>
> :::::: CC: Bjorn Helgaas <bhelgaas@google.com>
> 
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation


--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Joao Pinto Feb. 10, 2016, 9:48 a.m. UTC | #3
Hi Bjorn,

On 2/8/2016 11:28 PM, Bjorn Helgaas wrote:
>>      if (!pci_has_flag(PCI_PROBE_ONLY)) {
>>                        ^
>>    drivers/pci/host/pcie-designware.c:581:20: note: each undeclared identifier is reported only once for each function it appears in
>>    cc1: some warnings being treated as errors
> 
> I have fixes for this on my pci/misc branch, so you don't need to
> worry about these errors.  Basically I'm moving the PCI_PROBE_ONLY and
> pci_has_flag() declarations from the arch-specific asm/pci-bridge.h
> files to the generic linux/pci.h file.

Ok, thanks.

> 
>> vim +/pci_has_flag +581 drivers/pci/host/pcie-designware.c
>>
>> cbce7900 Zhou Wang   2015-10-29  575  
>> cbce7900 Zhou Wang   2015-10-29  576  #ifdef CONFIG_ARM
>> cbce7900 Zhou Wang   2015-10-29  577  	/* support old dtbs that incorrectly describe IRQs */
>> cbce7900 Zhou Wang   2015-10-29  578  	pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
>> 0815f957 Yijing Wang 2014-11-11  579  #endif
>> 0815f957 Yijing Wang 2014-11-11  580  
>> cbce7900 Zhou Wang   2015-10-29 @581  	if (!pci_has_flag(PCI_PROBE_ONLY)) {
>> cbce7900 Zhou Wang   2015-10-29  582  		pci_bus_size_bridges(bus);
>> cbce7900 Zhou Wang   2015-10-29  583  		pci_bus_assign_resources(bus);
>> 4b1ced84 Jingoo Han  2013-07-31  584  
>>
>> :::::: The code at line 581 was first introduced by commit
>> :::::: cbce7900598c26a12652f8ca9c41c5b29034c38d PCI: designware: Make driver arch-agnostic
>>
>> :::::: TO: Zhou Wang <wangzhou1@hisilicon.com>
>> :::::: CC: Bjorn Helgaas <bhelgaas@google.com>
>>
>> ---
>> 0-DAY kernel test infrastructure                Open Source Technology Center
>> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
> 
> 

If there's anything else that is needed for the pcie-designware platform driver,
please let me know.

Thanks,
Joao
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Joao Pinto Feb. 15, 2016, 6:15 p.m. UTC | #4
Hi Bjorn!
Did you have opportunity to check the v9 patch set?
Thanks.

Joao

On 2/10/2016 9:48 AM, Joao Pinto wrote:
> Hi Bjorn,
> 
> On 2/8/2016 11:28 PM, Bjorn Helgaas wrote:
>>>      if (!pci_has_flag(PCI_PROBE_ONLY)) {
>>>                        ^
>>>    drivers/pci/host/pcie-designware.c:581:20: note: each undeclared identifier is reported only once for each function it appears in
>>>    cc1: some warnings being treated as errors
>>
>> I have fixes for this on my pci/misc branch, so you don't need to
>> worry about these errors.  Basically I'm moving the PCI_PROBE_ONLY and
>> pci_has_flag() declarations from the arch-specific asm/pci-bridge.h
>> files to the generic linux/pci.h file.
> 
> Ok, thanks.
> 
>>
>>> vim +/pci_has_flag +581 drivers/pci/host/pcie-designware.c
>>>
>>> cbce7900 Zhou Wang   2015-10-29  575  
>>> cbce7900 Zhou Wang   2015-10-29  576  #ifdef CONFIG_ARM
>>> cbce7900 Zhou Wang   2015-10-29  577  	/* support old dtbs that incorrectly describe IRQs */
>>> cbce7900 Zhou Wang   2015-10-29  578  	pci_fixup_irqs(pci_common_swizzle, of_irq_parse_and_map_pci);
>>> 0815f957 Yijing Wang 2014-11-11  579  #endif
>>> 0815f957 Yijing Wang 2014-11-11  580  
>>> cbce7900 Zhou Wang   2015-10-29 @581  	if (!pci_has_flag(PCI_PROBE_ONLY)) {
>>> cbce7900 Zhou Wang   2015-10-29  582  		pci_bus_size_bridges(bus);
>>> cbce7900 Zhou Wang   2015-10-29  583  		pci_bus_assign_resources(bus);
>>> 4b1ced84 Jingoo Han  2013-07-31  584  
>>>
>>> :::::: The code at line 581 was first introduced by commit
>>> :::::: cbce7900598c26a12652f8ca9c41c5b29034c38d PCI: designware: Make driver arch-agnostic
>>>
>>> :::::: TO: Zhou Wang <wangzhou1@hisilicon.com>
>>> :::::: CC: Bjorn Helgaas <bhelgaas@google.com>
>>>
>>> ---
>>> 0-DAY kernel test infrastructure                Open Source Technology Center
>>> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
>>
>>
> 
> If there's anything else that is needed for the pcie-designware platform driver,
> please let me know.
> 
> Thanks,
> Joao
> 

--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bjorn Helgaas Feb. 25, 2016, 4:32 p.m. UTC | #5
Hi Joao,

On Wed, Feb 17, 2016 at 05:46:19PM +0000, Joao Pinto wrote:
> The "wait for link" routine was centralised and so all drivers using it
> (dra7xx, exynos, imx6 and spear13xx) were updated to include the new
> function. The keystone driver was not updated because it had some custom
> opreations in the link waiting loop. 
> 
> A simple module (pcie-designware-plat) was created to contain the 
> specific platform init code for pcie-designware.
> 
> Signed-off-by: Joao Pinto <jpinto@synopsys.com>

This seems to have a couple things mashed together that should be
split apart:

  - add and use dw_pcie_wait_for_link(), and
  - add pcie-designware-plat.c)
  
I'll untangle them and post a v10 for your approval.

> ---
> Change v8 -> v9 (Bjorn Helgaas and Arnd Bergmann):
> - wait for link routine was moved to pcie-designware, this implicated
>   an update in ra7xx, exynos, imx6 and spear13xx pcie drivers
> - the proposed synopsys pcie rc platform driver is now a simple module
>   that uses pcie-designware functions only and has no custom features
> - the designware-pcie.txt was updated with a DT example
> Change v7 -> v8 (Bjorn Helgaas and Arnd Bergmann):
> - driver name was changed from pcie-synopsys to pcie-dw-pltfm
> - mdelay() replaced for msleep() in the new driver
> - Devicetree bindings for the new driver was updated (config space removed
> from ranges)
> - Unnecessary synopsys_pcie_irq_handler() was removed
> - Driver compatibility strings updated
> Change v6 -> v7 (Bjorn Helgaas):
> - driver name was changed from pcie-snpsdev to pcie-synopsys
> - driver internals (functions and certain variables) also changed name
> accordingly
> - devicetree bindings documentation also changed accordingly
> - quirk function dw_pcie_link_retrain() was removed (tests were made
> successfully without it)
> - driver was changed to meet pci standards (link up confirmation routine,
> init rc functions, etc.)
> - EPROBE_DEFER were removed
> Change v5 -> v6:
> - Nothing changed (just to keep up with patch set version).
> Change v4 -> v5:
> - Nothing changed (just to keep up with patch set version).
> Changes v3 -> v4 (Bjorn Helgaas):
> - ARCH dependencies were added to the drivers/pci/host/kconfig for the
> PCIE_SNPSDEV.
> Changes v2 -> v3 (Bjorn Helgaas):
> - link init stuff was moved to a snpsdev_pcie_establish_link() function in
> pcie-snpsdev
> - pcie-snpsdev driver declaration was changed to be more 
> standard (Bjorn Helgaas)
> - pcie-designware' dw_pcie_link_retrain() now use standard registers from
> pci-regs.h (Bjorn Helgaas)
> - pcie-snpsdev.txt was complemented with more info (Mark Rutland)
> Changes v1 -> v2 (Bjorn Helgaas):
> - Fixups snpsdev_pcie_fixup_bridge() and snpsdev_pcie_fixup_res() were removed
> from the driver (these functions were for specific tests only and not usefull
> in mainline)
> - Driver' comments were reviewed (fix Typos and excessive comments removal)
> - Removed unnecessary definitions in the driver source (PCIE_PHY_CTRL and
> PCIE_PHY_STAT)
> 
>  .../devicetree/bindings/pci/designware-pcie.txt    |  17 +++
>  drivers/pci/host/Kconfig                           |  11 ++
>  drivers/pci/host/Makefile                          |   1 +
>  drivers/pci/host/pci-dra7xx.c                      |  11 +-
>  drivers/pci/host/pci-exynos.c                      |  11 +-
>  drivers/pci/host/pci-imx6.c                        |  11 +-
>  drivers/pci/host/pcie-designware-plat.c            | 143 +++++++++++++++++++++
>  drivers/pci/host/pcie-designware.c                 |  31 ++++-
>  drivers/pci/host/pcie-designware.h                 |   6 +
>  drivers/pci/host/pcie-spear13xx.c                  |  12 +-
>  10 files changed, 217 insertions(+), 37 deletions(-)
>  create mode 100644 drivers/pci/host/pcie-designware-plat.c
> 
> diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt
> index 5b0853d..64f2fff 100644
> --- a/Documentation/devicetree/bindings/pci/designware-pcie.txt
> +++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt
> @@ -28,3 +28,20 @@ Optional properties:
>  - clock-names: Must include the following entries:
>  	- "pcie"
>  	- "pcie_bus"
> +
> +Example configuration:
> +
> +	pcie: pcie@0xdffff000 {
> +		compatible = "snps,dw-pcie";
> +		reg = <0xdffff000 0x1000>, /* Controller registers */
> +		      <0xd0000000 0x2000>; /* PCI config space */
> +		reg-names = "ctrlreg", "config";
> +		#address-cells = <3>;
> +		#size-cells = <2>;
> +		device_type = "pci";
> +		ranges = <0x81000000 0 0x00000000 0xde000000 0 0x00010000
> +			  0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>;
> +		interrupts = <25>, <24>;
> +		#interrupt-cells = <1>;
> +		num-lanes = <1>;
> +	};
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index f131ba9..b564f8a 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -15,6 +15,17 @@ config PCI_MVEBU
>  	depends on ARCH_MVEBU || ARCH_DOVE
>  	depends on OF
>  
> +config PCIE_DW_PLAT
> +	bool "Platform bus based DesignWare PCIe Controller"
> +	select PCIE_DW
> +	---help---
> +	 This selects the DesignWare PCIe controller support. Select this if
> +	 you have an PCIe controller on Platform bus.
> +
> +	 If you have a controller with this interface, say Y or M here.
> +
> +	 If unsure, say N.
> +
>  config PCIE_DW
>  	bool
>  
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index 9d4d3c6..d979121 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -1,4 +1,5 @@
>  obj-$(CONFIG_PCIE_DW) += pcie-designware.o
> +obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
>  obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
>  obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
>  obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
> diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c
> index 8c36880..9b394bb 100644
> --- a/drivers/pci/host/pci-dra7xx.c
> +++ b/drivers/pci/host/pci-dra7xx.c
> @@ -10,7 +10,6 @@
>   * published by the Free Software Foundation.
>   */
>  
> -#include <linux/delay.h>
>  #include <linux/err.h>
>  #include <linux/interrupt.h>
>  #include <linux/irq.h>
> @@ -108,7 +107,6 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
>  {
>  	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
>  	u32 reg;
> -	unsigned int retries;
>  
>  	if (dw_pcie_link_up(pp)) {
>  		dev_err(pp->dev, "link is already up\n");
> @@ -119,13 +117,10 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
>  	reg |= LTSSM_EN;
>  	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
>  
> -	for (retries = 0; retries < 1000; retries++) {
> -		if (dw_pcie_link_up(pp))
> -			return 0;
> -		usleep_range(10, 20);
> -	}
> +	/* check if the link is up or not */
> +	if (!dw_pcie_wait_for_link(pp))
> +		return 0;
>  
> -	dev_err(pp->dev, "link is not up\n");
>  	return -EINVAL;
>  }
>  
> diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
> index 01095e1..bd26e15 100644
> --- a/drivers/pci/host/pci-exynos.c
> +++ b/drivers/pci/host/pci-exynos.c
> @@ -318,7 +318,6 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
>  {
>  	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
>  	u32 val;
> -	unsigned int retries;
>  
>  	if (dw_pcie_link_up(pp)) {
>  		dev_err(pp->dev, "Link already up\n");
> @@ -357,13 +356,8 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
>  			  PCIE_APP_LTSSM_ENABLE);
>  
>  	/* check if the link is up or not */
> -	for (retries = 0; retries < 10; retries++) {
> -		if (dw_pcie_link_up(pp)) {
> -			dev_info(pp->dev, "Link up\n");
> -			return 0;
> -		}
> -		mdelay(100);
> -	}
> +	if (!dw_pcie_wait_for_link(pp))
> +		return 0;
>  
>  	while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) {
>  		val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED);
> @@ -372,7 +366,6 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
>  	/* power off phy */
>  	exynos_pcie_power_off_phy(pp);
>  
> -	dev_err(pp->dev, "PCIe Link Fail\n");
>  	return -EINVAL;
>  }
>  
> diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
> index 22e8224..42c6930 100644
> --- a/drivers/pci/host/pci-imx6.c
> +++ b/drivers/pci/host/pci-imx6.c
> @@ -330,15 +330,10 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
>  
>  static int imx6_pcie_wait_for_link(struct pcie_port *pp)
>  {
> -	unsigned int retries;
> -
> -	for (retries = 0; retries < 200; retries++) {
> -		if (dw_pcie_link_up(pp))
> -			return 0;
> -		usleep_range(100, 1000);
> -	}
> +	/* check if the link is up or not */
> +	if (!dw_pcie_wait_for_link(pp))
> +		return 0;
>  
> -	dev_err(pp->dev, "phy link never came up\n");
>  	dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
>  		readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
>  		readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
> diff --git a/drivers/pci/host/pcie-designware-plat.c b/drivers/pci/host/pcie-designware-plat.c
> new file mode 100644
> index 0000000..5f75aba
> --- /dev/null
> +++ b/drivers/pci/host/pcie-designware-plat.c
> @@ -0,0 +1,143 @@
> +/*
> + * PCIe RC driver for Synopsys Designware Core
> + *
> + * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
> + *
> + * Authors: Joao Pinto <jpinto@synopsys.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_gpio.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/resource.h>
> +#include <linux/signal.h>
> +#include <linux/types.h>
> +
> +#include "pcie-designware.h"
> +
> +struct dw_plat_pcie {
> +	void __iomem		*mem_base;
> +	struct pcie_port	pp;
> +};
> +
> +static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
> +{
> +	struct pcie_port *pp = arg;
> +
> +	return dw_handle_msi_irq(pp);
> +}
> +
> +static void dw_plat_pcie_host_init(struct pcie_port *pp)
> +{
> +	dw_pcie_setup_rc(pp);
> +
> +	dw_pcie_wait_for_link(pp);
> +
> +	if (IS_ENABLED(CONFIG_PCI_MSI))
> +		dw_pcie_msi_init(pp);
> +}
> +
> +static struct pcie_host_ops dw_plat_pcie_host_ops = {
> +	.host_init = dw_plat_pcie_host_init,
> +};
> +
> +static int dw_plat_add_pcie_port(struct pcie_port *pp,
> +				 struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	pp->irq = platform_get_irq(pdev, 1);
> +
> +	if (pp->irq < 0)
> +		return pp->irq;
> +
> +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
> +		pp->msi_irq = platform_get_irq(pdev, 0);
> +
> +		if (pp->msi_irq < 0)
> +			return pp->msi_irq;
> +
> +		ret = devm_request_irq(&pdev->dev, pp->msi_irq,
> +					dw_plat_pcie_msi_irq_handler,
> +					IRQF_SHARED, "dw-plat-pcie-msi", pp);
> +		if (ret) {
> +			dev_err(&pdev->dev, "failed to request msi irq\n");
> +			return ret;
> +		}
> +	}
> +
> +	pp->root_bus_nr = -1;
> +	pp->ops = &dw_plat_pcie_host_ops;
> +
> +	ret = dw_pcie_host_init(pp);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to initialize host\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dw_plat_pcie_probe(struct platform_device *pdev)
> +{
> +	struct dw_plat_pcie *dw_plat_pcie;
> +	struct pcie_port *pp;
> +	struct resource *res;  /* Resource from DT */
> +	int ret;
> +
> +	dw_plat_pcie = devm_kzalloc(&pdev->dev, sizeof(*dw_plat_pcie),
> +					GFP_KERNEL);
> +	if (!dw_plat_pcie)
> +		return -ENOMEM;
> +
> +	pp = &dw_plat_pcie->pp;
> +	pp->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +	if (!res)
> +		return -ENODEV;
> +
> +	dw_plat_pcie->mem_base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(dw_plat_pcie->mem_base))
> +		return PTR_ERR(dw_plat_pcie->mem_base);
> +
> +	pp->dbi_base = dw_plat_pcie->mem_base;
> +
> +	ret = dw_plat_add_pcie_port(pp, pdev);
> +	if (ret < 0)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, dw_plat_pcie);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id dw_plat_pcie_of_match[] = {
> +	{ .compatible = "snps,dw-pcie", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dw_plat_pcie_of_match);
> +
> +static struct platform_driver dw_plat_pcie_driver = {
> +	.driver = {
> +		.name	= "dw-pcie",
> +		.of_match_table = dw_plat_pcie_of_match,
> +	},
> +	.probe = dw_plat_pcie_probe,
> +};
> +
> +module_platform_driver(dw_plat_pcie_driver);
> +
> +MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>");
> +MODULE_DESCRIPTION("Synopsys PCIe host controller glue platform driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
> index 540f077..68ca614 100644
> --- a/drivers/pci/host/pcie-designware.c
> +++ b/drivers/pci/host/pcie-designware.c
> @@ -22,6 +22,7 @@
>  #include <linux/pci_regs.h>
>  #include <linux/platform_device.h>
>  #include <linux/types.h>
> +#include <linux/delay.h>
>  
>  #include "pcie-designware.h"
>  
> @@ -69,6 +70,11 @@
>  #define PCIE_ATU_FUNC(x)		(((x) & 0x7) << 16)
>  #define PCIE_ATU_UPPER_TARGET		0x91C
>  
> +/* PCIe Port Logic registers */
> +#define PCIE_PHY_DEBUG_R1_LINK_UP 0x00000010
> +#define PLR_OFFSET 0x700
> +#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
> +
>  static struct pci_ops dw_pcie_ops;
>  
>  int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
> @@ -380,12 +386,33 @@ static struct msi_controller dw_pcie_msi_chip = {
>  	.teardown_irq = dw_msi_teardown_irq,
>  };
>  
> +int dw_pcie_wait_for_link(struct pcie_port *pp)
> +{
> +	int retries;
> +
> +	/* check if the link is up or not */
> +	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
> +		if (dw_pcie_link_up(pp)) {
> +			dev_info(pp->dev, "link up\n");
> +			return 0;
> +		}
> +		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
> +	}
> +
> +	dev_err(pp->dev, "phy link never came up\n");
> +
> +	return 1;
> +}
> +
>  int dw_pcie_link_up(struct pcie_port *pp)
>  {
> +	u32 val;
> +
>  	if (pp->ops->link_up)
>  		return pp->ops->link_up(pp);
> -	else
> -		return 0;
> +
> +	val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
> +	return val & PCIE_PHY_DEBUG_R1_LINK_UP;
>  }
>  
>  static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
> diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
> index 2356d29..8e42624 100644
> --- a/drivers/pci/host/pcie-designware.h
> +++ b/drivers/pci/host/pcie-designware.h
> @@ -22,6 +22,11 @@
>  #define MAX_MSI_IRQS			32
>  #define MAX_MSI_CTRLS			(MAX_MSI_IRQS / 32)
>  
> +/* Parameters for the Waiting for link up routine */
> +#define LINK_WAIT_MAX_RETRIES		10
> +#define LINK_WAIT_USLEEP_MIN		90000
> +#define LINK_WAIT_USLEEP_MAX		100000
> +
>  struct pcie_port {
>  	struct device		*dev;
>  	u8			root_bus_nr;
> @@ -76,6 +81,7 @@ int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
>  int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
>  irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
>  void dw_pcie_msi_init(struct pcie_port *pp);
> +int dw_pcie_wait_for_link(struct pcie_port *pp);
>  int dw_pcie_link_up(struct pcie_port *pp);
>  void dw_pcie_setup_rc(struct pcie_port *pp);
>  int dw_pcie_host_init(struct pcie_port *pp);
> diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/host/pcie-spear13xx.c
> index b95b756..ffdff1d 100644
> --- a/drivers/pci/host/pcie-spear13xx.c
> +++ b/drivers/pci/host/pcie-spear13xx.c
> @@ -13,7 +13,6 @@
>   */
>  
>  #include <linux/clk.h>
> -#include <linux/delay.h>
>  #include <linux/interrupt.h>
>  #include <linux/kernel.h>
>  #include <linux/module.h>
> @@ -149,7 +148,6 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
>  	struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
>  	struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
>  	u32 exp_cap_off = EXP_CAP_ID_OFFSET;
> -	unsigned int retries;
>  
>  	if (dw_pcie_link_up(pp)) {
>  		dev_err(pp->dev, "link already up\n");
> @@ -201,15 +199,9 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
>  			&app_reg->app_ctrl_0);
>  
>  	/* check if the link is up or not */
> -	for (retries = 0; retries < 10; retries++) {
> -		if (dw_pcie_link_up(pp)) {
> -			dev_info(pp->dev, "link up\n");
> -			return 0;
> -		}
> -		mdelay(100);
> -	}
> +	if (!dw_pcie_wait_for_link(pp))
> +		return 0;
>  
> -	dev_err(pp->dev, "link Fail\n");
>  	return -EINVAL;
>  }
>  
> -- 
> 1.8.1.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Bjorn Helgaas Feb. 25, 2016, 5:01 p.m. UTC | #6
[+cc Murali]

On Wed, Feb 17, 2016 at 05:46:19PM +0000, Joao Pinto wrote:
> The "wait for link" routine was centralised and so all drivers using it
> (dra7xx, exynos, imx6 and spear13xx) were updated to include the new
> function. The keystone driver was not updated because it had some custom
> opreations in the link waiting loop. 

I'm dubious about the keystone code:

  ks_pcie_establish_link(...)
  {
    if (dw_pcie_link_up(...))
      return 0;

    ks_dw_pcie_initiate_link_train(...);
    for (retries = 0; retries < 200; retries++) {
      if (dw_pcie_link_up(...))
        return 0;
      usleep_range(100, 1000);
      ks_dw_pcie_initiate_link_train(...);
    }
  }

It doesn't seem reasonable to me to disable, then re-initiate link
training every time around the loop, after waiting only 100 to 1000
usec.  What if we did somethin like this:

  ks_pcie_establish_link(...)
  {
    if (dw_pcie_link_up(...))
      return 0;

    for (retries = 0; retries < 5; retries++) {
      ks_dw_pcie_initiate_link_train(...);
      if (!dw_pcie_wait_for_link(...))
        return 0;
    }

Murali, any thoughts on this?

Was there a reason you didn't update pcie-qcom.c?

> A simple module (pcie-designware-plat) was created to contain the 
> specific platform init code for pcie-designware.
> 
> Signed-off-by: Joao Pinto <jpinto@synopsys.com>
> ---
> Change v8 -> v9 (Bjorn Helgaas and Arnd Bergmann):
> - wait for link routine was moved to pcie-designware, this implicated
>   an update in ra7xx, exynos, imx6 and spear13xx pcie drivers
> - the proposed synopsys pcie rc platform driver is now a simple module
>   that uses pcie-designware functions only and has no custom features
> - the designware-pcie.txt was updated with a DT example
> Change v7 -> v8 (Bjorn Helgaas and Arnd Bergmann):
> - driver name was changed from pcie-synopsys to pcie-dw-pltfm
> - mdelay() replaced for msleep() in the new driver
> - Devicetree bindings for the new driver was updated (config space removed
> from ranges)
> - Unnecessary synopsys_pcie_irq_handler() was removed
> - Driver compatibility strings updated
> Change v6 -> v7 (Bjorn Helgaas):
> - driver name was changed from pcie-snpsdev to pcie-synopsys
> - driver internals (functions and certain variables) also changed name
> accordingly
> - devicetree bindings documentation also changed accordingly
> - quirk function dw_pcie_link_retrain() was removed (tests were made
> successfully without it)
> - driver was changed to meet pci standards (link up confirmation routine,
> init rc functions, etc.)
> - EPROBE_DEFER were removed
> Change v5 -> v6:
> - Nothing changed (just to keep up with patch set version).
> Change v4 -> v5:
> - Nothing changed (just to keep up with patch set version).
> Changes v3 -> v4 (Bjorn Helgaas):
> - ARCH dependencies were added to the drivers/pci/host/kconfig for the
> PCIE_SNPSDEV.
> Changes v2 -> v3 (Bjorn Helgaas):
> - link init stuff was moved to a snpsdev_pcie_establish_link() function in
> pcie-snpsdev
> - pcie-snpsdev driver declaration was changed to be more 
> standard (Bjorn Helgaas)
> - pcie-designware' dw_pcie_link_retrain() now use standard registers from
> pci-regs.h (Bjorn Helgaas)
> - pcie-snpsdev.txt was complemented with more info (Mark Rutland)
> Changes v1 -> v2 (Bjorn Helgaas):
> - Fixups snpsdev_pcie_fixup_bridge() and snpsdev_pcie_fixup_res() were removed
> from the driver (these functions were for specific tests only and not usefull
> in mainline)
> - Driver' comments were reviewed (fix Typos and excessive comments removal)
> - Removed unnecessary definitions in the driver source (PCIE_PHY_CTRL and
> PCIE_PHY_STAT)
> 
>  .../devicetree/bindings/pci/designware-pcie.txt    |  17 +++
>  drivers/pci/host/Kconfig                           |  11 ++
>  drivers/pci/host/Makefile                          |   1 +
>  drivers/pci/host/pci-dra7xx.c                      |  11 +-
>  drivers/pci/host/pci-exynos.c                      |  11 +-
>  drivers/pci/host/pci-imx6.c                        |  11 +-
>  drivers/pci/host/pcie-designware-plat.c            | 143 +++++++++++++++++++++
>  drivers/pci/host/pcie-designware.c                 |  31 ++++-
>  drivers/pci/host/pcie-designware.h                 |   6 +
>  drivers/pci/host/pcie-spear13xx.c                  |  12 +-
>  10 files changed, 217 insertions(+), 37 deletions(-)
>  create mode 100644 drivers/pci/host/pcie-designware-plat.c
> 
> diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt
> index 5b0853d..64f2fff 100644
> --- a/Documentation/devicetree/bindings/pci/designware-pcie.txt
> +++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt
> @@ -28,3 +28,20 @@ Optional properties:
>  - clock-names: Must include the following entries:
>  	- "pcie"
>  	- "pcie_bus"
> +
> +Example configuration:
> +
> +	pcie: pcie@0xdffff000 {
> +		compatible = "snps,dw-pcie";
> +		reg = <0xdffff000 0x1000>, /* Controller registers */
> +		      <0xd0000000 0x2000>; /* PCI config space */
> +		reg-names = "ctrlreg", "config";
> +		#address-cells = <3>;
> +		#size-cells = <2>;
> +		device_type = "pci";
> +		ranges = <0x81000000 0 0x00000000 0xde000000 0 0x00010000
> +			  0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>;
> +		interrupts = <25>, <24>;
> +		#interrupt-cells = <1>;
> +		num-lanes = <1>;
> +	};
> diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
> index f131ba9..b564f8a 100644
> --- a/drivers/pci/host/Kconfig
> +++ b/drivers/pci/host/Kconfig
> @@ -15,6 +15,17 @@ config PCI_MVEBU
>  	depends on ARCH_MVEBU || ARCH_DOVE
>  	depends on OF
>  
> +config PCIE_DW_PLAT
> +	bool "Platform bus based DesignWare PCIe Controller"
> +	select PCIE_DW
> +	---help---
> +	 This selects the DesignWare PCIe controller support. Select this if
> +	 you have an PCIe controller on Platform bus.
> +
> +	 If you have a controller with this interface, say Y or M here.
> +
> +	 If unsure, say N.
> +
>  config PCIE_DW
>  	bool
>  
> diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
> index 9d4d3c6..d979121 100644
> --- a/drivers/pci/host/Makefile
> +++ b/drivers/pci/host/Makefile
> @@ -1,4 +1,5 @@
>  obj-$(CONFIG_PCIE_DW) += pcie-designware.o
> +obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
>  obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
>  obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
>  obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
> diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c
> index 8c36880..9b394bb 100644
> --- a/drivers/pci/host/pci-dra7xx.c
> +++ b/drivers/pci/host/pci-dra7xx.c
> @@ -10,7 +10,6 @@
>   * published by the Free Software Foundation.
>   */
>  
> -#include <linux/delay.h>
>  #include <linux/err.h>
>  #include <linux/interrupt.h>
>  #include <linux/irq.h>
> @@ -108,7 +107,6 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
>  {
>  	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
>  	u32 reg;
> -	unsigned int retries;
>  
>  	if (dw_pcie_link_up(pp)) {
>  		dev_err(pp->dev, "link is already up\n");
> @@ -119,13 +117,10 @@ static int dra7xx_pcie_establish_link(struct pcie_port *pp)
>  	reg |= LTSSM_EN;
>  	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
>  
> -	for (retries = 0; retries < 1000; retries++) {
> -		if (dw_pcie_link_up(pp))
> -			return 0;
> -		usleep_range(10, 20);
> -	}
> +	/* check if the link is up or not */
> +	if (!dw_pcie_wait_for_link(pp))
> +		return 0;
>  
> -	dev_err(pp->dev, "link is not up\n");
>  	return -EINVAL;
>  }
>  
> diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
> index 01095e1..bd26e15 100644
> --- a/drivers/pci/host/pci-exynos.c
> +++ b/drivers/pci/host/pci-exynos.c
> @@ -318,7 +318,6 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
>  {
>  	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
>  	u32 val;
> -	unsigned int retries;
>  
>  	if (dw_pcie_link_up(pp)) {
>  		dev_err(pp->dev, "Link already up\n");
> @@ -357,13 +356,8 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
>  			  PCIE_APP_LTSSM_ENABLE);
>  
>  	/* check if the link is up or not */
> -	for (retries = 0; retries < 10; retries++) {
> -		if (dw_pcie_link_up(pp)) {
> -			dev_info(pp->dev, "Link up\n");
> -			return 0;
> -		}
> -		mdelay(100);
> -	}
> +	if (!dw_pcie_wait_for_link(pp))
> +		return 0;
>  
>  	while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) {
>  		val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED);
> @@ -372,7 +366,6 @@ static int exynos_pcie_establish_link(struct pcie_port *pp)
>  	/* power off phy */
>  	exynos_pcie_power_off_phy(pp);
>  
> -	dev_err(pp->dev, "PCIe Link Fail\n");
>  	return -EINVAL;
>  }
>  
> diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
> index 22e8224..42c6930 100644
> --- a/drivers/pci/host/pci-imx6.c
> +++ b/drivers/pci/host/pci-imx6.c
> @@ -330,15 +330,10 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
>  
>  static int imx6_pcie_wait_for_link(struct pcie_port *pp)
>  {
> -	unsigned int retries;
> -
> -	for (retries = 0; retries < 200; retries++) {
> -		if (dw_pcie_link_up(pp))
> -			return 0;
> -		usleep_range(100, 1000);
> -	}
> +	/* check if the link is up or not */
> +	if (!dw_pcie_wait_for_link(pp))
> +		return 0;
>  
> -	dev_err(pp->dev, "phy link never came up\n");
>  	dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
>  		readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
>  		readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
> diff --git a/drivers/pci/host/pcie-designware-plat.c b/drivers/pci/host/pcie-designware-plat.c
> new file mode 100644
> index 0000000..5f75aba
> --- /dev/null
> +++ b/drivers/pci/host/pcie-designware-plat.c
> @@ -0,0 +1,143 @@
> +/*
> + * PCIe RC driver for Synopsys Designware Core
> + *
> + * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
> + *
> + * Authors: Joao Pinto <jpinto@synopsys.com>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +#include <linux/clk.h>
> +#include <linux/delay.h>
> +#include <linux/gpio.h>
> +#include <linux/interrupt.h>
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/of_gpio.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/resource.h>
> +#include <linux/signal.h>
> +#include <linux/types.h>
> +
> +#include "pcie-designware.h"
> +
> +struct dw_plat_pcie {
> +	void __iomem		*mem_base;
> +	struct pcie_port	pp;
> +};
> +
> +static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
> +{
> +	struct pcie_port *pp = arg;
> +
> +	return dw_handle_msi_irq(pp);
> +}
> +
> +static void dw_plat_pcie_host_init(struct pcie_port *pp)
> +{
> +	dw_pcie_setup_rc(pp);
> +
> +	dw_pcie_wait_for_link(pp);
> +
> +	if (IS_ENABLED(CONFIG_PCI_MSI))
> +		dw_pcie_msi_init(pp);
> +}
> +
> +static struct pcie_host_ops dw_plat_pcie_host_ops = {
> +	.host_init = dw_plat_pcie_host_init,
> +};
> +
> +static int dw_plat_add_pcie_port(struct pcie_port *pp,
> +				 struct platform_device *pdev)
> +{
> +	int ret;
> +
> +	pp->irq = platform_get_irq(pdev, 1);
> +
> +	if (pp->irq < 0)
> +		return pp->irq;
> +
> +	if (IS_ENABLED(CONFIG_PCI_MSI)) {
> +		pp->msi_irq = platform_get_irq(pdev, 0);
> +
> +		if (pp->msi_irq < 0)
> +			return pp->msi_irq;
> +
> +		ret = devm_request_irq(&pdev->dev, pp->msi_irq,
> +					dw_plat_pcie_msi_irq_handler,
> +					IRQF_SHARED, "dw-plat-pcie-msi", pp);
> +		if (ret) {
> +			dev_err(&pdev->dev, "failed to request msi irq\n");
> +			return ret;
> +		}
> +	}
> +
> +	pp->root_bus_nr = -1;
> +	pp->ops = &dw_plat_pcie_host_ops;
> +
> +	ret = dw_pcie_host_init(pp);
> +	if (ret) {
> +		dev_err(&pdev->dev, "failed to initialize host\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int dw_plat_pcie_probe(struct platform_device *pdev)
> +{
> +	struct dw_plat_pcie *dw_plat_pcie;
> +	struct pcie_port *pp;
> +	struct resource *res;  /* Resource from DT */
> +	int ret;
> +
> +	dw_plat_pcie = devm_kzalloc(&pdev->dev, sizeof(*dw_plat_pcie),
> +					GFP_KERNEL);
> +	if (!dw_plat_pcie)
> +		return -ENOMEM;
> +
> +	pp = &dw_plat_pcie->pp;
> +	pp->dev = &pdev->dev;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +
> +	if (!res)
> +		return -ENODEV;
> +
> +	dw_plat_pcie->mem_base = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(dw_plat_pcie->mem_base))
> +		return PTR_ERR(dw_plat_pcie->mem_base);
> +
> +	pp->dbi_base = dw_plat_pcie->mem_base;
> +
> +	ret = dw_plat_add_pcie_port(pp, pdev);
> +	if (ret < 0)
> +		return ret;
> +
> +	platform_set_drvdata(pdev, dw_plat_pcie);
> +
> +	return 0;
> +}
> +
> +static const struct of_device_id dw_plat_pcie_of_match[] = {
> +	{ .compatible = "snps,dw-pcie", },
> +	{},
> +};
> +MODULE_DEVICE_TABLE(of, dw_plat_pcie_of_match);
> +
> +static struct platform_driver dw_plat_pcie_driver = {
> +	.driver = {
> +		.name	= "dw-pcie",
> +		.of_match_table = dw_plat_pcie_of_match,
> +	},
> +	.probe = dw_plat_pcie_probe,
> +};
> +
> +module_platform_driver(dw_plat_pcie_driver);
> +
> +MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>");
> +MODULE_DESCRIPTION("Synopsys PCIe host controller glue platform driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
> index 540f077..68ca614 100644
> --- a/drivers/pci/host/pcie-designware.c
> +++ b/drivers/pci/host/pcie-designware.c
> @@ -22,6 +22,7 @@
>  #include <linux/pci_regs.h>
>  #include <linux/platform_device.h>
>  #include <linux/types.h>
> +#include <linux/delay.h>
>  
>  #include "pcie-designware.h"
>  
> @@ -69,6 +70,11 @@
>  #define PCIE_ATU_FUNC(x)		(((x) & 0x7) << 16)
>  #define PCIE_ATU_UPPER_TARGET		0x91C
>  
> +/* PCIe Port Logic registers */
> +#define PCIE_PHY_DEBUG_R1_LINK_UP 0x00000010
> +#define PLR_OFFSET 0x700
> +#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
> +
>  static struct pci_ops dw_pcie_ops;
>  
>  int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
> @@ -380,12 +386,33 @@ static struct msi_controller dw_pcie_msi_chip = {
>  	.teardown_irq = dw_msi_teardown_irq,
>  };
>  
> +int dw_pcie_wait_for_link(struct pcie_port *pp)
> +{
> +	int retries;
> +
> +	/* check if the link is up or not */
> +	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
> +		if (dw_pcie_link_up(pp)) {
> +			dev_info(pp->dev, "link up\n");
> +			return 0;
> +		}
> +		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
> +	}
> +
> +	dev_err(pp->dev, "phy link never came up\n");
> +
> +	return 1;
> +}
> +
>  int dw_pcie_link_up(struct pcie_port *pp)
>  {
> +	u32 val;
> +
>  	if (pp->ops->link_up)
>  		return pp->ops->link_up(pp);
> -	else
> -		return 0;
> +
> +	val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
> +	return val & PCIE_PHY_DEBUG_R1_LINK_UP;
>  }
>  
>  static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
> diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
> index 2356d29..8e42624 100644
> --- a/drivers/pci/host/pcie-designware.h
> +++ b/drivers/pci/host/pcie-designware.h
> @@ -22,6 +22,11 @@
>  #define MAX_MSI_IRQS			32
>  #define MAX_MSI_CTRLS			(MAX_MSI_IRQS / 32)
>  
> +/* Parameters for the Waiting for link up routine */
> +#define LINK_WAIT_MAX_RETRIES		10
> +#define LINK_WAIT_USLEEP_MIN		90000
> +#define LINK_WAIT_USLEEP_MAX		100000
> +
>  struct pcie_port {
>  	struct device		*dev;
>  	u8			root_bus_nr;
> @@ -76,6 +81,7 @@ int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
>  int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
>  irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
>  void dw_pcie_msi_init(struct pcie_port *pp);
> +int dw_pcie_wait_for_link(struct pcie_port *pp);
>  int dw_pcie_link_up(struct pcie_port *pp);
>  void dw_pcie_setup_rc(struct pcie_port *pp);
>  int dw_pcie_host_init(struct pcie_port *pp);
> diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/host/pcie-spear13xx.c
> index b95b756..ffdff1d 100644
> --- a/drivers/pci/host/pcie-spear13xx.c
> +++ b/drivers/pci/host/pcie-spear13xx.c
> @@ -13,7 +13,6 @@
>   */
>  
>  #include <linux/clk.h>
> -#include <linux/delay.h>
>  #include <linux/interrupt.h>
>  #include <linux/kernel.h>
>  #include <linux/module.h>
> @@ -149,7 +148,6 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
>  	struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
>  	struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
>  	u32 exp_cap_off = EXP_CAP_ID_OFFSET;
> -	unsigned int retries;
>  
>  	if (dw_pcie_link_up(pp)) {
>  		dev_err(pp->dev, "link already up\n");
> @@ -201,15 +199,9 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp)
>  			&app_reg->app_ctrl_0);
>  
>  	/* check if the link is up or not */
> -	for (retries = 0; retries < 10; retries++) {
> -		if (dw_pcie_link_up(pp)) {
> -			dev_info(pp->dev, "link up\n");
> -			return 0;
> -		}
> -		mdelay(100);
> -	}
> +	if (!dw_pcie_wait_for_link(pp))
> +		return 0;
>  
> -	dev_err(pp->dev, "link Fail\n");
>  	return -EINVAL;
>  }
>  
> -- 
> 1.8.1.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-pci" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-pci" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/pci/designware-pcie.txt b/Documentation/devicetree/bindings/pci/designware-pcie.txt
index 5b0853d..64f2fff 100644
--- a/Documentation/devicetree/bindings/pci/designware-pcie.txt
+++ b/Documentation/devicetree/bindings/pci/designware-pcie.txt
@@ -28,3 +28,20 @@  Optional properties:
 - clock-names: Must include the following entries:
 	- "pcie"
 	- "pcie_bus"
+
+Example configuration:
+
+	pcie: pcie@0xdffff000 {
+		compatible = "snps,dw-pcie";
+		reg = <0xdffff000 0x1000>, /* Controller registers */
+		      <0xd0000000 0x2000>; /* PCI config space */
+		reg-names = "ctrlreg", "config";
+		#address-cells = <3>;
+		#size-cells = <2>;
+		device_type = "pci";
+		ranges = <0x81000000 0 0x00000000 0xde000000 0 0x00010000
+			  0x82000000 0 0xd0400000 0xd0400000 0 0x0d000000>;
+		interrupts = <25>, <24>;
+		#interrupt-cells = <1>;
+		num-lanes = <1>;
+	};
diff --git a/drivers/pci/host/Kconfig b/drivers/pci/host/Kconfig
index f131ba9..b564f8a 100644
--- a/drivers/pci/host/Kconfig
+++ b/drivers/pci/host/Kconfig
@@ -15,6 +15,17 @@  config PCI_MVEBU
 	depends on ARCH_MVEBU || ARCH_DOVE
 	depends on OF
 
+config PCIE_DW_PLAT
+	bool "Platform bus based DesignWare PCIe Controller"
+	select PCIE_DW
+	---help---
+	 This selects the DesignWare PCIe controller support. Select this if
+	 you have an PCIe controller on Platform bus.
+
+	 If you have a controller with this interface, say Y or M here.
+
+	 If unsure, say N.
+
 config PCIE_DW
 	bool
 
diff --git a/drivers/pci/host/Makefile b/drivers/pci/host/Makefile
index 9d4d3c6..d979121 100644
--- a/drivers/pci/host/Makefile
+++ b/drivers/pci/host/Makefile
@@ -1,4 +1,5 @@ 
 obj-$(CONFIG_PCIE_DW) += pcie-designware.o
+obj-$(CONFIG_PCIE_DW_PLAT) += pcie-designware-plat.o
 obj-$(CONFIG_PCI_DRA7XX) += pci-dra7xx.o
 obj-$(CONFIG_PCI_EXYNOS) += pci-exynos.o
 obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
diff --git a/drivers/pci/host/pci-dra7xx.c b/drivers/pci/host/pci-dra7xx.c
index 8c36880..9b394bb 100644
--- a/drivers/pci/host/pci-dra7xx.c
+++ b/drivers/pci/host/pci-dra7xx.c
@@ -10,7 +10,6 @@ 
  * published by the Free Software Foundation.
  */
 
-#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
@@ -108,7 +107,6 @@  static int dra7xx_pcie_establish_link(struct pcie_port *pp)
 {
 	struct dra7xx_pcie *dra7xx = to_dra7xx_pcie(pp);
 	u32 reg;
-	unsigned int retries;
 
 	if (dw_pcie_link_up(pp)) {
 		dev_err(pp->dev, "link is already up\n");
@@ -119,13 +117,10 @@  static int dra7xx_pcie_establish_link(struct pcie_port *pp)
 	reg |= LTSSM_EN;
 	dra7xx_pcie_writel(dra7xx, PCIECTRL_DRA7XX_CONF_DEVICE_CMD, reg);
 
-	for (retries = 0; retries < 1000; retries++) {
-		if (dw_pcie_link_up(pp))
-			return 0;
-		usleep_range(10, 20);
-	}
+	/* check if the link is up or not */
+	if (!dw_pcie_wait_for_link(pp))
+		return 0;
 
-	dev_err(pp->dev, "link is not up\n");
 	return -EINVAL;
 }
 
diff --git a/drivers/pci/host/pci-exynos.c b/drivers/pci/host/pci-exynos.c
index 01095e1..bd26e15 100644
--- a/drivers/pci/host/pci-exynos.c
+++ b/drivers/pci/host/pci-exynos.c
@@ -318,7 +318,6 @@  static int exynos_pcie_establish_link(struct pcie_port *pp)
 {
 	struct exynos_pcie *exynos_pcie = to_exynos_pcie(pp);
 	u32 val;
-	unsigned int retries;
 
 	if (dw_pcie_link_up(pp)) {
 		dev_err(pp->dev, "Link already up\n");
@@ -357,13 +356,8 @@  static int exynos_pcie_establish_link(struct pcie_port *pp)
 			  PCIE_APP_LTSSM_ENABLE);
 
 	/* check if the link is up or not */
-	for (retries = 0; retries < 10; retries++) {
-		if (dw_pcie_link_up(pp)) {
-			dev_info(pp->dev, "Link up\n");
-			return 0;
-		}
-		mdelay(100);
-	}
+	if (!dw_pcie_wait_for_link(pp))
+		return 0;
 
 	while (exynos_phy_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED) == 0) {
 		val = exynos_blk_readl(exynos_pcie, PCIE_PHY_PLL_LOCKED);
@@ -372,7 +366,6 @@  static int exynos_pcie_establish_link(struct pcie_port *pp)
 	/* power off phy */
 	exynos_pcie_power_off_phy(pp);
 
-	dev_err(pp->dev, "PCIe Link Fail\n");
 	return -EINVAL;
 }
 
diff --git a/drivers/pci/host/pci-imx6.c b/drivers/pci/host/pci-imx6.c
index 22e8224..42c6930 100644
--- a/drivers/pci/host/pci-imx6.c
+++ b/drivers/pci/host/pci-imx6.c
@@ -330,15 +330,10 @@  static void imx6_pcie_init_phy(struct pcie_port *pp)
 
 static int imx6_pcie_wait_for_link(struct pcie_port *pp)
 {
-	unsigned int retries;
-
-	for (retries = 0; retries < 200; retries++) {
-		if (dw_pcie_link_up(pp))
-			return 0;
-		usleep_range(100, 1000);
-	}
+	/* check if the link is up or not */
+	if (!dw_pcie_wait_for_link(pp))
+		return 0;
 
-	dev_err(pp->dev, "phy link never came up\n");
 	dev_dbg(pp->dev, "DEBUG_R0: 0x%08x, DEBUG_R1: 0x%08x\n",
 		readl(pp->dbi_base + PCIE_PHY_DEBUG_R0),
 		readl(pp->dbi_base + PCIE_PHY_DEBUG_R1));
diff --git a/drivers/pci/host/pcie-designware-plat.c b/drivers/pci/host/pcie-designware-plat.c
new file mode 100644
index 0000000..5f75aba
--- /dev/null
+++ b/drivers/pci/host/pcie-designware-plat.c
@@ -0,0 +1,143 @@ 
+/*
+ * PCIe RC driver for Synopsys Designware Core
+ *
+ * Copyright (C) 2015-2016 Synopsys, Inc. (www.synopsys.com)
+ *
+ * Authors: Joao Pinto <jpinto@synopsys.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/resource.h>
+#include <linux/signal.h>
+#include <linux/types.h>
+
+#include "pcie-designware.h"
+
+struct dw_plat_pcie {
+	void __iomem		*mem_base;
+	struct pcie_port	pp;
+};
+
+static irqreturn_t dw_plat_pcie_msi_irq_handler(int irq, void *arg)
+{
+	struct pcie_port *pp = arg;
+
+	return dw_handle_msi_irq(pp);
+}
+
+static void dw_plat_pcie_host_init(struct pcie_port *pp)
+{
+	dw_pcie_setup_rc(pp);
+
+	dw_pcie_wait_for_link(pp);
+
+	if (IS_ENABLED(CONFIG_PCI_MSI))
+		dw_pcie_msi_init(pp);
+}
+
+static struct pcie_host_ops dw_plat_pcie_host_ops = {
+	.host_init = dw_plat_pcie_host_init,
+};
+
+static int dw_plat_add_pcie_port(struct pcie_port *pp,
+				 struct platform_device *pdev)
+{
+	int ret;
+
+	pp->irq = platform_get_irq(pdev, 1);
+
+	if (pp->irq < 0)
+		return pp->irq;
+
+	if (IS_ENABLED(CONFIG_PCI_MSI)) {
+		pp->msi_irq = platform_get_irq(pdev, 0);
+
+		if (pp->msi_irq < 0)
+			return pp->msi_irq;
+
+		ret = devm_request_irq(&pdev->dev, pp->msi_irq,
+					dw_plat_pcie_msi_irq_handler,
+					IRQF_SHARED, "dw-plat-pcie-msi", pp);
+		if (ret) {
+			dev_err(&pdev->dev, "failed to request msi irq\n");
+			return ret;
+		}
+	}
+
+	pp->root_bus_nr = -1;
+	pp->ops = &dw_plat_pcie_host_ops;
+
+	ret = dw_pcie_host_init(pp);
+	if (ret) {
+		dev_err(&pdev->dev, "failed to initialize host\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static int dw_plat_pcie_probe(struct platform_device *pdev)
+{
+	struct dw_plat_pcie *dw_plat_pcie;
+	struct pcie_port *pp;
+	struct resource *res;  /* Resource from DT */
+	int ret;
+
+	dw_plat_pcie = devm_kzalloc(&pdev->dev, sizeof(*dw_plat_pcie),
+					GFP_KERNEL);
+	if (!dw_plat_pcie)
+		return -ENOMEM;
+
+	pp = &dw_plat_pcie->pp;
+	pp->dev = &pdev->dev;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	if (!res)
+		return -ENODEV;
+
+	dw_plat_pcie->mem_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dw_plat_pcie->mem_base))
+		return PTR_ERR(dw_plat_pcie->mem_base);
+
+	pp->dbi_base = dw_plat_pcie->mem_base;
+
+	ret = dw_plat_add_pcie_port(pp, pdev);
+	if (ret < 0)
+		return ret;
+
+	platform_set_drvdata(pdev, dw_plat_pcie);
+
+	return 0;
+}
+
+static const struct of_device_id dw_plat_pcie_of_match[] = {
+	{ .compatible = "snps,dw-pcie", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, dw_plat_pcie_of_match);
+
+static struct platform_driver dw_plat_pcie_driver = {
+	.driver = {
+		.name	= "dw-pcie",
+		.of_match_table = dw_plat_pcie_of_match,
+	},
+	.probe = dw_plat_pcie_probe,
+};
+
+module_platform_driver(dw_plat_pcie_driver);
+
+MODULE_AUTHOR("Joao Pinto <Joao.Pinto@synopsys.com>");
+MODULE_DESCRIPTION("Synopsys PCIe host controller glue platform driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/pci/host/pcie-designware.c b/drivers/pci/host/pcie-designware.c
index 540f077..68ca614 100644
--- a/drivers/pci/host/pcie-designware.c
+++ b/drivers/pci/host/pcie-designware.c
@@ -22,6 +22,7 @@ 
 #include <linux/pci_regs.h>
 #include <linux/platform_device.h>
 #include <linux/types.h>
+#include <linux/delay.h>
 
 #include "pcie-designware.h"
 
@@ -69,6 +70,11 @@ 
 #define PCIE_ATU_FUNC(x)		(((x) & 0x7) << 16)
 #define PCIE_ATU_UPPER_TARGET		0x91C
 
+/* PCIe Port Logic registers */
+#define PCIE_PHY_DEBUG_R1_LINK_UP 0x00000010
+#define PLR_OFFSET 0x700
+#define PCIE_PHY_DEBUG_R1 (PLR_OFFSET + 0x2c)
+
 static struct pci_ops dw_pcie_ops;
 
 int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val)
@@ -380,12 +386,33 @@  static struct msi_controller dw_pcie_msi_chip = {
 	.teardown_irq = dw_msi_teardown_irq,
 };
 
+int dw_pcie_wait_for_link(struct pcie_port *pp)
+{
+	int retries;
+
+	/* check if the link is up or not */
+	for (retries = 0; retries < LINK_WAIT_MAX_RETRIES; retries++) {
+		if (dw_pcie_link_up(pp)) {
+			dev_info(pp->dev, "link up\n");
+			return 0;
+		}
+		usleep_range(LINK_WAIT_USLEEP_MIN, LINK_WAIT_USLEEP_MAX);
+	}
+
+	dev_err(pp->dev, "phy link never came up\n");
+
+	return 1;
+}
+
 int dw_pcie_link_up(struct pcie_port *pp)
 {
+	u32 val;
+
 	if (pp->ops->link_up)
 		return pp->ops->link_up(pp);
-	else
-		return 0;
+
+	val = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1);
+	return val & PCIE_PHY_DEBUG_R1_LINK_UP;
 }
 
 static int dw_pcie_msi_map(struct irq_domain *domain, unsigned int irq,
diff --git a/drivers/pci/host/pcie-designware.h b/drivers/pci/host/pcie-designware.h
index 2356d29..8e42624 100644
--- a/drivers/pci/host/pcie-designware.h
+++ b/drivers/pci/host/pcie-designware.h
@@ -22,6 +22,11 @@ 
 #define MAX_MSI_IRQS			32
 #define MAX_MSI_CTRLS			(MAX_MSI_IRQS / 32)
 
+/* Parameters for the Waiting for link up routine */
+#define LINK_WAIT_MAX_RETRIES		10
+#define LINK_WAIT_USLEEP_MIN		90000
+#define LINK_WAIT_USLEEP_MAX		100000
+
 struct pcie_port {
 	struct device		*dev;
 	u8			root_bus_nr;
@@ -76,6 +81,7 @@  int dw_pcie_cfg_read(void __iomem *addr, int size, u32 *val);
 int dw_pcie_cfg_write(void __iomem *addr, int size, u32 val);
 irqreturn_t dw_handle_msi_irq(struct pcie_port *pp);
 void dw_pcie_msi_init(struct pcie_port *pp);
+int dw_pcie_wait_for_link(struct pcie_port *pp);
 int dw_pcie_link_up(struct pcie_port *pp);
 void dw_pcie_setup_rc(struct pcie_port *pp);
 int dw_pcie_host_init(struct pcie_port *pp);
diff --git a/drivers/pci/host/pcie-spear13xx.c b/drivers/pci/host/pcie-spear13xx.c
index b95b756..ffdff1d 100644
--- a/drivers/pci/host/pcie-spear13xx.c
+++ b/drivers/pci/host/pcie-spear13xx.c
@@ -13,7 +13,6 @@ 
  */
 
 #include <linux/clk.h>
-#include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -149,7 +148,6 @@  static int spear13xx_pcie_establish_link(struct pcie_port *pp)
 	struct spear13xx_pcie *spear13xx_pcie = to_spear13xx_pcie(pp);
 	struct pcie_app_reg *app_reg = spear13xx_pcie->app_base;
 	u32 exp_cap_off = EXP_CAP_ID_OFFSET;
-	unsigned int retries;
 
 	if (dw_pcie_link_up(pp)) {
 		dev_err(pp->dev, "link already up\n");
@@ -201,15 +199,9 @@  static int spear13xx_pcie_establish_link(struct pcie_port *pp)
 			&app_reg->app_ctrl_0);
 
 	/* check if the link is up or not */
-	for (retries = 0; retries < 10; retries++) {
-		if (dw_pcie_link_up(pp)) {
-			dev_info(pp->dev, "link up\n");
-			return 0;
-		}
-		mdelay(100);
-	}
+	if (!dw_pcie_wait_for_link(pp))
+		return 0;
 
-	dev_err(pp->dev, "link Fail\n");
 	return -EINVAL;
 }