rockchip: use 'arch-rockchip' as header file path
[oweals/u-boot.git] / drivers / i2c / rk_i2c.c
index 63b141838b6f14be71e0d38c708681d043ac6276..cdd94bb05a9009c07e278667f086163e97597516 100644 (file)
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * (C) Copyright 2015 Google, Inc
  *
  * (C) Copyright 2008-2014 Rockchip Electronics
  * Peter, Software Engineering, <superpeter.cai@gmail.com>.
- *
- * SPDX-License-Identifier:     GPL-2.0+
  */
 
 #include <common.h>
 #include <errno.h>
 #include <i2c.h>
 #include <asm/io.h>
-#include <asm/arch/clock.h>
-#include <asm/arch/i2c.h>
-#include <asm/arch/periph.h>
+#include <asm/arch-rockchip/clock.h>
+#include <asm/arch-rockchip/i2c.h>
+#include <asm/arch-rockchip/periph.h>
 #include <dm/pinctrl.h>
 #include <linux/sizes.h>
 
-DECLARE_GLOBAL_DATA_PTR;
-
 /* i2c timerout */
 #define I2C_TIMEOUT_MS         100
 #define I2C_RETRY_COUNT                3
@@ -34,6 +31,18 @@ struct rk_i2c {
        unsigned int speed;
 };
 
+enum {
+       RK_I2C_LEGACY,
+       RK_I2C_NEW,
+};
+
+/**
+ * @controller_type: i2c controller type
+ */
+struct rk_i2c_soc_data {
+       int controller_type;
+};
+
 static inline void rk_i2c_get_div(int div, int *divh, int *divl)
 {
        *divl = div / 2;
@@ -164,6 +173,7 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
        uint rxdata;
        uint i, j;
        int err;
+       bool snd_chunk = false;
 
        debug("rk_i2c_read: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
              chip, reg, r_len, b_len);
@@ -184,15 +194,26 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
 
        while (bytes_remain_len) {
                if (bytes_remain_len > RK_I2C_FIFO_SIZE) {
-                       con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX);
+                       con = I2C_CON_EN;
                        bytes_xferred = 32;
                } else {
-                       con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX) |
-                               I2C_CON_LASTACK;
+                       /*
+                        * The hw can read up to 32 bytes at a time. If we need
+                        * more than one chunk, send an ACK after the last byte.
+                        */
+                       con = I2C_CON_EN | I2C_CON_LASTACK;
                        bytes_xferred = bytes_remain_len;
                }
                words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
 
+               /*
+                * make sure we are in plain RX mode if we read a second chunk
+                */
+               if (snd_chunk)
+                       con |= I2C_CON_MOD(I2C_MODE_RX);
+               else
+                       con |= I2C_CON_MOD(I2C_MODE_TRX);
+
                writel(con, &regs->con);
                writel(bytes_xferred, &regs->mrxcnt);
                writel(I2C_MBRFIEN | I2C_NAKRCVIEN, &regs->ien);
@@ -227,6 +248,7 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
                }
 
                bytes_remain_len -= bytes_xferred;
+               snd_chunk = true;
                debug("I2C Read bytes_remain_len %d\n", bytes_remain_len);
        }
 
@@ -258,7 +280,7 @@ static int rk_i2c_write(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
 
        while (bytes_remain_len) {
                if (bytes_remain_len > RK_I2C_FIFO_SIZE)
-                       bytes_xferred = 32;
+                       bytes_xferred = RK_I2C_FIFO_SIZE;
                else
                        bytes_xferred = bytes_remain_len;
                words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
@@ -269,17 +291,17 @@ static int rk_i2c_write(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
                                if ((i * 4 + j) == bytes_xferred)
                                        break;
 
-                               if (i == 0 && j == 0) {
+                               if (i == 0 && j == 0 && pbuf == buf) {
                                        txdata |= (chip << 1);
-                               } else if (i == 0 && j <= r_len) {
+                               } else if (i == 0 && j <= r_len && pbuf == buf) {
                                        txdata |= (reg &
                                                (0xff << ((j - 1) * 8))) << 8;
                                } else {
                                        txdata |= (*pbuf++)<<(j * 8);
                                }
-                               writel(txdata, &regs->txdata[i]);
                        }
-                       debug("I2c Write TXDATA[%d] = 0x%x\n", i, txdata);
+                       writel(txdata, &regs->txdata[i]);
+                       debug("I2c Write TXDATA[%d] = 0x%08x\n", i, txdata);
                }
 
                writel(I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TX), &regs->con);
@@ -368,8 +390,37 @@ static int rockchip_i2c_ofdata_to_platdata(struct udevice *bus)
 static int rockchip_i2c_probe(struct udevice *bus)
 {
        struct rk_i2c *priv = dev_get_priv(bus);
+       struct rk_i2c_soc_data *soc_data;
+       struct udevice *pinctrl;
+       int bus_nr;
+       int ret;
+
+       priv->regs = dev_read_addr_ptr(bus);
+
+       soc_data = (struct rk_i2c_soc_data*)dev_get_driver_data(bus);
 
-       priv->regs = (void *)dev_get_addr(bus);
+       if (soc_data->controller_type == RK_I2C_LEGACY) {
+               ret = dev_read_alias_seq(bus, &bus_nr);
+               if (ret < 0) {
+                       debug("%s: Could not get alias for %s: %d\n",
+                        __func__, bus->name, ret);
+                       return ret;
+               }
+
+               ret = uclass_get_device(UCLASS_PINCTRL, 0, &pinctrl);
+               if (ret) {
+                       debug("%s: Cannot find pinctrl device\n", __func__);
+                       return ret;
+               }
+
+               /* pinctrl will switch I2C to new type */
+               ret = pinctrl_request_noflags(pinctrl, PERIPH_ID_I2C0 + bus_nr);
+               if (ret) {
+                       debug("%s: Failed to switch I2C to new type %s: %d\n",
+                               __func__, bus->name, ret);
+                       return ret;
+               }
+       }
 
        return 0;
 }
@@ -379,8 +430,55 @@ static const struct dm_i2c_ops rockchip_i2c_ops = {
        .set_bus_speed  = rockchip_i2c_set_bus_speed,
 };
 
+static const struct rk_i2c_soc_data rk3066_soc_data = {
+       .controller_type = RK_I2C_LEGACY,
+};
+
+static const struct rk_i2c_soc_data rk3188_soc_data = {
+       .controller_type = RK_I2C_LEGACY,
+};
+
+static const struct rk_i2c_soc_data rk3228_soc_data = {
+       .controller_type = RK_I2C_NEW,
+};
+
+static const struct rk_i2c_soc_data rk3288_soc_data = {
+       .controller_type = RK_I2C_NEW,
+};
+
+static const struct rk_i2c_soc_data rk3328_soc_data = {
+       .controller_type = RK_I2C_NEW,
+};
+
+static const struct rk_i2c_soc_data rk3399_soc_data = {
+       .controller_type = RK_I2C_NEW,
+};
+
 static const struct udevice_id rockchip_i2c_ids[] = {
-       { .compatible = "rockchip,rk3288-i2c" },
+       {
+               .compatible = "rockchip,rk3066-i2c",
+               .data = (ulong)&rk3066_soc_data,
+       },
+       {
+               .compatible = "rockchip,rk3188-i2c",
+               .data = (ulong)&rk3188_soc_data,
+       },
+       {
+               .compatible = "rockchip,rk3228-i2c",
+               .data = (ulong)&rk3228_soc_data,
+       },
+       {
+               .compatible = "rockchip,rk3288-i2c",
+               .data = (ulong)&rk3288_soc_data,
+       },
+       {
+               .compatible = "rockchip,rk3328-i2c",
+               .data = (ulong)&rk3328_soc_data,
+       },
+       {
+               .compatible = "rockchip,rk3399-i2c",
+               .data = (ulong)&rk3399_soc_data,
+       },
        { }
 };