diff options
-rw-r--r-- | board/freescale/mx53_pcba/mx53_pcba.c | 274 |
1 files changed, 274 insertions, 0 deletions
diff --git a/board/freescale/mx53_pcba/mx53_pcba.c b/board/freescale/mx53_pcba/mx53_pcba.c index 2261321..5168e5d 100644 --- a/board/freescale/mx53_pcba/mx53_pcba.c +++ b/board/freescale/mx53_pcba/mx53_pcba.c @@ -65,6 +65,19 @@ DECLARE_GLOBAL_DATA_PTR; static u32 system_rev; static enum boot_device boot_dev; +/* 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 inline void setup_boot_device(void) { uint soc_sbmr = readl(SRC_BASE_ADDR + 0x4); @@ -776,10 +789,271 @@ int check_recovery_cmd_file(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 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 int board_late_init(void) { #ifdef CONFIG_I2C_MXC setup_i2c(CONFIG_SYS_I2C_PORT); + i2c_bus_recovery(); /* Increase VDDGP voltage */ setup_pmic_voltages(); #endif |