diff mbox series

[i2c-next,v8,5/5] i2c: aspeed: Add bus idle waiting logic for multi-master use cases

Message ID 20181029211414.20771-6-jae.hyun.yoo@linux.intel.com (mailing list archive)
State Superseded
Headers show
Series i2c: aspeed: Add bus idle waiting logic for multi-master use cases | expand

Commit Message

Jae Hyun Yoo Oct. 29, 2018, 9:14 p.m. UTC
In multi-master environment, this driver's master cannot know
exactly when a peer master sends data to this driver's slave so a
case can be happened that this master tries to send data through
the master_xfer function but slave data from peer master is still
being processed by this driver.

To prevent any state corruption in the case, this patch adds
checking code if any slave operation is ongoing and it waits up to
the bus timeout duration before starting a master_xfer operation.

Signed-off-by: Jae Hyun Yoo <jae.hyun.yoo@linux.intel.com>
Reviewed-by: Brendan Higgins <brendanhiggins@google.com>
---
 drivers/i2c/busses/i2c-aspeed.c | 52 +++++++++++++++++++++++----------
 1 file changed, 37 insertions(+), 15 deletions(-)

Comments

kernel test robot Oct. 29, 2018, 10:49 p.m. UTC | #1
Hi Jae,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on wsa/i2c/for-next]
[also build test WARNING on v4.19 next-20181029]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Jae-Hyun-Yoo/i2c-aspeed-Add-bus-idle-waiting-logic-for-multi-master-use-cases/20181030-051719
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/for-next
config: xtensa-allyesconfig (attached as .config)
compiler: xtensa-linux-gcc (GCC) 8.1.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=8.1.0 make.cross ARCH=xtensa 

Note: it may well be a FALSE warning. FWIW you are at least aware of it now.
http://gcc.gnu.org/wiki/Better_Uninitialized_Warnings

All warnings (new ones prefixed by >>):

   In file included from include/linux/ktime.h:25,
                    from include/linux/rcutiny.h:28,
                    from include/linux/rcupdate.h:209,
                    from include/linux/srcu.h:33,
                    from include/linux/notifier.h:16,
                    from include/linux/clk.h:17,
                    from drivers/i2c/busses/i2c-aspeed.c:13:
   drivers/i2c/busses/i2c-aspeed.c: In function 'aspeed_i2c_master_xfer':
>> include/linux/jiffies.h:108:15: warning: 'check_started' may be used uninitialized in this function [-Wmaybe-uninitialized]
      ((long)((b) - (a)) < 0))
                  ^
   drivers/i2c/busses/i2c-aspeed.c:607:16: note: 'check_started' was declared here
     unsigned long check_started;
                   ^~~~~~~~~~~~~
--
   In file included from include/linux/ktime.h:25,
                    from include/linux/rcutiny.h:28,
                    from include/linux/rcupdate.h:209,
                    from include/linux/srcu.h:33,
                    from include/linux/notifier.h:16,
                    from include/linux/clk.h:17,
                    from drivers/i2c//busses/i2c-aspeed.c:13:
   drivers/i2c//busses/i2c-aspeed.c: In function 'aspeed_i2c_master_xfer':
>> include/linux/jiffies.h:108:15: warning: 'check_started' may be used uninitialized in this function [-Wmaybe-uninitialized]
      ((long)((b) - (a)) < 0))
                  ^
   drivers/i2c//busses/i2c-aspeed.c:607:16: note: 'check_started' was declared here
     unsigned long check_started;
                   ^~~~~~~~~~~~~

vim +/check_started +108 include/linux/jiffies.h

^1da177e Linus Torvalds   2005-04-16   91  
^1da177e Linus Torvalds   2005-04-16   92  /*
^1da177e Linus Torvalds   2005-04-16   93   *	These inlines deal with timer wrapping correctly. You are 
^1da177e Linus Torvalds   2005-04-16   94   *	strongly encouraged to use them
^1da177e Linus Torvalds   2005-04-16   95   *	1. Because people otherwise forget
^1da177e Linus Torvalds   2005-04-16   96   *	2. Because if the timer wrap changes in future you won't have to
^1da177e Linus Torvalds   2005-04-16   97   *	   alter your driver code.
^1da177e Linus Torvalds   2005-04-16   98   *
^1da177e Linus Torvalds   2005-04-16   99   * time_after(a,b) returns true if the time a is after time b.
^1da177e Linus Torvalds   2005-04-16  100   *
^1da177e Linus Torvalds   2005-04-16  101   * Do this with "<0" and ">=0" to only test the sign of the result. A
^1da177e Linus Torvalds   2005-04-16  102   * good compiler would generate better code (and a really good compiler
^1da177e Linus Torvalds   2005-04-16  103   * wouldn't care). Gcc is currently neither.
^1da177e Linus Torvalds   2005-04-16  104   */
^1da177e Linus Torvalds   2005-04-16  105  #define time_after(a,b)		\
^1da177e Linus Torvalds   2005-04-16  106  	(typecheck(unsigned long, a) && \
^1da177e Linus Torvalds   2005-04-16  107  	 typecheck(unsigned long, b) && \
5a581b36 Paul E. McKenney 2013-07-27 @108  	 ((long)((b) - (a)) < 0))
^1da177e Linus Torvalds   2005-04-16  109  #define time_before(a,b)	time_after(b,a)
^1da177e Linus Torvalds   2005-04-16  110  

:::::: The code at line 108 was first introduced by commit
:::::: 5a581b367b5df0531265311fc681c2abd377e5e6 jiffies: Avoid undefined behavior from signed overflow

:::::: TO: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
:::::: CC: Paul E. McKenney <paulmck@linux.vnet.ibm.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
kernel test robot Oct. 30, 2018, 6:36 a.m. UTC | #2
Hi Jae,

Thank you for the patch! Yet something to improve:

[auto build test ERROR on wsa/i2c/for-next]
[also build test ERROR on v4.19 next-20181029]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Jae-Hyun-Yoo/i2c-aspeed-Add-bus-idle-waiting-logic-for-multi-master-use-cases/20181030-051719
base:   https://git.kernel.org/pub/scm/linux/kernel/git/wsa/linux.git i2c/for-next
config: arm-multi_v5_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        GCC_VERSION=7.2.0 make.cross ARCH=arm 

All errors (new ones prefixed by >>):

   drivers/i2c//busses/i2c-aspeed.c: In function 'aspeed_i2c_check_bus_busy':
>> drivers/i2c//busses/i2c-aspeed.c:617:12: error: 'struct aspeed_i2c_bus' has no member named 'slave_state'; did you mean 'master_state'?
          bus->slave_state == ASPEED_I2C_SLAVE_STOP)
               ^~~~~~~~~~~
               master_state

vim +617 drivers/i2c//busses/i2c-aspeed.c

   604	
   605	static int aspeed_i2c_check_bus_busy(struct aspeed_i2c_bus *bus)
   606	{
   607		unsigned long check_started;
   608	
   609		if (bus->multi_master) {
   610			might_sleep();
   611			check_started = jiffies;
   612		}
   613	
   614		for (;;) {
   615			if (!(readl(bus->base + ASPEED_I2C_CMD_REG) &
   616			      ASPEED_I2CD_BUS_BUSY_STS) &&
 > 617			    bus->slave_state == ASPEED_I2C_SLAVE_STOP)
   618				return 0;
   619			if (!bus->multi_master)
   620				break;
   621			if (time_after(jiffies, check_started + bus->adap.timeout))
   622				break;
   623			usleep_range((ASPEED_I2C_BUS_BUSY_CHECK_INTERVAL_US >> 2) + 1,
   624				     ASPEED_I2C_BUS_BUSY_CHECK_INTERVAL_US);
   625		}
   626	
   627		return aspeed_i2c_recover_bus(bus);
   628	}
   629	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
diff mbox series

Patch

diff --git a/drivers/i2c/busses/i2c-aspeed.c b/drivers/i2c/busses/i2c-aspeed.c
index 833b6b6a4c7e..4e92a405210b 100644
--- a/drivers/i2c/busses/i2c-aspeed.c
+++ b/drivers/i2c/busses/i2c-aspeed.c
@@ -12,6 +12,7 @@ 
 
 #include <linux/clk.h>
 #include <linux/completion.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/errno.h>
 #include <linux/i2c.h>
@@ -115,6 +116,9 @@ 
 /* 0x18 : I2CD Slave Device Address Register   */
 #define ASPEED_I2CD_DEV_ADDR_MASK			GENMASK(6, 0)
 
+/* Busy checking */
+#define ASPEED_I2C_BUS_BUSY_CHECK_INTERVAL_US		(10 * 1000)
+
 enum aspeed_i2c_master_state {
 	ASPEED_I2C_MASTER_INACTIVE,
 	ASPEED_I2C_MASTER_START,
@@ -156,6 +160,8 @@  struct aspeed_i2c_bus {
 	int				cmd_err;
 	/* Protected only by i2c_lock_bus */
 	int				master_xfer_result;
+	/* Multi-master */
+	bool				multi_master;
 #if IS_ENABLED(CONFIG_I2C_SLAVE)
 	struct i2c_client		*slave;
 	enum aspeed_i2c_slave_state	slave_state;
@@ -596,27 +602,41 @@  static irqreturn_t aspeed_i2c_bus_irq(int irq, void *dev_id)
 	return irq_remaining ? IRQ_NONE : IRQ_HANDLED;
 }
 
+static int aspeed_i2c_check_bus_busy(struct aspeed_i2c_bus *bus)
+{
+	unsigned long check_started;
+
+	if (bus->multi_master) {
+		might_sleep();
+		check_started = jiffies;
+	}
+
+	for (;;) {
+		if (!(readl(bus->base + ASPEED_I2C_CMD_REG) &
+		      ASPEED_I2CD_BUS_BUSY_STS) &&
+		    bus->slave_state == ASPEED_I2C_SLAVE_STOP)
+			return 0;
+		if (!bus->multi_master)
+			break;
+		if (time_after(jiffies, check_started + bus->adap.timeout))
+			break;
+		usleep_range((ASPEED_I2C_BUS_BUSY_CHECK_INTERVAL_US >> 2) + 1,
+			     ASPEED_I2C_BUS_BUSY_CHECK_INTERVAL_US);
+	}
+
+	return aspeed_i2c_recover_bus(bus);
+}
+
 static int aspeed_i2c_master_xfer(struct i2c_adapter *adap,
 				  struct i2c_msg *msgs, int num)
 {
 	struct aspeed_i2c_bus *bus = i2c_get_adapdata(adap);
 	unsigned long time_left, flags;
-	int ret = 0;
 
-	spin_lock_irqsave(&bus->lock, flags);
-	bus->cmd_err = 0;
-
-	/* If bus is busy, attempt recovery. We assume a single master
-	 * environment.
-	 */
-	if (readl(bus->base + ASPEED_I2C_CMD_REG) & ASPEED_I2CD_BUS_BUSY_STS) {
-		spin_unlock_irqrestore(&bus->lock, flags);
-		ret = aspeed_i2c_recover_bus(bus);
-		if (ret)
-			return ret;
-		spin_lock_irqsave(&bus->lock, flags);
-	}
+	if (aspeed_i2c_check_bus_busy(bus))
+		return -EAGAIN;
 
+	spin_lock_irqsave(&bus->lock, flags);
 	bus->cmd_err = 0;
 	bus->msgs = msgs;
 	bus->msgs_index = 0;
@@ -827,7 +847,9 @@  static int aspeed_i2c_init(struct aspeed_i2c_bus *bus,
 	if (ret < 0)
 		return ret;
 
-	if (!of_property_read_bool(pdev->dev.of_node, "multi-master"))
+	if (of_property_read_bool(pdev->dev.of_node, "multi-master"))
+		bus->multi_master = true;
+	else
 		fun_ctrl_reg |= ASPEED_I2CD_MULTI_MASTER_DIS;
 
 	/* Enable Master Mode */