[U-Boot] [PATCH v4] i2c: s3c24xx: add hsi2c controller support

Naveen Krishna Ch naveenkrishna.ch at gmail.com
Sat Apr 13 06:42:15 CEST 2013


On 6 April 2013 07:07, Naveen Krishna Chatradhi
<naveenkrishna.ch at gmail.com> wrote:
> 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 <ch.naveen at samsung.com>
> Signed-off-by: R. Chandrasekar <rc.sekar at samsung.com>
> Reviewed-by: Vadim Bendebury <vbendeb at google.com>
> Reviewed-by: Simon Glass <sjg at google.com>
> ---
> Changes since v3:
>
> 1. Implemented get_timer instead of while and udelay for master busy function
> 2. Use reg base address from device tree
> 3. Split the timing function to check for the errors
> 4. Implemented reset function for to recover from failure cases
> 5. Implemented a comat string for hsi2c to distingush the channels
> 6. Minor cosmotic changes
>
> Note: FIFOs will be implemented in subsequent patches
>
>  drivers/i2c/s3c24x0_i2c.c |  494 +++++++++++++++++++++++++++++++++++++++++----
>  drivers/i2c/s3c24x0_i2c.h |   36 ++++
>  2 files changed, 486 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/i2c/s3c24x0_i2c.c b/drivers/i2c/s3c24x0_i2c.c
> index 46d2506..32be91b 100644
> --- a/drivers/i2c/s3c24x0_i2c.c
> +++ b/drivers/i2c/s3c24x0_i2c.c
> @@ -50,6 +50,60 @@
>  #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_INT_TX_ALMOSTEMPTY_EN    (1u << 0)
> +#define HSI2C_INT_RX_ALMOSTFULL_EN     (1u << 1)
> +#define HSI2C_INT_TRAILING_EN          (1u << 6)
> +#define HSI2C_INT_I2C_EN               (1u << 9)
> +
> +/* 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_NO_DEV                   (1u << 3)
> +#define HSI2C_NO_DEV_ACK               (1u << 2)
> +#define HSI2C_TRANS_ABORT              (1u << 1)
> +#define HSI2C_TRANS_DONE               (1u << 0)
> +#define HSI2C_TIMEOUT_AUTO             (0u << 0)
> +
> +#define HSI2C_SLV_ADDR_MAS(x)          ((x & 0x3ff) << 10)
> +
> +/* Controller operating frequency, timing values for operation
> + * are calculated against this frequency
> + */
> +#define HSI2C_FS_TX_CLOCK              1000000
> +
> +/* S3C I2C Controller bits */
>  #define I2CSTAT_BSY    0x20    /* Busy bit */
>  #define I2CSTAT_NACK   0x01    /* Nack bit */
>  #define I2CCON_ACKGEN  0x80    /* Acknowledge generation */
> @@ -61,6 +115,7 @@
>
>  #define I2C_TIMEOUT 1          /* 1 second */
>
> +#define        HSI2C_TIMEOUT   100
>
>  /*
>   * For SPL boot some boards need i2c before SDRAM is initialised so force
> @@ -73,6 +128,15 @@ static struct s3c24x0_i2c_bus i2c_bus[CONFIG_MAX_I2C_NUM]
>                         __attribute__((section(".data")));
>  #endif
>
> +static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx)
> +{
> +       if (bus_idx < i2c_busses)
> +               return &i2c_bus[bus_idx];
> +
> +       debug("Undefined bus: %d\n", bus_idx);
> +       return NULL;
> +}
> +
>  #if !(defined CONFIG_EXYNOS4 || defined CONFIG_EXYNOS5)
>  static int GetI2CSDA(void)
>  {
> @@ -113,9 +177,23 @@ static int WaitForXfer(struct s3c24x0_i2c *i2c)
>         return (readl(&i2c->iiccon) & I2CCON_IRPND) ? I2C_OK : I2C_NOK_TOUT;
>  }
>
> -static int IsACK(struct s3c24x0_i2c *i2c)
> +static int hsi2c_wait_for_irq(struct exynos5_hsi2c *i2c)
>  {
> -       return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
> +       int i = HSI2C_TIMEOUT * 10;
> +       int ret = I2C_NOK_TOUT;
> +
> +       while (i > 0) {
> +               /* wait for a while and retry */
> +               udelay(100);
> +               if (readl(&i2c->usi_int_stat) &
> +                       (HSI2C_INT_I2C_EN | HSI2C_INT_TX_ALMOSTEMPTY_EN)) {
> +                       ret = I2C_OK;
> +                       break;
> +               }
> +               i--;
> +       }
> +
> +       return ret;
>  }
>
>  static void ReadWriteByte(struct s3c24x0_i2c *i2c)
> @@ -123,6 +201,22 @@ static void ReadWriteByte(struct s3c24x0_i2c *i2c)
>         writel(readl(&i2c->iiccon) & ~I2CCON_IRPND, &i2c->iiccon);
>  }
>
> +static void hsi2c_clear_irqpd(struct exynos5_hsi2c *i2c)
> +{
> +       writel(readl(&i2c->usi_int_stat), &i2c->usi_int_stat);
> +}
> +
> +static int hsi2c_isack(struct exynos5_hsi2c *i2c)
> +{
> +       return readl(&i2c->usi_trans_status) &
> +                       (HSI2C_NO_DEV | HSI2C_NO_DEV_ACK);
> +}
> +
> +static int IsACK(struct s3c24x0_i2c *i2c)
> +{
> +       return !(readl(&i2c->iicstat) & I2CSTAT_NACK);
> +}
> +
>  static struct s3c24x0_i2c *get_base_i2c(void)
>  {
>  #ifdef CONFIG_EXYNOS4
> @@ -167,6 +261,97 @@ static void i2c_ch_init(struct s3c24x0_i2c *i2c, int speed, int slaveadd)
>         writel(I2C_MODE_MT | I2C_TXRX_ENA, &i2c->iicstat);
>  }
>
> +static int hsi2c_get_clk_details(struct s3c24x0_i2c_bus *i2c_bus)
> +{
> +       struct exynos5_hsi2c *hsregs = i2c_bus->hsregs;
> +       ulong clkin = get_i2c_clk();
> +       unsigned int op_clk = HSI2C_FS_TX_CLOCK;
> +       unsigned int i = 0, utemp0 = 0, utemp1 = 0;
> +       unsigned int t_ftl_cycle;
> +
> +       /* 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;
> +}
> +
> +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);
> +
> +       writel(readl(&hsregs->usi_conf) | HSI2C_AUTO_MODE, &hsregs->usi_conf);
> +
> +       /* 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
>   */
> @@ -174,7 +359,7 @@ 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;
>
>         if ((bus < 0) || (bus >= CONFIG_MAX_I2C_NUM)) {
>                 debug("Bad bus: %d\n", bus);
> @@ -182,8 +367,16 @@ int i2c_set_bus_num(unsigned int bus)
>         }
>
>         g_current_bus = bus;
> -       i2c = get_base_i2c();
> -       i2c_ch_init(i2c, CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
> +       i2c_bus = get_bus(i2c_get_bus_num());
> +
> +       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, CONFIG_SYS_I2C_SPEED,
> +                           CONFIG_SYS_I2C_SLAVE);
> +       }
>
>         return 0;
>  }
> @@ -261,6 +454,165 @@ void i2c_init(int speed, int slaveadd)
>         i2c_ch_init(i2c, speed, slaveadd);
>  }
>
> +static int hsi2c_master_busy(struct exynos5_hsi2c *i2c)
> +{
> +       ulong start = get_timer(0);
> +
> +       /* Check I2C bus idle */
> +       while ((readl(&i2c->usi_trans_status) & HSI2C_MASTER_BUSY)) {
> +               if (get_timer(start) > (HSI2C_TIMEOUT))
> +                       return I2C_NOK_TOUT;
> +               udelay(10);
> +       }
> +
> +       return I2C_OK;
> +}
> +
> +/*
> + * Send a STOP event and wait for it to have completed
> + *
> + * @param mode If it is a master transmitter or receiver
> + * @return I2C_OK if the line became idle before timeout I2C_NOK_TOUT otherwise
> + */
> +static int hsi2c_send_stop(struct exynos5_hsi2c *i2c, int result)
> +{
> +       int ret = hsi2c_master_busy(i2c);
> +
> +       /* Setting the STOP event to fire */
> +       writel(HSI2C_FUNC_MODE_I2C, &i2c->usi_ctl);
> +       writel(0x0, &i2c->usi_int_en);
> +
> +       return (result == I2C_OK) ? ret : result;
> +}
> +
> +static int hsi2c_write(struct exynos5_hsi2c *i2c,
> +                       unsigned char chip,
> +                       unsigned char addr[],
> +                       unsigned char alen,
> +                       unsigned char data[],
> +                       unsigned short len)
> +{
> +       int i = 0, result = I2C_OK;
> +       u32 i2c_auto_conf;
> +       int ret = hsi2c_master_busy(i2c);
> +
> +       if (ret) {
> +               debug("%s: bus busy\n", __func__);
> +               return I2C_NOK_TOUT;
> +       }
> +
> +       /* Disable TXFIFO and RXFIFO */
> +       writel(0, &i2c->usi_fifo_ctl);
> +
> +       /* chip address */
> +       writel(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr);
> +
> +       /* Enable interrupts */
> +       writel((HSI2C_INT_I2C_EN |
> +               HSI2C_INT_TX_ALMOSTEMPTY_EN), &i2c->usi_int_en);
> +
> +       /* usi_ctl enable i2c func, master write configure */
> +       writel((HSI2C_TXCHON | HSI2C_FUNC_MODE_I2C |
> +               HSI2C_MASTER), &i2c->usi_ctl);
> +
> +       /* i2c_conf configure */
> +       writel(readl(&i2c->usi_conf) |
> +              HSI2C_AUTO_MODE, &i2c->usi_conf);
> +
> +       /* auto_conf for write length and stop configure */
> +       i2c_auto_conf = ((len + alen) | HSI2C_STOP_AFTER_TRANS);
> +       i2c_auto_conf &= ~HSI2C_READ_WRITE;
> +       writel(i2c_auto_conf, &i2c->usi_auto_conf);
> +
> +       /* Master run, start xfer */
> +       writel(readl(&i2c->usi_auto_conf) |
> +              HSI2C_MASTER_RUN, &i2c->usi_auto_conf);
> +
> +       result = hsi2c_wait_for_irq(i2c);
> +       if ((result == I2C_OK) && hsi2c_isack(i2c)) {
> +               result = I2C_NACK;
> +               goto err;
> +       }
> +
> +       for (i = 0; i < alen && (result == I2C_OK); i++) {
> +               writel(addr[i], &i2c->usi_txdata);
> +               result = hsi2c_wait_for_irq(i2c);
> +       }
> +
> +       for (i = 0; i < len && (result == I2C_OK); i++) {
> +               writel(data[i], &i2c->usi_txdata);
> +               result = hsi2c_wait_for_irq(i2c);
> +       }
> +
> + err:
> +       hsi2c_clear_irqpd(i2c);
> +       return hsi2c_send_stop(i2c, result);
> +}
> +
> +static int hsi2c_read(struct exynos5_hsi2c *i2c,
> +                       unsigned char chip,
> +                       unsigned char addr[],
> +                       unsigned char alen,
> +                       unsigned char data[],
> +                       unsigned short len,
> +                       int check)
> +{
> +       int i, result;
> +       u32 i2c_auto_conf;
> +
> +       if (!check) {
> +               result =  hsi2c_write(i2c, chip, addr, alen, data, 0);
> +               if (result != I2C_OK) {
> +                       debug("write failed Result = %d\n", result);
> +                       return result;
> +               }
> +       }
> +
> +       /* start read */
> +       /* Disable TXFIFO and RXFIFO */
> +       writel(0, &i2c->usi_fifo_ctl);
> +
> +       /* chip address */
> +       writel(HSI2C_SLV_ADDR_MAS(chip), &i2c->i2c_addr);
> +
> +       /* Enable interrupts */
> +       writel(HSI2C_INT_I2C_EN, &i2c->usi_int_en);
> +
> +       /* i2c_conf configure */
> +       writel(readl(&i2c->usi_conf) |
> +                    HSI2C_AUTO_MODE, &i2c->usi_conf);
> +
> +       /* auto_conf, length and stop configure */
> +       i2c_auto_conf =
> +               (len | HSI2C_STOP_AFTER_TRANS | HSI2C_READ_WRITE);
> +
> +       writel(i2c_auto_conf, &i2c->usi_auto_conf);
> +
> +       /* usi_ctl enable i2c func, master WRITE configure */
> +       writel((HSI2C_RXCHON | HSI2C_FUNC_MODE_I2C |
> +               HSI2C_MASTER), &i2c->usi_ctl);
> +
> +       /* Master run, start xfer */
> +       writel(readl(&i2c->usi_auto_conf) |
> +              HSI2C_MASTER_RUN, &i2c->usi_auto_conf);
> +
> +       result = hsi2c_wait_for_irq(i2c);
> +       if ((result == I2C_OK) && hsi2c_isack(i2c)) {
> +               result = I2C_NACK;
> +               goto err;
> +       }
> +
> +       for (i = 0; i < len && (result == I2C_OK); i++) {
> +               result = hsi2c_wait_for_irq(i2c);
> +               data[i] = readl(&i2c->usi_rxdata);
> +               udelay(100);
> +       }
> + err:
> +       /* Stop and quit */
> +       hsi2c_clear_irqpd(i2c);
> +       return hsi2c_send_stop(i2c, result);
> +}
> +
>  /*
>   * cmd_type is 0 for write, 1 for read.
>   *
> @@ -279,7 +631,6 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
>         int i, result;
>
>         if (data == 0 || data_len == 0) {
> -               /*Don't support data transfer of no length or to address 0 */
>                 debug("i2c_transfer: bad call\n");
>                 return I2C_NOK;
>         }
> @@ -419,23 +770,29 @@ static int i2c_transfer(struct s3c24x0_i2c *i2c,
>
>  int i2c_probe(uchar chip)
>  {
> -       struct s3c24x0_i2c *i2c;
> +       struct s3c24x0_i2c_bus *i2c;
>         uchar buf[1];
> +       int ret;
>
> -       i2c = get_base_i2c();
> +       i2c = get_bus(g_current_bus);
> +       if (!i2c)
> +               return -1;
>         buf[0] = 0;
>
> -       /*
> -        * What is needed is to send the chip address and verify that the
> -        * address was <ACK>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->is_highspeed) {
> +               ret = hsi2c_read(i2c->hsregs,
> +                               chip, 0, 1, buf, 1, 1);
> +       } else {
> +               ret = i2c_transfer(i2c->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;
>
> @@ -467,10 +824,19 @@ 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) {
> +       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, 0);
> +       else
> +               ret = i2c_transfer(i2c_bus->regs, I2C_READ, chip << 1,
> +                               &xaddr[4 - alen], alen, buffer, len);
> +
> +       if (ret) {
> +               exynos5_i2c_reset(i2c_bus);
>                 debug("I2c read: failed %d\n", ret);
>                 return 1;
>         }
> @@ -479,8 +845,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);
> @@ -509,45 +876,77 @@ 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);
> +       else
> +               ret = i2c_transfer(i2c_bus->regs, I2C_WRITE, chip << 1,
> +                               &xaddr[4 - alen], alen, buffer, len);
> +
> +
> +       if (ret != 0) {
> +               exynos5_i2c_reset(i2c_bus);
> +               return 1;
> +       }
> +
> +       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)
>  {
> -       int node_list[CONFIG_MAX_I2C_NUM];
> -       int count, i;
> -
> -       count = fdtdec_find_aliases_for_id(blob, "i2c",
> -               COMPAT_SAMSUNG_S3C2440_I2C, node_list,
> -               CONFIG_MAX_I2C_NUM);
> +       struct s3c24x0_i2c_bus *bus;
> +       int i;
>
>         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->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->node = node;
>                 bus->bus_num = i2c_busses++;
>                 exynos_pinmux_config(bus->id, 0);
> +
> +               /* Mark position as used */
> +               node_list[i] = -1;
>         }
>  }
>
> -static struct s3c24x0_i2c_bus *get_bus(unsigned int bus_idx)
> +#ifdef CONFIG_OF_CONTROL
> +void board_i2c_init(const void *blob)
>  {
> -       if (bus_idx < i2c_busses)
> -               return &i2c_bus[bus_idx];
> +       int node_list[CONFIG_MAX_I2C_NUM];
> +       int count;
>
> -       debug("Undefined bus: %d\n", bus_idx);
> -       return NULL;
> +       /* 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);
> +
> +       return;
>  }
>
>  int i2c_get_bus_num_fdt(int node)
> @@ -565,7 +964,7 @@ int i2c_get_bus_num_fdt(int node)
>
>  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);
> @@ -574,13 +973,20 @@ 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,
> +                           CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
> +       }
>
>         return 0;
>  }
> diff --git a/drivers/i2c/s3c24x0_i2c.h b/drivers/i2c/s3c24x0_i2c.h
> index a56d749..55931e8 100644
> --- a/drivers/i2c/s3c24x0_i2c.h
> +++ b/drivers/i2c/s3c24x0_i2c.h
> @@ -31,10 +31,46 @@ 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 {
>         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 */
>         int id;
> +       unsigned clk_cycle;
> +       unsigned clk_div;
>  };
>  #endif /* _S3C24X0_I2C_H */
> --
> 1.7.9.5
Hello all i got it review by Simon and Vadim.
Any updates on this driver please
>



--
Shine bright,
(: Nav :)


More information about the U-Boot mailing list