karo: tx6: try to revive I2C bus
authorLothar Waßmann <LW@KARO-electronics.de>
Fri, 21 Aug 2015 14:00:10 +0000 (16:00 +0200)
committerLothar Waßmann <LW@KARO-electronics.de>
Fri, 21 Aug 2015 14:00:10 +0000 (16:00 +0200)
When resetting the module during an ongoing I2C transfer with the
PMIC, the PMIC may hang the I2C bus. Configure the I2C pins as GPIOs
and drive them high to revive the I2C bus.

board/karo/tx6/lowlevel_init.S
board/karo/tx6/tx6qdl.c

index 34a7b28..1f140ec 100644 (file)
@@ -481,6 +481,8 @@ ivt_end:
 
 #ifdef CONFIG_SOC_MX6Q
 #define IOMUXC_GPR1                            0x020e0004
+#define IOMUXC_SW_MUX_CTL_PAD_EIM_DATA21       0x020e00a4
+#define IOMUXC_SW_MUX_CTL_PAD_EIM_DATA28       0x020e00c4
 #define IOMUXC_SW_MUX_CTL_PAD_GPIO17           0x020e024c
 #define IOMUXC_SW_MUX_CTL_PAD_SD3_DATA7                0x020e02a8
 #define IOMUXC_SW_MUX_CTL_PAD_SD3_DATA6                0x020e02ac
@@ -502,6 +504,9 @@ ivt_end:
 #define IOMUXC_SW_MUX_CTL_PAD_NAND_DATA05      0x020e0310
 #define IOMUXC_SW_MUX_CTL_PAD_NAND_DATA06      0x020e0314
 #define IOMUXC_SW_MUX_CTL_PAD_NAND_DATA07      0x020e0318
+
+#define IOMUXC_SW_PAD_CTL_PAD_EIM_DATA21       0x020e03b8
+#define IOMUXC_SW_PAD_CTL_PAD_EIM_DATA28       0x020e03d8
 #define IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS5_P     0x020e050c
 #define IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM5                0x020e0510
 #define IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM4                0x020e0514
@@ -570,12 +575,19 @@ ivt_end:
 #define IOMUXC_SW_PAD_CTL_GRP_B4DS             0x020e07a0
 #define IOMUXC_SW_PAD_CTL_GRP_B5DS             0x020e07a4
 #define IOMUXC_SW_PAD_CTL_GRP_B6DS             0x020e07a8
+
 #define IOMUXC_UART1_UART_RTS_B_SELECT_INPUT   0x020e091c
 #define IOMUXC_UART1_UART_RX_DATA_SELECT_INPUT 0x020e0920
+
+#define IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA21     0x020e0898
+#define IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA28     0x020e089c
+#define TX6_I2C1_SEL_INP_VAL                   1
 #endif
 
 #if defined(CONFIG_SOC_MX6DL) || defined(CONFIG_SOC_MX6S)
 #define IOMUXC_GPR1                            0x020e0004
+#define IOMUXC_SW_MUX_CTL_PAD_EIM_DATA21       0x020e0158
+#define IOMUXC_SW_MUX_CTL_PAD_EIM_DATA28       0x020e0174
 #define IOMUXC_SW_MUX_CTL_PAD_GPIO17           0x020e0218
 #define IOMUXC_SW_MUX_CTL_PAD_SD3_DATA7                0x020e0330
 #define IOMUXC_SW_MUX_CTL_PAD_SD3_DATA6                0x020e032c
@@ -597,6 +609,9 @@ ivt_end:
 #define IOMUXC_SW_MUX_CTL_PAD_NAND_DATA05      0x020e0298
 #define IOMUXC_SW_MUX_CTL_PAD_NAND_DATA06      0x020e029c
 #define IOMUXC_SW_MUX_CTL_PAD_NAND_DATA07      0x020e02a0
+
+#define IOMUXC_SW_PAD_CTL_PAD_EIM_DATA21       0x020e0528
+#define IOMUXC_SW_PAD_CTL_PAD_EIM_DATA28       0x020e0544
 #define IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS5_P     0x020e04d0
 #define IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM5                0x020e0484
 #define IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM4                0x020e0480
@@ -657,13 +672,26 @@ ivt_end:
 #define IOMUXC_SW_PAD_CTL_GRP_B4DS             0x020e07a0
 #define IOMUXC_SW_PAD_CTL_GRP_B5DS             0x020e07a4
 #define IOMUXC_SW_PAD_CTL_GRP_B6DS             0x020e07a8
+
 #define IOMUXC_UART1_UART_RTS_B_SELECT_INPUT   0x020e08f8
 #define IOMUXC_UART1_UART_RX_DATA_SELECT_INPUT 0x020e08fc
+
+#define IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA21     0x020e0868
+#define IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA28     0x020e086c
+#define TX6_I2C1_SEL_INP_VAL                   1
 #endif
 
 dcd_hdr:
        MXC_DCD_START
        MXC_DCD_CMD_WRT(MXC_DCD_CMD_SZ_WORD, MXC_DCD_CMD_FLAG_WRITE)
+       /* setup I2C pads for PMIC */
+       MXC_DCD_ITEM(IOMUXC_SW_MUX_CTL_PAD_EIM_DATA21, 0x00000016)
+       MXC_DCD_ITEM(IOMUXC_SW_MUX_CTL_PAD_EIM_DATA28, 0x00000011)
+       MXC_DCD_ITEM(IOMUXC_SW_PAD_CTL_PAD_EIM_DATA21, 0x0000f079)
+       MXC_DCD_ITEM(IOMUXC_SW_PAD_CTL_PAD_EIM_DATA28, 0x0000f079)
+       MXC_DCD_ITEM(IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA21, TX6_I2C1_SEL_INP_VAL)
+       MXC_DCD_ITEM(IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA28, TX6_I2C1_SEL_INP_VAL)
+
        /* RESET_OUT GPIO_7_12 */
        MXC_DCD_ITEM(IOMUXC_SW_MUX_CTL_PAD_GPIO17, 0x00000005)
 
index 04a9222..1d09de4 100644 (file)
@@ -51,6 +51,8 @@
 #define TX6_LCD_BACKLIGHT_GPIO         IMX_GPIO_NR(1, 1)
 
 #define TX6_RESET_OUT_GPIO             IMX_GPIO_NR(7, 12)
+#define TX6_I2C1_SCL_GPIO              IMX_GPIO_NR(3, 21)
+#define TX6_I2C1_SDA_GPIO              IMX_GPIO_NR(3, 28)
 
 #ifdef CONFIG_MX6_TEMPERATURE_MIN
 #define TEMPERATURE_MIN                        CONFIG_MX6_TEMPERATURE_MIN
@@ -123,7 +125,17 @@ static const iomux_v3_cfg_t const tx6qdl_fec_pads[] = {
        MX6_PAD_ENET_TXD0__ENET_TX_DATA0,
 };
 
+static const iomux_v3_cfg_t const tx6_i2c_pads[] = {
+       /* internal I2C */
+       MX6_PAD_EIM_D28__I2C1_SDA,
+       MX6_PAD_EIM_D21__I2C1_SCL,
+};
+
 static const struct gpio const tx6qdl_gpios[] = {
+       /* These two entries are used to forcefully reinitialize the I2C bus */
+       { TX6_I2C1_SCL_GPIO, GPIOFLAG_INPUT, "I2C1 SCL", },
+       { TX6_I2C1_SDA_GPIO, GPIOFLAG_INPUT, "I2C1 SDA", },
+
        { TX6_RESET_OUT_GPIO, GPIOFLAG_OUTPUT_INIT_HIGH, "#RESET_OUT", },
        { TX6_FEC_PWR_GPIO, GPIOFLAG_OUTPUT_INIT_HIGH, "FEC PHY PWR", },
        { TX6_FEC_RST_GPIO, GPIOFLAG_OUTPUT_INIT_LOW, "FEC PHY RESET", },
@@ -244,6 +256,150 @@ static bool tx6_temp_check_enabled = true;
 #endif
 static int pmic_addr __data;
 
+#if defined(CONFIG_SOC_MX6Q)
+#define IOMUXC_SW_MUX_CTL_PAD_EIM_DATA21       0x020e00a4
+#define IOMUXC_SW_MUX_CTL_PAD_EIM_DATA28       0x020e00c4
+#define IOMUXC_SW_PAD_CTL_PAD_EIM_DATA21       0x020e03b8
+#define IOMUXC_SW_PAD_CTL_PAD_EIM_DATA28       0x020e03d8
+#define IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA21     0x020e0898
+#define IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA28     0x020e089c
+#define I2C1_SEL_INPUT_VAL                     0
+#endif
+#if defined(CONFIG_SOC_MX6DL) || defined(CONFIG_SOC_MX6S)
+#define IOMUXC_SW_MUX_CTL_PAD_EIM_DATA21       0x020e0158
+#define IOMUXC_SW_MUX_CTL_PAD_EIM_DATA28       0x020e0174
+#define IOMUXC_SW_PAD_CTL_PAD_EIM_DATA21       0x020e0528
+#define IOMUXC_SW_PAD_CTL_PAD_EIM_DATA28       0x020e0544
+#define IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA21     0x020e0868
+#define IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA28     0x020e086c
+#define I2C1_SEL_INPUT_VAL                     1
+#endif
+
+#define GPIO_DR 0
+#define GPIO_DIR 4
+#define GPIO_PSR 8
+
+static const struct i2c_gpio_regs {
+       const char *label;
+       u32 gpio;
+       unsigned long gpio_base;
+       unsigned long muxctl;
+       unsigned long padctl;
+       unsigned long sel_input;
+} tx6_i2c_iomux_regs[] = {
+       {
+               .label = "PMIC SCL",
+               .gpio = TX6_I2C1_SCL_GPIO,
+               .gpio_base = GPIO3_BASE_ADDR,
+               .muxctl = IOMUXC_SW_MUX_CTL_PAD_EIM_DATA21,
+               .padctl = IOMUXC_SW_PAD_CTL_PAD_EIM_DATA21,
+               .sel_input = IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA21,
+       }, {
+               .label = "PMIC SDA",
+               .gpio = TX6_I2C1_SDA_GPIO,
+               .gpio_base = GPIO3_BASE_ADDR,
+               .muxctl = IOMUXC_SW_MUX_CTL_PAD_EIM_DATA28,
+               .padctl = IOMUXC_SW_PAD_CTL_PAD_EIM_DATA28,
+               .sel_input = IOMUXC_SW_SEL_INPUT_PAD_EIM_DATA28,
+       },
+};
+
+static inline u32 __tx6_readl(void *addr,
+                       const char *fn, int ln)
+{
+       u32 val = readl(addr);
+       debug("%s@%d: read %08x from %p\n", fn, ln, val, addr);
+       return val;
+}
+#undef readl
+#define readl(a)       __tx6_readl((void *)(a), __func__, __LINE__)
+
+static inline void __tx6_writel(u32 val, void *addr,
+                               const char *fn, int ln)
+{
+       debug("%s@%d: writing %08x to %p\n", fn, ln, val, addr);
+       writel(val, addr);
+}
+#undef writel
+#define writel(v, a)   __tx6_writel(v, (void *)(a), __func__, __LINE__)
+
+static void tx6_i2c_recover(void)
+{
+       int i;
+       int bad = 0;
+       int failed = 0;
+#define MAX_TRIES 100
+
+       debug("Clearing I2C bus\n");
+
+       for (i = 0; i < ARRAY_SIZE(tx6_i2c_iomux_regs); i++) {
+               int gpio = tx6_i2c_iomux_regs[i].gpio;
+               u32 gpio_mask = 1 << (gpio % 32);
+
+               void *gpio_base = (void *)tx6_i2c_iomux_regs[i].gpio_base;
+
+               if ((readl(gpio_base + GPIO_PSR) & gpio_mask) == 0) {
+                       int retries = MAX_TRIES;
+
+                       bad++;
+                       printf("%s (GPIO%u_%u) is not HIGH\n",
+                               tx6_i2c_iomux_regs[i].label,
+                               gpio / 32 + 1, gpio % 32);
+                       writel(readl(gpio_base + GPIO_DR) | gpio_mask,
+                               gpio_base + GPIO_DR);
+                       writel(readl(gpio_base + GPIO_DIR) | gpio_mask,
+                               gpio_base + GPIO_DIR);
+                       writel(0x15, tx6_i2c_iomux_regs[i].muxctl);
+                       writel(0x0f079, tx6_i2c_iomux_regs[i].padctl);
+                       writel(I2C1_SEL_INPUT_VAL, tx6_i2c_iomux_regs[i].sel_input);
+                       if ((readl(gpio_base + GPIO_DR) & gpio_mask) == 0)
+                               hang();
+                       if ((readl(gpio_base + GPIO_DIR) & gpio_mask) == 0)
+                               hang();
+                       while ((readl(gpio_base + GPIO_PSR) & gpio_mask) == 0 &&
+                               retries-- > 0) {
+                               udelay(100);
+                       }
+                       writel(readl(gpio_base + GPIO_DIR) & ~gpio_mask,
+                               gpio_base + GPIO_DIR);
+
+                       if ((readl(gpio_base + GPIO_PSR) & gpio_mask) == 0) {
+                               printf("Failed to force %s (GPIO%u_%u) HIGH\n",
+                                       tx6_i2c_iomux_regs[i].label,
+                                       gpio / 32 + 1, gpio % 32);
+                               failed++;
+                       } else if (retries < MAX_TRIES) {
+                               printf("%s (GPIO%u_%u) forced HIGH after %u loops\n",
+                                       tx6_i2c_iomux_regs[i].label,
+                                       gpio / 32 + 1, gpio % 32,
+                                       MAX_TRIES - retries);
+                       }
+               } else {
+                       debug("%s (GPIO%u_%u) is HIGH\n",
+                               tx6_i2c_iomux_regs[i].label,
+                               gpio / 32 + 1, gpio % 32);
+               }
+       }
+       debug("Setting up I2C Pads\n");
+       imx_iomux_v3_setup_multiple_pads(tx6_i2c_pads,
+                                       ARRAY_SIZE(tx6_i2c_pads));
+       if (bad) {
+               if (failed)
+                       printf("I2C bus recovery FAILED\n");
+               else
+                       printf("I2C bus recovery succeeded\n");
+       }
+}
+
+#define pr_reg(b, n)   debug("%12s@%p=%08x\n", #n, (void *)(b) + (n), readl((b) + (n)))
+
+static inline void dump_regs(void)
+{
+       pr_reg(GPIO3_BASE_ADDR, GPIO_DR);
+       pr_reg(GPIO3_BASE_ADDR, GPIO_DIR);
+       pr_reg(GPIO3_BASE_ADDR, GPIO_PSR);
+}
+
 int board_init(void)
 {
        int ret;
@@ -1257,6 +1413,7 @@ static int tx6_pmic_probe(void)
 {
        int i;
 
+       tx6_i2c_recover();
        i2c_init_all();
 
        for (i = 0; i < ARRAY_SIZE(tx6_mod_revs); i++) {