]> git.sur5r.net Git - u-boot/blobdiff - drivers/i2c/designware_i2c.c
SPDX: Convert all of our single license tags to Linux Kernel style
[u-boot] / drivers / i2c / designware_i2c.c
index 0c7cd0ba727921e2dc49acaf761036bc7e511a86..dbc3326b5a9923c78550465ec31a55e7511161a4 100644 (file)
@@ -1,14 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * (C) Copyright 2009
  * Vipin Kumar, ST Micoelectronics, vipin.kumar@st.com.
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
 #include <dm.h>
 #include <i2c.h>
 #include <pci.h>
+#include <reset.h>
 #include <asm/io.h>
 #include "designware_i2c.h"
 
@@ -34,8 +34,17 @@ static struct dw_scl_sda_cfg byt_config = {
 struct dw_i2c {
        struct i2c_regs *regs;
        struct dw_scl_sda_cfg *scl_sda_cfg;
+       struct reset_ctl reset_ctl;
 };
 
+#ifdef CONFIG_SYS_I2C_DW_ENABLE_STATUS_UNSUPPORTED
+static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
+{
+       u32 ena = enable ? IC_ENABLE_0B : 0;
+
+       writel(ena, &i2c_base->ic_enable);
+}
+#else
 static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
 {
        u32 ena = enable ? IC_ENABLE_0B : 0;
@@ -56,6 +65,7 @@ static void dw_i2c_enable(struct i2c_regs *i2c_base, bool enable)
 
        printf("timeout in %sabling I2C adapter\n", enable ? "en" : "dis");
 }
+#endif
 
 /*
  * i2c_set_bus_speed - Set the i2c speed
@@ -240,6 +250,7 @@ static int __dw_i2c_read(struct i2c_regs *i2c_base, u8 dev, uint addr,
                         int alen, u8 *buffer, int len)
 {
        unsigned long start_time_rx;
+       unsigned int active = 0;
 
 #ifdef CONFIG_SYS_I2C_EEPROM_ADDR_OVERFLOW
        /*
@@ -265,18 +276,28 @@ static int __dw_i2c_read(struct i2c_regs *i2c_base, u8 dev, uint addr,
 
        start_time_rx = get_timer(0);
        while (len) {
-               if (len == 1)
-                       writel(IC_CMD | IC_STOP, &i2c_base->ic_cmd_data);
-               else
-                       writel(IC_CMD, &i2c_base->ic_cmd_data);
+               if (!active) {
+                       /*
+                        * Avoid writing to ic_cmd_data multiple times
+                        * in case this loop spins too quickly and the
+                        * ic_status RFNE bit isn't set after the first
+                        * write. Subsequent writes to ic_cmd_data can
+                        * trigger spurious i2c transfer.
+                        */
+                       if (len == 1)
+                               writel(IC_CMD | IC_STOP, &i2c_base->ic_cmd_data);
+                       else
+                               writel(IC_CMD, &i2c_base->ic_cmd_data);
+                       active = 1;
+               }
 
                if (readl(&i2c_base->ic_status) & IC_STATUS_RFNE) {
                        *buffer++ = (uchar)readl(&i2c_base->ic_cmd_data);
                        len--;
                        start_time_rx = get_timer(0);
-
+                       active = 0;
                } else if (get_timer(start_time_rx) > I2C_BYTE_TO) {
-                               return 1;
+                       return 1;
                }
        }
 
@@ -354,7 +375,8 @@ static void __dw_i2c_init(struct i2c_regs *i2c_base, int speed, int slaveaddr)
        /* Disable i2c */
        dw_i2c_enable(i2c_base, false);
 
-       writel((IC_CON_SD | IC_CON_SPD_FS | IC_CON_MM), &i2c_base->ic_con);
+       writel(IC_CON_SD | IC_CON_RE | IC_CON_SPD_FS | IC_CON_MM,
+              &i2c_base->ic_con);
        writel(IC_RX_TL, &i2c_base->ic_rx_tl);
        writel(IC_TX_TL, &i2c_base->ic_tx_tl);
        writel(IC_STOP_DET, &i2c_base->ic_intr_mask);
@@ -513,6 +535,7 @@ static int designware_i2c_probe_chip(struct udevice *bus, uint chip_addr,
 static int designware_i2c_probe(struct udevice *bus)
 {
        struct dw_i2c *priv = dev_get_priv(bus);
+       int ret;
 
        if (device_is_on_pci_bus(bus)) {
 #ifdef CONFIG_DM_PCI
@@ -525,9 +548,16 @@ static int designware_i2c_probe(struct udevice *bus)
 #endif
 #endif
        } else {
-               priv->regs = (struct i2c_regs *)dev_get_addr_ptr(bus);
+               priv->regs = (struct i2c_regs *)devfdt_get_addr_ptr(bus);
        }
 
+       ret = reset_get_by_name(bus, "i2c", &priv->reset_ctl);
+       if (ret)
+               pr_info("reset_get_by_name() failed: %d\n", ret);
+
+       if (&priv->reset_ctl)
+               reset_deassert(&priv->reset_ctl);
+
        __dw_i2c_init(priv->regs, 0, 0);
 
        return 0;