]> git.kernelconcepts.de Git - karo-tx-uboot.git/blobdiff - drivers/i2c/designware_i2c.c
Merge branch 'master' of git://git.denx.de/u-boot-imx
[karo-tx-uboot.git] / drivers / i2c / designware_i2c.c
index bf64a2a643ab2871c053188d7f3f8dd061aaeb58..e085a7095eaf55ef62bc9c47283bcfe20636740a 100644 (file)
@@ -2,31 +2,20 @@
  * (C) Copyright 2009
  * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
  *
- * See file CREDITS for list of people who contributed to this
- * project.
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License as
- * published by the Free Software Foundation; either version 2 of
- * the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
- * MA 02111-1307 USA
+ * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <asm/io.h>
-#include <asm/arch/hardware.h>
 #include "designware_i2c.h"
+#include <i2c.h>
 
-static struct i2c_regs *const i2c_regs_p =
+#ifdef CONFIG_I2C_MULTI_BUS
+static unsigned int bus_initialized[CONFIG_SYS_I2C_BUS_MAX];
+static unsigned int current_bus = 0;
+#endif
+
+static struct i2c_regs *i2c_regs_p =
     (struct i2c_regs *)CONFIG_SYS_I2C_BASE;
 
 /*
@@ -39,7 +28,6 @@ static void set_speed(int i2c_spd)
 {
        unsigned int cntl;
        unsigned int hcnt, lcnt;
-       unsigned int high, low;
        unsigned int enbl;
 
        /* to set speed cltr must be disabled */
@@ -47,39 +35,38 @@ static void set_speed(int i2c_spd)
        enbl &= ~IC_ENABLE_0B;
        writel(enbl, &i2c_regs_p->ic_enable);
 
-
        cntl = (readl(&i2c_regs_p->ic_con) & (~IC_CON_SPD_MSK));
 
        switch (i2c_spd) {
        case IC_SPEED_MODE_MAX:
                cntl |= IC_CON_SPD_HS;
-               high = MIN_HS_SCL_HIGHTIME;
-               low = MIN_HS_SCL_LOWTIME;
+               hcnt = (IC_CLK * MIN_HS_SCL_HIGHTIME) / NANO_TO_MICRO;
+               writel(hcnt, &i2c_regs_p->ic_hs_scl_hcnt);
+               lcnt = (IC_CLK * MIN_HS_SCL_LOWTIME) / NANO_TO_MICRO;
+               writel(lcnt, &i2c_regs_p->ic_hs_scl_lcnt);
                break;
 
        case IC_SPEED_MODE_STANDARD:
                cntl |= IC_CON_SPD_SS;
-               high = MIN_SS_SCL_HIGHTIME;
-               low = MIN_SS_SCL_LOWTIME;
+               hcnt = (IC_CLK * MIN_SS_SCL_HIGHTIME) / NANO_TO_MICRO;
+               writel(hcnt, &i2c_regs_p->ic_ss_scl_hcnt);
+               lcnt = (IC_CLK * MIN_SS_SCL_LOWTIME) / NANO_TO_MICRO;
+               writel(lcnt, &i2c_regs_p->ic_ss_scl_lcnt);
                break;
 
        case IC_SPEED_MODE_FAST:
        default:
                cntl |= IC_CON_SPD_FS;
-               high = MIN_FS_SCL_HIGHTIME;
-               low = MIN_FS_SCL_LOWTIME;
+               hcnt = (IC_CLK * MIN_FS_SCL_HIGHTIME) / NANO_TO_MICRO;
+               writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt);
+               lcnt = (IC_CLK * MIN_FS_SCL_LOWTIME) / NANO_TO_MICRO;
+               writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt);
                break;
        }
 
        writel(cntl, &i2c_regs_p->ic_con);
 
-       hcnt = (IC_CLK * high) / NANO_TO_MICRO;
-       writel(hcnt, &i2c_regs_p->ic_fs_scl_hcnt);
-
-       lcnt = (IC_CLK * low) / NANO_TO_MICRO;
-       writel(lcnt, &i2c_regs_p->ic_fs_scl_lcnt);
-
-       /* re-enable i2c ctrl back now that speed is set */
+       /* Enable back i2c now speed set */
        enbl |= IC_ENABLE_0B;
        writel(enbl, &i2c_regs_p->ic_enable);
 }
@@ -90,16 +77,20 @@ static void set_speed(int i2c_spd)
  *
  * Set the i2c speed.
  */
-int i2c_set_bus_speed(int speed)
+int i2c_set_bus_speed(unsigned int speed)
 {
+       int i2c_spd;
+
        if (speed >= I2C_MAX_SPEED)
-               set_speed(IC_SPEED_MODE_MAX);
+               i2c_spd = IC_SPEED_MODE_MAX;
        else if (speed >= I2C_FAST_SPEED)
-               set_speed(IC_SPEED_MODE_FAST);
+               i2c_spd = IC_SPEED_MODE_FAST;
        else
-               set_speed(IC_SPEED_MODE_STANDARD);
+               i2c_spd = IC_SPEED_MODE_STANDARD;
 
-       return 0;
+       set_speed(i2c_spd);
+
+       return i2c_spd;
 }
 
 /*
@@ -107,7 +98,7 @@ int i2c_set_bus_speed(int speed)
  *
  * Gets the i2c speed.
  */
-int i2c_get_bus_speed(void)
+unsigned int i2c_get_bus_speed(void)
 {
        u32 cntl;
 
@@ -150,6 +141,10 @@ void i2c_init(int speed, int slaveadd)
        enbl = readl(&i2c_regs_p->ic_enable);
        enbl |= IC_ENABLE_0B;
        writel(enbl, &i2c_regs_p->ic_enable);
+
+#ifdef CONFIG_I2C_MULTI_BUS
+       bus_initialized[current_bus] = 1;
+#endif
 }
 
 /*
@@ -160,7 +155,19 @@ void i2c_init(int speed, int slaveadd)
  */
 static void i2c_setaddress(unsigned int i2c_addr)
 {
+       unsigned int enbl;
+
+       /* Disable i2c */
+       enbl = readl(&i2c_regs_p->ic_enable);
+       enbl &= ~IC_ENABLE_0B;
+       writel(enbl, &i2c_regs_p->ic_enable);
+
        writel(i2c_addr, &i2c_regs_p->ic_tar);
+
+       /* Enable i2c */
+       enbl = readl(&i2c_regs_p->ic_enable);
+       enbl |= IC_ENABLE_0B;
+       writel(enbl, &i2c_regs_p->ic_enable);
 }
 
 /*
@@ -194,35 +201,18 @@ static int i2c_wait_for_bb(void)
        return 0;
 }
 
-/* check parameters for i2c_read and i2c_write */
-static int check_params(uint addr, int alen, uchar *buffer, int len)
-{
-       if (buffer == NULL) {
-               printf("Buffer is invalid\n");
-               return 1;
-       }
-
-       if (alen > 1) {
-               printf("addr len %d not supported\n", alen);
-               return 1;
-       }
-
-       if (addr + len > 256) {
-               printf("address out of range\n");
-               return 1;
-       }
-
-       return 0;
-}
-
-static int i2c_xfer_init(uchar chip, uint addr)
+static int i2c_xfer_init(uchar chip, uint addr, int alen)
 {
        if (i2c_wait_for_bb())
                return 1;
 
        i2c_setaddress(chip);
-       writel(addr, &i2c_regs_p->ic_cmd_data);
-
+       while (alen) {
+               alen--;
+               /* high byte address going out first */
+               writel((addr >> (alen * 8)) & 0xff,
+                      &i2c_regs_p->ic_cmd_data);
+       }
        return 0;
 }
 
@@ -246,9 +236,6 @@ static int i2c_xfer_finish(void)
 
        i2c_flush_rxfifo();
 
-       /* Wait for read/write operation to complete on actual memory */
-       udelay(10000);
-
        return 0;
 }
 
@@ -266,15 +253,34 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buffer, int len)
 {
        unsigned long start_time_rx;
 
-       if (check_params(addr, alen, buffer, len))
-               return 1;
+#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
+       /*
+        * EEPROM chips that implement "address overflow" are ones
+        * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
+        * address and the extra bits end up in the "chip address"
+        * bit slots. This makes a 24WC08 (1Kbyte) chip look like
+        * four 256 byte chips.
+        *
+        * Note that we consider the length of the address field to
+        * still be one byte because the extra address bits are
+        * hidden in the chip address.
+        */
+       chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
+       addr &= ~(CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW << (alen * 8));
+
+       debug("%s: fix addr_overflow: chip %02x addr %02x\n", __func__, chip,
+             addr);
+#endif
 
-       if (i2c_xfer_init(chip, addr))
+       if (i2c_xfer_init(chip, addr, alen))
                return 1;
 
        start_time_rx = get_timer(0);
        while (len) {
-               writel(IC_CMD, &i2c_regs_p->ic_cmd_data);
+               if (len == 1)
+                       writel(IC_CMD | IC_STOP, &i2c_regs_p->ic_cmd_data);
+               else
+                       writel(IC_CMD, &i2c_regs_p->ic_cmd_data);
 
                if (readl(&i2c_regs_p->ic_status) & IC_STATUS_RFNE) {
                        *buffer++ = (uchar)readl(&i2c_regs_p->ic_cmd_data);
@@ -304,18 +310,36 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buffer, int len)
        int nb = len;
        unsigned long start_time_tx;
 
-       if (check_params(addr, alen, buffer, len))
-               return 1;
+#ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
+       /*
+        * EEPROM chips that implement "address overflow" are ones
+        * like Catalyst 24WC04/08/16 which has 9/10/11 bits of
+        * address and the extra bits end up in the "chip address"
+        * bit slots. This makes a 24WC08 (1Kbyte) chip look like
+        * four 256 byte chips.
+        *
+        * Note that we consider the length of the address field to
+        * still be one byte because the extra address bits are
+        * hidden in the chip address.
+        */
+       chip |= ((addr >> (alen * 8)) & CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW);
+       addr &= ~(CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW << (alen * 8));
+
+       debug("%s: fix addr_overflow: chip %02x addr %02x\n", __func__, chip,
+             addr);
+#endif
 
-       if (i2c_xfer_init(chip, addr))
+       if (i2c_xfer_init(chip, addr, alen))
                return 1;
 
        start_time_tx = get_timer(0);
        while (len) {
                if (readl(&i2c_regs_p->ic_status) & IC_STATUS_TFNF) {
-                       writel(*buffer, &i2c_regs_p->ic_cmd_data);
+                       if (--len == 0)
+                               writel(*buffer | IC_STOP, &i2c_regs_p->ic_cmd_data);
+                       else
+                               writel(*buffer, &i2c_regs_p->ic_cmd_data);
                        buffer++;
-                       len--;
                        start_time_tx = get_timer(0);
 
                } else if (get_timer(start_time_tx) > (nb * I2C_BYTE_TO)) {
@@ -344,3 +368,74 @@ int i2c_probe(uchar chip)
 
        return ret;
 }
+
+#ifdef CONFIG_I2C_MULTI_BUS
+int i2c_set_bus_num(unsigned int bus)
+{
+       switch (bus) {
+       case 0:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE;
+               break;
+#ifdef CONFIG_SYS_I2C_BASE1
+       case 1:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE1;
+               break;
+#endif
+#ifdef CONFIG_SYS_I2C_BASE2
+       case 2:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE2;
+               break;
+#endif
+#ifdef CONFIG_SYS_I2C_BASE3
+       case 3:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE3;
+               break;
+#endif
+#ifdef CONFIG_SYS_I2C_BASE4
+       case 4:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE4;
+               break;
+#endif
+#ifdef CONFIG_SYS_I2C_BASE5
+       case 5:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE5;
+               break;
+#endif
+#ifdef CONFIG_SYS_I2C_BASE6
+       case 6:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE6;
+               break;
+#endif
+#ifdef CONFIG_SYS_I2C_BASE7
+       case 7:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE7;
+               break;
+#endif
+#ifdef CONFIG_SYS_I2C_BASE8
+       case 8:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE8;
+               break;
+#endif
+#ifdef CONFIG_SYS_I2C_BASE9
+       case 9:
+               i2c_regs_p = (void *)CONFIG_SYS_I2C_BASE9;
+               break;
+#endif
+       default:
+               printf("Bad bus: %d\n", bus);
+               return -1;
+       }
+
+       current_bus = bus;
+
+       if (!bus_initialized[current_bus])
+               i2c_init(CONFIG_SYS_I2C_SPEED, CONFIG_SYS_I2C_SLAVE);
+
+       return 0;
+}
+
+unsigned int i2c_get_bus_num(void)
+{
+       return current_bus;
+}
+#endif