diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:17:52 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-08-07 13:17:52 +0000 |
commit | 3afb00d3f86d3d924f88b56fa8285d4e9db85852 (patch) | |
tree | 95a985d3019522cea546b7d8df621369bc44fc6c /drivers/iio | |
parent | Adding debian version 6.9.12-1. (diff) | |
download | linux-3afb00d3f86d3d924f88b56fa8285d4e9db85852.tar.xz linux-3afb00d3f86d3d924f88b56fa8285d4e9db85852.zip |
Merging upstream version 6.10.3.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'drivers/iio')
96 files changed, 7160 insertions, 1587 deletions
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile index 0ba0e1521b..cb80ef837e 100644 --- a/drivers/iio/Makefile +++ b/drivers/iio/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_IIO) += industrialio.o industrialio-y := industrialio-core.o industrialio-event.o inkern.o industrialio-$(CONFIG_IIO_BUFFER) += industrialio-buffer.o industrialio-$(CONFIG_IIO_TRIGGER) += industrialio-trigger.o +industrialio-$(CONFIG_ACPI) += industrialio-acpi.o obj-$(CONFIG_IIO_CONFIGFS) += industrialio-configfs.o obj-$(CONFIG_IIO_GTS_HELPER) += industrialio-gts-helper.o diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h index 284bd387ce..3d5c8719db 100644 --- a/drivers/iio/accel/adxl345.h +++ b/drivers/iio/accel/adxl345.h @@ -8,6 +8,39 @@ #ifndef _ADXL345_H_ #define _ADXL345_H_ +#define ADXL345_REG_DEVID 0x00 +#define ADXL345_REG_OFSX 0x1E +#define ADXL345_REG_OFSY 0x1F +#define ADXL345_REG_OFSZ 0x20 +#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) +#define ADXL345_REG_BW_RATE 0x2C +#define ADXL345_REG_POWER_CTL 0x2D +#define ADXL345_REG_DATA_FORMAT 0x31 +#define ADXL345_REG_DATAX0 0x32 +#define ADXL345_REG_DATAY0 0x34 +#define ADXL345_REG_DATAZ0 0x36 +#define ADXL345_REG_DATA_AXIS(index) \ + (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) + +#define ADXL345_BW_RATE GENMASK(3, 0) +#define ADXL345_BASE_RATE_NANO_HZ 97656250LL + +#define ADXL345_POWER_CTL_MEASURE BIT(3) +#define ADXL345_POWER_CTL_STANDBY 0x00 + +#define ADXL345_DATA_FORMAT_RANGE GENMASK(1, 0) /* Set the g range */ +#define ADXL345_DATA_FORMAT_JUSTIFY BIT(2) /* Left-justified (MSB) mode */ +#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */ +#define ADXL345_DATA_FORMAT_SPI_3WIRE BIT(6) /* 3-wire SPI mode */ +#define ADXL345_DATA_FORMAT_SELF_TEST BIT(7) /* Enable a self test */ + +#define ADXL345_DATA_FORMAT_2G 0 +#define ADXL345_DATA_FORMAT_4G 1 +#define ADXL345_DATA_FORMAT_8G 2 +#define ADXL345_DATA_FORMAT_16G 3 + +#define ADXL345_DEVID 0xE5 + /* * In full-resolution mode, scale factor is maintained at ~4 mg/LSB * in all g ranges. @@ -28,6 +61,7 @@ struct adxl345_chip_info { int uscale; }; -int adxl345_core_probe(struct device *dev, struct regmap *regmap); +int adxl345_core_probe(struct device *dev, struct regmap *regmap, + int (*setup)(struct device*, struct regmap*)); #endif /* _ADXL345_H_ */ diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c index 8bd30a23ed..006ce66c0a 100644 --- a/drivers/iio/accel/adxl345_core.c +++ b/drivers/iio/accel/adxl345_core.c @@ -17,38 +17,9 @@ #include "adxl345.h" -#define ADXL345_REG_DEVID 0x00 -#define ADXL345_REG_OFSX 0x1e -#define ADXL345_REG_OFSY 0x1f -#define ADXL345_REG_OFSZ 0x20 -#define ADXL345_REG_OFS_AXIS(index) (ADXL345_REG_OFSX + (index)) -#define ADXL345_REG_BW_RATE 0x2C -#define ADXL345_REG_POWER_CTL 0x2D -#define ADXL345_REG_DATA_FORMAT 0x31 -#define ADXL345_REG_DATAX0 0x32 -#define ADXL345_REG_DATAY0 0x34 -#define ADXL345_REG_DATAZ0 0x36 -#define ADXL345_REG_DATA_AXIS(index) \ - (ADXL345_REG_DATAX0 + (index) * sizeof(__le16)) - -#define ADXL345_BW_RATE GENMASK(3, 0) -#define ADXL345_BASE_RATE_NANO_HZ 97656250LL - -#define ADXL345_POWER_CTL_MEASURE BIT(3) -#define ADXL345_POWER_CTL_STANDBY 0x00 - -#define ADXL345_DATA_FORMAT_FULL_RES BIT(3) /* Up to 13-bits resolution */ -#define ADXL345_DATA_FORMAT_2G 0 -#define ADXL345_DATA_FORMAT_4G 1 -#define ADXL345_DATA_FORMAT_8G 2 -#define ADXL345_DATA_FORMAT_16G 3 - -#define ADXL345_DEVID 0xE5 - struct adxl345_data { const struct adxl345_chip_info *info; struct regmap *regmap; - u8 data_range; }; #define ADXL345_CHANNEL(index, axis) { \ @@ -197,44 +168,75 @@ static void adxl345_powerdown(void *regmap) regmap_write(regmap, ADXL345_REG_POWER_CTL, ADXL345_POWER_CTL_STANDBY); } -int adxl345_core_probe(struct device *dev, struct regmap *regmap) +/** + * adxl345_core_probe() - probe and setup for the adxl345 accelerometer, + * also covers the adlx375 accelerometer + * @dev: Driver model representation of the device + * @regmap: Regmap instance for the device + * @setup: Setup routine to be executed right before the standard device + * setup + * + * Return: 0 on success, negative errno on error + */ +int adxl345_core_probe(struct device *dev, struct regmap *regmap, + int (*setup)(struct device*, struct regmap*)) { struct adxl345_data *data; struct iio_dev *indio_dev; u32 regval; + unsigned int data_format_mask = (ADXL345_DATA_FORMAT_RANGE | + ADXL345_DATA_FORMAT_JUSTIFY | + ADXL345_DATA_FORMAT_FULL_RES | + ADXL345_DATA_FORMAT_SELF_TEST); int ret; - ret = regmap_read(regmap, ADXL345_REG_DEVID, ®val); - if (ret < 0) - return dev_err_probe(dev, ret, "Error reading device ID\n"); - - if (regval != ADXL345_DEVID) - return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n", - regval, ADXL345_DEVID); - indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); if (!indio_dev) return -ENOMEM; data = iio_priv(indio_dev); data->regmap = regmap; - /* Enable full-resolution mode */ - data->data_range = ADXL345_DATA_FORMAT_FULL_RES; data->info = device_get_match_data(dev); if (!data->info) return -ENODEV; - ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT, - data->data_range); - if (ret < 0) - return dev_err_probe(dev, ret, "Failed to set data range\n"); - indio_dev->name = data->info->name; indio_dev->info = &adxl345_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = adxl345_channels; indio_dev->num_channels = ARRAY_SIZE(adxl345_channels); + if (setup) { + /* Perform optional initial bus specific configuration */ + ret = setup(dev, data->regmap); + if (ret) + return ret; + + /* Enable full-resolution mode */ + ret = regmap_update_bits(data->regmap, ADXL345_REG_DATA_FORMAT, + data_format_mask, + ADXL345_DATA_FORMAT_FULL_RES); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set data range\n"); + + } else { + /* Enable full-resolution mode (init all data_format bits) */ + ret = regmap_write(data->regmap, ADXL345_REG_DATA_FORMAT, + ADXL345_DATA_FORMAT_FULL_RES); + if (ret) + return dev_err_probe(dev, ret, + "Failed to set data range\n"); + } + + ret = regmap_read(data->regmap, ADXL345_REG_DEVID, ®val); + if (ret < 0) + return dev_err_probe(dev, ret, "Error reading device ID\n"); + + if (regval != ADXL345_DEVID) + return dev_err_probe(dev, -ENODEV, "Invalid device ID: %x, expected %x\n", + regval, ADXL345_DEVID); + /* Enable measurement mode */ ret = adxl345_powerup(data->regmap); if (ret < 0) diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c index a3084b0a8f..4065b8f7c8 100644 --- a/drivers/iio/accel/adxl345_i2c.c +++ b/drivers/iio/accel/adxl345_i2c.c @@ -27,7 +27,7 @@ static int adxl345_i2c_probe(struct i2c_client *client) if (IS_ERR(regmap)) return dev_err_probe(&client->dev, PTR_ERR(regmap), "Error initializing regmap\n"); - return adxl345_core_probe(&client->dev, regmap); + return adxl345_core_probe(&client->dev, regmap, NULL); } static const struct adxl345_chip_info adxl345_i2c_info = { diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c index 93ca349f17..57e16b4417 100644 --- a/drivers/iio/accel/adxl345_spi.c +++ b/drivers/iio/accel/adxl345_spi.c @@ -20,6 +20,11 @@ static const struct regmap_config adxl345_spi_regmap_config = { .read_flag_mask = BIT(7) | BIT(6), }; +static int adxl345_spi_setup(struct device *dev, struct regmap *regmap) +{ + return regmap_write(regmap, ADXL345_REG_DATA_FORMAT, ADXL345_DATA_FORMAT_SPI_3WIRE); +} + static int adxl345_spi_probe(struct spi_device *spi) { struct regmap *regmap; @@ -33,7 +38,10 @@ static int adxl345_spi_probe(struct spi_device *spi) if (IS_ERR(regmap)) return dev_err_probe(&spi->dev, PTR_ERR(regmap), "Error initializing regmap\n"); - return adxl345_core_probe(&spi->dev, regmap); + if (spi->mode & SPI_3WIRE) + return adxl345_core_probe(&spi->dev, regmap, adxl345_spi_setup); + else + return adxl345_core_probe(&spi->dev, regmap, NULL); } static const struct adxl345_chip_info adxl345_spi_info = { diff --git a/drivers/iio/accel/adxl367.c b/drivers/iio/accel/adxl367.c index 210228affb..5cf4828a5e 100644 --- a/drivers/iio/accel/adxl367.c +++ b/drivers/iio/accel/adxl367.c @@ -621,7 +621,7 @@ static int _adxl367_set_odr(struct adxl367_state *st, enum adxl367_odr odr) static int adxl367_set_odr(struct iio_dev *indio_dev, enum adxl367_odr odr) { iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { - struct adxl367_state *st = iio_priv(indio_dev);; + struct adxl367_state *st = iio_priv(indio_dev); int ret; guard(mutex)(&st->lock); diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 110591804b..ae0cd48a3e 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -386,13 +386,9 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) static bool bmc150_apply_bosc0200_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; - acpi_status status; - int i, j, val[3]; + char *name, *alt_name, *label; if (strcmp(dev_name(dev), "i2c-BOSC0200:base") == 0) { alt_name = "ROMK"; @@ -411,43 +407,7 @@ static bool bmc150_apply_bosc0200_acpi_orientation(struct device *dev, return false; } - status = acpi_evaluate_object(adev->handle, name, NULL, &buffer); - if (ACPI_FAILURE(status)) { - dev_warn(dev, "Failed to get ACPI mount matrix: %d\n", status); - return false; - } - - obj = buffer.pointer; - if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) - goto unknown_format; - - elements = obj->package.elements; - for (i = 0; i < 3; i++) { - if (elements[i].type != ACPI_TYPE_STRING) - goto unknown_format; - - str = elements[i].string.pointer; - if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) - goto unknown_format; - - for (j = 0; j < 3; j++) { - switch (val[j]) { - case -1: str = "-1"; break; - case 0: str = "0"; break; - case 1: str = "1"; break; - default: goto unknown_format; - } - orientation->rotation[i * 3 + j] = str; - } - } - - kfree(buffer.pointer); - return true; - -unknown_format: - dev_warn(dev, "Unknown ACPI mount matrix format, ignoring\n"); - kfree(buffer.pointer); - return false; + return iio_read_acpi_mount_matrix(dev, orientation, name); } static bool bmc150_apply_dual250e_acpi_orientation(struct device *dev, diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c index be8a15cb94..4fbc01bda6 100644 --- a/drivers/iio/accel/fxls8962af-core.c +++ b/drivers/iio/accel/fxls8962af-core.c @@ -15,9 +15,11 @@ #include <linux/bits.h> #include <linux/bitfield.h> #include <linux/i2c.h> +#include <linux/irq.h> #include <linux/module.h> -#include <linux/of_irq.h> +#include <linux/mod_devicetable.h> #include <linux/pm_runtime.h> +#include <linux/property.h> #include <linux/regulator/consumer.h> #include <linux/regmap.h> @@ -1062,12 +1064,12 @@ static void fxls8962af_pm_disable(void *dev_ptr) fxls8962af_standby(iio_priv(indio_dev)); } -static void fxls8962af_get_irq(struct device_node *of_node, +static void fxls8962af_get_irq(struct device *dev, enum fxls8962af_int_pin *pin) { int irq; - irq = of_irq_get_byname(of_node, "INT2"); + irq = fwnode_irq_get_byname(dev_fwnode(dev), "INT2"); if (irq > 0) { *pin = FXLS8962AF_PIN_INT2; return; @@ -1086,7 +1088,7 @@ static int fxls8962af_irq_setup(struct iio_dev *indio_dev, int irq) u8 int_pin_sel; int ret; - fxls8962af_get_irq(dev->of_node, &int_pin); + fxls8962af_get_irq(dev, &int_pin); switch (int_pin) { case FXLS8962AF_PIN_INT1: int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT1; diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index 126e8bdd6d..8280d2bef0 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -636,84 +636,6 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) return 0; } -#ifdef CONFIG_ACPI -static bool kxj_acpi_orientation(struct device *dev, - struct iio_mount_matrix *orientation) -{ - struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_device *adev = ACPI_COMPANION(dev); - char *str; - union acpi_object *obj, *elements; - acpi_status status; - int i, j, val[3]; - bool ret = false; - - if (!acpi_has_method(adev->handle, "ROTM")) - return false; - - status = acpi_evaluate_object(adev->handle, "ROTM", NULL, &buffer); - if (ACPI_FAILURE(status)) { - dev_err(dev, "Failed to get ACPI mount matrix: %d\n", status); - return false; - } - - obj = buffer.pointer; - if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) { - dev_err(dev, "Unknown ACPI mount matrix package format\n"); - goto out_free_buffer; - } - - elements = obj->package.elements; - for (i = 0; i < 3; i++) { - if (elements[i].type != ACPI_TYPE_STRING) { - dev_err(dev, "Unknown ACPI mount matrix element format\n"); - goto out_free_buffer; - } - - str = elements[i].string.pointer; - if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) { - dev_err(dev, "Incorrect ACPI mount matrix string format\n"); - goto out_free_buffer; - } - - for (j = 0; j < 3; j++) { - switch (val[j]) { - case -1: str = "-1"; break; - case 0: str = "0"; break; - case 1: str = "1"; break; - default: - dev_err(dev, "Invalid value in ACPI mount matrix: %d\n", val[j]); - goto out_free_buffer; - } - orientation->rotation[i * 3 + j] = str; - } - } - - ret = true; - -out_free_buffer: - kfree(buffer.pointer); - return ret; -} - -static bool kxj1009_apply_acpi_orientation(struct device *dev, - struct iio_mount_matrix *orientation) -{ - struct acpi_device *adev = ACPI_COMPANION(dev); - - if (adev && acpi_dev_hid_uid_match(adev, "KIOX000A", NULL)) - return kxj_acpi_orientation(dev, orientation); - - return false; -} -#else -static bool kxj1009_apply_acpi_orientation(struct device *dev, - struct iio_mount_matrix *orientation) -{ - return false; -} -#endif - static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) { int ret; @@ -1544,7 +1466,7 @@ static int kxcjk1013_probe(struct i2c_client *client) } else { data->active_high_intr = true; /* default polarity */ - if (!kxj1009_apply_acpi_orientation(&client->dev, &data->orientation)) { + if (!iio_read_acpi_mount_matrix(&client->dev, &data->orientation, "ROTM")) { ret = iio_read_mount_matrix(&client->dev, &data->orientation); if (ret) return ret; diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index d3fd0318e4..62e6369e22 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -19,6 +19,8 @@ */ #include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #include <linux/i2c.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -28,8 +30,6 @@ #include <linux/iio/triggered_buffer.h> #include <linux/iio/events.h> #include <linux/delay.h> -#include <linux/of.h> -#include <linux/of_irq.h> #include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> @@ -1642,7 +1642,7 @@ static int mma8452_probe(struct i2c_client *client) if (client->irq) { int irq2; - irq2 = of_irq_get_byname(client->dev.of_node, "INT2"); + irq2 = fwnode_irq_get_byname(dev_fwnode(&client->dev), "INT2"); if (irq2 == client->irq) { dev_dbg(&client->dev, "using interrupt line INT2\n"); diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index 63c3566a53..e56407b6f2 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -65,6 +65,7 @@ struct mxc4005_data { struct mutex mutex; struct regmap *regmap; struct iio_trigger *dready_trig; + struct iio_mount_matrix orientation; /* Ensure timestamp is naturally aligned */ struct { __be16 chans[3]; @@ -272,6 +273,20 @@ static int mxc4005_write_raw(struct iio_dev *indio_dev, } } +static const struct iio_mount_matrix * +mxc4005_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct mxc4005_data *data = iio_priv(indio_dev); + + return &data->orientation; +} + +static const struct iio_chan_spec_ext_info mxc4005_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, mxc4005_get_mount_matrix), + { } +}; + static const struct iio_info mxc4005_info = { .read_raw = mxc4005_read_raw, .write_raw = mxc4005_write_raw, @@ -298,6 +313,7 @@ static const unsigned long mxc4005_scan_masks[] = { .shift = 4, \ .endianness = IIO_BE, \ }, \ + .ext_info = mxc4005_ext_info, \ } static const struct iio_chan_spec mxc4005_channels[] = { @@ -440,6 +456,12 @@ static int mxc4005_probe(struct i2c_client *client) mutex_init(&data->mutex); + if (!iio_read_acpi_mount_matrix(&client->dev, &data->orientation, "ROTM")) { + ret = iio_read_mount_matrix(&client->dev, &data->orientation); + if (ret) + return ret; + } + indio_dev->channels = mxc4005_channels; indio_dev->num_channels = ARRAY_SIZE(mxc4005_channels); indio_dev->available_scan_masks = mxc4005_scan_masks; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 0d9282fa67..8db68b80b3 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -70,6 +70,23 @@ config AD7124 To compile this driver as a module, choose M here: the module will be called ad7124. +config AD7173 + tristate "Analog Devices AD7173 driver" + depends on SPI_MASTER + select AD_SIGMA_DELTA + select GPIO_REGMAP if GPIOLIB + select REGMAP_SPI if GPIOLIB + help + Say yes here to build support for Analog Devices AD7173 and similar ADC + Currently supported models: + - AD7172-2 + - AD7173-8 + - AD7175-2 + - AD7176-2 + + To compile this driver as a module, choose M here: the module will be + called ad7173. + config AD7192 tristate "Analog Devices AD7190 AD7192 AD7193 AD7195 ADC driver" depends on SPI @@ -264,6 +281,16 @@ config AD7923 To compile this driver as a module, choose M here: the module will be called ad7923. +config AD7944 + tristate "Analog Devices AD7944 and similar ADCs driver" + depends on SPI + help + Say yes here to build support for Analog Devices + AD7944, AD7985, AD7986 ADCs. + + To compile this driver as a module, choose M here: the + module will be called ad7944 + config AD7949 tristate "Analog Devices AD7949 and similar ADCs driver" depends on SPI diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index b3c4347223..edb32ce2af 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_AD7091R) += ad7091r-base.o obj-$(CONFIG_AD7091R5) += ad7091r5.o obj-$(CONFIG_AD7091R8) += ad7091r8.o obj-$(CONFIG_AD7124) += ad7124.o +obj-$(CONFIG_AD7173) += ad7173.o obj-$(CONFIG_AD7192) += ad7192.o obj-$(CONFIG_AD7266) += ad7266.o obj-$(CONFIG_AD7280) += ad7280a.o @@ -28,6 +29,7 @@ obj-$(CONFIG_AD7780) += ad7780.o obj-$(CONFIG_AD7791) += ad7791.o obj-$(CONFIG_AD7793) += ad7793.o obj-$(CONFIG_AD7887) += ad7887.o +obj-$(CONFIG_AD7944) += ad7944.o obj-$(CONFIG_AD7949) += ad7949.o obj-$(CONFIG_AD799X) += ad799x.o obj-$(CONFIG_AD9467) += ad9467.o diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c index 80645fee79..59f66e9cb0 100644 --- a/drivers/iio/adc/ab8500-gpadc.c +++ b/drivers/iio/adc/ab8500-gpadc.c @@ -1021,14 +1021,13 @@ static int ab8500_gpadc_parse_channel(struct device *dev, /** * ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT * @gpadc: the GPADC to configure the channels for - * @chans: the IIO channels we parsed - * @nchans: the number of IIO channels we parsed + * @chans_parsed: the IIO channels we parsed + * @nchans_parsed: the number of IIO channels we parsed */ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, struct iio_chan_spec **chans_parsed, unsigned int *nchans_parsed) { - struct fwnode_handle *child; struct ab8500_gpadc_chan_info *ch; struct iio_chan_spec *iio_chans; unsigned int nchans; @@ -1052,7 +1051,7 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, return -ENOMEM; i = 0; - device_for_each_child_node(gpadc->dev, child) { + device_for_each_child_node_scoped(gpadc->dev, child) { struct iio_chan_spec *iio_chan; int ret; @@ -1062,7 +1061,6 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc, ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch, iio_chan); if (ret) { - fwnode_handle_put(child); return ret; } i++; diff --git a/drivers/iio/adc/ad4130.c b/drivers/iio/adc/ad4130.c index febb64e679..aaf1fb0ac4 100644 --- a/drivers/iio/adc/ad4130.c +++ b/drivers/iio/adc/ad4130.c @@ -1600,17 +1600,14 @@ static int ad4130_parse_fw_children(struct iio_dev *indio_dev) { struct ad4130_state *st = iio_priv(indio_dev); struct device *dev = &st->spi->dev; - struct fwnode_handle *child; int ret; indio_dev->channels = st->chans; - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { ret = ad4130_parse_fw_channel(indio_dev, child); - if (ret) { - fwnode_handle_put(child); + if (ret) return ret; - } } return 0; diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index b9b206fcd7..e7b1d517d3 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -14,7 +14,8 @@ #include <linux/kernel.h> #include <linux/kfifo.h> #include <linux/module.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> @@ -807,22 +808,19 @@ static int ad7124_check_chip_id(struct ad7124_state *st) return 0; } -static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, - struct device_node *np) +static int ad7124_parse_channel_config(struct iio_dev *indio_dev, + struct device *dev) { 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; unsigned int ain[2], channel = 0, tmp; int ret; - st->num_channels = of_get_available_child_count(np); - if (!st->num_channels) { - dev_err(indio_dev->dev.parent, "no channel children\n"); - return -ENODEV; - } + st->num_channels = device_get_child_node_count(dev); + if (!st->num_channels) + return dev_err_probe(dev, -ENODEV, "no channel children\n"); chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels, sizeof(*chan), GFP_KERNEL); @@ -838,39 +836,38 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, indio_dev->num_channels = st->num_channels; st->channels = channels; - for_each_available_child_of_node(np, child) { + device_for_each_child_node_scoped(dev, child) { cfg = &st->channels[channel].cfg; - ret = of_property_read_u32(child, "reg", &channel); + ret = fwnode_property_read_u32(child, "reg", &channel); if (ret) - goto err; + return ret; - if (channel >= indio_dev->num_channels) { - dev_err(indio_dev->dev.parent, + if (channel >= indio_dev->num_channels) + return dev_err_probe(dev, -EINVAL, "Channel index >= number of channels\n"); - ret = -EINVAL; - goto err; - } - ret = of_property_read_u32_array(child, "diff-channels", - ain, 2); + ret = fwnode_property_read_u32_array(child, "diff-channels", + ain, 2); if (ret) - goto err; + return ret; st->channels[channel].nr = channel; st->channels[channel].ain = AD7124_CHANNEL_AINP(ain[0]) | AD7124_CHANNEL_AINM(ain[1]); - cfg->bipolar = of_property_read_bool(child, "bipolar"); + cfg->bipolar = fwnode_property_read_bool(child, "bipolar"); - ret = of_property_read_u32(child, "adi,reference-select", &tmp); + ret = fwnode_property_read_u32(child, "adi,reference-select", &tmp); if (ret) cfg->refsel = AD7124_INT_REF; else cfg->refsel = tmp; - cfg->buf_positive = of_property_read_bool(child, "adi,buffered-positive"); - cfg->buf_negative = of_property_read_bool(child, "adi,buffered-negative"); + cfg->buf_positive = + fwnode_property_read_bool(child, "adi,buffered-positive"); + cfg->buf_negative = + fwnode_property_read_bool(child, "adi,buffered-negative"); chan[channel] = ad7124_channel_template; chan[channel].address = channel; @@ -880,10 +877,6 @@ static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev, } return 0; -err: - of_node_put(child); - - return ret; } static int ad7124_setup(struct ad7124_state *st) @@ -943,9 +936,7 @@ static int ad7124_probe(struct spi_device *spi) struct iio_dev *indio_dev; int i, ret; - info = of_device_get_match_data(&spi->dev); - if (!info) - info = (void *)spi_get_device_id(spi)->driver_data; + info = spi_get_device_match_data(spi); if (!info) return -ENODEV; @@ -965,7 +956,7 @@ static int ad7124_probe(struct spi_device *spi) if (ret < 0) return ret; - ret = ad7124_of_parse_channel_config(indio_dev, spi->dev.of_node); + ret = ad7124_parse_channel_config(indio_dev, &spi->dev); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c new file mode 100644 index 0000000000..b26d4575e2 --- /dev/null +++ b/drivers/iio/adc/ad7173.c @@ -0,0 +1,1181 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * AD717x family SPI ADC driver + * + * Supported devices: + * AD7172-2/AD7172-4/AD7173-8/AD7175-2 + * AD7175-8/AD7176-2/AD7177-2 + * + * Copyright (C) 2015, 2024 Analog Devices, Inc. + */ + +#include <linux/array_size.h> +#include <linux/bitfield.h> +#include <linux/bitmap.h> +#include <linux/container_of.h> +#include <linux/clk.h> +#include <linux/clk-provider.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/driver.h> +#include <linux/gpio/regmap.h> +#include <linux/idr.h> +#include <linux/interrupt.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#include <linux/iio/adc/ad_sigma_delta.h> + +#define AD7173_REG_COMMS 0x00 +#define AD7173_REG_ADC_MODE 0x01 +#define AD7173_REG_INTERFACE_MODE 0x02 +#define AD7173_REG_CRC 0x03 +#define AD7173_REG_DATA 0x04 +#define AD7173_REG_GPIO 0x06 +#define AD7173_REG_ID 0x07 +#define AD7173_REG_CH(x) (0x10 + (x)) +#define AD7173_REG_SETUP(x) (0x20 + (x)) +#define AD7173_REG_FILTER(x) (0x28 + (x)) +#define AD7173_REG_OFFSET(x) (0x30 + (x)) +#define AD7173_REG_GAIN(x) (0x38 + (x)) + +#define AD7173_RESET_LENGTH BITS_TO_BYTES(64) + +#define AD7173_CH_ENABLE BIT(15) +#define AD7173_CH_SETUP_SEL_MASK GENMASK(14, 12) +#define AD7173_CH_SETUP_AINPOS_MASK GENMASK(9, 5) +#define AD7173_CH_SETUP_AINNEG_MASK GENMASK(4, 0) + +#define AD7173_CH_ADDRESS(pos, neg) \ + (FIELD_PREP(AD7173_CH_SETUP_AINPOS_MASK, pos) | \ + FIELD_PREP(AD7173_CH_SETUP_AINNEG_MASK, neg)) +#define AD7173_AIN_TEMP_POS 17 +#define AD7173_AIN_TEMP_NEG 18 + +#define AD7172_2_ID 0x00d0 +#define AD7175_ID 0x0cd0 +#define AD7176_ID 0x0c90 +#define AD7175_2_ID 0x0cd0 +#define AD7172_4_ID 0x2050 +#define AD7173_ID 0x30d0 +#define AD7175_8_ID 0x3cd0 +#define AD7177_ID 0x4fd0 +#define AD7173_ID_MASK GENMASK(15, 4) + +#define AD7173_ADC_MODE_REF_EN BIT(15) +#define AD7173_ADC_MODE_SING_CYC BIT(13) +#define AD7173_ADC_MODE_MODE_MASK GENMASK(6, 4) +#define AD7173_ADC_MODE_CLOCKSEL_MASK GENMASK(3, 2) +#define AD7173_ADC_MODE_CLOCKSEL_INT 0x0 +#define AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT 0x1 +#define AD7173_ADC_MODE_CLOCKSEL_EXT 0x2 +#define AD7173_ADC_MODE_CLOCKSEL_XTAL 0x3 + +#define AD7173_GPIO_PDSW BIT(14) +#define AD7173_GPIO_OP_EN2_3 BIT(13) +#define AD7173_GPIO_MUX_IO BIT(12) +#define AD7173_GPIO_SYNC_EN BIT(11) +#define AD7173_GPIO_ERR_EN BIT(10) +#define AD7173_GPIO_ERR_DAT BIT(9) +#define AD7173_GPIO_GP_DATA3 BIT(7) +#define AD7173_GPIO_GP_DATA2 BIT(6) +#define AD7173_GPIO_IP_EN1 BIT(5) +#define AD7173_GPIO_IP_EN0 BIT(4) +#define AD7173_GPIO_OP_EN1 BIT(3) +#define AD7173_GPIO_OP_EN0 BIT(2) +#define AD7173_GPIO_GP_DATA1 BIT(1) +#define AD7173_GPIO_GP_DATA0 BIT(0) + +#define AD7173_GPO12_DATA(x) BIT((x) + 0) +#define AD7173_GPO23_DATA(x) BIT((x) + 4) +#define AD7173_GPO_DATA(x) ((x) < 2 ? AD7173_GPO12_DATA(x) : AD7173_GPO23_DATA(x)) + +#define AD7173_INTERFACE_DATA_STAT BIT(6) +#define AD7173_INTERFACE_DATA_STAT_EN(x) \ + FIELD_PREP(AD7173_INTERFACE_DATA_STAT, x) + +#define AD7173_SETUP_BIPOLAR BIT(12) +#define AD7173_SETUP_AREF_BUF_MASK GENMASK(11, 10) +#define AD7173_SETUP_AIN_BUF_MASK GENMASK(9, 8) + +#define AD7173_SETUP_REF_SEL_MASK GENMASK(5, 4) +#define AD7173_SETUP_REF_SEL_AVDD1_AVSS 0x3 +#define AD7173_SETUP_REF_SEL_INT_REF 0x2 +#define AD7173_SETUP_REF_SEL_EXT_REF2 0x1 +#define AD7173_SETUP_REF_SEL_EXT_REF 0x0 +#define AD7173_VOLTAGE_INT_REF_uV 2500000 +#define AD7173_TEMP_SENSIIVITY_uV_per_C 477 +#define AD7177_ODR_START_VALUE 0x07 + +#define AD7173_FILTER_ODR0_MASK GENMASK(5, 0) +#define AD7173_MAX_CONFIGS 8 + +enum ad7173_ids { + ID_AD7172_2, + ID_AD7172_4, + ID_AD7173_8, + ID_AD7175_2, + ID_AD7175_8, + ID_AD7176_2, + ID_AD7177_2, +}; + +struct ad7173_device_info { + const unsigned int *sinc5_data_rates; + unsigned int num_sinc5_data_rates; + unsigned int odr_start_value; + unsigned int num_channels; + unsigned int num_configs; + unsigned int num_inputs; + unsigned int clock; + unsigned int id; + char *name; + bool has_temp; + bool has_input_buf; + bool has_int_ref; + bool has_ref2; + u8 num_gpios; +}; + +struct ad7173_channel_config { + u8 cfg_slot; + bool live; + + /* Following fields are used to compare equality. */ + struct_group(config_props, + bool bipolar; + bool input_buf; + u8 odr; + u8 ref_sel; + ); +}; + +struct ad7173_channel { + unsigned int chan_reg; + unsigned int ain; + struct ad7173_channel_config cfg; +}; + +struct ad7173_state { + struct ad_sigma_delta sd; + const struct ad7173_device_info *info; + struct ad7173_channel *channels; + struct regulator_bulk_data regulators[3]; + unsigned int adc_mode; + unsigned int interface_mode; + unsigned int num_channels; + struct ida cfg_slots_status; + unsigned long long config_usage_counter; + unsigned long long *config_cnts; + struct clk *ext_clk; + struct clk_hw int_clk_hw; +#if IS_ENABLED(CONFIG_GPIOLIB) + struct regmap *reg_gpiocon_regmap; + struct gpio_regmap *gpio_regmap; +#endif +}; + +static const unsigned int ad7173_sinc5_data_rates[] = { + 6211000, 6211000, 6211000, 6211000, 6211000, 6211000, 5181000, 4444000, /* 0-7 */ + 3115000, 2597000, 1007000, 503800, 381000, 200300, 100500, 59520, /* 8-15 */ + 49680, 20010, 16333, 10000, 5000, 2500, 1250, /* 16-22 */ +}; + +static const unsigned int ad7175_sinc5_data_rates[] = { + 50000000, 41667000, 31250000, 27778000, /* 0-3 */ + 20833000, 17857000, 12500000, 10000000, /* 4-7 */ + 5000000, 2500000, 1000000, 500000, /* 8-11 */ + 397500, 200000, 100000, 59920, /* 12-15 */ + 49960, 20000, 16666, 10000, /* 16-19 */ + 5000, /* 20 */ +}; + +static const struct ad7173_device_info ad7173_device_info[] = { + [ID_AD7172_2] = { + .name = "ad7172-2", + .id = AD7172_2_ID, + .num_inputs = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), + }, + [ID_AD7172_4] = { + .name = "ad7172-4", + .id = AD7172_4_ID, + .num_inputs = 9, + .num_channels = 8, + .num_configs = 8, + .num_gpios = 4, + .has_temp = false, + .has_input_buf = true, + .has_ref2 = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), + }, + [ID_AD7173_8] = { + .name = "ad7173-8", + .id = AD7173_ID, + .num_inputs = 17, + .num_channels = 16, + .num_configs = 8, + .num_gpios = 4, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_ref2 = true, + .clock = 2 * HZ_PER_MHZ, + .sinc5_data_rates = ad7173_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7173_sinc5_data_rates), + }, + [ID_AD7175_2] = { + .name = "ad7175-2", + .id = AD7175_2_ID, + .num_inputs = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), + }, + [ID_AD7175_8] = { + .name = "ad7175-8", + .id = AD7175_8_ID, + .num_inputs = 17, + .num_channels = 16, + .num_configs = 8, + .num_gpios = 4, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .has_ref2 = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), + }, + [ID_AD7176_2] = { + .name = "ad7176-2", + .id = AD7176_ID, + .num_inputs = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = false, + .has_input_buf = false, + .has_int_ref = true, + .clock = 16 * HZ_PER_MHZ, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), + }, + [ID_AD7177_2] = { + .name = "ad7177-2", + .id = AD7177_ID, + .num_inputs = 5, + .num_channels = 4, + .num_configs = 4, + .num_gpios = 2, + .has_temp = true, + .has_input_buf = true, + .has_int_ref = true, + .clock = 16 * HZ_PER_MHZ, + .odr_start_value = AD7177_ODR_START_VALUE, + .sinc5_data_rates = ad7175_sinc5_data_rates, + .num_sinc5_data_rates = ARRAY_SIZE(ad7175_sinc5_data_rates), + }, +}; + +static const char *const ad7173_ref_sel_str[] = { + [AD7173_SETUP_REF_SEL_EXT_REF] = "vref", + [AD7173_SETUP_REF_SEL_EXT_REF2] = "vref2", + [AD7173_SETUP_REF_SEL_INT_REF] = "refout-avss", + [AD7173_SETUP_REF_SEL_AVDD1_AVSS] = "avdd", +}; + +static const char *const ad7173_clk_sel[] = { + "ext-clk", "xtal" +}; + +#if IS_ENABLED(CONFIG_GPIOLIB) + +static const struct regmap_range ad7173_range_gpio[] = { + regmap_reg_range(AD7173_REG_GPIO, AD7173_REG_GPIO), +}; + +static const struct regmap_access_table ad7173_access_table = { + .yes_ranges = ad7173_range_gpio, + .n_yes_ranges = ARRAY_SIZE(ad7173_range_gpio), +}; + +static const struct regmap_config ad7173_regmap_config = { + .reg_bits = 8, + .val_bits = 16, + .rd_table = &ad7173_access_table, + .wr_table = &ad7173_access_table, + .read_flag_mask = BIT(6), +}; + +static int ad7173_mask_xlate(struct gpio_regmap *gpio, unsigned int base, + unsigned int offset, unsigned int *reg, + unsigned int *mask) +{ + *mask = AD7173_GPO_DATA(offset); + *reg = base; + return 0; +} + +static void ad7173_gpio_disable(void *data) +{ + struct ad7173_state *st = data; + unsigned int mask; + + mask = AD7173_GPIO_OP_EN0 | AD7173_GPIO_OP_EN1 | AD7173_GPIO_OP_EN2_3; + regmap_update_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, mask, ~mask); +} + +static int ad7173_gpio_init(struct ad7173_state *st) +{ + struct gpio_regmap_config gpio_regmap = {}; + struct device *dev = &st->sd.spi->dev; + unsigned int mask; + int ret; + + st->reg_gpiocon_regmap = devm_regmap_init_spi(st->sd.spi, &ad7173_regmap_config); + ret = PTR_ERR_OR_ZERO(st->reg_gpiocon_regmap); + if (ret) + return dev_err_probe(dev, ret, "Unable to init regmap\n"); + + mask = AD7173_GPIO_OP_EN0 | AD7173_GPIO_OP_EN1 | AD7173_GPIO_OP_EN2_3; + regmap_update_bits(st->reg_gpiocon_regmap, AD7173_REG_GPIO, mask, mask); + + ret = devm_add_action_or_reset(dev, ad7173_gpio_disable, st); + if (ret) + return ret; + + gpio_regmap.parent = dev; + gpio_regmap.regmap = st->reg_gpiocon_regmap; + gpio_regmap.ngpio = st->info->num_gpios; + gpio_regmap.reg_set_base = AD7173_REG_GPIO; + gpio_regmap.reg_mask_xlate = ad7173_mask_xlate; + + st->gpio_regmap = devm_gpio_regmap_register(dev, &gpio_regmap); + ret = PTR_ERR_OR_ZERO(st->gpio_regmap); + if (ret) + return dev_err_probe(dev, ret, "Unable to init gpio-regmap\n"); + + return 0; +} +#else +static int ad7173_gpio_init(struct ad7173_state *st) +{ + return 0; +} +#endif /* CONFIG_GPIOLIB */ + +static struct ad7173_state *ad_sigma_delta_to_ad7173(struct ad_sigma_delta *sd) +{ + return container_of(sd, struct ad7173_state, sd); +} + +static struct ad7173_state *clk_hw_to_ad7173(struct clk_hw *hw) +{ + return container_of(hw, struct ad7173_state, int_clk_hw); +} + +static void ad7173_ida_destroy(void *data) +{ + struct ad7173_state *st = data; + + ida_destroy(&st->cfg_slots_status); +} + +static void ad7173_reset_usage_cnts(struct ad7173_state *st) +{ + memset64(st->config_cnts, 0, st->info->num_configs); + st->config_usage_counter = 0; +} + +static struct ad7173_channel_config * +ad7173_find_live_config(struct ad7173_state *st, struct ad7173_channel_config *cfg) +{ + struct ad7173_channel_config *cfg_aux; + ptrdiff_t cmp_size; + int i; + + cmp_size = sizeof_field(struct ad7173_channel_config, config_props); + for (i = 0; i < st->num_channels; i++) { + cfg_aux = &st->channels[i].cfg; + + if (cfg_aux->live && + !memcmp(&cfg->config_props, &cfg_aux->config_props, cmp_size)) + return cfg_aux; + } + return NULL; +} + +/* Could be replaced with a generic LRU implementation */ +static int ad7173_free_config_slot_lru(struct ad7173_state *st) +{ + int i, lru_position = 0; + + for (i = 1; i < st->info->num_configs; i++) + if (st->config_cnts[i] < st->config_cnts[lru_position]) + lru_position = i; + + for (i = 0; i < st->num_channels; i++) + if (st->channels[i].cfg.cfg_slot == lru_position) + st->channels[i].cfg.live = false; + + ida_free(&st->cfg_slots_status, lru_position); + return ida_alloc(&st->cfg_slots_status, GFP_KERNEL); +} + +/* Could be replaced with a generic LRU implementation */ +static int ad7173_load_config(struct ad7173_state *st, + struct ad7173_channel_config *cfg) +{ + unsigned int config; + int free_cfg_slot, ret; + + free_cfg_slot = ida_alloc_range(&st->cfg_slots_status, 0, + st->info->num_configs - 1, GFP_KERNEL); + if (free_cfg_slot < 0) + free_cfg_slot = ad7173_free_config_slot_lru(st); + + cfg->cfg_slot = free_cfg_slot; + config = FIELD_PREP(AD7173_SETUP_REF_SEL_MASK, cfg->ref_sel); + + if (cfg->bipolar) + config |= AD7173_SETUP_BIPOLAR; + + if (cfg->input_buf) + config |= AD7173_SETUP_AIN_BUF_MASK; + + ret = ad_sd_write_reg(&st->sd, AD7173_REG_SETUP(free_cfg_slot), 2, config); + if (ret) + return ret; + + return ad_sd_write_reg(&st->sd, AD7173_REG_FILTER(free_cfg_slot), 2, + AD7173_FILTER_ODR0_MASK & cfg->odr); +} + +static int ad7173_config_channel(struct ad7173_state *st, int addr) +{ + struct ad7173_channel_config *cfg = &st->channels[addr].cfg; + struct ad7173_channel_config *live_cfg; + int ret; + + if (!cfg->live) { + live_cfg = ad7173_find_live_config(st, cfg); + if (live_cfg) { + cfg->cfg_slot = live_cfg->cfg_slot; + } else { + ret = ad7173_load_config(st, cfg); + if (ret) + return ret; + cfg->live = true; + } + } + + if (st->config_usage_counter == U64_MAX) + ad7173_reset_usage_cnts(st); + + st->config_usage_counter++; + st->config_cnts[cfg->cfg_slot] = st->config_usage_counter; + + return 0; +} + +static int ad7173_set_channel(struct ad_sigma_delta *sd, unsigned int channel) +{ + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + unsigned int val; + int ret; + + ret = ad7173_config_channel(st, channel); + if (ret) + return ret; + + val = AD7173_CH_ENABLE | + FIELD_PREP(AD7173_CH_SETUP_SEL_MASK, st->channels[channel].cfg.cfg_slot) | + st->channels[channel].ain; + + return ad_sd_write_reg(&st->sd, AD7173_REG_CH(channel), 2, val); +} + +static int ad7173_set_mode(struct ad_sigma_delta *sd, + enum ad_sigma_delta_mode mode) +{ + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + + st->adc_mode &= ~AD7173_ADC_MODE_MODE_MASK; + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_MODE_MASK, mode); + + return ad_sd_write_reg(&st->sd, AD7173_REG_ADC_MODE, 2, st->adc_mode); +} + +static int ad7173_append_status(struct ad_sigma_delta *sd, bool append) +{ + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + unsigned int interface_mode = st->interface_mode; + int ret; + + interface_mode &= ~AD7173_INTERFACE_DATA_STAT; + interface_mode |= AD7173_INTERFACE_DATA_STAT_EN(append); + ret = ad_sd_write_reg(&st->sd, AD7173_REG_INTERFACE_MODE, 2, interface_mode); + if (ret) + return ret; + + st->interface_mode = interface_mode; + + return 0; +} + +static int ad7173_disable_all(struct ad_sigma_delta *sd) +{ + struct ad7173_state *st = ad_sigma_delta_to_ad7173(sd); + int ret; + int i; + + for (i = 0; i < st->num_channels; i++) { + ret = ad_sd_write_reg(sd, AD7173_REG_CH(i), 2, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static struct ad_sigma_delta_info ad7173_sigma_delta_info = { + .set_channel = ad7173_set_channel, + .append_status = ad7173_append_status, + .disable_all = ad7173_disable_all, + .set_mode = ad7173_set_mode, + .has_registers = true, + .addr_shift = 0, + .read_mask = BIT(6), + .status_ch_mask = GENMASK(3, 0), + .data_reg = AD7173_REG_DATA, +}; + +static int ad7173_setup(struct iio_dev *indio_dev) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct device *dev = &st->sd.spi->dev; + u8 buf[AD7173_RESET_LENGTH]; + unsigned int id; + int ret; + + /* reset the serial interface */ + memset(buf, 0xff, AD7173_RESET_LENGTH); + ret = spi_write_then_read(st->sd.spi, buf, sizeof(buf), NULL, 0); + if (ret < 0) + return ret; + + /* datasheet recommends a delay of at least 500us after reset */ + fsleep(500); + + ret = ad_sd_read_reg(&st->sd, AD7173_REG_ID, 2, &id); + if (ret) + return ret; + + id &= AD7173_ID_MASK; + if (id != st->info->id) + dev_warn(dev, "Unexpected device id: 0x%04X, expected: 0x%04X\n", + id, st->info->id); + + st->adc_mode |= AD7173_ADC_MODE_SING_CYC; + st->interface_mode = 0x0; + + st->config_usage_counter = 0; + st->config_cnts = devm_kcalloc(dev, st->info->num_configs, + sizeof(*st->config_cnts), GFP_KERNEL); + if (!st->config_cnts) + return -ENOMEM; + + /* All channels are enabled by default after a reset */ + return ad7173_disable_all(&st->sd); +} + +static unsigned int ad7173_get_ref_voltage_milli(struct ad7173_state *st, + u8 reference_select) +{ + int vref; + + switch (reference_select) { + case AD7173_SETUP_REF_SEL_EXT_REF: + vref = regulator_get_voltage(st->regulators[0].consumer); + break; + + case AD7173_SETUP_REF_SEL_EXT_REF2: + vref = regulator_get_voltage(st->regulators[1].consumer); + break; + + case AD7173_SETUP_REF_SEL_INT_REF: + vref = AD7173_VOLTAGE_INT_REF_uV; + break; + + case AD7173_SETUP_REF_SEL_AVDD1_AVSS: + vref = regulator_get_voltage(st->regulators[2].consumer); + break; + + default: + return -EINVAL; + } + + if (vref < 0) + return vref; + + return vref / (MICRO / MILLI); +} + +static int ad7173_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long info) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel *ch = &st->channels[chan->address]; + unsigned int reg; + u64 temp; + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = ad_sigma_delta_single_conversion(indio_dev, chan, val); + if (ret < 0) + return ret; + + /* disable channel after single conversion */ + ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(chan->address), 2, 0); + if (ret < 0) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + temp = AD7173_VOLTAGE_INT_REF_uV * MILLI; + temp /= AD7173_TEMP_SENSIIVITY_uV_per_C; + *val = temp; + *val2 = chan->scan_type.realbits; + } else { + *val = ad7173_get_ref_voltage_milli(st, ch->cfg.ref_sel); + *val2 = chan->scan_type.realbits - !!(ch->cfg.bipolar); + } + return IIO_VAL_FRACTIONAL_LOG2; + case IIO_CHAN_INFO_OFFSET: + if (chan->type == IIO_TEMP) { + /* 0 Kelvin -> raw sample */ + temp = -ABSOLUTE_ZERO_MILLICELSIUS; + temp *= AD7173_TEMP_SENSIIVITY_uV_per_C; + temp <<= chan->scan_type.realbits; + temp = DIV_U64_ROUND_CLOSEST(temp, + AD7173_VOLTAGE_INT_REF_uV * + MILLI); + *val = -temp; + } else { + *val = -BIT(chan->scan_type.realbits - 1); + } + return IIO_VAL_INT; + case IIO_CHAN_INFO_SAMP_FREQ: + reg = st->channels[chan->address].cfg.odr; + + *val = st->info->sinc5_data_rates[reg] / MILLI; + *val2 = (st->info->sinc5_data_rates[reg] % MILLI) * (MICRO / MILLI); + + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int ad7173_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long info) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct ad7173_channel_config *cfg; + unsigned int freq, i; + int ret; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + switch (info) { + case IIO_CHAN_INFO_SAMP_FREQ: + freq = val * MILLI + val2 / MILLI; + for (i = st->info->odr_start_value; i < st->info->num_sinc5_data_rates - 1; i++) + if (freq >= st->info->sinc5_data_rates[i]) + break; + + cfg = &st->channels[chan->address].cfg; + cfg->odr = i; + cfg->live = false; + break; + + default: + ret = -EINVAL; + break; + } + + iio_device_release_direct_mode(indio_dev); + return ret; +} + +static int ad7173_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *scan_mask) +{ + struct ad7173_state *st = iio_priv(indio_dev); + int i, ret; + + for (i = 0; i < indio_dev->num_channels; i++) { + if (test_bit(i, scan_mask)) + ret = ad7173_set_channel(&st->sd, i); + else + ret = ad_sd_write_reg(&st->sd, AD7173_REG_CH(i), 2, 0); + if (ret < 0) + return ret; + } + + return 0; +} + +static int ad7173_debug_reg_access(struct iio_dev *indio_dev, unsigned int reg, + unsigned int writeval, unsigned int *readval) +{ + struct ad7173_state *st = iio_priv(indio_dev); + u8 reg_size; + + if (reg == AD7173_REG_COMMS) + reg_size = 1; + else if (reg == AD7173_REG_CRC || reg == AD7173_REG_DATA || + reg >= AD7173_REG_OFFSET(0)) + reg_size = 3; + else + reg_size = 2; + + if (readval) + return ad_sd_read_reg(&st->sd, reg, reg_size, readval); + + return ad_sd_write_reg(&st->sd, reg, reg_size, writeval); +} + +static const struct iio_info ad7173_info = { + .read_raw = &ad7173_read_raw, + .write_raw = &ad7173_write_raw, + .debugfs_reg_access = &ad7173_debug_reg_access, + .validate_trigger = ad_sd_validate_trigger, + .update_scan_mode = ad7173_update_scan_mode, +}; + +static const struct iio_chan_spec ad7173_channel_template = { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, +}; + +static const struct iio_chan_spec ad7173_temp_iio_channel_template = { + .type = IIO_TEMP, + .channel = AD7173_AIN_TEMP_POS, + .channel2 = AD7173_AIN_TEMP_NEG, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_OFFSET) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .scan_type = { + .sign = 'u', + .realbits = 24, + .storagebits = 32, + .endianness = IIO_BE, + }, +}; + +static void ad7173_disable_regulators(void *data) +{ + struct ad7173_state *st = data; + + regulator_bulk_disable(ARRAY_SIZE(st->regulators), st->regulators); +} + +static void ad7173_clk_disable_unprepare(void *clk) +{ + clk_disable_unprepare(clk); +} + +static unsigned long ad7173_sel_clk(struct ad7173_state *st, + unsigned int clk_sel) +{ + int ret; + + st->adc_mode &= ~AD7173_ADC_MODE_CLOCKSEL_MASK; + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, clk_sel); + ret = ad_sd_write_reg(&st->sd, AD7173_REG_ADC_MODE, 0x2, st->adc_mode); + + return ret; +} + +static unsigned long ad7173_clk_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct ad7173_state *st = clk_hw_to_ad7173(hw); + + return st->info->clock / HZ_PER_KHZ; +} + +static int ad7173_clk_output_is_enabled(struct clk_hw *hw) +{ + struct ad7173_state *st = clk_hw_to_ad7173(hw); + u32 clk_sel; + + clk_sel = FIELD_GET(AD7173_ADC_MODE_CLOCKSEL_MASK, st->adc_mode); + return clk_sel == AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT; +} + +static int ad7173_clk_output_prepare(struct clk_hw *hw) +{ + struct ad7173_state *st = clk_hw_to_ad7173(hw); + + return ad7173_sel_clk(st, AD7173_ADC_MODE_CLOCKSEL_INT_OUTPUT); +} + +static void ad7173_clk_output_unprepare(struct clk_hw *hw) +{ + struct ad7173_state *st = clk_hw_to_ad7173(hw); + + ad7173_sel_clk(st, AD7173_ADC_MODE_CLOCKSEL_INT); +} + +static const struct clk_ops ad7173_int_clk_ops = { + .recalc_rate = ad7173_clk_recalc_rate, + .is_enabled = ad7173_clk_output_is_enabled, + .prepare = ad7173_clk_output_prepare, + .unprepare = ad7173_clk_output_unprepare, +}; + +static int ad7173_register_clk_provider(struct iio_dev *indio_dev) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct fwnode_handle *fwnode = dev_fwnode(dev); + struct clk_init_data init = {}; + int ret; + + if (!IS_ENABLED(CONFIG_COMMON_CLK)) + return 0; + + init.name = fwnode_get_name(fwnode); + init.ops = &ad7173_int_clk_ops; + + st->int_clk_hw.init = &init; + ret = devm_clk_hw_register(dev, &st->int_clk_hw); + if (ret) + return ret; + + return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, + &st->int_clk_hw); +} + +static int ad7173_fw_parse_channel_config(struct iio_dev *indio_dev) +{ + struct ad7173_channel *chans_st_arr, *chan_st_priv; + struct ad7173_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + struct iio_chan_spec *chan_arr, *chan; + unsigned int ain[2], chan_index = 0; + int ref_sel, ret; + + chan_arr = devm_kcalloc(dev, sizeof(*indio_dev->channels), + st->num_channels, GFP_KERNEL); + if (!chan_arr) + return -ENOMEM; + + chans_st_arr = devm_kcalloc(dev, st->num_channels, sizeof(*st->channels), + GFP_KERNEL); + if (!chans_st_arr) + return -ENOMEM; + + indio_dev->channels = chan_arr; + st->channels = chans_st_arr; + + if (st->info->has_temp) { + chan_arr[chan_index] = ad7173_temp_iio_channel_template; + chan_st_priv = &chans_st_arr[chan_index]; + chan_st_priv->ain = + AD7173_CH_ADDRESS(chan_arr[chan_index].channel, + chan_arr[chan_index].channel2); + chan_st_priv->cfg.bipolar = false; + chan_st_priv->cfg.input_buf = st->info->has_input_buf; + chan_st_priv->cfg.ref_sel = AD7173_SETUP_REF_SEL_INT_REF; + st->adc_mode |= AD7173_ADC_MODE_REF_EN; + + chan_index++; + } + + device_for_each_child_node_scoped(dev, child) { + chan = &chan_arr[chan_index]; + chan_st_priv = &chans_st_arr[chan_index]; + ret = fwnode_property_read_u32_array(child, "diff-channels", + ain, ARRAY_SIZE(ain)); + if (ret) + return ret; + + if (ain[0] >= st->info->num_inputs || + ain[1] >= st->info->num_inputs) + return dev_err_probe(dev, -EINVAL, + "Input pin number out of range for pair (%d %d).\n", + ain[0], ain[1]); + + ret = fwnode_property_match_property_string(child, + "adi,reference-select", + ad7173_ref_sel_str, + ARRAY_SIZE(ad7173_ref_sel_str)); + if (ret < 0) + ref_sel = AD7173_SETUP_REF_SEL_INT_REF; + else + ref_sel = ret; + + if (ref_sel == AD7173_SETUP_REF_SEL_INT_REF && + !st->info->has_int_ref) + return dev_err_probe(dev, -EINVAL, + "Internal reference is not available on current model.\n"); + + if (ref_sel == AD7173_SETUP_REF_SEL_EXT_REF2 && !st->info->has_ref2) + return dev_err_probe(dev, -EINVAL, + "External reference 2 is not available on current model.\n"); + + ret = ad7173_get_ref_voltage_milli(st, ref_sel); + if (ret < 0) + return dev_err_probe(dev, ret, + "Cannot use reference %u\n", ref_sel); + + if (ref_sel == AD7173_SETUP_REF_SEL_INT_REF) + st->adc_mode |= AD7173_ADC_MODE_REF_EN; + chan_st_priv->cfg.ref_sel = ref_sel; + + *chan = ad7173_channel_template; + chan->address = chan_index; + chan->scan_index = chan_index; + chan->channel = ain[0]; + chan->channel2 = ain[1]; + chan->differential = true; + + chan_st_priv->ain = AD7173_CH_ADDRESS(ain[0], ain[1]); + chan_st_priv->chan_reg = chan_index; + chan_st_priv->cfg.input_buf = st->info->has_input_buf; + chan_st_priv->cfg.odr = 0; + + chan_st_priv->cfg.bipolar = fwnode_property_read_bool(child, "bipolar"); + if (chan_st_priv->cfg.bipolar) + chan->info_mask_separate |= BIT(IIO_CHAN_INFO_OFFSET); + + chan_index++; + } + return 0; +} + +static int ad7173_fw_parse_device_config(struct iio_dev *indio_dev) +{ + struct ad7173_state *st = iio_priv(indio_dev); + struct device *dev = indio_dev->dev.parent; + unsigned int num_channels; + int ret; + + st->regulators[0].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_EXT_REF]; + st->regulators[1].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_EXT_REF2]; + st->regulators[2].supply = ad7173_ref_sel_str[AD7173_SETUP_REF_SEL_AVDD1_AVSS]; + + /* + * If a regulator is not available, it will be set to a dummy regulator. + * Each channel reference is checked with regulator_get_voltage() before + * setting attributes so if any channel uses a dummy supply the driver + * probe will fail. + */ + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(st->regulators), + st->regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to get regulators\n"); + + ret = regulator_bulk_enable(ARRAY_SIZE(st->regulators), st->regulators); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulators\n"); + + ret = devm_add_action_or_reset(dev, ad7173_disable_regulators, st); + if (ret) + return dev_err_probe(dev, ret, + "Failed to add regulators disable action\n"); + + ret = device_property_match_property_string(dev, "clock-names", + ad7173_clk_sel, + ARRAY_SIZE(ad7173_clk_sel)); + if (ret < 0) { + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, + AD7173_ADC_MODE_CLOCKSEL_INT); + ad7173_register_clk_provider(indio_dev); + } else { + st->adc_mode |= FIELD_PREP(AD7173_ADC_MODE_CLOCKSEL_MASK, + AD7173_ADC_MODE_CLOCKSEL_EXT + ret); + st->ext_clk = devm_clk_get(dev, ad7173_clk_sel[ret]); + if (IS_ERR(st->ext_clk)) + return dev_err_probe(dev, PTR_ERR(st->ext_clk), + "Failed to get external clock\n"); + + ret = clk_prepare_enable(st->ext_clk); + if (ret) + return dev_err_probe(dev, ret, + "Failed to enable external clock\n"); + + ret = devm_add_action_or_reset(dev, ad7173_clk_disable_unprepare, + st->ext_clk); + if (ret) + return ret; + } + + ret = fwnode_irq_get_byname(dev_fwnode(dev), "rdy"); + if (ret < 0) + return dev_err_probe(dev, ret, "Interrupt 'rdy' is required\n"); + + ad7173_sigma_delta_info.irq_line = ret; + + num_channels = device_get_child_node_count(dev); + + if (st->info->has_temp) + num_channels++; + + if (num_channels == 0) + return dev_err_probe(dev, -ENODATA, "No channels specified\n"); + indio_dev->num_channels = num_channels; + st->num_channels = num_channels; + + return ad7173_fw_parse_channel_config(indio_dev); +} + +static int ad7173_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct ad7173_state *st; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + st->info = spi_get_device_match_data(spi); + if (!st->info) + return -ENODEV; + + ida_init(&st->cfg_slots_status); + ret = devm_add_action_or_reset(dev, ad7173_ida_destroy, st); + if (ret) + return ret; + + indio_dev->name = st->info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7173_info; + + spi->mode = SPI_MODE_3; + spi_setup(spi); + + ad7173_sigma_delta_info.num_slots = st->info->num_configs; + ret = ad_sd_init(&st->sd, indio_dev, spi, &ad7173_sigma_delta_info); + if (ret) + return ret; + + ret = ad7173_fw_parse_device_config(indio_dev); + if (ret) + return ret; + + ret = devm_ad_sd_setup_buffer_and_trigger(dev, indio_dev); + if (ret) + return ret; + + ret = ad7173_setup(indio_dev); + if (ret) + return ret; + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return ret; + + if (IS_ENABLED(CONFIG_GPIOLIB)) + return ad7173_gpio_init(st); + + return 0; +} + +static const struct of_device_id ad7173_of_match[] = { + { .compatible = "adi,ad7172-2", + .data = &ad7173_device_info[ID_AD7172_2]}, + { .compatible = "adi,ad7172-4", + .data = &ad7173_device_info[ID_AD7172_4]}, + { .compatible = "adi,ad7173-8", + .data = &ad7173_device_info[ID_AD7173_8]}, + { .compatible = "adi,ad7175-2", + .data = &ad7173_device_info[ID_AD7175_2]}, + { .compatible = "adi,ad7175-8", + .data = &ad7173_device_info[ID_AD7175_8]}, + { .compatible = "adi,ad7176-2", + .data = &ad7173_device_info[ID_AD7176_2]}, + { .compatible = "adi,ad7177-2", + .data = &ad7173_device_info[ID_AD7177_2]}, + { } +}; +MODULE_DEVICE_TABLE(of, ad7173_of_match); + +static const struct spi_device_id ad7173_id_table[] = { + { "ad7172-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7172_2]}, + { "ad7172-4", (kernel_ulong_t)&ad7173_device_info[ID_AD7172_4]}, + { "ad7173-8", (kernel_ulong_t)&ad7173_device_info[ID_AD7173_8]}, + { "ad7175-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7175_2]}, + { "ad7175-8", (kernel_ulong_t)&ad7173_device_info[ID_AD7175_8]}, + { "ad7176-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7176_2]}, + { "ad7177-2", (kernel_ulong_t)&ad7173_device_info[ID_AD7177_2]}, + { } +}; +MODULE_DEVICE_TABLE(spi, ad7173_id_table); + +static struct spi_driver ad7173_driver = { + .driver = { + .name = "ad7173", + .of_match_table = ad7173_of_match, + }, + .probe = ad7173_probe, + .id_table = ad7173_id_table, +}; +module_spi_driver(ad7173_driver); + +MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA); +MODULE_AUTHOR("Lars-Peter Clausen <lars@metafo.de>"); +MODULE_AUTHOR("Dumitru Ceclan <dumitru.ceclan@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD7172/AD7173/AD7175/AD7176 ADC driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index adc3cbe92d..7bcc7e2aa2 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -17,7 +17,9 @@ #include <linux/err.h> #include <linux/sched.h> #include <linux/delay.h> -#include <linux/of.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -364,19 +366,19 @@ static inline bool ad7192_valid_external_frequency(u32 freq) freq <= AD7192_EXT_FREQ_MHZ_MAX); } -static int ad7192_of_clock_select(struct ad7192_state *st) +static int ad7192_clock_select(struct ad7192_state *st) { - struct device_node *np = st->sd.spi->dev.of_node; + struct device *dev = &st->sd.spi->dev; unsigned int clock_sel; clock_sel = AD7192_CLK_INT; /* use internal clock */ if (!st->mclk) { - if (of_property_read_bool(np, "adi,int-clock-output-enable")) + if (device_property_read_bool(dev, "adi,int-clock-output-enable")) clock_sel = AD7192_CLK_INT_CO; } else { - if (of_property_read_bool(np, "adi,clock-xtal")) + if (device_property_read_bool(dev, "adi,clock-xtal")) clock_sel = AD7192_CLK_EXT_MCLK1_2; else clock_sel = AD7192_CLK_EXT_MCLK2; @@ -385,7 +387,7 @@ static int ad7192_of_clock_select(struct ad7192_state *st) return clock_sel; } -static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np) +static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev) { struct ad7192_state *st = iio_priv(indio_dev); bool rej60_en, refin2_en; @@ -407,7 +409,7 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np) id = FIELD_GET(AD7192_ID_MASK, id); if (id != st->chip_info->chip_id) - dev_warn(&st->sd.spi->dev, "device ID query failed (0x%X != 0x%X)\n", + dev_warn(dev, "device ID query failed (0x%X != 0x%X)\n", id, st->chip_info->chip_id); st->mode = FIELD_PREP(AD7192_MODE_SEL_MASK, AD7192_MODE_IDLE) | @@ -416,30 +418,30 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device_node *np) st->conf = FIELD_PREP(AD7192_CONF_GAIN_MASK, 0); - rej60_en = of_property_read_bool(np, "adi,rejection-60-Hz-enable"); + rej60_en = device_property_read_bool(dev, "adi,rejection-60-Hz-enable"); if (rej60_en) st->mode |= AD7192_MODE_REJ60; - refin2_en = of_property_read_bool(np, "adi,refin2-pins-enable"); + refin2_en = device_property_read_bool(dev, "adi,refin2-pins-enable"); if (refin2_en && st->chip_info->chip_id != CHIPID_AD7195) st->conf |= AD7192_CONF_REFSEL; st->conf &= ~AD7192_CONF_CHOP; - buf_en = of_property_read_bool(np, "adi,buffer-enable"); + buf_en = device_property_read_bool(dev, "adi,buffer-enable"); if (buf_en) st->conf |= AD7192_CONF_BUF; - bipolar = of_property_read_bool(np, "bipolar"); + bipolar = device_property_read_bool(dev, "bipolar"); if (!bipolar) st->conf |= AD7192_CONF_UNIPOLAR; - burnout_curr_en = of_property_read_bool(np, - "adi,burnout-currents-enable"); + burnout_curr_en = device_property_read_bool(dev, + "adi,burnout-currents-enable"); if (burnout_curr_en && buf_en) { st->conf |= AD7192_CONF_BURN; } else if (burnout_curr_en) { - dev_warn(&st->sd.spi->dev, + dev_warn(dev, "Can't enable burnout currents: see CHOP or buffer\n"); } @@ -1117,9 +1119,7 @@ static int ad7192_probe(struct spi_device *spi) } st->int_vref_mv = ret / 1000; - st->chip_info = of_device_get_match_data(&spi->dev); - if (!st->chip_info) - st->chip_info = (void *)spi_get_device_id(spi)->driver_data; + st->chip_info = spi_get_device_match_data(spi); indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->chip_info->channels; @@ -1140,7 +1140,7 @@ static int ad7192_probe(struct spi_device *spi) if (IS_ERR(st->mclk)) return PTR_ERR(st->mclk); - st->clock_sel = ad7192_of_clock_select(st); + st->clock_sel = ad7192_clock_select(st); if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || st->clock_sel == AD7192_CLK_EXT_MCLK2) { @@ -1152,7 +1152,7 @@ static int ad7192_probe(struct spi_device *spi) } } - ret = ad7192_setup(indio_dev, spi->dev.of_node); + ret = ad7192_setup(indio_dev, &spi->dev); if (ret) return ret; diff --git a/drivers/iio/adc/ad7266.c b/drivers/iio/adc/ad7266.c index 98648c679a..13ea8a1073 100644 --- a/drivers/iio/adc/ad7266.c +++ b/drivers/iio/adc/ad7266.c @@ -373,7 +373,6 @@ static void ad7266_init_channels(struct iio_dev *indio_dev) indio_dev->channels = chan_info->channels; indio_dev->num_channels = chan_info->num_channels; indio_dev->available_scan_masks = chan_info->scan_masks; - indio_dev->masklength = chan_info->num_channels - 1; } static const char * const ad7266_gpio_labels[] = { diff --git a/drivers/iio/adc/ad7292.c b/drivers/iio/adc/ad7292.c index cccacec5db..6aadd14f45 100644 --- a/drivers/iio/adc/ad7292.c +++ b/drivers/iio/adc/ad7292.c @@ -8,7 +8,8 @@ #include <linux/bitfield.h> #include <linux/device.h> #include <linux/module.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> @@ -260,7 +261,6 @@ static int ad7292_probe(struct spi_device *spi) { struct ad7292_state *st; struct iio_dev *indio_dev; - struct device_node *child; bool diff_channels = false; int ret; @@ -305,12 +305,11 @@ static int ad7292_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ad7292_info; - for_each_available_child_of_node(spi->dev.of_node, child) { - diff_channels = of_property_read_bool(child, "diff-channels"); - if (diff_channels) { - of_node_put(child); + device_for_each_child_node_scoped(&spi->dev, child) { + diff_channels = fwnode_property_read_bool(child, + "diff-channels"); + if (diff_channels) break; - } } if (diff_channels) { diff --git a/drivers/iio/adc/ad7944.c b/drivers/iio/adc/ad7944.c new file mode 100644 index 0000000000..4602ab5ed2 --- /dev/null +++ b/drivers/iio/adc/ad7944.c @@ -0,0 +1,690 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD7944/85/86 PulSAR ADC family driver. + * + * Copyright 2024 Analog Devices, Inc. + * Copyright 2024 BayLibre, SAS + */ + +#include <linux/align.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/gpio/consumer.h> +#include <linux/module.h> +#include <linux/property.h> +#include <linux/regulator/consumer.h> +#include <linux/spi/spi.h> +#include <linux/string_helpers.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define AD7944_INTERNAL_REF_MV 4096 + +struct ad7944_timing_spec { + /* Normal mode max conversion time (t_{CONV}). */ + unsigned int conv_ns; + /* TURBO mode max conversion time (t_{CONV}). */ + unsigned int turbo_conv_ns; +}; + +enum ad7944_spi_mode { + /* datasheet calls this "4-wire mode" */ + AD7944_SPI_MODE_DEFAULT, + /* datasheet calls this "3-wire mode" (not related to SPI_3WIRE!) */ + AD7944_SPI_MODE_SINGLE, + /* datasheet calls this "chain mode" */ + AD7944_SPI_MODE_CHAIN, +}; + +/* maps adi,spi-mode property value to enum */ +static const char * const ad7944_spi_modes[] = { + [AD7944_SPI_MODE_DEFAULT] = "", + [AD7944_SPI_MODE_SINGLE] = "single", + [AD7944_SPI_MODE_CHAIN] = "chain", +}; + +struct ad7944_adc { + struct spi_device *spi; + enum ad7944_spi_mode spi_mode; + struct spi_transfer xfers[3]; + struct spi_message msg; + void *chain_mode_buf; + /* Chip-specific timing specifications. */ + const struct ad7944_timing_spec *timing_spec; + /* GPIO connected to CNV pin. */ + struct gpio_desc *cnv; + /* Optional GPIO to enable turbo mode. */ + struct gpio_desc *turbo; + /* Indicates TURBO is hard-wired to be always enabled. */ + bool always_turbo; + /* Reference voltage (millivolts). */ + unsigned int ref_mv; + + /* + * DMA (thus cache coherency maintenance) requires the + * transfer buffers to live in their own cache lines. + */ + struct { + union { + u16 u16; + u32 u32; + } raw; + u64 timestamp __aligned(8); + } sample __aligned(IIO_DMA_MINALIGN); +}; + +/* quite time before CNV rising edge */ +#define T_QUIET_NS 20 + +static const struct ad7944_timing_spec ad7944_timing_spec = { + .conv_ns = 420, + .turbo_conv_ns = 320, +}; + +static const struct ad7944_timing_spec ad7986_timing_spec = { + .conv_ns = 500, + .turbo_conv_ns = 400, +}; + +struct ad7944_chip_info { + const char *name; + const struct ad7944_timing_spec *timing_spec; + const struct iio_chan_spec channels[2]; +}; + +/* + * AD7944_DEFINE_CHIP_INFO - Define a chip info structure for a specific chip + * @_name: The name of the chip + * @_ts: The timing specification for the chip + * @_bits: The number of bits in the conversion result + * @_diff: Whether the chip is true differential or not + */ +#define AD7944_DEFINE_CHIP_INFO(_name, _ts, _bits, _diff) \ +static const struct ad7944_chip_info _name##_chip_info = { \ + .name = #_name, \ + .timing_spec = &_ts##_timing_spec, \ + .channels = { \ + { \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .differential = _diff, \ + .channel = 0, \ + .channel2 = _diff ? 1 : 0, \ + .scan_index = 0, \ + .scan_type.sign = _diff ? 's' : 'u', \ + .scan_type.realbits = _bits, \ + .scan_type.storagebits = _bits > 16 ? 32 : 16, \ + .scan_type.endianness = IIO_CPU, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) \ + | BIT(IIO_CHAN_INFO_SCALE), \ + }, \ + IIO_CHAN_SOFT_TIMESTAMP(1), \ + }, \ +} + +/* pseudo-differential with ground sense */ +AD7944_DEFINE_CHIP_INFO(ad7944, ad7944, 14, 0); +AD7944_DEFINE_CHIP_INFO(ad7985, ad7944, 16, 0); +/* fully differential */ +AD7944_DEFINE_CHIP_INFO(ad7986, ad7986, 18, 1); + +static void ad7944_unoptimize_msg(void *msg) +{ + spi_unoptimize_message(msg); +} + +static int ad7944_3wire_cs_mode_init_msg(struct device *dev, struct ad7944_adc *adc, + const struct iio_chan_spec *chan) +{ + unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns + : adc->timing_spec->conv_ns; + struct spi_transfer *xfers = adc->xfers; + int ret; + + /* + * NB: can get better performance from some SPI controllers if we use + * the same bits_per_word in every transfer. + */ + xfers[0].bits_per_word = chan->scan_type.realbits; + /* + * CS is tied to CNV and we need a low to high transition to start the + * conversion, so place CNV low for t_QUIET to prepare for this. + */ + xfers[0].delay.value = T_QUIET_NS; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + + /* + * CS has to be high for full conversion time to avoid triggering the + * busy indication. + */ + xfers[1].cs_off = 1; + xfers[1].delay.value = t_conv_ns; + xfers[1].delay.unit = SPI_DELAY_UNIT_NSECS; + xfers[1].bits_per_word = chan->scan_type.realbits; + + /* Then we can read the data during the acquisition phase */ + xfers[2].rx_buf = &adc->sample.raw; + xfers[2].len = BITS_TO_BYTES(chan->scan_type.storagebits); + xfers[2].bits_per_word = chan->scan_type.realbits; + + spi_message_init_with_transfers(&adc->msg, xfers, 3); + + ret = spi_optimize_message(adc->spi, &adc->msg); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg); +} + +static int ad7944_4wire_mode_init_msg(struct device *dev, struct ad7944_adc *adc, + const struct iio_chan_spec *chan) +{ + unsigned int t_conv_ns = adc->always_turbo ? adc->timing_spec->turbo_conv_ns + : adc->timing_spec->conv_ns; + struct spi_transfer *xfers = adc->xfers; + int ret; + + /* + * NB: can get better performance from some SPI controllers if we use + * the same bits_per_word in every transfer. + */ + xfers[0].bits_per_word = chan->scan_type.realbits; + /* + * CS has to be high for full conversion time to avoid triggering the + * busy indication. + */ + xfers[0].cs_off = 1; + xfers[0].delay.value = t_conv_ns; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + + xfers[1].rx_buf = &adc->sample.raw; + xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits); + xfers[1].bits_per_word = chan->scan_type.realbits; + + spi_message_init_with_transfers(&adc->msg, xfers, 2); + + ret = spi_optimize_message(adc->spi, &adc->msg); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg); +} + +static int ad7944_chain_mode_init_msg(struct device *dev, struct ad7944_adc *adc, + const struct iio_chan_spec *chan, + u32 n_chain_dev) +{ + struct spi_transfer *xfers = adc->xfers; + int ret; + + /* + * NB: SCLK has to be low before we toggle CS to avoid triggering the + * busy indication. + */ + if (adc->spi->mode & SPI_CPOL) + return dev_err_probe(dev, -EINVAL, + "chain mode requires ~SPI_CPOL\n"); + + /* + * We only support CNV connected to CS in chain mode and we need CNV + * to be high during the transfer to trigger the conversion. + */ + if (!(adc->spi->mode & SPI_CS_HIGH)) + return dev_err_probe(dev, -EINVAL, + "chain mode requires SPI_CS_HIGH\n"); + + /* CNV has to be high for full conversion time before reading data. */ + xfers[0].delay.value = adc->timing_spec->conv_ns; + xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS; + + xfers[1].rx_buf = adc->chain_mode_buf; + xfers[1].len = BITS_TO_BYTES(chan->scan_type.storagebits) * n_chain_dev; + xfers[1].bits_per_word = chan->scan_type.realbits; + + spi_message_init_with_transfers(&adc->msg, xfers, 2); + + ret = spi_optimize_message(adc->spi, &adc->msg); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, ad7944_unoptimize_msg, &adc->msg); +} + +/** + * ad7944_convert_and_acquire - Perform a single conversion and acquisition + * @adc: The ADC device structure + * @chan: The channel specification + * Return: 0 on success, a negative error code on failure + * + * Perform a conversion and acquisition of a single sample using the + * pre-optimized adc->msg. + * + * Upon successful return adc->sample.raw will contain the conversion result + * (or adc->chain_mode_buf if the device is using chain mode). + */ +static int ad7944_convert_and_acquire(struct ad7944_adc *adc, + const struct iio_chan_spec *chan) +{ + int ret; + + /* + * In 4-wire mode, the CNV line is held high for the entire conversion + * and acquisition process. In other modes adc->cnv is NULL and is + * ignored (CS is wired to CNV in those cases). + */ + gpiod_set_value_cansleep(adc->cnv, 1); + ret = spi_sync(adc->spi, &adc->msg); + gpiod_set_value_cansleep(adc->cnv, 0); + + return ret; +} + +static int ad7944_single_conversion(struct ad7944_adc *adc, + const struct iio_chan_spec *chan, + int *val) +{ + int ret; + + ret = ad7944_convert_and_acquire(adc, chan); + if (ret) + return ret; + + if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) { + if (chan->scan_type.storagebits > 16) + *val = ((u32 *)adc->chain_mode_buf)[chan->scan_index]; + else + *val = ((u16 *)adc->chain_mode_buf)[chan->scan_index]; + } else { + if (chan->scan_type.storagebits > 16) + *val = adc->sample.raw.u32; + else + *val = adc->sample.raw.u16; + } + + if (chan->scan_type.sign == 's') + *val = sign_extend32(*val, chan->scan_type.realbits - 1); + + return IIO_VAL_INT; +} + +static int ad7944_read_raw(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + int *val, int *val2, long info) +{ + struct ad7944_adc *adc = iio_priv(indio_dev); + int ret; + + switch (info) { + case IIO_CHAN_INFO_RAW: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = ad7944_single_conversion(adc, chan, val); + iio_device_release_direct_mode(indio_dev); + return ret; + + case IIO_CHAN_INFO_SCALE: + switch (chan->type) { + case IIO_VOLTAGE: + *val = adc->ref_mv; + + if (chan->scan_type.sign == 's') + *val2 = chan->scan_type.realbits - 1; + else + *val2 = chan->scan_type.realbits; + + return IIO_VAL_FRACTIONAL_LOG2; + default: + return -EINVAL; + } + + default: + return -EINVAL; + } +} + +static const struct iio_info ad7944_iio_info = { + .read_raw = &ad7944_read_raw, +}; + +static irqreturn_t ad7944_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct ad7944_adc *adc = iio_priv(indio_dev); + int ret; + + ret = ad7944_convert_and_acquire(adc, &indio_dev->channels[0]); + if (ret) + goto out; + + if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) + iio_push_to_buffers_with_timestamp(indio_dev, adc->chain_mode_buf, + pf->timestamp); + else + iio_push_to_buffers_with_timestamp(indio_dev, &adc->sample.raw, + pf->timestamp); + +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +/** + * ad7944_chain_mode_alloc - allocate and initialize channel specs and buffers + * for daisy-chained devices + * @dev: The device for devm_ functions + * @chan_template: The channel template for the devices (array of 2 channels + * voltage and timestamp) + * @n_chain_dev: The number of devices in the chain + * @chain_chan: Pointer to receive the allocated channel specs + * @chain_mode_buf: Pointer to receive the allocated rx buffer + * @chain_scan_masks: Pointer to receive the allocated scan masks + * Return: 0 on success, a negative error code on failure + */ +static int ad7944_chain_mode_alloc(struct device *dev, + const struct iio_chan_spec *chan_template, + u32 n_chain_dev, + struct iio_chan_spec **chain_chan, + void **chain_mode_buf, + unsigned long **chain_scan_masks) +{ + struct iio_chan_spec *chan; + size_t chain_mode_buf_size; + unsigned long *scan_masks; + void *buf; + int i; + + /* 1 channel for each device in chain plus 1 for soft timestamp */ + + chan = devm_kcalloc(dev, n_chain_dev + 1, sizeof(*chan), GFP_KERNEL); + if (!chan) + return -ENOMEM; + + for (i = 0; i < n_chain_dev; i++) { + chan[i] = chan_template[0]; + + if (chan_template[0].differential) { + chan[i].channel = 2 * i; + chan[i].channel2 = 2 * i + 1; + } else { + chan[i].channel = i; + } + + chan[i].scan_index = i; + } + + /* soft timestamp */ + chan[i] = chan_template[1]; + chan[i].scan_index = i; + + *chain_chan = chan; + + /* 1 word for each voltage channel + aligned u64 for timestamp */ + + chain_mode_buf_size = ALIGN(n_chain_dev * + BITS_TO_BYTES(chan[0].scan_type.storagebits), sizeof(u64)) + + sizeof(u64); + buf = devm_kzalloc(dev, chain_mode_buf_size, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + *chain_mode_buf = buf; + + /* + * Have to limit n_chain_dev due to current implementation of + * available_scan_masks. + */ + if (n_chain_dev > BITS_PER_LONG) + return dev_err_probe(dev, -EINVAL, + "chain is limited to 32 devices\n"); + + scan_masks = devm_kcalloc(dev, 2, sizeof(*scan_masks), GFP_KERNEL); + if (!scan_masks) + return -ENOMEM; + + /* + * Scan mask is needed since we always have to read all devices in the + * chain in one SPI transfer. + */ + scan_masks[0] = GENMASK(n_chain_dev - 1, 0); + + *chain_scan_masks = scan_masks; + + return 0; +} + +static const char * const ad7944_power_supplies[] = { + "avdd", "dvdd", "bvdd", "vio" +}; + +static void ad7944_ref_disable(void *ref) +{ + regulator_disable(ref); +} + +static int ad7944_probe(struct spi_device *spi) +{ + const struct ad7944_chip_info *chip_info; + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad7944_adc *adc; + bool have_refin = false; + struct regulator *ref; + struct iio_chan_spec *chain_chan; + unsigned long *chain_scan_masks; + u32 n_chain_dev; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*adc)); + if (!indio_dev) + return -ENOMEM; + + adc = iio_priv(indio_dev); + adc->spi = spi; + + chip_info = spi_get_device_match_data(spi); + if (!chip_info) + return dev_err_probe(dev, -EINVAL, "no chip info\n"); + + adc->timing_spec = chip_info->timing_spec; + + ret = device_property_match_property_string(dev, "adi,spi-mode", + ad7944_spi_modes, + ARRAY_SIZE(ad7944_spi_modes)); + /* absence of adi,spi-mode property means default mode */ + if (ret == -EINVAL) + adc->spi_mode = AD7944_SPI_MODE_DEFAULT; + else if (ret < 0) + return dev_err_probe(dev, ret, + "getting adi,spi-mode property failed\n"); + else + adc->spi_mode = ret; + + /* + * Some chips use unusual word sizes, so check now instead of waiting + * for the first xfer. + */ + if (!spi_is_bpw_supported(spi, chip_info->channels[0].scan_type.realbits)) + return dev_err_probe(dev, -EINVAL, + "SPI host does not support %d bits per word\n", + chip_info->channels[0].scan_type.realbits); + + ret = devm_regulator_bulk_get_enable(dev, + ARRAY_SIZE(ad7944_power_supplies), + ad7944_power_supplies); + if (ret) + return dev_err_probe(dev, ret, + "failed to get and enable supplies\n"); + + /* + * Sort out what is being used for the reference voltage. Options are: + * - internal reference: neither REF or REFIN is connected + * - internal reference with external buffer: REF not connected, REFIN + * is connected + * - external reference: REF is connected, REFIN is not connected + */ + + ref = devm_regulator_get_optional(dev, "ref"); + if (IS_ERR(ref)) { + if (PTR_ERR(ref) != -ENODEV) + return dev_err_probe(dev, PTR_ERR(ref), + "failed to get REF supply\n"); + + ref = NULL; + } + + ret = devm_regulator_get_enable_optional(dev, "refin"); + if (ret == 0) + have_refin = true; + else if (ret != -ENODEV) + return dev_err_probe(dev, ret, + "failed to get and enable REFIN supply\n"); + + if (have_refin && ref) + return dev_err_probe(dev, -EINVAL, + "cannot have both refin and ref supplies\n"); + + if (ref) { + ret = regulator_enable(ref); + if (ret) + return dev_err_probe(dev, ret, + "failed to enable REF supply\n"); + + ret = devm_add_action_or_reset(dev, ad7944_ref_disable, ref); + if (ret) + return ret; + + ret = regulator_get_voltage(ref); + if (ret < 0) + return dev_err_probe(dev, ret, + "failed to get REF voltage\n"); + + /* external reference */ + adc->ref_mv = ret / 1000; + } else { + /* internal reference */ + adc->ref_mv = AD7944_INTERNAL_REF_MV; + } + + adc->cnv = devm_gpiod_get_optional(dev, "cnv", GPIOD_OUT_LOW); + if (IS_ERR(adc->cnv)) + return dev_err_probe(dev, PTR_ERR(adc->cnv), + "failed to get CNV GPIO\n"); + + if (!adc->cnv && adc->spi_mode == AD7944_SPI_MODE_DEFAULT) + return dev_err_probe(&spi->dev, -EINVAL, "CNV GPIO is required\n"); + if (adc->cnv && adc->spi_mode != AD7944_SPI_MODE_DEFAULT) + return dev_err_probe(&spi->dev, -EINVAL, + "CNV GPIO in single and chain mode is not currently supported\n"); + + adc->turbo = devm_gpiod_get_optional(dev, "turbo", GPIOD_OUT_LOW); + if (IS_ERR(adc->turbo)) + return dev_err_probe(dev, PTR_ERR(adc->turbo), + "failed to get TURBO GPIO\n"); + + adc->always_turbo = device_property_present(dev, "adi,always-turbo"); + + if (adc->turbo && adc->always_turbo) + return dev_err_probe(dev, -EINVAL, + "cannot have both turbo-gpios and adi,always-turbo\n"); + + if (adc->spi_mode == AD7944_SPI_MODE_CHAIN && adc->always_turbo) + return dev_err_probe(dev, -EINVAL, + "cannot have both chain mode and always turbo\n"); + + switch (adc->spi_mode) { + case AD7944_SPI_MODE_DEFAULT: + ret = ad7944_4wire_mode_init_msg(dev, adc, &chip_info->channels[0]); + if (ret) + return ret; + + break; + case AD7944_SPI_MODE_SINGLE: + ret = ad7944_3wire_cs_mode_init_msg(dev, adc, &chip_info->channels[0]); + if (ret) + return ret; + + break; + case AD7944_SPI_MODE_CHAIN: + ret = device_property_read_u32(dev, "#daisy-chained-devices", + &n_chain_dev); + if (ret) + return dev_err_probe(dev, ret, + "failed to get #daisy-chained-devices\n"); + + ret = ad7944_chain_mode_alloc(dev, chip_info->channels, + n_chain_dev, &chain_chan, + &adc->chain_mode_buf, + &chain_scan_masks); + if (ret) + return ret; + + ret = ad7944_chain_mode_init_msg(dev, adc, &chain_chan[0], + n_chain_dev); + if (ret) + return ret; + + break; + } + + indio_dev->name = chip_info->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &ad7944_iio_info; + + if (adc->spi_mode == AD7944_SPI_MODE_CHAIN) { + indio_dev->available_scan_masks = chain_scan_masks; + indio_dev->channels = chain_chan; + indio_dev->num_channels = n_chain_dev + 1; + } else { + indio_dev->channels = chip_info->channels; + indio_dev->num_channels = ARRAY_SIZE(chip_info->channels); + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + iio_pollfunc_store_time, + ad7944_trigger_handler, NULL); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ad7944_of_match[] = { + { .compatible = "adi,ad7944", .data = &ad7944_chip_info }, + { .compatible = "adi,ad7985", .data = &ad7985_chip_info }, + { .compatible = "adi,ad7986", .data = &ad7986_chip_info }, + { } +}; +MODULE_DEVICE_TABLE(of, ad7944_of_match); + +static const struct spi_device_id ad7944_spi_id[] = { + { "ad7944", (kernel_ulong_t)&ad7944_chip_info }, + { "ad7985", (kernel_ulong_t)&ad7985_chip_info }, + { "ad7986", (kernel_ulong_t)&ad7986_chip_info }, + { } + +}; +MODULE_DEVICE_TABLE(spi, ad7944_spi_id); + +static struct spi_driver ad7944_driver = { + .driver = { + .name = "ad7944", + .of_match_table = ad7944_of_match, + }, + .probe = ad7944_probe, + .id_table = ad7944_spi_id, +}; +module_spi_driver(ad7944_driver); + +MODULE_AUTHOR("David Lechner <dlechner@baylibre.com>"); +MODULE_DESCRIPTION("Analog Devices AD7944 PulSAR ADC family driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/adc/ad799x.c b/drivers/iio/adc/ad799x.c index b757cc45c4..0f0dcd9ca6 100644 --- a/drivers/iio/adc/ad799x.c +++ b/drivers/iio/adc/ad799x.c @@ -128,7 +128,7 @@ struct ad799x_state { struct regulator *vref; /* lock to protect against multiple access to the device */ struct mutex lock; - unsigned id; + unsigned int id; u16 config; u8 *rx_buf; @@ -253,7 +253,7 @@ static int ad799x_update_scan_mode(struct iio_dev *indio_dev, } } -static int ad799x_scan_direct(struct ad799x_state *st, unsigned ch) +static int ad799x_scan_direct(struct ad799x_state *st, unsigned int ch) { u8 cmd; @@ -335,6 +335,7 @@ static ssize_t ad799x_read_frequency(struct device *dev, struct ad799x_state *st = iio_priv(indio_dev); int ret = i2c_smbus_read_byte_data(st->client, AD7998_CYCLE_TMR_REG); + if (ret < 0) return ret; @@ -523,7 +524,7 @@ done: return IRQ_HANDLED; } -static IIO_DEV_ATTR_SAMP_FREQ(S_IWUSR | S_IRUGO, +static IIO_DEV_ATTR_SAMP_FREQ(0644, ad799x_read_frequency, ad799x_write_frequency); static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("15625 7812 3906 1953 976 488 244 0"); diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 13e1bba45b..1fd2211e29 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -4,7 +4,11 @@ * * Copyright 2012-2020 Analog Devices Inc. */ + +#include <linux/bitmap.h> +#include <linux/bitops.h> #include <linux/cleanup.h> +#include <linux/debugfs.h> #include <linux/module.h> #include <linux/mutex.h> #include <linux/device.h> @@ -100,6 +104,8 @@ #define AD9467_DEF_OUTPUT_MODE 0x08 #define AD9467_REG_VREF_MASK 0x0F +#define AD9647_MAX_TEST_POINTS 32 + struct ad9467_chip_info { const char *name; unsigned int id; @@ -110,6 +116,9 @@ struct ad9467_chip_info { unsigned long max_rate; unsigned int default_output_mode; unsigned int vref_mask; + unsigned int num_lanes; + /* data clock output */ + bool has_dco; }; struct ad9467_state { @@ -119,13 +128,23 @@ struct ad9467_state { struct clk *clk; unsigned int output_mode; unsigned int (*scales)[2]; - + /* + * Times 2 because we may also invert the signal polarity and run the + * calibration again. For some reference on the test points (ad9265) see: + * https://www.analog.com/media/en/technical-documentation/data-sheets/ad9265.pdf + * at page 38 for the dco output delay. On devices as ad9467, the + * calibration is done at the backend level. For the ADI axi-adc: + * https://wiki.analog.com/resources/fpga/docs/axi_adc_ip + * at the io delay control section. + */ + DECLARE_BITMAP(calib_map, AD9647_MAX_TEST_POINTS * 2); struct gpio_desc *pwrdown_gpio; /* ensure consistent state obtained on multiple related accesses */ struct mutex lock; + u8 buf[3] __aligned(IIO_DMA_MINALIGN); }; -static int ad9467_spi_read(struct spi_device *spi, unsigned int reg) +static int ad9467_spi_read(struct ad9467_state *st, unsigned int reg) { unsigned char tbuf[2], rbuf[1]; int ret; @@ -133,7 +152,7 @@ static int ad9467_spi_read(struct spi_device *spi, unsigned int reg) tbuf[0] = 0x80 | (reg >> 8); tbuf[1] = reg & 0xFF; - ret = spi_write_then_read(spi, + ret = spi_write_then_read(st->spi, tbuf, ARRAY_SIZE(tbuf), rbuf, ARRAY_SIZE(rbuf)); @@ -143,35 +162,32 @@ static int ad9467_spi_read(struct spi_device *spi, unsigned int reg) return rbuf[0]; } -static int ad9467_spi_write(struct spi_device *spi, unsigned int reg, +static int ad9467_spi_write(struct ad9467_state *st, unsigned int reg, unsigned int val) { - unsigned char buf[3]; - - buf[0] = reg >> 8; - buf[1] = reg & 0xFF; - buf[2] = val; + st->buf[0] = reg >> 8; + st->buf[1] = reg & 0xFF; + st->buf[2] = val; - return spi_write(spi, buf, ARRAY_SIZE(buf)); + return spi_write(st->spi, st->buf, ARRAY_SIZE(st->buf)); } static int ad9467_reg_access(struct iio_dev *indio_dev, unsigned int reg, unsigned int writeval, unsigned int *readval) { struct ad9467_state *st = iio_priv(indio_dev); - struct spi_device *spi = st->spi; int ret; if (!readval) { guard(mutex)(&st->lock); - ret = ad9467_spi_write(spi, reg, writeval); + ret = ad9467_spi_write(st, reg, writeval); if (ret) return ret; - return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER, + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, AN877_ADC_TRANSFER_SYNC); } - ret = ad9467_spi_read(spi, reg); + ret = ad9467_spi_read(st, reg); if (ret < 0) return ret; *readval = ret; @@ -242,6 +258,7 @@ static const struct ad9467_chip_info ad9467_chip_tbl = { .num_channels = ARRAY_SIZE(ad9467_channels), .default_output_mode = AD9467_DEF_OUTPUT_MODE, .vref_mask = AD9467_REG_VREF_MASK, + .num_lanes = 8, }; static const struct ad9467_chip_info ad9434_chip_tbl = { @@ -254,6 +271,7 @@ static const struct ad9467_chip_info ad9434_chip_tbl = { .num_channels = ARRAY_SIZE(ad9434_channels), .default_output_mode = AD9434_DEF_OUTPUT_MODE, .vref_mask = AD9434_REG_VREF_MASK, + .num_lanes = 6, }; static const struct ad9467_chip_info ad9265_chip_tbl = { @@ -266,6 +284,7 @@ static const struct ad9467_chip_info ad9265_chip_tbl = { .num_channels = ARRAY_SIZE(ad9467_channels), .default_output_mode = AD9265_DEF_OUTPUT_MODE, .vref_mask = AD9265_REG_VREF_MASK, + .has_dco = true, }; static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2) @@ -274,7 +293,7 @@ static int ad9467_get_scale(struct ad9467_state *st, int *val, int *val2) unsigned int i, vref_val; int ret; - ret = ad9467_spi_read(st->spi, AN877_ADC_REG_VREF); + ret = ad9467_spi_read(st, AN877_ADC_REG_VREF); if (ret < 0) return ret; @@ -309,18 +328,257 @@ static int ad9467_set_scale(struct ad9467_state *st, int val, int val2) continue; guard(mutex)(&st->lock); - ret = ad9467_spi_write(st->spi, AN877_ADC_REG_VREF, + ret = ad9467_spi_write(st, AN877_ADC_REG_VREF, info->scale_table[i][1]); if (ret < 0) return ret; - return ad9467_spi_write(st->spi, AN877_ADC_REG_TRANSFER, + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, AN877_ADC_TRANSFER_SYNC); } return -EINVAL; } +static int ad9467_outputmode_set(struct ad9467_state *st, unsigned int mode) +{ + int ret; + + ret = ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_MODE, mode); + if (ret < 0) + return ret; + + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); +} + +static int ad9647_calibrate_prepare(struct ad9467_state *st) +{ + struct iio_backend_data_fmt data = { + .enable = false, + }; + unsigned int c; + int ret; + + ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO, + AN877_ADC_TESTMODE_PN9_SEQ); + if (ret) + return ret; + + ret = ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); + if (ret) + return ret; + + ret = ad9467_outputmode_set(st, st->info->default_output_mode); + if (ret) + return ret; + + for (c = 0; c < st->info->num_channels; c++) { + ret = iio_backend_data_format_set(st->back, c, &data); + if (ret) + return ret; + } + + ret = iio_backend_test_pattern_set(st->back, 0, + IIO_BACKEND_ADI_PRBS_9A); + if (ret) + return ret; + + return iio_backend_chan_enable(st->back, 0); +} + +static int ad9647_calibrate_polarity_set(struct ad9467_state *st, + bool invert) +{ + enum iio_backend_sample_trigger trigger; + + if (st->info->has_dco) { + unsigned int phase = AN877_ADC_OUTPUT_EVEN_ODD_MODE_EN; + + if (invert) + phase |= AN877_ADC_INVERT_DCO_CLK; + + return ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_PHASE, + phase); + } + + if (invert) + trigger = IIO_BACKEND_SAMPLE_TRIGGER_EDGE_FALLING; + else + trigger = IIO_BACKEND_SAMPLE_TRIGGER_EDGE_RISING; + + return iio_backend_data_sample_trigger(st->back, trigger); +} + +/* + * The idea is pretty simple. Find the max number of successful points in a row + * and get the one in the middle. + */ +static unsigned int ad9467_find_optimal_point(const unsigned long *calib_map, + unsigned int start, + unsigned int nbits, + unsigned int *val) +{ + unsigned int bit = start, end, start_cnt, cnt = 0; + + for_each_clear_bitrange_from(bit, end, calib_map, nbits + start) { + if (end - bit > cnt) { + cnt = end - bit; + start_cnt = bit; + } + } + + if (cnt) + *val = start_cnt + cnt / 2; + + return cnt; +} + +static int ad9467_calibrate_apply(struct ad9467_state *st, unsigned int val) +{ + unsigned int lane; + int ret; + + if (st->info->has_dco) { + ret = ad9467_spi_write(st, AN877_ADC_REG_OUTPUT_DELAY, + val); + if (ret) + return ret; + + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); + } + + for (lane = 0; lane < st->info->num_lanes; lane++) { + ret = iio_backend_iodelay_set(st->back, lane, val); + if (ret) + return ret; + } + + return 0; +} + +static int ad9647_calibrate_stop(struct ad9467_state *st) +{ + struct iio_backend_data_fmt data = { + .sign_extend = true, + .enable = true, + }; + unsigned int c, mode; + int ret; + + ret = iio_backend_chan_disable(st->back, 0); + if (ret) + return ret; + + ret = iio_backend_test_pattern_set(st->back, 0, + IIO_BACKEND_NO_TEST_PATTERN); + if (ret) + return ret; + + for (c = 0; c < st->info->num_channels; c++) { + ret = iio_backend_data_format_set(st->back, c, &data); + if (ret) + return ret; + } + + mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT; + ret = ad9467_outputmode_set(st, mode); + if (ret) + return ret; + + ret = ad9467_spi_write(st, AN877_ADC_REG_TEST_IO, + AN877_ADC_TESTMODE_OFF); + if (ret) + return ret; + + return ad9467_spi_write(st, AN877_ADC_REG_TRANSFER, + AN877_ADC_TRANSFER_SYNC); +} + +static int ad9467_calibrate(struct ad9467_state *st) +{ + unsigned int point, val, inv_val, cnt, inv_cnt = 0; + /* + * Half of the bitmap is for the inverted signal. The number of test + * points is the same though... + */ + unsigned int test_points = AD9647_MAX_TEST_POINTS; + unsigned long sample_rate = clk_get_rate(st->clk); + struct device *dev = &st->spi->dev; + bool invert = false, stat; + int ret; + + /* all points invalid */ + bitmap_fill(st->calib_map, BITS_PER_TYPE(st->calib_map)); + + ret = ad9647_calibrate_prepare(st); + if (ret) + return ret; +retune: + ret = ad9647_calibrate_polarity_set(st, invert); + if (ret) + return ret; + + for (point = 0; point < test_points; point++) { + ret = ad9467_calibrate_apply(st, point); + if (ret) + return ret; + + ret = iio_backend_chan_status(st->back, 0, &stat); + if (ret) + return ret; + + __assign_bit(point + invert * test_points, st->calib_map, stat); + } + + if (!invert) { + cnt = ad9467_find_optimal_point(st->calib_map, 0, test_points, + &val); + /* + * We're happy if we find, at least, three good test points in + * a row. + */ + if (cnt < 3) { + invert = true; + goto retune; + } + } else { + inv_cnt = ad9467_find_optimal_point(st->calib_map, test_points, + test_points, &inv_val); + if (!inv_cnt && !cnt) + return -EIO; + } + + if (inv_cnt < cnt) { + ret = ad9647_calibrate_polarity_set(st, false); + if (ret) + return ret; + } else { + /* + * polarity inverted is the last test to run. Hence, there's no + * need to re-do any configuration. We just need to "normalize" + * the selected value. + */ + val = inv_val - test_points; + } + + if (st->info->has_dco) + dev_dbg(dev, "%sDCO 0x%X CLK %lu Hz\n", inv_cnt >= cnt ? "INVERT " : "", + val, sample_rate); + else + dev_dbg(dev, "%sIDELAY 0x%x\n", inv_cnt >= cnt ? "INVERT " : "", + val); + + ret = ad9467_calibrate_apply(st, val); + if (ret) + return ret; + + /* finally apply the optimal value */ + return ad9647_calibrate_stop(st); +} + static int ad9467_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val, int *val2, long m) @@ -345,7 +603,9 @@ static int ad9467_write_raw(struct iio_dev *indio_dev, { struct ad9467_state *st = iio_priv(indio_dev); const struct ad9467_chip_info *info = st->info; + unsigned long sample_rate; long r_clk; + int ret; switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -358,7 +618,23 @@ static int ad9467_write_raw(struct iio_dev *indio_dev, return -EINVAL; } - return clk_set_rate(st->clk, r_clk); + sample_rate = clk_get_rate(st->clk); + /* + * clk_set_rate() would also do this but since we would still + * need it for avoiding an unnecessary calibration, do it now. + */ + if (sample_rate == r_clk) + return 0; + + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + ret = clk_set_rate(st->clk, r_clk); + if (ret) + return ret; + + guard(mutex)(&st->lock); + ret = ad9467_calibrate(st); + } + return ret; default: return -EINVAL; } @@ -411,18 +687,6 @@ static const struct iio_info ad9467_info = { .read_avail = ad9467_read_avail, }; -static int ad9467_outputmode_set(struct spi_device *spi, unsigned int mode) -{ - int ret; - - ret = ad9467_spi_write(spi, AN877_ADC_REG_OUTPUT_MODE, mode); - if (ret < 0) - return ret; - - return ad9467_spi_write(spi, AN877_ADC_REG_TRANSFER, - AN877_ADC_TRANSFER_SYNC); -} - static int ad9467_scale_fill(struct ad9467_state *st) { const struct ad9467_chip_info *info = st->info; @@ -442,29 +706,6 @@ static int ad9467_scale_fill(struct ad9467_state *st) return 0; } -static int ad9467_setup(struct ad9467_state *st) -{ - struct iio_backend_data_fmt data = { - .sign_extend = true, - .enable = true, - }; - unsigned int c, mode; - int ret; - - mode = st->info->default_output_mode | AN877_ADC_OUTPUT_MODE_TWOS_COMPLEMENT; - ret = ad9467_outputmode_set(st->spi, mode); - if (ret) - return ret; - - for (c = 0; c < st->info->num_channels; c++) { - ret = iio_backend_data_format_set(st->back, c, &data); - if (ret) - return ret; - } - - return 0; -} - static int ad9467_reset(struct device *dev) { struct gpio_desc *gpio; @@ -521,6 +762,52 @@ static int ad9467_iio_backend_get(struct ad9467_state *st) return -ENODEV; } +static ssize_t ad9467_dump_calib_table(struct file *file, + char __user *userbuf, + size_t count, loff_t *ppos) +{ + struct ad9467_state *st = file->private_data; + unsigned int bit, size = BITS_PER_TYPE(st->calib_map); + /* +2 for the newline and +1 for the string termination */ + unsigned char map[AD9647_MAX_TEST_POINTS * 2 + 3]; + ssize_t len = 0; + + guard(mutex)(&st->lock); + if (*ppos) + goto out_read; + + for (bit = 0; bit < size; bit++) { + if (bit == size / 2) + len += scnprintf(map + len, sizeof(map) - len, "\n"); + + len += scnprintf(map + len, sizeof(map) - len, "%c", + test_bit(bit, st->calib_map) ? 'x' : 'o'); + } + + len += scnprintf(map + len, sizeof(map) - len, "\n"); +out_read: + return simple_read_from_buffer(userbuf, count, ppos, map, len); +} + +static const struct file_operations ad9467_calib_table_fops = { + .open = simple_open, + .read = ad9467_dump_calib_table, + .llseek = default_llseek, + .owner = THIS_MODULE, +}; + +static void ad9467_debugfs_init(struct iio_dev *indio_dev) +{ + struct dentry *d = iio_get_debugfs_dentry(indio_dev); + struct ad9467_state *st = iio_priv(indio_dev); + + if (!IS_ENABLED(CONFIG_DEBUG_FS)) + return; + + debugfs_create_file("calibration_table_dump", 0400, d, st, + &ad9467_calib_table_fops); +} + static int ad9467_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -556,7 +843,7 @@ static int ad9467_probe(struct spi_device *spi) if (ret) return ret; - id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID); + id = ad9467_spi_read(st, AN877_ADC_REG_CHIP_ID); if (id != st->info->id) { dev_err(&spi->dev, "Mismatch CHIP_ID, got 0x%X, expected 0x%X\n", id, st->info->id); @@ -580,11 +867,17 @@ static int ad9467_probe(struct spi_device *spi) if (ret) return ret; - ret = ad9467_setup(st); + ret = ad9467_calibrate(st); if (ret) return ret; - return devm_iio_device_register(&spi->dev, indio_dev); + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) + return ret; + + ad9467_debugfs_init(indio_dev); + + return 0; } static const struct of_device_id ad9467_of_match[] = { diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index a602429cdd..a2b87f6b7a 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -206,7 +206,7 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) { int ret; - unsigned long timeout; + unsigned long time_left; ret = ad_sigma_delta_set_channel(sigma_delta, channel); if (ret) @@ -222,11 +222,11 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, goto out; sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); - timeout = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); - if (timeout == 0) { + enable_irq(sigma_delta->irq_line); + time_left = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); + if (time_left == 0) { sigma_delta->irq_dis = true; - disable_irq_nosync(sigma_delta->spi->irq); + disable_irq_nosync(sigma_delta->irq_line); ret = -EIO; } else { ret = 0; @@ -295,7 +295,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + enable_irq(sigma_delta->irq_line); ret = wait_for_completion_interruptible_timeout( &sigma_delta->completion, HZ); @@ -315,7 +315,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, out: if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->spi->irq); + disable_irq_nosync(sigma_delta->irq_line); sigma_delta->irq_dis = true; } @@ -396,7 +396,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) goto err_unlock; sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + enable_irq(sigma_delta->irq_line); return 0; @@ -414,7 +414,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) wait_for_completion_timeout(&sigma_delta->completion, HZ); if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->spi->irq); + disable_irq_nosync(sigma_delta->irq_line); sigma_delta->irq_dis = true; } @@ -516,7 +516,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) irq_handled: iio_trigger_notify_done(indio_dev->trig); sigma_delta->irq_dis = false; - enable_irq(sigma_delta->spi->irq); + enable_irq(sigma_delta->irq_line); return IRQ_HANDLED; } @@ -587,13 +587,13 @@ static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_de sigma_delta->irq_dis = true; /* the IRQ core clears IRQ_DISABLE_UNLAZY flag when freeing an IRQ */ - irq_set_status_flags(sigma_delta->spi->irq, IRQ_DISABLE_UNLAZY); + irq_set_status_flags(sigma_delta->irq_line, IRQ_DISABLE_UNLAZY); /* Allow overwriting the flags from firmware */ if (!irq_flags) irq_flags = sigma_delta->info->irq_flags; - ret = devm_request_irq(dev, sigma_delta->spi->irq, + ret = devm_request_irq(dev, sigma_delta->irq_line, ad_sd_data_rdy_trig_poll, irq_flags | IRQF_NO_AUTOEN, indio_dev->name, @@ -673,6 +673,11 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, } } + if (info->irq_line) + sigma_delta->irq_line = info->irq_line; + else + sigma_delta->irq_line = spi->irq; + iio_device_set_drvdata(indio_dev, sigma_delta); return 0; diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index e3b2158829..bf51d619eb 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -7,11 +7,13 @@ */ #include <linux/bitfield.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/err.h> #include <linux/io.h> #include <linux/delay.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/of.h> #include <linux/platform_device.h> #include <linux/property.h> @@ -37,6 +39,9 @@ #define ADI_AXI_REG_RSTN_MMCM_RSTN BIT(1) #define ADI_AXI_REG_RSTN_RSTN BIT(0) +#define ADI_AXI_ADC_REG_CTRL 0x0044 +#define ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK BIT(1) + /* ADC Channel controls */ #define ADI_AXI_REG_CHAN_CTRL(c) (0x0400 + (c) * 0x40) @@ -51,14 +56,28 @@ #define ADI_AXI_REG_CHAN_CTRL_PN_TYPE_OWR BIT(1) #define ADI_AXI_REG_CHAN_CTRL_ENABLE BIT(0) +#define ADI_AXI_ADC_REG_CHAN_STATUS(c) (0x0404 + (c) * 0x40) +#define ADI_AXI_ADC_CHAN_STAT_PN_MASK GENMASK(2, 1) + +#define ADI_AXI_ADC_REG_CHAN_CTRL_3(c) (0x0418 + (c) * 0x40) +#define ADI_AXI_ADC_CHAN_PN_SEL_MASK GENMASK(19, 16) + +/* IO Delays */ +#define ADI_AXI_ADC_REG_DELAY(l) (0x0800 + (l) * 0x4) +#define AXI_ADC_DELAY_CTRL_MASK GENMASK(4, 0) + +#define ADI_AXI_ADC_MAX_IO_NUM_LANES 15 + #define ADI_AXI_REG_CHAN_CTRL_DEFAULTS \ (ADI_AXI_REG_CHAN_CTRL_FMT_SIGNEXT | \ ADI_AXI_REG_CHAN_CTRL_FMT_EN | \ ADI_AXI_REG_CHAN_CTRL_ENABLE) struct adi_axi_adc_state { - struct regmap *regmap; - struct device *dev; + struct regmap *regmap; + struct device *dev; + /* lock to protect multiple accesses to the device registers */ + struct mutex lock; }; static int axi_adc_enable(struct iio_backend *back) @@ -66,6 +85,7 @@ static int axi_adc_enable(struct iio_backend *back) struct adi_axi_adc_state *st = iio_backend_get_priv(back); int ret; + guard(mutex)(&st->lock); ret = regmap_set_bits(st->regmap, ADI_AXI_REG_RSTN, ADI_AXI_REG_RSTN_MMCM_RSTN); if (ret) @@ -80,6 +100,7 @@ static void axi_adc_disable(struct iio_backend *back) { struct adi_axi_adc_state *st = iio_backend_get_priv(back); + guard(mutex)(&st->lock); regmap_write(st->regmap, ADI_AXI_REG_RSTN, 0); } @@ -104,6 +125,100 @@ static int axi_adc_data_format_set(struct iio_backend *back, unsigned int chan, ADI_AXI_REG_CHAN_CTRL_FMT_MASK, val); } +static int axi_adc_data_sample_trigger(struct iio_backend *back, + enum iio_backend_sample_trigger trigger) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + switch (trigger) { + case IIO_BACKEND_SAMPLE_TRIGGER_EDGE_RISING: + return regmap_clear_bits(st->regmap, ADI_AXI_ADC_REG_CTRL, + ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK); + case IIO_BACKEND_SAMPLE_TRIGGER_EDGE_FALLING: + return regmap_set_bits(st->regmap, ADI_AXI_ADC_REG_CTRL, + ADI_AXI_ADC_CTRL_DDR_EDGESEL_MASK); + default: + return -EINVAL; + } +} + +static int axi_adc_iodelays_set(struct iio_backend *back, unsigned int lane, + unsigned int tap) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + int ret; + u32 val; + + if (tap > FIELD_MAX(AXI_ADC_DELAY_CTRL_MASK)) + return -EINVAL; + if (lane > ADI_AXI_ADC_MAX_IO_NUM_LANES) + return -EINVAL; + + guard(mutex)(&st->lock); + ret = regmap_write(st->regmap, ADI_AXI_ADC_REG_DELAY(lane), tap); + if (ret) + return ret; + /* + * If readback is ~0, that means there are issues with the + * delay_clk. + */ + ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_DELAY(lane), &val); + if (ret) + return ret; + if (val == U32_MAX) + return -EIO; + + return 0; +} + +static int axi_adc_test_pattern_set(struct iio_backend *back, + unsigned int chan, + enum iio_backend_test_pattern pattern) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + + switch (pattern) { + case IIO_BACKEND_NO_TEST_PATTERN: + /* nothing to do */ + return 0; + case IIO_BACKEND_ADI_PRBS_9A: + return regmap_update_bits(st->regmap, ADI_AXI_ADC_REG_CHAN_CTRL_3(chan), + ADI_AXI_ADC_CHAN_PN_SEL_MASK, + FIELD_PREP(ADI_AXI_ADC_CHAN_PN_SEL_MASK, 0)); + default: + return -EINVAL; + } +} + +static int axi_adc_chan_status(struct iio_backend *back, unsigned int chan, + bool *error) +{ + struct adi_axi_adc_state *st = iio_backend_get_priv(back); + int ret; + u32 val; + + guard(mutex)(&st->lock); + /* reset test bits by setting them */ + ret = regmap_write(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan), + ADI_AXI_ADC_CHAN_STAT_PN_MASK); + if (ret) + return ret; + + /* let's give enough time to validate or erroring the incoming pattern */ + fsleep(1000); + + ret = regmap_read(st->regmap, ADI_AXI_ADC_REG_CHAN_STATUS(chan), &val); + if (ret) + return ret; + + if (ADI_AXI_ADC_CHAN_STAT_PN_MASK & val) + *error = true; + else + *error = false; + + return 0; +} + static int axi_adc_chan_enable(struct iio_backend *back, unsigned int chan) { struct adi_axi_adc_state *st = iio_backend_get_priv(back); @@ -124,26 +239,12 @@ static struct iio_buffer *axi_adc_request_buffer(struct iio_backend *back, struct iio_dev *indio_dev) { struct adi_axi_adc_state *st = iio_backend_get_priv(back); - struct iio_buffer *buffer; const char *dma_name; - int ret; if (device_property_read_string(st->dev, "dma-names", &dma_name)) dma_name = "rx"; - buffer = iio_dmaengine_buffer_alloc(st->dev, dma_name); - if (IS_ERR(buffer)) { - dev_err(st->dev, "Could not get DMA buffer, %ld\n", - PTR_ERR(buffer)); - return ERR_CAST(buffer); - } - - indio_dev->modes |= INDIO_BUFFER_HARDWARE; - ret = iio_device_attach_buffer(indio_dev, buffer); - if (ret) - return ERR_PTR(ret); - - return buffer; + return iio_dmaengine_buffer_setup(st->dev, indio_dev, dma_name); } static void axi_adc_free_buffer(struct iio_backend *back, @@ -156,7 +257,6 @@ static const struct regmap_config axi_adc_regmap_config = { .val_bits = 32, .reg_bits = 32, .reg_stride = 4, - .max_register = 0x0800, }; static const struct iio_backend_ops adi_axi_adc_generic = { @@ -167,6 +267,10 @@ static const struct iio_backend_ops adi_axi_adc_generic = { .chan_disable = axi_adc_chan_disable, .request_buffer = axi_adc_request_buffer, .free_buffer = axi_adc_free_buffer, + .data_sample_trigger = axi_adc_data_sample_trigger, + .iodelay_set = axi_adc_iodelays_set, + .test_pattern_set = axi_adc_test_pattern_set, + .chan_status = axi_adc_chan_status, }; static int adi_axi_adc_probe(struct platform_device *pdev) diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 614de96448..78fada4b7b 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -538,7 +538,7 @@ static int exynos_read_raw(struct iio_dev *indio_dev, long mask) { struct exynos_adc *info = iio_priv(indio_dev); - unsigned long timeout; + unsigned long time_left; int ret; if (mask == IIO_CHAN_INFO_SCALE) { @@ -562,9 +562,9 @@ static int exynos_read_raw(struct iio_dev *indio_dev, if (info->data->start_conv) info->data->start_conv(info, chan->address); - timeout = wait_for_completion_timeout(&info->completion, - EXYNOS_ADC_TIMEOUT); - if (timeout == 0) { + time_left = wait_for_completion_timeout(&info->completion, + EXYNOS_ADC_TIMEOUT); + if (time_left == 0) { dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n"); if (info->data->init_hw) info->data->init_hw(info); @@ -583,7 +583,7 @@ static int exynos_read_raw(struct iio_dev *indio_dev, static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y) { struct exynos_adc *info = iio_priv(indio_dev); - unsigned long timeout; + unsigned long time_left; int ret; mutex_lock(&info->lock); @@ -597,9 +597,9 @@ static int exynos_read_s3c64xx_ts(struct iio_dev *indio_dev, int *x, int *y) /* Select the ts channel to be used and Trigger conversion */ info->data->start_conv(info, ADC_S3C2410_MUX_TS); - timeout = wait_for_completion_timeout(&info->completion, - EXYNOS_ADC_TIMEOUT); - if (timeout == 0) { + time_left = wait_for_completion_timeout(&info->completion, + EXYNOS_ADC_TIMEOUT); + if (time_left == 0) { dev_warn(&indio_dev->dev, "Conversion timed out! Resetting\n"); if (info->data->init_hw) info->data->init_hw(info); diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c index 68c813de06..b680690631 100644 --- a/drivers/iio/adc/fsl-imx25-gcq.c +++ b/drivers/iio/adc/fsl-imx25-gcq.c @@ -12,8 +12,9 @@ #include <linux/interrupt.h> #include <linux/mfd/imx25-tsadc.h> #include <linux/module.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/regmap.h> #include <linux/regulator/consumer.h> @@ -107,7 +108,7 @@ static int mx25_gcq_get_raw_value(struct device *dev, struct mx25_gcq_priv *priv, int *val) { - long timeout; + long time_left; u32 data; /* Setup the configuration we want to use */ @@ -120,12 +121,12 @@ static int mx25_gcq_get_raw_value(struct device *dev, regmap_update_bits(priv->regs, MX25_ADCQ_CR, MX25_ADCQ_CR_FQS, MX25_ADCQ_CR_FQS); - timeout = wait_for_completion_interruptible_timeout( + time_left = wait_for_completion_interruptible_timeout( &priv->completed, MX25_GCQ_TIMEOUT); - if (timeout < 0) { + if (time_left < 0) { dev_err(dev, "ADC wait for measurement failed\n"); - return timeout; - } else if (timeout == 0) { + return time_left; + } else if (time_left == 0) { dev_err(dev, "ADC timed out\n"); return -ETIMEDOUT; } @@ -198,8 +199,6 @@ static int mx25_gcq_ext_regulator_setup(struct device *dev, static int mx25_gcq_setup_cfgs(struct platform_device *pdev, struct mx25_gcq_priv *priv) { - struct device_node *np = pdev->dev.of_node; - struct device_node *child; struct device *dev = &pdev->dev; int ret, i; @@ -216,37 +215,30 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, MX25_ADCQ_CFG_IN(i) | MX25_ADCQ_CFG_REFN_NGND2); - for_each_child_of_node(np, child) { + device_for_each_child_node_scoped(dev, child) { u32 reg; u32 refp = MX25_ADCQ_CFG_REFP_INT; u32 refn = MX25_ADCQ_CFG_REFN_NGND2; - ret = of_property_read_u32(child, "reg", ®); - if (ret) { - dev_err(dev, "Failed to get reg property\n"); - of_node_put(child); - return ret; - } + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) + return dev_err_probe(dev, ret, + "Failed to get reg property\n"); - if (reg >= MX25_NUM_CFGS) { - dev_err(dev, + if (reg >= MX25_NUM_CFGS) + return dev_err_probe(dev, -EINVAL, "reg value is greater than the number of available configuration registers\n"); - of_node_put(child); - return -EINVAL; - } - of_property_read_u32(child, "fsl,adc-refp", &refp); - of_property_read_u32(child, "fsl,adc-refn", &refn); + fwnode_property_read_u32(child, "fsl,adc-refp", &refp); + fwnode_property_read_u32(child, "fsl,adc-refn", &refn); switch (refp) { case MX25_ADC_REFP_EXT: case MX25_ADC_REFP_XP: case MX25_ADC_REFP_YP: ret = mx25_gcq_ext_regulator_setup(&pdev->dev, priv, refp); - if (ret) { - of_node_put(child); + if (ret) return ret; - } priv->channel_vref_mv[reg] = regulator_get_voltage(priv->vref[refp]); /* Conversion from uV to mV */ @@ -256,9 +248,8 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, priv->channel_vref_mv[reg] = 2500; break; default: - dev_err(dev, "Invalid positive reference %d\n", refp); - of_node_put(child); - return -EINVAL; + return dev_err_probe(dev, -EINVAL, + "Invalid positive reference %d\n", refp); } /* @@ -268,16 +259,13 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, refp = MX25_ADCQ_CFG_REFP(refp); refn = MX25_ADCQ_CFG_REFN(refn); - if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) { - dev_err(dev, "Invalid fsl,adc-refp property value\n"); - of_node_put(child); - return -EINVAL; - } - if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) { - dev_err(dev, "Invalid fsl,adc-refn property value\n"); - of_node_put(child); - return -EINVAL; - } + if ((refp & MX25_ADCQ_CFG_REFP_MASK) != refp) + return dev_err_probe(dev, -EINVAL, + "Invalid fsl,adc-refp property value\n"); + + if ((refn & MX25_ADCQ_CFG_REFN_MASK) != refn) + return dev_err_probe(dev, -EINVAL, + "Invalid fsl,adc-refn property value\n"); regmap_update_bits(priv->regs, MX25_ADCQ_CFG(reg), MX25_ADCQ_CFG_REFP_MASK | @@ -294,6 +282,17 @@ static int mx25_gcq_setup_cfgs(struct platform_device *pdev, return 0; } +static void mx25_gcq_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +/* Custom handling needed as this driver doesn't own the clock */ +static void mx25_gcq_clk_disable(void *clk) +{ + clk_disable_unprepare(clk); +} + static int mx25_gcq_probe(struct platform_device *pdev) { struct iio_dev *indio_dev; @@ -315,10 +314,9 @@ static int mx25_gcq_probe(struct platform_device *pdev) return PTR_ERR(mem); priv->regs = devm_regmap_init_mmio(dev, mem, &mx25_gcq_regconfig); - if (IS_ERR(priv->regs)) { - dev_err(dev, "Failed to initialize regmap\n"); - return PTR_ERR(priv->regs); - } + if (IS_ERR(priv->regs)) + return dev_err_probe(dev, PTR_ERR(priv->regs), + "Failed to initialize regmap\n"); mutex_init(&priv->lock); @@ -334,69 +332,44 @@ static int mx25_gcq_probe(struct platform_device *pdev) ret = regulator_enable(priv->vref[i]); if (ret) - goto err_regulator_disable; + return ret; + + ret = devm_add_action_or_reset(dev, mx25_gcq_reg_disable, + priv->vref[i]); + if (ret) + return ret; } priv->clk = tsadc->clk; ret = clk_prepare_enable(priv->clk); - if (ret) { - dev_err(dev, "Failed to enable clock\n"); - goto err_vref_disable; - } + if (ret) + return dev_err_probe(dev, ret, "Failed to enable clock\n"); + + ret = devm_add_action_or_reset(dev, mx25_gcq_clk_disable, + priv->clk); + if (ret) + return ret; ret = platform_get_irq(pdev, 0); if (ret < 0) - goto err_clk_unprepare; + return ret; priv->irq = ret; - ret = request_irq(priv->irq, mx25_gcq_irq, 0, pdev->name, priv); - if (ret) { - dev_err(dev, "Failed requesting IRQ\n"); - goto err_clk_unprepare; - } + ret = devm_request_irq(dev, priv->irq, mx25_gcq_irq, 0, pdev->name, + priv); + if (ret) + return dev_err_probe(dev, ret, "Failed requesting IRQ\n"); indio_dev->channels = mx25_gcq_channels; indio_dev->num_channels = ARRAY_SIZE(mx25_gcq_channels); indio_dev->info = &mx25_gcq_iio_info; indio_dev->name = driver_name; - ret = iio_device_register(indio_dev); - if (ret) { - dev_err(dev, "Failed to register iio device\n"); - goto err_irq_free; - } - - platform_set_drvdata(pdev, indio_dev); + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "Failed to register iio device\n"); return 0; - -err_irq_free: - free_irq(priv->irq, priv); -err_clk_unprepare: - clk_disable_unprepare(priv->clk); -err_vref_disable: - i = 4; -err_regulator_disable: - for (; i-- > 0;) { - if (priv->vref[i]) - regulator_disable(priv->vref[i]); - } - return ret; -} - -static void mx25_gcq_remove(struct platform_device *pdev) -{ - struct iio_dev *indio_dev = platform_get_drvdata(pdev); - struct mx25_gcq_priv *priv = iio_priv(indio_dev); - int i; - - iio_device_unregister(indio_dev); - free_irq(priv->irq, priv); - clk_disable_unprepare(priv->clk); - for (i = 4; i-- > 0;) { - if (priv->vref[i]) - regulator_disable(priv->vref[i]); - } } static const struct of_device_id mx25_gcq_ids[] = { @@ -411,7 +384,6 @@ static struct platform_driver mx25_gcq_driver = { .of_match_table = mx25_gcq_ids, }, .probe = mx25_gcq_probe, - .remove_new = mx25_gcq_remove, }; module_platform_driver(mx25_gcq_driver); diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index c80c55fb8c..fef97c1d22 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -7,7 +7,7 @@ #include <linux/err.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/of.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include <linux/property.h> #include <linux/slab.h> @@ -459,7 +459,6 @@ static const struct iio_chan_spec hx711_chan_spec[] = { static int hx711_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; struct hx711_data *hx711_data; struct iio_dev *indio_dev; int ret; @@ -533,7 +532,7 @@ static int hx711_probe(struct platform_device *pdev) hx711_data->gain_chan_a = 128; hx711_data->clock_frequency = 400000; - ret = of_property_read_u32(np, "clock-frequency", + ret = device_property_read_u32(&pdev->dev, "clock-frequency", &hx711_data->clock_frequency); /* diff --git a/drivers/iio/adc/intel_mrfld_adc.c b/drivers/iio/adc/intel_mrfld_adc.c index 7263ad7612..c7f40ae6e6 100644 --- a/drivers/iio/adc/intel_mrfld_adc.c +++ b/drivers/iio/adc/intel_mrfld_adc.c @@ -75,7 +75,7 @@ static int mrfld_adc_single_conv(struct iio_dev *indio_dev, struct mrfld_adc *adc = iio_priv(indio_dev); struct regmap *regmap = adc->regmap; unsigned int req; - long timeout; + long time_left; __be16 value; int ret; @@ -95,13 +95,13 @@ static int mrfld_adc_single_conv(struct iio_dev *indio_dev, if (ret) goto done; - timeout = wait_for_completion_interruptible_timeout(&adc->completion, - BCOVE_ADC_TIMEOUT); - if (timeout < 0) { - ret = timeout; + time_left = wait_for_completion_interruptible_timeout(&adc->completion, + BCOVE_ADC_TIMEOUT); + if (time_left < 0) { + ret = time_left; goto done; } - if (timeout == 0) { + if (time_left == 0) { ret = -ETIMEDOUT; goto done; } diff --git a/drivers/iio/adc/max11410.c b/drivers/iio/adc/max11410.c index 6af829349b..45368850b2 100644 --- a/drivers/iio/adc/max11410.c +++ b/drivers/iio/adc/max11410.c @@ -696,7 +696,6 @@ static int max11410_parse_channels(struct max11410_state *st, struct device *dev = &st->spi_dev->dev; struct max11410_channel_config *cfg; struct iio_chan_spec *channels; - struct fwnode_handle *child; u32 reference, sig_path; const char *node_name; u32 inputs[2], scale; @@ -720,7 +719,7 @@ static int max11410_parse_channels(struct max11410_state *st, if (!st->channels) return -ENOMEM; - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { node_name = fwnode_get_name(child); if (fwnode_property_present(child, "diff-channels")) { ret = fwnode_property_read_u32_array(child, @@ -735,47 +734,37 @@ static int max11410_parse_channels(struct max11410_state *st, inputs[1] = 0; chanspec.differential = 0; } - if (ret) { - fwnode_handle_put(child); + if (ret) return ret; - } if (inputs[0] > MAX11410_CHANNEL_INDEX_MAX || - inputs[1] > MAX11410_CHANNEL_INDEX_MAX) { - fwnode_handle_put(child); + inputs[1] > MAX11410_CHANNEL_INDEX_MAX) return dev_err_probe(&indio_dev->dev, -EINVAL, "Invalid channel index for %s, should be less than %d\n", node_name, MAX11410_CHANNEL_INDEX_MAX + 1); - } cfg = &st->channels[chan_idx]; reference = MAX11410_REFSEL_AVDD_AGND; fwnode_property_read_u32(child, "adi,reference", &reference); - if (reference > MAX11410_REFSEL_MAX) { - fwnode_handle_put(child); + if (reference > MAX11410_REFSEL_MAX) return dev_err_probe(&indio_dev->dev, -EINVAL, "Invalid adi,reference value for %s, should be less than %d.\n", node_name, MAX11410_REFSEL_MAX + 1); - } if (!max11410_get_vrefp(st, reference) || - (!max11410_get_vrefn(st, reference) && reference <= 2)) { - fwnode_handle_put(child); + (!max11410_get_vrefn(st, reference) && reference <= 2)) return dev_err_probe(&indio_dev->dev, -EINVAL, "Invalid VREF configuration for %s, either specify corresponding VREF regulators or change adi,reference property.\n", node_name); - } sig_path = MAX11410_PGA_SIG_PATH_BUFFERED; fwnode_property_read_u32(child, "adi,input-mode", &sig_path); - if (sig_path > MAX11410_SIG_PATH_MAX) { - fwnode_handle_put(child); + if (sig_path > MAX11410_SIG_PATH_MAX) return dev_err_probe(&indio_dev->dev, -EINVAL, "Invalid adi,input-mode value for %s, should be less than %d.\n", node_name, MAX11410_SIG_PATH_MAX + 1); - } fwnode_property_read_u32(child, "settling-time-us", &cfg->settling_time_us); @@ -793,10 +782,8 @@ static int max11410_parse_channels(struct max11410_state *st, cfg->scale_avail = devm_kcalloc(dev, MAX11410_SCALE_AVAIL_SIZE * 2, sizeof(*cfg->scale_avail), GFP_KERNEL); - if (!cfg->scale_avail) { - fwnode_handle_put(child); + if (!cfg->scale_avail) return -ENOMEM; - } scale = max11410_get_scale(st, *cfg); for (i = 0; i < MAX11410_SCALE_AVAIL_SIZE; i++) { diff --git a/drivers/iio/adc/mcp3564.c b/drivers/iio/adc/mcp3564.c index 311b613b60..e2ae13f1e8 100644 --- a/drivers/iio/adc/mcp3564.c +++ b/drivers/iio/adc/mcp3564.c @@ -998,7 +998,6 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev) struct mcp3564_state *adc = iio_priv(indio_dev); struct device *dev = &adc->spi->dev; struct iio_chan_spec *channels; - struct fwnode_handle *child; struct iio_chan_spec chanspec = mcp3564_channel_template; struct iio_chan_spec temp_chanspec = mcp3564_temp_channel_template; struct iio_chan_spec burnout_chanspec = mcp3564_burnout_channel_template; @@ -1025,7 +1024,7 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev) if (!channels) return dev_err_probe(dev, -ENOMEM, "Can't allocate memory\n"); - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { node_name = fwnode_get_name(child); if (fwnode_property_present(child, "diff-channels")) { @@ -1033,26 +1032,25 @@ static int mcp3564_parse_fw_children(struct iio_dev *indio_dev) "diff-channels", inputs, ARRAY_SIZE(inputs)); + if (ret) + return ret; + chanspec.differential = 1; } else { ret = fwnode_property_read_u32(child, "reg", &inputs[0]); + if (ret) + return ret; chanspec.differential = 0; inputs[1] = MCP3564_AGND; } - if (ret) { - fwnode_handle_put(child); - return ret; - } if (inputs[0] > MCP3564_INTERNAL_VCM || - inputs[1] > MCP3564_INTERNAL_VCM) { - fwnode_handle_put(child); + inputs[1] > MCP3564_INTERNAL_VCM) return dev_err_probe(&indio_dev->dev, -EINVAL, "Channel index > %d, for %s\n", MCP3564_INTERNAL_VCM + 1, node_name); - } chanspec.address = (inputs[0] << 4) | inputs[1]; chanspec.channel = inputs[0]; diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c index 2e60c10ee4..8c7b64e78d 100644 --- a/drivers/iio/adc/mxs-lradc-adc.c +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -724,7 +724,6 @@ static int mxs_lradc_adc_probe(struct platform_device *pdev) iio->dev.of_node = dev->parent->of_node; iio->info = &mxs_lradc_adc_iio_info; iio->modes = INDIO_DIRECT_MODE; - iio->masklength = LRADC_MAX_TOTAL_CHANS; if (lradc->soc == IMX23_LRADC) { iio->channels = mx23_lradc_chan_spec; diff --git a/drivers/iio/adc/pac1934.c b/drivers/iio/adc/pac1934.c index 8a0c357422..456f12faa3 100644 --- a/drivers/iio/adc/pac1934.c +++ b/drivers/iio/adc/pac1934.c @@ -1088,8 +1088,8 @@ static int pac1934_chip_identify(struct pac1934_chip_info *info) * documentation related to the ACPI device definition * https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ApplicationNotes/ApplicationNotes/PAC1934-Integration-Notes-for-Microsoft-Windows-10-and-Windows-11-Driver-Support-DS00002534.pdf */ -static bool pac1934_acpi_parse_channel_config(struct i2c_client *client, - struct pac1934_chip_info *info) +static int pac1934_acpi_parse_channel_config(struct i2c_client *client, + struct pac1934_chip_info *info) { acpi_handle handle; union acpi_object *rez; @@ -1104,7 +1104,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client, rez = acpi_evaluate_dsm(handle, &guid, 0, PAC1934_ACPI_GET_NAMES_AND_MOHMS_VALS, NULL); if (!rez) - return false; + return -EINVAL; for (i = 0; i < rez->package.count; i += 2) { idx = i / 2; @@ -1127,7 +1127,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client, * and assign the default sampling rate */ info->sample_rate_value = PAC1934_DEFAULT_CHIP_SAMP_SPEED_HZ; - return true; + return 0; } for (i = 0; i < rez->package.count; i++) { @@ -1140,7 +1140,7 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client, rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_BIPOLAR_SETTINGS, NULL); if (!rez) - return false; + return -EINVAL; bi_dir_mask = rez->package.elements[0].integer.value; info->bi_dir[0] = ((bi_dir_mask & (1 << 3)) | (bi_dir_mask & (1 << 7))) != 0; @@ -1152,19 +1152,18 @@ static bool pac1934_acpi_parse_channel_config(struct i2c_client *client, rez = acpi_evaluate_dsm(handle, &guid, 1, PAC1934_ACPI_GET_SAMP, NULL); if (!rez) - return false; + return -EINVAL; info->sample_rate_value = rez->package.elements[0].integer.value; ACPI_FREE(rez); - return true; + return 0; } -static bool pac1934_of_parse_channel_config(struct i2c_client *client, - struct pac1934_chip_info *info) +static int pac1934_fw_parse_channel_config(struct i2c_client *client, + struct pac1934_chip_info *info) { - struct fwnode_handle *node, *fwnode; struct device *dev = &client->dev; unsigned int current_channel; int idx, ret; @@ -1172,46 +1171,38 @@ static bool pac1934_of_parse_channel_config(struct i2c_client *client, info->sample_rate_value = 1024; current_channel = 1; - fwnode = dev_fwnode(dev); - fwnode_for_each_available_child_node(fwnode, node) { + device_for_each_child_node_scoped(dev, node) { ret = fwnode_property_read_u32(node, "reg", &idx); - if (ret) { - dev_err_probe(dev, ret, - "reading invalid channel index\n"); - goto err_fwnode; - } + if (ret) + return dev_err_probe(dev, ret, + "reading invalid channel index\n"); + /* adjust idx to match channel index (1 to 4) from the datasheet */ idx--; if (current_channel >= (info->phys_channels + 1) || - idx >= info->phys_channels || idx < 0) { - dev_err_probe(dev, -EINVAL, - "%s: invalid channel_index %d value\n", - fwnode_get_name(node), idx); - goto err_fwnode; - } + idx >= info->phys_channels || idx < 0) + return dev_err_probe(dev, -EINVAL, + "%s: invalid channel_index %d value\n", + fwnode_get_name(node), idx); /* enable channel */ info->active_channels[idx] = true; ret = fwnode_property_read_u32(node, "shunt-resistor-micro-ohms", &info->shunts[idx]); - if (ret) { - dev_err_probe(dev, ret, - "%s: invalid shunt-resistor value: %d\n", - fwnode_get_name(node), info->shunts[idx]); - goto err_fwnode; - } + if (ret) + return dev_err_probe(dev, ret, + "%s: invalid shunt-resistor value: %d\n", + fwnode_get_name(node), info->shunts[idx]); if (fwnode_property_present(node, "label")) { ret = fwnode_property_read_string(node, "label", (const char **)&info->labels[idx]); - if (ret) { - dev_err_probe(dev, ret, - "%s: invalid rail-name value\n", - fwnode_get_name(node)); - goto err_fwnode; - } + if (ret) + return dev_err_probe(dev, ret, + "%s: invalid rail-name value\n", + fwnode_get_name(node)); } info->bi_dir[idx] = fwnode_property_read_bool(node, "bipolar"); @@ -1219,12 +1210,7 @@ static bool pac1934_of_parse_channel_config(struct i2c_client *client, current_channel++; } - return true; - -err_fwnode: - fwnode_handle_put(node); - - return false; + return 0; } static void pac1934_cancel_delayed_work(void *dwork) @@ -1494,7 +1480,6 @@ static int pac1934_probe(struct i2c_client *client) const struct pac1934_features *chip; struct iio_dev *indio_dev; int cnt, ret; - bool match = false; struct device *dev = &client->dev; indio_dev = devm_iio_device_alloc(dev, sizeof(*info)); @@ -1528,16 +1513,16 @@ static int pac1934_probe(struct i2c_client *client) } if (acpi_match_device(dev->driver->acpi_match_table, dev)) - match = pac1934_acpi_parse_channel_config(client, info); + ret = pac1934_acpi_parse_channel_config(client, info); else /* * This makes it possible to use also ACPI PRP0001 for * registering the device using device tree properties. */ - match = pac1934_of_parse_channel_config(client, info); + ret = pac1934_fw_parse_channel_config(client, info); - if (!match) - return dev_err_probe(dev, -EINVAL, + if (ret) + return dev_err_probe(dev, ret, "parameter parsing returned an error\n"); mutex_init(&info->lock); diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c index b6b612d733..9b69f40bee 100644 --- a/drivers/iio/adc/qcom-spmi-adc5.c +++ b/drivers/iio/adc/qcom-spmi-adc5.c @@ -825,7 +825,6 @@ static int adc5_get_fw_data(struct adc5_chip *adc) const struct adc5_channels *adc_chan; struct iio_chan_spec *iio_chan; struct adc5_channel_prop prop, *chan_props; - struct fwnode_handle *child; unsigned int index = 0; int ret; @@ -849,12 +848,10 @@ static int adc5_get_fw_data(struct adc5_chip *adc) if (!adc->data) adc->data = &adc5_data_pmic; - device_for_each_child_node(adc->dev, child) { + device_for_each_child_node_scoped(adc->dev, child) { ret = adc5_get_fw_channel_data(adc, &prop, child, adc->data); - if (ret) { - fwnode_handle_put(child); + if (ret) return ret; - } prop.scale_fn_type = adc->data->adc_chans[prop.channel].scale_fn_type; diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index d524f2e8e9..15a21d2860 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -318,7 +318,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) struct rcar_gyroadc *priv = iio_priv(indio_dev); struct device *dev = priv->dev; struct device_node *np = dev->of_node; - struct device_node *child; struct regulator *vref; unsigned int reg; unsigned int adcmode = -1, childmode; @@ -326,7 +325,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) unsigned int num_channels; int ret, first = 1; - for_each_child_of_node(np, child) { + for_each_available_child_of_node_scoped(np, child) { of_id = of_match_node(rcar_gyroadc_child_match, child); if (!of_id) { dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".", @@ -352,7 +351,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) num_channels = ARRAY_SIZE(rcar_gyroadc_iio_channels_3); break; default: - goto err_e_inval; + return -EINVAL; } /* @@ -369,7 +368,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) dev_err(dev, "Failed to get child reg property of ADC \"%pOFn\".\n", child); - goto err_of_node_put; + return ret; } /* Channel number is too high. */ @@ -377,7 +376,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) dev_err(dev, "Only %i channels supported with %pOFn, but reg = <%i>.\n", num_channels, child, reg); - goto err_e_inval; + return -EINVAL; } } @@ -386,7 +385,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) dev_err(dev, "Channel %i uses different ADC mode than the rest.\n", reg); - goto err_e_inval; + return -EINVAL; } /* Channel is valid, grab the regulator. */ @@ -396,8 +395,7 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) if (IS_ERR(vref)) { dev_dbg(dev, "Channel %i 'vref' supply not connected.\n", reg); - ret = PTR_ERR(vref); - goto err_of_node_put; + return PTR_ERR(vref); } priv->vref[reg] = vref; @@ -422,7 +420,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) * we can stop parsing here. */ if (childmode == RCAR_GYROADC_MODE_SELECT_1_MB88101A) { - of_node_put(child); break; } } @@ -433,12 +430,6 @@ static int rcar_gyroadc_parse_subdevs(struct iio_dev *indio_dev) } return 0; - -err_e_inval: - ret = -EINVAL; -err_of_node_put: - of_node_put(child); - return ret; } static void rcar_gyroadc_deinit_supplies(struct iio_dev *indio_dev) diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c index a5464737e5..bcb1298409 100644 --- a/drivers/iio/adc/rtq6056.c +++ b/drivers/iio/adc/rtq6056.c @@ -520,32 +520,20 @@ static int rtq6056_adc_write_raw(struct iio_dev *indio_dev, { struct rtq6056_priv *priv = iio_priv(indio_dev); const struct richtek_dev_data *devdata = priv->devdata; - int ret; - ret = iio_device_claim_direct_mode(indio_dev); - if (ret) - return ret; - - switch (mask) { - case IIO_CHAN_INFO_SAMP_FREQ: - if (devdata->fixed_samp_freq) { - ret = -EINVAL; - break; + iio_device_claim_direct_scoped(return -EBUSY, indio_dev) { + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + if (devdata->fixed_samp_freq) + return -EINVAL; + return rtq6056_adc_set_samp_freq(priv, chan, val); + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: + return devdata->set_average(priv, val); + default: + return -EINVAL; } - - ret = rtq6056_adc_set_samp_freq(priv, chan, val); - break; - case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - ret = devdata->set_average(priv, val); - break; - default: - ret = -EINVAL; - break; } - - iio_device_release_direct_mode(indio_dev); - - return ret; + unreachable(); } static const char *rtq6056_channel_labels[RTQ6056_MAX_CHANNEL] = { diff --git a/drivers/iio/adc/rzg2l_adc.c b/drivers/iio/adc/rzg2l_adc.c index 0921ff2d9b..cd3a7e46ea 100644 --- a/drivers/iio/adc/rzg2l_adc.c +++ b/drivers/iio/adc/rzg2l_adc.c @@ -302,7 +302,6 @@ static irqreturn_t rzg2l_adc_isr(int irq, void *dev_id) static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l_adc *adc) { struct iio_chan_spec *chan_array; - struct fwnode_handle *fwnode; struct rzg2l_adc_data *data; unsigned int channel; int num_channels; @@ -330,17 +329,13 @@ static int rzg2l_adc_parse_properties(struct platform_device *pdev, struct rzg2l return -ENOMEM; i = 0; - device_for_each_child_node(&pdev->dev, fwnode) { + device_for_each_child_node_scoped(&pdev->dev, fwnode) { ret = fwnode_property_read_u32(fwnode, "reg", &channel); - if (ret) { - fwnode_handle_put(fwnode); + if (ret) return ret; - } - if (channel >= RZG2L_ADC_MAX_CHANNELS) { - fwnode_handle_put(fwnode); + if (channel >= RZG2L_ADC_MAX_CHANNELS) return -EINVAL; - } chan_array[i].type = IIO_VOLTAGE; chan_array[i].indexed = 1; diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c index 71362c2ddf..b6dd096391 100644 --- a/drivers/iio/adc/spear_adc.c +++ b/drivers/iio/adc/spear_adc.c @@ -5,8 +5,10 @@ * Copyright 2012 Stefan Roese <sr@denx.de> */ +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/interrupt.h> #include <linux/device.h> #include <linux/kernel.h> @@ -15,8 +17,6 @@ #include <linux/clk.h> #include <linux/err.h> #include <linux/completion.h> -#include <linux/of.h> -#include <linux/of_address.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -70,7 +70,7 @@ struct adc_regs_spear6xx { }; struct spear_adc_state { - struct device_node *np; + struct device *dev; struct adc_regs_spear3xx __iomem *adc_base_spear3xx; struct adc_regs_spear6xx __iomem *adc_base_spear6xx; struct clk *clk; @@ -123,7 +123,7 @@ static void spear_adc_set_ctrl(struct spear_adc_state *st, int n, static u32 spear_adc_get_average(struct spear_adc_state *st) { - if (of_device_is_compatible(st->np, "st,spear600-adc")) { + if (device_is_compatible(st->dev, "st,spear600-adc")) { return __raw_readl(&st->adc_base_spear6xx->average.msb) & SPEAR_ADC_DATA_MASK; } else { @@ -134,7 +134,7 @@ static u32 spear_adc_get_average(struct spear_adc_state *st) static void spear_adc_set_scanrate(struct spear_adc_state *st, u32 rate) { - if (of_device_is_compatible(st->np, "st,spear600-adc")) { + if (device_is_compatible(st->dev, "st,spear600-adc")) { __raw_writel(SPEAR600_ADC_SCAN_RATE_LO(rate), &st->adc_base_spear6xx->scan_rate_lo); __raw_writel(SPEAR600_ADC_SCAN_RATE_HI(rate), @@ -266,7 +266,6 @@ static const struct iio_info spear_adc_info = { static int spear_adc_probe(struct platform_device *pdev) { - struct device_node *np = pdev->dev.of_node; struct device *dev = &pdev->dev; struct spear_adc_state *st; struct iio_dev *indio_dev = NULL; @@ -279,11 +278,10 @@ static int spear_adc_probe(struct platform_device *pdev) "failed allocating iio device\n"); st = iio_priv(indio_dev); + st->dev = dev; mutex_init(&st->lock); - st->np = np; - /* * SPEAr600 has a different register layout than other SPEAr SoC's * (e.g. SPEAr3xx). Let's provide two register base addresses @@ -310,8 +308,7 @@ static int spear_adc_probe(struct platform_device *pdev) if (ret < 0) return dev_err_probe(dev, ret, "failed requesting interrupt\n"); - if (of_property_read_u32(np, "sampling-frequency", - &st->sampling_freq)) + if (device_property_read_u32(dev, "sampling-frequency", &st->sampling_freq)) return dev_err_probe(dev, -EINVAL, "sampling-frequency missing in DT\n"); @@ -319,13 +316,13 @@ static int spear_adc_probe(struct platform_device *pdev) * Optional avg_samples defaults to 0, resulting in single data * conversion */ - of_property_read_u32(np, "average-samples", &st->avg_samples); + device_property_read_u32(dev, "average-samples", &st->avg_samples); /* * Optional vref_external defaults to 0, resulting in internal vref * selection */ - of_property_read_u32(np, "vref-external", &st->vref_external); + device_property_read_u32(dev, "vref-external", &st->vref_external); spear_adc_configure(st); @@ -346,19 +343,17 @@ static int spear_adc_probe(struct platform_device *pdev) return 0; } -#ifdef CONFIG_OF static const struct of_device_id spear_adc_dt_ids[] = { { .compatible = "st,spear600-adc", }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, spear_adc_dt_ids); -#endif static struct platform_driver spear_adc_driver = { .probe = spear_adc_probe, .driver = { .name = SPEAR_ADC_MOD_NAME, - .of_match_table = of_match_ptr(spear_adc_dt_ids), + .of_match_table = spear_adc_dt_ids, }, }; diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index 283c207571..375aa7720f 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -1408,7 +1408,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, struct stm32_adc *adc = iio_priv(indio_dev); struct device *dev = indio_dev->dev.parent; const struct stm32_adc_regspec *regs = adc->cfg->regs; - long timeout; + long time_left; u32 val; int ret; @@ -1440,12 +1440,12 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, adc->cfg->start_conv(indio_dev, false); - timeout = wait_for_completion_interruptible_timeout( + time_left = wait_for_completion_interruptible_timeout( &adc->completion, STM32_ADC_TIMEOUT); - if (timeout == 0) { + if (time_left == 0) { ret = -ETIMEDOUT; - } else if (timeout < 0) { - ret = timeout; + } else if (time_left < 0) { + ret = time_left; } else { *res = adc->buffer[0]; ret = IIO_VAL_INT; @@ -2187,59 +2187,52 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, struct iio_chan_spec *channels) { const struct stm32_adc_info *adc_info = adc->cfg->adc_info; - struct fwnode_handle *child; + struct device *dev = &indio_dev->dev; const char *name; int val, scan_index = 0, ret; bool differential; u32 vin[2]; - device_for_each_child_node(&indio_dev->dev, child) { + device_for_each_child_node_scoped(dev, child) { ret = fwnode_property_read_u32(child, "reg", &val); - if (ret) { - dev_err(&indio_dev->dev, "Missing channel index %d\n", ret); - goto err; - } + if (ret) + return dev_err_probe(dev, ret, + "Missing channel index\n"); ret = fwnode_property_read_string(child, "label", &name); /* label is optional */ if (!ret) { - if (strlen(name) >= STM32_ADC_CH_SZ) { - dev_err(&indio_dev->dev, "Label %s exceeds %d characters\n", - name, STM32_ADC_CH_SZ); - ret = -EINVAL; - goto err; - } + if (strlen(name) >= STM32_ADC_CH_SZ) + return dev_err_probe(dev, -EINVAL, + "Label %s exceeds %d characters\n", + name, STM32_ADC_CH_SZ); + strscpy(adc->chan_name[val], name, STM32_ADC_CH_SZ); ret = stm32_adc_populate_int_ch(indio_dev, name, val); if (ret == -ENOENT) continue; else if (ret) - goto err; + return ret; } else if (ret != -EINVAL) { - dev_err(&indio_dev->dev, "Invalid label %d\n", ret); - goto err; + return dev_err_probe(dev, ret, "Invalid label\n"); } - if (val >= adc_info->max_channels) { - dev_err(&indio_dev->dev, "Invalid channel %d\n", val); - ret = -EINVAL; - goto err; - } + if (val >= adc_info->max_channels) + return dev_err_probe(dev, -EINVAL, + "Invalid channel %d\n", val); differential = false; ret = fwnode_property_read_u32_array(child, "diff-channels", vin, 2); /* diff-channels is optional */ if (!ret) { differential = true; - if (vin[0] != val || vin[1] >= adc_info->max_channels) { - dev_err(&indio_dev->dev, "Invalid channel in%d-in%d\n", - vin[0], vin[1]); - ret = -EINVAL; - goto err; - } + if (vin[0] != val || vin[1] >= adc_info->max_channels) + return dev_err_probe(dev, -EINVAL, + "Invalid channel in%d-in%d\n", + vin[0], vin[1]); } else if (ret != -EINVAL) { - dev_err(&indio_dev->dev, "Invalid diff-channels property %d\n", ret); - goto err; + return dev_err_probe(dev, ret, + "Invalid diff-channels property\n"); } stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val, @@ -2248,11 +2241,9 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, val = 0; ret = fwnode_property_read_u32(child, "st,min-sample-time-ns", &val); /* st,min-sample-time-ns is optional */ - if (ret && ret != -EINVAL) { - dev_err(&indio_dev->dev, "Invalid st,min-sample-time-ns property %d\n", - ret); - goto err; - } + if (ret && ret != -EINVAL) + return dev_err_probe(dev, ret, + "Invalid st,min-sample-time-ns property\n"); stm32_adc_smpr_init(adc, channels[scan_index].channel, val); if (differential) @@ -2262,11 +2253,6 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev, } return scan_index; - -err: - fwnode_handle_put(child); - - return ret; } static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping) diff --git a/drivers/iio/adc/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c index ca08ae3108..9a47d2c87f 100644 --- a/drivers/iio/adc/stm32-dfsdm-adc.c +++ b/drivers/iio/adc/stm32-dfsdm-adc.c @@ -1116,7 +1116,7 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *res) { struct stm32_dfsdm_adc *adc = iio_priv(indio_dev); - long timeout; + long time_left; int ret; reinit_completion(&adc->completion); @@ -1141,17 +1141,17 @@ static int stm32_dfsdm_single_conv(struct iio_dev *indio_dev, goto stop_dfsdm; } - timeout = wait_for_completion_interruptible_timeout(&adc->completion, - DFSDM_TIMEOUT); + time_left = wait_for_completion_interruptible_timeout(&adc->completion, + DFSDM_TIMEOUT); /* Mask IRQ for regular conversion achievement*/ regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id), DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0)); - if (timeout == 0) + if (time_left == 0) ret = -ETIMEDOUT; - else if (timeout < 0) - ret = timeout; + else if (time_left < 0) + ret = time_left; else ret = IIO_VAL_INT; diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 6ae967e4d8..d3363d02f2 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -902,10 +902,9 @@ static int ads1015_client_get_channels_config(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); struct ads1015_data *data = iio_priv(indio_dev); struct device *dev = &client->dev; - struct fwnode_handle *node; int i = -1; - device_for_each_child_node(dev, node) { + device_for_each_child_node_scoped(dev, node) { u32 pval; unsigned int channel; unsigned int pga = ADS1015_DEFAULT_PGA; @@ -927,7 +926,6 @@ static int ads1015_client_get_channels_config(struct i2c_client *client) pga = pval; if (pga > 5) { dev_err(dev, "invalid gain on %pfw\n", node); - fwnode_handle_put(node); return -EINVAL; } } @@ -936,7 +934,6 @@ static int ads1015_client_get_channels_config(struct i2c_client *client) data_rate = pval; if (data_rate > 7) { dev_err(dev, "invalid data_rate on %pfw\n", node); - fwnode_handle_put(node); return -EINVAL; } } diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index fcfc462543..cb04a29b3d 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -694,7 +694,6 @@ static int ads131e08_alloc_channels(struct iio_dev *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; @@ -736,10 +735,10 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev) return -ENOMEM; i = 0; - device_for_each_child_node(dev, node) { + device_for_each_child_node_scoped(dev, node) { ret = fwnode_property_read_u32(node, "reg", &channel); if (ret) - goto err_child_out; + return ret; ret = fwnode_property_read_u32(node, "ti,gain", &tmp); if (ret) { @@ -747,7 +746,7 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev) } else { ret = ads131e08_pga_gain_to_field_value(st, tmp); if (ret < 0) - goto err_child_out; + return ret; channel_config[i].pga_gain = tmp; } @@ -758,7 +757,7 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev) } else { ret = ads131e08_validate_channel_mux(st, tmp); if (ret) - goto err_child_out; + return ret; channel_config[i].mux = tmp; } @@ -785,9 +784,6 @@ static int ads131e08_alloc_channels(struct iio_dev *indio_dev) return 0; -err_child_out: - fwnode_handle_put(node); - return ret; } static void ads131e08_regulator_disable(void *data) diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c index 4a247ca25a..0253064fad 100644 --- a/drivers/iio/adc/twl4030-madc.c +++ b/drivers/iio/adc/twl4030-madc.c @@ -19,10 +19,12 @@ #include <linux/interrupt.h> #include <linux/kernel.h> #include <linux/delay.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> #include <linux/platform_device.h> +#include <linux/property.h> #include <linux/slab.h> #include <linux/mfd/twl.h> -#include <linux/module.h> #include <linux/stddef.h> #include <linux/mutex.h> #include <linux/bitops.h> @@ -30,7 +32,6 @@ #include <linux/types.h> #include <linux/gfp.h> #include <linux/err.h> -#include <linux/of.h> #include <linux/regulator/consumer.h> #include <linux/iio/iio.h> @@ -744,14 +745,14 @@ static int twl4030_madc_set_power(struct twl4030_madc_data *madc, int on) */ static int twl4030_madc_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct twl4030_madc_platform_data *pdata = dev_get_platdata(dev); struct twl4030_madc_data *madc; - struct twl4030_madc_platform_data *pdata = dev_get_platdata(&pdev->dev); - struct device_node *np = pdev->dev.of_node; int irq, ret; u8 regval; struct iio_dev *iio_dev = NULL; - if (!pdata && !np) { + if (!pdata && !dev_fwnode(dev)) { dev_err(&pdev->dev, "neither platform data nor Device Tree node available\n"); return -EINVAL; } @@ -779,7 +780,7 @@ static int twl4030_madc_probe(struct platform_device *pdev) if (pdata) madc->use_second_irq = (pdata->irq_line != 1); else - madc->use_second_irq = of_property_read_bool(np, + madc->use_second_irq = device_property_read_bool(dev, "ti,system-uses-second-madc-irq"); madc->imr = madc->use_second_irq ? TWL4030_MADC_IMR2 : @@ -905,20 +906,18 @@ static void twl4030_madc_remove(struct platform_device *pdev) regulator_disable(madc->usb3v1); } -#ifdef CONFIG_OF static const struct of_device_id twl_madc_of_match[] = { { .compatible = "ti,twl4030-madc", }, - { }, + { } }; MODULE_DEVICE_TABLE(of, twl_madc_of_match); -#endif static struct platform_driver twl4030_madc_driver = { .probe = twl4030_madc_probe, .remove_new = twl4030_madc_remove, .driver = { .name = "twl4030_madc", - .of_match_table = of_match_ptr(twl_madc_of_match), + .of_match_table = twl_madc_of_match, }, }; diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c index 78bf55438b..6a3db2bce4 100644 --- a/drivers/iio/adc/twl6030-gpadc.c +++ b/drivers/iio/adc/twl6030-gpadc.c @@ -519,7 +519,7 @@ static int twl6030_gpadc_read_raw(struct iio_dev *indio_dev, { struct twl6030_gpadc_data *gpadc = iio_priv(indio_dev); int ret; - long timeout; + long time_left; mutex_lock(&gpadc->lock); @@ -529,12 +529,12 @@ static int twl6030_gpadc_read_raw(struct iio_dev *indio_dev, goto err; } /* wait for conversion to complete */ - timeout = wait_for_completion_interruptible_timeout( + time_left = wait_for_completion_interruptible_timeout( &gpadc->irq_complete, msecs_to_jiffies(5000)); - if (timeout == 0) { + if (time_left == 0) { ret = -ETIMEDOUT; goto err; - } else if (timeout < 0) { + } else if (time_left < 0) { ret = -EINTR; goto err; } diff --git a/drivers/iio/addac/ad74115.c b/drivers/iio/addac/ad74115.c index e6bc5eb378..12dc43d487 100644 --- a/drivers/iio/addac/ad74115.c +++ b/drivers/iio/addac/ad74115.c @@ -199,7 +199,6 @@ struct ad74115_state { struct spi_device *spi; struct regmap *regmap; struct iio_trigger *trig; - struct regulator *avdd; /* * Synchronize consecutive operations when doing a one-shot @@ -1672,13 +1671,9 @@ static int ad74115_setup(struct iio_dev *indio_dev) if (ret) return ret; - if (val == AD74115_DIN_THRESHOLD_MODE_AVDD) { - ret = regulator_get_voltage(st->avdd); - if (ret < 0) - return ret; - - st->avdd_mv = ret / 1000; - } + if (val == AD74115_DIN_THRESHOLD_MODE_AVDD && !st->avdd_mv) + return dev_err_probe(dev, -EINVAL, + "AVDD voltage is required for digital input threshold mode AVDD\n"); st->din_threshold_mode = val; @@ -1788,11 +1783,6 @@ static int ad74115_reset(struct ad74115_state *st) return 0; } -static void ad74115_regulator_disable(void *data) -{ - regulator_disable(data); -} - static int ad74115_setup_trigger(struct iio_dev *indio_dev) { struct ad74115_state *st = iio_priv(indio_dev); @@ -1855,20 +1845,20 @@ static int ad74115_probe(struct spi_device *spi) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ad74115_info; - st->avdd = devm_regulator_get(dev, "avdd"); - if (IS_ERR(st->avdd)) - return PTR_ERR(st->avdd); - - ret = regulator_enable(st->avdd); - if (ret) { - dev_err(dev, "Failed to enable avdd regulator\n"); - return ret; + ret = devm_regulator_get_enable_read_voltage(dev, "avdd"); + if (ret < 0) { + /* + * Since this is both a power supply and only optionally a + * reference voltage, make sure to enable it even when the + * voltage is not available. + */ + ret = devm_regulator_get_enable(dev, "avdd"); + if (ret) + return dev_err_probe(dev, ret, "failed to enable avdd\n"); + } else { + st->avdd_mv = ret / 1000; } - ret = devm_add_action_or_reset(dev, ad74115_regulator_disable, st->avdd); - if (ret) - return ret; - ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(regulator_names), regulator_names); if (ret) diff --git a/drivers/iio/addac/ad74413r.c b/drivers/iio/addac/ad74413r.c index 7af3e4b8fe..cd26a16dc0 100644 --- a/drivers/iio/addac/ad74413r.c +++ b/drivers/iio/addac/ad74413r.c @@ -1255,21 +1255,15 @@ static int ad74413r_parse_channel_config(struct iio_dev *indio_dev, static int ad74413r_parse_channel_configs(struct iio_dev *indio_dev) { struct ad74413r_state *st = iio_priv(indio_dev); - struct fwnode_handle *channel_node = NULL; int ret; - fwnode_for_each_available_child_node(dev_fwnode(st->dev), channel_node) { + device_for_each_child_node_scoped(st->dev, channel_node) { ret = ad74413r_parse_channel_config(indio_dev, channel_node); if (ret) - goto put_channel_node; + return ret; } return 0; - -put_channel_node: - fwnode_handle_put(channel_node); - - return ret; } static int ad74413r_setup_channels(struct iio_dev *indio_dev) diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 5610ba6792..13b1a85896 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -195,6 +195,18 @@ static void _iio_dma_buffer_block_done(struct iio_dma_buffer_block *block) block->state = IIO_BLOCK_STATE_DONE; } +static void iio_dma_buffer_queue_wake(struct iio_dma_buffer_queue *queue) +{ + __poll_t flags; + + if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) + flags = EPOLLIN | EPOLLRDNORM; + else + flags = EPOLLOUT | EPOLLWRNORM; + + wake_up_interruptible_poll(&queue->buffer.pollq, flags); +} + /** * iio_dma_buffer_block_done() - Indicate that a block has been completed * @block: The completed block @@ -212,7 +224,7 @@ void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block) spin_unlock_irqrestore(&queue->list_lock, flags); iio_buffer_block_put_atomic(block); - wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM); + iio_dma_buffer_queue_wake(queue); } EXPORT_SYMBOL_GPL(iio_dma_buffer_block_done); @@ -241,7 +253,7 @@ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue, } spin_unlock_irqrestore(&queue->list_lock, flags); - wake_up_interruptible_poll(&queue->buffer.pollq, EPOLLIN | EPOLLRDNORM); + iio_dma_buffer_queue_wake(queue); } EXPORT_SYMBOL_GPL(iio_dma_buffer_block_list_abort); @@ -335,8 +347,24 @@ int iio_dma_buffer_request_update(struct iio_buffer *buffer) queue->fileio.blocks[i] = block; } - block->state = IIO_BLOCK_STATE_QUEUED; - list_add_tail(&block->head, &queue->incoming); + /* + * block->bytes_used may have been modified previously, e.g. by + * iio_dma_buffer_block_list_abort(). Reset it here to the + * block's so that iio_dma_buffer_io() will work. + */ + block->bytes_used = block->size; + + /* + * If it's an input buffer, mark the block as queued, and + * iio_dma_buffer_enable() will submit it. Otherwise mark it as + * done, which means it's ready to be dequeued. + */ + if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) { + block->state = IIO_BLOCK_STATE_QUEUED; + list_add_tail(&block->head, &queue->incoming); + } else { + block->state = IIO_BLOCK_STATE_DONE; + } } out_unlock: @@ -488,20 +516,12 @@ static struct iio_dma_buffer_block *iio_dma_buffer_dequeue( return block; } -/** - * iio_dma_buffer_read() - DMA buffer read callback - * @buffer: Buffer to read form - * @n: Number of bytes to read - * @user_buffer: Userspace buffer to copy the data to - * - * Should be used as the read callback for iio_buffer_access_ops - * struct for DMA buffers. - */ -int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, - char __user *user_buffer) +static int iio_dma_buffer_io(struct iio_buffer *buffer, size_t n, + char __user *user_buffer, bool is_from_user) { struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer); struct iio_dma_buffer_block *block; + void *addr; int ret; if (n < buffer->bytes_per_datum) @@ -524,8 +544,13 @@ int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, n = rounddown(n, buffer->bytes_per_datum); if (n > block->bytes_used - queue->fileio.pos) n = block->bytes_used - queue->fileio.pos; + addr = block->vaddr + queue->fileio.pos; - if (copy_to_user(user_buffer, block->vaddr + queue->fileio.pos, n)) { + if (is_from_user) + ret = copy_from_user(addr, user_buffer, n); + else + ret = copy_to_user(user_buffer, addr, n); + if (ret) { ret = -EFAULT; goto out_unlock; } @@ -544,16 +569,49 @@ out_unlock: return ret; } + +/** + * iio_dma_buffer_read() - DMA buffer read callback + * @buffer: Buffer to read form + * @n: Number of bytes to read + * @user_buffer: Userspace buffer to copy the data to + * + * Should be used as the read callback for iio_buffer_access_ops + * struct for DMA buffers. + */ +int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, + char __user *user_buffer) +{ + return iio_dma_buffer_io(buffer, n, user_buffer, false); +} EXPORT_SYMBOL_GPL(iio_dma_buffer_read); /** - * iio_dma_buffer_data_available() - DMA buffer data_available callback - * @buf: Buffer to check for data availability + * iio_dma_buffer_write() - DMA buffer write callback + * @buffer: Buffer to read form + * @n: Number of bytes to read + * @user_buffer: Userspace buffer to copy the data from * - * Should be used as the data_available callback for iio_buffer_access_ops + * Should be used as the write callback for iio_buffer_access_ops * struct for DMA buffers. */ -size_t iio_dma_buffer_data_available(struct iio_buffer *buf) +int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n, + const char __user *user_buffer) +{ + return iio_dma_buffer_io(buffer, n, + (__force __user char *)user_buffer, true); +} +EXPORT_SYMBOL_GPL(iio_dma_buffer_write); + +/** + * iio_dma_buffer_usage() - DMA buffer data_available and + * space_available callback + * @buf: Buffer to check for data availability + * + * Should be used as the data_available and space_available callbacks for + * iio_buffer_access_ops struct for DMA buffers. + */ +size_t iio_dma_buffer_usage(struct iio_buffer *buf) { struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buf); struct iio_dma_buffer_block *block; @@ -586,7 +644,7 @@ size_t iio_dma_buffer_data_available(struct iio_buffer *buf) return data_available; } -EXPORT_SYMBOL_GPL(iio_dma_buffer_data_available); +EXPORT_SYMBOL_GPL(iio_dma_buffer_usage); /** * iio_dma_buffer_set_bytes_per_datum() - DMA buffer set_bytes_per_datum callback diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index a18c1da292..918f6f8d65 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -64,14 +64,25 @@ static int iio_dmaengine_buffer_submit_block(struct iio_dma_buffer_queue *queue, struct dmaengine_buffer *dmaengine_buffer = iio_buffer_to_dmaengine_buffer(&queue->buffer); struct dma_async_tx_descriptor *desc; + enum dma_transfer_direction dma_dir; + size_t max_size; dma_cookie_t cookie; - block->bytes_used = min(block->size, dmaengine_buffer->max_size); - block->bytes_used = round_down(block->bytes_used, - dmaengine_buffer->align); + max_size = min(block->size, dmaengine_buffer->max_size); + max_size = round_down(max_size, dmaengine_buffer->align); + + if (queue->buffer.direction == IIO_BUFFER_DIRECTION_IN) { + block->bytes_used = max_size; + dma_dir = DMA_DEV_TO_MEM; + } else { + dma_dir = DMA_MEM_TO_DEV; + } + + if (!block->bytes_used || block->bytes_used > max_size) + return -EINVAL; desc = dmaengine_prep_slave_single(dmaengine_buffer->chan, - block->phys_addr, block->bytes_used, DMA_DEV_TO_MEM, + block->phys_addr, block->bytes_used, dma_dir, DMA_PREP_INTERRUPT); if (!desc) return -ENOMEM; @@ -112,12 +123,14 @@ static void iio_dmaengine_buffer_release(struct iio_buffer *buf) static const struct iio_buffer_access_funcs iio_dmaengine_buffer_ops = { .read = iio_dma_buffer_read, + .write = iio_dma_buffer_write, .set_bytes_per_datum = iio_dma_buffer_set_bytes_per_datum, .set_length = iio_dma_buffer_set_length, .request_update = iio_dma_buffer_request_update, .enable = iio_dma_buffer_enable, .disable = iio_dma_buffer_disable, - .data_available = iio_dma_buffer_data_available, + .data_available = iio_dma_buffer_usage, + .space_available = iio_dma_buffer_usage, .release = iio_dmaengine_buffer_release, .modes = INDIO_BUFFER_HARDWARE, @@ -159,7 +172,7 @@ static const struct iio_dev_attr *iio_dmaengine_buffer_attrs[] = { * Once done using the buffer iio_dmaengine_buffer_free() should be used to * release it. */ -struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, +static struct iio_buffer *iio_dmaengine_buffer_alloc(struct device *dev, const char *channel) { struct dmaengine_buffer *dmaengine_buffer; @@ -210,7 +223,6 @@ err_free: kfree(dmaengine_buffer); return ERR_PTR(ret); } -EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_alloc, IIO_DMAENGINE_BUFFER); /** * iio_dmaengine_buffer_free() - Free dmaengine buffer @@ -230,66 +242,64 @@ void iio_dmaengine_buffer_free(struct iio_buffer *buffer) } EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_free, IIO_DMAENGINE_BUFFER); -static void __devm_iio_dmaengine_buffer_free(void *buffer) -{ - iio_dmaengine_buffer_free(buffer); -} - -/** - * devm_iio_dmaengine_buffer_alloc() - Resource-managed iio_dmaengine_buffer_alloc() - * @dev: Parent device for the buffer - * @channel: DMA channel name, typically "rx". - * - * This allocates a new IIO buffer which internally uses the DMAengine framework - * to perform its transfers. The parent device will be used to request the DMA - * channel. - * - * The buffer will be automatically de-allocated once the device gets destroyed. - */ -static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, - const char *channel) +struct iio_buffer *iio_dmaengine_buffer_setup_ext(struct device *dev, + struct iio_dev *indio_dev, + const char *channel, + enum iio_buffer_direction dir) { struct iio_buffer *buffer; int ret; buffer = iio_dmaengine_buffer_alloc(dev, channel); if (IS_ERR(buffer)) - return buffer; + return ERR_CAST(buffer); - ret = devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free, - buffer); - if (ret) + indio_dev->modes |= INDIO_BUFFER_HARDWARE; + + buffer->direction = dir; + + ret = iio_device_attach_buffer(indio_dev, buffer); + if (ret) { + iio_dmaengine_buffer_free(buffer); return ERR_PTR(ret); + } return buffer; } +EXPORT_SYMBOL_NS_GPL(iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER); + +static void __devm_iio_dmaengine_buffer_free(void *buffer) +{ + iio_dmaengine_buffer_free(buffer); +} /** - * devm_iio_dmaengine_buffer_setup() - Setup a DMA buffer for an IIO device + * devm_iio_dmaengine_buffer_setup_ext() - 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". + * @dir: Direction of buffer (in or out) * * 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) +int devm_iio_dmaengine_buffer_setup_ext(struct device *dev, + struct iio_dev *indio_dev, + const char *channel, + enum iio_buffer_direction dir) { struct iio_buffer *buffer; - buffer = devm_iio_dmaengine_buffer_alloc(dev, channel); + buffer = iio_dmaengine_buffer_setup_ext(dev, indio_dev, channel, dir); if (IS_ERR(buffer)) return PTR_ERR(buffer); - indio_dev->modes |= INDIO_BUFFER_HARDWARE; - - return iio_device_attach_buffer(indio_dev, buffer); + return devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free, + buffer); } -EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup, IIO_DMAENGINE_BUFFER); +EXPORT_SYMBOL_NS_GPL(devm_iio_dmaengine_buffer_setup_ext, IIO_DMAENGINE_BUFFER); MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); MODULE_DESCRIPTION("DMA buffer for the IIO framework"); diff --git a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c index adceb1b7a0..f44458c380 100644 --- a/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c +++ b/drivers/iio/common/inv_sensors/inv_sensors_timestamp.c @@ -74,13 +74,13 @@ int inv_sensors_timestamp_update_odr(struct inv_sensors_timestamp *ts, } EXPORT_SYMBOL_NS_GPL(inv_sensors_timestamp_update_odr, IIO_INV_SENSORS_TIMESTAMP); -static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period, uint32_t mult) +static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t period) { uint32_t period_min, period_max; /* check that period is acceptable */ - period_min = ts->min_period * mult; - period_max = ts->max_period * mult; + period_min = ts->min_period * ts->mult; + period_max = ts->max_period * ts->mult; if (period > period_min && period < period_max) return true; else @@ -88,15 +88,15 @@ static bool inv_validate_period(struct inv_sensors_timestamp *ts, uint32_t perio } static bool inv_update_chip_period(struct inv_sensors_timestamp *ts, - uint32_t mult, uint32_t period) + uint32_t period) { uint32_t new_chip_period; - if (!inv_validate_period(ts, period, mult)) + if (!inv_validate_period(ts, period)) return false; /* update chip internal period estimation */ - new_chip_period = period / mult; + new_chip_period = period / ts->mult; inv_update_acc(&ts->chip_period, new_chip_period); ts->period = ts->mult * ts->chip_period.val; @@ -129,16 +129,14 @@ static void inv_align_timestamp_it(struct inv_sensors_timestamp *ts) } void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, - uint32_t fifo_period, size_t fifo_nb, - size_t sensor_nb, int64_t timestamp) + size_t sample_nb, int64_t timestamp) { struct inv_sensors_timestamp_interval *it; int64_t delta, interval; - const uint32_t fifo_mult = fifo_period / ts->chip.clock_period; uint32_t period; bool valid = false; - if (fifo_nb == 0) + if (sample_nb == 0) return; /* update interrupt timestamp and compute chip and sensor periods */ @@ -148,14 +146,14 @@ void inv_sensors_timestamp_interrupt(struct inv_sensors_timestamp *ts, delta = it->up - it->lo; if (it->lo != 0) { /* compute period: delta time divided by number of samples */ - period = div_s64(delta, fifo_nb); - valid = inv_update_chip_period(ts, fifo_mult, period); + period = div_s64(delta, sample_nb); + valid = inv_update_chip_period(ts, period); } /* no previous data, compute theoritical value from interrupt */ if (ts->timestamp == 0) { /* elapsed time: sensor period * sensor samples number */ - interval = (int64_t)ts->period * (int64_t)sensor_nb; + interval = (int64_t)ts->period * (int64_t)sample_nb; ts->timestamp = it->up - interval; return; } diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig index 34eb40bb95..ee0d9798d8 100644 --- a/drivers/iio/dac/Kconfig +++ b/drivers/iio/dac/Kconfig @@ -131,6 +131,43 @@ config AD5624R_SPI Say yes here to build support for Analog Devices AD5624R, AD5644R and AD5664R converters (DAC). This driver uses the common SPI interface. +config AD9739A + tristate "Analog Devices AD9739A RF DAC spi driver" + depends on SPI + select REGMAP_SPI + select IIO_BACKEND + help + Say yes here to build support for Analog Devices AD9739A Digital-to + Analog Converter. + + The driver requires the assistance of the AXI DAC IP core to operate, + since SPI is used for configuration only, while data has to be + streamed into memory via DMA. + + To compile this driver as a module, choose M here: the module will be + called ad9739a. + +config ADI_AXI_DAC + tristate "Analog Devices Generic AXI DAC IP core driver" + select IIO_BUFFER + select IIO_BUFFER_DMAENGINE + select REGMAP_MMIO + select IIO_BACKEND + help + Say yes here to build support for Analog Devices Generic + AXI DAC IP core. The IP core is used for interfacing with + digital-to-analog (DAC) converters that require either a high-speed + serial interface (JESD204B/C) or a source synchronous parallel + interface (LVDS/CMOS). + Typically (for such devices) SPI will be used for configuration only, + while this IP core handles the streaming of data into memory via DMA. + + Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip + If unsure, say N (but it's safe to say "Y"). + + To compile this driver as a module, choose M here: the + module will be called adi-axi-dac. + config LTC2688 tristate "Analog Devices LTC2688 DAC spi driver" depends on SPI diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile index 55bf89739d..8432a81a19 100644 --- a/drivers/iio/dac/Makefile +++ b/drivers/iio/dac/Makefile @@ -29,6 +29,8 @@ obj-$(CONFIG_AD5696_I2C) += ad5696-i2c.o obj-$(CONFIG_AD7293) += ad7293.o obj-$(CONFIG_AD7303) += ad7303.o obj-$(CONFIG_AD8801) += ad8801.o +obj-$(CONFIG_AD9739A) += ad9739a.o +obj-$(CONFIG_ADI_AXI_DAC) += adi-axi-dac.o obj-$(CONFIG_CIO_DAC) += cio-dac.o obj-$(CONFIG_DPOT_DAC) += dpot-dac.o obj-$(CONFIG_DS4424) += ds4424.o diff --git a/drivers/iio/dac/ad3552r.c b/drivers/iio/dac/ad3552r.c index a492e8f2fc..8aa942896b 100644 --- a/drivers/iio/dac/ad3552r.c +++ b/drivers/iio/dac/ad3552r.c @@ -801,51 +801,45 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac, u32 ch) { struct device *dev = &dac->spi->dev; - struct fwnode_handle *gain_child; u32 val; int err; u8 addr; u16 reg = 0, offset; - gain_child = fwnode_get_named_child_node(child, - "custom-output-range-config"); - if (!gain_child) { - dev_err(dev, - "mandatory custom-output-range-config property missing\n"); - return -EINVAL; - } + struct fwnode_handle *gain_child __free(fwnode_handle) + = fwnode_get_named_child_node(child, + "custom-output-range-config"); + if (!gain_child) + return dev_err_probe(dev, -EINVAL, + "mandatory custom-output-range-config property missing\n"); dac->ch_data[ch].range_override = 1; reg |= ad3552r_field_prep(1, AD3552R_MASK_CH_RANGE_OVERRIDE); err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-p", &val); - if (err) { - dev_err(dev, "mandatory adi,gain-scaling-p property missing\n"); - goto put_child; - } + if (err) + return dev_err_probe(dev, err, + "mandatory adi,gain-scaling-p property missing\n"); reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_P); dac->ch_data[ch].p = val; err = fwnode_property_read_u32(gain_child, "adi,gain-scaling-n", &val); - if (err) { - dev_err(dev, "mandatory adi,gain-scaling-n property missing\n"); - goto put_child; - } + if (err) + return dev_err_probe(dev, err, + "mandatory adi,gain-scaling-n property missing\n"); reg |= ad3552r_field_prep(val, AD3552R_MASK_CH_GAIN_SCALING_N); dac->ch_data[ch].n = val; err = fwnode_property_read_u32(gain_child, "adi,rfb-ohms", &val); - if (err) { - dev_err(dev, "mandatory adi,rfb-ohms property missing\n"); - goto put_child; - } + if (err) + return dev_err_probe(dev, err, + "mandatory adi,rfb-ohms property missing\n"); dac->ch_data[ch].rfb = val; err = fwnode_property_read_u32(gain_child, "adi,gain-offset", &val); - if (err) { - dev_err(dev, "mandatory adi,gain-offset property missing\n"); - goto put_child; - } + if (err) + return dev_err_probe(dev, err, + "mandatory adi,gain-offset property missing\n"); dac->ch_data[ch].gain_offset = val; offset = abs((s32)val); @@ -855,21 +849,14 @@ static int ad3552r_configure_custom_gain(struct ad3552r_desc *dac, addr = AD3552R_REG_ADDR_CH_GAIN(ch); err = ad3552r_write_reg(dac, addr, offset & AD3552R_MASK_CH_OFFSET_BITS_0_7); - if (err) { - dev_err(dev, "Error writing register\n"); - goto put_child; - } + if (err) + return dev_err_probe(dev, err, "Error writing register\n"); err = ad3552r_write_reg(dac, addr, reg); - if (err) { - dev_err(dev, "Error writing register\n"); - goto put_child; - } - -put_child: - fwnode_handle_put(gain_child); + if (err) + return dev_err_probe(dev, err, "Error writing register\n"); - return err; + return 0; } static void ad3552r_reg_disable(void *reg) @@ -880,7 +867,6 @@ static void ad3552r_reg_disable(void *reg) static int ad3552r_configure_device(struct ad3552r_desc *dac) { struct device *dev = &dac->spi->dev; - struct fwnode_handle *child; struct regulator *vref; int err, cnt = 0, voltage, delta = 100000; u32 vals[2], val, ch; @@ -949,53 +935,45 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac) return -ENODEV; } - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { err = fwnode_property_read_u32(child, "reg", &ch); - if (err) { - dev_err(dev, "mandatory reg property missing\n"); - goto put_child; - } - if (ch >= AD3552R_NUM_CH) { - dev_err(dev, "reg must be less than %d\n", - AD3552R_NUM_CH); - err = -EINVAL; - goto put_child; - } + if (err) + return dev_err_probe(dev, err, + "mandatory reg property missing\n"); + if (ch >= AD3552R_NUM_CH) + return dev_err_probe(dev, -EINVAL, + "reg must be less than %d\n", + AD3552R_NUM_CH); if (fwnode_property_present(child, "adi,output-range-microvolt")) { err = fwnode_property_read_u32_array(child, "adi,output-range-microvolt", vals, 2); - if (err) { - dev_err(dev, + if (err) + return dev_err_probe(dev, err, "adi,output-range-microvolt property could not be parsed\n"); - goto put_child; - } err = ad3552r_find_range(dac->chip_id, vals); - if (err < 0) { - dev_err(dev, - "Invalid adi,output-range-microvolt value\n"); - goto put_child; - } + if (err < 0) + return dev_err_probe(dev, err, + "Invalid adi,output-range-microvolt value\n"); + val = err; err = ad3552r_set_ch_value(dac, AD3552R_CH_OUTPUT_RANGE_SEL, ch, val); if (err) - goto put_child; + return err; dac->ch_data[ch].range = val; } else if (dac->chip_id == AD3542R_ID) { - dev_err(dev, - "adi,output-range-microvolt is required for ad3542r\n"); - err = -EINVAL; - goto put_child; + return dev_err_probe(dev, -EINVAL, + "adi,output-range-microvolt is required for ad3542r\n"); } else { err = ad3552r_configure_custom_gain(dac, child, ch); if (err) - goto put_child; + return err; } ad3552r_calc_gain_and_offset(dac, ch); @@ -1003,7 +981,7 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac) err = ad3552r_set_ch_value(dac, AD3552R_CH_SELECT, ch, 1); if (err < 0) - goto put_child; + return err; dac->channels[cnt] = AD3552R_CH_DAC(ch); ++cnt; @@ -1021,10 +999,6 @@ static int ad3552r_configure_device(struct ad3552r_desc *dac) dac->num_ch = cnt; return 0; -put_child: - fwnode_handle_put(child); - - return err; } static int ad3552r_init(struct ad3552r_desc *dac) diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c index 404865e354..0b24cb19ac 100644 --- a/drivers/iio/dac/ad5755.c +++ b/drivers/iio/dac/ad5755.c @@ -809,7 +809,6 @@ static struct ad5755_platform_data *ad5755_parse_fw(struct device *dev) static int ad5755_probe(struct spi_device *spi) { - enum ad5755_type type = spi_get_device_id(spi)->driver_data; const struct ad5755_platform_data *pdata; struct iio_dev *indio_dev; struct ad5755_state *st; @@ -824,7 +823,7 @@ static int ad5755_probe(struct spi_device *spi) st = iio_priv(indio_dev); spi_set_drvdata(spi, indio_dev); - st->chip_info = &ad5755_chip_info_tbl[type]; + st->chip_info = spi_get_device_match_data(spi); st->spi = spi; st->pwr_down = 0xf; @@ -854,21 +853,21 @@ static int ad5755_probe(struct spi_device *spi) } static const struct spi_device_id ad5755_id[] = { - { "ad5755", ID_AD5755 }, - { "ad5755-1", ID_AD5755 }, - { "ad5757", ID_AD5757 }, - { "ad5735", ID_AD5735 }, - { "ad5737", ID_AD5737 }, + { "ad5755", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, + { "ad5755-1", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5755] }, + { "ad5757", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5757] }, + { "ad5735", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5735] }, + { "ad5737", (kernel_ulong_t)&ad5755_chip_info_tbl[ID_AD5737] }, {} }; MODULE_DEVICE_TABLE(spi, ad5755_id); static const struct of_device_id ad5755_of_match[] = { - { .compatible = "adi,ad5755" }, - { .compatible = "adi,ad5755-1" }, - { .compatible = "adi,ad5757" }, - { .compatible = "adi,ad5735" }, - { .compatible = "adi,ad5737" }, + { .compatible = "adi,ad5755", &ad5755_chip_info_tbl[ID_AD5755] }, + { .compatible = "adi,ad5755-1", &ad5755_chip_info_tbl[ID_AD5755] }, + { .compatible = "adi,ad5757", &ad5755_chip_info_tbl[ID_AD5757] }, + { .compatible = "adi,ad5735", &ad5755_chip_info_tbl[ID_AD5735] }, + { .compatible = "adi,ad5737", &ad5755_chip_info_tbl[ID_AD5737] }, { } }; MODULE_DEVICE_TABLE(of, ad5755_of_match); @@ -876,6 +875,7 @@ MODULE_DEVICE_TABLE(of, ad5755_of_match); static struct spi_driver ad5755_driver = { .driver = { .name = "ad5755", + .of_match_table = ad5755_of_match, }, .probe = ad5755_probe, .id_table = ad5755_id, diff --git a/drivers/iio/dac/ad5770r.c b/drivers/iio/dac/ad5770r.c index f66d67402e..c360ebf529 100644 --- a/drivers/iio/dac/ad5770r.c +++ b/drivers/iio/dac/ad5770r.c @@ -515,39 +515,32 @@ static int ad5770r_channel_config(struct ad5770r_state *st) { int ret, tmp[2], min, max; unsigned int num; - struct fwnode_handle *child; num = device_get_child_node_count(&st->spi->dev); if (num != AD5770R_MAX_CHANNELS) return -EINVAL; - device_for_each_child_node(&st->spi->dev, child) { + device_for_each_child_node_scoped(&st->spi->dev, child) { ret = fwnode_property_read_u32(child, "reg", &num); if (ret) - goto err_child_out; - if (num >= AD5770R_MAX_CHANNELS) { - ret = -EINVAL; - goto err_child_out; - } + return ret; + if (num >= AD5770R_MAX_CHANNELS) + return -EINVAL; ret = fwnode_property_read_u32_array(child, "adi,range-microamp", tmp, 2); if (ret) - goto err_child_out; + return ret; min = tmp[0] / 1000; max = tmp[1] / 1000; ret = ad5770r_store_output_range(st, min, max, num); if (ret) - goto err_child_out; + return ret; } return 0; - -err_child_out: - fwnode_handle_put(child); - return ret; } static int ad5770r_init(struct ad5770r_state *st) diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c new file mode 100644 index 0000000000..f56eabe537 --- /dev/null +++ b/drivers/iio/dac/ad9739a.c @@ -0,0 +1,464 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices AD9739a SPI DAC driver + * + * Copyright 2015-2024 Analog Devices Inc. + */ +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/errno.h> +#include <linux/gpio/consumer.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> +#include <linux/units.h> + +#include <linux/iio/backend.h> +#include <linux/iio/iio.h> +#include <linux/iio/types.h> + +#define AD9739A_REG_MODE 0 +#define AD9739A_RESET_MASK BIT(5) +#define AD9739A_REG_FSC_1 0x06 +#define AD9739A_REG_FSC_2 0x07 +#define AD9739A_FSC_MSB GENMASK(1, 0) +#define AD9739A_REG_DEC_CNT 0x8 +#define AD9739A_NORMAL_MODE 0 +#define AD9739A_MIXED_MODE 2 +#define AD9739A_DAC_DEC GENMASK(1, 0) +#define AD9739A_REG_LVDS_REC_CNT1 0x10 +#define AD9739A_RCVR_LOOP_EN_MASK GENMASK(1, 0) +#define AD9739A_REG_LVDS_REC_CNT4 0x13 +#define AD9739A_FINE_DEL_SKW_MASK GENMASK(3, 0) +#define AD9739A_REG_LVDS_REC_STAT9 0x21 +#define AD9739A_RCVR_TRACK_AND_LOCK (BIT(3) | BIT(0)) +#define AD9739A_REG_CROSS_CNT1 0x22 +#define AD9739A_REG_CROSS_CNT2 0x23 +#define AD9739A_REG_PHS_DET 0x24 +#define AD9739A_REG_MU_DUTY 0x25 +#define AD9739A_REG_MU_CNT1 0x26 +#define AD9739A_MU_EN_MASK BIT(0) +#define AD9739A_MU_GAIN_MASK BIT(1) +#define AD9739A_REG_MU_CNT2 0x27 +#define AD9739A_REG_MU_CNT3 0x28 +#define AD9739A_REG_MU_CNT4 0x29 +#define AD9739A_MU_CNT4_DEFAULT 0xcb +#define AD9739A_REG_MU_STAT1 0x2A +#define AD9739A_MU_LOCK_MASK BIT(0) +#define AD9739A_REG_ANA_CNT_1 0x32 +#define AD9739A_REG_ID 0x35 + +#define AD9739A_ID 0x24 +#define AD9739A_REG_IS_RESERVED(reg) \ + ((reg) == 0x5 || (reg) == 0x9 || (reg) == 0x0E || (reg) == 0x0D || \ + (reg) == 0x2B || (reg) == 0x2C || (reg) == 0x34) + +#define AD9739A_FSC_MIN 8580 +#define AD9739A_FSC_MAX 31700 +#define AD9739A_FSC_RANGE (AD9739A_FSC_MAX - AD9739A_FSC_MIN + 1) + +#define AD9739A_MIN_DAC_CLK (1600 * MEGA) +#define AD9739A_MAX_DAC_CLK (2500 * MEGA) +#define AD9739A_DAC_CLK_RANGE (AD9739A_MAX_DAC_CLK - AD9739A_MIN_DAC_CLK + 1) +/* as recommended by the datasheet */ +#define AD9739A_LOCK_N_TRIES 3 + +struct ad9739a_state { + struct iio_backend *back; + struct regmap *regmap; + unsigned long sample_rate; +}; + +static int ad9739a_oper_mode_get(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct ad9739a_state *st = iio_priv(indio_dev); + u32 mode; + int ret; + + ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode); + if (ret) + return ret; + + mode = FIELD_GET(AD9739A_DAC_DEC, mode); + /* sanity check we get valid values from the HW */ + if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE) + return -EIO; + if (!mode) + return AD9739A_NORMAL_MODE; + + /* + * We get 2 from the device but for IIO modes, that means 1. Hence the + * minus 1. + */ + return AD9739A_MIXED_MODE - 1; +} + +static int ad9739a_oper_mode_set(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, u32 mode) +{ + struct ad9739a_state *st = iio_priv(indio_dev); + + /* + * On the IIO interface we have 0 and 1 for mode. But for mixed_mode, we + * need to write 2 in the device. That's what the below check is about. + */ + if (mode == AD9739A_MIXED_MODE - 1) + mode = AD9739A_MIXED_MODE; + + return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT, + AD9739A_DAC_DEC, mode); +} + +static int ad9739a_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad9739a_state *st = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + *val = st->sample_rate; + *val2 = 0; + return IIO_VAL_INT_64; + default: + return -EINVAL; + } +} + +static int ad9739a_buffer_preenable(struct iio_dev *indio_dev) +{ + struct ad9739a_state *st = iio_priv(indio_dev); + + return iio_backend_data_source_set(st->back, 0, IIO_BACKEND_EXTERNAL); +} + +static int ad9739a_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct ad9739a_state *st = iio_priv(indio_dev); + + return iio_backend_data_source_set(st->back, 0, + IIO_BACKEND_INTERNAL_CONTINUOS_WAVE); +} + +static bool ad9739a_reg_accessible(struct device *dev, unsigned int reg) +{ + if (AD9739A_REG_IS_RESERVED(reg)) + return false; + if (reg > AD9739A_REG_MU_STAT1 && reg < AD9739A_REG_ANA_CNT_1) + return false; + + return true; +} + +static int ad9739a_reset(struct device *dev, const struct ad9739a_state *st) +{ + struct gpio_desc *gpio; + int ret; + + gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + if (gpio) { + /* minimum pulse width of 40ns */ + ndelay(40); + gpiod_set_value_cansleep(gpio, 0); + return 0; + } + + /* bring all registers to their default state */ + ret = regmap_set_bits(st->regmap, AD9739A_REG_MODE, AD9739A_RESET_MASK); + if (ret) + return ret; + + ndelay(40); + + return regmap_clear_bits(st->regmap, AD9739A_REG_MODE, + AD9739A_RESET_MASK); +} + +/* + * Recommended values (as per datasheet) for the dac clk common mode voltage + * and Mu controller. Look at table 29. + */ +static const struct reg_sequence ad9739a_clk_mu_ctrl[] = { + /* DAC clk common mode voltage */ + { AD9739A_REG_CROSS_CNT1, 0x0f }, + { AD9739A_REG_CROSS_CNT2, 0x0f }, + /* Mu controller configuration */ + { AD9739A_REG_PHS_DET, 0x30 }, + { AD9739A_REG_MU_DUTY, 0x80 }, + { AD9739A_REG_MU_CNT2, 0x44 }, + { AD9739A_REG_MU_CNT3, 0x6c }, +}; + +static int ad9739a_init(struct device *dev, const struct ad9739a_state *st) +{ + unsigned int i = 0, lock, fsc; + u32 fsc_raw; + int ret; + + ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl, + ARRAY_SIZE(ad9739a_clk_mu_ctrl)); + if (ret) + return ret; + + /* + * Try to get the Mu lock. Repeat the below steps AD9739A_LOCK_N_TRIES + * (as specified by the datasheet) until we get the lock. + */ + do { + ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4, + AD9739A_MU_CNT4_DEFAULT); + if (ret) + return ret; + + /* Enable the Mu controller search and track mode. */ + ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT1, + AD9739A_MU_EN_MASK | AD9739A_MU_GAIN_MASK); + if (ret) + return ret; + + /* Ensure the DLL loop is locked */ + ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1, + lock, lock & AD9739A_MU_LOCK_MASK, + 0, 1000); + if (ret && ret != -ETIMEDOUT) + return ret; + } while (ret && ++i < AD9739A_LOCK_N_TRIES); + + if (i == AD9739A_LOCK_N_TRIES) + return dev_err_probe(dev, ret, "Mu lock timeout\n"); + + /* Receiver tracking and lock. Same deal as the Mu controller */ + i = 0; + do { + ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4, + AD9739A_FINE_DEL_SKW_MASK, + FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK, 2)); + if (ret) + return ret; + + /* Disable the receiver and the loop. */ + ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0); + if (ret) + return ret; + + /* + * Re-enable the loop so it falls out of lock and begins the + * search/track routine again. + */ + ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1, + AD9739A_RCVR_LOOP_EN_MASK); + if (ret) + return ret; + + /* Ensure the DLL loop is locked */ + ret = regmap_read_poll_timeout(st->regmap, + AD9739A_REG_LVDS_REC_STAT9, lock, + lock == AD9739A_RCVR_TRACK_AND_LOCK, + 0, 1000); + if (ret && ret != -ETIMEDOUT) + return ret; + } while (ret && ++i < AD9739A_LOCK_N_TRIES); + + if (i == AD9739A_LOCK_N_TRIES) + return dev_err_probe(dev, ret, "Receiver lock timeout\n"); + + ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc); + if (ret && ret == -EINVAL) + return 0; + if (ret) + return ret; + if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE)) + return dev_err_probe(dev, -EINVAL, + "Invalid full scale current(%u) [%u %u]\n", + fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX); + /* + * IOUTFS is given by + * Ioutfs = 0.0226 * FSC + 8.58 + * and is given in mA. Hence we'll have to multiply by 10 * MILLI in + * order to get rid of the fractional. + */ + fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226); + + ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff); + if (ret) + return ret; + + return regmap_update_bits(st->regmap, AD9739A_REG_FSC_2, + AD9739A_FSC_MSB, fsc_raw >> 8); +} + +static const char * const ad9739a_modes_avail[] = { "normal", "mixed-mode" }; + +static const struct iio_enum ad9739a_modes = { + .items = ad9739a_modes_avail, + .num_items = ARRAY_SIZE(ad9739a_modes_avail), + .get = ad9739a_oper_mode_get, + .set = ad9739a_oper_mode_set, +}; + +static const struct iio_chan_spec_ext_info ad9739a_ext_info[] = { + IIO_ENUM_AVAILABLE("operating_mode", IIO_SEPARATE, &ad9739a_modes), + IIO_ENUM("operating_mode", IIO_SEPARATE, &ad9739a_modes), + { } +}; + +/* + * The reason for having two different channels is because we have, in reality, + * two sources of data: + * ALTVOLTAGE: It's a Continuous Wave that's internally generated by the + * backend device. + * VOLTAGE: It's the typical data we can have in a DAC device and the source + * of it has nothing to do with the backend. The backend will only + * forward it into our data interface to be sent out. + */ +static struct iio_chan_spec ad9739a_channels[] = { + { + .type = IIO_ALTVOLTAGE, + .indexed = 1, + .output = 1, + .scan_index = -1, + }, + { + .type = IIO_VOLTAGE, + .indexed = 1, + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + .output = 1, + .ext_info = ad9739a_ext_info, + .scan_type = { + .sign = 's', + .storagebits = 16, + .realbits = 16, + }, + } +}; + +static const struct iio_info ad9739a_info = { + .read_raw = ad9739a_read_raw, +}; + +static const struct iio_buffer_setup_ops ad9739a_buffer_setup_ops = { + .preenable = &ad9739a_buffer_preenable, + .postdisable = &ad9739a_buffer_postdisable, +}; + +static const struct regmap_config ad9739a_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .readable_reg = ad9739a_reg_accessible, + .writeable_reg = ad9739a_reg_accessible, + .max_register = AD9739A_REG_ID, +}; + +static int ad9739a_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct iio_dev *indio_dev; + struct ad9739a_state *st; + unsigned int id; + struct clk *clk; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*st)); + if (!indio_dev) + return -ENOMEM; + + st = iio_priv(indio_dev); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return dev_err_probe(dev, PTR_ERR(clk), "Could not get clkin\n"); + + st->sample_rate = clk_get_rate(clk); + if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK, + AD9739A_DAC_CLK_RANGE)) + return dev_err_probe(dev, -EINVAL, + "Invalid dac clk range(%lu) [%lu %lu]\n", + st->sample_rate, AD9739A_MIN_DAC_CLK, + AD9739A_MAX_DAC_CLK); + + st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + ret = regmap_read(st->regmap, AD9739A_REG_ID, &id); + if (ret) + return ret; + + if (id != AD9739A_ID) + dev_warn(dev, "Unrecognized CHIP_ID 0x%X", id); + + ret = ad9739a_reset(dev, st); + if (ret) + return ret; + + ret = ad9739a_init(dev, st); + if (ret) + return ret; + + st->back = devm_iio_backend_get(dev, NULL); + if (IS_ERR(st->back)) + return PTR_ERR(st->back); + + ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev); + if (ret) + return ret; + + ret = iio_backend_extend_chan_spec(indio_dev, st->back, + &ad9739a_channels[0]); + if (ret) + return ret; + + ret = iio_backend_set_sampling_freq(st->back, 0, st->sample_rate); + if (ret) + return ret; + + ret = devm_iio_backend_enable(dev, st->back); + if (ret) + return ret; + + indio_dev->name = "ad9739a"; + indio_dev->info = &ad9739a_info; + indio_dev->channels = ad9739a_channels; + indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels); + indio_dev->setup_ops = &ad9739a_buffer_setup_ops; + + return devm_iio_device_register(&spi->dev, indio_dev); +} + +static const struct of_device_id ad9739a_of_match[] = { + { .compatible = "adi,ad9739a" }, + {} +}; +MODULE_DEVICE_TABLE(of, ad9739a_of_match); + +static const struct spi_device_id ad9739a_id[] = { + {"ad9739a"}, + {} +}; +MODULE_DEVICE_TABLE(spi, ad9739a_id); + +static struct spi_driver ad9739a_driver = { + .driver = { + .name = "ad9739a", + .of_match_table = ad9739a_of_match, + }, + .probe = ad9739a_probe, + .id_table = ad9739a_id, +}; +module_spi_driver(ad9739a_driver); + +MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>"); +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices AD9739 DAC"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_BACKEND); diff --git a/drivers/iio/dac/adi-axi-dac.c b/drivers/iio/dac/adi-axi-dac.c new file mode 100644 index 0000000000..880d83a014 --- /dev/null +++ b/drivers/iio/dac/adi-axi-dac.c @@ -0,0 +1,635 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Analog Devices Generic AXI DAC IP core + * Link: https://wiki.analog.com/resources/fpga/docs/axi_dac_ip + * + * Copyright 2016-2024 Analog Devices Inc. + */ +#include <linux/bitfield.h> +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/err.h> +#include <linux/limits.h> +#include <linux/kstrtox.h> +#include <linux/math.h> +#include <linux/math64.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/property.h> +#include <linux/regmap.h> +#include <linux/units.h> + +#include <linux/fpga/adi-axi-common.h> +#include <linux/iio/backend.h> +#include <linux/iio/buffer-dmaengine.h> +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> + +/* + * Register definitions: + * https://wiki.analog.com/resources/fpga/docs/axi_dac_ip#register_map + */ + +/* Base controls */ +#define AXI_DAC_REG_CONFIG 0x0c +#define AXI_DDS_DISABLE BIT(6) + + /* DAC controls */ +#define AXI_DAC_REG_RSTN 0x0040 +#define AXI_DAC_RSTN_CE_N BIT(2) +#define AXI_DAC_RSTN_MMCM_RSTN BIT(1) +#define AXI_DAC_RSTN_RSTN BIT(0) +#define AXI_DAC_REG_CNTRL_1 0x0044 +#define AXI_DAC_SYNC BIT(0) +#define AXI_DAC_REG_CNTRL_2 0x0048 +#define ADI_DAC_R1_MODE BIT(4) +#define AXI_DAC_DRP_STATUS 0x0074 +#define AXI_DAC_DRP_LOCKED BIT(17) +/* DAC Channel controls */ +#define AXI_DAC_REG_CHAN_CNTRL_1(c) (0x0400 + (c) * 0x40) +#define AXI_DAC_REG_CHAN_CNTRL_3(c) (0x0408 + (c) * 0x40) +#define AXI_DAC_SCALE_SIGN BIT(15) +#define AXI_DAC_SCALE_INT BIT(14) +#define AXI_DAC_SCALE GENMASK(14, 0) +#define AXI_DAC_REG_CHAN_CNTRL_2(c) (0x0404 + (c) * 0x40) +#define AXI_DAC_REG_CHAN_CNTRL_4(c) (0x040c + (c) * 0x40) +#define AXI_DAC_PHASE GENMASK(31, 16) +#define AXI_DAC_FREQUENCY GENMASK(15, 0) +#define AXI_DAC_REG_CHAN_CNTRL_7(c) (0x0418 + (c) * 0x40) +#define AXI_DAC_DATA_SEL GENMASK(3, 0) + +/* 360 degrees in rad */ +#define AXI_DAC_2_PI_MEGA 6283190 +enum { + AXI_DAC_DATA_INTERNAL_TONE, + AXI_DAC_DATA_DMA = 2, +}; + +struct axi_dac_state { + struct regmap *regmap; + struct device *dev; + /* + * lock to protect multiple accesses to the device registers and global + * data/variables. + */ + struct mutex lock; + u64 dac_clk; + u32 reg_config; + bool int_tone; +}; + +static int axi_dac_enable(struct iio_backend *back) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + unsigned int __val; + int ret; + + guard(mutex)(&st->lock); + ret = regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN, + AXI_DAC_RSTN_MMCM_RSTN); + if (ret) + return ret; + /* + * Make sure the DRP (Dynamic Reconfiguration Port) is locked. Not all + * designs really use it but if they don't we still get the lock bit + * set. So let's do it all the time so the code is generic. + */ + ret = regmap_read_poll_timeout(st->regmap, AXI_DAC_DRP_STATUS, __val, + __val & AXI_DAC_DRP_LOCKED, 100, 1000); + if (ret) + return ret; + + return regmap_set_bits(st->regmap, AXI_DAC_REG_RSTN, + AXI_DAC_RSTN_RSTN | AXI_DAC_RSTN_MMCM_RSTN); +} + +static void axi_dac_disable(struct iio_backend *back) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + guard(mutex)(&st->lock); + regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0); +} + +static struct iio_buffer *axi_dac_request_buffer(struct iio_backend *back, + struct iio_dev *indio_dev) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + const char *dma_name; + + if (device_property_read_string(st->dev, "dma-names", &dma_name)) + dma_name = "tx"; + + return iio_dmaengine_buffer_setup_ext(st->dev, indio_dev, dma_name, + IIO_BUFFER_DIRECTION_OUT); +} + +static void axi_dac_free_buffer(struct iio_backend *back, + struct iio_buffer *buffer) +{ + iio_dmaengine_buffer_free(buffer); +} + +enum { + AXI_DAC_FREQ_TONE_1, + AXI_DAC_FREQ_TONE_2, + AXI_DAC_SCALE_TONE_1, + AXI_DAC_SCALE_TONE_2, + AXI_DAC_PHASE_TONE_1, + AXI_DAC_PHASE_TONE_2, +}; + +static int __axi_dac_frequency_get(struct axi_dac_state *st, unsigned int chan, + unsigned int tone_2, unsigned int *freq) +{ + u32 reg, raw; + int ret; + + if (!st->dac_clk) { + dev_err(st->dev, "Sampling rate is 0...\n"); + return -EINVAL; + } + + if (tone_2) + reg = AXI_DAC_REG_CHAN_CNTRL_4(chan); + else + reg = AXI_DAC_REG_CHAN_CNTRL_2(chan); + + ret = regmap_read(st->regmap, reg, &raw); + if (ret) + return ret; + + raw = FIELD_GET(AXI_DAC_FREQUENCY, raw); + *freq = DIV_ROUND_CLOSEST_ULL(raw * st->dac_clk, BIT(16)); + + return 0; +} + +static int axi_dac_frequency_get(struct axi_dac_state *st, + const struct iio_chan_spec *chan, char *buf, + unsigned int tone_2) +{ + unsigned int freq; + int ret; + + scoped_guard(mutex, &st->lock) { + ret = __axi_dac_frequency_get(st, chan->channel, tone_2, &freq); + if (ret) + return ret; + } + + return sysfs_emit(buf, "%u\n", freq); +} + +static int axi_dac_scale_get(struct axi_dac_state *st, + const struct iio_chan_spec *chan, char *buf, + unsigned int tone_2) +{ + unsigned int scale, sign; + int ret, vals[2]; + u32 reg, raw; + + if (tone_2) + reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel); + else + reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel); + + ret = regmap_read(st->regmap, reg, &raw); + if (ret) + return ret; + + sign = FIELD_GET(AXI_DAC_SCALE_SIGN, raw); + raw = FIELD_GET(AXI_DAC_SCALE, raw); + scale = DIV_ROUND_CLOSEST_ULL((u64)raw * MEGA, AXI_DAC_SCALE_INT); + + vals[0] = scale / MEGA; + vals[1] = scale % MEGA; + + if (sign) { + vals[0] *= -1; + if (!vals[0]) + vals[1] *= -1; + } + + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals), + vals); +} + +static int axi_dac_phase_get(struct axi_dac_state *st, + const struct iio_chan_spec *chan, char *buf, + unsigned int tone_2) +{ + u32 reg, raw, phase; + int ret, vals[2]; + + if (tone_2) + reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel); + else + reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel); + + ret = regmap_read(st->regmap, reg, &raw); + if (ret) + return ret; + + raw = FIELD_GET(AXI_DAC_PHASE, raw); + phase = DIV_ROUND_CLOSEST_ULL((u64)raw * AXI_DAC_2_PI_MEGA, U16_MAX); + + vals[0] = phase / MEGA; + vals[1] = phase % MEGA; + + return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, ARRAY_SIZE(vals), + vals); +} + +static int __axi_dac_frequency_set(struct axi_dac_state *st, unsigned int chan, + u64 sample_rate, unsigned int freq, + unsigned int tone_2) +{ + u32 reg; + u16 raw; + int ret; + + if (!sample_rate || freq > sample_rate / 2) { + dev_err(st->dev, "Invalid frequency(%u) dac_clk(%llu)\n", + freq, sample_rate); + return -EINVAL; + } + + if (tone_2) + reg = AXI_DAC_REG_CHAN_CNTRL_4(chan); + else + reg = AXI_DAC_REG_CHAN_CNTRL_2(chan); + + raw = DIV64_U64_ROUND_CLOSEST((u64)freq * BIT(16), sample_rate); + + ret = regmap_update_bits(st->regmap, reg, AXI_DAC_FREQUENCY, raw); + if (ret) + return ret; + + /* synchronize channels */ + return regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC); +} + +static int axi_dac_frequency_set(struct axi_dac_state *st, + const struct iio_chan_spec *chan, + const char *buf, size_t len, unsigned int tone_2) +{ + unsigned int freq; + int ret; + + ret = kstrtou32(buf, 10, &freq); + if (ret) + return ret; + + guard(mutex)(&st->lock); + ret = __axi_dac_frequency_set(st, chan->channel, st->dac_clk, freq, + tone_2); + if (ret) + return ret; + + return len; +} + +static int axi_dac_scale_set(struct axi_dac_state *st, + const struct iio_chan_spec *chan, + const char *buf, size_t len, unsigned int tone_2) +{ + int integer, frac, scale; + u32 raw = 0, reg; + int ret; + + ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac); + if (ret) + return ret; + + scale = integer * MEGA + frac; + if (scale <= -2 * (int)MEGA || scale >= 2 * (int)MEGA) + return -EINVAL; + + /* format is 1.1.14 (sign, integer and fractional bits) */ + if (scale < 0) { + raw = FIELD_PREP(AXI_DAC_SCALE_SIGN, 1); + scale *= -1; + } + + raw |= div_u64((u64)scale * AXI_DAC_SCALE_INT, MEGA); + + if (tone_2) + reg = AXI_DAC_REG_CHAN_CNTRL_3(chan->channel); + else + reg = AXI_DAC_REG_CHAN_CNTRL_1(chan->channel); + + guard(mutex)(&st->lock); + ret = regmap_write(st->regmap, reg, raw); + if (ret) + return ret; + + /* synchronize channels */ + ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC); + if (ret) + return ret; + + return len; +} + +static int axi_dac_phase_set(struct axi_dac_state *st, + const struct iio_chan_spec *chan, + const char *buf, size_t len, unsigned int tone_2) +{ + int integer, frac, phase; + u32 raw, reg; + int ret; + + ret = iio_str_to_fixpoint(buf, 100000, &integer, &frac); + if (ret) + return ret; + + phase = integer * MEGA + frac; + if (phase < 0 || phase > AXI_DAC_2_PI_MEGA) + return -EINVAL; + + raw = DIV_ROUND_CLOSEST_ULL((u64)phase * U16_MAX, AXI_DAC_2_PI_MEGA); + + if (tone_2) + reg = AXI_DAC_REG_CHAN_CNTRL_4(chan->channel); + else + reg = AXI_DAC_REG_CHAN_CNTRL_2(chan->channel); + + guard(mutex)(&st->lock); + ret = regmap_update_bits(st->regmap, reg, AXI_DAC_PHASE, + FIELD_PREP(AXI_DAC_PHASE, raw)); + if (ret) + return ret; + + /* synchronize channels */ + ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_1, AXI_DAC_SYNC); + if (ret) + return ret; + + return len; +} + +static int axi_dac_ext_info_set(struct iio_backend *back, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + switch (private) { + case AXI_DAC_FREQ_TONE_1: + case AXI_DAC_FREQ_TONE_2: + return axi_dac_frequency_set(st, chan, buf, len, + private == AXI_DAC_FREQ_TONE_2); + case AXI_DAC_SCALE_TONE_1: + case AXI_DAC_SCALE_TONE_2: + return axi_dac_scale_set(st, chan, buf, len, + private == AXI_DAC_SCALE_TONE_2); + case AXI_DAC_PHASE_TONE_1: + case AXI_DAC_PHASE_TONE_2: + return axi_dac_phase_set(st, chan, buf, len, + private == AXI_DAC_PHASE_TONE_2); + default: + return -EOPNOTSUPP; + } +} + +static int axi_dac_ext_info_get(struct iio_backend *back, uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + switch (private) { + case AXI_DAC_FREQ_TONE_1: + case AXI_DAC_FREQ_TONE_2: + return axi_dac_frequency_get(st, chan, buf, + private - AXI_DAC_FREQ_TONE_1); + case AXI_DAC_SCALE_TONE_1: + case AXI_DAC_SCALE_TONE_2: + return axi_dac_scale_get(st, chan, buf, + private - AXI_DAC_SCALE_TONE_1); + case AXI_DAC_PHASE_TONE_1: + case AXI_DAC_PHASE_TONE_2: + return axi_dac_phase_get(st, chan, buf, + private - AXI_DAC_PHASE_TONE_1); + default: + return -EOPNOTSUPP; + } +} + +static const struct iio_chan_spec_ext_info axi_dac_ext_info[] = { + IIO_BACKEND_EX_INFO("frequency0", IIO_SEPARATE, AXI_DAC_FREQ_TONE_1), + IIO_BACKEND_EX_INFO("frequency1", IIO_SEPARATE, AXI_DAC_FREQ_TONE_2), + IIO_BACKEND_EX_INFO("scale0", IIO_SEPARATE, AXI_DAC_SCALE_TONE_1), + IIO_BACKEND_EX_INFO("scale1", IIO_SEPARATE, AXI_DAC_SCALE_TONE_2), + IIO_BACKEND_EX_INFO("phase0", IIO_SEPARATE, AXI_DAC_PHASE_TONE_1), + IIO_BACKEND_EX_INFO("phase1", IIO_SEPARATE, AXI_DAC_PHASE_TONE_2), + {} +}; + +static int axi_dac_extend_chan(struct iio_backend *back, + struct iio_chan_spec *chan) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + if (chan->type != IIO_ALTVOLTAGE) + return -EINVAL; + if (st->reg_config & AXI_DDS_DISABLE) + /* nothing to extend */ + return 0; + + chan->ext_info = axi_dac_ext_info; + + return 0; +} + +static int axi_dac_data_source_set(struct iio_backend *back, unsigned int chan, + enum iio_backend_data_source data) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + + switch (data) { + case IIO_BACKEND_INTERNAL_CONTINUOS_WAVE: + return regmap_update_bits(st->regmap, + AXI_DAC_REG_CHAN_CNTRL_7(chan), + AXI_DAC_DATA_SEL, + AXI_DAC_DATA_INTERNAL_TONE); + case IIO_BACKEND_EXTERNAL: + return regmap_update_bits(st->regmap, + AXI_DAC_REG_CHAN_CNTRL_7(chan), + AXI_DAC_DATA_SEL, AXI_DAC_DATA_DMA); + default: + return -EINVAL; + } +} + +static int axi_dac_set_sample_rate(struct iio_backend *back, unsigned int chan, + u64 sample_rate) +{ + struct axi_dac_state *st = iio_backend_get_priv(back); + unsigned int freq; + int ret, tone; + + if (!sample_rate) + return -EINVAL; + if (st->reg_config & AXI_DDS_DISABLE) + /* sample_rate has no meaning if DDS is disabled */ + return 0; + + guard(mutex)(&st->lock); + /* + * If dac_clk is 0 then this must be the first time we're being notified + * about the interface sample rate. Hence, just update our internal + * variable and bail... If it's not 0, then we get the current DDS + * frequency (for the old rate) and update the registers for the new + * sample rate. + */ + if (!st->dac_clk) { + st->dac_clk = sample_rate; + return 0; + } + + for (tone = 0; tone <= AXI_DAC_FREQ_TONE_2; tone++) { + ret = __axi_dac_frequency_get(st, chan, tone, &freq); + if (ret) + return ret; + + ret = __axi_dac_frequency_set(st, chan, sample_rate, tone, freq); + if (ret) + return ret; + } + + st->dac_clk = sample_rate; + + return 0; +} + +static const struct iio_backend_ops axi_dac_generic = { + .enable = axi_dac_enable, + .disable = axi_dac_disable, + .request_buffer = axi_dac_request_buffer, + .free_buffer = axi_dac_free_buffer, + .extend_chan_spec = axi_dac_extend_chan, + .ext_info_set = axi_dac_ext_info_set, + .ext_info_get = axi_dac_ext_info_get, + .data_source_set = axi_dac_data_source_set, + .set_sample_rate = axi_dac_set_sample_rate, +}; + +static const struct regmap_config axi_dac_regmap_config = { + .val_bits = 32, + .reg_bits = 32, + .reg_stride = 4, + .max_register = 0x0800, +}; + +static int axi_dac_probe(struct platform_device *pdev) +{ + const unsigned int *expected_ver; + struct axi_dac_state *st; + void __iomem *base; + unsigned int ver; + struct clk *clk; + int ret; + + st = devm_kzalloc(&pdev->dev, sizeof(*st), GFP_KERNEL); + if (!st) + return -ENOMEM; + + expected_ver = device_get_match_data(&pdev->dev); + if (!expected_ver) + return -ENODEV; + + clk = devm_clk_get_enabled(&pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + st->dev = &pdev->dev; + st->regmap = devm_regmap_init_mmio(&pdev->dev, base, + &axi_dac_regmap_config); + if (IS_ERR(st->regmap)) + return PTR_ERR(st->regmap); + + /* + * Force disable the core. Up to the frontend to enable us. And we can + * still read/write registers... + */ + ret = regmap_write(st->regmap, AXI_DAC_REG_RSTN, 0); + if (ret) + return ret; + + ret = regmap_read(st->regmap, ADI_AXI_REG_VERSION, &ver); + if (ret) + return ret; + + if (ADI_AXI_PCORE_VER_MAJOR(ver) != ADI_AXI_PCORE_VER_MAJOR(*expected_ver)) { + dev_err(&pdev->dev, + "Major version mismatch. Expected %d.%.2d.%c, Reported %d.%.2d.%c\n", + ADI_AXI_PCORE_VER_MAJOR(*expected_ver), + ADI_AXI_PCORE_VER_MINOR(*expected_ver), + ADI_AXI_PCORE_VER_PATCH(*expected_ver), + ADI_AXI_PCORE_VER_MAJOR(ver), + ADI_AXI_PCORE_VER_MINOR(ver), + ADI_AXI_PCORE_VER_PATCH(ver)); + return -ENODEV; + } + + /* Let's get the core read only configuration */ + ret = regmap_read(st->regmap, AXI_DAC_REG_CONFIG, &st->reg_config); + if (ret) + return ret; + + /* + * In some designs, setting the R1_MODE bit to 0 (which is the default + * value) causes all channels of the frontend to be routed to the same + * DMA (so they are sampled together). This is for things like + * Multiple-Input and Multiple-Output (MIMO). As most of the times we + * want independent channels let's override the core's default value and + * set the R1_MODE bit. + */ + ret = regmap_set_bits(st->regmap, AXI_DAC_REG_CNTRL_2, ADI_DAC_R1_MODE); + if (ret) + return ret; + + mutex_init(&st->lock); + ret = devm_iio_backend_register(&pdev->dev, &axi_dac_generic, st); + if (ret) + return ret; + + dev_info(&pdev->dev, "AXI DAC IP core (%d.%.2d.%c) probed\n", + ADI_AXI_PCORE_VER_MAJOR(ver), + ADI_AXI_PCORE_VER_MINOR(ver), + ADI_AXI_PCORE_VER_PATCH(ver)); + + return 0; +} + +static unsigned int axi_dac_9_1_b_info = ADI_AXI_PCORE_VER(9, 1, 'b'); + +static const struct of_device_id axi_dac_of_match[] = { + { .compatible = "adi,axi-dac-9.1.b", .data = &axi_dac_9_1_b_info }, + {} +}; +MODULE_DEVICE_TABLE(of, axi_dac_of_match); + +static struct platform_driver axi_dac_driver = { + .driver = { + .name = "adi-axi-dac", + .of_match_table = axi_dac_of_match, + }, + .probe = axi_dac_probe, +}; +module_platform_driver(axi_dac_driver); + +MODULE_AUTHOR("Nuno Sa <nuno.sa@analog.com>"); +MODULE_DESCRIPTION("Analog Devices Generic AXI DAC IP core driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_DMAENGINE_BUFFER); +MODULE_IMPORT_NS(IIO_BACKEND); diff --git a/drivers/iio/dac/ltc2688.c b/drivers/iio/dac/ltc2688.c index fc8eb53c65..c4b1ba30f9 100644 --- a/drivers/iio/dac/ltc2688.c +++ b/drivers/iio/dac/ltc2688.c @@ -746,26 +746,21 @@ static int ltc2688_span_lookup(const struct ltc2688_state *st, int min, int max) static int ltc2688_channel_config(struct ltc2688_state *st) { struct device *dev = &st->spi->dev; - struct fwnode_handle *child; u32 reg, clk_input, val, tmp[2]; int ret, span; - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { struct ltc2688_chan *chan; ret = fwnode_property_read_u32(child, "reg", ®); - if (ret) { - fwnode_handle_put(child); + if (ret) return dev_err_probe(dev, ret, "Failed to get reg property\n"); - } - if (reg >= LTC2688_DAC_CHANNELS) { - fwnode_handle_put(child); + if (reg >= LTC2688_DAC_CHANNELS) return dev_err_probe(dev, -EINVAL, "reg bigger than: %d\n", LTC2688_DAC_CHANNELS); - } val = 0; chan = &st->channels[reg]; @@ -786,12 +781,10 @@ static int ltc2688_channel_config(struct ltc2688_state *st) if (!ret) { span = ltc2688_span_lookup(st, (int)tmp[0] / 1000, tmp[1] / 1000); - if (span < 0) { - fwnode_handle_put(child); - return dev_err_probe(dev, -EINVAL, + if (span < 0) + return dev_err_probe(dev, span, "output range not valid:[%d %d]\n", tmp[0], tmp[1]); - } val |= FIELD_PREP(LTC2688_CH_SPAN_MSK, span); } @@ -800,17 +793,14 @@ static int ltc2688_channel_config(struct ltc2688_state *st) &clk_input); if (!ret) { if (clk_input >= LTC2688_CH_TGP_MAX) { - fwnode_handle_put(child); return dev_err_probe(dev, -EINVAL, "toggle-dither-input inv value(%d)\n", clk_input); } ret = ltc2688_tgp_clk_setup(st, chan, child, clk_input); - if (ret) { - fwnode_handle_put(child); + if (ret) return ret; - } /* * 0 means software toggle which is the default mode. @@ -844,11 +834,9 @@ static int ltc2688_channel_config(struct ltc2688_state *st) ret = regmap_write(st->regmap, LTC2688_CMD_CH_SETTING(reg), val); - if (ret) { - fwnode_handle_put(child); - return dev_err_probe(dev, -EINVAL, + if (ret) + return dev_err_probe(dev, ret, "failed to set chan settings\n"); - } } return 0; diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c index efb1269a77..c5162b7295 100644 --- a/drivers/iio/dac/ti-dac5571.c +++ b/drivers/iio/dac/ti-dac5571.c @@ -13,6 +13,7 @@ * https://www.ti.com/lit/ds/symlink/dac5573.pdf * https://www.ti.com/lit/ds/symlink/dac6573.pdf * https://www.ti.com/lit/ds/symlink/dac7573.pdf + * https://www.ti.com/lit/ds/symlink/dac081c081.pdf * https://www.ti.com/lit/ds/symlink/dac121c081.pdf */ @@ -386,6 +387,7 @@ static void dac5571_remove(struct i2c_client *i2c) } static const struct of_device_id dac5571_of_id[] = { + {.compatible = "ti,dac081c081", .data = &dac5571_spec[single_8bit] }, {.compatible = "ti,dac121c081", .data = &dac5571_spec[single_12bit] }, {.compatible = "ti,dac5571", .data = &dac5571_spec[single_8bit] }, {.compatible = "ti,dac6571", .data = &dac5571_spec[single_10bit] }, @@ -401,6 +403,7 @@ static const struct of_device_id dac5571_of_id[] = { MODULE_DEVICE_TABLE(of, dac5571_of_id); static const struct i2c_device_id dac5571_id[] = { + {"dac081c081", (kernel_ulong_t)&dac5571_spec[single_8bit] }, {"dac121c081", (kernel_ulong_t)&dac5571_spec[single_12bit] }, {"dac5571", (kernel_ulong_t)&dac5571_spec[single_8bit] }, {"dac6571", (kernel_ulong_t)&dac5571_spec[single_10bit] }, diff --git a/drivers/iio/frequency/admfm2000.c b/drivers/iio/frequency/admfm2000.c index c34d79e55a..b2263b9afe 100644 --- a/drivers/iio/frequency/admfm2000.c +++ b/drivers/iio/frequency/admfm2000.c @@ -160,26 +160,21 @@ static int admfm2000_channel_config(struct admfm2000_state *st, { struct platform_device *pdev = to_platform_device(indio_dev->dev.parent); struct device *dev = &pdev->dev; - struct fwnode_handle *child; struct gpio_desc **dsa; struct gpio_desc **sw; int ret, i; bool mode; u32 reg; - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { ret = fwnode_property_read_u32(child, "reg", ®); - if (ret) { - fwnode_handle_put(child); + if (ret) return dev_err_probe(dev, ret, "Failed to get reg property\n"); - } - if (reg >= indio_dev->num_channels) { - fwnode_handle_put(child); + if (reg >= indio_dev->num_channels) return dev_err_probe(dev, -EINVAL, "reg bigger than: %d\n", indio_dev->num_channels); - } if (fwnode_property_present(child, "adi,mixer-mode")) mode = ADMFM2000_MIXER_MODE; @@ -196,36 +191,29 @@ static int admfm2000_channel_config(struct admfm2000_state *st, dsa = st->dsa2_gpios; break; default: - fwnode_handle_put(child); return -EINVAL; } for (i = 0; i < ADMFM2000_MODE_GPIOS; i++) { sw[i] = devm_fwnode_gpiod_get_index(dev, child, "switch", i, GPIOD_OUT_LOW, NULL); - if (IS_ERR(sw[i])) { - fwnode_handle_put(child); + if (IS_ERR(sw[i])) return dev_err_probe(dev, PTR_ERR(sw[i]), "Failed to get gpios\n"); - } } for (i = 0; i < ADMFM2000_DSA_GPIOS; i++) { dsa[i] = devm_fwnode_gpiod_get_index(dev, child, "attenuation", i, GPIOD_OUT_LOW, NULL); - if (IS_ERR(dsa[i])) { - fwnode_handle_put(child); + if (IS_ERR(dsa[i])) return dev_err_probe(dev, PTR_ERR(dsa[i]), "Failed to get gpios\n"); - } } ret = admfm2000_mode(indio_dev, reg, mode); - if (ret) { - fwnode_handle_put(child); + if (ret) return ret; - } } return 0; diff --git a/drivers/iio/frequency/admv1013.c b/drivers/iio/frequency/admv1013.c index 92923074f9..c0cd5d9844 100644 --- a/drivers/iio/frequency/admv1013.c +++ b/drivers/iio/frequency/admv1013.c @@ -95,7 +95,6 @@ struct admv1013_state { struct clk *clkin; /* Protect against concurrent accesses to the device and to data */ struct mutex lock; - struct regulator *reg; struct notifier_block nb; unsigned int input_mode; unsigned int quad_se_mode; @@ -342,14 +341,9 @@ static int admv1013_update_quad_filters(struct admv1013_state *st) FIELD_PREP(ADMV1013_QUAD_FILTERS_MSK, filt_raw)); } -static int admv1013_update_mixer_vgate(struct admv1013_state *st) +static int admv1013_update_mixer_vgate(struct admv1013_state *st, int vcm) { unsigned int mixer_vgate; - int vcm; - - vcm = regulator_get_voltage(st->reg); - if (vcm < 0) - return vcm; if (vcm <= 1800000) mixer_vgate = (2389 * vcm / 1000000 + 8100) / 100; @@ -443,7 +437,7 @@ static const struct iio_chan_spec admv1013_channels[] = { ADMV1013_CHAN_CALIB(1, Q), }; -static int admv1013_init(struct admv1013_state *st) +static int admv1013_init(struct admv1013_state *st, int vcm_uv) { int ret; unsigned int data; @@ -483,7 +477,7 @@ static int admv1013_init(struct admv1013_state *st) if (ret) return ret; - ret = admv1013_update_mixer_vgate(st); + ret = admv1013_update_mixer_vgate(st, vcm_uv); if (ret) return ret; @@ -498,11 +492,6 @@ static int admv1013_init(struct admv1013_state *st) st->input_mode); } -static void admv1013_reg_disable(void *data) -{ - regulator_disable(data); -} - static void admv1013_powerdown(void *data) { unsigned int enable_reg, enable_reg_msk; @@ -557,11 +546,6 @@ static int admv1013_properties_parse(struct admv1013_state *st) else return -EINVAL; - st->reg = devm_regulator_get(&spi->dev, "vcm"); - if (IS_ERR(st->reg)) - return dev_err_probe(&spi->dev, PTR_ERR(st->reg), - "failed to get the common-mode voltage\n"); - ret = devm_regulator_bulk_get_enable(&st->spi->dev, ARRAY_SIZE(admv1013_vcc_regs), admv1013_vcc_regs); @@ -578,7 +562,7 @@ static int admv1013_probe(struct spi_device *spi) { struct iio_dev *indio_dev; struct admv1013_state *st; - int ret; + int ret, vcm_uv; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); if (!indio_dev) @@ -597,16 +581,12 @@ static int admv1013_probe(struct spi_device *spi) if (ret) return ret; - ret = regulator_enable(st->reg); - if (ret) { - dev_err(&spi->dev, "Failed to enable specified Common-Mode Voltage!\n"); - return ret; - } + ret = devm_regulator_get_enable_read_voltage(&spi->dev, "vcm"); + if (ret < 0) + return dev_err_probe(&spi->dev, ret, + "failed to get the common-mode voltage\n"); - ret = devm_add_action_or_reset(&spi->dev, admv1013_reg_disable, - st->reg); - if (ret) - return ret; + vcm_uv = ret; st->clkin = devm_clk_get_enabled(&spi->dev, "lo_in"); if (IS_ERR(st->clkin)) @@ -620,7 +600,7 @@ static int admv1013_probe(struct spi_device *spi) mutex_init(&st->lock); - ret = admv1013_init(st); + ret = admv1013_init(st, vcm_uv); if (ret) { dev_err(&spi->dev, "admv1013 init failed\n"); return ret; diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c index b4defb82f3..3f46032c92 100644 --- a/drivers/iio/frequency/adrf6780.c +++ b/drivers/iio/frequency/adrf6780.c @@ -9,7 +9,6 @@ #include <linux/bits.h> #include <linux/clk.h> #include <linux/clkdev.h> -#include <linux/clk-provider.h> #include <linux/delay.h> #include <linux/device.h> #include <linux/iio/iio.h> diff --git a/drivers/iio/gyro/mpu3050-i2c.c b/drivers/iio/gyro/mpu3050-i2c.c index 52b6feed26..29ecfa6fd6 100644 --- a/drivers/iio/gyro/mpu3050-i2c.c +++ b/drivers/iio/gyro/mpu3050-i2c.c @@ -72,7 +72,7 @@ static int mpu3050_i2c_probe(struct i2c_client *client) else { mpu3050->i2cmux->priv = mpu3050; /* Ignore failure, not critical */ - i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0, 0); + i2c_mux_add_adapter(mpu3050->i2cmux, 0, 0); } return 0; diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index 37e619827e..6616729af5 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -613,6 +613,7 @@ static void max30102_remove(struct i2c_client *client) } static const struct i2c_device_id max30102_id[] = { + { "max30101", max30105 }, { "max30102", max30102 }, { "max30105", max30105 }, {} @@ -620,6 +621,7 @@ static const struct i2c_device_id max30102_id[] = { MODULE_DEVICE_TABLE(i2c, max30102_id); static const struct of_device_id max30102_dt_ids[] = { + { .compatible = "maxim,max30101" }, { .compatible = "maxim,max30102" }, { .compatible = "maxim,max30105" }, { } diff --git a/drivers/iio/humidity/hdc3020.c b/drivers/iio/humidity/hdc3020.c index 1e702fcc48..a82dcc3da4 100644 --- a/drivers/iio/humidity/hdc3020.c +++ b/drivers/iio/humidity/hdc3020.c @@ -15,12 +15,15 @@ #include <linux/cleanup.h> #include <linux/crc8.h> #include <linux/delay.h> +#include <linux/gpio/consumer.h> #include <linux/i2c.h> #include <linux/init.h> #include <linux/interrupt.h> #include <linux/math64.h> #include <linux/module.h> #include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/regulator/consumer.h> #include <linux/units.h> #include <asm/unaligned.h> @@ -71,6 +74,8 @@ struct hdc3020_data { struct i2c_client *client; + struct gpio_desc *reset_gpio; + struct regulator *vdd_supply; /* * Ensure that the sensor configuration (currently only heater is * supported) will not be changed during the process of reading @@ -724,9 +729,53 @@ static const struct iio_info hdc3020_info = { .write_event_value = hdc3020_write_thresh, }; -static void hdc3020_stop(void *data) +static int hdc3020_power_off(struct hdc3020_data *data) { - hdc3020_exec_cmd((struct hdc3020_data *)data, HDC3020_EXIT_AUTO); + hdc3020_exec_cmd(data, HDC3020_EXIT_AUTO); + + if (data->reset_gpio) + gpiod_set_value_cansleep(data->reset_gpio, 1); + + return regulator_disable(data->vdd_supply); +} + +static int hdc3020_power_on(struct hdc3020_data *data) +{ + int ret; + + ret = regulator_enable(data->vdd_supply); + if (ret) + return ret; + + fsleep(5000); + + if (data->reset_gpio) { + gpiod_set_value_cansleep(data->reset_gpio, 0); + fsleep(3000); + } + + if (data->client->irq) { + /* + * The alert output is activated by default upon power up, + * hardware reset, and soft reset. Clear the status register. + */ + ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS); + if (ret) { + hdc3020_power_off(data); + return ret; + } + } + + ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0); + if (ret) + hdc3020_power_off(data); + + return ret; +} + +static void hdc3020_exit(void *data) +{ + hdc3020_power_off(data); } static int hdc3020_probe(struct i2c_client *client) @@ -742,6 +791,8 @@ static int hdc3020_probe(struct i2c_client *client) if (!indio_dev) return -ENOMEM; + dev_set_drvdata(&client->dev, indio_dev); + data = iio_priv(indio_dev); data->client = client; mutex_init(&data->lock); @@ -753,6 +804,26 @@ static int hdc3020_probe(struct i2c_client *client) indio_dev->info = &hdc3020_info; indio_dev->channels = hdc3020_channels; indio_dev->num_channels = ARRAY_SIZE(hdc3020_channels); + + data->vdd_supply = devm_regulator_get(&client->dev, "vdd"); + if (IS_ERR(data->vdd_supply)) + return dev_err_probe(&client->dev, PTR_ERR(data->vdd_supply), + "Unable to get VDD regulator\n"); + + data->reset_gpio = devm_gpiod_get_optional(&client->dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(data->reset_gpio)) + return dev_err_probe(&client->dev, PTR_ERR(data->reset_gpio), + "Cannot get reset GPIO\n"); + + ret = hdc3020_power_on(data); + if (ret) + return dev_err_probe(&client->dev, ret, "Power on failed\n"); + + ret = devm_add_action_or_reset(&data->client->dev, hdc3020_exit, data); + if (ret) + return ret; + if (client->irq) { ret = devm_request_threaded_irq(&client->dev, client->irq, NULL, hdc3020_interrupt_handler, @@ -761,25 +832,8 @@ static int hdc3020_probe(struct i2c_client *client) if (ret) return dev_err_probe(&client->dev, ret, "Failed to request IRQ\n"); - - /* - * The alert output is activated by default upon power up, - * hardware reset, and soft reset. Clear the status register. - */ - ret = hdc3020_exec_cmd(data, HDC3020_S_STATUS); - if (ret) - return ret; } - ret = hdc3020_exec_cmd(data, HDC3020_S_AUTO_10HZ_MOD0); - if (ret) - return dev_err_probe(&client->dev, ret, - "Unable to set up measurement\n"); - - ret = devm_add_action_or_reset(&data->client->dev, hdc3020_stop, data); - if (ret) - return ret; - ret = devm_iio_device_register(&data->client->dev, indio_dev); if (ret) return dev_err_probe(&client->dev, ret, "Failed to add device"); @@ -787,6 +841,24 @@ static int hdc3020_probe(struct i2c_client *client) return 0; } +static int hdc3020_suspend(struct device *dev) +{ + struct iio_dev *iio_dev = dev_get_drvdata(dev); + struct hdc3020_data *data = iio_priv(iio_dev); + + return hdc3020_power_off(data); +} + +static int hdc3020_resume(struct device *dev) +{ + struct iio_dev *iio_dev = dev_get_drvdata(dev); + struct hdc3020_data *data = iio_priv(iio_dev); + + return hdc3020_power_on(data); +} + +static DEFINE_SIMPLE_DEV_PM_OPS(hdc3020_pm_ops, hdc3020_suspend, hdc3020_resume); + static const struct i2c_device_id hdc3020_id[] = { { "hdc3020" }, { "hdc3021" }, @@ -806,6 +878,7 @@ MODULE_DEVICE_TABLE(of, hdc3020_dt_ids); static struct i2c_driver hdc3020_driver = { .driver = { .name = "hdc3020", + .pm = pm_sleep_ptr(&hdc3020_pm_ops), .of_match_table = hdc3020_dt_ids, }, .probe = hdc3020_probe, diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index 2a413da87b..87627d116e 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -573,7 +573,7 @@ int hts221_probe(struct device *dev, int irq, const char *name, if (!iio_dev) return -ENOMEM; - dev_set_drvdata(dev, (void *)iio_dev); + dev_set_drvdata(dev, iio_dev); hw = iio_priv(iio_dev); hw->name = name; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600.h b/drivers/iio/imu/inv_icm42600/inv_icm42600.h index 0e290c807b..c4ac91f6ba 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600.h @@ -13,6 +13,7 @@ #include <linux/regulator/consumer.h> #include <linux/pm.h> #include <linux/iio/iio.h> +#include <linux/iio/common/inv_sensors_timestamp.h> #include "inv_icm42600_buffer.h" @@ -21,7 +22,9 @@ enum inv_icm42600_chip { INV_CHIP_ICM42600, INV_CHIP_ICM42602, INV_CHIP_ICM42605, + INV_CHIP_ICM42686, INV_CHIP_ICM42622, + INV_CHIP_ICM42688, INV_CHIP_ICM42631, INV_CHIP_NB, }; @@ -56,6 +59,17 @@ enum inv_icm42600_gyro_fs { INV_ICM42600_GYRO_FS_15_625DPS, INV_ICM42600_GYRO_FS_NB, }; +enum inv_icm42686_gyro_fs { + INV_ICM42686_GYRO_FS_4000DPS, + INV_ICM42686_GYRO_FS_2000DPS, + INV_ICM42686_GYRO_FS_1000DPS, + INV_ICM42686_GYRO_FS_500DPS, + INV_ICM42686_GYRO_FS_250DPS, + INV_ICM42686_GYRO_FS_125DPS, + INV_ICM42686_GYRO_FS_62_5DPS, + INV_ICM42686_GYRO_FS_31_25DPS, + INV_ICM42686_GYRO_FS_NB, +}; /* accelerometer fullscale values */ enum inv_icm42600_accel_fs { @@ -65,6 +79,14 @@ enum inv_icm42600_accel_fs { INV_ICM42600_ACCEL_FS_2G, INV_ICM42600_ACCEL_FS_NB, }; +enum inv_icm42686_accel_fs { + INV_ICM42686_ACCEL_FS_32G, + INV_ICM42686_ACCEL_FS_16G, + INV_ICM42686_ACCEL_FS_8G, + INV_ICM42686_ACCEL_FS_4G, + INV_ICM42686_ACCEL_FS_2G, + INV_ICM42686_ACCEL_FS_NB, +}; /* ODR suffixed by LN or LP are Low-Noise or Low-Power mode only */ enum inv_icm42600_odr { @@ -150,6 +172,19 @@ struct inv_icm42600_state { } timestamp; }; + +/** + * struct inv_icm42600_sensor_state - sensor state variables + * @scales: table of scales. + * @scales_len: length (nb of items) of the scales table. + * @ts: timestamp module states. + */ +struct inv_icm42600_sensor_state { + const int *scales; + size_t scales_len; + struct inv_sensors_timestamp ts; +}; + /* Virtual register addresses: @bank on MSB (4 upper bits), @address on LSB */ /* Bank selection register, available in all banks */ @@ -303,7 +338,9 @@ struct inv_icm42600_state { #define INV_ICM42600_WHOAMI_ICM42600 0x40 #define INV_ICM42600_WHOAMI_ICM42602 0x41 #define INV_ICM42600_WHOAMI_ICM42605 0x42 +#define INV_ICM42600_WHOAMI_ICM42686 0x44 #define INV_ICM42600_WHOAMI_ICM42622 0x46 +#define INV_ICM42600_WHOAMI_ICM42688 0x47 #define INV_ICM42600_WHOAMI_ICM42631 0x5C /* User bank 1 (MSB 0x10) */ diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c index 6d9cb010f6..4b25666936 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_accel.c @@ -99,7 +99,8 @@ static int inv_icm42600_accel_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &accel_st->ts; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; unsigned int fifo_en = 0; unsigned int sleep_temp = 0; @@ -206,33 +207,54 @@ static const int inv_icm42600_accel_scale[] = { [2 * INV_ICM42600_ACCEL_FS_2G] = 0, [2 * INV_ICM42600_ACCEL_FS_2G + 1] = 598550, }; +static const int inv_icm42686_accel_scale[] = { + /* +/- 32G => 0.009576807 m/s-2 */ + [2 * INV_ICM42686_ACCEL_FS_32G] = 0, + [2 * INV_ICM42686_ACCEL_FS_32G + 1] = 9576807, + /* +/- 16G => 0.004788403 m/s-2 */ + [2 * INV_ICM42686_ACCEL_FS_16G] = 0, + [2 * INV_ICM42686_ACCEL_FS_16G + 1] = 4788403, + /* +/- 8G => 0.002394202 m/s-2 */ + [2 * INV_ICM42686_ACCEL_FS_8G] = 0, + [2 * INV_ICM42686_ACCEL_FS_8G + 1] = 2394202, + /* +/- 4G => 0.001197101 m/s-2 */ + [2 * INV_ICM42686_ACCEL_FS_4G] = 0, + [2 * INV_ICM42686_ACCEL_FS_4G + 1] = 1197101, + /* +/- 2G => 0.000598550 m/s-2 */ + [2 * INV_ICM42686_ACCEL_FS_2G] = 0, + [2 * INV_ICM42686_ACCEL_FS_2G + 1] = 598550, +}; -static int inv_icm42600_accel_read_scale(struct inv_icm42600_state *st, +static int inv_icm42600_accel_read_scale(struct iio_dev *indio_dev, int *val, int *val2) { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); unsigned int idx; idx = st->conf.accel.fs; - *val = inv_icm42600_accel_scale[2 * idx]; - *val2 = inv_icm42600_accel_scale[2 * idx + 1]; + *val = accel_st->scales[2 * idx]; + *val2 = accel_st->scales[2 * idx + 1]; return IIO_VAL_INT_PLUS_NANO; } -static int inv_icm42600_accel_write_scale(struct inv_icm42600_state *st, +static int inv_icm42600_accel_write_scale(struct iio_dev *indio_dev, int val, int val2) { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); struct device *dev = regmap_get_device(st->map); unsigned int idx; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; int ret; - for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_accel_scale); idx += 2) { - if (val == inv_icm42600_accel_scale[idx] && - val2 == inv_icm42600_accel_scale[idx + 1]) + for (idx = 0; idx < accel_st->scales_len; idx += 2) { + if (val == accel_st->scales[idx] && + val2 == accel_st->scales[idx + 1]) break; } - if (idx >= ARRAY_SIZE(inv_icm42600_accel_scale)) + if (idx >= accel_st->scales_len) return -EINVAL; conf.fs = idx / 2; @@ -305,7 +327,8 @@ static int inv_icm42600_accel_write_odr(struct iio_dev *indio_dev, int val, int val2) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &accel_st->ts; struct device *dev = regmap_get_device(st->map); unsigned int idx; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; @@ -561,7 +584,7 @@ static int inv_icm42600_accel_read_raw(struct iio_dev *indio_dev, *val = data; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - return inv_icm42600_accel_read_scale(st, val, val2); + return inv_icm42600_accel_read_scale(indio_dev, val, val2); case IIO_CHAN_INFO_SAMP_FREQ: return inv_icm42600_accel_read_odr(st, val, val2); case IIO_CHAN_INFO_CALIBBIAS: @@ -576,14 +599,16 @@ static int inv_icm42600_accel_read_avail(struct iio_dev *indio_dev, const int **vals, int *type, int *length, long mask) { + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); + if (chan->type != IIO_ACCEL) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_SCALE: - *vals = inv_icm42600_accel_scale; + *vals = accel_st->scales; *type = IIO_VAL_INT_PLUS_NANO; - *length = ARRAY_SIZE(inv_icm42600_accel_scale); + *length = accel_st->scales_len; return IIO_AVAIL_LIST; case IIO_CHAN_INFO_SAMP_FREQ: *vals = inv_icm42600_accel_odr; @@ -614,7 +639,7 @@ static int inv_icm42600_accel_write_raw(struct iio_dev *indio_dev, ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; - ret = inv_icm42600_accel_write_scale(st, val, val2); + ret = inv_icm42600_accel_write_scale(indio_dev, val, val2); iio_device_release_direct_mode(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: @@ -701,8 +726,8 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) { struct device *dev = regmap_get_device(st->map); const char *name; + struct inv_icm42600_sensor_state *accel_st; struct inv_sensors_timestamp_chip ts_chip; - struct inv_sensors_timestamp *ts; struct iio_dev *indio_dev; int ret; @@ -710,9 +735,21 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) if (!name) return ERR_PTR(-ENOMEM); - indio_dev = devm_iio_device_alloc(dev, sizeof(*ts)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*accel_st)); if (!indio_dev) return ERR_PTR(-ENOMEM); + accel_st = iio_priv(indio_dev); + + switch (st->chip) { + case INV_CHIP_ICM42686: + accel_st->scales = inv_icm42686_accel_scale; + accel_st->scales_len = ARRAY_SIZE(inv_icm42686_accel_scale); + break; + default: + accel_st->scales = inv_icm42600_accel_scale; + accel_st->scales_len = ARRAY_SIZE(inv_icm42600_accel_scale); + break; + } /* * clock period is 32kHz (31250ns) @@ -721,8 +758,7 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) ts_chip.clock_period = 31250; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); - ts = iio_priv(indio_dev); - inv_sensors_timestamp_init(ts, &ts_chip); + inv_sensors_timestamp_init(&accel_st->ts, &ts_chip); iio_device_set_drvdata(indio_dev, st); indio_dev->name = name; @@ -747,7 +783,8 @@ struct iio_dev *inv_icm42600_accel_init(struct inv_icm42600_state *st) int inv_icm42600_accel_parse_fifo(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct inv_icm42600_sensor_state *accel_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &accel_st->ts; ssize_t i, size; unsigned int no; const void *accel, *gyro, *timestamp; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c index b52f328fd2..a8cf74c84c 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.c @@ -222,10 +222,15 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) latency_accel = period_accel * wm_accel; /* 0 value for watermark means that the sensor is turned off */ + if (wm_gyro == 0 && wm_accel == 0) + return 0; + if (latency_gyro == 0) { watermark = wm_accel; + st->fifo.watermark.eff_accel = wm_accel; } else if (latency_accel == 0) { watermark = wm_gyro; + st->fifo.watermark.eff_gyro = wm_gyro; } else { /* compute the smallest latency that is a multiple of both */ if (latency_gyro <= latency_accel) @@ -241,6 +246,13 @@ int inv_icm42600_buffer_update_watermark(struct inv_icm42600_state *st) watermark = latency / period; if (watermark < 1) watermark = 1; + /* update effective watermark */ + st->fifo.watermark.eff_gyro = latency / period_gyro; + if (st->fifo.watermark.eff_gyro < 1) + st->fifo.watermark.eff_gyro = 1; + st->fifo.watermark.eff_accel = latency / period_accel; + if (st->fifo.watermark.eff_accel < 1) + st->fifo.watermark.eff_accel = 1; } /* compute watermark value in bytes */ @@ -276,7 +288,8 @@ static int inv_icm42600_buffer_preenable(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); struct device *dev = regmap_get_device(st->map); - struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct inv_icm42600_sensor_state *sensor_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &sensor_st->ts; pm_runtime_get_sync(dev); @@ -502,6 +515,8 @@ int inv_icm42600_buffer_fifo_read(struct inv_icm42600_state *st, int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st) { + struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro); + struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel); struct inv_sensors_timestamp *ts; int ret; @@ -509,20 +524,20 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st) return 0; /* handle gyroscope timestamp and FIFO data parsing */ - ts = iio_priv(st->indio_gyro); - inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, - st->fifo.nb.gyro, st->timestamp.gyro); if (st->fifo.nb.gyro > 0) { + ts = &gyro_st->ts; + inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_gyro, + st->timestamp.gyro); ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro); if (ret) return ret; } /* handle accelerometer timestamp and FIFO data parsing */ - ts = iio_priv(st->indio_accel); - inv_sensors_timestamp_interrupt(ts, st->fifo.period, st->fifo.nb.total, - st->fifo.nb.accel, st->timestamp.accel); if (st->fifo.nb.accel > 0) { + ts = &accel_st->ts; + inv_sensors_timestamp_interrupt(ts, st->fifo.watermark.eff_accel, + st->timestamp.accel); ret = inv_icm42600_accel_parse_fifo(st->indio_accel); if (ret) return ret; @@ -534,6 +549,8 @@ int inv_icm42600_buffer_fifo_parse(struct inv_icm42600_state *st) int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st, unsigned int count) { + struct inv_icm42600_sensor_state *gyro_st = iio_priv(st->indio_gyro); + struct inv_icm42600_sensor_state *accel_st = iio_priv(st->indio_accel); struct inv_sensors_timestamp *ts; int64_t gyro_ts, accel_ts; int ret; @@ -549,20 +566,16 @@ int inv_icm42600_buffer_hwfifo_flush(struct inv_icm42600_state *st, return 0; if (st->fifo.nb.gyro > 0) { - ts = iio_priv(st->indio_gyro); - inv_sensors_timestamp_interrupt(ts, st->fifo.period, - st->fifo.nb.total, st->fifo.nb.gyro, - gyro_ts); + ts = &gyro_st->ts; + inv_sensors_timestamp_interrupt(ts, st->fifo.nb.gyro, gyro_ts); ret = inv_icm42600_gyro_parse_fifo(st->indio_gyro); if (ret) return ret; } if (st->fifo.nb.accel > 0) { - ts = iio_priv(st->indio_accel); - inv_sensors_timestamp_interrupt(ts, st->fifo.period, - st->fifo.nb.total, st->fifo.nb.accel, - accel_ts); + ts = &accel_st->ts; + inv_sensors_timestamp_interrupt(ts, st->fifo.nb.accel, accel_ts); ret = inv_icm42600_accel_parse_fifo(st->indio_accel); if (ret) return ret; @@ -576,6 +589,9 @@ int inv_icm42600_buffer_init(struct inv_icm42600_state *st) unsigned int val; int ret; + st->fifo.watermark.eff_gyro = 1; + st->fifo.watermark.eff_accel = 1; + /* * Default FIFO configuration (bits 7 to 5) * - use invalid value diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h index 8b85ee333b..f6c85daf42 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_buffer.h @@ -32,6 +32,8 @@ struct inv_icm42600_fifo { struct { unsigned int gyro; unsigned int accel; + unsigned int eff_gyro; + unsigned int eff_accel; } watermark; size_t count; struct { diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index a5e81906e3..62fdae5303 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -66,6 +66,22 @@ static const struct inv_icm42600_conf inv_icm42600_default_conf = { .temp_en = false, }; +static const struct inv_icm42600_conf inv_icm42686_default_conf = { + .gyro = { + .mode = INV_ICM42600_SENSOR_MODE_OFF, + .fs = INV_ICM42686_GYRO_FS_4000DPS, + .odr = INV_ICM42600_ODR_50HZ, + .filter = INV_ICM42600_FILTER_BW_ODR_DIV_2, + }, + .accel = { + .mode = INV_ICM42600_SENSOR_MODE_OFF, + .fs = INV_ICM42686_ACCEL_FS_32G, + .odr = INV_ICM42600_ODR_50HZ, + .filter = INV_ICM42600_FILTER_BW_ODR_DIV_2, + }, + .temp_en = false, +}; + static const struct inv_icm42600_hw inv_icm42600_hw[INV_CHIP_NB] = { [INV_CHIP_ICM42600] = { .whoami = INV_ICM42600_WHOAMI_ICM42600, @@ -82,11 +98,21 @@ static const struct inv_icm42600_hw inv_icm42600_hw[INV_CHIP_NB] = { .name = "icm42605", .conf = &inv_icm42600_default_conf, }, + [INV_CHIP_ICM42686] = { + .whoami = INV_ICM42600_WHOAMI_ICM42686, + .name = "icm42686", + .conf = &inv_icm42686_default_conf, + }, [INV_CHIP_ICM42622] = { .whoami = INV_ICM42600_WHOAMI_ICM42622, .name = "icm42622", .conf = &inv_icm42600_default_conf, }, + [INV_CHIP_ICM42688] = { + .whoami = INV_ICM42600_WHOAMI_ICM42688, + .name = "icm42688", + .conf = &inv_icm42600_default_conf, + }, [INV_CHIP_ICM42631] = { .whoami = INV_ICM42600_WHOAMI_ICM42631, .name = "icm42631", @@ -511,6 +537,7 @@ static int inv_icm42600_irq_init(struct inv_icm42600_state *st, int irq, if (ret) return ret; + irq_type |= IRQF_ONESHOT; return devm_request_threaded_irq(dev, irq, inv_icm42600_irq_timestamp, inv_icm42600_irq_handler, irq_type, "inv_icm42600", st); diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c index fc80b4a97f..938af5b640 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_gyro.c @@ -99,7 +99,8 @@ static int inv_icm42600_gyro_update_scan_mode(struct iio_dev *indio_dev, const unsigned long *scan_mask) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &gyro_st->ts; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; unsigned int fifo_en = 0; unsigned int sleep_gyro = 0; @@ -218,33 +219,63 @@ static const int inv_icm42600_gyro_scale[] = { [2 * INV_ICM42600_GYRO_FS_15_625DPS] = 0, [2 * INV_ICM42600_GYRO_FS_15_625DPS + 1] = 8322, }; +static const int inv_icm42686_gyro_scale[] = { + /* +/- 4000dps => 0.002130529 rad/s */ + [2 * INV_ICM42686_GYRO_FS_4000DPS] = 0, + [2 * INV_ICM42686_GYRO_FS_4000DPS + 1] = 2130529, + /* +/- 2000dps => 0.001065264 rad/s */ + [2 * INV_ICM42686_GYRO_FS_2000DPS] = 0, + [2 * INV_ICM42686_GYRO_FS_2000DPS + 1] = 1065264, + /* +/- 1000dps => 0.000532632 rad/s */ + [2 * INV_ICM42686_GYRO_FS_1000DPS] = 0, + [2 * INV_ICM42686_GYRO_FS_1000DPS + 1] = 532632, + /* +/- 500dps => 0.000266316 rad/s */ + [2 * INV_ICM42686_GYRO_FS_500DPS] = 0, + [2 * INV_ICM42686_GYRO_FS_500DPS + 1] = 266316, + /* +/- 250dps => 0.000133158 rad/s */ + [2 * INV_ICM42686_GYRO_FS_250DPS] = 0, + [2 * INV_ICM42686_GYRO_FS_250DPS + 1] = 133158, + /* +/- 125dps => 0.000066579 rad/s */ + [2 * INV_ICM42686_GYRO_FS_125DPS] = 0, + [2 * INV_ICM42686_GYRO_FS_125DPS + 1] = 66579, + /* +/- 62.5dps => 0.000033290 rad/s */ + [2 * INV_ICM42686_GYRO_FS_62_5DPS] = 0, + [2 * INV_ICM42686_GYRO_FS_62_5DPS + 1] = 33290, + /* +/- 31.25dps => 0.000016645 rad/s */ + [2 * INV_ICM42686_GYRO_FS_31_25DPS] = 0, + [2 * INV_ICM42686_GYRO_FS_31_25DPS + 1] = 16645, +}; -static int inv_icm42600_gyro_read_scale(struct inv_icm42600_state *st, +static int inv_icm42600_gyro_read_scale(struct iio_dev *indio_dev, int *val, int *val2) { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev); unsigned int idx; idx = st->conf.gyro.fs; - *val = inv_icm42600_gyro_scale[2 * idx]; - *val2 = inv_icm42600_gyro_scale[2 * idx + 1]; + *val = gyro_st->scales[2 * idx]; + *val2 = gyro_st->scales[2 * idx + 1]; return IIO_VAL_INT_PLUS_NANO; } -static int inv_icm42600_gyro_write_scale(struct inv_icm42600_state *st, +static int inv_icm42600_gyro_write_scale(struct iio_dev *indio_dev, int val, int val2) { + struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); + struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev); struct device *dev = regmap_get_device(st->map); unsigned int idx; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; int ret; - for (idx = 0; idx < ARRAY_SIZE(inv_icm42600_gyro_scale); idx += 2) { - if (val == inv_icm42600_gyro_scale[idx] && - val2 == inv_icm42600_gyro_scale[idx + 1]) + for (idx = 0; idx < gyro_st->scales_len; idx += 2) { + if (val == gyro_st->scales[idx] && + val2 == gyro_st->scales[idx + 1]) break; } - if (idx >= ARRAY_SIZE(inv_icm42600_gyro_scale)) + if (idx >= gyro_st->scales_len) return -EINVAL; conf.fs = idx / 2; @@ -317,7 +348,8 @@ static int inv_icm42600_gyro_write_odr(struct iio_dev *indio_dev, int val, int val2) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &gyro_st->ts; struct device *dev = regmap_get_device(st->map); unsigned int idx; struct inv_icm42600_sensor_conf conf = INV_ICM42600_SENSOR_CONF_INIT; @@ -572,7 +604,7 @@ static int inv_icm42600_gyro_read_raw(struct iio_dev *indio_dev, *val = data; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - return inv_icm42600_gyro_read_scale(st, val, val2); + return inv_icm42600_gyro_read_scale(indio_dev, val, val2); case IIO_CHAN_INFO_SAMP_FREQ: return inv_icm42600_gyro_read_odr(st, val, val2); case IIO_CHAN_INFO_CALIBBIAS: @@ -587,14 +619,16 @@ static int inv_icm42600_gyro_read_avail(struct iio_dev *indio_dev, const int **vals, int *type, int *length, long mask) { + struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev); + if (chan->type != IIO_ANGL_VEL) return -EINVAL; switch (mask) { case IIO_CHAN_INFO_SCALE: - *vals = inv_icm42600_gyro_scale; + *vals = gyro_st->scales; *type = IIO_VAL_INT_PLUS_NANO; - *length = ARRAY_SIZE(inv_icm42600_gyro_scale); + *length = gyro_st->scales_len; return IIO_AVAIL_LIST; case IIO_CHAN_INFO_SAMP_FREQ: *vals = inv_icm42600_gyro_odr; @@ -625,7 +659,7 @@ static int inv_icm42600_gyro_write_raw(struct iio_dev *indio_dev, ret = iio_device_claim_direct_mode(indio_dev); if (ret) return ret; - ret = inv_icm42600_gyro_write_scale(st, val, val2); + ret = inv_icm42600_gyro_write_scale(indio_dev, val, val2); iio_device_release_direct_mode(indio_dev); return ret; case IIO_CHAN_INFO_SAMP_FREQ: @@ -712,8 +746,8 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) { struct device *dev = regmap_get_device(st->map); const char *name; + struct inv_icm42600_sensor_state *gyro_st; struct inv_sensors_timestamp_chip ts_chip; - struct inv_sensors_timestamp *ts; struct iio_dev *indio_dev; int ret; @@ -721,9 +755,21 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) if (!name) return ERR_PTR(-ENOMEM); - indio_dev = devm_iio_device_alloc(dev, sizeof(*ts)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*gyro_st)); if (!indio_dev) return ERR_PTR(-ENOMEM); + gyro_st = iio_priv(indio_dev); + + switch (st->chip) { + case INV_CHIP_ICM42686: + gyro_st->scales = inv_icm42686_gyro_scale; + gyro_st->scales_len = ARRAY_SIZE(inv_icm42686_gyro_scale); + break; + default: + gyro_st->scales = inv_icm42600_gyro_scale; + gyro_st->scales_len = ARRAY_SIZE(inv_icm42600_gyro_scale); + break; + } /* * clock period is 32kHz (31250ns) @@ -732,8 +778,7 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) ts_chip.clock_period = 31250; ts_chip.jitter = 20; ts_chip.init_period = inv_icm42600_odr_to_period(st->conf.accel.odr); - ts = iio_priv(indio_dev); - inv_sensors_timestamp_init(ts, &ts_chip); + inv_sensors_timestamp_init(&gyro_st->ts, &ts_chip); iio_device_set_drvdata(indio_dev, st); indio_dev->name = name; @@ -759,7 +804,8 @@ struct iio_dev *inv_icm42600_gyro_init(struct inv_icm42600_state *st) int inv_icm42600_gyro_parse_fifo(struct iio_dev *indio_dev) { struct inv_icm42600_state *st = iio_device_get_drvdata(indio_dev); - struct inv_sensors_timestamp *ts = iio_priv(indio_dev); + struct inv_icm42600_sensor_state *gyro_st = iio_priv(indio_dev); + struct inv_sensors_timestamp *ts = &gyro_st->ts; ssize_t i, size; unsigned int no; const void *accel, *gyro, *timestamp; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c index 1af559403b..8d33504d77 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_i2c.c @@ -82,9 +82,15 @@ static const struct of_device_id inv_icm42600_of_matches[] = { .compatible = "invensense,icm42605", .data = (void *)INV_CHIP_ICM42605, }, { + .compatible = "invensense,icm42686", + .data = (void *)INV_CHIP_ICM42686, + }, { .compatible = "invensense,icm42622", .data = (void *)INV_CHIP_ICM42622, }, { + .compatible = "invensense,icm42688", + .data = (void *)INV_CHIP_ICM42688, + }, { .compatible = "invensense,icm42631", .data = (void *)INV_CHIP_ICM42631, }, diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c index 6be4ac7949..cc2bf1799a 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_spi.c @@ -78,9 +78,15 @@ static const struct of_device_id inv_icm42600_of_matches[] = { .compatible = "invensense,icm42605", .data = (void *)INV_CHIP_ICM42605, }, { + .compatible = "invensense,icm42686", + .data = (void *)INV_CHIP_ICM42686, + }, { .compatible = "invensense,icm42622", .data = (void *)INV_CHIP_ICM42622, }, { + .compatible = "invensense,icm42688", + .data = (void *)INV_CHIP_ICM42688, + }, { .compatible = "invensense,icm42631", .data = (void *)INV_CHIP_ICM42631, }, diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 0e94e5335e..14d95f34e9 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -15,6 +15,8 @@ #include <linux/acpi.h> #include <linux/platform_device.h> #include <linux/regulator/consumer.h> +#include <linux/math64.h> +#include <linux/minmax.h> #include <linux/pm.h> #include <linux/pm_runtime.h> #include <linux/property.h> @@ -287,7 +289,7 @@ static const struct inv_mpu6050_hw hw_info[] = { }; static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep, - int clock, int temp_dis) + bool cycle, int clock, int temp_dis) { u8 val; @@ -301,6 +303,8 @@ static int inv_mpu6050_pwr_mgmt_1_write(struct inv_mpu6050_state *st, bool sleep val |= INV_MPU6050_BIT_TEMP_DIS; if (sleep) val |= INV_MPU6050_BIT_SLEEP; + if (cycle) + val |= INV_MPU6050_BIT_CYCLE; dev_dbg(regmap_get_device(st->map), "pwr_mgmt_1: 0x%x\n", val); return regmap_write(st->map, st->reg->pwr_mgmt_1, val); @@ -316,7 +320,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st, case INV_MPU6000: case INV_MPU9150: /* old chips: switch clock manually */ - ret = inv_mpu6050_pwr_mgmt_1_write(st, false, clock, -1); + ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, clock, -1); if (ret) return ret; st->chip_config.clk = clock; @@ -332,7 +336,7 @@ static int inv_mpu6050_clock_switch(struct inv_mpu6050_state *st, int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, unsigned int mask) { - unsigned int sleep; + unsigned int sleep, val; u8 pwr_mgmt2, user_ctrl; int ret; @@ -345,12 +349,20 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, mask &= ~INV_MPU6050_SENSOR_TEMP; if (mask & INV_MPU6050_SENSOR_MAGN && en == st->chip_config.magn_en) mask &= ~INV_MPU6050_SENSOR_MAGN; + if (mask & INV_MPU6050_SENSOR_WOM && en == st->chip_config.wom_en) + mask &= ~INV_MPU6050_SENSOR_WOM; + + /* force accel on if WoM is on and not going off */ + if (!en && (mask & INV_MPU6050_SENSOR_ACCL) && st->chip_config.wom_en && + !(mask & INV_MPU6050_SENSOR_WOM)) + mask &= ~INV_MPU6050_SENSOR_ACCL; + if (mask == 0) return 0; /* turn on/off temperature sensor */ if (mask & INV_MPU6050_SENSOR_TEMP) { - ret = inv_mpu6050_pwr_mgmt_1_write(st, false, -1, !en); + ret = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, !en); if (ret) return ret; st->chip_config.temp_en = en; @@ -439,6 +451,16 @@ int inv_mpu6050_switch_engine(struct inv_mpu6050_state *st, bool en, } } + /* enable/disable accel intelligence control */ + if (mask & INV_MPU6050_SENSOR_WOM) { + val = en ? INV_MPU6500_BIT_ACCEL_INTEL_EN | + INV_MPU6500_BIT_ACCEL_INTEL_MODE : 0; + ret = regmap_write(st->map, INV_MPU6500_REG_ACCEL_INTEL_CTRL, val); + if (ret) + return ret; + st->chip_config.wom_en = en; + } + return 0; } @@ -447,7 +469,7 @@ static int inv_mpu6050_set_power_itg(struct inv_mpu6050_state *st, { int result; - result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, -1, -1); + result = inv_mpu6050_pwr_mgmt_1_write(st, !power_on, false, -1, -1); if (result) return result; @@ -477,22 +499,9 @@ static int inv_mpu6050_set_gyro_fsr(struct inv_mpu6050_state *st, return regmap_write(st->map, st->reg->gyro_config, data); } -/* - * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent - * - * MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope - * MPU6500 and above have a dedicated register for accelerometer - */ -static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st, - enum inv_mpu6050_filter_e val) +static int inv_mpu6050_set_accel_lpf_regs(struct inv_mpu6050_state *st, + enum inv_mpu6050_filter_e val) { - int result; - - result = regmap_write(st->map, st->reg->lpf, val); - if (result) - return result; - - /* set accel lpf */ switch (st->chip_type) { case INV_MPU6050: case INV_MPU6000: @@ -512,6 +521,25 @@ static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st, } /* + * inv_mpu6050_set_lpf_regs() - set low pass filter registers, chip dependent + * + * MPU60xx/MPU9150 use only 1 register for accelerometer + gyroscope + * MPU6500 and above have a dedicated register for accelerometer + */ +static int inv_mpu6050_set_lpf_regs(struct inv_mpu6050_state *st, + enum inv_mpu6050_filter_e val) +{ + int result; + + result = regmap_write(st->map, st->reg->lpf, val); + if (result) + return result; + + /* set accel lpf */ + return inv_mpu6050_set_accel_lpf_regs(st, val); +} + +/* * inv_mpu6050_init_config() - Initialize hardware, disable FIFO. * * Initial configuration: @@ -893,6 +921,317 @@ error_write_raw_unlock: return result; } +static u64 inv_mpu6050_convert_wom_to_roc(unsigned int threshold, unsigned int freq_div) +{ + /* 4mg per LSB converted in m/s² in micro (1000000) */ + const unsigned int convert = 4U * 9807U; + u64 value; + + value = threshold * convert; + + /* compute the differential by multiplying by the frequency */ + return div_u64(value * INV_MPU6050_INTERNAL_FREQ_HZ, freq_div); +} + +static unsigned int inv_mpu6050_convert_roc_to_wom(u64 roc, unsigned int freq_div) +{ + /* 4mg per LSB converted in m/s² in micro (1000000) */ + const unsigned int convert = 4U * 9807U; + u64 value; + + /* return 0 only if roc is 0 */ + if (roc == 0) + return 0; + + value = div_u64(roc * freq_div, convert * INV_MPU6050_INTERNAL_FREQ_HZ); + + /* limit value to 8 bits and prevent 0 */ + return min(255, max(1, value)); +} + +static int inv_mpu6050_set_wom_int(struct inv_mpu6050_state *st, bool on) +{ + unsigned int reg_val, val; + + switch (st->chip_type) { + case INV_MPU6050: + case INV_MPU6500: + case INV_MPU6515: + case INV_MPU6880: + case INV_MPU6000: + case INV_MPU9150: + case INV_MPU9250: + case INV_MPU9255: + reg_val = INV_MPU6500_BIT_WOM_INT_EN; + break; + default: + reg_val = INV_ICM20608_BIT_WOM_INT_EN; + break; + } + + val = on ? reg_val : 0; + + return regmap_update_bits(st->map, st->reg->int_enable, reg_val, val); +} + +static int inv_mpu6050_set_wom_threshold(struct inv_mpu6050_state *st, u64 value, + unsigned int freq_div) +{ + unsigned int threshold; + int result; + + /* convert roc to wom threshold and convert back to handle clipping */ + threshold = inv_mpu6050_convert_roc_to_wom(value, freq_div); + value = inv_mpu6050_convert_wom_to_roc(threshold, freq_div); + + dev_dbg(regmap_get_device(st->map), "wom_threshold: 0x%x\n", threshold); + + switch (st->chip_type) { + case INV_ICM20609: + case INV_ICM20689: + case INV_ICM20600: + case INV_ICM20602: + case INV_ICM20690: + st->data[0] = threshold; + st->data[1] = threshold; + st->data[2] = threshold; + result = regmap_bulk_write(st->map, INV_ICM20609_REG_ACCEL_WOM_X_THR, + st->data, 3); + break; + default: + result = regmap_write(st->map, INV_MPU6500_REG_WOM_THRESHOLD, threshold); + break; + } + if (result) + return result; + + st->chip_config.roc_threshold = value; + + return 0; +} + +static int inv_mpu6050_set_lp_odr(struct inv_mpu6050_state *st, unsigned int freq_div, + unsigned int *lp_div) +{ + static const unsigned int freq_dividers[] = {2, 4, 8, 16, 32, 64, 128, 256}; + static const unsigned int reg_values[] = { + INV_MPU6050_LPOSC_500HZ, INV_MPU6050_LPOSC_250HZ, + INV_MPU6050_LPOSC_125HZ, INV_MPU6050_LPOSC_62HZ, + INV_MPU6050_LPOSC_31HZ, INV_MPU6050_LPOSC_16HZ, + INV_MPU6050_LPOSC_8HZ, INV_MPU6050_LPOSC_4HZ, + }; + unsigned int val, i; + + switch (st->chip_type) { + case INV_ICM20609: + case INV_ICM20689: + case INV_ICM20600: + case INV_ICM20602: + case INV_ICM20690: + /* nothing to do */ + *lp_div = INV_MPU6050_FREQ_DIVIDER(st); + return 0; + default: + break; + } + + /* found the nearest superior frequency divider */ + i = ARRAY_SIZE(reg_values) - 1; + val = reg_values[i]; + *lp_div = freq_dividers[i]; + for (i = 0; i < ARRAY_SIZE(freq_dividers); ++i) { + if (freq_div <= freq_dividers[i]) { + val = reg_values[i]; + *lp_div = freq_dividers[i]; + break; + } + } + + dev_dbg(regmap_get_device(st->map), "lp_odr: 0x%x\n", val); + return regmap_write(st->map, INV_MPU6500_REG_LP_ODR, val); +} + +static int inv_mpu6050_set_wom_lp(struct inv_mpu6050_state *st, bool on) +{ + unsigned int lp_div; + int result; + + if (on) { + /* set low power ODR */ + result = inv_mpu6050_set_lp_odr(st, INV_MPU6050_FREQ_DIVIDER(st), &lp_div); + if (result) + return result; + /* disable accel low pass filter */ + result = inv_mpu6050_set_accel_lpf_regs(st, INV_MPU6050_FILTER_NOLPF); + if (result) + return result; + /* update wom threshold with new low-power frequency divider */ + result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, lp_div); + if (result) + return result; + /* set cycle mode */ + result = inv_mpu6050_pwr_mgmt_1_write(st, false, true, -1, -1); + } else { + /* disable cycle mode */ + result = inv_mpu6050_pwr_mgmt_1_write(st, false, false, -1, -1); + if (result) + return result; + /* restore wom threshold */ + result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, + INV_MPU6050_FREQ_DIVIDER(st)); + if (result) + return result; + /* restore accel low pass filter */ + result = inv_mpu6050_set_accel_lpf_regs(st, st->chip_config.lpf); + } + + return result; +} + +static int inv_mpu6050_enable_wom(struct inv_mpu6050_state *st, bool en) +{ + struct device *pdev = regmap_get_device(st->map); + unsigned int mask; + int result; + + if (en) { + result = pm_runtime_resume_and_get(pdev); + if (result) + return result; + + mask = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_WOM; + result = inv_mpu6050_switch_engine(st, true, mask); + if (result) + goto error_suspend; + + result = inv_mpu6050_set_wom_int(st, true); + if (result) + goto error_suspend; + } else { + result = inv_mpu6050_set_wom_int(st, false); + if (result) + dev_err(pdev, "error %d disabling WoM interrupt bit", result); + + /* disable only WoM and let accel be disabled by autosuspend */ + result = inv_mpu6050_switch_engine(st, false, INV_MPU6050_SENSOR_WOM); + if (result) { + dev_err(pdev, "error %d disabling WoM force off", result); + /* force WoM off */ + st->chip_config.wom_en = false; + } + + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); + } + + return result; + +error_suspend: + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); + return result; +} + +static int inv_mpu6050_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 inv_mpu6050_state *st = iio_priv(indio_dev); + + /* support only WoM (accel roc rising) event */ + if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC || + dir != IIO_EV_DIR_RISING) + return -EINVAL; + + guard(mutex)(&st->lock); + + return st->chip_config.wom_en ? 1 : 0; +} + +static int inv_mpu6050_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 inv_mpu6050_state *st = iio_priv(indio_dev); + int enable; + + /* support only WoM (accel roc rising) event */ + if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC || + dir != IIO_EV_DIR_RISING) + return -EINVAL; + + enable = !!state; + + guard(mutex)(&st->lock); + + if (st->chip_config.wom_en == enable) + return 0; + + return inv_mpu6050_enable_wom(st, enable); +} + +static int inv_mpu6050_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 inv_mpu6050_state *st = iio_priv(indio_dev); + u32 rem; + + /* support only WoM (accel roc rising) event value */ + if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC || + dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE) + return -EINVAL; + + guard(mutex)(&st->lock); + + /* return value in micro */ + *val = div_u64_rem(st->chip_config.roc_threshold, 1000000U, &rem); + *val2 = rem; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int inv_mpu6050_write_event_value(struct iio_dev *indio_dev, + const struct iio_chan_spec *chan, + enum iio_event_type type, + enum iio_event_direction dir, + enum iio_event_info info, + int val, int val2) +{ + struct inv_mpu6050_state *st = iio_priv(indio_dev); + struct device *pdev = regmap_get_device(st->map); + u64 value; + int result; + + /* support only WoM (accel roc rising) event value */ + if (chan->type != IIO_ACCEL || type != IIO_EV_TYPE_ROC || + dir != IIO_EV_DIR_RISING || info != IIO_EV_INFO_VALUE) + return -EINVAL; + + if (val < 0 || val2 < 0) + return -EINVAL; + + guard(mutex)(&st->lock); + + result = pm_runtime_resume_and_get(pdev); + if (result) + return result; + + value = (u64)val * 1000000ULL + (u64)val2; + result = inv_mpu6050_set_wom_threshold(st, value, INV_MPU6050_FREQ_DIVIDER(st)); + + pm_runtime_mark_last_busy(pdev); + pm_runtime_put_autosuspend(pdev); + + return result; +} + /* * inv_mpu6050_set_lpf() - set low pass filer based on fifo rate. * @@ -989,6 +1328,12 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, if (result) goto fifo_rate_fail_power_off; + /* update wom threshold since roc is dependent on sampling frequency */ + result = inv_mpu6050_set_wom_threshold(st, st->chip_config.roc_threshold, + INV_MPU6050_FREQ_DIVIDER(st)); + if (result) + goto fifo_rate_fail_power_off; + pm_runtime_mark_last_busy(pdev); fifo_rate_fail_power_off: pm_runtime_put_autosuspend(pdev); @@ -1089,6 +1434,15 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = { { } }; +static const struct iio_event_spec inv_wom_events[] = { + { + .type = IIO_EV_TYPE_ROC, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_ENABLE) | + BIT(IIO_EV_INFO_VALUE), + }, +}; + #define INV_MPU6050_CHAN(_type, _channel2, _index) \ { \ .type = _type, \ @@ -1124,7 +1478,31 @@ static const struct iio_chan_spec_ext_info inv_ext_info[] = { }, \ } -static const struct iio_chan_spec inv_mpu_channels[] = { +#define INV_MPU6050_EVENT_CHAN(_type, _channel2, _events, _events_nb) \ +{ \ + .type = _type, \ + .modified = 1, \ + .channel2 = _channel2, \ + .event_spec = _events, \ + .num_event_specs = _events_nb, \ + .scan_index = -1, \ +} + +static const struct iio_chan_spec inv_mpu6050_channels[] = { + IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP), + + INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP), + + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_MPU6050_SCAN_GYRO_X), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_MPU6050_SCAN_GYRO_Y), + INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_MPU6050_SCAN_GYRO_Z), + + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y), + INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), +}; + +static const struct iio_chan_spec inv_mpu6500_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(INV_MPU6050_SCAN_TIMESTAMP), INV_MPU6050_TEMP_CHAN(INV_MPU6050_SCAN_TEMP), @@ -1136,6 +1514,9 @@ static const struct iio_chan_spec inv_mpu_channels[] = { INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_MPU6050_SCAN_ACCL_X), INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_MPU6050_SCAN_ACCL_Y), INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z), + + INV_MPU6050_EVENT_CHAN(IIO_ACCEL, IIO_MOD_X_OR_Y_OR_Z, + inv_wom_events, ARRAY_SIZE(inv_wom_events)), }; #define INV_MPU6050_SCAN_MASK_3AXIS_ACCEL \ @@ -1326,6 +1707,10 @@ static const struct iio_info mpu_info = { .write_raw = &inv_mpu6050_write_raw, .write_raw_get_fmt = &inv_write_raw_get_fmt, .attrs = &inv_attribute_group, + .read_event_config = inv_mpu6050_read_event_config, + .write_event_config = inv_mpu6050_write_event_config, + .read_event_value = inv_mpu6050_read_event_value, + .write_event_value = inv_mpu6050_write_event_value, .validate_trigger = inv_mpu6050_validate_trigger, .debugfs_reg_access = &inv_mpu6050_reg_access, }; @@ -1537,6 +1922,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, irq_type); return -EINVAL; } + device_set_wakeup_capable(dev, true); st->vdd_supply = devm_regulator_get(dev, "vdd"); if (IS_ERR(st->vdd_supply)) @@ -1613,6 +1999,12 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, return result; switch (chip_type) { + case INV_MPU6000: + case INV_MPU6050: + indio_dev->channels = inv_mpu6050_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels); + indio_dev->available_scan_masks = inv_mpu_scan_masks; + break; case INV_MPU9150: indio_dev->channels = inv_mpu9150_channels; indio_dev->num_channels = ARRAY_SIZE(inv_mpu9150_channels); @@ -1626,13 +2018,13 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, break; case INV_ICM20600: case INV_ICM20602: - indio_dev->channels = inv_mpu_channels; - indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); + indio_dev->channels = inv_mpu6500_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels); indio_dev->available_scan_masks = inv_icm20602_scan_masks; break; default: - indio_dev->channels = inv_mpu_channels; - indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); + indio_dev->channels = inv_mpu6500_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels); indio_dev->available_scan_masks = inv_mpu_scan_masks; break; } @@ -1641,9 +2033,18 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, * auxiliary device in use. Otherwise Going back to 6-axis only. */ if (st->magn_disabled) { - indio_dev->channels = inv_mpu_channels; - indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels); - indio_dev->available_scan_masks = inv_mpu_scan_masks; + switch (chip_type) { + case INV_MPU9150: + indio_dev->channels = inv_mpu6050_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu6050_channels); + indio_dev->available_scan_masks = inv_mpu_scan_masks; + break; + default: + indio_dev->channels = inv_mpu6500_channels; + indio_dev->num_channels = ARRAY_SIZE(inv_mpu6500_channels); + indio_dev->available_scan_masks = inv_mpu_scan_masks; + break; + } } indio_dev->info = &mpu_info; @@ -1687,16 +2088,27 @@ static int inv_mpu_resume(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct inv_mpu6050_state *st = iio_priv(indio_dev); + bool wakeup; int result; - mutex_lock(&st->lock); - result = inv_mpu_core_enable_regulator_vddio(st); - if (result) - goto out_unlock; + guard(mutex)(&st->lock); - result = inv_mpu6050_set_power_itg(st, true); - if (result) - goto out_unlock; + wakeup = device_may_wakeup(dev) && st->chip_config.wom_en; + + if (wakeup) { + enable_irq(st->irq); + disable_irq_wake(st->irq); + result = inv_mpu6050_set_wom_lp(st, false); + if (result) + return result; + } else { + result = inv_mpu_core_enable_regulator_vddio(st); + if (result) + return result; + result = inv_mpu6050_set_power_itg(st, true); + if (result) + return result; + } pm_runtime_disable(dev); pm_runtime_set_active(dev); @@ -1704,14 +2116,17 @@ static int inv_mpu_resume(struct device *dev) result = inv_mpu6050_switch_engine(st, true, st->suspended_sensors); if (result) - goto out_unlock; + return result; + + if (st->chip_config.wom_en && !wakeup) { + result = inv_mpu6050_set_wom_int(st, true); + if (result) + return result; + } if (iio_buffer_enabled(indio_dev)) result = inv_mpu6050_prepare_fifo(st, true); -out_unlock: - mutex_unlock(&st->lock); - return result; } @@ -1719,23 +2134,30 @@ static int inv_mpu_suspend(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); struct inv_mpu6050_state *st = iio_priv(indio_dev); + bool wakeup; int result; - mutex_lock(&st->lock); + guard(mutex)(&st->lock); st->suspended_sensors = 0; - if (pm_runtime_suspended(dev)) { - result = 0; - goto out_unlock; - } + if (pm_runtime_suspended(dev)) + return 0; if (iio_buffer_enabled(indio_dev)) { result = inv_mpu6050_prepare_fifo(st, false); if (result) - goto out_unlock; + return result; + } + + wakeup = device_may_wakeup(dev) && st->chip_config.wom_en; + + if (st->chip_config.wom_en && !wakeup) { + result = inv_mpu6050_set_wom_int(st, false); + if (result) + return result; } - if (st->chip_config.accl_en) + if (st->chip_config.accl_en && !wakeup) st->suspended_sensors |= INV_MPU6050_SENSOR_ACCL; if (st->chip_config.gyro_en) st->suspended_sensors |= INV_MPU6050_SENSOR_GYRO; @@ -1743,19 +2165,26 @@ static int inv_mpu_suspend(struct device *dev) st->suspended_sensors |= INV_MPU6050_SENSOR_TEMP; if (st->chip_config.magn_en) st->suspended_sensors |= INV_MPU6050_SENSOR_MAGN; + if (st->chip_config.wom_en && !wakeup) + st->suspended_sensors |= INV_MPU6050_SENSOR_WOM; result = inv_mpu6050_switch_engine(st, false, st->suspended_sensors); if (result) - goto out_unlock; - - result = inv_mpu6050_set_power_itg(st, false); - if (result) - goto out_unlock; + return result; - inv_mpu_core_disable_regulator_vddio(st); -out_unlock: - mutex_unlock(&st->lock); + if (wakeup) { + result = inv_mpu6050_set_wom_lp(st, true); + if (result) + return result; + enable_irq_wake(st->irq); + disable_irq(st->irq); + } else { + result = inv_mpu6050_set_power_itg(st, false); + if (result) + return result; + inv_mpu_core_disable_regulator_vddio(st); + } - return result; + return 0; } static int inv_mpu_runtime_suspend(struct device *dev) @@ -1767,7 +2196,8 @@ static int inv_mpu_runtime_suspend(struct device *dev) mutex_lock(&st->lock); sensors = INV_MPU6050_SENSOR_ACCL | INV_MPU6050_SENSOR_GYRO | - INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN; + INV_MPU6050_SENSOR_TEMP | INV_MPU6050_SENSOR_MAGN | + INV_MPU6050_SENSOR_WOM; ret = inv_mpu6050_switch_engine(st, false, sensors); if (ret) goto out_unlock; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c index 410ea39fd4..0e03137fb3 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c @@ -142,7 +142,7 @@ static int inv_mpu_probe(struct i2c_client *client) if (!st->muxc) return -ENOMEM; st->muxc->priv = dev_get_drvdata(&client->dev); - result = i2c_mux_add_adapter(st->muxc, 0, 0, 0); + result = i2c_mux_add_adapter(st->muxc, 0, 0); if (result) return result; result = inv_mpu_acpi_create_mux_client(client); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h index 5950e2419e..e1c0c51468 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h @@ -88,11 +88,12 @@ enum inv_devices { INV_NUM_PARTS }; -/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer */ +/* chip sensors mask: accelerometer, gyroscope, temperature, magnetometer, WoM */ #define INV_MPU6050_SENSOR_ACCL BIT(0) #define INV_MPU6050_SENSOR_GYRO BIT(1) #define INV_MPU6050_SENSOR_TEMP BIT(2) #define INV_MPU6050_SENSOR_MAGN BIT(3) +#define INV_MPU6050_SENSOR_WOM BIT(4) /** * struct inv_mpu6050_chip_config - Cached chip configuration data. @@ -104,11 +105,13 @@ enum inv_devices { * @gyro_en: gyro engine enabled * @temp_en: temperature sensor enabled * @magn_en: magn engine (i2c master) enabled + * @wom_en: Wake-on-Motion enabled * @accl_fifo_enable: enable accel data output * @gyro_fifo_enable: enable gyro data output * @temp_fifo_enable: enable temp data output * @magn_fifo_enable: enable magn data output * @divider: chip sample rate divider (sample rate divider - 1) + * @roc_threshold: save ROC threshold (WoM) set value */ struct inv_mpu6050_chip_config { unsigned int clk:3; @@ -119,12 +122,14 @@ struct inv_mpu6050_chip_config { unsigned int gyro_en:1; unsigned int temp_en:1; unsigned int magn_en:1; + unsigned int wom_en:1; unsigned int accl_fifo_enable:1; unsigned int gyro_fifo_enable:1; unsigned int temp_fifo_enable:1; unsigned int magn_fifo_enable:1; u8 divider; u8 user_ctrl; + u64 roc_threshold; }; /* @@ -180,6 +185,7 @@ struct inv_mpu6050_hw { * @magn_orient: magnetometer sensor chip orientation if available. * @suspended_sensors: sensors mask of sensors turned off for suspend * @data: read buffer used for bulk reads. + * @it_timestamp: interrupt timestamp. */ struct inv_mpu6050_state { struct mutex lock; @@ -205,6 +211,7 @@ struct inv_mpu6050_state { unsigned int suspended_sensors; bool level_shifter; u8 *data; + s64 it_timestamp; }; /*register and associated bit definition*/ @@ -256,12 +263,16 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_INT_ENABLE 0x38 #define INV_MPU6050_BIT_DATA_RDY_EN 0x01 #define INV_MPU6050_BIT_DMP_INT_EN 0x02 +#define INV_MPU6500_BIT_WOM_INT_EN BIT(6) +#define INV_ICM20608_BIT_WOM_INT_EN GENMASK(7, 5) #define INV_MPU6050_REG_RAW_ACCEL 0x3B #define INV_MPU6050_REG_TEMPERATURE 0x41 #define INV_MPU6050_REG_RAW_GYRO 0x43 #define INV_MPU6050_REG_INT_STATUS 0x3A +#define INV_MPU6500_BIT_WOM_INT BIT(6) +#define INV_ICM20608_BIT_WOM_INT GENMASK(7, 5) #define INV_MPU6050_BIT_FIFO_OVERFLOW_INT 0x10 #define INV_MPU6050_BIT_RAW_DATA_RDY_INT 0x01 @@ -294,6 +305,7 @@ struct inv_mpu6050_state { #define INV_MPU6050_REG_PWR_MGMT_1 0x6B #define INV_MPU6050_BIT_H_RESET 0x80 #define INV_MPU6050_BIT_SLEEP 0x40 +#define INV_MPU6050_BIT_CYCLE 0x20 #define INV_MPU6050_BIT_TEMP_DIS 0x08 #define INV_MPU6050_BIT_CLK_MASK 0x7 @@ -301,6 +313,11 @@ struct inv_mpu6050_state { #define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38 #define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07 +/* ICM20609 registers */ +#define INV_ICM20609_REG_ACCEL_WOM_X_THR 0x20 +#define INV_ICM20609_REG_ACCEL_WOM_Y_THR 0x21 +#define INV_ICM20609_REG_ACCEL_WOM_Z_THR 0x22 + /* ICM20602 register */ #define INV_ICM20602_REG_I2C_IF 0x70 #define INV_ICM20602_BIT_I2C_IF_DIS 0x40 @@ -320,6 +337,11 @@ struct inv_mpu6050_state { /* mpu6500 registers */ #define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D #define INV_ICM20689_BITS_FIFO_SIZE_MAX 0xC0 +#define INV_MPU6500_REG_LP_ODR 0x1E +#define INV_MPU6500_REG_WOM_THRESHOLD 0x1F +#define INV_MPU6500_REG_ACCEL_INTEL_CTRL 0x69 +#define INV_MPU6500_BIT_ACCEL_INTEL_EN BIT(7) +#define INV_MPU6500_BIT_ACCEL_INTEL_MODE BIT(6) #define INV_MPU6500_REG_ACCEL_OFFSET 0x77 /* delay time in milliseconds */ @@ -432,6 +454,18 @@ enum inv_mpu6050_filter_e { NUM_MPU6050_FILTER }; +enum inv_mpu6050_lposc_e { + INV_MPU6050_LPOSC_4HZ = 4, + INV_MPU6050_LPOSC_8HZ, + INV_MPU6050_LPOSC_16HZ, + INV_MPU6050_LPOSC_31HZ, + INV_MPU6050_LPOSC_62HZ, + INV_MPU6050_LPOSC_125HZ, + INV_MPU6050_LPOSC_250HZ, + INV_MPU6050_LPOSC_500HZ, + NUM_MPU6050_LPOSC, +}; + /* IIO attribute address */ enum INV_MPU6050_IIO_ATTR_ADDR { ATTR_GYRO_MATRIX, diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c index d4f9b5d8d2..3d3b27f28c 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c @@ -33,10 +33,8 @@ static int inv_reset_fifo(struct iio_dev *indio_dev) reset_fifo_fail: dev_err(regmap_get_device(st->map), "reset fifo failed %d\n", result); - result = regmap_write(st->map, st->reg->int_enable, - INV_MPU6050_BIT_DATA_RDY_EN); - - return result; + return regmap_update_bits(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN); } /* @@ -53,21 +51,10 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) u32 fifo_period; s64 timestamp; u8 data[INV_MPU6050_OUTPUT_DATA_SIZE]; - int int_status; size_t i, nb; mutex_lock(&st->lock); - /* ack interrupt and check status */ - result = regmap_read(st->map, st->reg->int_status, &int_status); - if (result) { - dev_err(regmap_get_device(st->map), - "failed to ack interrupt\n"); - goto flush_fifo; - } - if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) - goto end_session; - if (!(st->chip_config.accl_fifo_enable | st->chip_config.gyro_fifo_enable | st->chip_config.magn_fifo_enable)) @@ -113,8 +100,8 @@ irqreturn_t inv_mpu6050_read_fifo(int irq, void *p) goto end_session; /* Each FIFO data contains all sensors, so same number for FIFO and sensor data */ fifo_period = NSEC_PER_SEC / INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); - inv_sensors_timestamp_interrupt(&st->timestamp, fifo_period, nb, nb, pf->timestamp); - inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, nb, 0); + inv_sensors_timestamp_interrupt(&st->timestamp, 1, pf->timestamp); + inv_sensors_timestamp_apply_odr(&st->timestamp, fifo_period, 1, 0); /* clear internal data buffer for avoiding kernel data leak */ memset(data, 0, sizeof(data)); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index e6e6e94452..84273660ca 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -6,6 +6,7 @@ #include <linux/pm_runtime.h> #include <linux/iio/common/inv_sensors_timestamp.h> +#include <linux/iio/events.h> #include "inv_mpu_iio.h" @@ -135,11 +136,13 @@ int inv_mpu6050_prepare_fifo(struct inv_mpu6050_state *st, bool enable) ret = regmap_write(st->map, st->reg->user_ctrl, d); if (ret) return ret; - /* enable interrupt */ - ret = regmap_write(st->map, st->reg->int_enable, - INV_MPU6050_BIT_DATA_RDY_EN); + /* enable data interrupt */ + ret = regmap_update_bits(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN, INV_MPU6050_BIT_DATA_RDY_EN); } else { - ret = regmap_write(st->map, st->reg->int_enable, 0); + /* disable data interrupt */ + ret = regmap_update_bits(st->map, st->reg->int_enable, + INV_MPU6050_BIT_DATA_RDY_EN, 0); if (ret) return ret; ret = regmap_write(st->map, st->reg->fifo_en, 0); @@ -172,9 +175,9 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) return result; /* * In case autosuspend didn't trigger, turn off first not - * required sensors. + * required sensors excepted WoM */ - result = inv_mpu6050_switch_engine(st, false, ~scan); + result = inv_mpu6050_switch_engine(st, false, ~scan & ~INV_MPU6050_SENSOR_WOM); if (result) goto error_power_off; result = inv_mpu6050_switch_engine(st, true, scan); @@ -226,6 +229,65 @@ static const struct iio_trigger_ops inv_mpu_trigger_ops = { .set_trigger_state = &inv_mpu_data_rdy_trigger_set_state, }; +static irqreturn_t inv_mpu6050_interrupt_timestamp(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct inv_mpu6050_state *st = iio_priv(indio_dev); + + st->it_timestamp = iio_get_time_ns(indio_dev); + + return IRQ_WAKE_THREAD; +} + +static irqreturn_t inv_mpu6050_interrupt_handle(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct inv_mpu6050_state *st = iio_priv(indio_dev); + unsigned int int_status, wom_bits; + u64 ev_code; + int result; + + switch (st->chip_type) { + case INV_MPU6050: + case INV_MPU6500: + case INV_MPU6515: + case INV_MPU6880: + case INV_MPU6000: + case INV_MPU9150: + case INV_MPU9250: + case INV_MPU9255: + wom_bits = INV_MPU6500_BIT_WOM_INT; + break; + default: + wom_bits = INV_ICM20608_BIT_WOM_INT; + break; + } + + scoped_guard(mutex, &st->lock) { + /* ack interrupt and check status */ + result = regmap_read(st->map, st->reg->int_status, &int_status); + if (result) { + dev_err(regmap_get_device(st->map), "failed to ack interrupt\n"); + return IRQ_HANDLED; + } + + /* handle WoM event */ + if (st->chip_config.wom_en && (int_status & wom_bits)) { + ev_code = IIO_MOD_EVENT_CODE(IIO_ACCEL, 0, IIO_MOD_X_OR_Y_OR_Z, + IIO_EV_TYPE_ROC, IIO_EV_DIR_RISING); + iio_push_event(indio_dev, ev_code, st->it_timestamp); + } + } + + /* handle raw data interrupt */ + if (int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT) { + indio_dev->pollfunc->timestamp = st->it_timestamp; + iio_trigger_poll_nested(st->trig); + } + + return IRQ_HANDLED; +} + int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type) { int ret; @@ -238,11 +300,11 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type) if (!st->trig) return -ENOMEM; - ret = devm_request_irq(&indio_dev->dev, st->irq, - &iio_trigger_generic_data_rdy_poll, - irq_type, - "inv_mpu", - st->trig); + irq_type |= IRQF_ONESHOT; + ret = devm_request_threaded_irq(&indio_dev->dev, st->irq, + &inv_mpu6050_interrupt_timestamp, + &inv_mpu6050_interrupt_handle, + irq_type, "inv_mpu", indio_dev); if (ret) return ret; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 0716986f98..937ff9c5a7 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -2726,7 +2726,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, if (!hw) return -ENOMEM; - dev_set_drvdata(dev, (void *)hw); + dev_set_drvdata(dev, hw); mutex_init(&hw->fifo_lock); mutex_init(&hw->conf_lock); diff --git a/drivers/iio/industrialio-acpi.c b/drivers/iio/industrialio-acpi.c new file mode 100644 index 0000000000..981b75d407 --- /dev/null +++ b/drivers/iio/industrialio-acpi.c @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* IIO ACPI helper functions */ + +#include <linux/acpi.h> +#include <linux/dev_printk.h> +#include <linux/iio/iio.h> +#include <linux/sprintf.h> + +/** + * iio_read_acpi_mount_matrix() - Read accelerometer mount matrix info from ACPI + * @dev: Device structure + * @orientation: iio_mount_matrix struct to fill + * @acpi_method: ACPI method name to read the matrix from, usually "ROTM" + * + * Try to read the mount-matrix by calling the specified method on the device's + * ACPI firmware-node. If the device has no ACPI firmware-node; or the method + * does not exist then this will fail silently. This expects the method to + * return data in the ACPI "ROTM" format defined by Microsoft: + * https://learn.microsoft.com/en-us/windows-hardware/drivers/sensors/sensors-acpi-entries + * This is a Microsoft extension and not part of the official ACPI spec. + * The method name is configurable because some dual-accel setups define 2 mount + * matrices in a single ACPI device using separate "ROMK" and "ROMS" methods. + * + * Returns: true if the matrix was successfully, false otherwise. + */ +bool iio_read_acpi_mount_matrix(struct device *dev, + struct iio_mount_matrix *orientation, + char *acpi_method) +{ + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_device *adev = ACPI_COMPANION(dev); + char *str; + union acpi_object *obj, *elements; + acpi_status status; + int i, j, val[3]; + bool ret = false; + + if (!adev || !acpi_has_method(adev->handle, acpi_method)) + return false; + + status = acpi_evaluate_object(adev->handle, acpi_method, NULL, &buffer); + if (ACPI_FAILURE(status)) { + dev_err(dev, "Failed to get ACPI mount matrix: %d\n", status); + return false; + } + + obj = buffer.pointer; + if (obj->type != ACPI_TYPE_PACKAGE || obj->package.count != 3) { + dev_err(dev, "Unknown ACPI mount matrix package format\n"); + goto out_free_buffer; + } + + elements = obj->package.elements; + for (i = 0; i < 3; i++) { + if (elements[i].type != ACPI_TYPE_STRING) { + dev_err(dev, "Unknown ACPI mount matrix element format\n"); + goto out_free_buffer; + } + + str = elements[i].string.pointer; + if (sscanf(str, "%d %d %d", &val[0], &val[1], &val[2]) != 3) { + dev_err(dev, "Incorrect ACPI mount matrix string format\n"); + goto out_free_buffer; + } + + for (j = 0; j < 3; j++) { + switch (val[j]) { + case -1: str = "-1"; break; + case 0: str = "0"; break; + case 1: str = "1"; break; + default: + dev_err(dev, "Invalid value in ACPI mount matrix: %d\n", val[j]); + goto out_free_buffer; + } + orientation->rotation[i * 3 + j] = str; + } + } + + ret = true; + +out_free_buffer: + kfree(buffer.pointer); + return ret; +} +EXPORT_SYMBOL_GPL(iio_read_acpi_mount_matrix); diff --git a/drivers/iio/industrialio-backend.c b/drivers/iio/industrialio-backend.c index 2fea2bbbe4..929aff4040 100644 --- a/drivers/iio/industrialio-backend.c +++ b/drivers/iio/industrialio-backend.c @@ -43,10 +43,12 @@ #include <linux/types.h> #include <linux/iio/backend.h> +#include <linux/iio/iio.h> struct iio_backend { struct list_head entry; const struct iio_backend_ops *ops; + struct device *frontend_dev; struct device *dev; struct module *owner; void *priv; @@ -113,8 +115,8 @@ static DEFINE_MUTEX(iio_back_lock); /** * iio_backend_chan_enable - Enable a backend channel - * @back: Backend device - * @chan: Channel number + * @back: Backend device + * @chan: Channel number * * RETURNS: * 0 on success, negative error number on failure. @@ -127,8 +129,8 @@ EXPORT_SYMBOL_NS_GPL(iio_backend_chan_enable, IIO_BACKEND); /** * iio_backend_chan_disable - Disable a backend channel - * @back: Backend device - * @chan: Channel number + * @back: Backend device + * @chan: Channel number * * RETURNS: * 0 on success, negative error number on failure. @@ -146,8 +148,8 @@ static void __iio_backend_disable(void *back) /** * devm_iio_backend_enable - Device managed backend enable - * @dev: Consumer device for the backend - * @back: Backend device + * @dev: Consumer device for the backend + * @back: Backend device * * RETURNS: * 0 on success, negative error number on failure. @@ -166,9 +168,9 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_backend_enable, IIO_BACKEND); /** * iio_backend_data_format_set - Configure the channel data format - * @back: Backend device - * @chan: Channel number - * @data: Data format + * @back: Backend device + * @chan: Channel number + * @data: Data format * * Properly configure a channel with respect to the expected data format. A * @struct iio_backend_data_fmt must be passed with the settings. @@ -186,6 +188,130 @@ int iio_backend_data_format_set(struct iio_backend *back, unsigned int chan, } EXPORT_SYMBOL_NS_GPL(iio_backend_data_format_set, IIO_BACKEND); +/** + * iio_backend_data_source_set - Select data source + * @back: Backend device + * @chan: Channel number + * @data: Data source + * + * A given backend may have different sources to stream/sync data. This allows + * to choose that source. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_data_source_set(struct iio_backend *back, unsigned int chan, + enum iio_backend_data_source data) +{ + if (data >= IIO_BACKEND_DATA_SOURCE_MAX) + return -EINVAL; + + return iio_backend_op_call(back, data_source_set, chan, data); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_data_source_set, IIO_BACKEND); + +/** + * iio_backend_set_sampling_freq - Set channel sampling rate + * @back: Backend device + * @chan: Channel number + * @sample_rate_hz: Sample rate + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_set_sampling_freq(struct iio_backend *back, unsigned int chan, + u64 sample_rate_hz) +{ + return iio_backend_op_call(back, set_sample_rate, chan, sample_rate_hz); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_set_sampling_freq, IIO_BACKEND); + +/** + * iio_backend_test_pattern_set - Configure a test pattern + * @back: Backend device + * @chan: Channel number + * @pattern: Test pattern + * + * Configure a test pattern on the backend. This is typically used for + * calibrating the timings on the data digital interface. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_test_pattern_set(struct iio_backend *back, + unsigned int chan, + enum iio_backend_test_pattern pattern) +{ + if (pattern >= IIO_BACKEND_TEST_PATTERN_MAX) + return -EINVAL; + + return iio_backend_op_call(back, test_pattern_set, chan, pattern); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_test_pattern_set, IIO_BACKEND); + +/** + * iio_backend_chan_status - Get the channel status + * @back: Backend device + * @chan: Channel number + * @error: Error indication + * + * Get the current state of the backend channel. Typically used to check if + * there were any errors sending/receiving data. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_chan_status(struct iio_backend *back, unsigned int chan, + bool *error) +{ + return iio_backend_op_call(back, chan_status, chan, error); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_chan_status, IIO_BACKEND); + +/** + * iio_backend_iodelay_set - Set digital I/O delay + * @back: Backend device + * @lane: Lane number + * @taps: Number of taps + * + * Controls delays on sending/receiving data. One usecase for this is to + * calibrate the data digital interface so we get the best results when + * transferring data. Note that @taps has no unit since the actual delay per tap + * is very backend specific. Hence, frontend devices typically should go through + * an array of @taps (the size of that array should typically match the size of + * calibration points on the frontend device) and call this API. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_iodelay_set(struct iio_backend *back, unsigned int lane, + unsigned int taps) +{ + return iio_backend_op_call(back, iodelay_set, lane, taps); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_iodelay_set, IIO_BACKEND); + +/** + * iio_backend_data_sample_trigger - Control when to sample data + * @back: Backend device + * @trigger: Data trigger + * + * Mostly useful for input backends. Configures the backend for when to sample + * data (eg: rising vs falling edge). + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_data_sample_trigger(struct iio_backend *back, + enum iio_backend_sample_trigger trigger) +{ + if (trigger >= IIO_BACKEND_SAMPLE_TRIGGER_MAX) + return -EINVAL; + + return iio_backend_op_call(back, data_sample_trigger, trigger); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_data_sample_trigger, IIO_BACKEND); + static void iio_backend_free_buffer(void *arg) { struct iio_backend_buffer_pair *pair = arg; @@ -195,9 +321,9 @@ static void iio_backend_free_buffer(void *arg) /** * devm_iio_backend_request_buffer - Device managed buffer request - * @dev: Consumer device for the backend - * @back: Backend device - * @indio_dev: IIO device + * @dev: Consumer device for the backend + * @back: Backend device + * @indio_dev: IIO device * * Request an IIO buffer from the backend. The type of the buffer (typically * INDIO_BUFFER_HARDWARE) is up to the backend to decide. This is because, @@ -231,6 +357,143 @@ int devm_iio_backend_request_buffer(struct device *dev, } EXPORT_SYMBOL_NS_GPL(devm_iio_backend_request_buffer, IIO_BACKEND); +static struct iio_backend *iio_backend_from_indio_dev_parent(const struct device *dev) +{ + struct iio_backend *back = ERR_PTR(-ENODEV), *iter; + + /* + * We deliberately go through all backends even after finding a match. + * The reason is that we want to catch frontend devices which have more + * than one backend in which case returning the first we find is bogus. + * For those cases, frontends need to explicitly define + * get_iio_backend() in struct iio_info. + */ + guard(mutex)(&iio_back_lock); + list_for_each_entry(iter, &iio_back_list, entry) { + if (dev == iter->frontend_dev) { + if (!IS_ERR(back)) { + dev_warn(dev, + "Multiple backends! get_iio_backend() needs to be implemented"); + return ERR_PTR(-ENODEV); + } + + back = iter; + } + } + + return back; +} + +/** + * iio_backend_ext_info_get - IIO ext_info read callback + * @indio_dev: IIO device + * @private: Data private to the driver + * @chan: IIO channel + * @buf: Buffer where to place the attribute data + * + * This helper is intended to be used by backends that extend an IIO channel + * (through iio_backend_extend_chan_spec()) with extended info. In that case, + * backends are not supposed to give their own callbacks (as they would not have + * a way to get the backend from indio_dev). This is the getter. + * + * RETURNS: + * Number of bytes written to buf, negative error number on failure. + */ +ssize_t iio_backend_ext_info_get(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, char *buf) +{ + struct iio_backend *back; + + /* + * The below should work for the majority of the cases. It will not work + * when one frontend has multiple backends in which case we'll need a + * new callback in struct iio_info so we can directly request the proper + * backend from the frontend. Anyways, let's only introduce new options + * when really needed... + */ + back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent); + if (IS_ERR(back)) + return PTR_ERR(back); + + return iio_backend_op_call(back, ext_info_get, private, chan, buf); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_get, IIO_BACKEND); + +/** + * iio_backend_ext_info_set - IIO ext_info write callback + * @indio_dev: IIO device + * @private: Data private to the driver + * @chan: IIO channel + * @buf: Buffer holding the sysfs attribute + * @len: Buffer length + * + * This helper is intended to be used by backends that extend an IIO channel + * (trough iio_backend_extend_chan_spec()) with extended info. In that case, + * backends are not supposed to give their own callbacks (as they would not have + * a way to get the backend from indio_dev). This is the setter. + * + * RETURNS: + * Buffer length on success, negative error number on failure. + */ +ssize_t iio_backend_ext_info_set(struct iio_dev *indio_dev, uintptr_t private, + const struct iio_chan_spec *chan, + const char *buf, size_t len) +{ + struct iio_backend *back; + + back = iio_backend_from_indio_dev_parent(indio_dev->dev.parent); + if (IS_ERR(back)) + return PTR_ERR(back); + + return iio_backend_op_call(back, ext_info_set, private, chan, buf, len); +} +EXPORT_SYMBOL_NS_GPL(iio_backend_ext_info_set, IIO_BACKEND); + +/** + * iio_backend_extend_chan_spec - Extend an IIO channel + * @indio_dev: IIO device + * @back: Backend device + * @chan: IIO channel + * + * Some backends may have their own functionalities and hence capable of + * extending a frontend's channel. + * + * RETURNS: + * 0 on success, negative error number on failure. + */ +int iio_backend_extend_chan_spec(struct iio_dev *indio_dev, + struct iio_backend *back, + struct iio_chan_spec *chan) +{ + const struct iio_chan_spec_ext_info *frontend_ext_info = chan->ext_info; + const struct iio_chan_spec_ext_info *back_ext_info; + int ret; + + ret = iio_backend_op_call(back, extend_chan_spec, chan); + if (ret) + return ret; + /* + * Let's keep things simple for now. Don't allow to overwrite the + * frontend's extended info. If ever needed, we can support appending + * it. + */ + if (frontend_ext_info && chan->ext_info != frontend_ext_info) + return -EOPNOTSUPP; + if (!chan->ext_info) + return 0; + + /* Don't allow backends to get creative and force their own handlers */ + for (back_ext_info = chan->ext_info; back_ext_info->name; back_ext_info++) { + if (back_ext_info->read != iio_backend_ext_info_get) + return -EINVAL; + if (back_ext_info->write != iio_backend_ext_info_set) + return -EINVAL; + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_backend_extend_chan_spec, IIO_BACKEND); + static void iio_backend_release(void *arg) { struct iio_backend *back = arg; @@ -263,6 +526,8 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back) "Could not link to supplier(%s)\n", dev_name(back->dev)); + back->frontend_dev = dev; + dev_dbg(dev, "Found backend(%s) device\n", dev_name(back->dev)); return 0; @@ -270,8 +535,8 @@ static int __devm_iio_backend_get(struct device *dev, struct iio_backend *back) /** * devm_iio_backend_get - Device managed backend device get - * @dev: Consumer device for the backend - * @name: Backend name + * @dev: Consumer device for the backend + * @name: Backend name * * Get's the backend associated with @dev. * @@ -322,8 +587,8 @@ EXPORT_SYMBOL_NS_GPL(devm_iio_backend_get, IIO_BACKEND); /** * __devm_iio_backend_get_from_fwnode_lookup - Device managed fwnode backend device get - * @dev: Consumer device for the backend - * @fwnode: Firmware node of the backend device + * @dev: Consumer device for the backend + * @fwnode: Firmware node of the backend device * * Search the backend list for a device matching @fwnode. * This API should not be used and it's only present for preventing the first @@ -357,7 +622,7 @@ EXPORT_SYMBOL_NS_GPL(__devm_iio_backend_get_from_fwnode_lookup, IIO_BACKEND); /** * iio_backend_get_priv - Get driver private data - * @back: Backend device + * @back: Backend device */ void *iio_backend_get_priv(const struct iio_backend *back) { @@ -375,9 +640,9 @@ static void iio_backend_unregister(void *arg) /** * devm_iio_backend_register - Device managed backend device register - * @dev: Backend device being registered - * @ops: Backend ops - * @priv: Device private data + * @dev: Backend device being registered + * @ops: Backend ops + * @priv: Device private data * * @ops is mandatory. Not providing it results in -EINVAL. * diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index b581a7e805..cec58a604d 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -10,6 +10,7 @@ * - Alternative access techniques? */ #include <linux/anon_inodes.h> +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/export.h> #include <linux/device.h> @@ -533,28 +534,26 @@ static ssize_t iio_scan_el_store(struct device *dev, ret = kstrtobool(buf, &state); if (ret < 0) return ret; - mutex_lock(&iio_dev_opaque->mlock); - if (iio_buffer_is_active(buffer)) { - ret = -EBUSY; - goto error_ret; - } + + guard(mutex)(&iio_dev_opaque->mlock); + if (iio_buffer_is_active(buffer)) + return -EBUSY; + ret = iio_scan_mask_query(indio_dev, buffer, this_attr->address); if (ret < 0) - goto error_ret; - if (!state && ret) { - ret = iio_scan_mask_clear(buffer, this_attr->address); - if (ret) - goto error_ret; - } else if (state && !ret) { - ret = iio_scan_mask_set(indio_dev, buffer, this_attr->address); - if (ret) - goto error_ret; - } + return ret; -error_ret: - mutex_unlock(&iio_dev_opaque->mlock); + if (state && ret) + return len; - return ret < 0 ? ret : len; + if (state) + ret = iio_scan_mask_set(indio_dev, buffer, this_attr->address); + else + ret = iio_scan_mask_clear(buffer, this_attr->address); + if (ret) + return ret; + + return len; } static ssize_t iio_scan_el_ts_show(struct device *dev, @@ -581,16 +580,13 @@ static ssize_t iio_scan_el_ts_store(struct device *dev, if (ret < 0) return ret; - mutex_lock(&iio_dev_opaque->mlock); - if (iio_buffer_is_active(buffer)) { - ret = -EBUSY; - goto error_ret; - } + guard(mutex)(&iio_dev_opaque->mlock); + if (iio_buffer_is_active(buffer)) + return -EBUSY; + buffer->scan_timestamp = state; -error_ret: - mutex_unlock(&iio_dev_opaque->mlock); - return ret ? ret : len; + return len; } static int iio_buffer_add_channel_sysfs(struct iio_dev *indio_dev, @@ -674,21 +670,16 @@ static ssize_t length_store(struct device *dev, struct device_attribute *attr, if (val == buffer->length) return len; - mutex_lock(&iio_dev_opaque->mlock); - if (iio_buffer_is_active(buffer)) { - ret = -EBUSY; - } else { - buffer->access->set_length(buffer, val); - ret = 0; - } - if (ret) - goto out; + guard(mutex)(&iio_dev_opaque->mlock); + if (iio_buffer_is_active(buffer)) + return -EBUSY; + + buffer->access->set_length(buffer, val); + if (buffer->length && buffer->length < buffer->watermark) buffer->watermark = buffer->length; -out: - mutex_unlock(&iio_dev_opaque->mlock); - return ret ? ret : len; + return len; } static ssize_t enable_show(struct device *dev, struct device_attribute *attr, @@ -1268,7 +1259,6 @@ int iio_update_buffers(struct iio_dev *indio_dev, struct iio_buffer *remove_buffer) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - int ret; if (insert_buffer == remove_buffer) return 0; @@ -1277,8 +1267,8 @@ int iio_update_buffers(struct iio_dev *indio_dev, insert_buffer->direction == IIO_BUFFER_DIRECTION_OUT) return -EINVAL; - mutex_lock(&iio_dev_opaque->info_exist_lock); - mutex_lock(&iio_dev_opaque->mlock); + guard(mutex)(&iio_dev_opaque->info_exist_lock); + guard(mutex)(&iio_dev_opaque->mlock); if (insert_buffer && iio_buffer_is_active(insert_buffer)) insert_buffer = NULL; @@ -1286,23 +1276,13 @@ int iio_update_buffers(struct iio_dev *indio_dev, if (remove_buffer && !iio_buffer_is_active(remove_buffer)) remove_buffer = NULL; - if (!insert_buffer && !remove_buffer) { - ret = 0; - goto out_unlock; - } - - if (!indio_dev->info) { - ret = -ENODEV; - goto out_unlock; - } - - ret = __iio_update_buffers(indio_dev, insert_buffer, remove_buffer); + if (!insert_buffer && !remove_buffer) + return 0; -out_unlock: - mutex_unlock(&iio_dev_opaque->mlock); - mutex_unlock(&iio_dev_opaque->info_exist_lock); + if (!indio_dev->info) + return -ENODEV; - return ret; + return __iio_update_buffers(indio_dev, insert_buffer, remove_buffer); } EXPORT_SYMBOL_GPL(iio_update_buffers); @@ -1326,22 +1306,22 @@ static ssize_t enable_store(struct device *dev, struct device_attribute *attr, if (ret < 0) return ret; - mutex_lock(&iio_dev_opaque->mlock); + guard(mutex)(&iio_dev_opaque->mlock); /* Find out if it is in the list */ inlist = iio_buffer_is_active(buffer); /* Already in desired state */ if (inlist == requested_state) - goto done; + return len; if (requested_state) ret = __iio_update_buffers(indio_dev, buffer, NULL); else ret = __iio_update_buffers(indio_dev, NULL, buffer); + if (ret) + return ret; -done: - mutex_unlock(&iio_dev_opaque->mlock); - return (ret < 0) ? ret : len; + return len; } static ssize_t watermark_show(struct device *dev, struct device_attribute *attr, @@ -1368,23 +1348,17 @@ static ssize_t watermark_store(struct device *dev, if (!val) return -EINVAL; - mutex_lock(&iio_dev_opaque->mlock); + guard(mutex)(&iio_dev_opaque->mlock); - if (val > buffer->length) { - ret = -EINVAL; - goto out; - } + if (val > buffer->length) + return -EINVAL; - if (iio_buffer_is_active(buffer)) { - ret = -EBUSY; - goto out; - } + if (iio_buffer_is_active(buffer)) + return -EBUSY; buffer->watermark = val; -out: - mutex_unlock(&iio_dev_opaque->mlock); - return ret ? ret : len; + return len; } static ssize_t data_available_show(struct device *dev, @@ -1770,7 +1744,7 @@ int iio_buffers_alloc_sysfs_and_mask(struct iio_dev *indio_dev) channels = indio_dev->channels; if (channels) { - int ml = indio_dev->masklength; + int ml = 0; for (i = 0; i < indio_dev->num_channels; i++) ml = max(ml, channels[i].scan_index + 1); diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 8684ba2469..fa7cc051b4 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -11,6 +11,7 @@ #include <linux/anon_inodes.h> #include <linux/cdev.h> +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/device.h> #include <linux/err.h> @@ -1643,11 +1644,10 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) struct iio_dev *indio_dev; size_t alloc_size; - alloc_size = sizeof(struct iio_dev_opaque); - if (sizeof_priv) { - alloc_size = ALIGN(alloc_size, IIO_DMA_MINALIGN); - alloc_size += sizeof_priv; - } + if (sizeof_priv) + alloc_size = ALIGN(sizeof(*iio_dev_opaque), IIO_DMA_MINALIGN) + sizeof_priv; + else + alloc_size = sizeof(*iio_dev_opaque); iio_dev_opaque = kzalloc(alloc_size, GFP_KERNEL); if (!iio_dev_opaque) @@ -1811,31 +1811,24 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) 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; - - mutex_lock(&iio_dev_opaque->info_exist_lock); + int ret; + guard(mutex)(&iio_dev_opaque->info_exist_lock); /* * The NULL check here is required to prevent crashing when a device * is being removed while userspace would still have open file handles * to try to access this device. */ if (!indio_dev->info) - goto out_unlock; + return -ENODEV; list_for_each_entry(h, &iio_dev_opaque->ioctl_handlers, entry) { ret = h->ioctl(indio_dev, filp, cmd, arg); if (ret != IIO_IOCTL_UNHANDLED) - break; + return ret; } - if (ret == IIO_IOCTL_UNHANDLED) - ret = -ENODEV; - -out_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); - - return ret; + return -ENODEV; } static const struct file_operations iio_buffer_fileops = { @@ -2063,18 +2056,16 @@ void iio_device_unregister(struct iio_dev *indio_dev) cdev_device_del(&iio_dev_opaque->chrdev, &indio_dev->dev); - mutex_lock(&iio_dev_opaque->info_exist_lock); + scoped_guard(mutex, &iio_dev_opaque->info_exist_lock) { + iio_device_unregister_debugfs(indio_dev); - iio_device_unregister_debugfs(indio_dev); + iio_disable_all_buffers(indio_dev); - iio_disable_all_buffers(indio_dev); + indio_dev->info = NULL; - indio_dev->info = NULL; - - iio_device_wakeup_eventset(indio_dev); - iio_buffer_wakeup_poll(indio_dev); - - mutex_unlock(&iio_dev_opaque->info_exist_lock); + iio_device_wakeup_eventset(indio_dev); + iio_buffer_wakeup_poll(indio_dev); + } iio_buffers_free_sysfs_and_mask(indio_dev); } diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c index b51eb6cb76..59d7615c0f 100644 --- a/drivers/iio/industrialio-gts-helper.c +++ b/drivers/iio/industrialio-gts-helper.c @@ -362,17 +362,20 @@ static int iio_gts_build_avail_time_table(struct iio_gts *gts) for (i = gts->num_itime - 1; i >= 0; i--) { int new = gts->itime_table[i].time_us; - if (times[idx] < new) { + if (idx == 0 || times[idx - 1] < new) { times[idx++] = new; continue; } - for (j = 0; j <= idx; j++) { + for (j = 0; j < idx; j++) { + if (times[j] == new) + break; if (times[j] > new) { memmove(×[j + 1], ×[j], (idx - j) * sizeof(int)); times[j] = new; idx++; + break; } } } diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index b5fed8a000..2e84776f4f 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -4,6 +4,7 @@ * Copyright (c) 2008 Jonathan Cameron */ +#include <linux/cleanup.h> #include <linux/kernel.h> #include <linux/idr.h> #include <linux/err.h> @@ -80,19 +81,18 @@ int iio_trigger_register(struct iio_trigger *trig_info) goto error_unregister_id; /* Add to list of available triggers held by the IIO core */ - mutex_lock(&iio_trigger_list_lock); - if (__iio_trigger_find_by_name(trig_info->name)) { - pr_err("Duplicate trigger name '%s'\n", trig_info->name); - ret = -EEXIST; - goto error_device_del; + scoped_guard(mutex, &iio_trigger_list_lock) { + if (__iio_trigger_find_by_name(trig_info->name)) { + pr_err("Duplicate trigger name '%s'\n", trig_info->name); + ret = -EEXIST; + goto error_device_del; + } + list_add_tail(&trig_info->list, &iio_trigger_list); } - list_add_tail(&trig_info->list, &iio_trigger_list); - mutex_unlock(&iio_trigger_list_lock); return 0; error_device_del: - mutex_unlock(&iio_trigger_list_lock); device_del(&trig_info->dev); error_unregister_id: ida_free(&iio_trigger_ida, trig_info->id); @@ -102,9 +102,8 @@ EXPORT_SYMBOL(iio_trigger_register); void iio_trigger_unregister(struct iio_trigger *trig_info) { - mutex_lock(&iio_trigger_list_lock); - list_del(&trig_info->list); - mutex_unlock(&iio_trigger_list_lock); + scoped_guard(mutex, &iio_trigger_list_lock) + list_del(&trig_info->list); ida_free(&iio_trigger_ida, trig_info->id); /* Possible issue in here */ @@ -120,12 +119,11 @@ int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *tri return -EINVAL; iio_dev_opaque = to_iio_dev_opaque(indio_dev); - mutex_lock(&iio_dev_opaque->mlock); + guard(mutex)(&iio_dev_opaque->mlock); WARN_ON(iio_dev_opaque->trig_readonly); indio_dev->trig = iio_trigger_get(trig); iio_dev_opaque->trig_readonly = true; - mutex_unlock(&iio_dev_opaque->mlock); return 0; } @@ -145,18 +143,14 @@ static struct iio_trigger *__iio_trigger_find_by_name(const char *name) static struct iio_trigger *iio_trigger_acquire_by_name(const char *name) { - struct iio_trigger *trig = NULL, *iter; + struct iio_trigger *iter; - mutex_lock(&iio_trigger_list_lock); + guard(mutex)(&iio_trigger_list_lock); list_for_each_entry(iter, &iio_trigger_list, list) - if (sysfs_streq(iter->name, name)) { - trig = iter; - iio_trigger_get(trig); - break; - } - mutex_unlock(&iio_trigger_list_lock); + if (sysfs_streq(iter->name, name)) + return iio_trigger_get(iter); - return trig; + return NULL; } static void iio_reenable_work_fn(struct work_struct *work) @@ -259,22 +253,21 @@ 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, - ilog2(1)); - mutex_unlock(&trig->pool_lock); - if (ret >= 0) - ret += trig->subirq_base; + scoped_guard(mutex, &trig->pool_lock) { + ret = bitmap_find_free_region(trig->pool, + CONFIG_IIO_CONSUMERS_PER_TRIGGER, + ilog2(1)); + if (ret < 0) + return ret; + } - return ret; + return ret + trig->subirq_base; } static void iio_trigger_put_irq(struct iio_trigger *trig, int irq) { - mutex_lock(&trig->pool_lock); + guard(mutex)(&trig->pool_lock); clear_bit(irq - trig->subirq_base, trig->pool); - mutex_unlock(&trig->pool_lock); } /* Complexity in here. With certain triggers (datardy) an acknowledgement @@ -451,16 +444,12 @@ static ssize_t current_trigger_store(struct device *dev, struct iio_trigger *trig; int ret; - mutex_lock(&iio_dev_opaque->mlock); - if (iio_dev_opaque->currentmode == INDIO_BUFFER_TRIGGERED) { - mutex_unlock(&iio_dev_opaque->mlock); - return -EBUSY; - } - if (iio_dev_opaque->trig_readonly) { - mutex_unlock(&iio_dev_opaque->mlock); - return -EPERM; + scoped_guard(mutex, &iio_dev_opaque->mlock) { + if (iio_dev_opaque->currentmode == INDIO_BUFFER_TRIGGERED) + return -EBUSY; + if (iio_dev_opaque->trig_readonly) + return -EPERM; } - mutex_unlock(&iio_dev_opaque->mlock); trig = iio_trigger_acquire_by_name(buf); if (oldtrig == trig) { diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 7a1f671331..485e6fc44a 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -3,6 +3,7 @@ * * Copyright (c) 2011 Jonathan Cameron */ +#include <linux/cleanup.h> #include <linux/err.h> #include <linux/export.h> #include <linux/minmax.h> @@ -43,13 +44,14 @@ static int iio_map_array_unregister_locked(struct iio_dev *indio_dev) int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps) { - int i = 0, ret = 0; struct iio_map_internal *mapi; + int i = 0; + int ret; if (!maps) return 0; - mutex_lock(&iio_map_list_lock); + guard(mutex)(&iio_map_list_lock); while (maps[i].consumer_dev_name) { mapi = kzalloc(sizeof(*mapi), GFP_KERNEL); if (!mapi) { @@ -61,11 +63,10 @@ int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps) list_add_tail(&mapi->l, &iio_map_list); i++; } -error_ret: - if (ret) - iio_map_array_unregister_locked(indio_dev); - mutex_unlock(&iio_map_list_lock); + return 0; +error_ret: + iio_map_array_unregister_locked(indio_dev); return ret; } EXPORT_SYMBOL_GPL(iio_map_array_register); @@ -75,13 +76,8 @@ EXPORT_SYMBOL_GPL(iio_map_array_register); */ int iio_map_array_unregister(struct iio_dev *indio_dev) { - int ret; - - mutex_lock(&iio_map_list_lock); - ret = iio_map_array_unregister_locked(indio_dev); - mutex_unlock(&iio_map_list_lock); - - return ret; + guard(mutex)(&iio_map_list_lock); + return iio_map_array_unregister_locked(indio_dev); } EXPORT_SYMBOL_GPL(iio_map_array_unregister); @@ -183,25 +179,21 @@ err_put: static struct iio_channel *fwnode_iio_channel_get(struct fwnode_handle *fwnode, int index) { - struct iio_channel *channel; int err; if (index < 0) return ERR_PTR(-EINVAL); - channel = kzalloc(sizeof(*channel), GFP_KERNEL); + struct iio_channel *channel __free(kfree) = + kzalloc(sizeof(*channel), GFP_KERNEL); if (!channel) return ERR_PTR(-ENOMEM); err = __fwnode_iio_channel_get(channel, fwnode, index); if (err) - goto err_free_channel; + return ERR_PTR(err); - return channel; - -err_free_channel: - kfree(channel); - return ERR_PTR(err); + return_ptr(channel); } static struct iio_channel * @@ -291,7 +283,6 @@ EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name); static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) { struct fwnode_handle *fwnode = dev_fwnode(dev); - struct iio_channel *chans; int i, mapind, nummaps = 0; int ret; @@ -307,7 +298,8 @@ static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) return ERR_PTR(-ENODEV); /* NULL terminated array to save passing size */ - chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); + struct iio_channel *chans __free(kfree) = + kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); if (!chans) return ERR_PTR(-ENOMEM); @@ -317,12 +309,11 @@ static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev) if (ret) goto error_free_chans; } - return chans; + return_ptr(chans); error_free_chans: for (i = 0; i < mapind; i++) iio_device_put(chans[i].indio_dev); - kfree(chans); return ERR_PTR(ret); } @@ -330,28 +321,28 @@ static struct iio_channel *iio_channel_get_sys(const char *name, const char *channel_name) { struct iio_map_internal *c_i = NULL, *c = NULL; - struct iio_channel *channel; int err; if (!(name || channel_name)) return ERR_PTR(-ENODEV); /* first find matching entry the channel map */ - mutex_lock(&iio_map_list_lock); - list_for_each_entry(c_i, &iio_map_list, l) { - if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) || - (channel_name && - strcmp(channel_name, c_i->map->consumer_channel) != 0)) - continue; - c = c_i; - iio_device_get(c->indio_dev); - break; + scoped_guard(mutex, &iio_map_list_lock) { + list_for_each_entry(c_i, &iio_map_list, l) { + if ((name && strcmp(name, c_i->map->consumer_dev_name) != 0) || + (channel_name && + strcmp(channel_name, c_i->map->consumer_channel) != 0)) + continue; + c = c_i; + iio_device_get(c->indio_dev); + break; + } } - mutex_unlock(&iio_map_list_lock); if (!c) return ERR_PTR(-ENODEV); - channel = kzalloc(sizeof(*channel), GFP_KERNEL); + struct iio_channel *channel __free(kfree) = + kzalloc(sizeof(*channel), GFP_KERNEL); if (!channel) { err = -ENOMEM; goto error_no_mem; @@ -366,14 +357,12 @@ static struct iio_channel *iio_channel_get_sys(const char *name, if (!channel->channel) { err = -EINVAL; - goto error_no_chan; + goto error_no_mem; } } - return channel; + return_ptr(channel); -error_no_chan: - kfree(channel); error_no_mem: iio_device_put(c->indio_dev); return ERR_PTR(err); @@ -450,8 +439,8 @@ EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name); struct iio_channel *iio_channel_get_all(struct device *dev) { const char *name; - struct iio_channel *chans; struct iio_map_internal *c = NULL; + struct iio_channel *fw_chans; int nummaps = 0; int mapind = 0; int i, ret; @@ -459,17 +448,17 @@ struct iio_channel *iio_channel_get_all(struct device *dev) if (!dev) return ERR_PTR(-EINVAL); - chans = fwnode_iio_channel_get_all(dev); + fw_chans = fwnode_iio_channel_get_all(dev); /* * We only want to carry on if the error is -ENODEV. Anything else * should be reported up the stack. */ - if (!IS_ERR(chans) || PTR_ERR(chans) != -ENODEV) - return chans; + if (!IS_ERR(fw_chans) || PTR_ERR(fw_chans) != -ENODEV) + return fw_chans; name = dev_name(dev); - mutex_lock(&iio_map_list_lock); + guard(mutex)(&iio_map_list_lock); /* first count the matching maps */ list_for_each_entry(c, &iio_map_list, l) if (name && strcmp(name, c->map->consumer_dev_name) != 0) @@ -477,17 +466,14 @@ struct iio_channel *iio_channel_get_all(struct device *dev) else nummaps++; - if (nummaps == 0) { - ret = -ENODEV; - goto error_ret; - } + if (nummaps == 0) + return ERR_PTR(-ENODEV); /* NULL terminated array to save passing size */ - chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); - if (!chans) { - ret = -ENOMEM; - goto error_ret; - } + struct iio_channel *chans __free(kfree) = + kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL); + if (!chans) + return ERR_PTR(-ENOMEM); /* for each map fill in the chans element */ list_for_each_entry(c, &iio_map_list, l) { @@ -509,17 +495,12 @@ struct iio_channel *iio_channel_get_all(struct device *dev) ret = -ENODEV; goto error_free_chans; } - mutex_unlock(&iio_map_list_lock); - return chans; + return_ptr(chans); error_free_chans: for (i = 0; i < nummaps; i++) iio_device_put(chans[i].indio_dev); - kfree(chans); -error_ret: - mutex_unlock(&iio_map_list_lock); - return ERR_PTR(ret); } EXPORT_SYMBOL_GPL(iio_channel_get_all); @@ -590,38 +571,24 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2, int iio_read_channel_raw(struct iio_channel *chan, int *val) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); - int ret; - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } - - ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; - return ret; + return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); } EXPORT_SYMBOL_GPL(iio_read_channel_raw); int iio_read_channel_average_raw(struct iio_channel *chan, int *val) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); - int ret; - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } - - ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; - return ret; + return iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); } EXPORT_SYMBOL_GPL(iio_read_channel_average_raw); @@ -708,20 +675,13 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, int *processed, unsigned int scale) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); - int ret; - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } - - ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed, - scale); -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; - return ret; + return iio_convert_raw_to_processed_unlocked(chan, raw, processed, + scale); } EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); @@ -729,19 +689,12 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, enum iio_chan_info_enum attribute) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); - int ret; - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } - - ret = iio_channel_read(chan, val, val2, attribute); -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; - return ret; + return iio_channel_read(chan, val, val2, attribute); } EXPORT_SYMBOL_GPL(iio_read_channel_attribute); @@ -757,30 +710,26 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; 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; + return ret; *val *= scale; + + return ret; } 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, - scale); - } + return ret; -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); - - return ret; + return iio_convert_raw_to_processed_unlocked(chan, *val, val, + scale); + } } EXPORT_SYMBOL_GPL(iio_read_channel_processed_scale); @@ -813,19 +762,12 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan, enum iio_chan_info_enum attribute) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); - int ret; - - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } - ret = iio_channel_read_avail(chan, vals, type, length, attribute); -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; - return ret; + return iio_channel_read_avail(chan, vals, type, length, attribute); } EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute); @@ -892,20 +834,13 @@ static int iio_channel_read_max(struct iio_channel *chan, int iio_read_max_channel_raw(struct iio_channel *chan, int *val) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); - int ret; int type; - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } - - ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; - return ret; + return iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } EXPORT_SYMBOL_GPL(iio_read_max_channel_raw); @@ -955,40 +890,27 @@ static int iio_channel_read_min(struct iio_channel *chan, int iio_read_min_channel_raw(struct iio_channel *chan, int *val) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); - int ret; int type; - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } - - ret = iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; - return ret; + return iio_channel_read_min(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); } EXPORT_SYMBOL_GPL(iio_read_min_channel_raw); int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); - int ret = 0; - /* Need to verify underlying driver has not gone away */ - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; *type = chan->channel->type; -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); - return ret; + return 0; } EXPORT_SYMBOL_GPL(iio_get_channel_type); @@ -1003,19 +925,12 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, enum iio_chan_info_enum attribute) { struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); - int ret; - mutex_lock(&iio_dev_opaque->info_exist_lock); - if (!chan->indio_dev->info) { - ret = -ENODEV; - goto err_unlock; - } + guard(mutex)(&iio_dev_opaque->info_exist_lock); + if (!chan->indio_dev->info) + return -ENODEV; - ret = iio_channel_write(chan, val, val2, attribute); -err_unlock: - mutex_unlock(&iio_dev_opaque->info_exist_lock); - - return ret; + return iio_channel_write(chan, val, val2, attribute); } EXPORT_SYMBOL_GPL(iio_write_channel_attribute); diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index fd5a9879a5..9a587d4031 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -73,6 +73,18 @@ config APDS9300 To compile this driver as a module, choose M here: the module will be called apds9300. +config APDS9306 + tristate "Avago APDS9306 Ambient Light Sensor" + depends on I2C + select REGMAP_I2C + select IIO_GTS_HELPER + help + If you say Y or M here, you get support for Avago APDS9306 + Ambient Light Sensor. + + If built as a dynamically linked module, it will be called + apds9306. + config APDS9960 tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor" select REGMAP_I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index 2e5fdb33e0..a30f906e91 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_ADUX1020) += adux1020.o obj-$(CONFIG_AL3010) += al3010.o obj-$(CONFIG_AL3320A) += al3320a.o obj-$(CONFIG_APDS9300) += apds9300.o +obj-$(CONFIG_APDS9306) += apds9306.o obj-$(CONFIG_APDS9960) += apds9960.o obj-$(CONFIG_AS73211) += as73211.o obj-$(CONFIG_BH1750) += bh1750.o diff --git a/drivers/iio/light/apds9306.c b/drivers/iio/light/apds9306.c new file mode 100644 index 0000000000..66a063ea3d --- /dev/null +++ b/drivers/iio/light/apds9306.c @@ -0,0 +1,1361 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * APDS-9306/APDS-9306-065 Ambient Light Sensor + * I2C Address: 0x52 + * Datasheet: https://docs.broadcom.com/doc/AV02-4755EN + * + * Copyright (C) 2024 Subhajit Ghosh <subhajit.ghosh@tweaklogic.com> + */ + +#include <linux/bits.h> +#include <linux/cleanup.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/minmax.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> +#include <linux/types.h> +#include <linux/units.h> + +#include <linux/iio/iio.h> +#include <linux/iio/iio-gts-helper.h> +#include <linux/iio/events.h> +#include <linux/iio/sysfs.h> + +#include <asm/unaligned.h> + +#define APDS9306_MAIN_CTRL_REG 0x00 +#define APDS9306_ALS_MEAS_RATE_REG 0x04 +#define APDS9306_ALS_GAIN_REG 0x05 +#define APDS9306_PART_ID_REG 0x06 +#define APDS9306_MAIN_STATUS_REG 0x07 +#define APDS9306_CLEAR_DATA_0_REG 0x0A +#define APDS9306_CLEAR_DATA_1_REG 0x0B +#define APDS9306_CLEAR_DATA_2_REG 0x0C +#define APDS9306_ALS_DATA_0_REG 0x0D +#define APDS9306_ALS_DATA_1_REG 0x0E +#define APDS9306_ALS_DATA_2_REG 0x0F +#define APDS9306_INT_CFG_REG 0x19 +#define APDS9306_INT_PERSISTENCE_REG 0x1A +#define APDS9306_ALS_THRES_UP_0_REG 0x21 +#define APDS9306_ALS_THRES_UP_1_REG 0x22 +#define APDS9306_ALS_THRES_UP_2_REG 0x23 +#define APDS9306_ALS_THRES_LOW_0_REG 0x24 +#define APDS9306_ALS_THRES_LOW_1_REG 0x25 +#define APDS9306_ALS_THRES_LOW_2_REG 0x26 +#define APDS9306_ALS_THRES_VAR_REG 0x27 + +#define APDS9306_ALS_INT_STAT_MASK BIT(4) +#define APDS9306_ALS_DATA_STAT_MASK BIT(3) + +#define APDS9306_ALS_THRES_VAL_MAX (BIT(20) - 1) +#define APDS9306_ALS_THRES_VAR_NUM_VALS 8 +#define APDS9306_ALS_PERSIST_NUM_VALS 16 +#define APDS9306_ALS_READ_DATA_DELAY_US (20 * USEC_PER_MSEC) +#define APDS9306_NUM_REPEAT_RATES 7 +#define APDS9306_INT_SRC_CLEAR 0 +#define APDS9306_INT_SRC_ALS 1 +#define APDS9306_SAMP_FREQ_10HZ 0 + +/** + * struct part_id_gts_multiplier - Part no. and corresponding gts multiplier + * + * GTS (Gain Time Scale) are helper functions for Light sensors which along + * with hardware gains also has gains associated with Integration times. + * + * There are two variants of the device with slightly different characteristics, + * they have same ADC count for different Lux levels as mentioned in the + * datasheet. This multiplier array is used to store the derived Lux per count + * value for the two variants to be used by the GTS helper functions. + * + * @part_id: Part ID of the device + * @max_scale_int: Multiplier for iio_init_iio_gts() + * @max_scale_nano: Multiplier for iio_init_iio_gts() + */ +struct part_id_gts_multiplier { + int part_id; + int max_scale_int; + int max_scale_nano; +}; + +/* + * As per the datasheet, at HW Gain = 3x, Integration time 100mS (32x), + * typical 2000 ADC counts are observed for 49.8 uW per sq cm (340.134 lux) + * for apds9306 and 43 uW per sq cm (293.69 lux) for apds9306-065. + * Assuming lux per count is linear across all integration time ranges. + * + * Lux = (raw + offset) * scale; offset can be any value by userspace. + * HG = Hardware Gain; ITG = Gain by changing integration time. + * Scale table by IIO GTS Helpers = (1 / HG) * (1 / ITG) * Multiplier. + * + * The Lux values provided in the datasheet are at ITG=32x and HG=3x, + * at typical 2000 count for both variants of the device. + * + * Lux per ADC count at 3x and 32x for apds9306 = 340.134 / 2000 + * Lux per ADC count at 3x and 32x for apds9306-065 = 293.69 / 2000 + * + * The Multiplier for the scale table provided to userspace: + * IIO GTS scale Multiplier for apds9306 = (340.134 / 2000) * 32 * 3 = 16.326432 + * and for apds9306-065 = (293.69 / 2000) * 32 * 3 = 14.09712 + */ +static const struct part_id_gts_multiplier apds9306_gts_mul[] = { + { + .part_id = 0xB1, + .max_scale_int = 16, + .max_scale_nano = 3264320, + }, { + .part_id = 0xB3, + .max_scale_int = 14, + .max_scale_nano = 9712000, + }, +}; + +static const int apds9306_repeat_rate_freq[APDS9306_NUM_REPEAT_RATES][2] = { + { 40, 0 }, + { 20, 0 }, + { 10, 0 }, + { 5, 0 }, + { 2, 0 }, + { 1, 0 }, + { 0, 500000 }, +}; + +static const int apds9306_repeat_rate_period[APDS9306_NUM_REPEAT_RATES] = { + 25000, 50000, 100000, 200000, 500000, 1000000, 2000000, +}; + +/** + * struct apds9306_regfields - apds9306 regmap fields definitions + * + * @sw_reset: Software reset regfield + * @en: Enable regfield + * @intg_time: Resolution regfield + * @repeat_rate: Measurement Rate regfield + * @gain: Hardware gain regfield + * @int_src: Interrupt channel regfield + * @int_thresh_var_en: Interrupt variance threshold regfield + * @int_en: Interrupt enable regfield + * @int_persist_val: Interrupt persistence regfield + * @int_thresh_var_val: Interrupt threshold variance value regfield + */ +struct apds9306_regfields { + struct regmap_field *sw_reset; + struct regmap_field *en; + struct regmap_field *intg_time; + struct regmap_field *repeat_rate; + struct regmap_field *gain; + struct regmap_field *int_src; + struct regmap_field *int_thresh_var_en; + struct regmap_field *int_en; + struct regmap_field *int_persist_val; + struct regmap_field *int_thresh_var_val; +}; + +/** + * struct apds9306_data - apds9306 private data and registers definitions + * + * @dev: Pointer to the device structure + * @gts: IIO Gain Time Scale structure + * @mutex: Lock for protecting adc reads, device settings changes where + * some calculations are required before or after setting or + * getting the raw settings values from regmap writes or reads + * respectively. + * @regmap: Regmap structure pointer + * @rf: Regmap register fields structure + * @nlux_per_count: Nano lux per ADC count for a particular model + * @read_data_available: Flag set by IRQ handler for ADC data available + */ +struct apds9306_data { + struct device *dev; + struct iio_gts gts; + + struct mutex mutex; + + struct regmap *regmap; + struct apds9306_regfields rf; + + int nlux_per_count; + int read_data_available; +}; + +/* + * Available scales with gain 1x - 18x, timings 3.125, 25, 50, 100, 200, 400 ms + * Time impacts to gain: 1x, 8x, 16x, 32x, 64x, 128x + */ +#define APDS9306_GSEL_1X 0x00 +#define APDS9306_GSEL_3X 0x01 +#define APDS9306_GSEL_6X 0x02 +#define APDS9306_GSEL_9X 0x03 +#define APDS9306_GSEL_18X 0x04 + +static const struct iio_gain_sel_pair apds9306_gains[] = { + GAIN_SCALE_GAIN(1, APDS9306_GSEL_1X), + GAIN_SCALE_GAIN(3, APDS9306_GSEL_3X), + GAIN_SCALE_GAIN(6, APDS9306_GSEL_6X), + GAIN_SCALE_GAIN(9, APDS9306_GSEL_9X), + GAIN_SCALE_GAIN(18, APDS9306_GSEL_18X), +}; + +#define APDS9306_MEAS_MODE_400MS 0x00 +#define APDS9306_MEAS_MODE_200MS 0x01 +#define APDS9306_MEAS_MODE_100MS 0x02 +#define APDS9306_MEAS_MODE_50MS 0x03 +#define APDS9306_MEAS_MODE_25MS 0x04 +#define APDS9306_MEAS_MODE_3125US 0x05 + +static const struct iio_itime_sel_mul apds9306_itimes[] = { + GAIN_SCALE_ITIME_US(400000, APDS9306_MEAS_MODE_400MS, BIT(7)), + GAIN_SCALE_ITIME_US(200000, APDS9306_MEAS_MODE_200MS, BIT(6)), + GAIN_SCALE_ITIME_US(100000, APDS9306_MEAS_MODE_100MS, BIT(5)), + GAIN_SCALE_ITIME_US(50000, APDS9306_MEAS_MODE_50MS, BIT(4)), + GAIN_SCALE_ITIME_US(25000, APDS9306_MEAS_MODE_25MS, BIT(3)), + GAIN_SCALE_ITIME_US(3125, APDS9306_MEAS_MODE_3125US, BIT(0)), +}; + +static const struct iio_event_spec apds9306_event_spec[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_shared_by_all = BIT(IIO_EV_INFO_PERIOD), + .mask_separate = BIT(IIO_EV_INFO_ENABLE), + }, { + .type = IIO_EV_TYPE_THRESH_ADAPTIVE, + .mask_shared_by_all = BIT(IIO_EV_INFO_VALUE) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec apds9306_channels_with_events[] = { + { + .type = IIO_LIGHT, + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), + .event_spec = apds9306_event_spec, + .num_event_specs = ARRAY_SIZE(apds9306_event_spec), + }, { + .type = IIO_INTENSITY, + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .channel2 = IIO_MOD_LIGHT_CLEAR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .modified = 1, + .event_spec = apds9306_event_spec, + .num_event_specs = ARRAY_SIZE(apds9306_event_spec), + }, +}; + +static const struct iio_chan_spec apds9306_channels_without_events[] = { + { + .type = IIO_LIGHT, + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE), + .info_mask_separate_available = BIT(IIO_CHAN_INFO_SCALE), + }, { + .type = IIO_INTENSITY, + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_SAMP_FREQ), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .modified = 1, + .channel2 = IIO_MOD_LIGHT_CLEAR, + }, +}; + +/* INT_PERSISTENCE available */ +static IIO_CONST_ATTR(thresh_either_period_available, "[0 1 15]"); + +/* ALS_THRESH_VAR available */ +static IIO_CONST_ATTR(thresh_adaptive_either_values_available, "[0 1 7]"); + +static struct attribute *apds9306_event_attributes[] = { + &iio_const_attr_thresh_either_period_available.dev_attr.attr, + &iio_const_attr_thresh_adaptive_either_values_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group apds9306_event_attr_group = { + .attrs = apds9306_event_attributes, +}; + +static const struct regmap_range apds9306_readable_ranges[] = { + regmap_reg_range(APDS9306_MAIN_CTRL_REG, APDS9306_ALS_THRES_VAR_REG) +}; + +static const struct regmap_range apds9306_writable_ranges[] = { + regmap_reg_range(APDS9306_MAIN_CTRL_REG, APDS9306_ALS_GAIN_REG), + regmap_reg_range(APDS9306_INT_CFG_REG, APDS9306_ALS_THRES_VAR_REG) +}; + +static const struct regmap_range apds9306_volatile_ranges[] = { + regmap_reg_range(APDS9306_MAIN_STATUS_REG, APDS9306_MAIN_STATUS_REG), + regmap_reg_range(APDS9306_CLEAR_DATA_0_REG, APDS9306_ALS_DATA_2_REG) +}; + +static const struct regmap_range apds9306_precious_ranges[] = { + regmap_reg_range(APDS9306_MAIN_STATUS_REG, APDS9306_MAIN_STATUS_REG) +}; + +static const struct regmap_access_table apds9306_readable_table = { + .yes_ranges = apds9306_readable_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9306_readable_ranges) +}; + +static const struct regmap_access_table apds9306_writable_table = { + .yes_ranges = apds9306_writable_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9306_writable_ranges) +}; + +static const struct regmap_access_table apds9306_volatile_table = { + .yes_ranges = apds9306_volatile_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9306_volatile_ranges) +}; + +static const struct regmap_access_table apds9306_precious_table = { + .yes_ranges = apds9306_precious_ranges, + .n_yes_ranges = ARRAY_SIZE(apds9306_precious_ranges) +}; + +static const struct regmap_config apds9306_regmap = { + .name = "apds9306_regmap", + .reg_bits = 8, + .val_bits = 8, + .rd_table = &apds9306_readable_table, + .wr_table = &apds9306_writable_table, + .volatile_table = &apds9306_volatile_table, + .precious_table = &apds9306_precious_table, + .max_register = APDS9306_ALS_THRES_VAR_REG, + .cache_type = REGCACHE_RBTREE, +}; + +static const struct reg_field apds9306_rf_sw_reset = + REG_FIELD(APDS9306_MAIN_CTRL_REG, 4, 4); + +static const struct reg_field apds9306_rf_en = + REG_FIELD(APDS9306_MAIN_CTRL_REG, 1, 1); + +static const struct reg_field apds9306_rf_intg_time = + REG_FIELD(APDS9306_ALS_MEAS_RATE_REG, 4, 6); + +static const struct reg_field apds9306_rf_repeat_rate = + REG_FIELD(APDS9306_ALS_MEAS_RATE_REG, 0, 2); + +static const struct reg_field apds9306_rf_gain = + REG_FIELD(APDS9306_ALS_GAIN_REG, 0, 2); + +static const struct reg_field apds9306_rf_int_src = + REG_FIELD(APDS9306_INT_CFG_REG, 4, 5); + +static const struct reg_field apds9306_rf_int_thresh_var_en = + REG_FIELD(APDS9306_INT_CFG_REG, 3, 3); + +static const struct reg_field apds9306_rf_int_en = + REG_FIELD(APDS9306_INT_CFG_REG, 2, 2); + +static const struct reg_field apds9306_rf_int_persist_val = + REG_FIELD(APDS9306_INT_PERSISTENCE_REG, 4, 7); + +static const struct reg_field apds9306_rf_int_thresh_var_val = + REG_FIELD(APDS9306_ALS_THRES_VAR_REG, 0, 2); + +static int apds9306_regfield_init(struct apds9306_data *data) +{ + struct device *dev = data->dev; + struct regmap *regmap = data->regmap; + struct regmap_field *tmp; + struct apds9306_regfields *rf = &data->rf; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_sw_reset); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->sw_reset = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_en); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->en = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_intg_time); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->intg_time = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_repeat_rate); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->repeat_rate = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_gain); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->gain = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_src); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->int_src = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_thresh_var_en); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->int_thresh_var_en = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_en); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->int_en = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_persist_val); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->int_persist_val = tmp; + + tmp = devm_regmap_field_alloc(dev, regmap, apds9306_rf_int_thresh_var_val); + if (IS_ERR(tmp)) + return PTR_ERR(tmp); + rf->int_thresh_var_val = tmp; + + return 0; +} + +static int apds9306_power_state(struct apds9306_data *data, bool state) +{ + struct apds9306_regfields *rf = &data->rf; + int ret; + + /* Reset not included as it causes ugly I2C bus error */ + if (state) { + ret = regmap_field_write(rf->en, 1); + if (ret) + return ret; + /* 5ms wake up time */ + fsleep(5000); + return 0; + } + + return regmap_field_write(rf->en, 0); +} + +static int apds9306_read_data(struct apds9306_data *data, int *val, int reg) +{ + struct device *dev = data->dev; + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct apds9306_regfields *rf = &data->rf; + u64 ev_code; + int ret, delay, intg_time, intg_time_idx, repeat_rate_idx, int_src; + int status = 0; + u8 buff[3]; + + ret = pm_runtime_resume_and_get(data->dev); + if (ret) + return ret; + + ret = regmap_field_read(rf->intg_time, &intg_time_idx); + if (ret) + return ret; + + ret = regmap_field_read(rf->repeat_rate, &repeat_rate_idx); + if (ret) + return ret; + + ret = regmap_field_read(rf->int_src, &int_src); + if (ret) + return ret; + + intg_time = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx); + if (intg_time < 0) + return intg_time; + + /* Whichever is greater - integration time period or sampling period. */ + delay = max(intg_time, apds9306_repeat_rate_period[repeat_rate_idx]); + + /* + * Clear stale data flag that might have been set by the interrupt + * handler if it got data available flag set in the status reg. + */ + data->read_data_available = 0; + + /* + * If this function runs parallel with the interrupt handler, either + * this reads and clears the status registers or the interrupt handler + * does. The interrupt handler sets a flag for read data available + * in our private structure which we read here. + */ + ret = regmap_read_poll_timeout(data->regmap, APDS9306_MAIN_STATUS_REG, + status, data->read_data_available || + (status & (APDS9306_ALS_DATA_STAT_MASK | + APDS9306_ALS_INT_STAT_MASK)), + APDS9306_ALS_READ_DATA_DELAY_US, delay * 2); + if (ret) + return ret; + + /* If we reach here before the interrupt handler we push an event */ + if ((status & APDS9306_ALS_INT_STAT_MASK)) { + if (int_src == APDS9306_INT_SRC_ALS) + ev_code = IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER); + else + ev_code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, 0, + IIO_MOD_LIGHT_CLEAR, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER); + + iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev)); + } + + ret = regmap_bulk_read(data->regmap, reg, buff, sizeof(buff)); + if (ret) { + dev_err_ratelimited(dev, "read data failed\n"); + return ret; + } + + *val = get_unaligned_le24(&buff); + + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + + return 0; +} + +static int apds9306_intg_time_get(struct apds9306_data *data, int *val2) +{ + struct apds9306_regfields *rf = &data->rf; + int ret, intg_time_idx; + + ret = regmap_field_read(rf->intg_time, &intg_time_idx); + if (ret) + return ret; + + ret = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx); + if (ret < 0) + return ret; + + *val2 = ret; + + return 0; +} + +static int apds9306_intg_time_set(struct apds9306_data *data, int val2) +{ + struct device *dev = data->dev; + struct apds9306_regfields *rf = &data->rf; + int ret, intg_old, gain_old, gain_new, gain_new_closest, intg_time_idx; + int gain_idx; + bool ok; + + if (!iio_gts_valid_time(&data->gts, val2)) { + dev_err_ratelimited(dev, "Unsupported integration time %u\n", val2); + return -EINVAL; + } + + ret = regmap_field_read(rf->intg_time, &intg_time_idx); + if (ret) + return ret; + + ret = regmap_field_read(rf->gain, &gain_idx); + if (ret) + return ret; + + intg_old = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx); + if (intg_old < 0) + return intg_old; + + if (intg_old == val2) + return 0; + + gain_old = iio_gts_find_gain_by_sel(&data->gts, gain_idx); + if (gain_old < 0) + return gain_old; + + iio_gts_find_new_gain_by_old_gain_time(&data->gts, gain_old, intg_old, + val2, &gain_new); + + if (gain_new < 0) { + dev_err_ratelimited(dev, "Unsupported gain with time\n"); + return gain_new; + } + + gain_new_closest = iio_find_closest_gain_low(&data->gts, gain_new, &ok); + if (gain_new_closest < 0) { + gain_new_closest = iio_gts_get_min_gain(&data->gts); + if (gain_new_closest < 0) + return gain_new_closest; + } + if (!ok) + dev_dbg(dev, "Unable to find optimum gain, setting minimum"); + + ret = iio_gts_find_sel_by_int_time(&data->gts, val2); + if (ret < 0) + return ret; + + ret = regmap_field_write(rf->intg_time, ret); + if (ret) + return ret; + + ret = iio_gts_find_sel_by_gain(&data->gts, gain_new_closest); + if (ret < 0) + return ret; + + return regmap_field_write(rf->gain, ret); +} + +static int apds9306_sampling_freq_get(struct apds9306_data *data, int *val, + int *val2) +{ + struct apds9306_regfields *rf = &data->rf; + int ret, repeat_rate_idx; + + ret = regmap_field_read(rf->repeat_rate, &repeat_rate_idx); + if (ret) + return ret; + + if (repeat_rate_idx >= ARRAY_SIZE(apds9306_repeat_rate_freq)) + return -EINVAL; + + *val = apds9306_repeat_rate_freq[repeat_rate_idx][0]; + *val2 = apds9306_repeat_rate_freq[repeat_rate_idx][1]; + + return 0; +} + +static int apds9306_sampling_freq_set(struct apds9306_data *data, int val, + int val2) +{ + struct apds9306_regfields *rf = &data->rf; + int i; + + for (i = 0; i < ARRAY_SIZE(apds9306_repeat_rate_freq); i++) { + if (apds9306_repeat_rate_freq[i][0] == val && + apds9306_repeat_rate_freq[i][1] == val2) + return regmap_field_write(rf->repeat_rate, i); + } + + return -EINVAL; +} + +static int apds9306_scale_get(struct apds9306_data *data, int *val, int *val2) +{ + struct apds9306_regfields *rf = &data->rf; + int gain, intg, ret, intg_time_idx, gain_idx; + + ret = regmap_field_read(rf->gain, &gain_idx); + if (ret) + return ret; + + ret = regmap_field_read(rf->intg_time, &intg_time_idx); + if (ret) + return ret; + + gain = iio_gts_find_gain_by_sel(&data->gts, gain_idx); + if (gain < 0) + return gain; + + intg = iio_gts_find_int_time_by_sel(&data->gts, intg_time_idx); + if (intg < 0) + return intg; + + return iio_gts_get_scale(&data->gts, gain, intg, val, val2); +} + +static int apds9306_scale_set(struct apds9306_data *data, int val, int val2) +{ + struct apds9306_regfields *rf = &data->rf; + int i, ret, time_sel, gain_sel, intg_time_idx; + + ret = regmap_field_read(rf->intg_time, &intg_time_idx); + if (ret) + return ret; + + ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, + intg_time_idx, val, val2, &gain_sel); + if (ret) { + for (i = 0; i < data->gts.num_itime; i++) { + time_sel = data->gts.itime_table[i].sel; + + if (time_sel == intg_time_idx) + continue; + + ret = iio_gts_find_gain_sel_for_scale_using_time(&data->gts, + time_sel, val, val2, &gain_sel); + if (!ret) + break; + } + if (ret) + return -EINVAL; + + ret = regmap_field_write(rf->intg_time, time_sel); + if (ret) + return ret; + } + + return regmap_field_write(rf->gain, gain_sel); +} + +static int apds9306_event_period_get(struct apds9306_data *data, int *val) +{ + struct apds9306_regfields *rf = &data->rf; + int period, ret; + + ret = regmap_field_read(rf->int_persist_val, &period); + if (ret) + return ret; + + if (!in_range(period, 0, APDS9306_ALS_PERSIST_NUM_VALS)) + return -EINVAL; + + *val = period; + + return ret; +} + +static int apds9306_event_period_set(struct apds9306_data *data, int val) +{ + struct apds9306_regfields *rf = &data->rf; + + if (!in_range(val, 0, APDS9306_ALS_PERSIST_NUM_VALS)) + return -EINVAL; + + return regmap_field_write(rf->int_persist_val, val); +} + +static int apds9306_event_thresh_get(struct apds9306_data *data, int dir, + int *val) +{ + int var, ret; + u8 buff[3]; + + if (dir == IIO_EV_DIR_RISING) + var = APDS9306_ALS_THRES_UP_0_REG; + else if (dir == IIO_EV_DIR_FALLING) + var = APDS9306_ALS_THRES_LOW_0_REG; + else + return -EINVAL; + + ret = regmap_bulk_read(data->regmap, var, buff, sizeof(buff)); + if (ret) + return ret; + + *val = get_unaligned_le24(&buff); + + return 0; +} + +static int apds9306_event_thresh_set(struct apds9306_data *data, int dir, + int val) +{ + int var; + u8 buff[3]; + + if (dir == IIO_EV_DIR_RISING) + var = APDS9306_ALS_THRES_UP_0_REG; + else if (dir == IIO_EV_DIR_FALLING) + var = APDS9306_ALS_THRES_LOW_0_REG; + else + return -EINVAL; + + if (!in_range(val, 0, APDS9306_ALS_THRES_VAL_MAX)) + return -EINVAL; + + put_unaligned_le24(val, buff); + + return regmap_bulk_write(data->regmap, var, buff, sizeof(buff)); +} + +static int apds9306_event_thresh_adaptive_get(struct apds9306_data *data, int *val) +{ + struct apds9306_regfields *rf = &data->rf; + int thr_adpt, ret; + + ret = regmap_field_read(rf->int_thresh_var_val, &thr_adpt); + if (ret) + return ret; + + if (!in_range(thr_adpt, 0, APDS9306_ALS_THRES_VAR_NUM_VALS)) + return -EINVAL; + + *val = thr_adpt; + + return ret; +} + +static int apds9306_event_thresh_adaptive_set(struct apds9306_data *data, int val) +{ + struct apds9306_regfields *rf = &data->rf; + + if (!in_range(val, 0, APDS9306_ALS_THRES_VAR_NUM_VALS)) + return -EINVAL; + + return regmap_field_write(rf->int_thresh_var_val, val); +} + +static int apds9306_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct apds9306_data *data = iio_priv(indio_dev); + int ret, reg; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->channel2 == IIO_MOD_LIGHT_CLEAR) + reg = APDS9306_CLEAR_DATA_0_REG; + else + reg = APDS9306_ALS_DATA_0_REG; + /* + * Changing device parameters during adc operation, resets + * the ADC which has to avoided. + */ + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + ret = apds9306_read_data(data, val, reg); + iio_device_release_direct_mode(indio_dev); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_CHAN_INFO_INT_TIME: + ret = apds9306_intg_time_get(data, val2); + if (ret) + return ret; + *val = 0; + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = apds9306_sampling_freq_get(data, val, val2); + if (ret) + return ret; + + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + ret = apds9306_scale_get(data, val, val2); + if (ret) + return ret; + + return IIO_VAL_INT_PLUS_NANO; + default: + return -EINVAL; + } +}; + +static int apds9306_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + struct apds9306_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + return iio_gts_avail_times(&data->gts, vals, type, length); + case IIO_CHAN_INFO_SCALE: + return iio_gts_all_avail_scales(&data->gts, vals, type, length); + case IIO_CHAN_INFO_SAMP_FREQ: + *length = ARRAY_SIZE(apds9306_repeat_rate_freq) * 2; + *vals = (const int *)apds9306_repeat_rate_freq; + *type = IIO_VAL_INT_PLUS_MICRO; + + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int apds9306_write_raw_get_fmt(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + return IIO_VAL_INT_PLUS_NANO; + case IIO_CHAN_INFO_INT_TIME: + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int apds9306_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int val, + int val2, long mask) +{ + struct apds9306_data *data = iio_priv(indio_dev); + + guard(mutex)(&data->mutex); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val) + return -EINVAL; + return apds9306_intg_time_set(data, val2); + case IIO_CHAN_INFO_SCALE: + return apds9306_scale_set(data, val, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return apds9306_sampling_freq_set(data, val, val2); + default: + return -EINVAL; + } +} + +static irqreturn_t apds9306_irq_handler(int irq, void *priv) +{ + struct iio_dev *indio_dev = priv; + struct apds9306_data *data = iio_priv(indio_dev); + struct apds9306_regfields *rf = &data->rf; + u64 ev_code; + int ret, status, int_src; + + /* + * The interrupt line is released and the interrupt flag is + * cleared as a result of reading the status register. All the + * status flags are cleared as a result of this read. + */ + ret = regmap_read(data->regmap, APDS9306_MAIN_STATUS_REG, &status); + if (ret < 0) { + dev_err_ratelimited(data->dev, "status reg read failed\n"); + return IRQ_HANDLED; + } + + ret = regmap_field_read(rf->int_src, &int_src); + if (ret) + return ret; + + if ((status & APDS9306_ALS_INT_STAT_MASK)) { + if (int_src == APDS9306_INT_SRC_ALS) + ev_code = IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER); + else + ev_code = IIO_MOD_EVENT_CODE(IIO_INTENSITY, 0, + IIO_MOD_LIGHT_CLEAR, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER); + + iio_push_event(indio_dev, ev_code, iio_get_time_ns(indio_dev)); + } + + /* + * If a one-shot read through sysfs is underway at the same time + * as this interrupt handler is executing and a read data available + * flag was set, this flag is set to inform read_poll_timeout() + * to exit. + */ + if ((status & APDS9306_ALS_DATA_STAT_MASK)) + data->read_data_available = 1; + + return IRQ_HANDLED; +} + +static int apds9306_read_event(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 apds9306_data *data = iio_priv(indio_dev); + int ret; + + switch (type) { + case IIO_EV_TYPE_THRESH: + if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD) + ret = apds9306_event_period_get(data, val); + else + ret = apds9306_event_thresh_get(data, dir, val); + if (ret) + return ret; + + return IIO_VAL_INT; + case IIO_EV_TYPE_THRESH_ADAPTIVE: + ret = apds9306_event_thresh_adaptive_get(data, val); + if (ret) + return ret; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int apds9306_write_event(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 apds9306_data *data = iio_priv(indio_dev); + + switch (type) { + case IIO_EV_TYPE_THRESH: + if (dir == IIO_EV_DIR_EITHER && info == IIO_EV_INFO_PERIOD) + return apds9306_event_period_set(data, val); + + return apds9306_event_thresh_set(data, dir, val); + case IIO_EV_TYPE_THRESH_ADAPTIVE: + return apds9306_event_thresh_adaptive_set(data, val); + default: + return -EINVAL; + } +} + +static int apds9306_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 apds9306_data *data = iio_priv(indio_dev); + struct apds9306_regfields *rf = &data->rf; + int int_en, int_src, ret; + + switch (type) { + case IIO_EV_TYPE_THRESH: { + guard(mutex)(&data->mutex); + + ret = regmap_field_read(rf->int_src, &int_src); + if (ret) + return ret; + + ret = regmap_field_read(rf->int_en, &int_en); + if (ret) + return ret; + + if (chan->type == IIO_LIGHT) + return int_en && (int_src == APDS9306_INT_SRC_ALS); + + if (chan->type == IIO_INTENSITY) + return int_en && (int_src == APDS9306_INT_SRC_CLEAR); + + return -EINVAL; + } + case IIO_EV_TYPE_THRESH_ADAPTIVE: + ret = regmap_field_read(rf->int_thresh_var_en, &int_en); + if (ret) + return ret; + + return int_en; + default: + return -EINVAL; + } +} + +static int apds9306_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 apds9306_data *data = iio_priv(indio_dev); + struct apds9306_regfields *rf = &data->rf; + int ret, enabled; + + switch (type) { + case IIO_EV_TYPE_THRESH: { + guard(mutex)(&data->mutex); + + ret = regmap_field_read(rf->int_en, &enabled); + if (ret) + return ret; + + /* + * If interrupt is enabled, the channel is set before enabling + * the interrupt. In case of disable, no need to switch + * channels. In case of different channel is selected while + * interrupt in on, just change the channel. + */ + if (state) { + if (chan->type == IIO_LIGHT) + ret = regmap_field_write(rf->int_src, 1); + else if (chan->type == IIO_INTENSITY) + ret = regmap_field_write(rf->int_src, 0); + else + return -EINVAL; + + if (ret) + return ret; + + if (enabled) + return 0; + + ret = regmap_field_write(rf->int_en, 1); + if (ret) + return ret; + + return pm_runtime_resume_and_get(data->dev); + } else { + if (!enabled) + return 0; + + ret = regmap_field_write(rf->int_en, 0); + if (ret) + return ret; + + pm_runtime_mark_last_busy(data->dev); + pm_runtime_put_autosuspend(data->dev); + + return 0; + } + } + case IIO_EV_TYPE_THRESH_ADAPTIVE: + if (state) + return regmap_field_write(rf->int_thresh_var_en, 1); + else + return regmap_field_write(rf->int_thresh_var_en, 0); + default: + return -EINVAL; + } +} + +static const struct iio_info apds9306_info_no_events = { + .read_avail = apds9306_read_avail, + .read_raw = apds9306_read_raw, + .write_raw = apds9306_write_raw, + .write_raw_get_fmt = apds9306_write_raw_get_fmt, +}; + +static const struct iio_info apds9306_info = { + .read_avail = apds9306_read_avail, + .read_raw = apds9306_read_raw, + .write_raw = apds9306_write_raw, + .write_raw_get_fmt = apds9306_write_raw_get_fmt, + .read_event_value = apds9306_read_event, + .write_event_value = apds9306_write_event, + .read_event_config = apds9306_read_event_config, + .write_event_config = apds9306_write_event_config, + .event_attrs = &apds9306_event_attr_group, +}; + +static int apds9306_init_iio_gts(struct apds9306_data *data) +{ + int i, ret, part_id; + + ret = regmap_read(data->regmap, APDS9306_PART_ID_REG, &part_id); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(apds9306_gts_mul); i++) + if (part_id == apds9306_gts_mul[i].part_id) + break; + + if (i == ARRAY_SIZE(apds9306_gts_mul)) + return -ENOENT; + + return devm_iio_init_iio_gts(data->dev, + apds9306_gts_mul[i].max_scale_int, + apds9306_gts_mul[i].max_scale_nano, + apds9306_gains, ARRAY_SIZE(apds9306_gains), + apds9306_itimes, ARRAY_SIZE(apds9306_itimes), + &data->gts); +} + +static void apds9306_powerdown(void *ptr) +{ + struct apds9306_data *data = (struct apds9306_data *)ptr; + struct apds9306_regfields *rf = &data->rf; + int ret; + + ret = regmap_field_write(rf->int_thresh_var_en, 0); + if (ret) + return; + + ret = regmap_field_write(rf->int_en, 0); + if (ret) + return; + + apds9306_power_state(data, false); +} + +static int apds9306_device_init(struct apds9306_data *data) +{ + struct apds9306_regfields *rf = &data->rf; + int ret; + + ret = apds9306_init_iio_gts(data); + if (ret) + return ret; + + ret = regmap_field_write(rf->intg_time, APDS9306_MEAS_MODE_100MS); + if (ret) + return ret; + + ret = regmap_field_write(rf->repeat_rate, APDS9306_SAMP_FREQ_10HZ); + if (ret) + return ret; + + ret = regmap_field_write(rf->gain, APDS9306_GSEL_3X); + if (ret) + return ret; + + ret = regmap_field_write(rf->int_src, APDS9306_INT_SRC_ALS); + if (ret) + return ret; + + ret = regmap_field_write(rf->int_en, 0); + if (ret) + return ret; + + return regmap_field_write(rf->int_thresh_var_en, 0); +} + +static int apds9306_pm_init(struct apds9306_data *data) +{ + struct device *dev = data->dev; + int ret; + + ret = apds9306_power_state(data, true); + if (ret) + return ret; + + ret = pm_runtime_set_active(dev); + if (ret) + return ret; + + ret = devm_pm_runtime_enable(dev); + if (ret) + return ret; + + pm_runtime_set_autosuspend_delay(dev, 5000); + pm_runtime_use_autosuspend(dev); + pm_runtime_get(dev); + + return 0; +} + +static int apds9306_probe(struct i2c_client *client) +{ + struct device *dev = &client->dev; + struct apds9306_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); + + mutex_init(&data->mutex); + + data->regmap = devm_regmap_init_i2c(client, &apds9306_regmap); + if (IS_ERR(data->regmap)) + return dev_err_probe(dev, PTR_ERR(data->regmap), + "regmap initialization failed\n"); + + data->dev = dev; + i2c_set_clientdata(client, indio_dev); + + ret = apds9306_regfield_init(data); + if (ret) + return dev_err_probe(dev, ret, "regfield initialization failed\n"); + + ret = devm_regulator_get_enable(dev, "vdd"); + if (ret) + return dev_err_probe(dev, ret, "Failed to enable regulator\n"); + + indio_dev->name = "apds9306"; + indio_dev->modes = INDIO_DIRECT_MODE; + if (client->irq) { + indio_dev->info = &apds9306_info; + indio_dev->channels = apds9306_channels_with_events; + indio_dev->num_channels = ARRAY_SIZE(apds9306_channels_with_events); + ret = devm_request_threaded_irq(dev, client->irq, NULL, + apds9306_irq_handler, IRQF_ONESHOT, + "apds9306_event", indio_dev); + if (ret) + return dev_err_probe(dev, ret, + "failed to assign interrupt.\n"); + } else { + indio_dev->info = &apds9306_info_no_events; + indio_dev->channels = apds9306_channels_without_events; + indio_dev->num_channels = + ARRAY_SIZE(apds9306_channels_without_events); + } + + ret = apds9306_pm_init(data); + if (ret) + return dev_err_probe(dev, ret, "failed pm init\n"); + + ret = apds9306_device_init(data); + if (ret) + return dev_err_probe(dev, ret, "failed to init device\n"); + + ret = devm_add_action_or_reset(dev, apds9306_powerdown, data); + if (ret) + return dev_err_probe(dev, ret, "failed to add action or reset\n"); + + ret = devm_iio_device_register(dev, indio_dev); + if (ret) + return dev_err_probe(dev, ret, "failed iio device registration\n"); + + pm_runtime_put_autosuspend(dev); + + return 0; +} + +static int apds9306_runtime_suspend(struct device *dev) +{ + struct apds9306_data *data = iio_priv(dev_get_drvdata(dev)); + + return apds9306_power_state(data, false); +} + +static int apds9306_runtime_resume(struct device *dev) +{ + struct apds9306_data *data = iio_priv(dev_get_drvdata(dev)); + + return apds9306_power_state(data, true); +} + +static DEFINE_RUNTIME_DEV_PM_OPS(apds9306_pm_ops, + apds9306_runtime_suspend, + apds9306_runtime_resume, + NULL); + +static const struct of_device_id apds9306_of_match[] = { + { .compatible = "avago,apds9306" }, + { } +}; +MODULE_DEVICE_TABLE(of, apds9306_of_match); + +static struct i2c_driver apds9306_driver = { + .driver = { + .name = "apds9306", + .pm = pm_ptr(&apds9306_pm_ops), + .of_match_table = apds9306_of_match, + }, + .probe = apds9306_probe, +}; +module_i2c_driver(apds9306_driver); + +MODULE_AUTHOR("Subhajit Ghosh <subhajit.ghosh@tweaklogic.com>"); +MODULE_DESCRIPTION("APDS9306 Ambient Light Sensor driver"); +MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_GTS_HELPER); diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c index 50f95c5d20..d4e17079b2 100644 --- a/drivers/iio/light/st_uvis25_core.c +++ b/drivers/iio/light/st_uvis25_core.c @@ -291,7 +291,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap) if (!iio_dev) return -ENOMEM; - dev_set_drvdata(dev, (void *)iio_dev); + dev_set_drvdata(dev, iio_dev); hw = iio_priv(iio_dev); hw->irq = irq; diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c index 7b71ad71d7..08d4714381 100644 --- a/drivers/iio/light/stk3310.c +++ b/drivers/iio/light/stk3310.c @@ -693,7 +693,6 @@ MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id); static const struct acpi_device_id stk3310_acpi_id[] = { {"STK3310", 0}, {"STK3311", 0}, - {"STK3335", 0}, {} }; diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c index 0199d055e0..221fa2c552 100644 --- a/drivers/iio/pressure/bmp280-core.c +++ b/drivers/iio/pressure/bmp280-core.c @@ -27,20 +27,20 @@ #include <linux/bitops.h> #include <linux/bitfield.h> -#include <linux/device.h> -#include <linux/module.h> -#include <linux/nvmem-provider.h> -#include <linux/regmap.h> +#include <linux/completion.h> #include <linux/delay.h> -#include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> +#include <linux/device.h> #include <linux/gpio/consumer.h> -#include <linux/regulator/consumer.h> #include <linux/interrupt.h> #include <linux/irq.h> /* For irq_get_irq_data() */ -#include <linux/completion.h> +#include <linux/module.h> +#include <linux/nvmem-provider.h> #include <linux/pm_runtime.h> #include <linux/random.h> +#include <linux/regmap.h> +#include <linux/regulator/consumer.h> + +#include <linux/iio/iio.h> #include <asm/unaligned.h> diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c index d0a516d56d..7d882e15e5 100644 --- a/drivers/iio/pressure/dps310.c +++ b/drivers/iio/pressure/dps310.c @@ -171,7 +171,7 @@ static int dps310_temp_workaround(struct dps310_data *data) int reg; rc = regmap_read(data->regmap, 0x32, ®); - if (rc) + if (rc < 0) return rc; /* @@ -256,24 +256,24 @@ static int dps310_startup(struct dps310_data *data) return dps310_temp_workaround(data); } -static int dps310_get_pres_precision(struct dps310_data *data) +static int dps310_get_pres_precision(struct dps310_data *data, int *val) { - int rc; - int val; + int reg_val, rc; - rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val); + rc = regmap_read(data->regmap, DPS310_PRS_CFG, ®_val); if (rc < 0) return rc; - return BIT(val & GENMASK(2, 0)); + *val = BIT(reg_val & GENMASK(2, 0)); + + return 0; } -static int dps310_get_temp_precision(struct dps310_data *data) +static int dps310_get_temp_precision(struct dps310_data *data, int *val) { - int rc; - int val; + int reg_val, rc; - rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val); + rc = regmap_read(data->regmap, DPS310_TMP_CFG, ®_val); if (rc < 0) return rc; @@ -281,7 +281,9 @@ static int dps310_get_temp_precision(struct dps310_data *data) * Scale factor is bottom 4 bits of the register, but 1111 is * reserved so just grab bottom three */ - return BIT(val & GENMASK(2, 0)); + *val = BIT(reg_val & GENMASK(2, 0)); + + return 0; } /* Called with lock held */ @@ -350,48 +352,56 @@ static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq) DPS310_TMP_RATE_BITS, val); } -static int dps310_get_pres_samp_freq(struct dps310_data *data) +static int dps310_get_pres_samp_freq(struct dps310_data *data, int *val) { - int rc; - int val; + int reg_val, rc; - rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val); + rc = regmap_read(data->regmap, DPS310_PRS_CFG, ®_val); if (rc < 0) return rc; - return BIT((val & DPS310_PRS_RATE_BITS) >> 4); + *val = BIT((reg_val & DPS310_PRS_RATE_BITS) >> 4); + + return 0; } -static int dps310_get_temp_samp_freq(struct dps310_data *data) +static int dps310_get_temp_samp_freq(struct dps310_data *data, int *val) { - int rc; - int val; + int reg_val, rc; - rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val); + rc = regmap_read(data->regmap, DPS310_TMP_CFG, ®_val); if (rc < 0) return rc; - return BIT((val & DPS310_TMP_RATE_BITS) >> 4); + *val = BIT((reg_val & DPS310_TMP_RATE_BITS) >> 4); + + return 0; } -static int dps310_get_pres_k(struct dps310_data *data) +static int dps310_get_pres_k(struct dps310_data *data, int *val) { - int rc = dps310_get_pres_precision(data); + int reg_val, rc; + rc = regmap_read(data->regmap, DPS310_PRS_CFG, ®_val); if (rc < 0) return rc; - return scale_factors[ilog2(rc)]; + *val = scale_factors[reg_val & GENMASK(2, 0)]; + + return 0; } -static int dps310_get_temp_k(struct dps310_data *data) +static int dps310_get_temp_k(struct dps310_data *data, int *val) { - int rc = dps310_get_temp_precision(data); + int reg_val, rc; + rc = regmap_read(data->regmap, DPS310_TMP_CFG, ®_val); if (rc < 0) return rc; - return scale_factors[ilog2(rc)]; + *val = scale_factors[reg_val & GENMASK(2, 0)]; + + return 0; } static int dps310_reset_wait(struct dps310_data *data) @@ -464,7 +474,10 @@ static int dps310_read_pres_raw(struct dps310_data *data) if (mutex_lock_interruptible(&data->lock)) return -EINTR; - rate = dps310_get_pres_samp_freq(data); + rc = dps310_get_pres_samp_freq(data, &rate); + if (rc) + goto done; + timeout = DPS310_POLL_TIMEOUT_US(rate); /* Poll for sensor readiness; base the timeout upon the sample rate. */ @@ -510,7 +523,10 @@ static int dps310_read_temp_raw(struct dps310_data *data) if (mutex_lock_interruptible(&data->lock)) return -EINTR; - rate = dps310_get_temp_samp_freq(data); + rc = dps310_get_temp_samp_freq(data, &rate); + if (rc) + goto done; + timeout = DPS310_POLL_TIMEOUT_US(rate); /* Poll for sensor readiness; base the timeout upon the sample rate. */ @@ -612,13 +628,13 @@ static int dps310_write_raw(struct iio_dev *iio, return rc; } -static int dps310_calculate_pressure(struct dps310_data *data) +static int dps310_calculate_pressure(struct dps310_data *data, int *val) { int i; int rc; int t_ready; - int kpi = dps310_get_pres_k(data); - int kti = dps310_get_temp_k(data); + int kpi; + int kti; s64 rem = 0ULL; s64 pressure = 0ULL; s64 p; @@ -629,11 +645,13 @@ static int dps310_calculate_pressure(struct dps310_data *data) s64 kp; s64 kt; - if (kpi < 0) - return kpi; + rc = dps310_get_pres_k(data, &kpi); + if (rc) + return rc; - if (kti < 0) - return kti; + rc = dps310_get_temp_k(data, &kti); + if (rc) + return rc; kp = (s64)kpi; kt = (s64)kti; @@ -687,7 +705,9 @@ static int dps310_calculate_pressure(struct dps310_data *data) if (pressure < 0LL) return -ERANGE; - return (int)min_t(s64, pressure, INT_MAX); + *val = (int)min_t(s64, pressure, INT_MAX); + + return 0; } static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, @@ -697,11 +717,10 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - rc = dps310_get_pres_samp_freq(data); - if (rc < 0) + rc = dps310_get_pres_samp_freq(data, val); + if (rc) return rc; - *val = rc; return IIO_VAL_INT; case IIO_CHAN_INFO_PROCESSED: @@ -709,20 +728,17 @@ static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2, if (rc) return rc; - rc = dps310_calculate_pressure(data); - if (rc < 0) + rc = dps310_calculate_pressure(data, val); + if (rc) return rc; - *val = rc; *val2 = 1000; /* Convert Pa to KPa per IIO ABI */ return IIO_VAL_FRACTIONAL; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - rc = dps310_get_pres_precision(data); - if (rc < 0) + rc = dps310_get_pres_precision(data, val); + if (rc) return rc; - - *val = rc; return IIO_VAL_INT; default: @@ -734,10 +750,11 @@ static int dps310_calculate_temp(struct dps310_data *data, int *val) { s64 c0; s64 t; - int kt = dps310_get_temp_k(data); + int kt, rc; - if (kt < 0) - return kt; + rc = dps310_get_temp_k(data, &kt); + if (rc) + return rc; /* Obtain inverse-scaled offset */ c0 = div_s64((s64)kt * (s64)data->c0, 2); @@ -758,11 +775,10 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2, switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - rc = dps310_get_temp_samp_freq(data); - if (rc < 0) + rc = dps310_get_temp_samp_freq(data, val); + if (rc) return rc; - *val = rc; return IIO_VAL_INT; case IIO_CHAN_INFO_PROCESSED: @@ -777,11 +793,10 @@ static int dps310_read_temp(struct dps310_data *data, int *val, int *val2, return IIO_VAL_INT; case IIO_CHAN_INFO_OVERSAMPLING_RATIO: - rc = dps310_get_temp_precision(data); - if (rc < 0) + rc = dps310_get_temp_precision(data, val); + if (rc) return rc; - *val = rc; return IIO_VAL_INT; default: diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c index 818fa63034..337eecc577 100644 --- a/drivers/iio/pressure/hsc030pa_spi.c +++ b/drivers/iio/pressure/hsc030pa_spi.c @@ -23,14 +23,9 @@ static int hsc_spi_recv(struct hsc_data *data) { struct spi_device *spi = to_spi_device(data->dev); - struct spi_transfer xfer = { - .tx_buf = NULL, - .rx_buf = data->buffer, - .len = HSC_REG_MEASUREMENT_RD_SIZE, - }; msleep_interruptible(HSC_RESP_TIME_MS); - return spi_sync_transfer(spi, &xfer, 1); + return spi_read(spi, data->buffer, HSC_REG_MEASUREMENT_RD_SIZE); } static int hsc_spi_probe(struct spi_device *spi) diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index 421e059d1f..dcc87a9015 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -861,13 +861,13 @@ static int zpa2326_wait_oneshot_completion(const struct iio_dev *indio_dev, struct zpa2326_private *private) { unsigned int val; - long timeout; + long time_left; zpa2326_dbg(indio_dev, "waiting for one shot completion interrupt"); - timeout = wait_for_completion_interruptible_timeout( + time_left = wait_for_completion_interruptible_timeout( &private->data_ready, ZPA2326_CONVERSION_JIFFIES); - if (timeout > 0) + if (time_left > 0) /* * Interrupt handler completed before timeout: return operation * status. @@ -877,10 +877,10 @@ static int zpa2326_wait_oneshot_completion(const struct iio_dev *indio_dev, /* Clear all interrupts just to be sure. */ regmap_read(private->regmap, ZPA2326_INT_SOURCE_REG, &val); - if (!timeout) { + if (!time_left) { /* Timed out. */ zpa2326_warn(indio_dev, "no one shot interrupt occurred (%ld)", - timeout); + time_left); return -ETIME; } diff --git a/drivers/iio/temperature/ltc2983.c b/drivers/iio/temperature/ltc2983.c index 39447c786a..24d19f3c72 100644 --- a/drivers/iio/temperature/ltc2983.c +++ b/drivers/iio/temperature/ltc2983.c @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/property.h> #include <linux/regmap.h> +#include <linux/regulator/consumer.h> #include <linux/spi/spi.h> #include <asm/byteorder.h> @@ -657,7 +658,6 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data const struct ltc2983_sensor *sensor) { struct ltc2983_thermocouple *thermo; - struct fwnode_handle *ref; u32 oc_current; int ret; @@ -704,7 +704,8 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data return ERR_PTR(-EINVAL); } - ref = fwnode_find_reference(child, "adi,cold-junction-handle", 0); + struct fwnode_handle *ref __free(fwnode_handle) = + fwnode_find_reference(child, "adi,cold-junction-handle", 0); if (IS_ERR(ref)) { ref = NULL; } else { @@ -715,7 +716,7 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data * the error right away. */ dev_err(&st->spi->dev, "Property reg must be given\n"); - goto fail; + return ERR_PTR(ret); } } @@ -726,22 +727,15 @@ ltc2983_thermocouple_new(const struct fwnode_handle *child, struct ltc2983_data thermo->custom = __ltc2983_custom_sensor_new(st, child, propname, false, 16384, true); - if (IS_ERR(thermo->custom)) { - ret = PTR_ERR(thermo->custom); - goto fail; - } + if (IS_ERR(thermo->custom)) + return ERR_CAST(thermo->custom); } /* set common parameters */ thermo->sensor.fault_handler = ltc2983_thermocouple_fault_handler; thermo->sensor.assign_chan = ltc2983_thermocouple_assign_chan; - fwnode_handle_put(ref); return &thermo->sensor; - -fail: - fwnode_handle_put(ref); - return ERR_PTR(ret); } static struct ltc2983_sensor * @@ -751,14 +745,14 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, struct ltc2983_rtd *rtd; int ret = 0; struct device *dev = &st->spi->dev; - struct fwnode_handle *ref; u32 excitation_current = 0, n_wires = 0; rtd = devm_kzalloc(dev, sizeof(*rtd), GFP_KERNEL); if (!rtd) return ERR_PTR(-ENOMEM); - ref = fwnode_find_reference(child, "adi,rsense-handle", 0); + struct fwnode_handle *ref __free(fwnode_handle) = + fwnode_find_reference(child, "adi,rsense-handle", 0); if (IS_ERR(ref)) { dev_err(dev, "Property adi,rsense-handle missing or invalid"); return ERR_CAST(ref); @@ -767,7 +761,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, ret = fwnode_property_read_u32(ref, "reg", &rtd->r_sense_chan); if (ret) { dev_err(dev, "Property reg must be given\n"); - goto fail; + return ERR_PTR(ret); } ret = fwnode_property_read_u32(child, "adi,number-of-wires", &n_wires); @@ -788,8 +782,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, break; default: dev_err(dev, "Invalid number of wires:%u\n", n_wires); - ret = -EINVAL; - goto fail; + return ERR_PTR(-EINVAL); } } @@ -799,8 +792,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, if (n_wires == 2 || n_wires == 3) { dev_err(dev, "Rotation not allowed for 2/3 Wire RTDs"); - ret = -EINVAL; - goto fail; + return ERR_PTR(-EINVAL); } rtd->sensor_config |= LTC2983_RTD_C_ROTATE(1); } else { @@ -830,16 +822,14 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, "Invalid rsense chann:%d to use in kelvin rsense", rtd->r_sense_chan); - ret = -EINVAL; - goto fail; + return ERR_PTR(-EINVAL); } if (sensor->chan < min || sensor->chan > max) { dev_err(dev, "Invalid chann:%d for the rtd config", sensor->chan); - ret = -EINVAL; - goto fail; + return ERR_PTR(-EINVAL); } } else { /* same as differential case */ @@ -847,8 +837,7 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, dev_err(&st->spi->dev, "Invalid chann:%d for RTD", sensor->chan); - ret = -EINVAL; - goto fail; + return ERR_PTR(-EINVAL); } } @@ -857,10 +846,8 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, rtd->custom = __ltc2983_custom_sensor_new(st, child, "adi,custom-rtd", false, 2048, false); - if (IS_ERR(rtd->custom)) { - ret = PTR_ERR(rtd->custom); - goto fail; - } + if (IS_ERR(rtd->custom)) + return ERR_CAST(rtd->custom); } /* set common parameters */ @@ -902,18 +889,13 @@ ltc2983_rtd_new(const struct fwnode_handle *child, struct ltc2983_data *st, dev_err(&st->spi->dev, "Invalid value for excitation current(%u)", excitation_current); - ret = -EINVAL; - goto fail; + return ERR_PTR(-EINVAL); } } fwnode_property_read_u32(child, "adi,rtd-curve", &rtd->rtd_curve); - fwnode_handle_put(ref); return &rtd->sensor; -fail: - fwnode_handle_put(ref); - return ERR_PTR(ret); } static struct ltc2983_sensor * @@ -922,7 +904,6 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s { struct ltc2983_thermistor *thermistor; struct device *dev = &st->spi->dev; - struct fwnode_handle *ref; u32 excitation_current = 0; int ret = 0; @@ -930,7 +911,8 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s if (!thermistor) return ERR_PTR(-ENOMEM); - ref = fwnode_find_reference(child, "adi,rsense-handle", 0); + struct fwnode_handle *ref __free(fwnode_handle) = + fwnode_find_reference(child, "adi,rsense-handle", 0); if (IS_ERR(ref)) { dev_err(dev, "Property adi,rsense-handle missing or invalid"); return ERR_CAST(ref); @@ -939,7 +921,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s ret = fwnode_property_read_u32(ref, "reg", &thermistor->r_sense_chan); if (ret) { dev_err(dev, "rsense channel must be configured...\n"); - goto fail; + return ERR_PTR(ret); } if (fwnode_property_read_bool(child, "adi,single-ended")) { @@ -959,8 +941,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s dev_err(&st->spi->dev, "Invalid chann:%d for differential thermistor", sensor->chan); - ret = -EINVAL; - goto fail; + return ERR_PTR(-EINVAL); } /* check custom sensor */ @@ -979,10 +960,8 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s propname, steinhart, 64, false); - if (IS_ERR(thermistor->custom)) { - ret = PTR_ERR(thermistor->custom); - goto fail; - } + if (IS_ERR(thermistor->custom)) + return ERR_CAST(thermistor->custom); } /* set common parameters */ thermistor->sensor.fault_handler = ltc2983_common_fault_handler; @@ -1006,8 +985,7 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s LTC2983_SENSOR_THERMISTOR_STEINHART) { dev_err(&st->spi->dev, "Auto Range not allowed for custom sensors\n"); - ret = -EINVAL; - goto fail; + return ERR_PTR(-EINVAL); } thermistor->excitation_current = 0x0c; break; @@ -1048,16 +1026,11 @@ ltc2983_thermistor_new(const struct fwnode_handle *child, struct ltc2983_data *s dev_err(&st->spi->dev, "Invalid value for excitation current(%u)", excitation_current); - ret = -EINVAL; - goto fail; + return ERR_PTR(-EINVAL); } } - fwnode_handle_put(ref); return &thermistor->sensor; -fail: - fwnode_handle_put(ref); - return ERR_PTR(ret); } static struct ltc2983_sensor * @@ -1350,8 +1323,7 @@ static irqreturn_t ltc2983_irq_handler(int irq, void *data) static int ltc2983_parse_fw(struct ltc2983_data *st) { struct device *dev = &st->spi->dev; - struct fwnode_handle *child; - int ret = 0, chan = 0, channel_avail_mask = 0; + int ret, chan = 0, channel_avail_mask = 0; device_property_read_u32(dev, "adi,mux-delay-config-us", &st->mux_delay_config); @@ -1369,38 +1341,35 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) return -ENOMEM; st->iio_channels = st->num_channels; - device_for_each_child_node(dev, child) { + device_for_each_child_node_scoped(dev, child) { struct ltc2983_sensor sensor; ret = fwnode_property_read_u32(child, "reg", &sensor.chan); - if (ret) { - dev_err(dev, "reg property must given for child nodes\n"); - goto put_child; - } + if (ret) + return dev_err_probe(dev, ret, + "reg property must given for child nodes\n"); /* check if we have a valid channel */ if (sensor.chan < LTC2983_MIN_CHANNELS_NR || - sensor.chan > st->info->max_channels_nr) { - ret = -EINVAL; - dev_err(dev, "chan:%d must be from %u to %u\n", sensor.chan, - LTC2983_MIN_CHANNELS_NR, st->info->max_channels_nr); - goto put_child; - } else if (channel_avail_mask & BIT(sensor.chan)) { - ret = -EINVAL; - dev_err(dev, "chan:%d already in use\n", sensor.chan); - goto put_child; - } + sensor.chan > st->info->max_channels_nr) + return dev_err_probe(dev, -EINVAL, + "chan:%d must be from %u to %u\n", + sensor.chan, + LTC2983_MIN_CHANNELS_NR, + st->info->max_channels_nr); + + if (channel_avail_mask & BIT(sensor.chan)) + return dev_err_probe(dev, -EINVAL, + "chan:%d already in use\n", + sensor.chan); ret = fwnode_property_read_u32(child, "adi,sensor-type", &sensor.type); - if (ret) { - dev_err(dev, + if (ret) + return dev_err_probe(dev, ret, "adi,sensor-type property must given for child nodes\n"); - goto put_child; - } dev_dbg(dev, "Create new sensor, type %u, chann %u", - sensor.type, - sensor.chan); + sensor.type, sensor.chan); if (sensor.type >= LTC2983_SENSOR_THERMOCOUPLE && sensor.type <= LTC2983_SENSOR_THERMOCOUPLE_CUSTOM) { @@ -1427,17 +1396,15 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) sensor.type == LTC2983_SENSOR_ACTIVE_TEMP) { st->sensors[chan] = ltc2983_temp_new(child, st, &sensor); } else { - dev_err(dev, "Unknown sensor type %d\n", sensor.type); - ret = -EINVAL; - goto put_child; + return dev_err_probe(dev, -EINVAL, + "Unknown sensor type %d\n", + sensor.type); } - if (IS_ERR(st->sensors[chan])) { - dev_err(dev, "Failed to create sensor %ld", - PTR_ERR(st->sensors[chan])); - ret = PTR_ERR(st->sensors[chan]); - goto put_child; - } + if (IS_ERR(st->sensors[chan])) + return dev_err_probe(dev, PTR_ERR(st->sensors[chan]), + "Failed to create sensor\n"); + /* set generic sensor parameters */ st->sensors[chan]->chan = sensor.chan; st->sensors[chan]->type = sensor.type; @@ -1447,9 +1414,6 @@ static int ltc2983_parse_fw(struct ltc2983_data *st) } return 0; -put_child: - fwnode_handle_put(child); - return ret; } static int ltc2983_eeprom_cmd(struct ltc2983_data *st, unsigned int cmd, @@ -1634,6 +1598,10 @@ static int ltc2983_probe(struct spi_device *spi) if (ret) return ret; + ret = devm_regulator_get_enable(&spi->dev, "vdd"); + if (ret) + return ret; + gpio = devm_gpiod_get_optional(&st->spi->dev, "reset", GPIOD_OUT_HIGH); if (IS_ERR(gpio)) return PTR_ERR(gpio); |