drivers, block: remove sil680 driver
[platform/kernel/u-boot.git] / drivers / i2c / ppc4xx_i2c.c
index c924874..8a38d11 100644 (file)
@@ -7,23 +7,10 @@
  * (C) Copyright 2001
  * Bill Hunter,  Wave 7 Optics, williamhunter@mediaone.net
  *
- * See file CREDITS for list of people who contributed to this
- * project.
+ * SPDX-License-Identifier:    GPL-2.0+
  *
- * 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
+ * NOTE: This driver should be converted to driver model before June 2017.
+ * Please see doc/driver-model/i2c-howto.txt for instructions.
  */
 
 #include <common.h>
@@ -174,8 +161,7 @@ static void ppc4xx_i2c_init(struct i2c_adapter *adap, int speed, int slaveaddr)
  *
  * Typical case is a Write of an addr followd by a Read. The
  * IBM FAQ does not cover this. On the last byte of the write
- * we don't set the creg CHT bit, and on the first bytes of the
- * read we set the RPST bit.
+ * we don't set the creg CHT bit but the RPST bit.
  *
  * It does not support address only transfers, there must be
  * a data part. If you want to write the address yourself, put
@@ -263,6 +249,10 @@ static int _i2c_transfer(struct i2c_adapter *adap,
                if ((!cmd_type && (ptr == addr)) || ((tran + bc) != cnt))
                        creg |= IIC_CNTL_CHT;
 
+               /* last part of address, prepare for repeated start on read */
+               if (cmd_type && (ptr == addr) && ((tran + bc) == cnt))
+                       creg |= IIC_CNTL_RPST;
+
                if (reading) {
                        creg |= IIC_CNTL_READ;
                } else {
@@ -302,6 +292,27 @@ static int _i2c_transfer(struct i2c_adapter *adap,
                        /* Transfer aborted? */
                        if (status & IIC_EXTSTS_XFRA)
                                result = IIC_NOK_XFRA;
+                       /* Is bus free?
+                        * If error happened during combined xfer
+                        * IIC interface is usually stuck in some strange
+                        * state without a valid stop condition.
+                        * Brute, but working: generate stop, then soft reset.
+                        */
+                       if ((status & IIC_EXTSTS_BCS_MASK)
+                           != IIC_EXTSTS_BCS_FREE){
+                               u8 mdcntl = in_8(&i2c->mdcntl);
+
+                               /* Generate valid stop condition */
+                               out_8(&i2c->xtcntlss, IIC_XTCNTLSS_SRST);
+                               out_8(&i2c->directcntl, IIC_DIRCNTL_SCC);
+                               udelay(10);
+                               out_8(&i2c->directcntl,
+                                     IIC_DIRCNTL_SCC | IIC_DIRCNTL_SDAC);
+                               out_8(&i2c->xtcntlss, 0);
+
+                               ppc4xx_i2c_init(adap, (mdcntl & IIC_MDCNTL_FSM)
+                                               ? 400000 : 100000, 0);
+                       }
                } else if ( status & IIC_STS_PT) {
                        result = IIC_NOK_TOUT;
                }
@@ -330,8 +341,6 @@ static int _i2c_transfer(struct i2c_adapter *adap,
                        cnt = data_len;
                        tran = 0;
                        reading = cmd_type;
-                       if (reading)
-                               creg = IIC_CNTL_RPST;
                }
        }
        return result;