diff --git a/drivers/of/Makefile b/drivers/of/Makefile index 773548e..ec43870 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 0000000..e2c3c02 --- /dev/null +++ b/drivers/of/device.c @@ -0,0 +1,33 @@ +#include +#include + + +/** + * 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 46483b4..affc705 100644 --- a/drivers/pci/pci-imx6.c +++ b/drivers/pci/pci-imx6.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -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 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 @@ 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 @@ /* 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,45 +218,71 @@ 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_REF_CLK_EN; + writel(gpr1, imx6_pcie->iomuxc_gpr + IOMUXC_GPR1); + break; } - 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); - return 0; } @@ -298,6 +328,22 @@ 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 @@ 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 @@ 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_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 @@ 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 @@ 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 @@ 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 @@ */ 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 @@ 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 @@ /* 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 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 a7ef1c8..b2c9da6 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 0000000..e84fc9c --- /dev/null +++ b/include/of_device.h @@ -0,0 +1,48 @@ +#ifndef __OF_DEVICE_H +#define __OF_DEVICE_H + +#include +#include + + +#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 */