From: Peng Fan <Peng.Fan@freescale.com>
To: u-boot@lists.denx.de
Subject: [U-Boot] [PATCH V2] i2c: mxc: refactor i2c driver and support dm
Date: Sun, 26 Apr 2015 15:38:40 +0800 [thread overview]
Message-ID: <553C9600.6020605@freescale.com> (raw)
In-Reply-To: <1429967080-6137-1-git-send-email-Peng.Fan@freescale.com>
Please ignore this version, since bus_i2c_init is not good for DM, I'll
implement a weak function for this.
On 4/25/2015 9:04 PM, Peng Fan wrote:
> 1. Introduce a new structure `struct mxc_i2c_bus`, this structure will
> used for non-DM and DM.
> 1. Remove `struct mxc_i2c_regs` structure, but use register offset to access
> registers based on `base` entry of `struct mxc_i2c_bus`.
> 2. Remove most `#ifdef I2C_QUIRK_REG`. Using driver_data to contain platform
> flags. A new flag is introduced, I2C_QUIRK_FLAG.
> 3. Most functions use `struct mxc_i2c_bus` as one of the parameters.
> 4. Support DM
> 5. Make most functions common to DM and non-DM, try to avoid duplicated code.
> 6. struct i2c_parms and struct sram_data are removed, since we move to use
> struct mxc_i2c_regs.
> 7. Remove bus_i2c_read bus_i2c_write prototype in header file
> 8. The frist paramter of bus_i2c_init is modified to i2c index, since
> bus_i2c_init is a must now, so it is also implemented for DM part.
>
> Signed-off-by: Peng Fan <Peng.Fan@freescale.com>
> ---
>
> Changes v2:
> This patch is a big change from v1 patch.
> 1. Refactor driver, remove register access based on structure, but use
> 'base + offset'
> 2. Introduce mxc_i2c_bus structure
> 3. Introduce I2C_QUIRK_FLAG and remove most I2C_QUIRK_REG and use
> driver_data to contain the flags for different platforms
> 4. Avoid duplicated code between DM and non-DM part
> 5. The function name i2c_init_transfer is not changed.
> 6. Remove bus_i2c_read/write prototype from header file
> 7. change bus_i2c_init's first parameter to i2c index
> 8. Rename patch name, since refactor non-DM part.
>
> arch/arm/imx-common/i2c-mxv7.c | 3 +-
> arch/arm/include/asm/imx-common/mxc_i2c.h | 6 +-
> drivers/i2c/mxc_i2c.c | 585 ++++++++++++++++++++----------
> 3 files changed, 402 insertions(+), 192 deletions(-)
>
> diff --git a/arch/arm/imx-common/i2c-mxv7.c b/arch/arm/imx-common/i2c-mxv7.c
> index 1a632e7..3c793ef 100644
> --- a/arch/arm/imx-common/i2c-mxv7.c
> +++ b/arch/arm/imx-common/i2c-mxv7.c
> @@ -99,8 +99,7 @@ int setup_i2c(unsigned i2c_index, int speed, int slave_addr,
> if (ret)
> goto err_idle;
>
> - bus_i2c_init(i2c_bases[i2c_index], speed, slave_addr,
> - force_idle_bus, p);
> + bus_i2c_init(i2c_index, speed, slave_addr, force_idle_bus, p);
>
> return 0;
>
> diff --git a/arch/arm/include/asm/imx-common/mxc_i2c.h b/arch/arm/include/asm/imx-common/mxc_i2c.h
> index af86163..94b085f 100644
> --- a/arch/arm/include/asm/imx-common/mxc_i2c.h
> +++ b/arch/arm/include/asm/imx-common/mxc_i2c.h
> @@ -54,10 +54,6 @@ struct i2c_pads_info {
>
> int setup_i2c(unsigned i2c_index, int speed, int slave_addr,
> struct i2c_pads_info *p);
> -void bus_i2c_init(void *base, int speed, int slave_addr,
> +void bus_i2c_init(int index, int speed, int slave_addr,
> int (*idle_bus_fn)(void *p), void *p);
> -int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf,
> - int len);
> -int bus_i2c_write(void *base, uchar chip, uint addr, int alen,
> - const uchar *buf, int len);
> #endif
> diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c
> index fc5ee35..9894f3e 100644
> --- a/drivers/i2c/mxc_i2c.c
> +++ b/drivers/i2c/mxc_i2c.c
> @@ -21,26 +21,21 @@
> #include <asm/io.h>
> #include <i2c.h>
> #include <watchdog.h>
> +#include <dm.h>
> +#include <fdtdec.h>
>
> DECLARE_GLOBAL_DATA_PTR;
>
> -#ifdef I2C_QUIRK_REG
> -struct mxc_i2c_regs {
> - uint8_t iadr;
> - uint8_t ifdr;
> - uint8_t i2cr;
> - uint8_t i2sr;
> - uint8_t i2dr;
> -};
> -#else
> -struct mxc_i2c_regs {
> - uint32_t iadr;
> - uint32_t ifdr;
> - uint32_t i2cr;
> - uint32_t i2sr;
> - uint32_t i2dr;
> -};
> -#endif
> +#define I2C_QUIRK_FLAG (1 << 0)
> +
> +#define IMX_I2C_REGSHIFT 2
> +#define VF610_I2C_REGSHIFT 0
> +/* Register index */
> +#define IADR 0
> +#define IFDR 1
> +#define I2CR 2
> +#define I2SR 3
> +#define I2DR 4
>
> #define I2CR_IIEN (1 << 6)
> #define I2CR_MSTA (1 << 5)
> @@ -68,6 +63,23 @@ struct mxc_i2c_regs {
> #error "define CONFIG_SYS_I2C_BASE to use the mxc_i2c driver"
> #endif
>
> +/*
> + * Information about i2c controller
> + * struct mxc_i2c_bus - information about the i2c[x] bus
> + * @base: Address of I2C bus controller
> + * @speed: Speed of I2C bus
> + * @driver_data: Flags for different platforms, such as I2C_QUIRK_FLAG.
> + * @idle_bus_fn: function to force bus idle
> + * @idle_bus_data: parameter for idle_bus_fun
> + */
> +struct mxc_i2c_bus {
> + ulong base;
> + ulong driver_data;
> + int speed;
> + int (*idle_bus_fn)(void *p);
> + void *idle_bus_data;
> +};
> +
> #ifdef I2C_QUIRK_REG
> static u16 i2c_clk_div[60][2] = {
> { 20, 0x00 }, { 22, 0x01 }, { 24, 0x02 }, { 26, 0x03 },
> @@ -104,7 +116,6 @@ static u16 i2c_clk_div[50][2] = {
> };
> #endif
>
> -
> #ifndef CONFIG_SYS_MXC_I2C1_SPEED
> #define CONFIG_SYS_MXC_I2C1_SPEED 100000
> #endif
> @@ -125,11 +136,10 @@ static u16 i2c_clk_div[50][2] = {
> #define CONFIG_SYS_MXC_I2C3_SLAVE 0
> #endif
>
> -
> /*
> * Calculate and set proper clock divider
> */
> -static uint8_t i2c_imx_get_clk(unsigned int rate)
> +static uint8_t i2c_imx_get_clk(struct mxc_i2c_bus *i2c_bus, unsigned int rate)
> {
> unsigned int i2c_clk_rate;
> unsigned int div;
> @@ -162,18 +172,20 @@ static uint8_t i2c_imx_get_clk(unsigned int rate)
> /*
> * Set I2C Bus speed
> */
> -static int bus_i2c_set_bus_speed(void *base, int speed)
> +static int bus_i2c_set_bus_speed(struct mxc_i2c_bus *i2c_bus, int speed)
> {
> - struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base;
> - u8 clk_idx = i2c_imx_get_clk(speed);
> + ulong base = i2c_bus->base;
> + bool quirk = i2c_bus->driver_data & I2C_QUIRK_FLAG ? true : false;
> + u8 clk_idx = i2c_imx_get_clk(i2c_bus, speed);
> u8 idx = i2c_clk_div[clk_idx][1];
> + int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
>
> /* Store divider value */
> - writeb(idx, &i2c_regs->ifdr);
> + writeb(idx, base + (IFDR << reg_shift));
>
> /* Reset module */
> - writeb(I2CR_IDIS, &i2c_regs->i2cr);
> - writeb(0, &i2c_regs->i2sr);
> + writeb(I2CR_IDIS, base + (I2CR << reg_shift));
> + writeb(0, base + (I2SR << reg_shift));
> return 0;
> }
>
> @@ -181,21 +193,26 @@ static int bus_i2c_set_bus_speed(void *base, int speed)
> #define ST_BUS_BUSY (I2SR_IBB | (I2SR_IBB << 8))
> #define ST_IIF (I2SR_IIF | (I2SR_IIF << 8))
>
> -static int wait_for_sr_state(struct mxc_i2c_regs *i2c_regs, unsigned state)
> +static int wait_for_sr_state(struct mxc_i2c_bus *i2c_bus, unsigned state)
> {
> unsigned sr;
> ulong elapsed;
> + bool quirk = i2c_bus->driver_data & I2C_QUIRK_FLAG ? true : false;
> + int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
> + ulong base = i2c_bus->base;
> ulong start_time = get_timer(0);
> for (;;) {
> - sr = readb(&i2c_regs->i2sr);
> + sr = readb(base + (I2SR << reg_shift));
> if (sr & I2SR_IAL) {
> -#ifdef I2C_QUIRK_REG
> - writeb(sr | I2SR_IAL, &i2c_regs->i2sr);
> -#else
> - writeb(sr & ~I2SR_IAL, &i2c_regs->i2sr);
> -#endif
> + if (quirk)
> + writeb(sr | I2SR_IAL, base +
> + (I2SR << reg_shift));
> + else
> + writeb(sr & ~I2SR_IAL, base +
> + (I2SR << reg_shift));
> printf("%s: Arbitration lost sr=%x cr=%x state=%x\n",
> - __func__, sr, readb(&i2c_regs->i2cr), state);
> + __func__, sr, readb(base + (I2CR << reg_shift)),
> + state);
> return -ERESTART;
> }
> if ((sr & (state >> 8)) == (unsigned char)state)
> @@ -206,17 +223,21 @@ static int wait_for_sr_state(struct mxc_i2c_regs *i2c_regs, unsigned state)
> break;
> }
> printf("%s: failed sr=%x cr=%x state=%x\n", __func__,
> - sr, readb(&i2c_regs->i2cr), state);
> + sr, readb(base + (I2CR << reg_shift)), state);
> return -ETIMEDOUT;
> }
>
> -static int tx_byte(struct mxc_i2c_regs *i2c_regs, u8 byte)
> +static int tx_byte(struct mxc_i2c_bus *i2c_bus, u8 byte)
> {
> int ret;
> + int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
> + VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
> + ulong base = i2c_bus->base;
>
> - writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr);
> - writeb(byte, &i2c_regs->i2dr);
> - ret = wait_for_sr_state(i2c_regs, ST_IIF);
> + writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
> + writeb(byte, base + (I2DR << reg_shift));
> +
> + ret = wait_for_sr_state(i2c_bus, ST_IIF);
> if (ret < 0)
> return ret;
> if (ret & I2SR_RX_NO_AK)
> @@ -225,16 +246,28 @@ static int tx_byte(struct mxc_i2c_regs *i2c_regs, u8 byte)
> }
>
> /*
> + * Stub implementations for outer i2c slave operations.
> + */
> +void __i2c_force_reset_slave(void)
> +{
> +}
> +void i2c_force_reset_slave(void)
> + __attribute__((weak, alias("__i2c_force_reset_slave")));
> +
> +/*
> * Stop I2C transaction
> */
> -static void i2c_imx_stop(struct mxc_i2c_regs *i2c_regs)
> +static void i2c_imx_stop(struct mxc_i2c_bus *i2c_bus)
> {
> int ret;
> - unsigned int temp = readb(&i2c_regs->i2cr);
> + int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
> + VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
> + ulong base = i2c_bus->base;
> + unsigned int temp = readb(base + (I2CR << reg_shift));
>
> temp &= ~(I2CR_MSTA | I2CR_MTX);
> - writeb(temp, &i2c_regs->i2cr);
> - ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE);
> + writeb(temp, base + (I2CR << reg_shift));
> + ret = wait_for_sr_state(i2c_bus, ST_BUS_IDLE);
> if (ret < 0)
> printf("%s:trigger stop failed\n", __func__);
> }
> @@ -243,66 +276,82 @@ static void i2c_imx_stop(struct mxc_i2c_regs *i2c_regs)
> * Send start signal, chip address and
> * write register address
> */
> -static int i2c_init_transfer_(struct mxc_i2c_regs *i2c_regs,
> - uchar chip, uint addr, int alen)
> +static int i2c_init_transfer_(struct mxc_i2c_bus *i2c_bus, u8 chip,
> + u32 addr, int alen)
> {
> unsigned int temp;
> int ret;
> + bool quirk = i2c_bus->driver_data & I2C_QUIRK_FLAG ? true : false;
> + ulong base = i2c_bus->base;
> + int reg_shift = quirk ? VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
> +
> + /* Reset i2c slave */
> + i2c_force_reset_slave();
>
> /* Enable I2C controller */
> -#ifdef I2C_QUIRK_REG
> - if (readb(&i2c_regs->i2cr) & I2CR_IDIS) {
> -#else
> - if (!(readb(&i2c_regs->i2cr) & I2CR_IEN)) {
> -#endif
> - writeb(I2CR_IEN, &i2c_regs->i2cr);
> + if (quirk)
> + ret = readb(base + (I2CR << reg_shift)) & I2CR_IDIS;
> + else
> + ret = !(readb(base + (I2CR << reg_shift)) & I2CR_IEN);
> +
> + if (ret) {
> + writeb(I2CR_IEN, base + (I2CR << reg_shift));
> /* Wait for controller to be stable */
> udelay(50);
> }
> - if (readb(&i2c_regs->iadr) == (chip << 1))
> - writeb((chip << 1) ^ 2, &i2c_regs->iadr);
> - writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr);
> - ret = wait_for_sr_state(i2c_regs, ST_BUS_IDLE);
> +
> + if (readb(base + (IADR << reg_shift)) == (chip << 1))
> + writeb((chip << 1) ^ 2, base + (IADR << reg_shift));
> + writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
> + ret = wait_for_sr_state(i2c_bus, ST_BUS_IDLE);
> if (ret < 0)
> return ret;
>
> /* Start I2C transaction */
> - temp = readb(&i2c_regs->i2cr);
> + temp = readb(base + (I2CR << reg_shift));
> temp |= I2CR_MSTA;
> - writeb(temp, &i2c_regs->i2cr);
> + writeb(temp, base + (I2CR << reg_shift));
>
> - ret = wait_for_sr_state(i2c_regs, ST_BUS_BUSY);
> + ret = wait_for_sr_state(i2c_bus, ST_BUS_BUSY);
> if (ret < 0)
> return ret;
>
> temp |= I2CR_MTX | I2CR_TX_NO_AK;
> - writeb(temp, &i2c_regs->i2cr);
> + writeb(temp, base + (I2CR << reg_shift));
>
> /* write slave address */
> - ret = tx_byte(i2c_regs, chip << 1);
> + ret = tx_byte(i2c_bus, chip << 1);
> if (ret < 0)
> return ret;
>
> while (alen--) {
> - ret = tx_byte(i2c_regs, (addr >> (alen * 8)) & 0xff);
> + ret = tx_byte(i2c_bus, (addr >> (alen * 8)) & 0xff);
> if (ret < 0)
> return ret;
> }
> return 0;
> }
>
> -static int i2c_idle_bus(void *base);
> +static int i2c_idle_bus(struct mxc_i2c_bus *i2c_bus)
> +{
> + if (i2c_bus && i2c_bus->idle_bus_fn)
> + return i2c_bus->idle_bus_fn(i2c_bus->idle_bus_data);
> + return 0;
> +}
>
> -static int i2c_init_transfer(struct mxc_i2c_regs *i2c_regs,
> - uchar chip, uint addr, int alen)
> +
> +static int i2c_init_transfer(struct mxc_i2c_bus *i2c_bus, u8 chip,
> + u32 addr, int alen)
> {
> int retry;
> int ret;
> + int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
> + VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
> for (retry = 0; retry < 3; retry++) {
> - ret = i2c_init_transfer_(i2c_regs, chip, addr, alen);
> + ret = i2c_init_transfer_(i2c_bus, chip, addr, alen);
> if (ret >= 0)
> return 0;
> - i2c_imx_stop(i2c_regs);
> + i2c_imx_stop(i2c_bus);
> if (ret == -ENODEV)
> return ret;
>
> @@ -310,54 +359,67 @@ static int i2c_init_transfer(struct mxc_i2c_regs *i2c_regs,
> retry);
> if (ret != -ERESTART)
> /* Disable controller */
> - writeb(I2CR_IDIS, &i2c_regs->i2cr);
> + writeb(I2CR_IDIS, i2c_bus->base + (I2CR << reg_shift));
> udelay(100);
> - if (i2c_idle_bus(i2c_regs) < 0)
> + if (i2c_idle_bus(i2c_bus) < 0)
> break;
> }
> - printf("%s: give up i2c_regs=%p\n", __func__, i2c_regs);
> + printf("%s: give up i2c_regs=0x%lx\n", __func__, i2c_bus->base);
> return ret;
> }
>
> -/*
> - * Read data from I2C device
> - */
> -int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf,
> - int len)
> +
> +static int i2c_write_data(struct mxc_i2c_bus *i2c_bus, u8 chip, const u8 *buf,
> + int len)
> +{
> + int i, ret = 0;
> +
> + debug("i2c_write_data: chip=0x%x, len=0x%x\n", chip, len);
> + debug("write_data: ");
> + /* use rc for counter */
> + for (i = 0; i < len; ++i)
> + debug(" 0x%02x", buf[i]);
> + debug("\n");
> +
> + for (i = 0; i < len; i++) {
> + ret = tx_byte(i2c_bus, buf[i]);
> + if (ret < 0) {
> + debug("i2c_write_data(): rc=%d\n", ret);
> + break;
> + }
> + }
> +
> + return ret;
> +}
> +
> +static int i2c_read_data(struct mxc_i2c_bus *i2c_bus, uchar chip, uchar *buf,
> + int len)
> {
> int ret;
> unsigned int temp;
> int i;
> - struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base;
> + int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
> + VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
> + ulong base = i2c_bus->base;
>
> - ret = i2c_init_transfer(i2c_regs, chip, addr, alen);
> - if (ret < 0)
> - return ret;
> -
> - temp = readb(&i2c_regs->i2cr);
> - temp |= I2CR_RSTA;
> - writeb(temp, &i2c_regs->i2cr);
> -
> - ret = tx_byte(i2c_regs, (chip << 1) | 1);
> - if (ret < 0) {
> - i2c_imx_stop(i2c_regs);
> - return ret;
> - }
> + debug("i2c_read_data: chip=0x%x, len=0x%x\n", chip, len);
>
> /* setup bus to read data */
> - temp = readb(&i2c_regs->i2cr);
> + temp = readb(base + (I2CR << reg_shift));
> temp &= ~(I2CR_MTX | I2CR_TX_NO_AK);
> if (len == 1)
> temp |= I2CR_TX_NO_AK;
> - writeb(temp, &i2c_regs->i2cr);
> - writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr);
> - readb(&i2c_regs->i2dr); /* dummy read to clear ICF */
> + writeb(temp, base + (I2CR << reg_shift));
> + writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
> + /* dummy read to clear ICF */
> + readb(base + (I2DR << reg_shift));
>
> /* read data */
> for (i = 0; i < len; i++) {
> - ret = wait_for_sr_state(i2c_regs, ST_IIF);
> + ret = wait_for_sr_state(i2c_bus, ST_IIF);
> if (ret < 0) {
> - i2c_imx_stop(i2c_regs);
> + debug("i2c_read_data(): ret=%d\n", ret);
> + i2c_imx_stop(i2c_bus);
> return ret;
> }
>
> @@ -366,105 +428,111 @@ int bus_i2c_read(void *base, uchar chip, uint addr, int alen, uchar *buf,
> * controller from generating another clock cycle
> */
> if (i == (len - 1)) {
> - i2c_imx_stop(i2c_regs);
> + i2c_imx_stop(i2c_bus);
> } else if (i == (len - 2)) {
> - temp = readb(&i2c_regs->i2cr);
> + temp = readb(base + (I2CR << reg_shift));
> temp |= I2CR_TX_NO_AK;
> - writeb(temp, &i2c_regs->i2cr);
> + writeb(temp, base + (I2CR << reg_shift));
> }
> - writeb(I2SR_IIF_CLEAR, &i2c_regs->i2sr);
> - buf[i] = readb(&i2c_regs->i2dr);
> + writeb(I2SR_IIF_CLEAR, base + (I2SR << reg_shift));
> + buf[i] = readb(base + (I2DR << reg_shift));
> }
> - i2c_imx_stop(i2c_regs);
> +
> + /* reuse ret for counter*/
> + for (ret = 0; ret < len; ++ret)
> + debug(" 0x%02x", buf[ret]);
> + debug("\n");
> +
> + i2c_imx_stop(i2c_bus);
> return 0;
> }
>
> +#ifndef CONFIG_DM_I2C
> /*
> - * Write data to I2C device
> + * Read data from I2C device
> */
> -int bus_i2c_write(void *base, uchar chip, uint addr, int alen,
> - const uchar *buf, int len)
> +static int bus_i2c_read(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr,
> + int alen, u8 *buf, int len)
> {
> - int ret;
> - int i;
> - struct mxc_i2c_regs *i2c_regs = (struct mxc_i2c_regs *)base;
> + int ret = 0;
> + u32 temp;
> + int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
> + VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
> + ulong base = i2c_bus->base;
>
> - ret = i2c_init_transfer(i2c_regs, chip, addr, alen);
> + ret = i2c_init_transfer(i2c_bus, chip, addr, alen);
> if (ret < 0)
> return ret;
>
> - for (i = 0; i < len; i++) {
> - ret = tx_byte(i2c_regs, buf[i]);
> - if (ret < 0)
> - break;
> + temp = readb(base + (I2CR << reg_shift));
> + temp |= I2CR_RSTA;
> + writeb(temp, base + (I2CR << reg_shift));
> +
> + ret = tx_byte(i2c_bus, (chip << 1) | 1);
> + if (ret < 0) {
> + i2c_imx_stop(i2c_bus);
> + return ret;
> }
> - i2c_imx_stop(i2c_regs);
> +
> + ret = i2c_read_data(i2c_bus, chip, buf, len);
> +
> + i2c_imx_stop(i2c_bus);
> return ret;
> }
>
> -static void * const i2c_bases[] = {
> +/*
> + * Write data to I2C device
> + */
> +static int bus_i2c_write(struct mxc_i2c_bus *i2c_bus, u8 chip, u32 addr,
> + int alen, const u8 *buf, int len)
> +{
> + int ret = 0;
> +
> + ret = i2c_init_transfer(i2c_bus, chip, addr, alen);
> + if (ret < 0)
> + return ret;
> +
> + ret = i2c_write_data(i2c_bus, chip, buf, len);
> +
> + i2c_imx_stop(i2c_bus);
> +
> + return ret;
> +}
> +
> +static struct mxc_i2c_bus mxc_i2c_buses[] = {
> #if defined(CONFIG_MX25)
> - (void *)IMX_I2C_BASE,
> - (void *)IMX_I2C2_BASE,
> - (void *)IMX_I2C3_BASE
> + { IMX_I2C_BASE, 0 },
> + { IMX_I2C2_BASE, 0 },
> + { IMX_I2C3_BASE, 0 },
> #elif defined(CONFIG_MX27)
> - (void *)IMX_I2C1_BASE,
> - (void *)IMX_I2C2_BASE
> + { IMX_I2C1_BASE, 0 },
> + { IMX_I2C2_BASE, 0 },
> #elif defined(CONFIG_MX31) || defined(CONFIG_MX35) || \
> defined(CONFIG_MX51) || defined(CONFIG_MX53) || \
> - defined(CONFIG_MX6) || defined(CONFIG_LS102XA)
> - (void *)I2C1_BASE_ADDR,
> - (void *)I2C2_BASE_ADDR,
> - (void *)I2C3_BASE_ADDR
> + defined(CONFIG_MX6)
> + { I2C1_BASE_ADDR, 0 },
> + { I2C2_BASE_ADDR, 0 },
> + { I2C3_BASE_ADDR, 0 },
> +#elif defined(CONFIG_LS102XA)
> + { I2C1_BASE_ADDR, I2C_QUIRK_FLAG },
> + { I2C2_BASE_ADDR, I2C_QUIRK_FLAG },
> + { I2C3_BASE_ADDR, I2C_QUIRK_FLAG },
> #elif defined(CONFIG_VF610)
> - (void *)I2C0_BASE_ADDR
> + { I2C0_BASE_ADDR, I2C_QUIRK_FLAG },
> #elif defined(CONFIG_FSL_LSCH3)
> - (void *)I2C1_BASE_ADDR,
> - (void *)I2C2_BASE_ADDR,
> - (void *)I2C3_BASE_ADDR,
> - (void *)I2C4_BASE_ADDR
> + { I2C1_BASE_ADDR, I2C_QUIRK_FLAG },
> + { I2C2_BASE_ADDR, I2C_QUIRK_FLAG },
> + { I2C3_BASE_ADDR, I2C_QUIRK_FLAG },
> + { I2C4_BASE_ADDR, I2C_QUIRK_FLAG },
> #else
> #error "architecture not supported"
> #endif
> + { }
> };
>
> -struct i2c_parms {
> - void *base;
> - void *idle_bus_data;
> - int (*idle_bus_fn)(void *p);
> -};
> -
> -struct sram_data {
> - unsigned curr_i2c_bus;
> - struct i2c_parms i2c_data[ARRAY_SIZE(i2c_bases)];
> -};
> -
> -void *i2c_get_base(struct i2c_adapter *adap)
> +struct mxc_i2c_bus *i2c_get_base(struct i2c_adapter *adap)
> {
> - return i2c_bases[adap->hwadapnr];
> -}
> -
> -static struct i2c_parms *i2c_get_parms(void *base)
> -{
> - struct sram_data *srdata = (void *)gd->srdata;
> - int i = 0;
> - struct i2c_parms *p = srdata->i2c_data;
> - while (i < ARRAY_SIZE(srdata->i2c_data)) {
> - if (p->base == base)
> - return p;
> - p++;
> - i++;
> - }
> - printf("Invalid I2C base: %p\n", base);
> - return NULL;
> -}
> -
> -static int i2c_idle_bus(void *base)
> -{
> - struct i2c_parms *p = i2c_get_parms(base);
> - if (p && p->idle_bus_fn)
> - return p->idle_bus_fn(p->idle_bus_data);
> - return 0;
> + return &mxc_i2c_buses[adap->hwadapnr];
> }
>
> static int mxc_i2c_read(struct i2c_adapter *adap, uint8_t chip,
> @@ -489,29 +557,18 @@ static int mxc_i2c_probe(struct i2c_adapter *adap, uint8_t chip)
> return bus_i2c_write(i2c_get_base(adap), chip, 0, 0, NULL, 0);
> }
>
> -void bus_i2c_init(void *base, int speed, int unused,
> - int (*idle_bus_fn)(void *p), void *idle_bus_data)
> +void bus_i2c_init(int index, int speed, int unused,
> + int (*idle_bus_fn)(void *p), void *idle_bus_data)
> {
> - struct sram_data *srdata = (void *)gd->srdata;
> - int i = 0;
> - struct i2c_parms *p = srdata->i2c_data;
> - if (!base)
> + if (index >= ARRAY_SIZE(mxc_i2c_buses)) {
> + debug("Error i2c index\n");
> return;
> - for (;;) {
> - if (!p->base || (p->base == base)) {
> - p->base = base;
> - if (idle_bus_fn) {
> - p->idle_bus_fn = idle_bus_fn;
> - p->idle_bus_data = idle_bus_data;
> - }
> - break;
> - }
> - p++;
> - i++;
> - if (i >= ARRAY_SIZE(srdata->i2c_data))
> - return;
> }
> - bus_i2c_set_bus_speed(base, speed);
> +
> + mxc_i2c_buses[index].idle_bus_fn = idle_bus_fn;
> + mxc_i2c_buses[index].idle_bus_data = idle_bus_data;
> +
> + bus_i2c_set_bus_speed(&mxc_i2c_buses[index], speed);
> }
>
> /*
> @@ -519,13 +576,13 @@ void bus_i2c_init(void *base, int speed, int unused,
> */
> static void mxc_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
> {
> - bus_i2c_init(i2c_get_base(adap), speed, slaveaddr, NULL, NULL);
> + bus_i2c_init(adap->hwadapnr, speed, slaveaddr, NULL, NULL);
> }
>
> /*
> * Set I2C Speed
> */
> -static uint mxc_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed)
> +static u32 mxc_i2c_set_bus_speed(struct i2c_adapter *adap, uint speed)
> {
> return bus_i2c_set_bus_speed(i2c_get_base(adap), speed);
> }
> @@ -552,3 +609,161 @@ U_BOOT_I2C_ADAP_COMPLETE(mxc2, mxc_i2c_init, mxc_i2c_probe,
> CONFIG_SYS_MXC_I2C3_SPEED,
> CONFIG_SYS_MXC_I2C3_SLAVE, 2)
> #endif
> +
> +#else
> +
> +static int mxc_i2c_set_bus_speed(struct udevice *bus, unsigned int speed)
> +{
> + struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
> +
> + return bus_i2c_set_bus_speed(i2c_bus, speed);
> +}
> +
> +static int mxc_i2c_probe(struct udevice *bus)
> +{
> + struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
> + fdt_addr_t addr;
> +
> + i2c_bus->driver_data = dev_get_driver_data(bus);
> +
> + addr = dev_get_addr(bus);
> + if (addr == FDT_ADDR_T_NONE)
> + return -ENODEV;
> +
> + i2c_bus->base = addr;
> +
> + /*
> + * Pinmux settings are in board file now, until pinmux is supported,
> + * we can set pinmux here in probe function.
> + */
> +
> + debug("i2c : controller bus %d at %lu , speed %d: ",
> + bus->seq, i2c_bus->base,
> + i2c_bus->speed);
> +
> + return 0;
> +}
> +
> +/*
> + * This function is for setup_i2c.We need idle_bus_fn and pinmux setting.
> + * When something wrong happens during data transfer, we can use gpio
> + * mode to force i2c bus idle.
> + * This way not friend to DM. Since arch/arm/imx-common/i2c-mxv7.c is used
> + * for all i.MX platforms, it is not easy to refactor it for DM. So,
> + * implement bus_i2c_init here temporarily.
> + *
> + * In future, when we can remove setup_i2c from board file, bus_i2c_init
> + * can be removed.
> + */
> +void bus_i2c_init(int busnum, int speed, int slave, int (*idle_bus_fn)(void *p),
> + void *idle_bus_data)
> +{
> + struct udevice *bus;
> + struct mxc_i2c_bus *i2c_bus;
> + int ret;
> +
> + ret = uclass_get_device_by_seq(UCLASS_I2C, busnum, &bus);
> + if (ret) {
> + debug("Cannot find I2C bus %d\n", busnum);
> + return;
> + }
> +
> + i2c_bus = dev_get_priv(bus);
> +
> + i2c_bus->idle_bus_fn = idle_bus_fn;
> + i2c_bus->idle_bus_data = idle_bus_data;
> +
> + return;
> +}
> +
> +static int mxc_i2c_probe_chip(struct udevice *bus, u32 chip_addr,
> + u32 chip_flags)
> +{
> + int ret;
> + struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
> +
> + ret = i2c_init_transfer(i2c_bus, chip_addr, 0, 0);
> + if (ret < 0) {
> + debug("%s failed, ret = %d\n", __func__, ret);
> + return ret;
> + }
> +
> + i2c_imx_stop(i2c_bus);
> +
> + return 0;
> +}
> +
> +static int mxc_i2c_xfer(struct udevice *bus, struct i2c_msg *msg, int nmsgs)
> +{
> + struct mxc_i2c_bus *i2c_bus = dev_get_priv(bus);
> + int ret = 0;
> + ulong base = i2c_bus->base;
> + int reg_shift = i2c_bus->driver_data & I2C_QUIRK_FLAG ?
> + VF610_I2C_REGSHIFT : IMX_I2C_REGSHIFT;
> +
> + /*
> + * Here the 3rd parameter addr and the 4th one alen are set to 0,
> + * because here we only want to send out chip address. The register
> + * address is wrapped in msg.
> + */
> + ret = i2c_init_transfer(i2c_bus, msg->addr, 0, 0);
> + if (ret < 0) {
> + debug("i2c_init_transfer error: %d\n", ret);
> + return ret;
> + }
> +
> + for (; nmsgs > 0; nmsgs--, msg++) {
> + bool next_is_read = nmsgs > 1 && (msg[1].flags & I2C_M_RD);
> + debug("i2c_xfer: chip=0x%x, len=0x%x\n", msg->addr, msg->len);
> + if (msg->flags & I2C_M_RD)
> + ret = i2c_read_data(i2c_bus, msg->addr, msg->buf,
> + msg->len);
> + else {
> + ret = i2c_write_data(i2c_bus, msg->addr, msg->buf,
> + msg->len);
> + if (ret)
> + break;
> + if (next_is_read) {
> + /* Reuse ret */
> + ret = readb(base + (I2CR << reg_shift));
> + ret |= I2CR_RSTA;
> + writeb(ret, base + (I2CR << reg_shift));
> +
> + ret = tx_byte(i2c_bus, (msg->addr << 1) | 1);
> + if (ret < 0) {
> + i2c_imx_stop(i2c_bus);
> + break;
> + }
> + }
> + }
> + }
> +
> + if (ret)
> + debug("i2c_write: error sending\n");
> +
> + i2c_imx_stop(i2c_bus);
> +
> + return ret;
> +}
> +
> +static const struct dm_i2c_ops mxc_i2c_ops = {
> + .xfer = mxc_i2c_xfer,
> + .probe_chip = mxc_i2c_probe_chip,
> + .set_bus_speed = mxc_i2c_set_bus_speed,
> +};
> +
> +static const struct udevice_id mxc_i2c_ids[] = {
> + { .compatible = "fsl,imx21-i2c", },
> + { .compatible = "fsl,vf610-i2c", .data = I2C_QUIRK_FLAG, },
> + {}
> +};
> +
> +U_BOOT_DRIVER(i2c_mxc) = {
> + .name = "i2c_mxc",
> + .id = UCLASS_I2C,
> + .of_match = mxc_i2c_ids,
> + .probe = mxc_i2c_probe,
> + .priv_auto_alloc_size = sizeof(struct mxc_i2c_bus),
> + .ops = &mxc_i2c_ops,
> +};
> +#endif
Regards,
Peng.
prev parent reply other threads:[~2015-04-26 7:38 UTC|newest]
Thread overview: 2+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-04-25 13:04 [U-Boot] [PATCH V2] i2c: mxc: refactor i2c driver and support dm Peng Fan
2015-04-26 7:38 ` Peng Fan [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=553C9600.6020605@freescale.com \
--to=peng.fan@freescale.com \
--cc=u-boot@lists.denx.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.