summaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/accel/Kconfig56
-rw-r--r--drivers/iio/accel/Makefile5
-rw-r--r--drivers/iio/accel/bma180.c9
-rw-r--r--drivers/iio/accel/dmard06.c241
-rw-r--r--drivers/iio/accel/dmard09.c157
-rw-r--r--drivers/iio/accel/kxcjk-1013.c1
-rw-r--r--drivers/iio/accel/kxsd9-i2c.c64
-rw-r--r--drivers/iio/accel/kxsd9-spi.c56
-rw-r--r--drivers/iio/accel/kxsd9.c435
-rw-r--r--drivers/iio/accel/kxsd9.h12
-rw-r--r--drivers/iio/accel/mc3230.c211
-rw-r--r--drivers/iio/accel/mma7660.c1
-rw-r--r--drivers/iio/accel/mxc6255.c4
-rw-r--r--drivers/iio/accel/ssp_accel_sensor.c2
-rw-r--r--drivers/iio/adc/Kconfig61
-rw-r--r--drivers/iio/adc/Makefile5
-rw-r--r--drivers/iio/adc/ad7266.c4
-rw-r--r--drivers/iio/adc/ad7298.c20
-rw-r--r--drivers/iio/adc/ad7793.c10
-rw-r--r--drivers/iio/adc/at91_adc.c13
-rw-r--r--drivers/iio/adc/ina2xx-adc.c5
-rw-r--r--drivers/iio/adc/ltc2485.c148
-rw-r--r--drivers/iio/adc/men_z188_adc.c2
-rw-r--r--drivers/iio/adc/mt6577_auxadc.c291
-rw-r--r--drivers/iio/adc/nau7802.c2
-rw-r--r--drivers/iio/adc/stx104.c (renamed from drivers/iio/dac/stx104.c)153
-rw-r--r--drivers/iio/adc/ti-adc12138.c552
-rw-r--r--drivers/iio/adc/ti-adc161s626.c248
-rw-r--r--drivers/iio/adc/ti-ads1015.c2
-rw-r--r--drivers/iio/adc/ti-ads8688.c4
-rw-r--r--drivers/iio/buffer/industrialio-buffer-cb.c24
-rw-r--r--drivers/iio/buffer/industrialio-triggered-buffer.c42
-rw-r--r--drivers/iio/chemical/Kconfig1
-rw-r--r--drivers/iio/chemical/atlas-ph-sensor.c81
-rw-r--r--drivers/iio/chemical/vz89x.c201
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-trigger.c29
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_buffer.c2
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c53
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_trigger.c4
-rw-r--r--drivers/iio/dac/Kconfig29
-rw-r--r--drivers/iio/dac/Makefile3
-rw-r--r--drivers/iio/dac/ad5755.c2
-rw-r--r--drivers/iio/dac/ad8801.c239
-rw-r--r--drivers/iio/dac/cio-dac.c144
-rw-r--r--drivers/iio/gyro/ssp_gyro_sensor.c2
-rw-r--r--drivers/iio/humidity/Kconfig8
-rw-r--r--drivers/iio/industrialio-core.c3
-rw-r--r--drivers/iio/industrialio-event.c8
-rw-r--r--drivers/iio/industrialio-trigger.c95
-rw-r--r--drivers/iio/light/Kconfig19
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/si1145.c1404
-rw-r--r--drivers/iio/light/us5182d.c2
-rw-r--r--drivers/iio/light/vcnl4000.c72
-rw-r--r--drivers/iio/magnetometer/Kconfig16
-rw-r--r--drivers/iio/magnetometer/Makefile1
-rw-r--r--drivers/iio/magnetometer/ak8974.c860
-rw-r--r--drivers/iio/magnetometer/mag3110.c23
-rw-r--r--drivers/iio/pressure/Kconfig22
-rw-r--r--drivers/iio/pressure/Makefile3
-rw-r--r--drivers/iio/pressure/ms5611_core.c6
-rw-r--r--drivers/iio/pressure/zpa2326.c1735
-rw-r--r--drivers/iio/pressure/zpa2326.h89
-rw-r--r--drivers/iio/pressure/zpa2326_i2c.c99
-rw-r--r--drivers/iio/pressure/zpa2326_spi.c103
-rw-r--r--drivers/iio/proximity/sx9500.c9
-rw-r--r--drivers/iio/temperature/Kconfig16
-rw-r--r--drivers/iio/temperature/Makefile1
-rw-r--r--drivers/iio/temperature/maxim_thermocouple.c281
69 files changed, 8182 insertions, 324 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 78f148ea9d9f..2b791fe1e2bc 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -52,6 +52,27 @@ config BMC150_ACCEL_SPI
tristate
select REGMAP_SPI
+config DMARD06
+ tristate "Domintech DMARD06 Digital Accelerometer Driver"
+ depends on OF || COMPILE_TEST
+ depends on I2C
+ help
+ Say yes here to build support for the Domintech low-g tri-axial
+ digital accelerometers: DMARD05, DMARD06, DMARD07.
+
+ To compile this driver as a module, choose M here: the
+ module will be called dmard06.
+
+config DMARD09
+ tristate "Domintech DMARD09 3-axis Accelerometer Driver"
+ depends on I2C
+ help
+ Say yes here to get support for the Domintech DMARD09 3-axis
+ accelerometer.
+
+ Choosing M will build the driver as a module. If so, the module
+ will be called dmard09.
+
config HID_SENSOR_ACCEL_3D
depends on HID_SENSOR_HUB
select IIO_BUFFER
@@ -98,14 +119,35 @@ config IIO_ST_ACCEL_SPI_3AXIS
config KXSD9
tristate "Kionix KXSD9 Accelerometer Driver"
- depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the Kionix KXSD9 accelerometer.
- Currently this only supports the device via an SPI interface.
+ It can be accessed using an (optional) SPI or I2C interface.
To compile this driver as a module, choose M here: the module
will be called kxsd9.
+config KXSD9_SPI
+ tristate "Kionix KXSD9 SPI transport"
+ depends on KXSD9
+ depends on SPI
+ default KXSD9
+ select REGMAP_SPI
+ help
+ Say yes here to enable the Kionix KXSD9 accelerometer
+ SPI transport channel.
+
+config KXSD9_I2C
+ tristate "Kionix KXSD9 I2C transport"
+ depends on KXSD9
+ depends on I2C
+ default KXSD9
+ select REGMAP_I2C
+ help
+ Say yes here to enable the Kionix KXSD9 accelerometer
+ I2C transport channel.
+
config KXCJK1013
tristate "Kionix 3-Axis Accelerometer Driver"
depends on I2C
@@ -119,6 +161,16 @@ config KXCJK1013
To compile this driver as a module, choose M here: the module will
be called kxcjk-1013.
+config MC3230
+ tristate "mCube MC3230 Digital Accelerometer Driver"
+ depends on I2C
+ help
+ Say yes here to build support for the mCube MC3230 low-g tri-axial
+ digital accelerometer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mc3230.
+
config MMA7455
tristate
select IIO_BUFFER
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 6cedbecca2ee..f5d3ddee619e 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -8,9 +8,14 @@ obj-$(CONFIG_BMA220) += bma220_spi.o
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o
obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o
+obj-$(CONFIG_DMARD06) += dmard06.o
+obj-$(CONFIG_DMARD09) += dmard09.o
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
+obj-$(CONFIG_KXSD9_SPI) += kxsd9-spi.o
+obj-$(CONFIG_KXSD9_I2C) += kxsd9-i2c.o
+obj-$(CONFIG_MC3230) += mc3230.o
obj-$(CONFIG_MMA7455) += mma7455_core.o
obj-$(CONFIG_MMA7455_I2C) += mma7455_i2c.o
diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c
index e3f88ba5faf3..0890934ef66f 100644
--- a/drivers/iio/accel/bma180.c
+++ b/drivers/iio/accel/bma180.c
@@ -469,13 +469,14 @@ static int bma180_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
mutex_lock(&data->mutex);
- if (iio_buffer_enabled(indio_dev)) {
- mutex_unlock(&data->mutex);
- return -EBUSY;
- }
ret = bma180_get_data_reg(data, chan->scan_index);
mutex_unlock(&data->mutex);
+ iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
*val = sign_extend32(ret >> chan->scan_type.shift,
diff --git a/drivers/iio/accel/dmard06.c b/drivers/iio/accel/dmard06.c
new file mode 100644
index 000000000000..656ca8e1927f
--- /dev/null
+++ b/drivers/iio/accel/dmard06.c
@@ -0,0 +1,241 @@
+/*
+ * IIO driver for Domintech DMARD06 accelerometer
+ *
+ * Copyright (C) 2016 Aleksei Mamlin <mamlinav@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#define DMARD06_DRV_NAME "dmard06"
+
+/* Device data registers */
+#define DMARD06_CHIP_ID_REG 0x0f
+#define DMARD06_TOUT_REG 0x40
+#define DMARD06_XOUT_REG 0x41
+#define DMARD06_YOUT_REG 0x42
+#define DMARD06_ZOUT_REG 0x43
+#define DMARD06_CTRL1_REG 0x44
+
+/* Device ID value */
+#define DMARD05_CHIP_ID 0x05
+#define DMARD06_CHIP_ID 0x06
+#define DMARD07_CHIP_ID 0x07
+
+/* Device values */
+#define DMARD05_AXIS_SCALE_VAL 15625
+#define DMARD06_AXIS_SCALE_VAL 31250
+#define DMARD06_TEMP_CENTER_VAL 25
+#define DMARD06_SIGN_BIT 7
+
+/* Device power modes */
+#define DMARD06_MODE_NORMAL 0x27
+#define DMARD06_MODE_POWERDOWN 0x00
+
+/* Device channels */
+#define DMARD06_ACCEL_CHANNEL(_axis, _reg) { \
+ .type = IIO_ACCEL, \
+ .address = _reg, \
+ .channel2 = IIO_MOD_##_axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .modified = 1, \
+}
+
+#define DMARD06_TEMP_CHANNEL(_reg) { \
+ .type = IIO_TEMP, \
+ .address = _reg, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
+struct dmard06_data {
+ struct i2c_client *client;
+ u8 chip_id;
+};
+
+static const struct iio_chan_spec dmard06_channels[] = {
+ DMARD06_ACCEL_CHANNEL(X, DMARD06_XOUT_REG),
+ DMARD06_ACCEL_CHANNEL(Y, DMARD06_YOUT_REG),
+ DMARD06_ACCEL_CHANNEL(Z, DMARD06_ZOUT_REG),
+ DMARD06_TEMP_CHANNEL(DMARD06_TOUT_REG),
+};
+
+static int dmard06_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct dmard06_data *dmard06 = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_smbus_read_byte_data(dmard06->client,
+ chan->address);
+ if (ret < 0) {
+ dev_err(&dmard06->client->dev,
+ "Error reading data: %d\n", ret);
+ return ret;
+ }
+
+ *val = sign_extend32(ret, DMARD06_SIGN_BIT);
+
+ if (dmard06->chip_id == DMARD06_CHIP_ID)
+ *val = *val >> 1;
+
+ switch (chan->type) {
+ case IIO_ACCEL:
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ if (dmard06->chip_id != DMARD06_CHIP_ID)
+ *val = *val / 2;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = DMARD06_TEMP_CENTER_VAL;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ACCEL:
+ *val = 0;
+ if (dmard06->chip_id == DMARD06_CHIP_ID)
+ *val2 = DMARD06_AXIS_SCALE_VAL;
+ else
+ *val2 = DMARD05_AXIS_SCALE_VAL;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info dmard06_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = dmard06_read_raw,
+};
+
+static int dmard06_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct dmard06_data *dmard06;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "I2C check functionality failed\n");
+ return -ENXIO;
+ }
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dmard06));
+ if (!indio_dev) {
+ dev_err(&client->dev, "Failed to allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ dmard06 = iio_priv(indio_dev);
+ dmard06->client = client;
+
+ ret = i2c_smbus_read_byte_data(dmard06->client, DMARD06_CHIP_ID_REG);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error reading chip id: %d\n", ret);
+ return ret;
+ }
+
+ if (ret != DMARD05_CHIP_ID && ret != DMARD06_CHIP_ID &&
+ ret != DMARD07_CHIP_ID) {
+ dev_err(&client->dev, "Invalid chip id: %02d\n", ret);
+ return -ENODEV;
+ }
+
+ dmard06->chip_id = ret;
+
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = DMARD06_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = dmard06_channels;
+ indio_dev->num_channels = ARRAY_SIZE(dmard06_channels);
+ indio_dev->info = &dmard06_info;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dmard06_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct dmard06_data *dmard06 = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
+ DMARD06_MODE_POWERDOWN);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int dmard06_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct dmard06_data *dmard06 = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(dmard06->client, DMARD06_CTRL1_REG,
+ DMARD06_MODE_NORMAL);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(dmard06_pm_ops, dmard06_suspend, dmard06_resume);
+#define DMARD06_PM_OPS (&dmard06_pm_ops)
+#else
+#define DMARD06_PM_OPS NULL
+#endif
+
+static const struct i2c_device_id dmard06_id[] = {
+ { "dmard05", 0 },
+ { "dmard06", 0 },
+ { "dmard07", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, dmard06_id);
+
+static const struct of_device_id dmard06_of_match[] = {
+ { .compatible = "domintech,dmard05" },
+ { .compatible = "domintech,dmard06" },
+ { .compatible = "domintech,dmard07" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, dmard06_of_match);
+
+static struct i2c_driver dmard06_driver = {
+ .probe = dmard06_probe,
+ .id_table = dmard06_id,
+ .driver = {
+ .name = DMARD06_DRV_NAME,
+ .of_match_table = of_match_ptr(dmard06_of_match),
+ .pm = DMARD06_PM_OPS,
+ },
+};
+module_i2c_driver(dmard06_driver);
+
+MODULE_AUTHOR("Aleksei Mamlin <mamlinav@gmail.com>");
+MODULE_DESCRIPTION("Domintech DMARD06 accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c
new file mode 100644
index 000000000000..d3a28f96565c
--- /dev/null
+++ b/drivers/iio/accel/dmard09.c
@@ -0,0 +1,157 @@
+/*
+ * IIO driver for the 3-axis accelerometer Domintech DMARD09.
+ *
+ * Copyright (c) 2016, Jelle van der Waa <jelle@vdwaa.nl>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <asm/unaligned.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#define DMARD09_DRV_NAME "dmard09"
+
+#define DMARD09_REG_CHIPID 0x18
+#define DMARD09_REG_STAT 0x0A
+#define DMARD09_REG_X 0x0C
+#define DMARD09_REG_Y 0x0E
+#define DMARD09_REG_Z 0x10
+#define DMARD09_CHIPID 0x95
+
+#define DMARD09_BUF_LEN 8
+#define DMARD09_AXIS_X 0
+#define DMARD09_AXIS_Y 1
+#define DMARD09_AXIS_Z 2
+#define DMARD09_AXIS_X_OFFSET ((DMARD09_AXIS_X + 1) * 2)
+#define DMARD09_AXIS_Y_OFFSET ((DMARD09_AXIS_Y + 1 )* 2)
+#define DMARD09_AXIS_Z_OFFSET ((DMARD09_AXIS_Z + 1) * 2)
+
+struct dmard09_data {
+ struct i2c_client *client;
+};
+
+#define DMARD09_CHANNEL(_axis, offset) { \
+ .type = IIO_ACCEL, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .modified = 1, \
+ .address = offset, \
+ .channel2 = IIO_MOD_##_axis, \
+}
+
+static const struct iio_chan_spec dmard09_channels[] = {
+ DMARD09_CHANNEL(X, DMARD09_AXIS_X_OFFSET),
+ DMARD09_CHANNEL(Y, DMARD09_AXIS_Y_OFFSET),
+ DMARD09_CHANNEL(Z, DMARD09_AXIS_Z_OFFSET),
+};
+
+static int dmard09_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct dmard09_data *data = iio_priv(indio_dev);
+ u8 buf[DMARD09_BUF_LEN];
+ int ret;
+ s16 accel;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * Read from the DMAR09_REG_STAT register, since the chip
+ * caches reads from the individual X, Y, Z registers.
+ */
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ DMARD09_REG_STAT,
+ DMARD09_BUF_LEN, buf);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Error reading reg %d\n",
+ DMARD09_REG_STAT);
+ return ret;
+ }
+
+ accel = get_unaligned_le16(&buf[chan->address]);
+
+ /* Remove lower 3 bits and sign extend */
+ accel <<= 4;
+ accel >>= 7;
+
+ *val = accel;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info dmard09_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = dmard09_read_raw,
+};
+
+static int dmard09_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct dmard09_data *data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio allocation failed\n");
+ return -ENOMEM;
+ }
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+
+ ret = i2c_smbus_read_byte_data(data->client, DMARD09_REG_CHIPID);
+ if (ret < 0) {
+ dev_err(&client->dev, "Error reading chip id %d\n", ret);
+ return ret;
+ }
+
+ if (ret != DMARD09_CHIPID) {
+ dev_err(&client->dev, "Invalid chip id %d\n", ret);
+ return -ENODEV;
+ }
+
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = DMARD09_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = dmard09_channels;
+ indio_dev->num_channels = ARRAY_SIZE(dmard09_channels);
+ indio_dev->info = &dmard09_info;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id dmard09_id[] = {
+ { "dmard09", 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, dmard09_id);
+
+static struct i2c_driver dmard09_driver = {
+ .driver = {
+ .name = DMARD09_DRV_NAME
+ },
+ .probe = dmard09_probe,
+ .id_table = dmard09_id,
+};
+
+module_i2c_driver(dmard09_driver);
+
+MODULE_AUTHOR("Jelle van der Waa <jelle@vdwaa.nl>");
+MODULE_DESCRIPTION("DMARD09 3-axis accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index 765a72362dc6..3f968c46e667 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -1392,6 +1392,7 @@ static const struct acpi_device_id kx_acpi_match[] = {
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXCJ9000", KXCJ91008},
+ {"KIOX000A", KXCJ91008},
{"KXTJ1009", KXTJ21009},
{"SMO8500", KXCJ91008},
{ },
diff --git a/drivers/iio/accel/kxsd9-i2c.c b/drivers/iio/accel/kxsd9-i2c.c
new file mode 100644
index 000000000000..95e20855d2ef
--- /dev/null
+++ b/drivers/iio/accel/kxsd9-i2c.c
@@ -0,0 +1,64 @@
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+
+#include "kxsd9.h"
+
+static int kxsd9_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ static const struct regmap_config config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x0e,
+ };
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &config);
+ if (IS_ERR(regmap)) {
+ dev_err(&i2c->dev, "Failed to register i2c regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ return kxsd9_common_probe(&i2c->dev,
+ regmap,
+ i2c->name);
+}
+
+static int kxsd9_i2c_remove(struct i2c_client *client)
+{
+ return kxsd9_common_remove(&client->dev);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id kxsd9_of_match[] = {
+ { .compatible = "kionix,kxsd9", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, kxsd9_of_match);
+#else
+#define kxsd9_of_match NULL
+#endif
+
+static const struct i2c_device_id kxsd9_i2c_id[] = {
+ {"kxsd9", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, kxsd9_i2c_id);
+
+static struct i2c_driver kxsd9_i2c_driver = {
+ .driver = {
+ .name = "kxsd9",
+ .of_match_table = of_match_ptr(kxsd9_of_match),
+ .pm = &kxsd9_dev_pm_ops,
+ },
+ .probe = kxsd9_i2c_probe,
+ .remove = kxsd9_i2c_remove,
+ .id_table = kxsd9_i2c_id,
+};
+module_i2c_driver(kxsd9_i2c_driver);
diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c
new file mode 100644
index 000000000000..b7d0078fd00e
--- /dev/null
+++ b/drivers/iio/accel/kxsd9-spi.c
@@ -0,0 +1,56 @@
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/regmap.h>
+
+#include "kxsd9.h"
+
+static int kxsd9_spi_probe(struct spi_device *spi)
+{
+ static const struct regmap_config config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0x0e,
+ };
+ struct regmap *regmap;
+
+ spi->mode = SPI_MODE_0;
+ regmap = devm_regmap_init_spi(spi, &config);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "%s: regmap allocation failed: %ld\n",
+ __func__, PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ return kxsd9_common_probe(&spi->dev,
+ regmap,
+ spi_get_device_id(spi)->name);
+}
+
+static int kxsd9_spi_remove(struct spi_device *spi)
+{
+ return kxsd9_common_remove(&spi->dev);
+}
+
+static const struct spi_device_id kxsd9_spi_id[] = {
+ {"kxsd9", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(spi, kxsd9_spi_id);
+
+static struct spi_driver kxsd9_spi_driver = {
+ .driver = {
+ .name = "kxsd9",
+ .pm = &kxsd9_dev_pm_ops,
+ },
+ .probe = kxsd9_spi_probe,
+ .remove = kxsd9_spi_remove,
+ .id_table = kxsd9_spi_id,
+};
+module_spi_driver(kxsd9_spi_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
index 9d72d4bcf5e9..9af60ac70738 100644
--- a/drivers/iio/accel/kxsd9.c
+++ b/drivers/iio/accel/kxsd9.c
@@ -12,19 +12,25 @@
* I have a suitable wire made up.
*
* TODO: Support the motion detector
- * Uses register address incrementing so could have a
- * heavily optimized ring buffer access function.
*/
#include <linux/device.h>
#include <linux/kernel.h>
-#include <linux/spi/spi.h>
#include <linux/sysfs.h>
#include <linux/slab.h>
#include <linux/module.h>
-
+#include <linux/regmap.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include "kxsd9.h"
#define KXSD9_REG_X 0x00
#define KXSD9_REG_Y 0x02
@@ -33,28 +39,45 @@
#define KXSD9_REG_RESET 0x0a
#define KXSD9_REG_CTRL_C 0x0c
-#define KXSD9_FS_MASK 0x03
+#define KXSD9_CTRL_C_FS_MASK 0x03
+#define KXSD9_CTRL_C_FS_8G 0x00
+#define KXSD9_CTRL_C_FS_6G 0x01
+#define KXSD9_CTRL_C_FS_4G 0x02
+#define KXSD9_CTRL_C_FS_2G 0x03
+#define KXSD9_CTRL_C_MOT_LAT BIT(3)
+#define KXSD9_CTRL_C_MOT_LEV BIT(4)
+#define KXSD9_CTRL_C_LP_MASK 0xe0
+#define KXSD9_CTRL_C_LP_NONE 0x00
+#define KXSD9_CTRL_C_LP_2000HZC BIT(5)
+#define KXSD9_CTRL_C_LP_2000HZB BIT(6)
+#define KXSD9_CTRL_C_LP_2000HZA (BIT(5)|BIT(6))
+#define KXSD9_CTRL_C_LP_1000HZ BIT(7)
+#define KXSD9_CTRL_C_LP_500HZ (BIT(7)|BIT(5))
+#define KXSD9_CTRL_C_LP_100HZ (BIT(7)|BIT(6))
+#define KXSD9_CTRL_C_LP_50HZ (BIT(7)|BIT(6)|BIT(5))
#define KXSD9_REG_CTRL_B 0x0d
-#define KXSD9_REG_CTRL_A 0x0e
-#define KXSD9_READ(a) (0x80 | (a))
-#define KXSD9_WRITE(a) (a)
+#define KXSD9_CTRL_B_CLK_HLD BIT(7)
+#define KXSD9_CTRL_B_ENABLE BIT(6)
+#define KXSD9_CTRL_B_ST BIT(5) /* Self-test */
+
+#define KXSD9_REG_CTRL_A 0x0e
-#define KXSD9_STATE_RX_SIZE 2
-#define KXSD9_STATE_TX_SIZE 2
/**
* struct kxsd9_state - device related storage
- * @buf_lock: protect the rx and tx buffers.
- * @us: spi device
- * @rx: single rx buffer storage
- * @tx: single tx buffer storage
- **/
+ * @dev: pointer to the parent device
+ * @map: regmap to the device
+ * @orientation: mounting matrix, flipped axis etc
+ * @regs: regulators for this device, VDD and IOVDD
+ * @scale: the current scaling setting
+ */
struct kxsd9_state {
- struct mutex buf_lock;
- struct spi_device *us;
- u8 rx[KXSD9_STATE_RX_SIZE] ____cacheline_aligned;
- u8 tx[KXSD9_STATE_TX_SIZE];
+ struct device *dev;
+ struct regmap *map;
+ struct iio_mount_matrix orientation;
+ struct regulator_bulk_data regs[2];
+ u8 scale;
};
#define KXSD9_SCALE_2G "0.011978"
@@ -65,6 +88,14 @@ struct kxsd9_state {
/* reverse order */
static const int kxsd9_micro_scales[4] = { 47853, 35934, 23927, 11978 };
+#define KXSD9_ZERO_G_OFFSET -2048
+
+/*
+ * Regulator names
+ */
+static const char kxsd9_reg_vdd[] = "vdd";
+static const char kxsd9_reg_iovdd[] = "iovdd";
+
static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
{
int ret, i;
@@ -79,42 +110,17 @@ static int kxsd9_write_scale(struct iio_dev *indio_dev, int micro)
if (!foundit)
return -EINVAL;
- mutex_lock(&st->buf_lock);
- ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
+ ret = regmap_update_bits(st->map,
+ KXSD9_REG_CTRL_C,
+ KXSD9_CTRL_C_FS_MASK,
+ i);
if (ret < 0)
goto error_ret;
- st->tx[0] = KXSD9_WRITE(KXSD9_REG_CTRL_C);
- st->tx[1] = (ret & ~KXSD9_FS_MASK) | i;
- ret = spi_write(st->us, st->tx, 2);
-error_ret:
- mutex_unlock(&st->buf_lock);
- return ret;
-}
+ /* Cached scale when the sensor is powered down */
+ st->scale = i;
-static int kxsd9_read(struct iio_dev *indio_dev, u8 address)
-{
- int ret;
- struct kxsd9_state *st = iio_priv(indio_dev);
- struct spi_transfer xfers[] = {
- {
- .bits_per_word = 8,
- .len = 1,
- .delay_usecs = 200,
- .tx_buf = st->tx,
- }, {
- .bits_per_word = 8,
- .len = 2,
- .rx_buf = st->rx,
- },
- };
-
- mutex_lock(&st->buf_lock);
- st->tx[0] = KXSD9_READ(address);
- ret = spi_sync_transfer(st->us, xfers, ARRAY_SIZE(xfers));
- if (!ret)
- ret = (((u16)(st->rx[0])) << 8) | (st->rx[1] & 0xF0);
- mutex_unlock(&st->buf_lock);
+error_ret:
return ret;
}
@@ -136,6 +142,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev,
long mask)
{
int ret = -EINVAL;
+ struct kxsd9_state *st = iio_priv(indio_dev);
+
+ pm_runtime_get_sync(st->dev);
if (mask == IIO_CHAN_INFO_SCALE) {
/* Check no integer component */
@@ -144,6 +153,9 @@ static int kxsd9_write_raw(struct iio_dev *indio_dev,
ret = kxsd9_write_scale(indio_dev, val2);
}
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
return ret;
}
@@ -153,46 +165,154 @@ static int kxsd9_read_raw(struct iio_dev *indio_dev,
{
int ret = -EINVAL;
struct kxsd9_state *st = iio_priv(indio_dev);
+ unsigned int regval;
+ __be16 raw_val;
+ u16 nval;
+
+ pm_runtime_get_sync(st->dev);
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = kxsd9_read(indio_dev, chan->address);
- if (ret < 0)
+ ret = regmap_bulk_read(st->map, chan->address, &raw_val,
+ sizeof(raw_val));
+ if (ret)
goto error_ret;
- *val = ret;
+ nval = be16_to_cpu(raw_val);
+ /* Only 12 bits are valid */
+ nval >>= 4;
+ *val = nval;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ /* This has a bias of -2048 */
+ *val = KXSD9_ZERO_G_OFFSET;
ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
- ret = spi_w8r8(st->us, KXSD9_READ(KXSD9_REG_CTRL_C));
+ ret = regmap_read(st->map,
+ KXSD9_REG_CTRL_C,
+ &regval);
if (ret < 0)
goto error_ret;
*val = 0;
- *val2 = kxsd9_micro_scales[ret & KXSD9_FS_MASK];
+ *val2 = kxsd9_micro_scales[regval & KXSD9_CTRL_C_FS_MASK];
ret = IIO_VAL_INT_PLUS_MICRO;
break;
}
error_ret:
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
return ret;
};
-#define KXSD9_ACCEL_CHAN(axis) \
+
+static irqreturn_t kxsd9_trigger_handler(int irq, void *p)
+{
+ const struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct kxsd9_state *st = iio_priv(indio_dev);
+ int ret;
+ /* 4 * 16bit values AND timestamp */
+ __be16 hw_values[8];
+
+ ret = regmap_bulk_read(st->map,
+ KXSD9_REG_X,
+ &hw_values,
+ 8);
+ if (ret) {
+ dev_err(st->dev,
+ "error reading data\n");
+ return ret;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ hw_values,
+ iio_get_time_ns(indio_dev));
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int kxsd9_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct kxsd9_state *st = iio_priv(indio_dev);
+
+ pm_runtime_get_sync(st->dev);
+
+ return 0;
+}
+
+static int kxsd9_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct kxsd9_state *st = iio_priv(indio_dev);
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops kxsd9_buffer_setup_ops = {
+ .preenable = kxsd9_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = kxsd9_buffer_postdisable,
+};
+
+static const struct iio_mount_matrix *
+kxsd9_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct kxsd9_state *st = iio_priv(indio_dev);
+
+ return &st->orientation;
+}
+
+static const struct iio_chan_spec_ext_info kxsd9_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, kxsd9_get_mount_matrix),
+ { },
+};
+
+#define KXSD9_ACCEL_CHAN(axis, index) \
{ \
.type = IIO_ACCEL, \
.modified = 1, \
.channel2 = IIO_MOD_##axis, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .ext_info = kxsd9_ext_info, \
.address = KXSD9_REG_##axis, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 4, \
+ .endianness = IIO_BE, \
+ }, \
}
static const struct iio_chan_spec kxsd9_channels[] = {
- KXSD9_ACCEL_CHAN(X), KXSD9_ACCEL_CHAN(Y), KXSD9_ACCEL_CHAN(Z),
+ KXSD9_ACCEL_CHAN(X, 0),
+ KXSD9_ACCEL_CHAN(Y, 1),
+ KXSD9_ACCEL_CHAN(Z, 2),
{
.type = IIO_VOLTAGE,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.indexed = 1,
.address = KXSD9_REG_AUX,
- }
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 12,
+ .storagebits = 16,
+ .shift = 4,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(4),
};
static const struct attribute_group kxsd9_attribute_group = {
@@ -203,17 +323,69 @@ static int kxsd9_power_up(struct kxsd9_state *st)
{
int ret;
- st->tx[0] = 0x0d;
- st->tx[1] = 0x40;
- ret = spi_write(st->us, st->tx, 2);
+ /* Enable the regulators */
+ ret = regulator_bulk_enable(ARRAY_SIZE(st->regs), st->regs);
+ if (ret) {
+ dev_err(st->dev, "Cannot enable regulators\n");
+ return ret;
+ }
+
+ /* Power up */
+ ret = regmap_write(st->map,
+ KXSD9_REG_CTRL_B,
+ KXSD9_CTRL_B_ENABLE);
if (ret)
return ret;
- st->tx[0] = 0x0c;
- st->tx[1] = 0x9b;
- return spi_write(st->us, st->tx, 2);
+ /*
+ * Set 1000Hz LPF, 2g fullscale, motion wakeup threshold 1g,
+ * latched wakeup
+ */
+ ret = regmap_write(st->map,
+ KXSD9_REG_CTRL_C,
+ KXSD9_CTRL_C_LP_1000HZ |
+ KXSD9_CTRL_C_MOT_LEV |
+ KXSD9_CTRL_C_MOT_LAT |
+ st->scale);
+ if (ret)
+ return ret;
+
+ /*
+ * Power-up time depends on the LPF setting, but typ 15.9 ms, let's
+ * set 20 ms to allow for some slack.
+ */
+ msleep(20);
+
+ return 0;
};
+static int kxsd9_power_down(struct kxsd9_state *st)
+{
+ int ret;
+
+ /*
+ * Set into low power mode - since there may be more users of the
+ * regulators this is the first step of the power saving: it will
+ * make sure we conserve power even if there are others users on the
+ * regulators.
+ */
+ ret = regmap_update_bits(st->map,
+ KXSD9_REG_CTRL_B,
+ KXSD9_CTRL_B_ENABLE,
+ 0);
+ if (ret)
+ return ret;
+
+ /* Disable the regulators */
+ ret = regulator_bulk_disable(ARRAY_SIZE(st->regs), st->regs);
+ if (ret) {
+ dev_err(st->dev, "Cannot disable regulators\n");
+ return ret;
+ }
+
+ return 0;
+}
+
static const struct iio_info kxsd9_info = {
.read_raw = &kxsd9_read_raw,
.write_raw = &kxsd9_write_raw,
@@ -221,57 +393,136 @@ static const struct iio_info kxsd9_info = {
.driver_module = THIS_MODULE,
};
-static int kxsd9_probe(struct spi_device *spi)
+/* Four channels apart from timestamp, scan mask = 0x0f */
+static const unsigned long kxsd9_scan_masks[] = { 0xf, 0 };
+
+int kxsd9_common_probe(struct device *dev,
+ struct regmap *map,
+ const char *name)
{
struct iio_dev *indio_dev;
struct kxsd9_state *st;
+ int ret;
- indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
- spi_set_drvdata(spi, indio_dev);
+ st->dev = dev;
+ st->map = map;
- st->us = spi;
- mutex_init(&st->buf_lock);
indio_dev->channels = kxsd9_channels;
indio_dev->num_channels = ARRAY_SIZE(kxsd9_channels);
- indio_dev->name = spi_get_device_id(spi)->name;
- indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = name;
+ indio_dev->dev.parent = dev;
indio_dev->info = &kxsd9_info;
indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->available_scan_masks = kxsd9_scan_masks;
+
+ /* Read the mounting matrix, if present */
+ ret = of_iio_read_mount_matrix(dev,
+ "mount-matrix",
+ &st->orientation);
+ if (ret)
+ return ret;
+
+ /* Fetch and turn on regulators */
+ st->regs[0].supply = kxsd9_reg_vdd;
+ st->regs[1].supply = kxsd9_reg_iovdd;
+ ret = devm_regulator_bulk_get(dev,
+ ARRAY_SIZE(st->regs),
+ st->regs);
+ if (ret) {
+ dev_err(dev, "Cannot get regulators\n");
+ return ret;
+ }
+ /* Default scaling */
+ st->scale = KXSD9_CTRL_C_FS_2G;
- spi->mode = SPI_MODE_0;
- spi_setup(spi);
kxsd9_power_up(st);
- return iio_device_register(indio_dev);
+ ret = iio_triggered_buffer_setup(indio_dev,
+ iio_pollfunc_store_time,
+ kxsd9_trigger_handler,
+ &kxsd9_buffer_setup_ops);
+ if (ret) {
+ dev_err(dev, "triggered buffer setup failed\n");
+ goto err_power_down;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_cleanup_buffer;
+
+ dev_set_drvdata(dev, indio_dev);
+
+ /* Enable runtime PM */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ /*
+ * Set autosuspend to two orders of magnitude larger than the
+ * start-up time. 20ms start-up time means 2000ms autosuspend,
+ * i.e. 2 seconds.
+ */
+ pm_runtime_set_autosuspend_delay(dev, 2000);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_put(dev);
+
+ return 0;
+
+err_cleanup_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_power_down:
+ kxsd9_power_down(st);
+
+ return ret;
}
+EXPORT_SYMBOL(kxsd9_common_probe);
-static int kxsd9_remove(struct spi_device *spi)
+int kxsd9_common_remove(struct device *dev)
{
- iio_device_unregister(spi_get_drvdata(spi));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct kxsd9_state *st = iio_priv(indio_dev);
+
+ iio_triggered_buffer_cleanup(indio_dev);
+ iio_device_unregister(indio_dev);
+ pm_runtime_get_sync(dev);
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+ kxsd9_power_down(st);
return 0;
}
+EXPORT_SYMBOL(kxsd9_common_remove);
-static const struct spi_device_id kxsd9_id[] = {
- {"kxsd9", 0},
- { },
-};
-MODULE_DEVICE_TABLE(spi, kxsd9_id);
+#ifdef CONFIG_PM
+static int kxsd9_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct kxsd9_state *st = iio_priv(indio_dev);
-static struct spi_driver kxsd9_driver = {
- .driver = {
- .name = "kxsd9",
- },
- .probe = kxsd9_probe,
- .remove = kxsd9_remove,
- .id_table = kxsd9_id,
+ return kxsd9_power_down(st);
+}
+
+static int kxsd9_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct kxsd9_state *st = iio_priv(indio_dev);
+
+ return kxsd9_power_up(st);
+}
+#endif /* CONFIG_PM */
+
+const struct dev_pm_ops kxsd9_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(kxsd9_runtime_suspend,
+ kxsd9_runtime_resume, NULL)
};
-module_spi_driver(kxsd9_driver);
+EXPORT_SYMBOL(kxsd9_dev_pm_ops);
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
-MODULE_DESCRIPTION("Kionix KXSD9 SPI driver");
+MODULE_DESCRIPTION("Kionix KXSD9 driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/kxsd9.h b/drivers/iio/accel/kxsd9.h
new file mode 100644
index 000000000000..7e8a28168310
--- /dev/null
+++ b/drivers/iio/accel/kxsd9.h
@@ -0,0 +1,12 @@
+#include <linux/device.h>
+#include <linux/kernel.h>
+
+#define KXSD9_STATE_RX_SIZE 2
+#define KXSD9_STATE_TX_SIZE 2
+
+int kxsd9_common_probe(struct device *dev,
+ struct regmap *map,
+ const char *name);
+int kxsd9_common_remove(struct device *dev);
+
+extern const struct dev_pm_ops kxsd9_dev_pm_ops;
diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c
new file mode 100644
index 000000000000..4ea2ff623a6d
--- /dev/null
+++ b/drivers/iio/accel/mc3230.c
@@ -0,0 +1,211 @@
+/**
+ * mCube MC3230 3-Axis Accelerometer
+ *
+ * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * IIO driver for mCube MC3230; 7-bit I2C address: 0x4c.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define MC3230_REG_XOUT 0x00
+#define MC3230_REG_YOUT 0x01
+#define MC3230_REG_ZOUT 0x02
+
+#define MC3230_REG_MODE 0x07
+#define MC3230_MODE_OPCON_MASK 0x03
+#define MC3230_MODE_OPCON_WAKE 0x01
+#define MC3230_MODE_OPCON_STANDBY 0x03
+
+#define MC3230_REG_CHIP_ID 0x18
+#define MC3230_CHIP_ID 0x01
+
+#define MC3230_REG_PRODUCT_CODE 0x3b
+#define MC3230_PRODUCT_CODE 0x19
+
+/*
+ * The accelerometer has one measurement range:
+ *
+ * -1.5g - +1.5g (8-bit, signed)
+ *
+ * scale = (1.5 + 1.5) * 9.81 / (2^8 - 1) = 0.115411765
+ */
+
+static const int mc3230_nscale = 115411765;
+
+#define MC3230_CHANNEL(reg, axis) { \
+ .type = IIO_ACCEL, \
+ .address = reg, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mc3230_channels[] = {
+ MC3230_CHANNEL(MC3230_REG_XOUT, X),
+ MC3230_CHANNEL(MC3230_REG_YOUT, Y),
+ MC3230_CHANNEL(MC3230_REG_ZOUT, Z),
+};
+
+struct mc3230_data {
+ struct i2c_client *client;
+};
+
+static int mc3230_set_opcon(struct mc3230_data *data, int opcon)
+{
+ int ret;
+ struct i2c_client *client = data->client;
+
+ ret = i2c_smbus_read_byte_data(client, MC3230_REG_MODE);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to read mode reg: %d\n", ret);
+ return ret;
+ }
+
+ ret &= ~MC3230_MODE_OPCON_MASK;
+ ret |= opcon;
+
+ ret = i2c_smbus_write_byte_data(client, MC3230_REG_MODE, ret);
+ if (ret < 0) {
+ dev_err(&client->dev, "failed to write mode reg: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int mc3230_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mc3230_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_smbus_read_byte_data(data->client, chan->address);
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(ret, 7);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = mc3230_nscale;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mc3230_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = mc3230_read_raw,
+};
+
+static int mc3230_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct mc3230_data *data;
+
+ /* First check chip-id and product-id */
+ ret = i2c_smbus_read_byte_data(client, MC3230_REG_CHIP_ID);
+ if (ret != MC3230_CHIP_ID)
+ return (ret < 0) ? ret : -ENODEV;
+
+ ret = i2c_smbus_read_byte_data(client, MC3230_REG_PRODUCT_CODE);
+ if (ret != MC3230_PRODUCT_CODE)
+ return (ret < 0) ? ret : -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev) {
+ dev_err(&client->dev, "iio allocation failed!\n");
+ return -ENOMEM;
+ }
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ i2c_set_clientdata(client, indio_dev);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &mc3230_info;
+ indio_dev->name = "mc3230";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mc3230_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mc3230_channels);
+
+ ret = mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "device_register failed\n");
+ mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
+ }
+
+ return ret;
+}
+
+static int mc3230_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+
+ return mc3230_set_opcon(iio_priv(indio_dev), MC3230_MODE_OPCON_STANDBY);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mc3230_suspend(struct device *dev)
+{
+ struct mc3230_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return mc3230_set_opcon(data, MC3230_MODE_OPCON_STANDBY);
+}
+
+static int mc3230_resume(struct device *dev)
+{
+ struct mc3230_data *data;
+
+ data = iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ return mc3230_set_opcon(data, MC3230_MODE_OPCON_WAKE);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(mc3230_pm_ops, mc3230_suspend, mc3230_resume);
+
+static const struct i2c_device_id mc3230_i2c_id[] = {
+ {"mc3230", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, mc3230_i2c_id);
+
+static struct i2c_driver mc3230_driver = {
+ .driver = {
+ .name = "mc3230",
+ .pm = &mc3230_pm_ops,
+ },
+ .probe = mc3230_probe,
+ .remove = mc3230_remove,
+ .id_table = mc3230_i2c_id,
+};
+
+module_i2c_driver(mc3230_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("mCube MC3230 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c
index 0acdee516973..03beadf14ad3 100644
--- a/drivers/iio/accel/mma7660.c
+++ b/drivers/iio/accel/mma7660.c
@@ -251,6 +251,7 @@ static const struct i2c_device_id mma7660_i2c_id[] = {
{"mma7660", 0},
{}
};
+MODULE_DEVICE_TABLE(i2c, mma7660_i2c_id);
static const struct acpi_device_id mma7660_acpi_id[] = {
{"MMA7660", 0},
diff --git a/drivers/iio/accel/mxc6255.c b/drivers/iio/accel/mxc6255.c
index 97ccde722e7b..0abad6948201 100644
--- a/drivers/iio/accel/mxc6255.c
+++ b/drivers/iio/accel/mxc6255.c
@@ -154,7 +154,7 @@ static int mxc6255_probe(struct i2c_client *client,
return ret;
}
- if (chip_id != MXC6255_CHIP_ID) {
+ if ((chip_id & 0x1f) != MXC6255_CHIP_ID) {
dev_err(&client->dev, "Invalid chip id %x\n", chip_id);
return -ENODEV;
}
@@ -171,12 +171,14 @@ static int mxc6255_probe(struct i2c_client *client,
}
static const struct acpi_device_id mxc6255_acpi_match[] = {
+ {"MXC6225", 0},
{"MXC6255", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, mxc6255_acpi_match);
static const struct i2c_device_id mxc6255_id[] = {
+ {"mxc6225", 0},
{"mxc6255", 0},
{ }
};
diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
index 4ae05fce9f24..31db00970fa0 100644
--- a/drivers/iio/accel/ssp_accel_sensor.c
+++ b/drivers/iio/accel/ssp_accel_sensor.c
@@ -74,7 +74,7 @@ static int ssp_accel_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static struct iio_info ssp_accel_iio_info = {
+static const struct iio_info ssp_accel_iio_info = {
.read_raw = &ssp_accel_read_raw,
.write_raw = &ssp_accel_write_raw,
};
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 767577298ee3..7edcf3238620 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -264,6 +264,15 @@ config LPC18XX_ADC
To compile this driver as a module, choose M here: the module will be
called lpc18xx_adc.
+config LTC2485
+ tristate "Linear Technology LTC2485 ADC driver"
+ depends on I2C
+ help
+ Say yes here to build support for Linear Technology LTC2485 ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ltc2485.
+
config MAX1027
tristate "Maxim max1027 ADC driver"
depends on SPI
@@ -317,6 +326,19 @@ config MCP3422
This driver can also be built as a module. If so, the module will be
called mcp3422.
+config MEDIATEK_MT6577_AUXADC
+ tristate "MediaTek AUXADC driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to enable support for MediaTek mt65xx AUXADC.
+
+ The driver supports immediate mode operation to read from one of sixteen
+ channels (external or internal).
+
+ This driver can also be built as a module. If so, the module will be
+ called mt6577_auxadc.
+
config MEN_Z188_ADC
tristate "MEN 16z188 ADC IP Core support"
depends on MCB
@@ -397,6 +419,21 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
+config STX104
+ tristate "Apex Embedded Systems STX104 driver"
+ depends on X86 && ISA_BUS_API
+ select GPIOLIB
+ help
+ Say yes here to build support for the Apex Embedded Systems STX104
+ integrated analog PC/104 card.
+
+ This driver supports the 16 channels of single-ended (8 channels of
+ differential) analog inputs, 2 channels of analog output, 4 digital
+ inputs, and 4 digital outputs provided by the STX104.
+
+ The base port addresses for the devices may be configured via the base
+ array module parameter.
+
config TI_ADC081C
tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
depends on I2C
@@ -417,6 +454,18 @@ config TI_ADC0832
This driver can also be built as a module. If so, the module will be
called ti-adc0832.
+config TI_ADC12138
+ tristate "Texas Instruments ADC12130/ADC12132/ADC12138"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADC12130,
+ ADC12132 and ADC12138 chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-adc12138.
+
config TI_ADC128S052
tristate "Texas Instruments ADC128S052/ADC122S021/ADC124S021"
depends on SPI
@@ -427,6 +476,18 @@ config TI_ADC128S052
This driver can also be built as a module. If so, the module will be
called ti-adc128s052.
+config TI_ADC161S626
+ tristate "Texas Instruments ADC161S626 1-channel differential ADC"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for Texas Instruments ADC141S626,
+ and ADC161S626 chips.
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-adc161s626.
+
config TI_ADS1015
tristate "Texas Instruments ADS1015 ADC"
depends on I2C && !SENSORS_ADS1015
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 0ba0d500eedb..7a40c04c311f 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -27,10 +27,12 @@ obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
+obj-$(CONFIG_LTC2485) += ltc2485.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
+obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MXS_LRADC) += mxs-lradc.o
obj-$(CONFIG_NAU7802) += nau7802.o
@@ -38,9 +40,12 @@ obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
+obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
+obj-$(CONFIG_TI_ADC12138) += ti-adc12138.o
obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
+obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c
index c0f6a98fd9bd..b8d5cfd57ec4 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -481,7 +481,7 @@ error_free_gpios:
if (!st->fixed_addr)
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
error_disable_reg:
- if (!IS_ERR_OR_NULL(st->reg))
+ if (!IS_ERR(st->reg))
regulator_disable(st->reg);
return ret;
@@ -496,7 +496,7 @@ static int ad7266_remove(struct spi_device *spi)
iio_triggered_buffer_cleanup(indio_dev);
if (!st->fixed_addr)
gpio_free_array(st->gpios, ARRAY_SIZE(st->gpios));
- if (!IS_ERR_OR_NULL(st->reg))
+ if (!IS_ERR(st->reg))
regulator_disable(st->reg);
return 0;
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
index 10ec8fce395f..e399bf04c73a 100644
--- a/drivers/iio/adc/ad7298.c
+++ b/drivers/iio/adc/ad7298.c
@@ -239,16 +239,16 @@ static int ad7298_read_raw(struct iio_dev *indio_dev,
switch (m) {
case IIO_CHAN_INFO_RAW:
- mutex_lock(&indio_dev->mlock);
- if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
- ret = -EBUSY;
- } else {
- if (chan->address == AD7298_CH_TEMP)
- ret = ad7298_scan_temp(st, val);
- else
- ret = ad7298_scan_direct(st, chan->address);
- }
- mutex_unlock(&indio_dev->mlock);
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ if (chan->address == AD7298_CH_TEMP)
+ ret = ad7298_scan_temp(st, val);
+ else
+ ret = ad7298_scan_direct(st, chan->address);
+
+ iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index 847789bae821..e6706a09e100 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -519,11 +519,9 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
int ret, i;
unsigned int tmp;
- mutex_lock(&indio_dev->mlock);
- if (iio_buffer_enabled(indio_dev)) {
- mutex_unlock(&indio_dev->mlock);
- return -EBUSY;
- }
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@@ -548,7 +546,7 @@ static int ad7793_write_raw(struct iio_dev *indio_dev,
ret = -EINVAL;
}
- mutex_unlock(&indio_dev->mlock);
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 0438c68015e8..bbdac07f4aaa 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -113,6 +113,7 @@
#define AT91_ADC_TSMR_TSAV (3 << 4) /* Averages samples */
#define AT91_ADC_TSMR_TSAV_(x) ((x) << 4)
#define AT91_ADC_TSMR_SCTIM (0x0f << 16) /* Switch closure time */
+#define AT91_ADC_TSMR_SCTIM_(x) ((x) << 16)
#define AT91_ADC_TSMR_PENDBC (0x0f << 28) /* Pen Debounce time */
#define AT91_ADC_TSMR_PENDBC_(x) ((x) << 28)
#define AT91_ADC_TSMR_NOTSDMA (1 << 22) /* No Touchscreen DMA */
@@ -150,6 +151,7 @@
#define MAX_RLPOS_BITS 10
#define TOUCH_SAMPLE_PERIOD_US_RL 10000 /* 10ms, the SoC can't keep up with 2ms */
#define TOUCH_SHTIM 0xa
+#define TOUCH_SCTIM_US 10 /* 10us for the Touchscreen Switches Closure Time */
/**
* struct at91_adc_reg_desc - Various informations relative to registers
@@ -1001,7 +1003,9 @@ static void atmel_ts_close(struct input_dev *dev)
static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz)
{
+ struct iio_dev *idev = iio_priv_to_dev(st);
u32 reg = 0;
+ u32 tssctim = 0;
int i = 0;
/* a Pen Detect Debounce Time is necessary for the ADC Touch to avoid
@@ -1034,11 +1038,20 @@ static int at91_ts_hw_init(struct at91_adc_state *st, u32 adc_clk_khz)
return 0;
}
+ /* Touchscreen Switches Closure time needed for allowing the value to
+ * stabilize.
+ * Switch Closure Time = (TSSCTIM * 4) ADCClock periods
+ */
+ tssctim = DIV_ROUND_UP(TOUCH_SCTIM_US * adc_clk_khz / 1000, 4);
+ dev_dbg(&idev->dev, "adc_clk at: %d KHz, tssctim at: %d\n",
+ adc_clk_khz, tssctim);
+
if (st->touchscreen_type == ATMEL_ADC_TOUCHSCREEN_4WIRE)
reg = AT91_ADC_TSMR_TSMODE_4WIRE_PRESS;
else
reg = AT91_ADC_TSMR_TSMODE_5WIRE;
+ reg |= AT91_ADC_TSMR_SCTIM_(tssctim) & AT91_ADC_TSMR_SCTIM;
reg |= AT91_ADC_TSMR_TSAV_(st->caps->ts_filter_average)
& AT91_ADC_TSMR_TSAV;
reg |= AT91_ADC_TSMR_PENDBC_(st->ts_pendbc) & AT91_ADC_TSMR_PENDBC;
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index 955f3fdaf519..59b7d76e1ad2 100644
--- a/drivers/iio/adc/ina2xx-adc.c
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -114,7 +114,6 @@ struct ina2xx_chip_info {
struct mutex state_lock;
unsigned int shunt_resistor;
int avg;
- s64 prev_ns; /* track buffer capture time, check for underruns */
int int_time_vbus; /* Bus voltage integration time uS */
int int_time_vshunt; /* Shunt voltage integration time uS */
bool allow_async_readout;
@@ -509,8 +508,6 @@ static int ina2xx_work_buffer(struct iio_dev *indio_dev)
iio_push_to_buffers_with_timestamp(indio_dev,
(unsigned int *)data, time_a);
- chip->prev_ns = time_a;
-
return (unsigned long)(time_b - time_a) / 1000;
};
@@ -554,8 +551,6 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev)
dev_dbg(&indio_dev->dev, "Async readout mode: %d\n",
chip->allow_async_readout);
- chip->prev_ns = iio_get_time_ns(indio_dev);
-
chip->task = kthread_run(ina2xx_capture_thread, (void *)indio_dev,
"%s:%d-%uus", indio_dev->name, indio_dev->id,
sampling_us);
diff --git a/drivers/iio/adc/ltc2485.c b/drivers/iio/adc/ltc2485.c
new file mode 100644
index 000000000000..eab91f12454a
--- /dev/null
+++ b/drivers/iio/adc/ltc2485.c
@@ -0,0 +1,148 @@
+/*
+ * ltc2485.c - Driver for Linear Technology LTC2485 ADC
+ *
+ * Copyright (C) 2016 Alison Schofield <amsfield22@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * Datasheet: http://cds.linear.com/docs/en/datasheet/2485fd.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* Power-on configuration: rejects both 50/60Hz, operates at 1x speed */
+#define LTC2485_CONFIG_DEFAULT 0
+
+struct ltc2485_data {
+ struct i2c_client *client;
+ ktime_t time_prev; /* last conversion */
+};
+
+static void ltc2485_wait_conv(struct ltc2485_data *data)
+{
+ const unsigned int conv_time = 147; /* conversion time ms */
+ unsigned int time_elapsed;
+
+ /* delay if conversion time not passed since last read or write */
+ time_elapsed = ktime_ms_delta(ktime_get(), data->time_prev);
+
+ if (time_elapsed < conv_time)
+ msleep(conv_time - time_elapsed);
+}
+
+static int ltc2485_read(struct ltc2485_data *data, int *val)
+{
+ struct i2c_client *client = data->client;
+ __be32 buf = 0;
+ int ret;
+
+ ltc2485_wait_conv(data);
+
+ ret = i2c_master_recv(client, (char *)&buf, 4);
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c_master_recv failed\n");
+ return ret;
+ }
+ data->time_prev = ktime_get();
+ *val = sign_extend32(be32_to_cpu(buf) >> 6, 24);
+
+ return ret;
+}
+
+static int ltc2485_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ltc2485_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (mask == IIO_CHAN_INFO_RAW) {
+ ret = ltc2485_read(data, val);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ } else if (mask == IIO_CHAN_INFO_SCALE) {
+ *val = 5000; /* on board vref millivolts */
+ *val2 = 25; /* 25 (24 + sign) data bits */
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ } else {
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec ltc2485_channel[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE)
+ },
+};
+
+static const struct iio_info ltc2485_info = {
+ .read_raw = ltc2485_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int ltc2485_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct ltc2485_data *data;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+ I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->info = &ltc2485_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ltc2485_channel;
+ indio_dev->num_channels = ARRAY_SIZE(ltc2485_channel);
+
+ ret = i2c_smbus_write_byte(data->client, LTC2485_CONFIG_DEFAULT);
+ if (ret < 0)
+ return ret;
+
+ data->time_prev = ktime_get();
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id ltc2485_id[] = {
+ { "ltc2485", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ltc2485_id);
+
+static struct i2c_driver ltc2485_driver = {
+ .driver = {
+ .name = "ltc2485",
+ },
+ .probe = ltc2485_probe,
+ .id_table = ltc2485_id,
+};
+module_i2c_driver(ltc2485_driver);
+
+MODULE_AUTHOR("Alison Schofield <amsfield22@gmail.com>");
+MODULE_DESCRIPTION("Linear Technology LTC2485 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c
index d095efe1ba14..8f3606de4eaf 100644
--- a/drivers/iio/adc/men_z188_adc.c
+++ b/drivers/iio/adc/men_z188_adc.c
@@ -78,7 +78,7 @@ static int z188_iio_read_raw(struct iio_dev *iio_dev,
return ret;
}
-static struct iio_info z188_adc_info = {
+static const struct iio_info z188_adc_info = {
.read_raw = &z188_iio_read_raw,
.driver_module = THIS_MODULE,
};
diff --git a/drivers/iio/adc/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c
new file mode 100644
index 000000000000..2d104c828041
--- /dev/null
+++ b/drivers/iio/adc/mt6577_auxadc.c
@@ -0,0 +1,291 @@
+/*
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Zhiyong Tao <zhiyong.tao@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/iopoll.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+
+/* Register definitions */
+#define MT6577_AUXADC_CON0 0x00
+#define MT6577_AUXADC_CON1 0x04
+#define MT6577_AUXADC_CON2 0x10
+#define MT6577_AUXADC_STA BIT(0)
+
+#define MT6577_AUXADC_DAT0 0x14
+#define MT6577_AUXADC_RDY0 BIT(12)
+
+#define MT6577_AUXADC_MISC 0x94
+#define MT6577_AUXADC_PDN_EN BIT(14)
+
+#define MT6577_AUXADC_DAT_MASK 0xfff
+#define MT6577_AUXADC_SLEEP_US 1000
+#define MT6577_AUXADC_TIMEOUT_US 10000
+#define MT6577_AUXADC_POWER_READY_MS 1
+#define MT6577_AUXADC_SAMPLE_READY_US 25
+
+struct mt6577_auxadc_device {
+ void __iomem *reg_base;
+ struct clk *adc_clk;
+ struct mutex lock;
+};
+
+#define MT6577_AUXADC_CHANNEL(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (idx), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+}
+
+static const struct iio_chan_spec mt6577_auxadc_iio_channels[] = {
+ MT6577_AUXADC_CHANNEL(0),
+ MT6577_AUXADC_CHANNEL(1),
+ MT6577_AUXADC_CHANNEL(2),
+ MT6577_AUXADC_CHANNEL(3),
+ MT6577_AUXADC_CHANNEL(4),
+ MT6577_AUXADC_CHANNEL(5),
+ MT6577_AUXADC_CHANNEL(6),
+ MT6577_AUXADC_CHANNEL(7),
+ MT6577_AUXADC_CHANNEL(8),
+ MT6577_AUXADC_CHANNEL(9),
+ MT6577_AUXADC_CHANNEL(10),
+ MT6577_AUXADC_CHANNEL(11),
+ MT6577_AUXADC_CHANNEL(12),
+ MT6577_AUXADC_CHANNEL(13),
+ MT6577_AUXADC_CHANNEL(14),
+ MT6577_AUXADC_CHANNEL(15),
+};
+
+static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
+ u32 or_mask, u32 and_mask)
+{
+ u32 val;
+
+ val = readl(reg);
+ val |= or_mask;
+ val &= ~and_mask;
+ writel(val, reg);
+}
+
+static int mt6577_auxadc_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ u32 val;
+ void __iomem *reg_channel;
+ int ret;
+ struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
+ reg_channel = adc_dev->reg_base + MT6577_AUXADC_DAT0 +
+ chan->channel * 0x04;
+
+ mutex_lock(&adc_dev->lock);
+
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
+ 0, 1 << chan->channel);
+
+ /* read channel and make sure old ready bit == 0 */
+ ret = readl_poll_timeout(reg_channel, val,
+ ((val & MT6577_AUXADC_RDY0) == 0),
+ MT6577_AUXADC_SLEEP_US,
+ MT6577_AUXADC_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "wait for channel[%d] ready bit clear time out\n",
+ chan->channel);
+ goto err_timeout;
+ }
+
+ /* set bit to trigger sample */
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_CON1,
+ 1 << chan->channel, 0);
+
+ /* we must delay here for hardware sample channel data */
+ udelay(MT6577_AUXADC_SAMPLE_READY_US);
+
+ /* check MTK_AUXADC_CON2 if auxadc is idle */
+ ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val,
+ ((val & MT6577_AUXADC_STA) == 0),
+ MT6577_AUXADC_SLEEP_US,
+ MT6577_AUXADC_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "wait for auxadc idle time out\n");
+ goto err_timeout;
+ }
+
+ /* read channel and make sure ready bit == 1 */
+ ret = readl_poll_timeout(reg_channel, val,
+ ((val & MT6577_AUXADC_RDY0) != 0),
+ MT6577_AUXADC_SLEEP_US,
+ MT6577_AUXADC_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "wait for channel[%d] data ready time out\n",
+ chan->channel);
+ goto err_timeout;
+ }
+
+ /* read data */
+ val = readl(reg_channel) & MT6577_AUXADC_DAT_MASK;
+
+ mutex_unlock(&adc_dev->lock);
+
+ return val;
+
+err_timeout:
+
+ mutex_unlock(&adc_dev->lock);
+
+ return -ETIMEDOUT;
+}
+
+static int mt6577_auxadc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_PROCESSED:
+ *val = mt6577_auxadc_read(indio_dev, chan);
+ if (*val < 0) {
+ dev_err(indio_dev->dev.parent,
+ "failed to sample data on channel[%d]\n",
+ chan->channel);
+ return *val;
+ }
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mt6577_auxadc_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &mt6577_auxadc_read_raw,
+};
+
+static int mt6577_auxadc_probe(struct platform_device *pdev)
+{
+ struct mt6577_auxadc_device *adc_dev;
+ unsigned long adc_clk_rate;
+ struct resource *res;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc_dev));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc_dev = iio_priv(indio_dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->info = &mt6577_auxadc_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mt6577_auxadc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mt6577_auxadc_iio_channels);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ adc_dev->reg_base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(adc_dev->reg_base)) {
+ dev_err(&pdev->dev, "failed to get auxadc base address\n");
+ return PTR_ERR(adc_dev->reg_base);
+ }
+
+ adc_dev->adc_clk = devm_clk_get(&pdev->dev, "main");
+ if (IS_ERR(adc_dev->adc_clk)) {
+ dev_err(&pdev->dev, "failed to get auxadc clock\n");
+ return PTR_ERR(adc_dev->adc_clk);
+ }
+
+ ret = clk_prepare_enable(adc_dev->adc_clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable auxadc clock\n");
+ return ret;
+ }
+
+ adc_clk_rate = clk_get_rate(adc_dev->adc_clk);
+ if (!adc_clk_rate) {
+ ret = -EINVAL;
+ dev_err(&pdev->dev, "null clock rate\n");
+ goto err_disable_clk;
+ }
+
+ mutex_init(&adc_dev->lock);
+
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+ MT6577_AUXADC_PDN_EN, 0);
+ mdelay(MT6577_AUXADC_POWER_READY_MS);
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed to register iio device\n");
+ goto err_power_off;
+ }
+
+ return 0;
+
+err_power_off:
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+ 0, MT6577_AUXADC_PDN_EN);
+err_disable_clk:
+ clk_disable_unprepare(adc_dev->adc_clk);
+ return ret;
+}
+
+static int mt6577_auxadc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ mt6577_auxadc_mod_reg(adc_dev->reg_base + MT6577_AUXADC_MISC,
+ 0, MT6577_AUXADC_PDN_EN);
+
+ clk_disable_unprepare(adc_dev->adc_clk);
+
+ return 0;
+}
+
+static const struct of_device_id mt6577_auxadc_of_match[] = {
+ { .compatible = "mediatek,mt2701-auxadc", },
+ { .compatible = "mediatek,mt8173-auxadc", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
+
+static struct platform_driver mt6577_auxadc_driver = {
+ .driver = {
+ .name = "mt6577-auxadc",
+ .of_match_table = mt6577_auxadc_of_match,
+ },
+ .probe = mt6577_auxadc_probe,
+ .remove = mt6577_auxadc_remove,
+};
+module_platform_driver(mt6577_auxadc_driver);
+
+MODULE_AUTHOR("Zhiyong Tao <zhiyong.tao@mediatek.com>");
+MODULE_DESCRIPTION("MTK AUXADC Device Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c
index db9b829ccf0d..08f446695f97 100644
--- a/drivers/iio/adc/nau7802.c
+++ b/drivers/iio/adc/nau7802.c
@@ -197,7 +197,7 @@ static irqreturn_t nau7802_eoc_trigger(int irq, void *private)
if (st->conversion_count < NAU7802_MIN_CONVERSIONS)
st->conversion_count++;
if (st->conversion_count >= NAU7802_MIN_CONVERSIONS)
- complete_all(&st->value_ok);
+ complete(&st->value_ok);
return IRQ_HANDLED;
}
diff --git a/drivers/iio/dac/stx104.c b/drivers/iio/adc/stx104.c
index bebbd00304ce..7e3645749eaf 100644
--- a/drivers/iio/dac/stx104.c
+++ b/drivers/iio/adc/stx104.c
@@ -1,5 +1,5 @@
/*
- * DAC driver for the Apex Embedded Systems STX104
+ * IIO driver for the Apex Embedded Systems STX104
* Copyright (C) 2016 William Breathitt Gray
*
* This program is free software; you can redistribute it and/or modify
@@ -20,19 +20,30 @@
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/isa.h>
+#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/spinlock.h>
-#define STX104_NUM_CHAN 2
-
-#define STX104_CHAN(chan) { \
+#define STX104_OUT_CHAN(chan) { \
.type = IIO_VOLTAGE, \
.channel = chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.indexed = 1, \
.output = 1 \
}
+#define STX104_IN_CHAN(chan, diff) { \
+ .type = IIO_VOLTAGE, \
+ .channel = chan, \
+ .channel2 = chan, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_HARDWAREGAIN) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .indexed = 1, \
+ .differential = diff \
+}
+
+#define STX104_NUM_OUT_CHAN 2
#define STX104_EXTENT 16
@@ -47,8 +58,8 @@ MODULE_PARM_DESC(base, "Apex Embedded Systems STX104 base addresses");
* @base: base port address of the IIO device
*/
struct stx104_iio {
- unsigned chan_out_states[STX104_NUM_CHAN];
- unsigned base;
+ unsigned int chan_out_states[STX104_NUM_OUT_CHAN];
+ unsigned int base;
};
/**
@@ -79,28 +90,95 @@ static int stx104_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val, int *val2, long mask)
{
struct stx104_iio *const priv = iio_priv(indio_dev);
+ unsigned int adc_config;
+ int adbu;
+ int gain;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ /* get gain configuration */
+ adc_config = inb(priv->base + 11);
+ gain = adc_config & 0x3;
+
+ *val = 1 << gain;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_RAW:
+ if (chan->output) {
+ *val = priv->chan_out_states[chan->channel];
+ return IIO_VAL_INT;
+ }
+
+ /* select ADC channel */
+ outb(chan->channel | (chan->channel << 4), priv->base + 2);
+
+ /* trigger ADC sample capture and wait for completion */
+ outb(0, priv->base);
+ while (inb(priv->base + 8) & BIT(7));
+
+ *val = inw(priv->base);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ /* get ADC bipolar/unipolar configuration */
+ adc_config = inb(priv->base + 11);
+ adbu = !(adc_config & BIT(2));
+
+ *val = -32768 * adbu;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* get ADC bipolar/unipolar and gain configuration */
+ adc_config = inb(priv->base + 11);
+ adbu = !(adc_config & BIT(2));
+ gain = adc_config & 0x3;
+
+ *val = 5;
+ *val2 = 15 - adbu + gain;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
- if (mask != IIO_CHAN_INFO_RAW)
- return -EINVAL;
-
- *val = priv->chan_out_states[chan->channel];
-
- return IIO_VAL_INT;
+ return -EINVAL;
}
static int stx104_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int val, int val2, long mask)
{
struct stx104_iio *const priv = iio_priv(indio_dev);
- const unsigned chan_addr_offset = 2 * chan->channel;
- if (mask != IIO_CHAN_INFO_RAW)
+ switch (mask) {
+ case IIO_CHAN_INFO_HARDWAREGAIN:
+ /* Only four gain states (x1, x2, x4, x8) */
+ switch (val) {
+ case 1:
+ outb(0, priv->base + 11);
+ break;
+ case 2:
+ outb(1, priv->base + 11);
+ break;
+ case 4:
+ outb(2, priv->base + 11);
+ break;
+ case 8:
+ outb(3, priv->base + 11);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+ case IIO_CHAN_INFO_RAW:
+ if (chan->output) {
+ /* DAC can only accept up to a 16-bit value */
+ if ((unsigned int)val > 65535)
+ return -EINVAL;
+
+ priv->chan_out_states[chan->channel] = val;
+ outw(val, priv->base + 4 + 2 * chan->channel);
+
+ return 0;
+ }
return -EINVAL;
+ }
- priv->chan_out_states[chan->channel] = val;
- outw(val, priv->base + 4 + chan_addr_offset);
-
- return 0;
+ return -EINVAL;
}
static const struct iio_info stx104_info = {
@@ -109,9 +187,22 @@ static const struct iio_info stx104_info = {
.write_raw = stx104_write_raw
};
-static const struct iio_chan_spec stx104_channels[STX104_NUM_CHAN] = {
- STX104_CHAN(0),
- STX104_CHAN(1)
+/* single-ended input channels configuration */
+static const struct iio_chan_spec stx104_channels_sing[] = {
+ STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
+ STX104_IN_CHAN(0, 0), STX104_IN_CHAN(1, 0), STX104_IN_CHAN(2, 0),
+ STX104_IN_CHAN(3, 0), STX104_IN_CHAN(4, 0), STX104_IN_CHAN(5, 0),
+ STX104_IN_CHAN(6, 0), STX104_IN_CHAN(7, 0), STX104_IN_CHAN(8, 0),
+ STX104_IN_CHAN(9, 0), STX104_IN_CHAN(10, 0), STX104_IN_CHAN(11, 0),
+ STX104_IN_CHAN(12, 0), STX104_IN_CHAN(13, 0), STX104_IN_CHAN(14, 0),
+ STX104_IN_CHAN(15, 0)
+};
+/* differential input channels configuration */
+static const struct iio_chan_spec stx104_channels_diff[] = {
+ STX104_OUT_CHAN(0), STX104_OUT_CHAN(1),
+ STX104_IN_CHAN(0, 1), STX104_IN_CHAN(1, 1), STX104_IN_CHAN(2, 1),
+ STX104_IN_CHAN(3, 1), STX104_IN_CHAN(4, 1), STX104_IN_CHAN(5, 1),
+ STX104_IN_CHAN(6, 1), STX104_IN_CHAN(7, 1)
};
static int stx104_gpio_get_direction(struct gpio_chip *chip,
@@ -204,13 +295,27 @@ static int stx104_probe(struct device *dev, unsigned int id)
indio_dev->info = &stx104_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = stx104_channels;
- indio_dev->num_channels = STX104_NUM_CHAN;
+
+ /* determine if differential inputs */
+ if (inb(base[id] + 8) & BIT(5)) {
+ indio_dev->num_channels = ARRAY_SIZE(stx104_channels_diff);
+ indio_dev->channels = stx104_channels_diff;
+ } else {
+ indio_dev->num_channels = ARRAY_SIZE(stx104_channels_sing);
+ indio_dev->channels = stx104_channels_sing;
+ }
+
indio_dev->name = dev_name(dev);
priv = iio_priv(indio_dev);
priv->base = base[id];
+ /* configure device for software trigger operation */
+ outb(0, base[id] + 9);
+
+ /* initialize gain setting to x1 */
+ outb(0, base[id] + 11);
+
/* initialize DAC output to 0V */
outw(0, base[id] + 4);
outw(0, base[id] + 6);
@@ -271,5 +376,5 @@ static struct isa_driver stx104_driver = {
module_isa_driver(stx104_driver, num_stx104);
MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
-MODULE_DESCRIPTION("Apex Embedded Systems STX104 DAC driver");
+MODULE_DESCRIPTION("Apex Embedded Systems STX104 IIO driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c
new file mode 100644
index 000000000000..072f03bfe6a0
--- /dev/null
+++ b/drivers/iio/adc/ti-adc12138.c
@@ -0,0 +1,552 @@
+/*
+ * ADC12130/ADC12132/ADC12138 12-bit plus sign ADC driver
+ *
+ * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * Datasheet: http://www.ti.com/lit/ds/symlink/adc12138.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/completion.h>
+#include <linux/clk.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/regulator/consumer.h>
+
+#define ADC12138_MODE_AUTO_CAL 0x08
+#define ADC12138_MODE_READ_STATUS 0x0c
+#define ADC12138_MODE_ACQUISITION_TIME_6 0x0e
+#define ADC12138_MODE_ACQUISITION_TIME_10 0x4e
+#define ADC12138_MODE_ACQUISITION_TIME_18 0x8e
+#define ADC12138_MODE_ACQUISITION_TIME_34 0xce
+
+#define ADC12138_STATUS_CAL BIT(6)
+
+enum {
+ adc12130,
+ adc12132,
+ adc12138,
+};
+
+struct adc12138 {
+ struct spi_device *spi;
+ unsigned int id;
+ /* conversion clock */
+ struct clk *cclk;
+ /* positive analog voltage reference */
+ struct regulator *vref_p;
+ /* negative analog voltage reference */
+ struct regulator *vref_n;
+ struct mutex lock;
+ struct completion complete;
+ /* The number of cclk periods for the S/H's acquisition time */
+ unsigned int acquisition_time;
+
+ u8 tx_buf[2] ____cacheline_aligned;
+ u8 rx_buf[2];
+};
+
+#define ADC12138_VOLTAGE_CHANNEL(chan) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_index = chan, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 13, \
+ .storagebits = 16, \
+ .shift = 3, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define ADC12138_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan1), \
+ .channel2 = (chan2), \
+ .differential = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
+ | BIT(IIO_CHAN_INFO_OFFSET), \
+ .scan_index = si, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 13, \
+ .storagebits = 16, \
+ .shift = 3, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec adc12132_channels[] = {
+ ADC12138_VOLTAGE_CHANNEL(0),
+ ADC12138_VOLTAGE_CHANNEL(1),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 3),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static const struct iio_chan_spec adc12138_channels[] = {
+ ADC12138_VOLTAGE_CHANNEL(0),
+ ADC12138_VOLTAGE_CHANNEL(1),
+ ADC12138_VOLTAGE_CHANNEL(2),
+ ADC12138_VOLTAGE_CHANNEL(3),
+ ADC12138_VOLTAGE_CHANNEL(4),
+ ADC12138_VOLTAGE_CHANNEL(5),
+ ADC12138_VOLTAGE_CHANNEL(6),
+ ADC12138_VOLTAGE_CHANNEL(7),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(1, 0, 9),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(2, 3, 10),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(3, 2, 11),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(4, 5, 12),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(5, 4, 13),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(6, 7, 14),
+ ADC12138_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
+ IIO_CHAN_SOFT_TIMESTAMP(16),
+};
+
+static int adc12138_mode_programming(struct adc12138 *adc, u8 mode,
+ void *rx_buf, int len)
+{
+ struct spi_transfer xfer = {
+ .tx_buf = adc->tx_buf,
+ .rx_buf = adc->rx_buf,
+ .len = len,
+ };
+ int ret;
+
+ /* Skip unused bits for ADC12130 and ADC12132 */
+ if (adc->id != adc12138)
+ mode = (mode & 0xc0) | ((mode & 0x0f) << 2);
+
+ adc->tx_buf[0] = mode;
+
+ ret = spi_sync_transfer(adc->spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ memcpy(rx_buf, adc->rx_buf, len);
+
+ return 0;
+}
+
+static int adc12138_read_status(struct adc12138 *adc)
+{
+ u8 rx_buf[2];
+ int ret;
+
+ ret = adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS,
+ rx_buf, 2);
+ if (ret)
+ return ret;
+
+ return (rx_buf[0] << 1) | (rx_buf[1] >> 7);
+}
+
+static int __adc12138_start_conv(struct adc12138 *adc,
+ struct iio_chan_spec const *channel,
+ void *data, int len)
+
+{
+ const u8 ch_to_mux[] = { 0, 4, 1, 5, 2, 6, 3, 7 };
+ u8 mode = (ch_to_mux[channel->channel] << 4) |
+ (channel->differential ? 0 : 0x80);
+
+ return adc12138_mode_programming(adc, mode, data, len);
+}
+
+static int adc12138_start_conv(struct adc12138 *adc,
+ struct iio_chan_spec const *channel)
+{
+ u8 trash;
+
+ return __adc12138_start_conv(adc, channel, &trash, 1);
+}
+
+static int adc12138_start_and_read_conv(struct adc12138 *adc,
+ struct iio_chan_spec const *channel,
+ __be16 *data)
+{
+ return __adc12138_start_conv(adc, channel, data, 2);
+}
+
+static int adc12138_read_conv_data(struct adc12138 *adc, __be16 *value)
+{
+ /* Issue a read status instruction and read previous conversion data */
+ return adc12138_mode_programming(adc, ADC12138_MODE_READ_STATUS,
+ value, sizeof(*value));
+}
+
+static int adc12138_wait_eoc(struct adc12138 *adc, unsigned long timeout)
+{
+ if (!wait_for_completion_timeout(&adc->complete, timeout))
+ return -ETIMEDOUT;
+
+ return 0;
+}
+
+static int adc12138_adc_conversion(struct adc12138 *adc,
+ struct iio_chan_spec const *channel,
+ __be16 *value)
+{
+ int ret;
+
+ reinit_completion(&adc->complete);
+
+ ret = adc12138_start_conv(adc, channel);
+ if (ret)
+ return ret;
+
+ ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100));
+ if (ret)
+ return ret;
+
+ return adc12138_read_conv_data(adc, value);
+}
+
+static int adc12138_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int *value,
+ int *shift, long mask)
+{
+ struct adc12138 *adc = iio_priv(iio);
+ int ret;
+ __be16 data;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ ret = adc12138_adc_conversion(adc, channel, &data);
+ mutex_unlock(&adc->lock);
+ if (ret)
+ return ret;
+
+ *value = sign_extend32(be16_to_cpu(data) >> 3, 12);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(adc->vref_p);
+ if (ret < 0)
+ return ret;
+ *value = ret;
+
+ if (!IS_ERR(adc->vref_n)) {
+ ret = regulator_get_voltage(adc->vref_n);
+ if (ret < 0)
+ return ret;
+ *value -= ret;
+ }
+
+ /* convert regulator output voltage to mV */
+ *value /= 1000;
+ *shift = channel->scan_type.realbits - 1;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ if (!IS_ERR(adc->vref_n)) {
+ *value = regulator_get_voltage(adc->vref_n);
+ if (*value < 0)
+ return *value;
+ } else {
+ *value = 0;
+ }
+
+ /* convert regulator output voltage to mV */
+ *value /= 1000;
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info adc12138_info = {
+ .read_raw = adc12138_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static int adc12138_init(struct adc12138 *adc)
+{
+ int ret;
+ int status;
+ u8 mode;
+ u8 trash;
+
+ reinit_completion(&adc->complete);
+
+ ret = adc12138_mode_programming(adc, ADC12138_MODE_AUTO_CAL, &trash, 1);
+ if (ret)
+ return ret;
+
+ /* data output at this time has no significance */
+ status = adc12138_read_status(adc);
+ if (status < 0)
+ return status;
+
+ adc12138_wait_eoc(adc, msecs_to_jiffies(100));
+
+ status = adc12138_read_status(adc);
+ if (status & ADC12138_STATUS_CAL) {
+ dev_warn(&adc->spi->dev,
+ "Auto Cal sequence is still in progress: %#x\n",
+ status);
+ return -EIO;
+ }
+
+ switch (adc->acquisition_time) {
+ case 6:
+ mode = ADC12138_MODE_ACQUISITION_TIME_6;
+ break;
+ case 10:
+ mode = ADC12138_MODE_ACQUISITION_TIME_10;
+ break;
+ case 18:
+ mode = ADC12138_MODE_ACQUISITION_TIME_18;
+ break;
+ case 34:
+ mode = ADC12138_MODE_ACQUISITION_TIME_34;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return adc12138_mode_programming(adc, mode, &trash, 1);
+}
+
+static irqreturn_t adc12138_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adc12138 *adc = iio_priv(indio_dev);
+ __be16 data[20] = { }; /* 16x 2 bytes ADC data + 8 bytes timestamp */
+ __be16 trash;
+ int ret;
+ int scan_index;
+ int i = 0;
+
+ mutex_lock(&adc->lock);
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ const struct iio_chan_spec *scan_chan =
+ &indio_dev->channels[scan_index];
+
+ reinit_completion(&adc->complete);
+
+ ret = adc12138_start_and_read_conv(adc, scan_chan,
+ i ? &data[i - 1] : &trash);
+ if (ret) {
+ dev_warn(&adc->spi->dev,
+ "failed to start conversion\n");
+ goto out;
+ }
+
+ ret = adc12138_wait_eoc(adc, msecs_to_jiffies(100));
+ if (ret) {
+ dev_warn(&adc->spi->dev, "wait eoc timeout\n");
+ goto out;
+ }
+
+ i++;
+ }
+
+ if (i) {
+ ret = adc12138_read_conv_data(adc, &data[i - 1]);
+ if (ret) {
+ dev_warn(&adc->spi->dev,
+ "failed to get conversion data\n");
+ goto out;
+ }
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data,
+ iio_get_time_ns(indio_dev));
+out:
+ mutex_unlock(&adc->lock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t adc12138_eoc_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct adc12138 *adc = iio_priv(indio_dev);
+
+ complete(&adc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int adc12138_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct adc12138 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+ adc->id = spi_get_device_id(spi)->driver_data;
+ mutex_init(&adc->lock);
+ init_completion(&adc->complete);
+
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &adc12138_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ switch (adc->id) {
+ case adc12130:
+ case adc12132:
+ indio_dev->channels = adc12132_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc12132_channels);
+ break;
+ case adc12138:
+ indio_dev->channels = adc12138_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adc12138_channels);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = of_property_read_u32(spi->dev.of_node, "ti,acquisition-time",
+ &adc->acquisition_time);
+ if (ret)
+ adc->acquisition_time = 10;
+
+ adc->cclk = devm_clk_get(&spi->dev, NULL);
+ if (IS_ERR(adc->cclk))
+ return PTR_ERR(adc->cclk);
+
+ adc->vref_p = devm_regulator_get(&spi->dev, "vref-p");
+ if (IS_ERR(adc->vref_p))
+ return PTR_ERR(adc->vref_p);
+
+ adc->vref_n = devm_regulator_get_optional(&spi->dev, "vref-n");
+ if (IS_ERR(adc->vref_n)) {
+ /*
+ * Assume vref_n is 0V if an optional regulator is not
+ * specified, otherwise return the error code.
+ */
+ ret = PTR_ERR(adc->vref_n);
+ if (ret != -ENODEV)
+ return ret;
+ }
+
+ ret = devm_request_irq(&spi->dev, spi->irq, adc12138_eoc_handler,
+ IRQF_TRIGGER_RISING, indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(adc->cclk);
+ if (ret)
+ return ret;
+
+ ret = regulator_enable(adc->vref_p);
+ if (ret)
+ goto err_clk_disable;
+
+ if (!IS_ERR(adc->vref_n)) {
+ ret = regulator_enable(adc->vref_n);
+ if (ret)
+ goto err_vref_p_disable;
+ }
+
+ ret = adc12138_init(adc);
+ if (ret)
+ goto err_vref_n_disable;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ adc12138_trigger_handler, NULL);
+ if (ret)
+ goto err_vref_n_disable;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto err_buffer_cleanup;
+
+ return 0;
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_vref_n_disable:
+ if (!IS_ERR(adc->vref_n))
+ regulator_disable(adc->vref_n);
+err_vref_p_disable:
+ regulator_disable(adc->vref_p);
+err_clk_disable:
+ clk_disable_unprepare(adc->cclk);
+
+ return ret;
+}
+
+static int adc12138_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adc12138 *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ if (!IS_ERR(adc->vref_n))
+ regulator_disable(adc->vref_n);
+ regulator_disable(adc->vref_p);
+ clk_disable_unprepare(adc->cclk);
+
+ return 0;
+}
+
+#ifdef CONFIG_OF
+
+static const struct of_device_id adc12138_dt_ids[] = {
+ { .compatible = "ti,adc12130", },
+ { .compatible = "ti,adc12132", },
+ { .compatible = "ti,adc12138", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adc12138_dt_ids);
+
+#endif
+
+static const struct spi_device_id adc12138_id[] = {
+ { "adc12130", adc12130 },
+ { "adc12132", adc12132 },
+ { "adc12138", adc12138 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adc12138_id);
+
+static struct spi_driver adc12138_driver = {
+ .driver = {
+ .name = "adc12138",
+ .of_match_table = of_match_ptr(adc12138_dt_ids),
+ },
+ .probe = adc12138_probe,
+ .remove = adc12138_remove,
+ .id_table = adc12138_id,
+};
+module_spi_driver(adc12138_driver);
+
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_DESCRIPTION("ADC12130/ADC12132/ADC12138 driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
new file mode 100644
index 000000000000..f94b69f9c288
--- /dev/null
+++ b/drivers/iio/adc/ti-adc161s626.c
@@ -0,0 +1,248 @@
+/*
+ * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC
+ *
+ * ADC Devices Supported:
+ * adc141s626 - 14-bit ADC
+ * adc161s626 - 16-bit ADC
+ *
+ * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define TI_ADC_DRV_NAME "ti-adc161s626"
+
+enum {
+ TI_ADC141S626,
+ TI_ADC161S626,
+};
+
+static const struct iio_chan_spec ti_adc141s626_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 14,
+ .storagebits = 16,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct iio_chan_spec ti_adc161s626_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+struct ti_adc_data {
+ struct iio_dev *indio_dev;
+ struct spi_device *spi;
+ u8 read_size;
+ u8 shift;
+
+ u8 buffer[16] ____cacheline_aligned;
+};
+
+static int ti_adc_read_measurement(struct ti_adc_data *data,
+ struct iio_chan_spec const *chan, int *val)
+{
+ int ret;
+
+ switch (data->read_size) {
+ case 2: {
+ __be16 buf;
+
+ ret = spi_read(data->spi, (void *) &buf, 2);
+ if (ret)
+ return ret;
+
+ *val = be16_to_cpu(buf);
+ break;
+ }
+ case 3: {
+ __be32 buf;
+
+ ret = spi_read(data->spi, (void *) &buf, 3);
+ if (ret)
+ return ret;
+
+ *val = be32_to_cpu(buf) >> 8;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ *val = sign_extend32(*val >> data->shift, chan->scan_type.realbits - 1);
+
+ return 0;
+}
+
+static irqreturn_t ti_adc_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ti_adc_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = ti_adc_read_measurement(data, &indio_dev->channels[0],
+ (int *) &data->buffer);
+ if (!ret)
+ iio_push_to_buffers_with_timestamp(indio_dev,
+ data->buffer,
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ti_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ti_adc_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ti_adc_read_measurement(data, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (!ret)
+ return IIO_VAL_INT;
+
+ return 0;
+}
+
+static const struct iio_info ti_adc_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = ti_adc_read_raw,
+};
+
+static int ti_adc_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ti_adc_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &ti_adc_info;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->dev.of_node = spi->dev.of_node;
+ indio_dev->name = TI_ADC_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ spi_set_drvdata(spi, indio_dev);
+
+ data = iio_priv(indio_dev);
+ data->spi = spi;
+
+ switch (spi_get_device_id(spi)->driver_data) {
+ case TI_ADC141S626:
+ indio_dev->channels = ti_adc141s626_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ti_adc141s626_channels);
+ data->shift = 0;
+ data->read_size = 2;
+ break;
+ case TI_ADC161S626:
+ indio_dev->channels = ti_adc161s626_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ti_adc161s626_channels);
+ data->shift = 6;
+ data->read_size = 3;
+ break;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ ti_adc_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_unreg_buffer;
+
+ return 0;
+
+error_unreg_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+}
+
+static int ti_adc_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return 0;
+}
+
+static const struct of_device_id ti_adc_dt_ids[] = {
+ { .compatible = "ti,adc141s626", },
+ { .compatible = "ti,adc161s626", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ti_adc_dt_ids);
+
+static const struct spi_device_id ti_adc_id[] = {
+ {"adc141s626", TI_ADC141S626},
+ {"adc161s626", TI_ADC161S626},
+ {},
+};
+MODULE_DEVICE_TABLE(spi, ti_adc_id);
+
+static struct spi_driver ti_adc_driver = {
+ .driver = {
+ .name = TI_ADC_DRV_NAME,
+ .of_match_table = of_match_ptr(ti_adc_dt_ids),
+ },
+ .probe = ti_adc_probe,
+ .remove = ti_adc_remove,
+ .id_table = ti_adc_id,
+};
+module_spi_driver(ti_adc_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
index 066abaf80201..cde6f130a99a 100644
--- a/drivers/iio/adc/ti-ads1015.c
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -522,6 +522,7 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
if (pga > 6) {
dev_err(&client->dev, "invalid gain on %s\n",
node->full_name);
+ of_node_put(node);
return -EINVAL;
}
}
@@ -532,6 +533,7 @@ static int ads1015_get_channels_config_of(struct i2c_client *client)
dev_err(&client->dev,
"invalid data_rate on %s\n",
node->full_name);
+ of_node_put(node);
return -EINVAL;
}
}
diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c
index c400439900af..4a163496d9e4 100644
--- a/drivers/iio/adc/ti-ads8688.c
+++ b/drivers/iio/adc/ti-ads8688.c
@@ -438,7 +438,7 @@ static int ads8688_probe(struct spi_device *spi)
return 0;
error_out:
- if (!IS_ERR_OR_NULL(st->reg))
+ if (!IS_ERR(st->reg))
regulator_disable(st->reg);
return ret;
@@ -451,7 +451,7 @@ static int ads8688_remove(struct spi_device *spi)
iio_device_unregister(indio_dev);
- if (!IS_ERR_OR_NULL(st->reg))
+ if (!IS_ERR(st->reg))
regulator_disable(st->reg);
return 0;
diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
index 323079c3ccce..b8f550e47d3d 100644
--- a/drivers/iio/buffer/industrialio-buffer-cb.c
+++ b/drivers/iio/buffer/industrialio-buffer-cb.c
@@ -18,6 +18,7 @@ struct iio_cb_buffer {
int (*cb)(const void *data, void *private);
void *private;
struct iio_channel *channels;
+ struct iio_dev *indio_dev;
};
static struct iio_cb_buffer *buffer_to_cb_buffer(struct iio_buffer *buffer)
@@ -52,7 +53,6 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
{
int ret;
struct iio_cb_buffer *cb_buff;
- struct iio_dev *indio_dev;
struct iio_channel *chan;
cb_buff = kzalloc(sizeof(*cb_buff), GFP_KERNEL);
@@ -72,17 +72,17 @@ struct iio_cb_buffer *iio_channel_get_all_cb(struct device *dev,
goto error_free_cb_buff;
}
- indio_dev = cb_buff->channels[0].indio_dev;
+ cb_buff->indio_dev = cb_buff->channels[0].indio_dev;
cb_buff->buffer.scan_mask
- = kcalloc(BITS_TO_LONGS(indio_dev->masklength), sizeof(long),
- GFP_KERNEL);
+ = kcalloc(BITS_TO_LONGS(cb_buff->indio_dev->masklength),
+ sizeof(long), GFP_KERNEL);
if (cb_buff->buffer.scan_mask == NULL) {
ret = -ENOMEM;
goto error_release_channels;
}
chan = &cb_buff->channels[0];
while (chan->indio_dev) {
- if (chan->indio_dev != indio_dev) {
+ if (chan->indio_dev != cb_buff->indio_dev) {
ret = -EINVAL;
goto error_free_scan_mask;
}
@@ -105,17 +105,14 @@ EXPORT_SYMBOL_GPL(iio_channel_get_all_cb);
int iio_channel_start_all_cb(struct iio_cb_buffer *cb_buff)
{
- return iio_update_buffers(cb_buff->channels[0].indio_dev,
- &cb_buff->buffer,
+ return iio_update_buffers(cb_buff->indio_dev, &cb_buff->buffer,
NULL);
}
EXPORT_SYMBOL_GPL(iio_channel_start_all_cb);
void iio_channel_stop_all_cb(struct iio_cb_buffer *cb_buff)
{
- iio_update_buffers(cb_buff->channels[0].indio_dev,
- NULL,
- &cb_buff->buffer);
+ iio_update_buffers(cb_buff->indio_dev, NULL, &cb_buff->buffer);
}
EXPORT_SYMBOL_GPL(iio_channel_stop_all_cb);
@@ -133,6 +130,13 @@ struct iio_channel
}
EXPORT_SYMBOL_GPL(iio_channel_cb_get_channels);
+struct iio_dev
+*iio_channel_cb_get_iio_dev(const struct iio_cb_buffer *cb_buffer)
+{
+ return cb_buffer->indio_dev;
+}
+EXPORT_SYMBOL_GPL(iio_channel_cb_get_iio_dev);
+
MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
MODULE_DESCRIPTION("Industrial I/O callback buffer");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c
index 4b2858ba1fd6..d3db1fce54d2 100644
--- a/drivers/iio/buffer/industrialio-triggered-buffer.c
+++ b/drivers/iio/buffer/industrialio-triggered-buffer.c
@@ -98,6 +98,48 @@ void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev)
}
EXPORT_SYMBOL(iio_triggered_buffer_cleanup);
+static void devm_iio_triggered_buffer_clean(struct device *dev, void *res)
+{
+ iio_triggered_buffer_cleanup(*(struct iio_dev **)res);
+}
+
+int devm_iio_triggered_buffer_setup(struct device *dev,
+ struct iio_dev *indio_dev,
+ irqreturn_t (*h)(int irq, void *p),
+ irqreturn_t (*thread)(int irq, void *p),
+ const struct iio_buffer_setup_ops *ops)
+{
+ struct iio_dev **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_iio_triggered_buffer_clean, sizeof(*ptr),
+ GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ *ptr = indio_dev;
+
+ ret = iio_triggered_buffer_setup(indio_dev, h, thread, ops);
+ if (!ret)
+ devres_add(dev, ptr);
+ else
+ devres_free(ptr);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_iio_triggered_buffer_setup);
+
+void devm_iio_triggered_buffer_cleanup(struct device *dev,
+ struct iio_dev *indio_dev)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_iio_triggered_buffer_clean,
+ devm_iio_device_match, indio_dev);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_iio_triggered_buffer_cleanup);
+
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("IIO helper functions for setting up triggered buffers");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index 4bcc025e8c8a..cea7f9857a1f 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -16,6 +16,7 @@ config ATLAS_PH_SENSOR
Atlas Scientific OEM SM sensors:
* pH SM sensor
* EC SM sensor
+ * ORP SM sensor
To compile this driver as module, choose M here: the
module will be called atlas-ph-sensor.
diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c
index 407f141a1eee..bd321b305a0a 100644
--- a/drivers/iio/chemical/atlas-ph-sensor.c
+++ b/drivers/iio/chemical/atlas-ph-sensor.c
@@ -66,12 +66,17 @@
#define ATLAS_REG_TDS_DATA 0x1c
#define ATLAS_REG_PSS_DATA 0x20
+#define ATLAS_REG_ORP_CALIB_STATUS 0x0d
+#define ATLAS_REG_ORP_DATA 0x0e
+
#define ATLAS_PH_INT_TIME_IN_US 450000
#define ATLAS_EC_INT_TIME_IN_US 650000
+#define ATLAS_ORP_INT_TIME_IN_US 450000
enum {
ATLAS_PH_SM,
ATLAS_EC_SM,
+ ATLAS_ORP_SM,
};
struct atlas_data {
@@ -84,26 +89,10 @@ struct atlas_data {
__be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */
};
-static const struct regmap_range atlas_volatile_ranges[] = {
- regmap_reg_range(ATLAS_REG_INT_CONTROL, ATLAS_REG_INT_CONTROL),
- regmap_reg_range(ATLAS_REG_PH_DATA, ATLAS_REG_PH_DATA + 4),
- regmap_reg_range(ATLAS_REG_EC_DATA, ATLAS_REG_PSS_DATA + 4),
-};
-
-static const struct regmap_access_table atlas_volatile_table = {
- .yes_ranges = atlas_volatile_ranges,
- .n_yes_ranges = ARRAY_SIZE(atlas_volatile_ranges),
-};
-
static const struct regmap_config atlas_regmap_config = {
.name = ATLAS_REGMAP_NAME,
-
.reg_bits = 8,
.val_bits = 8,
-
- .volatile_table = &atlas_volatile_table,
- .max_register = ATLAS_REG_PSS_DATA + 4,
- .cache_type = REGCACHE_RBTREE,
};
static const struct iio_chan_spec atlas_ph_channels[] = {
@@ -175,6 +164,23 @@ static const struct iio_chan_spec atlas_ec_channels[] = {
},
};
+static const struct iio_chan_spec atlas_orp_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .address = ATLAS_REG_ORP_DATA,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
static int atlas_check_ph_calibration(struct atlas_data *data)
{
struct device *dev = &data->client->dev;
@@ -240,6 +246,22 @@ static int atlas_check_ec_calibration(struct atlas_data *data)
return 0;
}
+static int atlas_check_orp_calibration(struct atlas_data *data)
+{
+ struct device *dev = &data->client->dev;
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(data->regmap, ATLAS_REG_ORP_CALIB_STATUS, &val);
+ if (ret)
+ return ret;
+
+ if (!val)
+ dev_warn(dev, "device has not been calibrated\n");
+
+ return 0;
+};
+
struct atlas_device {
const struct iio_chan_spec *channels;
int num_channels;
@@ -264,7 +286,13 @@ static struct atlas_device atlas_devices[] = {
.calibration = &atlas_check_ec_calibration,
.delay = ATLAS_EC_INT_TIME_IN_US,
},
-
+ [ATLAS_ORP_SM] = {
+ .channels = atlas_orp_channels,
+ .num_channels = 2,
+ .data_reg = ATLAS_REG_ORP_DATA,
+ .calibration = &atlas_check_orp_calibration,
+ .delay = ATLAS_ORP_INT_TIME_IN_US,
+ },
};
static int atlas_set_powermode(struct atlas_data *data, int on)
@@ -402,15 +430,14 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
case IIO_PH:
case IIO_CONCENTRATION:
case IIO_ELECTRICALCONDUCTIVITY:
- mutex_lock(&indio_dev->mlock);
+ case IIO_VOLTAGE:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
- if (iio_buffer_enabled(indio_dev))
- ret = -EBUSY;
- else
- ret = atlas_read_measurement(data,
- chan->address, &reg);
+ ret = atlas_read_measurement(data, chan->address, &reg);
- mutex_unlock(&indio_dev->mlock);
+ iio_device_release_direct_mode(indio_dev);
break;
default:
ret = -EINVAL;
@@ -440,6 +467,10 @@ static int atlas_read_raw(struct iio_dev *indio_dev,
*val = 0; /* 0.000000001 */
*val2 = 1000;
return IIO_VAL_INT_PLUS_NANO;
+ case IIO_VOLTAGE:
+ *val = 1; /* 0.1 */
+ *val2 = 10;
+ break;
default:
return -EINVAL;
}
@@ -475,6 +506,7 @@ static const struct iio_info atlas_info = {
static const struct i2c_device_id atlas_id[] = {
{ "atlas-ph-sm", ATLAS_PH_SM},
{ "atlas-ec-sm", ATLAS_EC_SM},
+ { "atlas-orp-sm", ATLAS_ORP_SM},
{}
};
MODULE_DEVICE_TABLE(i2c, atlas_id);
@@ -482,6 +514,7 @@ MODULE_DEVICE_TABLE(i2c, atlas_id);
static const struct of_device_id atlas_dt_ids[] = {
{ .compatible = "atlas,ph-sm", .data = (void *)ATLAS_PH_SM, },
{ .compatible = "atlas,ec-sm", .data = (void *)ATLAS_EC_SM, },
+ { .compatible = "atlas,orp-sm", .data = (void *)ATLAS_ORP_SM, },
{ }
};
MODULE_DEVICE_TABLE(of, atlas_dt_ids);
diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c
index 652649da500f..8e0e4415c161 100644
--- a/drivers/iio/chemical/vz89x.c
+++ b/drivers/iio/chemical/vz89x.c
@@ -19,25 +19,55 @@
#include <linux/mutex.h>
#include <linux/init.h>
#include <linux/i2c.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#define VZ89X_REG_MEASUREMENT 0x09
-#define VZ89X_REG_MEASUREMENT_SIZE 6
+#define VZ89X_REG_MEASUREMENT_RD_SIZE 6
+#define VZ89X_REG_MEASUREMENT_WR_SIZE 3
#define VZ89X_VOC_CO2_IDX 0
#define VZ89X_VOC_SHORT_IDX 1
#define VZ89X_VOC_TVOC_IDX 2
#define VZ89X_VOC_RESISTANCE_IDX 3
+#define VZ89TE_REG_MEASUREMENT 0x0c
+#define VZ89TE_REG_MEASUREMENT_RD_SIZE 7
+#define VZ89TE_REG_MEASUREMENT_WR_SIZE 6
+
+#define VZ89TE_VOC_TVOC_IDX 0
+#define VZ89TE_VOC_CO2_IDX 1
+#define VZ89TE_VOC_RESISTANCE_IDX 2
+
+enum {
+ VZ89X,
+ VZ89TE,
+};
+
+struct vz89x_chip_data;
+
struct vz89x_data {
struct i2c_client *client;
+ const struct vz89x_chip_data *chip;
struct mutex lock;
int (*xfer)(struct vz89x_data *data, u8 cmd);
+ bool is_valid;
unsigned long last_update;
- u8 buffer[VZ89X_REG_MEASUREMENT_SIZE];
+ u8 buffer[VZ89TE_REG_MEASUREMENT_RD_SIZE];
+};
+
+struct vz89x_chip_data {
+ bool (*valid)(struct vz89x_data *data);
+ const struct iio_chan_spec *channels;
+ u8 num_channels;
+
+ u8 cmd;
+ u8 read_size;
+ u8 write_size;
};
static const struct iio_chan_spec vz89x_channels[] = {
@@ -70,6 +100,40 @@ static const struct iio_chan_spec vz89x_channels[] = {
.info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
.address = VZ89X_VOC_RESISTANCE_IDX,
+ .scan_index = -1,
+ .scan_type = {
+ .endianness = IIO_LE,
+ },
+ },
+};
+
+static const struct iio_chan_spec vz89te_channels[] = {
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_VOC,
+ .modified = 1,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
+ .address = VZ89TE_VOC_TVOC_IDX,
+ },
+
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_CO2,
+ .modified = 1,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW),
+ .address = VZ89TE_VOC_CO2_IDX,
+ },
+ {
+ .type = IIO_RESISTANCE,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .address = VZ89TE_VOC_RESISTANCE_IDX,
+ .scan_index = -1,
+ .scan_type = {
+ .endianness = IIO_BE,
+ },
},
};
@@ -93,29 +157,45 @@ static const struct attribute_group vz89x_attrs_group = {
* always zero, and by also confirming the VOC_short isn't zero.
*/
-static int vz89x_measurement_is_valid(struct vz89x_data *data)
+static bool vz89x_measurement_is_valid(struct vz89x_data *data)
{
if (data->buffer[VZ89X_VOC_SHORT_IDX] == 0)
- return 1;
+ return true;
- return !!(data->buffer[VZ89X_REG_MEASUREMENT_SIZE - 1] > 0);
+ return !!(data->buffer[data->chip->read_size - 1] > 0);
+}
+
+/* VZ89TE device has a modified CRC-8 two complement check */
+static bool vz89te_measurement_is_valid(struct vz89x_data *data)
+{
+ u8 crc = 0;
+ int i, sum = 0;
+
+ for (i = 0; i < (data->chip->read_size - 1); i++) {
+ sum = crc + data->buffer[i];
+ crc = sum;
+ crc += sum / 256;
+ }
+
+ return !((0xff - crc) == data->buffer[data->chip->read_size - 1]);
}
static int vz89x_i2c_xfer(struct vz89x_data *data, u8 cmd)
{
+ const struct vz89x_chip_data *chip = data->chip;
struct i2c_client *client = data->client;
struct i2c_msg msg[2];
int ret;
- u8 buf[3] = { cmd, 0, 0};
+ u8 buf[6] = { cmd, 0, 0, 0, 0, 0xf3 };
msg[0].addr = client->addr;
msg[0].flags = client->flags;
- msg[0].len = 3;
+ msg[0].len = chip->write_size;
msg[0].buf = (char *) &buf;
msg[1].addr = client->addr;
msg[1].flags = client->flags | I2C_M_RD;
- msg[1].len = VZ89X_REG_MEASUREMENT_SIZE;
+ msg[1].len = chip->read_size;
msg[1].buf = (char *) &data->buffer;
ret = i2c_transfer(client->adapter, msg, 2);
@@ -133,7 +213,7 @@ static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd)
if (ret < 0)
return ret;
- for (i = 0; i < VZ89X_REG_MEASUREMENT_SIZE; i++) {
+ for (i = 0; i < data->chip->read_size; i++) {
ret = i2c_smbus_read_byte(client);
if (ret < 0)
return ret;
@@ -145,30 +225,47 @@ static int vz89x_smbus_xfer(struct vz89x_data *data, u8 cmd)
static int vz89x_get_measurement(struct vz89x_data *data)
{
+ const struct vz89x_chip_data *chip = data->chip;
int ret;
/* sensor can only be polled once a second max per datasheet */
if (!time_after(jiffies, data->last_update + HZ))
- return 0;
+ return data->is_valid ? 0 : -EAGAIN;
+
+ data->is_valid = false;
+ data->last_update = jiffies;
- ret = data->xfer(data, VZ89X_REG_MEASUREMENT);
+ ret = data->xfer(data, chip->cmd);
if (ret < 0)
return ret;
- ret = vz89x_measurement_is_valid(data);
+ ret = chip->valid(data);
if (ret)
return -EAGAIN;
- data->last_update = jiffies;
+ data->is_valid = true;
return 0;
}
-static int vz89x_get_resistance_reading(struct vz89x_data *data)
+static int vz89x_get_resistance_reading(struct vz89x_data *data,
+ struct iio_chan_spec const *chan,
+ int *val)
{
- u8 *buf = &data->buffer[VZ89X_VOC_RESISTANCE_IDX];
+ u8 *tmp = (u8 *) &data->buffer[chan->address];
- return buf[0] | (buf[1] << 8);
+ switch (chan->scan_type.endianness) {
+ case IIO_LE:
+ *val = le32_to_cpup((__le32 *) tmp) & GENMASK(23, 0);
+ break;
+ case IIO_BE:
+ *val = be32_to_cpup((__be32 *) tmp) >> 8;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
}
static int vz89x_read_raw(struct iio_dev *indio_dev,
@@ -187,15 +284,15 @@ static int vz89x_read_raw(struct iio_dev *indio_dev,
if (ret)
return ret;
- switch (chan->address) {
- case VZ89X_VOC_CO2_IDX:
- case VZ89X_VOC_SHORT_IDX:
- case VZ89X_VOC_TVOC_IDX:
+ switch (chan->type) {
+ case IIO_CONCENTRATION:
*val = data->buffer[chan->address];
return IIO_VAL_INT;
- case VZ89X_VOC_RESISTANCE_IDX:
- *val = vz89x_get_resistance_reading(data);
- return IIO_VAL_INT;
+ case IIO_RESISTANCE:
+ ret = vz89x_get_resistance_reading(data, chan, val);
+ if (!ret)
+ return IIO_VAL_INT;
+ break;
default:
return -EINVAL;
}
@@ -210,12 +307,12 @@ static int vz89x_read_raw(struct iio_dev *indio_dev,
}
break;
case IIO_CHAN_INFO_OFFSET:
- switch (chan->address) {
- case VZ89X_VOC_CO2_IDX:
+ switch (chan->channel2) {
+ case IIO_MOD_CO2:
*val = 44;
*val2 = 250000;
return IIO_VAL_INT_PLUS_MICRO;
- case VZ89X_VOC_TVOC_IDX:
+ case IIO_MOD_VOC:
*val = -13;
return IIO_VAL_INT;
default:
@@ -232,11 +329,43 @@ static const struct iio_info vz89x_info = {
.driver_module = THIS_MODULE,
};
+static const struct vz89x_chip_data vz89x_chips[] = {
+ {
+ .valid = vz89x_measurement_is_valid,
+
+ .cmd = VZ89X_REG_MEASUREMENT,
+ .read_size = VZ89X_REG_MEASUREMENT_RD_SIZE,
+ .write_size = VZ89X_REG_MEASUREMENT_WR_SIZE,
+
+ .channels = vz89x_channels,
+ .num_channels = ARRAY_SIZE(vz89x_channels),
+ },
+ {
+ .valid = vz89te_measurement_is_valid,
+
+ .cmd = VZ89TE_REG_MEASUREMENT,
+ .read_size = VZ89TE_REG_MEASUREMENT_RD_SIZE,
+ .write_size = VZ89TE_REG_MEASUREMENT_WR_SIZE,
+
+ .channels = vz89te_channels,
+ .num_channels = ARRAY_SIZE(vz89te_channels),
+ },
+};
+
+static const struct of_device_id vz89x_dt_ids[] = {
+ { .compatible = "sgx,vz89x", .data = (void *) VZ89X },
+ { .compatible = "sgx,vz89te", .data = (void *) VZ89TE },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
+
static int vz89x_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct vz89x_data *data;
+ const struct of_device_id *of_id;
+ int chip_id;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@@ -251,8 +380,15 @@ static int vz89x_probe(struct i2c_client *client,
else
return -EOPNOTSUPP;
+ of_id = of_match_device(vz89x_dt_ids, &client->dev);
+ if (!of_id)
+ chip_id = id->driver_data;
+ else
+ chip_id = (unsigned long)of_id->data;
+
i2c_set_clientdata(client, indio_dev);
data->client = client;
+ data->chip = &vz89x_chips[chip_id];
data->last_update = jiffies - HZ;
mutex_init(&data->lock);
@@ -261,24 +397,19 @@ static int vz89x_probe(struct i2c_client *client,
indio_dev->name = dev_name(&client->dev);
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = vz89x_channels;
- indio_dev->num_channels = ARRAY_SIZE(vz89x_channels);
+ indio_dev->channels = data->chip->channels;
+ indio_dev->num_channels = data->chip->num_channels;
return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id vz89x_id[] = {
- { "vz89x", 0 },
+ { "vz89x", VZ89X },
+ { "vz89te", VZ89TE },
{ }
};
MODULE_DEVICE_TABLE(i2c, vz89x_id);
-static const struct of_device_id vz89x_dt_ids[] = {
- { .compatible = "sgx,vz89x" },
- { }
-};
-MODULE_DEVICE_TABLE(of, vz89x_dt_ids);
-
static struct i2c_driver vz89x_driver = {
.driver = {
.name = "vz89x",
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index 5b41f9d0d4f3..a3cce3a38300 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -122,6 +122,14 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state)
#endif
}
+static void hid_sensor_set_power_work(struct work_struct *work)
+{
+ struct hid_sensor_common *attrb = container_of(work,
+ struct hid_sensor_common,
+ work);
+ _hid_sensor_power_state(attrb, true);
+}
+
static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
bool state)
{
@@ -130,6 +138,7 @@ static int hid_sensor_data_rdy_trigger_set_state(struct iio_trigger *trig,
void hid_sensor_remove_trigger(struct hid_sensor_common *attrb)
{
+ cancel_work_sync(&attrb->work);
iio_trigger_unregister(attrb->trigger);
iio_trigger_free(attrb->trigger);
}
@@ -170,6 +179,9 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name,
goto error_unreg_trigger;
iio_device_set_drvdata(indio_dev, attrb);
+
+ INIT_WORK(&attrb->work, hid_sensor_set_power_work);
+
pm_suspend_ignore_children(&attrb->pdev->dev, true);
pm_runtime_enable(&attrb->pdev->dev);
/* Default to 3 seconds, but can be changed from sysfs */
@@ -187,8 +199,7 @@ error_ret:
}
EXPORT_SYMBOL(hid_sensor_setup_trigger);
-#ifdef CONFIG_PM
-static int hid_sensor_suspend(struct device *dev)
+static int __maybe_unused hid_sensor_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
@@ -197,21 +208,27 @@ static int hid_sensor_suspend(struct device *dev)
return _hid_sensor_power_state(attrb, false);
}
-static int hid_sensor_resume(struct device *dev)
+static int __maybe_unused hid_sensor_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
+ schedule_work(&attrb->work);
+ return 0;
+}
+static int __maybe_unused hid_sensor_runtime_resume(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct hid_sensor_common *attrb = iio_device_get_drvdata(indio_dev);
return _hid_sensor_power_state(attrb, true);
}
-#endif
-
const struct dev_pm_ops hid_sensor_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(hid_sensor_suspend, hid_sensor_resume)
SET_RUNTIME_PM_OPS(hid_sensor_suspend,
- hid_sensor_resume, NULL)
+ hid_sensor_runtime_resume, NULL)
};
EXPORT_SYMBOL(hid_sensor_pm_ops);
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
index d06e728cea37..fe7775bb3740 100644
--- a/drivers/iio/common/st_sensors/st_sensors_buffer.c
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -63,7 +63,7 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p)
* the hardware trigger) and the hw_timestamp may get updated.
* By storing it in a local variable first, we are safe.
*/
- if (sdata->hw_irq_trigger)
+ if (iio_trigger_using_own(indio_dev))
timestamp = sdata->hw_timestamp;
else
timestamp = iio_get_time_ns(indio_dev);
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 2d5282e05482..285a64a589d7 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -234,39 +234,35 @@ int st_sensors_power_enable(struct iio_dev *indio_dev)
int err;
/* Regulators not mandatory, but if requested we should enable them. */
- pdata->vdd = devm_regulator_get_optional(indio_dev->dev.parent, "vdd");
- if (!IS_ERR(pdata->vdd)) {
- err = regulator_enable(pdata->vdd);
- if (err != 0) {
- dev_warn(&indio_dev->dev,
- "Failed to enable specified Vdd supply\n");
- return err;
- }
- } else {
- err = PTR_ERR(pdata->vdd);
- if (err != -ENODEV)
- return err;
+ pdata->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd");
+ if (IS_ERR(pdata->vdd)) {
+ dev_err(&indio_dev->dev, "unable to get Vdd supply\n");
+ return PTR_ERR(pdata->vdd);
+ }
+ err = regulator_enable(pdata->vdd);
+ if (err != 0) {
+ dev_warn(&indio_dev->dev,
+ "Failed to enable specified Vdd supply\n");
+ return err;
}
- pdata->vdd_io = devm_regulator_get_optional(indio_dev->dev.parent, "vddio");
- if (!IS_ERR(pdata->vdd_io)) {
- err = regulator_enable(pdata->vdd_io);
- if (err != 0) {
- dev_warn(&indio_dev->dev,
- "Failed to enable specified Vdd_IO supply\n");
- goto st_sensors_disable_vdd;
- }
- } else {
+ pdata->vdd_io = devm_regulator_get(indio_dev->dev.parent, "vddio");
+ if (IS_ERR(pdata->vdd_io)) {
+ dev_err(&indio_dev->dev, "unable to get Vdd_IO supply\n");
err = PTR_ERR(pdata->vdd_io);
- if (err != -ENODEV)
- goto st_sensors_disable_vdd;
+ goto st_sensors_disable_vdd;
+ }
+ err = regulator_enable(pdata->vdd_io);
+ if (err != 0) {
+ dev_warn(&indio_dev->dev,
+ "Failed to enable specified Vdd_IO supply\n");
+ goto st_sensors_disable_vdd;
}
return 0;
st_sensors_disable_vdd:
- if (!IS_ERR_OR_NULL(pdata->vdd))
- regulator_disable(pdata->vdd);
+ regulator_disable(pdata->vdd);
return err;
}
EXPORT_SYMBOL(st_sensors_power_enable);
@@ -275,11 +271,8 @@ void st_sensors_power_disable(struct iio_dev *indio_dev)
{
struct st_sensor_data *pdata = iio_priv(indio_dev);
- if (!IS_ERR_OR_NULL(pdata->vdd))
- regulator_disable(pdata->vdd);
-
- if (!IS_ERR_OR_NULL(pdata->vdd_io))
- regulator_disable(pdata->vdd_io);
+ regulator_disable(pdata->vdd);
+ regulator_disable(pdata->vdd_io);
}
EXPORT_SYMBOL(st_sensors_power_disable);
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
index e66f12ee8a55..fa73e6795359 100644
--- a/drivers/iio/common/st_sensors/st_sensors_trigger.c
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -66,7 +66,7 @@ static int st_sensors_new_samples_available(struct iio_dev *indio_dev,
* @irq: irq number
* @p: private handler data
*/
-irqreturn_t st_sensors_irq_handler(int irq, void *p)
+static irqreturn_t st_sensors_irq_handler(int irq, void *p)
{
struct iio_trigger *trig = p;
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
@@ -82,7 +82,7 @@ irqreturn_t st_sensors_irq_handler(int irq, void *p)
* @irq: irq number
* @p: private handler data
*/
-irqreturn_t st_sensors_irq_thread(int irq, void *p)
+static irqreturn_t st_sensors_irq_thread(int irq, void *p)
{
struct iio_trigger *trig = p;
struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index ca814479fadf..120b24478469 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -181,6 +181,25 @@ config AD7303
To compile this driver as module choose M here: the module will be called
ad7303.
+config CIO_DAC
+ tristate "Measurement Computing CIO-DAC IIO driver"
+ depends on X86 && ISA_BUS_API
+ help
+ Say yes here to build support for the Measurement Computing CIO-DAC
+ analog output device family (CIO-DAC16, CIO-DAC08, PC104-DAC06). The
+ base port addresses for the devices may be configured via the base
+ array module parameter.
+
+config AD8801
+ tristate "Analog Devices AD8801/AD8803 DAC driver"
+ depends on SPI_MASTER
+ help
+ Say yes here to build support for Analog Devices AD8801, AD8803 Digital to
+ Analog Converters (DAC).
+
+ To compile this driver as a module choose M here: the module will be called
+ ad8801.
+
config LPC18XX_DAC
tristate "NXP LPC18xx DAC driver"
depends on ARCH_LPC18XX || COMPILE_TEST
@@ -245,16 +264,6 @@ config MCP4922
To compile this driver as a module, choose M here: the module
will be called mcp4922.
-config STX104
- tristate "Apex Embedded Systems STX104 DAC driver"
- depends on X86 && ISA_BUS_API
- select GPIOLIB
- help
- Say yes here to build support for the 2-channel DAC and GPIO on the
- Apex Embedded Systems STX104 integrated analog PC/104 card. The base
- port addresses for the devices may be configured via the base array
- module parameter.
-
config VF610_DAC
tristate "Vybrid vf610 DAC driver"
depends on OF
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 8b78d5ca9b11..27642bbf75f2 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -20,11 +20,12 @@ obj-$(CONFIG_AD5764) += ad5764.o
obj-$(CONFIG_AD5791) += ad5791.o
obj-$(CONFIG_AD5686) += ad5686.o
obj-$(CONFIG_AD7303) += ad7303.o
+obj-$(CONFIG_AD8801) += ad8801.o
+obj-$(CONFIG_CIO_DAC) += cio-dac.o
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
obj-$(CONFIG_MAX5821) += max5821.o
obj-$(CONFIG_MCP4725) += mcp4725.o
obj-$(CONFIG_MCP4922) += mcp4922.o
-obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_VF610_DAC) += vf610_dac.o
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
index 0fde593ec0d9..5f7968232564 100644
--- a/drivers/iio/dac/ad5755.c
+++ b/drivers/iio/dac/ad5755.c
@@ -655,7 +655,7 @@ static struct ad5755_platform_data *ad5755_parse_dt(struct device *dev)
devnr = 0;
for_each_child_of_node(np, pp) {
- if (devnr > AD5755_NUM_CHANNELS) {
+ if (devnr >= AD5755_NUM_CHANNELS) {
dev_err(dev,
"There is to many channels defined in DT\n");
goto error_out;
diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c
new file mode 100644
index 000000000000..f06faa1aec09
--- /dev/null
+++ b/drivers/iio/dac/ad8801.c
@@ -0,0 +1,239 @@
+/*
+ * IIO DAC driver for Analog Devices AD8801 DAC
+ *
+ * Copyright (C) 2016 Gwenhael Goavec-Merou
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+
+#define AD8801_CFG_ADDR_OFFSET 8
+
+enum ad8801_device_ids {
+ ID_AD8801,
+ ID_AD8803,
+};
+
+struct ad8801_state {
+ struct spi_device *spi;
+ unsigned char dac_cache[8]; /* Value write on each channel */
+ unsigned int vrefh_mv;
+ unsigned int vrefl_mv;
+ struct regulator *vrefh_reg;
+ struct regulator *vrefl_reg;
+
+ __be16 data ____cacheline_aligned;
+};
+
+static int ad8801_spi_write(struct ad8801_state *state,
+ u8 channel, unsigned char value)
+{
+ state->data = cpu_to_be16((channel << AD8801_CFG_ADDR_OFFSET) | value);
+ return spi_write(state->spi, &state->data, sizeof(state->data));
+}
+
+static int ad8801_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct ad8801_state *state = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val >= 256 || val < 0)
+ return -EINVAL;
+
+ ret = ad8801_spi_write(state, chan->channel, val);
+ if (ret == 0)
+ state->dac_cache[chan->channel] = val;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ad8801_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long info)
+{
+ struct ad8801_state *state = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ *val = state->dac_cache[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = state->vrefh_mv - state->vrefl_mv;
+ *val2 = 8;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = state->vrefl_mv;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad8801_info = {
+ .read_raw = ad8801_read_raw,
+ .write_raw = ad8801_write_raw,
+ .driver_module = THIS_MODULE,
+};
+
+#define AD8801_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+}
+
+static const struct iio_chan_spec ad8801_channels[] = {
+ AD8801_CHANNEL(0),
+ AD8801_CHANNEL(1),
+ AD8801_CHANNEL(2),
+ AD8801_CHANNEL(3),
+ AD8801_CHANNEL(4),
+ AD8801_CHANNEL(5),
+ AD8801_CHANNEL(6),
+ AD8801_CHANNEL(7),
+};
+
+static int ad8801_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ad8801_state *state;
+ const struct spi_device_id *id;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ state = iio_priv(indio_dev);
+ state->spi = spi;
+ id = spi_get_device_id(spi);
+
+ state->vrefh_reg = devm_regulator_get(&spi->dev, "vrefh");
+ if (IS_ERR(state->vrefh_reg)) {
+ dev_err(&spi->dev, "Vrefh regulator not specified\n");
+ return PTR_ERR(state->vrefh_reg);
+ }
+
+ ret = regulator_enable(state->vrefh_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vrefh regulator: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(state->vrefh_reg);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to read vrefh regulator: %d\n",
+ ret);
+ goto error_disable_vrefh_reg;
+ }
+ state->vrefh_mv = ret / 1000;
+
+ if (id->driver_data == ID_AD8803) {
+ state->vrefl_reg = devm_regulator_get(&spi->dev, "vrefl");
+ if (IS_ERR(state->vrefl_reg)) {
+ dev_err(&spi->dev, "Vrefl regulator not specified\n");
+ ret = PTR_ERR(state->vrefl_reg);
+ goto error_disable_vrefh_reg;
+ }
+
+ ret = regulator_enable(state->vrefl_reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable vrefl regulator: %d\n",
+ ret);
+ goto error_disable_vrefh_reg;
+ }
+
+ ret = regulator_get_voltage(state->vrefl_reg);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to read vrefl regulator: %d\n",
+ ret);
+ goto error_disable_vrefl_reg;
+ }
+ state->vrefl_mv = ret / 1000;
+ } else {
+ state->vrefl_mv = 0;
+ state->vrefl_reg = NULL;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ad8801_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad8801_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad8801_channels);
+ indio_dev->name = id->name;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to register iio device: %d\n",
+ ret);
+ goto error_disable_vrefl_reg;
+ }
+
+ return 0;
+
+error_disable_vrefl_reg:
+ if (state->vrefl_reg)
+ regulator_disable(state->vrefl_reg);
+error_disable_vrefh_reg:
+ regulator_disable(state->vrefh_reg);
+ return ret;
+}
+
+static int ad8801_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad8801_state *state = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ if (state->vrefl_reg)
+ regulator_disable(state->vrefl_reg);
+ regulator_disable(state->vrefh_reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad8801_ids[] = {
+ {"ad8801", ID_AD8801},
+ {"ad8803", ID_AD8803},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad8801_ids);
+
+static struct spi_driver ad8801_driver = {
+ .driver = {
+ .name = "ad8801",
+ },
+ .probe = ad8801_probe,
+ .remove = ad8801_remove,
+ .id_table = ad8801_ids,
+};
+module_spi_driver(ad8801_driver);
+
+MODULE_AUTHOR("Gwenhael Goavec-Merou <gwenhael.goavec-merou@trabucayre.com>");
+MODULE_DESCRIPTION("Analog Devices AD8801/AD8803 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c
new file mode 100644
index 000000000000..5a743e2a779d
--- /dev/null
+++ b/drivers/iio/dac/cio-dac.c
@@ -0,0 +1,144 @@
+/*
+ * IIO driver for the Measurement Computing CIO-DAC
+ * Copyright (C) 2016 William Breathitt Gray
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * This driver supports the following Measurement Computing devices: CIO-DAC16,
+ * CIO-DAC06, and PC104-DAC06.
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/isa.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+
+#define CIO_DAC_NUM_CHAN 16
+
+#define CIO_DAC_CHAN(chan) { \
+ .type = IIO_VOLTAGE, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .indexed = 1, \
+ .output = 1 \
+}
+
+#define CIO_DAC_EXTENT 32
+
+static unsigned int base[max_num_isa_dev(CIO_DAC_EXTENT)];
+static unsigned int num_cio_dac;
+module_param_array(base, uint, &num_cio_dac, 0);
+MODULE_PARM_DESC(base, "Measurement Computing CIO-DAC base addresses");
+
+/**
+ * struct cio_dac_iio - IIO device private data structure
+ * @chan_out_states: channels' output states
+ * @base: base port address of the IIO device
+ */
+struct cio_dac_iio {
+ int chan_out_states[CIO_DAC_NUM_CHAN];
+ unsigned int base;
+};
+
+static int cio_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ struct cio_dac_iio *const priv = iio_priv(indio_dev);
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ *val = priv->chan_out_states[chan->channel];
+
+ return IIO_VAL_INT;
+}
+
+static int cio_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct cio_dac_iio *const priv = iio_priv(indio_dev);
+ const unsigned int chan_addr_offset = 2 * chan->channel;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ /* DAC can only accept up to a 16-bit value */
+ if ((unsigned int)val > 65535)
+ return -EINVAL;
+
+ priv->chan_out_states[chan->channel] = val;
+ outw(val, priv->base + chan_addr_offset);
+
+ return 0;
+}
+
+static const struct iio_info cio_dac_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = cio_dac_read_raw,
+ .write_raw = cio_dac_write_raw
+};
+
+static const struct iio_chan_spec cio_dac_channels[CIO_DAC_NUM_CHAN] = {
+ CIO_DAC_CHAN(0), CIO_DAC_CHAN(1), CIO_DAC_CHAN(2), CIO_DAC_CHAN(3),
+ CIO_DAC_CHAN(4), CIO_DAC_CHAN(5), CIO_DAC_CHAN(6), CIO_DAC_CHAN(7),
+ CIO_DAC_CHAN(8), CIO_DAC_CHAN(9), CIO_DAC_CHAN(10), CIO_DAC_CHAN(11),
+ CIO_DAC_CHAN(12), CIO_DAC_CHAN(13), CIO_DAC_CHAN(14), CIO_DAC_CHAN(15)
+};
+
+static int cio_dac_probe(struct device *dev, unsigned int id)
+{
+ struct iio_dev *indio_dev;
+ struct cio_dac_iio *priv;
+ unsigned int i;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ if (!devm_request_region(dev, base[id], CIO_DAC_EXTENT,
+ dev_name(dev))) {
+ dev_err(dev, "Unable to request port addresses (0x%X-0x%X)\n",
+ base[id], base[id] + CIO_DAC_EXTENT);
+ return -EBUSY;
+ }
+
+ indio_dev->info = &cio_dac_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = cio_dac_channels;
+ indio_dev->num_channels = CIO_DAC_NUM_CHAN;
+ indio_dev->name = dev_name(dev);
+
+ priv = iio_priv(indio_dev);
+ priv->base = base[id];
+
+ /* initialize DAC outputs to 0V */
+ for (i = 0; i < 32; i += 2)
+ outw(0, base[id] + i);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct isa_driver cio_dac_driver = {
+ .probe = cio_dac_probe,
+ .driver = {
+ .name = "cio-dac"
+ }
+};
+
+module_isa_driver(cio_dac_driver, num_cio_dac);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("Measurement Computing CIO-DAC IIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
index 0a8afdd21728..1f25f406c545 100644
--- a/drivers/iio/gyro/ssp_gyro_sensor.c
+++ b/drivers/iio/gyro/ssp_gyro_sensor.c
@@ -74,7 +74,7 @@ static int ssp_gyro_write_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static struct iio_info ssp_gyro_iio_info = {
+static const struct iio_info ssp_gyro_iio_info = {
.read_raw = &ssp_gyro_read_raw,
.write_raw = &ssp_gyro_write_raw,
};
diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
index d04124345992..b17e2e2bd4f5 100644
--- a/drivers/iio/humidity/Kconfig
+++ b/drivers/iio/humidity/Kconfig
@@ -28,11 +28,11 @@ config HDC100X
tristate "TI HDC100x relative humidity and temperature sensor"
depends on I2C
help
- Say yes here to build support for the TI HDC100x series of
- relative humidity and temperature sensors.
+ Say yes here to build support for the Texas Instruments
+ HDC1000 and HDC1008 relative humidity and temperature sensors.
- To compile this driver as a module, choose M here: the module
- will be called hdc100x.
+ To compile this driver as a module, choose M here: the module
+ will be called hdc100x.
config HTU21
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index d2b889918c3e..fc340ed3dca1 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1308,7 +1308,7 @@ static void devm_iio_device_release(struct device *dev, void *res)
iio_device_free(*(struct iio_dev **)res);
}
-static int devm_iio_device_match(struct device *dev, void *res, void *data)
+int devm_iio_device_match(struct device *dev, void *res, void *data)
{
struct iio_dev **r = res;
if (!r || !*r) {
@@ -1317,6 +1317,7 @@ static int devm_iio_device_match(struct device *dev, void *res, void *data)
}
return *r == data;
}
+EXPORT_SYMBOL_GPL(devm_iio_device_match);
/**
* devm_iio_device_alloc - Resource-managed iio_device_alloc()
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index 0ebfc923a997..90fac8ec63c9 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -57,6 +57,11 @@ bool iio_event_enabled(const struct iio_event_interface *ev_int)
*
* Note: The caller must make sure that this function is not running
* concurrently for the same indio_dev more than once.
+ *
+ * This function may be safely used as soon as a valid reference to iio_dev has
+ * been obtained via iio_device_alloc(), but any events that are submitted
+ * before iio_device_register() has successfully completed will be silently
+ * discarded.
**/
int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
{
@@ -64,6 +69,9 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp)
struct iio_event_data ev;
int copied;
+ if (!ev_int)
+ return 0;
+
/* Does anyone care? */
if (iio_event_enabled(ev_int)) {
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index 7ad82fdd3e5b..e1e104845e38 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -119,6 +119,22 @@ void iio_trigger_unregister(struct iio_trigger *trig_info)
}
EXPORT_SYMBOL(iio_trigger_unregister);
+int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *trig)
+{
+ if (!indio_dev || !trig)
+ return -EINVAL;
+
+ mutex_lock(&indio_dev->mlock);
+ WARN_ON(indio_dev->trig_readonly);
+
+ indio_dev->trig = iio_trigger_get(trig);
+ indio_dev->trig_readonly = true;
+ mutex_unlock(&indio_dev->mlock);
+
+ return 0;
+}
+EXPORT_SYMBOL(iio_trigger_set_immutable);
+
/* Search for trigger by name, assuming iio_trigger_list_lock held */
static struct iio_trigger *__iio_trigger_find_by_name(const char *name)
{
@@ -255,6 +271,14 @@ static int iio_trigger_attach_poll_func(struct iio_trigger *trig,
goto out_free_irq;
}
+ /*
+ * Check if we just registered to our own trigger: we determine that
+ * this is the case if the IIO device and the trigger device share the
+ * same parent device.
+ */
+ if (pf->indio_dev->dev.parent == trig->dev.parent)
+ trig->attached_own_device = true;
+
return ret;
out_free_irq:
@@ -279,6 +303,8 @@ static int iio_trigger_detach_poll_func(struct iio_trigger *trig,
if (ret)
return ret;
}
+ if (pf->indio_dev->dev.parent == trig->dev.parent)
+ trig->attached_own_device = false;
iio_trigger_put_irq(trig, pf->irq);
free_irq(pf->irq, pf);
module_put(pf->indio_dev->info->driver_module);
@@ -384,6 +410,10 @@ static ssize_t iio_trigger_write_current(struct device *dev,
mutex_unlock(&indio_dev->mlock);
return -EBUSY;
}
+ if (indio_dev->trig_readonly) {
+ mutex_unlock(&indio_dev->mlock);
+ return -EPERM;
+ }
mutex_unlock(&indio_dev->mlock);
trig = iio_trigger_find_by_name(buf, len);
@@ -622,6 +652,71 @@ void devm_iio_trigger_free(struct device *dev, struct iio_trigger *iio_trig)
}
EXPORT_SYMBOL_GPL(devm_iio_trigger_free);
+static void devm_iio_trigger_unreg(struct device *dev, void *res)
+{
+ iio_trigger_unregister(*(struct iio_trigger **)res);
+}
+
+/**
+ * devm_iio_trigger_register - Resource-managed iio_trigger_register()
+ * @dev: device this trigger was allocated for
+ * @trig_info: trigger to register
+ *
+ * Managed iio_trigger_register(). The IIO trigger registered with this
+ * function is automatically unregistered on driver detach. This function
+ * calls iio_trigger_register() internally. Refer to that function for more
+ * information.
+ *
+ * If an iio_trigger registered with this function needs to be unregistered
+ * separately, devm_iio_trigger_unregister() must be used.
+ *
+ * RETURNS:
+ * 0 on success, negative error number on failure.
+ */
+int devm_iio_trigger_register(struct device *dev, struct iio_trigger *trig_info)
+{
+ struct iio_trigger **ptr;
+ int ret;
+
+ ptr = devres_alloc(devm_iio_trigger_unreg, sizeof(*ptr), GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ *ptr = trig_info;
+ ret = iio_trigger_register(trig_info);
+ if (!ret)
+ devres_add(dev, ptr);
+ else
+ devres_free(ptr);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(devm_iio_trigger_register);
+
+/**
+ * devm_iio_trigger_unregister - Resource-managed iio_trigger_unregister()
+ * @dev: device this iio_trigger belongs to
+ * @trig_info: the trigger associated with the device
+ *
+ * Unregister trigger registered with devm_iio_trigger_register().
+ */
+void devm_iio_trigger_unregister(struct device *dev,
+ struct iio_trigger *trig_info)
+{
+ int rc;
+
+ rc = devres_release(dev, devm_iio_trigger_unreg, devm_iio_trigger_match,
+ trig_info);
+ WARN_ON(rc);
+}
+EXPORT_SYMBOL_GPL(devm_iio_trigger_unregister);
+
+bool iio_trigger_using_own(struct iio_dev *indio_dev)
+{
+ return indio_dev->trig->attached_own_device;
+}
+EXPORT_SYMBOL(iio_trigger_using_own);
+
void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
{
indio_dev->groups[indio_dev->groupcounter++] =
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 3574945183fe..ba2e64d7ee58 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -267,6 +267,19 @@ config PA12203001
This driver can also be built as a module. If so, the module
will be called pa12203001.
+config SI1145
+ tristate "SI1132 and SI1141/2/3/5/6/7 combined ALS, UV index and proximity sensor"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say Y here if you want to build a driver for the Silicon Labs SI1132 or
+ SI1141/2/3/5/6/7 combined ambient light, UV index and proximity sensor
+ chips.
+
+ To compile this driver as a module, choose M here: the module will be
+ called si1145.
+
config STK3310
tristate "STK3310 ALS and proximity sensor"
depends on I2C
@@ -334,11 +347,11 @@ config US5182D
will be called us5182d.
config VCNL4000
- tristate "VCNL4000 combined ALS and proximity sensor"
+ tristate "VCNL4000/4010/4020 combined ALS and proximity sensor"
depends on I2C
help
- Say Y here if you want to build a driver for the Vishay VCNL4000
- combined ambient light and proximity sensor.
+ Say Y here if you want to build a driver for the Vishay VCNL4000,
+ VCNL4010, VCNL4020 combined ambient light and proximity sensor.
To compile this driver as a module, choose M here: the
module will be called vcnl4000.
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 6f2a3c62de27..c5768df87a17 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -26,6 +26,7 @@ obj-$(CONFIG_OPT3001) += opt3001.o
obj-$(CONFIG_PA12203001) += pa12203001.o
obj-$(CONFIG_RPR0521) += rpr0521.o
obj-$(CONFIG_SENSORS_TSL2563) += tsl2563.o
+obj-$(CONFIG_SI1145) += si1145.o
obj-$(CONFIG_STK3310) += stk3310.o
obj-$(CONFIG_TCS3414) += tcs3414.o
obj-$(CONFIG_TCS3472) += tcs3472.o
diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c
new file mode 100644
index 000000000000..096034c126a4
--- /dev/null
+++ b/drivers/iio/light/si1145.c
@@ -0,0 +1,1404 @@
+/*
+ * si1145.c - Support for Silabs SI1132 and SI1141/2/3/5/6/7 combined ambient
+ * light, UV index and proximity sensors
+ *
+ * Copyright 2014-16 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
+ * Copyright 2016 Crestez Dan Leonard <leonard.crestez@intel.com>
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License. See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * SI1132 (7-bit I2C slave address 0x60)
+ * SI1141/2/3 (7-bit I2C slave address 0x5a)
+ * SI1145/6/6 (7-bit I2C slave address 0x60)
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/buffer.h>
+#include <linux/util_macros.h>
+
+#define SI1145_REG_PART_ID 0x00
+#define SI1145_REG_REV_ID 0x01
+#define SI1145_REG_SEQ_ID 0x02
+#define SI1145_REG_INT_CFG 0x03
+#define SI1145_REG_IRQ_ENABLE 0x04
+#define SI1145_REG_IRQ_MODE 0x05
+#define SI1145_REG_HW_KEY 0x07
+#define SI1145_REG_MEAS_RATE 0x08
+#define SI1145_REG_PS_LED21 0x0f
+#define SI1145_REG_PS_LED3 0x10
+#define SI1145_REG_UCOEF1 0x13
+#define SI1145_REG_UCOEF2 0x14
+#define SI1145_REG_UCOEF3 0x15
+#define SI1145_REG_UCOEF4 0x16
+#define SI1145_REG_PARAM_WR 0x17
+#define SI1145_REG_COMMAND 0x18
+#define SI1145_REG_RESPONSE 0x20
+#define SI1145_REG_IRQ_STATUS 0x21
+#define SI1145_REG_ALSVIS_DATA 0x22
+#define SI1145_REG_ALSIR_DATA 0x24
+#define SI1145_REG_PS1_DATA 0x26
+#define SI1145_REG_PS2_DATA 0x28
+#define SI1145_REG_PS3_DATA 0x2a
+#define SI1145_REG_AUX_DATA 0x2c
+#define SI1145_REG_PARAM_RD 0x2e
+#define SI1145_REG_CHIP_STAT 0x30
+
+#define SI1145_UCOEF1_DEFAULT 0x7b
+#define SI1145_UCOEF2_DEFAULT 0x6b
+#define SI1145_UCOEF3_DEFAULT 0x01
+#define SI1145_UCOEF4_DEFAULT 0x00
+
+/* Helper to figure out PS_LED register / shift per channel */
+#define SI1145_PS_LED_REG(ch) \
+ (((ch) == 2) ? SI1145_REG_PS_LED3 : SI1145_REG_PS_LED21)
+#define SI1145_PS_LED_SHIFT(ch) \
+ (((ch) == 1) ? 4 : 0)
+
+/* Parameter offsets */
+#define SI1145_PARAM_CHLIST 0x01
+#define SI1145_PARAM_PSLED12_SELECT 0x02
+#define SI1145_PARAM_PSLED3_SELECT 0x03
+#define SI1145_PARAM_PS_ENCODING 0x05
+#define SI1145_PARAM_ALS_ENCODING 0x06
+#define SI1145_PARAM_PS1_ADC_MUX 0x07
+#define SI1145_PARAM_PS2_ADC_MUX 0x08
+#define SI1145_PARAM_PS3_ADC_MUX 0x09
+#define SI1145_PARAM_PS_ADC_COUNTER 0x0a
+#define SI1145_PARAM_PS_ADC_GAIN 0x0b
+#define SI1145_PARAM_PS_ADC_MISC 0x0c
+#define SI1145_PARAM_ALS_ADC_MUX 0x0d
+#define SI1145_PARAM_ALSIR_ADC_MUX 0x0e
+#define SI1145_PARAM_AUX_ADC_MUX 0x0f
+#define SI1145_PARAM_ALSVIS_ADC_COUNTER 0x10
+#define SI1145_PARAM_ALSVIS_ADC_GAIN 0x11
+#define SI1145_PARAM_ALSVIS_ADC_MISC 0x12
+#define SI1145_PARAM_LED_RECOVERY 0x1c
+#define SI1145_PARAM_ALSIR_ADC_COUNTER 0x1d
+#define SI1145_PARAM_ALSIR_ADC_GAIN 0x1e
+#define SI1145_PARAM_ALSIR_ADC_MISC 0x1f
+#define SI1145_PARAM_ADC_OFFSET 0x1a
+
+/* Channel enable masks for CHLIST parameter */
+#define SI1145_CHLIST_EN_PS1 BIT(0)
+#define SI1145_CHLIST_EN_PS2 BIT(1)
+#define SI1145_CHLIST_EN_PS3 BIT(2)
+#define SI1145_CHLIST_EN_ALSVIS BIT(4)
+#define SI1145_CHLIST_EN_ALSIR BIT(5)
+#define SI1145_CHLIST_EN_AUX BIT(6)
+#define SI1145_CHLIST_EN_UV BIT(7)
+
+/* Proximity measurement mode for ADC_MISC parameter */
+#define SI1145_PS_ADC_MODE_NORMAL BIT(2)
+/* Signal range mask for ADC_MISC parameter */
+#define SI1145_ADC_MISC_RANGE BIT(5)
+
+/* Commands for REG_COMMAND */
+#define SI1145_CMD_NOP 0x00
+#define SI1145_CMD_RESET 0x01
+#define SI1145_CMD_PS_FORCE 0x05
+#define SI1145_CMD_ALS_FORCE 0x06
+#define SI1145_CMD_PSALS_FORCE 0x07
+#define SI1145_CMD_PS_PAUSE 0x09
+#define SI1145_CMD_ALS_PAUSE 0x0a
+#define SI1145_CMD_PSALS_PAUSE 0x0b
+#define SI1145_CMD_PS_AUTO 0x0d
+#define SI1145_CMD_ALS_AUTO 0x0e
+#define SI1145_CMD_PSALS_AUTO 0x0f
+#define SI1145_CMD_PARAM_QUERY 0x80
+#define SI1145_CMD_PARAM_SET 0xa0
+
+#define SI1145_RSP_INVALID_SETTING 0x80
+#define SI1145_RSP_COUNTER_MASK 0x0F
+
+/* Minimum sleep after each command to ensure it's received */
+#define SI1145_COMMAND_MINSLEEP_MS 5
+/* Return -ETIMEDOUT after this long */
+#define SI1145_COMMAND_TIMEOUT_MS 25
+
+/* Interrupt configuration masks for INT_CFG register */
+#define SI1145_INT_CFG_OE BIT(0) /* enable interrupt */
+#define SI1145_INT_CFG_MODE BIT(1) /* auto reset interrupt pin */
+
+/* Interrupt enable masks for IRQ_ENABLE register */
+#define SI1145_MASK_ALL_IE (BIT(4) | BIT(3) | BIT(2) | BIT(0))
+
+#define SI1145_MUX_TEMP 0x65
+#define SI1145_MUX_VDD 0x75
+
+/* Proximity LED current; see Table 2 in datasheet */
+#define SI1145_LED_CURRENT_45mA 0x04
+
+enum {
+ SI1132,
+ SI1141,
+ SI1142,
+ SI1143,
+ SI1145,
+ SI1146,
+ SI1147,
+};
+
+struct si1145_part_info {
+ u8 part;
+ const struct iio_info *iio_info;
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ unsigned int num_leds;
+ bool uncompressed_meas_rate;
+};
+
+/**
+ * struct si1145_data - si1145 chip state data
+ * @client: I2C client
+ * @lock: mutex to protect shared state.
+ * @cmdlock: Low-level mutex to protect command execution only
+ * @rsp_seq: Next expected response number or -1 if counter reset required
+ * @scan_mask: Saved scan mask to avoid duplicate set_chlist
+ * @autonomous: If automatic measurements are active (for buffer support)
+ * @part_info: Part information
+ * @trig: Pointer to iio trigger
+ * @meas_rate: Value of MEAS_RATE register. Only set in HW in auto mode
+ */
+struct si1145_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ struct mutex cmdlock;
+ int rsp_seq;
+ const struct si1145_part_info *part_info;
+ unsigned long scan_mask;
+ bool autonomous;
+ struct iio_trigger *trig;
+ int meas_rate;
+};
+
+/**
+ * __si1145_command_reset() - Send CMD_NOP and wait for response 0
+ *
+ * Does not modify data->rsp_seq
+ *
+ * Return: 0 on success and -errno on error.
+ */
+static int __si1145_command_reset(struct si1145_data *data)
+{
+ struct device *dev = &data->client->dev;
+ unsigned long stop_jiffies;
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND,
+ SI1145_CMD_NOP);
+ if (ret < 0)
+ return ret;
+ msleep(SI1145_COMMAND_MINSLEEP_MS);
+
+ stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000;
+ while (true) {
+ ret = i2c_smbus_read_byte_data(data->client,
+ SI1145_REG_RESPONSE);
+ if (ret <= 0)
+ return ret;
+ if (time_after(jiffies, stop_jiffies)) {
+ dev_warn(dev, "timeout on reset\n");
+ return -ETIMEDOUT;
+ }
+ msleep(SI1145_COMMAND_MINSLEEP_MS);
+ continue;
+ }
+}
+
+/**
+ * si1145_command() - Execute a command and poll the response register
+ *
+ * All conversion overflows are reported as -EOVERFLOW
+ * INVALID_SETTING is reported as -EINVAL
+ * Timeouts are reported as -ETIMEDOUT
+ *
+ * Return: 0 on success or -errno on failure
+ */
+static int si1145_command(struct si1145_data *data, u8 cmd)
+{
+ struct device *dev = &data->client->dev;
+ unsigned long stop_jiffies;
+ int ret;
+
+ mutex_lock(&data->cmdlock);
+
+ if (data->rsp_seq < 0) {
+ ret = __si1145_command_reset(data);
+ if (ret < 0) {
+ dev_err(dev, "failed to reset command counter, ret=%d\n",
+ ret);
+ goto out;
+ }
+ data->rsp_seq = 0;
+ }
+
+ ret = i2c_smbus_write_byte_data(data->client, SI1145_REG_COMMAND, cmd);
+ if (ret) {
+ dev_warn(dev, "failed to write command, ret=%d\n", ret);
+ goto out;
+ }
+ /* Sleep a little to ensure the command is received */
+ msleep(SI1145_COMMAND_MINSLEEP_MS);
+
+ stop_jiffies = jiffies + SI1145_COMMAND_TIMEOUT_MS * HZ / 1000;
+ while (true) {
+ ret = i2c_smbus_read_byte_data(data->client,
+ SI1145_REG_RESPONSE);
+ if (ret < 0) {
+ dev_warn(dev, "failed to read response, ret=%d\n", ret);
+ break;
+ }
+
+ if ((ret & ~SI1145_RSP_COUNTER_MASK) == 0) {
+ if (ret == data->rsp_seq) {
+ if (time_after(jiffies, stop_jiffies)) {
+ dev_warn(dev, "timeout on command %#02hhx\n",
+ cmd);
+ ret = -ETIMEDOUT;
+ break;
+ }
+ msleep(SI1145_COMMAND_MINSLEEP_MS);
+ continue;
+ }
+ if (ret == ((data->rsp_seq + 1) &
+ SI1145_RSP_COUNTER_MASK)) {
+ data->rsp_seq = ret;
+ ret = 0;
+ break;
+ }
+ dev_warn(dev, "unexpected response counter %d instead of %d\n",
+ ret, (data->rsp_seq + 1) &
+ SI1145_RSP_COUNTER_MASK);
+ ret = -EIO;
+ } else {
+ if (ret == SI1145_RSP_INVALID_SETTING) {
+ dev_warn(dev, "INVALID_SETTING error on command %#02hhx\n",
+ cmd);
+ ret = -EINVAL;
+ } else {
+ /* All overflows are treated identically */
+ dev_dbg(dev, "overflow, ret=%d, cmd=%#02hhx\n",
+ ret, cmd);
+ ret = -EOVERFLOW;
+ }
+ }
+
+ /* Force a counter reset next time */
+ data->rsp_seq = -1;
+ break;
+ }
+
+out:
+ mutex_unlock(&data->cmdlock);
+
+ return ret;
+}
+
+static int si1145_param_update(struct si1145_data *data, u8 op, u8 param,
+ u8 value)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_PARAM_WR, value);
+ if (ret < 0)
+ return ret;
+
+ return si1145_command(data, op | (param & 0x1F));
+}
+
+static int si1145_param_set(struct si1145_data *data, u8 param, u8 value)
+{
+ return si1145_param_update(data, SI1145_CMD_PARAM_SET, param, value);
+}
+
+/* Set param. Returns negative errno or current value */
+static int si1145_param_query(struct si1145_data *data, u8 param)
+{
+ int ret;
+
+ ret = si1145_command(data, SI1145_CMD_PARAM_QUERY | (param & 0x1F));
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_read_byte_data(data->client, SI1145_REG_PARAM_RD);
+}
+
+/* Expand 8 bit compressed value to 16 bit, see Silabs AN498 */
+static u16 si1145_uncompress(u8 x)
+{
+ u16 result = 0;
+ u8 exponent = 0;
+
+ if (x < 8)
+ return 0;
+
+ exponent = (x & 0xf0) >> 4;
+ result = 0x10 | (x & 0x0f);
+
+ if (exponent >= 4)
+ return result << (exponent - 4);
+ return result >> (4 - exponent);
+}
+
+/* Compress 16 bit value to 8 bit, see Silabs AN498 */
+static u8 si1145_compress(u16 x)
+{
+ u32 exponent = 0;
+ u32 significand = 0;
+ u32 tmp = x;
+
+ if (x == 0x0000)
+ return 0x00;
+ if (x == 0x0001)
+ return 0x08;
+
+ while (1) {
+ tmp >>= 1;
+ exponent += 1;
+ if (tmp == 1)
+ break;
+ }
+
+ if (exponent < 5) {
+ significand = x << (4 - exponent);
+ return (exponent << 4) | (significand & 0xF);
+ }
+
+ significand = x >> (exponent - 5);
+ if (significand & 1) {
+ significand += 2;
+ if (significand & 0x0040) {
+ exponent += 1;
+ significand >>= 1;
+ }
+ }
+
+ return (exponent << 4) | ((significand >> 1) & 0xF);
+}
+
+/* Write meas_rate in hardware */
+static int si1145_set_meas_rate(struct si1145_data *data, int interval)
+{
+ if (data->part_info->uncompressed_meas_rate)
+ return i2c_smbus_write_word_data(data->client,
+ SI1145_REG_MEAS_RATE, interval);
+ else
+ return i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_MEAS_RATE, interval);
+}
+
+static int si1145_read_samp_freq(struct si1145_data *data, int *val, int *val2)
+{
+ *val = 32000;
+ if (data->part_info->uncompressed_meas_rate)
+ *val2 = data->meas_rate;
+ else
+ *val2 = si1145_uncompress(data->meas_rate);
+ return IIO_VAL_FRACTIONAL;
+}
+
+/* Set the samp freq in driver private data */
+static int si1145_store_samp_freq(struct si1145_data *data, int val)
+{
+ int ret = 0;
+ int meas_rate;
+
+ if (val <= 0 || val > 32000)
+ return -ERANGE;
+ meas_rate = 32000 / val;
+
+ mutex_lock(&data->lock);
+ if (data->autonomous) {
+ ret = si1145_set_meas_rate(data, meas_rate);
+ if (ret)
+ goto out;
+ }
+ if (data->part_info->uncompressed_meas_rate)
+ data->meas_rate = meas_rate;
+ else
+ data->meas_rate = si1145_compress(meas_rate);
+
+out:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static irqreturn_t si1145_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct si1145_data *data = iio_priv(indio_dev);
+ /*
+ * Maximum buffer size:
+ * 6*2 bytes channels data + 4 bytes alignment +
+ * 8 bytes timestamp
+ */
+ u8 buffer[24];
+ int i, j = 0;
+ int ret;
+ u8 irq_status = 0;
+
+ if (!data->autonomous) {
+ ret = si1145_command(data, SI1145_CMD_PSALS_FORCE);
+ if (ret < 0 && ret != -EOVERFLOW)
+ goto done;
+ } else {
+ irq_status = ret = i2c_smbus_read_byte_data(data->client,
+ SI1145_REG_IRQ_STATUS);
+ if (ret < 0)
+ goto done;
+ if (!(irq_status & SI1145_MASK_ALL_IE))
+ goto done;
+ }
+
+ for_each_set_bit(i, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ int run = 1;
+
+ while (i + run < indio_dev->masklength) {
+ if (!test_bit(i + run, indio_dev->active_scan_mask))
+ break;
+ if (indio_dev->channels[i + run].address !=
+ indio_dev->channels[i].address + 2 * run)
+ break;
+ run++;
+ }
+
+ ret = i2c_smbus_read_i2c_block_data_or_emulated(
+ data->client, indio_dev->channels[i].address,
+ sizeof(u16) * run, &buffer[j]);
+ if (ret < 0)
+ goto done;
+ j += run * sizeof(u16);
+ i += run - 1;
+ }
+
+ if (data->autonomous) {
+ ret = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_IRQ_STATUS,
+ irq_status & SI1145_MASK_ALL_IE);
+ if (ret < 0)
+ goto done;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ iio_get_time_ns(indio_dev));
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int si1145_set_chlist(struct iio_dev *indio_dev, unsigned long scan_mask)
+{
+ struct si1145_data *data = iio_priv(indio_dev);
+ u8 reg = 0, mux;
+ int ret;
+ int i;
+
+ /* channel list already set, no need to reprogram */
+ if (data->scan_mask == scan_mask)
+ return 0;
+
+ for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
+ switch (indio_dev->channels[i].address) {
+ case SI1145_REG_ALSVIS_DATA:
+ reg |= SI1145_CHLIST_EN_ALSVIS;
+ break;
+ case SI1145_REG_ALSIR_DATA:
+ reg |= SI1145_CHLIST_EN_ALSIR;
+ break;
+ case SI1145_REG_PS1_DATA:
+ reg |= SI1145_CHLIST_EN_PS1;
+ break;
+ case SI1145_REG_PS2_DATA:
+ reg |= SI1145_CHLIST_EN_PS2;
+ break;
+ case SI1145_REG_PS3_DATA:
+ reg |= SI1145_CHLIST_EN_PS3;
+ break;
+ case SI1145_REG_AUX_DATA:
+ switch (indio_dev->channels[i].type) {
+ case IIO_UVINDEX:
+ reg |= SI1145_CHLIST_EN_UV;
+ break;
+ default:
+ reg |= SI1145_CHLIST_EN_AUX;
+ if (indio_dev->channels[i].type == IIO_TEMP)
+ mux = SI1145_MUX_TEMP;
+ else
+ mux = SI1145_MUX_VDD;
+ ret = si1145_param_set(data,
+ SI1145_PARAM_AUX_ADC_MUX, mux);
+ if (ret < 0)
+ return ret;
+
+ break;
+ }
+ }
+ }
+
+ data->scan_mask = scan_mask;
+ ret = si1145_param_set(data, SI1145_PARAM_CHLIST, reg);
+
+ return ret < 0 ? ret : 0;
+}
+
+static int si1145_measure(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ struct si1145_data *data = iio_priv(indio_dev);
+ u8 cmd;
+ int ret;
+
+ ret = si1145_set_chlist(indio_dev, BIT(chan->scan_index));
+ if (ret < 0)
+ return ret;
+
+ cmd = (chan->type == IIO_PROXIMITY) ? SI1145_CMD_PS_FORCE :
+ SI1145_CMD_ALS_FORCE;
+ ret = si1145_command(data, cmd);
+ if (ret < 0 && ret != -EOVERFLOW)
+ return ret;
+
+ return i2c_smbus_read_word_data(data->client, chan->address);
+}
+
+/*
+ * Conversion between iio scale and ADC_GAIN values
+ * These could be further adjusted but proximity/intensity are dimensionless
+ */
+static const int si1145_proximity_scale_available[] = {
+ 128, 64, 32, 16, 8, 4};
+static const int si1145_intensity_scale_available[] = {
+ 128, 64, 32, 16, 8, 4, 2, 1};
+static IIO_CONST_ATTR(in_proximity_scale_available,
+ "128 64 32 16 8 4");
+static IIO_CONST_ATTR(in_intensity_scale_available,
+ "128 64 32 16 8 4 2 1");
+static IIO_CONST_ATTR(in_intensity_ir_scale_available,
+ "128 64 32 16 8 4 2 1");
+
+static int si1145_scale_from_adcgain(int regval)
+{
+ return 128 >> regval;
+}
+
+static int si1145_proximity_adcgain_from_scale(int val, int val2)
+{
+ val = find_closest_descending(val, si1145_proximity_scale_available,
+ ARRAY_SIZE(si1145_proximity_scale_available));
+ if (val < 0 || val > 5 || val2 != 0)
+ return -EINVAL;
+
+ return val;
+}
+
+static int si1145_intensity_adcgain_from_scale(int val, int val2)
+{
+ val = find_closest_descending(val, si1145_intensity_scale_available,
+ ARRAY_SIZE(si1145_intensity_scale_available));
+ if (val < 0 || val > 7 || val2 != 0)
+ return -EINVAL;
+
+ return val;
+}
+
+static int si1145_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct si1145_data *data = iio_priv(indio_dev);
+ int ret;
+ u8 reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_INTENSITY:
+ case IIO_PROXIMITY:
+ case IIO_VOLTAGE:
+ case IIO_TEMP:
+ case IIO_UVINDEX:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = si1145_measure(indio_dev, chan);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CURRENT:
+ ret = i2c_smbus_read_byte_data(data->client,
+ SI1145_PS_LED_REG(chan->channel));
+ if (ret < 0)
+ return ret;
+
+ *val = (ret >> SI1145_PS_LED_SHIFT(chan->channel))
+ & 0x0f;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ reg = SI1145_PARAM_PS_ADC_GAIN;
+ break;
+ case IIO_INTENSITY:
+ if (chan->channel2 == IIO_MOD_LIGHT_IR)
+ reg = SI1145_PARAM_ALSIR_ADC_GAIN;
+ else
+ reg = SI1145_PARAM_ALSVIS_ADC_GAIN;
+ break;
+ case IIO_TEMP:
+ *val = 28;
+ *val2 = 571429;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_UVINDEX:
+ *val = 0;
+ *val2 = 10000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+
+ ret = si1145_param_query(data, reg);
+ if (ret < 0)
+ return ret;
+
+ *val = si1145_scale_from_adcgain(ret & 0x07);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ /*
+ * -ADC offset - ADC counts @ 25°C -
+ * 35 * ADC counts / °C
+ */
+ *val = -256 - 11136 + 25 * 35;
+ return IIO_VAL_INT;
+ default:
+ /*
+ * All ADC measurements have are by default offset
+ * by -256
+ * See AN498 5.6.3
+ */
+ ret = si1145_param_query(data, SI1145_PARAM_ADC_OFFSET);
+ if (ret < 0)
+ return ret;
+ *val = -si1145_uncompress(ret);
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return si1145_read_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int si1145_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct si1145_data *data = iio_priv(indio_dev);
+ u8 reg1, reg2, shift;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PROXIMITY:
+ val = si1145_proximity_adcgain_from_scale(val, val2);
+ if (val < 0)
+ return val;
+ reg1 = SI1145_PARAM_PS_ADC_GAIN;
+ reg2 = SI1145_PARAM_PS_ADC_COUNTER;
+ break;
+ case IIO_INTENSITY:
+ val = si1145_intensity_adcgain_from_scale(val, val2);
+ if (val < 0)
+ return val;
+ if (chan->channel2 == IIO_MOD_LIGHT_IR) {
+ reg1 = SI1145_PARAM_ALSIR_ADC_GAIN;
+ reg2 = SI1145_PARAM_ALSIR_ADC_COUNTER;
+ } else {
+ reg1 = SI1145_PARAM_ALSVIS_ADC_GAIN;
+ reg2 = SI1145_PARAM_ALSVIS_ADC_COUNTER;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = si1145_param_set(data, reg1, val);
+ if (ret < 0) {
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ }
+ /* Set recovery period to one's complement of gain */
+ ret = si1145_param_set(data, reg2, (~val & 0x07) << 4);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type != IIO_CURRENT)
+ return -EINVAL;
+
+ if (val < 0 || val > 15 || val2 != 0)
+ return -EINVAL;
+
+ reg1 = SI1145_PS_LED_REG(chan->channel);
+ shift = SI1145_PS_LED_SHIFT(chan->channel);
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, reg1);
+ if (ret < 0) {
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ }
+ ret = i2c_smbus_write_byte_data(data->client, reg1,
+ (ret & ~(0x0f << shift)) |
+ ((val & 0x0f) << shift));
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return si1145_store_samp_freq(data, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+#define SI1145_ST { \
+ .sign = 'u', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+}
+
+#define SI1145_INTENSITY_CHANNEL(_si) { \
+ .type = IIO_INTENSITY, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_type = SI1145_ST, \
+ .scan_index = _si, \
+ .address = SI1145_REG_ALSVIS_DATA, \
+}
+
+#define SI1145_INTENSITY_IR_CHANNEL(_si) { \
+ .type = IIO_INTENSITY, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .modified = 1, \
+ .channel2 = IIO_MOD_LIGHT_IR, \
+ .scan_type = SI1145_ST, \
+ .scan_index = _si, \
+ .address = SI1145_REG_ALSIR_DATA, \
+}
+
+#define SI1145_TEMP_CHANNEL(_si) { \
+ .type = IIO_TEMP, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_type = SI1145_ST, \
+ .scan_index = _si, \
+ .address = SI1145_REG_AUX_DATA, \
+}
+
+#define SI1145_UV_CHANNEL(_si) { \
+ .type = IIO_UVINDEX, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_type = SI1145_ST, \
+ .scan_index = _si, \
+ .address = SI1145_REG_AUX_DATA, \
+}
+
+#define SI1145_PROXIMITY_CHANNEL(_si, _ch) { \
+ .type = IIO_PROXIMITY, \
+ .indexed = 1, \
+ .channel = _ch, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_type = SI1145_ST, \
+ .scan_index = _si, \
+ .address = SI1145_REG_PS1_DATA + _ch * 2, \
+}
+
+#define SI1145_VOLTAGE_CHANNEL(_si) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_type = SI1145_ST, \
+ .scan_index = _si, \
+ .address = SI1145_REG_AUX_DATA, \
+}
+
+#define SI1145_CURRENT_CHANNEL(_ch) { \
+ .type = IIO_CURRENT, \
+ .indexed = 1, \
+ .channel = _ch, \
+ .output = 1, \
+ .scan_index = -1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+}
+
+static const struct iio_chan_spec si1132_channels[] = {
+ SI1145_INTENSITY_CHANNEL(0),
+ SI1145_INTENSITY_IR_CHANNEL(1),
+ SI1145_TEMP_CHANNEL(2),
+ SI1145_VOLTAGE_CHANNEL(3),
+ SI1145_UV_CHANNEL(4),
+ IIO_CHAN_SOFT_TIMESTAMP(6),
+};
+
+static const struct iio_chan_spec si1141_channels[] = {
+ SI1145_INTENSITY_CHANNEL(0),
+ SI1145_INTENSITY_IR_CHANNEL(1),
+ SI1145_PROXIMITY_CHANNEL(2, 0),
+ SI1145_TEMP_CHANNEL(3),
+ SI1145_VOLTAGE_CHANNEL(4),
+ IIO_CHAN_SOFT_TIMESTAMP(5),
+ SI1145_CURRENT_CHANNEL(0),
+};
+
+static const struct iio_chan_spec si1142_channels[] = {
+ SI1145_INTENSITY_CHANNEL(0),
+ SI1145_INTENSITY_IR_CHANNEL(1),
+ SI1145_PROXIMITY_CHANNEL(2, 0),
+ SI1145_PROXIMITY_CHANNEL(3, 1),
+ SI1145_TEMP_CHANNEL(4),
+ SI1145_VOLTAGE_CHANNEL(5),
+ IIO_CHAN_SOFT_TIMESTAMP(6),
+ SI1145_CURRENT_CHANNEL(0),
+ SI1145_CURRENT_CHANNEL(1),
+};
+
+static const struct iio_chan_spec si1143_channels[] = {
+ SI1145_INTENSITY_CHANNEL(0),
+ SI1145_INTENSITY_IR_CHANNEL(1),
+ SI1145_PROXIMITY_CHANNEL(2, 0),
+ SI1145_PROXIMITY_CHANNEL(3, 1),
+ SI1145_PROXIMITY_CHANNEL(4, 2),
+ SI1145_TEMP_CHANNEL(5),
+ SI1145_VOLTAGE_CHANNEL(6),
+ IIO_CHAN_SOFT_TIMESTAMP(7),
+ SI1145_CURRENT_CHANNEL(0),
+ SI1145_CURRENT_CHANNEL(1),
+ SI1145_CURRENT_CHANNEL(2),
+};
+
+static const struct iio_chan_spec si1145_channels[] = {
+ SI1145_INTENSITY_CHANNEL(0),
+ SI1145_INTENSITY_IR_CHANNEL(1),
+ SI1145_PROXIMITY_CHANNEL(2, 0),
+ SI1145_TEMP_CHANNEL(3),
+ SI1145_VOLTAGE_CHANNEL(4),
+ SI1145_UV_CHANNEL(5),
+ IIO_CHAN_SOFT_TIMESTAMP(6),
+ SI1145_CURRENT_CHANNEL(0),
+};
+
+static const struct iio_chan_spec si1146_channels[] = {
+ SI1145_INTENSITY_CHANNEL(0),
+ SI1145_INTENSITY_IR_CHANNEL(1),
+ SI1145_TEMP_CHANNEL(2),
+ SI1145_VOLTAGE_CHANNEL(3),
+ SI1145_UV_CHANNEL(4),
+ SI1145_PROXIMITY_CHANNEL(5, 0),
+ SI1145_PROXIMITY_CHANNEL(6, 1),
+ IIO_CHAN_SOFT_TIMESTAMP(7),
+ SI1145_CURRENT_CHANNEL(0),
+ SI1145_CURRENT_CHANNEL(1),
+};
+
+static const struct iio_chan_spec si1147_channels[] = {
+ SI1145_INTENSITY_CHANNEL(0),
+ SI1145_INTENSITY_IR_CHANNEL(1),
+ SI1145_PROXIMITY_CHANNEL(2, 0),
+ SI1145_PROXIMITY_CHANNEL(3, 1),
+ SI1145_PROXIMITY_CHANNEL(4, 2),
+ SI1145_TEMP_CHANNEL(5),
+ SI1145_VOLTAGE_CHANNEL(6),
+ SI1145_UV_CHANNEL(7),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ SI1145_CURRENT_CHANNEL(0),
+ SI1145_CURRENT_CHANNEL(1),
+ SI1145_CURRENT_CHANNEL(2),
+};
+
+static struct attribute *si1132_attributes[] = {
+ &iio_const_attr_in_intensity_scale_available.dev_attr.attr,
+ &iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static struct attribute *si114x_attributes[] = {
+ &iio_const_attr_in_intensity_scale_available.dev_attr.attr,
+ &iio_const_attr_in_intensity_ir_scale_available.dev_attr.attr,
+ &iio_const_attr_in_proximity_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group si1132_attribute_group = {
+ .attrs = si1132_attributes,
+};
+
+static const struct attribute_group si114x_attribute_group = {
+ .attrs = si114x_attributes,
+};
+
+
+static const struct iio_info si1132_info = {
+ .read_raw = si1145_read_raw,
+ .write_raw = si1145_write_raw,
+ .driver_module = THIS_MODULE,
+ .attrs = &si1132_attribute_group,
+};
+
+static const struct iio_info si114x_info = {
+ .read_raw = si1145_read_raw,
+ .write_raw = si1145_write_raw,
+ .driver_module = THIS_MODULE,
+ .attrs = &si114x_attribute_group,
+};
+
+#define SI1145_PART(id, iio_info, chans, leds, uncompressed_meas_rate) \
+ {id, iio_info, chans, ARRAY_SIZE(chans), leds, uncompressed_meas_rate}
+
+static const struct si1145_part_info si1145_part_info[] = {
+ [SI1132] = SI1145_PART(0x32, &si1132_info, si1132_channels, 0, true),
+ [SI1141] = SI1145_PART(0x41, &si114x_info, si1141_channels, 1, false),
+ [SI1142] = SI1145_PART(0x42, &si114x_info, si1142_channels, 2, false),
+ [SI1143] = SI1145_PART(0x43, &si114x_info, si1143_channels, 3, false),
+ [SI1145] = SI1145_PART(0x45, &si114x_info, si1145_channels, 1, true),
+ [SI1146] = SI1145_PART(0x46, &si114x_info, si1146_channels, 2, true),
+ [SI1147] = SI1145_PART(0x47, &si114x_info, si1147_channels, 3, true),
+};
+
+static int si1145_initialize(struct si1145_data *data)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, SI1145_REG_COMMAND,
+ SI1145_CMD_RESET);
+ if (ret < 0)
+ return ret;
+ msleep(SI1145_COMMAND_TIMEOUT_MS);
+
+ /* Hardware key, magic value */
+ ret = i2c_smbus_write_byte_data(client, SI1145_REG_HW_KEY, 0x17);
+ if (ret < 0)
+ return ret;
+ msleep(SI1145_COMMAND_TIMEOUT_MS);
+
+ /* Turn off autonomous mode */
+ ret = si1145_set_meas_rate(data, 0);
+ if (ret < 0)
+ return ret;
+
+ /* Initialize sampling freq to 10 Hz */
+ ret = si1145_store_samp_freq(data, 10);
+ if (ret < 0)
+ return ret;
+
+ /* Set LED currents to 45 mA; have 4 bits, see Table 2 in datasheet */
+ switch (data->part_info->num_leds) {
+ case 3:
+ ret = i2c_smbus_write_byte_data(client,
+ SI1145_REG_PS_LED3,
+ SI1145_LED_CURRENT_45mA);
+ if (ret < 0)
+ return ret;
+ /* fallthrough */
+ case 2:
+ ret = i2c_smbus_write_byte_data(client,
+ SI1145_REG_PS_LED21,
+ (SI1145_LED_CURRENT_45mA << 4) |
+ SI1145_LED_CURRENT_45mA);
+ break;
+ case 1:
+ ret = i2c_smbus_write_byte_data(client,
+ SI1145_REG_PS_LED21,
+ SI1145_LED_CURRENT_45mA);
+ break;
+ default:
+ ret = 0;
+ break;
+ }
+ if (ret < 0)
+ return ret;
+
+ /* Set normal proximity measurement mode */
+ ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_MISC,
+ SI1145_PS_ADC_MODE_NORMAL);
+ if (ret < 0)
+ return ret;
+
+ ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_GAIN, 0x01);
+ if (ret < 0)
+ return ret;
+
+ /* ADC_COUNTER should be one complement of ADC_GAIN */
+ ret = si1145_param_set(data, SI1145_PARAM_PS_ADC_COUNTER, 0x06 << 4);
+ if (ret < 0)
+ return ret;
+
+ /* Set ALS visible measurement mode */
+ ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_MISC,
+ SI1145_ADC_MISC_RANGE);
+ if (ret < 0)
+ return ret;
+
+ ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_GAIN, 0x03);
+ if (ret < 0)
+ return ret;
+
+ ret = si1145_param_set(data, SI1145_PARAM_ALSVIS_ADC_COUNTER,
+ 0x04 << 4);
+ if (ret < 0)
+ return ret;
+
+ /* Set ALS IR measurement mode */
+ ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_MISC,
+ SI1145_ADC_MISC_RANGE);
+ if (ret < 0)
+ return ret;
+
+ ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_GAIN, 0x01);
+ if (ret < 0)
+ return ret;
+
+ ret = si1145_param_set(data, SI1145_PARAM_ALSIR_ADC_COUNTER,
+ 0x06 << 4);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Initialize UCOEF to default values in datasheet
+ * These registers are normally zero on reset
+ */
+ if (data->part_info == &si1145_part_info[SI1132] ||
+ data->part_info == &si1145_part_info[SI1145] ||
+ data->part_info == &si1145_part_info[SI1146] ||
+ data->part_info == &si1145_part_info[SI1147]) {
+ ret = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_UCOEF1,
+ SI1145_UCOEF1_DEFAULT);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_UCOEF2, SI1145_UCOEF2_DEFAULT);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_UCOEF3, SI1145_UCOEF3_DEFAULT);
+ if (ret < 0)
+ return ret;
+ ret = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_UCOEF4, SI1145_UCOEF4_DEFAULT);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/*
+ * Program the channels we want to measure with CMD_PSALS_AUTO. No need for
+ * _postdisable as we stop with CMD_PSALS_PAUSE; single measurement (direct)
+ * mode reprograms the channels list anyway...
+ */
+static int si1145_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct si1145_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = si1145_set_chlist(indio_dev, *indio_dev->active_scan_mask);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static bool si1145_validate_scan_mask(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct si1145_data *data = iio_priv(indio_dev);
+ unsigned int count = 0;
+ int i;
+
+ /* Check that at most one AUX channel is enabled */
+ for_each_set_bit(i, scan_mask, data->part_info->num_channels) {
+ if (indio_dev->channels[i].address == SI1145_REG_AUX_DATA)
+ count++;
+ }
+
+ return count <= 1;
+}
+
+static const struct iio_buffer_setup_ops si1145_buffer_setup_ops = {
+ .preenable = si1145_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .validate_scan_mask = si1145_validate_scan_mask,
+};
+
+/**
+ * si1145_trigger_set_state() - Set trigger state
+ *
+ * When not using triggers interrupts are disabled and measurement rate is
+ * set to zero in order to minimize power consumption.
+ */
+static int si1145_trigger_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct si1145_data *data = iio_priv(indio_dev);
+ int err = 0, ret;
+
+ mutex_lock(&data->lock);
+
+ if (state) {
+ data->autonomous = true;
+ err = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_INT_CFG, SI1145_INT_CFG_OE);
+ if (err < 0)
+ goto disable;
+ err = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_IRQ_ENABLE, SI1145_MASK_ALL_IE);
+ if (err < 0)
+ goto disable;
+ err = si1145_set_meas_rate(data, data->meas_rate);
+ if (err < 0)
+ goto disable;
+ err = si1145_command(data, SI1145_CMD_PSALS_AUTO);
+ if (err < 0)
+ goto disable;
+ } else {
+disable:
+ /* Disable as much as possible skipping errors */
+ ret = si1145_command(data, SI1145_CMD_PSALS_PAUSE);
+ if (ret < 0 && !err)
+ err = ret;
+ ret = si1145_set_meas_rate(data, 0);
+ if (ret < 0 && !err)
+ err = ret;
+ ret = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_IRQ_ENABLE, 0);
+ if (ret < 0 && !err)
+ err = ret;
+ ret = i2c_smbus_write_byte_data(data->client,
+ SI1145_REG_INT_CFG, 0);
+ if (ret < 0 && !err)
+ err = ret;
+ data->autonomous = false;
+ }
+
+ mutex_unlock(&data->lock);
+ return err;
+}
+
+static const struct iio_trigger_ops si1145_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = si1145_trigger_set_state,
+};
+
+static int si1145_probe_trigger(struct iio_dev *indio_dev)
+{
+ struct si1145_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ struct iio_trigger *trig;
+ int ret;
+
+ trig = devm_iio_trigger_alloc(&client->dev,
+ "%s-dev%d", indio_dev->name, indio_dev->id);
+ if (!trig)
+ return -ENOMEM;
+
+ trig->dev.parent = &client->dev;
+ trig->ops = &si1145_trigger_ops;
+ iio_trigger_set_drvdata(trig, indio_dev);
+
+ ret = devm_request_irq(&client->dev, client->irq,
+ iio_trigger_generic_data_rdy_poll,
+ IRQF_TRIGGER_FALLING,
+ "si1145_irq",
+ trig);
+ if (ret < 0) {
+ dev_err(&client->dev, "irq request failed\n");
+ return ret;
+ }
+
+ ret = iio_trigger_register(trig);
+ if (ret)
+ return ret;
+
+ data->trig = trig;
+ indio_dev->trig = iio_trigger_get(data->trig);
+
+ return 0;
+}
+
+static void si1145_remove_trigger(struct iio_dev *indio_dev)
+{
+ struct si1145_data *data = iio_priv(indio_dev);
+
+ if (data->trig) {
+ iio_trigger_unregister(data->trig);
+ data->trig = NULL;
+ }
+}
+
+static int si1145_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct si1145_data *data;
+ struct iio_dev *indio_dev;
+ u8 part_id, rev_id, seq_id;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ data->part_info = &si1145_part_info[id->driver_data];
+
+ part_id = ret = i2c_smbus_read_byte_data(data->client,
+ SI1145_REG_PART_ID);
+ if (ret < 0)
+ return ret;
+ rev_id = ret = i2c_smbus_read_byte_data(data->client,
+ SI1145_REG_REV_ID);
+ if (ret < 0)
+ return ret;
+ seq_id = ret = i2c_smbus_read_byte_data(data->client,
+ SI1145_REG_SEQ_ID);
+ if (ret < 0)
+ return ret;
+ dev_info(&client->dev, "device ID part %#02hhx rev %#02hhx seq %#02hhx\n",
+ part_id, rev_id, seq_id);
+ if (part_id != data->part_info->part) {
+ dev_err(&client->dev, "part ID mismatch got %#02hhx, expected %#02x\n",
+ part_id, data->part_info->part);
+ return -ENODEV;
+ }
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = id->name;
+ indio_dev->channels = data->part_info->channels;
+ indio_dev->num_channels = data->part_info->num_channels;
+ indio_dev->info = data->part_info->iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ mutex_init(&data->lock);
+ mutex_init(&data->cmdlock);
+
+ ret = si1145_initialize(data);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ si1145_trigger_handler, &si1145_buffer_setup_ops);
+ if (ret < 0)
+ return ret;
+
+ if (client->irq) {
+ ret = si1145_probe_trigger(indio_dev);
+ if (ret < 0)
+ goto error_free_buffer;
+ } else {
+ dev_info(&client->dev, "no irq, using polling\n");
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto error_free_trigger;
+
+ return 0;
+
+error_free_trigger:
+ si1145_remove_trigger(indio_dev);
+error_free_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+}
+
+static const struct i2c_device_id si1145_ids[] = {
+ { "si1132", SI1132 },
+ { "si1141", SI1141 },
+ { "si1142", SI1142 },
+ { "si1143", SI1143 },
+ { "si1145", SI1145 },
+ { "si1146", SI1146 },
+ { "si1147", SI1147 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, si1145_ids);
+
+static int si1145_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ si1145_remove_trigger(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return 0;
+}
+
+static struct i2c_driver si1145_driver = {
+ .driver = {
+ .name = "si1145",
+ },
+ .probe = si1145_probe,
+ .remove = si1145_remove,
+ .id_table = si1145_ids,
+};
+
+module_i2c_driver(si1145_driver);
+
+MODULE_AUTHOR("Peter Meerwald-Stadler <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("Silabs SI1132 and SI1141/2/3/5/6/7 proximity, ambient light and UV index sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c
index 20c40f780964..18cf2e29e4d5 100644
--- a/drivers/iio/light/us5182d.c
+++ b/drivers/iio/light/us5182d.c
@@ -894,7 +894,7 @@ static int us5182d_probe(struct i2c_client *client,
goto out_err;
if (data->default_continuous) {
- pm_runtime_set_active(&client->dev);
+ ret = pm_runtime_set_active(&client->dev);
if (ret < 0)
goto out_err;
}
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index c9d85bbc9230..360b6e98137a 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1,6 +1,6 @@
/*
- * vcnl4000.c - Support for Vishay VCNL4000 combined ambient light and
- * proximity sensor
+ * vcnl4000.c - Support for Vishay VCNL4000/4010/4020 combined ambient
+ * light and proximity sensor
*
* Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
*
@@ -13,6 +13,8 @@
* TODO:
* allow to adjust IR current
* proximity threshold and event handling
+ * periodic ALS/proximity measurement (VCNL4010/20)
+ * interrupts (VCNL4010/20)
*/
#include <linux/module.h>
@@ -24,6 +26,8 @@
#include <linux/iio/sysfs.h>
#define VCNL4000_DRV_NAME "vcnl4000"
+#define VCNL4000_ID 0x01
+#define VCNL4010_ID 0x02 /* for VCNL4020, VCNL4010 */
#define VCNL4000_COMMAND 0x80 /* Command register */
#define VCNL4000_PROD_REV 0x81 /* Product ID and Revision ID */
@@ -37,13 +41,14 @@
#define VCNL4000_PS_MOD_ADJ 0x8a /* Proximity modulator timing adjustment */
/* Bit masks for COMMAND register */
-#define VCNL4000_AL_RDY 0x40 /* ALS data ready? */
-#define VCNL4000_PS_RDY 0x20 /* proximity data ready? */
-#define VCNL4000_AL_OD 0x10 /* start on-demand ALS measurement */
-#define VCNL4000_PS_OD 0x08 /* start on-demand proximity measurement */
+#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
+#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
+#define VCNL4000_AL_OD BIT(4) /* start on-demand ALS measurement */
+#define VCNL4000_PS_OD BIT(3) /* start on-demand proximity measurement */
struct vcnl4000_data {
struct i2c_client *client;
+ struct mutex lock;
};
static const struct i2c_device_id vcnl4000_id[] = {
@@ -59,16 +64,18 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
__be16 buf;
int ret;
+ mutex_lock(&data->lock);
+
ret = i2c_smbus_write_byte_data(data->client, VCNL4000_COMMAND,
req_mask);
if (ret < 0)
- return ret;
+ goto fail;
/* wait for data to become ready */
while (tries--) {
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_COMMAND);
if (ret < 0)
- return ret;
+ goto fail;
if (ret & rdy_mask)
break;
msleep(20); /* measurement takes up to 100 ms */
@@ -77,17 +84,23 @@ static int vcnl4000_measure(struct vcnl4000_data *data, u8 req_mask,
if (tries < 0) {
dev_err(&data->client->dev,
"vcnl4000_measure() failed, data not ready\n");
- return -EIO;
+ ret = -EIO;
+ goto fail;
}
ret = i2c_smbus_read_i2c_block_data(data->client,
data_reg, sizeof(buf), (u8 *) &buf);
if (ret < 0)
- return ret;
+ goto fail;
+ mutex_unlock(&data->lock);
*val = be16_to_cpu(buf);
return 0;
+
+fail:
+ mutex_unlock(&data->lock);
+ return ret;
}
static const struct iio_chan_spec vcnl4000_channels[] = {
@@ -105,7 +118,7 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
- int ret = -EINVAL;
+ int ret;
struct vcnl4000_data *data = iio_priv(indio_dev);
switch (mask) {
@@ -117,32 +130,27 @@ static int vcnl4000_read_raw(struct iio_dev *indio_dev,
VCNL4000_AL_RESULT_HI, val);
if (ret < 0)
return ret;
- ret = IIO_VAL_INT;
- break;
+ return IIO_VAL_INT;
case IIO_PROXIMITY:
ret = vcnl4000_measure(data,
VCNL4000_PS_OD, VCNL4000_PS_RDY,
VCNL4000_PS_RESULT_HI, val);
if (ret < 0)
return ret;
- ret = IIO_VAL_INT;
- break;
+ return IIO_VAL_INT;
default:
- break;
+ return -EINVAL;
}
- break;
case IIO_CHAN_INFO_SCALE:
- if (chan->type == IIO_LIGHT) {
- *val = 0;
- *val2 = 250000;
- ret = IIO_VAL_INT_PLUS_MICRO;
- }
- break;
+ if (chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ *val = 0;
+ *val2 = 250000;
+ return IIO_VAL_INT_PLUS_MICRO;
default:
- break;
+ return -EINVAL;
}
-
- return ret;
}
static const struct iio_info vcnl4000_info = {
@@ -155,7 +163,7 @@ static int vcnl4000_probe(struct i2c_client *client,
{
struct vcnl4000_data *data;
struct iio_dev *indio_dev;
- int ret;
+ int ret, prod_id;
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
@@ -164,13 +172,19 @@ static int vcnl4000_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
+ mutex_init(&data->lock);
ret = i2c_smbus_read_byte_data(data->client, VCNL4000_PROD_REV);
if (ret < 0)
return ret;
- dev_info(&client->dev, "VCNL4000 Ambient light/proximity sensor, Prod %02x, Rev: %02x\n",
- ret >> 4, ret & 0xf);
+ prod_id = ret >> 4;
+ if (prod_id != VCNL4010_ID && prod_id != VCNL4000_ID)
+ return -ENODEV;
+
+ dev_dbg(&client->dev, "%s Ambient light/proximity sensor, Rev: %02x\n",
+ (prod_id == VCNL4010_ID) ? "VCNL4010/4020" : "VCNL4000",
+ ret & 0xf);
indio_dev->dev.parent = &client->dev;
indio_dev->info = &vcnl4000_info;
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index 1f842abcb4a4..421ad90a5fbe 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -5,8 +5,22 @@
menu "Magnetometer sensors"
+config AK8974
+ tristate "Asahi Kasei AK8974 3-Axis Magnetometer"
+ depends on I2C
+ depends on OF
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Asahi Kasei AK8974 or
+ AMI305 I2C-based 3-axis magnetometer chips.
+
+ To compile this driver as a module, choose M here: the module
+ will be called ak8974.
+
config AK8975
- tristate "Asahi Kasei AK 3-Axis Magnetometer"
+ tristate "Asahi Kasei AK8975 3-Axis Magnetometer"
depends on I2C
depends on GPIOLIB || COMPILE_TEST
select IIO_BUFFER
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 92a745c9a6e8..b86d6cb7f285 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -3,6 +3,7 @@
#
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_AK8974) += ak8974.o
obj-$(CONFIG_AK8975) += ak8975.o
obj-$(CONFIG_BMC150_MAGN) += bmc150_magn.o
obj-$(CONFIG_BMC150_MAGN_I2C) += bmc150_magn_i2c.o
diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c
new file mode 100644
index 000000000000..217353145676
--- /dev/null
+++ b/drivers/iio/magnetometer/ak8974.c
@@ -0,0 +1,860 @@
+/*
+ * Driver for the Asahi Kasei EMD Corporation AK8974
+ * and Aichi Steel AMI305 magnetometer chips.
+ * Based on a patch from Samu Onkalo and the AK8975 IIO driver.
+ *
+ * Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+ * Copyright (c) 2010 NVIDIA Corporation.
+ * Copyright (C) 2016 Linaro Ltd.
+ *
+ * Author: Samu Onkalo <samu.p.onkalo@nokia.com>
+ * Author: Linus Walleij <linus.walleij@linaro.org>
+ */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h> /* For irq_get_irq_data() */
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/*
+ * 16-bit registers are little-endian. LSB is at the address defined below
+ * and MSB is at the next higher address.
+ */
+
+/* These registers are common for AK8974 and AMI305 */
+#define AK8974_SELFTEST 0x0C
+#define AK8974_SELFTEST_IDLE 0x55
+#define AK8974_SELFTEST_OK 0xAA
+
+#define AK8974_INFO 0x0D
+
+#define AK8974_WHOAMI 0x0F
+#define AK8974_WHOAMI_VALUE_AMI305 0x47
+#define AK8974_WHOAMI_VALUE_AK8974 0x48
+
+#define AK8974_DATA_X 0x10
+#define AK8974_DATA_Y 0x12
+#define AK8974_DATA_Z 0x14
+#define AK8974_INT_SRC 0x16
+#define AK8974_STATUS 0x18
+#define AK8974_INT_CLEAR 0x1A
+#define AK8974_CTRL1 0x1B
+#define AK8974_CTRL2 0x1C
+#define AK8974_CTRL3 0x1D
+#define AK8974_INT_CTRL 0x1E
+#define AK8974_INT_THRES 0x26 /* Absolute any axis value threshold */
+#define AK8974_PRESET 0x30
+
+/* AK8974-specific offsets */
+#define AK8974_OFFSET_X 0x20
+#define AK8974_OFFSET_Y 0x22
+#define AK8974_OFFSET_Z 0x24
+/* AMI305-specific offsets */
+#define AMI305_OFFSET_X 0x6C
+#define AMI305_OFFSET_Y 0x72
+#define AMI305_OFFSET_Z 0x78
+
+/* Different temperature registers */
+#define AK8974_TEMP 0x31
+#define AMI305_TEMP 0x60
+
+#define AK8974_INT_X_HIGH BIT(7) /* Axis over +threshold */
+#define AK8974_INT_Y_HIGH BIT(6)
+#define AK8974_INT_Z_HIGH BIT(5)
+#define AK8974_INT_X_LOW BIT(4) /* Axis below -threshold */
+#define AK8974_INT_Y_LOW BIT(3)
+#define AK8974_INT_Z_LOW BIT(2)
+#define AK8974_INT_RANGE BIT(1) /* Range overflow (any axis) */
+
+#define AK8974_STATUS_DRDY BIT(6) /* Data ready */
+#define AK8974_STATUS_OVERRUN BIT(5) /* Data overrun */
+#define AK8974_STATUS_INT BIT(4) /* Interrupt occurred */
+
+#define AK8974_CTRL1_POWER BIT(7) /* 0 = standby; 1 = active */
+#define AK8974_CTRL1_RATE BIT(4) /* 0 = 10 Hz; 1 = 20 Hz */
+#define AK8974_CTRL1_FORCE_EN BIT(1) /* 0 = normal; 1 = force */
+#define AK8974_CTRL1_MODE2 BIT(0) /* 0 */
+
+#define AK8974_CTRL2_INT_EN BIT(4) /* 1 = enable interrupts */
+#define AK8974_CTRL2_DRDY_EN BIT(3) /* 1 = enable data ready signal */
+#define AK8974_CTRL2_DRDY_POL BIT(2) /* 1 = data ready active high */
+#define AK8974_CTRL2_RESDEF (AK8974_CTRL2_DRDY_POL)
+
+#define AK8974_CTRL3_RESET BIT(7) /* Software reset */
+#define AK8974_CTRL3_FORCE BIT(6) /* Start forced measurement */
+#define AK8974_CTRL3_SELFTEST BIT(4) /* Set selftest register */
+#define AK8974_CTRL3_RESDEF 0x00
+
+#define AK8974_INT_CTRL_XEN BIT(7) /* Enable interrupt for this axis */
+#define AK8974_INT_CTRL_YEN BIT(6)
+#define AK8974_INT_CTRL_ZEN BIT(5)
+#define AK8974_INT_CTRL_XYZEN (BIT(7)|BIT(6)|BIT(5))
+#define AK8974_INT_CTRL_POL BIT(3) /* 0 = active low; 1 = active high */
+#define AK8974_INT_CTRL_PULSE BIT(1) /* 0 = latched; 1 = pulse (50 usec) */
+#define AK8974_INT_CTRL_RESDEF (AK8974_INT_CTRL_XYZEN | AK8974_INT_CTRL_POL)
+
+/* The AMI305 has elaborate FW version and serial number registers */
+#define AMI305_VER 0xE8
+#define AMI305_SN 0xEA
+
+#define AK8974_MAX_RANGE 2048
+
+#define AK8974_POWERON_DELAY 50
+#define AK8974_ACTIVATE_DELAY 1
+#define AK8974_SELFTEST_DELAY 1
+/*
+ * Set the autosuspend to two orders of magnitude larger than the poweron
+ * delay to make sane reasonable power tradeoff savings (5 seconds in
+ * this case).
+ */
+#define AK8974_AUTOSUSPEND_DELAY 5000
+
+#define AK8974_MEASTIME 3
+
+#define AK8974_PWR_ON 1
+#define AK8974_PWR_OFF 0
+
+/**
+ * struct ak8974 - state container for the AK8974 driver
+ * @i2c: parent I2C client
+ * @orientation: mounting matrix, flipped axis etc
+ * @map: regmap to access the AK8974 registers over I2C
+ * @regs: the avdd and dvdd power regulators
+ * @name: the name of the part
+ * @variant: the whoami ID value (for selecting code paths)
+ * @lock: locks the magnetometer for exclusive use during a measurement
+ * @drdy_irq: uses the DRDY IRQ line
+ * @drdy_complete: completion for DRDY
+ * @drdy_active_low: the DRDY IRQ is active low
+ */
+struct ak8974 {
+ struct i2c_client *i2c;
+ struct iio_mount_matrix orientation;
+ struct regmap *map;
+ struct regulator_bulk_data regs[2];
+ const char *name;
+ u8 variant;
+ struct mutex lock;
+ bool drdy_irq;
+ struct completion drdy_complete;
+ bool drdy_active_low;
+};
+
+static const char ak8974_reg_avdd[] = "avdd";
+static const char ak8974_reg_dvdd[] = "dvdd";
+
+static int ak8974_set_power(struct ak8974 *ak8974, bool mode)
+{
+ int ret;
+ u8 val;
+
+ val = mode ? AK8974_CTRL1_POWER : 0;
+ val |= AK8974_CTRL1_FORCE_EN;
+ ret = regmap_write(ak8974->map, AK8974_CTRL1, val);
+ if (ret < 0)
+ return ret;
+
+ if (mode)
+ msleep(AK8974_ACTIVATE_DELAY);
+
+ return 0;
+}
+
+static int ak8974_reset(struct ak8974 *ak8974)
+{
+ int ret;
+
+ /* Power on to get register access. Sets CTRL1 reg to reset state */
+ ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
+ if (ret)
+ return ret;
+ ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_RESDEF);
+ if (ret)
+ return ret;
+ ret = regmap_write(ak8974->map, AK8974_CTRL3, AK8974_CTRL3_RESDEF);
+ if (ret)
+ return ret;
+ ret = regmap_write(ak8974->map, AK8974_INT_CTRL,
+ AK8974_INT_CTRL_RESDEF);
+ if (ret)
+ return ret;
+
+ /* After reset, power off is default state */
+ return ak8974_set_power(ak8974, AK8974_PWR_OFF);
+}
+
+static int ak8974_configure(struct ak8974 *ak8974)
+{
+ int ret;
+
+ ret = regmap_write(ak8974->map, AK8974_CTRL2, AK8974_CTRL2_DRDY_EN |
+ AK8974_CTRL2_INT_EN);
+ if (ret)
+ return ret;
+ ret = regmap_write(ak8974->map, AK8974_CTRL3, 0);
+ if (ret)
+ return ret;
+ ret = regmap_write(ak8974->map, AK8974_INT_CTRL, AK8974_INT_CTRL_POL);
+ if (ret)
+ return ret;
+
+ return regmap_write(ak8974->map, AK8974_PRESET, 0);
+}
+
+static int ak8974_trigmeas(struct ak8974 *ak8974)
+{
+ unsigned int clear;
+ u8 mask;
+ u8 val;
+ int ret;
+
+ /* Clear any previous measurement overflow status */
+ ret = regmap_read(ak8974->map, AK8974_INT_CLEAR, &clear);
+ if (ret)
+ return ret;
+
+ /* If we have a DRDY IRQ line, use it */
+ if (ak8974->drdy_irq) {
+ mask = AK8974_CTRL2_INT_EN |
+ AK8974_CTRL2_DRDY_EN |
+ AK8974_CTRL2_DRDY_POL;
+ val = AK8974_CTRL2_DRDY_EN;
+
+ if (!ak8974->drdy_active_low)
+ val |= AK8974_CTRL2_DRDY_POL;
+
+ init_completion(&ak8974->drdy_complete);
+ ret = regmap_update_bits(ak8974->map, AK8974_CTRL2,
+ mask, val);
+ if (ret)
+ return ret;
+ }
+
+ /* Force a measurement */
+ return regmap_update_bits(ak8974->map,
+ AK8974_CTRL3,
+ AK8974_CTRL3_FORCE,
+ AK8974_CTRL3_FORCE);
+}
+
+static int ak8974_await_drdy(struct ak8974 *ak8974)
+{
+ int timeout = 2;
+ unsigned int val;
+ int ret;
+
+ if (ak8974->drdy_irq) {
+ ret = wait_for_completion_timeout(&ak8974->drdy_complete,
+ 1 + msecs_to_jiffies(1000));
+ if (!ret) {
+ dev_err(&ak8974->i2c->dev,
+ "timeout waiting for DRDY IRQ\n");
+ return -ETIMEDOUT;
+ }
+ return 0;
+ }
+
+ /* Default delay-based poll loop */
+ do {
+ msleep(AK8974_MEASTIME);
+ ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
+ if (ret < 0)
+ return ret;
+ if (val & AK8974_STATUS_DRDY)
+ return 0;
+ } while (--timeout);
+ if (!timeout) {
+ dev_err(&ak8974->i2c->dev,
+ "timeout waiting for DRDY\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int ak8974_getresult(struct ak8974 *ak8974, s16 *result)
+{
+ unsigned int src;
+ int ret;
+
+ ret = ak8974_await_drdy(ak8974);
+ if (ret)
+ return ret;
+ ret = regmap_read(ak8974->map, AK8974_INT_SRC, &src);
+ if (ret < 0)
+ return ret;
+
+ /* Out of range overflow! Strong magnet close? */
+ if (src & AK8974_INT_RANGE) {
+ dev_err(&ak8974->i2c->dev,
+ "range overflow in sensor\n");
+ return -ERANGE;
+ }
+
+ ret = regmap_bulk_read(ak8974->map, AK8974_DATA_X, result, 6);
+ if (ret)
+ return ret;
+
+ return ret;
+}
+
+static irqreturn_t ak8974_drdy_irq(int irq, void *d)
+{
+ struct ak8974 *ak8974 = d;
+
+ if (!ak8974->drdy_irq)
+ return IRQ_NONE;
+
+ /* TODO: timestamp here to get good measurement stamps */
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t ak8974_drdy_irq_thread(int irq, void *d)
+{
+ struct ak8974 *ak8974 = d;
+ unsigned int val;
+ int ret;
+
+ /* Check if this was a DRDY from us */
+ ret = regmap_read(ak8974->map, AK8974_STATUS, &val);
+ if (ret < 0) {
+ dev_err(&ak8974->i2c->dev, "error reading DRDY status\n");
+ return IRQ_HANDLED;
+ }
+ if (val & AK8974_STATUS_DRDY) {
+ /* Yes this was our IRQ */
+ complete(&ak8974->drdy_complete);
+ return IRQ_HANDLED;
+ }
+
+ /* We may be on a shared IRQ, let the next client check */
+ return IRQ_NONE;
+}
+
+static int ak8974_selftest(struct ak8974 *ak8974)
+{
+ struct device *dev = &ak8974->i2c->dev;
+ unsigned int val;
+ int ret;
+
+ ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
+ if (ret)
+ return ret;
+ if (val != AK8974_SELFTEST_IDLE) {
+ dev_err(dev, "selftest not idle before test\n");
+ return -EIO;
+ }
+
+ /* Trigger self-test */
+ ret = regmap_update_bits(ak8974->map,
+ AK8974_CTRL3,
+ AK8974_CTRL3_SELFTEST,
+ AK8974_CTRL3_SELFTEST);
+ if (ret) {
+ dev_err(dev, "could not write CTRL3\n");
+ return ret;
+ }
+
+ msleep(AK8974_SELFTEST_DELAY);
+
+ ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
+ if (ret)
+ return ret;
+ if (val != AK8974_SELFTEST_OK) {
+ dev_err(dev, "selftest result NOT OK (%02x)\n", val);
+ return -EIO;
+ }
+
+ ret = regmap_read(ak8974->map, AK8974_SELFTEST, &val);
+ if (ret)
+ return ret;
+ if (val != AK8974_SELFTEST_IDLE) {
+ dev_err(dev, "selftest not idle after test (%02x)\n", val);
+ return -EIO;
+ }
+ dev_dbg(dev, "passed self-test\n");
+
+ return 0;
+}
+
+static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
+{
+ int ret;
+ u16 bulk;
+
+ ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
+ if (ret)
+ return ret;
+ *val = le16_to_cpu(bulk);
+
+ return 0;
+}
+
+static int ak8974_detect(struct ak8974 *ak8974)
+{
+ unsigned int whoami;
+ const char *name;
+ int ret;
+ unsigned int fw;
+ u16 sn;
+
+ ret = regmap_read(ak8974->map, AK8974_WHOAMI, &whoami);
+ if (ret)
+ return ret;
+
+ switch (whoami) {
+ case AK8974_WHOAMI_VALUE_AMI305:
+ name = "ami305";
+ ret = regmap_read(ak8974->map, AMI305_VER, &fw);
+ if (ret)
+ return ret;
+ fw &= 0x7f; /* only bits 0 thru 6 valid */
+ ret = ak8974_get_u16_val(ak8974, AMI305_SN, &sn);
+ if (ret)
+ return ret;
+ dev_info(&ak8974->i2c->dev,
+ "detected %s, FW ver %02x, S/N: %04x\n",
+ name, fw, sn);
+ break;
+ case AK8974_WHOAMI_VALUE_AK8974:
+ name = "ak8974";
+ dev_info(&ak8974->i2c->dev, "detected AK8974\n");
+ break;
+ default:
+ dev_err(&ak8974->i2c->dev, "unsupported device (%02x) ",
+ whoami);
+ return -ENODEV;
+ }
+
+ ak8974->name = name;
+ ak8974->variant = whoami;
+
+ return 0;
+}
+
+static int ak8974_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct ak8974 *ak8974 = iio_priv(indio_dev);
+ s16 hw_values[3];
+ int ret = -EINVAL;
+
+ pm_runtime_get_sync(&ak8974->i2c->dev);
+ mutex_lock(&ak8974->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->address > 2) {
+ dev_err(&ak8974->i2c->dev, "faulty channel address\n");
+ ret = -EIO;
+ goto out_unlock;
+ }
+ ret = ak8974_trigmeas(ak8974);
+ if (ret)
+ goto out_unlock;
+ ret = ak8974_getresult(ak8974, hw_values);
+ if (ret)
+ goto out_unlock;
+
+ /*
+ * We read all axes and discard all but one, for optimized
+ * reading, use the triggered buffer.
+ */
+ *val = le16_to_cpu(hw_values[chan->address]);
+
+ ret = IIO_VAL_INT;
+ }
+
+ out_unlock:
+ mutex_unlock(&ak8974->lock);
+ pm_runtime_mark_last_busy(&ak8974->i2c->dev);
+ pm_runtime_put_autosuspend(&ak8974->i2c->dev);
+
+ return ret;
+}
+
+static void ak8974_fill_buffer(struct iio_dev *indio_dev)
+{
+ struct ak8974 *ak8974 = iio_priv(indio_dev);
+ int ret;
+ s16 hw_values[8]; /* Three axes + 64bit padding */
+
+ pm_runtime_get_sync(&ak8974->i2c->dev);
+ mutex_lock(&ak8974->lock);
+
+ ret = ak8974_trigmeas(ak8974);
+ if (ret) {
+ dev_err(&ak8974->i2c->dev, "error triggering measure\n");
+ goto out_unlock;
+ }
+ ret = ak8974_getresult(ak8974, hw_values);
+ if (ret) {
+ dev_err(&ak8974->i2c->dev, "error getting measures\n");
+ goto out_unlock;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, hw_values,
+ iio_get_time_ns(indio_dev));
+
+ out_unlock:
+ mutex_unlock(&ak8974->lock);
+ pm_runtime_mark_last_busy(&ak8974->i2c->dev);
+ pm_runtime_put_autosuspend(&ak8974->i2c->dev);
+}
+
+static irqreturn_t ak8974_handle_trigger(int irq, void *p)
+{
+ const struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+
+ ak8974_fill_buffer(indio_dev);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_mount_matrix *
+ak8974_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ak8974 *ak8974 = iio_priv(indio_dev);
+
+ return &ak8974->orientation;
+}
+
+static const struct iio_chan_spec_ext_info ak8974_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8974_get_mount_matrix),
+ { },
+};
+
+#define AK8974_AXIS_CHANNEL(axis, index) \
+ { \
+ .type = IIO_MAGN, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .ext_info = ak8974_ext_info, \
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE \
+ }, \
+ }
+
+static const struct iio_chan_spec ak8974_channels[] = {
+ AK8974_AXIS_CHANNEL(X, 0),
+ AK8974_AXIS_CHANNEL(Y, 1),
+ AK8974_AXIS_CHANNEL(Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const unsigned long ak8974_scan_masks[] = { 0x7, 0 };
+
+static const struct iio_info ak8974_info = {
+ .read_raw = &ak8974_read_raw,
+ .driver_module = THIS_MODULE,
+};
+
+static bool ak8974_writeable_reg(struct device *dev, unsigned int reg)
+{
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
+ struct ak8974 *ak8974 = iio_priv(indio_dev);
+
+ switch (reg) {
+ case AK8974_CTRL1:
+ case AK8974_CTRL2:
+ case AK8974_CTRL3:
+ case AK8974_INT_CTRL:
+ case AK8974_INT_THRES:
+ case AK8974_INT_THRES + 1:
+ case AK8974_PRESET:
+ case AK8974_PRESET + 1:
+ return true;
+ case AK8974_OFFSET_X:
+ case AK8974_OFFSET_X + 1:
+ case AK8974_OFFSET_Y:
+ case AK8974_OFFSET_Y + 1:
+ case AK8974_OFFSET_Z:
+ case AK8974_OFFSET_Z + 1:
+ if (ak8974->variant == AK8974_WHOAMI_VALUE_AK8974)
+ return true;
+ return false;
+ case AMI305_OFFSET_X:
+ case AMI305_OFFSET_X + 1:
+ case AMI305_OFFSET_Y:
+ case AMI305_OFFSET_Y + 1:
+ case AMI305_OFFSET_Z:
+ case AMI305_OFFSET_Z + 1:
+ if (ak8974->variant == AK8974_WHOAMI_VALUE_AMI305)
+ return true;
+ return false;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config ak8974_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = 0xff,
+ .writeable_reg = ak8974_writeable_reg,
+};
+
+static int ak8974_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct ak8974 *ak8974;
+ unsigned long irq_trig;
+ int irq = i2c->irq;
+ int ret;
+
+ /* Register with IIO */
+ indio_dev = devm_iio_device_alloc(&i2c->dev, sizeof(*ak8974));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ ak8974 = iio_priv(indio_dev);
+ i2c_set_clientdata(i2c, indio_dev);
+ ak8974->i2c = i2c;
+ mutex_init(&ak8974->lock);
+
+ ret = of_iio_read_mount_matrix(&i2c->dev,
+ "mount-matrix",
+ &ak8974->orientation);
+ if (ret)
+ return ret;
+
+ ak8974->regs[0].supply = ak8974_reg_avdd;
+ ak8974->regs[1].supply = ak8974_reg_dvdd;
+
+ ret = devm_regulator_bulk_get(&i2c->dev,
+ ARRAY_SIZE(ak8974->regs),
+ ak8974->regs);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "cannot get regulators\n");
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+ if (ret < 0) {
+ dev_err(&i2c->dev, "cannot enable regulators\n");
+ return ret;
+ }
+
+ /* Take runtime PM online */
+ pm_runtime_get_noresume(&i2c->dev);
+ pm_runtime_set_active(&i2c->dev);
+ pm_runtime_enable(&i2c->dev);
+
+ ak8974->map = devm_regmap_init_i2c(i2c, &ak8974_regmap_config);
+ if (IS_ERR(ak8974->map)) {
+ dev_err(&i2c->dev, "failed to allocate register map\n");
+ return PTR_ERR(ak8974->map);
+ }
+
+ ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
+ if (ret) {
+ dev_err(&i2c->dev, "could not power on\n");
+ goto power_off;
+ }
+
+ ret = ak8974_detect(ak8974);
+ if (ret) {
+ dev_err(&i2c->dev, "neither AK8974 nor AMI305 found\n");
+ goto power_off;
+ }
+
+ ret = ak8974_selftest(ak8974);
+ if (ret)
+ dev_err(&i2c->dev, "selftest failed (continuing anyway)\n");
+
+ ret = ak8974_reset(ak8974);
+ if (ret) {
+ dev_err(&i2c->dev, "AK8974 reset failed\n");
+ goto power_off;
+ }
+
+ pm_runtime_set_autosuspend_delay(&i2c->dev,
+ AK8974_AUTOSUSPEND_DELAY);
+ pm_runtime_use_autosuspend(&i2c->dev);
+ pm_runtime_put(&i2c->dev);
+
+ indio_dev->dev.parent = &i2c->dev;
+ indio_dev->channels = ak8974_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ak8974_channels);
+ indio_dev->info = &ak8974_info;
+ indio_dev->available_scan_masks = ak8974_scan_masks;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = ak8974->name;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ ak8974_handle_trigger,
+ NULL);
+ if (ret) {
+ dev_err(&i2c->dev, "triggered buffer setup failed\n");
+ goto disable_pm;
+ }
+
+ /* If we have a valid DRDY IRQ, make use of it */
+ if (irq > 0) {
+ irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
+ if (irq_trig == IRQF_TRIGGER_RISING) {
+ dev_info(&i2c->dev, "enable rising edge DRDY IRQ\n");
+ } else if (irq_trig == IRQF_TRIGGER_FALLING) {
+ ak8974->drdy_active_low = true;
+ dev_info(&i2c->dev, "enable falling edge DRDY IRQ\n");
+ } else {
+ irq_trig = IRQF_TRIGGER_RISING;
+ }
+ irq_trig |= IRQF_ONESHOT;
+ irq_trig |= IRQF_SHARED;
+
+ ret = devm_request_threaded_irq(&i2c->dev,
+ irq,
+ ak8974_drdy_irq,
+ ak8974_drdy_irq_thread,
+ irq_trig,
+ ak8974->name,
+ ak8974);
+ if (ret) {
+ dev_err(&i2c->dev, "unable to request DRDY IRQ "
+ "- proceeding without IRQ\n");
+ goto no_irq;
+ }
+ ak8974->drdy_irq = true;
+ }
+
+no_irq:
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&i2c->dev, "device register failed\n");
+ goto cleanup_buffer;
+ }
+
+ return 0;
+
+cleanup_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+disable_pm:
+ pm_runtime_put_noidle(&i2c->dev);
+ pm_runtime_disable(&i2c->dev);
+ ak8974_set_power(ak8974, AK8974_PWR_OFF);
+power_off:
+ regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+ return ret;
+}
+
+static int __exit ak8974_remove(struct i2c_client *i2c)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
+ struct ak8974 *ak8974 = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+ pm_runtime_get_sync(&i2c->dev);
+ pm_runtime_put_noidle(&i2c->dev);
+ pm_runtime_disable(&i2c->dev);
+ ak8974_set_power(ak8974, AK8974_PWR_OFF);
+ regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+ return 0;
+}
+
+static int __maybe_unused ak8974_runtime_suspend(struct device *dev)
+{
+ struct ak8974 *ak8974 =
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+
+ ak8974_set_power(ak8974, AK8974_PWR_OFF);
+ regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+ return 0;
+}
+
+static int __maybe_unused ak8974_runtime_resume(struct device *dev)
+{
+ struct ak8974 *ak8974 =
+ iio_priv(i2c_get_clientdata(to_i2c_client(dev)));
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+ if (ret)
+ return ret;
+ msleep(AK8974_POWERON_DELAY);
+ ret = ak8974_set_power(ak8974, AK8974_PWR_ON);
+ if (ret)
+ goto out_regulator_disable;
+
+ ret = ak8974_configure(ak8974);
+ if (ret)
+ goto out_disable_power;
+
+ return 0;
+
+out_disable_power:
+ ak8974_set_power(ak8974, AK8974_PWR_OFF);
+out_regulator_disable:
+ regulator_bulk_disable(ARRAY_SIZE(ak8974->regs), ak8974->regs);
+
+ return ret;
+}
+
+static const struct dev_pm_ops ak8974_dev_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(ak8974_runtime_suspend,
+ ak8974_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id ak8974_id[] = {
+ {"ami305", 0 },
+ {"ak8974", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ak8974_id);
+
+static const struct of_device_id ak8974_of_match[] = {
+ { .compatible = "asahi-kasei,ak8974", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ak8974_of_match);
+
+static struct i2c_driver ak8974_driver = {
+ .driver = {
+ .name = "ak8974",
+ .pm = &ak8974_dev_pm_ops,
+ .of_match_table = of_match_ptr(ak8974_of_match),
+ },
+ .probe = ak8974_probe,
+ .remove = __exit_p(ak8974_remove),
+ .id_table = ak8974_id,
+};
+module_i2c_driver(ak8974_driver);
+
+MODULE_DESCRIPTION("AK8974 and AMI305 3-axis magnetometer driver");
+MODULE_AUTHOR("Samu Onkalo");
+MODULE_AUTHOR("Linus Walleij");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c
index f2be4a049056..f2b3bd7bf862 100644
--- a/drivers/iio/magnetometer/mag3110.c
+++ b/drivers/iio/magnetometer/mag3110.c
@@ -154,34 +154,41 @@ static int mag3110_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (iio_buffer_enabled(indio_dev))
- return -EBUSY;
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
switch (chan->type) {
case IIO_MAGN: /* in 0.1 uT / LSB */
ret = mag3110_read(data, buffer);
if (ret < 0)
- return ret;
+ goto release;
*val = sign_extend32(
be16_to_cpu(buffer[chan->scan_index]), 15);
- return IIO_VAL_INT;
+ ret = IIO_VAL_INT;
+ break;
case IIO_TEMP: /* in 1 C / LSB */
mutex_lock(&data->lock);
ret = mag3110_request(data);
if (ret < 0) {
mutex_unlock(&data->lock);
- return ret;
+ goto release;
}
ret = i2c_smbus_read_byte_data(data->client,
MAG3110_DIE_TEMP);
mutex_unlock(&data->lock);
if (ret < 0)
- return ret;
+ goto release;
*val = sign_extend32(ret, 7);
- return IIO_VAL_INT;
+ ret = IIO_VAL_INT;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
}
+release:
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_MAGN:
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index 7fa65ab664fb..15cd416365c1 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -185,4 +185,26 @@ config HP206C
This driver can also be built as a module. If so, the module will
be called hp206c.
+config ZPA2326
+ tristate "Murata ZPA2326 pressure sensor driver"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP
+ select ZPA2326_I2C if I2C
+ select ZPA2326_SPI if SPI_MASTER
+ help
+ Say Y here to build support for the Murata ZPA2326 pressure and
+ temperature sensor.
+
+ To compile this driver as a module, choose M here: the module will
+ be called zpa2326.
+
+config ZPA2326_I2C
+ tristate
+ select REGMAP_I2C
+
+config ZPA2326_SPI
+ tristate
+ select REGMAP_SPI
+
endmenu
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index 7f395bed5e88..fff77185a5cc 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -22,6 +22,9 @@ st_pressure-y := st_pressure_core.o
st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
obj-$(CONFIG_T5403) += t5403.o
obj-$(CONFIG_HP206C) += hp206c.o
+obj-$(CONFIG_ZPA2326) += zpa2326.o
+obj-$(CONFIG_ZPA2326_I2C) += zpa2326_i2c.o
+obj-$(CONFIG_ZPA2326_SPI) += zpa2326_spi.o
obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o
obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o
diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
index feb41f82c64a..a74ed1f0c880 100644
--- a/drivers/iio/pressure/ms5611_core.c
+++ b/drivers/iio/pressure/ms5611_core.c
@@ -416,8 +416,7 @@ static int ms5611_init(struct iio_dev *indio_dev)
return 0;
err_regulator_disable:
- if (!IS_ERR_OR_NULL(st->vdd))
- regulator_disable(st->vdd);
+ regulator_disable(st->vdd);
return ret;
}
@@ -425,8 +424,7 @@ static void ms5611_fini(const struct iio_dev *indio_dev)
{
const struct ms5611_state *st = iio_priv(indio_dev);
- if (!IS_ERR_OR_NULL(st->vdd))
- regulator_disable(st->vdd);
+ regulator_disable(st->vdd);
}
int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c
new file mode 100644
index 000000000000..19d2eb46fda6
--- /dev/null
+++ b/drivers/iio/pressure/zpa2326.c
@@ -0,0 +1,1735 @@
+/*
+ * Murata ZPA2326 pressure and temperature sensor IIO driver
+ *
+ * Copyright (c) 2016 Parrot S.A.
+ *
+ * Author: Gregor Boirie <gregor.boirie@parrot.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+/**
+ * DOC: ZPA2326 theory of operations
+ *
+ * This driver supports %INDIO_DIRECT_MODE and %INDIO_BUFFER_TRIGGERED IIO
+ * modes.
+ * A internal hardware trigger is also implemented to dispatch registered IIO
+ * trigger consumers upon "sample ready" interrupts.
+ *
+ * ZPA2326 hardware supports 2 sampling mode: one shot and continuous.
+ *
+ * A complete one shot sampling cycle gets device out of low power mode,
+ * performs pressure and temperature measurements, then automatically switches
+ * back to low power mode. It is meant for on demand sampling with optimal power
+ * saving at the cost of lower sampling rate and higher software overhead.
+ * This is a natural candidate for IIO read_raw hook implementation
+ * (%INDIO_DIRECT_MODE). It is also used for triggered buffering support to
+ * ensure explicit synchronization with external trigger events
+ * (%INDIO_BUFFER_TRIGGERED).
+ *
+ * The continuous mode works according to a periodic hardware measurement
+ * process continuously pushing samples into an internal hardware FIFO (for
+ * pressure samples only). Measurement cycle completion may be signaled by a
+ * "sample ready" interrupt.
+ * Typical software sequence of operations :
+ * - get device out of low power mode,
+ * - setup hardware sampling period,
+ * - at end of period, upon data ready interrupt: pop pressure samples out of
+ * hardware FIFO and fetch temperature sample
+ * - when no longer needed, stop sampling process by putting device into
+ * low power mode.
+ * This mode is used to implement %INDIO_BUFFER_TRIGGERED mode if device tree
+ * declares a valid interrupt line. In this case, the internal hardware trigger
+ * drives acquisition.
+ *
+ * Note that hardware sampling frequency is taken into account only when
+ * internal hardware trigger is attached as the highest sampling rate seems to
+ * be the most energy efficient.
+ *
+ * TODO:
+ * preset pressure threshold crossing / IIO events ;
+ * differential pressure sampling ;
+ * hardware samples averaging.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include "zpa2326.h"
+
+/* 200 ms should be enough for the longest conversion time in one-shot mode. */
+#define ZPA2326_CONVERSION_JIFFIES (HZ / 5)
+
+/* There should be a 1 ms delay (Tpup) after getting out of reset. */
+#define ZPA2326_TPUP_USEC_MIN (1000)
+#define ZPA2326_TPUP_USEC_MAX (2000)
+
+/**
+ * struct zpa2326_frequency - Hardware sampling frequency descriptor
+ * @hz : Frequency in Hertz.
+ * @odr: Output Data Rate word as expected by %ZPA2326_CTRL_REG3_REG.
+ */
+struct zpa2326_frequency {
+ int hz;
+ u16 odr;
+};
+
+/*
+ * Keep these in strict ascending order: last array entry is expected to
+ * correspond to the highest sampling frequency.
+ */
+static const struct zpa2326_frequency zpa2326_sampling_frequencies[] = {
+ { .hz = 1, .odr = 1 << ZPA2326_CTRL_REG3_ODR_SHIFT },
+ { .hz = 5, .odr = 5 << ZPA2326_CTRL_REG3_ODR_SHIFT },
+ { .hz = 11, .odr = 6 << ZPA2326_CTRL_REG3_ODR_SHIFT },
+ { .hz = 23, .odr = 7 << ZPA2326_CTRL_REG3_ODR_SHIFT },
+};
+
+/* Return the highest hardware sampling frequency available. */
+static const struct zpa2326_frequency *zpa2326_highest_frequency(void)
+{
+ return &zpa2326_sampling_frequencies[
+ ARRAY_SIZE(zpa2326_sampling_frequencies) - 1];
+}
+
+/**
+ * struct zpa_private - Per-device internal private state
+ * @timestamp: Buffered samples ready datum.
+ * @regmap: Underlying I2C / SPI bus adapter used to abstract slave register
+ * accesses.
+ * @result: Allows sampling logic to get completion status of operations
+ * that interrupt handlers perform asynchronously.
+ * @data_ready: Interrupt handler uses this to wake user context up at sampling
+ * operation completion.
+ * @trigger: Optional hardware / interrupt driven trigger used to notify
+ * external devices a new sample is ready.
+ * @waken: Flag indicating whether or not device has just been powered on.
+ * @irq: Optional interrupt line: negative or zero if not declared into
+ * DT, in which case sampling logic keeps polling status register
+ * to detect completion.
+ * @frequency: Current hardware sampling frequency.
+ * @vref: Power / voltage reference.
+ * @vdd: Power supply.
+ */
+struct zpa2326_private {
+ s64 timestamp;
+ struct regmap *regmap;
+ int result;
+ struct completion data_ready;
+ struct iio_trigger *trigger;
+ bool waken;
+ int irq;
+ const struct zpa2326_frequency *frequency;
+ struct regulator *vref;
+ struct regulator *vdd;
+};
+
+#define zpa2326_err(_idev, _format, _arg...) \
+ dev_err(_idev->dev.parent, _format, ##_arg)
+
+#define zpa2326_warn(_idev, _format, _arg...) \
+ dev_warn(_idev->dev.parent, _format, ##_arg)
+
+#ifdef DEBUG
+#define zpa2326_dbg(_idev, _format, _arg...) \
+ dev_dbg(_idev->dev.parent, _format, ##_arg)
+#else
+#define zpa2326_dbg(_idev, _format, _arg...)
+#endif
+
+bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ZPA2326_REF_P_XL_REG:
+ case ZPA2326_REF_P_L_REG:
+ case ZPA2326_REF_P_H_REG:
+ case ZPA2326_RES_CONF_REG:
+ case ZPA2326_CTRL_REG0_REG:
+ case ZPA2326_CTRL_REG1_REG:
+ case ZPA2326_CTRL_REG2_REG:
+ case ZPA2326_CTRL_REG3_REG:
+ case ZPA2326_THS_P_LOW_REG:
+ case ZPA2326_THS_P_HIGH_REG:
+ return true;
+
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(zpa2326_isreg_writeable);
+
+bool zpa2326_isreg_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ZPA2326_REF_P_XL_REG:
+ case ZPA2326_REF_P_L_REG:
+ case ZPA2326_REF_P_H_REG:
+ case ZPA2326_DEVICE_ID_REG:
+ case ZPA2326_RES_CONF_REG:
+ case ZPA2326_CTRL_REG0_REG:
+ case ZPA2326_CTRL_REG1_REG:
+ case ZPA2326_CTRL_REG2_REG:
+ case ZPA2326_CTRL_REG3_REG:
+ case ZPA2326_INT_SOURCE_REG:
+ case ZPA2326_THS_P_LOW_REG:
+ case ZPA2326_THS_P_HIGH_REG:
+ case ZPA2326_STATUS_REG:
+ case ZPA2326_PRESS_OUT_XL_REG:
+ case ZPA2326_PRESS_OUT_L_REG:
+ case ZPA2326_PRESS_OUT_H_REG:
+ case ZPA2326_TEMP_OUT_L_REG:
+ case ZPA2326_TEMP_OUT_H_REG:
+ return true;
+
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(zpa2326_isreg_readable);
+
+bool zpa2326_isreg_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ZPA2326_INT_SOURCE_REG:
+ case ZPA2326_PRESS_OUT_H_REG:
+ return true;
+
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_GPL(zpa2326_isreg_precious);
+
+/**
+ * zpa2326_enable_device() - Enable device, i.e. get out of low power mode.
+ * @indio_dev: The IIO device associated with the hardware to enable.
+ *
+ * Required to access complete register space and to perform any sampling
+ * or control operations.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_enable_device(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = regmap_write(((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap,
+ ZPA2326_CTRL_REG0_REG, ZPA2326_CTRL_REG0_ENABLE);
+ if (err) {
+ zpa2326_err(indio_dev, "failed to enable device (%d)", err);
+ return err;
+ }
+
+ zpa2326_dbg(indio_dev, "enabled");
+
+ return 0;
+}
+
+/**
+ * zpa2326_sleep() - Disable device, i.e. switch to low power mode.
+ * @indio_dev: The IIO device associated with the hardware to disable.
+ *
+ * Only %ZPA2326_DEVICE_ID_REG and %ZPA2326_CTRL_REG0_REG registers may be
+ * accessed once device is in the disabled state.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_sleep(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = regmap_write(((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap,
+ ZPA2326_CTRL_REG0_REG, 0);
+ if (err) {
+ zpa2326_err(indio_dev, "failed to sleep (%d)", err);
+ return err;
+ }
+
+ zpa2326_dbg(indio_dev, "sleeping");
+
+ return 0;
+}
+
+/**
+ * zpa2326_reset_device() - Reset device to default hardware state.
+ * @indio_dev: The IIO device associated with the hardware to reset.
+ *
+ * Disable sampling and empty hardware FIFO.
+ * Device must be enabled before reset, i.e. not in low power mode.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_reset_device(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = regmap_write(((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap,
+ ZPA2326_CTRL_REG2_REG, ZPA2326_CTRL_REG2_SWRESET);
+ if (err) {
+ zpa2326_err(indio_dev, "failed to reset device (%d)", err);
+ return err;
+ }
+
+ usleep_range(ZPA2326_TPUP_USEC_MIN, ZPA2326_TPUP_USEC_MAX);
+
+ zpa2326_dbg(indio_dev, "reset");
+
+ return 0;
+}
+
+/**
+ * zpa2326_start_oneshot() - Start a single sampling cycle, i.e. in one shot
+ * mode.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Device must have been previously enabled and configured for one shot mode.
+ * Device will be switched back to low power mode at end of cycle.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_start_oneshot(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = regmap_write(((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap,
+ ZPA2326_CTRL_REG0_REG,
+ ZPA2326_CTRL_REG0_ENABLE |
+ ZPA2326_CTRL_REG0_ONE_SHOT);
+ if (err) {
+ zpa2326_err(indio_dev, "failed to start one shot cycle (%d)",
+ err);
+ return err;
+ }
+
+ zpa2326_dbg(indio_dev, "one shot cycle started");
+
+ return 0;
+}
+
+/**
+ * zpa2326_power_on() - Power on device to allow subsequent configuration.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ *
+ * Sampling will be disabled, preventing strange things from happening in our
+ * back. Hardware FIFO content will be cleared.
+ * When successful, device will be left in the enabled state to allow further
+ * configuration.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_power_on(const struct iio_dev *indio_dev,
+ const struct zpa2326_private *private)
+{
+ int err;
+
+ err = regulator_enable(private->vref);
+ if (err)
+ return err;
+
+ err = regulator_enable(private->vdd);
+ if (err)
+ goto vref;
+
+ zpa2326_dbg(indio_dev, "powered on");
+
+ err = zpa2326_enable_device(indio_dev);
+ if (err)
+ goto vdd;
+
+ err = zpa2326_reset_device(indio_dev);
+ if (err)
+ goto sleep;
+
+ return 0;
+
+sleep:
+ zpa2326_sleep(indio_dev);
+vdd:
+ regulator_disable(private->vdd);
+vref:
+ regulator_disable(private->vref);
+
+ zpa2326_dbg(indio_dev, "powered off");
+
+ return err;
+}
+
+/**
+ * zpa2326_power_off() - Power off device, i.e. disable attached power
+ * regulators.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static void zpa2326_power_off(const struct iio_dev *indio_dev,
+ const struct zpa2326_private *private)
+{
+ regulator_disable(private->vdd);
+ regulator_disable(private->vref);
+
+ zpa2326_dbg(indio_dev, "powered off");
+}
+
+/**
+ * zpa2326_config_oneshot() - Setup device for one shot / on demand mode.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @irq: Optional interrupt line the hardware uses to notify new data
+ * samples are ready. Negative or zero values indicate no interrupts
+ * are available, meaning polling is required.
+ *
+ * Output Data Rate is configured for the highest possible rate so that
+ * conversion time and power consumption are reduced to a minimum.
+ * Note that hardware internal averaging machinery (not implemented in this
+ * driver) is not applicable in this mode.
+ *
+ * Device must have been previously enabled before calling
+ * zpa2326_config_oneshot().
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_config_oneshot(const struct iio_dev *indio_dev,
+ int irq)
+{
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ const struct zpa2326_frequency *freq = zpa2326_highest_frequency();
+ int err;
+
+ /* Setup highest available Output Data Rate for one shot mode. */
+ err = regmap_write(regs, ZPA2326_CTRL_REG3_REG, freq->odr);
+ if (err)
+ return err;
+
+ if (irq > 0) {
+ /* Request interrupt when new sample is available. */
+ err = regmap_write(regs, ZPA2326_CTRL_REG1_REG,
+ (u8)~ZPA2326_CTRL_REG1_MASK_DATA_READY);
+
+ if (err) {
+ dev_err(indio_dev->dev.parent,
+ "failed to setup one shot mode (%d)", err);
+ return err;
+ }
+ }
+
+ zpa2326_dbg(indio_dev, "one shot mode setup @%dHz", freq->hz);
+
+ return 0;
+}
+
+/**
+ * zpa2326_clear_fifo() - Clear remaining entries in hardware FIFO.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @min_count: Number of samples present within hardware FIFO.
+ *
+ * @min_count argument is a hint corresponding to the known minimum number of
+ * samples currently living in the FIFO. This allows to reduce the number of bus
+ * accesses by skipping status register read operation as long as we know for
+ * sure there are still entries left.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_clear_fifo(const struct iio_dev *indio_dev,
+ unsigned int min_count)
+{
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ int err;
+ unsigned int val;
+
+ if (!min_count) {
+ /*
+ * No hint: read status register to determine whether FIFO is
+ * empty or not.
+ */
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+
+ if (err < 0)
+ goto err;
+
+ if (val & ZPA2326_STATUS_FIFO_E)
+ /* Fifo is empty: nothing to trash. */
+ return 0;
+ }
+
+ /* Clear FIFO. */
+ do {
+ /*
+ * A single fetch from pressure MSB register is enough to pop
+ * values out of FIFO.
+ */
+ err = regmap_read(regs, ZPA2326_PRESS_OUT_H_REG, &val);
+ if (err < 0)
+ goto err;
+
+ if (min_count) {
+ /*
+ * We know for sure there are at least min_count entries
+ * left in FIFO. Skip status register read.
+ */
+ min_count--;
+ continue;
+ }
+
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+ if (err < 0)
+ goto err;
+
+ } while (!(val & ZPA2326_STATUS_FIFO_E));
+
+ zpa2326_dbg(indio_dev, "FIFO cleared");
+
+ return 0;
+
+err:
+ zpa2326_err(indio_dev, "failed to clear FIFO (%d)", err);
+
+ return err;
+}
+
+/**
+ * zpa2326_dequeue_pressure() - Retrieve the most recent pressure sample from
+ * hardware FIFO.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @pressure: Sampled pressure output.
+ *
+ * Note that ZPA2326 hardware FIFO stores pressure samples only.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_dequeue_pressure(const struct iio_dev *indio_dev,
+ u32 *pressure)
+{
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ unsigned int val;
+ int err;
+ int cleared = -1;
+
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+ if (err < 0)
+ return err;
+
+ *pressure = 0;
+
+ if (val & ZPA2326_STATUS_P_OR) {
+ /*
+ * Fifo overrun : first sample dequeued from FIFO is the
+ * newest.
+ */
+ zpa2326_warn(indio_dev, "FIFO overflow");
+
+ err = regmap_bulk_read(regs, ZPA2326_PRESS_OUT_XL_REG, pressure,
+ 3);
+ if (err)
+ return err;
+
+#define ZPA2326_FIFO_DEPTH (16U)
+ /* Hardware FIFO may hold no more than 16 pressure samples. */
+ return zpa2326_clear_fifo(indio_dev, ZPA2326_FIFO_DEPTH - 1);
+ }
+
+ /*
+ * Fifo has not overflown : retrieve newest sample. We need to pop
+ * values out until FIFO is empty : last fetched pressure is the newest.
+ * In nominal cases, we should find a single queued sample only.
+ */
+ do {
+ err = regmap_bulk_read(regs, ZPA2326_PRESS_OUT_XL_REG, pressure,
+ 3);
+ if (err)
+ return err;
+
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+ if (err < 0)
+ return err;
+
+ cleared++;
+ } while (!(val & ZPA2326_STATUS_FIFO_E));
+
+ if (cleared)
+ /*
+ * Samples were pushed by hardware during previous rounds but we
+ * didn't consume them fast enough: inform user.
+ */
+ zpa2326_dbg(indio_dev, "cleared %d FIFO entries", cleared);
+
+ return 0;
+}
+
+/**
+ * zpa2326_fill_sample_buffer() - Enqueue new channel samples to IIO buffer.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_fill_sample_buffer(struct iio_dev *indio_dev,
+ const struct zpa2326_private *private)
+{
+ struct {
+ u32 pressure;
+ u16 temperature;
+ u64 timestamp;
+ } sample;
+ int err;
+
+ if (test_bit(0, indio_dev->active_scan_mask)) {
+ /* Get current pressure from hardware FIFO. */
+ err = zpa2326_dequeue_pressure(indio_dev, &sample.pressure);
+ if (err) {
+ zpa2326_warn(indio_dev, "failed to fetch pressure (%d)",
+ err);
+ return err;
+ }
+ }
+
+ if (test_bit(1, indio_dev->active_scan_mask)) {
+ /* Get current temperature. */
+ err = regmap_bulk_read(private->regmap, ZPA2326_TEMP_OUT_L_REG,
+ &sample.temperature, 2);
+ if (err) {
+ zpa2326_warn(indio_dev,
+ "failed to fetch temperature (%d)", err);
+ return err;
+ }
+ }
+
+ /*
+ * Now push samples using timestamp stored either :
+ * - by hardware interrupt handler if interrupt is available: see
+ * zpa2326_handle_irq(),
+ * - or oneshot completion polling machinery : see
+ * zpa2326_trigger_handler().
+ */
+ zpa2326_dbg(indio_dev, "filling raw samples buffer");
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &sample,
+ private->timestamp);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int zpa2326_runtime_suspend(struct device *parent)
+{
+ const struct iio_dev *indio_dev = dev_get_drvdata(parent);
+
+ if (pm_runtime_autosuspend_expiration(parent))
+ /* Userspace changed autosuspend delay. */
+ return -EAGAIN;
+
+ zpa2326_power_off(indio_dev, iio_priv(indio_dev));
+
+ return 0;
+}
+
+static int zpa2326_runtime_resume(struct device *parent)
+{
+ const struct iio_dev *indio_dev = dev_get_drvdata(parent);
+
+ return zpa2326_power_on(indio_dev, iio_priv(indio_dev));
+}
+
+const struct dev_pm_ops zpa2326_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(zpa2326_runtime_suspend, zpa2326_runtime_resume,
+ NULL)
+};
+EXPORT_SYMBOL_GPL(zpa2326_pm_ops);
+
+/**
+ * zpa2326_resume() - Request the PM layer to power supply the device.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Return:
+ * < 0 - a negative error code meaning failure ;
+ * 0 - success, device has just been powered up ;
+ * 1 - success, device was already powered.
+ */
+static int zpa2326_resume(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = pm_runtime_get_sync(indio_dev->dev.parent);
+ if (err < 0)
+ return err;
+
+ if (err > 0) {
+ /*
+ * Device was already power supplied: get it out of low power
+ * mode and inform caller.
+ */
+ zpa2326_enable_device(indio_dev);
+ return 1;
+ }
+
+ /* Inform caller device has just been brought back to life. */
+ return 0;
+}
+
+/**
+ * zpa2326_suspend() - Schedule a power down using autosuspend feature of PM
+ * layer.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Device is switched to low power mode at first to save power even when
+ * attached regulator is a "dummy" one.
+ */
+static void zpa2326_suspend(struct iio_dev *indio_dev)
+{
+ struct device *parent = indio_dev->dev.parent;
+
+ zpa2326_sleep(indio_dev);
+
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+}
+
+static void zpa2326_init_runtime(struct device *parent)
+{
+ pm_runtime_get_noresume(parent);
+ pm_runtime_set_active(parent);
+ pm_runtime_enable(parent);
+ pm_runtime_set_autosuspend_delay(parent, 1000);
+ pm_runtime_use_autosuspend(parent);
+ pm_runtime_mark_last_busy(parent);
+ pm_runtime_put_autosuspend(parent);
+}
+
+static void zpa2326_fini_runtime(struct device *parent)
+{
+ pm_runtime_disable(parent);
+ pm_runtime_set_suspended(parent);
+}
+#else /* !CONFIG_PM */
+static int zpa2326_resume(const struct iio_dev *indio_dev)
+{
+ zpa2326_enable_device(indio_dev);
+
+ return 0;
+}
+
+static void zpa2326_suspend(struct iio_dev *indio_dev)
+{
+ zpa2326_sleep(indio_dev);
+}
+
+#define zpa2326_init_runtime(_parent)
+#define zpa2326_fini_runtime(_parent)
+#endif /* !CONFIG_PM */
+
+/**
+ * zpa2326_handle_irq() - Process hardware interrupts.
+ * @irq: Interrupt line the hardware uses to notify new data has arrived.
+ * @data: The IIO device associated with the sampling hardware.
+ *
+ * Timestamp buffered samples as soon as possible then schedule threaded bottom
+ * half.
+ *
+ * Return: Always successful.
+ */
+static irqreturn_t zpa2326_handle_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = (struct iio_dev *)data;
+
+ if (iio_buffer_enabled(indio_dev)) {
+ /* Timestamping needed for buffered sampling only. */
+ ((struct zpa2326_private *)
+ iio_priv(indio_dev))->timestamp = iio_get_time_ns(indio_dev);
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+/**
+ * zpa2326_handle_threaded_irq() - Interrupt bottom-half handler.
+ * @irq: Interrupt line the hardware uses to notify new data has arrived.
+ * @data: The IIO device associated with the sampling hardware.
+ *
+ * Mainly ensures interrupt is caused by a real "new sample available"
+ * condition. This relies upon the ability to perform blocking / sleeping bus
+ * accesses to slave's registers. This is why zpa2326_handle_threaded_irq() is
+ * called from within a thread, i.e. not called from hard interrupt context.
+ *
+ * When device is using its own internal hardware trigger in continuous sampling
+ * mode, data are available into hardware FIFO once interrupt has occurred. All
+ * we have to do is to dispatch the trigger, which in turn will fetch data and
+ * fill IIO buffer.
+ *
+ * When not using its own internal hardware trigger, the device has been
+ * configured in one-shot mode either by an external trigger or the IIO read_raw
+ * hook. This means one of the latter is currently waiting for sampling
+ * completion, in which case we must simply wake it up.
+ *
+ * See zpa2326_trigger_handler().
+ *
+ * Return:
+ * %IRQ_NONE - no consistent interrupt happened ;
+ * %IRQ_HANDLED - there was new samples available.
+ */
+static irqreturn_t zpa2326_handle_threaded_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = (struct iio_dev *)data;
+ struct zpa2326_private *priv = iio_priv(indio_dev);
+ unsigned int val;
+ bool cont;
+ irqreturn_t ret = IRQ_NONE;
+
+ /*
+ * Are we using our own internal trigger in triggered buffer mode, i.e.,
+ * currently working in continuous sampling mode ?
+ */
+ cont = (iio_buffer_enabled(indio_dev) &&
+ iio_trigger_using_own(indio_dev));
+
+ /*
+ * Device works according to a level interrupt scheme: reading interrupt
+ * status de-asserts interrupt line.
+ */
+ priv->result = regmap_read(priv->regmap, ZPA2326_INT_SOURCE_REG, &val);
+ if (priv->result < 0) {
+ if (cont)
+ return IRQ_NONE;
+
+ goto complete;
+ }
+
+ /* Data ready is the only interrupt source we requested. */
+ if (!(val & ZPA2326_INT_SOURCE_DATA_READY)) {
+ /*
+ * Interrupt happened but no new sample available: likely caused
+ * by spurious interrupts, in which case, returning IRQ_NONE
+ * allows to benefit from the generic spurious interrupts
+ * handling.
+ */
+ zpa2326_warn(indio_dev, "unexpected interrupt status %02x",
+ val);
+
+ if (cont)
+ return IRQ_NONE;
+
+ priv->result = -ENODATA;
+ goto complete;
+ }
+
+ /* New sample available: dispatch internal trigger consumers. */
+ iio_trigger_poll_chained(priv->trigger);
+
+ if (cont)
+ /*
+ * Internal hardware trigger has been scheduled above : it will
+ * fetch data on its own.
+ */
+ return IRQ_HANDLED;
+
+ ret = IRQ_HANDLED;
+
+complete:
+ /*
+ * Wake up direct or externaly triggered buffer mode waiters: see
+ * zpa2326_sample_oneshot() and zpa2326_trigger_handler().
+ */
+ complete(&priv->data_ready);
+
+ return ret;
+}
+
+/**
+ * zpa2326_wait_oneshot_completion() - Wait for oneshot data ready interrupt.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_wait_oneshot_completion(const struct iio_dev *indio_dev,
+ struct zpa2326_private *private)
+{
+ int ret;
+ unsigned int val;
+
+ zpa2326_dbg(indio_dev, "waiting for one shot completion interrupt");
+
+ ret = wait_for_completion_interruptible_timeout(
+ &private->data_ready, ZPA2326_CONVERSION_JIFFIES);
+ if (ret > 0)
+ /*
+ * Interrupt handler completed before timeout: return operation
+ * status.
+ */
+ return private->result;
+
+ /* Clear all interrupts just to be sure. */
+ regmap_read(private->regmap, ZPA2326_INT_SOURCE_REG, &val);
+
+ if (!ret)
+ /* Timed out. */
+ ret = -ETIME;
+
+ if (ret != -ERESTARTSYS)
+ zpa2326_warn(indio_dev, "no one shot interrupt occurred (%d)",
+ ret);
+
+ return ret;
+}
+
+static int zpa2326_init_managed_irq(struct device *parent,
+ struct iio_dev *indio_dev,
+ struct zpa2326_private *private,
+ int irq)
+{
+ int err;
+
+ private->irq = irq;
+
+ if (irq <= 0) {
+ /*
+ * Platform declared no interrupt line: device will be polled
+ * for data availability.
+ */
+ dev_info(parent, "no interrupt found, running in polling mode");
+ return 0;
+ }
+
+ init_completion(&private->data_ready);
+
+ /* Request handler to be scheduled into threaded interrupt context. */
+ err = devm_request_threaded_irq(parent, irq, zpa2326_handle_irq,
+ zpa2326_handle_threaded_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(parent), indio_dev);
+ if (err) {
+ dev_err(parent, "failed to request interrupt %d (%d)", irq,
+ err);
+ return err;
+ }
+
+ dev_info(parent, "using interrupt %d", irq);
+
+ return 0;
+}
+
+/**
+ * zpa2326_poll_oneshot_completion() - Actively poll for one shot data ready.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Loop over registers content to detect end of sampling cycle. Used when DT
+ * declared no valid interrupt lines.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_poll_oneshot_completion(const struct iio_dev *indio_dev)
+{
+ unsigned long tmout = jiffies + ZPA2326_CONVERSION_JIFFIES;
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ unsigned int val;
+ int err;
+
+ zpa2326_dbg(indio_dev, "polling for one shot completion");
+
+ /*
+ * At least, 100 ms is needed for the device to complete its one-shot
+ * cycle.
+ */
+ if (msleep_interruptible(100))
+ return -ERESTARTSYS;
+
+ /* Poll for conversion completion in hardware. */
+ while (true) {
+ err = regmap_read(regs, ZPA2326_CTRL_REG0_REG, &val);
+ if (err < 0)
+ goto err;
+
+ if (!(val & ZPA2326_CTRL_REG0_ONE_SHOT))
+ /* One-shot bit self clears at conversion end. */
+ break;
+
+ if (time_after(jiffies, tmout)) {
+ /* Prevent from waiting forever : let's time out. */
+ err = -ETIME;
+ goto err;
+ }
+
+ usleep_range(10000, 20000);
+ }
+
+ /*
+ * In oneshot mode, pressure sample availability guarantees that
+ * temperature conversion has also completed : just check pressure
+ * status bit to keep things simple.
+ */
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+ if (err < 0)
+ goto err;
+
+ if (!(val & ZPA2326_STATUS_P_DA)) {
+ /* No sample available. */
+ err = -ENODATA;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ zpa2326_warn(indio_dev, "failed to poll one shot completion (%d)", err);
+
+ return err;
+}
+
+/**
+ * zpa2326_fetch_raw_sample() - Retrieve a raw sample and convert it to CPU
+ * endianness.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @type: Type of measurement / channel to fetch from.
+ * @value: Sample output.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_fetch_raw_sample(const struct iio_dev *indio_dev,
+ enum iio_chan_type type,
+ int *value)
+{
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ int err;
+
+ switch (type) {
+ case IIO_PRESSURE:
+ zpa2326_dbg(indio_dev, "fetching raw pressure sample");
+
+ err = regmap_bulk_read(regs, ZPA2326_PRESS_OUT_XL_REG, value,
+ 3);
+ if (err) {
+ zpa2326_warn(indio_dev, "failed to fetch pressure (%d)",
+ err);
+ return err;
+ }
+
+ /* Pressure is a 24 bits wide little-endian unsigned int. */
+ *value = (((u8 *)value)[2] << 16) | (((u8 *)value)[1] << 8) |
+ ((u8 *)value)[0];
+
+ return IIO_VAL_INT;
+
+ case IIO_TEMP:
+ zpa2326_dbg(indio_dev, "fetching raw temperature sample");
+
+ err = regmap_bulk_read(regs, ZPA2326_TEMP_OUT_L_REG, value, 2);
+ if (err) {
+ zpa2326_warn(indio_dev,
+ "failed to fetch temperature (%d)", err);
+ return err;
+ }
+
+ /* Temperature is a 16 bits wide little-endian signed int. */
+ *value = (int)le16_to_cpup((__le16 *)value);
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * zpa2326_sample_oneshot() - Perform a complete one shot sampling cycle.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @type: Type of measurement / channel to fetch from.
+ * @value: Sample output.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_sample_oneshot(struct iio_dev *indio_dev,
+ enum iio_chan_type type,
+ int *value)
+{
+ int ret;
+ struct zpa2326_private *priv;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = zpa2326_resume(indio_dev);
+ if (ret < 0)
+ goto release;
+
+ priv = iio_priv(indio_dev);
+
+ if (ret > 0) {
+ /*
+ * We were already power supplied. Just clear hardware FIFO to
+ * get rid of samples acquired during previous rounds (if any).
+ * Sampling operation always generates both temperature and
+ * pressure samples. The latter are always enqueued into
+ * hardware FIFO. This may lead to situations were pressure
+ * samples still sit into FIFO when previous cycle(s) fetched
+ * temperature data only.
+ * Hence, we need to clear hardware FIFO content to prevent from
+ * getting outdated values at the end of current cycle.
+ */
+ if (type == IIO_PRESSURE) {
+ ret = zpa2326_clear_fifo(indio_dev, 0);
+ if (ret)
+ goto suspend;
+ }
+ } else {
+ /*
+ * We have just been power supplied, i.e. device is in default
+ * "out of reset" state, meaning we need to reconfigure it
+ * entirely.
+ */
+ ret = zpa2326_config_oneshot(indio_dev, priv->irq);
+ if (ret)
+ goto suspend;
+ }
+
+ /* Start a sampling cycle in oneshot mode. */
+ ret = zpa2326_start_oneshot(indio_dev);
+ if (ret)
+ goto suspend;
+
+ /* Wait for sampling cycle to complete. */
+ if (priv->irq > 0)
+ ret = zpa2326_wait_oneshot_completion(indio_dev, priv);
+ else
+ ret = zpa2326_poll_oneshot_completion(indio_dev);
+
+ if (ret)
+ goto suspend;
+
+ /* Retrieve raw sample value and convert it to CPU endianness. */
+ ret = zpa2326_fetch_raw_sample(indio_dev, type, value);
+
+suspend:
+ zpa2326_suspend(indio_dev);
+release:
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+/**
+ * zpa2326_trigger_handler() - Perform an IIO buffered sampling round in one
+ * shot mode.
+ * @irq: The software interrupt assigned to @data
+ * @data: The IIO poll function dispatched by external trigger our device is
+ * attached to.
+ *
+ * Bottom-half handler called by the IIO trigger to which our device is
+ * currently attached. Allows us to synchronize this device buffered sampling
+ * either with external events (such as timer expiration, external device sample
+ * ready, etc...) or with its own interrupt (internal hardware trigger).
+ *
+ * When using an external trigger, basically run the same sequence of operations
+ * as for zpa2326_sample_oneshot() with the following hereafter. Hardware FIFO
+ * is not cleared since already done at buffering enable time and samples
+ * dequeueing always retrieves the most recent value.
+ *
+ * Otherwise, when internal hardware trigger has dispatched us, just fetch data
+ * from hardware FIFO.
+ *
+ * Fetched data will pushed unprocessed to IIO buffer since samples conversion
+ * is delegated to userspace in buffered mode (endianness, etc...).
+ *
+ * Return:
+ * %IRQ_NONE - no consistent interrupt happened ;
+ * %IRQ_HANDLED - there was new samples available.
+ */
+static irqreturn_t zpa2326_trigger_handler(int irq, void *data)
+{
+ struct iio_dev *indio_dev = ((struct iio_poll_func *)
+ data)->indio_dev;
+ struct zpa2326_private *priv = iio_priv(indio_dev);
+ bool cont;
+
+ /*
+ * We have been dispatched, meaning we are in triggered buffer mode.
+ * Using our own internal trigger implies we are currently in continuous
+ * hardware sampling mode.
+ */
+ cont = iio_trigger_using_own(indio_dev);
+
+ if (!cont) {
+ /* On demand sampling : start a one shot cycle. */
+ if (zpa2326_start_oneshot(indio_dev))
+ goto out;
+
+ /* Wait for sampling cycle to complete. */
+ if (priv->irq <= 0) {
+ /* No interrupt available: poll for completion. */
+ if (zpa2326_poll_oneshot_completion(indio_dev))
+ goto out;
+
+ /* Only timestamp sample once it is ready. */
+ priv->timestamp = iio_get_time_ns(indio_dev);
+ } else {
+ /* Interrupt handlers will timestamp for us. */
+ if (zpa2326_wait_oneshot_completion(indio_dev, priv))
+ goto out;
+ }
+ }
+
+ /* Enqueue to IIO buffer / userspace. */
+ zpa2326_fill_sample_buffer(indio_dev, priv);
+
+out:
+ if (!cont)
+ /* Don't switch to low power if sampling continuously. */
+ zpa2326_sleep(indio_dev);
+
+ /* Inform attached trigger we are done. */
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * zpa2326_preenable_buffer() - Prepare device for configuring triggered
+ * sampling
+ * modes.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Basically power up device.
+ * Called with IIO device's lock held.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_preenable_buffer(struct iio_dev *indio_dev)
+{
+ int ret = zpa2326_resume(indio_dev);
+
+ if (ret < 0)
+ return ret;
+
+ /* Tell zpa2326_postenable_buffer() if we have just been powered on. */
+ ((struct zpa2326_private *)
+ iio_priv(indio_dev))->waken = iio_priv(indio_dev);
+
+ return 0;
+}
+
+/**
+ * zpa2326_postenable_buffer() - Configure device for triggered sampling.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Basically setup one-shot mode if plugging external trigger.
+ * Otherwise, let internal trigger configure continuous sampling :
+ * see zpa2326_set_trigger_state().
+ *
+ * If an error is returned, IIO layer will call our postdisable hook for us,
+ * i.e. no need to explicitly power device off here.
+ * Called with IIO device's lock held.
+ *
+ * Called with IIO device's lock held.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_postenable_buffer(struct iio_dev *indio_dev)
+{
+ const struct zpa2326_private *priv = iio_priv(indio_dev);
+ int err;
+
+ if (!priv->waken) {
+ /*
+ * We were already power supplied. Just clear hardware FIFO to
+ * get rid of samples acquired during previous rounds (if any).
+ */
+ err = zpa2326_clear_fifo(indio_dev, 0);
+ if (err)
+ goto err;
+ }
+
+ if (!iio_trigger_using_own(indio_dev) && priv->waken) {
+ /*
+ * We are using an external trigger and we have just been
+ * powered up: reconfigure one-shot mode.
+ */
+ err = zpa2326_config_oneshot(indio_dev, priv->irq);
+ if (err)
+ goto err;
+ }
+
+ /* Plug our own trigger event handler. */
+ err = iio_triggered_buffer_postenable(indio_dev);
+ if (err)
+ goto err;
+
+ return 0;
+
+err:
+ zpa2326_err(indio_dev, "failed to enable buffering (%d)", err);
+
+ return err;
+}
+
+static int zpa2326_postdisable_buffer(struct iio_dev *indio_dev)
+{
+ zpa2326_suspend(indio_dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops zpa2326_buffer_setup_ops = {
+ .preenable = zpa2326_preenable_buffer,
+ .postenable = zpa2326_postenable_buffer,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = zpa2326_postdisable_buffer
+};
+
+/**
+ * zpa2326_set_trigger_state() - Start / stop continuous sampling.
+ * @trig: The trigger being attached to IIO device associated with the sampling
+ * hardware.
+ * @state: Tell whether to start (true) or stop (false)
+ *
+ * Basically enable / disable hardware continuous sampling mode.
+ *
+ * Called with IIO device's lock held at postenable() or predisable() time.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+ const struct iio_dev *indio_dev = dev_get_drvdata(
+ trig->dev.parent);
+ const struct zpa2326_private *priv = iio_priv(indio_dev);
+ int err;
+
+ if (!state) {
+ /*
+ * Switch trigger off : in case of failure, interrupt is left
+ * disabled in order to prevent handler from accessing released
+ * resources.
+ */
+ unsigned int val;
+
+ /*
+ * As device is working in continuous mode, handlers may be
+ * accessing resources we are currently freeing...
+ * Prevent this by disabling interrupt handlers and ensure
+ * the device will generate no more interrupts unless explicitly
+ * required to, i.e. by restoring back to default one shot mode.
+ */
+ disable_irq(priv->irq);
+
+ /*
+ * Disable continuous sampling mode to restore settings for
+ * one shot / direct sampling operations.
+ */
+ err = regmap_write(priv->regmap, ZPA2326_CTRL_REG3_REG,
+ zpa2326_highest_frequency()->odr);
+ if (err)
+ return err;
+
+ /*
+ * Now that device won't generate interrupts on its own,
+ * acknowledge any currently active interrupts (may happen on
+ * rare occasions while stopping continuous mode).
+ */
+ err = regmap_read(priv->regmap, ZPA2326_INT_SOURCE_REG, &val);
+ if (err < 0)
+ return err;
+
+ /*
+ * Re-enable interrupts only if we can guarantee the device will
+ * generate no more interrupts to prevent handlers from
+ * accessing released resources.
+ */
+ enable_irq(priv->irq);
+
+ zpa2326_dbg(indio_dev, "continuous mode stopped");
+ } else {
+ /*
+ * Switch trigger on : start continuous sampling at required
+ * frequency.
+ */
+
+ if (priv->waken) {
+ /* Enable interrupt if getting out of reset. */
+ err = regmap_write(priv->regmap, ZPA2326_CTRL_REG1_REG,
+ (u8)
+ ~ZPA2326_CTRL_REG1_MASK_DATA_READY);
+ if (err)
+ return err;
+ }
+
+ /* Enable continuous sampling at specified frequency. */
+ err = regmap_write(priv->regmap, ZPA2326_CTRL_REG3_REG,
+ ZPA2326_CTRL_REG3_ENABLE_MEAS |
+ priv->frequency->odr);
+ if (err)
+ return err;
+
+ zpa2326_dbg(indio_dev, "continuous mode setup @%dHz",
+ priv->frequency->hz);
+ }
+
+ return 0;
+}
+
+static const struct iio_trigger_ops zpa2326_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = zpa2326_set_trigger_state,
+};
+
+/**
+ * zpa2326_init_trigger() - Create an interrupt driven / hardware trigger
+ * allowing to notify external devices a new sample is
+ * ready.
+ * @parent: Hardware sampling device @indio_dev is a child of.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ * @irq: Optional interrupt line the hardware uses to notify new data
+ * samples are ready. Negative or zero values indicate no interrupts
+ * are available, meaning polling is required.
+ *
+ * Only relevant when DT declares a valid interrupt line.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_init_managed_trigger(struct device *parent,
+ struct iio_dev *indio_dev,
+ struct zpa2326_private *private,
+ int irq)
+{
+ struct iio_trigger *trigger;
+ int ret;
+
+ if (irq <= 0)
+ return 0;
+
+ trigger = devm_iio_trigger_alloc(parent, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+ if (!trigger)
+ return -ENOMEM;
+
+ /* Basic setup. */
+ trigger->dev.parent = parent;
+ trigger->ops = &zpa2326_trigger_ops;
+
+ private->trigger = trigger;
+
+ /* Register to triggers space. */
+ ret = devm_iio_trigger_register(parent, trigger);
+ if (ret)
+ dev_err(parent, "failed to register hardware trigger (%d)",
+ ret);
+
+ return ret;
+}
+
+static int zpa2326_get_frequency(const struct iio_dev *indio_dev)
+{
+ return ((struct zpa2326_private *)iio_priv(indio_dev))->frequency->hz;
+}
+
+static int zpa2326_set_frequency(struct iio_dev *indio_dev, int hz)
+{
+ struct zpa2326_private *priv = iio_priv(indio_dev);
+ int freq;
+ int err;
+
+ /* Check if requested frequency is supported. */
+ for (freq = 0; freq < ARRAY_SIZE(zpa2326_sampling_frequencies); freq++)
+ if (zpa2326_sampling_frequencies[freq].hz == hz)
+ break;
+ if (freq == ARRAY_SIZE(zpa2326_sampling_frequencies))
+ return -EINVAL;
+
+ /* Don't allow changing frequency if buffered sampling is ongoing. */
+ err = iio_device_claim_direct_mode(indio_dev);
+ if (err)
+ return err;
+
+ priv->frequency = &zpa2326_sampling_frequencies[freq];
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return 0;
+}
+
+/* Expose supported hardware sampling frequencies (Hz) through sysfs. */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 5 11 23");
+
+static struct attribute *zpa2326_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group zpa2326_attribute_group = {
+ .attrs = zpa2326_attributes,
+};
+
+static int zpa2326_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return zpa2326_sample_oneshot(indio_dev, chan->type, val);
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ /*
+ * Pressure resolution is 1/64 Pascal. Scale to kPascal
+ * as required by IIO ABI.
+ */
+ *val = 1;
+ *val2 = 64000;
+ return IIO_VAL_FRACTIONAL;
+
+ case IIO_TEMP:
+ /*
+ * Temperature follows the equation:
+ * Temp[degC] = Tempcode * 0.00649 - 176.83
+ * where:
+ * Tempcode is composed the raw sampled 16 bits.
+ *
+ * Hence, to produce a temperature in milli-degrees
+ * Celsius according to IIO ABI, we need to apply the
+ * following equation to raw samples:
+ * Temp[milli degC] = (Tempcode + Offset) * Scale
+ * where:
+ * Offset = -176.83 / 0.00649
+ * Scale = 0.00649 * 1000
+ */
+ *val = 6;
+ *val2 = 490000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = -17683000;
+ *val2 = 649;
+ return IIO_VAL_FRACTIONAL;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = zpa2326_get_frequency(indio_dev);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int zpa2326_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ if ((mask != IIO_CHAN_INFO_SAMP_FREQ) || val2)
+ return -EINVAL;
+
+ return zpa2326_set_frequency(indio_dev, val);
+}
+
+static const struct iio_chan_spec zpa2326_channels[] = {
+ [0] = {
+ .type = IIO_PRESSURE,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ },
+ [1] = {
+ .type = IIO_TEMP,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ },
+ [2] = IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const struct iio_info zpa2326_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &zpa2326_attribute_group,
+ .read_raw = zpa2326_read_raw,
+ .write_raw = zpa2326_write_raw,
+};
+
+static struct iio_dev *zpa2326_create_managed_iiodev(struct device *device,
+ const char *name,
+ struct regmap *regmap)
+{
+ struct iio_dev *indio_dev;
+
+ /* Allocate space to hold IIO device internal state. */
+ indio_dev = devm_iio_device_alloc(device,
+ sizeof(struct zpa2326_private));
+ if (!indio_dev)
+ return NULL;
+
+ /* Setup for userspace synchronous on demand sampling. */
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->dev.parent = device;
+ indio_dev->channels = zpa2326_channels;
+ indio_dev->num_channels = ARRAY_SIZE(zpa2326_channels);
+ indio_dev->name = name;
+ indio_dev->info = &zpa2326_info;
+
+ return indio_dev;
+}
+
+int zpa2326_probe(struct device *parent,
+ const char *name,
+ int irq,
+ unsigned int hwid,
+ struct regmap *regmap)
+{
+ struct iio_dev *indio_dev;
+ struct zpa2326_private *priv;
+ int err;
+ unsigned int id;
+
+ indio_dev = zpa2326_create_managed_iiodev(parent, name, regmap);
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+
+ priv->vref = devm_regulator_get(parent, "vref");
+ if (IS_ERR(priv->vref))
+ return PTR_ERR(priv->vref);
+
+ priv->vdd = devm_regulator_get(parent, "vdd");
+ if (IS_ERR(priv->vdd))
+ return PTR_ERR(priv->vdd);
+
+ /* Set default hardware sampling frequency to highest rate supported. */
+ priv->frequency = zpa2326_highest_frequency();
+
+ /*
+ * Plug device's underlying bus abstraction : this MUST be set before
+ * registering interrupt handlers since an interrupt might happen if
+ * power up sequence is not properly applied.
+ */
+ priv->regmap = regmap;
+
+ err = devm_iio_triggered_buffer_setup(parent, indio_dev, NULL,
+ zpa2326_trigger_handler,
+ &zpa2326_buffer_setup_ops);
+ if (err)
+ return err;
+
+ err = zpa2326_init_managed_trigger(parent, indio_dev, priv, irq);
+ if (err)
+ return err;
+
+ err = zpa2326_init_managed_irq(parent, indio_dev, priv, irq);
+ if (err)
+ return err;
+
+ /* Power up to check device ID and perform initial hardware setup. */
+ err = zpa2326_power_on(indio_dev, priv);
+ if (err)
+ return err;
+
+ /* Read id register to check we are talking to the right slave. */
+ err = regmap_read(regmap, ZPA2326_DEVICE_ID_REG, &id);
+ if (err)
+ goto sleep;
+
+ if (id != hwid) {
+ dev_err(parent, "found device with unexpected id %02x", id);
+ err = -ENODEV;
+ goto sleep;
+ }
+
+ err = zpa2326_config_oneshot(indio_dev, irq);
+ if (err)
+ goto sleep;
+
+ /* Setup done : go sleeping. Device will be awaken upon user request. */
+ err = zpa2326_sleep(indio_dev);
+ if (err)
+ goto poweroff;
+
+ dev_set_drvdata(parent, indio_dev);
+
+ zpa2326_init_runtime(parent);
+
+ err = iio_device_register(indio_dev);
+ if (err) {
+ zpa2326_fini_runtime(parent);
+ goto poweroff;
+ }
+
+ return 0;
+
+sleep:
+ /* Put to sleep just in case power regulators are "dummy" ones. */
+ zpa2326_sleep(indio_dev);
+poweroff:
+ zpa2326_power_off(indio_dev, priv);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(zpa2326_probe);
+
+void zpa2326_remove(const struct device *parent)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(parent);
+
+ iio_device_unregister(indio_dev);
+ zpa2326_fini_runtime(indio_dev->dev.parent);
+ zpa2326_sleep(indio_dev);
+ zpa2326_power_off(indio_dev, iio_priv(indio_dev));
+}
+EXPORT_SYMBOL_GPL(zpa2326_remove);
+
+MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
+MODULE_DESCRIPTION("Core driver for Murata ZPA2326 pressure sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/zpa2326.h b/drivers/iio/pressure/zpa2326.h
new file mode 100644
index 000000000000..05d3e1e3a449
--- /dev/null
+++ b/drivers/iio/pressure/zpa2326.h
@@ -0,0 +1,89 @@
+/*
+ * Murata ZPA2326 pressure and temperature sensor IIO driver
+ *
+ * Copyright (c) 2016 Parrot S.A.
+ *
+ * Author: Gregor Boirie <gregor.boirie@parrot.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#ifndef _ZPA2326_H
+#define _ZPA2326_H
+
+/* Register map. */
+#define ZPA2326_REF_P_XL_REG (0x8)
+#define ZPA2326_REF_P_L_REG (0x9)
+#define ZPA2326_REF_P_H_REG (0xa)
+#define ZPA2326_DEVICE_ID_REG (0xf)
+#define ZPA2326_DEVICE_ID (0xb9)
+#define ZPA2326_RES_CONF_REG (0x10)
+#define ZPA2326_CTRL_REG0_REG (0x20)
+#define ZPA2326_CTRL_REG0_ONE_SHOT BIT(0)
+#define ZPA2326_CTRL_REG0_ENABLE BIT(1)
+#define ZPA2326_CTRL_REG1_REG (0x21)
+#define ZPA2326_CTRL_REG1_MASK_DATA_READY BIT(2)
+#define ZPA2326_CTRL_REG2_REG (0x22)
+#define ZPA2326_CTRL_REG2_SWRESET BIT(2)
+#define ZPA2326_CTRL_REG3_REG (0x23)
+#define ZPA2326_CTRL_REG3_ODR_SHIFT (4)
+#define ZPA2326_CTRL_REG3_ENABLE_MEAS BIT(7)
+#define ZPA2326_INT_SOURCE_REG (0x24)
+#define ZPA2326_INT_SOURCE_DATA_READY BIT(2)
+#define ZPA2326_THS_P_LOW_REG (0x25)
+#define ZPA2326_THS_P_HIGH_REG (0x26)
+#define ZPA2326_STATUS_REG (0x27)
+#define ZPA2326_STATUS_P_DA BIT(1)
+#define ZPA2326_STATUS_FIFO_E BIT(2)
+#define ZPA2326_STATUS_P_OR BIT(5)
+#define ZPA2326_PRESS_OUT_XL_REG (0x28)
+#define ZPA2326_PRESS_OUT_L_REG (0x29)
+#define ZPA2326_PRESS_OUT_H_REG (0x2a)
+#define ZPA2326_TEMP_OUT_L_REG (0x2b)
+#define ZPA2326_TEMP_OUT_H_REG (0x2c)
+
+struct device;
+struct regmap;
+
+bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg);
+bool zpa2326_isreg_readable(struct device *dev, unsigned int reg);
+bool zpa2326_isreg_precious(struct device *dev, unsigned int reg);
+
+/**
+ * zpa2326_probe() - Instantiate and register core ZPA2326 IIO device
+ * @parent: Hardware sampling device the created IIO device will be a child of.
+ * @name: Arbitrary name to identify the device.
+ * @irq: Interrupt line, negative if none.
+ * @hwid: Expected device hardware id.
+ * @regmap: Registers map used to abstract underlying bus accesses.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+int zpa2326_probe(struct device *parent,
+ const char *name,
+ int irq,
+ unsigned int hwid,
+ struct regmap *regmap);
+
+/**
+ * zpa2326_remove() - Unregister and destroy core ZPA2326 IIO device.
+ * @parent: Hardware sampling device the IIO device to remove is a child of.
+ */
+void zpa2326_remove(const struct device *parent);
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+extern const struct dev_pm_ops zpa2326_pm_ops;
+#define ZPA2326_PM_OPS (&zpa2326_pm_ops)
+#else
+#define ZPA2326_PM_OPS (NULL)
+#endif
+
+#endif
diff --git a/drivers/iio/pressure/zpa2326_i2c.c b/drivers/iio/pressure/zpa2326_i2c.c
new file mode 100644
index 000000000000..e4d27dd4493a
--- /dev/null
+++ b/drivers/iio/pressure/zpa2326_i2c.c
@@ -0,0 +1,99 @@
+/*
+ * Murata ZPA2326 I2C pressure and temperature sensor driver
+ *
+ * Copyright (c) 2016 Parrot S.A.
+ *
+ * Author: Gregor Boirie <gregor.boirie@parrot.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include "zpa2326.h"
+
+/*
+ * read_flag_mask:
+ * - address bit 7 must be set to request a register read operation
+ */
+static const struct regmap_config zpa2326_regmap_i2c_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = zpa2326_isreg_writeable,
+ .readable_reg = zpa2326_isreg_readable,
+ .precious_reg = zpa2326_isreg_precious,
+ .max_register = ZPA2326_TEMP_OUT_H_REG,
+ .read_flag_mask = BIT(7),
+ .cache_type = REGCACHE_NONE,
+};
+
+static unsigned int zpa2326_i2c_hwid(const struct i2c_client *client)
+{
+#define ZPA2326_SA0(_addr) (_addr & BIT(0))
+#define ZPA2326_DEVICE_ID_SA0_SHIFT (1)
+
+ /* Identification register bit 1 mirrors device address bit 0. */
+ return (ZPA2326_DEVICE_ID |
+ (ZPA2326_SA0(client->addr) << ZPA2326_DEVICE_ID_SA0_SHIFT));
+}
+
+static int zpa2326_probe_i2c(struct i2c_client *client,
+ const struct i2c_device_id *i2c_id)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &zpa2326_regmap_i2c_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "failed to init registers map");
+ return PTR_ERR(regmap);
+ }
+
+ return zpa2326_probe(&client->dev, i2c_id->name, client->irq,
+ zpa2326_i2c_hwid(client), regmap);
+}
+
+static int zpa2326_remove_i2c(struct i2c_client *client)
+{
+ zpa2326_remove(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id zpa2326_i2c_ids[] = {
+ { "zpa2326", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(i2c, zpa2326_i2c_ids);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id zpa2326_i2c_matches[] = {
+ { .compatible = "murata,zpa2326" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, zpa2326_i2c_matches);
+#endif
+
+static struct i2c_driver zpa2326_i2c_driver = {
+ .driver = {
+ .name = "zpa2326-i2c",
+ .of_match_table = of_match_ptr(zpa2326_i2c_matches),
+ .pm = ZPA2326_PM_OPS,
+ },
+ .probe = zpa2326_probe_i2c,
+ .remove = zpa2326_remove_i2c,
+ .id_table = zpa2326_i2c_ids,
+};
+module_i2c_driver(zpa2326_i2c_driver);
+
+MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
+MODULE_DESCRIPTION("I2C driver for Murata ZPA2326 pressure sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c
new file mode 100644
index 000000000000..bd2c1c319fca
--- /dev/null
+++ b/drivers/iio/pressure/zpa2326_spi.c
@@ -0,0 +1,103 @@
+/*
+ * Murata ZPA2326 SPI pressure and temperature sensor driver
+ *
+ * Copyright (c) 2016 Parrot S.A.
+ *
+ * Author: Gregor Boirie <gregor.boirie@parrot.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include <linux/of_device.h>
+#include "zpa2326.h"
+
+/*
+ * read_flag_mask:
+ * - address bit 7 must be set to request a register read operation
+ * - address bit 6 must be set to request register address auto increment
+ */
+static const struct regmap_config zpa2326_regmap_spi_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = zpa2326_isreg_writeable,
+ .readable_reg = zpa2326_isreg_readable,
+ .precious_reg = zpa2326_isreg_precious,
+ .max_register = ZPA2326_TEMP_OUT_H_REG,
+ .read_flag_mask = BIT(7) | BIT(6),
+ .cache_type = REGCACHE_NONE,
+};
+
+static int zpa2326_probe_spi(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ int err;
+
+ regmap = devm_regmap_init_spi(spi, &zpa2326_regmap_spi_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "failed to init registers map");
+ return PTR_ERR(regmap);
+ }
+
+ /*
+ * Enforce SPI slave settings to prevent from DT misconfiguration.
+ *
+ * Clock is idle high. Sampling happens on trailing edge, i.e., rising
+ * edge. Maximum bus frequency is 1 MHz. Registers are 8 bits wide.
+ */
+ spi->mode = SPI_MODE_3;
+ spi->max_speed_hz = min(spi->max_speed_hz, 1000000U);
+ spi->bits_per_word = 8;
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
+
+ return zpa2326_probe(&spi->dev, spi_get_device_id(spi)->name,
+ spi->irq, ZPA2326_DEVICE_ID, regmap);
+}
+
+static int zpa2326_remove_spi(struct spi_device *spi)
+{
+ zpa2326_remove(&spi->dev);
+
+ return 0;
+}
+
+static const struct spi_device_id zpa2326_spi_ids[] = {
+ { "zpa2326", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id zpa2326_spi_matches[] = {
+ { .compatible = "murata,zpa2326" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, zpa2326_spi_matches);
+#endif
+
+static struct spi_driver zpa2326_spi_driver = {
+ .driver = {
+ .name = "zpa2326-spi",
+ .of_match_table = of_match_ptr(zpa2326_spi_matches),
+ .pm = ZPA2326_PM_OPS,
+ },
+ .probe = zpa2326_probe_spi,
+ .remove = zpa2326_remove_spi,
+ .id_table = zpa2326_spi_ids,
+};
+module_spi_driver(zpa2326_spi_driver);
+
+MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
+MODULE_DESCRIPTION("SPI driver for Murata ZPA2326 pressure sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c
index 1d74b3aafeed..1f06282ec793 100644
--- a/drivers/iio/proximity/sx9500.c
+++ b/drivers/iio/proximity/sx9500.c
@@ -516,7 +516,7 @@ static irqreturn_t sx9500_irq_thread_handler(int irq, void *private)
sx9500_push_events(indio_dev);
if (val & SX9500_CONVDONE_IRQ)
- complete_all(&data->completion);
+ complete(&data->completion);
out:
mutex_unlock(&data->mutex);
@@ -1025,6 +1025,12 @@ static const struct acpi_device_id sx9500_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, sx9500_acpi_match);
+static const struct of_device_id sx9500_of_match[] = {
+ { .compatible = "semtech,sx9500", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sx9500_of_match);
+
static const struct i2c_device_id sx9500_id[] = {
{"sx9500", 0},
{ },
@@ -1035,6 +1041,7 @@ static struct i2c_driver sx9500_driver = {
.driver = {
.name = SX9500_DRIVER_NAME,
.acpi_match_table = ACPI_PTR(sx9500_acpi_match),
+ .of_match_table = of_match_ptr(sx9500_of_match),
.pm = &sx9500_pm_ops,
},
.probe = sx9500_probe,
diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig
index c4664e5de791..5ea77a7e261d 100644
--- a/drivers/iio/temperature/Kconfig
+++ b/drivers/iio/temperature/Kconfig
@@ -3,6 +3,22 @@
#
menu "Temperature sensors"
+config MAXIM_THERMOCOUPLE
+ tristate "Maxim thermocouple sensors"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for the Maxim series of
+ thermocouple sensors connected via SPI.
+
+ Supported sensors:
+ * MAX6675
+ * MAX31855
+
+ This driver can also be built as a module. If so, the module will
+ be called maxim_thermocouple.
+
config MLX90614
tristate "MLX90614 contact-less infrared sensor"
depends on I2C
diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile
index 02bc79d49b24..78c3de0dc3f0 100644
--- a/drivers/iio/temperature/Makefile
+++ b/drivers/iio/temperature/Makefile
@@ -2,6 +2,7 @@
# Makefile for industrial I/O temperature drivers
#
+obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
obj-$(CONFIG_MLX90614) += mlx90614.o
obj-$(CONFIG_TMP006) += tmp006.o
obj-$(CONFIG_TSYS01) += tsys01.o
diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c
new file mode 100644
index 000000000000..39dd2026ccc9
--- /dev/null
+++ b/drivers/iio/temperature/maxim_thermocouple.c
@@ -0,0 +1,281 @@
+/*
+ * maxim_thermocouple.c - Support for Maxim thermocouple chips
+ *
+ * Copyright (C) 2016 Matt Ranostay <mranostay@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mutex.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define MAXIM_THERMOCOUPLE_DRV_NAME "maxim_thermocouple"
+
+enum {
+ MAX6675,
+ MAX31855,
+};
+
+static const struct iio_chan_spec max6675_channels[] = {
+ { /* thermocouple temperature */
+ .type = IIO_TEMP,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 13,
+ .storagebits = 16,
+ .shift = 3,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct iio_chan_spec max31855_channels[] = {
+ { /* thermocouple temperature */
+ .type = IIO_TEMP,
+ .address = 2,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 14,
+ .storagebits = 16,
+ .shift = 2,
+ .endianness = IIO_BE,
+ },
+ },
+ { /* cold junction temperature */
+ .type = IIO_TEMP,
+ .address = 0,
+ .channel2 = IIO_MOD_TEMP_AMBIENT,
+ .modified = 1,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 12,
+ .storagebits = 16,
+ .shift = 4,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const unsigned long max31855_scan_masks[] = {0x3, 0};
+
+struct maxim_thermocouple_chip {
+ const struct iio_chan_spec *channels;
+ const unsigned long *scan_masks;
+ u8 num_channels;
+ u8 read_size;
+
+ /* bit-check for valid input */
+ u32 status_bit;
+};
+
+static const struct maxim_thermocouple_chip maxim_thermocouple_chips[] = {
+ [MAX6675] = {
+ .channels = max6675_channels,
+ .num_channels = ARRAY_SIZE(max6675_channels),
+ .read_size = 2,
+ .status_bit = BIT(2),
+ },
+ [MAX31855] = {
+ .channels = max31855_channels,
+ .num_channels = ARRAY_SIZE(max31855_channels),
+ .read_size = 4,
+ .scan_masks = max31855_scan_masks,
+ .status_bit = BIT(16),
+ },
+};
+
+struct maxim_thermocouple_data {
+ struct spi_device *spi;
+ const struct maxim_thermocouple_chip *chip;
+
+ u8 buffer[16] ____cacheline_aligned;
+};
+
+static int maxim_thermocouple_read(struct maxim_thermocouple_data *data,
+ struct iio_chan_spec const *chan, int *val)
+{
+ unsigned int storage_bytes = data->chip->read_size;
+ unsigned int shift = chan->scan_type.shift + (chan->address * 8);
+ unsigned int buf;
+ int ret;
+
+ ret = spi_read(data->spi, (void *) &buf, storage_bytes);
+ if (ret)
+ return ret;
+
+ switch (storage_bytes) {
+ case 2:
+ *val = be16_to_cpu(buf);
+ break;
+ case 4:
+ *val = be32_to_cpu(buf);
+ break;
+ }
+
+ /* check to be sure this is a valid reading */
+ if (*val & data->chip->status_bit)
+ return -EINVAL;
+
+ *val = sign_extend32(*val >> shift, chan->scan_type.realbits - 1);
+
+ return 0;
+}
+
+static irqreturn_t maxim_thermocouple_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct maxim_thermocouple_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_read(data->spi, data->buffer, data->chip->read_size);
+ if (!ret) {
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ iio_get_time_ns(indio_dev));
+ }
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int maxim_thermocouple_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct maxim_thermocouple_data *data = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = maxim_thermocouple_read(data, chan, val);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (!ret)
+ return IIO_VAL_INT;
+
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel2) {
+ case IIO_MOD_TEMP_AMBIENT:
+ *val = 62;
+ *val2 = 500000; /* 1000 * 0.0625 */
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ *val = 250; /* 1000 * 0.25 */
+ ret = IIO_VAL_INT;
+ };
+ break;
+ }
+
+ return ret;
+}
+
+static const struct iio_info maxim_thermocouple_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = maxim_thermocouple_read_raw,
+};
+
+static int maxim_thermocouple_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct maxim_thermocouple_data *data;
+ const struct maxim_thermocouple_chip *chip =
+ &maxim_thermocouple_chips[id->driver_data];
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &maxim_thermocouple_info;
+ indio_dev->name = MAXIM_THERMOCOUPLE_DRV_NAME;
+ indio_dev->channels = chip->channels;
+ indio_dev->available_scan_masks = chip->scan_masks;
+ indio_dev->num_channels = chip->num_channels;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data = iio_priv(indio_dev);
+ data->spi = spi;
+ data->chip = chip;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ maxim_thermocouple_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_unreg_buffer;
+
+ return 0;
+
+error_unreg_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+}
+
+static int maxim_thermocouple_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id maxim_thermocouple_id[] = {
+ {"max6675", MAX6675},
+ {"max31855", MAX31855},
+ {},
+};
+MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id);
+
+static struct spi_driver maxim_thermocouple_driver = {
+ .driver = {
+ .name = MAXIM_THERMOCOUPLE_DRV_NAME,
+ },
+ .probe = maxim_thermocouple_probe,
+ .remove = maxim_thermocouple_remove,
+ .id_table = maxim_thermocouple_id,
+};
+module_spi_driver(maxim_thermocouple_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("Maxim thermocouple sensors");
+MODULE_LICENSE("GPL");