summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--board/freescale/mx53_pcba/mx53_pcba.c274
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