summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/of/Makefile2
-rw-r--r--drivers/of/device.c33
-rw-r--r--drivers/pci/pci-imx6.c242
-rw-r--r--include/mfd/imx6q-iomuxc-gpr.h1
-rw-r--r--include/of_device.h48
5 files changed, 217 insertions, 109 deletions
diff --git a/drivers/of/Makefile b/drivers/of/Makefile
index 773548e133..ec43870061 100644
--- a/drivers/of/Makefile
+++ b/drivers/of/Makefile
@@ -1,4 +1,4 @@
-obj-y += address.o base.o fdt.o platform.o of_path.o
+obj-y += address.o base.o fdt.o platform.o of_path.o device.o
obj-$(CONFIG_OFTREE_MEM_GENERIC) += mem_generic.o
obj-$(CONFIG_OF_GPIO) += of_gpio.o
obj-$(CONFIG_OF_PCI) += of_pci.o
diff --git a/drivers/of/device.c b/drivers/of/device.c
new file mode 100644
index 0000000000..e2c3c02d27
--- /dev/null
+++ b/drivers/of/device.c
@@ -0,0 +1,33 @@
+#include <common.h>
+#include <of.h>
+
+
+/**
+ * of_match_device - Tell if a struct device matches an of_device_id list
+ * @ids: array of of device match structures to search in
+ * @dev: the of device structure to match against
+ *
+ * Used by a driver to check whether an platform_device present in the
+ * system is in its list of supported devices.
+ */
+const struct of_device_id *of_match_device(const struct of_device_id *matches,
+ const struct device_d *dev)
+{
+ if ((!matches) || (!dev->device_node))
+ return NULL;
+
+ return of_match_node(matches, dev->device_node);
+}
+EXPORT_SYMBOL(of_match_device);
+
+const void *of_device_get_match_data(const struct device_d *dev)
+{
+ const struct of_device_id *match;
+
+ match = of_match_device(dev->driver->of_compatible, dev);
+ if (!match)
+ return NULL;
+
+ return match->data;
+}
+EXPORT_SYMBOL(of_device_get_match_data);
diff --git a/drivers/pci/pci-imx6.c b/drivers/pci/pci-imx6.c
index 46483b4cc8..affc705d0a 100644
--- a/drivers/pci/pci-imx6.c
+++ b/drivers/pci/pci-imx6.c
@@ -20,6 +20,7 @@
#include <gpio.h>
#include <asm/mmu.h>
#include <of_gpio.h>
+#include <of_device.h>
#include <linux/clk.h>
#include <linux/kernel.h>
#include <of_address.h>
@@ -36,6 +37,11 @@
#define to_imx6_pcie(x) container_of(x, struct imx6_pcie, pp)
+enum imx6_pcie_variants {
+ IMX6Q,
+ IMX6QP,
+};
+
struct imx6_pcie {
int reset_gpio;
struct clk *pcie_bus;
@@ -43,7 +49,13 @@ struct imx6_pcie {
struct clk *pcie;
struct pcie_port pp;
void __iomem *iomuxc_gpr;
+ enum imx6_pcie_variants variant;
void __iomem *mem_base;
+ u32 tx_deemph_gen1;
+ u32 tx_deemph_gen2_3p5db;
+ u32 tx_deemph_gen2_6db;
+ u32 tx_swing_full;
+ u32 tx_swing_low;
};
/* PCIe Root Complex registers (memory-mapped) */
@@ -122,11 +134,7 @@ static int pcie_phy_wait_ack(void __iomem *dbi_base, int addr)
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;
+ return pcie_phy_poll_ack(dbi_base, 0);
}
/* Read from the 16-bit PCIe PHY control registers (not memory-mapped) */
@@ -153,11 +161,7 @@ static int pcie_phy_read(void __iomem *dbi_base, int addr , int *data)
/* deassert Read signal */
writel(0x00, dbi_base + PCIE_PHY_CTRL);
- ret = pcie_phy_poll_ack(dbi_base, 0);
- if (ret)
- return ret;
-
- return 0;
+ return pcie_phy_poll_ack(dbi_base, 0);
}
static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
@@ -214,44 +218,70 @@ static int pcie_phy_write(void __iomem *dbi_base, int addr, int data)
return 0;
}
+static void imx6_pcie_reset_phy(struct pcie_port *pp)
+{
+ uint32_t temp;
+
+ 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);
+
+ udelay(2000);
+
+ 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);
+}
+
static int imx6_pcie_assert_core_reset(struct pcie_port *pp)
{
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
u32 val, gpr1, gpr12;
- /*
- * If the bootloader already enabled the link we need some special
- * handling to get the core back into a state where it is safe to
- * touch it for configuration. As there is no dedicated reset signal
- * wired up for MX6QDL, we need to manually force LTSSM into "detect"
- * state before completely disabling LTSSM, which is a prerequisite
- * for core configuration.
- *
- * If both LTSSM_ENABLE and REF_SSP_ENABLE are active we have a strong
- * indication that the bootloader activated the link.
- */
- gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
- gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12);
+ switch (imx6_pcie->variant) {
+ case IMX6QP:
+ gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
+ gpr1 |= IMX6Q_GPR1_PCIE_SW_RST;
+ writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
+ break;
+ case IMX6Q:
+ /*
+ * If the bootloader already enabled the link we need some special
+ * handling to get the core back into a state where it is safe to
+ * touch it for configuration. As there is no dedicated reset signal
+ * wired up for MX6QDL, we need to manually force LTSSM into "detect"
+ * state before completely disabling LTSSM, which is a prerequisite
+ * for core configuration.
+ *
+ * If both LTSSM_ENABLE and REF_SSP_ENABLE are active we have a strong
+ * indication that the bootloader activated the link.
+ */
+ gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
+ gpr12 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12);
- if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) &&
- (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) {
- val = readl(pp->dbi_base + PCIE_PL_PFLR);
- val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
- val |= PCIE_PL_PFLR_FORCE_LINK;
+ if ((gpr1 & IMX6Q_GPR1_PCIE_REF_CLK_EN) &&
+ (gpr12 & IMX6Q_GPR12_PCIE_CTL_2)) {
+ val = readl(pp->dbi_base + PCIE_PL_PFLR);
+ val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
+ val |= PCIE_PL_PFLR_FORCE_LINK;
- data_abort_mask();
- writel(val, pp->dbi_base + PCIE_PL_PFLR);
- data_abort_unmask();
+ data_abort_mask();
+ writel(val, pp->dbi_base + PCIE_PL_PFLR);
+ data_abort_unmask();
- gpr12 &= ~IMX6Q_GPR12_PCIE_CTL_2;
- writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12);
- }
+ gpr12 &= ~IMX6Q_GPR12_PCIE_CTL_2;
+ writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12);
+ }
- gpr1 |= IMX6Q_GPR1_PCIE_TEST_PD;
- writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
+ gpr1 |= IMX6Q_GPR1_PCIE_TEST_PD;
+ writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
- gpr1 &= ~IMX6Q_GPR1_PCIE_REF_CLK_EN;
- writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
+ gpr1 &= ~IMX6Q_GPR1_PCIE_REF_CLK_EN;
+ writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
+ break;
+ }
return 0;
}
@@ -298,6 +328,22 @@ static int imx6_pcie_deassert_core_reset(struct pcie_port *pp)
mdelay(100);
gpio_set_value(imx6_pcie->reset_gpio, 1);
}
+
+ /*
+ * Release the PCIe PHY reset here
+ */
+ switch (imx6_pcie->variant) {
+ case IMX6QP:
+ gpr1 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
+ gpr1 &= ~IMX6Q_GPR1_PCIE_SW_RST;
+ writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
+
+ udelay(200);
+ break;
+ case IMX6Q: /* Nothing to do */
+ break;
+ }
+
return 0;
err_pcie:
@@ -329,21 +375,23 @@ static void imx6_pcie_init_phy(struct pcie_port *pp)
gpr8 = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR8);
gpr8 &= ~IMX6Q_GPR8_TX_DEEMPH_GEN1;
+ gpr8 |= imx6_pcie->tx_deemph_gen1 << 0;
writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8);
gpr8 &= ~IMX6Q_GPR8_TX_DEEMPH_GEN2_3P5DB;
+ gpr8 |= imx6_pcie->tx_deemph_gen2_3p5db << 6;
writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8);
gpr8 &= ~IMX6Q_GPR8_TX_DEEMPH_GEN2_6DB;
- gpr8 |= 20 << 12;
+ gpr8 |= imx6_pcie->tx_deemph_gen2_6db << 12;
writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8);
gpr8 &= ~IMX6Q_GPR8_TX_SWING_FULL;
- gpr8 |= 127 << 18;
+ gpr8 |= imx6_pcie->tx_swing_full << 18;
writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8);
gpr8 &= ~IMX6Q_GPR8_TX_SWING_LOW;
- gpr8 |= 127 << 25;
+ gpr8 |= imx6_pcie->tx_swing_low << 25;
writel(gpr8, imx6_pcie->iomuxc_gpr + IOMUXC_GPR8);
}
@@ -358,10 +406,6 @@ static int imx6_pcie_wait_for_link(struct pcie_port *pp)
if (!is_timeout(start, SECOND))
continue;
- 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));
return -EINVAL;
}
}
@@ -383,7 +427,7 @@ static int imx6_pcie_wait_for_speed_change(struct pcie_port *pp)
}
-static int imx6_pcie_start_link(struct pcie_port *pp)
+static int imx6_pcie_establish_link(struct pcie_port *pp)
{
struct imx6_pcie *imx6_pcie = to_imx6_pcie(pp);
uint32_t tmp;
@@ -406,8 +450,10 @@ static int imx6_pcie_start_link(struct pcie_port *pp)
writel(gpr12, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12);
ret = imx6_pcie_wait_for_link(pp);
- if (ret)
- return ret;
+ if (ret) {
+ dev_info(pp->dev, "Link never came up\n");
+ goto err_reset_phy;
+ }
/* Allow Gen2 mode after the link is up. */
tmp = readl(pp->dbi_base + PCIE_RC_LCR);
@@ -426,19 +472,28 @@ static int imx6_pcie_start_link(struct pcie_port *pp)
ret = imx6_pcie_wait_for_speed_change(pp);
if (ret) {
dev_err(pp->dev, "Failed to bring link up!\n");
- return ret;
+ goto err_reset_phy;
}
/* Make sure link training is finished as well! */
ret = imx6_pcie_wait_for_link(pp);
if (ret) {
dev_err(pp->dev, "Failed to bring link up!\n");
- return ret;
+ goto err_reset_phy;
}
tmp = readl(pp->dbi_base + PCIE_RC_LCSR);
dev_dbg(pp->dev, "Link up, Gen=%i\n", (tmp >> 16) & 0xf);
+
return 0;
+
+err_reset_phy:
+ dev_dbg(pp->dev, "PHY 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));
+ imx6_pcie_reset_phy(pp);
+
+ return ret;
}
static void imx6_pcie_host_init(struct pcie_port *pp)
@@ -451,29 +506,12 @@ static void imx6_pcie_host_init(struct pcie_port *pp)
dw_pcie_setup_rc(pp);
- imx6_pcie_start_link(pp);
-}
-
-static void imx6_pcie_reset_phy(struct pcie_port *pp)
-{
- uint32_t temp;
-
- 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);
-
- udelay(2000);
-
- 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);
+ imx6_pcie_establish_link(pp);
}
static int imx6_pcie_link_up(struct pcie_port *pp)
{
- u32 rc, debug_r0, rx_valid;
+ u32 rc;
int count = 5;
/*
@@ -507,26 +545,6 @@ static int imx6_pcie_link_up(struct pcie_port *pp)
*/
udelay(1000);
}
- /*
- * 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);
- debug_r0 = readl(pp->dbi_base + PCIE_PHY_DEBUG_R0);
-
- if (rx_valid & 0x01)
- return 0;
-
- if ((debug_r0 & 0x3f) != 0x0d)
- return 0;
-
- dev_err(pp->dev, "transition to gen2 is stuck, reset PHY!\n");
- dev_dbg(pp->dev, "debug_r0=%08x debug_r1=%08x\n", debug_r0, rc);
-
- imx6_pcie_reset_phy(pp);
return 0;
}
@@ -568,6 +586,9 @@ static int __init imx6_pcie_probe(struct device_d *dev)
pp = &imx6_pcie->pp;
pp->dev = dev;
+ imx6_pcie->variant =
+ (enum imx6_pcie_variants)of_device_get_match_data(dev);
+
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return PTR_ERR(iores);
@@ -606,6 +627,27 @@ static int __init imx6_pcie_probe(struct device_d *dev)
/* Grab GPR config register range */
imx6_pcie->iomuxc_gpr = IOMEM(MX6_IOMUXC_BASE_ADDR);
+ /* Grab PCIe PHY Tx Settings */
+ if (of_property_read_u32(np, "fsl,tx-deemph-gen1",
+ &imx6_pcie->tx_deemph_gen1))
+ imx6_pcie->tx_deemph_gen1 = 0;
+
+ if (of_property_read_u32(np, "fsl,tx-deemph-gen2-3p5db",
+ &imx6_pcie->tx_deemph_gen2_3p5db))
+ imx6_pcie->tx_deemph_gen2_3p5db = 0;
+
+ if (of_property_read_u32(np, "fsl,tx-deemph-gen2-6db",
+ &imx6_pcie->tx_deemph_gen2_6db))
+ imx6_pcie->tx_deemph_gen2_6db = 20;
+
+ if (of_property_read_u32(np, "fsl,tx-swing-full",
+ &imx6_pcie->tx_swing_full))
+ imx6_pcie->tx_swing_full = 127;
+
+ if (of_property_read_u32(np, "fsl,tx-swing-low",
+ &imx6_pcie->tx_swing_low))
+ imx6_pcie->tx_swing_low = 127;
+
ret = imx6_add_pcie_port(pp, dev);
if (ret < 0)
return ret;
@@ -618,29 +660,13 @@ static int __init imx6_pcie_probe(struct device_d *dev)
static void imx6_pcie_remove(struct device_d *dev)
{
struct imx6_pcie *imx6_pcie = dev->priv;
- u32 val;
-
- val = readl(imx6_pcie->pp.dbi_base + PCIE_PL_PFLR);
- val &= ~PCIE_PL_PFLR_LINK_STATE_MASK;
- val |= PCIE_PL_PFLR_FORCE_LINK;
- data_abort_mask();
- writel(val, imx6_pcie->pp.dbi_base + PCIE_PL_PFLR);
- data_abort_unmask();
-
- val = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR12);
- val &= ~IMX6Q_GPR12_PCIE_CTL_2;
- writel(val, imx6_pcie->iomuxc_gpr + IOMUXC_GPR12);
-
- val = readl(imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
- val |= IMX6Q_GPR1_PCIE_TEST_PD;
- writel(val, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
- val &= ~IMX6Q_GPR1_PCIE_REF_CLK_EN;
- writel(val, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1);
+ imx6_pcie_assert_core_reset(&imx6_pcie->pp);
}
static struct of_device_id imx6_pcie_of_match[] = {
- { .compatible = "fsl,imx6q-pcie", },
+ { .compatible = "fsl,imx6q-pcie", .data = (void *)IMX6Q, },
+ { .compatible = "fsl,imx6qp-pcie", .data = (void *)IMX6QP, },
{},
};
diff --git a/include/mfd/imx6q-iomuxc-gpr.h b/include/mfd/imx6q-iomuxc-gpr.h
index a7ef1c8915..b2c9da6579 100644
--- a/include/mfd/imx6q-iomuxc-gpr.h
+++ b/include/mfd/imx6q-iomuxc-gpr.h
@@ -95,6 +95,7 @@
#define IMX6Q_GPR0_DMAREQ_MUX_SEL0_IOMUX BIT(0)
#define IMX6Q_GPR1_PCIE_REQ_MASK (0x3 << 30)
+#define IMX6Q_GPR1_PCIE_SW_RST BIT(29)
#define IMX6Q_GPR1_PCIE_EXIT_L1 BIT(28)
#define IMX6Q_GPR1_PCIE_RDY_L23 BIT(27)
#define IMX6Q_GPR1_PCIE_ENTER_L1 BIT(26)
diff --git a/include/of_device.h b/include/of_device.h
new file mode 100644
index 0000000000..e84fc9c377
--- /dev/null
+++ b/include/of_device.h
@@ -0,0 +1,48 @@
+#ifndef __OF_DEVICE_H
+#define __OF_DEVICE_H
+
+#include <driver.h>
+#include <of.h>
+
+
+#ifdef CONFIG_OFTREE
+extern const struct of_device_id *of_match_device(
+ const struct of_device_id *matches, const struct device_d *dev);
+
+/**
+ * of_driver_match_device - Tell if a driver's of_match_table matches a device.
+ * @drv: the device_driver structure to test
+ * @dev: the device structure to match against
+ */
+static inline int of_driver_match_device(struct device_d *dev,
+ const struct driver_d *drv)
+{
+ return of_match_device(drv->of_compatible, dev) != NULL;
+}
+
+extern const void *of_device_get_match_data(const struct device_d *dev);
+
+#else /* CONFIG_OF */
+
+static inline int of_driver_match_device(struct device_d *dev,
+ const struct device_d *drv)
+{
+ return 0;
+}
+
+static inline const void *of_device_get_match_data(const struct device_d *dev)
+{
+ return NULL;
+}
+
+static inline const struct of_device_id *__of_match_device(
+ const struct of_device_id *matches, const struct device_d *dev)
+{
+ return NULL;
+}
+#define of_match_device(matches, dev) \
+ __of_match_device(of_match_ptr(matches), (dev))
+
+#endif /* CONFIG_OF */
+
+#endif /* _LINUX_OF_DEVICE_H */