linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
@ 2018-09-20 13:13 Song Qiang
  2018-09-20 13:46 ` Peter Meerwald-Stadler
                   ` (4 more replies)
  0 siblings, 5 replies; 19+ messages in thread
From: Song Qiang @ 2018-09-20 13:13 UTC (permalink / raw)
  To: jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: linux-iio, devicetree, linux-kernel, Song Qiang

PNI RM3100 magnetometer is a high resolution, large signal immunity
magnetometer, composed of 3 single sensors and a processing chip.
PNI is currently not in the vendors list, so this is also adding it.

Following functions are available:
 - Single-shot measurement from
   /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
 - Triggerd buffer measurement.
 - Both i2c and spi interface are supported.
 - Both interrupt and polling measurement is supported, depands on if
   the 'interrupts' in DT is declared.

Signed-off-by: Song Qiang <songqiang1304521@gmail.com>
---
 .../bindings/iio/magnetometer/pni,rm3100.txt  |  57 +++
 .../devicetree/bindings/vendor-prefixes.txt   |   1 +
 MAINTAINERS                                   |  10 +
 drivers/iio/magnetometer/Kconfig              |  29 ++
 drivers/iio/magnetometer/Makefile             |   4 +
 drivers/iio/magnetometer/rm3100-core.c        | 399 ++++++++++++++++++
 drivers/iio/magnetometer/rm3100-i2c.c         |  66 +++
 drivers/iio/magnetometer/rm3100-spi.c         |  72 ++++
 drivers/iio/magnetometer/rm3100.h             |  90 ++++
 9 files changed, 728 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
 create mode 100644 drivers/iio/magnetometer/rm3100-core.c
 create mode 100644 drivers/iio/magnetometer/rm3100-i2c.c
 create mode 100644 drivers/iio/magnetometer/rm3100-spi.c
 create mode 100644 drivers/iio/magnetometer/rm3100.h

diff --git a/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
new file mode 100644
index 000000000000..d0d2063e943f
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
@@ -0,0 +1,57 @@
+* PNI RM3100 9-axis magnetometer sensor
+
+I2C Bus:
+
+Required properties:
+
+- compatible : should be "pni,rm3100-i2c"
+- reg : the I2C address of the magnetometer
+
+Optional properties:
+
+- interrupts: data ready (DRDY) from the chip.
+  The interrupts can be triggered on rising edges.
+
+	Refer to interrupt-controller/interrupts.txt for generic
+	interrupt client node bindings.
+
+- pinctrl-*: pinctrl setup for DRDY line.
+
+Example:
+
+rm3100: rm3100@20 {
+	pinctrl-names = "default";
+	pinctrl-0 = <&rm3100_pins>;
+
+	compatible = "pni,rm3100-i2c";
+	reg = <0x20>;
+	interrupt-parent = <&gpio0>;
+	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
+};
+
+SPI Bus:
+
+Required properties:
+
+- compatible : should be "pni,rm3100-spi"
+- reg : address of sensor, usually 0 or 1.
+
+Optional properties:
+
+- interrupts: data ready (DRDY) from the chip.
+  The interrupts can be triggered on rising edges.
+
+	Refer to interrupt-controller/interrupts.txt for generic
+	interrupt client node bindings.
+
+- pinctrl-*: pinctrl setup for DRDY line, depands on archtechture.
+
+Example:
+
+rm3100: rm3100@0{
+	compatible = "pni,rm3100-spi";
+	reg = <0>;
+
+	interrupt-parent = <&gpio0>;
+	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
+};
diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 41f0b97eb933..5bf3395fe9ae 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -288,6 +288,7 @@ pine64	Pine64
 pixcir  PIXCIR MICROELECTRONICS Co., Ltd
 plathome	Plat'Home Co., Ltd.
 plda	PLDA
+pni     PNI
 portwell	Portwell Inc.
 poslab	Poslab Technology Co., Ltd.
 powervr	PowerVR (deprecated, use img)
diff --git a/MAINTAINERS b/MAINTAINERS
index 967ce8cdd1cc..30ee8cf98312 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11393,6 +11393,16 @@ M:	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
 S:	Maintained
 F:	drivers/pnp/
 
+PNI RM3100 IIO DRIVER
+M:	Song Qiang <songqiang1304521@gmail.com>
+L:	linux-iio@vger.kernel.org
+S:	Maintained
+F:	drivers/iio/magnetometer/rm3100-core.c
+F:	drivers/iio/magnetometer/rm3100-i2c.c
+F:	drivers/iio/magnetometer/rm3100-spi.c
+F:	drivers/iio/magnetometer/rm3100.h
+F:	Documentation/devicetree/bindings/iio/magnetometer/rm3100.txt
+
 POSIX CLOCKS and TIMERS
 M:	Thomas Gleixner <tglx@linutronix.de>
 L:	linux-kernel@vger.kernel.org
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index ed9d776d01af..f130b866a4fc 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -175,4 +175,33 @@ config SENSORS_HMC5843_SPI
 	  - hmc5843_core (core functions)
 	  - hmc5843_spi (support for HMC5983)
 
+config SENSORS_RM3100
+	tristate
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+
+config SENSORS_RM3100_I2C
+	tristate "PNI RM3100 9-Axis Magnetometer (I2C)"
+	depends on I2C
+	select SENSORS_RM3100
+	select REGMAP_I2C
+	help
+	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
+
+	  This driver can also be compiled as a module.
+	  To compile this driver as a module, choose M here: the module
+	  will be called rm3100-i2c.
+
+config SENSORS_RM3100_SPI
+	tristate "PNI RM3100 9-Axis Magnetometer (SPI)"
+	depends on SPI_MASTER
+	select SENSORS_RM3100
+	select REGMAP_SPI
+	help
+	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
+
+	  This driver can also be compiled as a module.
+	  To compile this driver as a module, choose M here: the module
+	  will be called rm3100-spi.
+
 endmenu
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 664b2f866472..ba1bc34b82fa 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -24,3 +24,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
 obj-$(CONFIG_SENSORS_HMC5843)		+= hmc5843_core.o
 obj-$(CONFIG_SENSORS_HMC5843_I2C)	+= hmc5843_i2c.o
 obj-$(CONFIG_SENSORS_HMC5843_SPI)	+= hmc5843_spi.o
+
+obj-$(CONFIG_SENSORS_RM3100)		+= rm3100-core.o
+obj-$(CONFIG_SENSORS_RM3100_I2C)	+= rm3100-i2c.o
+obj-$(CONFIG_SENSORS_RM3100_SPI)	+= rm3100-spi.o
diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
new file mode 100644
index 000000000000..55d515e0fe67
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-core.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * PNI RM3100 9-axis geomagnetic sensor driver core.
+ *
+ * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
+ *
+ * User Manual available at
+ * <https://www.pnicorp.com/download/rm3100-user-manual/>
+ *
+ * TODO: Scale channel, event generaton, pm.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#include "rm3100.h"
+
+static const struct regmap_range rm3100_readable_ranges[] = {
+		regmap_reg_range(RM_W_REG_START, RM_W_REG_END),
+};
+
+const struct regmap_access_table rm3100_readable_table = {
+		.yes_ranges = rm3100_readable_ranges,
+		.n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
+};
+
+static const struct regmap_range rm3100_writable_ranges[] = {
+		regmap_reg_range(RM_R_REG_START, RM_R_REG_END),
+};
+
+const struct regmap_access_table rm3100_writable_table = {
+		.yes_ranges = rm3100_writable_ranges,
+		.n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
+};
+
+static const struct regmap_range rm3100_volatile_ranges[] = {
+		regmap_reg_range(RM_V_REG_START, RM_V_REG_END),
+};
+
+const struct regmap_access_table rm3100_volatile_table = {
+		.yes_ranges = rm3100_volatile_ranges,
+		.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
+};
+
+static irqreturn_t rm3100_measurement_irq_handler(int irq, void *d)
+{
+	struct rm3100_data *data = d;
+
+	complete(&data->measuring_done);
+
+	return IRQ_HANDLED;
+}
+
+static int rm3100_wait_measurement(struct rm3100_data *data)
+{
+	struct regmap *regmap = data->regmap;
+	unsigned int val;
+	u16 tries = 20;
+	int ret;
+
+	/* A read cycle of 400kbits i2c bus is about 20us, plus the time
+	 * used for schduling, a read cycle of fast mode of this device
+	 * can reach 1.7ms, it may be possible for data arrives just
+	 * after we check the RM_REG_STATUS. In this case, irq_handler is
+	 * called before measuring_done is reinitialized, it will wait
+	 * forever for a data that has already been ready.
+	 * Reinitialize measuring_done before looking up makes sure we
+	 * will always capture interrupt no matter when it happened.
+	 */
+	if (data->use_interrupt)
+		reinit_completion(&data->measuring_done);
+
+	ret = regmap_read(regmap, RM_REG_STATUS, &val);
+	if (ret < 0)
+		return ret;
+
+	if ((val & RM_STATUS_DRDY) != RM_STATUS_DRDY) {
+		if (data->use_interrupt) {
+			ret = wait_for_completion_timeout(&data->measuring_done,
+				msecs_to_jiffies(data->conversion_time));
+			if (!ret)
+				return -ETIMEDOUT;
+		} else {
+			do {
+				ret = regmap_read(regmap, RM_REG_STATUS, &val);
+				if (ret < 0)
+					return ret;
+
+				if (val & RM_STATUS_DRDY)
+					break;
+
+				usleep_range(1000, 5000);
+			} while (--tries);
+			if (!tries)
+				return -ETIMEDOUT;
+		}
+	}
+	return 0;
+}
+
+static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val)
+{
+	struct regmap *regmap = data->regmap;
+	u8 buffer[3];
+	int ret;
+
+	mutex_lock(&data->lock);
+	ret = rm3100_wait_measurement(data);
+	if (ret < 0) {
+		mutex_unlock(&data->lock);
+		return ret;
+	}
+
+	ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * idx, buffer, 3);
+	mutex_unlock(&data->lock);
+	if (ret < 0)
+		return ret;
+
+	*val = le32_to_cpu((buffer[0] << 16) + (buffer[1] << 8) + buffer[2]);
+	*val = sign_extend32(*val, 23);
+
+	return IIO_VAL_INT;
+}
+
+#define RM_CHANNEL(axis, idx)					\
+	{								\
+		.type = IIO_MAGN,					\
+		.modified = 1,						\
+		.channel2 = IIO_MOD_##axis,				\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+		.scan_index = idx,					\
+		.scan_type = {						\
+			.sign = 's',					\
+			.realbits = 24,					\
+			.storagebits = 32,				\
+			.shift = 8,					\
+			.endianness = IIO_LE,				\
+		},							\
+	}
+
+static const struct iio_chan_spec rm3100_channels[] = {
+	RM_CHANNEL(X, 0),
+	RM_CHANNEL(Y, 1),
+	RM_CHANNEL(Z, 2),
+	IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
+
+#define RM_SAMP_NUM	14
+
+/* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
+ * Time between reading: rm3100_sam_rates[][2]ms (The first on is actially 1.7).
+ */
+static const int rm3100_samp_rates[RM_SAMP_NUM][3] = {
+	{600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27},
+	{18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440},
+	{1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300},
+	{0, 15000, 6700},  {0, 75000, 13000}
+};
+
+static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
+{
+	int ret;
+	int tmp;
+
+	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
+	if (ret < 0)
+		return ret;
+	*val = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][0];
+	*val2 = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][1];
+
+	return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
+{
+	struct regmap *regmap = data->regmap;
+	int cycle_count;
+	int ret;
+	int i;
+
+	/* All cycle count registers use the same value. */
+	ret = regmap_read(regmap, RM_REG_CCXL, &cycle_count);
+	if (cycle_count < 0)
+		return cycle_count;
+
+	for (i = 0; i < RM_SAMP_NUM; i++) {
+		if (val == rm3100_samp_rates[i][0] &&
+			val2 == rm3100_samp_rates[i][1])
+			break;
+	}
+
+	if (i != RM_SAMP_NUM) {
+		mutex_lock(&data->lock);
+		ret = regmap_write(regmap, RM_REG_TMRC, i + RM_TMRC_OFFSET);
+		if (ret < 0)
+			return ret;
+
+		/* Checking if cycle count registers need changing. */
+		if (val == 600 && cycle_count == 200) {
+			for (i = 0; i < 3; i++) {
+				regmap_write(regmap, RM_REG_CCXL + 2 * i, 100);
+				if (ret < 0)
+					return ret;
+			}
+		} else if (val != 600 && cycle_count == 100) {
+			for (i = 0; i < 3; i++) {
+				regmap_write(regmap, RM_REG_CCXL + 2 * i, 200);
+				if (ret < 0)
+					return ret;
+			}
+		}
+		/* Writing TMRC registers requires CMM reset. */
+		ret = regmap_write(regmap, RM_REG_CMM, 0);
+		if (ret < 0)
+			return ret;
+		ret = regmap_write(regmap, RM_REG_CMM, RM_CMM_PMX |
+			RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
+		if (ret < 0)
+			return ret;
+		mutex_unlock(&data->lock);
+
+		data->conversion_time = rm3100_samp_rates[i][2] + 3000;
+		return 0;
+	}
+	return -EINVAL;
+}
+
+static int rm3100_read_raw(struct iio_dev *indio_dev,
+			   const struct iio_chan_spec *chan,
+			   int *val, int *val2, long mask)
+{
+	struct rm3100_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = iio_device_claim_direct_mode(indio_dev);
+		if (ret < 0)
+			return ret;
+		ret = rm3100_read_mag(data, chan->scan_index, val);
+		iio_device_release_direct_mode(indio_dev);
+
+		return ret;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return ret = rm3100_get_samp_freq(data, val, val2);
+	default:
+		return -EINVAL;
+	}
+}
+
+static int rm3100_write_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan,
+			    int val, int val2, long mask)
+{
+	struct rm3100_data *data = iio_priv(indio_dev);
+	int ret;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = rm3100_set_samp_freq(data, val, val2);
+		if (ret < 0)
+			return ret;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+
+}
+
+static const struct iio_info rm3100_info = {
+	.read_raw = rm3100_read_raw,
+	.write_raw = rm3100_write_raw,
+};
+
+static irqreturn_t rm3100_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct rm3100_data *data = iio_priv(indio_dev);
+	struct regmap *regmap = data->regmap;
+	u8 *buffer;
+	int ret;
+	int i;
+
+	buffer = devm_kzalloc(data->dev, indio_dev->scan_bytes, GFP_KERNEL);
+	if (!buffer)
+		goto done;
+
+	mutex_lock(&data->lock);
+	ret = rm3100_wait_measurement(data);
+	if (ret < 0) {
+		mutex_unlock(&data->lock);
+		goto done;
+	}
+
+	for (i = 0; i < 3; i++) {
+		ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * i,
+				buffer + 4 * i, 3);
+		if (ret < 0)
+			return ret;
+	}
+	mutex_unlock(&data->lock);
+
+	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+			iio_get_time_ns(indio_dev));
+done:
+	iio_trigger_notify_done(indio_dev->trig);
+
+	return IRQ_HANDLED;
+}
+
+int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
+{
+	struct iio_dev *indio_dev;
+	struct rm3100_data *data;
+	int tmp;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	dev_set_drvdata(dev, indio_dev);
+	data->dev = dev;
+	data->regmap = regmap;
+
+	mutex_init(&data->lock);
+
+	indio_dev->dev.parent = dev;
+	indio_dev->name = "rm3100";
+	indio_dev->info = &rm3100_info;
+	indio_dev->channels = rm3100_channels;
+	indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+	indio_dev->available_scan_masks = rm3100_scan_masks;
+
+	if (!irq)
+		data->use_interrupt = false;
+	else {
+		data->use_interrupt = true;
+		ret = devm_request_irq(dev,
+			irq,
+			rm3100_measurement_irq_handler,
+			IRQF_TRIGGER_RISING,
+			indio_dev->name,
+			data);
+		if (ret < 0) {
+			dev_err(dev,
+			"request irq line failed.");
+			return -ret;
+		}
+		init_completion(&data->measuring_done);
+	}
+
+	ret = iio_triggered_buffer_setup(indio_dev, NULL,
+					rm3100_trigger_handler, NULL);
+	if (ret < 0)
+		return ret;
+
+	/* 3sec more wait time. */
+	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
+	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
+
+	/* Starting all channels' conversion. */
+	ret = regmap_write(regmap, RM_REG_CMM,
+		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
+	if (ret < 0)
+		return ret;
+
+	return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL(rm3100_common_probe);
+
+int rm3100_common_remove(struct device *dev)
+{
+	struct iio_dev *indio_dev = dev_get_drvdata(dev);
+	struct rm3100_data *data = iio_priv(indio_dev);
+	struct regmap *regmap = data->regmap;
+
+	regmap_write(regmap, RM_REG_CMM, 0x00);
+
+	return 0;
+}
+EXPORT_SYMBOL(rm3100_common_remove);
+
+MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
+MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
new file mode 100644
index 000000000000..b50dc5b1b30b
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-i2c.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
+ *
+ * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
+ *
+ * User Manual available at
+ * <https://www.pnicorp.com/download/rm3100-user-manual/>
+ *
+ * i2c slave address 0x20 + SA1 << 1 + SA0.
+ */
+
+#include <linux/i2c.h>
+
+#include "rm3100.h"
+
+static const struct regmap_config rm3100_regmap_config = {
+		.reg_bits = 8,
+		.val_bits = 8,
+
+		.rd_table = &rm3100_readable_table,
+		.wr_table = &rm3100_writable_table,
+		.volatile_table = &rm3100_volatile_table,
+
+		.cache_type = REGCACHE_RBTREE,
+};
+
+static int rm3100_probe(struct i2c_client *client)
+{
+	struct regmap *regmap;
+
+	if (!i2c_check_functionality(client->adapter,
+		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
+		return -EOPNOTSUPP;
+
+	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	return rm3100_common_probe(&client->dev, regmap, client->irq);
+}
+
+static int rm3100_remove(struct i2c_client *client)
+{
+	return rm3100_common_remove(&client->dev);
+}
+
+static const struct of_device_id rm3100_dt_match[] = {
+	{ .compatible = "pni,rm3100-i2c", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rm3100_dt_match);
+
+static struct i2c_driver rm3100_driver = {
+	.driver = {
+		.name = "rm3100-i2c",
+		.of_match_table = rm3100_dt_match,
+	},
+	.probe_new = rm3100_probe,
+	.remove = rm3100_remove,
+};
+module_i2c_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
+MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
new file mode 100644
index 000000000000..2c7dd9e3a1a2
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-spi.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
+ *
+ * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
+ *
+ * User Manual available at
+ * <https://www.pnicorp.com/download/rm3100-user-manual/>
+ */
+
+#include <linux/spi/spi.h>
+
+#include "rm3100.h"
+
+static const struct regmap_config rm3100_regmap_config = {
+		.reg_bits = 8,
+		.val_bits = 8,
+
+		.rd_table = &rm3100_readable_table,
+		.wr_table = &rm3100_writable_table,
+		.volatile_table = &rm3100_volatile_table,
+
+		.read_flag_mask = 0x80,
+
+		.cache_type = REGCACHE_RBTREE,
+};
+
+static int rm3100_probe(struct spi_device *spi)
+{
+	struct regmap *regmap;
+	int ret;
+
+	/* Actually this device supports both mode 0 and mode 3. */
+	spi->mode = SPI_MODE_0;
+	/* data rates cannot exceeds 1Mbits. */
+	spi->max_speed_hz = 1000000;
+	spi->bits_per_word = 8;
+	ret = spi_setup(spi);
+	if (ret)
+		return ret;
+
+	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
+}
+
+static int rm3100_remove(struct spi_device *spi)
+{
+	return rm3100_common_remove(&spi->dev);
+}
+
+static const struct of_device_id rm3100_dt_match[] = {
+	{ .compatible = "pni,rm3100-spi", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, rm3100_dt_match);
+
+static struct spi_driver rm3100_driver = {
+		.driver = {
+				.name = "rm3100-spi",
+				.of_match_table = rm3100_dt_match,
+		},
+		.probe = rm3100_probe,
+		.remove = rm3100_remove,
+};
+module_spi_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
+MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
new file mode 100644
index 000000000000..5e30bc0f5149
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Header file for PNI RM3100 driver
+ *
+ * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
+ */
+
+#ifndef RM3100_CORE_H
+#define RM3100_CORE_H
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#define RM_REG_REV_ID		0x36
+
+/* Cycle Count Registers MSBs and LSBs. */
+#define RM_REG_CCXM		0x04
+#define RM_REG_CCXL		0x05
+#define RM_REG_CCYM		0x06
+#define RM_REG_CCYL		0x07
+#define RM_REG_CCZM		0x08
+#define RM_REG_CCZL		0x09
+
+/* Single Measurement Mode register. */
+#define RM_REG_POLL		0x00
+#define RM_POLL_PMX		BIT(4)
+#define RM_POLL_PMY		BIT(5)
+#define RM_POLL_PMZ		BIT(6)
+
+/* Continues Measurement Mode register. */
+#define RM_REG_CMM		0x01
+#define RM_CMM_START		BIT(0)
+#define RM_CMM_DRDM		BIT(2)
+#define RM_CMM_PMX		BIT(4)
+#define RM_CMM_PMY		BIT(5)
+#define RM_CMM_PMZ		BIT(6)
+
+/* TiMe Rate Configuration register. */
+#define RM_REG_TMRC		0x0B
+#define RM_TMRC_OFFSET		0x92
+
+/* Result Status register. */
+#define RM_REG_STATUS		0x34
+#define RM_STATUS_DRDY		BIT(7)
+
+/* Measurement result registers. */
+#define RM_REG_MX2		0x24
+#define RM_REG_MX1		0x25
+#define RM_REG_MX0		0x26
+#define RM_REG_MY2		0x27
+#define RM_REG_MY1		0x28
+#define RM_REG_MY0		0x29
+#define RM_REG_MZ2		0x2a
+#define RM_REG_MZ1		0x2b
+#define RM_REG_MZ0		0x2c
+
+#define RM_REG_HSHAKE		0x35
+
+#define RM_W_REG_START		RM_REG_POLL
+#define RM_W_REG_END		RM_REG_REV_ID
+#define RM_R_REG_START		RM_REG_POLL
+#define RM_R_REG_END		RM_REG_HSHAKE
+#define RM_V_REG_START		RM_REG_MX2
+#define RM_V_REG_END		RM_REG_HSHAKE
+
+/* Built-In Self Test reigister. */
+#define RM_REG_BIST		0x33
+
+struct rm3100_data {
+	struct device *dev;
+	struct regmap *regmap;
+	struct completion measuring_done;
+	bool use_interrupt;
+
+	int conversion_time;
+
+	/* To protect consistency of every measurement and sampling
+	 * frequency change operations.
+	 */
+	struct mutex lock;
+};
+
+extern const struct regmap_access_table rm3100_readable_table;
+extern const struct regmap_access_table rm3100_writable_table;
+extern const struct regmap_access_table rm3100_volatile_table;
+
+int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
+int rm3100_common_remove(struct device *dev);
+
+#endif /* RM3100_CORE_H */
-- 
2.17.1


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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-20 13:13 [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer Song Qiang
@ 2018-09-20 13:46 ` Peter Meerwald-Stadler
  2018-09-20 18:05   ` Song Qiang
  2018-09-21  5:07   ` Phil Reid
  2018-09-21  2:05 ` Phil Reid
                   ` (3 subsequent siblings)
  4 siblings, 2 replies; 19+ messages in thread
From: Peter Meerwald-Stadler @ 2018-09-20 13:46 UTC (permalink / raw)
  To: Song Qiang; +Cc: jic23, lars, linux-iio, devicetree, linux-kernel

On Thu, 20 Sep 2018, Song Qiang wrote:

> PNI RM3100 magnetometer is a high resolution, large signal immunity
> magnetometer, composed of 3 single sensors and a processing chip.
> PNI is currently not in the vendors list, so this is also adding it.

comments below

> 
> Following functions are available:
>  - Single-shot measurement from
>    /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
>  - Triggerd buffer measurement.
>  - Both i2c and spi interface are supported.
>  - Both interrupt and polling measurement is supported, depands on if
>    the 'interrupts' in DT is declared.
> 
> Signed-off-by: Song Qiang <songqiang1304521@gmail.com>
> ---
>  .../bindings/iio/magnetometer/pni,rm3100.txt  |  57 +++
>  .../devicetree/bindings/vendor-prefixes.txt   |   1 +
>  MAINTAINERS                                   |  10 +
>  drivers/iio/magnetometer/Kconfig              |  29 ++
>  drivers/iio/magnetometer/Makefile             |   4 +
>  drivers/iio/magnetometer/rm3100-core.c        | 399 ++++++++++++++++++
>  drivers/iio/magnetometer/rm3100-i2c.c         |  66 +++
>  drivers/iio/magnetometer/rm3100-spi.c         |  72 ++++
>  drivers/iio/magnetometer/rm3100.h             |  90 ++++
>  9 files changed, 728 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
>  create mode 100644 drivers/iio/magnetometer/rm3100-core.c
>  create mode 100644 drivers/iio/magnetometer/rm3100-i2c.c
>  create mode 100644 drivers/iio/magnetometer/rm3100-spi.c
>  create mode 100644 drivers/iio/magnetometer/rm3100.h
> 
> diff --git a/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> new file mode 100644
> index 000000000000..d0d2063e943f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> @@ -0,0 +1,57 @@
> +* PNI RM3100 9-axis magnetometer sensor
> +
> +I2C Bus:
> +
> +Required properties:
> +
> +- compatible : should be "pni,rm3100-i2c"
> +- reg : the I2C address of the magnetometer
> +
> +Optional properties:
> +
> +- interrupts: data ready (DRDY) from the chip.
> +  The interrupts can be triggered on rising edges.
> +
> +	Refer to interrupt-controller/interrupts.txt for generic
> +	interrupt client node bindings.
> +
> +- pinctrl-*: pinctrl setup for DRDY line.
> +
> +Example:
> +
> +rm3100: rm3100@20 {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&rm3100_pins>;
> +
> +	compatible = "pni,rm3100-i2c";
> +	reg = <0x20>;
> +	interrupt-parent = <&gpio0>;
> +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> +};
> +
> +SPI Bus:
> +
> +Required properties:
> +
> +- compatible : should be "pni,rm3100-spi"
> +- reg : address of sensor, usually 0 or 1.
> +
> +Optional properties:
> +
> +- interrupts: data ready (DRDY) from the chip.
> +  The interrupts can be triggered on rising edges.
> +
> +	Refer to interrupt-controller/interrupts.txt for generic
> +	interrupt client node bindings.
> +
> +- pinctrl-*: pinctrl setup for DRDY line, depands on archtechture.

depends
architecture

> +
> +Example:
> +
> +rm3100: rm3100@0{
> +	compatible = "pni,rm3100-spi";
> +	reg = <0>;
> +
> +	interrupt-parent = <&gpio0>;
> +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index 41f0b97eb933..5bf3395fe9ae 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -288,6 +288,7 @@ pine64	Pine64
>  pixcir  PIXCIR MICROELECTRONICS Co., Ltd
>  plathome	Plat'Home Co., Ltd.
>  plda	PLDA
> +pni     PNI
>  portwell	Portwell Inc.
>  poslab	Poslab Technology Co., Ltd.
>  powervr	PowerVR (deprecated, use img)
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 967ce8cdd1cc..30ee8cf98312 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11393,6 +11393,16 @@ M:	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
>  S:	Maintained
>  F:	drivers/pnp/
>  
> +PNI RM3100 IIO DRIVER
> +M:	Song Qiang <songqiang1304521@gmail.com>
> +L:	linux-iio@vger.kernel.org
> +S:	Maintained
> +F:	drivers/iio/magnetometer/rm3100-core.c
> +F:	drivers/iio/magnetometer/rm3100-i2c.c
> +F:	drivers/iio/magnetometer/rm3100-spi.c
> +F:	drivers/iio/magnetometer/rm3100.h
> +F:	Documentation/devicetree/bindings/iio/magnetometer/rm3100.txt
> +
>  POSIX CLOCKS and TIMERS
>  M:	Thomas Gleixner <tglx@linutronix.de>
>  L:	linux-kernel@vger.kernel.org
> diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
> index ed9d776d01af..f130b866a4fc 100644
> --- a/drivers/iio/magnetometer/Kconfig
> +++ b/drivers/iio/magnetometer/Kconfig
> @@ -175,4 +175,33 @@ config SENSORS_HMC5843_SPI
>  	  - hmc5843_core (core functions)
>  	  - hmc5843_spi (support for HMC5983)
>  
> +config SENSORS_RM3100
> +	tristate
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +
> +config SENSORS_RM3100_I2C
> +	tristate "PNI RM3100 9-Axis Magnetometer (I2C)"
> +	depends on I2C
> +	select SENSORS_RM3100
> +	select REGMAP_I2C
> +	help
> +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
> +
> +	  This driver can also be compiled as a module.
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called rm3100-i2c.
> +
> +config SENSORS_RM3100_SPI
> +	tristate "PNI RM3100 9-Axis Magnetometer (SPI)"
> +	depends on SPI_MASTER
> +	select SENSORS_RM3100
> +	select REGMAP_SPI
> +	help
> +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
> +
> +	  This driver can also be compiled as a module.
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called rm3100-spi.
> +
>  endmenu
> diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
> index 664b2f866472..ba1bc34b82fa 100644
> --- a/drivers/iio/magnetometer/Makefile
> +++ b/drivers/iio/magnetometer/Makefile
> @@ -24,3 +24,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
>  obj-$(CONFIG_SENSORS_HMC5843)		+= hmc5843_core.o
>  obj-$(CONFIG_SENSORS_HMC5843_I2C)	+= hmc5843_i2c.o
>  obj-$(CONFIG_SENSORS_HMC5843_SPI)	+= hmc5843_spi.o
> +
> +obj-$(CONFIG_SENSORS_RM3100)		+= rm3100-core.o
> +obj-$(CONFIG_SENSORS_RM3100_I2C)	+= rm3100-i2c.o
> +obj-$(CONFIG_SENSORS_RM3100_SPI)	+= rm3100-spi.o
> diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
> new file mode 100644
> index 000000000000..55d515e0fe67
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-core.c
> @@ -0,0 +1,399 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * PNI RM3100 9-axis geomagnetic sensor driver core.
> + *
> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + *
> + * TODO: Scale channel, event generaton, pm.

at least read support for _SCALE is mandatory, IMHO

> + */
> +
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/kfifo_buf.h>
> +
> +#include "rm3100.h"
> +
> +static const struct regmap_range rm3100_readable_ranges[] = {
> +		regmap_reg_range(RM_W_REG_START, RM_W_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_readable_table = {

static

> +		.yes_ranges = rm3100_readable_ranges,
> +		.n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> +};
> +
> +static const struct regmap_range rm3100_writable_ranges[] = {
> +		regmap_reg_range(RM_R_REG_START, RM_R_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_writable_table = {

static

> +		.yes_ranges = rm3100_writable_ranges,
> +		.n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> +};
> +
> +static const struct regmap_range rm3100_volatile_ranges[] = {
> +		regmap_reg_range(RM_V_REG_START, RM_V_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_volatile_table = {

static

> +		.yes_ranges = rm3100_volatile_ranges,
> +		.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> +};
> +
> +static irqreturn_t rm3100_measurement_irq_handler(int irq, void *d)
> +{
> +	struct rm3100_data *data = d;
> +
> +	complete(&data->measuring_done);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int rm3100_wait_measurement(struct rm3100_data *data)
> +{
> +	struct regmap *regmap = data->regmap;
> +	unsigned int val;
> +	u16 tries = 20;

why not use int for tries?

> +	int ret;
> +
> +	/* A read cycle of 400kbits i2c bus is about 20us, plus the time
> +	 * used for schduling, a read cycle of fast mode of this device

scheduling

> +	 * can reach 1.7ms, it may be possible for data arrives just

to arrive

> +	 * after we check the RM_REG_STATUS. In this case, irq_handler is
> +	 * called before measuring_done is reinitialized, it will wait
> +	 * forever for a data that has already been ready.

for data

> +	 * Reinitialize measuring_done before looking up makes sure we
> +	 * will always capture interrupt no matter when it happened.
> +	 */
> +	if (data->use_interrupt)
> +		reinit_completion(&data->measuring_done);
> +
> +	ret = regmap_read(regmap, RM_REG_STATUS, &val);
> +	if (ret < 0)
> +		return ret;
> +
> +	if ((val & RM_STATUS_DRDY) != RM_STATUS_DRDY) {
> +		if (data->use_interrupt) {
> +			ret = wait_for_completion_timeout(&data->measuring_done,
> +				msecs_to_jiffies(data->conversion_time));
> +			if (!ret)
> +				return -ETIMEDOUT;
> +		} else {
> +			do {
> +				ret = regmap_read(regmap, RM_REG_STATUS, &val);
> +				if (ret < 0)
> +					return ret;
> +
> +				if (val & RM_STATUS_DRDY)
> +					break;
> +
> +				usleep_range(1000, 5000);
> +			} while (--tries);
> +			if (!tries)
> +				return -ETIMEDOUT;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val)
> +{
> +	struct regmap *regmap = data->regmap;
> +	u8 buffer[3];
> +	int ret;
> +
> +	mutex_lock(&data->lock);
> +	ret = rm3100_wait_measurement(data);
> +	if (ret < 0) {
> +		mutex_unlock(&data->lock);
> +		return ret;
> +	}
> +
> +	ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * idx, buffer, 3);

sizeof(buf)

> +	mutex_unlock(&data->lock);
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = le32_to_cpu((buffer[0] << 16) + (buffer[1] << 8) + buffer[2]);

no need for le32_to_cpu() 

> +	*val = sign_extend32(*val, 23);
> +
> +	return IIO_VAL_INT;
> +}
> +
> +#define RM_CHANNEL(axis, idx)					\

use RM3100_ prefix please

> +	{								\
> +		.type = IIO_MAGN,					\
> +		.modified = 1,						\
> +		.channel2 = IIO_MOD_##axis,				\
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> +		.scan_index = idx,					\
> +		.scan_type = {						\
> +			.sign = 's',					\
> +			.realbits = 24,					\
> +			.storagebits = 32,				\
> +			.shift = 8,					\
> +			.endianness = IIO_LE,				\
> +		},							\
> +	}
> +
> +static const struct iio_chan_spec rm3100_channels[] = {
> +	RM_CHANNEL(X, 0),
> +	RM_CHANNEL(Y, 1),
> +	RM_CHANNEL(Z, 2),
> +	IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
> +
> +#define RM_SAMP_NUM	14

prefix

> +
> +/* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> + * Time between reading: rm3100_sam_rates[][2]ms (The first on is actially 1.7).

one
actually
1.7 what unit?


> + */
> +static const int rm3100_samp_rates[RM_SAMP_NUM][3] = {
> +	{600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27},
> +	{18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440},
> +	{1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300},
> +	{0, 15000, 6700},  {0, 75000, 13000}
> +};
> +
> +static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
> +{
> +	int ret;
> +	int tmp;
> +
> +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> +	if (ret < 0)
> +		return ret;
> +	*val = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][0];

space around - operator

> +	*val2 = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][1];
> +
> +	return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
> +{
> +	struct regmap *regmap = data->regmap;
> +	int cycle_count;
> +	int ret;
> +	int i;
> +
> +	/* All cycle count registers use the same value. */
> +	ret = regmap_read(regmap, RM_REG_CCXL, &cycle_count);

check ret?

> +	if (cycle_count < 0)
> +		return cycle_count;
> +
> +	for (i = 0; i < RM_SAMP_NUM; i++) {
> +		if (val == rm3100_samp_rates[i][0] &&
> +			val2 == rm3100_samp_rates[i][1])
> +			break;
> +	}
> +
> +	if (i != RM_SAMP_NUM) {
> +		mutex_lock(&data->lock);
> +		ret = regmap_write(regmap, RM_REG_TMRC, i + RM_TMRC_OFFSET);
> +		if (ret < 0)

unlock?

> +			return ret;
> +
> +		/* Checking if cycle count registers need changing. */
> +		if (val == 600 && cycle_count == 200) {
> +			for (i = 0; i < 3; i++) {
> +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 100);
> +				if (ret < 0)

unlock?

> +					return ret;
> +			}
> +		} else if (val != 600 && cycle_count == 100) {
> +			for (i = 0; i < 3; i++) {
> +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 200);
> +				if (ret < 0)

unlock?

> +					return ret;
> +			}
> +		}
> +		/* Writing TMRC registers requires CMM reset. */
> +		ret = regmap_write(regmap, RM_REG_CMM, 0);
> +		if (ret < 0)

unlock?

> +			return ret;
> +		ret = regmap_write(regmap, RM_REG_CMM, RM_CMM_PMX |
> +			RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> +		if (ret < 0)

unlock?

> +			return ret;
> +		mutex_unlock(&data->lock);
> +
> +		data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> +		return 0;
> +	}
> +	return -EINVAL;
> +}
> +
> +static int rm3100_read_raw(struct iio_dev *indio_dev,
> +			   const struct iio_chan_spec *chan,
> +			   int *val, int *val2, long mask)
> +{
> +	struct rm3100_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = iio_device_claim_direct_mode(indio_dev);
> +		if (ret < 0)

release_direct_mode() here?

> +			return ret;
> +		ret = rm3100_read_mag(data, chan->scan_index, val);
> +		iio_device_release_direct_mode(indio_dev);
> +
> +		return ret;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		return ret = rm3100_get_samp_freq(data, val, val2);

return ret = ???, just
return rm3100_...

> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int rm3100_write_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int val, int val2, long mask)
> +{
> +	struct rm3100_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		ret = rm3100_set_samp_freq(data, val, val2);
> +		if (ret < 0)
> +			return ret;
> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +}
> +
> +static const struct iio_info rm3100_info = {
> +	.read_raw = rm3100_read_raw,
> +	.write_raw = rm3100_write_raw,
> +};
> +
> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct rm3100_data *data = iio_priv(indio_dev);
> +	struct regmap *regmap = data->regmap;
> +	u8 *buffer;
> +	int ret;
> +	int i;
> +
> +	buffer = devm_kzalloc(data->dev, indio_dev->scan_bytes, GFP_KERNEL);

try to allocate the maximum needed amount of memory beforehand, in 
_probe() perhaps 

> +	if (!buffer)
> +		goto done;
> +
> +	mutex_lock(&data->lock);
> +	ret = rm3100_wait_measurement(data);
> +	if (ret < 0) {
> +		mutex_unlock(&data->lock);
> +		goto done;
> +	}
> +
> +	for (i = 0; i < 3; i++) {
> +		ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * i,
> +				buffer + 4 * i, 3);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	mutex_unlock(&data->lock);
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
> +			iio_get_time_ns(indio_dev));
> +done:
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> +{
> +	struct iio_dev *indio_dev;
> +	struct rm3100_data *data;
> +	int tmp;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	data = iio_priv(indio_dev);
> +	dev_set_drvdata(dev, indio_dev);
> +	data->dev = dev;
> +	data->regmap = regmap;
> +
> +	mutex_init(&data->lock);
> +
> +	indio_dev->dev.parent = dev;
> +	indio_dev->name = "rm3100";
> +	indio_dev->info = &rm3100_info;
> +	indio_dev->channels = rm3100_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->available_scan_masks = rm3100_scan_masks;
> +
> +	if (!irq)
> +		data->use_interrupt = false;
> +	else {
> +		data->use_interrupt = true;
> +		ret = devm_request_irq(dev,
> +			irq,
> +			rm3100_measurement_irq_handler,
> +			IRQF_TRIGGER_RISING,
> +			indio_dev->name,
> +			data);
> +		if (ret < 0) {
> +			dev_err(dev,
> +			"request irq line failed.");

\n

> +			return -ret;
> +		}
> +		init_completion(&data->measuring_done);
> +	}
> +
> +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> +					rm3100_trigger_handler, NULL);
> +	if (ret < 0)
> +		return ret;
> +
> +	/* 3sec more wait time. */
> +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);

check ret

> +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> +
> +	/* Starting all channels' conversion. */
> +	ret = regmap_write(regmap, RM_REG_CMM,
> +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> +	if (ret < 0)
> +		return ret;
> +
> +	return devm_iio_device_register(dev, indio_dev);
> +}
> +EXPORT_SYMBOL(rm3100_common_probe);
> +
> +int rm3100_common_remove(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct rm3100_data *data = iio_priv(indio_dev);
> +	struct regmap *regmap = data->regmap;
> +
> +	regmap_write(regmap, RM_REG_CMM, 0x00);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(rm3100_common_remove);
> +
> +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
> new file mode 100644
> index 000000000000..b50dc5b1b30b
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> @@ -0,0 +1,66 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
> + *
> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + *
> + * i2c slave address 0x20 + SA1 << 1 + SA0.
> + */
> +
> +#include <linux/i2c.h>
> +
> +#include "rm3100.h"
> +
> +static const struct regmap_config rm3100_regmap_config = {
> +		.reg_bits = 8,
> +		.val_bits = 8,
> +
> +		.rd_table = &rm3100_readable_table,
> +		.wr_table = &rm3100_writable_table,
> +		.volatile_table = &rm3100_volatile_table,
> +
> +		.cache_type = REGCACHE_RBTREE,
> +};
> +
> +static int rm3100_probe(struct i2c_client *client)
> +{
> +	struct regmap *regmap;
> +
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
> +		return -EOPNOTSUPP;
> +
> +	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
> +	if (IS_ERR(regmap))
> +		return PTR_ERR(regmap);
> +
> +	return rm3100_common_probe(&client->dev, regmap, client->irq);
> +}
> +
> +static int rm3100_remove(struct i2c_client *client)
> +{
> +	return rm3100_common_remove(&client->dev);
> +}
> +
> +static const struct of_device_id rm3100_dt_match[] = {
> +	{ .compatible = "pni,rm3100-i2c", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> +
> +static struct i2c_driver rm3100_driver = {
> +	.driver = {
> +		.name = "rm3100-i2c",
> +		.of_match_table = rm3100_dt_match,
> +	},
> +	.probe_new = rm3100_probe,
> +	.remove = rm3100_remove,
> +};
> +module_i2c_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
> new file mode 100644
> index 000000000000..2c7dd9e3a1a2
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-spi.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
> + *
> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + */
> +
> +#include <linux/spi/spi.h>
> +
> +#include "rm3100.h"
> +
> +static const struct regmap_config rm3100_regmap_config = {
> +		.reg_bits = 8,
> +		.val_bits = 8,
> +
> +		.rd_table = &rm3100_readable_table,
> +		.wr_table = &rm3100_writable_table,
> +		.volatile_table = &rm3100_volatile_table,
> +
> +		.read_flag_mask = 0x80,
> +
> +		.cache_type = REGCACHE_RBTREE,
> +};
> +
> +static int rm3100_probe(struct spi_device *spi)
> +{
> +	struct regmap *regmap;
> +	int ret;
> +
> +	/* Actually this device supports both mode 0 and mode 3. */
> +	spi->mode = SPI_MODE_0;
> +	/* data rates cannot exceeds 1Mbits. */

exceed

> +	spi->max_speed_hz = 1000000;
> +	spi->bits_per_word = 8;
> +	ret = spi_setup(spi);
> +	if (ret)
> +		return ret;
> +
> +	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
> +	if (IS_ERR(regmap))
> +		return PTR_ERR(regmap);
> +
> +	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
> +}
> +
> +static int rm3100_remove(struct spi_device *spi)
> +{
> +	return rm3100_common_remove(&spi->dev);
> +}
> +
> +static const struct of_device_id rm3100_dt_match[] = {
> +	{ .compatible = "pni,rm3100-spi", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> +
> +static struct spi_driver rm3100_driver = {
> +		.driver = {
> +				.name = "rm3100-spi",
> +				.of_match_table = rm3100_dt_match,
> +		},
> +		.probe = rm3100_probe,
> +		.remove = rm3100_remove,
> +};
> +module_spi_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
> new file mode 100644
> index 000000000000..5e30bc0f5149
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100.h
> @@ -0,0 +1,90 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Header file for PNI RM3100 driver
> + *
> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> + */
> +
> +#ifndef RM3100_CORE_H
> +#define RM3100_CORE_H
> +
> +#include <linux/module.h>
> +#include <linux/regmap.h>
> +
> +#define RM_REG_REV_ID		0x36
> +
> +/* Cycle Count Registers MSBs and LSBs. */
> +#define RM_REG_CCXM		0x04
> +#define RM_REG_CCXL		0x05
> +#define RM_REG_CCYM		0x06
> +#define RM_REG_CCYL		0x07
> +#define RM_REG_CCZM		0x08
> +#define RM_REG_CCZL		0x09
> +
> +/* Single Measurement Mode register. */
> +#define RM_REG_POLL		0x00
> +#define RM_POLL_PMX		BIT(4)
> +#define RM_POLL_PMY		BIT(5)
> +#define RM_POLL_PMZ		BIT(6)
> +
> +/* Continues Measurement Mode register. */
> +#define RM_REG_CMM		0x01
> +#define RM_CMM_START		BIT(0)
> +#define RM_CMM_DRDM		BIT(2)
> +#define RM_CMM_PMX		BIT(4)
> +#define RM_CMM_PMY		BIT(5)
> +#define RM_CMM_PMZ		BIT(6)
> +
> +/* TiMe Rate Configuration register. */
> +#define RM_REG_TMRC		0x0B
> +#define RM_TMRC_OFFSET		0x92
> +
> +/* Result Status register. */
> +#define RM_REG_STATUS		0x34
> +#define RM_STATUS_DRDY		BIT(7)
> +
> +/* Measurement result registers. */
> +#define RM_REG_MX2		0x24
> +#define RM_REG_MX1		0x25
> +#define RM_REG_MX0		0x26
> +#define RM_REG_MY2		0x27
> +#define RM_REG_MY1		0x28
> +#define RM_REG_MY0		0x29
> +#define RM_REG_MZ2		0x2a
> +#define RM_REG_MZ1		0x2b
> +#define RM_REG_MZ0		0x2c
> +
> +#define RM_REG_HSHAKE		0x35
> +
> +#define RM_W_REG_START		RM_REG_POLL
> +#define RM_W_REG_END		RM_REG_REV_ID
> +#define RM_R_REG_START		RM_REG_POLL
> +#define RM_R_REG_END		RM_REG_HSHAKE
> +#define RM_V_REG_START		RM_REG_MX2
> +#define RM_V_REG_END		RM_REG_HSHAKE
> +
> +/* Built-In Self Test reigister. */
> +#define RM_REG_BIST		0x33
> +
> +struct rm3100_data {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct completion measuring_done;
> +	bool use_interrupt;
> +
> +	int conversion_time;
> +
> +	/* To protect consistency of every measurement and sampling
> +	 * frequency change operations.
> +	 */
> +	struct mutex lock;
> +};
> +
> +extern const struct regmap_access_table rm3100_readable_table;
> +extern const struct regmap_access_table rm3100_writable_table;
> +extern const struct regmap_access_table rm3100_volatile_table;
> +
> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> +int rm3100_common_remove(struct device *dev);
> +
> +#endif /* RM3100_CORE_H */
> 

-- 

Peter Meerwald-Stadler
Mobile: +43 664 24 44 418

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-20 13:46 ` Peter Meerwald-Stadler
@ 2018-09-20 18:05   ` Song Qiang
  2018-09-22  9:42     ` Jonathan Cameron
  2018-09-21  5:07   ` Phil Reid
  1 sibling, 1 reply; 19+ messages in thread
From: Song Qiang @ 2018-09-20 18:05 UTC (permalink / raw)
  To: Peter Meerwald-Stadler
  Cc: jic23, lars, knaack.h, robh+dt, mark.rutland, devicetree,
	linux-iio, linux-kernel

On Thu, Sep 20, 2018 at 03:46:03PM +0200, Peter Meerwald-Stadler wrote:
> On Thu, 20 Sep 2018, Song Qiang wrote:
> 
> > PNI RM3100 magnetometer is a high resolution, large signal immunity
> > magnetometer, composed of 3 single sensors and a processing chip.
> > PNI is currently not in the vendors list, so this is also adding it.
> 
> comments below
> 
> > 
> > Following functions are available:
> >  - Single-shot measurement from
> >    /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
> >  - Triggerd buffer measurement.
> >  - Both i2c and spi interface are supported.
> >  - Both interrupt and polling measurement is supported, depands on if
> >    the 'interrupts' in DT is declared.
> > 
> > Signed-off-by: Song Qiang <songqiang1304521@gmail.com>
> > ---
> >  .../bindings/iio/magnetometer/pni,rm3100.txt  |  57 +++
> >  .../devicetree/bindings/vendor-prefixes.txt   |   1 +
> >  MAINTAINERS                                   |  10 +
> >  drivers/iio/magnetometer/Kconfig              |  29 ++
> >  drivers/iio/magnetometer/Makefile             |   4 +
> >  drivers/iio/magnetometer/rm3100-core.c        | 399 ++++++++++++++++++
> >  drivers/iio/magnetometer/rm3100-i2c.c         |  66 +++
> >  drivers/iio/magnetometer/rm3100-spi.c         |  72 ++++
> >  drivers/iio/magnetometer/rm3100.h             |  90 ++++
> >  9 files changed, 728 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> >  create mode 100644 drivers/iio/magnetometer/rm3100-core.c
> >  create mode 100644 drivers/iio/magnetometer/rm3100-i2c.c
> >  create mode 100644 drivers/iio/magnetometer/rm3100-spi.c
> >  create mode 100644 drivers/iio/magnetometer/rm3100.h
> > 
> > diff --git a/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> > new file mode 100644
> > index 000000000000..d0d2063e943f
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> > @@ -0,0 +1,57 @@
> > +* PNI RM3100 9-axis magnetometer sensor
> > +
> > +I2C Bus:
> > +
> > +Required properties:
> > +
> > +- compatible : should be "pni,rm3100-i2c"
> > +- reg : the I2C address of the magnetometer
> > +
> > +Optional properties:
> > +
> > +- interrupts: data ready (DRDY) from the chip.
> > +  The interrupts can be triggered on rising edges.
> > +
> > +	Refer to interrupt-controller/interrupts.txt for generic
> > +	interrupt client node bindings.
> > +
> > +- pinctrl-*: pinctrl setup for DRDY line.
> > +
> > +Example:
> > +
> > +rm3100: rm3100@20 {
> > +	pinctrl-names = "default";
> > +	pinctrl-0 = <&rm3100_pins>;
> > +
> > +	compatible = "pni,rm3100-i2c";
> > +	reg = <0x20>;
> > +	interrupt-parent = <&gpio0>;
> > +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> > +};
> > +
> > +SPI Bus:
> > +
> > +Required properties:
> > +
> > +- compatible : should be "pni,rm3100-spi"
> > +- reg : address of sensor, usually 0 or 1.
> > +
> > +Optional properties:
> > +
> > +- interrupts: data ready (DRDY) from the chip.
> > +  The interrupts can be triggered on rising edges.
> > +
> > +	Refer to interrupt-controller/interrupts.txt for generic
> > +	interrupt client node bindings.
> > +
> > +- pinctrl-*: pinctrl setup for DRDY line, depands on archtechture.
> 
> depends
> architecture
> 

Hi Peter,

Thanks for spending time with my patch!
Sorry for my English, I'll use a spell checker next time.

> > +
> > +Example:
> > +
> > +rm3100: rm3100@0{
> > +	compatible = "pni,rm3100-spi";
> > +	reg = <0>;
> > +
> > +	interrupt-parent = <&gpio0>;
> > +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> > +};
> > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > index 41f0b97eb933..5bf3395fe9ae 100644
> > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > @@ -288,6 +288,7 @@ pine64	Pine64
> >  pixcir  PIXCIR MICROELECTRONICS Co., Ltd
> >  plathome	Plat'Home Co., Ltd.
> >  plda	PLDA
> > +pni     PNI
> >  portwell	Portwell Inc.
> >  poslab	Poslab Technology Co., Ltd.
> >  powervr	PowerVR (deprecated, use img)
> > diff --git a/MAINTAINERS b/MAINTAINERS
> > index 967ce8cdd1cc..30ee8cf98312 100644
> > --- a/MAINTAINERS
> > +++ b/MAINTAINERS
> > @@ -11393,6 +11393,16 @@ M:	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
> >  S:	Maintained
> >  F:	drivers/pnp/
> >  
> > +PNI RM3100 IIO DRIVER
> > +M:	Song Qiang <songqiang1304521@gmail.com>
> > +L:	linux-iio@vger.kernel.org
> > +S:	Maintained
> > +F:	drivers/iio/magnetometer/rm3100-core.c
> > +F:	drivers/iio/magnetometer/rm3100-i2c.c
> > +F:	drivers/iio/magnetometer/rm3100-spi.c
> > +F:	drivers/iio/magnetometer/rm3100.h
> > +F:	Documentation/devicetree/bindings/iio/magnetometer/rm3100.txt
> > +
> >  POSIX CLOCKS and TIMERS
> >  M:	Thomas Gleixner <tglx@linutronix.de>
> >  L:	linux-kernel@vger.kernel.org
> > diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
> > index ed9d776d01af..f130b866a4fc 100644
> > --- a/drivers/iio/magnetometer/Kconfig
> > +++ b/drivers/iio/magnetometer/Kconfig
> > @@ -175,4 +175,33 @@ config SENSORS_HMC5843_SPI
> >  	  - hmc5843_core (core functions)
> >  	  - hmc5843_spi (support for HMC5983)
> >  
> > +config SENSORS_RM3100
> > +	tristate
> > +	select IIO_BUFFER
> > +	select IIO_TRIGGERED_BUFFER
> > +
> > +config SENSORS_RM3100_I2C
> > +	tristate "PNI RM3100 9-Axis Magnetometer (I2C)"
> > +	depends on I2C
> > +	select SENSORS_RM3100
> > +	select REGMAP_I2C
> > +	help
> > +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
> > +
> > +	  This driver can also be compiled as a module.
> > +	  To compile this driver as a module, choose M here: the module
> > +	  will be called rm3100-i2c.
> > +
> > +config SENSORS_RM3100_SPI
> > +	tristate "PNI RM3100 9-Axis Magnetometer (SPI)"
> > +	depends on SPI_MASTER
> > +	select SENSORS_RM3100
> > +	select REGMAP_SPI
> > +	help
> > +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
> > +
> > +	  This driver can also be compiled as a module.
> > +	  To compile this driver as a module, choose M here: the module
> > +	  will be called rm3100-spi.
> > +
> >  endmenu
> > diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
> > index 664b2f866472..ba1bc34b82fa 100644
> > --- a/drivers/iio/magnetometer/Makefile
> > +++ b/drivers/iio/magnetometer/Makefile
> > @@ -24,3 +24,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
> >  obj-$(CONFIG_SENSORS_HMC5843)		+= hmc5843_core.o
> >  obj-$(CONFIG_SENSORS_HMC5843_I2C)	+= hmc5843_i2c.o
> >  obj-$(CONFIG_SENSORS_HMC5843_SPI)	+= hmc5843_spi.o
> > +
> > +obj-$(CONFIG_SENSORS_RM3100)		+= rm3100-core.o
> > +obj-$(CONFIG_SENSORS_RM3100_I2C)	+= rm3100-i2c.o
> > +obj-$(CONFIG_SENSORS_RM3100_SPI)	+= rm3100-spi.o
> > diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
> > new file mode 100644
> > index 000000000000..55d515e0fe67
> > --- /dev/null
> > +++ b/drivers/iio/magnetometer/rm3100-core.c
> > @@ -0,0 +1,399 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * PNI RM3100 9-axis geomagnetic sensor driver core.
> > + *
> > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > + *
> > + * User Manual available at
> > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > + *
> > + * TODO: Scale channel, event generaton, pm.
> 
> at least read support for _SCALE is mandatory, IMHO
> 

Okay, I'll add it in next version.

> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/slab.h>
> > +
> > +#include <linux/iio/iio.h>
> > +#include <linux/iio/triggered_buffer.h>
> > +#include <linux/iio/trigger_consumer.h>
> > +#include <linux/iio/buffer.h>
> > +#include <linux/iio/kfifo_buf.h>
> > +
> > +#include "rm3100.h"
> > +
> > +static const struct regmap_range rm3100_readable_ranges[] = {
> > +		regmap_reg_range(RM_W_REG_START, RM_W_REG_END),
> > +};
> > +
> > +const struct regmap_access_table rm3100_readable_table = {
> 
> static
> 

I was planning to let rm3100-i2c.c and rm3100-spi.c to share these 6
structures, because the only different configuration of regmap between
these two files lies in 'struct regmap_config'. To achieve this, I have
to expose these 3 structures to be referenced in rm3100-i2c.c and
rm3100-spi.c
Since *_common_probe() and *_common_remove() are exposed, I thought it
was fine to expose these structures to reduce redundant code, is this
prohibited?

> > +		.yes_ranges = rm3100_readable_ranges,
> > +		.n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> > +};
> > +
> > +static const struct regmap_range rm3100_writable_ranges[] = {
> > +		regmap_reg_range(RM_R_REG_START, RM_R_REG_END),
> > +};
> > +
> > +const struct regmap_access_table rm3100_writable_table = {
> 
> static
> 
> > +		.yes_ranges = rm3100_writable_ranges,
> > +		.n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> > +};
> > +
> > +static const struct regmap_range rm3100_volatile_ranges[] = {
> > +		regmap_reg_range(RM_V_REG_START, RM_V_REG_END),
> > +};
> > +
> > +const struct regmap_access_table rm3100_volatile_table = {
> 
> static
> 
> > +		.yes_ranges = rm3100_volatile_ranges,
> > +		.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> > +};
> > +
> > +static irqreturn_t rm3100_measurement_irq_handler(int irq, void *d)
> > +{
> > +	struct rm3100_data *data = d;
> > +
> > +	complete(&data->measuring_done);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +static int rm3100_wait_measurement(struct rm3100_data *data)
> > +{
> > +	struct regmap *regmap = data->regmap;
> > +	unsigned int val;
> > +	u16 tries = 20;
> 
> why not use int for tries?
> 

Okay.

> > +	int ret;
> > +
> > +	/* A read cycle of 400kbits i2c bus is about 20us, plus the time
> > +	 * used for schduling, a read cycle of fast mode of this device
> 
> scheduling
> 
> > +	 * can reach 1.7ms, it may be possible for data arrives just
> 
> to arrive
> 
> > +	 * after we check the RM_REG_STATUS. In this case, irq_handler is
> > +	 * called before measuring_done is reinitialized, it will wait
> > +	 * forever for a data that has already been ready.
> 
> for data
> 
> > +	 * Reinitialize measuring_done before looking up makes sure we
> > +	 * will always capture interrupt no matter when it happened.
> > +	 */
> > +	if (data->use_interrupt)
> > +		reinit_completion(&data->measuring_done);
> > +
> > +	ret = regmap_read(regmap, RM_REG_STATUS, &val);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	if ((val & RM_STATUS_DRDY) != RM_STATUS_DRDY) {
> > +		if (data->use_interrupt) {
> > +			ret = wait_for_completion_timeout(&data->measuring_done,
> > +				msecs_to_jiffies(data->conversion_time));
> > +			if (!ret)
> > +				return -ETIMEDOUT;
> > +		} else {
> > +			do {
> > +				ret = regmap_read(regmap, RM_REG_STATUS, &val);
> > +				if (ret < 0)
> > +					return ret;
> > +
> > +				if (val & RM_STATUS_DRDY)
> > +					break;
> > +
> > +				usleep_range(1000, 5000);
> > +			} while (--tries);
> > +			if (!tries)
> > +				return -ETIMEDOUT;
> > +		}
> > +	}
> > +	return 0;
> > +}
> > +
> > +static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val)
> > +{
> > +	struct regmap *regmap = data->regmap;
> > +	u8 buffer[3];
> > +	int ret;
> > +
> > +	mutex_lock(&data->lock);
> > +	ret = rm3100_wait_measurement(data);
> > +	if (ret < 0) {
> > +		mutex_unlock(&data->lock);
> > +		return ret;
> > +	}
> > +
> > +	ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * idx, buffer, 3);
> 
> sizeof(buf)
> 

Great!

> > +	mutex_unlock(&data->lock);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	*val = le32_to_cpu((buffer[0] << 16) + (buffer[1] << 8) + buffer[2]);
> 
> no need for le32_to_cpu() 
> 

I think I didn't fully understand this, I'll look into it.

> > +	*val = sign_extend32(*val, 23);
> > +
> > +	return IIO_VAL_INT;
> > +}
> > +
> > +#define RM_CHANNEL(axis, idx)					\
> 
> use RM3100_ prefix please
> 

In the last driver I wrote, name of registers are so long that I have to
suppress them as possible as I can, it seems like this one doesn't need
to. :)

> > +	{								\
> > +		.type = IIO_MAGN,					\
> > +		.modified = 1,						\
> > +		.channel2 = IIO_MOD_##axis,				\
> > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> > +		.scan_index = idx,					\
> > +		.scan_type = {						\
> > +			.sign = 's',					\
> > +			.realbits = 24,					\
> > +			.storagebits = 32,				\
> > +			.shift = 8,					\
> > +			.endianness = IIO_LE,				\
> > +		},							\
> > +	}
> > +
> > +static const struct iio_chan_spec rm3100_channels[] = {
> > +	RM_CHANNEL(X, 0),
> > +	RM_CHANNEL(Y, 1),
> > +	RM_CHANNEL(Z, 2),
> > +	IIO_CHAN_SOFT_TIMESTAMP(3),
> > +};
> > +
> > +static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
> > +
> > +#define RM_SAMP_NUM	14
> 
> prefix
> 
> > +
> > +/* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> > + * Time between reading: rm3100_sam_rates[][2]ms (The first on is actially 1.7).
> 
> one
> actually
> 1.7 what unit?
> 

It's in milliseconds. These time values are used for lookup so I do not
need to compute time between conversion from measurement frequency, and
they are used for wait time, I thought a little longer is better.
I think the comment above this structure isn't very clear, I'll find a
better way to explain it.

> 
> > + */
> > +static const int rm3100_samp_rates[RM_SAMP_NUM][3] = {
> > +	{600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27},
> > +	{18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440},
> > +	{1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300},
> > +	{0, 15000, 6700},  {0, 75000, 13000}
> > +};
> > +
> > +static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
> > +{
> > +	int ret;
> > +	int tmp;
> > +
> > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> > +	if (ret < 0)
> > +		return ret;
> > +	*val = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][0];
> 
> space around - operator
> 

Okay.

> > +	*val2 = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][1];
> > +
> > +	return IIO_VAL_INT_PLUS_MICRO;
> > +}
> > +
> > +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
> > +{
> > +	struct regmap *regmap = data->regmap;
> > +	int cycle_count;
> > +	int ret;
> > +	int i;
> > +
> > +	/* All cycle count registers use the same value. */
> > +	ret = regmap_read(regmap, RM_REG_CCXL, &cycle_count);
> 
> check ret?
> 

My fault.

> > +	if (cycle_count < 0)
> > +		return cycle_count;
> > +
> > +	for (i = 0; i < RM_SAMP_NUM; i++) {
> > +		if (val == rm3100_samp_rates[i][0] &&
> > +			val2 == rm3100_samp_rates[i][1])
> > +			break;
> > +	}
> > +
> > +	if (i != RM_SAMP_NUM) {
> > +		mutex_lock(&data->lock);
> > +		ret = regmap_write(regmap, RM_REG_TMRC, i + RM_TMRC_OFFSET);
> > +		if (ret < 0)
> 
> unlock?
> 

These actions are for changing the sampling frequency. This device
cannot start conversion if CMM register is not reset after reading from
CCX/CCY/CCZ registers. So I unlock it later since conversion should have
already been stopped and other threads should not access the bus.

> > +			return ret;
> > +
> > +		/* Checking if cycle count registers need changing. */
> > +		if (val == 600 && cycle_count == 200) {
> > +			for (i = 0; i < 3; i++) {
> > +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 100);
> > +				if (ret < 0)
> 
> unlock?
> 
> > +					return ret;
> > +			}
> > +		} else if (val != 600 && cycle_count == 100) {
> > +			for (i = 0; i < 3; i++) {
> > +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 200);
> > +				if (ret < 0)
> 
> unlock?
> 
> > +					return ret;
> > +			}
> > +		}
> > +		/* Writing TMRC registers requires CMM reset. */
> > +		ret = regmap_write(regmap, RM_REG_CMM, 0);
> > +		if (ret < 0)
> 
> unlock?
> 
> > +			return ret;
> > +		ret = regmap_write(regmap, RM_REG_CMM, RM_CMM_PMX |
> > +			RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > +		if (ret < 0)
> 
> unlock?
> 
> > +			return ret;
> > +		mutex_unlock(&data->lock);
> > +
> > +		data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> > +		return 0;
> > +	}
> > +	return -EINVAL;
> > +}
> > +
> > +static int rm3100_read_raw(struct iio_dev *indio_dev,
> > +			   const struct iio_chan_spec *chan,
> > +			   int *val, int *val2, long mask)
> > +{
> > +	struct rm3100_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_RAW:
> > +		ret = iio_device_claim_direct_mode(indio_dev);
> > +		if (ret < 0)
> 
> release_direct_mode() here?
> 

Oh..yes!

> > +			return ret;
> > +		ret = rm3100_read_mag(data, chan->scan_index, val);
> > +		iio_device_release_direct_mode(indio_dev);
> > +
> > +		return ret;
> > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > +		return ret = rm3100_get_samp_freq(data, val, val2);
> 
> return ret = ???, just
> return rm3100_...
> 

Sorry for this...

> > +	default:
> > +		return -EINVAL;
> > +	}
> > +}
> > +
> > +static int rm3100_write_raw(struct iio_dev *indio_dev,
> > +			    struct iio_chan_spec const *chan,
> > +			    int val, int val2, long mask)
> > +{
> > +	struct rm3100_data *data = iio_priv(indio_dev);
> > +	int ret;
> > +
> > +	switch (mask) {
> > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > +		ret = rm3100_set_samp_freq(data, val, val2);
> > +		if (ret < 0)
> > +			return ret;
> > +		return 0;
> > +	default:
> > +		return -EINVAL;
> > +	}
> > +
> > +}
> > +
> > +static const struct iio_info rm3100_info = {
> > +	.read_raw = rm3100_read_raw,
> > +	.write_raw = rm3100_write_raw,
> > +};
> > +
> > +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> > +{
> > +	struct iio_poll_func *pf = p;
> > +	struct iio_dev *indio_dev = pf->indio_dev;
> > +	struct rm3100_data *data = iio_priv(indio_dev);
> > +	struct regmap *regmap = data->regmap;
> > +	u8 *buffer;
> > +	int ret;
> > +	int i;
> > +
> > +	buffer = devm_kzalloc(data->dev, indio_dev->scan_bytes, GFP_KERNEL);
> 
> try to allocate the maximum needed amount of memory beforehand, in 
> _probe() perhaps 
> 

Okay.

> > +	if (!buffer)
> > +		goto done;
> > +
> > +	mutex_lock(&data->lock);
> > +	ret = rm3100_wait_measurement(data);
> > +	if (ret < 0) {
> > +		mutex_unlock(&data->lock);
> > +		goto done;
> > +	}
> > +
> > +	for (i = 0; i < 3; i++) {
> > +		ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * i,
> > +				buffer + 4 * i, 3);
> > +		if (ret < 0)
> > +			return ret;
> > +	}
> > +	mutex_unlock(&data->lock);
> > +
> > +	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
> > +			iio_get_time_ns(indio_dev));
> > +done:
> > +	iio_trigger_notify_done(indio_dev->trig);
> > +
> > +	return IRQ_HANDLED;
> > +}
> > +
> > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> > +{
> > +	struct iio_dev *indio_dev;
> > +	struct rm3100_data *data;
> > +	int tmp;
> > +	int ret;
> > +
> > +	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> > +	if (!indio_dev)
> > +		return -ENOMEM;
> > +
> > +	data = iio_priv(indio_dev);
> > +	dev_set_drvdata(dev, indio_dev);
> > +	data->dev = dev;
> > +	data->regmap = regmap;
> > +
> > +	mutex_init(&data->lock);
> > +
> > +	indio_dev->dev.parent = dev;
> > +	indio_dev->name = "rm3100";
> > +	indio_dev->info = &rm3100_info;
> > +	indio_dev->channels = rm3100_channels;
> > +	indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
> > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > +	indio_dev->available_scan_masks = rm3100_scan_masks;
> > +
> > +	if (!irq)
> > +		data->use_interrupt = false;
> > +	else {
> > +		data->use_interrupt = true;
> > +		ret = devm_request_irq(dev,
> > +			irq,
> > +			rm3100_measurement_irq_handler,
> > +			IRQF_TRIGGER_RISING,
> > +			indio_dev->name,
> > +			data);
> > +		if (ret < 0) {
> > +			dev_err(dev,
> > +			"request irq line failed.");
> 
> \n
> 
> > +			return -ret;
> > +		}
> > +		init_completion(&data->measuring_done);
> > +	}
> > +
> > +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> > +					rm3100_trigger_handler, NULL);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/* 3sec more wait time. */
> > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> 
> check ret
> 

I'll put this in my future checklist.

> > +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> > +
> > +	/* Starting all channels' conversion. */
> > +	ret = regmap_write(regmap, RM_REG_CMM,
> > +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return devm_iio_device_register(dev, indio_dev);
> > +}
> > +EXPORT_SYMBOL(rm3100_common_probe);
> > +
> > +int rm3100_common_remove(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct rm3100_data *data = iio_priv(indio_dev);
> > +	struct regmap *regmap = data->regmap;
> > +
> > +	regmap_write(regmap, RM_REG_CMM, 0x00);
> > +
> > +	return 0;
> > +}
> > +EXPORT_SYMBOL(rm3100_common_remove);
> > +
> > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
> > new file mode 100644
> > index 000000000000..b50dc5b1b30b
> > --- /dev/null
> > +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> > @@ -0,0 +1,66 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
> > + *
> > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > + *
> > + * User Manual available at
> > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > + *
> > + * i2c slave address 0x20 + SA1 << 1 + SA0.
> > + */
> > +
> > +#include <linux/i2c.h>
> > +
> > +#include "rm3100.h"
> > +
> > +static const struct regmap_config rm3100_regmap_config = {
> > +		.reg_bits = 8,
> > +		.val_bits = 8,
> > +
> > +		.rd_table = &rm3100_readable_table,
> > +		.wr_table = &rm3100_writable_table,
> > +		.volatile_table = &rm3100_volatile_table,
> > +
> > +		.cache_type = REGCACHE_RBTREE,
> > +};
> > +
> > +static int rm3100_probe(struct i2c_client *client)
> > +{
> > +	struct regmap *regmap;
> > +
> > +	if (!i2c_check_functionality(client->adapter,
> > +		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
> > +		return -EOPNOTSUPP;
> > +
> > +	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
> > +	if (IS_ERR(regmap))
> > +		return PTR_ERR(regmap);
> > +
> > +	return rm3100_common_probe(&client->dev, regmap, client->irq);
> > +}
> > +
> > +static int rm3100_remove(struct i2c_client *client)
> > +{
> > +	return rm3100_common_remove(&client->dev);
> > +}
> > +
> > +static const struct of_device_id rm3100_dt_match[] = {
> > +	{ .compatible = "pni,rm3100-i2c", },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > +
> > +static struct i2c_driver rm3100_driver = {
> > +	.driver = {
> > +		.name = "rm3100-i2c",
> > +		.of_match_table = rm3100_dt_match,
> > +	},
> > +	.probe_new = rm3100_probe,
> > +	.remove = rm3100_remove,
> > +};
> > +module_i2c_driver(rm3100_driver);
> > +
> > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
> > new file mode 100644
> > index 000000000000..2c7dd9e3a1a2
> > --- /dev/null
> > +++ b/drivers/iio/magnetometer/rm3100-spi.c
> > @@ -0,0 +1,72 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
> > + *
> > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > + *
> > + * User Manual available at
> > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > + */
> > +
> > +#include <linux/spi/spi.h>
> > +
> > +#include "rm3100.h"
> > +
> > +static const struct regmap_config rm3100_regmap_config = {
> > +		.reg_bits = 8,
> > +		.val_bits = 8,
> > +
> > +		.rd_table = &rm3100_readable_table,
> > +		.wr_table = &rm3100_writable_table,
> > +		.volatile_table = &rm3100_volatile_table,
> > +
> > +		.read_flag_mask = 0x80,
> > +
> > +		.cache_type = REGCACHE_RBTREE,
> > +};
> > +
> > +static int rm3100_probe(struct spi_device *spi)
> > +{
> > +	struct regmap *regmap;
> > +	int ret;
> > +
> > +	/* Actually this device supports both mode 0 and mode 3. */
> > +	spi->mode = SPI_MODE_0;
> > +	/* data rates cannot exceeds 1Mbits. */
> 
> exceed
> 
> > +	spi->max_speed_hz = 1000000;
> > +	spi->bits_per_word = 8;
> > +	ret = spi_setup(spi);
> > +	if (ret)
> > +		return ret;
> > +
> > +	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
> > +	if (IS_ERR(regmap))
> > +		return PTR_ERR(regmap);
> > +
> > +	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
> > +}
> > +
> > +static int rm3100_remove(struct spi_device *spi)
> > +{
> > +	return rm3100_common_remove(&spi->dev);
> > +}
> > +
> > +static const struct of_device_id rm3100_dt_match[] = {
> > +	{ .compatible = "pni,rm3100-spi", },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > +
> > +static struct spi_driver rm3100_driver = {
> > +		.driver = {
> > +				.name = "rm3100-spi",
> > +				.of_match_table = rm3100_dt_match,
> > +		},
> > +		.probe = rm3100_probe,
> > +		.remove = rm3100_remove,
> > +};
> > +module_spi_driver(rm3100_driver);
> > +
> > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
> > new file mode 100644
> > index 000000000000..5e30bc0f5149
> > --- /dev/null
> > +++ b/drivers/iio/magnetometer/rm3100.h
> > @@ -0,0 +1,90 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * Header file for PNI RM3100 driver
> > + *
> > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > + */
> > +
> > +#ifndef RM3100_CORE_H
> > +#define RM3100_CORE_H
> > +
> > +#include <linux/module.h>
> > +#include <linux/regmap.h>
> > +
> > +#define RM_REG_REV_ID		0x36
> > +
> > +/* Cycle Count Registers MSBs and LSBs. */
> > +#define RM_REG_CCXM		0x04
> > +#define RM_REG_CCXL		0x05
> > +#define RM_REG_CCYM		0x06
> > +#define RM_REG_CCYL		0x07
> > +#define RM_REG_CCZM		0x08
> > +#define RM_REG_CCZL		0x09
> > +
> > +/* Single Measurement Mode register. */
> > +#define RM_REG_POLL		0x00
> > +#define RM_POLL_PMX		BIT(4)
> > +#define RM_POLL_PMY		BIT(5)
> > +#define RM_POLL_PMZ		BIT(6)
> > +
> > +/* Continues Measurement Mode register. */
> > +#define RM_REG_CMM		0x01
> > +#define RM_CMM_START		BIT(0)
> > +#define RM_CMM_DRDM		BIT(2)
> > +#define RM_CMM_PMX		BIT(4)
> > +#define RM_CMM_PMY		BIT(5)
> > +#define RM_CMM_PMZ		BIT(6)
> > +
> > +/* TiMe Rate Configuration register. */
> > +#define RM_REG_TMRC		0x0B
> > +#define RM_TMRC_OFFSET		0x92
> > +
> > +/* Result Status register. */
> > +#define RM_REG_STATUS		0x34
> > +#define RM_STATUS_DRDY		BIT(7)
> > +
> > +/* Measurement result registers. */
> > +#define RM_REG_MX2		0x24
> > +#define RM_REG_MX1		0x25
> > +#define RM_REG_MX0		0x26
> > +#define RM_REG_MY2		0x27
> > +#define RM_REG_MY1		0x28
> > +#define RM_REG_MY0		0x29
> > +#define RM_REG_MZ2		0x2a
> > +#define RM_REG_MZ1		0x2b
> > +#define RM_REG_MZ0		0x2c
> > +
> > +#define RM_REG_HSHAKE		0x35
> > +
> > +#define RM_W_REG_START		RM_REG_POLL
> > +#define RM_W_REG_END		RM_REG_REV_ID
> > +#define RM_R_REG_START		RM_REG_POLL
> > +#define RM_R_REG_END		RM_REG_HSHAKE
> > +#define RM_V_REG_START		RM_REG_MX2
> > +#define RM_V_REG_END		RM_REG_HSHAKE
> > +
> > +/* Built-In Self Test reigister. */
> > +#define RM_REG_BIST		0x33
> > +
> > +struct rm3100_data {
> > +	struct device *dev;
> > +	struct regmap *regmap;
> > +	struct completion measuring_done;
> > +	bool use_interrupt;
> > +
> > +	int conversion_time;
> > +
> > +	/* To protect consistency of every measurement and sampling
> > +	 * frequency change operations.
> > +	 */
> > +	struct mutex lock;
> > +};
> > +
> > +extern const struct regmap_access_table rm3100_readable_table;
> > +extern const struct regmap_access_table rm3100_writable_table;
> > +extern const struct regmap_access_table rm3100_volatile_table;
> > +
> > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> > +int rm3100_common_remove(struct device *dev);
> > +
> > +#endif /* RM3100_CORE_H */
> > 
> 
> -- 
> 
> Peter Meerwald-Stadler
> Mobile: +43 664 24 44 418

yours,
Song Qiang

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-20 13:13 [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer Song Qiang
  2018-09-20 13:46 ` Peter Meerwald-Stadler
@ 2018-09-21  2:05 ` Phil Reid
  2018-09-21  9:13   ` Song Qiang
  2018-09-22 10:14 ` Jonathan Cameron
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 19+ messages in thread
From: Phil Reid @ 2018-09-21  2:05 UTC (permalink / raw)
  To: Song Qiang, jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland
  Cc: linux-iio, devicetree, linux-kernel

On 20/09/2018 9:13 PM, Song Qiang wrote:
> PNI RM3100 magnetometer is a high resolution, large signal immunity
> magnetometer, composed of 3 single sensors and a processing chip.
> PNI is currently not in the vendors list, so this is also adding it.
> 

In the subject: Isn't the RM3100 a 3axis mag.
The 9axis bit comes when you combine it with an accel / gryo I think.

... snip

> +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> @@ -0,0 +1,57 @@
> +* PNI RM3100 9-axis magnetometer sensor
> +
> +I2C Bus:
> +
> +Required properties:
> +
> +- compatible : should be "pni,rm3100-i2c"
> +- reg : the I2C address of the magnetometer
> +

... snip

> +SPI Bus:
> +
> +Required properties:
> +
> +- compatible : should be "pni,rm3100-spi"
> +- reg : address of sensor, usually 0 or 1.
> +

Looking at other drivers supporting i2c / spi.
They use the same compatible for both.

eg: see iio/accel/adxl345_*.c

and it's binding doc:

Required properties:
  - compatible : should be "adi,adxl345"
  - reg : the I2C address or SPI chip select number of the sensor


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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-20 13:46 ` Peter Meerwald-Stadler
  2018-09-20 18:05   ` Song Qiang
@ 2018-09-21  5:07   ` Phil Reid
  2018-09-21 11:29     ` Song Qiang
  1 sibling, 1 reply; 19+ messages in thread
From: Phil Reid @ 2018-09-21  5:07 UTC (permalink / raw)
  To: Peter Meerwald-Stadler, Song Qiang
  Cc: jic23, lars, linux-iio, devicetree, linux-kernel

G'day Song,

One more comment below.
On 20/09/2018 9:46 PM, Peter Meerwald-Stadler wrote:
> On Thu, 20 Sep 2018, Song Qiang wrote:
> 
>> PNI RM3100 magnetometer is a high resolution, large signal immunity
>> magnetometer, composed of 3 single sensors and a processing chip.
>> PNI is currently not in the vendors list, so this is also adding it.
> 
> comments below
> 
>>
>> Following functions are available:
>>   - Single-shot measurement from
>>     /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
>>   - Triggerd buffer measurement.
>>   - Both i2c and spi interface are supported.
>>   - Both interrupt and polling measurement is supported, depands on if
>>     the 'interrupts' in DT is declared.
>>
>> Signed-off-by: Song Qiang <songqiang1304521@gmail.com>
>> ---
>>   .../bindings/iio/magnetometer/pni,rm3100.txt  |  57 +++
>>   .../devicetree/bindings/vendor-prefixes.txt   |   1 +
>>   MAINTAINERS                                   |  10 +
>>   drivers/iio/magnetometer/Kconfig              |  29 ++
>>   drivers/iio/magnetometer/Makefile             |   4 +
>>   drivers/iio/magnetometer/rm3100-core.c        | 399 ++++++++++++++++++
>>   drivers/iio/magnetometer/rm3100-i2c.c         |  66 +++
>>   drivers/iio/magnetometer/rm3100-spi.c         |  72 ++++
>>   drivers/iio/magnetometer/rm3100.h             |  90 ++++
>>   9 files changed, 728 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
>>   create mode 100644 drivers/iio/magnetometer/rm3100-core.c
>>   create mode 100644 drivers/iio/magnetometer/rm3100-i2c.c
>>   create mode 100644 drivers/iio/magnetometer/rm3100-spi.c
>>   create mode 100644 drivers/iio/magnetometer/rm3100.h
>>
>> diff --git a/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
>> new file mode 100644
>> index 000000000000..d0d2063e943f
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
>> @@ -0,0 +1,57 @@
>> +* PNI RM3100 9-axis magnetometer sensor
>> +
>> +I2C Bus:
>> +
>> +Required properties:
>> +
>> +- compatible : should be "pni,rm3100-i2c"
>> +- reg : the I2C address of the magnetometer
>> +
>> +Optional properties:
>> +
>> +- interrupts: data ready (DRDY) from the chip.
>> +  The interrupts can be triggered on rising edges.
>> +
>> +	Refer to interrupt-controller/interrupts.txt for generic
>> +	interrupt client node bindings.
>> +
>> +- pinctrl-*: pinctrl setup for DRDY line.
>> +
>> +Example:
>> +
>> +rm3100: rm3100@20 {
>> +	pinctrl-names = "default";
>> +	pinctrl-0 = <&rm3100_pins>;
>> +
>> +	compatible = "pni,rm3100-i2c";
>> +	reg = <0x20>;
>> +	interrupt-parent = <&gpio0>;
>> +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
>> +};
>> +
>> +SPI Bus:
>> +
>> +Required properties:
>> +
>> +- compatible : should be "pni,rm3100-spi"
>> +- reg : address of sensor, usually 0 or 1.
>> +
>> +Optional properties:
>> +
>> +- interrupts: data ready (DRDY) from the chip.
>> +  The interrupts can be triggered on rising edges.
>> +
>> +	Refer to interrupt-controller/interrupts.txt for generic
>> +	interrupt client node bindings.
>> +
>> +- pinctrl-*: pinctrl setup for DRDY line, depands on archtechture.
> 
> depends
> architecture
> 
>> +
>> +Example:
>> +
>> +rm3100: rm3100@0{
>> +	compatible = "pni,rm3100-spi";
>> +	reg = <0>;
>> +
>> +	interrupt-parent = <&gpio0>;
>> +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
>> +};
>> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> index 41f0b97eb933..5bf3395fe9ae 100644
>> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
>> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
>> @@ -288,6 +288,7 @@ pine64	Pine64
>>   pixcir  PIXCIR MICROELECTRONICS Co., Ltd
>>   plathome	Plat'Home Co., Ltd.
>>   plda	PLDA
>> +pni     PNI
>>   portwell	Portwell Inc.
>>   poslab	Poslab Technology Co., Ltd.
>>   powervr	PowerVR (deprecated, use img)
>> diff --git a/MAINTAINERS b/MAINTAINERS
>> index 967ce8cdd1cc..30ee8cf98312 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -11393,6 +11393,16 @@ M:	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
>>   S:	Maintained
>>   F:	drivers/pnp/
>>   
>> +PNI RM3100 IIO DRIVER
>> +M:	Song Qiang <songqiang1304521@gmail.com>
>> +L:	linux-iio@vger.kernel.org
>> +S:	Maintained
>> +F:	drivers/iio/magnetometer/rm3100-core.c
>> +F:	drivers/iio/magnetometer/rm3100-i2c.c
>> +F:	drivers/iio/magnetometer/rm3100-spi.c
>> +F:	drivers/iio/magnetometer/rm3100.h
>> +F:	Documentation/devicetree/bindings/iio/magnetometer/rm3100.txt
>> +
>>   POSIX CLOCKS and TIMERS
>>   M:	Thomas Gleixner <tglx@linutronix.de>
>>   L:	linux-kernel@vger.kernel.org
>> diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
>> index ed9d776d01af..f130b866a4fc 100644
>> --- a/drivers/iio/magnetometer/Kconfig
>> +++ b/drivers/iio/magnetometer/Kconfig
>> @@ -175,4 +175,33 @@ config SENSORS_HMC5843_SPI
>>   	  - hmc5843_core (core functions)
>>   	  - hmc5843_spi (support for HMC5983)
>>   
>> +config SENSORS_RM3100
>> +	tristate
>> +	select IIO_BUFFER
>> +	select IIO_TRIGGERED_BUFFER
>> +
>> +config SENSORS_RM3100_I2C
>> +	tristate "PNI RM3100 9-Axis Magnetometer (I2C)"
>> +	depends on I2C
>> +	select SENSORS_RM3100
>> +	select REGMAP_I2C
>> +	help
>> +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
>> +
>> +	  This driver can also be compiled as a module.
>> +	  To compile this driver as a module, choose M here: the module
>> +	  will be called rm3100-i2c.
>> +
>> +config SENSORS_RM3100_SPI
>> +	tristate "PNI RM3100 9-Axis Magnetometer (SPI)"
>> +	depends on SPI_MASTER
>> +	select SENSORS_RM3100
>> +	select REGMAP_SPI
>> +	help
>> +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
>> +
>> +	  This driver can also be compiled as a module.
>> +	  To compile this driver as a module, choose M here: the module
>> +	  will be called rm3100-spi.
>> +
>>   endmenu
>> diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
>> index 664b2f866472..ba1bc34b82fa 100644
>> --- a/drivers/iio/magnetometer/Makefile
>> +++ b/drivers/iio/magnetometer/Makefile
>> @@ -24,3 +24,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
>>   obj-$(CONFIG_SENSORS_HMC5843)		+= hmc5843_core.o
>>   obj-$(CONFIG_SENSORS_HMC5843_I2C)	+= hmc5843_i2c.o
>>   obj-$(CONFIG_SENSORS_HMC5843_SPI)	+= hmc5843_spi.o
>> +
>> +obj-$(CONFIG_SENSORS_RM3100)		+= rm3100-core.o
>> +obj-$(CONFIG_SENSORS_RM3100_I2C)	+= rm3100-i2c.o
>> +obj-$(CONFIG_SENSORS_RM3100_SPI)	+= rm3100-spi.o
>> diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
>> new file mode 100644
>> index 000000000000..55d515e0fe67
>> --- /dev/null
>> +++ b/drivers/iio/magnetometer/rm3100-core.c
>> @@ -0,0 +1,399 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * PNI RM3100 9-axis geomagnetic sensor driver core.
>> + *
>> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
>> + *
>> + * User Manual available at
>> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
>> + *
>> + * TODO: Scale channel, event generaton, pm.
> 
> at least read support for _SCALE is mandatory, IMHO
> 
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/kfifo_buf.h>
>> +
>> +#include "rm3100.h"
>> +
>> +static const struct regmap_range rm3100_readable_ranges[] = {
>> +		regmap_reg_range(RM_W_REG_START, RM_W_REG_END),
>> +};
>> +
>> +const struct regmap_access_table rm3100_readable_table = {
> 
> static
> 
>> +		.yes_ranges = rm3100_readable_ranges,
>> +		.n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
>> +};
>> +
>> +static const struct regmap_range rm3100_writable_ranges[] = {
>> +		regmap_reg_range(RM_R_REG_START, RM_R_REG_END),
>> +};
>> +
>> +const struct regmap_access_table rm3100_writable_table = {
> 
> static
> 
>> +		.yes_ranges = rm3100_writable_ranges,
>> +		.n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
>> +};
>> +
>> +static const struct regmap_range rm3100_volatile_ranges[] = {
>> +		regmap_reg_range(RM_V_REG_START, RM_V_REG_END),
>> +};
>> +
>> +const struct regmap_access_table rm3100_volatile_table = {
> 
> static
> 
>> +		.yes_ranges = rm3100_volatile_ranges,
>> +		.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
>> +};
>> +
>> +static irqreturn_t rm3100_measurement_irq_handler(int irq, void *d)
>> +{
>> +	struct rm3100_data *data = d;
>> +
>> +	complete(&data->measuring_done);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +static int rm3100_wait_measurement(struct rm3100_data *data)
>> +{
>> +	struct regmap *regmap = data->regmap;
>> +	unsigned int val;
>> +	u16 tries = 20;
> 
> why not use int for tries?
> 
>> +	int ret;
>> +
>> +	/* A read cycle of 400kbits i2c bus is about 20us, plus the time
>> +	 * used for schduling, a read cycle of fast mode of this device
> 
> scheduling
> 
>> +	 * can reach 1.7ms, it may be possible for data arrives just
> 
> to arrive
> 
>> +	 * after we check the RM_REG_STATUS. In this case, irq_handler is
>> +	 * called before measuring_done is reinitialized, it will wait
>> +	 * forever for a data that has already been ready.
> 
> for data
> 
>> +	 * Reinitialize measuring_done before looking up makes sure we
>> +	 * will always capture interrupt no matter when it happened.
>> +	 */
>> +	if (data->use_interrupt)
>> +		reinit_completion(&data->measuring_done);
>> +
>> +	ret = regmap_read(regmap, RM_REG_STATUS, &val);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	if ((val & RM_STATUS_DRDY) != RM_STATUS_DRDY) {
>> +		if (data->use_interrupt) {
>> +			ret = wait_for_completion_timeout(&data->measuring_done,
>> +				msecs_to_jiffies(data->conversion_time));
>> +			if (!ret)
>> +				return -ETIMEDOUT;
>> +		} else {
>> +			do {
>> +				ret = regmap_read(regmap, RM_REG_STATUS, &val);
>> +				if (ret < 0)
>> +					return ret;
>> +
>> +				if (val & RM_STATUS_DRDY)
>> +					break;
>> +
>> +				usleep_range(1000, 5000);
>> +			} while (--tries);
>> +			if (!tries)
>> +				return -ETIMEDOUT;
>> +		}
>> +	}
>> +	return 0;
>> +}
>> +
>> +static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val)
>> +{
>> +	struct regmap *regmap = data->regmap;
>> +	u8 buffer[3];
>> +	int ret;
>> +
>> +	mutex_lock(&data->lock);
>> +	ret = rm3100_wait_measurement(data);
>> +	if (ret < 0) {
>> +		mutex_unlock(&data->lock);
>> +		return ret;
>> +	}
>> +
>> +	ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * idx, buffer, 3);
> 
> sizeof(buf)
> 
>> +	mutex_unlock(&data->lock);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	*val = le32_to_cpu((buffer[0] << 16) + (buffer[1] << 8) + buffer[2]);
> 
> no need for le32_to_cpu()
> 
>> +	*val = sign_extend32(*val, 23);
>> +
>> +	return IIO_VAL_INT;
>> +}
>> +
>> +#define RM_CHANNEL(axis, idx)					\
> 
> use RM3100_ prefix please
> 
>> +	{								\
>> +		.type = IIO_MAGN,					\
>> +		.modified = 1,						\
>> +		.channel2 = IIO_MOD_##axis,				\
>> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
>> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
>> +		.scan_index = idx,					\
>> +		.scan_type = {						\
>> +			.sign = 's',					\
>> +			.realbits = 24,					\
>> +			.storagebits = 32,				\
>> +			.shift = 8,					\
>> +			.endianness = IIO_LE,				\
>> +		},							\
>> +	}
>> +
>> +static const struct iio_chan_spec rm3100_channels[] = {
>> +	RM_CHANNEL(X, 0),
>> +	RM_CHANNEL(Y, 1),
>> +	RM_CHANNEL(Z, 2),
>> +	IIO_CHAN_SOFT_TIMESTAMP(3),
>> +};
>> +
>> +static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
>> +
>> +#define RM_SAMP_NUM	14
> 
> prefix
> 
>> +
>> +/* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
>> + * Time between reading: rm3100_sam_rates[][2]ms (The first on is actially 1.7).
> 
> one
> actually
> 1.7 what unit?
> 
> 
>> + */
>> +static const int rm3100_samp_rates[RM_SAMP_NUM][3] = {
>> +	{600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27},
>> +	{18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440},
>> +	{1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300},
>> +	{0, 15000, 6700},  {0, 75000, 13000}
>> +};
>> +
>> +static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
>> +{
>> +	int ret;
>> +	int tmp;
>> +
>> +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
>> +	if (ret < 0)
>> +		return ret;
>> +	*val = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][0];
> 
> space around - operator
> 
>> +	*val2 = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][1];
>> +
>> +	return IIO_VAL_INT_PLUS_MICRO;
>> +}
>> +
>> +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
>> +{
>> +	struct regmap *regmap = data->regmap;
>> +	int cycle_count;
>> +	int ret;
>> +	int i;
>> +
>> +	/* All cycle count registers use the same value. */
>> +	ret = regmap_read(regmap, RM_REG_CCXL, &cycle_count);
> 
> check ret?
> 
>> +	if (cycle_count < 0)
>> +		return cycle_count;
>> +
>> +	for (i = 0; i < RM_SAMP_NUM; i++) {
>> +		if (val == rm3100_samp_rates[i][0] &&
>> +			val2 == rm3100_samp_rates[i][1])
>> +			break;
>> +	}
>> +
>> +	if (i != RM_SAMP_NUM) {
>> +		mutex_lock(&data->lock);
>> +		ret = regmap_write(regmap, RM_REG_TMRC, i + RM_TMRC_OFFSET);
>> +		if (ret < 0)
> 
> unlock?
> 
>> +			return ret;
>> +
>> +		/* Checking if cycle count registers need changing. */
>> +		if (val == 600 && cycle_count == 200) {
>> +			for (i = 0; i < 3; i++) {
>> +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 100);
>> +				if (ret < 0)
> 
> unlock?
> 
>> +					return ret;
>> +			}
>> +		} else if (val != 600 && cycle_count == 100) {
>> +			for (i = 0; i < 3; i++) {
>> +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 200);
>> +				if (ret < 0)
> 
> unlock?
> 
>> +					return ret;
>> +			}
>> +		}
>> +		/* Writing TMRC registers requires CMM reset. */
>> +		ret = regmap_write(regmap, RM_REG_CMM, 0);
>> +		if (ret < 0)
> 
> unlock?
> 
>> +			return ret;
>> +		ret = regmap_write(regmap, RM_REG_CMM, RM_CMM_PMX |
>> +			RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
>> +		if (ret < 0)
> 
> unlock?
> 
>> +			return ret;
>> +		mutex_unlock(&data->lock);
>> +
>> +		data->conversion_time = rm3100_samp_rates[i][2] + 3000;
>> +		return 0;
>> +	}
>> +	return -EINVAL;
>> +}
>> +
>> +static int rm3100_read_raw(struct iio_dev *indio_dev,
>> +			   const struct iio_chan_spec *chan,
>> +			   int *val, int *val2, long mask)
>> +{
>> +	struct rm3100_data *data = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_RAW:
>> +		ret = iio_device_claim_direct_mode(indio_dev);
>> +		if (ret < 0)
> 
> release_direct_mode() here?
> 
>> +			return ret;
>> +		ret = rm3100_read_mag(data, chan->scan_index, val);
>> +		iio_device_release_direct_mode(indio_dev);
>> +
>> +		return ret;
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		return ret = rm3100_get_samp_freq(data, val, val2);
> 
> return ret = ???, just
> return rm3100_...
> 
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +}
>> +
>> +static int rm3100_write_raw(struct iio_dev *indio_dev,
>> +			    struct iio_chan_spec const *chan,
>> +			    int val, int val2, long mask)
>> +{
>> +	struct rm3100_data *data = iio_priv(indio_dev);
>> +	int ret;
>> +
>> +	switch (mask) {
>> +	case IIO_CHAN_INFO_SAMP_FREQ:
>> +		ret = rm3100_set_samp_freq(data, val, val2);
>> +		if (ret < 0)
>> +			return ret;
>> +		return 0;
>> +	default:
>> +		return -EINVAL;
>> +	}
>> +
>> +}
>> +
>> +static const struct iio_info rm3100_info = {
>> +	.read_raw = rm3100_read_raw,
>> +	.write_raw = rm3100_write_raw,
>> +};
>> +
>> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
>> +{
>> +	struct iio_poll_func *pf = p;
>> +	struct iio_dev *indio_dev = pf->indio_dev;
>> +	struct rm3100_data *data = iio_priv(indio_dev);
>> +	struct regmap *regmap = data->regmap;
>> +	u8 *buffer;
>> +	int ret;
>> +	int i;
>> +
>> +	buffer = devm_kzalloc(data->dev, indio_dev->scan_bytes, GFP_KERNEL);
> 
> try to allocate the maximum needed amount of memory beforehand, in
> _probe() perhaps
> 
>> +	if (!buffer)
>> +		goto done;
>> +
>> +	mutex_lock(&data->lock);
>> +	ret = rm3100_wait_measurement(data);
>> +	if (ret < 0) {
>> +		mutex_unlock(&data->lock);
>> +		goto done;
>> +	}
>> +
>> +	for (i = 0; i < 3; i++) {
>> +		ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * i,
>> +				buffer + 4 * i, 3);
>> +		if (ret < 0)
>> +			return ret;
>> +	}

Wouldn't it be better to read the 3 axis with one transaction here.
And if required shuffle the data into the iio buffer.


>> +	mutex_unlock(&data->lock);
>> +
>> +	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
>> +			iio_get_time_ns(indio_dev));
>> +done:
>> +	iio_trigger_notify_done(indio_dev->trig);
>> +
>> +	return IRQ_HANDLED;
>> +}
>> +
>> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
>> +{
>> +	struct iio_dev *indio_dev;
>> +	struct rm3100_data *data;
>> +	int tmp;
>> +	int ret;
>> +
>> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
>> +	if (!indio_dev)
>> +		return -ENOMEM;
>> +
>> +	data = iio_priv(indio_dev);
>> +	dev_set_drvdata(dev, indio_dev);
>> +	data->dev = dev;
>> +	data->regmap = regmap;
>> +
>> +	mutex_init(&data->lock);
>> +
>> +	indio_dev->dev.parent = dev;
>> +	indio_dev->name = "rm3100";
>> +	indio_dev->info = &rm3100_info;
>> +	indio_dev->channels = rm3100_channels;
>> +	indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
>> +	indio_dev->modes = INDIO_DIRECT_MODE;
>> +	indio_dev->available_scan_masks = rm3100_scan_masks;
>> +
>> +	if (!irq)
>> +		data->use_interrupt = false;
>> +	else {
>> +		data->use_interrupt = true;
>> +		ret = devm_request_irq(dev,
>> +			irq,
>> +			rm3100_measurement_irq_handler,
>> +			IRQF_TRIGGER_RISING,
>> +			indio_dev->name,
>> +			data);
>> +		if (ret < 0) {
>> +			dev_err(dev,
>> +			"request irq line failed.");
> 
> \n
> 
>> +			return -ret;
>> +		}
>> +		init_completion(&data->measuring_done);
>> +	}
>> +
>> +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
>> +					rm3100_trigger_handler, NULL);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	/* 3sec more wait time. */
>> +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> 
> check ret
> 
>> +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
>> +
>> +	/* Starting all channels' conversion. */
>> +	ret = regmap_write(regmap, RM_REG_CMM,
>> +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
>> +	if (ret < 0)
>> +		return ret;
>> +
>> +	return devm_iio_device_register(dev, indio_dev);
>> +}
>> +EXPORT_SYMBOL(rm3100_common_probe);
>> +
>> +int rm3100_common_remove(struct device *dev)
>> +{
>> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
>> +	struct rm3100_data *data = iio_priv(indio_dev);
>> +	struct regmap *regmap = data->regmap;
>> +
>> +	regmap_write(regmap, RM_REG_CMM, 0x00);
>> +
>> +	return 0;
>> +}
>> +EXPORT_SYMBOL(rm3100_common_remove);
>> +
>> +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
>> +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
>> new file mode 100644
>> index 000000000000..b50dc5b1b30b
>> --- /dev/null
>> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
>> @@ -0,0 +1,66 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
>> + *
>> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
>> + *
>> + * User Manual available at
>> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
>> + *
>> + * i2c slave address 0x20 + SA1 << 1 + SA0.
>> + */
>> +
>> +#include <linux/i2c.h>
>> +
>> +#include "rm3100.h"
>> +
>> +static const struct regmap_config rm3100_regmap_config = {
>> +		.reg_bits = 8,
>> +		.val_bits = 8,
>> +
>> +		.rd_table = &rm3100_readable_table,
>> +		.wr_table = &rm3100_writable_table,
>> +		.volatile_table = &rm3100_volatile_table,
>> +
>> +		.cache_type = REGCACHE_RBTREE,
>> +};
>> +
>> +static int rm3100_probe(struct i2c_client *client)
>> +{
>> +	struct regmap *regmap;
>> +
>> +	if (!i2c_check_functionality(client->adapter,
>> +		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
>> +		return -EOPNOTSUPP;
>> +
>> +	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
>> +	if (IS_ERR(regmap))
>> +		return PTR_ERR(regmap);
>> +
>> +	return rm3100_common_probe(&client->dev, regmap, client->irq);
>> +}
>> +
>> +static int rm3100_remove(struct i2c_client *client)
>> +{
>> +	return rm3100_common_remove(&client->dev);
>> +}
>> +
>> +static const struct of_device_id rm3100_dt_match[] = {
>> +	{ .compatible = "pni,rm3100-i2c", },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
>> +
>> +static struct i2c_driver rm3100_driver = {
>> +	.driver = {
>> +		.name = "rm3100-i2c",
>> +		.of_match_table = rm3100_dt_match,
>> +	},
>> +	.probe_new = rm3100_probe,
>> +	.remove = rm3100_remove,
>> +};
>> +module_i2c_driver(rm3100_driver);
>> +
>> +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
>> +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
>> new file mode 100644
>> index 000000000000..2c7dd9e3a1a2
>> --- /dev/null
>> +++ b/drivers/iio/magnetometer/rm3100-spi.c
>> @@ -0,0 +1,72 @@
>> +// SPDX-License-Identifier: GPL-2.0+
>> +/*
>> + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
>> + *
>> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
>> + *
>> + * User Manual available at
>> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
>> + */
>> +
>> +#include <linux/spi/spi.h>
>> +
>> +#include "rm3100.h"
>> +
>> +static const struct regmap_config rm3100_regmap_config = {
>> +		.reg_bits = 8,
>> +		.val_bits = 8,
>> +
>> +		.rd_table = &rm3100_readable_table,
>> +		.wr_table = &rm3100_writable_table,
>> +		.volatile_table = &rm3100_volatile_table,
>> +
>> +		.read_flag_mask = 0x80,
>> +
>> +		.cache_type = REGCACHE_RBTREE,
>> +};
>> +
>> +static int rm3100_probe(struct spi_device *spi)
>> +{
>> +	struct regmap *regmap;
>> +	int ret;
>> +
>> +	/* Actually this device supports both mode 0 and mode 3. */
>> +	spi->mode = SPI_MODE_0;
>> +	/* data rates cannot exceeds 1Mbits. */
> 
> exceed
> 
>> +	spi->max_speed_hz = 1000000;
>> +	spi->bits_per_word = 8;
>> +	ret = spi_setup(spi);
>> +	if (ret)
>> +		return ret;
>> +
>> +	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
>> +	if (IS_ERR(regmap))
>> +		return PTR_ERR(regmap);
>> +
>> +	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
>> +}
>> +
>> +static int rm3100_remove(struct spi_device *spi)
>> +{
>> +	return rm3100_common_remove(&spi->dev);
>> +}
>> +
>> +static const struct of_device_id rm3100_dt_match[] = {
>> +	{ .compatible = "pni,rm3100-spi", },
>> +	{ }
>> +};
>> +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
>> +
>> +static struct spi_driver rm3100_driver = {
>> +		.driver = {
>> +				.name = "rm3100-spi",
>> +				.of_match_table = rm3100_dt_match,
>> +		},
>> +		.probe = rm3100_probe,
>> +		.remove = rm3100_remove,
>> +};
>> +module_spi_driver(rm3100_driver);
>> +
>> +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
>> +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
>> new file mode 100644
>> index 000000000000..5e30bc0f5149
>> --- /dev/null
>> +++ b/drivers/iio/magnetometer/rm3100.h
>> @@ -0,0 +1,90 @@
>> +/* SPDX-License-Identifier: GPL-2.0+ */
>> +/*
>> + * Header file for PNI RM3100 driver
>> + *
>> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
>> + */
>> +
>> +#ifndef RM3100_CORE_H
>> +#define RM3100_CORE_H
>> +
>> +#include <linux/module.h>
>> +#include <linux/regmap.h>
>> +
>> +#define RM_REG_REV_ID		0x36
>> +
>> +/* Cycle Count Registers MSBs and LSBs. */
>> +#define RM_REG_CCXM		0x04
>> +#define RM_REG_CCXL		0x05
>> +#define RM_REG_CCYM		0x06
>> +#define RM_REG_CCYL		0x07
>> +#define RM_REG_CCZM		0x08
>> +#define RM_REG_CCZL		0x09
>> +
>> +/* Single Measurement Mode register. */
>> +#define RM_REG_POLL		0x00
>> +#define RM_POLL_PMX		BIT(4)
>> +#define RM_POLL_PMY		BIT(5)
>> +#define RM_POLL_PMZ		BIT(6)
>> +
>> +/* Continues Measurement Mode register. */
>> +#define RM_REG_CMM		0x01
>> +#define RM_CMM_START		BIT(0)
>> +#define RM_CMM_DRDM		BIT(2)
>> +#define RM_CMM_PMX		BIT(4)
>> +#define RM_CMM_PMY		BIT(5)
>> +#define RM_CMM_PMZ		BIT(6)
>> +
>> +/* TiMe Rate Configuration register. */
>> +#define RM_REG_TMRC		0x0B
>> +#define RM_TMRC_OFFSET		0x92
>> +
>> +/* Result Status register. */
>> +#define RM_REG_STATUS		0x34
>> +#define RM_STATUS_DRDY		BIT(7)
>> +
>> +/* Measurement result registers. */
>> +#define RM_REG_MX2		0x24
>> +#define RM_REG_MX1		0x25
>> +#define RM_REG_MX0		0x26
>> +#define RM_REG_MY2		0x27
>> +#define RM_REG_MY1		0x28
>> +#define RM_REG_MY0		0x29
>> +#define RM_REG_MZ2		0x2a
>> +#define RM_REG_MZ1		0x2b
>> +#define RM_REG_MZ0		0x2c
>> +
>> +#define RM_REG_HSHAKE		0x35
>> +
>> +#define RM_W_REG_START		RM_REG_POLL
>> +#define RM_W_REG_END		RM_REG_REV_ID
>> +#define RM_R_REG_START		RM_REG_POLL
>> +#define RM_R_REG_END		RM_REG_HSHAKE
>> +#define RM_V_REG_START		RM_REG_MX2
>> +#define RM_V_REG_END		RM_REG_HSHAKE
>> +
>> +/* Built-In Self Test reigister. */
>> +#define RM_REG_BIST		0x33
>> +
>> +struct rm3100_data {
>> +	struct device *dev;
>> +	struct regmap *regmap;
>> +	struct completion measuring_done;
>> +	bool use_interrupt;
>> +
>> +	int conversion_time;
>> +
>> +	/* To protect consistency of every measurement and sampling
>> +	 * frequency change operations.
>> +	 */
>> +	struct mutex lock;
>> +};
>> +
>> +extern const struct regmap_access_table rm3100_readable_table;
>> +extern const struct regmap_access_table rm3100_writable_table;
>> +extern const struct regmap_access_table rm3100_volatile_table;
>> +
>> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
>> +int rm3100_common_remove(struct device *dev);
>> +
>> +#endif /* RM3100_CORE_H */
>>
> 


-- 
Regards
Phil Reid


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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-21  2:05 ` Phil Reid
@ 2018-09-21  9:13   ` Song Qiang
  0 siblings, 0 replies; 19+ messages in thread
From: Song Qiang @ 2018-09-21  9:13 UTC (permalink / raw)
  To: Phil Reid
  Cc: jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland, linux-iio,
	devicetree, linux-kernel

On Fri, Sep 21, 2018 at 10:05:38AM +0800, Phil Reid wrote:
> On 20/09/2018 9:13 PM, Song Qiang wrote:
> > PNI RM3100 magnetometer is a high resolution, large signal immunity
> > magnetometer, composed of 3 single sensors and a processing chip.
> > PNI is currently not in the vendors list, so this is also adding it.
> > 
> 
> In the subject: Isn't the RM3100 a 3axis mag.
> The 9axis bit comes when you combine it with an accel / gryo I think.
> 
> ... snip
> 

Hi Phil,

That's right!
The first time I got this sensor I googled it on the internet and the
abstract of the first webpage told me that it's a 9-axis sensor, and
it's from its offcial website! I just googled it and saw it's still
there, but after I entered the website, there isn't anything on the
webpage about '9-axis', maybe I just saw the old wrong web cache of 
google. Newer websites do say it's a 3-axis magnetometer sensor, and 
it only has three channels of data can be read. 3-axis it is.

> > +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> > @@ -0,0 +1,57 @@
> > +* PNI RM3100 9-axis magnetometer sensor
> > +
> > +I2C Bus:
> > +
> > +Required properties:
> > +
> > +- compatible : should be "pni,rm3100-i2c"
> > +- reg : the I2C address of the magnetometer
> > +
> 
> ... snip
> 
> > +SPI Bus:
> > +
> > +Required properties:
> > +
> > +- compatible : should be "pni,rm3100-spi"
> > +- reg : address of sensor, usually 0 or 1.
> > +
> 
> Looking at other drivers supporting i2c / spi.
> They use the same compatible for both.
> 
> eg: see iio/accel/adxl345_*.c
> 
> and it's binding doc:
> 
> Required properties:
>  - compatible : should be "adi,adxl345"
>  - reg : the I2C address or SPI chip select number of the sensor
> 

Agreed, since nodes of this sensor should be already on the bus where it
should be in DT, there's no need for compitable string to identify which
bus it's on.

yours,
Song Qiang

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-21  5:07   ` Phil Reid
@ 2018-09-21 11:29     ` Song Qiang
  2018-09-21 12:20       ` Jonathan Cameron
  0 siblings, 1 reply; 19+ messages in thread
From: Song Qiang @ 2018-09-21 11:29 UTC (permalink / raw)
  To: Phil Reid
  Cc: Peter Meerwald-Stadler, jic23, lars, linux-iio, devicetree, linux-kernel

On Fri, Sep 21, 2018 at 01:07:17PM +0800, Phil Reid wrote:
> G'day Song,
> 
> One more comment below.
> On 20/09/2018 9:46 PM, Peter Meerwald-Stadler wrote:
> > On Thu, 20 Sep 2018, Song Qiang wrote:
> > 
> > > PNI RM3100 magnetometer is a high resolution, large signal immunity
> > > magnetometer, composed of 3 single sensors and a processing chip.
> > > PNI is currently not in the vendors list, so this is also adding it.
> > 
> > comments below
> > 
> > > 
> > > Following functions are available:
> > >   - Single-shot measurement from
> > >     /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
> > >   - Triggerd buffer measurement.
> > >   - Both i2c and spi interface are supported.
> > >   - Both interrupt and polling measurement is supported, depands on if
> > >     the 'interrupts' in DT is declared.
> > > 
> > > Signed-off-by: Song Qiang <songqiang1304521@gmail.com>
> > > ---
> > >   .../bindings/iio/magnetometer/pni,rm3100.txt  |  57 +++
> > >   .../devicetree/bindings/vendor-prefixes.txt   |   1 +
> > >   MAINTAINERS                                   |  10 +
> > >   drivers/iio/magnetometer/Kconfig              |  29 ++
> > >   drivers/iio/magnetometer/Makefile             |   4 +
> > >   drivers/iio/magnetometer/rm3100-core.c        | 399 ++++++++++++++++++
> > >   drivers/iio/magnetometer/rm3100-i2c.c         |  66 +++
> > >   drivers/iio/magnetometer/rm3100-spi.c         |  72 ++++
> > >   drivers/iio/magnetometer/rm3100.h             |  90 ++++
> > >   9 files changed, 728 insertions(+)
> > >   create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> > >   create mode 100644 drivers/iio/magnetometer/rm3100-core.c
> > >   create mode 100644 drivers/iio/magnetometer/rm3100-i2c.c
> > >   create mode 100644 drivers/iio/magnetometer/rm3100-spi.c
> > >   create mode 100644 drivers/iio/magnetometer/rm3100.h
> > > 
> > > diff --git a/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> > > new file mode 100644
> > > index 000000000000..d0d2063e943f
> > > --- /dev/null
> > > +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> > > @@ -0,0 +1,57 @@
> > > +* PNI RM3100 9-axis magnetometer sensor
> > > +
> > > +I2C Bus:
> > > +
> > > +Required properties:
> > > +
> > > +- compatible : should be "pni,rm3100-i2c"
> > > +- reg : the I2C address of the magnetometer
> > > +
> > > +Optional properties:
> > > +
> > > +- interrupts: data ready (DRDY) from the chip.
> > > +  The interrupts can be triggered on rising edges.
> > > +
> > > +	Refer to interrupt-controller/interrupts.txt for generic
> > > +	interrupt client node bindings.
> > > +
> > > +- pinctrl-*: pinctrl setup for DRDY line.
> > > +
> > > +Example:
> > > +
> > > +rm3100: rm3100@20 {
> > > +	pinctrl-names = "default";
> > > +	pinctrl-0 = <&rm3100_pins>;
> > > +
> > > +	compatible = "pni,rm3100-i2c";
> > > +	reg = <0x20>;
> > > +	interrupt-parent = <&gpio0>;
> > > +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> > > +};
> > > +
> > > +SPI Bus:
> > > +
> > > +Required properties:
> > > +
> > > +- compatible : should be "pni,rm3100-spi"
> > > +- reg : address of sensor, usually 0 or 1.
> > > +
> > > +Optional properties:
> > > +
> > > +- interrupts: data ready (DRDY) from the chip.
> > > +  The interrupts can be triggered on rising edges.
> > > +
> > > +	Refer to interrupt-controller/interrupts.txt for generic
> > > +	interrupt client node bindings.
> > > +
> > > +- pinctrl-*: pinctrl setup for DRDY line, depands on archtechture.
> > 
> > depends
> > architecture
> > 
> > > +
> > > +Example:
> > > +
> > > +rm3100: rm3100@0{
> > > +	compatible = "pni,rm3100-spi";
> > > +	reg = <0>;
> > > +
> > > +	interrupt-parent = <&gpio0>;
> > > +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> > > +};
> > > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > > index 41f0b97eb933..5bf3395fe9ae 100644
> > > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> > > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > > @@ -288,6 +288,7 @@ pine64	Pine64
> > >   pixcir  PIXCIR MICROELECTRONICS Co., Ltd
> > >   plathome	Plat'Home Co., Ltd.
> > >   plda	PLDA
> > > +pni     PNI
> > >   portwell	Portwell Inc.
> > >   poslab	Poslab Technology Co., Ltd.
> > >   powervr	PowerVR (deprecated, use img)
> > > diff --git a/MAINTAINERS b/MAINTAINERS
> > > index 967ce8cdd1cc..30ee8cf98312 100644
> > > --- a/MAINTAINERS
> > > +++ b/MAINTAINERS
> > > @@ -11393,6 +11393,16 @@ M:	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
> > >   S:	Maintained
> > >   F:	drivers/pnp/
> > > +PNI RM3100 IIO DRIVER
> > > +M:	Song Qiang <songqiang1304521@gmail.com>
> > > +L:	linux-iio@vger.kernel.org
> > > +S:	Maintained
> > > +F:	drivers/iio/magnetometer/rm3100-core.c
> > > +F:	drivers/iio/magnetometer/rm3100-i2c.c
> > > +F:	drivers/iio/magnetometer/rm3100-spi.c
> > > +F:	drivers/iio/magnetometer/rm3100.h
> > > +F:	Documentation/devicetree/bindings/iio/magnetometer/rm3100.txt
> > > +
> > >   POSIX CLOCKS and TIMERS
> > >   M:	Thomas Gleixner <tglx@linutronix.de>
> > >   L:	linux-kernel@vger.kernel.org
> > > diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
> > > index ed9d776d01af..f130b866a4fc 100644
> > > --- a/drivers/iio/magnetometer/Kconfig
> > > +++ b/drivers/iio/magnetometer/Kconfig
> > > @@ -175,4 +175,33 @@ config SENSORS_HMC5843_SPI
> > >   	  - hmc5843_core (core functions)
> > >   	  - hmc5843_spi (support for HMC5983)
> > > +config SENSORS_RM3100
> > > +	tristate
> > > +	select IIO_BUFFER
> > > +	select IIO_TRIGGERED_BUFFER
> > > +
> > > +config SENSORS_RM3100_I2C
> > > +	tristate "PNI RM3100 9-Axis Magnetometer (I2C)"
> > > +	depends on I2C
> > > +	select SENSORS_RM3100
> > > +	select REGMAP_I2C
> > > +	help
> > > +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
> > > +
> > > +	  This driver can also be compiled as a module.
> > > +	  To compile this driver as a module, choose M here: the module
> > > +	  will be called rm3100-i2c.
> > > +
> > > +config SENSORS_RM3100_SPI
> > > +	tristate "PNI RM3100 9-Axis Magnetometer (SPI)"
> > > +	depends on SPI_MASTER
> > > +	select SENSORS_RM3100
> > > +	select REGMAP_SPI
> > > +	help
> > > +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
> > > +
> > > +	  This driver can also be compiled as a module.
> > > +	  To compile this driver as a module, choose M here: the module
> > > +	  will be called rm3100-spi.
> > > +
> > >   endmenu
> > > diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
> > > index 664b2f866472..ba1bc34b82fa 100644
> > > --- a/drivers/iio/magnetometer/Makefile
> > > +++ b/drivers/iio/magnetometer/Makefile
> > > @@ -24,3 +24,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
> > >   obj-$(CONFIG_SENSORS_HMC5843)		+= hmc5843_core.o
> > >   obj-$(CONFIG_SENSORS_HMC5843_I2C)	+= hmc5843_i2c.o
> > >   obj-$(CONFIG_SENSORS_HMC5843_SPI)	+= hmc5843_spi.o
> > > +
> > > +obj-$(CONFIG_SENSORS_RM3100)		+= rm3100-core.o
> > > +obj-$(CONFIG_SENSORS_RM3100_I2C)	+= rm3100-i2c.o
> > > +obj-$(CONFIG_SENSORS_RM3100_SPI)	+= rm3100-spi.o
> > > diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
> > > new file mode 100644
> > > index 000000000000..55d515e0fe67
> > > --- /dev/null
> > > +++ b/drivers/iio/magnetometer/rm3100-core.c
> > > @@ -0,0 +1,399 @@
> > > +// SPDX-License-Identifier: GPL-2.0+
> > > +/*
> > > + * PNI RM3100 9-axis geomagnetic sensor driver core.
> > > + *
> > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > + *
> > > + * User Manual available at
> > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > + *
> > > + * TODO: Scale channel, event generaton, pm.
> > 
> > at least read support for _SCALE is mandatory, IMHO
> > 
> > > + */
> > > +
> > > +#include <linux/delay.h>
> > > +#include <linux/interrupt.h>
> > > +#include <linux/slab.h>
> > > +
> > > +#include <linux/iio/iio.h>
> > > +#include <linux/iio/triggered_buffer.h>
> > > +#include <linux/iio/trigger_consumer.h>
> > > +#include <linux/iio/buffer.h>
> > > +#include <linux/iio/kfifo_buf.h>
> > > +
> > > +#include "rm3100.h"
> > > +
> > > +static const struct regmap_range rm3100_readable_ranges[] = {
> > > +		regmap_reg_range(RM_W_REG_START, RM_W_REG_END),
> > > +};
> > > +
> > > +const struct regmap_access_table rm3100_readable_table = {
> > 
> > static
> > 
> > > +		.yes_ranges = rm3100_readable_ranges,
> > > +		.n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> > > +};
> > > +
> > > +static const struct regmap_range rm3100_writable_ranges[] = {
> > > +		regmap_reg_range(RM_R_REG_START, RM_R_REG_END),
> > > +};
> > > +
> > > +const struct regmap_access_table rm3100_writable_table = {
> > 
> > static
> > 
> > > +		.yes_ranges = rm3100_writable_ranges,
> > > +		.n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> > > +};
> > > +
> > > +static const struct regmap_range rm3100_volatile_ranges[] = {
> > > +		regmap_reg_range(RM_V_REG_START, RM_V_REG_END),
> > > +};
> > > +
> > > +const struct regmap_access_table rm3100_volatile_table = {
> > 
> > static
> > 
> > > +		.yes_ranges = rm3100_volatile_ranges,
> > > +		.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> > > +};
> > > +
> > > +static irqreturn_t rm3100_measurement_irq_handler(int irq, void *d)
> > > +{
> > > +	struct rm3100_data *data = d;
> > > +
> > > +	complete(&data->measuring_done);
> > > +
> > > +	return IRQ_HANDLED;
> > > +}
> > > +
> > > +static int rm3100_wait_measurement(struct rm3100_data *data)
> > > +{
> > > +	struct regmap *regmap = data->regmap;
> > > +	unsigned int val;
> > > +	u16 tries = 20;
> > 
> > why not use int for tries?
> > 
> > > +	int ret;
> > > +
> > > +	/* A read cycle of 400kbits i2c bus is about 20us, plus the time
> > > +	 * used for schduling, a read cycle of fast mode of this device
> > 
> > scheduling
> > 
> > > +	 * can reach 1.7ms, it may be possible for data arrives just
> > 
> > to arrive
> > 
> > > +	 * after we check the RM_REG_STATUS. In this case, irq_handler is
> > > +	 * called before measuring_done is reinitialized, it will wait
> > > +	 * forever for a data that has already been ready.
> > 
> > for data
> > 
> > > +	 * Reinitialize measuring_done before looking up makes sure we
> > > +	 * will always capture interrupt no matter when it happened.
> > > +	 */
> > > +	if (data->use_interrupt)
> > > +		reinit_completion(&data->measuring_done);
> > > +
> > > +	ret = regmap_read(regmap, RM_REG_STATUS, &val);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	if ((val & RM_STATUS_DRDY) != RM_STATUS_DRDY) {
> > > +		if (data->use_interrupt) {
> > > +			ret = wait_for_completion_timeout(&data->measuring_done,
> > > +				msecs_to_jiffies(data->conversion_time));
> > > +			if (!ret)
> > > +				return -ETIMEDOUT;
> > > +		} else {
> > > +			do {
> > > +				ret = regmap_read(regmap, RM_REG_STATUS, &val);
> > > +				if (ret < 0)
> > > +					return ret;
> > > +
> > > +				if (val & RM_STATUS_DRDY)
> > > +					break;
> > > +
> > > +				usleep_range(1000, 5000);
> > > +			} while (--tries);
> > > +			if (!tries)
> > > +				return -ETIMEDOUT;
> > > +		}
> > > +	}
> > > +	return 0;
> > > +}
> > > +
> > > +static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val)
> > > +{
> > > +	struct regmap *regmap = data->regmap;
> > > +	u8 buffer[3];
> > > +	int ret;
> > > +
> > > +	mutex_lock(&data->lock);
> > > +	ret = rm3100_wait_measurement(data);
> > > +	if (ret < 0) {
> > > +		mutex_unlock(&data->lock);
> > > +		return ret;
> > > +	}
> > > +
> > > +	ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * idx, buffer, 3);
> > 
> > sizeof(buf)
> > 
> > > +	mutex_unlock(&data->lock);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	*val = le32_to_cpu((buffer[0] << 16) + (buffer[1] << 8) + buffer[2]);
> > 
> > no need for le32_to_cpu()
> > 
> > > +	*val = sign_extend32(*val, 23);
> > > +
> > > +	return IIO_VAL_INT;
> > > +}
> > > +
> > > +#define RM_CHANNEL(axis, idx)					\
> > 
> > use RM3100_ prefix please
> > 
> > > +	{								\
> > > +		.type = IIO_MAGN,					\
> > > +		.modified = 1,						\
> > > +		.channel2 = IIO_MOD_##axis,				\
> > > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> > > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> > > +		.scan_index = idx,					\
> > > +		.scan_type = {						\
> > > +			.sign = 's',					\
> > > +			.realbits = 24,					\
> > > +			.storagebits = 32,				\
> > > +			.shift = 8,					\
> > > +			.endianness = IIO_LE,				\
> > > +		},							\
> > > +	}
> > > +
> > > +static const struct iio_chan_spec rm3100_channels[] = {
> > > +	RM_CHANNEL(X, 0),
> > > +	RM_CHANNEL(Y, 1),
> > > +	RM_CHANNEL(Z, 2),
> > > +	IIO_CHAN_SOFT_TIMESTAMP(3),
> > > +};
> > > +
> > > +static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
> > > +
> > > +#define RM_SAMP_NUM	14
> > 
> > prefix
> > 
> > > +
> > > +/* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> > > + * Time between reading: rm3100_sam_rates[][2]ms (The first on is actially 1.7).
> > 
> > one
> > actually
> > 1.7 what unit?
> > 
> > 
> > > + */
> > > +static const int rm3100_samp_rates[RM_SAMP_NUM][3] = {
> > > +	{600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27},
> > > +	{18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440},
> > > +	{1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300},
> > > +	{0, 15000, 6700},  {0, 75000, 13000}
> > > +};
> > > +
> > > +static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
> > > +{
> > > +	int ret;
> > > +	int tmp;
> > > +
> > > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +	*val = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][0];
> > 
> > space around - operator
> > 
> > > +	*val2 = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][1];
> > > +
> > > +	return IIO_VAL_INT_PLUS_MICRO;
> > > +}
> > > +
> > > +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
> > > +{
> > > +	struct regmap *regmap = data->regmap;
> > > +	int cycle_count;
> > > +	int ret;
> > > +	int i;
> > > +
> > > +	/* All cycle count registers use the same value. */
> > > +	ret = regmap_read(regmap, RM_REG_CCXL, &cycle_count);
> > 
> > check ret?
> > 
> > > +	if (cycle_count < 0)
> > > +		return cycle_count;
> > > +
> > > +	for (i = 0; i < RM_SAMP_NUM; i++) {
> > > +		if (val == rm3100_samp_rates[i][0] &&
> > > +			val2 == rm3100_samp_rates[i][1])
> > > +			break;
> > > +	}
> > > +
> > > +	if (i != RM_SAMP_NUM) {
> > > +		mutex_lock(&data->lock);
> > > +		ret = regmap_write(regmap, RM_REG_TMRC, i + RM_TMRC_OFFSET);
> > > +		if (ret < 0)
> > 
> > unlock?
> > 
> > > +			return ret;
> > > +
> > > +		/* Checking if cycle count registers need changing. */
> > > +		if (val == 600 && cycle_count == 200) {
> > > +			for (i = 0; i < 3; i++) {
> > > +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 100);
> > > +				if (ret < 0)
> > 
> > unlock?
> > 
> > > +					return ret;
> > > +			}
> > > +		} else if (val != 600 && cycle_count == 100) {
> > > +			for (i = 0; i < 3; i++) {
> > > +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 200);
> > > +				if (ret < 0)
> > 
> > unlock?
> > 
> > > +					return ret;
> > > +			}
> > > +		}
> > > +		/* Writing TMRC registers requires CMM reset. */
> > > +		ret = regmap_write(regmap, RM_REG_CMM, 0);
> > > +		if (ret < 0)
> > 
> > unlock?
> > 
> > > +			return ret;
> > > +		ret = regmap_write(regmap, RM_REG_CMM, RM_CMM_PMX |
> > > +			RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > > +		if (ret < 0)
> > 
> > unlock?
> > 
> > > +			return ret;
> > > +		mutex_unlock(&data->lock);
> > > +
> > > +		data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> > > +		return 0;
> > > +	}
> > > +	return -EINVAL;
> > > +}
> > > +
> > > +static int rm3100_read_raw(struct iio_dev *indio_dev,
> > > +			   const struct iio_chan_spec *chan,
> > > +			   int *val, int *val2, long mask)
> > > +{
> > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > +	int ret;
> > > +
> > > +	switch (mask) {
> > > +	case IIO_CHAN_INFO_RAW:
> > > +		ret = iio_device_claim_direct_mode(indio_dev);
> > > +		if (ret < 0)
> > 
> > release_direct_mode() here?
> > 
> > > +			return ret;
> > > +		ret = rm3100_read_mag(data, chan->scan_index, val);
> > > +		iio_device_release_direct_mode(indio_dev);
> > > +
> > > +		return ret;
> > > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > > +		return ret = rm3100_get_samp_freq(data, val, val2);
> > 
> > return ret = ???, just
> > return rm3100_...
> > 
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +}
> > > +
> > > +static int rm3100_write_raw(struct iio_dev *indio_dev,
> > > +			    struct iio_chan_spec const *chan,
> > > +			    int val, int val2, long mask)
> > > +{
> > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > +	int ret;
> > > +
> > > +	switch (mask) {
> > > +	case IIO_CHAN_INFO_SAMP_FREQ:
> > > +		ret = rm3100_set_samp_freq(data, val, val2);
> > > +		if (ret < 0)
> > > +			return ret;
> > > +		return 0;
> > > +	default:
> > > +		return -EINVAL;
> > > +	}
> > > +
> > > +}
> > > +
> > > +static const struct iio_info rm3100_info = {
> > > +	.read_raw = rm3100_read_raw,
> > > +	.write_raw = rm3100_write_raw,
> > > +};
> > > +
> > > +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> > > +{
> > > +	struct iio_poll_func *pf = p;
> > > +	struct iio_dev *indio_dev = pf->indio_dev;
> > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > +	struct regmap *regmap = data->regmap;
> > > +	u8 *buffer;
> > > +	int ret;
> > > +	int i;
> > > +
> > > +	buffer = devm_kzalloc(data->dev, indio_dev->scan_bytes, GFP_KERNEL);
> > 
> > try to allocate the maximum needed amount of memory beforehand, in
> > _probe() perhaps
> > 
> > > +	if (!buffer)
> > > +		goto done;
> > > +
> > > +	mutex_lock(&data->lock);
> > > +	ret = rm3100_wait_measurement(data);
> > > +	if (ret < 0) {
> > > +		mutex_unlock(&data->lock);
> > > +		goto done;
> > > +	}
> > > +
> > > +	for (i = 0; i < 3; i++) {
> > > +		ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * i,
> > > +				buffer + 4 * i, 3);
> > > +		if (ret < 0)
> > > +			return ret;
> > > +	}
> 
> Wouldn't it be better to read the 3 axis with one transaction here.
> And if required shuffle the data into the iio buffer.
> 
> 

Hi Phil,

That's surely something will make this code better!

But also, there is something worth discussing,
When triggered buffer is triggered here, I should push all the data into
userspace, and every time the buffer is enabled, the iio subsystem
automatically computes the alignment of my data. This sensor has 3
24bits channels, and my original thought of data alignment should be
like this because my machine is 32bits:
+----------+----+----+----+----+----+----+
| 3b(ytes) | 1b | 3b | 1b | 3b | 1b | 8b |
+----------+----+----+----+----+----+----+
3 bytes data and 1 byte for alignment, last 8 bytes for the timestamp.
Aligned to 4 bytes. 
But the actual layout is like this:
+----------+----+----+----+----+----+----+----+
| 3b(ytes) | 1b | 3b | 1b | 3b | 1b | 4b | 8b |
+----------+----+----+----+----+----+----+----+
Soon after I debugged the code of industrial io core I found that in the
iio_compute_scanbytes() in industialio-buffer.c, alignment was computed
each channel a time, and was aligned with the length of next channel. (eg:
In the last case, before the timestamp channel was appended, there are
12 bytes already in the buffer, to align to the timetamp which is 8 
bytes, it injected 4 bytes to make it 16, and then append timestamp. )
This leads to two questions:

1. I thought computer systems align their data in the memory for least
time access, shouldn't this always align to the bits of architecture,
like for a 32bits machine align to 4?? I'm a little confused here.
Jonathan must have considered something, Hoping to get some
explanations!

2. In this case I have 4 channels including the timetamp one, and I
thought it should be aligned like the first one. But actually it's
aligned like the second one, when I was testing the buffer, I got
confused, because the in_magn_x_type returned 24/32, 24/32, 24/32
and 64/64. If the iio core injected some empty bytes, shouldn't it
notify me? Or at least change the timestamp type in scan_bytes to
64/96>>0?? And when I only enabled two of three channels, the data
alignment in the buffer looks like this:
+----------+----+----+----+----+
| 3b(ytes) | 1b | 3b | 1b | 8b |
+----------+----+----+----+----+
Notice that 4 bytes have gone due to data of two channels have already
aligned to 8 bytes.
Hoping to get some answers, I'm very curious and interested about this!

yours,
Song Qiang

> > > +	mutex_unlock(&data->lock);
> > > +
> > > +	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
> > > +			iio_get_time_ns(indio_dev));
> > > +done:
> > > +	iio_trigger_notify_done(indio_dev->trig);
> > > +
> > > +	return IRQ_HANDLED;
> > > +}
> > > +
> > > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> > > +{
> > > +	struct iio_dev *indio_dev;
> > > +	struct rm3100_data *data;
> > > +	int tmp;
> > > +	int ret;
> > > +
> > > +	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> > > +	if (!indio_dev)
> > > +		return -ENOMEM;
> > > +
> > > +	data = iio_priv(indio_dev);
> > > +	dev_set_drvdata(dev, indio_dev);
> > > +	data->dev = dev;
> > > +	data->regmap = regmap;
> > > +
> > > +	mutex_init(&data->lock);
> > > +
> > > +	indio_dev->dev.parent = dev;
> > > +	indio_dev->name = "rm3100";
> > > +	indio_dev->info = &rm3100_info;
> > > +	indio_dev->channels = rm3100_channels;
> > > +	indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
> > > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > > +	indio_dev->available_scan_masks = rm3100_scan_masks;
> > > +
> > > +	if (!irq)
> > > +		data->use_interrupt = false;
> > > +	else {
> > > +		data->use_interrupt = true;
> > > +		ret = devm_request_irq(dev,
> > > +			irq,
> > > +			rm3100_measurement_irq_handler,
> > > +			IRQF_TRIGGER_RISING,
> > > +			indio_dev->name,
> > > +			data);
> > > +		if (ret < 0) {
> > > +			dev_err(dev,
> > > +			"request irq line failed.");
> > 
> > \n
> > 
> > > +			return -ret;
> > > +		}
> > > +		init_completion(&data->measuring_done);
> > > +	}
> > > +
> > > +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> > > +					rm3100_trigger_handler, NULL);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	/* 3sec more wait time. */
> > > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> > 
> > check ret
> > 
> > > +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> > > +
> > > +	/* Starting all channels' conversion. */
> > > +	ret = regmap_write(regmap, RM_REG_CMM,
> > > +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	return devm_iio_device_register(dev, indio_dev);
> > > +}
> > > +EXPORT_SYMBOL(rm3100_common_probe);
> > > +
> > > +int rm3100_common_remove(struct device *dev)
> > > +{
> > > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > +	struct regmap *regmap = data->regmap;
> > > +
> > > +	regmap_write(regmap, RM_REG_CMM, 0x00);
> > > +
> > > +	return 0;
> > > +}
> > > +EXPORT_SYMBOL(rm3100_common_remove);
> > > +
> > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > > +MODULE_LICENSE("GPL v2");
> > > diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
> > > new file mode 100644
> > > index 000000000000..b50dc5b1b30b
> > > --- /dev/null
> > > +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> > > @@ -0,0 +1,66 @@
> > > +// SPDX-License-Identifier: GPL-2.0+
> > > +/*
> > > + * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
> > > + *
> > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > + *
> > > + * User Manual available at
> > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > + *
> > > + * i2c slave address 0x20 + SA1 << 1 + SA0.
> > > + */
> > > +
> > > +#include <linux/i2c.h>
> > > +
> > > +#include "rm3100.h"
> > > +
> > > +static const struct regmap_config rm3100_regmap_config = {
> > > +		.reg_bits = 8,
> > > +		.val_bits = 8,
> > > +
> > > +		.rd_table = &rm3100_readable_table,
> > > +		.wr_table = &rm3100_writable_table,
> > > +		.volatile_table = &rm3100_volatile_table,
> > > +
> > > +		.cache_type = REGCACHE_RBTREE,
> > > +};
> > > +
> > > +static int rm3100_probe(struct i2c_client *client)
> > > +{
> > > +	struct regmap *regmap;
> > > +
> > > +	if (!i2c_check_functionality(client->adapter,
> > > +		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
> > > +		return -EOPNOTSUPP;
> > > +
> > > +	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
> > > +	if (IS_ERR(regmap))
> > > +		return PTR_ERR(regmap);
> > > +
> > > +	return rm3100_common_probe(&client->dev, regmap, client->irq);
> > > +}
> > > +
> > > +static int rm3100_remove(struct i2c_client *client)
> > > +{
> > > +	return rm3100_common_remove(&client->dev);
> > > +}
> > > +
> > > +static const struct of_device_id rm3100_dt_match[] = {
> > > +	{ .compatible = "pni,rm3100-i2c", },
> > > +	{ }
> > > +};
> > > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > > +
> > > +static struct i2c_driver rm3100_driver = {
> > > +	.driver = {
> > > +		.name = "rm3100-i2c",
> > > +		.of_match_table = rm3100_dt_match,
> > > +	},
> > > +	.probe_new = rm3100_probe,
> > > +	.remove = rm3100_remove,
> > > +};
> > > +module_i2c_driver(rm3100_driver);
> > > +
> > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > > +MODULE_LICENSE("GPL v2");
> > > diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
> > > new file mode 100644
> > > index 000000000000..2c7dd9e3a1a2
> > > --- /dev/null
> > > +++ b/drivers/iio/magnetometer/rm3100-spi.c
> > > @@ -0,0 +1,72 @@
> > > +// SPDX-License-Identifier: GPL-2.0+
> > > +/*
> > > + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
> > > + *
> > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > + *
> > > + * User Manual available at
> > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > + */
> > > +
> > > +#include <linux/spi/spi.h>
> > > +
> > > +#include "rm3100.h"
> > > +
> > > +static const struct regmap_config rm3100_regmap_config = {
> > > +		.reg_bits = 8,
> > > +		.val_bits = 8,
> > > +
> > > +		.rd_table = &rm3100_readable_table,
> > > +		.wr_table = &rm3100_writable_table,
> > > +		.volatile_table = &rm3100_volatile_table,
> > > +
> > > +		.read_flag_mask = 0x80,
> > > +
> > > +		.cache_type = REGCACHE_RBTREE,
> > > +};
> > > +
> > > +static int rm3100_probe(struct spi_device *spi)
> > > +{
> > > +	struct regmap *regmap;
> > > +	int ret;
> > > +
> > > +	/* Actually this device supports both mode 0 and mode 3. */
> > > +	spi->mode = SPI_MODE_0;
> > > +	/* data rates cannot exceeds 1Mbits. */
> > 
> > exceed
> > 
> > > +	spi->max_speed_hz = 1000000;
> > > +	spi->bits_per_word = 8;
> > > +	ret = spi_setup(spi);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
> > > +	if (IS_ERR(regmap))
> > > +		return PTR_ERR(regmap);
> > > +
> > > +	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
> > > +}
> > > +
> > > +static int rm3100_remove(struct spi_device *spi)
> > > +{
> > > +	return rm3100_common_remove(&spi->dev);
> > > +}
> > > +
> > > +static const struct of_device_id rm3100_dt_match[] = {
> > > +	{ .compatible = "pni,rm3100-spi", },
> > > +	{ }
> > > +};
> > > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > > +
> > > +static struct spi_driver rm3100_driver = {
> > > +		.driver = {
> > > +				.name = "rm3100-spi",
> > > +				.of_match_table = rm3100_dt_match,
> > > +		},
> > > +		.probe = rm3100_probe,
> > > +		.remove = rm3100_remove,
> > > +};
> > > +module_spi_driver(rm3100_driver);
> > > +
> > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
> > > +MODULE_LICENSE("GPL v2");
> > > diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
> > > new file mode 100644
> > > index 000000000000..5e30bc0f5149
> > > --- /dev/null
> > > +++ b/drivers/iio/magnetometer/rm3100.h
> > > @@ -0,0 +1,90 @@
> > > +/* SPDX-License-Identifier: GPL-2.0+ */
> > > +/*
> > > + * Header file for PNI RM3100 driver
> > > + *
> > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > + */
> > > +
> > > +#ifndef RM3100_CORE_H
> > > +#define RM3100_CORE_H
> > > +
> > > +#include <linux/module.h>
> > > +#include <linux/regmap.h>
> > > +
> > > +#define RM_REG_REV_ID		0x36
> > > +
> > > +/* Cycle Count Registers MSBs and LSBs. */
> > > +#define RM_REG_CCXM		0x04
> > > +#define RM_REG_CCXL		0x05
> > > +#define RM_REG_CCYM		0x06
> > > +#define RM_REG_CCYL		0x07
> > > +#define RM_REG_CCZM		0x08
> > > +#define RM_REG_CCZL		0x09
> > > +
> > > +/* Single Measurement Mode register. */
> > > +#define RM_REG_POLL		0x00
> > > +#define RM_POLL_PMX		BIT(4)
> > > +#define RM_POLL_PMY		BIT(5)
> > > +#define RM_POLL_PMZ		BIT(6)
> > > +
> > > +/* Continues Measurement Mode register. */
> > > +#define RM_REG_CMM		0x01
> > > +#define RM_CMM_START		BIT(0)
> > > +#define RM_CMM_DRDM		BIT(2)
> > > +#define RM_CMM_PMX		BIT(4)
> > > +#define RM_CMM_PMY		BIT(5)
> > > +#define RM_CMM_PMZ		BIT(6)
> > > +
> > > +/* TiMe Rate Configuration register. */
> > > +#define RM_REG_TMRC		0x0B
> > > +#define RM_TMRC_OFFSET		0x92
> > > +
> > > +/* Result Status register. */
> > > +#define RM_REG_STATUS		0x34
> > > +#define RM_STATUS_DRDY		BIT(7)
> > > +
> > > +/* Measurement result registers. */
> > > +#define RM_REG_MX2		0x24
> > > +#define RM_REG_MX1		0x25
> > > +#define RM_REG_MX0		0x26
> > > +#define RM_REG_MY2		0x27
> > > +#define RM_REG_MY1		0x28
> > > +#define RM_REG_MY0		0x29
> > > +#define RM_REG_MZ2		0x2a
> > > +#define RM_REG_MZ1		0x2b
> > > +#define RM_REG_MZ0		0x2c
> > > +
> > > +#define RM_REG_HSHAKE		0x35
> > > +
> > > +#define RM_W_REG_START		RM_REG_POLL
> > > +#define RM_W_REG_END		RM_REG_REV_ID
> > > +#define RM_R_REG_START		RM_REG_POLL
> > > +#define RM_R_REG_END		RM_REG_HSHAKE
> > > +#define RM_V_REG_START		RM_REG_MX2
> > > +#define RM_V_REG_END		RM_REG_HSHAKE
> > > +
> > > +/* Built-In Self Test reigister. */
> > > +#define RM_REG_BIST		0x33
> > > +
> > > +struct rm3100_data {
> > > +	struct device *dev;
> > > +	struct regmap *regmap;
> > > +	struct completion measuring_done;
> > > +	bool use_interrupt;
> > > +
> > > +	int conversion_time;
> > > +
> > > +	/* To protect consistency of every measurement and sampling
> > > +	 * frequency change operations.
> > > +	 */
> > > +	struct mutex lock;
> > > +};
> > > +
> > > +extern const struct regmap_access_table rm3100_readable_table;
> > > +extern const struct regmap_access_table rm3100_writable_table;
> > > +extern const struct regmap_access_table rm3100_volatile_table;
> > > +
> > > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> > > +int rm3100_common_remove(struct device *dev);
> > > +
> > > +#endif /* RM3100_CORE_H */
> > > 
> > 
> 
> 
> -- 
> Regards
> Phil Reid
> 

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-21 11:29     ` Song Qiang
@ 2018-09-21 12:20       ` Jonathan Cameron
  2018-09-22  9:18         ` Song Qiang
  0 siblings, 1 reply; 19+ messages in thread
From: Jonathan Cameron @ 2018-09-21 12:20 UTC (permalink / raw)
  To: Song Qiang
  Cc: Phil Reid, Peter Meerwald-Stadler, jic23, lars, linux-iio,
	devicetree, linux-kernel

Hi Song,

...

Please crop emails down to the relevant section when replying.  Lots of scrolling
for everyone otherwise and sometimes things get missed when doing that.

> > > > +	if (!buffer)
> > > > +		goto done;
> > > > +
> > > > +	mutex_lock(&data->lock);
> > > > +	ret = rm3100_wait_measurement(data);
> > > > +	if (ret < 0) {
> > > > +		mutex_unlock(&data->lock);
> > > > +		goto done;
> > > > +	}
> > > > +
> > > > +	for (i = 0; i < 3; i++) {
> > > > +		ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * i,
> > > > +				buffer + 4 * i, 3);
> > > > +		if (ret < 0)
> > > > +			return ret;
> > > > +	}  
> > 
> > Wouldn't it be better to read the 3 axis with one transaction here.
> > And if required shuffle the data into the iio buffer.
> > 
> >   
> 
> Hi Phil,
> 
> That's surely something will make this code better!
> 
> But also, there is something worth discussing,
> When triggered buffer is triggered here, I should push all the data into
> userspace, and every time the buffer is enabled, the iio subsystem
> automatically computes the alignment of my data. This sensor has 3
> 24bits channels, and my original thought of data alignment should be
> like this because my machine is 32bits:
> +----------+----+----+----+----+----+----+
> | 3b(ytes) | 1b | 3b | 1b | 3b | 1b | 8b |
> +----------+----+----+----+----+----+----+
> 3 bytes data and 1 byte for alignment, last 8 bytes for the timestamp.
> Aligned to 4 bytes. 
> But the actual layout is like this:
> +----------+----+----+----+----+----+----+----+
> | 3b(ytes) | 1b | 3b | 1b | 3b | 1b | 4b | 8b |
> +----------+----+----+----+----+----+----+----+
> Soon after I debugged the code of industrial io core I found that in the
> iio_compute_scanbytes() in industialio-buffer.c, alignment was computed
> each channel a time, and was aligned with the length of next channel. (eg:
> In the last case, before the timestamp channel was appended, there are
> 12 bytes already in the buffer, to align to the timetamp which is 8 
> bytes, it injected 4 bytes to make it 16, and then append timestamp. )
> This leads to two questions:
> 
> 1. I thought computer systems align their data in the memory for least
> time access, shouldn't this always align to the bits of architecture,
> like for a 32bits machine align to 4?? I'm a little confused here.
> Jonathan must have considered something, Hoping to get some
> explanations!

A couple of reasons for this:

1. It's much nicer to have the same spacing independent of the architecture
as it saves having to have userspace code do different things depending on
what the kernel is running at, which may not be the same as userspace.

2. The size of a memory read is not the only scale that makes sense for
alignment.  In theory at least you can have fun and games like crossing of
cacheline boundaries if you do 64 bit access only at 32bits aligned on a
32 bit platform.  Won't hit all that often with typically 64 byte cachelines
but it is the sort of subtle effect you get when you don't go with
'naturally' aligned. (which is what aligning to an elements own size is
normally called IIRC).

> 
> 2. In this case I have 4 channels including the timetamp one, and I
> thought it should be aligned like the first one. But actually it's
> aligned like the second one, when I was testing the buffer, I got
> confused, because the in_magn_x_type returned 24/32, 24/32, 24/32
> and 64/64. If the iio core injected some empty bytes, shouldn't it
> notify me? Or at least change the timestamp type in scan_bytes to
> 64/96>>0?? And when I only enabled two of three channels, the data  
> alignment in the buffer looks like this:
> +----------+----+----+----+----+
> | 3b(ytes) | 1b | 3b | 1b | 8b |
> +----------+----+----+----+----+
> Notice that 4 bytes have gone due to data of two channels have already
> aligned to 8 bytes.
> Hoping to get some answers, I'm very curious and interested about this!

Nope it shouldn't do the 64/96 need to tell you because it uses
the rules of natural alignment which are easy for userspace to predict.
So in this second case there is no need to pad so it doesn't - whereas the
4 bytes are needed in the first case.

We could have explicitly output the offsets and kept things more flexible
but that would have prevented some general optimizations on the userspace
side for common cases.  If you are reading a 3 axis accelerometer for
example with timestamp then you can probably safely prebake optimized handling
for,

1B (x), 1B (y), 1B(z), 5B(pad), 8B(ts)

2B (x), 2B (y), 2B(z), 2B(pad), 8B(ts)

in reality no one ever goes higher than this because accelerometers aren't
accurate enough...

If we did anything more complex we'd need to describe to userspace in some
sense what was happening beyond the static data currently provided (when
combined with the ordering and enables) and that makes for a complex ABI
we don't need.

Also we'd have to do unaligned puts of the u64 timestamp in kernel which
is nasty.

Jonathan
> 
> yours,
> Song Qiang
> 
> > > > +	mutex_unlock(&data->lock);
> > > > +
> > > > +	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
> > > > +			iio_get_time_ns(indio_dev));
> > > > +done:
> > > > +	iio_trigger_notify_done(indio_dev->trig);
> > > > +
> > > > +	return IRQ_HANDLED;
> > > > +}
> > > > +
> > > > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> > > > +{
> > > > +	struct iio_dev *indio_dev;
> > > > +	struct rm3100_data *data;
> > > > +	int tmp;
> > > > +	int ret;
> > > > +
> > > > +	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> > > > +	if (!indio_dev)
> > > > +		return -ENOMEM;
> > > > +
> > > > +	data = iio_priv(indio_dev);
> > > > +	dev_set_drvdata(dev, indio_dev);
> > > > +	data->dev = dev;
> > > > +	data->regmap = regmap;
> > > > +
> > > > +	mutex_init(&data->lock);
> > > > +
> > > > +	indio_dev->dev.parent = dev;
> > > > +	indio_dev->name = "rm3100";
> > > > +	indio_dev->info = &rm3100_info;
> > > > +	indio_dev->channels = rm3100_channels;
> > > > +	indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
> > > > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > > > +	indio_dev->available_scan_masks = rm3100_scan_masks;
> > > > +
> > > > +	if (!irq)
> > > > +		data->use_interrupt = false;
> > > > +	else {
> > > > +		data->use_interrupt = true;
> > > > +		ret = devm_request_irq(dev,
> > > > +			irq,
> > > > +			rm3100_measurement_irq_handler,
> > > > +			IRQF_TRIGGER_RISING,
> > > > +			indio_dev->name,
> > > > +			data);
> > > > +		if (ret < 0) {
> > > > +			dev_err(dev,
> > > > +			"request irq line failed.");  
> > > 
> > > \n
> > >   
> > > > +			return -ret;
> > > > +		}
> > > > +		init_completion(&data->measuring_done);
> > > > +	}
> > > > +
> > > > +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> > > > +					rm3100_trigger_handler, NULL);
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	/* 3sec more wait time. */
> > > > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);  
> > > 
> > > check ret
> > >   
> > > > +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> > > > +
> > > > +	/* Starting all channels' conversion. */
> > > > +	ret = regmap_write(regmap, RM_REG_CMM,
> > > > +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > > > +	if (ret < 0)
> > > > +		return ret;
> > > > +
> > > > +	return devm_iio_device_register(dev, indio_dev);
> > > > +}
> > > > +EXPORT_SYMBOL(rm3100_common_probe);
> > > > +
> > > > +int rm3100_common_remove(struct device *dev)
> > > > +{
> > > > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > > +	struct regmap *regmap = data->regmap;
> > > > +
> > > > +	regmap_write(regmap, RM_REG_CMM, 0x00);
> > > > +
> > > > +	return 0;
> > > > +}
> > > > +EXPORT_SYMBOL(rm3100_common_remove);
> > > > +
> > > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > > > +MODULE_LICENSE("GPL v2");
> > > > diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
> > > > new file mode 100644
> > > > index 000000000000..b50dc5b1b30b
> > > > --- /dev/null
> > > > +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> > > > @@ -0,0 +1,66 @@
> > > > +// SPDX-License-Identifier: GPL-2.0+
> > > > +/*
> > > > + * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
> > > > + *
> > > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > > + *
> > > > + * User Manual available at
> > > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > > + *
> > > > + * i2c slave address 0x20 + SA1 << 1 + SA0.
> > > > + */
> > > > +
> > > > +#include <linux/i2c.h>
> > > > +
> > > > +#include "rm3100.h"
> > > > +
> > > > +static const struct regmap_config rm3100_regmap_config = {
> > > > +		.reg_bits = 8,
> > > > +		.val_bits = 8,
> > > > +
> > > > +		.rd_table = &rm3100_readable_table,
> > > > +		.wr_table = &rm3100_writable_table,
> > > > +		.volatile_table = &rm3100_volatile_table,
> > > > +
> > > > +		.cache_type = REGCACHE_RBTREE,
> > > > +};
> > > > +
> > > > +static int rm3100_probe(struct i2c_client *client)
> > > > +{
> > > > +	struct regmap *regmap;
> > > > +
> > > > +	if (!i2c_check_functionality(client->adapter,
> > > > +		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
> > > > +		return -EOPNOTSUPP;
> > > > +
> > > > +	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
> > > > +	if (IS_ERR(regmap))
> > > > +		return PTR_ERR(regmap);
> > > > +
> > > > +	return rm3100_common_probe(&client->dev, regmap, client->irq);
> > > > +}
> > > > +
> > > > +static int rm3100_remove(struct i2c_client *client)
> > > > +{
> > > > +	return rm3100_common_remove(&client->dev);
> > > > +}
> > > > +
> > > > +static const struct of_device_id rm3100_dt_match[] = {
> > > > +	{ .compatible = "pni,rm3100-i2c", },
> > > > +	{ }
> > > > +};
> > > > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > > > +
> > > > +static struct i2c_driver rm3100_driver = {
> > > > +	.driver = {
> > > > +		.name = "rm3100-i2c",
> > > > +		.of_match_table = rm3100_dt_match,
> > > > +	},
> > > > +	.probe_new = rm3100_probe,
> > > > +	.remove = rm3100_remove,
> > > > +};
> > > > +module_i2c_driver(rm3100_driver);
> > > > +
> > > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > > > +MODULE_LICENSE("GPL v2");
> > > > diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
> > > > new file mode 100644
> > > > index 000000000000..2c7dd9e3a1a2
> > > > --- /dev/null
> > > > +++ b/drivers/iio/magnetometer/rm3100-spi.c
> > > > @@ -0,0 +1,72 @@
> > > > +// SPDX-License-Identifier: GPL-2.0+
> > > > +/*
> > > > + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
> > > > + *
> > > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > > + *
> > > > + * User Manual available at
> > > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > > + */
> > > > +
> > > > +#include <linux/spi/spi.h>
> > > > +
> > > > +#include "rm3100.h"
> > > > +
> > > > +static const struct regmap_config rm3100_regmap_config = {
> > > > +		.reg_bits = 8,
> > > > +		.val_bits = 8,
> > > > +
> > > > +		.rd_table = &rm3100_readable_table,
> > > > +		.wr_table = &rm3100_writable_table,
> > > > +		.volatile_table = &rm3100_volatile_table,
> > > > +
> > > > +		.read_flag_mask = 0x80,
> > > > +
> > > > +		.cache_type = REGCACHE_RBTREE,
> > > > +};
> > > > +
> > > > +static int rm3100_probe(struct spi_device *spi)
> > > > +{
> > > > +	struct regmap *regmap;
> > > > +	int ret;
> > > > +
> > > > +	/* Actually this device supports both mode 0 and mode 3. */
> > > > +	spi->mode = SPI_MODE_0;
> > > > +	/* data rates cannot exceeds 1Mbits. */  
> > > 
> > > exceed
> > >   
> > > > +	spi->max_speed_hz = 1000000;
> > > > +	spi->bits_per_word = 8;
> > > > +	ret = spi_setup(spi);
> > > > +	if (ret)
> > > > +		return ret;
> > > > +
> > > > +	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
> > > > +	if (IS_ERR(regmap))
> > > > +		return PTR_ERR(regmap);
> > > > +
> > > > +	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
> > > > +}
> > > > +
> > > > +static int rm3100_remove(struct spi_device *spi)
> > > > +{
> > > > +	return rm3100_common_remove(&spi->dev);
> > > > +}
> > > > +
> > > > +static const struct of_device_id rm3100_dt_match[] = {
> > > > +	{ .compatible = "pni,rm3100-spi", },
> > > > +	{ }
> > > > +};
> > > > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > > > +
> > > > +static struct spi_driver rm3100_driver = {
> > > > +		.driver = {
> > > > +				.name = "rm3100-spi",
> > > > +				.of_match_table = rm3100_dt_match,
> > > > +		},
> > > > +		.probe = rm3100_probe,
> > > > +		.remove = rm3100_remove,
> > > > +};
> > > > +module_spi_driver(rm3100_driver);
> > > > +
> > > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
> > > > +MODULE_LICENSE("GPL v2");
> > > > diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
> > > > new file mode 100644
> > > > index 000000000000..5e30bc0f5149
> > > > --- /dev/null
> > > > +++ b/drivers/iio/magnetometer/rm3100.h
> > > > @@ -0,0 +1,90 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0+ */
> > > > +/*
> > > > + * Header file for PNI RM3100 driver
> > > > + *
> > > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > > + */
> > > > +
> > > > +#ifndef RM3100_CORE_H
> > > > +#define RM3100_CORE_H
> > > > +
> > > > +#include <linux/module.h>
> > > > +#include <linux/regmap.h>
> > > > +
> > > > +#define RM_REG_REV_ID		0x36
> > > > +
> > > > +/* Cycle Count Registers MSBs and LSBs. */
> > > > +#define RM_REG_CCXM		0x04
> > > > +#define RM_REG_CCXL		0x05
> > > > +#define RM_REG_CCYM		0x06
> > > > +#define RM_REG_CCYL		0x07
> > > > +#define RM_REG_CCZM		0x08
> > > > +#define RM_REG_CCZL		0x09
> > > > +
> > > > +/* Single Measurement Mode register. */
> > > > +#define RM_REG_POLL		0x00
> > > > +#define RM_POLL_PMX		BIT(4)
> > > > +#define RM_POLL_PMY		BIT(5)
> > > > +#define RM_POLL_PMZ		BIT(6)
> > > > +
> > > > +/* Continues Measurement Mode register. */
> > > > +#define RM_REG_CMM		0x01
> > > > +#define RM_CMM_START		BIT(0)
> > > > +#define RM_CMM_DRDM		BIT(2)
> > > > +#define RM_CMM_PMX		BIT(4)
> > > > +#define RM_CMM_PMY		BIT(5)
> > > > +#define RM_CMM_PMZ		BIT(6)
> > > > +
> > > > +/* TiMe Rate Configuration register. */
> > > > +#define RM_REG_TMRC		0x0B
> > > > +#define RM_TMRC_OFFSET		0x92
> > > > +
> > > > +/* Result Status register. */
> > > > +#define RM_REG_STATUS		0x34
> > > > +#define RM_STATUS_DRDY		BIT(7)
> > > > +
> > > > +/* Measurement result registers. */
> > > > +#define RM_REG_MX2		0x24
> > > > +#define RM_REG_MX1		0x25
> > > > +#define RM_REG_MX0		0x26
> > > > +#define RM_REG_MY2		0x27
> > > > +#define RM_REG_MY1		0x28
> > > > +#define RM_REG_MY0		0x29
> > > > +#define RM_REG_MZ2		0x2a
> > > > +#define RM_REG_MZ1		0x2b
> > > > +#define RM_REG_MZ0		0x2c
> > > > +
> > > > +#define RM_REG_HSHAKE		0x35
> > > > +
> > > > +#define RM_W_REG_START		RM_REG_POLL
> > > > +#define RM_W_REG_END		RM_REG_REV_ID
> > > > +#define RM_R_REG_START		RM_REG_POLL
> > > > +#define RM_R_REG_END		RM_REG_HSHAKE
> > > > +#define RM_V_REG_START		RM_REG_MX2
> > > > +#define RM_V_REG_END		RM_REG_HSHAKE
> > > > +
> > > > +/* Built-In Self Test reigister. */
> > > > +#define RM_REG_BIST		0x33
> > > > +
> > > > +struct rm3100_data {
> > > > +	struct device *dev;
> > > > +	struct regmap *regmap;
> > > > +	struct completion measuring_done;
> > > > +	bool use_interrupt;
> > > > +
> > > > +	int conversion_time;
> > > > +
> > > > +	/* To protect consistency of every measurement and sampling
> > > > +	 * frequency change operations.
> > > > +	 */
> > > > +	struct mutex lock;
> > > > +};
> > > > +
> > > > +extern const struct regmap_access_table rm3100_readable_table;
> > > > +extern const struct regmap_access_table rm3100_writable_table;
> > > > +extern const struct regmap_access_table rm3100_volatile_table;
> > > > +
> > > > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> > > > +int rm3100_common_remove(struct device *dev);
> > > > +
> > > > +#endif /* RM3100_CORE_H */
> > > >   
> > >   
> > 
> > 
> > -- 
> > Regards
> > Phil Reid
> >   



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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-21 12:20       ` Jonathan Cameron
@ 2018-09-22  9:18         ` Song Qiang
  0 siblings, 0 replies; 19+ messages in thread
From: Song Qiang @ 2018-09-22  9:18 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Phil Reid, Peter Meerwald-Stadler, jic23, lars, linux-iio,
	devicetree, linux-kernel

On Fri, Sep 21, 2018 at 01:20:12PM +0100, Jonathan Cameron wrote:
> Hi Song,
> 
> ...
> 
> Please crop emails down to the relevant section when replying.  Lots of scrolling
> for everyone otherwise and sometimes things get missed when doing that.
> 

Hi Jonathan,

Sorryi for this, won't happen again.
I was intended to give a simple question, but it reminds me of a lot
unrelated stuff, next time I'll make it another email.

> > > > > +	if (!buffer)
> > > > > +		goto done;
> > > > > +
> > > > > +	mutex_lock(&data->lock);
> > > > > +	ret = rm3100_wait_measurement(data);
> > > > > +	if (ret < 0) {
> > > > > +		mutex_unlock(&data->lock);
> > > > > +		goto done;
> > > > > +	}
> > > > > +
> > > > > +	for (i = 0; i < 3; i++) {
> > > > > +		ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * i,
> > > > > +				buffer + 4 * i, 3);
> > > > > +		if (ret < 0)
> > > > > +			return ret;
> > > > > +	}  
> > > 
> > > Wouldn't it be better to read the 3 axis with one transaction here.
> > > And if required shuffle the data into the iio buffer.
> > > 
> > >   
> > 
> > Hi Phil,
> > 
> > That's surely something will make this code better!
> > 
> > But also, there is something worth discussing,
> > When triggered buffer is triggered here, I should push all the data into
> > userspace, and every time the buffer is enabled, the iio subsystem
> > automatically computes the alignment of my data. This sensor has 3
> > 24bits channels, and my original thought of data alignment should be
> > like this because my machine is 32bits:
> > +----------+----+----+----+----+----+----+
> > | 3b(ytes) | 1b | 3b | 1b | 3b | 1b | 8b |
> > +----------+----+----+----+----+----+----+
> > 3 bytes data and 1 byte for alignment, last 8 bytes for the timestamp.
> > Aligned to 4 bytes. 
> > But the actual layout is like this:
> > +----------+----+----+----+----+----+----+----+
> > | 3b(ytes) | 1b | 3b | 1b | 3b | 1b | 4b | 8b |
> > +----------+----+----+----+----+----+----+----+
> > Soon after I debugged the code of industrial io core I found that in the
> > iio_compute_scanbytes() in industialio-buffer.c, alignment was computed
> > each channel a time, and was aligned with the length of next channel. (eg:
> > In the last case, before the timestamp channel was appended, there are
> > 12 bytes already in the buffer, to align to the timetamp which is 8 
> > bytes, it injected 4 bytes to make it 16, and then append timestamp. )
> > This leads to two questions:
> > 
> > 1. I thought computer systems align their data in the memory for least
> > time access, shouldn't this always align to the bits of architecture,
> > like for a 32bits machine align to 4?? I'm a little confused here.
> > Jonathan must have considered something, Hoping to get some
> > explanations!
> 
> A couple of reasons for this:
> 
> 1. It's much nicer to have the same spacing independent of the architecture
> as it saves having to have userspace code do different things depending on
> what the kernel is running at, which may not be the same as userspace.
> 
> 2. The size of a memory read is not the only scale that makes sense for
> alignment.  In theory at least you can have fun and games like crossing of
> cacheline boundaries if you do 64 bit access only at 32bits aligned on a
> 32 bit platform.  Won't hit all that often with typically 64 byte cachelines
> but it is the sort of subtle effect you get when you don't go with
> 'naturally' aligned. (which is what aligning to an elements own size is
> normally called IIRC).
> 

I see, architecture independent surely should be the principle of the
Linux kernel, this makes sense, I think I should learn more stuff about
natural alignment. I googled something about it and understand what it
is, but still find it difficult to fully understand its benefits. I think
I'll learn more about it after. 

> > 
> > 2. In this case I have 4 channels including the timetamp one, and I
> > thought it should be aligned like the first one. But actually it's
> > aligned like the second one, when I was testing the buffer, I got
> > confused, because the in_magn_x_type returned 24/32, 24/32, 24/32
> > and 64/64. If the iio core injected some empty bytes, shouldn't it
> > notify me? Or at least change the timestamp type in scan_bytes to
> > 64/96>>0?? And when I only enabled two of three channels, the data  
> > alignment in the buffer looks like this:
> > +----------+----+----+----+----+
> > | 3b(ytes) | 1b | 3b | 1b | 8b |
> > +----------+----+----+----+----+
> > Notice that 4 bytes have gone due to data of two channels have already
> > aligned to 8 bytes.
> > Hoping to get some answers, I'm very curious and interested about this!
> 
> Nope it shouldn't do the 64/96 need to tell you because it uses
> the rules of natural alignment which are easy for userspace to predict.
> So in this second case there is no need to pad so it doesn't - whereas the
> 4 bytes are needed in the first case.
> 
> We could have explicitly output the offsets and kept things more flexible
> but that would have prevented some general optimizations on the userspace
> side for common cases.  If you are reading a 3 axis accelerometer for
> example with timestamp then you can probably safely prebake optimized handling
> for,
> 
> 1B (x), 1B (y), 1B(z), 5B(pad), 8B(ts)
> 
> 2B (x), 2B (y), 2B(z), 2B(pad), 8B(ts)
> 
> in reality no one ever goes higher than this because accelerometers aren't
> accurate enough...
> 
> If we did anything more complex we'd need to describe to userspace in some
> sense what was happening beyond the static data currently provided (when
> combined with the ordering and enables) and that makes for a complex ABI
> we don't need.
> 
> Also we'd have to do unaligned puts of the u64 timestamp in kernel which
> is nasty.
> 
> Jonathan

Yes, you make the point, since natural alignment is a generic rule for a
lot circumstances, surely it would be easy for userspace to predict. I 
think libiio should have done these kind stuff, shouldn't be a problem
for userspace!
Thanks for the explanation!

yours,
Song Qiang

> > 
> > yours,
> > Song Qiang
> > 
> > > > > +	mutex_unlock(&data->lock);
> > > > > +
> > > > > +	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
> > > > > +			iio_get_time_ns(indio_dev));
> > > > > +done:
> > > > > +	iio_trigger_notify_done(indio_dev->trig);
> > > > > +
> > > > > +	return IRQ_HANDLED;
> > > > > +}
> > > > > +
> > > > > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> > > > > +{
> > > > > +	struct iio_dev *indio_dev;
> > > > > +	struct rm3100_data *data;
> > > > > +	int tmp;
> > > > > +	int ret;
> > > > > +
> > > > > +	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> > > > > +	if (!indio_dev)
> > > > > +		return -ENOMEM;
> > > > > +
> > > > > +	data = iio_priv(indio_dev);
> > > > > +	dev_set_drvdata(dev, indio_dev);
> > > > > +	data->dev = dev;
> > > > > +	data->regmap = regmap;
> > > > > +
> > > > > +	mutex_init(&data->lock);
> > > > > +
> > > > > +	indio_dev->dev.parent = dev;
> > > > > +	indio_dev->name = "rm3100";
> > > > > +	indio_dev->info = &rm3100_info;
> > > > > +	indio_dev->channels = rm3100_channels;
> > > > > +	indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
> > > > > +	indio_dev->modes = INDIO_DIRECT_MODE;
> > > > > +	indio_dev->available_scan_masks = rm3100_scan_masks;
> > > > > +
> > > > > +	if (!irq)
> > > > > +		data->use_interrupt = false;
> > > > > +	else {
> > > > > +		data->use_interrupt = true;
> > > > > +		ret = devm_request_irq(dev,
> > > > > +			irq,
> > > > > +			rm3100_measurement_irq_handler,
> > > > > +			IRQF_TRIGGER_RISING,
> > > > > +			indio_dev->name,
> > > > > +			data);
> > > > > +		if (ret < 0) {
> > > > > +			dev_err(dev,
> > > > > +			"request irq line failed.");  
> > > > 
> > > > \n
> > > >   
> > > > > +			return -ret;
> > > > > +		}
> > > > > +		init_completion(&data->measuring_done);
> > > > > +	}
> > > > > +
> > > > > +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> > > > > +					rm3100_trigger_handler, NULL);
> > > > > +	if (ret < 0)
> > > > > +		return ret;
> > > > > +
> > > > > +	/* 3sec more wait time. */
> > > > > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);  
> > > > 
> > > > check ret
> > > >   
> > > > > +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> > > > > +
> > > > > +	/* Starting all channels' conversion. */
> > > > > +	ret = regmap_write(regmap, RM_REG_CMM,
> > > > > +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > > > > +	if (ret < 0)
> > > > > +		return ret;
> > > > > +
> > > > > +	return devm_iio_device_register(dev, indio_dev);
> > > > > +}
> > > > > +EXPORT_SYMBOL(rm3100_common_probe);
> > > > > +
> > > > > +int rm3100_common_remove(struct device *dev)
> > > > > +{
> > > > > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > > > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > > > +	struct regmap *regmap = data->regmap;
> > > > > +
> > > > > +	regmap_write(regmap, RM_REG_CMM, 0x00);
> > > > > +
> > > > > +	return 0;
> > > > > +}
> > > > > +EXPORT_SYMBOL(rm3100_common_remove);
> > > > > +
> > > > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > > > > +MODULE_LICENSE("GPL v2");
> > > > > diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
> > > > > new file mode 100644
> > > > > index 000000000000..b50dc5b1b30b
> > > > > --- /dev/null
> > > > > +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> > > > > @@ -0,0 +1,66 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0+
> > > > > +/*
> > > > > + * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
> > > > > + *
> > > > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > > > + *
> > > > > + * User Manual available at
> > > > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > > > + *
> > > > > + * i2c slave address 0x20 + SA1 << 1 + SA0.
> > > > > + */
> > > > > +
> > > > > +#include <linux/i2c.h>
> > > > > +
> > > > > +#include "rm3100.h"
> > > > > +
> > > > > +static const struct regmap_config rm3100_regmap_config = {
> > > > > +		.reg_bits = 8,
> > > > > +		.val_bits = 8,
> > > > > +
> > > > > +		.rd_table = &rm3100_readable_table,
> > > > > +		.wr_table = &rm3100_writable_table,
> > > > > +		.volatile_table = &rm3100_volatile_table,
> > > > > +
> > > > > +		.cache_type = REGCACHE_RBTREE,
> > > > > +};
> > > > > +
> > > > > +static int rm3100_probe(struct i2c_client *client)
> > > > > +{
> > > > > +	struct regmap *regmap;
> > > > > +
> > > > > +	if (!i2c_check_functionality(client->adapter,
> > > > > +		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
> > > > > +		return -EOPNOTSUPP;
> > > > > +
> > > > > +	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
> > > > > +	if (IS_ERR(regmap))
> > > > > +		return PTR_ERR(regmap);
> > > > > +
> > > > > +	return rm3100_common_probe(&client->dev, regmap, client->irq);
> > > > > +}
> > > > > +
> > > > > +static int rm3100_remove(struct i2c_client *client)
> > > > > +{
> > > > > +	return rm3100_common_remove(&client->dev);
> > > > > +}
> > > > > +
> > > > > +static const struct of_device_id rm3100_dt_match[] = {
> > > > > +	{ .compatible = "pni,rm3100-i2c", },
> > > > > +	{ }
> > > > > +};
> > > > > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > > > > +
> > > > > +static struct i2c_driver rm3100_driver = {
> > > > > +	.driver = {
> > > > > +		.name = "rm3100-i2c",
> > > > > +		.of_match_table = rm3100_dt_match,
> > > > > +	},
> > > > > +	.probe_new = rm3100_probe,
> > > > > +	.remove = rm3100_remove,
> > > > > +};
> > > > > +module_i2c_driver(rm3100_driver);
> > > > > +
> > > > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > > > > +MODULE_LICENSE("GPL v2");
> > > > > diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
> > > > > new file mode 100644
> > > > > index 000000000000..2c7dd9e3a1a2
> > > > > --- /dev/null
> > > > > +++ b/drivers/iio/magnetometer/rm3100-spi.c
> > > > > @@ -0,0 +1,72 @@
> > > > > +// SPDX-License-Identifier: GPL-2.0+
> > > > > +/*
> > > > > + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
> > > > > + *
> > > > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > > > + *
> > > > > + * User Manual available at
> > > > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > > > + */
> > > > > +
> > > > > +#include <linux/spi/spi.h>
> > > > > +
> > > > > +#include "rm3100.h"
> > > > > +
> > > > > +static const struct regmap_config rm3100_regmap_config = {
> > > > > +		.reg_bits = 8,
> > > > > +		.val_bits = 8,
> > > > > +
> > > > > +		.rd_table = &rm3100_readable_table,
> > > > > +		.wr_table = &rm3100_writable_table,
> > > > > +		.volatile_table = &rm3100_volatile_table,
> > > > > +
> > > > > +		.read_flag_mask = 0x80,
> > > > > +
> > > > > +		.cache_type = REGCACHE_RBTREE,
> > > > > +};
> > > > > +
> > > > > +static int rm3100_probe(struct spi_device *spi)
> > > > > +{
> > > > > +	struct regmap *regmap;
> > > > > +	int ret;
> > > > > +
> > > > > +	/* Actually this device supports both mode 0 and mode 3. */
> > > > > +	spi->mode = SPI_MODE_0;
> > > > > +	/* data rates cannot exceeds 1Mbits. */  
> > > > 
> > > > exceed
> > > >   
> > > > > +	spi->max_speed_hz = 1000000;
> > > > > +	spi->bits_per_word = 8;
> > > > > +	ret = spi_setup(spi);
> > > > > +	if (ret)
> > > > > +		return ret;
> > > > > +
> > > > > +	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
> > > > > +	if (IS_ERR(regmap))
> > > > > +		return PTR_ERR(regmap);
> > > > > +
> > > > > +	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
> > > > > +}
> > > > > +
> > > > > +static int rm3100_remove(struct spi_device *spi)
> > > > > +{
> > > > > +	return rm3100_common_remove(&spi->dev);
> > > > > +}
> > > > > +
> > > > > +static const struct of_device_id rm3100_dt_match[] = {
> > > > > +	{ .compatible = "pni,rm3100-spi", },
> > > > > +	{ }
> > > > > +};
> > > > > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > > > > +
> > > > > +static struct spi_driver rm3100_driver = {
> > > > > +		.driver = {
> > > > > +				.name = "rm3100-spi",
> > > > > +				.of_match_table = rm3100_dt_match,
> > > > > +		},
> > > > > +		.probe = rm3100_probe,
> > > > > +		.remove = rm3100_remove,
> > > > > +};
> > > > > +module_spi_driver(rm3100_driver);
> > > > > +
> > > > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
> > > > > +MODULE_LICENSE("GPL v2");
> > > > > diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
> > > > > new file mode 100644
> > > > > index 000000000000..5e30bc0f5149
> > > > > --- /dev/null
> > > > > +++ b/drivers/iio/magnetometer/rm3100.h
> > > > > @@ -0,0 +1,90 @@
> > > > > +/* SPDX-License-Identifier: GPL-2.0+ */
> > > > > +/*
> > > > > + * Header file for PNI RM3100 driver
> > > > > + *
> > > > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > > > + */
> > > > > +
> > > > > +#ifndef RM3100_CORE_H
> > > > > +#define RM3100_CORE_H
> > > > > +
> > > > > +#include <linux/module.h>
> > > > > +#include <linux/regmap.h>
> > > > > +
> > > > > +#define RM_REG_REV_ID		0x36
> > > > > +
> > > > > +/* Cycle Count Registers MSBs and LSBs. */
> > > > > +#define RM_REG_CCXM		0x04
> > > > > +#define RM_REG_CCXL		0x05
> > > > > +#define RM_REG_CCYM		0x06
> > > > > +#define RM_REG_CCYL		0x07
> > > > > +#define RM_REG_CCZM		0x08
> > > > > +#define RM_REG_CCZL		0x09
> > > > > +
> > > > > +/* Single Measurement Mode register. */
> > > > > +#define RM_REG_POLL		0x00
> > > > > +#define RM_POLL_PMX		BIT(4)
> > > > > +#define RM_POLL_PMY		BIT(5)
> > > > > +#define RM_POLL_PMZ		BIT(6)
> > > > > +
> > > > > +/* Continues Measurement Mode register. */
> > > > > +#define RM_REG_CMM		0x01
> > > > > +#define RM_CMM_START		BIT(0)
> > > > > +#define RM_CMM_DRDM		BIT(2)
> > > > > +#define RM_CMM_PMX		BIT(4)
> > > > > +#define RM_CMM_PMY		BIT(5)
> > > > > +#define RM_CMM_PMZ		BIT(6)
> > > > > +
> > > > > +/* TiMe Rate Configuration register. */
> > > > > +#define RM_REG_TMRC		0x0B
> > > > > +#define RM_TMRC_OFFSET		0x92
> > > > > +
> > > > > +/* Result Status register. */
> > > > > +#define RM_REG_STATUS		0x34
> > > > > +#define RM_STATUS_DRDY		BIT(7)
> > > > > +
> > > > > +/* Measurement result registers. */
> > > > > +#define RM_REG_MX2		0x24
> > > > > +#define RM_REG_MX1		0x25
> > > > > +#define RM_REG_MX0		0x26
> > > > > +#define RM_REG_MY2		0x27
> > > > > +#define RM_REG_MY1		0x28
> > > > > +#define RM_REG_MY0		0x29
> > > > > +#define RM_REG_MZ2		0x2a
> > > > > +#define RM_REG_MZ1		0x2b
> > > > > +#define RM_REG_MZ0		0x2c
> > > > > +
> > > > > +#define RM_REG_HSHAKE		0x35
> > > > > +
> > > > > +#define RM_W_REG_START		RM_REG_POLL
> > > > > +#define RM_W_REG_END		RM_REG_REV_ID
> > > > > +#define RM_R_REG_START		RM_REG_POLL
> > > > > +#define RM_R_REG_END		RM_REG_HSHAKE
> > > > > +#define RM_V_REG_START		RM_REG_MX2
> > > > > +#define RM_V_REG_END		RM_REG_HSHAKE
> > > > > +
> > > > > +/* Built-In Self Test reigister. */
> > > > > +#define RM_REG_BIST		0x33
> > > > > +
> > > > > +struct rm3100_data {
> > > > > +	struct device *dev;
> > > > > +	struct regmap *regmap;
> > > > > +	struct completion measuring_done;
> > > > > +	bool use_interrupt;
> > > > > +
> > > > > +	int conversion_time;
> > > > > +
> > > > > +	/* To protect consistency of every measurement and sampling
> > > > > +	 * frequency change operations.
> > > > > +	 */
> > > > > +	struct mutex lock;
> > > > > +};
> > > > > +
> > > > > +extern const struct regmap_access_table rm3100_readable_table;
> > > > > +extern const struct regmap_access_table rm3100_writable_table;
> > > > > +extern const struct regmap_access_table rm3100_volatile_table;
> > > > > +
> > > > > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> > > > > +int rm3100_common_remove(struct device *dev);
> > > > > +
> > > > > +#endif /* RM3100_CORE_H */
> > > > >   
> > > >   
> > > 
> > > 
> > > -- 
> > > Regards
> > > Phil Reid
> > >   
> 
> 

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-20 18:05   ` Song Qiang
@ 2018-09-22  9:42     ` Jonathan Cameron
  2018-09-22 10:08       ` Jonathan Cameron
  0 siblings, 1 reply; 19+ messages in thread
From: Jonathan Cameron @ 2018-09-22  9:42 UTC (permalink / raw)
  To: Song Qiang
  Cc: Peter Meerwald-Stadler, lars, knaack.h, robh+dt, mark.rutland,
	devicetree, linux-iio, linux-kernel

A few follow ups to the discussion here from me..

Note it's helpful to crop and email and no one really minds if you
just act on their review without acknowledging it (so cut the
bits you fully agree with out too - saves on scrolling / reading time ;)

A catch all, "Agree with everything else and will fix for v2" covers
everything you don't want to discuss further.
(not that I'm great at doing this either :(
...
> > > diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
> > > new file mode 100644
> > > index 000000000000..55d515e0fe67
> > > --- /dev/null
> > > +++ b/drivers/iio/magnetometer/rm3100-core.c
> > > @@ -0,0 +1,399 @@
> > > +// SPDX-License-Identifier: GPL-2.0+
> > > +/*
> > > + * PNI RM3100 9-axis geomagnetic sensor driver core.
> > > + *
> > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > + *
> > > + * User Manual available at
> > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > + *
> > > + * TODO: Scale channel, event generaton, pm.  
> > 
> > at least read support for _SCALE is mandatory, IMHO
> >   
> 
> Okay, I'll add it in next version.
> 
Just for the record, it's only mandatory in cases where
it is known (you'd be amazed how often we only know the value is monotonic)

Now as you put it in the todo list, it's presumably known here
hence Peter's comment :)

(just avoiding setting precedence)



...
> > > +static const struct regmap_range rm3100_readable_ranges[] = {
> > > +		regmap_reg_range(RM_W_REG_START, RM_W_REG_END),
> > > +};
> > > +
> > > +const struct regmap_access_table rm3100_readable_table = {  
> > 
> > static
> >   
> 
> I was planning to let rm3100-i2c.c and rm3100-spi.c to share these 6
> structures, because the only different configuration of regmap between
> these two files lies in 'struct regmap_config'. To achieve this, I have
> to expose these 3 structures to be referenced in rm3100-i2c.c and
> rm3100-spi.c
> Since *_common_probe() and *_common_remove() are exposed, I thought it
> was fine to expose these structures to reduce redundant code, is this
> prohibited?
Nope, but are you doing it in this patch? + you need to export the
symbols if you are going to do that otherwise modular builds
will fail to probe (and possibly build - I can't recall and am too
lazy to check this morning - not enough coffee yet!)

..
> > > +	*val = le32_to_cpu((buffer[0] << 16) + (buffer[1] << 8) + buffer[2]);  
> > 
> > no need for le32_to_cpu() 
> >   
> 
> I think I didn't fully understand this, I'll look into it.
> 

To point you in the right direction here.   When you explicitly pull out
individual bytes like you are doing here, you are getting them in a particular
endian order.   Shifts and adding (though normally convention is | when doing
this) are done in machine endianness, hence the *val is already in the
endian type of your cpu.

> > > +	*val = sign_extend32(*val, 23);
> > > +
> > > +	return IIO_VAL_INT;
> > > +}
> > > +
> > > +#define RM_CHANNEL(axis, idx)					\  
> > 
> > use RM3100_ prefix please
> >   
> 
> In the last driver I wrote, name of registers are so long that I have to
> suppress them as possible as I can, it seems like this one doesn't need
> to. :)
> 
> > > +	{								\
> > > +		.type = IIO_MAGN,					\
> > > +		.modified = 1,						\
> > > +		.channel2 = IIO_MOD_##axis,				\
> > > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> > > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> > > +		.scan_index = idx,					\
> > > +		.scan_type = {						\
> > > +			.sign = 's',					\
> > > +			.realbits = 24,					\
> > > +			.storagebits = 32,				\
> > > +			.shift = 8,					\
> > > +			.endianness = IIO_LE,				\
> > > +		},							\
> > > +	}
> > > +
> > > +static const struct iio_chan_spec rm3100_channels[] = {
> > > +	RM_CHANNEL(X, 0),
> > > +	RM_CHANNEL(Y, 1),
> > > +	RM_CHANNEL(Z, 2),
> > > +	IIO_CHAN_SOFT_TIMESTAMP(3),
> > > +};
> > > +
> > > +static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
> > > +
> > > +#define RM_SAMP_NUM	14  
> > 
> > prefix
> >   
> > > +
> > > +/* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> > > + * Time between reading: rm3100_sam_rates[][2]ms (The first on is actially 1.7).  
> > 
> > one
> > actually
> > 1.7 what unit?
> >   
> 
> It's in milliseconds. These time values are used for lookup so I do not
> need to compute time between conversion from measurement frequency, and
> they are used for wait time, I thought a little longer is better.
> I think the comment above this structure isn't very clear, I'll find a
> better way to explain it.
Please also use kernel standard comment syntax

/*
 * Frequency...
 */

> 
...
> > > +	if (i != RM_SAMP_NUM) {
> > > +		mutex_lock(&data->lock);
> > > +		ret = regmap_write(regmap, RM_REG_TMRC, i + RM_TMRC_OFFSET);
> > > +		if (ret < 0)  
> > 
> > unlock?
> >   
> 
> These actions are for changing the sampling frequency. This device
> cannot start conversion if CMM register is not reset after reading from
> CCX/CCY/CCZ registers. So I unlock it later since conversion should have
> already been stopped and other threads should not access the bus.
Hmm.  If you are holding the lock across function calls you need
to look at lockdep annotations. 

Also, I suspect something is wrong here as you are unlocking in
the good path but not the bad one which seems unlikely to be 
as intended...

> 
> > > +			return ret;
> > > +
> > > +		/* Checking if cycle count registers need changing. */
> > > +		if (val == 600 && cycle_count == 200) {
> > > +			for (i = 0; i < 3; i++) {
> > > +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 100);
> > > +				if (ret < 0)  
> > 
> > unlock?
> >   
> > > +					return ret;
> > > +			}
> > > +		} else if (val != 600 && cycle_count == 100) {
> > > +			for (i = 0; i < 3; i++) {
> > > +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 200);
> > > +				if (ret < 0)  
> > 
> > unlock?
> >   
> > > +					return ret;
> > > +			}
> > > +		}
> > > +		/* Writing TMRC registers requires CMM reset. */
> > > +		ret = regmap_write(regmap, RM_REG_CMM, 0);
> > > +		if (ret < 0)  
> > 
> > unlock?
> >   
> > > +			return ret;
> > > +		ret = regmap_write(regmap, RM_REG_CMM, RM_CMM_PMX |
> > > +			RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > > +		if (ret < 0)  
> > 
> > unlock?
> >   
> > > +			return ret;
> > > +		mutex_unlock(&data->lock);
> > > +
> > > +		data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> > > +		return 0;
> > > +	}
> > > +	return -EINVAL;
> > > +}
> > > +
> > > +static int rm3100_read_raw(struct iio_dev *indio_dev,
> > > +			   const struct iio_chan_spec *chan,
> > > +			   int *val, int *val2, long mask)
> > > +{
> > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > +	int ret;
> > > +
> > > +	switch (mask) {
> > > +	case IIO_CHAN_INFO_RAW:
> > > +		ret = iio_device_claim_direct_mode(indio_dev);
> > > +		if (ret < 0)  
> > 
> > release_direct_mode() here?
> >   
> 
> Oh..yes!

This should have stopped you reading more than once so I'm surprised
this slipped through.  I guess the usual last minute change problem ;)
(we all do it however much we know we should retest properly)

Jonathan

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-22  9:42     ` Jonathan Cameron
@ 2018-09-22 10:08       ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2018-09-22 10:08 UTC (permalink / raw)
  To: Song Qiang
  Cc: Peter Meerwald-Stadler, lars, knaack.h, robh+dt, mark.rutland,
	devicetree, linux-iio, linux-kernel

On Sat, 22 Sep 2018 10:42:44 +0100
Jonathan Cameron <jic23@kernel.org> wrote:

> A few follow ups to the discussion here from me..
> 
> Note it's helpful to crop and email and no one really minds if you
> just act on their review without acknowledging it (so cut the
> bits you fully agree with out too - saves on scrolling / reading time ;)
> 
> A catch all, "Agree with everything else and will fix for v2" covers
> everything you don't want to discuss further.
> (not that I'm great at doing this either :(
> ...
> > > > diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
> > > > new file mode 100644
> > > > index 000000000000..55d515e0fe67
> > > > --- /dev/null
> > > > +++ b/drivers/iio/magnetometer/rm3100-core.c
> > > > @@ -0,0 +1,399 @@
> > > > +// SPDX-License-Identifier: GPL-2.0+
> > > > +/*
> > > > + * PNI RM3100 9-axis geomagnetic sensor driver core.
> > > > + *
> > > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > > + *
> > > > + * User Manual available at
> > > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > > + *
> > > > + * TODO: Scale channel, event generaton, pm.    
> > > 
> > > at least read support for _SCALE is mandatory, IMHO
> > >     
> > 
> > Okay, I'll add it in next version.
> >   
> Just for the record, it's only mandatory in cases where
> it is known (you'd be amazed how often we only know the value is monotonic)
> 
> Now as you put it in the todo list, it's presumably known here
> hence Peter's comment :)
> 
> (just avoiding setting precedence)
> 
> 
> 
> ...
> > > > +static const struct regmap_range rm3100_readable_ranges[] = {
> > > > +		regmap_reg_range(RM_W_REG_START, RM_W_REG_END),
> > > > +};
> > > > +
> > > > +const struct regmap_access_table rm3100_readable_table = {    
> > > 
> > > static
> > >     
> > 
> > I was planning to let rm3100-i2c.c and rm3100-spi.c to share these 6
> > structures, because the only different configuration of regmap between
> > these two files lies in 'struct regmap_config'. To achieve this, I have
> > to expose these 3 structures to be referenced in rm3100-i2c.c and
> > rm3100-spi.c
> > Since *_common_probe() and *_common_remove() are exposed, I thought it
> > was fine to expose these structures to reduce redundant code, is this
> > prohibited?  
> Nope, but are you doing it in this patch? + you need to export the
> symbols if you are going to do that otherwise modular builds
> will fail to probe (and possibly build - I can't recall and am too
> lazy to check this morning - not enough coffee yet!)
> 
> ..
> > > > +	*val = le32_to_cpu((buffer[0] << 16) + (buffer[1] << 8) + buffer[2]);    
> > > 
> > > no need for le32_to_cpu() 
> > >     
> > 
> > I think I didn't fully understand this, I'll look into it.
> >   
> 
> To point you in the right direction here.   When you explicitly pull out
> individual bytes like you are doing here, you are getting them in a particular
> endian order.   Shifts and adding (though normally convention is | when doing
> this) are done in machine endianness, hence the *val is already in the
> endian type of your cpu.
> 
> > > > +	*val = sign_extend32(*val, 23);
> > > > +
> > > > +	return IIO_VAL_INT;
> > > > +}
> > > > +
> > > > +#define RM_CHANNEL(axis, idx)					\    
> > > 
> > > use RM3100_ prefix please
> > >     
> > 
> > In the last driver I wrote, name of registers are so long that I have to
> > suppress them as possible as I can, it seems like this one doesn't need
> > to. :)
> >   
> > > > +	{								\
> > > > +		.type = IIO_MAGN,					\
> > > > +		.modified = 1,						\
> > > > +		.channel2 = IIO_MOD_##axis,				\
> > > > +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> > > > +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> > > > +		.scan_index = idx,					\
> > > > +		.scan_type = {						\
> > > > +			.sign = 's',					\
> > > > +			.realbits = 24,					\
> > > > +			.storagebits = 32,				\
> > > > +			.shift = 8,					\
> > > > +			.endianness = IIO_LE,				\
> > > > +		},							\
> > > > +	}
> > > > +
> > > > +static const struct iio_chan_spec rm3100_channels[] = {
> > > > +	RM_CHANNEL(X, 0),
> > > > +	RM_CHANNEL(Y, 1),
> > > > +	RM_CHANNEL(Z, 2),
> > > > +	IIO_CHAN_SOFT_TIMESTAMP(3),
> > > > +};
> > > > +
> > > > +static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
> > > > +
> > > > +#define RM_SAMP_NUM	14    
> > > 
> > > prefix
> > >     
> > > > +
> > > > +/* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> > > > + * Time between reading: rm3100_sam_rates[][2]ms (The first on is actially 1.7).    
> > > 
> > > one
> > > actually
> > > 1.7 what unit?
> > >     
> > 
> > It's in milliseconds. These time values are used for lookup so I do not
> > need to compute time between conversion from measurement frequency, and
> > they are used for wait time, I thought a little longer is better.
> > I think the comment above this structure isn't very clear, I'll find a
> > better way to explain it.  
> Please also use kernel standard comment syntax
> 
> /*
>  * Frequency...
>  */
> 
> >   
> ...
> > > > +	if (i != RM_SAMP_NUM) {
> > > > +		mutex_lock(&data->lock);
> > > > +		ret = regmap_write(regmap, RM_REG_TMRC, i + RM_TMRC_OFFSET);
> > > > +		if (ret < 0)    
> > > 
> > > unlock?
> > >     
> > 
> > These actions are for changing the sampling frequency. This device
> > cannot start conversion if CMM register is not reset after reading from
> > CCX/CCY/CCZ registers. So I unlock it later since conversion should have
> > already been stopped and other threads should not access the bus.  
> Hmm.  If you are holding the lock across function calls you need
> to look at lockdep annotations. 
> 
> Also, I suspect something is wrong here as you are unlocking in
> the good path but not the bad one which seems unlikely to be 
> as intended...
> 
> >   
> > > > +			return ret;
> > > > +
> > > > +		/* Checking if cycle count registers need changing. */
> > > > +		if (val == 600 && cycle_count == 200) {
> > > > +			for (i = 0; i < 3; i++) {
> > > > +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 100);
> > > > +				if (ret < 0)    
> > > 
> > > unlock?
> > >     
> > > > +					return ret;
> > > > +			}
> > > > +		} else if (val != 600 && cycle_count == 100) {
> > > > +			for (i = 0; i < 3; i++) {
> > > > +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 200);
> > > > +				if (ret < 0)    
> > > 
> > > unlock?
> > >     
> > > > +					return ret;
> > > > +			}
> > > > +		}
> > > > +		/* Writing TMRC registers requires CMM reset. */
> > > > +		ret = regmap_write(regmap, RM_REG_CMM, 0);
> > > > +		if (ret < 0)    
> > > 
> > > unlock?
> > >     
> > > > +			return ret;
> > > > +		ret = regmap_write(regmap, RM_REG_CMM, RM_CMM_PMX |
> > > > +			RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > > > +		if (ret < 0)    
> > > 
> > > unlock?
> > >     
> > > > +			return ret;
> > > > +		mutex_unlock(&data->lock);
> > > > +
> > > > +		data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> > > > +		return 0;
> > > > +	}
> > > > +	return -EINVAL;
> > > > +}
> > > > +
> > > > +static int rm3100_read_raw(struct iio_dev *indio_dev,
> > > > +			   const struct iio_chan_spec *chan,
> > > > +			   int *val, int *val2, long mask)
> > > > +{
> > > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > > +	int ret;
> > > > +
> > > > +	switch (mask) {
> > > > +	case IIO_CHAN_INFO_RAW:
> > > > +		ret = iio_device_claim_direct_mode(indio_dev);
> > > > +		if (ret < 0)    
> > > 
> > > release_direct_mode() here?
> > >     
> > 
> > Oh..yes!  
> 
> This should have stopped you reading more than once so I'm surprised
> this slipped through.  I guess the usual last minute change problem ;)
> (we all do it however much we know we should retest properly)
Ah. Just realised. Error path :)

> 
> Jonathan


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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-20 13:13 [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer Song Qiang
  2018-09-20 13:46 ` Peter Meerwald-Stadler
  2018-09-21  2:05 ` Phil Reid
@ 2018-09-22 10:14 ` Jonathan Cameron
  2018-09-23 15:17   ` Song Qiang
  2018-09-24 14:37   ` Song Qiang
  2018-09-23 11:01 ` Dan Carpenter
       [not found] ` <5baa1b01.1c69fb81.f0d8f.30c6@mx.google.com>
  4 siblings, 2 replies; 19+ messages in thread
From: Jonathan Cameron @ 2018-09-22 10:14 UTC (permalink / raw)
  To: Song Qiang
  Cc: knaack.h, lars, pmeerw, robh+dt, mark.rutland, linux-iio,
	devicetree, linux-kernel

On Thu, 20 Sep 2018 21:13:40 +0800
Song Qiang <songqiang1304521@gmail.com> wrote:

> PNI RM3100 magnetometer is a high resolution, large signal immunity
> magnetometer, composed of 3 single sensors and a processing chip.
> PNI is currently not in the vendors list, so this is also adding it.
> 
> Following functions are available:
>  - Single-shot measurement from
>    /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
>  - Triggerd buffer measurement.
>  - Both i2c and spi interface are supported.
>  - Both interrupt and polling measurement is supported, depands on if
>    the 'interrupts' in DT is declared.
> 
> Signed-off-by: Song Qiang <songqiang1304521@gmail.com>
Hi Song,

Great to have a driver for a pni device.  I hadn't come across them
before!

I'll try and avoid repeating Peter and Phil's comments on the basis
you probably have those covered by now.  I would have left this
for V2 before reviewing but not sure if I'll get to IIO stuff
during the coming week so better to get you some comments from me today.

Thanks,

Jonathan

> ---
>  .../bindings/iio/magnetometer/pni,rm3100.txt  |  57 +++
>  .../devicetree/bindings/vendor-prefixes.txt   |   1 +
>  MAINTAINERS                                   |  10 +
>  drivers/iio/magnetometer/Kconfig              |  29 ++
>  drivers/iio/magnetometer/Makefile             |   4 +
>  drivers/iio/magnetometer/rm3100-core.c        | 399 ++++++++++++++++++
>  drivers/iio/magnetometer/rm3100-i2c.c         |  66 +++
>  drivers/iio/magnetometer/rm3100-spi.c         |  72 ++++
>  drivers/iio/magnetometer/rm3100.h             |  90 ++++
>  9 files changed, 728 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
>  create mode 100644 drivers/iio/magnetometer/rm3100-core.c
>  create mode 100644 drivers/iio/magnetometer/rm3100-i2c.c
>  create mode 100644 drivers/iio/magnetometer/rm3100-spi.c
>  create mode 100644 drivers/iio/magnetometer/rm3100.h
> 
> diff --git a/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> new file mode 100644
> index 000000000000..d0d2063e943f
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> @@ -0,0 +1,57 @@
> +* PNI RM3100 9-axis magnetometer sensor
> +
> +I2C Bus:
> +
> +Required properties:
> +
> +- compatible : should be "pni,rm3100-i2c"
> +- reg : the I2C address of the magnetometer
> +
> +Optional properties:
> +
> +- interrupts: data ready (DRDY) from the chip.
> +  The interrupts can be triggered on rising edges.
> +
> +	Refer to interrupt-controller/interrupts.txt for generic
> +	interrupt client node bindings.
> +
> +- pinctrl-*: pinctrl setup for DRDY line.

I'm not a DT expert by any means, but I think the convention is
to not mention pinctrl in a given driver because otherwise
in theory we may need to mention it everywhere...

> +
> +Example:
> +
> +rm3100: rm3100@20 {
> +	pinctrl-names = "default";
> +	pinctrl-0 = <&rm3100_pins>;
> +
> +	compatible = "pni,rm3100-i2c";
> +	reg = <0x20>;
> +	interrupt-parent = <&gpio0>;
> +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> +};
> +
> +SPI Bus:
> +
> +Required properties:
> +
> +- compatible : should be "pni,rm3100-spi"
> +- reg : address of sensor, usually 0 or 1.
> +
> +Optional properties:
> +
> +- interrupts: data ready (DRDY) from the chip.
> +  The interrupts can be triggered on rising edges.
> +
> +	Refer to interrupt-controller/interrupts.txt for generic
> +	interrupt client node bindings.
> +
> +- pinctrl-*: pinctrl setup for DRDY line, depands on archtechture.
> +
> +Example:
> +
> +rm3100: rm3100@0{
> +	compatible = "pni,rm3100-spi";
> +	reg = <0>;
> +
> +	interrupt-parent = <&gpio0>;
> +	interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> +};
> diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> index 41f0b97eb933..5bf3395fe9ae 100644
> --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> @@ -288,6 +288,7 @@ pine64	Pine64
>  pixcir  PIXCIR MICROELECTRONICS Co., Ltd
>  plathome	Plat'Home Co., Ltd.
>  plda	PLDA
> +pni     PNI

Please break the whole binding out as a separate patch.  Ideally do this
vendor-prefixes.txt as a separate patch again.  This cuts down
on the amount of searching that the DT people have to do to find
the bits that they want to look at.

>  portwell	Portwell Inc.
>  poslab	Poslab Technology Co., Ltd.
>  powervr	PowerVR (deprecated, use img)
> diff --git a/MAINTAINERS b/MAINTAINERS
> index 967ce8cdd1cc..30ee8cf98312 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11393,6 +11393,16 @@ M:	"Rafael J. Wysocki" <rafael.j.wysocki@intel.com>
>  S:	Maintained
>  F:	drivers/pnp/
>  
> +PNI RM3100 IIO DRIVER
> +M:	Song Qiang <songqiang1304521@gmail.com>
> +L:	linux-iio@vger.kernel.org
> +S:	Maintained
> +F:	drivers/iio/magnetometer/rm3100-core.c

rm3110* perhaps?

> +F:	drivers/iio/magnetometer/rm3100-i2c.c
> +F:	drivers/iio/magnetometer/rm3100-spi.c
> +F:	drivers/iio/magnetometer/rm3100.h
> +F:	Documentation/devicetree/bindings/iio/magnetometer/rm3100.txt
> +
>  POSIX CLOCKS and TIMERS
>  M:	Thomas Gleixner <tglx@linutronix.de>
>  L:	linux-kernel@vger.kernel.org
> diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
> index ed9d776d01af..f130b866a4fc 100644
> --- a/drivers/iio/magnetometer/Kconfig
> +++ b/drivers/iio/magnetometer/Kconfig
> @@ -175,4 +175,33 @@ config SENSORS_HMC5843_SPI
>  	  - hmc5843_core (core functions)
>  	  - hmc5843_spi (support for HMC5983)
>  
> +config SENSORS_RM3100
> +	tristate
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +
> +config SENSORS_RM3100_I2C
> +	tristate "PNI RM3100 9-Axis Magnetometer (I2C)"
> +	depends on I2C
> +	select SENSORS_RM3100
> +	select REGMAP_I2C
> +	help
> +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
> +
> +	  This driver can also be compiled as a module.
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called rm3100-i2c.
> +
> +config SENSORS_RM3100_SPI
> +	tristate "PNI RM3100 9-Axis Magnetometer (SPI)"
> +	depends on SPI_MASTER
> +	select SENSORS_RM3100
> +	select REGMAP_SPI
> +	help
> +	  Say Y here to add support for the PNI RM3100 9-Axis Magnetometer.
> +
> +	  This driver can also be compiled as a module.
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called rm3100-spi.
> +
>  endmenu
> diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
> index 664b2f866472..ba1bc34b82fa 100644
> --- a/drivers/iio/magnetometer/Makefile
> +++ b/drivers/iio/magnetometer/Makefile
> @@ -24,3 +24,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
>  obj-$(CONFIG_SENSORS_HMC5843)		+= hmc5843_core.o
>  obj-$(CONFIG_SENSORS_HMC5843_I2C)	+= hmc5843_i2c.o
>  obj-$(CONFIG_SENSORS_HMC5843_SPI)	+= hmc5843_spi.o
> +
> +obj-$(CONFIG_SENSORS_RM3100)		+= rm3100-core.o
> +obj-$(CONFIG_SENSORS_RM3100_I2C)	+= rm3100-i2c.o
> +obj-$(CONFIG_SENSORS_RM3100_SPI)	+= rm3100-spi.o
> diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
> new file mode 100644
> index 000000000000..55d515e0fe67
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-core.c
> @@ -0,0 +1,399 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * PNI RM3100 9-axis geomagnetic sensor driver core.
> + *
> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + *
> + * TODO: Scale channel, event generaton, pm.
> + */
> +

module.h should be included here not in the header (see below)

> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/kfifo_buf.h>
> +
> +#include "rm3100.h"
> +
> +static const struct regmap_range rm3100_readable_ranges[] = {
> +		regmap_reg_range(RM_W_REG_START, RM_W_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_readable_table = {
> +		.yes_ranges = rm3100_readable_ranges,
> +		.n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> +};
> +
> +static const struct regmap_range rm3100_writable_ranges[] = {
> +		regmap_reg_range(RM_R_REG_START, RM_R_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_writable_table = {
> +		.yes_ranges = rm3100_writable_ranges,
> +		.n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> +};
> +
> +static const struct regmap_range rm3100_volatile_ranges[] = {
> +		regmap_reg_range(RM_V_REG_START, RM_V_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_volatile_table = {
> +		.yes_ranges = rm3100_volatile_ranges,
> +		.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> +};
> +
> +static irqreturn_t rm3100_measurement_irq_handler(int irq, void *d)

Silly question: Does the chip have two interrupt lines?  (if so they
should be in the binding). If not, then this is the irq handler
for everything so why have the measurement in it's name?

> +{
> +	struct rm3100_data *data = d;
> +
> +	complete(&data->measuring_done);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int rm3100_wait_measurement(struct rm3100_data *data)
> +{
> +	struct regmap *regmap = data->regmap;
> +	unsigned int val;
> +	u16 tries = 20;
> +	int ret;
> +
> +	/* A read cycle of 400kbits i2c bus is about 20us, plus the time
> +	 * used for schduling, a read cycle of fast mode of this device
> +	 * can reach 1.7ms, it may be possible for data arrives just
> +	 * after we check the RM_REG_STATUS. In this case, irq_handler is
> +	 * called before measuring_done is reinitialized, it will wait
> +	 * forever for a data that has already been ready.
> +	 * Reinitialize measuring_done before looking up makes sure we
> +	 * will always capture interrupt no matter when it happened.
> +	 */
> +	if (data->use_interrupt)
> +		reinit_completion(&data->measuring_done);
> +
> +	ret = regmap_read(regmap, RM_REG_STATUS, &val);
> +	if (ret < 0)
> +		return ret;
> +
> +	if ((val & RM_STATUS_DRDY) != RM_STATUS_DRDY) {
> +		if (data->use_interrupt) {
> +			ret = wait_for_completion_timeout(&data->measuring_done,
> +				msecs_to_jiffies(data->conversion_time));
> +			if (!ret)
> +				return -ETIMEDOUT;
> +		} else {
> +			do {
> +				ret = regmap_read(regmap, RM_REG_STATUS, &val);
> +				if (ret < 0)
> +					return ret;
> +
> +				if (val & RM_STATUS_DRDY)
> +					break;
> +
> +				usleep_range(1000, 5000);
> +			} while (--tries);
> +			if (!tries)
> +				return -ETIMEDOUT;
> +		}
> +	}
> +	return 0;
> +}
> +
> +static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val)
> +{
> +	struct regmap *regmap = data->regmap;
> +	u8 buffer[3];
> +	int ret;
> +
> +	mutex_lock(&data->lock);
> +	ret = rm3100_wait_measurement(data);
> +	if (ret < 0) {
> +		mutex_unlock(&data->lock);
> +		return ret;
> +	}
> +
> +	ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * idx, buffer, 3);
> +	mutex_unlock(&data->lock);
> +	if (ret < 0)
> +		return ret;
> +
> +	*val = le32_to_cpu((buffer[0] << 16) + (buffer[1] << 8) + buffer[2]);
> +	*val = sign_extend32(*val, 23);
> +
> +	return IIO_VAL_INT;
> +}
> +
> +#define RM_CHANNEL(axis, idx)					\
> +	{								\
> +		.type = IIO_MAGN,					\
> +		.modified = 1,						\
> +		.channel2 = IIO_MOD_##axis,				\
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
> +		.scan_index = idx,					\
> +		.scan_type = {						\
> +			.sign = 's',					\
> +			.realbits = 24,					\
> +			.storagebits = 32,				\
> +			.shift = 8,					\
> +			.endianness = IIO_LE,				\
> +		},							\
> +	}
> +
> +static const struct iio_chan_spec rm3100_channels[] = {
> +	RM_CHANNEL(X, 0),
> +	RM_CHANNEL(Y, 1),
> +	RM_CHANNEL(Z, 2),
> +	IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
> +
> +#define RM_SAMP_NUM	14
> +
> +/* Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> + * Time between reading: rm3100_sam_rates[][2]ms (The first on is actially 1.7).
> + */
> +static const int rm3100_samp_rates[RM_SAMP_NUM][3] = {
> +	{600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27},
> +	{18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440},
> +	{1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300},
> +	{0, 15000, 6700},  {0, 75000, 13000}
> +};
> +
> +static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
> +{
> +	int ret;
> +	int tmp;
> +
> +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> +	if (ret < 0)
> +		return ret;
> +	*val = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][0];
> +	*val2 = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][1];
> +
> +	return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
> +{
> +	struct regmap *regmap = data->regmap;
> +	int cycle_count;
> +	int ret;
> +	int i;
> +
> +	/* All cycle count registers use the same value. */
> +	ret = regmap_read(regmap, RM_REG_CCXL, &cycle_count);
> +	if (cycle_count < 0)
> +		return cycle_count;
> +
> +	for (i = 0; i < RM_SAMP_NUM; i++) {
> +		if (val == rm3100_samp_rates[i][0] &&
> +			val2 == rm3100_samp_rates[i][1])
> +			break;
> +	}
> +
> +	if (i != RM_SAMP_NUM) {
> +		mutex_lock(&data->lock);
> +		ret = regmap_write(regmap, RM_REG_TMRC, i + RM_TMRC_OFFSET);
> +		if (ret < 0)

Peter raised this anyway but critical you unlock in these error paths.

> +			return ret;
> +
> +		/* Checking if cycle count registers need changing. */
> +		if (val == 600 && cycle_count == 200) {
> +			for (i = 0; i < 3; i++) {
> +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 100);
> +				if (ret < 0)
> +					return ret;
> +			}
> +		} else if (val != 600 && cycle_count == 100) {
> +			for (i = 0; i < 3; i++) {
> +				regmap_write(regmap, RM_REG_CCXL + 2 * i, 200);
> +				if (ret < 0)
> +					return ret;
> +			}
> +		}
> +		/* Writing TMRC registers requires CMM reset. */
> +		ret = regmap_write(regmap, RM_REG_CMM, 0);
> +		if (ret < 0)
> +			return ret;
> +		ret = regmap_write(regmap, RM_REG_CMM, RM_CMM_PMX |
> +			RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);

That is not nice line breaking...

> +		if (ret < 0)
> +			return ret;
> +		mutex_unlock(&data->lock);
> +
> +		data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> +		return 0;
> +	}
> +	return -EINVAL;
Flip the logic here so 
if (i == RM_SAMP_NUM)
	return -EINVAL;

... cary on.

A;ways more natural to find errors on the indented path + it
drops your indent by one tab for most of the code.
> +}
> +
> +static int rm3100_read_raw(struct iio_dev *indio_dev,
> +			   const struct iio_chan_spec *chan,
> +			   int *val, int *val2, long mask)
> +{
> +	struct rm3100_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
> +		ret = iio_device_claim_direct_mode(indio_dev);
> +		if (ret < 0)
> +			return ret;
> +		ret = rm3100_read_mag(data, chan->scan_index, val);
> +		iio_device_release_direct_mode(indio_dev);
> +
> +		return ret;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		return ret = rm3100_get_samp_freq(data, val, val2);
> +	default:
> +		return -EINVAL;
> +	}
> +}
> +
> +static int rm3100_write_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int val, int val2, long mask)
> +{
> +	struct rm3100_data *data = iio_priv(indio_dev);
> +	int ret;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		ret = rm3100_set_samp_freq(data, val, val2);
> +		if (ret < 0)
> +			return ret;
ids set_samp_freq ever returning greater than 0?

If not just return rm3100_set_samp_freq directly.

> +		return 0;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +}
> +
> +static const struct iio_info rm3100_info = {
> +	.read_raw = rm3100_read_raw,
> +	.write_raw = rm3100_write_raw,
> +};
> +
> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct rm3100_data *data = iio_priv(indio_dev);
> +	struct regmap *regmap = data->regmap;
> +	u8 *buffer;
> +	int ret;
> +	int i;
> +
> +	buffer = devm_kzalloc(data->dev, indio_dev->scan_bytes, GFP_KERNEL);
That is 'interesting'.  You are leaking a buffer every time this reads..

> +	if (!buffer)
> +		goto done;
> +
> +	mutex_lock(&data->lock);
> +	ret = rm3100_wait_measurement(data);
> +	if (ret < 0) {
> +		mutex_unlock(&data->lock);
> +		goto done;
> +	}
> +
> +	for (i = 0; i < 3; i++) {
> +		ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * i,
> +				buffer + 4 * i, 3);
> +		if (ret < 0)
> +			return ret;
> +	}
> +	mutex_unlock(&data->lock);
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
> +			iio_get_time_ns(indio_dev));
> +done:
> +	iio_trigger_notify_done(indio_dev->trig);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> +{
> +	struct iio_dev *indio_dev;
> +	struct rm3100_data *data;
> +	int tmp;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	data = iio_priv(indio_dev);
> +	dev_set_drvdata(dev, indio_dev);
> +	data->dev = dev;
> +	data->regmap = regmap;
> +
> +	mutex_init(&data->lock);
> +
> +	indio_dev->dev.parent = dev;
> +	indio_dev->name = "rm3100";
> +	indio_dev->info = &rm3100_info;
> +	indio_dev->channels = rm3100_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->available_scan_masks = rm3100_scan_masks;
> +
> +	if (!irq)
> +		data->use_interrupt = false;
> +	else {
> +		data->use_interrupt = true;
> +		ret = devm_request_irq(dev,
> +			irq,
> +			rm3100_measurement_irq_handler,
> +			IRQF_TRIGGER_RISING,
> +			indio_dev->name,
> +			data);
> +		if (ret < 0) {
> +			dev_err(dev,
> +			"request irq line failed.");
It could be diff being annoying but I'm guessing this isn't aligned
with the opening bracket.  Sometimes it's hard to do but when you
can do it easily please do as it helps readability.

> +			return -ret;
> +		}
> +		init_completion(&data->measuring_done);
> +	}
> +
> +	ret = iio_triggered_buffer_setup(indio_dev, NULL,
> +					rm3100_trigger_handler, NULL);

devm_

Otherwise you don't clean this up properly.

> +	if (ret < 0)
> +		return ret;
> +
> +	/* 3sec more wait time. */
> +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> +
> +	/* Starting all channels' conversion. */
> +	ret = regmap_write(regmap, RM_REG_CMM,
> +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> +	if (ret < 0)
> +		return ret;
> +
> +	return devm_iio_device_register(dev, indio_dev);
Nope.  Can't do this without having a race condition.   You need
to ensure the userspace and in kernel interfaces are removed 'before'.
you do that RM_REG_CMM write in remove.

One option is to use devm_add_action to add a custom unwind function
to the automatic handling. The other is to not use devm for everything 
after the write above and do the device_unregister manually.

> +}
> +EXPORT_SYMBOL(rm3100_common_probe);
> +
> +int rm3100_common_remove(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> +	struct rm3100_data *data = iio_priv(indio_dev);
> +	struct regmap *regmap = data->regmap;
> +
> +	regmap_write(regmap, RM_REG_CMM, 0x00);
> +
> +	return 0;
No real point in returning int if you are always going to put 0 in
in it.  Should probably check the regmap_write though and output
a log message if it fails (as no other way of telling).

> +}
> +EXPORT_SYMBOL(rm3100_common_remove);
> +
> +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
> new file mode 100644
> index 000000000000..b50dc5b1b30b
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> @@ -0,0 +1,66 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
> + *
> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + *
> + * i2c slave address 0x20 + SA1 << 1 + SA0.
> + */
> +
> +#include <linux/i2c.h>
> +
> +#include "rm3100.h"
> +
> +static const struct regmap_config rm3100_regmap_config = {
> +		.reg_bits = 8,
This indenting seems an extra tab on conventional choice...
> +		.val_bits = 8,
> +
> +		.rd_table = &rm3100_readable_table,
> +		.wr_table = &rm3100_writable_table,
> +		.volatile_table = &rm3100_volatile_table,
> +
> +		.cache_type = REGCACHE_RBTREE,
> +};
> +
> +static int rm3100_probe(struct i2c_client *client)
> +{
> +	struct regmap *regmap;
> +
> +	if (!i2c_check_functionality(client->adapter,
> +		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
> +		return -EOPNOTSUPP;
> +
> +	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
> +	if (IS_ERR(regmap))
> +		return PTR_ERR(regmap);
> +
> +	return rm3100_common_probe(&client->dev, regmap, client->irq);
> +}
> +
> +static int rm3100_remove(struct i2c_client *client)
> +{
> +	return rm3100_common_remove(&client->dev);
> +}
> +
> +static const struct of_device_id rm3100_dt_match[] = {
> +	{ .compatible = "pni,rm3100-i2c", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> +
> +static struct i2c_driver rm3100_driver = {
> +	.driver = {
> +		.name = "rm3100-i2c",
> +		.of_match_table = rm3100_dt_match,
> +	},
> +	.probe_new = rm3100_probe,
> +	.remove = rm3100_remove,
> +};
> +module_i2c_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
> new file mode 100644
> index 000000000000..2c7dd9e3a1a2
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-spi.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
> + *
> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>

Probably only worth mentioning the manual in the main file.

> + */
> +
> +#include <linux/spi/spi.h>
> +
> +#include "rm3100.h"
> +
> +static const struct regmap_config rm3100_regmap_config = {
> +		.reg_bits = 8,
> +		.val_bits = 8,
> +
> +		.rd_table = &rm3100_readable_table,
> +		.wr_table = &rm3100_writable_table,
> +		.volatile_table = &rm3100_volatile_table,
> +
> +		.read_flag_mask = 0x80,
> +
> +		.cache_type = REGCACHE_RBTREE,
> +};
> +
> +static int rm3100_probe(struct spi_device *spi)
> +{
> +	struct regmap *regmap;
> +	int ret;
> +
> +	/* Actually this device supports both mode 0 and mode 3. */
> +	spi->mode = SPI_MODE_0;
> +	/* data rates cannot exceeds 1Mbits. */
> +	spi->max_speed_hz = 1000000;
> +	spi->bits_per_word = 8;
> +	ret = spi_setup(spi);
> +	if (ret)
> +		return ret;
> +
> +	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
> +	if (IS_ERR(regmap))
> +		return PTR_ERR(regmap);
> +
> +	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
> +}
> +
> +static int rm3100_remove(struct spi_device *spi)
> +{
> +	return rm3100_common_remove(&spi->dev);
> +}
> +
> +static const struct of_device_id rm3100_dt_match[] = {
> +	{ .compatible = "pni,rm3100-spi", },
> +	{ }
> +};
> +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> +
> +static struct spi_driver rm3100_driver = {
> +		.driver = {
> +				.name = "rm3100-spi",
> +				.of_match_table = rm3100_dt_match,

Indenting looks oddly deep...

> +		},
> +		.probe = rm3100_probe,
> +		.remove = rm3100_remove,
> +};
> +module_spi_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
> new file mode 100644
> index 000000000000..5e30bc0f5149
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100.h
> @@ -0,0 +1,90 @@
> +/* SPDX-License-Identifier: GPL-2.0+ */
> +/*
> + * Header file for PNI RM3100 driver
> + *
> + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> + */
> +
> +#ifndef RM3100_CORE_H
> +#define RM3100_CORE_H
> +
> +#include <linux/module.h>
> +#include <linux/regmap.h>

What in here needs module.h?  Push that down into the
individual c files.  It's repetition but it si generally
good practice to include headers at as low a level as possible.

> +
> +#define RM_REG_REV_ID		0x36
I would prefer RM3100 as the prefix.
> +
> +/* Cycle Count Registers MSBs and LSBs. */
Could probably give these slightly more human readable names?
RM3100_REG_CC_X_MSB

Actually I doubt you use both of them in the driver anyway...
(just searched, nope you don't so drop the ones you don't
 use and then the MSB, LSB definition probably not necessary).

> +#define RM_REG_CCXM		0x04
> +#define RM_REG_CCXL		0x05
> +#define RM_REG_CCYM		0x06
> +#define RM_REG_CCYL		0x07
> +#define RM_REG_CCZM		0x08
> +#define RM_REG_CCZL		0x09
> +
> +/* Single Measurement Mode register. */
> +#define RM_REG_POLL		0x00
> +#define RM_POLL_PMX		BIT(4)
> +#define RM_POLL_PMY		BIT(5)
> +#define RM_POLL_PMZ		BIT(6)
> +
> +/* Continues Measurement Mode register. */
> +#define RM_REG_CMM		0x01
> +#define RM_CMM_START		BIT(0)
> +#define RM_CMM_DRDM		BIT(2)
> +#define RM_CMM_PMX		BIT(4)
> +#define RM_CMM_PMY		BIT(5)
> +#define RM_CMM_PMZ		BIT(6)
> +
> +/* TiMe Rate Configuration register. */
> +#define RM_REG_TMRC		0x0B
> +#define RM_TMRC_OFFSET		0x92
> +
> +/* Result Status register. */
> +#define RM_REG_STATUS		0x34
> +#define RM_STATUS_DRDY		BIT(7)
> +
> +/* Measurement result registers. */
> +#define RM_REG_MX2		0x24

You only use some of these. Not sure having defines for the
others is really helpful.

> +#define RM_REG_MX1		0x25
> +#define RM_REG_MX0		0x26
> +#define RM_REG_MY2		0x27
> +#define RM_REG_MY1		0x28
> +#define RM_REG_MY0		0x29
> +#define RM_REG_MZ2		0x2a
> +#define RM_REG_MZ1		0x2b
> +#define RM_REG_MZ0		0x2c
> +
> +#define RM_REG_HSHAKE		0x35
> +
> +#define RM_W_REG_START		RM_REG_POLL
> +#define RM_W_REG_END		RM_REG_REV_ID
> +#define RM_R_REG_START		RM_REG_POLL
> +#define RM_R_REG_END		RM_REG_HSHAKE
> +#define RM_V_REG_START		RM_REG_MX2
> +#define RM_V_REG_END		RM_REG_HSHAKE
> +
> +/* Built-In Self Test reigister. */
> +#define RM_REG_BIST		0x33
> +
> +struct rm3100_data {
> +	struct device *dev;
> +	struct regmap *regmap;
> +	struct completion measuring_done;
> +	bool use_interrupt;
> +
> +	int conversion_time;
> +
> +	/* To protect consistency of every measurement and sampling

/*
 * To protect
 */
(common format to most of the kernel other than those 'crazy' :)
people in net and a few other corners.

> +	 * frequency change operations.
> +	 */
> +	struct mutex lock;
> +};
> +
> +extern const struct regmap_access_table rm3100_readable_table;
> +extern const struct regmap_access_table rm3100_writable_table;
> +extern const struct regmap_access_table rm3100_volatile_table;
> +
> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> +int rm3100_common_remove(struct device *dev);
> +
> +#endif /* RM3100_CORE_H */


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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-20 13:13 [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer Song Qiang
                   ` (2 preceding siblings ...)
  2018-09-22 10:14 ` Jonathan Cameron
@ 2018-09-23 11:01 ` Dan Carpenter
       [not found] ` <5baa1b01.1c69fb81.f0d8f.30c6@mx.google.com>
  4 siblings, 0 replies; 19+ messages in thread
From: Dan Carpenter @ 2018-09-23 11:01 UTC (permalink / raw)
  To: kbuild, Song Qiang
  Cc: kbuild-all, jic23, knaack.h, lars, pmeerw, robh+dt, mark.rutland,
	linux-iio, devicetree, linux-kernel, Song Qiang

Hi Song,

I love your patch! Perhaps something to improve:

url:    https://github.com/0day-ci/linux/commits/Song-Qiang/iio-magnetometer-Add-support-for-PNI-RM3100-9-axis-magnetometer/20180920-215124
base:   https://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio.git togreg

smatch warnings:
drivers/iio/magnetometer/rm3100-core.c:235 rm3100_set_samp_freq() warn: inconsistent returns 'mutex:&data->lock'.
  Locked on:   line 206
  Unlocked on: line 194
drivers/iio/magnetometer/rm3100-core.c:319 rm3100_trigger_handler() warn: inconsistent returns 'mutex:&data->lock'.
  Locked on:   line 310
  Unlocked on: line 319

# https://github.com/0day-ci/linux/commit/0345472c15bab336397b25a25eb76a9f8586faf0
git remote add linux-review https://github.com/0day-ci/linux
git remote update linux-review
git checkout 0345472c15bab336397b25a25eb76a9f8586faf0
vim +235 drivers/iio/magnetometer/rm3100-core.c

0345472c Song Qiang 2018-09-20  183  
0345472c Song Qiang 2018-09-20  184  static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
0345472c Song Qiang 2018-09-20  185  {
0345472c Song Qiang 2018-09-20  186  	struct regmap *regmap = data->regmap;
0345472c Song Qiang 2018-09-20  187  	int cycle_count;
0345472c Song Qiang 2018-09-20  188  	int ret;
0345472c Song Qiang 2018-09-20  189  	int i;
0345472c Song Qiang 2018-09-20  190  
0345472c Song Qiang 2018-09-20  191  	/* All cycle count registers use the same value. */
0345472c Song Qiang 2018-09-20  192  	ret = regmap_read(regmap, RM_REG_CCXL, &cycle_count);
0345472c Song Qiang 2018-09-20  193  	if (cycle_count < 0)
0345472c Song Qiang 2018-09-20  194  		return cycle_count;
0345472c Song Qiang 2018-09-20  195  
0345472c Song Qiang 2018-09-20  196  	for (i = 0; i < RM_SAMP_NUM; i++) {
0345472c Song Qiang 2018-09-20  197  		if (val == rm3100_samp_rates[i][0] &&
0345472c Song Qiang 2018-09-20  198  			val2 == rm3100_samp_rates[i][1])
0345472c Song Qiang 2018-09-20  199  			break;
0345472c Song Qiang 2018-09-20  200  	}
0345472c Song Qiang 2018-09-20  201  
0345472c Song Qiang 2018-09-20  202  	if (i != RM_SAMP_NUM) {
0345472c Song Qiang 2018-09-20  203  		mutex_lock(&data->lock);
                                                ^^^^^^^^^^^^^^^^^^^^^^^
0345472c Song Qiang 2018-09-20  204  		ret = regmap_write(regmap, RM_REG_TMRC, i + RM_TMRC_OFFSET);
0345472c Song Qiang 2018-09-20  205  		if (ret < 0)
0345472c Song Qiang 2018-09-20  206  			return ret;
                                                        ^^^^^^^^^^
goto unlock;  there are a bunch of these.

0345472c Song Qiang 2018-09-20  207  
0345472c Song Qiang 2018-09-20  208  		/* Checking if cycle count registers need changing. */
0345472c Song Qiang 2018-09-20  209  		if (val == 600 && cycle_count == 200) {
0345472c Song Qiang 2018-09-20  210  			for (i = 0; i < 3; i++) {
0345472c Song Qiang 2018-09-20  211  				regmap_write(regmap, RM_REG_CCXL + 2 * i, 100);
0345472c Song Qiang 2018-09-20  212  				if (ret < 0)
0345472c Song Qiang 2018-09-20  213  					return ret;
0345472c Song Qiang 2018-09-20  214  			}
0345472c Song Qiang 2018-09-20  215  		} else if (val != 600 && cycle_count == 100) {
0345472c Song Qiang 2018-09-20  216  			for (i = 0; i < 3; i++) {
0345472c Song Qiang 2018-09-20  217  				regmap_write(regmap, RM_REG_CCXL + 2 * i, 200);
0345472c Song Qiang 2018-09-20  218  				if (ret < 0)
0345472c Song Qiang 2018-09-20  219  					return ret;
0345472c Song Qiang 2018-09-20  220  			}
0345472c Song Qiang 2018-09-20  221  		}
0345472c Song Qiang 2018-09-20  222  		/* Writing TMRC registers requires CMM reset. */
0345472c Song Qiang 2018-09-20  223  		ret = regmap_write(regmap, RM_REG_CMM, 0);
0345472c Song Qiang 2018-09-20  224  		if (ret < 0)
0345472c Song Qiang 2018-09-20  225  			return ret;
0345472c Song Qiang 2018-09-20  226  		ret = regmap_write(regmap, RM_REG_CMM, RM_CMM_PMX |
0345472c Song Qiang 2018-09-20  227  			RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
0345472c Song Qiang 2018-09-20  228  		if (ret < 0)
0345472c Song Qiang 2018-09-20  229  			return ret;
0345472c Song Qiang 2018-09-20  230  		mutex_unlock(&data->lock);
0345472c Song Qiang 2018-09-20  231  
0345472c Song Qiang 2018-09-20  232  		data->conversion_time = rm3100_samp_rates[i][2] + 3000;
0345472c Song Qiang 2018-09-20  233  		return 0;
0345472c Song Qiang 2018-09-20  234  	}
0345472c Song Qiang 2018-09-20 @235  	return -EINVAL;
0345472c Song Qiang 2018-09-20  236  }
0345472c Song Qiang 2018-09-20  237  
0345472c Song Qiang 2018-09-20  238  static int rm3100_read_raw(struct iio_dev *indio_dev,
0345472c Song Qiang 2018-09-20  239  			   const struct iio_chan_spec *chan,
0345472c Song Qiang 2018-09-20  240  			   int *val, int *val2, long mask)
0345472c Song Qiang 2018-09-20  241  {
0345472c Song Qiang 2018-09-20  242  	struct rm3100_data *data = iio_priv(indio_dev);
0345472c Song Qiang 2018-09-20  243  	int ret;
0345472c Song Qiang 2018-09-20  244  
0345472c Song Qiang 2018-09-20  245  	switch (mask) {
0345472c Song Qiang 2018-09-20  246  	case IIO_CHAN_INFO_RAW:
0345472c Song Qiang 2018-09-20  247  		ret = iio_device_claim_direct_mode(indio_dev);
0345472c Song Qiang 2018-09-20  248  		if (ret < 0)
0345472c Song Qiang 2018-09-20  249  			return ret;
0345472c Song Qiang 2018-09-20  250  		ret = rm3100_read_mag(data, chan->scan_index, val);
0345472c Song Qiang 2018-09-20  251  		iio_device_release_direct_mode(indio_dev);
0345472c Song Qiang 2018-09-20  252  
0345472c Song Qiang 2018-09-20  253  		return ret;
0345472c Song Qiang 2018-09-20  254  	case IIO_CHAN_INFO_SAMP_FREQ:
0345472c Song Qiang 2018-09-20  255  		return ret = rm3100_get_samp_freq(data, val, val2);
0345472c Song Qiang 2018-09-20  256  	default:
0345472c Song Qiang 2018-09-20  257  		return -EINVAL;
0345472c Song Qiang 2018-09-20  258  	}
0345472c Song Qiang 2018-09-20  259  }
0345472c Song Qiang 2018-09-20  260  
0345472c Song Qiang 2018-09-20  261  static int rm3100_write_raw(struct iio_dev *indio_dev,
0345472c Song Qiang 2018-09-20  262  			    struct iio_chan_spec const *chan,
0345472c Song Qiang 2018-09-20  263  			    int val, int val2, long mask)
0345472c Song Qiang 2018-09-20  264  {
0345472c Song Qiang 2018-09-20  265  	struct rm3100_data *data = iio_priv(indio_dev);
0345472c Song Qiang 2018-09-20  266  	int ret;
0345472c Song Qiang 2018-09-20  267  
0345472c Song Qiang 2018-09-20  268  	switch (mask) {
0345472c Song Qiang 2018-09-20  269  	case IIO_CHAN_INFO_SAMP_FREQ:
0345472c Song Qiang 2018-09-20  270  		ret = rm3100_set_samp_freq(data, val, val2);
0345472c Song Qiang 2018-09-20  271  		if (ret < 0)
0345472c Song Qiang 2018-09-20  272  			return ret;
0345472c Song Qiang 2018-09-20  273  		return 0;
0345472c Song Qiang 2018-09-20  274  	default:
0345472c Song Qiang 2018-09-20  275  		return -EINVAL;
0345472c Song Qiang 2018-09-20  276  	}
0345472c Song Qiang 2018-09-20  277  
0345472c Song Qiang 2018-09-20  278  }
0345472c Song Qiang 2018-09-20  279  
0345472c Song Qiang 2018-09-20  280  static const struct iio_info rm3100_info = {
0345472c Song Qiang 2018-09-20  281  	.read_raw = rm3100_read_raw,
0345472c Song Qiang 2018-09-20  282  	.write_raw = rm3100_write_raw,
0345472c Song Qiang 2018-09-20  283  };
0345472c Song Qiang 2018-09-20  284  
0345472c Song Qiang 2018-09-20  285  static irqreturn_t rm3100_trigger_handler(int irq, void *p)
0345472c Song Qiang 2018-09-20  286  {
0345472c Song Qiang 2018-09-20  287  	struct iio_poll_func *pf = p;
0345472c Song Qiang 2018-09-20  288  	struct iio_dev *indio_dev = pf->indio_dev;
0345472c Song Qiang 2018-09-20  289  	struct rm3100_data *data = iio_priv(indio_dev);
0345472c Song Qiang 2018-09-20  290  	struct regmap *regmap = data->regmap;
0345472c Song Qiang 2018-09-20  291  	u8 *buffer;
0345472c Song Qiang 2018-09-20  292  	int ret;
0345472c Song Qiang 2018-09-20  293  	int i;
0345472c Song Qiang 2018-09-20  294  
0345472c Song Qiang 2018-09-20  295  	buffer = devm_kzalloc(data->dev, indio_dev->scan_bytes, GFP_KERNEL);
0345472c Song Qiang 2018-09-20  296  	if (!buffer)
0345472c Song Qiang 2018-09-20  297  		goto done;
0345472c Song Qiang 2018-09-20  298  
0345472c Song Qiang 2018-09-20  299  	mutex_lock(&data->lock);
0345472c Song Qiang 2018-09-20  300  	ret = rm3100_wait_measurement(data);
0345472c Song Qiang 2018-09-20  301  	if (ret < 0) {
0345472c Song Qiang 2018-09-20  302  		mutex_unlock(&data->lock);
0345472c Song Qiang 2018-09-20  303  		goto done;
0345472c Song Qiang 2018-09-20  304  	}
0345472c Song Qiang 2018-09-20  305  
0345472c Song Qiang 2018-09-20  306  	for (i = 0; i < 3; i++) {
0345472c Song Qiang 2018-09-20  307  		ret = regmap_bulk_read(regmap, RM_REG_MX2 + 3 * i,
0345472c Song Qiang 2018-09-20  308  				buffer + 4 * i, 3);
0345472c Song Qiang 2018-09-20  309  		if (ret < 0)
0345472c Song Qiang 2018-09-20  310  			return ret;
0345472c Song Qiang 2018-09-20  311  	}
0345472c Song Qiang 2018-09-20  312  	mutex_unlock(&data->lock);
0345472c Song Qiang 2018-09-20  313  
0345472c Song Qiang 2018-09-20  314  	iio_push_to_buffers_with_timestamp(indio_dev, buffer,
0345472c Song Qiang 2018-09-20  315  			iio_get_time_ns(indio_dev));
0345472c Song Qiang 2018-09-20  316  done:
0345472c Song Qiang 2018-09-20  317  	iio_trigger_notify_done(indio_dev->trig);
0345472c Song Qiang 2018-09-20  318  
0345472c Song Qiang 2018-09-20 @319  	return IRQ_HANDLED;
0345472c Song Qiang 2018-09-20  320  }
0345472c Song Qiang 2018-09-20  321  

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

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-22 10:14 ` Jonathan Cameron
@ 2018-09-23 15:17   ` Song Qiang
  2018-09-24 20:04     ` Jonathan Cameron
  2018-09-24 14:37   ` Song Qiang
  1 sibling, 1 reply; 19+ messages in thread
From: Song Qiang @ 2018-09-23 15:17 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: knaack.h, lars, pmeerw, robh+dt, mark.rutland, linux-iio,
	devicetree, linux-kernel

On Sat, Sep 22, 2018 at 11:14:09AM +0100, Jonathan Cameron wrote:
> On Thu, 20 Sep 2018 21:13:40 +0800
> Song Qiang <songqiang1304521@gmail.com> wrote:
> 

...

> > +const struct regmap_access_table rm3100_volatile_table = {
> > +		.yes_ranges = rm3100_volatile_ranges,
> > +		.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> > +};
> > +
> > +static irqreturn_t rm3100_measurement_irq_handler(int irq, void *d)
> 
> Silly question: Does the chip have two interrupt lines?  (if so they
> should be in the binding). If not, then this is the irq handler
> for everything so why have the measurement in it's name?
> 

Hi Jonathan

Ah, always some other things need to care, I didn't put enough focus on
this naming and thought it looks like ok. So I should throw these
unnecessary information in names away!

> > +{
> > +	struct rm3100_data *data = d;
> > +
> > +	complete(&data->measuring_done);
> > +
> > +	return IRQ_HANDLED;
> > +}

... 

> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/* 3sec more wait time. */
> > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> > +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> > +
> > +	/* Starting all channels' conversion. */
> > +	ret = regmap_write(regmap, RM_REG_CMM,
> > +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return devm_iio_device_register(dev, indio_dev);
> Nope.  Can't do this without having a race condition.   You need
> to ensure the userspace and in kernel interfaces are removed 'before'.
> you do that RM_REG_CMM write in remove.
> 
> One option is to use devm_add_action to add a custom unwind function
> to the automatic handling. The other is to not use devm for everything 
> after the write above and do the device_unregister manually.
> 

I've already handled some of those problems, and most of them are not a 
big deal, except this one and the locking problems, about how should I 
deal with locks properly. I'm already reading the lockdep conventions and
some articles about it.
Autobuilder are complaining about my locks, seems like a mess it is!

> > +}
> > +EXPORT_SYMBOL(rm3100_common_probe);
> > +
> > +int rm3100_common_remove(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct rm3100_data *data = iio_priv(indio_dev);
> > +	struct regmap *regmap = data->regmap;
> > +
> > +	regmap_write(regmap, RM_REG_CMM, 0x00);
> > +
> > +	return 0;
> No real point in returning int if you are always going to put 0 in
> in it.  Should probably check the regmap_write though and output
> a log message if it fails (as no other way of telling).
> 
> > +}
> > +EXPORT_SYMBOL(rm3100_common_remove);
> > +

...

> > +struct rm3100_data {
> > +	struct device *dev;
> > +	struct regmap *regmap;
> > +	struct completion measuring_done;
> > +	bool use_interrupt;
> > +
> > +	int conversion_time;
> > +
> > +	/* To protect consistency of every measurement and sampling
> 
> /*
>  * To protect
>  */
> (common format to most of the kernel other than those 'crazy' :)
> people in net and a few other corners.
> 

Actually, I've been wondering why the perl scripts didn't find this out,
and not only this one, many other problems like too many indents, 
parameters in open brackets are not aligned can be detected.
I don't know perl, but this has drawn my attention. Is there any
particular reason these problems still can not be detected? or I think
we can work some patch out! Make reviewing code like mine easier!

yours,
Song Qiang

> > +	 * frequency change operations.
> > +	 */
> > +	struct mutex lock;
> > +};
> > +
> > +extern const struct regmap_access_table rm3100_readable_table;
> > +extern const struct regmap_access_table rm3100_writable_table;
> > +extern const struct regmap_access_table rm3100_volatile_table;
> > +
> > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> > +int rm3100_common_remove(struct device *dev);
> > +
> > +#endif /* RM3100_CORE_H */
> 

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-22 10:14 ` Jonathan Cameron
  2018-09-23 15:17   ` Song Qiang
@ 2018-09-24 14:37   ` Song Qiang
  2018-09-29 12:44     ` Jonathan Cameron
  1 sibling, 1 reply; 19+ messages in thread
From: Song Qiang @ 2018-09-24 14:37 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: knaack.h, lars, pmeerw, robh+dt, mark.rutland, linux-iio,
	devicetree, linux-kernel

On Sat, Sep 22, 2018 at 11:14:09AM +0100, Jonathan Cameron wrote:
> On Thu, 20 Sep 2018 21:13:40 +0800
> Song Qiang <songqiang1304521@gmail.com> wrote:
> 
> > PNI RM3100 magnetometer is a high resolution, large signal immunity
> > magnetometer, composed of 3 single sensors and a processing chip.
> > PNI is currently not in the vendors list, so this is also adding it.
> > 
> > Following functions are available:
> >  - Single-shot measurement from
> >    /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
> >  - Triggerd buffer measurement.
> >  - Both i2c and spi interface are supported.
> >  - Both interrupt and polling measurement is supported, depands on if
> >    the 'interrupts' in DT is declared.
> > 
> > Signed-off-by: Song Qiang <songqiang1304521@gmail.com>
> Hi Song,

...

> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	/* 3sec more wait time. */
> > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> > +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> > +
> > +	/* Starting all channels' conversion. */
> > +	ret = regmap_write(regmap, RM_REG_CMM,
> > +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > +	if (ret < 0)
> > +		return ret;
> > +
> > +	return devm_iio_device_register(dev, indio_dev);
> Nope.  Can't do this without having a race condition.   You need
> to ensure the userspace and in kernel interfaces are removed 'before'.
> you do that RM_REG_CMM write in remove.
> 
> One option is to use devm_add_action to add a custom unwind function
> to the automatic handling. The other is to not use devm for everything 
> after the write above and do the device_unregister manually.
> 

Hi Jonathan,

I considered the both options you mentioned, and I was going to use the
manual way. But then something came to my mind, what if there is another
devm_* operation needs care, what if more. I checked devm_add_action in
source code, and it says that this function only adds unwinding handlers.
I guess this method was designed for this situation, and if we have
already used devm_ in our code, it's better to use devm_add_action for
cleanup, is that right?

yours,
Song Qiang

> > +}
> > +EXPORT_SYMBOL(rm3100_common_probe);
> > +
> > +int rm3100_common_remove(struct device *dev)
> > +{
> > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > +	struct rm3100_data *data = iio_priv(indio_dev);
> > +	struct regmap *regmap = data->regmap;
> > +
> > +	regmap_write(regmap, RM_REG_CMM, 0x00);
> > +
> > +	return 0;
> No real point in returning int if you are always going to put 0 in
> in it.  Should probably check the regmap_write though and output
> a log message if it fails (as no other way of telling).
> 
> > +}
> > +EXPORT_SYMBOL(rm3100_common_remove);
> > +
> > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
> > new file mode 100644
> > index 000000000000..b50dc5b1b30b
> > --- /dev/null
> > +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> > @@ -0,0 +1,66 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
> > + *
> > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > + *
> > + * User Manual available at
> > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > + *
> > + * i2c slave address 0x20 + SA1 << 1 + SA0.
> > + */
> > +
> > +#include <linux/i2c.h>
> > +
> > +#include "rm3100.h"
> > +
> > +static const struct regmap_config rm3100_regmap_config = {
> > +		.reg_bits = 8,
> This indenting seems an extra tab on conventional choice...
> > +		.val_bits = 8,
> > +
> > +		.rd_table = &rm3100_readable_table,
> > +		.wr_table = &rm3100_writable_table,
> > +		.volatile_table = &rm3100_volatile_table,
> > +
> > +		.cache_type = REGCACHE_RBTREE,
> > +};
> > +
> > +static int rm3100_probe(struct i2c_client *client)
> > +{
> > +	struct regmap *regmap;
> > +
> > +	if (!i2c_check_functionality(client->adapter,
> > +		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
> > +		return -EOPNOTSUPP;
> > +
> > +	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
> > +	if (IS_ERR(regmap))
> > +		return PTR_ERR(regmap);
> > +
> > +	return rm3100_common_probe(&client->dev, regmap, client->irq);
> > +}
> > +
> > +static int rm3100_remove(struct i2c_client *client)
> > +{
> > +	return rm3100_common_remove(&client->dev);
> > +}
> > +
> > +static const struct of_device_id rm3100_dt_match[] = {
> > +	{ .compatible = "pni,rm3100-i2c", },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > +
> > +static struct i2c_driver rm3100_driver = {
> > +	.driver = {
> > +		.name = "rm3100-i2c",
> > +		.of_match_table = rm3100_dt_match,
> > +	},
> > +	.probe_new = rm3100_probe,
> > +	.remove = rm3100_remove,
> > +};
> > +module_i2c_driver(rm3100_driver);
> > +
> > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
> > new file mode 100644
> > index 000000000000..2c7dd9e3a1a2
> > --- /dev/null
> > +++ b/drivers/iio/magnetometer/rm3100-spi.c
> > @@ -0,0 +1,72 @@
> > +// SPDX-License-Identifier: GPL-2.0+
> > +/*
> > + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
> > + *
> > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > + *
> > + * User Manual available at
> > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> 
> Probably only worth mentioning the manual in the main file.
> 
> > + */
> > +
> > +#include <linux/spi/spi.h>
> > +
> > +#include "rm3100.h"
> > +
> > +static const struct regmap_config rm3100_regmap_config = {
> > +		.reg_bits = 8,
> > +		.val_bits = 8,
> > +
> > +		.rd_table = &rm3100_readable_table,
> > +		.wr_table = &rm3100_writable_table,
> > +		.volatile_table = &rm3100_volatile_table,
> > +
> > +		.read_flag_mask = 0x80,
> > +
> > +		.cache_type = REGCACHE_RBTREE,
> > +};
> > +
> > +static int rm3100_probe(struct spi_device *spi)
> > +{
> > +	struct regmap *regmap;
> > +	int ret;
> > +
> > +	/* Actually this device supports both mode 0 and mode 3. */
> > +	spi->mode = SPI_MODE_0;
> > +	/* data rates cannot exceeds 1Mbits. */
> > +	spi->max_speed_hz = 1000000;
> > +	spi->bits_per_word = 8;
> > +	ret = spi_setup(spi);
> > +	if (ret)
> > +		return ret;
> > +
> > +	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
> > +	if (IS_ERR(regmap))
> > +		return PTR_ERR(regmap);
> > +
> > +	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
> > +}
> > +
> > +static int rm3100_remove(struct spi_device *spi)
> > +{
> > +	return rm3100_common_remove(&spi->dev);
> > +}
> > +
> > +static const struct of_device_id rm3100_dt_match[] = {
> > +	{ .compatible = "pni,rm3100-spi", },
> > +	{ }
> > +};
> > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > +
> > +static struct spi_driver rm3100_driver = {
> > +		.driver = {
> > +				.name = "rm3100-spi",
> > +				.of_match_table = rm3100_dt_match,
> 
> Indenting looks oddly deep...
> 
> > +		},
> > +		.probe = rm3100_probe,
> > +		.remove = rm3100_remove,
> > +};
> > +module_spi_driver(rm3100_driver);
> > +
> > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
> > +MODULE_LICENSE("GPL v2");
> > diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
> > new file mode 100644
> > index 000000000000..5e30bc0f5149
> > --- /dev/null
> > +++ b/drivers/iio/magnetometer/rm3100.h
> > @@ -0,0 +1,90 @@
> > +/* SPDX-License-Identifier: GPL-2.0+ */
> > +/*
> > + * Header file for PNI RM3100 driver
> > + *
> > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > + */
> > +
> > +#ifndef RM3100_CORE_H
> > +#define RM3100_CORE_H
> > +
> > +#include <linux/module.h>
> > +#include <linux/regmap.h>
> 
> What in here needs module.h?  Push that down into the
> individual c files.  It's repetition but it si generally
> good practice to include headers at as low a level as possible.
> 
> > +
> > +#define RM_REG_REV_ID		0x36
> I would prefer RM3100 as the prefix.
> > +
> > +/* Cycle Count Registers MSBs and LSBs. */
> Could probably give these slightly more human readable names?
> RM3100_REG_CC_X_MSB
> 
> Actually I doubt you use both of them in the driver anyway...
> (just searched, nope you don't so drop the ones you don't
>  use and then the MSB, LSB definition probably not necessary).
> 
> > +#define RM_REG_CCXM		0x04
> > +#define RM_REG_CCXL		0x05
> > +#define RM_REG_CCYM		0x06
> > +#define RM_REG_CCYL		0x07
> > +#define RM_REG_CCZM		0x08
> > +#define RM_REG_CCZL		0x09
> > +
> > +/* Single Measurement Mode register. */
> > +#define RM_REG_POLL		0x00
> > +#define RM_POLL_PMX		BIT(4)
> > +#define RM_POLL_PMY		BIT(5)
> > +#define RM_POLL_PMZ		BIT(6)
> > +
> > +/* Continues Measurement Mode register. */
> > +#define RM_REG_CMM		0x01
> > +#define RM_CMM_START		BIT(0)
> > +#define RM_CMM_DRDM		BIT(2)
> > +#define RM_CMM_PMX		BIT(4)
> > +#define RM_CMM_PMY		BIT(5)
> > +#define RM_CMM_PMZ		BIT(6)
> > +
> > +/* TiMe Rate Configuration register. */
> > +#define RM_REG_TMRC		0x0B
> > +#define RM_TMRC_OFFSET		0x92
> > +
> > +/* Result Status register. */
> > +#define RM_REG_STATUS		0x34
> > +#define RM_STATUS_DRDY		BIT(7)
> > +
> > +/* Measurement result registers. */
> > +#define RM_REG_MX2		0x24
> 
> You only use some of these. Not sure having defines for the
> others is really helpful.
> 
> > +#define RM_REG_MX1		0x25
> > +#define RM_REG_MX0		0x26
> > +#define RM_REG_MY2		0x27
> > +#define RM_REG_MY1		0x28
> > +#define RM_REG_MY0		0x29
> > +#define RM_REG_MZ2		0x2a
> > +#define RM_REG_MZ1		0x2b
> > +#define RM_REG_MZ0		0x2c
> > +
> > +#define RM_REG_HSHAKE		0x35
> > +
> > +#define RM_W_REG_START		RM_REG_POLL
> > +#define RM_W_REG_END		RM_REG_REV_ID
> > +#define RM_R_REG_START		RM_REG_POLL
> > +#define RM_R_REG_END		RM_REG_HSHAKE
> > +#define RM_V_REG_START		RM_REG_MX2
> > +#define RM_V_REG_END		RM_REG_HSHAKE
> > +
> > +/* Built-In Self Test reigister. */
> > +#define RM_REG_BIST		0x33
> > +
> > +struct rm3100_data {
> > +	struct device *dev;
> > +	struct regmap *regmap;
> > +	struct completion measuring_done;
> > +	bool use_interrupt;
> > +
> > +	int conversion_time;
> > +
> > +	/* To protect consistency of every measurement and sampling
> 
> /*
>  * To protect
>  */
> (common format to most of the kernel other than those 'crazy' :)
> people in net and a few other corners.
> 
> > +	 * frequency change operations.
> > +	 */
> > +	struct mutex lock;
> > +};
> > +
> > +extern const struct regmap_access_table rm3100_readable_table;
> > +extern const struct regmap_access_table rm3100_writable_table;
> > +extern const struct regmap_access_table rm3100_volatile_table;
> > +
> > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> > +int rm3100_common_remove(struct device *dev);
> > +
> > +#endif /* RM3100_CORE_H */
> 

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-23 15:17   ` Song Qiang
@ 2018-09-24 20:04     ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2018-09-24 20:04 UTC (permalink / raw)
  To: Song Qiang
  Cc: knaack.h, lars, pmeerw, robh+dt, mark.rutland, linux-iio,
	devicetree, linux-kernel

On Sun, 23 Sep 2018 23:17:22 +0800
Song Qiang <songqiang1304521@gmail.com> wrote:

> On Sat, Sep 22, 2018 at 11:14:09AM +0100, Jonathan Cameron wrote:
> > On Thu, 20 Sep 2018 21:13:40 +0800
> > Song Qiang <songqiang1304521@gmail.com> wrote:
> >   
> 
> ...
> 
> > > +const struct regmap_access_table rm3100_volatile_table = {
> > > +		.yes_ranges = rm3100_volatile_ranges,
> > > +		.n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> > > +};
> > > +
> > > +static irqreturn_t rm3100_measurement_irq_handler(int irq, void *d)  
> > 
> > Silly question: Does the chip have two interrupt lines?  (if so they
> > should be in the binding). If not, then this is the irq handler
> > for everything so why have the measurement in it's name?
> >   
> 
> Hi Jonathan
Hi Song.

> 
> Ah, always some other things need to care, I didn't put enough focus on
> this naming and thought it looks like ok. So I should throw these
> unnecessary information in names away!
> 
> > > +{
> > > +	struct rm3100_data *data = d;
> > > +
> > > +	complete(&data->measuring_done);
> > > +
> > > +	return IRQ_HANDLED;
> > > +}  
> 
> ... 
> 
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	/* 3sec more wait time. */
> > > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> > > +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> > > +
> > > +	/* Starting all channels' conversion. */
> > > +	ret = regmap_write(regmap, RM_REG_CMM,
> > > +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	return devm_iio_device_register(dev, indio_dev);  
> > Nope.  Can't do this without having a race condition.   You need
> > to ensure the userspace and in kernel interfaces are removed 'before'.
> > you do that RM_REG_CMM write in remove.
> > 
> > One option is to use devm_add_action to add a custom unwind function
> > to the automatic handling. The other is to not use devm for everything 
> > after the write above and do the device_unregister manually.
> >   
> 
> I've already handled some of those problems, and most of them are not a 
> big deal, except this one and the locking problems, about how should I 
> deal with locks properly. I'm already reading the lockdep conventions and
> some articles about it.
> Autobuilder are complaining about my locks, seems like a mess it is!

I suspect it's mostly about error paths with no unlocks in them.
At least to my eye there isn't any complex locking needed in here.

> 
> > > +}
> > > +EXPORT_SYMBOL(rm3100_common_probe);
> > > +
> > > +int rm3100_common_remove(struct device *dev)
> > > +{
> > > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > +	struct regmap *regmap = data->regmap;
> > > +
> > > +	regmap_write(regmap, RM_REG_CMM, 0x00);
> > > +
> > > +	return 0;  
> > No real point in returning int if you are always going to put 0 in
> > in it.  Should probably check the regmap_write though and output
> > a log message if it fails (as no other way of telling).
> >   
> > > +}
> > > +EXPORT_SYMBOL(rm3100_common_remove);
> > > +  
> 
> ...
> 
> > > +struct rm3100_data {
> > > +	struct device *dev;
> > > +	struct regmap *regmap;
> > > +	struct completion measuring_done;
> > > +	bool use_interrupt;
> > > +
> > > +	int conversion_time;
> > > +
> > > +	/* To protect consistency of every measurement and sampling  
> > 
> > /*
> >  * To protect
> >  */
> > (common format to most of the kernel other than those 'crazy' :)
> > people in net and a few other corners.
> >   
> 
> Actually, I've been wondering why the perl scripts didn't find this out,
> and not only this one, many other problems like too many indents, 
> parameters in open brackets are not aligned can be detected.
> I don't know perl, but this has drawn my attention. Is there any
> particular reason these problems still can not be detected? or I think
> we can work some patch out! Make reviewing code like mine easier!

Unfortunately there are enough corners of the kernel with different
formats, and legacy code that predates there being any conventions at
all, that checkpatch tends to be 'relaxed' on this stuff these days.

> 
> yours,
> Song Qiang
> 
> > > +	 * frequency change operations.
> > > +	 */
> > > +	struct mutex lock;
> > > +};
> > > +
> > > +extern const struct regmap_access_table rm3100_readable_table;
> > > +extern const struct regmap_access_table rm3100_writable_table;
> > > +extern const struct regmap_access_table rm3100_volatile_table;
> > > +
> > > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> > > +int rm3100_common_remove(struct device *dev);
> > > +
> > > +#endif /* RM3100_CORE_H */  
> >   


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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
       [not found] ` <5baa1b01.1c69fb81.f0d8f.30c6@mx.google.com>
@ 2018-09-26  0:34   ` Song Qiang
  2018-09-29 11:22     ` Jonathan Cameron
  0 siblings, 1 reply; 19+ messages in thread
From: Song Qiang @ 2018-09-26  0:34 UTC (permalink / raw)
  To: Rob Herring
  Cc: jic23, knaack.h, lars, pmeerw, mark.rutland, linux-iio,
	devicetree, linux-kernel

On Mon, Sep 24, 2018 at 03:23:52PM -0700, Rob Herring wrote:
> On Thu, Sep 20, 2018 at 09:13:40PM +0800, Song Qiang wrote:
> > PNI RM3100 magnetometer is a high resolution, large signal immunity
> > magnetometer, composed of 3 single sensors and a processing chip.
> > PNI is currently not in the vendors list, so this is also adding it.
> > 

...

> > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > index 41f0b97eb933..5bf3395fe9ae 100644
> > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > @@ -288,6 +288,7 @@ pine64	Pine64
> >  pixcir  PIXCIR MICROELECTRONICS Co., Ltd
> >  plathome	Plat'Home Co., Ltd.
> >  plda	PLDA
> > +pni     PNI
> 
> PNI doesn't stand for something?
> 

Hi Rob,

PNI should be 'PNI Sensor Corporation'. I saw that PLDA above mine and
thought I should write down its abbreviation, which apparently is wrong.

yours,
Song Qiang

> >  portwell	Portwell Inc.
> >  poslab	Poslab Technology Co., Ltd.
> >  powervr	PowerVR (deprecated, use img)
> 

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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-26  0:34   ` Song Qiang
@ 2018-09-29 11:22     ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2018-09-29 11:22 UTC (permalink / raw)
  To: Song Qiang
  Cc: Rob Herring, knaack.h, lars, pmeerw, mark.rutland, linux-iio,
	devicetree, linux-kernel

On Wed, 26 Sep 2018 08:34:02 +0800
Song Qiang <songqiang1304521@gmail.com> wrote:

> On Mon, Sep 24, 2018 at 03:23:52PM -0700, Rob Herring wrote:
> > On Thu, Sep 20, 2018 at 09:13:40PM +0800, Song Qiang wrote:  
> > > PNI RM3100 magnetometer is a high resolution, large signal immunity
> > > magnetometer, composed of 3 single sensors and a processing chip.
> > > PNI is currently not in the vendors list, so this is also adding it.
> > >   
> 
> ...
> 
> > > diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > > index 41f0b97eb933..5bf3395fe9ae 100644
> > > --- a/Documentation/devicetree/bindings/vendor-prefixes.txt
> > > +++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
> > > @@ -288,6 +288,7 @@ pine64	Pine64
> > >  pixcir  PIXCIR MICROELECTRONICS Co., Ltd
> > >  plathome	Plat'Home Co., Ltd.
> > >  plda	PLDA
> > > +pni     PNI  
> > 
> > PNI doesn't stand for something?
> >   
> 
> Hi Rob,
> 
> PNI should be 'PNI Sensor Corporation'. I saw that PLDA above mine and
> thought I should write down its abbreviation, which apparently is wrong.

To answer more directly.  If PNI itself stands for anything the company
is deliberately making it non obvious!  I had a good search to try and
find out and got nowhere...

> 
> yours,
> Song Qiang
> 
> > >  portwell	Portwell Inc.
> > >  poslab	Poslab Technology Co., Ltd.
> > >  powervr	PowerVR (deprecated, use img)  
> >   


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

* Re: [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer
  2018-09-24 14:37   ` Song Qiang
@ 2018-09-29 12:44     ` Jonathan Cameron
  0 siblings, 0 replies; 19+ messages in thread
From: Jonathan Cameron @ 2018-09-29 12:44 UTC (permalink / raw)
  To: Song Qiang
  Cc: knaack.h, lars, pmeerw, robh+dt, mark.rutland, linux-iio,
	devicetree, linux-kernel

On Mon, 24 Sep 2018 22:37:14 +0800
Song Qiang <songqiang1304521@gmail.com> wrote:

> On Sat, Sep 22, 2018 at 11:14:09AM +0100, Jonathan Cameron wrote:
> > On Thu, 20 Sep 2018 21:13:40 +0800
> > Song Qiang <songqiang1304521@gmail.com> wrote:
> >   
> > > PNI RM3100 magnetometer is a high resolution, large signal immunity
> > > magnetometer, composed of 3 single sensors and a processing chip.
> > > PNI is currently not in the vendors list, so this is also adding it.
> > > 
> > > Following functions are available:
> > >  - Single-shot measurement from
> > >    /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
> > >  - Triggerd buffer measurement.
> > >  - Both i2c and spi interface are supported.
> > >  - Both interrupt and polling measurement is supported, depands on if
> > >    the 'interrupts' in DT is declared.
> > > 
> > > Signed-off-by: Song Qiang <songqiang1304521@gmail.com>  
> > Hi Song,  
> 
> ...
> 
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	/* 3sec more wait time. */
> > > +	ret = regmap_read(data->regmap, RM_REG_TMRC, &tmp);
> > > +	data->conversion_time = rm3100_samp_rates[tmp-RM_TMRC_OFFSET][2] + 3000;
> > > +
> > > +	/* Starting all channels' conversion. */
> > > +	ret = regmap_write(regmap, RM_REG_CMM,
> > > +		RM_CMM_PMX | RM_CMM_PMY | RM_CMM_PMZ | RM_CMM_START);
> > > +	if (ret < 0)
> > > +		return ret;
> > > +
> > > +	return devm_iio_device_register(dev, indio_dev);  
> > Nope.  Can't do this without having a race condition.   You need
> > to ensure the userspace and in kernel interfaces are removed 'before'.
> > you do that RM_REG_CMM write in remove.
> > 
> > One option is to use devm_add_action to add a custom unwind function
> > to the automatic handling. The other is to not use devm for everything 
> > after the write above and do the device_unregister manually.
> >   
> 
> Hi Jonathan,
> 
> I considered the both options you mentioned, and I was going to use the
> manual way. But then something came to my mind, what if there is another
> devm_* operation needs care, what if more. I checked devm_add_action in
> source code, and it says that this function only adds unwinding handlers.
> I guess this method was designed for this situation, and if we have
> already used devm_ in our code, it's better to use devm_add_action for
> cleanup, is that right?

It can provide a very clean solution.  Be careful though. You do need
to make sure to handle a failure of this function itself.

Jonathan

> 
> yours,
> Song Qiang
> 
> > > +}
> > > +EXPORT_SYMBOL(rm3100_common_probe);
> > > +
> > > +int rm3100_common_remove(struct device *dev)
> > > +{
> > > +	struct iio_dev *indio_dev = dev_get_drvdata(dev);
> > > +	struct rm3100_data *data = iio_priv(indio_dev);
> > > +	struct regmap *regmap = data->regmap;
> > > +
> > > +	regmap_write(regmap, RM_REG_CMM, 0x00);
> > > +
> > > +	return 0;  
> > No real point in returning int if you are always going to put 0 in
> > in it.  Should probably check the regmap_write though and output
> > a log message if it fails (as no other way of telling).
> >   
> > > +}
> > > +EXPORT_SYMBOL(rm3100_common_remove);
> > > +
> > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > > +MODULE_LICENSE("GPL v2");
> > > diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
> > > new file mode 100644
> > > index 000000000000..b50dc5b1b30b
> > > --- /dev/null
> > > +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> > > @@ -0,0 +1,66 @@
> > > +// SPDX-License-Identifier: GPL-2.0+
> > > +/*
> > > + * Support for PNI RM3100 9-axis geomagnetic sensor a i2c bus.
> > > + *
> > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > + *
> > > + * User Manual available at
> > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> > > + *
> > > + * i2c slave address 0x20 + SA1 << 1 + SA0.
> > > + */
> > > +
> > > +#include <linux/i2c.h>
> > > +
> > > +#include "rm3100.h"
> > > +
> > > +static const struct regmap_config rm3100_regmap_config = {
> > > +		.reg_bits = 8,  
> > This indenting seems an extra tab on conventional choice...  
> > > +		.val_bits = 8,
> > > +
> > > +		.rd_table = &rm3100_readable_table,
> > > +		.wr_table = &rm3100_writable_table,
> > > +		.volatile_table = &rm3100_volatile_table,
> > > +
> > > +		.cache_type = REGCACHE_RBTREE,
> > > +};
> > > +
> > > +static int rm3100_probe(struct i2c_client *client)
> > > +{
> > > +	struct regmap *regmap;
> > > +
> > > +	if (!i2c_check_functionality(client->adapter,
> > > +		I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_BYTE_DATA))
> > > +		return -EOPNOTSUPP;
> > > +
> > > +	regmap =  devm_regmap_init_i2c(client, &rm3100_regmap_config);
> > > +	if (IS_ERR(regmap))
> > > +		return PTR_ERR(regmap);
> > > +
> > > +	return rm3100_common_probe(&client->dev, regmap, client->irq);
> > > +}
> > > +
> > > +static int rm3100_remove(struct i2c_client *client)
> > > +{
> > > +	return rm3100_common_remove(&client->dev);
> > > +}
> > > +
> > > +static const struct of_device_id rm3100_dt_match[] = {
> > > +	{ .compatible = "pni,rm3100-i2c", },
> > > +	{ }
> > > +};
> > > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > > +
> > > +static struct i2c_driver rm3100_driver = {
> > > +	.driver = {
> > > +		.name = "rm3100-i2c",
> > > +		.of_match_table = rm3100_dt_match,
> > > +	},
> > > +	.probe_new = rm3100_probe,
> > > +	.remove = rm3100_remove,
> > > +};
> > > +module_i2c_driver(rm3100_driver);
> > > +
> > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer i2c driver");
> > > +MODULE_LICENSE("GPL v2");
> > > diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
> > > new file mode 100644
> > > index 000000000000..2c7dd9e3a1a2
> > > --- /dev/null
> > > +++ b/drivers/iio/magnetometer/rm3100-spi.c
> > > @@ -0,0 +1,72 @@
> > > +// SPDX-License-Identifier: GPL-2.0+
> > > +/*
> > > + * Support for PNI RM3100 9-axis geomagnetic sensor a spi bus.
> > > + *
> > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > + *
> > > + * User Manual available at
> > > + * <https://www.pnicorp.com/download/rm3100-user-manual/>  
> > 
> > Probably only worth mentioning the manual in the main file.
> >   
> > > + */
> > > +
> > > +#include <linux/spi/spi.h>
> > > +
> > > +#include "rm3100.h"
> > > +
> > > +static const struct regmap_config rm3100_regmap_config = {
> > > +		.reg_bits = 8,
> > > +		.val_bits = 8,
> > > +
> > > +		.rd_table = &rm3100_readable_table,
> > > +		.wr_table = &rm3100_writable_table,
> > > +		.volatile_table = &rm3100_volatile_table,
> > > +
> > > +		.read_flag_mask = 0x80,
> > > +
> > > +		.cache_type = REGCACHE_RBTREE,
> > > +};
> > > +
> > > +static int rm3100_probe(struct spi_device *spi)
> > > +{
> > > +	struct regmap *regmap;
> > > +	int ret;
> > > +
> > > +	/* Actually this device supports both mode 0 and mode 3. */
> > > +	spi->mode = SPI_MODE_0;
> > > +	/* data rates cannot exceeds 1Mbits. */
> > > +	spi->max_speed_hz = 1000000;
> > > +	spi->bits_per_word = 8;
> > > +	ret = spi_setup(spi);
> > > +	if (ret)
> > > +		return ret;
> > > +
> > > +	regmap =  devm_regmap_init_spi(spi, &rm3100_regmap_config);
> > > +	if (IS_ERR(regmap))
> > > +		return PTR_ERR(regmap);
> > > +
> > > +	return rm3100_common_probe(&spi->dev, regmap, spi->irq);
> > > +}
> > > +
> > > +static int rm3100_remove(struct spi_device *spi)
> > > +{
> > > +	return rm3100_common_remove(&spi->dev);
> > > +}
> > > +
> > > +static const struct of_device_id rm3100_dt_match[] = {
> > > +	{ .compatible = "pni,rm3100-spi", },
> > > +	{ }
> > > +};
> > > +MODULE_DEVICE_TABLE(of, rm3100_dt_match);
> > > +
> > > +static struct spi_driver rm3100_driver = {
> > > +		.driver = {
> > > +				.name = "rm3100-spi",
> > > +				.of_match_table = rm3100_dt_match,  
> > 
> > Indenting looks oddly deep...
> >   
> > > +		},
> > > +		.probe = rm3100_probe,
> > > +		.remove = rm3100_remove,
> > > +};
> > > +module_spi_driver(rm3100_driver);
> > > +
> > > +MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
> > > +MODULE_DESCRIPTION("PNI RM3100 9-axis magnetometer spi driver");
> > > +MODULE_LICENSE("GPL v2");
> > > diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
> > > new file mode 100644
> > > index 000000000000..5e30bc0f5149
> > > --- /dev/null
> > > +++ b/drivers/iio/magnetometer/rm3100.h
> > > @@ -0,0 +1,90 @@
> > > +/* SPDX-License-Identifier: GPL-2.0+ */
> > > +/*
> > > + * Header file for PNI RM3100 driver
> > > + *
> > > + * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
> > > + */
> > > +
> > > +#ifndef RM3100_CORE_H
> > > +#define RM3100_CORE_H
> > > +
> > > +#include <linux/module.h>
> > > +#include <linux/regmap.h>  
> > 
> > What in here needs module.h?  Push that down into the
> > individual c files.  It's repetition but it si generally
> > good practice to include headers at as low a level as possible.
> >   
> > > +
> > > +#define RM_REG_REV_ID		0x36  
> > I would prefer RM3100 as the prefix.  
> > > +
> > > +/* Cycle Count Registers MSBs and LSBs. */  
> > Could probably give these slightly more human readable names?
> > RM3100_REG_CC_X_MSB
> > 
> > Actually I doubt you use both of them in the driver anyway...
> > (just searched, nope you don't so drop the ones you don't
> >  use and then the MSB, LSB definition probably not necessary).
> >   
> > > +#define RM_REG_CCXM		0x04
> > > +#define RM_REG_CCXL		0x05
> > > +#define RM_REG_CCYM		0x06
> > > +#define RM_REG_CCYL		0x07
> > > +#define RM_REG_CCZM		0x08
> > > +#define RM_REG_CCZL		0x09
> > > +
> > > +/* Single Measurement Mode register. */
> > > +#define RM_REG_POLL		0x00
> > > +#define RM_POLL_PMX		BIT(4)
> > > +#define RM_POLL_PMY		BIT(5)
> > > +#define RM_POLL_PMZ		BIT(6)
> > > +
> > > +/* Continues Measurement Mode register. */
> > > +#define RM_REG_CMM		0x01
> > > +#define RM_CMM_START		BIT(0)
> > > +#define RM_CMM_DRDM		BIT(2)
> > > +#define RM_CMM_PMX		BIT(4)
> > > +#define RM_CMM_PMY		BIT(5)
> > > +#define RM_CMM_PMZ		BIT(6)
> > > +
> > > +/* TiMe Rate Configuration register. */
> > > +#define RM_REG_TMRC		0x0B
> > > +#define RM_TMRC_OFFSET		0x92
> > > +
> > > +/* Result Status register. */
> > > +#define RM_REG_STATUS		0x34
> > > +#define RM_STATUS_DRDY		BIT(7)
> > > +
> > > +/* Measurement result registers. */
> > > +#define RM_REG_MX2		0x24  
> > 
> > You only use some of these. Not sure having defines for the
> > others is really helpful.
> >   
> > > +#define RM_REG_MX1		0x25
> > > +#define RM_REG_MX0		0x26
> > > +#define RM_REG_MY2		0x27
> > > +#define RM_REG_MY1		0x28
> > > +#define RM_REG_MY0		0x29
> > > +#define RM_REG_MZ2		0x2a
> > > +#define RM_REG_MZ1		0x2b
> > > +#define RM_REG_MZ0		0x2c
> > > +
> > > +#define RM_REG_HSHAKE		0x35
> > > +
> > > +#define RM_W_REG_START		RM_REG_POLL
> > > +#define RM_W_REG_END		RM_REG_REV_ID
> > > +#define RM_R_REG_START		RM_REG_POLL
> > > +#define RM_R_REG_END		RM_REG_HSHAKE
> > > +#define RM_V_REG_START		RM_REG_MX2
> > > +#define RM_V_REG_END		RM_REG_HSHAKE
> > > +
> > > +/* Built-In Self Test reigister. */
> > > +#define RM_REG_BIST		0x33
> > > +
> > > +struct rm3100_data {
> > > +	struct device *dev;
> > > +	struct regmap *regmap;
> > > +	struct completion measuring_done;
> > > +	bool use_interrupt;
> > > +
> > > +	int conversion_time;
> > > +
> > > +	/* To protect consistency of every measurement and sampling  
> > 
> > /*
> >  * To protect
> >  */
> > (common format to most of the kernel other than those 'crazy' :)
> > people in net and a few other corners.
> >   
> > > +	 * frequency change operations.
> > > +	 */
> > > +	struct mutex lock;
> > > +};
> > > +
> > > +extern const struct regmap_access_table rm3100_readable_table;
> > > +extern const struct regmap_access_table rm3100_writable_table;
> > > +extern const struct regmap_access_table rm3100_volatile_table;
> > > +
> > > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
> > > +int rm3100_common_remove(struct device *dev);
> > > +
> > > +#endif /* RM3100_CORE_H */  
> >   


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

end of thread, other threads:[~2018-09-29 12:45 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-09-20 13:13 [PATCH] iio: magnetometer: Add support for PNI RM3100 9-axis magnetometer Song Qiang
2018-09-20 13:46 ` Peter Meerwald-Stadler
2018-09-20 18:05   ` Song Qiang
2018-09-22  9:42     ` Jonathan Cameron
2018-09-22 10:08       ` Jonathan Cameron
2018-09-21  5:07   ` Phil Reid
2018-09-21 11:29     ` Song Qiang
2018-09-21 12:20       ` Jonathan Cameron
2018-09-22  9:18         ` Song Qiang
2018-09-21  2:05 ` Phil Reid
2018-09-21  9:13   ` Song Qiang
2018-09-22 10:14 ` Jonathan Cameron
2018-09-23 15:17   ` Song Qiang
2018-09-24 20:04     ` Jonathan Cameron
2018-09-24 14:37   ` Song Qiang
2018-09-29 12:44     ` Jonathan Cameron
2018-09-23 11:01 ` Dan Carpenter
     [not found] ` <5baa1b01.1c69fb81.f0d8f.30c6@mx.google.com>
2018-09-26  0:34   ` Song Qiang
2018-09-29 11:22     ` Jonathan Cameron

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).