summaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorGreg Kroah-Hartman2016-10-24 11:34:22 +0200
committerGreg Kroah-Hartman2016-10-24 11:34:22 +0200
commitc7c88e998aff87e0fe3fd3af1d998803ef15fa52 (patch)
treeaa0b4717daaa6847712d5471370582e087a6c629 /drivers/iio
parentStaging: vt6655: Replace dwIoBase by iobase (diff)
parentiio: dac: mcp4725: fix odd_ptr_err.cocci warnings (diff)
downloadkernel-qcow2-linux-c7c88e998aff87e0fe3fd3af1d998803ef15fa52.tar.gz
kernel-qcow2-linux-c7c88e998aff87e0fe3fd3af1d998803ef15fa52.tar.xz
kernel-qcow2-linux-c7c88e998aff87e0fe3fd3af1d998803ef15fa52.zip
Merge tag 'iio-for-4.10a' of git://git.kernel.org/pub/scm/linux/kernel/git/jic23/iio into staging-next
Jonathan writes: First round of IIO new device support, features and cleanups for the 4.10 cycle. Fair number of outreachy related patches in here. Some of these may well have already been picked up by Greg but git will sort that out for us. Also some good staging cleanup work from other sources. Thanks Brian and Lars in particular for this. New device support * ACCES 104-quad-8 - New driver for this 8 channel encoder input board. Lots of new ABI with this one. * AD7766 - New driver supporting AD7766, AD7766-1, AD7766-2, AD7767, AD7767-1 and AD7767-2 24 bit ADCs. * dmard 10 - New driver for this 3 axis accelerometer. * Honeywell ABP pressure sensors. - New driver covering 56 parts in this series (too many to list here!) * HTS221 - New driver to support this relative humidiy and temperature device. * LMP91000 - New driver for this potentiostat (form of chemical sensor). Nice example of use of the buffered consumer interfaces and the use of a consumer provided trigger. * MiraMEMS DA311 - New driver for this 3 axis accelerometer. * MiraMEMS DA280 - New driver for this 3 axis accelerometer. Follow up caught up with vendor prefixes for these. Staging graduations * isl29018 light sensor - Fixes and cleanups listed below (thanks for your hard work on this Brian!) * sca3000 - Fixes and cleanups listed below. This was one of the small set of drivers that went into staging when IIO was first added. Turns out it had a few bugs and needed to be brought into the modern era! Not clear if I am the only person who actually has one of these still wired to a board. New features (Core) - Add an iio_trigger_validate_own_device helper which relies on the device and trigger having the same parent. Convenient to have this for some of the more complex trigger / device interactions. Was hand rolled in a few drivers already so good to bring it into the core. - Add an iio_read_channel_offset in kernel access helper (similar to the existing one for scale). - IIO_ATTR_{RO, WO, RW} and IIO_DEVICE_ATTR_{RO, WO, RW} macros. These lead some rather contrived function naming, but there is no denying they do reduced boilerplate. I'm going to resist their introduction in drivers 'unless' they form part of a larger set of cleanups. - Counter channel type and index type. New features (Drivers) * hdc100x - Triggered buffer support. * mcp4725 - Device tree bindings and support. - Voltage reference selection. * ti-adc0832 - Triggered buffer support. * ti-adc161s626 - Add regulator support allowing _scale and _offset values to be established and exported. New features (Tools) * iio_generic_buffer - -A option to force enable all channels rather than faulting if some are already enabled (like -a does). Followup patches tidied this support up. Cleanups (Core) - Use kmalloc_array in iio_scan_mask_set. - Take event_attrs field of iio_info structure constant - Staging todo list updates. Most of it was long done. - MAINTAINERS had a wrong directory listing. Cleanups (Drivers) * Missing i2c trivial devices entries. * ad5592r - Fix an endian type related sparse warnings. * ad7150 - Constify the event attribute_group structures. * ad7152 - Add some blank lines to improve readability. - Sampling frequency control via chan-info element rather than hand rolled attributes. - add a new lock to avoid use of mlock for non state change related locking. * ad7280 - Constify atrribute_group structure (second patch covers the event ones) * ad7606 (Lars is driving most of the cleanup on this with some additions from Eva) - Fix improper setting of oversampling pins. This has been broken a very long time in this staging driver, so not going to push this back to stable. - Implement oversampling configuration via the chan_info mask element. - Remove an unused int_vref_mv field. - Remove a reundant name field from ad7606_chip_info. - Remove default device configuration from platform_data in favour of whatever the power on defaults are. - Remove out of band error reporting in the kernel log as not providing much information. - Fix oversampling ratio by having 1 be the value for no oversampling. - Avoid allocating buffer for each data capture. - Factor out common code between periodic and one-shot capture. - Move set_drvdat into common code. - Let the common probe function return int rather than jumping through an ERR_PTR. - Pass struct device * into common remove to simplify code. - Always run trigger handler only once per event (no one can remember why it was being possibly done twice). - Move over to the GPIO descriptor API to shorten and clarify code. - Move the buffer code into the main file as it's not optional and is now rather short in this driver. - Fix the naming of the supply regulator. - Rework regulator handling to handle errors including deferred probing. - Tidy up a ptr_err or 0 return. * ad7746 - Sampling frequency control via info_mask element rather than hand rolled * ad7758 - Sampling frequency control via info_mask element rather than hand rolled attributes. * ad7816 - Constify the event attribute_group structure. * adt7316 - Constify the event attribute group structures. * ak8974 - Cleanup some sparse warnings about endian types. * ak8975 - Cleanup some sparse warnings about endian types. * bmi160 - Spare endian warning cleanups. * isl29018 (towards staging graduation) - Remove unusedvariables and defines. - Improve consistency of error handling. - Signed / unsigned comparison fixes. - Use the IIO_DEVICE_ATTR_{RO, RW} macros - Fix a race in in_illuminance_scale_available_show. - Cleanup exit points of _read_raw - Sanity check if in suspended state during a write_raw call as was already done for read_raw. - Document device tree bidnings. - Document infrared supression controls. - Add some newlines to improve readability and drop one that shouldn't be there. - Fix a poorly named functions name. - Fix multiline coment syntax. - Tidy up a pair or return statements by unifying them. - Rename description in Kconfig for consistency with similar drivers. * lidar - cleanup power management by dropping unnecessary call. * ltr501 - Use the claim_direct_mode helpers. Fix a race condition along the way. * max1027 - Fix a dubious x | !y sparse warning. - Use the new iio_trigger_validate_own_device helper. * max440000 - Clean up some sparse warnings about endian types. * mcp4725 - Use the regulator framework to establish the reference voltage rather than getting it from platform data. - Tidy up a comment typo. - Fix a wrong PTR_ERR query (wrong regulator). * mma7660 - Take a mma7660_nscale static. * mma8452 - Use the new iio_trigger_validate_own_device helper. - Use claim_direct_mode helpers - fix a race condition along the way. * mpl3115 - Use claim_direct_mode helpers - fix a race condition along the way. * ms65611 - Tidy up regulator error handling and clean out a static warning in the mix. * sca3000 - Avoid a potential unitialized variable if a hardware read returns a value that isn't actually supported (mostly warning supression). - Fix a use before setting of the indio_dev->buffer pointer. Broken for a very long time so not going to rush this into stable. - Merge buffer file with core file. We used to always split these. Sometimes it's just not worth the hassle. In this case the device's main feature is it's hardware fifos so unlikely anyone would want to run it without. - Drop the sca3000_register_ring_funcs function as it's a pointless wrapper once we have only one file. - Fix cleaning of flag + setting of size of scan. Without this you can't start the buffer twice and expect sensible (or any) results. Again, broken for a long time so not heading for stable. - Drop the custom watershed setting ABI - for now we'll just support one value. - Move to a hybrid hard / soft buffer design (how we've been doing it for similar devices for a while now!) - Cleanup some unusued variables. - Use a fake channel to support core handling of freefall event registration. - Cleanup the register defines. - Fix an off by one error in axis due to IIO_NO_MOD taking up the 0 value. Been broken since first admission of IIO to the staging tree. - Add readback of the 3db low pass filter frequency and later writing allowing droppign of custom measurement mode attributes as they can be represented by the filter choices that is their main characteristic. - Drop non standard revision attr and replace with dev_info on probe. - Avoid a race in probe. - Various formatting fixes. - Kernel-docify docs that were very nearly in the write format. * tsl2583 - Constify attribute_group structure. * zpa2326 - Drop a redundant DEBUG ifdef. Cleanups (Tools) * iio_generic_buffer - Fix the ? arguement. Previously it sort of worked as you got the help message as a result of it not recognising the arguement.
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/Kconfig2
-rw-r--r--drivers/iio/Makefile2
-rw-r--r--drivers/iio/accel/Kconfig42
-rw-r--r--drivers/iio/accel/Makefile5
-rw-r--r--drivers/iio/accel/da280.c183
-rw-r--r--drivers/iio/accel/da311.c305
-rw-r--r--drivers/iio/accel/dmard10.c266
-rw-r--r--drivers/iio/accel/mma7660.c2
-rw-r--r--drivers/iio/accel/mma8452.c79
-rw-r--r--drivers/iio/accel/sca3000.c1576
-rw-r--r--drivers/iio/adc/Kconfig14
-rw-r--r--drivers/iio/adc/Makefile1
-rw-r--r--drivers/iio/adc/ad7766.c330
-rw-r--r--drivers/iio/adc/max1027.c17
-rw-r--r--drivers/iio/adc/ti-adc0832.c106
-rw-r--r--drivers/iio/adc/ti-adc161s626.c55
-rw-r--r--drivers/iio/counter/104-quad-8.c593
-rw-r--r--drivers/iio/counter/Kconfig24
-rw-r--r--drivers/iio/counter/Makefile7
-rw-r--r--drivers/iio/dac/ad5592r.c2
-rw-r--r--drivers/iio/dac/mcp4725.c176
-rw-r--r--drivers/iio/humidity/Kconfig24
-rw-r--r--drivers/iio/humidity/Makefile7
-rw-r--r--drivers/iio/humidity/hdc100x.c130
-rw-r--r--drivers/iio/humidity/hts221.h73
-rw-r--r--drivers/iio/humidity/hts221_buffer.c169
-rw-r--r--drivers/iio/humidity/hts221_core.c687
-rw-r--r--drivers/iio/humidity/hts221_i2c.c110
-rw-r--r--drivers/iio/humidity/hts221_spi.c125
-rw-r--r--drivers/iio/imu/bmi160/bmi160_core.c3
-rw-r--r--drivers/iio/industrialio-buffer.c7
-rw-r--r--drivers/iio/industrialio-core.c2
-rw-r--r--drivers/iio/industrialio-trigger.c21
-rw-r--r--drivers/iio/inkern.c39
-rw-r--r--drivers/iio/light/Kconfig12
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/isl29018.c847
-rw-r--r--drivers/iio/light/ltr501.c111
-rw-r--r--drivers/iio/light/max44000.c5
-rw-r--r--drivers/iio/magnetometer/ak8974.c8
-rw-r--r--drivers/iio/magnetometer/ak8975.c16
-rw-r--r--drivers/iio/potentiostat/Kconfig22
-rw-r--r--drivers/iio/potentiostat/Makefile6
-rw-r--r--drivers/iio/potentiostat/lmp91000.c446
-rw-r--r--drivers/iio/pressure/Kconfig10
-rw-r--r--drivers/iio/pressure/Makefile1
-rw-r--r--drivers/iio/pressure/abp060mg.c276
-rw-r--r--drivers/iio/pressure/mpl3115.c26
-rw-r--r--drivers/iio/pressure/ms5611_core.c19
-rw-r--r--drivers/iio/pressure/zpa2326.c4
-rw-r--r--drivers/iio/proximity/pulsedlight-lidar-lite-v2.c2
51 files changed, 6800 insertions, 196 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index 6743b18194fb..a918270d6f54 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -73,6 +73,7 @@ source "drivers/iio/adc/Kconfig"
source "drivers/iio/amplifiers/Kconfig"
source "drivers/iio/chemical/Kconfig"
source "drivers/iio/common/Kconfig"
+source "drivers/iio/counter/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/dummy/Kconfig"
source "drivers/iio/frequency/Kconfig"
@@ -87,6 +88,7 @@ if IIO_TRIGGER
source "drivers/iio/trigger/Kconfig"
endif #IIO_TRIGGER
source "drivers/iio/potentiometer/Kconfig"
+source "drivers/iio/potentiostat/Kconfig"
source "drivers/iio/pressure/Kconfig"
source "drivers/iio/proximity/Kconfig"
source "drivers/iio/temperature/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index 87e4c4369e2f..33fa4026f92c 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -18,6 +18,7 @@ obj-y += amplifiers/
obj-y += buffer/
obj-y += chemical/
obj-y += common/
+obj-y += counter/
obj-y += dac/
obj-y += dummy/
obj-y += gyro/
@@ -29,6 +30,7 @@ obj-y += light/
obj-y += magnetometer/
obj-y += orientation/
obj-y += potentiometer/
+obj-y += potentiostat/
obj-y += pressure/
obj-y += proximity/
obj-y += temperature/
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 2b791fe1e2bc..c6cc2c0909a1 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -52,6 +52,26 @@ config BMC150_ACCEL_SPI
tristate
select REGMAP_SPI
+config DA280
+ tristate "MiraMEMS DA280 3-axis 14-bit digital accelerometer driver"
+ depends on I2C
+ help
+ Say yes here to build support for the MiraMEMS DA280 3-axis 14-bit
+ digital accelerometer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called da280.
+
+config DA311
+ tristate "MiraMEMS DA311 3-axis 12-bit digital accelerometer driver"
+ depends on I2C
+ help
+ Say yes here to build support for the MiraMEMS DA311 3-axis 12-bit
+ digital accelerometer.
+
+ To compile this driver as a module, choose M here: the
+ module will be called da311.
+
config DMARD06
tristate "Domintech DMARD06 Digital Accelerometer Driver"
depends on OF || COMPILE_TEST
@@ -73,6 +93,16 @@ config DMARD09
Choosing M will build the driver as a module. If so, the module
will be called dmard09.
+config DMARD10
+ tristate "Domintech DMARD10 3-axis Accelerometer Driver"
+ depends on I2C
+ help
+ Say yes here to get support for the Domintech DMARD10 3-axis
+ accelerometer.
+
+ Choosing M will build the driver as a module. If so, the module
+ will be called dmard10.
+
config HID_SENSOR_ACCEL_3D
depends on HID_SENSOR_HUB
select IIO_BUFFER
@@ -273,6 +303,18 @@ config MXC6255
To compile this driver as a module, choose M here: the module will be
called mxc6255.
+config SCA3000
+ select IIO_BUFFER
+ select IIO_KFIFO_BUF
+ depends on SPI
+ tristate "VTI SCA3000 series accelerometers"
+ help
+ Say Y here to build support for the VTI SCA3000 series of SPI
+ accelerometers. These devices use a hardware ring buffer.
+
+ To compile this driver as a module, say M here: the module will be
+ called sca3000.
+
config STK8312
tristate "Sensortek STK8312 3-Axis Accelerometer Driver"
depends on I2C
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index f5d3ddee619e..69fe8edc57a2 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -8,8 +8,11 @@ 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_DA280) += da280.o
+obj-$(CONFIG_DA311) += da311.o
obj-$(CONFIG_DMARD06) += dmard06.o
obj-$(CONFIG_DMARD09) += dmard09.o
+obj-$(CONFIG_DMARD10) += dmard10.o
obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o
obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o
obj-$(CONFIG_KXSD9) += kxsd9.o
@@ -32,6 +35,8 @@ obj-$(CONFIG_MMA9553) += mma9553.o
obj-$(CONFIG_MXC4005) += mxc4005.o
obj-$(CONFIG_MXC6255) += mxc6255.o
+obj-$(CONFIG_SCA3000) += sca3000.o
+
obj-$(CONFIG_STK8312) += stk8312.o
obj-$(CONFIG_STK8BA50) += stk8ba50.o
diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c
new file mode 100644
index 000000000000..ed8343aeac9c
--- /dev/null
+++ b/drivers/iio/accel/da280.c
@@ -0,0 +1,183 @@
+/**
+ * IIO driver for the MiraMEMS DA280 3-axis accelerometer and
+ * IIO driver for the MiraMEMS DA226 2-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 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>
+#include <linux/iio/sysfs.h>
+#include <linux/byteorder/generic.h>
+
+#define DA280_REG_CHIP_ID 0x01
+#define DA280_REG_ACC_X_LSB 0x02
+#define DA280_REG_ACC_Y_LSB 0x04
+#define DA280_REG_ACC_Z_LSB 0x06
+#define DA280_REG_MODE_BW 0x11
+
+#define DA280_CHIP_ID 0x13
+#define DA280_MODE_ENABLE 0x1e
+#define DA280_MODE_DISABLE 0x9e
+
+enum { da226, da280 };
+
+/*
+ * a value of + or -4096 corresponds to + or - 1G
+ * scale = 9.81 / 4096 = 0.002395019
+ */
+
+static const int da280_nscale = 2395019;
+
+#define DA280_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 da280_channels[] = {
+ DA280_CHANNEL(DA280_REG_ACC_X_LSB, X),
+ DA280_CHANNEL(DA280_REG_ACC_Y_LSB, Y),
+ DA280_CHANNEL(DA280_REG_ACC_Z_LSB, Z),
+};
+
+struct da280_data {
+ struct i2c_client *client;
+};
+
+static int da280_enable(struct i2c_client *client, bool enable)
+{
+ u8 data = enable ? DA280_MODE_ENABLE : DA280_MODE_DISABLE;
+
+ return i2c_smbus_write_byte_data(client, DA280_REG_MODE_BW, data);
+}
+
+static int da280_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct da280_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_smbus_read_word_data(data->client, chan->address);
+ if (ret < 0)
+ return ret;
+ /*
+ * Values are 14 bits, stored as 16 bits with the 2
+ * least significant bits always 0.
+ */
+ *val = (short)ret >> 2;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = da280_nscale;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info da280_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = da280_read_raw,
+};
+
+static int da280_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct da280_data *data;
+
+ ret = i2c_smbus_read_byte_data(client, DA280_REG_CHIP_ID);
+ if (ret != DA280_CHIP_ID)
+ return (ret < 0) ? ret : -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ 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 = &da280_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = da280_channels;
+ if (id->driver_data == da226) {
+ indio_dev->name = "da226";
+ indio_dev->num_channels = 2;
+ } else {
+ indio_dev->name = "da280";
+ indio_dev->num_channels = 3;
+ }
+
+ ret = da280_enable(client, true);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "device_register failed\n");
+ da280_enable(client, false);
+ }
+
+ return ret;
+}
+
+static int da280_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+
+ return da280_enable(client, false);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int da280_suspend(struct device *dev)
+{
+ return da280_enable(to_i2c_client(dev), false);
+}
+
+static int da280_resume(struct device *dev)
+{
+ return da280_enable(to_i2c_client(dev), true);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(da280_pm_ops, da280_suspend, da280_resume);
+
+static const struct i2c_device_id da280_i2c_id[] = {
+ { "da226", da226 },
+ { "da280", da280 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, da280_i2c_id);
+
+static struct i2c_driver da280_driver = {
+ .driver = {
+ .name = "da280",
+ .pm = &da280_pm_ops,
+ },
+ .probe = da280_probe,
+ .remove = da280_remove,
+ .id_table = da280_i2c_id,
+};
+
+module_i2c_driver(da280_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("MiraMEMS DA280 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c
new file mode 100644
index 000000000000..537cfa8b6edf
--- /dev/null
+++ b/drivers/iio/accel/da311.c
@@ -0,0 +1,305 @@
+/**
+ * IIO driver for the MiraMEMS DA311 3-axis accelerometer
+ *
+ * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (c) 2011-2013 MiraMEMS Sensing Technology Co., Ltd.
+ *
+ * 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>
+#include <linux/iio/sysfs.h>
+#include <linux/byteorder/generic.h>
+
+#define DA311_CHIP_ID 0x13
+
+/*
+ * Note register addressed go from 0 - 0x3f and then wrap.
+ * For some reason there are 2 banks with 0 - 0x3f addresses,
+ * rather then a single 0-0x7f bank.
+ */
+
+/* Bank 0 regs */
+#define DA311_REG_BANK 0x0000
+#define DA311_REG_LDO_REG 0x0006
+#define DA311_REG_CHIP_ID 0x000f
+#define DA311_REG_TEMP_CFG_REG 0x001f
+#define DA311_REG_CTRL_REG1 0x0020
+#define DA311_REG_CTRL_REG3 0x0022
+#define DA311_REG_CTRL_REG4 0x0023
+#define DA311_REG_CTRL_REG5 0x0024
+#define DA311_REG_CTRL_REG6 0x0025
+#define DA311_REG_STATUS_REG 0x0027
+#define DA311_REG_OUT_X_L 0x0028
+#define DA311_REG_OUT_X_H 0x0029
+#define DA311_REG_OUT_Y_L 0x002a
+#define DA311_REG_OUT_Y_H 0x002b
+#define DA311_REG_OUT_Z_L 0x002c
+#define DA311_REG_OUT_Z_H 0x002d
+#define DA311_REG_INT1_CFG 0x0030
+#define DA311_REG_INT1_SRC 0x0031
+#define DA311_REG_INT1_THS 0x0032
+#define DA311_REG_INT1_DURATION 0x0033
+#define DA311_REG_INT2_CFG 0x0034
+#define DA311_REG_INT2_SRC 0x0035
+#define DA311_REG_INT2_THS 0x0036
+#define DA311_REG_INT2_DURATION 0x0037
+#define DA311_REG_CLICK_CFG 0x0038
+#define DA311_REG_CLICK_SRC 0x0039
+#define DA311_REG_CLICK_THS 0x003a
+#define DA311_REG_TIME_LIMIT 0x003b
+#define DA311_REG_TIME_LATENCY 0x003c
+#define DA311_REG_TIME_WINDOW 0x003d
+
+/* Bank 1 regs */
+#define DA311_REG_SOFT_RESET 0x0105
+#define DA311_REG_OTP_XOFF_L 0x0110
+#define DA311_REG_OTP_XOFF_H 0x0111
+#define DA311_REG_OTP_YOFF_L 0x0112
+#define DA311_REG_OTP_YOFF_H 0x0113
+#define DA311_REG_OTP_ZOFF_L 0x0114
+#define DA311_REG_OTP_ZOFF_H 0x0115
+#define DA311_REG_OTP_XSO 0x0116
+#define DA311_REG_OTP_YSO 0x0117
+#define DA311_REG_OTP_ZSO 0x0118
+#define DA311_REG_OTP_TRIM_OSC 0x011b
+#define DA311_REG_LPF_ABSOLUTE 0x011c
+#define DA311_REG_TEMP_OFF1 0x0127
+#define DA311_REG_TEMP_OFF2 0x0128
+#define DA311_REG_TEMP_OFF3 0x0129
+#define DA311_REG_OTP_TRIM_THERM_H 0x011a
+
+/*
+ * a value of + or -1024 corresponds to + or - 1G
+ * scale = 9.81 / 1024 = 0.009580078
+ */
+
+static const int da311_nscale = 9580078;
+
+#define DA311_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 da311_channels[] = {
+ /* | 0x80 comes from the android driver */
+ DA311_CHANNEL(DA311_REG_OUT_X_L | 0x80, X),
+ DA311_CHANNEL(DA311_REG_OUT_Y_L | 0x80, Y),
+ DA311_CHANNEL(DA311_REG_OUT_Z_L | 0x80, Z),
+};
+
+struct da311_data {
+ struct i2c_client *client;
+};
+
+static int da311_register_mask_write(struct i2c_client *client, u16 addr,
+ u8 mask, u8 data)
+{
+ int ret;
+ u8 tmp_data = 0;
+
+ if (addr & 0xff00) {
+ /* Select bank 1 */
+ ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x01);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (mask != 0xff) {
+ ret = i2c_smbus_read_byte_data(client, addr);
+ if (ret < 0)
+ return ret;
+ tmp_data = ret;
+ }
+
+ tmp_data &= ~mask;
+ tmp_data |= data & mask;
+ ret = i2c_smbus_write_byte_data(client, addr & 0xff, tmp_data);
+ if (ret < 0)
+ return ret;
+
+ if (addr & 0xff00) {
+ /* Back to bank 0 */
+ ret = i2c_smbus_write_byte_data(client, DA311_REG_BANK, 0x00);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+/* Init sequence taken from the android driver */
+static int da311_reset(struct i2c_client *client)
+{
+ const struct {
+ u16 addr;
+ u8 mask;
+ u8 data;
+ } init_data[] = {
+ { DA311_REG_TEMP_CFG_REG, 0xff, 0x08 },
+ { DA311_REG_CTRL_REG5, 0xff, 0x80 },
+ { DA311_REG_CTRL_REG4, 0x30, 0x00 },
+ { DA311_REG_CTRL_REG1, 0xff, 0x6f },
+ { DA311_REG_TEMP_CFG_REG, 0xff, 0x88 },
+ { DA311_REG_LDO_REG, 0xff, 0x02 },
+ { DA311_REG_OTP_TRIM_OSC, 0xff, 0x27 },
+ { DA311_REG_LPF_ABSOLUTE, 0xff, 0x30 },
+ { DA311_REG_TEMP_OFF1, 0xff, 0x3f },
+ { DA311_REG_TEMP_OFF2, 0xff, 0xff },
+ { DA311_REG_TEMP_OFF3, 0xff, 0x0f },
+ };
+ int i, ret;
+
+ /* Reset */
+ ret = da311_register_mask_write(client, DA311_REG_SOFT_RESET,
+ 0xff, 0xaa);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(init_data); i++) {
+ ret = da311_register_mask_write(client,
+ init_data[i].addr,
+ init_data[i].mask,
+ init_data[i].data);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int da311_enable(struct i2c_client *client, bool enable)
+{
+ u8 data = enable ? 0x00 : 0x20;
+
+ return da311_register_mask_write(client, DA311_REG_TEMP_CFG_REG,
+ 0x20, data);
+}
+
+static int da311_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct da311_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_smbus_read_word_data(data->client, chan->address);
+ if (ret < 0)
+ return ret;
+ /*
+ * Values are 12 bits, stored as 16 bits with the 4
+ * least significant bits always 0.
+ */
+ *val = (short)ret >> 4;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = da311_nscale;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info da311_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = da311_read_raw,
+};
+
+static int da311_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct da311_data *data;
+
+ ret = i2c_smbus_read_byte_data(client, DA311_REG_CHIP_ID);
+ if (ret != DA311_CHIP_ID)
+ return (ret < 0) ? ret : -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ 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 = &da311_info;
+ indio_dev->name = "da311";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = da311_channels;
+ indio_dev->num_channels = ARRAY_SIZE(da311_channels);
+
+ ret = da311_reset(client);
+ if (ret < 0)
+ return ret;
+
+ ret = da311_enable(client, true);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "device_register failed\n");
+ da311_enable(client, false);
+ }
+
+ return ret;
+}
+
+static int da311_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+
+ return da311_enable(client, false);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int da311_suspend(struct device *dev)
+{
+ return da311_enable(to_i2c_client(dev), false);
+}
+
+static int da311_resume(struct device *dev)
+{
+ return da311_enable(to_i2c_client(dev), true);
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(da311_pm_ops, da311_suspend, da311_resume);
+
+static const struct i2c_device_id da311_i2c_id[] = {
+ {"da311", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, da311_i2c_id);
+
+static struct i2c_driver da311_driver = {
+ .driver = {
+ .name = "da311",
+ .pm = &da311_pm_ops,
+ },
+ .probe = da311_probe,
+ .remove = da311_remove,
+ .id_table = da311_i2c_id,
+};
+
+module_i2c_driver(da311_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("MiraMEMS DA311 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/dmard10.c b/drivers/iio/accel/dmard10.c
new file mode 100644
index 000000000000..b8736cc75656
--- /dev/null
+++ b/drivers/iio/accel/dmard10.c
@@ -0,0 +1,266 @@
+/**
+ * IIO driver for the 3-axis accelerometer Domintech ARD10.
+ *
+ * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
+ * Copyright (c) 2012 Domintech Technology Co., Ltd
+ *
+ * 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>
+#include <linux/iio/sysfs.h>
+#include <linux/byteorder/generic.h>
+
+#define DMARD10_REG_ACTR 0x00
+#define DMARD10_REG_AFEM 0x0c
+#define DMARD10_REG_STADR 0x12
+#define DMARD10_REG_STAINT 0x1c
+#define DMARD10_REG_MISC2 0x1f
+#define DMARD10_REG_PD 0x21
+
+#define DMARD10_MODE_OFF 0x00
+#define DMARD10_MODE_STANDBY 0x02
+#define DMARD10_MODE_ACTIVE 0x06
+#define DMARD10_MODE_READ_OTP 0x12
+#define DMARD10_MODE_RESET_DATA_PATH 0x82
+
+/* AFEN set 1, ATM[2:0]=b'000 (normal), EN_Z/Y/X/T=1 */
+#define DMARD10_VALUE_AFEM_AFEN_NORMAL 0x8f
+/* ODR[3:0]=b'0111 (100Hz), CCK[3:0]=b'0100 (204.8kHZ) */
+#define DMARD10_VALUE_CKSEL_ODR_100_204 0x74
+/* INTC[6:5]=b'00 */
+#define DMARD10_VALUE_INTC 0x00
+/* TAP1/TAP2 Average 2 */
+#define DMARD10_VALUE_TAPNS_AVE_2 0x11
+
+#define DMARD10_VALUE_STADR 0x55
+#define DMARD10_VALUE_STAINT 0xaa
+#define DMARD10_VALUE_MISC2_OSCA_EN 0x08
+#define DMARD10_VALUE_PD_RST 0x52
+
+/* Offsets into the buffer read in dmard10_read_raw() */
+#define DMARD10_X_OFFSET 1
+#define DMARD10_Y_OFFSET 2
+#define DMARD10_Z_OFFSET 3
+
+/*
+ * a value of + or -128 corresponds to + or - 1G
+ * scale = 9.81 / 128 = 0.076640625
+ */
+
+static const int dmard10_nscale = 76640625;
+
+#define DMARD10_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 dmard10_channels[] = {
+ DMARD10_CHANNEL(DMARD10_X_OFFSET, X),
+ DMARD10_CHANNEL(DMARD10_Y_OFFSET, Y),
+ DMARD10_CHANNEL(DMARD10_Z_OFFSET, Z),
+};
+
+struct dmard10_data {
+ struct i2c_client *client;
+};
+
+/* Init sequence taken from the android driver */
+static int dmard10_reset(struct i2c_client *client)
+{
+ unsigned char buffer[7];
+ int ret;
+
+ /* 1. Powerdown reset */
+ ret = i2c_smbus_write_byte_data(client, DMARD10_REG_PD,
+ DMARD10_VALUE_PD_RST);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * 2. ACTR => Standby mode => Download OTP to parameter reg =>
+ * Standby mode => Reset data path => Standby mode
+ */
+ buffer[0] = DMARD10_REG_ACTR;
+ buffer[1] = DMARD10_MODE_STANDBY;
+ buffer[2] = DMARD10_MODE_READ_OTP;
+ buffer[3] = DMARD10_MODE_STANDBY;
+ buffer[4] = DMARD10_MODE_RESET_DATA_PATH;
+ buffer[5] = DMARD10_MODE_STANDBY;
+ ret = i2c_master_send(client, buffer, 6);
+ if (ret < 0)
+ return ret;
+
+ /* 3. OSCA_EN = 1, TSTO = b'000 (INT1 = normal, TEST0 = normal) */
+ ret = i2c_smbus_write_byte_data(client, DMARD10_REG_MISC2,
+ DMARD10_VALUE_MISC2_OSCA_EN);
+ if (ret < 0)
+ return ret;
+
+ /* 4. AFEN = 1 (AFE will powerdown after ADC) */
+ buffer[0] = DMARD10_REG_AFEM;
+ buffer[1] = DMARD10_VALUE_AFEM_AFEN_NORMAL;
+ buffer[2] = DMARD10_VALUE_CKSEL_ODR_100_204;
+ buffer[3] = DMARD10_VALUE_INTC;
+ buffer[4] = DMARD10_VALUE_TAPNS_AVE_2;
+ buffer[5] = 0x00; /* DLYC, no delay timing */
+ buffer[6] = 0x07; /* INTD=1 push-pull, INTA=1 active high, AUTOT=1 */
+ ret = i2c_master_send(client, buffer, 7);
+ if (ret < 0)
+ return ret;
+
+ /* 5. Activation mode */
+ ret = i2c_smbus_write_byte_data(client, DMARD10_REG_ACTR,
+ DMARD10_MODE_ACTIVE);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+/* Shutdown sequence taken from the android driver */
+static int dmard10_shutdown(struct i2c_client *client)
+{
+ unsigned char buffer[3];
+
+ buffer[0] = DMARD10_REG_ACTR;
+ buffer[1] = DMARD10_MODE_STANDBY;
+ buffer[2] = DMARD10_MODE_OFF;
+
+ return i2c_master_send(client, buffer, 3);
+}
+
+static int dmard10_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct dmard10_data *data = iio_priv(indio_dev);
+ __le16 buf[4];
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ /*
+ * Read 8 bytes starting at the REG_STADR register, trying to
+ * read the individual X, Y, Z registers will always read 0.
+ */
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ DMARD10_REG_STADR,
+ sizeof(buf), (u8 *)buf);
+ if (ret < 0)
+ return ret;
+ ret = le16_to_cpu(buf[chan->address]);
+ *val = sign_extend32(ret, 12);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = dmard10_nscale;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info dmard10_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = dmard10_read_raw,
+};
+
+static int dmard10_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ int ret;
+ struct iio_dev *indio_dev;
+ struct dmard10_data *data;
+
+ /* These 2 registers have special POR reset values used for id */
+ ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STADR);
+ if (ret != DMARD10_VALUE_STADR)
+ return (ret < 0) ? ret : -ENODEV;
+
+ ret = i2c_smbus_read_byte_data(client, DMARD10_REG_STAINT);
+ if (ret != DMARD10_VALUE_STAINT)
+ 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 = &dmard10_info;
+ indio_dev->name = "dmard10";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = dmard10_channels;
+ indio_dev->num_channels = ARRAY_SIZE(dmard10_channels);
+
+ ret = dmard10_reset(client);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "device_register failed\n");
+ dmard10_shutdown(client);
+ }
+
+ return ret;
+}
+
+static int dmard10_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+
+ return dmard10_shutdown(client);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int dmard10_suspend(struct device *dev)
+{
+ return dmard10_shutdown(to_i2c_client(dev));
+}
+
+static int dmard10_resume(struct device *dev)
+{
+ return dmard10_reset(to_i2c_client(dev));
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(dmard10_pm_ops, dmard10_suspend, dmard10_resume);
+
+static const struct i2c_device_id dmard10_i2c_id[] = {
+ {"dmard10", 0},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, dmard10_i2c_id);
+
+static struct i2c_driver dmard10_driver = {
+ .driver = {
+ .name = "dmard10",
+ .pm = &dmard10_pm_ops,
+ },
+ .probe = dmard10_probe,
+ .remove = dmard10_remove,
+ .id_table = dmard10_i2c_id,
+};
+
+module_i2c_driver(dmard10_driver);
+
+MODULE_AUTHOR("Hans de Goede <hdegoede@redhat.com>");
+MODULE_DESCRIPTION("Domintech ARD10 3-Axis Accelerometer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c
index 03beadf14ad3..3a40774cca74 100644
--- a/drivers/iio/accel/mma7660.c
+++ b/drivers/iio/accel/mma7660.c
@@ -39,7 +39,7 @@
#define MMA7660_SCALE_AVAIL "0.467142857"
-const int mma7660_nscale = 467142857;
+static const int mma7660_nscale = 467142857;
#define MMA7660_CHANNEL(reg, axis) { \
.type = IIO_ACCEL, \
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index d41e1b588e68..f418c588af6a 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -459,12 +459,14 @@ static int mma8452_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;
mutex_lock(&data->lock);
ret = mma8452_read(data, buffer);
mutex_unlock(&data->lock);
+ iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
@@ -664,37 +666,46 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
struct mma8452_data *data = iio_priv(indio_dev);
int i, ret;
- if (iio_buffer_enabled(indio_dev))
- return -EBUSY;
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
i = mma8452_get_samp_freq_index(data, val, val2);
- if (i < 0)
- return i;
-
+ if (i < 0) {
+ ret = i;
+ break;
+ }
data->ctrl_reg1 &= ~MMA8452_CTRL_DR_MASK;
data->ctrl_reg1 |= i << MMA8452_CTRL_DR_SHIFT;
- return mma8452_change_config(data, MMA8452_CTRL_REG1,
- data->ctrl_reg1);
+ ret = mma8452_change_config(data, MMA8452_CTRL_REG1,
+ data->ctrl_reg1);
+ break;
case IIO_CHAN_INFO_SCALE:
i = mma8452_get_scale_index(data, val, val2);
- if (i < 0)
- return i;
+ if (i < 0) {
+ ret = i;
+ break;
+ }
data->data_cfg &= ~MMA8452_DATA_CFG_FS_MASK;
data->data_cfg |= i;
- return mma8452_change_config(data, MMA8452_DATA_CFG,
- data->data_cfg);
+ ret = mma8452_change_config(data, MMA8452_DATA_CFG,
+ data->data_cfg);
+ break;
case IIO_CHAN_INFO_CALIBBIAS:
- if (val < -128 || val > 127)
- return -EINVAL;
+ if (val < -128 || val > 127) {
+ ret = -EINVAL;
+ break;
+ }
- return mma8452_change_config(data,
- MMA8452_OFF_X + chan->scan_index,
- val);
+ ret = mma8452_change_config(data,
+ MMA8452_OFF_X + chan->scan_index,
+ val);
+ break;
case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
if (val == 0 && val2 == 0) {
@@ -703,23 +714,30 @@ static int mma8452_write_raw(struct iio_dev *indio_dev,
data->data_cfg |= MMA8452_DATA_CFG_HPF_MASK;
ret = mma8452_set_hp_filter_frequency(data, val, val2);
if (ret < 0)
- return ret;
+ break;
}
- return mma8452_change_config(data, MMA8452_DATA_CFG,
+ ret = mma8452_change_config(data, MMA8452_DATA_CFG,
data->data_cfg);
+ break;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
ret = mma8452_get_odr_index(data);
for (i = 0; i < ARRAY_SIZE(mma8452_os_ratio); i++) {
- if (mma8452_os_ratio[i][ret] == val)
- return mma8452_set_power_mode(data, i);
+ if (mma8452_os_ratio[i][ret] == val) {
+ ret = mma8452_set_power_mode(data, i);
+ break;
+ }
}
-
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
}
static int mma8452_read_thresh(struct iio_dev *indio_dev,
@@ -1347,20 +1365,9 @@ static int mma8452_data_rdy_trigger_set_state(struct iio_trigger *trig,
return mma8452_change_config(data, MMA8452_CTRL_REG4, reg);
}
-static int mma8452_validate_device(struct iio_trigger *trig,
- struct iio_dev *indio_dev)
-{
- struct iio_dev *indio = iio_trigger_get_drvdata(trig);
-
- if (indio != indio_dev)
- return -EINVAL;
-
- return 0;
-}
-
static const struct iio_trigger_ops mma8452_trigger_ops = {
.set_trigger_state = mma8452_data_rdy_trigger_set_state,
- .validate_device = mma8452_validate_device,
+ .validate_device = iio_trigger_validate_own_device,
.owner = THIS_MODULE,
};
diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c
new file mode 100644
index 000000000000..cb1d83fa19a0
--- /dev/null
+++ b/drivers/iio/accel/sca3000.c
@@ -0,0 +1,1576 @@
+/*
+ * sca3000_core.c -- support VTI sca3000 series accelerometers via SPI
+ *
+ * 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.
+ *
+ * Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
+ *
+ * See industrialio/accels/sca3000.h for comments.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/fs.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/spi/spi.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/kfifo_buf.h>
+
+#define SCA3000_WRITE_REG(a) (((a) << 2) | 0x02)
+#define SCA3000_READ_REG(a) ((a) << 2)
+
+#define SCA3000_REG_REVID_ADDR 0x00
+#define SCA3000_REG_REVID_MAJOR_MASK GENMASK(8, 4)
+#define SCA3000_REG_REVID_MINOR_MASK GENMASK(3, 0)
+
+#define SCA3000_REG_STATUS_ADDR 0x02
+#define SCA3000_LOCKED BIT(5)
+#define SCA3000_EEPROM_CS_ERROR BIT(1)
+#define SCA3000_SPI_FRAME_ERROR BIT(0)
+
+/* All reads done using register decrement so no need to directly access LSBs */
+#define SCA3000_REG_X_MSB_ADDR 0x05
+#define SCA3000_REG_Y_MSB_ADDR 0x07
+#define SCA3000_REG_Z_MSB_ADDR 0x09
+
+#define SCA3000_REG_RING_OUT_ADDR 0x0f
+
+/* Temp read untested - the e05 doesn't have the sensor */
+#define SCA3000_REG_TEMP_MSB_ADDR 0x13
+
+#define SCA3000_REG_MODE_ADDR 0x14
+#define SCA3000_MODE_PROT_MASK 0x28
+#define SCA3000_REG_MODE_RING_BUF_ENABLE BIT(7)
+#define SCA3000_REG_MODE_RING_BUF_8BIT BIT(6)
+
+/*
+ * Free fall detection triggers an interrupt if the acceleration
+ * is below a threshold for equivalent of 25cm drop
+ */
+#define SCA3000_REG_MODE_FREE_FALL_DETECT BIT(4)
+#define SCA3000_REG_MODE_MEAS_MODE_NORMAL 0x00
+#define SCA3000_REG_MODE_MEAS_MODE_OP_1 0x01
+#define SCA3000_REG_MODE_MEAS_MODE_OP_2 0x02
+
+/*
+ * In motion detection mode the accelerations are band pass filtered
+ * (approx 1 - 25Hz) and then a programmable threshold used to trigger
+ * and interrupt.
+ */
+#define SCA3000_REG_MODE_MEAS_MODE_MOT_DET 0x03
+#define SCA3000_REG_MODE_MODE_MASK 0x03
+
+#define SCA3000_REG_BUF_COUNT_ADDR 0x15
+
+#define SCA3000_REG_INT_STATUS_ADDR 0x16
+#define SCA3000_REG_INT_STATUS_THREE_QUARTERS BIT(7)
+#define SCA3000_REG_INT_STATUS_HALF BIT(6)
+
+#define SCA3000_INT_STATUS_FREE_FALL BIT(3)
+#define SCA3000_INT_STATUS_Y_TRIGGER BIT(2)
+#define SCA3000_INT_STATUS_X_TRIGGER BIT(1)
+#define SCA3000_INT_STATUS_Z_TRIGGER BIT(0)
+
+/* Used to allow access to multiplexed registers */
+#define SCA3000_REG_CTRL_SEL_ADDR 0x18
+/* Only available for SCA3000-D03 and SCA3000-D01 */
+#define SCA3000_REG_CTRL_SEL_I2C_DISABLE 0x01
+#define SCA3000_REG_CTRL_SEL_MD_CTRL 0x02
+#define SCA3000_REG_CTRL_SEL_MD_Y_TH 0x03
+#define SCA3000_REG_CTRL_SEL_MD_X_TH 0x04
+#define SCA3000_REG_CTRL_SEL_MD_Z_TH 0x05
+/*
+ * BE VERY CAREFUL WITH THIS, IF 3 BITS ARE NOT SET the device
+ * will not function
+ */
+#define SCA3000_REG_CTRL_SEL_OUT_CTRL 0x0B
+
+#define SCA3000_REG_OUT_CTRL_PROT_MASK 0xE0
+#define SCA3000_REG_OUT_CTRL_BUF_X_EN 0x10
+#define SCA3000_REG_OUT_CTRL_BUF_Y_EN 0x08
+#define SCA3000_REG_OUT_CTRL_BUF_Z_EN 0x04
+#define SCA3000_REG_OUT_CTRL_BUF_DIV_MASK 0x03
+#define SCA3000_REG_OUT_CTRL_BUF_DIV_4 0x02
+#define SCA3000_REG_OUT_CTRL_BUF_DIV_2 0x01
+
+
+/*
+ * Control which motion detector interrupts are on.
+ * For now only OR combinations are supported.
+ */
+#define SCA3000_MD_CTRL_PROT_MASK 0xC0
+#define SCA3000_MD_CTRL_OR_Y BIT(0)
+#define SCA3000_MD_CTRL_OR_X BIT(1)
+#define SCA3000_MD_CTRL_OR_Z BIT(2)
+/* Currently unsupported */
+#define SCA3000_MD_CTRL_AND_Y BIT(3)
+#define SCA3000_MD_CTRL_AND_X BIT(4)
+#define SAC3000_MD_CTRL_AND_Z BIT(5)
+
+/*
+ * Some control registers of complex access methods requiring this register to
+ * be used to remove a lock.
+ */
+#define SCA3000_REG_UNLOCK_ADDR 0x1e
+
+#define SCA3000_REG_INT_MASK_ADDR 0x21
+#define SCA3000_REG_INT_MASK_PROT_MASK 0x1C
+
+#define SCA3000_REG_INT_MASK_RING_THREE_QUARTER BIT(7)
+#define SCA3000_REG_INT_MASK_RING_HALF BIT(6)
+
+#define SCA3000_REG_INT_MASK_ALL_INTS 0x02
+#define SCA3000_REG_INT_MASK_ACTIVE_HIGH 0x01
+#define SCA3000_REG_INT_MASK_ACTIVE_LOW 0x00
+/* Values of multiplexed registers (write to ctrl_data after select) */
+#define SCA3000_REG_CTRL_DATA_ADDR 0x22
+
+/*
+ * Measurement modes available on some sca3000 series chips. Code assumes others
+ * may become available in the future.
+ *
+ * Bypass - Bypass the low-pass filter in the signal channel so as to increase
+ * signal bandwidth.
+ *
+ * Narrow - Narrow low-pass filtering of the signal channel and half output
+ * data rate by decimation.
+ *
+ * Wide - Widen low-pass filtering of signal channel to increase bandwidth
+ */
+#define SCA3000_OP_MODE_BYPASS 0x01
+#define SCA3000_OP_MODE_NARROW 0x02
+#define SCA3000_OP_MODE_WIDE 0x04
+#define SCA3000_MAX_TX 6
+#define SCA3000_MAX_RX 2
+
+/**
+ * struct sca3000_state - device instance state information
+ * @us: the associated spi device
+ * @info: chip variant information
+ * @last_timestamp: the timestamp of the last event
+ * @mo_det_use_count: reference counter for the motion detection unit
+ * @lock: lock used to protect elements of sca3000_state
+ * and the underlying device state.
+ * @tx: dma-able transmit buffer
+ * @rx: dma-able receive buffer
+ **/
+struct sca3000_state {
+ struct spi_device *us;
+ const struct sca3000_chip_info *info;
+ s64 last_timestamp;
+ int mo_det_use_count;
+ struct mutex lock;
+ /* Can these share a cacheline ? */
+ u8 rx[384] ____cacheline_aligned;
+ u8 tx[6] ____cacheline_aligned;
+};
+
+/**
+ * struct sca3000_chip_info - model dependent parameters
+ * @scale: scale * 10^-6
+ * @temp_output: some devices have temperature sensors.
+ * @measurement_mode_freq: normal mode sampling frequency
+ * @measurement_mode_3db_freq: 3db cutoff frequency of the low pass filter for
+ * the normal measurement mode.
+ * @option_mode_1: first optional mode. Not all models have one
+ * @option_mode_1_freq: option mode 1 sampling frequency
+ * @option_mode_1_3db_freq: 3db cutoff frequency of the low pass filter for
+ * the first option mode.
+ * @option_mode_2: second optional mode. Not all chips have one
+ * @option_mode_2_freq: option mode 2 sampling frequency
+ * @option_mode_2_3db_freq: 3db cutoff frequency of the low pass filter for
+ * the second option mode.
+ * @mod_det_mult_xz: Bit wise multipliers to calculate the threshold
+ * for motion detection in the x and z axis.
+ * @mod_det_mult_y: Bit wise multipliers to calculate the threshold
+ * for motion detection in the y axis.
+ *
+ * This structure is used to hold information about the functionality of a given
+ * sca3000 variant.
+ **/
+struct sca3000_chip_info {
+ unsigned int scale;
+ bool temp_output;
+ int measurement_mode_freq;
+ int measurement_mode_3db_freq;
+ int option_mode_1;
+ int option_mode_1_freq;
+ int option_mode_1_3db_freq;
+ int option_mode_2;
+ int option_mode_2_freq;
+ int option_mode_2_3db_freq;
+ int mot_det_mult_xz[6];
+ int mot_det_mult_y[7];
+};
+
+enum sca3000_variant {
+ d01,
+ e02,
+ e04,
+ e05,
+};
+
+/*
+ * Note where option modes are not defined, the chip simply does not
+ * support any.
+ * Other chips in the sca3000 series use i2c and are not included here.
+ *
+ * Some of these devices are only listed in the family data sheet and
+ * do not actually appear to be available.
+ */
+static const struct sca3000_chip_info sca3000_spi_chip_info_tbl[] = {
+ [d01] = {
+ .scale = 7357,
+ .temp_output = true,
+ .measurement_mode_freq = 250,
+ .measurement_mode_3db_freq = 45,
+ .option_mode_1 = SCA3000_OP_MODE_BYPASS,
+ .option_mode_1_freq = 250,
+ .option_mode_1_3db_freq = 70,
+ .mot_det_mult_xz = {50, 100, 200, 350, 650, 1300},
+ .mot_det_mult_y = {50, 100, 150, 250, 450, 850, 1750},
+ },
+ [e02] = {
+ .scale = 9810,
+ .measurement_mode_freq = 125,
+ .measurement_mode_3db_freq = 40,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 63,
+ .option_mode_1_3db_freq = 11,
+ .mot_det_mult_xz = {100, 150, 300, 550, 1050, 2050},
+ .mot_det_mult_y = {50, 100, 200, 350, 700, 1350, 2700},
+ },
+ [e04] = {
+ .scale = 19620,
+ .measurement_mode_freq = 100,
+ .measurement_mode_3db_freq = 38,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 50,
+ .option_mode_1_3db_freq = 9,
+ .option_mode_2 = SCA3000_OP_MODE_WIDE,
+ .option_mode_2_freq = 400,
+ .option_mode_2_3db_freq = 70,
+ .mot_det_mult_xz = {200, 300, 600, 1100, 2100, 4100},
+ .mot_det_mult_y = {100, 200, 400, 7000, 1400, 2700, 54000},
+ },
+ [e05] = {
+ .scale = 61313,
+ .measurement_mode_freq = 200,
+ .measurement_mode_3db_freq = 60,
+ .option_mode_1 = SCA3000_OP_MODE_NARROW,
+ .option_mode_1_freq = 50,
+ .option_mode_1_3db_freq = 9,
+ .option_mode_2 = SCA3000_OP_MODE_WIDE,
+ .option_mode_2_freq = 400,
+ .option_mode_2_3db_freq = 75,
+ .mot_det_mult_xz = {600, 900, 1700, 3200, 6100, 11900},
+ .mot_det_mult_y = {300, 600, 1200, 2000, 4100, 7800, 15600},
+ },
+};
+
+static int sca3000_write_reg(struct sca3000_state *st, u8 address, u8 val)
+{
+ st->tx[0] = SCA3000_WRITE_REG(address);
+ st->tx[1] = val;
+ return spi_write(st->us, st->tx, 2);
+}
+
+static int sca3000_read_data_short(struct sca3000_state *st,
+ u8 reg_address_high,
+ int len)
+{
+ struct spi_transfer xfer[2] = {
+ {
+ .len = 1,
+ .tx_buf = st->tx,
+ }, {
+ .len = len,
+ .rx_buf = st->rx,
+ }
+ };
+ st->tx[0] = SCA3000_READ_REG(reg_address_high);
+
+ return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+}
+
+/**
+ * sca3000_reg_lock_on() - test if the ctrl register lock is on
+ * @st: Driver specific device instance data.
+ *
+ * Lock must be held.
+ **/
+static int sca3000_reg_lock_on(struct sca3000_state *st)
+{
+ int ret;
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_STATUS_ADDR, 1);
+ if (ret < 0)
+ return ret;
+
+ return !(st->rx[0] & SCA3000_LOCKED);
+}
+
+/**
+ * __sca3000_unlock_reg_lock() - unlock the control registers
+ * @st: Driver specific device instance data.
+ *
+ * Note the device does not appear to support doing this in a single transfer.
+ * This should only ever be used as part of ctrl reg read.
+ * Lock must be held before calling this
+ */
+static int __sca3000_unlock_reg_lock(struct sca3000_state *st)
+{
+ struct spi_transfer xfer[3] = {
+ {
+ .len = 2,
+ .cs_change = 1,
+ .tx_buf = st->tx,
+ }, {
+ .len = 2,
+ .cs_change = 1,
+ .tx_buf = st->tx + 2,
+ }, {
+ .len = 2,
+ .tx_buf = st->tx + 4,
+ },
+ };
+ st->tx[0] = SCA3000_WRITE_REG(SCA3000_REG_UNLOCK_ADDR);
+ st->tx[1] = 0x00;
+ st->tx[2] = SCA3000_WRITE_REG(SCA3000_REG_UNLOCK_ADDR);
+ st->tx[3] = 0x50;
+ st->tx[4] = SCA3000_WRITE_REG(SCA3000_REG_UNLOCK_ADDR);
+ st->tx[5] = 0xA0;
+
+ return spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+}
+
+/**
+ * sca3000_write_ctrl_reg() write to a lock protect ctrl register
+ * @st: Driver specific device instance data.
+ * @sel: selects which registers we wish to write to
+ * @val: the value to be written
+ *
+ * Certain control registers are protected against overwriting by the lock
+ * register and use a shared write address. This function allows writing of
+ * these registers.
+ * Lock must be held.
+ */
+static int sca3000_write_ctrl_reg(struct sca3000_state *st,
+ u8 sel,
+ uint8_t val)
+{
+ int ret;
+
+ ret = sca3000_reg_lock_on(st);
+ if (ret < 0)
+ goto error_ret;
+ if (ret) {
+ ret = __sca3000_unlock_reg_lock(st);
+ if (ret)
+ goto error_ret;
+ }
+
+ /* Set the control select register */
+ ret = sca3000_write_reg(st, SCA3000_REG_CTRL_SEL_ADDR, sel);
+ if (ret)
+ goto error_ret;
+
+ /* Write the actual value into the register */
+ ret = sca3000_write_reg(st, SCA3000_REG_CTRL_DATA_ADDR, val);
+
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_read_ctrl_reg() read from lock protected control register.
+ * @st: Driver specific device instance data.
+ * @ctrl_reg: Which ctrl register do we want to read.
+ *
+ * Lock must be held.
+ */
+static int sca3000_read_ctrl_reg(struct sca3000_state *st,
+ u8 ctrl_reg)
+{
+ int ret;
+
+ ret = sca3000_reg_lock_on(st);
+ if (ret < 0)
+ goto error_ret;
+ if (ret) {
+ ret = __sca3000_unlock_reg_lock(st);
+ if (ret)
+ goto error_ret;
+ }
+ /* Set the control select register */
+ ret = sca3000_write_reg(st, SCA3000_REG_CTRL_SEL_ADDR, ctrl_reg);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_read_data_short(st, SCA3000_REG_CTRL_DATA_ADDR, 1);
+ if (ret)
+ goto error_ret;
+ return st->rx[0];
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_show_rev() - sysfs interface to read the chip revision number
+ * @indio_dev: Device instance specific generic IIO data.
+ * Driver specific device instance data can be obtained via
+ * via iio_priv(indio_dev)
+ */
+static int sca3000_print_rev(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_REVID_ADDR, 1);
+ if (ret < 0)
+ goto error_ret;
+ dev_info(&indio_dev->dev,
+ "sca3000 revision major=%lu, minor=%lu\n",
+ st->rx[0] & SCA3000_REG_REVID_MAJOR_MASK,
+ st->rx[0] & SCA3000_REG_REVID_MINOR_MASK);
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static ssize_t
+sca3000_show_available_3db_freqs(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int len;
+
+ len = sprintf(buf, "%d", st->info->measurement_mode_3db_freq);
+ if (st->info->option_mode_1)
+ len += sprintf(buf + len, " %d",
+ st->info->option_mode_1_3db_freq);
+ if (st->info->option_mode_2)
+ len += sprintf(buf + len, " %d",
+ st->info->option_mode_2_3db_freq);
+ len += sprintf(buf + len, "\n");
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available,
+ S_IRUGO, sca3000_show_available_3db_freqs,
+ NULL, 0);
+
+static const struct iio_event_spec sca3000_event = {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) | BIT(IIO_EV_INFO_ENABLE),
+};
+
+/*
+ * Note the hack in the number of bits to pretend we have 2 more than
+ * we do in the fifo.
+ */
+#define SCA3000_CHAN(index, mod) \
+ { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |\
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),\
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\
+ .address = index, \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 13, \
+ .storagebits = 16, \
+ .shift = 3, \
+ .endianness = IIO_BE, \
+ }, \
+ .event_spec = &sca3000_event, \
+ .num_event_specs = 1, \
+ }
+
+static const struct iio_event_spec sca3000_freefall_event_spec = {
+ .type = IIO_EV_TYPE_MAG,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_PERIOD),
+};
+
+static const struct iio_chan_spec sca3000_channels[] = {
+ SCA3000_CHAN(0, IIO_MOD_X),
+ SCA3000_CHAN(1, IIO_MOD_Y),
+ SCA3000_CHAN(2, IIO_MOD_Z),
+ {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X_AND_Y_AND_Z,
+ .scan_index = -1, /* Fake channel */
+ .event_spec = &sca3000_freefall_event_spec,
+ .num_event_specs = 1,
+ },
+};
+
+static const struct iio_chan_spec sca3000_channels_with_temp[] = {
+ SCA3000_CHAN(0, IIO_MOD_X),
+ SCA3000_CHAN(1, IIO_MOD_Y),
+ SCA3000_CHAN(2, IIO_MOD_Z),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ /* No buffer support */
+ .scan_index = -1,
+ },
+ {
+ .type = IIO_ACCEL,
+ .modified = 1,
+ .channel2 = IIO_MOD_X_AND_Y_AND_Z,
+ .scan_index = -1, /* Fake channel */
+ .event_spec = &sca3000_freefall_event_spec,
+ .num_event_specs = 1,
+ },
+};
+
+static u8 sca3000_addresses[3][3] = {
+ [0] = {SCA3000_REG_X_MSB_ADDR, SCA3000_REG_CTRL_SEL_MD_X_TH,
+ SCA3000_MD_CTRL_OR_X},
+ [1] = {SCA3000_REG_Y_MSB_ADDR, SCA3000_REG_CTRL_SEL_MD_Y_TH,
+ SCA3000_MD_CTRL_OR_Y},
+ [2] = {SCA3000_REG_Z_MSB_ADDR, SCA3000_REG_CTRL_SEL_MD_Z_TH,
+ SCA3000_MD_CTRL_OR_Z},
+};
+
+/**
+ * __sca3000_get_base_freq() - obtain mode specific base frequency
+ * @st: Private driver specific device instance specific state.
+ * @info: chip type specific information.
+ * @base_freq: Base frequency for the current measurement mode.
+ *
+ * lock must be held
+ */
+static inline int __sca3000_get_base_freq(struct sca3000_state *st,
+ const struct sca3000_chip_info *info,
+ int *base_freq)
+{
+ int ret;
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+ if (ret)
+ goto error_ret;
+ switch (SCA3000_REG_MODE_MODE_MASK & st->rx[0]) {
+ case SCA3000_REG_MODE_MEAS_MODE_NORMAL:
+ *base_freq = info->measurement_mode_freq;
+ break;
+ case SCA3000_REG_MODE_MEAS_MODE_OP_1:
+ *base_freq = info->option_mode_1_freq;
+ break;
+ case SCA3000_REG_MODE_MEAS_MODE_OP_2:
+ *base_freq = info->option_mode_2_freq;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+error_ret:
+ return ret;
+}
+
+/**
+ * sca3000_read_raw_samp_freq() - read_raw handler for IIO_CHAN_INFO_SAMP_FREQ
+ * @st: Private driver specific device instance specific state.
+ * @val: The frequency read back.
+ *
+ * lock must be held
+ **/
+static int sca3000_read_raw_samp_freq(struct sca3000_state *st, int *val)
+{
+ int ret;
+
+ ret = __sca3000_get_base_freq(st, st->info, val);
+ if (ret)
+ return ret;
+
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+ if (ret < 0)
+ return ret;
+
+ if (*val > 0) {
+ ret &= SCA3000_REG_OUT_CTRL_BUF_DIV_MASK;
+ switch (ret) {
+ case SCA3000_REG_OUT_CTRL_BUF_DIV_2:
+ *val /= 2;
+ break;
+ case SCA3000_REG_OUT_CTRL_BUF_DIV_4:
+ *val /= 4;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * sca3000_write_raw_samp_freq() - write_raw handler for IIO_CHAN_INFO_SAMP_FREQ
+ * @st: Private driver specific device instance specific state.
+ * @val: The frequency desired.
+ *
+ * lock must be held
+ */
+static int sca3000_write_raw_samp_freq(struct sca3000_state *st, int val)
+{
+ int ret, base_freq, ctrlval;
+
+ ret = __sca3000_get_base_freq(st, st->info, &base_freq);
+ if (ret)
+ return ret;
+
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+ if (ret < 0)
+ return ret;
+
+ ctrlval = ret & ~SCA3000_REG_OUT_CTRL_BUF_DIV_MASK;
+
+ if (val == base_freq / 2)
+ ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_2;
+ if (val == base_freq / 4)
+ ctrlval |= SCA3000_REG_OUT_CTRL_BUF_DIV_4;
+ else if (val != base_freq)
+ return -EINVAL;
+
+ return sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
+ ctrlval);
+}
+
+static int sca3000_read_3db_freq(struct sca3000_state *st, int *val)
+{
+ int ret;
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+ if (ret)
+ return ret;
+
+ /* mask bottom 2 bits - only ones that are relevant */
+ st->rx[0] &= SCA3000_REG_MODE_MODE_MASK;
+ switch (st->rx[0]) {
+ case SCA3000_REG_MODE_MEAS_MODE_NORMAL:
+ *val = st->info->measurement_mode_3db_freq;
+ return IIO_VAL_INT;
+ case SCA3000_REG_MODE_MEAS_MODE_MOT_DET:
+ return -EBUSY;
+ case SCA3000_REG_MODE_MEAS_MODE_OP_1:
+ *val = st->info->option_mode_1_3db_freq;
+ return IIO_VAL_INT;
+ case SCA3000_REG_MODE_MEAS_MODE_OP_2:
+ *val = st->info->option_mode_2_3db_freq;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sca3000_write_3db_freq(struct sca3000_state *st, int val)
+{
+ int ret;
+ int mode;
+
+ if (val == st->info->measurement_mode_3db_freq)
+ mode = SCA3000_REG_MODE_MEAS_MODE_NORMAL;
+ else if (st->info->option_mode_1 &&
+ (val == st->info->option_mode_1_3db_freq))
+ mode = SCA3000_REG_MODE_MEAS_MODE_OP_1;
+ else if (st->info->option_mode_2 &&
+ (val == st->info->option_mode_2_3db_freq))
+ mode = SCA3000_REG_MODE_MEAS_MODE_OP_2;
+ else
+ return -EINVAL;
+ ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+ if (ret)
+ return ret;
+
+ st->rx[0] &= ~SCA3000_REG_MODE_MODE_MASK;
+ st->rx[0] |= (mode & SCA3000_REG_MODE_MODE_MASK);
+
+ return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR, st->rx[0]);
+}
+
+static int sca3000_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+ u8 address;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ if (chan->type == IIO_ACCEL) {
+ if (st->mo_det_use_count) {
+ mutex_unlock(&st->lock);
+ return -EBUSY;
+ }
+ address = sca3000_addresses[chan->address][0];
+ ret = sca3000_read_data_short(st, address, 2);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ *val = (be16_to_cpup((__be16 *)st->rx) >> 3) & 0x1FFF;
+ *val = ((*val) << (sizeof(*val) * 8 - 13)) >>
+ (sizeof(*val) * 8 - 13);
+ } else {
+ /* get the temperature when available */
+ ret = sca3000_read_data_short(st,
+ SCA3000_REG_TEMP_MSB_ADDR,
+ 2);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ *val = ((st->rx[0] & 0x3F) << 3) |
+ ((st->rx[1] & 0xE0) >> 5);
+ }
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ if (chan->type == IIO_ACCEL)
+ *val2 = st->info->scale;
+ else /* temperature */
+ *val2 = 555556;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -214;
+ *val2 = 600000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ mutex_lock(&st->lock);
+ ret = sca3000_read_raw_samp_freq(st, val);
+ mutex_unlock(&st->lock);
+ return ret ? ret : IIO_VAL_INT;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ mutex_lock(&st->lock);
+ ret = sca3000_read_3db_freq(st, val);
+ mutex_unlock(&st->lock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int sca3000_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val2)
+ return -EINVAL;
+ mutex_lock(&st->lock);
+ ret = sca3000_write_raw_samp_freq(st, val);
+ mutex_unlock(&st->lock);
+ return ret;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ if (val2)
+ return -EINVAL;
+ mutex_lock(&st->lock);
+ ret = sca3000_write_3db_freq(st, val);
+ mutex_unlock(&st->lock);
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+/**
+ * sca3000_read_av_freq() - sysfs function to get available frequencies
+ * @dev: Device structure for this device.
+ * @attr: Description of the attribute.
+ * @buf: Incoming string
+ *
+ * The later modes are only relevant to the ring buffer - and depend on current
+ * mode. Note that data sheet gives rather wide tolerances for these so integer
+ * division will give good enough answer and not all chips have them specified
+ * at all.
+ **/
+static ssize_t sca3000_read_av_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int len = 0, ret, val;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto error_ret;
+
+ switch (val & SCA3000_REG_MODE_MODE_MASK) {
+ case SCA3000_REG_MODE_MEAS_MODE_NORMAL:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->measurement_mode_freq,
+ st->info->measurement_mode_freq / 2,
+ st->info->measurement_mode_freq / 4);
+ break;
+ case SCA3000_REG_MODE_MEAS_MODE_OP_1:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->option_mode_1_freq,
+ st->info->option_mode_1_freq / 2,
+ st->info->option_mode_1_freq / 4);
+ break;
+ case SCA3000_REG_MODE_MEAS_MODE_OP_2:
+ len += sprintf(buf + len, "%d %d %d\n",
+ st->info->option_mode_2_freq,
+ st->info->option_mode_2_freq / 2,
+ st->info->option_mode_2_freq / 4);
+ break;
+ }
+ return len;
+error_ret:
+ return ret;
+}
+
+/*
+ * Should only really be registered if ring buffer support is compiled in.
+ * Does no harm however and doing it right would add a fair bit of complexity
+ */
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(sca3000_read_av_freq);
+
+/**
+ * sca3000_read_event_value() - query of a threshold or period
+ **/
+static int sca3000_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ int ret, i;
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ mutex_lock(&st->lock);
+ ret = sca3000_read_ctrl_reg(st,
+ sca3000_addresses[chan->address][1]);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+ *val = 0;
+ if (chan->channel2 == IIO_MOD_Y)
+ for_each_set_bit(i, (unsigned long *)&ret,
+ ARRAY_SIZE(st->info->mot_det_mult_y))
+ *val += st->info->mot_det_mult_y[i];
+ else
+ for_each_set_bit(i, (unsigned long *)&ret,
+ ARRAY_SIZE(st->info->mot_det_mult_xz))
+ *val += st->info->mot_det_mult_xz[i];
+
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_PERIOD:
+ *val = 0;
+ *val2 = 226000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * sca3000_write_value() - control of threshold and period
+ * @indio_dev: Device instance specific IIO information.
+ * @chan: Description of the channel for which the event is being
+ * configured.
+ * @type: The type of event being configured, here magnitude rising
+ * as everything else is read only.
+ * @dir: Direction of the event (here rising)
+ * @info: What information about the event are we configuring.
+ * Here the threshold only.
+ * @val: Integer part of the value being written..
+ * @val2: Non integer part of the value being written. Here always 0.
+ */
+static int sca3000_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+ int i;
+ u8 nonlinear = 0;
+
+ if (chan->channel2 == IIO_MOD_Y) {
+ i = ARRAY_SIZE(st->info->mot_det_mult_y);
+ while (i > 0)
+ if (val >= st->info->mot_det_mult_y[--i]) {
+ nonlinear |= (1 << i);
+ val -= st->info->mot_det_mult_y[i];
+ }
+ } else {
+ i = ARRAY_SIZE(st->info->mot_det_mult_xz);
+ while (i > 0)
+ if (val >= st->info->mot_det_mult_xz[--i]) {
+ nonlinear |= (1 << i);
+ val -= st->info->mot_det_mult_xz[i];
+ }
+ }
+
+ mutex_lock(&st->lock);
+ ret = sca3000_write_ctrl_reg(st,
+ sca3000_addresses[chan->address][1],
+ nonlinear);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static struct attribute *sca3000_attributes[] = {
+ &iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr,
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group sca3000_attribute_group = {
+ .attrs = sca3000_attributes,
+};
+
+static int sca3000_read_data(struct sca3000_state *st,
+ u8 reg_address_high,
+ u8 *rx,
+ int len)
+{
+ int ret;
+ struct spi_transfer xfer[2] = {
+ {
+ .len = 1,
+ .tx_buf = st->tx,
+ }, {
+ .len = len,
+ .rx_buf = rx,
+ }
+ };
+
+ st->tx[0] = SCA3000_READ_REG(reg_address_high);
+ ret = spi_sync_transfer(st->us, xfer, ARRAY_SIZE(xfer));
+ if (ret) {
+ dev_err(get_device(&st->us->dev), "problem reading register");
+ return ret;
+ }
+
+ return 0;
+}
+
+/**
+ * sca3000_ring_int_process() - ring specific interrupt handling.
+ * @val: Value of the interrupt status register.
+ * @indio_dev: Device instance specific IIO device structure.
+ */
+static void sca3000_ring_int_process(u8 val, struct iio_dev *indio_dev)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, i, num_available;
+
+ mutex_lock(&st->lock);
+
+ if (val & SCA3000_REG_INT_STATUS_HALF) {
+ ret = sca3000_read_data_short(st, SCA3000_REG_BUF_COUNT_ADDR,
+ 1);
+ if (ret)
+ goto error_ret;
+ num_available = st->rx[0];
+ /*
+ * num_available is the total number of samples available
+ * i.e. number of time points * number of channels.
+ */
+ ret = sca3000_read_data(st, SCA3000_REG_RING_OUT_ADDR, st->rx,
+ num_available * 2);
+ if (ret)
+ goto error_ret;
+ for (i = 0; i < num_available / 3; i++) {
+ /*
+ * Dirty hack to cover for 11 bit in fifo, 13 bit
+ * direct reading.
+ *
+ * In theory the bottom two bits are undefined.
+ * In reality they appear to always be 0.
+ */
+ iio_push_to_buffers(indio_dev, st->rx + i * 3 * 2);
+ }
+ }
+error_ret:
+ mutex_unlock(&st->lock);
+}
+
+/**
+ * sca3000_event_handler() - handling ring and non ring events
+ * @irq: The irq being handled.
+ * @private: struct iio_device pointer for the device.
+ *
+ * Ring related interrupt handler. Depending on event, push to
+ * the ring buffer event chrdev or the event one.
+ *
+ * This function is complicated by the fact that the devices can signify ring
+ * and non ring events via the same interrupt line and they can only
+ * be distinguished via a read of the relevant status register.
+ */
+static irqreturn_t sca3000_event_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, val;
+ s64 last_timestamp = iio_get_time_ns(indio_dev);
+
+ /*
+ * Could lead if badly timed to an extra read of status reg,
+ * but ensures no interrupt is missed.
+ */
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_INT_STATUS_ADDR, 1);
+ val = st->rx[0];
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto done;
+
+ sca3000_ring_int_process(val, indio_dev);
+
+ if (val & SCA3000_INT_STATUS_FREE_FALL)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X_AND_Y_AND_Z,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_FALLING),
+ last_timestamp);
+
+ if (val & SCA3000_INT_STATUS_Y_TRIGGER)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Y,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ last_timestamp);
+
+ if (val & SCA3000_INT_STATUS_X_TRIGGER)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_X,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ last_timestamp);
+
+ if (val & SCA3000_INT_STATUS_Z_TRIGGER)
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL,
+ 0,
+ IIO_MOD_Z,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_RISING),
+ last_timestamp);
+
+done:
+ return IRQ_HANDLED;
+}
+
+/**
+ * sca3000_read_event_config() what events are enabled
+ **/
+static int sca3000_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+ /* read current value of mode register */
+ mutex_lock(&st->lock);
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+ if (ret)
+ goto error_ret;
+
+ switch (chan->channel2) {
+ case IIO_MOD_X_AND_Y_AND_Z:
+ ret = !!(st->rx[0] & SCA3000_REG_MODE_FREE_FALL_DETECT);
+ break;
+ case IIO_MOD_X:
+ case IIO_MOD_Y:
+ case IIO_MOD_Z:
+ /*
+ * Motion detection mode cannot run at the same time as
+ * acceleration data being read.
+ */
+ if ((st->rx[0] & SCA3000_REG_MODE_MODE_MASK)
+ != SCA3000_REG_MODE_MEAS_MODE_MOT_DET) {
+ ret = 0;
+ } else {
+ ret = sca3000_read_ctrl_reg(st,
+ SCA3000_REG_CTRL_SEL_MD_CTRL);
+ if (ret < 0)
+ goto error_ret;
+ /* only supporting logical or's for now */
+ ret = !!(ret & sca3000_addresses[chan->address][2]);
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int sca3000_freefall_set_state(struct iio_dev *indio_dev, int state)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /* read current value of mode register */
+ ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+ if (ret)
+ return ret;
+
+ /* if off and should be on */
+ if (state && !(st->rx[0] & SCA3000_REG_MODE_FREE_FALL_DETECT))
+ return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+ st->rx[0] | SCA3000_REG_MODE_FREE_FALL_DETECT);
+ /* if on and should be off */
+ else if (!state && (st->rx[0] & SCA3000_REG_MODE_FREE_FALL_DETECT))
+ return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+ st->rx[0] & ~SCA3000_REG_MODE_FREE_FALL_DETECT);
+ else
+ return 0;
+}
+
+static int sca3000_motion_detect_set_state(struct iio_dev *indio_dev, int axis,
+ int state)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret, ctrlval;
+
+ /*
+ * First read the motion detector config to find out if
+ * this axis is on
+ */
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+ if (ret < 0)
+ return ret;
+ ctrlval = ret;
+ /* if off and should be on */
+ if (state && !(ctrlval & sca3000_addresses[axis][2])) {
+ ret = sca3000_write_ctrl_reg(st,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ ctrlval |
+ sca3000_addresses[axis][2]);
+ if (ret)
+ return ret;
+ st->mo_det_use_count++;
+ } else if (!state && (ctrlval & sca3000_addresses[axis][2])) {
+ ret = sca3000_write_ctrl_reg(st,
+ SCA3000_REG_CTRL_SEL_MD_CTRL,
+ ctrlval &
+ ~(sca3000_addresses[axis][2]));
+ if (ret)
+ return ret;
+ st->mo_det_use_count--;
+ }
+
+ /* read current value of mode register */
+ ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+ if (ret)
+ return ret;
+ /* if off and should be on */
+ if ((st->mo_det_use_count) &&
+ ((st->rx[0] & SCA3000_REG_MODE_MODE_MASK)
+ != SCA3000_REG_MODE_MEAS_MODE_MOT_DET))
+ return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+ (st->rx[0] & ~SCA3000_REG_MODE_MODE_MASK)
+ | SCA3000_REG_MODE_MEAS_MODE_MOT_DET);
+ /* if on and should be off */
+ else if (!(st->mo_det_use_count) &&
+ ((st->rx[0] & SCA3000_REG_MODE_MODE_MASK)
+ == SCA3000_REG_MODE_MEAS_MODE_MOT_DET))
+ return sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+ st->rx[0] & SCA3000_REG_MODE_MODE_MASK);
+ else
+ return 0;
+}
+
+/**
+ * sca3000_write_event_config() - simple on off control for motion detector
+ * @indio_dev: IIO device instance specific structure. Data specific to this
+ * particular driver may be accessed via iio_priv(indio_dev).
+ * @chan: Description of the channel whose event we are configuring.
+ * @type: The type of event.
+ * @dir: The direction of the event.
+ * @state: Desired state of event being configured.
+ *
+ * This is a per axis control, but enabling any will result in the
+ * motion detector unit being enabled.
+ * N.B. enabling motion detector stops normal data acquisition.
+ * There is a complexity in knowing which mode to return to when
+ * this mode is disabled. Currently normal mode is assumed.
+ **/
+static int sca3000_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ switch (chan->channel2) {
+ case IIO_MOD_X_AND_Y_AND_Z:
+ ret = sca3000_freefall_set_state(indio_dev, state);
+ break;
+
+ case IIO_MOD_X:
+ case IIO_MOD_Y:
+ case IIO_MOD_Z:
+ ret = sca3000_motion_detect_set_state(indio_dev,
+ chan->address,
+ state);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int sca3000_configure_ring(struct iio_dev *indio_dev)
+{
+ struct iio_buffer *buffer;
+
+ buffer = iio_kfifo_allocate();
+ if (!buffer)
+ return -ENOMEM;
+
+ iio_device_attach_buffer(indio_dev, buffer);
+ indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
+
+ return 0;
+}
+
+static void sca3000_unconfigure_ring(struct iio_dev *indio_dev)
+{
+ iio_kfifo_free(indio_dev->buffer);
+}
+
+static inline
+int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state)
+{
+ struct sca3000_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+ if (ret)
+ goto error_ret;
+ if (state) {
+ dev_info(&indio_dev->dev, "supposedly enabling ring buffer\n");
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_MODE_ADDR,
+ (st->rx[0] | SCA3000_REG_MODE_RING_BUF_ENABLE));
+ } else
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_MODE_ADDR,
+ (st->rx[0] & ~SCA3000_REG_MODE_RING_BUF_ENABLE));
+error_ret:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+/**
+ * sca3000_hw_ring_preenable() - hw ring buffer preenable function
+ * @indio_dev: structure representing the IIO device. Device instance
+ * specific state can be accessed via iio_priv(indio_dev).
+ *
+ * Very simple enable function as the chip will allows normal reads
+ * during ring buffer operation so as long as it is indeed running
+ * before we notify the core, the precise ordering does not matter.
+ */
+static int sca3000_hw_ring_preenable(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ mutex_lock(&st->lock);
+
+ /* Enable the 50% full interrupt */
+ ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1);
+ if (ret)
+ goto error_unlock;
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_INT_MASK_ADDR,
+ st->rx[0] | SCA3000_REG_INT_MASK_RING_HALF);
+ if (ret)
+ goto error_unlock;
+
+ mutex_unlock(&st->lock);
+
+ return __sca3000_hw_ring_state_set(indio_dev, 1);
+
+error_unlock:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int sca3000_hw_ring_postdisable(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ ret = __sca3000_hw_ring_state_set(indio_dev, 0);
+ if (ret)
+ return ret;
+
+ /* Disable the 50% full interrupt */
+ mutex_lock(&st->lock);
+
+ ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1);
+ if (ret)
+ goto unlock;
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_INT_MASK_ADDR,
+ st->rx[0] & ~SCA3000_REG_INT_MASK_RING_HALF);
+unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops sca3000_ring_setup_ops = {
+ .preenable = &sca3000_hw_ring_preenable,
+ .postdisable = &sca3000_hw_ring_postdisable,
+};
+
+/**
+ * sca3000_clean_setup() - get the device into a predictable state
+ * @st: Device instance specific private data structure
+ *
+ * Devices use flash memory to store many of the register values
+ * and hence can come up in somewhat unpredictable states.
+ * Hence reset everything on driver load.
+ */
+static int sca3000_clean_setup(struct sca3000_state *st)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ /* Ensure all interrupts have been acknowledged */
+ ret = sca3000_read_data_short(st, SCA3000_REG_INT_STATUS_ADDR, 1);
+ if (ret)
+ goto error_ret;
+
+ /* Turn off all motion detection channels */
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL);
+ if (ret < 0)
+ goto error_ret;
+ ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_MD_CTRL,
+ ret & SCA3000_MD_CTRL_PROT_MASK);
+ if (ret)
+ goto error_ret;
+
+ /* Disable ring buffer */
+ ret = sca3000_read_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL);
+ if (ret < 0)
+ goto error_ret;
+ ret = sca3000_write_ctrl_reg(st, SCA3000_REG_CTRL_SEL_OUT_CTRL,
+ (ret & SCA3000_REG_OUT_CTRL_PROT_MASK)
+ | SCA3000_REG_OUT_CTRL_BUF_X_EN
+ | SCA3000_REG_OUT_CTRL_BUF_Y_EN
+ | SCA3000_REG_OUT_CTRL_BUF_Z_EN
+ | SCA3000_REG_OUT_CTRL_BUF_DIV_4);
+ if (ret)
+ goto error_ret;
+ /* Enable interrupts, relevant to mode and set up as active low */
+ ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(st,
+ SCA3000_REG_INT_MASK_ADDR,
+ (ret & SCA3000_REG_INT_MASK_PROT_MASK)
+ | SCA3000_REG_INT_MASK_ACTIVE_LOW);
+ if (ret)
+ goto error_ret;
+ /*
+ * Select normal measurement mode, free fall off, ring off
+ * Ring in 12 bit mode - it is fine to overwrite reserved bits 3,5
+ * as that occurs in one of the example on the datasheet
+ */
+ ret = sca3000_read_data_short(st, SCA3000_REG_MODE_ADDR, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(st, SCA3000_REG_MODE_ADDR,
+ (st->rx[0] & SCA3000_MODE_PROT_MASK));
+
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static const struct iio_info sca3000_info = {
+ .attrs = &sca3000_attribute_group,
+ .read_raw = &sca3000_read_raw,
+ .write_raw = &sca3000_write_raw,
+ .read_event_value = &sca3000_read_event_value,
+ .write_event_value = &sca3000_write_event_value,
+ .read_event_config = &sca3000_read_event_config,
+ .write_event_config = &sca3000_write_event_config,
+ .driver_module = THIS_MODULE,
+};
+
+static int sca3000_probe(struct spi_device *spi)
+{
+ int ret;
+ struct sca3000_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->us = spi;
+ mutex_init(&st->lock);
+ st->info = &sca3000_spi_chip_info_tbl[spi_get_device_id(spi)
+ ->driver_data];
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &sca3000_info;
+ if (st->info->temp_output) {
+ indio_dev->channels = sca3000_channels_with_temp;
+ indio_dev->num_channels =
+ ARRAY_SIZE(sca3000_channels_with_temp);
+ } else {
+ indio_dev->channels = sca3000_channels;
+ indio_dev->num_channels = ARRAY_SIZE(sca3000_channels);
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ sca3000_configure_ring(indio_dev);
+
+ if (spi->irq) {
+ ret = request_threaded_irq(spi->irq,
+ NULL,
+ &sca3000_event_handler,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ "sca3000",
+ indio_dev);
+ if (ret)
+ return ret;
+ }
+ indio_dev->setup_ops = &sca3000_ring_setup_ops;
+ ret = sca3000_clean_setup(st);
+ if (ret)
+ goto error_free_irq;
+
+ ret = sca3000_print_rev(indio_dev);
+ if (ret)
+ goto error_free_irq;
+
+ return iio_device_register(indio_dev);
+
+error_free_irq:
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+
+ return ret;
+}
+
+static int sca3000_stop_all_interrupts(struct sca3000_state *st)
+{
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = sca3000_read_data_short(st, SCA3000_REG_INT_MASK_ADDR, 1);
+ if (ret)
+ goto error_ret;
+ ret = sca3000_write_reg(st, SCA3000_REG_INT_MASK_ADDR,
+ (st->rx[0] &
+ ~(SCA3000_REG_INT_MASK_RING_THREE_QUARTER |
+ SCA3000_REG_INT_MASK_RING_HALF |
+ SCA3000_REG_INT_MASK_ALL_INTS)));
+error_ret:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int sca3000_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct sca3000_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ /* Must ensure no interrupts can be generated after this! */
+ sca3000_stop_all_interrupts(st);
+ if (spi->irq)
+ free_irq(spi->irq, indio_dev);
+
+ sca3000_unconfigure_ring(indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id sca3000_id[] = {
+ {"sca3000_d01", d01},
+ {"sca3000_e02", e02},
+ {"sca3000_e04", e04},
+ {"sca3000_e05", e05},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, sca3000_id);
+
+static struct spi_driver sca3000_driver = {
+ .driver = {
+ .name = "sca3000",
+ },
+ .probe = sca3000_probe,
+ .remove = sca3000_remove,
+ .id_table = sca3000_id,
+};
+module_spi_driver(sca3000_driver);
+
+MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
+MODULE_DESCRIPTION("VTI SCA3000 Series Accelerometers SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7edcf3238620..c3fe98d2dc3b 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -58,6 +58,18 @@ config AD7476
To compile this driver as a module, choose M here: the
module will be called ad7476.
+config AD7766
+ tristate "Analog Devices AD7766/AD7767 ADC driver"
+ depends on SPI_MASTER
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7766, AD7766-1,
+ AD7766-2, AD7767, AD7767-1, AD7767-2 SPI analog to digital converters.
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7766.
+
config AD7791
tristate "Analog Devices AD7791 ADC driver"
depends on SPI
@@ -447,6 +459,8 @@ config TI_ADC081C
config TI_ADC0832
tristate "Texas Instruments ADC0831/ADC0832/ADC0834/ADC0838"
depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
help
If you say yes here you get support for Texas Instruments ADC0831,
ADC0832, ADC0834, ADC0838 ADC chips.
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 7a40c04c311f..96894b32300d 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_AD7291) += ad7291.o
obj-$(CONFIG_AD7298) += ad7298.o
obj-$(CONFIG_AD7923) += ad7923.o
obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD7766) += ad7766.o
obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
new file mode 100644
index 000000000000..d906686c48eb
--- /dev/null
+++ b/drivers/iio/adc/ad7766.c
@@ -0,0 +1,330 @@
+/*
+ * AD7766/AD7767 SPI ADC driver
+ *
+ * Copyright 2016 Analog Devices Inc.
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/slab.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+struct ad7766_chip_info {
+ unsigned int decimation_factor;
+};
+
+enum {
+ AD7766_SUPPLY_AVDD = 0,
+ AD7766_SUPPLY_DVDD = 1,
+ AD7766_SUPPLY_VREF = 2,
+ AD7766_NUM_SUPPLIES = 3
+};
+
+struct ad7766 {
+ const struct ad7766_chip_info *chip_info;
+ struct spi_device *spi;
+ struct clk *mclk;
+ struct gpio_desc *pd_gpio;
+ struct regulator_bulk_data reg[AD7766_NUM_SUPPLIES];
+
+ struct iio_trigger *trig;
+
+ struct spi_transfer xfer;
+ struct spi_message msg;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ * Make the buffer large enough for one 24 bit sample and one 64 bit
+ * aligned 64 bit timestamp.
+ */
+ unsigned char data[ALIGN(3, sizeof(s64)) + sizeof(s64)]
+ ____cacheline_aligned;
+};
+
+/*
+ * AD7766 and AD7767 variations are interface compatible, the main difference is
+ * analog performance. Both parts will use the same ID.
+ */
+enum ad7766_device_ids {
+ ID_AD7766,
+ ID_AD7766_1,
+ ID_AD7766_2,
+};
+
+static irqreturn_t ad7766_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7766 *ad7766 = iio_priv(indio_dev);
+ int ret;
+
+ ret = spi_sync(ad7766->spi, &ad7766->msg);
+ if (ret < 0)
+ goto done;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, ad7766->data,
+ pf->timestamp);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ad7766_preenable(struct iio_dev *indio_dev)
+{
+ struct ad7766 *ad7766 = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
+ if (ret < 0) {
+ dev_err(&ad7766->spi->dev, "Failed to enable supplies: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = clk_prepare_enable(ad7766->mclk);
+ if (ret < 0) {
+ dev_err(&ad7766->spi->dev, "Failed to enable MCLK: %d\n", ret);
+ regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
+ return ret;
+ }
+
+ if (ad7766->pd_gpio)
+ gpiod_set_value(ad7766->pd_gpio, 0);
+
+ return 0;
+}
+
+static int ad7766_postdisable(struct iio_dev *indio_dev)
+{
+ struct ad7766 *ad7766 = iio_priv(indio_dev);
+
+ if (ad7766->pd_gpio)
+ gpiod_set_value(ad7766->pd_gpio, 1);
+
+ /*
+ * The PD pin is synchronous to the clock, so give it some time to
+ * notice the change before we disable the clock.
+ */
+ msleep(20);
+
+ clk_disable_unprepare(ad7766->mclk);
+ regulator_bulk_disable(ARRAY_SIZE(ad7766->reg), ad7766->reg);
+
+ return 0;
+}
+
+static int ad7766_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct ad7766 *ad7766 = iio_priv(indio_dev);
+ struct regulator *vref = ad7766->reg[AD7766_SUPPLY_VREF].consumer;
+ int scale_uv;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = regulator_get_voltage(vref);
+ if (scale_uv < 0)
+ return scale_uv;
+ *val = scale_uv / 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = clk_get_rate(ad7766->mclk) /
+ ad7766->chip_info->decimation_factor;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec ad7766_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct ad7766_chip_info ad7766_chip_info[] = {
+ [ID_AD7766] = {
+ .decimation_factor = 8,
+ },
+ [ID_AD7766_1] = {
+ .decimation_factor = 16,
+ },
+ [ID_AD7766_2] = {
+ .decimation_factor = 32,
+ },
+};
+
+static const struct iio_buffer_setup_ops ad7766_buffer_setup_ops = {
+ .preenable = &ad7766_preenable,
+ .postenable = &iio_triggered_buffer_postenable,
+ .predisable = &iio_triggered_buffer_predisable,
+ .postdisable = &ad7766_postdisable,
+};
+
+static const struct iio_info ad7766_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = &ad7766_read_raw,
+};
+
+static irqreturn_t ad7766_irq(int irq, void *private)
+{
+ iio_trigger_poll(private);
+ return IRQ_HANDLED;
+}
+
+static int ad7766_set_trigger_state(struct iio_trigger *trig, bool enable)
+{
+ struct ad7766 *ad7766 = iio_trigger_get_drvdata(trig);
+
+ if (enable)
+ enable_irq(ad7766->spi->irq);
+ else
+ disable_irq(ad7766->spi->irq);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops ad7766_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = ad7766_set_trigger_state,
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static int ad7766_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct ad7766 *ad7766;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ad7766));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ ad7766 = iio_priv(indio_dev);
+ ad7766->chip_info = &ad7766_chip_info[id->driver_data];
+
+ ad7766->mclk = devm_clk_get(&spi->dev, "mclk");
+ if (IS_ERR(ad7766->mclk))
+ return PTR_ERR(ad7766->mclk);
+
+ ad7766->reg[AD7766_SUPPLY_AVDD].supply = "avdd";
+ ad7766->reg[AD7766_SUPPLY_DVDD].supply = "dvdd";
+ ad7766->reg[AD7766_SUPPLY_VREF].supply = "vref";
+
+ ret = devm_regulator_bulk_get(&spi->dev, ARRAY_SIZE(ad7766->reg),
+ ad7766->reg);
+ if (IS_ERR(ad7766->reg))
+ return PTR_ERR(ad7766->reg);
+
+ ad7766->pd_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(ad7766->pd_gpio))
+ return PTR_ERR(ad7766->pd_gpio);
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7766_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7766_channels);
+ indio_dev->info = &ad7766_info;
+
+ if (spi->irq > 0) {
+ ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+ if (!ad7766->trig)
+ return -ENOMEM;
+
+ ad7766->trig->ops = &ad7766_trigger_ops;
+ ad7766->trig->dev.parent = &spi->dev;
+ iio_trigger_set_drvdata(ad7766->trig, ad7766);
+
+ ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq,
+ IRQF_TRIGGER_FALLING, dev_name(&spi->dev),
+ ad7766->trig);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The device generates interrupts as long as it is powered up.
+ * Some platforms might not allow the option to power it down so
+ * disable the interrupt to avoid extra load on the system
+ */
+ disable_irq(spi->irq);
+
+ ret = devm_iio_trigger_register(&spi->dev, ad7766->trig);
+ if (ret)
+ return ret;
+ }
+
+ spi_set_drvdata(spi, indio_dev);
+
+ ad7766->spi = spi;
+
+ /* First byte always 0 */
+ ad7766->xfer.rx_buf = &ad7766->data[1];
+ ad7766->xfer.len = 3;
+
+ spi_message_init(&ad7766->msg);
+ spi_message_add_tail(&ad7766->xfer, &ad7766->msg);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time, &ad7766_trigger_handler,
+ &ad7766_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+ return 0;
+}
+
+static const struct spi_device_id ad7766_id[] = {
+ {"ad7766", ID_AD7766},
+ {"ad7766-1", ID_AD7766_1},
+ {"ad7766-2", ID_AD7766_2},
+ {"ad7767", ID_AD7766},
+ {"ad7767-1", ID_AD7766_1},
+ {"ad7767-2", ID_AD7766_2},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7766_id);
+
+static struct spi_driver ad7766_driver = {
+ .driver = {
+ .name = "ad7766",
+ },
+ .probe = ad7766_probe,
+ .id_table = ad7766_id,
+};
+module_spi_driver(ad7766_driver);
+
+MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
+MODULE_DESCRIPTION("Analog Devices AD7766 and AD7767 ADCs driver support");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index 712fbd2b1f16..3b7c4f78f37a 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -238,7 +238,9 @@ static int max1027_read_single_value(struct iio_dev *indio_dev,
/* Configure conversion register with the requested chan */
st->reg = MAX1027_CONV_REG | MAX1027_CHAN(chan->channel) |
- MAX1027_NOSCAN | !!(chan->type == IIO_TEMP);
+ MAX1027_NOSCAN;
+ if (chan->type == IIO_TEMP)
+ st->reg |= MAX1027_TEMP;
ret = spi_write(st->spi, &st->reg, 1);
if (ret < 0) {
dev_err(&indio_dev->dev,
@@ -360,17 +362,6 @@ static int max1027_set_trigger_state(struct iio_trigger *trig, bool state)
return 0;
}
-static int max1027_validate_device(struct iio_trigger *trig,
- struct iio_dev *indio_dev)
-{
- struct iio_dev *indio = iio_trigger_get_drvdata(trig);
-
- if (indio != indio_dev)
- return -EINVAL;
-
- return 0;
-}
-
static irqreturn_t max1027_trigger_handler(int irq, void *private)
{
struct iio_poll_func *pf = (struct iio_poll_func *)private;
@@ -391,7 +382,7 @@ static irqreturn_t max1027_trigger_handler(int irq, void *private)
static const struct iio_trigger_ops max1027_trigger_ops = {
.owner = THIS_MODULE,
- .validate_device = &max1027_validate_device,
+ .validate_device = &iio_trigger_validate_own_device,
.set_trigger_state = &max1027_set_trigger_state,
};
diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c
index f4ba23effe9a..e952e94a14af 100644
--- a/drivers/iio/adc/ti-adc0832.c
+++ b/drivers/iio/adc/ti-adc0832.c
@@ -14,6 +14,10 @@
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/regulator/consumer.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
enum {
adc0831,
@@ -38,10 +42,16 @@ struct adc0832 {
.indexed = 1, \
.channel = chan, \
.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), \
+ .scan_index = chan, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ }, \
}
-#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2) \
+#define ADC0832_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
{ \
.type = IIO_VOLTAGE, \
.indexed = 1, \
@@ -49,18 +59,26 @@ struct adc0832 {
.channel2 = (chan2), \
.differential = 1, \
.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), \
+ .scan_index = si, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 8, \
+ .storagebits = 8, \
+ }, \
}
static const struct iio_chan_spec adc0831_channels[] = {
- ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 0),
+ IIO_CHAN_SOFT_TIMESTAMP(1),
};
static const struct iio_chan_spec adc0832_channels[] = {
ADC0832_VOLTAGE_CHANNEL(0),
ADC0832_VOLTAGE_CHANNEL(1),
- ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
- ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 2),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 3),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
};
static const struct iio_chan_spec adc0834_channels[] = {
@@ -68,10 +86,11 @@ static const struct iio_chan_spec adc0834_channels[] = {
ADC0832_VOLTAGE_CHANNEL(1),
ADC0832_VOLTAGE_CHANNEL(2),
ADC0832_VOLTAGE_CHANNEL(3),
- ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
- ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
- ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
- ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 4),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 5),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 6),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 7),
+ IIO_CHAN_SOFT_TIMESTAMP(8),
};
static const struct iio_chan_spec adc0838_channels[] = {
@@ -83,14 +102,15 @@ static const struct iio_chan_spec adc0838_channels[] = {
ADC0832_VOLTAGE_CHANNEL(5),
ADC0832_VOLTAGE_CHANNEL(6),
ADC0832_VOLTAGE_CHANNEL(7),
- ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1),
- ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0),
- ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3),
- ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2),
- ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5),
- ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4),
- ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7),
- ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(1, 0, 9),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(2, 3, 10),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(3, 2, 11),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(4, 5, 12),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(5, 4, 13),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(6, 7, 14),
+ ADC0832_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
+ IIO_CHAN_SOFT_TIMESTAMP(16),
};
static int adc0831_adc_conversion(struct adc0832 *adc)
@@ -178,6 +198,42 @@ static const struct iio_info adc0832_info = {
.driver_module = THIS_MODULE,
};
+static irqreturn_t adc0832_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adc0832 *adc = iio_priv(indio_dev);
+ u8 data[24] = { }; /* 16x 1 byte ADC data + 8 bytes timestamp */
+ 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];
+ int ret = adc0832_adc_conversion(adc, scan_chan->channel,
+ scan_chan->differential);
+ if (ret < 0) {
+ dev_warn(&adc->spi->dev,
+ "failed to get conversion data\n");
+ goto out;
+ }
+
+ data[i] = ret;
+ i++;
+ }
+ 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 int adc0832_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
@@ -233,9 +289,20 @@ static int adc0832_probe(struct spi_device *spi)
spi_set_drvdata(spi, indio_dev);
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ adc0832_trigger_handler, NULL);
+ if (ret)
+ goto err_reg_disable;
+
ret = iio_device_register(indio_dev);
if (ret)
- regulator_disable(adc->reg);
+ goto err_buffer_cleanup;
+
+ return 0;
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+err_reg_disable:
+ regulator_disable(adc->reg);
return ret;
}
@@ -246,6 +313,7 @@ static int adc0832_remove(struct spi_device *spi)
struct adc0832 *adc = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(adc->reg);
return 0;
diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c
index f94b69f9c288..4836a0d7aef5 100644
--- a/drivers/iio/adc/ti-adc161s626.c
+++ b/drivers/iio/adc/ti-adc161s626.c
@@ -27,6 +27,7 @@
#include <linux/iio/buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/regulator/consumer.h>
#define TI_ADC_DRV_NAME "ti-adc161s626"
@@ -39,7 +40,9 @@ static const struct iio_chan_spec ti_adc141s626_channels[] = {
{
.type = IIO_VOLTAGE,
.channel = 0,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
.sign = 's',
@@ -54,7 +57,9 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
{
.type = IIO_VOLTAGE,
.channel = 0,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
.scan_index = 0,
.scan_type = {
.sign = 's',
@@ -68,6 +73,8 @@ static const struct iio_chan_spec ti_adc161s626_channels[] = {
struct ti_adc_data {
struct iio_dev *indio_dev;
struct spi_device *spi;
+ struct regulator *ref;
+
u8 read_size;
u8 shift;
@@ -135,18 +142,32 @@ static int ti_adc_read_raw(struct iio_dev *indio_dev,
struct ti_adc_data *data = iio_priv(indio_dev);
int ret;
- if (mask != IIO_CHAN_INFO_RAW)
- return -EINVAL;
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
- 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);
- ret = ti_adc_read_measurement(data, chan, val);
- iio_device_release_direct_mode(indio_dev);
+ if (ret)
+ return ret;
- if (!ret)
return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(data->ref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 1 << (chan->scan_type.realbits - 1);
+ return IIO_VAL_INT;
+ }
return 0;
}
@@ -191,10 +212,17 @@ static int ti_adc_probe(struct spi_device *spi)
break;
}
+ data->ref = devm_regulator_get(&spi->dev, "vdda");
+ if (!IS_ERR(data->ref)) {
+ ret = regulator_enable(data->ref);
+ if (ret < 0)
+ return ret;
+ }
+
ret = iio_triggered_buffer_setup(indio_dev, NULL,
ti_adc_trigger_handler, NULL);
if (ret)
- return ret;
+ goto error_regulator_disable;
ret = iio_device_register(indio_dev);
if (ret)
@@ -205,15 +233,20 @@ static int ti_adc_probe(struct spi_device *spi)
error_unreg_buffer:
iio_triggered_buffer_cleanup(indio_dev);
+error_regulator_disable:
+ regulator_disable(data->ref);
+
return ret;
}
static int ti_adc_remove(struct spi_device *spi)
{
struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ti_adc_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
+ regulator_disable(data->ref);
return 0;
}
diff --git a/drivers/iio/counter/104-quad-8.c b/drivers/iio/counter/104-quad-8.c
new file mode 100644
index 000000000000..2d2ee353dde7
--- /dev/null
+++ b/drivers/iio/counter/104-quad-8.c
@@ -0,0 +1,593 @@
+/*
+ * IIO driver for the ACCES 104-QUAD-8
+ * 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 ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
+ */
+#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/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/types.h>
+
+#define QUAD8_EXTENT 32
+
+static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
+static unsigned int num_quad8;
+module_param_array(base, uint, &num_quad8, 0);
+MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
+
+#define QUAD8_NUM_COUNTERS 8
+
+/**
+ * struct quad8_iio - IIO device private data structure
+ * @preset: array of preset values
+ * @count_mode: array of count mode configurations
+ * @quadrature_mode: array of quadrature mode configurations
+ * @quadrature_scale: array of quadrature mode scale configurations
+ * @ab_enable: array of A and B inputs enable configurations
+ * @preset_enable: array of set_to_preset_on_index attribute configurations
+ * @synchronous_mode: array of index function synchronous mode configurations
+ * @index_polarity: array of index function polarity configurations
+ * @base: base port address of the IIO device
+ */
+struct quad8_iio {
+ unsigned int preset[QUAD8_NUM_COUNTERS];
+ unsigned int count_mode[QUAD8_NUM_COUNTERS];
+ unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
+ unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
+ unsigned int ab_enable[QUAD8_NUM_COUNTERS];
+ unsigned int preset_enable[QUAD8_NUM_COUNTERS];
+ unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
+ unsigned int index_polarity[QUAD8_NUM_COUNTERS];
+ unsigned int base;
+};
+
+static int quad8_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ const int base_offset = priv->base + 2 * chan->channel;
+ unsigned int flags;
+ unsigned int borrow;
+ unsigned int carry;
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_INDEX) {
+ *val = !!(inb(priv->base + 0x16) & BIT(chan->channel));
+ return IIO_VAL_INT;
+ }
+
+ flags = inb(base_offset);
+ borrow = flags & BIT(0);
+ carry = !!(flags & BIT(1));
+
+ /* Borrow XOR Carry effectively doubles count range */
+ *val = (borrow ^ carry) << 24;
+
+ /* Reset Byte Pointer; transfer Counter to Output Latch */
+ outb(0x11, base_offset + 1);
+
+ for (i = 0; i < 3; i++)
+ *val |= (unsigned int)inb(base_offset) << (8 * i);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_ENABLE:
+ *val = priv->ab_enable[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1;
+ *val2 = priv->quadrature_scale[chan->channel];
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static int quad8_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val, int val2, long mask)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ const int base_offset = priv->base + 2 * chan->channel;
+ int i;
+ unsigned int ior_cfg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (chan->type == IIO_INDEX)
+ return -EINVAL;
+
+ /* Only 24-bit values are supported */
+ if ((unsigned int)val > 0xFFFFFF)
+ return -EINVAL;
+
+ /* Reset Byte Pointer */
+ outb(0x01, base_offset + 1);
+
+ /* Counter can only be set via Preset Register */
+ for (i = 0; i < 3; i++)
+ outb(val >> (8 * i), base_offset);
+
+ /* Transfer Preset Register to Counter */
+ outb(0x08, base_offset + 1);
+
+ /* Reset Byte Pointer */
+ outb(0x01, base_offset + 1);
+
+ /* Set Preset Register back to original value */
+ val = priv->preset[chan->channel];
+ for (i = 0; i < 3; i++)
+ outb(val >> (8 * i), base_offset);
+
+ /* Reset Borrow, Carry, Compare, and Sign flags */
+ outb(0x02, base_offset + 1);
+ /* Reset Error flag */
+ outb(0x06, base_offset + 1);
+
+ return 0;
+ case IIO_CHAN_INFO_ENABLE:
+ /* only boolean values accepted */
+ if (val < 0 || val > 1)
+ return -EINVAL;
+
+ priv->ab_enable[chan->channel] = val;
+
+ ior_cfg = val | priv->preset_enable[chan->channel] << 1;
+
+ /* Load I/O control configuration */
+ outb(0x40 | ior_cfg, base_offset);
+
+ return 0;
+ case IIO_CHAN_INFO_SCALE:
+ /* Quadrature scaling only available in quadrature mode */
+ if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1))
+ return -EINVAL;
+
+ /* Only three gain states (1, 0.5, 0.25) */
+ if (val == 1 && !val2)
+ priv->quadrature_scale[chan->channel] = 0;
+ else if (!val)
+ switch (val2) {
+ case 500000:
+ priv->quadrature_scale[chan->channel] = 1;
+ break;
+ case 250000:
+ priv->quadrature_scale[chan->channel] = 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+ else
+ return -EINVAL;
+
+ return 0;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info quad8_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = quad8_read_raw,
+ .write_raw = quad8_write_raw
+};
+
+static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]);
+}
+
+static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
+ const struct iio_chan_spec *chan, const char *buf, size_t len)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ const int base_offset = priv->base + 2 * chan->channel;
+ unsigned int preset;
+ int ret;
+ int i;
+
+ ret = kstrtouint(buf, 0, &preset);
+ if (ret)
+ return ret;
+
+ /* Only 24-bit values are supported */
+ if (preset > 0xFFFFFF)
+ return -EINVAL;
+
+ priv->preset[chan->channel] = preset;
+
+ /* Reset Byte Pointer */
+ outb(0x01, base_offset + 1);
+
+ /* Set Preset Register */
+ for (i = 0; i < 3; i++)
+ outb(preset >> (8 * i), base_offset);
+
+ return len;
+}
+
+static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, char *buf)
+{
+ const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ priv->preset_enable[chan->channel]);
+}
+
+static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
+ uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
+ size_t len)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ const int base_offset = priv->base + 2 * chan->channel;
+ bool preset_enable;
+ int ret;
+ unsigned int ior_cfg;
+
+ ret = kstrtobool(buf, &preset_enable);
+ if (ret)
+ return ret;
+
+ priv->preset_enable[chan->channel] = preset_enable;
+
+ ior_cfg = priv->ab_enable[chan->channel] |
+ (unsigned int)preset_enable << 1;
+
+ /* Load I/O control configuration to Input / Output Control Register */
+ outb(0x40 | ior_cfg, base_offset);
+
+ return len;
+}
+
+static const char *const quad8_noise_error_states[] = {
+ "No excessive noise is present at the count inputs",
+ "Excessive noise is present at the count inputs"
+};
+
+static int quad8_get_noise_error(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ const int base_offset = priv->base + 2 * chan->channel + 1;
+
+ return !!(inb(base_offset) & BIT(4));
+}
+
+static const struct iio_enum quad8_noise_error_enum = {
+ .items = quad8_noise_error_states,
+ .num_items = ARRAY_SIZE(quad8_noise_error_states),
+ .get = quad8_get_noise_error
+};
+
+static const char *const quad8_count_direction_states[] = {
+ "down",
+ "up"
+};
+
+static int quad8_get_count_direction(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ const int base_offset = priv->base + 2 * chan->channel + 1;
+
+ return !!(inb(base_offset) & BIT(5));
+}
+
+static const struct iio_enum quad8_count_direction_enum = {
+ .items = quad8_count_direction_states,
+ .num_items = ARRAY_SIZE(quad8_count_direction_states),
+ .get = quad8_get_count_direction
+};
+
+static const char *const quad8_count_modes[] = {
+ "normal",
+ "range limit",
+ "non-recycle",
+ "modulo-n"
+};
+
+static int quad8_set_count_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int count_mode)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ unsigned int mode_cfg = count_mode << 1;
+ const int base_offset = priv->base + 2 * chan->channel + 1;
+
+ priv->count_mode[chan->channel] = count_mode;
+
+ /* Add quadrature mode configuration */
+ if (priv->quadrature_mode[chan->channel])
+ mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
+
+ /* Load mode configuration to Counter Mode Register */
+ outb(0x20 | mode_cfg, base_offset);
+
+ return 0;
+}
+
+static int quad8_get_count_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+ return priv->count_mode[chan->channel];
+}
+
+static const struct iio_enum quad8_count_mode_enum = {
+ .items = quad8_count_modes,
+ .num_items = ARRAY_SIZE(quad8_count_modes),
+ .set = quad8_set_count_mode,
+ .get = quad8_get_count_mode
+};
+
+static const char *const quad8_synchronous_modes[] = {
+ "non-synchronous",
+ "synchronous"
+};
+
+static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int synchronous_mode)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ const unsigned int idr_cfg = synchronous_mode |
+ priv->index_polarity[chan->channel] << 1;
+ const int base_offset = priv->base + 2 * chan->channel + 1;
+
+ /* Index function must be non-synchronous in non-quadrature mode */
+ if (synchronous_mode && !priv->quadrature_mode[chan->channel])
+ return -EINVAL;
+
+ priv->synchronous_mode[chan->channel] = synchronous_mode;
+
+ /* Load Index Control configuration to Index Control Register */
+ outb(0x40 | idr_cfg, base_offset);
+
+ return 0;
+}
+
+static int quad8_get_synchronous_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+ return priv->synchronous_mode[chan->channel];
+}
+
+static const struct iio_enum quad8_synchronous_mode_enum = {
+ .items = quad8_synchronous_modes,
+ .num_items = ARRAY_SIZE(quad8_synchronous_modes),
+ .set = quad8_set_synchronous_mode,
+ .get = quad8_get_synchronous_mode
+};
+
+static const char *const quad8_quadrature_modes[] = {
+ "non-quadrature",
+ "quadrature"
+};
+
+static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int quadrature_mode)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ unsigned int mode_cfg = priv->count_mode[chan->channel] << 1;
+ const int base_offset = priv->base + 2 * chan->channel + 1;
+
+ if (quadrature_mode)
+ mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
+ else {
+ /* Quadrature scaling only available in quadrature mode */
+ priv->quadrature_scale[chan->channel] = 0;
+
+ /* Synchronous function not supported in non-quadrature mode */
+ if (priv->synchronous_mode[chan->channel])
+ quad8_set_synchronous_mode(indio_dev, chan, 0);
+ }
+
+ priv->quadrature_mode[chan->channel] = quadrature_mode;
+
+ /* Load mode configuration to Counter Mode Register */
+ outb(0x20 | mode_cfg, base_offset);
+
+ return 0;
+}
+
+static int quad8_get_quadrature_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+ return priv->quadrature_mode[chan->channel];
+}
+
+static const struct iio_enum quad8_quadrature_mode_enum = {
+ .items = quad8_quadrature_modes,
+ .num_items = ARRAY_SIZE(quad8_quadrature_modes),
+ .set = quad8_set_quadrature_mode,
+ .get = quad8_get_quadrature_mode
+};
+
+static const char *const quad8_index_polarity_modes[] = {
+ "negative",
+ "positive"
+};
+
+static int quad8_set_index_polarity(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, unsigned int index_polarity)
+{
+ struct quad8_iio *const priv = iio_priv(indio_dev);
+ const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] |
+ index_polarity << 1;
+ const int base_offset = priv->base + 2 * chan->channel + 1;
+
+ priv->index_polarity[chan->channel] = index_polarity;
+
+ /* Load Index Control configuration to Index Control Register */
+ outb(0x40 | idr_cfg, base_offset);
+
+ return 0;
+}
+
+static int quad8_get_index_polarity(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ const struct quad8_iio *const priv = iio_priv(indio_dev);
+
+ return priv->index_polarity[chan->channel];
+}
+
+static const struct iio_enum quad8_index_polarity_enum = {
+ .items = quad8_index_polarity_modes,
+ .num_items = ARRAY_SIZE(quad8_index_polarity_modes),
+ .set = quad8_set_index_polarity,
+ .get = quad8_get_index_polarity
+};
+
+static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = {
+ {
+ .name = "preset",
+ .shared = IIO_SEPARATE,
+ .read = quad8_read_preset,
+ .write = quad8_write_preset
+ },
+ {
+ .name = "set_to_preset_on_index",
+ .shared = IIO_SEPARATE,
+ .read = quad8_read_set_to_preset_on_index,
+ .write = quad8_write_set_to_preset_on_index
+ },
+ IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum),
+ IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum),
+ IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum),
+ IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum),
+ IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum),
+ IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum),
+ IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum),
+ IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum),
+ {}
+};
+
+static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = {
+ IIO_ENUM("synchronous_mode", IIO_SEPARATE,
+ &quad8_synchronous_mode_enum),
+ IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum),
+ IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum),
+ IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum),
+ {}
+};
+
+#define QUAD8_COUNT_CHAN(_chan) { \
+ .type = IIO_COUNT, \
+ .channel = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), \
+ .ext_info = quad8_count_ext_info, \
+ .indexed = 1 \
+}
+
+#define QUAD8_INDEX_CHAN(_chan) { \
+ .type = IIO_INDEX, \
+ .channel = (_chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .ext_info = quad8_index_ext_info, \
+ .indexed = 1 \
+}
+
+static const struct iio_chan_spec quad8_channels[] = {
+ QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0),
+ QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1),
+ QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2),
+ QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3),
+ QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4),
+ QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5),
+ QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6),
+ QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
+};
+
+static int quad8_probe(struct device *dev, unsigned int id)
+{
+ struct iio_dev *indio_dev;
+ struct quad8_iio *priv;
+ int i, j;
+ unsigned int base_offset;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
+ dev_name(dev))) {
+ dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
+ base[id], base[id] + QUAD8_EXTENT);
+ return -EBUSY;
+ }
+
+ indio_dev->info = &quad8_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
+ indio_dev->channels = quad8_channels;
+ indio_dev->name = dev_name(dev);
+
+ priv = iio_priv(indio_dev);
+ priv->base = base[id];
+
+ /* Reset all counters and disable interrupt function */
+ outb(0x01, base[id] + 0x11);
+ /* Set initial configuration for all counters */
+ for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
+ base_offset = base[id] + 2 * i;
+ /* Reset Byte Pointer */
+ outb(0x01, base_offset + 1);
+ /* Reset Preset Register */
+ for (j = 0; j < 3; j++)
+ outb(0x00, base_offset);
+ /* Reset Borrow, Carry, Compare, and Sign flags */
+ outb(0x04, base_offset + 1);
+ /* Reset Error flag */
+ outb(0x06, base_offset + 1);
+ /* Binary encoding; Normal count; non-quadrature mode */
+ outb(0x20, base_offset + 1);
+ /* Disable A and B inputs; preset on index; FLG1 as Carry */
+ outb(0x40, base_offset + 1);
+ /* Disable index function; negative index polarity */
+ outb(0x60, base_offset + 1);
+ }
+ /* Enable all counters */
+ outb(0x00, base[id] + 0x11);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct isa_driver quad8_driver = {
+ .probe = quad8_probe,
+ .driver = {
+ .name = "104-quad-8"
+ }
+};
+
+module_isa_driver(quad8_driver, num_quad8);
+
+MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
+MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
new file mode 100644
index 000000000000..44627f6e4861
--- /dev/null
+++ b/drivers/iio/counter/Kconfig
@@ -0,0 +1,24 @@
+#
+# Counter devices
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Counters"
+
+config 104_QUAD_8
+ tristate "ACCES 104-QUAD-8 driver"
+ depends on X86 && ISA_BUS_API
+ help
+ Say yes here to build support for the ACCES 104-QUAD-8 quadrature
+ encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
+
+ Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and
+ also clears the counter's respective error flag. Although the counters
+ have a 25-bit range, only the lower 24 bits may be set, either directly
+ or via a counter's preset attribute. Interrupts are not supported by
+ this driver.
+
+ The base port addresses for the devices may be configured via the base
+ array module parameter.
+
+endmenu
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
new file mode 100644
index 000000000000..007e88411648
--- /dev/null
+++ b/drivers/iio/counter/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for IIO counter devices
+#
+
+# When adding new entries keep the list in alphabetical order
+
+obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c
index 0b235a2c7359..6eed5b7729be 100644
--- a/drivers/iio/dac/ad5592r.c
+++ b/drivers/iio/dac/ad5592r.c
@@ -17,7 +17,7 @@
#define AD5592R_GPIO_READBACK_EN BIT(10)
#define AD5592R_LDAC_READBACK_EN BIT(6)
-static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, u16 *buf)
+static int ad5592r_spi_wnop_r16(struct ad5592r_state *st, __be16 *buf)
{
struct spi_device *spi = container_of(st->dev, struct spi_device, dev);
struct spi_transfer t = {
diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
index cca935c06f2b..db109f0cdd8c 100644
--- a/drivers/iio/dac/mcp4725.c
+++ b/drivers/iio/dac/mcp4725.c
@@ -18,6 +18,8 @@
#include <linux/i2c.h>
#include <linux/err.h>
#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -26,12 +28,20 @@
#define MCP4725_DRV_NAME "mcp4725"
+#define MCP472X_REF_VDD 0x00
+#define MCP472X_REF_VREF_UNBUFFERED 0x02
+#define MCP472X_REF_VREF_BUFFERED 0x03
+
struct mcp4725_data {
struct i2c_client *client;
- u16 vref_mv;
+ int id;
+ unsigned ref_mode;
+ bool vref_buffered;
u16 dac_value;
bool powerdown;
unsigned powerdown_mode;
+ struct regulator *vdd_reg;
+ struct regulator *vref_reg;
};
static int mcp4725_suspend(struct device *dev)
@@ -86,6 +96,7 @@ static ssize_t mcp4725_store_eeprom(struct device *dev,
return 0;
inoutbuf[0] = 0x60; /* write EEPROM */
+ inoutbuf[0] |= data->ref_mode << 3;
inoutbuf[1] = data->dac_value >> 4;
inoutbuf[2] = (data->dac_value & 0xf) << 4;
@@ -278,18 +289,49 @@ static int mcp4725_set_value(struct iio_dev *indio_dev, int val)
return 0;
}
+static int mcp4726_set_cfg(struct iio_dev *indio_dev)
+{
+ struct mcp4725_data *data = iio_priv(indio_dev);
+ u8 outbuf[3];
+ int ret;
+
+ outbuf[0] = 0x40;
+ outbuf[0] |= data->ref_mode << 3;
+ if (data->powerdown)
+ outbuf[0] |= data->powerdown << 1;
+ outbuf[1] = data->dac_value >> 4;
+ outbuf[2] = (data->dac_value & 0xf) << 4;
+
+ ret = i2c_master_send(data->client, outbuf, 3);
+ if (ret < 0)
+ return ret;
+ else if (ret != 3)
+ return -EIO;
+ else
+ return 0;
+}
+
static int mcp4725_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
struct mcp4725_data *data = iio_priv(indio_dev);
+ int ret;
switch (mask) {
case IIO_CHAN_INFO_RAW:
*val = data->dac_value;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- *val = data->vref_mv;
+ if (data->ref_mode == MCP472X_REF_VDD)
+ ret = regulator_get_voltage(data->vdd_reg);
+ else
+ ret = regulator_get_voltage(data->vref_reg);
+
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
*val2 = 12;
return IIO_VAL_FRACTIONAL_LOG2;
}
@@ -323,27 +365,98 @@ static const struct iio_info mcp4725_info = {
.driver_module = THIS_MODULE,
};
+#ifdef CONFIG_OF
+static int mcp4725_probe_dt(struct device *dev,
+ struct mcp4725_platform_data *pdata)
+{
+ struct device_node *np = dev->of_node;
+
+ if (!np)
+ return -ENODEV;
+
+ /* check if is the vref-supply defined */
+ pdata->use_vref = of_property_read_bool(np, "vref-supply");
+ pdata->vref_buffered =
+ of_property_read_bool(np, "microchip,vref-buffered");
+
+ return 0;
+}
+#else
+static int mcp4725_probe_dt(struct device *dev,
+ struct mcp4725_platform_data *platform_data)
+{
+ return -ENODEV;
+}
+#endif
+
static int mcp4725_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct mcp4725_data *data;
struct iio_dev *indio_dev;
- struct mcp4725_platform_data *platform_data = client->dev.platform_data;
- u8 inbuf[3];
+ struct mcp4725_platform_data *pdata, pdata_dt;
+ u8 inbuf[4];
u8 pd;
+ u8 ref;
int err;
- if (!platform_data || !platform_data->vref_mv) {
- dev_err(&client->dev, "invalid platform data");
- return -EINVAL;
- }
-
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (indio_dev == NULL)
return -ENOMEM;
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
data->client = client;
+ data->id = id->driver_data;
+ pdata = dev_get_platdata(&client->dev);
+
+ if (!pdata) {
+ err = mcp4725_probe_dt(&client->dev, &pdata_dt);
+ if (err) {
+ dev_err(&client->dev,
+ "invalid platform or devicetree data");
+ return err;
+ }
+ pdata = &pdata_dt;
+ }
+
+ if (data->id == MCP4725 && pdata->use_vref) {
+ dev_err(&client->dev,
+ "external reference is unavailable on MCP4725");
+ return -EINVAL;
+ }
+
+ if (!pdata->use_vref && pdata->vref_buffered) {
+ dev_err(&client->dev,
+ "buffering is unavailable on the internal reference");
+ return -EINVAL;
+ }
+
+ if (!pdata->use_vref)
+ data->ref_mode = MCP472X_REF_VDD;
+ else
+ data->ref_mode = pdata->vref_buffered ?
+ MCP472X_REF_VREF_BUFFERED :
+ MCP472X_REF_VREF_UNBUFFERED;
+
+ data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(data->vdd_reg))
+ return PTR_ERR(data->vdd_reg);
+
+ err = regulator_enable(data->vdd_reg);
+ if (err)
+ return err;
+
+ if (pdata->use_vref) {
+ data->vref_reg = devm_regulator_get(&client->dev, "vref");
+ if (IS_ERR(data->vref_reg)) {
+ err = PTR_ERR(data->vref_reg);
+ goto err_disable_vdd_reg;
+ }
+
+ err = regulator_enable(data->vref_reg);
+ if (err)
+ goto err_disable_vdd_reg;
+ }
indio_dev->dev.parent = &client->dev;
indio_dev->name = id->name;
@@ -352,25 +465,56 @@ static int mcp4725_probe(struct i2c_client *client,
indio_dev->num_channels = 1;
indio_dev->modes = INDIO_DIRECT_MODE;
- data->vref_mv = platform_data->vref_mv;
+ /* read current DAC value and settings */
+ err = i2c_master_recv(client, inbuf, data->id == MCP4725 ? 3 : 4);
- /* read current DAC value */
- err = i2c_master_recv(client, inbuf, 3);
if (err < 0) {
dev_err(&client->dev, "failed to read DAC value");
- return err;
+ goto err_disable_vref_reg;
}
pd = (inbuf[0] >> 1) & 0x3;
data->powerdown = pd > 0 ? true : false;
- data->powerdown_mode = pd ? pd - 1 : 2; /* largest register to gnd */
+ data->powerdown_mode = pd ? pd - 1 : 2; /* largest resistor to gnd */
data->dac_value = (inbuf[1] << 4) | (inbuf[2] >> 4);
+ if (data->id == MCP4726)
+ ref = (inbuf[3] >> 3) & 0x3;
+
+ if (data->id == MCP4726 && ref != data->ref_mode) {
+ dev_info(&client->dev,
+ "voltage reference mode differs (conf: %u, eeprom: %u), setting %u",
+ data->ref_mode, ref, data->ref_mode);
+ err = mcp4726_set_cfg(indio_dev);
+ if (err < 0)
+ goto err_disable_vref_reg;
+ }
+
+ err = iio_device_register(indio_dev);
+ if (err)
+ goto err_disable_vref_reg;
+
+ return 0;
+
+err_disable_vref_reg:
+ if (data->vref_reg)
+ regulator_disable(data->vref_reg);
- return iio_device_register(indio_dev);
+err_disable_vdd_reg:
+ regulator_disable(data->vdd_reg);
+
+ return err;
}
static int mcp4725_remove(struct i2c_client *client)
{
- iio_device_unregister(i2c_get_clientdata(client));
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mcp4725_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ if (data->vref_reg)
+ regulator_disable(data->vref_reg);
+ regulator_disable(data->vdd_reg);
+
return 0;
}
diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
index b17e2e2bd4f5..912477d54be2 100644
--- a/drivers/iio/humidity/Kconfig
+++ b/drivers/iio/humidity/Kconfig
@@ -27,6 +27,8 @@ config DHT11
config HDC100X
tristate "TI HDC100x relative humidity and temperature sensor"
depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for the Texas Instruments
HDC1000 and HDC1008 relative humidity and temperature sensors.
@@ -34,6 +36,28 @@ config HDC100X
To compile this driver as a module, choose M here: the module
will be called hdc100x.
+config HTS221
+ tristate "STMicroelectronics HTS221 sensor Driver"
+ depends on (I2C || SPI)
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select HTS221_I2C if (I2C)
+ select HTS221_SPI if (SPI_MASTER)
+ help
+ Say yes here to build support for STMicroelectronics HTS221
+ temperature-humidity sensor
+
+ To compile this driver as a module, choose M here: the module
+ will be called hts221.
+
+config HTS221_I2C
+ tristate
+ depends on HTS221
+
+config HTS221_SPI
+ tristate
+ depends on HTS221
+
config HTU21
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
depends on I2C
diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile
index 4a73442fcd9c..a6850e47c100 100644
--- a/drivers/iio/humidity/Makefile
+++ b/drivers/iio/humidity/Makefile
@@ -5,6 +5,13 @@
obj-$(CONFIG_AM2315) += am2315.o
obj-$(CONFIG_DHT11) += dht11.o
obj-$(CONFIG_HDC100X) += hdc100x.o
+
+hts221-y := hts221_core.o \
+ hts221_buffer.o
+obj-$(CONFIG_HTS221) += hts221.o
+obj-$(CONFIG_HTS221_I2C) += hts221_i2c.o
+obj-$(CONFIG_HTS221_SPI) += hts221_spi.o
+
obj-$(CONFIG_HTU21) += htu21.o
obj-$(CONFIG_SI7005) += si7005.o
obj-$(CONFIG_SI7020) += si7020.o
diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c
index e0c9c70c2a4a..265c34da52d1 100644
--- a/drivers/iio/humidity/hdc100x.c
+++ b/drivers/iio/humidity/hdc100x.c
@@ -22,11 +22,15 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#define HDC100X_REG_TEMP 0x00
#define HDC100X_REG_HUMIDITY 0x01
#define HDC100X_REG_CONFIG 0x02
+#define HDC100X_REG_CONFIG_ACQ_MODE BIT(12)
#define HDC100X_REG_CONFIG_HEATER_EN BIT(13)
struct hdc100x_data {
@@ -87,22 +91,40 @@ static const struct iio_chan_spec hdc100x_channels[] = {
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_INT_TIME) |
BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
},
{
.type = IIO_HUMIDITYRELATIVE,
.address = HDC100X_REG_HUMIDITY,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_SCALE) |
- BIT(IIO_CHAN_INFO_INT_TIME)
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
},
{
.type = IIO_CURRENT,
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
.extend_name = "heater",
.output = 1,
+ .scan_index = -1,
},
+ IIO_CHAN_SOFT_TIMESTAMP(2),
};
+static const unsigned long hdc100x_scan_masks[] = {0x3, 0};
+
static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val)
{
int tmp = (~mask & data->config) | val;
@@ -183,7 +205,14 @@ static int hdc100x_read_raw(struct iio_dev *indio_dev,
*val = hdc100x_get_heater_status(data);
ret = IIO_VAL_INT;
} else {
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret) {
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+
ret = hdc100x_get_measurement(data, chan);
+ iio_device_release_direct_mode(indio_dev);
if (ret >= 0) {
*val = ret;
ret = IIO_VAL_INT;
@@ -246,6 +275,78 @@ static int hdc100x_write_raw(struct iio_dev *indio_dev,
}
}
+static int hdc100x_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct hdc100x_data *data = iio_priv(indio_dev);
+ int ret;
+
+ /* Buffer is enabled. First set ACQ Mode, then attach poll func */
+ mutex_lock(&data->lock);
+ ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE,
+ HDC100X_REG_CONFIG_ACQ_MODE);
+ mutex_unlock(&data->lock);
+ if (ret)
+ return ret;
+
+ return iio_triggered_buffer_postenable(indio_dev);
+}
+
+static int hdc100x_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct hdc100x_data *data = iio_priv(indio_dev);
+ int ret;
+
+ /* First detach poll func, then reset ACQ mode. OK to disable buffer */
+ ret = iio_triggered_buffer_predisable(indio_dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&data->lock);
+ ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static const struct iio_buffer_setup_ops hdc_buffer_setup_ops = {
+ .postenable = hdc100x_buffer_postenable,
+ .predisable = hdc100x_buffer_predisable,
+};
+
+static irqreturn_t hdc100x_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct hdc100x_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+ int delay = data->adc_int_us[0] + data->adc_int_us[1];
+ int ret;
+ s16 buf[8]; /* 2x s16 + padding + 8 byte timestamp */
+
+ /* dual read starts at temp register */
+ mutex_lock(&data->lock);
+ ret = i2c_smbus_write_byte(client, HDC100X_REG_TEMP);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot start measurement\n");
+ goto err;
+ }
+ usleep_range(delay, delay + 1000);
+
+ ret = i2c_master_recv(client, (u8 *)buf, 4);
+ if (ret < 0) {
+ dev_err(&client->dev, "cannot read sensor data\n");
+ goto err;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buf,
+ iio_get_time_ns(indio_dev));
+err:
+ mutex_unlock(&data->lock);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
static const struct iio_info hdc100x_info = {
.read_raw = hdc100x_read_raw,
.write_raw = hdc100x_write_raw,
@@ -258,6 +359,7 @@ static int hdc100x_probe(struct i2c_client *client,
{
struct iio_dev *indio_dev;
struct hdc100x_data *data;
+ int ret;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
@@ -279,12 +381,35 @@ static int hdc100x_probe(struct i2c_client *client,
indio_dev->channels = hdc100x_channels;
indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels);
+ indio_dev->available_scan_masks = hdc100x_scan_masks;
/* be sure we are in a known state */
hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]);
hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
+ hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ hdc100x_trigger_handler,
+ &hdc_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&client->dev, "iio triggered buffer setup failed\n");
+ return ret;
+ }
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ iio_triggered_buffer_cleanup(indio_dev);
+
+ return ret;
+}
+
+static int hdc100x_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ iio_device_unregister(indio_dev);
+ iio_triggered_buffer_cleanup(indio_dev);
- return devm_iio_device_register(&client->dev, indio_dev);
+ return 0;
}
static const struct i2c_device_id hdc100x_id[] = {
@@ -298,6 +423,7 @@ static struct i2c_driver hdc100x_driver = {
.name = "hdc100x",
},
.probe = hdc100x_probe,
+ .remove = hdc100x_remove,
.id_table = hdc100x_id,
};
module_i2c_driver(hdc100x_driver);
diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h
new file mode 100644
index 000000000000..c7154665512e
--- /dev/null
+++ b/drivers/iio/humidity/hts221.h
@@ -0,0 +1,73 @@
+/*
+ * STMicroelectronics hts221 sensor driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#ifndef HTS221_H
+#define HTS221_H
+
+#define HTS221_DEV_NAME "hts221"
+
+#include <linux/iio/iio.h>
+
+#define HTS221_RX_MAX_LENGTH 8
+#define HTS221_TX_MAX_LENGTH 8
+
+#define HTS221_DATA_SIZE 2
+
+struct hts221_transfer_buffer {
+ u8 rx_buf[HTS221_RX_MAX_LENGTH];
+ u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
+};
+
+struct hts221_transfer_function {
+ int (*read)(struct device *dev, u8 addr, int len, u8 *data);
+ int (*write)(struct device *dev, u8 addr, int len, u8 *data);
+};
+
+#define HTS221_AVG_DEPTH 8
+struct hts221_avg_avl {
+ u16 avg;
+ u8 val;
+};
+
+enum hts221_sensor_type {
+ HTS221_SENSOR_H,
+ HTS221_SENSOR_T,
+ HTS221_SENSOR_MAX,
+};
+
+struct hts221_sensor {
+ u8 cur_avg_idx;
+ int slope, b_gen;
+};
+
+struct hts221_hw {
+ const char *name;
+ struct device *dev;
+
+ struct mutex lock;
+ struct iio_trigger *trig;
+ int irq;
+
+ struct hts221_sensor sensors[HTS221_SENSOR_MAX];
+
+ u8 odr;
+
+ const struct hts221_transfer_function *tf;
+ struct hts221_transfer_buffer tb;
+};
+
+int hts221_config_drdy(struct hts221_hw *hw, bool enable);
+int hts221_probe(struct iio_dev *iio_dev);
+int hts221_power_on(struct hts221_hw *hw);
+int hts221_power_off(struct hts221_hw *hw);
+int hts221_allocate_buffers(struct hts221_hw *hw);
+int hts221_allocate_trigger(struct hts221_hw *hw);
+
+#endif /* HTS221_H */
diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c
new file mode 100644
index 000000000000..76b2e8130b6f
--- /dev/null
+++ b/drivers/iio/humidity/hts221_buffer.c
@@ -0,0 +1,169 @@
+/*
+ * STMicroelectronics hts221 sensor driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/irqreturn.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/interrupt.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/buffer.h>
+
+#include "hts221.h"
+
+#define HTS221_REG_STATUS_ADDR 0x27
+#define HTS221_RH_DRDY_MASK BIT(1)
+#define HTS221_TEMP_DRDY_MASK BIT(0)
+
+static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
+ struct hts221_hw *hw = iio_priv(iio_dev);
+
+ return hts221_config_drdy(hw, state);
+}
+
+static const struct iio_trigger_ops hts221_trigger_ops = {
+ .owner = THIS_MODULE,
+ .set_trigger_state = hts221_trig_set_state,
+};
+
+static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
+{
+ struct hts221_hw *hw = (struct hts221_hw *)private;
+ u8 status;
+ int err;
+
+ err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
+ &status);
+ if (err < 0)
+ return IRQ_HANDLED;
+
+ /*
+ * H_DA bit (humidity data available) is routed to DRDY line.
+ * Humidity sample is computed after temperature one.
+ * Here we can assume data channels are both available if H_DA bit
+ * is set in status register
+ */
+ if (!(status & HTS221_RH_DRDY_MASK))
+ return IRQ_NONE;
+
+ iio_trigger_poll_chained(hw->trig);
+
+ return IRQ_HANDLED;
+}
+
+int hts221_allocate_trigger(struct hts221_hw *hw)
+{
+ struct iio_dev *iio_dev = iio_priv_to_dev(hw);
+ unsigned long irq_type;
+ int err;
+
+ irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
+
+ switch (irq_type) {
+ case IRQF_TRIGGER_HIGH:
+ case IRQF_TRIGGER_RISING:
+ break;
+ default:
+ dev_info(hw->dev,
+ "mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
+ irq_type);
+ irq_type = IRQF_TRIGGER_RISING;
+ break;
+ }
+
+ err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
+ hts221_trigger_handler_thread,
+ irq_type | IRQF_ONESHOT,
+ hw->name, hw);
+ if (err) {
+ dev_err(hw->dev, "failed to request trigger irq %d\n",
+ hw->irq);
+ return err;
+ }
+
+ hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
+ iio_dev->name);
+ if (!hw->trig)
+ return -ENOMEM;
+
+ iio_trigger_set_drvdata(hw->trig, iio_dev);
+ hw->trig->ops = &hts221_trigger_ops;
+ hw->trig->dev.parent = hw->dev;
+ iio_dev->trig = iio_trigger_get(hw->trig);
+
+ return devm_iio_trigger_register(hw->dev, hw->trig);
+}
+
+static int hts221_buffer_preenable(struct iio_dev *iio_dev)
+{
+ return hts221_power_on(iio_priv(iio_dev));
+}
+
+static int hts221_buffer_postdisable(struct iio_dev *iio_dev)
+{
+ return hts221_power_off(iio_priv(iio_dev));
+}
+
+static const struct iio_buffer_setup_ops hts221_buffer_ops = {
+ .preenable = hts221_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = hts221_buffer_postdisable,
+};
+
+static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
+{
+ u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)];
+ struct iio_poll_func *pf = p;
+ struct iio_dev *iio_dev = pf->indio_dev;
+ struct hts221_hw *hw = iio_priv(iio_dev);
+ struct iio_chan_spec const *ch;
+ int err;
+
+ /* humidity data */
+ ch = &iio_dev->channels[HTS221_SENSOR_H];
+ err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
+ buffer);
+ if (err < 0)
+ goto out;
+
+ /* temperature data */
+ ch = &iio_dev->channels[HTS221_SENSOR_T];
+ err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
+ buffer + HTS221_DATA_SIZE);
+ if (err < 0)
+ goto out;
+
+ iio_push_to_buffers_with_timestamp(iio_dev, buffer,
+ iio_get_time_ns(iio_dev));
+
+out:
+ iio_trigger_notify_done(hw->trig);
+
+ return IRQ_HANDLED;
+}
+
+int hts221_allocate_buffers(struct hts221_hw *hw)
+{
+ return devm_iio_triggered_buffer_setup(hw->dev, iio_priv_to_dev(hw),
+ NULL, hts221_buffer_handler_thread,
+ &hts221_buffer_ops);
+}
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c
new file mode 100644
index 000000000000..3f3ef4a1a474
--- /dev/null
+++ b/drivers/iio/humidity/hts221_core.c
@@ -0,0 +1,687 @@
+/*
+ * STMicroelectronics hts221 sensor driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/iio/sysfs.h>
+#include <linux/delay.h>
+#include <asm/unaligned.h>
+
+#include "hts221.h"
+
+#define HTS221_REG_WHOAMI_ADDR 0x0f
+#define HTS221_REG_WHOAMI_VAL 0xbc
+
+#define HTS221_REG_CNTRL1_ADDR 0x20
+#define HTS221_REG_CNTRL2_ADDR 0x21
+#define HTS221_REG_CNTRL3_ADDR 0x22
+
+#define HTS221_REG_AVG_ADDR 0x10
+#define HTS221_REG_H_OUT_L 0x28
+#define HTS221_REG_T_OUT_L 0x2a
+
+#define HTS221_HUMIDITY_AVG_MASK 0x07
+#define HTS221_TEMP_AVG_MASK 0x38
+
+#define HTS221_ODR_MASK 0x87
+#define HTS221_BDU_MASK BIT(2)
+
+#define HTS221_DRDY_MASK BIT(2)
+
+#define HTS221_ENABLE_SENSOR BIT(7)
+
+#define HTS221_HUMIDITY_AVG_4 0x00 /* 0.4 %RH */
+#define HTS221_HUMIDITY_AVG_8 0x01 /* 0.3 %RH */
+#define HTS221_HUMIDITY_AVG_16 0x02 /* 0.2 %RH */
+#define HTS221_HUMIDITY_AVG_32 0x03 /* 0.15 %RH */
+#define HTS221_HUMIDITY_AVG_64 0x04 /* 0.1 %RH */
+#define HTS221_HUMIDITY_AVG_128 0x05 /* 0.07 %RH */
+#define HTS221_HUMIDITY_AVG_256 0x06 /* 0.05 %RH */
+#define HTS221_HUMIDITY_AVG_512 0x07 /* 0.03 %RH */
+
+#define HTS221_TEMP_AVG_2 0x00 /* 0.08 degC */
+#define HTS221_TEMP_AVG_4 0x08 /* 0.05 degC */
+#define HTS221_TEMP_AVG_8 0x10 /* 0.04 degC */
+#define HTS221_TEMP_AVG_16 0x18 /* 0.03 degC */
+#define HTS221_TEMP_AVG_32 0x20 /* 0.02 degC */
+#define HTS221_TEMP_AVG_64 0x28 /* 0.015 degC */
+#define HTS221_TEMP_AVG_128 0x30 /* 0.01 degC */
+#define HTS221_TEMP_AVG_256 0x38 /* 0.007 degC */
+
+/* calibration registers */
+#define HTS221_REG_0RH_CAL_X_H 0x36
+#define HTS221_REG_1RH_CAL_X_H 0x3a
+#define HTS221_REG_0RH_CAL_Y_H 0x30
+#define HTS221_REG_1RH_CAL_Y_H 0x31
+#define HTS221_REG_0T_CAL_X_L 0x3c
+#define HTS221_REG_1T_CAL_X_L 0x3e
+#define HTS221_REG_0T_CAL_Y_H 0x32
+#define HTS221_REG_1T_CAL_Y_H 0x33
+#define HTS221_REG_T1_T0_CAL_Y_H 0x35
+
+struct hts221_odr {
+ u8 hz;
+ u8 val;
+};
+
+struct hts221_avg {
+ u8 addr;
+ u8 mask;
+ struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH];
+};
+
+static const struct hts221_odr hts221_odr_table[] = {
+ { 1, 0x01 }, /* 1Hz */
+ { 7, 0x02 }, /* 7Hz */
+ { 13, 0x03 }, /* 12.5Hz */
+};
+
+static const struct hts221_avg hts221_avg_list[] = {
+ {
+ .addr = HTS221_REG_AVG_ADDR,
+ .mask = HTS221_HUMIDITY_AVG_MASK,
+ .avg_avl = {
+ { 4, HTS221_HUMIDITY_AVG_4 },
+ { 8, HTS221_HUMIDITY_AVG_8 },
+ { 16, HTS221_HUMIDITY_AVG_16 },
+ { 32, HTS221_HUMIDITY_AVG_32 },
+ { 64, HTS221_HUMIDITY_AVG_64 },
+ { 128, HTS221_HUMIDITY_AVG_128 },
+ { 256, HTS221_HUMIDITY_AVG_256 },
+ { 512, HTS221_HUMIDITY_AVG_512 },
+ },
+ },
+ {
+ .addr = HTS221_REG_AVG_ADDR,
+ .mask = HTS221_TEMP_AVG_MASK,
+ .avg_avl = {
+ { 2, HTS221_TEMP_AVG_2 },
+ { 4, HTS221_TEMP_AVG_4 },
+ { 8, HTS221_TEMP_AVG_8 },
+ { 16, HTS221_TEMP_AVG_16 },
+ { 32, HTS221_TEMP_AVG_32 },
+ { 64, HTS221_TEMP_AVG_64 },
+ { 128, HTS221_TEMP_AVG_128 },
+ { 256, HTS221_TEMP_AVG_256 },
+ },
+ },
+};
+
+static const struct iio_chan_spec hts221_channels[] = {
+ {
+ .type = IIO_HUMIDITYRELATIVE,
+ .address = HTS221_REG_H_OUT_L,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ .address = HTS221_REG_T_OUT_L,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask,
+ u8 val)
+{
+ u8 data;
+ int err;
+
+ mutex_lock(&hw->lock);
+
+ err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
+ if (err < 0) {
+ dev_err(hw->dev, "failed to read %02x register\n", addr);
+ goto unlock;
+ }
+
+ data = (data & ~mask) | (val & mask);
+
+ err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
+ if (err < 0)
+ dev_err(hw->dev, "failed to write %02x register\n", addr);
+
+unlock:
+ mutex_unlock(&hw->lock);
+
+ return err;
+}
+
+static int hts221_check_whoami(struct hts221_hw *hw)
+{
+ u8 data;
+ int err;
+
+ err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
+ &data);
+ if (err < 0) {
+ dev_err(hw->dev, "failed to read whoami register\n");
+ return err;
+ }
+
+ if (data != HTS221_REG_WHOAMI_VAL) {
+ dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
+ data, HTS221_REG_WHOAMI_VAL);
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+int hts221_config_drdy(struct hts221_hw *hw, bool enable)
+{
+ u8 val = enable ? BIT(2) : 0;
+ int err;
+
+ err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR,
+ HTS221_DRDY_MASK, val);
+
+ return err < 0 ? err : 0;
+}
+
+static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
+{
+ int i, err;
+ u8 val;
+
+ for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
+ if (hts221_odr_table[i].hz == odr)
+ break;
+
+ if (i == ARRAY_SIZE(hts221_odr_table))
+ return -EINVAL;
+
+ val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val;
+ err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
+ HTS221_ODR_MASK, val);
+ if (err < 0)
+ return err;
+
+ hw->odr = odr;
+
+ return 0;
+}
+
+static int hts221_update_avg(struct hts221_hw *hw,
+ enum hts221_sensor_type type,
+ u16 val)
+{
+ int i, err;
+ const struct hts221_avg *avg = &hts221_avg_list[type];
+
+ for (i = 0; i < HTS221_AVG_DEPTH; i++)
+ if (avg->avg_avl[i].avg == val)
+ break;
+
+ if (i == HTS221_AVG_DEPTH)
+ return -EINVAL;
+
+ err = hts221_write_with_mask(hw, avg->addr, avg->mask,
+ avg->avg_avl[i].val);
+ if (err < 0)
+ return err;
+
+ hw->sensors[type].cur_avg_idx = i;
+
+ return 0;
+}
+
+static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ int i;
+ ssize_t len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+ hts221_odr_table[i].hz);
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t
+hts221_sysfs_rh_oversampling_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+ avg->avg_avl[i].avg);
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t
+hts221_sysfs_temp_oversampling_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
+ ssize_t len = 0;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+ avg->avg_avl[i].avg);
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+int hts221_power_on(struct hts221_hw *hw)
+{
+ return hts221_update_odr(hw, hw->odr);
+}
+
+int hts221_power_off(struct hts221_hw *hw)
+{
+ u8 data[] = {0x00, 0x00};
+
+ return hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data),
+ data);
+}
+
+static int hts221_parse_temp_caldata(struct hts221_hw *hw)
+{
+ int err, *slope, *b_gen;
+ s16 cal_x0, cal_x1, cal_y0, cal_y1;
+ u8 cal0, cal1;
+
+ err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
+ sizeof(cal0), &cal0);
+ if (err < 0)
+ return err;
+
+ err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
+ sizeof(cal1), &cal1);
+ if (err < 0)
+ return err;
+ cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
+
+ err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
+ sizeof(cal0), &cal0);
+ if (err < 0)
+ return err;
+ cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
+
+ err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
+ (u8 *)&cal_x0);
+ if (err < 0)
+ return err;
+ cal_x0 = le16_to_cpu(cal_x0);
+
+ err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
+ (u8 *)&cal_x1);
+ if (err < 0)
+ return err;
+ cal_x1 = le16_to_cpu(cal_x1);
+
+ slope = &hw->sensors[HTS221_SENSOR_T].slope;
+ b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
+
+ *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
+ *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
+ (cal_x1 - cal_x0);
+ *b_gen *= 8;
+
+ return 0;
+}
+
+static int hts221_parse_rh_caldata(struct hts221_hw *hw)
+{
+ int err, *slope, *b_gen;
+ s16 cal_x0, cal_x1, cal_y0, cal_y1;
+ u8 data;
+
+ err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
+ &data);
+ if (err < 0)
+ return err;
+ cal_y0 = data;
+
+ err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
+ &data);
+ if (err < 0)
+ return err;
+ cal_y1 = data;
+
+ err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
+ (u8 *)&cal_x0);
+ if (err < 0)
+ return err;
+ cal_x0 = le16_to_cpu(cal_x0);
+
+ err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
+ (u8 *)&cal_x1);
+ if (err < 0)
+ return err;
+ cal_x1 = le16_to_cpu(cal_x1);
+
+ slope = &hw->sensors[HTS221_SENSOR_H].slope;
+ b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
+
+ *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
+ *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
+ (cal_x1 - cal_x0);
+ *b_gen *= 8;
+
+ return 0;
+}
+
+static int hts221_get_sensor_scale(struct hts221_hw *hw,
+ enum iio_chan_type ch_type,
+ int *val, int *val2)
+{
+ s64 tmp;
+ s32 rem, div, data;
+
+ switch (ch_type) {
+ case IIO_HUMIDITYRELATIVE:
+ data = hw->sensors[HTS221_SENSOR_H].slope;
+ div = (1 << 4) * 1000;
+ break;
+ case IIO_TEMP:
+ data = hw->sensors[HTS221_SENSOR_T].slope;
+ div = (1 << 6) * 1000;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp = div_s64(data * 1000000000LL, div);
+ tmp = div_s64_rem(tmp, 1000000000LL, &rem);
+
+ *val = tmp;
+ *val2 = rem;
+
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int hts221_get_sensor_offset(struct hts221_hw *hw,
+ enum iio_chan_type ch_type,
+ int *val, int *val2)
+{
+ s64 tmp;
+ s32 rem, div, data;
+
+ switch (ch_type) {
+ case IIO_HUMIDITYRELATIVE:
+ data = hw->sensors[HTS221_SENSOR_H].b_gen;
+ div = hw->sensors[HTS221_SENSOR_H].slope;
+ break;
+ case IIO_TEMP:
+ data = hw->sensors[HTS221_SENSOR_T].b_gen;
+ div = hw->sensors[HTS221_SENSOR_T].slope;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ tmp = div_s64(data * 1000000000LL, div);
+ tmp = div_s64_rem(tmp, 1000000000LL, &rem);
+
+ *val = tmp;
+ *val2 = rem;
+
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
+{
+ u8 data[HTS221_DATA_SIZE];
+ int err;
+
+ err = hts221_power_on(hw);
+ if (err < 0)
+ return err;
+
+ msleep(50);
+
+ err = hw->tf->read(hw->dev, addr, sizeof(data), data);
+ if (err < 0)
+ return err;
+
+ hts221_power_off(hw);
+
+ *val = (s16)get_unaligned_le16(data);
+
+ return IIO_VAL_INT;
+}
+
+static int hts221_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *ch,
+ int *val, int *val2, long mask)
+{
+ struct hts221_hw *hw = iio_priv(iio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(iio_dev);
+ if (ret)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = hts221_read_oneshot(hw, ch->address, val);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ ret = hts221_get_sensor_scale(hw, ch->type, val, val2);
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ ret = hts221_get_sensor_offset(hw, ch->type, val, val2);
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = hw->odr;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
+ u8 idx;
+ const struct hts221_avg *avg;
+
+ switch (ch->type) {
+ case IIO_HUMIDITYRELATIVE:
+ avg = &hts221_avg_list[HTS221_SENSOR_H];
+ idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx;
+ *val = avg->avg_avl[idx].avg;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_TEMP:
+ avg = &hts221_avg_list[HTS221_SENSOR_T];
+ idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx;
+ *val = avg->avg_avl[idx].avg;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ }
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ iio_device_release_direct_mode(iio_dev);
+
+ return ret;
+}
+
+static int hts221_write_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct hts221_hw *hw = iio_priv(iio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(iio_dev);
+ if (ret)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hts221_update_odr(hw, val);
+ break;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_HUMIDITYRELATIVE:
+ ret = hts221_update_avg(hw, HTS221_SENSOR_H, val);
+ break;
+ case IIO_TEMP:
+ ret = hts221_update_avg(hw, HTS221_SENSOR_T, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ iio_device_release_direct_mode(iio_dev);
+
+ return ret;
+}
+
+static int hts221_validate_trigger(struct iio_dev *iio_dev,
+ struct iio_trigger *trig)
+{
+ struct hts221_hw *hw = iio_priv(iio_dev);
+
+ return hw->trig == trig ? 0 : -EINVAL;
+}
+
+static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
+ hts221_sysfs_rh_oversampling_avail, NULL, 0);
+static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
+ hts221_sysfs_temp_oversampling_avail, NULL, 0);
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
+
+static struct attribute *hts221_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
+ &iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group hts221_attribute_group = {
+ .attrs = hts221_attributes,
+};
+
+static const struct iio_info hts221_info = {
+ .driver_module = THIS_MODULE,
+ .attrs = &hts221_attribute_group,
+ .read_raw = hts221_read_raw,
+ .write_raw = hts221_write_raw,
+ .validate_trigger = hts221_validate_trigger,
+};
+
+static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
+
+int hts221_probe(struct iio_dev *iio_dev)
+{
+ struct hts221_hw *hw = iio_priv(iio_dev);
+ int err;
+ u8 data;
+
+ mutex_init(&hw->lock);
+
+ err = hts221_check_whoami(hw);
+ if (err < 0)
+ return err;
+
+ hw->odr = hts221_odr_table[0].hz;
+
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->dev.parent = hw->dev;
+ iio_dev->available_scan_masks = hts221_scan_masks;
+ iio_dev->channels = hts221_channels;
+ iio_dev->num_channels = ARRAY_SIZE(hts221_channels);
+ iio_dev->name = HTS221_DEV_NAME;
+ iio_dev->info = &hts221_info;
+
+ /* configure humidity sensor */
+ err = hts221_parse_rh_caldata(hw);
+ if (err < 0) {
+ dev_err(hw->dev, "failed to get rh calibration data\n");
+ return err;
+ }
+
+ data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg;
+ err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
+ if (err < 0) {
+ dev_err(hw->dev, "failed to set rh oversampling ratio\n");
+ return err;
+ }
+
+ /* configure temperature sensor */
+ err = hts221_parse_temp_caldata(hw);
+ if (err < 0) {
+ dev_err(hw->dev,
+ "failed to get temperature calibration data\n");
+ return err;
+ }
+
+ data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg;
+ err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
+ if (err < 0) {
+ dev_err(hw->dev,
+ "failed to set temperature oversampling ratio\n");
+ return err;
+ }
+
+ if (hw->irq > 0) {
+ err = hts221_allocate_buffers(hw);
+ if (err < 0)
+ return err;
+
+ err = hts221_allocate_trigger(hw);
+ if (err)
+ return err;
+ }
+
+ return devm_iio_device_register(hw->dev, iio_dev);
+}
+EXPORT_SYMBOL(hts221_probe);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c
new file mode 100644
index 000000000000..367ecd509f31
--- /dev/null
+++ b/drivers/iio/humidity/hts221_i2c.c
@@ -0,0 +1,110 @@
+/*
+ * STMicroelectronics hts221 i2c driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include "hts221.h"
+
+#define I2C_AUTO_INCREMENT 0x80
+
+static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
+{
+ struct i2c_msg msg[2];
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (len > 1)
+ addr |= I2C_AUTO_INCREMENT;
+
+ msg[0].addr = client->addr;
+ msg[0].flags = client->flags;
+ msg[0].len = 1;
+ msg[0].buf = &addr;
+
+ msg[1].addr = client->addr;
+ msg[1].flags = client->flags | I2C_M_RD;
+ msg[1].len = len;
+ msg[1].buf = data;
+
+ return i2c_transfer(client->adapter, msg, 2);
+}
+
+static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
+{
+ u8 send[len + 1];
+ struct i2c_msg msg;
+ struct i2c_client *client = to_i2c_client(dev);
+
+ if (len > 1)
+ addr |= I2C_AUTO_INCREMENT;
+
+ send[0] = addr;
+ memcpy(&send[1], data, len * sizeof(u8));
+
+ msg.addr = client->addr;
+ msg.flags = client->flags;
+ msg.len = len + 1;
+ msg.buf = send;
+
+ return i2c_transfer(client->adapter, &msg, 1);
+}
+
+static const struct hts221_transfer_function hts221_transfer_fn = {
+ .read = hts221_i2c_read,
+ .write = hts221_i2c_write,
+};
+
+static int hts221_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct hts221_hw *hw;
+ struct iio_dev *iio_dev;
+
+ iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*hw));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, iio_dev);
+
+ hw = iio_priv(iio_dev);
+ hw->name = client->name;
+ hw->dev = &client->dev;
+ hw->irq = client->irq;
+ hw->tf = &hts221_transfer_fn;
+
+ return hts221_probe(iio_dev);
+}
+
+static const struct of_device_id hts221_i2c_of_match[] = {
+ { .compatible = "st,hts221", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hts221_i2c_of_match);
+
+static const struct i2c_device_id hts221_i2c_id_table[] = {
+ { HTS221_DEV_NAME },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, hts221_i2c_id_table);
+
+static struct i2c_driver hts221_driver = {
+ .driver = {
+ .name = "hts221_i2c",
+ .of_match_table = of_match_ptr(hts221_i2c_of_match),
+ },
+ .probe = hts221_i2c_probe,
+ .id_table = hts221_i2c_id_table,
+};
+module_i2c_driver(hts221_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics hts221 i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c
new file mode 100644
index 000000000000..70df5e7150c1
--- /dev/null
+++ b/drivers/iio/humidity/hts221_spi.c
@@ -0,0 +1,125 @@
+/*
+ * STMicroelectronics hts221 spi driver
+ *
+ * Copyright 2016 STMicroelectronics Inc.
+ *
+ * Lorenzo Bianconi <lorenzo.bianconi@st.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include "hts221.h"
+
+#define SENSORS_SPI_READ 0x80
+#define SPI_AUTO_INCREMENT 0x40
+
+static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
+{
+ int err;
+ struct spi_device *spi = to_spi_device(dev);
+ struct iio_dev *iio_dev = spi_get_drvdata(spi);
+ struct hts221_hw *hw = iio_priv(iio_dev);
+
+ struct spi_transfer xfers[] = {
+ {
+ .tx_buf = hw->tb.tx_buf,
+ .bits_per_word = 8,
+ .len = 1,
+ },
+ {
+ .rx_buf = hw->tb.rx_buf,
+ .bits_per_word = 8,
+ .len = len,
+ }
+ };
+
+ if (len > 1)
+ addr |= SPI_AUTO_INCREMENT;
+ hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
+
+ err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
+ if (err < 0)
+ return err;
+
+ memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
+
+ return len;
+}
+
+static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct iio_dev *iio_dev = spi_get_drvdata(spi);
+ struct hts221_hw *hw = iio_priv(iio_dev);
+
+ struct spi_transfer xfers = {
+ .tx_buf = hw->tb.tx_buf,
+ .bits_per_word = 8,
+ .len = len + 1,
+ };
+
+ if (len >= HTS221_TX_MAX_LENGTH)
+ return -ENOMEM;
+
+ if (len > 1)
+ addr |= SPI_AUTO_INCREMENT;
+ hw->tb.tx_buf[0] = addr;
+ memcpy(&hw->tb.tx_buf[1], data, len);
+
+ return spi_sync_transfer(spi, &xfers, 1);
+}
+
+static const struct hts221_transfer_function hts221_transfer_fn = {
+ .read = hts221_spi_read,
+ .write = hts221_spi_write,
+};
+
+static int hts221_spi_probe(struct spi_device *spi)
+{
+ struct hts221_hw *hw;
+ struct iio_dev *iio_dev;
+
+ iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*hw));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, iio_dev);
+
+ hw = iio_priv(iio_dev);
+ hw->name = spi->modalias;
+ hw->dev = &spi->dev;
+ hw->irq = spi->irq;
+ hw->tf = &hts221_transfer_fn;
+
+ return hts221_probe(iio_dev);
+}
+
+static const struct of_device_id hts221_spi_of_match[] = {
+ { .compatible = "st,hts221", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, hts221_spi_of_match);
+
+static const struct spi_device_id hts221_spi_id_table[] = {
+ { HTS221_DEV_NAME },
+ {},
+};
+MODULE_DEVICE_TABLE(spi, hts221_spi_id_table);
+
+static struct spi_driver hts221_driver = {
+ .driver = {
+ .name = "hts221_spi",
+ .of_match_table = of_match_ptr(hts221_spi_of_match),
+ },
+ .probe = hts221_spi_probe,
+ .id_table = hts221_spi_id_table,
+};
+module_spi_driver(hts221_driver);
+
+MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics hts221 spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index e0251b8c1a52..5355507f8fa1 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -398,7 +398,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p)
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
struct bmi160_data *data = iio_priv(indio_dev);
- s16 buf[16]; /* 3 sens x 3 axis x s16 + 3 x s16 pad + 4 x s16 tstamp */
+ __le16 buf[16];
+ /* 3 sens x 3 axis x __le16 + 3 x __le16 pad + 4 x __le16 tstamp */
int i, ret, j = 0, base = BMI160_REG_DATA_MAGN_XOUT_L;
__le16 sample;
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index 158aaf44dd95..b12830b09c7d 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -307,10 +307,9 @@ static int iio_scan_mask_set(struct iio_dev *indio_dev,
const unsigned long *mask;
unsigned long *trialmask;
- trialmask = kmalloc(sizeof(*trialmask)*
- BITS_TO_LONGS(indio_dev->masklength),
- GFP_KERNEL);
-
+ trialmask = kmalloc_array(BITS_TO_LONGS(indio_dev->masklength),
+ sizeof(*trialmask),
+ GFP_KERNEL);
if (trialmask == NULL)
return -ENOMEM;
if (!indio_dev->masklength) {
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index fc340ed3dca1..649725bc15c1 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -81,6 +81,8 @@ static const char * const iio_chan_type_name_spec[] = {
[IIO_PH] = "ph",
[IIO_UVINDEX] = "uvindex",
[IIO_ELECTRICALCONDUCTIVITY] = "electricalconductivity",
+ [IIO_COUNT] = "count",
+ [IIO_INDEX] = "index",
};
static const char * const iio_modifier_names[] = {
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index e1e104845e38..978729f6d7c4 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -717,6 +717,27 @@ bool iio_trigger_using_own(struct iio_dev *indio_dev)
}
EXPORT_SYMBOL(iio_trigger_using_own);
+/**
+ * iio_trigger_validate_own_device - Check if a trigger and IIO device belong to
+ * the same device
+ * @trig: The IIO trigger to check
+ * @indio_dev: the IIO device to check
+ *
+ * This function can be used as the validate_device callback for triggers that
+ * can only be attached to their own device.
+ *
+ * Return: 0 if both the trigger and the IIO device belong to the same
+ * device, -EINVAL otherwise.
+ */
+int iio_trigger_validate_own_device(struct iio_trigger *trig,
+ struct iio_dev *indio_dev)
+{
+ if (indio_dev->dev.parent != trig->dev.parent)
+ return -EINVAL;
+ return 0;
+}
+EXPORT_SYMBOL(iio_trigger_validate_own_device);
+
void iio_device_register_trigger_consumer(struct iio_dev *indio_dev)
{
indio_dev->groups[indio_dev->groupcounter++] =
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index c4757e6367e7..29df11572858 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -658,6 +658,31 @@ err_unlock:
}
EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed);
+static int iio_read_channel_attribute(struct iio_channel *chan,
+ int *val, int *val2,
+ enum iio_chan_info_enum attribute)
+{
+ int ret;
+
+ mutex_lock(&chan->indio_dev->info_exist_lock);
+ if (chan->indio_dev->info == NULL) {
+ ret = -ENODEV;
+ goto err_unlock;
+ }
+
+ ret = iio_channel_read(chan, val, val2, attribute);
+err_unlock:
+ mutex_unlock(&chan->indio_dev->info_exist_lock);
+
+ return ret;
+}
+
+int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2)
+{
+ return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_OFFSET);
+}
+EXPORT_SYMBOL_GPL(iio_read_channel_offset);
+
int iio_read_channel_processed(struct iio_channel *chan, int *val)
{
int ret;
@@ -687,19 +712,7 @@ EXPORT_SYMBOL_GPL(iio_read_channel_processed);
int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2)
{
- int ret;
-
- mutex_lock(&chan->indio_dev->info_exist_lock);
- if (chan->indio_dev->info == NULL) {
- ret = -ENODEV;
- goto err_unlock;
- }
-
- ret = iio_channel_read(chan, val, val2, IIO_CHAN_INFO_SCALE);
-err_unlock:
- mutex_unlock(&chan->indio_dev->info_exist_lock);
-
- return ret;
+ return iio_read_channel_attribute(chan, val, val2, IIO_CHAN_INFO_SCALE);
}
EXPORT_SYMBOL_GPL(iio_read_channel_scale);
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index ba2e64d7ee58..d01172089828 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -140,6 +140,18 @@ config GP2AP020A00F
To compile this driver as a module, choose M here: the
module will be called gp2ap020a00f.
+config SENSORS_ISL29018
+ tristate "Intersil 29018 light and proximity sensor"
+ depends on I2C
+ select REGMAP_I2C
+ default n
+ help
+ If you say yes here you get support for ambient light sensing and
+ proximity infrared sensing from Intersil ISL29018.
+ This driver will provide the measurements of ambient light intensity
+ in lux, proximity infrared sensing and normal infrared sensing.
+ Data from sensor is accessible via sysfs.
+
config ISL29125
tristate "Intersil ISL29125 digital color light sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index c5768df87a17..15f24c557f5f 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -17,6 +17,7 @@ obj-$(CONFIG_CM36651) += cm36651.o
obj-$(CONFIG_GP2AP020A00F) += gp2ap020a00f.o
obj-$(CONFIG_HID_SENSOR_ALS) += hid-sensor-als.o
obj-$(CONFIG_HID_SENSOR_PROX) += hid-sensor-prox.o
+obj-$(CONFIG_SENSORS_ISL29018) += isl29018.o
obj-$(CONFIG_ISL29125) += isl29125.o
obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c
new file mode 100644
index 000000000000..917dd8b43e72
--- /dev/null
+++ b/drivers/iio/light/isl29018.c
@@ -0,0 +1,847 @@
+/*
+ * A iio driver for the light sensor ISL 29018/29023/29035.
+ *
+ * IIO driver for monitoring ambient light intensity in luxi, proximity
+ * sensing and infrared sensing.
+ *
+ * Copyright (c) 2010, NVIDIA Corporation.
+ *
+ * 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/i2c.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/acpi.h>
+
+#define ISL29018_CONV_TIME_MS 100
+
+#define ISL29018_REG_ADD_COMMAND1 0x00
+#define ISL29018_CMD1_OPMODE_SHIFT 5
+#define ISL29018_CMD1_OPMODE_MASK (7 << ISL29018_CMD1_OPMODE_SHIFT)
+#define ISL29018_CMD1_OPMODE_POWER_DOWN 0
+#define ISL29018_CMD1_OPMODE_ALS_ONCE 1
+#define ISL29018_CMD1_OPMODE_IR_ONCE 2
+#define ISL29018_CMD1_OPMODE_PROX_ONCE 3
+
+#define ISL29018_REG_ADD_COMMAND2 0x01
+#define ISL29018_CMD2_RESOLUTION_SHIFT 2
+#define ISL29018_CMD2_RESOLUTION_MASK (0x3 << ISL29018_CMD2_RESOLUTION_SHIFT)
+
+#define ISL29018_CMD2_RANGE_SHIFT 0
+#define ISL29018_CMD2_RANGE_MASK (0x3 << ISL29018_CMD2_RANGE_SHIFT)
+
+#define ISL29018_CMD2_SCHEME_SHIFT 7
+#define ISL29018_CMD2_SCHEME_MASK (0x1 << ISL29018_CMD2_SCHEME_SHIFT)
+
+#define ISL29018_REG_ADD_DATA_LSB 0x02
+#define ISL29018_REG_ADD_DATA_MSB 0x03
+
+#define ISL29018_REG_TEST 0x08
+#define ISL29018_TEST_SHIFT 0
+#define ISL29018_TEST_MASK (0xFF << ISL29018_TEST_SHIFT)
+
+#define ISL29035_REG_DEVICE_ID 0x0F
+#define ISL29035_DEVICE_ID_SHIFT 0x03
+#define ISL29035_DEVICE_ID_MASK (0x7 << ISL29035_DEVICE_ID_SHIFT)
+#define ISL29035_DEVICE_ID 0x5
+#define ISL29035_BOUT_SHIFT 0x07
+#define ISL29035_BOUT_MASK (0x01 << ISL29035_BOUT_SHIFT)
+
+enum isl29018_int_time {
+ ISL29018_INT_TIME_16,
+ ISL29018_INT_TIME_12,
+ ISL29018_INT_TIME_8,
+ ISL29018_INT_TIME_4,
+};
+
+static const unsigned int isl29018_int_utimes[3][4] = {
+ {90000, 5630, 351, 21},
+ {90000, 5600, 352, 22},
+ {105000, 6500, 410, 25},
+};
+
+static const struct isl29018_scale {
+ unsigned int scale;
+ unsigned int uscale;
+} isl29018_scales[4][4] = {
+ { {0, 15258}, {0, 61035}, {0, 244140}, {0, 976562} },
+ { {0, 244140}, {0, 976562}, {3, 906250}, {15, 625000} },
+ { {3, 906250}, {15, 625000}, {62, 500000}, {250, 0} },
+ { {62, 500000}, {250, 0}, {1000, 0}, {4000, 0} }
+};
+
+struct isl29018_chip {
+ struct regmap *regmap;
+ struct mutex lock;
+ int type;
+ unsigned int calibscale;
+ unsigned int ucalibscale;
+ unsigned int int_time;
+ struct isl29018_scale scale;
+ int prox_scheme;
+ bool suspended;
+};
+
+static int isl29018_set_integration_time(struct isl29018_chip *chip,
+ unsigned int utime)
+{
+ unsigned int i;
+ int ret;
+ unsigned int int_time, new_int_time;
+
+ for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i) {
+ if (utime == isl29018_int_utimes[chip->type][i]) {
+ new_int_time = i;
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(isl29018_int_utimes[chip->type]))
+ return -EINVAL;
+
+ ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
+ ISL29018_CMD2_RESOLUTION_MASK,
+ i << ISL29018_CMD2_RESOLUTION_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ /* Keep the same range when integration time changes */
+ int_time = chip->int_time;
+ for (i = 0; i < ARRAY_SIZE(isl29018_scales[int_time]); ++i) {
+ if (chip->scale.scale == isl29018_scales[int_time][i].scale &&
+ chip->scale.uscale == isl29018_scales[int_time][i].uscale) {
+ chip->scale = isl29018_scales[new_int_time][i];
+ break;
+ }
+ }
+ chip->int_time = new_int_time;
+
+ return 0;
+}
+
+static int isl29018_set_scale(struct isl29018_chip *chip, int scale, int uscale)
+{
+ unsigned int i;
+ int ret;
+ struct isl29018_scale new_scale;
+
+ for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i) {
+ if (scale == isl29018_scales[chip->int_time][i].scale &&
+ uscale == isl29018_scales[chip->int_time][i].uscale) {
+ new_scale = isl29018_scales[chip->int_time][i];
+ break;
+ }
+ }
+
+ if (i >= ARRAY_SIZE(isl29018_scales[chip->int_time]))
+ return -EINVAL;
+
+ ret = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
+ ISL29018_CMD2_RANGE_MASK,
+ i << ISL29018_CMD2_RANGE_SHIFT);
+ if (ret < 0)
+ return ret;
+
+ chip->scale = new_scale;
+
+ return 0;
+}
+
+static int isl29018_read_sensor_input(struct isl29018_chip *chip, int mode)
+{
+ int status;
+ unsigned int lsb;
+ unsigned int msb;
+ struct device *dev = regmap_get_device(chip->regmap);
+
+ /* Set mode */
+ status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1,
+ mode << ISL29018_CMD1_OPMODE_SHIFT);
+ if (status) {
+ dev_err(dev,
+ "Error in setting operating mode err %d\n", status);
+ return status;
+ }
+ msleep(ISL29018_CONV_TIME_MS);
+ status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_LSB, &lsb);
+ if (status < 0) {
+ dev_err(dev,
+ "Error in reading LSB DATA with err %d\n", status);
+ return status;
+ }
+
+ status = regmap_read(chip->regmap, ISL29018_REG_ADD_DATA_MSB, &msb);
+ if (status < 0) {
+ dev_err(dev,
+ "Error in reading MSB DATA with error %d\n", status);
+ return status;
+ }
+ dev_vdbg(dev, "MSB 0x%x and LSB 0x%x\n", msb, lsb);
+
+ return (msb << 8) | lsb;
+}
+
+static int isl29018_read_lux(struct isl29018_chip *chip, int *lux)
+{
+ int lux_data;
+ unsigned int data_x_range;
+
+ lux_data = isl29018_read_sensor_input(chip,
+ ISL29018_CMD1_OPMODE_ALS_ONCE);
+ if (lux_data < 0)
+ return lux_data;
+
+ data_x_range = lux_data * chip->scale.scale +
+ lux_data * chip->scale.uscale / 1000000;
+ *lux = data_x_range * chip->calibscale +
+ data_x_range * chip->ucalibscale / 1000000;
+
+ return 0;
+}
+
+static int isl29018_read_ir(struct isl29018_chip *chip, int *ir)
+{
+ int ir_data;
+
+ ir_data = isl29018_read_sensor_input(chip,
+ ISL29018_CMD1_OPMODE_IR_ONCE);
+ if (ir_data < 0)
+ return ir_data;
+
+ *ir = ir_data;
+
+ return 0;
+}
+
+static int isl29018_read_proximity_ir(struct isl29018_chip *chip, int scheme,
+ int *near_ir)
+{
+ int status;
+ int prox_data = -1;
+ int ir_data = -1;
+ struct device *dev = regmap_get_device(chip->regmap);
+
+ /* Do proximity sensing with required scheme */
+ status = regmap_update_bits(chip->regmap, ISL29018_REG_ADD_COMMAND2,
+ ISL29018_CMD2_SCHEME_MASK,
+ scheme << ISL29018_CMD2_SCHEME_SHIFT);
+ if (status) {
+ dev_err(dev, "Error in setting operating mode\n");
+ return status;
+ }
+
+ prox_data = isl29018_read_sensor_input(chip,
+ ISL29018_CMD1_OPMODE_PROX_ONCE);
+ if (prox_data < 0)
+ return prox_data;
+
+ if (scheme == 1) {
+ *near_ir = prox_data;
+ return 0;
+ }
+
+ ir_data = isl29018_read_sensor_input(chip,
+ ISL29018_CMD1_OPMODE_IR_ONCE);
+ if (ir_data < 0)
+ return ir_data;
+
+ if (prox_data >= ir_data)
+ *near_ir = prox_data - ir_data;
+ else
+ *near_ir = 0;
+
+ return 0;
+}
+
+static ssize_t in_illuminance_scale_available_show
+ (struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+ unsigned int i;
+ int len = 0;
+
+ mutex_lock(&chip->lock);
+ for (i = 0; i < ARRAY_SIZE(isl29018_scales[chip->int_time]); ++i)
+ len += sprintf(buf + len, "%d.%06d ",
+ isl29018_scales[chip->int_time][i].scale,
+ isl29018_scales[chip->int_time][i].uscale);
+ mutex_unlock(&chip->lock);
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t in_illuminance_integration_time_available_show
+ (struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+ unsigned int i;
+ int len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(isl29018_int_utimes[chip->type]); ++i)
+ len += sprintf(buf + len, "0.%06d ",
+ isl29018_int_utimes[chip->type][i]);
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+/*
+ * From ISL29018 Data Sheet (FN6619.4, Oct 8, 2012) regarding the
+ * infrared suppression:
+ *
+ * Proximity Sensing Scheme: Bit 7. This bit programs the function
+ * of the proximity detection. Logic 0 of this bit, Scheme 0, makes
+ * full n (4, 8, 12, 16) bits (unsigned) proximity detection. The range
+ * of Scheme 0 proximity count is from 0 to 2^n. Logic 1 of this bit,
+ * Scheme 1, makes n-1 (3, 7, 11, 15) bits (2's complementary)
+ * proximity_less_ambient detection. The range of Scheme 1
+ * proximity count is from -2^(n-1) to 2^(n-1) . The sign bit is extended
+ * for resolutions less than 16. While Scheme 0 has wider dynamic
+ * range, Scheme 1 proximity detection is less affected by the
+ * ambient IR noise variation.
+ *
+ * 0 Sensing IR from LED and ambient
+ * 1 Sensing IR from LED with ambient IR rejection
+ */
+static ssize_t proximity_on_chip_ambient_infrared_suppression_show
+ (struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+
+ /*
+ * Return the "proximity scheme" i.e. if the chip does on chip
+ * infrared suppression (1 means perform on chip suppression)
+ */
+ return sprintf(buf, "%d\n", chip->prox_scheme);
+}
+
+static ssize_t proximity_on_chip_ambient_infrared_suppression_store
+ (struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+ int val;
+
+ if (kstrtoint(buf, 10, &val))
+ return -EINVAL;
+ if (!(val == 0 || val == 1))
+ return -EINVAL;
+
+ /*
+ * Get the "proximity scheme" i.e. if the chip does on chip
+ * infrared suppression (1 means perform on chip suppression)
+ */
+ mutex_lock(&chip->lock);
+ chip->prox_scheme = val;
+ mutex_unlock(&chip->lock);
+
+ return count;
+}
+
+static int isl29018_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&chip->lock);
+ if (chip->suspended) {
+ ret = -EBUSY;
+ goto write_done;
+ }
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->type == IIO_LIGHT) {
+ chip->calibscale = val;
+ chip->ucalibscale = val2;
+ ret = 0;
+ }
+ break;
+ case IIO_CHAN_INFO_INT_TIME:
+ if (chan->type == IIO_LIGHT && !val)
+ ret = isl29018_set_integration_time(chip, val2);
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_LIGHT)
+ ret = isl29018_set_scale(chip, val, val2);
+ break;
+ default:
+ break;
+ }
+
+write_done:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+static int isl29018_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ int ret = -EINVAL;
+ struct isl29018_chip *chip = iio_priv(indio_dev);
+
+ mutex_lock(&chip->lock);
+ if (chip->suspended) {
+ ret = -EBUSY;
+ goto read_done;
+ }
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = isl29018_read_lux(chip, val);
+ break;
+ case IIO_INTENSITY:
+ ret = isl29018_read_ir(chip, val);
+ break;
+ case IIO_PROXIMITY:
+ ret = isl29018_read_proximity_ir(chip,
+ chip->prox_scheme,
+ val);
+ break;
+ default:
+ break;
+ }
+ if (!ret)
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_INT_TIME:
+ if (chan->type == IIO_LIGHT) {
+ *val = 0;
+ *val2 = isl29018_int_utimes[chip->type][chip->int_time];
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ }
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_LIGHT) {
+ *val = chip->scale.scale;
+ *val2 = chip->scale.uscale;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ }
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (chan->type == IIO_LIGHT) {
+ *val = chip->calibscale;
+ *val2 = chip->ucalibscale;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ }
+ break;
+ default:
+ break;
+ }
+
+read_done:
+ mutex_unlock(&chip->lock);
+
+ return ret;
+}
+
+#define ISL29018_LIGHT_CHANNEL { \
+ .type = IIO_LIGHT, \
+ .indexed = 1, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_INT_TIME), \
+}
+
+#define ISL29018_IR_CHANNEL { \
+ .type = IIO_INTENSITY, \
+ .modified = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .channel2 = IIO_MOD_LIGHT_IR, \
+}
+
+#define ISL29018_PROXIMITY_CHANNEL { \
+ .type = IIO_PROXIMITY, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+}
+
+static const struct iio_chan_spec isl29018_channels[] = {
+ ISL29018_LIGHT_CHANNEL,
+ ISL29018_IR_CHANNEL,
+ ISL29018_PROXIMITY_CHANNEL,
+};
+
+static const struct iio_chan_spec isl29023_channels[] = {
+ ISL29018_LIGHT_CHANNEL,
+ ISL29018_IR_CHANNEL,
+};
+
+static IIO_DEVICE_ATTR_RO(in_illuminance_integration_time_available, 0);
+static IIO_DEVICE_ATTR_RO(in_illuminance_scale_available, 0);
+static IIO_DEVICE_ATTR_RW(proximity_on_chip_ambient_infrared_suppression, 0);
+
+#define ISL29018_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr)
+
+static struct attribute *isl29018_attributes[] = {
+ ISL29018_DEV_ATTR(in_illuminance_scale_available),
+ ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
+ ISL29018_DEV_ATTR(proximity_on_chip_ambient_infrared_suppression),
+ NULL
+};
+
+static struct attribute *isl29023_attributes[] = {
+ ISL29018_DEV_ATTR(in_illuminance_scale_available),
+ ISL29018_DEV_ATTR(in_illuminance_integration_time_available),
+ NULL
+};
+
+static const struct attribute_group isl29018_group = {
+ .attrs = isl29018_attributes,
+};
+
+static const struct attribute_group isl29023_group = {
+ .attrs = isl29023_attributes,
+};
+
+enum {
+ isl29018,
+ isl29023,
+ isl29035,
+};
+
+static int isl29018_chip_init(struct isl29018_chip *chip)
+{
+ int status;
+ struct device *dev = regmap_get_device(chip->regmap);
+
+ if (chip->type == isl29035) {
+ unsigned int id;
+
+ status = regmap_read(chip->regmap, ISL29035_REG_DEVICE_ID, &id);
+ if (status < 0) {
+ dev_err(dev,
+ "Error reading ID register with error %d\n",
+ status);
+ return status;
+ }
+
+ id = (id & ISL29035_DEVICE_ID_MASK) >> ISL29035_DEVICE_ID_SHIFT;
+
+ if (id != ISL29035_DEVICE_ID)
+ return -ENODEV;
+
+ /* Clear brownout bit */
+ status = regmap_update_bits(chip->regmap,
+ ISL29035_REG_DEVICE_ID,
+ ISL29035_BOUT_MASK, 0);
+ if (status < 0)
+ return status;
+ }
+
+ /*
+ * Code added per Intersil Application Note 1534:
+ * When VDD sinks to approximately 1.8V or below, some of
+ * the part's registers may change their state. When VDD
+ * recovers to 2.25V (or greater), the part may thus be in an
+ * unknown mode of operation. The user can return the part to
+ * a known mode of operation either by (a) setting VDD = 0V for
+ * 1 second or more and then powering back up with a slew rate
+ * of 0.5V/ms or greater, or (b) via I2C disable all ALS/PROX
+ * conversions, clear the test registers, and then rewrite all
+ * registers to the desired values.
+ * ...
+ * For ISL29011, ISL29018, ISL29021, ISL29023
+ * 1. Write 0x00 to register 0x08 (TEST)
+ * 2. Write 0x00 to register 0x00 (CMD1)
+ * 3. Rewrite all registers to the desired values
+ *
+ * ISL29018 Data Sheet (FN6619.1, Feb 11, 2010) essentially says
+ * the same thing EXCEPT the data sheet asks for a 1ms delay after
+ * writing the CMD1 register.
+ */
+ status = regmap_write(chip->regmap, ISL29018_REG_TEST, 0x0);
+ if (status < 0) {
+ dev_err(dev, "Failed to clear isl29018 TEST reg.(%d)\n",
+ status);
+ return status;
+ }
+
+ /*
+ * See Intersil AN1534 comments above.
+ * "Operating Mode" (COMMAND1) register is reprogrammed when
+ * data is read from the device.
+ */
+ status = regmap_write(chip->regmap, ISL29018_REG_ADD_COMMAND1, 0);
+ if (status < 0) {
+ dev_err(dev, "Failed to clear isl29018 CMD1 reg.(%d)\n",
+ status);
+ return status;
+ }
+
+ usleep_range(1000, 2000); /* per data sheet, page 10 */
+
+ /* Set defaults */
+ status = isl29018_set_scale(chip, chip->scale.scale,
+ chip->scale.uscale);
+ if (status < 0) {
+ dev_err(dev, "Init of isl29018 fails\n");
+ return status;
+ }
+
+ status = isl29018_set_integration_time(chip,
+ isl29018_int_utimes[chip->type][chip->int_time]);
+ if (status < 0)
+ dev_err(dev, "Init of isl29018 fails\n");
+
+ return status;
+}
+
+static const struct iio_info isl29018_info = {
+ .attrs = &isl29018_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = isl29018_read_raw,
+ .write_raw = isl29018_write_raw,
+};
+
+static const struct iio_info isl29023_info = {
+ .attrs = &isl29023_group,
+ .driver_module = THIS_MODULE,
+ .read_raw = isl29018_read_raw,
+ .write_raw = isl29018_write_raw,
+};
+
+static bool isl29018_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ISL29018_REG_ADD_DATA_LSB:
+ case ISL29018_REG_ADD_DATA_MSB:
+ case ISL29018_REG_ADD_COMMAND1:
+ case ISL29018_REG_TEST:
+ case ISL29035_REG_DEVICE_ID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config isl29018_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = isl29018_is_volatile_reg,
+ .max_register = ISL29018_REG_TEST,
+ .num_reg_defaults_raw = ISL29018_REG_TEST + 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct regmap_config isl29035_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_reg = isl29018_is_volatile_reg,
+ .max_register = ISL29035_REG_DEVICE_ID,
+ .num_reg_defaults_raw = ISL29035_REG_DEVICE_ID + 1,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+struct isl29018_chip_info {
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ const struct iio_info *indio_info;
+ const struct regmap_config *regmap_cfg;
+};
+
+static const struct isl29018_chip_info isl29018_chip_info_tbl[] = {
+ [isl29018] = {
+ .channels = isl29018_channels,
+ .num_channels = ARRAY_SIZE(isl29018_channels),
+ .indio_info = &isl29018_info,
+ .regmap_cfg = &isl29018_regmap_config,
+ },
+ [isl29023] = {
+ .channels = isl29023_channels,
+ .num_channels = ARRAY_SIZE(isl29023_channels),
+ .indio_info = &isl29023_info,
+ .regmap_cfg = &isl29018_regmap_config,
+ },
+ [isl29035] = {
+ .channels = isl29023_channels,
+ .num_channels = ARRAY_SIZE(isl29023_channels),
+ .indio_info = &isl29023_info,
+ .regmap_cfg = &isl29035_regmap_config,
+ },
+};
+
+static const char *isl29018_match_acpi_device(struct device *dev, int *data)
+{
+ const struct acpi_device_id *id;
+
+ id = acpi_match_device(dev->driver->acpi_match_table, dev);
+
+ if (!id)
+ return NULL;
+
+ *data = (int)id->driver_data;
+
+ return dev_name(dev);
+}
+
+static int isl29018_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct isl29018_chip *chip;
+ struct iio_dev *indio_dev;
+ int err;
+ const char *name = NULL;
+ int dev_id = 0;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+
+ i2c_set_clientdata(client, indio_dev);
+
+ if (id) {
+ name = id->name;
+ dev_id = id->driver_data;
+ }
+
+ if (ACPI_HANDLE(&client->dev))
+ name = isl29018_match_acpi_device(&client->dev, &dev_id);
+
+ mutex_init(&chip->lock);
+
+ chip->type = dev_id;
+ chip->calibscale = 1;
+ chip->ucalibscale = 0;
+ chip->int_time = ISL29018_INT_TIME_16;
+ chip->scale = isl29018_scales[chip->int_time][0];
+ chip->suspended = false;
+
+ chip->regmap = devm_regmap_init_i2c(client,
+ isl29018_chip_info_tbl[dev_id].regmap_cfg);
+ if (IS_ERR(chip->regmap)) {
+ err = PTR_ERR(chip->regmap);
+ dev_err(&client->dev, "regmap initialization fails: %d\n", err);
+ return err;
+ }
+
+ err = isl29018_chip_init(chip);
+ if (err)
+ return err;
+
+ indio_dev->info = isl29018_chip_info_tbl[dev_id].indio_info;
+ indio_dev->channels = isl29018_chip_info_tbl[dev_id].channels;
+ indio_dev->num_channels = isl29018_chip_info_tbl[dev_id].num_channels;
+ indio_dev->name = name;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int isl29018_suspend(struct device *dev)
+{
+ struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
+
+ mutex_lock(&chip->lock);
+
+ /*
+ * Since this driver uses only polling commands, we are by default in
+ * auto shutdown (ie, power-down) mode.
+ * So we do not have much to do here.
+ */
+ chip->suspended = true;
+
+ mutex_unlock(&chip->lock);
+
+ return 0;
+}
+
+static int isl29018_resume(struct device *dev)
+{
+ struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
+ int err;
+
+ mutex_lock(&chip->lock);
+
+ err = isl29018_chip_init(chip);
+ if (!err)
+ chip->suspended = false;
+
+ mutex_unlock(&chip->lock);
+
+ return err;
+}
+
+static SIMPLE_DEV_PM_OPS(isl29018_pm_ops, isl29018_suspend, isl29018_resume);
+#define ISL29018_PM_OPS (&isl29018_pm_ops)
+#else
+#define ISL29018_PM_OPS NULL
+#endif
+
+static const struct acpi_device_id isl29018_acpi_match[] = {
+ {"ISL29018", isl29018},
+ {"ISL29023", isl29023},
+ {"ISL29035", isl29035},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, isl29018_acpi_match);
+
+static const struct i2c_device_id isl29018_id[] = {
+ {"isl29018", isl29018},
+ {"isl29023", isl29023},
+ {"isl29035", isl29035},
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, isl29018_id);
+
+static const struct of_device_id isl29018_of_match[] = {
+ { .compatible = "isil,isl29018", },
+ { .compatible = "isil,isl29023", },
+ { .compatible = "isil,isl29035", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, isl29018_of_match);
+
+static struct i2c_driver isl29018_driver = {
+ .driver = {
+ .name = "isl29018",
+ .acpi_match_table = ACPI_PTR(isl29018_acpi_match),
+ .pm = ISL29018_PM_OPS,
+ .of_match_table = isl29018_of_match,
+ },
+ .probe = isl29018_probe,
+ .id_table = isl29018_id,
+};
+module_i2c_driver(isl29018_driver);
+
+MODULE_DESCRIPTION("ISL29018 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c
index 3afc53a3d0b6..b30e0c1c6cc4 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -631,14 +631,16 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_PROCESSED:
- if (iio_buffer_enabled(indio_dev))
- return -EBUSY;
-
switch (chan->type) {
case IIO_LIGHT:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
mutex_lock(&data->lock_als);
ret = ltr501_read_als(data, buf);
mutex_unlock(&data->lock_als);
+ iio_device_release_direct_mode(indio_dev);
if (ret < 0)
return ret;
*val = ltr501_calculate_lux(le16_to_cpu(buf[1]),
@@ -648,8 +650,9 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
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_INTENSITY:
@@ -657,21 +660,28 @@ static int ltr501_read_raw(struct iio_dev *indio_dev,
ret = ltr501_read_als(data, buf);
mutex_unlock(&data->lock_als);
if (ret < 0)
- return ret;
+ break;
*val = le16_to_cpu(chan->address == LTR501_ALS_DATA1 ?
buf[0] : buf[1]);
- return IIO_VAL_INT;
+ ret = IIO_VAL_INT;
+ break;
case IIO_PROXIMITY:
mutex_lock(&data->lock_ps);
ret = ltr501_read_ps(data);
mutex_unlock(&data->lock_ps);
if (ret < 0)
- return ret;
+ break;
*val = ret & LTR501_PS_DATA_MASK;
- return IIO_VAL_INT;
+ ret = IIO_VAL_INT;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_INTENSITY:
@@ -729,8 +739,9 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
int i, ret, freq_val, freq_val2;
struct ltr501_chip_info *info = data->chip_info;
- if (iio_buffer_enabled(indio_dev))
- return -EBUSY;
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
switch (mask) {
case IIO_CHAN_INFO_SCALE:
@@ -739,85 +750,105 @@ static int ltr501_write_raw(struct iio_dev *indio_dev,
i = ltr501_get_gain_index(info->als_gain,
info->als_gain_tbl_size,
val, val2);
- if (i < 0)
- return -EINVAL;
+ if (i < 0) {
+ ret = -EINVAL;
+ break;
+ }
data->als_contr &= ~info->als_gain_mask;
data->als_contr |= i << info->als_gain_shift;
- return regmap_write(data->regmap, LTR501_ALS_CONTR,
- data->als_contr);
+ ret = regmap_write(data->regmap, LTR501_ALS_CONTR,
+ data->als_contr);
+ break;
case IIO_PROXIMITY:
i = ltr501_get_gain_index(info->ps_gain,
info->ps_gain_tbl_size,
val, val2);
- if (i < 0)
- return -EINVAL;
+ if (i < 0) {
+ ret = -EINVAL;
+ break;
+ }
data->ps_contr &= ~LTR501_CONTR_PS_GAIN_MASK;
data->ps_contr |= i << LTR501_CONTR_PS_GAIN_SHIFT;
- return regmap_write(data->regmap, LTR501_PS_CONTR,
- data->ps_contr);
+ ret = regmap_write(data->regmap, LTR501_PS_CONTR,
+ data->ps_contr);
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
+ break;
+
case IIO_CHAN_INFO_INT_TIME:
switch (chan->type) {
case IIO_INTENSITY:
- if (val != 0)
- return -EINVAL;
+ if (val != 0) {
+ ret = -EINVAL;
+ break;
+ }
mutex_lock(&data->lock_als);
- i = ltr501_set_it_time(data, val2);
+ ret = ltr501_set_it_time(data, val2);
mutex_unlock(&data->lock_als);
- return i;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
+ break;
+
case IIO_CHAN_INFO_SAMP_FREQ:
switch (chan->type) {
case IIO_INTENSITY:
ret = ltr501_als_read_samp_freq(data, &freq_val,
&freq_val2);
if (ret < 0)
- return ret;
+ break;
ret = ltr501_als_write_samp_freq(data, val, val2);
if (ret < 0)
- return ret;
+ break;
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->als_period);
if (ret < 0)
- return ltr501_als_write_samp_freq(data,
- freq_val,
- freq_val2);
- return ret;
+ ret = ltr501_als_write_samp_freq(data, freq_val,
+ freq_val2);
+ break;
case IIO_PROXIMITY:
ret = ltr501_ps_read_samp_freq(data, &freq_val,
&freq_val2);
if (ret < 0)
- return ret;
+ break;
ret = ltr501_ps_write_samp_freq(data, val, val2);
if (ret < 0)
- return ret;
+ break;
/* update persistence count when changing frequency */
ret = ltr501_write_intr_prst(data, chan->type,
0, data->ps_period);
if (ret < 0)
- return ltr501_ps_write_samp_freq(data,
- freq_val,
- freq_val2);
- return ret;
+ ret = ltr501_ps_write_samp_freq(data, freq_val,
+ freq_val2);
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
+ break;
+
+ default:
+ ret = -EINVAL;
+ break;
}
- return -EINVAL;
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
}
static int ltr501_read_thresh(struct iio_dev *indio_dev,
diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
index 6511b20a2a29..a144ca3461fc 100644
--- a/drivers/iio/light/max44000.c
+++ b/drivers/iio/light/max44000.c
@@ -204,17 +204,18 @@ static int max44000_write_alspga(struct max44000_data *data, int val)
static int max44000_read_alsval(struct max44000_data *data)
{
u16 regval;
+ __be16 val;
int alstim, ret;
ret = regmap_bulk_read(data->regmap, MAX44000_REG_ALS_DATA_HI,
- &regval, sizeof(regval));
+ &val, sizeof(val));
if (ret < 0)
return ret;
alstim = ret = max44000_read_alstim(data);
if (ret < 0)
return ret;
- regval = be16_to_cpu(regval);
+ regval = be16_to_cpu(val);
/*
* Overflow is explained on datasheet page 17.
diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c
index 217353145676..ce09d771c1fb 100644
--- a/drivers/iio/magnetometer/ak8974.c
+++ b/drivers/iio/magnetometer/ak8974.c
@@ -287,7 +287,7 @@ static int ak8974_await_drdy(struct ak8974 *ak8974)
return 0;
}
-static int ak8974_getresult(struct ak8974 *ak8974, s16 *result)
+static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result)
{
unsigned int src;
int ret;
@@ -395,7 +395,7 @@ static int ak8974_selftest(struct ak8974 *ak8974)
static int ak8974_get_u16_val(struct ak8974 *ak8974, u8 reg, u16 *val)
{
int ret;
- u16 bulk;
+ __le16 bulk;
ret = regmap_bulk_read(ak8974->map, reg, &bulk, 2);
if (ret)
@@ -453,7 +453,7 @@ static int ak8974_read_raw(struct iio_dev *indio_dev,
long mask)
{
struct ak8974 *ak8974 = iio_priv(indio_dev);
- s16 hw_values[3];
+ __le16 hw_values[3];
int ret = -EINVAL;
pm_runtime_get_sync(&ak8974->i2c->dev);
@@ -494,7 +494,7 @@ 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 */
+ __le16 hw_values[8]; /* Three axes + 64bit padding */
pm_runtime_get_sync(&ak8974->i2c->dev);
mutex_lock(&ak8974->lock);
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index af8606cc7812..825369fb1c57 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -690,6 +690,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
struct ak8975_data *data = iio_priv(indio_dev);
const struct i2c_client *client = data->client;
const struct ak_def *def = data->def;
+ __le16 rval;
u16 buff;
int ret;
@@ -703,7 +704,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
ret = i2c_smbus_read_i2c_block_data_or_emulated(
client, def->data_regs[index],
- sizeof(buff), (u8*)&buff);
+ sizeof(rval), (u8*)&rval);
if (ret < 0)
goto exit;
@@ -713,7 +714,7 @@ static int ak8975_read_axis(struct iio_dev *indio_dev, int index, int *val)
pm_runtime_put_autosuspend(&data->client->dev);
/* Swap bytes and convert to valid range. */
- buff = le16_to_cpu(buff);
+ buff = le16_to_cpu(rval);
*val = clamp_t(s16, buff, -def->range, def->range);
return IIO_VAL_INT;
@@ -813,6 +814,7 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
const struct ak_def *def = data->def;
int ret;
s16 buff[8]; /* 3 x 16 bits axis values + 1 aligned 64 bits timestamp */
+ __le16 fval[3];
mutex_lock(&data->lock);
@@ -826,17 +828,17 @@ static void ak8975_fill_buffer(struct iio_dev *indio_dev)
*/
ret = i2c_smbus_read_i2c_block_data_or_emulated(client,
def->data_regs[0],
- 3 * sizeof(buff[0]),
- (u8 *)buff);
+ 3 * sizeof(fval[0]),
+ (u8 *)fval);
if (ret < 0)
goto unlock;
mutex_unlock(&data->lock);
/* Clamp to valid range. */
- buff[0] = clamp_t(s16, le16_to_cpu(buff[0]), -def->range, def->range);
- buff[1] = clamp_t(s16, le16_to_cpu(buff[1]), -def->range, def->range);
- buff[2] = clamp_t(s16, le16_to_cpu(buff[2]), -def->range, def->range);
+ buff[0] = clamp_t(s16, le16_to_cpu(fval[0]), -def->range, def->range);
+ buff[1] = clamp_t(s16, le16_to_cpu(fval[1]), -def->range, def->range);
+ buff[2] = clamp_t(s16, le16_to_cpu(fval[2]), -def->range, def->range);
iio_push_to_buffers_with_timestamp(indio_dev, buff,
iio_get_time_ns(indio_dev));
diff --git a/drivers/iio/potentiostat/Kconfig b/drivers/iio/potentiostat/Kconfig
new file mode 100644
index 000000000000..1e3baf2cc97d
--- /dev/null
+++ b/drivers/iio/potentiostat/Kconfig
@@ -0,0 +1,22 @@
+#
+# Potentiostat drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Digital potentiostats"
+
+config LMP91000
+ tristate "Texas Instruments LMP91000 potentiostat driver"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_BUFFER_CB
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the Texas Instruments
+ LMP91000 digital potentiostat chip.
+
+ To compile this driver as a module, choose M here: the
+ module will be called lmp91000
+
+endmenu
diff --git a/drivers/iio/potentiostat/Makefile b/drivers/iio/potentiostat/Makefile
new file mode 100644
index 000000000000..64d315ef4449
--- /dev/null
+++ b/drivers/iio/potentiostat/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for industrial I/O potentiostat drivers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_LMP91000) += lmp91000.o
diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c
new file mode 100644
index 000000000000..e22714365022
--- /dev/null
+++ b/drivers/iio/potentiostat/lmp91000.c
@@ -0,0 +1,446 @@
+/*
+ * lmp91000.c - Support for Texas Instruments digital potentiostats
+ *
+ * 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.
+ *
+ * TODO: bias voltage + polarity control, and multiple chip support
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/delay.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/consumer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define LMP91000_REG_LOCK 0x01
+#define LMP91000_REG_TIACN 0x10
+#define LMP91000_REG_TIACN_GAIN_SHIFT 2
+
+#define LMP91000_REG_REFCN 0x11
+#define LMP91000_REG_REFCN_EXT_REF 0x20
+#define LMP91000_REG_REFCN_50_ZERO 0x80
+
+#define LMP91000_REG_MODECN 0x12
+#define LMP91000_REG_MODECN_3LEAD 0x03
+#define LMP91000_REG_MODECN_TEMP 0x07
+
+#define LMP91000_DRV_NAME "lmp91000"
+
+static const int lmp91000_tia_gain[] = { 0, 2750, 3500, 7000, 14000, 35000,
+ 120000, 350000 };
+
+static const int lmp91000_rload[] = { 10, 33, 50, 100 };
+
+#define LMP91000_TEMP_BASE -40
+
+static const u16 lmp91000_temp_lut[] = {
+ 1875, 1867, 1860, 1852, 1844, 1836, 1828, 1821, 1813, 1805,
+ 1797, 1789, 1782, 1774, 1766, 1758, 1750, 1742, 1734, 1727,
+ 1719, 1711, 1703, 1695, 1687, 1679, 1671, 1663, 1656, 1648,
+ 1640, 1632, 1624, 1616, 1608, 1600, 1592, 1584, 1576, 1568,
+ 1560, 1552, 1544, 1536, 1528, 1520, 1512, 1504, 1496, 1488,
+ 1480, 1472, 1464, 1456, 1448, 1440, 1432, 1424, 1415, 1407,
+ 1399, 1391, 1383, 1375, 1367, 1359, 1351, 1342, 1334, 1326,
+ 1318, 1310, 1302, 1293, 1285, 1277, 1269, 1261, 1253, 1244,
+ 1236, 1228, 1220, 1212, 1203, 1195, 1187, 1179, 1170, 1162,
+ 1154, 1146, 1137, 1129, 1121, 1112, 1104, 1096, 1087, 1079,
+ 1071, 1063, 1054, 1046, 1038, 1029, 1021, 1012, 1004, 996,
+ 987, 979, 971, 962, 954, 945, 937, 929, 920, 912,
+ 903, 895, 886, 878, 870, 861 };
+
+static const struct regmap_config lmp91000_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+};
+
+struct lmp91000_data {
+ struct regmap *regmap;
+ struct device *dev;
+
+ struct iio_trigger *trig;
+ struct iio_cb_buffer *cb_buffer;
+ struct iio_channel *adc_chan;
+
+ struct completion completion;
+ u8 chan_select;
+
+ u32 buffer[4]; /* 64-bit data + 64-bit timestamp */
+};
+
+static const struct iio_chan_spec lmp91000_channels[] = {
+ { /* chemical channel mV */
+ .type = IIO_VOLTAGE,
+ .channel = 0,
+ .address = LMP91000_REG_MODECN_3LEAD,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+ { /* temperature channel mV */
+ .type = IIO_TEMP,
+ .channel = 1,
+ .address = LMP91000_REG_MODECN_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .scan_index = -1,
+ },
+};
+
+static int lmp91000_read(struct lmp91000_data *data, int channel, int *val)
+{
+ int state, ret;
+
+ ret = regmap_read(data->regmap, LMP91000_REG_MODECN, &state);
+ if (ret)
+ return -EINVAL;
+
+ ret = regmap_write(data->regmap, LMP91000_REG_MODECN, channel);
+ if (ret)
+ return -EINVAL;
+
+ /* delay till first temperature reading is complete */
+ if ((state != channel) && (channel == LMP91000_REG_MODECN_TEMP))
+ usleep_range(3000, 4000);
+
+ data->chan_select = channel != LMP91000_REG_MODECN_3LEAD;
+
+ iio_trigger_poll_chained(data->trig);
+
+ ret = wait_for_completion_timeout(&data->completion, HZ);
+ reinit_completion(&data->completion);
+
+ if (!ret)
+ return -ETIMEDOUT;
+
+ *val = data->buffer[data->chan_select];
+
+ return 0;
+}
+
+static irqreturn_t lmp91000_buffer_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct lmp91000_data *data = iio_priv(indio_dev);
+ int ret, val;
+
+ memset(data->buffer, 0, sizeof(data->buffer));
+
+ ret = lmp91000_read(data, LMP91000_REG_MODECN_3LEAD, &val);
+ if (!ret) {
+ data->buffer[0] = val;
+ 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 lmp91000_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct lmp91000_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED: {
+ int ret = iio_channel_start_all_cb(data->cb_buffer);
+
+ if (ret)
+ return ret;
+
+ ret = lmp91000_read(data, chan->address, val);
+
+ iio_channel_stop_all_cb(data->cb_buffer);
+
+ if (ret)
+ return ret;
+
+ if (mask == IIO_CHAN_INFO_PROCESSED) {
+ int tmp, i;
+
+ ret = iio_convert_raw_to_processed(data->adc_chan,
+ *val, &tmp, 1);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(lmp91000_temp_lut); i++)
+ if (lmp91000_temp_lut[i] < tmp)
+ break;
+
+ *val = (LMP91000_TEMP_BASE + i) * 1000;
+ }
+ return IIO_VAL_INT;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ return iio_read_channel_offset(data->adc_chan, val, val2);
+ case IIO_CHAN_INFO_SCALE:
+ return iio_read_channel_scale(data->adc_chan, val, val2);
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info lmp91000_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = lmp91000_read_raw,
+};
+
+static int lmp91000_read_config(struct lmp91000_data *data)
+{
+ struct device *dev = data->dev;
+ struct device_node *np = dev->of_node;
+ unsigned int reg, val;
+ int i, ret;
+
+ ret = of_property_read_u32(np, "ti,tia-gain-ohm", &val);
+ if (ret) {
+ if (of_property_read_bool(np, "ti,external-tia-resistor"))
+ val = 0;
+ else {
+ dev_err(dev, "no ti,tia-gain-ohm defined");
+ return ret;
+ }
+ }
+
+ ret = -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(lmp91000_tia_gain); i++) {
+ if (lmp91000_tia_gain[i] == val) {
+ reg = i << LMP91000_REG_TIACN_GAIN_SHIFT;
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret) {
+ dev_err(dev, "invalid ti,tia-gain-ohm %d\n", val);
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "ti,rload-ohm", &val);
+ if (ret) {
+ val = 100;
+ dev_info(dev, "no ti,rload-ohm defined, default to %d\n", val);
+ }
+
+ ret = -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(lmp91000_rload); i++) {
+ if (lmp91000_rload[i] == val) {
+ reg |= i;
+ ret = 0;
+ break;
+ }
+ }
+
+ if (ret) {
+ dev_err(dev, "invalid ti,rload-ohm %d\n", val);
+ return ret;
+ }
+
+ regmap_write(data->regmap, LMP91000_REG_LOCK, 0);
+ regmap_write(data->regmap, LMP91000_REG_TIACN, reg);
+ regmap_write(data->regmap, LMP91000_REG_REFCN, LMP91000_REG_REFCN_EXT_REF
+ | LMP91000_REG_REFCN_50_ZERO);
+ regmap_write(data->regmap, LMP91000_REG_LOCK, 1);
+
+ return 0;
+}
+
+static int lmp91000_buffer_cb(const void *val, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct lmp91000_data *data = iio_priv(indio_dev);
+
+ data->buffer[data->chan_select] = *((int *)val);
+ complete_all(&data->completion);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops lmp91000_trigger_ops = {
+ .owner = THIS_MODULE,
+};
+
+
+static int lmp91000_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct lmp91000_data *data = iio_priv(indio_dev);
+
+ return iio_channel_start_all_cb(data->cb_buffer);
+}
+
+static int lmp91000_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct lmp91000_data *data = iio_priv(indio_dev);
+
+ iio_channel_stop_all_cb(data->cb_buffer);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops lmp91000_buffer_setup_ops = {
+ .preenable = lmp91000_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = lmp91000_buffer_predisable,
+};
+
+static int lmp91000_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct lmp91000_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ indio_dev->info = &lmp91000_info;
+ indio_dev->channels = lmp91000_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lmp91000_channels);
+ indio_dev->name = LMP91000_DRV_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ i2c_set_clientdata(client, indio_dev);
+
+ data = iio_priv(indio_dev);
+ data->dev = dev;
+ data->regmap = devm_regmap_init_i2c(client, &lmp91000_regmap_config);
+ if (IS_ERR(data->regmap)) {
+ dev_err(dev, "regmap initialization failed.\n");
+ return PTR_ERR(data->regmap);
+ }
+
+ data->trig = devm_iio_trigger_alloc(data->dev, "%s-mux%d",
+ indio_dev->name, indio_dev->id);
+ if (!data->trig) {
+ dev_err(dev, "cannot allocate iio trigger.\n");
+ return -ENOMEM;
+ }
+
+ data->trig->ops = &lmp91000_trigger_ops;
+ data->trig->dev.parent = dev;
+ init_completion(&data->completion);
+
+ ret = lmp91000_read_config(data);
+ if (ret)
+ return ret;
+
+ ret = iio_trigger_set_immutable(iio_channel_cb_get_iio_dev(data->cb_buffer),
+ data->trig);
+ if (ret) {
+ dev_err(dev, "cannot set immutable trigger.\n");
+ return ret;
+ }
+
+ ret = iio_trigger_register(data->trig);
+ if (ret) {
+ dev_err(dev, "cannot register iio trigger.\n");
+ return ret;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ &lmp91000_buffer_handler,
+ &lmp91000_buffer_setup_ops);
+ if (ret)
+ goto error_unreg_trigger;
+
+ data->cb_buffer = iio_channel_get_all_cb(dev, &lmp91000_buffer_cb,
+ indio_dev);
+
+ if (IS_ERR(data->cb_buffer)) {
+ if (PTR_ERR(data->cb_buffer) == -ENODEV)
+ ret = -EPROBE_DEFER;
+ else
+ ret = PTR_ERR(data->cb_buffer);
+
+ goto error_unreg_buffer;
+ }
+
+ data->adc_chan = iio_channel_cb_get_channels(data->cb_buffer);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_unreg_cb_buffer;
+
+ return 0;
+
+error_unreg_cb_buffer:
+ iio_channel_release_all_cb(data->cb_buffer);
+
+error_unreg_buffer:
+ iio_triggered_buffer_cleanup(indio_dev);
+
+error_unreg_trigger:
+ iio_trigger_unregister(data->trig);
+
+ return ret;
+}
+
+static int lmp91000_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct lmp91000_data *data = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ iio_channel_stop_all_cb(data->cb_buffer);
+ iio_channel_release_all_cb(data->cb_buffer);
+
+ iio_triggered_buffer_cleanup(indio_dev);
+ iio_trigger_unregister(data->trig);
+
+ return 0;
+}
+
+static const struct of_device_id lmp91000_of_match[] = {
+ { .compatible = "ti,lmp91000", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, lmp91000_of_match);
+
+static const struct i2c_device_id lmp91000_id[] = {
+ { "lmp91000", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, lmp91000_id);
+
+static struct i2c_driver lmp91000_driver = {
+ .driver = {
+ .name = LMP91000_DRV_NAME,
+ .of_match_table = of_match_ptr(lmp91000_of_match),
+ },
+ .probe = lmp91000_probe,
+ .remove = lmp91000_remove,
+ .id_table = lmp91000_id,
+};
+module_i2c_driver(lmp91000_driver);
+
+MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>");
+MODULE_DESCRIPTION("LMP91000 digital potentiostat");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index 15cd416365c1..bd8d96b96771 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -5,6 +5,16 @@
menu "Pressure sensors"
+config ABP060MG
+ tristate "Honeywell ABP pressure sensor driver"
+ depends on I2C
+ help
+ Say yes here to build support for the Honeywell ABP pressure
+ sensors.
+
+ To compile this driver as a module, choose M here: the module
+ will be called abp060mg.
+
config BMP280
tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver"
depends on (I2C || SPI_MASTER)
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index fff77185a5cc..de3dbc81dc5a 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -3,6 +3,7 @@
#
# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_ABP060MG) += abp060mg.o
obj-$(CONFIG_BMP280) += bmp280.o
bmp280-objs := bmp280-core.o bmp280-regmap.o
obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
diff --git a/drivers/iio/pressure/abp060mg.c b/drivers/iio/pressure/abp060mg.c
new file mode 100644
index 000000000000..43bdd0b9155f
--- /dev/null
+++ b/drivers/iio/pressure/abp060mg.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2016 - Marcin Malagowski <mrc@bourne.st>
+ *
+ * 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/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+
+#define ABP060MG_ERROR_MASK 0xC000
+#define ABP060MG_RESP_TIME_MS 40
+#define ABP060MG_MIN_COUNTS 1638 /* = 0x0666 (10% of u14) */
+#define ABP060MG_MAX_COUNTS 14745 /* = 0x3999 (90% of u14) */
+#define ABP060MG_NUM_COUNTS (ABP060MG_MAX_COUNTS - ABP060MG_MIN_COUNTS)
+
+enum abp_variant {
+ /* gage [kPa] */
+ ABP006KG, ABP010KG, ABP016KG, ABP025KG, ABP040KG, ABP060KG, ABP100KG,
+ ABP160KG, ABP250KG, ABP400KG, ABP600KG, ABP001GG,
+ /* differential [kPa] */
+ ABP006KD, ABP010KD, ABP016KD, ABP025KD, ABP040KD, ABP060KD, ABP100KD,
+ ABP160KD, ABP250KD, ABP400KD,
+ /* gage [psi] */
+ ABP001PG, ABP005PG, ABP015PG, ABP030PG, ABP060PG, ABP100PG, ABP150PG,
+ /* differential [psi] */
+ ABP001PD, ABP005PD, ABP015PD, ABP030PD, ABP060PD,
+};
+
+struct abp_config {
+ int min;
+ int max;
+};
+
+static struct abp_config abp_config[] = {
+ /* mbar & kPa variants */
+ [ABP006KG] = { .min = 0, .max = 6000 },
+ [ABP010KG] = { .min = 0, .max = 10000 },
+ [ABP016KG] = { .min = 0, .max = 16000 },
+ [ABP025KG] = { .min = 0, .max = 25000 },
+ [ABP040KG] = { .min = 0, .max = 40000 },
+ [ABP060KG] = { .min = 0, .max = 60000 },
+ [ABP100KG] = { .min = 0, .max = 100000 },
+ [ABP160KG] = { .min = 0, .max = 160000 },
+ [ABP250KG] = { .min = 0, .max = 250000 },
+ [ABP400KG] = { .min = 0, .max = 400000 },
+ [ABP600KG] = { .min = 0, .max = 600000 },
+ [ABP001GG] = { .min = 0, .max = 1000000 },
+ [ABP006KD] = { .min = -6000, .max = 6000 },
+ [ABP010KD] = { .min = -10000, .max = 10000 },
+ [ABP016KD] = { .min = -16000, .max = 16000 },
+ [ABP025KD] = { .min = -25000, .max = 25000 },
+ [ABP040KD] = { .min = -40000, .max = 40000 },
+ [ABP060KD] = { .min = -60000, .max = 60000 },
+ [ABP100KD] = { .min = -100000, .max = 100000 },
+ [ABP160KD] = { .min = -160000, .max = 160000 },
+ [ABP250KD] = { .min = -250000, .max = 250000 },
+ [ABP400KD] = { .min = -400000, .max = 400000 },
+ /* psi variants (1 psi ~ 6895 Pa) */
+ [ABP001PG] = { .min = 0, .max = 6985 },
+ [ABP005PG] = { .min = 0, .max = 34474 },
+ [ABP015PG] = { .min = 0, .max = 103421 },
+ [ABP030PG] = { .min = 0, .max = 206843 },
+ [ABP060PG] = { .min = 0, .max = 413686 },
+ [ABP100PG] = { .min = 0, .max = 689476 },
+ [ABP150PG] = { .min = 0, .max = 1034214 },
+ [ABP001PD] = { .min = -6895, .max = 6895 },
+ [ABP005PD] = { .min = -34474, .max = 34474 },
+ [ABP015PD] = { .min = -103421, .max = 103421 },
+ [ABP030PD] = { .min = -206843, .max = 206843 },
+ [ABP060PD] = { .min = -413686, .max = 413686 },
+};
+
+struct abp_state {
+ struct i2c_client *client;
+ struct mutex lock;
+
+ /*
+ * bus-dependent MEASURE_REQUEST length.
+ * If no SMBUS_QUICK support, need to send dummy byte
+ */
+ int mreq_len;
+
+ /* model-dependent values (calculated on probe) */
+ int scale;
+ int offset;
+};
+
+static const struct iio_chan_spec abp060mg_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int abp060mg_get_measurement(struct abp_state *state, int *val)
+{
+ struct i2c_client *client = state->client;
+ __be16 buf[2];
+ u16 pressure;
+ int ret;
+
+ buf[0] = 0;
+ ret = i2c_master_send(client, (u8 *)&buf, state->mreq_len);
+ if (ret < 0)
+ return ret;
+
+ msleep_interruptible(ABP060MG_RESP_TIME_MS);
+
+ ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ pressure = be16_to_cpu(buf[0]);
+ if (pressure & ABP060MG_ERROR_MASK)
+ return -EIO;
+
+ if (pressure < ABP060MG_MIN_COUNTS || pressure > ABP060MG_MAX_COUNTS)
+ return -EIO;
+
+ *val = pressure;
+
+ return IIO_VAL_INT;
+}
+
+static int abp060mg_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct abp_state *state = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&state->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = abp060mg_get_measurement(state, val);
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = state->offset;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = state->scale;
+ *val2 = ABP060MG_NUM_COUNTS * 1000; /* to kPa */
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&state->lock);
+ return ret;
+}
+
+static const struct iio_info abp060mg_info = {
+ .driver_module = THIS_MODULE,
+ .read_raw = abp060mg_read_raw,
+};
+
+static void abp060mg_init_device(struct iio_dev *indio_dev, unsigned long id)
+{
+ struct abp_state *state = iio_priv(indio_dev);
+ struct abp_config *cfg = &abp_config[id];
+
+ state->scale = cfg->max - cfg->min;
+ state->offset = -ABP060MG_MIN_COUNTS;
+
+ if (cfg->min < 0) /* differential */
+ state->offset -= ABP060MG_NUM_COUNTS >> 1;
+}
+
+static int abp060mg_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct abp_state *state;
+ unsigned long cfg_id = id->driver_data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ state = iio_priv(indio_dev);
+ i2c_set_clientdata(client, state);
+ state->client = client;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_QUICK))
+ state->mreq_len = 1;
+
+ abp060mg_init_device(indio_dev, cfg_id);
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = dev_name(&client->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &abp060mg_info;
+
+ indio_dev->channels = abp060mg_channels;
+ indio_dev->num_channels = ARRAY_SIZE(abp060mg_channels);
+
+ mutex_init(&state->lock);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id abp060mg_id_table[] = {
+ /* mbar & kPa variants (abp060m [60 mbar] == abp006k [6 kPa]) */
+ /* gage: */
+ { "abp060mg", ABP006KG }, { "abp006kg", ABP006KG },
+ { "abp100mg", ABP010KG }, { "abp010kg", ABP010KG },
+ { "abp160mg", ABP016KG }, { "abp016kg", ABP016KG },
+ { "abp250mg", ABP025KG }, { "abp025kg", ABP025KG },
+ { "abp400mg", ABP040KG }, { "abp040kg", ABP040KG },
+ { "abp600mg", ABP060KG }, { "abp060kg", ABP060KG },
+ { "abp001bg", ABP100KG }, { "abp100kg", ABP100KG },
+ { "abp1_6bg", ABP160KG }, { "abp160kg", ABP160KG },
+ { "abp2_5bg", ABP250KG }, { "abp250kg", ABP250KG },
+ { "abp004bg", ABP400KG }, { "abp400kg", ABP400KG },
+ { "abp006bg", ABP600KG }, { "abp600kg", ABP600KG },
+ { "abp010bg", ABP001GG }, { "abp001gg", ABP001GG },
+ /* differential: */
+ { "abp060md", ABP006KD }, { "abp006kd", ABP006KD },
+ { "abp100md", ABP010KD }, { "abp010kd", ABP010KD },
+ { "abp160md", ABP016KD }, { "abp016kd", ABP016KD },
+ { "abp250md", ABP025KD }, { "abp025kd", ABP025KD },
+ { "abp400md", ABP040KD }, { "abp040kd", ABP040KD },
+ { "abp600md", ABP060KD }, { "abp060kd", ABP060KD },
+ { "abp001bd", ABP100KD }, { "abp100kd", ABP100KD },
+ { "abp1_6bd", ABP160KD }, { "abp160kd", ABP160KD },
+ { "abp2_5bd", ABP250KD }, { "abp250kd", ABP250KD },
+ { "abp004bd", ABP400KD }, { "abp400kd", ABP400KD },
+ /* psi variants */
+ /* gage: */
+ { "abp001pg", ABP001PG },
+ { "abp005pg", ABP005PG },
+ { "abp015pg", ABP015PG },
+ { "abp030pg", ABP030PG },
+ { "abp060pg", ABP060PG },
+ { "abp100pg", ABP100PG },
+ { "abp150pg", ABP150PG },
+ /* differential: */
+ { "abp001pd", ABP001PD },
+ { "abp005pd", ABP005PD },
+ { "abp015pd", ABP015PD },
+ { "abp030pd", ABP030PD },
+ { "abp060pd", ABP060PD },
+ { /* empty */ },
+};
+MODULE_DEVICE_TABLE(i2c, abp060mg_id_table);
+
+static struct i2c_driver abp060mg_driver = {
+ .driver = {
+ .name = "abp060mg",
+ },
+ .probe = abp060mg_probe,
+ .id_table = abp060mg_id_table,
+};
+module_i2c_driver(abp060mg_driver);
+
+MODULE_AUTHOR("Marcin Malagowski <mrc@bourne.st>");
+MODULE_DESCRIPTION("Honeywell ABP pressure sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c
index 6392d7b62841..cc3f84139157 100644
--- a/drivers/iio/pressure/mpl3115.c
+++ b/drivers/iio/pressure/mpl3115.c
@@ -82,8 +82,9 @@ static int mpl3115_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_PRESSURE: /* in 0.25 pascal / LSB */
@@ -91,32 +92,39 @@ static int mpl3115_read_raw(struct iio_dev *indio_dev,
ret = mpl3115_request(data);
if (ret < 0) {
mutex_unlock(&data->lock);
- return ret;
+ break;
}
ret = i2c_smbus_read_i2c_block_data(data->client,
MPL3115_OUT_PRESS, 3, (u8 *) &tmp);
mutex_unlock(&data->lock);
if (ret < 0)
- return ret;
+ break;
*val = be32_to_cpu(tmp) >> 12;
- return IIO_VAL_INT;
+ ret = IIO_VAL_INT;
+ break;
case IIO_TEMP: /* in 0.0625 celsius / LSB */
mutex_lock(&data->lock);
ret = mpl3115_request(data);
if (ret < 0) {
mutex_unlock(&data->lock);
- return ret;
+ break;
}
ret = i2c_smbus_read_i2c_block_data(data->client,
MPL3115_OUT_TEMP, 2, (u8 *) &tmp);
mutex_unlock(&data->lock);
if (ret < 0)
- return ret;
+ break;
*val = sign_extend32(be32_to_cpu(tmp) >> 20, 11);
- return IIO_VAL_INT;
+ ret = IIO_VAL_INT;
+ break;
default:
- return -EINVAL;
+ ret = -EINVAL;
+ break;
}
+
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
+
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_PRESSURE:
diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
index a74ed1f0c880..6bd53e702667 100644
--- a/drivers/iio/pressure/ms5611_core.c
+++ b/drivers/iio/pressure/ms5611_core.c
@@ -392,17 +392,14 @@ static int ms5611_init(struct iio_dev *indio_dev)
/* Enable attached regulator if any. */
st->vdd = devm_regulator_get(indio_dev->dev.parent, "vdd");
- if (!IS_ERR(st->vdd)) {
- ret = regulator_enable(st->vdd);
- if (ret) {
- dev_err(indio_dev->dev.parent,
- "failed to enable Vdd supply: %d\n", ret);
- return ret;
- }
- } else {
- ret = PTR_ERR(st->vdd);
- if (ret != -ENODEV)
- return ret;
+ if (IS_ERR(st->vdd))
+ return PTR_ERR(st->vdd);
+
+ ret = regulator_enable(st->vdd);
+ if (ret) {
+ dev_err(indio_dev->dev.parent,
+ "failed to enable Vdd supply: %d\n", ret);
+ return ret;
}
ret = ms5611_reset(indio_dev);
diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c
index 19d2eb46fda6..c720c3ac0b9b 100644
--- a/drivers/iio/pressure/zpa2326.c
+++ b/drivers/iio/pressure/zpa2326.c
@@ -147,12 +147,8 @@ struct zpa2326_private {
#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)
{
diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
index 3141c3c161bb..1fa9eefa0982 100644
--- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
+++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c
@@ -301,8 +301,6 @@ static int lidar_probe(struct i2c_client *client,
if (ret)
goto error_unreg_buffer;
pm_runtime_enable(&client->dev);
-
- pm_runtime_mark_last_busy(&client->dev);
pm_runtime_idle(&client->dev);
return 0;