summaryrefslogtreecommitdiff
path: root/board/freescale/mx53_smd
diff options
context:
space:
mode:
authorZhang Jiejing <jiejing.zhang@freescale.com>2011-12-02 15:08:57 +0800
committerZhang Jiejing <jiejing.zhang@freescale.com>2011-12-05 15:03:29 +0800
commitde0443f4d45852313f1231f48a83365e8f3f26bf (patch)
treef3d0052917516e92193ede8d1626e4e7495afa9a /board/freescale/mx53_smd
parente696f7dac56591a5616b397bc32a1d9bb57147ee (diff)
downloadu-boot-imx-de0443f4d45852313f1231f48a83365e8f3f26bf.zip
u-boot-imx-de0443f4d45852313f1231f48a83365e8f3f26bf.tar.gz
u-boot-imx-de0443f4d45852313f1231f48a83365e8f3f26bf.tar.bz2
ENGR00163704: MX5X: add i2c recovery function in board_lateinit.
This patch add a i2c bus recovery function, 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. To fix this issue: when we found SDA is low, we generate 8 clock to let device send data, then send a NAK, and STOP to finish this I2C transaction , after this the clock will be clean. Signed-off-by: Zhang Jiejing <jiejing.zhang@freescale.com>
Diffstat (limited to 'board/freescale/mx53_smd')
-rw-r--r--board/freescale/mx53_smd/mx53_smd.c264
1 files changed, 264 insertions, 0 deletions
diff --git a/board/freescale/mx53_smd/mx53_smd.c b/board/freescale/mx53_smd/mx53_smd.c
index 660dfc2..e493ea5 100644
--- a/board/freescale/mx53_smd/mx53_smd.c
+++ b/board/freescale/mx53_smd/mx53_smd.c
@@ -553,6 +553,267 @@ int i2c_write_da9053(uchar chip, uint addr, int alen, uchar *buf, int len)
}
}
+#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 mx53_i2c_gpio_scl_direction(int bus, int output)
+{
+ u32 reg;
+
+ switch (bus) {
+ case 1:
+ mxc_request_iomux(MX53_PIN_CSI0_D9, IOMUX_CONFIG_ALT1);
+ 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:
+ mxc_request_iomux(MX53_PIN_KEY_COL3, IOMUX_CONFIG_ALT1);
+ 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:
+ mxc_request_iomux(MX53_PIN_GPIO_3, IOMUX_CONFIG_ALT1);
+ 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 mx53_i2c_gpio_sda_direction(int bus, int output)
+{
+ u32 reg;
+
+ switch (bus) {
+ case 1:
+ mxc_request_iomux(MX53_PIN_CSI0_D8, IOMUX_CONFIG_ALT1);
+
+ reg = readl(GPIO5_BASE_ADDR + GPIO_GDIR);
+ if (output) {
+ mxc_iomux_set_pad(MX53_PIN_CSI0_D8,
+ PAD_CTL_ODE_OPENDRAIN_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU);
+ 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:
+ mxc_request_iomux(MX53_PIN_KEY_ROW3, IOMUX_CONFIG_ALT1);
+
+ mxc_iomux_set_pad(MX53_PIN_KEY_ROW3,
+ PAD_CTL_SRE_FAST |
+ PAD_CTL_ODE_OPENDRAIN_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU |
+ PAD_CTL_HYS_ENABLE);
+
+ 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:
+ mxc_request_iomux(MX53_PIN_GPIO_6, IOMUX_CONFIG_ALT1);
+ mxc_iomux_set_pad(MX53_PIN_GPIO_6,
+ PAD_CTL_PUE_PULL | PAD_CTL_PKE_ENABLE |
+ PAD_CTL_DRV_HIGH | PAD_CTL_360K_PD |
+ PAD_CTL_HYS_ENABLE);
+ 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 mx53_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 mx53_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 mx53_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++) {
+ mx53_i2c_gpio_sda_direction(bus, 0);
+
+ if (mx53_i2c_gpio_check_sda(bus) == 0) {
+ printf("i2c: I2C%d SDA is low, start i2c recovery...\n", bus);
+ mx53_i2c_gpio_scl_direction(bus, 1);
+ mx53_i2c_gpio_scl_set_level(bus, 1);
+ __udelay(10000);
+
+ for (i = 0; i < 9; i++) {
+ mx53_i2c_gpio_scl_set_level(bus, 1);
+ __udelay(5);
+ mx53_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.*/
+ mx53_i2c_gpio_sda_direction(bus, 1);
+ mx53_i2c_gpio_sda_set_level(bus, 1);
+ __udelay(1); /* Pull up SDA first */
+ mx53_i2c_gpio_scl_set_level(bus, 1);
+ __udelay(5); /* plus pervious 1 us */
+ mx53_i2c_gpio_scl_set_level(bus, 0);
+ __udelay(5);
+ mx53_i2c_gpio_sda_set_level(bus, 0);
+ __udelay(5);
+ mx53_i2c_gpio_scl_set_level(bus, 1);
+ __udelay(5);
+ /* Here: SCL is high, and SDA from low to high, it's a
+ * stop condition */
+ mx53_i2c_gpio_sda_set_level(bus, 1);
+ __udelay(5);
+
+ mx53_i2c_gpio_sda_direction(bus, 0);
+ if (mx53_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;
+}
+#endif
+
/* restore VUSB 2V5 active after suspend */
#define BUCKPERI_RESTORE_SW_STEP (0x55)
/* restore VUSB 2V5 power supply after suspend */
@@ -1159,6 +1420,9 @@ int board_late_init(void)
{
#ifdef CONFIG_I2C_MXC
setup_i2c(CONFIG_SYS_I2C_PORT);
+ /* first recovery I2C bus in case other device are reset
+ * during transcation */
+ i2c_bus_recovery();
/* Increase VDDGP voltage */
setup_pmic_voltages();
/* Switch to 1GHZ */