diff options
author | Linus Torvalds | 2021-07-05 14:01:53 -0700 |
---|---|---|
committer | Linus Torvalds | 2021-07-05 14:01:53 -0700 |
commit | a16d8644bad461bb073b92e812080ea6715ddf2b (patch) | |
tree | 2baa375527e57c0df2bf56d28e7cf36fb97a435c /drivers/iio | |
parent | f5c13f1fdef9fed65b95c3c5f343d22c425ac1d7 (diff) | |
parent | 77ad1f0e99bd00af024e650b862cfda3137af660 (diff) |
Merge tag 'staging-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging
Pull staging / IIO driver updates from Greg KH:
"Here is the big set of IIO and staging driver patches for 5.14-rc1.
Loads of IIO driver updates and additions in here, the shortlog has
the full details.
For the staging side, we moved a few drivers out of staging, and
deleted the kpc2000 drivers as the original developer asked us to
because no one was working on them anymore.
Also in here are loads of coding style cleanups due to different
intern projects focusing on the staging tree to try to get experience
doing kernel development.
All of these have been in the linux-next tree for a while with no
reported problems"
* tag 'staging-5.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/staging: (744 commits)
staging: hi6421-spmi-pmic: cleanup some macros
staging: hi6421-spmi-pmic: change identation of a table
staging: hi6421-spmi-pmic: change a return code
staging: hi6421-spmi-pmic: better name IRQs
staging: hi6421-spmi-pmic: use devm_request_threaded_irq()
staging: hisilicon,hi6421-spmi-pmic.yaml: cleanup descriptions
spmi: hisi-spmi-controller: move driver from staging
phy: phy-hi3670-usb3: move driver from staging into phy
staging: rtl8188eu: remove include/rtw_debug.h header
staging: rtl8188eu: remove GlobalDebugLevel variable
staging: rtl8188eu: remove DRIVER_PREFIX preprocessor definition
staging: rtl8188eu: remove RT_TRACE macro
staging: rtl8188eu: remove all RT_TRACE calls from hal/rtl8188eu_recv.c
staging: rtl8188eu: remove all RT_TRACE calls from hal/hal_intf.c
staging: rtl8188eu: remove all RT_TRACE calls from hal/rtl8188eu_xmit.c
staging: rtl8188eu: remove all RT_TRACE calls from core/rtw_xmit.c
staging: rtl8188eu: remove all RT_TRACE calls from core/rtw_pwrctrl.c
staging: rtl8188eu: remove all RT_TRACE calls from core/rtw_recv.c
staging: rtl8188eu: remove all RT_TRACE calls from core/rtw_ioctl_set.c
staging: rtl8188eu: remove all RT_TRACE calls from core/rtw_ieee80211.c
...
Diffstat (limited to 'drivers/iio')
204 files changed, 7142 insertions, 1989 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig index 8b1723635cce..0e56ace61103 100644 --- a/drivers/iio/accel/Kconfig +++ b/drivers/iio/accel/Kconfig @@ -89,13 +89,13 @@ config ADXL372_I2C module will be called adxl372_i2c. config BMA180 - tristate "Bosch BMA023/BMA1x0/BMA25x 3-Axis Accelerometer Driver" + tristate "Bosch BMA023/BMA1x0/BMA250 3-Axis Accelerometer Driver" depends on I2C && INPUT_BMA150=n select IIO_BUFFER select IIO_TRIGGERED_BUFFER help Say Y here if you want to build a driver for the Bosch BMA023, BMA150 - BMA180, SMB380, or BMA25x triaxial acceleration sensor. + BMA180, BMA250 or SMB380 triaxial acceleration sensor. To compile this driver as a module, choose M here: the module will be called bma180. @@ -143,9 +143,12 @@ config BMC150_ACCEL select BMC150_ACCEL_SPI if SPI help Say yes here to build support for the following Bosch accelerometers: - BMC150, BMI055, BMA250E, BMA222E, BMA255, BMA280. + BMA222, BMA222E, BMA250E, BMA253, BMA254, BMA255, BMA280, BMC150, BMI055. + + Note that some of these are combo modules: + - BMC150: accelerometer and magnetometer + - BMI055: accelerometer and gyroscope - This is a combo module with both accelerometer and magnetometer. This driver is only implementing accelerometer part, which has its own address and register map. @@ -226,6 +229,33 @@ config DMARD10 Choosing M will build the driver as a module. If so, the module will be called dmard10. +config FXLS8962AF + tristate + +config FXLS8962AF_I2C + tristate "NXP FXLS8962AF/FXLS8964AF Accelerometer I2C Driver" + depends on I2C + select FXLS8962AF + select REGMAP_I2C + help + Say yes here to build support for the NXP 3-axis automotive + accelerometer FXLS8962AF/FXLS8964AF with I2C support. + + To compile this driver as a module, choose M here: the module + will be called fxls8962af_i2c. + +config FXLS8962AF_SPI + tristate "NXP FXLS8962AF/FXLS8964AF Accelerometer SPI Driver" + depends on SPI + select FXLS8962AF + select REGMAP_SPI + help + Say yes here to build support for the NXP 3-axis automotive + accelerometer FXLS8962AF/FXLS8964AF with SPI support. + + To compile this driver as a module, choose M here: the module + will be called fxls8962af_spi. + config HID_SENSOR_ACCEL_3D depends on HID_SENSOR_HUB select IIO_BUFFER @@ -449,6 +479,19 @@ config SCA3000 To compile this driver as a module, say M here: the module will be called sca3000. +config SCA3300 + tristate "Murata SCA3300 3-Axis Accelerometer Driver" + depends on SPI + select CRC8 + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for Murata SCA3300 3-Axis + accelerometer. + + To compile this driver as a module, choose M here: the module will be + called sca3300. + config STK8312 tristate "Sensortek STK8312 3-Axis Accelerometer Driver" depends on I2C diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile index 32cd1342a31a..89280e823bcd 100644 --- a/drivers/iio/accel/Makefile +++ b/drivers/iio/accel/Makefile @@ -27,6 +27,9 @@ obj-$(CONFIG_DA311) += da311.o obj-$(CONFIG_DMARD06) += dmard06.o obj-$(CONFIG_DMARD09) += dmard09.o obj-$(CONFIG_DMARD10) += dmard10.o +obj-$(CONFIG_FXLS8962AF) += fxls8962af-core.o +obj-$(CONFIG_FXLS8962AF_I2C) += fxls8962af-i2c.o +obj-$(CONFIG_FXLS8962AF_SPI) += fxls8962af-spi.o obj-$(CONFIG_HID_SENSOR_ACCEL_3D) += hid-sensor-accel-3d.o obj-$(CONFIG_KXCJK1013) += kxcjk-1013.o obj-$(CONFIG_KXSD9) += kxsd9.o @@ -50,6 +53,7 @@ obj-$(CONFIG_MXC4005) += mxc4005.o obj-$(CONFIG_MXC6255) += mxc6255.o obj-$(CONFIG_SCA3000) += sca3000.o +obj-$(CONFIG_SCA3300) += sca3300.o obj-$(CONFIG_STK8312) += stk8312.o obj-$(CONFIG_STK8BA50) += stk8ba50.o diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c index fe225990de24..7a434e2884d4 100644 --- a/drivers/iio/accel/adis16201.c +++ b/drivers/iio/accel/adis16201.c @@ -8,10 +8,7 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/module.h> -#include <linux/mutex.h> -#include <linux/slab.h> #include <linux/spi/spi.h> -#include <linux/sysfs.h> #include <linux/iio/iio.h> #include <linux/iio/imu/adis.h> diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c index 6c2d4a967de7..ac08e866d612 100644 --- a/drivers/iio/accel/adis16209.c +++ b/drivers/iio/accel/adis16209.c @@ -7,11 +7,8 @@ #include <linux/device.h> #include <linux/kernel.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/spi/spi.h> -#include <linux/slab.h> -#include <linux/sysfs.h> #include <linux/iio/iio.h> #include <linux/iio/imu/adis.h> diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c index 9c9a896a872a..fc9592407717 100644 --- a/drivers/iio/accel/adxl372.c +++ b/drivers/iio/accel/adxl372.c @@ -1223,14 +1223,14 @@ int adxl372_probe(struct device *dev, struct regmap *regmap, st->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (st->dready_trig == NULL) return -ENOMEM; st->peak_datardy_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-peak", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!st->peak_datardy_trig) return -ENOMEM; diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c index b8a7469cdae4..2edfcb4819b7 100644 --- a/drivers/iio/accel/bma180.c +++ b/drivers/iio/accel/bma180.c @@ -10,7 +10,6 @@ * BMA023/BMA150/SMB380: 7-bit I2C slave address 0x38 * BMA180: 7-bit I2C slave address 0x40 or 0x41 * BMA250: 7-bit I2C slave address 0x18 or 0x19 - * BMA254: 7-bit I2C slave address 0x18 or 0x19 */ #include <linux/module.h> @@ -38,7 +37,6 @@ enum chip_ids { BMA150, BMA180, BMA250, - BMA254, }; struct bma180_data; @@ -55,11 +53,10 @@ struct bma180_part_info { u8 int_reset_reg, int_reset_mask; u8 sleep_reg, sleep_mask; - u8 bw_reg, bw_mask; + u8 bw_reg, bw_mask, bw_offset; u8 scale_reg, scale_mask; u8 power_reg, power_mask, lowpower_val; u8 int_enable_reg, int_enable_mask; - u8 int_map_reg, int_enable_dataready_int1_mask; u8 softreset_reg, softreset_val; int (*chip_config)(struct bma180_data *data); @@ -112,7 +109,6 @@ struct bma180_part_info { #define BMA023_ID_REG_VAL 0x02 #define BMA180_ID_REG_VAL 0x03 #define BMA250_ID_REG_VAL 0x03 -#define BMA254_ID_REG_VAL 0xfa /* 250 decimal */ /* Chip power modes */ #define BMA180_LOW_POWER 0x03 @@ -127,29 +123,13 @@ struct bma180_part_info { #define BMA250_RANGE_MASK GENMASK(3, 0) /* Range of accel values */ #define BMA250_BW_MASK GENMASK(4, 0) /* Accel bandwidth */ +#define BMA250_BW_OFFSET 8 #define BMA250_SUSPEND_MASK BIT(7) /* chip will sleep */ #define BMA250_LOWPOWER_MASK BIT(6) #define BMA250_DATA_INTEN_MASK BIT(4) #define BMA250_INT1_DATA_MASK BIT(0) #define BMA250_INT_RESET_MASK BIT(7) /* Reset pending interrupts */ -#define BMA254_RANGE_REG 0x0f -#define BMA254_BW_REG 0x10 -#define BMA254_POWER_REG 0x11 -#define BMA254_RESET_REG 0x14 -#define BMA254_INT_ENABLE_REG 0x17 -#define BMA254_INT_MAP_REG 0x1a -#define BMA254_INT_RESET_REG 0x21 - -#define BMA254_RANGE_MASK GENMASK(3, 0) /* Range of accel values */ -#define BMA254_BW_MASK GENMASK(4, 0) /* Accel bandwidth */ -#define BMA254_SUSPEND_MASK BIT(7) /* chip will sleep */ -#define BMA254_LOWPOWER_MASK BIT(6) -#define BMA254_DATA_INTEN_MASK BIT(4) -#define BMA254_INT2_DATA_MASK BIT(7) -#define BMA254_INT1_DATA_MASK BIT(0) -#define BMA254_INT_RESET_MASK BIT(7) /* Reset pending interrupts */ - struct bma180_data { struct regulator *vdd_supply; struct regulator *vddio_supply; @@ -162,7 +142,11 @@ struct bma180_data { int scale; int bw; bool pmode; - u8 buff[16]; /* 3x 16-bit + 8-bit + padding + timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + s16 chan[4]; + s64 timestamp __aligned(8); + } scan; }; enum bma180_chan { @@ -178,8 +162,8 @@ static int bma023_scale_table[] = { 2452, 4903, 9709, }; static int bma180_bw_table[] = { 10, 20, 40, 75, 150, 300 }; /* Hz */ static int bma180_scale_table[] = { 1275, 1863, 2452, 3727, 4903, 9709, 19417 }; -static int bma25x_bw_table[] = { 8, 16, 31, 63, 125, 250 }; /* Hz */ -static int bma25x_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0, +static int bma250_bw_table[] = { 8, 16, 31, 63, 125, 250, 500, 1000 }; /* Hz */ +static int bma250_scale_table[] = { 0, 0, 0, 38344, 0, 76590, 0, 0, 153180, 0, 0, 0, 306458 }; static int bma180_get_data_reg(struct bma180_data *data, enum bma180_chan chan) @@ -283,7 +267,8 @@ static int bma180_set_bw(struct bma180_data *data, int val) for (i = 0; i < data->part_info->num_bw; ++i) { if (data->part_info->bw_table[i] == val) { ret = bma180_set_bits(data, data->part_info->bw_reg, - data->part_info->bw_mask, i); + data->part_info->bw_mask, + i + data->part_info->bw_offset); if (ret) { dev_err(&data->client->dev, "failed to set bandwidth\n"); @@ -425,7 +410,7 @@ err: return ret; } -static int bma25x_chip_config(struct bma180_data *data) +static int bma250_chip_config(struct bma180_data *data) { int ret = bma180_chip_init(data); @@ -444,8 +429,7 @@ static int bma25x_chip_config(struct bma180_data *data) * This enables dataready interrupt on the INT1 pin * FIXME: support using the INT2 pin */ - ret = bma180_set_bits(data, data->part_info->int_map_reg, - data->part_info->int_enable_dataready_int1_mask, 1); + ret = bma180_set_bits(data, BMA250_INT_MAP_REG, BMA250_INT1_DATA_MASK, 1); if (ret) goto err; @@ -482,7 +466,7 @@ err: dev_err(&data->client->dev, "failed to disable the chip\n"); } -static void bma25x_chip_disable(struct bma180_data *data) +static void bma250_chip_disable(struct bma180_data *data) { if (bma180_set_new_data_intr_state(data, false)) goto err; @@ -768,14 +752,6 @@ static const struct iio_chan_spec bma250_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; -static const struct iio_chan_spec bma254_channels[] = { - BMA180_ACC_CHANNEL(X, 12), - BMA180_ACC_CHANNEL(Y, 12), - BMA180_ACC_CHANNEL(Z, 12), - BMA180_TEMP_CHANNEL, - IIO_CHAN_SOFT_TIMESTAMP(4), -}; - static const struct bma180_part_info bma180_part_info[] = { [BMA023] = { .chip_id = BMA023_ID_REG_VAL, @@ -865,10 +841,10 @@ static const struct bma180_part_info bma180_part_info[] = { .chip_id = BMA250_ID_REG_VAL, .channels = bma250_channels, .num_channels = ARRAY_SIZE(bma250_channels), - .scale_table = bma25x_scale_table, - .num_scales = ARRAY_SIZE(bma25x_scale_table), - .bw_table = bma25x_bw_table, - .num_bw = ARRAY_SIZE(bma25x_bw_table), + .scale_table = bma250_scale_table, + .num_scales = ARRAY_SIZE(bma250_scale_table), + .bw_table = bma250_bw_table, + .num_bw = ARRAY_SIZE(bma250_bw_table), .temp_offset = 48, /* 0 LSB @ 24 degree C */ .int_reset_reg = BMA250_INT_RESET_REG, .int_reset_mask = BMA250_INT_RESET_MASK, @@ -876,6 +852,7 @@ static const struct bma180_part_info bma180_part_info[] = { .sleep_mask = BMA250_SUSPEND_MASK, .bw_reg = BMA250_BW_REG, .bw_mask = BMA250_BW_MASK, + .bw_offset = BMA250_BW_OFFSET, .scale_reg = BMA250_RANGE_REG, .scale_mask = BMA250_RANGE_MASK, .power_reg = BMA250_POWER_REG, @@ -883,41 +860,10 @@ static const struct bma180_part_info bma180_part_info[] = { .lowpower_val = 1, .int_enable_reg = BMA250_INT_ENABLE_REG, .int_enable_mask = BMA250_DATA_INTEN_MASK, - .int_map_reg = BMA250_INT_MAP_REG, - .int_enable_dataready_int1_mask = BMA250_INT1_DATA_MASK, .softreset_reg = BMA250_RESET_REG, .softreset_val = BMA180_RESET_VAL, - .chip_config = bma25x_chip_config, - .chip_disable = bma25x_chip_disable, - }, - [BMA254] = { - .chip_id = BMA254_ID_REG_VAL, - .channels = bma254_channels, - .num_channels = ARRAY_SIZE(bma254_channels), - .scale_table = bma25x_scale_table, - .num_scales = ARRAY_SIZE(bma25x_scale_table), - .bw_table = bma25x_bw_table, - .num_bw = ARRAY_SIZE(bma25x_bw_table), - .temp_offset = 46, /* 0 LSB @ 23 degree C */ - .int_reset_reg = BMA254_INT_RESET_REG, - .int_reset_mask = BMA254_INT_RESET_MASK, - .sleep_reg = BMA254_POWER_REG, - .sleep_mask = BMA254_SUSPEND_MASK, - .bw_reg = BMA254_BW_REG, - .bw_mask = BMA254_BW_MASK, - .scale_reg = BMA254_RANGE_REG, - .scale_mask = BMA254_RANGE_MASK, - .power_reg = BMA254_POWER_REG, - .power_mask = BMA254_LOWPOWER_MASK, - .lowpower_val = 1, - .int_enable_reg = BMA254_INT_ENABLE_REG, - .int_enable_mask = BMA254_DATA_INTEN_MASK, - .int_map_reg = BMA254_INT_MAP_REG, - .int_enable_dataready_int1_mask = BMA254_INT1_DATA_MASK, - .softreset_reg = BMA254_RESET_REG, - .softreset_val = BMA180_RESET_VAL, - .chip_config = bma25x_chip_config, - .chip_disable = bma25x_chip_disable, + .chip_config = bma250_chip_config, + .chip_disable = bma250_chip_disable, }, }; @@ -938,12 +884,12 @@ static irqreturn_t bma180_trigger_handler(int irq, void *p) mutex_unlock(&data->mutex); goto err; } - ((s16 *)data->buff)[i++] = ret; + data->scan.chan[i++] = ret; } mutex_unlock(&data->mutex); - iio_push_to_buffers_with_timestamp(indio_dev, data->buff, time_ns); + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, time_ns); err: iio_trigger_notify_done(indio_dev->trig); @@ -997,8 +943,7 @@ static int bma180_probe(struct i2c_client *client, chip = id->driver_data; data->part_info = &bma180_part_info[chip]; - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; @@ -1045,7 +990,7 @@ static int bma180_probe(struct i2c_client *client, if (client->irq > 0) { data->trig = iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->trig) { ret = -ENOMEM; goto err_chip_disable; @@ -1158,7 +1103,6 @@ static const struct i2c_device_id bma180_ids[] = { { "bma150", BMA150 }, { "bma180", BMA180 }, { "bma250", BMA250 }, - { "bma254", BMA254 }, { "smb380", BMA150 }, { } }; @@ -1183,10 +1127,6 @@ static const struct of_device_id bma180_of_match[] = { .data = (void *)BMA250 }, { - .compatible = "bosch,bma254", - .data = (void *)BMA254 - }, - { .compatible = "bosch,smb380", .data = (void *)BMA150 }, @@ -1209,5 +1149,5 @@ module_i2c_driver(bma180_driver); MODULE_AUTHOR("Kravchenko Oleksandr <x0199363@ti.com>"); MODULE_AUTHOR("Texas Instruments, Inc."); -MODULE_DESCRIPTION("Bosch BMA023/BMA1x0/BMA25x triaxial acceleration sensor"); +MODULE_DESCRIPTION("Bosch BMA023/BMA1x0/BMA250 triaxial acceleration sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c index 36fc9876dbca..0622c7936499 100644 --- a/drivers/iio/accel/bma220_spi.c +++ b/drivers/iio/accel/bma220_spi.c @@ -63,7 +63,11 @@ static const int bma220_scale_table[][2] = { struct bma220_data { struct spi_device *spi_device; struct mutex lock; - s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 8x8 timestamp */ + struct { + s8 chans[3]; + /* Ensure timestamp is naturally aligned. */ + s64 timestamp __aligned(8); + } scan; u8 tx_buf[2] ____cacheline_aligned; }; @@ -94,12 +98,12 @@ static irqreturn_t bma220_trigger_handler(int irq, void *p) mutex_lock(&data->lock); data->tx_buf[0] = BMA220_REG_ACCEL_X | BMA220_READ_MASK; - ret = spi_write_then_read(spi, data->tx_buf, 1, data->buffer, + ret = spi_write_then_read(spi, data->tx_buf, 1, &data->scan.chans, ARRAY_SIZE(bma220_channels) - 1); if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: mutex_unlock(&data->lock); diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c index 7eeba80e32cb..21520e022a21 100644 --- a/drivers/iio/accel/bma400_core.c +++ b/drivers/iio/accel/bma400_core.c @@ -811,7 +811,7 @@ int bma400_probe(struct device *dev, struct regmap *regmap, const char *name) if (ret) return ret; - ret = iio_read_mount_matrix(dev, "mount-matrix", &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c index 04d85ce34e9f..5ce384ebe6c7 100644 --- a/drivers/iio/accel/bmc150-accel-core.c +++ b/drivers/iio/accel/bmc150-accel-core.c @@ -1,14 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * 3-axis accelerometer driver supporting following Bosch-Sensortec chips: - * - BMC150 - * - BMI055 - * - BMA255 - * - BMA250E - * - BMA222 - * - BMA222E - * - BMA280 - * + * 3-axis accelerometer driver supporting many Bosch-Sensortec chips * Copyright (c) 2014, Intel Corporation. */ @@ -157,59 +149,6 @@ struct bmc150_accel_chip_info { const struct bmc150_scale_info scale_table[4]; }; -struct bmc150_accel_interrupt { - const struct bmc150_accel_interrupt_info *info; - atomic_t users; -}; - -struct bmc150_accel_trigger { - struct bmc150_accel_data *data; - struct iio_trigger *indio_trig; - int (*setup)(struct bmc150_accel_trigger *t, bool state); - int intr; - bool enabled; -}; - -enum bmc150_accel_interrupt_id { - BMC150_ACCEL_INT_DATA_READY, - BMC150_ACCEL_INT_ANY_MOTION, - BMC150_ACCEL_INT_WATERMARK, - BMC150_ACCEL_INTERRUPTS, -}; - -enum bmc150_accel_trigger_id { - BMC150_ACCEL_TRIGGER_DATA_READY, - BMC150_ACCEL_TRIGGER_ANY_MOTION, - BMC150_ACCEL_TRIGGERS, -}; - -struct bmc150_accel_data { - struct regmap *regmap; - struct regulator_bulk_data regulators[2]; - struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS]; - struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS]; - struct mutex mutex; - u8 fifo_mode, watermark; - s16 buffer[8]; - /* - * Ensure there is sufficient space and correct alignment for - * the timestamp if enabled - */ - struct { - __le16 channels[3]; - s64 ts __aligned(8); - } scan; - u8 bw_bits; - u32 slope_dur; - u32 slope_thres; - u32 range; - int ev_enable_state; - int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ - const struct bmc150_accel_chip_info *chip_info; - struct i2c_client *second_device; - struct iio_mount_matrix orientation; -}; - static const struct { int val; int val2; @@ -389,7 +328,7 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); @@ -398,9 +337,6 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) if (ret < 0) { dev_err(dev, "Failed: %s for %d\n", __func__, on); - if (on) - pm_runtime_put_noidle(dev); - return ret; } @@ -439,8 +375,8 @@ static int bmc150_accel_set_power_state(struct bmc150_accel_data *data, bool on) * Onda V80 plus * Predia Basic Tablet */ -static bool bmc150_apply_acpi_orientation(struct device *dev, - struct iio_mount_matrix *orientation) +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); @@ -450,9 +386,6 @@ static bool bmc150_apply_acpi_orientation(struct device *dev, acpi_status status; int i, j, val[3]; - if (!adev || !acpi_dev_hid_uid_match(adev, "BOSC0200", NULL)) - return false; - if (strcmp(dev_name(dev), "i2c-BOSC0200:base") == 0) { alt_name = "ROMK"; label = "accel-base"; @@ -508,6 +441,33 @@ unknown_format: kfree(buffer.pointer); return false; } + +static bool bmc150_apply_dual250e_acpi_orientation(struct device *dev, + struct iio_mount_matrix *orientation) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + if (strcmp(dev_name(dev), "i2c-DUAL250E:base") == 0) + indio_dev->label = "accel-base"; + else + indio_dev->label = "accel-display"; + + return false; /* DUAL250E fwnodes have no mount matrix info */ +} + +static bool bmc150_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, "BOSC0200", NULL)) + return bmc150_apply_bosc0200_acpi_orientation(dev, orientation); + + if (adev && acpi_dev_hid_uid_match(adev, "DUAL250E", NULL)) + return bmc150_apply_dual250e_acpi_orientation(dev, orientation); + + return false; +} #else static bool bmc150_apply_acpi_orientation(struct device *dev, struct iio_mount_matrix *orientation) @@ -1128,80 +1088,63 @@ static const struct iio_chan_spec bmc150_accel_channels[] = static const struct iio_chan_spec bma280_accel_channels[] = BMC150_ACCEL_CHANNELS(14); +/* + * The range for the Bosch sensors is typically +-2g/4g/8g/16g, distributed + * over the amount of bits (see above). The scale table can be calculated using + * (range / 2^bits) * g = (range / 2^bits) * 9.80665 m/s^2 + * e.g. for +-2g and 12 bits: (4 / 2^12) * 9.80665 m/s^2 = 0.0095768... m/s^2 + * Multiply 10^6 and round to get the values listed below. + */ static const struct bmc150_accel_chip_info bmc150_accel_chip_info_tbl[] = { - [bmc150] = { - .name = "BMC150A", - .chip_id = 0xFA, - .channels = bmc150_accel_channels, - .num_channels = ARRAY_SIZE(bmc150_accel_channels), - .scale_table = { {9610, BMC150_ACCEL_DEF_RANGE_2G}, - {19122, BMC150_ACCEL_DEF_RANGE_4G}, - {38344, BMC150_ACCEL_DEF_RANGE_8G}, - {76590, BMC150_ACCEL_DEF_RANGE_16G} }, - }, - [bmi055] = { - .name = "BMI055A", - .chip_id = 0xFA, - .channels = bmc150_accel_channels, - .num_channels = ARRAY_SIZE(bmc150_accel_channels), - .scale_table = { {9610, BMC150_ACCEL_DEF_RANGE_2G}, - {19122, BMC150_ACCEL_DEF_RANGE_4G}, - {38344, BMC150_ACCEL_DEF_RANGE_8G}, - {76590, BMC150_ACCEL_DEF_RANGE_16G} }, - }, - [bma255] = { - .name = "BMA0255", - .chip_id = 0xFA, - .channels = bmc150_accel_channels, - .num_channels = ARRAY_SIZE(bmc150_accel_channels), - .scale_table = { {9610, BMC150_ACCEL_DEF_RANGE_2G}, - {19122, BMC150_ACCEL_DEF_RANGE_4G}, - {38344, BMC150_ACCEL_DEF_RANGE_8G}, - {76590, BMC150_ACCEL_DEF_RANGE_16G} }, - }, - [bma250e] = { - .name = "BMA250E", - .chip_id = 0xF9, - .channels = bma250e_accel_channels, - .num_channels = ARRAY_SIZE(bma250e_accel_channels), - .scale_table = { {38344, BMC150_ACCEL_DEF_RANGE_2G}, - {76590, BMC150_ACCEL_DEF_RANGE_4G}, - {153277, BMC150_ACCEL_DEF_RANGE_8G}, - {306457, BMC150_ACCEL_DEF_RANGE_16G} }, - }, - [bma222] = { + { .name = "BMA222", .chip_id = 0x03, .channels = bma222e_accel_channels, .num_channels = ARRAY_SIZE(bma222e_accel_channels), - /* - * The datasheet page 17 says: - * 15.6, 31.3, 62.5 and 125 mg per LSB. - */ - .scale_table = { {156000, BMC150_ACCEL_DEF_RANGE_2G}, - {313000, BMC150_ACCEL_DEF_RANGE_4G}, - {625000, BMC150_ACCEL_DEF_RANGE_8G}, - {1250000, BMC150_ACCEL_DEF_RANGE_16G} }, + .scale_table = { {153229, BMC150_ACCEL_DEF_RANGE_2G}, + {306458, BMC150_ACCEL_DEF_RANGE_4G}, + {612916, BMC150_ACCEL_DEF_RANGE_8G}, + {1225831, BMC150_ACCEL_DEF_RANGE_16G} }, }, - [bma222e] = { + { .name = "BMA222E", .chip_id = 0xF8, .channels = bma222e_accel_channels, .num_channels = ARRAY_SIZE(bma222e_accel_channels), - .scale_table = { {153277, BMC150_ACCEL_DEF_RANGE_2G}, - {306457, BMC150_ACCEL_DEF_RANGE_4G}, - {612915, BMC150_ACCEL_DEF_RANGE_8G}, + .scale_table = { {153229, BMC150_ACCEL_DEF_RANGE_2G}, + {306458, BMC150_ACCEL_DEF_RANGE_4G}, + {612916, BMC150_ACCEL_DEF_RANGE_8G}, {1225831, BMC150_ACCEL_DEF_RANGE_16G} }, }, - [bma280] = { - .name = "BMA0280", + { + .name = "BMA250E", + .chip_id = 0xF9, + .channels = bma250e_accel_channels, + .num_channels = ARRAY_SIZE(bma250e_accel_channels), + .scale_table = { {38307, BMC150_ACCEL_DEF_RANGE_2G}, + {76614, BMC150_ACCEL_DEF_RANGE_4G}, + {153229, BMC150_ACCEL_DEF_RANGE_8G}, + {306458, BMC150_ACCEL_DEF_RANGE_16G} }, + }, + { + .name = "BMA253/BMA254/BMA255/BMC150/BMI055", + .chip_id = 0xFA, + .channels = bmc150_accel_channels, + .num_channels = ARRAY_SIZE(bmc150_accel_channels), + .scale_table = { {9577, BMC150_ACCEL_DEF_RANGE_2G}, + {19154, BMC150_ACCEL_DEF_RANGE_4G}, + {38307, BMC150_ACCEL_DEF_RANGE_8G}, + {76614, BMC150_ACCEL_DEF_RANGE_16G} }, + }, + { + .name = "BMA280", .chip_id = 0xFB, .channels = bma280_accel_channels, .num_channels = ARRAY_SIZE(bma280_accel_channels), - .scale_table = { {2392, BMC150_ACCEL_DEF_RANGE_2G}, - {4785, BMC150_ACCEL_DEF_RANGE_4G}, - {9581, BMC150_ACCEL_DEF_RANGE_8G}, - {19152, BMC150_ACCEL_DEF_RANGE_16G} }, + .scale_table = { {2394, BMC150_ACCEL_DEF_RANGE_2G}, + {4788, BMC150_ACCEL_DEF_RANGE_4G}, + {9577, BMC150_ACCEL_DEF_RANGE_8G}, + {19154, BMC150_ACCEL_DEF_RANGE_16G} }, }, }; @@ -1470,9 +1413,9 @@ static int bmc150_accel_triggers_setup(struct iio_dev *indio_dev, struct bmc150_accel_trigger *t = &data->triggers[i]; t->indio_trig = devm_iio_trigger_alloc(dev, - bmc150_accel_triggers[i].name, + bmc150_accel_triggers[i].name, indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!t->indio_trig) { ret = -ENOMEM; break; @@ -1688,8 +1631,7 @@ int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, data->regmap = regmap; if (!bmc150_apply_acpi_orientation(dev, &data->orientation)) { - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; } @@ -1807,26 +1749,6 @@ err_disable_regulators: } EXPORT_SYMBOL_GPL(bmc150_accel_core_probe); -struct i2c_client *bmc150_get_second_device(struct i2c_client *client) -{ - struct bmc150_accel_data *data = i2c_get_clientdata(client); - - if (!data) - return NULL; - - return data->second_device; -} -EXPORT_SYMBOL_GPL(bmc150_get_second_device); - -void bmc150_set_second_device(struct i2c_client *client) -{ - struct bmc150_accel_data *data = i2c_get_clientdata(client); - - if (data) - data->second_device = client; -} -EXPORT_SYMBOL_GPL(bmc150_set_second_device); - int bmc150_accel_core_remove(struct device *dev) { struct iio_dev *indio_dev = dev_get_drvdata(dev); @@ -1836,7 +1758,6 @@ int bmc150_accel_core_remove(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); bmc150_accel_unregister_triggers(data, BMC150_ACCEL_TRIGGERS - 1); @@ -1876,6 +1797,9 @@ static int bmc150_accel_resume(struct device *dev) bmc150_accel_fifo_set_mode(data); mutex_unlock(&data->mutex); + if (data->resume_callback) + data->resume_callback(dev); + return 0; } #endif diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c index 69f709319484..999495f0669d 100644 --- a/drivers/iio/accel/bmc150-accel-i2c.c +++ b/drivers/iio/accel/bmc150-accel-i2c.c @@ -1,14 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * 3-axis accelerometer driver supporting following I2C Bosch-Sensortec chips: - * - BMC150 - * - BMI055 - * - BMA255 - * - BMA250E - * - BMA222 - * - BMA222E - * - BMA280 - * + * 3-axis accelerometer driver supporting many I2C Bosch-Sensortec chips * Copyright (c) 2014, Intel Corporation. */ @@ -21,6 +13,164 @@ #include "bmc150-accel.h" +#ifdef CONFIG_ACPI +static const struct acpi_device_id bmc150_acpi_dual_accel_ids[] = { + {"BOSC0200"}, + {"DUAL250E"}, + { } +}; + +/* + * The DUAL250E ACPI device for 360° hinges type 2-in-1s with 1 accelerometer + * in the display and 1 in the hinge has an ACPI-method (DSM) to tell the + * ACPI code about the angle between the 2 halves. This will make the ACPI + * code enable/disable the keyboard and touchpad. We need to call this to avoid + * the keyboard being disabled when the 2-in-1 is turned-on or resumed while + * fully folded into tablet mode (which gets detected with a HALL-sensor). + * If we don't call this then the keyboard won't work even when the 2-in-1 is + * changed to be used in laptop mode after the power-on / resume. + * + * This DSM takes 2 angles, selected by setting aux0 to 0 or 1, these presumably + * define the angle between the gravity vector measured by the accelerometer in + * the display (aux0=0) resp. the base (aux0=1) and some reference vector. + * The 2 angles get subtracted from each other so the reference vector does + * not matter and we can simply leave the second angle at 0. + */ + +#define BMC150_DSM_GUID "7681541e-8827-4239-8d9d-36be7fe12542" +#define DUAL250E_SET_ANGLE_FN_INDEX 3 + +struct dual250e_set_angle_args { + u32 aux0; + u32 ang0; + u32 rawx; + u32 rawy; + u32 rawz; +} __packed; + +static bool bmc150_acpi_set_angle_dsm(struct i2c_client *client, u32 aux0, u32 ang0) +{ + struct acpi_device *adev = ACPI_COMPANION(&client->dev); + struct dual250e_set_angle_args args = { + .aux0 = aux0, + .ang0 = ang0, + }; + union acpi_object args_obj, *obj; + guid_t guid; + + if (!acpi_dev_hid_uid_match(adev, "DUAL250E", NULL)) + return false; + + guid_parse(BMC150_DSM_GUID, &guid); + + if (!acpi_check_dsm(adev->handle, &guid, 0, BIT(DUAL250E_SET_ANGLE_FN_INDEX))) + return false; + + /* + * Note this triggers the following warning: + * "ACPI Warning: \_SB.PCI0.I2C2.ACC1._DSM: Argument #4 type mismatch - + * Found [Buffer], ACPI requires [Package]" + * This is unavoidable since the _DSM implementation expects a "naked" + * buffer, so wrapping it in a package will _not_ work. + */ + args_obj.type = ACPI_TYPE_BUFFER; + args_obj.buffer.length = sizeof(args); + args_obj.buffer.pointer = (u8 *)&args; + + obj = acpi_evaluate_dsm(adev->handle, &guid, 0, DUAL250E_SET_ANGLE_FN_INDEX, &args_obj); + if (!obj) { + dev_err(&client->dev, "Failed to call DSM to enable keyboard and touchpad\n"); + return false; + } + + ACPI_FREE(obj); + return true; +} + +static bool bmc150_acpi_enable_keyboard(struct i2c_client *client) +{ + /* + * The EC must see a change for it to re-enable the kbd, so first + * set the angle to 270° (tent/stand mode) and then change it to + * 90° (laptop mode). + */ + if (!bmc150_acpi_set_angle_dsm(client, 0, 270)) + return false; + + /* The EC needs some time to notice the angle being changed */ + msleep(100); + + return bmc150_acpi_set_angle_dsm(client, 0, 90); +} + +static void bmc150_acpi_resume_work(struct work_struct *work) +{ + struct bmc150_accel_data *data = + container_of(work, struct bmc150_accel_data, resume_work.work); + + bmc150_acpi_enable_keyboard(data->second_device); +} + +static void bmc150_acpi_resume_handler(struct device *dev) +{ + struct bmc150_accel_data *data = iio_priv(dev_get_drvdata(dev)); + + /* + * Delay the bmc150_acpi_enable_keyboard() call till after the system + * resume has completed, otherwise it will not work. + */ + schedule_delayed_work(&data->resume_work, msecs_to_jiffies(1000)); +} + +/* + * Some acpi_devices describe 2 accelerometers in a single ACPI device, + * try instantiating a second i2c_client for an I2cSerialBusV2 ACPI resource + * with index 1. + */ +static void bmc150_acpi_dual_accel_probe(struct i2c_client *client) +{ + struct bmc150_accel_data *data = iio_priv(i2c_get_clientdata(client)); + struct acpi_device *adev = ACPI_COMPANION(&client->dev); + char dev_name[16]; + struct i2c_board_info board_info = { + .type = "bmc150_accel", + .dev_name = dev_name, + .fwnode = client->dev.fwnode, + }; + + if (acpi_match_device_ids(adev, bmc150_acpi_dual_accel_ids)) + return; + + /* + * The 2nd accel sits in the base of 2-in-1s. The suffix is static, as + * there should never be more then 1 ACPI node with 2 accelerometers. + */ + snprintf(dev_name, sizeof(dev_name), "%s:base", acpi_device_hid(adev)); + + board_info.irq = acpi_dev_gpio_irq_get(adev, 1); + + data->second_device = i2c_acpi_new_device(&client->dev, 1, &board_info); + + if (!IS_ERR(data->second_device) && bmc150_acpi_enable_keyboard(data->second_device)) { + INIT_DELAYED_WORK(&data->resume_work, bmc150_acpi_resume_work); + data->resume_callback = bmc150_acpi_resume_handler; + } +} + +static void bmc150_acpi_dual_accel_remove(struct i2c_client *client) +{ + struct bmc150_accel_data *data = iio_priv(i2c_get_clientdata(client)); + + if (data->resume_callback) + cancel_delayed_work_sync(&data->resume_work); + + i2c_unregister_device(data->second_device); +} +#else +static void bmc150_acpi_dual_accel_probe(struct i2c_client *client) {} +static void bmc150_acpi_dual_accel_remove(struct i2c_client *client) {} +#endif + static int bmc150_accel_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -30,7 +180,6 @@ static int bmc150_accel_probe(struct i2c_client *client, i2c_check_functionality(client->adapter, I2C_FUNC_I2C) || i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK); - struct acpi_device __maybe_unused *adev; int ret; regmap = devm_regmap_init_i2c(client, &bmc150_regmap_conf); @@ -47,80 +196,62 @@ static int bmc150_accel_probe(struct i2c_client *client, return ret; /* - * Some BOSC0200 acpi_devices describe 2 accelerometers in a single ACPI - * device, try instantiating a second i2c_client for an I2cSerialBusV2 - * ACPI resource with index 1. The !id check avoids recursion when - * bmc150_accel_probe() gets called for the second client. + * The !id check avoids recursion when probe() gets called + * for the second client. */ -#ifdef CONFIG_ACPI - adev = ACPI_COMPANION(&client->dev); - if (!id && adev && strcmp(acpi_device_hid(adev), "BOSC0200") == 0) { - struct i2c_board_info board_info = { - .type = "bmc150_accel", - /* - * The 2nd accel sits in the base of 2-in-1s. Note this - * name is static, as there should never be more then 1 - * BOSC0200 ACPI node with 2 accelerometers in it. - */ - .dev_name = "BOSC0200:base", - .fwnode = client->dev.fwnode, - .irq = -ENOENT, - }; - struct i2c_client *second_dev; - - second_dev = i2c_acpi_new_device(&client->dev, 1, &board_info); - if (!IS_ERR(second_dev)) - bmc150_set_second_device(second_dev); - } -#endif + if (!id && has_acpi_companion(&client->dev)) + bmc150_acpi_dual_accel_probe(client); return 0; } static int bmc150_accel_remove(struct i2c_client *client) { - struct i2c_client *second_dev = bmc150_get_second_device(client); - - i2c_unregister_device(second_dev); + bmc150_acpi_dual_accel_remove(client); return bmc150_accel_core_remove(&client->dev); } static const struct acpi_device_id bmc150_accel_acpi_match[] = { - {"BSBA0150", bmc150}, - {"BMC150A", bmc150}, - {"BMI055A", bmi055}, - {"BMA0255", bma255}, - {"BMA250E", bma250e}, - {"BMA222", bma222}, - {"BMA222E", bma222e}, - {"BMA0280", bma280}, + {"BMA0255"}, + {"BMA0280"}, + {"BMA222"}, + {"BMA222E"}, + {"BMA250E"}, + {"BMC150A"}, + {"BMI055A"}, {"BOSC0200"}, + {"BSBA0150"}, + {"DUAL250E"}, { }, }; MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); static const struct i2c_device_id bmc150_accel_id[] = { - {"bmc150_accel", bmc150}, - {"bmi055_accel", bmi055}, - {"bma255", bma255}, - {"bma250e", bma250e}, - {"bma222", bma222}, - {"bma222e", bma222e}, - {"bma280", bma280}, + {"bma222"}, + {"bma222e"}, + {"bma250e"}, + {"bma253"}, + {"bma254"}, + {"bma255"}, + {"bma280"}, + {"bmc150_accel"}, + {"bmi055_accel"}, {} }; MODULE_DEVICE_TABLE(i2c, bmc150_accel_id); static const struct of_device_id bmc150_accel_of_match[] = { - { .compatible = "bosch,bmc150_accel" }, - { .compatible = "bosch,bmi055_accel" }, - { .compatible = "bosch,bma255" }, - { .compatible = "bosch,bma250e" }, { .compatible = "bosch,bma222" }, { .compatible = "bosch,bma222e" }, + { .compatible = "bosch,bma250e" }, + { .compatible = "bosch,bma253" }, + { .compatible = "bosch,bma254" }, + { .compatible = "bosch,bma255" }, { .compatible = "bosch,bma280" }, + { .compatible = "bosch,bmc150_accel" }, + { .compatible = "bosch,bmi055_accel" }, { }, }; MODULE_DEVICE_TABLE(of, bmc150_accel_of_match); diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c index 74a8aee4f612..54b8c9c8068b 100644 --- a/drivers/iio/accel/bmc150-accel-spi.c +++ b/drivers/iio/accel/bmc150-accel-spi.c @@ -34,26 +34,27 @@ static int bmc150_accel_remove(struct spi_device *spi) } static const struct acpi_device_id bmc150_accel_acpi_match[] = { - {"BSBA0150", bmc150}, - {"BMC150A", bmc150}, - {"BMI055A", bmi055}, - {"BMA0255", bma255}, - {"BMA250E", bma250e}, - {"BMA222", bma222}, - {"BMA222E", bma222e}, - {"BMA0280", bma280}, + {"BMA0255"}, + {"BMA0280"}, + {"BMA222"}, + {"BMA222E"}, + {"BMA250E"}, + {"BMC150A"}, + {"BMI055A"}, + {"BSBA0150"}, { }, }; MODULE_DEVICE_TABLE(acpi, bmc150_accel_acpi_match); static const struct spi_device_id bmc150_accel_id[] = { - {"bmc150_accel", bmc150}, - {"bmi055_accel", bmi055}, - {"bma255", bma255}, - {"bma250e", bma250e}, - {"bma222", bma222}, - {"bma222e", bma222e}, - {"bma280", bma280}, + {"bma222"}, + {"bma222e"}, + {"bma250e"}, + {"bma253"}, + {"bma255"}, + {"bma280"}, + {"bmc150_accel"}, + {"bmi055_accel"}, {} }; MODULE_DEVICE_TABLE(spi, bmc150_accel_id); diff --git a/drivers/iio/accel/bmc150-accel.h b/drivers/iio/accel/bmc150-accel.h index 6024f15b9700..47121f070fe9 100644 --- a/drivers/iio/accel/bmc150-accel.h +++ b/drivers/iio/accel/bmc150-accel.h @@ -2,23 +2,75 @@ #ifndef _BMC150_ACCEL_H_ #define _BMC150_ACCEL_H_ +#include <linux/atomic.h> +#include <linux/iio/iio.h> +#include <linux/mutex.h> +#include <linux/regulator/consumer.h> +#include <linux/workqueue.h> + struct regmap; +struct i2c_client; +struct bmc150_accel_chip_info; +struct bmc150_accel_interrupt_info; + +struct bmc150_accel_interrupt { + const struct bmc150_accel_interrupt_info *info; + atomic_t users; +}; + +struct bmc150_accel_trigger { + struct bmc150_accel_data *data; + struct iio_trigger *indio_trig; + int (*setup)(struct bmc150_accel_trigger *t, bool state); + int intr; + bool enabled; +}; + +enum bmc150_accel_interrupt_id { + BMC150_ACCEL_INT_DATA_READY, + BMC150_ACCEL_INT_ANY_MOTION, + BMC150_ACCEL_INT_WATERMARK, + BMC150_ACCEL_INTERRUPTS, +}; + +enum bmc150_accel_trigger_id { + BMC150_ACCEL_TRIGGER_DATA_READY, + BMC150_ACCEL_TRIGGER_ANY_MOTION, + BMC150_ACCEL_TRIGGERS, +}; -enum { - bmc150, - bmi055, - bma255, - bma250e, - bma222, - bma222e, - bma280, +struct bmc150_accel_data { + struct regmap *regmap; + struct regulator_bulk_data regulators[2]; + struct bmc150_accel_interrupt interrupts[BMC150_ACCEL_INTERRUPTS]; + struct bmc150_accel_trigger triggers[BMC150_ACCEL_TRIGGERS]; + struct mutex mutex; + u8 fifo_mode, watermark; + s16 buffer[8]; + /* + * Ensure there is sufficient space and correct alignment for + * the timestamp if enabled + */ + struct { + __le16 channels[3]; + s64 ts __aligned(8); + } scan; + u8 bw_bits; + u32 slope_dur; + u32 slope_thres; + u32 range; + int ev_enable_state; + int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ + const struct bmc150_accel_chip_info *chip_info; + struct i2c_client *second_device; + void (*resume_callback)(struct device *dev); + struct delayed_work resume_work; + struct iio_mount_matrix orientation; }; int bmc150_accel_core_probe(struct device *dev, struct regmap *regmap, int irq, const char *name, bool block_supported); int bmc150_accel_core_remove(struct device *dev); -struct i2c_client *bmc150_get_second_device(struct i2c_client *second_device); -void bmc150_set_second_device(struct i2c_client *second_device); extern const struct dev_pm_ops bmc150_accel_pm_ops; extern const struct regmap_config bmc150_regmap_conf; diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c index 12d00658e46f..a06dae5c971d 100644 --- a/drivers/iio/accel/bmi088-accel-core.c +++ b/drivers/iio/accel/bmi088-accel-core.c @@ -285,11 +285,17 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev, case IIO_CHAN_INFO_RAW: switch (chan->type) { case IIO_TEMP: - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + ret = bmi088_accel_get_temp(data, val); goto out_read_raw_pm_put; case IIO_ACCEL: - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + ret = iio_device_claim_direct_mode(indio_dev); if (ret) goto out_read_raw_pm_put; @@ -319,7 +325,10 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev, *val = BMI088_ACCEL_TEMP_UNIT; return IIO_VAL_INT; case IIO_ACCEL: - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + ret = regmap_read(data->regmap, BMI088_ACCEL_REG_ACC_RANGE, val); if (ret) @@ -334,7 +343,10 @@ static int bmi088_accel_read_raw(struct iio_dev *indio_dev, return -EINVAL; } case IIO_CHAN_INFO_SAMP_FREQ: - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + ret = bmi088_accel_get_sample_freq(data, val, val2); goto out_read_raw_pm_put; default: @@ -376,7 +388,10 @@ static int bmi088_accel_write_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_SAMP_FREQ: - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + return ret; + ret = bmi088_accel_set_sample_freq(data, val); pm_runtime_mark_last_busy(dev); pm_runtime_put_autosuspend(dev); @@ -496,7 +511,6 @@ int bmi088_accel_core_probe(struct device *dev, struct regmap *regmap, if (ret) return ret; - indio_dev->dev.parent = dev; indio_dev->channels = data->chip_info->channels; indio_dev->num_channels = data->chip_info->num_channels; indio_dev->name = name ? name : data->chip_info->name; @@ -531,7 +545,6 @@ int bmi088_accel_core_remove(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); bmi088_accel_power_down(data); return 0; diff --git a/drivers/iio/accel/fxls8962af-core.c b/drivers/iio/accel/fxls8962af-core.c new file mode 100644 index 000000000000..078d87865fde --- /dev/null +++ b/drivers/iio/accel/fxls8962af-core.c @@ -0,0 +1,968 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NXP FXLS8962AF/FXLS8964AF Accelerometer Core Driver + * + * Copyright 2021 Connected Cars A/S + * + * Datasheet: + * https://www.nxp.com/docs/en/data-sheet/FXLS8962AF.pdf + * https://www.nxp.com/docs/en/data-sheet/FXLS8964AF.pdf + * + * Errata: + * https://www.nxp.com/docs/en/errata/ES_FXLS8962AF.pdf + */ + +#include <linux/bits.h> +#include <linux/bitfield.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/pm_runtime.h> +#include <linux/regulator/consumer.h> +#include <linux/regmap.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/kfifo_buf.h> +#include <linux/iio/sysfs.h> + +#include "fxls8962af.h" + +#define FXLS8962AF_INT_STATUS 0x00 +#define FXLS8962AF_INT_STATUS_SRC_BOOT BIT(0) +#define FXLS8962AF_INT_STATUS_SRC_BUF BIT(5) +#define FXLS8962AF_INT_STATUS_SRC_DRDY BIT(7) +#define FXLS8962AF_TEMP_OUT 0x01 +#define FXLS8962AF_VECM_LSB 0x02 +#define FXLS8962AF_OUT_X_LSB 0x04 +#define FXLS8962AF_OUT_Y_LSB 0x06 +#define FXLS8962AF_OUT_Z_LSB 0x08 +#define FXLS8962AF_BUF_STATUS 0x0b +#define FXLS8962AF_BUF_STATUS_BUF_CNT GENMASK(5, 0) +#define FXLS8962AF_BUF_STATUS_BUF_OVF BIT(6) +#define FXLS8962AF_BUF_STATUS_BUF_WMRK BIT(7) +#define FXLS8962AF_BUF_X_LSB 0x0c +#define FXLS8962AF_BUF_Y_LSB 0x0e +#define FXLS8962AF_BUF_Z_LSB 0x10 + +#define FXLS8962AF_PROD_REV 0x12 +#define FXLS8962AF_WHO_AM_I 0x13 + +#define FXLS8962AF_SYS_MODE 0x14 +#define FXLS8962AF_SENS_CONFIG1 0x15 +#define FXLS8962AF_SENS_CONFIG1_ACTIVE BIT(0) +#define FXLS8962AF_SENS_CONFIG1_RST BIT(7) +#define FXLS8962AF_SC1_FSR_MASK GENMASK(2, 1) +#define FXLS8962AF_SC1_FSR_PREP(x) FIELD_PREP(FXLS8962AF_SC1_FSR_MASK, (x)) +#define FXLS8962AF_SC1_FSR_GET(x) FIELD_GET(FXLS8962AF_SC1_FSR_MASK, (x)) + +#define FXLS8962AF_SENS_CONFIG2 0x16 +#define FXLS8962AF_SENS_CONFIG3 0x17 +#define FXLS8962AF_SC3_WAKE_ODR_MASK GENMASK(7, 4) +#define FXLS8962AF_SC3_WAKE_ODR_PREP(x) FIELD_PREP(FXLS8962AF_SC3_WAKE_ODR_MASK, (x)) +#define FXLS8962AF_SC3_WAKE_ODR_GET(x) FIELD_GET(FXLS8962AF_SC3_WAKE_ODR_MASK, (x)) +#define FXLS8962AF_SENS_CONFIG4 0x18 +#define FXLS8962AF_SC4_INT_PP_OD_MASK BIT(1) +#define FXLS8962AF_SC4_INT_PP_OD_PREP(x) FIELD_PREP(FXLS8962AF_SC4_INT_PP_OD_MASK, (x)) +#define FXLS8962AF_SC4_INT_POL_MASK BIT(0) +#define FXLS8962AF_SC4_INT_POL_PREP(x) FIELD_PREP(FXLS8962AF_SC4_INT_POL_MASK, (x)) +#define FXLS8962AF_SENS_CONFIG5 0x19 + +#define FXLS8962AF_WAKE_IDLE_LSB 0x1b +#define FXLS8962AF_SLEEP_IDLE_LSB 0x1c +#define FXLS8962AF_ASLP_COUNT_LSB 0x1e + +#define FXLS8962AF_INT_EN 0x20 +#define FXLS8962AF_INT_EN_BUF_EN BIT(6) +#define FXLS8962AF_INT_PIN_SEL 0x21 +#define FXLS8962AF_INT_PIN_SEL_MASK GENMASK(7, 0) +#define FXLS8962AF_INT_PIN_SEL_INT1 0x00 +#define FXLS8962AF_INT_PIN_SEL_INT2 GENMASK(7, 0) + +#define FXLS8962AF_OFF_X 0x22 +#define FXLS8962AF_OFF_Y 0x23 +#define FXLS8962AF_OFF_Z 0x24 + +#define FXLS8962AF_BUF_CONFIG1 0x26 +#define FXLS8962AF_BC1_BUF_MODE_MASK GENMASK(6, 5) +#define FXLS8962AF_BC1_BUF_MODE_PREP(x) FIELD_PREP(FXLS8962AF_BC1_BUF_MODE_MASK, (x)) +#define FXLS8962AF_BUF_CONFIG2 0x27 +#define FXLS8962AF_BUF_CONFIG2_BUF_WMRK GENMASK(5, 0) + +#define FXLS8962AF_ORIENT_STATUS 0x28 +#define FXLS8962AF_ORIENT_CONFIG 0x29 +#define FXLS8962AF_ORIENT_DBCOUNT 0x2a +#define FXLS8962AF_ORIENT_BF_ZCOMP 0x2b +#define FXLS8962AF_ORIENT_THS_REG 0x2c + +#define FXLS8962AF_SDCD_INT_SRC1 0x2d +#define FXLS8962AF_SDCD_INT_SRC2 0x2e +#define FXLS8962AF_SDCD_CONFIG1 0x2f +#define FXLS8962AF_SDCD_CONFIG2 0x30 +#define FXLS8962AF_SDCD_OT_DBCNT 0x31 +#define FXLS8962AF_SDCD_WT_DBCNT 0x32 +#define FXLS8962AF_SDCD_LTHS_LSB 0x33 +#define FXLS8962AF_SDCD_UTHS_LSB 0x35 + +#define FXLS8962AF_SELF_TEST_CONFIG1 0x37 +#define FXLS8962AF_SELF_TEST_CONFIG2 0x38 + +#define FXLS8962AF_MAX_REG 0x38 + +#define FXLS8962AF_DEVICE_ID 0x62 +#define FXLS8964AF_DEVICE_ID 0x84 + +/* Raw temp channel offset */ +#define FXLS8962AF_TEMP_CENTER_VAL 25 + +#define FXLS8962AF_AUTO_SUSPEND_DELAY_MS 2000 + +#define FXLS8962AF_FIFO_LENGTH 32 +#define FXLS8962AF_SCALE_TABLE_LEN 4 +#define FXLS8962AF_SAMP_FREQ_TABLE_LEN 13 + +static const int fxls8962af_scale_table[FXLS8962AF_SCALE_TABLE_LEN][2] = { + {0, IIO_G_TO_M_S_2(980000)}, + {0, IIO_G_TO_M_S_2(1950000)}, + {0, IIO_G_TO_M_S_2(3910000)}, + {0, IIO_G_TO_M_S_2(7810000)}, +}; + +static const int fxls8962af_samp_freq_table[FXLS8962AF_SAMP_FREQ_TABLE_LEN][2] = { + {3200, 0}, {1600, 0}, {800, 0}, {400, 0}, {200, 0}, {100, 0}, + {50, 0}, {25, 0}, {12, 500000}, {6, 250000}, {3, 125000}, + {1, 563000}, {0, 781000}, +}; + +struct fxls8962af_chip_info { + const char *name; + const struct iio_chan_spec *channels; + int num_channels; + u8 chip_id; +}; + +struct fxls8962af_data { + struct regmap *regmap; + const struct fxls8962af_chip_info *chip_info; + struct regulator *vdd_reg; + struct { + __le16 channels[3]; + s64 ts __aligned(8); + } scan; + int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */ + struct iio_mount_matrix orientation; + u8 watermark; +}; + +const struct regmap_config fxls8962af_regmap_conf = { + .reg_bits = 8, + .val_bits = 8, + .max_register = FXLS8962AF_MAX_REG, +}; +EXPORT_SYMBOL_GPL(fxls8962af_regmap_conf); + +enum { + fxls8962af_idx_x, + fxls8962af_idx_y, + fxls8962af_idx_z, + fxls8962af_idx_ts, +}; + +enum fxls8962af_int_pin { + FXLS8962AF_PIN_INT1, + FXLS8962AF_PIN_INT2, +}; + +static int fxls8962af_power_on(struct fxls8962af_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + ret = pm_runtime_resume_and_get(dev); + if (ret) + dev_err(dev, "failed to power on\n"); + + return ret; +} + +static int fxls8962af_power_off(struct fxls8962af_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + int ret; + + pm_runtime_mark_last_busy(dev); + ret = pm_runtime_put_autosuspend(dev); + if (ret) + dev_err(dev, "failed to power off\n"); + + return ret; +} + +static int fxls8962af_standby(struct fxls8962af_data *data) +{ + return regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG1, + FXLS8962AF_SENS_CONFIG1_ACTIVE, 0); +} + +static int fxls8962af_active(struct fxls8962af_data *data) +{ + return regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG1, + FXLS8962AF_SENS_CONFIG1_ACTIVE, 1); +} + +static int fxls8962af_is_active(struct fxls8962af_data *data) +{ + unsigned int reg; + int ret; + + ret = regmap_read(data->regmap, FXLS8962AF_SENS_CONFIG1, ®); + if (ret) + return ret; + + return reg & FXLS8962AF_SENS_CONFIG1_ACTIVE; +} + +static int fxls8962af_get_out(struct fxls8962af_data *data, + struct iio_chan_spec const *chan, int *val) +{ + struct device *dev = regmap_get_device(data->regmap); + __le16 raw_val; + int is_active; + int ret; + + is_active = fxls8962af_is_active(data); + if (!is_active) { + ret = fxls8962af_power_on(data); + if (ret) + return ret; + } + + ret = regmap_bulk_read(data->regmap, chan->address, + &raw_val, (chan->scan_type.storagebits / 8)); + + if (!is_active) + fxls8962af_power_off(data); + + if (ret) { + dev_err(dev, "failed to get out reg 0x%lx\n", chan->address); + return ret; + } + + *val = sign_extend32(le16_to_cpu(raw_val), + chan->scan_type.realbits - 1); + + return IIO_VAL_INT; +} + +static int fxls8962af_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *type = IIO_VAL_INT_PLUS_NANO; + *vals = (int *)fxls8962af_scale_table; + *length = ARRAY_SIZE(fxls8962af_scale_table) * 2; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_SAMP_FREQ: + *type = IIO_VAL_INT_PLUS_MICRO; + *vals = (int *)fxls8962af_samp_freq_table; + *length = ARRAY_SIZE(fxls8962af_samp_freq_table) * 2; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int fxls8962af_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_SAMP_FREQ: + return IIO_VAL_INT_PLUS_MICRO; + default: + return IIO_VAL_INT_PLUS_NANO; + } +} + +static int fxls8962af_update_config(struct fxls8962af_data *data, u8 reg, + u8 mask, u8 val) +{ + int ret; + int is_active; + + is_active = fxls8962af_is_active(data); + if (is_active) { + ret = fxls8962af_standby(data); + if (ret) + return ret; + } + + ret = regmap_update_bits(data->regmap, reg, mask, val); + if (ret) + return ret; + + if (is_active) { + ret = fxls8962af_active(data); + if (ret) + return ret; + } + + return 0; +} + +static int fxls8962af_set_full_scale(struct fxls8962af_data *data, u32 scale) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fxls8962af_scale_table); i++) + if (scale == fxls8962af_scale_table[i][1]) + break; + + if (i == ARRAY_SIZE(fxls8962af_scale_table)) + return -EINVAL; + + return fxls8962af_update_config(data, FXLS8962AF_SENS_CONFIG1, + FXLS8962AF_SC1_FSR_MASK, + FXLS8962AF_SC1_FSR_PREP(i)); +} + +static unsigned int fxls8962af_read_full_scale(struct fxls8962af_data *data, + int *val) +{ + int ret; + unsigned int reg; + u8 range_idx; + + ret = regmap_read(data->regmap, FXLS8962AF_SENS_CONFIG1, ®); + if (ret) + return ret; + + range_idx = FXLS8962AF_SC1_FSR_GET(reg); + + *val = fxls8962af_scale_table[range_idx][1]; + + return IIO_VAL_INT_PLUS_NANO; +} + +static int fxls8962af_set_samp_freq(struct fxls8962af_data *data, u32 val, + u32 val2) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(fxls8962af_samp_freq_table); i++) + if (val == fxls8962af_samp_freq_table[i][0] && + val2 == fxls8962af_samp_freq_table[i][1]) + break; + + if (i == ARRAY_SIZE(fxls8962af_samp_freq_table)) + return -EINVAL; + + return fxls8962af_update_config(data, FXLS8962AF_SENS_CONFIG3, + FXLS8962AF_SC3_WAKE_ODR_MASK, + FXLS8962AF_SC3_WAKE_ODR_PREP(i)); +} + +static unsigned int fxls8962af_read_samp_freq(struct fxls8962af_data *data, + int *val, int *val2) +{ + int ret; + unsigned int reg; + u8 range_idx; + + ret = regmap_read(data->regmap, FXLS8962AF_SENS_CONFIG3, ®); + if (ret) + return ret; + + range_idx = FXLS8962AF_SC3_WAKE_ODR_GET(reg); + + *val = fxls8962af_samp_freq_table[range_idx][0]; + *val2 = fxls8962af_samp_freq_table[range_idx][1]; + + return IIO_VAL_INT_PLUS_MICRO; +} + +static int fxls8962af_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + switch (chan->type) { + case IIO_TEMP: + case IIO_ACCEL: + return fxls8962af_get_out(data, chan, val); + default: + return -EINVAL; + } + case IIO_CHAN_INFO_OFFSET: + if (chan->type != IIO_TEMP) + return -EINVAL; + + *val = FXLS8962AF_TEMP_CENTER_VAL; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + *val = 0; + return fxls8962af_read_full_scale(data, val2); + case IIO_CHAN_INFO_SAMP_FREQ: + return fxls8962af_read_samp_freq(data, val, val2); + default: + return -EINVAL; + } +} + +static int fxls8962af_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val != 0) + return -EINVAL; + + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = fxls8962af_set_full_scale(data, val2); + + iio_device_release_direct_mode(indio_dev); + return ret; + case IIO_CHAN_INFO_SAMP_FREQ: + ret = iio_device_claim_direct_mode(indio_dev); + if (ret) + return ret; + + ret = fxls8962af_set_samp_freq(data, val, val2); + + iio_device_release_direct_mode(indio_dev); + return ret; + default: + return -EINVAL; + } +} + +static int fxls8962af_set_watermark(struct iio_dev *indio_dev, unsigned val) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + + if (val > FXLS8962AF_FIFO_LENGTH) + val = FXLS8962AF_FIFO_LENGTH; + + data->watermark = val; + + return 0; +} + +#define FXLS8962AF_CHANNEL(axis, reg, idx) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_SAMP_FREQ), \ + .scan_index = idx, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 12, \ + .storagebits = 16, \ + .shift = 4, \ + .endianness = IIO_BE, \ + }, \ +} + +#define FXLS8962AF_TEMP_CHANNEL { \ + .type = IIO_TEMP, \ + .address = FXLS8962AF_TEMP_OUT, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \ + BIT(IIO_CHAN_INFO_OFFSET),\ + .scan_index = -1, \ + .scan_type = { \ + .realbits = 8, \ + .storagebits = 8, \ + }, \ +} + +static const struct iio_chan_spec fxls8962af_channels[] = { + FXLS8962AF_CHANNEL(X, FXLS8962AF_OUT_X_LSB, fxls8962af_idx_x), + FXLS8962AF_CHANNEL(Y, FXLS8962AF_OUT_Y_LSB, fxls8962af_idx_y), + FXLS8962AF_CHANNEL(Z, FXLS8962AF_OUT_Z_LSB, fxls8962af_idx_z), + IIO_CHAN_SOFT_TIMESTAMP(fxls8962af_idx_ts), + FXLS8962AF_TEMP_CHANNEL, +}; + +static const struct fxls8962af_chip_info fxls_chip_info_table[] = { + [fxls8962af] = { + .chip_id = FXLS8962AF_DEVICE_ID, + .name = "fxls8962af", + .channels = fxls8962af_channels, + .num_channels = ARRAY_SIZE(fxls8962af_channels), + }, + [fxls8964af] = { + .chip_id = FXLS8964AF_DEVICE_ID, + .name = "fxls8964af", + .channels = fxls8962af_channels, + .num_channels = ARRAY_SIZE(fxls8962af_channels), + }, +}; + +static const struct iio_info fxls8962af_info = { + .read_raw = &fxls8962af_read_raw, + .write_raw = &fxls8962af_write_raw, + .write_raw_get_fmt = fxls8962af_write_raw_get_fmt, + .read_avail = fxls8962af_read_avail, + .hwfifo_set_watermark = fxls8962af_set_watermark, +}; + +static int fxls8962af_reset(struct fxls8962af_data *data) +{ + struct device *dev = regmap_get_device(data->regmap); + unsigned int reg; + int ret; + + ret = regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG1, + FXLS8962AF_SENS_CONFIG1_RST, + FXLS8962AF_SENS_CONFIG1_RST); + if (ret) + return ret; + + /* TBOOT1, TBOOT2, specifies we have to wait between 1 - 17.7ms */ + ret = regmap_read_poll_timeout(data->regmap, FXLS8962AF_INT_STATUS, reg, + (reg & FXLS8962AF_INT_STATUS_SRC_BOOT), + 1000, 18000); + if (ret == -ETIMEDOUT) + dev_err(dev, "reset timeout, int_status = 0x%x\n", reg); + + return ret; +} + +static int __fxls8962af_fifo_set_mode(struct fxls8962af_data *data, bool onoff) +{ + int ret; + + /* Enable watermark at max fifo size */ + ret = regmap_update_bits(data->regmap, FXLS8962AF_BUF_CONFIG2, + FXLS8962AF_BUF_CONFIG2_BUF_WMRK, + data->watermark); + if (ret) + return ret; + + return regmap_update_bits(data->regmap, FXLS8962AF_BUF_CONFIG1, + FXLS8962AF_BC1_BUF_MODE_MASK, + FXLS8962AF_BC1_BUF_MODE_PREP(onoff)); +} + +static int fxls8962af_buffer_preenable(struct iio_dev *indio_dev) +{ + return fxls8962af_power_on(iio_priv(indio_dev)); +} + +static int fxls8962af_buffer_postenable(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret; + + fxls8962af_standby(data); + + /* Enable buffer interrupt */ + ret = regmap_update_bits(data->regmap, FXLS8962AF_INT_EN, + FXLS8962AF_INT_EN_BUF_EN, + FXLS8962AF_INT_EN_BUF_EN); + if (ret) + return ret; + + ret = __fxls8962af_fifo_set_mode(data, true); + + fxls8962af_active(data); + + return ret; +} + +static int fxls8962af_buffer_predisable(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + int ret; + + fxls8962af_standby(data); + + /* Disable buffer interrupt */ + ret = regmap_update_bits(data->regmap, FXLS8962AF_INT_EN, + FXLS8962AF_INT_EN_BUF_EN, 0); + if (ret) + return ret; + + ret = __fxls8962af_fifo_set_mode(data, false); + + fxls8962af_active(data); + + return ret; +} + +static int fxls8962af_buffer_postdisable(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + + return fxls8962af_power_off(data); +} + +static const struct iio_buffer_setup_ops fxls8962af_buffer_ops = { + .preenable = fxls8962af_buffer_preenable, + .postenable = fxls8962af_buffer_postenable, + .predisable = fxls8962af_buffer_predisable, + .postdisable = fxls8962af_buffer_postdisable, +}; + +static int fxls8962af_i2c_raw_read_errata3(struct fxls8962af_data *data, + u16 *buffer, int samples, + int sample_length) +{ + int i, ret; + + for (i = 0; i < samples; i++) { + ret = regmap_raw_read(data->regmap, FXLS8962AF_BUF_X_LSB, + &buffer[i * 3], sample_length); + if (ret) + return ret; + } + + return ret; +} + +static int fxls8962af_fifo_transfer(struct fxls8962af_data *data, + u16 *buffer, int samples) +{ + struct device *dev = regmap_get_device(data->regmap); + int sample_length = 3 * sizeof(*buffer); + int total_length = samples * sample_length; + int ret; + + if (i2c_verify_client(dev)) + /* + * Due to errata bug: + * E3: FIFO burst read operation error using I2C interface + * We have to avoid burst reads on I2C.. + */ + ret = fxls8962af_i2c_raw_read_errata3(data, buffer, samples, + sample_length); + else + ret = regmap_raw_read(data->regmap, FXLS8962AF_BUF_X_LSB, buffer, + total_length); + + if (ret) + dev_err(dev, "Error transferring data from fifo: %d\n", ret); + + return ret; +} + +static int fxls8962af_fifo_flush(struct iio_dev *indio_dev) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + u16 buffer[FXLS8962AF_FIFO_LENGTH * 3]; + uint64_t sample_period; + unsigned int reg; + int64_t tstamp; + int ret, i; + u8 count; + + ret = regmap_read(data->regmap, FXLS8962AF_BUF_STATUS, ®); + if (ret) + return ret; + + if (reg & FXLS8962AF_BUF_STATUS_BUF_OVF) { + dev_err(dev, "Buffer overflow"); + return -EOVERFLOW; + } + + count = reg & FXLS8962AF_BUF_STATUS_BUF_CNT; + if (!count) + return 0; + + data->old_timestamp = data->timestamp; + data->timestamp = iio_get_time_ns(indio_dev); + + /* + * Approximate timestamps for each of the sample based on the sampling, + * frequency, timestamp for last sample and number of samples. + */ + sample_period = (data->timestamp - data->old_timestamp); + do_div(sample_period, count); + tstamp = data->timestamp - (count - 1) * sample_period; + + ret = fxls8962af_fifo_transfer(data, buffer, count); + if (ret) + return ret; + + /* Demux hw FIFO into kfifo. */ + for (i = 0; i < count; i++) { + int j, bit; + + j = 0; + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + memcpy(&data->scan.channels[j++], &buffer[i * 3 + bit], + sizeof(data->scan.channels[0])); + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, + tstamp); + + tstamp += sample_period; + } + + return count; +} + +static irqreturn_t fxls8962af_interrupt(int irq, void *p) +{ + struct iio_dev *indio_dev = p; + struct fxls8962af_data *data = iio_priv(indio_dev); + unsigned int reg; + int ret; + + ret = regmap_read(data->regmap, FXLS8962AF_INT_STATUS, ®); + if (ret) + return IRQ_NONE; + + if (reg & FXLS8962AF_INT_STATUS_SRC_BUF) { + ret = fxls8962af_fifo_flush(indio_dev); + if (ret) + return IRQ_NONE; + + return IRQ_HANDLED; + } + + return IRQ_NONE; +} + +static void fxls8962af_regulator_disable(void *data_ptr) +{ + struct fxls8962af_data *data = data_ptr; + + regulator_disable(data->vdd_reg); +} + +static void fxls8962af_pm_disable(void *dev_ptr) +{ + struct device *dev = dev_ptr; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + + pm_runtime_disable(dev); + pm_runtime_set_suspended(dev); + pm_runtime_put_noidle(dev); + + fxls8962af_standby(iio_priv(indio_dev)); +} + +static void fxls8962af_get_irq(struct device_node *of_node, + enum fxls8962af_int_pin *pin) +{ + int irq; + + irq = of_irq_get_byname(of_node, "INT2"); + if (irq > 0) { + *pin = FXLS8962AF_PIN_INT2; + return; + } + + *pin = FXLS8962AF_PIN_INT1; +} + +static int fxls8962af_irq_setup(struct iio_dev *indio_dev, int irq) +{ + struct fxls8962af_data *data = iio_priv(indio_dev); + struct device *dev = regmap_get_device(data->regmap); + unsigned long irq_type; + bool irq_active_high; + enum fxls8962af_int_pin int_pin; + u8 int_pin_sel; + int ret; + + fxls8962af_get_irq(dev->of_node, &int_pin); + switch (int_pin) { + case FXLS8962AF_PIN_INT1: + int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT1; + break; + case FXLS8962AF_PIN_INT2: + int_pin_sel = FXLS8962AF_INT_PIN_SEL_INT2; + break; + default: + dev_err(dev, "unsupported int pin selected\n"); + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, FXLS8962AF_INT_PIN_SEL, + FXLS8962AF_INT_PIN_SEL_MASK, int_pin_sel); + if (ret) + return ret; + + irq_type = irqd_get_trigger_type(irq_get_irq_data(irq)); + + switch (irq_type) { + case IRQF_TRIGGER_HIGH: + case IRQF_TRIGGER_RISING: + irq_active_high = true; + break; + case IRQF_TRIGGER_LOW: + case IRQF_TRIGGER_FALLING: + irq_active_high = false; + break; + default: + dev_info(dev, "mode %lx unsupported\n", irq_type); + return -EINVAL; + } + + ret = regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG4, + FXLS8962AF_SC4_INT_POL_MASK, + FXLS8962AF_SC4_INT_POL_PREP(irq_active_high)); + if (ret) + return ret; + + if (device_property_read_bool(dev, "drive-open-drain")) { + ret = regmap_update_bits(data->regmap, FXLS8962AF_SENS_CONFIG4, + FXLS8962AF_SC4_INT_PP_OD_MASK, + FXLS8962AF_SC4_INT_PP_OD_PREP(1)); + if (ret) + return ret; + + irq_type |= IRQF_SHARED; + } + + return devm_request_threaded_irq(dev, + irq, + NULL, fxls8962af_interrupt, + irq_type | IRQF_ONESHOT, + indio_dev->name, indio_dev); +} + +int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq) +{ + struct fxls8962af_data *data; + struct iio_dev *indio_dev; + unsigned int reg; + int ret, i; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + dev_set_drvdata(dev, indio_dev); + data->regmap = regmap; + + ret = iio_read_mount_matrix(dev, &data->orientation); + if (ret) + return ret; + + data->vdd_reg = devm_regulator_get(dev, "vdd"); + if (IS_ERR(data->vdd_reg)) + return dev_err_probe(dev, PTR_ERR(data->vdd_reg), + "Failed to get vdd regulator\n"); + + ret = regulator_enable(data->vdd_reg); + if (ret) { + dev_err(dev, "Failed to enable vdd regulator: %d\n", ret); + return ret; + } + + ret = devm_add_action_or_reset(dev, fxls8962af_regulator_disable, data); + if (ret) + return ret; + + ret = regmap_read(data->regmap, FXLS8962AF_WHO_AM_I, ®); + if (ret) + return ret; + + for (i = 0; i < ARRAY_SIZE(fxls_chip_info_table); i++) { + if (fxls_chip_info_table[i].chip_id == reg) { + data->chip_info = &fxls_chip_info_table[i]; + break; + } + } + if (i == ARRAY_SIZE(fxls_chip_info_table)) { + dev_err(dev, "failed to match device in table\n"); + return -ENXIO; + } + + indio_dev->channels = data->chip_info->channels; + indio_dev->num_channels = data->chip_info->num_channels; + indio_dev->name = data->chip_info->name; + indio_dev->info = &fxls8962af_info; + indio_dev->modes = INDIO_DIRECT_MODE; + + ret = fxls8962af_reset(data); + if (ret) + return ret; + + if (irq) { + ret = fxls8962af_irq_setup(indio_dev, irq); + if (ret) + return ret; + + ret = devm_iio_kfifo_buffer_setup(dev, indio_dev, + INDIO_BUFFER_SOFTWARE, + &fxls8962af_buffer_ops); + if (ret) + return ret; + } + + ret = pm_runtime_set_active(dev); + if (ret) + return ret; + + pm_runtime_enable(dev); + pm_runtime_set_autosuspend_delay(dev, FXLS8962AF_AUTO_SUSPEND_DELAY_MS); + pm_runtime_use_autosuspend(dev); + + ret = devm_add_action_or_reset(dev, fxls8962af_pm_disable, dev); + if (ret) + return ret; + + return devm_iio_device_register(dev, indio_dev); +} +EXPORT_SYMBOL_GPL(fxls8962af_core_probe); + +static int __maybe_unused fxls8962af_runtime_suspend(struct device *dev) +{ + struct fxls8962af_data *data = iio_priv(dev_get_drvdata(dev)); + int ret; + + ret = fxls8962af_standby(data); + if (ret) { + dev_err(dev, "powering off device failed\n"); + return ret; + } + + return 0; +} + +static int __maybe_unused fxls8962af_runtime_resume(struct device *dev) +{ + struct fxls8962af_data *data = iio_priv(dev_get_drvdata(dev)); + + return fxls8962af_active(data); +} + +const struct dev_pm_ops fxls8962af_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, + pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(fxls8962af_runtime_suspend, + fxls8962af_runtime_resume, NULL) +}; +EXPORT_SYMBOL_GPL(fxls8962af_pm_ops); + +MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>"); +MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/fxls8962af-i2c.c b/drivers/iio/accel/fxls8962af-i2c.c new file mode 100644 index 000000000000..cfb004b20455 --- /dev/null +++ b/drivers/iio/accel/fxls8962af-i2c.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NXP FXLS8962AF/FXLS8964AF Accelerometer I2C Driver + * + * Copyright 2021 Connected Cars A/S + */ + +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/regmap.h> + +#include "fxls8962af.h" + +static int fxls8962af_probe(struct i2c_client *client) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &fxls8962af_regmap_conf); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to initialize i2c regmap\n"); + return PTR_ERR(regmap); + } + + return fxls8962af_core_probe(&client->dev, regmap, client->irq); +} + +static const struct i2c_device_id fxls8962af_id[] = { + { "fxls8962af", fxls8962af }, + { "fxls8964af", fxls8964af }, + {} +}; +MODULE_DEVICE_TABLE(i2c, fxls8962af_id); + +static const struct of_device_id fxls8962af_of_match[] = { + { .compatible = "nxp,fxls8962af" }, + { .compatible = "nxp,fxls8964af" }, + {} +}; +MODULE_DEVICE_TABLE(of, fxls8962af_of_match); + +static struct i2c_driver fxls8962af_driver = { + .driver = { + .name = "fxls8962af_i2c", + .of_match_table = fxls8962af_of_match, + .pm = &fxls8962af_pm_ops, + }, + .probe_new = fxls8962af_probe, + .id_table = fxls8962af_id, +}; +module_i2c_driver(fxls8962af_driver); + +MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>"); +MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/fxls8962af-spi.c b/drivers/iio/accel/fxls8962af-spi.c new file mode 100644 index 000000000000..57108d3d480b --- /dev/null +++ b/drivers/iio/accel/fxls8962af-spi.c @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * NXP FXLS8962AF/FXLS8964AF Accelerometer SPI Driver + * + * Copyright 2021 Connected Cars A/S + */ + +#include <linux/dev_printk.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> +#include <linux/spi/spi.h> +#include <linux/regmap.h> + +#include "fxls8962af.h" + +static int fxls8962af_probe(struct spi_device *spi) +{ + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &fxls8962af_regmap_conf); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to initialize spi regmap\n"); + return PTR_ERR(regmap); + } + + return fxls8962af_core_probe(&spi->dev, regmap, spi->irq); +} + +static const struct of_device_id fxls8962af_spi_of_match[] = { + { .compatible = "nxp,fxls8962af" }, + { .compatible = "nxp,fxls8964af" }, + {} +}; +MODULE_DEVICE_TABLE(of, fxls8962af_spi_of_match); + +static const struct spi_device_id fxls8962af_spi_id_table[] = { + { "fxls8962af", fxls8962af }, + { "fxls8964af", fxls8964af }, + {} +}; +MODULE_DEVICE_TABLE(spi, fxls8962af_spi_id_table); + +static struct spi_driver fxls8962af_driver = { + .driver = { + .name = "fxls8962af_spi", + .pm = &fxls8962af_pm_ops, + .of_match_table = fxls8962af_spi_of_match, + }, + .probe = fxls8962af_probe, + .id_table = fxls8962af_spi_id_table, +}; +module_spi_driver(fxls8962af_driver); + +MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.com>"); +MODULE_DESCRIPTION("NXP FXLS8962AF/FXLS8964AF accelerometer spi driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/fxls8962af.h b/drivers/iio/accel/fxls8962af.h new file mode 100644 index 000000000000..b67572c3ef06 --- /dev/null +++ b/drivers/iio/accel/fxls8962af.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright 2021 Connected Cars A/S + */ +#ifndef _FXLS8962AF_H_ +#define _FXLS8962AF_H_ + +struct regmap; +struct device; + +enum { + fxls8962af, + fxls8964af, +}; + +int fxls8962af_core_probe(struct device *dev, struct regmap *regmap, int irq); +int fxls8962af_core_remove(struct device *dev); + +extern const struct dev_pm_ops fxls8962af_pm_ops; +extern const struct regmap_config fxls8962af_regmap_conf; + +#endif /* _FXLS8962AF_H_ */ diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c index 2f9465cb382f..55cdca818b3b 100644 --- a/drivers/iio/accel/hid-sensor-accel-3d.c +++ b/drivers/iio/accel/hid-sensor-accel-3d.c @@ -6,13 +6,10 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> +#include <linux/mod_devicetable.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include "../common/hid-sensors/hid-sensor-trigger.h" @@ -28,8 +25,11 @@ struct accel_3d_state { struct hid_sensor_hub_callbacks callbacks; struct hid_sensor_common common_attributes; struct hid_sensor_hub_attribute_info accel[ACCEL_3D_CHANNEL_MAX]; - /* Reserve for 3 channels + padding + timestamp */ - u32 accel_val[ACCEL_3D_CHANNEL_MAX + 3]; + /* Ensure timestamp is naturally aligned */ + struct { + u32 accel_val[3]; + s64 timestamp __aligned(8); + } scan; int scale_pre_decml; int scale_post_decml; int scale_precision; @@ -245,8 +245,8 @@ static int accel_3d_proc_event(struct hid_sensor_hub_device *hsdev, accel_state->timestamp = iio_get_time_ns(indio_dev); hid_sensor_push_data(indio_dev, - accel_state->accel_val, - sizeof(accel_state->accel_val), + &accel_state->scan, + sizeof(accel_state->scan), accel_state->timestamp); accel_state->timestamp = 0; @@ -271,7 +271,7 @@ static int accel_3d_capture_sample(struct hid_sensor_hub_device *hsdev, case HID_USAGE_SENSOR_ACCEL_Y_AXIS: case HID_USAGE_SENSOR_ACCEL_Z_AXIS: offset = usage_id - HID_USAGE_SENSOR_ACCEL_X_AXIS; - accel_state->accel_val[CHANNEL_SCAN_INDEX_X + offset] = + accel_state->scan.accel_val[CHANNEL_SCAN_INDEX_X + offset] = *(u32 *)raw_data; ret = 0; break; @@ -462,3 +462,4 @@ module_platform_driver(hid_accel_3d_platform_driver); MODULE_DESCRIPTION("HID Sensor Accel 3D"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c index ff724bc17a45..a51fdd3c9b5b 100644 --- a/drivers/iio/accel/kxcjk-1013.c +++ b/drivers/iio/accel/kxcjk-1013.c @@ -51,13 +51,15 @@ #define KXTF9_REG_TILT_POS_CUR 0x10 #define KXTF9_REG_TILT_POS_PREV 0x11 #define KXTF9_REG_INT_SRC1 0x15 -#define KXCJK1013_REG_INT_SRC1 0x16 /* compatible, but called INT_SRC2 in KXTF9 ds */ +#define KXTF9_REG_INT_SRC2 0x16 +#define KXCJK1013_REG_INT_SRC1 0x16 #define KXCJK1013_REG_INT_SRC2 0x17 #define KXCJK1013_REG_STATUS_REG 0x18 #define KXCJK1013_REG_INT_REL 0x1A #define KXCJK1013_REG_CTRL1 0x1B #define KXTF9_REG_CTRL2 0x1C -#define KXCJK1013_REG_CTRL2 0x1D /* mostly compatible, CTRL_REG3 in KTXF9 ds */ +#define KXTF9_REG_CTRL3 0x1D +#define KXCJK1013_REG_CTRL2 0x1D #define KXCJK1013_REG_INT_CTRL1 0x1E #define KXCJK1013_REG_INT_CTRL2 0x1F #define KXTF9_REG_INT_CTRL3 0x20 @@ -77,6 +79,45 @@ #define KXTF9_REG_HYST_SET 0x5F #define KXCJK1013_REG_WAKE_THRES 0x6A +/* Everything up to 0x11 is equal to KXCJK1013/KXTF9 above */ +#define KX023_REG_INS1 0x12 +#define KX023_REG_INS2 0x13 +#define KX023_REG_INS3 0x14 +#define KX023_REG_STAT 0x15 +#define KX023_REG_INT_REL 0x17 +#define KX023_REG_CNTL1 0x18 +#define KX023_REG_CNTL2 0x19 +#define KX023_REG_CNTL3 0x1A +#define KX023_REG_ODCNTL 0x1B +#define KX023_REG_INC1 0x1C +#define KX023_REG_INC2 0x1D +#define KX023_REG_INC3 0x1E +#define KX023_REG_INC4 0x1F +#define KX023_REG_INC5 0x20 +#define KX023_REG_INC6 0x21 +#define KX023_REG_TILT_TIMER 0x22 +#define KX023_REG_WUFC 0x23 +#define KX023_REG_TDTRC 0x24 +#define KX023_REG_TDTC 0x25 +#define KX023_REG_TTH 0x26 +#define KX023_REG_TTL 0x27 +#define KX023_REG_FTD 0x28 +#define KX023_REG_STD 0x29 +#define KX023_REG_TLT 0x2A +#define KX023_REG_TWS 0x2B +#define KX023_REG_ATH 0x30 +#define KX023_REG_TILT_ANGLE_LL 0x32 +#define KX023_REG_TILT_ANGLE_HL 0x33 +#define KX023_REG_HYST_SET 0x34 +#define KX023_REG_LP_CNTL 0x35 +#define KX023_REG_BUF_CNTL1 0x3A +#define KX023_REG_BUF_CNTL2 0x3B +#define KX023_REG_BUF_STATUS_1 0x3C +#define KX023_REG_BUF_STATUS_2 0x3D +#define KX023_REG_BUF_CLEAR 0x3E +#define KX023_REG_BUF_READ 0x3F +#define KX023_REG_SELF_TEST 0x60 + #define KXCJK1013_REG_CTRL1_BIT_PC1 BIT(7) #define KXCJK1013_REG_CTRL1_BIT_RES BIT(6) #define KXCJK1013_REG_CTRL1_BIT_DRDY BIT(5) @@ -117,6 +158,14 @@ #define KXCJK1013_REG_INT_SRC2_BIT_XP BIT(4) #define KXCJK1013_REG_INT_SRC2_BIT_XN BIT(5) +/* KX023 interrupt routing to INT1. INT2 can be configured with INC6 */ +#define KX023_REG_INC4_BFI1 BIT(6) +#define KX023_REG_INC4_WMI1 BIT(5) +#define KX023_REG_INC4_DRDY1 BIT(4) +#define KX023_REG_INC4_TDTI1 BIT(2) +#define KX023_REG_INC4_WUFI1 BIT(1) +#define KX023_REG_INC4_TPI1 BIT(0) + #define KXCJK1013_DEFAULT_WAKE_THRES 1 enum kx_chipset { @@ -124,6 +173,7 @@ enum kx_chipset { KXCJ91008, KXTJ21009, KXTF9, + KX0231025, KX_MAX_CHIPS /* this must be last */ }; @@ -133,6 +183,63 @@ enum kx_acpi_type { ACPI_KIOX010A, }; +struct kx_chipset_regs { + u8 int_src1; + u8 int_src2; + u8 int_rel; + u8 ctrl1; + u8 wuf_ctrl; + u8 int_ctrl1; + u8 data_ctrl; + u8 wake_timer; + u8 wake_thres; +}; + +static const struct kx_chipset_regs kxcjk1013_regs = { + .int_src1 = KXCJK1013_REG_INT_SRC1, + .int_src2 = KXCJK1013_REG_INT_SRC2, + .int_rel = KXCJK1013_REG_INT_REL, + .ctrl1 = KXCJK1013_REG_CTRL1, + .wuf_ctrl = KXCJK1013_REG_CTRL2, + .int_ctrl1 = KXCJK1013_REG_INT_CTRL1, + .data_ctrl = KXCJK1013_REG_DATA_CTRL, + .wake_timer = KXCJK1013_REG_WAKE_TIMER, + .wake_thres = KXCJK1013_REG_WAKE_THRES, +}; + +static const struct kx_chipset_regs kxtf9_regs = { + /* .int_src1 was moved to INT_SRC2 on KXTF9 */ + .int_src1 = KXTF9_REG_INT_SRC2, + /* .int_src2 is not available */ + .int_rel = KXCJK1013_REG_INT_REL, + .ctrl1 = KXCJK1013_REG_CTRL1, + .wuf_ctrl = KXTF9_REG_CTRL3, + .int_ctrl1 = KXCJK1013_REG_INT_CTRL1, + .data_ctrl = KXCJK1013_REG_DATA_CTRL, + .wake_timer = KXCJK1013_REG_WAKE_TIMER, + .wake_thres = KXTF9_REG_WAKE_THRESH, +}; + +/* The registers have totally different names but the bits are compatible */ +static const struct kx_chipset_regs kx0231025_regs = { + .int_src1 = KX023_REG_INS2, + .int_src2 = KX023_REG_INS3, + .int_rel = KX023_REG_INT_REL, + .ctrl1 = KX023_REG_CNTL1, + .wuf_ctrl = KX023_REG_CNTL3, + .int_ctrl1 = KX023_REG_INC1, + .data_ctrl = KX023_REG_ODCNTL, + .wake_timer = KX023_REG_WUFC, + .wake_thres = KX023_REG_ATH, +}; + +enum kxcjk1013_axis { + AXIS_X, + AXIS_Y, + AXIS_Z, + AXIS_MAX +}; + struct kxcjk1013_data { struct regulator_bulk_data regulators[2]; struct i2c_client *client; @@ -140,7 +247,11 @@ struct kxcjk1013_data { struct iio_trigger *motion_trig; struct iio_mount_matrix orientation; struct mutex mutex; - s16 buffer[8]; + /* Ensure timestamp naturally aligned */ + struct { + s16 chans[AXIS_MAX]; + s64 timestamp __aligned(8); + } scan; u8 odr_bits; u8 range; int wake_thres; @@ -152,13 +263,7 @@ struct kxcjk1013_data { int64_t timestamp; enum kx_chipset chipset; enum kx_acpi_type acpi_type; -}; - -enum kxcjk1013_axis { - AXIS_X, - AXIS_Y, - AXIS_Z, - AXIS_MAX, + const struct kx_chipset_regs *regs; }; enum kxcjk1013_mode { @@ -268,6 +373,22 @@ static const struct { {0x05, 5100}, {0x06, 2700}, }, + /* KX023-1025 */ + { + /* First 4 are not in datasheet, taken from KXCTJ2-1009 */ + {0x08, 1240000}, + {0x09, 621000}, + {0x0A, 309000}, + {0x0B, 151000}, + {0, 81000}, + {0x01, 40000}, + {0x02, 22000}, + {0x03, 12000}, + {0x04, 7000}, + {0x05, 4400}, + {0x06, 3000}, + {0x07, 3000}, + }, }; static const struct { @@ -309,7 +430,7 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data, { int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -320,8 +441,7 @@ static int kxcjk1013_set_mode(struct kxcjk1013_data *data, else ret |= KXCJK1013_REG_CTRL1_BIT_PC1; - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_CTRL1, ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -335,7 +455,7 @@ static int kxcjk1013_get_mode(struct kxcjk1013_data *data, { int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -353,7 +473,7 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) { int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -364,9 +484,7 @@ static int kxcjk1013_set_range(struct kxcjk1013_data *data, int range_index) ret |= (KXCJK1013_scale_table[range_index].gsel_0 << 3); ret |= (KXCJK1013_scale_table[range_index].gsel_1 << 4); - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -400,7 +518,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -409,8 +527,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) /* Set 12 bit mode */ ret |= KXCJK1013_REG_CTRL1_BIT_RES; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl\n"); return ret; @@ -421,7 +538,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_DATA_CTRL); + ret = i2c_smbus_read_byte_data(data->client, data->regs->data_ctrl); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_data_ctrl\n"); return ret; @@ -430,7 +547,7 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) data->odr_bits = ret; /* Set up INT polarity */ - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); return ret; @@ -441,13 +558,23 @@ static int kxcjk1013_chip_init(struct kxcjk1013_data *data) else ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEA; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret; } + /* On KX023, route all used interrupts to INT1 for now */ + if (data->chipset == KX0231025 && data->client->irq > 0) { + ret = i2c_smbus_write_byte_data(data->client, KX023_REG_INC4, + KX023_REG_INC4_DRDY1 | + KX023_REG_INC4_WUFI1); + if (ret < 0) { + dev_err(&data->client->dev, "Error writing reg_inc4\n"); + return ret; + } + } + ret = kxcjk1013_set_mode(data, OPERATION); if (ret < 0) return ret; @@ -478,7 +605,7 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) int ret; if (on) - ret = pm_runtime_get_sync(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); else { pm_runtime_mark_last_busy(&data->client->dev); ret = pm_runtime_put_autosuspend(&data->client->dev); @@ -486,8 +613,6 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) if (ret < 0) { dev_err(&data->client->dev, "Failed: %s for %d\n", __func__, on); - if (on) - pm_runtime_put_noidle(&data->client->dev); return ret; } #endif @@ -497,10 +622,9 @@ static int kxcjk1013_set_power_state(struct kxcjk1013_data *data, bool on) static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) { - int waketh_reg, ret; + int ret; - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_WAKE_TIMER, + ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_timer, data->wake_dur); if (ret < 0) { dev_err(&data->client->dev, @@ -508,9 +632,7 @@ static int kxcjk1013_chip_update_thresholds(struct kxcjk1013_data *data) return ret; } - waketh_reg = data->chipset == KXTF9 ? - KXTF9_REG_WAKE_THRESH : KXCJK1013_REG_WAKE_THRES; - ret = i2c_smbus_write_byte_data(data->client, waketh_reg, + ret = i2c_smbus_write_byte_data(data->client, data->regs->wake_thres, data->wake_thres); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_wake_thres\n"); @@ -539,7 +661,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); return ret; @@ -550,14 +672,13 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret; } - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -568,8 +689,7 @@ static int kxcjk1013_setup_any_motion_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_CTRL1_BIT_WUFE; - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_CTRL1, ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -599,7 +719,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, if (ret < 0) return ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_ctrl1\n"); return ret; @@ -610,14 +730,13 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_INT_CTRL1_BIT_IEN; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_INT_CTRL1, - ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->int_ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_int_ctrl1\n"); return ret; } - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_CTRL1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->ctrl1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_ctrl1\n"); return ret; @@ -628,8 +747,7 @@ static int kxcjk1013_setup_new_data_interrupt(struct kxcjk1013_data *data, else ret &= ~KXCJK1013_REG_CTRL1_BIT_DRDY; - ret = i2c_smbus_write_byte_data(data->client, - KXCJK1013_REG_CTRL1, ret); + ret = i2c_smbus_write_byte_data(data->client, data->regs->ctrl1, ret); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl1\n"); return ret; @@ -701,7 +819,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) if (ret < 0) return ret; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_DATA_CTRL, + ret = i2c_smbus_write_byte_data(data->client, data->regs->data_ctrl, odr_setting->odr_bits); if (ret < 0) { dev_err(&data->client->dev, "Error writing data_ctrl\n"); @@ -710,7 +828,7 @@ static int kxcjk1013_set_odr(struct kxcjk1013_data *data, int val, int val2) data->odr_bits = odr_setting->odr_bits; - ret = i2c_smbus_write_byte_data(data->client, KXCJK1013_REG_CTRL2, + ret = i2c_smbus_write_byte_data(data->client, data->regs->wuf_ctrl, odr_setting->wuf_bits); if (ret < 0) { dev_err(&data->client->dev, "Error writing reg_ctrl2\n"); @@ -1094,12 +1212,12 @@ static irqreturn_t kxcjk1013_trigger_handler(int irq, void *p) ret = i2c_smbus_read_i2c_block_data_or_emulated(data->client, KXCJK1013_REG_XOUT_L, AXIS_MAX * 2, - (u8 *)data->buffer); + (u8 *)data->scan.chans); mutex_unlock(&data->mutex); if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, data->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -1113,7 +1231,7 @@ static void kxcjk1013_trig_reen(struct iio_trigger *trig) struct kxcjk1013_data *data = iio_priv(indio_dev); int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_REL); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_rel); if (ret < 0) dev_err(&data->client->dev, "Error reading reg_int_rel\n"); } @@ -1166,8 +1284,7 @@ static void kxcjk1013_report_motion_event(struct iio_dev *indio_dev) { struct kxcjk1013_data *data = iio_priv(indio_dev); - int ret = i2c_smbus_read_byte_data(data->client, - KXCJK1013_REG_INT_SRC2); + int ret = i2c_smbus_read_byte_data(data->client, data->regs->int_src2); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_src2\n"); return; @@ -1234,7 +1351,7 @@ static irqreturn_t kxcjk1013_event_handler(int irq, void *private) struct kxcjk1013_data *data = iio_priv(indio_dev); int ret; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_SRC1); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_src1); if (ret < 0) { dev_err(&data->client->dev, "Error reading reg_int_src1\n"); goto ack_intr; @@ -1257,7 +1374,7 @@ ack_intr: if (data->dready_trigger_on) return IRQ_HANDLED; - ret = i2c_smbus_read_byte_data(data->client, KXCJK1013_REG_INT_REL); + ret = i2c_smbus_read_byte_data(data->client, data->regs->int_rel); if (ret < 0) dev_err(&data->client->dev, "Error reading reg_int_rel\n"); @@ -1338,8 +1455,7 @@ static int kxcjk1013_probe(struct i2c_client *client, } else { data->active_high_intr = true; /* default polarity */ - ret = iio_read_mount_matrix(&client->dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(&client->dev, &data->orientation); if (ret) return ret; } @@ -1378,6 +1494,22 @@ static int kxcjk1013_probe(struct i2c_client *client, } else return -ENODEV; + switch (data->chipset) { + case KXCJK1013: + case KXCJ91008: + case KXTJ21009: + data->regs = &kxcjk1013_regs; + break; + case KXTF9: + data->regs = &kxtf9_regs; + break; + case KX0231025: + data->regs = &kx0231025_regs; + break; + default: + return -EINVAL; + } + ret = kxcjk1013_chip_init(data); if (ret < 0) return ret; @@ -1404,7 +1536,7 @@ static int kxcjk1013_probe(struct i2c_client *client, data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) { ret = -ENOMEM; goto err_poweroff; @@ -1413,7 +1545,7 @@ static int kxcjk1013_probe(struct i2c_client *client, data->motion_trig = devm_iio_trigger_alloc(&client->dev, "%s-any-motion-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->motion_trig) { ret = -ENOMEM; goto err_poweroff; @@ -1485,7 +1617,6 @@ static int kxcjk1013_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); if (data->dready_trig) { iio_triggered_buffer_cleanup(indio_dev); @@ -1593,6 +1724,7 @@ static const struct i2c_device_id kxcjk1013_id[] = { {"kxcj91008", KXCJ91008}, {"kxtj21009", KXTJ21009}, {"kxtf9", KXTF9}, + {"kx023-1025", KX0231025}, {"SMO8500", KXCJ91008}, {} }; @@ -1604,6 +1736,7 @@ static const struct of_device_id kxcjk1013_of_match[] = { { .compatible = "kionix,kxcj91008", }, { .compatible = "kionix,kxtj21009", }, { .compatible = "kionix,kxtf9", }, + { .compatible = "kionix,kx023-1025", }, { } }; MODULE_DEVICE_TABLE(of, kxcjk1013_of_match); diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c index 0e18b92e2099..bf7ed9e7d00f 100644 --- a/drivers/iio/accel/kxsd9.c +++ b/drivers/iio/accel/kxsd9.c @@ -420,7 +420,7 @@ int kxsd9_common_probe(struct device *dev, indio_dev->available_scan_masks = kxsd9_scan_masks; /* Read the mounting matrix, if present */ - ret = iio_read_mount_matrix(dev, "mount-matrix", &st->orientation); + ret = iio_read_mount_matrix(dev, &st->orientation); if (ret) return ret; diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c index 4d307dfb9169..715b8138fb71 100644 --- a/drivers/iio/accel/mma8452.c +++ b/drivers/iio/accel/mma8452.c @@ -221,7 +221,7 @@ static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(&client->dev); + ret = pm_runtime_resume_and_get(&client->dev); } else { pm_runtime_mark_last_busy(&client->dev); ret = pm_runtime_put_autosuspend(&client->dev); @@ -230,8 +230,6 @@ static int mma8452_set_runtime_pm_state(struct i2c_client *client, bool on) if (ret < 0) { dev_err(&client->dev, "failed to change power state to %d\n", on); - if (on) - pm_runtime_put_noidle(&client->dev); return ret; } @@ -1461,7 +1459,7 @@ static int mma8452_trigger_setup(struct iio_dev *indio_dev) trig = devm_iio_trigger_alloc(&data->client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!trig) return -ENOMEM; @@ -1711,7 +1709,6 @@ static int mma8452_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); iio_triggered_buffer_cleanup(indio_dev); mma8452_trigger_cleanup(indio_dev); diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c index 08a2303cc9df..4c359fb05480 100644 --- a/drivers/iio/accel/mma9551.c +++ b/drivers/iio/accel/mma9551.c @@ -515,7 +515,6 @@ static int mma9551_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); mutex_lock(&data->mutex); mma9551_set_device_state(data->client, false); diff --git a/drivers/iio/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c index 666e7a04a7d7..fbf2e2c45678 100644 --- a/drivers/iio/accel/mma9551_core.c +++ b/drivers/iio/accel/mma9551_core.c @@ -664,7 +664,7 @@ int mma9551_set_power_state(struct i2c_client *client, bool on) int ret; if (on) - ret = pm_runtime_get_sync(&client->dev); + ret = pm_runtime_resume_and_get(&client->dev); else { pm_runtime_mark_last_busy(&client->dev); ret = pm_runtime_put_autosuspend(&client->dev); @@ -673,8 +673,6 @@ int mma9551_set_power_state(struct i2c_client *client, bool on) if (ret < 0) { dev_err(&client->dev, "failed to change power state to %d\n", on); - if (on) - pm_runtime_put_noidle(&client->dev); return ret; } diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c index c15908faa381..ba3ecb3b57dc 100644 --- a/drivers/iio/accel/mma9553.c +++ b/drivers/iio/accel/mma9553.c @@ -1154,7 +1154,6 @@ static int mma9553_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); mutex_lock(&data->mutex); mma9551_set_device_state(data->client, false); diff --git a/drivers/iio/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c index fb3cbaa62bd8..b3afbf064915 100644 --- a/drivers/iio/accel/mxc4005.c +++ b/drivers/iio/accel/mxc4005.c @@ -56,7 +56,11 @@ struct mxc4005_data { struct mutex mutex; struct regmap *regmap; struct iio_trigger *dready_trig; - __be16 buffer[8]; + /* Ensure timestamp is naturally aligned */ + struct { + __be16 chans[3]; + s64 timestamp __aligned(8); + } scan; bool trigger_enabled; }; @@ -135,7 +139,7 @@ static int mxc4005_read_xyz(struct mxc4005_data *data) int ret; ret = regmap_bulk_read(data->regmap, MXC4005_REG_XOUT_UPPER, - data->buffer, sizeof(data->buffer)); + data->scan.chans, sizeof(data->scan.chans)); if (ret < 0) { dev_err(data->dev, "failed to read axes\n"); return ret; @@ -301,7 +305,7 @@ static irqreturn_t mxc4005_trigger_handler(int irq, void *private) if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: @@ -433,7 +437,7 @@ static int mxc4005_probe(struct i2c_client *client, data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) return -ENOMEM; diff --git a/drivers/iio/accel/sca3300.c b/drivers/iio/accel/sca3300.c new file mode 100644 index 000000000000..f7ef8ecfd34a --- /dev/null +++ b/drivers/iio/accel/sca3300.c @@ -0,0 +1,472 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Murata SCA3300 3-axis industrial accelerometer + * + * Copyright (c) 2021 Vaisala Oyj. All rights reserved. + */ + +#include <linux/bitops.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include <asm/unaligned.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> + +#define SCA3300_ALIAS "sca3300" + +#define SCA3300_CRC8_POLYNOMIAL 0x1d + +/* Device mode register */ +#define SCA3300_REG_MODE 0xd +#define SCA3300_MODE_SW_RESET 0x20 + +/* Last register in map */ +#define SCA3300_REG_SELBANK 0x1f + +/* Device status and mask */ +#define SCA3300_REG_STATUS 0x6 +#define SCA3300_STATUS_MASK GENMASK(8, 0) + +/* Device ID */ +#define SCA3300_REG_WHOAMI 0x10 +#define SCA3300_WHOAMI_ID 0x51 + +/* Device return status and mask */ +#define SCA3300_VALUE_RS_ERROR 0x3 +#define SCA3300_MASK_RS_STATUS GENMASK(1, 0) + +enum sca3300_scan_indexes { + SCA3300_ACC_X = 0, + SCA3300_ACC_Y, + SCA3300_ACC_Z, + SCA3300_TEMP, + SCA3300_TIMESTAMP, +}; + +#define SCA3300_ACCEL_CHANNEL(index, reg, axis) { \ + .type = IIO_ACCEL, \ + .address = reg, \ + .modified = 1, \ + .channel2 = IIO_MOD_##axis, \ + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \ + .info_mask_shared_by_type = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .info_mask_shared_by_type_available = \ + BIT(IIO_CHAN_INFO_SCALE) | \ + BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \ + .scan_index = index, \ + .scan_type = { \ + .sign = 's', \ + .realbits = 16, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +static const struct iio_chan_spec sca3300_channels[] = { + SCA3300_ACCEL_CHANNEL(SCA3300_ACC_X, 0x1, X), + SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Y, 0x2, Y), + SCA3300_ACCEL_CHANNEL(SCA3300_ACC_Z, 0x3, Z), + { + .type = IIO_TEMP, + .address = 0x5, + .scan_index = SCA3300_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .scan_type = { + .sign = 's', + .realbits = 16, + .storagebits = 16, + .endianness = IIO_CPU, + }, + }, + IIO_CHAN_SOFT_TIMESTAMP(4), +}; + +static const int sca3300_lp_freq[] = {70, 70, 70, 10}; +static const int sca3300_accel_scale[][2] = {{0, 370}, {0, 741}, {0, 185}, {0, 185}}; + +static const unsigned long sca3300_scan_masks[] = { + BIT(SCA3300_ACC_X) | BIT(SCA3300_ACC_Y) | BIT(SCA3300_ACC_Z) | + BIT(SCA3300_TEMP), + 0 +}; + +/** + * struct sca3300_data - device data + * @spi: SPI device structure + * @lock: Data buffer lock + * @scan: Triggered buffer. Four channel 16-bit data + 64-bit timestamp + * @txbuf: Transmit buffer + * @rxbuf: Receive buffer + */ +struct sca3300_data { + struct spi_device *spi; + struct mutex lock; + struct { + s16 channels[4]; + s64 ts __aligned(sizeof(s64)); + } scan; + u8 txbuf[4] ____cacheline_aligned; + u8 rxbuf[4]; +}; + +DECLARE_CRC8_TABLE(sca3300_crc_table); + +static int sca3300_transfer(struct sca3300_data *sca_data, int *val) +{ + /* Consecutive requests min. 10 us delay (Datasheet section 5.1.2) */ + struct spi_delay delay = { .value = 10, .unit = SPI_DELAY_UNIT_USECS }; + int32_t ret; + int rs; + u8 crc; + struct spi_transfer xfers[2] = { + { + .tx_buf = sca_data->txbuf, + .len = ARRAY_SIZE(sca_data->txbuf), + .delay = delay, + .cs_change = 1, + }, + { + .rx_buf = sca_data->rxbuf, + .len = ARRAY_SIZE(sca_data->rxbuf), + .delay = delay, + } + }; + + /* inverted crc value as described in device data sheet */ + crc = ~crc8(sca3300_crc_table, &sca_data->txbuf[0], 3, CRC8_INIT_VALUE); + sca_data->txbuf[3] = crc; + + ret = spi_sync_transfer(sca_data->spi, xfers, ARRAY_SIZE(xfers)); + if (ret) { + dev_err(&sca_data->spi->dev, + "transfer error, error: %d\n", ret); + return -EIO; + } + + crc = ~crc8(sca3300_crc_table, &sca_data->rxbuf[0], 3, CRC8_INIT_VALUE); + if (sca_data->rxbuf[3] != crc) { + dev_err(&sca_data->spi->dev, "CRC checksum mismatch"); + return -EIO; + } + + /* get return status */ + rs = sca_data->rxbuf[0] & SCA3300_MASK_RS_STATUS; + if (rs == SCA3300_VALUE_RS_ERROR) + ret = -EINVAL; + + *val = sign_extend32(get_unaligned_be16(&sca_data->rxbuf[1]), 15); + + return ret; +} + +static int sca3300_error_handler(struct sca3300_data *sca_data) +{ + int ret; + int val; + + mutex_lock(&sca_data->lock); + sca_data->txbuf[0] = SCA3300_REG_STATUS << 2; + ret = sca3300_transfer(sca_data, &val); + mutex_unlock(&sca_data->lock); + /* + * Return status error is cleared after reading status register once, + * expect EINVAL here. + */ + if (ret != -EINVAL) { + dev_err(&sca_data->spi->dev, + "error reading device status: %d\n", ret); + return ret; + } + + dev_err(&sca_data->spi->dev, "device status: 0x%lx\n", + val & SCA3300_STATUS_MASK); + + return 0; +} + +static int sca3300_read_reg(struct sca3300_data *sca_data, u8 reg, int *val) +{ + int ret; + + mutex_lock(&sca_data->lock); + sca_data->txbuf[0] = reg << 2; + ret = sca3300_transfer(sca_data, val); + mutex_unlock(&sca_data->lock); + if (ret != -EINVAL) + return ret; + + return sca3300_error_handler(sca_data); +} + +static int sca3300_write_reg(struct sca3300_data *sca_data, u8 reg, int val) +{ + int reg_val = 0; + int ret; + + mutex_lock(&sca_data->lock); + /* BIT(7) for write operation */ + sca_data->txbuf[0] = BIT(7) | (reg << 2); + put_unaligned_be16(val, &sca_data->txbuf[1]); + ret = sca3300_transfer(sca_data, ®_val); + mutex_unlock(&sca_data->lock); + if (ret != -EINVAL) + return ret; + + return sca3300_error_handler(sca_data); +} + +static int sca3300_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct sca3300_data *data = iio_priv(indio_dev); + int reg_val; + int ret; + int i; + + switch (mask) { + case IIO_CHAN_INFO_SCALE: + if (val) + return -EINVAL; + + for (i = 0; i < ARRAY_SIZE(sca3300_accel_scale); i++) { + if (val2 == sca3300_accel_scale[i][1]) + return sca3300_write_reg(data, SCA3300_REG_MODE, i); + } + return -EINVAL; + + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + ret = sca3300_read_reg(data, SCA3300_REG_MODE, ®_val); + if (ret) + return ret; + /* freq. change is possible only for mode 3 and 4 */ + if (reg_val == 2 && val == sca3300_lp_freq[3]) + return sca3300_write_reg(data, SCA3300_REG_MODE, 3); + if (reg_val == 3 && val == sca3300_lp_freq[2]) + return sca3300_write_reg(data, SCA3300_REG_MODE, 2); + return -EINVAL; + default: + return -EINVAL; + } +} + +static int sca3300_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct sca3300_data *data = iio_priv(indio_dev); + int ret; + int reg_val; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = sca3300_read_reg(data, chan->address, val); + if (ret) + return ret; + return IIO_VAL_INT; + case IIO_CHAN_INFO_SCALE: + ret = sca3300_read_reg(data, SCA3300_REG_MODE, ®_val); + if (ret) + return ret; + *val = 0; + *val2 = sca3300_accel_scale[reg_val][1]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + ret = sca3300_read_reg(data, SCA3300_REG_MODE, ®_val); + if (ret) + return ret; + *val = sca3300_lp_freq[reg_val]; + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static irqreturn_t sca3300_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct sca3300_data *data = iio_priv(indio_dev); + int bit, ret, val, i = 0; + + for_each_set_bit(bit, indio_dev->active_scan_mask, + indio_dev->masklength) { + ret = sca3300_read_reg(data, sca3300_channels[bit].address, + &val); + if (ret) { + dev_err_ratelimited(&data->spi->dev, + "failed to read register, error: %d\n", ret); + /* handled, but bailing out due to errors */ + goto out; + } + data->scan.channels[i++] = val; + } + + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, + iio_get_time_ns(indio_dev)); +out: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +/* + * sca3300_init - Device init sequence. See datasheet rev 2 section + * 4.2 Start-Up Sequence for details. + */ +static int sca3300_init(struct sca3300_data *sca_data, + struct iio_dev *indio_dev) +{ + int value = 0; + int ret; + + ret = sca3300_write_reg(sca_data, SCA3300_REG_MODE, + SCA3300_MODE_SW_RESET); + if (ret) + return ret; + + /* + * Wait 1ms after SW-reset command. + * Wait 15ms for settling of signal paths. + */ + usleep_range(16e3, 50e3); + + ret = sca3300_read_reg(sca_data, SCA3300_REG_WHOAMI, &value); + if (ret) + return ret; + + if (value != SCA3300_WHOAMI_ID) { + dev_err(&sca_data->spi->dev, + "device id not expected value, %d != %u\n", + value, SCA3300_WHOAMI_ID); + return -ENODEV; + } + return 0; +} + +static int sca3300_debugfs_reg_access(struct iio_dev *indio_dev, + unsigned int reg, unsigned int writeval, + unsigned int *readval) +{ + struct sca3300_data *data = iio_priv(indio_dev); + int value; + int ret; + + if (reg > SCA3300_REG_SELBANK) + return -EINVAL; + + if (!readval) + return sca3300_write_reg(data, reg, writeval); + + ret = sca3300_read_reg(data, reg, &value); + if (ret) + return ret; + + *readval = value; + + return 0; +} + +static int sca3300_read_avail(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_SCALE: + *vals = (const int *)sca3300_accel_scale; + *length = ARRAY_SIZE(sca3300_accel_scale) * 2 - 2; + *type = IIO_VAL_INT_PLUS_MICRO; + return IIO_AVAIL_LIST; + case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: + *vals = &sca3300_lp_freq[2]; + *length = 2; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static const struct iio_info sca3300_info = { + .read_raw = sca3300_read_raw, + .write_raw = sca3300_write_raw, + .debugfs_reg_access = &sca3300_debugfs_reg_access, + .read_avail = sca3300_read_avail, +}; + +static int sca3300_probe(struct spi_device *spi) +{ + struct sca3300_data *sca_data; + struct iio_dev *indio_dev; + int ret; + + indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*sca_data)); + if (!indio_dev) + return -ENOMEM; + + sca_data = iio_priv(indio_dev); + mutex_init(&sca_data->lock); + sca_data->spi = spi; + + crc8_populate_msb(sca3300_crc_table, SCA3300_CRC8_POLYNOMIAL); + + indio_dev->info = &sca3300_info; + indio_dev->name = SCA3300_ALIAS; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->channels = sca3300_channels; + indio_dev->num_channels = ARRAY_SIZE(sca3300_channels); + indio_dev->available_scan_masks = sca3300_scan_masks; + + ret = sca3300_init(sca_data, indio_dev); + if (ret) { + dev_err(&spi->dev, "failed to init device, error: %d\n", ret); + return ret; + } + + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, + iio_pollfunc_store_time, + sca3300_trigger_handler, NULL); + if (ret) { + dev_err(&spi->dev, + "iio triggered buffer setup failed, error: %d\n", ret); + return ret; + } + + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) { + dev_err(&spi->dev, "iio device register failed, error: %d\n", + ret); + } + + return ret; +} + +static const struct of_device_id sca3300_dt_ids[] = { + { .compatible = "murata,sca3300"}, + {} +}; +MODULE_DEVICE_TABLE(of, sca3300_dt_ids); + +static struct spi_driver sca3300_driver = { + .driver = { + .name = SCA3300_ALIAS, + .of_match_table = sca3300_dt_ids, + }, + .probe = sca3300_probe, +}; +module_spi_driver(sca3300_driver); + +MODULE_AUTHOR("Tomas Melin <tomas.melin@vaisala.com>"); +MODULE_DESCRIPTION("Murata SCA3300 SPI Accelerometer"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h index 5d356288e001..f5b0b8bbaff7 100644 --- a/drivers/iio/accel/st_accel.h +++ b/drivers/iio/accel/st_accel.h @@ -62,18 +62,6 @@ enum st_accel_type { #define LIS2DE12_ACCEL_DEV_NAME "lis2de12" #define LIS2HH12_ACCEL_DEV_NAME "lis2hh12" -/** -* struct st_sensors_platform_data - default accel platform data -* @drdy_int_pin: default accel DRDY is available on INT1 pin. -*/ -static __maybe_unused const struct st_sensors_platform_data default_accel_pdata = { - .drdy_int_pin = 1, -}; - -const struct st_sensor_settings *st_accel_get_settings(const char *name); -int st_accel_common_probe(struct iio_dev *indio_dev); -void st_accel_common_remove(struct iio_dev *indio_dev); - #ifdef CONFIG_IIO_BUFFER int st_accel_allocate_ring(struct iio_dev *indio_dev); void st_accel_deallocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c index 43c50167d220..28fceac9f2f6 100644 --- a/drivers/iio/accel/st_accel_core.c +++ b/drivers/iio/accel/st_accel_core.c @@ -41,51 +41,74 @@ #define ST_ACCEL_FS_AVL_200G 200 #define ST_ACCEL_FS_AVL_400G 400 +static const struct iio_mount_matrix * +st_accel_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct st_sensor_data *adata = iio_priv(indio_dev); + + return &adata->mount_matrix; +} + +static const struct iio_chan_spec_ext_info st_accel_mount_matrix_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_accel_get_mount_matrix), + { } +}; + static const struct iio_chan_spec st_accel_8bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 8, 8, - ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR+1, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 8, 8, - ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR+1, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 8, 8, - ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1), + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR+1, + st_accel_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_accel_12bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 12, 16, - ST_ACCEL_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 12, 16, - ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 12, 16, - ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR, + st_accel_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_accel_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, - ST_ACCEL_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_X_L_ADDR, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, - ST_ACCEL_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ACCEL, + ST_ACCEL_DEFAULT_OUT_Y_L_ADDR, + st_accel_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ACCEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, - ST_ACCEL_DEFAULT_OUT_Z_L_ADDR), + ST_ACCEL_DEFAULT_OUT_Z_L_ADDR, + st_accel_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -980,7 +1003,99 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = { .multi_read_bit = true, .bootime = 2, }, + { + .wai = 0x49, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LSM9DS0_IMU_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_accel_16bit_channels, + .odr = { + .addr = 0x20, + .mask = GENMASK(7, 4), + .odr_avl = { + { 3, 0x01, }, + { 6, 0x02, }, + { 12, 0x03, }, + { 25, 0x04, }, + { 50, 0x05, }, + { 100, 0x06, }, + { 200, 0x07, }, + { 400, 0x08, }, + { 800, 0x09, }, + { 1600, 0x0a, }, + }, + }, + .pw = { + .addr = 0x20, + .mask = GENMASK(7, 4), + .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE, + }, + .enable_axis = { + .addr = ST_SENSORS_DEFAULT_AXIS_ADDR, + .mask = ST_SENSORS_DEFAULT_AXIS_MASK, + }, + .fs = { + .addr = 0x21, + .mask = GENMASK(5, 3), + .fs_avl = { + [0] = { + .num = ST_ACCEL_FS_AVL_2G, + .value = 0x00, + .gain = IIO_G_TO_M_S_2(61), + }, + [1] = { + .num = ST_ACCEL_FS_AVL_4G, + .value = 0x01, + .gain = IIO_G_TO_M_S_2(122), + }, + [2] = { + .num = ST_ACCEL_FS_AVL_6G, + .value = 0x02, + .gain = IIO_G_TO_M_S_2(183), + }, + [3] = { + .num = ST_ACCEL_FS_AVL_8G, + .value = 0x03, + .gain = IIO_G_TO_M_S_2(244), + }, + [4] = { + .num = ST_ACCEL_FS_AVL_16G, + .value = 0x04, + .gain = IIO_G_TO_M_S_2(732), + }, + }, + }, + .bdu = { + .addr = 0x20, + .mask = BIT(3), + }, + .drdy_irq = { + .int1 = { + .addr = 0x22, + .mask = BIT(2), + }, + .int2 = { + .addr = 0x23, + .mask = BIT(3), + }, + .stat_drdy = { + .addr = ST_SENSORS_DEFAULT_STAT_ADDR, + .mask = GENMASK(2, 0), + }, + }, + .sim = { + .addr = 0x21, + .value = BIT(0), + }, + .multi_read_bit = true, + .bootime = 2, + }, +}; +/* Default accel DRDY is available on INT1 pin */ +static const struct st_sensors_platform_data default_accel_pdata = { + .drdy_int_pin = 1, }; static int st_accel_read_raw(struct iio_dev *indio_dev, @@ -1070,25 +1185,10 @@ static const struct iio_trigger_ops st_accel_trigger_ops = { #endif #ifdef CONFIG_ACPI -static const struct iio_mount_matrix * -get_mount_matrix(const struct iio_dev *indio_dev, - const struct iio_chan_spec *chan) -{ - struct st_sensor_data *adata = iio_priv(indio_dev); - - return adata->mount_matrix; -} - -static const struct iio_chan_spec_ext_info mount_matrix_ext_info[] = { - IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, get_mount_matrix), - { }, -}; - /* Read ST-specific _ONT orientation data from ACPI and generate an * appropriate mount matrix. */ -static int apply_acpi_orientation(struct iio_dev *indio_dev, - struct iio_chan_spec *channels) +static int apply_acpi_orientation(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; @@ -1177,14 +1277,6 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev, } /* Convert our integer matrix to a string-based iio_mount_matrix */ - adata->mount_matrix = devm_kmalloc(&indio_dev->dev, - sizeof(*adata->mount_matrix), - GFP_KERNEL); - if (!adata->mount_matrix) { - ret = -ENOMEM; - goto out; - } - for (i = 0; i < 3; i++) { for (j = 0; j < 3; j++) { int matrix_val = final_ont[i][j]; @@ -1203,26 +1295,25 @@ static int apply_acpi_orientation(struct iio_dev *indio_dev, default: goto out; } - adata->mount_matrix->rotation[i * 3 + j] = str_value; + adata->mount_matrix.rotation[i * 3 + j] = str_value; } } - /* Expose the mount matrix via ext_info */ - for (i = 0; i < indio_dev->num_channels; i++) - channels[i].ext_info = mount_matrix_ext_info; - ret = 0; dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n"); out: kfree(buffer.pointer); + if (ret) + dev_dbg(&indio_dev->dev, + "failed to apply ACPI orientation data: %d\n", ret); + return ret; } #else /* !CONFIG_ACPI */ -static int apply_acpi_orientation(struct iio_dev *indio_dev, - struct iio_chan_spec *channels) +static int apply_acpi_orientation(struct iio_dev *indio_dev) { - return 0; + return -EINVAL; } #endif @@ -1248,38 +1339,30 @@ int st_accel_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); struct st_sensors_platform_data *pdata = dev_get_platdata(adata->dev); - struct iio_chan_spec *channels; - size_t channels_size; int err; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &accel_info; - err = st_sensors_power_enable(indio_dev); - if (err) - return err; - err = st_sensors_verify_id(indio_dev); if (err < 0) - goto st_accel_power_off; + return err; adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS; + indio_dev->channels = adata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; - channels_size = indio_dev->num_channels * sizeof(struct iio_chan_spec); - channels = devm_kmemdup(&indio_dev->dev, - adata->sensor_settings->ch, - channels_size, GFP_KERNEL); - if (!channels) { - err = -ENOMEM; - goto st_accel_power_off; + /* + * First try specific ACPI methods to retrieve orientation then try the + * generic function. + */ + err = apply_acpi_orientation(indio_dev); + if (err) { + err = iio_read_mount_matrix(adata->dev, &adata->mount_matrix); + if (err) + return err; } - if (apply_acpi_orientation(indio_dev, channels)) - dev_warn(&indio_dev->dev, - "failed to apply ACPI orientation data: %d\n", err); - - indio_dev->channels = channels; adata->current_fullscale = &adata->sensor_settings->fs.fs_avl[0]; adata->odr = adata->sensor_settings->odr.odr_avl[0].hz; @@ -1288,11 +1371,11 @@ int st_accel_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) - goto st_accel_power_off; + return err; err = st_accel_allocate_ring(indio_dev); if (err < 0) - goto st_accel_power_off; + return err; if (adata->irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -1315,9 +1398,6 @@ st_accel_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_accel_probe_trigger_error: st_accel_deallocate_ring(indio_dev); -st_accel_power_off: - st_sensors_power_disable(indio_dev); - return err; } EXPORT_SYMBOL(st_accel_common_probe); @@ -1326,8 +1406,6 @@ void st_accel_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *adata = iio_priv(indio_dev); - st_sensors_power_disable(indio_dev); - iio_device_unregister(indio_dev); if (adata->irq > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 360e16f2cadb..95e305b88d5e 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -174,16 +174,29 @@ static int st_accel_i2c_probe(struct i2c_client *client) if (ret < 0) return ret; + ret = st_sensors_power_enable(indio_dev); + if (ret) + return ret; + ret = st_accel_common_probe(indio_dev); if (ret < 0) - return ret; + goto st_accel_power_off; return 0; + +st_accel_power_off: + st_sensors_power_disable(indio_dev); + + return ret; } static int st_accel_i2c_remove(struct i2c_client *client) { - st_accel_common_remove(i2c_get_clientdata(client)); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + st_sensors_power_disable(indio_dev); + + st_accel_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c index 568ff1bae0ee..83d3308ce5cc 100644 --- a/drivers/iio/accel/st_accel_spi.c +++ b/drivers/iio/accel/st_accel_spi.c @@ -123,16 +123,29 @@ static int st_accel_spi_probe(struct spi_device *spi) if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_accel_common_probe(indio_dev); if (err < 0) - return err; + goto st_accel_power_off; return 0; + +st_accel_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_accel_spi_remove(struct spi_device *spi) { - st_accel_common_remove(spi_get_drvdata(spi)); + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + st_sensors_power_disable(indio_dev); + + st_accel_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/accel/stk8312.c b/drivers/iio/accel/stk8312.c index 157d8faefb9e..43c621d0f11e 100644 --- a/drivers/iio/accel/stk8312.c +++ b/drivers/iio/accel/stk8312.c @@ -7,7 +7,6 @@ * IIO driver for STK8312; 7-bit I2C address: 0x3D. */ -#include <linux/acpi.h> #include <linux/i2c.h> #include <linux/interrupt.h> #include <linux/kernel.h> @@ -103,7 +102,11 @@ struct stk8312_data { u8 mode; struct iio_trigger *dready_trig; bool dready_trigger_on; - s8 buffer[16]; /* 3x8-bit channels + 5x8 padding + 64-bit timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + s8 chans[3]; + s64 timestamp __aligned(8); + } scan; }; static IIO_CONST_ATTR(in_accel_scale_available, STK8312_SCALE_AVAIL); @@ -438,7 +441,7 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p) ret = i2c_smbus_read_i2c_block_data(data->client, STK8312_REG_XOUT, STK8312_ALL_CHANNEL_SIZE, - data->buffer); + data->scan.chans); if (ret < STK8312_ALL_CHANNEL_SIZE) { dev_err(&data->client->dev, "register read failed\n"); mutex_unlock(&data->lock); @@ -452,12 +455,12 @@ static irqreturn_t stk8312_trigger_handler(int irq, void *p) mutex_unlock(&data->lock); goto err; } - data->buffer[i++] = ret; + data->scan.chans[i++] = ret; } } mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -552,7 +555,7 @@ static int stk8312_probe(struct i2c_client *client, data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) { ret = -ENOMEM; goto err_power_off; @@ -635,23 +638,17 @@ static SIMPLE_DEV_PM_OPS(stk8312_pm_ops, stk8312_suspend, stk8312_resume); #endif static const struct i2c_device_id stk8312_i2c_id[] = { - {"STK8312", 0}, + /* Deprecated in favour of lowercase form */ + { "STK8312", 0 }, + { "stk8312", 0 }, {} }; MODULE_DEVICE_TABLE(i2c, stk8312_i2c_id); -static const struct acpi_device_id stk8312_acpi_id[] = { - {"STK8312", 0}, - {} -}; - -MODULE_DEVICE_TABLE(acpi, stk8312_acpi_id); - static struct i2c_driver stk8312_driver = { .driver = { .name = STK8312_DRIVER_NAME, .pm = STK8312_PM_OPS, - .acpi_match_table = ACPI_PTR(stk8312_acpi_id), }, .probe = stk8312_probe, .remove = stk8312_remove, diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c index 7cf9cb7e8666..e137a34b5c9a 100644 --- a/drivers/iio/accel/stk8ba50.c +++ b/drivers/iio/accel/stk8ba50.c @@ -91,12 +91,11 @@ struct stk8ba50_data { u8 sample_rate_idx; struct iio_trigger *dready_trig; bool dready_trigger_on; - /* - * 3 x 16-bit channels (10-bit data, 6-bit padding) + - * 1 x 16 padding + - * 4 x 16 64-bit timestamp - */ - s16 buffer[8]; + /* Ensure timestamp is naturally aligned */ + struct { + s16 chans[3]; + s64 timetamp __aligned(8); + } scan; }; #define STK8BA50_ACCEL_CHANNEL(index, reg, axis) { \ @@ -324,7 +323,7 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p) ret = i2c_smbus_read_i2c_block_data(data->client, STK8BA50_REG_XOUT, STK8BA50_ALL_CHANNEL_SIZE, - (u8 *)data->buffer); + (u8 *)data->scan.chans); if (ret < STK8BA50_ALL_CHANNEL_SIZE) { dev_err(&data->client->dev, "register read failed\n"); goto err; @@ -337,10 +336,10 @@ static irqreturn_t stk8ba50_trigger_handler(int irq, void *p) if (ret < 0) goto err; - data->buffer[i++] = ret; + data->scan.chans[i++] = ret; } } - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: mutex_unlock(&data->lock); @@ -448,7 +447,7 @@ static int stk8ba50_probe(struct i2c_client *client, data->dready_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) { ret = -ENOMEM; goto err_power_off; diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index c7946c439612..db0c8fb60515 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -1190,6 +1190,18 @@ config TI_TLC4541 This driver can also be built as a module. If so, the module will be called ti-tlc4541. +config TI_TSC2046 + tristate "Texas Instruments TSC2046 ADC driver" + depends on SPI + select IIO_BUFFER + select IIO_TRIGGERED_BUFFER + help + Say yes here to build support for ADC functionality of Texas + Instruments TSC2046 touch screen controller. + + This driver can also be built as a module. If so, the module will be + called ti-tsc2046. + config TWL4030_MADC tristate "TWL4030 MADC (Monitoring A/D Converter)" depends on TWL4030_CORE diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile index a226657d19c0..f70d877c555a 100644 --- a/drivers/iio/adc/Makefile +++ b/drivers/iio/adc/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o obj-$(CONFIG_TI_ADS131E08) += ti-ads131e08.o obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o +obj-$(CONFIG_TI_TSC2046) += ti-tsc2046.o obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o obj-$(CONFIG_VF610_ADC) += vf610_adc.o diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index a27db78ea13e..e45c600fccc0 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -862,6 +862,11 @@ static void ad7124_reg_disable(void *r) regulator_disable(r); } +static void ad7124_clk_disable(void *c) +{ + clk_disable_unprepare(c); +} + static int ad7124_probe(struct spi_device *spi) { const struct ad7124_chip_info *info; @@ -883,8 +888,6 @@ static int ad7124_probe(struct spi_device *spi) ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info); - spi_set_drvdata(spi, indio_dev); - indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &ad7124_info; @@ -922,48 +925,28 @@ static int ad7124_probe(struct spi_device *spi) if (ret < 0) return ret; + ret = devm_add_action_or_reset(&spi->dev, ad7124_clk_disable, st->mclk); + if (ret) + return ret; + ret = ad7124_soft_reset(st); if (ret < 0) - goto error_clk_disable_unprepare; + return ret; ret = ad7124_check_chip_id(st); if (ret) - goto error_clk_disable_unprepare; + return ret; ret = ad7124_setup(st); if (ret < 0) - goto error_clk_disable_unprepare; + return ret; - ret = ad_sd_setup_buffer_and_trigger(indio_dev); + ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); if (ret < 0) - goto error_clk_disable_unprepare; - - ret = iio_device_register(indio_dev); - if (ret < 0) { - dev_err(&spi->dev, "Failed to register iio device\n"); - goto error_remove_trigger; - } - - return 0; - -error_remove_trigger: - ad_sd_cleanup_buffer_and_trigger(indio_dev); -error_clk_disable_unprepare: - clk_disable_unprepare(st->mclk); - - return ret; -} - -static int ad7124_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7124_state *st = iio_priv(indio_dev); + return ret; - iio_device_unregister(indio_dev); - ad_sd_cleanup_buffer_and_trigger(indio_dev); - clk_disable_unprepare(st->mclk); + return devm_iio_device_register(&spi->dev, indio_dev); - return 0; } static const struct of_device_id ad7124_of_match[] = { @@ -981,7 +964,6 @@ static struct spi_driver ad71124_driver = { .of_match_table = ad7124_of_match, }, .probe = ad7124_probe, - .remove = ad7124_remove, }; module_spi_driver(ad71124_driver); diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 1141cc13a124..ee8ed9481025 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -326,7 +326,7 @@ static int ad7192_of_clock_select(struct ad7192_state *st) clock_sel = AD7192_CLK_INT; /* use internal clock */ - if (PTR_ERR(st->mclk) == -ENOENT) { + if (st->mclk) { if (of_property_read_bool(np, "adi,int-clock-output-enable")) clock_sel = AD7192_CLK_INT_CO; } else { @@ -908,6 +908,16 @@ static int ad7192_channels_config(struct iio_dev *indio_dev) return 0; } +static void ad7192_reg_disable(void *reg) +{ + regulator_disable(reg); +} + +static void ad7192_clk_disable(void *clk) +{ + clk_disable_unprepare(clk); +} + static int ad7192_probe(struct spi_device *spi) { struct ad7192_state *st; @@ -937,33 +947,38 @@ static int ad7192_probe(struct spi_device *spi) return ret; } + ret = devm_add_action_or_reset(&spi->dev, ad7192_reg_disable, st->avdd); + if (ret) + return ret; + st->dvdd = devm_regulator_get(&spi->dev, "dvdd"); - if (IS_ERR(st->dvdd)) { - ret = PTR_ERR(st->dvdd); - goto error_disable_avdd; - } + if (IS_ERR(st->dvdd)) + return PTR_ERR(st->dvdd); ret = regulator_enable(st->dvdd); if (ret) { dev_err(&spi->dev, "Failed to enable specified DVdd supply\n"); - goto error_disable_avdd; + return ret; } + ret = devm_add_action_or_reset(&spi->dev, ad7192_reg_disable, st->dvdd); + if (ret) + return ret; + ret = regulator_get_voltage(st->avdd); if (ret < 0) { dev_err(&spi->dev, "Device tree error, reference voltage undefined\n"); - goto error_disable_avdd; + return ret; } st->int_vref_mv = ret / 1000; - spi_set_drvdata(spi, indio_dev); st->chip_info = of_device_get_match_data(&spi->dev); indio_dev->name = st->chip_info->name; indio_dev->modes = INDIO_DIRECT_MODE; ret = ad7192_channels_config(indio_dev); if (ret < 0) - goto error_disable_dvdd; + return ret; if (st->chip_info->chip_id == CHIPID_AD7195) indio_dev->info = &ad7195_info; @@ -972,17 +987,15 @@ static int ad7192_probe(struct spi_device *spi) ad_sd_init(&st->sd, indio_dev, spi, &ad7192_sigma_delta_info); - ret = ad_sd_setup_buffer_and_trigger(indio_dev); + ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); if (ret) - goto error_disable_dvdd; + return ret; st->fclk = AD7192_INT_FREQ_MHZ; - st->mclk = devm_clk_get(&st->sd.spi->dev, "mclk"); - if (IS_ERR(st->mclk) && PTR_ERR(st->mclk) != -ENOENT) { - ret = PTR_ERR(st->mclk); - goto error_remove_trigger; - } + st->mclk = devm_clk_get_optional(&spi->dev, "mclk"); + if (IS_ERR(st->mclk)) + return PTR_ERR(st->mclk); st->clock_sel = ad7192_of_clock_select(st); @@ -990,55 +1003,26 @@ static int ad7192_probe(struct spi_device *spi) st->clock_sel == AD7192_CLK_EXT_MCLK2) { ret = clk_prepare_enable(st->mclk); if (ret < 0) - goto error_remove_trigger; + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7192_clk_disable, + st->mclk); + if (ret) + return ret; st->fclk = clk_get_rate(st->mclk); if (!ad7192_valid_external_frequency(st->fclk)) { - ret = -EINVAL; dev_err(&spi->dev, "External clock frequency out of bounds\n"); - goto error_disable_clk; + return -EINVAL; } } ret = ad7192_setup(st, spi->dev.of_node); if (ret) - goto error_disable_clk; - - ret = iio_device_register(indio_dev); - if (ret < 0) - goto error_disable_clk; - return 0; - -error_disable_clk: - if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || - st->clock_sel == AD7192_CLK_EXT_MCLK2) - clk_disable_unprepare(st->mclk); -error_remove_trigger: - ad_sd_cleanup_buffer_and_trigger(indio_dev); -error_disable_dvdd: - regulator_disable(st->dvdd); -error_disable_avdd: - regulator_disable(st->avdd); - - return ret; -} - -static int ad7192_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7192_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - if (st->clock_sel == AD7192_CLK_EXT_MCLK1_2 || - st->clock_sel == AD7192_CLK_EXT_MCLK2) - clk_disable_unprepare(st->mclk); - ad_sd_cleanup_buffer_and_trigger(indio_dev); - - regulator_disable(st->dvdd); - regulator_disable(st->avdd); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id ad7192_of_match[] = { @@ -1056,7 +1040,6 @@ static struct spi_driver ad7192_driver = { .of_match_table = ad7192_of_match, }, .probe = ad7192_probe, - .remove = ad7192_remove, }; module_spi_driver(ad7192_driver); diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c index d2163cb62f4f..3f4e73f7d35a 100644 --- a/drivers/iio/adc/ad7298.c +++ b/drivers/iio/adc/ad7298.c @@ -13,6 +13,7 @@ #include <linux/regulator/consumer.h> #include <linux/err.h> #include <linux/delay.h> +#include <linux/mod_devicetable.h> #include <linux/module.h> #include <linux/interrupt.h> #include <linux/bitops.h> @@ -346,6 +347,12 @@ static int ad7298_probe(struct spi_device *spi) return devm_iio_device_register(&spi->dev, indio_dev); } +static const struct acpi_device_id ad7298_acpi_ids[] = { + { "INT3494", 0 }, + { } +}; +MODULE_DEVICE_TABLE(acpi, ad7298_acpi_ids); + static const struct spi_device_id ad7298_id[] = { {"ad7298", 0}, {} @@ -355,6 +362,7 @@ MODULE_DEVICE_TABLE(spi, ad7298_id); static struct spi_driver ad7298_driver = { .driver = { .name = "ad7298", + .acpi_match_table = ad7298_acpi_ids, }, .probe = ad7298_probe, .id_table = ad7298_id, diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 9e9ff07cf972..a1e8b32671cf 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -32,12 +32,14 @@ struct ad7476_chip_info { /* channels used when convst gpio is defined */ struct iio_chan_spec convst_channel[2]; void (*reset)(struct ad7476_state *); + bool has_vref; + bool has_vdrive; }; struct ad7476_state { struct spi_device *spi; const struct ad7476_chip_info *chip_info; - struct regulator *reg; + struct regulator *ref_reg; struct gpio_desc *convst_gpio; struct spi_transfer xfer; struct spi_message msg; @@ -52,13 +54,17 @@ struct ad7476_state { }; enum ad7476_supported_device_ids { + ID_AD7091, ID_AD7091R, + ID_AD7273, + ID_AD7274, ID_AD7276, ID_AD7277, ID_AD7278, ID_AD7466, ID_AD7467, ID_AD7468, + ID_AD7475, ID_AD7495, ID_AD7940, ID_ADC081S, @@ -145,8 +151,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, GENMASK(st->chip_info->channel[0].scan_type.realbits - 1, 0); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - if (!st->chip_info->int_vref_uv) { - scale_uv = regulator_get_voltage(st->reg); + if (st->ref_reg) { + scale_uv = regulator_get_voltage(st->ref_reg); if (scale_uv < 0) return scale_uv; } else { @@ -187,13 +193,32 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, BIT(IIO_CHAN_INFO_RAW)) static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { + [ID_AD7091] = { + .channel[0] = AD7091R_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .convst_channel[0] = AD7091R_CONVST_CHAN(12), + .convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .reset = ad7091_reset, + }, [ID_AD7091R] = { .channel[0] = AD7091R_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), .convst_channel[0] = AD7091R_CONVST_CHAN(12), .convst_channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .int_vref_uv = 2500000, + .has_vref = true, .reset = ad7091_reset, }, + [ID_AD7273] = { + .channel[0] = AD7940_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .has_vref = true, + }, + [ID_AD7274] = { + .channel[0] = AD7940_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .has_vref = true, + }, [ID_AD7276] = { .channel[0] = AD7940_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), @@ -218,10 +243,17 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { .channel[0] = AD7476_CHAN(8), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, + [ID_AD7475] = { + .channel[0] = AD7476_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .has_vref = true, + .has_vdrive = true, + }, [ID_AD7495] = { .channel[0] = AD7476_CHAN(12), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), .int_vref_uv = 2500000, + .has_vdrive = true, }, [ID_AD7940] = { .channel[0] = AD7940_CHAN(14), @@ -254,6 +286,7 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { [ID_LTC2314_14] = { .channel[0] = AD7940_CHAN(14), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + .has_vref = true, }, }; @@ -263,15 +296,16 @@ static const struct iio_info ad7476_info = { static void ad7476_reg_disable(void *data) { - struct ad7476_state *st = data; + struct regulator *reg = data; - regulator_disable(st->reg); + regulator_disable(reg); } static int ad7476_probe(struct spi_device *spi) { struct ad7476_state *st; struct iio_dev *indio_dev; + struct regulator *reg; int ret; indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st)); @@ -282,27 +316,79 @@ static int ad7476_probe(struct spi_device *spi) st->chip_info = &ad7476_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - st->reg = devm_regulator_get(&spi->dev, "vcc"); - if (IS_ERR(st->reg)) - return PTR_ERR(st->reg); + reg = devm_regulator_get(&spi->dev, "vcc"); + if (IS_ERR(reg)) + return PTR_ERR(reg); - ret = regulator_enable(st->reg); + ret = regulator_enable(reg); if (ret) return ret; - ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, - st); + ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, reg); if (ret) return ret; + /* Either vcc or vref (below) as appropriate */ + if (!st->chip_info->int_vref_uv) + st->ref_reg = reg; + + if (st->chip_info->has_vref) { + + /* If a device has an internal reference vref is optional */ + if (st->chip_info->int_vref_uv) { + reg = devm_regulator_get_optional(&spi->dev, "vref"); + if (IS_ERR(reg) && (PTR_ERR(reg) != -ENODEV)) + return PTR_ERR(reg); + } else { + reg = devm_regulator_get(&spi->dev, "vref"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + } + + if (!IS_ERR(reg)) { + ret = regulator_enable(reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, + ad7476_reg_disable, + reg); + if (ret) + return ret; + st->ref_reg = reg; + } else { + /* + * Can only get here if device supports both internal + * and external reference, but the regulator connected + * to the external reference is not connected. + * Set the reference regulator pointer to NULL to + * indicate this. + */ + st->ref_reg = NULL; + } + } + + if (st->chip_info->has_vdrive) { + reg = devm_regulator_get(&spi->dev, "vdrive"); + if (IS_ERR(reg)) + return PTR_ERR(reg); + + ret = regulator_enable(reg); + if (ret) + return ret; + + ret = devm_add_action_or_reset(&spi->dev, ad7476_reg_disable, + reg); + if (ret) + return ret; + } + st->convst_gpio = devm_gpiod_get_optional(&spi->dev, "adi,conversion-start", GPIOD_OUT_LOW); if (IS_ERR(st->convst_gpio)) return PTR_ERR(st->convst_gpio); - spi_set_drvdata(spi, indio_dev); - st->spi = spi; indio_dev->name = spi_get_device_id(spi)->name; @@ -333,17 +419,17 @@ static int ad7476_probe(struct spi_device *spi) } static const struct spi_device_id ad7476_id[] = { - {"ad7091", ID_AD7091R}, + {"ad7091", ID_AD7091}, {"ad7091r", ID_AD7091R}, - {"ad7273", ID_AD7277}, - {"ad7274", ID_AD7276}, + {"ad7273", ID_AD7273}, + {"ad7274", ID_AD7274}, {"ad7276", ID_AD7276}, {"ad7277", ID_AD7277}, {"ad7278", ID_AD7278}, {"ad7466", ID_AD7466}, {"ad7467", ID_AD7467}, {"ad7468", ID_AD7468}, - {"ad7475", ID_AD7466}, + {"ad7475", ID_AD7475}, {"ad7476", ID_AD7466}, {"ad7476a", ID_AD7466}, {"ad7477", ID_AD7467}, diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c index 0af0bb4d5a7f..0a60ecc69d38 100644 --- a/drivers/iio/adc/ad7606.c +++ b/drivers/iio/adc/ad7606.c @@ -663,7 +663,8 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address, } st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!st->trig) return -ENOMEM; diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c index 1e41759f3ee5..51ee9482e0df 100644 --- a/drivers/iio/adc/ad7766.c +++ b/drivers/iio/adc/ad7766.c @@ -248,7 +248,8 @@ static int ad7766_probe(struct spi_device *spi) if (spi->irq > 0) { ad7766->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!ad7766->trig) return -ENOMEM; @@ -272,8 +273,6 @@ static int ad7766_probe(struct spi_device *spi) return ret; } - spi_set_drvdata(spi, indio_dev); - ad7766->spi = spi; /* First byte always 0 */ @@ -289,10 +288,7 @@ static int ad7766_probe(struct spi_device *spi) if (ret) return ret; - ret = devm_iio_device_register(&spi->dev, indio_dev); - if (ret) - return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad7766_id[] = { diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c index 60f21fed6dcb..2c5c8a3672b2 100644 --- a/drivers/iio/adc/ad7768-1.c +++ b/drivers/iio/adc/ad7768-1.c @@ -614,7 +614,6 @@ static int ad7768_probe(struct spi_device *spi) st->mclk_freq = clk_get_rate(st->mclk); - spi_set_drvdata(spi, indio_dev); mutex_init(&st->lock); indio_dev->channels = ad7768_channels; @@ -630,7 +629,8 @@ static int ad7768_probe(struct spi_device *spi) } st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!st->trig) return -ENOMEM; diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c index 42e7e8e595d1..42bb952f4738 100644 --- a/drivers/iio/adc/ad7780.c +++ b/drivers/iio/adc/ad7780.c @@ -300,6 +300,11 @@ static int ad7780_init_gpios(struct device *dev, struct ad7780_state *st) return 0; } +static void ad7780_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int ad7780_probe(struct spi_device *spi) { struct ad7780_state *st; @@ -318,8 +323,6 @@ static int ad7780_probe(struct spi_device *spi) st->chip_info = &ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - spi_set_drvdata(spi, indio_dev); - indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = &st->chip_info->channel; @@ -340,35 +343,15 @@ static int ad7780_probe(struct spi_device *spi) return ret; } - ret = ad_sd_setup_buffer_and_trigger(indio_dev); + ret = devm_add_action_or_reset(&spi->dev, ad7780_reg_disable, st->reg); if (ret) - goto error_disable_reg; + return ret; - ret = iio_device_register(indio_dev); + ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); if (ret) - goto error_cleanup_buffer_and_trigger; - - return 0; - -error_cleanup_buffer_and_trigger: - ad_sd_cleanup_buffer_and_trigger(indio_dev); -error_disable_reg: - regulator_disable(st->reg); - - return ret; -} - -static int ad7780_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7780_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - ad_sd_cleanup_buffer_and_trigger(indio_dev); - - regulator_disable(st->reg); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad7780_id[] = { @@ -385,7 +368,6 @@ static struct spi_driver ad7780_driver = { .name = "ad7780", }, .probe = ad7780_probe, - .remove = ad7780_remove, .id_table = ad7780_id, }; module_spi_driver(ad7780_driver); diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index d57ad966e17c..cb579aa89f39 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -394,6 +394,11 @@ static int ad7791_setup(struct ad7791_state *st, st->mode); } +static void ad7791_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int ad7791_probe(struct spi_device *spi) { struct ad7791_platform_data *pdata = spi->dev.platform_data; @@ -420,11 +425,13 @@ static int ad7791_probe(struct spi_device *spi) if (ret) return ret; + ret = devm_add_action_or_reset(&spi->dev, ad7791_reg_disable, st->reg); + if (ret) + return ret; + st->info = &ad7791_chip_infos[spi_get_device_id(spi)->driver_data]; ad_sd_init(&st->sd, indio_dev, spi, &ad7791_sigma_delta_info); - spi_set_drvdata(spi, indio_dev); - indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->info->channels; @@ -434,39 +441,15 @@ static int ad7791_probe(struct spi_device *spi) else indio_dev->info = &ad7791_no_filter_info; - ret = ad_sd_setup_buffer_and_trigger(indio_dev); + ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); if (ret) - goto error_disable_reg; + return ret; ret = ad7791_setup(st, pdata); if (ret) - goto error_remove_trigger; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_remove_trigger; - - return 0; - -error_remove_trigger: - ad_sd_cleanup_buffer_and_trigger(indio_dev); -error_disable_reg: - regulator_disable(st->reg); - - return ret; -} - -static int ad7791_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7791_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - ad_sd_cleanup_buffer_and_trigger(indio_dev); - - regulator_disable(st->reg); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad7791_spi_ids[] = { @@ -484,7 +467,6 @@ static struct spi_driver ad7791_driver = { .name = "ad7791", }, .probe = ad7791_probe, - .remove = ad7791_remove, .id_table = ad7791_spi_ids, }; module_spi_driver(ad7791_driver); diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index 440ef4c7be07..ef3e2d3ecb0c 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -769,6 +769,11 @@ static const struct ad7793_chip_info ad7793_chip_info_tbl[] = { }, }; +static void ad7793_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int ad7793_probe(struct spi_device *spi) { const struct ad7793_platform_data *pdata = spi->dev.platform_data; @@ -803,11 +808,13 @@ static int ad7793_probe(struct spi_device *spi) if (ret) return ret; + ret = devm_add_action_or_reset(&spi->dev, ad7793_reg_disable, st->reg); + if (ret) + return ret; + vref_mv = regulator_get_voltage(st->reg); - if (vref_mv < 0) { - ret = vref_mv; - goto error_disable_reg; - } + if (vref_mv < 0) + return vref_mv; vref_mv /= 1000; } else { @@ -817,50 +824,21 @@ static int ad7793_probe(struct spi_device *spi) st->chip_info = &ad7793_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - spi_set_drvdata(spi, indio_dev); - indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; indio_dev->info = st->chip_info->iio_info; - ret = ad_sd_setup_buffer_and_trigger(indio_dev); + ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev); if (ret) - goto error_disable_reg; + return ret; ret = ad7793_setup(indio_dev, pdata, vref_mv); if (ret) - goto error_remove_trigger; - - ret = iio_device_register(indio_dev); - if (ret) - goto error_remove_trigger; - - return 0; - -error_remove_trigger: - ad_sd_cleanup_buffer_and_trigger(indio_dev); -error_disable_reg: - if (pdata->refsel != AD7793_REFSEL_INTERNAL) - regulator_disable(st->reg); - - return ret; -} - -static int ad7793_remove(struct spi_device *spi) -{ - const struct ad7793_platform_data *pdata = spi->dev.platform_data; - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ad7793_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - ad_sd_cleanup_buffer_and_trigger(indio_dev); - - if (pdata->refsel != AD7793_REFSEL_INTERNAL) - regulator_disable(st->reg); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id ad7793_id[] = { @@ -882,7 +860,6 @@ static struct spi_driver ad7793_driver = { .name = "ad7793", }, .probe = ad7793_probe, - .remove = ad7793_remove, .id_table = ad7793_id, }; module_spi_driver(ad7793_driver); diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c index 9b3cbe1ddc6f..f64999714a4d 100644 --- a/drivers/iio/adc/ad7887.c +++ b/drivers/iio/adc/ad7887.c @@ -268,7 +268,6 @@ static int ad7887_probe(struct spi_device *spi) st->chip_info = &ad7887_chip_info_tbl[spi_get_device_id(spi)->driver_data]; - spi_set_drvdata(spi, indio_dev); st->spi = spi; indio_dev->name = spi_get_device_id(spi)->name; diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c index 19a45dd43796..dbfc8517cb8a 100644 --- a/drivers/iio/adc/ad9467.c +++ b/drivers/iio/adc/ad9467.c @@ -434,8 +434,6 @@ static int ad9467_probe(struct spi_device *spi) mdelay(10); } - spi_set_drvdata(spi, st); - conv->chip_info = &info->axi_adc_info; id = ad9467_spi_read(spi, AN877_ADC_REG_CHIP_ID); diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index e777ec718973..1d652d9b2f5c 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -470,91 +470,65 @@ EXPORT_SYMBOL_GPL(ad_sd_validate_trigger); static const struct iio_trigger_ops ad_sd_trigger_ops = { }; -static int ad_sd_probe_trigger(struct iio_dev *indio_dev) +static int devm_ad_sd_probe_trigger(struct device *dev, struct iio_dev *indio_dev) { struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); int ret; - sigma_delta->trig = iio_trigger_alloc(&sigma_delta->spi->dev, - "%s-dev%d", indio_dev->name, - indio_dev->id); - if (sigma_delta->trig == NULL) { - ret = -ENOMEM; - goto error_ret; + if (dev != &sigma_delta->spi->dev) { + dev_err(dev, "Trigger parent should be '%s', got '%s'\n", + dev_name(dev), dev_name(&sigma_delta->spi->dev)); + return -EFAULT; } + + sigma_delta->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); + if (sigma_delta->trig == NULL) + return -ENOMEM; + sigma_delta->trig->ops = &ad_sd_trigger_ops; init_completion(&sigma_delta->completion); sigma_delta->irq_dis = true; - ret = request_irq(sigma_delta->spi->irq, - ad_sd_data_rdy_trig_poll, - sigma_delta->info->irq_flags | IRQF_NO_AUTOEN, - indio_dev->name, - sigma_delta); + ret = devm_request_irq(dev, sigma_delta->spi->irq, + ad_sd_data_rdy_trig_poll, + sigma_delta->info->irq_flags | IRQF_NO_AUTOEN, + indio_dev->name, + sigma_delta); if (ret) - goto error_free_trig; + return ret; iio_trigger_set_drvdata(sigma_delta->trig, sigma_delta); - ret = iio_trigger_register(sigma_delta->trig); + ret = devm_iio_trigger_register(dev, sigma_delta->trig); if (ret) - goto error_free_irq; + return ret; /* select default trigger */ indio_dev->trig = iio_trigger_get(sigma_delta->trig); return 0; - -error_free_irq: - free_irq(sigma_delta->spi->irq, sigma_delta); -error_free_trig: - iio_trigger_free(sigma_delta->trig); -error_ret: - return ret; -} - -static void ad_sd_remove_trigger(struct iio_dev *indio_dev) -{ - struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev); - - iio_trigger_unregister(sigma_delta->trig); - free_irq(sigma_delta->spi->irq, sigma_delta); - iio_trigger_free(sigma_delta->trig); } /** - * ad_sd_setup_buffer_and_trigger() - + * devm_ad_sd_setup_buffer_and_trigger() - Device-managed buffer & trigger setup + * @dev: Device object to which to bind the life-time of the resources attached * @indio_dev: The IIO device */ -int ad_sd_setup_buffer_and_trigger(struct iio_dev *indio_dev) +int devm_ad_sd_setup_buffer_and_trigger(struct device *dev, struct iio_dev *indio_dev) { int ret; - ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time, - &ad_sd_trigger_handler, &ad_sd_buffer_setup_ops); + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, + &iio_pollfunc_store_time, + &ad_sd_trigger_handler, + &ad_sd_buffer_setup_ops); if (ret) return ret; - ret = ad_sd_probe_trigger(indio_dev); - if (ret) { - iio_triggered_buffer_cleanup(indio_dev); - return ret; - } - - return 0; -} -EXPORT_SYMBOL_GPL(ad_sd_setup_buffer_and_trigger); - -/** - * ad_sd_cleanup_buffer_and_trigger() - - * @indio_dev: The IIO device - */ -void ad_sd_cleanup_buffer_and_trigger(struct iio_dev *indio_dev) -{ - ad_sd_remove_trigger(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); + return devm_ad_sd_probe_trigger(dev, indio_dev); } -EXPORT_SYMBOL_GPL(ad_sd_cleanup_buffer_and_trigger); +EXPORT_SYMBOL_GPL(devm_ad_sd_setup_buffer_and_trigger); /** * ad_sd_init() - Initializes a ad_sigma_delta struct diff --git a/drivers/iio/adc/adi-axi-adc.c b/drivers/iio/adc/adi-axi-adc.c index d5f6ffc5b5bc..a73e3c2d212f 100644 --- a/drivers/iio/adc/adi-axi-adc.c +++ b/drivers/iio/adc/adi-axi-adc.c @@ -202,29 +202,25 @@ static void adi_axi_adc_conv_unregister(struct adi_axi_adc_conv *conv) kfree(cl); } -static void devm_adi_axi_adc_conv_release(struct device *dev, void *res) +static void devm_adi_axi_adc_conv_release(void *conv) { - adi_axi_adc_conv_unregister(*(struct adi_axi_adc_conv **)res); + adi_axi_adc_conv_unregister(conv); } struct adi_axi_adc_conv *devm_adi_axi_adc_conv_register(struct device *dev, size_t sizeof_priv) { - struct adi_axi_adc_conv **ptr, *conv; - - ptr = devres_alloc(devm_adi_axi_adc_conv_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct adi_axi_adc_conv *conv; + int ret; conv = adi_axi_adc_conv_register(dev, sizeof_priv); - if (IS_ERR(conv)) { - devres_free(ptr); - return ERR_CAST(conv); - } + if (IS_ERR(conv)) + return conv; - *ptr = conv; - devres_add(dev, ptr); + ret = devm_add_action_or_reset(dev, devm_adi_axi_adc_conv_release, + conv); + if (ret) + return ERR_PTR(ret); return conv; } diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c index a7826f097b95..ea5ca163d879 100644 --- a/drivers/iio/adc/at91-sama5d2_adc.c +++ b/drivers/iio/adc/at91-sama5d2_adc.c @@ -403,7 +403,8 @@ struct at91_adc_state { struct at91_adc_dma dma_st; struct at91_adc_touch touch_st; struct iio_dev *indio_dev; - u16 buffer[AT91_BUFFER_MAX_HWORDS]; + /* Ensure naturally aligned timestamp */ + u16 buffer[AT91_BUFFER_MAX_HWORDS] __aligned(8); /* * lock to prevent concurrent 'single conversion' requests through * sysfs. @@ -997,7 +998,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio, int ret; trig = devm_iio_trigger_alloc(&indio->dev, "%s-dev%d-%s", indio->name, - indio->id, trigger_name); + iio_device_id(indio), trigger_name); if (!trig) return NULL; diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c index 0b5f0c91d0d7..5a7d3a3a5fa8 100644 --- a/drivers/iio/adc/at91_adc.c +++ b/drivers/iio/adc/at91_adc.c @@ -547,7 +547,7 @@ static int at91_adc_get_trigger_value_by_name(struct iio_dev *idev, char *name = kasprintf(GFP_KERNEL, "%s-dev%d-%s", idev->name, - idev->id, + iio_device_id(idev), triggers[i].name); if (!name) return -ENOMEM; @@ -626,7 +626,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *idev, int ret; trig = iio_trigger_alloc(idev->dev.parent, "%s-dev%d-%s", idev->name, - idev->id, trigger->name); + iio_device_id(idev), trigger->name); if (trig == NULL) return NULL; diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c index 0d53ef18e045..16407664182c 100644 --- a/drivers/iio/adc/dln2-adc.c +++ b/drivers/iio/adc/dln2-adc.c @@ -649,7 +649,8 @@ static int dln2_adc_probe(struct platform_device *pdev) indio_dev->setup_ops = &dln2_adc_buffer_setup_ops; dln2->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!dln2->trig) { dev_err(dev, "failed to allocate trigger\n"); return -ENOMEM; diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index c08ab3c6dfaf..a10a4e8d94fd 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -165,10 +165,8 @@ static int ep93xx_adc_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); priv->base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(priv->base)) { - dev_err(&pdev->dev, "Cannot map memory resource\n"); + if (IS_ERR(priv->base)) return PTR_ERR(priv->base); - } iiodev->name = dev_name(&pdev->dev); iiodev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c index 8c98d8c9ab1f..3b3868aa2533 100644 --- a/drivers/iio/adc/exynos_adc.c +++ b/drivers/iio/adc/exynos_adc.c @@ -794,7 +794,7 @@ static int exynos_adc_probe(struct platform_device *pdev) struct s3c2410_ts_mach_info *pdata = dev_get_platdata(&pdev->dev); struct iio_dev *indio_dev = NULL; bool has_ts = false; - int ret = -ENODEV; + int ret; int irq; indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct exynos_adc)); diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c index 074c30970465..8b353e26668e 100644 --- a/drivers/iio/adc/hi8435.c +++ b/drivers/iio/adc/hi8435.c @@ -483,7 +483,6 @@ static int hi8435_probe(struct spi_device *spi) gpiod_set_value_cansleep(reset_gpio, 1); } - spi_set_drvdata(spi, idev); mutex_init(&priv->lock); idev->name = spi_get_device_id(spi)->name; diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c index 6a173531d355..f7ee856a6b8b 100644 --- a/drivers/iio/adc/hx711.c +++ b/drivers/iio/adc/hx711.c @@ -86,9 +86,9 @@ struct hx711_data { struct mutex lock; /* * triggered buffer - * 2x32-bit channel + 64-bit timestamp + * 2x32-bit channel + 64-bit naturally aligned timestamp */ - u32 buffer[4]; + u32 buffer[4] __aligned(8); /* * delay after a rising edge on SCK until the data is ready DOUT * this is dependent on the hx711 where the datasheet tells a diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c index 2ae54258b221..a4b2ff9e0dd5 100644 --- a/drivers/iio/adc/ina2xx-adc.c +++ b/drivers/iio/adc/ina2xx-adc.c @@ -843,7 +843,8 @@ static int ina2xx_buffer_enable(struct iio_dev *indio_dev) chip->allow_async_readout); task = kthread_create(ina2xx_capture_thread, (void *)indio_dev, - "%s:%d-%uus", indio_dev->name, indio_dev->id, + "%s:%d-%uus", indio_dev->name, + iio_device_id(indio_dev), sampling_us); if (IS_ERR(task)) return PTR_ERR(task); diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c index e3c8ec107722..655ab02d03d8 100644 --- a/drivers/iio/adc/max1027.c +++ b/drivers/iio/adc/max1027.c @@ -430,8 +430,6 @@ static int max1027_probe(struct spi_device *spi) return -ENOMEM; } - spi_set_drvdata(spi, indio_dev); - st = iio_priv(indio_dev); st->spi = spi; st->info = &max1027_chip_info_tbl[spi_get_device_id(spi)->driver_data]; diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c index 6cf21758ca66..eb1ce6a0315c 100644 --- a/drivers/iio/adc/max11100.c +++ b/drivers/iio/adc/max11100.c @@ -12,6 +12,7 @@ #include <linux/module.h> #include <linux/regulator/consumer.h> #include <linux/spi/spi.h> +#include <asm/unaligned.h> #include <linux/iio/iio.h> #include <linux/iio/driver.h> @@ -63,7 +64,7 @@ static int max11100_read_single(struct iio_dev *indio_dev, int *val) return -EINVAL; } - *val = (state->buffer[1] << 8) | state->buffer[2]; + *val = get_unaligned_be16(&state->buffer[1]); return 0; } @@ -101,6 +102,11 @@ static const struct iio_info max11100_info = { .read_raw = max11100_read_raw, }; +static void max11100_regulator_disable(void *reg) +{ + regulator_disable(reg); +} + static int max11100_probe(struct spi_device *spi) { int ret; @@ -111,8 +117,6 @@ static int max11100_probe(struct spi_device *spi) if (!indio_dev) return -ENOMEM; - spi_set_drvdata(spi, indio_dev); - state = iio_priv(indio_dev); state->spi = spi; @@ -130,27 +134,12 @@ static int max11100_probe(struct spi_device *spi) if (ret) return ret; - ret = iio_device_register(indio_dev); + ret = devm_add_action_or_reset(&spi->dev, max11100_regulator_disable, + state->vref_reg); if (ret) - goto disable_regulator; - - return 0; - -disable_regulator: - regulator_disable(state->vref_reg); - - return ret; -} - -static int max11100_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct max11100_state *state = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - regulator_disable(state->vref_reg); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id max11100_ids[] = { @@ -165,7 +154,6 @@ static struct spi_driver max11100_driver = { .of_match_table = max11100_ids, }, .probe = max11100_probe, - .remove = max11100_remove, }; module_spi_driver(max11100_driver); diff --git a/drivers/iio/adc/max1118.c b/drivers/iio/adc/max1118.c index 6efb0b43d938..8cec9d949083 100644 --- a/drivers/iio/adc/max1118.c +++ b/drivers/iio/adc/max1118.c @@ -66,9 +66,8 @@ static const struct iio_chan_spec max1118_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(2), }; -static int max1118_read(struct spi_device *spi, int channel) +static int max1118_read(struct iio_dev *indio_dev, int channel) { - struct iio_dev *indio_dev = spi_get_drvdata(spi); struct max1118 *adc = iio_priv(indio_dev); struct spi_transfer xfers[] = { /* @@ -103,9 +102,9 @@ static int max1118_read(struct spi_device *spi, int channel) int ret; if (channel == 0) - ret = spi_sync_transfer(spi, xfers + 1, 2); + ret = spi_sync_transfer(adc->spi, xfers + 1, 2); else - ret = spi_sync_transfer(spi, xfers, 3); + ret = spi_sync_transfer(adc->spi, xfers, 3); if (ret) return ret; @@ -113,11 +112,10 @@ static int max1118_read(struct spi_device *spi, int channel) return adc->data; } -static int max1118_get_vref_mV(struct spi_device *spi) +static int max1118_get_vref_mV(struct iio_dev *indio_dev) { - struct iio_dev *indio_dev = spi_get_drvdata(spi); struct max1118 *adc = iio_priv(indio_dev); - const struct spi_device_id *id = spi_get_device_id(spi); + const struct spi_device_id *id = spi_get_device_id(adc->spi); int vref_uV; switch (id->driver_data) { @@ -144,14 +142,14 @@ static int max1118_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: mutex_lock(&adc->lock); - *val = max1118_read(adc->spi, chan->channel); + *val = max1118_read(indio_dev, chan->channel); mutex_unlock(&adc->lock); if (*val < 0) return *val; return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: - *val = max1118_get_vref_mV(adc->spi); + *val = max1118_get_vref_mV(indio_dev); if (*val < 0) return *val; *val2 = 8; @@ -180,7 +178,7 @@ static irqreturn_t max1118_trigger_handler(int irq, void *p) indio_dev->masklength) { const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index]; - int ret = max1118_read(adc->spi, scan_chan->channel); + int ret = max1118_read(indio_dev, scan_chan->channel); if (ret < 0) { dev_warn(&adc->spi->dev, @@ -201,6 +199,11 @@ out: return IRQ_HANDLED; } +static void max1118_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int max1118_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -225,9 +228,13 @@ static int max1118_probe(struct spi_device *spi) ret = regulator_enable(adc->reg); if (ret) return ret; - } - spi_set_drvdata(spi, indio_dev); + ret = devm_add_action_or_reset(&spi->dev, max1118_reg_disable, + adc->reg); + if (ret) + return ret; + + } indio_dev->name = spi_get_device_id(spi)->name; indio_dev->info = &max1118_info; @@ -241,40 +248,14 @@ static int max1118_probe(struct spi_device *spi) * a conversion has been completed, the MAX1117/MAX1118/MAX1119 will go * into AutoShutdown mode until the next conversion is initiated. */ - max1118_read(spi, 0); - - ret = iio_triggered_buffer_setup(indio_dev, NULL, - max1118_trigger_handler, NULL); - if (ret) - goto err_reg_disable; + max1118_read(indio_dev, 0); - ret = iio_device_register(indio_dev); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + max1118_trigger_handler, NULL); if (ret) - goto err_buffer_cleanup; - - return 0; - -err_buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); -err_reg_disable: - if (id->driver_data == max1118) - regulator_disable(adc->reg); - - return ret; -} - -static int max1118_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct max1118 *adc = iio_priv(indio_dev); - const struct spi_device_id *id = spi_get_device_id(spi); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - if (id->driver_data == max1118) - return regulator_disable(adc->reg); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct spi_device_id max1118_id[] = { @@ -299,7 +280,6 @@ static struct spi_driver max1118_spi_driver = { .of_match_table = max1118_dt_ids, }, .probe = max1118_probe, - .remove = max1118_remove, .id_table = max1118_id, }; module_spi_driver(max1118_spi_driver); diff --git a/drivers/iio/adc/max1241.c b/drivers/iio/adc/max1241.c index 0cbbb3c56d08..b60f8448f21a 100644 --- a/drivers/iio/adc/max1241.c +++ b/drivers/iio/adc/max1241.c @@ -147,8 +147,6 @@ static int max1241_probe(struct spi_device *spi) adc->spi = spi; mutex_init(&adc->lock); - spi_set_drvdata(spi, indio_dev); - adc->vdd = devm_regulator_get(dev, "vdd"); if (IS_ERR(adc->vdd)) { dev_err(dev, "failed to get vdd regulator\n"); diff --git a/drivers/iio/adc/mp2629_adc.c b/drivers/iio/adc/mp2629_adc.c index 331a9a728217..aca084f1e78a 100644 --- a/drivers/iio/adc/mp2629_adc.c +++ b/drivers/iio/adc/mp2629_adc.c @@ -144,7 +144,6 @@ static int mp2629_adc_probe(struct platform_device *pdev) } indio_dev->name = "mp2629-adc"; - indio_dev->dev.parent = dev; indio_dev->channels = mp2629_channels; indio_dev->num_channels = ARRAY_SIZE(mp2629_channels); indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c index 6b39a139ce28..07c0e6768391 100644 --- a/drivers/iio/adc/mt6360-adc.c +++ b/drivers/iio/adc/mt6360-adc.c @@ -337,7 +337,6 @@ static int mt6360_adc_probe(struct platform_device *pdev) } indio_dev->name = dev_name(&pdev->dev); - indio_dev->dev.parent = &pdev->dev; indio_dev->info = &mt6360_adc_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->channels = mt6360_adc_channels; diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c index 30e29f44ebd2..bca79a93cbe4 100644 --- a/drivers/iio/adc/mxs-lradc-adc.c +++ b/drivers/iio/adc/mxs-lradc-adc.c @@ -115,7 +115,8 @@ struct mxs_lradc_adc { struct device *dev; void __iomem *base; - u32 buffer[10]; + /* Maximum of 8 channels + 8 byte ts */ + u32 buffer[10] __aligned(8); struct iio_trigger *trig; struct completion completion; spinlock_t lock; @@ -455,7 +456,7 @@ static int mxs_lradc_adc_trigger_init(struct iio_dev *iio) struct mxs_lradc_adc *adc = iio_priv(iio); trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name, - iio->id); + iio_device_id(iio)); if (!trig) return -ENOMEM; diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c index 9f38cf3c7dc2..a48895046408 100644 --- a/drivers/iio/adc/rcar-gyroadc.c +++ b/drivers/iio/adc/rcar-gyroadc.c @@ -162,18 +162,13 @@ static const struct iio_chan_spec rcar_gyroadc_iio_channels_3[] = { static int rcar_gyroadc_set_power(struct rcar_gyroadc *priv, bool on) { struct device *dev = priv->dev; - int ret; if (on) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); + return pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); - ret = pm_runtime_put_autosuspend(dev); + return pm_runtime_put_autosuspend(dev); } - - return ret; } static int rcar_gyroadc_read_raw(struct iio_dev *indio_dev, @@ -535,7 +530,10 @@ static int rcar_gyroadc_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(dev); pm_runtime_enable(dev); - pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) + goto err_power_up; + rcar_gyroadc_hw_init(priv); rcar_gyroadc_hw_start(priv); @@ -552,6 +550,7 @@ static int rcar_gyroadc_probe(struct platform_device *pdev) err_iio_device_register: rcar_gyroadc_hw_stop(priv); pm_runtime_put_sync(dev); +err_power_up: pm_runtime_disable(dev); pm_runtime_set_suspended(dev); clk_disable_unprepare(priv->clk); diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c index 301cf66de695..00098caf6d9e 100644 --- a/drivers/iio/adc/sc27xx_adc.c +++ b/drivers/iio/adc/sc27xx_adc.c @@ -549,6 +549,7 @@ static const struct of_device_id sc27xx_adc_of_match[] = { { .compatible = "sprd,sc2731-adc", }, { } }; +MODULE_DEVICE_TABLE(of, sc27xx_adc_of_match); static struct platform_driver sc27xx_adc_driver = { .probe = sc27xx_adc_probe, diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c index b25386b19373..5088de835bb1 100644 --- a/drivers/iio/adc/stm32-adc.c +++ b/drivers/iio/adc/stm32-adc.c @@ -449,7 +449,7 @@ static const struct stm32_adc_regspec stm32h7_adc_regspec = { .smp_bits = stm32h7_smp_bits, }; -/** +/* * STM32 ADC registers access routines * @adc: stm32 adc instance * @reg: reg offset in adc instance @@ -851,7 +851,7 @@ static int stm32h7_adc_restore_selfcalib(struct iio_dev *indio_dev) return 0; } -/** +/* * Fixed timeout value for ADC calibration. * worst cases: * - low clock frequency @@ -1158,11 +1158,9 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev, adc->bufi = 0; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) return ret; - } /* Apply sampling time settings */ stm32_adc_writel(adc, regs->smpr[0], adc->smpr_val[0]); @@ -1364,11 +1362,9 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev, struct device *dev = indio_dev->dev.parent; int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) return ret; - } adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength); @@ -1413,11 +1409,9 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev, struct device *dev = indio_dev->dev.parent; int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) return ret; - } if (!readval) stm32_adc_writel(adc, reg, writeval); @@ -1537,11 +1531,9 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev) struct device *dev = indio_dev->dev.parent; int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) return ret; - } ret = stm32_adc_set_trig(indio_dev, indio_dev->trig); if (ret) { diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c index bb925a11c8ae..a627af9a825e 100644 --- a/drivers/iio/adc/stm32-dfsdm-core.c +++ b/drivers/iio/adc/stm32-dfsdm-core.c @@ -135,11 +135,9 @@ int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm) int ret; if (atomic_inc_return(&priv->n_active_ch) == 1) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) goto error_ret; - } /* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */ clk_src = priv->aclk ? 1 : 0; diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c index b64718daa201..16fc608db36a 100644 --- a/drivers/iio/adc/ti-adc081c.c +++ b/drivers/iio/adc/ti-adc081c.c @@ -146,6 +146,11 @@ out: return IRQ_HANDLED; } +static void adc081c_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int adc081c_probe(struct i2c_client *client, const struct i2c_device_id *id) { @@ -175,6 +180,11 @@ static int adc081c_probe(struct i2c_client *client, if (err < 0) return err; + err = devm_add_action_or_reset(&client->dev, adc081c_reg_disable, + adc->ref); + if (err) + return err; + iio->name = dev_name(&client->dev); iio->modes = INDIO_DIRECT_MODE; iio->info = &adc081c_info; @@ -182,38 +192,14 @@ static int adc081c_probe(struct i2c_client *client, iio->channels = model->channels; iio->num_channels = ADC081C_NUM_CHANNELS; - err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, NULL); + err = devm_iio_triggered_buffer_setup(&client->dev, iio, NULL, + adc081c_trigger_handler, NULL); if (err < 0) { dev_err(&client->dev, "iio triggered buffer setup failed\n"); - goto err_regulator_disable; + return err; } - err = iio_device_register(iio); - if (err < 0) - goto err_buffer_cleanup; - - i2c_set_clientdata(client, iio); - - return 0; - -err_buffer_cleanup: - iio_triggered_buffer_cleanup(iio); -err_regulator_disable: - regulator_disable(adc->ref); - - return err; -} - -static int adc081c_remove(struct i2c_client *client) -{ - struct iio_dev *iio = i2c_get_clientdata(client); - struct adc081c *adc = iio_priv(iio); - - iio_device_unregister(iio); - iio_triggered_buffer_cleanup(iio); - regulator_disable(adc->ref); - - return 0; + return devm_iio_device_register(&client->dev, iio); } static const struct i2c_device_id adc081c_id[] = { @@ -238,7 +224,6 @@ static struct i2c_driver adc081c_driver = { .of_match_table = adc081c_of_match, }, .probe = adc081c_probe, - .remove = adc081c_remove, .id_table = adc081c_id, }; module_i2c_driver(adc081c_driver); diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c index 0261b3cfc92b..fb5e72600b96 100644 --- a/drivers/iio/adc/ti-adc0832.c +++ b/drivers/iio/adc/ti-adc0832.c @@ -236,6 +236,11 @@ out: return IRQ_HANDLED; } +static void adc0832_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int adc0832_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -287,36 +292,17 @@ static int adc0832_probe(struct spi_device *spi) if (ret) return ret; - spi_set_drvdata(spi, indio_dev); - - ret = iio_triggered_buffer_setup(indio_dev, NULL, - adc0832_trigger_handler, NULL); + ret = devm_add_action_or_reset(&spi->dev, adc0832_reg_disable, + adc->reg); if (ret) - goto err_reg_disable; + return ret; - ret = iio_device_register(indio_dev); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + adc0832_trigger_handler, NULL); if (ret) - goto err_buffer_cleanup; - - return 0; -err_buffer_cleanup: - iio_triggered_buffer_cleanup(indio_dev); -err_reg_disable: - regulator_disable(adc->reg); - - return ret; -} - -static int adc0832_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct adc0832 *adc = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - regulator_disable(adc->reg); + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id adc0832_dt_ids[] = { @@ -343,7 +329,6 @@ static struct spi_driver adc0832_driver = { .of_match_table = adc0832_dt_ids, }, .probe = adc0832_probe, - .remove = adc0832_remove, .id_table = adc0832_id, }; module_spi_driver(adc0832_driver); diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c index 33aea961d850..ce3f5a3814f9 100644 --- a/drivers/iio/adc/ti-adc084s021.c +++ b/drivers/iio/adc/ti-adc084s021.c @@ -210,9 +210,6 @@ static int adc084s021_probe(struct spi_device *spi) adc = iio_priv(indio_dev); adc->spi = spi; - /* Connect the SPI device and the iio dev */ - spi_set_drvdata(spi, indio_dev); - /* Initiate the Industrial I/O device */ indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c index 183b2245e89b..db902aef2abe 100644 --- a/drivers/iio/adc/ti-adc108s102.c +++ b/drivers/iio/adc/ti-adc108s102.c @@ -215,6 +215,11 @@ static const struct iio_info adc108s102_info = { .update_scan_mode = &adc108s102_update_scan_mode, }; +static void adc108s102_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int adc108s102_probe(struct spi_device *spi) { struct adc108s102_state *st; @@ -239,6 +244,10 @@ static int adc108s102_probe(struct spi_device *spi) dev_err(&spi->dev, "Cannot enable vref regulator\n"); return ret; } + ret = devm_add_action_or_reset(&spi->dev, adc108s102_reg_disable, + st->reg); + if (ret) + return ret; ret = regulator_get_voltage(st->reg); if (ret < 0) { @@ -249,7 +258,6 @@ static int adc108s102_probe(struct spi_device *spi) st->va_millivolt = ret / 1000; } - spi_set_drvdata(spi, indio_dev); st->spi = spi; indio_dev->name = spi->modalias; @@ -266,40 +274,18 @@ static int adc108s102_probe(struct spi_device *spi) spi_message_init_with_transfers(&st->scan_single_msg, &st->scan_single_xfer, 1); - ret = iio_triggered_buffer_setup(indio_dev, NULL, - &adc108s102_trigger_handler, NULL); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + &adc108s102_trigger_handler, + NULL); if (ret) - goto error_disable_reg; + return ret; - ret = iio_device_register(indio_dev); - if (ret) { + ret = devm_iio_device_register(&spi->dev, indio_dev); + if (ret) dev_err(&spi->dev, "Failed to register IIO device\n"); - goto error_cleanup_triggered_buffer; - } - return 0; - -error_cleanup_triggered_buffer: - iio_triggered_buffer_cleanup(indio_dev); - -error_disable_reg: - regulator_disable(st->reg); - return ret; } -static int adc108s102_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct adc108s102_state *st = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - - regulator_disable(st->reg); - - return 0; -} - static const struct of_device_id adc108s102_of_match[] = { { .compatible = "ti,adc108s102" }, { } @@ -327,7 +313,6 @@ static struct spi_driver adc108s102_driver = { .acpi_match_table = ACPI_PTR(adc108s102_acpi_ids), }, .probe = adc108s102_probe, - .remove = adc108s102_remove, .id_table = adc108s102_id, }; module_spi_driver(adc108s102_driver); diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 607791ffe7f0..75ca7f1c8726 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -169,6 +169,11 @@ static const struct iio_info ti_adc_info = { .read_raw = ti_adc_read_raw, }; +static void ti_adc_reg_disable(void *reg) +{ + regulator_disable(reg); +} + static int ti_adc_probe(struct spi_device *spi) { struct iio_dev *indio_dev; @@ -182,7 +187,6 @@ static int ti_adc_probe(struct spi_device *spi) indio_dev->info = &ti_adc_info; indio_dev->name = TI_ADC_DRV_NAME; indio_dev->modes = INDIO_DIRECT_MODE; - spi_set_drvdata(spi, indio_dev); data = iio_priv(indio_dev); data->spi = spi; @@ -203,42 +207,24 @@ static int ti_adc_probe(struct spi_device *spi) } data->ref = devm_regulator_get(&spi->dev, "vdda"); - if (!IS_ERR(data->ref)) { - ret = regulator_enable(data->ref); - if (ret < 0) - return ret; - } + if (IS_ERR(data->ref)) + return PTR_ERR(data->ref); - ret = iio_triggered_buffer_setup(indio_dev, NULL, - ti_adc_trigger_handler, NULL); - if (ret) - goto error_regulator_disable; + ret = regulator_enable(data->ref); + if (ret < 0) + return ret; - ret = iio_device_register(indio_dev); + ret = devm_add_action_or_reset(&spi->dev, ti_adc_reg_disable, + data->ref); if (ret) - goto error_unreg_buffer; - - return 0; + return ret; -error_unreg_buffer: - iio_triggered_buffer_cleanup(indio_dev); - -error_regulator_disable: - regulator_disable(data->ref); - - return ret; -} - -static int ti_adc_remove(struct spi_device *spi) -{ - struct iio_dev *indio_dev = spi_get_drvdata(spi); - struct ti_adc_data *data = iio_priv(indio_dev); - - iio_device_unregister(indio_dev); - iio_triggered_buffer_cleanup(indio_dev); - regulator_disable(data->ref); + ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL, + ti_adc_trigger_handler, NULL); + if (ret) + return ret; - return 0; + return devm_iio_device_register(&spi->dev, indio_dev); } static const struct of_device_id ti_adc_dt_ids[] = { @@ -261,7 +247,6 @@ static struct spi_driver ti_adc_driver = { .of_match_table = ti_adc_dt_ids, }, .probe = ti_adc_probe, - .remove = ti_adc_remove, .id_table = ti_adc_id, }; module_spi_driver(ti_adc_driver); diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c index 9fef39bcf997..b0352e91ac16 100644 --- a/drivers/iio/adc/ti-ads1015.c +++ b/drivers/iio/adc/ti-ads1015.c @@ -323,9 +323,7 @@ static int ads1015_set_power_state(struct ads1015_data *data, bool on) struct device *dev = regmap_get_device(data->regmap); if (on) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); @@ -395,10 +393,14 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct ads1015_data *data = iio_priv(indio_dev); - s16 buf[8]; /* 1x s16 ADC val + 3x s16 padding + 4x s16 timestamp */ + /* Ensure natural alignment of timestamp */ + struct { + s16 chan; + s64 timestamp __aligned(8); + } scan; int chan, ret, res; - memset(buf, 0, sizeof(buf)); + memset(&scan, 0, sizeof(scan)); mutex_lock(&data->lock); chan = find_first_bit(indio_dev->active_scan_mask, @@ -409,10 +411,10 @@ static irqreturn_t ads1015_trigger_handler(int irq, void *p) goto err; } - buf[0] = res; + scan.chan = res; mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, buf, + iio_push_to_buffers_with_timestamp(indio_dev, &scan, iio_get_time_ns(indio_dev)); err: @@ -1066,7 +1068,6 @@ static int ads1015_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); /* power down single shot mode */ return ads1015_set_conv_mode(data, ADS1015_SINGLESHOT); diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c index b4a128b19188..17d0da5877a9 100644 --- a/drivers/iio/adc/ti-ads124s08.c +++ b/drivers/iio/adc/ti-ads124s08.c @@ -327,8 +327,6 @@ static int ads124s_probe(struct spi_device *spi) ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data]; - spi_set_drvdata(spi, indio_dev); - ads124s_priv->spi = spi; indio_dev->name = spi_id->name; diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c index 764dab087b41..0c2025a22575 100644 --- a/drivers/iio/adc/ti-ads131e08.c +++ b/drivers/iio/adc/ti-ads131e08.c @@ -830,7 +830,6 @@ static int ads131e08_probe(struct spi_device *spi) return ret; indio_dev->name = st->info->name; - indio_dev->dev.parent = &spi->dev; indio_dev->info = &ads131e08_iio_info; indio_dev->modes = INDIO_DIRECT_MODE; @@ -850,7 +849,7 @@ static int ads131e08_probe(struct spi_device *spi) } st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, iio_device_id(indio_dev)); if (!st->trig) { dev_err(&spi->dev, "failed to allocate IIO trigger\n"); return -ENOMEM; diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c index 16bcb37eebb7..79c803537dc4 100644 --- a/drivers/iio/adc/ti-ads8688.c +++ b/drivers/iio/adc/ti-ads8688.c @@ -383,7 +383,8 @@ static irqreturn_t ads8688_trigger_handler(int irq, void *p) { struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; - u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)]; + /* Ensure naturally aligned timestamp */ + u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)] __aligned(8); int i, j = 0; for (i = 0; i < indio_dev->masklength; i++) { diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c new file mode 100644 index 000000000000..170950d5dd49 --- /dev/null +++ b/drivers/iio/adc/ti-tsc2046.c @@ -0,0 +1,712 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Texas Instruments TSC2046 SPI ADC driver + * + * Copyright (c) 2021 Oleksij Rempel <kernel@pengutronix.de>, Pengutronix + */ + +#include <linux/bitfield.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/spi/spi.h> + +#include <asm/unaligned.h> + +#include <linux/iio/buffer.h> +#include <linux/iio/trigger_consumer.h> +#include <linux/iio/triggered_buffer.h> +#include <linux/iio/trigger.h> + +/* + * The PENIRQ of TSC2046 controller is implemented as level shifter attached to + * the X+ line. If voltage of the X+ line reaches a specific level the IRQ will + * be activated or deactivated. + * To make this kind of IRQ reusable as trigger following additions were + * implemented: + * - rate limiting: + * For typical touchscreen use case, we need to trigger about each 10ms. + * - hrtimer: + * Continue triggering at least once after the IRQ was deactivated. Then + * deactivate this trigger to stop sampling in order to reduce power + * consumption. + */ + +#define TI_TSC2046_NAME "tsc2046" + +/* This driver doesn't aim at the peak continuous sample rate */ +#define TI_TSC2046_MAX_SAMPLE_RATE 125000 +#define TI_TSC2046_SAMPLE_BITS \ + BITS_PER_TYPE(struct tsc2046_adc_atom) +#define TI_TSC2046_MAX_CLK_FREQ \ + (TI_TSC2046_MAX_SAMPLE_RATE * TI_TSC2046_SAMPLE_BITS) + +#define TI_TSC2046_SAMPLE_INTERVAL_US 10000 + +#define TI_TSC2046_START BIT(7) +#define TI_TSC2046_ADDR GENMASK(6, 4) +#define TI_TSC2046_ADDR_TEMP1 7 +#define TI_TSC2046_ADDR_AUX 6 +#define TI_TSC2046_ADDR_X 5 +#define TI_TSC2046_ADDR_Z2 4 +#define TI_TSC2046_ADDR_Z1 3 +#define TI_TSC2046_ADDR_VBAT 2 +#define TI_TSC2046_ADDR_Y 1 +#define TI_TSC2046_ADDR_TEMP0 0 + +/* + * The mode bit sets the resolution of the ADC. With this bit low, the next + * conversion has 12-bit resolution, whereas with this bit high, the next + * conversion has 8-bit resolution. This driver is optimized for 12-bit mode. + * So, for this driver, this bit should stay zero. + */ +#define TI_TSC2046_8BIT_MODE BIT(3) + +/* + * SER/DFR - The SER/DFR bit controls the reference mode, either single-ended + * (high) or differential (low). + */ +#define TI_TSC2046_SER BIT(2) + +/* + * If VREF_ON and ADC_ON are both zero, then the chip operates in + * auto-wake/suspend mode. In most case this bits should stay zero. + */ +#define TI_TSC2046_PD1_VREF_ON BIT(1) +#define TI_TSC2046_PD0_ADC_ON BIT(0) + +/* + * All supported devices can do 8 or 12bit resolution. This driver + * supports only 12bit mode, here we have a 16bit data transfer, where + * the MSB and the 3 LSB are 0. + */ +#define TI_TSC2046_DATA_12BIT GENMASK(14, 3) + +#define TI_TSC2046_MAX_CHAN 8 + +/* Represents a HW sample */ +struct tsc2046_adc_atom { + /* + * Command transmitted to the controller. This field is empty on the RX + * buffer. + */ + u8 cmd; + /* + * Data received from the controller. This field is empty for the TX + * buffer + */ + __be16 data; +} __packed; + +/* Layout of atomic buffers within big buffer */ +struct tsc2046_adc_group_layout { + /* Group offset within the SPI RX buffer */ + unsigned int offset; + /* + * Amount of tsc2046_adc_atom structs within the same command gathered + * within same group. + */ + unsigned int count; + /* + * Settling samples (tsc2046_adc_atom structs) which should be skipped + * before good samples will start. + */ + unsigned int skip; +}; + +struct tsc2046_adc_dcfg { + const struct iio_chan_spec *channels; + unsigned int num_channels; +}; + +struct tsc2046_adc_ch_cfg { + unsigned int settling_time_us; + unsigned int oversampling_ratio; +}; + +struct tsc2046_adc_priv { + struct spi_device *spi; + const struct tsc2046_adc_dcfg *dcfg; + + struct iio_trigger *trig; + struct hrtimer trig_timer; + spinlock_t trig_lock; + unsigned int trig_more_count; + + struct spi_transfer xfer; + struct spi_message msg; + + struct { + /* Scan data for each channel */ + u16 data[TI_TSC2046_MAX_CHAN]; + /* Timestamp */ + s64 ts __aligned(8); + } scan_buf; + + /* + * Lock to protect the layout and the SPI transfer buffer. + * tsc2046_adc_group_layout can be changed within update_scan_mode(), + * in this case the l[] and tx/rx buffer will be out of sync to each + * other. + */ + struct mutex slock; + struct tsc2046_adc_group_layout l[TI_TSC2046_MAX_CHAN]; + struct tsc2046_adc_atom *rx; + struct tsc2046_adc_atom *tx; + + struct tsc2046_adc_atom *rx_one; + struct tsc2046_adc_atom *tx_one; + + unsigned int count; + unsigned int groups; + u32 effective_speed_hz; + u32 scan_interval_us; + u32 time_per_scan_us; + u32 time_per_bit_ns; + + struct tsc2046_adc_ch_cfg ch_cfg[TI_TSC2046_MAX_CHAN]; +}; + +#define TI_TSC2046_V_CHAN(index, bits, name) \ +{ \ + .type = IIO_VOLTAGE, \ + .indexed = 1, \ + .channel = index, \ + .datasheet_name = "#name", \ + .scan_index = index, \ + .scan_type = { \ + .sign = 'u', \ + .realbits = bits, \ + .storagebits = 16, \ + .endianness = IIO_CPU, \ + }, \ +} + +#define DECLARE_TI_TSC2046_8_CHANNELS(name, bits) \ +const struct iio_chan_spec name ## _channels[] = { \ + TI_TSC2046_V_CHAN(0, bits, TEMP0), \ + TI_TSC2046_V_CHAN(1, bits, Y), \ + TI_TSC2046_V_CHAN(2, bits, VBAT), \ + TI_TSC2046_V_CHAN(3, bits, Z1), \ + TI_TSC2046_V_CHAN(4, bits, Z2), \ + TI_TSC2046_V_CHAN(5, bits, X), \ + TI_TSC2046_V_CHAN(6, bits, AUX), \ + TI_TSC2046_V_CHAN(7, bits, TEMP1), \ + IIO_CHAN_SOFT_TIMESTAMP(8), \ +} + +static DECLARE_TI_TSC2046_8_CHANNELS(tsc2046_adc, 12); + +static const struct tsc2046_adc_dcfg tsc2046_adc_dcfg_tsc2046e = { + .channels = tsc2046_adc_channels, + .num_channels = ARRAY_SIZE(tsc2046_adc_channels), +}; + +/* + * Convert time to a number of samples which can be transferred within this + * time. + */ +static unsigned int tsc2046_adc_time_to_count(struct tsc2046_adc_priv *priv, + unsigned long time) +{ + unsigned int bit_count, sample_count; + + bit_count = DIV_ROUND_UP(time * NSEC_PER_USEC, priv->time_per_bit_ns); + sample_count = DIV_ROUND_UP(bit_count, TI_TSC2046_SAMPLE_BITS); + + dev_dbg(&priv->spi->dev, "Effective speed %u, time per bit: %u, count bits: %u, count samples: %u\n", + priv->effective_speed_hz, priv->time_per_bit_ns, + bit_count, sample_count); + + return sample_count; +} + +static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx, + bool keep_power) +{ + u32 pd; + + /* + * if PD bits are 0, controller will automatically disable ADC, VREF and + * enable IRQ. + */ + if (keep_power) + pd = TI_TSC2046_PD0_ADC_ON; + else + pd = 0; + + return TI_TSC2046_START | FIELD_PREP(TI_TSC2046_ADDR, ch_idx) | pd; +} + +static u16 tsc2046_adc_get_value(struct tsc2046_adc_atom *buf) +{ + return FIELD_GET(TI_TSC2046_DATA_12BIT, get_unaligned_be16(&buf->data)); +} + +static int tsc2046_adc_read_one(struct tsc2046_adc_priv *priv, int ch_idx, + u32 *effective_speed_hz) +{ + struct spi_transfer xfer; + struct spi_message msg; + int ret; + + memset(&xfer, 0, sizeof(xfer)); + priv->tx_one->cmd = tsc2046_adc_get_cmd(priv, ch_idx, false); + priv->tx_one->data = 0; + xfer.tx_buf = priv->tx_one; + xfer.rx_buf = priv->rx_one; + xfer.len = sizeof(*priv->tx_one); + spi_message_init_with_transfers(&msg, &xfer, 1); + + /* + * We aren't using spi_write_then_read() because we need to be able + * to get hold of the effective_speed_hz from the xfer + */ + ret = spi_sync(priv->spi, &msg); + if (ret) { + dev_err_ratelimited(&priv->spi->dev, "SPI transfer failed %pe\n", + ERR_PTR(ret)); + return ret; + } + + if (effective_speed_hz) + *effective_speed_hz = xfer.effective_speed_hz; + + return tsc2046_adc_get_value(priv->rx_one); +} + +static size_t tsc2046_adc_group_set_layout(struct tsc2046_adc_priv *priv, + unsigned int group, + unsigned int ch_idx) +{ + struct tsc2046_adc_ch_cfg *ch = &priv->ch_cfg[ch_idx]; + struct tsc2046_adc_group_layout *cur; + unsigned int max_count, count_skip; + unsigned int offset = 0; + + if (group) + offset = priv->l[group - 1].offset + priv->l[group - 1].count; + + count_skip = tsc2046_adc_time_to_count(priv, ch->settling_time_us); + max_count = count_skip + ch->oversampling_ratio; + + cur = &priv->l[group]; + cur->offset = offset; + cur->count = max_count; + cur->skip = count_skip; + + return sizeof(*priv->tx) * max_count; +} + +static void tsc2046_adc_group_set_cmd(struct tsc2046_adc_priv *priv, + unsigned int group, int ch_idx) +{ + struct tsc2046_adc_group_layout *l = &priv->l[group]; + unsigned int i; + u8 cmd; + + /* + * Do not enable automatic power down on working samples. Otherwise the + * plates will never be completely charged. + */ + cmd = tsc2046_adc_get_cmd(priv, ch_idx, true); + + for (i = 0; i < l->count - 1; i++) + priv->tx[l->offset + i].cmd = cmd; + + /* automatically power down on last sample */ + priv->tx[l->offset + i].cmd = tsc2046_adc_get_cmd(priv, ch_idx, false); +} + +static u16 tsc2046_adc_get_val(struct tsc2046_adc_priv *priv, int group) +{ + struct tsc2046_adc_group_layout *l; + unsigned int val, val_normalized = 0; + int valid_count, i; + + l = &priv->l[group]; + valid_count = l->count - l->skip; + + for (i = 0; i < valid_count; i++) { + val = tsc2046_adc_get_value(&priv->rx[l->offset + l->skip + i]); + val_normalized += val; + } + + return DIV_ROUND_UP(val_normalized, valid_count); +} + +static int tsc2046_adc_scan(struct iio_dev *indio_dev) +{ + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + struct device *dev = &priv->spi->dev; + int group; + int ret; + + ret = spi_sync(priv->spi, &priv->msg); + if (ret < 0) { + dev_err_ratelimited(dev, "SPI transfer failed: %pe\n", ERR_PTR(ret)); + return ret; + } + + for (group = 0; group < priv->groups; group++) + priv->scan_buf.data[group] = tsc2046_adc_get_val(priv, group); + + ret = iio_push_to_buffers_with_timestamp(indio_dev, &priv->scan_buf, + iio_get_time_ns(indio_dev)); + /* If the consumer is kfifo, we may get a EBUSY here - ignore it. */ + if (ret < 0 && ret != -EBUSY) { + dev_err_ratelimited(dev, "Failed to push scan buffer %pe\n", + ERR_PTR(ret)); + + return ret; + } + + return 0; +} + +static irqreturn_t tsc2046_adc_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + + mutex_lock(&priv->slock); + tsc2046_adc_scan(indio_dev); + mutex_unlock(&priv->slock); + + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + +static int tsc2046_adc_update_scan_mode(struct iio_dev *indio_dev, + const unsigned long *active_scan_mask) +{ + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + unsigned int ch_idx, group = 0; + size_t size; + + mutex_lock(&priv->slock); + + size = 0; + for_each_set_bit(ch_idx, active_scan_mask, indio_dev->num_channels) { + size += tsc2046_adc_group_set_layout(priv, group, ch_idx); + tsc2046_adc_group_set_cmd(priv, group, ch_idx); + group++; + } + + priv->groups = group; + priv->xfer.len = size; + priv->time_per_scan_us = size * 8 * priv->time_per_bit_ns / NSEC_PER_USEC; + + if (priv->scan_interval_us > priv->time_per_scan_us) + dev_warn(&priv->spi->dev, "The scan interval (%d) is less then calculated scan time (%d)\n", + priv->scan_interval_us, priv->time_per_scan_us); + + mutex_unlock(&priv->slock); + + return 0; +} + +static const struct iio_info tsc2046_adc_info = { + .update_scan_mode = tsc2046_adc_update_scan_mode, +}; + +static enum hrtimer_restart tsc2046_adc_trig_more(struct hrtimer *hrtimer) +{ + struct tsc2046_adc_priv *priv = container_of(hrtimer, + struct tsc2046_adc_priv, + trig_timer); + unsigned long flags; + + spin_lock_irqsave(&priv->trig_lock, flags); + + disable_irq_nosync(priv->spi->irq); + + priv->trig_more_count++; + iio_trigger_poll(priv->trig); + + spin_unlock_irqrestore(&priv->trig_lock, flags); + + return HRTIMER_NORESTART; +} + +static irqreturn_t tsc2046_adc_irq(int irq, void *dev_id) +{ + struct iio_dev *indio_dev = dev_id; + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + + spin_lock(&priv->trig_lock); + + hrtimer_try_to_cancel(&priv->trig_timer); + + priv->trig_more_count = 0; + disable_irq_nosync(priv->spi->irq); + iio_trigger_poll(priv->trig); + + spin_unlock(&priv->trig_lock); + + return IRQ_HANDLED; +} + +static void tsc2046_adc_reenable_trigger(struct iio_trigger *trig) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + unsigned long flags; + int delta; + + /* + * We can sample it as fast as we can, but usually we do not need so + * many samples. Reduce the sample rate for default (touchscreen) use + * case. + * Currently we do not need a highly precise sample rate. It is enough + * to have calculated numbers. + */ + delta = priv->scan_interval_us - priv->time_per_scan_us; + if (delta > 0) + fsleep(delta); + + spin_lock_irqsave(&priv->trig_lock, flags); + + /* + * We need to trigger at least one extra sample to detect state + * difference on ADC side. + */ + if (!priv->trig_more_count) { + int timeout_ms = DIV_ROUND_UP(priv->scan_interval_us, + USEC_PER_MSEC); + + hrtimer_start(&priv->trig_timer, ms_to_ktime(timeout_ms), + HRTIMER_MODE_REL_SOFT); + } + + enable_irq(priv->spi->irq); + + spin_unlock_irqrestore(&priv->trig_lock, flags); +} + +static int tsc2046_adc_set_trigger_state(struct iio_trigger *trig, bool enable) +{ + struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig); + struct tsc2046_adc_priv *priv = iio_priv(indio_dev); + + if (enable) { + enable_irq(priv->spi->irq); + } else { + disable_irq(priv->spi->irq); + hrtimer_try_to_cancel(&priv->trig_timer); + } + + return 0; +} + +static const struct iio_trigger_ops tsc2046_adc_trigger_ops = { + .set_trigger_state = tsc2046_adc_set_trigger_state, + .reenable = tsc2046_adc_reenable_trigger, +}; + +static int tsc2046_adc_setup_spi_msg(struct tsc2046_adc_priv *priv) +{ + unsigned int ch_idx; + size_t size; + int ret; + + priv->tx_one = devm_kzalloc(&priv->spi->dev, sizeof(*priv->tx_one), + GFP_KERNEL); + if (!priv->tx_one) + return -ENOMEM; + + priv->rx_one = devm_kzalloc(&priv->spi->dev, sizeof(*priv->rx_one), + GFP_KERNEL); + if (!priv->rx_one) + return -ENOMEM; + + /* + * Make dummy read to set initial power state and get real SPI clock + * freq. It seems to be not important which channel is used for this + * case. + */ + ret = tsc2046_adc_read_one(priv, TI_TSC2046_ADDR_TEMP0, + &priv->effective_speed_hz); + if (ret < 0) + return ret; + + /* + * In case SPI controller do not report effective_speed_hz, use + * configure value and hope it will match. + */ + if (!priv->effective_speed_hz) + priv->effective_speed_hz = priv->spi->max_speed_hz; + + + priv->scan_interval_us = TI_TSC2046_SAMPLE_INTERVAL_US; + priv->time_per_bit_ns = DIV_ROUND_UP(NSEC_PER_SEC, + priv->effective_speed_hz); + + /* + * Calculate and allocate maximal size buffer if all channels are + * enabled. + */ + size = 0; + for (ch_idx = 0; ch_idx < priv->dcfg->num_channels; ch_idx++) + size += tsc2046_adc_group_set_layout(priv, ch_idx, ch_idx); + + priv->tx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL); + if (!priv->tx) + return -ENOMEM; + + priv->rx = devm_kzalloc(&priv->spi->dev, size, GFP_KERNEL); + if (!priv->rx) + return -ENOMEM; + + priv->xfer.tx_buf = priv->tx; + priv->xfer.rx_buf = priv->rx; + priv->xfer.len = size; + spi_message_init_with_transfers(&priv->msg, &priv->xfer, 1); + + return 0; +} + +static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv) +{ + struct fwnode_handle *child; + struct device *dev = &priv->spi->dev; + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(priv->ch_cfg); i++) { + priv->ch_cfg[i].settling_time_us = 1; + priv->ch_cfg[i].oversampling_ratio = 1; + } + + device_for_each_child_node(dev, child) { + u32 stl, overs, reg; + int ret; + + ret = fwnode_property_read_u32(child, "reg", ®); + if (ret) { + dev_err(dev, "invalid reg on %pfw, err: %pe\n", child, + ERR_PTR(ret)); + continue; + } + + if (reg >= ARRAY_SIZE(priv->ch_cfg)) { + dev_err(dev, "%pfw: Unsupported reg value: %i, max supported is: %zu.\n", + child, reg, ARRAY_SIZE(priv->ch_cfg)); + continue; + } + + ret = fwnode_property_read_u32(child, "settling-time-us", &stl); + if (!ret) + priv->ch_cfg[reg].settling_time_us = stl; + + ret = fwnode_property_read_u32(child, "oversampling-ratio", + &overs); + if (!ret) + priv->ch_cfg[reg].oversampling_ratio = overs; + } +} + +static int tsc2046_adc_probe(struct spi_device *spi) +{ + const struct tsc2046_adc_dcfg *dcfg; + struct device *dev = &spi->dev; + struct tsc2046_adc_priv *priv; + struct iio_dev *indio_dev; + struct iio_trigger *trig; + int ret; + + if (spi->max_speed_hz > TI_TSC2046_MAX_CLK_FREQ) { + dev_err(dev, "SPI max_speed_hz is too high: %d Hz. Max supported freq is %zu Hz\n", + spi->max_speed_hz, TI_TSC2046_MAX_CLK_FREQ); + return -EINVAL; + } + + dcfg = device_get_match_data(dev); + if (!dcfg) + return -EINVAL; + + spi->bits_per_word = 8; + spi->mode &= ~SPI_MODE_X_MASK; + spi->mode |= SPI_MODE_0; + ret = spi_setup(spi); + if (ret < 0) + return dev_err_probe(dev, ret, "Error in SPI setup\n"); + + indio_dev = devm_iio_device_alloc(dev, sizeof(*priv)); + if (!indio_dev) + return -ENOMEM; + + priv = iio_priv(indio_dev); + priv->dcfg = dcfg; + + priv->spi = spi; + + indio_dev->name = TI_TSC2046_NAME; + indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED; + indio_dev->channels = dcfg->channels; + indio_dev->num_channels = dcfg->num_channels; + indio_dev->info = &tsc2046_adc_info; + + tsc2046_adc_parse_fwnode(priv); + + ret = tsc2046_adc_setup_spi_msg(priv); + if (ret) + return ret; + + mutex_init(&priv->slock); + + ret = devm_request_irq(dev, spi->irq, &tsc2046_adc_irq, + IRQF_NO_AUTOEN, indio_dev->name, indio_dev); + if (ret) + return ret; + + trig = devm_iio_trigger_alloc(dev, "touchscreen-%s", indio_dev->name); + if (!trig) + return -ENOMEM; + + priv->trig = trig; + iio_trigger_set_drvdata(trig, indio_dev); + trig->ops = &tsc2046_adc_trigger_ops; + + spin_lock_init(&priv->trig_lock); + hrtimer_init(&priv->trig_timer, CLOCK_MONOTONIC, + HRTIMER_MODE_REL_SOFT); + priv->trig_timer.function = tsc2046_adc_trig_more; + + ret = devm_iio_trigger_register(dev, trig); + if (ret) { + dev_err(dev, "failed to register trigger\n"); + return ret; + } + + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, + &tsc2046_adc_trigger_handler, NULL); + if (ret) { + dev_err(dev, "Failed to setup triggered buffer\n"); + return ret; + } + + /* set default trigger */ + indio_dev->trig = iio_trigger_get(priv->trig); + + return devm_iio_device_register(dev, indio_dev); +} + +static const struct of_device_id ads7950_of_table[] = { + { .compatible = "ti,tsc2046e-adc", .data = &tsc2046_adc_dcfg_tsc2046e }, + { } +}; +MODULE_DEVICE_TABLE(of, ads7950_of_table); + +static struct spi_driver tsc2046_adc_driver = { + .driver = { + .name = "tsc2046", + .of_match_table = ads7950_of_table, + }, + .probe = tsc2046_adc_probe, +}; +module_spi_driver(tsc2046_adc_driver); + +MODULE_AUTHOR("Oleksij Rempel <kernel@pengutronix.de>"); +MODULE_DESCRIPTION("TI TSC2046 ADC"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c index 1d794cf3e3f1..fd57fc43e8e5 100644 --- a/drivers/iio/adc/vf610_adc.c +++ b/drivers/iio/adc/vf610_adc.c @@ -167,7 +167,11 @@ struct vf610_adc { u32 sample_freq_avail[5]; struct completion completion; - u16 buffer[8]; + /* Ensure the timestamp is naturally aligned */ + struct { + u16 chan; + s64 timestamp __aligned(8); + } scan; }; static const u32 vf610_hw_avgs[] = { 1, 4, 8, 16, 32 }; @@ -579,9 +583,9 @@ static irqreturn_t vf610_adc_isr(int irq, void *dev_id) if (coco & VF610_ADC_HS_COCO0) { info->value = vf610_adc_read_data(info); if (iio_buffer_enabled(indio_dev)) { - info->buffer[0] = info->value; + info->scan.chan = info->value; iio_push_to_buffers_with_timestamp(indio_dev, - info->buffer, + &info->scan, iio_get_time_ns(indio_dev)); iio_trigger_notify_done(indio_dev->trig); } else diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c index 6914c1900ed0..198d2916266d 100644 --- a/drivers/iio/adc/xilinx-xadc-core.c +++ b/drivers/iio/adc/xilinx-xadc-core.c @@ -743,7 +743,7 @@ static struct iio_trigger *xadc_alloc_trigger(struct iio_dev *indio_dev, int ret; trig = devm_iio_trigger_alloc(dev, "%s%d-%s", indio_dev->name, - indio_dev->id, name); + iio_device_id(indio_dev), name); if (trig == NULL) return ERR_PTR(-ENOMEM); diff --git a/drivers/iio/afe/iio-rescale.c b/drivers/iio/afe/iio-rescale.c index e42ea2b1707d..774eb3044edd 100644 --- a/drivers/iio/afe/iio-rescale.c +++ b/drivers/iio/afe/iio-rescale.c @@ -29,6 +29,7 @@ struct rescale { struct iio_channel *source; struct iio_chan_spec chan; struct iio_chan_spec_ext_info *ext_info; + bool chan_processed; s32 numerator; s32 denominator; }; @@ -43,10 +44,27 @@ static int rescale_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - return iio_read_channel_raw(rescale->source, val); + if (rescale->chan_processed) + /* + * When only processed channels are supported, we + * read the processed data and scale it by 1/1 + * augmented with whatever the rescaler has calculated. + */ + return iio_read_channel_processed(rescale->source, val); + else + return iio_read_channel_raw(rescale->source, val); case IIO_CHAN_INFO_SCALE: - ret = iio_read_channel_scale(rescale->source, val, val2); + if (rescale->chan_processed) { + /* + * Processed channels are scaled 1-to-1 + */ + *val = 1; + *val2 = 1; + ret = IIO_VAL_FRACTIONAL; + } else { + ret = iio_read_channel_scale(rescale->source, val, val2); + } switch (ret) { case IIO_VAL_FRACTIONAL: *val *= rescale->numerator; @@ -130,16 +148,27 @@ static int rescale_configure_channel(struct device *dev, chan->ext_info = rescale->ext_info; chan->type = rescale->cfg->type; - if (!iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) || - !iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) { - dev_err(dev, "source channel does not support raw/scale\n"); + if (iio_channel_has_info(schan, IIO_CHAN_INFO_RAW) || + iio_channel_has_info(schan, IIO_CHAN_INFO_SCALE)) { + dev_info(dev, "using raw+scale source channel\n"); + } else if (iio_channel_has_info(schan, IIO_CHAN_INFO_PROCESSED)) { + dev_info(dev, "using processed channel\n"); + rescale->chan_processed = true; + } else { + dev_err(dev, "source channel is not supported\n"); return -EINVAL; } chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE); - if (iio_channel_has_available(schan, IIO_CHAN_INFO_RAW)) + /* + * Using .read_avail() is fringe to begin with and makes no sense + * whatsoever for processed channels, so we make sure that this cannot + * be called on a processed channel. + */ + if (iio_channel_has_available(schan, IIO_CHAN_INFO_RAW) && + !rescale->chan_processed) chan->info_mask_separate_available |= BIT(IIO_CHAN_INFO_RAW); return 0; diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c index d76179878ff9..1ac94c4e9792 100644 --- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c +++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c @@ -228,9 +228,9 @@ static void iio_dmaengine_buffer_free(struct iio_buffer *buffer) iio_buffer_put(buffer); } -static void __devm_iio_dmaengine_buffer_free(struct device *dev, void *res) +static void __devm_iio_dmaengine_buffer_free(void *buffer) { - iio_dmaengine_buffer_free(*(struct iio_buffer **)res); + iio_dmaengine_buffer_free(buffer); } /** @@ -247,21 +247,17 @@ static void __devm_iio_dmaengine_buffer_free(struct device *dev, void *res) static struct iio_buffer *devm_iio_dmaengine_buffer_alloc(struct device *dev, const char *channel) { - struct iio_buffer **bufferp, *buffer; - - bufferp = devres_alloc(__devm_iio_dmaengine_buffer_free, - sizeof(*bufferp), GFP_KERNEL); - if (!bufferp) - return ERR_PTR(-ENOMEM); + struct iio_buffer *buffer; + int ret; buffer = iio_dmaengine_buffer_alloc(dev, channel); - if (IS_ERR(buffer)) { - devres_free(bufferp); + if (IS_ERR(buffer)) return buffer; - } - *bufferp = buffer; - devres_add(dev, bufferp); + ret = devm_add_action_or_reset(dev, __devm_iio_dmaengine_buffer_free, + buffer); + if (ret) + return ERR_PTR(ret); return buffer; } diff --git a/drivers/iio/buffer/industrialio-hw-consumer.c b/drivers/iio/buffer/industrialio-hw-consumer.c index f2d27788f666..87d9aabd20c7 100644 --- a/drivers/iio/buffer/industrialio-hw-consumer.c +++ b/drivers/iio/buffer/industrialio-hw-consumer.c @@ -137,9 +137,9 @@ void iio_hw_consumer_free(struct iio_hw_consumer *hwc) } EXPORT_SYMBOL_GPL(iio_hw_consumer_free); -static void devm_iio_hw_consumer_release(struct device *dev, void *res) +static void devm_iio_hw_consumer_release(void *iio_hwc) { - iio_hw_consumer_free(*(struct iio_hw_consumer **)res); + iio_hw_consumer_free(iio_hwc); } /** @@ -153,20 +153,17 @@ static void devm_iio_hw_consumer_release(struct device *dev, void *res) */ struct iio_hw_consumer *devm_iio_hw_consumer_alloc(struct device *dev) { - struct iio_hw_consumer **ptr, *iio_hwc; - - ptr = devres_alloc(devm_iio_hw_consumer_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return NULL; + struct iio_hw_consumer *iio_hwc; + int ret; iio_hwc = iio_hw_consumer_alloc(dev); - if (IS_ERR(iio_hwc)) { - devres_free(ptr); - } else { - *ptr = iio_hwc; - devres_add(dev, ptr); - } + if (IS_ERR(iio_hwc)) + return iio_hwc; + + ret = devm_add_action_or_reset(dev, devm_iio_hw_consumer_release, + iio_hwc); + if (ret) + return ERR_PTR(ret); return iio_hwc; } diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c index b2b1b7d27af4..f77c4538141e 100644 --- a/drivers/iio/buffer/industrialio-triggered-buffer.c +++ b/drivers/iio/buffer/industrialio-triggered-buffer.c @@ -56,7 +56,7 @@ int iio_triggered_buffer_setup_ext(struct iio_dev *indio_dev, indio_dev, "%s_consumer%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (indio_dev->pollfunc == NULL) { ret = -ENOMEM; goto error_kfifo_free; @@ -96,9 +96,9 @@ void iio_triggered_buffer_cleanup(struct iio_dev *indio_dev) } EXPORT_SYMBOL(iio_triggered_buffer_cleanup); -static void devm_iio_triggered_buffer_clean(struct device *dev, void *res) +static void devm_iio_triggered_buffer_clean(void *indio_dev) { - iio_triggered_buffer_cleanup(*(struct iio_dev **)res); + iio_triggered_buffer_cleanup(indio_dev); } int devm_iio_triggered_buffer_setup_ext(struct device *dev, @@ -108,24 +108,15 @@ int devm_iio_triggered_buffer_setup_ext(struct device *dev, const struct iio_buffer_setup_ops *ops, const struct attribute **buffer_attrs) { - struct iio_dev **ptr; int ret; - ptr = devres_alloc(devm_iio_triggered_buffer_clean, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - *ptr = indio_dev; - ret = iio_triggered_buffer_setup_ext(indio_dev, h, thread, ops, buffer_attrs); - if (!ret) - devres_add(dev, ptr); - else - devres_free(ptr); + if (ret) + return ret; - return ret; + return devm_add_action_or_reset(dev, devm_iio_triggered_buffer_clean, + indio_dev); } EXPORT_SYMBOL_GPL(devm_iio_triggered_buffer_setup_ext); diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig index 10bb431bc3ce..a4920646e9be 100644 --- a/drivers/iio/chemical/Kconfig +++ b/drivers/iio/chemical/Kconfig @@ -132,17 +132,32 @@ config SENSIRION_SGP30 module will be called sgp30. config SPS30 - tristate "SPS30 particulate matter sensor" - depends on I2C - select CRC8 + tristate select IIO_BUFFER select IIO_TRIGGERED_BUFFER + +config SPS30_I2C + tristate "SPS30 particulate matter sensor I2C driver" + depends on I2C + select SPS30 + select CRC8 help - Say Y here to build support for the Sensirion SPS30 particulate - matter sensor. + Say Y here to build support for the Sensirion SPS30 I2C interface + driver. + + To compile this driver as a module, choose M here: the module will + be called sps30_i2c. + +config SPS30_SERIAL + tristate "SPS30 particulate matter sensor serial driver" + depends on SERIAL_DEV_BUS + select SPS30 + help + Say Y here to build support for the Sensirion SPS30 serial interface + driver. To compile this driver as a module, choose M here: the module will - be called sps30. + be called sps30_serial. config VZ89X tristate "SGX Sensortech MiCS VZ89X VOC sensor" diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile index fef63dd5bf92..4898690cc155 100644 --- a/drivers/iio/chemical/Makefile +++ b/drivers/iio/chemical/Makefile @@ -17,4 +17,6 @@ obj-$(CONFIG_SCD30_I2C) += scd30_i2c.o obj-$(CONFIG_SCD30_SERIAL) += scd30_serial.o obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o obj-$(CONFIG_SPS30) += sps30.o +obj-$(CONFIG_SPS30_I2C) += sps30_i2c.o +obj-$(CONFIG_SPS30_SERIAL) += sps30_serial.o obj-$(CONFIG_VZ89X) += vz89x.o diff --git a/drivers/iio/chemical/atlas-sensor.c b/drivers/iio/chemical/atlas-sensor.c index 56ba6c82b501..9cb99585b6ff 100644 --- a/drivers/iio/chemical/atlas-sensor.c +++ b/drivers/iio/chemical/atlas-sensor.c @@ -91,8 +91,8 @@ struct atlas_data { struct regmap *regmap; struct irq_work work; unsigned int interrupt_enabled; - - __be32 buffer[6]; /* 96-bit data + 32-bit pad + 64-bit timestamp */ + /* 96-bit data + 32-bit pad + 64-bit timestamp */ + __be32 buffer[6] __aligned(8); }; static const struct regmap_config atlas_regmap_config = { @@ -410,11 +410,9 @@ static int atlas_buffer_postenable(struct iio_dev *indio_dev) struct atlas_data *data = iio_priv(indio_dev); int ret; - ret = pm_runtime_get_sync(&data->client->dev); - if (ret < 0) { - pm_runtime_put_noidle(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); + if (ret) return ret; - } return atlas_set_interrupt(data, true); } @@ -487,11 +485,9 @@ static int atlas_read_measurement(struct atlas_data *data, int reg, __be32 *val) int suspended = pm_runtime_suspended(dev); int ret; - ret = pm_runtime_get_sync(dev); - if (ret < 0) { - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); + if (ret) return ret; - } if (suspended) msleep(data->chip->delay); @@ -640,7 +636,7 @@ static int atlas_probe(struct i2c_client *client, indio_dev->modes = INDIO_BUFFER_SOFTWARE | INDIO_DIRECT_MODE; trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, iio_device_id(indio_dev)); if (!trig) return -ENOMEM; @@ -741,7 +737,6 @@ static int atlas_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); return atlas_set_powermode(data, 0); } diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c index 29c0dfa4702b..74cf89c82c0a 100644 --- a/drivers/iio/chemical/bme680_i2c.c +++ b/drivers/iio/chemical/bme680_i2c.c @@ -11,7 +11,6 @@ * Note: SDO pin cannot be left floating otherwise I2C address * will be undefined. */ -#include <linux/acpi.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/regmap.h> @@ -42,12 +41,6 @@ static const struct i2c_device_id bme680_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, bme680_i2c_id); -static const struct acpi_device_id bme680_acpi_match[] = { - {"BME0680", 0}, - {}, -}; -MODULE_DEVICE_TABLE(acpi, bme680_acpi_match); - static const struct of_device_id bme680_of_i2c_match[] = { { .compatible = "bosch,bme680", }, {}, @@ -57,7 +50,6 @@ MODULE_DEVICE_TABLE(of, bme680_of_i2c_match); static struct i2c_driver bme680_i2c_driver = { .driver = { .name = "bme680_i2c", - .acpi_match_table = ACPI_PTR(bme680_acpi_match), .of_match_table = bme680_of_i2c_match, }, .probe = bme680_i2c_probe, diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c index 6f56ad48cc40..cc579a7ac5ce 100644 --- a/drivers/iio/chemical/bme680_spi.c +++ b/drivers/iio/chemical/bme680_spi.c @@ -4,7 +4,6 @@ * * Copyright (C) 2018 Himanshu Jha <himanshujha199640@gmail.com> */ -#include <linux/acpi.h> #include <linux/module.h> #include <linux/of.h> #include <linux/regmap.h> @@ -145,12 +144,6 @@ static const struct spi_device_id bme680_spi_id[] = { }; MODULE_DEVICE_TABLE(spi, bme680_spi_id); -static const struct acpi_device_id bme680_acpi_match[] = { - {"BME0680", 0}, - {}, -}; -MODULE_DEVICE_TABLE(acpi, bme680_acpi_match); - static const struct of_device_id bme680_of_spi_match[] = { { .compatible = "bosch,bme680", }, {}, @@ -160,7 +153,6 @@ MODULE_DEVICE_TABLE(of, bme680_of_spi_match); static struct spi_driver bme680_spi_driver = { .driver = { .name = "bme680_spi", - .acpi_match_table = ACPI_PTR(bme680_acpi_match), .of_match_table = bme680_of_spi_match, }, .probe = bme680_spi_probe, diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index 886e96496dbf..847194fa1e46 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -491,7 +491,7 @@ static int ccs811_probe(struct i2c_client *client, data->drdy_trig = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->drdy_trig) { ret = -ENOMEM; goto err_poweroff; diff --git a/drivers/iio/chemical/scd30_core.c b/drivers/iio/chemical/scd30_core.c index d89f117dd0ef..9fe6bbe9ee04 100644 --- a/drivers/iio/chemical/scd30_core.c +++ b/drivers/iio/chemical/scd30_core.c @@ -640,7 +640,8 @@ static int scd30_setup_trigger(struct iio_dev *indio_dev) struct iio_trigger *trig; int ret; - trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); + trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); if (!trig) { dev_err(dev, "failed to allocate trigger\n"); return -ENOMEM; diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c index 1029c457be15..2343d444604d 100644 --- a/drivers/iio/chemical/sgp30.c +++ b/drivers/iio/chemical/sgp30.c @@ -425,7 +425,7 @@ static int sgp_check_compat(struct sgp_data *data, product = SGP_VERS_PRODUCT(data); if (product != product_id) { - dev_err(dev, "sensor reports a different product: 0x%04hx\n", + dev_err(dev, "sensor reports a different product: 0x%04x\n", product); return -ENODEV; } diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c index 2ea9a5c4d846..d51314505115 100644 --- a/drivers/iio/chemical/sps30.c +++ b/drivers/iio/chemical/sps30.c @@ -3,11 +3,8 @@ * Sensirion SPS30 particulate matter sensor driver * * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com> - * - * I2C slave address: 0x69 */ -#include <asm/unaligned.h> #include <linux/crc8.h> #include <linux/delay.h> #include <linux/i2c.h> @@ -19,27 +16,14 @@ #include <linux/kernel.h> #include <linux/module.h> -#define SPS30_CRC8_POLYNOMIAL 0x31 -/* max number of bytes needed to store PM measurements or serial string */ -#define SPS30_MAX_READ_SIZE 48 +#include "sps30.h" + /* sensor measures reliably up to 3000 ug / m3 */ #define SPS30_MAX_PM 3000 /* minimum and maximum self cleaning periods in seconds */ #define SPS30_AUTO_CLEANING_PERIOD_MIN 0 #define SPS30_AUTO_CLEANING_PERIOD_MAX 604800 -/* SPS30 commands */ -#define SPS30_START_MEAS 0x0010 -#define SPS30_STOP_MEAS 0x0104 -#define SPS30_RESET 0xd304 -#define SPS30_READ_DATA_READY_FLAG 0x0202 -#define SPS30_READ_DATA 0x0300 -#define SPS30_READ_SERIAL 0xd033 -#define SPS30_START_FAN_CLEANING 0x5607 -#define SPS30_AUTO_CLEANING_PERIOD 0x8004 -/* not a sensor command per se, used only to distinguish write from read */ -#define SPS30_READ_AUTO_CLEANING_PERIOD 0x8005 - enum { PM1, PM2P5, @@ -52,114 +36,9 @@ enum { MEASURING, }; -struct sps30_state { - struct i2c_client *client; - /* - * Guards against concurrent access to sensor registers. - * Must be held whenever sequence of commands is to be executed. - */ - struct mutex lock; - int state; -}; - -DECLARE_CRC8_TABLE(sps30_crc8_table); - -static int sps30_write_then_read(struct sps30_state *state, u8 *txbuf, - int txsize, u8 *rxbuf, int rxsize) -{ - int ret; - - /* - * Sensor does not support repeated start so instead of - * sending two i2c messages in a row we just send one by one. - */ - ret = i2c_master_send(state->client, txbuf, txsize); - if (ret != txsize) - return ret < 0 ? ret : -EIO; - - if (!rxbuf) - return 0; - - ret = i2c_master_recv(state->client, rxbuf, rxsize); - if (ret != rxsize) - return ret < 0 ? ret : -EIO; - - return 0; -} - -static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size) -{ - /* - * Internally sensor stores measurements in a following manner: - * - * PM1: upper two bytes, crc8, lower two bytes, crc8 - * PM2P5: upper two bytes, crc8, lower two bytes, crc8 - * PM4: upper two bytes, crc8, lower two bytes, crc8 - * PM10: upper two bytes, crc8, lower two bytes, crc8 - * - * What follows next are number concentration measurements and - * typical particle size measurement which we omit. - */ - u8 buf[SPS30_MAX_READ_SIZE] = { cmd >> 8, cmd }; - int i, ret = 0; - - switch (cmd) { - case SPS30_START_MEAS: - buf[2] = 0x03; - buf[3] = 0x00; - buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE); - ret = sps30_write_then_read(state, buf, 5, NULL, 0); - break; - case SPS30_STOP_MEAS: - case SPS30_RESET: - case SPS30_START_FAN_CLEANING: - ret = sps30_write_then_read(state, buf, 2, NULL, 0); - break; - case SPS30_READ_AUTO_CLEANING_PERIOD: - buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8; - buf[1] = (u8)(SPS30_AUTO_CLEANING_PERIOD & 0xff); - fallthrough; - case SPS30_READ_DATA_READY_FLAG: - case SPS30_READ_DATA: - case SPS30_READ_SERIAL: - /* every two data bytes are checksummed */ - size += size / 2; - ret = sps30_write_then_read(state, buf, 2, buf, size); - break; - case SPS30_AUTO_CLEANING_PERIOD: - buf[2] = data[0]; - buf[3] = data[1]; - buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE); - buf[5] = data[2]; - buf[6] = data[3]; - buf[7] = crc8(sps30_crc8_table, &buf[5], 2, CRC8_INIT_VALUE); - ret = sps30_write_then_read(state, buf, 8, NULL, 0); - break; - } - - if (ret) - return ret; - - /* validate received data and strip off crc bytes */ - for (i = 0; i < size; i += 3) { - u8 crc = crc8(sps30_crc8_table, &buf[i], 2, CRC8_INIT_VALUE); - - if (crc != buf[i + 2]) { - dev_err(&state->client->dev, - "data integrity check failed\n"); - return -EIO; - } - - *data++ = buf[i]; - *data++ = buf[i + 1]; - } - - return 0; -} - -static s32 sps30_float_to_int_clamped(const u8 *fp) +static s32 sps30_float_to_int_clamped(__be32 *fp) { - int val = get_unaligned_be32(fp); + int val = be32_to_cpup(fp); int mantissa = val & GENMASK(22, 0); /* this is fine since passed float is always non-negative */ int exp = val >> 23; @@ -188,38 +67,35 @@ static s32 sps30_float_to_int_clamped(const u8 *fp) static int sps30_do_meas(struct sps30_state *state, s32 *data, int size) { - int i, ret, tries = 5; - u8 tmp[16]; + int i, ret; if (state->state == RESET) { - ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0); + ret = state->ops->start_meas(state); if (ret) return ret; state->state = MEASURING; } - while (tries--) { - ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2); - if (ret) - return -EIO; + ret = state->ops->read_meas(state, (__be32 *)data, size); + if (ret) + return ret; - /* new measurements ready to be read */ - if (tmp[1] == 1) - break; + for (i = 0; i < size; i++) + data[i] = sps30_float_to_int_clamped((__be32 *)&data[i]); - msleep_interruptible(300); - } + return 0; +} - if (tries == -1) - return -ETIMEDOUT; +static int sps30_do_reset(struct sps30_state *state) +{ + int ret; - ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size); + ret = state->ops->reset(state); if (ret) return ret; - for (i = 0; i < size; i++) - data[i] = sps30_float_to_int_clamped(&tmp[4 * i]); + state->state = RESET; return 0; } @@ -310,24 +186,6 @@ static int sps30_read_raw(struct iio_dev *indio_dev, return -EINVAL; } -static int sps30_do_cmd_reset(struct sps30_state *state) -{ - int ret; - - ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0); - msleep(300); - /* - * Power-on-reset causes sensor to produce some glitch on i2c bus and - * some controllers end up in error state. Recover simply by placing - * some data on the bus, for example STOP_MEAS command, which - * is NOP in this case. - */ - sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0); - state->state = RESET; - - return ret; -} - static ssize_t start_cleaning_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) @@ -340,7 +198,7 @@ static ssize_t start_cleaning_store(struct device *dev, return -EINVAL; mutex_lock(&state->lock); - ret = sps30_do_cmd(state, SPS30_START_FAN_CLEANING, NULL, 0); + ret = state->ops->clean_fan(state); mutex_unlock(&state->lock); if (ret) return ret; @@ -349,31 +207,29 @@ static ssize_t start_cleaning_store(struct device *dev, } static ssize_t cleaning_period_show(struct device *dev, - struct device_attribute *attr, - char *buf) + struct device_attribute *attr, + char *buf) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct sps30_state *state = iio_priv(indio_dev); - u8 tmp[4]; + __be32 val; int ret; mutex_lock(&state->lock); - ret = sps30_do_cmd(state, SPS30_READ_AUTO_CLEANING_PERIOD, tmp, 4); + ret = state->ops->read_cleaning_period(state, &val); mutex_unlock(&state->lock); if (ret) return ret; - return sprintf(buf, "%d\n", get_unaligned_be32(tmp)); + return sprintf(buf, "%d\n", be32_to_cpu(val)); } -static ssize_t cleaning_period_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t len) +static ssize_t cleaning_period_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct sps30_state *state = iio_priv(indio_dev); int val, ret; - u8 tmp[4]; if (kstrtoint(buf, 0, &val)) return -EINVAL; @@ -382,10 +238,8 @@ static ssize_t cleaning_period_store(struct device *dev, (val > SPS30_AUTO_CLEANING_PERIOD_MAX)) return -EINVAL; - put_unaligned_be32(val, tmp); - mutex_lock(&state->lock); - ret = sps30_do_cmd(state, SPS30_AUTO_CLEANING_PERIOD, tmp, 0); + ret = state->ops->write_cleaning_period(state, cpu_to_be32(val)); if (ret) { mutex_unlock(&state->lock); return ret; @@ -397,7 +251,7 @@ static ssize_t cleaning_period_store(struct device *dev, * sensor requires reset in order to return up to date self cleaning * period */ - ret = sps30_do_cmd_reset(state); + ret = sps30_do_reset(state); if (ret) dev_warn(dev, "period changed but reads will return the old value\n"); @@ -411,9 +265,9 @@ static ssize_t cleaning_period_available_show(struct device *dev, struct device_attribute *attr, char *buf) { - return snprintf(buf, PAGE_SIZE, "[%d %d %d]\n", - SPS30_AUTO_CLEANING_PERIOD_MIN, 1, - SPS30_AUTO_CLEANING_PERIOD_MAX); + return sysfs_emit(buf, "[%d %d %d]\n", + SPS30_AUTO_CLEANING_PERIOD_MIN, 1, + SPS30_AUTO_CLEANING_PERIOD_MAX); } static IIO_DEVICE_ATTR_WO(start_cleaning, 0); @@ -460,90 +314,65 @@ static const struct iio_chan_spec sps30_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(4), }; -static void sps30_stop_meas(void *data) +static void sps30_devm_stop_meas(void *data) { struct sps30_state *state = data; - sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0); + if (state->state == MEASURING) + state->ops->stop_meas(state); } static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 }; -static int sps30_probe(struct i2c_client *client) +int sps30_probe(struct device *dev, const char *name, void *priv, const struct sps30_ops *ops) { struct iio_dev *indio_dev; struct sps30_state *state; - u8 buf[32]; int ret; - if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) - return -EOPNOTSUPP; - - indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state)); + indio_dev = devm_iio_device_alloc(dev, sizeof(*state)); if (!indio_dev) return -ENOMEM; + dev_set_drvdata(dev, indio_dev); + state = iio_priv(indio_dev); - i2c_set_clientdata(client, indio_dev); - state->client = client; - state->state = RESET; + state->dev = dev; + state->priv = priv; + state->ops = ops; + mutex_init(&state->lock); + indio_dev->info = &sps30_info; - indio_dev->name = client->name; + indio_dev->name = name; indio_dev->channels = sps30_channels; indio_dev->num_channels = ARRAY_SIZE(sps30_channels); indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->available_scan_masks = sps30_scan_masks; - mutex_init(&state->lock); - crc8_populate_msb(sps30_crc8_table, SPS30_CRC8_POLYNOMIAL); - - ret = sps30_do_cmd_reset(state); + ret = sps30_do_reset(state); if (ret) { - dev_err(&client->dev, "failed to reset device\n"); + dev_err(dev, "failed to reset device\n"); return ret; } - ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf)); + ret = state->ops->show_info(state); if (ret) { - dev_err(&client->dev, "failed to read serial number\n"); + dev_err(dev, "failed to read device info\n"); return ret; } - /* returned serial number is already NUL terminated */ - dev_info(&client->dev, "serial number: %s\n", buf); - ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state); + ret = devm_add_action_or_reset(dev, sps30_devm_stop_meas, state); if (ret) return ret; - ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL, + ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL, sps30_trigger_handler, NULL); if (ret) return ret; - return devm_iio_device_register(&client->dev, indio_dev); + return devm_iio_device_register(dev, indio_dev); } - -static const struct i2c_device_id sps30_id[] = { - { "sps30" }, - { } -}; -MODULE_DEVICE_TABLE(i2c, sps30_id); - -static const struct of_device_id sps30_of_match[] = { - { .compatible = "sensirion,sps30" }, - { } -}; -MODULE_DEVICE_TABLE(of, sps30_of_match); - -static struct i2c_driver sps30_driver = { - .driver = { - .name = "sps30", - .of_match_table = sps30_of_match, - }, - .id_table = sps30_id, - .probe_new = sps30_probe, -}; -module_i2c_driver(sps30_driver); +EXPORT_SYMBOL_GPL(sps30_probe); MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>"); MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver"); diff --git a/drivers/iio/chemical/sps30.h b/drivers/iio/chemical/sps30.h new file mode 100644 index 000000000000..a58ee43cf45d --- /dev/null +++ b/drivers/iio/chemical/sps30.h @@ -0,0 +1,35 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _SPS30_H +#define _SPS30_H + +#include <linux/types.h> + +struct sps30_state; +struct sps30_ops { + int (*start_meas)(struct sps30_state *state); + int (*stop_meas)(struct sps30_state *state); + int (*read_meas)(struct sps30_state *state, __be32 *meas, size_t num); + int (*reset)(struct sps30_state *state); + int (*clean_fan)(struct sps30_state *state); + int (*read_cleaning_period)(struct sps30_state *state, __be32 *period); + int (*write_cleaning_period)(struct sps30_state *state, __be32 period); + int (*show_info)(struct sps30_state *state); +}; + +struct sps30_state { + /* serialize access to the device */ + struct mutex lock; + struct device *dev; + int state; + /* + * priv pointer is solely for serdev driver private data. We keep it + * here because driver_data inside dev has been already used for iio and + * struct serdev_device doesn't have one. + */ + void *priv; + const struct sps30_ops *ops; +}; + +int sps30_probe(struct device *dev, const char *name, void *priv, const struct sps30_ops *ops); + +#endif diff --git a/drivers/iio/chemical/sps30_i2c.c b/drivers/iio/chemical/sps30_i2c.c new file mode 100644 index 000000000000..d33560ed7184 --- /dev/null +++ b/drivers/iio/chemical/sps30_i2c.c @@ -0,0 +1,258 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SPS30 particulate matter sensor i2c driver + * + * Copyright (c) 2020 Tomasz Duszynski <tomasz.duszynski@octakon.com> + * + * I2C slave address: 0x69 + */ +#include <asm/unaligned.h> +#include <linux/crc8.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/i2c.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/types.h> + +#include "sps30.h" + +#define SPS30_I2C_CRC8_POLYNOMIAL 0x31 +/* max number of bytes needed to store PM measurements or serial string */ +#define SPS30_I2C_MAX_BUF_SIZE 48 + +DECLARE_CRC8_TABLE(sps30_i2c_crc8_table); + +#define SPS30_I2C_START_MEAS 0x0010 +#define SPS30_I2C_STOP_MEAS 0x0104 +#define SPS30_I2C_READ_MEAS 0x0300 +#define SPS30_I2C_MEAS_READY 0x0202 +#define SPS30_I2C_RESET 0xd304 +#define SPS30_I2C_CLEAN_FAN 0x5607 +#define SPS30_I2C_PERIOD 0x8004 +#define SPS30_I2C_READ_SERIAL 0xd033 +#define SPS30_I2C_READ_VERSION 0xd100 + +static int sps30_i2c_xfer(struct sps30_state *state, unsigned char *txbuf, size_t txsize, + unsigned char *rxbuf, size_t rxsize) +{ + struct i2c_client *client = to_i2c_client(state->dev); + int ret; + + /* + * Sensor does not support repeated start so instead of + * sending two i2c messages in a row we just send one by one. + */ + ret = i2c_master_send(client, txbuf, txsize); + if (ret < 0) + return ret; + if (ret != txsize) + return -EIO; + + if (!rxsize) + return 0; + + ret = i2c_master_recv(client, rxbuf, rxsize); + if (ret < 0) + return ret; + if (ret != rxsize) + return -EIO; + + return 0; +} + +static int sps30_i2c_command(struct sps30_state *state, u16 cmd, void *arg, size_t arg_size, + void *rsp, size_t rsp_size) +{ + /* + * Internally sensor stores measurements in a following manner: + * + * PM1: upper two bytes, crc8, lower two bytes, crc8 + * PM2P5: upper two bytes, crc8, lower two bytes, crc8 + * PM4: upper two bytes, crc8, lower two bytes, crc8 + * PM10: upper two bytes, crc8, lower two bytes, crc8 + * + * What follows next are number concentration measurements and + * typical particle size measurement which we omit. + */ + unsigned char buf[SPS30_I2C_MAX_BUF_SIZE]; + unsigned char *tmp; + unsigned char crc; + size_t i; + int ret; + + put_unaligned_be16(cmd, buf); + i = 2; + + if (rsp) { + /* each two bytes are followed by a crc8 */ + rsp_size += rsp_size / 2; + } else { + tmp = arg; + + while (arg_size) { + buf[i] = *tmp++; + buf[i + 1] = *tmp++; + buf[i + 2] = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE); + arg_size -= 2; + i += 3; + } + } + + ret = sps30_i2c_xfer(state, buf, i, buf, rsp_size); + if (ret) + return ret; + + /* validate received data and strip off crc bytes */ + tmp = rsp; + for (i = 0; i < rsp_size; i += 3) { + crc = crc8(sps30_i2c_crc8_table, buf + i, 2, CRC8_INIT_VALUE); + if (crc != buf[i + 2]) { + dev_err(state->dev, "data integrity check failed\n"); + return -EIO; + } + + *tmp++ = buf[i]; + *tmp++ = buf[i + 1]; + } + + return 0; +} + +static int sps30_i2c_start_meas(struct sps30_state *state) +{ + /* request BE IEEE754 formatted data */ + unsigned char buf[] = { 0x03, 0x00 }; + + return sps30_i2c_command(state, SPS30_I2C_START_MEAS, buf, sizeof(buf), NULL, 0); +} + +static int sps30_i2c_stop_meas(struct sps30_state *state) +{ + return sps30_i2c_command(state, SPS30_I2C_STOP_MEAS, NULL, 0, NULL, 0); +} + +static int sps30_i2c_reset(struct sps30_state *state) +{ + int ret; + + ret = sps30_i2c_command(state, SPS30_I2C_RESET, NULL, 0, NULL, 0); + msleep(500); + /* + * Power-on-reset causes sensor to produce some glitch on i2c bus and + * some controllers end up in error state. Recover simply by placing + * some data on the bus, for example STOP_MEAS command, which + * is NOP in this case. + */ + sps30_i2c_stop_meas(state); + + return ret; +} + +static bool sps30_i2c_meas_ready(struct sps30_state *state) +{ + unsigned char buf[2]; + int ret; + + ret = sps30_i2c_command(state, SPS30_I2C_MEAS_READY, NULL, 0, buf, sizeof(buf)); + if (ret) + return false; + + return buf[1]; +} + +static int sps30_i2c_read_meas(struct sps30_state *state, __be32 *meas, size_t num) +{ + /* measurements are ready within a second */ + if (msleep_interruptible(1000)) + return -EINTR; + + if (!sps30_i2c_meas_ready(state)) + return -ETIMEDOUT; + + return sps30_i2c_command(state, SPS30_I2C_READ_MEAS, NULL, 0, meas, sizeof(num) * num); +} + +static int sps30_i2c_clean_fan(struct sps30_state *state) +{ + return sps30_i2c_command(state, SPS30_I2C_CLEAN_FAN, NULL, 0, NULL, 0); +} + +static int sps30_i2c_read_cleaning_period(struct sps30_state *state, __be32 *period) +{ + return sps30_i2c_command(state, SPS30_I2C_PERIOD, NULL, 0, period, sizeof(*period)); +} + +static int sps30_i2c_write_cleaning_period(struct sps30_state *state, __be32 period) +{ + return sps30_i2c_command(state, SPS30_I2C_PERIOD, &period, sizeof(period), NULL, 0); +} + +static int sps30_i2c_show_info(struct sps30_state *state) +{ + /* extra nul just in case */ + unsigned char buf[32 + 1] = { 0x00 }; + int ret; + + ret = sps30_i2c_command(state, SPS30_I2C_READ_SERIAL, NULL, 0, buf, sizeof(buf) - 1); + if (ret) + return ret; + + dev_info(state->dev, "serial number: %s\n", buf); + + ret = sps30_i2c_command(state, SPS30_I2C_READ_VERSION, NULL, 0, buf, 2); + if (ret) + return ret; + + dev_info(state->dev, "fw version: %u.%u\n", buf[0], buf[1]); + + return 0; +} + +static const struct sps30_ops sps30_i2c_ops = { + .start_meas = sps30_i2c_start_meas, + .stop_meas = sps30_i2c_stop_meas, + .read_meas = sps30_i2c_read_meas, + .reset = sps30_i2c_reset, + .clean_fan = sps30_i2c_clean_fan, + .read_cleaning_period = sps30_i2c_read_cleaning_period, + .write_cleaning_period = sps30_i2c_write_cleaning_period, + .show_info = sps30_i2c_show_info, +}; + +static int sps30_i2c_probe(struct i2c_client *client) +{ + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -EOPNOTSUPP; + + crc8_populate_msb(sps30_i2c_crc8_table, SPS30_I2C_CRC8_POLYNOMIAL); + + return sps30_probe(&client->dev, client->name, NULL, &sps30_i2c_ops); +} + +static const struct i2c_device_id sps30_i2c_id[] = { + { "sps30" }, + { } +}; +MODULE_DEVICE_TABLE(i2c, sps30_i2c_id); + +static const struct of_device_id sps30_i2c_of_match[] = { + { .compatible = "sensirion,sps30" }, + { } +}; +MODULE_DEVICE_TABLE(of, sps30_i2c_of_match); + +static struct i2c_driver sps30_i2c_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = sps30_i2c_of_match, + }, + .id_table = sps30_i2c_id, + .probe_new = sps30_i2c_probe, +}; +module_i2c_driver(sps30_i2c_driver); + +MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor i2c driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/sps30_serial.c b/drivers/iio/chemical/sps30_serial.c new file mode 100644 index 000000000000..3f311d50087c --- /dev/null +++ b/drivers/iio/chemical/sps30_serial.c @@ -0,0 +1,431 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Sensirion SPS30 particulate matter sensor serial driver + * + * Copyright (c) 2021 Tomasz Duszynski <tomasz.duszynski@octakon.com> + */ +#include <linux/completion.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/iio/iio.h> +#include <linux/minmax.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/serdev.h> +#include <linux/types.h> + +#include "sps30.h" + +#define SPS30_SERIAL_DEV_NAME "sps30" + +#define SPS30_SERIAL_SOF_EOF 0x7e +#define SPS30_SERIAL_TIMEOUT msecs_to_jiffies(20) +#define SPS30_SERIAL_MAX_BUF_SIZE 263 +#define SPS30_SERIAL_ESCAPE_CHAR 0x7d + +#define SPS30_SERIAL_FRAME_MIN_SIZE 7 +#define SPS30_SERIAL_FRAME_ADR_OFFSET 1 +#define SPS30_SERIAL_FRAME_CMD_OFFSET 2 +#define SPS30_SERIAL_FRAME_MOSI_LEN_OFFSET 3 +#define SPS30_SERIAL_FRAME_MISO_STATE_OFFSET 3 +#define SPS30_SERIAL_FRAME_MISO_LEN_OFFSET 4 +#define SPS30_SERIAL_FRAME_MISO_DATA_OFFSET 5 + +#define SPS30_SERIAL_START_MEAS 0x00 +#define SPS30_SERIAL_STOP_MEAS 0x01 +#define SPS30_SERIAL_READ_MEAS 0x03 +#define SPS30_SERIAL_RESET 0xd3 +#define SPS30_SERIAL_CLEAN_FAN 0x56 +#define SPS30_SERIAL_PERIOD 0x80 +#define SPS30_SERIAL_DEV_INFO 0xd0 +#define SPS30_SERIAL_READ_VERSION 0xd1 + +struct sps30_serial_priv { + struct completion new_frame; + unsigned char buf[SPS30_SERIAL_MAX_BUF_SIZE]; + size_t num; + bool escaped; + bool done; +}; + +static int sps30_serial_xfer(struct sps30_state *state, const unsigned char *buf, size_t size) +{ + struct serdev_device *serdev = to_serdev_device(state->dev); + struct sps30_serial_priv *priv = state->priv; + int ret; + + priv->num = 0; + priv->escaped = false; + priv->done = false; + + ret = serdev_device_write(serdev, buf, size, SPS30_SERIAL_TIMEOUT); + if (ret < 0) + return ret; + if (ret != size) + return -EIO; + + ret = wait_for_completion_interruptible_timeout(&priv->new_frame, SPS30_SERIAL_TIMEOUT); + if (ret < 0) + return ret; + if (!ret) + return -ETIMEDOUT; + + return 0; +} + +static const struct { + unsigned char byte; + unsigned char byte2; +} sps30_serial_bytes[] = { + { 0x11, 0x31 }, + { 0x13, 0x33 }, + { 0x7e, 0x5e }, + { 0x7d, 0x5d }, +}; + +static int sps30_serial_put_byte(unsigned char *buf, unsigned char byte) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(sps30_serial_bytes); i++) { + if (sps30_serial_bytes[i].byte != byte) + continue; + + buf[0] = SPS30_SERIAL_ESCAPE_CHAR; + buf[1] = sps30_serial_bytes[i].byte2; + + return 2; + } + + buf[0] = byte; + + return 1; +} + +static char sps30_serial_get_byte(bool escaped, unsigned char byte2) +{ + int i; + + if (!escaped) + return byte2; + + for (i = 0; i < ARRAY_SIZE(sps30_serial_bytes); i++) { + if (sps30_serial_bytes[i].byte2 != byte2) + continue; + + return sps30_serial_bytes[i].byte; + } + + return 0; +} + +static unsigned char sps30_serial_calc_chksum(const unsigned char *buf, size_t num) +{ + unsigned int chksum = 0; + size_t i; + + for (i = 0; i < num; i++) + chksum += buf[i]; + + return ~chksum; +} + +static int sps30_serial_prep_frame(unsigned char *buf, unsigned char cmd, + const unsigned char *arg, size_t arg_size) +{ + unsigned char chksum; + int num = 0; + size_t i; + + buf[num++] = SPS30_SERIAL_SOF_EOF; + buf[num++] = 0; + num += sps30_serial_put_byte(buf + num, cmd); + num += sps30_serial_put_byte(buf + num, arg_size); + + for (i = 0; i < arg_size; i++) + num += sps30_serial_put_byte(buf + num, arg[i]); + + /* SOF isn't checksummed */ + chksum = sps30_serial_calc_chksum(buf + 1, num - 1); + num += sps30_serial_put_byte(buf + num, chksum); + buf[num++] = SPS30_SERIAL_SOF_EOF; + + return num; +} + +static bool sps30_serial_frame_valid(struct sps30_state *state, const unsigned char *buf) +{ + struct sps30_serial_priv *priv = state->priv; + unsigned char chksum; + + if ((priv->num < SPS30_SERIAL_FRAME_MIN_SIZE) || + (priv->num != SPS30_SERIAL_FRAME_MIN_SIZE + + priv->buf[SPS30_SERIAL_FRAME_MISO_LEN_OFFSET])) { + dev_err(state->dev, "frame has invalid number of bytes\n"); + return false; + } + + if ((priv->buf[SPS30_SERIAL_FRAME_ADR_OFFSET] != buf[SPS30_SERIAL_FRAME_ADR_OFFSET]) || + (priv->buf[SPS30_SERIAL_FRAME_CMD_OFFSET] != buf[SPS30_SERIAL_FRAME_CMD_OFFSET])) { + dev_err(state->dev, "frame has wrong ADR and CMD bytes\n"); + return false; + } + + if (priv->buf[SPS30_SERIAL_FRAME_MISO_STATE_OFFSET]) { + dev_err(state->dev, "frame with non-zero state received (0x%02x)\n", + priv->buf[SPS30_SERIAL_FRAME_MISO_STATE_OFFSET]); + return false; + } + + /* SOF, checksum and EOF are not checksummed */ + chksum = sps30_serial_calc_chksum(priv->buf + 1, priv->num - 3); + if (priv->buf[priv->num - 2] != chksum) { + dev_err(state->dev, "frame integrity check failed\n"); + return false; + } + + return true; +} + +static int sps30_serial_command(struct sps30_state *state, unsigned char cmd, + const void *arg, size_t arg_size, void *rsp, size_t rsp_size) +{ + struct sps30_serial_priv *priv = state->priv; + unsigned char buf[SPS30_SERIAL_MAX_BUF_SIZE]; + int ret, size; + + size = sps30_serial_prep_frame(buf, cmd, arg, arg_size); + ret = sps30_serial_xfer(state, buf, size); + if (ret) + return ret; + + if (!sps30_serial_frame_valid(state, buf)) + return -EIO; + + if (rsp) { + rsp_size = min_t(size_t, priv->buf[SPS30_SERIAL_FRAME_MISO_LEN_OFFSET], rsp_size); + memcpy(rsp, &priv->buf[SPS30_SERIAL_FRAME_MISO_DATA_OFFSET], rsp_size); + } + + return rsp_size; +} + +static int sps30_serial_receive_buf(struct serdev_device *serdev, + const unsigned char *buf, size_t size) +{ + struct iio_dev *indio_dev = dev_get_drvdata(&serdev->dev); + struct sps30_serial_priv *priv; + struct sps30_state *state; + unsigned char byte; + size_t i; + + if (!indio_dev) + return 0; + + state = iio_priv(indio_dev); + priv = state->priv; + + /* just in case device put some unexpected data on the bus */ + if (priv->done) + return size; + + /* wait for the start of frame */ + if (!priv->num && size && buf[0] != SPS30_SERIAL_SOF_EOF) + return 1; + + if (priv->num + size >= ARRAY_SIZE(priv->buf)) + size = ARRAY_SIZE(priv->buf) - priv->num; + + for (i = 0; i < size; i++) { + byte = buf[i]; + /* remove stuffed bytes on-the-fly */ + if (byte == SPS30_SERIAL_ESCAPE_CHAR) { + priv->escaped = true; + continue; + } + + byte = sps30_serial_get_byte(priv->escaped, byte); + if (priv->escaped && !byte) + dev_warn(state->dev, "unrecognized escaped char (0x%02x)\n", byte); + + priv->buf[priv->num++] = byte; + + /* EOF received */ + if (!priv->escaped && byte == SPS30_SERIAL_SOF_EOF) { + if (priv->num < SPS30_SERIAL_FRAME_MIN_SIZE) + continue; + + priv->done = true; + complete(&priv->new_frame); + i++; + break; + } + + priv->escaped = false; + } + + return i; +} + +static const struct serdev_device_ops sps30_serial_device_ops = { + .receive_buf = sps30_serial_receive_buf, + .write_wakeup = serdev_device_write_wakeup, +}; + +static int sps30_serial_start_meas(struct sps30_state *state) +{ + /* request BE IEEE754 formatted data */ + unsigned char buf[] = { 0x01, 0x03 }; + + return sps30_serial_command(state, SPS30_SERIAL_START_MEAS, buf, sizeof(buf), NULL, 0); +} + +static int sps30_serial_stop_meas(struct sps30_state *state) +{ + return sps30_serial_command(state, SPS30_SERIAL_STOP_MEAS, NULL, 0, NULL, 0); +} + +static int sps30_serial_reset(struct sps30_state *state) +{ + int ret; + + ret = sps30_serial_command(state, SPS30_SERIAL_RESET, NULL, 0, NULL, 0); + msleep(500); + + return ret; +} + +static int sps30_serial_read_meas(struct sps30_state *state, __be32 *meas, size_t num) +{ + int ret; + + /* measurements are ready within a second */ + if (msleep_interruptible(1000)) + return -EINTR; + + ret = sps30_serial_command(state, SPS30_SERIAL_READ_MEAS, NULL, 0, meas, num * sizeof(num)); + if (ret < 0) + return ret; + /* if measurements aren't ready sensor returns empty frame */ + if (ret == SPS30_SERIAL_FRAME_MIN_SIZE) + return -ETIMEDOUT; + if (ret != num * sizeof(*meas)) + return -EIO; + + return 0; +} + +static int sps30_serial_clean_fan(struct sps30_state *state) +{ + return sps30_serial_command(state, SPS30_SERIAL_CLEAN_FAN, NULL, 0, NULL, 0); +} + +static int sps30_serial_read_cleaning_period(struct sps30_state *state, __be32 *period) +{ + unsigned char buf[] = { 0x00 }; + int ret; + + ret = sps30_serial_command(state, SPS30_SERIAL_PERIOD, buf, sizeof(buf), + period, sizeof(*period)); + if (ret < 0) + return ret; + if (ret != sizeof(*period)) + return -EIO; + + return 0; +} + +static int sps30_serial_write_cleaning_period(struct sps30_state *state, __be32 period) +{ + unsigned char buf[5] = { 0x00 }; + + memcpy(buf + 1, &period, sizeof(period)); + + return sps30_serial_command(state, SPS30_SERIAL_PERIOD, buf, sizeof(buf), NULL, 0); +} + +static int sps30_serial_show_info(struct sps30_state *state) +{ + /* + * tell device do return serial number and add extra nul byte just in case + * serial number isn't a valid string + */ + unsigned char buf[32 + 1] = { 0x03 }; + struct device *dev = state->dev; + int ret; + + ret = sps30_serial_command(state, SPS30_SERIAL_DEV_INFO, buf, 1, buf, sizeof(buf) - 1); + if (ret < 0) + return ret; + if (ret != sizeof(buf) - 1) + return -EIO; + + dev_info(dev, "serial number: %s\n", buf); + + ret = sps30_serial_command(state, SPS30_SERIAL_READ_VERSION, NULL, 0, buf, sizeof(buf) - 1); + if (ret < 0) + return ret; + if (ret < 2) + return -EIO; + + dev_info(dev, "fw version: %u.%u\n", buf[0], buf[1]); + + return 0; +} + +static const struct sps30_ops sps30_serial_ops = { + .start_meas = sps30_serial_start_meas, + .stop_meas = sps30_serial_stop_meas, + .read_meas = sps30_serial_read_meas, + .reset = sps30_serial_reset, + .clean_fan = sps30_serial_clean_fan, + .read_cleaning_period = sps30_serial_read_cleaning_period, + .write_cleaning_period = sps30_serial_write_cleaning_period, + .show_info = sps30_serial_show_info, +}; + +static int sps30_serial_probe(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct sps30_serial_priv *priv; + int ret; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + init_completion(&priv->new_frame); + serdev_device_set_client_ops(serdev, &sps30_serial_device_ops); + + ret = devm_serdev_device_open(dev, serdev); + if (ret) + return ret; + + serdev_device_set_baudrate(serdev, 115200); + serdev_device_set_flow_control(serdev, false); + + ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE); + if (ret) + return ret; + + return sps30_probe(dev, SPS30_SERIAL_DEV_NAME, priv, &sps30_serial_ops); +} + +static const struct of_device_id sps30_serial_of_match[] = { + { .compatible = "sensirion,sps30" }, + { } +}; +MODULE_DEVICE_TABLE(of, sps30_serial_of_match); + +static struct serdev_device_driver sps30_serial_driver = { + .driver = { + .name = KBUILD_MODNAME, + .of_match_table = sps30_serial_of_match, + }, + .probe = sps30_serial_probe, +}; +module_serdev_device_driver(sps30_serial_driver); + +MODULE_AUTHOR("Tomasz Duszynski <tomasz.duszynski@octakon.com>"); +MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor serial driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c index cb52b4fd6bf7..043f199e7bc6 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c @@ -3,18 +3,12 @@ * HID Sensors Driver * Copyright (c) 2012, Intel Corporation. */ -#include <linux/device.h> -#include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> #include <linux/kernel.h> -#include <linux/slab.h> #include <linux/time.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #define HZ_PER_MHZ 1000000L @@ -176,7 +170,7 @@ s32 hid_sensor_read_poll_value(struct hid_sensor_common *st) return value; } -EXPORT_SYMBOL(hid_sensor_read_poll_value); +EXPORT_SYMBOL_NS(hid_sensor_read_poll_value, IIO_HID_ATTRIBUTES); int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, int *val1, int *val2) @@ -203,7 +197,7 @@ int hid_sensor_read_samp_freq_value(struct hid_sensor_common *st, return IIO_VAL_INT_PLUS_MICRO; } -EXPORT_SYMBOL(hid_sensor_read_samp_freq_value); +EXPORT_SYMBOL_NS(hid_sensor_read_samp_freq_value, IIO_HID); int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, int val1, int val2) @@ -238,7 +232,7 @@ int hid_sensor_write_samp_freq_value(struct hid_sensor_common *st, return 0; } -EXPORT_SYMBOL(hid_sensor_write_samp_freq_value); +EXPORT_SYMBOL_NS(hid_sensor_write_samp_freq_value, IIO_HID); int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, int *val1, int *val2) @@ -261,7 +255,7 @@ int hid_sensor_read_raw_hyst_value(struct hid_sensor_common *st, return IIO_VAL_INT_PLUS_MICRO; } -EXPORT_SYMBOL(hid_sensor_read_raw_hyst_value); +EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_value, IIO_HID); int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, int *val2) @@ -283,7 +277,7 @@ int hid_sensor_read_raw_hyst_rel_value(struct hid_sensor_common *st, int *val1, return IIO_VAL_INT_PLUS_MICRO; } -EXPORT_SYMBOL(hid_sensor_read_raw_hyst_rel_value); +EXPORT_SYMBOL_NS(hid_sensor_read_raw_hyst_rel_value, IIO_HID); int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, @@ -315,7 +309,7 @@ int hid_sensor_write_raw_hyst_value(struct hid_sensor_common *st, return 0; } -EXPORT_SYMBOL(hid_sensor_write_raw_hyst_value); +EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_value, IIO_HID); int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, int val1, int val2) @@ -346,7 +340,7 @@ int hid_sensor_write_raw_hyst_rel_value(struct hid_sensor_common *st, return 0; } -EXPORT_SYMBOL(hid_sensor_write_raw_hyst_rel_value); +EXPORT_SYMBOL_NS(hid_sensor_write_raw_hyst_rel_value, IIO_HID); /* * This fuction applies the unit exponent to the scale. @@ -430,14 +424,14 @@ int hid_sensor_format_scale(u32 usage_id, return IIO_VAL_INT_PLUS_NANO; } -EXPORT_SYMBOL(hid_sensor_format_scale); +EXPORT_SYMBOL_NS(hid_sensor_format_scale, IIO_HID); int64_t hid_sensor_convert_timestamp(struct hid_sensor_common *st, int64_t raw_value) { return st->timestamp_ns_scale * raw_value; } -EXPORT_SYMBOL(hid_sensor_convert_timestamp); +EXPORT_SYMBOL_NS(hid_sensor_convert_timestamp, IIO_HID); static int hid_sensor_get_reporting_interval(struct hid_sensor_hub_device *hsdev, @@ -484,7 +478,7 @@ int hid_sensor_get_report_latency(struct hid_sensor_common *st) return value; } -EXPORT_SYMBOL(hid_sensor_get_report_latency); +EXPORT_SYMBOL_NS(hid_sensor_get_report_latency, IIO_HID_ATTRIBUTES); int hid_sensor_set_report_latency(struct hid_sensor_common *st, int latency_ms) { @@ -492,13 +486,13 @@ int hid_sensor_set_report_latency(struct hid_sensor_common *st, int latency_ms) st->report_latency.index, sizeof(latency_ms), &latency_ms); } -EXPORT_SYMBOL(hid_sensor_set_report_latency); +EXPORT_SYMBOL_NS(hid_sensor_set_report_latency, IIO_HID_ATTRIBUTES); bool hid_sensor_batch_mode_supported(struct hid_sensor_common *st) { return st->report_latency.index > 0 && st->report_latency.report_id > 0; } -EXPORT_SYMBOL(hid_sensor_batch_mode_supported); +EXPORT_SYMBOL_NS(hid_sensor_batch_mode_supported, IIO_HID_ATTRIBUTES); int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, u32 usage_id, @@ -590,7 +584,7 @@ int hid_sensor_parse_common_attributes(struct hid_sensor_hub_device *hsdev, return 0; } -EXPORT_SYMBOL(hid_sensor_parse_common_attributes); +EXPORT_SYMBOL_NS(hid_sensor_parse_common_attributes, IIO_HID); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); MODULE_DESCRIPTION("HID Sensor common attribute processing"); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c index 95ddccb44f1c..a4ec11a3b68a 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c @@ -6,16 +6,13 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/slab.h> #include <linux/delay.h> #include <linux/hid-sensor-hub.h> +#include <linux/workqueue.h> #include <linux/iio/iio.h> #include <linux/iio/trigger.h> #include <linux/iio/triggered_buffer.h> #include <linux/iio/trigger_consumer.h> -#include <linux/iio/buffer.h> #include <linux/iio/sysfs.h> #include "hid-sensor-trigger.h" @@ -150,7 +147,7 @@ static int _hid_sensor_power_state(struct hid_sensor_common *st, bool state) return 0; } -EXPORT_SYMBOL(hid_sensor_power_state); +EXPORT_SYMBOL_NS(hid_sensor_power_state, IIO_HID); int hid_sensor_power_state(struct hid_sensor_common *st, bool state) { @@ -163,18 +160,15 @@ int hid_sensor_power_state(struct hid_sensor_common *st, bool state) if (state) { atomic_inc(&st->user_requested_state); - ret = pm_runtime_get_sync(&st->pdev->dev); + ret = pm_runtime_resume_and_get(&st->pdev->dev); } else { atomic_dec(&st->user_requested_state); pm_runtime_mark_last_busy(&st->pdev->dev); pm_runtime_use_autosuspend(&st->pdev->dev); ret = pm_runtime_put_autosuspend(&st->pdev->dev); } - if (ret < 0) { - if (state) - pm_runtime_put_noidle(&st->pdev->dev); + if (ret < 0) return ret; - } return 0; #else @@ -222,14 +216,13 @@ void hid_sensor_remove_trigger(struct iio_dev *indio_dev, pm_runtime_disable(&attrb->pdev->dev); pm_runtime_set_suspended(&attrb->pdev->dev); - pm_runtime_put_noidle(&attrb->pdev->dev); cancel_work_sync(&attrb->work); iio_trigger_unregister(attrb->trigger); iio_trigger_free(attrb->trigger); iio_triggered_buffer_cleanup(indio_dev); } -EXPORT_SYMBOL(hid_sensor_remove_trigger); +EXPORT_SYMBOL_NS(hid_sensor_remove_trigger, IIO_HID); static const struct iio_trigger_ops hid_sensor_trigger_ops = { .set_trigger_state = &hid_sensor_data_rdy_trigger_set_state, @@ -256,7 +249,7 @@ int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, } trig = iio_trigger_alloc(indio_dev->dev.parent, - "%s-dev%d", name, indio_dev->id); + "%s-dev%d", name, iio_device_id(indio_dev)); if (trig == NULL) { dev_err(&indio_dev->dev, "Trigger Allocate Failed\n"); ret = -ENOMEM; @@ -295,7 +288,7 @@ error_triggered_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); return ret; } -EXPORT_SYMBOL(hid_sensor_setup_trigger); +EXPORT_SYMBOL_NS(hid_sensor_setup_trigger, IIO_HID); static int __maybe_unused hid_sensor_suspend(struct device *dev) { @@ -325,8 +318,9 @@ const struct dev_pm_ops hid_sensor_pm_ops = { SET_RUNTIME_PM_OPS(hid_sensor_suspend, hid_sensor_runtime_resume, NULL) }; -EXPORT_SYMBOL(hid_sensor_pm_ops); +EXPORT_SYMBOL_NS(hid_sensor_pm_ops, IIO_HID); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); MODULE_DESCRIPTION("HID Sensor trigger processing"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID_ATTRIBUTES); diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h index bb45cc89e551..f94fca4f1edf 100644 --- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h +++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h @@ -9,6 +9,9 @@ #include <linux/pm.h> #include <linux/pm_runtime.h> +struct hid_sensor_common; +struct iio_dev; + extern const struct dev_pm_ops hid_sensor_pm_ops; int hid_sensor_setup_trigger(struct iio_dev *indio_dev, const char *name, diff --git a/drivers/iio/common/scmi_sensors/Makefile b/drivers/iio/common/scmi_sensors/Makefile index f13140a2575a..645e0fce1a73 100644 --- a/drivers/iio/common/scmi_sensors/Makefile +++ b/drivers/iio/common/scmi_sensors/Makefile @@ -1,4 +1,4 @@ -# SPDX - License - Identifier : GPL - 2.0 - only +# SPDX-License-Identifier: GPL-2.0-only # # Makefile for the IIO over SCMI # diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c index 141e8aa6911e..7cf2bf282cef 100644 --- a/drivers/iio/common/scmi_sensors/scmi_iio.c +++ b/drivers/iio/common/scmi_sensors/scmi_iio.c @@ -525,7 +525,6 @@ scmi_alloc_iiodev(struct scmi_device *sdev, return ERR_PTR(-ENOMEM); iiodev->modes = INDIO_DIRECT_MODE; - iiodev->dev.parent = dev; sensor = iio_priv(iiodev); sensor->sensor_ops = ops; sensor->ph = ph; diff --git a/drivers/iio/dac/ad5766.c b/drivers/iio/dac/ad5766.c index 79837a4b3a41..3104ec32dfac 100644 --- a/drivers/iio/dac/ad5766.c +++ b/drivers/iio/dac/ad5766.c @@ -597,8 +597,6 @@ static int ad5766_probe(struct spi_device *spi) indio_dev->channels = st->chip_info->channels; indio_dev->num_channels = st->chip_info->num_channels; indio_dev->info = &ad5766_info; - indio_dev->dev.parent = &spi->dev; - indio_dev->dev.of_node = spi->dev.of_node; indio_dev->name = spi_get_device_id(spi)->name; indio_dev->modes = INDIO_DIRECT_MODE; diff --git a/drivers/iio/dac/stm32-dac.c b/drivers/iio/dac/stm32-dac.c index a5b0a52bf86e..dd2e306824e7 100644 --- a/drivers/iio/dac/stm32-dac.c +++ b/drivers/iio/dac/stm32-dac.c @@ -69,9 +69,8 @@ static int stm32_dac_set_enable_state(struct iio_dev *indio_dev, int ch, } if (enable) { - ret = pm_runtime_get_sync(dev); + ret = pm_runtime_resume_and_get(dev); if (ret < 0) { - pm_runtime_put_noidle(dev); mutex_unlock(&dac->lock); return ret; } diff --git a/drivers/iio/dummy/Kconfig b/drivers/iio/dummy/Kconfig index 5c5c2f8c55f3..1f46cb9e51b7 100644 --- a/drivers/iio/dummy/Kconfig +++ b/drivers/iio/dummy/Kconfig @@ -34,6 +34,7 @@ config IIO_SIMPLE_DUMMY_BUFFER select IIO_BUFFER select IIO_TRIGGER select IIO_KFIFO_BUF + select IIO_TRIGGERED_BUFFER help Add buffered data capture to the simple dummy driver. diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c index 1462a6a5bc6d..3d9eba716b69 100644 --- a/drivers/iio/frequency/adf4350.c +++ b/drivers/iio/frequency/adf4350.c @@ -563,8 +563,10 @@ static int adf4350_probe(struct spi_device *spi) st->lock_detect_gpiod = devm_gpiod_get_optional(&spi->dev, NULL, GPIOD_IN); - if (IS_ERR(st->lock_detect_gpiod)) - return PTR_ERR(st->lock_detect_gpiod); + if (IS_ERR(st->lock_detect_gpiod)) { + ret = PTR_ERR(st->lock_detect_gpiod); + goto error_disable_reg; + } if (pdata->power_up_frequency) { ret = adf4350_set_freq(st, pdata->power_up_frequency); diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c index a11ae9db0d11..36879f01e28c 100644 --- a/drivers/iio/gyro/adis16136.c +++ b/drivers/iio/gyro/adis16136.c @@ -6,19 +6,14 @@ * Author: Lars-Peter Clausen <lars@metafoo.de> */ -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/slab.h> #include <linux/sysfs.h> #include <linux/module.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> -#include <linux/iio/buffer.h> #include <linux/iio/imu/adis.h> #include <linux/debugfs.h> @@ -223,13 +218,12 @@ static ssize_t adis16136_read_frequency(struct device *dev, { struct iio_dev *indio_dev = dev_to_iio_dev(dev); struct adis16136 *adis16136 = iio_priv(indio_dev); - struct mutex *slock = &adis16136->adis.state_lock; unsigned int freq; int ret; - mutex_lock(slock); + adis_dev_lock(&adis16136->adis); ret = __adis16136_get_freq(adis16136, &freq); - mutex_unlock(slock); + adis_dev_unlock(&adis16136->adis); if (ret) return ret; @@ -254,11 +248,10 @@ static const unsigned adis16136_3db_divisors[] = { static int adis16136_set_filter(struct iio_dev *indio_dev, int val) { struct adis16136 *adis16136 = iio_priv(indio_dev); - struct mutex *slock = &adis16136->adis.state_lock; unsigned int freq; int i, ret; - mutex_lock(slock); + adis_dev_lock(&adis16136->adis); ret = __adis16136_get_freq(adis16136, &freq); if (ret) goto out_unlock; @@ -270,7 +263,7 @@ static int adis16136_set_filter(struct iio_dev *indio_dev, int val) ret = __adis_write_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, i); out_unlock: - mutex_unlock(slock); + adis_dev_unlock(&adis16136->adis); return ret; } @@ -278,12 +271,11 @@ out_unlock: static int adis16136_get_filter(struct iio_dev *indio_dev, int *val) { struct adis16136 *adis16136 = iio_priv(indio_dev); - struct mutex *slock = &adis16136->adis.state_lock; unsigned int freq; uint16_t val16; int ret; - mutex_lock(slock); + adis_dev_lock(&adis16136->adis); ret = __adis_read_reg_16(&adis16136->adis, ADIS16136_REG_AVG_CNT, &val16); @@ -297,7 +289,7 @@ static int adis16136_get_filter(struct iio_dev *indio_dev, int *val) *val = freq / adis16136_3db_divisors[val16 & 0x07]; err_unlock: - mutex_unlock(slock); + adis_dev_unlock(&adis16136->adis); return ret ? ret : IIO_VAL_INT; } diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c index e7c9a3e31c45..66b6b7bd5e1b 100644 --- a/drivers/iio/gyro/adis16260.c +++ b/drivers/iio/gyro/adis16260.c @@ -5,17 +5,12 @@ * Copyright 2010 Analog Devices Inc. */ -#include <linux/interrupt.h> -#include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/sysfs.h> #include <linux/module.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> -#include <linux/iio/buffer.h> #include <linux/iio/imu/adis.h> #define ADIS16260_STARTUP_DELAY 220 /* ms */ @@ -293,7 +288,7 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, addr = adis16260_addresses[chan->scan_index][1]; return adis_write_reg_16(adis, addr, val); case IIO_CHAN_INFO_SAMP_FREQ: - mutex_lock(&adis->state_lock); + adis_dev_lock(adis); if (spi_get_device_id(adis->spi)->driver_data) t = 256 / val; else @@ -310,7 +305,7 @@ static int adis16260_write_raw(struct iio_dev *indio_dev, adis->spi->max_speed_hz = ADIS16260_SPI_FAST; ret = __adis_write_reg_8(adis, ADIS16260_SMPL_PRD, t); - mutex_unlock(&adis->state_lock); + adis_dev_unlock(adis); return ret; } return -EINVAL; diff --git a/drivers/iio/gyro/adxrs290.c b/drivers/iio/gyro/adxrs290.c index cec5e1f17c22..3e0734ddafe3 100644 --- a/drivers/iio/gyro/adxrs290.c +++ b/drivers/iio/gyro/adxrs290.c @@ -589,7 +589,7 @@ static int adxrs290_probe_trigger(struct iio_dev *indio_dev) st->dready_trig = devm_iio_trigger_alloc(&st->spi->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!st->dready_trig) return -ENOMEM; diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c index b11ebd9bb7a4..17b939a367ad 100644 --- a/drivers/iio/gyro/bmg160_core.c +++ b/drivers/iio/gyro/bmg160_core.c @@ -98,7 +98,11 @@ struct bmg160_data { struct iio_trigger *motion_trig; struct iio_mount_matrix orientation; struct mutex mutex; - s16 buffer[8]; + /* Ensure naturally aligned timestamp */ + struct { + s16 chans[3]; + s64 timestamp __aligned(8); + } scan; u32 dps_range; int ev_enable_state; int slope_thres; @@ -882,12 +886,12 @@ static irqreturn_t bmg160_trigger_handler(int irq, void *p) mutex_lock(&data->mutex); ret = regmap_bulk_read(data->regmap, BMG160_REG_XOUT_L, - data->buffer, AXIS_MAX * 2); + data->scan.chans, AXIS_MAX * 2); mutex_unlock(&data->mutex); if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -1102,8 +1106,7 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, if (ret) return ret; - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; @@ -1137,14 +1140,14 @@ int bmg160_core_probe(struct device *dev, struct regmap *regmap, int irq, data->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) return -ENOMEM; data->motion_trig = devm_iio_trigger_alloc(dev, "%s-any-motion-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->motion_trig) return -ENOMEM; diff --git a/drivers/iio/gyro/fxas21002c_core.c b/drivers/iio/gyro/fxas21002c_core.c index 645461c70454..410e5e9f2672 100644 --- a/drivers/iio/gyro/fxas21002c_core.c +++ b/drivers/iio/gyro/fxas21002c_core.c @@ -366,14 +366,7 @@ out_unlock: static int fxas21002c_pm_get(struct fxas21002c_data *data) { - struct device *dev = regmap_get_device(data->regmap); - int ret; - - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); - - return ret; + return pm_runtime_resume_and_get(regmap_get_device(data->regmap)); } static int fxas21002c_pm_put(struct fxas21002c_data *data) @@ -854,7 +847,7 @@ static int fxas21002c_trigger_probe(struct fxas21002c_data *data) data->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) return -ENOMEM; @@ -1004,7 +997,6 @@ int fxas21002c_core_probe(struct device *dev, struct regmap *regmap, int irq, pm_disable: pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); return ret; } @@ -1018,7 +1010,6 @@ void fxas21002c_core_remove(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); } EXPORT_SYMBOL_GPL(fxas21002c_core_remove); diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c index dad26ee4fd1f..bc63c2a34c5e 100644 --- a/drivers/iio/gyro/hid-sensor-gyro-3d.c +++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c @@ -6,13 +6,10 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> +#include <linux/mod_devicetable.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include "../common/hid-sensors/hid-sensor-trigger.h" @@ -400,3 +397,4 @@ module_platform_driver(hid_gyro_3d_platform_driver); MODULE_DESCRIPTION("HID Sensor Gyroscope 3D"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c index af0aaa146f0c..04dd6a7969ea 100644 --- a/drivers/iio/gyro/itg3200_buffer.c +++ b/drivers/iio/gyro/itg3200_buffer.c @@ -114,7 +114,7 @@ int itg3200_probe_trigger(struct iio_dev *indio_dev) struct itg3200 *st = iio_priv(indio_dev); st->trig = iio_trigger_alloc(&st->i2c->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!st->trig) return -ENOMEM; diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c index e9804664db73..a7f1bbb5f289 100644 --- a/drivers/iio/gyro/itg3200_core.c +++ b/drivers/iio/gyro/itg3200_core.c @@ -308,8 +308,7 @@ static int itg3200_probe(struct i2c_client *client, st = iio_priv(indio_dev); - ret = iio_read_mount_matrix(&client->dev, "mount-matrix", - &st->orientation); + ret = iio_read_mount_matrix(&client->dev, &st->orientation); if (ret) return ret; diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c index f17a93519535..3225de1f023b 100644 --- a/drivers/iio/gyro/mpu3050-core.c +++ b/drivers/iio/gyro/mpu3050-core.c @@ -1058,7 +1058,7 @@ static int mpu3050_trigger_probe(struct iio_dev *indio_dev, int irq) mpu3050->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!mpu3050->trig) return -ENOMEM; @@ -1164,7 +1164,7 @@ int mpu3050_common_probe(struct device *dev, mpu3050->divisor = 99; /* Read the mounting matrix, if present */ - ret = iio_read_mount_matrix(dev, "mount-matrix", &mpu3050->orientation); + ret = iio_read_mount_matrix(dev, &mpu3050->orientation); if (ret) return ret; diff --git a/drivers/iio/gyro/st_gyro.h b/drivers/iio/gyro/st_gyro.h index fd9171cc3aba..6537f5cb8320 100644 --- a/drivers/iio/gyro/st_gyro.h +++ b/drivers/iio/gyro/st_gyro.h @@ -24,18 +24,6 @@ #define LSM330_GYRO_DEV_NAME "lsm330_gyro" #define LSM9DS0_GYRO_DEV_NAME "lsm9ds0_gyro" -/** - * struct st_sensors_platform_data - gyro platform data - * @drdy_int_pin: DRDY on gyros is available only on INT2 pin. - */ -static __maybe_unused const struct st_sensors_platform_data gyro_pdata = { - .drdy_int_pin = 2, -}; - -const struct st_sensor_settings *st_gyro_get_settings(const char *name); -int st_gyro_common_probe(struct iio_dev *indio_dev); -void st_gyro_common_remove(struct iio_dev *indio_dev); - #ifdef CONFIG_IIO_BUFFER int st_gyro_allocate_ring(struct iio_dev *indio_dev); void st_gyro_deallocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c index c8aa051995d3..b86ee4d940d9 100644 --- a/drivers/iio/gyro/st_gyro_core.c +++ b/drivers/iio/gyro/st_gyro_core.c @@ -37,19 +37,36 @@ #define ST_GYRO_FS_AVL_500DPS 500 #define ST_GYRO_FS_AVL_2000DPS 2000 +static const struct iio_mount_matrix * +st_gyro_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct st_sensor_data *gdata = iio_priv(indio_dev); + + return &gdata->mount_matrix; +} + +static const struct iio_chan_spec_ext_info st_gyro_mount_matrix_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_gyro_get_mount_matrix), + { } +}; + static const struct iio_chan_spec st_gyro_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ANGL_VEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, - ST_GYRO_DEFAULT_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, + ST_GYRO_DEFAULT_OUT_X_L_ADDR, + st_gyro_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ANGL_VEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, - ST_GYRO_DEFAULT_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_ANGL_VEL, + ST_GYRO_DEFAULT_OUT_Y_L_ADDR, + st_gyro_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_ANGL_VEL, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, - ST_GYRO_DEFAULT_OUT_Z_L_ADDR), + ST_GYRO_DEFAULT_OUT_Z_L_ADDR, + st_gyro_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -357,6 +374,11 @@ static const struct st_sensor_settings st_gyro_sensors_settings[] = { }, }; +/* DRDY on gyros is available only on INT2 pin */ +static const struct st_sensors_platform_data gyro_pdata = { + .drdy_int_pin = 2, +}; + static int st_gyro_read_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *ch, int *val, int *val2, long mask) @@ -466,18 +488,18 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &gyro_info; - err = st_sensors_power_enable(indio_dev); - if (err) - return err; - err = st_sensors_verify_id(indio_dev); if (err < 0) - goto st_gyro_power_off; + return err; gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS; indio_dev->channels = gdata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + err = iio_read_mount_matrix(gdata->dev, &gdata->mount_matrix); + if (err) + return err; + gdata->current_fullscale = &gdata->sensor_settings->fs.fs_avl[0]; gdata->odr = gdata->sensor_settings->odr.odr_avl[0].hz; @@ -485,11 +507,11 @@ int st_gyro_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) - goto st_gyro_power_off; + return err; err = st_gyro_allocate_ring(indio_dev); if (err < 0) - goto st_gyro_power_off; + return err; if (gdata->irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -512,9 +534,6 @@ st_gyro_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_gyro_probe_trigger_error: st_gyro_deallocate_ring(indio_dev); -st_gyro_power_off: - st_sensors_power_disable(indio_dev); - return err; } EXPORT_SYMBOL(st_gyro_common_probe); @@ -523,8 +542,6 @@ void st_gyro_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *gdata = iio_priv(indio_dev); - st_sensors_power_disable(indio_dev); - iio_device_unregister(indio_dev); if (gdata->irq > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c index 8190966e6ff0..a25cc0379e16 100644 --- a/drivers/iio/gyro/st_gyro_i2c.c +++ b/drivers/iio/gyro/st_gyro_i2c.c @@ -86,16 +86,29 @@ static int st_gyro_i2c_probe(struct i2c_client *client, if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_gyro_common_probe(indio_dev); if (err < 0) - return err; + goto st_gyro_power_off; return 0; + +st_gyro_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_gyro_i2c_remove(struct i2c_client *client) { - st_gyro_common_remove(i2c_get_clientdata(client)); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + st_sensors_power_disable(indio_dev); + + st_gyro_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c index efb862763ca3..18d6a2aeda45 100644 --- a/drivers/iio/gyro/st_gyro_spi.c +++ b/drivers/iio/gyro/st_gyro_spi.c @@ -90,16 +90,29 @@ static int st_gyro_spi_probe(struct spi_device *spi) if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_gyro_common_probe(indio_dev); if (err < 0) - return err; + goto st_gyro_power_off; return 0; + +st_gyro_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_gyro_spi_remove(struct spi_device *spi) { - st_gyro_common_remove(spi_get_drvdata(spi)); + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + st_sensors_power_disable(indio_dev); + + st_gyro_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c index 1fa8d51d5080..d4921385aaf7 100644 --- a/drivers/iio/health/afe4403.c +++ b/drivers/iio/health/afe4403.c @@ -521,7 +521,7 @@ static int afe4403_probe(struct spi_device *spi) afe->trig = devm_iio_trigger_alloc(afe->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!afe->trig) { dev_err(afe->dev, "Unable to allocate IIO trigger\n"); ret = -ENOMEM; diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c index e1476bf79fe2..d8a27dfe074a 100644 --- a/drivers/iio/health/afe4404.c +++ b/drivers/iio/health/afe4404.c @@ -528,7 +528,7 @@ static int afe4404_probe(struct i2c_client *client, afe->trig = devm_iio_trigger_alloc(afe->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!afe->trig) { dev_err(afe->dev, "Unable to allocate IIO trigger\n"); ret = -ENOMEM; diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c index 23bc9c784ef4..4a39f1019347 100644 --- a/drivers/iio/humidity/am2315.c +++ b/drivers/iio/humidity/am2315.c @@ -7,7 +7,6 @@ * 7-bit I2C address: 0x5C. */ -#include <linux/acpi.h> #include <linux/delay.h> #include <linux/i2c.h> #include <linux/kernel.h> @@ -33,7 +32,11 @@ struct am2315_data { struct i2c_client *client; struct mutex lock; - s16 buffer[8]; /* 2x16-bit channels + 2x16 padding + 4x16 timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + s16 chans[2]; + s64 timestamp __aligned(8); + } scan; }; struct am2315_sensor_data { @@ -167,20 +170,20 @@ static irqreturn_t am2315_trigger_handler(int irq, void *p) mutex_lock(&data->lock); if (*(indio_dev->active_scan_mask) == AM2315_ALL_CHANNEL_MASK) { - data->buffer[0] = sensor_data.hum_data; - data->buffer[1] = sensor_data.temp_data; + data->scan.chans[0] = sensor_data.hum_data; + data->scan.chans[1] = sensor_data.temp_data; } else { i = 0; for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { - data->buffer[i] = (bit ? sensor_data.temp_data : - sensor_data.hum_data); + data->scan.chans[i] = (bit ? sensor_data.temp_data : + sensor_data.hum_data); i++; } } mutex_unlock(&data->lock); - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: iio_trigger_notify_done(indio_dev->trig); @@ -256,17 +259,9 @@ static const struct i2c_device_id am2315_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, am2315_i2c_id); -static const struct acpi_device_id am2315_acpi_id[] = { - {"AOS2315", 0}, - {} -}; - -MODULE_DEVICE_TABLE(acpi, am2315_acpi_id); - static struct i2c_driver am2315_driver = { .driver = { .name = "am2315", - .acpi_match_table = ACPI_PTR(am2315_acpi_id), }, .probe = am2315_probe, .id_table = am2315_i2c_id, diff --git a/drivers/iio/humidity/hdc2010.c b/drivers/iio/humidity/hdc2010.c index 83f5b9f60780..1381df46187c 100644 --- a/drivers/iio/humidity/hdc2010.c +++ b/drivers/iio/humidity/hdc2010.c @@ -272,7 +272,6 @@ static int hdc2010_probe(struct i2c_client *client, data->client = client; mutex_init(&data->lock); - indio_dev->dev.parent = &client->dev; /* * As DEVICE ID register does not differentiate between * HDC2010 and HDC2080, we have the name hardcoded diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c index 74383abc0d44..fa0fe404a70a 100644 --- a/drivers/iio/humidity/hid-sensor-humidity.c +++ b/drivers/iio/humidity/hid-sensor-humidity.c @@ -8,6 +8,7 @@ #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include "hid-sensor-trigger.h" @@ -295,3 +296,4 @@ module_platform_driver(hid_humidity_platform_driver); MODULE_DESCRIPTION("HID Environmental humidity sensor"); MODULE_AUTHOR("Song Hongyan <hongyan.song@intel.com>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig index f02883b08480..001ca2c3ff95 100644 --- a/drivers/iio/imu/Kconfig +++ b/drivers/iio/imu/Kconfig @@ -94,6 +94,7 @@ config KMX61 source "drivers/iio/imu/inv_icm42600/Kconfig" source "drivers/iio/imu/inv_mpu6050/Kconfig" source "drivers/iio/imu/st_lsm6dsx/Kconfig" +source "drivers/iio/imu/st_lsm9ds0/Kconfig" endmenu diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile index 13e9ff442b11..c82748096c77 100644 --- a/drivers/iio/imu/Makefile +++ b/drivers/iio/imu/Makefile @@ -26,3 +26,4 @@ obj-y += inv_mpu6050/ obj-$(CONFIG_KMX61) += kmx61.o obj-y += st_lsm6dsx/ +obj-y += st_lsm9ds0/ diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c index 319b64b2fd88..a5b421f42287 100644 --- a/drivers/iio/imu/adis.c +++ b/drivers/iio/imu/adis.c @@ -12,14 +12,10 @@ #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/slab.h> -#include <linux/sysfs.h> #include <linux/module.h> #include <asm/unaligned.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> -#include <linux/iio/buffer.h> #include <linux/iio/imu/adis.h> #define ADIS_MSC_CTRL_DATA_RDY_EN BIT(2) diff --git a/drivers/iio/imu/adis16400.c b/drivers/iio/imu/adis16400.c index 768aa493a1a6..b12917a7cb60 100644 --- a/drivers/iio/imu/adis16400.c +++ b/drivers/iio/imu/adis16400.c @@ -10,22 +10,15 @@ * Copyright (c) 2011 Analog Devices Inc. */ -#include <linux/interrupt.h> #include <linux/irq.h> -#include <linux/delay.h> -#include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/slab.h> -#include <linux/sysfs.h> -#include <linux/list.h> #include <linux/module.h> #include <linux/debugfs.h> #include <linux/bitops.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include <linux/iio/trigger_consumer.h> #include <linux/iio/imu/adis.h> @@ -641,28 +634,13 @@ static irqreturn_t adis16400_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct adis16400_state *st = iio_priv(indio_dev); struct adis *adis = &st->adis; - u32 old_speed_hz = st->adis.spi->max_speed_hz; void *buffer; int ret; - if (!adis->buffer) - return -ENOMEM; - - if (!(st->variant->flags & ADIS16400_NO_BURST) && - st->adis.spi->max_speed_hz > ADIS16400_SPI_BURST) { - st->adis.spi->max_speed_hz = ADIS16400_SPI_BURST; - spi_setup(st->adis.spi); - } - ret = spi_sync(adis->spi, &adis->msg); if (ret) dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret); - if (!(st->variant->flags & ADIS16400_NO_BURST)) { - st->adis.spi->max_speed_hz = old_speed_hz; - spi_setup(st->adis.spi); - } - if (st->variant->flags & ADIS16400_BURST_DIAG_STAT) buffer = adis->buffer + sizeof(u16); else @@ -968,7 +946,8 @@ static const char * const adis16400_status_error_msgs[] = { BIT(ADIS16400_DIAG_STAT_POWER_LOW), \ .timeouts = (_timeouts), \ .burst_reg_cmd = ADIS16400_GLOB_CMD, \ - .burst_len = (_burst_len) \ + .burst_len = (_burst_len), \ + .burst_max_speed_hz = ADIS16400_SPI_BURST \ } static const struct adis_timeout adis16300_timeouts = { @@ -1178,8 +1157,6 @@ static int adis16400_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - /* this is only used for removal purposes */ - spi_set_drvdata(spi, indio_dev); /* setup the industrialio driver allocated elements */ st->variant = &adis16400_chips[spi_get_device_id(spi)->driver_data]; diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c index 73bf45e859b8..a6f9fba3e03f 100644 --- a/drivers/iio/imu/adis16460.c +++ b/drivers/iio/imu/adis16460.c @@ -5,7 +5,6 @@ * Copyright 2019 Analog Devices Inc. */ -#include <linux/delay.h> #include <linux/module.h> #include <linux/spi/spi.h> @@ -388,8 +387,6 @@ static int adis16460_probe(struct spi_device *spi) if (indio_dev == NULL) return -ENOMEM; - spi_set_drvdata(spi, indio_dev); - st = iio_priv(indio_dev); st->chip_info = &adis16460_chip_info; diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c index 1de62fc79e0f..eb48102f9424 100644 --- a/drivers/iio/imu/adis16475.c +++ b/drivers/iio/imu/adis16475.c @@ -14,7 +14,6 @@ #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/iio/imu/adis.h> -#include <linux/iio/sysfs.h> #include <linux/iio/trigger_consumer.h> #include <linux/irq.h> #include <linux/lcm.h> @@ -645,7 +644,8 @@ static int adis16475_enable_irq(struct adis *adis, bool enable) .timeouts = (_timeouts), \ .burst_reg_cmd = ADIS16475_REG_GLOB_CMD, \ .burst_len = ADIS16475_BURST_MAX_DATA, \ - .burst_max_len = ADIS16475_BURST32_MAX_DATA \ + .burst_max_len = ADIS16475_BURST32_MAX_DATA, \ + .burst_max_speed_hz = ADIS16475_BURST_MAX_SPEED \ } static const struct adis16475_sync adis16475_sync_mode[] = { @@ -1062,15 +1062,11 @@ static irqreturn_t adis16475_trigger_handler(int irq, void *p) bool valid; /* offset until the first element after gyro and accel */ const u8 offset = st->burst32 ? 13 : 7; - const u32 cached_spi_speed_hz = adis->spi->max_speed_hz; - - adis->spi->max_speed_hz = ADIS16475_BURST_MAX_SPEED; ret = spi_sync(adis->spi, &adis->msg); if (ret) - return ret; + goto check_burst32; - adis->spi->max_speed_hz = cached_spi_speed_hz; buffer = adis->buffer; crc = be16_to_cpu(buffer[offset + 2]); @@ -1332,7 +1328,6 @@ static int adis16475_probe(struct spi_device *spi) return -ENOMEM; st = iio_priv(indio_dev); - spi_set_drvdata(spi, indio_dev); st->info = device_get_match_data(&spi->dev); if (!st->info) diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c index f81b86690b76..a869a6e52a16 100644 --- a/drivers/iio/imu/adis16480.c +++ b/drivers/iio/imu/adis16480.c @@ -9,21 +9,19 @@ #include <linux/bitfield.h> #include <linux/of_irq.h> #include <linux/interrupt.h> -#include <linux/delay.h> #include <linux/math.h> -#include <linux/mutex.h> #include <linux/device.h> #include <linux/kernel.h> #include <linux/spi/spi.h> -#include <linux/slab.h> -#include <linux/sysfs.h> #include <linux/module.h> #include <linux/lcm.h> +#include <linux/swab.h> +#include <linux/crc32.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include <linux/iio/imu/adis.h> +#include <linux/iio/trigger_consumer.h> #include <linux/debugfs.h> @@ -103,6 +101,12 @@ * Available only for ADIS1649x devices */ #define ADIS16495_REG_SYNC_SCALE ADIS16480_REG(0x03, 0x10) +#define ADIS16495_REG_BURST_CMD ADIS16480_REG(0x00, 0x7C) +#define ADIS16495_BURST_ID 0xA5A5 +/* total number of segments in burst */ +#define ADIS16495_BURST_MAX_DATA 20 +/* spi max speed in burst mode */ +#define ADIS16495_BURST_MAX_SPEED 6000000 #define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20) @@ -163,6 +167,8 @@ struct adis16480 { struct clk *ext_clk; enum adis16480_clock_mode clk_mode; unsigned int clk_freq; + /* Alignment needed for the timestamp */ + __be16 data[ADIS16495_BURST_MAX_DATA] __aligned(8); }; static const char * const adis16480_int_pin_names[4] = { @@ -863,7 +869,7 @@ static const char * const adis16480_status_error_msgs[] = { static int adis16480_enable_irq(struct adis *adis, bool enable); -#define ADIS16480_DATA(_prod_id, _timeouts) \ +#define ADIS16480_DATA(_prod_id, _timeouts, _burst_len) \ { \ .diag_stat_reg = ADIS16480_REG_DIAG_STS, \ .glob_cmd_reg = ADIS16480_REG_GLOB_CMD, \ @@ -887,6 +893,9 @@ static int adis16480_enable_irq(struct adis *adis, bool enable); BIT(ADIS16480_DIAG_STAT_BARO_FAIL), \ .enable_irq = adis16480_enable_irq, \ .timeouts = (_timeouts), \ + .burst_reg_cmd = ADIS16495_REG_BURST_CMD, \ + .burst_len = (_burst_len), \ + .burst_max_speed_hz = ADIS16495_BURST_MAX_SPEED \ } static const struct adis_timeout adis16485_timeouts = { @@ -931,7 +940,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(16375, &adis16485_timeouts), + .adis_data = ADIS16480_DATA(16375, &adis16485_timeouts, 0), }, [ADIS16480] = { .channels = adis16480_channels, @@ -944,7 +953,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(16480, &adis16480_timeouts), + .adis_data = ADIS16480_DATA(16480, &adis16480_timeouts, 0), }, [ADIS16485] = { .channels = adis16485_channels, @@ -957,7 +966,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(16485, &adis16485_timeouts), + .adis_data = ADIS16480_DATA(16485, &adis16485_timeouts, 0), }, [ADIS16488] = { .channels = adis16480_channels, @@ -970,7 +979,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .int_clk = 2460000, .max_dec_rate = 2048, .filter_freqs = adis16480_def_filter_freqs, - .adis_data = ADIS16480_DATA(16488, &adis16485_timeouts), + .adis_data = ADIS16480_DATA(16488, &adis16485_timeouts, 0), }, [ADIS16490] = { .channels = adis16485_channels, @@ -984,7 +993,7 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16490, &adis16495_timeouts), + .adis_data = ADIS16480_DATA(16490, &adis16495_timeouts, 0), }, [ADIS16495_1] = { .channels = adis16485_channels, @@ -998,7 +1007,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16495_2] = { .channels = adis16485_channels, @@ -1012,7 +1023,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16495_3] = { .channels = adis16485_channels, @@ -1026,7 +1039,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16495, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16497_1] = { .channels = adis16485_channels, @@ -1040,7 +1055,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16497_2] = { .channels = adis16485_channels, @@ -1054,7 +1071,9 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, [ADIS16497_3] = { .channels = adis16485_channels, @@ -1068,10 +1087,118 @@ static const struct adis16480_chip_info adis16480_chip_info[] = { .max_dec_rate = 4250, .filter_freqs = adis16495_def_filter_freqs, .has_pps_clk_mode = true, - .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts), + /* 20 elements of 16bits */ + .adis_data = ADIS16480_DATA(16497, &adis16495_1_timeouts, + ADIS16495_BURST_MAX_DATA * 2), }, }; +static bool adis16480_validate_crc(const u16 *buf, const u8 n_elem, const u32 crc) +{ + u32 crc_calc; + u16 crc_buf[15]; + int j; + + for (j = 0; j < n_elem; j++) + crc_buf[j] = swab16(buf[j]); + + crc_calc = crc32(~0, crc_buf, n_elem * 2); + crc_calc ^= ~0; + + return (crc == crc_calc); +} + +static irqreturn_t adis16480_trigger_handler(int irq, void *p) +{ + struct iio_poll_func *pf = p; + struct iio_dev *indio_dev = pf->indio_dev; + struct adis16480 *st = iio_priv(indio_dev); + struct adis *adis = &st->adis; + int ret, bit, offset, i = 0; + __be16 *buffer; + u32 crc; + bool valid; + + adis_dev_lock(adis); + if (adis->current_page != 0) { + adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); + adis->tx[1] = 0; + ret = spi_write(adis->spi, adis->tx, 2); + if (ret) { + dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret); + adis_dev_unlock(adis); + goto irq_done; + } + + adis->current_page = 0; + } + + ret = spi_sync(adis->spi, &adis->msg); + if (ret) { + dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret); + adis_dev_unlock(adis); + goto irq_done; + } + + adis_dev_unlock(adis); + + /* + * After making the burst request, the response can have one or two + * 16-bit responses containing the BURST_ID depending on the sclk. If + * clk > 3.6MHz, then we will have two BURST_ID in a row. If clk < 3MHZ, + * we have only one. To manage that variation, we use the transition from the + * BURST_ID to the SYS_E_FLAG register, which will not be equal to 0xA5A5. If + * we not find this variation in the first 4 segments, then the data should + * not be valid. + */ + buffer = adis->buffer; + for (offset = 0; offset < 4; offset++) { + u16 curr = be16_to_cpu(buffer[offset]); + u16 next = be16_to_cpu(buffer[offset + 1]); + + if (curr == ADIS16495_BURST_ID && next != ADIS16495_BURST_ID) { + offset++; + break; + } + } + + if (offset == 4) { + dev_err(&adis->spi->dev, "Invalid burst data\n"); + goto irq_done; + } + + crc = be16_to_cpu(buffer[offset + 16]) << 16 | be16_to_cpu(buffer[offset + 15]); + valid = adis16480_validate_crc((u16 *)&buffer[offset], 15, crc); + if (!valid) { + dev_err(&adis->spi->dev, "Invalid crc\n"); + goto irq_done; + } + + for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) { + /* + * When burst mode is used, temperature is the first data + * channel in the sequence, but the temperature scan index + * is 10. + */ + switch (bit) { + case ADIS16480_SCAN_TEMP: + st->data[i++] = buffer[offset + 1]; + break; + case ADIS16480_SCAN_GYRO_X ... ADIS16480_SCAN_ACCEL_Z: + /* The lower register data is sequenced first */ + st->data[i++] = buffer[2 * bit + offset + 3]; + st->data[i++] = buffer[2 * bit + offset + 2]; + break; + } + } + + iio_push_to_buffers_with_timestamp(indio_dev, st->data, pf->timestamp); +irq_done: + iio_trigger_notify_done(indio_dev->trig); + + return IRQ_HANDLED; +} + static const struct iio_info adis16480_info = { .read_raw = &adis16480_read_raw, .write_raw = &adis16480_write_raw, @@ -1279,8 +1406,6 @@ static int adis16480_probe(struct spi_device *spi) if (indio_dev == NULL) return -ENOMEM; - spi_set_drvdata(spi, indio_dev); - st = iio_priv(indio_dev); st->chip_info = &adis16480_chip_info[id->driver_data]; @@ -1341,7 +1466,8 @@ static int adis16480_probe(struct spi_device *spi) st->clk_freq = st->chip_info->int_clk; } - ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL); + ret = devm_adis_setup_buffer_and_trigger(&st->adis, indio_dev, + adis16480_trigger_handler); if (ret) return ret; diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c index ac354321f63a..351c303c8a8c 100644 --- a/drivers/iio/imu/adis_buffer.c +++ b/drivers/iio/imu/adis_buffer.c @@ -51,9 +51,13 @@ static int adis_update_scan_mode_burst(struct iio_dev *indio_dev, adis->xfer[0].tx_buf = tx; adis->xfer[0].bits_per_word = 8; adis->xfer[0].len = 2; + if (adis->data->burst_max_speed_hz) + adis->xfer[0].speed_hz = adis->data->burst_max_speed_hz; adis->xfer[1].rx_buf = adis->buffer; adis->xfer[1].bits_per_word = 8; adis->xfer[1].len = burst_length; + if (adis->data->burst_max_speed_hz) + adis->xfer[1].speed_hz = adis->data->burst_max_speed_hz; spi_message_init(&adis->msg); spi_message_add_tail(&adis->xfer[0], &adis->msg); @@ -129,31 +133,34 @@ static irqreturn_t adis_trigger_handler(int irq, void *p) struct adis *adis = iio_device_get_drvdata(indio_dev); int ret; - if (!adis->buffer) - return -ENOMEM; - if (adis->data->has_paging) { mutex_lock(&adis->state_lock); if (adis->current_page != 0) { adis->tx[0] = ADIS_WRITE_REG(ADIS_REG_PAGE_ID); adis->tx[1] = 0; - spi_write(adis->spi, adis->tx, 2); + ret = spi_write(adis->spi, adis->tx, 2); + if (ret) { + dev_err(&adis->spi->dev, "Failed to change device page: %d\n", ret); + mutex_unlock(&adis->state_lock); + goto irq_done; + } + + adis->current_page = 0; } } ret = spi_sync(adis->spi, &adis->msg); - if (ret) - dev_err(&adis->spi->dev, "Failed to read data: %d", ret); - - - if (adis->data->has_paging) { - adis->current_page = 0; + if (adis->data->has_paging) mutex_unlock(&adis->state_lock); + if (ret) { + dev_err(&adis->spi->dev, "Failed to read data: %d", ret); + goto irq_done; } iio_push_to_buffers_with_timestamp(indio_dev, adis->buffer, pf->timestamp); +irq_done: iio_trigger_notify_done(indio_dev->trig); return IRQ_HANDLED; diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c index fa5540fabacc..48eedc29b28a 100644 --- a/drivers/iio/imu/adis_trigger.c +++ b/drivers/iio/imu/adis_trigger.c @@ -62,7 +62,8 @@ int devm_adis_probe_trigger(struct adis *adis, struct iio_dev *indio_dev) int ret; adis->trig = devm_iio_trigger_alloc(&adis->spi->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!adis->trig) return -ENOMEM; diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c index 290b5ef83f77..824b5124a5f5 100644 --- a/drivers/iio/imu/bmi160/bmi160_core.c +++ b/drivers/iio/imu/bmi160/bmi160_core.c @@ -785,7 +785,8 @@ int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type) int ret; data->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (data->trig == NULL) return -ENOMEM; @@ -851,8 +852,7 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap, return ret; } - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; diff --git a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c index 8bd77185ccb7..86858da9cc38 100644 --- a/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c +++ b/drivers/iio/imu/inv_icm42600/inv_icm42600_core.c @@ -592,7 +592,7 @@ int inv_icm42600_core_probe(struct regmap *regmap, int chip, int irq, st->chip = chip; st->map = regmap; - ret = iio_read_mount_matrix(dev, "mount-matrix", &st->orientation); + ret = iio_read_mount_matrix(dev, &st->orientation); if (ret) { dev_err(dev, "failed to retrieve mounting matrix %d\n", ret); return ret; diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c index 6244a07048df..8a7a920e6200 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c @@ -570,11 +570,9 @@ static int inv_mpu6050_read_channel_data(struct iio_dev *indio_dev, freq_hz = INV_MPU6050_DIVIDER_TO_FIFO_RATE(st->chip_config.divider); period_us = 1000000 / freq_hz; - result = pm_runtime_get_sync(pdev); - if (result < 0) { - pm_runtime_put_noidle(pdev); + result = pm_runtime_resume_and_get(pdev); + if (result) return result; - } switch (chan->type) { case IIO_ANGL_VEL: @@ -812,11 +810,9 @@ static int inv_mpu6050_write_raw(struct iio_dev *indio_dev, return result; mutex_lock(&st->lock); - result = pm_runtime_get_sync(pdev); - if (result < 0) { - pm_runtime_put_noidle(pdev); + result = pm_runtime_resume_and_get(pdev); + if (result) goto error_write_raw_unlock; - } switch (mask) { case IIO_CHAN_INFO_SCALE: @@ -930,11 +926,9 @@ inv_mpu6050_fifo_rate_store(struct device *dev, struct device_attribute *attr, result = 0; goto fifo_rate_fail_unlock; } - result = pm_runtime_get_sync(pdev); - if (result < 0) { - pm_runtime_put_noidle(pdev); + result = pm_runtime_resume_and_get(pdev); + if (result) goto fifo_rate_fail_unlock; - } result = regmap_write(st->map, st->reg->sample_rate_div, d); if (result) @@ -1314,8 +1308,7 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) for (i = 0; i < INV_NUM_PARTS; ++i) { if (regval == hw_info[i].whoami) { dev_warn(regmap_get_device(st->map), - "whoami mismatch got %#02x (%s)" - "expected %#02hhx (%s)\n", + "whoami mismatch got 0x%02x (%s) expected 0x%02x (%s)\n", regval, hw_info[i].name, st->hw->whoami, st->hw->name); break; @@ -1323,7 +1316,7 @@ static int inv_check_and_setup_chip(struct inv_mpu6050_state *st) } if (i >= INV_NUM_PARTS) { dev_err(regmap_get_device(st->map), - "invalid whoami %#02x expected %#02hhx (%s)\n", + "invalid whoami 0x%02x expected 0x%02x (%s)\n", regval, st->hw->whoami, st->hw->name); return -ENODEV; } @@ -1422,7 +1415,6 @@ static void inv_mpu_pm_disable(void *data) { struct device *dev = data; - pm_runtime_put_sync_suspend(dev); pm_runtime_disable(dev); } @@ -1455,8 +1447,7 @@ int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name, pdata = dev_get_platdata(dev); if (!pdata) { - result = iio_read_mount_matrix(dev, "mount-matrix", - &st->orientation); + result = iio_read_mount_matrix(dev, &st->orientation); if (result) { dev_err(dev, "Failed to retrieve mounting matrix %d\n", result); diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c index de8ed1446d60..2d0e8cdd4848 100644 --- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c +++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c @@ -173,11 +173,9 @@ static int inv_mpu6050_set_enable(struct iio_dev *indio_dev, bool enable) if (enable) { scan = inv_scan_query(indio_dev); - result = pm_runtime_get_sync(pdev); - if (result < 0) { - pm_runtime_put_noidle(pdev); + result = pm_runtime_resume_and_get(pdev); + if (result) return result; - } /* * In case autosuspend didn't trigger, turn off first not * required sensors. @@ -238,7 +236,7 @@ int inv_mpu6050_probe_trigger(struct iio_dev *indio_dev, int irq_type) st->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!st->trig) return -ENOMEM; diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c index fc5a60fcfec0..1dabfd615dab 100644 --- a/drivers/iio/imu/kmx61.c +++ b/drivers/iio/imu/kmx61.c @@ -750,7 +750,7 @@ static int kmx61_set_power_state(struct kmx61_data *data, bool on, u8 device) } if (on) { - ret = pm_runtime_get_sync(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); } else { pm_runtime_mark_last_busy(&data->client->dev); ret = pm_runtime_put_autosuspend(&data->client->dev); @@ -759,8 +759,6 @@ static int kmx61_set_power_state(struct kmx61_data *data, bool on, u8 device) dev_err(&data->client->dev, "Failed: kmx61_set_power_state for %d, ret %d\n", on, ret); - if (on) - pm_runtime_put_noidle(&data->client->dev); return ret; } @@ -1264,7 +1262,7 @@ static struct iio_trigger *kmx61_trigger_setup(struct kmx61_data *data, "%s-%s-dev%d", indio_dev->name, tag, - indio_dev->id); + iio_device_id(indio_dev)); if (!trig) return ERR_PTR(-ENOMEM); @@ -1426,7 +1424,6 @@ static int kmx61_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); if (client->irq > 0) { iio_triggered_buffer_cleanup(data->acc_indio_dev); diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index 7cedaab096a7..db45f1fc0b81 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -15,19 +15,19 @@ * * Supported sensors: * - LSM6DS3: - * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 8KB * * - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C: - * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416 + * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 * - FIFO size: 4KB * * - LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP: - * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416, + * - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416, * 833 * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16 * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000 @@ -2256,7 +2256,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, return err; } - err = iio_read_mount_matrix(hw->dev, "mount-matrix", &hw->orientation); + err = iio_read_mount_matrix(hw->dev, &hw->orientation); if (err) return err; diff --git a/drivers/iio/imu/st_lsm9ds0/Kconfig b/drivers/iio/imu/st_lsm9ds0/Kconfig new file mode 100644 index 000000000000..53b7017014f8 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/Kconfig @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0-only + +config IIO_ST_LSM9DS0 + tristate "STMicroelectronics LSM9DS0 IMU driver" + depends on (I2C || SPI_MASTER) && SYSFS + depends on !SENSORS_LIS3_I2C + depends on !SENSORS_LIS3_SPI + select IIO_ST_LSM9DS0_I2C if I2C + select IIO_ST_LSM9DS0_SPI if SPI_MASTER + select IIO_ST_ACCEL_3AXIS + select IIO_ST_MAGN_3AXIS + + help + Say yes here to build support for STMicroelectronics LSM9DS0 IMU + sensor. Supported devices: accelerometer/magnetometer of lsm9ds0. + + To compile this driver as a module, choose M here: the module + will be called st_lsm9ds0. + +config IIO_ST_LSM9DS0_I2C + tristate + depends on IIO_ST_LSM9DS0 + select REGMAP_I2C + +config IIO_ST_LSM9DS0_SPI + tristate + depends on IIO_ST_LSM9DS0 + select REGMAP_SPI diff --git a/drivers/iio/imu/st_lsm9ds0/Makefile b/drivers/iio/imu/st_lsm9ds0/Makefile new file mode 100644 index 000000000000..488af523f648 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_IIO_ST_LSM9DS0) += st_lsm9ds0.o +st_lsm9ds0-y := st_lsm9ds0_core.o +obj-$(CONFIG_IIO_ST_LSM9DS0_I2C) += st_lsm9ds0_i2c.o +obj-$(CONFIG_IIO_ST_LSM9DS0_SPI) += st_lsm9ds0_spi.o diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h new file mode 100644 index 000000000000..146393afd9a7 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0.h @@ -0,0 +1,23 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +// STMicroelectronics LSM9DS0 IMU driver + +#ifndef ST_LSM9DS0_H +#define ST_LSM9DS0_H + +struct iio_dev; +struct regulator; + +struct st_lsm9ds0 { + struct device *dev; + const char *name; + int irq; + struct iio_dev *accel; + struct iio_dev *magn; + struct regulator *vdd; + struct regulator *vdd_io; +}; + +int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap); +int st_lsm9ds0_remove(struct st_lsm9ds0 *lsm9ds0); + +#endif /* ST_LSM9DS0_H */ diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c new file mode 100644 index 000000000000..8204f7303fd7 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_core.c @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics LSM9DS0 IMU driver + * + * Copyright (C) 2021, Intel Corporation + * + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/device.h> +#include <linux/err.h> +#include <linux/module.h> +#include <linux/regulator/consumer.h> + +#include <linux/iio/common/st_sensors.h> +#include <linux/iio/iio.h> + +#include "st_lsm9ds0.h" + +static int st_lsm9ds0_power_enable(struct device *dev, struct st_lsm9ds0 *lsm9ds0) +{ + int ret; + + /* Regulators not mandatory, but if requested we should enable them. */ + lsm9ds0->vdd = devm_regulator_get(dev, "vdd"); + if (IS_ERR(lsm9ds0->vdd)) { + dev_err(dev, "unable to get Vdd supply\n"); + return PTR_ERR(lsm9ds0->vdd); + } + ret = regulator_enable(lsm9ds0->vdd); + if (ret) { + dev_warn(dev, "Failed to enable specified Vdd supply\n"); + return ret; + } + + lsm9ds0->vdd_io = devm_regulator_get(dev, "vddio"); + if (IS_ERR(lsm9ds0->vdd_io)) { + dev_err(dev, "unable to get Vdd_IO supply\n"); + regulator_disable(lsm9ds0->vdd); + return PTR_ERR(lsm9ds0->vdd_io); + } + ret = regulator_enable(lsm9ds0->vdd_io); + if (ret) { + dev_warn(dev, "Failed to enable specified Vdd_IO supply\n"); + regulator_disable(lsm9ds0->vdd); + return ret; + } + + return 0; +} + +static void st_lsm9ds0_power_disable(void *data) +{ + struct st_lsm9ds0 *lsm9ds0 = data; + + regulator_disable(lsm9ds0->vdd_io); + regulator_disable(lsm9ds0->vdd); +} + +static int devm_st_lsm9ds0_power_enable(struct st_lsm9ds0 *lsm9ds0) +{ + struct device *dev = lsm9ds0->dev; + int ret; + + ret = st_lsm9ds0_power_enable(dev, lsm9ds0); + if (ret) + return ret; + + return devm_add_action_or_reset(dev, st_lsm9ds0_power_disable, lsm9ds0); +} + +static int st_lsm9ds0_probe_accel(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) +{ + const struct st_sensor_settings *settings; + struct device *dev = lsm9ds0->dev; + struct st_sensor_data *data; + + settings = st_accel_get_settings(lsm9ds0->name); + if (!settings) { + dev_err(dev, "device name %s not recognized.\n", lsm9ds0->name); + return -ENODEV; + } + + lsm9ds0->accel = devm_iio_device_alloc(dev, sizeof(*data)); + if (!lsm9ds0->accel) + return -ENOMEM; + + lsm9ds0->accel->name = lsm9ds0->name; + + data = iio_priv(lsm9ds0->accel); + data->sensor_settings = (struct st_sensor_settings *)settings; + data->dev = dev; + data->irq = lsm9ds0->irq; + data->regmap = regmap; + data->vdd = lsm9ds0->vdd; + data->vdd_io = lsm9ds0->vdd_io; + + return st_accel_common_probe(lsm9ds0->accel); +} + +static int st_lsm9ds0_probe_magn(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) +{ + const struct st_sensor_settings *settings; + struct device *dev = lsm9ds0->dev; + struct st_sensor_data *data; + + settings = st_magn_get_settings(lsm9ds0->name); + if (!settings) { + dev_err(dev, "device name %s not recognized.\n", lsm9ds0->name); + return -ENODEV; + } + + lsm9ds0->magn = devm_iio_device_alloc(dev, sizeof(*data)); + if (!lsm9ds0->magn) + return -ENOMEM; + + lsm9ds0->magn->name = lsm9ds0->name; + + data = iio_priv(lsm9ds0->magn); + data->sensor_settings = (struct st_sensor_settings *)settings; + data->dev = dev; + data->irq = lsm9ds0->irq; + data->regmap = regmap; + data->vdd = lsm9ds0->vdd; + data->vdd_io = lsm9ds0->vdd_io; + + return st_magn_common_probe(lsm9ds0->magn); +} + +int st_lsm9ds0_probe(struct st_lsm9ds0 *lsm9ds0, struct regmap *regmap) +{ + int ret; + + ret = devm_st_lsm9ds0_power_enable(lsm9ds0); + if (ret) + return ret; + + /* Setup accelerometer device */ + ret = st_lsm9ds0_probe_accel(lsm9ds0, regmap); + if (ret) + return ret; + + /* Setup magnetometer device */ + ret = st_lsm9ds0_probe_magn(lsm9ds0, regmap); + if (ret) + st_accel_common_remove(lsm9ds0->accel); + + return ret; +} +EXPORT_SYMBOL_GPL(st_lsm9ds0_probe); + +int st_lsm9ds0_remove(struct st_lsm9ds0 *lsm9ds0) +{ + st_magn_common_remove(lsm9ds0->magn); + st_accel_common_remove(lsm9ds0->accel); + + return 0; +} +EXPORT_SYMBOL_GPL(st_lsm9ds0_remove); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU core driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c new file mode 100644 index 000000000000..50a36ab53bc3 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_i2c.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics LSM9DS0 IMU driver + * + * Copyright (C) 2021, Intel Corporation + * + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> + +#include <linux/iio/common/st_sensors_i2c.h> + +#include "st_lsm9ds0.h" + +static const struct of_device_id st_lsm9ds0_of_match[] = { + { + .compatible = "st,lsm9ds0-imu", + .data = LSM9DS0_IMU_DEV_NAME, + }, + {} +}; +MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); + +static const struct i2c_device_id st_lsm9ds0_id_table[] = { + { LSM9DS0_IMU_DEV_NAME }, + {} +}; +MODULE_DEVICE_TABLE(i2c, st_lsm9ds0_id_table); + +static const struct regmap_config st_lsm9ds0_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0x80, +}; + +static int st_lsm9ds0_i2c_probe(struct i2c_client *client) +{ + const struct regmap_config *config = &st_lsm9ds0_regmap_config; + struct device *dev = &client->dev; + struct st_lsm9ds0 *lsm9ds0; + struct regmap *regmap; + + st_sensors_dev_name_probe(dev, client->name, sizeof(client->name)); + + lsm9ds0 = devm_kzalloc(dev, sizeof(*lsm9ds0), GFP_KERNEL); + if (!lsm9ds0) + return -ENOMEM; + + lsm9ds0->dev = dev; + lsm9ds0->name = client->name; + lsm9ds0->irq = client->irq; + + regmap = devm_regmap_init_i2c(client, config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + i2c_set_clientdata(client, lsm9ds0); + + return st_lsm9ds0_probe(lsm9ds0, regmap); +} + +static int st_lsm9ds0_i2c_remove(struct i2c_client *client) +{ + return st_lsm9ds0_remove(i2c_get_clientdata(client)); +} + +static struct i2c_driver st_lsm9ds0_driver = { + .driver = { + .name = "st-lsm9ds0-i2c", + .of_match_table = st_lsm9ds0_of_match, + }, + .probe_new = st_lsm9ds0_i2c_probe, + .remove = st_lsm9ds0_i2c_remove, + .id_table = st_lsm9ds0_id_table, +}; +module_i2c_driver(st_lsm9ds0_driver); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU I2C driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c new file mode 100644 index 000000000000..272c88990dd0 --- /dev/null +++ b/drivers/iio/imu/st_lsm9ds0/st_lsm9ds0_spi.c @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics LSM9DS0 IMU driver + * + * Copyright (C) 2021, Intel Corporation + * + * Author: Andy Shevchenko <andriy.shevchenko@linux.intel.com> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/spi/spi.h> + +#include <linux/iio/common/st_sensors_spi.h> + +#include "st_lsm9ds0.h" + +static const struct of_device_id st_lsm9ds0_of_match[] = { + { + .compatible = "st,lsm9ds0-imu", + .data = LSM9DS0_IMU_DEV_NAME, + }, + {} +}; +MODULE_DEVICE_TABLE(of, st_lsm9ds0_of_match); + +static const struct spi_device_id st_lsm9ds0_id_table[] = { + { LSM9DS0_IMU_DEV_NAME }, + {} +}; +MODULE_DEVICE_TABLE(spi, st_lsm9ds0_id_table); + +static const struct regmap_config st_lsm9ds0_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .read_flag_mask = 0xc0, +}; + +static int st_lsm9ds0_spi_probe(struct spi_device *spi) +{ + struct device *dev = &spi->dev; + struct st_lsm9ds0 *lsm9ds0; + struct regmap *regmap; + + st_sensors_dev_name_probe(dev, spi->modalias, sizeof(spi->modalias)); + + lsm9ds0 = devm_kzalloc(dev, sizeof(*lsm9ds0), GFP_KERNEL); + if (!lsm9ds0) + return -ENOMEM; + + lsm9ds0->dev = dev; + lsm9ds0->name = spi->modalias; + lsm9ds0->irq = spi->irq; + + regmap = devm_regmap_init_spi(spi, &st_lsm9ds0_regmap_config); + if (IS_ERR(regmap)) + return PTR_ERR(regmap); + + spi_set_drvdata(spi, lsm9ds0); + + return st_lsm9ds0_probe(lsm9ds0, regmap); +} + +static int st_lsm9ds0_spi_remove(struct spi_device *spi) +{ + return st_lsm9ds0_remove(spi_get_drvdata(spi)); +} + +static struct spi_driver st_lsm9ds0_driver = { + .driver = { + .name = "st-lsm9ds0-spi", + .of_match_table = st_lsm9ds0_of_match, + }, + .probe = st_lsm9ds0_spi_probe, + .remove = st_lsm9ds0_spi_remove, + .id_table = st_lsm9ds0_id_table, +}; +module_spi_driver(st_lsm9ds0_driver); + +MODULE_AUTHOR("Andy Shevchenko <andriy.shevchenko@linux.intel.com>"); +MODULE_DESCRIPTION("STMicroelectronics LSM9DS0 IMU SPI driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 9a8e16c7e9af..fdd623407b96 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -601,8 +601,10 @@ static unsigned int iio_storage_bytes_for_si(struct iio_dev *indio_dev, static unsigned int iio_storage_bytes_for_timestamp(struct iio_dev *indio_dev) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + return iio_storage_bytes_for_si(indio_dev, - indio_dev->scan_index_timestamp); + iio_dev_opaque->scan_index_timestamp); } static int iio_compute_scan_bytes(struct iio_dev *indio_dev, @@ -924,7 +926,6 @@ static int iio_buffer_update_demux(struct iio_dev *indio_dev, if (ret) goto error_clear_mux_table; out_loc += length; - in_loc += length; } buffer->demux_bounce = kzalloc(out_loc, GFP_KERNEL); if (buffer->demux_bounce == NULL) { @@ -1148,12 +1149,13 @@ int iio_update_buffers(struct iio_dev *indio_dev, struct iio_buffer *insert_buffer, 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; - mutex_lock(&indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); mutex_lock(&indio_dev->mlock); if (insert_buffer && iio_buffer_is_active(insert_buffer)) @@ -1176,7 +1178,7 @@ int iio_update_buffers(struct iio_dev *indio_dev, out_unlock: mutex_unlock(&indio_dev->mlock); - mutex_unlock(&indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -1469,6 +1471,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, struct iio_dev *indio_dev, int index) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_dev_attr *p; struct attribute **attr; int ret, i, attrn, scan_el_attrcount, buffer_attrcount; @@ -1495,7 +1498,7 @@ static int __iio_buffer_alloc_sysfs_and_mask(struct iio_buffer *buffer, goto error_cleanup_dynamic; scan_el_attrcount += ret; if (channels[i].type == IIO_TIMESTAMP) - indio_dev->scan_index_timestamp = + iio_dev_opaque->scan_index_timestamp = channels[i].scan_index; } if (indio_dev->masklength && buffer->scan_mask == NULL) { diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 59efb36db2c7..6d2175eb7af2 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -169,6 +169,20 @@ static const char * const iio_chan_info_postfix[] = { [IIO_CHAN_INFO_THERMOCOUPLE_TYPE] = "thermocouple_type", [IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient", }; +/** + * iio_device_id() - query the unique ID for the device + * @indio_dev: Device structure whose ID is being queried + * + * The IIO device ID is a unique index used for example for the naming + * of the character device /dev/iio\:device[ID] + */ +int iio_device_id(struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + return iio_dev_opaque->id; +} +EXPORT_SYMBOL_GPL(iio_device_id); /** * iio_sysfs_match_string_with_gaps - matches given string in an array with gaps @@ -257,7 +271,7 @@ int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id) mutex_unlock(&indio_dev->mlock); return -EBUSY; } - indio_dev->clock_id = clock_id; + iio_dev_opaque->clock_id = clock_id; mutex_unlock(&indio_dev->mlock); return 0; @@ -265,6 +279,18 @@ int iio_device_set_clock(struct iio_dev *indio_dev, clockid_t clock_id) EXPORT_SYMBOL(iio_device_set_clock); /** + * iio_device_get_clock() - Retrieve current timestamping clock for the device + * @indio_dev: IIO device structure containing the device + */ +clockid_t iio_device_get_clock(const struct iio_dev *indio_dev) +{ + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); + + return iio_dev_opaque->clock_id; +} +EXPORT_SYMBOL(iio_device_get_clock); + +/** * iio_get_time_ns() - utility function to get a time stamp for events etc * @indio_dev: device */ @@ -591,7 +617,6 @@ EXPORT_SYMBOL_GPL(iio_show_mount_matrix); * iio_read_mount_matrix() - retrieve iio device mounting matrix from * device "mount-matrix" property * @dev: device the mounting matrix property is assigned to - * @propname: device specific mounting matrix property name * @matrix: where to store retrieved matrix * * If device is assigned no mounting matrix property, a default 3x3 identity @@ -599,14 +624,12 @@ EXPORT_SYMBOL_GPL(iio_show_mount_matrix); * * Return: 0 if success, or a negative error code on failure. */ -int iio_read_mount_matrix(struct device *dev, const char *propname, - struct iio_mount_matrix *matrix) +int iio_read_mount_matrix(struct device *dev, struct iio_mount_matrix *matrix) { size_t len = ARRAY_SIZE(iio_mount_idmatrix.rotation); int err; - err = device_property_read_string_array(dev, propname, - matrix->rotation, len); + err = device_property_read_string_array(dev, "mount-matrix", matrix->rotation, len); if (err == len) return 0; @@ -1588,7 +1611,7 @@ static void iio_dev_release(struct device *device) iio_device_detach_buffers(indio_dev); - ida_simple_remove(&iio_ida, indio_dev->id); + ida_simple_remove(&iio_ida, iio_dev_opaque->id); kfree(iio_dev_opaque); } @@ -1628,17 +1651,17 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv) device_initialize(&indio_dev->dev); iio_device_set_drvdata(indio_dev, (void *)indio_dev); mutex_init(&indio_dev->mlock); - mutex_init(&indio_dev->info_exist_lock); + mutex_init(&iio_dev_opaque->info_exist_lock); INIT_LIST_HEAD(&iio_dev_opaque->channel_attr_list); - indio_dev->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); - if (indio_dev->id < 0) { + iio_dev_opaque->id = ida_simple_get(&iio_ida, 0, 0, GFP_KERNEL); + if (iio_dev_opaque->id < 0) { /* cannot use a dev_err as the name isn't available */ pr_err("failed to get device id\n"); kfree(iio_dev_opaque); return NULL; } - dev_set_name(&indio_dev->dev, "iio:device%d", indio_dev->id); + dev_set_name(&indio_dev->dev, "iio:device%d", iio_dev_opaque->id); INIT_LIST_HEAD(&iio_dev_opaque->buffer_list); INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers); @@ -1657,9 +1680,9 @@ void iio_device_free(struct iio_dev *dev) } EXPORT_SYMBOL(iio_device_free); -static void devm_iio_device_release(struct device *dev, void *res) +static void devm_iio_device_release(void *iio_dev) { - iio_device_free(*(struct iio_dev **)res); + iio_device_free(iio_dev); } /** @@ -1675,20 +1698,17 @@ static void devm_iio_device_release(struct device *dev, void *res) */ struct iio_dev *devm_iio_device_alloc(struct device *parent, int sizeof_priv) { - struct iio_dev **ptr, *iio_dev; + struct iio_dev *iio_dev; + int ret; - ptr = devres_alloc(devm_iio_device_release, sizeof(*ptr), - GFP_KERNEL); - if (!ptr) + iio_dev = iio_device_alloc(parent, sizeof_priv); + if (!iio_dev) return NULL; - iio_dev = iio_device_alloc(parent, sizeof_priv); - if (iio_dev) { - *ptr = iio_dev; - devres_add(parent, ptr); - } else { - devres_free(ptr); - } + ret = devm_add_action_or_reset(parent, devm_iio_device_release, + iio_dev); + if (ret) + return NULL; return iio_dev; } @@ -1704,11 +1724,12 @@ EXPORT_SYMBOL_GPL(devm_iio_device_alloc); **/ static int iio_chrdev_open(struct inode *inode, struct file *filp) { - struct iio_dev *indio_dev = container_of(inode->i_cdev, - struct iio_dev, chrdev); + struct iio_dev_opaque *iio_dev_opaque = + container_of(inode->i_cdev, struct iio_dev_opaque, chrdev); + struct iio_dev *indio_dev = &iio_dev_opaque->indio_dev; struct iio_dev_buffer_pair *ib; - if (test_and_set_bit(IIO_BUSY_BIT_POS, &indio_dev->flags)) + if (test_and_set_bit(IIO_BUSY_BIT_POS, &iio_dev_opaque->flags)) return -EBUSY; iio_device_get(indio_dev); @@ -1716,7 +1737,7 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp) ib = kmalloc(sizeof(*ib), GFP_KERNEL); if (!ib) { iio_device_put(indio_dev); - clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags); + clear_bit(IIO_BUSY_BIT_POS, &iio_dev_opaque->flags); return -ENOMEM; } @@ -1738,10 +1759,11 @@ static int iio_chrdev_open(struct inode *inode, struct file *filp) static int iio_chrdev_release(struct inode *inode, struct file *filp) { struct iio_dev_buffer_pair *ib = filp->private_data; - struct iio_dev *indio_dev = container_of(inode->i_cdev, - struct iio_dev, chrdev); + struct iio_dev_opaque *iio_dev_opaque = + container_of(inode->i_cdev, struct iio_dev_opaque, chrdev); + struct iio_dev *indio_dev = &iio_dev_opaque->indio_dev; kfree(ib); - clear_bit(IIO_BUSY_BIT_POS, &indio_dev->flags); + clear_bit(IIO_BUSY_BIT_POS, &iio_dev_opaque->flags); iio_device_put(indio_dev); return 0; @@ -1768,7 +1790,7 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) struct iio_ioctl_handler *h; int ret = -ENODEV; - mutex_lock(&indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); /** * The NULL check here is required to prevent crashing when a device @@ -1788,7 +1810,7 @@ static long iio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg) ret = -ENODEV; out_unlock: - mutex_unlock(&indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -1847,7 +1869,7 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) if (!indio_dev->info) return -EINVAL; - indio_dev->driver_module = this_mod; + iio_dev_opaque->driver_module = this_mod; /* If the calling driver did not initialize of_node, do it here */ if (!indio_dev->dev.of_node && indio_dev->dev.parent) indio_dev->dev.of_node = indio_dev->dev.parent->of_node; @@ -1889,19 +1911,19 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod) indio_dev->setup_ops = &noop_ring_setup_ops; if (iio_dev_opaque->attached_buffers_cnt) - cdev_init(&indio_dev->chrdev, &iio_buffer_fileops); + cdev_init(&iio_dev_opaque->chrdev, &iio_buffer_fileops); else if (iio_dev_opaque->event_interface) - cdev_init(&indio_dev->chrdev, &iio_event_fileops); + cdev_init(&iio_dev_opaque->chrdev, &iio_event_fileops); if (iio_dev_opaque->attached_buffers_cnt || iio_dev_opaque->event_interface) { - indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id); - indio_dev->chrdev.owner = this_mod; + indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), iio_dev_opaque->id); + iio_dev_opaque->chrdev.owner = this_mod; } /* assign device groups now; they should be all registered now */ indio_dev->dev.groups = iio_dev_opaque->groups; - ret = cdev_device_add(&indio_dev->chrdev, &indio_dev->dev); + ret = cdev_device_add(&iio_dev_opaque->chrdev, &indio_dev->dev); if (ret < 0) goto error_unreg_eventset; @@ -1925,9 +1947,11 @@ EXPORT_SYMBOL(__iio_device_register); **/ void iio_device_unregister(struct iio_dev *indio_dev) { - cdev_device_del(&indio_dev->chrdev, &indio_dev->dev); + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - mutex_lock(&indio_dev->info_exist_lock); + cdev_device_del(&iio_dev_opaque->chrdev, &indio_dev->dev); + + mutex_lock(&iio_dev_opaque->info_exist_lock); iio_device_unregister_debugfs(indio_dev); @@ -1938,35 +1962,27 @@ void iio_device_unregister(struct iio_dev *indio_dev) iio_device_wakeup_eventset(indio_dev); iio_buffer_wakeup_poll(indio_dev); - mutex_unlock(&indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); iio_buffers_free_sysfs_and_mask(indio_dev); } EXPORT_SYMBOL(iio_device_unregister); -static void devm_iio_device_unreg(struct device *dev, void *res) +static void devm_iio_device_unreg(void *indio_dev) { - iio_device_unregister(*(struct iio_dev **)res); + iio_device_unregister(indio_dev); } int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev, struct module *this_mod) { - struct iio_dev **ptr; int ret; - ptr = devres_alloc(devm_iio_device_unreg, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - *ptr = indio_dev; ret = __iio_device_register(indio_dev, this_mod); - if (!ret) - devres_add(dev, ptr); - else - devres_free(ptr); + if (ret) + return ret; - return ret; + return devm_add_action_or_reset(dev, devm_iio_device_unreg, indio_dev); } EXPORT_SYMBOL_GPL(__devm_iio_device_register); diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c index b2c94abbb487..b23caa2f2aa1 100644 --- a/drivers/iio/industrialio-trigger.c +++ b/drivers/iio/industrialio-trigger.c @@ -13,6 +13,7 @@ #include <linux/slab.h> #include <linux/iio/iio.h> +#include <linux/iio/iio-opaque.h> #include <linux/iio/trigger.h> #include "iio_core.h" #include "iio_core_trigger.h" @@ -116,14 +117,17 @@ EXPORT_SYMBOL(iio_trigger_unregister); int iio_trigger_set_immutable(struct iio_dev *indio_dev, struct iio_trigger *trig) { + struct iio_dev_opaque *iio_dev_opaque; + if (!indio_dev || !trig) return -EINVAL; + iio_dev_opaque = to_iio_dev_opaque(indio_dev); mutex_lock(&indio_dev->mlock); - WARN_ON(indio_dev->trig_readonly); + WARN_ON(iio_dev_opaque->trig_readonly); indio_dev->trig = iio_trigger_get(trig); - indio_dev->trig_readonly = true; + iio_dev_opaque->trig_readonly = true; mutex_unlock(&indio_dev->mlock); return 0; @@ -240,12 +244,13 @@ static void iio_trigger_put_irq(struct iio_trigger *trig, int irq) int iio_trigger_attach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(pf->indio_dev); bool notinuse = bitmap_empty(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER); int ret = 0; /* Prevent the module from being removed whilst attached to a trigger */ - __module_get(pf->indio_dev->driver_module); + __module_get(iio_dev_opaque->driver_module); /* Get irq number */ pf->irq = iio_trigger_get_irq(trig); @@ -284,13 +289,14 @@ out_free_irq: out_put_irq: iio_trigger_put_irq(trig, pf->irq); out_put_module: - module_put(pf->indio_dev->driver_module); + module_put(iio_dev_opaque->driver_module); return ret; } int iio_trigger_detach_poll_func(struct iio_trigger *trig, struct iio_poll_func *pf) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(pf->indio_dev); bool no_other_users = bitmap_weight(trig->pool, CONFIG_IIO_CONSUMERS_PER_TRIGGER) == 1; int ret = 0; @@ -304,7 +310,7 @@ int iio_trigger_detach_poll_func(struct iio_trigger *trig, trig->attached_own_device = false; iio_trigger_put_irq(trig, pf->irq); free_irq(pf->irq, pf); - module_put(pf->indio_dev->driver_module); + module_put(iio_dev_opaque->driver_module); return ret; } @@ -399,6 +405,7 @@ static ssize_t iio_trigger_write_current(struct device *dev, size_t len) { struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); struct iio_trigger *oldtrig = indio_dev->trig; struct iio_trigger *trig; int ret; @@ -408,7 +415,7 @@ static ssize_t iio_trigger_write_current(struct device *dev, mutex_unlock(&indio_dev->mlock); return -EBUSY; } - if (indio_dev->trig_readonly) { + if (iio_dev_opaque->trig_readonly) { mutex_unlock(&indio_dev->mlock); return -EPERM; } @@ -634,9 +641,9 @@ struct iio_trigger *devm_iio_trigger_alloc(struct device *parent, const char *fm } EXPORT_SYMBOL_GPL(devm_iio_trigger_alloc); -static void devm_iio_trigger_unreg(struct device *dev, void *res) +static void devm_iio_trigger_unreg(void *trigger_info) { - iio_trigger_unregister(*(struct iio_trigger **)res); + iio_trigger_unregister(trigger_info); } /** @@ -657,21 +664,13 @@ int __devm_iio_trigger_register(struct device *dev, struct iio_trigger *trig_info, struct module *this_mod) { - struct iio_trigger **ptr; int ret; - ptr = devres_alloc(devm_iio_trigger_unreg, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return -ENOMEM; - - *ptr = trig_info; ret = __iio_trigger_register(trig_info, this_mod); - if (!ret) - devres_add(dev, ptr); - else - devres_free(ptr); + if (ret) + return ret; - return ret; + return devm_add_action_or_reset(dev, devm_iio_trigger_unreg, trig_info); } EXPORT_SYMBOL_GPL(__devm_iio_trigger_register); diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c index 53da9ab17a62..4bedc65c9fe3 100644 --- a/drivers/iio/industrialio-triggered-event.c +++ b/drivers/iio/industrialio-triggered-event.c @@ -37,7 +37,7 @@ int iio_triggered_event_setup(struct iio_dev *indio_dev, indio_dev, "%s_consumer%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (indio_dev->pollfunc_event == NULL) return -ENOMEM; diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 9c22697b7e83..391a3380a1d1 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -10,6 +10,7 @@ #include <linux/of.h> #include <linux/iio/iio.h> +#include <linux/iio/iio-opaque.h> #include "iio_core.h" #include <linux/iio/machine.h> #include <linux/iio/driver.h> @@ -359,30 +360,24 @@ void iio_channel_release(struct iio_channel *channel) } EXPORT_SYMBOL_GPL(iio_channel_release); -static void devm_iio_channel_free(struct device *dev, void *res) +static void devm_iio_channel_free(void *iio_channel) { - struct iio_channel *channel = *(struct iio_channel **)res; - - iio_channel_release(channel); + iio_channel_release(iio_channel); } struct iio_channel *devm_iio_channel_get(struct device *dev, const char *channel_name) { - struct iio_channel **ptr, *channel; - - ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct iio_channel *channel; + int ret; channel = iio_channel_get(dev, channel_name); - if (IS_ERR(channel)) { - devres_free(ptr); + if (IS_ERR(channel)) return channel; - } - *ptr = channel; - devres_add(dev, ptr); + ret = devm_add_action_or_reset(dev, devm_iio_channel_free, channel); + if (ret) + return ERR_PTR(ret); return channel; } @@ -392,20 +387,16 @@ struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev, struct device_node *np, const char *channel_name) { - struct iio_channel **ptr, *channel; - - ptr = devres_alloc(devm_iio_channel_free, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct iio_channel *channel; + int ret; channel = of_iio_channel_get_by_name(np, channel_name); - if (IS_ERR(channel)) { - devres_free(ptr); + if (IS_ERR(channel)) return channel; - } - *ptr = channel; - devres_add(dev, ptr); + ret = devm_add_action_or_reset(dev, devm_iio_channel_free, channel); + if (ret) + return ERR_PTR(ret); return channel; } @@ -496,29 +487,24 @@ void iio_channel_release_all(struct iio_channel *channels) } EXPORT_SYMBOL_GPL(iio_channel_release_all); -static void devm_iio_channel_free_all(struct device *dev, void *res) +static void devm_iio_channel_free_all(void *iio_channels) { - struct iio_channel *channels = *(struct iio_channel **)res; - - iio_channel_release_all(channels); + iio_channel_release_all(iio_channels); } struct iio_channel *devm_iio_channel_get_all(struct device *dev) { - struct iio_channel **ptr, *channels; - - ptr = devres_alloc(devm_iio_channel_free_all, sizeof(*ptr), GFP_KERNEL); - if (!ptr) - return ERR_PTR(-ENOMEM); + struct iio_channel *channels; + int ret; channels = iio_channel_get_all(dev); - if (IS_ERR(channels)) { - devres_free(ptr); + if (IS_ERR(channels)) return channels; - } - *ptr = channels; - devres_add(dev, ptr); + ret = devm_add_action_or_reset(dev, devm_iio_channel_free_all, + channels); + if (ret) + return ERR_PTR(ret); return channels; } @@ -553,9 +539,10 @@ 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(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -563,7 +550,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val) ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_RAW); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -571,9 +558,10 @@ 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(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -581,7 +569,7 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val) ret = iio_channel_read(chan, val, NULL, IIO_CHAN_INFO_AVERAGE_RAW); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -646,9 +634,10 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan, 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(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -657,7 +646,7 @@ int iio_convert_raw_to_processed(struct iio_channel *chan, int raw, ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed, scale); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -666,9 +655,10 @@ EXPORT_SYMBOL_GPL(iio_convert_raw_to_processed); 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(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -676,7 +666,7 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2, ret = iio_channel_read(chan, val, val2, attribute); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -691,9 +681,10 @@ EXPORT_SYMBOL_GPL(iio_read_channel_offset); int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, unsigned int scale) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -714,7 +705,7 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val, } err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -748,9 +739,10 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan, const int **vals, int *type, int *length, enum iio_chan_info_enum attribute) { + struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev); int ret; - mutex_lock(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; @@ -758,7 +750,7 @@ int iio_read_avail_channel_attribute(struct iio_channel *chan, ret = iio_channel_read_avail(chan, vals, type, length, attribute); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -830,10 +822,11 @@ 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(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (!chan->indio_dev->info) { ret = -ENODEV; goto err_unlock; @@ -841,7 +834,7 @@ int iio_read_max_channel_raw(struct iio_channel *chan, int *val) ret = iio_channel_read_max(chan, val, NULL, &type, IIO_CHAN_INFO_RAW); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -849,10 +842,11 @@ EXPORT_SYMBOL_GPL(iio_read_max_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(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -860,7 +854,7 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type) *type = chan->channel->type; err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } @@ -876,9 +870,10 @@ static int iio_channel_write(struct iio_channel *chan, int val, int val2, 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(&chan->indio_dev->info_exist_lock); + mutex_lock(&iio_dev_opaque->info_exist_lock); if (chan->indio_dev->info == NULL) { ret = -ENODEV; goto err_unlock; @@ -886,7 +881,7 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2, ret = iio_channel_write(chan, val, val2, attribute); err_unlock: - mutex_unlock(&chan->indio_dev->info_exist_lock); + mutex_unlock(&iio_dev_opaque->info_exist_lock); return ret; } diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig index 917f9becf9c7..a62c7b4b8678 100644 --- a/drivers/iio/light/Kconfig +++ b/drivers/iio/light/Kconfig @@ -499,6 +499,17 @@ config TSL2583 Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices. Access ALS data via iio, sysfs. +config TSL2591 + tristate "TAOS TSL2591 ambient light sensor" + depends on I2C + help + Select Y here for support of the AMS/TAOS TSL2591 ambient light sensor, + featuring channels for combined visible + IR intensity and lux illuminance. + Access data via iio and sysfs. Supports iio_events. + + To compile this driver as a module, select M: the + module will be called tsl2591. + config TSL2772 tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors" depends on I2C diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile index ea376deaca54..d10912faf964 100644 --- a/drivers/iio/light/Makefile +++ b/drivers/iio/light/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_ST_UVIS25_SPI) += st_uvis25_spi.o obj-$(CONFIG_TCS3414) += tcs3414.o obj-$(CONFIG_TCS3472) += tcs3472.o obj-$(CONFIG_TSL2583) += tsl2583.o +obj-$(CONFIG_TSL2591) += tsl2591.o obj-$(CONFIG_TSL2772) += tsl2772.o obj-$(CONFIG_TSL4531) += tsl4531.o obj-$(CONFIG_US5182D) += us5182d.o diff --git a/drivers/iio/light/acpi-als.c b/drivers/iio/light/acpi-als.c index 0a6ab5761eec..e1ff6f524f4b 100644 --- a/drivers/iio/light/acpi-als.c +++ b/drivers/iio/light/acpi-als.c @@ -204,7 +204,8 @@ static int acpi_als_add(struct acpi_device *device) indio_dev->channels = acpi_als_channels; indio_dev->num_channels = ARRAY_SIZE(acpi_als_channels); - als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, indio_dev->id); + als->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, + iio_device_id(indio_dev)); if (!als->trig) return -ENOMEM; diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c index 85c8a05b73cb..2ff252c75c03 100644 --- a/drivers/iio/light/hid-sensor-als.c +++ b/drivers/iio/light/hid-sensor-als.c @@ -6,13 +6,10 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> +#include <linux/mod_devicetable.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include "../common/hid-sensors/hid-sensor-trigger.h" @@ -392,3 +389,4 @@ module_platform_driver(hid_als_platform_driver); MODULE_DESCRIPTION("HID Sensor ALS"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c index 17d167c3d595..1621530f5f61 100644 --- a/drivers/iio/light/hid-sensor-prox.c +++ b/drivers/iio/light/hid-sensor-prox.c @@ -6,13 +6,10 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> +#include <linux/mod_devicetable.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include "../common/hid-sensors/hid-sensor-trigger.h" @@ -350,3 +347,4 @@ module_platform_driver(hid_prox_platform_driver); MODULE_DESCRIPTION("HID Sensor Proximity"); MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c index 2f8b494f3e08..9de3262aa688 100644 --- a/drivers/iio/light/isl29028.c +++ b/drivers/iio/light/isl29028.c @@ -339,9 +339,7 @@ static int isl29028_set_pm_runtime_busy(struct isl29028_chip *chip, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); @@ -647,7 +645,6 @@ static int isl29028_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); return isl29028_clear_configure_reg(chip); } diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c index b93b85dbc3a6..ba53b50d711a 100644 --- a/drivers/iio/light/isl29125.c +++ b/drivers/iio/light/isl29125.c @@ -51,7 +51,11 @@ struct isl29125_data { struct i2c_client *client; u8 conf1; - u16 buffer[8]; /* 3x 16-bit, padding, 8 bytes timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u16 chans[3]; + s64 timestamp __aligned(8); + } scan; }; #define ISL29125_CHANNEL(_color, _si) { \ @@ -184,10 +188,10 @@ static irqreturn_t isl29125_trigger_handler(int irq, void *p) if (ret < 0) goto done; - data->buffer[j++] = ret; + data->scan.chans[j++] = ret; } - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); done: diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c index b4323d2db0b1..1830221da48d 100644 --- a/drivers/iio/light/ltr501.c +++ b/drivers/iio/light/ltr501.c @@ -32,9 +32,12 @@ #define LTR501_PART_ID 0x86 #define LTR501_MANUFAC_ID 0x87 #define LTR501_ALS_DATA1 0x88 /* 16-bit, little endian */ +#define LTR501_ALS_DATA1_UPPER 0x89 /* upper 8 bits of LTR501_ALS_DATA1 */ #define LTR501_ALS_DATA0 0x8a /* 16-bit, little endian */ +#define LTR501_ALS_DATA0_UPPER 0x8b /* upper 8 bits of LTR501_ALS_DATA0 */ #define LTR501_ALS_PS_STATUS 0x8c #define LTR501_PS_DATA 0x8d /* 16-bit, little endian */ +#define LTR501_PS_DATA_UPPER 0x8e /* upper 8 bits of LTR501_PS_DATA */ #define LTR501_INTR 0x8f /* output mode, polarity, mode */ #define LTR501_PS_THRESH_UP 0x90 /* 11 bit, ps upper threshold */ #define LTR501_PS_THRESH_LOW 0x92 /* 11 bit, ps lower threshold */ @@ -149,7 +152,7 @@ struct ltr501_chip_info { struct ltr501_data { struct i2c_client *client; struct mutex lock_als, lock_ps; - struct ltr501_chip_info *chip_info; + const struct ltr501_chip_info *chip_info; u8 als_contr, ps_contr; int als_period, ps_period; /* period in micro seconds */ struct regmap *regmap; @@ -406,18 +409,19 @@ static int ltr501_read_als(const struct ltr501_data *data, __le16 buf[2]) static int ltr501_read_ps(const struct ltr501_data *data) { - int ret, status; + __le16 status; + int ret; ret = ltr501_drdy(data, LTR501_STATUS_PS_RDY); if (ret < 0) return ret; ret = regmap_bulk_read(data->regmap, LTR501_PS_DATA, - &status, 2); + &status, sizeof(status)); if (ret < 0) return ret; - return status; + return le16_to_cpu(status); } static int ltr501_read_intr_prst(const struct ltr501_data *data, @@ -735,7 +739,7 @@ static int ltr501_write_raw(struct iio_dev *indio_dev, { struct ltr501_data *data = iio_priv(indio_dev); int i, ret, freq_val, freq_val2; - struct ltr501_chip_info *info = data->chip_info; + const struct ltr501_chip_info *info = data->chip_info; ret = iio_device_claim_direct_mode(indio_dev); if (ret) @@ -1082,7 +1086,7 @@ static ssize_t ltr501_show_proximity_scale_avail(struct device *dev, char *buf) { struct ltr501_data *data = iio_priv(dev_to_iio_dev(dev)); - struct ltr501_chip_info *info = data->chip_info; + const struct ltr501_chip_info *info = data->chip_info; ssize_t len = 0; int i; @@ -1104,7 +1108,7 @@ static ssize_t ltr501_show_intensity_scale_avail(struct device *dev, char *buf) { struct ltr501_data *data = iio_priv(dev_to_iio_dev(dev)); - struct ltr501_chip_info *info = data->chip_info; + const struct ltr501_chip_info *info = data->chip_info; ssize_t len = 0; int i; @@ -1184,7 +1188,7 @@ static const struct iio_info ltr301_info = { .write_event_config = <r501_write_event_config, }; -static struct ltr501_chip_info ltr501_chip_info_tbl[] = { +static const struct ltr501_chip_info ltr501_chip_info_tbl[] = { [ltr501] = { .partid = 0x08, .als_gain = ltr501_als_gain_tbl, @@ -1205,7 +1209,7 @@ static struct ltr501_chip_info ltr501_chip_info_tbl[] = { .als_gain_tbl_size = ARRAY_SIZE(ltr559_als_gain_tbl), .ps_gain = ltr559_ps_gain_tbl, .ps_gain_tbl_size = ARRAY_SIZE(ltr559_ps_gain_tbl), - .als_mode_active = BIT(1), + .als_mode_active = BIT(0), .als_gain_mask = BIT(2) | BIT(3) | BIT(4), .als_gain_shift = 2, .info = <r501_info, @@ -1354,9 +1358,12 @@ static bool ltr501_is_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { case LTR501_ALS_DATA1: + case LTR501_ALS_DATA1_UPPER: case LTR501_ALS_DATA0: + case LTR501_ALS_DATA0_UPPER: case LTR501_ALS_PS_STATUS: case LTR501_PS_DATA: + case LTR501_PS_DATA_UPPER: return true; default: return false; diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c index bfade6577a38..a52b2c788540 100644 --- a/drivers/iio/light/pa12203001.c +++ b/drivers/iio/light/pa12203001.c @@ -186,9 +186,7 @@ static int pa12203001_set_power_state(struct pa12203001_data *data, bool on, } if (on) { - ret = pm_runtime_get_sync(&data->client->dev); - if (ret < 0) - pm_runtime_put_noidle(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); } else { pm_runtime_mark_last_busy(&data->client->dev); diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c index 033578f444e4..c2dd8a3d4217 100644 --- a/drivers/iio/light/rpr0521.c +++ b/drivers/iio/light/rpr0521.c @@ -360,7 +360,7 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on, * both stay enabled until _suspend(). */ if (on) { - ret = pm_runtime_get_sync(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); } else { pm_runtime_mark_last_busy(&data->client->dev); ret = pm_runtime_put_autosuspend(&data->client->dev); @@ -369,9 +369,6 @@ static int rpr0521_set_power_state(struct rpr0521_data *data, bool on, dev_err(&data->client->dev, "Failed: rpr0521_set_power_state for %d, ret %d\n", on, ret); - if (on) - pm_runtime_put_noidle(&data->client->dev); - return ret; } @@ -985,7 +982,7 @@ static int rpr0521_probe(struct i2c_client *client, /* Trigger0 producer setup */ data->drdy_trigger0 = devm_iio_trigger_alloc( indio_dev->dev.parent, - "%s-dev%d", indio_dev->name, indio_dev->id); + "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); if (!data->drdy_trigger0) { ret = -ENOMEM; goto err_pm_disable; @@ -1038,7 +1035,6 @@ static int rpr0521_probe(struct i2c_client *client, err_pm_disable: pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); err_poweroff: rpr0521_poweroff(data); @@ -1053,7 +1049,6 @@ static int rpr0521_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); rpr0521_poweroff(iio_priv(indio_dev)); diff --git a/drivers/iio/light/si1133.c b/drivers/iio/light/si1133.c index c280b4195003..f8c9b2cc322e 100644 --- a/drivers/iio/light/si1133.c +++ b/drivers/iio/light/si1133.c @@ -352,22 +352,22 @@ static int si1133_parse_response_err(struct device *dev, u32 resp, u8 cmd) switch (resp) { case SI1133_ERR_OUTPUT_BUFFER_OVERFLOW: - dev_warn(dev, "Output buffer overflow: %#02hhx\n", cmd); + dev_warn(dev, "Output buffer overflow: 0x%02x\n", cmd); return -EOVERFLOW; case SI1133_ERR_SATURATION_ADC_OR_OVERFLOW_ACCUMULATION: - dev_warn(dev, "Saturation of the ADC or overflow of accumulation: %#02hhx\n", + dev_warn(dev, "Saturation of the ADC or overflow of accumulation: 0x%02x\n", cmd); return -EOVERFLOW; case SI1133_ERR_INVALID_LOCATION_CMD: dev_warn(dev, - "Parameter access to an invalid location: %#02hhx\n", + "Parameter access to an invalid location: 0x%02x\n", cmd); return -EINVAL; case SI1133_ERR_INVALID_CMD: - dev_warn(dev, "Invalid command %#02hhx\n", cmd); + dev_warn(dev, "Invalid command 0x%02x\n", cmd); return -EINVAL; default: - dev_warn(dev, "Unknown error %#02hhx\n", cmd); + dev_warn(dev, "Unknown error 0x%02x\n", cmd); return -EINVAL; } } @@ -400,7 +400,7 @@ static int si1133_command(struct si1133_data *data, u8 cmd) err = regmap_write(data->regmap, SI1133_REG_COMMAND, cmd); if (err) { - dev_warn(dev, "Failed to write command %#02hhx, ret=%d\n", cmd, + dev_warn(dev, "Failed to write command 0x%02x, ret=%d\n", cmd, err); goto out; } @@ -425,7 +425,7 @@ static int si1133_command(struct si1133_data *data, u8 cmd) SI1133_CMD_TIMEOUT_MS * 1000); if (err) { dev_warn(dev, - "Failed to read command %#02hhx, ret=%d\n", + "Failed to read command 0x%02x, ret=%d\n", cmd, err); goto out; } @@ -978,11 +978,11 @@ static int si1133_validate_ids(struct iio_dev *iio_dev) return err; dev_info(&iio_dev->dev, - "Device ID part %#02hhx rev %#02hhx mfr %#02hhx\n", + "Device ID part 0x%02x rev 0x%02x mfr 0x%02x\n", part_id, rev_id, mfr_id); if (part_id != SI1133_PART_ID) { dev_err(&iio_dev->dev, - "Part ID mismatch got %#02hhx, expected %#02x\n", + "Part ID mismatch got 0x%02x, expected 0x%02x\n", part_id, SI1133_PART_ID); return -ENODEV; } diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c index 9b5c99823943..e2abad48b9f4 100644 --- a/drivers/iio/light/si1145.c +++ b/drivers/iio/light/si1145.c @@ -271,7 +271,7 @@ static int si1145_command(struct si1145_data *data, u8 cmd) if ((ret & ~SI1145_RSP_COUNTER_MASK) == 0) { if (ret == data->rsp_seq) { if (time_after(jiffies, stop_jiffies)) { - dev_warn(dev, "timeout on command %#02hhx\n", + dev_warn(dev, "timeout on command 0x%02x\n", cmd); ret = -ETIMEDOUT; break; @@ -291,12 +291,12 @@ static int si1145_command(struct si1145_data *data, u8 cmd) ret = -EIO; } else { if (ret == SI1145_RSP_INVALID_SETTING) { - dev_warn(dev, "INVALID_SETTING error on command %#02hhx\n", + dev_warn(dev, "INVALID_SETTING error on command 0x%02x\n", cmd); ret = -EINVAL; } else { /* All overflows are treated identically */ - dev_dbg(dev, "overflow, ret=%d, cmd=%#02hhx\n", + dev_dbg(dev, "overflow, ret=%d, cmd=0x%02x\n", ret, cmd); ret = -EOVERFLOW; } @@ -1243,7 +1243,7 @@ static int si1145_probe_trigger(struct iio_dev *indio_dev) int ret; trig = devm_iio_trigger_alloc(&client->dev, - "%s-dev%d", indio_dev->name, indio_dev->id); + "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); if (!trig) return -ENOMEM; @@ -1299,10 +1299,10 @@ static int si1145_probe(struct i2c_client *client, SI1145_REG_SEQ_ID); if (ret < 0) return ret; - dev_info(&client->dev, "device ID part %#02hhx rev %#02hhx seq %#02hhx\n", + dev_info(&client->dev, "device ID part 0x%02x rev 0x%02x seq 0x%02x\n", part_id, rev_id, seq_id); if (part_id != data->part_info->part) { - dev_err(&client->dev, "part ID mismatch got %#02hhx, expected %#02x\n", + dev_err(&client->dev, "part ID mismatch got 0x%02x, expected 0x%02x\n", part_id, data->part_info->part); return -ENODEV; } diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c index 6fe5d46f80d4..0593abd600ec 100644 --- a/drivers/iio/light/tcs3414.c +++ b/drivers/iio/light/tcs3414.c @@ -53,7 +53,11 @@ struct tcs3414_data { u8 control; u8 gain; u8 timing; - u16 buffer[8]; /* 4x 16-bit + 8 bytes timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u16 chans[4]; + s64 timestamp __aligned(8); + } scan; }; #define TCS3414_CHANNEL(_color, _si, _addr) { \ @@ -209,10 +213,10 @@ static irqreturn_t tcs3414_trigger_handler(int irq, void *p) if (ret < 0) goto done; - data->buffer[j++] = ret; + data->scan.chans[j++] = ret; } - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); done: diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c index a0dc447aeb68..371c6a39a165 100644 --- a/drivers/iio/light/tcs3472.c +++ b/drivers/iio/light/tcs3472.c @@ -64,7 +64,11 @@ struct tcs3472_data { u8 control; u8 atime; u8 apers; - u16 buffer[8]; /* 4 16-bit channels + 64-bit timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u16 chans[4]; + s64 timestamp __aligned(8); + } scan; }; static const struct iio_event_spec tcs3472_events[] = { @@ -386,10 +390,10 @@ static irqreturn_t tcs3472_trigger_handler(int irq, void *p) if (ret < 0) goto done; - data->buffer[j++] = ret; + data->scan.chans[j++] = ret; } - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); done: @@ -531,7 +535,8 @@ static int tcs3472_probe(struct i2c_client *client, return 0; free_irq: - free_irq(client->irq, indio_dev); + if (client->irq) + free_irq(client->irq, indio_dev); buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); return ret; @@ -559,7 +564,8 @@ static int tcs3472_remove(struct i2c_client *client) struct iio_dev *indio_dev = i2c_get_clientdata(client); iio_device_unregister(indio_dev); - free_irq(client->irq, indio_dev); + if (client->irq) + free_irq(client->irq, indio_dev); iio_triggered_buffer_cleanup(indio_dev); tcs3472_powerdown(iio_priv(indio_dev)); diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c index c9d8f07a6fcd..7e101d5f72ee 100644 --- a/drivers/iio/light/tsl2583.c +++ b/drivers/iio/light/tsl2583.c @@ -644,9 +644,7 @@ static int tsl2583_set_pm_runtime_busy(struct tsl2583_chip *chip, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(&chip->client->dev); - if (ret < 0) - pm_runtime_put_noidle(&chip->client->dev); + ret = pm_runtime_resume_and_get(&chip->client->dev); } else { pm_runtime_mark_last_busy(&chip->client->dev); ret = pm_runtime_put_autosuspend(&chip->client->dev); @@ -729,8 +727,10 @@ static int tsl2583_read_raw(struct iio_dev *indio_dev, read_done: mutex_unlock(&chip->als_mutex); - if (ret < 0) + if (ret < 0) { + tsl2583_set_pm_runtime_busy(chip, false); return ret; + } /* * Preserve the ret variable if the call to @@ -791,8 +791,10 @@ static int tsl2583_write_raw(struct iio_dev *indio_dev, mutex_unlock(&chip->als_mutex); - if (ret < 0) + if (ret < 0) { + tsl2583_set_pm_runtime_busy(chip, false); return ret; + } ret = tsl2583_set_pm_runtime_busy(chip, false); if (ret < 0) @@ -880,7 +882,6 @@ static int tsl2583_remove(struct i2c_client *client) pm_runtime_disable(&client->dev); pm_runtime_set_suspended(&client->dev); - pm_runtime_put_noidle(&client->dev); return tsl2583_set_power_state(chip, TSL2583_CNTL_PWR_OFF); } diff --git a/drivers/iio/light/tsl2591.c b/drivers/iio/light/tsl2591.c new file mode 100644 index 000000000000..39e68d0c9d6a --- /dev/null +++ b/drivers/iio/light/tsl2591.c @@ -0,0 +1,1225 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright (C) 2021 Joe Sandom <joe.g.sandom@gmail.com> + * + * Datasheet: https://ams.com/tsl25911#tab/documents + * + * Device driver for the TAOS TSL2591. This is a very-high sensitivity + * light-to-digital converter that transforms light intensity into a digital + * signal. + */ + +#include <linux/bitfield.h> +#include <linux/debugfs.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/iopoll.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/pm_runtime.h> +#include <linux/sysfs.h> + +#include <asm/unaligned.h> + +#include <linux/iio/events.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +/* ADC integration time, field value to time in ms */ +#define TSL2591_FVAL_TO_MSEC(x) (((x) + 1) * 100) +/* ADC integration time, field value to time in seconds */ +#define TSL2591_FVAL_TO_SEC(x) ((x) + 1) +/* ADC integration time, time in seconds to field value */ +#define TSL2591_SEC_TO_FVAL(x) ((x) - 1) + +/* TSL2591 register set */ +#define TSL2591_ENABLE 0x00 +#define TSL2591_CONTROL 0x01 +#define TSL2591_AILTL 0x04 +#define TSL2591_AILTH 0x05 +#define TSL2591_AIHTL 0x06 +#define TSL2591_AIHTH 0x07 +#define TSL2591_NP_AILTL 0x08 +#define TSL2591_NP_AILTH 0x09 +#define TSL2591_NP_AIHTL 0x0A +#define TSL2591_NP_AIHTH 0x0B +#define TSL2591_PERSIST 0x0C +#define TSL2591_PACKAGE_ID 0x11 +#define TSL2591_DEVICE_ID 0x12 +#define TSL2591_STATUS 0x13 +#define TSL2591_C0_DATAL 0x14 +#define TSL2591_C0_DATAH 0x15 +#define TSL2591_C1_DATAL 0x16 +#define TSL2591_C1_DATAH 0x17 + +/* TSL2591 command register definitions */ +#define TSL2591_CMD_NOP 0xA0 +#define TSL2591_CMD_SF_INTSET 0xE4 +#define TSL2591_CMD_SF_CALS_I 0xE5 +#define TSL2591_CMD_SF_CALS_NPI 0xE7 +#define TSL2591_CMD_SF_CNP_ALSI 0xEA + +/* TSL2591 enable register definitions */ +#define TSL2591_PWR_ON 0x01 +#define TSL2591_PWR_OFF 0x00 +#define TSL2591_ENABLE_ALS 0x02 +#define TSL2591_ENABLE_ALS_INT 0x10 +#define TSL2591_ENABLE_SLEEP_INT 0x40 +#define TSL2591_ENABLE_NP_INT 0x80 + +/* TSL2591 control register definitions */ +#define TSL2591_CTRL_ALS_INTEGRATION_100MS 0x00 +#define TSL2591_CTRL_ALS_INTEGRATION_200MS 0x01 +#define TSL2591_CTRL_ALS_INTEGRATION_300MS 0x02 +#define TSL2591_CTRL_ALS_INTEGRATION_400MS 0x03 +#define TSL2591_CTRL_ALS_INTEGRATION_500MS 0x04 +#define TSL2591_CTRL_ALS_INTEGRATION_600MS 0x05 +#define TSL2591_CTRL_ALS_LOW_GAIN 0x00 +#define TSL2591_CTRL_ALS_MED_GAIN 0x10 +#define TSL2591_CTRL_ALS_HIGH_GAIN 0x20 +#define TSL2591_CTRL_ALS_MAX_GAIN 0x30 +#define TSL2591_CTRL_SYS_RESET 0x80 + +/* TSL2591 persist register definitions */ +#define TSL2591_PRST_ALS_INT_CYCLE_0 0x00 +#define TSL2591_PRST_ALS_INT_CYCLE_ANY 0x01 +#define TSL2591_PRST_ALS_INT_CYCLE_2 0x02 +#define TSL2591_PRST_ALS_INT_CYCLE_3 0x03 +#define TSL2591_PRST_ALS_INT_CYCLE_5 0x04 +#define TSL2591_PRST_ALS_INT_CYCLE_10 0x05 +#define TSL2591_PRST_ALS_INT_CYCLE_15 0x06 +#define TSL2591_PRST_ALS_INT_CYCLE_20 0x07 +#define TSL2591_PRST_ALS_INT_CYCLE_25 0x08 +#define TSL2591_PRST_ALS_INT_CYCLE_30 0x09 +#define TSL2591_PRST_ALS_INT_CYCLE_35 0x0A +#define TSL2591_PRST_ALS_INT_CYCLE_40 0x0B +#define TSL2591_PRST_ALS_INT_CYCLE_45 0x0C +#define TSL2591_PRST_ALS_INT_CYCLE_50 0x0D +#define TSL2591_PRST_ALS_INT_CYCLE_55 0x0E +#define TSL2591_PRST_ALS_INT_CYCLE_60 0x0F +#define TSL2591_PRST_ALS_INT_CYCLE_MAX (BIT(4) - 1) + +/* TSL2591 PID register mask */ +#define TSL2591_PACKAGE_ID_MASK GENMASK(5, 4) + +/* TSL2591 ID register mask */ +#define TSL2591_DEVICE_ID_MASK GENMASK(7, 0) + +/* TSL2591 status register masks */ +#define TSL2591_STS_ALS_VALID_MASK BIT(0) +#define TSL2591_STS_ALS_INT_MASK BIT(4) +#define TSL2591_STS_NPERS_INT_MASK BIT(5) +#define TSL2591_STS_VAL_HIGH_MASK BIT(0) + +/* TSL2591 constant values */ +#define TSL2591_PACKAGE_ID_VAL 0x00 +#define TSL2591_DEVICE_ID_VAL 0x50 + +/* Power off suspend delay time MS */ +#define TSL2591_POWER_OFF_DELAY_MS 2000 + +/* TSL2591 default values */ +#define TSL2591_DEFAULT_ALS_INT_TIME TSL2591_CTRL_ALS_INTEGRATION_300MS +#define TSL2591_DEFAULT_ALS_GAIN TSL2591_CTRL_ALS_MED_GAIN +#define TSL2591_DEFAULT_ALS_PERSIST TSL2591_PRST_ALS_INT_CYCLE_ANY +#define TSL2591_DEFAULT_ALS_LOWER_THRESH 100 +#define TSL2591_DEFAULT_ALS_UPPER_THRESH 1500 + +/* TSL2591 number of data registers */ +#define TSL2591_NUM_DATA_REGISTERS 4 + +/* TSL2591 number of valid status reads on ADC complete */ +#define TSL2591_ALS_STS_VALID_COUNT 10 + +/* TSL2591 delay period between polls when checking for ALS valid flag */ +#define TSL2591_DELAY_PERIOD_US 10000 + +/* TSL2591 maximum values */ +#define TSL2591_MAX_ALS_INT_TIME_MS 600 +#define TSL2591_ALS_MAX_VALUE (BIT(16) - 1) + +/* + * LUX calculations; + * AGAIN values from Adafruit's TSL2591 Arduino library + * https://github.com/adafruit/Adafruit_TSL2591_Library + */ +#define TSL2591_CTRL_ALS_LOW_GAIN_MULTIPLIER 1 +#define TSL2591_CTRL_ALS_MED_GAIN_MULTIPLIER 25 +#define TSL2591_CTRL_ALS_HIGH_GAIN_MULTIPLIER 428 +#define TSL2591_CTRL_ALS_MAX_GAIN_MULTIPLIER 9876 +#define TSL2591_LUX_COEFFICIENT 408 + +struct tsl2591_als_settings { + u16 als_lower_thresh; + u16 als_upper_thresh; + u8 als_int_time; + u8 als_persist; + u8 als_gain; +}; + +struct tsl2591_chip { + struct tsl2591_als_settings als_settings; + struct i2c_client *client; + /* + * Keep als_settings in sync with hardware state + * and ensure multiple readers are serialized. + */ + struct mutex als_mutex; + bool events_enabled; +}; + +/* + * Period table is ALS persist cycle x integration time setting + * Integration times: 100ms, 200ms, 300ms, 400ms, 500ms, 600ms + * ALS cycles: 1, 2, 3, 5, 10, 20, 25, 30, 35, 40, 45, 50, 55, 60 + */ +static const char * const tsl2591_als_period_list[] = { + "0.1 0.2 0.3 0.5 1.0 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5 6.0", + "0.2 0.4 0.6 1.0 2.0 4.0 5.0 6.0 7.0 8.0 9.0 10.0 11.0 12.0", + "0.3 0.6 0.9 1.5 3.0 6.0 7.5 9.0 10.5 12.0 13.5 15.0 16.5 18.0", + "0.4 0.8 1.2 2.0 4.0 8.0 10.0 12.0 14.0 16.0 18.0 20.0 22.0 24.0", + "0.5 1.0 1.5 2.5 5.0 10.0 12.5 15.0 17.5 20.0 22.5 25.0 27.5 30.0", + "0.6 1.2 1.8 3.0 6.0 12.0 15.0 18.0 21.0 24.0 27.0 30.0 33.0 36.0", +}; + +static const int tsl2591_int_time_available[] = { + 1, 2, 3, 4, 5, 6, +}; + +static const int tsl2591_calibscale_available[] = { + 1, 25, 428, 9876, +}; + +static int tsl2591_set_als_lower_threshold(struct tsl2591_chip *chip, + u16 als_lower_threshold); +static int tsl2591_set_als_upper_threshold(struct tsl2591_chip *chip, + u16 als_upper_threshold); + +static int tsl2591_gain_to_multiplier(const u8 als_gain) +{ + switch (als_gain) { + case TSL2591_CTRL_ALS_LOW_GAIN: + return TSL2591_CTRL_ALS_LOW_GAIN_MULTIPLIER; + case TSL2591_CTRL_ALS_MED_GAIN: + return TSL2591_CTRL_ALS_MED_GAIN_MULTIPLIER; + case TSL2591_CTRL_ALS_HIGH_GAIN: + return TSL2591_CTRL_ALS_HIGH_GAIN_MULTIPLIER; + case TSL2591_CTRL_ALS_MAX_GAIN: + return TSL2591_CTRL_ALS_MAX_GAIN_MULTIPLIER; + default: + return -EINVAL; + } +} + +static int tsl2591_multiplier_to_gain(const u32 multiplier) +{ + switch (multiplier) { + case TSL2591_CTRL_ALS_LOW_GAIN_MULTIPLIER: + return TSL2591_CTRL_ALS_LOW_GAIN; + case TSL2591_CTRL_ALS_MED_GAIN_MULTIPLIER: + return TSL2591_CTRL_ALS_MED_GAIN; + case TSL2591_CTRL_ALS_HIGH_GAIN_MULTIPLIER: + return TSL2591_CTRL_ALS_HIGH_GAIN; + case TSL2591_CTRL_ALS_MAX_GAIN_MULTIPLIER: + return TSL2591_CTRL_ALS_MAX_GAIN; + default: + return -EINVAL; + } +} + +static int tsl2591_persist_cycle_to_lit(const u8 als_persist) +{ + switch (als_persist) { + case TSL2591_PRST_ALS_INT_CYCLE_ANY: + return 1; + case TSL2591_PRST_ALS_INT_CYCLE_2: + return 2; + case TSL2591_PRST_ALS_INT_CYCLE_3: + return 3; + case TSL2591_PRST_ALS_INT_CYCLE_5: + return 5; + case TSL2591_PRST_ALS_INT_CYCLE_10: + return 10; + case TSL2591_PRST_ALS_INT_CYCLE_15: + return 15; + case TSL2591_PRST_ALS_INT_CYCLE_20: + return 20; + case TSL2591_PRST_ALS_INT_CYCLE_25: + return 25; + case TSL2591_PRST_ALS_INT_CYCLE_30: + return 30; + case TSL2591_PRST_ALS_INT_CYCLE_35: + return 35; + case TSL2591_PRST_ALS_INT_CYCLE_40: + return 40; + case TSL2591_PRST_ALS_INT_CYCLE_45: + return 45; + case TSL2591_PRST_ALS_INT_CYCLE_50: + return 50; + case TSL2591_PRST_ALS_INT_CYCLE_55: + return 55; + case TSL2591_PRST_ALS_INT_CYCLE_60: + return 60; + default: + return -EINVAL; + } +} + +static int tsl2591_persist_lit_to_cycle(const u8 als_persist) +{ + switch (als_persist) { + case 1: + return TSL2591_PRST_ALS_INT_CYCLE_ANY; + case 2: + return TSL2591_PRST_ALS_INT_CYCLE_2; + case 3: + return TSL2591_PRST_ALS_INT_CYCLE_3; + case 5: + return TSL2591_PRST_ALS_INT_CYCLE_5; + case 10: + return TSL2591_PRST_ALS_INT_CYCLE_10; + case 15: + return TSL2591_PRST_ALS_INT_CYCLE_15; + case 20: + return TSL2591_PRST_ALS_INT_CYCLE_20; + case 25: + return TSL2591_PRST_ALS_INT_CYCLE_25; + case 30: + return TSL2591_PRST_ALS_INT_CYCLE_30; + case 35: + return TSL2591_PRST_ALS_INT_CYCLE_35; + case 40: + return TSL2591_PRST_ALS_INT_CYCLE_40; + case 45: + return TSL2591_PRST_ALS_INT_CYCLE_45; + case 50: + return TSL2591_PRST_ALS_INT_CYCLE_50; + case 55: + return TSL2591_PRST_ALS_INT_CYCLE_55; + case 60: + return TSL2591_PRST_ALS_INT_CYCLE_60; + default: + return -EINVAL; + } +} + +static int tsl2591_compatible_int_time(struct tsl2591_chip *chip, + const u32 als_integration_time) +{ + switch (als_integration_time) { + case TSL2591_CTRL_ALS_INTEGRATION_100MS: + case TSL2591_CTRL_ALS_INTEGRATION_200MS: + case TSL2591_CTRL_ALS_INTEGRATION_300MS: + case TSL2591_CTRL_ALS_INTEGRATION_400MS: + case TSL2591_CTRL_ALS_INTEGRATION_500MS: + case TSL2591_CTRL_ALS_INTEGRATION_600MS: + return 0; + default: + return -EINVAL; + } +} + +static int tsl2591_als_time_to_fval(const u32 als_integration_time) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(tsl2591_int_time_available); i++) { + if (als_integration_time == tsl2591_int_time_available[i]) + return TSL2591_SEC_TO_FVAL(als_integration_time); + } + + return -EINVAL; +} + +static int tsl2591_compatible_gain(struct tsl2591_chip *chip, const u8 als_gain) +{ + switch (als_gain) { + case TSL2591_CTRL_ALS_LOW_GAIN: + case TSL2591_CTRL_ALS_MED_GAIN: + case TSL2591_CTRL_ALS_HIGH_GAIN: + case TSL2591_CTRL_ALS_MAX_GAIN: + return 0; + default: + return -EINVAL; + } +} + +static int tsl2591_compatible_als_persist_cycle(struct tsl2591_chip *chip, + const u32 als_persist) +{ + switch (als_persist) { + case TSL2591_PRST_ALS_INT_CYCLE_ANY: + case TSL2591_PRST_ALS_INT_CYCLE_2: + case TSL2591_PRST_ALS_INT_CYCLE_3: + case TSL2591_PRST_ALS_INT_CYCLE_5: + case TSL2591_PRST_ALS_INT_CYCLE_10: + case TSL2591_PRST_ALS_INT_CYCLE_15: + case TSL2591_PRST_ALS_INT_CYCLE_20: + case TSL2591_PRST_ALS_INT_CYCLE_25: + case TSL2591_PRST_ALS_INT_CYCLE_30: + case TSL2591_PRST_ALS_INT_CYCLE_35: + case TSL2591_PRST_ALS_INT_CYCLE_40: + case TSL2591_PRST_ALS_INT_CYCLE_45: + case TSL2591_PRST_ALS_INT_CYCLE_50: + case TSL2591_PRST_ALS_INT_CYCLE_55: + case TSL2591_PRST_ALS_INT_CYCLE_60: + return 0; + default: + return -EINVAL; + } +} + +static int tsl2591_check_als_valid(struct i2c_client *client) +{ + int ret; + + ret = i2c_smbus_read_byte_data(client, TSL2591_CMD_NOP | TSL2591_STATUS); + if (ret < 0) { + dev_err(&client->dev, "Failed to read register\n"); + return -EINVAL; + } + + return FIELD_GET(TSL2591_STS_ALS_VALID_MASK, ret); +} + +static int tsl2591_wait_adc_complete(struct tsl2591_chip *chip) +{ + struct tsl2591_als_settings settings = chip->als_settings; + struct i2c_client *client = chip->client; + int delay; + int val; + int ret; + + delay = TSL2591_FVAL_TO_MSEC(settings.als_int_time); + if (!delay) + return -EINVAL; + + /* + * Sleep for ALS integration time to allow enough time or an ADC read + * cycle to complete. Check status after delay for ALS valid. + */ + msleep(delay); + + /* Check for status ALS valid flag for up to 100ms */ + ret = readx_poll_timeout(tsl2591_check_als_valid, client, + val, val == TSL2591_STS_VAL_HIGH_MASK, + TSL2591_DELAY_PERIOD_US, + TSL2591_DELAY_PERIOD_US * TSL2591_ALS_STS_VALID_COUNT); + if (ret) + dev_err(&client->dev, "Timed out waiting for valid ALS data\n"); + + return ret; +} + +/* + * tsl2591_read_channel_data - Reads raw channel data and calculates lux + * + * Formula for lux calculation; + * Derived from Adafruit's TSL2591 library + * Link: https://github.com/adafruit/Adafruit_TSL2591_Library + * Counts Per Lux (CPL) = (ATIME_ms * AGAIN) / LUX DF + * lux = ((C0DATA - C1DATA) * (1 - (C1DATA / C0DATA))) / CPL + * + * Scale values to get more representative value of lux i.e. + * lux = ((C0DATA - C1DATA) * (1000 - ((C1DATA * 1000) / C0DATA))) / CPL + * + * Channel 0 = IR + Visible + * Channel 1 = IR only + */ +static int tsl2591_read_channel_data(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + struct tsl2591_als_settings *settings = &chip->als_settings; + struct i2c_client *client = chip->client; + u8 als_data[TSL2591_NUM_DATA_REGISTERS]; + int counts_per_lux, int_time_fval, gain_multi, lux; + u16 als_ch0, als_ch1; + int ret; + + ret = tsl2591_wait_adc_complete(chip); + if (ret < 0) { + dev_err(&client->dev, "No data available. Err: %d\n", ret); + return ret; + } + + ret = i2c_smbus_read_i2c_block_data(client, + TSL2591_CMD_NOP | TSL2591_C0_DATAL, + sizeof(als_data), als_data); + if (ret < 0) { + dev_err(&client->dev, "Failed to read data bytes"); + return ret; + } + + als_ch0 = get_unaligned_le16(&als_data[0]); + als_ch1 = get_unaligned_le16(&als_data[2]); + + switch (chan->type) { + case IIO_INTENSITY: + if (chan->channel2 == IIO_MOD_LIGHT_BOTH) + *val = als_ch0; + else if (chan->channel2 == IIO_MOD_LIGHT_IR) + *val = als_ch1; + else + return -EINVAL; + break; + case IIO_LIGHT: + gain_multi = tsl2591_gain_to_multiplier(settings->als_gain); + if (gain_multi < 0) { + dev_err(&client->dev, "Invalid multiplier"); + return gain_multi; + } + + int_time_fval = TSL2591_FVAL_TO_MSEC(settings->als_int_time); + /* Calculate counts per lux value */ + counts_per_lux = (int_time_fval * gain_multi) / TSL2591_LUX_COEFFICIENT; + + dev_dbg(&client->dev, "Counts Per Lux: %d\n", counts_per_lux); + + /* Calculate lux value */ + lux = ((als_ch0 - als_ch1) * + (1000 - ((als_ch1 * 1000) / als_ch0))) / counts_per_lux; + + dev_dbg(&client->dev, "Raw lux calculation: %d\n", lux); + + /* Divide by 1000 to get real lux value before scaling */ + *val = lux / 1000; + + /* Get the decimal part of lux reading */ + *val2 = (lux - (*val * 1000)) * 1000; + + break; + default: + return -EINVAL; + } + + return 0; +} + +static int tsl2591_set_als_gain_int_time(struct tsl2591_chip *chip) +{ + struct tsl2591_als_settings als_settings = chip->als_settings; + struct i2c_client *client = chip->client; + int ret; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_CONTROL, + als_settings.als_int_time | als_settings.als_gain); + if (ret) + dev_err(&client->dev, "Failed to set als gain & int time\n"); + + return ret; +} + +static int tsl2591_set_als_lower_threshold(struct tsl2591_chip *chip, + u16 als_lower_threshold) +{ + struct tsl2591_als_settings als_settings = chip->als_settings; + struct i2c_client *client = chip->client; + u16 als_upper_threshold; + u8 als_lower_l; + u8 als_lower_h; + int ret; + + chip->als_settings.als_lower_thresh = als_lower_threshold; + + /* + * Lower threshold should not be greater or equal to upper. + * If this is the case, then assert upper threshold to new lower + * threshold + 1 to avoid ordering issues when setting thresholds. + */ + if (als_lower_threshold >= als_settings.als_upper_thresh) { + als_upper_threshold = als_lower_threshold + 1; + tsl2591_set_als_upper_threshold(chip, als_upper_threshold); + } + + als_lower_l = als_lower_threshold; + als_lower_h = als_lower_threshold >> 8; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_AILTL, + als_lower_l); + if (ret) { + dev_err(&client->dev, "Failed to set als lower threshold\n"); + return ret; + } + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_AILTH, + als_lower_h); + if (ret) { + dev_err(&client->dev, "Failed to set als lower threshold\n"); + return ret; + } + + return 0; +} + +static int tsl2591_set_als_upper_threshold(struct tsl2591_chip *chip, + u16 als_upper_threshold) +{ + struct tsl2591_als_settings als_settings = chip->als_settings; + struct i2c_client *client = chip->client; + u16 als_lower_threshold; + u8 als_upper_l; + u8 als_upper_h; + int ret; + + if (als_upper_threshold > TSL2591_ALS_MAX_VALUE) + return -EINVAL; + + chip->als_settings.als_upper_thresh = als_upper_threshold; + + /* + * Upper threshold should not be less than lower. If this + * is the case, then assert lower threshold to new upper + * threshold - 1 to avoid ordering issues when setting thresholds. + */ + if (als_upper_threshold < als_settings.als_lower_thresh) { + als_lower_threshold = als_upper_threshold - 1; + tsl2591_set_als_lower_threshold(chip, als_lower_threshold); + } + + als_upper_l = als_upper_threshold; + als_upper_h = als_upper_threshold >> 8; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_AIHTL, + als_upper_l); + if (ret) { + dev_err(&client->dev, "Failed to set als upper threshold\n"); + return ret; + } + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_AIHTH, + als_upper_h); + if (ret) { + dev_err(&client->dev, "Failed to set als upper threshold\n"); + return ret; + } + + return 0; +} + +static int tsl2591_set_als_persist_cycle(struct tsl2591_chip *chip, + u8 als_persist) +{ + struct i2c_client *client = chip->client; + int ret; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_PERSIST, + als_persist); + if (ret) + dev_err(&client->dev, "Failed to set als persist cycle\n"); + + chip->als_settings.als_persist = als_persist; + + return ret; +} + +static int tsl2591_set_power_state(struct tsl2591_chip *chip, u8 state) +{ + struct i2c_client *client = chip->client; + int ret; + + ret = i2c_smbus_write_byte_data(client, + TSL2591_CMD_NOP | TSL2591_ENABLE, + state); + if (ret) + dev_err(&client->dev, + "Failed to set the power state to %#04x\n", state); + + return ret; +} + +static ssize_t tsl2591_in_illuminance_period_available_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct tsl2591_chip *chip = iio_priv(indio_dev); + + return sysfs_emit(buf, "%s\n", + tsl2591_als_period_list[chip->als_settings.als_int_time]); +} + +static IIO_DEVICE_ATTR_RO(tsl2591_in_illuminance_period_available, 0); + +static struct attribute *tsl2591_event_attrs_ctrl[] = { + &iio_dev_attr_tsl2591_in_illuminance_period_available.dev_attr.attr, + NULL +}; + +static const struct attribute_group tsl2591_event_attribute_group = { + .attrs = tsl2591_event_attrs_ctrl, +}; + +static const struct iio_event_spec tsl2591_events[] = { + { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_RISING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_FALLING, + .mask_separate = BIT(IIO_EV_INFO_VALUE), + }, { + .type = IIO_EV_TYPE_THRESH, + .dir = IIO_EV_DIR_EITHER, + .mask_separate = BIT(IIO_EV_INFO_PERIOD) | + BIT(IIO_EV_INFO_ENABLE), + }, +}; + +static const struct iio_chan_spec tsl2591_channels[] = { + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_IR, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) + }, + { + .type = IIO_INTENSITY, + .modified = 1, + .channel2 = IIO_MOD_LIGHT_BOTH, + .event_spec = tsl2591_events, + .num_event_specs = ARRAY_SIZE(tsl2591_events), + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) + }, + { + .type = IIO_LIGHT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_CALIBSCALE) + }, +}; + +static int tsl2591_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + int ret; + + pm_runtime_get_sync(&client->dev); + + mutex_lock(&chip->als_mutex); + + switch (mask) { + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_INTENSITY) { + ret = -EINVAL; + goto err_unlock; + } + + ret = tsl2591_read_channel_data(indio_dev, chan, val, val2); + if (ret < 0) + goto err_unlock; + + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_PROCESSED: + if (chan->type != IIO_LIGHT) { + ret = -EINVAL; + goto err_unlock; + } + + ret = tsl2591_read_channel_data(indio_dev, chan, val, val2); + if (ret < 0) + break; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + case IIO_CHAN_INFO_INT_TIME: + if (chan->type != IIO_INTENSITY) { + ret = -EINVAL; + goto err_unlock; + } + + *val = TSL2591_FVAL_TO_SEC(chip->als_settings.als_int_time); + ret = IIO_VAL_INT; + break; + case IIO_CHAN_INFO_CALIBSCALE: + if (chan->type != IIO_INTENSITY) { + ret = -EINVAL; + goto err_unlock; + } + + *val = tsl2591_gain_to_multiplier(chip->als_settings.als_gain); + ret = IIO_VAL_INT; + break; + default: + ret = -EINVAL; + break; + } + +err_unlock: + mutex_unlock(&chip->als_mutex); + + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + + return ret; +} + +static int tsl2591_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct tsl2591_chip *chip = iio_priv(indio_dev); + int int_time; + int gain; + int ret; + + mutex_lock(&chip->als_mutex); + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + int_time = tsl2591_als_time_to_fval(val); + if (int_time < 0) { + ret = int_time; + goto err_unlock; + } + ret = tsl2591_compatible_int_time(chip, int_time); + if (ret < 0) + goto err_unlock; + + chip->als_settings.als_int_time = int_time; + break; + case IIO_CHAN_INFO_CALIBSCALE: + gain = tsl2591_multiplier_to_gain(val); + if (gain < 0) { + ret = gain; + goto err_unlock; + } + ret = tsl2591_compatible_gain(chip, gain); + if (ret < 0) + goto err_unlock; + + chip->als_settings.als_gain = gain; + break; + default: + ret = -EINVAL; + goto err_unlock; + } + + ret = tsl2591_set_als_gain_int_time(chip); + +err_unlock: + mutex_unlock(&chip->als_mutex); + return ret; +} + +static int tsl2591_read_available(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + const int **vals, int *type, int *length, + long mask) +{ + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + *length = ARRAY_SIZE(tsl2591_int_time_available); + *vals = tsl2591_int_time_available; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + + case IIO_CHAN_INFO_CALIBSCALE: + *length = ARRAY_SIZE(tsl2591_calibscale_available); + *vals = tsl2591_calibscale_available; + *type = IIO_VAL_INT; + return IIO_AVAIL_LIST; + default: + return -EINVAL; + } +} + +static int tsl2591_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 tsl2591_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + int als_persist, int_time, period; + int ret; + + mutex_lock(&chip->als_mutex); + + switch (info) { + case IIO_EV_INFO_VALUE: + switch (dir) { + case IIO_EV_DIR_RISING: + *val = chip->als_settings.als_upper_thresh; + break; + case IIO_EV_DIR_FALLING: + *val = chip->als_settings.als_lower_thresh; + break; + default: + ret = -EINVAL; + goto err_unlock; + } + ret = IIO_VAL_INT; + break; + case IIO_EV_INFO_PERIOD: + ret = i2c_smbus_read_byte_data(client, + TSL2591_CMD_NOP | TSL2591_PERSIST); + if (ret <= 0 || ret > TSL2591_PRST_ALS_INT_CYCLE_MAX) + goto err_unlock; + + als_persist = tsl2591_persist_cycle_to_lit(ret); + int_time = TSL2591_FVAL_TO_MSEC(chip->als_settings.als_int_time); + period = als_persist * (int_time * MSEC_PER_SEC); + + *val = period / USEC_PER_SEC; + *val2 = period % USEC_PER_SEC; + + ret = IIO_VAL_INT_PLUS_MICRO; + break; + default: + ret = -EINVAL; + break; + } + +err_unlock: + mutex_unlock(&chip->als_mutex); + return ret; +} + +static int tsl2591_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 tsl2591_chip *chip = iio_priv(indio_dev); + int period, int_time, als_persist; + int ret; + + if (val < 0 || val2 < 0) + return -EINVAL; + + mutex_lock(&chip->als_mutex); + + switch (info) { + case IIO_EV_INFO_VALUE: + if (val > TSL2591_ALS_MAX_VALUE) { + ret = -EINVAL; + goto err_unlock; + } + + switch (dir) { + case IIO_EV_DIR_RISING: + ret = tsl2591_set_als_upper_threshold(chip, val); + if (ret < 0) + goto err_unlock; + break; + case IIO_EV_DIR_FALLING: + ret = tsl2591_set_als_lower_threshold(chip, val); + if (ret < 0) + goto err_unlock; + break; + default: + ret = -EINVAL; + goto err_unlock; + } + break; + case IIO_EV_INFO_PERIOD: + int_time = TSL2591_FVAL_TO_MSEC(chip->als_settings.als_int_time); + + period = ((val * MSEC_PER_SEC) + + (val2 / MSEC_PER_SEC)) / int_time; + + als_persist = tsl2591_persist_lit_to_cycle(period); + if (als_persist < 0) { + ret = -EINVAL; + goto err_unlock; + } + + ret = tsl2591_compatible_als_persist_cycle(chip, als_persist); + if (ret < 0) + goto err_unlock; + + ret = tsl2591_set_als_persist_cycle(chip, als_persist); + if (ret < 0) + goto err_unlock; + break; + default: + ret = -EINVAL; + break; + } + +err_unlock: + mutex_unlock(&chip->als_mutex); + return ret; +} + +static int tsl2591_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 tsl2591_chip *chip = iio_priv(indio_dev); + + return chip->events_enabled; +} + +static int tsl2591_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 tsl2591_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + + if (state && !chip->events_enabled) { + chip->events_enabled = true; + pm_runtime_get_sync(&client->dev); + } else if (!state && chip->events_enabled) { + chip->events_enabled = false; + pm_runtime_mark_last_busy(&client->dev); + pm_runtime_put_autosuspend(&client->dev); + } + + return 0; +} + +static const struct iio_info tsl2591_info = { + .event_attrs = &tsl2591_event_attribute_group, + .read_raw = tsl2591_read_raw, + .write_raw = tsl2591_write_raw, + .read_avail = tsl2591_read_available, + .read_event_value = tsl2591_read_event_value, + .write_event_value = tsl2591_write_event_value, + .read_event_config = tsl2591_read_event_config, + .write_event_config = tsl2591_write_event_config, +}; + +static const struct iio_info tsl2591_info_no_irq = { + .read_raw = tsl2591_read_raw, + .write_raw = tsl2591_write_raw, + .read_avail = tsl2591_read_available, +}; + +static int __maybe_unused tsl2591_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2591_chip *chip = iio_priv(indio_dev); + int ret; + + mutex_lock(&chip->als_mutex); + ret = tsl2591_set_power_state(chip, TSL2591_PWR_OFF); + mutex_unlock(&chip->als_mutex); + + return ret; +} + +static int __maybe_unused tsl2591_resume(struct device *dev) +{ + int power_state = TSL2591_PWR_ON | TSL2591_ENABLE_ALS; + struct iio_dev *indio_dev = dev_get_drvdata(dev); + struct tsl2591_chip *chip = iio_priv(indio_dev); + int ret; + + if (chip->events_enabled) + power_state |= TSL2591_ENABLE_ALS_INT; + + mutex_lock(&chip->als_mutex); + ret = tsl2591_set_power_state(chip, power_state); + mutex_unlock(&chip->als_mutex); + + return ret; +} + +static const struct dev_pm_ops tsl2591_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume) + SET_RUNTIME_PM_OPS(tsl2591_suspend, tsl2591_resume, NULL) +}; + +static irqreturn_t tsl2591_event_handler(int irq, void *private) +{ + struct iio_dev *dev_info = private; + struct tsl2591_chip *chip = iio_priv(dev_info); + struct i2c_client *client = chip->client; + + if (!chip->events_enabled) + return IRQ_NONE; + + iio_push_event(dev_info, + IIO_UNMOD_EVENT_CODE(IIO_LIGHT, 0, + IIO_EV_TYPE_THRESH, + IIO_EV_DIR_EITHER), + iio_get_time_ns(dev_info)); + + /* Clear ALS irq */ + i2c_smbus_write_byte(client, TSL2591_CMD_SF_CALS_NPI); + + return IRQ_HANDLED; +} + +static int tsl2591_load_defaults(struct tsl2591_chip *chip) +{ + int ret; + + chip->als_settings.als_int_time = TSL2591_DEFAULT_ALS_INT_TIME; + chip->als_settings.als_gain = TSL2591_DEFAULT_ALS_GAIN; + chip->als_settings.als_lower_thresh = TSL2591_DEFAULT_ALS_LOWER_THRESH; + chip->als_settings.als_upper_thresh = TSL2591_DEFAULT_ALS_UPPER_THRESH; + + ret = tsl2591_set_als_gain_int_time(chip); + if (ret < 0) + return ret; + + ret = tsl2591_set_als_persist_cycle(chip, TSL2591_DEFAULT_ALS_PERSIST); + if (ret < 0) + return ret; + + ret = tsl2591_set_als_lower_threshold(chip, TSL2591_DEFAULT_ALS_LOWER_THRESH); + if (ret < 0) + return ret; + + ret = tsl2591_set_als_upper_threshold(chip, TSL2591_DEFAULT_ALS_UPPER_THRESH); + if (ret < 0) + return ret; + + return 0; +} + +static void tsl2591_chip_off(void *data) +{ + struct iio_dev *indio_dev = data; + struct tsl2591_chip *chip = iio_priv(indio_dev); + struct i2c_client *client = chip->client; + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + tsl2591_set_power_state(chip, TSL2591_PWR_OFF); +} + +static int tsl2591_probe(struct i2c_client *client) +{ + struct tsl2591_chip *chip; + struct iio_dev *indio_dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&client->dev, + "I2C smbus byte data functionality is not supported\n"); + return -EOPNOTSUPP; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip)); + if (!indio_dev) + return -ENOMEM; + + chip = iio_priv(indio_dev); + chip->client = client; + i2c_set_clientdata(client, indio_dev); + + if (client->irq) { + ret = devm_request_threaded_irq(&client->dev, client->irq, + NULL, tsl2591_event_handler, + IRQF_TRIGGER_FALLING | IRQF_ONESHOT, + "tsl2591_irq", indio_dev); + if (ret) { + dev_err_probe(&client->dev, ret, "IRQ request error\n"); + return -EINVAL; + } + indio_dev->info = &tsl2591_info; + } else { + indio_dev->info = &tsl2591_info_no_irq; + } + + mutex_init(&chip->als_mutex); + + ret = i2c_smbus_read_byte_data(client, + TSL2591_CMD_NOP | TSL2591_DEVICE_ID); + if (ret < 0) { + dev_err(&client->dev, + "Failed to read the device ID register\n"); + return ret; + } + ret = FIELD_GET(TSL2591_DEVICE_ID_MASK, ret); + if (ret != TSL2591_DEVICE_ID_VAL) { + dev_err(&client->dev, "Device ID: %#04x unknown\n", ret); + return -EINVAL; + } + + indio_dev->channels = tsl2591_channels; + indio_dev->num_channels = ARRAY_SIZE(tsl2591_channels); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->name = chip->client->name; + chip->events_enabled = false; + + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, + TSL2591_POWER_OFF_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + /* + * Add chip off to automatically managed path and disable runtime + * power management. This ensures that the chip power management + * is handled correctly on driver remove. tsl2591_chip_off() must be + * added to the managed path after pm runtime is enabled and before + * any error exit paths are met to ensure we're not left in a state + * of pm runtime not being disabled properly. + */ + ret = devm_add_action_or_reset(&client->dev, tsl2591_chip_off, + indio_dev); + if (ret < 0) + return -EINVAL; + + ret = tsl2591_load_defaults(chip); + if (ret < 0) { + dev_err(&client->dev, "Failed to load sensor defaults\n"); + return -EINVAL; + } + + ret = i2c_smbus_write_byte(client, TSL2591_CMD_SF_CALS_NPI); + if (ret < 0) { + dev_err(&client->dev, "Failed to clear als irq\n"); + return -EINVAL; + } + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id tsl2591_of_match[] = { + { .compatible = "amstaos,tsl2591"}, + {} +}; +MODULE_DEVICE_TABLE(of, tsl2591_of_match); + +static struct i2c_driver tsl2591_driver = { + .driver = { + .name = "tsl2591", + .pm = &tsl2591_pm_ops, + .of_match_table = tsl2591_of_match, + }, + .probe_new = tsl2591_probe +}; +module_i2c_driver(tsl2591_driver); + +MODULE_AUTHOR("Joe Sandom <joe.g.sandom@gmail.com>"); +MODULE_DESCRIPTION("TAOS tsl2591 ambient light sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c index 393f27b75c75..96e4a66ddf28 100644 --- a/drivers/iio/light/us5182d.c +++ b/drivers/iio/light/us5182d.c @@ -367,9 +367,7 @@ static int us5182d_set_power_state(struct us5182d_data *data, bool on) return 0; if (on) { - ret = pm_runtime_get_sync(&data->client->dev); - if (ret < 0) - pm_runtime_put_noidle(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); } else { pm_runtime_mark_last_busy(&data->client->dev); ret = pm_runtime_put_autosuspend(&data->client->dev); diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c index 2f7916f95689..e02e92bc2928 100644 --- a/drivers/iio/light/vcnl4000.c +++ b/drivers/iio/light/vcnl4000.c @@ -413,9 +413,7 @@ static int vcnl4000_set_pm_runtime_state(struct vcnl4000_data *data, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); @@ -910,7 +908,7 @@ static irqreturn_t vcnl4010_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct vcnl4000_data *data = iio_priv(indio_dev); const unsigned long *active_scan_mask = indio_dev->active_scan_mask; - u16 buffer[8] = {0}; /* 1x16-bit + ts */ + u16 buffer[8] __aligned(8) = {0}; /* 1x16-bit + naturally aligned ts */ bool data_read = false; unsigned long isr; int val = 0; @@ -998,7 +996,8 @@ static int vcnl4010_probe_trigger(struct iio_dev *indio_dev) struct iio_trigger *trigger; trigger = devm_iio_trigger_alloc(&client->dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!trigger) return -ENOMEM; diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c index ae87740d9cef..0db306ee910e 100644 --- a/drivers/iio/light/vcnl4035.c +++ b/drivers/iio/light/vcnl4035.c @@ -102,7 +102,8 @@ static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p) struct iio_poll_func *pf = p; struct iio_dev *indio_dev = pf->indio_dev; struct vcnl4035_data *data = iio_priv(indio_dev); - u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)]; + /* Ensure naturally aligned timestamp */ + u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)] __aligned(8); int ret; ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer); @@ -144,9 +145,7 @@ static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on) struct device *dev = &data->client->dev; if (on) { - ret = pm_runtime_get_sync(dev); - if (ret < 0) - pm_runtime_put_noidle(dev); + ret = pm_runtime_resume_and_get(dev); } else { pm_runtime_mark_last_busy(dev); ret = pm_runtime_put_autosuspend(dev); @@ -507,7 +506,7 @@ static int vcnl4035_probe_trigger(struct iio_dev *indio_dev) data->drdy_trigger0 = devm_iio_trigger_alloc( indio_dev->dev.parent, - "%s-dev%d", indio_dev->name, indio_dev->id); + "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); if (!data->drdy_trigger0) return -ENOMEM; diff --git a/drivers/iio/light/veml6030.c b/drivers/iio/light/veml6030.c index de85c9b30be1..3c937c55a10d 100644 --- a/drivers/iio/light/veml6030.c +++ b/drivers/iio/light/veml6030.c @@ -128,7 +128,7 @@ static ssize_t in_illuminance_period_available_show(struct device *dev, return -EINVAL; } - return snprintf(buf, PAGE_SIZE, "%s\n", period_values[x]); + return sysfs_emit(buf, "%s\n", period_values[x]); } static IIO_DEVICE_ATTR_RO(in_illuminance_period_available, 0); diff --git a/drivers/iio/magnetometer/ak8974.c b/drivers/iio/magnetometer/ak8974.c index 24b2f7b1fe44..e54feacfb980 100644 --- a/drivers/iio/magnetometer/ak8974.c +++ b/drivers/iio/magnetometer/ak8974.c @@ -833,8 +833,7 @@ static int ak8974_probe(struct i2c_client *i2c, ak8974->i2c = i2c; mutex_init(&ak8974->lock); - ret = iio_read_mount_matrix(&i2c->dev, "mount-matrix", - &ak8974->orientation); + ret = iio_read_mount_matrix(&i2c->dev, &ak8974->orientation); if (ret) return ret; diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c index d988b6ac3659..42b8a2680e3a 100644 --- a/drivers/iio/magnetometer/ak8975.c +++ b/drivers/iio/magnetometer/ak8975.c @@ -890,7 +890,7 @@ static int ak8975_probe(struct i2c_client *client, data->reset_gpiod = reset_gpiod; data->eoc_irq = 0; - err = iio_read_mount_matrix(&client->dev, "mount-matrix", &data->orientation); + err = iio_read_mount_matrix(&client->dev, &data->orientation); if (err) return err; diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c index 00f9766bad5c..f96f53175349 100644 --- a/drivers/iio/magnetometer/bmc150_magn.c +++ b/drivers/iio/magnetometer/bmc150_magn.c @@ -138,8 +138,11 @@ struct bmc150_magn_data { struct regmap *regmap; struct regulator_bulk_data regulators[2]; struct iio_mount_matrix orientation; - /* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */ - s32 buffer[6]; + /* Ensure timestamp is naturally aligned */ + struct { + s32 chans[3]; + s64 timestamp __aligned(8); + } scan; struct iio_trigger *dready_trig; bool dready_trigger_on; int max_odr; @@ -262,7 +265,7 @@ static int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on) int ret; if (on) { - ret = pm_runtime_get_sync(data->dev); + ret = pm_runtime_resume_and_get(data->dev); } else { pm_runtime_mark_last_busy(data->dev); ret = pm_runtime_put_autosuspend(data->dev); @@ -271,9 +274,6 @@ static int bmc150_magn_set_power_state(struct bmc150_magn_data *data, bool on) if (ret < 0) { dev_err(data->dev, "failed to change power state to %d\n", on); - if (on) - pm_runtime_put_noidle(data->dev); - return ret; } #endif @@ -675,11 +675,11 @@ static irqreturn_t bmc150_magn_trigger_handler(int irq, void *p) int ret; mutex_lock(&data->mutex); - ret = bmc150_magn_read_xyz(data, data->buffer); + ret = bmc150_magn_read_xyz(data, data->scan.chans); if (ret < 0) goto err; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, pf->timestamp); err: @@ -890,8 +890,7 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, if (ret) return dev_err_probe(dev, ret, "failed to get regulators\n"); - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; @@ -915,7 +914,7 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, data->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->dready_trig) { ret = -ENOMEM; dev_err(dev, "iio trigger alloc failed\n"); @@ -963,12 +962,14 @@ int bmc150_magn_probe(struct device *dev, struct regmap *regmap, ret = iio_device_register(indio_dev); if (ret < 0) { dev_err(dev, "unable to register iio device\n"); - goto err_buffer_cleanup; + goto err_disable_runtime_pm; } dev_dbg(dev, "Registered device %s\n", name); return 0; +err_disable_runtime_pm: + pm_runtime_disable(dev); err_buffer_cleanup: iio_triggered_buffer_cleanup(indio_dev); err_free_irq: @@ -992,7 +993,6 @@ int bmc150_magn_remove(struct device *dev) pm_runtime_disable(dev); pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); iio_triggered_buffer_cleanup(indio_dev); diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c index b78691523dd4..e85a3a8eea90 100644 --- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c +++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c @@ -6,13 +6,9 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/slab.h> -#include <linux/delay.h> +#include <linux/mod_devicetable.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include "../common/hid-sensors/hid-sensor-trigger.h" @@ -587,3 +583,4 @@ module_platform_driver(hid_magn_3d_platform_driver); MODULE_DESCRIPTION("HID Sensor Magnetometer 3D"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h index 3f6c0b662941..242f742f2643 100644 --- a/drivers/iio/magnetometer/hmc5843.h +++ b/drivers/iio/magnetometer/hmc5843.h @@ -33,7 +33,8 @@ enum hmc5843_ids { * @lock: update and read regmap data * @regmap: hardware access register maps * @variant: describe chip variants - * @buffer: 3x 16-bit channels + padding + 64-bit timestamp + * @scan: buffer to pack data for passing to + * iio_push_to_buffers_with_timestamp() */ struct hmc5843_data { struct device *dev; @@ -41,7 +42,10 @@ struct hmc5843_data { struct regmap *regmap; const struct hmc5843_chip_info *variant; struct iio_mount_matrix orientation; - __be16 buffer[8]; + struct { + __be16 chans[3]; + s64 timestamp __aligned(8); + } scan; }; int hmc5843_common_probe(struct device *dev, struct regmap *regmap, diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c index 780faea61d82..cf62057480cf 100644 --- a/drivers/iio/magnetometer/hmc5843_core.c +++ b/drivers/iio/magnetometer/hmc5843_core.c @@ -446,13 +446,13 @@ static irqreturn_t hmc5843_trigger_handler(int irq, void *p) } ret = regmap_bulk_read(data->regmap, HMC5843_DATA_OUT_MSB_REGS, - data->buffer, 3 * sizeof(__be16)); + data->scan.chans, sizeof(data->scan.chans)); mutex_unlock(&data->lock); if (ret < 0) goto done; - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); done: @@ -637,8 +637,7 @@ int hmc5843_common_probe(struct device *dev, struct regmap *regmap, data->variant = &hmc5843_chip_info_tbl[id]; mutex_init(&data->lock); - ret = iio_read_mount_matrix(dev, "mount-matrix", - &data->orientation); + ret = iio_read_mount_matrix(dev, &data->orientation); if (ret) return ret; diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c index dd811da9cb6d..13914273c999 100644 --- a/drivers/iio/magnetometer/rm3100-core.c +++ b/drivers/iio/magnetometer/rm3100-core.c @@ -78,7 +78,8 @@ struct rm3100_data { bool use_interrupt; int conversion_time; int scale; - u8 buffer[RM3100_SCAN_BYTES]; + /* Ensure naturally aligned timestamp */ + u8 buffer[RM3100_SCAN_BYTES] __aligned(8); struct iio_trigger *drdy_trig; /* @@ -575,7 +576,7 @@ int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq) data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->drdy_trig) return -ENOMEM; diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h index 7ba6a6ba5c58..fb6c906c4c0c 100644 --- a/drivers/iio/magnetometer/st_magn.h +++ b/drivers/iio/magnetometer/st_magn.h @@ -23,10 +23,6 @@ #define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn" #define IIS2MDC_MAGN_DEV_NAME "iis2mdc" -const struct st_sensor_settings *st_magn_get_settings(const char *name); -int st_magn_common_probe(struct iio_dev *indio_dev); -void st_magn_common_remove(struct iio_dev *indio_dev); - #ifdef CONFIG_IIO_BUFFER int st_magn_allocate_ring(struct iio_dev *indio_dev); void st_magn_deallocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c index 71faebd07feb..0048c3cd36ee 100644 --- a/drivers/iio/magnetometer/st_magn_core.c +++ b/drivers/iio/magnetometer/st_magn_core.c @@ -33,6 +33,7 @@ /* FULLSCALE */ #define ST_MAGN_FS_AVL_1300MG 1300 #define ST_MAGN_FS_AVL_1900MG 1900 +#define ST_MAGN_FS_AVL_2000MG 2000 #define ST_MAGN_FS_AVL_2500MG 2500 #define ST_MAGN_FS_AVL_4000MG 4000 #define ST_MAGN_FS_AVL_4700MG 4700 @@ -53,51 +54,95 @@ #define ST_MAGN_3_OUT_Y_L_ADDR 0x6a #define ST_MAGN_3_OUT_Z_L_ADDR 0x6c +/* Special L addresses for sensor 4 */ +#define ST_MAGN_4_OUT_X_L_ADDR 0x08 +#define ST_MAGN_4_OUT_Y_L_ADDR 0x0a +#define ST_MAGN_4_OUT_Z_L_ADDR 0x0c + +static const struct iio_mount_matrix * +st_magn_get_mount_matrix(const struct iio_dev *indio_dev, + const struct iio_chan_spec *chan) +{ + struct st_sensor_data *mdata = iio_priv(indio_dev); + + return &mdata->mount_matrix; +} + +static const struct iio_chan_spec_ext_info st_magn_mount_matrix_ext_info[] = { + IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, st_magn_get_mount_matrix), + { } +}; + static const struct iio_chan_spec st_magn_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_BE, 16, 16, - ST_MAGN_DEFAULT_OUT_X_H_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_MAGN_DEFAULT_OUT_X_H_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_BE, 16, 16, - ST_MAGN_DEFAULT_OUT_Y_H_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_MAGN_DEFAULT_OUT_Y_H_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_BE, 16, 16, - ST_MAGN_DEFAULT_OUT_Z_H_ADDR), + ST_MAGN_DEFAULT_OUT_Z_H_ADDR, + st_magn_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_magn_2_16bit_channels[] = { - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, - ST_MAGN_2_OUT_X_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_MAGN_2_OUT_X_L_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, - ST_MAGN_2_OUT_Y_L_ADDR), - ST_SENSORS_LSM_CHANNELS(IIO_MAGN, + ST_MAGN_2_OUT_Y_L_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, - ST_MAGN_2_OUT_Z_L_ADDR), + ST_MAGN_2_OUT_Z_L_ADDR, + st_magn_mount_matrix_ext_info), IIO_CHAN_SOFT_TIMESTAMP(3) }; static const struct iio_chan_spec st_magn_3_16bit_channels[] = { + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, + ST_MAGN_3_OUT_X_L_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, + ST_MAGN_3_OUT_Y_L_ADDR, + st_magn_mount_matrix_ext_info), + ST_SENSORS_LSM_CHANNELS_EXT(IIO_MAGN, + BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), + ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, + ST_MAGN_3_OUT_Z_L_ADDR, + st_magn_mount_matrix_ext_info), + IIO_CHAN_SOFT_TIMESTAMP(3) +}; + +static const struct iio_chan_spec st_magn_4_16bit_channels[] = { ST_SENSORS_LSM_CHANNELS(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_X, 1, IIO_MOD_X, 's', IIO_LE, 16, 16, - ST_MAGN_3_OUT_X_L_ADDR), + ST_MAGN_4_OUT_X_L_ADDR), ST_SENSORS_LSM_CHANNELS(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Y, 1, IIO_MOD_Y, 's', IIO_LE, 16, 16, - ST_MAGN_3_OUT_Y_L_ADDR), + ST_MAGN_4_OUT_Y_L_ADDR), ST_SENSORS_LSM_CHANNELS(IIO_MAGN, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE), ST_SENSORS_SCAN_Z, 1, IIO_MOD_Z, 's', IIO_LE, 16, 16, - ST_MAGN_3_OUT_Z_L_ADDR), + ST_MAGN_4_OUT_Z_L_ADDR), IIO_CHAN_SOFT_TIMESTAMP(3) }; @@ -381,6 +426,87 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = { .multi_read_bit = false, .bootime = 2, }, + { + .wai = 0x49, + .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS, + .sensors_supported = { + [0] = LSM9DS0_IMU_DEV_NAME, + }, + .ch = (struct iio_chan_spec *)st_magn_4_16bit_channels, + .odr = { + .addr = 0x24, + .mask = GENMASK(4, 2), + .odr_avl = { + { 3, 0x00, }, + { 6, 0x01, }, + { 12, 0x02, }, + { 25, 0x03, }, + { 50, 0x04, }, + { 100, 0x05, }, + }, + }, + .pw = { + .addr = 0x26, + .mask = GENMASK(1, 0), + .value_on = 0x00, + .value_off = 0x03, + }, + .fs = { + .addr = 0x25, + .mask = GENMASK(6, 5), + .fs_avl = { + [0] = { + .num = ST_MAGN_FS_AVL_2000MG, + .value = 0x00, + .gain = 73, + }, + [1] = { + .num = ST_MAGN_FS_AVL_4000MG, + .value = 0x01, + .gain = 146, + }, + [2] = { + .num = ST_MAGN_FS_AVL_8000MG, + .value = 0x02, + .gain = 292, + }, + [3] = { + .num = ST_MAGN_FS_AVL_12000MG, + .value = 0x03, + .gain = 438, + }, + }, + }, + .bdu = { + .addr = 0x20, + .mask = BIT(3), + }, + .drdy_irq = { + .int1 = { + .addr = 0x22, + .mask = BIT(1), + }, + .int2 = { + .addr = 0x23, + .mask = BIT(2), + }, + .stat_drdy = { + .addr = 0x07, + .mask = GENMASK(2, 0), + }, + }, + .sim = { + .addr = 0x21, + .value = BIT(0), + }, + .multi_read_bit = true, + .bootime = 2, + }, +}; + +/* Default magn DRDY is available on INT2 pin */ +static const struct st_sensors_platform_data default_magn_pdata = { + .drdy_int_pin = 2, }; static int st_magn_read_raw(struct iio_dev *indio_dev, @@ -490,33 +616,37 @@ EXPORT_SYMBOL(st_magn_get_settings); int st_magn_common_probe(struct iio_dev *indio_dev) { struct st_sensor_data *mdata = iio_priv(indio_dev); + struct st_sensors_platform_data *pdata = dev_get_platdata(mdata->dev); int err; indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &magn_info; - err = st_sensors_power_enable(indio_dev); - if (err) - return err; - err = st_sensors_verify_id(indio_dev); if (err < 0) - goto st_magn_power_off; + return err; mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS; indio_dev->channels = mdata->sensor_settings->ch; indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS; + err = iio_read_mount_matrix(mdata->dev, &mdata->mount_matrix); + if (err) + return err; + mdata->current_fullscale = &mdata->sensor_settings->fs.fs_avl[0]; mdata->odr = mdata->sensor_settings->odr.odr_avl[0].hz; - err = st_sensors_init_sensor(indio_dev, NULL); + if (!pdata) + pdata = (struct st_sensors_platform_data *)&default_magn_pdata; + + err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) - goto st_magn_power_off; + return err; err = st_magn_allocate_ring(indio_dev); if (err < 0) - goto st_magn_power_off; + return err; if (mdata->irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -539,9 +669,6 @@ st_magn_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_magn_probe_trigger_error: st_magn_deallocate_ring(indio_dev); -st_magn_power_off: - st_sensors_power_disable(indio_dev); - return err; } EXPORT_SYMBOL(st_magn_common_probe); @@ -550,8 +677,6 @@ void st_magn_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *mdata = iio_priv(indio_dev); - st_sensors_power_disable(indio_dev); - iio_device_unregister(indio_dev); if (mdata->irq > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c index 36f4e7b53b24..3e23c117de8e 100644 --- a/drivers/iio/magnetometer/st_magn_i2c.c +++ b/drivers/iio/magnetometer/st_magn_i2c.c @@ -82,16 +82,28 @@ static int st_magn_i2c_probe(struct i2c_client *client, if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_magn_common_probe(indio_dev); if (err < 0) - return err; + goto st_magn_power_off; return 0; + +st_magn_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_magn_i2c_remove(struct i2c_client *client) { struct iio_dev *indio_dev = i2c_get_clientdata(client); + + st_sensors_power_disable(indio_dev); + st_magn_common_remove(indio_dev); return 0; diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c index 0e2323dfc687..03c0a737aba6 100644 --- a/drivers/iio/magnetometer/st_magn_spi.c +++ b/drivers/iio/magnetometer/st_magn_spi.c @@ -76,16 +76,28 @@ static int st_magn_spi_probe(struct spi_device *spi) if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_magn_common_probe(indio_dev); if (err < 0) - return err; + goto st_magn_power_off; return 0; + +st_magn_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_magn_spi_remove(struct spi_device *spi) { struct iio_dev *indio_dev = spi_get_drvdata(spi); + + st_sensors_power_disable(indio_dev); + st_magn_common_remove(indio_dev); return 0; diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c index 2f2f8cb3c26c..9ff7b0e56cf6 100644 --- a/drivers/iio/magnetometer/yamaha-yas530.c +++ b/drivers/iio/magnetometer/yamaha-yas530.c @@ -831,7 +831,7 @@ static int yas5xx_probe(struct i2c_client *i2c, yas5xx->dev = dev; mutex_init(&yas5xx->lock); - ret = iio_read_mount_matrix(dev, "mount-matrix", &yas5xx->orientation); + ret = iio_read_mount_matrix(dev, &yas5xx->orientation); if (ret) return ret; diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c index 7af48d336285..c0079e2c8807 100644 --- a/drivers/iio/orientation/hid-sensor-incl-3d.c +++ b/drivers/iio/orientation/hid-sensor-incl-3d.c @@ -7,13 +7,10 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> +#include <linux/mod_devicetable.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include "../common/hid-sensors/hid-sensor-trigger.h" @@ -425,3 +422,4 @@ module_platform_driver(hid_incl_3d_platform_driver); MODULE_DESCRIPTION("HID Sensor Inclinometer 3D"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c index cf7f57a47681..a033699910e8 100644 --- a/drivers/iio/orientation/hid-sensor-rotation.c +++ b/drivers/iio/orientation/hid-sensor-rotation.c @@ -7,9 +7,7 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> -#include <linux/slab.h> +#include <linux/mod_devicetable.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> #include <linux/iio/sysfs.h> @@ -373,3 +371,4 @@ module_platform_driver(hid_dev_rot_platform_driver); MODULE_DESCRIPTION("HID Sensor Device Rotation"); MODULE_AUTHOR("Srinivas Pandruvada <srinivas.pandruvada@linux.intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/position/hid-sensor-custom-intel-hinge.c b/drivers/iio/position/hid-sensor-custom-intel-hinge.c index fd77e7ee87f3..07c30d217255 100644 --- a/drivers/iio/position/hid-sensor-custom-intel-hinge.c +++ b/drivers/iio/position/hid-sensor-custom-intel-hinge.c @@ -7,6 +7,8 @@ #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/platform_device.h> +#include <linux/module.h> +#include <linux/mod_devicetable.h> #include "../common/hid-sensors/hid-sensor-trigger.h" @@ -303,7 +305,6 @@ static int hid_hinge_probe(struct platform_device *pdev) return ret; } - indio_dev->dev.parent = &pdev->dev; indio_dev->info = &hinge_info; indio_dev->name = "hinge"; indio_dev->modes = INDIO_DIRECT_MODE; @@ -377,3 +378,4 @@ module_platform_driver(hid_hinge_platform_driver); MODULE_DESCRIPTION("HID Sensor INTEL Hinge"); MODULE_AUTHOR("Ye Xiang <xiang.ye@intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index 8a9c576616ee..ed30bdaa10ec 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -71,8 +71,8 @@ struct lmp91000_data { struct completion completion; u8 chan_select; - - u32 buffer[4]; /* 64-bit data + 64-bit timestamp */ + /* 64-bit data + 64-bit naturally aligned timestamp */ + u32 buffer[4] __aligned(8); }; static const struct iio_chan_spec lmp91000_channels[] = { @@ -323,7 +323,8 @@ static int lmp91000_probe(struct i2c_client *client, } data->trig = devm_iio_trigger_alloc(dev, "%s-mux%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!data->trig) { dev_err(dev, "cannot allocate iio trigger.\n"); return -ENOMEM; diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c index c416d261e3e3..10c52b8df2ba 100644 --- a/drivers/iio/pressure/hid-sensor-press.c +++ b/drivers/iio/pressure/hid-sensor-press.c @@ -6,13 +6,10 @@ #include <linux/device.h> #include <linux/platform_device.h> #include <linux/module.h> -#include <linux/interrupt.h> -#include <linux/irq.h> +#include <linux/mod_devicetable.h> #include <linux/slab.h> -#include <linux/delay.h> #include <linux/hid-sensor-hub.h> #include <linux/iio/iio.h> -#include <linux/iio/sysfs.h> #include <linux/iio/buffer.h> #include "../common/hid-sensors/hid-sensor-trigger.h" @@ -357,3 +354,4 @@ module_platform_driver(hid_press_platform_driver); MODULE_DESCRIPTION("HID Sensor Pressure"); MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>"); MODULE_LICENSE("GPL"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/pressure/icp10100.c b/drivers/iio/pressure/icp10100.c index 48759fc4bf18..af4621eaa6b5 100644 --- a/drivers/iio/pressure/icp10100.c +++ b/drivers/iio/pressure/icp10100.c @@ -250,7 +250,9 @@ static int icp10100_get_measures(struct icp10100_state *st, __be16 measures[3]; int ret; - pm_runtime_get_sync(&st->client->dev); + ret = pm_runtime_resume_and_get(&st->client->dev); + if (ret < 0) + return ret; mutex_lock(&st->lock); cmd = &icp10100_cmd_measure[st->mode]; @@ -525,7 +527,6 @@ static void icp10100_pm_disable(void *data) { struct device *dev = data; - pm_runtime_put_sync_suspend(dev); pm_runtime_disable(dev); } diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h index 5c746ff6087e..9417b3bd7513 100644 --- a/drivers/iio/pressure/st_pressure.h +++ b/drivers/iio/pressure/st_pressure.h @@ -41,10 +41,6 @@ static __maybe_unused const struct st_sensors_platform_data default_press_pdata .drdy_int_pin = 1, }; -const struct st_sensor_settings *st_press_get_settings(const char *name); -int st_press_common_probe(struct iio_dev *indio_dev); -void st_press_common_remove(struct iio_dev *indio_dev); - #ifdef CONFIG_IIO_BUFFER int st_press_allocate_ring(struct iio_dev *indio_dev); void st_press_deallocate_ring(struct iio_dev *indio_dev); diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c index 789a2928504a..7912b5a68395 100644 --- a/drivers/iio/pressure/st_pressure_core.c +++ b/drivers/iio/pressure/st_pressure_core.c @@ -689,13 +689,9 @@ int st_press_common_probe(struct iio_dev *indio_dev) indio_dev->modes = INDIO_DIRECT_MODE; indio_dev->info = &press_info; - err = st_sensors_power_enable(indio_dev); - if (err) - return err; - err = st_sensors_verify_id(indio_dev); if (err < 0) - goto st_press_power_off; + return err; /* * Skip timestamping channel while declaring available channels to @@ -718,11 +714,11 @@ int st_press_common_probe(struct iio_dev *indio_dev) err = st_sensors_init_sensor(indio_dev, pdata); if (err < 0) - goto st_press_power_off; + return err; err = st_press_allocate_ring(indio_dev); if (err < 0) - goto st_press_power_off; + return err; if (press_data->irq > 0) { err = st_sensors_allocate_trigger(indio_dev, @@ -745,9 +741,6 @@ st_press_device_register_error: st_sensors_deallocate_trigger(indio_dev); st_press_probe_trigger_error: st_press_deallocate_ring(indio_dev); -st_press_power_off: - st_sensors_power_disable(indio_dev); - return err; } EXPORT_SYMBOL(st_press_common_probe); @@ -756,8 +749,6 @@ void st_press_common_remove(struct iio_dev *indio_dev) { struct st_sensor_data *press_data = iio_priv(indio_dev); - st_sensors_power_disable(indio_dev); - iio_device_unregister(indio_dev); if (press_data->irq > 0) st_sensors_deallocate_trigger(indio_dev); diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c index 09c6903f99b8..f0a5af314ceb 100644 --- a/drivers/iio/pressure/st_pressure_i2c.c +++ b/drivers/iio/pressure/st_pressure_i2c.c @@ -98,16 +98,29 @@ static int st_press_i2c_probe(struct i2c_client *client, if (ret < 0) return ret; + ret = st_sensors_power_enable(indio_dev); + if (ret) + return ret; + ret = st_press_common_probe(indio_dev); if (ret < 0) - return ret; + goto st_press_power_off; return 0; + +st_press_power_off: + st_sensors_power_disable(indio_dev); + + return ret; } static int st_press_i2c_remove(struct i2c_client *client) { - st_press_common_remove(i2c_get_clientdata(client)); + struct iio_dev *indio_dev = i2c_get_clientdata(client); + + st_sensors_power_disable(indio_dev); + + st_press_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c index b5ee3ec2764f..b48cf7d01cd7 100644 --- a/drivers/iio/pressure/st_pressure_spi.c +++ b/drivers/iio/pressure/st_pressure_spi.c @@ -82,16 +82,29 @@ static int st_press_spi_probe(struct spi_device *spi) if (err < 0) return err; + err = st_sensors_power_enable(indio_dev); + if (err) + return err; + err = st_press_common_probe(indio_dev); if (err < 0) - return err; + goto st_press_power_off; return 0; + +st_press_power_off: + st_sensors_power_disable(indio_dev); + + return err; } static int st_press_spi_remove(struct spi_device *spi) { - st_press_common_remove(spi_get_drvdata(spi)); + struct iio_dev *indio_dev = spi_get_drvdata(spi); + + st_sensors_power_disable(indio_dev); + + st_press_common_remove(indio_dev); return 0; } diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c index a93411216aee..89295c90f801 100644 --- a/drivers/iio/pressure/zpa2326.c +++ b/drivers/iio/pressure/zpa2326.c @@ -1408,7 +1408,8 @@ static int zpa2326_init_managed_trigger(struct device *parent, return 0; trigger = devm_iio_trigger_alloc(parent, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!trigger) return -ENOMEM; diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index edc4a35ae66d..3797a8f54276 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -59,7 +59,11 @@ struct as3935_state { unsigned long noise_tripped; u32 tune_cap; u32 nflwdth_reg; - u8 buffer[16]; /* 8-bit data + 56-bit padding + 64-bit timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u8 chan; + s64 timestamp __aligned(8); + } scan; u8 buf[2] ____cacheline_aligned; }; @@ -225,8 +229,8 @@ static irqreturn_t as3935_trigger_handler(int irq, void *private) if (ret) goto err_read; - st->buffer[0] = val & AS3935_DATA_MASK; - iio_push_to_buffers_with_timestamp(indio_dev, &st->buffer, + st->scan.chan = val & AS3935_DATA_MASK; + iio_push_to_buffers_with_timestamp(indio_dev, &st->scan, iio_get_time_ns(indio_dev)); err_read: iio_trigger_notify_done(indio_dev->trig); @@ -404,7 +408,8 @@ static int as3935_probe(struct spi_device *spi) indio_dev->info = &as3935_info; trig = devm_iio_trigger_alloc(dev, "%s-dev%d", - indio_dev->name, indio_dev->id); + indio_dev->name, + iio_device_id(indio_dev)); if (!trig) return -ENOMEM; diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c index 90e76451c972..5b6ea783795d 100644 --- a/drivers/iio/proximity/isl29501.c +++ b/drivers/iio/proximity/isl29501.c @@ -938,7 +938,7 @@ static irqreturn_t isl29501_trigger_handler(int irq, void *p) struct iio_dev *indio_dev = pf->indio_dev; struct isl29501_private *isl29501 = iio_priv(indio_dev); const unsigned long *active_mask = indio_dev->active_scan_mask; - u32 buffer[4] = {}; /* 1x16-bit + ts */ + u32 buffer[4] __aligned(8) = {}; /* 1x16-bit + naturally aligned ts */ if (test_bit(ISL29501_DISTANCE_SCAN_INDEX, active_mask)) isl29501_register_read(isl29501, REG_DISTANCE, buffer); diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index cc206bfa09c7..27026c060ab9 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -44,7 +44,11 @@ struct lidar_data { int (*xfer)(struct lidar_data *data, u8 reg, u8 *val, int len); int i2c_enabled; - u16 buffer[8]; /* 2 byte distance + 8 byte timestamp */ + /* Ensure timestamp is naturally aligned */ + struct { + u16 chan; + s64 timestamp __aligned(8); + } scan; }; static const struct iio_chan_spec lidar_channels[] = { @@ -154,7 +158,9 @@ static int lidar_get_measurement(struct lidar_data *data, u16 *reg) int tries = 10; int ret; - pm_runtime_get_sync(&client->dev); + ret = pm_runtime_resume_and_get(&client->dev); + if (ret < 0) + return ret; /* start sample */ ret = lidar_write_control(data, LIDAR_REG_CONTROL_ACQUIRE); @@ -230,9 +236,9 @@ static irqreturn_t lidar_trigger_handler(int irq, void *private) struct lidar_data *data = iio_priv(indio_dev); int ret; - ret = lidar_get_measurement(data, data->buffer); + ret = lidar_get_measurement(data, &data->scan.chan); if (!ret) { - iio_push_to_buffers_with_timestamp(indio_dev, data->buffer, + iio_push_to_buffers_with_timestamp(indio_dev, &data->scan, iio_get_time_ns(indio_dev)); } else if (ret != -EINVAL) { dev_err(&data->client->dev, "cannot read LIDAR measurement"); diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c index 420c37c72de4..fe88b2bb60bc 100644 --- a/drivers/iio/proximity/srf04.c +++ b/drivers/iio/proximity/srf04.c @@ -100,9 +100,11 @@ static int srf04_read(struct srf04_data *data) u64 dt_ns; u32 time_ns, distance_mm; - if (data->gpiod_power) - pm_runtime_get_sync(data->dev); - + if (data->gpiod_power) { + ret = pm_runtime_resume_and_get(data->dev); + if (ret < 0) + return ret; + } /* * just one read-echo-cycle can take place at a time * ==> lock against concurrent reading calls diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c index 70beac5c9c1d..9b0886760f76 100644 --- a/drivers/iio/proximity/srf08.c +++ b/drivers/iio/proximity/srf08.c @@ -63,11 +63,11 @@ struct srf08_data { int range_mm; struct mutex lock; - /* - * triggered buffer - * 1x16-bit channel + 3x16 padding + 4x16 timestamp - */ - s16 buffer[8]; + /* Ensure timestamp is naturally aligned */ + struct { + s16 chan; + s64 timestamp __aligned(8); + } scan; /* Sensor-Type */ enum srf08_sensor_type sensor_type; @@ -190,9 +190,9 @@ static irqreturn_t srf08_trigger_handler(int irq, void *p) mutex_lock(&data->lock); - data->buffer[0] = sensor_data; + data->scan.chan = sensor_data; iio_push_to_buffers_with_timestamp(indio_dev, - data->buffer, pf->timestamp); + &data->scan, pf->timestamp); mutex_unlock(&data->lock); err: diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c index 327ebb7ddbb9..175f3b7c61d7 100644 --- a/drivers/iio/proximity/sx9310.c +++ b/drivers/iio/proximity/sx9310.c @@ -1473,7 +1473,7 @@ static int sx9310_probe(struct i2c_client *client) data->trig = devm_iio_trigger_alloc(dev, "%s-dev%d", indio_dev->name, - indio_dev->id); + iio_device_id(indio_dev)); if (!data->trig) return -ENOMEM; diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c index a87f4a8e4327..3e4ddb2e8c2b 100644 --- a/drivers/iio/proximity/sx9500.c +++ b/drivers/iio/proximity/sx9500.c @@ -946,7 +946,7 @@ static int sx9500_probe(struct i2c_client *client, return ret; data->trig = devm_iio_trigger_alloc(&client->dev, - "%s-dev%d", indio_dev->name, indio_dev->id); + "%s-dev%d", indio_dev->name, iio_device_id(indio_dev)); if (!data->trig) return -ENOMEM; diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index 4df60082c1fa..f20ae3c963cb 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -96,6 +96,16 @@ config TMP007 This driver can also be built as a module. If so, the module will be called tmp007. +config TMP117 + tristate "TMP117 Digital temperature sensor with integrated NV memory" + depends on I2C + help + If you say yes here you get support for the Texas Instruments + TMP117 Digital temperature sensor with integrated NV memory. + + This driver can also be built as a module. If so, the module will + be called tmp117. + config TSYS01 tristate "Measurement Specialties TSYS01 temperature sensor using I2C bus connection" depends on I2C diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 90c113115422..e3392c4b29b4 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -12,5 +12,6 @@ obj-$(CONFIG_MLX90614) += mlx90614.o obj-$(CONFIG_MLX90632) += mlx90632.o obj-$(CONFIG_TMP006) += tmp006.o obj-$(CONFIG_TMP007) += tmp007.o +obj-$(CONFIG_TMP117) += tmp117.o obj-$(CONFIG_TSYS01) += tsys01.o obj-$(CONFIG_TSYS02D) += tsys02d.o diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c index dc534ed784c3..d40f235af1d4 100644 --- a/drivers/iio/temperature/hid-sensor-temperature.c +++ b/drivers/iio/temperature/hid-sensor-temperature.c @@ -8,6 +8,7 @@ #include <linux/iio/buffer.h> #include <linux/iio/iio.h> #include <linux/module.h> +#include <linux/mod_devicetable.h> #include <linux/platform_device.h> #include "../common/hid-sensors/hid-sensor-trigger.h" @@ -291,3 +292,4 @@ module_platform_driver(hid_temperature_platform_driver); MODULE_DESCRIPTION("HID Environmental temperature sensor"); MODULE_AUTHOR("Song Hongyan <hongyan.song@intel.com>"); MODULE_LICENSE("GPL v2"); +MODULE_IMPORT_NS(IIO_HID); diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c index ef0fec94d269..afcb10ea7c44 100644 --- a/drivers/iio/temperature/mlx90614.c +++ b/drivers/iio/temperature/mlx90614.c @@ -176,11 +176,14 @@ static inline s32 mlx90614_iir_search(const struct i2c_client *client, static int mlx90614_power_get(struct mlx90614_data *data, bool startup) { unsigned long now; + int ret; if (!data->wakeup_gpio) return 0; - pm_runtime_get_sync(&data->client->dev); + ret = pm_runtime_resume_and_get(&data->client->dev); + if (ret < 0) + return ret; if (startup) { now = jiffies; @@ -267,7 +270,10 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev, *val = MLX90614_CONST_SCALE; return IIO_VAL_INT; case IIO_CHAN_INFO_CALIBEMISSIVITY: /* 1/65535 / LSB */ - mlx90614_power_get(data, false); + ret = mlx90614_power_get(data, false); + if (ret < 0) + return ret; + mutex_lock(&data->lock); ret = i2c_smbus_read_word_data(data->client, MLX90614_EMISSIVITY); @@ -287,7 +293,10 @@ static int mlx90614_read_raw(struct iio_dev *indio_dev, return IIO_VAL_INT_PLUS_NANO; case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY: /* IIR setting with FIR = 1024 */ - mlx90614_power_get(data, false); + ret = mlx90614_power_get(data, false); + if (ret < 0) + return ret; + mutex_lock(&data->lock); ret = i2c_smbus_read_word_data(data->client, MLX90614_CONFIG); mutex_unlock(&data->lock); @@ -319,7 +328,10 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev, val = val * MLX90614_CONST_RAW_EMISSIVITY_MAX + val2 / MLX90614_CONST_EMISSIVITY_RESOLUTION; - mlx90614_power_get(data, false); + ret = mlx90614_power_get(data, false); + if (ret < 0) + return ret; + mutex_lock(&data->lock); ret = mlx90614_write_word(data->client, MLX90614_EMISSIVITY, val); @@ -331,7 +343,10 @@ static int mlx90614_write_raw(struct iio_dev *indio_dev, if (val < 0 || val2 < 0) return -EINVAL; - mlx90614_power_get(data, false); + ret = mlx90614_power_get(data, false); + if (ret < 0) + return ret; + mutex_lock(&data->lock); ret = mlx90614_iir_search(data->client, val * 100 + val2 / 10000); diff --git a/drivers/iio/temperature/tmp117.c b/drivers/iio/temperature/tmp117.c new file mode 100644 index 000000000000..f9b8f2b570f6 --- /dev/null +++ b/drivers/iio/temperature/tmp117.c @@ -0,0 +1,185 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Digital temperature sensor with integrated Non-volatile memory + * Copyright (c) 2021 Puranjay Mohan <puranjay12@gmail.com> + * + * Driver for the Texas Instruments TMP117 Temperature Sensor + * (7-bit I2C slave address (0x48 - 0x4B), changeable via ADD pins) + * + * Note: This driver assumes that the sensor has been calibrated beforehand. + */ + +#include <linux/err.h> +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/bitops.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/limits.h> + +#include <linux/iio/iio.h> + +#define TMP117_REG_TEMP 0x0 +#define TMP117_REG_CFGR 0x1 +#define TMP117_REG_HIGH_LIM 0x2 +#define TMP117_REG_LOW_LIM 0x3 +#define TMP117_REG_EEPROM_UL 0x4 +#define TMP117_REG_EEPROM1 0x5 +#define TMP117_REG_EEPROM2 0x6 +#define TMP117_REG_TEMP_OFFSET 0x7 +#define TMP117_REG_EEPROM3 0x8 +#define TMP117_REG_DEVICE_ID 0xF + +#define TMP117_RESOLUTION_10UC 78125 +#define TMP117_DEVICE_ID 0x0117 +#define MICRODEGREE_PER_10MILLIDEGREE 10000 + +struct tmp117_data { + struct i2c_client *client; + s16 calibbias; +}; + +static int tmp117_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct tmp117_data *data = iio_priv(indio_dev); + s32 ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: + ret = i2c_smbus_read_word_swapped(data->client, + TMP117_REG_TEMP); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 15); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_CALIBBIAS: + ret = i2c_smbus_read_word_swapped(data->client, + TMP117_REG_TEMP_OFFSET); + if (ret < 0) + return ret; + *val = sign_extend32(ret, 15); + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + /* + * Conversion from 10s of uC to mC + * as IIO reports temperature in mC + */ + *val = TMP117_RESOLUTION_10UC / MICRODEGREE_PER_10MILLIDEGREE; + *val2 = (TMP117_RESOLUTION_10UC % + MICRODEGREE_PER_10MILLIDEGREE) * 100; + + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int tmp117_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct tmp117_data *data = iio_priv(indio_dev); + s16 off; + + switch (mask) { + case IIO_CHAN_INFO_CALIBBIAS: + off = clamp_t(int, val, S16_MIN, S16_MAX); + if (off == data->calibbias) + return 0; + data->calibbias = off; + return i2c_smbus_write_word_swapped(data->client, + TMP117_REG_TEMP_OFFSET, off); + + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec tmp117_channels[] = { + { + .type = IIO_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_CALIBBIAS) | BIT(IIO_CHAN_INFO_SCALE), + }, +}; + +static const struct iio_info tmp117_info = { + .read_raw = tmp117_read_raw, + .write_raw = tmp117_write_raw, +}; + +static int tmp117_identify(struct i2c_client *client) +{ + int dev_id; + + dev_id = i2c_smbus_read_word_swapped(client, TMP117_REG_DEVICE_ID); + if (dev_id < 0) + return dev_id; + if (dev_id != TMP117_DEVICE_ID) { + dev_err(&client->dev, "TMP117 not found\n"); + return -ENODEV; + } + return 0; +} + +static int tmp117_probe(struct i2c_client *client) +{ + struct tmp117_data *data; + struct iio_dev *indio_dev; + int ret; + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA)) + return -EOPNOTSUPP; + + ret = tmp117_identify(client); + if (ret < 0) + return ret; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + data->client = client; + data->calibbias = 0; + + indio_dev->name = "tmp117"; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &tmp117_info; + + indio_dev->channels = tmp117_channels; + indio_dev->num_channels = ARRAY_SIZE(tmp117_channels); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct of_device_id tmp117_of_match[] = { + { .compatible = "ti,tmp117", }, + { } +}; +MODULE_DEVICE_TABLE(of, tmp117_of_match); + +static const struct i2c_device_id tmp117_id[] = { + { "tmp117", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, tmp117_id); + +static struct i2c_driver tmp117_driver = { + .driver = { + .name = "tmp117", + .of_match_table = tmp117_of_match, + }, + .probe_new = tmp117_probe, + .id_table = tmp117_id, +}; +module_i2c_driver(tmp117_driver); + +MODULE_AUTHOR("Puranjay Mohan <puranjay12@gmail.com>"); +MODULE_DESCRIPTION("TI TMP117 Temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index 3aa9e8bba005..33083877cd19 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -296,7 +296,7 @@ static ssize_t stm32_tt_show_master_mode(struct device *dev, else cr2 = (cr2 & TIM_CR2_MMS) >> TIM_CR2_MMS_SHIFT; - return snprintf(buf, PAGE_SIZE, "%s\n", master_mode_table[cr2]); + return sysfs_emit(buf, "%s\n", master_mode_table[cr2]); } static ssize_t stm32_tt_store_master_mode(struct device *dev, |