From 948b707df8d2493346f7a2979632ab5ae8b8abb6 Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Mon, 9 Jan 2017 08:25:26 +0530 Subject: [PATCH 01/42] iio:temperature: Add support for TI TMP007 sensor This patch adds support for TI TMP007 - 16 bit IR thermopile sensor with integrated Math engine. Sensor takes care of calculating the object temperature with the help of calibrated constants stored in non-volatile memory, thereby reducing the calculation overhead. Signed-off-by: Manivannan Sadhasivam Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../bindings/iio/temperature/tmp007.txt | 27 ++ drivers/iio/temperature/Kconfig | 10 + drivers/iio/temperature/Makefile | 1 + drivers/iio/temperature/tmp007.c | 345 ++++++++++++++++++ 4 files changed, 383 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/temperature/tmp007.txt create mode 100644 drivers/iio/temperature/tmp007.c diff --git a/Documentation/devicetree/bindings/iio/temperature/tmp007.txt b/Documentation/devicetree/bindings/iio/temperature/tmp007.txt new file mode 100644 index 000000000000..3b8f41fa670a --- /dev/null +++ b/Documentation/devicetree/bindings/iio/temperature/tmp007.txt @@ -0,0 +1,27 @@ +* TI TMP007 - IR thermopile sensor with integrated math engine + +Link to datasheet: http://www.ti.com/lit/ds/symlink/tmp007.pdf + +Required properties: + + - compatible: should be "ti,tmp007" + - reg: the I2C address of the sensor (changeable via ADR pins) + ------------------------------ + |ADR1 | ADR0 | Device Address| + ------------------------------ + 0 0 0x40 + 0 1 0x41 + 0 SDA 0x42 + 0 SCL 0x43 + 1 0 0x44 + 1 1 0x45 + 1 SDA 0x46 + 1 SCL 0x47 + +Example: + +tmp007@40 { + compatible = "ti,tmp007"; + reg = <0x40>; +}; + diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index 5ea77a7e261d..3089e8d0a32d 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -39,6 +39,16 @@ config TMP006 This driver can also be built as a module. If so, the module will be called tmp006. +config TMP007 + tristate "TMP007 infrared thermopile sensor with Integrated Math Engine" + depends on I2C + help + If you say yes here you get support for the Texas Instruments + TMP007 infrared thermopile sensor with Integrated Math Engine. + + This driver can also be built as a module. If so, the module will + be called tmp007. + config TSYS01 tristate "Measurement Specialties TSYS01 temperature sensor using I2C bus connection" depends on I2C diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 78c3de0dc3f0..4c4377480726 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_TMP006) += tmp006.o +obj-$(CONFIG_TMP007) += tmp007.o obj-$(CONFIG_TSYS01) += tsys01.o obj-$(CONFIG_TSYS02D) += tsys02d.o diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c new file mode 100644 index 000000000000..24c6c16d169d --- /dev/null +++ b/drivers/iio/temperature/tmp007.c @@ -0,0 +1,345 @@ +/* + * tmp007.c - Support for TI TMP007 IR thermopile sensor with integrated math engine + * + * Copyright (c) 2017 Manivannan Sadhasivam + * + * This file is subject to the terms and conditions of version 2 of + * the GNU General Public License. See the file COPYING in the main + * directory of this archive for more details. + * + * Driver for the Texas Instruments I2C 16-bit IR thermopile sensor + * + * (7-bit I2C slave address (0x40 - 0x47), changeable via ADR pins) + * + * Note: This driver assumes that the sensor has been calibrated beforehand + * + * TODO: ALERT irq, limit threshold events + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#define TMP007_TDIE 0x01 +#define TMP007_CONFIG 0x02 +#define TMP007_TOBJECT 0x03 +#define TMP007_STATUS 0x04 +#define TMP007_STATUS_MASK 0x05 +#define TMP007_MANUFACTURER_ID 0x1e +#define TMP007_DEVICE_ID 0x1f + +#define TMP007_CONFIG_CONV_EN BIT(12) +#define TMP007_CONFIG_COMP_EN BIT(5) +#define TMP007_CONFIG_TC_EN BIT(6) +#define TMP007_CONFIG_CR_MASK GENMASK(11, 9) +#define TMP007_CONFIG_CR_SHIFT 9 + +#define TMP007_STATUS_CONV_READY BIT(14) +#define TMP007_STATUS_DATA_VALID BIT(9) + +#define TMP007_MANUFACTURER_MAGIC 0x5449 +#define TMP007_DEVICE_MAGIC 0x0078 + +#define TMP007_TEMP_SHIFT 2 + +struct tmp007_data { + struct i2c_client *client; + u16 config; +}; + +static const int tmp007_avgs[5][2] = { {4, 0}, {2, 0}, {1, 0}, + {0, 500000}, {0, 250000} }; + +static int tmp007_read_temperature(struct tmp007_data *data, u8 reg) +{ + s32 ret; + int tries = 50; + + while (tries-- > 0) { + ret = i2c_smbus_read_word_swapped(data->client, + TMP007_STATUS); + if (ret < 0) + return ret; + if ((ret & TMP007_STATUS_CONV_READY) && + !(ret & TMP007_STATUS_DATA_VALID)) + break; + msleep(100); + } + + if (tries < 0) + return -EIO; + + return i2c_smbus_read_word_swapped(data->client, reg); +} + +static int tmp007_powerdown(struct tmp007_data *data) +{ + return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, + data->config & ~TMP007_CONFIG_CONV_EN); +} + +static int tmp007_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct tmp007_data *data = iio_priv(indio_dev); + s32 ret; + int conv_rate; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (channel->channel2) { + case IIO_MOD_TEMP_AMBIENT: /* LSB: 0.03125 degree Celsius */ + ret = i2c_smbus_read_word_swapped(data->client, TMP007_TDIE); + if (ret < 0) + return ret; + break; + case IIO_MOD_TEMP_OBJECT: + ret = tmp007_read_temperature(data, TMP007_TOBJECT); + if (ret < 0) + return ret; + break; + default: + return -EINVAL; + } + + *val = sign_extend32(ret, 15) >> TMP007_TEMP_SHIFT; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 31; + *val2 = 250000; + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + conv_rate = (data->config & TMP007_CONFIG_CR_MASK) + >> TMP007_CONFIG_CR_SHIFT; + *val = tmp007_avgs[conv_rate][0]; + *val2 = tmp007_avgs[conv_rate][1]; + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int tmp007_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct tmp007_data *data = iio_priv(indio_dev); + int i; + u16 tmp; + + if (mask == IIO_CHAN_INFO_SAMP_FREQ) { + for (i = 0; i < ARRAY_SIZE(tmp007_avgs); i++) { + if ((val == tmp007_avgs[i][0]) && + (val2 == tmp007_avgs[i][1])) { + tmp = data->config & ~TMP007_CONFIG_CR_MASK; + tmp |= (i << TMP007_CONFIG_CR_SHIFT); + + return i2c_smbus_write_word_swapped(data->client, + TMP007_CONFIG, + data->config = tmp); + } + } + } + + return -EINVAL; +} + +static IIO_CONST_ATTR(sampling_frequency_available, "4 2 1 0.5 0.25"); + +static struct attribute *tmp007_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group tmp007_attribute_group = { + .attrs = tmp007_attributes, +}; + +static const struct iio_chan_spec tmp007_channels[] = { + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_AMBIENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_OBJECT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + } +}; + +static const struct iio_info tmp007_info = { + .read_raw = tmp007_read_raw, + .write_raw = tmp007_write_raw, + .attrs = &tmp007_attribute_group, + .driver_module = THIS_MODULE, +}; + +static bool tmp007_identify(struct i2c_client *client) +{ + int manf_id, dev_id; + + manf_id = i2c_smbus_read_word_swapped(client, TMP007_MANUFACTURER_ID); + if (manf_id < 0) + return false; + + dev_id = i2c_smbus_read_word_swapped(client, TMP007_DEVICE_ID); + if (dev_id < 0) + return false; + + return (manf_id == TMP007_MANUFACTURER_MAGIC && dev_id == TMP007_DEVICE_MAGIC); +} + +static int tmp007_probe(struct i2c_client *client, + const struct i2c_device_id *tmp007_id) +{ + struct tmp007_data *data; + struct iio_dev *indio_dev; + int ret; + u16 status; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + if (!tmp007_identify(client)) { + dev_err(&client->dev, "TMP007 not found\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + + indio_dev->dev.parent = &client->dev; + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &tmp007_info; + + indio_dev->channels = tmp007_channels; + indio_dev->num_channels = ARRAY_SIZE(tmp007_channels); + + /* + * Set Configuration register: + * 1. Conversion ON + * 2. Comparator mode + * 3. Transient correction enable + */ + + ret = i2c_smbus_read_word_swapped(data->client, TMP007_CONFIG); + if (ret < 0) + return ret; + + data->config = ret; + data->config |= (TMP007_CONFIG_CONV_EN | TMP007_CONFIG_COMP_EN | TMP007_CONFIG_TC_EN); + + ret = i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, + data->config); + if (ret < 0) + return ret; + + /* + * Set Status Mask register: + * 1. Conversion ready enable + * 2. Data valid enable + */ + + ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK); + if (ret < 0) + goto error_powerdown; + + status = ret; + status |= (TMP007_STATUS_CONV_READY | TMP007_STATUS_DATA_VALID); + + ret = i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, status); + if (ret < 0) + goto error_powerdown; + + return iio_device_register(indio_dev); + +error_powerdown: + tmp007_powerdown(data); + + return ret; +} + +static int tmp007_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct tmp007_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + tmp007_powerdown(data); + + return 0; +} + +#ifdef CONFIG_PM_SLEEP +static int tmp007_suspend(struct device *dev) +{ + struct tmp007_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + + return tmp007_powerdown(data); +} + +static int tmp007_resume(struct device *dev) +{ + struct tmp007_data *data = iio_priv(i2c_get_clientdata( + to_i2c_client(dev))); + + return i2c_smbus_write_word_swapped(data->client, TMP007_CONFIG, + data->config | TMP007_CONFIG_CONV_EN); +} +#endif + +static SIMPLE_DEV_PM_OPS(tmp007_pm_ops, tmp007_suspend, tmp007_resume); + +static const struct of_device_id tmp007_of_match[] = { + { .compatible = "ti,tmp007", }, + { }, +}; +MODULE_DEVICE_TABLE(of, tmp007_of_match); + +static const struct i2c_device_id tmp007_id[] = { + { "tmp007", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tmp007_id); + +static struct i2c_driver tmp007_driver = { + .driver = { + .name = "tmp007", + .of_match_table = of_match_ptr(tmp007_of_match), + .pm = &tmp007_pm_ops, + }, + .probe = tmp007_probe, + .remove = tmp007_remove, + .id_table = tmp007_id, +}; +module_i2c_driver(tmp007_driver); + +MODULE_AUTHOR("Manivannan Sadhasivam "); +MODULE_DESCRIPTION("TI TMP007 IR thermopile sensor driver"); +MODULE_LICENSE("GPL"); From adc8ec5ff183d09ae7a9d2dd31125401d302ba63 Mon Sep 17 00:00:00 2001 From: Linus Walleij Date: Wed, 11 Jan 2017 11:48:39 +0100 Subject: [PATCH 02/42] iio: inkern: pass through raw values if no scaling When a consumer calls iio_read_channel_processed() the IIO core tries to apply scaling to the value, but if the channel only supports reading raw values, we should return that raw value to the cosumer instead of an error. This is what userspace is expected to do with sensors only providing raw values so the kernel should do the same, so as to avoid adding scaling boilerplate to drivers for hardware that actually return processed values from raw reads. A sensor not providing a scale, but providing a _raw attribute could be valid if for example the scale is the default of 1, but an offset needs to be applied to convert to the _processed form. Signed-off-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index b0f4630a163f..7a13535dc3e9 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -601,8 +601,14 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, scale_type = iio_channel_read(chan, &scale_val, &scale_val2, IIO_CHAN_INFO_SCALE); - if (scale_type < 0) - return scale_type; + if (scale_type < 0) { + /* + * Just pass raw values as processed if no scaling is + * available. + */ + *processed = raw; + return 0; + } switch (scale_type) { case IIO_VAL_INT: From 290a6ce11d938be52634b3ce1bbc6b78be4d23c1 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 10 Jan 2017 22:55:18 +0100 Subject: [PATCH 03/42] iio: imu: add support to lsm6dsx driver Add support to STM LSM6DS3-LSM6DSM 6-axis (acc + gyro) Mems sensor http://www.st.com/resource/en/datasheet/lsm6ds3.pdf http://www.st.com/resource/en/datasheet/lsm6dsm.pdf - continuous mode support - i2c support - spi support - sw fifo mode support - supported devices: lsm6ds3, lsm6dsm Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/Kconfig | 1 + drivers/iio/imu/Makefile | 2 + drivers/iio/imu/st_lsm6dsx/Kconfig | 22 + drivers/iio/imu/st_lsm6dsx/Makefile | 5 + drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 141 ++++ .../iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 454 ++++++++++++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 673 ++++++++++++++++++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c | 101 +++ drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c | 118 +++ 9 files changed, 1517 insertions(+) create mode 100644 drivers/iio/imu/st_lsm6dsx/Kconfig create mode 100644 drivers/iio/imu/st_lsm6dsx/Makefile create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c create mode 100644 drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index 1f1ad41ef881..156630a21696 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -39,6 +39,7 @@ config KMX61 be called kmx61. source "drivers/iio/imu/inv_mpu6050/Kconfig" +source "drivers/iio/imu/st_lsm6dsx/Kconfig" endmenu diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index c71bcd30dc38..8b563c3323b5 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -17,3 +17,5 @@ obj-y += bmi160/ obj-y += inv_mpu6050/ obj-$(CONFIG_KMX61) += kmx61.o + +obj-y += st_lsm6dsx/ diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig new file mode 100644 index 000000000000..935d4cd071a3 --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/Kconfig @@ -0,0 +1,22 @@ + +config IIO_ST_LSM6DSX + tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors" + depends on (I2C || SPI) + select IIO_BUFFER + select IIO_KFIFO_BUF + select IIO_ST_LSM6DSX_I2C if (I2C) + select IIO_ST_LSM6DSX_SPI if (SPI_MASTER) + help + Say yes here to build support for STMicroelectronics LSM6DSx imu + sensor. Supported devices: lsm6ds3, lsm6dsm + + To compile this driver as a module, choose M here: the module + will be called st_lsm6dsx. + +config IIO_ST_LSM6DSX_I2C + tristate + depends on IIO_ST_LSM6DSX + +config IIO_ST_LSM6DSX_SPI + tristate + depends on IIO_ST_LSM6DSX diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile new file mode 100644 index 000000000000..35919febea2a --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/Makefile @@ -0,0 +1,5 @@ +st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o + +obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o +obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o +obj-$(CONFIG_IIO_ST_LSM6DSX_SPI) += st_lsm6dsx_spi.o diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h new file mode 100644 index 000000000000..69deafe1c10d --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -0,0 +1,141 @@ +/* + * STMicroelectronics st_lsm6dsx sensor driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#ifndef ST_LSM6DSX_H +#define ST_LSM6DSX_H + +#include + +#define ST_LSM6DS3_DEV_NAME "lsm6ds3" +#define ST_LSM6DSM_DEV_NAME "lsm6dsm" + +enum st_lsm6dsx_hw_id { + ST_LSM6DS3_ID, + ST_LSM6DSM_ID, +}; + +#define ST_LSM6DSX_CHAN_SIZE 2 +#define ST_LSM6DSX_SAMPLE_SIZE 6 +#define ST_LSM6DSX_SAMPLE_DEPTH (ST_LSM6DSX_SAMPLE_SIZE / \ + ST_LSM6DSX_CHAN_SIZE) + +#if defined(CONFIG_SPI_MASTER) +#define ST_LSM6DSX_RX_MAX_LENGTH 256 +#define ST_LSM6DSX_TX_MAX_LENGTH 8 + +struct st_lsm6dsx_transfer_buffer { + u8 rx_buf[ST_LSM6DSX_RX_MAX_LENGTH]; + u8 tx_buf[ST_LSM6DSX_TX_MAX_LENGTH] ____cacheline_aligned; +}; +#endif /* CONFIG_SPI_MASTER */ + +struct st_lsm6dsx_transfer_function { + int (*read)(struct device *dev, u8 addr, int len, u8 *data); + int (*write)(struct device *dev, u8 addr, int len, u8 *data); +}; + +struct st_lsm6dsx_reg { + u8 addr; + u8 mask; +}; + +struct st_lsm6dsx_settings { + u8 wai; + u16 max_fifo_size; + enum st_lsm6dsx_hw_id id; +}; + +enum st_lsm6dsx_sensor_id { + ST_LSM6DSX_ID_ACC, + ST_LSM6DSX_ID_GYRO, + ST_LSM6DSX_ID_MAX, +}; + +enum st_lsm6dsx_fifo_mode { + ST_LSM6DSX_FIFO_BYPASS = 0x0, + ST_LSM6DSX_FIFO_CONT = 0x6, +}; + +/** + * struct st_lsm6dsx_sensor - ST IMU sensor instance + * @id: Sensor identifier. + * @hw: Pointer to instance of struct st_lsm6dsx_hw. + * @gain: Configured sensor sensitivity. + * @odr: Output data rate of the sensor [Hz]. + * @watermark: Sensor watermark level. + * @sip: Number of samples in a given pattern. + * @decimator: FIFO decimation factor. + * @decimator_mask: Sensor mask for decimation register. + * @delta_ts: Delta time between two consecutive interrupts. + * @ts: Latest timestamp from the interrupt handler. + */ +struct st_lsm6dsx_sensor { + enum st_lsm6dsx_sensor_id id; + struct st_lsm6dsx_hw *hw; + + u32 gain; + u16 odr; + + u16 watermark; + u8 sip; + u8 decimator; + u8 decimator_mask; + + s64 delta_ts; + s64 ts; +}; + +/** + * struct st_lsm6dsx_hw - ST IMU MEMS hw instance + * @dev: Pointer to instance of struct device (I2C or SPI). + * @irq: Device interrupt line (I2C or SPI). + * @lock: Mutex to protect read and write operations. + * @fifo_lock: Mutex to prevent concurrent access to the hw FIFO. + * @fifo_mode: FIFO operating mode supported by the device. + * @enable_mask: Enabled sensor bitmask. + * @sip: Total number of samples (acc/gyro) in a given pattern. + * @iio_devs: Pointers to acc/gyro iio_dev instances. + * @settings: Pointer to the specific sensor settings in use. + * @tf: Transfer function structure used by I/O operations. + * @tb: Transfer buffers used by SPI I/O operations. + */ +struct st_lsm6dsx_hw { + struct device *dev; + int irq; + + struct mutex lock; + struct mutex fifo_lock; + + enum st_lsm6dsx_fifo_mode fifo_mode; + u8 enable_mask; + u8 sip; + + struct iio_dev *iio_devs[ST_LSM6DSX_ID_MAX]; + + const struct st_lsm6dsx_settings *settings; + + const struct st_lsm6dsx_transfer_function *tf; +#if defined(CONFIG_SPI_MASTER) + struct st_lsm6dsx_transfer_buffer tb; +#endif /* CONFIG_SPI_MASTER */ +}; + +int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, + const struct st_lsm6dsx_transfer_function *tf_ops); +int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor); +int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor); +int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw); +int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_hw *hw, u8 addr, u8 mask, + u8 val); +int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, + u16 watermark); + +#endif /* ST_LSM6DSX_H */ diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c new file mode 100644 index 000000000000..78532ce07449 --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -0,0 +1,454 @@ +/* + * STMicroelectronics st_lsm6dsx FIFO buffer library driver + * + * LSM6DS3/LSM6DSM: The FIFO buffer can be configured to store data + * from gyroscope and accelerometer. Samples are queued without any tag + * according to a specific pattern based on 'FIFO data sets' (6 bytes each): + * - 1st data set is reserved for gyroscope data + * - 2nd data set is reserved for accelerometer data + * The FIFO pattern changes depending on the ODRs and decimation factors + * assigned to the FIFO data sets. The first sequence of data stored in FIFO + * buffer contains the data of all the enabled FIFO data sets + * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the + * value of the decimation factor and ODR set for each FIFO data set. + * FIFO supported modes: + * - BYPASS: FIFO disabled + * - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index + * restarts from the beginning and the oldest sample is overwritten + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ +#include +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +#define ST_LSM6DSX_REG_FIFO_THL_ADDR 0x06 +#define ST_LSM6DSX_REG_FIFO_THH_ADDR 0x07 +#define ST_LSM6DSX_FIFO_TH_MASK GENMASK(11, 0) +#define ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR 0x08 +#define ST_LSM6DSX_REG_FIFO_MODE_ADDR 0x0a +#define ST_LSM6DSX_FIFO_MODE_MASK GENMASK(2, 0) +#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3) +#define ST_LSM6DSX_REG_FIFO_DIFFL_ADDR 0x3a +#define ST_LSM6DSX_FIFO_DIFF_MASK GENMASK(11, 0) +#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12) +#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e + +#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08 + +struct st_lsm6dsx_decimator_entry { + u8 decimator; + u8 val; +}; + +static const +struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = { + { 0, 0x0 }, + { 1, 0x1 }, + { 2, 0x2 }, + { 3, 0x3 }, + { 4, 0x4 }, + { 8, 0x5 }, + { 16, 0x6 }, + { 32, 0x7 }, +}; + +static int st_lsm6dsx_get_decimator_val(u8 val) +{ + const int max_size = ARRAY_SIZE(st_lsm6dsx_decimator_table); + int i; + + for (i = 0; i < max_size; i++) + if (st_lsm6dsx_decimator_table[i].decimator == val) + break; + + return i == max_size ? 0 : st_lsm6dsx_decimator_table[i].val; +} + +static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw, + u16 *max_odr, u16 *min_odr) +{ + struct st_lsm6dsx_sensor *sensor; + int i; + + *max_odr = 0, *min_odr = ~0; + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + sensor = iio_priv(hw->iio_devs[i]); + + if (!(hw->enable_mask & BIT(sensor->id))) + continue; + + *max_odr = max_t(u16, *max_odr, sensor->odr); + *min_odr = min_t(u16, *min_odr, sensor->odr); + } +} + +static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) +{ + struct st_lsm6dsx_sensor *sensor; + u16 max_odr, min_odr, sip = 0; + int err, i; + u8 data; + + st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr); + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + sensor = iio_priv(hw->iio_devs[i]); + + /* update fifo decimators and sample in pattern */ + if (hw->enable_mask & BIT(sensor->id)) { + sensor->sip = sensor->odr / min_odr; + sensor->decimator = max_odr / sensor->odr; + data = st_lsm6dsx_get_decimator_val(sensor->decimator); + } else { + sensor->sip = 0; + sensor->decimator = 0; + data = 0; + } + + err = st_lsm6dsx_write_with_mask(hw, + ST_LSM6DSX_REG_FIFO_DEC_GXL_ADDR, + sensor->decimator_mask, data); + if (err < 0) + return err; + + sip += sensor->sip; + } + hw->sip = sip; + + return 0; +} + +static int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, + enum st_lsm6dsx_fifo_mode fifo_mode) +{ + u8 data; + int err; + + switch (fifo_mode) { + case ST_LSM6DSX_FIFO_BYPASS: + data = fifo_mode; + break; + case ST_LSM6DSX_FIFO_CONT: + data = (ST_LSM6DSX_MAX_FIFO_ODR_VAL << + __ffs(ST_LSM6DSX_FIFO_ODR_MASK)) | fifo_mode; + break; + default: + return -EINVAL; + } + + err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_MODE_ADDR, + sizeof(data), &data); + if (err < 0) + return err; + + hw->fifo_mode = fifo_mode; + + return 0; +} + +int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark) +{ + u16 fifo_watermark = ~0, cur_watermark, sip = 0; + struct st_lsm6dsx_hw *hw = sensor->hw; + struct st_lsm6dsx_sensor *cur_sensor; + __le16 wdata; + int i, err; + u8 data; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + cur_sensor = iio_priv(hw->iio_devs[i]); + + if (!(hw->enable_mask & BIT(cur_sensor->id))) + continue; + + cur_watermark = (cur_sensor == sensor) ? watermark + : cur_sensor->watermark; + + fifo_watermark = min_t(u16, fifo_watermark, cur_watermark); + sip += cur_sensor->sip; + } + + if (!sip) + return 0; + + fifo_watermark = max_t(u16, fifo_watermark, sip); + fifo_watermark = (fifo_watermark / sip) * sip; + fifo_watermark = fifo_watermark * ST_LSM6DSX_SAMPLE_DEPTH; + + mutex_lock(&hw->lock); + + err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_THH_ADDR, + sizeof(data), &data); + if (err < 0) + goto out; + + fifo_watermark = ((data & ~ST_LSM6DSX_FIFO_TH_MASK) << 8) | + (fifo_watermark & ST_LSM6DSX_FIFO_TH_MASK); + + wdata = cpu_to_le16(fifo_watermark); + err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_FIFO_THL_ADDR, + sizeof(wdata), (u8 *)&wdata); +out: + mutex_unlock(&hw->lock); + + return err < 0 ? err : 0; +} + +/** + * st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DSM read FIFO routine + * @hw: Pointer to instance of struct st_lsm6dsx_hw. + * + * Read samples from the hw FIFO and push them to IIO buffers. + * + * Return: Number of bytes read from the FIFO + */ +static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) +{ + u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE; + int err, acc_sip, gyro_sip, read_len, samples, offset; + struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor; + s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts; + u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)]; + u8 buff[pattern_len]; + __le16 fifo_status; + + err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_DIFFL_ADDR, + sizeof(fifo_status), (u8 *)&fifo_status); + if (err < 0) + return err; + + if (fifo_status & cpu_to_le16(ST_LSM6DSX_FIFO_EMPTY_MASK)) + return 0; + + fifo_len = (le16_to_cpu(fifo_status) & ST_LSM6DSX_FIFO_DIFF_MASK) * + ST_LSM6DSX_CHAN_SIZE; + samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE; + fifo_len = (fifo_len / pattern_len) * pattern_len; + + /* + * compute delta timestamp between two consecutive samples + * in order to estimate queueing time of data generated + * by the sensor + */ + acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); + acc_ts = acc_sensor->ts - acc_sensor->delta_ts; + acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator, + samples); + + gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); + gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts; + gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator, + samples); + + for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { + err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_FIFO_OUTL_ADDR, + sizeof(buff), buff); + if (err < 0) + return err; + + /* + * Data are written to the FIFO with a specific pattern + * depending on the configured ODRs. The first sequence of data + * stored in FIFO contains the data of all enabled sensors + * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated + * depending on the value of the decimation factor set for each + * sensor. + * + * Supposing the FIFO is storing data from gyroscope and + * accelerometer at different ODRs: + * - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz + * Since the gyroscope ODR is twice the accelerometer one, the + * following pattern is repeated every 9 samples: + * - Gx, Gy, Gz, Ax, Ay, Az, Gx, Gy, Gz + */ + gyro_sip = gyro_sensor->sip; + acc_sip = acc_sensor->sip; + offset = 0; + + while (acc_sip > 0 || gyro_sip > 0) { + if (gyro_sip-- > 0) { + memcpy(iio_buff, &buff[offset], + ST_LSM6DSX_SAMPLE_SIZE); + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_GYRO], + iio_buff, gyro_ts); + offset += ST_LSM6DSX_SAMPLE_SIZE; + gyro_ts += gyro_delta_ts; + } + + if (acc_sip-- > 0) { + memcpy(iio_buff, &buff[offset], + ST_LSM6DSX_SAMPLE_SIZE); + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_ACC], + iio_buff, acc_ts); + offset += ST_LSM6DSX_SAMPLE_SIZE; + acc_ts += acc_delta_ts; + } + } + } + + return read_len; +} + +static int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw) +{ + int err; + + mutex_lock(&hw->fifo_lock); + + st_lsm6dsx_read_fifo(hw); + err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS); + + mutex_unlock(&hw->fifo_lock); + + return err; +} + +static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + int err; + + if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS) { + err = st_lsm6dsx_flush_fifo(hw); + if (err < 0) + return err; + } + + if (enable) { + err = st_lsm6dsx_sensor_enable(sensor); + if (err < 0) + return err; + } else { + err = st_lsm6dsx_sensor_disable(sensor); + if (err < 0) + return err; + } + + err = st_lsm6dsx_update_decimators(hw); + if (err < 0) + return err; + + err = st_lsm6dsx_update_watermark(sensor, sensor->watermark); + if (err < 0) + return err; + + if (hw->enable_mask) { + err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT); + if (err < 0) + return err; + + /* + * store enable buffer timestamp as reference to compute + * first delta timestamp + */ + sensor->ts = iio_get_time_ns(iio_dev); + } + + return 0; +} + +static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private) +{ + struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private; + struct st_lsm6dsx_sensor *sensor; + int i; + + if (!hw->sip) + return IRQ_NONE; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + sensor = iio_priv(hw->iio_devs[i]); + + if (sensor->sip > 0) { + s64 timestamp; + + timestamp = iio_get_time_ns(hw->iio_devs[i]); + sensor->delta_ts = timestamp - sensor->ts; + sensor->ts = timestamp; + } + } + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) +{ + struct st_lsm6dsx_hw *hw = (struct st_lsm6dsx_hw *)private; + int count; + + mutex_lock(&hw->fifo_lock); + count = st_lsm6dsx_read_fifo(hw); + mutex_unlock(&hw->fifo_lock); + + return !count ? IRQ_NONE : IRQ_HANDLED; +} + +static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev) +{ + return st_lsm6dsx_update_fifo(iio_dev, true); +} + +static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev) +{ + return st_lsm6dsx_update_fifo(iio_dev, false); +} + +static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = { + .preenable = st_lsm6dsx_buffer_preenable, + .postdisable = st_lsm6dsx_buffer_postdisable, +}; + +int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) +{ + struct iio_buffer *buffer; + unsigned long irq_type; + int i, 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\n", irq_type); + return -EINVAL; + } + + err = devm_request_threaded_irq(hw->dev, hw->irq, + st_lsm6dsx_handler_irq, + st_lsm6dsx_handler_thread, + irq_type | IRQF_ONESHOT, + "lsm6dsx", hw); + if (err) { + dev_err(hw->dev, "failed to request trigger irq %d\n", + hw->irq); + return err; + } + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + buffer = devm_iio_kfifo_allocate(hw->dev); + if (!buffer) + return -ENOMEM; + + iio_device_attach_buffer(hw->iio_devs[i], buffer); + hw->iio_devs[i]->modes |= INDIO_BUFFER_SOFTWARE; + hw->iio_devs[i]->setup_ops = &st_lsm6dsx_buffer_ops; + } + + return 0; +} diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c new file mode 100644 index 000000000000..01e002c7f990 --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -0,0 +1,673 @@ +/* + * STMicroelectronics st_lsm6dsx sensor driver + * + * The ST LSM6DSx IMU MEMS series consists of 3D digital accelerometer + * and 3D digital gyroscope system-in-package with a digital I2C/SPI serial + * interface standard output. + * LSM6DSx IMU MEMS series has a dynamic user-selectable full-scale + * acceleration range of +-2/+-4/+-8/+-16 g and an angular rate range of + * +-125/+-245/+-500/+-1000/+-2000 dps + * LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer + * allowing dynamic batching of sensor data. + * + * Supported sensors: + * - LSM6DS3: + * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 + * - FIFO size: 8KB + * + * - LSM6DSM: + * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 + * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 + * - FIFO size: 4KB + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +#define ST_LSM6DSX_REG_ACC_DEC_MASK GENMASK(2, 0) +#define ST_LSM6DSX_REG_GYRO_DEC_MASK GENMASK(5, 3) +#define ST_LSM6DSX_REG_INT1_ADDR 0x0d +#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK BIT(3) +#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f +#define ST_LSM6DSX_REG_RESET_ADDR 0x12 +#define ST_LSM6DSX_REG_RESET_MASK BIT(0) +#define ST_LSM6DSX_REG_BDU_ADDR 0x12 +#define ST_LSM6DSX_REG_BDU_MASK BIT(6) +#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13 +#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK BIT(5) +#define ST_LSM6DSX_REG_ROUNDING_ADDR 0x16 +#define ST_LSM6DSX_REG_ROUNDING_MASK BIT(2) +#define ST_LSM6DSX_REG_LIR_ADDR 0x58 +#define ST_LSM6DSX_REG_LIR_MASK BIT(0) + +#define ST_LSM6DSX_REG_ACC_ODR_ADDR 0x10 +#define ST_LSM6DSX_REG_ACC_ODR_MASK GENMASK(7, 4) +#define ST_LSM6DSX_REG_ACC_FS_ADDR 0x10 +#define ST_LSM6DSX_REG_ACC_FS_MASK GENMASK(3, 2) +#define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR 0x28 +#define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR 0x2a +#define ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR 0x2c + +#define ST_LSM6DSX_REG_GYRO_ODR_ADDR 0x11 +#define ST_LSM6DSX_REG_GYRO_ODR_MASK GENMASK(7, 4) +#define ST_LSM6DSX_REG_GYRO_FS_ADDR 0x11 +#define ST_LSM6DSX_REG_GYRO_FS_MASK GENMASK(3, 2) +#define ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR 0x22 +#define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR 0x24 +#define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR 0x26 + +#define ST_LSM6DS3_WHOAMI 0x69 +#define ST_LSM6DSM_WHOAMI 0x6a + +#define ST_LSM6DS3_MAX_FIFO_SIZE 8192 +#define ST_LSM6DSM_MAX_FIFO_SIZE 4096 + +#define ST_LSM6DSX_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61) +#define ST_LSM6DSX_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122) +#define ST_LSM6DSX_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244) +#define ST_LSM6DSX_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488) + +#define ST_LSM6DSX_GYRO_FS_245_GAIN IIO_DEGREE_TO_RAD(4375) +#define ST_LSM6DSX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(8750) +#define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(17500) +#define ST_LSM6DSX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000) + +struct st_lsm6dsx_odr { + u16 hz; + u8 val; +}; + +#define ST_LSM6DSX_ODR_LIST_SIZE 6 +struct st_lsm6dsx_odr_table_entry { + struct st_lsm6dsx_reg reg; + struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE]; +}; + +static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = ST_LSM6DSX_REG_ACC_ODR_ADDR, + .mask = ST_LSM6DSX_REG_ACC_ODR_MASK, + }, + .odr_avl[0] = { 13, 0x01 }, + .odr_avl[1] = { 26, 0x02 }, + .odr_avl[2] = { 52, 0x03 }, + .odr_avl[3] = { 104, 0x04 }, + .odr_avl[4] = { 208, 0x05 }, + .odr_avl[5] = { 416, 0x06 }, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = ST_LSM6DSX_REG_GYRO_ODR_ADDR, + .mask = ST_LSM6DSX_REG_GYRO_ODR_MASK, + }, + .odr_avl[0] = { 13, 0x01 }, + .odr_avl[1] = { 26, 0x02 }, + .odr_avl[2] = { 52, 0x03 }, + .odr_avl[3] = { 104, 0x04 }, + .odr_avl[4] = { 208, 0x05 }, + .odr_avl[5] = { 416, 0x06 }, + } +}; + +struct st_lsm6dsx_fs { + u32 gain; + u8 val; +}; + +#define ST_LSM6DSX_FS_LIST_SIZE 4 +struct st_lsm6dsx_fs_table_entry { + struct st_lsm6dsx_reg reg; + struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE]; +}; + +static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = { + [ST_LSM6DSX_ID_ACC] = { + .reg = { + .addr = ST_LSM6DSX_REG_ACC_FS_ADDR, + .mask = ST_LSM6DSX_REG_ACC_FS_MASK, + }, + .fs_avl[0] = { ST_LSM6DSX_ACC_FS_2G_GAIN, 0x0 }, + .fs_avl[1] = { ST_LSM6DSX_ACC_FS_4G_GAIN, 0x2 }, + .fs_avl[2] = { ST_LSM6DSX_ACC_FS_8G_GAIN, 0x3 }, + .fs_avl[3] = { ST_LSM6DSX_ACC_FS_16G_GAIN, 0x1 }, + }, + [ST_LSM6DSX_ID_GYRO] = { + .reg = { + .addr = ST_LSM6DSX_REG_GYRO_FS_ADDR, + .mask = ST_LSM6DSX_REG_GYRO_FS_MASK, + }, + .fs_avl[0] = { ST_LSM6DSX_GYRO_FS_245_GAIN, 0x0 }, + .fs_avl[1] = { ST_LSM6DSX_GYRO_FS_500_GAIN, 0x1 }, + .fs_avl[2] = { ST_LSM6DSX_GYRO_FS_1000_GAIN, 0x2 }, + .fs_avl[3] = { ST_LSM6DSX_GYRO_FS_2000_GAIN, 0x3 }, + } +}; + +static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { + { + .wai = ST_LSM6DS3_WHOAMI, + .max_fifo_size = ST_LSM6DS3_MAX_FIFO_SIZE, + .id = ST_LSM6DS3_ID, + }, + { + .wai = ST_LSM6DSM_WHOAMI, + .max_fifo_size = ST_LSM6DSM_MAX_FIFO_SIZE, + .id = ST_LSM6DSM_ID, + }, +}; + +#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \ +{ \ + .type = chan_type, \ + .address = addr, \ + .modified = 1, \ + .channel2 = mod, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_SCALE), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = scan_idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_LE, \ + }, \ +} + +static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = { + ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR, + IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR, + IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR, + IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = { + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR, + IIO_MOD_X, 0), + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR, + IIO_MOD_Y, 1), + ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR, + IIO_MOD_Z, 2), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +int st_lsm6dsx_write_with_mask(struct st_lsm6dsx_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 out; + } + + data = (data & ~mask) | ((val << __ffs(mask)) & 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); + +out: + mutex_unlock(&hw->lock); + + return err; +} + +static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id) +{ + int err, i; + u8 data; + + for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) { + if (id == st_lsm6dsx_sensor_settings[i].id) + break; + } + + if (i == ARRAY_SIZE(st_lsm6dsx_sensor_settings)) { + dev_err(hw->dev, "unsupported hw id [%02x]\n", id); + return -ENODEV; + } + + err = hw->tf->read(hw->dev, ST_LSM6DSX_REG_WHOAMI_ADDR, sizeof(data), + &data); + if (err < 0) { + dev_err(hw->dev, "failed to read whoami register\n"); + return err; + } + + if (data != st_lsm6dsx_sensor_settings[i].wai) { + dev_err(hw->dev, "unsupported whoami [%02x]\n", data); + return -ENODEV; + } + + hw->settings = &st_lsm6dsx_sensor_settings[i]; + + return 0; +} + +static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor, + u32 gain) +{ + enum st_lsm6dsx_sensor_id id = sensor->id; + int i, err; + u8 val; + + for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) + if (st_lsm6dsx_fs_table[id].fs_avl[i].gain == gain) + break; + + if (i == ST_LSM6DSX_FS_LIST_SIZE) + return -EINVAL; + + val = st_lsm6dsx_fs_table[id].fs_avl[i].val; + err = st_lsm6dsx_write_with_mask(sensor->hw, + st_lsm6dsx_fs_table[id].reg.addr, + st_lsm6dsx_fs_table[id].reg.mask, + val); + if (err < 0) + return err; + + sensor->gain = gain; + + return 0; +} + +static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr) +{ + enum st_lsm6dsx_sensor_id id = sensor->id; + int i, err; + u8 val; + + for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) + if (st_lsm6dsx_odr_table[id].odr_avl[i].hz == odr) + break; + + if (i == ST_LSM6DSX_ODR_LIST_SIZE) + return -EINVAL; + + val = st_lsm6dsx_odr_table[id].odr_avl[i].val; + err = st_lsm6dsx_write_with_mask(sensor->hw, + st_lsm6dsx_odr_table[id].reg.addr, + st_lsm6dsx_odr_table[id].reg.mask, + val); + if (err < 0) + return err; + + sensor->odr = odr; + + return 0; +} + +int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor) +{ + int err; + + err = st_lsm6dsx_set_odr(sensor, sensor->odr); + if (err < 0) + return err; + + sensor->hw->enable_mask |= BIT(sensor->id); + + return 0; +} + +int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor) +{ + enum st_lsm6dsx_sensor_id id = sensor->id; + int err; + + err = st_lsm6dsx_write_with_mask(sensor->hw, + st_lsm6dsx_odr_table[id].reg.addr, + st_lsm6dsx_odr_table[id].reg.mask, 0); + if (err < 0) + return err; + + sensor->hw->enable_mask &= ~BIT(id); + + return 0; +} + +static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor, + u8 addr, int *val) +{ + int err, delay; + __le16 data; + + err = st_lsm6dsx_sensor_enable(sensor); + if (err < 0) + return err; + + delay = 1000000 / sensor->odr; + usleep_range(delay, 2 * delay); + + err = sensor->hw->tf->read(sensor->hw->dev, addr, sizeof(data), + (u8 *)&data); + if (err < 0) + return err; + + st_lsm6dsx_sensor_disable(sensor); + + *val = (s16)data; + + return IIO_VAL_INT; +} + +static int st_lsm6dsx_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, + int *val, int *val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(iio_dev); + if (ret) + break; + + ret = st_lsm6dsx_read_oneshot(sensor, ch->address, val); + iio_device_release_direct_mode(iio_dev); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + *val = sensor->odr; + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_SCALE: + *val = 0; + *val2 = sensor->gain; + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + + return ret; +} + +static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + int err; + + err = iio_device_claim_direct_mode(iio_dev); + if (err) + return err; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + err = st_lsm6dsx_set_full_scale(sensor, val2); + break; + case IIO_CHAN_INFO_SAMP_FREQ: + err = st_lsm6dsx_set_odr(sensor, val); + break; + default: + err = -EINVAL; + break; + } + + iio_device_release_direct_mode(iio_dev); + + return err; +} + +static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev); + struct st_lsm6dsx_hw *hw = sensor->hw; + int err, max_fifo_len; + + max_fifo_len = hw->settings->max_fifo_size / ST_LSM6DSX_SAMPLE_SIZE; + if (val < 1 || val > max_fifo_len) + return -EINVAL; + + err = st_lsm6dsx_update_watermark(sensor, val); + if (err < 0) + return err; + + sensor->watermark = val; + + return 0; +} + +static ssize_t +st_lsm6dsx_sysfs_sampling_frequency_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + enum st_lsm6dsx_sensor_id id = sensor->id; + int i, len = 0; + + for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", + st_lsm6dsx_odr_table[id].odr_avl[i].hz); + buf[len - 1] = '\n'; + + return len; +} + +static ssize_t st_lsm6dsx_sysfs_scale_avail(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev)); + enum st_lsm6dsx_sensor_id id = sensor->id; + int i, len = 0; + + for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) + len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ", + st_lsm6dsx_fs_table[id].fs_avl[i].gain); + buf[len - 1] = '\n'; + + return len; +} + +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_sysfs_sampling_frequency_avail); +static IIO_DEVICE_ATTR(in_accel_scale_available, 0444, + st_lsm6dsx_sysfs_scale_avail, NULL, 0); +static IIO_DEVICE_ATTR(in_anglvel_scale_available, 0444, + st_lsm6dsx_sysfs_scale_avail, NULL, 0); + +static struct attribute *st_lsm6dsx_acc_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_accel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_lsm6dsx_acc_attribute_group = { + .attrs = st_lsm6dsx_acc_attributes, +}; + +static const struct iio_info st_lsm6dsx_acc_info = { + .driver_module = THIS_MODULE, + .attrs = &st_lsm6dsx_acc_attribute_group, + .read_raw = st_lsm6dsx_read_raw, + .write_raw = st_lsm6dsx_write_raw, + .hwfifo_set_watermark = st_lsm6dsx_set_watermark, +}; + +static struct attribute *st_lsm6dsx_gyro_attributes[] = { + &iio_dev_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_in_anglvel_scale_available.dev_attr.attr, + NULL, +}; + +static const struct attribute_group st_lsm6dsx_gyro_attribute_group = { + .attrs = st_lsm6dsx_gyro_attributes, +}; + +static const struct iio_info st_lsm6dsx_gyro_info = { + .driver_module = THIS_MODULE, + .attrs = &st_lsm6dsx_gyro_attribute_group, + .read_raw = st_lsm6dsx_read_raw, + .write_raw = st_lsm6dsx_write_raw, + .hwfifo_set_watermark = st_lsm6dsx_set_watermark, +}; + +static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0}; + +static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) +{ + int err; + u8 data; + + data = ST_LSM6DSX_REG_RESET_MASK; + err = hw->tf->write(hw->dev, ST_LSM6DSX_REG_RESET_ADDR, sizeof(data), + &data); + if (err < 0) + return err; + + msleep(200); + + /* latch interrupts */ + err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_LIR_ADDR, + ST_LSM6DSX_REG_LIR_MASK, 1); + if (err < 0) + return err; + + /* enable Block Data Update */ + err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_BDU_ADDR, + ST_LSM6DSX_REG_BDU_MASK, 1); + if (err < 0) + return err; + + err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_ROUNDING_ADDR, + ST_LSM6DSX_REG_ROUNDING_MASK, 1); + if (err < 0) + return err; + + /* enable FIFO watermak interrupt */ + err = st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT1_ADDR, + ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, 1); + if (err < 0) + return err; + + /* redirect INT2 on INT1 */ + return st_lsm6dsx_write_with_mask(hw, ST_LSM6DSX_REG_INT2_ON_INT1_ADDR, + ST_LSM6DSX_REG_INT2_ON_INT1_MASK, 1); +} + +static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw, + enum st_lsm6dsx_sensor_id id) +{ + struct st_lsm6dsx_sensor *sensor; + struct iio_dev *iio_dev; + + iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor)); + if (!iio_dev) + return NULL; + + iio_dev->modes = INDIO_DIRECT_MODE; + iio_dev->dev.parent = hw->dev; + iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks; + + sensor = iio_priv(iio_dev); + sensor->id = id; + sensor->hw = hw; + sensor->odr = st_lsm6dsx_odr_table[id].odr_avl[0].hz; + sensor->gain = st_lsm6dsx_fs_table[id].fs_avl[0].gain; + sensor->watermark = 1; + + switch (id) { + case ST_LSM6DSX_ID_ACC: + iio_dev->channels = st_lsm6dsx_acc_channels; + iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_acc_channels); + iio_dev->name = "lsm6dsx_accel"; + iio_dev->info = &st_lsm6dsx_acc_info; + + sensor->decimator_mask = ST_LSM6DSX_REG_ACC_DEC_MASK; + break; + case ST_LSM6DSX_ID_GYRO: + iio_dev->channels = st_lsm6dsx_gyro_channels; + iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels); + iio_dev->name = "lsm6dsx_gyro"; + iio_dev->info = &st_lsm6dsx_gyro_info; + + sensor->decimator_mask = ST_LSM6DSX_REG_GYRO_DEC_MASK; + break; + default: + return NULL; + } + + return iio_dev; +} + +int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, + const struct st_lsm6dsx_transfer_function *tf_ops) +{ + struct st_lsm6dsx_hw *hw; + int i, err; + + hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL); + if (!hw) + return -ENOMEM; + + dev_set_drvdata(dev, (void *)hw); + + mutex_init(&hw->lock); + mutex_init(&hw->fifo_lock); + + hw->dev = dev; + hw->irq = irq; + hw->tf = tf_ops; + + err = st_lsm6dsx_check_whoami(hw, hw_id); + if (err < 0) + return err; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i); + if (!hw->iio_devs[i]) + return -ENOMEM; + } + + err = st_lsm6dsx_init_device(hw); + if (err < 0) + return err; + + if (hw->irq > 0) { + err = st_lsm6dsx_fifo_setup(hw); + if (err < 0) + return err; + } + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + err = devm_iio_device_register(hw->dev, hw->iio_devs[i]); + if (err) + return err; + } + + return 0; +} +EXPORT_SYMBOL(st_lsm6dsx_probe); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c new file mode 100644 index 000000000000..ea3041186e1e --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -0,0 +1,101 @@ +/* + * STMicroelectronics st_lsm6dsx i2c driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +static int st_lsm6dsx_i2c_read(struct device *dev, u8 addr, int len, u8 *data) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msg[2]; + + 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 st_lsm6dsx_i2c_write(struct device *dev, u8 addr, int len, u8 *data) +{ + struct i2c_client *client = to_i2c_client(dev); + struct i2c_msg msg; + u8 send[len + 1]; + + 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 st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = { + .read = st_lsm6dsx_i2c_read, + .write = st_lsm6dsx_i2c_write, +}; + +static int st_lsm6dsx_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + return st_lsm6dsx_probe(&client->dev, client->irq, + (int)id->driver_data, + &st_lsm6dsx_transfer_fn); +} + +static const struct of_device_id st_lsm6dsx_i2c_of_match[] = { + { + .compatible = "st,lsm6ds3", + .data = (void *)ST_LSM6DS3_ID, + }, + { + .compatible = "st,lsm6dsm", + .data = (void *)ST_LSM6DSM_ID, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match); + +static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = { + { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, + { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, + {}, +}; +MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table); + +static struct i2c_driver st_lsm6dsx_driver = { + .driver = { + .name = "st_lsm6dsx_i2c", + .of_match_table = of_match_ptr(st_lsm6dsx_i2c_of_match), + }, + .probe = st_lsm6dsx_i2c_probe, + .id_table = st_lsm6dsx_i2c_id_table, +}; +module_i2c_driver(st_lsm6dsx_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c new file mode 100644 index 000000000000..fbe72470ed1e --- /dev/null +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -0,0 +1,118 @@ +/* + * STMicroelectronics st_lsm6dsx spi driver + * + * Copyright 2016 STMicroelectronics Inc. + * + * Lorenzo Bianconi + * Denis Ciocca + * + * Licensed under the GPL-2. + */ + +#include +#include +#include +#include +#include + +#include "st_lsm6dsx.h" + +#define SENSORS_SPI_READ BIT(7) + +static int st_lsm6dsx_spi_read(struct device *dev, u8 addr, int len, + u8 *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct st_lsm6dsx_hw *hw = spi_get_drvdata(spi); + int err; + + 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, + } + }; + + 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 st_lsm6dsx_spi_write(struct device *dev, u8 addr, int len, + u8 *data) +{ + struct st_lsm6dsx_hw *hw; + struct spi_device *spi; + + if (len >= ST_LSM6DSX_TX_MAX_LENGTH) + return -ENOMEM; + + spi = to_spi_device(dev); + hw = spi_get_drvdata(spi); + + hw->tb.tx_buf[0] = addr; + memcpy(&hw->tb.tx_buf[1], data, len); + + return spi_write(spi, hw->tb.tx_buf, len + 1); +} + +static const struct st_lsm6dsx_transfer_function st_lsm6dsx_transfer_fn = { + .read = st_lsm6dsx_spi_read, + .write = st_lsm6dsx_spi_write, +}; + +static int st_lsm6dsx_spi_probe(struct spi_device *spi) +{ + const struct spi_device_id *id = spi_get_device_id(spi); + + return st_lsm6dsx_probe(&spi->dev, spi->irq, + (int)id->driver_data, + &st_lsm6dsx_transfer_fn); +} + +static const struct of_device_id st_lsm6dsx_spi_of_match[] = { + { + .compatible = "st,lsm6ds3", + .data = (void *)ST_LSM6DS3_ID, + }, + { + .compatible = "st,lsm6dsm", + .data = (void *)ST_LSM6DSM_ID, + }, + {}, +}; +MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match); + +static const struct spi_device_id st_lsm6dsx_spi_id_table[] = { + { ST_LSM6DS3_DEV_NAME, ST_LSM6DS3_ID }, + { ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID }, + {}, +}; +MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table); + +static struct spi_driver st_lsm6dsx_driver = { + .driver = { + .name = "st_lsm6dsx_spi", + .of_match_table = of_match_ptr(st_lsm6dsx_spi_of_match), + }, + .probe = st_lsm6dsx_spi_probe, + .id_table = st_lsm6dsx_spi_id_table, +}; +module_spi_driver(st_lsm6dsx_driver); + +MODULE_AUTHOR("Lorenzo Bianconi "); +MODULE_AUTHOR("Denis Ciocca "); +MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx spi driver"); +MODULE_LICENSE("GPL v2"); From 53b36426d0505f26a28e12c2582e9d35c48864e4 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 10 Jan 2017 22:55:19 +0100 Subject: [PATCH 04/42] Documentation: dt: iio: add st_lsm6dsx sensor device binding Acked-by: Rob Herring Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- .../bindings/iio/imu/st_lsm6dsx.txt | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt diff --git a/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt new file mode 100644 index 000000000000..ed3cdac00f13 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/imu/st_lsm6dsx.txt @@ -0,0 +1,24 @@ +* ST_LSM6DSx driver for STM 6-axis (acc + gyro) imu Mems sensors + +Required properties: +- compatible: must be one of: + "st,lsm6ds3" + "st,lsm6dsm" +- reg: i2c address of the sensor / spi cs line + +Optional properties: +- interrupt-parent: should be the phandle for the interrupt controller +- interrupts: interrupt mapping for IRQ. It should be configured with + flags IRQ_TYPE_LEVEL_HIGH or IRQ_TYPE_EDGE_RISING. + + Refer to interrupt-controller/interrupts.txt for generic interrupt + client node bindings. + +Example: + +lsm6dsm@6b { + compatible = "st,lsm6dsm"; + reg = <0x6b>; + interrupt-parent = <&gpio0>; + interrupts = <0 IRQ_TYPE_EDGE_RISING>; +}; From a9c99c76f5b96d68f1d5a293d984c74896d3c024 Mon Sep 17 00:00:00 2001 From: Enric Balletbo i Serra Date: Wed, 11 Jan 2017 16:51:38 +0100 Subject: [PATCH 05/42] iio: Documentation: Add proximity unit To follow iio guidelines Where possible we stick to the raw SI unit, so specify meters for proximity. Signed-off-by: Enric Balletbo i Serra Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-bus-iio b/Documentation/ABI/testing/sysfs-bus-iio index 8ec362bd5948..530809ccfacf 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio +++ b/Documentation/ABI/testing/sysfs-bus-iio @@ -1255,7 +1255,8 @@ Description: reflectivity of infrared or ultrasound emitted. Often these sensors are unit less and as such conversion to SI units is not possible. Higher proximity measurements - indicate closer objects, and vice versa. + indicate closer objects, and vice versa. Units after + application of scale and offset are meters. What: /sys/.../iio:deviceX/in_illuminance_input What: /sys/.../iio:deviceX/in_illuminance_raw From e2eb179c0031b8cd172d8a7869451ff57184388b Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Wed, 11 Jan 2017 17:49:33 +0000 Subject: [PATCH 06/42] iio: magnetometer: ak8974: remove redundant zero timeout check At the end of the delay loop timeout will always be zero and hence the check for !timeout will always be true. Remove the redundant check and the redundant return 0 at the end of the function. Fixes CoverityScan CID#1357168 ("Logically dead code") Signed-off-by: Colin Ian King Reviewed-by: Linus Walleij Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/ak8974.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index ce09d771c1fb..6dd8cbd7ce95 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -278,13 +278,9 @@ static int ak8974_await_drdy(struct ak8974 *ak8974) if (val & AK8974_STATUS_DRDY) return 0; } while (--timeout); - if (!timeout) { - dev_err(&ak8974->i2c->dev, - "timeout waiting for DRDY\n"); - return -ETIMEDOUT; - } - return 0; + dev_err(&ak8974->i2c->dev, "timeout waiting for DRDY\n"); + return -ETIMEDOUT; } static int ak8974_getresult(struct ak8974 *ak8974, __le16 *result) From 0287c8d24bd70dc4f2d07829cfc1839deca76416 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 11 Jan 2017 11:52:49 -0600 Subject: [PATCH 07/42] DT/bindings: Add bindings for TI ADS7950 A/DC chips This adds device tree bindings for the TI ADS7950 family of A/DC chips. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- .../bindings/iio/adc/ti-ads7950.txt | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt diff --git a/Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt b/Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt new file mode 100644 index 000000000000..e77a6f7e1001 --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/ti-ads7950.txt @@ -0,0 +1,23 @@ +* Texas Instruments ADS7950 family of A/DC chips + +Required properties: + - compatible: Must be one of "ti,ads7950", "ti,ads7951", "ti,ads7952", + "ti,ads7953", "ti,ads7954", "ti,ads7955", "ti,ads7956", "ti,ads7957", + "ti,ads7958", "ti,ads7959", "ti,ads7960", or "ti,ads7961" + - reg: SPI chip select number for the device + - #io-channel-cells: Must be 1 as per ../iio-bindings.txt + - vref-supply: phandle to a regulator node that supplies the 2.5V or 5V + reference voltage + +Recommended properties: + - spi-max-frequency: Definition as per + Documentation/devicetree/bindings/spi/spi-bus.txt + +Example: +adc@0 { + compatible = "ti,ads7957"; + reg = <0>; + #io-channel-cells = <1>; + vref-supply = <&refin_supply>; + spi-max-frequency = <10000000>; +}; From 2b84f4ded44c77ead4e6e0abbb85e053c3dff5aa Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 11 Jan 2017 11:52:50 -0600 Subject: [PATCH 08/42] iio: adc: ti-ads7950: Drop "ti-" prefix from module name This drops the "ti-" prefix from the module name. It makes the module name consistent with other iio ti-ads* drivers and it makes the driver work with device tree (the spi subsystem drops the "ti," prefix when matching compatible strings from device tree). Tested working on LEGO MINDSTORMS EV3 with the following device tree node: adc@3 { compatible = "ti,ads7957"; reg = <3>; #io-channel-cells = <1>; spi-max-frequency = <10000000>; vref-supply = <&adc_ref>; }; Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index 0330361b8ed5..b587fa6c513f 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -459,25 +459,25 @@ static int ti_ads7950_remove(struct spi_device *spi) } static const struct spi_device_id ti_ads7950_id[] = { - {"ti-ads7950", TI_ADS7950}, - {"ti-ads7951", TI_ADS7951}, - {"ti-ads7952", TI_ADS7952}, - {"ti-ads7953", TI_ADS7953}, - {"ti-ads7954", TI_ADS7954}, - {"ti-ads7955", TI_ADS7955}, - {"ti-ads7956", TI_ADS7956}, - {"ti-ads7957", TI_ADS7957}, - {"ti-ads7958", TI_ADS7958}, - {"ti-ads7959", TI_ADS7959}, - {"ti-ads7960", TI_ADS7960}, - {"ti-ads7961", TI_ADS7961}, + { "ads7950", TI_ADS7950 }, + { "ads7951", TI_ADS7951 }, + { "ads7952", TI_ADS7952 }, + { "ads7953", TI_ADS7953 }, + { "ads7954", TI_ADS7954 }, + { "ads7955", TI_ADS7955 }, + { "ads7956", TI_ADS7956 }, + { "ads7957", TI_ADS7957 }, + { "ads7958", TI_ADS7958 }, + { "ads7959", TI_ADS7959 }, + { "ads7960", TI_ADS7960 }, + { "ads7961", TI_ADS7961 }, { } }; MODULE_DEVICE_TABLE(spi, ti_ads7950_id); static struct spi_driver ti_ads7950_driver = { .driver = { - .name = "ti-ads7950", + .name = "ads7950", }, .probe = ti_ads7950_probe, .remove = ti_ads7950_remove, From 0bf1a2aac6c5ea30eca6d99791037a7b7abd8d76 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Wed, 11 Jan 2017 11:52:51 -0600 Subject: [PATCH 09/42] iio: adc: ti-ads7950: Change regulator matching string to "vref" This changes the reference voltage regulator matching string from "refin" to "vref". This is to be consistent with other A/DC chips that also use "vref-supply" in their device tree bindings. Signed-off-by: David Lechner Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads7950.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c index b587fa6c513f..16a06633332c 100644 --- a/drivers/iio/adc/ti-ads7950.c +++ b/drivers/iio/adc/ti-ads7950.c @@ -411,15 +411,15 @@ static int ti_ads7950_probe(struct spi_device *spi) spi_message_init_with_transfers(&st->scan_single_msg, st->scan_single_xfer, 3); - st->reg = devm_regulator_get(&spi->dev, "refin"); + st->reg = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(st->reg)) { - dev_err(&spi->dev, "Failed get get regulator \"refin\"\n"); + dev_err(&spi->dev, "Failed get get regulator \"vref\"\n"); return PTR_ERR(st->reg); } ret = regulator_enable(st->reg); if (ret) { - dev_err(&spi->dev, "Failed to enable regulator \"refin\"\n"); + dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n"); return ret; } From 80dea21f95a4672cce545f48dc2ca500b69a2584 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Fri, 20 Jan 2017 12:39:32 -0800 Subject: [PATCH 10/42] iio: magnetometer: mag3110: claim direct mode during raw writes Driver was checking for direct mode but not locking it. Use claim/release helper functions to guarantee the device stays in direct mode during raw writes. Signed-off-by: Alison Schofield Acked-by: Peter Meerwald-Stadler Signed-off-by: Jonathan Cameron --- drivers/iio/magnetometer/mag3110.c | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c index f2b3bd7bf862..b4f643fb3b1e 100644 --- a/drivers/iio/magnetometer/mag3110.c +++ b/drivers/iio/magnetometer/mag3110.c @@ -222,29 +222,39 @@ static int mag3110_write_raw(struct iio_dev *indio_dev, int val, int val2, long mask) { struct mag3110_data *data = iio_priv(indio_dev); - int rate; + int rate, 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: rate = mag3110_get_samp_freq_index(data, val, val2); - if (rate < 0) - return -EINVAL; + if (rate < 0) { + ret = -EINVAL; + break; + } data->ctrl_reg1 &= ~MAG3110_CTRL_DR_MASK; data->ctrl_reg1 |= rate << MAG3110_CTRL_DR_SHIFT; - return i2c_smbus_write_byte_data(data->client, + ret = i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1, data->ctrl_reg1); + break; case IIO_CHAN_INFO_CALIBBIAS: - if (val < -10000 || val > 10000) - return -EINVAL; - return i2c_smbus_write_word_swapped(data->client, + if (val < -10000 || val > 10000) { + ret = -EINVAL; + break; + } + ret = i2c_smbus_write_word_swapped(data->client, MAG3110_OFF_X + 2 * chan->scan_index, val << 1); + break; default: - return -EINVAL; + ret = -EINVAL; + break; } + iio_device_release_direct_mode(indio_dev); + return ret; } static irqreturn_t mag3110_trigger_handler(int irq, void *p) From 6b2e7589b82ff534dd5c6d67dd83c53f13691bec Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Fri, 20 Jan 2017 14:11:30 -0800 Subject: [PATCH 11/42] iio: proximity: sx9500: claim direct mode during raw proximity reads Driver was checking for direct mode but not locking it. Use the claim/release helper functions to guarantee the device stays in direct mode during raw reads of proximity data. Signed-off-by: Alison Schofield Reviewed-by: Vlad Dogaru Signed-off-by: Jonathan Cameron --- drivers/iio/proximity/sx9500.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index 1f06282ec793..9ea147f1a50d 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -387,14 +387,18 @@ static int sx9500_read_raw(struct iio_dev *indio_dev, int *val, int *val2, long mask) { struct sx9500_data *data = iio_priv(indio_dev); + int ret; switch (chan->type) { case IIO_PROXIMITY: switch (mask) { case IIO_CHAN_INFO_RAW: - if (iio_buffer_enabled(indio_dev)) - return -EBUSY; - return sx9500_read_proximity(data, chan, val); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = sx9500_read_proximity(data, chan, val); + iio_device_release_direct_mode(indio_dev); + return ret; case IIO_CHAN_INFO_SAMP_FREQ: return sx9500_read_samp_freq(data, val, val2); default: From 3bc1abcddb24f55b9c251e03caa4f9bd22ff748b Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Fri, 20 Jan 2017 12:22:58 -0800 Subject: [PATCH 12/42] iio: pressure: ms5611: claim direct mode during oversampling changes Driver was checking for direct mode before changing oversampling ratios, but was not locking it. Use the claim/release helper functions to guarantee the device stays in direct mode while the oversampling ratios are being updated. Continue to use the drivers private state lock to protect against conflicting direct mode access of the state data. Signed-off-by: Alison Schofield Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611_core.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c index 6bd53e702667..2a77a2f15752 100644 --- a/drivers/iio/pressure/ms5611_core.c +++ b/drivers/iio/pressure/ms5611_core.c @@ -308,6 +308,7 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, { struct ms5611_state *st = iio_priv(indio_dev); const struct ms5611_osr *osr = NULL; + int ret; if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO) return -EINVAL; @@ -321,12 +322,11 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, if (!osr) return -EINVAL; - mutex_lock(&st->lock); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&st->lock); - return -EBUSY; - } + mutex_lock(&st->lock); if (chan->type == IIO_TEMP) st->temp_osr = osr; @@ -334,6 +334,8 @@ static int ms5611_write_raw(struct iio_dev *indio_dev, st->pressure_osr = osr; mutex_unlock(&st->lock); + iio_device_release_direct_mode(indio_dev); + return 0; } From d1242acc566a9288cbca441be1a7358c0316197e Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 20 Jan 2017 17:25:15 +0100 Subject: [PATCH 13/42] iio: light: cm3605: mark PM functions as __maybe_unused When CONFIG_PM_SLEEP is disabled, we get a harmless warning drivers/iio/light/cm3605.c:292:12: error: 'cm3605_pm_resume' defined but not used [-Werror=unused-function] drivers/iio/light/cm3605.c:281:12: error: 'cm3605_pm_suspend' defined but not used [-Werror=unused-function] Marking the functions as possibly unused avoids the warning without needing to add an #ifdef. Fixes: 8afa505c1230 ("iio: light: add driver for Capella CM3605") Signed-off-by: Arnd Bergmann Signed-off-by: Jonathan Cameron --- drivers/iio/light/cm3605.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c index 2d96543db1ef..980624e9ffb5 100644 --- a/drivers/iio/light/cm3605.c +++ b/drivers/iio/light/cm3605.c @@ -278,7 +278,7 @@ static int cm3605_remove(struct platform_device *pdev) return 0; } -static int cm3605_pm_suspend(struct device *dev) +static int __maybe_unused cm3605_pm_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct cm3605 *cm3605 = iio_priv(indio_dev); @@ -289,7 +289,7 @@ static int cm3605_pm_suspend(struct device *dev) return 0; } -static int cm3605_pm_resume(struct device *dev) +static int __maybe_unused cm3605_pm_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct cm3605 *cm3605 = iio_priv(indio_dev); From 2d3f956e2ea8c247ce433323ca9f764da139aae7 Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Sun, 15 Jan 2017 21:01:10 -0800 Subject: [PATCH 14/42] iio: bmi160: use variable names for sizeof() operator Replace the types with the actual variable names when using the sizeof() operator. This is kernel preferred style as it's more obvious that it is correct. Signed-off-by: Alison Schofield Signed-off-by: Jonathan Cameron --- drivers/iio/imu/bmi160/bmi160_core.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 5355507f8fa1..1e7c88bdf472 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -338,9 +338,9 @@ static int bmi160_get_data(struct bmi160_data *data, int chan_type, __le16 sample; enum bmi160_sensor_type t = bmi160_to_sensor(chan_type); - reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(__le16); + reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample); - ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(__le16)); + ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample)); if (ret < 0) return ret; @@ -405,8 +405,8 @@ static irqreturn_t bmi160_trigger_handler(int irq, void *p) for_each_set_bit(i, indio_dev->active_scan_mask, indio_dev->masklength) { - ret = regmap_bulk_read(data->regmap, base + i * sizeof(__le16), - &sample, sizeof(__le16)); + ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample), + &sample, sizeof(sample)); if (ret < 0) goto done; buf[j++] = sample; From a8e7e88df9ec1b7df2d12b6ee4ec48aee7b6aec7 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 18 Jan 2017 17:30:52 +0100 Subject: [PATCH 15/42] iio: adc: Add Maxim MAX11100 driver Add iio driver for Maxim MAX11100 single-channel ADC. Signed-off-by: Jacopo Mondi Tested-by: Marek Vasut Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 9 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/max11100.c | 192 +++++++++++++++++++++++++++++++++++++ 3 files changed, 202 insertions(+) create mode 100644 drivers/iio/adc/max11100.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 817693a14eb9..a8155221f470 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -326,6 +326,15 @@ config MAX1027 To compile this driver as a module, choose M here: the module will be called max1027. +config MAX11100 + tristate "Maxim max11100 ADC driver" + depends on SPI_MASTER + help + Say yes here to build support for Maxim max11100 SPI ADC + + To compile this driver as a module, choose M here: the module will be + called max11100. + config MAX1363 tristate "Maxim max1363 ADC driver" depends on I2C diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 260d083a2b3f..a90cdea61d02 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -32,6 +32,7 @@ obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o obj-$(CONFIG_LTC2485) += ltc2485.o obj-$(CONFIG_MAX1027) += max1027.o +obj-$(CONFIG_MAX11100) += max11100.o obj-$(CONFIG_MAX1363) += max1363.o obj-$(CONFIG_MCP320X) += mcp320x.o obj-$(CONFIG_MCP3422) += mcp3422.o diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c new file mode 100644 index 000000000000..b738ecf1b234 --- /dev/null +++ b/drivers/iio/adc/max11100.c @@ -0,0 +1,192 @@ +/* + * iio/adc/max11100.c + * Maxim max11100 ADC Driver with IIO interface + * + * Copyright (C) 2016-17 Renesas Electronics Corporation + * Copyright (C) 2016-17 Jacopo Mondi + * + * 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. + */ +#include +#include +#include +#include +#include + +#include +#include + +/* + * LSB is the ADC single digital step + * 1 LSB = (vref_mv / 2 ^ 16) + * + * LSB is used to calculate analog voltage value + * from the number of ADC steps count + * + * Ain = (count * LSB) + */ +#define MAX11100_LSB_DIV (1 << 16) + +struct max11100_state { + const struct max11100_chip_desc *desc; + struct regulator *vref_reg; + struct spi_device *spi; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + u8 buffer[3] ____cacheline_aligned; +}; + +static struct iio_chan_spec max11100_channels[] = { + { /* [0] */ + .type = IIO_VOLTAGE, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static struct max11100_chip_desc { + unsigned int num_chan; + const struct iio_chan_spec *channels; +} max11100_desc = { + .num_chan = ARRAY_SIZE(max11100_channels), + .channels = max11100_channels, +}; + +static int max11100_read_single(struct iio_dev *indio_dev, int *val) +{ + int ret; + struct max11100_state *state = iio_priv(indio_dev); + + ret = spi_read(state->spi, state->buffer, sizeof(state->buffer)); + if (ret) { + dev_err(&indio_dev->dev, "SPI transfer failed\n"); + return ret; + } + + /* the first 8 bits sent out from ADC must be 0s */ + if (state->buffer[0]) { + dev_err(&indio_dev->dev, "Invalid value: buffer[0] != 0\n"); + return -EINVAL; + } + + *val = (state->buffer[1] << 8) | state->buffer[2]; + + return 0; +} + +static int max11100_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + int ret, vref_uv; + struct max11100_state *state = iio_priv(indio_dev); + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = max11100_read_single(indio_dev, val); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + vref_uv = regulator_get_voltage(state->vref_reg); + if (vref_uv < 0) + /* dummy regulator "get_voltage" returns -EINVAL */ + return -EINVAL; + + *val = vref_uv / 1000; + *val2 = MAX11100_LSB_DIV; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static const struct iio_info max11100_info = { + .driver_module = THIS_MODULE, + .read_raw = max11100_read_raw, +}; + +static int max11100_probe(struct spi_device *spi) +{ + int ret; + struct iio_dev *indio_dev; + struct max11100_state *state; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*state)); + if (!indio_dev) + return -ENOMEM; + + spi_set_drvdata(spi, indio_dev); + + state = iio_priv(indio_dev); + state->spi = spi; + state->desc = &max11100_desc; + + indio_dev->dev.parent = &spi->dev; + indio_dev->dev.of_node = spi->dev.of_node; + indio_dev->name = "max11100"; + indio_dev->info = &max11100_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = state->desc->channels; + indio_dev->num_channels = state->desc->num_chan; + + state->vref_reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(state->vref_reg)) + return PTR_ERR(state->vref_reg); + + ret = regulator_enable(state->vref_reg); + if (ret) + return ret; + + ret = iio_device_register(indio_dev); + if (ret) + goto disable_regulator; + + return 0; + +disable_regulator: + regulator_disable(state->vref_reg); + + return ret; +} + +static int max11100_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct max11100_state *state = iio_priv(indio_dev); + + regulator_disable(state->vref_reg); + + iio_device_unregister(indio_dev); + + return 0; +} + +static const struct of_device_id max11100_ids[] = { + {.compatible = "maxim,max11100"}, + { }, +}; +MODULE_DEVICE_TABLE(of, max11100_ids); + +static struct spi_driver max11100_driver = { + .driver = { + .name = "max11100", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(max11100_ids), + }, + .probe = max11100_probe, + .remove = max11100_remove, +}; + +module_spi_driver(max11100_driver); + +MODULE_AUTHOR("Jacopo Mondi "); +MODULE_DESCRIPTION("Maxim max11100 ADC Driver"); +MODULE_LICENSE("GPL v2"); From 0dbe71c274550a4a848f0b0753337a57f9125c92 Mon Sep 17 00:00:00 2001 From: Jacopo Mondi Date: Wed, 18 Jan 2017 17:30:53 +0100 Subject: [PATCH 16/42] dt-bindings: iio: document MAX11100 ADC Add device tree bindings documentation for Maxim MAX11100 single-channel ADC Signed-off-by: Jacopo Mondi Reviewed-by: Geert Uytterhoeven Acked-by: Wolfram Sang Acked-by: Rob Herring Signed-off-by: Jonathan Cameron --- .../devicetree/bindings/iio/adc/max11100.txt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Documentation/devicetree/bindings/iio/adc/max11100.txt diff --git a/Documentation/devicetree/bindings/iio/adc/max11100.txt b/Documentation/devicetree/bindings/iio/adc/max11100.txt new file mode 100644 index 000000000000..b7f7177b8aca --- /dev/null +++ b/Documentation/devicetree/bindings/iio/adc/max11100.txt @@ -0,0 +1,18 @@ +* Maxim max11100 Analog to Digital Converter (ADC) + +Required properties: + - compatible: Should be "maxim,max11100" + - reg: the adc unit address + - vref-supply: phandle to the regulator that provides reference voltage + +Optional properties: + - spi-max-frequency: SPI maximum frequency + +Example: + +max11100: adc@0 { + compatible = "maxim,max11100"; + reg = <0>; + vref-supply = <&adc0_vref>; + spi-max-frequency = <240000>; +}; From ac2bec9d587c6a423a00c7a2d21a8a5928dfedf5 Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Mon, 16 Jan 2017 16:38:24 +0800 Subject: [PATCH 17/42] iio: adc: tlc4541: add support for TI tlc4541 adc This adds TI's tlc4541 16-bit ADC driver. Which is a single channel ADC. Supports raw and trigger buffer access. Also supports the tlc3541 14-bit device, which has not been tested. Implementation of the tlc3541 is fairly straight forward thou. Acked-by: Rob Herring Signed-off-by: Phil Reid Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 12 ++ drivers/iio/adc/Makefile | 1 + drivers/iio/adc/max11100.c | 17 +-- drivers/iio/adc/ti-tlc4541.c | 271 +++++++++++++++++++++++++++++++++++ 4 files changed, 287 insertions(+), 14 deletions(-) create mode 100644 drivers/iio/adc/ti-tlc4541.c diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index a8155221f470..0d55f73eadef 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -612,6 +612,18 @@ config TI_AM335X_ADC To compile this driver as a module, choose M here: the module will be called ti_am335x_adc. +config TI_TLC4541 + tristate "Texas Instruments TLC4541 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Texas Instruments TLC4541 / TLC3541 + ADC chips. + + This driver can also be built as a module. If so, the module will be + called ti-tlc4541. + config TWL4030_MADC tristate "TWL4030 MADC (Monitoring A/D Converter)" depends on TWL4030_CORE diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index a90cdea61d02..9475fd572e5b 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -56,6 +56,7 @@ obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o +obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o obj-$(CONFIG_VF610_ADC) += vf610_adc.o diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c index b738ecf1b234..a088cf99bfe1 100644 --- a/drivers/iio/adc/max11100.c +++ b/drivers/iio/adc/max11100.c @@ -30,7 +30,6 @@ #define MAX11100_LSB_DIV (1 << 16) struct max11100_state { - const struct max11100_chip_desc *desc; struct regulator *vref_reg; struct spi_device *spi; @@ -49,14 +48,6 @@ static struct iio_chan_spec max11100_channels[] = { }, }; -static struct max11100_chip_desc { - unsigned int num_chan; - const struct iio_chan_spec *channels; -} max11100_desc = { - .num_chan = ARRAY_SIZE(max11100_channels), - .channels = max11100_channels, -}; - static int max11100_read_single(struct iio_dev *indio_dev, int *val) { int ret; @@ -127,15 +118,14 @@ static int max11100_probe(struct spi_device *spi) state = iio_priv(indio_dev); state->spi = spi; - state->desc = &max11100_desc; indio_dev->dev.parent = &spi->dev; indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = "max11100"; indio_dev->info = &max11100_info; indio_dev->modes = INDIO_DIRECT_MODE; - indio_dev->channels = state->desc->channels; - indio_dev->num_channels = state->desc->num_chan; + indio_dev->channels = max11100_channels, + indio_dev->num_channels = ARRAY_SIZE(max11100_channels), state->vref_reg = devm_regulator_get(&spi->dev, "vref"); if (IS_ERR(state->vref_reg)) @@ -162,9 +152,8 @@ static int max11100_remove(struct spi_device *spi) struct iio_dev *indio_dev = spi_get_drvdata(spi); struct max11100_state *state = iio_priv(indio_dev); - regulator_disable(state->vref_reg); - iio_device_unregister(indio_dev); + regulator_disable(state->vref_reg); return 0; } diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c new file mode 100644 index 000000000000..78d91a069ea4 --- /dev/null +++ b/drivers/iio/adc/ti-tlc4541.c @@ -0,0 +1,271 @@ +/* + * TI tlc4541 ADC Driver + * + * Copyright (C) 2017 Phil Reid + * + * Datasheets can be found here: + * http://www.ti.com/lit/gpn/tlc3541 + * http://www.ti.com/lit/gpn/tlc4541 + * + * 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. + * + * The tlc4541 requires 24 clock cycles to start a transfer. + * Conversion then takes 2.94us to complete before data is ready + * Data is returned MSB first. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct tlc4541_state { + struct spi_device *spi; + struct regulator *reg; + struct spi_transfer scan_single_xfer[3]; + struct spi_message scan_single_msg; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + * 2 bytes data + 6 bytes padding + 8 bytes timestamp when + * call iio_push_to_buffers_with_timestamp. + */ + __be16 rx_buf[8] ____cacheline_aligned; +}; + +struct tlc4541_chip_info { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +enum tlc4541_id { + TLC3541, + TLC4541, +}; + +#define TLC4541_V_CHAN(bits, bitshift) { \ + .type = IIO_VOLTAGE, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \ + .scan_type = { \ + .sign = 'u', \ + .realbits = (bits), \ + .storagebits = 16, \ + .shift = (bitshift), \ + .endianness = IIO_BE, \ + }, \ + } + +#define DECLARE_TLC4541_CHANNELS(name, bits, bitshift) \ +const struct iio_chan_spec name ## _channels[] = { \ + TLC4541_V_CHAN(bits, bitshift), \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ +} + +static DECLARE_TLC4541_CHANNELS(tlc3541, 14, 2); +static DECLARE_TLC4541_CHANNELS(tlc4541, 16, 0); + +static const struct tlc4541_chip_info tlc4541_chip_info[] = { + [TLC3541] = { + .channels = tlc3541_channels, + .num_channels = ARRAY_SIZE(tlc3541_channels), + }, + [TLC4541] = { + .channels = tlc4541_channels, + .num_channels = ARRAY_SIZE(tlc4541_channels), + }, +}; + +static irqreturn_t tlc4541_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct tlc4541_state *st = iio_priv(indio_dev); + int ret; + + ret = spi_sync(st->spi, &st->scan_single_msg); + if (ret < 0) + goto done; + + iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf, + iio_get_time_ns(indio_dev)); + +done: + iio_trigger_notify_done(indio_dev->trig); + return IRQ_HANDLED; +} + +static int tlc4541_get_range(struct tlc4541_state *st) +{ + int vref; + + vref = regulator_get_voltage(st->reg); + if (vref < 0) + return vref; + + vref /= 1000; + + return vref; +} + +static int tlc4541_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long m) +{ + int ret = 0; + struct tlc4541_state *st = iio_priv(indio_dev); + + switch (m) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = spi_sync(st->spi, &st->scan_single_msg); + iio_device_release_direct_mode(indio_dev); + if (ret < 0) + return ret; + *val = be16_to_cpu(st->rx_buf[0]); + *val = *val >> chan->scan_type.shift; + *val &= GENMASK(chan->scan_type.realbits - 1, 0); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = tlc4541_get_range(st); + if (ret < 0) + return ret; + *val = ret; + *val2 = chan->scan_type.realbits; + return IIO_VAL_FRACTIONAL_LOG2; + } + return -EINVAL; +} + +static const struct iio_info tlc4541_info = { + .read_raw = &tlc4541_read_raw, + .driver_module = THIS_MODULE, +}; + +static int tlc4541_probe(struct spi_device *spi) +{ + struct tlc4541_state *st; + struct iio_dev *indio_dev; + const struct tlc4541_chip_info *info; + int ret; + int8_t device_init = 0; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (indio_dev == NULL) + return -ENOMEM; + + st = iio_priv(indio_dev); + + spi_set_drvdata(spi, indio_dev); + + st->spi = spi; + + info = &tlc4541_chip_info[spi_get_device_id(spi)->driver_data]; + + indio_dev->name = spi_get_device_id(spi)->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = info->channels; + indio_dev->num_channels = info->num_channels; + indio_dev->info = &tlc4541_info; + + /* perform reset */ + spi_write(spi, &device_init, 1); + + /* Setup default message */ + st->scan_single_xfer[0].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[0].len = 3; + st->scan_single_xfer[1].delay_usecs = 3; + st->scan_single_xfer[2].rx_buf = &st->rx_buf[0]; + st->scan_single_xfer[2].len = 2; + + spi_message_init_with_transfers(&st->scan_single_msg, + st->scan_single_xfer, 3); + + st->reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(st->reg)) + return PTR_ERR(st->reg); + + ret = regulator_enable(st->reg); + if (ret) + return ret; + + ret = iio_triggered_buffer_setup(indio_dev, NULL, + &tlc4541_trigger_handler, NULL); + if (ret) + goto error_disable_reg; + + ret = iio_device_register(indio_dev); + if (ret) + goto error_cleanup_buffer; + + return 0; + +error_cleanup_buffer: + iio_triggered_buffer_cleanup(indio_dev); +error_disable_reg: + regulator_disable(st->reg); + + return ret; +} + +static int tlc4541_remove(struct spi_device *spi) +{ + struct iio_dev *indio_dev = spi_get_drvdata(spi); + struct tlc4541_state *st = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + iio_triggered_buffer_cleanup(indio_dev); + regulator_disable(st->reg); + + return 0; +} + +#ifdef CONFIG_OF +static const struct of_device_id tlc4541_dt_ids[] = { + { .compatible = "ti,tlc3541", }, + { .compatible = "ti,tlc4541", }, + {} +}; +MODULE_DEVICE_TABLE(of, tlc4541_dt_ids); +#endif + +static const struct spi_device_id tlc4541_id[] = { + {"tlc3541", TLC3541}, + {"tlc4541", TLC4541}, + {} +}; +MODULE_DEVICE_TABLE(spi, tlc4541_id); + +static struct spi_driver tlc4541_driver = { + .driver = { + .name = "tlc4541", + .of_match_table = of_match_ptr(tlc4541_dt_ids), + }, + .probe = tlc4541_probe, + .remove = tlc4541_remove, + .id_table = tlc4541_id, +}; +module_spi_driver(tlc4541_driver); + +MODULE_AUTHOR("Phil Reid "); +MODULE_DESCRIPTION("Texas Instruments TLC4541 ADC"); +MODULE_LICENSE("GPL v2"); From 18e2452a3c4e830bff9628ec0e25a814df491e30 Mon Sep 17 00:00:00 2001 From: Nicholas Mc Guire Date: Mon, 16 Jan 2017 16:35:53 +0100 Subject: [PATCH 18/42] iio: health: max30100: use msleep() for long uncritical delays ulseep_range() uses hrtimers and provides no advantage over msleep() for larger delays. Fix up the 35ms delays here to use msleep() and reduce the load on the hrtimer subsystem. Fixes: commit 4d33615df58b ("iio: light: add MAX30100 oximeter driver support") Link: http://lkml.org/lkml/2017/1/11/377 Signed-off-by: Nicholas Mc Guire Reviewed-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/health/max30100.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 90ab8a2d2846..9648c69ea1a2 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -378,7 +378,7 @@ static int max30100_get_temp(struct max30100_data *data, int *val) if (ret) return ret; - usleep_range(35000, 50000); + msleep(35); return max30100_read_temp(data, val); } From b1450ae9b66c6d6118322b1fb839531e33aae6c4 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 16 Jan 2017 12:50:43 -0300 Subject: [PATCH 19/42] iio: light: opt3001: Export OF device ID table as module aliases The I2C core always reports a MODALIAS of the form i2c: even if the device was registered via OF, this means that exporting the OF device ID table device aliases in the module is not needed. But in order to change how the core reports modaliases to user-space, it's better to export it. Signed-off-by: Javier Martinez Canillas Signed-off-by: Jonathan Cameron --- drivers/iio/light/opt3001.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 78c9b3a6453a..b91ebc3483ce 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -840,6 +840,7 @@ static const struct of_device_id opt3001_of_match[] = { { .compatible = "ti,opt3001" }, { } }; +MODULE_DEVICE_TABLE(of, opt3001_of_match); static struct i2c_driver opt3001_driver = { .probe = opt3001_probe, From 9c6e4efded8d1d811f3d5ab5ee8b1cfd033d9107 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 16 Jan 2017 12:50:44 -0300 Subject: [PATCH 20/42] iio: potentiometer: mcp4531: Export OF device ID table as module aliases The I2C core always reports a MODALIAS of the form i2c: even if the device was registered via OF, this means that exporting the OF device ID table device aliases in the module is not needed. But in order to change how the core reports modaliases to user-space, it's better to export it. Signed-off-by: Javier Martinez Canillas Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/mcp4531.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/potentiometer/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c index 0d1bcf89ae17..314353d7ab59 100644 --- a/drivers/iio/potentiometer/mcp4531.c +++ b/drivers/iio/potentiometer/mcp4531.c @@ -284,6 +284,7 @@ static const struct of_device_id mcp4531_of_match[] = { MCP4531_COMPATIBLE("microchip,mcp4662-104", MCP466x_104), { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, mcp4531_of_match); #endif static int mcp4531_probe(struct i2c_client *client, From 99be230db9879088d291ab832641af539d94a5b4 Mon Sep 17 00:00:00 2001 From: Javier Martinez Canillas Date: Mon, 16 Jan 2017 12:50:45 -0300 Subject: [PATCH 21/42] iio: adc: max1363: Export OF device ID table as module aliases The I2C core always reports a MODALIAS of the form i2c: even if the device was registered via OF, this means that exporting the OF device ID table device aliases in the module is not needed. But in order to change how the core reports modaliases to user-space, it's better to export it. Signed-off-by: Javier Martinez Canillas Signed-off-by: Jonathan Cameron --- drivers/iio/adc/max1363.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c index 841a13c9b6ea..c6c12feb4a08 100644 --- a/drivers/iio/adc/max1363.c +++ b/drivers/iio/adc/max1363.c @@ -1567,6 +1567,7 @@ static const struct of_device_id max1363_of_match[] = { MAX1363_COMPATIBLE("maxim,max11647", max11647), { /* sentinel */ } }; +MODULE_DEVICE_TABLE(of, max1363_of_match); #endif static int max1363_probe(struct i2c_client *client, From c4f60925984f4b22cb11ba76141855b5562d5423 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:49 -0500 Subject: [PATCH 22/42] staging: iio: isl29028: fix alignment of function arguments Two separate calls to regmap_update_bits() in isl29028_set_als_scale() and isl29028_set_als_ir_mode() did not have their function arguments on the next line aligned correctly to the open parenthesis. This patch corrects the alignment. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index 1de81f53f34a..50a208f368ed 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -116,7 +116,7 @@ static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) ISL29028_CONF_ALS_RANGE_LOW_LUX; return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - ISL29028_CONF_ALS_RANGE_MASK, val); + ISL29028_CONF_ALS_RANGE_MASK, val); } static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, @@ -148,7 +148,8 @@ static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, case ISL29028_MODE_NONE: return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - ISL29028_CONF_ALS_EN_MASK, ISL29028_CONF_ALS_DIS); + ISL29028_CONF_ALS_EN_MASK, + ISL29028_CONF_ALS_DIS); } if (ret < 0) From b44b1f3193ff8f442c844035815d7b2ebc26366b Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:50 -0500 Subject: [PATCH 23/42] staging: iio: isl29028: combine isl29028_proxim_get() and isl29028_read_proxim() isl29028_proxim_get() checks to see if the promixity needs to be enabled on the chip and then calls isl29028_read_proxim(). There are no other callers of isl29028_read_proxim(). The naming between these two functions can be confusing so this patch combines the two to avoid the confusion. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index 50a208f368ed..dcec32dc9765 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -201,6 +201,13 @@ static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) unsigned int data; int ret; + if (!chip->enable_prox) { + ret = isl29028_enable_proximity(chip, true); + if (ret < 0) + return ret; + chip->enable_prox = true; + } + ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data); if (ret < 0) { dev_err(dev, "Error in reading register %d, error %d\n", @@ -211,19 +218,6 @@ static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) return 0; } -static int isl29028_proxim_get(struct isl29028_chip *chip, int *prox_data) -{ - int ret; - - if (!chip->enable_prox) { - ret = isl29028_enable_proximity(chip, true); - if (ret < 0) - return ret; - chip->enable_prox = true; - } - return isl29028_read_proxim(chip, prox_data); -} - static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) { struct device *dev = regmap_get_device(chip->regmap); @@ -349,7 +343,7 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, ret = isl29028_ir_get(chip, val); break; case IIO_PROXIMITY: - ret = isl29028_proxim_get(chip, val); + ret = isl29028_read_proxim(chip, val); break; default: break; From 6db2fa0ab5d9b0b431617a3711c7990e3084c561 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:48 -0500 Subject: [PATCH 24/42] staging: iio: isl29028: made alignment of variables in struct isl29028_chip consistent The alignment of the variables in the struct isl29028_chip is not consistent. This changes all of the variables to use consistent alignment to improve the code readability. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index dcec32dc9765..a13c8dbbec23 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -67,13 +67,13 @@ enum isl29028_als_ir_mode { }; struct isl29028_chip { - struct mutex lock; - struct regmap *regmap; + struct mutex lock; + struct regmap *regmap; - unsigned int prox_sampling; - bool enable_prox; + unsigned int prox_sampling; + bool enable_prox; - int lux_scale; + int lux_scale; enum isl29028_als_ir_mode als_ir_mode; }; From b1dda969350e75f2a4a0c036748c028f6f70ba7f Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:51 -0500 Subject: [PATCH 25/42] staging: iio: isl29028: change newlines to improve readability Add and remove newlines to improve code readability in preparation for moving the driver out of staging. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 33 ++++++++++++++++++++-------- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index a13c8dbbec23..d05d761bc0ef 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -69,10 +69,8 @@ enum isl29028_als_ir_mode { struct isl29028_chip { struct mutex lock; struct regmap *regmap; - unsigned int prox_sampling; bool enable_prox; - int lux_scale; enum isl29028_als_ir_mode als_ir_mode; }; @@ -88,6 +86,7 @@ static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, if (period >= prox_period[sel]) break; } + return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, ISL29028_CONF_PROX_SLP_MASK, sel << ISL29028_CONF_PROX_SLP_SH); @@ -107,6 +106,7 @@ static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) /* Wait for conversion to be complete for first sample */ mdelay(DIV_ROUND_UP(1000, chip->prox_sampling)); + return 0; } @@ -139,13 +139,11 @@ static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, ISL29028_CONF_ALS_RANGE_MASK, ISL29028_CONF_ALS_RANGE_HIGH_LUX); break; - case ISL29028_MODE_IR: ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, ISL29028_CONF_ALS_IR_MODE_MASK, ISL29028_CONF_ALS_IR_MODE_IR); break; - case ISL29028_MODE_NONE: return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, ISL29028_CONF_ALS_EN_MASK, @@ -192,6 +190,7 @@ static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir) } *als_ir = ((msb & 0xF) << 8) | (lsb & 0xFF); + return 0; } @@ -205,6 +204,7 @@ static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) ret = isl29028_enable_proximity(chip, true); if (ret < 0) return ret; + chip->enable_prox = true; } @@ -214,7 +214,9 @@ static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) ISL29028_REG_PROX_DATA, ret); return ret; } + *prox = data; + return 0; } @@ -245,6 +247,7 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) als_ir_data = (als_ir_data * 49) / 100; *als_data = als_ir_data; + return 0; } @@ -258,6 +261,7 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) dev_err(dev, "Error in enabling IR mode err %d\n", ret); return ret; } + return isl29028_read_als_ir(chip, ir_data); } @@ -279,11 +283,13 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, mask); break; } + if (val < 1 || val > 100) { dev_err(dev, "Samp_freq %d is not in range[1:100]\n", val); break; } + ret = isl29028_set_proxim_sampling(chip, val); if (ret < 0) { dev_err(dev, @@ -291,9 +297,9 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, ret); break; } + chip->prox_sampling = val; break; - case IIO_LIGHT: if (mask != IIO_CHAN_INFO_SCALE) { dev_err(dev, @@ -301,25 +307,29 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, mask); break; } + if ((val != 125) && (val != 2000)) { dev_err(dev, "lux scale %d is invalid [125, 2000]\n", val); break; } + ret = isl29028_set_als_scale(chip, val); if (ret < 0) { dev_err(dev, "Setting lux scale fail with error %d\n", ret); break; } + chip->lux_scale = val; break; - default: dev_err(dev, "Unsupported channel type\n"); break; } + mutex_unlock(&chip->lock); + return ret; } @@ -348,30 +358,32 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, default: break; } + if (ret < 0) break; + ret = IIO_VAL_INT; break; - case IIO_CHAN_INFO_SAMP_FREQ: if (chan->type != IIO_PROXIMITY) break; + *val = chip->prox_sampling; ret = IIO_VAL_INT; break; - case IIO_CHAN_INFO_SCALE: if (chan->type != IIO_LIGHT) break; *val = chip->lux_scale; ret = IIO_VAL_INT; break; - default: dev_err(dev, "mask value 0x%08lx not supported\n", mask); break; } + mutex_unlock(&chip->lock); + return ret; } @@ -497,6 +509,7 @@ static int isl29028_probe(struct i2c_client *client, ISL29028_REG_TEST1_MODE, ret); return ret; } + ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0); if (ret < 0) { dev_err(&client->dev, @@ -517,6 +530,7 @@ static int isl29028_probe(struct i2c_client *client, indio_dev->name = id->name; indio_dev->dev.parent = &client->dev; indio_dev->modes = INDIO_DIRECT_MODE; + ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); if (ret < 0) { dev_err(&client->dev, @@ -524,6 +538,7 @@ static int isl29028_probe(struct i2c_client *client, ret); return ret; } + return 0; } From 19140ae90484a89dc21ccea0cb077b80bf727019 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:52 -0500 Subject: [PATCH 26/42] staging: iio: isl29028: remove unused define ISL29028_DEV_ATTR The #define ISL29028_DEV_ATTR was not used so this patch removes the unnecessary code. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index d05d761bc0ef..7074e629229a 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -391,7 +391,6 @@ static IIO_CONST_ATTR(in_proximity_sampling_frequency_available, "1 3 5 10 13 20 83 100"); static IIO_CONST_ATTR(in_illuminance_scale_available, "125 2000"); -#define ISL29028_DEV_ATTR(name) (&iio_dev_attr_##name.dev_attr.attr) #define ISL29028_CONST_ATTR(name) (&iio_const_attr_##name.dev_attr.attr) static struct attribute *isl29028_attributes[] = { ISL29028_CONST_ATTR(in_proximity_sampling_frequency_available), From ae1e5238f71ca76f79fa79bfb357ea1bbd1b85b8 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:53 -0500 Subject: [PATCH 27/42] staging: iio: isl29028: move failure logging into isl29028_set_proxim_sampling() When isl29028_set_proxim_sampling() fails, it was up to both callers to log the failure message. This patch moves the logging into isl29028_set_proxim_sampling() to reduce the overall amount of code in the driver. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 31 ++++++++++++++-------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index 7074e629229a..bd85ccb1b120 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -78,18 +78,29 @@ struct isl29028_chip { static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, unsigned int sampling) { + struct device *dev = regmap_get_device(chip->regmap); static unsigned int prox_period[] = {800, 400, 200, 100, 75, 50, 12, 0}; - int sel; unsigned int period = DIV_ROUND_UP(1000, sampling); + int sel, ret; for (sel = 0; sel < ARRAY_SIZE(prox_period); ++sel) { if (period >= prox_period[sel]) break; } - return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - ISL29028_CONF_PROX_SLP_MASK, - sel << ISL29028_CONF_PROX_SLP_SH); + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_PROX_SLP_MASK, + sel << ISL29028_CONF_PROX_SLP_SH); + + if (ret < 0) { + dev_err(dev, "%s(): Error %d setting the proximity sampling\n", + __func__, ret); + return ret; + } + + chip->prox_sampling = sampling; + + return ret; } static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) @@ -291,14 +302,6 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, } ret = isl29028_set_proxim_sampling(chip, val); - if (ret < 0) { - dev_err(dev, - "Setting proximity samp_freq fail, err %d\n", - ret); - break; - } - - chip->prox_sampling = val; break; case IIO_LIGHT: if (mask != IIO_CHAN_INFO_SCALE) { @@ -437,10 +440,8 @@ static int isl29028_chip_init_and_power_on(struct isl29028_chip *chip) } ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling); - if (ret < 0) { - dev_err(dev, "setting the proximity, err = %d\n", ret); + if (ret < 0) return ret; - } ret = isl29028_set_als_scale(chip, chip->lux_scale); if (ret < 0) From bcf5104e27187b8441aa10714b2dbe49f154bc92 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:54 -0500 Subject: [PATCH 28/42] staging: iio: isl29028: move failure logging into isl29028_set_als_scale() When isl29028_set_als_scale() fails, it was up to both callers to log the failure message. This patch moves the logging into isl29028_set_als_scale() to reduce the overall amount of code in the driver. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index bd85ccb1b120..8e7b3dbee183 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -123,11 +123,22 @@ static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) { + struct device *dev = regmap_get_device(chip->regmap); int val = (lux_scale == 2000) ? ISL29028_CONF_ALS_RANGE_HIGH_LUX : ISL29028_CONF_ALS_RANGE_LOW_LUX; + int ret; - return regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - ISL29028_CONF_ALS_RANGE_MASK, val); + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, + ISL29028_CONF_ALS_RANGE_MASK, val); + if (ret < 0) { + dev_err(dev, "%s(): Error %d setting the ALS scale\n", __func__, + ret); + return ret; + } + + chip->lux_scale = lux_scale; + + return ret; } static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, @@ -318,13 +329,6 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, } ret = isl29028_set_als_scale(chip, val); - if (ret < 0) { - dev_err(dev, - "Setting lux scale fail with error %d\n", ret); - break; - } - - chip->lux_scale = val; break; default: dev_err(dev, "Unsupported channel type\n"); @@ -443,10 +447,7 @@ static int isl29028_chip_init_and_power_on(struct isl29028_chip *chip) if (ret < 0) return ret; - ret = isl29028_set_als_scale(chip, chip->lux_scale); - if (ret < 0) - dev_err(dev, "setting als scale failed, err = %d\n", ret); - return ret; + return isl29028_set_als_scale(chip, chip->lux_scale); } static bool isl29028_is_volatile_reg(struct device *dev, unsigned int reg) From 578e02a7ed551f8d5aa8ee9fd094369bd84e7b01 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:55 -0500 Subject: [PATCH 29/42] staging: iio: isl29028: made error messages consistent The wording and style of the different error messages was not consistent. This patch makes the wording and style consistent throughout the driver. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 56 ++++++++++++++++------------ 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index 8e7b3dbee183..cffecf9d6bde 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -200,14 +200,16 @@ static int isl29028_read_als_ir(struct isl29028_chip *chip, int *als_ir) ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_L, &lsb); if (ret < 0) { dev_err(dev, - "Error in reading register ALSIR_L err %d\n", ret); + "%s(): Error %d reading register ALSIR_L\n", + __func__, ret); return ret; } ret = regmap_read(chip->regmap, ISL29028_REG_ALSIR_U, &msb); if (ret < 0) { dev_err(dev, - "Error in reading register ALSIR_U err %d\n", ret); + "%s(): Error %d reading register ALSIR_U\n", + __func__, ret); return ret; } @@ -232,8 +234,8 @@ static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) ret = regmap_read(chip->regmap, ISL29028_REG_PROX_DATA, &data); if (ret < 0) { - dev_err(dev, "Error in reading register %d, error %d\n", - ISL29028_REG_PROX_DATA, ret); + dev_err(dev, "%s(): Error %d reading register PROX_DATA\n", + __func__, ret); return ret; } @@ -250,7 +252,8 @@ static int isl29028_als_get(struct isl29028_chip *chip, int *als_data) ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_ALS); if (ret < 0) { - dev_err(dev, "Error in enabling ALS mode err %d\n", ret); + dev_err(dev, "%s(): Error %d enabling ALS mode\n", __func__, + ret); return ret; } @@ -280,7 +283,8 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) ret = isl29028_set_als_ir_mode(chip, ISL29028_MODE_IR); if (ret < 0) { - dev_err(dev, "Error in enabling IR mode err %d\n", ret); + dev_err(dev, "%s(): Error %d enabling IR mode\n", __func__, + ret); return ret; } @@ -301,14 +305,15 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, case IIO_PROXIMITY: if (mask != IIO_CHAN_INFO_SAMP_FREQ) { dev_err(dev, - "proximity: mask value 0x%08lx not supported\n", - mask); + "%s(): proximity: Mask value 0x%08lx is not supported\n", + __func__, mask); break; } if (val < 1 || val > 100) { dev_err(dev, - "Samp_freq %d is not in range[1:100]\n", val); + "%s(): proximity: Sampling frequency %d is not in the range [1:100]\n", + __func__, val); break; } @@ -317,21 +322,23 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, case IIO_LIGHT: if (mask != IIO_CHAN_INFO_SCALE) { dev_err(dev, - "light: mask value 0x%08lx not supported\n", - mask); + "%s(): light: Mask value 0x%08lx is not supported\n", + __func__, mask); break; } if ((val != 125) && (val != 2000)) { dev_err(dev, - "lux scale %d is invalid [125, 2000]\n", val); + "%s(): light: Lux scale %d is not in the set {125, 2000}\n", + __func__, val); break; } ret = isl29028_set_als_scale(chip, val); break; default: - dev_err(dev, "Unsupported channel type\n"); + dev_err(dev, "%s(): Unsupported channel type %x\n", + __func__, chan->type); break; } @@ -385,7 +392,8 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, ret = IIO_VAL_INT; break; default: - dev_err(dev, "mask value 0x%08lx not supported\n", mask); + dev_err(dev, "%s(): mask value 0x%08lx is not supported\n", + __func__, mask); break; } @@ -438,8 +446,8 @@ static int isl29028_chip_init_and_power_on(struct isl29028_chip *chip) ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0); if (ret < 0) { - dev_err(dev, "%s(): write to reg %d failed, err = %d\n", - __func__, ISL29028_REG_CONFIGURE, ret); + dev_err(dev, "%s(): Error %d clearing the CONFIGURE register\n", + __func__, ret); return ret; } @@ -493,8 +501,8 @@ static int isl29028_probe(struct i2c_client *client, chip->regmap = devm_regmap_init_i2c(client, &isl29028_regmap_config); if (IS_ERR(chip->regmap)) { ret = PTR_ERR(chip->regmap); - dev_err(&client->dev, "regmap initialization failed: %d\n", - ret); + dev_err(&client->dev, "%s: Error %d initializing regmap\n", + __func__, ret); return ret; } @@ -506,16 +514,16 @@ static int isl29028_probe(struct i2c_client *client, ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0); if (ret < 0) { dev_err(&client->dev, - "%s(): write to reg %d failed, err = %d\n", __func__, - ISL29028_REG_TEST1_MODE, ret); + "%s(): Error %d writing to TEST1_MODE register\n", + __func__, ret); return ret; } ret = regmap_write(chip->regmap, ISL29028_REG_TEST2_MODE, 0x0); if (ret < 0) { dev_err(&client->dev, - "%s(): write to reg %d failed, err = %d\n", __func__, - ISL29028_REG_TEST2_MODE, ret); + "%s(): Error %d writing to TEST2_MODE register\n", + __func__, ret); return ret; } @@ -535,8 +543,8 @@ static int isl29028_probe(struct i2c_client *client, ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); if (ret < 0) { dev_err(&client->dev, - "iio registration fails with error %d\n", - ret); + "%s(): iio registration failed with error %d\n", + __func__, ret); return ret; } From 65ac716b2013b97d68dbec4be0b5fb01a16efe7e Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:56 -0500 Subject: [PATCH 30/42] staging: iio: isl29028: remove unnecessary error logging in isl29028_chip_init_and_power_on() If the call to isl29028_chip_init_and_power_on() in isl29028_probe() fails, then isl29028_probe() will log an error message. All of the error paths in that call path already have error logging in place. This patch removes the unnecessary logging. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index cffecf9d6bde..be1fc4a8960e 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -528,10 +528,8 @@ static int isl29028_probe(struct i2c_client *client, } ret = isl29028_chip_init_and_power_on(chip); - if (ret < 0) { - dev_err(&client->dev, "chip initialization failed: %d\n", ret); + if (ret < 0) return ret; - } indio_dev->info = &isl29028_info; indio_dev->channels = isl29028_channels; From 8c93940268065a69c08ae6bc3bc6165ba45cac71 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:57 -0500 Subject: [PATCH 31/42] staging: iio: isl29028: remove out of memory log message If the call to devm_iio_device_alloc() fails, then isl29028_probe() logs a message saying that memory cannot be allocated. The user's system most likely has larger issues at this point. This patch removes that error message since the error code is passed on and the message is not necessary. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index be1fc4a8960e..e93077b07c4f 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -488,10 +488,8 @@ static int isl29028_probe(struct i2c_client *client, int ret; indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); - if (!indio_dev) { - dev_err(&client->dev, "iio allocation fails\n"); + if (!indio_dev) return -ENOMEM; - } chip = iio_priv(indio_dev); From 71a0a643468e96caad4f0aa48d1041f319f3ba83 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:58 -0500 Subject: [PATCH 32/42] staging: iio: isl29028: remove unnecessary parenthesis isl29028_write_raw() contains unnecessary parenthesis when checking to see if the passed in lux scale is valid. This patch removes the unnecessary parenthesis. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index e93077b07c4f..bc9c01d05424 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -327,7 +327,7 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, break; } - if ((val != 125) && (val != 2000)) { + if (val != 125 && val != 2000) { dev_err(dev, "%s(): light: Lux scale %d is not in the set {125, 2000}\n", __func__, val); From 6766342441cbf286940f2c44b5cc869fa5a57046 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:24:59 -0500 Subject: [PATCH 33/42] staging: iio: isl29028: remove enable flag from isl29028_enable_proximity() isl29028_enable_proximity() has a boolean argument named enable. This function is only called once and the enable flag is set to true in that call. This patch removes the enable parameter from that function. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index bc9c01d05424..f1b3651c779b 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -103,15 +103,13 @@ static int isl29028_set_proxim_sampling(struct isl29028_chip *chip, return ret; } -static int isl29028_enable_proximity(struct isl29028_chip *chip, bool enable) +static int isl29028_enable_proximity(struct isl29028_chip *chip) { int ret; - int val = 0; - if (enable) - val = ISL29028_CONF_PROX_EN; ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, - ISL29028_CONF_PROX_EN_MASK, val); + ISL29028_CONF_PROX_EN_MASK, + ISL29028_CONF_PROX_EN); if (ret < 0) return ret; @@ -225,7 +223,7 @@ static int isl29028_read_proxim(struct isl29028_chip *chip, int *prox) int ret; if (!chip->enable_prox) { - ret = isl29028_enable_proximity(chip, true); + ret = isl29028_enable_proximity(chip); if (ret < 0) return ret; From 84a76694bc17dd31eed84ab2fae857f18909da42 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:25:00 -0500 Subject: [PATCH 34/42] staging: iio: isl29028: only set proximity sampling rate when proximity is enabled isl29028_chip_init_and_power_on() calls isl29028_set_proxim_sampling() and this is not needed until the user actually needs to take a proximity reading. This patch moves the isl29028_set_proxim_sampling() call from isl29028_chip_init_and_power_on() to isl29028_enable_proximity(). This sets the stage for faster resume times from the runtime power management if the user is only querying the ALS/IR sensor. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index f1b3651c779b..fa58d083906e 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -107,6 +107,10 @@ static int isl29028_enable_proximity(struct isl29028_chip *chip) { int ret; + ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling); + if (ret < 0) + return ret; + ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, ISL29028_CONF_PROX_EN_MASK, ISL29028_CONF_PROX_EN); @@ -449,10 +453,6 @@ static int isl29028_chip_init_and_power_on(struct isl29028_chip *chip) return ret; } - ret = isl29028_set_proxim_sampling(chip, chip->prox_sampling); - if (ret < 0) - return ret; - return isl29028_set_als_scale(chip, chip->lux_scale); } From 0fac96ed50d1bb6a0ce8dd44b94d3643a0dc3c91 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:25:01 -0500 Subject: [PATCH 35/42] staging: iio: isl29028: only set ALS scale when ALS/IR sensing is enabled isl29028_chip_init_and_power_on() calls isl29028_set_als_scale() and this is not needed until the user actually needs to take a reading from the ALS/IR sensor. This patch moves the isl29028_set_als_scale() call from isl29028_chip_init_and_power_on() to isl29028_set_als_ir_mode(). This sets the stage for faster resume times from runtime power management if the user is only querying the proximity sensor. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index fa58d083906e..598a5a5a8641 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -146,11 +146,15 @@ static int isl29028_set_als_scale(struct isl29028_chip *chip, int lux_scale) static int isl29028_set_als_ir_mode(struct isl29028_chip *chip, enum isl29028_als_ir_mode mode) { - int ret = 0; + int ret; if (chip->als_ir_mode == mode) return 0; + ret = isl29028_set_als_scale(chip, chip->lux_scale); + if (ret < 0) + return ret; + switch (mode) { case ISL29028_MODE_ALS: ret = regmap_update_bits(chip->regmap, ISL29028_REG_CONFIGURE, @@ -453,7 +457,7 @@ static int isl29028_chip_init_and_power_on(struct isl29028_chip *chip) return ret; } - return isl29028_set_als_scale(chip, chip->lux_scale); + return ret; } static bool isl29028_is_volatile_reg(struct device *dev, unsigned int reg) From 2db5054ac28d4ab2eaa6c67e2d9f61fa5ba006b8 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Tue, 17 Jan 2017 04:25:02 -0500 Subject: [PATCH 36/42] staging: iio: isl29028: add runtime power management support This patch adds runtime power management support to the isl29028 driver. It defaults to powering off the device after two seconds of inactivity. isl29028_chip_init_and_power_on() currently only zeros the CONFIGURE register on the chip, which will cause the chip to turn off. This patch also renames that function to isl29028_clear_configure_reg() since it is now used in several places. Signed-off-by: Brian Masney Signed-off-by: Jonathan Cameron --- drivers/staging/iio/light/isl29028.c | 118 +++++++++++++++++++++++++-- 1 file changed, 110 insertions(+), 8 deletions(-) diff --git a/drivers/staging/iio/light/isl29028.c b/drivers/staging/iio/light/isl29028.c index 598a5a5a8641..6bb6d37cc7d1 100644 --- a/drivers/staging/iio/light/isl29028.c +++ b/drivers/staging/iio/light/isl29028.c @@ -26,6 +26,7 @@ #include #include #include +#include #define ISL29028_CONV_TIME_MS 100 @@ -60,6 +61,8 @@ #define ISL29028_NUM_REGS (ISL29028_REG_TEST2_MODE + 1) +#define ISL29028_POWER_OFF_DELAY_MS 2000 + enum isl29028_als_ir_mode { ISL29028_MODE_NONE = 0, ISL29028_MODE_ALS, @@ -297,6 +300,23 @@ static int isl29028_ir_get(struct isl29028_chip *chip, int *ir_data) return isl29028_read_als_ir(chip, ir_data); } +static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on) +{ + struct device *dev = regmap_get_device(chip->regmap); + int ret; + + if (on) { + ret = pm_runtime_get_sync(dev); + if (ret < 0) + pm_runtime_put_noidle(dev); + } else { + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + } + + return ret; +} + /* Channel IO */ static int isl29028_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, @@ -304,9 +324,15 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, { struct isl29028_chip *chip = iio_priv(indio_dev); struct device *dev = regmap_get_device(chip->regmap); - int ret = -EINVAL; + int ret; + + ret = isl29028_set_pm_runtime_busy(chip, true); + if (ret < 0) + return ret; mutex_lock(&chip->lock); + + ret = -EINVAL; switch (chan->type) { case IIO_PROXIMITY: if (mask != IIO_CHAN_INFO_SAMP_FREQ) { @@ -350,6 +376,13 @@ static int isl29028_write_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->lock); + if (ret < 0) + return ret; + + ret = isl29028_set_pm_runtime_busy(chip, false); + if (ret < 0) + return ret; + return ret; } @@ -359,9 +392,15 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, { struct isl29028_chip *chip = iio_priv(indio_dev); struct device *dev = regmap_get_device(chip->regmap); - int ret = -EINVAL; + int ret, pm_ret; + + ret = isl29028_set_pm_runtime_busy(chip, true); + if (ret < 0) + return ret; mutex_lock(&chip->lock); + + ret = -EINVAL; switch (mask) { case IIO_CHAN_INFO_RAW: case IIO_CHAN_INFO_PROCESSED: @@ -405,6 +444,18 @@ static int isl29028_read_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->lock); + if (ret < 0) + return ret; + + /** + * Preserve the ret variable if the call to + * isl29028_set_pm_runtime_busy() is successful so the reading + * (if applicable) is returned to user space. + */ + pm_ret = isl29028_set_pm_runtime_busy(chip, false); + if (pm_ret < 0) + return pm_ret; + return ret; } @@ -445,17 +496,18 @@ static const struct iio_info isl29028_info = { .write_raw = isl29028_write_raw, }; -static int isl29028_chip_init_and_power_on(struct isl29028_chip *chip) +static int isl29028_clear_configure_reg(struct isl29028_chip *chip) { struct device *dev = regmap_get_device(chip->regmap); int ret; ret = regmap_write(chip->regmap, ISL29028_REG_CONFIGURE, 0x0); - if (ret < 0) { + if (ret < 0) dev_err(dev, "%s(): Error %d clearing the CONFIGURE register\n", __func__, ret); - return ret; - } + + chip->als_ir_mode = ISL29028_MODE_NONE; + chip->enable_prox = false; return ret; } @@ -509,7 +561,6 @@ static int isl29028_probe(struct i2c_client *client, chip->enable_prox = false; chip->prox_sampling = 20; chip->lux_scale = 2000; - chip->als_ir_mode = ISL29028_MODE_NONE; ret = regmap_write(chip->regmap, ISL29028_REG_TEST1_MODE, 0x0); if (ret < 0) { @@ -527,7 +578,7 @@ static int isl29028_probe(struct i2c_client *client, return ret; } - ret = isl29028_chip_init_and_power_on(chip); + ret = isl29028_clear_configure_reg(chip); if (ret < 0) return ret; @@ -538,6 +589,11 @@ static int isl29028_probe(struct i2c_client *client, indio_dev->dev.parent = &client->dev; indio_dev->modes = INDIO_DIRECT_MODE; + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, + ISL29028_POWER_OFF_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + ret = devm_iio_device_register(indio_dev->dev.parent, indio_dev); if (ret < 0) { dev_err(&client->dev, @@ -549,6 +605,50 @@ static int isl29028_probe(struct i2c_client *client, return 0; } +static int isl29028_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct isl29028_chip *chip = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + return isl29028_clear_configure_reg(chip); +} + +static int __maybe_unused isl29028_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct isl29028_chip *chip = iio_priv(indio_dev); + int ret; + + mutex_lock(&chip->lock); + + ret = isl29028_clear_configure_reg(chip); + + mutex_unlock(&chip->lock); + + return ret; +} + +static int __maybe_unused isl29028_resume(struct device *dev) +{ + /** + * The specific component (ALS/IR or proximity) will enable itself as + * needed the next time that the user requests a reading. This is done + * above in isl29028_set_als_ir_mode() and isl29028_enable_proximity(). + */ + return 0; +} + +static const struct dev_pm_ops isl29028_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(isl29028_suspend, isl29028_resume) + SET_RUNTIME_PM_OPS(isl29028_suspend, isl29028_resume, NULL) +}; + static const struct i2c_device_id isl29028_id[] = { {"isl29028", 0}, {} @@ -565,9 +665,11 @@ MODULE_DEVICE_TABLE(of, isl29028_of_match); static struct i2c_driver isl29028_driver = { .driver = { .name = "isl29028", + .pm = &isl29028_pm_ops, .of_match_table = isl29028_of_match, }, .probe = isl29028_probe, + .remove = isl29028_remove, .id_table = isl29028_id, }; From dfebd8d81dc69afa9f20459ec9839f857f8c135f Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Tue, 17 Jan 2017 19:38:23 +0100 Subject: [PATCH 37/42] iio: imu: st_lsm6dsx: fix typo in gyro sensitivity definition Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 01e002c7f990..f869dfa214e6 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -82,9 +82,9 @@ #define ST_LSM6DSX_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244) #define ST_LSM6DSX_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488) -#define ST_LSM6DSX_GYRO_FS_245_GAIN IIO_DEGREE_TO_RAD(4375) -#define ST_LSM6DSX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(8750) -#define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(17500) +#define ST_LSM6DSX_GYRO_FS_245_GAIN IIO_DEGREE_TO_RAD(8750) +#define ST_LSM6DSX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500) +#define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000) #define ST_LSM6DSX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000) struct st_lsm6dsx_odr { From d5d24bcc0a10a755ee77149e2f311c4ddafc19db Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Sat, 21 Jan 2017 19:28:52 -0800 Subject: [PATCH 38/42] iio: trigger: close race condition in acquiring trigger reference In iio_trigger_write_current() we find the trigger we want while holding mutex on the list of triggers, but we don't actually do a get on it while holding mutex. We wait until further validations are completed and we're sure it's the one we want. Race condition is that it could be freed by the time we do the get. Solution is to grab the trigger (iio_trigger_get) as soon as we find it while holding mutex on the list of triggers. If later we decide it's not the right one, put it back. (iio_trigger_put). Signed-off-by: Alison Schofield Suggested-by: Lars-Peter Clausen Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-trigger.c | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 978729f6d7c4..d0d869eb48f7 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -147,8 +147,7 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name) return NULL; } -static struct iio_trigger *iio_trigger_find_by_name(const char *name, - size_t len) +static struct iio_trigger *iio_trigger_acquire_by_name(const char *name) { struct iio_trigger *trig = NULL, *iter; @@ -156,6 +155,7 @@ static struct iio_trigger *iio_trigger_find_by_name(const char *name, list_for_each_entry(iter, &iio_trigger_list, list) if (sysfs_streq(iter->name, name)) { trig = iter; + iio_trigger_get(trig); break; } mutex_unlock(&iio_trigger_list_lock); @@ -416,20 +416,22 @@ static ssize_t iio_trigger_write_current(struct device *dev, } mutex_unlock(&indio_dev->mlock); - trig = iio_trigger_find_by_name(buf, len); - if (oldtrig == trig) - return len; + trig = iio_trigger_acquire_by_name(buf); + if (oldtrig == trig) { + ret = len; + goto out_trigger_put; + } if (trig && indio_dev->info->validate_trigger) { ret = indio_dev->info->validate_trigger(indio_dev, trig); if (ret) - return ret; + goto out_trigger_put; } if (trig && trig->ops->validate_device) { ret = trig->ops->validate_device(trig, indio_dev); if (ret) - return ret; + goto out_trigger_put; } indio_dev->trig = trig; @@ -441,13 +443,16 @@ static ssize_t iio_trigger_write_current(struct device *dev, iio_trigger_put(oldtrig); } if (indio_dev->trig) { - iio_trigger_get(indio_dev->trig); if (indio_dev->modes & INDIO_EVENT_TRIGGERED) iio_trigger_attach_poll_func(indio_dev->trig, indio_dev->pollfunc_event); } return len; + +out_trigger_put: + iio_trigger_put(trig); + return ret; } static DEVICE_ATTR(current_trigger, S_IRUGO | S_IWUSR, From 3bdafc49f5df9f2f741c71b12b76d202211e06f6 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Sat, 21 Jan 2017 22:29:49 +0530 Subject: [PATCH 39/42] iio: industrialio-trigger: constify device_type structures Declare device_type structure as const as it is only stored in the type field of a device structure. This field is of type const, so add const to declaration of device_type structure. File size before: text data bss dec hex filename 5389 208 48 5645 160d iio/industrialio-trigger.o File size after: text data bss dec hex filename 5453 176 48 5677 162d iio/industrialio-trigger.o Signed-off-by: Bhumika Goyal Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-trigger.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index d0d869eb48f7..54c4d5854ff0 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -492,7 +492,7 @@ static void iio_trig_release(struct device *device) kfree(trig); } -static struct device_type iio_trig_type = { +static const struct device_type iio_trig_type = { .release = iio_trig_release, .groups = iio_trig_dev_groups, }; From 99a22f062b1c279a5d332946de655ef0a244e583 Mon Sep 17 00:00:00 2001 From: Bhumika Goyal Date: Sat, 21 Jan 2017 22:33:00 +0530 Subject: [PATCH 40/42] iio: adc: constify iio_info structures Declare iio_info structures as const as they are only stored in the info field of a iio_dev structure. This field is of type const, so iio_info structures having similar properties can be made const too. File size before: text data bss dec hex filename 6944 792 0 7736 1e38 drivers/iio/adc/ti-ads1015.o File size after: text data bss dec hex filename 7264 472 0 7736 1e38 drivers/iio/adc/ti-ads1015.o Signed-off-by: Bhumika Goyal Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-ads1015.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index cde6f130a99a..422b314f5a3f 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -472,14 +472,14 @@ static const struct attribute_group ads1115_attribute_group = { .attrs = ads1115_attributes, }; -static struct iio_info ads1015_info = { +static const struct iio_info ads1015_info = { .driver_module = THIS_MODULE, .read_raw = ads1015_read_raw, .write_raw = ads1015_write_raw, .attrs = &ads1015_attribute_group, }; -static struct iio_info ads1115_info = { +static const struct iio_info ads1115_info = { .driver_module = THIS_MODULE, .read_raw = ads1015_read_raw, .write_raw = ads1015_write_raw, From 2c99f1a09da3dcc1fbbed4b85c274d045f352b43 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Sat, 21 Jan 2017 07:55:58 +0300 Subject: [PATCH 41/42] iio: trigger: clean up viio_trigger_alloc() 1) Reverse the test for kmalloc() failure so we can pull everything back one tab. 2) Use gotos for unwinding. 3) Some of the extra line breaks for the 80 character limit are no longer needed so we can remove them. Signed-off-by: Dan Carpenter Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-trigger.c | 67 +++++++++++++++--------------- 1 file changed, 33 insertions(+), 34 deletions(-) diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index 54c4d5854ff0..978e1592c2a3 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -518,46 +518,45 @@ static void iio_trig_subirqunmask(struct irq_data *d) static struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs) { struct iio_trigger *trig; + int i; + trig = kzalloc(sizeof *trig, GFP_KERNEL); - if (trig) { - int i; - trig->dev.type = &iio_trig_type; - trig->dev.bus = &iio_bus_type; - device_initialize(&trig->dev); + if (!trig) + return NULL; - mutex_init(&trig->pool_lock); - trig->subirq_base - = irq_alloc_descs(-1, 0, - CONFIG_IIO_CONSUMERS_PER_TRIGGER, - 0); - if (trig->subirq_base < 0) { - kfree(trig); - return NULL; - } + trig->dev.type = &iio_trig_type; + trig->dev.bus = &iio_bus_type; + device_initialize(&trig->dev); - trig->name = kvasprintf(GFP_KERNEL, fmt, vargs); - if (trig->name == NULL) { - irq_free_descs(trig->subirq_base, - CONFIG_IIO_CONSUMERS_PER_TRIGGER); - kfree(trig); - return NULL; - } - trig->subirq_chip.name = trig->name; - trig->subirq_chip.irq_mask = &iio_trig_subirqmask; - trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask; - for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { - irq_set_chip(trig->subirq_base + i, - &trig->subirq_chip); - irq_set_handler(trig->subirq_base + i, - &handle_simple_irq); - irq_modify_status(trig->subirq_base + i, - IRQ_NOREQUEST | IRQ_NOAUTOEN, - IRQ_NOPROBE); - } - get_device(&trig->dev); + mutex_init(&trig->pool_lock); + trig->subirq_base = irq_alloc_descs(-1, 0, + CONFIG_IIO_CONSUMERS_PER_TRIGGER, + 0); + if (trig->subirq_base < 0) + goto free_trig; + + trig->name = kvasprintf(GFP_KERNEL, fmt, vargs); + if (trig->name == NULL) + goto free_descs; + + trig->subirq_chip.name = trig->name; + trig->subirq_chip.irq_mask = &iio_trig_subirqmask; + trig->subirq_chip.irq_unmask = &iio_trig_subirqunmask; + for (i = 0; i < CONFIG_IIO_CONSUMERS_PER_TRIGGER; i++) { + irq_set_chip(trig->subirq_base + i, &trig->subirq_chip); + irq_set_handler(trig->subirq_base + i, &handle_simple_irq); + irq_modify_status(trig->subirq_base + i, + IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE); } + get_device(&trig->dev); return trig; + +free_descs: + irq_free_descs(trig->subirq_base, CONFIG_IIO_CONSUMERS_PER_TRIGGER); +free_trig: + kfree(trig); + return NULL; } struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) From 10e840dfb0b7fc345082dd9e5fff3c1c02e7690e Mon Sep 17 00:00:00 2001 From: Alison Schofield Date: Thu, 19 Jan 2017 19:47:38 -0800 Subject: [PATCH 42/42] iio: trigger: free trigger resource correctly These stand-alone trigger drivers were using iio_trigger_put() where they should have been using iio_trigger_free(). The iio_trigger_put() adds a module_put which is bad since they never did a module_get. In the sysfs driver, module_get/put's are used as triggers are added & removed. This extra module_put() occurs on an error path in the probe routine (probably rare). In the bfin-timer & interrupt trigger drivers, the module resources are not explicitly managed, so it's doing a put on something that was never get'd. It occurs on the probe error path and on the remove path (not so rare). Tested with the sysfs trigger driver. The bfin & interrupt drivers were build tested & inspected only. Signed-off-by: Alison Schofield Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/iio-trig-interrupt.c | 8 ++++---- drivers/iio/trigger/iio-trig-sysfs.c | 2 +- drivers/staging/iio/trigger/iio-trig-bfin-timer.c | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index 572bc6f02ca8..e18f12b74610 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -58,7 +58,7 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) trig_info = kzalloc(sizeof(*trig_info), GFP_KERNEL); if (!trig_info) { ret = -ENOMEM; - goto error_put_trigger; + goto error_free_trigger; } iio_trigger_set_drvdata(trig, trig_info); trig_info->irq = irq; @@ -83,8 +83,8 @@ error_release_irq: free_irq(irq, trig); error_free_trig_info: kfree(trig_info); -error_put_trigger: - iio_trigger_put(trig); +error_free_trigger: + iio_trigger_free(trig); error_ret: return ret; } @@ -99,7 +99,7 @@ static int iio_interrupt_trigger_remove(struct platform_device *pdev) iio_trigger_unregister(trig); free_irq(trig_info->irq, trig); kfree(trig_info); - iio_trigger_put(trig); + iio_trigger_free(trig); return 0; } diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index 3dfab2bc6d69..202e8b89caf2 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -174,7 +174,7 @@ static int iio_sysfs_trigger_probe(int id) return 0; out2: - iio_trigger_put(t->trig); + iio_trigger_free(t->trig); free_t: kfree(t); out1: diff --git a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c index 9658f2097c95..4e0b4eedb53d 100644 --- a/drivers/staging/iio/trigger/iio-trig-bfin-timer.c +++ b/drivers/staging/iio/trigger/iio-trig-bfin-timer.c @@ -260,7 +260,7 @@ out_free_irq: out1: iio_trigger_unregister(st->trig); out: - iio_trigger_put(st->trig); + iio_trigger_free(st->trig); return ret; } @@ -273,7 +273,7 @@ static int iio_bfin_tmr_trigger_remove(struct platform_device *pdev) peripheral_free(st->t->pin); free_irq(st->irq, st); iio_trigger_unregister(st->trig); - iio_trigger_put(st->trig); + iio_trigger_free(st->trig); return 0; }