]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
ENGR00179621 MX6 PCIE: bring up PCIE on i.MX6 SD board
authorRichard Zhu <r65037@freescale.com>
Thu, 12 Apr 2012 01:54:30 +0000 (09:54 +0800)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 24 May 2013 06:34:25 +0000 (08:34 +0200)
* Bring up the PCIE on i.MX6 SD board
* Add the PCIE PHY access routines
* Wrapper the board related codes by register one
  platform driver and data

Signed-off-by: Richard Zhu <r65037@freescale.com>
arch/arm/mach-mx6/Kconfig
arch/arm/mach-mx6/board-mx6q_arm2.c
arch/arm/mach-mx6/board-mx6q_sabresd.c
arch/arm/mach-mx6/board-mx6q_sabresd.h
arch/arm/mach-mx6/devices-imx6q.h
arch/arm/mach-mx6/pcie.c
arch/arm/plat-mxc/devices/Kconfig
arch/arm/plat-mxc/devices/Makefile
arch/arm/plat-mxc/devices/platform-imx-pcie.c [new file with mode: 0644]
arch/arm/plat-mxc/include/mach/devices-common.h
arch/arm/plat-mxc/include/mach/pcie.h [new file with mode: 0644]

index 1df24213195b0dfcd2653f223c94d7bbe11d8e97..1db924a0975e09507be64af422c1813c5351854b 100644 (file)
@@ -60,6 +60,7 @@ config MACH_MX6Q_ARM2
        select IMX_HAVE_PLATFORM_MXC_MLB
        select IMX_HAVE_PLATFORM_IMX_EPDC
        select IMX_HAVE_PLATFORM_IMX_PXP
+       select IMX_HAVE_PLATFORM_IMX_PCIE
        help
          Include support for i.MX 6Quad Armadillo2 platform. This includes specific
          configurations for the board and its peripherals.
@@ -124,6 +125,7 @@ config MACH_MX6Q_SABRESD
        select IMX_HAVE_PLATFORM_MXC_HDMI
        select IMX_HAVE_PLATFORM_IMX_ASRC
        select IMX_HAVE_PLATFORM_FLEXCAN
+       select IMX_HAVE_PLATFORM_IMX_PCIE
        help
          Include support for i.MX 6Quad SABRE SD platform. This includes specific
          configurations for the board and its peripherals.
index 2318f76cc5eb6f004ae283eec2b0110ca8224b8e..d71c6e2f18e1ddd7005d54b01c657059fe94a07d 100644 (file)
 #define MX6_ARM2_IO_EXP_GPIO1(x)       (MX6_ARM2_MAX7310_1_BASE_ADDR + (x))
 #define MX6_ARM2_IO_EXP_GPIO2(x)       (MX6_ARM2_MAX7310_2_BASE_ADDR + (x))
 
+#define MX6_ARM2_PCIE_PWR_EN           MX6_ARM2_IO_EXP_GPIO1(2)
+#define MX6_ARM2_PCIE_RESET            MX6_ARM2_IO_EXP_GPIO2(2)
+
 #define MX6_ARM2_CAN2_STBY             MX6_ARM2_IO_EXP_GPIO2(1)
 
 
@@ -1929,6 +1932,13 @@ static struct mxc_spdif_platform_data mxc_spdif_data = {
        .spdif_clk              = NULL, /* spdif bus clk */
 };
 
+static const struct imx_pcie_platform_data mx6_arm2_pcie_data  __initconst = {
+       .pcie_pwr_en    = MX6_ARM2_PCIE_PWR_EN,
+       .pcie_rst       = MX6_ARM2_PCIE_RESET,
+       .pcie_wake_up   = -EINVAL,
+       .pcie_dis       = -EINVAL,
+};
+
 static int __init early_disable_mipi_dsi(char *p)
 {
        /*enable on board HDMI*/
@@ -2164,6 +2174,7 @@ static void __init mx6_arm2_init(void)
                mxc_register_device(&max17135_sensor_device, NULL);
                imx6dl_add_imx_epdc(&epdc_data);
        }
+       imx6q_add_pcie(&mx6_arm2_pcie_data);
 }
 
 extern void __iomem *twd_base;
index d0d825463f9e5da1725f808daf53b8e31c6990e0..fba11a5789f1367296b4bcf52b772404330abee0 100644 (file)
 #define SABRESD_DI1_D0_CS      IMX_GPIO_NR(6, 31)
 
 #define SABRESD_HEADPHONE_DET  IMX_GPIO_NR(7, 8)
-#define SABRESD_USB_HUB_RESET  IMX_GPIO_NR(7, 12)
 #define SABRESD_PCIE_RST_B_REVB        IMX_GPIO_NR(7, 12)
 #define SABRESD_PMIC_INT_B     IMX_GPIO_NR(7, 13)
 #define SABRESD_PFUZE_INT      IMX_GPIO_NR(7, 13)
@@ -1433,6 +1432,13 @@ static void mx6_snvs_poweroff(void)
        writel(value | 0x60, mx6_snvs_base + SNVS_LPCR);
 }
 
+static const struct imx_pcie_platform_data mx6_sabresd_pcie_data __initconst = {
+       .pcie_pwr_en    = SABRESD_PCIE_PWR_EN,
+       .pcie_rst       = SABRESD_PCIE_RST_B_REVB,
+       .pcie_wake_up   = SABRESD_PCIE_WAKE_B,
+       .pcie_dis       = SABRESD_PCIE_DIS_B,
+};
+
 /*!
  * Board specific initialization.
  */
@@ -1549,9 +1555,6 @@ static void __init mx6_sabresd_board_init(void)
        imx_asrc_data.asrc_audio_clk = clk_get(NULL, "asrc_serial_clk");
        imx6q_add_asrc(&imx_asrc_data);
 
-       /* release USB Hub reset */
-       gpio_set_value(SABRESD_USB_HUB_RESET, 1);
-
        imx6q_add_mxc_pwm(0);
        imx6q_add_mxc_pwm(1);
        imx6q_add_mxc_pwm(2);
@@ -1622,6 +1625,7 @@ static void __init mx6_sabresd_board_init(void)
        platform_device_register(&sabresd_max8903_charger_1);
        pm_power_off = mx6_snvs_poweroff;
 
+       imx6q_add_pcie(&mx6_sabresd_pcie_data);
 }
 
 extern void __iomem *twd_base;
index 1b8bd70e3887a76deabd359ce20ae591d6b41b8b..d84a606f92063479bb46ebd028ff24528200c0c0 100644 (file)
@@ -123,9 +123,6 @@ static iomux_v3_cfg_t mx6q_sabresd_pads[] = {
        MX6Q_PAD_EIM_A23__GPIO_6_6,     /* J12 - Boot Mode Select */
        MX6Q_PAD_NANDF_RB0__GPIO_6_10, /* AUX_5V Enable */
 
-       /* GPIO7 */
-       MX6Q_PAD_GPIO_17__GPIO_7_12,    /* USB Hub Reset */
-
        /* I2C1, WM8958 */
        MX6Q_PAD_CSI0_DAT8__I2C1_SDA,
        MX6Q_PAD_CSI0_DAT9__I2C1_SCL,
@@ -251,7 +248,11 @@ static iomux_v3_cfg_t mx6q_sabresd_pads[] = {
        /*GPS AUX_3V15_EN*/
        MX6Q_PAD_NANDF_WP_B__GPIO_6_9,
 
+       /* PCIE */
        MX6Q_PAD_EIM_D19__GPIO_3_19, /* PCIE_PWR_EN */
+
+       MX6Q_PAD_GPIO_17__GPIO_7_12, /* PCIE_RST */
+       MX6Q_PAD_KEY_COL4__GPIO_4_14, /* PCIE_DIS */
 };
 
 static iomux_v3_cfg_t mx6q_sabresd_csi0_sensor_pads[] = {
index 02ac1f409799c8678d3027f2fb658e0a6597d415..df5e72cc791088a158d03a45ab2b9298b7dcf9c1 100644 (file)
@@ -210,3 +210,6 @@ extern const struct imx_epdc_data imx6dl_epdc_data __initconst;
        imx_add_imx_epdc(&imx6dl_epdc_data, pdata)
 extern const struct imx_vdoa_data imx6q_vdoa_data __initconst;
 #define imx6q_add_vdoa() imx_add_vdoa(&imx6q_vdoa_data)
+
+extern const struct imx_pcie_data imx6q_pcie_data __initconst;
+#define imx6q_add_pcie(pdata) imx_add_pcie(&imx6q_pcie_data, pdata)
index 985e6252a5f97964abacdd19dc4bb56b8338541d..3fcb115df95c5c88303f3cc66273bdf7372e1a44 100644 (file)
@@ -29,6 +29,9 @@
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
+#include <linux/platform_device.h>
+
+#include <mach/pcie.h>
 
 #include <asm/sizes.h>
 
 /* GPR8: iomuxc_gpr8_tx_swing_low(iomuxc_gpr8[31:25]) */
 #define iomuxc_gpr8_tx_swing_low               (0x7F << 25)
 
+/* Registers of PHY */
+/* Register PHY_STS_R */
+/* PHY Status Register */
+#define PHY_STS_R (PRT_LOG_R_BaseAddress + 0x110)
+
+/* Register PHY_CTRL_R */
+/* PHY Control Register */
+#define PHY_CTRL_R (PRT_LOG_R_BaseAddress + 0x114)
+
+#define SSP_CR_SUP_DIG_MPLL_OVRD_IN_LO 0x0011
+/* FIELD: RES_ACK_IN_OVRD [15:15]
+// FIELD: RES_ACK_IN [14:14]
+// FIELD: RES_REQ_IN_OVRD [13:13]
+// FIELD: RES_REQ_IN [12:12]
+// FIELD: RTUNE_REQ_OVRD [11:11]
+// FIELD: RTUNE_REQ [10:10]
+// FIELD: MPLL_MULTIPLIER_OVRD [9:9]
+// FIELD: MPLL_MULTIPLIER [8:2]
+// FIELD: MPLL_EN_OVRD [1:1]
+// FIELD: MPLL_EN [0:0]
+*/
+
+#define SSP_CR_SUP_DIG_ATEOVRD 0x0010
+/* FIELD: ateovrd_en [2:2]
+// FIELD: ref_usb2_en [1:1]
+// FIELD: ref_clkdiv2 [0:0]
+*/
+
+#define SSP_CR_LANE0_DIG_RX_OVRD_IN_LO 0x1005
+/* FIELD: RX_LOS_EN_OVRD [13:13]
+// FIELD: RX_LOS_EN [12:12]
+// FIELD: RX_TERM_EN_OVRD [11:11]
+// FIELD: RX_TERM_EN [10:10]
+// FIELD: RX_BIT_SHIFT_OVRD [9:9]
+// FIELD: RX_BIT_SHIFT [8:8]
+// FIELD: RX_ALIGN_EN_OVRD [7:7]
+// FIELD: RX_ALIGN_EN [6:6]
+// FIELD: RX_DATA_EN_OVRD [5:5]
+// FIELD: RX_DATA_EN [4:4]
+// FIELD: RX_PLL_EN_OVRD [3:3]
+// FIELD: RX_PLL_EN [2:2]
+// FIELD: RX_INVERT_OVRD [1:1]
+// FIELD: RX_INVERT [0:0]
+*/
+
+#define SSP_CR_LANE0_DIG_RX_ASIC_OUT 0x100D
+/* FIELD: LOS [2:2]
+// FIELD: PLL_STATE [1:1]
+// FIELD: VALID [0:0]
+*/
+
+/* control bus bit definition */
+#define PCIE_CR_CTL_DATA_LOC 0
+#define PCIE_CR_CTL_CAP_ADR_LOC 16
+#define PCIE_CR_CTL_CAP_DAT_LOC 17
+#define PCIE_CR_CTL_WR_LOC 18
+#define PCIE_CR_CTL_RD_LOC 19
+#define PCIE_CR_STAT_DATA_LOC 0
+#define PCIE_CR_STAT_ACK_LOC 16
+
+#define PCIE_CAP_STRUC_BaseAddress 0x70
+
+/* Register LNK_CAP */
+/* PCIE Link cap */
+#define LNK_CAP (PCIE_CAP_STRUC_BaseAddress + 0xc)
+#define LNK_CAP_RegisterSize 32
+#define LNK_CAP_RegisterResetValue 0x011cc12
+#define LNK_CAP_RegisterResetMask 0xffffffff
+
 /* End of Register Definitions */
 
 #define PCIE_DBI_BASE_ADDR     (PCIE_ARB_END_ADDR - SZ_16K + 1)
 #define  PCIE_CONF_FUNC(f)             (((f) & 0x7) << 8)
 #define  PCIE_CONF_REG(r)              ((r) & ~0x3)
 
-#define MX6_ARM2_PCIE_PWR_EN           (IMX_GPIO_NR(8, 0) + 2)
-#define MX6_ARM2_PCIE_RESET            (IMX_GPIO_NR(8, 8) + 2)
-
 static void __iomem *base;
 static void __iomem *dbi_base;
 
@@ -125,6 +194,10 @@ struct imx_pcie_port {
 static struct imx_pcie_port imx_pcie_port[1];
 static int num_pcie_ports;
 
+static int pcie_phy_cr_read(int addr, int *data);
+static int pcie_phy_cr_write(int addr, int data);
+static void change_field(int *in, int start, int end, int val);
+
 /* IMX PCIE GPR configure routines */
 static inline void imx_pcie_clrset(u32 mask, u32 val, void __iomem *addr)
 {
@@ -194,13 +267,39 @@ static int __init imx_pcie_setup(int nr, struct pci_sys_data *sys)
 static int imx_pcie_link_up(void __iomem *dbi_base)
 {
        /* Check the pcie link up or link down */
-       u32 rc, iterations = 0x100000;
+       int iterations = 200;
+       u32 rc, ltssm, rx_valid, temp;
 
        do {
                /* link is debug bit 36 debug 1 start in bit 32 */
-               rc = readl(dbi_base + DB_R1) & (0x1 << (36-32)) ;
+               rc = readl(dbi_base + DB_R1) & (0x1 << (36 - 32)) ;
                iterations--;
-               if ((iterations % 0x100000) == 0)
+               usleep_range(2000, 3000);
+
+               /* 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_cr_read(SSP_CR_LANE0_DIG_RX_ASIC_OUT, &rx_valid);
+               ltssm = readl(dbi_base + DB_R0) & 0x3F;
+               if ((ltssm == 0x0D) && ((rx_valid & 0x01) == 0)) {
+                       pr_info("Transition to gen2 is stuck, reset PHY!\n");
+                       pcie_phy_cr_read(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO, &temp);
+                       change_field(&temp, 3, 3, 0x1);
+                       change_field(&temp, 5, 5, 0x1);
+                       pcie_phy_cr_write(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO,
+                                       0x0028);
+                       usleep_range(2000, 3000);
+                       pcie_phy_cr_read(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO, &temp);
+                       change_field(&temp, 3, 3, 0x0);
+                       change_field(&temp, 5, 5, 0x0);
+                       pcie_phy_cr_write(SSP_CR_LANE0_DIG_RX_OVRD_IN_LO,
+                                       0x0000);
+               }
+
+               if ((iterations < 0))
                        pr_info("link up failed, DB_R0:0x%08x, DB_R1:0x%08x!\n"
                                        , readl(dbi_base + DB_R0)
                                        , readl(dbi_base + DB_R1));
@@ -351,15 +450,151 @@ static struct hw_pci imx_pci __initdata = {
        .map_irq        = imx_pcie_map_irq,
 };
 
-static void imx_pcie_enable_controller(void)
+/* PHY CR bus acess routines */
+static int pcie_phy_cr_ack_polling(int max_iterations, int exp_val)
+{
+       u32 temp_rd_data, wait_counter = 0;
+
+       do {
+               temp_rd_data = readl(dbi_base + PHY_STS_R);
+               temp_rd_data = (temp_rd_data >> PCIE_CR_STAT_ACK_LOC) & 0x1;
+               wait_counter++;
+       } while ((wait_counter < max_iterations) && (temp_rd_data != exp_val));
+
+       if (temp_rd_data != exp_val)
+               return 0 ;
+       return 1 ;
+}
+
+static int pcie_phy_cr_cap_addr(int addr)
+{
+       u32 temp_wr_data;
+
+       /* write addr */
+       temp_wr_data = addr << PCIE_CR_CTL_DATA_LOC ;
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* capture addr */
+       temp_wr_data |= (0x1 << PCIE_CR_CTL_CAP_ADR_LOC);
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* wait for ack */
+       if (!pcie_phy_cr_ack_polling(100, 1))
+               return 0;
+
+       /* deassert cap addr */
+       temp_wr_data = addr << PCIE_CR_CTL_DATA_LOC;
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* wait for ack de-assetion */
+       if (!pcie_phy_cr_ack_polling(100, 0))
+               return 0 ;
+
+       return 1 ;
+}
+
+static int pcie_phy_cr_read(int addr , int *data)
+{
+       u32 temp_rd_data, temp_wr_data;
+
+       /*  write addr */
+       /* cap addr */
+       if (!pcie_phy_cr_cap_addr(addr))
+               return 0;
+
+       /* assert rd signal */
+       temp_wr_data = 0x1 << PCIE_CR_CTL_RD_LOC;
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* wait for ack */
+       if (!pcie_phy_cr_ack_polling(100, 1))
+               return 0;
+
+       /* after got ack return data */
+       temp_rd_data = readl(dbi_base + PHY_STS_R);
+       *data = (temp_rd_data & (0xffff << PCIE_CR_STAT_DATA_LOC)) ;
+
+       /* deassert rd signal */
+       temp_wr_data = 0x0;
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* wait for ack de-assetion */
+       if (!pcie_phy_cr_ack_polling(100, 0))
+               return 0 ;
+
+       return 1 ;
+
+}
+
+static int pcie_phy_cr_write(int addr, int data)
+{
+       u32 temp_wr_data;
+
+       /* write addr */
+       /* cap addr */
+       if (!pcie_phy_cr_cap_addr(addr))
+               return 0 ;
+
+       temp_wr_data = data << PCIE_CR_CTL_DATA_LOC;
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* capture data */
+       temp_wr_data |= (0x1 << PCIE_CR_CTL_CAP_DAT_LOC);
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* wait for ack */
+       if (!pcie_phy_cr_ack_polling(100, 1))
+               return 0 ;
+
+       /* deassert cap data */
+       temp_wr_data = data << PCIE_CR_CTL_DATA_LOC;
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* wait for ack de-assetion */
+       if (!pcie_phy_cr_ack_polling(100, 0))
+               return 0;
+
+       /* assert wr signal */
+       temp_wr_data = 0x1 << PCIE_CR_CTL_WR_LOC;
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* wait for ack */
+       if (!pcie_phy_cr_ack_polling(100, 1))
+               return 0;
+
+       /* deassert wr signal */
+       temp_wr_data = data << PCIE_CR_CTL_DATA_LOC;
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       /* wait for ack de-assetion */
+       if (!pcie_phy_cr_ack_polling(100, 0))
+               return 0;
+
+       temp_wr_data = 0x0 ;
+       writel(temp_wr_data, dbi_base + PHY_CTRL_R);
+
+       return 1 ;
+}
+
+static void change_field(int *in, int start, int end, int val)
+{
+       int mask;
+
+       mask = ((0xFFFFFFFF << start) ^ (0xFFFFFFFF << (end + 1))) & 0xFFFFFFFF;
+       *in = (*in & ~mask) | (val << start);
+}
+
+static void imx_pcie_enable_controller(struct device *dev)
 {
        struct clk *pcie_clk;
+       struct imx_pcie_platform_data *pdata = dev->platform_data;
 
-       /* PCIE PWR_EN: EXP_IO2 of MAX7310_1 */
-       gpio_request(MX6_ARM2_PCIE_PWR_EN, "PCIE POWER_EN");
+       /* Enable PCIE power */
+       gpio_request(pdata->pcie_pwr_en, "PCIE POWER_EN");
+
+       /* activate PCIE_PWR_EN */
+       gpio_direction_output(pdata->pcie_pwr_en, 1);
 
-       /* activate PCIE_PWR_EN CTRL_2 */
-       gpio_direction_output(MX6_ARM2_PCIE_PWR_EN, 1);
        imx_pcie_clrset(iomuxc_gpr1_test_powerdown, 0 << 18, IOMUXC_GPR1);
 
        /* enable the clks */
@@ -373,23 +608,28 @@ static void imx_pcie_enable_controller(void)
        }
 }
 
-static void card_reset(void)
+static void card_reset(struct device *dev)
 {
-       /* PCIE RESET: EXP_IO2 of MAX7310_2 */
-       gpio_request(MX6_ARM2_PCIE_RESET, "PCIE RESET");
+       struct imx_pcie_platform_data *pdata = dev->platform_data;
+
+       /* PCIE RESET */
+       gpio_request(pdata->pcie_rst, "PCIE RESET");
 
        /* activate PERST_B */
-       gpio_direction_output(MX6_ARM2_PCIE_RESET, 0);
+       gpio_direction_output(pdata->pcie_rst, 0);
 
        /* Add one reset to the pcie external device */
        msleep(100);
 
        /* deactive PERST_B */
-       gpio_direction_output(MX6_ARM2_PCIE_RESET, 1);
+       gpio_direction_output(pdata->pcie_rst, 1);
 }
 
-static void __init add_pcie_port(void __iomem *base, void __iomem *dbi_base)
+static void __init add_pcie_port(void __iomem *base, void __iomem *dbi_base,
+               struct imx_pcie_platform_data *pdata)
 {
+       struct clk *pcie_clk;
+
        if (imx_pcie_link_up(dbi_base)) {
                struct imx_pcie_port *pp = &imx_pcie_port[num_pcie_ports++];
 
@@ -401,23 +641,51 @@ static void __init add_pcie_port(void __iomem *base, void __iomem *dbi_base)
                pp->dbi_base = dbi_base;
                spin_lock_init(&pp->conf_lock);
                memset(pp->res, 0, sizeof(pp->res));
-       } else
+       } else {
                pr_info("IMX PCIe port: link down!\n");
+               /* Release the clocks, and disable the power */
+
+               pcie_clk = clk_get(NULL, "pcie_clk");
+               if (IS_ERR(pcie_clk))
+                       pr_err("no pcie clock.\n");
+
+               clk_disable(pcie_clk);
+               clk_put(pcie_clk);
+
+               /* Disable PCIE power */
+               gpio_request(pdata->pcie_pwr_en, "PCIE POWER_EN");
+
+               /* activate PCIE_PWR_EN */
+               gpio_direction_output(pdata->pcie_pwr_en, 0);
+
+               imx_pcie_clrset(iomuxc_gpr1_test_powerdown, 1 << 18,
+                               IOMUXC_GPR1);
+       }
 }
 
-static int __init imx_pcie_init(void)
+static int __devinit imx_pcie_pltfm_probe(struct platform_device *pdev)
 {
+       struct resource *mem;
+       struct device *dev = &pdev->dev;
+       struct imx_pcie_platform_data *pdata = dev->platform_data;
+
+
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!mem) {
+               dev_err(dev, "no mmio space\n");
+               return -EINVAL;
+       }
+
        base = ioremap_nocache(PCIE_ARB_END_ADDR - SZ_1M + 1, SZ_1M - SZ_16K);
        if (!base) {
                pr_err("error with ioremap in function %s\n", __func__);
                return -EIO;
        }
 
-       dbi_base = ioremap_nocache(PCIE_DBI_BASE_ADDR, SZ_16K);
+       dbi_base = devm_ioremap(dev, mem->start, resource_size(mem));
        if (!dbi_base) {
-               pr_err("error with ioremap in function %s\n", __func__);
-               iounmap(base);
-               return -EIO;
+               dev_err(dev, "can't map %pR\n", mem);
+               return -ENOMEM;
        }
 
        /* FIXME the field name should be aligned to RM */
@@ -427,6 +695,7 @@ static int __init imx_pcie_init(void)
        imx_pcie_clrset(iomuxc_gpr12_device_type, PCI_EXP_TYPE_ROOT_PORT << 12,
                        IOMUXC_GPR12);
        imx_pcie_clrset(iomuxc_gpr12_los_level, 9 << 4, IOMUXC_GPR12);
+
        imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen1, 21 << 0, IOMUXC_GPR8);
        imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen2_3p5db, 21 << 6, IOMUXC_GPR8);
        imx_pcie_clrset(iomuxc_gpr8_tx_deemph_gen2_6db, 32 << 12, IOMUXC_GPR8);
@@ -434,24 +703,88 @@ static int __init imx_pcie_init(void)
        imx_pcie_clrset(iomuxc_gpr8_tx_swing_low, 115 << 25, IOMUXC_GPR8);
 
        /* Enable the pwr, clks and so on */
-       imx_pcie_enable_controller();
+       imx_pcie_enable_controller(dev);
 
        imx_pcie_clrset(iomuxc_gpr1_pcie_ref_clk_en, 1 << 16, IOMUXC_GPR1);
 
        /* togle the external card's reset */
-       card_reset() ;
+       card_reset(dev) ;
+
+       usleep_range(3000, 4000);
+       imx_pcie_regions_setup(dbi_base);
+       usleep_range(3000, 4000);
 
        /* start link up */
        imx_pcie_clrset(iomuxc_gpr12_app_ltssm_enable, 1 << 10, IOMUXC_GPR12);
 
        /* add the pcie port */
-       add_pcie_port(base, dbi_base);
+       add_pcie_port(base, dbi_base, pdata);
 
-       usleep_range(3000, 4000);
-       imx_pcie_regions_setup(dbi_base);
-       usleep_range(3000, 4000);
 
        pci_common_init(&imx_pci);
        return 0;
 }
-device_initcall(imx_pcie_init);
+
+static int __devexit imx_pcie_pltfm_remove(struct platform_device *pdev)
+{
+       struct clk *pcie_clk;
+       struct device *dev = &pdev->dev;
+       struct imx_pcie_platform_data *pdata = dev->platform_data;
+       struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       /* Release clocks, and disable power  */
+       pcie_clk = clk_get(NULL, "pcie_clk");
+       if (IS_ERR(pcie_clk))
+               pr_err("no pcie clock.\n");
+
+       if (pcie_clk) {
+               clk_disable(pcie_clk);
+               clk_put(pcie_clk);
+       }
+
+       /* Disable PCIE power */
+       gpio_request(pdata->pcie_pwr_en, "PCIE POWER_EN");
+
+       /* activate PCIE_PWR_EN */
+       gpio_direction_output(pdata->pcie_pwr_en, 0);
+
+       imx_pcie_clrset(iomuxc_gpr1_test_powerdown, 1 << 18, IOMUXC_GPR1);
+
+       iounmap(base);
+       iounmap(dbi_base);
+       release_mem_region(iomem->start, resource_size(iomem));
+       platform_set_drvdata(pdev, NULL);
+
+       return 0;
+}
+
+static struct platform_driver imx_pcie_pltfm_driver = {
+       .driver = {
+               .name   = "imx-pcie",
+               .owner  = THIS_MODULE,
+       },
+       .probe          = imx_pcie_pltfm_probe,
+       .remove         = __devexit_p(imx_pcie_pltfm_remove),
+};
+
+/*****************************************************************************\
+ *                                                                           *
+ * Driver init/exit                                                          *
+ *                                                                           *
+\*****************************************************************************/
+
+static int __init imx_pcie_drv_init(void)
+{
+       return platform_driver_register(&imx_pcie_pltfm_driver);
+}
+
+static void __exit imx_pcie_drv_exit(void)
+{
+       platform_driver_unregister(&imx_pcie_pltfm_driver);
+}
+
+module_init(imx_pcie_drv_init);
+module_exit(imx_pcie_drv_exit);
+
+MODULE_DESCRIPTION("i.MX PCIE platform driver");
+MODULE_LICENSE("GPL v2");
index 350845eb64f07838645446e777a95eadec25149e..47d19921c6b558b3c245068c61565b330170bc01 100755 (executable)
@@ -172,3 +172,6 @@ config IMX_HAVE_PLATFORM_IMX_MIPI_CSI2
 
 config IMX_HAVE_PLATFORM_IMX_VDOA
        bool
+
+config IMX_HAVE_PLATFORM_IMX_PCIE
+       bool
index f2741caecfcaacc6f51c909d1e5875560420fa3e..be2b0a6748257e665c3c4fe4bb3ab80b260cfe34 100755 (executable)
@@ -62,3 +62,4 @@ obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_ASRC) += platform-imx-asrc.o
 obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_MIPI_DSI) += platform-imx-mipi_dsi.o
 obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_MIPI_CSI2) += platform-imx-mipi_csi2.o
 obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_VDOA) += platform-imx-vdoa.o
+obj-$(CONFIG_IMX_HAVE_PLATFORM_IMX_PCIE) += platform-imx-pcie.o
diff --git a/arch/arm/plat-mxc/devices/platform-imx-pcie.c b/arch/arm/plat-mxc/devices/platform-imx-pcie.c
new file mode 100644 (file)
index 0000000..cf36093
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <mach/hardware.h>
+#include <mach/devices-common.h>
+
+#define imx_pcie_data_entry_single(soc, _id, _hwid, size)              \
+       {                                                               \
+               .id = _id,                                              \
+               .iobase = soc ## _PCIE ## _hwid ## _BASE_ADDR,  \
+               .iosize = size,                                         \
+               .irq    = soc ## _INT_PCIE ## _hwid,                    \
+       }
+
+#define imx_pcie_data_entry(soc, _id, _hwid, _size)                    \
+       [_id] = imx_pcie_data_entry_single(soc, _id, _hwid, _size)
+
+#ifdef CONFIG_SOC_IMX6Q
+#define MX6Q_PCIE_BASE_ADDR (PCIE_ARB_END_ADDR - SZ_16K + 1)
+#define MX6Q_INT_PCIE MXC_INT_PCIE_3
+const struct imx_pcie_data imx6q_pcie_data __initconst =
+                       imx_pcie_data_entry_single(MX6Q, 0, , SZ_16K);
+#endif
+
+struct platform_device *__init imx_add_pcie(
+               const struct imx_pcie_data *data,
+               const struct imx_pcie_platform_data *pdata)
+{
+       struct resource res[] = {
+               {
+                       .start = data->iobase,
+                       .end = data->iobase + data->iosize - 1,
+                       .flags = IORESOURCE_MEM,
+               }, {
+                       .start = data->irq,
+                       .end = data->irq,
+                       .flags = IORESOURCE_IRQ,
+               },
+       };
+
+       return imx_add_platform_device("imx-pcie", -1,
+                       res, ARRAY_SIZE(res),
+                       pdata, sizeof(*pdata));
+}
index 9f87b009f1294b174609125b487432b7912232d3..6fce8c4b8255c3b57f15cc155eec80d6ee207663 100755 (executable)
@@ -642,3 +642,15 @@ struct imx_vdoa_data {
 };
 struct platform_device *__init imx_add_vdoa(
                const struct imx_vdoa_data *data);
+
+#include <mach/pcie.h>
+struct imx_pcie_data {
+       int id;
+       resource_size_t iobase;
+       resource_size_t iosize;
+       resource_size_t irq;
+};
+
+struct platform_device *__init imx_add_pcie(
+               const struct imx_pcie_data *data,
+               const struct imx_pcie_platform_data *pdata);
diff --git a/arch/arm/plat-mxc/include/mach/pcie.h b/arch/arm/plat-mxc/include/mach/pcie.h
new file mode 100644 (file)
index 0000000..775f651
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2012 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef __ASM_ARCH_IMX_PCIE_H
+#define __ASM_ARCH_IMX_PCIE_H
+
+/**
+ * struct imx_pcie_platform_data - optional platform data for pcie on i.MX
+ *
+ * @pcie_pwr_en:       used for enable/disable pcie power (-EINVAL if unused)
+ * @pcie_rst:          used for reset pcie ep (-EINVAL if unused)
+ * @pcie_wake_up:      used for wake up (-EINVAL if unused)
+ * @pcie_dis:          used for disable pcie ep (-EINVAL if unused)
+ */
+
+struct imx_pcie_platform_data {
+       unsigned int pcie_pwr_en;
+       unsigned int pcie_rst;
+       unsigned int pcie_wake_up;
+       unsigned int pcie_dis;
+};
+#endif /* __ASM_ARCH_IMX_PCIE_H */