Skip to contentSkip to conversation

i2c: added i2c quirk flag for unsupported clock stretching

PATCH Superseded Nicola Corna 7 participants 31 messages Oct 27, 2015 – May 10, 2017

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

PATCH 1/4 i2c: added i2c quirk flag for unsupported clock stretching

Added I2C_AQ_NO_CLK_STRETCH, to be used when clock stretching is not supported.

---
 include/linux/i2c.h | 2 ++
 1 file changed, 2 insertions(+)
include/linux/i2c.h +2 -0
index 768063b..9697002 100644
@@ -493,6 +493,8 @@ struct i2c_adapter_quirks {
 /* convenience macro for typical write-then read case */
 #define I2C_AQ_COMB_WRITE_THEN_READ	(I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \
 					 I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR)
+/* clock stretching is not supported */
+#define I2C_AQ_NO_CLK_STRETCH		BIT(4)
 
 /*
  * i2c_adapter is the structure used to identify a physical i2c bus along
Signature · 1 line
2.6.1
#1 lore raw

PATCH 2/4 i2c: added I2C_AQ_NO_CLK_STRETCH to i2c-algo-bit.c

Added I2C_AQ_NO_CLK_STRETCH to drivers/i2c/algos/i2c-algo-bit.c when getscl is not available.

---
 drivers/i2c/algos/i2c-algo-bit.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/i2c/algos/i2c-algo-bit.c +6 -0
index 899bede..9d233bb 100644
@@ -617,6 +617,10 @@ const struct i2c_algorithm i2c_bit_algo = {
 };
 EXPORT_SYMBOL(i2c_bit_algo);
 
+const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = {
+	.flags = I2C_AQ_NO_CLK_STRETCH,
+};
+
 /*
  * registering functions to load algorithms at runtime
  */
@@ -635,6 +639,8 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
 	/* register new adapter to i2c module... */
 	adap->algo = &i2c_bit_algo;
 	adap->retries = 3;
+	if (bit_adap->getscl == NULL)
+		adap->quirks = &i2c_bit_quirk_no_clk_stretch;
 
 	ret = add_adapter(adap);
 	if (ret < 0)
Signature · 1 line
2.6.1
#2 lore raw

PATCH 3/4 i2c: added I2C_AQ_NO_CLK_STRETCH to i2c-bcm2835.c

In reply to Nicola Corna

As reported here
http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
the BCM2835 has a bug in its i2c implementation which prevents a correct clock stretching.

---
 drivers/i2c/busses/i2c-bcm2835.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/i2c/busses/i2c-bcm2835.c +5 -0
index 3032b89..38d7102 100644
@@ -222,6 +222,10 @@ static const struct i2c_algorithm bcm2835_i2c_algo = {
 	.functionality	= bcm2835_i2c_func,
 };
 
+static const struct i2c_adapter_quirks bcm2835_i2c_quirks = {
+	.flags = I2C_AQ_NO_CLK_STRETCH,
+};
+
 static int bcm2835_i2c_probe(struct platform_device *pdev)
 {
 	struct bcm2835_i2c_dev *i2c_dev;
@@ -293,6 +297,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
 	adap->algo = &bcm2835_i2c_algo;
 	adap->dev.parent = &pdev->dev;
 	adap->dev.of_node = pdev->dev.of_node;
+	adap->quirks = &bcm2835_i2c_quirks;
 
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
 
Signature · 1 line
2.6.1
#3 lore raw

PATCH 3/4 i2c: added I2C_AQ_NO_CLK_STRETCH to i2c-bcm2835.c

Quoted 26 lines · Nicola Corna

As reported here
http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
the BCM2835 has a bug in its i2c implementation which prevents a correct clock stretching.

---
 drivers/i2c/busses/i2c-bcm2835.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/i2c/busses/i2c-bcm2835.c +4 -0
index 3032b89..38d7102 100644
@@ -222,6 +222,10 @@ static const struct i2c_algorithm bcm2835_i2c_algo = {
 	.functionality	= bcm2835_i2c_func,
 };
 
+static const struct i2c_adapter_quirks bcm2835_i2c_quirks = {
+	.flags = I2C_AQ_NO_CLK_STRETCH,
+};
+
 static int bcm2835_i2c_probe(struct platform_device *pdev)
 {
 	struct bcm2835_i2c_dev *i2c_dev;

drivers/i2c/busses/i2c-bcm2835.c static int bcm2835_i2c_probe(struct platform_device *pdev) · line 302

Quoted 6 lines
drivers/i2c/busses/i2c-bcm2835.c +1 -0
@@ -293,6 +297,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
 	adap->algo = &bcm2835_i2c_algo;
 	adap->dev.parent = &pdev->dev;
 	adap->dev.of_node = pdev->dev.of_node;
+	adap->quirks = &bcm2835_i2c_quirks;
 
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);

Looking at the driver, we're not setting the REDL/FEDL fields, which are involved in the delays following clock stretching. Looks like I2C_DEL's low 16 bits (rising edge delay) should be I2C_DIV / 4 (wait half the period for the slave to update its data before we sample it), and the high 16 bits (falling edge delay) should be I2C_DIV / 16 (put our data out before we expet the slave to sample the value).

Note: this is not a detailed analysis, nor am I asserting that this is the bug, just something that we seem to be missing in the Linux driver.

(Added cc to linux-rpi-kernel, in case anyone on that list is interested
in I2C issues on the platform)
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 818 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151028/675f82b2/attachment.sig>

#4 lore raw

PATCH 3/4 i2c: added I2C_AQ_NO_CLK_STRETCH to i2c-bcm2835.c

In reply to Nicola Corna
Quoted 4 lines · Nicola Corna

As reported here
http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
the BCM2835 has a bug in its i2c implementation which prevents a correct clock stretching.

I was going to ask for some more official confirmation of this bug (e.g. update to BCM2835_Peripherals.pdf), but it looks like it's already been confirmed; check Gert van Loo's responses at:

https://www.raspberrypi.org/forums/viewtopic.php?p=146272

It might be useful to add this link into the patch description.

#5 lore raw

PATCH v3 4/4 iio: humidity: si7020: added No Hold read mode

In reply to Nicola Corna

The Si7013/20/21 modules support 2 read modes:
* Hold mode (blocking), where the device stretches the clock until the end
of the measurement
* No Hold mode (non-blocking), where the device replies NACK for every I2C
call during the measurement
Here the No Hold mode is implemented, selectable with the blocking_io variable within si7020_platform_data. The default mode is Hold, unless the adapter does not support clock stretching, in which case the No Hold mode is used.

---
This patch depends on patch "[PATCH v4 1/2] iio: humidity: si7020: replaced bitmask on humidity values with range check"

 drivers/iio/humidity/si7020.c        | 78 ++++++++++++++++++++++++++++++++----
 include/linux/platform_data/si7020.h | 21 ++++++++++
 2 files changed, 92 insertions(+), 7 deletions(-)
 create mode 100644 include/linux/platform_data/si7020.h
drivers/iio/humidity/si7020.c +71 -7
index 12128d1..cb65f34 100644
@@ -2,6 +2,7 @@
  * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors
  * Copyright (c) 2013,2014  Uplogix, Inc.
  * David Barksdale <[email protected]>
+ * Copyright (c) 2015 Nicola Corna <[email protected]>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -30,33 +31,79 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
+#include <linux/jiffies.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/platform_data/si7020.h>
 
 /* Measure Relative Humidity, Hold Master Mode */
 #define SI7020CMD_RH_HOLD	0xE5
+/* Measure Relative Humidity, No Hold Master Mode */
+#define SI7020CMD_RH_NO_HOLD	0xF5
 /* Measure Temperature, Hold Master Mode */
 #define SI7020CMD_TEMP_HOLD	0xE3
+/* Measure Temperature, No Hold Master Mode */
+#define SI7020CMD_TEMP_NO_HOLD	0xF3
 /* Software Reset */
 #define SI7020CMD_RESET		0xFE
+/* Relative humidity measurement timeout (us) */
+#define SI7020_RH_TIMEOUT	22800
+/* Temperature measurement timeout (us) */
+#define SI7020_TEMP_TIMEOUT	10800
+/* Minimum delay between retries (No Hold Mode) in us */
+#define SI7020_NOHOLD_SLEEP_MIN	2000
+/* Maximum delay between retries (No Hold Mode) in us */
+#define SI7020_NOHOLD_SLEEP_MAX	6000
 
 static int si7020_read_raw(struct iio_dev *indio_dev,
 			   struct iio_chan_spec const *chan, int *val,
 			   int *val2, long mask)
 {
 	struct i2c_client **client = iio_priv(indio_dev);
+	struct si7020_platform_data *pdata;
 	int ret;
+	bool holdmode;
+	unsigned char buf[2];
+	unsigned long start;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		ret = i2c_smbus_read_word_data(*client,
-					       chan->type == IIO_TEMP ?
-					       SI7020CMD_TEMP_HOLD :
-					       SI7020CMD_RH_HOLD);
-		if (ret < 0)
-			return ret;
-		*val = ret >> 2;
+		pdata = dev_get_platdata(&(*client)->dev);
+		if (pdata)
+			holdmode = pdata->blocking_io;
+		else
+			holdmode = !((*client)->adapter->quirks &&
+					(*client)->adapter->quirks->flags |
+					I2C_AQ_NO_CLK_STRETCH);
+		if (holdmode) {
+			ret = i2c_smbus_read_word_data(*client,
+						       chan->type == IIO_TEMP ?
+						       SI7020CMD_TEMP_HOLD :
+						       SI7020CMD_RH_HOLD);
+			if (ret < 0)
+				return ret;
+			*val = ret >> 2;
+		} else {
+			ret = i2c_smbus_write_byte(*client,
+						   chan->type == IIO_TEMP ?
+						   SI7020CMD_TEMP_NO_HOLD :
+						   SI7020CMD_RH_NO_HOLD);
+			if (ret < 0)
+				return ret;
+			start = jiffies;
+			while ((ret = i2c_master_recv(*client, buf, 2)) < 0) {
+				if (time_after(jiffies, start +
+					       usecs_to_jiffies(
+							chan->type == IIO_TEMP ?
+							SI7020_TEMP_TIMEOUT :
+							SI7020_RH_TIMEOUT)))
+					return ret;
+				usleep_range(SI7020_NOHOLD_SLEEP_MIN,
+					     SI7020_NOHOLD_SLEEP_MAX);
+			}
+			*val = ((buf[0] << 8) | buf[1]) >> 2;
+		}
 		/*
 		 * Humidity values can slightly exceed the 0-100%RH
 		 * range and should be corrected by software
@@ -116,6 +163,7 @@ static int si7020_probe(struct i2c_client *client,
 {
 	struct iio_dev *indio_dev;
 	struct i2c_client **data;
+	struct si7020_platform_data *pdata;
 	int ret;
 
 	if (!i2c_check_functionality(client->adapter,
@@ -123,6 +171,22 @@ static int si7020_probe(struct i2c_client *client,
 				     I2C_FUNC_SMBUS_READ_WORD_DATA))
 		return -ENODEV;
 
+	pdata = dev_get_platdata(&client->dev);
+	if (pdata) {
+		if (pdata->blocking_io) {
+			if (client->adapter->quirks &&
+			   client->adapter->quirks->flags |
+			   I2C_AQ_NO_CLK_STRETCH)
+				return -ENODEV;
+		} else if (!i2c_check_functionality(client->adapter,
+			  I2C_FUNC_I2C))
+			return -ENODEV;
+	} else
+		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
+		   client->adapter->quirks &&
+		   client->adapter->quirks->flags | I2C_AQ_NO_CLK_STRETCH)
+			return -ENODEV;
+
 	/* Reset device, loads default settings. */
 	ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);
 	if (ret < 0)
include/linux/platform_data/si7020.h new file +21 -0
new file mode 100644
index 0000000..8bb5848
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 Nicola Corna <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __SI7020_H_
+#define __SI7020_H_
+
+struct si7020_platform_data {
+	bool blocking_io;
+};
+#endif /* __SI7020_H_ */
Signature · 1 line
2.6.1
#6 lore raw

PATCH v3 4/4 iio: humidity: si7020: added No Hold read mode

Not a complete review, I just stumbled over this one:

drivers/iio/humidity/si7020.c static int si7020_probe(struct i2c_client *client, · line 178

Quoted 2 lines
drivers/iio/humidity/si7020.c +2 -0
+			if (client->adapter->quirks &&
+			   client->adapter->quirks->flags |

'|' should be '&', or?

Review

Quoted 2 lines
(quoted diff) +2 -0
+			   I2C_AQ_NO_CLK_STRETCH)
+				return -ENODEV;

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151028/0f7f2007/attachment.sig>

#7 lore raw

PATCH v3 4/4 iio: humidity: si7020: added No Hold read mode

Quoted 7 lines · Wolfram Sang

Not a complete review, I just stumbled over this one:

Quoted 2 lines

+ if (client->adapter->quirks &&
+ client->adapter->quirks->flags |

'|' should be '&', or?

Yes, sorry. I'll update the patch.

Nicola Corna

Quoted 2 lines
Quoted 2 lines

+ I2C_AQ_NO_CLK_STRETCH)
+ return -ENODEV;

#8 lore raw

PATCH v4 4/4 iio: humidity: si7020: added No Hold read mode

The Si7013/20/21 modules support 2 read modes:
* Hold mode (blocking), where the device stretches the clock until the end
of the measurement
* No Hold mode (non-blocking), where the device replies NACK for every I2C
call during the measurement
Here the No Hold mode is implemented, selectable with the blocking_io variable within si7020_platform_data. The default mode is Hold, unless the adapter does not support clock stretching, in which case the No Hold mode is used.

---
This patch depends on patch "[PATCH v4 1/2] iio: humidity: si7020: replaced bitmask on humidity values with range check"

 drivers/iio/humidity/si7020.c        | 78 ++++++++++++++++++++++++++++++++----
 include/linux/platform_data/si7020.h | 21 ++++++++++
 2 files changed, 92 insertions(+), 7 deletions(-)
 create mode 100644 include/linux/platform_data/si7020.h
drivers/iio/humidity/si7020.c +71 -7
index 12128d1..c728681 100644
@@ -2,6 +2,7 @@
  * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors
  * Copyright (c) 2013,2014  Uplogix, Inc.
  * David Barksdale <[email protected]>
+ * Copyright (c) 2015 Nicola Corna <[email protected]>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -30,33 +31,79 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
+#include <linux/jiffies.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/platform_data/si7020.h>
 
 /* Measure Relative Humidity, Hold Master Mode */
 #define SI7020CMD_RH_HOLD	0xE5
+/* Measure Relative Humidity, No Hold Master Mode */
+#define SI7020CMD_RH_NO_HOLD	0xF5
 /* Measure Temperature, Hold Master Mode */
 #define SI7020CMD_TEMP_HOLD	0xE3
+/* Measure Temperature, No Hold Master Mode */
+#define SI7020CMD_TEMP_NO_HOLD	0xF3
 /* Software Reset */
 #define SI7020CMD_RESET		0xFE
+/* Relative humidity measurement timeout (us) */
+#define SI7020_RH_TIMEOUT	22800
+/* Temperature measurement timeout (us) */
+#define SI7020_TEMP_TIMEOUT	10800
+/* Minimum delay between retries (No Hold Mode) in us */
+#define SI7020_NOHOLD_SLEEP_MIN	2000
+/* Maximum delay between retries (No Hold Mode) in us */
+#define SI7020_NOHOLD_SLEEP_MAX	6000
 
 static int si7020_read_raw(struct iio_dev *indio_dev,
 			   struct iio_chan_spec const *chan, int *val,
 			   int *val2, long mask)
 {
 	struct i2c_client **client = iio_priv(indio_dev);
+	struct si7020_platform_data *pdata;
 	int ret;
+	bool holdmode;
+	unsigned char buf[2];
+	unsigned long start;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		ret = i2c_smbus_read_word_data(*client,
-					       chan->type == IIO_TEMP ?
-					       SI7020CMD_TEMP_HOLD :
-					       SI7020CMD_RH_HOLD);
-		if (ret < 0)
-			return ret;
-		*val = ret >> 2;
+		pdata = dev_get_platdata(&(*client)->dev);
+		if (pdata)
+			holdmode = pdata->blocking_io;
+		else
+			holdmode = !((*client)->adapter->quirks &&
+					(*client)->adapter->quirks->flags &
+					I2C_AQ_NO_CLK_STRETCH);
+		if (holdmode) {
+			ret = i2c_smbus_read_word_data(*client,
+						       chan->type == IIO_TEMP ?
+						       SI7020CMD_TEMP_HOLD :
+						       SI7020CMD_RH_HOLD);
+			if (ret < 0)
+				return ret;
+			*val = ret >> 2;
+		} else {
+			ret = i2c_smbus_write_byte(*client,
+						   chan->type == IIO_TEMP ?
+						   SI7020CMD_TEMP_NO_HOLD :
+						   SI7020CMD_RH_NO_HOLD);
+			if (ret < 0)
+				return ret;
+			start = jiffies;
+			while ((ret = i2c_master_recv(*client, buf, 2)) < 0) {
+				if (time_after(jiffies, start +
+					       usecs_to_jiffies(
+							chan->type == IIO_TEMP ?
+							SI7020_TEMP_TIMEOUT :
+							SI7020_RH_TIMEOUT)))
+					return ret;
+				usleep_range(SI7020_NOHOLD_SLEEP_MIN,
+					     SI7020_NOHOLD_SLEEP_MAX);
+			}
+			*val = ((buf[0] << 8) | buf[1]) >> 2;
+		}
 		/*
 		 * Humidity values can slightly exceed the 0-100%RH
 		 * range and should be corrected by software
@@ -116,6 +163,7 @@ static int si7020_probe(struct i2c_client *client,
 {
 	struct iio_dev *indio_dev;
 	struct i2c_client **data;
+	struct si7020_platform_data *pdata;
 	int ret;
 
 	if (!i2c_check_functionality(client->adapter,
@@ -123,6 +171,22 @@ static int si7020_probe(struct i2c_client *client,
 				     I2C_FUNC_SMBUS_READ_WORD_DATA))
 		return -ENODEV;
 
+	pdata = dev_get_platdata(&client->dev);
+	if (pdata) {
+		if (pdata->blocking_io) {
+			if (client->adapter->quirks &&
+			   client->adapter->quirks->flags &
+			   I2C_AQ_NO_CLK_STRETCH)
+				return -ENODEV;
+		} else if (!i2c_check_functionality(client->adapter,
+			  I2C_FUNC_I2C))
+			return -ENODEV;
+	} else
+		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
+		   client->adapter->quirks &&
+		   client->adapter->quirks->flags & I2C_AQ_NO_CLK_STRETCH)
+			return -ENODEV;
+
 	/* Reset device, loads default settings. */
 	ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);
 	if (ret < 0)
include/linux/platform_data/si7020.h new file +21 -0
new file mode 100644
index 0000000..8bb5848
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 Nicola Corna <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __SI7020_H_
+#define __SI7020_H_
+
+struct si7020_platform_data {
+	bool blocking_io;
+};
+#endif /* __SI7020_H_ */
Signature · 1 line
2.6.1
#9 lore raw

PATCH v4 4/4 iio: humidity: si7020: added No Hold read mode

On 10/28/2015 07:58 AM, Nicola Corna wrote:
[...]

Quoted 2 lines
drivers/iio/humidity/si7020.c +2 -0
+			holdmode = !((*client)->adapter->quirks &&
+					(*client)->adapter->quirks->flags &

[...]

drivers/iio/humidity/si7020.c static int si7020_probe(struct i2c_client *client, · line 187

Quoted 2 lines
drivers/iio/humidity/si7020.c +2 -0
+		   client->adapter->quirks &&
+		   client->adapter->quirks->flags & I2C_AQ_NO_CLK_STRETCH)

This is rather ugly, can we get a helper in the I2C core something along the lines of

i2c_check_quirks(client->adapter, I2C_AQ_NO_CLK_STRETCH)

- Lars

#10 lore raw

PATCH v4 4/4 iio: humidity: si7020: added No Hold read mode

Quoted 11 lines · Lars-Peter Clausen

On 10/28/2015 07:58 AM, Nicola Corna wrote:
[...]

Quoted 2 lines

+ holdmode = !((*client)->adapter->quirks &&
+ (*client)->adapter->quirks->flags &

[...]

Quoted 2 lines

+ client->adapter->quirks &&
+ client->adapter->quirks->flags & I2C_AQ_NO_CLK_STRETCH)

Quoted 6 lines

This is rather ugly, can we get a helper in the I2C core something along the lines of

i2c_check_quirks(client->adapter, I2C_AQ_NO_CLK_STRETCH)

- Lars

Something like this?

---

include/linux/i2c.h +0 -0
index a69a9a0..a06ffc0 100644
@@ -613,6 +613,12 @@ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)

return (func & i2c_get_functionality(adap)) == func; }

+/* Return 1 if adapter has the specified quirks, 0 if not. */ +static inline int i2c_check_quirks(struct i2c_adapter *adap, u64 quirks) +{
+ return (quirks & (adap->quirks ? adap->quirks->flags : 0)) == quirks; +}
+
/* Return the adapter number for a specific adapter */
static inline int i2c_adapter_id(struct i2c_adapter *adap)
{

#11 lore raw

PATCH v4 4/4 iio: humidity: si7020: added No Hold read mode

Quoted 28 lines · Nicola Corna
Quoted 11 lines · Lars-Peter Clausen

On 10/28/2015 07:58 AM, Nicola Corna wrote:
[...]

Quoted 2 lines

+ holdmode = !((*client)->adapter->quirks &&
+ (*client)->adapter->quirks->flags &

[...]

Quoted 2 lines

+ client->adapter->quirks &&
+ client->adapter->quirks->flags & I2C_AQ_NO_CLK_STRETCH)

Quoted 6 lines

This is rather ugly, can we get a helper in the I2C core something along the lines of

i2c_check_quirks(client->adapter, I2C_AQ_NO_CLK_STRETCH)

- Lars

Something like this?

---

include/linux/i2c.h +0 -0
index a69a9a0..a06ffc0 100644

include/linux/i2c.h static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func) · line 620

Quoted 8 lines
include/linux/i2c.h +5 -0
@@ -613,6 +613,12 @@ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
 return (func & i2c_get_functionality(adap)) == func;
 }
 
+/* Return 1 if adapter has the specified quirks, 0 if not. */
+static inline int i2c_check_quirks(struct i2c_adapter *adap, u64 quirks)
+{
+ return (quirks & (adap->quirks ? adap->quirks->flags : 0)) == quirks;
+}

This is not a code obfuscation contest ;)

So maybe more like this:

static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks)

{
	if (!adap->quirks)
		return false;
	return (adap->quirks->flags & quirks) == quirks;
}

And please use kernel-doc for the documentation.

#12 lore raw

PATCH v4 4/4 iio: humidity: si7020: added No Hold read mode

Quoted 33 lines · Lars-Peter Clausen
Quoted 31 lines · Nicola Corna
Quoted 11 lines · Lars-Peter Clausen

On 10/28/2015 07:58 AM, Nicola Corna wrote:
[...]

Quoted 2 lines

+ holdmode = !((*client)->adapter->quirks &&
+ (*client)->adapter->quirks->flags &

[...]

Quoted 2 lines

+ client->adapter->quirks &&
+ client->adapter->quirks->flags & I2C_AQ_NO_CLK_STRETCH)

Quoted 6 lines

This is rather ugly, can we get a helper in the I2C core something along the lines of

i2c_check_quirks(client->adapter, I2C_AQ_NO_CLK_STRETCH)

- Lars

Something like this?

---

include/linux/i2c.h +0 -0
index a69a9a0..a06ffc0 100644
@@ -613,6 +613,12 @@ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
 return (func & i2c_get_functionality(adap)) == func;
 }
 
Quoted 7 lines

include/linux/i2c.h static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func) · line 620

Quoted 5 lines
include/linux/i2c.h +5 -0
+/* Return 1 if adapter has the specified quirks, 0 if not. */
+static inline int i2c_check_quirks(struct i2c_adapter *adap, u64 quirks)
+{
+ return (quirks & (adap->quirks ? adap->quirks->flags : 0)) == quirks;
+}

This is not a code obfuscation contest ;)

I love one-liners ;)

Quoted 8 lines

So maybe more like this:

static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks) {
if (!adap->quirks)
return false;
return (adap->quirks->flags & quirks) == quirks;
}

Should I use bool (like in your snippet) or int (like i2c_check_functionality) as return type?

Quoted 1 line

And please use kernel-doc for the documentation.

#13 lore raw

PATCH v4 4/4 iio: humidity: si7020: added No Hold read mode

In reply to Lars-Peter Clausen
Quoted 33 lines · Lars-Peter Clausen
Quoted 31 lines · Nicola Corna
Quoted 11 lines · Lars-Peter Clausen

On 10/28/2015 07:58 AM, Nicola Corna wrote:
[...]

Quoted 2 lines

+ holdmode = !((*client)->adapter->quirks &&
+ (*client)->adapter->quirks->flags &

[...]

Quoted 2 lines

+ client->adapter->quirks &&
+ client->adapter->quirks->flags & I2C_AQ_NO_CLK_STRETCH)

Quoted 6 lines

This is rather ugly, can we get a helper in the I2C core something along the lines of

i2c_check_quirks(client->adapter, I2C_AQ_NO_CLK_STRETCH)

- Lars

Something like this?

---

include/linux/i2c.h +0 -0
index a69a9a0..a06ffc0 100644
@@ -613,6 +613,12 @@ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
 return (func & i2c_get_functionality(adap)) == func;
 }
 
Quoted 7 lines

include/linux/i2c.h static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func) · line 620

Quoted 5 lines
include/linux/i2c.h +5 -0
+/* Return 1 if adapter has the specified quirks, 0 if not. */
+static inline int i2c_check_quirks(struct i2c_adapter *adap, u64 quirks)
+{
+ return (quirks & (adap->quirks ? adap->quirks->flags : 0)) == quirks;
+}

This is not a code obfuscation contest ;)

I love one-liners ;)

Quoted 8 lines

So maybe more like this:

static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks) {
if (!adap->quirks)
return false;
return (adap->quirks->flags & quirks) == quirks;
}

Should I use bool (like in your snippet) or int (like i2c_check_functionality) as return type?

Quoted 1 line

And please use kernel-doc for the documentation.

#14 lore raw

PATCH v4 4/4 iio: humidity: si7020: added No Hold read mode

Quoted 10 lines
Quoted 8 lines

So maybe more like this:

static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks) {
if (!adap->quirks)
return false;
return (adap->quirks->flags & quirks) == quirks;
}

Should I use bool (like in your snippet) or int (like i2c_check_functionality) as return type?

I'd use bool, given that the result is a boolean value. It's semantically more clear this way.

#15 lore raw

PATCH v2 1/5 i2c: add i2c quirk flag for unsupported clock stretching

In reply to Nicola Corna

Add I2C_AQ_NO_CLK_STRETCH quirk flag, to be used when clock stretching is not supported.

---
 include/linux/i2c.h | 2 ++
 1 file changed, 2 insertions(+)
include/linux/i2c.h +2 -0
index 768063b..9697002 100644
@@ -493,6 +493,8 @@ struct i2c_adapter_quirks {
 /* convenience macro for typical write-then read case */
 #define I2C_AQ_COMB_WRITE_THEN_READ	(I2C_AQ_COMB | I2C_AQ_COMB_WRITE_FIRST | \
 					 I2C_AQ_COMB_READ_SECOND | I2C_AQ_COMB_SAME_ADDR)
+/* clock stretching is not supported */
+#define I2C_AQ_NO_CLK_STRETCH		BIT(4)
 
 /*
  * i2c_adapter is the structure used to identify a physical i2c bus along
Signature · 1 line
2.6.1
#16 lore raw

PATCH v2 2/5 i2c: add I2C_AQ_NO_CLK_STRETCH to i2c-algo-bit.c

Add I2C_AQ_NO_CLK_STRETCH to drivers/i2c/algos/i2c-algo-bit.c when getscl is not available.

---
 drivers/i2c/algos/i2c-algo-bit.c | 6 ++++++
 1 file changed, 6 insertions(+)
drivers/i2c/algos/i2c-algo-bit.c +6 -0
index 899bede..9d233bb 100644
@@ -617,6 +617,10 @@ const struct i2c_algorithm i2c_bit_algo = {
 };
 EXPORT_SYMBOL(i2c_bit_algo);
 
+const struct i2c_adapter_quirks i2c_bit_quirk_no_clk_stretch = {
+	.flags = I2C_AQ_NO_CLK_STRETCH,
+};
+
 /*
  * registering functions to load algorithms at runtime
  */
@@ -635,6 +639,8 @@ static int __i2c_bit_add_bus(struct i2c_adapter *adap,
 	/* register new adapter to i2c module... */
 	adap->algo = &i2c_bit_algo;
 	adap->retries = 3;
+	if (bit_adap->getscl == NULL)
+		adap->quirks = &i2c_bit_quirk_no_clk_stretch;
 
 	ret = add_adapter(adap);
 	if (ret < 0)
Signature · 1 line
2.6.1
Applied
#17 lore raw

PATCH v2 2/5 i2c: add I2C_AQ_NO_CLK_STRETCH to i2c-algo-bit.c

Quoted 4 lines · Nicola Corna

Add I2C_AQ_NO_CLK_STRETCH to drivers/i2c/algos/i2c-algo-bit.c when getscl is not available.

Applied to for-next, thanks! Please check git-log next time to see how the subsystem likes its prefixes, here "i2c: algo-bit: ...".

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151130/4829bf49/attachment.sig>

#18 lore raw

PATCH v2 3/5 i2c: add I2C_AQ_NO_CLK_STRETCH to i2c-bcm2835.c

In reply to Nicola Corna

As reported here
http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
and confirmed here
https://www.raspberrypi.org/forums/viewtopic.php?p=146272
the BCM2835 has a hardware bug in its i2c module which prevents a correct clock stretching. This patch adds the I2C_AQ_NO_CLK_STRETCH quirk flag to i2c-bcm2835.

---
 drivers/i2c/busses/i2c-bcm2835.c | 5 +++++
 1 file changed, 5 insertions(+)
drivers/i2c/busses/i2c-bcm2835.c +5 -0
index 3032b89..38d7102 100644
@@ -222,6 +222,10 @@ static const struct i2c_algorithm bcm2835_i2c_algo = {
 	.functionality	= bcm2835_i2c_func,
 };
 
+static const struct i2c_adapter_quirks bcm2835_i2c_quirks = {
+	.flags = I2C_AQ_NO_CLK_STRETCH,
+};
+
 static int bcm2835_i2c_probe(struct platform_device *pdev)
 {
 	struct bcm2835_i2c_dev *i2c_dev;
@@ -293,6 +297,7 @@ static int bcm2835_i2c_probe(struct platform_device *pdev)
 	adap->algo = &bcm2835_i2c_algo;
 	adap->dev.parent = &pdev->dev;
 	adap->dev.of_node = pdev->dev.of_node;
+	adap->quirks = &bcm2835_i2c_quirks;
 
 	bcm2835_i2c_writel(i2c_dev, BCM2835_I2C_C, 0);
 
Signature · 1 line
2.6.1
#19 lore raw

PATCH v2 3/5 i2c: add I2C_AQ_NO_CLK_STRETCH to i2c-bcm2835.c

Quoted 9 lines · Nicola Corna

As reported here
http://www.advamation.com/knowhow/raspberrypi/rpi-i2c-bug.html
and confirmed here
https://www.raspberrypi.org/forums/viewtopic.php?p=146272
the BCM2835 has a hardware bug in its i2c module which prevents a correct clock stretching. This patch adds the I2C_AQ_NO_CLK_STRETCH quirk flag to i2c-bcm2835.

I put the above links into the code as comments, reworded the commit message a little and applied it to for-next, thanks!

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151130/eee0f79a/attachment.sig>

#20 lore raw

PATCH 4/5 i2c: add i2c_check_quirks helper function

In reply to Nicola Corna

This patch adds a i2c_check_quirks helper function to check the quirk flags of an i2c adapter, in a similar way to i2c_check_functionality.

---
 include/linux/i2c.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
include/linux/i2c.h +14 -0
index 9697002..51028f3 100644
@@ -617,6 +617,20 @@ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
 	return (func & i2c_get_functionality(adap)) == func;
 }
 
+/**
+ * i2c_check_quirks() - Function for checking the quirk flags in an i2c adapter
+ * @adap: i2c adapter
+ * @quirks: quirk flags
+ *
+ * Return: true if the adapter has all the specified quirk flags, false if not
+ */
+static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks)
+{
+	if (!adap->quirks)
+		return false;
+	return (adap->quirks->flags & quirks) == quirks;
+}
+
 /* Return the adapter number for a specific adapter */
 static inline int i2c_adapter_id(struct i2c_adapter *adap)
 {
Signature · 1 line
2.6.1
maintainer
#21 lore raw

PATCH 4/5 i2c: add i2c_check_quirks helper function

Quoted 4 lines · Nicola Corna

This patch adds a i2c_check_quirks helper function to check the quirk flags of an i2c adapter, in a similar way to i2c_check_functionality.

I don't know about everyone else, but I'm finding this particular style of email thread very difficult to follow.
Perhaps either:
* Start a new thread for each new series version.
* label the whole series with a rolling version number rather than doing it on a per patch basis.

Would just make it easier to keep track when one reads the thread a while after it started!

Jonathan

Quoted 30 lines
---
 include/linux/i2c.h | 14 ++++++++++++++
 1 file changed, 14 insertions(+)
include/linux/i2c.h +14 -0
index 9697002..51028f3 100644
@@ -617,6 +617,20 @@ static inline int i2c_check_functionality(struct i2c_adapter *adap, u32 func)
 	return (func & i2c_get_functionality(adap)) == func;
 }
 
+/**
+ * i2c_check_quirks() - Function for checking the quirk flags in an i2c adapter
+ * @adap: i2c adapter
+ * @quirks: quirk flags
+ *
+ * Return: true if the adapter has all the specified quirk flags, false if not
+ */
+static inline bool i2c_check_quirks(struct i2c_adapter *adap, u64 quirks)
+{
+	if (!adap->quirks)
+		return false;
+	return (adap->quirks->flags & quirks) == quirks;
+}
+
 /* Return the adapter number for a specific adapter */
 static inline int i2c_adapter_id(struct i2c_adapter *adap)
 {
#22 lore raw

PATCH 4/5 i2c: add i2c_check_quirks helper function

Quoted 2 lines

I don't know about everyone else, but I'm finding this particular style of email thread very difficult to follow.

Same here.

Quoted 2 lines

Perhaps either:
* Start a new thread for each new series version.

+1

Quoted 2 lines

* label the whole series with a rolling version number rather than doing it on a per patch basis.

+1

Quoted 3 lines

Would just make it easier to keep track when one reads the thread a while after it started!

+1 :)

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151130/6d0e85e9/attachment.sig>

Applied
#23 lore raw

PATCH 4/5 i2c: add i2c_check_quirks helper function

In reply to Nicola Corna
Quoted 4 lines · Nicola Corna

This patch adds a i2c_check_quirks helper function to check the quirk flags of an i2c adapter, in a similar way to i2c_check_functionality.

Applied to for-next, thanks!

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151130/d1c52025/attachment.sig>

#24 lore raw

PATCH v5 5/5 iio: humidity: si7020: add No Hold read mode

In reply to Nicola Corna

The Si7013/20/21 modules support 2 read modes:
* Hold mode (blocking), where the device stretches the clock until the end
of the measurement
* No Hold mode (non-blocking), where the device replies NACK for every I2C
call during the measurement
Here the No Hold mode is implemented, selectable with the blocking_io variable within si7020_platform_data. The default mode is Hold, unless the adapter does not support clock stretching, in which case the No Hold mode is used.

---
This patch depends on patch "[PATCH v4 1/2] iio: humidity: si7020: replaced bitmask on humidity values with range check"

 drivers/iio/humidity/si7020.c        | 75 ++++++++++++++++++++++++++++++++----
 include/linux/platform_data/si7020.h | 21 ++++++++++
 2 files changed, 89 insertions(+), 7 deletions(-)
 create mode 100644 include/linux/platform_data/si7020.h
drivers/iio/humidity/si7020.c +68 -7
index 12128d1..674a61a 100644
@@ -2,6 +2,7 @@
  * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors
  * Copyright (c) 2013,2014  Uplogix, Inc.
  * David Barksdale <[email protected]>
+ * Copyright (c) 2015 Nicola Corna <[email protected]>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -30,33 +31,78 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
+#include <linux/jiffies.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/platform_data/si7020.h>
 
 /* Measure Relative Humidity, Hold Master Mode */
 #define SI7020CMD_RH_HOLD	0xE5
+/* Measure Relative Humidity, No Hold Master Mode */
+#define SI7020CMD_RH_NO_HOLD	0xF5
 /* Measure Temperature, Hold Master Mode */
 #define SI7020CMD_TEMP_HOLD	0xE3
+/* Measure Temperature, No Hold Master Mode */
+#define SI7020CMD_TEMP_NO_HOLD	0xF3
 /* Software Reset */
 #define SI7020CMD_RESET		0xFE
+/* Relative humidity measurement timeout (us) */
+#define SI7020_RH_TIMEOUT	22800
+/* Temperature measurement timeout (us) */
+#define SI7020_TEMP_TIMEOUT	10800
+/* Minimum delay between retries (No Hold Mode) in us */
+#define SI7020_NOHOLD_SLEEP_MIN	2000
+/* Maximum delay between retries (No Hold Mode) in us */
+#define SI7020_NOHOLD_SLEEP_MAX	6000
 
 static int si7020_read_raw(struct iio_dev *indio_dev,
 			   struct iio_chan_spec const *chan, int *val,
 			   int *val2, long mask)
 {
 	struct i2c_client **client = iio_priv(indio_dev);
+	struct si7020_platform_data *pdata;
 	int ret;
+	bool holdmode;
+	unsigned char buf[2];
+	unsigned long start;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		ret = i2c_smbus_read_word_data(*client,
-					       chan->type == IIO_TEMP ?
-					       SI7020CMD_TEMP_HOLD :
-					       SI7020CMD_RH_HOLD);
-		if (ret < 0)
-			return ret;
-		*val = ret >> 2;
+		pdata = dev_get_platdata(&(*client)->dev);
+		if (pdata)
+			holdmode = pdata->blocking_io;
+		else
+			holdmode = !i2c_check_quirks((*client)->adapter,
+				   I2C_AQ_NO_CLK_STRETCH);
+		if (holdmode) {
+			ret = i2c_smbus_read_word_data(*client,
+						       chan->type == IIO_TEMP ?
+						       SI7020CMD_TEMP_HOLD :
+						       SI7020CMD_RH_HOLD);
+			if (ret < 0)
+				return ret;
+			*val = ret >> 2;
+		} else {
+			ret = i2c_smbus_write_byte(*client,
+						   chan->type == IIO_TEMP ?
+						   SI7020CMD_TEMP_NO_HOLD :
+						   SI7020CMD_RH_NO_HOLD);
+			if (ret < 0)
+				return ret;
+			start = jiffies;
+			while ((ret = i2c_master_recv(*client, buf, 2)) < 0) {
+				if (time_after(jiffies, start +
+					       usecs_to_jiffies(
+							chan->type == IIO_TEMP ?
+							SI7020_TEMP_TIMEOUT :
+							SI7020_RH_TIMEOUT)))
+					return ret;
+				usleep_range(SI7020_NOHOLD_SLEEP_MIN,
+					     SI7020_NOHOLD_SLEEP_MAX);
+			}
+			*val = ((buf[0] << 8) | buf[1]) >> 2;
+		}
 		/*
 		 * Humidity values can slightly exceed the 0-100%RH
 		 * range and should be corrected by software
@@ -116,6 +162,7 @@ static int si7020_probe(struct i2c_client *client,
 {
 	struct iio_dev *indio_dev;
 	struct i2c_client **data;
+	struct si7020_platform_data *pdata;
 	int ret;
 
 	if (!i2c_check_functionality(client->adapter,
@@ -123,6 +170,20 @@ static int si7020_probe(struct i2c_client *client,
 				     I2C_FUNC_SMBUS_READ_WORD_DATA))
 		return -ENODEV;
 
+	pdata = dev_get_platdata(&client->dev);
+	if (pdata) {
+		if (pdata->blocking_io) {
+			if (i2c_check_quirks(client->adapter,
+			   I2C_AQ_NO_CLK_STRETCH))
+				return -ENODEV;
+		} else if (!i2c_check_functionality(client->adapter,
+			  I2C_FUNC_I2C))
+			return -ENODEV;
+	} else
+		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
+		   i2c_check_quirks(client->adapter, I2C_AQ_NO_CLK_STRETCH))
+			return -ENODEV;
+
 	/* Reset device, loads default settings. */
 	ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);
 	if (ret < 0)
include/linux/platform_data/si7020.h new file +21 -0
new file mode 100644
index 0000000..8bb5848
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 Nicola Corna <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __SI7020_H_
+#define __SI7020_H_
+
+struct si7020_platform_data {
+	bool blocking_io;
+};
+#endif /* __SI7020_H_ */
Signature · 1 line
2.6.1
maintainer
#25 lore raw

PATCH v5 5/5 iio: humidity: si7020: add No Hold read mode

Quoted 11 lines · Nicola Corna

The Si7013/20/21 modules support 2 read modes:
* Hold mode (blocking), where the device stretches the clock until the end
of the measurement
* No Hold mode (non-blocking), where the device replies NACK for every I2C
call during the measurement
Here the No Hold mode is implemented, selectable with the blocking_io variable within si7020_platform_data. The default mode is Hold, unless the adapter does not support clock stretching, in which case the No Hold mode is used.

I'm fine with this. The dependency below should I think show up in the coming merge window, so this lot might as well go via Wolfram.

Quoted 163 lines

---
This patch depends on patch "[PATCH v4 1/2] iio: humidity: si7020: replaced bitmask on humidity values with range check"

 drivers/iio/humidity/si7020.c        | 75 ++++++++++++++++++++++++++++++++----
 include/linux/platform_data/si7020.h | 21 ++++++++++
 2 files changed, 89 insertions(+), 7 deletions(-)
 create mode 100644 include/linux/platform_data/si7020.h
drivers/iio/humidity/si7020.c +68 -7
index 12128d1..674a61a 100644
@@ -2,6 +2,7 @@
  * si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors
  * Copyright (c) 2013,2014  Uplogix, Inc.
  * David Barksdale <[email protected]>
+ * Copyright (c) 2015 Nicola Corna <[email protected]>
  *
  * This program is free software; you can redistribute it and/or modify it
  * under the terms and conditions of the GNU General Public License,
@@ -30,33 +31,78 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/sysfs.h>
+#include <linux/jiffies.h>
 
 #include <linux/iio/iio.h>
 #include <linux/iio/sysfs.h>
+#include <linux/platform_data/si7020.h>
 
 /* Measure Relative Humidity, Hold Master Mode */
 #define SI7020CMD_RH_HOLD	0xE5
+/* Measure Relative Humidity, No Hold Master Mode */
+#define SI7020CMD_RH_NO_HOLD	0xF5
 /* Measure Temperature, Hold Master Mode */
 #define SI7020CMD_TEMP_HOLD	0xE3
+/* Measure Temperature, No Hold Master Mode */
+#define SI7020CMD_TEMP_NO_HOLD	0xF3
 /* Software Reset */
 #define SI7020CMD_RESET		0xFE
+/* Relative humidity measurement timeout (us) */
+#define SI7020_RH_TIMEOUT	22800
+/* Temperature measurement timeout (us) */
+#define SI7020_TEMP_TIMEOUT	10800
+/* Minimum delay between retries (No Hold Mode) in us */
+#define SI7020_NOHOLD_SLEEP_MIN	2000
+/* Maximum delay between retries (No Hold Mode) in us */
+#define SI7020_NOHOLD_SLEEP_MAX	6000
 
 static int si7020_read_raw(struct iio_dev *indio_dev,
 			   struct iio_chan_spec const *chan, int *val,
 			   int *val2, long mask)
 {
 	struct i2c_client **client = iio_priv(indio_dev);
+	struct si7020_platform_data *pdata;
 	int ret;
+	bool holdmode;
+	unsigned char buf[2];
+	unsigned long start;
 
 	switch (mask) {
 	case IIO_CHAN_INFO_RAW:
-		ret = i2c_smbus_read_word_data(*client,
-					       chan->type == IIO_TEMP ?
-					       SI7020CMD_TEMP_HOLD :
-					       SI7020CMD_RH_HOLD);
-		if (ret < 0)
-			return ret;
-		*val = ret >> 2;
+		pdata = dev_get_platdata(&(*client)->dev);
+		if (pdata)
+			holdmode = pdata->blocking_io;
+		else
+			holdmode = !i2c_check_quirks((*client)->adapter,
+				   I2C_AQ_NO_CLK_STRETCH);
+		if (holdmode) {
+			ret = i2c_smbus_read_word_data(*client,
+						       chan->type == IIO_TEMP ?
+						       SI7020CMD_TEMP_HOLD :
+						       SI7020CMD_RH_HOLD);
+			if (ret < 0)
+				return ret;
+			*val = ret >> 2;
+		} else {
+			ret = i2c_smbus_write_byte(*client,
+						   chan->type == IIO_TEMP ?
+						   SI7020CMD_TEMP_NO_HOLD :
+						   SI7020CMD_RH_NO_HOLD);
+			if (ret < 0)
+				return ret;
+			start = jiffies;
+			while ((ret = i2c_master_recv(*client, buf, 2)) < 0) {
+				if (time_after(jiffies, start +
+					       usecs_to_jiffies(
+							chan->type == IIO_TEMP ?
+							SI7020_TEMP_TIMEOUT :
+							SI7020_RH_TIMEOUT)))
+					return ret;
+				usleep_range(SI7020_NOHOLD_SLEEP_MIN,
+					     SI7020_NOHOLD_SLEEP_MAX);
+			}
+			*val = ((buf[0] << 8) | buf[1]) >> 2;
+		}
 		/*
 		 * Humidity values can slightly exceed the 0-100%RH
 		 * range and should be corrected by software
@@ -116,6 +162,7 @@ static int si7020_probe(struct i2c_client *client,
 {
 	struct iio_dev *indio_dev;
 	struct i2c_client **data;
+	struct si7020_platform_data *pdata;
 	int ret;
 
 	if (!i2c_check_functionality(client->adapter,
@@ -123,6 +170,20 @@ static int si7020_probe(struct i2c_client *client,
 				     I2C_FUNC_SMBUS_READ_WORD_DATA))
 		return -ENODEV;
 
+	pdata = dev_get_platdata(&client->dev);
+	if (pdata) {
+		if (pdata->blocking_io) {
+			if (i2c_check_quirks(client->adapter,
+			   I2C_AQ_NO_CLK_STRETCH))
+				return -ENODEV;
+		} else if (!i2c_check_functionality(client->adapter,
+			  I2C_FUNC_I2C))
+			return -ENODEV;
+	} else
+		if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C) &&
+		   i2c_check_quirks(client->adapter, I2C_AQ_NO_CLK_STRETCH))
+			return -ENODEV;
+
 	/* Reset device, loads default settings. */
 	ret = i2c_smbus_write_byte(client, SI7020CMD_RESET);
 	if (ret < 0)
include/linux/platform_data/si7020.h new file +21 -0
new file mode 100644
index 0000000..8bb5848
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 Nicola Corna <[email protected]>
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ *
+ */
+
+#ifndef __SI7020_H_
+#define __SI7020_H_
+
+struct si7020_platform_data {
+	bool blocking_io;
+};
+#endif /* __SI7020_H_ */
#26 lore raw

PATCH v5 5/5 iio: humidity: si7020: add No Hold read mode

Quoted 11 lines · Jonathan Cameron
Quoted 10 lines · Nicola Corna

The Si7013/20/21 modules support 2 read modes:
* Hold mode (blocking), where the device stretches the clock until the end
of the measurement
* No Hold mode (non-blocking), where the device replies NACK for every I2C
call during the measurement
Here the No Hold mode is implemented, selectable with the blocking_io variable within si7020_platform_data. The default mode is Hold, unless the adapter does not support clock stretching, in which case the No Hold mode is used.

Quoted 5 lines
Quoted 1 line

I'm fine with this. The dependency below should I think show up in the coming merge window, so this lot might as well go via Wolfram.

Not sure, will think about it...

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151130/35d428e1/attachment.sig>

maintainer
#27 lore raw

PATCH v5 5/5 iio: humidity: si7020: add No Hold read mode

Quoted 12 lines · Wolfram Sang
Quoted 11 lines · Jonathan Cameron
Quoted 10 lines · Nicola Corna

The Si7013/20/21 modules support 2 read modes:
* Hold mode (blocking), where the device stretches the clock until the end
of the measurement
* No Hold mode (non-blocking), where the device replies NACK for every I2C
call during the measurement
Here the No Hold mode is implemented, selectable with the blocking_io variable within si7020_platform_data. The default mode is Hold, unless the adapter does not support clock stretching, in which case the No Hold mode is used.

Quoted 8 lines
Quoted 5 lines
Quoted 1 line

I'm fine with this. The dependency below should I think show up in the coming merge window, so this lot might as well go via Wolfram.

Not sure, will think about it...

Nicola,

If Wolfram doesn't pick this up, please ping me after the next merge window. Whilst I'll still have it in my queue, it'll be so long ago by then I'll probably miss it!

Jonathan

#28 lore raw

PATCH v5 5/5 iio: humidity: si7020: add No Hold read mode

Quoted 2 lines

If Wolfram doesn't pick this up, please ping me after the next merge window.

I tried to pick it a second ago, but it could not be applied (my branch
is based on 4.4-rc3). So, I really think it's better you pick it and
handle the conflicts.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151205/fe6c7694/attachment.sig>

#29 lore raw

Re: [PATCH v5 5/5] iio: humidity: si7020: add No Hold read mode

In reply to Jonathan Cameron

Hi!

Quoted 33 lines · Jonathan Cameron
Quoted 4 lines · Wolfram Sang
Quoted 3 lines · Jonathan Cameron
Quoted 2 lines · Nicola Corna

The Si7013/20/21 modules support 2 read modes:
* Hold mode (blocking), where the device stretches the clock until

the end

Quoted 2 lines
Quoted 2 lines
Quoted 2 lines

of the measurement
* No Hold mode (non-blocking), where the device replies NACK for

every I2C

Quoted 3 lines
Quoted 3 lines
Quoted 3 lines

call during the measurement
Here the No Hold mode is implemented, selectable with the blocking_io variable within si7020_platform_data. The default mode is Hold, unless

the

Quoted 1 line
Quoted 1 line
Quoted 1 line

adapter does not support clock stretching, in which case the No Hold

mode

Quoted 10 lines
Quoted 7 lines
Quoted 3 lines

is used.

I'm fine with this. The dependency below should I think show up in the coming merge window, so this lot might as well go via Wolfram.

Not sure, will think about it...

Nicola,

If Wolfram doesn't pick this up, please ping me after the next merge window.
Whilst I'll still have it in my queue, it'll be so long ago by then I'll probably
miss it!

Quoted 6 lines

Jonathan
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html

Resurrecting this almost two years old thread because I just found myself in the situation of using Si7020 with an I2C adapter with broken clock stretching (probably the same that originated this work). Well, AFAIKT it seems that this already ack-ed patchset never landed in mainline, probably due to unlucky timing with merging windows. Didn't try the original patches myself yet, but going to do it soon... Just wanted to ping people who may already have an updated version at hand...

Regards

P.S: sorry for unintentional double posting of HTML emails... :-(

Applied
#30 lore raw

PATCH v2 1/5 i2c: add i2c quirk flag for unsupported clock stretching

In reply to Nicola Corna
Quoted 4 lines · Nicola Corna

Add I2C_AQ_NO_CLK_STRETCH quirk flag, to be used when clock stretching is not supported.

Applied to for-next, thanks!

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20151130/4bb983be/attachment.sig>