Просмотр исходного кода

imx6: update PCIe driver

Signed-off-by: Tim Harvey <[email protected]>
Signed-off-by: Luka Perkov <[email protected]>

SVN-Revision: 38080
Luka Perkov 12 лет назад
Родитель
Сommit
3f059f58db

+ 4 - 3
target/linux/imx6/config-3.10

@@ -20,7 +20,6 @@ CONFIG_ARCH_REQUIRE_GPIOLIB=y
 # CONFIG_ARCH_SELECT_MEMORY_MODEL is not set
 # CONFIG_ARCH_SPARSEMEM_DEFAULT is not set
 # CONFIG_ARCH_SUNXI is not set
-CONFIG_ARCH_SUPPORTS_MSI=y
 CONFIG_ARCH_SUSPEND_POSSIBLE=y
 # CONFIG_ARCH_VIRT is not set
 CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
@@ -171,7 +170,6 @@ CONFIG_I2C_BOARDINFO=y
 CONFIG_I2C_IMX=y
 # CONFIG_IMX2_WDT is not set
 # CONFIG_IMX_DMA is not set
-CONFIG_IMX_PCIE=y
 # CONFIG_IMX_SDMA is not set
 CONFIG_INITRAMFS_SOURCE=""
 CONFIG_IRQCHIP=y
@@ -190,6 +188,7 @@ CONFIG_M25PXX_USE_FAST_READ=y
 # CONFIG_MACH_MX51_BABBAGE is not set
 CONFIG_MDIO_BOARDINFO=y
 CONFIG_MFD_SYSCON=y
+CONFIG_MIGHT_HAVE_PCI=y
 CONFIG_MMC=y
 CONFIG_MMC_BLOCK=y
 # CONFIG_MMC_MXC is not set
@@ -244,7 +243,9 @@ CONFIG_OLD_SIGSUSPEND3=y
 CONFIG_PAGEFLAGS_EXTENDED=y
 CONFIG_PAGE_OFFSET=0x80000000
 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_USE_VMALLOC=y
 CONFIG_PHYLIB=y

+ 1 - 2
target/linux/imx6/files-3.10/arch/arm/boot/dts/imx6q-gw5400-a.dts

@@ -271,8 +271,7 @@
 };
 
 &pcie {
-	rst-gpios = <&gpio1 29 0>; /* PCIESWT_RST# */
-	clken-gpios = <&gpio1 20 0>; /* not used */
+	reset-gpios = <&gpio1 29 0>;
 	status = "okay";
 
 	eth1: sky2@8 { /* MAC/PHY on bus 8 */

+ 565 - 0
target/linux/imx6/files-3.10/drivers/pci/host/pcie-designware.c

@@ -0,0 +1,565 @@
+/*
+ * Synopsys Designware PCIe host controller driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Jingoo Han <[email protected]>
+ *
+ * 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 <[email protected]>");
+MODULE_DESCRIPTION("Designware PCIe host controller driver");
+MODULE_LICENSE("GPL v2");

+ 65 - 0
target/linux/imx6/files-3.10/drivers/pci/host/pcie-designware.h

@@ -0,0 +1,65 @@
+/*
+ * Synopsys Designware PCIe host controller driver
+ *
+ * Copyright (C) 2013 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Jingoo Han <[email protected]>
+ *
+ * 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);

+ 201 - 0
target/linux/imx6/patches-3.10/0009-of-pci-Provide-support-for-parsing-PCI-DT-ranges-pro.patch

@@ -0,0 +1,201 @@
+From: Andrew Murray <[email protected]>
+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 <[email protected]>
+Signed-off-by: Liviu Dudau <[email protected]>
+Signed-off-by: Thomas Petazzoni <[email protected]>
+Reviewed-by: Rob Herring <[email protected]>
+Tested-by: Thomas Petazzoni <[email protected]>
+Tested-by: Linus Walleij <[email protected]>
+Tested-by: Jingoo Han <[email protected]>
+Acked-by: Grant Likely <[email protected]>
+Signed-off-by: Jason Cooper <[email protected]>
+---
+ 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
+

+ 57 - 0
target/linux/imx6/patches-3.10/0010-ARM-imx-Add-LVDS-general-purpose-clocks-to-i.MX6Q.patch

@@ -0,0 +1,57 @@
+Subject: [v6,1/3] ARM: imx: Add LVDS general-purpose clocks to i.MX6Q
+From: Sean Cross <[email protected]>
+
+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 <[email protected]>
+---
+ 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);

+ 37 - 0
target/linux/imx6/patches-3.10/0011-ARM-imx6q-Add-PCIe-bits-to-GPR-syscon-definition.patch

@@ -0,0 +1,37 @@
+Subject: [v6,2/3] ARM: imx6q: Add PCIe bits to GPR syscon definition
+From: Sean Cross <[email protected]>
+
+PCIe requires additional bits be defined for GPR8 and GPR12.
+
+Signed-off-by: Sean Cross <[email protected]>
+---
+ 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)

+ 674 - 0
target/linux/imx6/patches-3.10/0012-PCI-imx6-Add-support-for-i.MX6-PCIe-controller.patch

@@ -0,0 +1,674 @@
+Subject: [v6,3/3] PCI: imx6: Add support for i.MX6 PCIe controller
+From: Sean Cross <[email protected]>
+
+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 <[email protected]>
+Acked-by: Bjorn Helgaas <[email protected]>
+Acked-by: Sascha Hauer <[email protected]>
+---
+ 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 <[email protected]>
++ *
++ * 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 <[email protected]>");
++MODULE_DESCRIPTION("Freescale i.MX6 PCIe host controller driver");
++MODULE_LICENSE("GPL v2");

+ 17 - 0
target/linux/imx6/patches-3.10/010-pcie-backport-fixes.patch

@@ -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 - 0
target/linux/imx6/patches-3.10/011-pci-tweaks.patch

@@ -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");

+ 0 - 91
target/linux/imx6/patches-3.10/200-imx6_pcie.patch

@@ -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))