2018-09-25 03:20:06

by Song Qiang

[permalink] [raw]
Subject: [PATCH 1/2] iio: magnetometer: Add DT support for PNI RM3100

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

Signed-off-by: Song Qiang <[email protected]>
---
Changes in v2:
- Remove bus specific part in compatible string.

.../bindings/iio/magnetometer/pni,rm3100.txt | 20 +++++++++++++++++++
.../devicetree/bindings/vendor-prefixes.txt | 1 +
2 files changed, 21 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt

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..4677690fc5d0
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
@@ -0,0 +1,20 @@
+* PNI RM3100 3-axis magnetometer sensor
+
+Required properties:
+
+- compatible : should be "pni,rm3100"
+- reg : the I2C address or SPI chip select number of the sensor.
+
+Optional properties:
+
+- interrupts: data ready (DRDY) from the chip.
+ The interrupts can be triggered on rising edges.
+
+Example:
+
+rm3100: rm3100@20 {
+ compatible = "pni,rm3100";
+ reg = <0x20>;
+ 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)
--
2.17.1



2018-09-25 03:18:31

by Song Qiang

[permalink] [raw]
Subject: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

PNI RM3100 is a high resolution, large signal immunity magnetometer,
composed of 3 single sensors and a processing chip with MagI2C Interface.

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 <[email protected]>
---
Changes in v2:
- Add scale channel.
- Add EXPORT_SYMBOL_GPL() to export regmap confuguration
structures.
- Add sampling frequency available attribute.
- Clean up headers and License declarations.
- Change axis number to 3.
- Remove bus specific part in compatible string.
- Remove le32_to_cpu().
- Check cycle count registers at *_probe().
- Format comments.
- Spell check.
- Change prefix from RM_* to RM3100_*.
- Check all error return paths.
- Add devm_add_action() to avoid race condition when remove.

MAINTAINERS | 7 +
drivers/iio/magnetometer/Kconfig | 29 ++
drivers/iio/magnetometer/Makefile | 4 +
drivers/iio/magnetometer/rm3100-core.c | 470 +++++++++++++++++++++++++
drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
drivers/iio/magnetometer/rm3100-spi.c | 64 ++++
drivers/iio/magnetometer/rm3100.h | 73 ++++
7 files changed, 705 insertions(+)
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/MAINTAINERS b/MAINTAINERS
index 967ce8cdd1cc..14eeeb072403 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
S: Maintained
F: drivers/pnp/

+PNI RM3100 IIO DRIVER
+M: Song Qiang <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/iio/magnetometer/rm3100*
+F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
+
POSIX CLOCKS and TIMERS
M: Thomas Gleixner <[email protected]>
L: [email protected]
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..5d28b53b7a04
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-core.c
@@ -0,0 +1,470 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PNI RM3100 3-axis geomagnetic sensor driver core.
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ *
+ * User Manual available at
+ * <https://www.pnicorp.com/download/rm3100-user-manual/>
+ *
+ * TODO: event generaton, pm.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.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(RM3100_W_REG_START, RM3100_W_REG_END),
+};
+
+const struct regmap_access_table rm3100_readable_table = {
+ .yes_ranges = rm3100_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_readable_table);
+
+static const struct regmap_range rm3100_writable_ranges[] = {
+ regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
+};
+
+const struct regmap_access_table rm3100_writable_table = {
+ .yes_ranges = rm3100_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_writable_table);
+
+static const struct regmap_range rm3100_volatile_ranges[] = {
+ regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
+};
+
+const struct regmap_access_table rm3100_volatile_table = {
+ .yes_ranges = rm3100_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_volatile_table);
+
+static irqreturn_t rm3100_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;
+ int tries = 20;
+ int ret;
+
+ /*
+ * A read cycle of 400kbits i2c; bus is about 20us, plus the time
+ * used for scheduling, a read cycle of fast mode of this device
+ * can reach 1.7ms, it may be possible for data to arrive just
+ * after we check the RM3100_REG_STATUS. In this case, irq_handler is
+ * called before measuring_done is reinitialized, it will wait
+ * forever for 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, RM3100_REG_STATUS, &val);
+ if (ret < 0)
+ return ret;
+
+ if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
+ if (data->use_interrupt) {
+ ret = wait_for_completion_timeout(&data->measuring_done,
+ msecs_to_jiffies(data->conversion_time));
+ if (ret < 0)
+ return -ETIMEDOUT;
+ } else {
+ do {
+ usleep_range(1000, 5000);
+
+ ret = regmap_read(regmap, RM3100_REG_STATUS,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & RM3100_STATUS_DRDY)
+ break;
+ } 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, RM3100_REG_MX2 + 3 * idx, buffer, 3);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+
+ *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
+ 23);
+
+ return IIO_VAL_INT;
+}
+
+#define RM3100_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_SCALE) | \
+ 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[] = {
+ RM3100_CHANNEL(X, 0),
+ RM3100_CHANNEL(Y, 1),
+ RM3100_CHANNEL(Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+ "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
+);
+
+static struct attribute *rm3100_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group rm3100_attribute_group = {
+ .attrs = rm3100_attributes,
+};
+
+#define RM3100_SAMP_NUM 14
+
+/*
+ * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
+ * Time between reading: rm3100_sam_rates[][2]ms.
+ * The first one is actually 1.7ms.
+ */
+static const int rm3100_samp_rates[RM3100_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;
+
+ mutex_lock(&data->lock);
+ ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+ *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
+ *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
+{
+ int ret;
+ u8 i;
+
+ for (i = 0; i < 3; i++) {
+ ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, 100);
+ if (ret < 0)
+ return ret;
+ }
+ if (val == 50)
+ data->cycle_count_index = 0;
+ else if (val == 100)
+ data->cycle_count_index = 1;
+ else
+ data->cycle_count_index = 2;
+
+ return 0;
+}
+
+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;
+
+ mutex_lock(&data->lock);
+ /* All cycle count registers use the same value. */
+ ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
+ if (ret < 0)
+ goto unlock_return;
+
+ for (i = 0; i < RM3100_SAMP_NUM; i++) {
+ if (val == rm3100_samp_rates[i][0] &&
+ val2 == rm3100_samp_rates[i][1])
+ break;
+ }
+ if (i == RM3100_SAMP_NUM) {
+ ret = -EINVAL;
+ goto unlock_return;
+ }
+
+ ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
+ if (ret < 0)
+ goto unlock_return;
+
+ /* Checking if cycle count registers need changing. */
+ if (val == 600 && cycle_count == 200) {
+ ret = rm3100_set_cycle_count(data, 100);
+ if (ret < 0)
+ goto unlock_return;
+ } else if (val != 600 && cycle_count == 100) {
+ ret = rm3100_set_cycle_count(data, 200);
+ if (ret < 0)
+ goto unlock_return;
+ }
+
+ /* Writing TMRC registers requires CMM reset. */
+ ret = regmap_write(regmap, RM3100_REG_CMM, 0);
+ if (ret < 0)
+ goto unlock_return;
+ ret = regmap_write(regmap, RM3100_REG_CMM,
+ RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+
+ data->conversion_time = rm3100_samp_rates[i][2] + 3000;
+ return 0;
+
+unlock_return:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+/*
+ * Scale of this sensor depends on cycle count value, these three values
+ * are corresponding to cycle count value 50, 100, 200.
+ * scale = output / gain * 10e4.
+ */
+const static int rm3100_scale[] = {500, 263, 133};
+
+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) {
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ }
+ ret = rm3100_read_mag(data, chan->scan_index, val);
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = rm3100_scale[data->cycle_count_index];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return 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);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return rm3100_set_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+
+}
+
+static const struct iio_info rm3100_info = {
+ .attrs = &rm3100_attribute_group,
+ .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[9];
+ int ret;
+ int i;
+
+ mutex_lock(&data->lock);
+ ret = rm3100_wait_measurement(data);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ goto done;
+ }
+
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, sizeof(buffer));
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+
+ /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
+ for (i = 0; i < 3; i++)
+ memcpy(data->buffer + i * 4, buffer + i * 3, 3);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ iio_get_time_ns(indio_dev));
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void rm3100_remove(void *dh)
+{
+ struct rm3100_data *data = (struct rm3100_data *)dh;
+ int ret;
+
+ ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
+ if (ret < 0)
+ dev_err(data->dev, "failed to stop device.\n");
+}
+
+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);
+ data->dev = dev;
+ data->regmap = regmap;
+ data->buffer = devm_kzalloc(dev, RM3100_SCAN_BYTES, GFP_KERNEL);
+ if (!data->buffer)
+ return -ENOMEM;
+
+ 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_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 = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ rm3100_trigger_handler, NULL);
+ if (ret < 0)
+ return ret;
+
+ /* 3sec more wait time. */
+ ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
+ if (ret < 0)
+ return ret;
+ data->conversion_time =
+ rm3100_samp_rates[tmp-RM3100_TMRC_OFFSET][2] + 3000;
+
+ /* Cycle count values may not be what we want. */
+ ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
+ if (ret < 0)
+ return ret;
+ if ((tmp - RM3100_TMRC_OFFSET) == 0)
+ rm3100_set_cycle_count(data, 100);
+ else
+ rm3100_set_cycle_count(data, 200);
+
+ /* Starting all channels' conversion. */
+ ret = regmap_write(regmap, RM3100_REG_CMM,
+ RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return devm_add_action(dev, rm3100_remove, data);
+}
+EXPORT_SYMBOL_GPL(rm3100_common_probe);
+
+MODULE_AUTHOR("Song Qiang <[email protected]>");
+MODULE_DESCRIPTION("PNI RM3100 3-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..e8b84fac771b
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-i2c.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for PNI RM3100 3-axis geomagnetic sensor a i2c bus.
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ *
+ * i2c slave address 0x20 + SA1 << 1 + SA0.
+ */
+
+#include <linux/module.h>
+#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 const struct of_device_id rm3100_dt_match[] = {
+ { .compatible = "pni,rm3100", },
+ { }
+};
+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,
+};
+module_i2c_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <[email protected]>");
+MODULE_DESCRIPTION("PNI RM3100 3-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..1a7a69fb9872
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-spi.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for PNI RM3100 3-axis geomagnetic sensor a spi bus.
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ */
+
+#include <linux/module.h>
+#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
+ { .compatible = "pni,rm3100", },
+ { }
+};
+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,
+};
+module_spi_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <[email protected]>");
+MODULE_DESCRIPTION("PNI RM3100 3-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..673647574add
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Header file for PNI RM3100 driver
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ */
+
+#ifndef RM3100_CORE_H
+#define RM3100_CORE_H
+
+#include <linux/regmap.h>
+
+#define RM3100_REG_REV_ID 0x36
+
+/* Cycle Count Registers. */
+#define RM3100_REG_CC_X 0x05
+#define RM3100_REG_CC_Y 0x07
+#define RM3100_REG_CC_Z 0x09
+
+/* Continues Measurement Mode register. */
+#define RM3100_REG_CMM 0x01
+#define RM3100_CMM_START BIT(0)
+#define RM3100_CMM_X BIT(4)
+#define RM3100_CMM_Y BIT(5)
+#define RM3100_CMM_Z BIT(6)
+
+/* TiMe Rate Configuration register. */
+#define RM3100_REG_TMRC 0x0B
+#define RM3100_TMRC_OFFSET 0x92
+
+/* Result Status register. */
+#define RM3100_REG_STATUS 0x34
+#define RM3100_STATUS_DRDY BIT(7)
+
+/* Measurement result registers. */
+#define RM3100_REG_MX2 0x24
+#define RM3100_REG_MY2 0x27
+#define RM3100_REG_MZ2 0x2a
+
+#define RM3100_W_REG_START RM3100_REG_CMM
+#define RM3100_W_REG_END RM3100_REG_REV_ID
+#define RM3100_R_REG_START RM3100_REG_CMM
+#define RM3100_R_REG_END RM3100_REG_STATUS
+#define RM3100_V_REG_START RM3100_REG_MX2
+#define RM3100_V_REG_END RM3100_REG_STATUS
+
+#define RM3100_SCAN_BYTES 24
+
+struct rm3100_data {
+ struct device *dev;
+ struct regmap *regmap;
+ struct completion measuring_done;
+ bool use_interrupt;
+
+ int conversion_time;
+ int cycle_count_index;
+
+ u8 *buffer;
+
+ /*
+ * 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);
+
+#endif /* RM3100_CORE_H */
--
2.17.1


2018-09-25 13:06:39

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 1/2] iio: magnetometer: Add DT support for PNI RM3100

On Tue, 25 Sep 2018 11:17:23 +0800
Song Qiang <[email protected]> wrote:

> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> composed of 3 single sensors and a processing chip with MagI2C Interface.
> PNI is not in the vendors' list, so this patch is also adding it.
>
> Signed-off-by: Song Qiang <[email protected]>
Hi Song,

Please clearly mark the title of the patch with V2.
[PATCH 1/2 V2] ...

(or I'm also happy with a cover letter that does the same though other
maintainers ask for every patch to be marked v2).

This makes it easy to quickly short the versions and check we are
looking at the latest.
> ---
> Changes in v2:
> - Remove bus specific part in compatible string.
>
> .../bindings/iio/magnetometer/pni,rm3100.txt | 20 +++++++++++++++++++
> .../devicetree/bindings/vendor-prefixes.txt | 1 +
> 2 files changed, 21 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
>
> 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..4677690fc5d0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> @@ -0,0 +1,20 @@
> +* PNI RM3100 3-axis magnetometer sensor
> +
> +Required properties:
> +
> +- compatible : should be "pni,rm3100"
> +- reg : the I2C address or SPI chip select number of the sensor.
> +
> +Optional properties:
> +
> +- interrupts: data ready (DRDY) from the chip.
> + The interrupts can be triggered on rising edges.
> +
> +Example:
> +
> +rm3100: rm3100@20 {
> + compatible = "pni,rm3100";
> + reg = <0x20>;
> + 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

A quick scan around their website suggests they are actually
"PNI Sensor Corporation"
I'm not sure how fussy we are on naming in here but probable best to be as specific
as possible.

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



2018-09-25 13:31:39

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On Tue, 25 Sep 2018 11:17:24 +0800
Song Qiang <[email protected]> wrote:

> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> composed of 3 single sensors and a processing chip with MagI2C Interface.
>
> 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 <[email protected]>
Various minor comments inline. Definitely heading in the right direction.

Good work.

Thanks,

Jonathan

> ---
> Changes in v2:
> - Add scale channel.
> - Add EXPORT_SYMBOL_GPL() to export regmap confuguration
> structures.
> - Add sampling frequency available attribute.
> - Clean up headers and License declarations.
> - Change axis number to 3.
> - Remove bus specific part in compatible string.
> - Remove le32_to_cpu().
> - Check cycle count registers at *_probe().
> - Format comments.
> - Spell check.
> - Change prefix from RM_* to RM3100_*.
> - Check all error return paths.
> - Add devm_add_action() to avoid race condition when remove.
>
> MAINTAINERS | 7 +
> drivers/iio/magnetometer/Kconfig | 29 ++
> drivers/iio/magnetometer/Makefile | 4 +
> drivers/iio/magnetometer/rm3100-core.c | 470 +++++++++++++++++++++++++
> drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
> drivers/iio/magnetometer/rm3100-spi.c | 64 ++++
> drivers/iio/magnetometer/rm3100.h | 73 ++++
> 7 files changed, 705 insertions(+)
> 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/MAINTAINERS b/MAINTAINERS
> index 967ce8cdd1cc..14eeeb072403 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
> S: Maintained
> F: drivers/pnp/
>
> +PNI RM3100 IIO DRIVER
> +M: Song Qiang <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/iio/magnetometer/rm3100*
> +F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> +
> POSIX CLOCKS and TIMERS
> M: Thomas Gleixner <[email protected]>
> L: [email protected]
> 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.

Still doesn't have 9 axes ;)

> +
> + 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..5d28b53b7a04
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-core.c
> @@ -0,0 +1,470 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PNI RM3100 3-axis geomagnetic sensor driver core.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + *
> + * TODO: event generaton, pm.
> + */
> +
> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>

If there is no other reason for a particular order of header includes
alphabetical is always preferred.

> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.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(RM3100_W_REG_START, RM3100_W_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_readable_table = {
> + .yes_ranges = rm3100_readable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_readable_table);
> +
> +static const struct regmap_range rm3100_writable_ranges[] = {
> + regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_writable_table = {
> + .yes_ranges = rm3100_writable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_writable_table);
> +
> +static const struct regmap_range rm3100_volatile_ranges[] = {
> + regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_volatile_table = {
> + .yes_ranges = rm3100_volatile_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_volatile_table);
> +
> +static irqreturn_t rm3100_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;
> + int tries = 20;
> + int ret;
> +
> + /*
> + * A read cycle of 400kbits i2c; bus is about 20us, plus the time
> + * used for scheduling, a read cycle of fast mode of this device
> + * can reach 1.7ms, it may be possible for data to arrive just
> + * after we check the RM3100_REG_STATUS. In this case, irq_handler is
> + * called before measuring_done is reinitialized, it will wait
> + * forever for 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, RM3100_REG_STATUS, &val);
> + if (ret < 0)
> + return ret;
> +
> + if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
> + if (data->use_interrupt) {
> + ret = wait_for_completion_timeout(&data->measuring_done,
> + msecs_to_jiffies(data->conversion_time));
> + if (ret < 0)
> + return -ETIMEDOUT;
> + } else {
> + do {
> + usleep_range(1000, 5000);
> +
> + ret = regmap_read(regmap, RM3100_REG_STATUS,
> + &val);
> + if (ret < 0)
> + return ret;
> +
> + if (val & RM3100_STATUS_DRDY)
> + break;
> + } 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, RM3100_REG_MX2 + 3 * idx, buffer, 3);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> +
> + *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
> + 23);
> +
> + return IIO_VAL_INT;
> +}
> +
> +#define RM3100_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_SCALE) | \
> + 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[] = {
> + RM3100_CHANNEL(X, 0),
> + RM3100_CHANNEL(Y, 1),
> + RM3100_CHANNEL(Z, 2),
> + IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};

This is really three separate enables, so I'd put it long hand
as BIT(0) | BIT(1) | BIT(2) to make that apparent in the code.

> +
> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
> + "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
> +);
> +
> +static struct attribute *rm3100_attributes[] = {
> + &iio_const_attr_sampling_frequency_available.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group rm3100_attribute_group = {
> + .attrs = rm3100_attributes,
> +};
> +
> +#define RM3100_SAMP_NUM 14
> +
> +/*
> + * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> + * Time between reading: rm3100_sam_rates[][2]ms.
> + * The first one is actually 1.7ms.
> + */
> +static const int rm3100_samp_rates[RM3100_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;
> +
> + mutex_lock(&data->lock);
> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
> + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
> +{
> + int ret;
> + u8 i;
> +
> + for (i = 0; i < 3; i++) {
> + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, 100);

Does this write it to the same value irrespective of the value of val?
Seems odd.

> + if (ret < 0)
> + return ret;
> + }
> + if (val == 50)

Switch would be nicer. Also why have all other values call back to
2 rather than generating an error?

> + data->cycle_count_index = 0;
> + else if (val == 100)
> + data->cycle_count_index = 1;
> + else
> + data->cycle_count_index = 2;
> +
> + return 0;
> +}
> +
> +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;
> +
> + mutex_lock(&data->lock);

Why are you taking this lock? It's not well documented what it protects
so it's not clear in this path why it is taken.
(hint add a comment to the definition of the lock to make it clear!)
- note I'm not saying it shouldn't be taken here, just that there is no
comment to justify it..

> + /* All cycle count registers use the same value. */
> + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
> + if (ret < 0)
> + goto unlock_return;
> +
> + for (i = 0; i < RM3100_SAMP_NUM; i++) {
> + if (val == rm3100_samp_rates[i][0] &&
> + val2 == rm3100_samp_rates[i][1])
> + break;
> + }
> + if (i == RM3100_SAMP_NUM) {
> + ret = -EINVAL;
> + goto unlock_return;
> + }
> +
> + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
> + if (ret < 0)
> + goto unlock_return;
> +
> + /* Checking if cycle count registers need changing. */
> + if (val == 600 && cycle_count == 200) {
> + ret = rm3100_set_cycle_count(data, 100);
> + if (ret < 0)
> + goto unlock_return;
> + } else if (val != 600 && cycle_count == 100) {
> + ret = rm3100_set_cycle_count(data, 200);
> + if (ret < 0)
> + goto unlock_return;
> + }
> +
> + /* Writing TMRC registers requires CMM reset. */
> + ret = regmap_write(regmap, RM3100_REG_CMM, 0);
> + if (ret < 0)
> + goto unlock_return;
> + ret = regmap_write(regmap, RM3100_REG_CMM,
> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> + mutex_unlock(&data->lock);

I would move this unlock until after setting the value below
(only slightly wider scope than the minimum)
then have a single exit point by moving the unlock_return label
above it and returning ret in all cases.

> + if (ret < 0)
> + return ret;
> +
> + data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> + return 0;
> +
> +unlock_return:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +/*
> + * Scale of this sensor depends on cycle count value, these three values
> + * are corresponding to cycle count value 50, 100, 200.
> + * scale = output / gain * 10e4.
> + */
> +const static int rm3100_scale[] = {500, 263, 133};
> +
> +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) {
> + iio_device_release_direct_mode(indio_dev);

Check what happens when claim_direct_mode fails.

You don't need to release it in this path. Any kernel function
should handle it's own error paths (and this one definitely does)
so if you get an error you should never need to clean up after
the function that gave it.

> + return ret;
> + }
> + ret = rm3100_read_mag(data, chan->scan_index, val);
> + iio_device_release_direct_mode(indio_dev);
> +
> + return ret;
> + case IIO_CHAN_INFO_SCALE:
> + *val = 0;
> + *val2 = rm3100_scale[data->cycle_count_index];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return 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);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return rm3100_set_samp_freq(data, val, val2);
> + default:
> + return -EINVAL;
> + }
> +
> +}
> +
> +static const struct iio_info rm3100_info = {
> + .attrs = &rm3100_attribute_group,
> + .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[9];
> + int ret;
> + int i;
> +
> + mutex_lock(&data->lock);
> + ret = rm3100_wait_measurement(data);
> + if (ret < 0) {
> + mutex_unlock(&data->lock);
> + goto done;
> + }
> +
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, sizeof(buffer));
> + mutex_unlock(&data->lock);

data->lock is I assume to protect the buffer. We are still using it
so you need to hold it longer... In fact all the way to the
done label. So just move your unlock down there for all paths.

> + if (ret < 0)
> + goto done;
> +
> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
> + for (i = 0; i < 3; i++)
> + memcpy(data->buffer + i * 4, buffer + i * 3, 3);

Firstly X doesn't need copying.
Secondly the copy of Y actually overwrites the value of Z
XXXYYYZZZxxx
XXXxYYYZZxxx
XXXxYYYxYZZx

I think...

> +
> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> + iio_get_time_ns(indio_dev));

Align with opening bracket.

> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void rm3100_remove(void *dh)
> +{
> + struct rm3100_data *data = (struct rm3100_data *)dh;
> + int ret;
> +
> + ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
> + if (ret < 0)
> + dev_err(data->dev, "failed to stop device.\n");
> +}
> +
> +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);
> + data->dev = dev;
> + data->regmap = regmap;
> + data->buffer = devm_kzalloc(dev, RM3100_SCAN_BYTES, GFP_KERNEL);
> + if (!data->buffer)
> + return -ENOMEM;
> +
> + 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_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 = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> + rm3100_trigger_handler, NULL);
> + if (ret < 0)
> + return ret;
> +
> + /* 3sec more wait time. */

I have no idea what this comment is referring to..
Not seeing any waiting here...

> + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
> + if (ret < 0)
> + return ret;
> + data->conversion_time =
> + rm3100_samp_rates[tmp-RM3100_TMRC_OFFSET][2] + 3000;

spacing around -
Please check this throughout.

> +
> + /* Cycle count values may not be what we want. */
> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> + if (ret < 0)
> + return ret;
> + if ((tmp - RM3100_TMRC_OFFSET) == 0)
> + rm3100_set_cycle_count(data, 100);
> + else
> + rm3100_set_cycle_count(data, 200);
> +
> + /* Starting all channels' conversion. */
> + ret = regmap_write(regmap, RM3100_REG_CMM,
> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> + if (ret < 0)
> + return ret;
> +
> + ret = devm_iio_device_register(dev, indio_dev);
> + if (ret < 0)
> + return ret;
> +
> + return devm_add_action(dev, rm3100_remove, data);

This is in the wrong order. If you were to get an error
from devm_iio_device_register you would not unwind the stuff
in remove.

Note however there is a nasty corner in that devm_add_action can
fail. If that fails, you need to manually call your unwind function
before returning.


> +}
> +EXPORT_SYMBOL_GPL(rm3100_common_probe);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..e8b84fac771b
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor a i2c bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * i2c slave address 0x20 + SA1 << 1 + SA0.
> + */
> +
> +#include <linux/module.h>
> +#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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_i2c_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..1a7a69fb9872
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-spi.c
> @@ -0,0 +1,64 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor a spi bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_spi_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..673647574add
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100.h
> @@ -0,0 +1,73 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Header file for PNI RM3100 driver
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#ifndef RM3100_CORE_H
> +#define RM3100_CORE_H
> +
> +#include <linux/regmap.h>
> +

Superficially it seems unlikely that any of the REG definitions
will ever be accessed from anywhere other than the -core.c file.

So it would be good to move them there.

General principle in the kernel is to limit the scope of anything
to the relevant files if possible.

> +#define RM3100_REG_REV_ID 0x36
> +
> +/* Cycle Count Registers. */
> +#define RM3100_REG_CC_X 0x05
> +#define RM3100_REG_CC_Y 0x07
> +#define RM3100_REG_CC_Z 0x09
> +
> +/* Continues Measurement Mode register. */
> +#define RM3100_REG_CMM 0x01
> +#define RM3100_CMM_START BIT(0)
> +#define RM3100_CMM_X BIT(4)
> +#define RM3100_CMM_Y BIT(5)
> +#define RM3100_CMM_Z BIT(6)
> +
> +/* TiMe Rate Configuration register. */
> +#define RM3100_REG_TMRC 0x0B
> +#define RM3100_TMRC_OFFSET 0x92
> +
> +/* Result Status register. */
> +#define RM3100_REG_STATUS 0x34
> +#define RM3100_STATUS_DRDY BIT(7)
> +
> +/* Measurement result registers. */
> +#define RM3100_REG_MX2 0x24
> +#define RM3100_REG_MY2 0x27
> +#define RM3100_REG_MZ2 0x2a
> +
> +#define RM3100_W_REG_START RM3100_REG_CMM
> +#define RM3100_W_REG_END RM3100_REG_REV_ID
> +#define RM3100_R_REG_START RM3100_REG_CMM
> +#define RM3100_R_REG_END RM3100_REG_STATUS
> +#define RM3100_V_REG_START RM3100_REG_MX2
> +#define RM3100_V_REG_END RM3100_REG_STATUS
> +
> +#define RM3100_SCAN_BYTES 24
> +
> +struct rm3100_data {
> + struct device *dev;
> + struct regmap *regmap;
> + struct completion measuring_done;
> + bool use_interrupt;
> +
> + int conversion_time;
> + int cycle_count_index;
> +
> + u8 *buffer;
> +
> + /*
> + * To protect consistency of every measurement and sampling
> + * frequency change operations.
> + */
> + struct mutex lock;
> +};
This doesn't seem to be used in the i2c and spi files either - so move
the definition to the top of the -core.c file.

> +
> +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);
> +
> +#endif /* RM3100_CORE_H */



2018-09-25 14:38:34

by Phil Reid

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On 25/09/2018 9:30 PM, Jonathan Cameron wrote:
>> +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[9];
>> + int ret;
>> + int i;
>> +
>> + mutex_lock(&data->lock);
>> + ret = rm3100_wait_measurement(data);
>> + if (ret < 0) {
>> + mutex_unlock(&data->lock);
>> + goto done;
>> + }
>> +
>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, sizeof(buffer));
>> + mutex_unlock(&data->lock);
>> + if (ret < 0)
>> + goto done;
>> +
>> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
>> + for (i = 0; i < 3; i++)
>> + memcpy(data->buffer + i * 4, buffer + i * 3, 3);
> Firstly X doesn't need copying.
> Secondly the copy of Y actually overwrites the value of Z
> XXXYYYZZZxxx
> XXXxYYYZZxxx
> XXXxYYYxYZZx
>
> I think...
>
>> +
>> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
>> + iio_get_time_ns(indio_dev));

memcpy target is a different buffer so should be ok.

But that raises the question of does it need to be?
'buffer' could be 12 bytes long and just shuffle Z then Y.
Do the unused bytes need to be zeroed? or does libiio mask them anyway?



--
Regards
Phil Reid

2018-09-25 17:51:00

by Himanshu Jha

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On Tue, Sep 25, 2018 at 11:17:24AM +0800, Song Qiang wrote:
> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> composed of 3 single sensors and a processing chip with MagI2C Interface.
>
> 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
depends

> the 'interrupts' in DT is declared.
>
> Signed-off-by: Song Qiang <[email protected]>
> ---
> Changes in v2:
> - Add scale channel.
> - Add EXPORT_SYMBOL_GPL() to export regmap confuguration
> structures.
> - Add sampling frequency available attribute.
> - Clean up headers and License declarations.
> - Change axis number to 3.
> - Remove bus specific part in compatible string.
> - Remove le32_to_cpu().
> - Check cycle count registers at *_probe().
> - Format comments.
> - Spell check.
> - Change prefix from RM_* to RM3100_*.
> - Check all error return paths.
> - Add devm_add_action() to avoid race condition when remove.
>
> MAINTAINERS | 7 +
> drivers/iio/magnetometer/Kconfig | 29 ++
> drivers/iio/magnetometer/Makefile | 4 +
> drivers/iio/magnetometer/rm3100-core.c | 470 +++++++++++++++++++++++++
> drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
> drivers/iio/magnetometer/rm3100-spi.c | 64 ++++
> drivers/iio/magnetometer/rm3100.h | 73 ++++
> 7 files changed, 705 insertions(+)
> 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/MAINTAINERS b/MAINTAINERS
> index 967ce8cdd1cc..14eeeb072403 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
> S: Maintained
> F: drivers/pnp/
>
> +PNI RM3100 IIO DRIVER
> +M: Song Qiang <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/iio/magnetometer/rm3100*
> +F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> +
> POSIX CLOCKS and TIMERS
> M: Thomas Gleixner <[email protected]>
> L: [email protected]
> 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.

Look at drivers/iio/chemical/Kconfig and do it similarly as done for
BME680.

> 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..5d28b53b7a04
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-core.c
> @@ -0,0 +1,470 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PNI RM3100 3-axis geomagnetic sensor driver core.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + *
> + * TODO: event generaton, pm.
generation


> +#include <linux/module.h>
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.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(RM3100_W_REG_START, RM3100_W_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_readable_table = {
> + .yes_ranges = rm3100_readable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_readable_table);
> +
> +static const struct regmap_range rm3100_writable_ranges[] = {
> + regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_writable_table = {
> + .yes_ranges = rm3100_writable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_writable_table);
> +
> +static const struct regmap_range rm3100_volatile_ranges[] = {
> + regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_volatile_table = {
> + .yes_ranges = rm3100_volatile_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_volatile_table);
> +
> +static irqreturn_t rm3100_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;
> + int tries = 20;
> + int ret;
> +
> + /*
> + * A read cycle of 400kbits i2c; bus is about 20us, plus the time
; was mistakenly added ?

> + * used for scheduling, a read cycle of fast mode of this device
> + * can reach 1.7ms, it may be possible for data to arrive just
> + * after we check the RM3100_REG_STATUS. In this case, irq_handler is
> + * called before measuring_done is reinitialized, it will wait
> + * forever for 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, RM3100_REG_STATUS, &val);
> + if (ret < 0)
> + return ret;
> +
> + if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
> + if (data->use_interrupt) {
> + ret = wait_for_completion_timeout(&data->measuring_done,
> + msecs_to_jiffies(data->conversion_time));
> + if (ret < 0)
> + return -ETIMEDOUT;
> + } else {
> + do {
> + usleep_range(1000, 5000);
> +
> + ret = regmap_read(regmap, RM3100_REG_STATUS,
> + &val);
> + if (ret < 0)
> + return ret;
> +
> + if (val & RM3100_STATUS_DRDY)
> + break;
> + } 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, RM3100_REG_MX2 + 3 * idx, buffer, 3);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> +
> + *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
> + 23);
> +
> + return IIO_VAL_INT;
> +}
> +
> +#define RM3100_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_SCALE) | \
> + 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[] = {
> + RM3100_CHANNEL(X, 0),
> + RM3100_CHANNEL(Y, 1),
> + RM3100_CHANNEL(Z, 2),
> + IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +static const unsigned long rm3100_scan_masks[] = {GENMASK(2, 0), 0};
> +
> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
> + "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
> +);
> +
> +static struct attribute *rm3100_attributes[] = {
> + &iio_const_attr_sampling_frequency_available.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group rm3100_attribute_group = {
> + .attrs = rm3100_attributes,
> +};
> +
> +#define RM3100_SAMP_NUM 14

Move this to top or .h header file.

> +/*
> + * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> + * Time between reading: rm3100_sam_rates[][2]ms.
> + * The first one is actually 1.7ms.
> + */
> +static const int rm3100_samp_rates[RM3100_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;
> +
> + mutex_lock(&data->lock);
> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);

use (unsigned int *) throughly as 3rd argument of
regmap_read() everywhere.

> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
> + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
> +{
> + int ret;
> + u8 i;
> +
> + for (i = 0; i < 3; i++) {
> + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, 100);

Would be better to use a descriptive macro for 100 instead ?

> + if (ret < 0)
> + return ret;
> + }
> + if (val == 50)
> + data->cycle_count_index = 0;
> + else if (val == 100)
> + data->cycle_count_index = 1;
> + else
> + data->cycle_count_index = 2;
> +
> + return 0;
> +}
> +
> +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;
> +
> + mutex_lock(&data->lock);
> + /* All cycle count registers use the same value. */
> + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
> + if (ret < 0)
> + goto unlock_return;
> +
> + for (i = 0; i < RM3100_SAMP_NUM; i++) {
> + if (val == rm3100_samp_rates[i][0] &&
> + val2 == rm3100_samp_rates[i][1])
> + break;
> + }
> + if (i == RM3100_SAMP_NUM) {
> + ret = -EINVAL;
> + goto unlock_return;
> + }
> +
> + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
> + if (ret < 0)
> + goto unlock_return;
> +
> + /* Checking if cycle count registers need changing. */
> + if (val == 600 && cycle_count == 200) {
> + ret = rm3100_set_cycle_count(data, 100);
> + if (ret < 0)
> + goto unlock_return;
> + } else if (val != 600 && cycle_count == 100) {
> + ret = rm3100_set_cycle_count(data, 200);
> + if (ret < 0)
> + goto unlock_return;
> + }
> +
> + /* Writing TMRC registers requires CMM reset. */
> + ret = regmap_write(regmap, RM3100_REG_CMM, 0);
> + if (ret < 0)
> + goto unlock_return;
> + ret = regmap_write(regmap, RM3100_REG_CMM,
> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> +
> + data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> + return 0;
> +
> +unlock_return:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +/*
> + * Scale of this sensor depends on cycle count value, these three values
> + * are corresponding to cycle count value 50, 100, 200.
> + * scale = output / gain * 10e4.
10^4 ?

> +const static int rm3100_scale[] = {500, 263, 133};
> +
> +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) {
> + iio_device_release_direct_mode(indio_dev);
> + return ret;
> + }
> + ret = rm3100_read_mag(data, chan->scan_index, val);
> + iio_device_release_direct_mode(indio_dev);
> +
> + return ret;
> + case IIO_CHAN_INFO_SCALE:
> + *val = 0;
> + *val2 = rm3100_scale[data->cycle_count_index];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return 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);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return rm3100_set_samp_freq(data, val, val2);
> + default:
> + return -EINVAL;
> + }
> +
> +}
> +
> +static const struct iio_info rm3100_info = {
> + .attrs = &rm3100_attribute_group,
> + .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[9];
> + int ret;
> + int i;
> +
> + mutex_lock(&data->lock);
> + ret = rm3100_wait_measurement(data);
> + if (ret < 0) {
> + mutex_unlock(&data->lock);
> + goto done;
> + }
> +
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, sizeof(buffer));
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + goto done;
> +
> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
> + for (i = 0; i < 3; i++)
> + memcpy(data->buffer + i * 4, buffer + i * 3, 3);
> +
> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> + iio_get_time_ns(indio_dev));
> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void rm3100_remove(void *dh)
> +{
> + struct rm3100_data *data = (struct rm3100_data *)dh;

No need for an explicit cast to dh.
Void pointer conversions are implicit.

> + int ret;
> +
> + ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
> + if (ret < 0)
> + dev_err(data->dev, "failed to stop device.\n");
> +}
> +
> +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);
> + data->dev = dev;

Check this if it's really required and possible remove
the `struct device *dev` from the private chip data struct.

Use can use `regmap_get_device()` for `rm3100_remove()`

> + data->regmap = regmap;
> + data->buffer = devm_kzalloc(dev, RM3100_SCAN_BYTES, GFP_KERNEL);
> + if (!data->buffer)
> + return -ENOMEM;
> +
> + 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_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 = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> + rm3100_trigger_handler, NULL);
> + if (ret < 0)
> + return ret;
> +
> + /* 3sec more wait time. */
> + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
> + if (ret < 0)
> + return ret;
> + data->conversion_time =
> + rm3100_samp_rates[tmp-RM3100_TMRC_OFFSET][2] + 3000;

This will fit in 80 char limit.

> + /* Cycle count values may not be what we want. */
> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> + if (ret < 0)
> + return ret;
> + if ((tmp - RM3100_TMRC_OFFSET) == 0)
> + rm3100_set_cycle_count(data, 100);
> + else
> + rm3100_set_cycle_count(data, 200);
> +
> + /* Starting all channels' conversion. */
> + ret = regmap_write(regmap, RM3100_REG_CMM,
> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> + if (ret < 0)
> + return ret;
> +
> + ret = devm_iio_device_register(dev, indio_dev);
> + if (ret < 0)
> + return ret;
> +
> + return devm_add_action(dev, rm3100_remove, data);
> +}
> +EXPORT_SYMBOL_GPL(rm3100_common_probe);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..e8b84fac771b
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor a i2c bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * i2c slave address 0x20 + SA1 << 1 + SA0.
> + */
> +
> +#include <linux/module.h>
> +#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,

I wonder when do we use other types of `.cache_type` ?

> +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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_i2c_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..1a7a69fb9872
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-spi.c
> @@ -0,0 +1,64 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor a spi bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_spi_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..673647574add
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100.h
> @@ -0,0 +1,73 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Header file for PNI RM3100 driver

Remove this bogus line.

> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#ifndef RM3100_CORE_H
> +#define RM3100_CORE_H
> +
> +#include <linux/regmap.h>
> +
> +#define RM3100_REG_REV_ID 0x36

Does this ID needs to be checked and validated during
intialisation with default state ID val ?

> +
> +/* Cycle Count Registers. */
> +#define RM3100_REG_CC_X 0x05
> +#define RM3100_REG_CC_Y 0x07
> +#define RM3100_REG_CC_Z 0x09
> +
> +/* Continues Measurement Mode register. */

Is it continuous ?

> +#define RM3100_REG_CMM 0x01
> +#define RM3100_CMM_START BIT(0)
> +#define RM3100_CMM_X BIT(4)
> +#define RM3100_CMM_Y BIT(5)
> +#define RM3100_CMM_Z BIT(6)
> +
> +/* TiMe Rate Configuration register. */

Time!

> +#define RM3100_REG_TMRC 0x0B
> +#define RM3100_TMRC_OFFSET 0x92
> +
> +/* Result Status register. */
> +#define RM3100_REG_STATUS 0x34
> +#define RM3100_STATUS_DRDY BIT(7)

If this status bit is a field of status register then
align this as:

#define RM3100_REG_STATUS 0x34
#define RM3100_STATUS_DRDY BIT(7)



> +/* Measurement result registers. */
> +#define RM3100_REG_MX2 0x24
> +#define RM3100_REG_MY2 0x27
> +#define RM3100_REG_MZ2 0x2a
> +
> +#define RM3100_W_REG_START RM3100_REG_CMM
> +#define RM3100_W_REG_END RM3100_REG_REV_ID
> +#define RM3100_R_REG_START RM3100_REG_CMM
> +#define RM3100_R_REG_END RM3100_REG_STATUS
> +#define RM3100_V_REG_START RM3100_REG_MX2
> +#define RM3100_V_REG_END RM3100_REG_STATUS
> +
> +#define RM3100_SCAN_BYTES 24
> +
> +struct rm3100_data {
> + struct device *dev;

Better remove this ?


--
Himanshu Jha
Undergraduate Student
Department of Electronics & Communication
Guru Tegh Bahadur Institute of Technology

2018-09-26 01:35:07

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On Tue, Sep 25, 2018 at 02:30:54PM +0100, Jonathan Cameron wrote:
> On Tue, 25 Sep 2018 11:17:24 +0800
> Song Qiang <[email protected]> wrote:
>
> > PNI RM3100 is a high resolution, large signal immunity magnetometer,
> > composed of 3 single sensors and a processing chip with MagI2C Interface.
> >
> > 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 <[email protected]>
> Various minor comments inline. Definitely heading in the right direction.
>
> Good work.
>
> Thanks,
>
> Jonathan
>

...

> > +static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
> > +{
> > + int ret;
> > + int tmp;
> > +
> > + mutex_lock(&data->lock);
> > + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> > + mutex_unlock(&data->lock);
> > + if (ret < 0)
> > + return ret;
> > + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
> > + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
> > +
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +}
> > +
> > +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
> > +{
> > + int ret;
> > + u8 i;
> > +
> > + for (i = 0; i < 3; i++) {
> > + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, 100);
>
> Does this write it to the same value irrespective of the value of val?
> Seems odd.
>
Hi Jonathan,

Sometimes I see these mistakes I couldn't stop laughing at my stupiness.
Should be val, my mistake submitting without a throughout functional
check, I think I'll write a script to prevent this from happening again.

> > + if (ret < 0)
> > + return ret;
> > + }
> > + if (val == 50)
>
> Switch would be nicer. Also why have all other values call back to
> 2 rather than generating an error?
>
I could have given it an error path, but I checked the code, 'val' in
this function is always given by my code. I think this leads no
possibility for val to go wrong. Do I still need an error path in this
circumstance?

> > + data->cycle_count_index = 0;
> > + else if (val == 100)
> > + data->cycle_count_index = 1;
> > + else
> > + data->cycle_count_index = 2;
> > +
> > + return 0;
> > +}
> > +
> > +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;
> > +
> > + mutex_lock(&data->lock);
>
> Why are you taking this lock? It's not well documented what it protects
> so it's not clear in this path why it is taken.
> (hint add a comment to the definition of the lock to make it clear!)
> - note I'm not saying it shouldn't be taken here, just that there is no
> comment to justify it..
>
When I was considering the lock, I read Robert Love's 'Linux Kernel
Development', whose chapter 9 talks about how to understand the lock.
It was talking about protecting data, but in here, my purpose is to
protect the i2c execution consistency. I think it may be possible that
one person is extracting the data while another person is planning set
the frequency. Setting the frequency within queuing data will causing
CMM(Continuous Measurement Mode) register reset, breaking the measurement
process. That's why I added lock to callbacks where a i2c sequence can
happen. I referred code from hmc5843, which did the similar lock stuff,
am I understanding about lock right?

> > + /* All cycle count registers use the same value. */
> > + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
> > + if (ret < 0)
> > + goto unlock_return;
> > +
> > + for (i = 0; i < RM3100_SAMP_NUM; i++) {
> > + if (val == rm3100_samp_rates[i][0] &&
> > + val2 == rm3100_samp_rates[i][1])
> > + break;
> > + }
> > + if (i == RM3100_SAMP_NUM) {
> > + ret = -EINVAL;
> > + goto unlock_return;
> > + }
> > +
> > + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
> > + if (ret < 0)
> > + goto unlock_return;

...

> > + if (ret < 0)
> > + goto done;
> > +
> > + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
> > + for (i = 0; i < 3; i++)
> > + memcpy(data->buffer + i * 4, buffer + i * 3, 3);
>
> Firstly X doesn't need copying.
> Secondly the copy of Y actually overwrites the value of Z
> XXXYYYZZZxxx
> XXXxYYYZZxxx
> XXXxYYYxYZZx
>
> I think...
>
They are in different memories, buffer is a temporary memory for data
reading out, data->buffer is for pushing to the userspace.

> > +
> > + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> > + iio_get_time_ns(indio_dev));
>
> Align with opening bracket.
>
> > +
> > +done:
> > + iio_trigger_notify_done(indio_dev->trig);
> > +
> > + return IRQ_HANDLED;
> > +}
> > +
> > +static void rm3100_remove(void *dh)
> > +{
> > + struct rm3100_data *data = (struct rm3100_data *)dh;
> > + int ret;
> > +
> > + ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
> > + if (ret < 0)
> > + dev_err(data->dev, "failed to stop device.\n");
> > +}
> > +
> > +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);
> > + data->dev = dev;
> > + data->regmap = regmap;
> > + data->buffer = devm_kzalloc(dev, RM3100_SCAN_BYTES, GFP_KERNEL);
> > + if (!data->buffer)
> > + return -ENOMEM;
> > +
> > + 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_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 = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> > + rm3100_trigger_handler, NULL);
> > + if (ret < 0)
> > + return ret;
> > +
> > + /* 3sec more wait time. */
>
> I have no idea what this comment is referring to..
> Not seeing any waiting here...
>
The thing is, measurement time of this sensor span from ~1.7ms to ~13
seconds. I'll need a wait time for *_wait_measurement(), and I use
measurement time + 3 seconds for waiting time. This is an initialization.
Usage of this is in *_wait_measurement.

yours,
Song Qiang

> > + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
> > + if (ret < 0)
> > + return ret;
> > + data->conversion_time =
> > + rm3100_samp_rates[tmp-RM3100_TMRC_OFFSET][2] + 3000;
>
> spacing around -
> Please check this throughout.

...

2018-09-26 01:50:21

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On Tue, Sep 25, 2018 at 10:36:54PM +0800, Phil Reid wrote:
> On 25/09/2018 9:30 PM, Jonathan Cameron wrote:
> > > +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[9];
> > > + int ret;
> > > + int i;
> > > +
> > > + mutex_lock(&data->lock);
> > > + ret = rm3100_wait_measurement(data);
> > > + if (ret < 0) {
> > > + mutex_unlock(&data->lock);
> > > + goto done;
> > > + }
> > > +
> > > + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, sizeof(buffer));
> > > + mutex_unlock(&data->lock);
> > > + if (ret < 0)
> > > + goto done;
> > > +
> > > + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
> > > + for (i = 0; i < 3; i++)
> > > + memcpy(data->buffer + i * 4, buffer + i * 3, 3);
> > Firstly X doesn't need copying.
> > Secondly the copy of Y actually overwrites the value of Z
> > XXXYYYZZZxxx
> > XXXxYYYZZxxx
> > XXXxYYYxYZZx
> >
> > I think...
> >
> > > +
> > > + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> > > + iio_get_time_ns(indio_dev));
>
> memcpy target is a different buffer so should be ok.
>
> But that raises the question of does it need to be?
> 'buffer' could be 12 bytes long and just shuffle Z then Y.
> Do the unused bytes need to be zeroed? or does libiio mask them anyway?
>
>
>
> --
> Regards
> Phil Reid

Hi Phil,

This is interesting, last patch I submitted uses three transactions and
shuffles X, Y and Z respectively. You said it should be better to use one
transactions, I thought it makes point, and one transaction may reduce
IO pressure of the i2c bus. :)
And that's not necessary for unused bytes to be zero. I'm not familiar
with libiio, actually just been studying it, can't say anything about
it.

yours,
Song Qiang

2018-09-26 02:24:58

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On Tue, Sep 25, 2018 at 11:20:18PM +0530, Himanshu Jha wrote:
> On Tue, Sep 25, 2018 at 11:17:24AM +0800, Song Qiang wrote:
> > PNI RM3100 is a high resolution, large signal immunity magnetometer,
> > composed of 3 single sensors and a processing chip with MagI2C Interface.
> >
> > 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
> depends
>

...

> +
> > +static int rm3100_wait_measurement(struct rm3100_data *data)
> > +{
> > + struct regmap *regmap = data->regmap;
> > + unsigned int val;
> > + int tries = 20;
> > + int ret;
> > +
> > + /*
> > + * A read cycle of 400kbits i2c; bus is about 20us, plus the time
> ; was mistakenly added ?
>

Hi Himanshu,

Oops, it shouldn't be there.

> > + * used for scheduling, a read cycle of fast mode of this device
> > + * can reach 1.7ms, it may be possible for data to arrive just
> > + * after we check the RM3100_REG_STATUS. In this case, irq_handler is
> > + * called before measuring_done is reinitialized, it will wait
> > + * forever for 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);
> > +

...

> > +static const struct attribute_group rm3100_attribute_group = {
> > + .attrs = rm3100_attributes,
> > +};
> > +
> > +#define RM3100_SAMP_NUM 14
>
> Move this to top or .h header file.
>

I could have move it to the top, but the only related section is
here. It only changes if a new frequency is supported, which seems
not possible from now. Just here to tell the readers how many
frequencies are in the array. So if one day a new frequency is
supported, we can change it easily.

> > +/*
> > + * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> > + * Time between reading: rm3100_sam_rates[][2]ms.
> > + * The first one is actually 1.7ms.
> > + */
> > +static const int rm3100_samp_rates[RM3100_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;
> > +
> > + mutex_lock(&data->lock);
> > + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
>
> use (unsigned int *) throughly as 3rd argument of
> regmap_read() everywhere.
>
> > + mutex_unlock(&data->lock);
> > + if (ret < 0)
> > + return ret;
> > + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
> > + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
> > +
> > + return IIO_VAL_INT_PLUS_MICRO;
> > +}
> > +
> > +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
> > +{
> > + int ret;
> > + u8 i;
> > +
> > + for (i = 0; i < 3; i++) {
> > + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, 100);
>
> Would be better to use a descriptive macro for 100 instead ?
>

This was a mistake, my fault.

> > + if (ret < 0)
> > + return ret;
> > + }
> > + if (val == 50)
> > + data->cycle_count_index = 0;
> > + else if (val == 100)
> > + data->cycle_count_index = 1;
> > + else
> > + data->cycle_count_index = 2;

...

> > +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,
>
> I wonder when do we use other types of `.cache_type` ?
>

Interesting question...

> > +static int rm3100_probe(struct i2c_client *client)
> > +{
> > + struct regmap *regmap;
> > +

...

> > +
> > +#ifndef RM3100_CORE_H
> > +#define RM3100_CORE_H
> > +
> > +#include <linux/regmap.h>
> > +
> > +#define RM3100_REG_REV_ID 0x36
>
> Does this ID needs to be checked and validated during
> intialisation with default state ID val ?
>

No, should remove it.

> > +
> > +/* Cycle Count Registers. */
> > +#define RM3100_REG_CC_X 0x05
> > +#define RM3100_REG_CC_Y 0x07
> > +#define RM3100_REG_CC_Z 0x09
> > +
> > +/* Continues Measurement Mode register. */
>
> Is it continuous ?
>
> > +#define RM3100_REG_CMM 0x01
> > +#define RM3100_CMM_START BIT(0)
> > +#define RM3100_CMM_X BIT(4)
> > +#define RM3100_CMM_Y BIT(5)
> > +#define RM3100_CMM_Z BIT(6)
> > +
> > +/* TiMe Rate Configuration register. */
>
> Time!
>
These uppercase letters are explaining why the register is called TMRC,
T(i)M(e) R(ate) C(onfiguration). :)
Though my bad english do carry a lot spell mistakes here...

> > +#define RM3100_REG_TMRC 0x0B
> > +#define RM3100_TMRC_OFFSET 0x92
> > +
> > +/* Result Status register. */
> > +#define RM3100_REG_STATUS 0x34
> > +#define RM3100_STATUS_DRDY BIT(7)
>
> If this status bit is a field of status register then
> align this as:
>
> #define RM3100_REG_STATUS 0x34
> #define RM3100_STATUS_DRDY BIT(7)
>
>
>
> > +/* Measurement result registers. */
> > +#define RM3100_REG_MX2 0x24
> > +#define RM3100_REG_MY2 0x27
> > +#define RM3100_REG_MZ2 0x2a
> > +
> > +#define RM3100_W_REG_START RM3100_REG_CMM
> > +#define RM3100_W_REG_END RM3100_REG_REV_ID
> > +#define RM3100_R_REG_START RM3100_REG_CMM
> > +#define RM3100_R_REG_END RM3100_REG_STATUS
> > +#define RM3100_V_REG_START RM3100_REG_MX2
> > +#define RM3100_V_REG_END RM3100_REG_STATUS
> > +
> > +#define RM3100_SCAN_BYTES 24
> > +
> > +struct rm3100_data {
> > + struct device *dev;
>
> Better remove this ?
>
All applied in the next patch. :)
>
> --
> Himanshu Jha
> Undergraduate Student
> Department of Electronics & Communication
> Guru Tegh Bahadur Institute of Technology

yours,
Song Qiang

2018-09-26 02:32:25

by Phil Reid

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On 26/09/2018 9:49 AM, Song Qiang wrote:
> On Tue, Sep 25, 2018 at 10:36:54PM +0800, Phil Reid wrote:
>> On 25/09/2018 9:30 PM, Jonathan Cameron wrote:
>>>> +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[9];
>>>> + int ret;
>>>> + int i;
>>>> +
>>>> + mutex_lock(&data->lock);
>>>> + ret = rm3100_wait_measurement(data);
>>>> + if (ret < 0) {
>>>> + mutex_unlock(&data->lock);
>>>> + goto done;
>>>> + }
>>>> +
>>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, sizeof(buffer));
>>>> + mutex_unlock(&data->lock);
>>>> + if (ret < 0)
>>>> + goto done;
>>>> +
>>>> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
>>>> + for (i = 0; i < 3; i++)
>>>> + memcpy(data->buffer + i * 4, buffer + i * 3, 3);
>>> Firstly X doesn't need copying.
>>> Secondly the copy of Y actually overwrites the value of Z
>>> XXXYYYZZZxxx
>>> XXXxYYYZZxxx
>>> XXXxYYYxYZZx
>>>
>>> I think...
>>>
>>>> +
>>>> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
>>>> + iio_get_time_ns(indio_dev));
>>
>> memcpy target is a different buffer so should be ok.
>>
>> But that raises the question of does it need to be?
>> 'buffer' could be 12 bytes long and just shuffle Z then Y.
>> Do the unused bytes need to be zeroed? or does libiio mask them anyway?
>>
>>
>>
>> --
>> Regards
>> Phil Reid
>
> Hi Phil,
>
> This is interesting, last patch I submitted uses three transactions and
> shuffles X, Y and Z respectively. You said it should be better to use one
> transactions, I thought it makes point, and one transaction may reduce
> IO pressure of the i2c bus. :)
> And that's not necessary for unused bytes to be zero. I'm not familiar
> with libiio, actually just been studying it, can't say anything about
> it.
>
> yours,
> Song Qiang
>
>
G'day Song,

yes the one transaction suggestion was to reduce pressure on the bus.
I think also with 3 calls you can up up with other devices taking over
the i2c / spi bus in between.

We've got a devkit for this part, but haven't got to wiring it up to our system as yet.
We're looking at using the i2c interface which could push things at max samplerate, so yes I'm
keen to see bus pressure reduced as much as possible.

I was thinking something like the following:

u8 buffer[12];
regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 9);

buffer[10] = buffer[8]; // or memcpy or some other fancy shuffle code.
buffer[9] = buffer[7];
buffer[8] = buffer[6];

buffer[6] = buffer[5];
buffer[5] = buffer[4];
buffer[4] = buffer[3];

iio_push_to_buffers_with_timestamp(indio_dev, buffer, iio_get_time_ns(indio_dev));

but I'm unsure if this would be needed:
buffer[7] = 0
buffer[3] = 0

What you've got does the job I think.

I haven't dug into the datasheet in great detail, and my iio knownledge is limited.
Are you sure the RM3100_CHANNEL scantype endianness is IIO_LE.
rm3100_read_mag looks to be doing big endian conversion and the datasheet agrees with that.


--
Regards
Phil Reid


2018-09-26 08:14:57

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On Wed, Sep 26, 2018 at 10:30:34AM +0800, Phil Reid wrote:
> On 26/09/2018 9:49 AM, Song Qiang wrote:
> > On Tue, Sep 25, 2018 at 10:36:54PM +0800, Phil Reid wrote:
> > > On 25/09/2018 9:30 PM, Jonathan Cameron wrote:
> > > > > +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[9];
> > > > > + int ret;
> > > > > + int i;
> > > > > +
> > > > > + mutex_lock(&data->lock);
> > > > > + ret = rm3100_wait_measurement(data);
> > > > > + if (ret < 0) {
> > > > > + mutex_unlock(&data->lock);
> > > > > + goto done;
> > > > > + }
> > > > > +
> > > > > + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, sizeof(buffer));
> > > > > + mutex_unlock(&data->lock);
> > > > > + if (ret < 0)
> > > > > + goto done;
> > > > > +
> > > > > + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
> > > > > + for (i = 0; i < 3; i++)
> > > > > + memcpy(data->buffer + i * 4, buffer + i * 3, 3);
> > > > Firstly X doesn't need copying.
> > > > Secondly the copy of Y actually overwrites the value of Z
> > > > XXXYYYZZZxxx
> > > > XXXxYYYZZxxx
> > > > XXXxYYYxYZZx
> > > >
> > > > I think...
> > > >
> > > > > +
> > > > > + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> > > > > + iio_get_time_ns(indio_dev));
> > >
> > > memcpy target is a different buffer so should be ok.
> > >
> > > But that raises the question of does it need to be?
> > > 'buffer' could be 12 bytes long and just shuffle Z then Y.
> > > Do the unused bytes need to be zeroed? or does libiio mask them anyway?
> > >
> > >
> > >
> > > --
> > > Regards
> > > Phil Reid
> >
> > Hi Phil,
> >
> > This is interesting, last patch I submitted uses three transactions and
> > shuffles X, Y and Z respectively. You said it should be better to use one
> > transactions, I thought it makes point, and one transaction may reduce
> > IO pressure of the i2c bus. :)
> > And that's not necessary for unused bytes to be zero. I'm not familiar
> > with libiio, actually just been studying it, can't say anything about
> > it.
> >
> > yours,
> > Song Qiang
> >
> >
> G'day Song,
>
> yes the one transaction suggestion was to reduce pressure on the bus.
> I think also with 3 calls you can up up with other devices taking over
> the i2c / spi bus in between.
>
> We've got a devkit for this part, but haven't got to wiring it up to our system as yet.
> We're looking at using the i2c interface which could push things at max samplerate, so yes I'm
> keen to see bus pressure reduced as much as possible.
>
> I was thinking something like the following:
>
> u8 buffer[12];
> regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 9);
>
> buffer[10] = buffer[8]; // or memcpy or some other fancy shuffle code.
> buffer[9] = buffer[7];
> buffer[8] = buffer[6];
>
> buffer[6] = buffer[5];
> buffer[5] = buffer[4];
> buffer[4] = buffer[3];
>
> iio_push_to_buffers_with_timestamp(indio_dev, buffer, iio_get_time_ns(indio_dev));
>
> but I'm unsure if this would be needed:
> buffer[7] = 0
> buffer[3] = 0
>
> What you've got does the job I think.
>
> I haven't dug into the datasheet in great detail, and my iio knownledge is limited.
> Are you sure the RM3100_CHANNEL scantype endianness is IIO_LE.
> rm3100_read_mag looks to be doing big endian conversion and the datasheet agrees with that.
>
>
> --
> Regards
> Phil Reid
>

Hi Phil,

You're absolutely right!
This should be big endian, I think I probably just want something there
when I was writing this code, planned to change it later, but apparently
I've forgotten it...

AFAIK, filling places we do not need with 0 is not needed, we just
extract valid data from valid bit field(24 here).

Both one transaction and three transactions way have their point, but
this is a OS, probably the spiltted one is better, I need some real
thinking about this...

I could have use the same buffer to read from the sensor and send it to
userspace like this:

int i = 0;
ret = regmap_bulk_read(regmap, RM3100_REG_MX2, 9);
if(ret)
...
/* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. */
for (i = 0; i < 2; i++)
memcpy(buffer + (2 - i) * 4, buffer + (2 - i) * 3), 3);

This code snippet will use the same buffer, actually that's what I was
using the first time. Jonathan must thinks so, from what he commented,
he assumed I was using the same buffer, also what you want.
But I changed this due to Peter's comment, maybe not a big deal, he
suggests to use sizeof(buffer), this makes me use an additional size 9
buffer. I thought this doesn't matter too much, just some additional
space from the stack, but now I think maybe less memory using would be
better...
After all, this length 9 seems like never shouldn't be changed...

yours,
Song Qiang


2018-09-27 01:59:35

by Phil Reid

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On 26/09/2018 4:09 PM, Song Qiang wrote:
> On Wed, Sep 26, 2018 at 10:30:34AM +0800, Phil Reid wrote:
>> On 26/09/2018 9:49 AM, Song Qiang wrote:
>>> On Tue, Sep 25, 2018 at 10:36:54PM +0800, Phil Reid wrote:
>>>> On 25/09/2018 9:30 PM, Jonathan Cameron wrote:
>>>>>> +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[9];
>>>>>> + int ret;
>>>>>> + int i;
>>>>>> +
>>>>>> + mutex_lock(&data->lock);
>>>>>> + ret = rm3100_wait_measurement(data);
>>>>>> + if (ret < 0) {
>>>>>> + mutex_unlock(&data->lock);
>>>>>> + goto done;
>>>>>> + }
>>>>>> +
>>>>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, sizeof(buffer));
>>>>>> + mutex_unlock(&data->lock);
>>>>>> + if (ret < 0)
>>>>>> + goto done;
>>>>>> +
>>>>>> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
>>>>>> + for (i = 0; i < 3; i++)
>>>>>> + memcpy(data->buffer + i * 4, buffer + i * 3, 3);
>>>>> Firstly X doesn't need copying.
>>>>> Secondly the copy of Y actually overwrites the value of Z
>>>>> XXXYYYZZZxxx
>>>>> XXXxYYYZZxxx
>>>>> XXXxYYYxYZZx
>>>>>
>>>>> I think...
>>>>>
>>>>>> +
>>>>>> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
>>>>>> + iio_get_time_ns(indio_dev));
>>>>
>>>> memcpy target is a different buffer so should be ok.
>>>>
>>>> But that raises the question of does it need to be?
>>>> 'buffer' could be 12 bytes long and just shuffle Z then Y.
>>>> Do the unused bytes need to be zeroed? or does libiio mask them anyway?
>>>>
>>>>
>>>>
>>>> --
>>>> Regards
>>>> Phil Reid
>>>
>>> Hi Phil,
>>>
>>> This is interesting, last patch I submitted uses three transactions and
>>> shuffles X, Y and Z respectively. You said it should be better to use one
>>> transactions, I thought it makes point, and one transaction may reduce
>>> IO pressure of the i2c bus. :)
>>> And that's not necessary for unused bytes to be zero. I'm not familiar
>>> with libiio, actually just been studying it, can't say anything about
>>> it.
>>>
>>> yours,
>>> Song Qiang
>>>
>>>
>> G'day Song,
>>
>> yes the one transaction suggestion was to reduce pressure on the bus.
>> I think also with 3 calls you can up up with other devices taking over
>> the i2c / spi bus in between.
>>
>> We've got a devkit for this part, but haven't got to wiring it up to our system as yet.
>> We're looking at using the i2c interface which could push things at max samplerate, so yes I'm
>> keen to see bus pressure reduced as much as possible.
>>
>> I was thinking something like the following:
>>
>> u8 buffer[12];
>> regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 9);
>>
>> buffer[10] = buffer[8]; // or memcpy or some other fancy shuffle code.
>> buffer[9] = buffer[7];
>> buffer[8] = buffer[6];
>>
>> buffer[6] = buffer[5];
>> buffer[5] = buffer[4];
>> buffer[4] = buffer[3];
>>
>> iio_push_to_buffers_with_timestamp(indio_dev, buffer, iio_get_time_ns(indio_dev));
>>
>> but I'm unsure if this would be needed:
>> buffer[7] = 0
>> buffer[3] = 0
>>
>> What you've got does the job I think.
>>
>> I haven't dug into the datasheet in great detail, and my iio knownledge is limited.
>> Are you sure the RM3100_CHANNEL scantype endianness is IIO_LE.
>> rm3100_read_mag looks to be doing big endian conversion and the datasheet agrees with that.
>>
>>
>> --
>> Regards
>> Phil Reid
>>
>
> Hi Phil,
>
> You're absolutely right!
> This should be big endian, I think I probably just want something there
> when I was writing this code, planned to change it later, but apparently
> I've forgotten it...
>
> AFAIK, filling places we do not need with 0 is not needed, we just
> extract valid data from valid bit field(24 here).
>
> Both one transaction and three transactions way have their point, but
> this is a OS, probably the spiltted one is better, I need some real
> thinking about this...
The one transaction is better.
Reduces i2c traffic by 6 bytes and ensure the measurements are tightly
coupled in time for the 3 axis.

>
> I could have use the same buffer to read from the sensor and send it to
> userspace like this:
>
> int i = 0;
> ret = regmap_bulk_read(regmap, RM3100_REG_MX2, 9);
> if(ret)
> ...
> /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. */
> for (i = 0; i < 2; i++)
> memcpy(buffer + (2 - i) * 4, buffer + (2 - i) * 3), 3);
>
> This code snippet will use the same buffer, actually that's what I was
> using the first time. Jonathan must thinks so, from what he commented,
> he assumed I was using the same buffer, also what you want.
> But I changed this due to Peter's comment, maybe not a big deal, he
> suggests to use sizeof(buffer), this makes me use an additional size 9
> buffer. I thought this doesn't matter too much, just some additional
> space from the stack, but now I think maybe less memory using would be
> better...
> After all, this length 9 seems like never shouldn't be changed...

yes that does the job.


--
Regards
Phil Reid



2018-09-29 11:38:04

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On Wed, 26 Sep 2018 16:09:45 +0800
Song Qiang <[email protected]> wrote:

> On Wed, Sep 26, 2018 at 10:30:34AM +0800, Phil Reid wrote:
> > On 26/09/2018 9:49 AM, Song Qiang wrote:
> > > On Tue, Sep 25, 2018 at 10:36:54PM +0800, Phil Reid wrote:
> > > > On 25/09/2018 9:30 PM, Jonathan Cameron wrote:
> > > > > > +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[9];
> > > > > > + int ret;
> > > > > > + int i;
> > > > > > +
> > > > > > + mutex_lock(&data->lock);
> > > > > > + ret = rm3100_wait_measurement(data);
> > > > > > + if (ret < 0) {
> > > > > > + mutex_unlock(&data->lock);
> > > > > > + goto done;
> > > > > > + }
> > > > > > +
> > > > > > + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, sizeof(buffer));
> > > > > > + mutex_unlock(&data->lock);
> > > > > > + if (ret < 0)
> > > > > > + goto done;
> > > > > > +
> > > > > > + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
> > > > > > + for (i = 0; i < 3; i++)
> > > > > > + memcpy(data->buffer + i * 4, buffer + i * 3, 3);
> > > > > Firstly X doesn't need copying.
> > > > > Secondly the copy of Y actually overwrites the value of Z
> > > > > XXXYYYZZZxxx
> > > > > XXXxYYYZZxxx
> > > > > XXXxYYYxYZZx
> > > > >
> > > > > I think...
> > > > >
> > > > > > +
> > > > > > + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> > > > > > + iio_get_time_ns(indio_dev));
> > > >
> > > > memcpy target is a different buffer so should be ok.
> > > >
> > > > But that raises the question of does it need to be?
> > > > 'buffer' could be 12 bytes long and just shuffle Z then Y.
> > > > Do the unused bytes need to be zeroed? or does libiio mask them anyway?
> > > >
> > > >
> > > >
> > > > --
> > > > Regards
> > > > Phil Reid
> > >
> > > Hi Phil,
> > >
> > > This is interesting, last patch I submitted uses three transactions and
> > > shuffles X, Y and Z respectively. You said it should be better to use one
> > > transactions, I thought it makes point, and one transaction may reduce
> > > IO pressure of the i2c bus. :)
> > > And that's not necessary for unused bytes to be zero. I'm not familiar
> > > with libiio, actually just been studying it, can't say anything about
> > > it.
> > >
> > > yours,
> > > Song Qiang
> > >
> > >
> > G'day Song,
> >
> > yes the one transaction suggestion was to reduce pressure on the bus.
> > I think also with 3 calls you can up up with other devices taking over
> > the i2c / spi bus in between.
> >
> > We've got a devkit for this part, but haven't got to wiring it up to our system as yet.
> > We're looking at using the i2c interface which could push things at max samplerate, so yes I'm
> > keen to see bus pressure reduced as much as possible.
> >
> > I was thinking something like the following:
> >
> > u8 buffer[12];
> > regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 9);
> >
> > buffer[10] = buffer[8]; // or memcpy or some other fancy shuffle code.
> > buffer[9] = buffer[7];
> > buffer[8] = buffer[6];
> >
> > buffer[6] = buffer[5];
> > buffer[5] = buffer[4];
> > buffer[4] = buffer[3];
> >
> > iio_push_to_buffers_with_timestamp(indio_dev, buffer, iio_get_time_ns(indio_dev));
> >
> > but I'm unsure if this would be needed:
> > buffer[7] = 0
> > buffer[3] = 0
> >
> > What you've got does the job I think.
> >
> > I haven't dug into the datasheet in great detail, and my iio knownledge is limited.
> > Are you sure the RM3100_CHANNEL scantype endianness is IIO_LE.
> > rm3100_read_mag looks to be doing big endian conversion and the datasheet agrees with that.
> >
> >
> > --
> > Regards
> > Phil Reid
> >
>
> Hi Phil,
>
> You're absolutely right!
> This should be big endian, I think I probably just want something there
> when I was writing this code, planned to change it later, but apparently
> I've forgotten it...
>
> AFAIK, filling places we do not need with 0 is not needed, we just
> extract valid data from valid bit field(24 here).
Indeed it isn't. We need to be careful not to leak any data (for
kernel security reasons), but it's fine if it just has some other
data from the sensor in it.
>
> Both one transaction and three transactions way have their point, but
> this is a OS, probably the spiltted one is better, I need some real
> thinking about this...
>
> I could have use the same buffer to read from the sensor and send it to
> userspace like this:
>
> int i = 0;
> ret = regmap_bulk_read(regmap, RM3100_REG_MX2, 9);
> if(ret)
> ...
> /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. */
> for (i = 0; i < 2; i++)
> memcpy(buffer + (2 - i) * 4, buffer + (2 - i) * 3), 3);
>
> This code snippet will use the same buffer, actually that's what I was
> using the first time. Jonathan must thinks so, from what he commented,
> he assumed I was using the same buffer, also what you want.

Gah. Good point! Two things called buffer and I failed to notice they
were different. :(

> But I changed this due to Peter's comment, maybe not a big deal, he
> suggests to use sizeof(buffer), this makes me use an additional size 9
> buffer. I thought this doesn't matter too much, just some additional
> space from the stack, but now I think maybe less memory using would be
> better...
> After all, this length 9 seems like never shouldn't be changed...

Agreed. Here in place is simple, avoids memory usage and also one of the
copies.

Jonathan

>
> yours,
> Song Qiang
>


2018-09-29 11:45:36

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 2/2] iio: magnetometer: Add driver support for PNI RM3100

On Wed, 26 Sep 2018 09:33:53 +0800
Song Qiang <[email protected]> wrote:

> On Tue, Sep 25, 2018 at 02:30:54PM +0100, Jonathan Cameron wrote:
> > On Tue, 25 Sep 2018 11:17:24 +0800
> > Song Qiang <[email protected]> wrote:
> >
> > > PNI RM3100 is a high resolution, large signal immunity magnetometer,
> > > composed of 3 single sensors and a processing chip with MagI2C Interface.
> > >
> > > 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 <[email protected]>
> > Various minor comments inline. Definitely heading in the right direction.
> >
> > Good work.
> >
> > Thanks,
> >
> > Jonathan
> >
>
> ...
>
> > > +static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
> > > +{
> > > + int ret;
> > > + int tmp;
> > > +
> > > + mutex_lock(&data->lock);
> > > + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> > > + mutex_unlock(&data->lock);
> > > + if (ret < 0)
> > > + return ret;
> > > + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
> > > + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
> > > +
> > > + return IIO_VAL_INT_PLUS_MICRO;
> > > +}
> > > +
> > > +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
> > > +{
> > > + int ret;
> > > + u8 i;
> > > +
> > > + for (i = 0; i < 3; i++) {
> > > + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, 100);
> >
> > Does this write it to the same value irrespective of the value of val?
> > Seems odd.
> >
> Hi Jonathan,
>
> Sometimes I see these mistakes I couldn't stop laughing at my stupiness.
> Should be val, my mistake submitting without a throughout functional
> check, I think I'll write a script to prevent this from happening again.
Not to worry, we all do it :) I had a good half day this week failing to
spot a really dumb buffer overrun (was evil as it was actually writing
a copy to memory that by coincidence was always just before where it
was being copied from so looked like a shift in the second array - hence
hours of looking in completely the wrong place).

Sadly, for an individual developer tests always tend to be a bit adhoc
and sometimes we simply forget one!

>
> > > + if (ret < 0)
> > > + return ret;
> > > + }
> > > + if (val == 50)
> >
> > Switch would be nicer. Also why have all other values call back to
> > 2 rather than generating an error?
> >
> I could have given it an error path, but I checked the code, 'val' in
> this function is always given by my code. I think this leads no
> possibility for val to go wrong. Do I still need an error path in this
> circumstance?

With a switch you will to make the static checkers happy. Put
a comment in there saying it won't happen.

>
> > > + data->cycle_count_index = 0;
> > > + else if (val == 100)
> > > + data->cycle_count_index = 1;
> > > + else
> > > + data->cycle_count_index = 2;
> > > +
> > > + return 0;
> > > +}
> > > +
> > > +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;
> > > +
> > > + mutex_lock(&data->lock);
> >
> > Why are you taking this lock? It's not well documented what it protects
> > so it's not clear in this path why it is taken.
> > (hint add a comment to the definition of the lock to make it clear!)
> > - note I'm not saying it shouldn't be taken here, just that there is no
> > comment to justify it..
> >
> When I was considering the lock, I read Robert Love's 'Linux Kernel
> Development', whose chapter 9 talks about how to understand the lock.
> It was talking about protecting data, but in here, my purpose is to
> protect the i2c execution consistency. I think it may be possible that
> one person is extracting the data while another person is planning set
> the frequency. Setting the frequency within queuing data will causing
> CMM(Continuous Measurement Mode) register reset, breaking the measurement
> process. That's why I added lock to callbacks where a i2c sequence can
> happen. I referred code from hmc5843, which did the similar lock stuff,
> am I understanding about lock right?

The use of lock is fine. Just needs documenting in the driver so that
other people can follow your logic in the future!
All locks should have a clear definition of purpose and scope.

>
> > > + /* All cycle count registers use the same value. */
> > > + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
> > > + if (ret < 0)
> > > + goto unlock_return;
> > > +
> > > + for (i = 0; i < RM3100_SAMP_NUM; i++) {
> > > + if (val == rm3100_samp_rates[i][0] &&
> > > + val2 == rm3100_samp_rates[i][1])
> > > + break;
> > > + }
> > > + if (i == RM3100_SAMP_NUM) {
> > > + ret = -EINVAL;
> > > + goto unlock_return;
> > > + }
> > > +
> > > + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
> > > + if (ret < 0)
> > > + goto unlock_return;
>
> ...
>
> > > + if (ret < 0)
> > > + goto done;
> > > +
> > > + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for padding. */
> > > + for (i = 0; i < 3; i++)
> > > + memcpy(data->buffer + i * 4, buffer + i * 3, 3);
> >
> > Firstly X doesn't need copying.
> > Secondly the copy of Y actually overwrites the value of Z
> > XXXYYYZZZxxx
> > XXXxYYYZZxxx
> > XXXxYYYxYZZx
> >
> > I think...
> >
> They are in different memories, buffer is a temporary memory for data
> reading out, data->buffer is for pushing to the userspace.

Yes. Stupid misread from me! Sorry about that.

>
> > > +
> > > + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> > > + iio_get_time_ns(indio_dev));
> >
> > Align with opening bracket.
> >
> > > +
> > > +done:
> > > + iio_trigger_notify_done(indio_dev->trig);
> > > +
> > > + return IRQ_HANDLED;
> > > +}
> > > +
> > > +static void rm3100_remove(void *dh)
> > > +{
> > > + struct rm3100_data *data = (struct rm3100_data *)dh;
> > > + int ret;
> > > +
> > > + ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
> > > + if (ret < 0)
> > > + dev_err(data->dev, "failed to stop device.\n");
> > > +}
> > > +
> > > +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);
> > > + data->dev = dev;
> > > + data->regmap = regmap;
> > > + data->buffer = devm_kzalloc(dev, RM3100_SCAN_BYTES, GFP_KERNEL);
> > > + if (!data->buffer)
> > > + return -ENOMEM;
> > > +
> > > + 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_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 = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> > > + rm3100_trigger_handler, NULL);
> > > + if (ret < 0)
> > > + return ret;
> > > +
> > > + /* 3sec more wait time. */
> >
> > I have no idea what this comment is referring to..
> > Not seeing any waiting here...
> >
> The thing is, measurement time of this sensor span from ~1.7ms to ~13
> seconds. I'll need a wait time for *_wait_measurement(), and I use
> measurement time + 3 seconds for waiting time. This is an initialization.
> Usage of this is in *_wait_measurement.

The wait comment is fine, but why here? It seemed to be floating on it's
own.

>
> yours,
> Song Qiang
>
> > > + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
> > > + if (ret < 0)
> > > + return ret;
> > > + data->conversion_time =
> > > + rm3100_samp_rates[tmp-RM3100_TMRC_OFFSET][2] + 3000;
> >
> > spacing around -
> > Please check this throughout.
>
> ...


2018-10-02 14:38:43

by Song Qiang

[permalink] [raw]
Subject: [PATCH v3 0/3] Add support for PNI RM3100 magnetometer

Hi,

The next 3 patches do the following:
- The first patch adds PNI Sensor Corporation to the vendor prefix list.
- The second patch adds devicetree binding for RM3100 magnetometer.
- The third patch adds the driver for RM3100 magnetometer.

-- Song Qiang

Changes in v3:
- Change PNI to PNI Sensor Corporation in vendor-prefix.txt.
- Sort header files in alpabetical order.
- Add more specific doc for the mutex.
- Fix the bug in the rm3100_set_cycle_count() where we always set the
cycle count value to 100 no matter what val is.
- Change some multiple else if to switch.
- Remove iio_device_release_direct_mode() in the case claim direct mode
fails.
- Use the same buffer in rm3100_trigger_handler().
- Add space around some operators.
- Add manually unwind functions in case devm_add_action() fails.
- Move devm_add_action before the devm_iio_device_register().
- Move register defines and rm3100_data struct to *-core.c.
- Change some storage variable type of regmap from int to unsigned int.
- Change 10e4 to 10^4.
- Remove explicit cast of data pointer in rm3100_remove().
- Remve dev in rm3100_data and use regmap_get_device() to obtain device
pointer instead.
- Change register bits alignment in defines.
- Move RM3100_SCAN_BYTES to the top.
- Change Channel endieness from LE to BE.
- Adjust the place of comment about the 3 second wait time.
- Some spell check.

Changes in v2:
- Add scale channel.
- Add EXPORT_SYMBOL_GPL() to export regmap confuguration structures.
- Add sampling frequency available attribute.
- Clean up headers and License declarations.
- Change axis number to 3.
- Remove bus specific part in compatible string.
- Remove le32_to_cpu().
- Check cycle count registers at *_probe().
- Format comments.
- Spell check.
- Change prefix from RM_* to RM3100_*.
- Check all error return paths.
- Add devm_add_action() to avoid race condition when remove.

Song Qiang (3):
dt-bindings: Add PNI to the vendor prefixes.
dt-bindings: Add PNI RM3100 device tree binding.
iio: magnetometer: Add driver support for PNI RM3100

.../bindings/iio/magnetometer/pni,rm3100.txt | 20 +
.../devicetree/bindings/vendor-prefixes.txt | 1 +
MAINTAINERS | 7 +
drivers/iio/magnetometer/Kconfig | 29 +
drivers/iio/magnetometer/Makefile | 4 +
drivers/iio/magnetometer/rm3100-core.c | 539 ++++++++++++++++++
drivers/iio/magnetometer/rm3100-i2c.c | 58 ++
drivers/iio/magnetometer/rm3100-spi.c | 64 +++
drivers/iio/magnetometer/rm3100.h | 17 +
9 files changed, 739 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

--
2.17.1


2018-10-02 14:38:57

by Song Qiang

[permalink] [raw]
Subject: [PATCH v3 1/3] dt-bindings: Add PNI to the vendor prefixes.

Signed-off-by: Song Qiang <[email protected]>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 41f0b97eb933..5b935f01e15b 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 Sensor Corporation
portwell Portwell Inc.
poslab Poslab Technology Co., Ltd.
powervr PowerVR (deprecated, use img)
--
2.17.1


2018-10-02 14:39:13

by Song Qiang

[permalink] [raw]
Subject: [PATCH v3 3/3] iio: magnetometer: Add driver support for PNI RM3100

PNI RM3100 is a high resolution, large signal immunity magnetometer,
composed of 3 single sensors and a processing chip with a MagI2C
interface.

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, depends on if
the 'interrupts' in DT is declared.

Signed-off-by: Song Qiang <[email protected]>
---
MAINTAINERS | 7 +
drivers/iio/magnetometer/Kconfig | 29 ++
drivers/iio/magnetometer/Makefile | 4 +
drivers/iio/magnetometer/rm3100-core.c | 539 +++++++++++++++++++++++++
drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
drivers/iio/magnetometer/rm3100-spi.c | 64 +++
drivers/iio/magnetometer/rm3100.h | 17 +
7 files changed, 718 insertions(+)
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/MAINTAINERS b/MAINTAINERS
index 967ce8cdd1cc..14eeeb072403 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
S: Maintained
F: drivers/pnp/

+PNI RM3100 IIO DRIVER
+M: Song Qiang <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/iio/magnetometer/rm3100*
+F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
+
POSIX CLOCKS and TIMERS
M: Thomas Gleixner <[email protected]>
L: [email protected]
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index ed9d776d01af..8a63cbbca4b7 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 3-Axis Magnetometer (I2C)"
+ depends on I2C
+ select SENSORS_RM3100
+ select REGMAP_I2C
+ help
+ Say Y here to add support for the PNI RM3100 3-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 3-Axis Magnetometer (SPI)"
+ depends on SPI_MASTER
+ select SENSORS_RM3100
+ select REGMAP_SPI
+ help
+ Say Y here to add support for the PNI RM3100 3-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..cacdaee28ae3
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-core.c
@@ -0,0 +1,539 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PNI RM3100 3-axis geomagnetic sensor driver core.
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ *
+ * User Manual available at
+ * <https://www.pnicorp.com/download/rm3100-user-manual/>
+ *
+ * TODO: event generation, pm.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include "rm3100.h"
+
+/* Cycle Count Registers. */
+#define RM3100_REG_CC_X 0x05
+#define RM3100_REG_CC_Y 0x07
+#define RM3100_REG_CC_Z 0x09
+
+/* Continuous Measurement Mode register. */
+#define RM3100_REG_CMM 0x01
+#define RM3100_CMM_START BIT(0)
+#define RM3100_CMM_X BIT(4)
+#define RM3100_CMM_Y BIT(5)
+#define RM3100_CMM_Z BIT(6)
+
+/* TiMe Rate Configuration register. */
+#define RM3100_REG_TMRC 0x0B
+#define RM3100_TMRC_OFFSET 0x92
+
+/* Result Status register. */
+#define RM3100_REG_STATUS 0x34
+#define RM3100_STATUS_DRDY BIT(7)
+
+/* Measurement result registers. */
+#define RM3100_REG_MX2 0x24
+#define RM3100_REG_MY2 0x27
+#define RM3100_REG_MZ2 0x2a
+
+#define RM3100_W_REG_START RM3100_REG_CMM
+#define RM3100_W_REG_END RM3100_REG_STATUS
+#define RM3100_R_REG_START RM3100_REG_CMM
+#define RM3100_R_REG_END RM3100_REG_STATUS
+#define RM3100_V_REG_START RM3100_REG_MX2
+#define RM3100_V_REG_END RM3100_REG_STATUS
+
+/*
+ * This is computed by hand, is the sum of channel storage bits and padding
+ * bits, which is 4+4+4+12=24 in here.
+ */
+#define RM3100_SCAN_BYTES 24
+
+struct rm3100_data {
+ struct regmap *regmap;
+ struct completion measuring_done;
+ bool use_interrupt;
+
+ int conversion_time;
+ int cycle_count_index;
+
+ u8 buffer[RM3100_SCAN_BYTES];
+
+ /*
+ * This lock is for protecting the consistency of series of i2c
+ * operations, that is, to make sure a measurement process will
+ * not be interrupted by a set frequency operation, which should
+ * be taken where a series of i2c operation starts, released where
+ * the operation ends.
+ */
+ struct mutex lock;
+};
+
+static const struct regmap_range rm3100_readable_ranges[] = {
+ regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
+};
+
+const struct regmap_access_table rm3100_readable_table = {
+ .yes_ranges = rm3100_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_readable_table);
+
+static const struct regmap_range rm3100_writable_ranges[] = {
+ regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
+};
+
+const struct regmap_access_table rm3100_writable_table = {
+ .yes_ranges = rm3100_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_writable_table);
+
+static const struct regmap_range rm3100_volatile_ranges[] = {
+ regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
+};
+
+const struct regmap_access_table rm3100_volatile_table = {
+ .yes_ranges = rm3100_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_volatile_table);
+
+static irqreturn_t rm3100_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;
+ int tries = 20;
+ int ret;
+
+ /*
+ * A read cycle of 400kbits i2c bus is about 20us, plus the time
+ * used for scheduling, a read cycle of fast mode of this device
+ * can reach 1.7ms, it may be possible for data to arrive just
+ * after we check the RM3100_REG_STATUS. In this case, irq_handler is
+ * called before measuring_done is reinitialized, it will wait
+ * forever for data that has already been ready.
+ * Reinitialize measuring_done before looking up makes sure we
+ * will always capture interrupt no matter when it happens.
+ */
+ if (data->use_interrupt)
+ reinit_completion(&data->measuring_done);
+
+ ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
+ if (ret < 0)
+ return ret;
+
+ if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
+ if (data->use_interrupt) {
+ ret = wait_for_completion_timeout(&data->measuring_done,
+ msecs_to_jiffies(data->conversion_time));
+ if (ret < 0)
+ return -ETIMEDOUT;
+ } else {
+ do {
+ usleep_range(1000, 5000);
+
+ ret = regmap_read(regmap, RM3100_REG_STATUS,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & RM3100_STATUS_DRDY)
+ break;
+ } 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, RM3100_REG_MX2 + 3 * idx, buffer, 3);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+
+ *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
+ 23);
+
+ return IIO_VAL_INT;
+}
+
+#define RM3100_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_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 24, \
+ .storagebits = 32, \
+ .shift = 8, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec rm3100_channels[] = {
+ RM3100_CHANNEL(X, 0),
+ RM3100_CHANNEL(Y, 1),
+ RM3100_CHANNEL(Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const unsigned long rm3100_scan_masks[] = {BIT(0) | BIT(1) | BIT(2), 0};
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+ "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
+);
+
+static struct attribute *rm3100_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group rm3100_attribute_group = {
+ .attrs = rm3100_attributes,
+};
+
+#define RM3100_SAMP_NUM 14
+
+/*
+ * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
+ * Time between reading: rm3100_sam_rates[][2]ms.
+ * The first one is actually 1.7ms.
+ */
+static const int rm3100_samp_rates[RM3100_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;
+ unsigned int tmp;
+
+ mutex_lock(&data->lock);
+ ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+ *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
+ *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
+{
+ int ret;
+ u8 i;
+
+ for (i = 0; i < 3; i++) {
+ ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (val) {
+ case 50:
+ data->cycle_count_index = 0;
+ break;
+ case 100:
+ data->cycle_count_index = 1;
+ break;
+ /*
+ * This function will never be called by users' code, so here we
+ * assume that it will never get a wrong parameter.
+ */
+ default:
+ data->cycle_count_index = 2;
+ }
+
+ return 0;
+}
+
+static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
+{
+ struct regmap *regmap = data->regmap;
+ unsigned int cycle_count;
+ int ret;
+ int i;
+
+ mutex_lock(&data->lock);
+ /* All cycle count registers use the same value. */
+ ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
+ if (ret < 0)
+ goto unlock_return;
+
+ for (i = 0; i < RM3100_SAMP_NUM; i++) {
+ if (val == rm3100_samp_rates[i][0] &&
+ val2 == rm3100_samp_rates[i][1])
+ break;
+ }
+ if (i == RM3100_SAMP_NUM) {
+ ret = -EINVAL;
+ goto unlock_return;
+ }
+
+ ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
+ if (ret < 0)
+ goto unlock_return;
+
+ /* Checking if cycle count registers need changing. */
+ if (val == 600 && cycle_count == 200) {
+ ret = rm3100_set_cycle_count(data, 100);
+ if (ret < 0)
+ goto unlock_return;
+ } else if (val != 600 && cycle_count == 100) {
+ ret = rm3100_set_cycle_count(data, 200);
+ if (ret < 0)
+ goto unlock_return;
+ }
+
+ /* Writing TMRC registers requires CMM reset. */
+ ret = regmap_write(regmap, RM3100_REG_CMM, 0);
+ if (ret < 0)
+ goto unlock_return;
+ ret = regmap_write(regmap, RM3100_REG_CMM,
+ RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
+ if (ret < 0)
+ goto unlock_return;
+ mutex_unlock(&data->lock);
+
+ data->conversion_time = rm3100_samp_rates[i][2] + 3000;
+ return 0;
+
+unlock_return:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+/*
+ * The scale of this sensor depends on the cycle count value, these three
+ * values are corresponding to the cycle count value 50, 100, 200.
+ * scale = output / gain * 10^4.
+ */
+const static int rm3100_scale[] = {500, 263, 133};
+
+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_SCALE:
+ *val = 0;
+ *val2 = rm3100_scale[data->cycle_count_index];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return 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);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return rm3100_set_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+
+}
+
+static const struct iio_info rm3100_info = {
+ .attrs = &rm3100_attribute_group,
+ .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;
+ int ret;
+ int i;
+
+ mutex_lock(&data->lock);
+ ret = rm3100_wait_measurement(data);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ goto done;
+ }
+
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+
+ /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
+ for (i = 0; i < 2; i++)
+ memcpy(data->buffer + (2 - i) * 4, data->buffer + (2 - i) * 3,
+ 3);
+
+ /*
+ * Always using the same buffer so that we wouldn't need to set the
+ * paddings to 0 in case of leaking any data.
+ */
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ iio_get_time_ns(indio_dev));
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void rm3100_remove(void *pdata)
+{
+ struct rm3100_data *data = pdata;
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
+
+ ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
+ if (ret < 0)
+ dev_err(dev, "failed to stop the device.\n");
+}
+
+int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
+{
+ struct iio_dev *indio_dev;
+ struct rm3100_data *data;
+ unsigned int tmp;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_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_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 = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ rm3100_trigger_handler, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
+ if (ret < 0)
+ return ret;
+ /* Initializing max wait time, 3sec more wait time for conversion. */
+ data->conversion_time =
+ rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2] + 3000;
+
+ /* Cycle count values may not be what we want. */
+ ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
+ if (ret < 0)
+ return ret;
+ if ((tmp - RM3100_TMRC_OFFSET) == 0)
+ rm3100_set_cycle_count(data, 100);
+ else
+ rm3100_set_cycle_count(data, 200);
+
+ /* Starting all channels' conversion. */
+ ret = regmap_write(regmap, RM3100_REG_CMM,
+ RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action(dev, rm3100_remove, data);
+ if (ret < 0) {
+ rm3100_remove(data);
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_GPL(rm3100_common_probe);
+
+MODULE_AUTHOR("Song Qiang <[email protected]>");
+MODULE_DESCRIPTION("PNI RM3100 3-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..8f02e0366886
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-i2c.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ *
+ * i2c slave address: 0x20 + SA1 << 1 + SA0.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.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 const struct of_device_id rm3100_dt_match[] = {
+ { .compatible = "pni,rm3100", },
+ { }
+};
+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,
+};
+module_i2c_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <[email protected]>");
+MODULE_DESCRIPTION("PNI RM3100 3-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..65d5eb9e4f5e
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-spi.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ */
+
+#include <linux/module.h>
+#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
+ { .compatible = "pni,rm3100", },
+ { }
+};
+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,
+};
+module_spi_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <[email protected]>");
+MODULE_DESCRIPTION("PNI RM3100 3-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..c3508218bc77
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ */
+
+#ifndef RM3100_CORE_H
+#define RM3100_CORE_H
+
+#include <linux/regmap.h>
+
+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);
+
+#endif /* RM3100_CORE_H */
--
2.17.1


2018-10-02 14:40:19

by Song Qiang

[permalink] [raw]
Subject: [PATCH v3 2/3] dt-bindings: Add PNI RM3100 device tree binding.

Signed-off-by: Song Qiang <[email protected]>
---
.../bindings/iio/magnetometer/pni,rm3100.txt | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt

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..4677690fc5d0
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
@@ -0,0 +1,20 @@
+* PNI RM3100 3-axis magnetometer sensor
+
+Required properties:
+
+- compatible : should be "pni,rm3100"
+- reg : the I2C address or SPI chip select number of the sensor.
+
+Optional properties:
+
+- interrupts: data ready (DRDY) from the chip.
+ The interrupts can be triggered on rising edges.
+
+Example:
+
+rm3100: rm3100@20 {
+ compatible = "pni,rm3100";
+ reg = <0x20>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <4 IRQ_TYPE_EDGE_RISING>;
+};
--
2.17.1


2018-10-03 01:43:09

by Phil Reid

[permalink] [raw]
Subject: Re: [PATCH v3 3/3] iio: magnetometer: Add driver support for PNI RM3100

G'day Song,

Noticed a one more thing below

On 2/10/2018 10:38 PM, Song Qiang wrote:
> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> composed of 3 single sensors and a processing chip with a MagI2C
> interface.
>
> 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, depends on if
> the 'interrupts' in DT is declared.
>
> Signed-off-by: Song Qiang <[email protected]>



> ---
> MAINTAINERS | 7 +
> drivers/iio/magnetometer/Kconfig | 29 ++
> drivers/iio/magnetometer/Makefile | 4 +
> drivers/iio/magnetometer/rm3100-core.c | 539 +++++++++++++++++++++++++
> drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
> drivers/iio/magnetometer/rm3100-spi.c | 64 +++
> drivers/iio/magnetometer/rm3100.h | 17 +
> 7 files changed, 718 insertions(+)
> 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/MAINTAINERS b/MAINTAINERS
> index 967ce8cdd1cc..14eeeb072403 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
> S: Maintained
> F: drivers/pnp/
>
> +PNI RM3100 IIO DRIVER
> +M: Song Qiang <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/iio/magnetometer/rm3100*
> +F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> +
> POSIX CLOCKS and TIMERS
> M: Thomas Gleixner <[email protected]>
> L: [email protected]
> diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
> index ed9d776d01af..8a63cbbca4b7 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 3-Axis Magnetometer (I2C)"
> + depends on I2C
> + select SENSORS_RM3100
> + select REGMAP_I2C
> + help
> + Say Y here to add support for the PNI RM3100 3-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 3-Axis Magnetometer (SPI)"
> + depends on SPI_MASTER
> + select SENSORS_RM3100
> + select REGMAP_SPI
> + help
> + Say Y here to add support for the PNI RM3100 3-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..cacdaee28ae3
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-core.c
> @@ -0,0 +1,539 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PNI RM3100 3-axis geomagnetic sensor driver core.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + *
> + * TODO: event generation, pm.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +#include "rm3100.h"
> +
> +/* Cycle Count Registers. */
> +#define RM3100_REG_CC_X 0x05
> +#define RM3100_REG_CC_Y 0x07
> +#define RM3100_REG_CC_Z 0x09
> +
> +/* Continuous Measurement Mode register. */
> +#define RM3100_REG_CMM 0x01
> +#define RM3100_CMM_START BIT(0)
> +#define RM3100_CMM_X BIT(4)
> +#define RM3100_CMM_Y BIT(5)
> +#define RM3100_CMM_Z BIT(6)
> +
> +/* TiMe Rate Configuration register. */
> +#define RM3100_REG_TMRC 0x0B
> +#define RM3100_TMRC_OFFSET 0x92
> +
> +/* Result Status register. */
> +#define RM3100_REG_STATUS 0x34
> +#define RM3100_STATUS_DRDY BIT(7)
> +
> +/* Measurement result registers. */
> +#define RM3100_REG_MX2 0x24
> +#define RM3100_REG_MY2 0x27
> +#define RM3100_REG_MZ2 0x2a
> +
> +#define RM3100_W_REG_START RM3100_REG_CMM
> +#define RM3100_W_REG_END RM3100_REG_STATUS
> +#define RM3100_R_REG_START RM3100_REG_CMM
> +#define RM3100_R_REG_END RM3100_REG_STATUS
> +#define RM3100_V_REG_START RM3100_REG_MX2
> +#define RM3100_V_REG_END RM3100_REG_STATUS
> +
> +/*
> + * This is computed by hand, is the sum of channel storage bits and padding
> + * bits, which is 4+4+4+12=24 in here.
> + */
> +#define RM3100_SCAN_BYTES 24
> +
> +struct rm3100_data {
> + struct regmap *regmap;
> + struct completion measuring_done;
> + bool use_interrupt;
> +
> + int conversion_time;
> + int cycle_count_index;
> +
> + u8 buffer[RM3100_SCAN_BYTES];
> +
> + /*
> + * This lock is for protecting the consistency of series of i2c
> + * operations, that is, to make sure a measurement process will
> + * not be interrupted by a set frequency operation, which should
> + * be taken where a series of i2c operation starts, released where
> + * the operation ends.
> + */
> + struct mutex lock;
> +};
> +
> +static const struct regmap_range rm3100_readable_ranges[] = {
> + regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_readable_table = {
> + .yes_ranges = rm3100_readable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_readable_table);
> +
> +static const struct regmap_range rm3100_writable_ranges[] = {
> + regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_writable_table = {
> + .yes_ranges = rm3100_writable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_writable_table);
> +
> +static const struct regmap_range rm3100_volatile_ranges[] = {
> + regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_volatile_table = {
> + .yes_ranges = rm3100_volatile_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_volatile_table);
> +
> +static irqreturn_t rm3100_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;
> + int tries = 20;
> + int ret;
> +
> + /*
> + * A read cycle of 400kbits i2c bus is about 20us, plus the time
> + * used for scheduling, a read cycle of fast mode of this device
> + * can reach 1.7ms, it may be possible for data to arrive just
> + * after we check the RM3100_REG_STATUS. In this case, irq_handler is
> + * called before measuring_done is reinitialized, it will wait
> + * forever for data that has already been ready.
> + * Reinitialize measuring_done before looking up makes sure we
> + * will always capture interrupt no matter when it happens.
> + */
> + if (data->use_interrupt)
> + reinit_completion(&data->measuring_done);
> +
> + ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
> + if (ret < 0)
> + return ret;
> +
> + if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
> + if (data->use_interrupt) {
> + ret = wait_for_completion_timeout(&data->measuring_done,
> + msecs_to_jiffies(data->conversion_time));
> + if (ret < 0)
> + return -ETIMEDOUT;
> + } else {
> + do {
> + usleep_range(1000, 5000);
> +
> + ret = regmap_read(regmap, RM3100_REG_STATUS,
> + &val);
> + if (ret < 0)
> + return ret;
> +
> + if (val & RM3100_STATUS_DRDY)
> + break;
> + } 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, RM3100_REG_MX2 + 3 * idx, buffer, 3);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> +
> + *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
> + 23);
> +
> + return IIO_VAL_INT;
> +}
> +
> +#define RM3100_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_SCALE) | \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .scan_index = idx, \
> + .scan_type = { \
> + .sign = 's', \
> + .realbits = 24, \
> + .storagebits = 32, \
> + .shift = 8, \
> + .endianness = IIO_BE, \
> + }, \
> + }
> +
> +static const struct iio_chan_spec rm3100_channels[] = {
> + RM3100_CHANNEL(X, 0),
> + RM3100_CHANNEL(Y, 1),
> + RM3100_CHANNEL(Z, 2),
> + IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +static const unsigned long rm3100_scan_masks[] = {BIT(0) | BIT(1) | BIT(2), 0};
> +
> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
> + "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
> +);
> +
> +static struct attribute *rm3100_attributes[] = {
> + &iio_const_attr_sampling_frequency_available.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group rm3100_attribute_group = {
> + .attrs = rm3100_attributes,
> +};
> +
> +#define RM3100_SAMP_NUM 14
> +
> +/*
> + * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> + * Time between reading: rm3100_sam_rates[][2]ms.
> + * The first one is actually 1.7ms.
> + */
> +static const int rm3100_samp_rates[RM3100_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;
> + unsigned int tmp;
> +
> + mutex_lock(&data->lock);
> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
> + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
> +{
> + int ret;
> + u8 i;
> +
> + for (i = 0; i < 3; i++) {
> + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
> + if (ret < 0)
> + return ret;
> + }
> +
> + switch (val) {
> + case 50:
> + data->cycle_count_index = 0;
> + break;
> + case 100:
> + data->cycle_count_index = 1;
> + break;
> + /*
> + * This function will never be called by users' code, so here we
> + * assume that it will never get a wrong parameter.
> + */
> + default:
> + data->cycle_count_index = 2;
> + }
> +
> + return 0;
> +}
> +
> +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
> +{
> + struct regmap *regmap = data->regmap;
> + unsigned int cycle_count;
> + int ret;
> + int i;
> +
> + mutex_lock(&data->lock);
> + /* All cycle count registers use the same value. */
> + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
> + if (ret < 0)
> + goto unlock_return;
> +
> + for (i = 0; i < RM3100_SAMP_NUM; i++) {
> + if (val == rm3100_samp_rates[i][0] &&
> + val2 == rm3100_samp_rates[i][1])
> + break;
> + }
> + if (i == RM3100_SAMP_NUM) {
> + ret = -EINVAL;
> + goto unlock_return;
> + }
> +
> + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
> + if (ret < 0)
> + goto unlock_return;
> +
> + /* Checking if cycle count registers need changing. */
> + if (val == 600 && cycle_count == 200) {
> + ret = rm3100_set_cycle_count(data, 100);
> + if (ret < 0)
> + goto unlock_return;
> + } else if (val != 600 && cycle_count == 100) {
> + ret = rm3100_set_cycle_count(data, 200);
> + if (ret < 0)
> + goto unlock_return;
> + }
> +
> + /* Writing TMRC registers requires CMM reset. */
> + ret = regmap_write(regmap, RM3100_REG_CMM, 0);
> + if (ret < 0)
> + goto unlock_return;
> + ret = regmap_write(regmap, RM3100_REG_CMM,
> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> + if (ret < 0)
> + goto unlock_return;
> + mutex_unlock(&data->lock);
> +
> + data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> + return 0;
> +
> +unlock_return:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +/*
> + * The scale of this sensor depends on the cycle count value, these three
> + * values are corresponding to the cycle count value 50, 100, 200.
> + * scale = output / gain * 10^4.
> + */
> +const static int rm3100_scale[] = {500, 263, 133};
> +
> +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_SCALE:
> + *val = 0;
> + *val2 = rm3100_scale[data->cycle_count_index];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return 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);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return rm3100_set_samp_freq(data, val, val2);
> + default:
> + return -EINVAL;
> + }
> +
> +}
> +
> +static const struct iio_info rm3100_info = {
> + .attrs = &rm3100_attribute_group,
> + .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;
> + int ret;
> + int i;
> +
> + mutex_lock(&data->lock);
> + ret = rm3100_wait_measurement(data);
> + if (ret < 0) {
> + mutex_unlock(&data->lock);
> + goto done;
> + }
> +
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + goto done;
> +
> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
> + for (i = 0; i < 2; i++)
> + memcpy(data->buffer + (2 - i) * 4, data->buffer + (2 - i) * 3,
> + 3);
> +
> + /*
> + * Always using the same buffer so that we wouldn't need to set the
> + * paddings to 0 in case of leaking any data.
> + */
> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> + iio_get_time_ns(indio_dev));
> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void rm3100_remove(void *pdata)
> +{
> + struct rm3100_data *data = pdata;
> + struct device *dev = regmap_get_device(data->regmap);
> + int ret;
> +
> + ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
> + if (ret < 0)
> + dev_err(dev, "failed to stop the device.\n");
> +}
> +
> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> +{
> + struct iio_dev *indio_dev;
> + struct rm3100_data *data;
> + unsigned int tmp;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + data = iio_priv(indio_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_irq_handler,
> + IRQF_TRIGGER_RISING,
> + indio_dev->name,
> + data);

Assuming the irq source is the DRDY pin, this pin is active high.
Which is automatically cleared when reading the measurement results register.

Perhaps when using interrupts the irq handler should do the read into data->buffer
to clear the irq. Would need to use devm_request_threaded_irq instead.

The SOC irq block I use doesn't support edge triggered irqs, so this currently wouldn't work if
DRDY was connected to that.


> + if (ret < 0) {
> + dev_err(dev, "request irq line failed.\n");
> + return ret;
> + }
> + init_completion(&data->measuring_done);
> + }
> +
> + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> + rm3100_trigger_handler, NULL);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
> + if (ret < 0)
> + return ret;
> + /* Initializing max wait time, 3sec more wait time for conversion. */
> + data->conversion_time =
> + rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2] + 3000;
> +
> + /* Cycle count values may not be what we want. */
> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> + if (ret < 0)
> + return ret;
> + if ((tmp - RM3100_TMRC_OFFSET) == 0)
> + rm3100_set_cycle_count(data, 100);
> + else
> + rm3100_set_cycle_count(data, 200);
> +
> + /* Starting all channels' conversion. */
> + ret = regmap_write(regmap, RM3100_REG_CMM,
> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> + if (ret < 0)
> + return ret;
> +
> + ret = devm_add_action(dev, rm3100_remove, data);
> + if (ret < 0) {
> + rm3100_remove(data);
> + return ret;
> + }
> +
> + return devm_iio_device_register(dev, indio_dev);
> +}
> +EXPORT_SYMBOL_GPL(rm3100_common_probe);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..8f02e0366886
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * i2c slave address: 0x20 + SA1 << 1 + SA0.
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/module.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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_i2c_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..65d5eb9e4f5e
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-spi.c
> @@ -0,0 +1,64 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_spi_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..c3508218bc77
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#ifndef RM3100_CORE_H
> +#define RM3100_CORE_H
> +
> +#include <linux/regmap.h>
> +
> +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);
> +
> +#endif /* RM3100_CORE_H */
>


--
Regards
Phil Reid

ElectroMagnetic Imaging Technology Pty Ltd
Development of Geophysical Instrumentation & Software
http://www.electromag.com.au

3 The Avenue, Midland WA 6056, AUSTRALIA
Ph: +61 8 9250 8100
Fax: +61 8 9250 7100
Email: [email protected]

2018-10-07 15:08:01

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 3/3] iio: magnetometer: Add driver support for PNI RM3100

On Wed, 3 Oct 2018 09:42:14 +0800
Phil Reid <[email protected]> wrote:

> G'day Song,

Hi Phil. Good point below. One general thing though, if you
could possibly crop down a review email when you are addressing one specific
point it would be very much appreciated!

Saves everyone else scrolling and if they have a rubbish email client
missing the comment entirely!

Thanks,
>
> Noticed a one more thing below
>
...
> > +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> > +{
> > + struct iio_dev *indio_dev;
> > + struct rm3100_data *data;
> > + unsigned int tmp;
> > + int ret;
> > +
> > + indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> > + if (!indio_dev)
> > + return -ENOMEM;
> > +
> > + data = iio_priv(indio_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_irq_handler,
> > + IRQF_TRIGGER_RISING,
> > + indio_dev->name,
> > + data);
>
> Assuming the irq source is the DRDY pin, this pin is active high.
> Which is automatically cleared when reading the measurement results register.
>
> Perhaps when using interrupts the irq handler should do the read into data->buffer
> to clear the irq. Would need to use devm_request_threaded_irq instead.

Agreed. With a level interrupt that is the easiest way to handle it.
Grab the data in the threaded handler and complete after that.

Jonathan


>
> The SOC irq block I use doesn't support edge triggered irqs, so this currently wouldn't work if
> DRDY was connected to that.
>
>
> > + if (ret < 0) {
> > + dev_err(dev, "request irq line failed.\n");
> > + return ret;
> > + }
> > + init_completion(&data->measuring_done);
> > + }
> > +
> > + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> > + rm3100_trigger_handler, NULL);
> > + if (ret < 0)
> > + return ret;
> > +
> > + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
> > + if (ret < 0)
> > + return ret;
> > + /* Initializing max wait time, 3sec more wait time for conversion. */
> > + data->conversion_time =
> > + rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2] + 3000;
> > +
...



2018-10-07 15:18:56

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 2/3] dt-bindings: Add PNI RM3100 device tree binding.

On Tue, 2 Oct 2018 22:38:11 +0800
Song Qiang <[email protected]> wrote:

> Signed-off-by: Song Qiang <[email protected]>
> ---
> .../bindings/iio/magnetometer/pni,rm3100.txt | 20 +++++++++++++++++++
> 1 file changed, 20 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
>
> 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..4677690fc5d0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> @@ -0,0 +1,20 @@
> +* PNI RM3100 3-axis magnetometer sensor
> +
> +Required properties:
> +
> +- compatible : should be "pni,rm3100"
> +- reg : the I2C address or SPI chip select number of the sensor.
> +
> +Optional properties:
> +
> +- interrupts: data ready (DRDY) from the chip.
> + The interrupts can be triggered on rising edges.
From Phil's response this appears to be incorrect and
it's actually a level sensitive interrupt.
I haven't checked the data sheet to confirm this.

That'll bring all sorts of pain if you have a host that can only
do edge sensitive so I'm hoping that's not true for you
(edge sensitive only interrupts on hosts are pretty unusual
though it cause me a lot of problems when I started out
with IIO years ago :(

The docs aren't super clear on this. The subtlety is
whether there is a guaranteed 'low' time between reading the
data and it going high again due to another reading. This usually
only matters if you are running very quickly though so may be
fine here.

This will only become relevant with continuous mode if you
add support for that later (I think!)

Jonathan

> +
> +Example:
> +
> +rm3100: rm3100@20 {
> + compatible = "pni,rm3100";
> + reg = <0x20>;
> + interrupt-parent = <&gpio0>;
> + interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> +};


2018-10-07 15:21:02

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 2/3] dt-bindings: Add PNI RM3100 device tree binding.

On Sun, 7 Oct 2018 16:18:08 +0100
Jonathan Cameron <[email protected]> wrote:

> On Tue, 2 Oct 2018 22:38:11 +0800
> Song Qiang <[email protected]> wrote:
>
> > Signed-off-by: Song Qiang <[email protected]>
> > ---
> > .../bindings/iio/magnetometer/pni,rm3100.txt | 20 +++++++++++++++++++
> > 1 file changed, 20 insertions(+)
> > create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> >
> > 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..4677690fc5d0
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> > @@ -0,0 +1,20 @@
> > +* PNI RM3100 3-axis magnetometer sensor
> > +
> > +Required properties:
> > +
> > +- compatible : should be "pni,rm3100"
> > +- reg : the I2C address or SPI chip select number of the sensor.
> > +
> > +Optional properties:
> > +
> > +- interrupts: data ready (DRDY) from the chip.
> > + The interrupts can be triggered on rising edges.
> From Phil's response this appears to be incorrect and
> it's actually a level sensitive interrupt.
> I haven't checked the data sheet to confirm this.
>
> That'll bring all sorts of pain if you have a host that can only
> do edge sensitive so I'm hoping that's not true for you
> (edge sensitive only interrupts on hosts are pretty unusual
> though it cause me a lot of problems when I started out
> with IIO years ago :(
>
> The docs aren't super clear on this. The subtlety is
> whether there is a guaranteed 'low' time between reading the
> data and it going high again due to another reading. This usually
> only matters if you are running very quickly though so may be
> fine here.
>
> This will only become relevant with continuous mode if you
> add support for that later (I think!)
I realised just after I sent this that I was being unclear.

If you aren't doing continuous mode and hence have a fairly
strong guarantee of a decent gap between reads, you may be
able to get away with supporting either LEVEL or EDGE
interrupts. In continuous mode, it is necessary to have
a minimum low time for EDGE interrupts to work.

Jonathan
>
> Jonathan
>
> > +
> > +Example:
> > +
> > +rm3100: rm3100@20 {
> > + compatible = "pni,rm3100";
> > + reg = <0x20>;
> > + interrupt-parent = <&gpio0>;
> > + interrupts = <4 IRQ_TYPE_EDGE_RISING>;
> > +};
>


2018-10-07 15:48:25

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 3/3] iio: magnetometer: Add driver support for PNI RM3100

On Tue, 2 Oct 2018 22:38:12 +0800
Song Qiang <[email protected]> wrote:

> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> composed of 3 single sensors and a processing chip with a MagI2C
> interface.
>
> 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, depends on if
> the 'interrupts' in DT is declared.
>
> Signed-off-by: Song Qiang <[email protected]>

I realise now that I should have read the datasheet properly.
Sorry about that.

What we have here is a hybrid of polled and continuous measurement.

If you are using the dataready as a trigger it is fine to support
continuous measurement, but you aren't doing that here.

The single shot measurement should be done with the method
described in the datasheet where you write a POLL command and then wait
for the single interrupt. There is no problem with racing and that
interrupt is a high level one and can be handled as such. We should
not do it by waiting for the next continuous measurement to happen
after clearing the status register, which is what I think is happening
here.

If you want to use it in continuous mode, you should provide a trigger.
That trigger will be fired by the dataready signal and the
discussion I put in the earlier reply becomes relevant.

Doing both of these options requires the interrupt handler to know
which mode you are in, but that is straight forward to implement and
is done in a number of other drivers.

Sorry again that I failed to identify this issue earlier.

Thanks to Phil as his question in the interrupt type got me thinking
about how you were handing the interrupts.

Jonathan


> ---
> MAINTAINERS | 7 +
> drivers/iio/magnetometer/Kconfig | 29 ++
> drivers/iio/magnetometer/Makefile | 4 +
> drivers/iio/magnetometer/rm3100-core.c | 539 +++++++++++++++++++++++++
> drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
> drivers/iio/magnetometer/rm3100-spi.c | 64 +++
> drivers/iio/magnetometer/rm3100.h | 17 +
> 7 files changed, 718 insertions(+)
> 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/MAINTAINERS b/MAINTAINERS
> index 967ce8cdd1cc..14eeeb072403 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
> S: Maintained
> F: drivers/pnp/
>
> +PNI RM3100 IIO DRIVER
> +M: Song Qiang <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/iio/magnetometer/rm3100*
> +F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> +
> POSIX CLOCKS and TIMERS
> M: Thomas Gleixner <[email protected]>
> L: [email protected]
> diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
> index ed9d776d01af..8a63cbbca4b7 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 3-Axis Magnetometer (I2C)"
> + depends on I2C
> + select SENSORS_RM3100
> + select REGMAP_I2C
> + help
> + Say Y here to add support for the PNI RM3100 3-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 3-Axis Magnetometer (SPI)"
> + depends on SPI_MASTER
> + select SENSORS_RM3100
> + select REGMAP_SPI
> + help
> + Say Y here to add support for the PNI RM3100 3-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..cacdaee28ae3
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-core.c
> @@ -0,0 +1,539 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PNI RM3100 3-axis geomagnetic sensor driver core.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + *
> + * TODO: event generation, pm.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +#include "rm3100.h"
> +
> +/* Cycle Count Registers. */
> +#define RM3100_REG_CC_X 0x05
> +#define RM3100_REG_CC_Y 0x07
> +#define RM3100_REG_CC_Z 0x09
> +
> +/* Continuous Measurement Mode register. */
> +#define RM3100_REG_CMM 0x01
> +#define RM3100_CMM_START BIT(0)
> +#define RM3100_CMM_X BIT(4)
> +#define RM3100_CMM_Y BIT(5)
> +#define RM3100_CMM_Z BIT(6)
> +
> +/* TiMe Rate Configuration register. */
> +#define RM3100_REG_TMRC 0x0B
> +#define RM3100_TMRC_OFFSET 0x92
> +
> +/* Result Status register. */
> +#define RM3100_REG_STATUS 0x34
> +#define RM3100_STATUS_DRDY BIT(7)
> +
> +/* Measurement result registers. */
> +#define RM3100_REG_MX2 0x24
> +#define RM3100_REG_MY2 0x27
> +#define RM3100_REG_MZ2 0x2a
> +
> +#define RM3100_W_REG_START RM3100_REG_CMM
> +#define RM3100_W_REG_END RM3100_REG_STATUS
> +#define RM3100_R_REG_START RM3100_REG_CMM
> +#define RM3100_R_REG_END RM3100_REG_STATUS
> +#define RM3100_V_REG_START RM3100_REG_MX2
> +#define RM3100_V_REG_END RM3100_REG_STATUS
> +
> +/*
> + * This is computed by hand, is the sum of channel storage bits and padding
> + * bits, which is 4+4+4+12=24 in here.
> + */
> +#define RM3100_SCAN_BYTES 24
> +
> +struct rm3100_data {
> + struct regmap *regmap;
> + struct completion measuring_done;
> + bool use_interrupt;
> +
> + int conversion_time;
> + int cycle_count_index;
> +
> + u8 buffer[RM3100_SCAN_BYTES];
> +
> + /*
> + * This lock is for protecting the consistency of series of i2c
> + * operations, that is, to make sure a measurement process will
> + * not be interrupted by a set frequency operation, which should
> + * be taken where a series of i2c operation starts, released where
> + * the operation ends.
> + */
> + struct mutex lock;
> +};
> +
> +static const struct regmap_range rm3100_readable_ranges[] = {
> + regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_readable_table = {
> + .yes_ranges = rm3100_readable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_readable_table);
> +
> +static const struct regmap_range rm3100_writable_ranges[] = {
> + regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_writable_table = {
> + .yes_ranges = rm3100_writable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_writable_table);
> +
> +static const struct regmap_range rm3100_volatile_ranges[] = {
> + regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_volatile_table = {
> + .yes_ranges = rm3100_volatile_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_volatile_table);
> +
> +static irqreturn_t rm3100_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;
> + int tries = 20;
> + int ret;
> +
> + /*
> + * A read cycle of 400kbits i2c bus is about 20us, plus the time
> + * used for scheduling, a read cycle of fast mode of this device
> + * can reach 1.7ms, it may be possible for data to arrive just
> + * after we check the RM3100_REG_STATUS. In this case, irq_handler is
> + * called before measuring_done is reinitialized, it will wait
> + * forever for data that has already been ready.
> + * Reinitialize measuring_done before looking up makes sure we
> + * will always capture interrupt no matter when it happens.
> + */
> + if (data->use_interrupt)
> + reinit_completion(&data->measuring_done);
> +
> + ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
> + if (ret < 0)
> + return ret;
> +
> + if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
> + if (data->use_interrupt) {
> + ret = wait_for_completion_timeout(&data->measuring_done,
> + msecs_to_jiffies(data->conversion_time));
> + if (ret < 0)
> + return -ETIMEDOUT;
> + } else {
> + do {
> + usleep_range(1000, 5000);
> +
> + ret = regmap_read(regmap, RM3100_REG_STATUS,
> + &val);
> + if (ret < 0)
> + return ret;
> +
> + if (val & RM3100_STATUS_DRDY)
> + break;
> + } 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, RM3100_REG_MX2 + 3 * idx, buffer, 3);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> +
> + *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
> + 23);
> +
> + return IIO_VAL_INT;
> +}
> +
> +#define RM3100_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_SCALE) | \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .scan_index = idx, \
> + .scan_type = { \
> + .sign = 's', \
> + .realbits = 24, \
> + .storagebits = 32, \
> + .shift = 8, \
> + .endianness = IIO_BE, \
> + }, \
> + }
> +
> +static const struct iio_chan_spec rm3100_channels[] = {
> + RM3100_CHANNEL(X, 0),
> + RM3100_CHANNEL(Y, 1),
> + RM3100_CHANNEL(Z, 2),
> + IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +static const unsigned long rm3100_scan_masks[] = {BIT(0) | BIT(1) | BIT(2), 0};
> +
> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
> + "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
> +);
> +
> +static struct attribute *rm3100_attributes[] = {
> + &iio_const_attr_sampling_frequency_available.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group rm3100_attribute_group = {
> + .attrs = rm3100_attributes,
> +};
> +
> +#define RM3100_SAMP_NUM 14
> +
> +/*
> + * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> + * Time between reading: rm3100_sam_rates[][2]ms.
> + * The first one is actually 1.7ms.
> + */
> +static const int rm3100_samp_rates[RM3100_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;
> + unsigned int tmp;
> +
> + mutex_lock(&data->lock);
> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
> + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
> +{
> + int ret;
> + u8 i;
> +
> + for (i = 0; i < 3; i++) {
> + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
> + if (ret < 0)
> + return ret;
> + }
> +
> + switch (val) {
> + case 50:
> + data->cycle_count_index = 0;
> + break;
> + case 100:
> + data->cycle_count_index = 1;
> + break;
> + /*
> + * This function will never be called by users' code, so here we
> + * assume that it will never get a wrong parameter.
> + */
> + default:
> + data->cycle_count_index = 2;
> + }
> +
> + return 0;
> +}
> +
> +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
> +{
> + struct regmap *regmap = data->regmap;
> + unsigned int cycle_count;
> + int ret;
> + int i;
> +
> + mutex_lock(&data->lock);
> + /* All cycle count registers use the same value. */
> + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
> + if (ret < 0)
> + goto unlock_return;
> +
> + for (i = 0; i < RM3100_SAMP_NUM; i++) {
> + if (val == rm3100_samp_rates[i][0] &&
> + val2 == rm3100_samp_rates[i][1])
> + break;
> + }
> + if (i == RM3100_SAMP_NUM) {
> + ret = -EINVAL;
> + goto unlock_return;
> + }
> +
> + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
> + if (ret < 0)
> + goto unlock_return;
> +
> + /* Checking if cycle count registers need changing. */
> + if (val == 600 && cycle_count == 200) {
> + ret = rm3100_set_cycle_count(data, 100);
> + if (ret < 0)
> + goto unlock_return;
> + } else if (val != 600 && cycle_count == 100) {
> + ret = rm3100_set_cycle_count(data, 200);
> + if (ret < 0)
> + goto unlock_return;
> + }
> +
> + /* Writing TMRC registers requires CMM reset. */
> + ret = regmap_write(regmap, RM3100_REG_CMM, 0);
> + if (ret < 0)
> + goto unlock_return;
> + ret = regmap_write(regmap, RM3100_REG_CMM,
> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> + if (ret < 0)
> + goto unlock_return;
> + mutex_unlock(&data->lock);
> +
> + data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> + return 0;
> +
> +unlock_return:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +/*
> + * The scale of this sensor depends on the cycle count value, these three
> + * values are corresponding to the cycle count value 50, 100, 200.
> + * scale = output / gain * 10^4.
> + */
> +const static int rm3100_scale[] = {500, 263, 133};
> +
> +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_SCALE:
> + *val = 0;
> + *val2 = rm3100_scale[data->cycle_count_index];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return 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);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return rm3100_set_samp_freq(data, val, val2);
> + default:
> + return -EINVAL;
> + }
> +
> +}
> +
> +static const struct iio_info rm3100_info = {
> + .attrs = &rm3100_attribute_group,
> + .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;
> + int ret;
> + int i;
> +
> + mutex_lock(&data->lock);
> + ret = rm3100_wait_measurement(data);
> + if (ret < 0) {
> + mutex_unlock(&data->lock);
> + goto done;
> + }
> +
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + goto done;
> +
> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
> + for (i = 0; i < 2; i++)
> + memcpy(data->buffer + (2 - i) * 4, data->buffer + (2 - i) * 3,
> + 3);
> +
> + /*
> + * Always using the same buffer so that we wouldn't need to set the
> + * paddings to 0 in case of leaking any data.
> + */
> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> + iio_get_time_ns(indio_dev));
> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void rm3100_remove(void *pdata)
> +{
Given this is no longer the opposite of probe, but rather just an
action to undo one particular element of probe (be it the only
one that needs undoing - yeah a subtle distinction ;)...

Can we give it a more meaningful name?

rm3100_stop perhaps? Perhaps even create a little function
to be rm3100_start as well so that it's easy to see how these
are paired?


> + struct rm3100_data *data = pdata;
> + struct device *dev = regmap_get_device(data->regmap);
> + int ret;
> +
> + ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
> + if (ret < 0)
> + dev_err(dev, "failed to stop the device.\n");
> +}
> +
> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> +{
> + struct iio_dev *indio_dev;
> + struct rm3100_data *data;
> + unsigned int tmp;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + data = iio_priv(indio_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_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 = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> + rm3100_trigger_handler, NULL);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
> + if (ret < 0)
> + return ret;
> + /* Initializing max wait time, 3sec more wait time for conversion. */
> + data->conversion_time =
> + rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2] + 3000;
> +
> + /* Cycle count values may not be what we want. */
> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> + if (ret < 0)
> + return ret;
> + if ((tmp - RM3100_TMRC_OFFSET) == 0)
> + rm3100_set_cycle_count(data, 100);
> + else
> + rm3100_set_cycle_count(data, 200);
> +
> + /* Starting all channels' conversion. */
> + ret = regmap_write(regmap, RM3100_REG_CMM,
> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> + if (ret < 0)
> + return ret;
> +
> + ret = devm_add_action(dev, rm3100_remove, data);
> + if (ret < 0) {
> + rm3100_remove(data);
> + return ret;
> + }
> +
> + return devm_iio_device_register(dev, indio_dev);
> +}
> +EXPORT_SYMBOL_GPL(rm3100_common_probe);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..8f02e0366886
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * i2c slave address: 0x20 + SA1 << 1 + SA0.
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/module.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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_i2c_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..65d5eb9e4f5e
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-spi.c
> @@ -0,0 +1,64 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_spi_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..c3508218bc77
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#ifndef RM3100_CORE_H
> +#define RM3100_CORE_H
> +
> +#include <linux/regmap.h>
> +
> +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);
> +
> +#endif /* RM3100_CORE_H */


2018-10-11 04:36:49

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH v3 3/3] iio: magnetometer: Add driver support for PNI RM3100



On 2018年10月07日 23:44, Jonathan Cameron wrote:
> On Tue, 2 Oct 2018 22:38:12 +0800
> Song Qiang <[email protected]> wrote:
>
>> PNI RM3100 is a high resolution, large signal immunity magnetometer,
>> composed of 3 single sensors and a processing chip with a MagI2C
>> interface.
>>
>> 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, depends on if
>> the 'interrupts' in DT is declared.
>>
>> Signed-off-by: Song Qiang <[email protected]>
> I realise now that I should have read the datasheet properly.
> Sorry about that.
>
> What we have here is a hybrid of polled and continuous measurement.
>
> If you are using the dataready as a trigger it is fine to support
> continuous measurement, but you aren't doing that here.
>
> The single shot measurement should be done with the method
> described in the datasheet where you write a POLL command and then wait
> for the single interrupt. There is no problem with racing and that
> interrupt is a high level one and can be handled as such. We should
> not do it by waiting for the next continuous measurement to happen
> after clearing the status register, which is what I think is happening
> here.
>
> If you want to use it in continuous mode, you should provide a trigger.
> That trigger will be fired by the dataready signal and the
> discussion I put in the earlier reply becomes relevant.
>
> Doing both of these options requires the interrupt handler to know
> which mode you are in, but that is straight forward to implement and
> is done in a number of other drivers.
>
> Sorry again that I failed to identify this issue earlier.
>
> Thanks to Phil as his question in the interrupt type got me thinking
> about how you were handing the interrupts.
>
> Jonathan
>

Hi Jonathan,

I learned the way of handling single shot from the driver of hmc5843,
seems like it needs changing, too.
There was some problems with my computer. Lenovo updates told me to
update BIOS and it went dead. I didn't write any code the past few days,
just got it fixed today.

yours,
Song Qiang

>> ---
>> MAINTAINERS | 7 +
>> drivers/iio/magnetometer/Kconfig | 29 ++
>> drivers/iio/magnetometer/Makefile | 4 +
>> drivers/iio/magnetometer/rm3100-core.c | 539 +++++++++++++++++++++++++
>> drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
>> drivers/iio/magnetometer/rm3100-spi.c | 64 +++
>> drivers/iio/magnetometer/rm3100.h | 17 +
>> 7 files changed, 718 insertions(+)
>> 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/MAINTAINERS b/MAINTAINERS
>> index 967ce8cdd1cc..14eeeb072403 100644
>> --- a/MAINTAINERS
>> +++ b/MAINTAINERS
>> @@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
>> S: Maintained
>> F: drivers/pnp/
>>
>> +PNI RM3100 IIO DRIVER
>> +M: Song Qiang <[email protected]>
>> +L: [email protected]
>> +S: Maintained
>> +F: drivers/iio/magnetometer/rm3100*
>> +F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
>> +
>> POSIX CLOCKS and TIMERS
>> M: Thomas Gleixner <[email protected]>
>> L: [email protected]
>> diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
>> index ed9d776d01af..8a63cbbca4b7 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 3-Axis Magnetometer (I2C)"
>> + depends on I2C
>> + select SENSORS_RM3100
>> + select REGMAP_I2C
>> + help
>> + Say Y here to add support for the PNI RM3100 3-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 3-Axis Magnetometer (SPI)"
>> + depends on SPI_MASTER
>> + select SENSORS_RM3100
>> + select REGMAP_SPI
>> + help
>> + Say Y here to add support for the PNI RM3100 3-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..cacdaee28ae3
>> --- /dev/null
>> +++ b/drivers/iio/magnetometer/rm3100-core.c
>> @@ -0,0 +1,539 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * PNI RM3100 3-axis geomagnetic sensor driver core.
>> + *
>> + * Copyright (C) 2018 Song Qiang <[email protected]>
>> + *
>> + * User Manual available at
>> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
>> + *
>> + * TODO: event generation, pm.
>> + */
>> +
>> +#include <linux/delay.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/module.h>
>> +#include <linux/slab.h>
>> +
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/kfifo_buf.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +
>> +#include "rm3100.h"
>> +
>> +/* Cycle Count Registers. */
>> +#define RM3100_REG_CC_X 0x05
>> +#define RM3100_REG_CC_Y 0x07
>> +#define RM3100_REG_CC_Z 0x09
>> +
>> +/* Continuous Measurement Mode register. */
>> +#define RM3100_REG_CMM 0x01
>> +#define RM3100_CMM_START BIT(0)
>> +#define RM3100_CMM_X BIT(4)
>> +#define RM3100_CMM_Y BIT(5)
>> +#define RM3100_CMM_Z BIT(6)
>> +
>> +/* TiMe Rate Configuration register. */
>> +#define RM3100_REG_TMRC 0x0B
>> +#define RM3100_TMRC_OFFSET 0x92
>> +
>> +/* Result Status register. */
>> +#define RM3100_REG_STATUS 0x34
>> +#define RM3100_STATUS_DRDY BIT(7)
>> +
>> +/* Measurement result registers. */
>> +#define RM3100_REG_MX2 0x24
>> +#define RM3100_REG_MY2 0x27
>> +#define RM3100_REG_MZ2 0x2a
>> +
>> +#define RM3100_W_REG_START RM3100_REG_CMM
>> +#define RM3100_W_REG_END RM3100_REG_STATUS
>> +#define RM3100_R_REG_START RM3100_REG_CMM
>> +#define RM3100_R_REG_END RM3100_REG_STATUS
>> +#define RM3100_V_REG_START RM3100_REG_MX2
>> +#define RM3100_V_REG_END RM3100_REG_STATUS
>> +
>> +/*
>> + * This is computed by hand, is the sum of channel storage bits and padding
>> + * bits, which is 4+4+4+12=24 in here.
>> + */
>> +#define RM3100_SCAN_BYTES 24
>> +
>> +struct rm3100_data {
>> + struct regmap *regmap;
>> + struct completion measuring_done;
>> + bool use_interrupt;
>> +
>> + int conversion_time;
>> + int cycle_count_index;
>> +
>> + u8 buffer[RM3100_SCAN_BYTES];
>> +
>> + /*
>> + * This lock is for protecting the consistency of series of i2c
>> + * operations, that is, to make sure a measurement process will
>> + * not be interrupted by a set frequency operation, which should
>> + * be taken where a series of i2c operation starts, released where
>> + * the operation ends.
>> + */
>> + struct mutex lock;
>> +};
>> +
>> +static const struct regmap_range rm3100_readable_ranges[] = {
>> + regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
>> +};
>> +
>> +const struct regmap_access_table rm3100_readable_table = {
>> + .yes_ranges = rm3100_readable_ranges,
>> + .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
>> +};
>> +EXPORT_SYMBOL_GPL(rm3100_readable_table);
>> +
>> +static const struct regmap_range rm3100_writable_ranges[] = {
>> + regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
>> +};
>> +
>> +const struct regmap_access_table rm3100_writable_table = {
>> + .yes_ranges = rm3100_writable_ranges,
>> + .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
>> +};
>> +EXPORT_SYMBOL_GPL(rm3100_writable_table);
>> +
>> +static const struct regmap_range rm3100_volatile_ranges[] = {
>> + regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
>> +};
>> +
>> +const struct regmap_access_table rm3100_volatile_table = {
>> + .yes_ranges = rm3100_volatile_ranges,
>> + .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
>> +};
>> +EXPORT_SYMBOL_GPL(rm3100_volatile_table);
>> +
>> +static irqreturn_t rm3100_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;
>> + int tries = 20;
>> + int ret;
>> +
>> + /*
>> + * A read cycle of 400kbits i2c bus is about 20us, plus the time
>> + * used for scheduling, a read cycle of fast mode of this device
>> + * can reach 1.7ms, it may be possible for data to arrive just
>> + * after we check the RM3100_REG_STATUS. In this case, irq_handler is
>> + * called before measuring_done is reinitialized, it will wait
>> + * forever for data that has already been ready.
>> + * Reinitialize measuring_done before looking up makes sure we
>> + * will always capture interrupt no matter when it happens.
>> + */
>> + if (data->use_interrupt)
>> + reinit_completion(&data->measuring_done);
>> +
>> + ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
>> + if (data->use_interrupt) {
>> + ret = wait_for_completion_timeout(&data->measuring_done,
>> + msecs_to_jiffies(data->conversion_time));
>> + if (ret < 0)
>> + return -ETIMEDOUT;
>> + } else {
>> + do {
>> + usleep_range(1000, 5000);
>> +
>> + ret = regmap_read(regmap, RM3100_REG_STATUS,
>> + &val);
>> + if (ret < 0)
>> + return ret;
>> +
>> + if (val & RM3100_STATUS_DRDY)
>> + break;
>> + } 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, RM3100_REG_MX2 + 3 * idx, buffer, 3);
>> + mutex_unlock(&data->lock);
>> + if (ret < 0)
>> + return ret;
>> +
>> + *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
>> + 23);
>> +
>> + return IIO_VAL_INT;
>> +}
>> +
>> +#define RM3100_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_SCALE) | \
>> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
>> + .scan_index = idx, \
>> + .scan_type = { \
>> + .sign = 's', \
>> + .realbits = 24, \
>> + .storagebits = 32, \
>> + .shift = 8, \
>> + .endianness = IIO_BE, \
>> + }, \
>> + }
>> +
>> +static const struct iio_chan_spec rm3100_channels[] = {
>> + RM3100_CHANNEL(X, 0),
>> + RM3100_CHANNEL(Y, 1),
>> + RM3100_CHANNEL(Z, 2),
>> + IIO_CHAN_SOFT_TIMESTAMP(3),
>> +};
>> +
>> +static const unsigned long rm3100_scan_masks[] = {BIT(0) | BIT(1) | BIT(2), 0};
>> +
>> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
>> + "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
>> +);
>> +
>> +static struct attribute *rm3100_attributes[] = {
>> + &iio_const_attr_sampling_frequency_available.dev_attr.attr,
>> + NULL,
>> +};
>> +
>> +static const struct attribute_group rm3100_attribute_group = {
>> + .attrs = rm3100_attributes,
>> +};
>> +
>> +#define RM3100_SAMP_NUM 14
>> +
>> +/*
>> + * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
>> + * Time between reading: rm3100_sam_rates[][2]ms.
>> + * The first one is actually 1.7ms.
>> + */
>> +static const int rm3100_samp_rates[RM3100_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;
>> + unsigned int tmp;
>> +
>> + mutex_lock(&data->lock);
>> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
>> + mutex_unlock(&data->lock);
>> + if (ret < 0)
>> + return ret;
>> + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
>> + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
>> +
>> + return IIO_VAL_INT_PLUS_MICRO;
>> +}
>> +
>> +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
>> +{
>> + int ret;
>> + u8 i;
>> +
>> + for (i = 0; i < 3; i++) {
>> + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
>> + if (ret < 0)
>> + return ret;
>> + }
>> +
>> + switch (val) {
>> + case 50:
>> + data->cycle_count_index = 0;
>> + break;
>> + case 100:
>> + data->cycle_count_index = 1;
>> + break;
>> + /*
>> + * This function will never be called by users' code, so here we
>> + * assume that it will never get a wrong parameter.
>> + */
>> + default:
>> + data->cycle_count_index = 2;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
>> +{
>> + struct regmap *regmap = data->regmap;
>> + unsigned int cycle_count;
>> + int ret;
>> + int i;
>> +
>> + mutex_lock(&data->lock);
>> + /* All cycle count registers use the same value. */
>> + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
>> + if (ret < 0)
>> + goto unlock_return;
>> +
>> + for (i = 0; i < RM3100_SAMP_NUM; i++) {
>> + if (val == rm3100_samp_rates[i][0] &&
>> + val2 == rm3100_samp_rates[i][1])
>> + break;
>> + }
>> + if (i == RM3100_SAMP_NUM) {
>> + ret = -EINVAL;
>> + goto unlock_return;
>> + }
>> +
>> + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
>> + if (ret < 0)
>> + goto unlock_return;
>> +
>> + /* Checking if cycle count registers need changing. */
>> + if (val == 600 && cycle_count == 200) {
>> + ret = rm3100_set_cycle_count(data, 100);
>> + if (ret < 0)
>> + goto unlock_return;
>> + } else if (val != 600 && cycle_count == 100) {
>> + ret = rm3100_set_cycle_count(data, 200);
>> + if (ret < 0)
>> + goto unlock_return;
>> + }
>> +
>> + /* Writing TMRC registers requires CMM reset. */
>> + ret = regmap_write(regmap, RM3100_REG_CMM, 0);
>> + if (ret < 0)
>> + goto unlock_return;
>> + ret = regmap_write(regmap, RM3100_REG_CMM,
>> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
>> + if (ret < 0)
>> + goto unlock_return;
>> + mutex_unlock(&data->lock);
>> +
>> + data->conversion_time = rm3100_samp_rates[i][2] + 3000;
>> + return 0;
>> +
>> +unlock_return:
>> + mutex_unlock(&data->lock);
>> + return ret;
>> +}
>> +
>> +/*
>> + * The scale of this sensor depends on the cycle count value, these three
>> + * values are corresponding to the cycle count value 50, 100, 200.
>> + * scale = output / gain * 10^4.
>> + */
>> +const static int rm3100_scale[] = {500, 263, 133};
>> +
>> +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_SCALE:
>> + *val = 0;
>> + *val2 = rm3100_scale[data->cycle_count_index];
>> +
>> + return IIO_VAL_INT_PLUS_MICRO;
>> + case IIO_CHAN_INFO_SAMP_FREQ:
>> + return 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);
>> +
>> + switch (mask) {
>> + case IIO_CHAN_INFO_SAMP_FREQ:
>> + return rm3100_set_samp_freq(data, val, val2);
>> + default:
>> + return -EINVAL;
>> + }
>> +
>> +}
>> +
>> +static const struct iio_info rm3100_info = {
>> + .attrs = &rm3100_attribute_group,
>> + .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;
>> + int ret;
>> + int i;
>> +
>> + mutex_lock(&data->lock);
>> + ret = rm3100_wait_measurement(data);
>> + if (ret < 0) {
>> + mutex_unlock(&data->lock);
>> + goto done;
>> + }
>> +
>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
>> + mutex_unlock(&data->lock);
>> + if (ret < 0)
>> + goto done;
>> +
>> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
>> + for (i = 0; i < 2; i++)
>> + memcpy(data->buffer + (2 - i) * 4, data->buffer + (2 - i) * 3,
>> + 3);
>> +
>> + /*
>> + * Always using the same buffer so that we wouldn't need to set the
>> + * paddings to 0 in case of leaking any data.
>> + */
>> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
>> + iio_get_time_ns(indio_dev));
>> +
>> +done:
>> + iio_trigger_notify_done(indio_dev->trig);
>> +
>> + return IRQ_HANDLED;
>> +}
>> +
>> +static void rm3100_remove(void *pdata)
>> +{
> Given this is no longer the opposite of probe, but rather just an
> action to undo one particular element of probe (be it the only
> one that needs undoing - yeah a subtle distinction ;)...
>
> Can we give it a more meaningful name?
>
> rm3100_stop perhaps? Perhaps even create a little function
> to be rm3100_start as well so that it's easy to see how these
> are paired?
>
>
>> + struct rm3100_data *data = pdata;
>> + struct device *dev = regmap_get_device(data->regmap);
>> + int ret;
>> +
>> + ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
>> + if (ret < 0)
>> + dev_err(dev, "failed to stop the device.\n");
>> +}
>> +
>> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
>> +{
>> + struct iio_dev *indio_dev;
>> + struct rm3100_data *data;
>> + unsigned int tmp;
>> + int ret;
>> +
>> + indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
>> + if (!indio_dev)
>> + return -ENOMEM;
>> +
>> + data = iio_priv(indio_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_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 = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
>> + rm3100_trigger_handler, NULL);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
>> + if (ret < 0)
>> + return ret;
>> + /* Initializing max wait time, 3sec more wait time for conversion. */
>> + data->conversion_time =
>> + rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2] + 3000;
>> +
>> + /* Cycle count values may not be what we want. */
>> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
>> + if (ret < 0)
>> + return ret;
>> + if ((tmp - RM3100_TMRC_OFFSET) == 0)
>> + rm3100_set_cycle_count(data, 100);
>> + else
>> + rm3100_set_cycle_count(data, 200);
>> +
>> + /* Starting all channels' conversion. */
>> + ret = regmap_write(regmap, RM3100_REG_CMM,
>> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = devm_add_action(dev, rm3100_remove, data);
>> + if (ret < 0) {
>> + rm3100_remove(data);
>> + return ret;
>> + }
>> +
>> + return devm_iio_device_register(dev, indio_dev);
>> +}
>> +EXPORT_SYMBOL_GPL(rm3100_common_probe);
>> +
>> +MODULE_AUTHOR("Song Qiang <[email protected]>");
>> +MODULE_DESCRIPTION("PNI RM3100 3-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..8f02e0366886
>> --- /dev/null
>> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
>> @@ -0,0 +1,58 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
>> + *
>> + * Copyright (C) 2018 Song Qiang <[email protected]>
>> + *
>> + * i2c slave address: 0x20 + SA1 << 1 + SA0.
>> + */
>> +
>> +#include <linux/i2c.h>
>> +#include <linux/module.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 const struct of_device_id rm3100_dt_match[] = {
>> + { .compatible = "pni,rm3100", },
>> + { }
>> +};
>> +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,
>> +};
>> +module_i2c_driver(rm3100_driver);
>> +
>> +MODULE_AUTHOR("Song Qiang <[email protected]>");
>> +MODULE_DESCRIPTION("PNI RM3100 3-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..65d5eb9e4f5e
>> --- /dev/null
>> +++ b/drivers/iio/magnetometer/rm3100-spi.c
>> @@ -0,0 +1,64 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
>> + *
>> + * Copyright (C) 2018 Song Qiang <[email protected]>
>> + */
>> +
>> +#include <linux/module.h>
>> +#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
>> + { .compatible = "pni,rm3100", },
>> + { }
>> +};
>> +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,
>> +};
>> +module_spi_driver(rm3100_driver);
>> +
>> +MODULE_AUTHOR("Song Qiang <[email protected]>");
>> +MODULE_DESCRIPTION("PNI RM3100 3-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..c3508218bc77
>> --- /dev/null
>> +++ b/drivers/iio/magnetometer/rm3100.h
>> @@ -0,0 +1,17 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Copyright (C) 2018 Song Qiang <[email protected]>
>> + */
>> +
>> +#ifndef RM3100_CORE_H
>> +#define RM3100_CORE_H
>> +
>> +#include <linux/regmap.h>
>> +
>> +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);
>> +
>> +#endif /* RM3100_CORE_H */


2018-10-12 07:37:03

by Song Qiang

[permalink] [raw]
Subject: [PATCH v4 1/3] dt-bindings: Add PNI to the vendor prefixes.

Signed-off-by: Song Qiang <[email protected]>
---
Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 41f0b97eb933..5b935f01e15b 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 Sensor Corporation
portwell Portwell Inc.
poslab Poslab Technology Co., Ltd.
powervr PowerVR (deprecated, use img)
--
2.17.1


2018-10-12 07:37:16

by Song Qiang

[permalink] [raw]
Subject: [PATCH v4 2/3] iio: magnetometer: Add DT support for PNI RM3100

Signed-off-by: Song Qiang <[email protected]>
---
.../bindings/iio/magnetometer/pni,rm3100.txt | 20 +++++++++++++++++++
1 file changed, 20 insertions(+)
create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt

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..497c932e9e39
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
@@ -0,0 +1,20 @@
+* PNI RM3100 3-axis magnetometer sensor
+
+Required properties:
+
+- compatible : should be "pni,rm3100"
+- reg : the I2C address or SPI chip select number of the sensor.
+
+Optional properties:
+
+- interrupts: data ready (DRDY) from the chip.
+ The interrupts can be triggered on level high.
+
+Example:
+
+rm3100: rm3100@20 {
+ compatible = "pni,rm3100";
+ reg = <0x20>;
+ interrupt-parent = <&gpio0>;
+ interrupts = <4 IRQ_TYPE_LEVEL_HIGH>;
+};
--
2.17.1


2018-10-12 07:37:21

by Song Qiang

[permalink] [raw]
Subject: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100

PNI RM3100 is a high resolution, large signal immunity magnetometer,
composed of 3 single sensors and a processing chip with a MagI2C
interface.

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

Signed-off-by: Song Qiang <[email protected]>
---
MAINTAINERS | 7 +
drivers/iio/magnetometer/Kconfig | 29 ++
drivers/iio/magnetometer/Makefile | 4 +
drivers/iio/magnetometer/rm3100-core.c | 627 +++++++++++++++++++++++++
drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
drivers/iio/magnetometer/rm3100-spi.c | 64 +++
drivers/iio/magnetometer/rm3100.h | 17 +
7 files changed, 806 insertions(+)
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/MAINTAINERS b/MAINTAINERS
index 967ce8cdd1cc..14eeeb072403 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
S: Maintained
F: drivers/pnp/

+PNI RM3100 IIO DRIVER
+M: Song Qiang <[email protected]>
+L: [email protected]
+S: Maintained
+F: drivers/iio/magnetometer/rm3100*
+F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
+
POSIX CLOCKS and TIMERS
M: Thomas Gleixner <[email protected]>
L: [email protected]
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index ed9d776d01af..8a63cbbca4b7 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 3-Axis Magnetometer (I2C)"
+ depends on I2C
+ select SENSORS_RM3100
+ select REGMAP_I2C
+ help
+ Say Y here to add support for the PNI RM3100 3-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 3-Axis Magnetometer (SPI)"
+ depends on SPI_MASTER
+ select SENSORS_RM3100
+ select REGMAP_SPI
+ help
+ Say Y here to add support for the PNI RM3100 3-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..d455982ce315
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-core.c
@@ -0,0 +1,627 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PNI RM3100 3-axis geomagnetic sensor driver core.
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ *
+ * User Manual available at
+ * <https://www.pnicorp.com/download/rm3100-user-manual/>
+ *
+ * TODO: event generation, pm.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include "rm3100.h"
+
+/* Cycle Count Registers. */
+#define RM3100_REG_CC_X 0x05
+#define RM3100_REG_CC_Y 0x07
+#define RM3100_REG_CC_Z 0x09
+
+/* Poll Measurement Mode register. */
+#define RM3100_REG_POLL 0x00
+#define RM3100_POLL_X BIT(4)
+#define RM3100_POLL_Y BIT(5)
+#define RM3100_POLL_Z BIT(6)
+
+/* Continuous Measurement Mode register. */
+#define RM3100_REG_CMM 0x01
+#define RM3100_CMM_START BIT(0)
+#define RM3100_CMM_X BIT(4)
+#define RM3100_CMM_Y BIT(5)
+#define RM3100_CMM_Z BIT(6)
+
+/* TiMe Rate Configuration register. */
+#define RM3100_REG_TMRC 0x0B
+#define RM3100_TMRC_OFFSET 0x92
+
+/* Result Status register. */
+#define RM3100_REG_STATUS 0x34
+#define RM3100_STATUS_DRDY BIT(7)
+
+/* Measurement result registers. */
+#define RM3100_REG_MX2 0x24
+#define RM3100_REG_MY2 0x27
+#define RM3100_REG_MZ2 0x2a
+
+#define RM3100_W_REG_START RM3100_REG_POLL
+#define RM3100_W_REG_END RM3100_REG_TMRC
+#define RM3100_R_REG_START RM3100_REG_POLL
+#define RM3100_R_REG_END RM3100_REG_STATUS
+#define RM3100_V_REG_START RM3100_REG_POLL
+#define RM3100_V_REG_END RM3100_REG_STATUS
+
+/*
+ * This is computed by hand, is the sum of channel storage bits and padding
+ * bits, which is 4+4+4+12=24 in here.
+ */
+#define RM3100_SCAN_BYTES 24
+
+struct rm3100_data {
+ struct regmap *regmap;
+ struct completion measuring_done;
+ bool use_interrupt;
+ int conversion_time;
+ int cycle_count_index;
+ u8 buffer[RM3100_SCAN_BYTES];
+ struct iio_trigger *drdy_trig;
+
+ /*
+ * This lock is for protecting the consistency of series of i2c
+ * operations, that is, to make sure a measurement process will
+ * not be interrupted by a set frequency operation, which should
+ * be taken where a series of i2c operation starts, released where
+ * the operation ends.
+ */
+ struct mutex lock;
+};
+
+static const struct regmap_range rm3100_readable_ranges[] = {
+ regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
+};
+
+const struct regmap_access_table rm3100_readable_table = {
+ .yes_ranges = rm3100_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_readable_table);
+
+static const struct regmap_range rm3100_writable_ranges[] = {
+ regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
+};
+
+const struct regmap_access_table rm3100_writable_table = {
+ .yes_ranges = rm3100_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_writable_table);
+
+static const struct regmap_range rm3100_volatile_ranges[] = {
+ regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
+};
+
+const struct regmap_access_table rm3100_volatile_table = {
+ .yes_ranges = rm3100_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_volatile_table);
+
+static irqreturn_t rm3100_thread_fn(int irq, void *d)
+{
+ struct iio_dev *indio_dev = d;
+ struct rm3100_data *data = iio_priv(indio_dev);
+
+ /*
+ * Write operation to any register or read operation
+ * to first byte of results will clear the interrupt.
+ */
+ regmap_write(data->regmap, RM3100_REG_POLL, 0);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rm3100_irq_handler(int irq, void *d)
+{
+ struct iio_dev *indio_dev = d;
+ struct rm3100_data *data = iio_priv(indio_dev);
+
+ switch (indio_dev->currentmode) {
+ case INDIO_DIRECT_MODE:
+ complete(&data->measuring_done);
+ break;
+ case INDIO_BUFFER_TRIGGERED:
+ iio_trigger_poll(data->drdy_trig);
+ break;
+ /* Initializing state. */
+ case 0:
+ break;
+ default:
+ dev_err(indio_dev->dev.parent,
+ "device mode out of control, current mode: %d",
+ indio_dev->currentmode);
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int rm3100_wait_measurement(struct rm3100_data *data)
+{
+ struct regmap *regmap = data->regmap;
+ unsigned int val;
+ int tries = 20;
+ int ret;
+
+ /*
+ * A read cycle of 400kbits i2c bus is about 20us, plus the time
+ * used for scheduling, a read cycle of fast mode of this device
+ * can reach 1.7ms, it may be possible for data to arrive just
+ * after we check the RM3100_REG_STATUS. In this case, irq_handler is
+ * called before measuring_done is reinitialized, it will wait
+ * forever for data that has already been ready.
+ * Reinitialize measuring_done before looking up makes sure we
+ * will always capture interrupt no matter when it happens.
+ */
+ if (data->use_interrupt)
+ reinit_completion(&data->measuring_done);
+
+ ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
+ if (ret < 0)
+ return ret;
+
+ if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
+ if (data->use_interrupt) {
+ ret = wait_for_completion_timeout(&data->measuring_done,
+ msecs_to_jiffies(data->conversion_time));
+ if (ret < 0)
+ return -ETIMEDOUT;
+ } else {
+ do {
+ usleep_range(1000, 5000);
+
+ ret = regmap_read(regmap, RM3100_REG_STATUS,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & RM3100_STATUS_DRDY)
+ break;
+ } 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 = regmap_write(regmap, RM3100_REG_POLL, BIT(4 + idx));
+ if (ret < 0)
+ goto unlock_return;
+
+ ret = rm3100_wait_measurement(data);
+ if (ret < 0)
+ goto unlock_return;
+
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * idx, buffer, 3);
+ if (ret < 0)
+ goto unlock_return;
+ mutex_unlock(&data->lock);
+
+ *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
+ 23);
+
+ return IIO_VAL_INT;
+
+unlock_return:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+#define RM3100_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_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 24, \
+ .storagebits = 32, \
+ .shift = 8, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec rm3100_channels[] = {
+ RM3100_CHANNEL(X, 0),
+ RM3100_CHANNEL(Y, 1),
+ RM3100_CHANNEL(Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+ "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
+);
+
+static struct attribute *rm3100_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group rm3100_attribute_group = {
+ .attrs = rm3100_attributes,
+};
+
+#define RM3100_SAMP_NUM 14
+
+/*
+ * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
+ * Time between reading: rm3100_sam_rates[][2]ms.
+ * The first one is actually 1.7ms.
+ */
+static const int rm3100_samp_rates[RM3100_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;
+ unsigned int tmp;
+
+ mutex_lock(&data->lock);
+ ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+ *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
+ *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
+{
+ int ret;
+ u8 i;
+
+ for (i = 0; i < 3; i++) {
+ ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ switch (val) {
+ case 50:
+ data->cycle_count_index = 0;
+ break;
+ case 100:
+ data->cycle_count_index = 1;
+ break;
+ /*
+ * This function will never be called by users' code, so here we
+ * assume that it will never get a wrong parameter.
+ */
+ default:
+ data->cycle_count_index = 2;
+ }
+
+ return 0;
+}
+
+static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
+{
+ struct regmap *regmap = data->regmap;
+ unsigned int cycle_count;
+ int ret;
+ int i;
+
+ mutex_lock(&data->lock);
+ /* All cycle count registers use the same value. */
+ ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
+ if (ret < 0)
+ goto unlock_return;
+
+ for (i = 0; i < RM3100_SAMP_NUM; i++) {
+ if (val == rm3100_samp_rates[i][0] &&
+ val2 == rm3100_samp_rates[i][1])
+ break;
+ }
+ if (i == RM3100_SAMP_NUM) {
+ ret = -EINVAL;
+ goto unlock_return;
+ }
+
+ ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
+ if (ret < 0)
+ goto unlock_return;
+
+ /* Checking if cycle count registers need changing. */
+ if (val == 600 && cycle_count == 200) {
+ ret = rm3100_set_cycle_count(data, 100);
+ if (ret < 0)
+ goto unlock_return;
+ } else if (val != 600 && cycle_count == 100) {
+ ret = rm3100_set_cycle_count(data, 200);
+ if (ret < 0)
+ goto unlock_return;
+ }
+
+ /* Writing TMRC registers requires CMM reset. */
+ ret = regmap_write(regmap, RM3100_REG_CMM, 0);
+ if (ret < 0)
+ goto unlock_return;
+ ret = regmap_write(regmap, RM3100_REG_CMM,
+ RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
+ if (ret < 0)
+ goto unlock_return;
+ mutex_unlock(&data->lock);
+
+ data->conversion_time = rm3100_samp_rates[i][2] + 3000;
+ return 0;
+
+unlock_return:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+/*
+ * The scale of this sensor depends on the cycle count value, these three
+ * values are corresponding to the cycle count value 50, 100, 200.
+ * scale = output / gain * 10^4.
+ */
+const static int rm3100_scale[] = {500, 263, 133};
+
+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_SCALE:
+ *val = 0;
+ *val2 = rm3100_scale[data->cycle_count_index];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return 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);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return rm3100_set_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+
+}
+
+static const struct iio_info rm3100_info = {
+ .attrs = &rm3100_attribute_group,
+ .read_raw = rm3100_read_raw,
+ .write_raw = rm3100_write_raw,
+};
+
+static int rm3100_buffer_preenable(struct iio_dev *iio_dev)
+{
+ struct rm3100_data *data = iio_priv(iio_dev);
+
+ /* Starting channels enabled. */
+ return regmap_write(data->regmap, RM3100_REG_CMM,
+ (*iio_dev->active_scan_mask & 0x7) << 4 | RM3100_CMM_START);
+}
+
+static int rm3100_buffer_postdisable(struct iio_dev *iio_dev)
+{
+ struct rm3100_data *data = iio_priv(iio_dev);
+
+ return regmap_write(data->regmap, RM3100_REG_CMM, 0);
+}
+
+static const struct iio_buffer_setup_ops rm3100_buffer_ops = {
+ .preenable = rm3100_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = rm3100_buffer_postdisable,
+};
+
+static irqreturn_t rm3100_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ unsigned long scan_mask = *indio_dev->active_scan_mask;
+ unsigned int mask_len = indio_dev->masklength;
+ struct rm3100_data *data = iio_priv(indio_dev);
+ struct regmap *regmap = data->regmap;
+ int ret, i, bit;
+
+ mutex_lock(&data->lock);
+ switch (scan_mask) {
+ case BIT(0) | BIT(1) | BIT(2):
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+ break;
+ case BIT(0) | BIT(1):
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+ break;
+ case BIT(1) | BIT(2):
+ ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+ break;
+ default:
+ for_each_set_bit(bit, &scan_mask, mask_len) {
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * bit,
+ data->buffer + bit * 3, 3);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ goto done;
+ }
+ }
+ mutex_unlock(&data->lock);
+ }
+
+ /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
+ for (i = mask_len - 1; i > 0; i--)
+ if (scan_mask | BIT(i))
+ memcpy(data->buffer + i * 4, data->buffer + i * 3, 3);
+
+ /*
+ * Always using the same buffer so that we wouldn't need to set the
+ * paddings to 0 in case of leaking any data.
+ */
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ iio_get_time_ns(indio_dev));
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void rm3100_remove(void *pdata)
+{
+ struct rm3100_data *data = pdata;
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
+
+ ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
+ if (ret < 0)
+ dev_err(dev, "failed to stop the device.\n");
+}
+
+int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
+{
+ struct iio_dev *indio_dev;
+ struct rm3100_data *data;
+ unsigned int tmp;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_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;
+
+ if (!irq)
+ data->use_interrupt = false;
+ else {
+ data->use_interrupt = true;
+
+ ret = devm_request_threaded_irq(dev,
+ irq,
+ rm3100_irq_handler,
+ rm3100_thread_fn,
+ IRQF_TRIGGER_HIGH |
+ IRQF_ONESHOT,
+ indio_dev->name,
+ indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "request irq line failed.\n");
+ return ret;
+ }
+ init_completion(&data->measuring_done);
+
+ data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->drdy_trig)
+ return -ENOMEM;
+
+ data->drdy_trig->dev.parent = dev;
+ ret = devm_iio_trigger_register(dev, data->drdy_trig);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ rm3100_trigger_handler,
+ &rm3100_buffer_ops);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
+ if (ret < 0)
+ return ret;
+ /* Initializing max wait time, 3sec more wait time for conversion. */
+ data->conversion_time =
+ rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2] + 3000;
+
+ /* Cycle count values may not be what we want. */
+ if ((tmp - RM3100_TMRC_OFFSET) == 0)
+ rm3100_set_cycle_count(data, 100);
+ else
+ rm3100_set_cycle_count(data, 200);
+
+ ret = devm_add_action(dev, rm3100_remove, data);
+ if (ret < 0) {
+ rm3100_remove(data);
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_GPL(rm3100_common_probe);
+
+MODULE_AUTHOR("Song Qiang <[email protected]>");
+MODULE_DESCRIPTION("PNI RM3100 3-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..8f02e0366886
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-i2c.c
@@ -0,0 +1,58 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ *
+ * i2c slave address: 0x20 + SA1 << 1 + SA0.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.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 const struct of_device_id rm3100_dt_match[] = {
+ { .compatible = "pni,rm3100", },
+ { }
+};
+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,
+};
+module_i2c_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <[email protected]>");
+MODULE_DESCRIPTION("PNI RM3100 3-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..65d5eb9e4f5e
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-spi.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
+ *
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ */
+
+#include <linux/module.h>
+#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
+ { .compatible = "pni,rm3100", },
+ { }
+};
+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,
+};
+module_spi_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <[email protected]>");
+MODULE_DESCRIPTION("PNI RM3100 3-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..c3508218bc77
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Song Qiang <[email protected]>
+ */
+
+#ifndef RM3100_CORE_H
+#define RM3100_CORE_H
+
+#include <linux/regmap.h>
+
+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);
+
+#endif /* RM3100_CORE_H */
--
2.17.1


2018-10-12 07:37:39

by Song Qiang

[permalink] [raw]
Subject: [PATCH v4 0/3] Add support for PNI RM3100 magnetometer

Hi,

The next 3 patches do the following:
- The first patch adds PNI Sensor Corporation to the vendor prefix list.
- The second patch adds devicetree binding for RM3100 magnetometer.
- The third patch adds the driver for RM3100 magnetometer.

-- Song Qiang

Changes in v4:
- Fix a bug about regmap_range configurations.
- Add DRDY trigger support.
- Change DRDY interrupt trigger source from RISING to HIGH.
- Add a thread function to clear the interrupt.
- Change the logic of one-shot read from waiting next continuous ends to
writing to POLL register and waiting conversion ends.
- Add iio_buffer_setup_ops to enable continuous conversion only when
buffer is enabled.
- Add active_scan_mask check in continuous conversions.
- Remove *wait_measurement() in *trigger_handler().

Changes in v3:
- Change PNI to PNI Sensor Corporation in vendor-prefix.txt.
- Sort header files in alpabetical order.
- Add more specific doc for the mutex.
- Fix the bug in the rm3100_set_cycle_count() where we always set the
cycle count value to 100 no matter what val is.
- Change some multiple else if to switch.
- Remove iio_device_release_direct_mode() in the case claim direct mode
fails.
- Use the same buffer in rm3100_trigger_handler().
- Add space around some operators.
- Add manually unwind functions in case devm_add_action() fails.
- Move devm_add_action before the devm_iio_device_register().
- Move register defines and rm3100_data struct to *-core.c.
- Change some storage variable type of regmap from int to unsigned int.
- Change 10e4 to 10^4.
- Remove explicit cast of data pointer in rm3100_remove().
- Remve dev in rm3100_data and use regmap_get_device() to obtain device
pointer instead.
- Change register bits alignment in defines.
- Move RM3100_SCAN_BYTES to the top.
- Change Channel endieness from LE to BE.
- Adjust the place of comment about the 3 second wait time.
- Some spell check.

Changes in v2:
- Add scale channel.
- Add EXPORT_SYMBOL_GPL() to export regmap confuguration structures.
- Add sampling frequency available attribute.
- Clean up headers and License declarations.
- Change axis number to 3.
- Remove bus specific part in compatible string.
- Remove le32_to_cpu().
- Check cycle count registers at *_probe().
- Format comments.
- Spell check.
- Change prefix from RM_* to RM3100_*.
- Check all error return paths.
- Add devm_add_action() to avoid race condition when remove.

Song Qiang (3):
dt-bindings: Add PNI to the vendor prefixes.
iio: magnetometer: Add DT support for PNI RM3100
iio: magnetometer: Add driver support for PNI RM3100

.../bindings/iio/magnetometer/pni,rm3100.txt | 20 +
.../devicetree/bindings/vendor-prefixes.txt | 1 +
MAINTAINERS | 7 +
drivers/iio/magnetometer/Kconfig | 29 +
drivers/iio/magnetometer/Makefile | 4 +
drivers/iio/magnetometer/rm3100-core.c | 627 ++++++++++++++++++
drivers/iio/magnetometer/rm3100-i2c.c | 58 ++
drivers/iio/magnetometer/rm3100-spi.c | 64 ++
drivers/iio/magnetometer/rm3100.h | 17 +
9 files changed, 827 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

--
2.17.1


2018-10-12 08:36:41

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100



On 2018年10月12日 15:35, Song Qiang wrote:
> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> composed of 3 single sensors and a processing chip with a MagI2C
> interface.
>
...
> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + unsigned long scan_mask = *indio_dev->active_scan_mask;
> + unsigned int mask_len = indio_dev->masklength;
> + struct rm3100_data *data = iio_priv(indio_dev);
> + struct regmap *regmap = data->regmap;
> + int ret, i, bit;
> +
> + mutex_lock(&data->lock);
> + switch (scan_mask) {
> + case BIT(0) | BIT(1) | BIT(2):
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + goto done;
> + break;
> + case BIT(0) | BIT(1):
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + goto done;
> + break;
> + case BIT(1) | BIT(2):
> + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + goto done;
> + break;
Hi Jonathan,

I just noticed that these three breaks are not proper aligned.

yours,
Song Qiang

2018-10-12 11:38:47

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v4 1/3] dt-bindings: Add PNI to the vendor prefixes.

On Fri, 12 Oct 2018 15:35:34 +0800, Song Qiang wrote:
> Signed-off-by: Song Qiang <[email protected]>
> ---
> Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
> 1 file changed, 1 insertion(+)
>

Reviewed-by: Rob Herring <[email protected]>

2018-10-12 11:38:56

by Rob Herring (Arm)

[permalink] [raw]
Subject: Re: [PATCH v4 2/3] iio: magnetometer: Add DT support for PNI RM3100

On Fri, 12 Oct 2018 15:35:35 +0800, Song Qiang wrote:
> Signed-off-by: Song Qiang <[email protected]>
> ---
> .../bindings/iio/magnetometer/pni,rm3100.txt | 20 +++++++++++++++++++
> 1 file changed, 20 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
>

Reviewed-by: Rob Herring <[email protected]>

2018-10-12 12:55:23

by Himanshu Jha

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100

Hi Qiang,

On Fri, Oct 12, 2018 at 04:36:01PM +0800, Song Qiang wrote:
>
>
> On 2018年10月12日 15:35, Song Qiang wrote:
> > PNI RM3100 is a high resolution, large signal immunity magnetometer,
> > composed of 3 single sensors and a processing chip with a MagI2C
> > interface.
> >
> ...
> > +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> > +{
> > + struct iio_poll_func *pf = p;
> > + struct iio_dev *indio_dev = pf->indio_dev;
> > + unsigned long scan_mask = *indio_dev->active_scan_mask;
> > + unsigned int mask_len = indio_dev->masklength;
> > + struct rm3100_data *data = iio_priv(indio_dev);
> > + struct regmap *regmap = data->regmap;
> > + int ret, i, bit;
> > +
> > + mutex_lock(&data->lock);
> > + switch (scan_mask) {
> > + case BIT(0) | BIT(1) | BIT(2):
> > + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
> > + mutex_unlock(&data->lock);
> > + if (ret < 0)
> > + goto done;
> > + break;
> > + case BIT(0) | BIT(1):
> > + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
> > + mutex_unlock(&data->lock);
> > + if (ret < 0)
> > + goto done;
> > + break;
> > + case BIT(1) | BIT(2):
> > + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
> > + mutex_unlock(&data->lock);
> > + if (ret < 0)
> > + goto done;
> > + break;
> Hi Jonathan,
>
> I just noticed that these three breaks are not proper aligned.

Please send the new version of a patch as a *new* thread and don't
use `--in-reply-to` flag(if you're using) to chain into older versions
as whole thread of older discussion comes up and is often not required.

The changelog gives enough info of what's new in the revised series.


--
Himanshu Jha
Undergraduate Student
Department of Electronics & Communication
Guru Tegh Bahadur Institute of Technology

2018-10-13 09:25:27

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v3 3/3] iio: magnetometer: Add driver support for PNI RM3100

On Thu, 11 Oct 2018 12:35:18 +0800
Song Qiang <[email protected]> wrote:

> On 2018年10月07日 23:44, Jonathan Cameron wrote:
> > On Tue, 2 Oct 2018 22:38:12 +0800
> > Song Qiang <[email protected]> wrote:
> >
> >> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> >> composed of 3 single sensors and a processing chip with a MagI2C
> >> interface.
> >>
> >> 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, depends on if
> >> the 'interrupts' in DT is declared.
> >>
> >> Signed-off-by: Song Qiang <[email protected]>
> > I realise now that I should have read the datasheet properly.
> > Sorry about that.
> >
> > What we have here is a hybrid of polled and continuous measurement.
> >
> > If you are using the dataready as a trigger it is fine to support
> > continuous measurement, but you aren't doing that here.
> >
> > The single shot measurement should be done with the method
> > described in the datasheet where you write a POLL command and then wait
> > for the single interrupt. There is no problem with racing and that
> > interrupt is a high level one and can be handled as such. We should
> > not do it by waiting for the next continuous measurement to happen
> > after clearing the status register, which is what I think is happening
> > here.
> >
> > If you want to use it in continuous mode, you should provide a trigger.
> > That trigger will be fired by the dataready signal and the
> > discussion I put in the earlier reply becomes relevant.
> >
> > Doing both of these options requires the interrupt handler to know
> > which mode you are in, but that is straight forward to implement and
> > is done in a number of other drivers.
> >
> > Sorry again that I failed to identify this issue earlier.
> >
> > Thanks to Phil as his question in the interrupt type got me thinking
> > about how you were handing the interrupts.
> >
> > Jonathan
> >
>
> Hi Jonathan,
>
> I learned the way of handling single shot from the driver of hmc5843,
> seems like it needs changing, too.
Yikes, that is indeed much the same and for no apparent reason.
If I was guessing, I would say that driver started as an input device
streaming continuously and had no 'read_raw' equivalent so they
thought, oh we will just bolt it in as somewhat of a hack rather
than thinking about power usage and speeds etc.

It should have used the single read mode on that device as well.
Now the challenge is whether anyone has one of these fairly old parts
to test any changes?

> There was some problems with my computer. Lenovo updates told me to
> update BIOS and it went dead. I didn't write any code the past few days,
> just got it fixed today.
:(

Jonathan
>
> yours,
> Song Qiang
>
> >> ---
> >> MAINTAINERS | 7 +
> >> drivers/iio/magnetometer/Kconfig | 29 ++
> >> drivers/iio/magnetometer/Makefile | 4 +
> >> drivers/iio/magnetometer/rm3100-core.c | 539 +++++++++++++++++++++++++
> >> drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
> >> drivers/iio/magnetometer/rm3100-spi.c | 64 +++
> >> drivers/iio/magnetometer/rm3100.h | 17 +
> >> 7 files changed, 718 insertions(+)
> >> 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/MAINTAINERS b/MAINTAINERS
> >> index 967ce8cdd1cc..14eeeb072403 100644
> >> --- a/MAINTAINERS
> >> +++ b/MAINTAINERS
> >> @@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
> >> S: Maintained
> >> F: drivers/pnp/
> >>
> >> +PNI RM3100 IIO DRIVER
> >> +M: Song Qiang <[email protected]>
> >> +L: [email protected]
> >> +S: Maintained
> >> +F: drivers/iio/magnetometer/rm3100*
> >> +F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> >> +
> >> POSIX CLOCKS and TIMERS
> >> M: Thomas Gleixner <[email protected]>
> >> L: [email protected]
> >> diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
> >> index ed9d776d01af..8a63cbbca4b7 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 3-Axis Magnetometer (I2C)"
> >> + depends on I2C
> >> + select SENSORS_RM3100
> >> + select REGMAP_I2C
> >> + help
> >> + Say Y here to add support for the PNI RM3100 3-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 3-Axis Magnetometer (SPI)"
> >> + depends on SPI_MASTER
> >> + select SENSORS_RM3100
> >> + select REGMAP_SPI
> >> + help
> >> + Say Y here to add support for the PNI RM3100 3-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..cacdaee28ae3
> >> --- /dev/null
> >> +++ b/drivers/iio/magnetometer/rm3100-core.c
> >> @@ -0,0 +1,539 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * PNI RM3100 3-axis geomagnetic sensor driver core.
> >> + *
> >> + * Copyright (C) 2018 Song Qiang <[email protected]>
> >> + *
> >> + * User Manual available at
> >> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> >> + *
> >> + * TODO: event generation, pm.
> >> + */
> >> +
> >> +#include <linux/delay.h>
> >> +#include <linux/interrupt.h>
> >> +#include <linux/module.h>
> >> +#include <linux/slab.h>
> >> +
> >> +#include <linux/iio/buffer.h>
> >> +#include <linux/iio/iio.h>
> >> +#include <linux/iio/kfifo_buf.h>
> >> +#include <linux/iio/sysfs.h>
> >> +#include <linux/iio/triggered_buffer.h>
> >> +#include <linux/iio/trigger_consumer.h>
> >> +
> >> +#include "rm3100.h"
> >> +
> >> +/* Cycle Count Registers. */
> >> +#define RM3100_REG_CC_X 0x05
> >> +#define RM3100_REG_CC_Y 0x07
> >> +#define RM3100_REG_CC_Z 0x09
> >> +
> >> +/* Continuous Measurement Mode register. */
> >> +#define RM3100_REG_CMM 0x01
> >> +#define RM3100_CMM_START BIT(0)
> >> +#define RM3100_CMM_X BIT(4)
> >> +#define RM3100_CMM_Y BIT(5)
> >> +#define RM3100_CMM_Z BIT(6)
> >> +
> >> +/* TiMe Rate Configuration register. */
> >> +#define RM3100_REG_TMRC 0x0B
> >> +#define RM3100_TMRC_OFFSET 0x92
> >> +
> >> +/* Result Status register. */
> >> +#define RM3100_REG_STATUS 0x34
> >> +#define RM3100_STATUS_DRDY BIT(7)
> >> +
> >> +/* Measurement result registers. */
> >> +#define RM3100_REG_MX2 0x24
> >> +#define RM3100_REG_MY2 0x27
> >> +#define RM3100_REG_MZ2 0x2a
> >> +
> >> +#define RM3100_W_REG_START RM3100_REG_CMM
> >> +#define RM3100_W_REG_END RM3100_REG_STATUS
> >> +#define RM3100_R_REG_START RM3100_REG_CMM
> >> +#define RM3100_R_REG_END RM3100_REG_STATUS
> >> +#define RM3100_V_REG_START RM3100_REG_MX2
> >> +#define RM3100_V_REG_END RM3100_REG_STATUS
> >> +
> >> +/*
> >> + * This is computed by hand, is the sum of channel storage bits and padding
> >> + * bits, which is 4+4+4+12=24 in here.
> >> + */
> >> +#define RM3100_SCAN_BYTES 24
> >> +
> >> +struct rm3100_data {
> >> + struct regmap *regmap;
> >> + struct completion measuring_done;
> >> + bool use_interrupt;
> >> +
> >> + int conversion_time;
> >> + int cycle_count_index;
> >> +
> >> + u8 buffer[RM3100_SCAN_BYTES];
> >> +
> >> + /*
> >> + * This lock is for protecting the consistency of series of i2c
> >> + * operations, that is, to make sure a measurement process will
> >> + * not be interrupted by a set frequency operation, which should
> >> + * be taken where a series of i2c operation starts, released where
> >> + * the operation ends.
> >> + */
> >> + struct mutex lock;
> >> +};
> >> +
> >> +static const struct regmap_range rm3100_readable_ranges[] = {
> >> + regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
> >> +};
> >> +
> >> +const struct regmap_access_table rm3100_readable_table = {
> >> + .yes_ranges = rm3100_readable_ranges,
> >> + .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> >> +};
> >> +EXPORT_SYMBOL_GPL(rm3100_readable_table);
> >> +
> >> +static const struct regmap_range rm3100_writable_ranges[] = {
> >> + regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
> >> +};
> >> +
> >> +const struct regmap_access_table rm3100_writable_table = {
> >> + .yes_ranges = rm3100_writable_ranges,
> >> + .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> >> +};
> >> +EXPORT_SYMBOL_GPL(rm3100_writable_table);
> >> +
> >> +static const struct regmap_range rm3100_volatile_ranges[] = {
> >> + regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
> >> +};
> >> +
> >> +const struct regmap_access_table rm3100_volatile_table = {
> >> + .yes_ranges = rm3100_volatile_ranges,
> >> + .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> >> +};
> >> +EXPORT_SYMBOL_GPL(rm3100_volatile_table);
> >> +
> >> +static irqreturn_t rm3100_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;
> >> + int tries = 20;
> >> + int ret;
> >> +
> >> + /*
> >> + * A read cycle of 400kbits i2c bus is about 20us, plus the time
> >> + * used for scheduling, a read cycle of fast mode of this device
> >> + * can reach 1.7ms, it may be possible for data to arrive just
> >> + * after we check the RM3100_REG_STATUS. In this case, irq_handler is
> >> + * called before measuring_done is reinitialized, it will wait
> >> + * forever for data that has already been ready.
> >> + * Reinitialize measuring_done before looking up makes sure we
> >> + * will always capture interrupt no matter when it happens.
> >> + */
> >> + if (data->use_interrupt)
> >> + reinit_completion(&data->measuring_done);
> >> +
> >> + ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
> >> + if (data->use_interrupt) {
> >> + ret = wait_for_completion_timeout(&data->measuring_done,
> >> + msecs_to_jiffies(data->conversion_time));
> >> + if (ret < 0)
> >> + return -ETIMEDOUT;
> >> + } else {
> >> + do {
> >> + usleep_range(1000, 5000);
> >> +
> >> + ret = regmap_read(regmap, RM3100_REG_STATUS,
> >> + &val);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + if (val & RM3100_STATUS_DRDY)
> >> + break;
> >> + } 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, RM3100_REG_MX2 + 3 * idx, buffer, 3);
> >> + mutex_unlock(&data->lock);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
> >> + 23);
> >> +
> >> + return IIO_VAL_INT;
> >> +}
> >> +
> >> +#define RM3100_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_SCALE) | \
> >> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> >> + .scan_index = idx, \
> >> + .scan_type = { \
> >> + .sign = 's', \
> >> + .realbits = 24, \
> >> + .storagebits = 32, \
> >> + .shift = 8, \
> >> + .endianness = IIO_BE, \
> >> + }, \
> >> + }
> >> +
> >> +static const struct iio_chan_spec rm3100_channels[] = {
> >> + RM3100_CHANNEL(X, 0),
> >> + RM3100_CHANNEL(Y, 1),
> >> + RM3100_CHANNEL(Z, 2),
> >> + IIO_CHAN_SOFT_TIMESTAMP(3),
> >> +};
> >> +
> >> +static const unsigned long rm3100_scan_masks[] = {BIT(0) | BIT(1) | BIT(2), 0};
> >> +
> >> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
> >> + "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
> >> +);
> >> +
> >> +static struct attribute *rm3100_attributes[] = {
> >> + &iio_const_attr_sampling_frequency_available.dev_attr.attr,
> >> + NULL,
> >> +};
> >> +
> >> +static const struct attribute_group rm3100_attribute_group = {
> >> + .attrs = rm3100_attributes,
> >> +};
> >> +
> >> +#define RM3100_SAMP_NUM 14
> >> +
> >> +/*
> >> + * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> >> + * Time between reading: rm3100_sam_rates[][2]ms.
> >> + * The first one is actually 1.7ms.
> >> + */
> >> +static const int rm3100_samp_rates[RM3100_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;
> >> + unsigned int tmp;
> >> +
> >> + mutex_lock(&data->lock);
> >> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> >> + mutex_unlock(&data->lock);
> >> + if (ret < 0)
> >> + return ret;
> >> + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
> >> + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
> >> +
> >> + return IIO_VAL_INT_PLUS_MICRO;
> >> +}
> >> +
> >> +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
> >> +{
> >> + int ret;
> >> + u8 i;
> >> +
> >> + for (i = 0; i < 3; i++) {
> >> + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
> >> + if (ret < 0)
> >> + return ret;
> >> + }
> >> +
> >> + switch (val) {
> >> + case 50:
> >> + data->cycle_count_index = 0;
> >> + break;
> >> + case 100:
> >> + data->cycle_count_index = 1;
> >> + break;
> >> + /*
> >> + * This function will never be called by users' code, so here we
> >> + * assume that it will never get a wrong parameter.
> >> + */
> >> + default:
> >> + data->cycle_count_index = 2;
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
> >> +{
> >> + struct regmap *regmap = data->regmap;
> >> + unsigned int cycle_count;
> >> + int ret;
> >> + int i;
> >> +
> >> + mutex_lock(&data->lock);
> >> + /* All cycle count registers use the same value. */
> >> + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
> >> + if (ret < 0)
> >> + goto unlock_return;
> >> +
> >> + for (i = 0; i < RM3100_SAMP_NUM; i++) {
> >> + if (val == rm3100_samp_rates[i][0] &&
> >> + val2 == rm3100_samp_rates[i][1])
> >> + break;
> >> + }
> >> + if (i == RM3100_SAMP_NUM) {
> >> + ret = -EINVAL;
> >> + goto unlock_return;
> >> + }
> >> +
> >> + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
> >> + if (ret < 0)
> >> + goto unlock_return;
> >> +
> >> + /* Checking if cycle count registers need changing. */
> >> + if (val == 600 && cycle_count == 200) {
> >> + ret = rm3100_set_cycle_count(data, 100);
> >> + if (ret < 0)
> >> + goto unlock_return;
> >> + } else if (val != 600 && cycle_count == 100) {
> >> + ret = rm3100_set_cycle_count(data, 200);
> >> + if (ret < 0)
> >> + goto unlock_return;
> >> + }
> >> +
> >> + /* Writing TMRC registers requires CMM reset. */
> >> + ret = regmap_write(regmap, RM3100_REG_CMM, 0);
> >> + if (ret < 0)
> >> + goto unlock_return;
> >> + ret = regmap_write(regmap, RM3100_REG_CMM,
> >> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> >> + if (ret < 0)
> >> + goto unlock_return;
> >> + mutex_unlock(&data->lock);
> >> +
> >> + data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> >> + return 0;
> >> +
> >> +unlock_return:
> >> + mutex_unlock(&data->lock);
> >> + return ret;
> >> +}
> >> +
> >> +/*
> >> + * The scale of this sensor depends on the cycle count value, these three
> >> + * values are corresponding to the cycle count value 50, 100, 200.
> >> + * scale = output / gain * 10^4.
> >> + */
> >> +const static int rm3100_scale[] = {500, 263, 133};
> >> +
> >> +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_SCALE:
> >> + *val = 0;
> >> + *val2 = rm3100_scale[data->cycle_count_index];
> >> +
> >> + return IIO_VAL_INT_PLUS_MICRO;
> >> + case IIO_CHAN_INFO_SAMP_FREQ:
> >> + return 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);
> >> +
> >> + switch (mask) {
> >> + case IIO_CHAN_INFO_SAMP_FREQ:
> >> + return rm3100_set_samp_freq(data, val, val2);
> >> + default:
> >> + return -EINVAL;
> >> + }
> >> +
> >> +}
> >> +
> >> +static const struct iio_info rm3100_info = {
> >> + .attrs = &rm3100_attribute_group,
> >> + .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;
> >> + int ret;
> >> + int i;
> >> +
> >> + mutex_lock(&data->lock);
> >> + ret = rm3100_wait_measurement(data);
> >> + if (ret < 0) {
> >> + mutex_unlock(&data->lock);
> >> + goto done;
> >> + }
> >> +
> >> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
> >> + mutex_unlock(&data->lock);
> >> + if (ret < 0)
> >> + goto done;
> >> +
> >> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
> >> + for (i = 0; i < 2; i++)
> >> + memcpy(data->buffer + (2 - i) * 4, data->buffer + (2 - i) * 3,
> >> + 3);
> >> +
> >> + /*
> >> + * Always using the same buffer so that we wouldn't need to set the
> >> + * paddings to 0 in case of leaking any data.
> >> + */
> >> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> >> + iio_get_time_ns(indio_dev));
> >> +
> >> +done:
> >> + iio_trigger_notify_done(indio_dev->trig);
> >> +
> >> + return IRQ_HANDLED;
> >> +}
> >> +
> >> +static void rm3100_remove(void *pdata)
> >> +{
> > Given this is no longer the opposite of probe, but rather just an
> > action to undo one particular element of probe (be it the only
> > one that needs undoing - yeah a subtle distinction ;)...
> >
> > Can we give it a more meaningful name?
> >
> > rm3100_stop perhaps? Perhaps even create a little function
> > to be rm3100_start as well so that it's easy to see how these
> > are paired?
> >
> >
> >> + struct rm3100_data *data = pdata;
> >> + struct device *dev = regmap_get_device(data->regmap);
> >> + int ret;
> >> +
> >> + ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
> >> + if (ret < 0)
> >> + dev_err(dev, "failed to stop the device.\n");
> >> +}
> >> +
> >> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> >> +{
> >> + struct iio_dev *indio_dev;
> >> + struct rm3100_data *data;
> >> + unsigned int tmp;
> >> + int ret;
> >> +
> >> + indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> >> + if (!indio_dev)
> >> + return -ENOMEM;
> >> +
> >> + data = iio_priv(indio_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_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 = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> >> + rm3100_trigger_handler, NULL);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
> >> + if (ret < 0)
> >> + return ret;
> >> + /* Initializing max wait time, 3sec more wait time for conversion. */
> >> + data->conversion_time =
> >> + rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2] + 3000;
> >> +
> >> + /* Cycle count values may not be what we want. */
> >> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> >> + if (ret < 0)
> >> + return ret;
> >> + if ((tmp - RM3100_TMRC_OFFSET) == 0)
> >> + rm3100_set_cycle_count(data, 100);
> >> + else
> >> + rm3100_set_cycle_count(data, 200);
> >> +
> >> + /* Starting all channels' conversion. */
> >> + ret = regmap_write(regmap, RM3100_REG_CMM,
> >> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);
> >> + if (ret < 0)
> >> + return ret;
> >> +
> >> + ret = devm_add_action(dev, rm3100_remove, data);
> >> + if (ret < 0) {
> >> + rm3100_remove(data);
> >> + return ret;
> >> + }
> >> +
> >> + return devm_iio_device_register(dev, indio_dev);
> >> +}
> >> +EXPORT_SYMBOL_GPL(rm3100_common_probe);
> >> +
> >> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> >> +MODULE_DESCRIPTION("PNI RM3100 3-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..8f02e0366886
> >> --- /dev/null
> >> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> >> @@ -0,0 +1,58 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
> >> + *
> >> + * Copyright (C) 2018 Song Qiang <[email protected]>
> >> + *
> >> + * i2c slave address: 0x20 + SA1 << 1 + SA0.
> >> + */
> >> +
> >> +#include <linux/i2c.h>
> >> +#include <linux/module.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 const struct of_device_id rm3100_dt_match[] = {
> >> + { .compatible = "pni,rm3100", },
> >> + { }
> >> +};
> >> +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,
> >> +};
> >> +module_i2c_driver(rm3100_driver);
> >> +
> >> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> >> +MODULE_DESCRIPTION("PNI RM3100 3-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..65d5eb9e4f5e
> >> --- /dev/null
> >> +++ b/drivers/iio/magnetometer/rm3100-spi.c
> >> @@ -0,0 +1,64 @@
> >> +// SPDX-License-Identifier: GPL-2.0
> >> +/*
> >> + * Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
> >> + *
> >> + * Copyright (C) 2018 Song Qiang <[email protected]>
> >> + */
> >> +
> >> +#include <linux/module.h>
> >> +#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
> >> + { .compatible = "pni,rm3100", },
> >> + { }
> >> +};
> >> +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,
> >> +};
> >> +module_spi_driver(rm3100_driver);
> >> +
> >> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> >> +MODULE_DESCRIPTION("PNI RM3100 3-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..c3508218bc77
> >> --- /dev/null
> >> +++ b/drivers/iio/magnetometer/rm3100.h
> >> @@ -0,0 +1,17 @@
> >> +/* SPDX-License-Identifier: GPL-2.0 */
> >> +/*
> >> + * Copyright (C) 2018 Song Qiang <[email protected]>
> >> + */
> >> +
> >> +#ifndef RM3100_CORE_H
> >> +#define RM3100_CORE_H
> >> +
> >> +#include <linux/regmap.h>
> >> +
> >> +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);
> >> +
> >> +#endif /* RM3100_CORE_H */
>


2018-10-13 10:20:42

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100

On Fri, 12 Oct 2018 15:35:36 +0800
Song Qiang <[email protected]> wrote:

> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> composed of 3 single sensors and a processing chip with a MagI2C
> interface.
>
> Following functions are available:
> - Single-shot measurement from
> /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
> - Triggerd buffer measurement.
> - DRDY pin for data ready trigger.
> - Both i2c and spi interface are supported.
> - Both interrupt and polling measurement is supported, depends on if
> the 'interrupts' in DT is declared.
>
> Signed-off-by: Song Qiang <[email protected]>

A few questions for you (getting very close to being good to go btw!)

Why do we have the 3second additional wait for conversions? I know we
rarely wait that long, but still seems excessive.

Few more comments inline.

Thanks,

Jonathan


> ---
> MAINTAINERS | 7 +
> drivers/iio/magnetometer/Kconfig | 29 ++
> drivers/iio/magnetometer/Makefile | 4 +
> drivers/iio/magnetometer/rm3100-core.c | 627 +++++++++++++++++++++++++
> drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
> drivers/iio/magnetometer/rm3100-spi.c | 64 +++
> drivers/iio/magnetometer/rm3100.h | 17 +
> 7 files changed, 806 insertions(+)
> 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/MAINTAINERS b/MAINTAINERS
> index 967ce8cdd1cc..14eeeb072403 100644
> --- a/MAINTAINERS
> +++ b/MAINTAINERS
> @@ -11393,6 +11393,13 @@ M: "Rafael J. Wysocki" <[email protected]>
> S: Maintained
> F: drivers/pnp/
>
> +PNI RM3100 IIO DRIVER
> +M: Song Qiang <[email protected]>
> +L: [email protected]
> +S: Maintained
> +F: drivers/iio/magnetometer/rm3100*
> +F: Documentation/devicetree/bindings/iio/magnetometer/pni,rm3100.txt
> +
> POSIX CLOCKS and TIMERS
> M: Thomas Gleixner <[email protected]>
> L: [email protected]
> diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
> index ed9d776d01af..8a63cbbca4b7 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 3-Axis Magnetometer (I2C)"
> + depends on I2C
> + select SENSORS_RM3100
> + select REGMAP_I2C
> + help
> + Say Y here to add support for the PNI RM3100 3-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 3-Axis Magnetometer (SPI)"
> + depends on SPI_MASTER
> + select SENSORS_RM3100
> + select REGMAP_SPI
> + help
> + Say Y here to add support for the PNI RM3100 3-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..d455982ce315
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-core.c
> @@ -0,0 +1,627 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * PNI RM3100 3-axis geomagnetic sensor driver core.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * User Manual available at
> + * <https://www.pnicorp.com/download/rm3100-user-manual/>
> + *
> + * TODO: event generation, pm.
> + */
> +
> +#include <linux/delay.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/slab.h>
> +
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/iio.h>
> +#include <linux/iio/kfifo_buf.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/trigger.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +#include "rm3100.h"
> +
> +/* Cycle Count Registers. */
> +#define RM3100_REG_CC_X 0x05
> +#define RM3100_REG_CC_Y 0x07
> +#define RM3100_REG_CC_Z 0x09
> +
> +/* Poll Measurement Mode register. */
> +#define RM3100_REG_POLL 0x00
> +#define RM3100_POLL_X BIT(4)
> +#define RM3100_POLL_Y BIT(5)
> +#define RM3100_POLL_Z BIT(6)
> +
> +/* Continuous Measurement Mode register. */
> +#define RM3100_REG_CMM 0x01
> +#define RM3100_CMM_START BIT(0)
> +#define RM3100_CMM_X BIT(4)
> +#define RM3100_CMM_Y BIT(5)
> +#define RM3100_CMM_Z BIT(6)
> +
> +/* TiMe Rate Configuration register. */
> +#define RM3100_REG_TMRC 0x0B
> +#define RM3100_TMRC_OFFSET 0x92
> +
> +/* Result Status register. */
> +#define RM3100_REG_STATUS 0x34
> +#define RM3100_STATUS_DRDY BIT(7)
> +
> +/* Measurement result registers. */
> +#define RM3100_REG_MX2 0x24
> +#define RM3100_REG_MY2 0x27
> +#define RM3100_REG_MZ2 0x2a
> +
> +#define RM3100_W_REG_START RM3100_REG_POLL
> +#define RM3100_W_REG_END RM3100_REG_TMRC
> +#define RM3100_R_REG_START RM3100_REG_POLL
> +#define RM3100_R_REG_END RM3100_REG_STATUS
> +#define RM3100_V_REG_START RM3100_REG_POLL
> +#define RM3100_V_REG_END RM3100_REG_STATUS
> +
> +/*
> + * This is computed by hand, is the sum of channel storage bits and padding
> + * bits, which is 4+4+4+12=24 in here.
> + */
> +#define RM3100_SCAN_BYTES 24
> +
> +struct rm3100_data {
> + struct regmap *regmap;
> + struct completion measuring_done;
> + bool use_interrupt;
> + int conversion_time;
> + int cycle_count_index;
> + u8 buffer[RM3100_SCAN_BYTES];
> + struct iio_trigger *drdy_trig;
> +
> + /*
> + * This lock is for protecting the consistency of series of i2c
> + * operations, that is, to make sure a measurement process will
> + * not be interrupted by a set frequency operation, which should
> + * be taken where a series of i2c operation starts, released where
> + * the operation ends.
> + */
> + struct mutex lock;
> +};
> +
> +static const struct regmap_range rm3100_readable_ranges[] = {
> + regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_readable_table = {
> + .yes_ranges = rm3100_readable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_readable_table);
> +
> +static const struct regmap_range rm3100_writable_ranges[] = {
> + regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_writable_table = {
> + .yes_ranges = rm3100_writable_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_writable_table);
> +
> +static const struct regmap_range rm3100_volatile_ranges[] = {
> + regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
> +};
> +
> +const struct regmap_access_table rm3100_volatile_table = {
> + .yes_ranges = rm3100_volatile_ranges,
> + .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
> +};
> +EXPORT_SYMBOL_GPL(rm3100_volatile_table);
> +
> +static irqreturn_t rm3100_thread_fn(int irq, void *d)
> +{
> + struct iio_dev *indio_dev = d;
> + struct rm3100_data *data = iio_priv(indio_dev);
> +
> + /*
> + * Write operation to any register or read operation
> + * to first byte of results will clear the interrupt.
> + */
> + regmap_write(data->regmap, RM3100_REG_POLL, 0);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t rm3100_irq_handler(int irq, void *d)
> +{
> + struct iio_dev *indio_dev = d;
> + struct rm3100_data *data = iio_priv(indio_dev);
> +
> + switch (indio_dev->currentmode) {
> + case INDIO_DIRECT_MODE:
> + complete(&data->measuring_done);
> + break;
> + case INDIO_BUFFER_TRIGGERED:
> + iio_trigger_poll(data->drdy_trig);
> + break;
> + /* Initializing state. */

This needs more info. I can't immediately see how we would ever get here
without a spurious interrupt.

> + case 0:
> + break;
> + default:
> + dev_err(indio_dev->dev.parent,
> + "device mode out of control, current mode: %d",
> + indio_dev->currentmode);
> + }
> +
> + return IRQ_WAKE_THREAD;
> +}
> +
> +static int rm3100_wait_measurement(struct rm3100_data *data)
> +{
> + struct regmap *regmap = data->regmap;
> + unsigned int val;
> + int tries = 20;
> + int ret;
> +
> + /*
> + * A read cycle of 400kbits i2c bus is about 20us, plus the time
> + * used for scheduling, a read cycle of fast mode of this device
> + * can reach 1.7ms, it may be possible for data to arrive just
> + * after we check the RM3100_REG_STATUS. In this case, irq_handler is
> + * called before measuring_done is reinitialized, it will wait
> + * forever for data that has already been ready.
> + * Reinitialize measuring_done before looking up makes sure we
> + * will always capture interrupt no matter when it happens.
> + */
> + if (data->use_interrupt)
> + reinit_completion(&data->measuring_done);
> +
> + ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
> + if (ret < 0)
> + return ret;
> +
> + if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
> + if (data->use_interrupt) {
> + ret = wait_for_completion_timeout(&data->measuring_done,
> + msecs_to_jiffies(data->conversion_time));
> + if (ret < 0)
> + return -ETIMEDOUT;
> + } else {
> + do {
> + usleep_range(1000, 5000);
> +
> + ret = regmap_read(regmap, RM3100_REG_STATUS,
> + &val);
> + if (ret < 0)
> + return ret;
> +
> + if (val & RM3100_STATUS_DRDY)
> + break;
> + } 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 = regmap_write(regmap, RM3100_REG_POLL, BIT(4 + idx));
> + if (ret < 0)
> + goto unlock_return;
> +
> + ret = rm3100_wait_measurement(data);
> + if (ret < 0)
> + goto unlock_return;
> +
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * idx, buffer, 3);
> + if (ret < 0)
> + goto unlock_return;
> + mutex_unlock(&data->lock);
> +
> + *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
> + 23);
> +
> + return IIO_VAL_INT;
> +
> +unlock_return:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +#define RM3100_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_SCALE) | \
> + BIT(IIO_CHAN_INFO_SAMP_FREQ), \
> + .scan_index = idx, \
> + .scan_type = { \
> + .sign = 's', \
> + .realbits = 24, \
> + .storagebits = 32, \
> + .shift = 8, \
> + .endianness = IIO_BE, \
> + }, \
> + }
> +
> +static const struct iio_chan_spec rm3100_channels[] = {
> + RM3100_CHANNEL(X, 0),
> + RM3100_CHANNEL(Y, 1),
> + RM3100_CHANNEL(Z, 2),
> + IIO_CHAN_SOFT_TIMESTAMP(3),
> +};
> +
> +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
> + "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
> +);
> +
> +static struct attribute *rm3100_attributes[] = {
> + &iio_const_attr_sampling_frequency_available.dev_attr.attr,
> + NULL,
> +};
> +
> +static const struct attribute_group rm3100_attribute_group = {
> + .attrs = rm3100_attributes,
> +};
> +
> +#define RM3100_SAMP_NUM 14
> +
> +/*
> + * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
> + * Time between reading: rm3100_sam_rates[][2]ms.
> + * The first one is actually 1.7ms.
> + */
> +static const int rm3100_samp_rates[RM3100_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;
> + unsigned int tmp;
> +
> + mutex_lock(&data->lock);
> + ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + return ret;
> + *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
> + *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> +}
> +
> +static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
> +{
> + int ret;
> + u8 i;
> +
> + for (i = 0; i < 3; i++) {
> + ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
> + if (ret < 0)
> + return ret;
> + }
> +
> + switch (val) {
> + case 50:
> + data->cycle_count_index = 0;

This feels like a level of indirection that doesn't add anything.
The index value is only used to allow us to read back the value
if read from sysfs. Might as well just cache val directly rather
than jumping backwards and forwards.

> + break;
> + case 100:
> + data->cycle_count_index = 1;
> + break;
> + /*
> + * This function will never be called by users' code, so here we
> + * assume that it will never get a wrong parameter.
> + */
> + default:
> + data->cycle_count_index = 2;
> + }
> +
> + return 0;
> +}
> +
> +static int rm3100_set_samp_freq(struct rm3100_data *data, int val, int val2)
> +{
> + struct regmap *regmap = data->regmap;
> + unsigned int cycle_count;
> + int ret;
> + int i;
> +
> + mutex_lock(&data->lock);
> + /* All cycle count registers use the same value. */
> + ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
> + if (ret < 0)
> + goto unlock_return;
> +
> + for (i = 0; i < RM3100_SAMP_NUM; i++) {
> + if (val == rm3100_samp_rates[i][0] &&
> + val2 == rm3100_samp_rates[i][1])
> + break;
> + }
> + if (i == RM3100_SAMP_NUM) {
> + ret = -EINVAL;
> + goto unlock_return;
> + }
> +
> + ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
> + if (ret < 0)
> + goto unlock_return;
> +
> + /* Checking if cycle count registers need changing. */
> + if (val == 600 && cycle_count == 200) {
> + ret = rm3100_set_cycle_count(data, 100);
> + if (ret < 0)
> + goto unlock_return;
> + } else if (val != 600 && cycle_count == 100) {
> + ret = rm3100_set_cycle_count(data, 200);
> + if (ret < 0)
> + goto unlock_return;
> + }
> +
> + /* Writing TMRC registers requires CMM reset. */
> + ret = regmap_write(regmap, RM3100_REG_CMM, 0);
> + if (ret < 0)
> + goto unlock_return;
> + ret = regmap_write(regmap, RM3100_REG_CMM,
> + RM3100_CMM_X | RM3100_CMM_Y | RM3100_CMM_Z | RM3100_CMM_START);

This seems unlikely to be correct in general. I 'think' we only want to
do restore this value to what it was previously set to rather than enable
continuous mode under all conditions.

> + if (ret < 0)
> + goto unlock_return;
> + mutex_unlock(&data->lock);
> +
> + data->conversion_time = rm3100_samp_rates[i][2] + 3000;
> + return 0;
> +
> +unlock_return:
> + mutex_unlock(&data->lock);
> + return ret;
> +}
> +
> +/*
> + * The scale of this sensor depends on the cycle count value, these three
> + * values are corresponding to the cycle count value 50, 100, 200.
> + * scale = output / gain * 10^4.
> + */
> +const static int rm3100_scale[] = {500, 263, 133};
> +
> +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_SCALE:
> + *val = 0;
> + *val2 = rm3100_scale[data->cycle_count_index];
> +
> + return IIO_VAL_INT_PLUS_MICRO;
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return 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);
> +
> + switch (mask) {
> + case IIO_CHAN_INFO_SAMP_FREQ:
> + return rm3100_set_samp_freq(data, val, val2);
> + default:
> + return -EINVAL;
> + }
> +
Nitpick of the day: Blank line not useful here so get rid of it.

> +}
> +
> +static const struct iio_info rm3100_info = {
> + .attrs = &rm3100_attribute_group,
> + .read_raw = rm3100_read_raw,
> + .write_raw = rm3100_write_raw,
> +};
> +
> +static int rm3100_buffer_preenable(struct iio_dev *iio_dev)
> +{
> + struct rm3100_data *data = iio_priv(iio_dev);
> +
> + /* Starting channels enabled. */
> + return regmap_write(data->regmap, RM3100_REG_CMM,
> + (*iio_dev->active_scan_mask & 0x7) << 4 | RM3100_CMM_START);

That shift left 4 is a magic value (as is the 0x7 mask) so probably should
be done with defines so we know they are just describing 'where' this
value goes.

> +}
> +
> +static int rm3100_buffer_postdisable(struct iio_dev *iio_dev)
> +{
> + struct rm3100_data *data = iio_priv(iio_dev);
> +
> + return regmap_write(data->regmap, RM3100_REG_CMM, 0);
> +}
> +
> +static const struct iio_buffer_setup_ops rm3100_buffer_ops = {
> + .preenable = rm3100_buffer_preenable,
> + .postenable = iio_triggered_buffer_postenable,
> + .predisable = iio_triggered_buffer_predisable,
> + .postdisable = rm3100_buffer_postdisable,
> +};
> +
> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + unsigned long scan_mask = *indio_dev->active_scan_mask;
> + unsigned int mask_len = indio_dev->masklength;
> + struct rm3100_data *data = iio_priv(indio_dev);
> + struct regmap *regmap = data->regmap;
> + int ret, i, bit;
> +
> + mutex_lock(&data->lock);
> + switch (scan_mask) {
> + case BIT(0) | BIT(1) | BIT(2):
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + goto done;
> + break;
> + case BIT(0) | BIT(1):
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + goto done;
> + break;
> + case BIT(1) | BIT(2):
> + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
> + mutex_unlock(&data->lock);
> + if (ret < 0)
> + goto done;
> + break;
What about BIT(0) | BIT(2)?

Now you can do it like you have here and on that one corner case let the iio core
demux code take care of it, but then you will need to provide available_scan_masks
so the core knows it needs to handle this case.

> + default:
> + for_each_set_bit(bit, &scan_mask, mask_len) {
> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * bit,
> + data->buffer + bit * 3, 3);
> + if (ret < 0) {
> + mutex_unlock(&data->lock);
> + goto done;
> + }
> + }
> + mutex_unlock(&data->lock);
> + }
> +
> + /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
> + for (i = mask_len - 1; i > 0; i--)
> + if (scan_mask | BIT(i))
> + memcpy(data->buffer + i * 4, data->buffer + i * 3, 3);
> +
> + /*
> + * Always using the same buffer so that we wouldn't need to set the
> + * paddings to 0 in case of leaking any data.
> + */
> + iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> + iio_get_time_ns(indio_dev));

Really minor point, but we are typically responding to a dataready trigger
here. So the better timestamp would be one grabbed in the trigger and passed
down to here.
(under the condition it's our trigger which you'll find other drivers check
carefully).

Look for drivers using the iio_pollfunc_store function for examples.

> +
> +done:
> + iio_trigger_notify_done(indio_dev->trig);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static void rm3100_remove(void *pdata)

I thought I mentioned this in the previous review. The naming
was fine when it was being called as the opposite of probe, but
now we are calling it from a devm_action it would be more appropriate
to rename this function as something like, rm3100_stop_device...

The actual function is a little odd now as it doesn't unwind
anything done in the probe but rather is a catch all for
a forced removal whilst the buffer is running. In that particular
path we should have already had the big hammer call from
iio_device_unregister (via the devm_ path) call
iio_disable_all_buffers. That should end up calling your
post_disable which will have already written this register.

So the upshot is that with the refactoring of when we enable
the device to the buffer enable path, I don't think you need
this function any more. There is nothing to clean up!

> +{
> + struct rm3100_data *data = pdata;
> + struct device *dev = regmap_get_device(data->regmap);
> + int ret;
> +
> + ret = regmap_write(data->regmap, RM3100_REG_CMM, 0x00);
> + if (ret < 0)
> + dev_err(dev, "failed to stop the device.\n");
> +}
> +
> +int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
> +{
> + struct iio_dev *indio_dev;
> + struct rm3100_data *data;
> + unsigned int tmp;
> + int ret;
> +
> + indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
> + if (!indio_dev)
> + return -ENOMEM;
> +
> + data = iio_priv(indio_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;
> +
> + if (!irq)
> + data->use_interrupt = false;
> + else {
> + data->use_interrupt = true;
> +
> + ret = devm_request_threaded_irq(dev,
> + irq,
> + rm3100_irq_handler,
> + rm3100_thread_fn,
> + IRQF_TRIGGER_HIGH |
> + IRQF_ONESHOT,
> + indio_dev->name,
> + indio_dev);
> + if (ret < 0) {
> + dev_err(dev, "request irq line failed.\n");
> + return ret;
> + }
> + init_completion(&data->measuring_done);
> +
> + data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d",
> + indio_dev->name,
> + indio_dev->id);
> + if (!data->drdy_trig)
> + return -ENOMEM;
> +
> + data->drdy_trig->dev.parent = dev;
> + ret = devm_iio_trigger_register(dev, data->drdy_trig);
> + if (ret < 0)
> + return ret;
> + }
> +
> + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
> + rm3100_trigger_handler,
> + &rm3100_buffer_ops);
> + if (ret < 0)
> + return ret;
> +
> + ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
> + if (ret < 0)
> + return ret;
> + /* Initializing max wait time, 3sec more wait time for conversion. */
> + data->conversion_time =
> + rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2] + 3000;
Yikes, somehow I'd missed this before. 3 Seconds? That's huge.
Why is this device so very slow?

> +
> + /* Cycle count values may not be what we want. */
> + if ((tmp - RM3100_TMRC_OFFSET) == 0)
> + rm3100_set_cycle_count(data, 100);
> + else
> + rm3100_set_cycle_count(data, 200);
> +
> + ret = devm_add_action(dev, rm3100_remove, data);
> + if (ret < 0) {
> + rm3100_remove(data);
> + return ret;
> + }
> +
> + return devm_iio_device_register(dev, indio_dev);
> +}
> +EXPORT_SYMBOL_GPL(rm3100_common_probe);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..8f02e0366886
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-i2c.c
> @@ -0,0 +1,58 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + *
> + * i2c slave address: 0x20 + SA1 << 1 + SA0.
> + */
> +
> +#include <linux/i2c.h>
> +#include <linux/module.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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_i2c_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..65d5eb9e4f5e
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100-spi.c
> @@ -0,0 +1,64 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
> + *
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#include <linux/module.h>
> +#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 exceed 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 const struct of_device_id rm3100_dt_match[] = {
> + { .compatible = "pni,rm3100", },
> + { }
> +};
> +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,
> +};
> +module_spi_driver(rm3100_driver);
> +
> +MODULE_AUTHOR("Song Qiang <[email protected]>");
> +MODULE_DESCRIPTION("PNI RM3100 3-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..c3508218bc77
> --- /dev/null
> +++ b/drivers/iio/magnetometer/rm3100.h
> @@ -0,0 +1,17 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2018 Song Qiang <[email protected]>
> + */
> +
> +#ifndef RM3100_CORE_H
> +#define RM3100_CORE_H
> +
> +#include <linux/regmap.h>
> +
> +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);
> +
> +#endif /* RM3100_CORE_H */


2018-10-17 08:00:46

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100


On 2018/10/12 下午8:53, Himanshu Jha wrote:
> Hi Qiang,
>
> On Fri, Oct 12, 2018 at 04:36:01PM +0800, Song Qiang wrote:
>>
>> On 2018年10月12日 15:35, Song Qiang wrote:
>>> PNI RM3100 is a high resolution, large signal immunity magnetometer,
>>> composed of 3 single sensors and a processing chip with a MagI2C
>>> interface.
>>>
>> ...
>>> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
>>> +{
>>> + struct iio_poll_func *pf = p;
>>> + struct iio_dev *indio_dev = pf->indio_dev;
>>> + unsigned long scan_mask = *indio_dev->active_scan_mask;
>>> + unsigned int mask_len = indio_dev->masklength;
>>> + struct rm3100_data *data = iio_priv(indio_dev);
>>> + struct regmap *regmap = data->regmap;
>>> + int ret, i, bit;
>>> +
>>> + mutex_lock(&data->lock);
>>> + switch (scan_mask) {
>>> + case BIT(0) | BIT(1) | BIT(2):
>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
>>> + mutex_unlock(&data->lock);
>>> + if (ret < 0)
>>> + goto done;
>>> + break;
>>> + case BIT(0) | BIT(1):
>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
>>> + mutex_unlock(&data->lock);
>>> + if (ret < 0)
>>> + goto done;
>>> + break;
>>> + case BIT(1) | BIT(2):
>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
>>> + mutex_unlock(&data->lock);
>>> + if (ret < 0)
>>> + goto done;
>>> + break;
>> Hi Jonathan,
>>
>> I just noticed that these three breaks are not proper aligned.
> Please send the new version of a patch as a *new* thread and don't
> use `--in-reply-to` flag(if you're using) to chain into older versions
> as whole thread of older discussion comes up and is often not required.
>
> The changelog gives enough info of what's new in the revised series.
>
>
Hi Himanshu,


Thanks for your advise.

I did it because the following instruction tells me to, and I think it's
also a very quick way of gathering

all scattered messages. Both ways have their own advantages and
disadvantages I think. :)

<https://kernelnewbies.org/PatchPhilosophy> Section "Updating and
resending patches".


yours,

Song Qiang


2018-10-18 08:25:06

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100


On 2018/10/13 下午6:19, Jonathan Cameron wrote:
> On Fri, 12 Oct 2018 15:35:36 +0800
> Song Qiang<[email protected]> wrote:
>
>> PNI RM3100 is a high resolution, large signal immunity magnetometer,
>> composed of 3 single sensors and a processing chip with a MagI2C
>> interface.
>>
>> Following functions are available:
>> - Single-shot measurement from
>> /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
>> - Triggerd buffer measurement.
>> - DRDY pin for data ready trigger.
>> - Both i2c and spi interface are supported.
>> - Both interrupt and polling measurement is supported, depends on if
>> the 'interrupts' in DT is declared.
>>
>> Signed-off-by: Song Qiang<[email protected]>
> A few questions for you (getting very close to being good to go btw!)
>
> Why do we have the 3second additional wait for conversions? I know we
> rarely wait that long, but still seems excessive.
>
> Few more comments inline.
>
> Thanks,
>
> Jonathan

Hi Jonathan,


The measurement time of this device varies from 1.7ms to 13 seconds, 3 seconds
is just a number in the middle between them. This may be worth discussing,
hoping to get a better solution from the community.


>> ---
>> MAINTAINERS | 7 +
>> drivers/iio/magnetometer/Kconfig | 29 ++
>> drivers/iio/magnetometer/Makefile | 4 +
>> drivers/iio/magnetometer/rm3100-core.c | 627 +++++++++++++++++++++++++
>> drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
>> drivers/iio/magnetometer/rm3100-spi.c | 64 +++
>> drivers/iio/magnetometer/rm3100.h | 17 +
>> 7 files changed, 806 insertions(+)
>> 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
>>

...

>
>> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
>> +{
>> + struct iio_poll_func *pf = p;
>> + struct iio_dev *indio_dev = pf->indio_dev;
>> + unsigned long scan_mask = *indio_dev->active_scan_mask;
>> + unsigned int mask_len = indio_dev->masklength;
>> + struct rm3100_data *data = iio_priv(indio_dev);
>> + struct regmap *regmap = data->regmap;
>> + int ret, i, bit;
>> +
>> + mutex_lock(&data->lock);
>> + switch (scan_mask) {
>> + case BIT(0) | BIT(1) | BIT(2):
>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
>> + mutex_unlock(&data->lock);
>> + if (ret < 0)
>> + goto done;
>> + break;
>> + case BIT(0) | BIT(1):
>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
>> + mutex_unlock(&data->lock);
>> + if (ret < 0)
>> + goto done;
>> + break;
>> + case BIT(1) | BIT(2):
>> + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
>> + mutex_unlock(&data->lock);
>> + if (ret < 0)
>> + goto done;
>> + break;
> What about BIT(0) | BIT(2)?
>
> Now you can do it like you have here and on that one corner case let the iio core
> demux code take care of it, but then you will need to provide available_scan_masks
> so the core knows it needs to handle this case.
>

This confused me a little. The available_scan_masks I was using is {BIT(0) |
BIT(1) | BIT(2), 0x0}. Apparently in this version of patch I would like it to
handle every circumstances like BIT(0), BIT(0) | BIT(2), BIT(1) | BIT(2), etc.
Since Phil mentioned he would like this to reduce bus usage as much as we can
and I want it, too, I think these three circumstances can be read consecutively
while others can be read one axis at a time. So I plan to let  BIT(0) | BIT(2)
fall into the 'default' section, which reads axis one by one.

My question is, since this handles every possible combination, do I still need
to list every available scan masks in available_scan_masks?


All other problems will be fixed in the next patch.

yours,

Song Qiang


...

2018-10-21 14:11:08

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100

On Wed, 17 Oct 2018 16:00:02 +0800
Song Qiang <[email protected]> wrote:

> On 2018/10/12 下午8:53, Himanshu Jha wrote:
> > Hi Qiang,
> >
> > On Fri, Oct 12, 2018 at 04:36:01PM +0800, Song Qiang wrote:
> >>
> >> On 2018年10月12日 15:35, Song Qiang wrote:
> >>> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> >>> composed of 3 single sensors and a processing chip with a MagI2C
> >>> interface.
> >>>
> >> ...
> >>> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> >>> +{
> >>> + struct iio_poll_func *pf = p;
> >>> + struct iio_dev *indio_dev = pf->indio_dev;
> >>> + unsigned long scan_mask = *indio_dev->active_scan_mask;
> >>> + unsigned int mask_len = indio_dev->masklength;
> >>> + struct rm3100_data *data = iio_priv(indio_dev);
> >>> + struct regmap *regmap = data->regmap;
> >>> + int ret, i, bit;
> >>> +
> >>> + mutex_lock(&data->lock);
> >>> + switch (scan_mask) {
> >>> + case BIT(0) | BIT(1) | BIT(2):
> >>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
> >>> + mutex_unlock(&data->lock);
> >>> + if (ret < 0)
> >>> + goto done;
> >>> + break;
> >>> + case BIT(0) | BIT(1):
> >>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
> >>> + mutex_unlock(&data->lock);
> >>> + if (ret < 0)
> >>> + goto done;
> >>> + break;
> >>> + case BIT(1) | BIT(2):
> >>> + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
> >>> + mutex_unlock(&data->lock);
> >>> + if (ret < 0)
> >>> + goto done;
> >>> + break;
> >> Hi Jonathan,
> >>
> >> I just noticed that these three breaks are not proper aligned.
> > Please send the new version of a patch as a *new* thread and don't
> > use `--in-reply-to` flag(if you're using) to chain into older versions
> > as whole thread of older discussion comes up and is often not required.
> >
> > The changelog gives enough info of what's new in the revised series.
> >
> >
> Hi Himanshu,
>
>
> Thanks for your advise.
>
> I did it because the following instruction tells me to, and I think it's
> also a very quick way of gathering
>
> all scattered messages. Both ways have their own advantages and
> disadvantages I think. :)
>
> <https://kernelnewbies.org/PatchPhilosophy> Section "Updating and
> resending patches".
That's a curious bit of advice. There are certainly a lot of maintainers
who would not want that. It never works with anything beyond trivial
and short patches (we have had patches going to v13 + and hundreds of
emails) - no email client handles that depth and complexity in
a coherent fashion. Replying to previous versions is one of those things
that makes sense until you hit the 'unusual cases' ;)

Oh well. I should probably propose a change to that Doc, but it make
take some time for me to get around to it.

Thanks,

Jonathan

>
>
> yours,
>
> Song Qiang
>


2018-10-21 14:44:42

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100

On Thu, 18 Oct 2018 16:24:15 +0800
Song Qiang <[email protected]> wrote:

> On 2018/10/13 下午6:19, Jonathan Cameron wrote:
> > On Fri, 12 Oct 2018 15:35:36 +0800
> > Song Qiang<[email protected]> wrote:
> >
> >> PNI RM3100 is a high resolution, large signal immunity magnetometer,
> >> composed of 3 single sensors and a processing chip with a MagI2C
> >> interface.
> >>
> >> Following functions are available:
> >> - Single-shot measurement from
> >> /sys/bus/iio/devices/iio:deviceX/in_magn_{axis}_raw
> >> - Triggerd buffer measurement.
> >> - DRDY pin for data ready trigger.
> >> - Both i2c and spi interface are supported.
> >> - Both interrupt and polling measurement is supported, depends on if
> >> the 'interrupts' in DT is declared.
> >>
> >> Signed-off-by: Song Qiang<[email protected]>
> > A few questions for you (getting very close to being good to go btw!)
> >
> > Why do we have the 3second additional wait for conversions? I know we
> > rarely wait that long, but still seems excessive.
> >
> > Few more comments inline.
> >
> > Thanks,
> >
> > Jonathan
>
> Hi Jonathan,
>
>
> The measurement time of this device varies from 1.7ms to 13 seconds, 3 seconds
> is just a number in the middle between them. This may be worth discussing,
> hoping to get a better solution from the community.

We should 'know' which of those it will be though as I assume it is dependent
on the device configuration which we control.

So waiting for say, double, the expected time should be sufficient to detect
that things have gone horribly wrong.

>
>
> >> ---
> >> MAINTAINERS | 7 +
> >> drivers/iio/magnetometer/Kconfig | 29 ++
> >> drivers/iio/magnetometer/Makefile | 4 +
> >> drivers/iio/magnetometer/rm3100-core.c | 627 +++++++++++++++++++++++++
> >> drivers/iio/magnetometer/rm3100-i2c.c | 58 +++
> >> drivers/iio/magnetometer/rm3100-spi.c | 64 +++
> >> drivers/iio/magnetometer/rm3100.h | 17 +
> >> 7 files changed, 806 insertions(+)
> >> 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
> >>
>
> ...
>
> >
> >> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> >> +{
> >> + struct iio_poll_func *pf = p;
> >> + struct iio_dev *indio_dev = pf->indio_dev;
> >> + unsigned long scan_mask = *indio_dev->active_scan_mask;
> >> + unsigned int mask_len = indio_dev->masklength;
> >> + struct rm3100_data *data = iio_priv(indio_dev);
> >> + struct regmap *regmap = data->regmap;
> >> + int ret, i, bit;
> >> +
> >> + mutex_lock(&data->lock);
> >> + switch (scan_mask) {
> >> + case BIT(0) | BIT(1) | BIT(2):
> >> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
> >> + mutex_unlock(&data->lock);
> >> + if (ret < 0)
> >> + goto done;
> >> + break;
> >> + case BIT(0) | BIT(1):
> >> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
> >> + mutex_unlock(&data->lock);
> >> + if (ret < 0)
> >> + goto done;
> >> + break;
> >> + case BIT(1) | BIT(2):
> >> + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
> >> + mutex_unlock(&data->lock);
> >> + if (ret < 0)
> >> + goto done;
> >> + break;
> > What about BIT(0) | BIT(2)?
> >
> > Now you can do it like you have here and on that one corner case let the iio core
> > demux code take care of it, but then you will need to provide available_scan_masks
> > so the core knows it needs to handle this case.
> >
>
> This confused me a little. The available_scan_masks I was using is {BIT(0) |
> BIT(1) | BIT(2), 0x0}. Apparently in this version of patch I would like it to
> handle every circumstances like BIT(0), BIT(0) | BIT(2), BIT(1) | BIT(2), etc.
> Since Phil mentioned he would like this to reduce bus usage as much as we can
> and I want it, too, I think these three circumstances can be read consecutively
> while others can be read one axis at a time. So I plan to let  BIT(0) | BIT(2)
> fall into the 'default' section, which reads axis one by one.
>
> My question is, since this handles every possible combination, do I still need
> to list every available scan masks in available_scan_masks?

Ah. I see, I'd missed that the default was picking up that case as well as the
single axes. It would be interesting to sanity check if it is quicker on
a 'typical' platform to do the all axis read for the BIT(0) | BIT(2) case
and drop the middle value (which would be done using available scan_masks)
or to just do two independent reads.

(I would guess it is worth reading the 'dead' axis).

>
>
> All other problems will be fixed in the next patch.
>
> yours,
>
> Song Qiang
>
>
> ...
Thanks,

Jonathan

2018-11-02 07:57:33

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100


On 2018/10/21 下午10:14, Jonathan Cameron wrote:
> On Thu, 18 Oct 2018 16:24:15 +0800
> Song Qiang <[email protected]> wrote:
>
> ...
>>>> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
>>>> +{
>>>> + struct iio_poll_func *pf = p;
>>>> + struct iio_dev *indio_dev = pf->indio_dev;
>>>> + unsigned long scan_mask = *indio_dev->active_scan_mask;
>>>> + unsigned int mask_len = indio_dev->masklength;
>>>> + struct rm3100_data *data = iio_priv(indio_dev);
>>>> + struct regmap *regmap = data->regmap;
>>>> + int ret, i, bit;
>>>> +
>>>> + mutex_lock(&data->lock);
>>>> + switch (scan_mask) {
>>>> + case BIT(0) | BIT(1) | BIT(2):
>>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
>>>> + mutex_unlock(&data->lock);
>>>> + if (ret < 0)
>>>> + goto done;
>>>> + break;
>>>> + case BIT(0) | BIT(1):
>>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
>>>> + mutex_unlock(&data->lock);
>>>> + if (ret < 0)
>>>> + goto done;
>>>> + break;
>>>> + case BIT(1) | BIT(2):
>>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
>>>> + mutex_unlock(&data->lock);
>>>> + if (ret < 0)
>>>> + goto done;
>>>> + break;
>>> What about BIT(0) | BIT(2)?
>>>
>>> Now you can do it like you have here and on that one corner case let the iio core
>>> demux code take care of it, but then you will need to provide available_scan_masks
>>> so the core knows it needs to handle this case.
>>>
>> This confused me a little. The available_scan_masks I was using is {BIT(0) |
>> BIT(1) | BIT(2), 0x0}. Apparently in this version of patch I would like it to
>> handle every circumstances like BIT(0), BIT(0) | BIT(2), BIT(1) | BIT(2), etc.
>> Since Phil mentioned he would like this to reduce bus usage as much as we can
>> and I want it, too, I think these three circumstances can be read consecutively
>> while others can be read one axis at a time. So I plan to let  BIT(0) | BIT(2)
>> fall into the 'default' section, which reads axis one by one.
>>
>> My question is, since this handles every possible combination, do I still need
>> to list every available scan masks in available_scan_masks?
> Ah. I see, I'd missed that the default was picking up that case as well as the
> single axes. It would be interesting to sanity check if it is quicker on
> a 'typical' platform to do the all axis read for the BIT(0) | BIT(2) case
> and drop the middle value (which would be done using available scan_masks)
> or to just do two independent reads.
>
> (I would guess it is worth reading the 'dead' axis).
>
>>
>> All other problems will be fixed in the next patch.
>>
>> yours,
>>
>> Song Qiang
>>
>>
>> ...
> Thanks,
>
> Jonathan

I tested this two ways of getting data with the following code snippet:


    u8 buffer[9];
    struct timeval timebefore, timeafter;

    do_gettimeofday(&timebefore);
    ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 9);
    if (ret < 0)
        goto unlock_return;
    do_gettimeofday(&timeafter);
    printk(KERN_INFO "read with dead axis time: %ld",
           timeafter.tv_sec * 1000000 + timeafter.tv_usec -
           timebefore.tv_sec * 1000000 - timebefore.tv_usec);
    do_gettimeofday(&timebefore);

    ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 3);
    if (ret < 0)
        goto unlock_return;
    ret = regmap_bulk_read(regmap, RM3100_REG_MZ2, buffer + 6, 3);
    if (ret < 0)
        goto unlock_return;
    do_gettimeofday(&timeafter);
    printk(KERN_INFO "read two single axis time: %ld",
           timeafter.tv_sec * 1000000 + timeafter.tv_usec -
           timebefore.tv_sec * 1000000 - timebefore.tv_usec);


And get this result:

[  161.264777] read with dead axis time: 883
[  161.270621] read two single axis time: 1359
[  161.575134] read with dead axis time: 852
[  161.580973] read two single axis time: 1356
[  161.895704] read with dead axis time: 854
[  161.903744] read two single axis time: 3540
[  162.223600] read with dead axis time: 853
[  162.229451] read two single axis time: 1363
[  162.591227] read with dead axis time: 850
[  162.597630] read two single axis time: 1555
[  162.920102] read with dead axis time: 852
[  162.926467] read two single axis time: 1534
[  163.303121] read with dead axis time: 881
[  163.308997] read two single axis time: 1390
[  163.711004] read with dead axis time: 861


It seems like you're right! Reading consecutively 9 bytes does save a lot time
compared to read 3 bytes twice.


2018-11-02 09:25:10

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100

On Fri, 2 Nov 2018 15:55:27 +0800
Song Qiang <[email protected]> wrote:

> On 2018/10/21 下午10:14, Jonathan Cameron wrote:
> > On Thu, 18 Oct 2018 16:24:15 +0800
> > Song Qiang <[email protected]> wrote:
> >
> > ...
> >>>> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
> >>>> +{
> >>>> + struct iio_poll_func *pf = p;
> >>>> + struct iio_dev *indio_dev = pf->indio_dev;
> >>>> + unsigned long scan_mask = *indio_dev->active_scan_mask;
> >>>> + unsigned int mask_len = indio_dev->masklength;
> >>>> + struct rm3100_data *data = iio_priv(indio_dev);
> >>>> + struct regmap *regmap = data->regmap;
> >>>> + int ret, i, bit;
> >>>> +
> >>>> + mutex_lock(&data->lock);
> >>>> + switch (scan_mask) {
> >>>> + case BIT(0) | BIT(1) | BIT(2):
> >>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
> >>>> + mutex_unlock(&data->lock);
> >>>> + if (ret < 0)
> >>>> + goto done;
> >>>> + break;
> >>>> + case BIT(0) | BIT(1):
> >>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
> >>>> + mutex_unlock(&data->lock);
> >>>> + if (ret < 0)
> >>>> + goto done;
> >>>> + break;
> >>>> + case BIT(1) | BIT(2):
> >>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
> >>>> + mutex_unlock(&data->lock);
> >>>> + if (ret < 0)
> >>>> + goto done;
> >>>> + break;
> >>> What about BIT(0) | BIT(2)?
> >>>
> >>> Now you can do it like you have here and on that one corner case let the iio core
> >>> demux code take care of it, but then you will need to provide available_scan_masks
> >>> so the core knows it needs to handle this case.
> >>>
> >> This confused me a little. The available_scan_masks I was using is {BIT(0) |
> >> BIT(1) | BIT(2), 0x0}. Apparently in this version of patch I would like it to
> >> handle every circumstances like BIT(0), BIT(0) | BIT(2), BIT(1) | BIT(2), etc.
> >> Since Phil mentioned he would like this to reduce bus usage as much as we can
> >> and I want it, too, I think these three circumstances can be read consecutively
> >> while others can be read one axis at a time. So I plan to let  BIT(0) | BIT(2)
> >> fall into the 'default' section, which reads axis one by one.
> >>
> >> My question is, since this handles every possible combination, do I still need
> >> to list every available scan masks in available_scan_masks?
> > Ah. I see, I'd missed that the default was picking up that case as well as the
> > single axes. It would be interesting to sanity check if it is quicker on
> > a 'typical' platform to do the all axis read for the BIT(0) | BIT(2) case
> > and drop the middle value (which would be done using available scan_masks)
> > or to just do two independent reads.
> >
> > (I would guess it is worth reading the 'dead' axis).
> >
> >>
> >> All other problems will be fixed in the next patch.
> >>
> >> yours,
> >>
> >> Song Qiang
> >>
> >>
> >> ...
> > Thanks,
> >
> > Jonathan
>
> I tested this two ways of getting data with the following code snippet:
>
>
>     u8 buffer[9];
>     struct timeval timebefore, timeafter;
>
>     do_gettimeofday(&timebefore);
>     ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 9);
>     if (ret < 0)
>         goto unlock_return;
>     do_gettimeofday(&timeafter);
>     printk(KERN_INFO "read with dead axis time: %ld",
>            timeafter.tv_sec * 1000000 + timeafter.tv_usec -
>            timebefore.tv_sec * 1000000 - timebefore.tv_usec);
>     do_gettimeofday(&timebefore);
>
>     ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 3);
>     if (ret < 0)
>         goto unlock_return;
>     ret = regmap_bulk_read(regmap, RM3100_REG_MZ2, buffer + 6, 3);
>     if (ret < 0)
>         goto unlock_return;
>     do_gettimeofday(&timeafter);
>     printk(KERN_INFO "read two single axis time: %ld",
>            timeafter.tv_sec * 1000000 + timeafter.tv_usec -
>            timebefore.tv_sec * 1000000 - timebefore.tv_usec);
>
>
> And get this result:
>
> [  161.264777] read with dead axis time: 883
> [  161.270621] read two single axis time: 1359
> [  161.575134] read with dead axis time: 852
> [  161.580973] read two single axis time: 1356
> [  161.895704] read with dead axis time: 854
> [  161.903744] read two single axis time: 3540
> [  162.223600] read with dead axis time: 853
> [  162.229451] read two single axis time: 1363
> [  162.591227] read with dead axis time: 850
> [  162.597630] read two single axis time: 1555
> [  162.920102] read with dead axis time: 852
> [  162.926467] read two single axis time: 1534
> [  163.303121] read with dead axis time: 881
> [  163.308997] read two single axis time: 1390
> [  163.711004] read with dead axis time: 861
>
>
> It seems like you're right! Reading consecutively 9 bytes does save a lot time
> compared to read 3 bytes twice.
>
I've done this stuff before ;) We had this on the adis16365 parts back
in the early days of IIO. A worse case as it has a lot more channels,
but otherwise similar from what I recall.

It would be an interesting exercise to trace those paths and find out the
balance between real hardware stuff we can't change and potential software
overheads.

Chances are this is mostly 'real' stuff though but would be great to
confirm this. It's been on my list of things to do for years (not on
this driver obviously but in general)...

Jonathan


2018-11-05 00:55:42

by Song Qiang

[permalink] [raw]
Subject: Re: [PATCH v4 3/3] iio: magnetometer: Add driver support for PNI RM3100


On 2018/11/2 下午5:24, Jonathan Cameron wrote:
> On Fri, 2 Nov 2018 15:55:27 +0800
> Song Qiang <[email protected]> wrote:
>
>> On 2018/10/21 下午10:14, Jonathan Cameron wrote:
>>> On Thu, 18 Oct 2018 16:24:15 +0800
>>> Song Qiang <[email protected]> wrote:
>>>
>>> ...
>>>>>> +static irqreturn_t rm3100_trigger_handler(int irq, void *p)
>>>>>> +{
>>>>>> + struct iio_poll_func *pf = p;
>>>>>> + struct iio_dev *indio_dev = pf->indio_dev;
>>>>>> + unsigned long scan_mask = *indio_dev->active_scan_mask;
>>>>>> + unsigned int mask_len = indio_dev->masklength;
>>>>>> + struct rm3100_data *data = iio_priv(indio_dev);
>>>>>> + struct regmap *regmap = data->regmap;
>>>>>> + int ret, i, bit;
>>>>>> +
>>>>>> + mutex_lock(&data->lock);
>>>>>> + switch (scan_mask) {
>>>>>> + case BIT(0) | BIT(1) | BIT(2):
>>>>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
>>>>>> + mutex_unlock(&data->lock);
>>>>>> + if (ret < 0)
>>>>>> + goto done;
>>>>>> + break;
>>>>>> + case BIT(0) | BIT(1):
>>>>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
>>>>>> + mutex_unlock(&data->lock);
>>>>>> + if (ret < 0)
>>>>>> + goto done;
>>>>>> + break;
>>>>>> + case BIT(1) | BIT(2):
>>>>>> + ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
>>>>>> + mutex_unlock(&data->lock);
>>>>>> + if (ret < 0)
>>>>>> + goto done;
>>>>>> + break;
>>>>> What about BIT(0) | BIT(2)?
>>>>>
>>>>> Now you can do it like you have here and on that one corner case let the iio core
>>>>> demux code take care of it, but then you will need to provide available_scan_masks
>>>>> so the core knows it needs to handle this case.
>>>>>
>>>> This confused me a little. The available_scan_masks I was using is {BIT(0) |
>>>> BIT(1) | BIT(2), 0x0}. Apparently in this version of patch I would like it to
>>>> handle every circumstances like BIT(0), BIT(0) | BIT(2), BIT(1) | BIT(2), etc.
>>>> Since Phil mentioned he would like this to reduce bus usage as much as we can
>>>> and I want it, too, I think these three circumstances can be read consecutively
>>>> while others can be read one axis at a time. So I plan to let  BIT(0) | BIT(2)
>>>> fall into the 'default' section, which reads axis one by one.
>>>>
>>>> My question is, since this handles every possible combination, do I still need
>>>> to list every available scan masks in available_scan_masks?
>>> Ah. I see, I'd missed that the default was picking up that case as well as the
>>> single axes. It would be interesting to sanity check if it is quicker on
>>> a 'typical' platform to do the all axis read for the BIT(0) | BIT(2) case
>>> and drop the middle value (which would be done using available scan_masks)
>>> or to just do two independent reads.
>>>
>>> (I would guess it is worth reading the 'dead' axis).
>>>
>>>> All other problems will be fixed in the next patch.
>>>>
>>>> yours,
>>>>
>>>> Song Qiang
>>>>
>>>>
>>>> ...
>>> Thanks,
>>>
>>> Jonathan
>> I tested this two ways of getting data with the following code snippet:
>>
>>
>>     u8 buffer[9];
>>     struct timeval timebefore, timeafter;
>>
>>     do_gettimeofday(&timebefore);
>>     ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 9);
>>     if (ret < 0)
>>         goto unlock_return;
>>     do_gettimeofday(&timeafter);
>>     printk(KERN_INFO "read with dead axis time: %ld",
>>            timeafter.tv_sec * 1000000 + timeafter.tv_usec -
>>            timebefore.tv_sec * 1000000 - timebefore.tv_usec);
>>     do_gettimeofday(&timebefore);
>>
>>     ret = regmap_bulk_read(regmap, RM3100_REG_MX2, buffer, 3);
>>     if (ret < 0)
>>         goto unlock_return;
>>     ret = regmap_bulk_read(regmap, RM3100_REG_MZ2, buffer + 6, 3);
>>     if (ret < 0)
>>         goto unlock_return;
>>     do_gettimeofday(&timeafter);
>>     printk(KERN_INFO "read two single axis time: %ld",
>>            timeafter.tv_sec * 1000000 + timeafter.tv_usec -
>>            timebefore.tv_sec * 1000000 - timebefore.tv_usec);
>>
>>
>> And get this result:
>>
>> [  161.264777] read with dead axis time: 883
>> [  161.270621] read two single axis time: 1359
>> [  161.575134] read with dead axis time: 852
>> [  161.580973] read two single axis time: 1356
>> [  161.895704] read with dead axis time: 854
>> [  161.903744] read two single axis time: 3540
>> [  162.223600] read with dead axis time: 853
>> [  162.229451] read two single axis time: 1363
>> [  162.591227] read with dead axis time: 850
>> [  162.597630] read two single axis time: 1555
>> [  162.920102] read with dead axis time: 852
>> [  162.926467] read two single axis time: 1534
>> [  163.303121] read with dead axis time: 881
>> [  163.308997] read two single axis time: 1390
>> [  163.711004] read with dead axis time: 861
>>
>>
>> It seems like you're right! Reading consecutively 9 bytes does save a lot time
>> compared to read 3 bytes twice.
>>
> I've done this stuff before ;) We had this on the adis16365 parts back
> in the early days of IIO. A worse case as it has a lot more channels,
> but otherwise similar from what I recall.
>
> It would be an interesting exercise to trace those paths and find out the
> balance between real hardware stuff we can't change and potential software
> overheads.
>
> Chances are this is mostly 'real' stuff though but would be great to
> confirm this. It's been on my list of things to do for years (not on
> this driver obviously but in general)...
>
> Jonathan
>
I think I can try to use ftrace to trace it's flow path on my platform. I don't
familiar with it, may need some time.

yours,

Song Qiang