Merge branch 'pci/host-designware' into next
* pci/host-designware: PCI: designware: Add driver for prototyping kits based on ARC SDP PCI: designware: Add default link up check if sub-driver doesn't override PCI: designware: Add generic dw_pcie_wait_for_link() ARC: Add PCI support
This commit is contained in:
		
						commit
						562df5c852
					
				| @ -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>; | ||||
| 	}; | ||||
|  | ||||
| @ -8367,6 +8367,13 @@ L:	linux-pci@vger.kernel.org | ||||
| S:	Maintained | ||||
| F:	drivers/pci/host/*designware* | ||||
| 
 | ||||
| PCI DRIVER FOR SYNOPSYS PROTOTYPING DEVICE | ||||
| M:	Joao Pinto <jpinto@synopsys.com> | ||||
| L:	linux-pci@vger.kernel.org | ||||
| S:	Maintained | ||||
| F:	Documentation/devicetree/bindings/pci/designware-pcie.txt | ||||
| F:	drivers/pci/host/pcie-designware-plat.c | ||||
| 
 | ||||
| PCI DRIVER FOR GENERIC OF HOSTS | ||||
| M:	Will Deacon <will.deacon@arm.com> | ||||
| L:	linux-pci@vger.kernel.org | ||||
|  | ||||
| @ -19,6 +19,7 @@ config ARC | ||||
| 	select GENERIC_FIND_FIRST_BIT | ||||
| 	# for now, we don't need GENERIC_IRQ_PROBE, CONFIG_GENERIC_IRQ_CHIP | ||||
| 	select GENERIC_IRQ_SHOW | ||||
| 	select GENERIC_PCI_IOMAP | ||||
| 	select GENERIC_PENDING_IRQ if SMP | ||||
| 	select GENERIC_SMP_IDLE_THREAD | ||||
| 	select HAVE_ARCH_KGDB | ||||
| @ -39,6 +40,9 @@ config ARC | ||||
| 	select PERF_USE_VMALLOC | ||||
| 	select HAVE_DEBUG_STACKOVERFLOW | ||||
| 
 | ||||
| config MIGHT_HAVE_PCI | ||||
| 	bool | ||||
| 
 | ||||
| config TRACE_IRQFLAGS_SUPPORT | ||||
| 	def_bool y | ||||
| 
 | ||||
| @ -568,6 +572,28 @@ endmenu	 # "ARC Architecture Configuration" | ||||
| source "mm/Kconfig" | ||||
| source "net/Kconfig" | ||||
| source "drivers/Kconfig" | ||||
| 
 | ||||
| menu "Bus Support" | ||||
| 
 | ||||
| config PCI | ||||
| 	bool "PCI support" if MIGHT_HAVE_PCI | ||||
| 	help | ||||
| 	  PCI is the name of a bus system, i.e., the way the CPU talks to | ||||
| 	  the other stuff inside your box.  Find out if your board/platform | ||||
| 	  has PCI. | ||||
| 
 | ||||
| 	  Note: PCIe support for Synopsys Device will be available only | ||||
| 	  when HAPS DX is configured with PCIe RC bitmap. If you have PCI, | ||||
| 	  say Y, otherwise N. | ||||
| 
 | ||||
| config PCI_SYSCALL | ||||
| 	def_bool PCI | ||||
| 
 | ||||
| source "drivers/pci/Kconfig" | ||||
| source "drivers/pci/pcie/Kconfig" | ||||
| 
 | ||||
| endmenu | ||||
| 
 | ||||
| source "fs/Kconfig" | ||||
| source "arch/arc/Kconfig.debug" | ||||
| source "security/Kconfig" | ||||
|  | ||||
| @ -10,5 +10,10 @@ | ||||
| #define ASM_ARC_DMA_H | ||||
| 
 | ||||
| #define MAX_DMA_ADDRESS 0xC0000000 | ||||
| #ifdef CONFIG_PCI | ||||
| extern int isa_dma_bridge_buggy; | ||||
| #else | ||||
| #define isa_dma_bridge_buggy	0 | ||||
| #endif | ||||
| 
 | ||||
| #endif | ||||
|  | ||||
| @ -16,6 +16,15 @@ | ||||
| extern void __iomem *ioremap(unsigned long physaddr, unsigned long size); | ||||
| extern void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size, | ||||
| 				  unsigned long flags); | ||||
| static inline void __iomem *ioport_map(unsigned long port, unsigned int nr) | ||||
| { | ||||
| 	return (void __iomem *)port; | ||||
| } | ||||
| 
 | ||||
| static inline void ioport_unmap(void __iomem *addr) | ||||
| { | ||||
| } | ||||
| 
 | ||||
| extern void iounmap(const void __iomem *addr); | ||||
| 
 | ||||
| #define ioremap_nocache(phy, sz)	ioremap(phy, sz) | ||||
|  | ||||
							
								
								
									
										28
									
								
								arch/arc/include/asm/pci.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								arch/arc/include/asm/pci.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,28 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2015-2016 Synopsys, Inc. (www.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. | ||||
|  */ | ||||
| 
 | ||||
| #ifndef _ASM_ARC_PCI_H | ||||
| #define _ASM_ARC_PCI_H | ||||
| 
 | ||||
| #ifdef __KERNEL__ | ||||
| #include <linux/ioport.h> | ||||
| 
 | ||||
| #define PCIBIOS_MIN_IO 0x100 | ||||
| #define PCIBIOS_MIN_MEM 0x100000 | ||||
| 
 | ||||
| #define pcibios_assign_all_busses()	1 | ||||
| /*
 | ||||
|  * The PCI address space does equal the physical memory address space. | ||||
|  * The networking and block device layers use this boolean for bounce | ||||
|  * buffer decisions. | ||||
|  */ | ||||
| #define PCI_DMA_BUS_IS_PHYS	1 | ||||
| 
 | ||||
| #endif /* __KERNEL__ */ | ||||
| 
 | ||||
| #endif /* _ASM_ARC_PCI_H */ | ||||
| @ -12,6 +12,7 @@ obj-y	:= arcksyms.o setup.o irq.o time.o reset.o ptrace.o process.o devtree.o | ||||
| obj-y	+= signal.o traps.o sys.o troubleshoot.o stacktrace.o disasm.o clk.o | ||||
| obj-$(CONFIG_ISA_ARCOMPACT)		+= entry-compact.o intc-compact.o | ||||
| obj-$(CONFIG_ISA_ARCV2)			+= entry-arcv2.o intc-arcv2.o | ||||
| obj-$(CONFIG_PCI)  			+= pcibios.o | ||||
| 
 | ||||
| obj-$(CONFIG_MODULES)			+= arcksyms.o module.o | ||||
| obj-$(CONFIG_SMP) 			+= smp.o | ||||
|  | ||||
							
								
								
									
										22
									
								
								arch/arc/kernel/pcibios.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								arch/arc/kernel/pcibios.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,22 @@ | ||||
| /*
 | ||||
|  * Copyright (C) 2014-2015 Synopsys, Inc. (www.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/pci.h> | ||||
| 
 | ||||
| /*
 | ||||
|  * We don't have to worry about legacy ISA devices, so nothing to do here | ||||
|  */ | ||||
| resource_size_t pcibios_align_resource(void *data, const struct resource *res, | ||||
| 				resource_size_t size, resource_size_t align) | ||||
| { | ||||
| 	return res->start; | ||||
| } | ||||
| 
 | ||||
| void pcibios_fixup_bus(struct pci_bus *bus) | ||||
| { | ||||
| } | ||||
| @ -11,6 +11,7 @@ menuconfig ARC_PLAT_AXS10X | ||||
| 	select DW_APB_ICTL | ||||
| 	select GPIO_DWAPB | ||||
| 	select OF_GPIO | ||||
| 	select MIGHT_HAVE_PCI | ||||
| 	select GENERIC_IRQ_CHIP | ||||
| 	select ARCH_REQUIRE_GPIOLIB | ||||
| 	help | ||||
|  | ||||
| @ -32,6 +32,7 @@ obj-$(CONFIG_PCI_IOV) += iov.o | ||||
| # Some architectures use the generic PCI setup functions
 | ||||
| #
 | ||||
| obj-$(CONFIG_ALPHA) += setup-irq.o | ||||
| obj-$(CONFIG_ARC) += setup-irq.o | ||||
| obj-$(CONFIG_ARM) += setup-irq.o | ||||
| obj-$(CONFIG_ARM64) += setup-irq.o | ||||
| obj-$(CONFIG_UNICORE32) += setup-irq.o | ||||
|  | ||||
| @ -16,6 +16,7 @@ config PCI_MVEBU | ||||
| 	depends on ARCH_MVEBU || ARCH_DOVE | ||||
| 	depends on OF | ||||
| 
 | ||||
| 
 | ||||
| config PCIE_XILINX_NWL | ||||
| 	bool "NWL PCIe Core" | ||||
| 	depends on ARCH_ZYNQMP | ||||
| @ -26,6 +27,17 @@ config PCIE_XILINX_NWL | ||||
| 	 or End Point. The current option selection will only | ||||
| 	 support root port enabling. | ||||
| 
 | ||||
| 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 a 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 | ||||
| 
 | ||||
|  | ||||
| @ -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 | ||||
|  | ||||
| @ -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,14 +117,7 @@ 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); | ||||
| 	} | ||||
| 
 | ||||
| 	dev_err(pp->dev, "link is not up\n"); | ||||
| 	return -EINVAL; | ||||
| 	return dw_pcie_wait_for_link(pp); | ||||
| } | ||||
| 
 | ||||
| static void dra7xx_pcie_enable_interrupts(struct pcie_port *pp) | ||||
|  | ||||
| @ -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,8 +366,7 @@ 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; | ||||
| 	return -ETIMEDOUT; | ||||
| } | ||||
| 
 | ||||
| static void exynos_pcie_clear_irq_pulse(struct pcie_port *pp) | ||||
|  | ||||
| @ -357,33 +357,14 @@ static void imx6_pcie_init_phy(struct pcie_port *pp) | ||||
| 
 | ||||
| static int imx6_pcie_wait_for_link(struct pcie_port *pp) | ||||
| { | ||||
| 	unsigned int retries; | ||||
| 	/* check if the link is up or not */ | ||||
| 	if (!dw_pcie_wait_for_link(pp)) | ||||
| 		return 0; | ||||
| 
 | ||||
| 	/*
 | ||||
| 	 * Test if the PHY reports that the link is up and also that the LTSSM | ||||
| 	 * training finished. There are three possible states of the link when | ||||
| 	 * this code is called: | ||||
| 	 * 1) The link is DOWN (unlikely) | ||||
| 	 *    The link didn't come up yet for some reason. This usually means | ||||
| 	 *    we have a real problem somewhere, if it happens with a peripheral | ||||
| 	 *    connected. This state calls for inspection of the DEBUG registers. | ||||
| 	 * 2) The link is UP, but still in LTSSM training | ||||
| 	 *    Wait for the training to finish, which should take a very short | ||||
| 	 *    time. If the training does not finish, we have a problem and we | ||||
| 	 *    need to inspect the DEBUG registers. If the training does finish, | ||||
| 	 *    the link is up and operating correctly. | ||||
| 	 * 3) The link is UP and no longer in LTSSM training | ||||
| 	 *    The link is up and operating correctly. | ||||
| 	 */ | ||||
| 	for (retries = 0; retries < 200; retries++) { | ||||
| 		u32 reg = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1); | ||||
| 		if ((reg & PCIE_PHY_DEBUG_R1_XMLH_LINK_UP) && | ||||
| 		    !(reg & PCIE_PHY_DEBUG_R1_XMLH_LINK_IN_TRAINING)) | ||||
| 			return 0; | ||||
| 		usleep_range(1000, 2000); | ||||
| 	} | ||||
| 
 | ||||
| 	return -EINVAL; | ||||
| 	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)); | ||||
| 	return -ETIMEDOUT; | ||||
| } | ||||
| 
 | ||||
| static int imx6_pcie_wait_for_speed_change(struct pcie_port *pp) | ||||
|  | ||||
| @ -97,17 +97,15 @@ static int ks_pcie_establish_link(struct keystone_pcie *ks_pcie) | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	ks_dw_pcie_initiate_link_train(ks_pcie); | ||||
| 	/* check if the link is up or not */ | ||||
| 	for (retries = 0; retries < 200; retries++) { | ||||
| 		if (dw_pcie_link_up(pp)) | ||||
| 			return 0; | ||||
| 		usleep_range(100, 1000); | ||||
| 	for (retries = 0; retries < 5; retries++) { | ||||
| 		ks_dw_pcie_initiate_link_train(ks_pcie); | ||||
| 		if (!dw_pcie_wait_for_link(pp)) | ||||
| 			return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	dev_err(pp->dev, "phy link never came up\n"); | ||||
| 	return -EINVAL; | ||||
| 	return -ETIMEDOUT; | ||||
| } | ||||
| 
 | ||||
| static void ks_pcie_msi_irq_handler(struct irq_desc *desc) | ||||
|  | ||||
							
								
								
									
										138
									
								
								drivers/pci/host/pcie-designware-plat.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								drivers/pci/host/pcie-designware-plat.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,138 @@ | ||||
| /*
 | ||||
|  * 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"); | ||||
| @ -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 PLR_OFFSET			0x700 | ||||
| #define PCIE_PHY_DEBUG_R1		(PLR_OFFSET + 0x2c) | ||||
| #define PCIE_PHY_DEBUG_R1_LINK_UP	0x00000010 | ||||
| 
 | ||||
| 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 -ETIMEDOUT; | ||||
| } | ||||
| 
 | ||||
| int dw_pcie_link_up(struct pcie_port *pp) | ||||
| { | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	if (pp->ops->link_up) | ||||
| 		return pp->ops->link_up(pp); | ||||
| 
 | ||||
| 	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, | ||||
|  | ||||
| @ -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); | ||||
|  | ||||
| @ -116,8 +116,6 @@ static irqreturn_t qcom_pcie_msi_irq_handler(int irq, void *arg) | ||||
| 
 | ||||
| static int qcom_pcie_establish_link(struct qcom_pcie *pcie) | ||||
| { | ||||
| 	struct device *dev = pcie->dev; | ||||
| 	unsigned int retries = 0; | ||||
| 	u32 val; | ||||
| 
 | ||||
| 	if (dw_pcie_link_up(&pcie->pp)) | ||||
| @ -128,15 +126,7 @@ static int qcom_pcie_establish_link(struct qcom_pcie *pcie) | ||||
| 	val |= PCIE20_ELBI_SYS_CTRL_LT_ENABLE; | ||||
| 	writel(val, pcie->elbi + PCIE20_ELBI_SYS_CTRL); | ||||
| 
 | ||||
| 	do { | ||||
| 		if (dw_pcie_link_up(&pcie->pp)) | ||||
| 			return 0; | ||||
| 		usleep_range(250, 1000); | ||||
| 	} while (retries < 200); | ||||
| 
 | ||||
| 	dev_warn(dev, "phy link never came up\n"); | ||||
| 
 | ||||
| 	return -ETIMEDOUT; | ||||
| 	return dw_pcie_wait_for_link(&pcie->pp); | ||||
| } | ||||
| 
 | ||||
| static int qcom_pcie_get_resources_v0(struct qcom_pcie *pcie) | ||||
|  | ||||
| @ -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"); | ||||
| @ -200,17 +198,7 @@ static int spear13xx_pcie_establish_link(struct pcie_port *pp) | ||||
| 			| ((u32)1 << REG_TRANSLATION_ENABLE), | ||||
| 			&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); | ||||
| 	} | ||||
| 
 | ||||
| 	dev_err(pp->dev, "link Fail\n"); | ||||
| 	return -EINVAL; | ||||
| 	return dw_pcie_wait_for_link(pp); | ||||
| } | ||||
| 
 | ||||
| static irqreturn_t spear13xx_pcie_irq_handler(int irq, void *arg) | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user