diff options
Diffstat (limited to 'drivers/iio')
189 files changed, 5639 insertions, 1266 deletions
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig index 267553386c71..2334ad249b46 100644 --- a/drivers/iio/Kconfig +++ b/drivers/iio/Kconfig @@ -72,6 +72,7 @@ source "drivers/iio/accel/Kconfig" source "drivers/iio/adc/Kconfig" source "drivers/iio/afe/Kconfig" source "drivers/iio/amplifiers/Kconfig" +source "drivers/iio/cdc/Kconfig" source "drivers/iio/chemical/Kconfig" source "drivers/iio/common/Kconfig" source "drivers/iio/dac/Kconfig" @@ -85,6 +86,7 @@ source "drivers/iio/light/Kconfig" source "drivers/iio/magnetometer/Kconfig" source "drivers/iio/multiplexer/Kconfig" source "drivers/iio/orientation/Kconfig" +source "drivers/iio/test/Kconfig" if IIO_TRIGGER source "drivers/iio/trigger/Kconfig" endif #IIO_TRIGGER diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 1712011c0f4a..65e39bd4f934 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -18,6 +18,7 @@ obj-y += adc/ obj-y += afe/ obj-y += amplifiers/ obj-y += buffer/ +obj-y += cdc/ obj-y += chemical/ obj-y += common/ obj-y += dac/ @@ -38,4 +39,5 @@ obj-y += pressure/ obj-y += proximity/ obj-y += resolver/ obj-y += temperature/ +obj-y += test/ obj-y += trigger/ diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 2e0c62c39155..cceda3cecbcf 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -157,6 +157,24 @@ config BMC150_ACCEL_SPI tristate select REGMAP_SPI +config BMI088_ACCEL + tristate "Bosch BMI088 Accelerometer Driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + select REGMAP + select BMI088_ACCEL_SPI + help + Say yes here to build support for the Bosch BMI088 accelerometer. + + This is a combo module with both accelerometer and gyroscope. This + driver only implements the accelerometer part, which has its own + address and register map. BMG160 provides the gyroscope driver. + +config BMI088_ACCEL_SPI + tristate + select REGMAP_SPI + config DA280 tristate "MiraMEMS DA280 3-axis 14-bit digital accelerometer driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 4f6c1ebe13b0..32cd1342a31a 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -20,6 +20,8 @@ obj-$(CONFIG_BMA400_SPI) += bma400_spi.o obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o obj-$(CONFIG_BMC150_ACCEL_I2C) += bmc150-accel-i2c.o obj-$(CONFIG_BMC150_ACCEL_SPI) += bmc150-accel-spi.o +obj-$(CONFIG_BMI088_ACCEL) += bmi088-accel-core.o +obj-$(CONFIG_BMI088_ACCEL_SPI) += bmi088-accel-spi.o obj-$(CONFIG_DA280) += da280.o obj-$(CONFIG_DA311) += da311.o obj-$(CONFIG_DMARD06) += dmard06.o diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index 3633a4e302c6..fe225990de24 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -215,7 +215,7 @@ static const struct iio_chan_spec adis16201_channels[] = { ADIS_AUX_ADC_CHAN(ADIS16201_AUX_ADC_REG, ADIS16201_SCAN_AUX_ADC, 0, 12), ADIS_INCLI_CHAN(X, ADIS16201_XINCL_OUT_REG, ADIS16201_SCAN_INCLI_X, BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), - ADIS_INCLI_CHAN(X, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y, + ADIS_INCLI_CHAN(Y, ADIS16201_YINCL_OUT_REG, ADIS16201_SCAN_INCLI_Y, BIT(IIO_CHAN_INFO_CALIBBIAS), 0, 14), IIO_CHAN_SOFT_TIMESTAMP(7) }; diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 8ba1453b8dbf..9c9a896a872a 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1236,8 +1236,6 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, st->dready_trig->ops = &adxl372_trigger_ops; st->peak_datardy_trig->ops = &adxl372_peak_data_trigger_ops; - st->dready_trig->dev.parent = dev; - st->peak_datardy_trig->dev.parent = dev; iio_trigger_set_drvdata(st->dready_trig, indio_dev); iio_trigger_set_drvdata(st->peak_datardy_trig, indio_dev); ret = devm_iio_trigger_register(dev, st->dready_trig); diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index 71f85a3e525b..b8a7469cdae4 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -1044,7 +1044,7 @@ static int bma180_probe(struct i2c_client *client, indio_dev->info = &bma180_info; if (client->irq > 0) { - data->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, + data->trig = iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); if (!data->trig) { ret = -ENOMEM; @@ -1059,7 +1059,6 @@ static int bma180_probe(struct i2c_client *client, goto err_trigger_free; } - data->trig->dev.parent = dev; data->trig->ops = &bma180_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); indio_dev->trig = iio_trigger_get(data->trig); diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index 3c9b0c6954e6..36fc9876dbca 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * BMA220 Digital triaxial acceleration sensor driver * * Copyright (c) 2016,2020 Intel Corporation. diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 7e425ebcd7ea..04d85ce34e9f 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -443,26 +443,32 @@ static bool bmc150_apply_acpi_orientation(struct device *dev, struct iio_mount_matrix *orientation) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct iio_dev *indio_dev = dev_get_drvdata(dev); struct acpi_device *adev = ACPI_COMPANION(dev); + char *name, *alt_name, *label, *str; union acpi_object *obj, *elements; - char *name, *alt_name, *str; acpi_status status; int i, j, val[3]; if (!adev || !acpi_dev_hid_uid_match(adev, "BOSC0200", NULL)) return false; - if (strcmp(dev_name(dev), "i2c-BOSC0200:base") == 0) + if (strcmp(dev_name(dev), "i2c-BOSC0200:base") == 0) { alt_name = "ROMK"; - else + label = "accel-base"; + } else { alt_name = "ROMS"; + label = "accel-display"; + } - if (acpi_has_method(adev->handle, "ROTM")) + if (acpi_has_method(adev->handle, "ROTM")) { name = "ROTM"; - else if (acpi_has_method(adev->handle, alt_name)) + } else if (acpi_has_method(adev->handle, alt_name)) { name = alt_name; - else + indio_dev->label = label; + } else { return false; + } status = acpi_evaluate_object(adev->handle, name, NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -1472,7 +1478,6 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev, break; } - t->indio_trig->dev.parent = dev; t->indio_trig->ops = &bmc150_accel_trigger_ops; t->intr = bmc150_accel_triggers[i].intr; t->data = data; diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c new file mode 100644 index 000000000000..12d00658e46f --- /dev/null +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -0,0 +1,567 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: + * - BMI088 + * + * Copyright (c) 2018-2021, Topic Embedded Products + */ + +#include <linux/delay.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <asm/unaligned.h> + +#include "bmi088-accel.h" + +#define BMI088_ACCEL_REG_CHIP_ID 0x00 +#define BMI088_ACCEL_REG_ERROR 0x02 + +#define BMI088_ACCEL_REG_INT_STATUS 0x1D +#define BMI088_ACCEL_INT_STATUS_BIT_DRDY BIT(7) + +#define BMI088_ACCEL_REG_RESET 0x7E +#define BMI088_ACCEL_RESET_VAL 0xB6 + +#define BMI088_ACCEL_REG_PWR_CTRL 0x7D +#define BMI088_ACCEL_REG_PWR_CONF 0x7C + +#define BMI088_ACCEL_REG_INT_MAP_DATA 0x58 +#define BMI088_ACCEL_INT_MAP_DATA_BIT_INT1_DRDY BIT(2) +#define BMI088_ACCEL_INT_MAP_DATA_BIT_INT2_FWM BIT(5) + +#define BMI088_ACCEL_REG_INT1_IO_CONF 0x53 +#define BMI088_ACCEL_INT1_IO_CONF_BIT_ENABLE_OUT BIT(3) +#define BMI088_ACCEL_INT1_IO_CONF_BIT_LVL BIT(1) + +#define BMI088_ACCEL_REG_INT2_IO_CONF 0x54 +#define BMI088_ACCEL_INT2_IO_CONF_BIT_ENABLE_OUT BIT(3) +#define BMI088_ACCEL_INT2_IO_CONF_BIT_LVL BIT(1) + +#define BMI088_ACCEL_REG_ACC_CONF 0x40 +#define BMI088_ACCEL_MODE_ODR_MASK 0x0f + +#define BMI088_ACCEL_REG_ACC_RANGE 0x41 +#define BMI088_ACCEL_RANGE_3G 0x00 +#define BMI088_ACCEL_RANGE_6G 0x01 +#define BMI088_ACCEL_RANGE_12G 0x02 +#define BMI088_ACCEL_RANGE_24G 0x03 + +#define BMI088_ACCEL_REG_TEMP 0x22 +#define BMI088_ACCEL_REG_TEMP_SHIFT 5 +#define BMI088_ACCEL_TEMP_UNIT 125 +#define BMI088_ACCEL_TEMP_OFFSET 23000 + +#define BMI088_ACCEL_REG_XOUT_L 0x12 +#define BMI088_ACCEL_AXIS_TO_REG(axis) \ + (BMI088_ACCEL_REG_XOUT_L + (axis * 2)) + +#define BMI088_ACCEL_MAX_STARTUP_TIME_US 1000 +#define BMI088_AUTO_SUSPEND_DELAY_MS 2000 + +#define BMI088_ACCEL_REG_FIFO_STATUS 0x0E +#define BMI088_ACCEL_REG_FIFO_CONFIG0 0x48 +#define BMI088_ACCEL_REG_FIFO_CONFIG1 0x49 +#define BMI088_ACCEL_REG_FIFO_DATA 0x3F +#define BMI088_ACCEL_FIFO_LENGTH 100 + +#define BMI088_ACCEL_FIFO_MODE_FIFO 0x40 +#define BMI088_ACCEL_FIFO_MODE_STREAM 0x80 + +enum bmi088_accel_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, +}; + +static const int bmi088_sample_freqs[] = { + 12, 500000, + 25, 0, + 50, 0, + 100, 0, + 200, 0, + 400, 0, + 800, 0, + 1600, 0, +}; + +/* Available OSR (over sampling rate) sets the 3dB cut-off frequency */ +enum bmi088_osr_modes { + BMI088_ACCEL_MODE_OSR_NORMAL = 0xA, + BMI088_ACCEL_MODE_OSR_2 = 0x9, + BMI088_ACCEL_MODE_OSR_4 = 0x8, +}; + +/* Available ODR (output data rates) in Hz */ +enum bmi088_odr_modes { + BMI088_ACCEL_MODE_ODR_12_5 = 0x5, + BMI088_ACCEL_MODE_ODR_25 = 0x6, + BMI088_ACCEL_MODE_ODR_50 = 0x7, + BMI088_ACCEL_MODE_ODR_100 = 0x8, + BMI088_ACCEL_MODE_ODR_200 = 0x9, + BMI088_ACCEL_MODE_ODR_400 = 0xa, + BMI088_ACCEL_MODE_ODR_800 = 0xb, + BMI088_ACCEL_MODE_ODR_1600 = 0xc, +}; + +struct bmi088_scale_info { + int scale; + u8 reg_range; +}; + +struct bmi088_accel_chip_info { + const char *name; + u8 chip_id; + const struct iio_chan_spec *channels; + int num_channels; +}; + +struct bmi088_accel_data { + struct regmap *regmap; + const struct bmi088_accel_chip_info *chip_info; + u8 buffer[2] ____cacheline_aligned; /* shared DMA safe buffer */ +}; + +static const struct regmap_range bmi088_volatile_ranges[] = { + /* All registers below 0x40 are volatile, except the CHIP ID. */ + regmap_reg_range(BMI088_ACCEL_REG_ERROR, 0x3f), + /* Mark the RESET as volatile too, it is self-clearing */ + regmap_reg_range(BMI088_ACCEL_REG_RESET, BMI088_ACCEL_REG_RESET), +}; + +static const struct regmap_access_table bmi088_volatile_table = { + .yes_ranges = bmi088_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(bmi088_volatile_ranges), +}; + +const struct regmap_config bmi088_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = 0x7E, + .volatile_table = &bmi088_volatile_table, + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL_GPL(bmi088_regmap_conf); + +static int bmi088_accel_power_up(struct bmi088_accel_data *data) +{ + int ret; + + /* Enable accelerometer and temperature sensor */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x4); + if (ret) + return ret; + + /* Datasheet recommends to wait at least 5ms before communication */ + usleep_range(5000, 6000); + + /* Disable suspend mode */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x0); + if (ret) + return ret; + + /* Recommended at least 1ms before further communication */ + usleep_range(1000, 1200); + + return 0; +} + +static int bmi088_accel_power_down(struct bmi088_accel_data *data) +{ + int ret; + + /* Enable suspend mode */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CONF, 0x3); + if (ret) + return ret; + + /* Recommended at least 1ms before further communication */ + usleep_range(1000, 1200); + + /* Disable accelerometer and temperature sensor */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_PWR_CTRL, 0x0); + if (ret) + return ret; + + /* Datasheet recommends to wait at least 5ms before communication */ + usleep_range(5000, 6000); + + return 0; +} + +static int bmi088_accel_get_sample_freq(struct bmi088_accel_data *data, + int *val, int *val2) +{ + unsigned int value; + int ret; + + ret = regmap_read(data->regmap, BMI088_ACCEL_REG_ACC_CONF, + &value); + if (ret) + return ret; + + value &= BMI088_ACCEL_MODE_ODR_MASK; + value -= BMI088_ACCEL_MODE_ODR_12_5; + value <<= 1; + + if (value >= ARRAY_SIZE(bmi088_sample_freqs) - 1) + return -EINVAL; + + *val = bmi088_sample_freqs[value]; + *val2 = bmi088_sample_freqs[value + 1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int bmi088_accel_set_sample_freq(struct bmi088_accel_data *data, int val) +{ + unsigned int regval; + int index = 0; + + while (index < ARRAY_SIZE(bmi088_sample_freqs) && + bmi088_sample_freqs[index] != val) + index += 2; + + if (index >= ARRAY_SIZE(bmi088_sample_freqs)) + return -EINVAL; + + regval = (index >> 1) + BMI088_ACCEL_MODE_ODR_12_5; + + return regmap_update_bits(data->regmap, BMI088_ACCEL_REG_ACC_CONF, + BMI088_ACCEL_MODE_ODR_MASK, regval); +} + +static int bmi088_accel_get_temp(struct bmi088_accel_data *data, int *val) +{ + int ret; + s16 temp; + + ret = regmap_bulk_read(data->regmap, BMI088_ACCEL_REG_TEMP, + &data->buffer, sizeof(__be16)); + if (ret) + return ret; + + /* data->buffer is cacheline aligned */ + temp = be16_to_cpu(*(__be16 *)data->buffer); + + *val = temp >> BMI088_ACCEL_REG_TEMP_SHIFT; + + return IIO_VAL_INT; +} + +static int bmi088_accel_get_axis(struct bmi088_accel_data *data, + struct iio_chan_spec const *chan, + int *val) +{ + int ret; + s16 raw_val; + + ret = regmap_bulk_read(data->regmap, + BMI088_ACCEL_AXIS_TO_REG(chan->scan_index), + data->buffer, sizeof(__le16)); + if (ret) + return ret; + + raw_val = le16_to_cpu(*(__le16 *)data->buffer); + *val = raw_val; + + return IIO_VAL_INT; +} + +static int bmi088_accel_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct bmi088_accel_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + pm_runtime_get_sync(dev); + ret = bmi088_accel_get_temp(data, val); + goto out_read_raw_pm_put; + case IIO_ACCEL: + pm_runtime_get_sync(dev); + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + goto out_read_raw_pm_put; + + ret = bmi088_accel_get_axis(data, chan, val); + iio_device_release_direct_mode(indio_dev); + if (!ret) + ret = IIO_VAL_INT; + + goto out_read_raw_pm_put; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + switch (chan->type) { + case IIO_TEMP: + /* Offset applies before scale */ + *val = BMI088_ACCEL_TEMP_OFFSET/BMI088_ACCEL_TEMP_UNIT; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_TEMP: + /* 0.125 degrees per LSB */ + *val = BMI088_ACCEL_TEMP_UNIT; + return IIO_VAL_INT; + case IIO_ACCEL: + pm_runtime_get_sync(dev); + ret = regmap_read(data->regmap, + BMI088_ACCEL_REG_ACC_RANGE, val); + if (ret) + goto out_read_raw_pm_put; + + *val2 = 15 - (*val & 0x3); + *val = 3 * 980; + ret = IIO_VAL_FRACTIONAL_LOG2; + + goto out_read_raw_pm_put; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + pm_runtime_get_sync(dev); + ret = bmi088_accel_get_sample_freq(data, val, val2); + goto out_read_raw_pm_put; + default: + break; + } + + return -EINVAL; + +out_read_raw_pm_put: + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + + return ret; +} + +static int bmi088_accel_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = bmi088_sample_freqs; + *length = ARRAY_SIZE(bmi088_sample_freqs); + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int bmi088_accel_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct bmi088_accel_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + pm_runtime_get_sync(dev); + ret = bmi088_accel_set_sample_freq(data, val); + pm_runtime_mark_last_busy(dev); + pm_runtime_put_autosuspend(dev); + return ret; + default: + return -EINVAL; + } +} + +#define BMI088_ACCEL_CHANNEL(_axis) { \ + .type = IIO_ACCEL, \ + .modified = 1, \ + .channel2 = IIO_MOD_##_axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = AXIS_##_axis, \ +} + +static const struct iio_chan_spec bmi088_accel_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .scan_index = -1, + }, + BMI088_ACCEL_CHANNEL(X), + BMI088_ACCEL_CHANNEL(Y), + BMI088_ACCEL_CHANNEL(Z), + IIO_CHAN_SOFT_TIMESTAMP(3), +}; + +static const struct bmi088_accel_chip_info bmi088_accel_chip_info_tbl[] = { + [0] = { + .name = "bmi088a", + .chip_id = 0x1E, + .channels = bmi088_accel_channels, + .num_channels = ARRAY_SIZE(bmi088_accel_channels), + }, +}; + +static const struct iio_info bmi088_accel_info = { + .read_raw = bmi088_accel_read_raw, + .write_raw = bmi088_accel_write_raw, + .read_avail = bmi088_accel_read_avail, +}; + +static const unsigned long bmi088_accel_scan_masks[] = { + BIT(AXIS_X) | BIT(AXIS_Y) | BIT(AXIS_Z), + 0 +}; + +static int bmi088_accel_chip_init(struct bmi088_accel_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret, i; + unsigned int val; + + /* Do a dummy read to enable SPI interface, won't harm I2C */ + regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val); + + /* + * Reset chip to get it in a known good state. A delay of 1ms after + * reset is required according to the data sheet + */ + ret = regmap_write(data->regmap, BMI088_ACCEL_REG_RESET, + BMI088_ACCEL_RESET_VAL); + if (ret) + return ret; + + usleep_range(1000, 2000); + + /* Do a dummy read again after a reset to enable the SPI interface */ + regmap_read(data->regmap, BMI088_ACCEL_REG_INT_STATUS, &val); + + /* Read chip ID */ + ret = regmap_read(data->regmap, BMI088_ACCEL_REG_CHIP_ID, &val); + if (ret) { + dev_err(dev, "Error: Reading chip id\n"); + return ret; + } + + /* Validate chip ID */ + for (i = 0; i < ARRAY_SIZE(bmi088_accel_chip_info_tbl); i++) { + if (bmi088_accel_chip_info_tbl[i].chip_id == val) { + data->chip_info = &bmi088_accel_chip_info_tbl[i]; + break; + } + } + if (i == ARRAY_SIZE(bmi088_accel_chip_info_tbl)) { + dev_err(dev, "Invalid chip %x\n", val); + return -ENODEV; + } + + return 0; +} + +int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, + int irq, const char *name, bool block_supported) +{ + struct bmi088_accel_data *data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + + data->regmap = regmap; + + ret = bmi088_accel_chip_init(data); + if (ret) + return ret; + + indio_dev->dev.parent = dev; + indio_dev->channels = data->chip_info->channels; + indio_dev->num_channels = data->chip_info->num_channels; + indio_dev->name = name ? name : data->chip_info->name; + indio_dev->available_scan_masks = bmi088_accel_scan_masks; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &bmi088_accel_info; + + /* Enable runtime PM */ + pm_runtime_get_noresume(dev); + pm_runtime_set_suspended(dev); + pm_runtime_enable(dev); + /* We need ~6ms to startup, so set the delay to 6 seconds */ + pm_runtime_set_autosuspend_delay(dev, 6000); + pm_runtime_use_autosuspend(dev); + pm_runtime_put(dev); + + ret = iio_device_register(indio_dev); + if (ret) + dev_err(dev, "Unable to register iio device\n"); + + return ret; +} +EXPORT_SYMBOL_GPL(bmi088_accel_core_probe); + + +int bmi088_accel_core_remove(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi088_accel_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + bmi088_accel_power_down(data); + + return 0; +} +EXPORT_SYMBOL_GPL(bmi088_accel_core_remove); + +static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi088_accel_data *data = iio_priv(indio_dev); + + return bmi088_accel_power_down(data); +} + +static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct bmi088_accel_data *data = iio_priv(indio_dev); + + return bmi088_accel_power_up(data); +} + +const struct dev_pm_ops bmi088_accel_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend, + bmi088_accel_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(bmi088_accel_pm_ops); + +MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMI088 accelerometer driver (core)"); diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c new file mode 100644 index 000000000000..dd1e3f6cf211 --- /dev/null +++ b/drivers/iio/accel/bmi088-accel-spi.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: + * - BMI088 + * + * Copyright (c) 2018-2020, Topic Embedded Products + */ + +#include <linux/module.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include "bmi088-accel.h" + +static int bmi088_regmap_spi_write(void *context, const void *data, size_t count) +{ + struct spi_device *spi = context; + + /* Write register is same as generic SPI */ + return spi_write(spi, data, count); +} + +static int bmi088_regmap_spi_read(void *context, const void *reg, + size_t reg_size, void *val, size_t val_size) +{ + struct spi_device *spi = context; + u8 addr[2]; + + addr[0] = *(u8 *)reg; + addr[0] |= BIT(7); /* Set RW = '1' */ + addr[1] = 0; /* Read requires a dummy byte transfer */ + + return spi_write_then_read(spi, addr, sizeof(addr), val, val_size); +} + +static struct regmap_bus bmi088_regmap_bus = { + .write = bmi088_regmap_spi_write, + .read = bmi088_regmap_spi_read, +}; + +static int bmi088_accel_probe(struct spi_device *spi) +{ + struct regmap *regmap; + const struct spi_device_id *id = spi_get_device_id(spi); + + regmap = devm_regmap_init(&spi->dev, &bmi088_regmap_bus, + spi, &bmi088_regmap_conf); + + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to initialize spi regmap\n"); + return PTR_ERR(regmap); + } + + return bmi088_accel_core_probe(&spi->dev, regmap, spi->irq, id->name, + true); +} + +static int bmi088_accel_remove(struct spi_device *spi) +{ + return bmi088_accel_core_remove(&spi->dev); +} + +static const struct spi_device_id bmi088_accel_id[] = { + {"bmi088-accel", }, + {} +}; +MODULE_DEVICE_TABLE(spi, bmi088_accel_id); + +static struct spi_driver bmi088_accel_driver = { + .driver = { + .name = "bmi088_accel_spi", + .pm = &bmi088_accel_pm_ops, + }, + .probe = bmi088_accel_probe, + .remove = bmi088_accel_remove, + .id_table = bmi088_accel_id, +}; +module_spi_driver(bmi088_accel_driver); + +MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("BMI088 accelerometer driver (SPI)"); diff --git a/drivers/iio/accel/bmi088-accel.h b/drivers/iio/accel/bmi088-accel.h new file mode 100644 index 000000000000..5c25f16b672c --- /dev/null +++ b/drivers/iio/accel/bmi088-accel.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef BMI088_ACCEL_H +#define BMI088_ACCEL_H + +#include <linux/pm.h> +#include <linux/regmap.h> +#include <linux/types.h> + +struct device; + +extern const struct regmap_config bmi088_regmap_conf; +extern const struct dev_pm_ops bmi088_accel_pm_ops; + +int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, + const char *name, bool block_supported); +int bmi088_accel_core_remove(struct device *dev); + +#endif /* BMI088_ACCEL_H */ diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c index 8f1232c38e0d..b6f3471b62dc 100644 --- a/drivers/iio/accel/cros_ec_accel_legacy.c +++ b/drivers/iio/accel/cros_ec_accel_legacy.c @@ -215,7 +215,7 @@ static int cros_ec_accel_legacy_probe(struct platform_device *pdev) return -ENOMEM; ret = cros_ec_sensors_core_init(pdev, indio_dev, true, - cros_ec_sensors_capture, NULL, false); + cros_ec_sensors_capture, NULL); if (ret) return ret; diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c index 4472dde6899e..5edff9ba72da 100644 --- a/drivers/iio/accel/da280.c +++ b/drivers/iio/accel/da280.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IIO driver for the MiraMEMS DA280 3-axis accelerometer and * IIO driver for the MiraMEMS DA226 2-axis accelerometer * diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c index 3b3df620ba27..92593a1cd1aa 100644 --- a/drivers/iio/accel/da311.c +++ b/drivers/iio/accel/da311.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IIO driver for the MiraMEMS DA311 3-axis accelerometer * * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> diff --git a/drivers/iio/accel/dmard10.c b/drivers/iio/accel/dmard10.c index 90206f015857..e84bf8db1e89 100644 --- a/drivers/iio/accel/dmard10.c +++ b/drivers/iio/accel/dmard10.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * IIO driver for the 3-axis accelerometer Domintech ARD10. * * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 5d63ed19e6e2..2f9465cb382f 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -43,6 +43,10 @@ static const u32 accel_3d_addresses[ACCEL_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_ACCEL_Z_AXIS }; +static const u32 accel_3d_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ACCELERATION, +}; + /* Channel definitions */ static const struct iio_chan_spec accel_3d_channels[] = { { @@ -317,18 +321,6 @@ static int accel_3d_parse_report(struct platform_device *pdev, &st->accel[CHANNEL_SCAN_INDEX_X], &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ACCELERATION, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } - return ret; } @@ -366,8 +358,11 @@ static int hid_accel_3d_probe(struct platform_device *pdev) channel_size = sizeof(gravity_channels); indio_dev->num_channels = ARRAY_SIZE(gravity_channels); } - ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage, - &accel_state->common_attributes); + ret = hid_sensor_parse_common_attributes(hsdev, + hsdev->usage, + &accel_state->common_attributes, + accel_3d_sensitivity_addresses, + ARRAY_SIZE(accel_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 2fadafc860fd..ff724bc17a45 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -1284,7 +1284,8 @@ static irqreturn_t kxcjk1013_data_rdy_trig_poll(int irq, void *private) static const char *kxcjk1013_match_acpi_device(struct device *dev, enum kx_chipset *chipset, - enum kx_acpi_type *acpi_type) + enum kx_acpi_type *acpi_type, + const char **label) { const struct acpi_device_id *id; @@ -1292,10 +1293,14 @@ static const char *kxcjk1013_match_acpi_device(struct device *dev, if (!id) return NULL; - if (strcmp(id->id, "SMO8500") == 0) + if (strcmp(id->id, "SMO8500") == 0) { *acpi_type = ACPI_SMO8500; - else if (strcmp(id->id, "KIOX010A") == 0) + } else if (strcmp(id->id, "KIOX010A") == 0) { *acpi_type = ACPI_KIOX010A; + *label = "accel-display"; + } else if (strcmp(id->id, "KIOX020A") == 0) { + *label = "accel-base"; + } *chipset = (enum kx_chipset)id->driver_data; @@ -1368,7 +1373,8 @@ static int kxcjk1013_probe(struct i2c_client *client, } else if (ACPI_HANDLE(&client->dev)) { name = kxcjk1013_match_acpi_device(&client->dev, &data->chipset, - &data->acpi_type); + &data->acpi_type, + &indio_dev->label); } else return -ENODEV; @@ -1413,7 +1419,6 @@ static int kxcjk1013_probe(struct i2c_client *client, goto err_poweroff; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &kxcjk1013_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); indio_dev->trig = data->dready_trig; @@ -1422,7 +1427,6 @@ static int kxcjk1013_probe(struct i2c_client *client, if (ret) goto err_poweroff; - data->motion_trig->dev.parent = &client->dev; data->motion_trig->ops = &kxcjk1013_trigger_ops; iio_trigger_set_drvdata(data->motion_trig, indio_dev); ret = iio_trigger_register(data->motion_trig); diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c index 46e4283fc037..735002b716f3 100644 --- a/drivers/iio/accel/mc3230.c +++ b/drivers/iio/accel/mc3230.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-or-later -/** +/* * mCube MC3230 3-Axis Accelerometer * * Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com> diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c index b3c9136d51ec..47f5cd66e996 100644 --- a/drivers/iio/accel/mma7660.c +++ b/drivers/iio/accel/mma7660.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Freescale MMA7660FC 3-Axis Accelerometer * * Copyright (c) 2016, Intel Corporation. diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index b0176d936423..4d307dfb9169 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -58,7 +58,7 @@ #define MMA8452_FF_MT_THS 0x17 #define MMA8452_FF_MT_THS_MASK 0x7f #define MMA8452_FF_MT_COUNT 0x18 -#define MMA8452_FF_MT_CHAN_SHIFT 3 +#define MMA8452_FF_MT_CHAN_SHIFT 3 #define MMA8452_TRANSIENT_CFG 0x1d #define MMA8452_TRANSIENT_CFG_CHAN(chan) BIT(chan + 1) #define MMA8452_TRANSIENT_CFG_HPF_BYP BIT(0) @@ -70,7 +70,7 @@ #define MMA8452_TRANSIENT_THS 0x1f #define MMA8452_TRANSIENT_THS_MASK GENMASK(6, 0) #define MMA8452_TRANSIENT_COUNT 0x20 -#define MMA8452_TRANSIENT_CHAN_SHIFT 1 +#define MMA8452_TRANSIENT_CHAN_SHIFT 1 #define MMA8452_CTRL_REG1 0x2a #define MMA8452_CTRL_ACTIVE BIT(0) #define MMA8452_CTRL_DR_MASK GENMASK(5, 3) @@ -134,33 +134,33 @@ struct mma8452_data { * used for different chips and the relevant registers are included here. */ struct mma8452_event_regs { - u8 ev_cfg; - u8 ev_cfg_ele; - u8 ev_cfg_chan_shift; - u8 ev_src; - u8 ev_ths; - u8 ev_ths_mask; - u8 ev_count; + u8 ev_cfg; + u8 ev_cfg_ele; + u8 ev_cfg_chan_shift; + u8 ev_src; + u8 ev_ths; + u8 ev_ths_mask; + u8 ev_count; }; static const struct mma8452_event_regs ff_mt_ev_regs = { - .ev_cfg = MMA8452_FF_MT_CFG, - .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, - .ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT, - .ev_src = MMA8452_FF_MT_SRC, - .ev_ths = MMA8452_FF_MT_THS, - .ev_ths_mask = MMA8452_FF_MT_THS_MASK, - .ev_count = MMA8452_FF_MT_COUNT + .ev_cfg = MMA8452_FF_MT_CFG, + .ev_cfg_ele = MMA8452_FF_MT_CFG_ELE, + .ev_cfg_chan_shift = MMA8452_FF_MT_CHAN_SHIFT, + .ev_src = MMA8452_FF_MT_SRC, + .ev_ths = MMA8452_FF_MT_THS, + .ev_ths_mask = MMA8452_FF_MT_THS_MASK, + .ev_count = MMA8452_FF_MT_COUNT }; static const struct mma8452_event_regs trans_ev_regs = { - .ev_cfg = MMA8452_TRANSIENT_CFG, - .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, - .ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT, - .ev_src = MMA8452_TRANSIENT_SRC, - .ev_ths = MMA8452_TRANSIENT_THS, - .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, - .ev_count = MMA8452_TRANSIENT_COUNT, + .ev_cfg = MMA8452_TRANSIENT_CFG, + .ev_cfg_ele = MMA8452_TRANSIENT_CFG_ELE, + .ev_cfg_chan_shift = MMA8452_TRANSIENT_CHAN_SHIFT, + .ev_src = MMA8452_TRANSIENT_SRC, + .ev_ths = MMA8452_TRANSIENT_THS, + .ev_ths_mask = MMA8452_TRANSIENT_THS_MASK, + .ev_count = MMA8452_TRANSIENT_COUNT, }; /** @@ -1465,7 +1465,6 @@ static int mma8452_trigger_setup(struct iio_dev *indio_dev) if (!trig) return -ENOMEM; - trig->dev.parent = &data->client->dev; trig->ops = &mma8452_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 0f8fd687866d..fb3cbaa62bd8 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -450,7 +450,6 @@ static int mxc4005_probe(struct i2c_client *client, return ret; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &mxc4005_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); indio_dev->trig = data->dready_trig; diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c index 194738660523..cb753a43533c 100644 --- a/drivers/iio/accel/sca3000.c +++ b/drivers/iio/accel/sca3000.c @@ -351,7 +351,7 @@ static int __sca3000_unlock_reg_lock(struct sca3000_state *st) } /** - * sca3000_write_ctrl_reg() write to a lock protect ctrl register + * sca3000_write_ctrl_reg() - write to a lock protect ctrl register * @st: Driver specific device instance data. * @sel: selects which registers we wish to write to * @val: the value to be written @@ -389,7 +389,7 @@ error_ret: } /** - * sca3000_read_ctrl_reg() read from lock protected control register. + * sca3000_read_ctrl_reg() - read from lock protected control register. * @st: Driver specific device instance data. * @ctrl_reg: Which ctrl register do we want to read. * @@ -421,7 +421,7 @@ error_ret: } /** - * sca3000_show_rev() - sysfs interface to read the chip revision number + * sca3000_print_rev() - sysfs interface to read the chip revision number * @indio_dev: Device instance specific generic IIO data. * Driver specific device instance data can be obtained via * via iio_priv(indio_dev) @@ -902,7 +902,7 @@ static int sca3000_read_event_value(struct iio_dev *indio_dev, } /** - * sca3000_write_value() - control of threshold and period + * sca3000_write_event_value() - control of threshold and period * @indio_dev: Device instance specific IIO information. * @chan: Description of the channel for which the event is being * configured. @@ -1272,20 +1272,6 @@ static int sca3000_write_event_config(struct iio_dev *indio_dev, return ret; } -static int sca3000_configure_ring(struct iio_dev *indio_dev) -{ - struct iio_buffer *buffer; - - buffer = devm_iio_kfifo_allocate(&indio_dev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->modes |= INDIO_BUFFER_SOFTWARE; - - return 0; -} - static inline int __sca3000_hw_ring_state_set(struct iio_dev *indio_dev, bool state) { @@ -1479,7 +1465,9 @@ static int sca3000_probe(struct spi_device *spi) } indio_dev->modes = INDIO_DIRECT_MODE; - ret = sca3000_configure_ring(indio_dev); + ret = devm_iio_kfifo_buffer_setup(&spi->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &sca3000_ring_setup_ops); if (ret) return ret; @@ -1493,7 +1481,6 @@ static int sca3000_probe(struct spi_device *spi) if (ret) return ret; } - indio_dev->setup_ops = &sca3000_ring_setup_ops; ret = sca3000_clean_setup(st); if (ret) goto error_free_irq; diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c index 474477e91b5e..04dcb2b657ee 100644 --- a/drivers/iio/accel/ssp_accel_sensor.c +++ b/drivers/iio/accel/ssp_accel_sensor.c @@ -96,7 +96,6 @@ static int ssp_accel_probe(struct platform_device *pdev) int ret; struct iio_dev *indio_dev; struct ssp_sensor_data *spd; - struct iio_buffer *buffer; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd)); if (!indio_dev) @@ -109,18 +108,15 @@ static int ssp_accel_probe(struct platform_device *pdev) indio_dev->name = ssp_accel_device_name; indio_dev->info = &ssp_accel_iio_info; - indio_dev->modes = INDIO_BUFFER_SOFTWARE; indio_dev->channels = ssp_acc_channels; indio_dev->num_channels = ARRAY_SIZE(ssp_acc_channels); indio_dev->available_scan_masks = ssp_accel_scan_mask; - buffer = devm_iio_kfifo_allocate(&pdev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - - indio_dev->setup_ops = &ssp_accel_buffer_ops; + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &ssp_accel_buffer_ops); + if (ret) + return ret; platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 3b59887a8581..157d8faefb9e 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Sensortek STK8312 3-Axis Accelerometer * * Copyright (c) 2015, Intel Corporation. @@ -558,7 +558,6 @@ static int stk8312_probe(struct i2c_client *client, goto err_power_off; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &stk8312_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index 3ead378b02c9..7cf9cb7e8666 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Sensortek STK8BA50 3-Axis Accelerometer * * Copyright (c) 2015, Intel Corporation. @@ -454,7 +454,6 @@ static int stk8ba50_probe(struct i2c_client *client, goto err_power_off; } - data->dready_trig->dev.parent = &client->dev; data->dready_trig->ops = &stk8ba50_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index e0667c4b3c08..c7946c439612 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -97,7 +97,7 @@ config AD7298 module will be called ad7298. config AD7476 - tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD an TI" + tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD and TI" depends on SPI select IIO_BUFFER select IIO_TRIGGERED_BUFFER @@ -249,7 +249,7 @@ config AD799X config AD9467 tristate "Analog Devices AD9467 High Speed ADC driver" depends on SPI - select ADI_AXI_ADC + depends on ADI_AXI_ADC help Say yes here to build support for Analog Devices: * AD9467 16-Bit, 200 MSPS/250 MSPS Analog-to-Digital Converter @@ -1154,6 +1154,18 @@ config TI_ADS124S08 This driver can also be built as a module. If so, the module will be called ti-ads124s08. +config TI_ADS131E08 + tristate "Texas Instruments ADS131E08" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to get support for Texas Instruments ADS131E04, ADS131E06 + and ADS131E08 chips. + + This driver can also be built as a module. If so, the module will be + called ti-ads131e08. + config TI_AM335X_ADC tristate "TI's AM335X ADC driver" depends on MFD_TI_AM335X_TSCADC && HAS_DMA diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index 5fca90ada0ec..a226657d19c0 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -103,6 +103,7 @@ obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o +obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index 766c73333604..9d3952b4674f 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -5,12 +5,14 @@ * Copyright 2018 Analog Devices Inc. */ #include <linux/bitfield.h> +#include <linux/bitops.h> #include <linux/clk.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/err.h> #include <linux/interrupt.h> #include <linux/kernel.h> +#include <linux/kfifo.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/regulator/consumer.h> @@ -86,6 +88,10 @@ #define AD7124_SINC3_FILTER 2 #define AD7124_SINC4_FILTER 0 +#define AD7124_CONF_ADDR_OFFSET 20 +#define AD7124_MAX_CONFIGS 8 +#define AD7124_MAX_CHANNELS 16 + enum ad7124_ids { ID_AD7124_4, ID_AD7124_8, @@ -136,25 +142,37 @@ struct ad7124_chip_info { }; struct ad7124_channel_config { + bool live; + unsigned int cfg_slot; enum ad7124_ref_sel refsel; bool bipolar; bool buf_positive; bool buf_negative; - unsigned int ain; unsigned int vref_mv; unsigned int pga_bits; unsigned int odr; + unsigned int odr_sel_bits; unsigned int filter_type; }; +struct ad7124_channel { + unsigned int nr; + struct ad7124_channel_config cfg; + unsigned int ain; + unsigned int slot; +}; + struct ad7124_state { const struct ad7124_chip_info *chip_info; struct ad_sigma_delta sd; - struct ad7124_channel_config *channel_config; + struct ad7124_channel *channels; struct regulator *vref[4]; struct clk *mclk; unsigned int adc_control; unsigned int num_channels; + struct mutex cfgs_lock; /* lock for configs access */ + unsigned long cfg_slots_status; /* bitmap with slot status (1 means it is used) */ + DECLARE_KFIFO(live_cfgs_fifo, struct ad7124_channel_config *, AD7124_MAX_CONFIGS); }; static const struct iio_chan_spec ad7124_channel_template = { @@ -238,33 +256,9 @@ static int ad7124_set_mode(struct ad_sigma_delta *sd, return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control); } -static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel) -{ - struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); - unsigned int val; - - val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) | - AD7124_CHANNEL_SETUP(channel); - - return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(channel), 2, val); -} - -static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { - .set_channel = ad7124_set_channel, - .set_mode = ad7124_set_mode, - .has_registers = true, - .addr_shift = 0, - .read_mask = BIT(6), - .data_reg = AD7124_DATA, - .irq_flags = IRQF_TRIGGER_FALLING, -}; - -static int ad7124_set_channel_odr(struct ad7124_state *st, - unsigned int channel, - unsigned int odr) +static void ad7124_set_channel_odr(struct ad7124_state *st, unsigned int channel, unsigned int odr) { unsigned int fclk, odr_sel_bits; - int ret; fclk = clk_get_rate(st->mclk); /* @@ -280,36 +274,12 @@ static int ad7124_set_channel_odr(struct ad7124_state *st, else if (odr_sel_bits > 2047) odr_sel_bits = 2047; - ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel), - AD7124_FILTER_FS_MSK, - AD7124_FILTER_FS(odr_sel_bits), 3); - if (ret < 0) - return ret; - /* fADC = fCLK / (FS[10:0] x 32) */ - st->channel_config[channel].odr = - DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32); - - return 0; -} - -static int ad7124_set_channel_gain(struct ad7124_state *st, - unsigned int channel, - unsigned int gain) -{ - unsigned int res; - int ret; + if (odr_sel_bits != st->channels[channel].cfg.odr_sel_bits) + st->channels[channel].cfg.live = false; - res = ad7124_find_closest_match(ad7124_gain, - ARRAY_SIZE(ad7124_gain), gain); - ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel), - AD7124_CONFIG_PGA_MSK, - AD7124_CONFIG_PGA(res), 2); - if (ret < 0) - return ret; - - st->channel_config[channel].pga_bits = res; - - return 0; + /* fADC = fCLK / (FS[10:0] x 32) */ + st->channels[channel].cfg.odr = DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32); + st->channels[channel].cfg.odr_sel_bits = odr_sel_bits; } static int ad7124_get_3db_filter_freq(struct ad7124_state *st, @@ -317,9 +287,9 @@ static int ad7124_get_3db_filter_freq(struct ad7124_state *st, { unsigned int fadc; - fadc = st->channel_config[channel].odr; + fadc = st->channels[channel].cfg.odr; - switch (st->channel_config[channel].filter_type) { + switch (st->channels[channel].cfg.filter_type) { case AD7124_SINC3_FILTER: return DIV_ROUND_CLOSEST(fadc * 230, 1000); case AD7124_SINC4_FILTER: @@ -329,9 +299,8 @@ static int ad7124_get_3db_filter_freq(struct ad7124_state *st, } } -static int ad7124_set_3db_filter_freq(struct ad7124_state *st, - unsigned int channel, - unsigned int freq) +static void ad7124_set_3db_filter_freq(struct ad7124_state *st, unsigned int channel, + unsigned int freq) { unsigned int sinc4_3db_odr; unsigned int sinc3_3db_odr; @@ -349,21 +318,211 @@ static int ad7124_set_3db_filter_freq(struct ad7124_state *st, new_odr = sinc3_3db_odr; } - if (st->channel_config[channel].filter_type != new_filter) { - int ret; + if (new_odr != st->channels[channel].cfg.odr) + st->channels[channel].cfg.live = false; - st->channel_config[channel].filter_type = new_filter; - ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel), - AD7124_FILTER_TYPE_MSK, - AD7124_FILTER_TYPE_SEL(new_filter), - 3); - if (ret < 0) - return ret; + st->channels[channel].cfg.filter_type = new_filter; + st->channels[channel].cfg.odr = new_odr; +} + +static struct ad7124_channel_config *ad7124_find_similar_live_cfg(struct ad7124_state *st, + struct ad7124_channel_config *cfg) +{ + struct ad7124_channel_config *cfg_aux; + ptrdiff_t cmp_size; + int i; + + cmp_size = (u8 *)&cfg->live - (u8 *)cfg; + for (i = 0; i < st->num_channels; i++) { + cfg_aux = &st->channels[i].cfg; + + if (cfg_aux->live && !memcmp(cfg, cfg_aux, cmp_size)) + return cfg_aux; + } + + return NULL; +} + +static int ad7124_find_free_config_slot(struct ad7124_state *st) +{ + unsigned int free_cfg_slot; + + free_cfg_slot = find_next_zero_bit(&st->cfg_slots_status, AD7124_MAX_CONFIGS, 0); + if (free_cfg_slot == AD7124_MAX_CONFIGS) + return -1; + + return free_cfg_slot; +} + +static int ad7124_init_config_vref(struct ad7124_state *st, struct ad7124_channel_config *cfg) +{ + unsigned int refsel = cfg->refsel; + + switch (refsel) { + case AD7124_REFIN1: + case AD7124_REFIN2: + case AD7124_AVDD_REF: + if (IS_ERR(st->vref[refsel])) { + dev_err(&st->sd.spi->dev, + "Error, trying to use external voltage reference without a %s regulator.\n", + ad7124_ref_names[refsel]); + return PTR_ERR(st->vref[refsel]); + } + cfg->vref_mv = regulator_get_voltage(st->vref[refsel]); + /* Conversion from uV to mV */ + cfg->vref_mv /= 1000; + return 0; + case AD7124_INT_REF: + cfg->vref_mv = 2500; + st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK; + st->adc_control |= AD7124_ADC_CTRL_REF_EN(1); + return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, + 2, st->adc_control); + default: + dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel); + return -EINVAL; + } +} + +static int ad7124_write_config(struct ad7124_state *st, struct ad7124_channel_config *cfg, + unsigned int cfg_slot) +{ + unsigned int tmp; + unsigned int val; + int ret; + + cfg->cfg_slot = cfg_slot; + + tmp = (cfg->buf_positive << 1) + cfg->buf_negative; + val = AD7124_CONFIG_BIPOLAR(cfg->bipolar) | AD7124_CONFIG_REF_SEL(cfg->refsel) | + AD7124_CONFIG_IN_BUFF(tmp); + ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(cfg->cfg_slot), 2, val); + if (ret < 0) + return ret; + + tmp = AD7124_FILTER_TYPE_SEL(cfg->filter_type); + ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_TYPE_MSK, + tmp, 3); + if (ret < 0) + return ret; + + ret = ad7124_spi_write_mask(st, AD7124_FILTER(cfg->cfg_slot), AD7124_FILTER_FS_MSK, + AD7124_FILTER_FS(cfg->odr_sel_bits), 3); + if (ret < 0) + return ret; + + return ad7124_spi_write_mask(st, AD7124_CONFIG(cfg->cfg_slot), AD7124_CONFIG_PGA_MSK, + AD7124_CONFIG_PGA(cfg->pga_bits), 2); +} + +static struct ad7124_channel_config *ad7124_pop_config(struct ad7124_state *st) +{ + struct ad7124_channel_config *lru_cfg; + struct ad7124_channel_config *cfg; + int ret; + int i; + + /* + * Pop least recently used config from the fifo + * in order to make room for the new one + */ + ret = kfifo_get(&st->live_cfgs_fifo, &lru_cfg); + if (ret <= 0) + return NULL; + + lru_cfg->live = false; + + /* mark slot as free */ + assign_bit(lru_cfg->cfg_slot, &st->cfg_slots_status, 0); + + /* invalidate all other configs that pointed to this one */ + for (i = 0; i < st->num_channels; i++) { + cfg = &st->channels[i].cfg; + + if (cfg->cfg_slot == lru_cfg->cfg_slot) + cfg->live = false; + } + + return lru_cfg; +} + +static int ad7124_push_config(struct ad7124_state *st, struct ad7124_channel_config *cfg) +{ + struct ad7124_channel_config *lru_cfg; + int free_cfg_slot; + + free_cfg_slot = ad7124_find_free_config_slot(st); + if (free_cfg_slot >= 0) { + /* push the new config in configs queue */ + kfifo_put(&st->live_cfgs_fifo, cfg); + } else { + /* pop one config to make room for the new one */ + lru_cfg = ad7124_pop_config(st); + if (!lru_cfg) + return -EINVAL; + + /* push the new config in configs queue */ + free_cfg_slot = lru_cfg->cfg_slot; + kfifo_put(&st->live_cfgs_fifo, cfg); + } + + /* mark slot as used */ + assign_bit(free_cfg_slot, &st->cfg_slots_status, 1); + + return ad7124_write_config(st, cfg, free_cfg_slot); +} + +static int ad7124_enable_channel(struct ad7124_state *st, struct ad7124_channel *ch) +{ + ch->cfg.live = true; + return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(ch->nr), 2, ch->ain | + AD7124_CHANNEL_SETUP(ch->cfg.cfg_slot) | AD7124_CHANNEL_EN(1)); +} + +static int ad7124_prepare_read(struct ad7124_state *st, int address) +{ + struct ad7124_channel_config *cfg = &st->channels[address].cfg; + struct ad7124_channel_config *live_cfg; + + /* + * Before doing any reads assign the channel a configuration. + * Check if channel's config is on the device + */ + if (!cfg->live) { + /* check if config matches another one */ + live_cfg = ad7124_find_similar_live_cfg(st, cfg); + if (!live_cfg) + ad7124_push_config(st, cfg); + else + cfg->cfg_slot = live_cfg->cfg_slot; } - return ad7124_set_channel_odr(st, channel, new_odr); + /* point channel to the config slot and enable */ + return ad7124_enable_channel(st, &st->channels[address]); } +static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7124_state *st = container_of(sd, struct ad7124_state, sd); + int ret; + + mutex_lock(&st->cfgs_lock); + ret = ad7124_prepare_read(st, channel); + mutex_unlock(&st->cfgs_lock); + + return ret; +} + +static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { + .set_channel = ad7124_set_channel, + .set_mode = ad7124_set_mode, + .has_registers = true, + .addr_shift = 0, + .read_mask = BIT(6), + .data_reg = AD7124_DATA, + .irq_flags = IRQF_TRIGGER_FALLING +}; + static int ad7124_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) @@ -378,36 +537,44 @@ static int ad7124_read_raw(struct iio_dev *indio_dev, return ret; /* After the conversion is performed, disable the channel */ - ret = ad_sd_write_reg(&st->sd, - AD7124_CHANNEL(chan->address), 2, - st->channel_config[chan->address].ain | - AD7124_CHANNEL_EN(0)); + ret = ad_sd_write_reg(&st->sd, AD7124_CHANNEL(chan->address), 2, + st->channels[chan->address].ain | AD7124_CHANNEL_EN(0)); if (ret < 0) return ret; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - idx = st->channel_config[chan->address].pga_bits; - *val = st->channel_config[chan->address].vref_mv; - if (st->channel_config[chan->address].bipolar) + mutex_lock(&st->cfgs_lock); + + idx = st->channels[chan->address].cfg.pga_bits; + *val = st->channels[chan->address].cfg.vref_mv; + if (st->channels[chan->address].cfg.bipolar) *val2 = chan->scan_type.realbits - 1 + idx; else *val2 = chan->scan_type.realbits + idx; + mutex_unlock(&st->cfgs_lock); return IIO_VAL_FRACTIONAL_LOG2; case IIO_CHAN_INFO_OFFSET: - if (st->channel_config[chan->address].bipolar) + mutex_lock(&st->cfgs_lock); + if (st->channels[chan->address].cfg.bipolar) *val = -(1 << (chan->scan_type.realbits - 1)); else *val = 0; + mutex_unlock(&st->cfgs_lock); return IIO_VAL_INT; case IIO_CHAN_INFO_SAMP_FREQ: - *val = st->channel_config[chan->address].odr; + mutex_lock(&st->cfgs_lock); + *val = st->channels[chan->address].cfg.odr; + mutex_unlock(&st->cfgs_lock); return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + mutex_lock(&st->cfgs_lock); *val = ad7124_get_3db_filter_freq(st, chan->scan_index); + mutex_unlock(&st->cfgs_lock); + return IIO_VAL_INT; default: return -EINVAL; @@ -420,35 +587,54 @@ static int ad7124_write_raw(struct iio_dev *indio_dev, { struct ad7124_state *st = iio_priv(indio_dev); unsigned int res, gain, full_scale, vref; + int ret = 0; + + mutex_lock(&st->cfgs_lock); switch (info) { case IIO_CHAN_INFO_SAMP_FREQ: - if (val2 != 0) - return -EINVAL; + if (val2 != 0) { + ret = -EINVAL; + break; + } - return ad7124_set_channel_odr(st, chan->address, val); + ad7124_set_channel_odr(st, chan->address, val); + break; case IIO_CHAN_INFO_SCALE: - if (val != 0) - return -EINVAL; + if (val != 0) { + ret = -EINVAL; + break; + } - if (st->channel_config[chan->address].bipolar) + if (st->channels[chan->address].cfg.bipolar) full_scale = 1 << (chan->scan_type.realbits - 1); else full_scale = 1 << chan->scan_type.realbits; - vref = st->channel_config[chan->address].vref_mv * 1000000LL; + vref = st->channels[chan->address].cfg.vref_mv * 1000000LL; res = DIV_ROUND_CLOSEST(vref, full_scale); gain = DIV_ROUND_CLOSEST(res, val2); + res = ad7124_find_closest_match(ad7124_gain, ARRAY_SIZE(ad7124_gain), gain); + + if (st->channels[chan->address].cfg.pga_bits != res) + st->channels[chan->address].cfg.live = false; - return ad7124_set_channel_gain(st, chan->address, gain); + st->channels[chan->address].cfg.pga_bits = res; + break; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - if (val2 != 0) - return -EINVAL; + if (val2 != 0) { + ret = -EINVAL; + break; + } - return ad7124_set_3db_filter_freq(st, chan->address, val); + ad7124_set_3db_filter_freq(st, chan->address, val); + break; default: - return -EINVAL; + ret = -EINVAL; } + + mutex_unlock(&st->cfgs_lock); + return ret; } static int ad7124_reg_access(struct iio_dev *indio_dev, @@ -547,47 +733,14 @@ static int ad7124_check_chip_id(struct ad7124_state *st) return 0; } -static int ad7124_init_channel_vref(struct ad7124_state *st, - unsigned int channel_number) -{ - unsigned int refsel = st->channel_config[channel_number].refsel; - - switch (refsel) { - case AD7124_REFIN1: - case AD7124_REFIN2: - case AD7124_AVDD_REF: - if (IS_ERR(st->vref[refsel])) { - dev_err(&st->sd.spi->dev, - "Error, trying to use external voltage reference without a %s regulator.\n", - ad7124_ref_names[refsel]); - return PTR_ERR(st->vref[refsel]); - } - st->channel_config[channel_number].vref_mv = - regulator_get_voltage(st->vref[refsel]); - /* Conversion from uV to mV */ - st->channel_config[channel_number].vref_mv /= 1000; - break; - case AD7124_INT_REF: - st->channel_config[channel_number].vref_mv = 2500; - st->adc_control &= ~AD7124_ADC_CTRL_REF_EN_MSK; - st->adc_control |= AD7124_ADC_CTRL_REF_EN(1); - return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, - 2, st->adc_control); - default: - dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel); - return -EINVAL; - } - - return 0; -} - static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, struct device_node *np) { struct ad7124_state *st = iio_priv(indio_dev); + struct ad7124_channel_config *cfg; + struct ad7124_channel *channels; struct device_node *child; struct iio_chan_spec *chan; - struct ad7124_channel_config *chan_config; unsigned int ain[2], channel = 0, tmp; int ret; @@ -602,16 +755,18 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, if (!chan) return -ENOMEM; - chan_config = devm_kcalloc(indio_dev->dev.parent, st->num_channels, - sizeof(*chan_config), GFP_KERNEL); - if (!chan_config) + channels = devm_kcalloc(indio_dev->dev.parent, st->num_channels, sizeof(*channels), + GFP_KERNEL); + if (!channels) return -ENOMEM; indio_dev->channels = chan; indio_dev->num_channels = st->num_channels; - st->channel_config = chan_config; + st->channels = channels; for_each_available_child_of_node(np, child) { + cfg = &st->channels[channel].cfg; + ret = of_property_read_u32(child, "reg", &channel); if (ret) goto err; @@ -621,21 +776,20 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, if (ret) goto err; - st->channel_config[channel].ain = AD7124_CHANNEL_AINP(ain[0]) | + st->channels[channel].nr = channel; + st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) | AD7124_CHANNEL_AINM(ain[1]); - st->channel_config[channel].bipolar = - of_property_read_bool(child, "bipolar"); + + cfg->bipolar = of_property_read_bool(child, "bipolar"); ret = of_property_read_u32(child, "adi,reference-select", &tmp); if (ret) - st->channel_config[channel].refsel = AD7124_INT_REF; + cfg->refsel = AD7124_INT_REF; else - st->channel_config[channel].refsel = tmp; + cfg->refsel = tmp; - st->channel_config[channel].buf_positive = - of_property_read_bool(child, "adi,buffered-positive"); - st->channel_config[channel].buf_negative = - of_property_read_bool(child, "adi,buffered-negative"); + cfg->buf_positive = of_property_read_bool(child, "adi,buffered-positive"); + cfg->buf_negative = of_property_read_bool(child, "adi,buffered-negative"); chan[channel] = ad7124_channel_template; chan[channel].address = channel; @@ -653,8 +807,8 @@ err: static int ad7124_setup(struct ad7124_state *st) { - unsigned int val, fclk, power_mode; - int i, ret, tmp; + unsigned int fclk, power_mode; + int i, ret; fclk = clk_get_rate(st->mclk); if (!fclk) @@ -677,31 +831,20 @@ static int ad7124_setup(struct ad7124_state *st) if (ret < 0) return ret; + mutex_init(&st->cfgs_lock); + INIT_KFIFO(st->live_cfgs_fifo); for (i = 0; i < st->num_channels; i++) { - val = st->channel_config[i].ain | AD7124_CHANNEL_SETUP(i); - ret = ad_sd_write_reg(&st->sd, AD7124_CHANNEL(i), 2, val); - if (ret < 0) - return ret; - ret = ad7124_init_channel_vref(st, i); + ret = ad7124_init_config_vref(st, &st->channels[i].cfg); if (ret < 0) return ret; - tmp = (st->channel_config[i].buf_positive << 1) + - st->channel_config[i].buf_negative; - - val = AD7124_CONFIG_BIPOLAR(st->channel_config[i].bipolar) | - AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel) | - AD7124_CONFIG_IN_BUFF(tmp); - ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(i), 2, val); - if (ret < 0) - return ret; /* * 9.38 SPS is the minimum output data rate supported * regardless of the selected power mode. Round it up to 10 and - * set all the enabled channels to this default value. + * set all channels to this default value. */ - ret = ad7124_set_channel_odr(st, i, 10); + ad7124_set_channel_odr(st, i, 10); } return ret; diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index 70e33dd1c9f7..3271a31afde1 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -260,7 +260,7 @@ static int ad7292_probe(struct spi_device *spi) struct ad7292_state *st; struct iio_dev *indio_dev; struct device_node *child; - bool diff_channels = 0; + bool diff_channels = false; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index 689ecd5dd563..d2163cb62f4f 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -142,12 +142,6 @@ static int ad7298_update_scan_mode(struct iio_dev *indio_dev, return 0; } -/* - * ad7298_trigger_handler() bh of trigger launched polling to ring buffer - * - * Currently there is no option in this driver to disable the saving of - * timestamps within the ring. - */ static irqreturn_t ad7298_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 17402714b387..9e9ff07cf972 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -321,25 +321,15 @@ static int ad7476_probe(struct spi_device *spi) spi_message_init(&st->msg); spi_message_add_tail(&st->xfer, &st->msg); - ret = iio_triggered_buffer_setup(indio_dev, NULL, - &ad7476_trigger_handler, NULL); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &ad7476_trigger_handler, NULL); if (ret) - goto error_disable_reg; + return ret; if (st->chip_info->reset) st->chip_info->reset(st); - ret = iio_device_register(indio_dev); - if (ret) - goto error_ring_unregister; - return 0; - -error_ring_unregister: - iio_triggered_buffer_cleanup(indio_dev); -error_disable_reg: - regulator_disable(st->reg); - - return ret; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad7476_id[] = { diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index ee7b108688b3..0af0bb4d5a7f 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -668,7 +668,6 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, return -ENOMEM; st->trig->ops = &ad7606_trigger_ops; - st->trig->dev.parent = dev; iio_trigger_set_drvdata(st->trig, indio_dev); ret = devm_iio_trigger_register(dev, st->trig); if (ret) diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index b6b6765be7b4..1e41759f3ee5 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -253,21 +253,19 @@ static int ad7766_probe(struct spi_device *spi) return -ENOMEM; ad7766->trig->ops = &ad7766_trigger_ops; - ad7766->trig->dev.parent = &spi->dev; iio_trigger_set_drvdata(ad7766->trig, ad7766); - ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq, - IRQF_TRIGGER_FALLING, dev_name(&spi->dev), - ad7766->trig); - if (ret < 0) - return ret; - /* * The device generates interrupts as long as it is powered up. * Some platforms might not allow the option to power it down so - * disable the interrupt to avoid extra load on the system + * don't enable the interrupt to avoid extra load on the system */ - disable_irq(spi->irq); + ret = devm_request_irq(&spi->dev, spi->irq, ad7766_irq, + IRQF_TRIGGER_FALLING | IRQF_NO_AUTOEN, + dev_name(&spi->dev), + ad7766->trig); + if (ret < 0) + return ret; ret = devm_iio_trigger_register(&spi->dev, ad7766->trig); if (ret) diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 5c0cbee03230..c945f1349623 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -631,7 +631,6 @@ static int ad7768_probe(struct spi_device *spi) return -ENOMEM; st->trig->ops = &ad7768_trigger_ops; - st->trig->dev.parent = &spi->dev; iio_trigger_set_drvdata(st->trig, indio_dev); ret = devm_iio_trigger_register(&spi->dev, st->trig); if (ret) diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 4f6f0e0e03ee..9b3cbe1ddc6f 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -109,12 +109,6 @@ static int ad7887_ring_postdisable(struct iio_dev *indio_dev) return spi_sync(st->spi, &st->msg[AD7887_CH0]); } -/* - * ad7887_trigger_handler() bh of trigger launched polling to ring buffer - * - * Currently there is no option in this driver to disable the saving of - * timestamps within the ring. - **/ static irqreturn_t ad7887_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c index a2cc96658054..9a649745cd0a 100644 --- a/drivers/iio/adc/ad7923.c +++ b/drivers/iio/adc/ad7923.c @@ -192,12 +192,6 @@ static int ad7923_update_scan_mode(struct iio_dev *indio_dev, return 0; } -/* - * ad7923_trigger_handler() bh of trigger launched polling to ring buffer - * - * Currently there is no option in this driver to disable the saving of - * timestamps within the ring. - */ static irqreturn_t ad7923_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; @@ -293,6 +287,13 @@ static const struct iio_info ad7923_info = { .update_scan_mode = ad7923_update_scan_mode, }; +static void ad7923_regulator_disable(void *data) +{ + struct ad7923_state *st = data; + + regulator_disable(st->reg); +} + static int ad7923_probe(struct spi_device *spi) { struct ad7923_state *st; @@ -306,8 +307,6 @@ static int ad7923_probe(struct spi_device *spi) st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); - st->spi = spi; st->settings = AD7923_CODING | AD7923_RANGE | AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS); @@ -340,35 +339,16 @@ static int ad7923_probe(struct spi_device *spi) if (ret) return ret; - ret = iio_triggered_buffer_setup(indio_dev, NULL, - &ad7923_trigger_handler, NULL); + ret = devm_add_action_or_reset(&spi->dev, ad7923_regulator_disable, st); if (ret) - goto error_disable_reg; + return ret; - ret = iio_device_register(indio_dev); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &ad7923_trigger_handler, NULL); if (ret) - goto error_cleanup_ring; - - return 0; - -error_cleanup_ring: - iio_triggered_buffer_cleanup(indio_dev); -error_disable_reg: - regulator_disable(st->reg); - - return ret; -} - -static int ad7923_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7923_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - regulator_disable(st->reg); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad7923_id[] = { @@ -401,7 +381,6 @@ static struct spi_driver ad7923_driver = { .of_match_table = ad7923_of_match, }, .probe = ad7923_probe, - .remove = ad7923_remove, .id_table = ad7923_id, }; module_spi_driver(ad7923_driver); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index 1575b7670207..18bf8386d50a 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -182,12 +182,6 @@ static int ad799x_update_config(struct ad799x_state *st, u16 config) return 0; } -/* - * ad799x_trigger_handler() bh of trigger launched polling to ring buffer - * - * Currently there is no option in this driver to disable the saving of - * timestamps within the ring. - **/ static irqreturn_t ad799x_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 3a6f239d4acc..e777ec718973 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -475,8 +475,9 @@ static int ad_sd_probe_trigger(struct iio_dev *indio_dev) struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); int ret; - sigma_delta->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, - indio_dev->id); + sigma_delta->trig = iio_trigger_alloc(&sigma_delta->spi->dev, + "%s-dev%d", indio_dev->name, + indio_dev->id); if (sigma_delta->trig == NULL) { ret = -ENOMEM; goto error_ret; @@ -484,19 +485,15 @@ static int ad_sd_probe_trigger(struct iio_dev *indio_dev) sigma_delta->trig->ops = &ad_sd_trigger_ops; init_completion(&sigma_delta->completion); + sigma_delta->irq_dis = true; ret = request_irq(sigma_delta->spi->irq, ad_sd_data_rdy_trig_poll, - sigma_delta->info->irq_flags, + sigma_delta->info->irq_flags | IRQF_NO_AUTOEN, indio_dev->name, sigma_delta); if (ret) goto error_free_trig; - if (!sigma_delta->irq_dis) { - sigma_delta->irq_dis = true; - disable_irq_nosync(sigma_delta->spi->irq); - } - sigma_delta->trig->dev.parent = &sigma_delta->spi->dev; iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta); ret = iio_trigger_register(sigma_delta->trig); diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index 9109da2d2e15..d5f6ffc5b5bc 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -23,7 +23,7 @@ #include <linux/fpga/adi-axi-common.h> #include <linux/iio/adc/adi-axi-adc.h> -/** +/* * Register definitions: * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip#register_map */ @@ -104,7 +104,6 @@ static unsigned int adi_axi_adc_read(struct adi_axi_adc_state *st, static int adi_axi_adc_config_dma_buffer(struct device *dev, struct iio_dev *indio_dev) { - struct iio_buffer *buffer; const char *dma_name; if (!device_property_present(dev, "dmas")) @@ -113,15 +112,8 @@ static int adi_axi_adc_config_dma_buffer(struct device *dev, if (device_property_read_string(dev, "dma-names", &dma_name)) dma_name = "rx"; - buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent, - dma_name); - if (IS_ERR(buffer)) - return PTR_ERR(buffer); - - indio_dev->modes |= INDIO_BUFFER_HARDWARE; - iio_device_attach_buffer(indio_dev, buffer); - - return 0; + return devm_iio_dmaengine_buffer_setup(indio_dev->dev.parent, + indio_dev, dma_name); } static int adi_axi_adc_read_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 70750abb5dea..0b5f0c91d0d7 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -625,12 +625,11 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev, struct iio_trigger *trig; int ret; - trig = iio_trigger_alloc("%s-dev%d-%s", idev->name, + trig = iio_trigger_alloc(idev->dev.parent, "%s-dev%d-%s", idev->name, idev->id, trigger->name); if (trig == NULL) return NULL; - trig->dev.parent = idev->dev.parent; iio_trigger_set_drvdata(trig, idev); trig->ops = &at91_adc_trigger_ops; diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c index f19c9aa93f17..40e59f4c95bc 100644 --- a/drivers/iio/adc/cpcap-adc.c +++ b/drivers/iio/adc/cpcap-adc.c @@ -100,7 +100,7 @@ struct cpcap_adc_ato { }; /** - * struct cpcap-adc - cpcap adc device driver data + * struct cpcap_adc - cpcap adc device driver data * @reg: cpcap regmap * @dev: struct device * @vendor: cpcap vendor diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 784c10deeb1a..8c98d8c9ab1f 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -778,9 +778,9 @@ static int exynos_adc_ts_init(struct exynos_adc *info) return ret; } - disable_irq(info->tsirq); ret = request_threaded_irq(info->tsirq, NULL, exynos_ts_isr, - IRQF_ONESHOT, "touchscreen", info); + IRQF_ONESHOT | IRQF_NO_AUTOEN, + "touchscreen", info); if (ret) input_unregister_device(info->input); diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index b573ec60a8b8..2ae54258b221 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -953,7 +953,6 @@ static int ina2xx_probe(struct i2c_client *client, { struct ina2xx_chip_info *chip; struct iio_dev *indio_dev; - struct iio_buffer *buffer; unsigned int val; enum ina2xx_ids type; int ret; @@ -1017,7 +1016,7 @@ static int ina2xx_probe(struct i2c_client *client, return ret; } - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->modes = INDIO_DIRECT_MODE; if (id->driver_data == ina226) { indio_dev->channels = ina226_channels; indio_dev->num_channels = ARRAY_SIZE(ina226_channels); @@ -1028,13 +1027,12 @@ static int ina2xx_probe(struct i2c_client *client, indio_dev->info = &ina219_info; } indio_dev->name = id->name; - indio_dev->setup_ops = &ina2xx_setup_ops; - buffer = devm_iio_kfifo_allocate(&indio_dev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &ina2xx_setup_ops); + if (ret) + return ret; return iio_device_register(indio_dev); } diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index ca1dff3924ff..e3c8ec107722 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -473,7 +473,6 @@ static int max1027_probe(struct spi_device *spi) } st->trig->ops = &max1027_trigger_ops; - st->trig->dev.parent = &spi->dev; iio_trigger_set_drvdata(st->trig, indio_dev); ret = devm_iio_trigger_register(&indio_dev->dev, st->trig); diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c index f57db3056fbe..6b39a139ce28 100644 --- a/drivers/iio/adc/mt6360-adc.c +++ b/drivers/iio/adc/mt6360-adc.c @@ -9,13 +9,14 @@ #include <linux/mutex.h> #include <linux/platform_device.h> #include <linux/regmap.h> -#include <linux/unaligned/be_byteshift.h> #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> +#include <asm/unaligned.h> + #define MT6360_REG_PMUCHGCTRL3 0x313 #define MT6360_REG_PMUADCCFG 0x356 #define MT6360_REG_PMUADCIDLET 0x358 diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c index 07c85434b568..bb70b51d25b1 100644 --- a/drivers/iio/adc/nau7802.c +++ b/drivers/iio/adc/nau7802.c @@ -498,7 +498,8 @@ static int nau7802_probe(struct i2c_client *client, ret = request_threaded_irq(client->irq, NULL, nau7802_eoc_trigger, - IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT | + IRQF_NO_AUTOEN, client->dev.driver->name, indio_dev); if (ret) { @@ -513,8 +514,7 @@ static int nau7802_probe(struct i2c_client *client, dev_info(&client->dev, "Failed to allocate IRQ, using polling mode\n"); client->irq = 0; - } else - disable_irq(client->irq); + } } if (!client->irq) { diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c index d9d105920001..f7bc0bb7f112 100644 --- a/drivers/iio/adc/npcm_adc.c +++ b/drivers/iio/adc/npcm_adc.c @@ -25,6 +25,15 @@ struct npcm_adc { wait_queue_head_t wq; struct regulator *vref; struct reset_control *reset; + /* + * Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a event and finally a register + * read, during which userspace could issue another read request. + * This lock protects a read access from ocurring before another one + * has finished. + */ + struct mutex lock; }; /* ADC registers */ @@ -135,9 +144,9 @@ static int npcm_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&info->lock); ret = npcm_adc_read(info, val, chan->channel); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&info->lock); if (ret) { dev_err(info->dev, "NPCM ADC read failed\n"); return ret; @@ -187,6 +196,8 @@ static int npcm_adc_probe(struct platform_device *pdev) return -ENOMEM; info = iio_priv(indio_dev); + mutex_init(&info->lock); + info->dev = &pdev->dev; info->regs = devm_platform_ioremap_resource(pdev, 0); diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c index 889b88768b63..6ef09609be9f 100644 --- a/drivers/iio/adc/palmas_gpadc.c +++ b/drivers/iio/adc/palmas_gpadc.c @@ -90,6 +90,12 @@ static struct palmas_gpadc_info palmas_gpadc_info[] = { * 3: 800 uA * @extended_delay: enable the gpadc extended delay mode * @auto_conversion_period: define the auto_conversion_period + * @lock: Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a completion callback, + * and finally a register read, during which userspace could issue + * another read request. This lock protects a read access from + * ocurring before another one has finished. * * This is the palmas_gpadc structure to store run-time information * and pointers for this driver instance. @@ -110,6 +116,7 @@ struct palmas_gpadc { bool wakeup1_enable; bool wakeup2_enable; int auto_conversion_period; + struct mutex lock; }; /* @@ -388,7 +395,7 @@ static int palmas_gpadc_read_raw(struct iio_dev *indio_dev, if (adc_chan > PALMAS_ADC_CH_MAX) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&adc->lock); switch (mask) { case IIO_CHAN_INFO_RAW: @@ -414,12 +421,12 @@ static int palmas_gpadc_read_raw(struct iio_dev *indio_dev, goto out; } - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&adc->lock); return ret; out: palmas_gpadc_read_done(adc, adc_chan); - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&adc->lock); return ret; } @@ -516,8 +523,11 @@ static int palmas_gpadc_probe(struct platform_device *pdev) adc->dev = &pdev->dev; adc->palmas = dev_get_drvdata(pdev->dev.parent); adc->adc_info = palmas_gpadc_info; + + mutex_init(&adc->lock); + init_completion(&adc->conv_completion); - dev_set_drvdata(&pdev->dev, indio_dev); + platform_set_drvdata(pdev, indio_dev); adc->auto_conversion_period = gpadc_pdata->auto_conversion_period_ms; adc->irq = palmas_irq_get_virq(adc->palmas, PALMAS_GPADC_EOC_SW_IRQ); diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index 1bc986a7009d..d93e580b3dc5 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -75,6 +75,15 @@ struct spear_adc_state { struct adc_regs_spear6xx __iomem *adc_base_spear6xx; struct clk *clk; struct completion completion; + /* + * Lock to protect the device state during a potential concurrent + * read access from userspace. Reading a raw value requires a sequence + * of register writes, then a wait for a completion callback, + * and finally a register read, during which userspace could issue + * another read request. This lock protects a read access from + * ocurring before another one has finished. + */ + struct mutex lock; u32 current_clk; u32 sampling_freq; u32 avg_samples; @@ -146,7 +155,7 @@ static int spear_adc_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); status = SPEAR_ADC_STATUS_CHANNEL_NUM(chan->channel) | SPEAR_ADC_STATUS_AVG_SAMPLE(st->avg_samples) | @@ -159,7 +168,7 @@ static int spear_adc_read_raw(struct iio_dev *indio_dev, wait_for_completion(&st->completion); /* set by ISR */ *val = st->value; - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return IIO_VAL_INT; @@ -187,7 +196,7 @@ static int spear_adc_write_raw(struct iio_dev *indio_dev, if (mask != IIO_CHAN_INFO_SAMP_FREQ) return -EINVAL; - mutex_lock(&indio_dev->mlock); + mutex_lock(&st->lock); if ((val < SPEAR_ADC_CLK_MIN) || (val > SPEAR_ADC_CLK_MAX) || @@ -199,7 +208,7 @@ static int spear_adc_write_raw(struct iio_dev *indio_dev, spear_adc_set_clk(st, val); out: - mutex_unlock(&indio_dev->mlock); + mutex_unlock(&st->lock); return ret; } @@ -271,6 +280,9 @@ static int spear_adc_probe(struct platform_device *pdev) } st = iio_priv(indio_dev); + + mutex_init(&st->lock); + st->np = np; /* diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index f7c53cea509a..b25386b19373 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -177,7 +177,7 @@ struct stm32_adc_cfg { * @offset: ADC instance register offset in ADC block * @cfg: compatible configuration data * @completion: end of single conversion completion - * @buffer: data buffer + * @buffer: data buffer + 8 bytes for timestamp if enabled * @clk: clock for this adc instance * @irq: interrupt for this adc instance * @lock: spinlock @@ -200,7 +200,7 @@ struct stm32_adc { u32 offset; const struct stm32_adc_cfg *cfg; struct completion completion; - u16 buffer[STM32_ADC_MAX_SQ]; + u16 buffer[STM32_ADC_MAX_SQ + 4] __aligned(8); struct clk *clk; int irq; spinlock_t lock; /* interrupt lock */ @@ -1714,7 +1714,7 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev, } } -static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) +static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping) { struct device_node *node = indio_dev->dev.of_node; struct stm32_adc *adc = iio_priv(indio_dev); @@ -1762,6 +1762,9 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) return -EINVAL; } + if (timestamping) + num_channels++; + channels = devm_kcalloc(&indio_dev->dev, num_channels, sizeof(struct iio_chan_spec), GFP_KERNEL); if (!channels) @@ -1812,6 +1815,19 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev) stm32_adc_smpr_init(adc, channels[i].channel, smp); } + if (timestamping) { + struct iio_chan_spec *timestamp = &channels[scan_index]; + + timestamp->type = IIO_TIMESTAMP; + timestamp->channel = -1; + timestamp->scan_index = scan_index; + timestamp->scan_type.sign = 's'; + timestamp->scan_type.realbits = 64; + timestamp->scan_type.storagebits = 64; + + scan_index++; + } + indio_dev->num_channels = scan_index; indio_dev->channels = channels; @@ -1871,6 +1887,7 @@ static int stm32_adc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; irqreturn_t (*handler)(int irq, void *p) = NULL; struct stm32_adc *adc; + bool timestamping = false; int ret; if (!pdev->dev.of_node) @@ -1927,16 +1944,22 @@ static int stm32_adc_probe(struct platform_device *pdev) if (ret < 0) return ret; - ret = stm32_adc_chan_of_init(indio_dev); - if (ret < 0) - return ret; - ret = stm32_adc_dma_request(dev, indio_dev); if (ret < 0) return ret; - if (!adc->dma_chan) + if (!adc->dma_chan) { + /* For PIO mode only, iio_pollfunc_store_time stores a timestamp + * in the primary trigger IRQ handler and stm32_adc_trigger_handler + * runs in the IRQ thread to push out buffer along with timestamp. + */ handler = &stm32_adc_trigger_handler; + timestamping = true; + } + + ret = stm32_adc_chan_of_init(indio_dev, timestamping); + if (ret < 0) + goto err_dma_disable; ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, handler, diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index 9234f14167b7..1cfefb3b5e56 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -198,7 +198,7 @@ static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl, unsigned int p = fl->ford; /* filter order (ford) */ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fast]; - pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp); + pr_debug("Requested oversampling: %d\n", oversamp); /* * This function tries to compute filter oversampling and integrator * oversampling, base on oversampling ratio requested by user. @@ -295,8 +295,8 @@ static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl, flo->max = (s32)max; flo->bits = bits; - pr_debug("%s: fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n", - __func__, fast, flo->fosr, flo->iosr, + pr_debug("fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n", + fast, flo->fosr, flo->iosr, flo->res, bits, flo->rshift, flo->lshift); } @@ -864,7 +864,7 @@ static void stm32_dfsdm_dma_buffer_done(void *data) * support in IIO. */ - dev_dbg(&indio_dev->dev, "%s: pos = %d, available = %d\n", __func__, + dev_dbg(&indio_dev->dev, "pos = %d, available = %d\n", adc->bufi, available); old_pos = adc->bufi; @@ -918,7 +918,7 @@ static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev) if (!adc->dma_chan) return -EINVAL; - dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__, + dev_dbg(&indio_dev->dev, "size=%d watermark=%d\n", adc->buf_sz, adc->buf_sz / 2); if (adc->nconv == 1 && !indio_dev->trig) diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c index 99b43f28e879..2d393a4dfff6 100644 --- a/drivers/iio/adc/sun4i-gpadc-iio.c +++ b/drivers/iio/adc/sun4i-gpadc-iio.c @@ -470,7 +470,8 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name, } *irq = ret; - ret = devm_request_any_context_irq(&pdev->dev, *irq, handler, 0, + ret = devm_request_any_context_irq(&pdev->dev, *irq, handler, + IRQF_NO_AUTOEN, devname, info); if (ret < 0) { dev_err(&pdev->dev, "could not request %s interrupt: %d\n", @@ -478,7 +479,6 @@ static int sun4i_irq_init(struct platform_device *pdev, const char *name, return ret; } - disable_irq(*irq); atomic_set(atomic, 0); return 0; diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index fb14b92fa6e7..33aea961d850 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Copyright (C) 2017 Axis Communications AB * * Driver for Texas Instruments' ADC084S021 ADC chip. @@ -65,7 +65,7 @@ static const struct iio_chan_spec adc084s021_channels[] = { }; /** - * Read an ADC channel and return its value. + * adc084s021_adc_conversion() - Read an ADC channel and return its value. * * @adc: The ADC SPI data. * @data: Buffer for converted data. @@ -136,7 +136,7 @@ static int adc084s021_read_raw(struct iio_dev *indio_dev, } /** - * Read enabled ADC channels and push data to the buffer. + * adc084s021_buffer_trigger_handler() - Read ADC channels and push to buffer. * * @irq: The interrupt number (not used). * @pollfunc: Pointer to the poll func. diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c new file mode 100644 index 000000000000..764dab087b41 --- /dev/null +++ b/drivers/iio/adc/ti-ads131e08.c @@ -0,0 +1,954 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments ADS131E0x 4-, 6- and 8-Channel ADCs + * + * Copyright (c) 2020 AVL DiTEST GmbH + * Tomislav Denis <tomislav.denis@avl.com> + * + * Datasheet: https://www.ti.com/lit/ds/symlink/ads131e08.pdf + */ + +#include <linux/bitfield.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/module.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> + +#include <asm/unaligned.h> + +/* Commands */ +#define ADS131E08_CMD_RESET 0x06 +#define ADS131E08_CMD_START 0x08 +#define ADS131E08_CMD_STOP 0x0A +#define ADS131E08_CMD_OFFSETCAL 0x1A +#define ADS131E08_CMD_SDATAC 0x11 +#define ADS131E08_CMD_RDATA 0x12 +#define ADS131E08_CMD_RREG(r) (BIT(5) | (r & GENMASK(4, 0))) +#define ADS131E08_CMD_WREG(r) (BIT(6) | (r & GENMASK(4, 0))) + +/* Registers */ +#define ADS131E08_ADR_CFG1R 0x01 +#define ADS131E08_ADR_CFG3R 0x03 +#define ADS131E08_ADR_CH0R 0x05 + +/* Configuration register 1 */ +#define ADS131E08_CFG1R_DR_MASK GENMASK(2, 0) + +/* Configuration register 3 */ +#define ADS131E08_CFG3R_PDB_REFBUF_MASK BIT(7) +#define ADS131E08_CFG3R_VREF_4V_MASK BIT(5) + +/* Channel settings register */ +#define ADS131E08_CHR_GAIN_MASK GENMASK(6, 4) +#define ADS131E08_CHR_MUX_MASK GENMASK(2, 0) +#define ADS131E08_CHR_PWD_MASK BIT(7) + +/* ADC misc */ +#define ADS131E08_DEFAULT_DATA_RATE 1 +#define ADS131E08_DEFAULT_PGA_GAIN 1 +#define ADS131E08_DEFAULT_MUX 0 + +#define ADS131E08_VREF_2V4_mV 2400 +#define ADS131E08_VREF_4V_mV 4000 + +#define ADS131E08_WAIT_RESET_CYCLES 18 +#define ADS131E08_WAIT_SDECODE_CYCLES 4 +#define ADS131E08_WAIT_OFFSETCAL_MS 153 +#define ADS131E08_MAX_SETTLING_TIME_MS 6 + +#define ADS131E08_NUM_STATUS_BYTES 3 +#define ADS131E08_NUM_DATA_BYTES_MAX 24 +#define ADS131E08_NUM_DATA_BYTES(dr) (((dr) >= 32) ? 2 : 3) +#define ADS131E08_NUM_DATA_BITS(dr) (ADS131E08_NUM_DATA_BYTES(dr) * 8) +#define ADS131E08_NUM_STORAGE_BYTES 4 + +enum ads131e08_ids { + ads131e04, + ads131e06, + ads131e08, +}; + +struct ads131e08_info { + unsigned int max_channels; + const char *name; +}; + +struct ads131e08_channel_config { + unsigned int pga_gain; + unsigned int mux; +}; + +struct ads131e08_state { + const struct ads131e08_info *info; + struct spi_device *spi; + struct iio_trigger *trig; + struct clk *adc_clk; + struct regulator *vref_reg; + struct ads131e08_channel_config *channel_config; + unsigned int data_rate; + unsigned int vref_mv; + unsigned int sdecode_delay_us; + unsigned int reset_delay_us; + unsigned int readback_len; + struct completion completion; + struct { + u8 data[ADS131E08_NUM_DATA_BYTES_MAX]; + s64 ts __aligned(8); + } tmp_buf; + + u8 tx_buf[3] ____cacheline_aligned; + /* + * Add extra one padding byte to be able to access the last channel + * value using u32 pointer + */ + u8 rx_buf[ADS131E08_NUM_STATUS_BYTES + + ADS131E08_NUM_DATA_BYTES_MAX + 1]; +}; + +static const struct ads131e08_info ads131e08_info_tbl[] = { + [ads131e04] = { + .max_channels = 4, + .name = "ads131e04", + }, + [ads131e06] = { + .max_channels = 6, + .name = "ads131e06", + }, + [ads131e08] = { + .max_channels = 8, + .name = "ads131e08", + }, +}; + +struct ads131e08_data_rate_desc { + unsigned int rate; /* data rate in kSPS */ + u8 reg; /* reg value */ +}; + +static const struct ads131e08_data_rate_desc ads131e08_data_rate_tbl[] = { + { .rate = 64, .reg = 0x00 }, + { .rate = 32, .reg = 0x01 }, + { .rate = 16, .reg = 0x02 }, + { .rate = 8, .reg = 0x03 }, + { .rate = 4, .reg = 0x04 }, + { .rate = 2, .reg = 0x05 }, + { .rate = 1, .reg = 0x06 }, +}; + +struct ads131e08_pga_gain_desc { + unsigned int gain; /* PGA gain value */ + u8 reg; /* field value */ +}; + +static const struct ads131e08_pga_gain_desc ads131e08_pga_gain_tbl[] = { + { .gain = 1, .reg = 0x01 }, + { .gain = 2, .reg = 0x02 }, + { .gain = 4, .reg = 0x04 }, + { .gain = 8, .reg = 0x05 }, + { .gain = 12, .reg = 0x06 }, +}; + +static const u8 ads131e08_valid_channel_mux_values[] = { 0, 1, 3, 4 }; + +static int ads131e08_exec_cmd(struct ads131e08_state *st, u8 cmd) +{ + int ret; + + ret = spi_write_then_read(st->spi, &cmd, 1, NULL, 0); + if (ret) + dev_err(&st->spi->dev, "Exec cmd(%02x) failed\n", cmd); + + return ret; +} + +static int ads131e08_read_reg(struct ads131e08_state *st, u8 reg) +{ + int ret; + struct spi_transfer transfer[] = { + { + .tx_buf = &st->tx_buf, + .len = 2, + .delay = { + .value = st->sdecode_delay_us, + .unit = SPI_DELAY_UNIT_USECS, + }, + }, { + .rx_buf = &st->rx_buf, + .len = 1, + }, + }; + + st->tx_buf[0] = ADS131E08_CMD_RREG(reg); + st->tx_buf[1] = 0; + + ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); + if (ret) { + dev_err(&st->spi->dev, "Read register failed\n"); + return ret; + } + + return st->rx_buf[0]; +} + +static int ads131e08_write_reg(struct ads131e08_state *st, u8 reg, u8 value) +{ + int ret; + struct spi_transfer transfer[] = { + { + .tx_buf = &st->tx_buf, + .len = 3, + .delay = { + .value = st->sdecode_delay_us, + .unit = SPI_DELAY_UNIT_USECS, + }, + } + }; + + st->tx_buf[0] = ADS131E08_CMD_WREG(reg); + st->tx_buf[1] = 0; + st->tx_buf[2] = value; + + ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); + if (ret) + dev_err(&st->spi->dev, "Write register failed\n"); + + return ret; +} + +static int ads131e08_read_data(struct ads131e08_state *st, int rx_len) +{ + int ret; + struct spi_transfer transfer[] = { + { + .tx_buf = &st->tx_buf, + .len = 1, + }, { + .rx_buf = &st->rx_buf, + .len = rx_len, + }, + }; + + st->tx_buf[0] = ADS131E08_CMD_RDATA; + + ret = spi_sync_transfer(st->spi, transfer, ARRAY_SIZE(transfer)); + if (ret) + dev_err(&st->spi->dev, "Read data failed\n"); + + return ret; +} + +static int ads131e08_set_data_rate(struct ads131e08_state *st, int data_rate) +{ + int i, reg, ret; + + for (i = 0; i < ARRAY_SIZE(ads131e08_data_rate_tbl); i++) { + if (ads131e08_data_rate_tbl[i].rate == data_rate) + break; + } + + if (i == ARRAY_SIZE(ads131e08_data_rate_tbl)) { + dev_err(&st->spi->dev, "invalid data rate value\n"); + return -EINVAL; + } + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG1R); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CFG1R_DR_MASK; + reg |= FIELD_PREP(ADS131E08_CFG1R_DR_MASK, + ads131e08_data_rate_tbl[i].reg); + + ret = ads131e08_write_reg(st, ADS131E08_ADR_CFG1R, reg); + if (ret) + return ret; + + st->data_rate = data_rate; + st->readback_len = ADS131E08_NUM_STATUS_BYTES + + ADS131E08_NUM_DATA_BYTES(st->data_rate) * + st->info->max_channels; + + return 0; +} + +static int ads131e08_pga_gain_to_field_value(struct ads131e08_state *st, + unsigned int pga_gain) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads131e08_pga_gain_tbl); i++) { + if (ads131e08_pga_gain_tbl[i].gain == pga_gain) + break; + } + + if (i == ARRAY_SIZE(ads131e08_pga_gain_tbl)) { + dev_err(&st->spi->dev, "invalid PGA gain value\n"); + return -EINVAL; + } + + return ads131e08_pga_gain_tbl[i].reg; +} + +static int ads131e08_set_pga_gain(struct ads131e08_state *st, + unsigned int channel, unsigned int pga_gain) +{ + int field_value, reg; + + field_value = ads131e08_pga_gain_to_field_value(st, pga_gain); + if (field_value < 0) + return field_value; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CHR_GAIN_MASK; + reg |= FIELD_PREP(ADS131E08_CHR_GAIN_MASK, field_value); + + return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); +} + +static int ads131e08_validate_channel_mux(struct ads131e08_state *st, + unsigned int mux) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(ads131e08_valid_channel_mux_values); i++) { + if (ads131e08_valid_channel_mux_values[i] == mux) + break; + } + + if (i == ARRAY_SIZE(ads131e08_valid_channel_mux_values)) { + dev_err(&st->spi->dev, "invalid channel mux value\n"); + return -EINVAL; + } + + return 0; +} + +static int ads131e08_set_channel_mux(struct ads131e08_state *st, + unsigned int channel, unsigned int mux) +{ + int reg; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CHR_MUX_MASK; + reg |= FIELD_PREP(ADS131E08_CHR_MUX_MASK, mux); + + return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); +} + +static int ads131e08_power_down_channel(struct ads131e08_state *st, + unsigned int channel, bool value) +{ + int reg; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CH0R + channel); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CHR_PWD_MASK; + reg |= FIELD_PREP(ADS131E08_CHR_PWD_MASK, value); + + return ads131e08_write_reg(st, ADS131E08_ADR_CH0R + channel, reg); +} + +static int ads131e08_config_reference_voltage(struct ads131e08_state *st) +{ + int reg; + + reg = ads131e08_read_reg(st, ADS131E08_ADR_CFG3R); + if (reg < 0) + return reg; + + reg &= ~ADS131E08_CFG3R_PDB_REFBUF_MASK; + if (!st->vref_reg) { + reg |= FIELD_PREP(ADS131E08_CFG3R_PDB_REFBUF_MASK, 1); + reg &= ~ADS131E08_CFG3R_VREF_4V_MASK; + reg |= FIELD_PREP(ADS131E08_CFG3R_VREF_4V_MASK, + st->vref_mv == ADS131E08_VREF_4V_mV); + } + + return ads131e08_write_reg(st, ADS131E08_ADR_CFG3R, reg); +} + +static int ads131e08_initial_config(struct iio_dev *indio_dev) +{ + const struct iio_chan_spec *channel = indio_dev->channels; + struct ads131e08_state *st = iio_priv(indio_dev); + unsigned long active_channels = 0; + int ret, i; + + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_RESET); + if (ret) + return ret; + + udelay(st->reset_delay_us); + + /* Disable read data in continuous mode (enabled by default) */ + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_SDATAC); + if (ret) + return ret; + + ret = ads131e08_set_data_rate(st, ADS131E08_DEFAULT_DATA_RATE); + if (ret) + return ret; + + ret = ads131e08_config_reference_voltage(st); + if (ret) + return ret; + + for (i = 0; i < indio_dev->num_channels; i++) { + ret = ads131e08_set_pga_gain(st, channel->channel, + st->channel_config[i].pga_gain); + if (ret) + return ret; + + ret = ads131e08_set_channel_mux(st, channel->channel, + st->channel_config[i].mux); + if (ret) + return ret; + + active_channels |= BIT(channel->channel); + channel++; + } + + /* Power down unused channels */ + for_each_clear_bit(i, &active_channels, st->info->max_channels) { + ret = ads131e08_power_down_channel(st, i, true); + if (ret) + return ret; + } + + /* Request channel offset calibration */ + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_OFFSETCAL); + if (ret) + return ret; + + /* + * Channel offset calibration is triggered with the first START + * command. Since calibration takes more time than settling operation, + * this causes timeout error when command START is sent first + * time (e.g. first call of the ads131e08_read_direct method). + * To avoid this problem offset calibration is triggered here. + */ + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); + if (ret) + return ret; + + msleep(ADS131E08_WAIT_OFFSETCAL_MS); + + return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); +} + +static int ads131e08_pool_data(struct ads131e08_state *st) +{ + unsigned long timeout; + int ret; + + reinit_completion(&st->completion); + + ret = ads131e08_exec_cmd(st, ADS131E08_CMD_START); + if (ret) + return ret; + + timeout = msecs_to_jiffies(ADS131E08_MAX_SETTLING_TIME_MS); + ret = wait_for_completion_timeout(&st->completion, timeout); + if (!ret) + return -ETIMEDOUT; + + ret = ads131e08_read_data(st, st->readback_len); + if (ret) + return ret; + + return ads131e08_exec_cmd(st, ADS131E08_CMD_STOP); +} + +static int ads131e08_read_direct(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *value) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + u8 num_bits, *src; + int ret; + + ret = ads131e08_pool_data(st); + if (ret) + return ret; + + src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + + channel->channel * ADS131E08_NUM_DATA_BYTES(st->data_rate); + + num_bits = ADS131E08_NUM_DATA_BITS(st->data_rate); + *value = sign_extend32(get_unaligned_be32(src) >> (32 - num_bits), num_bits - 1); + + return 0; +} + +static int ads131e08_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *value, + int *value2, long mask) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ads131e08_read_direct(indio_dev, channel, value); + iio_device_release_direct_mode(indio_dev); + if (ret) + return ret; + + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + if (st->vref_reg) { + ret = regulator_get_voltage(st->vref_reg); + if (ret < 0) + return ret; + + *value = ret / 1000; + } else { + *value = st->vref_mv; + } + + *value /= st->channel_config[channel->address].pga_gain; + *value2 = ADS131E08_NUM_DATA_BITS(st->data_rate) - 1; + + return IIO_VAL_FRACTIONAL_LOG2; + + case IIO_CHAN_INFO_SAMP_FREQ: + *value = st->data_rate; + + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + +static int ads131e08_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int value, + int value2, long mask) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ads131e08_set_data_rate(st, value); + iio_device_release_direct_mode(indio_dev); + return ret; + + default: + return -EINVAL; + } +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 2 4 8 16 32 64"); + +static struct attribute *ads131e08_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group ads131e08_attribute_group = { + .attrs = ads131e08_attributes, +}; + +static int ads131e08_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, unsigned int *readval) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + + if (readval) { + int ret = ads131e08_read_reg(st, reg); + *readval = ret; + return ret; + } + + return ads131e08_write_reg(st, reg, writeval); +} + +static const struct iio_info ads131e08_iio_info = { + .read_raw = ads131e08_read_raw, + .write_raw = ads131e08_write_raw, + .attrs = &ads131e08_attribute_group, + .debugfs_reg_access = &ads131e08_debugfs_reg_access, +}; + +static int ads131e08_set_trigger_state(struct iio_trigger *trig, bool state) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct ads131e08_state *st = iio_priv(indio_dev); + u8 cmd = state ? ADS131E08_CMD_START : ADS131E08_CMD_STOP; + + return ads131e08_exec_cmd(st, cmd); +} + +static const struct iio_trigger_ops ads131e08_trigger_ops = { + .set_trigger_state = &ads131e08_set_trigger_state, + .validate_device = &iio_trigger_validate_own_device, +}; + +static irqreturn_t ads131e08_trigger_handler(int irq, void *private) +{ + struct iio_poll_func *pf = private; + struct iio_dev *indio_dev = pf->indio_dev; + struct ads131e08_state *st = iio_priv(indio_dev); + unsigned int chn, i = 0; + u8 *src, *dest; + int ret; + + /* + * The number of data bits per channel depends on the data rate. + * For 32 and 64 ksps data rates, number of data bits per channel + * is 16. This case is not compliant with used (fixed) scan element + * type (be:s24/32>>8). So we use a little tweak to pack properly + * 16 bits of data into the buffer. + */ + unsigned int num_bytes = ADS131E08_NUM_DATA_BYTES(st->data_rate); + u8 tweek_offset = num_bytes == 2 ? 1 : 0; + + if (iio_trigger_using_own(indio_dev)) + ret = ads131e08_read_data(st, st->readback_len); + else + ret = ads131e08_pool_data(st); + + if (ret) + goto out; + + for_each_set_bit(chn, indio_dev->active_scan_mask, indio_dev->masklength) { + src = st->rx_buf + ADS131E08_NUM_STATUS_BYTES + chn * num_bytes; + dest = st->tmp_buf.data + i * ADS131E08_NUM_STORAGE_BYTES; + + /* + * Tweek offset is 0: + * +---+---+---+---+ + * |D0 |D1 |D2 | X | (3 data bytes) + * +---+---+---+---+ + * a+0 a+1 a+2 a+3 + * + * Tweek offset is 1: + * +---+---+---+---+ + * |P0 |D0 |D1 | X | (one padding byte and 2 data bytes) + * +---+---+---+---+ + * a+0 a+1 a+2 a+3 + */ + memcpy(dest + tweek_offset, src, num_bytes); + + /* + * Data conversion from 16 bits of data to 24 bits of data + * is done by sign extension (properly filling padding byte). + */ + if (tweek_offset) + *dest = *src & BIT(7) ? 0xff : 0x00; + + i++; + } + + iio_push_to_buffers_with_timestamp(indio_dev, st->tmp_buf.data, + iio_get_time_ns(indio_dev)); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static irqreturn_t ads131e08_interrupt(int irq, void *private) +{ + struct iio_dev *indio_dev = private; + struct ads131e08_state *st = iio_priv(indio_dev); + + if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) + iio_trigger_poll(st->trig); + else + complete(&st->completion); + + return IRQ_HANDLED; +} + +static int ads131e08_alloc_channels(struct iio_dev *indio_dev) +{ + struct ads131e08_state *st = iio_priv(indio_dev); + struct ads131e08_channel_config *channel_config; + struct device *dev = &st->spi->dev; + struct iio_chan_spec *channels; + struct fwnode_handle *node; + unsigned int channel, tmp; + int num_channels, i, ret; + + ret = device_property_read_u32(dev, "ti,vref-internal", &tmp); + if (ret) + tmp = 0; + + switch (tmp) { + case 0: + st->vref_mv = ADS131E08_VREF_2V4_mV; + break; + case 1: + st->vref_mv = ADS131E08_VREF_4V_mV; + break; + default: + dev_err(&st->spi->dev, "invalid internal voltage reference\n"); + return -EINVAL; + } + + num_channels = device_get_child_node_count(dev); + if (num_channels == 0) { + dev_err(&st->spi->dev, "no channel children\n"); + return -ENODEV; + } + + if (num_channels > st->info->max_channels) { + dev_err(&st->spi->dev, "num of channel children out of range\n"); + return -EINVAL; + } + + channels = devm_kcalloc(&st->spi->dev, num_channels, + sizeof(*channels), GFP_KERNEL); + if (!channels) + return -ENOMEM; + + channel_config = devm_kcalloc(&st->spi->dev, num_channels, + sizeof(*channel_config), GFP_KERNEL); + if (!channel_config) + return -ENOMEM; + + i = 0; + device_for_each_child_node(dev, node) { + ret = fwnode_property_read_u32(node, "reg", &channel); + if (ret) + return ret; + + ret = fwnode_property_read_u32(node, "ti,gain", &tmp); + if (ret) { + channel_config[i].pga_gain = ADS131E08_DEFAULT_PGA_GAIN; + } else { + ret = ads131e08_pga_gain_to_field_value(st, tmp); + if (ret < 0) + return ret; + + channel_config[i].pga_gain = tmp; + } + + ret = fwnode_property_read_u32(node, "ti,mux", &tmp); + if (ret) { + channel_config[i].mux = ADS131E08_DEFAULT_MUX; + } else { + ret = ads131e08_validate_channel_mux(st, tmp); + if (ret) + return ret; + + channel_config[i].mux = tmp; + } + + channels[i].type = IIO_VOLTAGE; + channels[i].indexed = 1; + channels[i].channel = channel; + channels[i].address = i; + channels[i].info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE); + channels[i].info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ); + channels[i].scan_index = channel; + channels[i].scan_type.sign = 's'; + channels[i].scan_type.realbits = 24; + channels[i].scan_type.storagebits = 32; + channels[i].scan_type.shift = 8; + channels[i].scan_type.endianness = IIO_BE; + i++; + } + + indio_dev->channels = channels; + indio_dev->num_channels = num_channels; + st->channel_config = channel_config; + + return 0; +} + +static void ads131e08_regulator_disable(void *data) +{ + struct ads131e08_state *st = data; + + regulator_disable(st->vref_reg); +} + +static void ads131e08_clk_disable(void *data) +{ + struct ads131e08_state *st = data; + + clk_disable_unprepare(st->adc_clk); +} + +static int ads131e08_probe(struct spi_device *spi) +{ + const struct ads131e08_info *info; + struct ads131e08_state *st; + struct iio_dev *indio_dev; + unsigned long adc_clk_hz; + unsigned long adc_clk_ns; + int ret; + + info = device_get_match_data(&spi->dev); + if (!info) { + dev_err(&spi->dev, "failed to get match data\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); + if (!indio_dev) { + dev_err(&spi->dev, "failed to allocate IIO device\n"); + return -ENOMEM; + } + + st = iio_priv(indio_dev); + st->info = info; + st->spi = spi; + + ret = ads131e08_alloc_channels(indio_dev); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->dev.parent = &spi->dev; + indio_dev->info = &ads131e08_iio_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + init_completion(&st->completion); + + if (spi->irq) { + ret = devm_request_irq(&spi->dev, spi->irq, + ads131e08_interrupt, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + spi->dev.driver->name, indio_dev); + if (ret) + return dev_err_probe(&spi->dev, ret, + "request irq failed\n"); + } else { + dev_err(&spi->dev, "data ready IRQ missing\n"); + return -ENODEV; + } + + st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", + indio_dev->name, indio_dev->id); + if (!st->trig) { + dev_err(&spi->dev, "failed to allocate IIO trigger\n"); + return -ENOMEM; + } + + st->trig->ops = &ads131e08_trigger_ops; + st->trig->dev.parent = &spi->dev; + iio_trigger_set_drvdata(st->trig, indio_dev); + ret = devm_iio_trigger_register(&spi->dev, st->trig); + if (ret) { + dev_err(&spi->dev, "failed to register IIO trigger\n"); + return -ENOMEM; + } + + indio_dev->trig = iio_trigger_get(st->trig); + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + NULL, &ads131e08_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, "failed to setup IIO buffer\n"); + return ret; + } + + st->vref_reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (!IS_ERR(st->vref_reg)) { + ret = regulator_enable(st->vref_reg); + if (ret) { + dev_err(&spi->dev, + "failed to enable external vref supply\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ads131e08_regulator_disable, st); + if (ret) + return ret; + } else { + if (PTR_ERR(st->vref_reg) != -ENODEV) + return PTR_ERR(st->vref_reg); + + st->vref_reg = NULL; + } + + st->adc_clk = devm_clk_get(&spi->dev, "adc-clk"); + if (IS_ERR(st->adc_clk)) + return dev_err_probe(&spi->dev, PTR_ERR(st->adc_clk), + "failed to get the ADC clock\n"); + + ret = clk_prepare_enable(st->adc_clk); + if (ret) { + dev_err(&spi->dev, "failed to prepare/enable the ADC clock\n"); + return ret; + } + + ret = devm_add_action_or_reset(&spi->dev, ads131e08_clk_disable, st); + if (ret) + return ret; + + adc_clk_hz = clk_get_rate(st->adc_clk); + if (!adc_clk_hz) { + dev_err(&spi->dev, "failed to get the ADC clock rate\n"); + return -EINVAL; + } + + adc_clk_ns = NSEC_PER_SEC / adc_clk_hz; + st->sdecode_delay_us = DIV_ROUND_UP( + ADS131E08_WAIT_SDECODE_CYCLES * adc_clk_ns, NSEC_PER_USEC); + st->reset_delay_us = DIV_ROUND_UP( + ADS131E08_WAIT_RESET_CYCLES * adc_clk_ns, NSEC_PER_USEC); + + ret = ads131e08_initial_config(indio_dev); + if (ret) { + dev_err(&spi->dev, "initial configuration failed\n"); + return ret; + } + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ads131e08_of_match[] = { + { .compatible = "ti,ads131e04", + .data = &ads131e08_info_tbl[ads131e04], }, + { .compatible = "ti,ads131e06", + .data = &ads131e08_info_tbl[ads131e06], }, + { .compatible = "ti,ads131e08", + .data = &ads131e08_info_tbl[ads131e08], }, + {} +}; +MODULE_DEVICE_TABLE(of, ads131e08_of_match); + +static struct spi_driver ads131e08_driver = { + .driver = { + .name = "ads131e08", + .of_match_table = ads131e08_of_match, + }, + .probe = ads131e08_probe, +}; +module_spi_driver(ads131e08_driver); + +MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>"); +MODULE_DESCRIPTION("Driver for ADS131E0x ADC family"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c index e946903b0993..855cc2d64ac8 100644 --- a/drivers/iio/adc/ti_am335x_adc.c +++ b/drivers/iio/adc/ti_am335x_adc.c @@ -385,24 +385,16 @@ static int tiadc_iio_buffered_hardware_setup(struct device *dev, unsigned long flags, const struct iio_buffer_setup_ops *setup_ops) { - struct iio_buffer *buffer; int ret; - buffer = devm_iio_kfifo_allocate(dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - - ret = devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh, - flags, indio_dev->name, indio_dev); + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + setup_ops); if (ret) return ret; - indio_dev->setup_ops = setup_ops; - indio_dev->modes |= INDIO_BUFFER_SOFTWARE; - - return 0; + return devm_request_threaded_irq(dev, irq, pollfunc_th, pollfunc_bh, + flags, indio_dev->name, indio_dev); } static const char * const chan_name_ain[] = { diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 34800dccbf69..6914c1900ed0 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -747,7 +747,6 @@ static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev, if (trig == NULL) return ERR_PTR(-ENOMEM); - trig->dev.parent = indio_dev->dev.parent; trig->ops = &xadc_trigger_ops; iio_trigger_set_drvdata(trig, iio_priv(indio_dev)); diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index b0cb9a35f5cd..d76179878ff9 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -132,9 +132,9 @@ static const struct iio_dma_buffer_ops iio_dmaengine_default_ops = { static ssize_t iio_dmaengine_buffer_get_length_align(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; struct dmaengine_buffer *dmaengine_buffer = - iio_buffer_to_dmaengine_buffer(indio_dev->buffer); + iio_buffer_to_dmaengine_buffer(buffer); return sprintf(buf, "%zu\n", dmaengine_buffer->align); } @@ -244,7 +244,7 @@ static void __devm_iio_dmaengine_buffer_free(struct device *dev, void *res) * * The buffer will be automatically de-allocated once the device gets destroyed. */ -struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, +static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, const char *channel) { struct iio_buffer **bufferp, *buffer; @@ -265,7 +265,34 @@ struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, return buffer; } -EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_alloc); + +/** + * devm_iio_dmaengine_buffer_setup() - Setup a DMA buffer for an IIO device + * @dev: Parent device for the buffer + * @indio_dev: IIO device to which to attach this buffer. + * @channel: DMA channel name, typically "rx". + * + * This allocates a new IIO buffer with devm_iio_dmaengine_buffer_alloc() + * and attaches it to an IIO device with iio_device_attach_buffer(). + * It also appends the INDIO_BUFFER_HARDWARE mode to the supported modes of the + * IIO device. + */ +int devm_iio_dmaengine_buffer_setup(struct device *dev, + struct iio_dev *indio_dev, + const char *channel) +{ + struct iio_buffer *buffer; + + buffer = devm_iio_dmaengine_buffer_alloc(indio_dev->dev.parent, + channel); + if (IS_ERR(buffer)) + return PTR_ERR(buffer); + + indio_dev->modes |= INDIO_BUFFER_HARDWARE; + + return iio_device_attach_buffer(indio_dev, buffer); +} +EXPORT_SYMBOL_GPL(devm_iio_dmaengine_buffer_setup); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("DMA buffer for the IIO framework"); diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c index 92b8aea3e063..b2b1b7d27af4 100644 --- a/drivers/iio/buffer/industrialio-triggered-buffer.c +++ b/drivers/iio/buffer/industrialio-triggered-buffer.c @@ -50,8 +50,6 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, goto error_ret; } - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->pollfunc = iio_alloc_pollfunc(h, thread, IRQF_ONESHOT, @@ -72,10 +70,16 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, buffer->attrs = buffer_attrs; + ret = iio_device_attach_buffer(indio_dev, buffer); + if (ret < 0) + goto error_dealloc_pollfunc; + return 0; +error_dealloc_pollfunc: + iio_dealloc_pollfunc(indio_dev->pollfunc); error_kfifo_free: - iio_kfifo_free(indio_dev->buffer); + iio_kfifo_free(buffer); error_ret: return ret; } diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c index 1359abed3b31..516eb3465de1 100644 --- a/drivers/iio/buffer/kfifo_buf.c +++ b/drivers/iio/buffer/kfifo_buf.c @@ -180,13 +180,13 @@ static void devm_iio_kfifo_release(struct device *dev, void *res) } /** - * devm_iio_fifo_allocate - Resource-managed iio_kfifo_allocate() + * devm_iio_kfifo_allocate - Resource-managed iio_kfifo_allocate() * @dev: Device to allocate kfifo buffer for * * RETURNS: * Pointer to allocated iio_buffer on success, NULL on failure. */ -struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) +static struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) { struct iio_buffer **ptr, *r; @@ -204,6 +204,45 @@ struct iio_buffer *devm_iio_kfifo_allocate(struct device *dev) return r; } -EXPORT_SYMBOL(devm_iio_kfifo_allocate); + +/** + * devm_iio_kfifo_buffer_setup_ext - Allocate a kfifo buffer & attach it to an IIO device + * @dev: Device object to which to attach the life-time of this kfifo buffer + * @indio_dev: The device the buffer should be attached to + * @mode_flags: The mode flags for this buffer (INDIO_BUFFER_SOFTWARE and/or + * INDIO_BUFFER_TRIGGERED). + * @setup_ops: The setup_ops required to configure the HW part of the buffer (optional) + * @buffer_attrs: Extra sysfs buffer attributes for this IIO buffer + * + * This function allocates a kfifo buffer via devm_iio_kfifo_allocate() and + * attaches it to the IIO device via iio_device_attach_buffer(). + * This is meant to be a bit of a short-hand/helper function as there are a few + * drivers that seem to do this. + */ +int devm_iio_kfifo_buffer_setup_ext(struct device *dev, + struct iio_dev *indio_dev, + int mode_flags, + const struct iio_buffer_setup_ops *setup_ops, + const struct attribute **buffer_attrs) +{ + struct iio_buffer *buffer; + + if (!mode_flags) + return -EINVAL; + + buffer = devm_iio_kfifo_allocate(dev); + if (!buffer) + return -ENOMEM; + + mode_flags &= kfifo_access_funcs.modes; + + indio_dev->modes |= mode_flags; + indio_dev->setup_ops = setup_ops; + + buffer->attrs = buffer_attrs; + + return iio_device_attach_buffer(indio_dev, buffer); +} +EXPORT_SYMBOL_GPL(devm_iio_kfifo_buffer_setup_ext); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/cdc/Kconfig b/drivers/iio/cdc/Kconfig new file mode 100644 index 000000000000..5e3319a3ff48 --- /dev/null +++ b/drivers/iio/cdc/Kconfig @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# CDC drivers +# +menu "Capacitance to digital converters" + +config AD7150 + tristate "Analog Devices ad7150/1/6 capacitive sensor driver" + depends on I2C + help + Say yes here to build support for Analog Devices capacitive sensors. + (ad7150, ad7151, ad7156) Provides direct access via sysfs. + + To compile this driver as a module, choose M here: the + module will be called ad7150. + +endmenu diff --git a/drivers/iio/cdc/Makefile b/drivers/iio/cdc/Makefile new file mode 100644 index 000000000000..ee490637b032 --- /dev/null +++ b/drivers/iio/cdc/Makefile @@ -0,0 +1,6 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for industrial I/O capacitance to digital converter (CDC) drivers +# + +obj-$(CONFIG_AD7150) += ad7150.o diff --git a/drivers/iio/cdc/ad7150.c b/drivers/iio/cdc/ad7150.c new file mode 100644 index 000000000000..ebe112b4618b --- /dev/null +++ b/drivers/iio/cdc/ad7150.c @@ -0,0 +1,673 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD7150 capacitive sensor driver supporting AD7150/1/6 + * + * Copyright 2010-2011 Analog Devices Inc. + * Copyright 2021 Jonathan Cameron <Jonathan.Cameron@huawei.com> + */ + +#include <linux/bitfield.h> +#include <linux/device.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/events.h> + +#define AD7150_STATUS_REG 0 +#define AD7150_STATUS_OUT1 BIT(3) +#define AD7150_STATUS_OUT2 BIT(5) +#define AD7150_CH1_DATA_HIGH_REG 1 +#define AD7150_CH2_DATA_HIGH_REG 3 +#define AD7150_CH1_AVG_HIGH_REG 5 +#define AD7150_CH2_AVG_HIGH_REG 7 +#define AD7150_CH1_SENSITIVITY_REG 9 +#define AD7150_CH1_THR_HOLD_H_REG 9 +#define AD7150_CH1_TIMEOUT_REG 10 +#define AD7150_CH_TIMEOUT_RECEDING GENMASK(3, 0) +#define AD7150_CH_TIMEOUT_APPROACHING GENMASK(7, 4) +#define AD7150_CH1_SETUP_REG 11 +#define AD7150_CH2_SENSITIVITY_REG 12 +#define AD7150_CH2_THR_HOLD_H_REG 12 +#define AD7150_CH2_TIMEOUT_REG 13 +#define AD7150_CH2_SETUP_REG 14 +#define AD7150_CFG_REG 15 +#define AD7150_CFG_FIX BIT(7) +#define AD7150_CFG_THRESHTYPE_MSK GENMASK(6, 5) +#define AD7150_CFG_TT_NEG 0x0 +#define AD7150_CFG_TT_POS 0x1 +#define AD7150_CFG_TT_IN_WINDOW 0x2 +#define AD7150_CFG_TT_OUT_WINDOW 0x3 +#define AD7150_PD_TIMER_REG 16 +#define AD7150_CH1_CAPDAC_REG 17 +#define AD7150_CH2_CAPDAC_REG 18 +#define AD7150_SN3_REG 19 +#define AD7150_SN2_REG 20 +#define AD7150_SN1_REG 21 +#define AD7150_SN0_REG 22 +#define AD7150_ID_REG 23 + +enum { + AD7150, + AD7151, +}; + +/** + * struct ad7150_chip_info - instance specific chip data + * @client: i2c client for this device + * @threshold: thresholds for simple capacitance value events + * @thresh_sensitivity: threshold for simple capacitance offset + * from 'average' value. + * @thresh_timeout: a timeout, in samples from the moment an + * adaptive threshold event occurs to when the average + * value jumps to current value. Note made up of two fields, + * 3:0 are for timeout receding - applies if below lower threshold + * 7:4 are for timeout approaching - applies if above upper threshold + * @state_lock: ensure consistent state of this structure wrt the + * hardware. + * @interrupts: one or two interrupt numbers depending on device type. + * @int_enabled: is a given interrupt currently enabled. + * @type: threshold type + * @dir: threshold direction + */ +struct ad7150_chip_info { + struct i2c_client *client; + u16 threshold[2][2]; + u8 thresh_sensitivity[2][2]; + u8 thresh_timeout[2][2]; + struct mutex state_lock; + int interrupts[2]; + bool int_enabled[2]; + enum iio_event_type type; + enum iio_event_direction dir; +}; + +static const u8 ad7150_addresses[][6] = { + { AD7150_CH1_DATA_HIGH_REG, AD7150_CH1_AVG_HIGH_REG, + AD7150_CH1_SETUP_REG, AD7150_CH1_THR_HOLD_H_REG, + AD7150_CH1_SENSITIVITY_REG, AD7150_CH1_TIMEOUT_REG }, + { AD7150_CH2_DATA_HIGH_REG, AD7150_CH2_AVG_HIGH_REG, + AD7150_CH2_SETUP_REG, AD7150_CH2_THR_HOLD_H_REG, + AD7150_CH2_SENSITIVITY_REG, AD7150_CH2_TIMEOUT_REG }, +}; + +static int ad7150_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, + int *val2, + long mask) +{ + struct ad7150_chip_info *chip = iio_priv(indio_dev); + int channel = chan->channel; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_smbus_read_word_swapped(chip->client, + ad7150_addresses[channel][0]); + if (ret < 0) + return ret; + *val = ret >> 4; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_AVERAGE_RAW: + ret = i2c_smbus_read_word_swapped(chip->client, + ad7150_addresses[channel][1]); + if (ret < 0) + return ret; + *val = ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + /* + * Base units for capacitance are nano farads and the value + * calculated from the datasheet formula is in picofarad + * so multiply by 1000 + */ + *val = 1000; + *val2 = 40944 >> 4; /* To match shift in _RAW */ + return IIO_VAL_FRACTIONAL; + case IIO_CHAN_INFO_OFFSET: + *val = -(12288 >> 4); /* To match shift in _RAW */ + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + /* Strangely same for both 1 and 2 chan parts */ + *val = 100; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int ad7150_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7150_chip_info *chip = iio_priv(indio_dev); + u8 threshtype; + bool thrfixed; + int ret; + + ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG_REG); + if (ret < 0) + return ret; + + threshtype = FIELD_GET(AD7150_CFG_THRESHTYPE_MSK, ret); + + /*check if threshold mode is fixed or adaptive*/ + thrfixed = FIELD_GET(AD7150_CFG_FIX, ret); + + switch (type) { + case IIO_EV_TYPE_THRESH_ADAPTIVE: + if (dir == IIO_EV_DIR_RISING) + return !thrfixed && (threshtype == AD7150_CFG_TT_POS); + return !thrfixed && (threshtype == AD7150_CFG_TT_NEG); + case IIO_EV_TYPE_THRESH: + if (dir == IIO_EV_DIR_RISING) + return thrfixed && (threshtype == AD7150_CFG_TT_POS); + return thrfixed && (threshtype == AD7150_CFG_TT_NEG); + default: + break; + } + return -EINVAL; +} + +/* state_lock should be held to ensure consistent state */ +static int ad7150_write_event_params(struct iio_dev *indio_dev, + unsigned int chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct ad7150_chip_info *chip = iio_priv(indio_dev); + int rising = (dir == IIO_EV_DIR_RISING); + + /* Only update value live, if parameter is in use */ + if ((type != chip->type) || (dir != chip->dir)) + return 0; + + switch (type) { + /* Note completely different from the adaptive versions */ + case IIO_EV_TYPE_THRESH: { + u16 value = chip->threshold[rising][chan]; + return i2c_smbus_write_word_swapped(chip->client, + ad7150_addresses[chan][3], + value); + } + case IIO_EV_TYPE_THRESH_ADAPTIVE: { + int ret; + u8 sens, timeout; + + sens = chip->thresh_sensitivity[rising][chan]; + ret = i2c_smbus_write_byte_data(chip->client, + ad7150_addresses[chan][4], + sens); + if (ret) + return ret; + + /* + * Single timeout register contains timeouts for both + * directions. + */ + timeout = FIELD_PREP(AD7150_CH_TIMEOUT_APPROACHING, + chip->thresh_timeout[1][chan]); + timeout |= FIELD_PREP(AD7150_CH_TIMEOUT_RECEDING, + chip->thresh_timeout[0][chan]); + return i2c_smbus_write_byte_data(chip->client, + ad7150_addresses[chan][5], + timeout); + } + default: + return -EINVAL; + } +} + +static int ad7150_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct ad7150_chip_info *chip = iio_priv(indio_dev); + int ret = 0; + + /* + * There is only a single shared control and no on chip + * interrupt disables for the two interrupt lines. + * So, enabling will switch the events configured to enable + * whatever was most recently requested and if necessary enable_irq() + * the interrupt and any disable will disable_irq() for that + * channels interrupt. + */ + if (!state) { + if ((chip->int_enabled[chan->channel]) && + (type == chip->type) && (dir == chip->dir)) { + disable_irq(chip->interrupts[chan->channel]); + chip->int_enabled[chan->channel] = false; + } + return 0; + } + + mutex_lock(&chip->state_lock); + if ((type != chip->type) || (dir != chip->dir)) { + int rising = (dir == IIO_EV_DIR_RISING); + u8 thresh_type, cfg, fixed; + + /* + * Need to temporarily disable both interrupts if + * enabled - this is to avoid races around changing + * config and thresholds. + * Note enable/disable_irq() are reference counted so + * no need to check if already enabled. + */ + disable_irq(chip->interrupts[0]); + disable_irq(chip->interrupts[1]); + + ret = i2c_smbus_read_byte_data(chip->client, AD7150_CFG_REG); + if (ret < 0) + goto error_ret; + + cfg = ret & ~(AD7150_CFG_THRESHTYPE_MSK | AD7150_CFG_FIX); + + if (type == IIO_EV_TYPE_THRESH_ADAPTIVE) + fixed = 0; + else + fixed = 1; + + if (rising) + thresh_type = AD7150_CFG_TT_POS; + else + thresh_type = AD7150_CFG_TT_NEG; + + cfg |= FIELD_PREP(AD7150_CFG_FIX, fixed) | + FIELD_PREP(AD7150_CFG_THRESHTYPE_MSK, thresh_type); + + ret = i2c_smbus_write_byte_data(chip->client, AD7150_CFG_REG, + cfg); + if (ret < 0) + goto error_ret; + + /* + * There is a potential race condition here, but not easy + * to close given we can't disable the interrupt at the + * chip side of things. Rely on the status bit. + */ + chip->type = type; + chip->dir = dir; + + /* update control attributes */ + ret = ad7150_write_event_params(indio_dev, chan->channel, type, + dir); + if (ret) + goto error_ret; + /* reenable any irq's we disabled whilst changing mode */ + enable_irq(chip->interrupts[0]); + enable_irq(chip->interrupts[1]); + } + if (!chip->int_enabled[chan->channel]) { + enable_irq(chip->interrupts[chan->channel]); + chip->int_enabled[chan->channel] = true; + } + +error_ret: + mutex_unlock(&chip->state_lock); + + return ret; +} + +static int ad7150_read_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int *val, int *val2) +{ + struct ad7150_chip_info *chip = iio_priv(indio_dev); + int rising = (dir == IIO_EV_DIR_RISING); + + /* Complex register sharing going on here */ + switch (info) { + case IIO_EV_INFO_VALUE: + switch (type) { + case IIO_EV_TYPE_THRESH_ADAPTIVE: + *val = chip->thresh_sensitivity[rising][chan->channel]; + return IIO_VAL_INT; + case IIO_EV_TYPE_THRESH: + *val = chip->threshold[rising][chan->channel]; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_EV_INFO_TIMEOUT: + *val = 0; + *val2 = chip->thresh_timeout[rising][chan->channel] * 10000; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int ad7150_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + int ret; + struct ad7150_chip_info *chip = iio_priv(indio_dev); + int rising = (dir == IIO_EV_DIR_RISING); + + mutex_lock(&chip->state_lock); + switch (info) { + case IIO_EV_INFO_VALUE: + switch (type) { + case IIO_EV_TYPE_THRESH_ADAPTIVE: + chip->thresh_sensitivity[rising][chan->channel] = val; + break; + case IIO_EV_TYPE_THRESH: + chip->threshold[rising][chan->channel] = val; + break; + default: + ret = -EINVAL; + goto error_ret; + } + break; + case IIO_EV_INFO_TIMEOUT: { + /* + * Raw timeout is in cycles of 10 msecs as long as both + * channels are enabled. + * In terms of INT_PLUS_MICRO, that is in units of 10,000 + */ + int timeout = val2 / 10000; + + if (val != 0 || timeout < 0 || timeout > 15 || val2 % 10000) { + ret = -EINVAL; + goto error_ret; + } + + chip->thresh_timeout[rising][chan->channel] = timeout; + break; + } + default: + ret = -EINVAL; + goto error_ret; + } + + /* write back if active */ + ret = ad7150_write_event_params(indio_dev, chan->channel, type, dir); + +error_ret: + mutex_unlock(&chip->state_lock); + return ret; +} + +static const struct iio_event_spec ad7150_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH_ADAPTIVE, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_TIMEOUT), + }, { + .type = IIO_EV_TYPE_THRESH_ADAPTIVE, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_TIMEOUT), + }, +}; + +#define AD7150_CAPACITANCE_CHAN(_chan) { \ + .type = IIO_CAPACITANCE, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + .event_spec = ad7150_events, \ + .num_event_specs = ARRAY_SIZE(ad7150_events), \ + } + +#define AD7150_CAPACITANCE_CHAN_NO_IRQ(_chan) { \ + .type = IIO_CAPACITANCE, \ + .indexed = 1, \ + .channel = _chan, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_AVERAGE_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_OFFSET), \ + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),\ + } + +static const struct iio_chan_spec ad7150_channels[] = { + AD7150_CAPACITANCE_CHAN(0), + AD7150_CAPACITANCE_CHAN(1), +}; + +static const struct iio_chan_spec ad7150_channels_no_irq[] = { + AD7150_CAPACITANCE_CHAN_NO_IRQ(0), + AD7150_CAPACITANCE_CHAN_NO_IRQ(1), +}; + +static const struct iio_chan_spec ad7151_channels[] = { + AD7150_CAPACITANCE_CHAN(0), +}; + +static const struct iio_chan_spec ad7151_channels_no_irq[] = { + AD7150_CAPACITANCE_CHAN_NO_IRQ(0), +}; + +static irqreturn_t __ad7150_event_handler(void *private, u8 status_mask, + int channel) +{ + struct iio_dev *indio_dev = private; + struct ad7150_chip_info *chip = iio_priv(indio_dev); + s64 timestamp = iio_get_time_ns(indio_dev); + int int_status; + + int_status = i2c_smbus_read_byte_data(chip->client, AD7150_STATUS_REG); + if (int_status < 0) + return IRQ_HANDLED; + + if (!(int_status & status_mask)) + return IRQ_HANDLED; + + iio_push_event(indio_dev, + IIO_UNMOD_EVENT_CODE(IIO_CAPACITANCE, channel, + chip->type, chip->dir), + timestamp); + + return IRQ_HANDLED; +} + +static irqreturn_t ad7150_event_handler_ch1(int irq, void *private) +{ + return __ad7150_event_handler(private, AD7150_STATUS_OUT1, 0); +} + +static irqreturn_t ad7150_event_handler_ch2(int irq, void *private) +{ + return __ad7150_event_handler(private, AD7150_STATUS_OUT2, 1); +} + +static IIO_CONST_ATTR(in_capacitance_thresh_adaptive_timeout_available, + "[0 0.01 0.15]"); + +static struct attribute *ad7150_event_attributes[] = { + &iio_const_attr_in_capacitance_thresh_adaptive_timeout_available + .dev_attr.attr, + NULL, +}; + +static const struct attribute_group ad7150_event_attribute_group = { + .attrs = ad7150_event_attributes, + .name = "events", +}; + +static const struct iio_info ad7150_info = { + .event_attrs = &ad7150_event_attribute_group, + .read_raw = &ad7150_read_raw, + .read_event_config = &ad7150_read_event_config, + .write_event_config = &ad7150_write_event_config, + .read_event_value = &ad7150_read_event_value, + .write_event_value = &ad7150_write_event_value, +}; + +static const struct iio_info ad7150_info_no_irq = { + .read_raw = &ad7150_read_raw, +}; + +static void ad7150_reg_disable(void *data) +{ + struct regulator *reg = data; + + regulator_disable(reg); +} + +static int ad7150_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ad7150_chip_info *chip; + struct iio_dev *indio_dev; + struct regulator *reg; + int ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + mutex_init(&chip->state_lock); + chip->client = client; + + indio_dev->name = id->name; + + indio_dev->modes = INDIO_DIRECT_MODE; + + reg = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + ret = regulator_enable(reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&client->dev, ad7150_reg_disable, reg); + if (ret) + return ret; + + chip->interrupts[0] = fwnode_irq_get(dev_fwnode(&client->dev), 0); + if (chip->interrupts[0] < 0) + return chip->interrupts[0]; + if (id->driver_data == AD7150) { + chip->interrupts[1] = fwnode_irq_get(dev_fwnode(&client->dev), 1); + if (chip->interrupts[1] < 0) + return chip->interrupts[1]; + } + if (chip->interrupts[0] && + (id->driver_data == AD7151 || chip->interrupts[1])) { + irq_set_status_flags(chip->interrupts[0], IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(&client->dev, + chip->interrupts[0], + NULL, + &ad7150_event_handler_ch1, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, + "ad7150_irq1", + indio_dev); + if (ret) + return ret; + + indio_dev->info = &ad7150_info; + switch (id->driver_data) { + case AD7150: + indio_dev->channels = ad7150_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7150_channels); + irq_set_status_flags(chip->interrupts[1], IRQ_NOAUTOEN); + ret = devm_request_threaded_irq(&client->dev, + chip->interrupts[1], + NULL, + &ad7150_event_handler_ch2, + IRQF_TRIGGER_RISING | + IRQF_ONESHOT, + "ad7150_irq2", + indio_dev); + if (ret) + return ret; + break; + case AD7151: + indio_dev->channels = ad7151_channels; + indio_dev->num_channels = ARRAY_SIZE(ad7151_channels); + break; + default: + return -EINVAL; + } + + } else { + indio_dev->info = &ad7150_info_no_irq; + switch (id->driver_data) { + case AD7150: + indio_dev->channels = ad7150_channels_no_irq; + indio_dev->num_channels = + ARRAY_SIZE(ad7150_channels_no_irq); + break; + case AD7151: + indio_dev->channels = ad7151_channels_no_irq; + indio_dev->num_channels = + ARRAY_SIZE(ad7151_channels_no_irq); + break; + default: + return -EINVAL; + } + } + + return devm_iio_device_register(indio_dev->dev.parent, indio_dev); +} + +static const struct i2c_device_id ad7150_id[] = { + { "ad7150", AD7150 }, + { "ad7151", AD7151 }, + { "ad7156", AD7150 }, + {} +}; + +MODULE_DEVICE_TABLE(i2c, ad7150_id); + +static const struct of_device_id ad7150_of_match[] = { + { "adi,ad7150" }, + { "adi,ad7151" }, + { "adi,ad7156" }, + {} +}; +static struct i2c_driver ad7150_driver = { + .driver = { + .name = "ad7150", + .of_match_table = ad7150_of_match, + }, + .probe = ad7150_probe, + .id_table = ad7150_id, +}; +module_i2c_driver(ad7150_driver); + +MODULE_AUTHOR("Barry Song <21cnbao@gmail.com>"); +MODULE_DESCRIPTION("Analog Devices AD7150/1/6 capacitive sensor driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index cdab9d04dedd..56ba6c82b501 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -649,7 +649,6 @@ static int atlas_probe(struct i2c_client *client, data->client = client; data->trig = trig; data->chip = chip; - trig->dev.parent = indio_dev->dev.parent; trig->ops = &atlas_interrupt_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index de9c9e3d23ea..29c0dfa4702b 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -26,8 +26,7 @@ static int bme680_i2c_probe(struct i2c_client *client, regmap = devm_regmap_init_i2c(client, &bme680_regmap_config); if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index 3b838068a7e4..6f56ad48cc40 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -132,8 +132,7 @@ static int bme680_spi_probe(struct spi_device *spi) regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus, bus_context, &bme680_regmap_config); if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index 60dd87e96f5f..886e96496dbf 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -497,7 +497,6 @@ static int ccs811_probe(struct i2c_client *client, goto err_poweroff; } - data->drdy_trig->dev.parent = &client->dev; data->drdy_trig->ops = &ccs811_trigger_ops; iio_trigger_set_drvdata(data->drdy_trig, indio_dev); indio_dev->trig = data->drdy_trig; diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index 4d0d798c7cd3..d89f117dd0ef 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -646,7 +646,6 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) return -ENOMEM; } - trig->dev.parent = dev; trig->ops = &scd30_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); @@ -656,19 +655,19 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) indio_dev->trig = iio_trigger_get(trig); + /* + * Interrupt is enabled just before taking a fresh measurement + * and disabled afterwards. This means we need to ensure it is not + * enabled here to keep calls to enable/disable balanced. + */ ret = devm_request_threaded_irq(dev, state->irq, scd30_irq_handler, - scd30_irq_thread_handler, IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + scd30_irq_thread_handler, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT | + IRQF_NO_AUTOEN, indio_dev->name, indio_dev); if (ret) dev_err(dev, "failed to request irq\n"); - /* - * Interrupt is enabled just before taking a fresh measurement - * and disabled afterwards. This means we need to disable it here - * to keep calls to enable/disable balanced. - */ - disable_irq(state->irq); - return ret; } diff --git a/drivers/iio/chemical/scd30_serial.c b/drivers/iio/chemical/scd30_serial.c index 06f85eb1a4dd..568b34486c44 100644 --- a/drivers/iio/chemical/scd30_serial.c +++ b/drivers/iio/chemical/scd30_serial.c @@ -177,7 +177,7 @@ static int scd30_serdev_command(struct scd30_state *state, enum scd30_cmd cmd, u static int scd30_serdev_receive_buf(struct serdev_device *serdev, const unsigned char *buf, size_t size) { - struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); + struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev); struct scd30_serdev_priv *priv; struct scd30_state *state; int num; diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig index 2b9ee9161abd..0334b4954773 100644 --- a/drivers/iio/common/Kconfig +++ b/drivers/iio/common/Kconfig @@ -6,5 +6,6 @@ source "drivers/iio/common/cros_ec_sensors/Kconfig" source "drivers/iio/common/hid-sensors/Kconfig" source "drivers/iio/common/ms_sensors/Kconfig" +source "drivers/iio/common/scmi_sensors/Kconfig" source "drivers/iio/common/ssp_sensors/Kconfig" source "drivers/iio/common/st_sensors/Kconfig" diff --git a/drivers/iio/common/Makefile b/drivers/iio/common/Makefile index 4bc30bb548e2..fad40e1e1718 100644 --- a/drivers/iio/common/Makefile +++ b/drivers/iio/common/Makefile @@ -11,5 +11,6 @@ obj-y += cros_ec_sensors/ obj-y += hid-sensors/ obj-y += ms_sensors/ +obj-y += scmi_sensors/ obj-y += ssp_sensors/ obj-y += st_sensors/ diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c index 752f59037715..af801e203623 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c @@ -97,8 +97,7 @@ static int cros_ec_lid_angle_probe(struct platform_device *pdev) if (!indio_dev) return -ENOMEM; - ret = cros_ec_sensors_core_init(pdev, indio_dev, false, NULL, - NULL, false); + ret = cros_ec_sensors_core_init(pdev, indio_dev, false, NULL, NULL); if (ret) return ret; diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c index dee1191de752..376a5b30010a 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c @@ -236,8 +236,7 @@ static int cros_ec_sensors_probe(struct platform_device *pdev) ret = cros_ec_sensors_core_init(pdev, indio_dev, true, cros_ec_sensors_capture, - cros_ec_sensors_push_data, - true); + cros_ec_sensors_push_data); if (ret) return ret; diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index c833ec0ef214..28bde13003b7 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -12,6 +12,7 @@ #include <linux/iio/iio.h> #include <linux/iio/kfifo_buf.h> #include <linux/iio/sysfs.h> +#include <linux/iio/trigger.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> #include <linux/kernel.h> @@ -240,7 +241,6 @@ static void cros_ec_sensors_core_clean(void *arg) * for backward compatibility. * @push_data: function to call when cros_ec_sensorhub receives * a sample for that sensor. - * @has_hw_fifo: Set true if this device has/uses a HW FIFO * * Return: 0 on success, -errno on failure. */ @@ -248,8 +248,7 @@ int cros_ec_sensors_core_init(struct platform_device *pdev, struct iio_dev *indio_dev, bool physical_device, cros_ec_sensors_capture_t trigger_capture, - cros_ec_sensorhub_push_data_cb_t push_data, - bool has_hw_fifo) + cros_ec_sensorhub_push_data_cb_t push_data) { struct device *dev = &pdev->dev; struct cros_ec_sensors_core_state *state = iio_priv(indio_dev); @@ -334,14 +333,11 @@ int cros_ec_sensors_core_init(struct platform_device *pdev, * We can not use trigger here, as events are generated * as soon as sample_frequency is set. */ - struct iio_buffer *buffer; - - buffer = devm_iio_kfifo_allocate(dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->modes = INDIO_BUFFER_SOFTWARE; + ret = devm_iio_kfifo_buffer_setup_ext(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, NULL, + cros_ec_sensor_fifo_attributes); + if (ret) + return ret; ret = cros_ec_sensorhub_register_push_data( sensor_hub, sensor_platform->sensor_num, @@ -358,21 +354,14 @@ int cros_ec_sensors_core_init(struct platform_device *pdev, ret = iio_device_set_clock(indio_dev, CLOCK_BOOTTIME); if (ret) return ret; - } else { - const struct attribute **fifo_attrs; - - if (has_hw_fifo) - fifo_attrs = cros_ec_sensor_fifo_attributes; - else - fifo_attrs = NULL; + } else { /* * The only way to get samples in buffer is to set a * software trigger (systrig, hrtimer). */ - ret = devm_iio_triggered_buffer_setup_ext( - dev, indio_dev, NULL, trigger_capture, - NULL, fifo_attrs); + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + NULL, trigger_capture, NULL); if (ret) return ret; } @@ -562,7 +551,7 @@ static int cros_ec_sensors_read_until_not_busy( } /** - * read_ec_sensors_data_unsafe() - read acceleration data from EC shared memory + * cros_ec_sensors_read_data_unsafe() - read acceleration data from EC shared memory * @indio_dev: pointer to IIO device * @scan_mask: bitmap of the sensor indices to scan * @data: location to store data diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index 5b822a4298a0..cb52b4fd6bf7 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -263,6 +263,29 @@ int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, } EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value); +int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, + int *val2) +{ + s32 value; + int ret; + + ret = sensor_hub_get_feature(st->hsdev, + st->sensitivity_rel.report_id, + st->sensitivity_rel.index, sizeof(value), + &value); + if (ret < 0 || value < 0) { + *val1 = *val2 = 0; + return -EINVAL; + } + + convert_from_vtf_format(value, st->sensitivity_rel.size, + st->sensitivity_rel.unit_expo, val1, val2); + + return IIO_VAL_INT_PLUS_MICRO; +} +EXPORT_SYMBOL(hid_sensor_read_raw_hyst_rel_value); + + int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, int val1, int val2) { @@ -294,6 +317,37 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, } EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value); +int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, + int val1, int val2) +{ + s32 value; + int ret; + + if (val1 < 0 || val2 < 0) + return -EINVAL; + + value = convert_to_vtf_format(st->sensitivity_rel.size, + st->sensitivity_rel.unit_expo, + val1, val2); + ret = sensor_hub_set_feature(st->hsdev, st->sensitivity_rel.report_id, + st->sensitivity_rel.index, sizeof(value), + &value); + if (ret < 0 || value < 0) + return -EINVAL; + + ret = sensor_hub_get_feature(st->hsdev, + st->sensitivity_rel.report_id, + st->sensitivity_rel.index, sizeof(value), + &value); + if (ret < 0 || value < 0) + return -EINVAL; + + st->raw_hystersis = value; + + return 0; +} +EXPORT_SYMBOL(hid_sensor_write_raw_hyst_rel_value); + /* * This fuction applies the unit exponent to the scale. * For example: @@ -448,12 +502,15 @@ EXPORT_SYMBOL(hid_sensor_batch_mode_supported); int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, u32 usage_id, - struct hid_sensor_common *st) + struct hid_sensor_common *st, + const u32 *sensitivity_addresses, + u32 sensitivity_addresses_len) { struct hid_sensor_hub_attribute_info timestamp; s32 value; int ret; + int i; hid_sensor_get_reporting_interval(hsdev, usage_id, st); @@ -475,6 +532,30 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, HID_USAGE_SENSOR_PROP_SENSITIVITY_ABS, &st->sensitivity); + sensor_hub_input_get_attribute_info(hsdev, + HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_PROP_SENSITIVITY_REL_PCT, + &st->sensitivity_rel); + /* + * Set Sensitivity field ids, when there is no individual modifier, will + * check absolute sensitivity and relative sensitivity of data field + */ + for (i = 0; i < sensitivity_addresses_len; i++) { + if (st->sensitivity.index < 0) + sensor_hub_input_get_attribute_info( + hsdev, HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | + sensitivity_addresses[i], + &st->sensitivity); + + if (st->sensitivity_rel.index < 0) + sensor_hub_input_get_attribute_info( + hsdev, HID_FEATURE_REPORT, usage_id, + HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_REL_PCT | + sensitivity_addresses[i], + &st->sensitivity_rel); + } + st->raw_hystersis = -1; sensor_hub_input_get_attribute_info(hsdev, diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 064c32bec9c7..95ddccb44f1c 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -255,14 +255,14 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, return ret; } - trig = iio_trigger_alloc("%s-dev%d", name, indio_dev->id); + trig = iio_trigger_alloc(indio_dev->dev.parent, + "%s-dev%d", name, indio_dev->id); if (trig == NULL) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); ret = -ENOMEM; goto error_triggered_buffer_cleanup; } - trig->dev.parent = indio_dev->dev.parent; iio_trigger_set_drvdata(trig, attrb); trig->ops = &hid_sensor_trigger_ops; ret = iio_trigger_register(trig); diff --git a/drivers/iio/common/scmi_sensors/Kconfig b/drivers/iio/common/scmi_sensors/Kconfig new file mode 100644 index 000000000000..67e084cbb1ab --- /dev/null +++ b/drivers/iio/common/scmi_sensors/Kconfig @@ -0,0 +1,18 @@ +# +# IIO over SCMI +# +# When adding new entries keep the list in alphabetical order + +menu "IIO SCMI Sensors" + +config IIO_SCMI + tristate "IIO SCMI" + depends on ARM_SCMI_PROTOCOL + select IIO_BUFFER + select IIO_KFIFO_BUF + help + Say yes here to build support for IIO SCMI Driver. + This provides ARM SCMI Protocol based IIO device. + This driver provides support for accelerometer and gyroscope + sensors available on SCMI based platforms. +endmenu diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile new file mode 100644 index 000000000000..f13140a2575a --- /dev/null +++ b/drivers/iio/common/scmi_sensors/Makefile @@ -0,0 +1,5 @@ +# SPDX - License - Identifier : GPL - 2.0 - only +# +# Makefile for the IIO over SCMI +# +obj-$(CONFIG_IIO_SCMI) += scmi_iio.o diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c new file mode 100644 index 000000000000..141e8aa6911e --- /dev/null +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -0,0 +1,672 @@ +// SPDX-License-Identifier: GPL-2.0 + +/* + * System Control and Management Interface(SCMI) based IIO sensor driver + * + * Copyright (C) 2021 Google LLC + */ + +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/sysfs.h> +#include <linux/kernel.h> +#include <linux/kthread.h> +#include <linux/module.h> +#include <linux/scmi_protocol.h> +#include <linux/time.h> +#include <linux/types.h> + +#define SCMI_IIO_NUM_OF_AXIS 3 + +struct scmi_iio_priv { + const struct scmi_sensor_proto_ops *sensor_ops; + struct scmi_protocol_handle *ph; + const struct scmi_sensor_info *sensor_info; + struct iio_dev *indio_dev; + /* adding one additional channel for timestamp */ + s64 iio_buf[SCMI_IIO_NUM_OF_AXIS + 1]; + struct notifier_block sensor_update_nb; + u32 *freq_avail; +}; + +static int scmi_iio_sensor_update_cb(struct notifier_block *nb, + unsigned long event, void *data) +{ + struct scmi_sensor_update_report *sensor_update = data; + struct iio_dev *scmi_iio_dev; + struct scmi_iio_priv *sensor; + s8 tstamp_scale; + u64 time, time_ns; + int i; + + if (sensor_update->readings_count == 0) + return NOTIFY_DONE; + + sensor = container_of(nb, struct scmi_iio_priv, sensor_update_nb); + + for (i = 0; i < sensor_update->readings_count; i++) + sensor->iio_buf[i] = sensor_update->readings[i].value; + + if (!sensor->sensor_info->timestamped) { + time_ns = ktime_to_ns(sensor_update->timestamp); + } else { + /* + * All the axes are supposed to have the same value for timestamp. + * We are just using the values from the Axis 0 here. + */ + time = sensor_update->readings[0].timestamp; + + /* + * Timestamp returned by SCMI is in seconds and is equal to + * time * power-of-10 multiplier(tstamp_scale) seconds. + * Converting the timestamp to nanoseconds below. + */ + tstamp_scale = sensor->sensor_info->tstamp_scale + + const_ilog2(NSEC_PER_SEC) / const_ilog2(10); + if (tstamp_scale < 0) { + do_div(time, int_pow(10, abs(tstamp_scale))); + time_ns = time; + } else { + time_ns = time * int_pow(10, tstamp_scale); + } + } + + scmi_iio_dev = sensor->indio_dev; + iio_push_to_buffers_with_timestamp(scmi_iio_dev, sensor->iio_buf, + time_ns); + return NOTIFY_OK; +} + +static int scmi_iio_buffer_preenable(struct iio_dev *iio_dev) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u32 sensor_config = 0; + int err; + + if (sensor->sensor_info->timestamped) + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK, + SCMI_SENS_CFG_TSTAMP_ENABLE); + + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK, + SCMI_SENS_CFG_SENSOR_ENABLE); + err = sensor->sensor_ops->config_set(sensor->ph, + sensor->sensor_info->id, + sensor_config); + if (err) + dev_err(&iio_dev->dev, "Error in enabling sensor %s err %d", + sensor->sensor_info->name, err); + + return err; +} + +static int scmi_iio_buffer_postdisable(struct iio_dev *iio_dev) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u32 sensor_config = 0; + int err; + + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_SENSOR_ENABLED_MASK, + SCMI_SENS_CFG_SENSOR_DISABLE); + err = sensor->sensor_ops->config_set(sensor->ph, + sensor->sensor_info->id, + sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in disabling sensor %s with err %d", + sensor->sensor_info->name, err); + } + + return err; +} + +static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = { + .preenable = scmi_iio_buffer_preenable, + .postdisable = scmi_iio_buffer_postdisable, +}; + +static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + const unsigned long UHZ_PER_HZ = 1000000UL; + u64 sec, mult, uHz, sf; + u32 sensor_config; + char buf[32]; + + int err = sensor->sensor_ops->config_get(sensor->ph, + sensor->sensor_info->id, + &sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in getting sensor config for sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + uHz = val * UHZ_PER_HZ + val2; + + /* + * The seconds field in the sensor interval in SCMI is 16 bits long + * Therefore seconds = 1/Hz <= 0xFFFF. As floating point calculations are + * discouraged in the kernel driver code, to calculate the scale factor (sf) + * (1* 1000000 * sf)/uHz <= 0xFFFF. Therefore, sf <= (uHz * 0xFFFF)/1000000 + * To calculate the multiplier,we convert the sf into char string and + * count the number of characters + */ + sf = (u64)uHz * 0xFFFF; + do_div(sf, UHZ_PER_HZ); + mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1; + + sec = int_pow(10, mult) * UHZ_PER_HZ; + do_div(sec, uHz); + if (sec == 0) { + dev_err(&iio_dev->dev, + "Trying to set invalid sensor update value for sensor %s", + sensor->sensor_info->name); + return -EINVAL; + } + + sensor_config &= ~SCMI_SENS_CFG_UPDATE_SECS_MASK; + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_SECS_MASK, sec); + sensor_config &= ~SCMI_SENS_CFG_UPDATE_EXP_MASK; + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_UPDATE_EXP_MASK, -mult); + + if (sensor->sensor_info->timestamped) { + sensor_config &= ~SCMI_SENS_CFG_TSTAMP_ENABLED_MASK; + sensor_config |= FIELD_PREP(SCMI_SENS_CFG_TSTAMP_ENABLED_MASK, + SCMI_SENS_CFG_TSTAMP_ENABLE); + } + + sensor_config &= ~SCMI_SENS_CFG_ROUND_MASK; + sensor_config |= + FIELD_PREP(SCMI_SENS_CFG_ROUND_MASK, SCMI_SENS_CFG_ROUND_AUTO); + + err = sensor->sensor_ops->config_set(sensor->ph, + sensor->sensor_info->id, + sensor_config); + if (err) + dev_err(&iio_dev->dev, + "Error in setting sensor update interval for sensor %s value %u err %d", + sensor->sensor_info->name, sensor_config, err); + + return err; +} + +static int scmi_iio_write_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + int err; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + mutex_lock(&iio_dev->mlock); + err = scmi_iio_set_odr_val(iio_dev, val, val2); + mutex_unlock(&iio_dev->mlock); + return err; + default: + return -EINVAL; + } +} + +static int scmi_iio_read_avail(struct iio_dev *iio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = sensor->freq_avail; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = sensor->sensor_info->intervals.count * 2; + if (sensor->sensor_info->intervals.segmented) + return IIO_AVAIL_RANGE; + else + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static void convert_ns_to_freq(u64 interval_ns, u64 *hz, u64 *uhz) +{ + u64 rem, freq; + + freq = NSEC_PER_SEC; + rem = do_div(freq, interval_ns); + *hz = freq; + *uhz = rem * 1000000UL; + do_div(*uhz, interval_ns); +} + +static int scmi_iio_get_odr_val(struct iio_dev *iio_dev, int *val, int *val2) +{ + u64 sensor_update_interval, sensor_interval_mult, hz, uhz; + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u32 sensor_config; + int mult; + + int err = sensor->sensor_ops->config_get(sensor->ph, + sensor->sensor_info->id, + &sensor_config); + if (err) { + dev_err(&iio_dev->dev, + "Error in getting sensor config for sensor %s err %d", + sensor->sensor_info->name, err); + return err; + } + + sensor_update_interval = + SCMI_SENS_CFG_GET_UPDATE_SECS(sensor_config) * NSEC_PER_SEC; + + mult = SCMI_SENS_CFG_GET_UPDATE_EXP(sensor_config); + if (mult < 0) { + sensor_interval_mult = int_pow(10, abs(mult)); + do_div(sensor_update_interval, sensor_interval_mult); + } else { + sensor_interval_mult = int_pow(10, mult); + sensor_update_interval = + sensor_update_interval * sensor_interval_mult; + } + + convert_ns_to_freq(sensor_update_interval, &hz, &uhz); + *val = hz; + *val2 = uhz; + return 0; +} + +static int scmi_iio_read_raw(struct iio_dev *iio_dev, + struct iio_chan_spec const *ch, int *val, + int *val2, long mask) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + s8 scale; + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + scale = sensor->sensor_info->axis[ch->scan_index].scale; + if (scale < 0) { + *val = 1; + *val2 = int_pow(10, abs(scale)); + return IIO_VAL_FRACTIONAL; + } + *val = int_pow(10, scale); + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = scmi_iio_get_odr_val(iio_dev, val, val2); + return ret ? ret : IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static const struct iio_info scmi_iio_info = { + .read_raw = scmi_iio_read_raw, + .read_avail = scmi_iio_read_avail, + .write_raw = scmi_iio_write_raw, +}; + +static ssize_t scmi_iio_get_raw_available(struct iio_dev *iio_dev, + uintptr_t private, + const struct iio_chan_spec *chan, + char *buf) +{ + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + u64 resolution, rem; + s64 min_range, max_range; + s8 exponent, scale; + int len = 0; + + /* + * All the axes are supposed to have the same value for range and resolution. + * We are just using the values from the Axis 0 here. + */ + if (sensor->sensor_info->axis[0].extended_attrs) { + min_range = sensor->sensor_info->axis[0].attrs.min_range; + max_range = sensor->sensor_info->axis[0].attrs.max_range; + resolution = sensor->sensor_info->axis[0].resolution; + exponent = sensor->sensor_info->axis[0].exponent; + scale = sensor->sensor_info->axis[0].scale; + + /* + * To provide the raw value for the resolution to the userspace, + * need to divide the resolution exponent by the sensor scale + */ + exponent = exponent - scale; + if (exponent < 0) { + rem = do_div(resolution, + int_pow(10, abs(exponent)) + ); + len = scnprintf(buf, PAGE_SIZE, + "[%lld %llu.%llu %lld]\n", min_range, + resolution, rem, max_range); + } else { + resolution = resolution * int_pow(10, exponent); + len = scnprintf(buf, PAGE_SIZE, "[%lld %llu %lld]\n", + min_range, resolution, max_range); + } + } + return len; +} + +static const struct iio_chan_spec_ext_info scmi_iio_ext_info[] = { + { + .name = "raw_available", + .read = scmi_iio_get_raw_available, + .shared = IIO_SHARED_BY_TYPE, + }, + {}, +}; + +static void scmi_iio_set_timestamp_channel(struct iio_chan_spec *iio_chan, + int scan_index) +{ + iio_chan->type = IIO_TIMESTAMP; + iio_chan->channel = -1; + iio_chan->scan_index = scan_index; + iio_chan->scan_type.sign = 'u'; + iio_chan->scan_type.realbits = 64; + iio_chan->scan_type.storagebits = 64; +} + +static void scmi_iio_set_data_channel(struct iio_chan_spec *iio_chan, + enum iio_chan_type type, + enum iio_modifier mod, int scan_index) +{ + iio_chan->type = type; + iio_chan->modified = 1; + iio_chan->channel2 = mod; + iio_chan->info_mask_separate = BIT(IIO_CHAN_INFO_SCALE); + iio_chan->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ); + iio_chan->info_mask_shared_by_type_available = + BIT(IIO_CHAN_INFO_SAMP_FREQ); + iio_chan->scan_index = scan_index; + iio_chan->scan_type.sign = 's'; + iio_chan->scan_type.realbits = 64; + iio_chan->scan_type.storagebits = 64; + iio_chan->scan_type.endianness = IIO_LE; + iio_chan->ext_info = scmi_iio_ext_info; +} + +static int scmi_iio_get_chan_modifier(const char *name, + enum iio_modifier *modifier) +{ + char *pch, mod; + + if (!name) + return -EINVAL; + + pch = strrchr(name, '_'); + if (!pch) + return -EINVAL; + + mod = *(pch + 1); + switch (mod) { + case 'X': + *modifier = IIO_MOD_X; + return 0; + case 'Y': + *modifier = IIO_MOD_Y; + return 0; + case 'Z': + *modifier = IIO_MOD_Z; + return 0; + default: + return -EINVAL; + } +} + +static int scmi_iio_get_chan_type(u8 scmi_type, enum iio_chan_type *iio_type) +{ + switch (scmi_type) { + case METERS_SEC_SQUARED: + *iio_type = IIO_ACCEL; + return 0; + case RADIANS_SEC: + *iio_type = IIO_ANGL_VEL; + return 0; + default: + return -EINVAL; + } +} + +static u64 scmi_iio_convert_interval_to_ns(u32 val) +{ + u64 sensor_update_interval = + SCMI_SENS_INTVL_GET_SECS(val) * NSEC_PER_SEC; + u64 sensor_interval_mult; + int mult; + + mult = SCMI_SENS_INTVL_GET_EXP(val); + if (mult < 0) { + sensor_interval_mult = int_pow(10, abs(mult)); + do_div(sensor_update_interval, sensor_interval_mult); + } else { + sensor_interval_mult = int_pow(10, mult); + sensor_update_interval = + sensor_update_interval * sensor_interval_mult; + } + return sensor_update_interval; +} + +static int scmi_iio_set_sampling_freq_avail(struct iio_dev *iio_dev) +{ + u64 cur_interval_ns, low_interval_ns, high_interval_ns, step_size_ns, + hz, uhz; + unsigned int cur_interval, low_interval, high_interval, step_size; + struct scmi_iio_priv *sensor = iio_priv(iio_dev); + int i; + + sensor->freq_avail = + devm_kzalloc(&iio_dev->dev, + sizeof(*sensor->freq_avail) * + (sensor->sensor_info->intervals.count * 2), + GFP_KERNEL); + if (!sensor->freq_avail) + return -ENOMEM; + + if (sensor->sensor_info->intervals.segmented) { + low_interval = sensor->sensor_info->intervals + .desc[SCMI_SENS_INTVL_SEGMENT_LOW]; + low_interval_ns = scmi_iio_convert_interval_to_ns(low_interval); + convert_ns_to_freq(low_interval_ns, &hz, &uhz); + sensor->freq_avail[0] = hz; + sensor->freq_avail[1] = uhz; + + step_size = sensor->sensor_info->intervals + .desc[SCMI_SENS_INTVL_SEGMENT_STEP]; + step_size_ns = scmi_iio_convert_interval_to_ns(step_size); + convert_ns_to_freq(step_size_ns, &hz, &uhz); + sensor->freq_avail[2] = hz; + sensor->freq_avail[3] = uhz; + + high_interval = sensor->sensor_info->intervals + .desc[SCMI_SENS_INTVL_SEGMENT_HIGH]; + high_interval_ns = + scmi_iio_convert_interval_to_ns(high_interval); + convert_ns_to_freq(high_interval_ns, &hz, &uhz); + sensor->freq_avail[4] = hz; + sensor->freq_avail[5] = uhz; + } else { + for (i = 0; i < sensor->sensor_info->intervals.count; i++) { + cur_interval = sensor->sensor_info->intervals.desc[i]; + cur_interval_ns = + scmi_iio_convert_interval_to_ns(cur_interval); + convert_ns_to_freq(cur_interval_ns, &hz, &uhz); + sensor->freq_avail[i * 2] = hz; + sensor->freq_avail[i * 2 + 1] = uhz; + } + } + return 0; +} + +static struct iio_dev * +scmi_alloc_iiodev(struct scmi_device *sdev, + const struct scmi_sensor_proto_ops *ops, + struct scmi_protocol_handle *ph, + const struct scmi_sensor_info *sensor_info) +{ + struct iio_chan_spec *iio_channels; + struct scmi_iio_priv *sensor; + enum iio_modifier modifier; + enum iio_chan_type type; + struct iio_dev *iiodev; + struct device *dev = &sdev->dev; + const struct scmi_handle *handle = sdev->handle; + int i, ret; + + iiodev = devm_iio_device_alloc(dev, sizeof(*sensor)); + if (!iiodev) + return ERR_PTR(-ENOMEM); + + iiodev->modes = INDIO_DIRECT_MODE; + iiodev->dev.parent = dev; + sensor = iio_priv(iiodev); + sensor->sensor_ops = ops; + sensor->ph = ph; + sensor->sensor_info = sensor_info; + sensor->sensor_update_nb.notifier_call = scmi_iio_sensor_update_cb; + sensor->indio_dev = iiodev; + + /* adding one additional channel for timestamp */ + iiodev->num_channels = sensor_info->num_axis + 1; + iiodev->name = sensor_info->name; + iiodev->info = &scmi_iio_info; + + iio_channels = + devm_kzalloc(dev, + sizeof(*iio_channels) * (iiodev->num_channels), + GFP_KERNEL); + if (!iio_channels) + return ERR_PTR(-ENOMEM); + + ret = scmi_iio_set_sampling_freq_avail(iiodev); + if (ret < 0) + return ERR_PTR(ret); + + for (i = 0; i < sensor_info->num_axis; i++) { + ret = scmi_iio_get_chan_type(sensor_info->axis[i].type, &type); + if (ret < 0) + return ERR_PTR(ret); + + ret = scmi_iio_get_chan_modifier(sensor_info->axis[i].name, + &modifier); + if (ret < 0) + return ERR_PTR(ret); + + scmi_iio_set_data_channel(&iio_channels[i], type, modifier, + sensor_info->axis[i].id); + } + + ret = handle->notify_ops->devm_event_notifier_register(sdev, + SCMI_PROTOCOL_SENSOR, SCMI_EVENT_SENSOR_UPDATE, + &sensor->sensor_info->id, + &sensor->sensor_update_nb); + if (ret) { + dev_err(&iiodev->dev, + "Error in registering sensor update notifier for sensor %s err %d", + sensor->sensor_info->name, ret); + return ERR_PTR(ret); + } + + scmi_iio_set_timestamp_channel(&iio_channels[i], i); + iiodev->channels = iio_channels; + return iiodev; +} + +static int scmi_iio_dev_probe(struct scmi_device *sdev) +{ + const struct scmi_sensor_info *sensor_info; + struct scmi_handle *handle = sdev->handle; + const struct scmi_sensor_proto_ops *sensor_ops; + struct scmi_protocol_handle *ph; + struct device *dev = &sdev->dev; + struct iio_dev *scmi_iio_dev; + u16 nr_sensors; + int err = -ENODEV, i; + + if (!handle) + return -ENODEV; + + sensor_ops = handle->devm_protocol_get(sdev, SCMI_PROTOCOL_SENSOR, &ph); + if (IS_ERR(sensor_ops)) { + dev_err(dev, "SCMI device has no sensor interface\n"); + return PTR_ERR(sensor_ops); + } + + nr_sensors = sensor_ops->count_get(ph); + if (!nr_sensors) { + dev_dbg(dev, "0 sensors found via SCMI bus\n"); + return -ENODEV; + } + + for (i = 0; i < nr_sensors; i++) { + sensor_info = sensor_ops->info_get(ph, i); + if (!sensor_info) { + dev_err(dev, "SCMI sensor %d has missing info\n", i); + return -EINVAL; + } + + /* This driver only supports 3-axis accel and gyro, skipping other sensors */ + if (sensor_info->num_axis != SCMI_IIO_NUM_OF_AXIS) + continue; + + /* This driver only supports 3-axis accel and gyro, skipping other sensors */ + if (sensor_info->axis[0].type != METERS_SEC_SQUARED && + sensor_info->axis[0].type != RADIANS_SEC) + continue; + + scmi_iio_dev = scmi_alloc_iiodev(sdev, sensor_ops, ph, + sensor_info); + if (IS_ERR(scmi_iio_dev)) { + dev_err(dev, + "failed to allocate IIO device for sensor %s: %ld\n", + sensor_info->name, PTR_ERR(scmi_iio_dev)); + return PTR_ERR(scmi_iio_dev); + } + + err = devm_iio_kfifo_buffer_setup(&scmi_iio_dev->dev, + scmi_iio_dev, + INDIO_BUFFER_SOFTWARE, + &scmi_iio_buffer_ops); + if (err < 0) { + dev_err(dev, + "IIO buffer setup error at sensor %s: %d\n", + sensor_info->name, err); + return err; + } + + err = devm_iio_device_register(dev, scmi_iio_dev); + if (err) { + dev_err(dev, + "IIO device registration failed at sensor %s: %d\n", + sensor_info->name, err); + return err; + } + } + return err; +} + +static const struct scmi_device_id scmi_id_table[] = { + { SCMI_PROTOCOL_SENSOR, "iiodev" }, + {}, +}; + +MODULE_DEVICE_TABLE(scmi, scmi_id_table); + +static struct scmi_driver scmi_iiodev_driver = { + .name = "scmi-sensor-iiodev", + .probe = scmi_iio_dev_probe, + .id_table = scmi_id_table, +}; + +module_scmi_driver(scmi_iiodev_driver); + +MODULE_AUTHOR("Jyoti Bhayana <jbhayana@google.com>"); +MODULE_DESCRIPTION("SCMI IIO Driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c index eee30130ae23..802f9ae04cf4 100644 --- a/drivers/iio/common/st_sensors/st_sensors_buffer.c +++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c @@ -57,7 +57,7 @@ irqreturn_t st_sensors_trigger_handler(int irq, void *p) s64 timestamp; /* - * If we do timetamping here, do it before reading the values, because + * If we do timestamping here, do it before reading the values, because * once we've read the values, new interrupts can occur (when using * the hardware trigger) and the hw_timestamp may get updated. * By storing it in a local variable first, we are safe. diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c index 2dbd2646e44e..0b511665dee5 100644 --- a/drivers/iio/common/st_sensors/st_sensors_trigger.c +++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c @@ -123,7 +123,8 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, unsigned long irq_trig; int err; - sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name); + sdata->trig = iio_trigger_alloc(sdata->dev, "%s-trigger", + indio_dev->name); if (sdata->trig == NULL) { dev_err(&indio_dev->dev, "failed to allocate iio trigger.\n"); return -ENOMEM; @@ -131,7 +132,6 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev, iio_trigger_set_drvdata(sdata->trig, indio_dev); sdata->trig->ops = trigger_ops; - sdata->trig->dev.parent = sdata->dev; irq_trig = irqd_get_trigger_type(irq_get_irq_data(sdata->irq)); /* diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index cea07b4cced1..75e1f2b48638 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -142,8 +142,9 @@ config AD5696_I2C select AD5686 help Say yes here to build support for Analog Devices AD5311R, AD5338R, - AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R, AD5694, AD5694R, - AD5695R, AD5696, and AD5696R Digital to Analog converters. + AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, AD5693R, + AD5694, AD5694R, AD5695R, AD5696, and AD5696R Digital to Analog + converters. To compile this driver as a module, choose M here: the module will be called ad5696. diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c index 82abd4d6886c..dff623b65e4f 100644 --- a/drivers/iio/dac/ad5064.c +++ b/drivers/iio/dac/ad5064.c @@ -277,7 +277,7 @@ static ssize_t ad5064_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5064_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", st->pwr_down[chan->channel]); + return sysfs_emit(buf, "%d\n", st->pwr_down[chan->channel]); } static ssize_t ad5064_write_dac_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c index 602dd2ba61b5..2d3b14c407d8 100644 --- a/drivers/iio/dac/ad5360.c +++ b/drivers/iio/dac/ad5360.c @@ -255,7 +255,7 @@ static ssize_t ad5360_read_dac_powerdown(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct ad5360_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN)); + return sysfs_emit(buf, "%d\n", (bool)(st->ctrl & AD5360_SF_CTRL_PWR_DOWN)); } static int ad5360_update_ctrl(struct iio_dev *indio_dev, unsigned int set, diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c index 37ef653564b0..53db5b4e4c53 100644 --- a/drivers/iio/dac/ad5380.c +++ b/drivers/iio/dac/ad5380.c @@ -85,7 +85,7 @@ static ssize_t ad5380_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5380_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", st->pwr_down); + return sysfs_emit(buf, "%d\n", st->pwr_down); } static ssize_t ad5380_write_dac_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c index d87e21016863..488ec69967d6 100644 --- a/drivers/iio/dac/ad5446.c +++ b/drivers/iio/dac/ad5446.c @@ -100,7 +100,7 @@ static ssize_t ad5446_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5446_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", st->pwr_down); + return sysfs_emit(buf, "%d\n", st->pwr_down); } static ssize_t ad5446_write_dac_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c index e9297c25d4ef..19cdf9890d02 100644 --- a/drivers/iio/dac/ad5504.c +++ b/drivers/iio/dac/ad5504.c @@ -39,7 +39,7 @@ #define AD5504_DAC_PWRDN_3STATE 1 /** - * struct ad5446_state - driver instance specific data + * struct ad5504_state - driver instance specific data * @spi: spi_device * @reg: supply regulator * @vref_mv: actual reference voltage used @@ -170,8 +170,8 @@ static ssize_t ad5504_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5504_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", - !(st->pwr_down_mask & (1 << chan->channel))); + return sysfs_emit(buf, "%d\n", + !(st->pwr_down_mask & (1 << chan->channel))); } static ssize_t ad5504_write_dac_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c index 2b2b8edfd258..9bde86982912 100644 --- a/drivers/iio/dac/ad5624r_spi.c +++ b/drivers/iio/dac/ad5624r_spi.c @@ -117,8 +117,8 @@ static ssize_t ad5624r_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5624r_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", - !!(st->pwr_down_mask & (1 << chan->channel))); + return sysfs_emit(buf, "%d\n", + !!(st->pwr_down_mask & (1 << chan->channel))); } static ssize_t ad5624r_write_dac_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c index 7d6792ac1020..fcb64f20ff64 100644 --- a/drivers/iio/dac/ad5686.c +++ b/drivers/iio/dac/ad5686.c @@ -57,7 +57,7 @@ static ssize_t ad5686_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5686_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", !!(st->pwr_down_mask & + return sysfs_emit(buf, "%d\n", !!(st->pwr_down_mask & (0x3 << (chan->channel * 2)))); } @@ -301,6 +301,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .num_channels = 8, .regmap_type = AD5686_REGMAP, }, + [ID_AD5673R] = { + .channels = ad5674r_channels, + .int_vref_mv = 2500, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, + }, [ID_AD5674R] = { .channels = ad5674r_channels, .int_vref_mv = 2500, @@ -324,6 +330,12 @@ static const struct ad5686_chip_info ad5686_chip_info_tbl[] = { .num_channels = 8, .regmap_type = AD5686_REGMAP, }, + [ID_AD5677R] = { + .channels = ad5679r_channels, + .int_vref_mv = 2500, + .num_channels = 16, + .regmap_type = AD5686_REGMAP, + }, [ID_AD5679R] = { .channels = ad5679r_channels, .int_vref_mv = 2500, diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h index d9c8ba413fe9..f89a6f92b427 100644 --- a/drivers/iio/dac/ad5686.h +++ b/drivers/iio/dac/ad5686.h @@ -55,10 +55,12 @@ enum ad5686_supported_device_ids { ID_AD5338R, ID_AD5671R, ID_AD5672R, + ID_AD5673R, ID_AD5674R, ID_AD5675R, ID_AD5676, ID_AD5676R, + ID_AD5677R, ID_AD5679R, ID_AD5681R, ID_AD5682R, diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c index a39eda7c02d2..24a6a4a5a2e0 100644 --- a/drivers/iio/dac/ad5696-i2c.c +++ b/drivers/iio/dac/ad5696-i2c.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* - * AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R, - * AD5694, AD5694R, AD5695R, AD5696, AD5696R + * AD5338R, AD5671R, AD5673R, AD5675R, AD5677R, AD5691R, AD5692R, AD5693, + * AD5693R, AD5694, AD5694R, AD5695R, AD5696, AD5696R * Digital to analog converters driver * * Copyright 2018 Analog Devices Inc. @@ -74,7 +74,9 @@ static const struct i2c_device_id ad5686_i2c_id[] = { {"ad5311r", ID_AD5311R}, {"ad5338r", ID_AD5338R}, {"ad5671r", ID_AD5671R}, + {"ad5673r", ID_AD5673R}, {"ad5675r", ID_AD5675R}, + {"ad5677r", ID_AD5677R}, {"ad5691r", ID_AD5691R}, {"ad5692r", ID_AD5692R}, {"ad5693", ID_AD5693}, diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index 0df28acf074a..cabc38d54085 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -399,8 +399,8 @@ static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv, { struct ad5755_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", - (bool)(st->pwr_down & (1 << chan->channel))); + return sysfs_emit(buf, "%d\n", + (bool)(st->pwr_down & (1 << chan->channel))); } static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv, diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c index bd9ac8359d98..0572ef518101 100644 --- a/drivers/iio/dac/ad5758.c +++ b/drivers/iio/dac/ad5758.c @@ -574,7 +574,7 @@ static ssize_t ad5758_read_powerdown(struct iio_dev *indio_dev, { struct ad5758_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", st->pwr_down); + return sysfs_emit(buf, "%d\n", st->pwr_down); } static ssize_t ad5758_write_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index ef1618ea6a20..79837a4b3a41 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -89,7 +89,7 @@ static const char * const ad5766_dither_scales[] = { /** * struct ad5766_state - driver instance specific data * @spi: SPI device - * @lock: Lock used to restrict concurent access to SPI device + * @lock: Lock used to restrict concurrent access to SPI device * @chip_info: Chip model specific constants * @gpio_reset: Reset GPIO, used to reset the device * @crt_range: Current selected output range diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index 84dcf149261f..7ab2ccf90863 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -118,7 +118,7 @@ struct ad5770r_out_range { }; /** - * struct ad5770R_state - driver instance specific data + * struct ad5770r_state - driver instance specific data * @spi: spi_device * @regmap: regmap * @vref_reg: fixed regulator for reference configuration @@ -433,7 +433,7 @@ static ssize_t ad5770r_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5770r_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", st->ch_pwr_down[chan->channel]); + return sysfs_emit(buf, "%d\n", st->ch_pwr_down[chan->channel]); } static ssize_t ad5770r_write_dac_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c index e3ffa4b9f84c..a0923b76e8b6 100644 --- a/drivers/iio/dac/ad5791.c +++ b/drivers/iio/dac/ad5791.c @@ -76,7 +76,7 @@ struct ad5791_chip_info { * @chip_info: chip model specific constants * @vref_mv: actual reference voltage used * @vref_neg_mv: voltage of the negative supply - * @ctrl: control regster cache + * @ctrl: control register cache * @pwr_down_mode: current power down mode * @pwr_down: true if device is powered down * @data: spi transfer buffers @@ -177,7 +177,7 @@ static ssize_t ad5791_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad5791_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", st->pwr_down); + return sysfs_emit(buf, "%d\n", st->pwr_down); } static ssize_t ad5791_write_dac_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c index dbb4645ab6b1..e1b6a92df12f 100644 --- a/drivers/iio/dac/ad7303.c +++ b/drivers/iio/dac/ad7303.c @@ -65,7 +65,7 @@ static ssize_t ad7303_read_dac_powerdown(struct iio_dev *indio_dev, { struct ad7303_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", (bool)(st->config & + return sysfs_emit(buf, "%d\n", (bool)(st->config & AD7303_CFG_POWER_DOWN(chan->channel))); } diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c index 4002ed0868be..53e4b887d372 100644 --- a/drivers/iio/dac/ltc2632.c +++ b/drivers/iio/dac/ltc2632.c @@ -135,8 +135,8 @@ static ssize_t ltc2632_read_dac_powerdown(struct iio_dev *indio_dev, { struct ltc2632_state *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", - !!(st->powerdown_cache_mask & (1 << chan->channel))); + return sysfs_emit(buf, "%d\n", + !!(st->powerdown_cache_mask & (1 << chan->channel))); } static ssize_t ltc2632_write_dac_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/max517.c b/drivers/iio/dac/max517.c index daa60386bf0c..a6ef555153f4 100644 --- a/drivers/iio/dac/max517.c +++ b/drivers/iio/dac/max517.c @@ -153,7 +153,6 @@ static int max517_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); data->client = client; switch (id->driver_data) { @@ -186,13 +185,7 @@ static int max517_probe(struct i2c_client *client, data->vref_mv[chan] = platform_data->vref_mv[chan]; } - return iio_device_register(indio_dev); -} - -static int max517_remove(struct i2c_client *client) -{ - iio_device_unregister(i2c_get_clientdata(client)); - return 0; + return devm_iio_device_register(&client->dev, indio_dev); } static const struct i2c_device_id max517_id[] = { @@ -211,7 +204,6 @@ static struct i2c_driver max517_driver = { .pm = &max517_pm_ops, }, .probe = max517_probe, - .remove = max517_remove, .id_table = max517_id, }; module_i2c_driver(max517_driver); diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c index d6bb24db49c4..bd6e75699a63 100644 --- a/drivers/iio/dac/max5821.c +++ b/drivers/iio/dac/max5821.c @@ -84,7 +84,7 @@ static ssize_t max5821_read_dac_powerdown(struct iio_dev *indio_dev, { struct max5821_data *st = iio_priv(indio_dev); - return sprintf(buf, "%d\n", st->powerdown[chan->channel]); + return sysfs_emit(buf, "%d\n", st->powerdown[chan->channel]); } static int max5821_sync_powerdown_mode(struct max5821_data *data, diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c index beb9a15b7c74..34b14aafb630 100644 --- a/drivers/iio/dac/mcp4725.c +++ b/drivers/iio/dac/mcp4725.c @@ -167,7 +167,7 @@ static ssize_t mcp4725_read_powerdown(struct iio_dev *indio_dev, { struct mcp4725_data *data = iio_priv(indio_dev); - return sprintf(buf, "%d\n", data->powerdown); + return sysfs_emit(buf, "%d\n", data->powerdown); } static ssize_t mcp4725_write_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index 12dec68c16f7..a5b0a52bf86e 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -210,7 +210,7 @@ static ssize_t stm32_dac_read_powerdown(struct iio_dev *indio_dev, if (ret < 0) return ret; - return sprintf(buf, "%d\n", ret ? 0 : 1); + return sysfs_emit(buf, "%d\n", ret ? 0 : 1); } static ssize_t stm32_dac_write_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c index de33c1fc6e0b..5c14bfb16521 100644 --- a/drivers/iio/dac/ti-dac082s085.c +++ b/drivers/iio/dac/ti-dac082s085.c @@ -121,7 +121,7 @@ static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev, { struct ti_dac_chip *ti_dac = iio_priv(indio_dev); - return sprintf(buf, "%d\n", ti_dac->powerdown); + return sysfs_emit(buf, "%d\n", ti_dac->powerdown); } static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index d3295767a079..2a5ba1b08a1d 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -166,7 +166,7 @@ static ssize_t dac5571_read_powerdown(struct iio_dev *indio_dev, { struct dac5571_data *data = iio_priv(indio_dev); - return sprintf(buf, "%d\n", data->powerdown[chan->channel]); + return sysfs_emit(buf, "%d\n", data->powerdown[chan->channel]); } static ssize_t dac5571_write_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c index 63171e42f987..9d0b253be841 100644 --- a/drivers/iio/dac/ti-dac7311.c +++ b/drivers/iio/dac/ti-dac7311.c @@ -110,7 +110,7 @@ static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev, { struct ti_dac_chip *ti_dac = iio_priv(indio_dev); - return sprintf(buf, "%d\n", ti_dac->powerdown); + return sysfs_emit(buf, "%d\n", ti_dac->powerdown); } static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev, diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c index 5512d5edc707..59aa60d4ca37 100644 --- a/drivers/iio/dummy/iio_simple_dummy_buffer.c +++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c @@ -16,9 +16,9 @@ #include <linux/bitmap.h> #include <linux/iio/iio.h> -#include <linux/iio/trigger_consumer.h> #include <linux/iio/buffer.h> -#include <linux/iio/kfifo_buf.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> #include "iio_simple_dummy.h" @@ -103,64 +103,9 @@ static const struct iio_buffer_setup_ops iio_simple_dummy_buffer_setup_ops = { int iio_simple_dummy_configure_buffer(struct iio_dev *indio_dev) { - int ret; - struct iio_buffer *buffer; - - /* Allocate a buffer to use - here a kfifo */ - buffer = iio_kfifo_allocate(); - if (!buffer) { - ret = -ENOMEM; - goto error_ret; - } - - iio_device_attach_buffer(indio_dev, buffer); - - /* - * Tell the core what device type specific functions should - * be run on either side of buffer capture enable / disable. - */ - indio_dev->setup_ops = &iio_simple_dummy_buffer_setup_ops; - - /* - * Configure a polling function. - * When a trigger event with this polling function connected - * occurs, this function is run. Typically this grabs data - * from the device. - * - * NULL for the bottom half. This is normally implemented only if we - * either want to ping a capture now pin (no sleeping) or grab - * a timestamp as close as possible to a data ready trigger firing. - * - * IRQF_ONESHOT ensures irqs are masked such that only one instance - * of the handler can run at a time. - * - * "iio_simple_dummy_consumer%d" formatting string for the irq 'name' - * as seen under /proc/interrupts. Remaining parameters as per printk. - */ - indio_dev->pollfunc = iio_alloc_pollfunc(NULL, - &iio_simple_dummy_trigger_h, - IRQF_ONESHOT, - indio_dev, - "iio_simple_dummy_consumer%d", - indio_dev->id); - - if (!indio_dev->pollfunc) { - ret = -ENOMEM; - goto error_free_buffer; - } - - /* - * Notify the core that this device is capable of buffered capture - * driven by a trigger. - */ - indio_dev->modes |= INDIO_BUFFER_TRIGGERED; - - return 0; - -error_free_buffer: - iio_kfifo_free(indio_dev->buffer); -error_ret: - return ret; + return iio_triggered_buffer_setup(indio_dev, NULL, + iio_simple_dummy_trigger_h, + &iio_simple_dummy_buffer_setup_ops); } /** @@ -169,6 +114,5 @@ error_ret: */ void iio_simple_dummy_unconfigure_buffer(struct iio_dev *indio_dev) { - iio_dealloc_pollfunc(indio_dev->pollfunc); - iio_kfifo_free(indio_dev->buffer); + iio_triggered_buffer_cleanup(indio_dev); } diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index c45d8226cc2b..cec5e1f17c22 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -593,7 +593,6 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev) if (!st->dready_trig) return -ENOMEM; - st->dready_trig->dev.parent = &st->spi->dev; st->dready_trig->ops = &adxrs290_trigger_ops; iio_trigger_set_drvdata(st->dready_trig, indio_dev); diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index 029ef4c34604..b11ebd9bb7a4 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -1148,14 +1148,12 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, if (!data->motion_trig) return -ENOMEM; - data->dready_trig->dev.parent = dev; data->dready_trig->ops = &bmg160_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); if (ret) return ret; - data->motion_trig->dev.parent = dev; data->motion_trig->ops = &bmg160_trigger_ops; iio_trigger_set_drvdata(data->motion_trig, indio_dev); ret = iio_trigger_register(data->motion_trig); diff --git a/drivers/iio/gyro/fxas21002c_core.c b/drivers/iio/gyro/fxas21002c_core.c index 129eead8febc..1a20c6b88e7d 100644 --- a/drivers/iio/gyro/fxas21002c_core.c +++ b/drivers/iio/gyro/fxas21002c_core.c @@ -875,7 +875,6 @@ static int fxas21002c_trigger_probe(struct fxas21002c_data *data) if (ret < 0) return ret; - data->dready_trig->dev.parent = dev; data->dready_trig->ops = &fxas21002c_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index fb0d678ece1a..dad26ee4fd1f 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -45,6 +45,10 @@ static const u32 gyro_3d_addresses[GYRO_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_ANGL_VELOCITY_Z_AXIS }; +static const u32 gryo_3d_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ANGL_VELOCITY, +}; + /* Channel definitions */ static const struct iio_chan_spec gyro_3d_channels[] = { { @@ -271,17 +275,6 @@ static int gyro_3d_parse_report(struct platform_device *pdev, &st->gyro[CHANNEL_SCAN_INDEX_X], &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ANGL_VELOCITY, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } return ret; } @@ -305,7 +298,9 @@ static int hid_gyro_3d_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_GYRO_3D, - &gyro_state->common_attributes); + &gyro_state->common_attributes, + gryo_3d_sensitivity_addresses, + ARRAY_SIZE(gryo_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index 1c3c1bd53374..af0aaa146f0c 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -113,7 +113,7 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev) int ret; struct itg3200 *st = iio_priv(indio_dev); - st->trig = iio_trigger_alloc("%s-dev%d", indio_dev->name, + st->trig = iio_trigger_alloc(&st->i2c->dev, "%s-dev%d", indio_dev->name, indio_dev->id); if (!st->trig) return -ENOMEM; @@ -127,7 +127,6 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev) goto error_free_trig; - st->trig->dev.parent = &st->i2c->dev; st->trig->ops = &itg3200_trigger_ops; iio_trigger_set_drvdata(st->trig, indio_dev); ret = iio_trigger_register(st->trig); diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c index ac7c170a20de..46ed12771d2f 100644 --- a/drivers/iio/gyro/ssp_gyro_sensor.c +++ b/drivers/iio/gyro/ssp_gyro_sensor.c @@ -96,7 +96,6 @@ static int ssp_gyro_probe(struct platform_device *pdev) int ret; struct iio_dev *indio_dev; struct ssp_sensor_data *spd; - struct iio_buffer *buffer; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*spd)); if (!indio_dev) @@ -109,18 +108,15 @@ static int ssp_gyro_probe(struct platform_device *pdev) indio_dev->name = ssp_gyro_name; indio_dev->info = &ssp_gyro_iio_info; - indio_dev->modes = INDIO_BUFFER_SOFTWARE; indio_dev->channels = ssp_gyro_channels; indio_dev->num_channels = ARRAY_SIZE(ssp_gyro_channels); indio_dev->available_scan_masks = ssp_gyro_scan_mask; - buffer = devm_iio_kfifo_allocate(&pdev->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - - indio_dev->setup_ops = &ssp_gyro_buffer_ops; + ret = devm_iio_kfifo_buffer_setup(&pdev->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &ssp_gyro_buffer_ops); + if (ret) + return ret; platform_set_drvdata(pdev, indio_dev); diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 38734e4ce360..1fa8d51d5080 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -531,7 +531,6 @@ static int afe4403_probe(struct spi_device *spi) iio_trigger_set_drvdata(afe->trig, indio_dev); afe->trig->ops = &afe4403_trigger_ops; - afe->trig->dev.parent = afe->dev; ret = iio_trigger_register(afe->trig); if (ret) { diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index 61fe4932d81d..e1476bf79fe2 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -538,7 +538,6 @@ static int afe4404_probe(struct i2c_client *client, iio_trigger_set_drvdata(afe->trig, indio_dev); afe->trig->ops = &afe4404_trigger_ops; - afe->trig->dev.parent = afe->dev; ret = iio_trigger_register(afe->trig); if (ret) { diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 38aa2030f3c6..36ba7611d9ce 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -418,7 +418,6 @@ static int max30100_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max30100_data *data; - struct iio_buffer *buffer; struct iio_dev *indio_dev; int ret; @@ -426,19 +425,18 @@ static int max30100_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - buffer = devm_iio_kfifo_allocate(&client->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->name = MAX30100_DRV_NAME; indio_dev->channels = max30100_channels; indio_dev->info = &max30100_info; indio_dev->num_channels = ARRAY_SIZE(max30100_channels); indio_dev->available_scan_masks = max30100_scan_masks; - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); - indio_dev->setup_ops = &max30100_buffer_setup_ops; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &max30100_buffer_setup_ops); + if (ret) + return ret; data = iio_priv(indio_dev); data->indio_dev = indio_dev; diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index b35557a54ee2..2292876c55e2 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -506,7 +506,6 @@ static int max30102_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct max30102_data *data; - struct iio_buffer *buffer; struct iio_dev *indio_dev; int ret; unsigned int reg; @@ -515,16 +514,9 @@ static int max30102_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - buffer = devm_iio_kfifo_allocate(&client->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->name = MAX30102_DRV_NAME; indio_dev->info = &max30102_info; - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); - indio_dev->setup_ops = &max30102_buffer_setup_ops; + indio_dev->modes = INDIO_DIRECT_MODE; data = iio_priv(indio_dev); data->indio_dev = indio_dev; @@ -549,6 +541,12 @@ static int max30102_probe(struct i2c_client *client, return -ENODEV; } + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &max30102_buffer_setup_ops); + if (ret) + return ret; + data->regmap = devm_regmap_init_i2c(client, &max30102_regmap_config); if (IS_ERR(data->regmap)) { dev_err(&client->dev, "regmap initialization failed\n"); diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index 02ad1767c845..23bc9c784ef4 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Aosong AM2315 relative humidity and temperature * * Copyright (c) 2016, Intel Corporation. diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index d62705448ae2..74383abc0d44 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -25,6 +25,10 @@ struct hid_humidity_state { int value_offset; }; +static const u32 humidity_sensitivity_addresses[] = { + HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY, +}; + /* Channel definitions */ static const struct iio_chan_spec humidity_channels[] = { { @@ -176,14 +180,6 @@ static int humidity_parse_report(struct platform_device *pdev, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY, - &st->common_attributes.sensitivity); - return ret; } @@ -212,7 +208,9 @@ static int hid_humidity_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_HUMIDITY, - &humid_st->common_attributes); + &humid_st->common_attributes, + humidity_sensitivity_addresses, + ARRAY_SIZE(humidity_sensitivity_addresses)); if (ret) return ret; diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index 95e56917677f..f29692b9d2db 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -135,7 +135,6 @@ int hts221_allocate_trigger(struct iio_dev *iio_dev) iio_trigger_set_drvdata(hw->trig, iio_dev); hw->trig->ops = &hts221_trigger_ops; - hw->trig->dev.parent = hw->dev; iio_dev->trig = iio_trigger_get(hw->trig); return devm_iio_trigger_register(hw->dev, hw->trig); diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h index fced02cadcc3..8f4a9b264962 100644 --- a/drivers/iio/iio_core.h +++ b/drivers/iio/iio_core.h @@ -12,11 +12,17 @@ #include <linux/kernel.h> #include <linux/device.h> +struct iio_buffer; struct iio_chan_spec; struct iio_dev; extern struct device_type iio_device_type; +struct iio_dev_buffer_pair { + struct iio_dev *indio_dev; + struct iio_buffer *buffer; +}; + #define IIO_IOCTL_UNHANDLED 1 struct iio_ioctl_handler { struct list_head entry; @@ -43,9 +49,13 @@ int __iio_add_chan_devattr(const char *postfix, u64 mask, enum iio_shared_by shared_by, struct device *dev, + struct iio_buffer *buffer, struct list_head *attr_list); void iio_free_chan_devattr_list(struct list_head *attr_list); +int iio_device_register_sysfs_group(struct iio_dev *indio_dev, + const struct attribute_group *group); + ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals); /* Event interface flags */ @@ -54,34 +64,36 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals); #ifdef CONFIG_IIO_BUFFER struct poll_table_struct; -__poll_t iio_buffer_poll(struct file *filp, - struct poll_table_struct *wait); -ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, - size_t n, loff_t *f_ps); +__poll_t iio_buffer_poll_wrapper(struct file *filp, + struct poll_table_struct *wait); +ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, + size_t n, loff_t *f_ps); -int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev); -void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev); +int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev); +void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev); -#define iio_buffer_poll_addr (&iio_buffer_poll) -#define iio_buffer_read_outer_addr (&iio_buffer_read_outer) +#define iio_buffer_poll_addr (&iio_buffer_poll_wrapper) +#define iio_buffer_read_outer_addr (&iio_buffer_read_wrapper) void iio_disable_all_buffers(struct iio_dev *indio_dev); void iio_buffer_wakeup_poll(struct iio_dev *indio_dev); +void iio_device_detach_buffers(struct iio_dev *indio_dev); #else #define iio_buffer_poll_addr NULL #define iio_buffer_read_outer_addr NULL -static inline int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) +static inline int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { return 0; } -static inline void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) {} +static inline void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) {} static inline void iio_disable_all_buffers(struct iio_dev *indio_dev) {} static inline void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) {} +static inline void iio_device_detach_buffers(struct iio_dev *indio_dev) {} #endif diff --git a/drivers/iio/iio_core_trigger.h b/drivers/iio/iio_core_trigger.h index 374816bc3e73..e1a56824e07f 100644 --- a/drivers/iio/iio_core_trigger.h +++ b/drivers/iio/iio_core_trigger.h @@ -9,8 +9,10 @@ /** * iio_device_register_trigger_consumer() - set up an iio_dev to use triggers * @indio_dev: iio_dev associated with the device that will consume the trigger + * + * Return 0 if successful, negative otherwise **/ -void iio_device_register_trigger_consumer(struct iio_dev *indio_dev); +int iio_device_register_trigger_consumer(struct iio_dev *indio_dev); /** * iio_device_unregister_trigger_consumer() - reverse the registration process diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 785a4ce606d8..768aa493a1a6 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -504,7 +504,6 @@ static int adis16400_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long info) { struct adis16400_state *st = iio_priv(indio_dev); - struct mutex *slock = &st->adis.state_lock; int ret, sps; switch (info) { @@ -517,18 +516,18 @@ static int adis16400_write_raw(struct iio_dev *indio_dev, * Need to cache values so we can update if the frequency * changes. */ - mutex_lock(slock); + adis_dev_lock(&st->adis); st->filt_int = val; /* Work out update to current value */ sps = st->variant->get_freq(st); if (sps < 0) { - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return sps; } ret = __adis16400_set_filter(indio_dev, sps, val * 1000 + val2 / 1000); - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return ret; case IIO_CHAN_INFO_SAMP_FREQ: sps = val * 1000 + val2 / 1000; @@ -536,9 +535,9 @@ static int adis16400_write_raw(struct iio_dev *indio_dev, if (sps <= 0) return -EINVAL; - mutex_lock(slock); + adis_dev_lock(&st->adis); ret = st->variant->set_freq(st, sps); - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return ret; default: return -EINVAL; @@ -549,7 +548,6 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long info) { struct adis16400_state *st = iio_priv(indio_dev); - struct mutex *slock = &st->adis.state_lock; int16_t val16; int ret; @@ -605,17 +603,17 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, *val = st->variant->temp_offset; return IIO_VAL_INT; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: - mutex_lock(slock); + adis_dev_lock(&st->adis); /* Need both the number of taps and the sampling frequency */ ret = __adis_read_reg_16(&st->adis, ADIS16400_SENS_AVG, &val16); if (ret) { - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return ret; } ret = st->variant->get_freq(st); - mutex_unlock(slock); + adis_dev_unlock(&st->adis); if (ret) return ret; ret /= adis16400_3db_divisors[val16 & 0x07]; @@ -623,9 +621,9 @@ static int adis16400_read_raw(struct iio_dev *indio_dev, *val2 = (ret % 1000) * 1000; return IIO_VAL_INT_PLUS_MICRO; case IIO_CHAN_INFO_SAMP_FREQ: - mutex_lock(slock); + adis_dev_lock(&st->adis); ret = st->variant->get_freq(st); - mutex_unlock(slock); + adis_dev_unlock(&st->adis); if (ret) return ret; *val = ret / 1000; diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index 74a161e39733..73bf45e859b8 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -403,12 +403,12 @@ static int adis16460_probe(struct spi_device *spi) if (ret) return ret; + /* We cannot mask the interrupt, so ensure it isn't auto enabled */ + st->adis.irq_flag |= IRQF_NO_AUTOEN; ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL); if (ret) return ret; - adis16460_enable_irq(&st->adis, 0); - ret = __adis_initial_startup(&st->adis); if (ret) return ret; diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 197d48240991..1de62fc79e0f 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -17,6 +17,8 @@ #include <linux/iio/sysfs.h> #include <linux/iio/trigger_consumer.h> #include <linux/irq.h> +#include <linux/lcm.h> +#include <linux/math.h> #include <linux/module.h> #include <linux/mod_devicetable.h> #include <linux/property.h> @@ -101,6 +103,7 @@ struct adis16475 { u32 clk_freq; bool burst32; unsigned long lsb_flag; + u16 sync_mode; /* Alignment needed for the timestamp */ __be16 data[ADIS16475_MAX_SCAN_DATA] __aligned(8); }; @@ -117,6 +120,11 @@ enum { ADIS16475_SCAN_CRC_FAILURE, }; +static bool low_rate_allow; +module_param(low_rate_allow, bool, 0444); +MODULE_PARM_DESC(low_rate_allow, + "Allow IMU rates below the minimum advisable when external clk is used in SCALED mode (default: N)"); + #ifdef CONFIG_DEBUG_FS static ssize_t adis16475_show_firmware_revision(struct file *file, char __user *userbuf, @@ -253,25 +261,92 @@ static int adis16475_get_freq(struct adis16475 *st, u32 *freq) { int ret; u16 dec; + u32 sample_rate = st->clk_freq; + + adis_dev_lock(&st->adis); + + if (st->sync_mode == ADIS16475_SYNC_SCALED) { + u16 sync_scale; + + ret = __adis_read_reg_16(&st->adis, ADIS16475_REG_UP_SCALE, &sync_scale); + if (ret) + goto error; + + sample_rate = st->clk_freq * sync_scale; + } - ret = adis_read_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, &dec); + ret = __adis_read_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, &dec); if (ret) - return -EINVAL; + goto error; + + adis_dev_unlock(&st->adis); - *freq = DIV_ROUND_CLOSEST(st->clk_freq, dec + 1); + *freq = DIV_ROUND_CLOSEST(sample_rate, dec + 1); return 0; +error: + adis_dev_unlock(&st->adis); + return ret; } static int adis16475_set_freq(struct adis16475 *st, const u32 freq) { u16 dec; int ret; + u32 sample_rate = st->clk_freq; if (!freq) return -EINVAL; - dec = DIV_ROUND_CLOSEST(st->clk_freq, freq); + adis_dev_lock(&st->adis); + /* + * When using sync scaled mode, the input clock needs to be scaled so that we have + * an IMU sample rate between (optimally) 1900 and 2100. After this, we can use the + * decimation filter to lower the sampling rate in order to get what the user wants. + * Optimally, the user sample rate is a multiple of both the IMU sample rate and + * the input clock. Hence, calculating the sync_scale dynamically gives us better + * chances of achieving a perfect/integer value for DEC_RATE. The math here is: + * 1. lcm of the input clock and the desired output rate. + * 2. get the highest multiple of the previous result lower than the adis max rate. + * 3. The last result becomes the IMU sample rate. Use that to calculate SYNC_SCALE + * and DEC_RATE (to get the user output rate) + */ + if (st->sync_mode == ADIS16475_SYNC_SCALED) { + unsigned long scaled_rate = lcm(st->clk_freq, freq); + int sync_scale; + + /* + * If lcm is bigger than the IMU maximum sampling rate there's no perfect + * solution. In this case, we get the highest multiple of the input clock + * lower than the IMU max sample rate. + */ + if (scaled_rate > 2100000) + scaled_rate = 2100000 / st->clk_freq * st->clk_freq; + else + scaled_rate = 2100000 / scaled_rate * scaled_rate; + + /* + * This is not an hard requirement but it's not advised to run the IMU + * with a sample rate lower than 4000Hz due to possible undersampling + * issues. However, there are users that might really want to take the risk. + * Hence, we provide a module parameter for them. If set, we allow sample + * rates lower than 4KHz. By default, we won't allow this and we just roundup + * the rate to the next multiple of the input clock bigger than 4KHz. This + * is done like this as in some cases (when DEC_RATE is 0) might give + * us the closest value to the one desired by the user... + */ + if (scaled_rate < 1900000 && !low_rate_allow) + scaled_rate = roundup(1900000, st->clk_freq); + + sync_scale = scaled_rate / st->clk_freq; + ret = __adis_write_reg_16(&st->adis, ADIS16475_REG_UP_SCALE, sync_scale); + if (ret) + goto error; + + sample_rate = scaled_rate; + } + + dec = DIV_ROUND_CLOSEST(sample_rate, freq); if (dec) dec--; @@ -281,7 +356,7 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) ret = adis_write_reg_16(&st->adis, ADIS16475_REG_DEC_RATE, dec); if (ret) - return ret; + goto error; /* * If decimation is used, then gyro and accel data will have meaningful @@ -290,6 +365,9 @@ static int adis16475_set_freq(struct adis16475 *st, const u32 freq) assign_bit(ADIS16475_LSB_DEC_MASK, &st->lsb_flag, dec); return 0; +error: + adis_dev_unlock(&st->adis); + return ret; } /* The values are approximated. */ @@ -1085,6 +1163,7 @@ static int adis16475_config_sync_mode(struct adis16475 *st) } sync = &st->info->sync[sync_mode]; + st->sync_mode = sync->sync_mode; /* All the other modes require external input signal */ if (sync->sync_mode != ADIS16475_SYNC_OUTPUT) { @@ -1112,37 +1191,20 @@ static int adis16475_config_sync_mode(struct adis16475 *st) if (sync->sync_mode == ADIS16475_SYNC_SCALED) { u16 up_scale; - u32 scaled_out_freq = 0; + /* - * If we are in scaled mode, we must have an up_scale. - * In scaled mode the allowable input clock range is - * 1 Hz to 128 Hz, and the allowable output range is - * 1900 to 2100 Hz. Hence, a scale must be given to - * get the allowable output. + * In sync scaled mode, the IMU sample rate is the clk_freq * sync_scale. + * Hence, default the IMU sample rate to the highest multiple of the input + * clock lower than the IMU max sample rate. The optimal range is + * 1900-2100 sps... */ - ret = device_property_read_u32(dev, - "adi,scaled-output-hz", - &scaled_out_freq); - if (ret) { - dev_err(dev, "adi,scaled-output-hz must be given when in scaled sync mode"); - return -EINVAL; - } else if (scaled_out_freq < 1900 || - scaled_out_freq > 2100) { - dev_err(dev, "Invalid value: %u for adi,scaled-output-hz", - scaled_out_freq); - return -EINVAL; - } - - up_scale = DIV_ROUND_CLOSEST(scaled_out_freq, - st->clk_freq); + up_scale = 2100 / st->clk_freq; ret = __adis_write_reg_16(&st->adis, ADIS16475_REG_UP_SCALE, up_scale); if (ret) return ret; - - st->clk_freq = scaled_out_freq; } st->clk_freq *= 1000; @@ -1196,6 +1258,9 @@ static int adis16475_config_irq_pin(struct adis16475 *st) return -EINVAL; } + /* We cannot mask the interrupt so ensure it's not enabled at request */ + st->adis.irq_flag |= IRQF_NO_AUTOEN; + val = ADIS16475_MSG_CTRL_DR_POL(polarity); ret = __adis_update_bits(&st->adis, ADIS16475_REG_MSG_CTRL, ADIS16475_MSG_CTRL_DR_POL_MASK, val); @@ -1300,8 +1365,6 @@ static int adis16475_probe(struct spi_device *spi) if (ret) return ret; - adis16475_enable_irq(&st->adis, false); - ret = devm_iio_device_register(&spi->dev, indio_dev); if (ret) return ret; diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index dfe86c589325..f81b86690b76 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -10,6 +10,7 @@ #include <linux/of_irq.h> #include <linux/interrupt.h> #include <linux/delay.h> +#include <linux/math.h> #include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> @@ -17,6 +18,7 @@ #include <linux/slab.h> #include <linux/sysfs.h> #include <linux/module.h> +#include <linux/lcm.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -170,6 +172,11 @@ static const char * const adis16480_int_pin_names[4] = { [ADIS16480_PIN_DIO4] = "DIO4", }; +static bool low_rate_allow; +module_param(low_rate_allow, bool, 0444); +MODULE_PARM_DESC(low_rate_allow, + "Allow IMU rates below the minimum advisable when external clk is used in PPS mode (default: N)"); + #ifdef CONFIG_DEBUG_FS static ssize_t adis16480_show_firmware_revision(struct file *file, @@ -312,7 +319,8 @@ static int adis16480_debugfs_init(struct iio_dev *indio_dev) static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2) { struct adis16480 *st = iio_priv(indio_dev); - unsigned int t, reg; + unsigned int t, sample_rate = st->clk_freq; + int ret; if (val < 0 || val2 < 0) return -EINVAL; @@ -321,28 +329,65 @@ static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2) if (t == 0) return -EINVAL; + adis_dev_lock(&st->adis); /* - * When using PPS mode, the rate of data collection is equal to the - * product of the external clock frequency and the scale factor in the - * SYNC_SCALE register. - * When using sync mode, or internal clock, the output data rate is - * equal with the clock frequency divided by DEC_RATE + 1. + * When using PPS mode, the input clock needs to be scaled so that we have an IMU + * sample rate between (optimally) 4000 and 4250. After this, we can use the + * decimation filter to lower the sampling rate in order to get what the user wants. + * Optimally, the user sample rate is a multiple of both the IMU sample rate and + * the input clock. Hence, calculating the sync_scale dynamically gives us better + * chances of achieving a perfect/integer value for DEC_RATE. The math here is: + * 1. lcm of the input clock and the desired output rate. + * 2. get the highest multiple of the previous result lower than the adis max rate. + * 3. The last result becomes the IMU sample rate. Use that to calculate SYNC_SCALE + * and DEC_RATE (to get the user output rate) */ if (st->clk_mode == ADIS16480_CLK_PPS) { - t = t / st->clk_freq; - reg = ADIS16495_REG_SYNC_SCALE; - } else { - t = st->clk_freq / t; - reg = ADIS16480_REG_DEC_RATE; + unsigned long scaled_rate = lcm(st->clk_freq, t); + int sync_scale; + + /* + * If lcm is bigger than the IMU maximum sampling rate there's no perfect + * solution. In this case, we get the highest multiple of the input clock + * lower than the IMU max sample rate. + */ + if (scaled_rate > st->chip_info->int_clk) + scaled_rate = st->chip_info->int_clk / st->clk_freq * st->clk_freq; + else + scaled_rate = st->chip_info->int_clk / scaled_rate * scaled_rate; + + /* + * This is not an hard requirement but it's not advised to run the IMU + * with a sample rate lower than 4000Hz due to possible undersampling + * issues. However, there are users that might really want to take the risk. + * Hence, we provide a module parameter for them. If set, we allow sample + * rates lower than 4KHz. By default, we won't allow this and we just roundup + * the rate to the next multiple of the input clock bigger than 4KHz. This + * is done like this as in some cases (when DEC_RATE is 0) might give + * us the closest value to the one desired by the user... + */ + if (scaled_rate < 4000000 && !low_rate_allow) + scaled_rate = roundup(4000000, st->clk_freq); + + sync_scale = scaled_rate / st->clk_freq; + ret = __adis_write_reg_16(&st->adis, ADIS16495_REG_SYNC_SCALE, sync_scale); + if (ret) + goto error; + + sample_rate = scaled_rate; } + t = DIV_ROUND_CLOSEST(sample_rate, t); + if (t) + t--; + if (t > st->chip_info->max_dec_rate) t = st->chip_info->max_dec_rate; - if ((t != 0) && (st->clk_mode != ADIS16480_CLK_PPS)) - t--; - - return adis_write_reg_16(&st->adis, reg, t); + ret = __adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t); +error: + adis_dev_unlock(&st->adis); + return ret; } static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2) @@ -350,34 +395,35 @@ static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2) struct adis16480 *st = iio_priv(indio_dev); uint16_t t; int ret; - unsigned int freq; - unsigned int reg; + unsigned int freq, sample_rate = st->clk_freq; - if (st->clk_mode == ADIS16480_CLK_PPS) - reg = ADIS16495_REG_SYNC_SCALE; - else - reg = ADIS16480_REG_DEC_RATE; + adis_dev_lock(&st->adis); + + if (st->clk_mode == ADIS16480_CLK_PPS) { + u16 sync_scale; + + ret = __adis_read_reg_16(&st->adis, ADIS16495_REG_SYNC_SCALE, &sync_scale); + if (ret) + goto error; - ret = adis_read_reg_16(&st->adis, reg, &t); + sample_rate = st->clk_freq * sync_scale; + } + + ret = __adis_read_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, &t); if (ret) - return ret; + goto error; - /* - * When using PPS mode, the rate of data collection is equal to the - * product of the external clock frequency and the scale factor in the - * SYNC_SCALE register. - * When using sync mode, or internal clock, the output data rate is - * equal with the clock frequency divided by DEC_RATE + 1. - */ - if (st->clk_mode == ADIS16480_CLK_PPS) - freq = st->clk_freq * t; - else - freq = st->clk_freq / (t + 1); + adis_dev_unlock(&st->adis); + + freq = DIV_ROUND_CLOSEST(sample_rate, (t + 1)); *val = freq / 1000; *val2 = (freq % 1000) * 1000; return IIO_VAL_INT_PLUS_MICRO; +error: + adis_dev_unlock(&st->adis); + return ret; } enum { @@ -552,7 +598,6 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, unsigned int freq) { struct adis16480 *st = iio_priv(indio_dev); - struct mutex *slock = &st->adis.state_lock; unsigned int enable_mask, offset, reg; unsigned int diff, best_diff; unsigned int i, best_freq; @@ -563,7 +608,7 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, offset = ad16480_filter_data[chan->scan_index][1]; enable_mask = BIT(offset + 2); - mutex_lock(slock); + adis_dev_lock(&st->adis); ret = __adis_read_reg_16(&st->adis, reg, &val); if (ret) @@ -591,7 +636,7 @@ static int adis16480_set_filter_freq(struct iio_dev *indio_dev, ret = __adis_write_reg_16(&st->adis, reg, val); out_unlock: - mutex_unlock(slock); + adis_dev_unlock(&st->adis); return ret; } @@ -1278,6 +1323,20 @@ static int adis16480_probe(struct spi_device *spi) st->clk_freq = clk_get_rate(st->ext_clk); st->clk_freq *= 1000; /* micro */ + if (st->clk_mode == ADIS16480_CLK_PPS) { + u16 sync_scale; + + /* + * In PPS mode, the IMU sample rate is the clk_freq * sync_scale. Hence, + * default the IMU sample rate to the highest multiple of the input clock + * lower than the IMU max sample rate. The internal sample rate is the + * max... + */ + sync_scale = st->chip_info->int_clk / st->clk_freq; + ret = __adis_write_reg_16(&st->adis, ADIS16495_REG_SYNC_SCALE, sync_scale); + if (ret) + return ret; + } } else { st->clk_freq = st->chip_info->int_clk; } diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index 64e0ba51cb18..fa5540fabacc 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -27,27 +27,21 @@ static const struct iio_trigger_ops adis_trigger_ops = { .set_trigger_state = &adis_data_rdy_trigger_set_state, }; -static void adis_trigger_setup(struct adis *adis) -{ - adis->trig->dev.parent = &adis->spi->dev; - adis->trig->ops = &adis_trigger_ops; - iio_trigger_set_drvdata(adis->trig, adis); -} - static int adis_validate_irq_flag(struct adis *adis) { + unsigned long direction = adis->irq_flag & IRQF_TRIGGER_MASK; /* * Typically this devices have data ready either on the rising edge or * on the falling edge of the data ready pin. This checks enforces that * one of those is set in the drivers... It defaults to - * IRQF_TRIGGER_RISING for backward compatibility wiht devices that + * IRQF_TRIGGER_RISING for backward compatibility with devices that * don't support changing the pin polarity. */ - if (!adis->irq_flag) { - adis->irq_flag = IRQF_TRIGGER_RISING; + if (direction == IRQF_TRIGGER_NONE) { + adis->irq_flag |= IRQF_TRIGGER_RISING; return 0; - } else if (adis->irq_flag != IRQF_TRIGGER_RISING && - adis->irq_flag != IRQF_TRIGGER_FALLING) { + } else if (direction != IRQF_TRIGGER_RISING && + direction != IRQF_TRIGGER_FALLING) { dev_err(&adis->spi->dev, "Invalid IRQ mask: %08lx\n", adis->irq_flag); return -EINVAL; @@ -72,7 +66,8 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) if (!adis->trig) return -ENOMEM; - adis_trigger_setup(adis); + adis->trig->ops = &adis_trigger_ops; + iio_trigger_set_drvdata(adis->trig, adis); ret = adis_validate_irq_flag(adis); if (ret) diff --git a/drivers/iio/imu/fxos8700_i2c.c b/drivers/iio/imu/fxos8700_i2c.c index 3ceb76366313..40a570325b0a 100644 --- a/drivers/iio/imu/fxos8700_i2c.c +++ b/drivers/iio/imu/fxos8700_i2c.c @@ -26,8 +26,7 @@ static int fxos8700_i2c_probe(struct i2c_client *client, regmap = devm_regmap_init_i2c(client, &fxos8700_regmap_config); if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/imu/fxos8700_spi.c b/drivers/iio/imu/fxos8700_spi.c index 57e7bb6444e7..27e694cce173 100644 --- a/drivers/iio/imu/fxos8700_spi.c +++ b/drivers/iio/imu/fxos8700_spi.c @@ -17,8 +17,7 @@ static int fxos8700_spi_probe(struct spi_device *spi) regmap = devm_regmap_init_spi(spi, &fxos8700_regmap_config); if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 3441b0d61c5d..383cc3250342 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -709,7 +709,6 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) const char *name; struct inv_icm42600_timestamp *ts; struct iio_dev *indio_dev; - struct iio_buffer *buffer; int ret; name = devm_kasprintf(dev, GFP_KERNEL, "%s-accel", st->name); @@ -720,23 +719,22 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) if (!indio_dev) return ERR_PTR(-ENOMEM); - buffer = devm_iio_kfifo_allocate(dev); - if (!buffer) - return ERR_PTR(-ENOMEM); - ts = iio_priv(indio_dev); inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.accel.odr)); iio_device_set_drvdata(indio_dev, st); indio_dev->name = name; indio_dev->info = &inv_icm42600_accel_info; - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = inv_icm42600_accel_channels; indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_accel_channels); indio_dev->available_scan_masks = inv_icm42600_accel_scan_masks; - indio_dev->setup_ops = &inv_icm42600_buffer_ops; - iio_device_attach_buffer(indio_dev, buffer); + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &inv_icm42600_buffer_ops); + if (ret) + return ERR_PTR(ret); ret = devm_iio_device_register(dev, indio_dev); if (ret) diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index aee7b9ff4bf4..cec1dd0e0464 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -720,7 +720,6 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) const char *name; struct inv_icm42600_timestamp *ts; struct iio_dev *indio_dev; - struct iio_buffer *buffer; int ret; name = devm_kasprintf(dev, GFP_KERNEL, "%s-gyro", st->name); @@ -731,23 +730,23 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) if (!indio_dev) return ERR_PTR(-ENOMEM); - buffer = devm_iio_kfifo_allocate(dev); - if (!buffer) - return ERR_PTR(-ENOMEM); - ts = iio_priv(indio_dev); inv_icm42600_timestamp_init(ts, inv_icm42600_odr_to_period(st->conf.gyro.odr)); iio_device_set_drvdata(indio_dev, st); indio_dev->name = name; indio_dev->info = &inv_icm42600_gyro_info; - indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE; + indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = inv_icm42600_gyro_channels; indio_dev->num_channels = ARRAY_SIZE(inv_icm42600_gyro_channels); indio_dev->available_scan_masks = inv_icm42600_gyro_scan_masks; indio_dev->setup_ops = &inv_icm42600_buffer_ops; - iio_device_attach_buffer(indio_dev, buffer); + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &inv_icm42600_buffer_ops); + if (ret) + return ERR_PTR(ret); ret = devm_iio_device_register(dev, indio_dev); if (ret) diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 453c51c79655..6244a07048df 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -731,12 +731,16 @@ inv_mpu6050_read_raw(struct iio_dev *indio_dev, } } -static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val) +static int inv_mpu6050_write_gyro_scale(struct inv_mpu6050_state *st, int val, + int val2) { int result, i; + if (val != 0) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(gyro_scale_6050); ++i) { - if (gyro_scale_6050[i] == val) { + if (gyro_scale_6050[i] == val2) { result = inv_mpu6050_set_gyro_fsr(st, i); if (result) return result; @@ -767,13 +771,17 @@ static int inv_write_raw_get_fmt(struct iio_dev *indio_dev, return -EINVAL; } -static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val) +static int inv_mpu6050_write_accel_scale(struct inv_mpu6050_state *st, int val, + int val2) { int result, i; u8 d; + if (val != 0) + return -EINVAL; + for (i = 0; i < ARRAY_SIZE(accel_scale); ++i) { - if (accel_scale[i] == val) { + if (accel_scale[i] == val2) { d = (i << INV_MPU6050_ACCL_CONFIG_FSR_SHIFT); result = regmap_write(st->map, st->reg->accl_config, d); if (result) @@ -814,10 +822,10 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_SCALE: switch (chan->type) { case IIO_ANGL_VEL: - result = inv_mpu6050_write_gyro_scale(st, val2); + result = inv_mpu6050_write_gyro_scale(st, val, val2); break; case IIO_ACCEL: - result = inv_mpu6050_write_accel_scale(st, val2); + result = inv_mpu6050_write_accel_scale(st, val, val2); break; default: result = -EINVAL; @@ -1458,15 +1466,21 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, st->plat_data = *pdata; } - desc = irq_get_irq_data(irq); - if (!desc) { - dev_err(dev, "Could not find IRQ %d\n", irq); - return -EINVAL; - } + if (irq > 0) { + desc = irq_get_irq_data(irq); + if (!desc) { + dev_err(dev, "Could not find IRQ %d\n", irq); + return -EINVAL; + } - irq_type = irqd_get_trigger_type(desc); - if (!irq_type) + irq_type = irqd_get_trigger_type(desc); + if (!irq_type) + irq_type = IRQF_TRIGGER_RISING; + } else { + /* Doesn't really matter, use the default */ irq_type = IRQF_TRIGGER_RISING; + } + if (irq_type & IRQF_TRIGGER_RISING) // rising or both-edge st->irq_mask = INV_MPU6050_ACTIVE_HIGH; else if (irq_type == IRQF_TRIGGER_FALLING) @@ -1591,20 +1605,26 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, } indio_dev->info = &mpu_info; - indio_dev->modes = INDIO_BUFFER_TRIGGERED; - result = devm_iio_triggered_buffer_setup(dev, indio_dev, - iio_pollfunc_store_time, - inv_mpu6050_read_fifo, - NULL); - if (result) { - dev_err(dev, "configure buffer fail %d\n", result); - return result; - } - result = inv_mpu6050_probe_trigger(indio_dev, irq_type); - if (result) { - dev_err(dev, "trigger probe fail %d\n", result); - return result; + if (irq > 0) { + /* + * The driver currently only supports buffered capture with its + * own trigger. So no IRQ, no trigger, no buffer + */ + result = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + inv_mpu6050_read_fifo, + NULL); + if (result) { + dev_err(dev, "configure buffer fail %d\n", result); + return result; + } + + result = inv_mpu6050_probe_trigger(indio_dev, irq_type); + if (result) { + dev_err(dev, "trigger probe fail %d\n", result); + return result; + } } result = devm_iio_device_register(dev, indio_dev); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index f7b5a70be30f..de8ed1446d60 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -11,6 +11,16 @@ static unsigned int inv_scan_query_mpu6050(struct iio_dev *indio_dev) struct inv_mpu6050_state *st = iio_priv(indio_dev); unsigned int mask; + /* + * If the MPU6050 is just used as a trigger, then the scan mask + * is not allocated so we simply enable the temperature channel + * as a dummy and bail out. + */ + if (!indio_dev->active_scan_mask) { + st->chip_config.temp_fifo_enable = true; + return INV_MPU6050_SENSOR_TEMP; + } + st->chip_config.gyro_fifo_enable = test_bit(INV_MPU6050_SCAN_GYRO_X, indio_dev->active_scan_mask) || diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index 4377047d503a..fc5a60fcfec0 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -1268,7 +1268,6 @@ static struct iio_trigger *kmx61_trigger_setup(struct kmx61_data *data, if (!trig) return ERR_PTR(-ENOMEM); - trig->dev.parent = &data->client->dev; trig->ops = &kmx61_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index f1103ecedd64..16730a780964 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -739,20 +739,17 @@ static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = { int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw) { - struct iio_buffer *buffer; - int i; + int i, ret; for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { if (!hw->iio_devs[i]) continue; - 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; + ret = devm_iio_kfifo_buffer_setup(hw->dev, hw->iio_devs[i], + INDIO_BUFFER_SOFTWARE, + &st_lsm6dsx_buffer_ops); + if (ret) + return ret; } return 0; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c index ec8d4351390a..8b4fc2c15622 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c @@ -29,8 +29,7 @@ static int st_lsm6dsx_i2c_probe(struct i2c_client *client, regmap = devm_regmap_init_i2c(client, &st_lsm6dsx_i2c_regmap_config); if (IS_ERR(regmap)) { - dev_err(&client->dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&client->dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c index 57e633121bdc..8d4201b86e87 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c @@ -34,8 +34,7 @@ static int st_lsm6dsx_i3c_probe(struct i3c_device *i3cdev) regmap = devm_regmap_init_i3c(i3cdev, &st_lsm6dsx_i3c_regmap_config); if (IS_ERR(regmap)) { - dev_err(&i3cdev->dev, "Failed to register i3c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&i3cdev->dev, "Failed to register i3c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c index 349ec9c1890d..e80110b6b280 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c @@ -29,8 +29,7 @@ static int st_lsm6dsx_spi_probe(struct spi_device *spi) regmap = devm_regmap_init_spi(spi, &st_lsm6dsx_spi_regmap_config); if (IS_ERR(regmap)) { - dev_err(&spi->dev, "Failed to register spi regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(&spi->dev, "Failed to register spi regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 2f7426a2f47c..9a8e16c7e9af 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -9,9 +9,11 @@ * - Better memory allocation techniques? * - Alternative access techniques? */ +#include <linux/anon_inodes.h> #include <linux/kernel.h> #include <linux/export.h> #include <linux/device.h> +#include <linux/file.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/slab.h> @@ -89,7 +91,7 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, } /** - * iio_buffer_read_outer() - chrdev read for buffer access + * iio_buffer_read() - chrdev read for buffer access * @filp: File structure pointer for the char device * @buf: Destination buffer for iio buffer read * @n: First n bytes to read @@ -101,11 +103,12 @@ static bool iio_buffer_ready(struct iio_dev *indio_dev, struct iio_buffer *buf, * Return: negative values corresponding to error codes or ret != 0 * for ending the reading activity **/ -ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, - size_t n, loff_t *f_ps) +static ssize_t iio_buffer_read(struct file *filp, char __user *buf, + size_t n, loff_t *f_ps) { - struct iio_dev *indio_dev = filp->private_data; - struct iio_buffer *rb = indio_dev->buffer; + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + struct iio_dev *indio_dev = ib->indio_dev; DEFINE_WAIT_FUNC(wait, woken_wake_function); size_t datum_size; size_t to_wait; @@ -167,11 +170,12 @@ ssize_t iio_buffer_read_outer(struct file *filp, char __user *buf, * Return: (EPOLLIN | EPOLLRDNORM) if data is available for reading * or 0 for other cases */ -__poll_t iio_buffer_poll(struct file *filp, - struct poll_table_struct *wait) +static __poll_t iio_buffer_poll(struct file *filp, + struct poll_table_struct *wait) { - struct iio_dev *indio_dev = filp->private_data; - struct iio_buffer *rb = indio_dev->buffer; + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + struct iio_dev *indio_dev = ib->indio_dev; if (!indio_dev->info || rb == NULL) return 0; @@ -182,6 +186,32 @@ __poll_t iio_buffer_poll(struct file *filp, return 0; } +ssize_t iio_buffer_read_wrapper(struct file *filp, char __user *buf, + size_t n, loff_t *f_ps) +{ + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + + /* check if buffer was opened through new API */ + if (test_bit(IIO_BUSY_BIT_POS, &rb->flags)) + return -EBUSY; + + return iio_buffer_read(filp, buf, n, f_ps); +} + +__poll_t iio_buffer_poll_wrapper(struct file *filp, + struct poll_table_struct *wait) +{ + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_buffer *rb = ib->buffer; + + /* check if buffer was opened through new API */ + if (test_bit(IIO_BUSY_BIT_POS, &rb->flags)) + return 0; + + return iio_buffer_poll(filp, wait); +} + /** * iio_buffer_wakeup_poll - Wakes up the buffer waitqueue * @indio_dev: The IIO device @@ -191,12 +221,14 @@ __poll_t iio_buffer_poll(struct file *filp, */ void iio_buffer_wakeup_poll(struct iio_dev *indio_dev) { - struct iio_buffer *buffer = indio_dev->buffer; - - if (!buffer) - return; + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_buffer *buffer; + unsigned int i; - wake_up(&buffer->pollq); + for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) { + buffer = iio_dev_opaque->attached_buffers[i]; + wake_up(&buffer->pollq); + } } void iio_buffer_init(struct iio_buffer *buffer) @@ -210,11 +242,25 @@ void iio_buffer_init(struct iio_buffer *buffer) } EXPORT_SYMBOL(iio_buffer_init); +void iio_device_detach_buffers(struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_buffer *buffer; + unsigned int i; + + for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) { + buffer = iio_dev_opaque->attached_buffers[i]; + iio_buffer_put(buffer); + } + + kfree(iio_dev_opaque->attached_buffers); +} + static ssize_t iio_show_scan_index(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%u\n", to_iio_dev_attr(attr)->c->scan_index); + return sysfs_emit(buf, "%u\n", to_iio_dev_attr(attr)->c->scan_index); } static ssize_t iio_show_fixed_type(struct device *dev, @@ -232,15 +278,15 @@ static ssize_t iio_show_fixed_type(struct device *dev, #endif } if (this_attr->c->scan_type.repeat > 1) - return sprintf(buf, "%s:%c%d/%dX%d>>%u\n", + return sysfs_emit(buf, "%s:%c%d/%dX%d>>%u\n", iio_endian_prefix[type], this_attr->c->scan_type.sign, this_attr->c->scan_type.realbits, this_attr->c->scan_type.storagebits, this_attr->c->scan_type.repeat, this_attr->c->scan_type.shift); - else - return sprintf(buf, "%s:%c%d/%d>>%u\n", + else + return sysfs_emit(buf, "%s:%c%d/%d>>%u\n", iio_endian_prefix[type], this_attr->c->scan_type.sign, this_attr->c->scan_type.realbits, @@ -253,14 +299,13 @@ static ssize_t iio_scan_el_show(struct device *dev, char *buf) { int ret; - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; /* Ensure ret is 0 or 1. */ ret = !!test_bit(to_iio_dev_attr(attr)->address, buffer->scan_mask); - return sprintf(buf, "%d\n", ret); + return sysfs_emit(buf, "%d\n", ret); } /* Note NULL used as error indicator as it doesn't make sense. */ @@ -367,8 +412,8 @@ static ssize_t iio_scan_el_store(struct device *dev, int ret; bool state; struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); + struct iio_buffer *buffer = this_attr->buffer; ret = strtobool(buf, &state); if (ret < 0) @@ -402,10 +447,9 @@ static ssize_t iio_scan_el_ts_show(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; - return sprintf(buf, "%d\n", buffer->scan_timestamp); + return sysfs_emit(buf, "%d\n", buffer->scan_timestamp); } static ssize_t iio_scan_el_ts_store(struct device *dev, @@ -415,7 +459,7 @@ static ssize_t iio_scan_el_ts_store(struct device *dev, { int ret; struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; bool state; ret = strtobool(buf, &state); @@ -447,7 +491,8 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, 0, IIO_SEPARATE, &indio_dev->dev, - &buffer->scan_el_dev_attr_list); + buffer, + &buffer->buffer_attr_list); if (ret) return ret; attrcount++; @@ -458,7 +503,8 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, 0, 0, &indio_dev->dev, - &buffer->scan_el_dev_attr_list); + buffer, + &buffer->buffer_attr_list); if (ret) return ret; attrcount++; @@ -470,7 +516,8 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, chan->scan_index, 0, &indio_dev->dev, - &buffer->scan_el_dev_attr_list); + buffer, + &buffer->buffer_attr_list); else ret = __iio_add_chan_devattr("en", chan, @@ -479,7 +526,8 @@ static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, chan->scan_index, 0, &indio_dev->dev, - &buffer->scan_el_dev_attr_list); + buffer, + &buffer->buffer_attr_list); if (ret) return ret; attrcount++; @@ -491,10 +539,9 @@ static ssize_t iio_buffer_read_length(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; - return sprintf(buf, "%d\n", buffer->length); + return sysfs_emit(buf, "%d\n", buffer->length); } static ssize_t iio_buffer_write_length(struct device *dev, @@ -502,7 +549,7 @@ static ssize_t iio_buffer_write_length(struct device *dev, const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; unsigned int val; int ret; @@ -534,10 +581,9 @@ static ssize_t iio_buffer_show_enable(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; - return sprintf(buf, "%d\n", iio_buffer_is_active(buffer)); + return sysfs_emit(buf, "%d\n", iio_buffer_is_active(buffer)); } static unsigned int iio_storage_bytes_for_si(struct iio_dev *indio_dev, @@ -1150,7 +1196,7 @@ static ssize_t iio_buffer_store_enable(struct device *dev, int ret; bool requested_state; struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; bool inlist; ret = strtobool(buf, &requested_state); @@ -1175,16 +1221,13 @@ done: return (ret < 0) ? ret : len; } -static const char * const iio_scan_elements_group_name = "scan_elements"; - static ssize_t iio_buffer_show_watermark(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; - return sprintf(buf, "%u\n", buffer->watermark); + return sysfs_emit(buf, "%u\n", buffer->watermark); } static ssize_t iio_buffer_store_watermark(struct device *dev, @@ -1193,7 +1236,7 @@ static ssize_t iio_buffer_store_watermark(struct device *dev, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; unsigned int val; int ret; @@ -1226,10 +1269,9 @@ static ssize_t iio_dma_show_data_available(struct device *dev, struct device_attribute *attr, char *buf) { - struct iio_dev *indio_dev = dev_to_iio_dev(dev); - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_buffer *buffer = to_iio_dev_attr(attr)->buffer; - return sprintf(buf, "%zu\n", iio_buffer_data_available(buffer)); + return sysfs_emit(buf, "%zu\n", iio_buffer_data_available(buffer)); } static DEVICE_ATTR(length, S_IRUGO | S_IWUSR, iio_buffer_read_length, @@ -1252,45 +1294,194 @@ static struct attribute *iio_buffer_attrs[] = { &dev_attr_data_available.attr, }; -static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, - struct iio_dev *indio_dev) +#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr) + +static struct attribute *iio_buffer_wrap_attr(struct iio_buffer *buffer, + struct attribute *attr) { - struct iio_dev_attr *p; - struct attribute **attr; - int ret, i, attrn, attrcount; - const struct iio_chan_spec *channels; + struct device_attribute *dattr = to_dev_attr(attr); + struct iio_dev_attr *iio_attr; - attrcount = 0; - if (buffer->attrs) { - while (buffer->attrs[attrcount] != NULL) - attrcount++; - } + iio_attr = kzalloc(sizeof(*iio_attr), GFP_KERNEL); + if (!iio_attr) + return NULL; + + iio_attr->buffer = buffer; + memcpy(&iio_attr->dev_attr, dattr, sizeof(iio_attr->dev_attr)); + iio_attr->dev_attr.attr.name = kstrdup_const(attr->name, GFP_KERNEL); + sysfs_attr_init(&iio_attr->dev_attr.attr); + + list_add(&iio_attr->l, &buffer->buffer_attr_list); + + return &iio_attr->dev_attr.attr; +} + +static int iio_buffer_register_legacy_sysfs_groups(struct iio_dev *indio_dev, + struct attribute **buffer_attrs, + int buffer_attrcount, + int scan_el_attrcount) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct attribute_group *group; + struct attribute **attrs; + int ret; - attr = kcalloc(attrcount + ARRAY_SIZE(iio_buffer_attrs) + 1, - sizeof(struct attribute *), GFP_KERNEL); - if (!attr) + attrs = kcalloc(buffer_attrcount + 1, sizeof(*attrs), GFP_KERNEL); + if (!attrs) return -ENOMEM; - memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs)); - if (!buffer->access->set_length) - attr[0] = &dev_attr_length_ro.attr; + memcpy(attrs, buffer_attrs, buffer_attrcount * sizeof(*attrs)); - if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK) - attr[2] = &dev_attr_watermark_ro.attr; + group = &iio_dev_opaque->legacy_buffer_group; + group->attrs = attrs; + group->name = "buffer"; - if (buffer->attrs) - memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs, - sizeof(struct attribute *) * attrcount); + ret = iio_device_register_sysfs_group(indio_dev, group); + if (ret) + goto error_free_buffer_attrs; - attr[attrcount + ARRAY_SIZE(iio_buffer_attrs)] = NULL; + attrs = kcalloc(scan_el_attrcount + 1, sizeof(*attrs), GFP_KERNEL); + if (!attrs) { + ret = -ENOMEM; + goto error_free_buffer_attrs; + } - buffer->buffer_group.name = "buffer"; - buffer->buffer_group.attrs = attr; + memcpy(attrs, &buffer_attrs[buffer_attrcount], + scan_el_attrcount * sizeof(*attrs)); + + group = &iio_dev_opaque->legacy_scan_el_group; + group->attrs = attrs; + group->name = "scan_elements"; + + ret = iio_device_register_sysfs_group(indio_dev, group); + if (ret) + goto error_free_scan_el_attrs; + + return 0; + +error_free_buffer_attrs: + kfree(iio_dev_opaque->legacy_buffer_group.attrs); +error_free_scan_el_attrs: + kfree(iio_dev_opaque->legacy_scan_el_group.attrs); + + return ret; +} + +static void iio_buffer_unregister_legacy_sysfs_groups(struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - indio_dev->groups[indio_dev->groupcounter++] = &buffer->buffer_group; + kfree(iio_dev_opaque->legacy_buffer_group.attrs); + kfree(iio_dev_opaque->legacy_scan_el_group.attrs); +} + +static int iio_buffer_chrdev_release(struct inode *inode, struct file *filep) +{ + struct iio_dev_buffer_pair *ib = filep->private_data; + struct iio_dev *indio_dev = ib->indio_dev; + struct iio_buffer *buffer = ib->buffer; + + wake_up(&buffer->pollq); + + kfree(ib); + clear_bit(IIO_BUSY_BIT_POS, &buffer->flags); + iio_device_put(indio_dev); + + return 0; +} + +static const struct file_operations iio_buffer_chrdev_fileops = { + .owner = THIS_MODULE, + .llseek = noop_llseek, + .read = iio_buffer_read, + .poll = iio_buffer_poll, + .release = iio_buffer_chrdev_release, +}; + +static long iio_device_buffer_getfd(struct iio_dev *indio_dev, unsigned long arg) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + int __user *ival = (int __user *)arg; + struct iio_dev_buffer_pair *ib; + struct iio_buffer *buffer; + int fd, idx, ret; + + if (copy_from_user(&idx, ival, sizeof(idx))) + return -EFAULT; + + if (idx >= iio_dev_opaque->attached_buffers_cnt) + return -ENODEV; - attrcount = 0; - INIT_LIST_HEAD(&buffer->scan_el_dev_attr_list); + iio_device_get(indio_dev); + + buffer = iio_dev_opaque->attached_buffers[idx]; + + if (test_and_set_bit(IIO_BUSY_BIT_POS, &buffer->flags)) { + ret = -EBUSY; + goto error_iio_dev_put; + } + + ib = kzalloc(sizeof(*ib), GFP_KERNEL); + if (!ib) { + ret = -ENOMEM; + goto error_clear_busy_bit; + } + + ib->indio_dev = indio_dev; + ib->buffer = buffer; + + fd = anon_inode_getfd("iio:buffer", &iio_buffer_chrdev_fileops, + ib, O_RDWR | O_CLOEXEC); + if (fd < 0) { + ret = fd; + goto error_free_ib; + } + + if (copy_to_user(ival, &fd, sizeof(fd))) { + put_unused_fd(fd); + ret = -EFAULT; + goto error_free_ib; + } + + return 0; + +error_free_ib: + kfree(ib); +error_clear_busy_bit: + clear_bit(IIO_BUSY_BIT_POS, &buffer->flags); +error_iio_dev_put: + iio_device_put(indio_dev); + return ret; +} + +static long iio_device_buffer_ioctl(struct iio_dev *indio_dev, struct file *filp, + unsigned int cmd, unsigned long arg) +{ + switch (cmd) { + case IIO_BUFFER_GET_FD_IOCTL: + return iio_device_buffer_getfd(indio_dev, arg); + default: + return IIO_IOCTL_UNHANDLED; + } +} + +static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, + struct iio_dev *indio_dev, + int index) +{ + struct iio_dev_attr *p; + struct attribute **attr; + int ret, i, attrn, scan_el_attrcount, buffer_attrcount; + const struct iio_chan_spec *channels; + + buffer_attrcount = 0; + if (buffer->attrs) { + while (buffer->attrs[buffer_attrcount] != NULL) + buffer_attrcount++; + } + + scan_el_attrcount = 0; + INIT_LIST_HEAD(&buffer->buffer_attr_list); channels = indio_dev->channels; if (channels) { /* new magic */ @@ -1302,7 +1493,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, &channels[i]); if (ret < 0) goto error_cleanup_dynamic; - attrcount += ret; + scan_el_attrcount += ret; if (channels[i].type == IIO_TIMESTAMP) indio_dev->scan_index_timestamp = channels[i].scan_index; @@ -1317,37 +1508,93 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, } } - buffer->scan_el_group.name = iio_scan_elements_group_name; - - buffer->scan_el_group.attrs = kcalloc(attrcount + 1, - sizeof(buffer->scan_el_group.attrs[0]), - GFP_KERNEL); - if (buffer->scan_el_group.attrs == NULL) { + attrn = buffer_attrcount + scan_el_attrcount + ARRAY_SIZE(iio_buffer_attrs); + attr = kcalloc(attrn + 1, sizeof(* attr), GFP_KERNEL); + if (!attr) { ret = -ENOMEM; goto error_free_scan_mask; } + + memcpy(attr, iio_buffer_attrs, sizeof(iio_buffer_attrs)); + if (!buffer->access->set_length) + attr[0] = &dev_attr_length_ro.attr; + + if (buffer->access->flags & INDIO_BUFFER_FLAG_FIXED_WATERMARK) + attr[2] = &dev_attr_watermark_ro.attr; + + if (buffer->attrs) + memcpy(&attr[ARRAY_SIZE(iio_buffer_attrs)], buffer->attrs, + sizeof(struct attribute *) * buffer_attrcount); + + buffer_attrcount += ARRAY_SIZE(iio_buffer_attrs); + + for (i = 0; i < buffer_attrcount; i++) { + struct attribute *wrapped; + + wrapped = iio_buffer_wrap_attr(buffer, attr[i]); + if (!wrapped) { + ret = -ENOMEM; + goto error_free_scan_mask; + } + attr[i] = wrapped; + } + attrn = 0; + list_for_each_entry(p, &buffer->buffer_attr_list, l) + attr[attrn++] = &p->dev_attr.attr; - list_for_each_entry(p, &buffer->scan_el_dev_attr_list, l) - buffer->scan_el_group.attrs[attrn++] = &p->dev_attr.attr; - indio_dev->groups[indio_dev->groupcounter++] = &buffer->scan_el_group; + buffer->buffer_group.name = kasprintf(GFP_KERNEL, "buffer%d", index); + if (!buffer->buffer_group.name) { + ret = -ENOMEM; + goto error_free_buffer_attrs; + } + + buffer->buffer_group.attrs = attr; + + ret = iio_device_register_sysfs_group(indio_dev, &buffer->buffer_group); + if (ret) + goto error_free_buffer_attr_group_name; + + /* we only need to register the legacy groups for the first buffer */ + if (index > 0) + return 0; + + ret = iio_buffer_register_legacy_sysfs_groups(indio_dev, attr, + buffer_attrcount, + scan_el_attrcount); + if (ret) + goto error_free_buffer_attr_group_name; return 0; +error_free_buffer_attr_group_name: + kfree(buffer->buffer_group.name); +error_free_buffer_attrs: + kfree(buffer->buffer_group.attrs); error_free_scan_mask: bitmap_free(buffer->scan_mask); error_cleanup_dynamic: - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list); - kfree(buffer->buffer_group.attrs); + iio_free_chan_devattr_list(&buffer->buffer_attr_list); return ret; } -int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) +static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer) +{ + bitmap_free(buffer->scan_mask); + kfree(buffer->buffer_group.name); + kfree(buffer->buffer_group.attrs); + iio_free_chan_devattr_list(&buffer->buffer_attr_list); +} + +int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) { - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); const struct iio_chan_spec *channels; - int i; + struct iio_buffer *buffer; + int unwind_idx; + int ret, i; + size_t sz; channels = indio_dev->channels; if (channels) { @@ -1358,28 +1605,58 @@ int iio_buffer_alloc_sysfs_and_mask(struct iio_dev *indio_dev) indio_dev->masklength = ml; } - if (!buffer) + if (!iio_dev_opaque->attached_buffers_cnt) return 0; - return __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev); -} + for (i = 0; i < iio_dev_opaque->attached_buffers_cnt; i++) { + buffer = iio_dev_opaque->attached_buffers[i]; + ret = __iio_buffer_alloc_sysfs_and_mask(buffer, indio_dev, i); + if (ret) { + unwind_idx = i; + goto error_unwind_sysfs_and_mask; + } + } + unwind_idx = iio_dev_opaque->attached_buffers_cnt - 1; -static void __iio_buffer_free_sysfs_and_mask(struct iio_buffer *buffer) -{ - bitmap_free(buffer->scan_mask); - kfree(buffer->buffer_group.attrs); - kfree(buffer->scan_el_group.attrs); - iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list); + sz = sizeof(*(iio_dev_opaque->buffer_ioctl_handler)); + iio_dev_opaque->buffer_ioctl_handler = kzalloc(sz, GFP_KERNEL); + if (!iio_dev_opaque->buffer_ioctl_handler) { + ret = -ENOMEM; + goto error_unwind_sysfs_and_mask; + } + + iio_dev_opaque->buffer_ioctl_handler->ioctl = iio_device_buffer_ioctl; + iio_device_ioctl_handler_register(indio_dev, + iio_dev_opaque->buffer_ioctl_handler); + + return 0; + +error_unwind_sysfs_and_mask: + for (; unwind_idx >= 0; unwind_idx--) { + buffer = iio_dev_opaque->attached_buffers[unwind_idx]; + __iio_buffer_free_sysfs_and_mask(buffer); + } + return ret; } -void iio_buffer_free_sysfs_and_mask(struct iio_dev *indio_dev) +void iio_buffers_free_sysfs_and_mask(struct iio_dev *indio_dev) { - struct iio_buffer *buffer = indio_dev->buffer; + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_buffer *buffer; + int i; - if (!buffer) + if (!iio_dev_opaque->attached_buffers_cnt) return; - __iio_buffer_free_sysfs_and_mask(buffer); + iio_device_ioctl_handler_unregister(iio_dev_opaque->buffer_ioctl_handler); + kfree(iio_dev_opaque->buffer_ioctl_handler); + + iio_buffer_unregister_legacy_sysfs_groups(indio_dev); + + for (i = iio_dev_opaque->attached_buffers_cnt - 1; i >= 0; i--) { + buffer = iio_dev_opaque->attached_buffers[i]; + __iio_buffer_free_sysfs_and_mask(buffer); + } } /** @@ -1497,13 +1774,37 @@ EXPORT_SYMBOL_GPL(iio_buffer_put); * @indio_dev: The device the buffer should be attached to * @buffer: The buffer to attach to the device * + * Return 0 if successful, negative if error. + * * This function attaches a buffer to a IIO device. The buffer stays attached to - * the device until the device is freed. The function should only be called at - * most once per device. + * the device until the device is freed. For legacy reasons, the first attached + * buffer will also be assigned to 'indio_dev->buffer'. + * The array allocated here, will be free'd via the iio_device_detach_buffers() + * call which is handled by the iio_device_free(). */ -void iio_device_attach_buffer(struct iio_dev *indio_dev, - struct iio_buffer *buffer) +int iio_device_attach_buffer(struct iio_dev *indio_dev, + struct iio_buffer *buffer) { - indio_dev->buffer = iio_buffer_get(buffer); + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + struct iio_buffer **new, **old = iio_dev_opaque->attached_buffers; + unsigned int cnt = iio_dev_opaque->attached_buffers_cnt; + + cnt++; + + new = krealloc(old, sizeof(*new) * cnt, GFP_KERNEL); + if (!new) + return -ENOMEM; + iio_dev_opaque->attached_buffers = new; + + buffer = iio_buffer_get(buffer); + + /* first buffer is legacy; attach it to the IIO device directly */ + if (!indio_dev->buffer) + indio_dev->buffer = buffer; + + iio_dev_opaque->attached_buffers[cnt - 1] = buffer; + iio_dev_opaque->attached_buffers_cnt = cnt; + + return 0; } EXPORT_SYMBOL_GPL(iio_device_attach_buffer); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 7db761afa578..d92c58a94fe4 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -157,6 +157,7 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_PHASE] = "phase", [IIO_CHAN_INFO_HARDWAREGAIN] = "hardwaregain", [IIO_CHAN_INFO_HYSTERESIS] = "hysteresis", + [IIO_CHAN_INFO_HYSTERESIS_RELATIVE] = "hysteresis_relative", [IIO_CHAN_INFO_INT_TIME] = "integration_time", [IIO_CHAN_INFO_ENABLE] = "en", [IIO_CHAN_INFO_CALIBHEIGHT] = "calibheight", @@ -233,7 +234,7 @@ ssize_t iio_read_const_attr(struct device *dev, struct device_attribute *attr, char *buf) { - return sprintf(buf, "%s\n", to_iio_const_attr(attr)->string); + return sysfs_emit(buf, "%s\n", to_iio_const_attr(attr)->string); } EXPORT_SYMBOL(iio_read_const_attr); @@ -503,7 +504,7 @@ ssize_t iio_enum_available_read(struct iio_dev *indio_dev, for (i = 0; i < e->num_items; ++i) { if (!e->items[i]) continue; - len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", e->items[i]); + len += sysfs_emit_at(buf, len, "%s ", e->items[i]); } /* replace last space with a newline */ @@ -528,7 +529,7 @@ ssize_t iio_enum_read(struct iio_dev *indio_dev, else if (i >= e->num_items || !e->items[i]) return -EINVAL; - return snprintf(buf, PAGE_SIZE, "%s\n", e->items[i]); + return sysfs_emit(buf, "%s\n", e->items[i]); } EXPORT_SYMBOL_GPL(iio_enum_read); @@ -579,10 +580,10 @@ ssize_t iio_show_mount_matrix(struct iio_dev *indio_dev, uintptr_t priv, if (!mtx) mtx = &iio_mount_idmatrix; - return snprintf(buf, PAGE_SIZE, "%s, %s, %s; %s, %s, %s; %s, %s, %s\n", - mtx->rotation[0], mtx->rotation[1], mtx->rotation[2], - mtx->rotation[3], mtx->rotation[4], mtx->rotation[5], - mtx->rotation[6], mtx->rotation[7], mtx->rotation[8]); + return sysfs_emit(buf, "%s, %s, %s; %s, %s, %s; %s, %s, %s\n", + mtx->rotation[0], mtx->rotation[1], mtx->rotation[2], + mtx->rotation[3], mtx->rotation[4], mtx->rotation[5], + mtx->rotation[6], mtx->rotation[7], mtx->rotation[8]); } EXPORT_SYMBOL_GPL(iio_show_mount_matrix); @@ -622,59 +623,62 @@ int iio_read_mount_matrix(struct device *dev, const char *propname, } EXPORT_SYMBOL(iio_read_mount_matrix); -static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type, +static ssize_t __iio_format_value(char *buf, size_t offset, unsigned int type, int size, const int *vals) { - unsigned long long tmp; int tmp0, tmp1; s64 tmp2; bool scale_db = false; switch (type) { case IIO_VAL_INT: - return scnprintf(buf, len, "%d", vals[0]); + return sysfs_emit_at(buf, offset, "%d", vals[0]); case IIO_VAL_INT_PLUS_MICRO_DB: scale_db = true; fallthrough; case IIO_VAL_INT_PLUS_MICRO: if (vals[1] < 0) - return scnprintf(buf, len, "-%d.%06u%s", abs(vals[0]), - -vals[1], scale_db ? " dB" : ""); + return sysfs_emit_at(buf, offset, "-%d.%06u%s", + abs(vals[0]), -vals[1], + scale_db ? " dB" : ""); else - return scnprintf(buf, len, "%d.%06u%s", vals[0], vals[1], - scale_db ? " dB" : ""); + return sysfs_emit_at(buf, offset, "%d.%06u%s", vals[0], + vals[1], scale_db ? " dB" : ""); case IIO_VAL_INT_PLUS_NANO: if (vals[1] < 0) - return scnprintf(buf, len, "-%d.%09u", abs(vals[0]), - -vals[1]); + return sysfs_emit_at(buf, offset, "-%d.%09u", + abs(vals[0]), -vals[1]); else - return scnprintf(buf, len, "%d.%09u", vals[0], vals[1]); + return sysfs_emit_at(buf, offset, "%d.%09u", vals[0], + vals[1]); case IIO_VAL_FRACTIONAL: tmp2 = div_s64((s64)vals[0] * 1000000000LL, vals[1]); tmp1 = vals[1]; tmp0 = (int)div_s64_rem(tmp2, 1000000000, &tmp1); if ((tmp2 < 0) && (tmp0 == 0)) - return snprintf(buf, len, "-0.%09u", abs(tmp1)); + return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1)); else - return snprintf(buf, len, "%d.%09u", tmp0, abs(tmp1)); + return sysfs_emit_at(buf, offset, "%d.%09u", tmp0, + abs(tmp1)); case IIO_VAL_FRACTIONAL_LOG2: - tmp = shift_right((s64)vals[0] * 1000000000LL, vals[1]); - tmp0 = (int)div_s64_rem(tmp, 1000000000LL, &tmp1); - return scnprintf(buf, len, "%d.%09u", tmp0, abs(tmp1)); + tmp2 = shift_right((s64)vals[0] * 1000000000LL, vals[1]); + tmp0 = (int)div_s64_rem(tmp2, 1000000000LL, &tmp1); + if (tmp0 == 0 && tmp2 < 0) + return sysfs_emit_at(buf, offset, "-0.%09u", abs(tmp1)); + else + return sysfs_emit_at(buf, offset, "%d.%09u", tmp0, + abs(tmp1)); case IIO_VAL_INT_MULTIPLE: { int i; int l = 0; - for (i = 0; i < size; ++i) { - l += scnprintf(&buf[l], len - l, "%d ", vals[i]); - if (l >= len) - break; - } + for (i = 0; i < size; ++i) + l += sysfs_emit_at(buf, offset + l, "%d ", vals[i]); return l; } case IIO_VAL_CHAR: - return scnprintf(buf, len, "%c", (char)vals[0]); + return sysfs_emit_at(buf, offset, "%c", (char)vals[0]); default: return 0; } @@ -698,11 +702,11 @@ ssize_t iio_format_value(char *buf, unsigned int type, int size, int *vals) { ssize_t len; - len = __iio_format_value(buf, PAGE_SIZE, type, size, vals); + len = __iio_format_value(buf, 0, type, size, vals); if (len >= PAGE_SIZE - 1) return -EFBIG; - return len + sprintf(buf + len, "\n"); + return len + sysfs_emit_at(buf, len, "\n"); } EXPORT_SYMBOL_GPL(iio_format_value); @@ -760,22 +764,21 @@ static ssize_t iio_format_list(char *buf, const int *vals, int type, int length, break; } - len = scnprintf(buf, PAGE_SIZE, prefix); + len = sysfs_emit(buf, prefix); for (i = 0; i <= length - stride; i += stride) { if (i != 0) { - len += scnprintf(buf + len, PAGE_SIZE - len, " "); + len += sysfs_emit_at(buf, len, " "); if (len >= PAGE_SIZE) return -EFBIG; } - len += __iio_format_value(buf + len, PAGE_SIZE - len, type, - stride, &vals[i]); + len += __iio_format_value(buf, len, type, stride, &vals[i]); if (len >= PAGE_SIZE) return -EFBIG; } - len += scnprintf(buf + len, PAGE_SIZE - len, "%s\n", suffix); + len += sysfs_emit_at(buf, len, "%s\n", suffix); return len; } @@ -1114,6 +1117,7 @@ int __iio_add_chan_devattr(const char *postfix, u64 mask, enum iio_shared_by shared_by, struct device *dev, + struct iio_buffer *buffer, struct list_head *attr_list) { int ret; @@ -1129,6 +1133,7 @@ int __iio_add_chan_devattr(const char *postfix, goto error_iio_dev_attr_free; iio_attr->c = chan; iio_attr->address = mask; + iio_attr->buffer = buffer; list_for_each_entry(t, attr_list, l) if (strcmp(t->dev_attr.attr.name, iio_attr->dev_attr.attr.name) == 0) { @@ -1165,6 +1170,7 @@ static int iio_device_add_channel_label(struct iio_dev *indio_dev, 0, IIO_SEPARATE, &indio_dev->dev, + NULL, &iio_dev_opaque->channel_attr_list); if (ret < 0) return ret; @@ -1190,6 +1196,7 @@ static int iio_device_add_info_mask_type(struct iio_dev *indio_dev, i, shared_by, &indio_dev->dev, + NULL, &iio_dev_opaque->channel_attr_list); if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) continue; @@ -1226,6 +1233,7 @@ static int iio_device_add_info_mask_type_avail(struct iio_dev *indio_dev, i, shared_by, &indio_dev->dev, + NULL, &iio_dev_opaque->channel_attr_list); kfree(avail_postfix); if ((ret == -EBUSY) && (shared_by != IIO_SEPARATE)) @@ -1322,6 +1330,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev, i, ext_info->shared, &indio_dev->dev, + NULL, &iio_dev_opaque->channel_attr_list); i++; if (ret == -EBUSY && ext_info->shared) @@ -1349,7 +1358,7 @@ void iio_free_chan_devattr_list(struct list_head *attr_list) struct iio_dev_attr *p, *n; list_for_each_entry_safe(p, n, attr_list, l) { - kfree(p->dev_attr.attr.name); + kfree_const(p->dev_attr.attr.name); list_del(&p->l); kfree(p); } @@ -1360,7 +1369,7 @@ static ssize_t iio_show_dev_name(struct device *dev, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", indio_dev->name); + return sysfs_emit(buf, "%s\n", indio_dev->name); } static DEVICE_ATTR(name, S_IRUGO, iio_show_dev_name, NULL); @@ -1370,7 +1379,7 @@ static ssize_t iio_show_dev_label(struct device *dev, char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); - return snprintf(buf, PAGE_SIZE, "%s\n", indio_dev->label); + return sysfs_emit(buf, "%s\n", indio_dev->label); } static DEVICE_ATTR(label, S_IRUGO, iio_show_dev_label, NULL); @@ -1452,6 +1461,25 @@ static ssize_t iio_store_timestamp_clock(struct device *dev, return len; } +int iio_device_register_sysfs_group(struct iio_dev *indio_dev, + const struct attribute_group *group) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + const struct attribute_group **new, **old = iio_dev_opaque->groups; + unsigned int cnt = iio_dev_opaque->groupcounter; + + new = krealloc(old, sizeof(*new) * (cnt + 2), GFP_KERNEL); + if (!new) + return -ENOMEM; + + new[iio_dev_opaque->groupcounter++] = group; + new[iio_dev_opaque->groupcounter] = NULL; + + iio_dev_opaque->groups = new; + + return 0; +} + static DEVICE_ATTR(current_timestamp_clock, S_IRUGO | S_IWUSR, iio_show_timestamp_clock, iio_store_timestamp_clock); @@ -1525,8 +1553,10 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev) if (clk) iio_dev_opaque->chan_attr_group.attrs[attrn++] = clk; - indio_dev->groups[indio_dev->groupcounter++] = - &iio_dev_opaque->chan_attr_group; + ret = iio_device_register_sysfs_group(indio_dev, + &iio_dev_opaque->chan_attr_group); + if (ret) + goto error_clear_attrs; return 0; @@ -1543,6 +1573,7 @@ static void iio_device_unregister_sysfs(struct iio_dev *indio_dev) iio_free_chan_devattr_list(&iio_dev_opaque->channel_attr_list); kfree(iio_dev_opaque->chan_attr_group.attrs); iio_dev_opaque->chan_attr_group.attrs = NULL; + kfree(iio_dev_opaque->groups); } static void iio_dev_release(struct device *device) @@ -1555,7 +1586,7 @@ static void iio_dev_release(struct device *device) iio_device_unregister_eventset(indio_dev); iio_device_unregister_sysfs(indio_dev); - iio_buffer_put(indio_dev->buffer); + iio_device_detach_buffers(indio_dev); ida_simple_remove(&iio_ida, indio_dev->id); kfree(iio_dev_opaque); @@ -1574,7 +1605,7 @@ struct device_type iio_device_type = { struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) { struct iio_dev_opaque *iio_dev_opaque; - struct iio_dev *dev; + struct iio_dev *indio_dev; size_t alloc_size; alloc_size = sizeof(struct iio_dev_opaque); @@ -1587,32 +1618,31 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) if (!iio_dev_opaque) return NULL; - dev = &iio_dev_opaque->indio_dev; - dev->priv = (char *)iio_dev_opaque + + indio_dev = &iio_dev_opaque->indio_dev; + indio_dev->priv = (char *)iio_dev_opaque + ALIGN(sizeof(struct iio_dev_opaque), IIO_ALIGN); - dev->dev.parent = parent; - dev->dev.groups = dev->groups; - dev->dev.type = &iio_device_type; - dev->dev.bus = &iio_bus_type; - device_initialize(&dev->dev); - dev_set_drvdata(&dev->dev, (void *)dev); - mutex_init(&dev->mlock); - mutex_init(&dev->info_exist_lock); + indio_dev->dev.parent = parent; + indio_dev->dev.type = &iio_device_type; + indio_dev->dev.bus = &iio_bus_type; + device_initialize(&indio_dev->dev); + iio_device_set_drvdata(indio_dev, (void *)indio_dev); + mutex_init(&indio_dev->mlock); + mutex_init(&indio_dev->info_exist_lock); INIT_LIST_HEAD(&iio_dev_opaque->channel_attr_list); - dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); - if (dev->id < 0) { + indio_dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); + if (indio_dev->id < 0) { /* cannot use a dev_err as the name isn't available */ pr_err("failed to get device id\n"); kfree(iio_dev_opaque); return NULL; } - dev_set_name(&dev->dev, "iio:device%d", dev->id); + dev_set_name(&indio_dev->dev, "iio:device%d", indio_dev->id); INIT_LIST_HEAD(&iio_dev_opaque->buffer_list); INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers); - return dev; + return indio_dev; } EXPORT_SYMBOL(iio_device_alloc); @@ -1676,13 +1706,24 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp) { struct iio_dev *indio_dev = container_of(inode->i_cdev, struct iio_dev, chrdev); + struct iio_dev_buffer_pair *ib; if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags)) return -EBUSY; iio_device_get(indio_dev); - filp->private_data = indio_dev; + ib = kmalloc(sizeof(*ib), GFP_KERNEL); + if (!ib) { + iio_device_put(indio_dev); + clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags); + return -ENOMEM; + } + + ib->indio_dev = indio_dev; + ib->buffer = indio_dev->buffer; + + filp->private_data = ib; return 0; } @@ -1696,8 +1737,10 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp) */ static int iio_chrdev_release(struct inode *inode, struct file *filp) { + struct iio_dev_buffer_pair *ib = filp->private_data; struct iio_dev *indio_dev = container_of(inode->i_cdev, struct iio_dev, chrdev); + kfree(ib); clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags); iio_device_put(indio_dev); @@ -1719,7 +1762,8 @@ void iio_device_ioctl_handler_unregister(struct iio_ioctl_handler *h) static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) { - struct iio_dev *indio_dev = filp->private_data; + struct iio_dev_buffer_pair *ib = filp->private_data; + struct iio_dev *indio_dev = ib->indio_dev; struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_ioctl_handler *h; int ret = -ENODEV; @@ -1761,6 +1805,15 @@ static const struct file_operations iio_buffer_fileops = { .release = iio_chrdev_release, }; +static const struct file_operations iio_event_fileops = { + .owner = THIS_MODULE, + .llseek = noop_llseek, + .unlocked_ioctl = iio_ioctl, + .compat_ioctl = compat_ptr_ioctl, + .open = iio_chrdev_open, + .release = iio_chrdev_release, +}; + static int iio_check_unique_scan_index(struct iio_dev *indio_dev) { int i, j; @@ -1788,6 +1841,8 @@ static const struct iio_buffer_setup_ops noop_ring_setup_ops; int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + const char *label; int ret; if (!indio_dev->info) @@ -1798,19 +1853,17 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) if (!indio_dev->dev.of_node && indio_dev->dev.parent) indio_dev->dev.of_node = indio_dev->dev.parent->of_node; - indio_dev->label = of_get_property(indio_dev->dev.of_node, "label", - NULL); + label = of_get_property(indio_dev->dev.of_node, "label", NULL); + if (label) + indio_dev->label = label; ret = iio_check_unique_scan_index(indio_dev); if (ret < 0) return ret; - /* configure elements for the chrdev */ - indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); - iio_device_register_debugfs(indio_dev); - ret = iio_buffer_alloc_sysfs_and_mask(indio_dev); + ret = iio_buffers_alloc_sysfs_and_mask(indio_dev); if (ret) { dev_err(indio_dev->dev.parent, "Failed to create buffer sysfs interfaces\n"); @@ -1836,9 +1889,18 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) indio_dev->setup_ops == NULL) indio_dev->setup_ops = &noop_ring_setup_ops; - cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); + if (iio_dev_opaque->attached_buffers_cnt) + cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); + else if (iio_dev_opaque->event_interface) + cdev_init(&indio_dev->chrdev, &iio_event_fileops); + + if (iio_dev_opaque->attached_buffers_cnt || iio_dev_opaque->event_interface) { + indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); + indio_dev->chrdev.owner = this_mod; + } - indio_dev->chrdev.owner = this_mod; + /* assign device groups now; they should be all registered now */ + indio_dev->dev.groups = iio_dev_opaque->groups; ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev); if (ret < 0) @@ -1851,7 +1913,7 @@ error_unreg_eventset: error_free_sysfs: iio_device_unregister_sysfs(indio_dev); error_buffer_free_sysfs: - iio_buffer_free_sysfs_and_mask(indio_dev); + iio_buffers_free_sysfs_and_mask(indio_dev); error_unreg_debugfs: iio_device_unregister_debugfs(indio_dev); return ret; @@ -1885,7 +1947,7 @@ void iio_device_unregister(struct iio_dev *indio_dev) mutex_unlock(&indio_dev->info_exist_lock); - iio_buffer_free_sysfs_and_mask(indio_dev); + iio_buffers_free_sysfs_and_mask(indio_dev); } EXPORT_SYMBOL(iio_device_unregister); diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c index 7e532117ac55..d0732eac0f0a 100644 --- a/drivers/iio/industrialio-event.c +++ b/drivers/iio/industrialio-event.c @@ -245,6 +245,7 @@ static const char * const iio_ev_info_text[] = { [IIO_EV_INFO_PERIOD] = "period", [IIO_EV_INFO_HIGH_PASS_FILTER_3DB] = "high_pass_filter_3db", [IIO_EV_INFO_LOW_PASS_FILTER_3DB] = "low_pass_filter_3db", + [IIO_EV_INFO_TIMEOUT] = "timeout", }; static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr) @@ -297,7 +298,7 @@ static ssize_t iio_ev_state_show(struct device *dev, if (val < 0) return val; else - return sprintf(buf, "%d\n", val); + return sysfs_emit(buf, "%d\n", val); } static ssize_t iio_ev_value_show(struct device *dev, @@ -385,6 +386,7 @@ static int iio_device_add_event(struct iio_dev *indio_dev, ret = __iio_add_chan_devattr(postfix, chan, show, store, (i << 16) | spec_index, shared_by, &indio_dev->dev, + NULL, &iio_dev_opaque->event_interface->dev_attr_list); kfree(postfix); @@ -544,7 +546,10 @@ int iio_device_register_eventset(struct iio_dev *indio_dev) /* Add all elements from the list. */ list_for_each_entry(p, &ev_int->dev_attr_list, l) ev_int->group.attrs[attrn++] = &p->dev_attr.attr; - indio_dev->groups[indio_dev->groupcounter++] = &ev_int->group; + + ret = iio_device_register_sysfs_group(indio_dev, &ev_int->group); + if (ret) + goto error_free_setup_event_lines; ev_int->ioctl_handler.ioctl = iio_event_ioctl; iio_device_ioctl_handler_register(&iio_dev_opaque->indio_dev, diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index ea3c9859b258..b2c94abbb487 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -50,7 +50,7 @@ static ssize_t iio_trigger_read_name(struct device *dev, char *buf) { struct iio_trigger *trig = to_iio_trigger(dev); - return sprintf(buf, "%s\n", trig->name); + return sysfs_emit(buf, "%s\n", trig->name); } static DEVICE_ATTR(name, S_IRUGO, iio_trigger_read_name, NULL); @@ -75,8 +75,7 @@ int __iio_trigger_register(struct iio_trigger *trig_info, return trig_info->id; /* Set the name used for the sysfs directory etc */ - dev_set_name(&trig_info->dev, "trigger%ld", - (unsigned long) trig_info->id); + dev_set_name(&trig_info->dev, "trigger%d", trig_info->id); ret = device_add(&trig_info->dev); if (ret) @@ -212,6 +211,7 @@ EXPORT_SYMBOL(iio_trigger_notify_done); static int iio_trigger_get_irq(struct iio_trigger *trig) { int ret; + mutex_lock(&trig->pool_lock); ret = bitmap_find_free_region(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER, @@ -240,9 +240,9 @@ static void iio_trigger_put_irq(struct iio_trigger *trig, int irq) int iio_trigger_attach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf) { + bool notinuse = + bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER); int ret = 0; - bool notinuse - = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER); /* Prevent the module from being removed whilst attached to a trigger */ __module_get(pf->indio_dev->driver_module); @@ -291,11 +291,10 @@ out_put_module: int iio_trigger_detach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf) { + bool no_other_users = + bitmap_weight(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER) == 1; int ret = 0; - bool no_other_users - = (bitmap_weight(trig->pool, - CONFIG_IIO_CONSUMERS_PER_TRIGGER) - == 1); + if (trig->ops && trig->ops->set_trigger_state && no_other_users) { ret = trig->ops->set_trigger_state(trig, false); if (ret) @@ -313,6 +312,7 @@ int iio_trigger_detach_poll_func(struct iio_trigger *trig, irqreturn_t iio_pollfunc_store_time(int irq, void *p) { struct iio_poll_func *pf = p; + pf->timestamp = iio_get_time_ns(pf->indio_dev); return IRQ_WAKE_THREAD; } @@ -375,7 +375,7 @@ static ssize_t iio_trigger_read_current(struct device *dev, struct iio_dev *indio_dev = dev_to_iio_dev(dev); if (indio_dev->trig) - return sprintf(buf, "%s\n", indio_dev->trig->name); + return sysfs_emit(buf, "%s\n", indio_dev->trig->name); return 0; } @@ -499,23 +499,23 @@ static const struct device_type iio_trig_type = { static void iio_trig_subirqmask(struct irq_data *d) { struct irq_chip *chip = irq_data_get_irq_chip(d); - struct iio_trigger *trig - = container_of(chip, - struct iio_trigger, subirq_chip); + struct iio_trigger *trig = container_of(chip, struct iio_trigger, subirq_chip); + trig->subirqs[d->irq - trig->subirq_base].enabled = false; } static void iio_trig_subirqunmask(struct irq_data *d) { struct irq_chip *chip = irq_data_get_irq_chip(d); - struct iio_trigger *trig - = container_of(chip, - struct iio_trigger, subirq_chip); + struct iio_trigger *trig = container_of(chip, struct iio_trigger, subirq_chip); + trig->subirqs[d->irq - trig->subirq_base].enabled = true; } -static __printf(1, 0) -struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs) +static __printf(2, 0) +struct iio_trigger *viio_trigger_alloc(struct device *parent, + const char *fmt, + va_list vargs) { struct iio_trigger *trig; int i; @@ -524,6 +524,7 @@ struct iio_trigger *viio_trigger_alloc(const char *fmt, va_list vargs) if (!trig) return NULL; + trig->dev.parent = parent; trig->dev.type = &iio_trig_type; trig->dev.bus = &iio_bus_type; device_initialize(&trig->dev); @@ -559,13 +560,23 @@ free_trig: return NULL; } -struct iio_trigger *iio_trigger_alloc(const char *fmt, ...) +/** + * iio_trigger_alloc - Allocate a trigger + * @parent: Device to allocate iio_trigger for + * @fmt: trigger name format. If it includes format + * specifiers, the additional arguments following + * format are formatted and inserted in the resulting + * string replacing their respective specifiers. + * RETURNS: + * Pointer to allocated iio_trigger on success, NULL on failure. + */ +struct iio_trigger *iio_trigger_alloc(struct device *parent, const char *fmt, ...) { struct iio_trigger *trig; va_list vargs; va_start(vargs, fmt); - trig = viio_trigger_alloc(fmt, vargs); + trig = viio_trigger_alloc(parent, fmt, vargs); va_end(vargs); return trig; @@ -586,20 +597,19 @@ static void devm_iio_trigger_release(struct device *dev, void *res) /** * devm_iio_trigger_alloc - Resource-managed iio_trigger_alloc() - * @dev: Device to allocate iio_trigger for + * Managed iio_trigger_alloc. iio_trigger allocated with this function is + * automatically freed on driver detach. + * @parent: Device to allocate iio_trigger for * @fmt: trigger name format. If it includes format * specifiers, the additional arguments following * format are formatted and inserted in the resulting * string replacing their respective specifiers. * - * Managed iio_trigger_alloc. iio_trigger allocated with this function is - * automatically freed on driver detach. * * RETURNS: * Pointer to allocated iio_trigger on success, NULL on failure. */ -struct iio_trigger *devm_iio_trigger_alloc(struct device *dev, - const char *fmt, ...) +struct iio_trigger *devm_iio_trigger_alloc(struct device *parent, const char *fmt, ...) { struct iio_trigger **ptr, *trig; va_list vargs; @@ -611,11 +621,11 @@ struct iio_trigger *devm_iio_trigger_alloc(struct device *dev, /* use raw alloc_dr for kmalloc caller tracing */ va_start(vargs, fmt); - trig = viio_trigger_alloc(fmt, vargs); + trig = viio_trigger_alloc(parent, fmt, vargs); va_end(vargs); if (trig) { *ptr = trig; - devres_add(dev, ptr); + devres_add(parent, ptr); } else { devres_free(ptr); } @@ -684,7 +694,7 @@ EXPORT_SYMBOL(iio_trigger_using_own); * device, -EINVAL otherwise. */ int iio_trigger_validate_own_device(struct iio_trigger *trig, - struct iio_dev *indio_dev) + struct iio_dev *indio_dev) { if (indio_dev->dev.parent != trig->dev.parent) return -EINVAL; @@ -692,10 +702,10 @@ int iio_trigger_validate_own_device(struct iio_trigger *trig, } EXPORT_SYMBOL(iio_trigger_validate_own_device); -void iio_device_register_trigger_consumer(struct iio_dev *indio_dev) +int iio_device_register_trigger_consumer(struct iio_dev *indio_dev) { - indio_dev->groups[indio_dev->groupcounter++] = - &iio_trigger_consumer_attr_group; + return iio_device_register_sysfs_group(indio_dev, + &iio_trigger_consumer_attr_group); } void iio_device_unregister_trigger_consumer(struct iio_dev *indio_dev) diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index db77a2d4a56b..9c22697b7e83 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -688,7 +688,8 @@ int iio_read_channel_offset(struct iio_channel *chan, int *val, int *val2) } EXPORT_SYMBOL_GPL(iio_read_channel_offset); -int iio_read_channel_processed(struct iio_channel *chan, int *val) +int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, + unsigned int scale) { int ret; @@ -701,11 +702,15 @@ int iio_read_channel_processed(struct iio_channel *chan, int *val) if (iio_channel_has_info(chan->channel, IIO_CHAN_INFO_PROCESSED)) { ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_PROCESSED); + if (ret < 0) + goto err_unlock; + *val *= scale; } else { ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); if (ret < 0) goto err_unlock; - ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, 1); + ret = iio_convert_raw_to_processed_unlocked(chan, *val, val, + scale); } err_unlock: @@ -713,6 +718,13 @@ err_unlock: return ret; } +EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale); + +int iio_read_channel_processed(struct iio_channel *chan, int *val) +{ + /* This is just a special case with scale factor 1 */ + return iio_read_channel_processed_scale(chan, val, 1); +} EXPORT_SYMBOL_GPL(iio_read_channel_processed); int iio_read_channel_scale(struct iio_channel *chan, int *val, int *val2) diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 2be7180e2cbf..0a6ab5761eec 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -16,18 +16,19 @@ #include <linux/module.h> #include <linux/acpi.h> #include <linux/err.h> +#include <linux/irq.h> #include <linux/mutex.h> #include <linux/iio/iio.h> #include <linux/iio/buffer.h> -#include <linux/iio/kfifo_buf.h> +#include <linux/iio/trigger.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger_consumer.h> #define ACPI_ALS_CLASS "als" #define ACPI_ALS_DEVICE_NAME "acpi-als" #define ACPI_ALS_NOTIFY_ILLUMINANCE 0x80 -ACPI_MODULE_NAME("acpi-als"); - /* * So far, there's only one channel in here, but the specification for * ACPI0008 says there can be more to what the block can report. Like @@ -45,24 +46,23 @@ static const struct iio_chan_spec acpi_als_channels[] = { .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_PROCESSED), }, + IIO_CHAN_SOFT_TIMESTAMP(1), }; /* * The event buffer contains timestamp and all the data from * the ACPI0008 block. There are multiple, but so far we only - * support _ALI (illuminance). Once someone adds new channels - * to acpi_als_channels[], the evt_buffer below will grow - * automatically. + * support _ALI (illuminance): One channel, padding and timestamp. */ -#define ACPI_ALS_EVT_NR_SOURCES ARRAY_SIZE(acpi_als_channels) #define ACPI_ALS_EVT_BUFFER_SIZE \ - (sizeof(s64) + (ACPI_ALS_EVT_NR_SOURCES * sizeof(s32))) + (sizeof(s32) + sizeof(s32) + sizeof(s64)) struct acpi_als { struct acpi_device *device; struct mutex lock; + struct iio_trigger *trig; - s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE]; + s32 evt_buffer[ACPI_ALS_EVT_BUFFER_SIZE / sizeof(s32)] __aligned(8); }; /* @@ -91,7 +91,7 @@ static int acpi_als_read_value(struct acpi_als *als, char *prop, s32 *val) &temp_val); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Error reading ALS %s", prop)); + acpi_evaluation_failure_warn(als->device->handle, prop, status); return -EIO; } @@ -104,33 +104,19 @@ static void acpi_als_notify(struct acpi_device *device, u32 event) { struct iio_dev *indio_dev = acpi_driver_data(device); struct acpi_als *als = iio_priv(indio_dev); - s32 *buffer = als->evt_buffer; - s64 time_ns = iio_get_time_ns(indio_dev); - s32 val; - int ret; - mutex_lock(&als->lock); - - memset(buffer, 0, ACPI_ALS_EVT_BUFFER_SIZE); - - switch (event) { - case ACPI_ALS_NOTIFY_ILLUMINANCE: - ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val); - if (ret < 0) - goto out; - *buffer++ = val; - break; - default: - /* Unhandled event */ - dev_dbg(&device->dev, "Unhandled ACPI ALS event (%08x)!\n", - event); - goto out; + if (iio_buffer_enabled(indio_dev) && iio_trigger_using_own(indio_dev)) { + switch (event) { + case ACPI_ALS_NOTIFY_ILLUMINANCE: + iio_trigger_poll_chained(als->trig); + break; + default: + /* Unhandled event */ + dev_dbg(&device->dev, + "Unhandled ACPI ALS event (%08x)!\n", + event); + } } - - iio_push_to_buffers_with_timestamp(indio_dev, als->evt_buffer, time_ns); - -out: - mutex_unlock(&als->lock); } static int acpi_als_read_raw(struct iio_dev *indio_dev, @@ -161,13 +147,49 @@ static const struct iio_info acpi_als_info = { .read_raw = acpi_als_read_raw, }; +static irqreturn_t acpi_als_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct acpi_als *als = iio_priv(indio_dev); + s32 *buffer = als->evt_buffer; + s32 val; + int ret; + + mutex_lock(&als->lock); + + ret = acpi_als_read_value(als, ACPI_ALS_ILLUMINANCE, &val); + if (ret < 0) + goto out; + *buffer = val; + + /* + * When coming from own trigger via polls, set polling function + * timestamp here. Given ACPI notifier is already in a thread and call + * function directly, there is no need to set the timestamp in the + * notify function. + * + * If the timestamp was actually 0, the timestamp is set one more time. + */ + if (!pf->timestamp) + pf->timestamp = iio_get_time_ns(indio_dev); + + iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp); +out: + mutex_unlock(&als->lock); + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static int acpi_als_add(struct acpi_device *device) { - struct acpi_als *als; + struct device *dev = &device->dev; struct iio_dev *indio_dev; - struct iio_buffer *buffer; + struct acpi_als *als; + int ret; - indio_dev = devm_iio_device_alloc(&device->dev, sizeof(*als)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*als)); if (!indio_dev) return -ENOMEM; @@ -179,17 +201,30 @@ static int acpi_als_add(struct acpi_device *device) indio_dev->name = ACPI_ALS_DEVICE_NAME; indio_dev->info = &acpi_als_info; - indio_dev->modes = INDIO_BUFFER_SOFTWARE; indio_dev->channels = acpi_als_channels; indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels); - buffer = devm_iio_kfifo_allocate(&device->dev); - if (!buffer) + als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); + if (!als->trig) return -ENOMEM; - iio_device_attach_buffer(indio_dev, buffer); + ret = devm_iio_trigger_register(dev, als->trig); + if (ret) + return ret; + /* + * Set hardware trigger by default to let events flow when + * BIOS support notification. + */ + indio_dev->trig = iio_trigger_get(als->trig); + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + acpi_als_trigger_handler, + NULL); + if (ret) + return ret; - return devm_iio_device_register(&device->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } static const struct acpi_device_id acpi_als_device_ids[] = { diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index df0647856e5d..4141c0fa7bc4 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -988,7 +988,6 @@ static int apds9960_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct apds9960_data *data; - struct iio_buffer *buffer; struct iio_dev *indio_dev; int ret; @@ -996,19 +995,18 @@ static int apds9960_probe(struct i2c_client *client, if (!indio_dev) return -ENOMEM; - buffer = devm_iio_kfifo_allocate(&client->dev); - if (!buffer) - return -ENOMEM; - - iio_device_attach_buffer(indio_dev, buffer); - indio_dev->info = &apds9960_info; indio_dev->name = APDS9960_DRV_NAME; indio_dev->channels = apds9960_channels; indio_dev->num_channels = ARRAY_SIZE(apds9960_channels); indio_dev->available_scan_masks = apds9960_scan_masks; - indio_dev->modes = (INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE); - indio_dev->setup_ops = &apds9960_buffer_setup_ops; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = devm_iio_kfifo_buffer_setup(&client->dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &apds9960_buffer_setup_ops); + if (ret) + return ret; data = iio_priv(indio_dev); i2c_set_clientdata(client, indio_dev); diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c index 75d6b5fcf2cc..de472f23d1cb 100644 --- a/drivers/iio/light/cros_ec_light_prox.c +++ b/drivers/iio/light/cros_ec_light_prox.c @@ -182,8 +182,7 @@ static int cros_ec_light_prox_probe(struct platform_device *pdev) ret = cros_ec_sensors_core_init(pdev, indio_dev, true, cros_ec_sensors_capture, - cros_ec_sensors_push_data, - true); + cros_ec_sensors_push_data); if (ret) return ret; diff --git a/drivers/iio/light/gp2ap002.c b/drivers/iio/light/gp2ap002.c index 7ba7aa59437c..d048ae257c51 100644 --- a/drivers/iio/light/gp2ap002.c +++ b/drivers/iio/light/gp2ap002.c @@ -465,8 +465,7 @@ static int gp2ap002_probe(struct i2c_client *client, regmap = devm_regmap_init(dev, &gp2ap002_regmap_bus, dev, &config); if (IS_ERR(regmap)) { - dev_err(dev, "Failed to register i2c regmap %d\n", - (int)PTR_ERR(regmap)); + dev_err(dev, "Failed to register i2c regmap %ld\n", PTR_ERR(regmap)); return PTR_ERR(regmap); } gp2ap002->map = regmap; diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c index e2850c1a7353..d1d9f2d319e4 100644 --- a/drivers/iio/light/gp2ap020a00f.c +++ b/drivers/iio/light/gp2ap020a00f.c @@ -1551,7 +1551,6 @@ static int gp2ap020a00f_probe(struct i2c_client *client, } data->trig->ops = &gp2ap020a00f_trigger_ops; - data->trig->dev.parent = &data->client->dev; init_irq_work(&data->work, gp2ap020a00f_iio_trigger_work); diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 4093f2353d95..85c8a05b73cb 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -39,6 +39,11 @@ struct als_state { s64 timestamp; }; +static const u32 als_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_LIGHT, + HID_USAGE_SENSOR_LIGHT_ILLUM, +}; + /* Channel definitions */ static const struct iio_chan_spec als_channels[] = { { @@ -49,7 +54,8 @@ static const struct iio_chan_spec als_channels[] = { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | - BIT(IIO_CHAN_INFO_HYSTERESIS), + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), .scan_index = CHANNEL_SCAN_INDEX_INTENSITY, }, { @@ -58,7 +64,8 @@ static const struct iio_chan_spec als_channels[] = { .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ) | - BIT(IIO_CHAN_INFO_HYSTERESIS), + BIT(IIO_CHAN_INFO_HYSTERESIS) | + BIT(IIO_CHAN_INFO_HYSTERESIS_RELATIVE), .scan_index = CHANNEL_SCAN_INDEX_ILLUM, }, IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP) @@ -136,6 +143,10 @@ static int als_read_raw(struct iio_dev *indio_dev, ret_type = hid_sensor_read_raw_hyst_value( &als_state->common_attributes, val, val2); break; + case IIO_CHAN_INFO_HYSTERESIS_RELATIVE: + ret_type = hid_sensor_read_raw_hyst_rel_value( + &als_state->common_attributes, val, val2); + break; default: ret_type = -EINVAL; break; @@ -163,6 +174,10 @@ static int als_write_raw(struct iio_dev *indio_dev, ret = hid_sensor_write_raw_hyst_value( &als_state->common_attributes, val, val2); break; + case IIO_CHAN_INFO_HYSTERESIS_RELATIVE: + ret = hid_sensor_write_raw_hyst_rel_value( + &als_state->common_attributes, val, val2); + break; default: ret = -EINVAL; } @@ -252,17 +267,6 @@ static int als_parse_report(struct platform_device *pdev, &st->als_illum, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_LIGHT, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } return ret; } @@ -285,7 +289,9 @@ static int hid_als_probe(struct platform_device *pdev) als_state->common_attributes.pdev = pdev; ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_ALS, - &als_state->common_attributes); + &als_state->common_attributes, + als_sensitivity_addresses, + ARRAY_SIZE(als_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index e9e00ce0c6d4..17d167c3d595 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -28,6 +28,11 @@ struct prox_state { int scale_precision; }; +static const u32 prox_sensitivity_addresses[] = { + HID_USAGE_SENSOR_HUMAN_PRESENCE, + HID_USAGE_SENSOR_DATA_PRESENCE, +}; + /* Channel definitions */ static const struct iio_chan_spec prox_channels[] = { { @@ -220,29 +225,6 @@ static int prox_parse_report(struct platform_device *pdev, dev_dbg(&pdev->dev, "prox %x:%x\n", st->prox_attr.index, st->prox_attr.report_id); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_PRESENCE, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } - if (st->common_attributes.sensitivity.index < 0) - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_HUMAN_PRESENCE, - &st->common_attributes.sensitivity); - - st->scale_precision = hid_sensor_format_scale( - hsdev->usage, - &st->prox_attr, - &st->scale_pre_decml, &st->scale_post_decml); - return ret; } @@ -266,7 +248,9 @@ static int hid_prox_probe(struct platform_device *pdev) prox_state->common_attributes.pdev = pdev; ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_PROX, - &prox_state->common_attributes); + &prox_state->common_attributes, + prox_sensitivity_addresses, + ARRAY_SIZE(prox_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c index 2d48d61909a4..52963da401a7 100644 --- a/drivers/iio/light/opt3001.c +++ b/drivers/iio/light/opt3001.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * opt3001.c - Texas Instruments OPT3001 Light Sensor * * Copyright (C) 2014 Texas Instruments Incorporated - https://www.ti.com diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 31224a33bade..033578f444e4 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -990,7 +990,6 @@ static int rpr0521_probe(struct i2c_client *client, ret = -ENOMEM; goto err_pm_disable; } - data->drdy_trigger0->dev.parent = indio_dev->dev.parent; data->drdy_trigger0->ops = &rpr0521_trigger_ops; indio_dev->available_scan_masks = rpr0521_available_scan_masks; iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev); diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index b304801c7916..9b5c99823943 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -1247,7 +1247,6 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev) if (!trig) return -ENOMEM; - trig->dev.parent = &client->dev; trig->ops = &si1145_trigger_ops; iio_trigger_set_drvdata(trig, indio_dev); diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index 1055594b2276..41a2ce5a2d53 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -210,7 +210,6 @@ static int st_uvis25_allocate_trigger(struct iio_dev *iio_dev) return -ENOMEM; iio_trigger_set_drvdata(hw->trig, iio_dev); - hw->trig->dev.parent = dev; return devm_iio_trigger_register(dev, hw->trig); } diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index a2827d03ab0f..07e91846307c 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * Sensortek STK3310/STK3311 Ambient Light and Proximity Sensor * * Copyright (c) 2015, Intel Corporation. diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index fff4b36b8b58..2f7916f95689 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -1002,7 +1002,6 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) if (!trigger) return -ENOMEM; - trigger->dev.parent = &client->dev; trigger->ops = &vcnl4010_trigger_ops; iio_trigger_set_drvdata(trigger, indio_dev); diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index 73a28e30dddc..ae87740d9cef 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -511,7 +511,6 @@ static int vcnl4035_probe_trigger(struct iio_dev *indio_dev) if (!data->drdy_trigger0) return -ENOMEM; - data->drdy_trigger0->dev.parent = indio_dev->dev.parent; data->drdy_trigger0->ops = &vcnl4035_trigger_ops; iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev); ret = devm_iio_trigger_register(indio_dev->dev.parent, diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index b2f3129e1b4f..00f9766bad5c 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -922,7 +922,6 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, goto err_poweroff; } - data->dready_trig->dev.parent = dev; data->dready_trig->ops = &bmc150_magn_trigger_ops; iio_trigger_set_drvdata(data->dready_trig, indio_dev); ret = iio_trigger_register(data->dready_trig); diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index fa48044b7f5b..b78691523dd4 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -62,6 +62,11 @@ static const u32 magn_3d_addresses[MAGN_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_TIME_TIMESTAMP, }; +static const u32 magn_3d_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ORIENTATION, + HID_USAGE_SENSOR_ORIENT_MAGN_FLUX, +}; + /* Channel definitions */ static const struct iio_chan_spec magn_3d_channels[] = { { @@ -448,27 +453,6 @@ static int magn_3d_parse_report(struct platform_device *pdev, &st->rot_attr.scale_pre_decml, &st->rot_attr.scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->magn_flux_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ORIENTATION, - &st->magn_flux_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->magn_flux_attributes.sensitivity.index, - st->magn_flux_attributes.sensitivity.report_id); - } - if (st->magn_flux_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_ORIENT_MAGN_FLUX, - &st->magn_flux_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->magn_flux_attributes.sensitivity.index, - st->magn_flux_attributes.sensitivity.report_id); - } if (st->rot_attributes.sensitivity.index < 0) { sensor_hub_input_get_attribute_info(hsdev, HID_FEATURE_REPORT, usage_id, @@ -507,12 +491,16 @@ static int hid_magn_3d_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_COMPASS_3D, - &magn_state->magn_flux_attributes); + &magn_state->magn_flux_attributes, + magn_3d_sensitivity_addresses, + ARRAY_SIZE(magn_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; } magn_state->rot_attributes = magn_state->magn_flux_attributes; + /* sensitivity of rot_attribute is not the same as magn_flux_attributes */ + magn_state->rot_attributes.sensitivity.index = -1; ret = magn_3d_parse_report(pdev, hsdev, &channels, &chan_count, diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index 7242897a05e9..dd811da9cb6d 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -579,7 +579,6 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq) if (!data->drdy_trig) return -ENOMEM; - data->drdy_trig->dev.parent = dev; ret = devm_iio_trigger_register(dev, data->drdy_trig); if (ret < 0) return ret; diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h index 204b285725c8..7ba6a6ba5c58 100644 --- a/drivers/iio/magnetometer/st_magn.h +++ b/drivers/iio/magnetometer/st_magn.h @@ -21,6 +21,7 @@ #define LSM303AGR_MAGN_DEV_NAME "lsm303agr_magn" #define LIS2MDL_MAGN_DEV_NAME "lis2mdl" #define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn" +#define IIS2MDC_MAGN_DEV_NAME "iis2mdc" const struct st_sensor_settings *st_magn_get_settings(const char *name); int st_magn_common_probe(struct iio_dev *indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 79de721e6015..71faebd07feb 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -337,6 +337,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .sensors_supported = { [0] = LSM303AGR_MAGN_DEV_NAME, [1] = LIS2MDL_MAGN_DEV_NAME, + [2] = IIS2MDC_MAGN_DEV_NAME, }, .ch = (struct iio_chan_spec *)st_magn_3_16bit_channels, .odr = { diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index c6bb4ce77594..36f4e7b53b24 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -46,6 +46,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,lsm9ds1-magn", .data = LSM9DS1_MAGN_DEV_NAME, }, + { + .compatible = "st,iis2mdc", + .data = IIS2MDC_MAGN_DEV_NAME, + }, {}, }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -101,6 +105,7 @@ static const struct i2c_device_id st_magn_id_table[] = { { LSM303AGR_MAGN_DEV_NAME }, { LIS2MDL_MAGN_DEV_NAME }, { LSM9DS1_MAGN_DEV_NAME }, + { IIS2MDC_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(i2c, st_magn_id_table); diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 3d08d74c367d..0e2323dfc687 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -41,6 +41,10 @@ static const struct of_device_id st_magn_of_match[] = { .compatible = "st,lsm9ds1-magn", .data = LSM9DS1_MAGN_DEV_NAME, }, + { + .compatible = "st,iis2mdc", + .data = IIS2MDC_MAGN_DEV_NAME, + }, {} }; MODULE_DEVICE_TABLE(of, st_magn_of_match); @@ -92,6 +96,7 @@ static const struct spi_device_id st_magn_id_table[] = { { LSM303AGR_MAGN_DEV_NAME }, { LIS2MDL_MAGN_DEV_NAME }, { LSM9DS1_MAGN_DEV_NAME }, + { IIS2MDC_MAGN_DEV_NAME }, {}, }; MODULE_DEVICE_TABLE(spi, st_magn_id_table); diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index d46f23d82b3d..2f2f8cb3c26c 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -32,13 +32,14 @@ #include <linux/regmap.h> #include <linux/regulator/consumer.h> #include <linux/random.h> -#include <linux/unaligned/be_byteshift.h> #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/triggered_buffer.h> +#include <asm/unaligned.h> + /* This register map covers YAS530 and YAS532 but differs in YAS 537 and YAS539 */ #define YAS5XX_DEVICE_ID 0x80 #define YAS5XX_ACTUATE_INIT_COIL 0x81 @@ -887,6 +888,7 @@ static int yas5xx_probe(struct i2c_client *i2c, strncpy(yas5xx->name, "yas532", sizeof(yas5xx->name)); break; default: + ret = -ENODEV; dev_err(dev, "unhandled device ID %02x\n", yas5xx->devid); goto assert_reset; } diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 52ebef30f9be..7af48d336285 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -47,6 +47,11 @@ static const u32 incl_3d_addresses[INCLI_3D_CHANNEL_MAX] = { HID_USAGE_SENSOR_ORIENT_TILT_Z }; +static const u32 incl_3d_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ORIENTATION, + HID_USAGE_SENSOR_ORIENT_TILT, +}; + /* Channel definitions */ static const struct iio_chan_spec incl_3d_channels[] = { { @@ -291,17 +296,6 @@ static int incl_3d_parse_report(struct platform_device *pdev, &st->incl[CHANNEL_SCAN_INDEX_X], &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ORIENTATION, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } return ret; } @@ -327,7 +321,9 @@ static int hid_incl_3d_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_INCLINOMETER_3D, - &incl_state->common_attributes); + &incl_state->common_attributes, + incl_3d_sensitivity_addresses, + ARRAY_SIZE(incl_3d_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index 18e4ef060096..cf7f57a47681 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -21,7 +21,7 @@ struct dev_rot_state { struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info quaternion; struct { - u32 sampled_vals[4] __aligned(16); + s32 sampled_vals[4] __aligned(16); u64 timestamp __aligned(8); } scan; int scale_pre_decml; @@ -31,6 +31,11 @@ struct dev_rot_state { s64 timestamp; }; +static const u32 rotation_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ORIENTATION, + HID_USAGE_SENSOR_ORIENT_QUATERNION, +}; + /* Channel definitions */ static const struct iio_chan_spec dev_rot_channels[] = { { @@ -170,8 +175,15 @@ static int dev_rot_capture_sample(struct hid_sensor_hub_device *hsdev, struct dev_rot_state *rot_state = iio_priv(indio_dev); if (usage_id == HID_USAGE_SENSOR_ORIENT_QUATERNION) { - memcpy(&rot_state->scan.sampled_vals, raw_data, - sizeof(rot_state->scan.sampled_vals)); + if (raw_len / 4 == sizeof(s16)) { + rot_state->scan.sampled_vals[0] = ((s16 *)raw_data)[0]; + rot_state->scan.sampled_vals[1] = ((s16 *)raw_data)[1]; + rot_state->scan.sampled_vals[2] = ((s16 *)raw_data)[2]; + rot_state->scan.sampled_vals[3] = ((s16 *)raw_data)[3]; + } else { + memcpy(&rot_state->scan.sampled_vals, raw_data, + sizeof(rot_state->scan.sampled_vals)); + } dev_dbg(&indio_dev->dev, "Recd Quat len:%zu::%zu\n", raw_len, sizeof(rot_state->scan.sampled_vals)); @@ -214,18 +226,6 @@ static int dev_rot_parse_report(struct platform_device *pdev, &st->quaternion, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ORIENTATION, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } - return 0; } @@ -263,8 +263,11 @@ static int hid_dev_rot_probe(struct platform_device *pdev) return -EINVAL; } - ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage, - &rot_state->common_attributes); + ret = hid_sensor_parse_common_attributes(hsdev, + hsdev->usage, + &rot_state->common_attributes, + rotation_sensitivity_addresses, + ARRAY_SIZE(rotation_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index 64a7fa7db6af..fd77e7ee87f3 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -47,6 +47,10 @@ struct hinge_state { u64 timestamp; }; +static const u32 hinge_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1), +}; + /* Channel definitions */ static const struct iio_chan_spec hinge_channels[] = { { @@ -251,18 +255,6 @@ static int hinge_parse_report(struct platform_device *pdev, &st->hinge[CHANNEL_SCAN_INDEX_HINGE_ANGLE], &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_FIELD_CUSTOM_VALUE(1), - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } - return ret; } @@ -289,7 +281,9 @@ static int hid_hinge_probe(struct platform_device *pdev) st->labels[i] = hinge_labels[i]; ret = hid_sensor_parse_common_attributes(hsdev, hsdev->usage, - &st->common_attributes); + &st->common_attributes, + hinge_sensitivity_addresses, + ARRAY_SIZE(hinge_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c index a88ed0eb3adc..6e22b538091f 100644 --- a/drivers/iio/potentiometer/max5481.c +++ b/drivers/iio/potentiometer/max5481.c @@ -136,7 +136,7 @@ static int max5481_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; - dev_set_drvdata(&spi->dev, indio_dev); + spi_set_drvdata(spi, indio_dev); data = iio_priv(indio_dev); data->spi = spi; @@ -163,7 +163,7 @@ static int max5481_probe(struct spi_device *spi) static int max5481_remove(struct spi_device *spi) { - struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + struct iio_dev *indio_dev = spi_get_drvdata(spi); struct max5481_data *data = iio_priv(indio_dev); iio_device_unregister(indio_dev); diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c index 7ec51976ec99..1c0d46a96200 100644 --- a/drivers/iio/potentiometer/max5487.c +++ b/drivers/iio/potentiometer/max5487.c @@ -92,7 +92,7 @@ static int max5487_spi_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; - dev_set_drvdata(&spi->dev, indio_dev); + spi_set_drvdata(spi, indio_dev); data = iio_priv(indio_dev); data->spi = spi; @@ -114,7 +114,7 @@ static int max5487_spi_probe(struct spi_device *spi) static int max5487_spi_remove(struct spi_device *spi) { - struct iio_dev *indio_dev = dev_get_drvdata(&spi->dev); + struct iio_dev *indio_dev = spi_get_drvdata(spi); iio_device_unregister(indio_dev); diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index f34ca769dc20..8a9c576616ee 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -322,7 +322,7 @@ static int lmp91000_probe(struct i2c_client *client, return PTR_ERR(data->regmap); } - data->trig = devm_iio_trigger_alloc(data->dev, "%s-mux%d", + data->trig = devm_iio_trigger_alloc(dev, "%s-mux%d", indio_dev->name, indio_dev->id); if (!data->trig) { dev_err(dev, "cannot allocate iio trigger.\n"); @@ -330,7 +330,6 @@ static int lmp91000_probe(struct i2c_client *client, } data->trig->ops = &lmp91000_trigger_ops; - data->trig->dev.parent = dev; init_completion(&data->completion); ret = lmp91000_read_config(data); diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c index aa043cb9ac42..2f882e109423 100644 --- a/drivers/iio/pressure/cros_ec_baro.c +++ b/drivers/iio/pressure/cros_ec_baro.c @@ -139,8 +139,7 @@ static int cros_ec_baro_probe(struct platform_device *pdev) ret = cros_ec_sensors_core_init(pdev, indio_dev, true, cros_ec_sensors_capture, - cros_ec_sensors_push_data, - true); + cros_ec_sensors_push_data); if (ret) return ret; diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index 5c458788f346..c416d261e3e3 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -29,6 +29,11 @@ struct press_state { int value_offset; }; +static const u32 press_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE, + HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE +}; + /* Channel definitions */ static const struct iio_chan_spec press_channels[] = { { @@ -225,17 +230,6 @@ static int press_parse_report(struct platform_device *pdev, &st->press_attr, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) { - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE, - &st->common_attributes.sensitivity); - dev_dbg(&pdev->dev, "Sensitivity index:report %d:%d\n", - st->common_attributes.sensitivity.index, - st->common_attributes.sensitivity.report_id); - } return ret; } @@ -260,7 +254,9 @@ static int hid_press_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_PRESSURE, - &press_state->common_attributes); + &press_state->common_attributes, + press_sensitivity_addresses, + ARRAY_SIZE(press_sensitivity_addresses)); if (ret) { dev_err(&pdev->dev, "failed to setup common attributes\n"); return ret; diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 2cecbe0adb3f..a93411216aee 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -103,7 +103,7 @@ static const struct zpa2326_frequency *zpa2326_highest_frequency(void) } /** - * struct zpa_private - Per-device internal private state + * struct zpa2326_private - Per-device internal private state * @timestamp: Buffered samples ready datum. * @regmap: Underlying I2C / SPI bus adapter used to abstract slave register * accesses. @@ -1382,7 +1382,7 @@ static const struct iio_trigger_ops zpa2326_trigger_ops = { }; /** - * zpa2326_init_trigger() - Create an interrupt driven / hardware trigger + * zpa2326_init_managed_trigger() - Create interrupt driven / hardware trigger * allowing to notify external devices a new sample is * ready. * @parent: Hardware sampling device @indio_dev is a child of. @@ -1413,7 +1413,6 @@ static int zpa2326_init_managed_trigger(struct device *parent, return -ENOMEM; /* Basic setup. */ - trigger->dev.parent = parent; trigger->ops = &zpa2326_trigger_ops; private->trigger = trigger; diff --git a/drivers/iio/proximity/Kconfig b/drivers/iio/proximity/Kconfig index 12672a0e89ed..7c7203ca3ac6 100644 --- a/drivers/iio/proximity/Kconfig +++ b/drivers/iio/proximity/Kconfig @@ -21,6 +21,17 @@ endmenu menu "Proximity and distance sensors" +config CROS_EC_MKBP_PROXIMITY + tristate "ChromeOS EC MKBP Proximity sensor" + depends on CROS_EC + help + Say Y here to enable the proximity sensor implemented via the ChromeOS EC MKBP + switches protocol. You must enable one bus option (CROS_EC_I2C or CROS_EC_SPI) + to use this. + + To compile this driver as a module, choose M here: the + module will be called cros_ec_mkbp_proximity. + config ISL29501 tristate "Intersil ISL29501 Time Of Flight sensor" depends on I2C diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile index 9c1aca1a8b79..cbdac09433eb 100644 --- a/drivers/iio/proximity/Makefile +++ b/drivers/iio/proximity/Makefile @@ -5,6 +5,7 @@ # When adding new entries keep the list in alphabetical order obj-$(CONFIG_AS3935) += as3935.o +obj-$(CONFIG_CROS_EC_MKBP_PROXIMITY) += cros_ec_mkbp_proximity.o obj-$(CONFIG_ISL29501) += isl29501.o obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o obj-$(CONFIG_MB1232) += mb1232.o diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index b79ada839e01..edc4a35ae66d 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -411,7 +411,6 @@ static int as3935_probe(struct spi_device *spi) st->trig = trig; st->noise_tripped = jiffies - HZ; - trig->dev.parent = indio_dev->dev.parent; iio_trigger_set_drvdata(trig, indio_dev); trig->ops = &iio_interrupt_trigger_ops; diff --git a/drivers/iio/proximity/cros_ec_mkbp_proximity.c b/drivers/iio/proximity/cros_ec_mkbp_proximity.c new file mode 100644 index 000000000000..8213b0081713 --- /dev/null +++ b/drivers/iio/proximity/cros_ec_mkbp_proximity.c @@ -0,0 +1,271 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for cros-ec proximity sensor exposed through MKBP switch + * + * Copyright 2021 Google LLC. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/notifier.h> +#include <linux/of.h> +#include <linux/platform_device.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <linux/platform_data/cros_ec_commands.h> +#include <linux/platform_data/cros_ec_proto.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include <asm/unaligned.h> + +struct cros_ec_mkbp_proximity_data { + struct cros_ec_device *ec; + struct iio_dev *indio_dev; + struct mutex lock; + struct notifier_block notifier; + int last_proximity; + bool enabled; +}; + +static const struct iio_event_spec cros_ec_mkbp_proximity_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec cros_ec_mkbp_proximity_chan_spec[] = { + { + .type = IIO_PROXIMITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .event_spec = cros_ec_mkbp_proximity_events, + .num_event_specs = ARRAY_SIZE(cros_ec_mkbp_proximity_events), + }, +}; + +static int cros_ec_mkbp_proximity_parse_state(const void *data) +{ + u32 switches = get_unaligned_le32(data); + + return !!(switches & BIT(EC_MKBP_FRONT_PROXIMITY)); +} + +static int cros_ec_mkbp_proximity_query(struct cros_ec_device *ec_dev, + int *state) +{ + struct { + struct cros_ec_command msg; + union { + struct ec_params_mkbp_info params; + u32 switches; + }; + } __packed buf = { }; + struct ec_params_mkbp_info *params = &buf.params; + struct cros_ec_command *msg = &buf.msg; + u32 *switches = &buf.switches; + size_t insize = sizeof(*switches); + int ret; + + msg->command = EC_CMD_MKBP_INFO; + msg->version = 1; + msg->outsize = sizeof(*params); + msg->insize = insize; + + params->info_type = EC_MKBP_INFO_CURRENT; + params->event_type = EC_MKBP_EVENT_SWITCH; + + ret = cros_ec_cmd_xfer_status(ec_dev, msg); + if (ret < 0) + return ret; + + if (ret != insize) { + dev_warn(ec_dev->dev, "wrong result size: %d != %zu\n", ret, + insize); + return -EPROTO; + } + + *state = cros_ec_mkbp_proximity_parse_state(switches); + return IIO_VAL_INT; +} + +static void cros_ec_mkbp_proximity_push_event(struct cros_ec_mkbp_proximity_data *data, int state) +{ + s64 timestamp; + u64 ev; + int dir; + struct iio_dev *indio_dev = data->indio_dev; + struct cros_ec_device *ec = data->ec; + + mutex_lock(&data->lock); + if (state != data->last_proximity) { + if (data->enabled) { + timestamp = ktime_to_ns(ec->last_event_time); + if (iio_device_get_clock(indio_dev) != CLOCK_BOOTTIME) + timestamp = iio_get_time_ns(indio_dev); + + dir = state ? IIO_EV_DIR_FALLING : IIO_EV_DIR_RISING; + ev = IIO_UNMOD_EVENT_CODE(IIO_PROXIMITY, 0, + IIO_EV_TYPE_THRESH, dir); + iio_push_event(indio_dev, ev, timestamp); + } + data->last_proximity = state; + } + mutex_unlock(&data->lock); +} + +static int cros_ec_mkbp_proximity_notify(struct notifier_block *nb, + unsigned long queued_during_suspend, + void *_ec) +{ + struct cros_ec_mkbp_proximity_data *data; + struct cros_ec_device *ec = _ec; + u8 event_type = ec->event_data.event_type & EC_MKBP_EVENT_TYPE_MASK; + void *switches; + int state; + + if (event_type == EC_MKBP_EVENT_SWITCH) { + data = container_of(nb, struct cros_ec_mkbp_proximity_data, + notifier); + + switches = &ec->event_data.data.switches; + state = cros_ec_mkbp_proximity_parse_state(switches); + cros_ec_mkbp_proximity_push_event(data, state); + } + + return NOTIFY_OK; +} + +static int cros_ec_mkbp_proximity_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, int *val, + int *val2, long mask) +{ + struct cros_ec_mkbp_proximity_data *data = iio_priv(indio_dev); + struct cros_ec_device *ec = data->ec; + + if (chan->type == IIO_PROXIMITY && mask == IIO_CHAN_INFO_RAW) + return cros_ec_mkbp_proximity_query(ec, val); + + return -EINVAL; +} + +static int cros_ec_mkbp_proximity_read_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir) +{ + struct cros_ec_mkbp_proximity_data *data = iio_priv(indio_dev); + + return data->enabled; +} + +static int cros_ec_mkbp_proximity_write_event_config(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, int state) +{ + struct cros_ec_mkbp_proximity_data *data = iio_priv(indio_dev); + + mutex_lock(&data->lock); + data->enabled = state; + mutex_unlock(&data->lock); + + return 0; +} + +static const struct iio_info cros_ec_mkbp_proximity_info = { + .read_raw = cros_ec_mkbp_proximity_read_raw, + .read_event_config = cros_ec_mkbp_proximity_read_event_config, + .write_event_config = cros_ec_mkbp_proximity_write_event_config, +}; + +static __maybe_unused int cros_ec_mkbp_proximity_resume(struct device *dev) +{ + struct cros_ec_mkbp_proximity_data *data = dev_get_drvdata(dev); + struct cros_ec_device *ec = data->ec; + int ret, state; + + ret = cros_ec_mkbp_proximity_query(ec, &state); + if (ret < 0) { + dev_warn(dev, "failed to fetch proximity state on resume: %d\n", + ret); + } else { + cros_ec_mkbp_proximity_push_event(data, state); + } + + return 0; +} + +static SIMPLE_DEV_PM_OPS(cros_ec_mkbp_proximity_pm_ops, NULL, + cros_ec_mkbp_proximity_resume); + +static int cros_ec_mkbp_proximity_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct cros_ec_device *ec = dev_get_drvdata(dev->parent); + struct iio_dev *indio_dev; + struct cros_ec_mkbp_proximity_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->ec = ec; + data->indio_dev = indio_dev; + data->last_proximity = -1; /* Unknown to start */ + mutex_init(&data->lock); + platform_set_drvdata(pdev, data); + + indio_dev->name = dev->driver->name; + indio_dev->info = &cros_ec_mkbp_proximity_info; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = cros_ec_mkbp_proximity_chan_spec; + indio_dev->num_channels = ARRAY_SIZE(cros_ec_mkbp_proximity_chan_spec); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + data->notifier.notifier_call = cros_ec_mkbp_proximity_notify; + blocking_notifier_chain_register(&ec->event_notifier, &data->notifier); + + return 0; +} + +static int cros_ec_mkbp_proximity_remove(struct platform_device *pdev) +{ + struct cros_ec_mkbp_proximity_data *data = platform_get_drvdata(pdev); + struct cros_ec_device *ec = data->ec; + + blocking_notifier_chain_unregister(&ec->event_notifier, + &data->notifier); + + return 0; +} + +static const struct of_device_id cros_ec_mkbp_proximity_of_match[] = { + { .compatible = "google,cros-ec-mkbp-proximity" }, + {} +}; +MODULE_DEVICE_TABLE(of, cros_ec_mkbp_proximity_of_match); + +static struct platform_driver cros_ec_mkbp_proximity_driver = { + .driver = { + .name = "cros-ec-mkbp-proximity", + .of_match_table = cros_ec_mkbp_proximity_of_match, + .pm = &cros_ec_mkbp_proximity_pm_ops, + }, + .probe = cros_ec_mkbp_proximity_probe, + .remove = cros_ec_mkbp_proximity_remove, +}; +module_platform_driver(cros_ec_mkbp_proximity_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS EC MKBP proximity sensor driver"); diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index 37fd0b65a014..327ebb7ddbb9 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -763,7 +763,11 @@ static int sx9310_write_far_debounce(struct sx9310_data *data, int val) int ret; unsigned int regval; - val = ilog2(val); + if (val > 0) + val = ilog2(val); + if (!FIELD_FIT(SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, val)) + return -EINVAL; + regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_FAR_DEBOUNCE_MASK, val); mutex_lock(&data->mutex); @@ -780,7 +784,11 @@ static int sx9310_write_close_debounce(struct sx9310_data *data, int val) int ret; unsigned int regval; - val = ilog2(val); + if (val > 0) + val = ilog2(val); + if (!FIELD_FIT(SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, val)) + return -EINVAL; + regval = FIELD_PREP(SX9310_REG_PROX_CTRL10_CLOSE_DEBOUNCE_MASK, val); mutex_lock(&data->mutex); @@ -1213,17 +1221,17 @@ static int sx9310_init_compensation(struct iio_dev *indio_dev) } static const struct sx9310_reg_default * -sx9310_get_default_reg(struct sx9310_data *data, int i, +sx9310_get_default_reg(struct sx9310_data *data, int idx, struct sx9310_reg_default *reg_def) { - int ret; const struct device_node *np = data->client->dev.of_node; - u32 combined[SX9310_NUM_CHANNELS] = { 4, 4, 4, 4 }; + u32 combined[SX9310_NUM_CHANNELS]; + u32 start = 0, raw = 0, pos = 0; unsigned long comb_mask = 0; + int ret, i, count; const char *res; - u32 start = 0, raw = 0, pos = 0; - memcpy(reg_def, &sx9310_default_regs[i], sizeof(*reg_def)); + memcpy(reg_def, &sx9310_default_regs[idx], sizeof(*reg_def)); if (!np) return reg_def; @@ -1234,15 +1242,31 @@ sx9310_get_default_reg(struct sx9310_data *data, int i, reg_def->def |= SX9310_REG_PROX_CTRL2_SHIELDEN_GROUND; } - reg_def->def &= ~SX9310_REG_PROX_CTRL2_COMBMODE_MASK; - of_property_read_u32_array(np, "semtech,combined-sensors", - combined, ARRAY_SIZE(combined)); - for (i = 0; i < ARRAY_SIZE(combined); i++) { - if (combined[i] <= SX9310_NUM_CHANNELS) - comb_mask |= BIT(combined[i]); + count = of_property_count_elems_of_size(np, "semtech,combined-sensors", + sizeof(u32)); + if (count > 0 && count <= ARRAY_SIZE(combined)) { + ret = of_property_read_u32_array(np, "semtech,combined-sensors", + combined, count); + if (ret) + break; + } else { + /* + * Either the property does not exist in the DT or the + * number of entries is incorrect. + */ + break; } + for (i = 0; i < count; i++) { + if (combined[i] >= SX9310_NUM_CHANNELS) { + /* Invalid sensor (invalid DT). */ + break; + } + comb_mask |= BIT(combined[i]); + } + if (i < count) + break; - comb_mask &= 0xf; + reg_def->def &= ~SX9310_REG_PROX_CTRL2_COMBMODE_MASK; if (comb_mask == (BIT(3) | BIT(2) | BIT(1) | BIT(0))) reg_def->def |= SX9310_REG_PROX_CTRL2_COMBMODE_CS0_CS1_CS2_CS3; else if (comb_mask == (BIT(1) | BIT(2))) @@ -1453,7 +1477,6 @@ static int sx9310_probe(struct i2c_client *client) if (!data->trig) return -ENOMEM; - data->trig->dev.parent = dev; data->trig->ops = &sx9310_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index acb821cbad46..a87f4a8e4327 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -758,7 +758,7 @@ static const struct sx9500_reg_default sx9500_default_regs[] = { .reg = SX9500_REG_PROX_CTRL5, /* * Debouncer off, lowest average negative filter, - * highest average postive filter. + * highest average positive filter. */ .def = 0x0f, }, @@ -950,7 +950,6 @@ static int sx9500_probe(struct i2c_client *client, if (!data->trig) return -ENOMEM; - data->trig->dev.parent = &client->dev; data->trig->ops = &sx9500_trigger_ops; iio_trigger_set_drvdata(data->trig, indio_dev); diff --git a/drivers/iio/proximity/vcnl3020.c b/drivers/iio/proximity/vcnl3020.c index 37264f801ad0..43817f6b3086 100644 --- a/drivers/iio/proximity/vcnl3020.c +++ b/drivers/iio/proximity/vcnl3020.c @@ -40,6 +40,17 @@ #define VCNL_ON_DEMAND_TIMEOUT_US 100000 #define VCNL_POLL_US 20000 +static const int vcnl3020_prox_sampling_frequency[][2] = { + {1, 950000}, + {3, 906250}, + {7, 812500}, + {16, 625000}, + {31, 250000}, + {62, 500000}, + {125, 0}, + {250, 0}, +}; + /** * struct vcnl3020_data - vcnl3020 specific data. * @regmap: device register map. @@ -165,10 +176,51 @@ err_unlock: return rc; } +static int vcnl3020_read_proxy_samp_freq(struct vcnl3020_data *data, int *val, + int *val2) +{ + int rc; + unsigned int prox_rate; + + rc = regmap_read(data->regmap, VCNL_PROXIMITY_RATE, &prox_rate); + if (rc) + return rc; + + if (prox_rate >= ARRAY_SIZE(vcnl3020_prox_sampling_frequency)) + return -EINVAL; + + *val = vcnl3020_prox_sampling_frequency[prox_rate][0]; + *val2 = vcnl3020_prox_sampling_frequency[prox_rate][1]; + + return 0; +} + +static int vcnl3020_write_proxy_samp_freq(struct vcnl3020_data *data, int val, + int val2) +{ + unsigned int i; + int index = -1; + + for (i = 0; i < ARRAY_SIZE(vcnl3020_prox_sampling_frequency); i++) { + if (val == vcnl3020_prox_sampling_frequency[i][0] && + val2 == vcnl3020_prox_sampling_frequency[i][1]) { + index = i; + break; + } + } + + if (index < 0) + return -EINVAL; + + return regmap_write(data->regmap, VCNL_PROXIMITY_RATE, index); +} + static const struct iio_chan_spec vcnl3020_channels[] = { { .type = IIO_PROXIMITY, - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ), }, }; @@ -185,6 +237,47 @@ static int vcnl3020_read_raw(struct iio_dev *indio_dev, if (rc) return rc; return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + rc = vcnl3020_read_proxy_samp_freq(data, val, val2); + if (rc < 0) + return rc; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int vcnl3020_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + int rc; + struct vcnl3020_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + rc = iio_device_claim_direct_mode(indio_dev); + if (rc) + return rc; + rc = vcnl3020_write_proxy_samp_freq(data, val, val2); + iio_device_release_direct_mode(indio_dev); + return rc; + default: + return -EINVAL; + } +} + +static int vcnl3020_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *vals = (int *)vcnl3020_prox_sampling_frequency; + *type = IIO_VAL_INT_PLUS_MICRO; + *length = 2 * ARRAY_SIZE(vcnl3020_prox_sampling_frequency); + return IIO_AVAIL_LIST; default: return -EINVAL; } @@ -192,6 +285,8 @@ static int vcnl3020_read_raw(struct iio_dev *indio_dev, static const struct iio_info vcnl3020_info = { .read_raw = vcnl3020_read_raw, + .write_raw = vcnl3020_write_raw, + .read_avail = vcnl3020_read_avail, }; static const struct regmap_config vcnl3020_regmap_config = { diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index da9a247097fa..dc534ed784c3 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -25,6 +25,10 @@ struct temperature_state { int value_offset; }; +static const u32 temperature_sensitivity_addresses[] = { + HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE, +}; + /* Channel definitions */ static const struct iio_chan_spec temperature_channels[] = { { @@ -173,14 +177,6 @@ static int temperature_parse_report(struct platform_device *pdev, &st->temperature_attr, &st->scale_pre_decml, &st->scale_post_decml); - /* Set Sensitivity field ids, when there is no individual modifier */ - if (st->common_attributes.sensitivity.index < 0) - sensor_hub_input_get_attribute_info(hsdev, - HID_FEATURE_REPORT, usage_id, - HID_USAGE_SENSOR_DATA_MOD_CHANGE_SENSITIVITY_ABS | - HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE, - &st->common_attributes.sensitivity); - return ret; } @@ -209,7 +205,9 @@ static int hid_temperature_probe(struct platform_device *pdev) ret = hid_sensor_parse_common_attributes(hsdev, HID_USAGE_SENSOR_TEMPERATURE, - &temp_st->common_attributes); + &temp_st->common_attributes, + temperature_sensitivity_addresses, + ARRAY_SIZE(temperature_sensitivity_addresses)); if (ret) return ret; diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c index ad2b35c65548..b422371a4674 100644 --- a/drivers/iio/temperature/tmp007.c +++ b/drivers/iio/temperature/tmp007.c @@ -439,6 +439,13 @@ static bool tmp007_identify(struct i2c_client *client) return (manf_id == TMP007_MANUFACTURER_MAGIC && dev_id == TMP007_DEVICE_MAGIC); } +static void tmp007_powerdown_action_cb(void *priv) +{ + struct tmp007_data *data = priv; + + tmp007_powerdown(data); +} + static int tmp007_probe(struct i2c_client *client, const struct i2c_device_id *tmp007_id) { @@ -489,6 +496,10 @@ static int tmp007_probe(struct i2c_client *client, if (ret < 0) return ret; + ret = devm_add_action_or_reset(&client->dev, tmp007_powerdown_action_cb, data); + if (ret) + return ret; + /* * Only the following flags can activate ALERT pin. Data conversion/validity flags * flags can still be polled for getting temperature data @@ -502,7 +513,7 @@ static int tmp007_probe(struct i2c_client *client, ret = i2c_smbus_read_word_swapped(data->client, TMP007_STATUS_MASK); if (ret < 0) - goto error_powerdown; + return ret; data->status_mask = ret; data->status_mask |= (TMP007_STATUS_OHF | TMP007_STATUS_OLF @@ -510,7 +521,7 @@ static int tmp007_probe(struct i2c_client *client, ret = i2c_smbus_write_word_swapped(data->client, TMP007_STATUS_MASK, data->status_mask); if (ret < 0) - goto error_powerdown; + return ret; if (client->irq) { ret = devm_request_threaded_irq(&client->dev, client->irq, @@ -519,27 +530,11 @@ static int tmp007_probe(struct i2c_client *client, tmp007_id->name, indio_dev); if (ret) { dev_err(&client->dev, "irq request error %d\n", -ret); - goto error_powerdown; + return ret; } } - 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; + return devm_iio_device_register(&client->dev, indio_dev); } #ifdef CONFIG_PM_SLEEP @@ -582,7 +577,6 @@ static struct i2c_driver tmp007_driver = { .pm = &tmp007_pm_ops, }, .probe = tmp007_probe, - .remove = tmp007_remove, .id_table = tmp007_id, }; module_i2c_driver(tmp007_driver); diff --git a/drivers/iio/test/Kconfig b/drivers/iio/test/Kconfig new file mode 100644 index 000000000000..679a7794af20 --- /dev/null +++ b/drivers/iio/test/Kconfig @@ -0,0 +1,9 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Industrial I/O subsystem unit tests configuration +# + +# Keep in alphabetical order +config IIO_TEST_FORMAT + bool "Test IIO formatting functions" + depends on KUNIT=y diff --git a/drivers/iio/test/Makefile b/drivers/iio/test/Makefile new file mode 100644 index 000000000000..f1099b495301 --- /dev/null +++ b/drivers/iio/test/Makefile @@ -0,0 +1,7 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Makefile for the industrial I/O unit tests. +# + +# Keep in alphabetical order +obj-$(CONFIG_IIO_TEST_FORMAT) += iio-test-format.o diff --git a/drivers/iio/test/iio-test-format.c b/drivers/iio/test/iio-test-format.c new file mode 100644 index 000000000000..55a0cfe9181d --- /dev/null +++ b/drivers/iio/test/iio-test-format.c @@ -0,0 +1,198 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* Unit tests for IIO formatting functions + * + * Copyright (c) 2020 Lars-Peter Clausen <lars@metafoo.de> + */ + +#include <kunit/test.h> +#include <linux/iio/iio.h> + +#define IIO_TEST_FORMAT_EXPECT_EQ(_test, _buf, _ret, _val) do { \ + KUNIT_EXPECT_EQ(_test, (int)strlen(_buf), _ret); \ + KUNIT_EXPECT_STREQ(_test, (_buf), (_val)); \ + } while (0) + +static void iio_test_iio_format_value_integer(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int val; + int ret; + + val = 42; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "42\n"); + + val = -23; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-23\n"); + + val = 0; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0\n"); + + val = INT_MAX; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "2147483647\n"); + + val = INT_MIN; + ret = iio_format_value(buf, IIO_VAL_INT, 1, &val); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-2147483648\n"); +} + +static void iio_test_iio_format_value_fixedpoint(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int values[2]; + int ret; + + /* positive >= 1 */ + values[0] = 1; + values[1] = 10; + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000010\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000010 dB\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1.000000010\n"); + + /* positive < 1 */ + values[0] = 0; + values[1] = 12; + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000012\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000012 dB\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000012\n"); + + /* negative <= -1 */ + values[0] = -1; + values[1] = 10; + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000010\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000010 dB\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1.000000010\n"); + + /* negative > -1 */ + values[0] = 0; + values[1] = -123; + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000123\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO_DB, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000123 dB\n"); + + ret = iio_format_value(buf, IIO_VAL_INT_PLUS_NANO, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000000123\n"); +} + +static void iio_test_iio_format_value_fractional(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int values[2]; + int ret; + + /* positive < 1 */ + values[0] = 1; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.100000000\n"); + + /* positive >= 1 */ + values[0] = 100; + values[1] = 3; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "33.333333333\n"); + + /* negative > -1 */ + values[0] = -1; + values[1] = 1000000000; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.000000001\n"); + + /* negative <= -1 */ + values[0] = -200; + values[1] = 3; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-66.666666666\n"); + + /* Zero */ + values[0] = 0; + values[1] = -10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000000\n"); +} + +static void iio_test_iio_format_value_fractional_log2(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int values[2]; + int ret; + + /* positive < 1 */ + values[0] = 123; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.120117187\n"); + + /* positive >= 1 */ + values[0] = 1234567; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1205.631835937\n"); + + /* negative > -1 */ + values[0] = -123; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-0.120117187\n"); + + /* negative <= -1 */ + values[0] = -1234567; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "-1205.631835937\n"); + + /* Zero */ + values[0] = 0; + values[1] = 10; + ret = iio_format_value(buf, IIO_VAL_FRACTIONAL_LOG2, 2, values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "0.000000000\n"); +} + +static void iio_test_iio_format_value_multiple(struct kunit *test) +{ + char *buf = kunit_kmalloc(test, PAGE_SIZE, GFP_KERNEL); + int values[] = {1, -2, 3, -4, 5}; + int ret; + + ret = iio_format_value(buf, IIO_VAL_INT_MULTIPLE, + ARRAY_SIZE(values), values); + IIO_TEST_FORMAT_EXPECT_EQ(test, buf, ret, "1 -2 3 -4 5 \n"); +} + +static struct kunit_case iio_format_test_cases[] = { + KUNIT_CASE(iio_test_iio_format_value_integer), + KUNIT_CASE(iio_test_iio_format_value_fixedpoint), + KUNIT_CASE(iio_test_iio_format_value_fractional), + KUNIT_CASE(iio_test_iio_format_value_fractional_log2), + KUNIT_CASE(iio_test_iio_format_value_multiple), + {} +}; + +static struct kunit_suite iio_format_test_suite = { + .name = "iio-format", + .test_cases = iio_format_test_cases, +}; +kunit_test_suite(iio_format_test_suite); diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c index 410de837d041..716c795d08fb 100644 --- a/drivers/iio/trigger/iio-trig-hrtimer.c +++ b/drivers/iio/trigger/iio-trig-hrtimer.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0-only -/** +/* * The industrial I/O periodic hrtimer trigger driver * * Copyright (C) Intuitive Aerial AB @@ -16,13 +16,16 @@ #include <linux/iio/trigger.h> #include <linux/iio/sw_trigger.h> +/* Defined locally, not in time64.h yet. */ +#define PSEC_PER_SEC 1000000000000LL + /* default sampling frequency - 100Hz */ #define HRTIMER_DEFAULT_SAMPLING_FREQUENCY 100 struct iio_hrtimer_info { struct iio_sw_trigger swt; struct hrtimer timer; - unsigned long sampling_frequency; + int sampling_frequency[2]; ktime_t period; }; @@ -38,7 +41,9 @@ ssize_t iio_hrtimer_show_sampling_frequency(struct device *dev, struct iio_trigger *trig = to_iio_trigger(dev); struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig); - return snprintf(buf, PAGE_SIZE, "%lu\n", info->sampling_frequency); + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, + ARRAY_SIZE(info->sampling_frequency), + info->sampling_frequency); } static @@ -48,18 +53,26 @@ ssize_t iio_hrtimer_store_sampling_frequency(struct device *dev, { struct iio_trigger *trig = to_iio_trigger(dev); struct iio_hrtimer_info *info = iio_trigger_get_drvdata(trig); - unsigned long val; - int ret; + unsigned long long val; + u64 period; + int integer, fract, ret; - ret = kstrtoul(buf, 10, &val); + ret = iio_str_to_fixpoint(buf, 100, &integer, &fract); if (ret) return ret; + if (integer < 0 || fract < 0) + return -ERANGE; + + val = fract + 1000ULL * integer; /* mHz */ - if (!val || val > NSEC_PER_SEC) + if (!val || val > UINT_MAX) return -EINVAL; - info->sampling_frequency = val; - info->period = NSEC_PER_SEC / val; + info->sampling_frequency[0] = integer; /* Hz */ + info->sampling_frequency[1] = fract * 1000; /* uHz */ + period = PSEC_PER_SEC; + do_div(period, val); + info->period = period; /* nS */ return len; } @@ -122,7 +135,7 @@ static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name) if (!trig_info) return ERR_PTR(-ENOMEM); - trig_info->swt.trigger = iio_trigger_alloc("%s", name); + trig_info->swt.trigger = iio_trigger_alloc(NULL, "%s", name); if (!trig_info->swt.trigger) { ret = -ENOMEM; goto err_free_trig_info; @@ -135,8 +148,8 @@ static struct iio_sw_trigger *iio_trig_hrtimer_probe(const char *name) hrtimer_init(&trig_info->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); trig_info->timer.function = iio_hrtimer_trig_handler; - trig_info->sampling_frequency = HRTIMER_DEFAULT_SAMPLING_FREQUENCY; - trig_info->period = NSEC_PER_SEC / trig_info->sampling_frequency; + trig_info->sampling_frequency[0] = HRTIMER_DEFAULT_SAMPLING_FREQUENCY; + trig_info->period = NSEC_PER_SEC / trig_info->sampling_frequency[0]; ret = iio_trigger_register(trig_info->swt.trigger); if (ret) diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c index 94a487caf421..f746c460bf2a 100644 --- a/drivers/iio/trigger/iio-trig-interrupt.c +++ b/drivers/iio/trigger/iio-trig-interrupt.c @@ -45,7 +45,7 @@ static int iio_interrupt_trigger_probe(struct platform_device *pdev) irq = irq_res->start; - trig = iio_trigger_alloc("irqtrig%d", irq); + trig = iio_trigger_alloc(NULL, "irqtrig%d", irq); if (!trig) { ret = -ENOMEM; goto error_ret; diff --git a/drivers/iio/trigger/iio-trig-loop.c b/drivers/iio/trigger/iio-trig-loop.c index 4a00668e3258..96ec06bbe546 100644 --- a/drivers/iio/trigger/iio-trig-loop.c +++ b/drivers/iio/trigger/iio-trig-loop.c @@ -84,7 +84,7 @@ static struct iio_sw_trigger *iio_trig_loop_probe(const char *name) if (!trig_info) return ERR_PTR(-ENOMEM); - trig_info->swt.trigger = iio_trigger_alloc("%s", name); + trig_info->swt.trigger = iio_trigger_alloc(NULL, "%s", name); if (!trig_info->swt.trigger) { ret = -ENOMEM; goto err_free_trig_info; diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c index 0f6b512a5c37..e9adfff45b39 100644 --- a/drivers/iio/trigger/iio-trig-sysfs.c +++ b/drivers/iio/trigger/iio-trig-sysfs.c @@ -149,7 +149,7 @@ static int iio_sysfs_trigger_probe(int id) goto out1; } t->id = id; - t->trig = iio_trigger_alloc("sysfstrig%d", id); + t->trig = iio_trigger_alloc(&iio_sysfs_trig_dev, "sysfstrig%d", id); if (!t->trig) { ret = -ENOMEM; goto free_t; @@ -157,7 +157,6 @@ static int iio_sysfs_trigger_probe(int id) t->trig->dev.groups = iio_sysfs_trigger_attr_groups; t->trig->ops = &iio_sysfs_trigger_ops; - t->trig->dev.parent = &iio_sysfs_trig_dev; iio_trigger_set_drvdata(t->trig, t); t->work = IRQ_WORK_INIT_HARD(iio_sysfs_trigger_work); |