imx6: update PCIe driver
Signed-off-by: Tim Harvey <tharvey@gateworks.com> Signed-off-by: Luka Perkov <luka@openwrt.org> SVN-Revision: 38080
This commit is contained in:
		@@ -20,7 +20,6 @@ CONFIG_ARCH_REQUIRE_GPIOLIB=y
 | 
				
			|||||||
# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
 | 
					# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
 | 
				
			||||||
# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
 | 
					# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
 | 
				
			||||||
# CONFIG_ARCH_SUNXI is not set
 | 
					# CONFIG_ARCH_SUNXI is not set
 | 
				
			||||||
CONFIG_ARCH_SUPPORTS_MSI=y
 | 
					 | 
				
			||||||
CONFIG_ARCH_SUSPEND_POSSIBLE=y
 | 
					CONFIG_ARCH_SUSPEND_POSSIBLE=y
 | 
				
			||||||
# CONFIG_ARCH_VIRT is not set
 | 
					# CONFIG_ARCH_VIRT is not set
 | 
				
			||||||
CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
 | 
					CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
 | 
				
			||||||
@@ -171,7 +170,6 @@ CONFIG_I2C_BOARDINFO=y
 | 
				
			|||||||
CONFIG_I2C_IMX=y
 | 
					CONFIG_I2C_IMX=y
 | 
				
			||||||
# CONFIG_IMX2_WDT is not set
 | 
					# CONFIG_IMX2_WDT is not set
 | 
				
			||||||
# CONFIG_IMX_DMA is not set
 | 
					# CONFIG_IMX_DMA is not set
 | 
				
			||||||
CONFIG_IMX_PCIE=y
 | 
					 | 
				
			||||||
# CONFIG_IMX_SDMA is not set
 | 
					# CONFIG_IMX_SDMA is not set
 | 
				
			||||||
CONFIG_INITRAMFS_SOURCE=""
 | 
					CONFIG_INITRAMFS_SOURCE=""
 | 
				
			||||||
CONFIG_IRQCHIP=y
 | 
					CONFIG_IRQCHIP=y
 | 
				
			||||||
@@ -190,6 +188,7 @@ CONFIG_M25PXX_USE_FAST_READ=y
 | 
				
			|||||||
# CONFIG_MACH_MX51_BABBAGE is not set
 | 
					# CONFIG_MACH_MX51_BABBAGE is not set
 | 
				
			||||||
CONFIG_MDIO_BOARDINFO=y
 | 
					CONFIG_MDIO_BOARDINFO=y
 | 
				
			||||||
CONFIG_MFD_SYSCON=y
 | 
					CONFIG_MFD_SYSCON=y
 | 
				
			||||||
 | 
					CONFIG_MIGHT_HAVE_PCI=y
 | 
				
			||||||
CONFIG_MMC=y
 | 
					CONFIG_MMC=y
 | 
				
			||||||
CONFIG_MMC_BLOCK=y
 | 
					CONFIG_MMC_BLOCK=y
 | 
				
			||||||
# CONFIG_MMC_MXC is not set
 | 
					# CONFIG_MMC_MXC is not set
 | 
				
			||||||
@@ -244,7 +243,9 @@ CONFIG_OLD_SIGSUSPEND3=y
 | 
				
			|||||||
CONFIG_PAGEFLAGS_EXTENDED=y
 | 
					CONFIG_PAGEFLAGS_EXTENDED=y
 | 
				
			||||||
CONFIG_PAGE_OFFSET=0x80000000
 | 
					CONFIG_PAGE_OFFSET=0x80000000
 | 
				
			||||||
CONFIG_PCI=y
 | 
					CONFIG_PCI=y
 | 
				
			||||||
CONFIG_PCI_MSI=y
 | 
					CONFIG_PCIE_DW=y
 | 
				
			||||||
 | 
					CONFIG_PCI_DOMAINS=y
 | 
				
			||||||
 | 
					CONFIG_PCI_IMX6=y
 | 
				
			||||||
CONFIG_PERF_EVENTS=y
 | 
					CONFIG_PERF_EVENTS=y
 | 
				
			||||||
CONFIG_PERF_USE_VMALLOC=y
 | 
					CONFIG_PERF_USE_VMALLOC=y
 | 
				
			||||||
CONFIG_PHYLIB=y
 | 
					CONFIG_PHYLIB=y
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -271,8 +271,7 @@
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
&pcie {
 | 
					&pcie {
 | 
				
			||||||
	rst-gpios = <&gpio1 29 0>; /* PCIESWT_RST# */
 | 
						reset-gpios = <&gpio1 29 0>;
 | 
				
			||||||
	clken-gpios = <&gpio1 20 0>; /* not used */
 | 
					 | 
				
			||||||
	status = "okay";
 | 
						status = "okay";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	eth1: sky2@8 { /* MAC/PHY on bus 8 */
 | 
						eth1: sky2@8 { /* MAC/PHY on bus 8 */
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										565
									
								
								target/linux/imx6/files-3.10/drivers/pci/host/pcie-designware.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										565
									
								
								target/linux/imx6/files-3.10/drivers/pci/host/pcie-designware.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,565 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Synopsys Designware PCIe host controller driver
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
 | 
				
			||||||
 | 
					 *		http://www.samsung.com
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Jingoo Han <jg1.han@samsung.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/kernel.h>
 | 
				
			||||||
 | 
					#include <linux/module.h>
 | 
				
			||||||
 | 
					#include <linux/of_address.h>
 | 
				
			||||||
 | 
					#include <linux/pci.h>
 | 
				
			||||||
 | 
					#include <linux/pci_regs.h>
 | 
				
			||||||
 | 
					#include <linux/types.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "pcie-designware.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Synopsis specific PCIE configuration registers */
 | 
				
			||||||
 | 
					#define PCIE_PORT_LINK_CONTROL		0x710
 | 
				
			||||||
 | 
					#define PORT_LINK_MODE_MASK		(0x3f << 16)
 | 
				
			||||||
 | 
					#define PORT_LINK_MODE_1_LANES		(0x1 << 16)
 | 
				
			||||||
 | 
					#define PORT_LINK_MODE_2_LANES		(0x3 << 16)
 | 
				
			||||||
 | 
					#define PORT_LINK_MODE_4_LANES		(0x7 << 16)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PCIE_LINK_WIDTH_SPEED_CONTROL	0x80C
 | 
				
			||||||
 | 
					#define PORT_LOGIC_SPEED_CHANGE		(0x1 << 17)
 | 
				
			||||||
 | 
					#define PORT_LOGIC_LINK_WIDTH_MASK	(0x1ff << 8)
 | 
				
			||||||
 | 
					#define PORT_LOGIC_LINK_WIDTH_1_LANES	(0x1 << 8)
 | 
				
			||||||
 | 
					#define PORT_LOGIC_LINK_WIDTH_2_LANES	(0x2 << 8)
 | 
				
			||||||
 | 
					#define PORT_LOGIC_LINK_WIDTH_4_LANES	(0x4 << 8)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PCIE_MSI_ADDR_LO		0x820
 | 
				
			||||||
 | 
					#define PCIE_MSI_ADDR_HI		0x824
 | 
				
			||||||
 | 
					#define PCIE_MSI_INTR0_ENABLE		0x828
 | 
				
			||||||
 | 
					#define PCIE_MSI_INTR0_MASK		0x82C
 | 
				
			||||||
 | 
					#define PCIE_MSI_INTR0_STATUS		0x830
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PCIE_ATU_VIEWPORT		0x900
 | 
				
			||||||
 | 
					#define PCIE_ATU_REGION_INBOUND		(0x1 << 31)
 | 
				
			||||||
 | 
					#define PCIE_ATU_REGION_OUTBOUND	(0x0 << 31)
 | 
				
			||||||
 | 
					#define PCIE_ATU_REGION_INDEX1		(0x1 << 0)
 | 
				
			||||||
 | 
					#define PCIE_ATU_REGION_INDEX0		(0x0 << 0)
 | 
				
			||||||
 | 
					#define PCIE_ATU_CR1			0x904
 | 
				
			||||||
 | 
					#define PCIE_ATU_TYPE_MEM		(0x0 << 0)
 | 
				
			||||||
 | 
					#define PCIE_ATU_TYPE_IO		(0x2 << 0)
 | 
				
			||||||
 | 
					#define PCIE_ATU_TYPE_CFG0		(0x4 << 0)
 | 
				
			||||||
 | 
					#define PCIE_ATU_TYPE_CFG1		(0x5 << 0)
 | 
				
			||||||
 | 
					#define PCIE_ATU_CR2			0x908
 | 
				
			||||||
 | 
					#define PCIE_ATU_ENABLE			(0x1 << 31)
 | 
				
			||||||
 | 
					#define PCIE_ATU_BAR_MODE_ENABLE	(0x1 << 30)
 | 
				
			||||||
 | 
					#define PCIE_ATU_LOWER_BASE		0x90C
 | 
				
			||||||
 | 
					#define PCIE_ATU_UPPER_BASE		0x910
 | 
				
			||||||
 | 
					#define PCIE_ATU_LIMIT			0x914
 | 
				
			||||||
 | 
					#define PCIE_ATU_LOWER_TARGET		0x918
 | 
				
			||||||
 | 
					#define PCIE_ATU_BUS(x)			(((x) & 0xff) << 24)
 | 
				
			||||||
 | 
					#define PCIE_ATU_DEV(x)			(((x) & 0x1f) << 19)
 | 
				
			||||||
 | 
					#define PCIE_ATU_FUNC(x)		(((x) & 0x7) << 16)
 | 
				
			||||||
 | 
					#define PCIE_ATU_UPPER_TARGET		0x91C
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct hw_pci dw_pci;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					unsigned long global_io_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline struct pcie_port *sys_to_pcie(struct pci_sys_data *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return sys->private_data;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int cfg_read(void __iomem *addr, int where, int size, u32 *val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						*val = readl(addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (size == 1)
 | 
				
			||||||
 | 
							*val = (*val >> (8 * (where & 3))) & 0xff;
 | 
				
			||||||
 | 
						else if (size == 2)
 | 
				
			||||||
 | 
							*val = (*val >> (8 * (where & 3))) & 0xffff;
 | 
				
			||||||
 | 
						else if (size != 4)
 | 
				
			||||||
 | 
							return PCIBIOS_BAD_REGISTER_NUMBER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return PCIBIOS_SUCCESSFUL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int cfg_write(void __iomem *addr, int where, int size, u32 val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (size == 4)
 | 
				
			||||||
 | 
							writel(val, addr);
 | 
				
			||||||
 | 
						else if (size == 2)
 | 
				
			||||||
 | 
							writew(val, addr + (where & 2));
 | 
				
			||||||
 | 
						else if (size == 1)
 | 
				
			||||||
 | 
							writeb(val, addr + (where & 3));
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return PCIBIOS_BAD_REGISTER_NUMBER;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return PCIBIOS_SUCCESSFUL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void dw_pcie_readl_rc(struct pcie_port *pp, u32 reg, u32 *val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (pp->ops->readl_rc)
 | 
				
			||||||
 | 
							pp->ops->readl_rc(pp, pp->dbi_base + reg, val);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							*val = readl(pp->dbi_base + reg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void dw_pcie_writel_rc(struct pcie_port *pp, u32 val, u32 reg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (pp->ops->writel_rc)
 | 
				
			||||||
 | 
							pp->ops->writel_rc(pp, val, pp->dbi_base + reg);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							writel(val, pp->dbi_base + reg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size,
 | 
				
			||||||
 | 
									u32 *val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pp->ops->rd_own_conf)
 | 
				
			||||||
 | 
							ret = pp->ops->rd_own_conf(pp, where, size, val);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ret = cfg_read(pp->dbi_base + (where & ~0x3), where, size, val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size,
 | 
				
			||||||
 | 
									u32 val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pp->ops->wr_own_conf)
 | 
				
			||||||
 | 
							ret = pp->ops->wr_own_conf(pp, where, size, val);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ret = cfg_write(pp->dbi_base + (where & ~0x3), where, size,
 | 
				
			||||||
 | 
									val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dw_pcie_link_up(struct pcie_port *pp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (pp->ops->link_up)
 | 
				
			||||||
 | 
							return pp->ops->link_up(pp);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int __init dw_pcie_host_init(struct pcie_port *pp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct device_node *np = pp->dev->of_node;
 | 
				
			||||||
 | 
						struct of_pci_range range;
 | 
				
			||||||
 | 
						struct of_pci_range_parser parser;
 | 
				
			||||||
 | 
						u32 val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (of_pci_range_parser_init(&parser, np)) {
 | 
				
			||||||
 | 
							dev_err(pp->dev, "missing ranges property\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Get the I/O and memory ranges from DT */
 | 
				
			||||||
 | 
						for_each_of_pci_range(&parser, &range) {
 | 
				
			||||||
 | 
							unsigned long restype = range.flags & IORESOURCE_TYPE_BITS;
 | 
				
			||||||
 | 
							if (restype == IORESOURCE_IO) {
 | 
				
			||||||
 | 
								of_pci_range_to_resource(&range, np, &pp->io);
 | 
				
			||||||
 | 
								pp->io.name = "I/O";
 | 
				
			||||||
 | 
								pp->io.start = max_t(resource_size_t,
 | 
				
			||||||
 | 
										     PCIBIOS_MIN_IO,
 | 
				
			||||||
 | 
										     range.pci_addr + global_io_offset);
 | 
				
			||||||
 | 
								pp->io.end = min_t(resource_size_t,
 | 
				
			||||||
 | 
										   IO_SPACE_LIMIT,
 | 
				
			||||||
 | 
										   range.pci_addr + range.size
 | 
				
			||||||
 | 
										   + global_io_offset);
 | 
				
			||||||
 | 
								pp->config.io_size = resource_size(&pp->io);
 | 
				
			||||||
 | 
								pp->config.io_bus_addr = range.pci_addr;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (restype == IORESOURCE_MEM) {
 | 
				
			||||||
 | 
								of_pci_range_to_resource(&range, np, &pp->mem);
 | 
				
			||||||
 | 
								pp->mem.name = "MEM";
 | 
				
			||||||
 | 
								pp->config.mem_size = resource_size(&pp->mem);
 | 
				
			||||||
 | 
								pp->config.mem_bus_addr = range.pci_addr;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (restype == 0) {
 | 
				
			||||||
 | 
								of_pci_range_to_resource(&range, np, &pp->cfg);
 | 
				
			||||||
 | 
								pp->config.cfg0_size = resource_size(&pp->cfg)/2;
 | 
				
			||||||
 | 
								pp->config.cfg1_size = resource_size(&pp->cfg)/2;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pp->dbi_base) {
 | 
				
			||||||
 | 
							pp->dbi_base = devm_ioremap(pp->dev, pp->cfg.start,
 | 
				
			||||||
 | 
										resource_size(&pp->cfg));
 | 
				
			||||||
 | 
							if (!pp->dbi_base) {
 | 
				
			||||||
 | 
								dev_err(pp->dev, "error with ioremap\n");
 | 
				
			||||||
 | 
								return -ENOMEM;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pp->cfg0_base = pp->cfg.start;
 | 
				
			||||||
 | 
						pp->cfg1_base = pp->cfg.start + pp->config.cfg0_size;
 | 
				
			||||||
 | 
						pp->io_base = pp->io.start;
 | 
				
			||||||
 | 
						pp->mem_base = pp->mem.start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pp->va_cfg0_base = devm_ioremap(pp->dev, pp->cfg0_base,
 | 
				
			||||||
 | 
										pp->config.cfg0_size);
 | 
				
			||||||
 | 
						if (!pp->va_cfg0_base) {
 | 
				
			||||||
 | 
							dev_err(pp->dev, "error with ioremap in function\n");
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						pp->va_cfg1_base = devm_ioremap(pp->dev, pp->cfg1_base,
 | 
				
			||||||
 | 
										pp->config.cfg1_size);
 | 
				
			||||||
 | 
						if (!pp->va_cfg1_base) {
 | 
				
			||||||
 | 
							dev_err(pp->dev, "error with ioremap\n");
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (of_property_read_u32(np, "num-lanes", &pp->lanes)) {
 | 
				
			||||||
 | 
							dev_err(pp->dev, "Failed to parse the number of lanes\n");
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pp->ops->host_init)
 | 
				
			||||||
 | 
							pp->ops->host_init(pp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dw_pcie_wr_own_conf(pp, PCI_BASE_ADDRESS_0, 4, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* program correct class for RC */
 | 
				
			||||||
 | 
						dw_pcie_wr_own_conf(pp, PCI_CLASS_DEVICE, 2, PCI_CLASS_BRIDGE_PCI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dw_pcie_rd_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, &val);
 | 
				
			||||||
 | 
						val |= PORT_LOGIC_SPEED_CHANGE;
 | 
				
			||||||
 | 
						dw_pcie_wr_own_conf(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, 4, val);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dw_pci.nr_controllers = 1;
 | 
				
			||||||
 | 
						dw_pci.private_data = (void **)&pp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pci_common_init(&dw_pci);
 | 
				
			||||||
 | 
						pci_assign_unassigned_resources();
 | 
				
			||||||
 | 
					#ifdef CONFIG_PCI_DOMAINS
 | 
				
			||||||
 | 
						dw_pci.domain++;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void dw_pcie_prog_viewport_cfg0(struct pcie_port *pp, u32 busdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Program viewport 0 : OUTBOUND : CFG0 */
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
 | 
				
			||||||
 | 
								  PCIE_ATU_VIEWPORT);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->cfg0_base, PCIE_ATU_LOWER_BASE);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, (pp->cfg0_base >> 32), PCIE_ATU_UPPER_BASE);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->cfg0_base + pp->config.cfg0_size - 1,
 | 
				
			||||||
 | 
								  PCIE_ATU_LIMIT);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG0, PCIE_ATU_CR1);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void dw_pcie_prog_viewport_cfg1(struct pcie_port *pp, u32 busdev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Program viewport 1 : OUTBOUND : CFG1 */
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
 | 
				
			||||||
 | 
								  PCIE_ATU_VIEWPORT);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_CFG1, PCIE_ATU_CR1);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->cfg1_base, PCIE_ATU_LOWER_BASE);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, (pp->cfg1_base >> 32), PCIE_ATU_UPPER_BASE);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->cfg1_base + pp->config.cfg1_size - 1,
 | 
				
			||||||
 | 
								  PCIE_ATU_LIMIT);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, busdev, PCIE_ATU_LOWER_TARGET);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, 0, PCIE_ATU_UPPER_TARGET);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void dw_pcie_prog_viewport_mem_outbound(struct pcie_port *pp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Program viewport 0 : OUTBOUND : MEM */
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX0,
 | 
				
			||||||
 | 
								  PCIE_ATU_VIEWPORT);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_MEM, PCIE_ATU_CR1);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->mem_base, PCIE_ATU_LOWER_BASE);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, (pp->mem_base >> 32), PCIE_ATU_UPPER_BASE);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->mem_base + pp->config.mem_size - 1,
 | 
				
			||||||
 | 
								  PCIE_ATU_LIMIT);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->config.mem_bus_addr, PCIE_ATU_LOWER_TARGET);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, upper_32_bits(pp->config.mem_bus_addr),
 | 
				
			||||||
 | 
								  PCIE_ATU_UPPER_TARGET);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void dw_pcie_prog_viewport_io_outbound(struct pcie_port *pp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Program viewport 1 : OUTBOUND : IO */
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_REGION_OUTBOUND | PCIE_ATU_REGION_INDEX1,
 | 
				
			||||||
 | 
								  PCIE_ATU_VIEWPORT);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_TYPE_IO, PCIE_ATU_CR1);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, PCIE_ATU_ENABLE, PCIE_ATU_CR2);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->io_base, PCIE_ATU_LOWER_BASE);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, (pp->io_base >> 32), PCIE_ATU_UPPER_BASE);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->io_base + pp->config.io_size - 1,
 | 
				
			||||||
 | 
								  PCIE_ATU_LIMIT);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, pp->config.io_bus_addr, PCIE_ATU_LOWER_TARGET);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, upper_32_bits(pp->config.io_bus_addr),
 | 
				
			||||||
 | 
								  PCIE_ATU_UPPER_TARGET);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dw_pcie_rd_other_conf(struct pcie_port *pp, struct pci_bus *bus,
 | 
				
			||||||
 | 
							u32 devfn, int where, int size, u32 *val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret = PCIBIOS_SUCCESSFUL;
 | 
				
			||||||
 | 
						u32 address, busdev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
 | 
				
			||||||
 | 
							 PCIE_ATU_FUNC(PCI_FUNC(devfn));
 | 
				
			||||||
 | 
						address = where & ~0x3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bus->parent->number == pp->root_bus_nr) {
 | 
				
			||||||
 | 
							dw_pcie_prog_viewport_cfg0(pp, busdev);
 | 
				
			||||||
 | 
							ret = cfg_read(pp->va_cfg0_base + address, where, size, val);
 | 
				
			||||||
 | 
							dw_pcie_prog_viewport_mem_outbound(pp);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							dw_pcie_prog_viewport_cfg1(pp, busdev);
 | 
				
			||||||
 | 
							ret = cfg_read(pp->va_cfg1_base + address, where, size, val);
 | 
				
			||||||
 | 
							dw_pcie_prog_viewport_io_outbound(pp);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dw_pcie_wr_other_conf(struct pcie_port *pp, struct pci_bus *bus,
 | 
				
			||||||
 | 
							u32 devfn, int where, int size, u32 val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int ret = PCIBIOS_SUCCESSFUL;
 | 
				
			||||||
 | 
						u32 address, busdev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						busdev = PCIE_ATU_BUS(bus->number) | PCIE_ATU_DEV(PCI_SLOT(devfn)) |
 | 
				
			||||||
 | 
							 PCIE_ATU_FUNC(PCI_FUNC(devfn));
 | 
				
			||||||
 | 
						address = where & ~0x3;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bus->parent->number == pp->root_bus_nr) {
 | 
				
			||||||
 | 
							dw_pcie_prog_viewport_cfg0(pp, busdev);
 | 
				
			||||||
 | 
							ret = cfg_write(pp->va_cfg0_base + address, where, size, val);
 | 
				
			||||||
 | 
							dw_pcie_prog_viewport_mem_outbound(pp);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							dw_pcie_prog_viewport_cfg1(pp, busdev);
 | 
				
			||||||
 | 
							ret = cfg_write(pp->va_cfg1_base + address, where, size, val);
 | 
				
			||||||
 | 
							dw_pcie_prog_viewport_io_outbound(pp);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dw_pcie_valid_config(struct pcie_port *pp,
 | 
				
			||||||
 | 
									struct pci_bus *bus, int dev)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* If there is no link, then there is no device */
 | 
				
			||||||
 | 
						if (bus->number != pp->root_bus_nr) {
 | 
				
			||||||
 | 
							if (!dw_pcie_link_up(pp))
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* access only one slot on each root port */
 | 
				
			||||||
 | 
						if (bus->number == pp->root_bus_nr && dev > 0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * do not read more than one device on the bus directly attached
 | 
				
			||||||
 | 
						 * to RC's (Virtual Bridge's) DS side.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						if (bus->primary == pp->root_bus_nr && dev > 0)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dw_pcie_rd_conf(struct pci_bus *bus, u32 devfn, int where,
 | 
				
			||||||
 | 
								int size, u32 *val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pcie_port *pp = sys_to_pcie(bus->sysdata);
 | 
				
			||||||
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pp) {
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0) {
 | 
				
			||||||
 | 
							*val = 0xffffffff;
 | 
				
			||||||
 | 
							return PCIBIOS_DEVICE_NOT_FOUND;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock_irqsave(&pp->conf_lock, flags);
 | 
				
			||||||
 | 
						if (bus->number != pp->root_bus_nr)
 | 
				
			||||||
 | 
							ret = dw_pcie_rd_other_conf(pp, bus, devfn,
 | 
				
			||||||
 | 
											where, size, val);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ret = dw_pcie_rd_own_conf(pp, where, size, val);
 | 
				
			||||||
 | 
						spin_unlock_irqrestore(&pp->conf_lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int dw_pcie_wr_conf(struct pci_bus *bus, u32 devfn,
 | 
				
			||||||
 | 
								int where, int size, u32 val)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pcie_port *pp = sys_to_pcie(bus->sysdata);
 | 
				
			||||||
 | 
						unsigned long flags;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pp) {
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dw_pcie_valid_config(pp, bus, PCI_SLOT(devfn)) == 0)
 | 
				
			||||||
 | 
							return PCIBIOS_DEVICE_NOT_FOUND;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spin_lock_irqsave(&pp->conf_lock, flags);
 | 
				
			||||||
 | 
						if (bus->number != pp->root_bus_nr)
 | 
				
			||||||
 | 
							ret = dw_pcie_wr_other_conf(pp, bus, devfn,
 | 
				
			||||||
 | 
											where, size, val);
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							ret = dw_pcie_wr_own_conf(pp, where, size, val);
 | 
				
			||||||
 | 
						spin_unlock_irqrestore(&pp->conf_lock, flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct pci_ops dw_pcie_ops = {
 | 
				
			||||||
 | 
						.read = dw_pcie_rd_conf,
 | 
				
			||||||
 | 
						.write = dw_pcie_wr_conf,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dw_pcie_setup(int nr, struct pci_sys_data *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pcie_port *pp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pp = sys_to_pcie(sys);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pp)
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (global_io_offset < SZ_1M && pp->config.io_size > 0) {
 | 
				
			||||||
 | 
							sys->io_offset = global_io_offset - pp->config.io_bus_addr;
 | 
				
			||||||
 | 
							pci_ioremap_io(sys->io_offset, pp->io.start);
 | 
				
			||||||
 | 
							global_io_offset += SZ_64K;
 | 
				
			||||||
 | 
							pci_add_resource_offset(&sys->resources, &pp->io,
 | 
				
			||||||
 | 
										sys->io_offset);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						sys->mem_offset = pp->mem.start - pp->config.mem_bus_addr;
 | 
				
			||||||
 | 
						pci_add_resource_offset(&sys->resources, &pp->mem, sys->mem_offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pci_bus *bus;
 | 
				
			||||||
 | 
						struct pcie_port *pp = sys_to_pcie(sys);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (pp) {
 | 
				
			||||||
 | 
							pp->root_bus_nr = sys->busnr;
 | 
				
			||||||
 | 
							bus = pci_scan_root_bus(NULL, sys->busnr, &dw_pcie_ops,
 | 
				
			||||||
 | 
										sys, &sys->resources);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							bus = NULL;
 | 
				
			||||||
 | 
							BUG();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return bus;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pcie_port *pp = sys_to_pcie(dev->bus->sysdata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return pp->irq;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct hw_pci dw_pci = {
 | 
				
			||||||
 | 
						.setup		= dw_pcie_setup,
 | 
				
			||||||
 | 
						.scan		= dw_pcie_scan_bus,
 | 
				
			||||||
 | 
						.map_irq	= dw_pcie_map_irq,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void dw_pcie_setup_rc(struct pcie_port *pp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct pcie_port_info *config = &pp->config;
 | 
				
			||||||
 | 
						u32 val;
 | 
				
			||||||
 | 
						u32 membase;
 | 
				
			||||||
 | 
						u32 memlimit;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* set the number of lines as 4 */
 | 
				
			||||||
 | 
						dw_pcie_readl_rc(pp, PCIE_PORT_LINK_CONTROL, &val);
 | 
				
			||||||
 | 
						val &= ~PORT_LINK_MODE_MASK;
 | 
				
			||||||
 | 
						switch (pp->lanes) {
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							val |= PORT_LINK_MODE_1_LANES;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case 2:
 | 
				
			||||||
 | 
							val |= PORT_LINK_MODE_2_LANES;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case 4:
 | 
				
			||||||
 | 
							val |= PORT_LINK_MODE_4_LANES;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, val, PCIE_PORT_LINK_CONTROL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* set link width speed control register */
 | 
				
			||||||
 | 
						dw_pcie_readl_rc(pp, PCIE_LINK_WIDTH_SPEED_CONTROL, &val);
 | 
				
			||||||
 | 
						val &= ~PORT_LOGIC_LINK_WIDTH_MASK;
 | 
				
			||||||
 | 
						switch (pp->lanes) {
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							val |= PORT_LOGIC_LINK_WIDTH_1_LANES;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case 2:
 | 
				
			||||||
 | 
							val |= PORT_LOGIC_LINK_WIDTH_2_LANES;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case 4:
 | 
				
			||||||
 | 
							val |= PORT_LOGIC_LINK_WIDTH_4_LANES;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, val, PCIE_LINK_WIDTH_SPEED_CONTROL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* setup RC BARs */
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_0);
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, 0x00000004, PCI_BASE_ADDRESS_1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* setup interrupt pins */
 | 
				
			||||||
 | 
						dw_pcie_readl_rc(pp, PCI_INTERRUPT_LINE, &val);
 | 
				
			||||||
 | 
						val &= 0xffff00ff;
 | 
				
			||||||
 | 
						val |= 0x00000100;
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, val, PCI_INTERRUPT_LINE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* setup bus numbers */
 | 
				
			||||||
 | 
						dw_pcie_readl_rc(pp, PCI_PRIMARY_BUS, &val);
 | 
				
			||||||
 | 
						val &= 0xff000000;
 | 
				
			||||||
 | 
						val |= 0x00010100;
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, val, PCI_PRIMARY_BUS);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* setup memory base, memory limit */
 | 
				
			||||||
 | 
						membase = ((u32)pp->mem_base & 0xfff00000) >> 16;
 | 
				
			||||||
 | 
						memlimit = (config->mem_size + (u32)pp->mem_base) & 0xfff00000;
 | 
				
			||||||
 | 
						val = memlimit | membase;
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, val, PCI_MEMORY_BASE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* setup command register */
 | 
				
			||||||
 | 
						dw_pcie_readl_rc(pp, PCI_COMMAND, &val);
 | 
				
			||||||
 | 
						val &= 0xffff0000;
 | 
				
			||||||
 | 
						val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
 | 
				
			||||||
 | 
							PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
 | 
				
			||||||
 | 
						dw_pcie_writel_rc(pp, val, PCI_COMMAND);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					MODULE_AUTHOR("Jingoo Han <jg1.han@samsung.com>");
 | 
				
			||||||
 | 
					MODULE_DESCRIPTION("Designware PCIe host controller driver");
 | 
				
			||||||
 | 
					MODULE_LICENSE("GPL v2");
 | 
				
			||||||
@@ -0,0 +1,65 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Synopsys Designware PCIe host controller driver
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Copyright (C) 2013 Samsung Electronics Co., Ltd.
 | 
				
			||||||
 | 
					 *		http://www.samsung.com
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Jingoo Han <jg1.han@samsung.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.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pcie_port_info {
 | 
				
			||||||
 | 
						u32		cfg0_size;
 | 
				
			||||||
 | 
						u32		cfg1_size;
 | 
				
			||||||
 | 
						u32		io_size;
 | 
				
			||||||
 | 
						u32		mem_size;
 | 
				
			||||||
 | 
						phys_addr_t	io_bus_addr;
 | 
				
			||||||
 | 
						phys_addr_t	mem_bus_addr;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pcie_port {
 | 
				
			||||||
 | 
						struct device		*dev;
 | 
				
			||||||
 | 
						u8			root_bus_nr;
 | 
				
			||||||
 | 
						void __iomem		*dbi_base;
 | 
				
			||||||
 | 
						u64			cfg0_base;
 | 
				
			||||||
 | 
						void __iomem		*va_cfg0_base;
 | 
				
			||||||
 | 
						u64			cfg1_base;
 | 
				
			||||||
 | 
						void __iomem		*va_cfg1_base;
 | 
				
			||||||
 | 
						u64			io_base;
 | 
				
			||||||
 | 
						u64			mem_base;
 | 
				
			||||||
 | 
						spinlock_t		conf_lock;
 | 
				
			||||||
 | 
						struct resource		cfg;
 | 
				
			||||||
 | 
						struct resource		io;
 | 
				
			||||||
 | 
						struct resource		mem;
 | 
				
			||||||
 | 
						struct pcie_port_info	config;
 | 
				
			||||||
 | 
						int			irq;
 | 
				
			||||||
 | 
						u32			lanes;
 | 
				
			||||||
 | 
						struct pcie_host_ops	*ops;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct pcie_host_ops {
 | 
				
			||||||
 | 
						void (*readl_rc)(struct pcie_port *pp,
 | 
				
			||||||
 | 
								void __iomem *dbi_base, u32 *val);
 | 
				
			||||||
 | 
						void (*writel_rc)(struct pcie_port *pp,
 | 
				
			||||||
 | 
								u32 val, void __iomem *dbi_base);
 | 
				
			||||||
 | 
						int (*rd_own_conf)(struct pcie_port *pp, int where, int size, u32 *val);
 | 
				
			||||||
 | 
						int (*wr_own_conf)(struct pcie_port *pp, int where, int size, u32 val);
 | 
				
			||||||
 | 
						int (*link_up)(struct pcie_port *pp);
 | 
				
			||||||
 | 
						void (*host_init)(struct pcie_port *pp);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern unsigned long global_io_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int cfg_read(void __iomem *addr, int where, int size, u32 *val);
 | 
				
			||||||
 | 
					int cfg_write(void __iomem *addr, int where, int size, u32 val);
 | 
				
			||||||
 | 
					int dw_pcie_wr_own_conf(struct pcie_port *pp, int where, int size, u32 val);
 | 
				
			||||||
 | 
					int dw_pcie_rd_own_conf(struct pcie_port *pp, int where, int size, u32 *val);
 | 
				
			||||||
 | 
					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);
 | 
				
			||||||
 | 
					int dw_pcie_setup(int nr, struct pci_sys_data *sys);
 | 
				
			||||||
 | 
					struct pci_bus *dw_pcie_scan_bus(int nr, struct pci_sys_data *sys);
 | 
				
			||||||
 | 
					int dw_pcie_map_irq(const struct pci_dev *dev, u8 slot, u8 pin);
 | 
				
			||||||
@@ -0,0 +1,201 @@
 | 
				
			|||||||
 | 
					From: Andrew Murray <Andrew.Murray@arm.com>
 | 
				
			||||||
 | 
					Subject: [PATCH] of/pci: Provide support for parsing PCI DT ranges property
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This patch factors out common implementation patterns to reduce overall kernel
 | 
				
			||||||
 | 
					code and provide a means for host bridge drivers to directly obtain struct
 | 
				
			||||||
 | 
					resources from the DT's ranges property without relying on architecture specific
 | 
				
			||||||
 | 
					DT handling. This will make it easier to write archiecture independent host bridge
 | 
				
			||||||
 | 
					drivers and mitigate against further duplication of DT parsing code.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					This patch can be used in the following way:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct of_pci_range_parser parser;
 | 
				
			||||||
 | 
						struct of_pci_range range;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (of_pci_range_parser_init(&parser, np))
 | 
				
			||||||
 | 
							; //no ranges property
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for_each_of_pci_range(&parser, &range) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
								directly access properties of the address range, e.g.:
 | 
				
			||||||
 | 
								range.pci_space, range.pci_addr, range.cpu_addr,
 | 
				
			||||||
 | 
								range.size, range.flags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								alternatively obtain a struct resource, e.g.:
 | 
				
			||||||
 | 
								struct resource res;
 | 
				
			||||||
 | 
								of_pci_range_to_resource(&range, np, &res);
 | 
				
			||||||
 | 
							*/
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Additionally the implementation takes care of adjacent ranges and merges them
 | 
				
			||||||
 | 
					into a single range (as was the case with powerpc and microblaze).
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Signed-off-by: Andrew Murray <Andrew.Murray@arm.com>
 | 
				
			||||||
 | 
					Signed-off-by: Liviu Dudau <Liviu.Dudau@arm.com>
 | 
				
			||||||
 | 
					Signed-off-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
 | 
				
			||||||
 | 
					Reviewed-by: Rob Herring <rob.herring@calxeda.com>
 | 
				
			||||||
 | 
					Tested-by: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
 | 
				
			||||||
 | 
					Tested-by: Linus Walleij <linus.walleij@linaro.org>
 | 
				
			||||||
 | 
					Tested-by: Jingoo Han <jg1.han@samsung.com>
 | 
				
			||||||
 | 
					Acked-by: Grant Likely <grant.likely@secretlab.ca>
 | 
				
			||||||
 | 
					Signed-off-by: Jason Cooper <jason@lakedaemon.net>
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					 drivers/of/address.c       | 67 ++++++++++++++++++++++++++++++++++++++++++++++
 | 
				
			||||||
 | 
					 include/linux/of_address.h | 48 +++++++++++++++++++++++++++++++++
 | 
				
			||||||
 | 
					 2 files changed, 115 insertions(+)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					diff --git a/drivers/of/address.c b/drivers/of/address.c
 | 
				
			||||||
 | 
					index 04da786..fdd0636 100644
 | 
				
			||||||
 | 
					--- a/drivers/of/address.c
 | 
				
			||||||
 | 
					+++ b/drivers/of/address.c
 | 
				
			||||||
 | 
					@@ -227,6 +227,73 @@ int of_pci_address_to_resource(struct device_node *dev, int bar,
 | 
				
			||||||
 | 
					 	return __of_address_to_resource(dev, addrp, size, flags, NULL, r);
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 EXPORT_SYMBOL_GPL(of_pci_address_to_resource);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+int of_pci_range_parser_init(struct of_pci_range_parser *parser,
 | 
				
			||||||
 | 
					+				struct device_node *node)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	const int na = 3, ns = 2;
 | 
				
			||||||
 | 
					+	int rlen;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	parser->node = node;
 | 
				
			||||||
 | 
					+	parser->pna = of_n_addr_cells(node);
 | 
				
			||||||
 | 
					+	parser->np = parser->pna + na + ns;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	parser->range = of_get_property(node, "ranges", &rlen);
 | 
				
			||||||
 | 
					+	if (parser->range == NULL)
 | 
				
			||||||
 | 
					+		return -ENOENT;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	parser->end = parser->range + rlen / sizeof(__be32);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+EXPORT_SYMBOL_GPL(of_pci_range_parser_init);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+struct of_pci_range *of_pci_range_parser_one(struct of_pci_range_parser *parser,
 | 
				
			||||||
 | 
					+						struct of_pci_range *range)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	const int na = 3, ns = 2;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	if (!range)
 | 
				
			||||||
 | 
					+		return NULL;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	if (!parser->range || parser->range + parser->np > parser->end)
 | 
				
			||||||
 | 
					+		return NULL;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	range->pci_space = parser->range[0];
 | 
				
			||||||
 | 
					+	range->flags = of_bus_pci_get_flags(parser->range);
 | 
				
			||||||
 | 
					+	range->pci_addr = of_read_number(parser->range + 1, ns);
 | 
				
			||||||
 | 
					+	range->cpu_addr = of_translate_address(parser->node,
 | 
				
			||||||
 | 
					+				parser->range + na);
 | 
				
			||||||
 | 
					+	range->size = of_read_number(parser->range + parser->pna + na, ns);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	parser->range += parser->np;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* Now consume following elements while they are contiguous */
 | 
				
			||||||
 | 
					+	while (parser->range + parser->np <= parser->end) {
 | 
				
			||||||
 | 
					+		u32 flags, pci_space;
 | 
				
			||||||
 | 
					+		u64 pci_addr, cpu_addr, size;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+		pci_space = be32_to_cpup(parser->range);
 | 
				
			||||||
 | 
					+		flags = of_bus_pci_get_flags(parser->range);
 | 
				
			||||||
 | 
					+		pci_addr = of_read_number(parser->range + 1, ns);
 | 
				
			||||||
 | 
					+		cpu_addr = of_translate_address(parser->node,
 | 
				
			||||||
 | 
					+				parser->range + na);
 | 
				
			||||||
 | 
					+		size = of_read_number(parser->range + parser->pna + na, ns);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+		if (flags != range->flags)
 | 
				
			||||||
 | 
					+			break;
 | 
				
			||||||
 | 
					+		if (pci_addr != range->pci_addr + range->size ||
 | 
				
			||||||
 | 
					+		    cpu_addr != range->cpu_addr + range->size)
 | 
				
			||||||
 | 
					+			break;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+		range->size += size;
 | 
				
			||||||
 | 
					+		parser->range += parser->np;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return range;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+EXPORT_SYMBOL_GPL(of_pci_range_parser_one);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 #endif /* CONFIG_PCI */
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 /*
 | 
				
			||||||
 | 
					diff --git a/include/linux/of_address.h b/include/linux/of_address.h
 | 
				
			||||||
 | 
					index 0506eb5..4c2e6f2 100644
 | 
				
			||||||
 | 
					--- a/include/linux/of_address.h
 | 
				
			||||||
 | 
					+++ b/include/linux/of_address.h
 | 
				
			||||||
 | 
					@@ -4,6 +4,36 @@
 | 
				
			||||||
 | 
					 #include <linux/errno.h>
 | 
				
			||||||
 | 
					 #include <linux/of.h>
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+struct of_pci_range_parser {
 | 
				
			||||||
 | 
					+	struct device_node *node;
 | 
				
			||||||
 | 
					+	const __be32 *range;
 | 
				
			||||||
 | 
					+	const __be32 *end;
 | 
				
			||||||
 | 
					+	int np;
 | 
				
			||||||
 | 
					+	int pna;
 | 
				
			||||||
 | 
					+};
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+struct of_pci_range {
 | 
				
			||||||
 | 
					+	u32 pci_space;
 | 
				
			||||||
 | 
					+	u64 pci_addr;
 | 
				
			||||||
 | 
					+	u64 cpu_addr;
 | 
				
			||||||
 | 
					+	u64 size;
 | 
				
			||||||
 | 
					+	u32 flags;
 | 
				
			||||||
 | 
					+};
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+#define for_each_of_pci_range(parser, range) \
 | 
				
			||||||
 | 
					+	for (; of_pci_range_parser_one(parser, range);)
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static inline void of_pci_range_to_resource(struct of_pci_range *range,
 | 
				
			||||||
 | 
					+					    struct device_node *np,
 | 
				
			||||||
 | 
					+					    struct resource *res)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	res->flags = range->flags;
 | 
				
			||||||
 | 
					+	res->start = range->cpu_addr;
 | 
				
			||||||
 | 
					+	res->end = range->cpu_addr + range->size - 1;
 | 
				
			||||||
 | 
					+	res->parent = res->child = res->sibling = NULL;
 | 
				
			||||||
 | 
					+	res->name = np->full_name;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 #ifdef CONFIG_OF_ADDRESS
 | 
				
			||||||
 | 
					 extern u64 of_translate_address(struct device_node *np, const __be32 *addr);
 | 
				
			||||||
 | 
					 extern bool of_can_translate_address(struct device_node *dev);
 | 
				
			||||||
 | 
					@@ -27,6 +57,11 @@ static inline unsigned long pci_address_to_pio(phys_addr_t addr) { return -1; }
 | 
				
			||||||
 | 
					 #define pci_address_to_pio pci_address_to_pio
 | 
				
			||||||
 | 
					 #endif
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+extern int of_pci_range_parser_init(struct of_pci_range_parser *parser,
 | 
				
			||||||
 | 
					+			struct device_node *node);
 | 
				
			||||||
 | 
					+extern struct of_pci_range *of_pci_range_parser_one(
 | 
				
			||||||
 | 
					+					struct of_pci_range_parser *parser,
 | 
				
			||||||
 | 
					+					struct of_pci_range *range);
 | 
				
			||||||
 | 
					 #else /* CONFIG_OF_ADDRESS */
 | 
				
			||||||
 | 
					 #ifndef of_address_to_resource
 | 
				
			||||||
 | 
					 static inline int of_address_to_resource(struct device_node *dev, int index,
 | 
				
			||||||
 | 
					@@ -53,6 +88,19 @@ static inline const __be32 *of_get_address(struct device_node *dev, int index,
 | 
				
			||||||
 | 
					 {
 | 
				
			||||||
 | 
					 	return NULL;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static inline int of_pci_range_parser_init(struct of_pci_range_parser *parser,
 | 
				
			||||||
 | 
					+			struct device_node *node)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	return -1;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static inline struct of_pci_range *of_pci_range_parser_one(
 | 
				
			||||||
 | 
					+					struct of_pci_range_parser *parser,
 | 
				
			||||||
 | 
					+					struct of_pci_range *range)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	return NULL;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					 #endif /* CONFIG_OF_ADDRESS */
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					-- 
 | 
				
			||||||
 | 
					1.8.4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -0,0 +1,57 @@
 | 
				
			|||||||
 | 
					Subject: [v6,1/3] ARM: imx: Add LVDS general-purpose clocks to i.MX6Q
 | 
				
			||||||
 | 
					From: Sean Cross <xobs@kosagi.com>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The i.MX6 has two general-purpose LVDS clocks that can be driven
 | 
				
			||||||
 | 
					from a variety of sources.  This patch adds a mux and a gate for
 | 
				
			||||||
 | 
					both of these clocks.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Signed-off-by: Sean Cross <xobs@kosagi.com>
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					 arch/arm/mach-imx/clk-imx6q.c                      |   20 +++++++++++++++++++-
 | 
				
			||||||
 | 
					 2 files changed, 23 insertions(+), 1 deletion(-)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
 | 
				
			||||||
 | 
					index 9181a24..d94be84 100644
 | 
				
			||||||
 | 
					--- a/arch/arm/mach-imx/clk-imx6q.c
 | 
				
			||||||
 | 
					+++ b/arch/arm/mach-imx/clk-imx6q.c
 | 
				
			||||||
 | 
					@@ -205,6 +205,11 @@ static const char *vpu_axi_sels[]	= { "a
 | 
				
			||||||
 | 
					 static const char *cko1_sels[]	= { "pll3_usb_otg", "pll2_bus", "pll1_sys", "pll5_video_div",
 | 
				
			||||||
 | 
					 				    "dummy", "axi", "enfc", "ipu1_di0", "ipu1_di1", "ipu2_di0",
 | 
				
			||||||
 | 
					 				    "ipu2_di1", "ahb", "ipg", "ipg_per", "ckil", "pll4_post_div", };
 | 
				
			||||||
 | 
					+static const char *lvds_sels[] = {
 | 
				
			||||||
 | 
					+	"dummy", "dummy", "dummy", "dummy", "dummy", "dummy",
 | 
				
			||||||
 | 
					+	"pll4_audio", "pll5_video", "pll8_mlb", "enet_ref",
 | 
				
			||||||
 | 
					+	"pcie_ref", "sata_ref",
 | 
				
			||||||
 | 
					+};
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 enum mx6q_clks {
 | 
				
			||||||
 | 
					 	dummy, ckil, ckih, osc, pll2_pfd0_352m, pll2_pfd1_594m, pll2_pfd2_396m,
 | 
				
			||||||
 | 
					@@ -238,7 +243,8 @@ enum mx6q_clks {
 | 
				
			||||||
 | 
					 	pll4_audio, pll5_video, pll8_mlb, pll7_usb_host, pll6_enet, ssi1_ipg,
 | 
				
			||||||
 | 
					 	ssi2_ipg, ssi3_ipg, rom, usbphy1, usbphy2, ldb_di0_div_3_5, ldb_di1_div_3_5,
 | 
				
			||||||
 | 
					 	sata_ref, sata_ref_100m, pcie_ref, pcie_ref_125m, enet_ref, usbphy1_gate,
 | 
				
			||||||
 | 
					-	usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div, clk_max
 | 
				
			||||||
 | 
					+	usbphy2_gate, pll4_post_div, pll5_post_div, pll5_video_div,
 | 
				
			||||||
 | 
					+	lvds1_sel, lvds2_sel, lvds1_gate, lvds2_gate, clk_max
 | 
				
			||||||
 | 
					 };
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 static struct clk *clk[clk_max];
 | 
				
			||||||
 | 
					@@ -340,6 +347,18 @@ int __init mx6q_clocks_init(void)
 | 
				
			||||||
 | 
					 			base + 0xe0, 0, 2, 0, clk_enet_ref_table,
 | 
				
			||||||
 | 
					 			&imx_ccm_lock);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+	clk[lvds1_sel] = imx_clk_mux("lvds1_sel", base + 0x160, 0, 5, lvds_sels, ARRAY_SIZE(lvds_sels));
 | 
				
			||||||
 | 
					+	clk[lvds2_sel] = imx_clk_mux("lvds2_sel", base + 0x160, 5, 5, lvds_sels, ARRAY_SIZE(lvds_sels));
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/*
 | 
				
			||||||
 | 
					+	 * lvds1_gate and lvds2_gate are pseudo-gates.  Both can be
 | 
				
			||||||
 | 
					+	 * independently configured as clock inputs or outputs.  We treat
 | 
				
			||||||
 | 
					+	 * the "output_enable" bit as a gate, even though it's really just
 | 
				
			||||||
 | 
					+	 * enabling clock output.
 | 
				
			||||||
 | 
					+	 */
 | 
				
			||||||
 | 
					+	clk[lvds1_gate] = imx_clk_gate("lvds1_gate", "dummy", base + 0x160, 10);
 | 
				
			||||||
 | 
					+	clk[lvds2_gate] = imx_clk_gate("lvds2_gate", "dummy", base + 0x160, 11);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 	/*                                name              parent_name        reg       idx */
 | 
				
			||||||
 | 
					 	clk[pll2_pfd0_352m] = imx_clk_pfd("pll2_pfd0_352m", "pll2_bus",     base + 0x100, 0);
 | 
				
			||||||
 | 
					 	clk[pll2_pfd1_594m] = imx_clk_pfd("pll2_pfd1_594m", "pll2_bus",     base + 0x100, 1);
 | 
				
			||||||
@@ -0,0 +1,37 @@
 | 
				
			|||||||
 | 
					Subject: [v6,2/3] ARM: imx6q: Add PCIe bits to GPR syscon definition
 | 
				
			||||||
 | 
					From: Sean Cross <xobs@kosagi.com>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					PCIe requires additional bits be defined for GPR8 and GPR12.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Signed-off-by: Sean Cross <xobs@kosagi.com>
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					 include/linux/mfd/syscon/imx6q-iomuxc-gpr.h |    8 ++++++++
 | 
				
			||||||
 | 
					 1 file changed, 8 insertions(+)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					diff --git a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
 | 
				
			||||||
 | 
					index b6bdcd6..e00e9f3 100644
 | 
				
			||||||
 | 
					--- a/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
 | 
				
			||||||
 | 
					+++ b/include/linux/mfd/syscon/imx6q-iomuxc-gpr.h
 | 
				
			||||||
 | 
					@@ -241,6 +241,12 @@
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 #define IMX6Q_GPR5_L2_CLK_STOP			BIT(8)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+#define IMX6Q_GPR8_TX_SWING_LOW			(0x7f << 25)
 | 
				
			||||||
 | 
					+#define IMX6Q_GPR8_TX_SWING_FULL		(0x7f << 18)
 | 
				
			||||||
 | 
					+#define IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB		(0x3f << 12)
 | 
				
			||||||
 | 
					+#define IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB		(0x3f << 6)
 | 
				
			||||||
 | 
					+#define IMX6Q_GPR8_TX_DEEMPH_GEN1		(0x3f << 0)
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 #define IMX6Q_GPR9_TZASC2_BYP			BIT(1)
 | 
				
			||||||
 | 
					 #define IMX6Q_GPR9_TZASC1_BYP			BIT(0)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					@@ -273,7 +279,9 @@
 | 
				
			||||||
 | 
					 #define IMX6Q_GPR12_ARMP_AHB_CLK_EN		BIT(26)
 | 
				
			||||||
 | 
					 #define IMX6Q_GPR12_ARMP_ATB_CLK_EN		BIT(25)
 | 
				
			||||||
 | 
					 #define IMX6Q_GPR12_ARMP_APB_CLK_EN		BIT(24)
 | 
				
			||||||
 | 
					+#define IMX6Q_GPR12_DEVICE_TYPE			(0xf << 12)
 | 
				
			||||||
 | 
					 #define IMX6Q_GPR12_PCIE_CTL_2			BIT(10)
 | 
				
			||||||
 | 
					+#define IMX6Q_GPR12_LOS_LEVEL			(0x1f << 4)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 #define IMX6Q_GPR13_SDMA_STOP_REQ		BIT(30)
 | 
				
			||||||
 | 
					 #define IMX6Q_GPR13_CAN2_STOP_REQ		BIT(29)
 | 
				
			||||||
@@ -0,0 +1,674 @@
 | 
				
			|||||||
 | 
					Subject: [v6,3/3] PCI: imx6: Add support for i.MX6 PCIe controller
 | 
				
			||||||
 | 
					From: Sean Cross <xobs@kosagi.com>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Add support for the PCIe port present on the i.MX6 family of controllers.
 | 
				
			||||||
 | 
					These use the Synopsis Designware core tied to their own PHY.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Signed-off-by: Sean Cross <xobs@kosagi.com>
 | 
				
			||||||
 | 
					Acked-by: Bjorn Helgaas <bhelgaas@google.com>
 | 
				
			||||||
 | 
					Acked-by: Sascha Hauer <s.hauer@pengutronix.de>
 | 
				
			||||||
 | 
					---
 | 
				
			||||||
 | 
					 arch/arm/boot/dts/imx6qdl.dtsi                     |   16 +
 | 
				
			||||||
 | 
					 arch/arm/mach-imx/Kconfig                          |    2 +
 | 
				
			||||||
 | 
					 arch/arm/mach-imx/clk-imx6q.c                      |    4 +
 | 
				
			||||||
 | 
					 drivers/pci/host/Kconfig                           |    6 +
 | 
				
			||||||
 | 
					 drivers/pci/host/Makefile                          |    1 +
 | 
				
			||||||
 | 
					 drivers/pci/host/pci-imx6.c                        |  576 ++++++++++++++++++++
 | 
				
			||||||
 | 
					 7 files changed, 611 insertions(+), 1 deletion(-)
 | 
				
			||||||
 | 
					 create mode 100644 drivers/pci/host/pci-imx6.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					diff --git a/arch/arm/boot/dts/imx6qdl.dtsi b/arch/arm/boot/dts/imx6qdl.dtsi
 | 
				
			||||||
 | 
					index ccd55c2..125202e 100644
 | 
				
			||||||
 | 
					--- a/arch/arm/boot/dts/imx6qdl.dtsi
 | 
				
			||||||
 | 
					+++ b/arch/arm/boot/dts/imx6qdl.dtsi
 | 
				
			||||||
 | 
					@@ -116,6 +116,22 @@
 | 
				
			||||||
 | 
					 			arm,data-latency = <4 2 3>;
 | 
				
			||||||
 | 
					 		};
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+		pcie: pcie@0x01000000 {
 | 
				
			||||||
 | 
					+			compatible = "fsl,imx6q-pcie", "snps,dw-pcie";
 | 
				
			||||||
 | 
					+			reg = <0x01ffc000 0x4000>; /* DBI */
 | 
				
			||||||
 | 
					+			#address-cells = <3>;
 | 
				
			||||||
 | 
					+			#size-cells = <2>;
 | 
				
			||||||
 | 
					+			device_type = "pci";
 | 
				
			||||||
 | 
					+			ranges = <0x00000800 0 0x01f00000 0x01f00000 0 0x00080000 /* configuration space */
 | 
				
			||||||
 | 
					+				  0x81000000 0 0          0x01f80000 0 0x00010000 /* downstream I/O */
 | 
				
			||||||
 | 
					+				  0x82000000 0 0x01000000 0x01000000 0 0x00f00000>; /* non-prefetchable memory */
 | 
				
			||||||
 | 
					+			num-lanes = <1>;
 | 
				
			||||||
 | 
					+			interrupts = <0 123 0x04>;
 | 
				
			||||||
 | 
					+			clocks = <&clks 189>, <&clks 187>, <&clks 205>, <&clks 144>;
 | 
				
			||||||
 | 
					+			clock-names = "pcie_ref_125m", "sata_ref_100m", "lvds_gate", "pcie_axi";
 | 
				
			||||||
 | 
					+			status = "disabled";
 | 
				
			||||||
 | 
					+		};
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 		pmu {
 | 
				
			||||||
 | 
					 			compatible = "arm,cortex-a9-pmu";
 | 
				
			||||||
 | 
					 			interrupts = <0 94 0x04>;
 | 
				
			||||||
 | 
					diff --git a/arch/arm/mach-imx/Kconfig b/arch/arm/mach-imx/Kconfig
 | 
				
			||||||
 | 
					index 29a8af6..e6ac281 100644
 | 
				
			||||||
 | 
					--- a/arch/arm/mach-imx/Kconfig
 | 
				
			||||||
 | 
					+++ b/arch/arm/mach-imx/Kconfig
 | 
				
			||||||
 | 
					@@ -801,6 +801,8 @@ config SOC_IMX6Q
 | 
				
			||||||
 | 
					 	select HAVE_IMX_SRC
 | 
				
			||||||
 | 
					 	select HAVE_SMP
 | 
				
			||||||
 | 
					 	select MFD_SYSCON
 | 
				
			||||||
 | 
					+	select MIGHT_HAVE_PCI
 | 
				
			||||||
 | 
					+	select PCI_DOMAINS if PCI
 | 
				
			||||||
 | 
					 	select PINCTRL
 | 
				
			||||||
 | 
					 	select PINCTRL_IMX6Q
 | 
				
			||||||
 | 
					 	select PL310_ERRATA_588369 if CACHE_PL310
 | 
				
			||||||
 | 
					diff --git a/arch/arm/mach-imx/clk-imx6q.c b/arch/arm/mach-imx/clk-imx6q.c
 | 
				
			||||||
 | 
					index d94be84..6956995 100644
 | 
				
			||||||
 | 
					--- a/arch/arm/mach-imx/clk-imx6q.c
 | 
				
			||||||
 | 
					+++ b/arch/arm/mach-imx/clk-imx6q.c
 | 
				
			||||||
 | 
					@@ -621,6 +621,10 @@ static void __init imx6q_clocks_init(struct device_node *ccm_node)
 | 
				
			||||||
 | 
					 	if (ret)
 | 
				
			||||||
 | 
					 		pr_warn("failed to set up CLKO: %d\n", ret);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					+	/* All existing boards with PCIe use LVDS1 */
 | 
				
			||||||
 | 
					+	if (IS_ENABLED(CONFIG_PCI_IMX6))
 | 
				
			||||||
 | 
					+		clk_set_parent(clk[lvds1_sel], clk[sata_ref]);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					 	/* Set initial power mode */
 | 
				
			||||||
 | 
					 	imx6q_set_lpm(WAIT_CLOCKED);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					--- /dev/null
 | 
				
			||||||
 | 
					+++ b/drivers/pci/host/Kconfig
 | 
				
			||||||
 | 
					@@ -0,0 +1,13 @@
 | 
				
			||||||
 | 
					+menu "PCI host controller drivers"
 | 
				
			||||||
 | 
					+	depends on PCI
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+config PCIE_DW
 | 
				
			||||||
 | 
					+	bool
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+config PCI_IMX6
 | 
				
			||||||
 | 
					+	bool "Freescale i.MX6 PCIe controller"
 | 
				
			||||||
 | 
					+	depends on SOC_IMX6Q
 | 
				
			||||||
 | 
					+	select PCIEPORTBUS
 | 
				
			||||||
 | 
					+	select PCIE_DW
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+endmenu
 | 
				
			||||||
 | 
					--- /dev/null
 | 
				
			||||||
 | 
					+++ b/drivers/pci/host/Makefile
 | 
				
			||||||
 | 
					@@ -0,0 +1,2 @@
 | 
				
			||||||
 | 
					+obj-$(CONFIG_PCIE_DW) += pcie-designware.o
 | 
				
			||||||
 | 
					+obj-$(CONFIG_PCI_IMX6) += pci-imx6.o
 | 
				
			||||||
 | 
					--- /dev/null
 | 
				
			||||||
 | 
					+++ b/drivers/pci/host/pci-imx6.c
 | 
				
			||||||
 | 
					@@ -0,0 +1,576 @@
 | 
				
			||||||
 | 
					+/*
 | 
				
			||||||
 | 
					+ * PCIe host controller driver for Freescale i.MX6 SoCs
 | 
				
			||||||
 | 
					+ *
 | 
				
			||||||
 | 
					+ * Copyright (C) 2013 Kosagi
 | 
				
			||||||
 | 
					+ *		http://www.kosagi.com
 | 
				
			||||||
 | 
					+ *
 | 
				
			||||||
 | 
					+ * Author: Sean Cross <xobs@kosagi.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/kernel.h>
 | 
				
			||||||
 | 
					+#include <linux/mfd/syscon.h>
 | 
				
			||||||
 | 
					+#include <linux/mfd/syscon/imx6q-iomuxc-gpr.h>
 | 
				
			||||||
 | 
					+#include <linux/module.h>
 | 
				
			||||||
 | 
					+#include <linux/of_gpio.h>
 | 
				
			||||||
 | 
					+#include <linux/pci.h>
 | 
				
			||||||
 | 
					+#include <linux/platform_device.h>
 | 
				
			||||||
 | 
					+#include <linux/regmap.h>
 | 
				
			||||||
 | 
					+#include <linux/resource.h>
 | 
				
			||||||
 | 
					+#include <linux/signal.h>
 | 
				
			||||||
 | 
					+#include <linux/types.h>
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+#include "pcie-designware.h"
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+#define to_imx6_pcie(x)	container_of(x, struct imx6_pcie, pp)
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+struct imx6_pcie {
 | 
				
			||||||
 | 
					+	int			reset_gpio;
 | 
				
			||||||
 | 
					+	int			power_on_gpio;
 | 
				
			||||||
 | 
					+	int			wake_up_gpio;
 | 
				
			||||||
 | 
					+	int			disable_gpio;
 | 
				
			||||||
 | 
					+	struct clk		*lvds_gate;
 | 
				
			||||||
 | 
					+	struct clk		*sata_ref_100m;
 | 
				
			||||||
 | 
					+	struct clk		*pcie_ref_125m;
 | 
				
			||||||
 | 
					+	struct clk		*pcie_axi;
 | 
				
			||||||
 | 
					+	struct pcie_port	pp;
 | 
				
			||||||
 | 
					+	struct regmap		*iomuxc_gpr;
 | 
				
			||||||
 | 
					+	void __iomem		*mem_base;
 | 
				
			||||||
 | 
					+};
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+/* PCIe Port Logic registers (memory-mapped) */
 | 
				
			||||||
 | 
					+#define PL_OFFSET 0x700
 | 
				
			||||||
 | 
					+#define PCIE_PHY_DEBUG_R0 (PL_OFFSET + 0x28)
 | 
				
			||||||
 | 
					+#define PCIE_PHY_DEBUG_R1 (PL_OFFSET + 0x2c)
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+#define PCIE_PHY_CTRL (PL_OFFSET + 0x114)
 | 
				
			||||||
 | 
					+#define PCIE_PHY_CTRL_DATA_LOC 0
 | 
				
			||||||
 | 
					+#define PCIE_PHY_CTRL_CAP_ADR_LOC 16
 | 
				
			||||||
 | 
					+#define PCIE_PHY_CTRL_CAP_DAT_LOC 17
 | 
				
			||||||
 | 
					+#define PCIE_PHY_CTRL_WR_LOC 18
 | 
				
			||||||
 | 
					+#define PCIE_PHY_CTRL_RD_LOC 19
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+#define PCIE_PHY_STAT (PL_OFFSET + 0x110)
 | 
				
			||||||
 | 
					+#define PCIE_PHY_STAT_ACK_LOC 16
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+/* PHY registers (not memory-mapped) */
 | 
				
			||||||
 | 
					+#define PCIE_PHY_RX_ASIC_OUT 0x100D
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+#define PHY_RX_OVRD_IN_LO 0x1005
 | 
				
			||||||
 | 
					+#define PHY_RX_OVRD_IN_LO_RX_DATA_EN (1 << 5)
 | 
				
			||||||
 | 
					+#define PHY_RX_OVRD_IN_LO_RX_PLL_EN (1 << 3)
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int pcie_phy_poll_ack(void __iomem *dbi_base, int exp_val)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	u32 val;
 | 
				
			||||||
 | 
					+	u32 max_iterations = 10;
 | 
				
			||||||
 | 
					+	u32 wait_counter = 0;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	do {
 | 
				
			||||||
 | 
					+		val = readl(dbi_base + PCIE_PHY_STAT);
 | 
				
			||||||
 | 
					+		val = (val >> PCIE_PHY_STAT_ACK_LOC) & 0x1;
 | 
				
			||||||
 | 
					+		wait_counter++;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+		if (val == exp_val)
 | 
				
			||||||
 | 
					+			return 0;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+		udelay(1);
 | 
				
			||||||
 | 
					+	} while ((wait_counter < max_iterations) && (val != exp_val));
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return -ETIMEDOUT;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	u32 val;
 | 
				
			||||||
 | 
					+	int ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	val = addr << PCIE_PHY_CTRL_DATA_LOC;
 | 
				
			||||||
 | 
					+	writel(val, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	val |= (0x1 << PCIE_PHY_CTRL_CAP_ADR_LOC);
 | 
				
			||||||
 | 
					+	writel(val, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = pcie_phy_poll_ack(dbi_base, 1);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	val = addr << PCIE_PHY_CTRL_DATA_LOC;
 | 
				
			||||||
 | 
					+	writel(val, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = pcie_phy_poll_ack(dbi_base, 0);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
 | 
				
			||||||
 | 
					+static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	u32 val, phy_ctl;
 | 
				
			||||||
 | 
					+	int ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = pcie_phy_wait_ack(dbi_base, addr);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* assert Read signal */
 | 
				
			||||||
 | 
					+	phy_ctl = 0x1 << PCIE_PHY_CTRL_RD_LOC;
 | 
				
			||||||
 | 
					+	writel(phy_ctl, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = pcie_phy_poll_ack(dbi_base, 1);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	val = readl(dbi_base + PCIE_PHY_STAT);
 | 
				
			||||||
 | 
					+	*data = val & 0xffff;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* deassert Read signal */
 | 
				
			||||||
 | 
					+	writel(0x00, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = pcie_phy_poll_ack(dbi_base, 0);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	u32 var;
 | 
				
			||||||
 | 
					+	int ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* write addr */
 | 
				
			||||||
 | 
					+	/* cap addr */
 | 
				
			||||||
 | 
					+	ret = pcie_phy_wait_ack(dbi_base, addr);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	var = data << PCIE_PHY_CTRL_DATA_LOC;
 | 
				
			||||||
 | 
					+	writel(var, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* capture data */
 | 
				
			||||||
 | 
					+	var |= (0x1 << PCIE_PHY_CTRL_CAP_DAT_LOC);
 | 
				
			||||||
 | 
					+	writel(var, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = pcie_phy_poll_ack(dbi_base, 1);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* deassert cap data */
 | 
				
			||||||
 | 
					+	var = data << PCIE_PHY_CTRL_DATA_LOC;
 | 
				
			||||||
 | 
					+	writel(var, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* wait for ack de-assetion */
 | 
				
			||||||
 | 
					+	ret = pcie_phy_poll_ack(dbi_base, 0);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* assert wr signal */
 | 
				
			||||||
 | 
					+	var = 0x1 << PCIE_PHY_CTRL_WR_LOC;
 | 
				
			||||||
 | 
					+	writel(var, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* wait for ack */
 | 
				
			||||||
 | 
					+	ret = pcie_phy_poll_ack(dbi_base, 1);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* deassert wr signal */
 | 
				
			||||||
 | 
					+	var = data << PCIE_PHY_CTRL_DATA_LOC;
 | 
				
			||||||
 | 
					+	writel(var, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* wait for ack de-assetion */
 | 
				
			||||||
 | 
					+	ret = pcie_phy_poll_ack(dbi_base, 0);
 | 
				
			||||||
 | 
					+	if (ret)
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	writel(0x0, dbi_base + PCIE_PHY_CTRL);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+/*  Added for PCI abort handling */
 | 
				
			||||||
 | 
					+static int imx6q_pcie_abort_handler(unsigned long addr,
 | 
				
			||||||
 | 
					+		unsigned int fsr, struct pt_regs *regs)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	/*
 | 
				
			||||||
 | 
					+	 * If it was an imprecise abort, then we need to correct the
 | 
				
			||||||
 | 
					+	 * return address to be _after_ the instruction.
 | 
				
			||||||
 | 
					+	 */
 | 
				
			||||||
 | 
					+	if (fsr & (1 << 10))
 | 
				
			||||||
 | 
					+		regs->ARM_pc += 4;
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR1_PCIE_TEST_PD, 1 << 18);
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR1_PCIE_REF_CLK_EN, 0 << 16);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	gpio_set_value(imx6_pcie->reset_gpio, 0);
 | 
				
			||||||
 | 
					+	msleep(100);
 | 
				
			||||||
 | 
					+	gpio_set_value(imx6_pcie->reset_gpio, 1);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
 | 
				
			||||||
 | 
					+	int ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	if (gpio_is_valid(imx6_pcie->power_on_gpio))
 | 
				
			||||||
 | 
					+		gpio_set_value(imx6_pcie->power_on_gpio, 1);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR1_PCIE_TEST_PD, 0 << 18);
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR1,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR1_PCIE_REF_CLK_EN, 1 << 16);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = clk_prepare_enable(imx6_pcie->sata_ref_100m);
 | 
				
			||||||
 | 
					+	if (ret) {
 | 
				
			||||||
 | 
					+		dev_err(pp->dev, "unable to enable sata_ref_100m\n");
 | 
				
			||||||
 | 
					+		goto err_sata_ref;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = clk_prepare_enable(imx6_pcie->pcie_ref_125m);
 | 
				
			||||||
 | 
					+	if (ret) {
 | 
				
			||||||
 | 
					+		dev_err(pp->dev, "unable to enable pcie_ref_125m\n");
 | 
				
			||||||
 | 
					+		goto err_pcie_ref;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = clk_prepare_enable(imx6_pcie->lvds_gate);
 | 
				
			||||||
 | 
					+	if (ret) {
 | 
				
			||||||
 | 
					+		dev_err(pp->dev, "unable to enable lvds_gate\n");
 | 
				
			||||||
 | 
					+		goto err_lvds_gate;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = clk_prepare_enable(imx6_pcie->pcie_axi);
 | 
				
			||||||
 | 
					+	if (ret) {
 | 
				
			||||||
 | 
					+		dev_err(pp->dev, "unable to enable pcie_axi\n");
 | 
				
			||||||
 | 
					+		goto err_pcie_axi;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* allow the clocks to stabilize */
 | 
				
			||||||
 | 
					+	usleep_range(200, 500);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+err_pcie_axi:
 | 
				
			||||||
 | 
					+	clk_disable_unprepare(imx6_pcie->lvds_gate);
 | 
				
			||||||
 | 
					+err_lvds_gate:
 | 
				
			||||||
 | 
					+	clk_disable_unprepare(imx6_pcie->pcie_ref_125m);
 | 
				
			||||||
 | 
					+err_pcie_ref:
 | 
				
			||||||
 | 
					+	clk_disable_unprepare(imx6_pcie->sata_ref_100m);
 | 
				
			||||||
 | 
					+err_sata_ref:
 | 
				
			||||||
 | 
					+	return ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static void imx6_pcie_init_phy(struct pcie_port *pp)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR12_PCIE_CTL_2, 0 << 10);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* configure constant input signal to the pcie ctrl and phy */
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR12_DEVICE_TYPE, PCI_EXP_TYPE_ROOT_PORT << 12);
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR12_LOS_LEVEL, 9 << 4);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR8_TX_DEEMPH_GEN1, 0 << 0);
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB, 0 << 6);
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB, 20 << 12);
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR8_TX_SWING_FULL, 127 << 18);
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR8,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR8_TX_SWING_LOW, 127 << 25);
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static void imx6_pcie_host_init(struct pcie_port *pp)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	int count = 0;
 | 
				
			||||||
 | 
					+	struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie_assert_core_reset(pp);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie_init_phy(pp);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie_deassert_core_reset(pp);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	dw_pcie_setup_rc(pp);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	regmap_update_bits(imx6_pcie->iomuxc_gpr, IOMUXC_GPR12,
 | 
				
			||||||
 | 
					+			IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	while (!dw_pcie_link_up(pp)) {
 | 
				
			||||||
 | 
					+		usleep_range(100, 1000);
 | 
				
			||||||
 | 
					+		count++;
 | 
				
			||||||
 | 
					+		if (count >= 10) {
 | 
				
			||||||
 | 
					+			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));
 | 
				
			||||||
 | 
					+			break;
 | 
				
			||||||
 | 
					+		}
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int imx6_pcie_link_up(struct pcie_port *pp)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	u32 rc, ltssm, rx_valid, temp;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* link is debug bit 36, debug register 1 starts at bit 32 */
 | 
				
			||||||
 | 
					+	rc = readl(pp->dbi_base + PCIE_PHY_DEBUG_R1) & (0x1 << (36 - 32));
 | 
				
			||||||
 | 
					+	if (rc)
 | 
				
			||||||
 | 
					+		return -EAGAIN;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/*
 | 
				
			||||||
 | 
					+	 * From L0, initiate MAC entry to gen2 if EP/RC supports gen2.
 | 
				
			||||||
 | 
					+	 * Wait 2ms (LTSSM timeout is 24ms, PHY lock is ~5us in gen2).
 | 
				
			||||||
 | 
					+	 * If (MAC/LTSSM.state == Recovery.RcvrLock)
 | 
				
			||||||
 | 
					+	 * && (PHY/rx_valid==0) then pulse PHY/rx_reset. Transition
 | 
				
			||||||
 | 
					+	 * to gen2 is stuck
 | 
				
			||||||
 | 
					+	 */
 | 
				
			||||||
 | 
					+	pcie_phy_read(pp->dbi_base, PCIE_PHY_RX_ASIC_OUT, &rx_valid);
 | 
				
			||||||
 | 
					+	ltssm = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0) & 0x3F;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	if (rx_valid & 0x01)
 | 
				
			||||||
 | 
					+		return 0;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	if (ltssm != 0x0d)
 | 
				
			||||||
 | 
					+		return 0;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	dev_err(pp->dev,
 | 
				
			||||||
 | 
					+		"transition to gen2 is stuck, reset PHY!\n");
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	pcie_phy_read(pp->dbi_base,
 | 
				
			||||||
 | 
					+		PHY_RX_OVRD_IN_LO, &temp);
 | 
				
			||||||
 | 
					+	temp |= (PHY_RX_OVRD_IN_LO_RX_DATA_EN
 | 
				
			||||||
 | 
					+		| PHY_RX_OVRD_IN_LO_RX_PLL_EN);
 | 
				
			||||||
 | 
					+	pcie_phy_write(pp->dbi_base,
 | 
				
			||||||
 | 
					+		PHY_RX_OVRD_IN_LO, temp);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	usleep_range(2000, 3000);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	pcie_phy_read(pp->dbi_base,
 | 
				
			||||||
 | 
					+		PHY_RX_OVRD_IN_LO, &temp);
 | 
				
			||||||
 | 
					+	temp &= ~(PHY_RX_OVRD_IN_LO_RX_DATA_EN
 | 
				
			||||||
 | 
					+		| PHY_RX_OVRD_IN_LO_RX_PLL_EN);
 | 
				
			||||||
 | 
					+	pcie_phy_write(pp->dbi_base,
 | 
				
			||||||
 | 
					+		PHY_RX_OVRD_IN_LO, temp);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static struct pcie_host_ops imx6_pcie_host_ops = {
 | 
				
			||||||
 | 
					+	.link_up = imx6_pcie_link_up,
 | 
				
			||||||
 | 
					+	.host_init = imx6_pcie_host_init,
 | 
				
			||||||
 | 
					+};
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int imx6_add_pcie_port(struct pcie_port *pp,
 | 
				
			||||||
 | 
					+			struct platform_device *pdev)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	int ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	pp->irq = platform_get_irq(pdev, 0);
 | 
				
			||||||
 | 
					+	if (!pp->irq) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev, "failed to get irq\n");
 | 
				
			||||||
 | 
					+		return -ENODEV;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	pp->root_bus_nr = -1;
 | 
				
			||||||
 | 
					+	pp->ops = &imx6_pcie_host_ops;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	spin_lock_init(&pp->conf_lock);
 | 
				
			||||||
 | 
					+	ret = dw_pcie_host_init(pp);
 | 
				
			||||||
 | 
					+	if (ret) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev, "failed to initialize host\n");
 | 
				
			||||||
 | 
					+		return ret;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int __init imx6_pcie_probe(struct platform_device *pdev)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	struct imx6_pcie *imx6_pcie;
 | 
				
			||||||
 | 
					+	struct pcie_port *pp;
 | 
				
			||||||
 | 
					+	struct device_node *np = pdev->dev.of_node;
 | 
				
			||||||
 | 
					+	struct resource *dbi_base;
 | 
				
			||||||
 | 
					+	int ret;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie = devm_kzalloc(&pdev->dev, sizeof(*imx6_pcie), GFP_KERNEL);
 | 
				
			||||||
 | 
					+	if (!imx6_pcie)
 | 
				
			||||||
 | 
					+		return -ENOMEM;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	pp = &imx6_pcie->pp;
 | 
				
			||||||
 | 
					+	pp->dev = &pdev->dev;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* Added for PCI abort handling */
 | 
				
			||||||
 | 
					+	hook_fault_code(16 + 6, imx6q_pcie_abort_handler, SIGBUS, 0,
 | 
				
			||||||
 | 
					+		"imprecise external abort");
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	dbi_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 | 
				
			||||||
 | 
					+	if (!dbi_base) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev, "dbi_base memory resource not found\n");
 | 
				
			||||||
 | 
					+		return -ENODEV;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	pp->dbi_base = devm_ioremap_resource(&pdev->dev, dbi_base);
 | 
				
			||||||
 | 
					+	if (IS_ERR(pp->dbi_base)) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev, "unable to remap dbi_base\n");
 | 
				
			||||||
 | 
					+		ret = PTR_ERR(pp->dbi_base);
 | 
				
			||||||
 | 
					+		goto err;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* Fetch GPIOs */
 | 
				
			||||||
 | 
					+	imx6_pcie->reset_gpio = of_get_named_gpio(np, "reset-gpio", 0);
 | 
				
			||||||
 | 
					+	if (!gpio_is_valid(imx6_pcie->reset_gpio)) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev, "no reset-gpio defined\n");
 | 
				
			||||||
 | 
					+		ret = -ENODEV;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+	ret = devm_gpio_request_one(&pdev->dev,
 | 
				
			||||||
 | 
					+				imx6_pcie->reset_gpio,
 | 
				
			||||||
 | 
					+				GPIOF_OUT_INIT_LOW,
 | 
				
			||||||
 | 
					+				"PCIe reset");
 | 
				
			||||||
 | 
					+	if (ret) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev, "unable to get reset gpio\n");
 | 
				
			||||||
 | 
					+		goto err;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie->power_on_gpio = of_get_named_gpio(np, "power-on-gpio", 0);
 | 
				
			||||||
 | 
					+	if (gpio_is_valid(imx6_pcie->power_on_gpio)) {
 | 
				
			||||||
 | 
					+		ret = devm_gpio_request_one(&pdev->dev,
 | 
				
			||||||
 | 
					+					imx6_pcie->power_on_gpio,
 | 
				
			||||||
 | 
					+					GPIOF_OUT_INIT_LOW,
 | 
				
			||||||
 | 
					+					"PCIe power enable");
 | 
				
			||||||
 | 
					+		if (ret) {
 | 
				
			||||||
 | 
					+			dev_err(&pdev->dev, "unable to get power-on gpio\n");
 | 
				
			||||||
 | 
					+			goto err;
 | 
				
			||||||
 | 
					+		}
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie->wake_up_gpio = of_get_named_gpio(np, "wake-up-gpio", 0);
 | 
				
			||||||
 | 
					+	if (gpio_is_valid(imx6_pcie->wake_up_gpio)) {
 | 
				
			||||||
 | 
					+		ret = devm_gpio_request_one(&pdev->dev,
 | 
				
			||||||
 | 
					+					imx6_pcie->wake_up_gpio,
 | 
				
			||||||
 | 
					+					GPIOF_IN,
 | 
				
			||||||
 | 
					+					"PCIe wake up");
 | 
				
			||||||
 | 
					+		if (ret) {
 | 
				
			||||||
 | 
					+			dev_err(&pdev->dev, "unable to get wake-up gpio\n");
 | 
				
			||||||
 | 
					+			goto err;
 | 
				
			||||||
 | 
					+		}
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie->disable_gpio = of_get_named_gpio(np, "disable-gpio", 0);
 | 
				
			||||||
 | 
					+	if (gpio_is_valid(imx6_pcie->disable_gpio)) {
 | 
				
			||||||
 | 
					+		ret = devm_gpio_request_one(&pdev->dev,
 | 
				
			||||||
 | 
					+					imx6_pcie->disable_gpio,
 | 
				
			||||||
 | 
					+					GPIOF_OUT_INIT_HIGH,
 | 
				
			||||||
 | 
					+					"PCIe disable endpoint");
 | 
				
			||||||
 | 
					+		if (ret) {
 | 
				
			||||||
 | 
					+			dev_err(&pdev->dev, "unable to get disable-ep gpio\n");
 | 
				
			||||||
 | 
					+			goto err;
 | 
				
			||||||
 | 
					+		}
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* Fetch clocks */
 | 
				
			||||||
 | 
					+	imx6_pcie->lvds_gate = clk_get(&pdev->dev, "lvds_gate");
 | 
				
			||||||
 | 
					+	if (IS_ERR(imx6_pcie->lvds_gate)) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev,
 | 
				
			||||||
 | 
					+			"lvds_gate clock select missing or invalid\n");
 | 
				
			||||||
 | 
					+		ret = PTR_ERR(imx6_pcie->lvds_gate);
 | 
				
			||||||
 | 
					+		goto err;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie->sata_ref_100m = clk_get(&pdev->dev, "sata_ref_100m");
 | 
				
			||||||
 | 
					+	if (IS_ERR(imx6_pcie->sata_ref_100m)) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev,
 | 
				
			||||||
 | 
					+			"sata_ref_100m clock source missing or invalid\n");
 | 
				
			||||||
 | 
					+		ret = PTR_ERR(imx6_pcie->sata_ref_100m);
 | 
				
			||||||
 | 
					+		goto err;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie->pcie_ref_125m = clk_get(&pdev->dev, "pcie_ref_125m");
 | 
				
			||||||
 | 
					+	if (IS_ERR(imx6_pcie->pcie_ref_125m)) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev,
 | 
				
			||||||
 | 
					+			"pcie_ref_125m clock source missing or invalid\n");
 | 
				
			||||||
 | 
					+		ret = PTR_ERR(imx6_pcie->pcie_ref_125m);
 | 
				
			||||||
 | 
					+		goto err;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	imx6_pcie->pcie_axi = clk_get(&pdev->dev, "pcie_axi");
 | 
				
			||||||
 | 
					+	if (IS_ERR(imx6_pcie->pcie_axi)) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev,
 | 
				
			||||||
 | 
					+			"pcie_axi clock source missing or invalid\n");
 | 
				
			||||||
 | 
					+		ret = PTR_ERR(imx6_pcie->pcie_axi);
 | 
				
			||||||
 | 
					+		goto err;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	/* Grab GPR config register range */
 | 
				
			||||||
 | 
					+	imx6_pcie->iomuxc_gpr =
 | 
				
			||||||
 | 
					+		 syscon_regmap_lookup_by_compatible("fsl,imx6q-iomuxc-gpr");
 | 
				
			||||||
 | 
					+	if (IS_ERR(imx6_pcie->iomuxc_gpr)) {
 | 
				
			||||||
 | 
					+		dev_err(&pdev->dev, "unable to find iomuxc registers\n");
 | 
				
			||||||
 | 
					+		ret = PTR_ERR(imx6_pcie->iomuxc_gpr);
 | 
				
			||||||
 | 
					+		goto err;
 | 
				
			||||||
 | 
					+	}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	ret = imx6_add_pcie_port(pp, pdev);
 | 
				
			||||||
 | 
					+	if (ret < 0)
 | 
				
			||||||
 | 
					+		goto err;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+	platform_set_drvdata(pdev, imx6_pcie);
 | 
				
			||||||
 | 
					+	return 0;
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+err:
 | 
				
			||||||
 | 
					+	return ret;
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static const struct of_device_id imx6_pcie_of_match[] = {
 | 
				
			||||||
 | 
					+	{ .compatible = "fsl,imx6q-pcie", },
 | 
				
			||||||
 | 
					+	{},
 | 
				
			||||||
 | 
					+};
 | 
				
			||||||
 | 
					+MODULE_DEVICE_TABLE(of, imx6_pcie_of_match);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static struct platform_driver imx6_pcie_driver = {
 | 
				
			||||||
 | 
					+	.driver = {
 | 
				
			||||||
 | 
					+		.name	= "imx6q-pcie",
 | 
				
			||||||
 | 
					+		.owner	= THIS_MODULE,
 | 
				
			||||||
 | 
					+		.of_match_table = of_match_ptr(imx6_pcie_of_match),
 | 
				
			||||||
 | 
					+	},
 | 
				
			||||||
 | 
					+};
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+/* Freescale PCIe driver does not allow module unload */
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+static int __init imx6_init(void)
 | 
				
			||||||
 | 
					+{
 | 
				
			||||||
 | 
					+	return platform_driver_probe(&imx6_pcie_driver, imx6_pcie_probe);
 | 
				
			||||||
 | 
					+}
 | 
				
			||||||
 | 
					+module_init(imx6_init);
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+MODULE_AUTHOR("Sean Cross <xobs@kosagi.com>");
 | 
				
			||||||
 | 
					+MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver");
 | 
				
			||||||
 | 
					+MODULE_LICENSE("GPL v2");
 | 
				
			||||||
							
								
								
									
										17
									
								
								target/linux/imx6/patches-3.10/010-pcie-backport-fixes.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								target/linux/imx6/patches-3.10/010-pcie-backport-fixes.patch
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
				
			|||||||
 | 
					--- a/drivers/pci/Kconfig
 | 
				
			||||||
 | 
					+++ b/drivers/pci/Kconfig
 | 
				
			||||||
 | 
					@@ -125,3 +125,5 @@ config PCI_IOAPIC
 | 
				
			||||||
 | 
					 config PCI_LABEL
 | 
				
			||||||
 | 
					 	def_bool y if (DMI || ACPI)
 | 
				
			||||||
 | 
					 	select NLS
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+source "drivers/pci/host/Kconfig"
 | 
				
			||||||
 | 
					--- a/drivers/pci/Makefile
 | 
				
			||||||
 | 
					+++ b/drivers/pci/Makefile
 | 
				
			||||||
 | 
					@@ -67,3 +67,6 @@ obj-$(CONFIG_XEN_PCIDEV_FRONTEND) += xen
 | 
				
			||||||
 | 
					 obj-$(CONFIG_OF) += of.o
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG
 | 
				
			||||||
 | 
					+
 | 
				
			||||||
 | 
					+# PCI host controller drivers
 | 
				
			||||||
 | 
					+obj-y += host/
 | 
				
			||||||
							
								
								
									
										35
									
								
								target/linux/imx6/patches-3.10/011-pci-tweaks.patch
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								target/linux/imx6/patches-3.10/011-pci-tweaks.patch
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					--- a/arch/arm/boot/dts/imx6qdl.dtsi
 | 
				
			||||||
 | 
					+++ b/arch/arm/boot/dts/imx6qdl.dtsi
 | 
				
			||||||
 | 
					@@ -119,7 +119,7 @@
 | 
				
			||||||
 | 
					 				  0x82000000 0 0x01000000 0x01000000 0 0x00f00000>; /* non-prefetchable memory */
 | 
				
			||||||
 | 
					 			num-lanes = <1>;
 | 
				
			||||||
 | 
					 			interrupts = <0 123 0x04>;
 | 
				
			||||||
 | 
					-			clocks = <&clks 189>, <&clks 187>, <&clks 205>, <&clks 144>;
 | 
				
			||||||
 | 
					+			clocks = <&clks 189>, <&clks 187>, <&clks 198>, <&clks 144>;
 | 
				
			||||||
 | 
					 			clock-names = "pcie_ref_125m", "sata_ref_100m", "lvds_gate", "pcie_axi";
 | 
				
			||||||
 | 
					 			status = "disabled";
 | 
				
			||||||
 | 
					 		};
 | 
				
			||||||
 | 
					--- a/drivers/pci/host/pci-imx6.c
 | 
				
			||||||
 | 
					+++ b/drivers/pci/host/pci-imx6.c
 | 
				
			||||||
 | 
					@@ -200,12 +200,6 @@
 | 
				
			||||||
 | 
					 static int imx6q_pcie_abort_handler(unsigned long addr,
 | 
				
			||||||
 | 
					 		unsigned int fsr, struct pt_regs *regs)
 | 
				
			||||||
 | 
					 {
 | 
				
			||||||
 | 
					-	/*
 | 
				
			||||||
 | 
					-	 * If it was an imprecise abort, then we need to correct the
 | 
				
			||||||
 | 
					-	 * return address to be _after_ the instruction.
 | 
				
			||||||
 | 
					-	 */
 | 
				
			||||||
 | 
					-	if (fsr & (1 << 10))
 | 
				
			||||||
 | 
					-		regs->ARM_pc += 4;
 | 
				
			||||||
 | 
					 	return 0;
 | 
				
			||||||
 | 
					 }
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					@@ -322,7 +316,7 @@
 | 
				
			||||||
 | 
					 			IMX6Q_GPR12_PCIE_CTL_2, 1 << 10);
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					 	while (!dw_pcie_link_up(pp)) {
 | 
				
			||||||
 | 
					-		usleep_range(100, 1000);
 | 
				
			||||||
 | 
					+		usleep_range(2000, 3000);
 | 
				
			||||||
 | 
					 		count++;
 | 
				
			||||||
 | 
					 		if (count >= 10) {
 | 
				
			||||||
 | 
					 			dev_err(pp->dev, "phy link never came up\n");
 | 
				
			||||||
@@ -1,91 +0,0 @@
 | 
				
			|||||||
--- a/arch/arm/boot/dts/imx6q.dtsi
 | 
					 | 
				
			||||||
+++ b/arch/arm/boot/dts/imx6q.dtsi
 | 
					 | 
				
			||||||
@@ -391,6 +391,15 @@
 | 
					 | 
				
			||||||
 			status = "disabled";
 | 
					 | 
				
			||||||
 		};
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+		pcie: pcie@01ffc000 {
 | 
					 | 
				
			||||||
+			#crtc-cells = <1>;
 | 
					 | 
				
			||||||
+			compatible = "fsl,imx6q-pcie", "fsl,pcie";
 | 
					 | 
				
			||||||
+			reg = <0x01ffc000 0x4000>;
 | 
					 | 
				
			||||||
+			clocks = <&clks 144>, <&clks 189>;
 | 
					 | 
				
			||||||
+			clock-names = "pcie_axi", "pcie_ref_125m";
 | 
					 | 
				
			||||||
+			status = "disabled";
 | 
					 | 
				
			||||||
+		};
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 		ipu2: ipu@02800000 {
 | 
					 | 
				
			||||||
 			#crtc-cells = <1>;
 | 
					 | 
				
			||||||
 			compatible = "fsl,imx6q-ipu";
 | 
					 | 
				
			||||||
--- a/arch/arm/mach-imx/Kconfig
 | 
					 | 
				
			||||||
+++ b/arch/arm/mach-imx/Kconfig
 | 
					 | 
				
			||||||
@@ -790,6 +790,8 @@ config SOC_IMX6Q
 | 
					 | 
				
			||||||
 	bool "i.MX6 Quad/DualLite support"
 | 
					 | 
				
			||||||
 	select ARCH_HAS_CPUFREQ
 | 
					 | 
				
			||||||
 	select ARCH_HAS_OPP
 | 
					 | 
				
			||||||
+	select ARCH_HAS_IMX_PCIE
 | 
					 | 
				
			||||||
+	select ARCH_SUPPORTS_MSI
 | 
					 | 
				
			||||||
 	select ARM_CPU_SUSPEND if PM
 | 
					 | 
				
			||||||
 	select ARM_ERRATA_754322
 | 
					 | 
				
			||||||
 	select ARM_ERRATA_764369 if SMP
 | 
					 | 
				
			||||||
@@ -816,6 +818,10 @@ config SOC_IMX6Q
 | 
					 | 
				
			||||||
 	help
 | 
					 | 
				
			||||||
 	  This enables support for Freescale i.MX6 Quad processor.
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+config IMX_PCIE
 | 
					 | 
				
			||||||
+	bool "PCI Express support"
 | 
					 | 
				
			||||||
+	select PCI
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 source "arch/arm/mach-imx/devices/Kconfig"
 | 
					 | 
				
			||||||
--- a/arch/arm/mach-imx/Makefile
 | 
					 | 
				
			||||||
+++ b/arch/arm/mach-imx/Makefile
 | 
					 | 
				
			||||||
@@ -98,6 +98,8 @@ AFLAGS_headsmp.o :=-Wa,-march=armv7-a
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SMP) += headsmp.o platsmp.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SOC_IMX6Q) += clk-imx6q.o mach-imx6q.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_IMX_PCIE) += pcie.o
 | 
					 | 
				
			||||||
+obj-$(CONFIG_PCI_MSI) += msi.o
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 ifeq ($(CONFIG_PM),y)
 | 
					 | 
				
			||||||
 obj-$(CONFIG_SOC_IMX6Q) += pm-imx6q.o headsmp.o
 | 
					 | 
				
			||||||
--- a/arch/arm/mach-imx/clk-imx6q.c
 | 
					 | 
				
			||||||
+++ b/arch/arm/mach-imx/clk-imx6q.c
 | 
					 | 
				
			||||||
@@ -547,6 +547,12 @@ int __init mx6q_clocks_init(void)
 | 
					 | 
				
			||||||
 	clk_register_clkdev(clk[ahb], "ahb", NULL);
 | 
					 | 
				
			||||||
 	clk_register_clkdev(clk[cko1], "cko1", NULL);
 | 
					 | 
				
			||||||
 	clk_register_clkdev(clk[arm], NULL, "cpu0");
 | 
					 | 
				
			||||||
+	clk_register_clkdev(clk[pcie_axi_sel], "pcie_axi_sel", NULL);
 | 
					 | 
				
			||||||
+	clk_register_clkdev(clk[axi], "axi", NULL);
 | 
					 | 
				
			||||||
+	clk_register_clkdev(clk[pll6_enet], "pll6_enet", NULL);
 | 
					 | 
				
			||||||
+	clk_register_clkdev(clk[pcie_ref], "pcie_ref", NULL);
 | 
					 | 
				
			||||||
+	clk_register_clkdev(clk[pcie_ref_125m], "pcie_ref_125m", NULL);
 | 
					 | 
				
			||||||
+	clk_register_clkdev(clk[pcie_axi], "pcie_axi", NULL);
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
 	if (imx6q_revision() != IMX_CHIP_REVISION_1_0) {
 | 
					 | 
				
			||||||
 		clk_set_parent(clk[ldb_di0_sel], clk[pll5_video_div]);
 | 
					 | 
				
			||||||
--- a/arch/arm/mach-imx/mxc.h
 | 
					 | 
				
			||||||
+++ b/arch/arm/mach-imx/mxc.h
 | 
					 | 
				
			||||||
@@ -151,6 +151,10 @@ extern unsigned int __mxc_cpu_type;
 | 
					 | 
				
			||||||
 # define cpu_is_mx53()		(0)
 | 
					 | 
				
			||||||
 #endif
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
+#ifdef CONFIG_SOC_IMX6Q
 | 
					 | 
				
			||||||
+#  define mxc_cpu_type __mxc_cpu_type
 | 
					 | 
				
			||||||
+#endif
 | 
					 | 
				
			||||||
+
 | 
					 | 
				
			||||||
 #ifndef __ASSEMBLY__
 | 
					 | 
				
			||||||
 static inline bool cpu_is_imx6dl(void)
 | 
					 | 
				
			||||||
 {
 | 
					 | 
				
			||||||
--- a/arch/arm/include/asm/io.h
 | 
					 | 
				
			||||||
+++ b/arch/arm/include/asm/io.h
 | 
					 | 
				
			||||||
@@ -178,6 +178,9 @@ extern int pci_ioremap_io(unsigned int o
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 #ifdef CONFIG_NEED_MACH_IO_H
 | 
					 | 
				
			||||||
 #include <mach/io.h>
 | 
					 | 
				
			||||||
+#elif defined(CONFIG_SOC_IMX6Q) && defined(CONFIG_IMX_PCIE)
 | 
					 | 
				
			||||||
+#define IO_SPACE_LIMIT    ((resource_size_t)0xffffffff)
 | 
					 | 
				
			||||||
+#define __io(a)     __typesafe_io((a) & IO_SPACE_LIMIT)
 | 
					 | 
				
			||||||
 #elif defined(CONFIG_PCI)
 | 
					 | 
				
			||||||
 #define IO_SPACE_LIMIT	((resource_size_t)0xfffff)
 | 
					 | 
				
			||||||
 #define __io(a)		__typesafe_io(PCI_IO_VIRT_BASE + ((a) & IO_SPACE_LIMIT))
 | 
					 | 
				
			||||||
		Reference in New Issue
	
	Block a user