From 815a76f2ef08b700da611645c9329f632b681491 Mon Sep 17 00:00:00 2001 From: trem Date: Sat, 21 Sep 2013 18:13:34 +0200 Subject: i2c: fix init on generic board On generic board, the i2c init initialize only one bus. But the new i2c subsystem allow to manage severals i2c bus. So in the case, instead of initializing a bus, we just set the current i2c bus. The initialization will be done in the i2c command. Signed-off-by: Philippe Reynes diff --git a/common/board_f.c b/common/board_f.c index 0ada1af..f0664bc 100644 --- a/common/board_f.c +++ b/common/board_f.c @@ -249,7 +249,11 @@ void dram_init_banksize(void) static int init_func_i2c(void) { puts("I2C: "); +#ifdef CONFIG_SYS_I2C + i2c_init_all(); +#else i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); +#endif puts("ready\n"); return 0; } -- cgit v0.10.2 From fac9640895c3f778c3d2a33521dfd95bd5cba653 Mon Sep 17 00:00:00 2001 From: trem Date: Sat, 21 Sep 2013 18:13:35 +0200 Subject: i2c: mxc: move to new subsystem Signed-off-by: Philippe Reynes diff --git a/README b/README index 09662a4..7dc4f23 100644 --- a/README +++ b/README @@ -2003,6 +2003,17 @@ CBFS (Coreboot Filesystem) support - CONFIG_SYS_I2C_PPC4XX_CH0 activate hardware channel 0 - CONFIG_SYS_I2C_PPC4XX_CH1 activate hardware channel 1 + - drivers/i2c/i2c_mxc.c + - activate this driver with CONFIG_SYS_I2C_MXC + - define speed for bus 1 with CONFIG_SYS_MXC_I2C1_SPEED + - define slave for bus 1 with CONFIG_SYS_MXC_I2C1_SLAVE + - define speed for bus 2 with CONFIG_SYS_MXC_I2C2_SPEED + - define slave for bus 2 with CONFIG_SYS_MXC_I2C2_SLAVE + - define speed for bus 3 with CONFIG_SYS_MXC_I2C3_SPEED + - define slave for bus 3 with CONFIG_SYS_MXC_I2C3_SLAVE + If thoses defines are not set, default value is 100000 + for speed, and 0 for slave. + additional defines: CONFIG_SYS_NUM_I2C_BUSES diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 6bef254..fb3b128 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -94,7 +94,7 @@ void enable_usboh3_clk(bool enable) MXC_CCM_CCGR2_USBOH3_60M(cg)); } -#ifdef CONFIG_I2C_MXC +#ifdef CONFIG_SYS_I2C_MXC /* i2c_num can be from 0, to 1 for i.MX51 and 2 for i.MX53 */ int enable_i2c_clk(unsigned char enable, unsigned i2c_num) { diff --git a/arch/arm/cpu/armv7/mx6/clock.c b/arch/arm/cpu/armv7/mx6/clock.c index df11678..cf3a38e 100644 --- a/arch/arm/cpu/armv7/mx6/clock.c +++ b/arch/arm/cpu/armv7/mx6/clock.c @@ -48,7 +48,7 @@ void enable_usboh3_clk(unsigned char enable) } -#ifdef CONFIG_I2C_MXC +#ifdef CONFIG_SYS_I2C_MXC /* i2c_num can be from 0 - 2 */ int enable_i2c_clk(unsigned char enable, unsigned i2c_num) { diff --git a/arch/arm/imx-common/Makefile b/arch/arm/imx-common/Makefile index 727a052..6c78dd9 100644 --- a/arch/arm/imx-common/Makefile +++ b/arch/arm/imx-common/Makefile @@ -16,7 +16,7 @@ COBJS-y = iomux-v3.o endif ifeq ($(SOC),$(filter $(SOC),mx5 mx6)) COBJS-y += timer.o cpu.o speed.o -COBJS-$(CONFIG_I2C_MXC) += i2c-mxv7.o +COBJS-$(CONFIG_SYS_I2C_MXC) += i2c-mxv7.o endif ifeq ($(SOC),$(filter $(SOC),mx6 mxs)) COBJS-y += misc.o diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index df3092e..2395ded 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -14,7 +14,6 @@ COBJS-$(CONFIG_DRIVER_DAVINCI_I2C) += davinci_i2c.o COBJS-$(CONFIG_DW_I2C) += designware_i2c.o COBJS-$(CONFIG_I2C_MVTWSI) += mvtwsi.o COBJS-$(CONFIG_I2C_MV) += mv_i2c.o -COBJS-$(CONFIG_I2C_MXC) += mxc_i2c.o COBJS-$(CONFIG_I2C_MXS) += mxs_i2c.o COBJS-$(CONFIG_DRIVER_OMAP1510_I2C) += omap1510_i2c.o COBJS-$(CONFIG_DRIVER_OMAP24XX_I2C) += omap24xx_i2c.o @@ -28,6 +27,7 @@ COBJS-$(CONFIG_SH_SH7734_I2C) += sh_sh7734_i2c.o COBJS-$(CONFIG_SYS_I2C) += i2c_core.o COBJS-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o COBJS-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o +COBJS-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o COBJS-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o COBJS-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o COBJS-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 06ba4e3..595019b 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -102,6 +102,28 @@ static u16 i2c_clk_div[50][2] = { }; #endif + +#ifndef CONFIG_SYS_MXC_I2C1_SPEED +#define CONFIG_SYS_MXC_I2C1_SPEED 100000 +#endif +#ifndef CONFIG_SYS_MXC_I2C2_SPEED +#define CONFIG_SYS_MXC_I2C2_SPEED 100000 +#endif +#ifndef CONFIG_SYS_MXC_I2C3_SPEED +#define CONFIG_SYS_MXC_I2C3_SPEED 100000 +#endif + +#ifndef CONFIG_SYS_MXC_I2C1_SLAVE +#define CONFIG_SYS_MXC_I2C1_SLAVE 0 +#endif +#ifndef CONFIG_SYS_MXC_I2C2_SLAVE +#define CONFIG_SYS_MXC_I2C2_SLAVE 0 +#endif +#ifndef CONFIG_SYS_MXC_I2C3_SLAVE +#define CONFIG_SYS_MXC_I2C3_SLAVE 0 +#endif + + /* * Calculate and set proper clock divider */ @@ -153,21 +175,6 @@ static int bus_i2c_set_bus_speed(void *base, int speed) return 0; } -/* - * Get I2C Speed - */ -static unsigned int bus_i2c_get_bus_speed(void *base) -{ - struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base; - u8 clk_idx = readb(&i2c_regs->ifdr); - u8 clk_div; - - for (clk_div = 0; i2c_clk_div[clk_div][1] != clk_idx; clk_div++) - ; - - return mxc_get_clock(MXC_I2C_CLK) / i2c_clk_div[clk_div][0]; -} - #define ST_BUS_IDLE (0 | (I2SR_IBB << 8)) #define ST_BUS_BUSY (I2SR_IBB | (I2SR_IBB << 8)) #define ST_IIF (I2SR_IIF | (I2SR_IIF << 8)) @@ -410,20 +417,30 @@ struct sram_data { */ static struct sram_data __attribute__((section(".data"))) srdata; -void *get_base(void) -{ -#ifdef CONFIG_SYS_I2C_BASE -#ifdef CONFIG_I2C_MULTI_BUS - void *ret = srdata.i2c_data[srdata.curr_i2c_bus].base; - if (ret) - return ret; -#endif - return (void *)CONFIG_SYS_I2C_BASE; -#elif defined(CONFIG_I2C_MULTI_BUS) - return srdata.i2c_data[srdata.curr_i2c_bus].base; +static void * const i2c_bases[] = { +#if defined(CONFIG_MX25) + (void *)IMX_I2C_BASE, + (void *)IMX_I2C2_BASE, + (void *)IMX_I2C3_BASE +#elif defined(CONFIG_MX27) + (void *)IMX_I2C1_BASE, + (void *)IMX_I2C2_BASE +#elif defined(CONFIG_MX31) || defined(CONFIG_MX35) || \ + defined(CONFIG_MX51) || defined(CONFIG_MX53) || \ + defined(CONFIG_MX6) + (void *)I2C1_BASE_ADDR, + (void *)I2C2_BASE_ADDR, + (void *)I2C3_BASE_ADDR +#elif defined(CONFIG_VF610) + (void *)I2C0_BASE_ADDR #else - return srdata.i2c_data[0].base; +#error "architecture not supported" #endif +}; + +void *i2c_get_base(struct i2c_adapter *adap) +{ + return i2c_bases[adap->hwadapnr]; } static struct i2c_parms *i2c_get_parms(void *base) @@ -448,39 +465,26 @@ static int i2c_idle_bus(void *base) return 0; } -#ifdef CONFIG_I2C_MULTI_BUS -unsigned int i2c_get_bus_num(void) -{ - return srdata.curr_i2c_bus; -} - -int i2c_set_bus_num(unsigned bus_idx) -{ - if (bus_idx >= ARRAY_SIZE(srdata.i2c_data)) - return -1; - if (!srdata.i2c_data[bus_idx].base) - return -1; - srdata.curr_i2c_bus = bus_idx; - return 0; -} -#endif - -int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) +static int mxc_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, + int len) { - return bus_i2c_read(get_base(), chip, addr, alen, buf, len); + return bus_i2c_read(i2c_get_base(adap), chip, addr, alen, buffer, len); } -int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) +static int mxc_i2c_write(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, uint8_t *buffer, + int len) { - return bus_i2c_write(get_base(), chip, addr, alen, buf, len); + return bus_i2c_write(i2c_get_base(adap), chip, addr, alen, buffer, len); } /* * Test if a chip at a given address responds (probe the chip) */ -int i2c_probe(uchar chip) +static int mxc_i2c_probe(struct i2c_adapter *adap, uint8_t chip) { - return bus_i2c_write(get_base(), chip, 0, 0, NULL, 0); + return bus_i2c_write(i2c_get_base(adap), chip, 0, 0, NULL, 0); } void bus_i2c_init(void *base, int speed, int unused, @@ -510,23 +514,38 @@ void bus_i2c_init(void *base, int speed, int unused, /* * Init I2C Bus */ -void i2c_init(int speed, int unused) +static void mxc_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr) { - bus_i2c_init(get_base(), speed, unused, NULL, NULL); + bus_i2c_init(i2c_get_base(adap), speed, slaveaddr, NULL, NULL); } /* * Set I2C Speed */ -int i2c_set_bus_speed(unsigned int speed) +static uint mxc_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed) { - return bus_i2c_set_bus_speed(get_base(), speed); + return bus_i2c_set_bus_speed(i2c_get_base(adap), speed); } /* - * Get I2C Speed + * Register mxc i2c adapters */ -unsigned int i2c_get_bus_speed(void) -{ - return bus_i2c_get_bus_speed(get_base()); -} +U_BOOT_I2C_ADAP_COMPLETE(mxc0, mxc_i2c_init, mxc_i2c_probe, + mxc_i2c_read, mxc_i2c_write, + mxc_i2c_set_bus_speed, + CONFIG_SYS_MXC_I2C1_SPEED, + CONFIG_SYS_MXC_I2C1_SLAVE, 0) +U_BOOT_I2C_ADAP_COMPLETE(mxc1, mxc_i2c_init, mxc_i2c_probe, + mxc_i2c_read, mxc_i2c_write, + mxc_i2c_set_bus_speed, + CONFIG_SYS_MXC_I2C2_SPEED, + CONFIG_SYS_MXC_I2C2_SLAVE, 1) +#if defined(CONFIG_MX31) || defined(CONFIG_MX35) ||\ + defined(CONFIG_MX51) || defined(CONFIG_MX53) ||\ + defined(CONFIG_MX6) +U_BOOT_I2C_ADAP_COMPLETE(mxc2, mxc_i2c_init, mxc_i2c_probe, + mxc_i2c_read, mxc_i2c_write, + mxc_i2c_set_bus_speed, + CONFIG_SYS_MXC_I2C3_SPEED, + CONFIG_SYS_MXC_I2C3_SLAVE, 2) +#endif -- cgit v0.10.2 From b089d039b1971fc3abfe1d9bcebd0d35245fb110 Mon Sep 17 00:00:00 2001 From: trem Date: Sat, 21 Sep 2013 18:13:36 +0200 Subject: i2c: update config using mxc driver to new subsystem Signed-off-by: Philippe Reynes diff --git a/include/configs/apf27.h b/include/configs/apf27.h index e7e258f..1193013 100644 --- a/include/configs/apf27.h +++ b/include/configs/apf27.h @@ -321,11 +321,12 @@ */ #ifdef CONFIG_CMD_I2C -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE IMX_I2C1_BASE -#define CONFIG_SYS_I2C_SPEED 100000 /* 100 kHz */ -#define CONFIG_SYS_I2C_SLAVE 0x7F +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_MXC_I2C1_SPEED 100000 /* 100 kHz */ +#define CONFIG_SYS_MXC_I2C1_SLAVE 0x7F +#define CONFIG_SYS_MXC_I2C2_SPEED 100000 /* 100 kHz */ +#define CONFIG_SYS_MXC_I2C2_SLAVE 0x7F #define CONFIG_SYS_I2C_NOPROBES { } #ifdef CONFIG_CMD_EEPROM diff --git a/include/configs/flea3.h b/include/configs/flea3.h index 655df67..1781089 100644 --- a/include/configs/flea3.h +++ b/include/configs/flea3.h @@ -50,11 +50,10 @@ /* * Hardware drivers */ -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C3_BASE_ADDR -#define CONFIG_SYS_I2C_SPEED 100000 -#define CONFIG_SYS_I2C_SLAVE 0xfe +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 2 /* I2C3 */ +#define CONFIG_SYS_MXC_I2C3_SLAVE 0xfe #define CONFIG_MXC_SPI #define CONFIG_MXC_GPIO diff --git a/include/configs/imx31_phycore.h b/include/configs/imx31_phycore.h index 1e2b12c..6b99d1b 100644 --- a/include/configs/imx31_phycore.h +++ b/include/configs/imx31_phycore.h @@ -35,11 +35,10 @@ * Hardware drivers */ -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C2_BASE_ADDR +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 1 /* I2C2 */ #define CONFIG_SYS_I2C_CLK_OFFSET I2C2_CLK_OFFSET -#define CONFIG_SYS_I2C_SPEED 100000 #define CONFIG_MXC_UART #define CONFIG_MXC_UART_BASE UART1_BASE diff --git a/include/configs/m53evk.h b/include/configs/m53evk.h index 024d3a5..d7ca66b 100644 --- a/include/configs/m53evk.h +++ b/include/configs/m53evk.h @@ -161,10 +161,9 @@ * I2C */ #ifdef CONFIG_CMD_I2C -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C2_BASE_ADDR -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 1 /* I2C2 */ #endif /* diff --git a/include/configs/mx25pdk.h b/include/configs/mx25pdk.h index 543c415..256b3c1 100644 --- a/include/configs/mx25pdk.h +++ b/include/configs/mx25pdk.h @@ -111,10 +111,9 @@ /* I2C Configs */ #define CONFIG_CMD_I2C -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE IMX_I2C_BASE -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 0 /* I2C1 */ /* RTC */ #define CONFIG_RTC_IMXDI diff --git a/include/configs/mx35pdk.h b/include/configs/mx35pdk.h index 2f59104..2d1b800 100644 --- a/include/configs/mx35pdk.h +++ b/include/configs/mx35pdk.h @@ -40,10 +40,9 @@ /* * Hardware drivers */ -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C1_BASE_ADDR -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 0 /* I2C1 */ #define CONFIG_MXC_SPI #define CONFIG_MXC_GPIO diff --git a/include/configs/mx53ard.h b/include/configs/mx53ard.h index 7956083..b404247 100644 --- a/include/configs/mx53ard.h +++ b/include/configs/mx53ard.h @@ -44,10 +44,9 @@ /* I2C Configs */ #define CONFIG_CMD_I2C -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C2_BASE_ADDR -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 1 /* I2C2 */ /* MMC Configs */ #define CONFIG_FSL_ESDHC diff --git a/include/configs/mx53evk.h b/include/configs/mx53evk.h index fe5cf3c..d9c7df5 100644 --- a/include/configs/mx53evk.h +++ b/include/configs/mx53evk.h @@ -37,10 +37,9 @@ /* I2C Configs */ #define CONFIG_CMD_I2C -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C2_BASE_ADDR -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 1 /* I2C2 */ /* PMIC Configs */ #define CONFIG_POWER diff --git a/include/configs/mx53loco.h b/include/configs/mx53loco.h index bd2fa43..5f343b1 100644 --- a/include/configs/mx53loco.h +++ b/include/configs/mx53loco.h @@ -71,10 +71,9 @@ #define CONFIG_MXC_USB_FLAGS 0 /* I2C Configs */ -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C1_BASE_ADDR -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 0 /* I2C1 */ /* PMIC Controller */ #define CONFIG_POWER diff --git a/include/configs/mx53smd.h b/include/configs/mx53smd.h index 12b2c0d..2f7736d 100644 --- a/include/configs/mx53smd.h +++ b/include/configs/mx53smd.h @@ -34,10 +34,9 @@ /* I2C Configs */ #define CONFIG_CMD_I2C -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C2_BASE_ADDR -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 1 /* I2C2 */ /* MMC Configs */ #define CONFIG_FSL_ESDHC diff --git a/include/configs/mx6qsabreauto.h b/include/configs/mx6qsabreauto.h index 5530fc6..e0200c7 100644 --- a/include/configs/mx6qsabreauto.h +++ b/include/configs/mx6qsabreauto.h @@ -36,8 +36,8 @@ /* I2C Configs */ #define CONFIG_CMD_I2C -#define CONFIG_I2C_MULTI_BUS -#define CONFIG_I2C_MXC +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC #define CONFIG_SYS_I2C_SPEED 100000 #endif /* __MX6QSABREAUTO_CONFIG_H */ diff --git a/include/configs/nitrogen6x.h b/include/configs/nitrogen6x.h index 3454b86..d515e49 100644 --- a/include/configs/nitrogen6x.h +++ b/include/configs/nitrogen6x.h @@ -52,8 +52,8 @@ /* I2C Configs */ #define CONFIG_CMD_I2C -#define CONFIG_I2C_MULTI_BUS -#define CONFIG_I2C_MXC +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC #define CONFIG_SYS_I2C_SPEED 100000 /* OCOTP Configs */ diff --git a/include/configs/titanium.h b/include/configs/titanium.h index 077e25e..0769f07 100644 --- a/include/configs/titanium.h +++ b/include/configs/titanium.h @@ -41,8 +41,8 @@ /* I2C Configs */ #define CONFIG_CMD_I2C -#define CONFIG_I2C_MULTI_BUS -#define CONFIG_I2C_MXC +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC #define CONFIG_SYS_I2C_SPEED 100000 /* MMC Configs */ diff --git a/include/configs/vf610twr.h b/include/configs/vf610twr.h index 5a7a066..34861f6 100644 --- a/include/configs/vf610twr.h +++ b/include/configs/vf610twr.h @@ -70,10 +70,9 @@ /* I2C Configs */ #define CONFIG_CMD_I2C -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C0_BASE_ADDR -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 0 #define CONFIG_BOOTDELAY 3 diff --git a/include/configs/woodburn_common.h b/include/configs/woodburn_common.h index 5c442ad..8770e9c 100644 --- a/include/configs/woodburn_common.h +++ b/include/configs/woodburn_common.h @@ -45,10 +45,9 @@ /* * Hardware drivers */ -#define CONFIG_HARD_I2C -#define CONFIG_I2C_MXC -#define CONFIG_SYS_I2C_BASE I2C1_BASE_ADDR -#define CONFIG_SYS_I2C_SPEED 100000 +#define CONFIG_SYS_I2C +#define CONFIG_SYS_I2C_MXC +#define CONFIG_SYS_SPD_BUS_NUM 0 #define CONFIG_MXC_SPI #define CONFIG_MXC_GPIO -- cgit v0.10.2 From e66587495d75ad82c34e9f04af03d3c5741644d3 Mon Sep 17 00:00:00 2001 From: Michael Burr Date: Mon, 23 Sep 2013 22:35:45 +0000 Subject: i2c: Zynq: Support for TI PCA9548 bus multiplexer (Interface is not quite the same as Phillips PCA9547.) Signed-off-by: Michael Burr Cc: Heiko Schocher Cc: Michal Simek diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index d1072e8..b263562 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -138,6 +138,11 @@ static int i2c_mux_set(struct i2c_adapter *adap, int mux_id, int chip, return -1; buf = (uint8_t)((channel & 0x07) | (1 << 3)); break; + case I2C_MUX_PCA9548_ID: + if (channel > 7) + return -1; + buf = (uint8_t)(0x01 << channel); + break; default: printf("%s: wrong mux id: %d\n", __func__, mux_id); return -1; diff --git a/include/i2c.h b/include/i2c.h index 8fd17d1..c1be533 100644 --- a/include/i2c.h +++ b/include/i2c.h @@ -135,6 +135,8 @@ extern struct i2c_bus_hose i2c_bus[]; #define I2C_MUX_PCA9544 {I2C_MUX_PCA9544_ID, "PCA9544A"} #define I2C_MUX_PCA9547_ID 4 #define I2C_MUX_PCA9547 {I2C_MUX_PCA9547_ID, "PCA9547A"} +#define I2C_MUX_PCA9548_ID 5 +#define I2C_MUX_PCA9548 {I2C_MUX_PCA9548_ID, "PCA9548"} #endif #ifndef I2C_SOFT_DECLARATIONS -- cgit v0.10.2 From 1086bfa9f49127e40a92b1225af1ed5e41f8fa1c Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Fri, 27 Sep 2013 16:58:30 +0900 Subject: i2c: Add support for Renesas rcar This supports i2c controller for Renesas rcar. Signed-off-by: Hisashi Nakamura Signed-off-by: Nobuhiro Iwamatsu diff --git a/README b/README index 7dc4f23..e209c89 100644 --- a/README +++ b/README @@ -2014,6 +2014,20 @@ CBFS (Coreboot Filesystem) support If thoses defines are not set, default value is 100000 for speed, and 0 for slave. + - drivers/i2c/rcar_i2c.c: + - activate this driver with CONFIG_SYS_I2C_RCAR + - This driver adds 4 i2c buses + + - CONFIG_SYS_RCAR_I2C0_BASE for setting the register channel 0 + - CONFIG_SYS_RCAR_I2C0_SPEED for for the speed channel 0 + - CONFIG_SYS_RCAR_I2C1_BASE for setting the register channel 1 + - CONFIG_SYS_RCAR_I2C1_SPEED for for the speed channel 1 + - CONFIG_SYS_RCAR_I2C2_BASE for setting the register channel 2 + - CONFIG_SYS_RCAR_I2C2_SPEED for for the speed channel 2 + - CONFIG_SYS_RCAR_I2C3_BASE for setting the register channel 3 + - CONFIG_SYS_RCAR_I2C3_SPEED for for the speed channel 3 + - CONFIF_SYS_RCAR_I2C_NUM_CONTROLLERS for number of i2c buses + additional defines: CONFIG_SYS_NUM_I2C_BUSES diff --git a/drivers/i2c/Makefile b/drivers/i2c/Makefile index 2395ded..84a2754 100644 --- a/drivers/i2c/Makefile +++ b/drivers/i2c/Makefile @@ -29,6 +29,7 @@ COBJS-$(CONFIG_SYS_I2C_FSL) += fsl_i2c.o COBJS-$(CONFIG_SYS_I2C_FTI2C010) += fti2c010.o COBJS-$(CONFIG_SYS_I2C_MXC) += mxc_i2c.o COBJS-$(CONFIG_SYS_I2C_PPC4XX) += ppc4xx_i2c.o +COBJS-$(CONFIG_SYS_I2C_RCAR) += rcar_i2c.o COBJS-$(CONFIG_SYS_I2C_SOFT) += soft_i2c.o COBJS-$(CONFIG_SYS_I2C_TEGRA) += tegra_i2c.o COBJS-$(CONFIG_ZYNQ_I2C) += zynq_i2c.o diff --git a/drivers/i2c/rcar_i2c.c b/drivers/i2c/rcar_i2c.c new file mode 100644 index 0000000..ba2cadb --- /dev/null +++ b/drivers/i2c/rcar_i2c.c @@ -0,0 +1,288 @@ +/* + * drivers/i2c/rcar_i2c.c + * + * Copyright (C) 2013 Renesas Electronics Corporation + * Copyright (C) 2013 Nobuhiro Iwamatsu + * + * SPDX-License-Identifier: GPL-2.0 + */ + +#include +#include +#include + +DECLARE_GLOBAL_DATA_PTR; + +struct rcar_i2c { + u32 icscr; + u32 icmcr; + u32 icssr; + u32 icmsr; + u32 icsier; + u32 icmier; + u32 icccr; + u32 icsar; + u32 icmar; + u32 icrxdtxd; + u32 icccr2; + u32 icmpr; + u32 ichpr; + u32 iclpr; +}; + +#define MCR_MDBS 0x80 /* non-fifo mode switch */ +#define MCR_FSCL 0x40 /* override SCL pin */ +#define MCR_FSDA 0x20 /* override SDA pin */ +#define MCR_OBPC 0x10 /* override pins */ +#define MCR_MIE 0x08 /* master if enable */ +#define MCR_TSBE 0x04 +#define MCR_FSB 0x02 /* force stop bit */ +#define MCR_ESG 0x01 /* en startbit gen. */ + +#define MSR_MASK 0x7f +#define MSR_MNR 0x40 /* nack received */ +#define MSR_MAL 0x20 /* arbitration lost */ +#define MSR_MST 0x10 /* sent a stop */ +#define MSR_MDE 0x08 +#define MSR_MDT 0x04 +#define MSR_MDR 0x02 +#define MSR_MAT 0x01 /* slave addr xfer done */ + +static const struct rcar_i2c *i2c_dev[CONFIF_SYS_RCAR_I2C_NUM_CONTROLLERS] = { + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C0_BASE, + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C1_BASE, + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C2_BASE, + (struct rcar_i2c *)CONFIG_SYS_RCAR_I2C3_BASE, +}; + +static void rcar_i2c_raw_rw_common(struct rcar_i2c *dev, u8 chip, uint addr) +{ + /* set slave address */ + writel(chip << 1, &dev->icmar); + /* set register address */ + writel(addr, &dev->icrxdtxd); + /* clear status */ + writel(0, &dev->icmsr); + /* start master send */ + writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr); + + while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDE)) + != (MSR_MAT | MSR_MDE)) + udelay(10); + + /* clear ESG */ + writel(MCR_MDBS | MCR_MIE, &dev->icmcr); + /* start SCLclk */ + writel(~(MSR_MAT | MSR_MDE), &dev->icmsr); + + while (!(readl(&dev->icmsr) & MSR_MDE)) + udelay(10); +} + +static void rcar_i2c_raw_rw_finish(struct rcar_i2c *dev) +{ + while (!(readl(&dev->icmsr) & MSR_MST)) + udelay(10); + + writel(0, &dev->icmcr); +} + +static int +rcar_i2c_raw_write(struct rcar_i2c *dev, u8 chip, uint addr, u8 *val, int size) +{ + rcar_i2c_raw_rw_common(dev, chip, addr); + + /* set send date */ + writel(*val, &dev->icrxdtxd); + /* start SCLclk */ + writel(~MSR_MDE, &dev->icmsr); + + while (!(readl(&dev->icmsr) & MSR_MDE)) + udelay(10); + + /* set stop condition */ + writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr); + /* start SCLclk */ + writel(~MSR_MDE, &dev->icmsr); + + rcar_i2c_raw_rw_finish(dev); + + return 0; +} + +static u8 +rcar_i2c_raw_read(struct rcar_i2c *dev, u8 chip, uint addr) +{ + u8 ret; + + rcar_i2c_raw_rw_common(dev, chip, addr); + + /* set slave address, receive */ + writel((chip << 1) | 1, &dev->icmar); + /* start master receive */ + writel(MCR_MDBS | MCR_MIE | MCR_ESG, &dev->icmcr); + + while ((readl(&dev->icmsr) & (MSR_MAT | MSR_MDE)) + != (MSR_MAT | MSR_MDE)) + udelay(10); + + /* clear ESG */ + writel(MCR_MDBS | MCR_MIE, &dev->icmcr); + /* prepare stop condition */ + writel(MCR_MDBS | MCR_MIE | MCR_FSB, &dev->icmcr); + /* start SCLclk */ + writel(~(MSR_MAT | MSR_MDR), &dev->icmsr); + + while (!(readl(&dev->icmsr) & MSR_MDR)) + udelay(10); + + /* get receive data */ + ret = (u8)readl(&dev->icrxdtxd); + /* start SCLclk */ + writel(~MSR_MDR, &dev->icmsr); + + rcar_i2c_raw_rw_finish(dev); + + return ret; +} + +/* + * SCL = iicck / (20 + SCGD * 8 + F[(ticf + tr + intd) * iicck]) + * iicck : I2C internal clock < 20 MHz + * ticf : I2C SCL falling time: 35 ns + * tr : I2C SCL rising time: 200 ns + * intd : LSI internal delay: I2C0: 50 ns I2C1-3: 5 + * F[n] : n rounded up to an integer + */ +static u32 rcar_clock_gen(int i2c_no, u32 bus_speed) +{ + u32 iicck, f, scl, scgd; + u32 intd = 5; + + int bit = 0, cdf_width = 3; + for (bit = 0; bit < (1 << cdf_width); bit++) { + iicck = CONFIG_HP_CLK_FREQ / (1 + bit); + if (iicck < 20000000) + break; + } + + if (bit > (1 << cdf_width)) { + puts("rcar-i2c: Can not get CDF\n"); + return 0; + } + + if (i2c_no == 0) + intd = 50; + + f = (35 + 200 + intd) * (iicck / 1000000000); + + for (scgd = 0; scgd < 0x40; scgd++) { + scl = iicck / (20 + (scgd * 8) + f); + if (scl <= bus_speed) + break; + } + + if (scgd > 0x40) { + puts("rcar-i2c: Can not get SDGB\n"); + return 0; + } + + debug("%s: scl: %d\n", __func__, scl); + debug("%s: bit %x\n", __func__, bit); + debug("%s: scgd %x\n", __func__, scgd); + debug("%s: iccr %x\n", __func__, (scgd << (cdf_width) | bit)); + + return scgd << (cdf_width) | bit; +} + +static void +rcar_i2c_init(struct i2c_adapter *adap, int speed, int slaveadd) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + u32 icccr = 0; + + /* No i2c support prior to relocation */ + if (!(gd->flags & GD_FLG_RELOC)) + return; + + /* + * reset slave mode. + * slave mode is not used on this driver + */ + writel(0, &dev->icsier); + writel(0, &dev->icsar); + writel(0, &dev->icscr); + writel(0, &dev->icssr); + + /* reset master mode */ + writel(0, &dev->icmier); + writel(0, &dev->icmcr); + writel(0, &dev->icmsr); + writel(0, &dev->icmar); + + icccr = rcar_clock_gen(adap->hwadapnr, adap->speed); + if (icccr == 0) + puts("I2C: Init failed\n"); + else + writel(icccr, &dev->icccr); +} + +static int rcar_i2c_read(struct i2c_adapter *adap, uint8_t chip, + uint addr, int alen, u8 *data, int len) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + int i; + + for (i = 0; i < len; i++) + data[i] = rcar_i2c_raw_read(dev, chip, addr + i); + + return 0; +} + +static int rcar_i2c_write(struct i2c_adapter *adap, uint8_t chip, uint addr, + int alen, u8 *data, int len) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + return rcar_i2c_raw_write(dev, chip, addr, data, len); +} + +static int +rcar_i2c_probe(struct i2c_adapter *adap, u8 dev) +{ + return rcar_i2c_read(adap, dev, 0, 0, NULL, 0); +} + +static unsigned int rcar_i2c_set_bus_speed(struct i2c_adapter *adap, + unsigned int speed) +{ + struct rcar_i2c *dev = (struct rcar_i2c *)i2c_dev[adap->hwadapnr]; + u32 icccr; + int ret = 0; + + rcar_i2c_raw_rw_finish(dev); + + icccr = rcar_clock_gen(adap->hwadapnr, speed); + if (icccr == 0) { + puts("I2C: Init failed\n"); + ret = -1; + } else { + writel(icccr, &dev->icccr); + } + return ret; +} + +/* + * Register RCAR i2c adapters + */ +U_BOOT_I2C_ADAP_COMPLETE(rcar_0, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C0_SPEED, 0, 0) +U_BOOT_I2C_ADAP_COMPLETE(rcar_1, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C1_SPEED, 0, 1) +U_BOOT_I2C_ADAP_COMPLETE(rcar_2, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C2_SPEED, 0, 2) +U_BOOT_I2C_ADAP_COMPLETE(rcar_3, rcar_i2c_init, rcar_i2c_probe, rcar_i2c_read, + rcar_i2c_write, rcar_i2c_set_bus_speed, + CONFIG_SYS_RCAR_I2C3_SPEED, 0, 3) -- cgit v0.10.2 From 13c2433ccb4d9a5efb95ad953c2b3a0eb1909988 Mon Sep 17 00:00:00 2001 From: Heiko Schocher Date: Fri, 4 Oct 2013 07:36:34 +0200 Subject: i2c, core: optimze i2c_set_bus_num() check first, if we are on the bus, we want to enable. If so, return immediately, do not calc max adapter number, nor check other things. Signed-off-by: Heiko Schocher Cc: Lukasz Majewski Acked-by: Lukasz Majewski diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index b263562..fe341cd 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -283,20 +283,22 @@ unsigned int i2c_get_bus_num(void) */ int i2c_set_bus_num(unsigned int bus) { - int max = ll_entry_count(struct i2c_adapter, i2c); + int max; + + if ((bus == I2C_BUS) && (I2C_ADAP->init_done > 0)) + return 0; - if (I2C_ADAPTER(bus) >= max) { - printf("Error, wrong i2c adapter %d max %d possible\n", - I2C_ADAPTER(bus), max); - return -2; - } #ifndef CONFIG_SYS_I2C_DIRECT_BUS if (bus >= CONFIG_SYS_NUM_I2C_BUSES) return -1; #endif - if ((bus == I2C_BUS) && (I2C_ADAP->init_done > 0)) - return 0; + max = ll_entry_count(struct i2c_adapter, i2c); + if (I2C_ADAPTER(bus) >= max) { + printf("Error, wrong i2c adapter %d max %d possible\n", + I2C_ADAPTER(bus), max); + return -2; + } #ifndef CONFIG_SYS_I2C_DIRECT_BUS i2c_mux_disconnet_all(); -- cgit v0.10.2 From b55b8eef35f9ca04cf790381bab14510278b08a4 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Fri, 11 Oct 2013 16:23:54 +0900 Subject: i2c: sh_i2c: Avoid using I2C prior to relocation If user uses the I2C in before the relocation, board of sh and rmobile will not start. This will solve this problem. Signed-off-by: Nobuhiro Iwamatsu diff --git a/drivers/i2c/sh_i2c.c b/drivers/i2c/sh_i2c.c index 58f8bf1..808202c 100644 --- a/drivers/i2c/sh_i2c.c +++ b/drivers/i2c/sh_i2c.c @@ -1,6 +1,6 @@ /* - * Copyright (C) 2011 Renesas Solutions Corp. - * Copyright (C) 2011 Nobuhiro Iwamatsu + * Copyright (C) 2011, 2013 Renesas Solutions Corp. + * Copyright (C) 2011, 2013 Nobuhiro Iwamatsu * * SPDX-License-Identifier: GPL-2.0+ */ @@ -8,6 +8,8 @@ #include #include +DECLARE_GLOBAL_DATA_PTR; + /* Every register is 32bit aligned, but only 8bits in size */ #define ureg(name) u8 name; u8 __pad_##name##0; u16 __pad_##name##1; struct sh_i2c { @@ -240,6 +242,10 @@ void i2c_init(int speed, int slaveaddr) { int num, denom, tmp; + /* No i2c support prior to relocation */ + if (!(gd->flags & GD_FLG_RELOC)) + return; + #ifdef CONFIG_I2C_MULTI_BUS current_bus = 0; #endif -- cgit v0.10.2 From 83d271b5ffc19e98098861dd21e940cf53545a43 Mon Sep 17 00:00:00 2001 From: "Jens Scharsig (BuS Elektronik)" Date: Mon, 14 Oct 2013 10:27:38 +0200 Subject: Fix: nommu I2C adapter relocation error NoMMU systems have a access violation problem with i2c_reloc_fixup. Blame for it is a double relocation of the adapter itself. The i2c_adap_p is already relocated, if i2c_reloc_fixup is called. This patch removes the relocation of i2c_adap_p from i2c_reloc_fixup to fix this. Signed-off-by: Jens Scharsig (BuS Elektronik) diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index fe341cd..b067b7c 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -53,10 +53,6 @@ void i2c_reloc_fixup(void) return; for (i = 0; i < max; i++) { - /* adapter itself */ - addr = (unsigned long)i2c_adap_p; - addr += gd->reloc_off; - i2c_adap_p = (struct i2c_adapter *)addr; /* i2c_init() */ addr = (unsigned long)i2c_adap_p->init; addr += gd->reloc_off; -- cgit v0.10.2 From e4e2402071e8052eab7fb10ecfbdec01310ac06f Mon Sep 17 00:00:00 2001 From: Naveen Krishna Ch Date: Tue, 15 Oct 2013 16:01:43 +0530 Subject: exynos: i2c: Fix i2c driver to handle NACKs properly The Exynos5 i2c driver does not handle NACKs properly. This change: - fixes the NACK processing problem (do not continue transaction if address cycle was NACKed) - eliminates a fair amount of duplicate code Signed-off-by: Vadim Bendebury Reviewed-by: Simon Glass Signed-off-by: Naveen Krishna Chatradhi diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index cd09c78..c65360d 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -43,7 +43,7 @@ #define I2C_START_STOP 0x20 /* START / STOP */ #define I2C_TXRX_ENA 0x10 /* I2C Tx/Rx enable */ -#define I2C_TIMEOUT 1 /* 1 second */ +#define I2C_TIMEOUT_MS 1000 /* 1 second */ /* @@ -84,22 +84,26 @@ static void SetI2CSCL(int x) } #endif +/* + * Wait til the byte transfer is completed. + * + * @param i2c- pointer to the appropriate i2c register bank. + * @return I2C_OK, if transmission was ACKED + * I2C_NACK, if transmission was NACKED + * I2C_NOK_TIMEOUT, if transaction did not complete in I2C_TIMEOUT_MS + */ + static int WaitForXfer(struct s3c24x0_i2c *i2c) { - int i; + ulong start_time = get_timer(0); - i = I2C_TIMEOUT * 10000; - while (!(readl(&i2c->iiccon) & I2CCON_IRPND) && (i > 0)) { - udelay(100); - i--; - } + do { + if (readl(&i2c->iiccon) & I2CCON_IRPND) + return (readl(&i2c->iicstat) & I2CSTAT_NACK) ? + I2C_NACK : I2C_OK; + } while (get_timer(start_time) < I2C_TIMEOUT_MS); - return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT; -} - -static int IsACK(struct s3c24x0_i2c *i2c) -{ - return !(readl(&i2c->iicstat) & I2CSTAT_NACK); + return I2C_NOK_TOUT; } static void ReadWriteByte(struct s3c24x0_i2c *i2c) @@ -180,21 +184,27 @@ unsigned int i2c_get_bus_num(void) void i2c_init(int speed, int slaveadd) { + int i; struct s3c24x0_i2c *i2c; #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); #endif - int i; + ulong start_time = get_timer(0); /* By default i2c channel 0 is the current bus */ g_current_bus = 0; i2c = get_base_i2c(); - /* wait for some time to give previous transfer a chance to finish */ - i = I2C_TIMEOUT * 1000; - while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) { - udelay(1000); - i--; + /* + * In case the previous transfer is still going, wait to give it a + * chance to finish. + */ + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) { + printf("%s: I2C bus busy for %p\n", __func__, + &i2c->iicstat); + return; + } } #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) @@ -260,7 +270,8 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c, unsigned char data[], unsigned short data_len) { - int i, result; + int i = 0, result; + ulong start_time = get_timer(0); if (data == 0 || data_len == 0) { /*Don't support data transfer of no length or to address 0 */ @@ -268,128 +279,78 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c, return I2C_NOK; } - /* Check I2C bus idle */ - i = I2C_TIMEOUT * 1000; - while ((readl(&i2c->iicstat) & I2CSTAT_BSY) && (i > 0)) { - udelay(1000); - i--; + while (readl(&i2c->iicstat) & I2CSTAT_BSY) { + if (get_timer(start_time) > I2C_TIMEOUT_MS) + return I2C_NOK_TOUT; } - if (readl(&i2c->iicstat) & I2CSTAT_BSY) - return I2C_NOK_TOUT; - writel(readl(&i2c->iiccon) | I2CCON_ACKGEN, &i2c->iiccon); - result = I2C_OK; - switch (cmd_type) { - case I2C_WRITE: - if (addr && addr_len) { - writel(chip, &i2c->iicds); - /* send START */ - writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, - &i2c->iicstat); - i = 0; - while ((i < addr_len) && (result == I2C_OK)) { - result = WaitForXfer(i2c); - writel(addr[i], &i2c->iicds); - ReadWriteByte(i2c); - i++; - } - i = 0; - while ((i < data_len) && (result == I2C_OK)) { - result = WaitForXfer(i2c); - writel(data[i], &i2c->iicds); - ReadWriteByte(i2c); - i++; - } - } else { - writel(chip, &i2c->iicds); - /* send START */ - writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, - &i2c->iicstat); - i = 0; - while ((i < data_len) && (result == I2C_OK)) { - result = WaitForXfer(i2c); - writel(data[i], &i2c->iicds); - ReadWriteByte(i2c); - i++; - } + /* Get the slave chip address going */ + writel(chip, &i2c->iicds); + if ((cmd_type == I2C_WRITE) || (addr && addr_len)) + writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + else + writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, + &i2c->iicstat); + + /* Wait for chip address to transmit. */ + result = WaitForXfer(i2c); + if (result != I2C_OK) + goto bailout; + + /* If register address needs to be transmitted - do it now. */ + if (addr && addr_len) { + while ((i < addr_len) && (result == I2C_OK)) { + writel(addr[i++], &i2c->iicds); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); } + i = 0; + if (result != I2C_OK) + goto bailout; + } - if (result == I2C_OK) + switch (cmd_type) { + case I2C_WRITE: + while ((i < data_len) && (result == I2C_OK)) { + writel(data[i++], &i2c->iicds); + ReadWriteByte(i2c); result = WaitForXfer(i2c); - - /* send STOP */ - writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); - ReadWriteByte(i2c); + } break; case I2C_READ: if (addr && addr_len) { + /* + * Register address has been sent, now send slave chip + * address again to start the actual read transaction. + */ writel(chip, &i2c->iicds); - /* send START */ - writel(I2C_MODE_MT | I2C_TXRX_ENA | I2C_START_STOP, - &i2c->iicstat); - result = WaitForXfer(i2c); - if (IsACK(i2c)) { - i = 0; - while ((i < addr_len) && (result == I2C_OK)) { - writel(addr[i], &i2c->iicds); - ReadWriteByte(i2c); - result = WaitForXfer(i2c); - i++; - } - - writel(chip, &i2c->iicds); - /* resend START */ - writel(I2C_MODE_MR | I2C_TXRX_ENA | - I2C_START_STOP, &i2c->iicstat); - ReadWriteByte(i2c); - result = WaitForXfer(i2c); - i = 0; - while ((i < data_len) && (result == I2C_OK)) { - /* disable ACK for final READ */ - if (i == data_len - 1) - writel(readl(&i2c->iiccon) - & ~I2CCON_ACKGEN, - &i2c->iiccon); - ReadWriteByte(i2c); - result = WaitForXfer(i2c); - data[i] = readl(&i2c->iicds); - i++; - } - } else { - result = I2C_NACK; - } - - } else { - writel(chip, &i2c->iicds); - /* send START */ + + /* Generate a re-START. */ writel(I2C_MODE_MR | I2C_TXRX_ENA | I2C_START_STOP, &i2c->iicstat); + ReadWriteByte(i2c); result = WaitForXfer(i2c); - if (IsACK(i2c)) { - i = 0; - while ((i < data_len) && (result == I2C_OK)) { - /* disable ACK for final READ */ - if (i == data_len - 1) - writel(readl(&i2c->iiccon) & - ~I2CCON_ACKGEN, - &i2c->iiccon); - ReadWriteByte(i2c); - result = WaitForXfer(i2c); - data[i] = readl(&i2c->iicds); - i++; - } - } else { - result = I2C_NACK; - } + if (result != I2C_OK) + goto bailout; } - /* send STOP */ - writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); - ReadWriteByte(i2c); + while ((i < data_len) && (result == I2C_OK)) { + /* disable ACK for final READ */ + if (i == data_len - 1) + writel(readl(&i2c->iiccon) + & ~I2CCON_ACKGEN, + &i2c->iiccon); + ReadWriteByte(i2c); + result = WaitForXfer(i2c); + data[i++] = readl(&i2c->iicds); + } + if (result == I2C_NACK) + result = I2C_OK; /* Normal terminated read. */ break; default: @@ -398,6 +359,11 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c, break; } +bailout: + /* Send STOP. */ + writel(I2C_MODE_MR | I2C_TXRX_ENA, &i2c->iicstat); + ReadWriteByte(i2c); + return result; } -- cgit v0.10.2 From 940dd1624255baac928dbac5dc2e3147477ba02f Mon Sep 17 00:00:00 2001 From: Simon Glass Date: Tue, 15 Oct 2013 16:02:10 +0530 Subject: exynos: i2c: Change FDT bus setup code to enumerate ports correctly At present the i2c ports are enumerated in a strange way - the fdtdec_find_aliases_for_id() function is used, but then the ID returned is ignored and the ports are renumbered. The effect is the same provided that the device tree has the ports in the same order, or uses aliases, and has no gaps, but it is not correct. Adjust the code to use the function as intended. This will allows device tree aliases to change the device order if required. As a result, the i2c_busses variable is dropped. We can't be sure that there are no 'holes' in the list of buses, so must check the whole array. Note: it seems that non-FDT operation is now broken in this drive and will need to be reinstated for upstream. Signed-off-by: Simon Glass Reviewed-on: https://gerrit.chromium.org/gerrit/59369 Signed-off-by: Naveen Krishna Chatradhi diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index c65360d..0e6f241 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -52,7 +52,6 @@ */ static unsigned int g_current_bus __attribute__((section(".data"))); #ifdef CONFIG_OF_CONTROL -static int i2c_busses __attribute__((section(".data"))); static struct s3c24x0_i2c_bus i2c_bus[CONFIG_MAX_I2C_NUM] __attribute__((section(".data"))); #endif @@ -164,8 +163,8 @@ int i2c_set_bus_num(unsigned int bus) { struct s3c24x0_i2c *i2c; - if ((bus < 0) || (bus >= CONFIG_MAX_I2C_NUM)) { - debug("Bad bus: %d\n", bus); + i2c_bus = get_bus(bus); + if (!i2c_bus) return -1; } @@ -483,19 +482,31 @@ void board_i2c_init(const void *blob) if (node <= 0) continue; bus = &i2c_bus[i]; + bus->active = true; bus->regs = (struct s3c24x0_i2c *) fdtdec_get_addr(blob, node, "reg"); bus->id = pinmux_decode_periph_id(blob, node); bus->node = node; - bus->bus_num = i2c_busses++; + bus->bus_num = i; exynos_pinmux_config(bus->id, 0); } } +/** + * Get a pointer to the given bus index + * + * @bus_idx: Bus index to look up + * @return pointer to bus, or NULL if invalid or not available + */ static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx) { - if (bus_idx < i2c_busses) - return &i2c_bus[bus_idx]; + if (bus_idx < ARRAY_SIZE(i2c_bus)) { + struct s3c24x0_i2c_bus *bus; + + bus = &i2c_bus[bus_idx]; + if (bus->active) + return bus; + } debug("Undefined bus: %d\n", bus_idx); return NULL; @@ -505,7 +516,7 @@ int i2c_get_bus_num_fdt(int node) { int i; - for (i = 0; i < i2c_busses; i++) { + for (i = 0; i < ARRAY_SIZE(i2c_bus); i++) { if (node == i2c_bus[i].node) return i; } diff --git a/drivers/i2c/s3c24x0_i2c.h b/drivers/i2c/s3c24x0_i2c.h index b4a337a..882af62 100644 --- a/drivers/i2c/s3c24x0_i2c.h +++ b/drivers/i2c/s3c24x0_i2c.h @@ -16,6 +16,7 @@ struct s3c24x0_i2c { }; struct s3c24x0_i2c_bus { + bool active; /* port is active and available */ int node; /* device tree node */ int bus_num; /* i2c bus number */ struct s3c24x0_i2c *regs; -- cgit v0.10.2 From 296a461dcab70c4d18e51389a6f020b4ddfe88af Mon Sep 17 00:00:00 2001 From: Naveen Krishna Ch Date: Tue, 15 Oct 2013 16:02:44 +0530 Subject: i2c: s3c24xx: add hsi2c controller support Add support for hsi2c controller available on exynos5420. Note: driver currently supports only fast speed mode 100kbps Change-Id: I02555b1dc8f4ac21c50aa5158179768563c92f43 Signed-off-by: Naveen Krishna Chatradhi Signed-off-by: Vadim Bendebury Signed-off-by: R. Chandrasekar diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c index 0e6f241..f77a9d1 100644 --- a/drivers/i2c/s3c24x0_i2c.c +++ b/drivers/i2c/s3c24x0_i2c.c @@ -34,6 +34,76 @@ #define I2C_NOK_LA 3 /* Lost arbitration */ #define I2C_NOK_TOUT 4 /* time out */ +/* HSI2C specific register description */ + +/* I2C_CTL Register bits */ +#define HSI2C_FUNC_MODE_I2C (1u << 0) +#define HSI2C_MASTER (1u << 3) +#define HSI2C_RXCHON (1u << 6) /* Write/Send */ +#define HSI2C_TXCHON (1u << 7) /* Read/Receive */ +#define HSI2C_SW_RST (1u << 31) + +/* I2C_FIFO_CTL Register bits */ +#define HSI2C_RXFIFO_EN (1u << 0) +#define HSI2C_TXFIFO_EN (1u << 1) +#define HSI2C_TXFIFO_TRIGGER_LEVEL (0x20 << 16) +#define HSI2C_RXFIFO_TRIGGER_LEVEL (0x20 << 4) + +/* I2C_TRAILING_CTL Register bits */ +#define HSI2C_TRAILING_COUNT (0xff) + +/* I2C_INT_EN Register bits */ +#define HSI2C_TX_UNDERRUN_EN (1u << 2) +#define HSI2C_TX_OVERRUN_EN (1u << 3) +#define HSI2C_RX_UNDERRUN_EN (1u << 4) +#define HSI2C_RX_OVERRUN_EN (1u << 5) +#define HSI2C_INT_TRAILING_EN (1u << 6) +#define HSI2C_INT_I2C_EN (1u << 9) + +#define HSI2C_INT_ERROR_MASK (HSI2C_TX_UNDERRUN_EN |\ + HSI2C_TX_OVERRUN_EN |\ + HSI2C_RX_UNDERRUN_EN |\ + HSI2C_RX_OVERRUN_EN |\ + HSI2C_INT_TRAILING_EN) + +/* I2C_CONF Register bits */ +#define HSI2C_AUTO_MODE (1u << 31) +#define HSI2C_10BIT_ADDR_MODE (1u << 30) +#define HSI2C_HS_MODE (1u << 29) + +/* I2C_AUTO_CONF Register bits */ +#define HSI2C_READ_WRITE (1u << 16) +#define HSI2C_STOP_AFTER_TRANS (1u << 17) +#define HSI2C_MASTER_RUN (1u << 31) + +/* I2C_TIMEOUT Register bits */ +#define HSI2C_TIMEOUT_EN (1u << 31) + +/* I2C_TRANS_STATUS register bits */ +#define HSI2C_MASTER_BUSY (1u << 17) +#define HSI2C_SLAVE_BUSY (1u << 16) +#define HSI2C_TIMEOUT_AUTO (1u << 4) +#define HSI2C_NO_DEV (1u << 3) +#define HSI2C_NO_DEV_ACK (1u << 2) +#define HSI2C_TRANS_ABORT (1u << 1) +#define HSI2C_TRANS_SUCCESS (1u << 0) +#define HSI2C_TRANS_ERROR_MASK (HSI2C_TIMEOUT_AUTO |\ + HSI2C_NO_DEV | HSI2C_NO_DEV_ACK |\ + HSI2C_TRANS_ABORT) +#define HSI2C_TRANS_FINISHED_MASK (HSI2C_TRANS_ERROR_MASK | HSI2C_TRANS_SUCCESS) + + +/* I2C_FIFO_STAT Register bits */ +#define HSI2C_RX_FIFO_EMPTY (1u << 24) +#define HSI2C_RX_FIFO_FULL (1u << 23) +#define HSI2C_TX_FIFO_EMPTY (1u << 8) +#define HSI2C_TX_FIFO_FULL (1u << 7) +#define HSI2C_RX_FIFO_LEVEL(x) (((x) >> 16) & 0x7f) +#define HSI2C_TX_FIFO_LEVEL(x) ((x) & 0x7f) + +#define HSI2C_SLV_ADDR_MAS(x) ((x & 0x3ff) << 10) + +/* S3C I2C Controller bits */ #define I2CSTAT_BSY 0x20 /* Busy bit */ #define I2CSTAT_NACK 0x01 /* Nack bit */ #define I2CCON_ACKGEN 0x80 /* Acknowledge generation */ @@ -45,16 +115,41 @@ #define I2C_TIMEOUT_MS 1000 /* 1 second */ +#define HSI2C_TIMEOUT_US 100000 /* 100 ms, finer granularity */ + + +/* To support VCMA9 boards and other who dont define max_i2c_num */ +#ifndef CONFIG_MAX_I2C_NUM +#define CONFIG_MAX_I2C_NUM 1 +#endif /* * For SPL boot some boards need i2c before SDRAM is initialised so force * variables to live in SRAM */ static unsigned int g_current_bus __attribute__((section(".data"))); -#ifdef CONFIG_OF_CONTROL static struct s3c24x0_i2c_bus i2c_bus[CONFIG_MAX_I2C_NUM] __attribute__((section(".data"))); -#endif + +/** + * Get a pointer to the given bus index + * + * @bus_idx: Bus index to look up + * @return pointer to bus, or NULL if invalid or not available + */ +static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx) +{ + if (bus_idx < ARRAY_SIZE(i2c_bus)) { + struct s3c24x0_i2c_bus *bus; + + bus = &i2c_bus[bus_idx]; + if (bus->active) + return bus; + } + + debug("Undefined bus: %d\n", bus_idx); + return NULL; +} #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) static int GetI2CSDA(void) @@ -105,6 +200,55 @@ static int WaitForXfer(struct s3c24x0_i2c *i2c) return I2C_NOK_TOUT; } +/* + * Wait for transfer completion. + * + * This function reads the interrupt status register waiting for the INT_I2C + * bit to be set, which indicates copletion of a transaction. + * + * @param i2c: pointer to the appropriate register bank + * + * @return: I2C_OK in case of successful completion, I2C_NOK_TIMEOUT in case + * the status bits do not get set in time, or an approrpiate error + * value in case of transfer errors. + */ +static int hsi2c_wait_for_trx(struct exynos5_hsi2c *i2c) +{ + int i = HSI2C_TIMEOUT_US; + + while (i-- > 0) { + u32 int_status = readl(&i2c->usi_int_stat); + + if (int_status & HSI2C_INT_I2C_EN) { + u32 trans_status = readl(&i2c->usi_trans_status); + + /* Deassert pending interrupt. */ + writel(int_status, &i2c->usi_int_stat); + + if (trans_status & HSI2C_NO_DEV_ACK) { + debug("%s: no ACK from device\n", __func__); + return I2C_NACK; + } + if (trans_status & HSI2C_NO_DEV) { + debug("%s: no device\n", __func__); + return I2C_NOK; + } + if (trans_status & HSI2C_TRANS_ABORT) { + debug("%s: arbitration lost\n", __func__); + return I2C_NOK_LA; + } + if (trans_status & HSI2C_TIMEOUT_AUTO) { + debug("%s: device timed out\n", __func__); + return I2C_NOK_TOUT; + } + return I2C_OK; + } + udelay(1); + } + debug("%s: transaction timeout!\n", __func__); + return I2C_NOK_TOUT; +} + static void ReadWriteByte(struct s3c24x0_i2c *i2c) { writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon); @@ -154,6 +298,109 @@ static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat); } +#ifdef CONFIG_I2C_MULTI_BUS +static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; + ulong clkin; + unsigned int op_clk = i2c_bus->clock_frequency; + unsigned int i = 0, utemp0 = 0, utemp1 = 0; + unsigned int t_ftl_cycle; + +#if defined CONFIG_EXYNOS5 + clkin = get_i2c_clk(); +#endif + /* FPCLK / FI2C = + * (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + 8 + 2 * FLT_CYCLE + * uTemp0 = (CLK_DIV + 1) * (TSCLK_L + TSCLK_H + 2) + * uTemp1 = (TSCLK_L + TSCLK_H + 2) + * uTemp2 = TSCLK_L + TSCLK_H + */ + t_ftl_cycle = (readl(&hsregs->usi_conf) >> 16) & 0x7; + utemp0 = (clkin / op_clk) - 8 - 2 * t_ftl_cycle; + + /* CLK_DIV max is 256 */ + for (i = 0; i < 256; i++) { + utemp1 = utemp0 / (i + 1); + if ((utemp1 < 512) && (utemp1 > 4)) { + i2c_bus->clk_cycle = utemp1 - 2; + i2c_bus->clk_div = i; + return 0; + } + } + return -1; +} +#endif + +static void hsi2c_ch_init(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *hsregs = i2c_bus->hsregs; + unsigned int t_sr_release; + unsigned int n_clkdiv; + unsigned int t_start_su, t_start_hd; + unsigned int t_stop_su; + unsigned int t_data_su, t_data_hd; + unsigned int t_scl_l, t_scl_h; + u32 i2c_timing_s1; + u32 i2c_timing_s2; + u32 i2c_timing_s3; + u32 i2c_timing_sla; + + n_clkdiv = i2c_bus->clk_div; + t_scl_l = i2c_bus->clk_cycle / 2; + t_scl_h = i2c_bus->clk_cycle / 2; + t_start_su = t_scl_l; + t_start_hd = t_scl_l; + t_stop_su = t_scl_l; + t_data_su = t_scl_l / 2; + t_data_hd = t_scl_l / 2; + t_sr_release = i2c_bus->clk_cycle; + + i2c_timing_s1 = t_start_su << 24 | t_start_hd << 16 | t_stop_su << 8; + i2c_timing_s2 = t_data_su << 24 | t_scl_l << 8 | t_scl_h << 0; + i2c_timing_s3 = n_clkdiv << 16 | t_sr_release << 0; + i2c_timing_sla = t_data_hd << 0; + + writel(HSI2C_TRAILING_COUNT, &hsregs->usi_trailing_ctl); + + /* Clear to enable Timeout */ + clrsetbits_le32(&hsregs->usi_timeout, HSI2C_TIMEOUT_EN, 0); + + /* set AUTO mode */ + writel(readl(&hsregs->usi_conf) | HSI2C_AUTO_MODE, &hsregs->usi_conf); + + /* Enable completion conditions' reporting. */ + writel(HSI2C_INT_I2C_EN, &hsregs->usi_int_en); + + /* Enable FIFOs */ + writel(HSI2C_RXFIFO_EN | HSI2C_TXFIFO_EN, &hsregs->usi_fifo_ctl); + + /* Currently operating in Fast speed mode. */ + writel(i2c_timing_s1, &hsregs->usi_timing_fs1); + writel(i2c_timing_s2, &hsregs->usi_timing_fs2); + writel(i2c_timing_s3, &hsregs->usi_timing_fs3); + writel(i2c_timing_sla, &hsregs->usi_timing_sla); +} + +/* SW reset for the high speed bus */ +static void exynos5_i2c_reset(struct s3c24x0_i2c_bus *i2c_bus) +{ + struct exynos5_hsi2c *i2c = i2c_bus->hsregs; + u32 i2c_ctl; + + /* Set and clear the bit for reset */ + i2c_ctl = readl(&i2c->usi_ctl); + i2c_ctl |= HSI2C_SW_RST; + writel(i2c_ctl, &i2c->usi_ctl); + + i2c_ctl = readl(&i2c->usi_ctl); + i2c_ctl &= ~HSI2C_SW_RST; + writel(i2c_ctl, &i2c->usi_ctl); + + /* Initialize the configure registers */ + hsi2c_ch_init(i2c_bus); +} + /* * MULTI BUS I2C support */ @@ -161,16 +408,21 @@ static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd) #ifdef CONFIG_I2C_MULTI_BUS int i2c_set_bus_num(unsigned int bus) { - struct s3c24x0_i2c *i2c; + struct s3c24x0_i2c_bus *i2c_bus; i2c_bus = get_bus(bus); if (!i2c_bus) return -1; - } - g_current_bus = bus; - i2c = get_base_i2c(); - i2c_ch_init(i2c, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + + if (i2c_bus->is_highspeed) { + if (hsi2c_get_clk_details(i2c_bus)) + return -1; + hsi2c_ch_init(i2c_bus); + } else { + i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, + CONFIG_SYS_I2C_SLAVE); + } return 0; } @@ -183,7 +435,6 @@ unsigned int i2c_get_bus_num(void) void i2c_init(int speed, int slaveadd) { - int i; struct s3c24x0_i2c *i2c; #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) struct s3c24x0_gpio *gpio = s3c24x0_get_base_gpio(); @@ -207,6 +458,8 @@ void i2c_init(int speed, int slaveadd) } #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5) + int i; + if ((readl(&i2c->iicstat) & I2CSTAT_BSY) || GetI2CSDA() == 0) { #ifdef CONFIG_S3C2410 ulong old_gpecon = readl(&gpio->gpecon); @@ -255,6 +508,227 @@ void i2c_init(int speed, int slaveadd) } /* + * Poll the appropriate bit of the fifo status register until the interface is + * ready to process the next byte or timeout expires. + * + * In addition to the FIFO status register this function also polls the + * interrupt status register to be able to detect unexpected transaction + * completion. + * + * When FIFO is ready to process the next byte, this function returns I2C_OK. + * If in course of polling the INT_I2C assertion is detected, the function + * returns I2C_NOK. If timeout happens before any of the above conditions is + * met - the function returns I2C_NOK_TOUT; + + * @param i2c: pointer to the appropriate i2c register bank. + * @param rx_transfer: set to True if the receive transaction is in progress. + * @return: as described above. + */ +static unsigned hsi2c_poll_fifo(struct exynos5_hsi2c *i2c, bool rx_transfer) +{ + u32 fifo_bit = rx_transfer ? HSI2C_RX_FIFO_EMPTY : HSI2C_TX_FIFO_FULL; + int i = HSI2C_TIMEOUT_US; + + while (readl(&i2c->usi_fifo_stat) & fifo_bit) { + if (readl(&i2c->usi_int_stat) & HSI2C_INT_I2C_EN) { + /* + * There is a chance that assertion of + * HSI2C_INT_I2C_EN and deassertion of + * HSI2C_RX_FIFO_EMPTY happen simultaneously. Let's + * give FIFO status priority and check it one more + * time before reporting interrupt. The interrupt will + * be reported next time this function is called. + */ + if (rx_transfer && + !(readl(&i2c->usi_fifo_stat) & fifo_bit)) + break; + return I2C_NOK; + } + if (!i--) { + debug("%s: FIFO polling timeout!\n", __func__); + return I2C_NOK_TOUT; + } + udelay(1); + } + return I2C_OK; +} + +/* + * Preapre hsi2c transaction, either read or write. + * + * Set up transfer as described in section 27.5.1.2 'I2C Channel Auto Mode' of + * the 5420 UM. + * + * @param i2c: pointer to the appropriate i2c register bank. + * @param chip: slave address on the i2c bus (with read/write bit exlcuded) + * @param len: number of bytes expected to be sent or received + * @param rx_transfer: set to true for receive transactions + * @param: issue_stop: set to true if i2c stop condition should be generated + * after this transaction. + * @return: I2C_NOK_TOUT in case the bus remained busy for HSI2C_TIMEOUT_US, + * I2C_OK otherwise. + */ +static int hsi2c_prepare_transaction(struct exynos5_hsi2c *i2c, + u8 chip, + u16 len, + bool rx_transfer, + bool issue_stop) +{ + u32 conf; + + conf = len | HSI2C_MASTER_RUN; + + if (issue_stop) + conf |= HSI2C_STOP_AFTER_TRANS; + + /* Clear to enable Timeout */ + writel(readl(&i2c->usi_timeout) & ~HSI2C_TIMEOUT_EN, &i2c->usi_timeout); + + /* Set slave address */ + writel(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr); + + if (rx_transfer) { + /* i2c master, read transaction */ + writel((HSI2C_RXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + &i2c->usi_ctl); + + /* read up to len bytes, stop after transaction is finished */ + writel(conf | HSI2C_READ_WRITE, &i2c->usi_auto_conf); + } else { + /* i2c master, write transaction */ + writel((HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C | HSI2C_MASTER), + &i2c->usi_ctl); + + /* write up to len bytes, stop after transaction is finished */ + writel(conf, &i2c->usi_auto_conf); + } + + /* Reset all pending interrupt status bits we care about, if any */ + writel(HSI2C_INT_I2C_EN, &i2c->usi_int_stat); + + return I2C_OK; +} + +/* + * Wait while i2c bus is settling down (mostly stop gets completed). + */ +static int hsi2c_wait_while_busy(struct exynos5_hsi2c *i2c) +{ + int i = HSI2C_TIMEOUT_US; + + while (readl(&i2c->usi_trans_status) & HSI2C_MASTER_BUSY) { + if (!i--) { + debug("%s: bus busy\n", __func__); + return I2C_NOK_TOUT; + } + udelay(1); + } + return I2C_OK; +} + +static int hsi2c_write(struct exynos5_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + unsigned char data[], + unsigned short len, + bool issue_stop) +{ + int i, rv = 0; + + if (!(len + alen)) { + /* Writes of zero length not supported in auto mode. */ + debug("%s: zero length writes not supported\n", __func__); + return I2C_NOK; + } + + rv = hsi2c_prepare_transaction + (i2c, chip, len + alen, false, issue_stop); + if (rv != I2C_OK) + return rv; + + /* Move address, if any, and the data, if any, into the FIFO. */ + for (i = 0; i < alen; i++) { + rv = hsi2c_poll_fifo(i2c, false); + if (rv != I2C_OK) { + debug("%s: address write failed\n", __func__); + goto write_error; + } + writel(addr[i], &i2c->usi_txdata); + } + + for (i = 0; i < len; i++) { + rv = hsi2c_poll_fifo(i2c, false); + if (rv != I2C_OK) { + debug("%s: data write failed\n", __func__); + goto write_error; + } + writel(data[i], &i2c->usi_txdata); + } + + rv = hsi2c_wait_for_trx(i2c); + + write_error: + if (issue_stop) { + int tmp_ret = hsi2c_wait_while_busy(i2c); + if (rv == I2C_OK) + rv = tmp_ret; + } + + writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); /* done */ + return rv; +} + +static int hsi2c_read(struct exynos5_hsi2c *i2c, + unsigned char chip, + unsigned char addr[], + unsigned char alen, + unsigned char data[], + unsigned short len) +{ + int i, rv, tmp_ret; + bool drop_data = false; + + if (!len) { + /* Reads of zero length not supported in auto mode. */ + debug("%s: zero length read adjusted\n", __func__); + drop_data = true; + len = 1; + } + + if (alen) { + /* Internal register adress needs to be written first. */ + rv = hsi2c_write(i2c, chip, addr, alen, NULL, 0, false); + if (rv != I2C_OK) + return rv; + } + + rv = hsi2c_prepare_transaction(i2c, chip, len, true, true); + + if (rv != I2C_OK) + return rv; + + for (i = 0; i < len; i++) { + rv = hsi2c_poll_fifo(i2c, true); + if (rv != I2C_OK) + goto read_err; + if (drop_data) + continue; + data[i] = readl(&i2c->usi_rxdata); + } + + rv = hsi2c_wait_for_trx(i2c); + + read_err: + tmp_ret = hsi2c_wait_while_busy(i2c); + if (rv == I2C_OK) + rv = tmp_ret; + + writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl); /* done */ + return rv; +} + +/* * cmd_type is 0 for write, 1 for read. * * addr_len can take any value from 0-255, it is only limited @@ -368,10 +842,13 @@ bailout: int i2c_probe(uchar chip) { - struct s3c24x0_i2c *i2c; + struct s3c24x0_i2c_bus *i2c_bus; uchar buf[1]; + int ret; - i2c = get_base_i2c(); + i2c_bus = get_bus(g_current_bus); + if (!i2c_bus) + return -1; buf[0] = 0; /* @@ -379,12 +856,21 @@ int i2c_probe(uchar chip) * address was ed (i.e. there was a chip at that address which * drove the data line low). */ - return i2c_transfer(i2c, I2C_READ, chip << 1, 0, 0, buf, 1) != I2C_OK; + if (i2c_bus->is_highspeed) { + ret = hsi2c_read(i2c_bus->hsregs, + chip, 0, 0, buf, 1); + } else { + ret = i2c_transfer(i2c_bus->regs, + I2C_READ, chip << 1, 0, 0, buf, 1); + } + + + return ret != I2C_OK; } int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) { - struct s3c24x0_i2c *i2c; + struct s3c24x0_i2c_bus *i2c_bus; uchar xaddr[4]; int ret; @@ -416,11 +902,21 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif - i2c = get_base_i2c(); - ret = i2c_transfer(i2c, I2C_READ, chip << 1, &xaddr[4 - alen], alen, - buffer, len); - if (ret != 0) { - debug("I2c read: failed %d\n", ret); + i2c_bus = get_bus(g_current_bus); + if (!i2c_bus) + return -1; + + if (i2c_bus->is_highspeed) + ret = hsi2c_read(i2c_bus->hsregs, chip, &xaddr[4 - alen], + alen, buffer, len); + else + ret = i2c_transfer(i2c_bus->regs, I2C_READ, chip << 1, + &xaddr[4 - alen], alen, buffer, len); + + if (ret) { + if (i2c_bus->is_highspeed) + exynos5_i2c_reset(i2c_bus); + debug("I2c read failed %d\n", ret); return 1; } return 0; @@ -428,8 +924,9 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len) int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) { - struct s3c24x0_i2c *i2c; + struct s3c24x0_i2c_bus *i2c_bus; uchar xaddr[4]; + int ret; if (alen > 4) { debug("I2C write: addr len %d not supported\n", alen); @@ -458,58 +955,80 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len) chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW); #endif - i2c = get_base_i2c(); - return (i2c_transfer - (i2c, I2C_WRITE, chip << 1, &xaddr[4 - alen], alen, buffer, - len) != 0); + i2c_bus = get_bus(g_current_bus); + if (!i2c_bus) + return -1; + + if (i2c_bus->is_highspeed) + ret = hsi2c_write(i2c_bus->hsregs, chip, &xaddr[4 - alen], + alen, buffer, len, true); + else + ret = i2c_transfer(i2c_bus->regs, I2C_WRITE, chip << 1, + &xaddr[4 - alen], alen, buffer, len); + + if (ret != 0) { + if (i2c_bus->is_highspeed) + exynos5_i2c_reset(i2c_bus); + return 1; + } else { + return 0; + } } #ifdef CONFIG_OF_CONTROL -void board_i2c_init(const void *blob) +static void process_nodes(const void *blob, int node_list[], int count, + int is_highspeed) { + struct s3c24x0_i2c_bus *bus; int i; - int node_list[CONFIG_MAX_I2C_NUM]; - int count; - - count = fdtdec_find_aliases_for_id(blob, "i2c", - COMPAT_SAMSUNG_S3C2440_I2C, node_list, - CONFIG_MAX_I2C_NUM); for (i = 0; i < count; i++) { - struct s3c24x0_i2c_bus *bus; int node = node_list[i]; if (node <= 0) continue; + bus = &i2c_bus[i]; bus->active = true; - bus->regs = (struct s3c24x0_i2c *) - fdtdec_get_addr(blob, node, "reg"); + bus->is_highspeed = is_highspeed; + + if (is_highspeed) + bus->hsregs = (struct exynos5_hsi2c *) + fdtdec_get_addr(blob, node, "reg"); + else + bus->regs = (struct s3c24x0_i2c *) + fdtdec_get_addr(blob, node, "reg"); + bus->id = pinmux_decode_periph_id(blob, node); + bus->clock_frequency = fdtdec_get_int(blob, node, + "clock-frequency", + CONFIG_SYS_I2C_SPEED); bus->node = node; bus->bus_num = i; exynos_pinmux_config(bus->id, 0); + + /* Mark position as used */ + node_list[i] = -1; } } -/** - * Get a pointer to the given bus index - * - * @bus_idx: Bus index to look up - * @return pointer to bus, or NULL if invalid or not available - */ -static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx) +void board_i2c_init(const void *blob) { - if (bus_idx < ARRAY_SIZE(i2c_bus)) { - struct s3c24x0_i2c_bus *bus; + int node_list[CONFIG_MAX_I2C_NUM]; + int count; - bus = &i2c_bus[bus_idx]; - if (bus->active) - return bus; - } + /* First get the normal i2c ports */ + count = fdtdec_find_aliases_for_id(blob, "i2c", + COMPAT_SAMSUNG_S3C2440_I2C, node_list, + CONFIG_MAX_I2C_NUM); + process_nodes(blob, node_list, count, 0); + + /* Now look for high speed i2c ports */ + count = fdtdec_find_aliases_for_id(blob, "i2c", + COMPAT_SAMSUNG_EXYNOS5_I2C, node_list, + CONFIG_MAX_I2C_NUM); + process_nodes(blob, node_list, count, 1); - debug("Undefined bus: %d\n", bus_idx); - return NULL; } int i2c_get_bus_num_fdt(int node) @@ -525,9 +1044,10 @@ int i2c_get_bus_num_fdt(int node) return -1; } +#ifdef CONFIG_I2C_MULTI_BUS int i2c_reset_port_fdt(const void *blob, int node) { - struct s3c24x0_i2c_bus *i2c; + struct s3c24x0_i2c_bus *i2c_bus; int bus; bus = i2c_get_bus_num_fdt(node); @@ -536,16 +1056,24 @@ int i2c_reset_port_fdt(const void *blob, int node) return -1; } - i2c = get_bus(bus); - if (!i2c) { + i2c_bus = get_bus(bus); + if (!i2c_bus) { debug("get_bus() failed for node node %d\n", node); return -1; } - i2c_ch_init(i2c->regs, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE); + if (i2c_bus->is_highspeed) { + if (hsi2c_get_clk_details(i2c_bus)) + return -1; + hsi2c_ch_init(i2c_bus); + } else { + i2c_ch_init(i2c_bus->regs, i2c_bus->clock_frequency, + CONFIG_SYS_I2C_SLAVE); + } return 0; } #endif +#endif #endif /* CONFIG_HARD_I2C */ diff --git a/drivers/i2c/s3c24x0_i2c.h b/drivers/i2c/s3c24x0_i2c.h index 882af62..1ae73d2 100644 --- a/drivers/i2c/s3c24x0_i2c.h +++ b/drivers/i2c/s3c24x0_i2c.h @@ -15,11 +15,48 @@ struct s3c24x0_i2c { u32 iiclc; }; +struct exynos5_hsi2c { + u32 usi_ctl; + u32 usi_fifo_ctl; + u32 usi_trailing_ctl; + u32 usi_clk_ctl; + u32 usi_clk_slot; + u32 spi_ctl; + u32 uart_ctl; + u32 res1; + u32 usi_int_en; + u32 usi_int_stat; + u32 usi_modem_stat; + u32 usi_error_stat; + u32 usi_fifo_stat; + u32 usi_txdata; + u32 usi_rxdata; + u32 res2; + u32 usi_conf; + u32 usi_auto_conf; + u32 usi_timeout; + u32 usi_manual_cmd; + u32 usi_trans_status; + u32 usi_timing_hs1; + u32 usi_timing_hs2; + u32 usi_timing_hs3; + u32 usi_timing_fs1; + u32 usi_timing_fs2; + u32 usi_timing_fs3; + u32 usi_timing_sla; + u32 i2c_addr; +}; + struct s3c24x0_i2c_bus { bool active; /* port is active and available */ int node; /* device tree node */ int bus_num; /* i2c bus number */ struct s3c24x0_i2c *regs; + struct exynos5_hsi2c *hsregs; + int is_highspeed; /* High speed type, rather than I2C */ + unsigned clock_frequency; int id; + unsigned clk_cycle; + unsigned clk_div; }; #endif /* _S3C24X0_I2C_H */ -- cgit v0.10.2 From 10cee5166539ccfce7f7d5776fb75558af385405 Mon Sep 17 00:00:00 2001 From: Nobuhiro Iwamatsu Date: Fri, 11 Oct 2013 16:23:53 +0900 Subject: README: I2C: Fix indent Signed-off-by: Nobuhiro Iwamatsu diff --git a/README b/README index e209c89..f0eedbb 100644 --- a/README +++ b/README @@ -1994,9 +1994,9 @@ CBFS (Coreboot Filesystem) support second bus. - drivers/i2c/tegra_i2c.c: - - activate this driver with CONFIG_SYS_I2C_TEGRA - - This driver adds 4 i2c buses with a fix speed from - 100000 and the slave addr 0! + - activate this driver with CONFIG_SYS_I2C_TEGRA + - This driver adds 4 i2c buses with a fix speed from + 100000 and the slave addr 0! - drivers/i2c/ppc4xx_i2c.c - activate this driver with CONFIG_SYS_I2C_PPC4XX -- cgit v0.10.2 From 9a2a73d29fa395f42f3fb477ac99dda4d7f2f21f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 16 Oct 2013 13:53:02 +0900 Subject: i2c: eliminate warnings in i2c_reloc_fixup function The prototype of handlers had changed. This commit uses cast with (void *) rather than the handler-specific prototype. Signed-off-by: Masahiro Yamada diff --git a/drivers/i2c/i2c_core.c b/drivers/i2c/i2c_core.c index b067b7c..e1767f4 100644 --- a/drivers/i2c/i2c_core.c +++ b/drivers/i2c/i2c_core.c @@ -56,25 +56,23 @@ void i2c_reloc_fixup(void) /* i2c_init() */ addr = (unsigned long)i2c_adap_p->init; addr += gd->reloc_off; - i2c_adap_p->init = (void (*)(int, int))addr; + i2c_adap_p->init = (void *)addr; /* i2c_probe() */ addr = (unsigned long)i2c_adap_p->probe; addr += gd->reloc_off; - i2c_adap_p->probe = (int (*)(uint8_t))addr; + i2c_adap_p->probe = (void *)addr; /* i2c_read() */ addr = (unsigned long)i2c_adap_p->read; addr += gd->reloc_off; - i2c_adap_p->read = (int (*)(uint8_t, uint, int, uint8_t *, - int))addr; + i2c_adap_p->read = (void *)addr; /* i2c_write() */ addr = (unsigned long)i2c_adap_p->write; addr += gd->reloc_off; - i2c_adap_p->write = (int (*)(uint8_t, uint, int, uint8_t *, - int))addr; + i2c_adap_p->write = (void *)addr; /* i2c_set_bus_speed() */ addr = (unsigned long)i2c_adap_p->set_bus_speed; addr += gd->reloc_off; - i2c_adap_p->set_bus_speed = (uint (*)(uint))addr; + i2c_adap_p->set_bus_speed = (void *)addr; /* name */ addr = (unsigned long)i2c_adap_p->name; addr += gd->reloc_off; -- cgit v0.10.2