All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/7] add i2c controller support to st_lsm6dsx driver
@ 2018-11-04 14:38 Lorenzo Bianconi
  2018-11-04 14:39 ` [PATCH 1/7] iio: imu: st_lsm6dsx: introduce locked read/write utility routines Lorenzo Bianconi
                   ` (7 more replies)
  0 siblings, 8 replies; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 14:38 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree

Introduce i2c controller support to st_lsm6dsx driver for lsm6dso sensor.
Add register map for lis2mdl magnetometer sensor.
Add hw FIFO support to st_lsm6dsx sensorhub driver.

Lorenzo Bianconi (7):
  iio: imu: st_lsm6dsx: introduce locked read/write utility routines
  iio: imu: st_lsm6dsx: reboot memory content after reset
  iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark
  iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
  iio: imu: st_lsm6dsx: add i2c embedded controller support
  iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
  dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors

 .../bindings/iio/imu/st_lsm6dsx.txt           |   1 +
 drivers/iio/imu/st_lsm6dsx/Makefile           |   3 +-
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 167 +++-
 .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 169 ++--
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 273 +++++--
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 753 ++++++++++++++++++
 .../linux/platform_data/st_sensors_pdata.h    |   2 +
 7 files changed, 1226 insertions(+), 142 deletions(-)
 create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c

-- 
2.19.1

^ permalink raw reply	[flat|nested] 27+ messages in thread

* [PATCH 1/7] iio: imu: st_lsm6dsx: introduce locked read/write utility routines
  2018-11-04 14:38 [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Lorenzo Bianconi
@ 2018-11-04 14:39 ` Lorenzo Bianconi
  2018-11-04 17:11   ` Jonathan Cameron
  2018-11-04 14:39 ` [PATCH 2/7] iio: imu: st_lsm6dsx: reboot memory content after reset Lorenzo Bianconi
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 14:39 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree

Add st_lsm6dsx_update_bits_locked, st_lsm6dsx_read_locked and
st_lsm6dsx_write_locked utility routines in order to guarantee
the bus access is atomic respect to reg page configuration.
This is a preliminary patch to add i2c sensor hub support since
i2c master registers are accessed through a reg page multiplexer

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 41 +++++++++++
 .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 70 +++++++++----------
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 24 ++++---
 3 files changed, 91 insertions(+), 44 deletions(-)

diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index ef73519a0fb6..ec204d3b4b1f 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -148,6 +148,7 @@ struct st_lsm6dsx_sensor {
  * @irq: Device interrupt line (I2C or SPI).
  * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
  * @conf_lock: Mutex to prevent concurrent FIFO configuration update.
+ * @page_lock: Mutex to prevent concurrent memory page configuration.
  * @fifo_mode: FIFO operating mode supported by the device.
  * @enable_mask: Enabled sensor bitmask.
  * @ts_sip: Total number of timestamp samples in a given pattern.
@@ -163,6 +164,7 @@ struct st_lsm6dsx_hw {
 
 	struct mutex fifo_lock;
 	struct mutex conf_lock;
+	struct mutex page_lock;
 
 	enum st_lsm6dsx_fifo_mode fifo_mode;
 	u8 enable_mask;
@@ -192,4 +194,43 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw);
 int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw);
 int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val);
 
+static inline int
+st_lsm6dsx_update_bits_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
+			      unsigned int mask, unsigned int val)
+{
+	int err;
+
+	mutex_lock(&hw->page_lock);
+	err = regmap_update_bits(hw->regmap, addr, mask, val);
+	mutex_unlock(&hw->page_lock);
+
+	return err;
+}
+
+static inline int
+st_lsm6dsx_read_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
+		       void *val, unsigned int len)
+{
+	int err;
+
+	mutex_lock(&hw->page_lock);
+	err = regmap_bulk_read(hw->regmap, addr, val, len);
+	mutex_unlock(&hw->page_lock);
+
+	return err;
+}
+
+static inline int
+st_lsm6dsx_write_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
+			unsigned int val)
+{
+	int err;
+
+	mutex_lock(&hw->page_lock);
+	err = regmap_write(hw->regmap, addr, val);
+	mutex_unlock(&hw->page_lock);
+
+	return err;
+}
+
 #endif /* ST_LSM6DSX_H */
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index b5263fc522ca..4b3ba0956b5a 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -142,8 +142,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
 		if (dec_reg->addr) {
 			int val = ST_LSM6DSX_SHIFT_VAL(data, dec_reg->mask);
 
-			err = regmap_update_bits(hw->regmap, dec_reg->addr,
-						 dec_reg->mask, val);
+			err = st_lsm6dsx_update_bits_locked(hw, dec_reg->addr,
+							    dec_reg->mask,
+							    val);
 			if (err < 0)
 				return err;
 		}
@@ -162,8 +163,8 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
 		int val, ts_dec = !!hw->ts_sip;
 
 		val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask);
-		err = regmap_update_bits(hw->regmap, ts_dec_reg->addr,
-					 ts_dec_reg->mask, val);
+		err = st_lsm6dsx_update_bits_locked(hw, ts_dec_reg->addr,
+						    ts_dec_reg->mask, val);
 	}
 	return err;
 }
@@ -171,12 +172,12 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
 int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
 			     enum st_lsm6dsx_fifo_mode fifo_mode)
 {
+	unsigned int data;
 	int err;
 
-	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
-				 ST_LSM6DSX_FIFO_MODE_MASK,
-				 FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK,
-					    fifo_mode));
+	data = FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK, fifo_mode);
+	err = st_lsm6dsx_update_bits_locked(hw, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
+					    ST_LSM6DSX_FIFO_MODE_MASK, data);
 	if (err < 0)
 		return err;
 
@@ -207,15 +208,15 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor,
 			data = 0;
 		}
 		val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask);
-		return regmap_update_bits(hw->regmap, batch_reg->addr,
-					  batch_reg->mask, val);
+		return st_lsm6dsx_update_bits_locked(hw, batch_reg->addr,
+						     batch_reg->mask, val);
 	} else {
 		data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0;
-		return regmap_update_bits(hw->regmap,
-					  ST_LSM6DSX_REG_FIFO_MODE_ADDR,
-					  ST_LSM6DSX_FIFO_ODR_MASK,
-					  FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK,
-						     data));
+		return st_lsm6dsx_update_bits_locked(hw,
+					ST_LSM6DSX_REG_FIFO_MODE_ADDR,
+					ST_LSM6DSX_FIFO_ODR_MASK,
+					FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK,
+						   data));
 	}
 }
 
@@ -246,19 +247,23 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
 	fifo_watermark = (fifo_watermark / hw->sip) * hw->sip;
 	fifo_watermark = fifo_watermark * hw->settings->fifo_ops.th_wl;
 
+	mutex_lock(&hw->page_lock);
 	err = regmap_read(hw->regmap, hw->settings->fifo_ops.fifo_th.addr + 1,
 			  &data);
 	if (err < 0)
-		return err;
+		goto out;
 
 	fifo_th_mask = hw->settings->fifo_ops.fifo_th.mask;
 	fifo_watermark = ((data << 8) & ~fifo_th_mask) |
 			 (fifo_watermark & fifo_th_mask);
 
 	wdata = cpu_to_le16(fifo_watermark);
-	return regmap_bulk_write(hw->regmap,
-				 hw->settings->fifo_ops.fifo_th.addr,
-				 &wdata, sizeof(wdata));
+	err = regmap_bulk_write(hw->regmap,
+				hw->settings->fifo_ops.fifo_th.addr,
+				&wdata, sizeof(wdata));
+out:
+	mutex_unlock(&hw->page_lock);
+	return err;
 }
 
 static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
@@ -267,8 +272,8 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
 	int i, err;
 
 	/* reset hw ts counter */
-	err = regmap_write(hw->regmap, ST_LSM6DSX_REG_TS_RESET_ADDR,
-			   ST_LSM6DSX_TS_RESET_VAL);
+	err = st_lsm6dsx_write_locked(hw, ST_LSM6DSX_REG_TS_RESET_ADDR,
+				      ST_LSM6DSX_TS_RESET_VAL);
 	if (err < 0)
 		return err;
 
@@ -297,8 +302,8 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr,
 	while (read_len < data_len) {
 		word_len = min_t(unsigned int, data_len - read_len,
 				 max_word_len);
-		err = regmap_bulk_read(hw->regmap, addr, data + read_len,
-				       word_len);
+		err = st_lsm6dsx_read_locked(hw, addr, data + read_len,
+					     word_len);
 		if (err < 0)
 			return err;
 		read_len += word_len;
@@ -328,9 +333,9 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
 	__le16 fifo_status;
 	s64 ts = 0;
 
-	err = regmap_bulk_read(hw->regmap,
-			       hw->settings->fifo_ops.fifo_diff.addr,
-			       &fifo_status, sizeof(fifo_status));
+	err = st_lsm6dsx_read_locked(hw,
+				     hw->settings->fifo_ops.fifo_diff.addr,
+				     &fifo_status, sizeof(fifo_status));
 	if (err < 0) {
 		dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
 			err);
@@ -455,9 +460,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
 	__le16 fifo_status;
 	s64 ts = 0;
 
-	err = regmap_bulk_read(hw->regmap,
-			       hw->settings->fifo_ops.fifo_diff.addr,
-			       &fifo_status, sizeof(fifo_status));
+	err = st_lsm6dsx_read_locked(hw,
+				     hw->settings->fifo_ops.fifo_diff.addr,
+				     &fifo_status, sizeof(fifo_status));
 	if (err < 0) {
 		dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
 			err);
@@ -536,16 +541,11 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
 
 int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
 {
-	int err;
-
 	mutex_lock(&hw->fifo_lock);
-
 	hw->settings->fifo_ops.read_fifo(hw);
-	err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
-
 	mutex_unlock(&hw->fifo_lock);
 
-	return err;
+	return st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
 }
 
 static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index 2ad3c610e4b6..c8b993bea757 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -421,6 +421,7 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
 {
 	struct st_lsm6dsx_hw *hw = sensor->hw;
 	const struct st_lsm6dsx_reg *reg;
+	unsigned int data;
 	int i, err;
 	u8 val;
 
@@ -433,8 +434,8 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
 
 	val = st_lsm6dsx_fs_table[sensor->id].fs_avl[i].val;
 	reg = &st_lsm6dsx_fs_table[sensor->id].reg;
-	err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
-				 ST_LSM6DSX_SHIFT_VAL(val, reg->mask));
+	data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
+	err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
 	if (err < 0)
 		return err;
 
@@ -463,6 +464,7 @@ static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
 {
 	struct st_lsm6dsx_hw *hw = sensor->hw;
 	const struct st_lsm6dsx_reg *reg;
+	unsigned int data;
 	int err;
 	u8 val;
 
@@ -471,8 +473,8 @@ static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
 		return err;
 
 	reg = &st_lsm6dsx_odr_table[sensor->id].reg;
-	return regmap_update_bits(hw->regmap, reg->addr, reg->mask,
-				  ST_LSM6DSX_SHIFT_VAL(val, reg->mask));
+	data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
+	return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
 }
 
 int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
@@ -492,11 +494,12 @@ int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
 {
 	struct st_lsm6dsx_hw *hw = sensor->hw;
 	const struct st_lsm6dsx_reg *reg;
+	unsigned int data;
 	int err;
 
 	reg = &st_lsm6dsx_odr_table[sensor->id].reg;
-	err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
-				 ST_LSM6DSX_SHIFT_VAL(0, reg->mask));
+	data = ST_LSM6DSX_SHIFT_VAL(0, reg->mask);
+	err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
 	if (err < 0)
 		return err;
 
@@ -519,7 +522,7 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
 	delay = 1000000 / sensor->odr;
 	usleep_range(delay, 2 * delay);
 
-	err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data));
+	err = st_lsm6dsx_read_locked(hw, addr, &data, sizeof(data));
 	if (err < 0)
 		return err;
 
@@ -865,6 +868,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
 
 	mutex_init(&hw->fifo_lock);
 	mutex_init(&hw->conf_lock);
+	mutex_init(&hw->page_lock);
 
 	hw->buff = devm_kzalloc(dev, ST_LSM6DSX_BUFF_SIZE, GFP_KERNEL);
 	if (!hw->buff)
@@ -909,6 +913,7 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
 	struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev);
 	struct st_lsm6dsx_sensor *sensor;
 	const struct st_lsm6dsx_reg *reg;
+	unsigned int data;
 	int i, err = 0;
 
 	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
@@ -917,8 +922,9 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
 			continue;
 
 		reg = &st_lsm6dsx_odr_table[sensor->id].reg;
-		err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
-					 ST_LSM6DSX_SHIFT_VAL(0, reg->mask));
+		data = ST_LSM6DSX_SHIFT_VAL(0, reg->mask);
+		err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask,
+						    data);
 		if (err < 0)
 			return err;
 	}
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 2/7] iio: imu: st_lsm6dsx: reboot memory content after reset
  2018-11-04 14:38 [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Lorenzo Bianconi
  2018-11-04 14:39 ` [PATCH 1/7] iio: imu: st_lsm6dsx: introduce locked read/write utility routines Lorenzo Bianconi
@ 2018-11-04 14:39 ` Lorenzo Bianconi
  2018-11-04 17:12   ` Jonathan Cameron
  2018-11-04 14:39 ` [PATCH 3/7] iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark Lorenzo Bianconi
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 14:39 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree

Perform a memory content reboot after device reset

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index c8b993bea757..e3ebd04d8078 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -56,6 +56,7 @@
 #define ST_LSM6DSX_REG_WHOAMI_ADDR		0x0f
 #define ST_LSM6DSX_REG_RESET_ADDR		0x12
 #define ST_LSM6DSX_REG_RESET_MASK		BIT(0)
+#define ST_LSM6DSX_REG_BOOT_MASK		BIT(7)
 #define ST_LSM6DSX_REG_BDU_ADDR			0x12
 #define ST_LSM6DSX_REG_BDU_MASK			BIT(6)
 #define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR	0x13
@@ -778,12 +779,21 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
 	u8 drdy_int_reg;
 	int err;
 
-	err = regmap_write(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
-			   ST_LSM6DSX_REG_RESET_MASK);
+	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
+				 ST_LSM6DSX_REG_RESET_MASK,
+				 FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1));
 	if (err < 0)
 		return err;
 
-	msleep(200);
+	msleep(50);
+
+	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
+				 ST_LSM6DSX_REG_BOOT_MASK,
+				 FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1));
+	if (err < 0)
+		return err;
+
+	msleep(50);
 
 	/* enable Block Data Update */
 	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR,
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 3/7] iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark
  2018-11-04 14:38 [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Lorenzo Bianconi
  2018-11-04 14:39 ` [PATCH 1/7] iio: imu: st_lsm6dsx: introduce locked read/write utility routines Lorenzo Bianconi
  2018-11-04 14:39 ` [PATCH 2/7] iio: imu: st_lsm6dsx: reboot memory content after reset Lorenzo Bianconi
@ 2018-11-04 14:39 ` Lorenzo Bianconi
  2018-11-04 14:39 ` [PATCH 4/7] iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids Lorenzo Bianconi
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 14:39 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree

Remove static qualifier from st_lsm6dsx_set_watermark routine in
order to be reused supporting st_lsm6dsx i2c controller

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h      | 1 +
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index ec204d3b4b1f..ac4cbbb0b3fb 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -185,6 +185,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
 int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
 int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
 int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
+int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val);
 int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
 				u16 watermark);
 int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw);
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index e3ebd04d8078..3433a5b6bf4d 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -600,7 +600,7 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
 	return err;
 }
 
-static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
+int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
 {
 	struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
 	struct st_lsm6dsx_hw *hw = sensor->hw;
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 4/7] iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
  2018-11-04 14:38 [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Lorenzo Bianconi
                   ` (2 preceding siblings ...)
  2018-11-04 14:39 ` [PATCH 3/7] iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark Lorenzo Bianconi
@ 2018-11-04 14:39 ` Lorenzo Bianconi
  2018-11-04 17:18   ` Jonathan Cameron
  2018-11-04 14:39 ` [PATCH 5/7] iio: imu: st_lsm6dsx: add i2c embedded controller support Lorenzo Bianconi
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 14:39 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree

Add ST_LSM6DSX_ID_EXT{0,1,2} sensor ids as reference for slave devices
connected to st_lsm6dsx i2c controller. Moreover introduce odr dependency
between accel and ext devices since accelerometer is used as trigger for
i2c master controller read/write operations

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       |   9 +-
 .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    |  27 +++--
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 101 ++++++++++++------
 3 files changed, 95 insertions(+), 42 deletions(-)

diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index ac4cbbb0b3fb..2beb4f563892 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -105,8 +105,11 @@ struct st_lsm6dsx_settings {
 };
 
 enum st_lsm6dsx_sensor_id {
-	ST_LSM6DSX_ID_ACC,
 	ST_LSM6DSX_ID_GYRO,
+	ST_LSM6DSX_ID_ACC,
+	ST_LSM6DSX_ID_EXT0,
+	ST_LSM6DSX_ID_EXT1,
+	ST_LSM6DSX_ID_EXT2,
 	ST_LSM6DSX_ID_MAX,
 };
 
@@ -182,8 +185,8 @@ extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
 
 int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
 		     struct regmap *regmap);
-int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
-int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
+int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
+				 bool enable);
 int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
 int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val);
 int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index 4b3ba0956b5a..96f7d56d3b6d 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -102,6 +102,9 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
 
 	*max_odr = 0, *min_odr = ~0;
 	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		if (!hw->iio_devs[i])
+			continue;
+
 		sensor = iio_priv(hw->iio_devs[i]);
 
 		if (!(hw->enable_mask & BIT(sensor->id)))
@@ -125,6 +128,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
 	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
 		const struct st_lsm6dsx_reg *dec_reg;
 
+		if (!hw->iio_devs[i])
+			continue;
+
 		sensor = iio_priv(hw->iio_devs[i]);
 		/* update fifo decimators and sample in pattern */
 		if (hw->enable_mask & BIT(sensor->id)) {
@@ -232,6 +238,9 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
 		return 0;
 
 	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		if (!hw->iio_devs[i])
+			continue;
+
 		cur_sensor = iio_priv(hw->iio_devs[i]);
 
 		if (!(hw->enable_mask & BIT(cur_sensor->id)))
@@ -278,6 +287,9 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
 		return err;
 
 	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		if (!hw->iio_devs[i])
+			continue;
+
 		sensor = iio_priv(hw->iio_devs[i]);
 		/*
 		 * store enable buffer timestamp as reference for
@@ -562,15 +574,9 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
 			goto out;
 	}
 
-	if (enable) {
-		err = st_lsm6dsx_sensor_enable(sensor);
-		if (err < 0)
-			goto out;
-	} else {
-		err = st_lsm6dsx_sensor_disable(sensor);
-		if (err < 0)
-			goto out;
-	}
+	err = st_lsm6dsx_sensor_set_enable(sensor, enable);
+	if (err < 0)
+		goto out;
 
 	err = st_lsm6dsx_set_fifo_odr(sensor, enable);
 	if (err < 0)
@@ -690,6 +696,9 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
 	}
 
 	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		if (!hw->iio_devs[i])
+			continue;
+
 		buffer = devm_iio_kfifo_allocate(hw->dev);
 		if (!buffer)
 			return -ENOMEM;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index 3433a5b6bf4d..f2549ddfee20 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -450,7 +450,7 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
 	int i;
 
 	for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
-		if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz == odr)
+		if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz >= odr)
 			break;
 
 	if (i == ST_LSM6DSX_ODR_LIST_SIZE)
@@ -461,50 +461,82 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
 	return 0;
 }
 
-static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
+static u16 st_lsm6dsx_check_odr_dependency(struct st_lsm6dsx_hw *hw, u16 odr,
+					   enum st_lsm6dsx_sensor_id id)
 {
+	struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]);
+	u16 ret;
+
+	if (odr > 0) {
+		if (hw->enable_mask & BIT(id))
+			ret = max_t(u16, ref->odr, odr);
+		else
+			ret = odr;
+	} else {
+		ret = (hw->enable_mask & BIT(id)) ? ref->odr : 0;
+	}
+	return ret;
+}
+
+static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 req_odr)
+{
+	struct st_lsm6dsx_sensor *ref_sensor = sensor;
 	struct st_lsm6dsx_hw *hw = sensor->hw;
 	const struct st_lsm6dsx_reg *reg;
 	unsigned int data;
+	u8 val = 0;
 	int err;
-	u8 val;
 
-	err = st_lsm6dsx_check_odr(sensor, odr, &val);
-	if (err < 0)
-		return err;
+	switch (sensor->id) {
+	case ST_LSM6DSX_ID_EXT0:
+	case ST_LSM6DSX_ID_EXT1:
+	case ST_LSM6DSX_ID_EXT2:
+	case ST_LSM6DSX_ID_ACC: {
+		u16 odr;
+		int i;
+
+		/* use acc as trigger for ext devices */
+		ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+		for (i = ST_LSM6DSX_ID_ACC; i < ST_LSM6DSX_ID_MAX; i++) {
+			if (!hw->iio_devs[i] || i == sensor->id)
+				continue;
+			odr = st_lsm6dsx_check_odr_dependency(hw, req_odr, i);
+			if (odr != req_odr)
+				/* device already configured */
+				return 0;
+		}
+		break;
+	}
+	default:
+		break;
+	}
+
+	if (req_odr > 0) {
+		err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val);
+		if (err < 0)
+			return err;
+	}
 
-	reg = &st_lsm6dsx_odr_table[sensor->id].reg;
+	reg = &st_lsm6dsx_odr_table[ref_sensor->id].reg;
 	data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
 	return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
 }
 
-int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
-{
-	int err;
-
-	err = st_lsm6dsx_set_odr(sensor, sensor->odr);
-	if (err < 0)
-		return err;
-
-	sensor->hw->enable_mask |= BIT(sensor->id);
-
-	return 0;
-}
-
-int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
+int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
+				 bool enable)
 {
 	struct st_lsm6dsx_hw *hw = sensor->hw;
-	const struct st_lsm6dsx_reg *reg;
-	unsigned int data;
+	u16 odr = enable ? sensor->odr : 0;
 	int err;
 
-	reg = &st_lsm6dsx_odr_table[sensor->id].reg;
-	data = ST_LSM6DSX_SHIFT_VAL(0, reg->mask);
-	err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
+	err = st_lsm6dsx_set_odr(sensor, odr);
 	if (err < 0)
 		return err;
 
-	sensor->hw->enable_mask &= ~BIT(sensor->id);
+	if (enable)
+		hw->enable_mask |= BIT(sensor->id);
+	else
+		hw->enable_mask &= ~BIT(sensor->id);
 
 	return 0;
 }
@@ -516,7 +548,7 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
 	int err, delay;
 	__le16 data;
 
-	err = st_lsm6dsx_sensor_enable(sensor);
+	err = st_lsm6dsx_sensor_set_enable(sensor, true);
 	if (err < 0)
 		return err;
 
@@ -527,7 +559,7 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
 	if (err < 0)
 		return err;
 
-	st_lsm6dsx_sensor_disable(sensor);
+	st_lsm6dsx_sensor_set_enable(sensor, false);
 
 	*val = (s16)le16_to_cpu(data);
 
@@ -892,7 +924,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
 	if (err < 0)
 		return err;
 
-	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+	for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) {
 		hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
 		if (!hw->iio_devs[i])
 			return -ENOMEM;
@@ -909,6 +941,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
 	}
 
 	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		if (!hw->iio_devs[i])
+			continue;
+
 		err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
 		if (err)
 			return err;
@@ -927,6 +962,9 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
 	int i, err = 0;
 
 	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		if (!hw->iio_devs[i])
+			continue;
+
 		sensor = iio_priv(hw->iio_devs[i]);
 		if (!(hw->enable_mask & BIT(sensor->id)))
 			continue;
@@ -952,6 +990,9 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev)
 	int i, err = 0;
 
 	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+		if (!hw->iio_devs[i])
+			continue;
+
 		sensor = iio_priv(hw->iio_devs[i]);
 		if (!(hw->enable_mask & BIT(sensor->id)))
 			continue;
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 5/7] iio: imu: st_lsm6dsx: add i2c embedded controller support
  2018-11-04 14:38 [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Lorenzo Bianconi
                   ` (3 preceding siblings ...)
  2018-11-04 14:39 ` [PATCH 4/7] iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids Lorenzo Bianconi
@ 2018-11-04 14:39 ` Lorenzo Bianconi
  2018-11-04 17:42   ` Jonathan Cameron
  2018-11-04 14:39 ` [PATCH 6/7] iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller Lorenzo Bianconi
                   ` (2 subsequent siblings)
  7 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 14:39 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree

i2c controller embedded in lsm6dx series can connect up to four
slave devices using accelerometer sensor as trigger for i2c
read/write operations.
Introduce sensor hub support for lsm6dso sensor. Add register map
for lis2mdl magnetometer sensor

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 drivers/iio/imu/st_lsm6dsx/Makefile           |   3 +-
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 112 +++
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 135 ++--
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 677 ++++++++++++++++++
 .../linux/platform_data/st_sensors_pdata.h    |   2 +
 5 files changed, 886 insertions(+), 43 deletions(-)
 create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c

diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile
index 35919febea2a..e5f733ce6e11 100644
--- a/drivers/iio/imu/st_lsm6dsx/Makefile
+++ b/drivers/iio/imu/st_lsm6dsx/Makefile
@@ -1,4 +1,5 @@
-st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
+st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o \
+		st_lsm6dsx_shub.o
 
 obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
 obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index 2beb4f563892..d20746eb3d2d 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -43,6 +43,24 @@ enum st_lsm6dsx_hw_id {
 					 * ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
 #define ST_LSM6DSX_SHIFT_VAL(val, mask)	(((val) << __ffs(mask)) & (mask))
 
+#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx)		\
+{									\
+	.type = chan_type,						\
+	.address = addr,						\
+	.modified = 1,							\
+	.channel2 = mod,						\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
+			      BIT(IIO_CHAN_INFO_SCALE),			\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = scan_idx,						\
+	.scan_type = {							\
+		.sign = 's',						\
+		.realbits = 16,						\
+		.storagebits = 16,					\
+		.endianness = IIO_LE,					\
+	},								\
+}
+
 struct st_lsm6dsx_reg {
 	u8 addr;
 	u8 mask;
@@ -50,6 +68,28 @@ struct st_lsm6dsx_reg {
 
 struct st_lsm6dsx_hw;
 
+struct st_lsm6dsx_odr {
+	u16 hz;
+	u8 val;
+};
+
+#define ST_LSM6DSX_ODR_LIST_SIZE	6
+struct st_lsm6dsx_odr_table_entry {
+	struct st_lsm6dsx_reg reg;
+	struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
+};
+
+struct st_lsm6dsx_fs {
+	u32 gain;
+	u8 val;
+};
+
+#define ST_LSM6DSX_FS_LIST_SIZE		4
+struct st_lsm6dsx_fs_table_entry {
+	struct st_lsm6dsx_reg reg;
+	struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
+};
+
 /**
  * struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings
  * @read_fifo: Read FIFO callback.
@@ -84,6 +124,66 @@ struct st_lsm6dsx_hw_ts_settings {
 	struct st_lsm6dsx_reg decimator;
 };
 
+/**
+ * struct st_lsm6dsx_shub_settings - ST IMU hw i2c controller settings
+ * @page_mux: register page mux info (addr + mask).
+ * @master_en: master config register info (addr + mask).
+ * @pullup_en: i2c controller pull-up register info (addr + mask).
+ * @aux_sens: aux sensor register info (addr + mask).
+ * @shub_out: sensor hub first output register info.
+ * @slv0_addr: slave0 address in secondary page.
+ * @dw_slv0_addr: slave0 write register address in secondary page.
+ */
+struct st_lsm6dsx_shub_settings {
+	struct st_lsm6dsx_reg page_mux;
+	struct st_lsm6dsx_reg master_en;
+	struct st_lsm6dsx_reg pullup_en;
+	struct st_lsm6dsx_reg aux_sens;
+	u8 shub_out;
+	u8 slv0_addr;
+	u8 dw_slv0_addr;
+};
+
+enum st_lsm6dsx_ext_sensor_id {
+	ST_LSM6DSX_ID_MAGN,
+};
+
+/**
+ * struct st_lsm6dsx_ext_dev_settings - i2c controller slave settings
+ * @i2c_addr: I2c slave address list.
+ * @wai: Wai address info.
+ * @id: external sensor id.
+ * @odr: Output data rate of the sensor [Hz].
+ * @gain: Configured sensor sensitivity.
+ * @temp_comp: Temperature compensation register info (addr + mask).
+ * @pwr_table: Power on register info (addr + mask).
+ * @off_canc: Offset cancellation register info (addr + mask).
+ * @bdu: Block data update register info (addr + mask).
+ * @out: Output register info.
+ */
+struct st_lsm6dsx_ext_dev_settings {
+	u8 i2c_addr[2];
+	struct {
+		u8 addr;
+		u8 val;
+	} wai;
+	enum st_lsm6dsx_ext_sensor_id id;
+	struct st_lsm6dsx_odr_table_entry odr_table;
+	struct st_lsm6dsx_fs_table_entry fs_table;
+	struct st_lsm6dsx_reg temp_comp;
+	struct {
+		struct st_lsm6dsx_reg reg;
+		u8 off_val;
+		u8 on_val;
+	} pwr_table;
+	struct st_lsm6dsx_reg off_canc;
+	struct st_lsm6dsx_reg bdu;
+	struct {
+		u8 addr;
+		u8 len;
+	} out;
+};
+
 /**
  * struct st_lsm6dsx_settings - ST IMU sensor settings
  * @wai: Sensor WhoAmI default value.
@@ -93,6 +193,7 @@ struct st_lsm6dsx_hw_ts_settings {
  * @batch: List of FIFO batching register info (addr + mask).
  * @fifo_ops: Sensor hw FIFO parameters.
  * @ts_settings: Hw timer related settings.
+ * @shub_settings: i2c controller related settings.
  */
 struct st_lsm6dsx_settings {
 	u8 wai;
@@ -102,6 +203,7 @@ struct st_lsm6dsx_settings {
 	struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
 	struct st_lsm6dsx_fifo_ops fifo_ops;
 	struct st_lsm6dsx_hw_ts_settings ts_settings;
+	struct st_lsm6dsx_shub_settings shub_settings;
 };
 
 enum st_lsm6dsx_sensor_id {
@@ -129,6 +231,7 @@ enum st_lsm6dsx_fifo_mode {
  * @sip: Number of samples in a given pattern.
  * @decimator: FIFO decimation factor.
  * @ts_ref: Sensor timestamp reference for hw one.
+ * @ext_info: Sensor settings if it is connected to i2c controller
  */
 struct st_lsm6dsx_sensor {
 	char name[32];
@@ -142,6 +245,11 @@ struct st_lsm6dsx_sensor {
 	u8 sip;
 	u8 decimator;
 	s64 ts_ref;
+
+	struct {
+		const struct st_lsm6dsx_ext_dev_settings *settings;
+		u8 addr;
+	} ext_info;
 };
 
 /**
@@ -181,6 +289,7 @@ struct st_lsm6dsx_hw {
 	const struct st_lsm6dsx_settings *settings;
 };
 
+static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
 extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
 
 int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
@@ -197,6 +306,9 @@ int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
 int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw);
 int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw);
 int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val);
+int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name);
+int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable);
+int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable);
 
 static inline int
 st_lsm6dsx_update_bits_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index f2549ddfee20..463859f287da 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -88,17 +88,6 @@
 #define ST_LSM6DSX_GYRO_FS_1000_GAIN		IIO_DEGREE_TO_RAD(35000)
 #define ST_LSM6DSX_GYRO_FS_2000_GAIN		IIO_DEGREE_TO_RAD(70000)
 
-struct st_lsm6dsx_odr {
-	u16 hz;
-	u8 val;
-};
-
-#define ST_LSM6DSX_ODR_LIST_SIZE	6
-struct st_lsm6dsx_odr_table_entry {
-	struct st_lsm6dsx_reg reg;
-	struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
-};
-
 static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
 	[ST_LSM6DSX_ID_ACC] = {
 		.reg = {
@@ -126,17 +115,6 @@ static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
 	}
 };
 
-struct st_lsm6dsx_fs {
-	u32 gain;
-	u8 val;
-};
-
-#define ST_LSM6DSX_FS_LIST_SIZE		4
-struct st_lsm6dsx_fs_table_entry {
-	struct st_lsm6dsx_reg reg;
-	struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
-};
-
 static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
 	[ST_LSM6DSX_ID_ACC] = {
 		.reg = {
@@ -342,27 +320,30 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
 				.mask = GENMASK(7, 6),
 			},
 		},
+		.shub_settings = {
+			.page_mux = {
+				.addr = 0x01,
+				.mask = BIT(6),
+			},
+			.master_en = {
+				.addr = 0x14,
+				.mask = BIT(2),
+			},
+			.pullup_en = {
+				.addr = 0x14,
+				.mask = BIT(3),
+			},
+			.aux_sens = {
+				.addr = 0x14,
+				.mask = GENMASK(1, 0),
+			},
+			.shub_out = 0x02,
+			.slv0_addr = 0x15,
+			.dw_slv0_addr = 0x21,
+		}
 	},
 };
 
-#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx)		\
-{									\
-	.type = chan_type,						\
-	.address = addr,						\
-	.modified = 1,							\
-	.channel2 = mod,						\
-	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
-			      BIT(IIO_CHAN_INFO_SCALE),			\
-	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
-	.scan_index = scan_idx,						\
-	.scan_type = {							\
-		.sign = 's',						\
-		.realbits = 16,						\
-		.storagebits = 16,					\
-		.endianness = IIO_LE,					\
-	},								\
-}
-
 static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
 	ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
 			   IIO_MOD_X, 0),
@@ -383,6 +364,21 @@ static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
 	IIO_CHAN_SOFT_TIMESTAMP(3),
 };
 
+int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable)
+{
+	const struct st_lsm6dsx_shub_settings *hub_settings;
+	unsigned int data;
+	int err;
+
+	hub_settings = &hw->settings->shub_settings;
+	data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->page_mux.mask);
+	err = regmap_update_bits(hw->regmap, hub_settings->page_mux.addr,
+				 hub_settings->page_mux.mask, data);
+	usleep_range(100, 150);
+
+	return err;
+}
+
 static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
 {
 	int err, i, j, data;
@@ -728,8 +724,6 @@ static const struct iio_info st_lsm6dsx_gyro_info = {
 	.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
 };
 
-static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
-
 static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
 {
 	struct device_node *np = hw->dev->of_node;
@@ -768,6 +762,51 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
 	return err;
 }
 
+static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw)
+{
+	const struct st_lsm6dsx_shub_settings *hub_settings;
+	struct device_node *np = hw->dev->of_node;
+	struct st_sensors_platform_data *pdata;
+	unsigned int data;
+	int err = 0;
+
+	hub_settings = &hw->settings->shub_settings;
+
+	pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
+	if ((np && of_property_read_bool(np, "st,pullups")) ||
+	    (pdata && pdata->pullups)) {
+		err = st_lsm6dsx_set_page(hw, true);
+		if (err < 0)
+			return err;
+
+		data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->pullup_en.mask);
+		err = regmap_update_bits(hw->regmap,
+					 hub_settings->pullup_en.addr,
+					 hub_settings->pullup_en.mask, data);
+
+		st_lsm6dsx_set_page(hw, false);
+
+		if (err < 0)
+			return err;
+	}
+
+	if (hub_settings->aux_sens.addr) {
+		/* configure aux sensors */
+		err = st_lsm6dsx_set_page(hw, true);
+		if (err < 0)
+			return err;
+
+		data = ST_LSM6DSX_SHIFT_VAL(3, hub_settings->aux_sens.mask);
+		err = regmap_update_bits(hw->regmap,
+					 hub_settings->aux_sens.addr,
+					 hub_settings->aux_sens.mask, data);
+
+		st_lsm6dsx_set_page(hw, false);
+	}
+
+	return err;
+}
+
 static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
 {
 	const struct st_lsm6dsx_hw_ts_settings *ts_settings;
@@ -846,6 +885,10 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
 	if (err < 0)
 		return err;
 
+	err = st_lsm6dsx_init_shub(hw);
+	if (err < 0)
+		return err;
+
 	return st_lsm6dsx_init_hw_timer(hw);
 }
 
@@ -899,6 +942,7 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
 int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
 		     struct regmap *regmap)
 {
+	const struct st_lsm6dsx_shub_settings *hub_settings;
 	struct st_lsm6dsx_hw *hw;
 	int i, err;
 
@@ -934,6 +978,13 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
 	if (err < 0)
 		return err;
 
+	hub_settings = &hw->settings->shub_settings;
+	if (hub_settings->master_en.addr) {
+		err = st_lsm6dsx_shub_probe(hw, name);
+		if (err < 0)
+			return err;
+	}
+
 	if (hw->irq > 0) {
 		err = st_lsm6dsx_fifo_setup(hw);
 		if (err < 0)
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
new file mode 100644
index 000000000000..b37f9dbdad17
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
@@ -0,0 +1,677 @@
+/*
+ * STMicroelectronics st_lsm6dsx i2c controller driver
+ *
+ * i2c controller embedded in lsm6dx series can connect up to four
+ * slave devices using accelerometer sensor as trigger for i2c
+ * read/write operations. Current implementation use SLV0 for slave
+ * configuration and SLV{1,2,3} to read data and push them into the
+ * hw FIFO
+ *
+ * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/bitfield.h>
+
+#include "st_lsm6dsx.h"
+
+#define ST_LSM6DSX_MAX_SLV_NUM			3
+#define ST_LSM6DSX_SLV_ADDR(n, base)		((base) + (n) * 3)
+#define ST_LSM6DSX_SLV_SUB_ADDR(n, base)	((base) + 1 + (n) * 3)
+#define ST_LSM6DSX_SLV_CONFIG(n, base)		((base) + 2 + (n) * 3)
+
+#define ST_LS6DSX_READ_OP_MASK			GENMASK(2, 0)
+
+static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = {
+	/* LIS2MDL */
+	{
+		.i2c_addr = { 0x1e },
+		.wai = {
+			.addr = 0x4f,
+			.val = 0x40,
+		},
+		.id = ST_LSM6DSX_ID_MAGN,
+		.odr_table = {
+			.reg = {
+				.addr = 0x60,
+				.mask = GENMASK(3, 2),
+			},
+			.odr_avl[0] = {  10, 0x0 },
+			.odr_avl[1] = {  20, 0x1 },
+			.odr_avl[2] = {  50, 0x2 },
+			.odr_avl[3] = { 100, 0x3 },
+		},
+		.fs_table = {
+			.fs_avl[0] = {
+				.gain = 1500,
+				.val = 0x0,
+			}, /* 1500 uG/LSB */
+		},
+		.temp_comp = {
+			.addr = 0x60,
+			.mask = BIT(7),
+		},
+		.pwr_table = {
+			.reg = {
+				.addr = 0x60,
+				.mask = GENMASK(1, 0),
+			},
+			.off_val = 0x2,
+			.on_val = 0x0,
+		},
+		.off_canc = {
+			.addr = 0x61,
+			.mask = BIT(1),
+		},
+		.bdu = {
+			.addr = 0x62,
+			.mask = BIT(4),
+		},
+		.out = {
+			.addr = 0x68,
+			.len = 6,
+		},
+	},
+};
+
+static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw)
+{
+	struct st_lsm6dsx_sensor *sensor;
+
+	sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+	msleep((2000U / sensor->odr) + 1);
+}
+
+/**
+ * st_lsm6dsx_shub_read_reg - read i2c controller register
+ *
+ * Read st_lsm6dsx i2c controller register
+ */
+static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr,
+				    u8 *data, int len)
+{
+	const struct st_lsm6dsx_shub_settings *hub_settings;
+	int err;
+
+	mutex_lock(&hw->page_lock);
+
+	hub_settings = &hw->settings->shub_settings;
+	err = st_lsm6dsx_set_page(hw, true);
+	if (err < 0)
+		goto out;
+
+	err = regmap_bulk_read(hw->regmap, addr, data, len);
+
+	st_lsm6dsx_set_page(hw, false);
+out:
+	mutex_unlock(&hw->page_lock);
+
+	return err;
+}
+
+/**
+ * st_lsm6dsx_shub_write_reg - write i2c controller register
+ *
+ * Write st_lsm6dsx i2c controller register
+ */
+static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr,
+				     u8 *data, int len)
+{
+	int err;
+
+	mutex_lock(&hw->page_lock);
+	err = st_lsm6dsx_set_page(hw, true);
+	if (err < 0)
+		goto out;
+
+	err = regmap_bulk_write(hw->regmap, addr, data, len);
+
+	st_lsm6dsx_set_page(hw, false);
+out:
+	mutex_unlock(&hw->page_lock);
+
+	return err;
+}
+
+static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
+					 bool enable)
+{
+	const struct st_lsm6dsx_shub_settings *hub_settings;
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	unsigned int data;
+	int err;
+
+	/* enable acc sensor as trigger */
+	err = st_lsm6dsx_sensor_set_enable(sensor, enable);
+	if (err < 0)
+		return err;
+
+	mutex_lock(&hw->page_lock);
+
+	hub_settings = &hw->settings->shub_settings;
+	err = st_lsm6dsx_set_page(hw, true);
+	if (err < 0)
+		goto out;
+
+	data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask);
+	err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr,
+				 hub_settings->master_en.mask, data);
+
+	st_lsm6dsx_set_page(hw, false);
+out:
+	mutex_unlock(&hw->page_lock);
+
+	return err;
+}
+
+/**
+ * st_lsm6dsx_shub_read - read data from slave device register
+ *
+ * Read data from slave device register. SLV0 is used for
+ * one-shot read operation
+ */
+static int
+st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr,
+		     u8 *data, int len)
+{
+	const struct st_lsm6dsx_shub_settings *hub_settings;
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	u8 config[3], slv_addr;
+	int err;
+
+	hub_settings = &hw->settings->shub_settings;
+	slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
+
+	config[0] = (sensor->ext_info.addr << 1) | 1;
+	config[1] = addr;
+	config[2] = len & ST_LS6DSX_READ_OP_MASK;
+
+	err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+					sizeof(config));
+	if (err < 0)
+		return err;
+
+	err = st_lsm6dsx_shub_master_enable(sensor, true);
+	if (err < 0)
+		return err;
+
+	st_lsm6dsx_shub_wait_complete(hw);
+
+	err = st_lsm6dsx_shub_read_reg(hw, hub_settings->shub_out, data,
+				       len & ST_LS6DSX_READ_OP_MASK);
+
+	st_lsm6dsx_shub_master_enable(sensor, false);
+
+	memset(config, 0, sizeof(config));
+	return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+					 sizeof(config));
+}
+
+/**
+ * st_lsm6dsx_shub_write - write data to slave device register
+ *
+ * Write data from slave device register. SLV0 is used for
+ * one-shot write operation
+ */
+static int
+st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
+		      u8 *data, int len)
+{
+	const struct st_lsm6dsx_shub_settings *hub_settings;
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	u8 config[2], slv_addr;
+	int err, i;
+
+	hub_settings = &hw->settings->shub_settings;
+	slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
+	config[0] = sensor->ext_info.addr << 1;
+	for (i = 0 ; i < len; i++) {
+		config[1] = addr + i;
+
+		err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+						sizeof(config));
+		if (err < 0)
+			return err;
+
+		err = st_lsm6dsx_shub_write_reg(hw, hub_settings->dw_slv0_addr,
+						&data[i], 1);
+		if (err < 0)
+			return err;
+
+		err = st_lsm6dsx_shub_master_enable(sensor, true);
+		if (err < 0)
+			return err;
+
+		st_lsm6dsx_shub_wait_complete(hw);
+
+		st_lsm6dsx_shub_master_enable(sensor, false);
+	}
+
+	memset(config, 0, sizeof(config));
+	return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config));
+}
+
+static int
+st_lsm6dsx_shub_write_with_mask(struct st_lsm6dsx_sensor *sensor,
+				u8 addr, u8 mask, u8 val)
+{
+	int err;
+	u8 data;
+
+	err = st_lsm6dsx_shub_read(sensor, addr, &data, sizeof(data));
+	if (err < 0)
+		return err;
+
+	data = ((data & ~mask) | (val << __ffs(mask) & mask));
+
+	return st_lsm6dsx_shub_write(sensor, addr, &data, sizeof(data));
+}
+
+static int
+st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor,
+			    u16 odr, u16 *val)
+{
+	const struct st_lsm6dsx_ext_dev_settings *settings;
+	int i;
+
+	settings = sensor->ext_info.settings;
+	for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
+		if (settings->odr_table.odr_avl[i].hz == odr)
+			break;
+
+	if (i == ST_LSM6DSX_ODR_LIST_SIZE)
+		return -EINVAL;
+
+	*val = settings->odr_table.odr_avl[i].val;
+	return 0;
+}
+
+static int
+st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
+{
+	const struct st_lsm6dsx_ext_dev_settings *settings;
+	u16 val;
+	int err;
+
+	err = st_lsm6dsx_shub_get_odr_val(sensor, odr, &val);
+	if (err < 0)
+		return err;
+
+	settings = sensor->ext_info.settings;
+	return st_lsm6dsx_shub_write_with_mask(sensor,
+					       settings->odr_table.reg.addr,
+					       settings->odr_table.reg.mask,
+					       val);
+}
+
+int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
+{
+	const struct st_lsm6dsx_ext_dev_settings *settings;
+	int err;
+
+	settings = sensor->ext_info.settings;
+	if (enable) {
+		err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr);
+		if (err < 0)
+			return err;
+	} else {
+		err = st_lsm6dsx_shub_write_with_mask(sensor,
+					settings->odr_table.reg.addr,
+					settings->odr_table.reg.mask, 0);
+		if (err < 0)
+			return err;
+	}
+
+	if (settings->pwr_table.reg.addr) {
+		u8 val;
+
+		val = enable ? settings->pwr_table.on_val
+			     : settings->pwr_table.off_val;
+		err = st_lsm6dsx_shub_write_with_mask(sensor,
+					settings->pwr_table.reg.addr,
+					settings->pwr_table.reg.mask, val);
+		if (err < 0)
+			return err;
+	}
+
+	return st_lsm6dsx_shub_master_enable(sensor, enable);
+}
+
+static int
+st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor,
+			     struct iio_chan_spec const *ch,
+			     int *val)
+{
+	int err, delay, len = ch->scan_type.realbits >> 3;
+	__le32 data;
+
+	err = st_lsm6dsx_shub_set_enable(sensor, true);
+	if (err < 0)
+		return err;
+
+	delay = 1000000 / sensor->odr;
+	usleep_range(delay, 2 * delay);
+
+	err = st_lsm6dsx_shub_read(sensor, ch->address, (u8 *)&data, len);
+	if (err < 0)
+		return err;
+
+	st_lsm6dsx_shub_set_enable(sensor, false);
+
+	switch (len) {
+	case 2:
+		*val = (s16)le16_to_cpu(data);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return IIO_VAL_INT;
+}
+
+static int
+st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev,
+			 struct iio_chan_spec const *ch,
+			 int *val, int *val2, long mask)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(iio_dev);
+		if (ret)
+			break;
+
+		ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val);
+		iio_device_release_direct_mode(iio_dev);
+		break;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		*val = sensor->odr;
+		ret = IIO_VAL_INT;
+		break;
+	case IIO_CHAN_INFO_SCALE:
+		*val = 0;
+		*val2 = sensor->gain;
+		ret = IIO_VAL_INT_PLUS_MICRO;
+		break;
+	default:
+		ret = -EINVAL;
+		break;
+	}
+
+	return ret;
+}
+
+static int
+st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev,
+			  struct iio_chan_spec const *chan,
+			  int val, int val2, long mask)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+	int err;
+
+	err = iio_device_claim_direct_mode(iio_dev);
+	if (err)
+		return err;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ: {
+		u16 data;
+
+		err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data);
+		if (!err)
+			sensor->odr = val;
+		break;
+	}
+	default:
+		err = -EINVAL;
+		break;
+	}
+
+	iio_device_release_direct_mode(iio_dev);
+
+	return err;
+}
+
+static ssize_t
+st_lsm6dsx_shub_sampling_freq_avail(struct device *dev,
+				    struct device_attribute *attr,
+				    char *buf)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+	const struct st_lsm6dsx_ext_dev_settings *settings;
+	int i, len = 0;
+
+	settings = sensor->ext_info.settings;
+	for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) {
+		u16 val = settings->odr_table.odr_avl[i].hz;
+
+		if (val > 0)
+			len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+					 val);
+	}
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev,
+					   struct device_attribute *attr,
+					   char *buf)
+{
+	struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+	const struct st_lsm6dsx_ext_dev_settings *settings;
+	int i, len = 0;
+
+	settings = sensor->ext_info.settings;
+	for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) {
+		u16 val = settings->fs_table.fs_avl[i].gain;
+
+		if (val > 0)
+			len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
+					 val);
+	}
+	buf[len - 1] = '\n';
+
+	return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail);
+static IIO_DEVICE_ATTR(in_ext_scale_available, 0444,
+		       st_lsm6dsx_shub_scale_avail, NULL, 0);
+static struct attribute *st_lsm6dsx_ext_attributes[] = {
+	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+	&iio_dev_attr_in_ext_scale_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group st_lsm6dsx_ext_attribute_group = {
+	.attrs = st_lsm6dsx_ext_attributes,
+};
+
+static const struct iio_info st_lsm6dsx_ext_info = {
+	.attrs = &st_lsm6dsx_ext_attribute_group,
+	.read_raw = st_lsm6dsx_shub_read_raw,
+	.write_raw = st_lsm6dsx_shub_write_raw,
+	.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
+};
+
+static struct iio_dev *
+st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw,
+			     enum st_lsm6dsx_sensor_id id,
+			     const struct st_lsm6dsx_ext_dev_settings *info,
+			     u8 i2c_addr, const char *name)
+{
+	struct iio_chan_spec *ext_channels;
+	struct st_lsm6dsx_sensor *sensor;
+	struct iio_dev *iio_dev;
+
+	iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
+	if (!iio_dev)
+		return NULL;
+
+	iio_dev->modes = INDIO_DIRECT_MODE;
+	iio_dev->dev.parent = hw->dev;
+	iio_dev->info = &st_lsm6dsx_ext_info;
+
+	sensor = iio_priv(iio_dev);
+	sensor->id = id;
+	sensor->hw = hw;
+	sensor->odr = info->odr_table.odr_avl[0].hz;
+	sensor->gain = info->fs_table.fs_avl[0].gain;
+	sensor->ext_info.settings = info;
+	sensor->ext_info.addr = i2c_addr;
+	sensor->watermark = 1;
+
+	switch (info->id) {
+	case ST_LSM6DSX_ID_MAGN: {
+		const struct iio_chan_spec magn_channels[] = {
+			ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr,
+					   IIO_MOD_X, 0),
+			ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 2,
+					   IIO_MOD_Y, 1),
+			ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 4,
+					   IIO_MOD_Z, 2),
+			IIO_CHAN_SOFT_TIMESTAMP(3),
+		};
+
+		ext_channels = devm_kzalloc(hw->dev, sizeof(magn_channels),
+					    GFP_KERNEL);
+		if (!ext_channels)
+			return NULL;
+
+		memcpy(ext_channels, magn_channels, sizeof(magn_channels));
+		iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
+		iio_dev->channels = ext_channels;
+		iio_dev->num_channels = ARRAY_SIZE(magn_channels);
+
+		scnprintf(sensor->name, sizeof(sensor->name), "%s_magn",
+			  name);
+		break;
+	}
+	default:
+		return NULL;
+	}
+	iio_dev->name = sensor->name;
+
+	return iio_dev;
+}
+
+static int st_lsm6dsx_shub_init_device(struct st_lsm6dsx_sensor *sensor)
+{
+	const struct st_lsm6dsx_ext_dev_settings *settings;
+	int err = 0;
+
+	settings = sensor->ext_info.settings;
+	if (settings->bdu.addr) {
+		err = st_lsm6dsx_shub_write_with_mask(sensor,
+						      settings->bdu.addr,
+						      settings->bdu.mask, 1);
+		if (err < 0)
+			return err;
+	}
+
+	if (settings->temp_comp.addr) {
+		err = st_lsm6dsx_shub_write_with_mask(sensor,
+					settings->temp_comp.addr,
+					settings->temp_comp.mask, 1);
+		if (err < 0)
+			return err;
+	}
+
+	if (settings->off_canc.addr)
+		err = st_lsm6dsx_shub_write_with_mask(sensor,
+					settings->off_canc.addr,
+					settings->off_canc.mask, 1);
+
+	return err;
+}
+
+int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name)
+{
+	const struct st_lsm6dsx_shub_settings *hub_settings;
+	const struct st_lsm6dsx_ext_dev_settings *settings;
+	enum st_lsm6dsx_sensor_id id = ST_LSM6DSX_ID_EXT0;
+	struct st_lsm6dsx_sensor *acc_sensor, *sensor;
+	int err, i, j, num_ext_dev = 0;
+	u8 config[3], data, slv_addr;
+
+	acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+	hub_settings = &hw->settings->shub_settings;
+	slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
+
+	while (i < ARRAY_SIZE(st_lsm6dsx_ext_dev_table) &&
+	       num_ext_dev < ST_LSM6DSX_MAX_SLV_NUM) {
+		settings = &st_lsm6dsx_ext_dev_table[i];
+
+		for (j = 0; j < ARRAY_SIZE(settings->i2c_addr); j++) {
+			if (!settings->i2c_addr[j])
+				continue;
+
+			/* read wai slave register */
+			config[0] = (settings->i2c_addr[j] << 1) | 0x1;
+			config[1] = settings->wai.addr;
+			config[2] = 0x1;
+
+			err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+							sizeof(config));
+			if (err < 0)
+				return err;
+
+			err = st_lsm6dsx_shub_master_enable(acc_sensor, true);
+			if (err < 0)
+				return err;
+
+			st_lsm6dsx_shub_wait_complete(hw);
+
+			err = st_lsm6dsx_shub_read_reg(hw,
+						       hub_settings->shub_out,
+						       &data, sizeof(data));
+
+			st_lsm6dsx_shub_master_enable(acc_sensor, false);
+
+			if (err < 0)
+				return err;
+
+			if (data != settings->wai.val)
+				continue;
+
+			hw->iio_devs[id] = st_lsm6dsx_shub_alloc_iiodev(hw,
+							id, settings,
+							settings->i2c_addr[j],
+							name);
+			if (!hw->iio_devs[id])
+				return -ENOMEM;
+
+			sensor = iio_priv(hw->iio_devs[id]);
+			err = st_lsm6dsx_shub_init_device(sensor);
+			if (err < 0)
+				return err;
+
+			num_ext_dev++;
+			id++;
+			break;
+		}
+		i++;
+	}
+
+	memset(config, 0, sizeof(config));
+	return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+					 sizeof(config));
+}
diff --git a/include/linux/platform_data/st_sensors_pdata.h b/include/linux/platform_data/st_sensors_pdata.h
index f8274b0c6888..728193111c2f 100644
--- a/include/linux/platform_data/st_sensors_pdata.h
+++ b/include/linux/platform_data/st_sensors_pdata.h
@@ -18,11 +18,13 @@
  *	Accelerometer DRDY on LSM330 available only on pin 1 (see datasheet).
  * @open_drain: set the interrupt line to be open drain if possible.
  * @spi_3wire: enable spi-3wire mode.
+ * @pullups: enable/disable i2c controller pullup resistors.
  */
 struct st_sensors_platform_data {
 	u8 drdy_int_pin;
 	bool open_drain;
 	bool spi_3wire;
+	bool pullups;
 };
 
 #endif /* ST_SENSORS_PDATA_H */
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 6/7] iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
  2018-11-04 14:38 [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Lorenzo Bianconi
                   ` (4 preceding siblings ...)
  2018-11-04 14:39 ` [PATCH 5/7] iio: imu: st_lsm6dsx: add i2c embedded controller support Lorenzo Bianconi
@ 2018-11-04 14:39 ` Lorenzo Bianconi
  2018-11-04 17:54   ` Jonathan Cameron
  2018-11-04 14:39 ` [PATCH 7/7] dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors Lorenzo Bianconi
  2018-11-04 18:12 ` [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Jonathan Cameron
  7 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 14:39 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree

Introduce hw FIFO support to lsm6dsx sensorhub. Current implementation
use SLV0 for slave configuration and SLV{1,2,3} to read data and
push them into the hw FIFO

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       |  4 +
 .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 86 ++++++++++++++-----
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  |  5 ++
 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 76 ++++++++++++++++
 4 files changed, 150 insertions(+), 21 deletions(-)

diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index d20746eb3d2d..d1d8d07a0714 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -130,18 +130,22 @@ struct st_lsm6dsx_hw_ts_settings {
  * @master_en: master config register info (addr + mask).
  * @pullup_en: i2c controller pull-up register info (addr + mask).
  * @aux_sens: aux sensor register info (addr + mask).
+ * @wr_once: write_once register info (addr + mask).
  * @shub_out: sensor hub first output register info.
  * @slv0_addr: slave0 address in secondary page.
  * @dw_slv0_addr: slave0 write register address in secondary page.
+ * @batch_en: Enable/disable FIFO batching.
  */
 struct st_lsm6dsx_shub_settings {
 	struct st_lsm6dsx_reg page_mux;
 	struct st_lsm6dsx_reg master_en;
 	struct st_lsm6dsx_reg pullup_en;
 	struct st_lsm6dsx_reg aux_sens;
+	struct st_lsm6dsx_reg wr_once;
 	u8 shub_out;
 	u8 slv0_addr;
 	u8 dw_slv0_addr;
+	u8 batch_en;
 };
 
 enum st_lsm6dsx_ext_sensor_id {
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index 96f7d56d3b6d..d4e4fe54219f 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -68,6 +68,9 @@ enum st_lsm6dsx_fifo_tag {
 	ST_LSM6DSX_GYRO_TAG = 0x01,
 	ST_LSM6DSX_ACC_TAG = 0x02,
 	ST_LSM6DSX_TS_TAG = 0x04,
+	ST_LSM6DSX_EXT0_TAG = 0x0f,
+	ST_LSM6DSX_EXT1_TAG = 0x10,
+	ST_LSM6DSX_EXT2_TAG = 0x11,
 };
 
 static const
@@ -453,6 +456,52 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
 	return read_len;
 }
 
+static int
+st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag,
+			    u8 *data, s64 ts)
+{
+	struct st_lsm6dsx_sensor *sensor;
+	struct iio_dev *iio_dev;
+
+	switch (tag) {
+	case ST_LSM6DSX_GYRO_TAG:
+		iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO];
+		sensor = iio_priv(iio_dev);
+		break;
+	case ST_LSM6DSX_ACC_TAG:
+		iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC];
+		sensor = iio_priv(iio_dev);
+		break;
+	case ST_LSM6DSX_EXT0_TAG:
+		if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0))
+			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0];
+		else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
+			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
+		else
+			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
+		sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+		break;
+	case ST_LSM6DSX_EXT1_TAG:
+		if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
+			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
+		else
+			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
+		sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+		break;
+	case ST_LSM6DSX_EXT2_TAG:
+		iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
+		sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	iio_push_to_buffers_with_timestamp(iio_dev, data,
+					   ts + sensor->ts_ref);
+
+	return 0;
+}
+
 /**
  * st_lsm6dsx_read_tagged_fifo() - LSM6DSO read FIFO routine
  * @hw: Pointer to instance of struct st_lsm6dsx_hw.
@@ -508,8 +557,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
 			       ST_LSM6DSX_SAMPLE_SIZE);
 
 			tag = hw->buff[i] >> 3;
-			switch (tag) {
-			case ST_LSM6DSX_TS_TAG:
+			if (tag == ST_LSM6DSX_TS_TAG) {
 				/*
 				 * hw timestamp is 4B long and it is stored
 				 * in FIFO according to this schema:
@@ -526,19 +574,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
 				if (!reset_ts && ts >= 0xffff0000)
 					reset_ts = true;
 				ts *= ST_LSM6DSX_TS_SENSITIVITY;
-				break;
-			case ST_LSM6DSX_GYRO_TAG:
-				iio_push_to_buffers_with_timestamp(
-					hw->iio_devs[ST_LSM6DSX_ID_GYRO],
-					iio_buff, gyro_sensor->ts_ref + ts);
-				break;
-			case ST_LSM6DSX_ACC_TAG:
-				iio_push_to_buffers_with_timestamp(
-					hw->iio_devs[ST_LSM6DSX_ID_ACC],
-					iio_buff, acc_sensor->ts_ref + ts);
-				break;
-			default:
-				break;
+			} else {
+				st_lsm6dsx_push_tagged_data(hw, tag, iio_buff,
+							    ts);
 			}
 		}
 	}
@@ -574,13 +612,19 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
 			goto out;
 	}
 
-	err = st_lsm6dsx_sensor_set_enable(sensor, enable);
-	if (err < 0)
-		goto out;
+	if (sensor->id == ST_LSM6DSX_ID_EXT0 ||
+	    sensor->id == ST_LSM6DSX_ID_EXT1 ||
+	    sensor->id == ST_LSM6DSX_ID_EXT2) {
+		st_lsm6dsx_shub_set_enable(sensor, enable);
+	} else {
+		err = st_lsm6dsx_sensor_set_enable(sensor, enable);
+		if (err < 0)
+			goto out;
 
-	err = st_lsm6dsx_set_fifo_odr(sensor, enable);
-	if (err < 0)
-		goto out;
+		err = st_lsm6dsx_set_fifo_odr(sensor, enable);
+		if (err < 0)
+			goto out;
+	}
 
 	err = st_lsm6dsx_update_decimators(hw);
 	if (err < 0)
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index 463859f287da..5ec94f211905 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -337,9 +337,14 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
 				.addr = 0x14,
 				.mask = GENMASK(1, 0),
 			},
+			.wr_once = {
+				.addr = 0x14,
+				.mask = BIT(6),
+			},
 			.shub_out = 0x02,
 			.slv0_addr = 0x15,
 			.dw_slv0_addr = 0x21,
+			.batch_en = BIT(3),
 		}
 	},
 };
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
index b37f9dbdad17..d723b4adeeda 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
@@ -148,6 +148,26 @@ static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr,
 	return err;
 }
 
+static int
+st_lsm6dsx_shub_write_reg_with_mask(struct st_lsm6dsx_hw *hw, u8 addr,
+				    u8 mask, u8 val)
+{
+	int err;
+
+	mutex_lock(&hw->page_lock);
+	err = st_lsm6dsx_set_page(hw, true);
+	if (err < 0)
+		goto out;
+
+	err = regmap_update_bits(hw->regmap, addr, mask, val);
+
+	st_lsm6dsx_set_page(hw, false);
+out:
+	mutex_unlock(&hw->page_lock);
+
+	return err;
+}
+
 static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
 					 bool enable)
 {
@@ -238,8 +258,21 @@ st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
 	int err, i;
 
 	hub_settings = &hw->settings->shub_settings;
+	if (hub_settings->wr_once.addr) {
+		unsigned int data;
+
+		data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask);
+		err = st_lsm6dsx_shub_write_reg_with_mask(hw,
+			hub_settings->wr_once.addr,
+			hub_settings->wr_once.mask,
+			data);
+		if (err < 0)
+			return err;
+	}
+
 	slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
 	config[0] = sensor->ext_info.addr << 1;
+
 	for (i = 0 ; i < len; i++) {
 		config[1] = addr + i;
 
@@ -319,11 +352,54 @@ st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
 					       val);
 }
 
+/* use SLV{1,2,3} for FIFO read operations */
+static int
+st_lsm6dsx_shub_config_channels(struct st_lsm6dsx_sensor *sensor,
+				bool enable)
+{
+	const struct st_lsm6dsx_shub_settings *hub_settings;
+	const struct st_lsm6dsx_ext_dev_settings *settings;
+	u8 config[9] = {}, enable_mask, slv_addr;
+	struct st_lsm6dsx_hw *hw = sensor->hw;
+	struct st_lsm6dsx_sensor *cur_sensor;
+	int i, j = 0;
+
+	hub_settings = &hw->settings->shub_settings;
+	if (enable)
+		enable_mask = hw->enable_mask | BIT(sensor->id);
+	else
+		enable_mask = hw->enable_mask & ~BIT(sensor->id);
+
+	for (i = ST_LSM6DSX_ID_EXT0; i <= ST_LSM6DSX_ID_EXT2; i++) {
+		if (!hw->iio_devs[i])
+			continue;
+
+		cur_sensor = iio_priv(hw->iio_devs[i]);
+		if (!(enable_mask & BIT(cur_sensor->id)))
+			continue;
+
+		settings = cur_sensor->ext_info.settings;
+		config[j] = (sensor->ext_info.addr << 1) | 1;
+		config[j + 1] = settings->out.addr;
+		config[j + 2] = (settings->out.len & ST_LS6DSX_READ_OP_MASK) |
+				hub_settings->batch_en;
+		j += 3;
+	}
+
+	slv_addr = ST_LSM6DSX_SLV_ADDR(1, hub_settings->slv0_addr);
+	return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+					 sizeof(config));
+}
+
 int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
 {
 	const struct st_lsm6dsx_ext_dev_settings *settings;
 	int err;
 
+	err = st_lsm6dsx_shub_config_channels(sensor, enable);
+	if (err < 0)
+		return err;
+
 	settings = sensor->ext_info.settings;
 	if (enable) {
 		err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr);
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* [PATCH 7/7] dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors
  2018-11-04 14:38 [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Lorenzo Bianconi
                   ` (5 preceding siblings ...)
  2018-11-04 14:39 ` [PATCH 6/7] iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller Lorenzo Bianconi
@ 2018-11-04 14:39 ` Lorenzo Bianconi
  2018-11-04 18:12 ` [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Jonathan Cameron
  7 siblings, 0 replies; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 14:39 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, devicetree

Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
---
 Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt
index 879322ad50fd..69d53d98d0f0 100644
--- a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt
+++ b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt
@@ -13,6 +13,7 @@ Required properties:
 Optional properties:
 - st,drdy-int-pin: the pin on the package that will be used to signal
   "data ready" (valid values: 1 or 2).
+- st,pullups : enable/disable internal i2c controller pullup resistors.
 - drive-open-drain: the interrupt/data ready line will be configured
   as open drain, which is useful if several sensors share the same
   interrupt line. This is a boolean property.
-- 
2.19.1

^ permalink raw reply related	[flat|nested] 27+ messages in thread

* Re: [PATCH 1/7] iio: imu: st_lsm6dsx: introduce locked read/write utility routines
  2018-11-04 14:39 ` [PATCH 1/7] iio: imu: st_lsm6dsx: introduce locked read/write utility routines Lorenzo Bianconi
@ 2018-11-04 17:11   ` Jonathan Cameron
  0 siblings, 0 replies; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 17:11 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun,  4 Nov 2018 15:39:00 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> Add st_lsm6dsx_update_bits_locked, st_lsm6dsx_read_locked and
> st_lsm6dsx_write_locked utility routines in order to guarantee
> the bus access is atomic respect to reg page configuration.
> This is a preliminary patch to add i2c sensor hub support since
> i2c master registers are accessed through a reg page multiplexer
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>

Just one case inline where this doesn't seem as straight forward a
change as this suggests..

Jonathan

> ---
...
> @@ -536,16 +541,11 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
>  
>  int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
>  {
> -	int err;
> -
>  	mutex_lock(&hw->fifo_lock);
> -
>  	hw->settings->fifo_ops.read_fifo(hw);
> -	err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);

This one needs some explaining...  You've dropped the set_fifo_mode
out of the fifo_lock.  Is this because it was never needed or has
something changed?  If it was never needed then I'd like to see it
moved out in a precursor patch where you explain why that is safe.

Basically I want this patch to be purely mechanical application of
a lock around places where we need the page to be held constant.

> -
>  	mutex_unlock(&hw->fifo_lock);
>  
> -	return err;
> +	return st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
>  }
>  
>  static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/7] iio: imu: st_lsm6dsx: reboot memory content after reset
  2018-11-04 14:39 ` [PATCH 2/7] iio: imu: st_lsm6dsx: reboot memory content after reset Lorenzo Bianconi
@ 2018-11-04 17:12   ` Jonathan Cameron
  2018-11-04 17:30     ` Lorenzo Bianconi
  0 siblings, 1 reply; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 17:12 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun,  4 Nov 2018 15:39:01 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> Perform a memory content reboot after device reset
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> ---
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 16 +++++++++++++---
>  1 file changed, 13 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> index c8b993bea757..e3ebd04d8078 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> @@ -56,6 +56,7 @@
>  #define ST_LSM6DSX_REG_WHOAMI_ADDR		0x0f
>  #define ST_LSM6DSX_REG_RESET_ADDR		0x12
>  #define ST_LSM6DSX_REG_RESET_MASK		BIT(0)
> +#define ST_LSM6DSX_REG_BOOT_MASK		BIT(7)
>  #define ST_LSM6DSX_REG_BDU_ADDR			0x12
>  #define ST_LSM6DSX_REG_BDU_MASK			BIT(6)
>  #define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR	0x13
> @@ -778,12 +779,21 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
>  	u8 drdy_int_reg;
>  	int err;
>  
> -	err = regmap_write(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
> -			   ST_LSM6DSX_REG_RESET_MASK);
> +	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
> +				 ST_LSM6DSX_REG_RESET_MASK,
> +				 FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1));
>  	if (err < 0)
>  		return err;
>  
> -	msleep(200);
> +	msleep(50);
> +
> +	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
> +				 ST_LSM6DSX_REG_BOOT_MASK,
> +				 FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1));
> +	if (err < 0)
> +		return err;
> +
> +	msleep(50);
This change in the timing needs an explanatory comment in the patch
description.


>  
>  	/* enable Block Data Update */
>  	err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR,

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 4/7] iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
  2018-11-04 14:39 ` [PATCH 4/7] iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids Lorenzo Bianconi
@ 2018-11-04 17:18   ` Jonathan Cameron
  2018-11-04 17:47     ` Lorenzo Bianconi
  0 siblings, 1 reply; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 17:18 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun,  4 Nov 2018 15:39:03 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> Add ST_LSM6DSX_ID_EXT{0,1,2} sensor ids as reference for slave devices
> connected to st_lsm6dsx i2c controller. Moreover introduce odr dependency
> between accel and ext devices since accelerometer is used as trigger for
> i2c master controller read/write operations
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>

Hi Lorenzo,

There looks to be an unrelated cleanup in here around set_enable so 
please pull that out as a separate patch.

Also, it seems the odr dependency is not as simple as they should be
the same?  Perhaps you could add more here on what that dependency is?

Thanks,

Jonathan

> ---
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       |   9 +-
>  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    |  27 +++--
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 101 ++++++++++++------
>  3 files changed, 95 insertions(+), 42 deletions(-)
> 
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> index ac4cbbb0b3fb..2beb4f563892 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> @@ -105,8 +105,11 @@ struct st_lsm6dsx_settings {
>  };
>  
>  enum st_lsm6dsx_sensor_id {
> -	ST_LSM6DSX_ID_ACC,
>  	ST_LSM6DSX_ID_GYRO,
> +	ST_LSM6DSX_ID_ACC,
> +	ST_LSM6DSX_ID_EXT0,
> +	ST_LSM6DSX_ID_EXT1,
> +	ST_LSM6DSX_ID_EXT2,
>  	ST_LSM6DSX_ID_MAX,
>  };
>  
> @@ -182,8 +185,8 @@ extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
>  
>  int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
>  		     struct regmap *regmap);
> -int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
> -int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
> +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
> +				 bool enable);
Unrelated change?

>  int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
>  int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val);
>  int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> index 4b3ba0956b5a..96f7d56d3b6d 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> @@ -102,6 +102,9 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
>  
>  	*max_odr = 0, *min_odr = ~0;
>  	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> +		if (!hw->iio_devs[i])
> +			continue;
> +
>  		sensor = iio_priv(hw->iio_devs[i]);
>  
>  		if (!(hw->enable_mask & BIT(sensor->id)))
> @@ -125,6 +128,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
>  	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
>  		const struct st_lsm6dsx_reg *dec_reg;
>  
> +		if (!hw->iio_devs[i])
> +			continue;
> +
>  		sensor = iio_priv(hw->iio_devs[i]);
>  		/* update fifo decimators and sample in pattern */
>  		if (hw->enable_mask & BIT(sensor->id)) {
> @@ -232,6 +238,9 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
>  		return 0;
>  
>  	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> +		if (!hw->iio_devs[i])
> +			continue;
> +
>  		cur_sensor = iio_priv(hw->iio_devs[i]);
>  
>  		if (!(hw->enable_mask & BIT(cur_sensor->id)))
> @@ -278,6 +287,9 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
>  		return err;
>  
>  	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> +		if (!hw->iio_devs[i])
> +			continue;
> +
>  		sensor = iio_priv(hw->iio_devs[i]);
>  		/*
>  		 * store enable buffer timestamp as reference for
> @@ -562,15 +574,9 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
>  			goto out;
>  	}
>  
> -	if (enable) {
> -		err = st_lsm6dsx_sensor_enable(sensor);
> -		if (err < 0)
> -			goto out;
> -	} else {
> -		err = st_lsm6dsx_sensor_disable(sensor);
> -		if (err < 0)
> -			goto out;
> -	}
> +	err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> +	if (err < 0)
> +		goto out;

Another block of unrelated.

>  
>  	err = st_lsm6dsx_set_fifo_odr(sensor, enable);
>  	if (err < 0)
> @@ -690,6 +696,9 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
>  	}
>  
>  	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> +		if (!hw->iio_devs[i])
> +			continue;
> +
>  		buffer = devm_iio_kfifo_allocate(hw->dev);
>  		if (!buffer)
>  			return -ENOMEM;
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> index 3433a5b6bf4d..f2549ddfee20 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> @@ -450,7 +450,7 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
>  	int i;
>  
>  	for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
> -		if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz == odr)
> +		if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz >= odr)

Not sure why this change from the description...

>  			break;
>  
>  	if (i == ST_LSM6DSX_ODR_LIST_SIZE)
> @@ -461,50 +461,82 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
>  	return 0;
>  }
>  
> -static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
> +static u16 st_lsm6dsx_check_odr_dependency(struct st_lsm6dsx_hw *hw, u16 odr,
> +					   enum st_lsm6dsx_sensor_id id)
>  {
> +	struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]);
> +	u16 ret;
> +
> +	if (odr > 0) {
> +		if (hw->enable_mask & BIT(id))
> +			ret = max_t(u16, ref->odr, odr);
> +		else
> +			ret = odr;
> +	} else {
> +		ret = (hw->enable_mask & BIT(id)) ? ref->odr : 0;
> +	}
> +	return ret;

Cleaner just to return at all the places you set ret?

> +}
> +
> +static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 req_odr)
> +{
> +	struct st_lsm6dsx_sensor *ref_sensor = sensor;
>  	struct st_lsm6dsx_hw *hw = sensor->hw;
>  	const struct st_lsm6dsx_reg *reg;
>  	unsigned int data;
> +	u8 val = 0;
>  	int err;
> -	u8 val;
>  
> -	err = st_lsm6dsx_check_odr(sensor, odr, &val);
> -	if (err < 0)
> -		return err;
> +	switch (sensor->id) {
> +	case ST_LSM6DSX_ID_EXT0:
> +	case ST_LSM6DSX_ID_EXT1:
> +	case ST_LSM6DSX_ID_EXT2:
> +	case ST_LSM6DSX_ID_ACC: {
> +		u16 odr;
> +		int i;
> +
> +		/* use acc as trigger for ext devices */
> +		ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> +		for (i = ST_LSM6DSX_ID_ACC; i < ST_LSM6DSX_ID_MAX; i++) {
> +			if (!hw->iio_devs[i] || i == sensor->id)
> +				continue;
> +			odr = st_lsm6dsx_check_odr_dependency(hw, req_odr, i);
> +			if (odr != req_odr)
> +				/* device already configured */
> +				return 0;
> +		}
> +		break;
> +	}
> +	default:
> +		break;
> +	}
> +
> +	if (req_odr > 0) {
> +		err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val);
> +		if (err < 0)
> +			return err;
> +	}
>  
> -	reg = &st_lsm6dsx_odr_table[sensor->id].reg;
> +	reg = &st_lsm6dsx_odr_table[ref_sensor->id].reg;
>  	data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
>  	return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
>  }
>  
> -int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
> -{
> -	int err;
> -
> -	err = st_lsm6dsx_set_odr(sensor, sensor->odr);
> -	if (err < 0)
> -		return err;
> -
> -	sensor->hw->enable_mask |= BIT(sensor->id);
> -
> -	return 0;
> -}
> -
> -int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
> +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
> +				 bool enable)
>  {
>  	struct st_lsm6dsx_hw *hw = sensor->hw;
> -	const struct st_lsm6dsx_reg *reg;
> -	unsigned int data;
> +	u16 odr = enable ? sensor->odr : 0;
>  	int err;
>  
> -	reg = &st_lsm6dsx_odr_table[sensor->id].reg;
> -	data = ST_LSM6DSX_SHIFT_VAL(0, reg->mask);
> -	err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
> +	err = st_lsm6dsx_set_odr(sensor, odr);
>  	if (err < 0)
>  		return err;
>  
> -	sensor->hw->enable_mask &= ~BIT(sensor->id);
> +	if (enable)
> +		hw->enable_mask |= BIT(sensor->id);
> +	else
> +		hw->enable_mask &= ~BIT(sensor->id);
>  
>  	return 0;
>  }
> @@ -516,7 +548,7 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
>  	int err, delay;
>  	__le16 data;
>  
> -	err = st_lsm6dsx_sensor_enable(sensor);
> +	err = st_lsm6dsx_sensor_set_enable(sensor, true);
>  	if (err < 0)
>  		return err;
>  
> @@ -527,7 +559,7 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
>  	if (err < 0)
>  		return err;
>  
> -	st_lsm6dsx_sensor_disable(sensor);
> +	st_lsm6dsx_sensor_set_enable(sensor, false);
>  
>  	*val = (s16)le16_to_cpu(data);
>  
> @@ -892,7 +924,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
>  	if (err < 0)
>  		return err;
>  
> -	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> +	for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) {
>  		hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
>  		if (!hw->iio_devs[i])
>  			return -ENOMEM;
> @@ -909,6 +941,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
>  	}
>  
>  	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> +		if (!hw->iio_devs[i])
> +			continue;
> +
>  		err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
>  		if (err)
>  			return err;
> @@ -927,6 +962,9 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
>  	int i, err = 0;
>  
>  	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> +		if (!hw->iio_devs[i])
> +			continue;
> +
>  		sensor = iio_priv(hw->iio_devs[i]);
>  		if (!(hw->enable_mask & BIT(sensor->id)))
>  			continue;
> @@ -952,6 +990,9 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev)
>  	int i, err = 0;
>  
>  	for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> +		if (!hw->iio_devs[i])
> +			continue;
> +
>  		sensor = iio_priv(hw->iio_devs[i]);
>  		if (!(hw->enable_mask & BIT(sensor->id)))
>  			continue;

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 2/7] iio: imu: st_lsm6dsx: reboot memory content after reset
  2018-11-04 17:12   ` Jonathan Cameron
@ 2018-11-04 17:30     ` Lorenzo Bianconi
  0 siblings, 0 replies; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 17:30 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, devicetree

>
> On Sun,  4 Nov 2018 15:39:01 +0100
> Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
>
> > Perform a memory content reboot after device reset
> >
> > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> > ---
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 16 +++++++++++++---
> >  1 file changed, 13 insertions(+), 3 deletions(-)
> >
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > index c8b993bea757..e3ebd04d8078 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > @@ -56,6 +56,7 @@
> >  #define ST_LSM6DSX_REG_WHOAMI_ADDR           0x0f
> >  #define ST_LSM6DSX_REG_RESET_ADDR            0x12
> >  #define ST_LSM6DSX_REG_RESET_MASK            BIT(0)
> > +#define ST_LSM6DSX_REG_BOOT_MASK             BIT(7)
> >  #define ST_LSM6DSX_REG_BDU_ADDR                      0x12
> >  #define ST_LSM6DSX_REG_BDU_MASK                      BIT(6)
> >  #define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR     0x13
> > @@ -778,12 +779,21 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
> >       u8 drdy_int_reg;
> >       int err;
> >
> > -     err = regmap_write(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
> > -                        ST_LSM6DSX_REG_RESET_MASK);
> > +     err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
> > +                              ST_LSM6DSX_REG_RESET_MASK,
> > +                              FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1));
> >       if (err < 0)
> >               return err;
> >
> > -     msleep(200);
> > +     msleep(50);
> > +
> > +     err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
> > +                              ST_LSM6DSX_REG_BOOT_MASK,
> > +                              FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1));
> > +     if (err < 0)
> > +             return err;
> > +
> > +     msleep(50);
> This change in the timing needs an explanatory comment in the patch
> description.
>
>
> >
> >       /* enable Block Data Update */
> >       err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR,
>

Hi Jonathan,

This patch reloads the trimming parameters to perform a complete reset
of the device. Full explanation can be read here:
https://www.st.com/resource/en/application_note/dm00517282.pdf, section 5.7.
I will add a proper description in v2, thx

Regards,
Lorenzo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 5/7] iio: imu: st_lsm6dsx: add i2c embedded controller support
  2018-11-04 14:39 ` [PATCH 5/7] iio: imu: st_lsm6dsx: add i2c embedded controller support Lorenzo Bianconi
@ 2018-11-04 17:42   ` Jonathan Cameron
  2018-11-04 18:00     ` Lorenzo Bianconi
  0 siblings, 1 reply; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 17:42 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun,  4 Nov 2018 15:39:04 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> i2c controller embedded in lsm6dx series can connect up to four
> slave devices using accelerometer sensor as trigger for i2c
> read/write operations.
> Introduce sensor hub support for lsm6dso sensor. Add register map
> for lis2mdl magnetometer sensor
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>

I'd completely forgotten how this driver did the multiple device registration
so this had me completely confused initially ;)

Perhaps just state here that the result is an entirely separate apparent device
and what it's functionality is at this point in the patch set?

A few minor comments inline.  This looks like it is coming together quite
nicely to me.

Jonathan

> ---
>  drivers/iio/imu/st_lsm6dsx/Makefile           |   3 +-
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 112 +++
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 135 ++--
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 677 ++++++++++++++++++
>  .../linux/platform_data/st_sensors_pdata.h    |   2 +
>  5 files changed, 886 insertions(+), 43 deletions(-)
>  create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> 
> diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile
> index 35919febea2a..e5f733ce6e11 100644
> --- a/drivers/iio/imu/st_lsm6dsx/Makefile
> +++ b/drivers/iio/imu/st_lsm6dsx/Makefile
> @@ -1,4 +1,5 @@
> -st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
> +st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o \
> +		st_lsm6dsx_shub.o
>  
>  obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
>  obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> index 2beb4f563892..d20746eb3d2d 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> @@ -43,6 +43,24 @@ enum st_lsm6dsx_hw_id {
>  					 * ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
>  #define ST_LSM6DSX_SHIFT_VAL(val, mask)	(((val) << __ffs(mask)) & (mask))
>  
> +#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx)		\
> +{									\
> +	.type = chan_type,						\
> +	.address = addr,						\
> +	.modified = 1,							\
> +	.channel2 = mod,						\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
> +			      BIT(IIO_CHAN_INFO_SCALE),			\
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
> +	.scan_index = scan_idx,						\
> +	.scan_type = {							\
> +		.sign = 's',						\
> +		.realbits = 16,						\
> +		.storagebits = 16,					\
> +		.endianness = IIO_LE,					\
> +	},								\
> +}
> +
>  struct st_lsm6dsx_reg {
>  	u8 addr;
>  	u8 mask;
> @@ -50,6 +68,28 @@ struct st_lsm6dsx_reg {
>  
>  struct st_lsm6dsx_hw;
>  
> +struct st_lsm6dsx_odr {
> +	u16 hz;
> +	u8 val;
> +};
> +
> +#define ST_LSM6DSX_ODR_LIST_SIZE	6
> +struct st_lsm6dsx_odr_table_entry {
> +	struct st_lsm6dsx_reg reg;
> +	struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
> +};
> +
> +struct st_lsm6dsx_fs {
> +	u32 gain;
> +	u8 val;
> +};
> +
> +#define ST_LSM6DSX_FS_LIST_SIZE		4
> +struct st_lsm6dsx_fs_table_entry {
> +	struct st_lsm6dsx_reg reg;
> +	struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
> +};
> +
>  /**
>   * struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings
>   * @read_fifo: Read FIFO callback.
> @@ -84,6 +124,66 @@ struct st_lsm6dsx_hw_ts_settings {
>  	struct st_lsm6dsx_reg decimator;
>  };
>  
> +/**
> + * struct st_lsm6dsx_shub_settings - ST IMU hw i2c controller settings
> + * @page_mux: register page mux info (addr + mask).
> + * @master_en: master config register info (addr + mask).
> + * @pullup_en: i2c controller pull-up register info (addr + mask).
> + * @aux_sens: aux sensor register info (addr + mask).
> + * @shub_out: sensor hub first output register info.
> + * @slv0_addr: slave0 address in secondary page.
> + * @dw_slv0_addr: slave0 write register address in secondary page.
> + */
> +struct st_lsm6dsx_shub_settings {
> +	struct st_lsm6dsx_reg page_mux;
> +	struct st_lsm6dsx_reg master_en;
> +	struct st_lsm6dsx_reg pullup_en;
> +	struct st_lsm6dsx_reg aux_sens;
> +	u8 shub_out;
> +	u8 slv0_addr;
> +	u8 dw_slv0_addr;
> +};
> +
> +enum st_lsm6dsx_ext_sensor_id {
> +	ST_LSM6DSX_ID_MAGN,
> +};
> +
> +/**
> + * struct st_lsm6dsx_ext_dev_settings - i2c controller slave settings
> + * @i2c_addr: I2c slave address list.
> + * @wai: Wai address info.
> + * @id: external sensor id.
> + * @odr: Output data rate of the sensor [Hz].
> + * @gain: Configured sensor sensitivity.
> + * @temp_comp: Temperature compensation register info (addr + mask).
> + * @pwr_table: Power on register info (addr + mask).
> + * @off_canc: Offset cancellation register info (addr + mask).
> + * @bdu: Block data update register info (addr + mask).
> + * @out: Output register info.
> + */
> +struct st_lsm6dsx_ext_dev_settings {
> +	u8 i2c_addr[2];
> +	struct {
> +		u8 addr;
> +		u8 val;
> +	} wai;
> +	enum st_lsm6dsx_ext_sensor_id id;
> +	struct st_lsm6dsx_odr_table_entry odr_table;
> +	struct st_lsm6dsx_fs_table_entry fs_table;
> +	struct st_lsm6dsx_reg temp_comp;
> +	struct {
> +		struct st_lsm6dsx_reg reg;
> +		u8 off_val;
> +		u8 on_val;
> +	} pwr_table;
> +	struct st_lsm6dsx_reg off_canc;
> +	struct st_lsm6dsx_reg bdu;
> +	struct {
> +		u8 addr;
> +		u8 len;
> +	} out;
> +};
> +
>  /**
>   * struct st_lsm6dsx_settings - ST IMU sensor settings
>   * @wai: Sensor WhoAmI default value.
> @@ -93,6 +193,7 @@ struct st_lsm6dsx_hw_ts_settings {
>   * @batch: List of FIFO batching register info (addr + mask).
>   * @fifo_ops: Sensor hw FIFO parameters.
>   * @ts_settings: Hw timer related settings.
> + * @shub_settings: i2c controller related settings.
>   */
>  struct st_lsm6dsx_settings {
>  	u8 wai;
> @@ -102,6 +203,7 @@ struct st_lsm6dsx_settings {
>  	struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
>  	struct st_lsm6dsx_fifo_ops fifo_ops;
>  	struct st_lsm6dsx_hw_ts_settings ts_settings;
> +	struct st_lsm6dsx_shub_settings shub_settings;
>  };
>  
>  enum st_lsm6dsx_sensor_id {
> @@ -129,6 +231,7 @@ enum st_lsm6dsx_fifo_mode {
>   * @sip: Number of samples in a given pattern.
>   * @decimator: FIFO decimation factor.
>   * @ts_ref: Sensor timestamp reference for hw one.
> + * @ext_info: Sensor settings if it is connected to i2c controller
>   */
>  struct st_lsm6dsx_sensor {
>  	char name[32];
> @@ -142,6 +245,11 @@ struct st_lsm6dsx_sensor {
>  	u8 sip;
>  	u8 decimator;
>  	s64 ts_ref;
> +
> +	struct {
> +		const struct st_lsm6dsx_ext_dev_settings *settings;
> +		u8 addr;
> +	} ext_info;
>  };
>  
>  /**
> @@ -181,6 +289,7 @@ struct st_lsm6dsx_hw {
>  	const struct st_lsm6dsx_settings *settings;
>  };
>  
> +static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
>  extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
>  
>  int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> @@ -197,6 +306,9 @@ int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
>  int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw);
>  int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw);
>  int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val);
> +int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name);
> +int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable);
> +int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable);
>  
>  static inline int
>  st_lsm6dsx_update_bits_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> index f2549ddfee20..463859f287da 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> @@ -88,17 +88,6 @@
>  #define ST_LSM6DSX_GYRO_FS_1000_GAIN		IIO_DEGREE_TO_RAD(35000)
>  #define ST_LSM6DSX_GYRO_FS_2000_GAIN		IIO_DEGREE_TO_RAD(70000)
>  
> -struct st_lsm6dsx_odr {
> -	u16 hz;
> -	u8 val;
> -};
> -
> -#define ST_LSM6DSX_ODR_LIST_SIZE	6
> -struct st_lsm6dsx_odr_table_entry {
> -	struct st_lsm6dsx_reg reg;
> -	struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
> -};
> -
>  static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
>  	[ST_LSM6DSX_ID_ACC] = {
>  		.reg = {
> @@ -126,17 +115,6 @@ static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
>  	}
>  };
>  
> -struct st_lsm6dsx_fs {
> -	u32 gain;
> -	u8 val;
> -};
> -
> -#define ST_LSM6DSX_FS_LIST_SIZE		4
> -struct st_lsm6dsx_fs_table_entry {
> -	struct st_lsm6dsx_reg reg;
> -	struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
> -};
> -
>  static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
>  	[ST_LSM6DSX_ID_ACC] = {
>  		.reg = {
> @@ -342,27 +320,30 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
>  				.mask = GENMASK(7, 6),
>  			},
>  		},
> +		.shub_settings = {
> +			.page_mux = {
> +				.addr = 0x01,
> +				.mask = BIT(6),
> +			},
> +			.master_en = {
> +				.addr = 0x14,
> +				.mask = BIT(2),
> +			},
> +			.pullup_en = {
> +				.addr = 0x14,
> +				.mask = BIT(3),
> +			},
> +			.aux_sens = {
> +				.addr = 0x14,
> +				.mask = GENMASK(1, 0),
> +			},
> +			.shub_out = 0x02,
> +			.slv0_addr = 0x15,
> +			.dw_slv0_addr = 0x21,
> +		}
>  	},
>  };
>  
> -#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx)		\
> -{									\
> -	.type = chan_type,						\
> -	.address = addr,						\
> -	.modified = 1,							\
> -	.channel2 = mod,						\
> -	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |			\
> -			      BIT(IIO_CHAN_INFO_SCALE),			\
> -	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
> -	.scan_index = scan_idx,						\
> -	.scan_type = {							\
> -		.sign = 's',						\
> -		.realbits = 16,						\
> -		.storagebits = 16,					\
> -		.endianness = IIO_LE,					\
> -	},								\
> -}
> -
>  static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
>  	ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
>  			   IIO_MOD_X, 0),
> @@ -383,6 +364,21 @@ static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
>  	IIO_CHAN_SOFT_TIMESTAMP(3),
>  };
>  
> +int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable)
> +{
> +	const struct st_lsm6dsx_shub_settings *hub_settings;
> +	unsigned int data;
> +	int err;
> +
> +	hub_settings = &hw->settings->shub_settings;
> +	data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->page_mux.mask);
> +	err = regmap_update_bits(hw->regmap, hub_settings->page_mux.addr,
> +				 hub_settings->page_mux.mask, data);
> +	usleep_range(100, 150);
> +
> +	return err;
> +}
> +
>  static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
>  {
>  	int err, i, j, data;
> @@ -728,8 +724,6 @@ static const struct iio_info st_lsm6dsx_gyro_info = {
>  	.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
>  };
>  
> -static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
> -
>  static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
>  {
>  	struct device_node *np = hw->dev->of_node;
> @@ -768,6 +762,51 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
>  	return err;
>  }
>  
> +static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw)
> +{
> +	const struct st_lsm6dsx_shub_settings *hub_settings;
> +	struct device_node *np = hw->dev->of_node;
> +	struct st_sensors_platform_data *pdata;
> +	unsigned int data;
> +	int err = 0;
> +
> +	hub_settings = &hw->settings->shub_settings;
> +
> +	pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
> +	if ((np && of_property_read_bool(np, "st,pullups")) ||
> +	    (pdata && pdata->pullups)) {
> +		err = st_lsm6dsx_set_page(hw, true);
> +		if (err < 0)
> +			return err;
> +
> +		data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->pullup_en.mask);
> +		err = regmap_update_bits(hw->regmap,
> +					 hub_settings->pullup_en.addr,
> +					 hub_settings->pullup_en.mask, data);
> +
> +		st_lsm6dsx_set_page(hw, false);
> +
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	if (hub_settings->aux_sens.addr) {
> +		/* configure aux sensors */
> +		err = st_lsm6dsx_set_page(hw, true);
> +		if (err < 0)
> +			return err;
> +
> +		data = ST_LSM6DSX_SHIFT_VAL(3, hub_settings->aux_sens.mask);
> +		err = regmap_update_bits(hw->regmap,
> +					 hub_settings->aux_sens.addr,
> +					 hub_settings->aux_sens.mask, data);
> +
> +		st_lsm6dsx_set_page(hw, false);
> +	}
> +
> +	return err;
> +}
> +
>  static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
>  {
>  	const struct st_lsm6dsx_hw_ts_settings *ts_settings;
> @@ -846,6 +885,10 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
>  	if (err < 0)
>  		return err;
>  
> +	err = st_lsm6dsx_init_shub(hw);
> +	if (err < 0)
> +		return err;
> +
>  	return st_lsm6dsx_init_hw_timer(hw);
>  }
>  
> @@ -899,6 +942,7 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
>  int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
>  		     struct regmap *regmap)
>  {
> +	const struct st_lsm6dsx_shub_settings *hub_settings;
>  	struct st_lsm6dsx_hw *hw;
>  	int i, err;
>  
> @@ -934,6 +978,13 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
>  	if (err < 0)
>  		return err;
>  
> +	hub_settings = &hw->settings->shub_settings;
> +	if (hub_settings->master_en.addr) {
> +		err = st_lsm6dsx_shub_probe(hw, name);
> +		if (err < 0)
> +			return err;
> +	}
> +
>  	if (hw->irq > 0) {
>  		err = st_lsm6dsx_fifo_setup(hw);
>  		if (err < 0)
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> new file mode 100644
> index 000000000000..b37f9dbdad17
> --- /dev/null
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> @@ -0,0 +1,677 @@
> +/*
> + * STMicroelectronics st_lsm6dsx i2c controller driver
> + *
> + * i2c controller embedded in lsm6dx series can connect up to four
> + * slave devices using accelerometer sensor as trigger for i2c
> + * read/write operations. Current implementation use SLV0 for slave
> + * configuration and SLV{1,2,3} to read data and push them into the
> + * hw FIFO
> + *
> + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
> + *
> + * Permission to use, copy, modify, and/or distribute this software for any
> + * purpose with or without fee is hereby granted, provided that the above
> + * copyright notice and this permission notice appear in all copies.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> + *
> + */
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/bitfield.h>
> +
> +#include "st_lsm6dsx.h"
> +
> +#define ST_LSM6DSX_MAX_SLV_NUM			3
> +#define ST_LSM6DSX_SLV_ADDR(n, base)		((base) + (n) * 3)
> +#define ST_LSM6DSX_SLV_SUB_ADDR(n, base)	((base) + 1 + (n) * 3)
> +#define ST_LSM6DSX_SLV_CONFIG(n, base)		((base) + 2 + (n) * 3)
> +
> +#define ST_LS6DSX_READ_OP_MASK			GENMASK(2, 0)
> +
> +static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = {
> +	/* LIS2MDL */
> +	{
> +		.i2c_addr = { 0x1e },
> +		.wai = {
> +			.addr = 0x4f,
> +			.val = 0x40,
> +		},
> +		.id = ST_LSM6DSX_ID_MAGN,
> +		.odr_table = {
> +			.reg = {
> +				.addr = 0x60,
> +				.mask = GENMASK(3, 2),
> +			},
> +			.odr_avl[0] = {  10, 0x0 },
> +			.odr_avl[1] = {  20, 0x1 },
> +			.odr_avl[2] = {  50, 0x2 },
> +			.odr_avl[3] = { 100, 0x3 },
> +		},
> +		.fs_table = {
> +			.fs_avl[0] = {
> +				.gain = 1500,
> +				.val = 0x0,
> +			}, /* 1500 uG/LSB */
> +		},
> +		.temp_comp = {
> +			.addr = 0x60,
> +			.mask = BIT(7),
> +		},
> +		.pwr_table = {
> +			.reg = {
> +				.addr = 0x60,
> +				.mask = GENMASK(1, 0),
> +			},
> +			.off_val = 0x2,
> +			.on_val = 0x0,
> +		},
> +		.off_canc = {
> +			.addr = 0x61,
> +			.mask = BIT(1),
> +		},
> +		.bdu = {
> +			.addr = 0x62,
> +			.mask = BIT(4),
> +		},
> +		.out = {
> +			.addr = 0x68,
> +			.len = 6,
> +		},
> +	},
> +};
> +
> +static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw)
> +{
> +	struct st_lsm6dsx_sensor *sensor;
> +
> +	sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> +	msleep((2000U / sensor->odr) + 1);
> +}
> +
> +/**
> + * st_lsm6dsx_shub_read_reg - read i2c controller register
> + *
> + * Read st_lsm6dsx i2c controller register
> + */
> +static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr,
> +				    u8 *data, int len)
> +{
> +	const struct st_lsm6dsx_shub_settings *hub_settings;
> +	int err;
> +
> +	mutex_lock(&hw->page_lock);
> +
> +	hub_settings = &hw->settings->shub_settings;
> +	err = st_lsm6dsx_set_page(hw, true);
> +	if (err < 0)
> +		goto out;
> +
> +	err = regmap_bulk_read(hw->regmap, addr, data, len);
> +
> +	st_lsm6dsx_set_page(hw, false);
> +out:
> +	mutex_unlock(&hw->page_lock);
> +
> +	return err;
> +}
> +
> +/**
> + * st_lsm6dsx_shub_write_reg - write i2c controller register
> + *
> + * Write st_lsm6dsx i2c controller register
> + */
> +static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr,
> +				     u8 *data, int len)
> +{
> +	int err;
> +
> +	mutex_lock(&hw->page_lock);
> +	err = st_lsm6dsx_set_page(hw, true);
> +	if (err < 0)
> +		goto out;
> +
> +	err = regmap_bulk_write(hw->regmap, addr, data, len);
> +
> +	st_lsm6dsx_set_page(hw, false);
> +out:
> +	mutex_unlock(&hw->page_lock);
> +
> +	return err;
> +}
> +
> +static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
> +					 bool enable)
> +{
> +	const struct st_lsm6dsx_shub_settings *hub_settings;
> +	struct st_lsm6dsx_hw *hw = sensor->hw;
> +	unsigned int data;
> +	int err;
> +
> +	/* enable acc sensor as trigger */
> +	err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> +	if (err < 0)
> +		return err;
> +
> +	mutex_lock(&hw->page_lock);
> +
> +	hub_settings = &hw->settings->shub_settings;
> +	err = st_lsm6dsx_set_page(hw, true);
> +	if (err < 0)
> +		goto out;
> +
> +	data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask);
> +	err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr,
> +				 hub_settings->master_en.mask, data);
> +
> +	st_lsm6dsx_set_page(hw, false);
> +out:
> +	mutex_unlock(&hw->page_lock);
> +
> +	return err;
> +}
> +
> +/**
> + * st_lsm6dsx_shub_read - read data from slave device register
> + *
> + * Read data from slave device register. SLV0 is used for
> + * one-shot read operation
> + */
> +static int
> +st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr,
> +		     u8 *data, int len)
> +{
> +	const struct st_lsm6dsx_shub_settings *hub_settings;
> +	struct st_lsm6dsx_hw *hw = sensor->hw;
> +	u8 config[3], slv_addr;
> +	int err;
> +
> +	hub_settings = &hw->settings->shub_settings;
> +	slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
> +
> +	config[0] = (sensor->ext_info.addr << 1) | 1;
> +	config[1] = addr;
> +	config[2] = len & ST_LS6DSX_READ_OP_MASK;
> +
> +	err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> +					sizeof(config));
> +	if (err < 0)
> +		return err;
> +
> +	err = st_lsm6dsx_shub_master_enable(sensor, true);
> +	if (err < 0)
> +		return err;
> +
> +	st_lsm6dsx_shub_wait_complete(hw);
> +
> +	err = st_lsm6dsx_shub_read_reg(hw, hub_settings->shub_out, data,
> +				       len & ST_LS6DSX_READ_OP_MASK);
> +
> +	st_lsm6dsx_shub_master_enable(sensor, false);
> +
> +	memset(config, 0, sizeof(config));
> +	return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> +					 sizeof(config));
> +}
> +
> +/**
> + * st_lsm6dsx_shub_write - write data to slave device register
> + *
> + * Write data from slave device register. SLV0 is used for
> + * one-shot write operation
> + */
> +static int
> +st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
> +		      u8 *data, int len)
> +{
> +	const struct st_lsm6dsx_shub_settings *hub_settings;
> +	struct st_lsm6dsx_hw *hw = sensor->hw;
> +	u8 config[2], slv_addr;
> +	int err, i;
> +
> +	hub_settings = &hw->settings->shub_settings;
> +	slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
> +	config[0] = sensor->ext_info.addr << 1;
> +	for (i = 0 ; i < len; i++) {
> +		config[1] = addr + i;
> +
> +		err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> +						sizeof(config));
> +		if (err < 0)
> +			return err;
> +
> +		err = st_lsm6dsx_shub_write_reg(hw, hub_settings->dw_slv0_addr,
> +						&data[i], 1);
> +		if (err < 0)
> +			return err;
> +
> +		err = st_lsm6dsx_shub_master_enable(sensor, true);
> +		if (err < 0)
> +			return err;
> +
> +		st_lsm6dsx_shub_wait_complete(hw);
> +
> +		st_lsm6dsx_shub_master_enable(sensor, false);
> +	}
> +
> +	memset(config, 0, sizeof(config));
> +	return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config));
> +}
> +
> +static int
> +st_lsm6dsx_shub_write_with_mask(struct st_lsm6dsx_sensor *sensor,
> +				u8 addr, u8 mask, u8 val)
> +{
> +	int err;
> +	u8 data;
> +
> +	err = st_lsm6dsx_shub_read(sensor, addr, &data, sizeof(data));
> +	if (err < 0)
> +		return err;
> +
> +	data = ((data & ~mask) | (val << __ffs(mask) & mask));
> +
> +	return st_lsm6dsx_shub_write(sensor, addr, &data, sizeof(data));
> +}
> +
> +static int
> +st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor,
> +			    u16 odr, u16 *val)
> +{
> +	const struct st_lsm6dsx_ext_dev_settings *settings;
> +	int i;
> +
> +	settings = sensor->ext_info.settings;
> +	for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
> +		if (settings->odr_table.odr_avl[i].hz == odr)
> +			break;
> +
> +	if (i == ST_LSM6DSX_ODR_LIST_SIZE)
> +		return -EINVAL;
> +
> +	*val = settings->odr_table.odr_avl[i].val;
> +	return 0;
> +}
> +
> +static int
> +st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
> +{
> +	const struct st_lsm6dsx_ext_dev_settings *settings;
> +	u16 val;
> +	int err;
> +
> +	err = st_lsm6dsx_shub_get_odr_val(sensor, odr, &val);
> +	if (err < 0)
> +		return err;
> +
> +	settings = sensor->ext_info.settings;
> +	return st_lsm6dsx_shub_write_with_mask(sensor,
> +					       settings->odr_table.reg.addr,
> +					       settings->odr_table.reg.mask,
> +					       val);
> +}
> +
> +int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
> +{
> +	const struct st_lsm6dsx_ext_dev_settings *settings;
> +	int err;
> +
> +	settings = sensor->ext_info.settings;
> +	if (enable) {
> +		err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr);
> +		if (err < 0)
> +			return err;
> +	} else {
> +		err = st_lsm6dsx_shub_write_with_mask(sensor,
> +					settings->odr_table.reg.addr,
> +					settings->odr_table.reg.mask, 0);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	if (settings->pwr_table.reg.addr) {
> +		u8 val;
> +
> +		val = enable ? settings->pwr_table.on_val
> +			     : settings->pwr_table.off_val;
> +		err = st_lsm6dsx_shub_write_with_mask(sensor,
> +					settings->pwr_table.reg.addr,
> +					settings->pwr_table.reg.mask, val);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	return st_lsm6dsx_shub_master_enable(sensor, enable);
> +}
> +
> +static int
> +st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor,
> +			     struct iio_chan_spec const *ch,
> +			     int *val)
> +{
> +	int err, delay, len = ch->scan_type.realbits >> 3;
> +	__le32 data;
> +
> +	err = st_lsm6dsx_shub_set_enable(sensor, true);
> +	if (err < 0)
> +		return err;
> +
> +	delay = 1000000 / sensor->odr;
> +	usleep_range(delay, 2 * delay);
> +
> +	err = st_lsm6dsx_shub_read(sensor, ch->address, (u8 *)&data, len);
> +	if (err < 0)
> +		return err;
> +
> +	st_lsm6dsx_shub_set_enable(sensor, false);
> +
> +	switch (len) {
> +	case 2:
> +		*val = (s16)le16_to_cpu(data);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	return IIO_VAL_INT;
> +}
> +
> +static int
> +st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev,
> +			 struct iio_chan_spec const *ch,
> +			 int *val, int *val2, long mask)
> +{
> +	struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = iio_device_claim_direct_mode(iio_dev);
> +		if (ret)
> +			break;
> +
> +		ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val);
> +		iio_device_release_direct_mode(iio_dev);
> +		break;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*val = sensor->odr;
> +		ret = IIO_VAL_INT;
> +		break;
> +	case IIO_CHAN_INFO_SCALE:
> +		*val = 0;
> +		*val2 = sensor->gain;
> +		ret = IIO_VAL_INT_PLUS_MICRO;
> +		break;
> +	default:
> +		ret = -EINVAL;
> +		break;
> +	}
> +
> +	return ret;
> +}
> +
> +static int
> +st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev,
> +			  struct iio_chan_spec const *chan,
> +			  int val, int val2, long mask)
> +{
> +	struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
> +	int err;
> +
> +	err = iio_device_claim_direct_mode(iio_dev);
> +	if (err)
> +		return err;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ: {
> +		u16 data;
> +
> +		err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data);
> +		if (!err)
> +			sensor->odr = val;
> +		break;
> +	}
> +	default:
> +		err = -EINVAL;
> +		break;
> +	}
> +
> +	iio_device_release_direct_mode(iio_dev);
> +
> +	return err;
> +}
> +
> +static ssize_t
> +st_lsm6dsx_shub_sampling_freq_avail(struct device *dev,
> +				    struct device_attribute *attr,
> +				    char *buf)
> +{
> +	struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
> +	const struct st_lsm6dsx_ext_dev_settings *settings;
> +	int i, len = 0;
> +
> +	settings = sensor->ext_info.settings;
> +	for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) {
> +		u16 val = settings->odr_table.odr_avl[i].hz;
> +
> +		if (val > 0)
> +			len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
> +					 val);
> +	}
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +
> +static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev,
> +					   struct device_attribute *attr,
> +					   char *buf)
> +{
> +	struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
> +	const struct st_lsm6dsx_ext_dev_settings *settings;
> +	int i, len = 0;
> +
> +	settings = sensor->ext_info.settings;
> +	for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) {
> +		u16 val = settings->fs_table.fs_avl[i].gain;
> +
> +		if (val > 0)
> +			len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
> +					 val);
> +	}
> +	buf[len - 1] = '\n';
> +
> +	return len;
> +}
> +
> +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail);
> +static IIO_DEVICE_ATTR(in_ext_scale_available, 0444,
> +		       st_lsm6dsx_shub_scale_avail, NULL, 0);
> +static struct attribute *st_lsm6dsx_ext_attributes[] = {
> +	&iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> +	&iio_dev_attr_in_ext_scale_available.dev_attr.attr,

What's this abi element?

> +	NULL,
> +};
> +
> +static const struct attribute_group st_lsm6dsx_ext_attribute_group = {
> +	.attrs = st_lsm6dsx_ext_attributes,
> +};
> +
> +static const struct iio_info st_lsm6dsx_ext_info = {
> +	.attrs = &st_lsm6dsx_ext_attribute_group,
> +	.read_raw = st_lsm6dsx_shub_read_raw,
> +	.write_raw = st_lsm6dsx_shub_write_raw,
> +	.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
> +};
> +
> +static struct iio_dev *
> +st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw,
> +			     enum st_lsm6dsx_sensor_id id,
> +			     const struct st_lsm6dsx_ext_dev_settings *info,
> +			     u8 i2c_addr, const char *name)
> +{
> +	struct iio_chan_spec *ext_channels;
> +	struct st_lsm6dsx_sensor *sensor;
> +	struct iio_dev *iio_dev;
> +
> +	iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
> +	if (!iio_dev)
> +		return NULL;
> +
> +	iio_dev->modes = INDIO_DIRECT_MODE;
> +	iio_dev->dev.parent = hw->dev;
> +	iio_dev->info = &st_lsm6dsx_ext_info;
> +
> +	sensor = iio_priv(iio_dev);
> +	sensor->id = id;
> +	sensor->hw = hw;
> +	sensor->odr = info->odr_table.odr_avl[0].hz;
> +	sensor->gain = info->fs_table.fs_avl[0].gain;
> +	sensor->ext_info.settings = info;
> +	sensor->ext_info.addr = i2c_addr;
> +	sensor->watermark = 1;
> +
> +	switch (info->id) {
> +	case ST_LSM6DSX_ID_MAGN: {
> +		const struct iio_chan_spec magn_channels[] = {
> +			ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr,
> +					   IIO_MOD_X, 0),
> +			ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 2,
> +					   IIO_MOD_Y, 1),
> +			ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 4,
> +					   IIO_MOD_Z, 2),
> +			IIO_CHAN_SOFT_TIMESTAMP(3),
> +		};
> +
> +		ext_channels = devm_kzalloc(hw->dev, sizeof(magn_channels),
> +					    GFP_KERNEL);
> +		if (!ext_channels)
> +			return NULL;
> +
> +		memcpy(ext_channels, magn_channels, sizeof(magn_channels));
> +		iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
> +		iio_dev->channels = ext_channels;
> +		iio_dev->num_channels = ARRAY_SIZE(magn_channels);
> +
> +		scnprintf(sensor->name, sizeof(sensor->name), "%s_magn",
> +			  name);
> +		break;
> +	}
> +	default:
> +		return NULL;
> +	}
> +	iio_dev->name = sensor->name;
> +
> +	return iio_dev;
> +}
> +
> +static int st_lsm6dsx_shub_init_device(struct st_lsm6dsx_sensor *sensor)
> +{
> +	const struct st_lsm6dsx_ext_dev_settings *settings;
> +	int err = 0;
> +
> +	settings = sensor->ext_info.settings;
> +	if (settings->bdu.addr) {
> +		err = st_lsm6dsx_shub_write_with_mask(sensor,
> +						      settings->bdu.addr,
> +						      settings->bdu.mask, 1);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	if (settings->temp_comp.addr) {
> +		err = st_lsm6dsx_shub_write_with_mask(sensor,
> +					settings->temp_comp.addr,
> +					settings->temp_comp.mask, 1);
> +		if (err < 0)
> +			return err;
> +	}
> +
> +	if (settings->off_canc.addr)
> +		err = st_lsm6dsx_shub_write_with_mask(sensor,
> +					settings->off_canc.addr,
> +					settings->off_canc.mask, 1);
Return directly from this last command and return 0 perhaps below?

That drops the need to initialize err as well.

Doesn't matter though if you prefer it this way!
> +
> +	return err;
> +}
> +
> +int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name)
> +{
> +	const struct st_lsm6dsx_shub_settings *hub_settings;
> +	const struct st_lsm6dsx_ext_dev_settings *settings;
> +	enum st_lsm6dsx_sensor_id id = ST_LSM6DSX_ID_EXT0;
> +	struct st_lsm6dsx_sensor *acc_sensor, *sensor;
> +	int err, i, j, num_ext_dev = 0;
> +	u8 config[3], data, slv_addr;
> +
> +	acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> +	hub_settings = &hw->settings->shub_settings;
> +	slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
> +
> +	while (i < ARRAY_SIZE(st_lsm6dsx_ext_dev_table) &&
> +	       num_ext_dev < ST_LSM6DSX_MAX_SLV_NUM) {
> +		settings = &st_lsm6dsx_ext_dev_table[i];
Might be better as a for loop as then you'll have the loop
increment nice and obvious up here. Perhaps just break separately
for the num_ext_dev rather than having it as the loop condition?


> +
> +		for (j = 0; j < ARRAY_SIZE(settings->i2c_addr); j++) {
This is getting very deeply nested and perhaps worth pulling out to a utility
function?  I haven't checked just how many parameters that would have
though so perhaps not...

Could also reduce the scope of a few variables such as config by
pulling it in the for loop I think.

> +			if (!settings->i2c_addr[j])
> +				continue;
> +
> +			/* read wai slave register */
> +			config[0] = (settings->i2c_addr[j] << 1) | 0x1;
> +			config[1] = settings->wai.addr;
> +			config[2] = 0x1;
> +
> +			err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> +							sizeof(config));
> +			if (err < 0)
> +				return err;
> +
> +			err = st_lsm6dsx_shub_master_enable(acc_sensor, true);
> +			if (err < 0)
> +				return err;
> +
> +			st_lsm6dsx_shub_wait_complete(hw);
> +
> +			err = st_lsm6dsx_shub_read_reg(hw,
> +						       hub_settings->shub_out,
> +						       &data, sizeof(data));
> +
> +			st_lsm6dsx_shub_master_enable(acc_sensor, false);
> +
> +			if (err < 0)
> +				return err;
> +
> +			if (data != settings->wai.val)
> +				continue;
> +
> +			hw->iio_devs[id] = st_lsm6dsx_shub_alloc_iiodev(hw,
> +							id, settings,
> +							settings->i2c_addr[j],
> +							name);
> +			if (!hw->iio_devs[id])
> +				return -ENOMEM;
> +
> +			sensor = iio_priv(hw->iio_devs[id]);
> +			err = st_lsm6dsx_shub_init_device(sensor);
> +			if (err < 0)
> +				return err;
> +
> +			num_ext_dev++;
> +			id++;
> +			break;
> +		}
> +		i++;
> +	}
> +
> +	memset(config, 0, sizeof(config));
> +	return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> +					 sizeof(config));
> +}
> diff --git a/include/linux/platform_data/st_sensors_pdata.h b/include/linux/platform_data/st_sensors_pdata.h
> index f8274b0c6888..728193111c2f 100644
> --- a/include/linux/platform_data/st_sensors_pdata.h
> +++ b/include/linux/platform_data/st_sensors_pdata.h
> @@ -18,11 +18,13 @@
>   *	Accelerometer DRDY on LSM330 available only on pin 1 (see datasheet).
>   * @open_drain: set the interrupt line to be open drain if possible.
>   * @spi_3wire: enable spi-3wire mode.
> + * @pullups: enable/disable i2c controller pullup resistors.
>   */
>  struct st_sensors_platform_data {
>  	u8 drdy_int_pin;
>  	bool open_drain;
>  	bool spi_3wire;
> +	bool pullups;
>  };
>  
>  #endif /* ST_SENSORS_PDATA_H */

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 4/7] iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
  2018-11-04 17:18   ` Jonathan Cameron
@ 2018-11-04 17:47     ` Lorenzo Bianconi
  2018-11-04 18:18       ` Jonathan Cameron
  0 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 17:47 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, devicetree

>
> On Sun,  4 Nov 2018 15:39:03 +0100
> Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
>
> > Add ST_LSM6DSX_ID_EXT{0,1,2} sensor ids as reference for slave devices
> > connected to st_lsm6dsx i2c controller. Moreover introduce odr dependency
> > between accel and ext devices since accelerometer is used as trigger for
> > i2c master controller read/write operations
> >
> > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
>
> Hi Lorenzo,
>
> There looks to be an unrelated cleanup in here around set_enable so
> please pull that out as a separate patch.
>
> Also, it seems the odr dependency is not as simple as they should be
> the same?  Perhaps you could add more here on what that dependency is?
>

I added the odr change in this patch since external sensors need to
use accelerometer
device as trigger for i2c operations, in other words the accelerometer
sensor needs to
be powered up in order to enable i2c master (I was thinking to a
logical dependency here :)).
Anyway I am fine to add just ext_id definitions in this patch and add
odr dependency in a
separate patch.
Just one comment inline.

Regards,
Lorenzo


> Thanks,
>
> Jonathan
>
> > ---
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       |   9 +-
> >  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    |  27 +++--
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 101 ++++++++++++------
> >  3 files changed, 95 insertions(+), 42 deletions(-)
> >
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > index ac4cbbb0b3fb..2beb4f563892 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > @@ -105,8 +105,11 @@ struct st_lsm6dsx_settings {
> >  };
> >
> >  enum st_lsm6dsx_sensor_id {
> > -     ST_LSM6DSX_ID_ACC,
> >       ST_LSM6DSX_ID_GYRO,
> > +     ST_LSM6DSX_ID_ACC,
> > +     ST_LSM6DSX_ID_EXT0,
> > +     ST_LSM6DSX_ID_EXT1,
> > +     ST_LSM6DSX_ID_EXT2,
> >       ST_LSM6DSX_ID_MAX,
> >  };
> >
> > @@ -182,8 +185,8 @@ extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
> >
> >  int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> >                    struct regmap *regmap);
> > -int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
> > -int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
> > +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
> > +                              bool enable);
> Unrelated change?
>
> >  int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
> >  int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val);
> >  int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > index 4b3ba0956b5a..96f7d56d3b6d 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > @@ -102,6 +102,9 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
> >
> >       *max_odr = 0, *min_odr = ~0;
> >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > +             if (!hw->iio_devs[i])
> > +                     continue;
> > +
> >               sensor = iio_priv(hw->iio_devs[i]);
> >
> >               if (!(hw->enable_mask & BIT(sensor->id)))
> > @@ -125,6 +128,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
> >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> >               const struct st_lsm6dsx_reg *dec_reg;
> >
> > +             if (!hw->iio_devs[i])
> > +                     continue;
> > +
> >               sensor = iio_priv(hw->iio_devs[i]);
> >               /* update fifo decimators and sample in pattern */
> >               if (hw->enable_mask & BIT(sensor->id)) {
> > @@ -232,6 +238,9 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
> >               return 0;
> >
> >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > +             if (!hw->iio_devs[i])
> > +                     continue;
> > +
> >               cur_sensor = iio_priv(hw->iio_devs[i]);
> >
> >               if (!(hw->enable_mask & BIT(cur_sensor->id)))
> > @@ -278,6 +287,9 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
> >               return err;
> >
> >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > +             if (!hw->iio_devs[i])
> > +                     continue;
> > +
> >               sensor = iio_priv(hw->iio_devs[i]);
> >               /*
> >                * store enable buffer timestamp as reference for
> > @@ -562,15 +574,9 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
> >                       goto out;
> >       }
> >
> > -     if (enable) {
> > -             err = st_lsm6dsx_sensor_enable(sensor);
> > -             if (err < 0)
> > -                     goto out;
> > -     } else {
> > -             err = st_lsm6dsx_sensor_disable(sensor);
> > -             if (err < 0)
> > -                     goto out;
> > -     }
> > +     err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> > +     if (err < 0)
> > +             goto out;
>
> Another block of unrelated.
>
> >
> >       err = st_lsm6dsx_set_fifo_odr(sensor, enable);
> >       if (err < 0)
> > @@ -690,6 +696,9 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
> >       }
> >
> >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > +             if (!hw->iio_devs[i])
> > +                     continue;
> > +
> >               buffer = devm_iio_kfifo_allocate(hw->dev);
> >               if (!buffer)
> >                       return -ENOMEM;
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > index 3433a5b6bf4d..f2549ddfee20 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > @@ -450,7 +450,7 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
> >       int i;
> >
> >       for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
> > -             if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz == odr)
> > +             if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz >= odr)
>
> Not sure why this change from the description...

here st_lsm6dsx_odr_table is for lsm6dso accel sensor, while odr can
be from external sensors and
they can differ (e.g. lsm6dso accel device and lis2mdl conncted to i2c
controller)

>
> >                       break;
> >
> >       if (i == ST_LSM6DSX_ODR_LIST_SIZE)
> > @@ -461,50 +461,82 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
> >       return 0;
> >  }
> >
> > -static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
> > +static u16 st_lsm6dsx_check_odr_dependency(struct st_lsm6dsx_hw *hw, u16 odr,
> > +                                        enum st_lsm6dsx_sensor_id id)
> >  {
> > +     struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]);
> > +     u16 ret;
> > +
> > +     if (odr > 0) {
> > +             if (hw->enable_mask & BIT(id))
> > +                     ret = max_t(u16, ref->odr, odr);
> > +             else
> > +                     ret = odr;
> > +     } else {
> > +             ret = (hw->enable_mask & BIT(id)) ? ref->odr : 0;
> > +     }
> > +     return ret;
>
> Cleaner just to return at all the places you set ret?

ack fine, will do in v2

>
> > +}
> > +
> > +static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 req_odr)
> > +{
> > +     struct st_lsm6dsx_sensor *ref_sensor = sensor;
> >       struct st_lsm6dsx_hw *hw = sensor->hw;
> >       const struct st_lsm6dsx_reg *reg;
> >       unsigned int data;
> > +     u8 val = 0;
> >       int err;
> > -     u8 val;
> >
> > -     err = st_lsm6dsx_check_odr(sensor, odr, &val);
> > -     if (err < 0)
> > -             return err;
> > +     switch (sensor->id) {
> > +     case ST_LSM6DSX_ID_EXT0:
> > +     case ST_LSM6DSX_ID_EXT1:
> > +     case ST_LSM6DSX_ID_EXT2:
> > +     case ST_LSM6DSX_ID_ACC: {
> > +             u16 odr;
> > +             int i;
> > +
> > +             /* use acc as trigger for ext devices */
> > +             ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> > +             for (i = ST_LSM6DSX_ID_ACC; i < ST_LSM6DSX_ID_MAX; i++) {
> > +                     if (!hw->iio_devs[i] || i == sensor->id)
> > +                             continue;
> > +                     odr = st_lsm6dsx_check_odr_dependency(hw, req_odr, i);
> > +                     if (odr != req_odr)
> > +                             /* device already configured */
> > +                             return 0;
> > +             }
> > +             break;
> > +     }
> > +     default:
> > +             break;
> > +     }
> > +
> > +     if (req_odr > 0) {
> > +             err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val);
> > +             if (err < 0)
> > +                     return err;
> > +     }
> >
> > -     reg = &st_lsm6dsx_odr_table[sensor->id].reg;
> > +     reg = &st_lsm6dsx_odr_table[ref_sensor->id].reg;
> >       data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
> >       return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
> >  }
> >
> > -int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
> > -{
> > -     int err;
> > -
> > -     err = st_lsm6dsx_set_odr(sensor, sensor->odr);
> > -     if (err < 0)
> > -             return err;
> > -
> > -     sensor->hw->enable_mask |= BIT(sensor->id);
> > -
> > -     return 0;
> > -}
> > -
> > -int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
> > +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
> > +                              bool enable)
> >  {
> >       struct st_lsm6dsx_hw *hw = sensor->hw;
> > -     const struct st_lsm6dsx_reg *reg;
> > -     unsigned int data;
> > +     u16 odr = enable ? sensor->odr : 0;
> >       int err;
> >
> > -     reg = &st_lsm6dsx_odr_table[sensor->id].reg;
> > -     data = ST_LSM6DSX_SHIFT_VAL(0, reg->mask);
> > -     err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
> > +     err = st_lsm6dsx_set_odr(sensor, odr);
> >       if (err < 0)
> >               return err;
> >
> > -     sensor->hw->enable_mask &= ~BIT(sensor->id);
> > +     if (enable)
> > +             hw->enable_mask |= BIT(sensor->id);
> > +     else
> > +             hw->enable_mask &= ~BIT(sensor->id);
> >
> >       return 0;
> >  }
> > @@ -516,7 +548,7 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
> >       int err, delay;
> >       __le16 data;
> >
> > -     err = st_lsm6dsx_sensor_enable(sensor);
> > +     err = st_lsm6dsx_sensor_set_enable(sensor, true);
> >       if (err < 0)
> >               return err;
> >
> > @@ -527,7 +559,7 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
> >       if (err < 0)
> >               return err;
> >
> > -     st_lsm6dsx_sensor_disable(sensor);
> > +     st_lsm6dsx_sensor_set_enable(sensor, false);
> >
> >       *val = (s16)le16_to_cpu(data);
> >
> > @@ -892,7 +924,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> >       if (err < 0)
> >               return err;
> >
> > -     for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > +     for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) {
> >               hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
> >               if (!hw->iio_devs[i])
> >                       return -ENOMEM;
> > @@ -909,6 +941,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> >       }
> >
> >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > +             if (!hw->iio_devs[i])
> > +                     continue;
> > +
> >               err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
> >               if (err)
> >                       return err;
> > @@ -927,6 +962,9 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
> >       int i, err = 0;
> >
> >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > +             if (!hw->iio_devs[i])
> > +                     continue;
> > +
> >               sensor = iio_priv(hw->iio_devs[i]);
> >               if (!(hw->enable_mask & BIT(sensor->id)))
> >                       continue;
> > @@ -952,6 +990,9 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev)
> >       int i, err = 0;
> >
> >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > +             if (!hw->iio_devs[i])
> > +                     continue;
> > +
> >               sensor = iio_priv(hw->iio_devs[i]);
> >               if (!(hw->enable_mask & BIT(sensor->id)))
> >                       continue;
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 6/7] iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
  2018-11-04 14:39 ` [PATCH 6/7] iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller Lorenzo Bianconi
@ 2018-11-04 17:54   ` Jonathan Cameron
  2018-11-04 18:14     ` Lorenzo Bianconi
  0 siblings, 1 reply; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 17:54 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun,  4 Nov 2018 15:39:05 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> Introduce hw FIFO support to lsm6dsx sensorhub. Current implementation
> use SLV0 for slave configuration and SLV{1,2,3} to read data and
> push them into the hw FIFO
So, the words 'Current Implementation' suggest you are already thinking
of other implementations?  Perhaps a note here on why you chose this
method, it's limitations etc and why you might want to change it in future?
(I'm guessing when you potentially have 4 slave devices...)

A few comments inline.

Jonathan
> 
> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> ---
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       |  4 +
>  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 86 ++++++++++++++-----
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  |  5 ++
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 76 ++++++++++++++++
>  4 files changed, 150 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> index d20746eb3d2d..d1d8d07a0714 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> @@ -130,18 +130,22 @@ struct st_lsm6dsx_hw_ts_settings {
>   * @master_en: master config register info (addr + mask).
>   * @pullup_en: i2c controller pull-up register info (addr + mask).
>   * @aux_sens: aux sensor register info (addr + mask).
> + * @wr_once: write_once register info (addr + mask).
>   * @shub_out: sensor hub first output register info.
>   * @slv0_addr: slave0 address in secondary page.
>   * @dw_slv0_addr: slave0 write register address in secondary page.
> + * @batch_en: Enable/disable FIFO batching.
>   */
>  struct st_lsm6dsx_shub_settings {
>  	struct st_lsm6dsx_reg page_mux;
>  	struct st_lsm6dsx_reg master_en;
>  	struct st_lsm6dsx_reg pullup_en;
>  	struct st_lsm6dsx_reg aux_sens;
> +	struct st_lsm6dsx_reg wr_once;
>  	u8 shub_out;
>  	u8 slv0_addr;
>  	u8 dw_slv0_addr;
> +	u8 batch_en;
>  };
>  
>  enum st_lsm6dsx_ext_sensor_id {
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> index 96f7d56d3b6d..d4e4fe54219f 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> @@ -68,6 +68,9 @@ enum st_lsm6dsx_fifo_tag {
>  	ST_LSM6DSX_GYRO_TAG = 0x01,
>  	ST_LSM6DSX_ACC_TAG = 0x02,
>  	ST_LSM6DSX_TS_TAG = 0x04,
> +	ST_LSM6DSX_EXT0_TAG = 0x0f,
> +	ST_LSM6DSX_EXT1_TAG = 0x10,
> +	ST_LSM6DSX_EXT2_TAG = 0x11,
>  };
>  
>  static const
> @@ -453,6 +456,52 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
>  	return read_len;
>  }
>  
> +static int
> +st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag,
> +			    u8 *data, s64 ts)
> +{
> +	struct st_lsm6dsx_sensor *sensor;
> +	struct iio_dev *iio_dev;
> +
> +	switch (tag) {
> +	case ST_LSM6DSX_GYRO_TAG:
> +		iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO];
> +		sensor = iio_priv(iio_dev);
> +		break;
> +	case ST_LSM6DSX_ACC_TAG:
> +		iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC];
> +		sensor = iio_priv(iio_dev);
> +		break;
> +	case ST_LSM6DSX_EXT0_TAG:
> +		if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0))
> +			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0];
> +		else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
> +			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
> +		else
> +			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];

I would suggest this is non obvious enough to deserve some comments.
I 'think' EXT0_TAG gets used for the first enabled additional channel?


> +		sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
Perhaps a comment that explains this is just to get the right timestamp
or maybe better just have ts_ref as the local variable then it's
more obvious.

> +		break;
> +	case ST_LSM6DSX_EXT1_TAG:
> +		if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
> +			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
Under my assumption above about first come first served if 1 and 2
are enabled but not 0, I think you will get the iio_dev for 1 for
both of them...

> +		else
> +			iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
> +		sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> +		break;
> +	case ST_LSM6DSX_EXT2_TAG:
> +		iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
> +		sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	iio_push_to_buffers_with_timestamp(iio_dev, data,
> +					   ts + sensor->ts_ref);
> +
> +	return 0;
> +}
> +
>  /**
>   * st_lsm6dsx_read_tagged_fifo() - LSM6DSO read FIFO routine
>   * @hw: Pointer to instance of struct st_lsm6dsx_hw.
> @@ -508,8 +557,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
>  			       ST_LSM6DSX_SAMPLE_SIZE);
>  
>  			tag = hw->buff[i] >> 3;
> -			switch (tag) {
> -			case ST_LSM6DSX_TS_TAG:
> +			if (tag == ST_LSM6DSX_TS_TAG) {
>  				/*
>  				 * hw timestamp is 4B long and it is stored
>  				 * in FIFO according to this schema:
> @@ -526,19 +574,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
>  				if (!reset_ts && ts >= 0xffff0000)
>  					reset_ts = true;
>  				ts *= ST_LSM6DSX_TS_SENSITIVITY;
> -				break;
> -			case ST_LSM6DSX_GYRO_TAG:
> -				iio_push_to_buffers_with_timestamp(
> -					hw->iio_devs[ST_LSM6DSX_ID_GYRO],
> -					iio_buff, gyro_sensor->ts_ref + ts);
> -				break;
> -			case ST_LSM6DSX_ACC_TAG:
> -				iio_push_to_buffers_with_timestamp(
> -					hw->iio_devs[ST_LSM6DSX_ID_ACC],
> -					iio_buff, acc_sensor->ts_ref + ts);
> -				break;
> -			default:
> -				break;

There would be a reasonable argument to be made in favour of doing this
factoring out as a precursor patch so that we have less noise in this one.

> +			} else {
> +				st_lsm6dsx_push_tagged_data(hw, tag, iio_buff,
> +							    ts);
>  			}
>  		}
>  	}
> @@ -574,13 +612,19 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
>  			goto out;
>  	}
>  
> -	err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> -	if (err < 0)
> -		goto out;
> +	if (sensor->id == ST_LSM6DSX_ID_EXT0 ||
> +	    sensor->id == ST_LSM6DSX_ID_EXT1 ||
> +	    sensor->id == ST_LSM6DSX_ID_EXT2) {
> +		st_lsm6dsx_shub_set_enable(sensor, enable);
Rather feels like this should ultimately be able to fail and should
have error handling?

> +	} else {
> +		err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> +		if (err < 0)
> +			goto out;
>  
> -	err = st_lsm6dsx_set_fifo_odr(sensor, enable);
> -	if (err < 0)
> -		goto out;
> +		err = st_lsm6dsx_set_fifo_odr(sensor, enable);
> +		if (err < 0)
> +			goto out;
> +	}
>  
>  	err = st_lsm6dsx_update_decimators(hw);
>  	if (err < 0)
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> index 463859f287da..5ec94f211905 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> @@ -337,9 +337,14 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
>  				.addr = 0x14,
>  				.mask = GENMASK(1, 0),
>  			},
> +			.wr_once = {
> +				.addr = 0x14,
> +				.mask = BIT(6),
> +			},
>  			.shub_out = 0x02,
>  			.slv0_addr = 0x15,
>  			.dw_slv0_addr = 0x21,
> +			.batch_en = BIT(3),
>  		}
>  	},
>  };
> diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> index b37f9dbdad17..d723b4adeeda 100644
> --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> @@ -148,6 +148,26 @@ static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr,
>  	return err;
>  }
>  
> +static int
> +st_lsm6dsx_shub_write_reg_with_mask(struct st_lsm6dsx_hw *hw, u8 addr,
> +				    u8 mask, u8 val)
> +{
> +	int err;
> +
> +	mutex_lock(&hw->page_lock);
> +	err = st_lsm6dsx_set_page(hw, true);
> +	if (err < 0)
> +		goto out;
> +
> +	err = regmap_update_bits(hw->regmap, addr, mask, val);
> +
> +	st_lsm6dsx_set_page(hw, false);

I wonder if long run we want to think about changing the driver
to change page based only on what the coming read is?  Thus only
change at transitions in which page we want.  I haven't even
glanced at the balance of reads / writes to the two pages
so this is pure conjecture.

> +out:
> +	mutex_unlock(&hw->page_lock);
> +
> +	return err;
> +}
> +
>  static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
>  					 bool enable)
>  {
> @@ -238,8 +258,21 @@ st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
>  	int err, i;
>  
>  	hub_settings = &hw->settings->shub_settings;
> +	if (hub_settings->wr_once.addr) {
> +		unsigned int data;
> +
> +		data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask);
> +		err = st_lsm6dsx_shub_write_reg_with_mask(hw,
> +			hub_settings->wr_once.addr,
> +			hub_settings->wr_once.mask,
> +			data);
> +		if (err < 0)
> +			return err;
> +	}
> +
>  	slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
>  	config[0] = sensor->ext_info.addr << 1;
> +
Ahah! caught you in an unrelated white space change ;)  Meh. In a series this
big there was sure to be one somewhere and it doesn't really matter that much.

>  	for (i = 0 ; i < len; i++) {
>  		config[1] = addr + i;
>  
> @@ -319,11 +352,54 @@ st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
>  					       val);
>  }
>  
> +/* use SLV{1,2,3} for FIFO read operations */
> +static int
> +st_lsm6dsx_shub_config_channels(struct st_lsm6dsx_sensor *sensor,
> +				bool enable)
> +{
> +	const struct st_lsm6dsx_shub_settings *hub_settings;
> +	const struct st_lsm6dsx_ext_dev_settings *settings;
> +	u8 config[9] = {}, enable_mask, slv_addr;
> +	struct st_lsm6dsx_hw *hw = sensor->hw;
> +	struct st_lsm6dsx_sensor *cur_sensor;
> +	int i, j = 0;
> +
> +	hub_settings = &hw->settings->shub_settings;
> +	if (enable)
> +		enable_mask = hw->enable_mask | BIT(sensor->id);
> +	else
> +		enable_mask = hw->enable_mask & ~BIT(sensor->id);
> +
> +	for (i = ST_LSM6DSX_ID_EXT0; i <= ST_LSM6DSX_ID_EXT2; i++) {
> +		if (!hw->iio_devs[i])
> +			continue;
> +
> +		cur_sensor = iio_priv(hw->iio_devs[i]);
> +		if (!(enable_mask & BIT(cur_sensor->id)))
> +			continue;
> +
> +		settings = cur_sensor->ext_info.settings;
> +		config[j] = (sensor->ext_info.addr << 1) | 1;
> +		config[j + 1] = settings->out.addr;
> +		config[j + 2] = (settings->out.len & ST_LS6DSX_READ_OP_MASK) |
> +				hub_settings->batch_en;
> +		j += 3;
> +	}
> +
> +	slv_addr = ST_LSM6DSX_SLV_ADDR(1, hub_settings->slv0_addr);
> +	return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> +					 sizeof(config));
> +}
> +
>  int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
>  {
>  	const struct st_lsm6dsx_ext_dev_settings *settings;
>  	int err;
>  
> +	err = st_lsm6dsx_shub_config_channels(sensor, enable);
> +	if (err < 0)
> +		return err;
> +
>  	settings = sensor->ext_info.settings;
>  	if (enable) {
>  		err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr);

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 5/7] iio: imu: st_lsm6dsx: add i2c embedded controller support
  2018-11-04 17:42   ` Jonathan Cameron
@ 2018-11-04 18:00     ` Lorenzo Bianconi
  2018-11-04 18:21       ` Jonathan Cameron
  0 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 18:00 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, devicetree

>
> On Sun,  4 Nov 2018 15:39:04 +0100
> Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
>
> > i2c controller embedded in lsm6dx series can connect up to four
> > slave devices using accelerometer sensor as trigger for i2c
> > read/write operations.
> > Introduce sensor hub support for lsm6dso sensor. Add register map
> > for lis2mdl magnetometer sensor
> >
> > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
>
> I'd completely forgotten how this driver did the multiple device registration
> so this had me completely confused initially ;)
>
> Perhaps just state here that the result is an entirely separate apparent device
> and what it's functionality is at this point in the patch set?
>

ack, will do in v2

> A few minor comments inline.  This looks like it is coming together quite
> nicely to me.
>

Thx :). I will address your comments in v2, just one question inline.
Regards,

Lorenzo

> Jonathan
>
> > ---
> >  drivers/iio/imu/st_lsm6dsx/Makefile           |   3 +-
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 112 +++
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 135 ++--
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 677 ++++++++++++++++++
> >  .../linux/platform_data/st_sensors_pdata.h    |   2 +
> >  5 files changed, 886 insertions(+), 43 deletions(-)
> >  create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> >
> > diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile
> > index 35919febea2a..e5f733ce6e11 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/Makefile
> > +++ b/drivers/iio/imu/st_lsm6dsx/Makefile
> > @@ -1,4 +1,5 @@
> > -st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
> > +st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o \
> > +             st_lsm6dsx_shub.o
> >
> >  obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
> >  obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > index 2beb4f563892..d20746eb3d2d 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > @@ -43,6 +43,24 @@ enum st_lsm6dsx_hw_id {
> >                                        * ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
> >  #define ST_LSM6DSX_SHIFT_VAL(val, mask)      (((val) << __ffs(mask)) & (mask))
> >
> > +#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx)           \
> > +{                                                                    \
> > +     .type = chan_type,                                              \
> > +     .address = addr,                                                \
> > +     .modified = 1,                                                  \
> > +     .channel2 = mod,                                                \
> > +     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |                  \
> > +                           BIT(IIO_CHAN_INFO_SCALE),                 \
> > +     .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),        \
> > +     .scan_index = scan_idx,                                         \
> > +     .scan_type = {                                                  \
> > +             .sign = 's',                                            \
> > +             .realbits = 16,                                         \
> > +             .storagebits = 16,                                      \
> > +             .endianness = IIO_LE,                                   \
> > +     },                                                              \
> > +}
> > +
> >  struct st_lsm6dsx_reg {
> >       u8 addr;
> >       u8 mask;
> > @@ -50,6 +68,28 @@ struct st_lsm6dsx_reg {
> >
> >  struct st_lsm6dsx_hw;
> >
> > +struct st_lsm6dsx_odr {
> > +     u16 hz;
> > +     u8 val;
> > +};
> > +
> > +#define ST_LSM6DSX_ODR_LIST_SIZE     6
> > +struct st_lsm6dsx_odr_table_entry {
> > +     struct st_lsm6dsx_reg reg;
> > +     struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
> > +};
> > +
> > +struct st_lsm6dsx_fs {
> > +     u32 gain;
> > +     u8 val;
> > +};
> > +
> > +#define ST_LSM6DSX_FS_LIST_SIZE              4
> > +struct st_lsm6dsx_fs_table_entry {
> > +     struct st_lsm6dsx_reg reg;
> > +     struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
> > +};
> > +
> >  /**
> >   * struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings
> >   * @read_fifo: Read FIFO callback.
> > @@ -84,6 +124,66 @@ struct st_lsm6dsx_hw_ts_settings {
> >       struct st_lsm6dsx_reg decimator;
> >  };
> >
> > +/**
> > + * struct st_lsm6dsx_shub_settings - ST IMU hw i2c controller settings
> > + * @page_mux: register page mux info (addr + mask).
> > + * @master_en: master config register info (addr + mask).
> > + * @pullup_en: i2c controller pull-up register info (addr + mask).
> > + * @aux_sens: aux sensor register info (addr + mask).
> > + * @shub_out: sensor hub first output register info.
> > + * @slv0_addr: slave0 address in secondary page.
> > + * @dw_slv0_addr: slave0 write register address in secondary page.
> > + */
> > +struct st_lsm6dsx_shub_settings {
> > +     struct st_lsm6dsx_reg page_mux;
> > +     struct st_lsm6dsx_reg master_en;
> > +     struct st_lsm6dsx_reg pullup_en;
> > +     struct st_lsm6dsx_reg aux_sens;
> > +     u8 shub_out;
> > +     u8 slv0_addr;
> > +     u8 dw_slv0_addr;
> > +};
> > +
> > +enum st_lsm6dsx_ext_sensor_id {
> > +     ST_LSM6DSX_ID_MAGN,
> > +};
> > +
> > +/**
> > + * struct st_lsm6dsx_ext_dev_settings - i2c controller slave settings
> > + * @i2c_addr: I2c slave address list.
> > + * @wai: Wai address info.
> > + * @id: external sensor id.
> > + * @odr: Output data rate of the sensor [Hz].
> > + * @gain: Configured sensor sensitivity.
> > + * @temp_comp: Temperature compensation register info (addr + mask).
> > + * @pwr_table: Power on register info (addr + mask).
> > + * @off_canc: Offset cancellation register info (addr + mask).
> > + * @bdu: Block data update register info (addr + mask).
> > + * @out: Output register info.
> > + */
> > +struct st_lsm6dsx_ext_dev_settings {
> > +     u8 i2c_addr[2];
> > +     struct {
> > +             u8 addr;
> > +             u8 val;
> > +     } wai;
> > +     enum st_lsm6dsx_ext_sensor_id id;
> > +     struct st_lsm6dsx_odr_table_entry odr_table;
> > +     struct st_lsm6dsx_fs_table_entry fs_table;
> > +     struct st_lsm6dsx_reg temp_comp;
> > +     struct {
> > +             struct st_lsm6dsx_reg reg;
> > +             u8 off_val;
> > +             u8 on_val;
> > +     } pwr_table;
> > +     struct st_lsm6dsx_reg off_canc;
> > +     struct st_lsm6dsx_reg bdu;
> > +     struct {
> > +             u8 addr;
> > +             u8 len;
> > +     } out;
> > +};
> > +
> >  /**
> >   * struct st_lsm6dsx_settings - ST IMU sensor settings
> >   * @wai: Sensor WhoAmI default value.
> > @@ -93,6 +193,7 @@ struct st_lsm6dsx_hw_ts_settings {
> >   * @batch: List of FIFO batching register info (addr + mask).
> >   * @fifo_ops: Sensor hw FIFO parameters.
> >   * @ts_settings: Hw timer related settings.
> > + * @shub_settings: i2c controller related settings.
> >   */
> >  struct st_lsm6dsx_settings {
> >       u8 wai;
> > @@ -102,6 +203,7 @@ struct st_lsm6dsx_settings {
> >       struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
> >       struct st_lsm6dsx_fifo_ops fifo_ops;
> >       struct st_lsm6dsx_hw_ts_settings ts_settings;
> > +     struct st_lsm6dsx_shub_settings shub_settings;
> >  };
> >
> >  enum st_lsm6dsx_sensor_id {
> > @@ -129,6 +231,7 @@ enum st_lsm6dsx_fifo_mode {
> >   * @sip: Number of samples in a given pattern.
> >   * @decimator: FIFO decimation factor.
> >   * @ts_ref: Sensor timestamp reference for hw one.
> > + * @ext_info: Sensor settings if it is connected to i2c controller
> >   */
> >  struct st_lsm6dsx_sensor {
> >       char name[32];
> > @@ -142,6 +245,11 @@ struct st_lsm6dsx_sensor {
> >       u8 sip;
> >       u8 decimator;
> >       s64 ts_ref;
> > +
> > +     struct {
> > +             const struct st_lsm6dsx_ext_dev_settings *settings;
> > +             u8 addr;
> > +     } ext_info;
> >  };
> >
> >  /**
> > @@ -181,6 +289,7 @@ struct st_lsm6dsx_hw {
> >       const struct st_lsm6dsx_settings *settings;
> >  };
> >
> > +static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
> >  extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
> >
> >  int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> > @@ -197,6 +306,9 @@ int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
> >  int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw);
> >  int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw);
> >  int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val);
> > +int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name);
> > +int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable);
> > +int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable);
> >
> >  static inline int
> >  st_lsm6dsx_update_bits_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > index f2549ddfee20..463859f287da 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > @@ -88,17 +88,6 @@
> >  #define ST_LSM6DSX_GYRO_FS_1000_GAIN         IIO_DEGREE_TO_RAD(35000)
> >  #define ST_LSM6DSX_GYRO_FS_2000_GAIN         IIO_DEGREE_TO_RAD(70000)
> >
> > -struct st_lsm6dsx_odr {
> > -     u16 hz;
> > -     u8 val;
> > -};
> > -
> > -#define ST_LSM6DSX_ODR_LIST_SIZE     6
> > -struct st_lsm6dsx_odr_table_entry {
> > -     struct st_lsm6dsx_reg reg;
> > -     struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
> > -};
> > -
> >  static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
> >       [ST_LSM6DSX_ID_ACC] = {
> >               .reg = {
> > @@ -126,17 +115,6 @@ static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
> >       }
> >  };
> >
> > -struct st_lsm6dsx_fs {
> > -     u32 gain;
> > -     u8 val;
> > -};
> > -
> > -#define ST_LSM6DSX_FS_LIST_SIZE              4
> > -struct st_lsm6dsx_fs_table_entry {
> > -     struct st_lsm6dsx_reg reg;
> > -     struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
> > -};
> > -
> >  static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
> >       [ST_LSM6DSX_ID_ACC] = {
> >               .reg = {
> > @@ -342,27 +320,30 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
> >                               .mask = GENMASK(7, 6),
> >                       },
> >               },
> > +             .shub_settings = {
> > +                     .page_mux = {
> > +                             .addr = 0x01,
> > +                             .mask = BIT(6),
> > +                     },
> > +                     .master_en = {
> > +                             .addr = 0x14,
> > +                             .mask = BIT(2),
> > +                     },
> > +                     .pullup_en = {
> > +                             .addr = 0x14,
> > +                             .mask = BIT(3),
> > +                     },
> > +                     .aux_sens = {
> > +                             .addr = 0x14,
> > +                             .mask = GENMASK(1, 0),
> > +                     },
> > +                     .shub_out = 0x02,
> > +                     .slv0_addr = 0x15,
> > +                     .dw_slv0_addr = 0x21,
> > +             }
> >       },
> >  };
> >
> > -#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx)           \
> > -{                                                                    \
> > -     .type = chan_type,                                              \
> > -     .address = addr,                                                \
> > -     .modified = 1,                                                  \
> > -     .channel2 = mod,                                                \
> > -     .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |                  \
> > -                           BIT(IIO_CHAN_INFO_SCALE),                 \
> > -     .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),        \
> > -     .scan_index = scan_idx,                                         \
> > -     .scan_type = {                                                  \
> > -             .sign = 's',                                            \
> > -             .realbits = 16,                                         \
> > -             .storagebits = 16,                                      \
> > -             .endianness = IIO_LE,                                   \
> > -     },                                                              \
> > -}
> > -
> >  static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
> >       ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
> >                          IIO_MOD_X, 0),
> > @@ -383,6 +364,21 @@ static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
> >       IIO_CHAN_SOFT_TIMESTAMP(3),
> >  };
> >
> > +int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable)
> > +{
> > +     const struct st_lsm6dsx_shub_settings *hub_settings;
> > +     unsigned int data;
> > +     int err;
> > +
> > +     hub_settings = &hw->settings->shub_settings;
> > +     data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->page_mux.mask);
> > +     err = regmap_update_bits(hw->regmap, hub_settings->page_mux.addr,
> > +                              hub_settings->page_mux.mask, data);
> > +     usleep_range(100, 150);
> > +
> > +     return err;
> > +}
> > +
> >  static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
> >  {
> >       int err, i, j, data;
> > @@ -728,8 +724,6 @@ static const struct iio_info st_lsm6dsx_gyro_info = {
> >       .hwfifo_set_watermark = st_lsm6dsx_set_watermark,
> >  };
> >
> > -static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
> > -
> >  static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
> >  {
> >       struct device_node *np = hw->dev->of_node;
> > @@ -768,6 +762,51 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
> >       return err;
> >  }
> >
> > +static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw)
> > +{
> > +     const struct st_lsm6dsx_shub_settings *hub_settings;
> > +     struct device_node *np = hw->dev->of_node;
> > +     struct st_sensors_platform_data *pdata;
> > +     unsigned int data;
> > +     int err = 0;
> > +
> > +     hub_settings = &hw->settings->shub_settings;
> > +
> > +     pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
> > +     if ((np && of_property_read_bool(np, "st,pullups")) ||
> > +         (pdata && pdata->pullups)) {
> > +             err = st_lsm6dsx_set_page(hw, true);
> > +             if (err < 0)
> > +                     return err;
> > +
> > +             data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->pullup_en.mask);
> > +             err = regmap_update_bits(hw->regmap,
> > +                                      hub_settings->pullup_en.addr,
> > +                                      hub_settings->pullup_en.mask, data);
> > +
> > +             st_lsm6dsx_set_page(hw, false);
> > +
> > +             if (err < 0)
> > +                     return err;
> > +     }
> > +
> > +     if (hub_settings->aux_sens.addr) {
> > +             /* configure aux sensors */
> > +             err = st_lsm6dsx_set_page(hw, true);
> > +             if (err < 0)
> > +                     return err;
> > +
> > +             data = ST_LSM6DSX_SHIFT_VAL(3, hub_settings->aux_sens.mask);
> > +             err = regmap_update_bits(hw->regmap,
> > +                                      hub_settings->aux_sens.addr,
> > +                                      hub_settings->aux_sens.mask, data);
> > +
> > +             st_lsm6dsx_set_page(hw, false);
> > +     }
> > +
> > +     return err;
> > +}
> > +
> >  static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
> >  {
> >       const struct st_lsm6dsx_hw_ts_settings *ts_settings;
> > @@ -846,6 +885,10 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
> >       if (err < 0)
> >               return err;
> >
> > +     err = st_lsm6dsx_init_shub(hw);
> > +     if (err < 0)
> > +             return err;
> > +
> >       return st_lsm6dsx_init_hw_timer(hw);
> >  }
> >
> > @@ -899,6 +942,7 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
> >  int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> >                    struct regmap *regmap)
> >  {
> > +     const struct st_lsm6dsx_shub_settings *hub_settings;
> >       struct st_lsm6dsx_hw *hw;
> >       int i, err;
> >
> > @@ -934,6 +978,13 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> >       if (err < 0)
> >               return err;
> >
> > +     hub_settings = &hw->settings->shub_settings;
> > +     if (hub_settings->master_en.addr) {
> > +             err = st_lsm6dsx_shub_probe(hw, name);
> > +             if (err < 0)
> > +                     return err;
> > +     }
> > +
> >       if (hw->irq > 0) {
> >               err = st_lsm6dsx_fifo_setup(hw);
> >               if (err < 0)
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> > new file mode 100644
> > index 000000000000..b37f9dbdad17
> > --- /dev/null
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> > @@ -0,0 +1,677 @@
> > +/*
> > + * STMicroelectronics st_lsm6dsx i2c controller driver
> > + *
> > + * i2c controller embedded in lsm6dx series can connect up to four
> > + * slave devices using accelerometer sensor as trigger for i2c
> > + * read/write operations. Current implementation use SLV0 for slave
> > + * configuration and SLV{1,2,3} to read data and push them into the
> > + * hw FIFO
> > + *
> > + * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
> > + *
> > + * Permission to use, copy, modify, and/or distribute this software for any
> > + * purpose with or without fee is hereby granted, provided that the above
> > + * copyright notice and this permission notice appear in all copies.
> > + *
> > + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
> > + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
> > + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
> > + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
> > + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
> > + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
> > + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
> > + *
> > + */
> > +#include <linux/module.h>
> > +#include <linux/regmap.h>
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/sysfs.h>
> > +#include <linux/bitfield.h>
> > +
> > +#include "st_lsm6dsx.h"
> > +
> > +#define ST_LSM6DSX_MAX_SLV_NUM                       3
> > +#define ST_LSM6DSX_SLV_ADDR(n, base)         ((base) + (n) * 3)
> > +#define ST_LSM6DSX_SLV_SUB_ADDR(n, base)     ((base) + 1 + (n) * 3)
> > +#define ST_LSM6DSX_SLV_CONFIG(n, base)               ((base) + 2 + (n) * 3)
> > +
> > +#define ST_LS6DSX_READ_OP_MASK                       GENMASK(2, 0)
> > +
> > +static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = {
> > +     /* LIS2MDL */
> > +     {
> > +             .i2c_addr = { 0x1e },
> > +             .wai = {
> > +                     .addr = 0x4f,
> > +                     .val = 0x40,
> > +             },
> > +             .id = ST_LSM6DSX_ID_MAGN,
> > +             .odr_table = {
> > +                     .reg = {
> > +                             .addr = 0x60,
> > +                             .mask = GENMASK(3, 2),
> > +                     },
> > +                     .odr_avl[0] = {  10, 0x0 },
> > +                     .odr_avl[1] = {  20, 0x1 },
> > +                     .odr_avl[2] = {  50, 0x2 },
> > +                     .odr_avl[3] = { 100, 0x3 },
> > +             },
> > +             .fs_table = {
> > +                     .fs_avl[0] = {
> > +                             .gain = 1500,
> > +                             .val = 0x0,
> > +                     }, /* 1500 uG/LSB */
> > +             },
> > +             .temp_comp = {
> > +                     .addr = 0x60,
> > +                     .mask = BIT(7),
> > +             },
> > +             .pwr_table = {
> > +                     .reg = {
> > +                             .addr = 0x60,
> > +                             .mask = GENMASK(1, 0),
> > +                     },
> > +                     .off_val = 0x2,
> > +                     .on_val = 0x0,
> > +             },
> > +             .off_canc = {
> > +                     .addr = 0x61,
> > +                     .mask = BIT(1),
> > +             },
> > +             .bdu = {
> > +                     .addr = 0x62,
> > +                     .mask = BIT(4),
> > +             },
> > +             .out = {
> > +                     .addr = 0x68,
> > +                     .len = 6,
> > +             },
> > +     },
> > +};
> > +
> > +static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw)
> > +{
> > +     struct st_lsm6dsx_sensor *sensor;
> > +
> > +     sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> > +     msleep((2000U / sensor->odr) + 1);
> > +}
> > +
> > +/**
> > + * st_lsm6dsx_shub_read_reg - read i2c controller register
> > + *
> > + * Read st_lsm6dsx i2c controller register
> > + */
> > +static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr,
> > +                                 u8 *data, int len)
> > +{
> > +     const struct st_lsm6dsx_shub_settings *hub_settings;
> > +     int err;
> > +
> > +     mutex_lock(&hw->page_lock);
> > +
> > +     hub_settings = &hw->settings->shub_settings;
> > +     err = st_lsm6dsx_set_page(hw, true);
> > +     if (err < 0)
> > +             goto out;
> > +
> > +     err = regmap_bulk_read(hw->regmap, addr, data, len);
> > +
> > +     st_lsm6dsx_set_page(hw, false);
> > +out:
> > +     mutex_unlock(&hw->page_lock);
> > +
> > +     return err;
> > +}
> > +
> > +/**
> > + * st_lsm6dsx_shub_write_reg - write i2c controller register
> > + *
> > + * Write st_lsm6dsx i2c controller register
> > + */
> > +static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr,
> > +                                  u8 *data, int len)
> > +{
> > +     int err;
> > +
> > +     mutex_lock(&hw->page_lock);
> > +     err = st_lsm6dsx_set_page(hw, true);
> > +     if (err < 0)
> > +             goto out;
> > +
> > +     err = regmap_bulk_write(hw->regmap, addr, data, len);
> > +
> > +     st_lsm6dsx_set_page(hw, false);
> > +out:
> > +     mutex_unlock(&hw->page_lock);
> > +
> > +     return err;
> > +}
> > +
> > +static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
> > +                                      bool enable)
> > +{
> > +     const struct st_lsm6dsx_shub_settings *hub_settings;
> > +     struct st_lsm6dsx_hw *hw = sensor->hw;
> > +     unsigned int data;
> > +     int err;
> > +
> > +     /* enable acc sensor as trigger */
> > +     err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> > +     if (err < 0)
> > +             return err;
> > +
> > +     mutex_lock(&hw->page_lock);
> > +
> > +     hub_settings = &hw->settings->shub_settings;
> > +     err = st_lsm6dsx_set_page(hw, true);
> > +     if (err < 0)
> > +             goto out;
> > +
> > +     data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask);
> > +     err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr,
> > +                              hub_settings->master_en.mask, data);
> > +
> > +     st_lsm6dsx_set_page(hw, false);
> > +out:
> > +     mutex_unlock(&hw->page_lock);
> > +
> > +     return err;
> > +}
> > +
> > +/**
> > + * st_lsm6dsx_shub_read - read data from slave device register
> > + *
> > + * Read data from slave device register. SLV0 is used for
> > + * one-shot read operation
> > + */
> > +static int
> > +st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr,
> > +                  u8 *data, int len)
> > +{
> > +     const struct st_lsm6dsx_shub_settings *hub_settings;
> > +     struct st_lsm6dsx_hw *hw = sensor->hw;
> > +     u8 config[3], slv_addr;
> > +     int err;
> > +
> > +     hub_settings = &hw->settings->shub_settings;
> > +     slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
> > +
> > +     config[0] = (sensor->ext_info.addr << 1) | 1;
> > +     config[1] = addr;
> > +     config[2] = len & ST_LS6DSX_READ_OP_MASK;
> > +
> > +     err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> > +                                     sizeof(config));
> > +     if (err < 0)
> > +             return err;
> > +
> > +     err = st_lsm6dsx_shub_master_enable(sensor, true);
> > +     if (err < 0)
> > +             return err;
> > +
> > +     st_lsm6dsx_shub_wait_complete(hw);
> > +
> > +     err = st_lsm6dsx_shub_read_reg(hw, hub_settings->shub_out, data,
> > +                                    len & ST_LS6DSX_READ_OP_MASK);
> > +
> > +     st_lsm6dsx_shub_master_enable(sensor, false);
> > +
> > +     memset(config, 0, sizeof(config));
> > +     return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> > +                                      sizeof(config));
> > +}
> > +
> > +/**
> > + * st_lsm6dsx_shub_write - write data to slave device register
> > + *
> > + * Write data from slave device register. SLV0 is used for
> > + * one-shot write operation
> > + */
> > +static int
> > +st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
> > +                   u8 *data, int len)
> > +{
> > +     const struct st_lsm6dsx_shub_settings *hub_settings;
> > +     struct st_lsm6dsx_hw *hw = sensor->hw;
> > +     u8 config[2], slv_addr;
> > +     int err, i;
> > +
> > +     hub_settings = &hw->settings->shub_settings;
> > +     slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
> > +     config[0] = sensor->ext_info.addr << 1;
> > +     for (i = 0 ; i < len; i++) {
> > +             config[1] = addr + i;
> > +
> > +             err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> > +                                             sizeof(config));
> > +             if (err < 0)
> > +                     return err;
> > +
> > +             err = st_lsm6dsx_shub_write_reg(hw, hub_settings->dw_slv0_addr,
> > +                                             &data[i], 1);
> > +             if (err < 0)
> > +                     return err;
> > +
> > +             err = st_lsm6dsx_shub_master_enable(sensor, true);
> > +             if (err < 0)
> > +                     return err;
> > +
> > +             st_lsm6dsx_shub_wait_complete(hw);
> > +
> > +             st_lsm6dsx_shub_master_enable(sensor, false);
> > +     }
> > +
> > +     memset(config, 0, sizeof(config));
> > +     return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config));
> > +}
> > +
> > +static int
> > +st_lsm6dsx_shub_write_with_mask(struct st_lsm6dsx_sensor *sensor,
> > +                             u8 addr, u8 mask, u8 val)
> > +{
> > +     int err;
> > +     u8 data;
> > +
> > +     err = st_lsm6dsx_shub_read(sensor, addr, &data, sizeof(data));
> > +     if (err < 0)
> > +             return err;
> > +
> > +     data = ((data & ~mask) | (val << __ffs(mask) & mask));
> > +
> > +     return st_lsm6dsx_shub_write(sensor, addr, &data, sizeof(data));
> > +}
> > +
> > +static int
> > +st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor,
> > +                         u16 odr, u16 *val)
> > +{
> > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > +     int i;
> > +
> > +     settings = sensor->ext_info.settings;
> > +     for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
> > +             if (settings->odr_table.odr_avl[i].hz == odr)
> > +                     break;
> > +
> > +     if (i == ST_LSM6DSX_ODR_LIST_SIZE)
> > +             return -EINVAL;
> > +
> > +     *val = settings->odr_table.odr_avl[i].val;
> > +     return 0;
> > +}
> > +
> > +static int
> > +st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
> > +{
> > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > +     u16 val;
> > +     int err;
> > +
> > +     err = st_lsm6dsx_shub_get_odr_val(sensor, odr, &val);
> > +     if (err < 0)
> > +             return err;
> > +
> > +     settings = sensor->ext_info.settings;
> > +     return st_lsm6dsx_shub_write_with_mask(sensor,
> > +                                            settings->odr_table.reg.addr,
> > +                                            settings->odr_table.reg.mask,
> > +                                            val);
> > +}
> > +
> > +int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
> > +{
> > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > +     int err;
> > +
> > +     settings = sensor->ext_info.settings;
> > +     if (enable) {
> > +             err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr);
> > +             if (err < 0)
> > +                     return err;
> > +     } else {
> > +             err = st_lsm6dsx_shub_write_with_mask(sensor,
> > +                                     settings->odr_table.reg.addr,
> > +                                     settings->odr_table.reg.mask, 0);
> > +             if (err < 0)
> > +                     return err;
> > +     }
> > +
> > +     if (settings->pwr_table.reg.addr) {
> > +             u8 val;
> > +
> > +             val = enable ? settings->pwr_table.on_val
> > +                          : settings->pwr_table.off_val;
> > +             err = st_lsm6dsx_shub_write_with_mask(sensor,
> > +                                     settings->pwr_table.reg.addr,
> > +                                     settings->pwr_table.reg.mask, val);
> > +             if (err < 0)
> > +                     return err;
> > +     }
> > +
> > +     return st_lsm6dsx_shub_master_enable(sensor, enable);
> > +}
> > +
> > +static int
> > +st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor,
> > +                          struct iio_chan_spec const *ch,
> > +                          int *val)
> > +{
> > +     int err, delay, len = ch->scan_type.realbits >> 3;
> > +     __le32 data;
> > +
> > +     err = st_lsm6dsx_shub_set_enable(sensor, true);
> > +     if (err < 0)
> > +             return err;
> > +
> > +     delay = 1000000 / sensor->odr;
> > +     usleep_range(delay, 2 * delay);
> > +
> > +     err = st_lsm6dsx_shub_read(sensor, ch->address, (u8 *)&data, len);
> > +     if (err < 0)
> > +             return err;
> > +
> > +     st_lsm6dsx_shub_set_enable(sensor, false);
> > +
> > +     switch (len) {
> > +     case 2:
> > +             *val = (s16)le16_to_cpu(data);
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     return IIO_VAL_INT;
> > +}
> > +
> > +static int
> > +st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev,
> > +                      struct iio_chan_spec const *ch,
> > +                      int *val, int *val2, long mask)
> > +{
> > +     struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
> > +     int ret;
> > +
> > +     switch (mask) {
> > +     case IIO_CHAN_INFO_RAW:
> > +             ret = iio_device_claim_direct_mode(iio_dev);
> > +             if (ret)
> > +                     break;
> > +
> > +             ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val);
> > +             iio_device_release_direct_mode(iio_dev);
> > +             break;
> > +     case IIO_CHAN_INFO_SAMP_FREQ:
> > +             *val = sensor->odr;
> > +             ret = IIO_VAL_INT;
> > +             break;
> > +     case IIO_CHAN_INFO_SCALE:
> > +             *val = 0;
> > +             *val2 = sensor->gain;
> > +             ret = IIO_VAL_INT_PLUS_MICRO;
> > +             break;
> > +     default:
> > +             ret = -EINVAL;
> > +             break;
> > +     }
> > +
> > +     return ret;
> > +}
> > +
> > +static int
> > +st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev,
> > +                       struct iio_chan_spec const *chan,
> > +                       int val, int val2, long mask)
> > +{
> > +     struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
> > +     int err;
> > +
> > +     err = iio_device_claim_direct_mode(iio_dev);
> > +     if (err)
> > +             return err;
> > +
> > +     switch (mask) {
> > +     case IIO_CHAN_INFO_SAMP_FREQ: {
> > +             u16 data;
> > +
> > +             err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data);
> > +             if (!err)
> > +                     sensor->odr = val;
> > +             break;
> > +     }
> > +     default:
> > +             err = -EINVAL;
> > +             break;
> > +     }
> > +
> > +     iio_device_release_direct_mode(iio_dev);
> > +
> > +     return err;
> > +}
> > +
> > +static ssize_t
> > +st_lsm6dsx_shub_sampling_freq_avail(struct device *dev,
> > +                                 struct device_attribute *attr,
> > +                                 char *buf)
> > +{
> > +     struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
> > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > +     int i, len = 0;
> > +
> > +     settings = sensor->ext_info.settings;
> > +     for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) {
> > +             u16 val = settings->odr_table.odr_avl[i].hz;
> > +
> > +             if (val > 0)
> > +                     len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
> > +                                      val);
> > +     }
> > +     buf[len - 1] = '\n';
> > +
> > +     return len;
> > +}
> > +
> > +static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev,
> > +                                        struct device_attribute *attr,
> > +                                        char *buf)
> > +{
> > +     struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
> > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > +     int i, len = 0;
> > +
> > +     settings = sensor->ext_info.settings;
> > +     for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) {
> > +             u16 val = settings->fs_table.fs_avl[i].gain;
> > +
> > +             if (val > 0)
> > +                     len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
> > +                                      val);
> > +     }
> > +     buf[len - 1] = '\n';
> > +
> > +     return len;
> > +}
> > +
> > +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail);
> > +static IIO_DEVICE_ATTR(in_ext_scale_available, 0444,
> > +                    st_lsm6dsx_shub_scale_avail, NULL, 0);
> > +static struct attribute *st_lsm6dsx_ext_attributes[] = {
> > +     &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> > +     &iio_dev_attr_in_ext_scale_available.dev_attr.attr,
>
> What's this abi element?
>

what do you mean here? I did not get you sorry :)

> > +     NULL,
> > +};
> > +
> > +static const struct attribute_group st_lsm6dsx_ext_attribute_group = {
> > +     .attrs = st_lsm6dsx_ext_attributes,
> > +};
> > +
> > +static const struct iio_info st_lsm6dsx_ext_info = {
> > +     .attrs = &st_lsm6dsx_ext_attribute_group,
> > +     .read_raw = st_lsm6dsx_shub_read_raw,
> > +     .write_raw = st_lsm6dsx_shub_write_raw,
> > +     .hwfifo_set_watermark = st_lsm6dsx_set_watermark,
> > +};
> > +
> > +static struct iio_dev *
> > +st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw,
> > +                          enum st_lsm6dsx_sensor_id id,
> > +                          const struct st_lsm6dsx_ext_dev_settings *info,
> > +                          u8 i2c_addr, const char *name)
> > +{
> > +     struct iio_chan_spec *ext_channels;
> > +     struct st_lsm6dsx_sensor *sensor;
> > +     struct iio_dev *iio_dev;
> > +
> > +     iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
> > +     if (!iio_dev)
> > +             return NULL;
> > +
> > +     iio_dev->modes = INDIO_DIRECT_MODE;
> > +     iio_dev->dev.parent = hw->dev;
> > +     iio_dev->info = &st_lsm6dsx_ext_info;
> > +
> > +     sensor = iio_priv(iio_dev);
> > +     sensor->id = id;
> > +     sensor->hw = hw;
> > +     sensor->odr = info->odr_table.odr_avl[0].hz;
> > +     sensor->gain = info->fs_table.fs_avl[0].gain;
> > +     sensor->ext_info.settings = info;
> > +     sensor->ext_info.addr = i2c_addr;
> > +     sensor->watermark = 1;
> > +
> > +     switch (info->id) {
> > +     case ST_LSM6DSX_ID_MAGN: {
> > +             const struct iio_chan_spec magn_channels[] = {
> > +                     ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr,
> > +                                        IIO_MOD_X, 0),
> > +                     ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 2,
> > +                                        IIO_MOD_Y, 1),
> > +                     ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 4,
> > +                                        IIO_MOD_Z, 2),
> > +                     IIO_CHAN_SOFT_TIMESTAMP(3),
> > +             };
> > +
> > +             ext_channels = devm_kzalloc(hw->dev, sizeof(magn_channels),
> > +                                         GFP_KERNEL);
> > +             if (!ext_channels)
> > +                     return NULL;
> > +
> > +             memcpy(ext_channels, magn_channels, sizeof(magn_channels));
> > +             iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
> > +             iio_dev->channels = ext_channels;
> > +             iio_dev->num_channels = ARRAY_SIZE(magn_channels);
> > +
> > +             scnprintf(sensor->name, sizeof(sensor->name), "%s_magn",
> > +                       name);
> > +             break;
> > +     }
> > +     default:
> > +             return NULL;
> > +     }
> > +     iio_dev->name = sensor->name;
> > +
> > +     return iio_dev;
> > +}
> > +
> > +static int st_lsm6dsx_shub_init_device(struct st_lsm6dsx_sensor *sensor)
> > +{
> > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > +     int err = 0;
> > +
> > +     settings = sensor->ext_info.settings;
> > +     if (settings->bdu.addr) {
> > +             err = st_lsm6dsx_shub_write_with_mask(sensor,
> > +                                                   settings->bdu.addr,
> > +                                                   settings->bdu.mask, 1);
> > +             if (err < 0)
> > +                     return err;
> > +     }
> > +
> > +     if (settings->temp_comp.addr) {
> > +             err = st_lsm6dsx_shub_write_with_mask(sensor,
> > +                                     settings->temp_comp.addr,
> > +                                     settings->temp_comp.mask, 1);
> > +             if (err < 0)
> > +                     return err;
> > +     }
> > +
> > +     if (settings->off_canc.addr)
> > +             err = st_lsm6dsx_shub_write_with_mask(sensor,
> > +                                     settings->off_canc.addr,
> > +                                     settings->off_canc.mask, 1);
> Return directly from this last command and return 0 perhaps below?
>
> That drops the need to initialize err as well.
>
> Doesn't matter though if you prefer it this way!
> > +
> > +     return err;
> > +}
> > +
> > +int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name)
> > +{
> > +     const struct st_lsm6dsx_shub_settings *hub_settings;
> > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > +     enum st_lsm6dsx_sensor_id id = ST_LSM6DSX_ID_EXT0;
> > +     struct st_lsm6dsx_sensor *acc_sensor, *sensor;
> > +     int err, i, j, num_ext_dev = 0;
> > +     u8 config[3], data, slv_addr;
> > +
> > +     acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> > +     hub_settings = &hw->settings->shub_settings;
> > +     slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
> > +
> > +     while (i < ARRAY_SIZE(st_lsm6dsx_ext_dev_table) &&
> > +            num_ext_dev < ST_LSM6DSX_MAX_SLV_NUM) {
> > +             settings = &st_lsm6dsx_ext_dev_table[i];
> Might be better as a for loop as then you'll have the loop
> increment nice and obvious up here. Perhaps just break separately
> for the num_ext_dev rather than having it as the loop condition?
>
>
> > +
> > +             for (j = 0; j < ARRAY_SIZE(settings->i2c_addr); j++) {
> This is getting very deeply nested and perhaps worth pulling out to a utility
> function?  I haven't checked just how many parameters that would have
> though so perhaps not...
>
> Could also reduce the scope of a few variables such as config by
> pulling it in the for loop I think.
>
> > +                     if (!settings->i2c_addr[j])
> > +                             continue;
> > +
> > +                     /* read wai slave register */
> > +                     config[0] = (settings->i2c_addr[j] << 1) | 0x1;
> > +                     config[1] = settings->wai.addr;
> > +                     config[2] = 0x1;
> > +
> > +                     err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> > +                                                     sizeof(config));
> > +                     if (err < 0)
> > +                             return err;
> > +
> > +                     err = st_lsm6dsx_shub_master_enable(acc_sensor, true);
> > +                     if (err < 0)
> > +                             return err;
> > +
> > +                     st_lsm6dsx_shub_wait_complete(hw);
> > +
> > +                     err = st_lsm6dsx_shub_read_reg(hw,
> > +                                                    hub_settings->shub_out,
> > +                                                    &data, sizeof(data));
> > +
> > +                     st_lsm6dsx_shub_master_enable(acc_sensor, false);
> > +
> > +                     if (err < 0)
> > +                             return err;
> > +
> > +                     if (data != settings->wai.val)
> > +                             continue;
> > +
> > +                     hw->iio_devs[id] = st_lsm6dsx_shub_alloc_iiodev(hw,
> > +                                                     id, settings,
> > +                                                     settings->i2c_addr[j],
> > +                                                     name);
> > +                     if (!hw->iio_devs[id])
> > +                             return -ENOMEM;
> > +
> > +                     sensor = iio_priv(hw->iio_devs[id]);
> > +                     err = st_lsm6dsx_shub_init_device(sensor);
> > +                     if (err < 0)
> > +                             return err;
> > +
> > +                     num_ext_dev++;
> > +                     id++;
> > +                     break;
> > +             }
> > +             i++;
> > +     }
> > +
> > +     memset(config, 0, sizeof(config));
> > +     return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> > +                                      sizeof(config));
> > +}
> > diff --git a/include/linux/platform_data/st_sensors_pdata.h b/include/linux/platform_data/st_sensors_pdata.h
> > index f8274b0c6888..728193111c2f 100644
> > --- a/include/linux/platform_data/st_sensors_pdata.h
> > +++ b/include/linux/platform_data/st_sensors_pdata.h
> > @@ -18,11 +18,13 @@
> >   *   Accelerometer DRDY on LSM330 available only on pin 1 (see datasheet).
> >   * @open_drain: set the interrupt line to be open drain if possible.
> >   * @spi_3wire: enable spi-3wire mode.
> > + * @pullups: enable/disable i2c controller pullup resistors.
> >   */
> >  struct st_sensors_platform_data {
> >       u8 drdy_int_pin;
> >       bool open_drain;
> >       bool spi_3wire;
> > +     bool pullups;
> >  };
> >
> >  #endif /* ST_SENSORS_PDATA_H */
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/7] add i2c controller support to st_lsm6dsx driver
  2018-11-04 14:38 [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Lorenzo Bianconi
                   ` (6 preceding siblings ...)
  2018-11-04 14:39 ` [PATCH 7/7] dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors Lorenzo Bianconi
@ 2018-11-04 18:12 ` Jonathan Cameron
  2018-11-04 18:27   ` Lorenzo Bianconi
  7 siblings, 1 reply; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 18:12 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun,  4 Nov 2018 15:38:59 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> Introduce i2c controller support to st_lsm6dsx driver for lsm6dso sensor.
> Add register map for lis2mdl magnetometer sensor.
> Add hw FIFO support to st_lsm6dsx sensorhub driver.
> 
> Lorenzo Bianconi (7):
>   iio: imu: st_lsm6dsx: introduce locked read/write utility routines
>   iio: imu: st_lsm6dsx: reboot memory content after reset
>   iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark
>   iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
>   iio: imu: st_lsm6dsx: add i2c embedded controller support
>   iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
>   dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors
> 
>  .../bindings/iio/imu/st_lsm6dsx.txt           |   1 +
>  drivers/iio/imu/st_lsm6dsx/Makefile           |   3 +-
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 167 +++-
>  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 169 ++--
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 273 +++++--
>  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 753 ++++++++++++++++++
>  .../linux/platform_data/st_sensors_pdata.h    |   2 +
>  7 files changed, 1226 insertions(+), 142 deletions(-)
>  create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> 

Hi Lorenzo,

Great to see this series.

It seems to have come together fairly nicely given the inherent complexity
of dealing with these slave i2c controllers.   What I would like to
see in this cover letter though is a brief description of what can be
put behind these devices?

The one thing I want to be careful of is ending up with lots of sort
of replicated drivers where we have a version in each sensor hub
and one for directly connected devices.  Superficially this
device seems to have a very restricted I2C master so that might not
be a significant problem.  What do you think?
This gets particularly interesting if we think about switching in
and out of pass through.  At that point this looks like an i2c offload
engine (sort of) similar to the one Lars did for spi, be it a very
restricted one.

Jonathan

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 6/7] iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
  2018-11-04 17:54   ` Jonathan Cameron
@ 2018-11-04 18:14     ` Lorenzo Bianconi
  2018-11-04 18:31       ` Jonathan Cameron
  0 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 18:14 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, devicetree

>
> On Sun,  4 Nov 2018 15:39:05 +0100
> Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
>
> > Introduce hw FIFO support to lsm6dsx sensorhub. Current implementation
> > use SLV0 for slave configuration and SLV{1,2,3} to read data and
> > push them into the hw FIFO
> So, the words 'Current Implementation' suggest you are already thinking
> of other implementations?  Perhaps a note here on why you chose this
> method, it's limitations etc and why you might want to change it in future?
> (I'm guessing when you potentially have 4 slave devices...)

ack, will do in v2

>
> A few comments inline.
>
> Jonathan
> >
> > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> > ---
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       |  4 +
> >  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 86 ++++++++++++++-----
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  |  5 ++
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 76 ++++++++++++++++
> >  4 files changed, 150 insertions(+), 21 deletions(-)
> >
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > index d20746eb3d2d..d1d8d07a0714 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > @@ -130,18 +130,22 @@ struct st_lsm6dsx_hw_ts_settings {
> >   * @master_en: master config register info (addr + mask).
> >   * @pullup_en: i2c controller pull-up register info (addr + mask).
> >   * @aux_sens: aux sensor register info (addr + mask).
> > + * @wr_once: write_once register info (addr + mask).
> >   * @shub_out: sensor hub first output register info.
> >   * @slv0_addr: slave0 address in secondary page.
> >   * @dw_slv0_addr: slave0 write register address in secondary page.
> > + * @batch_en: Enable/disable FIFO batching.
> >   */
> >  struct st_lsm6dsx_shub_settings {
> >       struct st_lsm6dsx_reg page_mux;
> >       struct st_lsm6dsx_reg master_en;
> >       struct st_lsm6dsx_reg pullup_en;
> >       struct st_lsm6dsx_reg aux_sens;
> > +     struct st_lsm6dsx_reg wr_once;
> >       u8 shub_out;
> >       u8 slv0_addr;
> >       u8 dw_slv0_addr;
> > +     u8 batch_en;
> >  };
> >
> >  enum st_lsm6dsx_ext_sensor_id {
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > index 96f7d56d3b6d..d4e4fe54219f 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > @@ -68,6 +68,9 @@ enum st_lsm6dsx_fifo_tag {
> >       ST_LSM6DSX_GYRO_TAG = 0x01,
> >       ST_LSM6DSX_ACC_TAG = 0x02,
> >       ST_LSM6DSX_TS_TAG = 0x04,
> > +     ST_LSM6DSX_EXT0_TAG = 0x0f,
> > +     ST_LSM6DSX_EXT1_TAG = 0x10,
> > +     ST_LSM6DSX_EXT2_TAG = 0x11,
> >  };
> >
> >  static const
> > @@ -453,6 +456,52 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
> >       return read_len;
> >  }
> >
> > +static int
> > +st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag,
> > +                         u8 *data, s64 ts)
> > +{
> > +     struct st_lsm6dsx_sensor *sensor;
> > +     struct iio_dev *iio_dev;
> > +
> > +     switch (tag) {
> > +     case ST_LSM6DSX_GYRO_TAG:
> > +             iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO];
> > +             sensor = iio_priv(iio_dev);
> > +             break;
> > +     case ST_LSM6DSX_ACC_TAG:
> > +             iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC];
> > +             sensor = iio_priv(iio_dev);
> > +             break;
> > +     case ST_LSM6DSX_EXT0_TAG:
> > +             if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0))
> > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0];
> > +             else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
> > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
> > +             else
> > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
>
> I would suggest this is non obvious enough to deserve some comments.
> I 'think' EXT0_TAG gets used for the first enabled additional channel?

EXT0_TAG will be used for the first enabled sensor, so if we enable
channel 2 and 3 (SLV2 and SLV3; first 'data' channel is 1 since 0 is
used just for configuration), we will receive data with TAG0 and TAG1
There is no relation between tags used and i2c slave channels IIRC

>
>
> > +             sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> Perhaps a comment that explains this is just to get the right timestamp
> or maybe better just have ts_ref as the local variable then it's
> more obvious.
>
> > +             break;
> > +     case ST_LSM6DSX_EXT1_TAG:
> > +             if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
> > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
> Under my assumption above about first come first served if 1 and 2
> are enabled but not 0, I think you will get the iio_dev for 1 for
> both of them...
>
> > +             else
> > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
> > +             sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> > +             break;
> > +     case ST_LSM6DSX_EXT2_TAG:
> > +             iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
> > +             sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> > +             break;
> > +     default:
> > +             return -EINVAL;
> > +     }
> > +
> > +     iio_push_to_buffers_with_timestamp(iio_dev, data,
> > +                                        ts + sensor->ts_ref);
> > +
> > +     return 0;
> > +}
> > +
> >  /**
> >   * st_lsm6dsx_read_tagged_fifo() - LSM6DSO read FIFO routine
> >   * @hw: Pointer to instance of struct st_lsm6dsx_hw.
> > @@ -508,8 +557,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
> >                              ST_LSM6DSX_SAMPLE_SIZE);
> >
> >                       tag = hw->buff[i] >> 3;
> > -                     switch (tag) {
> > -                     case ST_LSM6DSX_TS_TAG:
> > +                     if (tag == ST_LSM6DSX_TS_TAG) {
> >                               /*
> >                                * hw timestamp is 4B long and it is stored
> >                                * in FIFO according to this schema:
> > @@ -526,19 +574,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
> >                               if (!reset_ts && ts >= 0xffff0000)
> >                                       reset_ts = true;
> >                               ts *= ST_LSM6DSX_TS_SENSITIVITY;
> > -                             break;
> > -                     case ST_LSM6DSX_GYRO_TAG:
> > -                             iio_push_to_buffers_with_timestamp(
> > -                                     hw->iio_devs[ST_LSM6DSX_ID_GYRO],
> > -                                     iio_buff, gyro_sensor->ts_ref + ts);
> > -                             break;
> > -                     case ST_LSM6DSX_ACC_TAG:
> > -                             iio_push_to_buffers_with_timestamp(
> > -                                     hw->iio_devs[ST_LSM6DSX_ID_ACC],
> > -                                     iio_buff, acc_sensor->ts_ref + ts);
> > -                             break;
> > -                     default:
> > -                             break;
>
> There would be a reasonable argument to be made in favour of doing this
> factoring out as a precursor patch so that we have less noise in this one.
>

ack, I will do in a separated patch

> > +                     } else {
> > +                             st_lsm6dsx_push_tagged_data(hw, tag, iio_buff,
> > +                                                         ts);
> >                       }
> >               }
> >       }
> > @@ -574,13 +612,19 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
> >                       goto out;
> >       }
> >
> > -     err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> > -     if (err < 0)
> > -             goto out;
> > +     if (sensor->id == ST_LSM6DSX_ID_EXT0 ||
> > +         sensor->id == ST_LSM6DSX_ID_EXT1 ||
> > +         sensor->id == ST_LSM6DSX_ID_EXT2) {
> > +             st_lsm6dsx_shub_set_enable(sensor, enable);
> Rather feels like this should ultimately be able to fail and should
> have error handling?

correct :) I will fix in v2

>
> > +     } else {
> > +             err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> > +             if (err < 0)
> > +                     goto out;
> >
> > -     err = st_lsm6dsx_set_fifo_odr(sensor, enable);
> > -     if (err < 0)
> > -             goto out;
> > +             err = st_lsm6dsx_set_fifo_odr(sensor, enable);
> > +             if (err < 0)
> > +                     goto out;
> > +     }
> >
> >       err = st_lsm6dsx_update_decimators(hw);
> >       if (err < 0)
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > index 463859f287da..5ec94f211905 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > @@ -337,9 +337,14 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
> >                               .addr = 0x14,
> >                               .mask = GENMASK(1, 0),
> >                       },
> > +                     .wr_once = {
> > +                             .addr = 0x14,
> > +                             .mask = BIT(6),
> > +                     },
> >                       .shub_out = 0x02,
> >                       .slv0_addr = 0x15,
> >                       .dw_slv0_addr = 0x21,
> > +                     .batch_en = BIT(3),
> >               }
> >       },
> >  };
> > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> > index b37f9dbdad17..d723b4adeeda 100644
> > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> > @@ -148,6 +148,26 @@ static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr,
> >       return err;
> >  }
> >
> > +static int
> > +st_lsm6dsx_shub_write_reg_with_mask(struct st_lsm6dsx_hw *hw, u8 addr,
> > +                                 u8 mask, u8 val)
> > +{
> > +     int err;
> > +
> > +     mutex_lock(&hw->page_lock);
> > +     err = st_lsm6dsx_set_page(hw, true);
> > +     if (err < 0)
> > +             goto out;
> > +
> > +     err = regmap_update_bits(hw->regmap, addr, mask, val);
> > +
> > +     st_lsm6dsx_set_page(hw, false);
>
> I wonder if long run we want to think about changing the driver
> to change page based only on what the coming read is?  Thus only
> change at transitions in which page we want.  I haven't even
> glanced at the balance of reads / writes to the two pages
> so this is pure conjecture.
>
> > +out:
> > +     mutex_unlock(&hw->page_lock);
> > +
> > +     return err;
> > +}
> > +
> >  static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
> >                                        bool enable)
> >  {
> > @@ -238,8 +258,21 @@ st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
> >       int err, i;
> >
> >       hub_settings = &hw->settings->shub_settings;
> > +     if (hub_settings->wr_once.addr) {
> > +             unsigned int data;
> > +
> > +             data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask);
> > +             err = st_lsm6dsx_shub_write_reg_with_mask(hw,
> > +                     hub_settings->wr_once.addr,
> > +                     hub_settings->wr_once.mask,
> > +                     data);
> > +             if (err < 0)
> > +                     return err;
> > +     }
> > +
> >       slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
> >       config[0] = sensor->ext_info.addr << 1;
> > +
> Ahah! caught you in an unrelated white space change ;)  Meh. In a series this
> big there was sure to be one somewhere and it doesn't really matter that much.

:) will fix it in v2
Thx a lot for the fast review

Regards,
Lorenzo

>
> >       for (i = 0 ; i < len; i++) {
> >               config[1] = addr + i;
> >
> > @@ -319,11 +352,54 @@ st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
> >                                              val);
> >  }
> >
> > +/* use SLV{1,2,3} for FIFO read operations */
> > +static int
> > +st_lsm6dsx_shub_config_channels(struct st_lsm6dsx_sensor *sensor,
> > +                             bool enable)
> > +{
> > +     const struct st_lsm6dsx_shub_settings *hub_settings;
> > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > +     u8 config[9] = {}, enable_mask, slv_addr;
> > +     struct st_lsm6dsx_hw *hw = sensor->hw;
> > +     struct st_lsm6dsx_sensor *cur_sensor;
> > +     int i, j = 0;
> > +
> > +     hub_settings = &hw->settings->shub_settings;
> > +     if (enable)
> > +             enable_mask = hw->enable_mask | BIT(sensor->id);
> > +     else
> > +             enable_mask = hw->enable_mask & ~BIT(sensor->id);
> > +
> > +     for (i = ST_LSM6DSX_ID_EXT0; i <= ST_LSM6DSX_ID_EXT2; i++) {
> > +             if (!hw->iio_devs[i])
> > +                     continue;
> > +
> > +             cur_sensor = iio_priv(hw->iio_devs[i]);
> > +             if (!(enable_mask & BIT(cur_sensor->id)))
> > +                     continue;
> > +
> > +             settings = cur_sensor->ext_info.settings;
> > +             config[j] = (sensor->ext_info.addr << 1) | 1;
> > +             config[j + 1] = settings->out.addr;
> > +             config[j + 2] = (settings->out.len & ST_LS6DSX_READ_OP_MASK) |
> > +                             hub_settings->batch_en;
> > +             j += 3;
> > +     }
> > +
> > +     slv_addr = ST_LSM6DSX_SLV_ADDR(1, hub_settings->slv0_addr);
> > +     return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
> > +                                      sizeof(config));
> > +}
> > +
> >  int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
> >  {
> >       const struct st_lsm6dsx_ext_dev_settings *settings;
> >       int err;
> >
> > +     err = st_lsm6dsx_shub_config_channels(sensor, enable);
> > +     if (err < 0)
> > +             return err;
> > +
> >       settings = sensor->ext_info.settings;
> >       if (enable) {
> >               err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr);
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 4/7] iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
  2018-11-04 17:47     ` Lorenzo Bianconi
@ 2018-11-04 18:18       ` Jonathan Cameron
  0 siblings, 0 replies; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 18:18 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun, 4 Nov 2018 18:47:04 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> >
> > On Sun,  4 Nov 2018 15:39:03 +0100
> > Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
> >  
> > > Add ST_LSM6DSX_ID_EXT{0,1,2} sensor ids as reference for slave devices
> > > connected to st_lsm6dsx i2c controller. Moreover introduce odr dependency
> > > between accel and ext devices since accelerometer is used as trigger for
> > > i2c master controller read/write operations
> > >
> > > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>  
> >
> > Hi Lorenzo,
> >
> > There looks to be an unrelated cleanup in here around set_enable so
> > please pull that out as a separate patch.
> >
> > Also, it seems the odr dependency is not as simple as they should be
> > the same?  Perhaps you could add more here on what that dependency is?
> >  
> 
> I added the odr change in this patch since external sensors need to
> use accelerometer
> device as trigger for i2c operations, in other words the accelerometer
> sensor needs to
> be powered up in order to enable i2c master (I was thinking to a
> logical dependency here :)).
> Anyway I am fine to add just ext_id definitions in this patch and add
> odr dependency in a
> separate patch.

I was mostly just looking for some more explanation of the contraint
in the description rather than any splitting of the ODR change
and the ext_id part.

(the enable stuff does want to be a separate patch however!)

> Just one comment inline.
> 
> Regards,
> Lorenzo
> 
> 
> > Thanks,
> >
> > Jonathan
> >  
> > > ---
> > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       |   9 +-
> > >  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    |  27 +++--
> > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 101 ++++++++++++------
> > >  3 files changed, 95 insertions(+), 42 deletions(-)
> > >
> > > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > > index ac4cbbb0b3fb..2beb4f563892 100644
> > > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
> > > @@ -105,8 +105,11 @@ struct st_lsm6dsx_settings {
> > >  };
> > >
> > >  enum st_lsm6dsx_sensor_id {
> > > -     ST_LSM6DSX_ID_ACC,
> > >       ST_LSM6DSX_ID_GYRO,
> > > +     ST_LSM6DSX_ID_ACC,
> > > +     ST_LSM6DSX_ID_EXT0,
> > > +     ST_LSM6DSX_ID_EXT1,
> > > +     ST_LSM6DSX_ID_EXT2,
> > >       ST_LSM6DSX_ID_MAX,
> > >  };
> > >
> > > @@ -182,8 +185,8 @@ extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
> > >
> > >  int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> > >                    struct regmap *regmap);
> > > -int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
> > > -int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
> > > +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
> > > +                              bool enable);  
> > Unrelated change?
> >  
> > >  int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
> > >  int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val);
> > >  int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
> > > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > > index 4b3ba0956b5a..96f7d56d3b6d 100644
> > > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
> > > @@ -102,6 +102,9 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
> > >
> > >       *max_odr = 0, *min_odr = ~0;
> > >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > +             if (!hw->iio_devs[i])
> > > +                     continue;
> > > +
> > >               sensor = iio_priv(hw->iio_devs[i]);
> > >
> > >               if (!(hw->enable_mask & BIT(sensor->id)))
> > > @@ -125,6 +128,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
> > >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > >               const struct st_lsm6dsx_reg *dec_reg;
> > >
> > > +             if (!hw->iio_devs[i])
> > > +                     continue;
> > > +
> > >               sensor = iio_priv(hw->iio_devs[i]);
> > >               /* update fifo decimators and sample in pattern */
> > >               if (hw->enable_mask & BIT(sensor->id)) {
> > > @@ -232,6 +238,9 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
> > >               return 0;
> > >
> > >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > +             if (!hw->iio_devs[i])
> > > +                     continue;
> > > +
> > >               cur_sensor = iio_priv(hw->iio_devs[i]);
> > >
> > >               if (!(hw->enable_mask & BIT(cur_sensor->id)))
> > > @@ -278,6 +287,9 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
> > >               return err;
> > >
> > >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > +             if (!hw->iio_devs[i])
> > > +                     continue;
> > > +
> > >               sensor = iio_priv(hw->iio_devs[i]);
> > >               /*
> > >                * store enable buffer timestamp as reference for
> > > @@ -562,15 +574,9 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
> > >                       goto out;
> > >       }
> > >
> > > -     if (enable) {
> > > -             err = st_lsm6dsx_sensor_enable(sensor);
> > > -             if (err < 0)
> > > -                     goto out;
> > > -     } else {
> > > -             err = st_lsm6dsx_sensor_disable(sensor);
> > > -             if (err < 0)
> > > -                     goto out;
> > > -     }
> > > +     err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> > > +     if (err < 0)
> > > +             goto out;  
> >
> > Another block of unrelated.
> >  
> > >
> > >       err = st_lsm6dsx_set_fifo_odr(sensor, enable);
> > >       if (err < 0)
> > > @@ -690,6 +696,9 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
> > >       }
> > >
> > >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > +             if (!hw->iio_devs[i])
> > > +                     continue;
> > > +
> > >               buffer = devm_iio_kfifo_allocate(hw->dev);
> > >               if (!buffer)
> > >                       return -ENOMEM;
> > > diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > > index 3433a5b6bf4d..f2549ddfee20 100644
> > > --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > > +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
> > > @@ -450,7 +450,7 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
> > >       int i;
> > >
> > >       for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
> > > -             if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz == odr)
> > > +             if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz >= odr)  
> >
> > Not sure why this change from the description...  
> 
> here st_lsm6dsx_odr_table is for lsm6dso accel sensor, while odr can
> be from external sensors and
> they can differ (e.g. lsm6dso accel device and lis2mdl conncted to i2c
> controller)

ah, I found the bit in the datasheet after sending this which says it is capped
by the lowest of the on chip devices so this makes more sense.

> 
> >  
> > >                       break;
> > >
> > >       if (i == ST_LSM6DSX_ODR_LIST_SIZE)
> > > @@ -461,50 +461,82 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
> > >       return 0;
> > >  }
> > >
> > > -static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
> > > +static u16 st_lsm6dsx_check_odr_dependency(struct st_lsm6dsx_hw *hw, u16 odr,
> > > +                                        enum st_lsm6dsx_sensor_id id)
> > >  {
> > > +     struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]);
> > > +     u16 ret;
> > > +
> > > +     if (odr > 0) {
> > > +             if (hw->enable_mask & BIT(id))
> > > +                     ret = max_t(u16, ref->odr, odr);
> > > +             else
> > > +                     ret = odr;
> > > +     } else {
> > > +             ret = (hw->enable_mask & BIT(id)) ? ref->odr : 0;
> > > +     }
> > > +     return ret;  
> >
> > Cleaner just to return at all the places you set ret?  
> 
> ack fine, will do in v2
> 
> >  
> > > +}
> > > +
> > > +static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 req_odr)
> > > +{
> > > +     struct st_lsm6dsx_sensor *ref_sensor = sensor;
> > >       struct st_lsm6dsx_hw *hw = sensor->hw;
> > >       const struct st_lsm6dsx_reg *reg;
> > >       unsigned int data;
> > > +     u8 val = 0;
> > >       int err;
> > > -     u8 val;
> > >
> > > -     err = st_lsm6dsx_check_odr(sensor, odr, &val);
> > > -     if (err < 0)
> > > -             return err;
> > > +     switch (sensor->id) {
> > > +     case ST_LSM6DSX_ID_EXT0:
> > > +     case ST_LSM6DSX_ID_EXT1:
> > > +     case ST_LSM6DSX_ID_EXT2:
> > > +     case ST_LSM6DSX_ID_ACC: {
> > > +             u16 odr;
> > > +             int i;
> > > +
> > > +             /* use acc as trigger for ext devices */
> > > +             ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> > > +             for (i = ST_LSM6DSX_ID_ACC; i < ST_LSM6DSX_ID_MAX; i++) {
> > > +                     if (!hw->iio_devs[i] || i == sensor->id)
> > > +                             continue;
> > > +                     odr = st_lsm6dsx_check_odr_dependency(hw, req_odr, i);
> > > +                     if (odr != req_odr)
> > > +                             /* device already configured */
> > > +                             return 0;
> > > +             }
> > > +             break;
> > > +     }
> > > +     default:
> > > +             break;
> > > +     }
> > > +
> > > +     if (req_odr > 0) {
> > > +             err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val);
> > > +             if (err < 0)
> > > +                     return err;
> > > +     }
> > >
> > > -     reg = &st_lsm6dsx_odr_table[sensor->id].reg;
> > > +     reg = &st_lsm6dsx_odr_table[ref_sensor->id].reg;
> > >       data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
> > >       return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
> > >  }
> > >
> > > -int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
> > > -{
> > > -     int err;
> > > -
> > > -     err = st_lsm6dsx_set_odr(sensor, sensor->odr);
> > > -     if (err < 0)
> > > -             return err;
> > > -
> > > -     sensor->hw->enable_mask |= BIT(sensor->id);
> > > -
> > > -     return 0;
> > > -}
> > > -
> > > -int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
> > > +int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
> > > +                              bool enable)
> > >  {
> > >       struct st_lsm6dsx_hw *hw = sensor->hw;
> > > -     const struct st_lsm6dsx_reg *reg;
> > > -     unsigned int data;
> > > +     u16 odr = enable ? sensor->odr : 0;
> > >       int err;
> > >
> > > -     reg = &st_lsm6dsx_odr_table[sensor->id].reg;
> > > -     data = ST_LSM6DSX_SHIFT_VAL(0, reg->mask);
> > > -     err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
> > > +     err = st_lsm6dsx_set_odr(sensor, odr);
> > >       if (err < 0)
> > >               return err;
> > >
> > > -     sensor->hw->enable_mask &= ~BIT(sensor->id);
> > > +     if (enable)
> > > +             hw->enable_mask |= BIT(sensor->id);
> > > +     else
> > > +             hw->enable_mask &= ~BIT(sensor->id);
> > >
> > >       return 0;
> > >  }
> > > @@ -516,7 +548,7 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
> > >       int err, delay;
> > >       __le16 data;
> > >
> > > -     err = st_lsm6dsx_sensor_enable(sensor);
> > > +     err = st_lsm6dsx_sensor_set_enable(sensor, true);
> > >       if (err < 0)
> > >               return err;
> > >
> > > @@ -527,7 +559,7 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
> > >       if (err < 0)
> > >               return err;
> > >
> > > -     st_lsm6dsx_sensor_disable(sensor);
> > > +     st_lsm6dsx_sensor_set_enable(sensor, false);
> > >
> > >       *val = (s16)le16_to_cpu(data);
> > >
> > > @@ -892,7 +924,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> > >       if (err < 0)
> > >               return err;
> > >
> > > -     for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > +     for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) {
> > >               hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
> > >               if (!hw->iio_devs[i])
> > >                       return -ENOMEM;
> > > @@ -909,6 +941,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
> > >       }
> > >
> > >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > +             if (!hw->iio_devs[i])
> > > +                     continue;
> > > +
> > >               err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
> > >               if (err)
> > >                       return err;
> > > @@ -927,6 +962,9 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
> > >       int i, err = 0;
> > >
> > >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > +             if (!hw->iio_devs[i])
> > > +                     continue;
> > > +
> > >               sensor = iio_priv(hw->iio_devs[i]);
> > >               if (!(hw->enable_mask & BIT(sensor->id)))
> > >                       continue;
> > > @@ -952,6 +990,9 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev)
> > >       int i, err = 0;
> > >
> > >       for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
> > > +             if (!hw->iio_devs[i])
> > > +                     continue;
> > > +
> > >               sensor = iio_priv(hw->iio_devs[i]);
> > >               if (!(hw->enable_mask & BIT(sensor->id)))
> > >                       continue;  
> >  

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 5/7] iio: imu: st_lsm6dsx: add i2c embedded controller support
  2018-11-04 18:00     ` Lorenzo Bianconi
@ 2018-11-04 18:21       ` Jonathan Cameron
  2018-11-04 18:29         ` Lorenzo Bianconi
  0 siblings, 1 reply; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 18:21 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun, 4 Nov 2018 19:00:39 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> >
> > On Sun,  4 Nov 2018 15:39:04 +0100
> > Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
> >  
> > > i2c controller embedded in lsm6dx series can connect up to four
> > > slave devices using accelerometer sensor as trigger for i2c
> > > read/write operations.
> > > Introduce sensor hub support for lsm6dso sensor. Add register map
> > > for lis2mdl magnetometer sensor
> > >
> > > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>  
> >
> > I'd completely forgotten how this driver did the multiple device registration
> > so this had me completely confused initially ;)
> >
> > Perhaps just state here that the result is an entirely separate apparent device
> > and what it's functionality is at this point in the patch set?
> >  
> 
> ack, will do in v2
> 
> > A few minor comments inline.  This looks like it is coming together quite
> > nicely to me.
> >  
> 
> Thx :). I will address your comments in v2, just one question inline.
> Regards,
> 
> Lorenzo
> 
> > Jonathan
...
> > > +
> > > +static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev,
> > > +                                        struct device_attribute *attr,
> > > +                                        char *buf)
> > > +{
> > > +     struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
> > > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > > +     int i, len = 0;
> > > +
> > > +     settings = sensor->ext_info.settings;
> > > +     for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) {
> > > +             u16 val = settings->fs_table.fs_avl[i].gain;
> > > +
> > > +             if (val > 0)
> > > +                     len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
> > > +                                      val);
> > > +     }
> > > +     buf[len - 1] = '\n';
> > > +
> > > +     return len;
> > > +}
> > > +
> > > +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail);
> > > +static IIO_DEVICE_ATTR(in_ext_scale_available, 0444,
> > > +                    st_lsm6dsx_shub_scale_avail, NULL, 0);
> > > +static struct attribute *st_lsm6dsx_ext_attributes[] = {
> > > +     &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> > > +     &iio_dev_attr_in_ext_scale_available.dev_attr.attr,  
> >
> > What's this abi element?
> >  
> 
> what do you mean here? I did not get you sorry :)
> 
Seems to be creating a sysfs file called
in_ext_scale_available which I don't think is documented anywhere?

Intent was probably in_scale_available?  Or maybe in_magn_scale_available?

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/7] add i2c controller support to st_lsm6dsx driver
  2018-11-04 18:12 ` [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Jonathan Cameron
@ 2018-11-04 18:27   ` Lorenzo Bianconi
  2018-11-04 18:34     ` Jonathan Cameron
  0 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 18:27 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, devicetree

>
> On Sun,  4 Nov 2018 15:38:59 +0100
> Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
>
> > Introduce i2c controller support to st_lsm6dsx driver for lsm6dso sensor.
> > Add register map for lis2mdl magnetometer sensor.
> > Add hw FIFO support to st_lsm6dsx sensorhub driver.
> >
> > Lorenzo Bianconi (7):
> >   iio: imu: st_lsm6dsx: introduce locked read/write utility routines
> >   iio: imu: st_lsm6dsx: reboot memory content after reset
> >   iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark
> >   iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
> >   iio: imu: st_lsm6dsx: add i2c embedded controller support
> >   iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
> >   dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors
> >
> >  .../bindings/iio/imu/st_lsm6dsx.txt           |   1 +
> >  drivers/iio/imu/st_lsm6dsx/Makefile           |   3 +-
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 167 +++-
> >  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 169 ++--
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 273 +++++--
> >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 753 ++++++++++++++++++
> >  .../linux/platform_data/st_sensors_pdata.h    |   2 +
> >  7 files changed, 1226 insertions(+), 142 deletions(-)
> >  create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> >
>
> Hi Lorenzo,
>
> Great to see this series.

Hi Jonathan,

thx a lot for the fast review :) I will fix your comments in a v2

>
> It seems to have come together fairly nicely given the inherent complexity
> of dealing with these slave i2c controllers.   What I would like to
> see in this cover letter though is a brief description of what can be
> put behind these devices?
>
> The one thing I want to be careful of is ending up with lots of sort
> of replicated drivers where we have a version in each sensor hub
> and one for directly connected devices.  Superficially this
> device seems to have a very restricted I2C master so that might not
> be a significant problem.  What do you think?
> This gets particularly interesting if we think about switching in
> and out of pass through.  At that point this looks like an i2c offload
> engine (sort of) similar to the one Lars did for spi, be it a very
> restricted one.

I think the main purpose of the i2c controller embedded in lsm6dso is
to connect a magnetometer sensor
to it and get in this way a 9axis device (please note this is just my opinion).
However we can think to connect other devices like temperature or
pressure sensors (I tested them in the past :)).
I think we will never use it as a 'general purpose' contoller, e.g. to
 connect an adc :)

Regards,
Lorenzo

>
> Jonathan

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 5/7] iio: imu: st_lsm6dsx: add i2c embedded controller support
  2018-11-04 18:21       ` Jonathan Cameron
@ 2018-11-04 18:29         ` Lorenzo Bianconi
  0 siblings, 0 replies; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 18:29 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, devicetree

>
> On Sun, 4 Nov 2018 19:00:39 +0100
> Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
>
> > >
> > > On Sun,  4 Nov 2018 15:39:04 +0100
> > > Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
> > >
> > > > i2c controller embedded in lsm6dx series can connect up to four
> > > > slave devices using accelerometer sensor as trigger for i2c
> > > > read/write operations.
> > > > Introduce sensor hub support for lsm6dso sensor. Add register map
> > > > for lis2mdl magnetometer sensor
> > > >
> > > > Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@redhat.com>
> > >
> > > I'd completely forgotten how this driver did the multiple device registration
> > > so this had me completely confused initially ;)
> > >
> > > Perhaps just state here that the result is an entirely separate apparent device
> > > and what it's functionality is at this point in the patch set?
> > >
> >
> > ack, will do in v2
> >
> > > A few minor comments inline.  This looks like it is coming together quite
> > > nicely to me.
> > >
> >
> > Thx :). I will address your comments in v2, just one question inline.
> > Regards,
> >
> > Lorenzo
> >
> > > Jonathan
> ...
> > > > +
> > > > +static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev,
> > > > +                                        struct device_attribute *attr,
> > > > +                                        char *buf)
> > > > +{
> > > > +     struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
> > > > +     const struct st_lsm6dsx_ext_dev_settings *settings;
> > > > +     int i, len = 0;
> > > > +
> > > > +     settings = sensor->ext_info.settings;
> > > > +     for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) {
> > > > +             u16 val = settings->fs_table.fs_avl[i].gain;
> > > > +
> > > > +             if (val > 0)
> > > > +                     len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
> > > > +                                      val);
> > > > +     }
> > > > +     buf[len - 1] = '\n';
> > > > +
> > > > +     return len;
> > > > +}
> > > > +
> > > > +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail);
> > > > +static IIO_DEVICE_ATTR(in_ext_scale_available, 0444,
> > > > +                    st_lsm6dsx_shub_scale_avail, NULL, 0);
> > > > +static struct attribute *st_lsm6dsx_ext_attributes[] = {
> > > > +     &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
> > > > +     &iio_dev_attr_in_ext_scale_available.dev_attr.attr,
> > >
> > > What's this abi element?
> > >
> >
> > what do you mean here? I did not get you sorry :)
> >
> Seems to be creating a sysfs file called
> in_ext_scale_available which I don't think is documented anywhere?
>
> Intent was probably in_scale_available?  Or maybe in_magn_scale_available?
>

ack, got it. Thx for the explanation. I will fix in v2

Regards,
Lorenzo

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 6/7] iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
  2018-11-04 18:14     ` Lorenzo Bianconi
@ 2018-11-04 18:31       ` Jonathan Cameron
  2018-11-04 18:56         ` Lorenzo Bianconi
  0 siblings, 1 reply; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 18:31 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun, 4 Nov 2018 19:14:22 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> >
> > On Sun,  4 Nov 2018 15:39:05 +0100
> > Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
> >  
> > > Introduce hw FIFO support to lsm6dsx sensorhub. Current implementation
> > > use SLV0 for slave configuration and SLV{1,2,3} to read data and
> > > push them into the hw FIFO  
> > So, the words 'Current Implementation' suggest you are already thinking
> > of other implementations?  Perhaps a note here on why you chose this
> > method, it's limitations etc and why you might want to change it in future?
> > (I'm guessing when you potentially have 4 slave devices...)  
> 
> ack, will do in v2
> 
Great.  
> > > +static int
> > > +st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag,
> > > +                         u8 *data, s64 ts)
> > > +{
> > > +     struct st_lsm6dsx_sensor *sensor;
> > > +     struct iio_dev *iio_dev;
> > > +
> > > +     switch (tag) {
> > > +     case ST_LSM6DSX_GYRO_TAG:
> > > +             iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO];
> > > +             sensor = iio_priv(iio_dev);
> > > +             break;
> > > +     case ST_LSM6DSX_ACC_TAG:
> > > +             iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC];
> > > +             sensor = iio_priv(iio_dev);
> > > +             break;
> > > +     case ST_LSM6DSX_EXT0_TAG:
> > > +             if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0))
> > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0];
> > > +             else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
> > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
> > > +             else
> > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];  
> >
> > I would suggest this is non obvious enough to deserve some comments.
> > I 'think' EXT0_TAG gets used for the first enabled additional channel?  
> 
> EXT0_TAG will be used for the first enabled sensor, so if we enable
> channel 2 and 3 (SLV2 and SLV3; first 'data' channel is 1 since 0 is
> used just for configuration), we will receive data with TAG0 and TAG1
> There is no relation between tags used and i2c slave channels IIRC

Great, add that comment to v2.

> 
> >
> >  
> > > +             sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);  
> > Perhaps a comment that explains this is just to get the right timestamp
> > or maybe better just have ts_ref as the local variable then it's
> > more obvious.
> >  
> > > +             break;
> > > +     case ST_LSM6DSX_EXT1_TAG:
> > > +             if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
> > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];  
> > Under my assumption above about first come first served if 1 and 2
> > are enabled but not 0, I think you will get the iio_dev for 1 for
> > both of them...
I 'think' my query still holds here..
> >  
> > > +             else
> > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
> > > +             sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> > > +             break;
...
> > > @@ -574,13 +612,19 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
> > >                       goto out;
> > >       }
> > >
> > > -     err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> > > -     if (err < 0)
> > > -             goto out;
> > > +     if (sensor->id == ST_LSM6DSX_ID_EXT0 ||
> > > +         sensor->id == ST_LSM6DSX_ID_EXT1 ||
> > > +         sensor->id == ST_LSM6DSX_ID_EXT2) {
> > > +             st_lsm6dsx_shub_set_enable(sensor, enable);  
> > Rather feels like this should ultimately be able to fail and should
> > have error handling?  
> 
> correct :) I will fix in v2
> 
> >  
> > > +     } else {
> > > +             err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> > > +             if (err < 0)
> > > +                     goto out;
...
> > > +out:
> > > +     mutex_unlock(&hw->page_lock);
> > > +
> > > +     return err;
> > > +}
> > > +
> > >  static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
> > >                                        bool enable)
> > >  {
> > > @@ -238,8 +258,21 @@ st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
> > >       int err, i;
> > >
> > >       hub_settings = &hw->settings->shub_settings;
> > > +     if (hub_settings->wr_once.addr) {
> > > +             unsigned int data;
> > > +
> > > +             data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask);
> > > +             err = st_lsm6dsx_shub_write_reg_with_mask(hw,
> > > +                     hub_settings->wr_once.addr,
> > > +                     hub_settings->wr_once.mask,
> > > +                     data);
> > > +             if (err < 0)
> > > +                     return err;
> > > +     }
> > > +
> > >       slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
> > >       config[0] = sensor->ext_info.addr << 1;
> > > +  
> > Ahah! caught you in an unrelated white space change ;)  Meh. In a series this
> > big there was sure to be one somewhere and it doesn't really matter that much.  
> 
> :) will fix it in v2
> Thx a lot for the fast review
You are welcome.  A rare thing from me, but I just happened to be catching up
today.

> 
> Regards,
> Lorenzo
> 

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/7] add i2c controller support to st_lsm6dsx driver
  2018-11-04 18:27   ` Lorenzo Bianconi
@ 2018-11-04 18:34     ` Jonathan Cameron
  2018-11-04 19:07       ` Lorenzo Bianconi
  0 siblings, 1 reply; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-04 18:34 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun, 4 Nov 2018 19:27:42 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> >
> > On Sun,  4 Nov 2018 15:38:59 +0100
> > Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
> >  
> > > Introduce i2c controller support to st_lsm6dsx driver for lsm6dso sensor.
> > > Add register map for lis2mdl magnetometer sensor.
> > > Add hw FIFO support to st_lsm6dsx sensorhub driver.
> > >
> > > Lorenzo Bianconi (7):
> > >   iio: imu: st_lsm6dsx: introduce locked read/write utility routines
> > >   iio: imu: st_lsm6dsx: reboot memory content after reset
> > >   iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark
> > >   iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
> > >   iio: imu: st_lsm6dsx: add i2c embedded controller support
> > >   iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
> > >   dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors
> > >
> > >  .../bindings/iio/imu/st_lsm6dsx.txt           |   1 +
> > >  drivers/iio/imu/st_lsm6dsx/Makefile           |   3 +-
> > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 167 +++-
> > >  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 169 ++--
> > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 273 +++++--
> > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 753 ++++++++++++++++++
> > >  .../linux/platform_data/st_sensors_pdata.h    |   2 +
> > >  7 files changed, 1226 insertions(+), 142 deletions(-)
> > >  create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> > >  
> >
> > Hi Lorenzo,
> >
> > Great to see this series.  
> 
> Hi Jonathan,
> 
> thx a lot for the fast review :) I will fix your comments in a v2
> 
> >
> > It seems to have come together fairly nicely given the inherent complexity
> > of dealing with these slave i2c controllers.   What I would like to
> > see in this cover letter though is a brief description of what can be
> > put behind these devices?
> >
> > The one thing I want to be careful of is ending up with lots of sort
> > of replicated drivers where we have a version in each sensor hub
> > and one for directly connected devices.  Superficially this
> > device seems to have a very restricted I2C master so that might not
> > be a significant problem.  What do you think?
> > This gets particularly interesting if we think about switching in
> > and out of pass through.  At that point this looks like an i2c offload
> > engine (sort of) similar to the one Lars did for spi, be it a very
> > restricted one.  
> 
> I think the main purpose of the i2c controller embedded in lsm6dso is
> to connect a magnetometer sensor
> to it and get in this way a 9axis device (please note this is just my opinion).
> However we can think to connect other devices like temperature or
> pressure sensors (I tested them in the past :)).
> I think we will never use it as a 'general purpose' contoller, e.g. to
>  connect an adc :)

Hmm. Lets keep an eye on what gets added. If it gets silly we can revisit how
to handle things.  Particularly if we get things that can't be so easily
probed.  Of course, we may find no one ever adds anything else and that
would be fine :)

Jonathan
> 
> Regards,
> Lorenzo
> 
> >
> > Jonathan  

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 6/7] iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
  2018-11-04 18:31       ` Jonathan Cameron
@ 2018-11-04 18:56         ` Lorenzo Bianconi
  0 siblings, 0 replies; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 18:56 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, devicetree

On Sun, Nov 4, 2018 at 7:31 PM Jonathan Cameron <jic23@kernel.org> wrote:
>
> On Sun, 4 Nov 2018 19:14:22 +0100
> Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
>
> > >
> > > On Sun,  4 Nov 2018 15:39:05 +0100
> > > Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
> > >
> > > > Introduce hw FIFO support to lsm6dsx sensorhub. Current implementation
> > > > use SLV0 for slave configuration and SLV{1,2,3} to read data and
> > > > push them into the hw FIFO
> > > So, the words 'Current Implementation' suggest you are already thinking
> > > of other implementations?  Perhaps a note here on why you chose this
> > > method, it's limitations etc and why you might want to change it in future?
> > > (I'm guessing when you potentially have 4 slave devices...)
> >
> > ack, will do in v2
> >
> Great.
> > > > +static int
> > > > +st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag,
> > > > +                         u8 *data, s64 ts)
> > > > +{
> > > > +     struct st_lsm6dsx_sensor *sensor;
> > > > +     struct iio_dev *iio_dev;
> > > > +
> > > > +     switch (tag) {
> > > > +     case ST_LSM6DSX_GYRO_TAG:
> > > > +             iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO];
> > > > +             sensor = iio_priv(iio_dev);
> > > > +             break;
> > > > +     case ST_LSM6DSX_ACC_TAG:
> > > > +             iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC];
> > > > +             sensor = iio_priv(iio_dev);
> > > > +             break;
> > > > +     case ST_LSM6DSX_EXT0_TAG:
> > > > +             if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0))
> > > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0];
> > > > +             else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
> > > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
> > > > +             else
> > > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
> > >
> > > I would suggest this is non obvious enough to deserve some comments.
> > > I 'think' EXT0_TAG gets used for the first enabled additional channel?
> >
> > EXT0_TAG will be used for the first enabled sensor, so if we enable
> > channel 2 and 3 (SLV2 and SLV3; first 'data' channel is 1 since 0 is
> > used just for configuration), we will receive data with TAG0 and TAG1
> > There is no relation between tags used and i2c slave channels IIRC
>
> Great, add that comment to v2.
>
> >
> > >
> > >
> > > > +             sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> > > Perhaps a comment that explains this is just to get the right timestamp
> > > or maybe better just have ts_ref as the local variable then it's
> > > more obvious.
> > >
> > > > +             break;
> > > > +     case ST_LSM6DSX_EXT1_TAG:
> > > > +             if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
> > > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
> > > Under my assumption above about first come first served if 1 and 2
> > > are enabled but not 0, I think you will get the iio_dev for 1 for
> > > both of them...
> I 'think' my query still holds here..

correct :) I will fix in v2

> > >
> > > > +             else
> > > > +                     iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
> > > > +             sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
> > > > +             break;
> ...
> > > > @@ -574,13 +612,19 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
> > > >                       goto out;
> > > >       }
> > > >
> > > > -     err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> > > > -     if (err < 0)
> > > > -             goto out;
> > > > +     if (sensor->id == ST_LSM6DSX_ID_EXT0 ||
> > > > +         sensor->id == ST_LSM6DSX_ID_EXT1 ||
> > > > +         sensor->id == ST_LSM6DSX_ID_EXT2) {
> > > > +             st_lsm6dsx_shub_set_enable(sensor, enable);
> > > Rather feels like this should ultimately be able to fail and should
> > > have error handling?
> >
> > correct :) I will fix in v2
> >
> > >
> > > > +     } else {
> > > > +             err = st_lsm6dsx_sensor_set_enable(sensor, enable);
> > > > +             if (err < 0)
> > > > +                     goto out;
> ...
> > > > +out:
> > > > +     mutex_unlock(&hw->page_lock);
> > > > +
> > > > +     return err;
> > > > +}
> > > > +
> > > >  static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
> > > >                                        bool enable)
> > > >  {
> > > > @@ -238,8 +258,21 @@ st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
> > > >       int err, i;
> > > >
> > > >       hub_settings = &hw->settings->shub_settings;
> > > > +     if (hub_settings->wr_once.addr) {
> > > > +             unsigned int data;
> > > > +
> > > > +             data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask);
> > > > +             err = st_lsm6dsx_shub_write_reg_with_mask(hw,
> > > > +                     hub_settings->wr_once.addr,
> > > > +                     hub_settings->wr_once.mask,
> > > > +                     data);
> > > > +             if (err < 0)
> > > > +                     return err;
> > > > +     }
> > > > +
> > > >       slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
> > > >       config[0] = sensor->ext_info.addr << 1;
> > > > +
> > > Ahah! caught you in an unrelated white space change ;)  Meh. In a series this
> > > big there was sure to be one somewhere and it doesn't really matter that much.
> >
> > :) will fix it in v2
> > Thx a lot for the fast review
> You are welcome.  A rare thing from me, but I just happened to be catching up
> today.
>
> >
> > Regards,
> > Lorenzo
> >

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/7] add i2c controller support to st_lsm6dsx driver
  2018-11-04 18:34     ` Jonathan Cameron
@ 2018-11-04 19:07       ` Lorenzo Bianconi
  2018-11-11 14:33         ` Jonathan Cameron
  0 siblings, 1 reply; 27+ messages in thread
From: Lorenzo Bianconi @ 2018-11-04 19:07 UTC (permalink / raw)
  To: Jonathan Cameron; +Cc: linux-iio, devicetree

>
> On Sun, 4 Nov 2018 19:27:42 +0100
> Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
>
> > >
> > > On Sun,  4 Nov 2018 15:38:59 +0100
> > > Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
> > >
> > > > Introduce i2c controller support to st_lsm6dsx driver for lsm6dso sensor.
> > > > Add register map for lis2mdl magnetometer sensor.
> > > > Add hw FIFO support to st_lsm6dsx sensorhub driver.
> > > >
> > > > Lorenzo Bianconi (7):
> > > >   iio: imu: st_lsm6dsx: introduce locked read/write utility routines
> > > >   iio: imu: st_lsm6dsx: reboot memory content after reset
> > > >   iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark
> > > >   iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
> > > >   iio: imu: st_lsm6dsx: add i2c embedded controller support
> > > >   iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
> > > >   dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors
> > > >
> > > >  .../bindings/iio/imu/st_lsm6dsx.txt           |   1 +
> > > >  drivers/iio/imu/st_lsm6dsx/Makefile           |   3 +-
> > > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 167 +++-
> > > >  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 169 ++--
> > > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 273 +++++--
> > > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 753 ++++++++++++++++++
> > > >  .../linux/platform_data/st_sensors_pdata.h    |   2 +
> > > >  7 files changed, 1226 insertions(+), 142 deletions(-)
> > > >  create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> > > >
> > >
> > > Hi Lorenzo,
> > >
> > > Great to see this series.
> >
> > Hi Jonathan,
> >
> > thx a lot for the fast review :) I will fix your comments in a v2
> >
> > >
> > > It seems to have come together fairly nicely given the inherent complexity
> > > of dealing with these slave i2c controllers.   What I would like to
> > > see in this cover letter though is a brief description of what can be
> > > put behind these devices?
> > >
> > > The one thing I want to be careful of is ending up with lots of sort
> > > of replicated drivers where we have a version in each sensor hub
> > > and one for directly connected devices.  Superficially this
> > > device seems to have a very restricted I2C master so that might not
> > > be a significant problem.  What do you think?
> > > This gets particularly interesting if we think about switching in
> > > and out of pass through.  At that point this looks like an i2c offload
> > > engine (sort of) similar to the one Lars did for spi, be it a very
> > > restricted one.
> >
> > I think the main purpose of the i2c controller embedded in lsm6dso is
> > to connect a magnetometer sensor
> > to it and get in this way a 9axis device (please note this is just my opinion).
> > However we can think to connect other devices like temperature or
> > pressure sensors (I tested them in the past :)).
> > I think we will never use it as a 'general purpose' contoller, e.g. to
> >  connect an adc :)
>
> Hmm. Lets keep an eye on what gets added. If it gets silly we can revisit how
> to handle things.  Particularly if we get things that can't be so easily
> probed.  Of course, we may find no one ever adds anything else and that
> would be fine :)
>

Do you have something to point out?
Maybe a future improvement can be a more general way to pass
slave register map. What do you think?

Regards,
Lorenzo

> Jonathan
> >
> > Regards,
> > Lorenzo
> >
> > >
> > > Jonathan
>

^ permalink raw reply	[flat|nested] 27+ messages in thread

* Re: [PATCH 0/7] add i2c controller support to st_lsm6dsx driver
  2018-11-04 19:07       ` Lorenzo Bianconi
@ 2018-11-11 14:33         ` Jonathan Cameron
  0 siblings, 0 replies; 27+ messages in thread
From: Jonathan Cameron @ 2018-11-11 14:33 UTC (permalink / raw)
  To: Lorenzo Bianconi; +Cc: linux-iio, devicetree

On Sun, 4 Nov 2018 20:07:02 +0100
Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:

> >
> > On Sun, 4 Nov 2018 19:27:42 +0100
> > Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
> >  
> > > >
> > > > On Sun,  4 Nov 2018 15:38:59 +0100
> > > > Lorenzo Bianconi <lorenzo.bianconi@redhat.com> wrote:
> > > >  
> > > > > Introduce i2c controller support to st_lsm6dsx driver for lsm6dso sensor.
> > > > > Add register map for lis2mdl magnetometer sensor.
> > > > > Add hw FIFO support to st_lsm6dsx sensorhub driver.
> > > > >
> > > > > Lorenzo Bianconi (7):
> > > > >   iio: imu: st_lsm6dsx: introduce locked read/write utility routines
> > > > >   iio: imu: st_lsm6dsx: reboot memory content after reset
> > > > >   iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark
> > > > >   iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids
> > > > >   iio: imu: st_lsm6dsx: add i2c embedded controller support
> > > > >   iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller
> > > > >   dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors
> > > > >
> > > > >  .../bindings/iio/imu/st_lsm6dsx.txt           |   1 +
> > > > >  drivers/iio/imu/st_lsm6dsx/Makefile           |   3 +-
> > > > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h       | 167 +++-
> > > > >  .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c    | 169 ++--
> > > > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c  | 273 +++++--
> > > > >  drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c  | 753 ++++++++++++++++++
> > > > >  .../linux/platform_data/st_sensors_pdata.h    |   2 +
> > > > >  7 files changed, 1226 insertions(+), 142 deletions(-)
> > > > >  create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
> > > > >  
> > > >
> > > > Hi Lorenzo,
> > > >
> > > > Great to see this series.  
> > >
> > > Hi Jonathan,
> > >
> > > thx a lot for the fast review :) I will fix your comments in a v2
> > >  
> > > >
> > > > It seems to have come together fairly nicely given the inherent complexity
> > > > of dealing with these slave i2c controllers.   What I would like to
> > > > see in this cover letter though is a brief description of what can be
> > > > put behind these devices?
> > > >
> > > > The one thing I want to be careful of is ending up with lots of sort
> > > > of replicated drivers where we have a version in each sensor hub
> > > > and one for directly connected devices.  Superficially this
> > > > device seems to have a very restricted I2C master so that might not
> > > > be a significant problem.  What do you think?
> > > > This gets particularly interesting if we think about switching in
> > > > and out of pass through.  At that point this looks like an i2c offload
> > > > engine (sort of) similar to the one Lars did for spi, be it a very
> > > > restricted one.  
> > >
> > > I think the main purpose of the i2c controller embedded in lsm6dso is
> > > to connect a magnetometer sensor
> > > to it and get in this way a 9axis device (please note this is just my opinion).
> > > However we can think to connect other devices like temperature or
> > > pressure sensors (I tested them in the past :)).
> > > I think we will never use it as a 'general purpose' contoller, e.g. to
> > >  connect an adc :)  
> >
> > Hmm. Lets keep an eye on what gets added. If it gets silly we can revisit how
> > to handle things.  Particularly if we get things that can't be so easily
> > probed.  Of course, we may find no one ever adds anything else and that
> > would be fine :)
> >  
> 
> Do you have something to point out?
Nothing in particular.

> Maybe a future improvement can be a more general way to pass
> slave register map. What do you think?
Possibly true.  These sort of 'half implementations' of an i2c master
with offload are getting more common so we may need to look at this in detail.

Jonathan
> 
> Regards,
> Lorenzo
> 
> > Jonathan  
> > >
> > > Regards,
> > > Lorenzo
> > >  
> > > >
> > > > Jonathan  
> >  

^ permalink raw reply	[flat|nested] 27+ messages in thread

end of thread, other threads:[~2018-11-12  0:21 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-11-04 14:38 [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Lorenzo Bianconi
2018-11-04 14:39 ` [PATCH 1/7] iio: imu: st_lsm6dsx: introduce locked read/write utility routines Lorenzo Bianconi
2018-11-04 17:11   ` Jonathan Cameron
2018-11-04 14:39 ` [PATCH 2/7] iio: imu: st_lsm6dsx: reboot memory content after reset Lorenzo Bianconi
2018-11-04 17:12   ` Jonathan Cameron
2018-11-04 17:30     ` Lorenzo Bianconi
2018-11-04 14:39 ` [PATCH 3/7] iio: imu: st_lsm6dsx: remove static from st_lsm6dsx_set_watermark Lorenzo Bianconi
2018-11-04 14:39 ` [PATCH 4/7] iio: imu: st_lsm6dsx: introduce ST_LSM6DSX_ID_EXT sensor ids Lorenzo Bianconi
2018-11-04 17:18   ` Jonathan Cameron
2018-11-04 17:47     ` Lorenzo Bianconi
2018-11-04 18:18       ` Jonathan Cameron
2018-11-04 14:39 ` [PATCH 5/7] iio: imu: st_lsm6dsx: add i2c embedded controller support Lorenzo Bianconi
2018-11-04 17:42   ` Jonathan Cameron
2018-11-04 18:00     ` Lorenzo Bianconi
2018-11-04 18:21       ` Jonathan Cameron
2018-11-04 18:29         ` Lorenzo Bianconi
2018-11-04 14:39 ` [PATCH 6/7] iio: imu: st_lsm6dsx: add hw FIFO support to i2c controller Lorenzo Bianconi
2018-11-04 17:54   ` Jonathan Cameron
2018-11-04 18:14     ` Lorenzo Bianconi
2018-11-04 18:31       ` Jonathan Cameron
2018-11-04 18:56         ` Lorenzo Bianconi
2018-11-04 14:39 ` [PATCH 7/7] dt-bindings: iio: imu: st_lsm6dsx: add support to i2c pullup resistors Lorenzo Bianconi
2018-11-04 18:12 ` [PATCH 0/7] add i2c controller support to st_lsm6dsx driver Jonathan Cameron
2018-11-04 18:27   ` Lorenzo Bianconi
2018-11-04 18:34     ` Jonathan Cameron
2018-11-04 19:07       ` Lorenzo Bianconi
2018-11-11 14:33         ` Jonathan Cameron

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.