diff options
Diffstat (limited to 'board')
-rw-r--r-- | board/freescale/mx6q_sabresd/mx6q_sabresd.c | 3 | ||||
-rw-r--r-- | board/freescale/mx6sl_arm2/mx6sl_arm2.c | 313 |
2 files changed, 316 insertions, 0 deletions
diff --git a/board/freescale/mx6q_sabresd/mx6q_sabresd.c b/board/freescale/mx6q_sabresd/mx6q_sabresd.c index ceb6c46..daeff26 100644 --- a/board/freescale/mx6q_sabresd/mx6q_sabresd.c +++ b/board/freescale/mx6q_sabresd/mx6q_sabresd.c @@ -686,6 +686,9 @@ int i2c_bus_recovery(void) void setup_pmic_voltages(void) { unsigned char value = 0 ; + #if CONFIG_MX6_INTER_LDO_BYPASS + unsigned int val = 0; + #endif i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); if (!i2c_probe(0x8)) { if (i2c_read(0x8, 0, 1, &value, 1)) diff --git a/board/freescale/mx6sl_arm2/mx6sl_arm2.c b/board/freescale/mx6sl_arm2/mx6sl_arm2.c index 60a263f..13428cb 100644 --- a/board/freescale/mx6sl_arm2/mx6sl_arm2.c +++ b/board/freescale/mx6sl_arm2/mx6sl_arm2.c @@ -26,6 +26,7 @@ #include <asm/arch/mx6_pins.h> #include <asm/arch/mx6sl_pins.h> #include <asm/arch/iomux-v3.h> +#include <asm/arch/regs-anadig.h> #include <asm/errno.h> #ifdef CONFIG_MXC_FEC #include <miiphy.h> @@ -53,6 +54,10 @@ #include <recovery.h> #endif +#if CONFIG_I2C_MXC +#include <i2c.h> +#endif + DECLARE_GLOBAL_DATA_PTR; static u32 system_rev; @@ -685,6 +690,309 @@ static int setup_fec(void) } #endif +#ifdef CONFIG_I2C_MXC +#define I2C1_SCL_GPIO3_12_BIT_MASK (1 << 12) +#define I2C1_SDA_GPIO3_13_BIT_MASK (1 << 13) +#define I2C2_SCL_GPIO3_14_BIT_MASK (1 << 14) +#define I2C2_SDA_GPIO3_15_BIT_MASK (1 << 15) + + +static void setup_i2c(unsigned int module_base) +{ + unsigned int reg; + + switch (module_base) { + case I2C1_BASE_ADDR: + /* i2c1 SDA */ + mxc_iomux_v3_setup_pad(MX6SL_PAD_I2C1_SDA__I2C1_SDA); + /* i2c1 SCL */ + mxc_iomux_v3_setup_pad(MX6SL_PAD_I2C1_SCL__I2C1_SCL); + + /* Enable i2c clock */ + reg = readl(CCM_BASE_ADDR + CLKCTL_CCGR2); + reg |= 0xC0; + writel(reg, CCM_BASE_ADDR + CLKCTL_CCGR2); + + break; + case I2C2_BASE_ADDR: + /* i2c2 SDA */ + mxc_iomux_v3_setup_pad(MX6SL_PAD_I2C2_SDA__I2C2_SDA); + + /* i2c2 SCL */ + mxc_iomux_v3_setup_pad(MX6SL_PAD_I2C2_SCL__I2C2_SCL); + + /* Enable i2c clock */ + reg = readl(CCM_BASE_ADDR + CLKCTL_CCGR2); + reg |= 0x300; + writel(reg, CCM_BASE_ADDR + CLKCTL_CCGR2); + + break; + default: + printf("Invalid I2C base: 0x%x\n", 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 mx6sl_i2c_gpio_scl_direction(int bus, int output) +{ + u32 reg; + + switch (bus) { + case 1: + mxc_iomux_v3_setup_pad(MX6SL_PAD_I2C1_SCL__GPIO_3_12); + reg = readl(GPIO3_BASE_ADDR + GPIO_GDIR); + if (output) + reg |= I2C1_SCL_GPIO3_12_BIT_MASK; + else + reg &= ~I2C1_SCL_GPIO3_12_BIT_MASK; + writel(reg, GPIO3_BASE_ADDR + GPIO_GDIR); + break; + case 2: + mxc_iomux_v3_setup_pad(MX6SL_PAD_I2C2_SCL__GPIO_3_14); + reg = readl(GPIO3_BASE_ADDR + GPIO_GDIR); + if (output) + reg |= I2C2_SCL_GPIO3_14_BIT_MASK; + else + reg &= ~I2C2_SCL_GPIO3_14_BIT_MASK; + writel(reg, GPIO3_BASE_ADDR + GPIO_GDIR); + break; + } +} + +/* set 1 to output, sent 0 to input */ +static void mx6sl_i2c_gpio_sda_direction(int bus, int output) +{ + u32 reg; + + switch (bus) { + case 1: + mxc_iomux_v3_setup_pad(MX6SL_PAD_I2C1_SDA__GPIO_3_13); + reg = readl(GPIO3_BASE_ADDR + GPIO_GDIR); + if (output) + reg |= I2C1_SDA_GPIO3_13_BIT_MASK; + else + reg &= ~I2C1_SDA_GPIO3_13_BIT_MASK; + writel(reg, GPIO3_BASE_ADDR + GPIO_GDIR); + break; + case 2: + mxc_iomux_v3_setup_pad(MX6SL_PAD_I2C2_SDA__GPIO_3_15); + reg = readl(GPIO3_BASE_ADDR + GPIO_GDIR); + if (output) + reg |= I2C2_SDA_GPIO3_15_BIT_MASK; + else + reg &= ~I2C2_SDA_GPIO3_15_BIT_MASK; + writel(reg, GPIO3_BASE_ADDR + GPIO_GDIR); + break; + } +} + + +/* set 1 to high 0 to low */ +static void mx6sl_i2c_gpio_scl_set_level(int bus, int high) +{ + u32 reg; + + switch (bus) { + case 1: + reg = readl(GPIO3_BASE_ADDR + GPIO_DR); + if (high) + reg |= I2C1_SCL_GPIO3_12_BIT_MASK; + else + reg &= ~I2C1_SCL_GPIO3_12_BIT_MASK; + writel(reg, GPIO3_BASE_ADDR + GPIO_DR); + break; + case 2: + reg = readl(GPIO3_BASE_ADDR + GPIO_DR); + if (high) + reg |= I2C2_SCL_GPIO3_14_BIT_MASK; + else + reg &= ~I2C2_SCL_GPIO3_14_BIT_MASK; + writel(reg, GPIO3_BASE_ADDR + GPIO_DR); + break; + } +} + +/* set 1 to high 0 to low */ +static void mx6sl_i2c_gpio_sda_set_level(int bus, int high) +{ + u32 reg; + + switch (bus) { + case 1: + reg = readl(GPIO3_BASE_ADDR + GPIO_DR); + if (high) + reg |= I2C1_SDA_GPIO3_13_BIT_MASK; + else + reg &= ~I2C1_SDA_GPIO3_13_BIT_MASK; + writel(reg, GPIO3_BASE_ADDR + GPIO_DR); + break; + case 2: + reg = readl(GPIO3_BASE_ADDR + GPIO_DR); + if (high) + reg |= I2C2_SDA_GPIO3_15_BIT_MASK; + else + reg &= ~I2C2_SDA_GPIO3_15_BIT_MASK; + writel(reg, GPIO3_BASE_ADDR + GPIO_DR); + break; + } +} + + +static int mx6sl_i2c_gpio_check_sda(int bus) +{ + u32 reg; + int result = 0; + + switch (bus) { + case 1: + reg = readl(GPIO3_BASE_ADDR + GPIO_PSR); + result = !!(reg & I2C1_SDA_GPIO3_13_BIT_MASK); + break; + case 2: + reg = readl(GPIO3_BASE_ADDR + GPIO_PSR); + result = !!(reg & I2C2_SDA_GPIO3_15_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 <= 2; bus++) { + mx6sl_i2c_gpio_sda_direction(bus, 0); + + if (mx6sl_i2c_gpio_check_sda(bus) == 0) { + printf("i2c: I2C%d SDA is low, start i2c recovery...\n", bus); + mx6sl_i2c_gpio_scl_direction(bus, 1); + mx6sl_i2c_gpio_scl_set_level(bus, 1); + __udelay(10000); + + for (i = 0; i < 9; i++) { + mx6sl_i2c_gpio_scl_set_level(bus, 1); + __udelay(5); + mx6sl_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.*/ + mx6sl_i2c_gpio_sda_direction(bus, 1); + mx6sl_i2c_gpio_sda_set_level(bus, 1); + __udelay(1); /* Pull up SDA first */ + mx6sl_i2c_gpio_scl_set_level(bus, 1); + __udelay(5); /* plus pervious 1 us */ + mx6sl_i2c_gpio_scl_set_level(bus, 0); + __udelay(5); + mx6sl_i2c_gpio_sda_set_level(bus, 0); + __udelay(5); + mx6sl_i2c_gpio_scl_set_level(bus, 1); + __udelay(5); + /* Here: SCL is high, and SDA from low to high, it's a + * stop condition */ + mx6sl_i2c_gpio_sda_set_level(bus, 1); + __udelay(5); + + mx6sl_i2c_gpio_sda_direction(bus, 0); + if (mx6sl_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; + } + } + + return result; +} + + +void setup_pmic_voltages(void) +{ + unsigned char value = 0 ; + #if CONFIG_MX6_INTER_LDO_BYPASS + unsigned int val = 0; + #endif + i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + if (!i2c_probe(0x8)) { + if (i2c_read(0x8, 0, 1, &value, 1)) + printf("%s:i2c_read:error\n", __func__); + printf("Found PFUZE100! device id=%x\n", value); + #if CONFIG_MX6_INTER_LDO_BYPASS + /*VDDCORE 1.1V@800Mhz: SW1AB*/ + value = 0x20; + i2c_write(0x8, 0x20, 1, &value, 1); + + /*VDDSOC 1.2V : SW1C*/ + value = 0x24; + i2c_write(0x8, 0x2e, 1, &value, 1); + + /* Bypass the VDDSOC from Anatop */ + val = REG_RD(ANATOP_BASE_ADDR, HW_ANADIG_REG_CORE); + val &= ~BM_ANADIG_REG_CORE_REG2_TRG; + val |= BF_ANADIG_REG_CORE_REG2_TRG(0x1f); + REG_WR(ANATOP_BASE_ADDR, HW_ANADIG_REG_CORE, val); + + /* Bypass the VDDCORE from Anatop */ + val = REG_RD(ANATOP_BASE_ADDR, HW_ANADIG_REG_CORE); + val &= ~BM_ANADIG_REG_CORE_REG0_TRG; + val |= BF_ANADIG_REG_CORE_REG0_TRG(0x1f); + REG_WR(ANATOP_BASE_ADDR, HW_ANADIG_REG_CORE, val); + + /* Bypass the VDDPU from Anatop */ + val = REG_RD(ANATOP_BASE_ADDR, HW_ANADIG_REG_CORE); + val &= ~BM_ANADIG_REG_CORE_REG1_TRG; + val |= BF_ANADIG_REG_CORE_REG1_TRG(0x1f); + REG_WR(ANATOP_BASE_ADDR, HW_ANADIG_REG_CORE, val); + + /*clear PowerDown Enable bit of WDOG1_WMCR*/ + writew(0, WDOG1_BASE_ADDR + 0x08); + printf("hw_anadig_reg_core=%x\n", + REG_RD(ANATOP_BASE_ADDR, HW_ANADIG_REG_CORE)); + #endif + + } +} +#endif + int board_init(void) { mxc_iomux_v3_init((void *)IOMUXC_BASE_ADDR); @@ -710,6 +1018,11 @@ int board_init(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; } |