summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRobin Gong <B38343@freescale.com>2012-05-11 10:24:06 +0800
committerRobin Gong <B38343@freescale.com>2012-05-11 11:07:31 +0800
commit970754bfefbbd8604d1d54022c4e417b04d78e8f (patch)
treed835674f63a042b4a3d422481ef37df9912c7b3a
parentfef250219ce2a99f10f38b9018796fe2610cd3f9 (diff)
downloadu-boot-imx-970754bfefbbd8604d1d54022c4e417b04d78e8f.zip
u-boot-imx-970754bfefbbd8604d1d54022c4e417b04d78e8f.tar.gz
u-boot-imx-970754bfefbbd8604d1d54022c4e417b04d78e8f.tar.bz2
ENGR00182739 sabresd I2C:add i2c recovery function in board_lateinit
This issue have been found in mx53_smd(ENGR00163704), and found in sabresd if accessing pfuze while system reboot or reset, I2C bus will be blocked even if reboot,then pfuze will be failed to be probed, all device driver which use pfuze regulator will be impacted. In u-boot, we can check the SDA line low or high, if low, generate SCL and STOP signal to tell I2C device release I2C bus. Please check ENGR00163704 Signed-off-by: Robin Gong <B38343@freescale.com>
-rw-r--r--board/freescale/mx6q_sabresd/mx6q_sabresd.c290
1 files changed, 286 insertions, 4 deletions
diff --git a/board/freescale/mx6q_sabresd/mx6q_sabresd.c b/board/freescale/mx6q_sabresd/mx6q_sabresd.c
index 2944b50..d785fe2 100644
--- a/board/freescale/mx6q_sabresd/mx6q_sabresd.c
+++ b/board/freescale/mx6q_sabresd/mx6q_sabresd.c
@@ -322,6 +322,14 @@ void setup_lvds_poweron(void)
#endif
#ifdef CONFIG_I2C_MXC
+#define I2C1_SDA_GPIO5_26_BIT_MASK (1 << 26)
+#define I2C1_SCL_GPIO5_27_BIT_MASK (1 << 27)
+#define I2C2_SCL_GPIO4_12_BIT_MASK (1 << 12)
+#define I2C2_SDA_GPIO4_13_BIT_MASK (1 << 13)
+#define I2C3_SCL_GPIO1_3_BIT_MASK (1 << 3)
+#define I2C3_SDA_GPIO1_6_BIT_MASK (1 << 6)
+
+
static void setup_i2c(unsigned int module_base)
{
unsigned int reg;
@@ -392,12 +400,285 @@ static void setup_i2c(unsigned int module_base)
break;
}
}
+/* Note: udelay() is not accurate for i2c timing */
+static void __udelay(int time)
+{
+ int i, j;
+
+ for (i = 0; i < time; i++) {
+ for (j = 0; j < 200; j++) {
+ asm("nop");
+ asm("nop");
+ }
+ }
+}
+static void mx6q_i2c_gpio_scl_direction(int bus, int output)
+{
+ u32 reg;
+
+ switch (bus) {
+ case 1:
+#if defined CONFIG_MX6Q
+ mxc_iomux_v3_setup_pad(MX6Q_PAD_CSI0_DAT9__GPIO_5_27);
+#elif defined CONFIG_MX6DL
+ mxc_iomux_v3_setup_pad(MX6DL_PAD_CSI0_DAT9__GPIO_5_27);
+#endif
+ reg = readl(GPIO5_BASE_ADDR + GPIO_GDIR);
+ if (output)
+ reg |= I2C1_SCL_GPIO5_27_BIT_MASK;
+ else
+ reg &= ~I2C1_SCL_GPIO5_27_BIT_MASK;
+ writel(reg, GPIO5_BASE_ADDR + GPIO_GDIR);
+ break;
+ case 2:
+#if defined CONFIG_MX6Q
+ mxc_iomux_v3_setup_pad(MX6Q_PAD_KEY_COL3__GPIO_4_12);
+#elif defined CONFIG_MX6DL
+ mxc_iomux_v3_setup_pad(MX6DL_PAD_KEY_COL3__GPIO_4_12);
+#endif
+ reg = readl(GPIO4_BASE_ADDR + GPIO_GDIR);
+ if (output)
+ reg |= I2C2_SCL_GPIO4_12_BIT_MASK;
+ else
+ reg &= ~I2C2_SCL_GPIO4_12_BIT_MASK;
+ writel(reg, GPIO4_BASE_ADDR + GPIO_GDIR);
+ break;
+ case 3:
+#if defined CONFIG_MX6Q
+ mxc_iomux_v3_setup_pad(MX6Q_PAD_GPIO_3__GPIO_1_3);
+#elif defined CONFIG_MX6DL
+ mxc_iomux_v3_setup_pad(MX6DL_PAD_GPIO_3__GPIO_1_3);
+#endif
+ reg = readl(GPIO1_BASE_ADDR + GPIO_GDIR);
+ if (output)
+ reg |= I2C3_SCL_GPIO1_3_BIT_MASK;
+ else
+ reg &= I2C3_SCL_GPIO1_3_BIT_MASK;
+ writel(reg, GPIO1_BASE_ADDR + GPIO_GDIR);
+ break;
+ }
+}
+
+/* set 1 to output, sent 0 to input */
+static void mx6q_i2c_gpio_sda_direction(int bus, int output)
+{
+ u32 reg;
+
+ switch (bus) {
+ case 1:
+#if defined CONFIG_MX6Q
+ mxc_iomux_v3_setup_pad(MX6Q_PAD_CSI0_DAT8__GPIO_5_26);
+#elif defined CONFIG_MX6DL
+ mxc_iomux_v3_setup_pad(MX6DL_PAD_CSI0_DAT8__GPIO_5_26);
+#endif
+ reg = readl(GPIO5_BASE_ADDR + GPIO_GDIR);
+ if (output)
+ reg |= I2C1_SDA_GPIO5_26_BIT_MASK;
+ else
+ reg &= ~I2C1_SDA_GPIO5_26_BIT_MASK;
+ writel(reg, GPIO5_BASE_ADDR + GPIO_GDIR);
+ break;
+ case 2:
+#if defined CONFIG_MX6Q
+ mxc_iomux_v3_setup_pad(MX6Q_PAD_KEY_ROW3__GPIO_4_13);
+#elif defined CONFIG_MX6DL
+ mxc_iomux_v3_setup_pad(MX6DL_PAD_KEY_ROW3__GPIO_4_13);
+#endif
+ reg = readl(GPIO4_BASE_ADDR + GPIO_GDIR);
+ if (output)
+ reg |= I2C2_SDA_GPIO4_13_BIT_MASK;
+ else
+ reg &= ~I2C2_SDA_GPIO4_13_BIT_MASK;
+ writel(reg, GPIO4_BASE_ADDR + GPIO_GDIR);
+ case 3:
+#if defined CONFIG_MX6Q
+ mxc_iomux_v3_setup_pad(MX6Q_PAD_GPIO_6__GPIO_1_6);
+#elif defined CONFIG_MX6DL
+ mxc_iomux_v3_setup_pad(MX6DL_PAD_GPIO_6__GPIO_1_6);
+#endif
+ reg = readl(GPIO1_BASE_ADDR + GPIO_GDIR);
+ if (output)
+ reg |= I2C3_SDA_GPIO1_6_BIT_MASK;
+ else
+ reg &= ~I2C3_SDA_GPIO1_6_BIT_MASK;
+ writel(reg, GPIO1_BASE_ADDR + GPIO_GDIR);
+ default:
+ break;
+ }
+}
+
+/* set 1 to high 0 to low */
+static void mx6q_i2c_gpio_scl_set_level(int bus, int high)
+{
+ u32 reg;
+
+ switch (bus) {
+ case 1:
+ reg = readl(GPIO5_BASE_ADDR + GPIO_DR);
+ if (high)
+ reg |= I2C1_SCL_GPIO5_27_BIT_MASK;
+ else
+ reg &= ~I2C1_SCL_GPIO5_27_BIT_MASK;
+ writel(reg, GPIO5_BASE_ADDR + GPIO_DR);
+ break;
+ case 2:
+ reg = readl(GPIO4_BASE_ADDR + GPIO_DR);
+ if (high)
+ reg |= I2C2_SCL_GPIO4_12_BIT_MASK;
+ else
+ reg &= ~I2C2_SCL_GPIO4_12_BIT_MASK;
+ writel(reg, GPIO4_BASE_ADDR + GPIO_DR);
+ break;
+ case 3:
+ reg = readl(GPIO1_BASE_ADDR + GPIO_DR);
+ if (high)
+ reg |= I2C3_SCL_GPIO1_3_BIT_MASK;
+ else
+ reg &= ~I2C3_SCL_GPIO1_3_BIT_MASK;
+ writel(reg, GPIO1_BASE_ADDR + GPIO_DR);
+ break;
+ }
+}
+
+/* set 1 to high 0 to low */
+static void mx6q_i2c_gpio_sda_set_level(int bus, int high)
+{
+ u32 reg;
+
+ switch (bus) {
+ case 1:
+ reg = readl(GPIO5_BASE_ADDR + GPIO_DR);
+ if (high)
+ reg |= I2C1_SDA_GPIO5_26_BIT_MASK;
+ else
+ reg &= ~I2C1_SDA_GPIO5_26_BIT_MASK;
+ writel(reg, GPIO5_BASE_ADDR + GPIO_DR);
+ break;
+ case 2:
+ reg = readl(GPIO4_BASE_ADDR + GPIO_DR);
+ if (high)
+ reg |= I2C2_SDA_GPIO4_13_BIT_MASK;
+ else
+ reg &= ~I2C2_SDA_GPIO4_13_BIT_MASK;
+ writel(reg, GPIO4_BASE_ADDR + GPIO_DR);
+ break;
+ case 3:
+ reg = readl(GPIO1_BASE_ADDR + GPIO_DR);
+ if (high)
+ reg |= I2C3_SDA_GPIO1_6_BIT_MASK;
+ else
+ reg &= ~I2C3_SDA_GPIO1_6_BIT_MASK;
+ writel(reg, GPIO1_BASE_ADDR + GPIO_DR);
+ break;
+ }
+}
+
+static int mx6q_i2c_gpio_check_sda(int bus)
+{
+ u32 reg;
+ int result = 0;
+
+ switch (bus) {
+ case 1:
+ reg = readl(GPIO5_BASE_ADDR + GPIO_PSR);
+ result = !!(reg & I2C1_SDA_GPIO5_26_BIT_MASK);
+ break;
+ case 2:
+ reg = readl(GPIO4_BASE_ADDR + GPIO_PSR);
+ result = !!(reg & I2C2_SDA_GPIO4_13_BIT_MASK);
+ break;
+ case 3:
+ reg = readl(GPIO1_BASE_ADDR + GPIO_PSR);
+ result = !!(reg & I2C3_SDA_GPIO1_6_BIT_MASK);
+ break;
+ }
+
+ return result;
+}
+
+ /* Random reboot cause i2c SDA low issue:
+ * the i2c bus busy because some device pull down the I2C SDA
+ * line. This happens when Host is reading some byte from slave, and
+ * then host is reset/reboot. Since in this case, device is
+ * controlling i2c SDA line, the only thing host can do this give the
+ * clock on SCL and sending NAK, and STOP to finish this
+ * transaction.
+ *
+ * How to fix this issue:
+ * detect if the SDA was low on bus send 8 dummy clock, and 1
+ * clock + NAK, and STOP to finish i2c transaction the pending
+ * transfer.
+ */
+int i2c_bus_recovery(void)
+{
+ int i, bus, result = 0;
+
+ for (bus = 1; bus <= 3; bus++) {
+ mx6q_i2c_gpio_sda_direction(bus, 0);
+
+ if (mx6q_i2c_gpio_check_sda(bus) == 0) {
+ printf("i2c: I2C%d SDA is low, start i2c recovery...\n", bus);
+ mx6q_i2c_gpio_scl_direction(bus, 1);
+ mx6q_i2c_gpio_scl_set_level(bus, 1);
+ __udelay(10000);
+
+ for (i = 0; i < 9; i++) {
+ mx6q_i2c_gpio_scl_set_level(bus, 1);
+ __udelay(5);
+ mx6q_i2c_gpio_scl_set_level(bus, 0);
+ __udelay(5);
+ }
+
+ /* 9th clock here, the slave should already
+ release the SDA, we can set SDA as high to
+ a NAK.*/
+ mx6q_i2c_gpio_sda_direction(bus, 1);
+ mx6q_i2c_gpio_sda_set_level(bus, 1);
+ __udelay(1); /* Pull up SDA first */
+ mx6q_i2c_gpio_scl_set_level(bus, 1);
+ __udelay(5); /* plus pervious 1 us */
+ mx6q_i2c_gpio_scl_set_level(bus, 0);
+ __udelay(5);
+ mx6q_i2c_gpio_sda_set_level(bus, 0);
+ __udelay(5);
+ mx6q_i2c_gpio_scl_set_level(bus, 1);
+ __udelay(5);
+ /* Here: SCL is high, and SDA from low to high, it's a
+ * stop condition */
+ mx6q_i2c_gpio_sda_set_level(bus, 1);
+ __udelay(5);
+
+ mx6q_i2c_gpio_sda_direction(bus, 0);
+ if (mx6q_i2c_gpio_check_sda(bus) == 1)
+ printf("I2C%d Recovery success\n", bus);
+ else {
+ printf("I2C%d Recovery failed, I2C1 SDA still low!!!\n", bus);
+ result |= 1 << bus;
+ }
+ }
+
+ /* configure back to i2c */
+ switch (bus) {
+ case 1:
+ setup_i2c(I2C1_BASE_ADDR);
+ break;
+ case 2:
+ setup_i2c(I2C2_BASE_ADDR);
+ break;
+ case 3:
+ setup_i2c(I2C3_BASE_ADDR);
+ break;
+ }
+ }
+
+ return result;
+}
+
void setup_pmic_voltages(void)
{
unsigned char value = 0 ;
unsigned int val = 0;
-
i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
if (!i2c_probe(0x8)) {
if (i2c_read(0x8, 0, 1, &value, 1))
@@ -887,9 +1168,6 @@ int board_init(void)
setup_sata();
#endif
-#ifdef CONFIG_I2C_MXC
- setup_i2c(CONFIG_SYS_I2C_PORT);
-#endif
#ifdef CONFIG_VIDEO_MX5
/* Enable lvds power */
setup_lvds_poweron();
@@ -1039,7 +1317,11 @@ int check_recovery_cmd_file(void)
int board_late_init(void)
{
+ #ifdef CONFIG_I2C_MXC
+ setup_i2c(CONFIG_SYS_I2C_PORT);
+ i2c_bus_recovery();
setup_pmic_voltages();
+ #endif
return 0;
}