aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/iio
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2022-10-08 08:56:37 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2022-10-08 08:56:37 -0700
commita09476668e3016ea4a7b0a7ebd02f44e0546c12c (patch)
tree0a2e435d40b1785bc3df0c455fbf858adc28d36c /drivers/iio
parentMerge tag 'driver-core-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core (diff)
parentw1: Split memcpy() of struct cn_msg flexible array (diff)
downloadlinux-dev-a09476668e3016ea4a7b0a7ebd02f44e0546c12c.tar.xz
linux-dev-a09476668e3016ea4a7b0a7ebd02f44e0546c12c.zip
Merge tag 'char-misc-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc and other driver updates from Greg KH: "Here is the large set of char/misc and other small driver subsystem changes for 6.1-rc1. Loads of different things in here: - IIO driver updates, additions, and changes. Probably the largest part of the diffstat - habanalabs driver update with support for new hardware and features, the second largest part of the diff. - fpga subsystem driver updates and additions - mhi subsystem updates - Coresight driver updates - gnss subsystem updates - extcon driver updates - icc subsystem updates - fsi subsystem updates - nvmem subsystem and driver updates - misc driver updates - speakup driver additions for new features - lots of tiny driver updates and cleanups All of these have been in the linux-next tree for a while with no reported issues" * tag 'char-misc-6.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (411 commits) w1: Split memcpy() of struct cn_msg flexible array spmi: pmic-arb: increase SPMI transaction timeout delay spmi: pmic-arb: block access for invalid PMIC arbiter v5 SPMI writes spmi: pmic-arb: correct duplicate APID to PPID mapping logic spmi: pmic-arb: add support to dispatch interrupt based on IRQ status spmi: pmic-arb: check apid against limits before calling irq handler spmi: pmic-arb: do not ack and clear peripheral interrupts in cleanup_irq spmi: pmic-arb: handle spurious interrupt spmi: pmic-arb: add a print in cleanup_irq drivers: spmi: Directly use ida_alloc()/free() MAINTAINERS: add TI ECAP driver info counter: ti-ecap-capture: capture driver support for ECAP Documentation: ABI: sysfs-bus-counter: add frequency & num_overflows items dt-bindings: counter: add ti,am62-ecap-capture.yaml counter: Introduce the COUNTER_COMP_ARRAY component type counter: Consolidate Counter extension sysfs attribute creation counter: Introduce the Count capture component counter: 104-quad-8: Add Signal polarity component counter: Introduce the Signal polarity component counter: interrupt-cnt: Implement watch_validate callback ...
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/accel/Kconfig13
-rw-r--r--drivers/iio/accel/Makefile2
-rw-r--r--drivers/iio/accel/adxl313.h35
-rw-r--r--drivers/iio/accel/adxl313_core.c202
-rw-r--r--drivers/iio/accel/adxl313_i2c.c74
-rw-r--r--drivers/iio/accel/adxl313_spi.c63
-rw-r--r--drivers/iio/accel/adxl345_core.c7
-rw-r--r--drivers/iio/accel/bma400.h14
-rw-r--r--drivers/iio/accel/bma400_core.c346
-rw-r--r--drivers/iio/accel/bmi088-accel-core.c15
-rw-r--r--drivers/iio/accel/bmi088-accel-spi.c2
-rw-r--r--drivers/iio/accel/kxcjk-1013.c2
-rw-r--r--drivers/iio/accel/msa311.c1321
-rw-r--r--drivers/iio/adc/Kconfig47
-rw-r--r--drivers/iio/adc/Makefile3
-rw-r--r--drivers/iio/adc/ab8500-gpadc.c27
-rw-r--r--drivers/iio/adc/ad7124.c15
-rw-r--r--drivers/iio/adc/ad7768-1.c17
-rw-r--r--drivers/iio/adc/ad7923.c11
-rw-r--r--drivers/iio/adc/ad9467.c17
-rw-r--r--drivers/iio/adc/at91-sama5d2_adc.c714
-rw-r--r--drivers/iio/adc/imx8qxp-adc.c8
-rw-r--r--drivers/iio/adc/ingenic-adc.c23
-rw-r--r--drivers/iio/adc/lpc18xx_adc.c18
-rw-r--r--drivers/iio/adc/ltc2496.c9
-rw-r--r--drivers/iio/adc/ltc2497-core.c12
-rw-r--r--drivers/iio/adc/ltc2497.c77
-rw-r--r--drivers/iio/adc/ltc2497.h6
-rw-r--r--drivers/iio/adc/max11205.c183
-rw-r--r--drivers/iio/adc/max1363.c6
-rw-r--r--drivers/iio/adc/mcp3911.c257
-rw-r--r--drivers/iio/adc/mt6360-adc.c2
-rw-r--r--drivers/iio/adc/qcom-pm8xxx-xoadc.c58
-rw-r--r--drivers/iio/adc/qcom-spmi-adc5.c73
-rw-r--r--drivers/iio/adc/qcom-spmi-vadc.c44
-rw-r--r--drivers/iio/adc/rtq6056.c661
-rw-r--r--drivers/iio/adc/stm32-adc-core.c59
-rw-r--r--drivers/iio/adc/stm32-adc-core.h31
-rw-r--r--drivers/iio/adc/stm32-adc.c128
-rw-r--r--drivers/iio/adc/ti-ads131e08.c19
-rw-r--r--drivers/iio/adc/ti-tsc2046.c69
-rw-r--r--drivers/iio/adc/xilinx-ams.c15
-rw-r--r--drivers/iio/adc/xilinx-xadc-core.c18
-rw-r--r--drivers/iio/addac/Kconfig16
-rw-r--r--drivers/iio/addac/Makefile1
-rw-r--r--drivers/iio/addac/stx104.c (renamed from drivers/iio/adc/stx104.c)0
-rw-r--r--drivers/iio/cdc/Kconfig10
-rw-r--r--drivers/iio/cdc/Makefile1
-rw-r--r--drivers/iio/cdc/ad7746.c820
-rw-r--r--drivers/iio/common/scmi_sensors/scmi_iio.c8
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c2
-rw-r--r--drivers/iio/dac/ad5593r.c50
-rw-r--r--drivers/iio/frequency/adf4371.c17
-rw-r--r--drivers/iio/frequency/admv1014.c3
-rw-r--r--drivers/iio/frequency/adrf6780.c16
-rw-r--r--drivers/iio/imu/Kconfig1
-rw-r--r--drivers/iio/imu/Makefile1
-rw-r--r--drivers/iio/imu/adis16475.c15
-rw-r--r--drivers/iio/imu/bno055/Kconfig25
-rw-r--r--drivers/iio/imu/bno055/Makefile10
-rw-r--r--drivers/iio/imu/bno055/bno055.c1685
-rw-r--r--drivers/iio/imu/bno055/bno055.h13
-rw-r--r--drivers/iio/imu/bno055/bno055_i2c.c57
-rw-r--r--drivers/iio/imu/bno055/bno055_ser_core.c560
-rw-r--r--drivers/iio/imu/bno055/bno055_ser_trace.c14
-rw-r--r--drivers/iio/imu/bno055/bno055_ser_trace.h104
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c4
-rw-r--r--drivers/iio/imu/st_lsm6dsx/Kconfig2
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h2
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c2
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c7
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c5
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c5
-rw-r--r--drivers/iio/industrialio-buffer.c5
-rw-r--r--drivers/iio/industrialio-core.c29
-rw-r--r--drivers/iio/industrialio-event.c14
-rw-r--r--drivers/iio/industrialio-trigger.c1
-rw-r--r--drivers/iio/inkern.c272
-rw-r--r--drivers/iio/light/Kconfig11
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/ltrf216a.c550
-rw-r--r--drivers/iio/light/st_uvis25_core.c9
-rw-r--r--drivers/iio/light/st_uvis25_i2c.c2
-rw-r--r--drivers/iio/light/st_uvis25_spi.c2
-rw-r--r--drivers/iio/magnetometer/Kconfig4
-rw-r--r--drivers/iio/magnetometer/hmc5843.h13
-rw-r--r--drivers/iio/magnetometer/hmc5843_core.c8
-rw-r--r--drivers/iio/magnetometer/hmc5843_i2c.c2
-rw-r--r--drivers/iio/magnetometer/hmc5843_spi.c14
-rw-r--r--drivers/iio/magnetometer/yamaha-yas530.c855
-rw-r--r--drivers/iio/pressure/Kconfig6
-rw-r--r--drivers/iio/pressure/bmp280-core.c974
-rw-r--r--drivers/iio/pressure/bmp280-i2c.c15
-rw-r--r--drivers/iio/pressure/bmp280-regmap.c55
-rw-r--r--drivers/iio/pressure/bmp280-spi.c5
-rw-r--r--drivers/iio/pressure/bmp280.h164
-rw-r--r--drivers/iio/pressure/dlhl60d.c5
-rw-r--r--drivers/iio/pressure/dps310.c262
-rw-r--r--drivers/iio/pressure/icp10100.c10
-rw-r--r--drivers/iio/pressure/st_pressure.h2
-rw-r--r--drivers/iio/pressure/st_pressure_core.c70
-rw-r--r--drivers/iio/pressure/st_pressure_i2c.c5
-rw-r--r--drivers/iio/pressure/st_pressure_spi.c5
-rw-r--r--drivers/iio/proximity/srf04.c10
-rw-r--r--drivers/iio/proximity/sx9310.c8
-rw-r--r--drivers/iio/proximity/sx9324.c8
-rw-r--r--drivers/iio/proximity/sx9360.c8
-rw-r--r--drivers/iio/temperature/mlx90614.c41
-rw-r--r--drivers/iio/temperature/mlx90632.c61
-rw-r--r--drivers/iio/test/iio-test-rescale.c4
110 files changed, 10350 insertions, 1339 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 35798712f811..ffac66db7ac9 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -541,6 +541,19 @@ config MMA9553
To compile this driver as a module, choose M here: the module
will be called mma9553.
+config MSA311
+ tristate "MEMSensing Digital 3-Axis Accelerometer Driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP_I2C
+ help
+ Say yes here to build support for the MEMSensing MSA311
+ accelerometer driver.
+
+ To compile this driver as a module, choose M here: the module will be
+ called msa311.
+
config MXC4005
tristate "Memsic MXC4005XC 3-Axis Accelerometer Driver"
depends on I2C
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 4d8792668838..5e45b5fa5ab5 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -58,6 +58,8 @@ obj-$(CONFIG_MMA9551_CORE) += mma9551_core.o
obj-$(CONFIG_MMA9551) += mma9551.o
obj-$(CONFIG_MMA9553) += mma9553.o
+obj-$(CONFIG_MSA311) += msa311.o
+
obj-$(CONFIG_MXC4005) += mxc4005.o
obj-$(CONFIG_MXC6255) += mxc6255.o
diff --git a/drivers/iio/accel/adxl313.h b/drivers/iio/accel/adxl313.h
index 4415f2fc07e1..72f624af4686 100644
--- a/drivers/iio/accel/adxl313.h
+++ b/drivers/iio/accel/adxl313.h
@@ -8,6 +8,8 @@
#ifndef _ADXL313_H_
#define _ADXL313_H_
+#include <linux/iio/iio.h>
+
/* ADXL313 register definitions */
#define ADXL313_REG_DEVID0 0x00
#define ADXL313_REG_DEVID1 0x01
@@ -26,6 +28,7 @@
#define ADXL313_REG_FIFO_STATUS 0x39
#define ADXL313_DEVID0 0xAD
+#define ADXL313_DEVID0_ADXL312_314 0xE5
#define ADXL313_DEVID1 0x1D
#define ADXL313_PARTID 0xCB
#define ADXL313_SOFT_RESET 0x52
@@ -37,18 +40,46 @@
#define ADXL313_MEASUREMENT_MODE BIT(3)
#define ADXL313_RANGE_MSK GENMASK(1, 0)
-#define ADXL313_RANGE_4G 3
+#define ADXL313_RANGE_MAX 3
#define ADXL313_FULL_RES BIT(3)
#define ADXL313_SPI_3WIRE BIT(6)
#define ADXL313_I2C_DISABLE BIT(6)
+extern const struct regmap_access_table adxl312_readable_regs_table;
extern const struct regmap_access_table adxl313_readable_regs_table;
+extern const struct regmap_access_table adxl314_readable_regs_table;
+extern const struct regmap_access_table adxl312_writable_regs_table;
extern const struct regmap_access_table adxl313_writable_regs_table;
+extern const struct regmap_access_table adxl314_writable_regs_table;
+
+enum adxl313_device_type {
+ ADXL312,
+ ADXL313,
+ ADXL314,
+};
+
+struct adxl313_data {
+ struct regmap *regmap;
+ const struct adxl313_chip_info *chip_info;
+ struct mutex lock; /* lock to protect transf_buf */
+ __le16 transf_buf __aligned(IIO_DMA_MINALIGN);
+};
+
+struct adxl313_chip_info {
+ const char *name;
+ enum adxl313_device_type type;
+ int scale_factor;
+ bool variable_range;
+ bool soft_reset;
+ int (*check_id)(struct device *dev, struct adxl313_data *data);
+};
+
+extern const struct adxl313_chip_info adxl31x_chip_info[];
int adxl313_core_probe(struct device *dev,
struct regmap *regmap,
- const char *name,
+ const struct adxl313_chip_info *chip_info,
int (*setup)(struct device *, struct regmap *));
#endif /* _ADXL313_H_ */
diff --git a/drivers/iio/accel/adxl313_core.c b/drivers/iio/accel/adxl313_core.c
index afeef779e1d0..4de0a41bd679 100644
--- a/drivers/iio/accel/adxl313_core.c
+++ b/drivers/iio/accel/adxl313_core.c
@@ -8,12 +8,18 @@
*/
#include <linux/bitfield.h>
-#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/regmap.h>
#include "adxl313.h"
+static const struct regmap_range adxl312_readable_reg_range[] = {
+ regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_DEVID0),
+ regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
+ regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
+ regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
+};
+
static const struct regmap_range adxl313_readable_reg_range[] = {
regmap_reg_range(ADXL313_REG_DEVID0, ADXL313_REG_XID),
regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
@@ -22,12 +28,109 @@ static const struct regmap_range adxl313_readable_reg_range[] = {
regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_FIFO_STATUS),
};
+const struct regmap_access_table adxl312_readable_regs_table = {
+ .yes_ranges = adxl312_readable_reg_range,
+ .n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
+};
+EXPORT_SYMBOL_NS_GPL(adxl312_readable_regs_table, IIO_ADXL313);
+
const struct regmap_access_table adxl313_readable_regs_table = {
.yes_ranges = adxl313_readable_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl313_readable_reg_range),
};
EXPORT_SYMBOL_NS_GPL(adxl313_readable_regs_table, IIO_ADXL313);
+const struct regmap_access_table adxl314_readable_regs_table = {
+ .yes_ranges = adxl312_readable_reg_range,
+ .n_yes_ranges = ARRAY_SIZE(adxl312_readable_reg_range),
+};
+EXPORT_SYMBOL_NS_GPL(adxl314_readable_regs_table, IIO_ADXL313);
+
+static int adxl312_check_id(struct device *dev,
+ struct adxl313_data *data)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, &regval);
+ if (ret)
+ return ret;
+
+ if (regval != ADXL313_DEVID0_ADXL312_314)
+ dev_warn(dev, "Invalid manufacturer ID: %#02x\n", regval);
+
+ return 0;
+}
+
+static int adxl313_check_id(struct device *dev,
+ struct adxl313_data *data)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, &regval);
+ if (ret)
+ return ret;
+
+ if (regval != ADXL313_DEVID0)
+ dev_warn(dev, "Invalid manufacturer ID: 0x%02x\n", regval);
+
+ /* Check DEVID1 and PARTID */
+ if (regval == ADXL313_DEVID0) {
+ ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);
+ if (ret)
+ return ret;
+
+ if (regval != ADXL313_DEVID1)
+ dev_warn(dev, "Invalid mems ID: 0x%02x\n", regval);
+
+ ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);
+ if (ret)
+ return ret;
+
+ if (regval != ADXL313_PARTID)
+ dev_warn(dev, "Invalid device ID: 0x%02x\n", regval);
+ }
+
+ return 0;
+}
+
+const struct adxl313_chip_info adxl31x_chip_info[] = {
+ [ADXL312] = {
+ .name = "adxl312",
+ .type = ADXL312,
+ .scale_factor = 28425072,
+ .variable_range = true,
+ .soft_reset = false,
+ .check_id = &adxl312_check_id,
+ },
+ [ADXL313] = {
+ .name = "adxl313",
+ .type = ADXL313,
+ .scale_factor = 9576806,
+ .variable_range = true,
+ .soft_reset = true,
+ .check_id = &adxl313_check_id,
+ },
+ [ADXL314] = {
+ .name = "adxl314",
+ .type = ADXL314,
+ .scale_factor = 478858719,
+ .variable_range = false,
+ .soft_reset = false,
+ .check_id = &adxl312_check_id,
+ },
+};
+EXPORT_SYMBOL_NS_GPL(adxl31x_chip_info, IIO_ADXL313);
+
+static const struct regmap_range adxl312_writable_reg_range[] = {
+ regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
+ regmap_reg_range(ADXL313_REG_THRESH_ACT, ADXL313_REG_ACT_INACT_CTL),
+ regmap_reg_range(ADXL313_REG_BW_RATE, ADXL313_REG_INT_MAP),
+ regmap_reg_range(ADXL313_REG_DATA_FORMAT, ADXL313_REG_DATA_FORMAT),
+ regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
+};
+
static const struct regmap_range adxl313_writable_reg_range[] = {
regmap_reg_range(ADXL313_REG_SOFT_RESET, ADXL313_REG_SOFT_RESET),
regmap_reg_range(ADXL313_REG_OFS_AXIS(0), ADXL313_REG_OFS_AXIS(2)),
@@ -37,17 +140,23 @@ static const struct regmap_range adxl313_writable_reg_range[] = {
regmap_reg_range(ADXL313_REG_FIFO_CTL, ADXL313_REG_FIFO_CTL),
};
+const struct regmap_access_table adxl312_writable_regs_table = {
+ .yes_ranges = adxl312_writable_reg_range,
+ .n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
+};
+EXPORT_SYMBOL_NS_GPL(adxl312_writable_regs_table, IIO_ADXL313);
+
const struct regmap_access_table adxl313_writable_regs_table = {
.yes_ranges = adxl313_writable_reg_range,
.n_yes_ranges = ARRAY_SIZE(adxl313_writable_reg_range),
};
EXPORT_SYMBOL_NS_GPL(adxl313_writable_regs_table, IIO_ADXL313);
-struct adxl313_data {
- struct regmap *regmap;
- struct mutex lock; /* lock to protect transf_buf */
- __le16 transf_buf __aligned(IIO_DMA_MINALIGN);
+const struct regmap_access_table adxl314_writable_regs_table = {
+ .yes_ranges = adxl312_writable_reg_range,
+ .n_yes_ranges = ARRAY_SIZE(adxl312_writable_reg_range),
};
+EXPORT_SYMBOL_NS_GPL(adxl314_writable_regs_table, IIO_ADXL313);
static const int adxl313_odr_freqs[][2] = {
[0] = { 6, 250000 },
@@ -156,12 +265,10 @@ static int adxl313_read_raw(struct iio_dev *indio_dev,
*val = sign_extend32(ret, chan->scan_type.realbits - 1);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- /*
- * Scale for any g range is given in datasheet as
- * 1024 LSB/g = 0.0009765625 * 9.80665 = 0.009576806640625 m/s^2
- */
*val = 0;
- *val2 = 9576806;
+
+ *val2 = data->chip_info->scale_factor;
+
return IIO_VAL_INT_PLUS_NANO;
case IIO_CHAN_INFO_CALIBBIAS:
ret = regmap_read(data->regmap,
@@ -170,7 +277,7 @@ static int adxl313_read_raw(struct iio_dev *indio_dev,
return ret;
/*
- * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
+ * 8-bit resolution at minimum range, that is 4x accel data scale
* factor at full resolution
*/
*val = sign_extend32(regval, 7) * 4;
@@ -198,7 +305,7 @@ static int adxl313_write_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
/*
- * 8-bit resolution at +/- 0.5g, that is 4x accel data scale
+ * 8-bit resolution at minimum range, that is 4x accel data scale
* factor at full resolution
*/
if (clamp_val(val, -128 * 4, 127 * 4) != val)
@@ -223,14 +330,18 @@ static const struct iio_info adxl313_info = {
static int adxl313_setup(struct device *dev, struct adxl313_data *data,
int (*setup)(struct device *, struct regmap *))
{
- unsigned int regval;
int ret;
- /* Ensures the device is in a consistent state after start up */
- ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
- ADXL313_SOFT_RESET);
- if (ret)
- return ret;
+ /*
+ * If sw reset available, ensures the device is in a consistent
+ * state after start up
+ */
+ if (data->chip_info->soft_reset) {
+ ret = regmap_write(data->regmap, ADXL313_REG_SOFT_RESET,
+ ADXL313_SOFT_RESET);
+ if (ret)
+ return ret;
+ }
if (setup) {
ret = setup(dev, data->regmap);
@@ -238,46 +349,25 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
return ret;
}
- ret = regmap_read(data->regmap, ADXL313_REG_DEVID0, &regval);
+ ret = data->chip_info->check_id(dev, data);
if (ret)
return ret;
- if (regval != ADXL313_DEVID0) {
- dev_err(dev, "Invalid manufacturer ID: 0x%02x\n", regval);
- return -ENODEV;
- }
-
- ret = regmap_read(data->regmap, ADXL313_REG_DEVID1, &regval);
- if (ret)
- return ret;
-
- if (regval != ADXL313_DEVID1) {
- dev_err(dev, "Invalid mems ID: 0x%02x\n", regval);
- return -ENODEV;
- }
-
- ret = regmap_read(data->regmap, ADXL313_REG_PARTID, &regval);
- if (ret)
- return ret;
+ /* Sets the range to maximum, full resolution, if applicable */
+ if (data->chip_info->variable_range) {
+ ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
+ ADXL313_RANGE_MSK,
+ FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_MAX));
+ if (ret)
+ return ret;
- if (regval != ADXL313_PARTID) {
- dev_err(dev, "Invalid device ID: 0x%02x\n", regval);
- return -ENODEV;
+ /* Enables full resolution */
+ ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
+ ADXL313_FULL_RES, ADXL313_FULL_RES);
+ if (ret)
+ return ret;
}
- /* Sets the range to +/- 4g */
- ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
- ADXL313_RANGE_MSK,
- FIELD_PREP(ADXL313_RANGE_MSK, ADXL313_RANGE_4G));
- if (ret)
- return ret;
-
- /* Enables full resolution */
- ret = regmap_update_bits(data->regmap, ADXL313_REG_DATA_FORMAT,
- ADXL313_FULL_RES, ADXL313_FULL_RES);
- if (ret)
- return ret;
-
/* Enables measurement mode */
return regmap_update_bits(data->regmap, ADXL313_REG_POWER_CTL,
ADXL313_POWER_CTL_MSK,
@@ -288,7 +378,7 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
* adxl313_core_probe() - probe and setup for adxl313 accelerometer
* @dev: Driver model representation of the device
* @regmap: Register map of the device
- * @name: Device name buffer reference
+ * @chip_info: Structure containing device specific data
* @setup: Setup routine to be executed right before the standard device
* setup, can also be set to NULL if not required
*
@@ -296,7 +386,7 @@ static int adxl313_setup(struct device *dev, struct adxl313_data *data,
*/
int adxl313_core_probe(struct device *dev,
struct regmap *regmap,
- const char *name,
+ const struct adxl313_chip_info *chip_info,
int (*setup)(struct device *, struct regmap *))
{
struct adxl313_data *data;
@@ -309,9 +399,11 @@ int adxl313_core_probe(struct device *dev,
data = iio_priv(indio_dev);
data->regmap = regmap;
+ data->chip_info = chip_info;
+
mutex_init(&data->lock);
- indio_dev->name = name;
+ indio_dev->name = chip_info->name;
indio_dev->info = &adxl313_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = adxl313_channels;
diff --git a/drivers/iio/accel/adxl313_i2c.c b/drivers/iio/accel/adxl313_i2c.c
index c329765dbf60..99cc7fc29488 100644
--- a/drivers/iio/accel/adxl313_i2c.c
+++ b/drivers/iio/accel/adxl313_i2c.c
@@ -14,42 +14,72 @@
#include "adxl313.h"
-static const struct regmap_config adxl313_i2c_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .rd_table = &adxl313_readable_regs_table,
- .wr_table = &adxl313_writable_regs_table,
- .max_register = 0x39,
+static const struct regmap_config adxl31x_i2c_regmap_config[] = {
+ [ADXL312] = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &adxl312_readable_regs_table,
+ .wr_table = &adxl312_writable_regs_table,
+ .max_register = 0x39,
+ },
+ [ADXL313] = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &adxl313_readable_regs_table,
+ .wr_table = &adxl313_writable_regs_table,
+ .max_register = 0x39,
+ },
+ [ADXL314] = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &adxl314_readable_regs_table,
+ .wr_table = &adxl314_writable_regs_table,
+ .max_register = 0x39,
+ },
};
-static int adxl313_i2c_probe(struct i2c_client *client)
-{
- struct regmap *regmap;
-
- regmap = devm_regmap_init_i2c(client, &adxl313_i2c_regmap_config);
- if (IS_ERR(regmap)) {
- dev_err(&client->dev, "Error initializing i2c regmap: %ld\n",
- PTR_ERR(regmap));
- return PTR_ERR(regmap);
- }
-
- return adxl313_core_probe(&client->dev, regmap, client->name, NULL);
-}
-
static const struct i2c_device_id adxl313_i2c_id[] = {
- { "adxl313" },
+ { .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
+ { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
+ { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
{ }
};
MODULE_DEVICE_TABLE(i2c, adxl313_i2c_id);
static const struct of_device_id adxl313_of_match[] = {
- { .compatible = "adi,adxl313" },
+ { .compatible = "adi,adxl312", .data = &adxl31x_chip_info[ADXL312] },
+ { .compatible = "adi,adxl313", .data = &adxl31x_chip_info[ADXL313] },
+ { .compatible = "adi,adxl314", .data = &adxl31x_chip_info[ADXL314] },
{ }
};
MODULE_DEVICE_TABLE(of, adxl313_of_match);
+static int adxl313_i2c_probe(struct i2c_client *client)
+{
+ const struct adxl313_chip_info *chip_data;
+ struct regmap *regmap;
+
+ /*
+ * Retrieves device specific data as a pointer to a
+ * adxl313_chip_info structure
+ */
+ chip_data = device_get_match_data(&client->dev);
+ if (!chip_data)
+ chip_data = (const struct adxl313_chip_info *)i2c_match_id(adxl313_i2c_id, client)->driver_data;
+
+ regmap = devm_regmap_init_i2c(client,
+ &adxl31x_i2c_regmap_config[chip_data->type]);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Error initializing i2c regmap: %ld\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ return adxl313_core_probe(&client->dev, regmap, chip_data, NULL);
+}
+
static struct i2c_driver adxl313_i2c_driver = {
.driver = {
.name = "adxl313_i2c",
diff --git a/drivers/iio/accel/adxl313_spi.c b/drivers/iio/accel/adxl313_spi.c
index a3c6d553462d..b7cc15678a2b 100644
--- a/drivers/iio/accel/adxl313_spi.c
+++ b/drivers/iio/accel/adxl313_spi.c
@@ -11,17 +11,38 @@
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
+#include <linux/property.h>
#include "adxl313.h"
-static const struct regmap_config adxl313_spi_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
- .rd_table = &adxl313_readable_regs_table,
- .wr_table = &adxl313_writable_regs_table,
- .max_register = 0x39,
- /* Setting bits 7 and 6 enables multiple-byte read */
- .read_flag_mask = BIT(7) | BIT(6),
+static const struct regmap_config adxl31x_spi_regmap_config[] = {
+ [ADXL312] = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &adxl312_readable_regs_table,
+ .wr_table = &adxl312_writable_regs_table,
+ .max_register = 0x39,
+ /* Setting bits 7 and 6 enables multiple-byte read */
+ .read_flag_mask = BIT(7) | BIT(6),
+ },
+ [ADXL313] = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &adxl313_readable_regs_table,
+ .wr_table = &adxl313_writable_regs_table,
+ .max_register = 0x39,
+ /* Setting bits 7 and 6 enables multiple-byte read */
+ .read_flag_mask = BIT(7) | BIT(6),
+ },
+ [ADXL314] = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .rd_table = &adxl314_readable_regs_table,
+ .wr_table = &adxl314_writable_regs_table,
+ .max_register = 0x39,
+ /* Setting bits 7 and 6 enables multiple-byte read */
+ .read_flag_mask = BIT(7) | BIT(6),
+ },
};
static int adxl313_spi_setup(struct device *dev, struct regmap *regmap)
@@ -42,7 +63,7 @@ static int adxl313_spi_setup(struct device *dev, struct regmap *regmap)
static int adxl313_spi_probe(struct spi_device *spi)
{
- const struct spi_device_id *id = spi_get_device_id(spi);
+ const struct adxl313_chip_info *chip_data;
struct regmap *regmap;
int ret;
@@ -51,26 +72,40 @@ static int adxl313_spi_probe(struct spi_device *spi)
if (ret)
return ret;
- regmap = devm_regmap_init_spi(spi, &adxl313_spi_regmap_config);
+ /*
+ * Retrieves device specific data as a pointer to a
+ * adxl313_chip_info structure
+ */
+ chip_data = device_get_match_data(&spi->dev);
+ if (!chip_data)
+ chip_data = (const struct adxl313_chip_info *)spi_get_device_id(spi)->driver_data;
+
+ regmap = devm_regmap_init_spi(spi,
+ &adxl31x_spi_regmap_config[chip_data->type]);
+
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
PTR_ERR(regmap));
return PTR_ERR(regmap);
}
- return adxl313_core_probe(&spi->dev, regmap, id->name,
- &adxl313_spi_setup);
+ return adxl313_core_probe(&spi->dev, regmap,
+ chip_data, &adxl313_spi_setup);
}
static const struct spi_device_id adxl313_spi_id[] = {
- { "adxl313" },
+ { .name = "adxl312", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL312] },
+ { .name = "adxl313", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL313] },
+ { .name = "adxl314", .driver_data = (kernel_ulong_t)&adxl31x_chip_info[ADXL314] },
{ }
};
MODULE_DEVICE_TABLE(spi, adxl313_spi_id);
static const struct of_device_id adxl313_of_match[] = {
- { .compatible = "adi,adxl313" },
+ { .compatible = "adi,adxl312", .data = &adxl31x_chip_info[ADXL312] },
+ { .compatible = "adi,adxl313", .data = &adxl31x_chip_info[ADXL313] },
+ { .compatible = "adi,adxl314", .data = &adxl31x_chip_info[ADXL314] },
{ }
};
diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
index 370bfec1275a..1919e0089c11 100644
--- a/drivers/iio/accel/adxl345_core.c
+++ b/drivers/iio/accel/adxl345_core.c
@@ -10,6 +10,7 @@
#include <linux/module.h>
#include <linux/property.h>
#include <linux/regmap.h>
+#include <linux/units.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -32,7 +33,6 @@
#define ADXL345_BW_RATE GENMASK(3, 0)
#define ADXL345_BASE_RATE_NANO_HZ 97656250LL
-#define NHZ_PER_HZ 1000000000LL
#define ADXL345_POWER_CTL_MEASURE BIT(3)
#define ADXL345_POWER_CTL_STANDBY 0x00
@@ -139,7 +139,7 @@ static int adxl345_read_raw(struct iio_dev *indio_dev,
samp_freq_nhz = ADXL345_BASE_RATE_NANO_HZ <<
(regval & ADXL345_BW_RATE);
- *val = div_s64_rem(samp_freq_nhz, NHZ_PER_HZ, val2);
+ *val = div_s64_rem(samp_freq_nhz, NANOHZ_PER_HZ, val2);
return IIO_VAL_INT_PLUS_NANO;
}
@@ -164,7 +164,8 @@ static int adxl345_write_raw(struct iio_dev *indio_dev,
ADXL345_REG_OFS_AXIS(chan->address),
val / 4);
case IIO_CHAN_INFO_SAMP_FREQ:
- n = div_s64(val * NHZ_PER_HZ + val2, ADXL345_BASE_RATE_NANO_HZ);
+ n = div_s64(val * NANOHZ_PER_HZ + val2,
+ ADXL345_BASE_RATE_NANO_HZ);
return regmap_update_bits(data->regmap, ADXL345_REG_BW_RATE,
ADXL345_BW_RATE,
diff --git a/drivers/iio/accel/bma400.h b/drivers/iio/accel/bma400.h
index e8f802a82300..36edbaff4f7f 100644
--- a/drivers/iio/accel/bma400.h
+++ b/drivers/iio/accel/bma400.h
@@ -40,6 +40,7 @@
#define BMA400_INT_STAT1_REG 0x0f
#define BMA400_INT_STAT2_REG 0x10
#define BMA400_INT12_MAP_REG 0x23
+#define BMA400_INT_ENG_OVRUN_MSK BIT(4)
/* Temperature register */
#define BMA400_TEMP_DATA_REG 0x11
@@ -105,6 +106,19 @@
#define BMA400_INT_GEN2_MSK BIT(3)
#define BMA400_GEN_HYST_MSK GENMASK(1, 0)
+/* TAP config registers */
+#define BMA400_TAP_CONFIG 0x57
+#define BMA400_TAP_CONFIG1 0x58
+#define BMA400_S_TAP_MSK BIT(2)
+#define BMA400_D_TAP_MSK BIT(3)
+#define BMA400_INT_S_TAP_MSK BIT(10)
+#define BMA400_INT_D_TAP_MSK BIT(11)
+#define BMA400_TAP_SEN_MSK GENMASK(2, 0)
+#define BMA400_TAP_TICSTH_MSK GENMASK(1, 0)
+#define BMA400_TAP_QUIET_MSK GENMASK(3, 2)
+#define BMA400_TAP_QUIETDT_MSK GENMASK(5, 4)
+#define BMA400_TAP_TIM_LIST_LEN 4
+
/*
* BMA400_SCALE_MIN macro value represents m/s^2 for 1 LSB before
* converting to micro values for +-2g range.
diff --git a/drivers/iio/accel/bma400_core.c b/drivers/iio/accel/bma400_core.c
index c31bdd9b168e..ad8fce3e08cd 100644
--- a/drivers/iio/accel/bma400_core.c
+++ b/drivers/iio/accel/bma400_core.c
@@ -26,6 +26,7 @@
#include <linux/iio/iio.h>
#include <linux/iio/buffer.h>
#include <linux/iio/events.h>
+#include <linux/iio/sysfs.h>
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@@ -47,6 +48,27 @@ static int bma400_sample_freqs[14];
static const int bma400_osr_range[] = { 0, 1, 3 };
+static int tap_reset_timeout[BMA400_TAP_TIM_LIST_LEN] = {
+ 300000,
+ 400000,
+ 500000,
+ 600000
+};
+
+static int tap_max2min_time[BMA400_TAP_TIM_LIST_LEN] = {
+ 30000,
+ 45000,
+ 60000,
+ 90000
+};
+
+static int double_tap2_min_delay[BMA400_TAP_TIM_LIST_LEN] = {
+ 20000,
+ 40000,
+ 60000,
+ 80000
+};
+
/* See the ACC_CONFIG0 section of the datasheet */
enum bma400_power_mode {
POWER_MODE_SLEEP = 0x00,
@@ -88,6 +110,7 @@ struct bma400_data {
bool step_event_en;
bool activity_event_en;
unsigned int generic_event_en;
+ unsigned int tap_event_en_bitmask;
/* Correct time stamp alignment */
struct {
__le16 buff[3];
@@ -216,6 +239,115 @@ static const struct iio_event_spec bma400_accel_event[] = {
BIT(IIO_EV_INFO_HYSTERESIS) |
BIT(IIO_EV_INFO_ENABLE),
},
+ {
+ .type = IIO_EV_TYPE_GESTURE,
+ .dir = IIO_EV_DIR_SINGLETAP,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_RESET_TIMEOUT),
+ },
+ {
+ .type = IIO_EV_TYPE_GESTURE,
+ .dir = IIO_EV_DIR_DOUBLETAP,
+ .mask_shared_by_type = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_RESET_TIMEOUT) |
+ BIT(IIO_EV_INFO_TAP2_MIN_DELAY),
+ },
+};
+
+static int usec_to_tapreg_raw(int usec, const int *time_list)
+{
+ int index;
+
+ for (index = 0; index < BMA400_TAP_TIM_LIST_LEN; index++) {
+ if (usec == time_list[index])
+ return index;
+ }
+ return -EINVAL;
+}
+
+static ssize_t in_accel_gesture_tap_maxtomin_time_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct bma400_data *data = iio_priv(indio_dev);
+ int ret, reg_val, raw, vals[2];
+
+ ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1, &reg_val);
+ if (ret)
+ return ret;
+
+ raw = FIELD_GET(BMA400_TAP_TICSTH_MSK, reg_val);
+ vals[0] = 0;
+ vals[1] = tap_max2min_time[raw];
+
+ return iio_format_value(buf, IIO_VAL_INT_PLUS_MICRO, 2, vals);
+}
+
+static ssize_t in_accel_gesture_tap_maxtomin_time_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 bma400_data *data = iio_priv(indio_dev);
+ int ret, val_int, val_fract, raw;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &val_int, &val_fract);
+ if (ret)
+ return ret;
+
+ raw = usec_to_tapreg_raw(val_fract, tap_max2min_time);
+ if (raw < 0)
+ return -EINVAL;
+
+ ret = regmap_update_bits(data->regmap, BMA400_TAP_CONFIG1,
+ BMA400_TAP_TICSTH_MSK,
+ FIELD_PREP(BMA400_TAP_TICSTH_MSK, raw));
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static IIO_DEVICE_ATTR_RW(in_accel_gesture_tap_maxtomin_time, 0);
+
+/*
+ * Tap interrupts works with 200 Hz input data rate and the time based tap
+ * controls are in the terms of data samples so the below calculation is
+ * used to convert the configuration values into seconds.
+ * e.g.:
+ * 60 data samples * 0.005 ms = 0.3 seconds.
+ * 80 data samples * 0.005 ms = 0.4 seconds.
+ */
+
+/* quiet configuration values in seconds */
+static IIO_CONST_ATTR(in_accel_gesture_tap_reset_timeout_available,
+ "0.3 0.4 0.5 0.6");
+
+/* tics_th configuration values in seconds */
+static IIO_CONST_ATTR(in_accel_gesture_tap_maxtomin_time_available,
+ "0.03 0.045 0.06 0.09");
+
+/* quiet_dt configuration values in seconds */
+static IIO_CONST_ATTR(in_accel_gesture_doubletap_tap2_min_delay_available,
+ "0.02 0.04 0.06 0.08");
+
+/* List of sensitivity values available to configure tap interrupts */
+static IIO_CONST_ATTR(in_accel_gesture_tap_value_available, "0 1 2 3 4 5 6 7");
+
+static struct attribute *bma400_event_attributes[] = {
+ &iio_const_attr_in_accel_gesture_tap_value_available.dev_attr.attr,
+ &iio_const_attr_in_accel_gesture_tap_reset_timeout_available.dev_attr.attr,
+ &iio_const_attr_in_accel_gesture_tap_maxtomin_time_available.dev_attr.attr,
+ &iio_const_attr_in_accel_gesture_doubletap_tap2_min_delay_available.dev_attr.attr,
+ &iio_dev_attr_in_accel_gesture_tap_maxtomin_time.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group bma400_event_attribute_group = {
+ .attrs = bma400_event_attributes,
};
#define BMA400_ACC_CHANNEL(_index, _axis) { \
@@ -1012,6 +1144,12 @@ static int bma400_read_event_config(struct iio_dev *indio_dev,
case IIO_EV_DIR_FALLING:
return FIELD_GET(BMA400_INT_GEN2_MSK,
data->generic_event_en);
+ case IIO_EV_DIR_SINGLETAP:
+ return FIELD_GET(BMA400_S_TAP_MSK,
+ data->tap_event_en_bitmask);
+ case IIO_EV_DIR_DOUBLETAP:
+ return FIELD_GET(BMA400_D_TAP_MSK,
+ data->tap_event_en_bitmask);
default:
return -EINVAL;
}
@@ -1046,7 +1184,8 @@ static int bma400_activity_event_en(struct bma400_data *data,
enum iio_event_direction dir,
int state)
{
- int ret, reg, msk, value, field_value;
+ int ret, reg, msk, value;
+ int field_value = 0;
switch (dir) {
case IIO_EV_DIR_RISING:
@@ -1101,6 +1240,80 @@ static int bma400_activity_event_en(struct bma400_data *data,
return 0;
}
+static int bma400_tap_event_en(struct bma400_data *data,
+ enum iio_event_direction dir, int state)
+{
+ unsigned int mask, field_value;
+ int ret;
+
+ /*
+ * Tap interrupts can be configured only in normal mode.
+ * See table in section 4.3 "Power modes - performance modes" of
+ * datasheet v1.2.
+ */
+ if (data->power_mode != POWER_MODE_NORMAL)
+ return -EINVAL;
+
+ /*
+ * Tap interrupts are operating with a data rate of 200Hz.
+ * See section 4.7 "Tap sensing interrupt" in datasheet v1.2.
+ */
+ if (data->sample_freq.hz != 200 && state) {
+ dev_err(data->dev, "Invalid data rate for tap interrupts.\n");
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT12_MAP_REG,
+ BMA400_S_TAP_MSK,
+ FIELD_PREP(BMA400_S_TAP_MSK, state));
+ if (ret)
+ return ret;
+
+ switch (dir) {
+ case IIO_EV_DIR_SINGLETAP:
+ mask = BMA400_S_TAP_MSK;
+ set_mask_bits(&field_value, BMA400_S_TAP_MSK,
+ FIELD_PREP(BMA400_S_TAP_MSK, state));
+ break;
+ case IIO_EV_DIR_DOUBLETAP:
+ mask = BMA400_D_TAP_MSK;
+ set_mask_bits(&field_value, BMA400_D_TAP_MSK,
+ FIELD_PREP(BMA400_D_TAP_MSK, state));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(data->regmap, BMA400_INT_CONFIG1_REG, mask,
+ field_value);
+ if (ret)
+ return ret;
+
+ set_mask_bits(&data->tap_event_en_bitmask, mask, field_value);
+
+ return 0;
+}
+
+static int bma400_disable_adv_interrupt(struct bma400_data *data)
+{
+ int ret;
+
+ ret = regmap_write(data->regmap, BMA400_INT_CONFIG0_REG, 0);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(data->regmap, BMA400_INT_CONFIG1_REG, 0);
+ if (ret)
+ return ret;
+
+ data->tap_event_en_bitmask = 0;
+ data->generic_event_en = 0;
+ data->step_event_en = false;
+ data->activity_event_en = false;
+
+ return 0;
+}
+
static int bma400_write_event_config(struct iio_dev *indio_dev,
const struct iio_chan_spec *chan,
enum iio_event_type type,
@@ -1111,10 +1324,20 @@ static int bma400_write_event_config(struct iio_dev *indio_dev,
switch (chan->type) {
case IIO_ACCEL:
- mutex_lock(&data->mutex);
- ret = bma400_activity_event_en(data, dir, state);
- mutex_unlock(&data->mutex);
- return ret;
+ switch (type) {
+ case IIO_EV_TYPE_MAG:
+ mutex_lock(&data->mutex);
+ ret = bma400_activity_event_en(data, dir, state);
+ mutex_unlock(&data->mutex);
+ return ret;
+ case IIO_EV_TYPE_GESTURE:
+ mutex_lock(&data->mutex);
+ ret = bma400_tap_event_en(data, dir, state);
+ mutex_unlock(&data->mutex);
+ return ret;
+ default:
+ return -EINVAL;
+ }
case IIO_STEPS:
mutex_lock(&data->mutex);
ret = bma400_steps_event_enable(data, state);
@@ -1157,10 +1380,13 @@ static int bma400_read_event_value(struct iio_dev *indio_dev,
int *val, int *val2)
{
struct bma400_data *data = iio_priv(indio_dev);
- int ret, reg;
+ int ret, reg, reg_val, raw;
- switch (chan->type) {
- case IIO_ACCEL:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (type) {
+ case IIO_EV_TYPE_MAG:
reg = get_gen_config_reg(dir);
if (reg < 0)
return -EINVAL;
@@ -1196,6 +1422,39 @@ static int bma400_read_event_value(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
+ case IIO_EV_TYPE_GESTURE:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ ret = regmap_read(data->regmap, BMA400_TAP_CONFIG,
+ &reg_val);
+ if (ret)
+ return ret;
+
+ *val = FIELD_GET(BMA400_TAP_SEN_MSK, reg_val);
+ return IIO_VAL_INT;
+ case IIO_EV_INFO_RESET_TIMEOUT:
+ ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1,
+ &reg_val);
+ if (ret)
+ return ret;
+
+ raw = FIELD_GET(BMA400_TAP_QUIET_MSK, reg_val);
+ *val = 0;
+ *val2 = tap_reset_timeout[raw];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_EV_INFO_TAP2_MIN_DELAY:
+ ret = regmap_read(data->regmap, BMA400_TAP_CONFIG1,
+ &reg_val);
+ if (ret)
+ return ret;
+
+ raw = FIELD_GET(BMA400_TAP_QUIETDT_MSK, reg_val);
+ *val = 0;
+ *val2 = double_tap2_min_delay[raw];
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
default:
return -EINVAL;
}
@@ -1209,10 +1468,13 @@ static int bma400_write_event_value(struct iio_dev *indio_dev,
int val, int val2)
{
struct bma400_data *data = iio_priv(indio_dev);
- int reg, ret;
+ int reg, ret, raw;
- switch (chan->type) {
- case IIO_ACCEL:
+ if (chan->type != IIO_ACCEL)
+ return -EINVAL;
+
+ switch (type) {
+ case IIO_EV_TYPE_MAG:
reg = get_gen_config_reg(dir);
if (reg < 0)
return -EINVAL;
@@ -1248,6 +1510,40 @@ static int bma400_write_event_value(struct iio_dev *indio_dev,
default:
return -EINVAL;
}
+ case IIO_EV_TYPE_GESTURE:
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ if (val < 0 || val > 7)
+ return -EINVAL;
+
+ return regmap_update_bits(data->regmap,
+ BMA400_TAP_CONFIG,
+ BMA400_TAP_SEN_MSK,
+ FIELD_PREP(BMA400_TAP_SEN_MSK,
+ val));
+ case IIO_EV_INFO_RESET_TIMEOUT:
+ raw = usec_to_tapreg_raw(val2, tap_reset_timeout);
+ if (raw < 0)
+ return -EINVAL;
+
+ return regmap_update_bits(data->regmap,
+ BMA400_TAP_CONFIG1,
+ BMA400_TAP_QUIET_MSK,
+ FIELD_PREP(BMA400_TAP_QUIET_MSK,
+ raw));
+ case IIO_EV_INFO_TAP2_MIN_DELAY:
+ raw = usec_to_tapreg_raw(val2, double_tap2_min_delay);
+ if (raw < 0)
+ return -EINVAL;
+
+ return regmap_update_bits(data->regmap,
+ BMA400_TAP_CONFIG1,
+ BMA400_TAP_QUIETDT_MSK,
+ FIELD_PREP(BMA400_TAP_QUIETDT_MSK,
+ raw));
+ default:
+ return -EINVAL;
+ }
default:
return -EINVAL;
}
@@ -1287,6 +1583,7 @@ static const struct iio_info bma400_info = {
.write_event_config = bma400_write_event_config,
.write_event_value = bma400_write_event_value,
.read_event_value = bma400_read_event_value,
+ .event_attrs = &bma400_event_attribute_group,
};
static const struct iio_trigger_ops bma400_trigger_ops = {
@@ -1350,6 +1647,32 @@ static irqreturn_t bma400_interrupt(int irq, void *private)
if (ret || !data->status)
goto unlock_err;
+ /*
+ * Disable all advance interrupts if interrupt engine overrun occurs.
+ * See section 4.7 "Interrupt engine overrun" in datasheet v1.2.
+ */
+ if (FIELD_GET(BMA400_INT_ENG_OVRUN_MSK, le16_to_cpu(data->status))) {
+ bma400_disable_adv_interrupt(data);
+ dev_err(data->dev, "Interrupt engine overrun\n");
+ goto unlock_err;
+ }
+
+ if (FIELD_GET(BMA400_INT_S_TAP_MSK, le16_to_cpu(data->status)))
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_GESTURE,
+ IIO_EV_DIR_SINGLETAP),
+ timestamp);
+
+ if (FIELD_GET(BMA400_INT_D_TAP_MSK, le16_to_cpu(data->status)))
+ iio_push_event(indio_dev,
+ IIO_MOD_EVENT_CODE(IIO_ACCEL, 0,
+ IIO_MOD_X_OR_Y_OR_Z,
+ IIO_EV_TYPE_GESTURE,
+ IIO_EV_DIR_DOUBLETAP),
+ timestamp);
+
if (FIELD_GET(BMA400_INT_GEN1_MSK, le16_to_cpu(data->status)))
ev_dir = IIO_EV_DIR_RISING;
@@ -1467,5 +1790,6 @@ int bma400_probe(struct device *dev, struct regmap *regmap, int irq,
EXPORT_SYMBOL_NS(bma400_probe, IIO_BMA400);
MODULE_AUTHOR("Dan Robertson <dan@dlrobertson.com>");
+MODULE_AUTHOR("Jagath Jog J <jagathjog1996@gmail.com>");
MODULE_DESCRIPTION("Bosch BMA400 triaxial acceleration sensor core");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/bmi088-accel-core.c b/drivers/iio/accel/bmi088-accel-core.c
index bca4cf98bf4d..84edcc78d796 100644
--- a/drivers/iio/accel/bmi088-accel-core.c
+++ b/drivers/iio/accel/bmi088-accel-core.c
@@ -606,7 +606,7 @@ void bmi088_accel_core_remove(struct device *dev)
}
EXPORT_SYMBOL_NS_GPL(bmi088_accel_core_remove, IIO_BMI088);
-static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev)
+static int bmi088_accel_runtime_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmi088_accel_data *data = iio_priv(indio_dev);
@@ -614,7 +614,7 @@ static int __maybe_unused bmi088_accel_runtime_suspend(struct device *dev)
return bmi088_accel_power_down(data);
}
-static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev)
+static int bmi088_accel_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct bmi088_accel_data *data = iio_priv(indio_dev);
@@ -622,13 +622,10 @@ static int __maybe_unused bmi088_accel_runtime_resume(struct device *dev)
return bmi088_accel_power_up(data);
}
-const struct dev_pm_ops bmi088_accel_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
- pm_runtime_force_resume)
- SET_RUNTIME_PM_OPS(bmi088_accel_runtime_suspend,
- bmi088_accel_runtime_resume, NULL)
-};
-EXPORT_SYMBOL_NS_GPL(bmi088_accel_pm_ops, IIO_BMI088);
+EXPORT_NS_GPL_RUNTIME_DEV_PM_OPS(bmi088_accel_pm_ops,
+ bmi088_accel_runtime_suspend,
+ bmi088_accel_runtime_resume, NULL,
+ IIO_BMI088);
MODULE_AUTHOR("Niek van Agt <niek.van.agt@topicproducts.com>");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/accel/bmi088-accel-spi.c b/drivers/iio/accel/bmi088-accel-spi.c
index 9e2ed3bd5661..ee540edd8412 100644
--- a/drivers/iio/accel/bmi088-accel-spi.c
+++ b/drivers/iio/accel/bmi088-accel-spi.c
@@ -80,7 +80,7 @@ MODULE_DEVICE_TABLE(spi, bmi088_accel_id);
static struct spi_driver bmi088_accel_driver = {
.driver = {
.name = "bmi088_accel_spi",
- .pm = &bmi088_accel_pm_ops,
+ .pm = pm_ptr(&bmi088_accel_pm_ops),
.of_match_table = bmi088_of_match,
},
.probe = bmi088_accel_probe,
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index 94f7b6ac5c87..adc66b3615c0 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -1064,7 +1064,7 @@ static int kxcjk1013_write_event_config(struct iio_dev *indio_dev,
/*
* We will expect the enable and disable to do operation in
- * in reverse order. This will happen here anyway as our
+ * reverse order. This will happen here anyway as our
* resume operation uses sync mode runtime pm calls, the
* suspend operation will be delayed by autosuspend delay
* So the disable operation will still happen in reverse of
diff --git a/drivers/iio/accel/msa311.c b/drivers/iio/accel/msa311.c
new file mode 100644
index 000000000000..2fded3759171
--- /dev/null
+++ b/drivers/iio/accel/msa311.c
@@ -0,0 +1,1321 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MEMSensing digital 3-Axis accelerometer
+ *
+ * MSA311 is a tri-axial, low-g accelerometer with I2C digital output for
+ * sensitivity consumer applications. It has dynamic user-selectable full
+ * scales range of +-2g/+-4g/+-8g/+-16g and allows acceleration measurements
+ * with output data rates from 1Hz to 1000Hz.
+ *
+ * MSA311 is available in an ultra small (2mm x 2mm, height 0.95mm) LGA package
+ * and is guaranteed to operate over -40C to +85C.
+ *
+ * This driver supports following MSA311 features:
+ * - IIO interface
+ * - Different power modes: NORMAL, SUSPEND
+ * - ODR (Output Data Rate) selection
+ * - Scale selection
+ * - IIO triggered buffer
+ * - NEW_DATA interrupt + trigger
+ *
+ * Below features to be done:
+ * - Motion Events: ACTIVE, TAP, ORIENT, FREEFALL
+ * - Low Power mode
+ *
+ * Copyright (c) 2022, SberDevices. All Rights Reserved.
+ *
+ * Author: Dmitry Rokosov <ddrokosov@sberdevices.ru>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/string_helpers.h>
+#include <linux/units.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define MSA311_SOFT_RESET_REG 0x00
+#define MSA311_PARTID_REG 0x01
+#define MSA311_ACC_X_REG 0x02
+#define MSA311_ACC_Y_REG 0x04
+#define MSA311_ACC_Z_REG 0x06
+#define MSA311_MOTION_INT_REG 0x09
+#define MSA311_DATA_INT_REG 0x0A
+#define MSA311_TAP_ACTIVE_STS_REG 0x0B
+#define MSA311_ORIENT_STS_REG 0x0C
+#define MSA311_RANGE_REG 0x0F
+#define MSA311_ODR_REG 0x10
+#define MSA311_PWR_MODE_REG 0x11
+#define MSA311_SWAP_POLARITY_REG 0x12
+#define MSA311_INT_SET_0_REG 0x16
+#define MSA311_INT_SET_1_REG 0x17
+#define MSA311_INT_MAP_0_REG 0x19
+#define MSA311_INT_MAP_1_REG 0x1A
+#define MSA311_INT_CONFIG_REG 0x20
+#define MSA311_INT_LATCH_REG 0x21
+#define MSA311_FREEFALL_DUR_REG 0x22
+#define MSA311_FREEFALL_TH_REG 0x23
+#define MSA311_FREEFALL_HY_REG 0x24
+#define MSA311_ACTIVE_DUR_REG 0x27
+#define MSA311_ACTIVE_TH_REG 0x28
+#define MSA311_TAP_DUR_REG 0x2A
+#define MSA311_TAP_TH_REG 0x2B
+#define MSA311_ORIENT_HY_REG 0x2C
+#define MSA311_Z_BLOCK_REG 0x2D
+#define MSA311_OFFSET_X_REG 0x38
+#define MSA311_OFFSET_Y_REG 0x39
+#define MSA311_OFFSET_Z_REG 0x3A
+
+enum msa311_fields {
+ /* Soft_Reset */
+ F_SOFT_RESET_I2C, F_SOFT_RESET_SPI,
+ /* Motion_Interrupt */
+ F_ORIENT_INT, F_S_TAP_INT, F_D_TAP_INT, F_ACTIVE_INT, F_FREEFALL_INT,
+ /* Data_Interrupt */
+ F_NEW_DATA_INT,
+ /* Tap_Active_Status */
+ F_TAP_SIGN, F_TAP_FIRST_X, F_TAP_FIRST_Y, F_TAP_FIRST_Z, F_ACTV_SIGN,
+ F_ACTV_FIRST_X, F_ACTV_FIRST_Y, F_ACTV_FIRST_Z,
+ /* Orientation_Status */
+ F_ORIENT_Z, F_ORIENT_X_Y,
+ /* Range */
+ F_FS,
+ /* ODR */
+ F_X_AXIS_DIS, F_Y_AXIS_DIS, F_Z_AXIS_DIS, F_ODR,
+ /* Power Mode/Bandwidth */
+ F_PWR_MODE, F_LOW_POWER_BW,
+ /* Swap_Polarity */
+ F_X_POLARITY, F_Y_POLARITY, F_Z_POLARITY, F_X_Y_SWAP,
+ /* Int_Set_0 */
+ F_ORIENT_INT_EN, F_S_TAP_INT_EN, F_D_TAP_INT_EN, F_ACTIVE_INT_EN_Z,
+ F_ACTIVE_INT_EN_Y, F_ACTIVE_INT_EN_X,
+ /* Int_Set_1 */
+ F_NEW_DATA_INT_EN, F_FREEFALL_INT_EN,
+ /* Int_Map_0 */
+ F_INT1_ORIENT, F_INT1_S_TAP, F_INT1_D_TAP, F_INT1_ACTIVE,
+ F_INT1_FREEFALL,
+ /* Int_Map_1 */
+ F_INT1_NEW_DATA,
+ /* Int_Config */
+ F_INT1_OD, F_INT1_LVL,
+ /* Int_Latch */
+ F_RESET_INT, F_LATCH_INT,
+ /* Freefall_Hy */
+ F_FREEFALL_MODE, F_FREEFALL_HY,
+ /* Active_Dur */
+ F_ACTIVE_DUR,
+ /* Tap_Dur */
+ F_TAP_QUIET, F_TAP_SHOCK, F_TAP_DUR,
+ /* Tap_Th */
+ F_TAP_TH,
+ /* Orient_Hy */
+ F_ORIENT_HYST, F_ORIENT_BLOCKING, F_ORIENT_MODE,
+ /* Z_Block */
+ F_Z_BLOCKING,
+ /* End of register map */
+ F_MAX_FIELDS,
+};
+
+static const struct reg_field msa311_reg_fields[] = {
+ /* Soft_Reset */
+ [F_SOFT_RESET_I2C] = REG_FIELD(MSA311_SOFT_RESET_REG, 2, 2),
+ [F_SOFT_RESET_SPI] = REG_FIELD(MSA311_SOFT_RESET_REG, 5, 5),
+ /* Motion_Interrupt */
+ [F_ORIENT_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 6, 6),
+ [F_S_TAP_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 5, 5),
+ [F_D_TAP_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 4, 4),
+ [F_ACTIVE_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 2, 2),
+ [F_FREEFALL_INT] = REG_FIELD(MSA311_MOTION_INT_REG, 0, 0),
+ /* Data_Interrupt */
+ [F_NEW_DATA_INT] = REG_FIELD(MSA311_DATA_INT_REG, 0, 0),
+ /* Tap_Active_Status */
+ [F_TAP_SIGN] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 7, 7),
+ [F_TAP_FIRST_X] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 6, 6),
+ [F_TAP_FIRST_Y] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 5, 5),
+ [F_TAP_FIRST_Z] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 4, 4),
+ [F_ACTV_SIGN] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 3, 3),
+ [F_ACTV_FIRST_X] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 2, 2),
+ [F_ACTV_FIRST_Y] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 1, 1),
+ [F_ACTV_FIRST_Z] = REG_FIELD(MSA311_TAP_ACTIVE_STS_REG, 0, 0),
+ /* Orientation_Status */
+ [F_ORIENT_Z] = REG_FIELD(MSA311_ORIENT_STS_REG, 6, 6),
+ [F_ORIENT_X_Y] = REG_FIELD(MSA311_ORIENT_STS_REG, 4, 5),
+ /* Range */
+ [F_FS] = REG_FIELD(MSA311_RANGE_REG, 0, 1),
+ /* ODR */
+ [F_X_AXIS_DIS] = REG_FIELD(MSA311_ODR_REG, 7, 7),
+ [F_Y_AXIS_DIS] = REG_FIELD(MSA311_ODR_REG, 6, 6),
+ [F_Z_AXIS_DIS] = REG_FIELD(MSA311_ODR_REG, 5, 5),
+ [F_ODR] = REG_FIELD(MSA311_ODR_REG, 0, 3),
+ /* Power Mode/Bandwidth */
+ [F_PWR_MODE] = REG_FIELD(MSA311_PWR_MODE_REG, 6, 7),
+ [F_LOW_POWER_BW] = REG_FIELD(MSA311_PWR_MODE_REG, 1, 4),
+ /* Swap_Polarity */
+ [F_X_POLARITY] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 3, 3),
+ [F_Y_POLARITY] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 2, 2),
+ [F_Z_POLARITY] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 1, 1),
+ [F_X_Y_SWAP] = REG_FIELD(MSA311_SWAP_POLARITY_REG, 0, 0),
+ /* Int_Set_0 */
+ [F_ORIENT_INT_EN] = REG_FIELD(MSA311_INT_SET_0_REG, 6, 6),
+ [F_S_TAP_INT_EN] = REG_FIELD(MSA311_INT_SET_0_REG, 5, 5),
+ [F_D_TAP_INT_EN] = REG_FIELD(MSA311_INT_SET_0_REG, 4, 4),
+ [F_ACTIVE_INT_EN_Z] = REG_FIELD(MSA311_INT_SET_0_REG, 2, 2),
+ [F_ACTIVE_INT_EN_Y] = REG_FIELD(MSA311_INT_SET_0_REG, 1, 1),
+ [F_ACTIVE_INT_EN_X] = REG_FIELD(MSA311_INT_SET_0_REG, 0, 0),
+ /* Int_Set_1 */
+ [F_NEW_DATA_INT_EN] = REG_FIELD(MSA311_INT_SET_1_REG, 4, 4),
+ [F_FREEFALL_INT_EN] = REG_FIELD(MSA311_INT_SET_1_REG, 3, 3),
+ /* Int_Map_0 */
+ [F_INT1_ORIENT] = REG_FIELD(MSA311_INT_MAP_0_REG, 6, 6),
+ [F_INT1_S_TAP] = REG_FIELD(MSA311_INT_MAP_0_REG, 5, 5),
+ [F_INT1_D_TAP] = REG_FIELD(MSA311_INT_MAP_0_REG, 4, 4),
+ [F_INT1_ACTIVE] = REG_FIELD(MSA311_INT_MAP_0_REG, 2, 2),
+ [F_INT1_FREEFALL] = REG_FIELD(MSA311_INT_MAP_0_REG, 0, 0),
+ /* Int_Map_1 */
+ [F_INT1_NEW_DATA] = REG_FIELD(MSA311_INT_MAP_1_REG, 0, 0),
+ /* Int_Config */
+ [F_INT1_OD] = REG_FIELD(MSA311_INT_CONFIG_REG, 1, 1),
+ [F_INT1_LVL] = REG_FIELD(MSA311_INT_CONFIG_REG, 0, 0),
+ /* Int_Latch */
+ [F_RESET_INT] = REG_FIELD(MSA311_INT_LATCH_REG, 7, 7),
+ [F_LATCH_INT] = REG_FIELD(MSA311_INT_LATCH_REG, 0, 3),
+ /* Freefall_Hy */
+ [F_FREEFALL_MODE] = REG_FIELD(MSA311_FREEFALL_HY_REG, 2, 2),
+ [F_FREEFALL_HY] = REG_FIELD(MSA311_FREEFALL_HY_REG, 0, 1),
+ /* Active_Dur */
+ [F_ACTIVE_DUR] = REG_FIELD(MSA311_ACTIVE_DUR_REG, 0, 1),
+ /* Tap_Dur */
+ [F_TAP_QUIET] = REG_FIELD(MSA311_TAP_DUR_REG, 7, 7),
+ [F_TAP_SHOCK] = REG_FIELD(MSA311_TAP_DUR_REG, 6, 6),
+ [F_TAP_DUR] = REG_FIELD(MSA311_TAP_DUR_REG, 0, 2),
+ /* Tap_Th */
+ [F_TAP_TH] = REG_FIELD(MSA311_TAP_TH_REG, 0, 4),
+ /* Orient_Hy */
+ [F_ORIENT_HYST] = REG_FIELD(MSA311_ORIENT_HY_REG, 4, 6),
+ [F_ORIENT_BLOCKING] = REG_FIELD(MSA311_ORIENT_HY_REG, 2, 3),
+ [F_ORIENT_MODE] = REG_FIELD(MSA311_ORIENT_HY_REG, 0, 1),
+ /* Z_Block */
+ [F_Z_BLOCKING] = REG_FIELD(MSA311_Z_BLOCK_REG, 0, 3),
+};
+
+#define MSA311_WHO_AM_I 0x13
+
+/*
+ * Possible Full Scale ranges
+ *
+ * Axis data is 12-bit signed value, so
+ *
+ * fs0 = (2 + 2) * 9.81 / (2^11) = 0.009580
+ * fs1 = (4 + 4) * 9.81 / (2^11) = 0.019160
+ * fs2 = (8 + 8) * 9.81 / (2^11) = 0.038320
+ * fs3 = (16 + 16) * 9.81 / (2^11) = 0.076641
+ */
+enum {
+ MSA311_FS_2G,
+ MSA311_FS_4G,
+ MSA311_FS_8G,
+ MSA311_FS_16G,
+};
+
+struct iio_decimal_fract {
+ int integral;
+ int microfract;
+};
+
+static const struct iio_decimal_fract msa311_fs_table[] = {
+ {0, 9580}, {0, 19160}, {0, 38320}, {0, 76641},
+};
+
+/* Possible Output Data Rate values */
+enum {
+ MSA311_ODR_1_HZ,
+ MSA311_ODR_1_95_HZ,
+ MSA311_ODR_3_9_HZ,
+ MSA311_ODR_7_81_HZ,
+ MSA311_ODR_15_63_HZ,
+ MSA311_ODR_31_25_HZ,
+ MSA311_ODR_62_5_HZ,
+ MSA311_ODR_125_HZ,
+ MSA311_ODR_250_HZ,
+ MSA311_ODR_500_HZ,
+ MSA311_ODR_1000_HZ,
+};
+
+static const struct iio_decimal_fract msa311_odr_table[] = {
+ {1, 0}, {1, 950000}, {3, 900000}, {7, 810000}, {15, 630000},
+ {31, 250000}, {62, 500000}, {125, 0}, {250, 0}, {500, 0}, {1000, 0},
+};
+
+/* All supported power modes */
+#define MSA311_PWR_MODE_NORMAL 0b00
+#define MSA311_PWR_MODE_LOW 0b01
+#define MSA311_PWR_MODE_UNKNOWN 0b10
+#define MSA311_PWR_MODE_SUSPEND 0b11
+static const char * const msa311_pwr_modes[] = {
+ [MSA311_PWR_MODE_NORMAL] = "normal",
+ [MSA311_PWR_MODE_LOW] = "low",
+ [MSA311_PWR_MODE_UNKNOWN] = "unknown",
+ [MSA311_PWR_MODE_SUSPEND] = "suspend",
+};
+
+/* Autosuspend delay */
+#define MSA311_PWR_SLEEP_DELAY_MS 2000
+
+/* Possible INT1 types and levels */
+enum {
+ MSA311_INT1_OD_PUSH_PULL,
+ MSA311_INT1_OD_OPEN_DRAIN,
+};
+
+enum {
+ MSA311_INT1_LVL_LOW,
+ MSA311_INT1_LVL_HIGH,
+};
+
+/* Latch INT modes */
+#define MSA311_LATCH_INT_NOT_LATCHED 0b0000
+#define MSA311_LATCH_INT_250MS 0b0001
+#define MSA311_LATCH_INT_500MS 0b0010
+#define MSA311_LATCH_INT_1S 0b0011
+#define MSA311_LATCH_INT_2S 0b0100
+#define MSA311_LATCH_INT_4S 0b0101
+#define MSA311_LATCH_INT_8S 0b0110
+#define MSA311_LATCH_INT_1MS 0b1010
+#define MSA311_LATCH_INT_2MS 0b1011
+#define MSA311_LATCH_INT_25MS 0b1100
+#define MSA311_LATCH_INT_50MS 0b1101
+#define MSA311_LATCH_INT_100MS 0b1110
+#define MSA311_LATCH_INT_LATCHED 0b0111
+
+static const struct regmap_range msa311_readonly_registers[] = {
+ regmap_reg_range(MSA311_PARTID_REG, MSA311_ORIENT_STS_REG),
+};
+
+static const struct regmap_access_table msa311_writeable_table = {
+ .no_ranges = msa311_readonly_registers,
+ .n_no_ranges = ARRAY_SIZE(msa311_readonly_registers),
+};
+
+static const struct regmap_range msa311_writeonly_registers[] = {
+ regmap_reg_range(MSA311_SOFT_RESET_REG, MSA311_SOFT_RESET_REG),
+};
+
+static const struct regmap_access_table msa311_readable_table = {
+ .no_ranges = msa311_writeonly_registers,
+ .n_no_ranges = ARRAY_SIZE(msa311_writeonly_registers),
+};
+
+static const struct regmap_range msa311_volatile_registers[] = {
+ regmap_reg_range(MSA311_ACC_X_REG, MSA311_ORIENT_STS_REG),
+};
+
+static const struct regmap_access_table msa311_volatile_table = {
+ .yes_ranges = msa311_volatile_registers,
+ .n_yes_ranges = ARRAY_SIZE(msa311_volatile_registers),
+};
+
+static const struct regmap_config msa311_regmap_config = {
+ .name = "msa311",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = MSA311_OFFSET_Z_REG,
+ .wr_table = &msa311_writeable_table,
+ .rd_table = &msa311_readable_table,
+ .volatile_table = &msa311_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+#define MSA311_GENMASK(field) ({ \
+ typeof(&(msa311_reg_fields)[0]) _field; \
+ _field = &msa311_reg_fields[(field)]; \
+ GENMASK(_field->msb, _field->lsb); \
+})
+
+/**
+ * struct msa311_priv - MSA311 internal private state
+ * @regs: Underlying I2C bus adapter used to abstract slave
+ * register accesses
+ * @fields: Abstract objects for each registers fields access
+ * @dev: Device handler associated with appropriate bus client
+ * @lock: Protects msa311 device state between setup and data access routines
+ * (power transitions, samp_freq/scale tune, retrieving axes data, etc)
+ * @chip_name: Chip name in the format "msa311-%02x" % partid
+ * @new_data_trig: Optional NEW_DATA interrupt driven trigger used
+ * to notify external consumers a new sample is ready
+ * @vdd: Optional external voltage regulator for the device power supply
+ */
+struct msa311_priv {
+ struct regmap *regs;
+ struct regmap_field *fields[F_MAX_FIELDS];
+
+ struct device *dev;
+ struct mutex lock;
+ char *chip_name;
+
+ struct iio_trigger *new_data_trig;
+ struct regulator *vdd;
+};
+
+enum msa311_si {
+ MSA311_SI_X,
+ MSA311_SI_Y,
+ MSA311_SI_Z,
+ MSA311_SI_TIMESTAMP,
+};
+
+#define MSA311_ACCEL_CHANNEL(axis) { \
+ .type = IIO_ACCEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = MSA311_SI_##axis, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 4, \
+ .endianness = IIO_LE, \
+ }, \
+ .datasheet_name = "ACC_"#axis, \
+}
+
+static const struct iio_chan_spec msa311_channels[] = {
+ MSA311_ACCEL_CHANNEL(X),
+ MSA311_ACCEL_CHANNEL(Y),
+ MSA311_ACCEL_CHANNEL(Z),
+ IIO_CHAN_SOFT_TIMESTAMP(MSA311_SI_TIMESTAMP),
+};
+
+/**
+ * msa311_get_odr() - Read Output Data Rate (ODR) value from MSA311 accel
+ * @msa311: MSA311 internal private state
+ * @odr: output ODR value
+ *
+ * This function should be called under msa311->lock.
+ *
+ * Return: 0 on success, -ERRNO in other failures
+ */
+static int msa311_get_odr(struct msa311_priv *msa311, unsigned int *odr)
+{
+ int err;
+
+ err = regmap_field_read(msa311->fields[F_ODR], odr);
+ if (err)
+ return err;
+
+ /*
+ * Filter the same 1000Hz ODR register values based on datasheet info.
+ * ODR can be equal to 1010-1111 for 1000Hz, but function returns 1010
+ * all the time.
+ */
+ if (*odr > MSA311_ODR_1000_HZ)
+ *odr = MSA311_ODR_1000_HZ;
+
+ return 0;
+}
+
+/**
+ * msa311_set_odr() - Setup Output Data Rate (ODR) value for MSA311 accel
+ * @msa311: MSA311 internal private state
+ * @odr: requested ODR value
+ *
+ * This function should be called under msa311->lock. Possible ODR values:
+ * - 1Hz (not available in normal mode)
+ * - 1.95Hz (not available in normal mode)
+ * - 3.9Hz
+ * - 7.81Hz
+ * - 15.63Hz
+ * - 31.25Hz
+ * - 62.5Hz
+ * - 125Hz
+ * - 250Hz
+ * - 500Hz
+ * - 1000Hz
+ *
+ * Return: 0 on success, -EINVAL for bad ODR value in the certain power mode,
+ * -ERRNO in other failures
+ */
+static int msa311_set_odr(struct msa311_priv *msa311, unsigned int odr)
+{
+ struct device *dev = msa311->dev;
+ unsigned int pwr_mode;
+ bool good_odr;
+ int err;
+
+ err = regmap_field_read(msa311->fields[F_PWR_MODE], &pwr_mode);
+ if (err)
+ return err;
+
+ /* Filter bad ODR values */
+ if (pwr_mode == MSA311_PWR_MODE_NORMAL)
+ good_odr = (odr > MSA311_ODR_1_95_HZ);
+ else
+ good_odr = false;
+
+ if (!good_odr) {
+ dev_err(dev,
+ "can't set odr %u.%06uHz, not available in %s mode\n",
+ msa311_odr_table[odr].integral,
+ msa311_odr_table[odr].microfract,
+ msa311_pwr_modes[pwr_mode]);
+ return -EINVAL;
+ }
+
+ return regmap_field_write(msa311->fields[F_ODR], odr);
+}
+
+/**
+ * msa311_wait_for_next_data() - Wait next accel data available after resume
+ * @msa311: MSA311 internal private state
+ *
+ * Return: 0 on success, -EINTR if msleep() was interrupted,
+ * -ERRNO in other failures
+ */
+static int msa311_wait_for_next_data(struct msa311_priv *msa311)
+{
+ static const unsigned int unintr_thresh_ms = 20;
+ struct device *dev = msa311->dev;
+ unsigned long freq_uhz;
+ unsigned long wait_ms;
+ unsigned int odr;
+ int err;
+
+ err = msa311_get_odr(msa311, &odr);
+ if (err) {
+ dev_err(dev, "can't get actual frequency (%pe)\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ /*
+ * After msa311 resuming is done, we need to wait for data
+ * to be refreshed by accel logic.
+ * A certain timeout is calculated based on the current ODR value.
+ * If requested timeout isn't so long (let's assume 20ms),
+ * we can wait for next data in uninterruptible sleep.
+ */
+ freq_uhz = msa311_odr_table[odr].integral * MICROHZ_PER_HZ +
+ msa311_odr_table[odr].microfract;
+ wait_ms = (MICROHZ_PER_HZ / freq_uhz) * MSEC_PER_SEC;
+
+ if (wait_ms < unintr_thresh_ms)
+ usleep_range(wait_ms * USEC_PER_MSEC,
+ unintr_thresh_ms * USEC_PER_MSEC);
+ else if (msleep_interruptible(wait_ms))
+ return -EINTR;
+
+ return 0;
+}
+
+/**
+ * msa311_set_pwr_mode() - Install certain MSA311 power mode
+ * @msa311: MSA311 internal private state
+ * @mode: Power mode can be equal to NORMAL or SUSPEND
+ *
+ * This function should be called under msa311->lock.
+ *
+ * Return: 0 on success, -ERRNO on failure
+ */
+static int msa311_set_pwr_mode(struct msa311_priv *msa311, unsigned int mode)
+{
+ struct device *dev = msa311->dev;
+ unsigned int prev_mode;
+ int err;
+
+ if (mode >= ARRAY_SIZE(msa311_pwr_modes))
+ return -EINVAL;
+
+ dev_dbg(dev, "transition to %s mode\n", msa311_pwr_modes[mode]);
+
+ err = regmap_field_read(msa311->fields[F_PWR_MODE], &prev_mode);
+ if (err)
+ return err;
+
+ err = regmap_field_write(msa311->fields[F_PWR_MODE], mode);
+ if (err)
+ return err;
+
+ /* Wait actual data if we wake up */
+ if (prev_mode == MSA311_PWR_MODE_SUSPEND &&
+ mode == MSA311_PWR_MODE_NORMAL)
+ return msa311_wait_for_next_data(msa311);
+
+ return 0;
+}
+
+/**
+ * msa311_get_axis() - Read MSA311 accel data for certain IIO channel axis spec
+ * @msa311: MSA311 internal private state
+ * @chan: IIO channel specification
+ * @axis: Output accel axis data for requested IIO channel spec
+ *
+ * This function should be called under msa311->lock.
+ *
+ * Return: 0 on success, -EINVAL for unknown IIO channel specification,
+ * -ERRNO in other failures
+ */
+static int msa311_get_axis(struct msa311_priv *msa311,
+ const struct iio_chan_spec * const chan,
+ __le16 *axis)
+{
+ struct device *dev = msa311->dev;
+ unsigned int axis_reg;
+
+ if (chan->scan_index < MSA311_SI_X || chan->scan_index > MSA311_SI_Z) {
+ dev_err(dev, "invalid scan_index value [%d]\n",
+ chan->scan_index);
+ return -EINVAL;
+ }
+
+ /* Axes data layout has 2 byte gap for each axis starting from X axis */
+ axis_reg = MSA311_ACC_X_REG + (chan->scan_index << 1);
+
+ return regmap_bulk_read(msa311->regs, axis_reg, axis, sizeof(*axis));
+}
+
+static int msa311_read_raw_data(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2)
+{
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ struct device *dev = msa311->dev;
+ __le16 axis;
+ int err;
+
+ err = pm_runtime_resume_and_get(dev);
+ if (err)
+ return err;
+
+ err = iio_device_claim_direct_mode(indio_dev);
+ if (err)
+ return err;
+
+ mutex_lock(&msa311->lock);
+ err = msa311_get_axis(msa311, chan, &axis);
+ mutex_unlock(&msa311->lock);
+
+ iio_device_release_direct_mode(indio_dev);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ if (err) {
+ dev_err(dev, "can't get axis %s (%pe)\n",
+ chan->datasheet_name, ERR_PTR(err));
+ return err;
+ }
+
+ /*
+ * Axis data format is:
+ * ACC_X = (ACC_X_MSB[7:0] << 4) | ACC_X_LSB[7:4]
+ */
+ *val = sign_extend32(le16_to_cpu(axis) >> chan->scan_type.shift,
+ chan->scan_type.realbits - 1);
+
+ return IIO_VAL_INT;
+}
+
+static int msa311_read_scale(struct iio_dev *indio_dev, int *val, int *val2)
+{
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ struct device *dev = msa311->dev;
+ unsigned int fs;
+ int err;
+
+ mutex_lock(&msa311->lock);
+ err = regmap_field_read(msa311->fields[F_FS], &fs);
+ mutex_unlock(&msa311->lock);
+ if (err) {
+ dev_err(dev, "can't get actual scale (%pe)\n", ERR_PTR(err));
+ return err;
+ }
+
+ *val = msa311_fs_table[fs].integral;
+ *val2 = msa311_fs_table[fs].microfract;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int msa311_read_samp_freq(struct iio_dev *indio_dev,
+ int *val, int *val2)
+{
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ struct device *dev = msa311->dev;
+ unsigned int odr;
+ int err;
+
+ mutex_lock(&msa311->lock);
+ err = msa311_get_odr(msa311, &odr);
+ mutex_unlock(&msa311->lock);
+ if (err) {
+ dev_err(dev, "can't get actual frequency (%pe)\n",
+ ERR_PTR(err));
+ return err;
+ }
+
+ *val = msa311_odr_table[odr].integral;
+ *val2 = msa311_odr_table[odr].microfract;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int msa311_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return msa311_read_raw_data(indio_dev, chan, val, val2);
+
+ case IIO_CHAN_INFO_SCALE:
+ return msa311_read_scale(indio_dev, val, val2);
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return msa311_read_samp_freq(indio_dev, val, val2);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int msa311_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type,
+ int *length, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (int *)msa311_odr_table;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ /* ODR value has 2 ints (integer and fractional parts) */
+ *length = ARRAY_SIZE(msa311_odr_table) * 2;
+ return IIO_AVAIL_LIST;
+
+ case IIO_CHAN_INFO_SCALE:
+ *vals = (int *)msa311_fs_table;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ /* FS value has 2 ints (integer and fractional parts) */
+ *length = ARRAY_SIZE(msa311_fs_table) * 2;
+ return IIO_AVAIL_LIST;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int msa311_write_scale(struct iio_dev *indio_dev, int val, int val2)
+{
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ struct device *dev = msa311->dev;
+ unsigned int fs;
+ int err;
+
+ /* We do not have fs >= 1, so skip such values */
+ if (val)
+ return 0;
+
+ err = pm_runtime_resume_and_get(dev);
+ if (err)
+ return err;
+
+ err = -EINVAL;
+ for (fs = 0; fs < ARRAY_SIZE(msa311_fs_table); fs++)
+ /* Do not check msa311_fs_table[fs].integral, it's always 0 */
+ if (val2 == msa311_fs_table[fs].microfract) {
+ mutex_lock(&msa311->lock);
+ err = regmap_field_write(msa311->fields[F_FS], fs);
+ mutex_unlock(&msa311->lock);
+ break;
+ }
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ if (err)
+ dev_err(dev, "can't update scale (%pe)\n", ERR_PTR(err));
+
+ return err;
+}
+
+static int msa311_write_samp_freq(struct iio_dev *indio_dev, int val, int val2)
+{
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ struct device *dev = msa311->dev;
+ unsigned int odr;
+ int err;
+
+ err = pm_runtime_resume_and_get(dev);
+ if (err)
+ return err;
+
+ /*
+ * Sampling frequency changing is prohibited when buffer mode is
+ * enabled, because sometimes MSA311 chip returns outliers during
+ * frequency values growing up in the read operation moment.
+ */
+ err = iio_device_claim_direct_mode(indio_dev);
+ if (err)
+ return err;
+
+ err = -EINVAL;
+ for (odr = 0; odr < ARRAY_SIZE(msa311_odr_table); odr++)
+ if (val == msa311_odr_table[odr].integral &&
+ val2 == msa311_odr_table[odr].microfract) {
+ mutex_lock(&msa311->lock);
+ err = msa311_set_odr(msa311, odr);
+ mutex_unlock(&msa311->lock);
+ break;
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ if (err)
+ dev_err(dev, "can't update frequency (%pe)\n", ERR_PTR(err));
+
+ return err;
+}
+
+static int msa311_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ return msa311_write_scale(indio_dev, val, val2);
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return msa311_write_samp_freq(indio_dev, val, val2);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int msa311_debugfs_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ struct device *dev = msa311->dev;
+ int err;
+
+ if (reg > regmap_get_max_register(msa311->regs))
+ return -EINVAL;
+
+ err = pm_runtime_resume_and_get(dev);
+ if (err)
+ return err;
+
+ mutex_lock(&msa311->lock);
+
+ if (readval)
+ err = regmap_read(msa311->regs, reg, readval);
+ else
+ err = regmap_write(msa311->regs, reg, writeval);
+
+ mutex_unlock(&msa311->lock);
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ if (err)
+ dev_err(dev, "can't %s register %u from debugfs (%pe)\n",
+ str_read_write(readval), reg, ERR_PTR(err));
+
+ return err;
+}
+
+static int msa311_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ struct device *dev = msa311->dev;
+
+ return pm_runtime_resume_and_get(dev);
+}
+
+static int msa311_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ struct device *dev = msa311->dev;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ return 0;
+}
+
+static int msa311_set_new_data_trig_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ struct device *dev = msa311->dev;
+ int err;
+
+ mutex_lock(&msa311->lock);
+ err = regmap_field_write(msa311->fields[F_NEW_DATA_INT_EN], state);
+ mutex_unlock(&msa311->lock);
+ if (err)
+ dev_err(dev,
+ "can't %s buffer due to new_data_int failure (%pe)\n",
+ str_enable_disable(state), ERR_PTR(err));
+
+ return err;
+}
+
+static int msa311_validate_device(struct iio_trigger *trig,
+ struct iio_dev *indio_dev)
+{
+ return iio_trigger_get_drvdata(trig) == indio_dev ? 0 : -EINVAL;
+}
+
+static irqreturn_t msa311_buffer_thread(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct msa311_priv *msa311 = iio_priv(pf->indio_dev);
+ struct iio_dev *indio_dev = pf->indio_dev;
+ const struct iio_chan_spec *chan;
+ struct device *dev = msa311->dev;
+ int bit, err, i = 0;
+ __le16 axis;
+ struct {
+ __le16 channels[MSA311_SI_Z + 1];
+ s64 ts __aligned(8);
+ } buf;
+
+ memset(&buf, 0, sizeof(buf));
+
+ mutex_lock(&msa311->lock);
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ chan = &msa311_channels[bit];
+
+ err = msa311_get_axis(msa311, chan, &axis);
+ if (err) {
+ mutex_unlock(&msa311->lock);
+ dev_err(dev, "can't get axis %s (%pe)\n",
+ chan->datasheet_name, ERR_PTR(err));
+ goto notify_done;
+ }
+
+ buf.channels[i++] = axis;
+ }
+
+ mutex_unlock(&msa311->lock);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &buf,
+ iio_get_time_ns(indio_dev));
+
+notify_done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t msa311_irq_thread(int irq, void *p)
+{
+ struct msa311_priv *msa311 = iio_priv(p);
+ unsigned int new_data_int_enabled;
+ struct device *dev = msa311->dev;
+ int err;
+
+ mutex_lock(&msa311->lock);
+
+ /*
+ * We do not check NEW_DATA int status, because based on the
+ * specification it's cleared automatically after a fixed time.
+ * So just check that is enabled by driver logic.
+ */
+ err = regmap_field_read(msa311->fields[F_NEW_DATA_INT_EN],
+ &new_data_int_enabled);
+
+ mutex_unlock(&msa311->lock);
+ if (err) {
+ dev_err(dev, "can't read new_data interrupt state (%pe)\n",
+ ERR_PTR(err));
+ return IRQ_NONE;
+ }
+
+ if (new_data_int_enabled)
+ iio_trigger_poll_chained(msa311->new_data_trig);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info msa311_info = {
+ .read_raw = msa311_read_raw,
+ .read_avail = msa311_read_avail,
+ .write_raw = msa311_write_raw,
+ .debugfs_reg_access = msa311_debugfs_reg_access,
+};
+
+static const struct iio_buffer_setup_ops msa311_buffer_setup_ops = {
+ .preenable = msa311_buffer_preenable,
+ .postdisable = msa311_buffer_postdisable,
+};
+
+static const struct iio_trigger_ops msa311_new_data_trig_ops = {
+ .set_trigger_state = msa311_set_new_data_trig_state,
+ .validate_device = msa311_validate_device,
+};
+
+static int msa311_check_partid(struct msa311_priv *msa311)
+{
+ struct device *dev = msa311->dev;
+ unsigned int partid;
+ int err;
+
+ err = regmap_read(msa311->regs, MSA311_PARTID_REG, &partid);
+ if (err)
+ return dev_err_probe(dev, err, "failed to read partid\n");
+
+ if (partid != MSA311_WHO_AM_I)
+ dev_warn(dev, "invalid partid (%#x), expected (%#x)\n",
+ partid, MSA311_WHO_AM_I);
+
+ msa311->chip_name = devm_kasprintf(dev, GFP_KERNEL,
+ "msa311-%02x", partid);
+ if (!msa311->chip_name)
+ return dev_err_probe(dev, -ENOMEM, "can't alloc chip name\n");
+
+ return 0;
+}
+
+static int msa311_soft_reset(struct msa311_priv *msa311)
+{
+ struct device *dev = msa311->dev;
+ int err;
+
+ err = regmap_write(msa311->regs, MSA311_SOFT_RESET_REG,
+ MSA311_GENMASK(F_SOFT_RESET_I2C) |
+ MSA311_GENMASK(F_SOFT_RESET_SPI));
+ if (err)
+ return dev_err_probe(dev, err, "can't soft reset all logic\n");
+
+ return 0;
+}
+
+static int msa311_chip_init(struct msa311_priv *msa311)
+{
+ struct device *dev = msa311->dev;
+ const char zero_bulk[2] = { };
+ int err;
+
+ err = regmap_write(msa311->regs, MSA311_RANGE_REG, MSA311_FS_16G);
+ if (err)
+ return dev_err_probe(dev, err, "failed to setup accel range\n");
+
+ /* Disable all interrupts by default */
+ err = regmap_bulk_write(msa311->regs, MSA311_INT_SET_0_REG,
+ zero_bulk, sizeof(zero_bulk));
+ if (err)
+ return dev_err_probe(dev, err,
+ "can't disable set0/set1 interrupts\n");
+
+ /* Unmap all INT1 interrupts by default */
+ err = regmap_bulk_write(msa311->regs, MSA311_INT_MAP_0_REG,
+ zero_bulk, sizeof(zero_bulk));
+ if (err)
+ return dev_err_probe(dev, err,
+ "failed to unmap map0/map1 interrupts\n");
+
+ /* Disable all axes by default */
+ err = regmap_update_bits(msa311->regs, MSA311_ODR_REG,
+ MSA311_GENMASK(F_X_AXIS_DIS) |
+ MSA311_GENMASK(F_Y_AXIS_DIS) |
+ MSA311_GENMASK(F_Z_AXIS_DIS), 0);
+ if (err)
+ return dev_err_probe(dev, err, "can't enable all axes\n");
+
+ err = msa311_set_odr(msa311, MSA311_ODR_125_HZ);
+ if (err)
+ return dev_err_probe(dev, err,
+ "failed to set accel frequency\n");
+
+ return 0;
+}
+
+static int msa311_setup_interrupts(struct msa311_priv *msa311)
+{
+ struct device *dev = msa311->dev;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct iio_dev *indio_dev = i2c_get_clientdata(i2c);
+ struct iio_trigger *trig;
+ int err;
+
+ /* Keep going without interrupts if no initialized I2C IRQ */
+ if (i2c->irq <= 0)
+ return 0;
+
+ err = devm_request_threaded_irq(&i2c->dev, i2c->irq, NULL,
+ msa311_irq_thread, IRQF_ONESHOT,
+ msa311->chip_name, indio_dev);
+ if (err)
+ return dev_err_probe(dev, err, "failed to request IRQ\n");
+
+ trig = devm_iio_trigger_alloc(dev, "%s-new-data", msa311->chip_name);
+ if (!trig)
+ return dev_err_probe(dev, -ENOMEM,
+ "can't allocate newdata trigger\n");
+
+ msa311->new_data_trig = trig;
+ msa311->new_data_trig->ops = &msa311_new_data_trig_ops;
+ iio_trigger_set_drvdata(msa311->new_data_trig, indio_dev);
+
+ err = devm_iio_trigger_register(dev, msa311->new_data_trig);
+ if (err)
+ return dev_err_probe(dev, err,
+ "can't register newdata trigger\n");
+
+ err = regmap_field_write(msa311->fields[F_INT1_OD],
+ MSA311_INT1_OD_PUSH_PULL);
+ if (err)
+ return dev_err_probe(dev, err,
+ "can't enable push-pull interrupt\n");
+
+ err = regmap_field_write(msa311->fields[F_INT1_LVL],
+ MSA311_INT1_LVL_HIGH);
+ if (err)
+ return dev_err_probe(dev, err,
+ "can't set active interrupt level\n");
+
+ err = regmap_field_write(msa311->fields[F_LATCH_INT],
+ MSA311_LATCH_INT_LATCHED);
+ if (err)
+ return dev_err_probe(dev, err,
+ "can't latch interrupt\n");
+
+ err = regmap_field_write(msa311->fields[F_RESET_INT], 1);
+ if (err)
+ return dev_err_probe(dev, err,
+ "can't reset interrupt\n");
+
+ err = regmap_field_write(msa311->fields[F_INT1_NEW_DATA], 1);
+ if (err)
+ return dev_err_probe(dev, err,
+ "can't map new data interrupt\n");
+
+ return 0;
+}
+
+static int msa311_regmap_init(struct msa311_priv *msa311)
+{
+ struct regmap_field **fields = msa311->fields;
+ struct device *dev = msa311->dev;
+ struct i2c_client *i2c = to_i2c_client(dev);
+ struct regmap *regmap;
+ int i;
+
+ regmap = devm_regmap_init_i2c(i2c, &msa311_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "failed to register i2c regmap\n");
+
+ msa311->regs = regmap;
+
+ for (i = 0; i < F_MAX_FIELDS; i++) {
+ fields[i] = devm_regmap_field_alloc(dev,
+ msa311->regs,
+ msa311_reg_fields[i]);
+ if (IS_ERR(msa311->fields[i]))
+ return dev_err_probe(dev, PTR_ERR(msa311->fields[i]),
+ "can't alloc field[%d]\n", i);
+ }
+
+ return 0;
+}
+
+static void msa311_powerdown(void *msa311)
+{
+ msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_SUSPEND);
+}
+
+static void msa311_vdd_disable(void *vdd)
+{
+ regulator_disable(vdd);
+}
+
+static int msa311_probe(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct msa311_priv *msa311;
+ struct iio_dev *indio_dev;
+ int err;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*msa311));
+ if (!indio_dev)
+ return dev_err_probe(dev, -ENOMEM,
+ "IIO device allocation failed\n");
+
+ msa311 = iio_priv(indio_dev);
+ msa311->dev = dev;
+ i2c_set_clientdata(i2c, indio_dev);
+
+ err = msa311_regmap_init(msa311);
+ if (err)
+ return err;
+
+ mutex_init(&msa311->lock);
+
+ msa311->vdd = devm_regulator_get(dev, "vdd");
+ if (IS_ERR(msa311->vdd))
+ return dev_err_probe(dev, PTR_ERR(msa311->vdd),
+ "can't get vdd supply\n");
+
+ err = regulator_enable(msa311->vdd);
+ if (err)
+ return dev_err_probe(dev, err, "can't enable vdd supply\n");
+
+ err = devm_add_action_or_reset(dev, msa311_vdd_disable, msa311->vdd);
+ if (err)
+ return dev_err_probe(dev, err,
+ "can't add vdd disable action\n");
+
+ err = msa311_check_partid(msa311);
+ if (err)
+ return err;
+
+ err = msa311_soft_reset(msa311);
+ if (err)
+ return err;
+
+ err = msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_NORMAL);
+ if (err)
+ return dev_err_probe(dev, err, "failed to power on device\n");
+
+ /*
+ * Register powerdown deferred callback which suspends the chip
+ * after module unloaded.
+ *
+ * MSA311 should be in SUSPEND mode in the two cases:
+ * 1) When driver is loaded, but we do not have any data or
+ * configuration requests to it (we are solving it using
+ * autosuspend feature).
+ * 2) When driver is unloaded and device is not used (devm action is
+ * used in this case).
+ */
+ err = devm_add_action_or_reset(dev, msa311_powerdown, msa311);
+ if (err)
+ return dev_err_probe(dev, err, "can't add powerdown action\n");
+
+ err = pm_runtime_set_active(dev);
+ if (err)
+ return err;
+
+ err = devm_pm_runtime_enable(dev);
+ if (err)
+ return err;
+
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_autosuspend_delay(dev, MSA311_PWR_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+
+ err = msa311_chip_init(msa311);
+ if (err)
+ return err;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = msa311_channels;
+ indio_dev->num_channels = ARRAY_SIZE(msa311_channels);
+ indio_dev->name = msa311->chip_name;
+ indio_dev->info = &msa311_info;
+
+ err = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ msa311_buffer_thread,
+ &msa311_buffer_setup_ops);
+ if (err)
+ return dev_err_probe(dev, err,
+ "can't setup IIO trigger buffer\n");
+
+ err = msa311_setup_interrupts(msa311);
+ if (err)
+ return err;
+
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
+ err = devm_iio_device_register(dev, indio_dev);
+ if (err)
+ return dev_err_probe(dev, err, "IIO device register failed\n");
+
+ return 0;
+}
+
+static int msa311_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ int err;
+
+ mutex_lock(&msa311->lock);
+ err = msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_SUSPEND);
+ mutex_unlock(&msa311->lock);
+ if (err)
+ dev_err(dev, "failed to power off device (%pe)\n",
+ ERR_PTR(err));
+
+ return err;
+}
+
+static int msa311_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct msa311_priv *msa311 = iio_priv(indio_dev);
+ int err;
+
+ mutex_lock(&msa311->lock);
+ err = msa311_set_pwr_mode(msa311, MSA311_PWR_MODE_NORMAL);
+ mutex_unlock(&msa311->lock);
+ if (err)
+ dev_err(dev, "failed to power on device (%pe)\n",
+ ERR_PTR(err));
+
+ return err;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(msa311_pm_ops, msa311_runtime_suspend,
+ msa311_runtime_resume, NULL);
+
+static const struct i2c_device_id msa311_i2c_id[] = {
+ { .name = "msa311" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, msa311_i2c_id);
+
+static const struct of_device_id msa311_of_match[] = {
+ { .compatible = "memsensing,msa311" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, msa311_of_match);
+
+static struct i2c_driver msa311_driver = {
+ .driver = {
+ .name = "msa311",
+ .of_match_table = msa311_of_match,
+ .pm = pm_ptr(&msa311_pm_ops),
+ },
+ .probe_new = msa311_probe,
+ .id_table = msa311_i2c_id,
+};
+module_i2c_driver(msa311_driver);
+
+MODULE_AUTHOR("Dmitry Rokosov <ddrokosov@sberdevices.ru>");
+MODULE_DESCRIPTION("MEMSensing MSA311 3-axis accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 7fe5930891e0..791612ca6012 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -653,6 +653,20 @@ config MAX1118
To compile this driver as a module, choose M here: the module will be
called max1118.
+config MAX11205
+ tristate "Maxim max11205 ADC driver"
+ depends on SPI
+ select AD_SIGMA_DELTA
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+ help
+ Say yes here to build support for Maxim max11205 16-bit, single-channel
+ ultra-low power delta-sigma ADC.
+
+ To compile this driver as a module, choose M here: the module will be
+ called max11205.
+
config MAX1241
tristate "Maxim max1241 ADC driver"
depends on SPI_MASTER
@@ -718,6 +732,8 @@ config MCP3422
config MCP3911
tristate "Microchip Technology MCP3911 driver"
depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
help
Say yes here to build support for Microchip Technology's MCP3911
analog to digital converter.
@@ -919,6 +935,21 @@ config ROCKCHIP_SARADC
To compile this driver as a module, choose M here: the
module will be called rockchip_saradc.
+config RICHTEK_RTQ6056
+ tristate "Richtek RTQ6056 Current and Power Monitor ADC"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to enable RQT6056 ADC support.
+ RTQ6056 is a high accuracy current-sense monitor with I2C and SMBus
+ compatible interface, and the device provides full information for
+ system by reading out the load current and power.
+
+ This driver can also be built as a module. If so, the module will be
+ called rtq6056.
+
config RZG2L_ADC
tristate "Renesas RZ/G2L ADC driver"
depends on ARCH_RZG2L || COMPILE_TEST
@@ -1022,22 +1053,6 @@ config STMPE_ADC
Say yes here to build support for ST Microelectronics STMPE
built-in ADC block (stmpe811).
-config STX104
- tristate "Apex Embedded Systems STX104 driver"
- depends on PC104 && X86
- select ISA_BUS_API
- select GPIOLIB
- help
- Say yes here to build support for the Apex Embedded Systems STX104
- integrated analog PC/104 card.
-
- This driver supports the 16 channels of single-ended (8 channels of
- differential) analog inputs, 2 channels of analog output, 4 digital
- inputs, and 4 digital outputs provided by the STX104.
-
- The base port addresses for the devices may be configured via the base
- array module parameter.
-
config SUN4I_GPADC
tristate "Support for the Allwinner SoCs GPADC"
depends on IIO
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 1772a549a3c8..46caba7a010c 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_LTC2497) += ltc2497.o ltc2497-core.o
obj-$(CONFIG_MAX1027) += max1027.o
obj-$(CONFIG_MAX11100) += max11100.o
obj-$(CONFIG_MAX1118) += max1118.o
+obj-$(CONFIG_MAX11205) += max11205.o
obj-$(CONFIG_MAX1241) += max1241.o
obj-$(CONFIG_MAX1363) += max1363.o
obj-$(CONFIG_MAX9611) += max9611.o
@@ -85,10 +86,10 @@ obj-$(CONFIG_QCOM_PM8XXX_XOADC) += qcom-pm8xxx-xoadc.o
obj-$(CONFIG_RCAR_GYRO_ADC) += rcar-gyroadc.o
obj-$(CONFIG_RN5T618_ADC) += rn5t618-adc.o
obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
+obj-$(CONFIG_RICHTEK_RTQ6056) += rtq6056.o
obj-$(CONFIG_RZG2L_ADC) += rzg2l_adc.o
obj-$(CONFIG_SC27XX_ADC) += sc27xx_adc.o
obj-$(CONFIG_SPEAR_ADC) += spear_adc.o
-obj-$(CONFIG_STX104) += stx104.o
obj-$(CONFIG_SUN4I_GPADC) += sun4i-gpadc-iio.o
obj-$(CONFIG_STM32_ADC_CORE) += stm32-adc-core.o
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
diff --git a/drivers/iio/adc/ab8500-gpadc.c b/drivers/iio/adc/ab8500-gpadc.c
index 930ce96e6ff5..4fa2126a354b 100644
--- a/drivers/iio/adc/ab8500-gpadc.c
+++ b/drivers/iio/adc/ab8500-gpadc.c
@@ -925,8 +925,8 @@ static int ab8500_gpadc_read_raw(struct iio_dev *indio_dev,
return -EINVAL;
}
-static int ab8500_gpadc_of_xlate(struct iio_dev *indio_dev,
- const struct of_phandle_args *iiospec)
+static int ab8500_gpadc_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
{
int i;
@@ -938,7 +938,7 @@ static int ab8500_gpadc_of_xlate(struct iio_dev *indio_dev,
}
static const struct iio_info ab8500_gpadc_info = {
- .of_xlate = ab8500_gpadc_of_xlate,
+ .fwnode_xlate = ab8500_gpadc_fwnode_xlate,
.read_raw = ab8500_gpadc_read_raw,
};
@@ -968,7 +968,7 @@ static int ab8500_gpadc_runtime_resume(struct device *dev)
/**
* ab8500_gpadc_parse_channel() - process devicetree channel configuration
* @dev: pointer to containing device
- * @np: device tree node for the channel to configure
+ * @fwnode: fw node for the channel to configure
* @ch: channel info to fill in
* @iio_chan: IIO channel specification to fill in
*
@@ -976,15 +976,15 @@ static int ab8500_gpadc_runtime_resume(struct device *dev)
* and define usage for things like AUX GPADC inputs more precisely.
*/
static int ab8500_gpadc_parse_channel(struct device *dev,
- struct device_node *np,
+ struct fwnode_handle *fwnode,
struct ab8500_gpadc_chan_info *ch,
struct iio_chan_spec *iio_chan)
{
- const char *name = np->name;
+ const char *name = fwnode_get_name(fwnode);
u32 chan;
int ret;
- ret = of_property_read_u32(np, "reg", &chan);
+ ret = fwnode_property_read_u32(fwnode, "reg", &chan);
if (ret) {
dev_err(dev, "invalid channel number %s\n", name);
return ret;
@@ -1021,22 +1021,20 @@ static int ab8500_gpadc_parse_channel(struct device *dev,
/**
* ab8500_gpadc_parse_channels() - Parse the GPADC channels from DT
* @gpadc: the GPADC to configure the channels for
- * @np: device tree node containing the channel configurations
* @chans: the IIO channels we parsed
* @nchans: the number of IIO channels we parsed
*/
static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
- struct device_node *np,
struct iio_chan_spec **chans_parsed,
unsigned int *nchans_parsed)
{
- struct device_node *child;
+ struct fwnode_handle *child;
struct ab8500_gpadc_chan_info *ch;
struct iio_chan_spec *iio_chans;
unsigned int nchans;
int i;
- nchans = of_get_available_child_count(np);
+ nchans = device_get_child_node_count(gpadc->dev);
if (!nchans) {
dev_err(gpadc->dev, "no channel children\n");
return -ENODEV;
@@ -1054,7 +1052,7 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
return -ENOMEM;
i = 0;
- for_each_available_child_of_node(np, child) {
+ device_for_each_child_node(gpadc->dev, child) {
struct iio_chan_spec *iio_chan;
int ret;
@@ -1064,7 +1062,7 @@ static int ab8500_gpadc_parse_channels(struct ab8500_gpadc *gpadc,
ret = ab8500_gpadc_parse_channel(gpadc->dev, child, ch,
iio_chan);
if (ret) {
- of_node_put(child);
+ fwnode_handle_put(child);
return ret;
}
i++;
@@ -1081,7 +1079,6 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
struct ab8500_gpadc *gpadc;
struct iio_dev *indio_dev;
struct device *dev = &pdev->dev;
- struct device_node *np = pdev->dev.of_node;
struct iio_chan_spec *iio_chans;
unsigned int n_iio_chans;
int ret;
@@ -1096,7 +1093,7 @@ static int ab8500_gpadc_probe(struct platform_device *pdev)
gpadc->dev = dev;
gpadc->ab8500 = dev_get_drvdata(dev->parent);
- ret = ab8500_gpadc_parse_channels(gpadc, np, &iio_chans, &n_iio_chans);
+ ret = ab8500_gpadc_parse_channels(gpadc, &iio_chans, &n_iio_chans);
if (ret)
return ret;
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
index c5b785d8b241..4088786e1026 100644
--- a/drivers/iio/adc/ad7124.c
+++ b/drivers/iio/adc/ad7124.c
@@ -936,11 +936,6 @@ 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;
@@ -993,18 +988,10 @@ static int ad7124_probe(struct spi_device *spi)
return ret;
}
- st->mclk = devm_clk_get(&spi->dev, "mclk");
+ st->mclk = devm_clk_get_enabled(&spi->dev, "mclk");
if (IS_ERR(st->mclk))
return PTR_ERR(st->mclk);
- ret = clk_prepare_enable(st->mclk);
- 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)
return ret;
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
index 652db768ef37..70a25949142c 100644
--- a/drivers/iio/adc/ad7768-1.c
+++ b/drivers/iio/adc/ad7768-1.c
@@ -539,13 +539,6 @@ static void ad7768_regulator_disable(void *data)
regulator_disable(st->vref);
}
-static void ad7768_clk_disable(void *data)
-{
- struct ad7768_state *st = data;
-
- clk_disable_unprepare(st->mclk);
-}
-
static int ad7768_set_channel_label(struct iio_dev *indio_dev,
int num_channels)
{
@@ -600,18 +593,10 @@ static int ad7768_probe(struct spi_device *spi)
if (ret)
return ret;
- st->mclk = devm_clk_get(&spi->dev, "mclk");
+ st->mclk = devm_clk_get_enabled(&spi->dev, "mclk");
if (IS_ERR(st->mclk))
return PTR_ERR(st->mclk);
- ret = clk_prepare_enable(st->mclk);
- if (ret < 0)
- return ret;
-
- ret = devm_add_action_or_reset(&spi->dev, ad7768_clk_disable, st);
- if (ret)
- return ret;
-
st->mclk_freq = clk_get_rate(st->mclk);
mutex_init(&st->lock);
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
index edad1f30121d..9d6bf6d0927a 100644
--- a/drivers/iio/adc/ad7923.c
+++ b/drivers/iio/adc/ad7923.c
@@ -8,6 +8,7 @@
#include <linux/device.h>
#include <linux/kernel.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
@@ -93,6 +94,7 @@ enum ad7923_id {
.sign = 'u', \
.realbits = (bits), \
.storagebits = 16, \
+ .shift = 12 - (bits), \
.endianness = IIO_BE, \
}, \
}
@@ -268,7 +270,8 @@ static int ad7923_read_raw(struct iio_dev *indio_dev,
return ret;
if (chan->address == EXTRACT(ret, 12, 4))
- *val = EXTRACT(ret, 0, 12);
+ *val = EXTRACT(ret, chan->scan_type.shift,
+ chan->scan_type.realbits);
else
return -EIO;
@@ -298,6 +301,7 @@ static void ad7923_regulator_disable(void *data)
static int ad7923_probe(struct spi_device *spi)
{
+ u32 ad7923_range = AD7923_RANGE;
struct ad7923_state *st;
struct iio_dev *indio_dev;
const struct ad7923_chip_info *info;
@@ -309,8 +313,11 @@ static int ad7923_probe(struct spi_device *spi)
st = iio_priv(indio_dev);
+ if (device_property_read_bool(&spi->dev, "adi,range-double"))
+ ad7923_range = 0;
+
st->spi = spi;
- st->settings = AD7923_CODING | AD7923_RANGE |
+ st->settings = AD7923_CODING | ad7923_range |
AD7923_PM_MODE_WRITE(AD7923_PM_MODE_OPS);
info = &ad7923_chip_info[spi_get_device_id(spi)->driver_data];
diff --git a/drivers/iio/adc/ad9467.c b/drivers/iio/adc/ad9467.c
index 5a5f33f7bc8f..7534572f7475 100644
--- a/drivers/iio/adc/ad9467.c
+++ b/drivers/iio/adc/ad9467.c
@@ -378,13 +378,6 @@ static int ad9467_preenable_setup(struct adi_axi_adc_conv *conv)
return ad9467_outputmode_set(st->spi, st->output_mode);
}
-static void ad9467_clk_disable(void *data)
-{
- struct ad9467_state *st = data;
-
- clk_disable_unprepare(st->clk);
-}
-
static int ad9467_probe(struct spi_device *spi)
{
const struct ad9467_chip_info *info;
@@ -404,18 +397,10 @@ static int ad9467_probe(struct spi_device *spi)
st = adi_axi_adc_conv_priv(conv);
st->spi = spi;
- st->clk = devm_clk_get(&spi->dev, "adc-clk");
+ st->clk = devm_clk_get_enabled(&spi->dev, "adc-clk");
if (IS_ERR(st->clk))
return PTR_ERR(st->clk);
- ret = clk_prepare_enable(st->clk);
- if (ret < 0)
- return ret;
-
- ret = devm_add_action_or_reset(&spi->dev, ad9467_clk_disable, st);
- if (ret)
- return ret;
-
st->pwrdown_gpio = devm_gpiod_get_optional(&spi->dev, "powerdown",
GPIOD_OUT_LOW);
if (IS_ERR(st->pwrdown_gpio))
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
index 279430c1d88c..4294d6539cdb 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -16,9 +16,11 @@
#include <linux/interrupt.h>
#include <linux/io.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/sched.h>
+#include <linux/units.h>
#include <linux/wait.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -26,9 +28,13 @@
#include <linux/iio/trigger.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
+#include <linux/nvmem-consumer.h>
#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
+#include <dt-bindings/iio/adc/at91-sama5d2_adc.h>
+
struct at91_adc_reg_layout {
/* Control Register */
u16 CR;
@@ -73,11 +79,14 @@ struct at91_adc_reg_layout {
/* Startup Time */
#define AT91_SAMA5D2_MR_STARTUP(v) ((v) << 16)
#define AT91_SAMA5D2_MR_STARTUP_MASK GENMASK(19, 16)
+/* Minimum startup time for temperature sensor */
+#define AT91_SAMA5D2_MR_STARTUP_TS_MIN (50)
/* Analog Change */
#define AT91_SAMA5D2_MR_ANACH BIT(23)
/* Tracking Time */
#define AT91_SAMA5D2_MR_TRACKTIM(v) ((v) << 24)
-#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xff
+#define AT91_SAMA5D2_MR_TRACKTIM_TS 6
+#define AT91_SAMA5D2_MR_TRACKTIM_MAX 0xf
/* Transfer Time */
#define AT91_SAMA5D2_MR_TRANSFER(v) ((v) << 28)
#define AT91_SAMA5D2_MR_TRANSFER_MAX 0x3
@@ -138,11 +147,19 @@ struct at91_adc_reg_layout {
/* Extended Mode Register */
u16 EMR;
/* Extended Mode Register - Oversampling rate */
-#define AT91_SAMA5D2_EMR_OSR(V) ((V) << 16)
-#define AT91_SAMA5D2_EMR_OSR_MASK GENMASK(17, 16)
+#define AT91_SAMA5D2_EMR_OSR(V, M) (((V) << 16) & (M))
#define AT91_SAMA5D2_EMR_OSR_1SAMPLES 0
#define AT91_SAMA5D2_EMR_OSR_4SAMPLES 1
#define AT91_SAMA5D2_EMR_OSR_16SAMPLES 2
+#define AT91_SAMA5D2_EMR_OSR_64SAMPLES 3
+#define AT91_SAMA5D2_EMR_OSR_256SAMPLES 4
+
+/* Extended Mode Register - TRACKX */
+#define AT91_SAMA5D2_TRACKX_MASK GENMASK(23, 22)
+#define AT91_SAMA5D2_TRACKX(x) (((x) << 22) & \
+ AT91_SAMA5D2_TRACKX_MASK)
+/* TRACKX for temperature sensor. */
+#define AT91_SAMA5D2_TRACKX_TS (1)
/* Extended Mode Register - Averaging on single trigger event */
#define AT91_SAMA5D2_EMR_ASTE(V) ((V) << 20)
@@ -159,6 +176,8 @@ struct at91_adc_reg_layout {
u16 ACR;
/* Analog Control Register - Pen detect sensitivity mask */
#define AT91_SAMA5D2_ACR_PENDETSENS_MASK GENMASK(1, 0)
+/* Analog Control Register - Source last channel */
+#define AT91_SAMA5D2_ACR_SRCLCH BIT(16)
/* Touchscreen Mode Register */
u16 TSMR;
@@ -226,6 +245,10 @@ struct at91_adc_reg_layout {
u16 WPSR;
/* Version Register */
u16 VERSION;
+/* Temperature Sensor Mode Register */
+ u16 TEMPMR;
+/* Temperature Sensor Mode - Temperature sensor on */
+#define AT91_SAMA5D2_TEMPMR_TEMPON BIT(0)
};
static const struct at91_adc_reg_layout sama5d2_layout = {
@@ -280,6 +303,7 @@ static const struct at91_adc_reg_layout sama7g5_layout = {
.EOC_IDR = 0x38,
.EOC_IMR = 0x3c,
.EOC_ISR = 0x40,
+ .TEMPMR = 0x44,
.OVER = 0x4c,
.EMR = 0x50,
.CWR = 0x54,
@@ -305,11 +329,6 @@ static const struct at91_adc_reg_layout sama7g5_layout = {
#define AT91_HWFIFO_MAX_SIZE_STR "128"
#define AT91_HWFIFO_MAX_SIZE 128
-/* Possible values for oversampling ratio */
-#define AT91_OSR_1SAMPLES 1
-#define AT91_OSR_4SAMPLES 4
-#define AT91_OSR_16SAMPLES 16
-
#define AT91_SAMA5D2_CHAN_SINGLE(index, num, addr) \
{ \
.type = IIO_VOLTAGE, \
@@ -325,6 +344,8 @@ static const struct at91_adc_reg_layout sama7g5_layout = {
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.datasheet_name = "CH"#num, \
.indexed = 1, \
}
@@ -346,6 +367,8 @@ static const struct at91_adc_reg_layout sama7g5_layout = {
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.datasheet_name = "CH"#num"-CH"#num2, \
.indexed = 1, \
}
@@ -365,6 +388,8 @@ static const struct at91_adc_reg_layout sama7g5_layout = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.datasheet_name = name, \
}
#define AT91_SAMA5D2_CHAN_PRESSURE(num, name) \
@@ -380,6 +405,23 @@ static const struct at91_adc_reg_layout sama7g5_layout = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ)|\
BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .datasheet_name = name, \
+ }
+
+#define AT91_SAMA5D2_CHAN_TEMP(num, name, addr) \
+ { \
+ .type = IIO_TEMP, \
+ .channel = num, \
+ .address = addr, \
+ .scan_index = num, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .info_mask_shared_by_all = \
+ BIT(IIO_CHAN_INFO_PROCESSED) | \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .info_mask_shared_by_all_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.datasheet_name = name, \
}
@@ -403,6 +445,12 @@ static const struct at91_adc_reg_layout sama7g5_layout = {
* @max_index: highest channel index (highest index may be higher
* than the total channel number)
* @hw_trig_cnt: number of possible hardware triggers
+ * @osr_mask: oversampling ratio bitmask on EMR register
+ * @oversampling_avail: available oversampling values
+ * @oversampling_avail_no: number of available oversampling values
+ * @chan_realbits: realbits for registered channels
+ * @temp_chan: temperature channel index
+ * @temp_sensor: temperature sensor supported
*/
struct at91_adc_platform {
const struct at91_adc_reg_layout *layout;
@@ -414,20 +462,58 @@ struct at91_adc_platform {
unsigned int max_channels;
unsigned int max_index;
unsigned int hw_trig_cnt;
+ unsigned int osr_mask;
+ unsigned int oversampling_avail[5];
+ unsigned int oversampling_avail_no;
+ unsigned int chan_realbits;
+ unsigned int temp_chan;
+ bool temp_sensor;
+};
+
+/**
+ * struct at91_adc_temp_sensor_clb - at91-sama5d2 temperature sensor
+ * calibration data structure
+ * @p1: P1 calibration temperature
+ * @p4: P4 calibration voltage
+ * @p6: P6 calibration voltage
+ */
+struct at91_adc_temp_sensor_clb {
+ u32 p1;
+ u32 p4;
+ u32 p6;
+};
+
+/**
+ * enum at91_adc_ts_clb_idx - calibration indexes in NVMEM buffer
+ * @AT91_ADC_TS_CLB_IDX_P1: index for P1
+ * @AT91_ADC_TS_CLB_IDX_P4: index for P4
+ * @AT91_ADC_TS_CLB_IDX_P6: index for P6
+ * @AT91_ADC_TS_CLB_IDX_MAX: max index for temperature calibration packet in OTP
+ */
+enum at91_adc_ts_clb_idx {
+ AT91_ADC_TS_CLB_IDX_P1 = 2,
+ AT91_ADC_TS_CLB_IDX_P4 = 5,
+ AT91_ADC_TS_CLB_IDX_P6 = 7,
+ AT91_ADC_TS_CLB_IDX_MAX = 19,
};
+/* Temperature sensor calibration - Vtemp voltage sensitivity to temperature. */
+#define AT91_ADC_TS_VTEMP_DT (2080U)
+
/**
* struct at91_adc_soc_info - at91-sama5d2 soc information struct
* @startup_time: device startup time
* @min_sample_rate: minimum sample rate in Hz
* @max_sample_rate: maximum sample rate in Hz
* @platform: pointer to the platform structure
+ * @temp_sensor_clb: temperature sensor calibration data structure
*/
struct at91_adc_soc_info {
unsigned startup_time;
unsigned min_sample_rate;
unsigned max_sample_rate;
const struct at91_adc_platform *platform;
+ struct at91_adc_temp_sensor_clb temp_sensor_clb;
};
struct at91_adc_trigger {
@@ -475,6 +561,18 @@ struct at91_adc_touch {
struct work_struct workq;
};
+/**
+ * struct at91_adc_temp - at91-sama5d2 temperature information structure
+ * @sample_period_val: sample period value
+ * @saved_sample_rate: saved sample rate
+ * @saved_oversampling: saved oversampling
+ */
+struct at91_adc_temp {
+ u16 sample_period_val;
+ u16 saved_sample_rate;
+ u16 saved_oversampling;
+};
+
/*
* Buffer size requirements:
* No channels * bytes_per_channel(2) + timestamp bytes (8)
@@ -502,7 +600,9 @@ struct at91_adc_state {
wait_queue_head_t wq_data_available;
struct at91_adc_dma dma_st;
struct at91_adc_touch touch_st;
+ struct at91_adc_temp temp_st;
struct iio_dev *indio_dev;
+ struct device *dev;
/* Ensure naturally aligned timestamp */
u16 buffer[AT91_BUFFER_MAX_HWORDS] __aligned(8);
/*
@@ -591,6 +691,7 @@ static const struct iio_chan_spec at91_sama7g5_adc_channels[] = {
AT91_SAMA5D2_CHAN_DIFF(22, 12, 13, 0x90),
AT91_SAMA5D2_CHAN_DIFF(23, 14, 15, 0x98),
IIO_CHAN_SOFT_TIMESTAMP(24),
+ AT91_SAMA5D2_CHAN_TEMP(AT91_SAMA7G5_ADC_TEMP_CHANNEL, "temp", 0xdc),
};
static const struct at91_adc_platform sama5d2_platform = {
@@ -612,6 +713,10 @@ static const struct at91_adc_platform sama5d2_platform = {
.max_index = AT91_SAMA5D2_MAX_CHAN_IDX,
#define AT91_SAMA5D2_HW_TRIG_CNT 3
.hw_trig_cnt = AT91_SAMA5D2_HW_TRIG_CNT,
+ .osr_mask = GENMASK(17, 16),
+ .oversampling_avail = { 1, 4, 16, },
+ .oversampling_avail_no = 3,
+ .chan_realbits = 14,
};
static const struct at91_adc_platform sama7g5_platform = {
@@ -619,14 +724,23 @@ static const struct at91_adc_platform sama7g5_platform = {
.adc_channels = &at91_sama7g5_adc_channels,
#define AT91_SAMA7G5_SINGLE_CHAN_CNT 16
#define AT91_SAMA7G5_DIFF_CHAN_CNT 8
+#define AT91_SAMA7G5_TEMP_CHAN_CNT 1
.nr_channels = AT91_SAMA7G5_SINGLE_CHAN_CNT +
- AT91_SAMA7G5_DIFF_CHAN_CNT,
+ AT91_SAMA7G5_DIFF_CHAN_CNT +
+ AT91_SAMA7G5_TEMP_CHAN_CNT,
#define AT91_SAMA7G5_MAX_CHAN_IDX (AT91_SAMA7G5_SINGLE_CHAN_CNT + \
- AT91_SAMA7G5_DIFF_CHAN_CNT)
+ AT91_SAMA7G5_DIFF_CHAN_CNT + \
+ AT91_SAMA7G5_TEMP_CHAN_CNT)
.max_channels = ARRAY_SIZE(at91_sama7g5_adc_channels),
.max_index = AT91_SAMA7G5_MAX_CHAN_IDX,
#define AT91_SAMA7G5_HW_TRIG_CNT 3
.hw_trig_cnt = AT91_SAMA7G5_HW_TRIG_CNT,
+ .osr_mask = GENMASK(18, 16),
+ .oversampling_avail = { 1, 4, 16, 64, 256, },
+ .oversampling_avail_no = 5,
+ .chan_realbits = 16,
+ .temp_sensor = true,
+ .temp_chan = AT91_SAMA7G5_ADC_TEMP_CHANNEL,
};
static int at91_adc_chan_xlate(struct iio_dev *indio_dev, int chan)
@@ -650,8 +764,8 @@ at91_adc_chan_get(struct iio_dev *indio_dev, int chan)
return indio_dev->channels + index;
}
-static inline int at91_adc_of_xlate(struct iio_dev *indio_dev,
- const struct of_phandle_args *iiospec)
+static inline int at91_adc_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
{
return at91_adc_chan_xlate(indio_dev, iiospec->args[0]);
}
@@ -725,51 +839,91 @@ static void at91_adc_eoc_ena(struct at91_adc_state *st, unsigned int channel)
at91_adc_writel(st, EOC_IER, BIT(channel));
}
-static void at91_adc_config_emr(struct at91_adc_state *st)
+static int at91_adc_config_emr(struct at91_adc_state *st,
+ u32 oversampling_ratio, u32 trackx)
{
/* configure the extended mode register */
- unsigned int emr = at91_adc_readl(st, EMR);
-
- /* select oversampling per single trigger event */
- emr |= AT91_SAMA5D2_EMR_ASTE(1);
+ unsigned int emr, osr;
+ unsigned int osr_mask = st->soc_info.platform->osr_mask;
+ int i, ret;
- /* delete leftover content if it's the case */
- emr &= ~AT91_SAMA5D2_EMR_OSR_MASK;
+ /* Check against supported oversampling values. */
+ for (i = 0; i < st->soc_info.platform->oversampling_avail_no; i++) {
+ if (oversampling_ratio == st->soc_info.platform->oversampling_avail[i])
+ break;
+ }
+ if (i == st->soc_info.platform->oversampling_avail_no)
+ return -EINVAL;
/* select oversampling ratio from configuration */
- switch (st->oversampling_ratio) {
- case AT91_OSR_1SAMPLES:
- emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES) &
- AT91_SAMA5D2_EMR_OSR_MASK;
+ switch (oversampling_ratio) {
+ case 1:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_1SAMPLES,
+ osr_mask);
break;
- case AT91_OSR_4SAMPLES:
- emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES) &
- AT91_SAMA5D2_EMR_OSR_MASK;
+ case 4:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_4SAMPLES,
+ osr_mask);
break;
- case AT91_OSR_16SAMPLES:
- emr |= AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES) &
- AT91_SAMA5D2_EMR_OSR_MASK;
+ case 16:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_16SAMPLES,
+ osr_mask);
+ break;
+ case 64:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_64SAMPLES,
+ osr_mask);
+ break;
+ case 256:
+ osr = AT91_SAMA5D2_EMR_OSR(AT91_SAMA5D2_EMR_OSR_256SAMPLES,
+ osr_mask);
break;
}
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
+ emr = at91_adc_readl(st, EMR);
+ /* select oversampling per single trigger event */
+ emr |= AT91_SAMA5D2_EMR_ASTE(1);
+ /* delete leftover content if it's the case */
+ emr &= ~(osr_mask | AT91_SAMA5D2_TRACKX_MASK);
+ /* Update osr and trackx. */
+ emr |= osr | AT91_SAMA5D2_TRACKX(trackx);
at91_adc_writel(st, EMR, emr);
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
+ st->oversampling_ratio = oversampling_ratio;
+
+ return 0;
}
static int at91_adc_adjust_val_osr(struct at91_adc_state *st, int *val)
{
- if (st->oversampling_ratio == AT91_OSR_1SAMPLES) {
- /*
- * in this case we only have 12 bits of real data, but channel
- * is registered as 14 bits, so shift left two bits
- */
- *val <<= 2;
- } else if (st->oversampling_ratio == AT91_OSR_4SAMPLES) {
- /*
- * in this case we have 13 bits of real data, but channel
- * is registered as 14 bits, so left shift one bit
- */
- *val <<= 1;
- }
+ int nbits, diff;
+
+ if (st->oversampling_ratio == 1)
+ nbits = 12;
+ else if (st->oversampling_ratio == 4)
+ nbits = 13;
+ else if (st->oversampling_ratio == 16)
+ nbits = 14;
+ else if (st->oversampling_ratio == 64)
+ nbits = 15;
+ else if (st->oversampling_ratio == 256)
+ nbits = 16;
+ else
+ /* Should not happen. */
+ return -EINVAL;
+
+ /*
+ * We have nbits of real data and channel is registered as
+ * st->soc_info.platform->chan_realbits, so shift left diff bits.
+ */
+ diff = st->soc_info.platform->chan_realbits - nbits;
+ *val <<= diff;
return IIO_VAL_INT;
}
@@ -799,15 +953,22 @@ static void at91_adc_adjust_val_osr_array(struct at91_adc_state *st, void *buf,
static int at91_adc_configure_touch(struct at91_adc_state *st, bool state)
{
u32 clk_khz = st->current_sample_rate / 1000;
- int i = 0;
+ int i = 0, ret;
u16 pendbc;
u32 tsmr, acr;
- if (!state) {
+ if (state) {
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+ } else {
/* disabling touch IRQs and setting mode to no touch enabled */
at91_adc_writel(st, IDR,
AT91_SAMA5D2_IER_PEN | AT91_SAMA5D2_IER_NOPEN);
at91_adc_writel(st, TSMR, 0);
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
return 0;
}
/*
@@ -948,10 +1109,9 @@ static int at91_adc_read_pressure(struct at91_adc_state *st, int chan, u16 *val)
return IIO_VAL_INT;
}
-static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
+static void at91_adc_configure_trigger_registers(struct at91_adc_state *st,
+ bool state)
{
- struct iio_dev *indio = iio_trigger_get_drvdata(trig);
- struct at91_adc_state *st = iio_priv(indio);
u32 status = at91_adc_readl(st, TRGR);
/* clear TRGMOD */
@@ -962,6 +1122,26 @@ static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
/* set/unset hw trigger */
at91_adc_writel(st, TRGR, status);
+}
+
+static int at91_adc_configure_trigger(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio = iio_trigger_get_drvdata(trig);
+ struct at91_adc_state *st = iio_priv(indio);
+ int ret;
+
+ if (state) {
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ at91_adc_configure_trigger_registers(st, state);
+
+ if (!state) {
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+ }
return 0;
}
@@ -1120,11 +1300,15 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev)
if (!(iio_device_get_current_mode(indio_dev) & INDIO_ALL_TRIGGERED_MODES))
return -EINVAL;
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
/* we continue with the triggered buffer */
ret = at91_adc_dma_start(indio_dev);
if (ret) {
dev_err(&indio_dev->dev, "buffer prepare failed\n");
- return ret;
+ goto pm_runtime_put;
}
for_each_set_bit(bit, indio_dev->active_scan_mask,
@@ -1135,7 +1319,8 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev)
continue;
/* these channel types cannot be handled by this trigger */
if (chan->type == IIO_POSITIONRELATIVE ||
- chan->type == IIO_PRESSURE)
+ chan->type == IIO_PRESSURE ||
+ chan->type == IIO_TEMP)
continue;
at91_adc_cor(st, chan);
@@ -1146,12 +1331,16 @@ static int at91_adc_buffer_prepare(struct iio_dev *indio_dev)
if (at91_adc_buffer_check_use_irq(indio_dev, st))
at91_adc_writel(st, IER, AT91_SAMA5D2_IER_DRDY);
- return 0;
+pm_runtime_put:
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+ return ret;
}
static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev)
{
struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
u8 bit;
/* check if we are disabling triggered buffer or the touchscreen */
@@ -1162,6 +1351,10 @@ static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev)
if (!(iio_device_get_current_mode(indio_dev) & INDIO_ALL_TRIGGERED_MODES))
return -EINVAL;
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
/*
* For each enable channel we must disable it in hardware.
* In the case of DMA, we must read the last converted value
@@ -1177,7 +1370,8 @@ static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev)
continue;
/* these channel types are virtual, no need to do anything */
if (chan->type == IIO_POSITIONRELATIVE ||
- chan->type == IIO_PRESSURE)
+ chan->type == IIO_PRESSURE ||
+ chan->type == IIO_TEMP)
continue;
at91_adc_writel(st, CHDR, BIT(chan->channel));
@@ -1196,6 +1390,9 @@ static int at91_adc_buffer_postdisable(struct iio_dev *indio_dev)
if (st->dma_st.dma_chan)
dmaengine_terminate_sync(st->dma_st.dma_chan);
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
return 0;
}
@@ -1224,6 +1421,7 @@ static struct iio_trigger *at91_adc_allocate_trigger(struct iio_dev *indio,
return trig;
}
+
static void at91_adc_trigger_handler_nodma(struct iio_dev *indio_dev,
struct iio_poll_func *pf)
{
@@ -1377,25 +1575,35 @@ static unsigned at91_adc_startup_time(unsigned startup_time_min,
return i;
}
-static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq)
+static void at91_adc_setup_samp_freq(struct iio_dev *indio_dev, unsigned freq,
+ unsigned int startup_time,
+ unsigned int tracktim)
{
struct at91_adc_state *st = iio_priv(indio_dev);
unsigned f_per, prescal, startup, mr;
+ int ret;
f_per = clk_get_rate(st->per_clk);
prescal = (f_per / (2 * freq)) - 1;
- startup = at91_adc_startup_time(st->soc_info.startup_time,
- freq / 1000);
+ startup = at91_adc_startup_time(startup_time, freq / 1000);
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return;
mr = at91_adc_readl(st, MR);
mr &= ~(AT91_SAMA5D2_MR_STARTUP_MASK | AT91_SAMA5D2_MR_PRESCAL_MASK);
mr |= AT91_SAMA5D2_MR_STARTUP(startup);
mr |= AT91_SAMA5D2_MR_PRESCAL(prescal);
+ mr |= AT91_SAMA5D2_MR_TRACKTIM(tracktim);
at91_adc_writel(st, MR, mr);
- dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u\n",
- freq, startup, prescal);
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
+ dev_dbg(&indio_dev->dev, "freq: %u, startup: %u, prescal: %u, tracktim=%u\n",
+ freq, startup, prescal, tracktim);
st->current_sample_rate = freq;
}
@@ -1522,6 +1730,7 @@ static irqreturn_t at91_adc_interrupt(int irq, void *private)
return IRQ_HANDLED;
}
+/* This needs to be called with direct mode claimed and st->lock locked. */
static int at91_adc_read_info_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan, int *val)
{
@@ -1529,50 +1738,46 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev,
u16 tmp_val;
int ret;
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
/*
* Keep in mind that we cannot use software trigger or touchscreen
* if external trigger is enabled
*/
if (chan->type == IIO_POSITIONRELATIVE) {
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
- mutex_lock(&st->lock);
-
ret = at91_adc_read_position(st, chan->channel,
&tmp_val);
*val = tmp_val;
- mutex_unlock(&st->lock);
- iio_device_release_direct_mode(indio_dev);
+ if (ret > 0)
+ ret = at91_adc_adjust_val_osr(st, val);
- return at91_adc_adjust_val_osr(st, val);
+ goto pm_runtime_put;
}
if (chan->type == IIO_PRESSURE) {
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
- mutex_lock(&st->lock);
-
ret = at91_adc_read_pressure(st, chan->channel,
&tmp_val);
*val = tmp_val;
- mutex_unlock(&st->lock);
- iio_device_release_direct_mode(indio_dev);
+ if (ret > 0)
+ ret = at91_adc_adjust_val_osr(st, val);
- return at91_adc_adjust_val_osr(st, val);
+ goto pm_runtime_put;
}
- /* in this case we have a voltage channel */
-
- ret = iio_device_claim_direct_mode(indio_dev);
- if (ret)
- return ret;
- mutex_lock(&st->lock);
+ /* in this case we have a voltage or temperature channel */
st->chan = chan;
at91_adc_cor(st, chan);
at91_adc_writel(st, CHER, BIT(chan->channel));
+ /*
+ * TEMPMR.TEMPON needs to update after CHER otherwise if none
+ * of the channels are enabled and TEMPMR.TEMPON = 1 will
+ * trigger DRDY interruption while preparing for temperature read.
+ */
+ if (chan->type == IIO_TEMP)
+ at91_adc_writel(st, TEMPMR, AT91_SAMA5D2_TEMPMR_TEMPON);
at91_adc_eoc_ena(st, chan->channel);
at91_adc_writel(st, CR, AT91_SAMA5D2_CR_START);
@@ -1592,14 +1797,125 @@ static int at91_adc_read_info_raw(struct iio_dev *indio_dev,
}
at91_adc_eoc_dis(st, st->chan->channel);
+ if (chan->type == IIO_TEMP)
+ at91_adc_writel(st, TEMPMR, 0U);
at91_adc_writel(st, CHDR, BIT(chan->channel));
/* Needed to ACK the DRDY interruption */
at91_adc_readl(st, LCDR);
+pm_runtime_put:
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+ return ret;
+}
+
+static int at91_adc_read_info_locked(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ mutex_lock(&st->lock);
+ ret = at91_adc_read_info_raw(indio_dev, chan, val);
mutex_unlock(&st->lock);
iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+static void at91_adc_temp_sensor_configure(struct at91_adc_state *st,
+ bool start)
+{
+ u32 sample_rate, oversampling_ratio;
+ u32 startup_time, tracktim, trackx;
+
+ if (start) {
+ /*
+ * Configure the sensor for best accuracy: 10MHz frequency,
+ * oversampling rate of 256, tracktim=0xf and trackx=1.
+ */
+ sample_rate = 10 * MEGA;
+ oversampling_ratio = 256;
+ startup_time = AT91_SAMA5D2_MR_STARTUP_TS_MIN;
+ tracktim = AT91_SAMA5D2_MR_TRACKTIM_TS;
+ trackx = AT91_SAMA5D2_TRACKX_TS;
+
+ st->temp_st.saved_sample_rate = st->current_sample_rate;
+ st->temp_st.saved_oversampling = st->oversampling_ratio;
+ } else {
+ /* Go back to previous settings. */
+ sample_rate = st->temp_st.saved_sample_rate;
+ oversampling_ratio = st->temp_st.saved_oversampling;
+ startup_time = st->soc_info.startup_time;
+ tracktim = 0;
+ trackx = 0;
+ }
+
+ at91_adc_setup_samp_freq(st->indio_dev, sample_rate, startup_time,
+ tracktim);
+ at91_adc_config_emr(st, oversampling_ratio, trackx);
+}
+
+static int at91_adc_read_temp(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+ struct at91_adc_temp_sensor_clb *clb = &st->soc_info.temp_sensor_clb;
+ u64 div1, div2;
+ u32 tmp;
+ int ret, vbg, vtemp;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ mutex_lock(&st->lock);
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ goto unlock;
+
+ at91_adc_temp_sensor_configure(st, true);
+
+ /* Read VBG. */
+ tmp = at91_adc_readl(st, ACR);
+ tmp |= AT91_SAMA5D2_ACR_SRCLCH;
+ at91_adc_writel(st, ACR, tmp);
+ ret = at91_adc_read_info_raw(indio_dev, chan, &vbg);
+ if (ret < 0)
+ goto restore_config;
+
+ /* Read VTEMP. */
+ tmp &= ~AT91_SAMA5D2_ACR_SRCLCH;
+ at91_adc_writel(st, ACR, tmp);
+ ret = at91_adc_read_info_raw(indio_dev, chan, &vtemp);
+
+restore_config:
+ /* Revert previous settings. */
+ at91_adc_temp_sensor_configure(st, false);
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+unlock:
+ mutex_unlock(&st->lock);
+ iio_device_release_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Temp[milli] = p1[milli] + (vtemp * clb->p6 - clb->p4 * vbg)/
+ * (vbg * AT91_ADC_TS_VTEMP_DT)
+ */
+ div1 = DIV_ROUND_CLOSEST_ULL(((u64)vtemp * clb->p6), vbg);
+ div1 = DIV_ROUND_CLOSEST_ULL((div1 * 1000), AT91_ADC_TS_VTEMP_DT);
+ div2 = DIV_ROUND_CLOSEST_ULL((u64)clb->p4, AT91_ADC_TS_VTEMP_DT);
+ div2 *= 1000;
+ *val = clb->p1 + (int)div1 - (int)div2;
+
return ret;
}
@@ -1611,7 +1927,8 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
switch (mask) {
case IIO_CHAN_INFO_RAW:
- return at91_adc_read_info_raw(indio_dev, chan, val);
+ return at91_adc_read_info_locked(indio_dev, chan, val);
+
case IIO_CHAN_INFO_SCALE:
*val = st->vref_uv / 1000;
if (chan->differential)
@@ -1619,6 +1936,11 @@ static int at91_adc_read_raw(struct iio_dev *indio_dev,
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_PROCESSED:
+ if (chan->type != IIO_TEMP)
+ return -EINVAL;
+ return at91_adc_read_temp(indio_dev, chan, val);
+
case IIO_CHAN_INFO_SAMP_FREQ:
*val = at91_adc_get_sample_freq(st);
return IIO_VAL_INT;
@@ -1637,31 +1959,60 @@ static int at91_adc_write_raw(struct iio_dev *indio_dev,
int val, int val2, long mask)
{
struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- if ((val != AT91_OSR_1SAMPLES) && (val != AT91_OSR_4SAMPLES) &&
- (val != AT91_OSR_16SAMPLES))
- return -EINVAL;
/* if no change, optimize out */
if (val == st->oversampling_ratio)
return 0;
- st->oversampling_ratio = val;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ mutex_lock(&st->lock);
/* update ratio */
- at91_adc_config_emr(st);
- return 0;
+ ret = at91_adc_config_emr(st, val, 0);
+ mutex_unlock(&st->lock);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
if (val < st->soc_info.min_sample_rate ||
val > st->soc_info.max_sample_rate)
return -EINVAL;
- at91_adc_setup_samp_freq(indio_dev, val);
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ mutex_lock(&st->lock);
+ at91_adc_setup_samp_freq(indio_dev, val,
+ st->soc_info.startup_time, 0);
+ mutex_unlock(&st->lock);
+ iio_device_release_direct_mode(indio_dev);
return 0;
default:
return -EINVAL;
}
}
+static int at91_adc_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = (int *)st->soc_info.platform->oversampling_avail;
+ *type = IIO_VAL_INT;
+ *length = st->soc_info.platform->oversampling_avail_no;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
static void at91_adc_dma_init(struct at91_adc_state *st)
{
struct device *dev = &st->indio_dev->dev;
@@ -1817,10 +2168,11 @@ static void at91_adc_hw_init(struct iio_dev *indio_dev)
at91_adc_writel(st, MR,
AT91_SAMA5D2_MR_TRANSFER(2) | AT91_SAMA5D2_MR_ANACH);
- at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate);
+ at91_adc_setup_samp_freq(indio_dev, st->soc_info.min_sample_rate,
+ st->soc_info.startup_time, 0);
/* configure extended mode register */
- at91_adc_config_emr(st);
+ at91_adc_config_emr(st, st->oversampling_ratio, 0);
}
static ssize_t at91_adc_get_fifo_state(struct device *dev,
@@ -1849,20 +2201,6 @@ static IIO_DEVICE_ATTR(hwfifo_watermark, 0444,
static IIO_CONST_ATTR(hwfifo_watermark_min, "2");
static IIO_CONST_ATTR(hwfifo_watermark_max, AT91_HWFIFO_MAX_SIZE_STR);
-static IIO_CONST_ATTR(oversampling_ratio_available,
- __stringify(AT91_OSR_1SAMPLES) " "
- __stringify(AT91_OSR_4SAMPLES) " "
- __stringify(AT91_OSR_16SAMPLES));
-
-static struct attribute *at91_adc_attributes[] = {
- &iio_const_attr_oversampling_ratio_available.dev_attr.attr,
- NULL,
-};
-
-static const struct attribute_group at91_adc_attribute_group = {
- .attrs = at91_adc_attributes,
-};
-
static const struct attribute *at91_adc_fifo_attributes[] = {
&iio_const_attr_hwfifo_watermark_min.dev_attr.attr,
&iio_const_attr_hwfifo_watermark_max.dev_attr.attr,
@@ -1872,11 +2210,11 @@ static const struct attribute *at91_adc_fifo_attributes[] = {
};
static const struct iio_info at91_adc_info = {
- .attrs = &at91_adc_attribute_group,
+ .read_avail = &at91_adc_read_avail,
.read_raw = &at91_adc_read_raw,
.write_raw = &at91_adc_write_raw,
.update_scan_mode = &at91_adc_update_scan_mode,
- .of_xlate = &at91_adc_of_xlate,
+ .fwnode_xlate = &at91_adc_fwnode_xlate,
.hwfifo_set_watermark = &at91_adc_set_watermark,
};
@@ -1918,12 +2256,62 @@ static int at91_adc_buffer_and_trigger_init(struct device *dev,
return 0;
}
+static int at91_adc_temp_sensor_init(struct at91_adc_state *st,
+ struct device *dev)
+{
+ struct at91_adc_temp_sensor_clb *clb = &st->soc_info.temp_sensor_clb;
+ struct nvmem_cell *temp_calib;
+ u32 *buf;
+ size_t len;
+ int ret = 0;
+
+ if (!st->soc_info.platform->temp_sensor)
+ return 0;
+
+ /* Get the calibration data from NVMEM. */
+ temp_calib = devm_nvmem_cell_get(dev, "temperature_calib");
+ if (IS_ERR(temp_calib)) {
+ ret = PTR_ERR(temp_calib);
+ if (ret != -ENOENT)
+ dev_err(dev, "Failed to get temperature_calib cell!\n");
+ return ret;
+ }
+
+ buf = nvmem_cell_read(temp_calib, &len);
+ if (IS_ERR(buf)) {
+ dev_err(dev, "Failed to read calibration data!\n");
+ return PTR_ERR(buf);
+ }
+ if (len < AT91_ADC_TS_CLB_IDX_MAX * 4) {
+ dev_err(dev, "Invalid calibration data!\n");
+ ret = -EINVAL;
+ goto free_buf;
+ }
+
+ /* Store calibration data for later use. */
+ clb->p1 = buf[AT91_ADC_TS_CLB_IDX_P1];
+ clb->p4 = buf[AT91_ADC_TS_CLB_IDX_P4];
+ clb->p6 = buf[AT91_ADC_TS_CLB_IDX_P6];
+
+ /*
+ * We prepare here the conversion to milli and also add constant
+ * factor (5 degrees Celsius) to p1 here to avoid doing it on
+ * hotpath.
+ */
+ clb->p1 = clb->p1 * 1000 + 5000;
+
+free_buf:
+ kfree(buf);
+ return ret;
+}
+
static int at91_adc_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
struct iio_dev *indio_dev;
struct at91_adc_state *st;
struct resource *res;
- int ret, i;
+ int ret, i, num_channels;
u32 edge_type = IRQ_TYPE_NONE;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*st));
@@ -1933,13 +2321,20 @@ static int at91_adc_probe(struct platform_device *pdev)
st = iio_priv(indio_dev);
st->indio_dev = indio_dev;
- st->soc_info.platform = of_device_get_match_data(&pdev->dev);
+ st->soc_info.platform = device_get_match_data(dev);
+
+ ret = at91_adc_temp_sensor_init(st, &pdev->dev);
+ /* Don't register temperature channel if initialization failed. */
+ if (ret)
+ num_channels = st->soc_info.platform->max_channels - 1;
+ else
+ num_channels = st->soc_info.platform->max_channels;
indio_dev->name = dev_name(&pdev->dev);
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
indio_dev->info = &at91_adc_info;
indio_dev->channels = *st->soc_info.platform->adc_channels;
- indio_dev->num_channels = st->soc_info.platform->max_channels;
+ indio_dev->num_channels = num_channels;
bitmap_set(&st->touch_st.channels_bitmask,
st->soc_info.platform->touch_chan_x, 1);
@@ -1948,36 +2343,34 @@ static int at91_adc_probe(struct platform_device *pdev)
bitmap_set(&st->touch_st.channels_bitmask,
st->soc_info.platform->touch_chan_p, 1);
- st->oversampling_ratio = AT91_OSR_1SAMPLES;
+ st->oversampling_ratio = 1;
- ret = of_property_read_u32(pdev->dev.of_node,
- "atmel,min-sample-rate-hz",
- &st->soc_info.min_sample_rate);
+ ret = device_property_read_u32(dev, "atmel,min-sample-rate-hz",
+ &st->soc_info.min_sample_rate);
if (ret) {
dev_err(&pdev->dev,
"invalid or missing value for atmel,min-sample-rate-hz\n");
return ret;
}
- ret = of_property_read_u32(pdev->dev.of_node,
- "atmel,max-sample-rate-hz",
- &st->soc_info.max_sample_rate);
+ ret = device_property_read_u32(dev, "atmel,max-sample-rate-hz",
+ &st->soc_info.max_sample_rate);
if (ret) {
dev_err(&pdev->dev,
"invalid or missing value for atmel,max-sample-rate-hz\n");
return ret;
}
- ret = of_property_read_u32(pdev->dev.of_node, "atmel,startup-time-ms",
- &st->soc_info.startup_time);
+ ret = device_property_read_u32(dev, "atmel,startup-time-ms",
+ &st->soc_info.startup_time);
if (ret) {
dev_err(&pdev->dev,
"invalid or missing value for atmel,startup-time-ms\n");
return ret;
}
- ret = of_property_read_u32(pdev->dev.of_node,
- "atmel,trigger-edge-type", &edge_type);
+ ret = device_property_read_u32(dev, "atmel,trigger-edge-type",
+ &edge_type);
if (ret) {
dev_dbg(&pdev->dev,
"atmel,trigger-edge-type not specified, only software trigger available\n");
@@ -2051,13 +2444,19 @@ static int at91_adc_probe(struct platform_device *pdev)
if (ret)
goto vref_disable;
- at91_adc_hw_init(indio_dev);
-
platform_set_drvdata(pdev, indio_dev);
+ st->dev = &pdev->dev;
+ pm_runtime_set_autosuspend_delay(st->dev, 500);
+ pm_runtime_use_autosuspend(st->dev);
+ pm_runtime_set_active(st->dev);
+ pm_runtime_enable(st->dev);
+ pm_runtime_get_noresume(st->dev);
+
+ at91_adc_hw_init(indio_dev);
ret = at91_adc_buffer_and_trigger_init(&pdev->dev, indio_dev);
if (ret < 0)
- goto per_clk_disable_unprepare;
+ goto err_pm_disable;
if (dma_coerce_mask_and_coherent(&indio_dev->dev, DMA_BIT_MASK(32)))
dev_info(&pdev->dev, "cannot set DMA mask to 32-bit\n");
@@ -2073,11 +2472,18 @@ static int at91_adc_probe(struct platform_device *pdev)
dev_info(&pdev->dev, "version: %x\n",
readl_relaxed(st->base + st->soc_info.platform->layout->VERSION));
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
+
return 0;
dma_disable:
at91_adc_dma_disable(st);
-per_clk_disable_unprepare:
+err_pm_disable:
+ pm_runtime_put_noidle(st->dev);
+ pm_runtime_disable(st->dev);
+ pm_runtime_set_suspended(st->dev);
+ pm_runtime_dont_use_autosuspend(st->dev);
clk_disable_unprepare(st->per_clk);
vref_disable:
regulator_disable(st->vref);
@@ -2095,6 +2501,8 @@ static int at91_adc_remove(struct platform_device *pdev)
at91_adc_dma_disable(st);
+ pm_runtime_disable(st->dev);
+ pm_runtime_set_suspended(st->dev);
clk_disable_unprepare(st->per_clk);
regulator_disable(st->vref);
@@ -2107,6 +2515,14 @@ static int at91_adc_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(st->dev);
+ if (ret < 0)
+ return ret;
+
+ if (iio_buffer_enabled(indio_dev))
+ at91_adc_buffer_postdisable(indio_dev);
/*
* Do a sofware reset of the ADC before we go to suspend.
@@ -2116,6 +2532,8 @@ static int at91_adc_suspend(struct device *dev)
*/
at91_adc_writel(st, CR, AT91_SAMA5D2_CR_SWRST);
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_noidle(st->dev);
clk_disable_unprepare(st->per_clk);
regulator_disable(st->vref);
regulator_disable(st->reg);
@@ -2145,21 +2563,28 @@ static int at91_adc_resume(struct device *dev)
if (ret)
goto vref_disable_resume;
+ pm_runtime_get_noresume(st->dev);
+
at91_adc_hw_init(indio_dev);
/* reconfiguring trigger hardware state */
- if (!iio_buffer_enabled(indio_dev))
- return 0;
+ if (iio_buffer_enabled(indio_dev)) {
+ ret = at91_adc_buffer_prepare(indio_dev);
+ if (ret)
+ goto pm_runtime_put;
- /* check if we are enabling triggered buffer or the touchscreen */
- if (at91_adc_current_chan_is_touch(indio_dev))
- return at91_adc_configure_touch(st, true);
- else
- return at91_adc_configure_trigger(st->trig, true);
+ at91_adc_configure_trigger_registers(st, true);
+ }
+
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_autosuspend(st->dev);
- /* not needed but more explicit */
return 0;
+pm_runtime_put:
+ pm_runtime_mark_last_busy(st->dev);
+ pm_runtime_put_noidle(st->dev);
+ clk_disable_unprepare(st->per_clk);
vref_disable_resume:
regulator_disable(st->vref);
reg_disable_resume:
@@ -2169,8 +2594,29 @@ resume_failed:
return ret;
}
-static DEFINE_SIMPLE_DEV_PM_OPS(at91_adc_pm_ops, at91_adc_suspend,
- at91_adc_resume);
+static int at91_adc_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ clk_disable(st->per_clk);
+
+ return 0;
+}
+
+static int at91_adc_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct at91_adc_state *st = iio_priv(indio_dev);
+
+ return clk_enable(st->per_clk);
+}
+
+static const struct dev_pm_ops at91_adc_pm_ops = {
+ SYSTEM_SLEEP_PM_OPS(at91_adc_suspend, at91_adc_resume)
+ RUNTIME_PM_OPS(at91_adc_runtime_suspend, at91_adc_runtime_resume,
+ NULL)
+};
static const struct of_device_id at91_adc_dt_match[] = {
{
@@ -2191,7 +2637,7 @@ static struct platform_driver at91_adc_driver = {
.driver = {
.name = "at91-sama5d2_adc",
.of_match_table = at91_adc_dt_match,
- .pm = pm_sleep_ptr(&at91_adc_pm_ops),
+ .pm = pm_ptr(&at91_adc_pm_ops),
},
};
module_platform_driver(at91_adc_driver)
diff --git a/drivers/iio/adc/imx8qxp-adc.c b/drivers/iio/adc/imx8qxp-adc.c
index e48446784a0a..36777b827165 100644
--- a/drivers/iio/adc/imx8qxp-adc.c
+++ b/drivers/iio/adc/imx8qxp-adc.c
@@ -202,7 +202,7 @@ static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev,
struct imx8qxp_adc *adc = iio_priv(indio_dev);
struct device *dev = adc->dev;
- u32 ctrl, vref_uv;
+ u32 ctrl;
long ret;
switch (mask) {
@@ -245,8 +245,10 @@ static int imx8qxp_adc_read_raw(struct iio_dev *indio_dev,
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- vref_uv = regulator_get_voltage(adc->vref);
- *val = vref_uv / 1000;
+ ret = regulator_get_voltage(adc->vref);
+ if (ret < 0)
+ return ret;
+ *val = ret / 1000;
*val2 = 12;
return IIO_VAL_FRACTIONAL_LOG2;
diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c
index bf5c03c34f84..a7325dbbb99a 100644
--- a/drivers/iio/adc/ingenic-adc.c
+++ b/drivers/iio/adc/ingenic-adc.c
@@ -719,12 +719,12 @@ static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
}
}
-static int ingenic_adc_of_xlate(struct iio_dev *iio_dev,
- const struct of_phandle_args *iiospec)
+static int ingenic_adc_fwnode_xlate(struct iio_dev *iio_dev,
+ const struct fwnode_reference_args *iiospec)
{
int i;
- if (!iiospec->args_count)
+ if (!iiospec->nargs)
return -EINVAL;
for (i = 0; i < iio_dev->num_channels; ++i)
@@ -734,16 +734,11 @@ static int ingenic_adc_of_xlate(struct iio_dev *iio_dev,
return -EINVAL;
}
-static void ingenic_adc_clk_cleanup(void *data)
-{
- clk_unprepare(data);
-}
-
static const struct iio_info ingenic_adc_info = {
.write_raw = ingenic_adc_write_raw,
.read_raw = ingenic_adc_read_raw,
.read_avail = ingenic_adc_read_avail,
- .of_xlate = ingenic_adc_of_xlate,
+ .fwnode_xlate = ingenic_adc_fwnode_xlate,
};
static int ingenic_adc_buffer_enable(struct iio_dev *iio_dev)
@@ -858,13 +853,13 @@ static int ingenic_adc_probe(struct platform_device *pdev)
if (IS_ERR(adc->base))
return PTR_ERR(adc->base);
- adc->clk = devm_clk_get(dev, "adc");
+ adc->clk = devm_clk_get_prepared(dev, "adc");
if (IS_ERR(adc->clk)) {
dev_err(dev, "Unable to get clock\n");
return PTR_ERR(adc->clk);
}
- ret = clk_prepare_enable(adc->clk);
+ ret = clk_enable(adc->clk);
if (ret) {
dev_err(dev, "Failed to enable clock\n");
return ret;
@@ -893,12 +888,6 @@ static int ingenic_adc_probe(struct platform_device *pdev)
usleep_range(2000, 3000); /* Must wait at least 2ms. */
clk_disable(adc->clk);
- ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk);
- if (ret) {
- dev_err(dev, "Unable to add action\n");
- return ret;
- }
-
iio_dev->name = "jz-adc";
iio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
iio_dev->setup_ops = &ingenic_buffer_setup_ops;
diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c
index 42e6cd6fa6f7..450a243d1f7c 100644
--- a/drivers/iio/adc/lpc18xx_adc.c
+++ b/drivers/iio/adc/lpc18xx_adc.c
@@ -121,11 +121,6 @@ static void lpc18xx_clear_cr_reg(void *data)
writel(0, adc->base + LPC18XX_ADC_CR);
}
-static void lpc18xx_clk_disable(void *clk)
-{
- clk_disable_unprepare(clk);
-}
-
static void lpc18xx_regulator_disable(void *vref)
{
regulator_disable(vref);
@@ -151,7 +146,7 @@ static int lpc18xx_adc_probe(struct platform_device *pdev)
if (IS_ERR(adc->base))
return PTR_ERR(adc->base);
- adc->clk = devm_clk_get(&pdev->dev, NULL);
+ adc->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(adc->clk))
return dev_err_probe(&pdev->dev, PTR_ERR(adc->clk),
"error getting clock\n");
@@ -177,17 +172,6 @@ static int lpc18xx_adc_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = clk_prepare_enable(adc->clk);
- if (ret) {
- dev_err(&pdev->dev, "unable to enable clock\n");
- return ret;
- }
-
- ret = devm_add_action_or_reset(&pdev->dev, lpc18xx_clk_disable,
- adc->clk);
- if (ret)
- return ret;
-
rate = clk_get_rate(adc->clk);
clkdiv = DIV_ROUND_UP(rate, LPC18XX_ADC_CLK_TARGET);
diff --git a/drivers/iio/adc/ltc2496.c b/drivers/iio/adc/ltc2496.c
index dfb3bb5997e5..2593fa4322eb 100644
--- a/drivers/iio/adc/ltc2496.c
+++ b/drivers/iio/adc/ltc2496.c
@@ -15,6 +15,7 @@
#include <linux/iio/driver.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
+#include <linux/property.h>
#include "ltc2497.h"
@@ -74,6 +75,7 @@ static int ltc2496_probe(struct spi_device *spi)
spi_set_drvdata(spi, indio_dev);
st->spi = spi;
st->common_ddata.result_and_measure = ltc2496_result_and_measure;
+ st->common_ddata.chip_info = device_get_match_data(dev);
return ltc2497core_probe(dev, indio_dev);
}
@@ -85,8 +87,13 @@ static void ltc2496_remove(struct spi_device *spi)
ltc2497core_remove(indio_dev);
}
+static const struct ltc2497_chip_info ltc2496_info = {
+ .resolution = 16,
+ .name = NULL,
+};
+
static const struct of_device_id ltc2496_of_match[] = {
- { .compatible = "lltc,ltc2496", },
+ { .compatible = "lltc,ltc2496", .data = &ltc2496_info, },
{},
};
MODULE_DEVICE_TABLE(of, ltc2496_of_match);
diff --git a/drivers/iio/adc/ltc2497-core.c b/drivers/iio/adc/ltc2497-core.c
index 2a485c8a1940..f52d37af4d1f 100644
--- a/drivers/iio/adc/ltc2497-core.c
+++ b/drivers/iio/adc/ltc2497-core.c
@@ -95,7 +95,7 @@ static int ltc2497core_read_raw(struct iio_dev *indio_dev,
return ret;
*val = ret / 1000;
- *val2 = 17;
+ *val2 = ddata->chip_info->resolution + 1;
return IIO_VAL_FRACTIONAL_LOG2;
@@ -169,7 +169,15 @@ int ltc2497core_probe(struct device *dev, struct iio_dev *indio_dev)
struct ltc2497core_driverdata *ddata = iio_priv(indio_dev);
int ret;
- indio_dev->name = dev_name(dev);
+ /*
+ * Keep using dev_name() for the iio_dev's name on some of the parts,
+ * since updating it would result in a ABI breakage.
+ */
+ if (ddata->chip_info->name)
+ indio_dev->name = ddata->chip_info->name;
+ else
+ indio_dev->name = dev_name(dev);
+
indio_dev->info = &ltc2497core_info;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->channels = ltc2497core_channel;
diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
index d58a432bafe1..556f10dfb502 100644
--- a/drivers/iio/adc/ltc2497.c
+++ b/drivers/iio/adc/ltc2497.c
@@ -12,18 +12,31 @@
#include <linux/iio/driver.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
+#include <linux/property.h>
+
+#include <asm/unaligned.h>
#include "ltc2497.h"
+enum ltc2497_chip_type {
+ TYPE_LTC2497,
+ TYPE_LTC2499,
+};
+
struct ltc2497_driverdata {
/* this must be the first member */
struct ltc2497core_driverdata common_ddata;
struct i2c_client *client;
+ u32 recv_size;
+ u32 sub_lsb;
/*
* DMA (thus cache coherency maintenance) may require the
* transfer buffers to live in their own cache lines.
*/
- __be32 buf __aligned(IIO_DMA_MINALIGN);
+ union {
+ __be32 d32;
+ u8 d8[3];
+ } data __aligned(IIO_DMA_MINALIGN);
};
static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
@@ -34,13 +47,43 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
int ret;
if (val) {
- ret = i2c_master_recv(st->client, (char *)&st->buf, 3);
+ if (st->recv_size == 3)
+ ret = i2c_master_recv(st->client, (char *)&st->data.d8,
+ st->recv_size);
+ else
+ ret = i2c_master_recv(st->client, (char *)&st->data.d32,
+ st->recv_size);
if (ret < 0) {
dev_err(&st->client->dev, "i2c_master_recv failed\n");
return ret;
}
- *val = (be32_to_cpu(st->buf) >> 14) - (1 << 17);
+ /*
+ * The data format is 16/24 bit 2s complement, but with an upper sign bit on the
+ * resolution + 1 position, which is set for positive values only. Given this
+ * bit's value, subtracting BIT(resolution + 1) from the ADC's result is
+ * equivalent to a sign extension.
+ */
+ if (st->recv_size == 3) {
+ *val = (get_unaligned_be24(st->data.d8) >> st->sub_lsb)
+ - BIT(ddata->chip_info->resolution + 1);
+ } else {
+ *val = (be32_to_cpu(st->data.d32) >> st->sub_lsb)
+ - BIT(ddata->chip_info->resolution + 1);
+ }
+
+ /*
+ * The part started a new conversion at the end of the above i2c
+ * transfer, so if the address didn't change since the last call
+ * everything is fine and we can return early.
+ * If not (which should only happen when some sort of bulk
+ * conversion is implemented) we have to program the new
+ * address. Note that this probably fails as the conversion that
+ * was triggered above is like not complete yet and the two
+ * operations have to be done in a single transfer.
+ */
+ if (ddata->addr_prev == address)
+ return 0;
}
ret = i2c_smbus_write_byte(st->client,
@@ -54,9 +97,11 @@ static int ltc2497_result_and_measure(struct ltc2497core_driverdata *ddata,
static int ltc2497_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
+ const struct ltc2497_chip_info *chip_info;
struct iio_dev *indio_dev;
struct ltc2497_driverdata *st;
struct device *dev = &client->dev;
+ u32 resolution;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
I2C_FUNC_SMBUS_WRITE_BYTE))
@@ -71,6 +116,15 @@ static int ltc2497_probe(struct i2c_client *client,
st->client = client;
st->common_ddata.result_and_measure = ltc2497_result_and_measure;
+ chip_info = device_get_match_data(dev);
+ if (!chip_info)
+ chip_info = (const struct ltc2497_chip_info *)id->driver_data;
+ st->common_ddata.chip_info = chip_info;
+
+ resolution = chip_info->resolution;
+ st->sub_lsb = 31 - (resolution + 1);
+ st->recv_size = BITS_TO_BYTES(resolution) + 1;
+
return ltc2497core_probe(dev, indio_dev);
}
@@ -81,14 +135,27 @@ static void ltc2497_remove(struct i2c_client *client)
ltc2497core_remove(indio_dev);
}
+static const struct ltc2497_chip_info ltc2497_info[] = {
+ [TYPE_LTC2497] = {
+ .resolution = 16,
+ .name = NULL,
+ },
+ [TYPE_LTC2499] = {
+ .resolution = 24,
+ .name = "ltc2499",
+ },
+};
+
static const struct i2c_device_id ltc2497_id[] = {
- { "ltc2497", 0 },
+ { "ltc2497", (kernel_ulong_t)&ltc2497_info[TYPE_LTC2497] },
+ { "ltc2499", (kernel_ulong_t)&ltc2497_info[TYPE_LTC2499] },
{ }
};
MODULE_DEVICE_TABLE(i2c, ltc2497_id);
static const struct of_device_id ltc2497_of_match[] = {
- { .compatible = "lltc,ltc2497", },
+ { .compatible = "lltc,ltc2497", .data = &ltc2497_info[TYPE_LTC2497] },
+ { .compatible = "lltc,ltc2499", .data = &ltc2497_info[TYPE_LTC2499] },
{},
};
MODULE_DEVICE_TABLE(of, ltc2497_of_match);
diff --git a/drivers/iio/adc/ltc2497.h b/drivers/iio/adc/ltc2497.h
index d0b42dd6b8ad..e023de0d88c4 100644
--- a/drivers/iio/adc/ltc2497.h
+++ b/drivers/iio/adc/ltc2497.h
@@ -4,9 +4,15 @@
#define LTC2497_CONFIG_DEFAULT LTC2497_ENABLE
#define LTC2497_CONVERSION_TIME_MS 150ULL
+struct ltc2497_chip_info {
+ u32 resolution;
+ const char *name;
+};
+
struct ltc2497core_driverdata {
struct regulator *ref;
ktime_t time_prev;
+ const struct ltc2497_chip_info *chip_info;
u8 addr_prev;
int (*result_and_measure)(struct ltc2497core_driverdata *ddata,
u8 address, int *val);
diff --git a/drivers/iio/adc/max11205.c b/drivers/iio/adc/max11205.c
new file mode 100644
index 000000000000..65fc32971ba5
--- /dev/null
+++ b/drivers/iio/adc/max11205.c
@@ -0,0 +1,183 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Maxim MAX11205 16-Bit Delta-Sigma ADC
+ *
+ * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1240-max11205.pdf
+ * Copyright (C) 2022 Analog Devices, Inc.
+ * Author: Ramona Bolboaca <ramona.bolboaca@analog.com>
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#define MAX11205_BIT_SCALE 15
+#define MAX11205A_OUT_DATA_RATE 116
+#define MAX11205B_OUT_DATA_RATE 13
+
+enum max11205_chip_type {
+ TYPE_MAX11205A,
+ TYPE_MAX11205B,
+};
+
+struct max11205_chip_info {
+ unsigned int out_data_rate;
+ const char *name;
+};
+
+struct max11205_state {
+ const struct max11205_chip_info *chip_info;
+ struct regulator *vref;
+ struct ad_sigma_delta sd;
+};
+
+static const struct ad_sigma_delta_info max11205_sigma_delta_info = {
+ .has_registers = false,
+};
+
+static int max11205_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max11205_state *st = iio_priv(indio_dev);
+ int reg_mv;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ reg_mv = regulator_get_voltage(st->vref);
+ if (reg_mv < 0)
+ return reg_mv;
+ reg_mv /= 1000;
+ *val = reg_mv;
+ *val2 = MAX11205_BIT_SCALE;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->chip_info->out_data_rate;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info max11205_iio_info = {
+ .read_raw = max11205_read_raw,
+ .validate_trigger = ad_sd_validate_trigger,
+};
+
+static const struct iio_chan_spec max11205_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static const struct max11205_chip_info max11205_chip_info[] = {
+ [TYPE_MAX11205A] = {
+ .out_data_rate = MAX11205A_OUT_DATA_RATE,
+ .name = "max11205a",
+ },
+ [TYPE_MAX11205B] = {
+ .out_data_rate = MAX11205B_OUT_DATA_RATE,
+ .name = "max11205b",
+ },
+};
+
+static void max11205_reg_disable(void *reg)
+{
+ regulator_disable(reg);
+}
+
+static int max11205_probe(struct spi_device *spi)
+{
+ struct max11205_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ ad_sd_init(&st->sd, indio_dev, spi, &max11205_sigma_delta_info);
+
+ st->chip_info = device_get_match_data(&spi->dev);
+ if (!st->chip_info)
+ st->chip_info =
+ (const struct max11205_chip_info *)spi_get_device_id(spi)->driver_data;
+
+ indio_dev->name = st->chip_info->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max11205_channels;
+ indio_dev->num_channels = 1;
+ indio_dev->info = &max11205_iio_info;
+
+ st->vref = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->vref))
+ return dev_err_probe(&spi->dev, PTR_ERR(st->vref),
+ "Failed to get vref regulator\n");
+
+ ret = regulator_enable(st->vref);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, max11205_reg_disable, st->vref);
+ if (ret)
+ return ret;
+
+ ret = devm_ad_sd_setup_buffer_and_trigger(&spi->dev, indio_dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id max11205_spi_ids[] = {
+ { "max11205a", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205A] },
+ { "max11205b", (kernel_ulong_t)&max11205_chip_info[TYPE_MAX11205B] },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, max11205_spi_ids);
+
+static const struct of_device_id max11205_dt_ids[] = {
+ {
+ .compatible = "maxim,max11205a",
+ .data = &max11205_chip_info[TYPE_MAX11205A],
+ },
+ {
+ .compatible = "maxim,max11205b",
+ .data = &max11205_chip_info[TYPE_MAX11205B],
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max11205_dt_ids);
+
+static struct spi_driver max11205_spi_driver = {
+ .driver = {
+ .name = "max11205",
+ .of_match_table = max11205_dt_ids,
+ },
+ .probe = max11205_probe,
+ .id_table = max11205_spi_ids,
+};
+module_spi_driver(max11205_spi_driver);
+
+MODULE_AUTHOR("Ramona Bolboaca <ramona.bolboaca@analog.com>");
+MODULE_DESCRIPTION("MAX11205 ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS(IIO_AD_SIGMA_DELTA);
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
index eef55ed4814a..a28cf86cdce8 100644
--- a/drivers/iio/adc/max1363.c
+++ b/drivers/iio/adc/max1363.c
@@ -29,7 +29,6 @@
#include <linux/iio/sysfs.h>
#include <linux/iio/events.h>
#include <linux/iio/buffer.h>
-#include <linux/iio/driver.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
@@ -1595,11 +1594,6 @@ static int max1363_probe(struct i2c_client *client,
if (!indio_dev)
return -ENOMEM;
- ret = devm_iio_map_array_register(&client->dev, indio_dev,
- client->dev.platform_data);
- if (ret < 0)
- return ret;
-
st = iio_priv(indio_dev);
mutex_init(&st->lock);
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
index 890af7dca62d..b35fd2c9c3c0 100644
--- a/drivers/iio/adc/mcp3911.c
+++ b/drivers/iio/adc/mcp3911.c
@@ -5,16 +5,25 @@
* Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
* Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
*/
+#include <linux/bitfield.h>
+#include <linux/bits.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/err.h>
-#include <linux/iio/iio.h>
#include <linux/module.h>
#include <linux/mod_devicetable.h>
#include <linux/property.h>
#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/trigger.h>
+
+#include <asm/unaligned.h>
+
#define MCP3911_REG_CHANNEL0 0x00
#define MCP3911_REG_CHANNEL1 0x03
#define MCP3911_REG_MOD 0x06
@@ -22,6 +31,8 @@
#define MCP3911_REG_GAIN 0x09
#define MCP3911_REG_STATUSCOM 0x0a
+#define MCP3911_STATUSCOM_DRHIZ BIT(12)
+#define MCP3911_STATUSCOM_READ GENMASK(7, 6)
#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4)
#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3)
#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2)
@@ -30,6 +41,7 @@
#define MCP3911_REG_CONFIG 0x0c
#define MCP3911_CONFIG_CLKEXT BIT(1)
#define MCP3911_CONFIG_VREFEXT BIT(2)
+#define MCP3911_CONFIG_OSR GENMASK(13, 11)
#define MCP3911_REG_OFFCAL_CH0 0x0e
#define MCP3911_REG_GAINCAL_CH0 0x11
@@ -48,12 +60,22 @@
#define MCP3911_NUM_CHANNELS 2
+static const int mcp3911_osr_table[] = { 32, 64, 128, 256, 512, 1024, 2048, 4096 };
+
struct mcp3911 {
struct spi_device *spi;
struct mutex lock;
struct regulator *vref;
struct clk *clki;
u32 dev_addr;
+ struct iio_trigger *trig;
+ struct {
+ u32 channels[MCP3911_NUM_CHANNELS];
+ s64 ts __aligned(8);
+ } scan;
+
+ u8 tx_buf __aligned(IIO_DMA_MINALIGN);
+ u8 rx_buf[MCP3911_NUM_CHANNELS * 3];
};
static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len)
@@ -98,6 +120,36 @@ static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask,
return mcp3911_write(adc, reg, val, len);
}
+static int mcp3911_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_OVERSAMPLING_RATIO:
+ return IIO_VAL_INT;
+ default:
+ return IIO_VAL_INT_PLUS_NANO;
+ }
+}
+
+static int mcp3911_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *type = IIO_VAL_INT;
+ *vals = mcp3911_osr_table;
+ *length = ARRAY_SIZE(mcp3911_osr_table);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
static int mcp3911_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *channel, int *val,
int *val2, long mask)
@@ -126,6 +178,15 @@ static int mcp3911_read_raw(struct iio_dev *indio_dev,
ret = IIO_VAL_INT;
break;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = mcp3911_read(adc, MCP3911_REG_CONFIG, val, 2);
+ if (ret)
+ goto out;
+
+ *val = FIELD_GET(MCP3911_CONFIG_OSR, *val);
+ *val = 32 << *val;
+ ret = IIO_VAL_INT;
+ break;
case IIO_CHAN_INFO_SCALE:
if (adc->vref) {
@@ -185,6 +246,17 @@ static int mcp3911_write_raw(struct iio_dev *indio_dev,
MCP3911_STATUSCOM_EN_OFFCAL,
MCP3911_STATUSCOM_EN_OFFCAL, 2);
break;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ for (int i = 0; i < sizeof(mcp3911_osr_table); i++) {
+ if (val == mcp3911_osr_table[i]) {
+ val = FIELD_PREP(MCP3911_CONFIG_OSR, i);
+ ret = mcp3911_update(adc, MCP3911_REG_CONFIG, MCP3911_CONFIG_OSR,
+ val, 2);
+ break;
+ }
+ }
+ break;
}
out:
@@ -196,25 +268,80 @@ out:
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = idx, \
+ .scan_index = idx, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_OFFSET) | \
BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_type_available = \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO), \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 24, \
+ .storagebits = 32, \
+ .endianness = IIO_BE, \
+ }, \
}
static const struct iio_chan_spec mcp3911_channels[] = {
MCP3911_CHAN(0),
MCP3911_CHAN(1),
+ IIO_CHAN_SOFT_TIMESTAMP(2),
};
+static irqreturn_t mcp3911_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ struct spi_transfer xfer[] = {
+ {
+ .tx_buf = &adc->tx_buf,
+ .len = 1,
+ }, {
+ .rx_buf = adc->rx_buf,
+ .len = sizeof(adc->rx_buf),
+ },
+ };
+ int scan_index;
+ int i = 0;
+ int ret;
+
+ mutex_lock(&adc->lock);
+ adc->tx_buf = MCP3911_REG_READ(MCP3911_CHANNEL(0), adc->dev_addr);
+ ret = spi_sync_transfer(adc->spi, xfer, ARRAY_SIZE(xfer));
+ if (ret < 0) {
+ dev_warn(&adc->spi->dev,
+ "failed to get conversion data\n");
+ goto out;
+ }
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask, indio_dev->masklength) {
+ const struct iio_chan_spec *scan_chan = &indio_dev->channels[scan_index];
+
+ adc->scan.channels[i] = get_unaligned_be24(&adc->rx_buf[scan_chan->channel * 3]);
+ i++;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev, &adc->scan,
+ iio_get_time_ns(indio_dev));
+out:
+ mutex_unlock(&adc->lock);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
static const struct iio_info mcp3911_info = {
.read_raw = mcp3911_read_raw,
.write_raw = mcp3911_write_raw,
+ .read_avail = mcp3911_read_avail,
+ .write_raw_get_fmt = mcp3911_write_raw_get_fmt,
};
static int mcp3911_config(struct mcp3911 *adc)
{
struct device *dev = &adc->spi->dev;
- u32 configreg;
+ u32 regval;
int ret;
ret = device_property_read_u32(dev, "microchip,device-addr", &adc->dev_addr);
@@ -233,31 +360,67 @@ static int mcp3911_config(struct mcp3911 *adc)
}
dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr);
- ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2);
+ ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &regval, 2);
if (ret)
return ret;
+ regval &= ~MCP3911_CONFIG_VREFEXT;
if (adc->vref) {
dev_dbg(&adc->spi->dev, "use external voltage reference\n");
- configreg |= MCP3911_CONFIG_VREFEXT;
+ regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 1);
} else {
dev_dbg(&adc->spi->dev,
"use internal voltage reference (1.2V)\n");
- configreg &= ~MCP3911_CONFIG_VREFEXT;
+ regval |= FIELD_PREP(MCP3911_CONFIG_VREFEXT, 0);
}
+ regval &= ~MCP3911_CONFIG_CLKEXT;
if (adc->clki) {
dev_dbg(&adc->spi->dev, "use external clock as clocksource\n");
- configreg |= MCP3911_CONFIG_CLKEXT;
+ regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 1);
} else {
dev_dbg(&adc->spi->dev,
"use crystal oscillator as clocksource\n");
- configreg &= ~MCP3911_CONFIG_CLKEXT;
+ regval |= FIELD_PREP(MCP3911_CONFIG_CLKEXT, 0);
}
- return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2);
+ ret = mcp3911_write(adc, MCP3911_REG_CONFIG, regval, 2);
+ if (ret)
+ return ret;
+
+ ret = mcp3911_read(adc, MCP3911_REG_STATUSCOM, &regval, 2);
+ if (ret)
+ return ret;
+
+ /* Address counter incremented, cycle through register types */
+ regval &= ~MCP3911_STATUSCOM_READ;
+ regval |= FIELD_PREP(MCP3911_STATUSCOM_READ, 0x02);
+
+ return mcp3911_write(adc, MCP3911_REG_STATUSCOM, regval, 2);
}
+static void mcp3911_cleanup_regulator(void *vref)
+{
+ regulator_disable(vref);
+}
+
+static int mcp3911_set_trigger_state(struct iio_trigger *trig, bool enable)
+{
+ struct mcp3911 *adc = iio_trigger_get_drvdata(trig);
+
+ if (enable)
+ enable_irq(adc->spi->irq);
+ else
+ disable_irq(adc->spi->irq);
+
+ return 0;
+}
+
+static const struct iio_trigger_ops mcp3911_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+ .set_trigger_state = mcp3911_set_trigger_state,
+};
+
static int mcp3911_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
@@ -286,9 +449,14 @@ static int mcp3911_probe(struct spi_device *spi)
ret = regulator_enable(adc->vref);
if (ret)
return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev,
+ mcp3911_cleanup_regulator, adc->vref);
+ if (ret)
+ return ret;
}
- adc->clki = devm_clk_get(&adc->spi->dev, NULL);
+ adc->clki = devm_clk_get_enabled(&adc->spi->dev, NULL);
if (IS_ERR(adc->clki)) {
if (PTR_ERR(adc->clki) == -ENOENT) {
adc->clki = NULL;
@@ -296,21 +464,22 @@ static int mcp3911_probe(struct spi_device *spi)
dev_err(&adc->spi->dev,
"failed to get adc clk (%ld)\n",
PTR_ERR(adc->clki));
- ret = PTR_ERR(adc->clki);
- goto reg_disable;
- }
- } else {
- ret = clk_prepare_enable(adc->clki);
- if (ret < 0) {
- dev_err(&adc->spi->dev,
- "Failed to enable clki: %d\n", ret);
- goto reg_disable;
+ return PTR_ERR(adc->clki);
}
}
ret = mcp3911_config(adc);
if (ret)
- goto clk_disable;
+ return ret;
+
+ if (device_property_read_bool(&adc->spi->dev, "microchip,data-ready-hiz"))
+ ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ,
+ 0, 2);
+ else
+ ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM, MCP3911_STATUSCOM_DRHIZ,
+ MCP3911_STATUSCOM_DRHIZ, 2);
+ if (ret)
+ return ret;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->modes = INDIO_DIRECT_MODE;
@@ -322,31 +491,38 @@ static int mcp3911_probe(struct spi_device *spi)
mutex_init(&adc->lock);
- ret = iio_device_register(indio_dev);
- if (ret)
- goto clk_disable;
-
- return ret;
-
-clk_disable:
- clk_disable_unprepare(adc->clki);
-reg_disable:
- if (adc->vref)
- regulator_disable(adc->vref);
+ if (spi->irq > 0) {
+ adc->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!adc->trig)
+ return PTR_ERR(adc->trig);
- return ret;
-}
+ adc->trig->ops = &mcp3911_trigger_ops;
+ iio_trigger_set_drvdata(adc->trig, adc);
+ ret = devm_iio_trigger_register(&spi->dev, adc->trig);
+ if (ret)
+ return ret;
-static void mcp3911_remove(struct spi_device *spi)
-{
- struct iio_dev *indio_dev = spi_get_drvdata(spi);
- struct mcp3911 *adc = iio_priv(indio_dev);
+ /*
+ * The device generates interrupts as long as it is powered up.
+ * Some platforms might not allow the option to power it down so
+ * don't enable the interrupt to avoid extra load on the system.
+ */
+ ret = devm_request_irq(&spi->dev, spi->irq,
+ &iio_trigger_generic_data_rdy_poll, IRQF_NO_AUTOEN | IRQF_ONESHOT,
+ indio_dev->name, adc->trig);
+ if (ret)
+ return ret;
+ }
- iio_device_unregister(indio_dev);
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ NULL,
+ mcp3911_trigger_handler, NULL);
+ if (ret)
+ return ret;
- clk_disable_unprepare(adc->clki);
- if (adc->vref)
- regulator_disable(adc->vref);
+ return devm_iio_device_register(&adc->spi->dev, indio_dev);
}
static const struct of_device_id mcp3911_dt_ids[] = {
@@ -367,7 +543,6 @@ static struct spi_driver mcp3911_driver = {
.of_match_table = mcp3911_dt_ids,
},
.probe = mcp3911_probe,
- .remove = mcp3911_remove,
.id_table = mcp3911_id,
};
module_spi_driver(mcp3911_driver);
diff --git a/drivers/iio/adc/mt6360-adc.c b/drivers/iio/adc/mt6360-adc.c
index 35260d9e4e47..3710473e526f 100644
--- a/drivers/iio/adc/mt6360-adc.c
+++ b/drivers/iio/adc/mt6360-adc.c
@@ -353,7 +353,7 @@ static int mt6360_adc_probe(struct platform_device *pdev)
return devm_iio_device_register(&pdev->dev, indio_dev);
}
-static const struct of_device_id __maybe_unused mt6360_adc_of_id[] = {
+static const struct of_device_id mt6360_adc_of_id[] = {
{ .compatible = "mediatek,mt6360-adc", },
{}
};
diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
index 5e9e56821075..eb424496ee1d 100644
--- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c
+++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
@@ -14,9 +14,9 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/init.h>
#include <linux/interrupt.h>
@@ -694,8 +694,8 @@ static int pm8xxx_read_raw(struct iio_dev *indio_dev,
}
}
-static int pm8xxx_of_xlate(struct iio_dev *indio_dev,
- const struct of_phandle_args *iiospec)
+static int pm8xxx_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
{
struct pm8xxx_xoadc *adc = iio_priv(indio_dev);
u8 pre_scale_mux;
@@ -706,10 +706,10 @@ static int pm8xxx_of_xlate(struct iio_dev *indio_dev,
* First cell is prescaler or premux, second cell is analog
* mux.
*/
- if (iiospec->args_count != 2) {
- dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n",
- iiospec->np,
- iiospec->args_count);
+ if (iiospec->nargs != 2) {
+ dev_err(&indio_dev->dev, "wrong number of arguments for %pfwP need 2 got %d\n",
+ iiospec->fwnode,
+ iiospec->nargs);
return -EINVAL;
}
pre_scale_mux = (u8)iiospec->args[0];
@@ -727,34 +727,34 @@ static int pm8xxx_of_xlate(struct iio_dev *indio_dev,
}
static const struct iio_info pm8xxx_xoadc_info = {
- .of_xlate = pm8xxx_of_xlate,
+ .fwnode_xlate = pm8xxx_fwnode_xlate,
.read_raw = pm8xxx_read_raw,
};
static int pm8xxx_xoadc_parse_channel(struct device *dev,
- struct device_node *np,
+ struct fwnode_handle *fwnode,
const struct xoadc_channel *hw_channels,
struct iio_chan_spec *iio_chan,
struct pm8xxx_chan_info *ch)
{
- const char *name = np->name;
+ const char *name = fwnode_get_name(fwnode);
const struct xoadc_channel *hwchan;
- u32 pre_scale_mux, amux_channel;
+ u32 pre_scale_mux, amux_channel, reg[2];
u32 rsv, dec;
int ret;
int chid;
- ret = of_property_read_u32_index(np, "reg", 0, &pre_scale_mux);
+ ret = fwnode_property_read_u32_array(fwnode, "reg", reg,
+ ARRAY_SIZE(reg));
if (ret) {
- dev_err(dev, "invalid pre scale/mux number %s\n", name);
- return ret;
- }
- ret = of_property_read_u32_index(np, "reg", 1, &amux_channel);
- if (ret) {
- dev_err(dev, "invalid amux channel number %s\n", name);
+ dev_err(dev, "invalid pre scale/mux or amux channel number %s\n",
+ name);
return ret;
}
+ pre_scale_mux = reg[0];
+ amux_channel = reg[1];
+
/* Find the right channel setting */
chid = 0;
hwchan = &hw_channels[0];
@@ -778,7 +778,7 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev,
/* Everyone seems to use default ("type 2") decimation */
ch->decimation = VADC_DEF_DECIMATION;
- if (!of_property_read_u32(np, "qcom,ratiometric", &rsv)) {
+ if (!fwnode_property_read_u32(fwnode, "qcom,ratiometric", &rsv)) {
ch->calibration = VADC_CALIB_RATIOMETRIC;
if (rsv > XOADC_RSV_MAX) {
dev_err(dev, "%s too large RSV value %d\n", name, rsv);
@@ -791,7 +791,7 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev,
}
/* Optional decimation, if omitted we use the default */
- ret = of_property_read_u32(np, "qcom,decimation", &dec);
+ ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &dec);
if (!ret) {
ret = qcom_vadc_decimation_from_dt(dec);
if (ret < 0) {
@@ -820,15 +820,14 @@ static int pm8xxx_xoadc_parse_channel(struct device *dev,
return 0;
}
-static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc,
- struct device_node *np)
+static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc)
{
- struct device_node *child;
+ struct fwnode_handle *child;
struct pm8xxx_chan_info *ch;
int ret;
int i;
- adc->nchans = of_get_available_child_count(np);
+ adc->nchans = device_get_child_node_count(adc->dev);
if (!adc->nchans) {
dev_err(adc->dev, "no channel children\n");
return -ENODEV;
@@ -846,14 +845,14 @@ static int pm8xxx_xoadc_parse_channels(struct pm8xxx_xoadc *adc,
return -ENOMEM;
i = 0;
- for_each_available_child_of_node(np, child) {
+ device_for_each_child_node(adc->dev, child) {
ch = &adc->chans[i];
ret = pm8xxx_xoadc_parse_channel(adc->dev, child,
adc->variant->channels,
&adc->iio_chans[i],
ch);
if (ret) {
- of_node_put(child);
+ fwnode_handle_put(child);
return ret;
}
i++;
@@ -884,12 +883,11 @@ static int pm8xxx_xoadc_probe(struct platform_device *pdev)
const struct xoadc_variant *variant;
struct pm8xxx_xoadc *adc;
struct iio_dev *indio_dev;
- struct device_node *np = pdev->dev.of_node;
struct regmap *map;
struct device *dev = &pdev->dev;
int ret;
- variant = of_device_get_match_data(dev);
+ variant = device_get_match_data(dev);
if (!variant)
return -ENODEV;
@@ -904,7 +902,7 @@ static int pm8xxx_xoadc_probe(struct platform_device *pdev)
init_completion(&adc->complete);
mutex_init(&adc->lock);
- ret = pm8xxx_xoadc_parse_channels(adc, np);
+ ret = pm8xxx_xoadc_parse_channels(adc);
if (ret)
return ret;
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
index 87438d1e5c0b..821fee60a765 100644
--- a/drivers/iio/adc/qcom-spmi-adc5.c
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -14,9 +14,9 @@
#include <linux/log2.h>
#include <linux/math64.h>
#include <linux/module.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -403,8 +403,8 @@ static irqreturn_t adc5_isr(int irq, void *dev_id)
return IRQ_HANDLED;
}
-static int adc5_of_xlate(struct iio_dev *indio_dev,
- const struct of_phandle_args *iiospec)
+static int adc5_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
{
struct adc5_chip *adc = iio_priv(indio_dev);
int i;
@@ -416,8 +416,8 @@ static int adc5_of_xlate(struct iio_dev *indio_dev,
return -EINVAL;
}
-static int adc7_of_xlate(struct iio_dev *indio_dev,
- const struct of_phandle_args *iiospec)
+static int adc7_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
{
struct adc5_chip *adc = iio_priv(indio_dev);
int i, v_channel;
@@ -481,12 +481,12 @@ static int adc7_read_raw(struct iio_dev *indio_dev,
static const struct iio_info adc5_info = {
.read_raw = adc5_read_raw,
- .of_xlate = adc5_of_xlate,
+ .fwnode_xlate = adc5_fwnode_xlate,
};
static const struct iio_info adc7_info = {
.read_raw = adc7_read_raw,
- .of_xlate = adc7_of_xlate,
+ .fwnode_xlate = adc7_fwnode_xlate,
};
struct adc5_channels {
@@ -526,6 +526,8 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_DEFAULT)
[ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 1,
+ SCALE_HW_CALIB_DEFAULT)
[ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
SCALE_HW_CALIB_PMIC_THERM)
[ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 0,
@@ -549,6 +551,12 @@ static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_THERM_100K_PULLUP)
[ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 0,
SCALE_HW_CALIB_PM5_SMB_TEMP)
+ [ADC5_GPIO1_100K_PU] = ADC5_CHAN_TEMP("gpio1_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_GPIO3_100K_PU] = ADC5_CHAN_TEMP("gpio3_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [ADC5_GPIO4_100K_PU] = ADC5_CHAN_TEMP("gpio4_100k_pu", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
};
static const struct adc5_channels adc7_chans_pmic[ADC5_MAX_CHANNEL] = {
@@ -589,6 +597,8 @@ static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_DEFAULT)
[ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VREF_VADC] = ADC5_CHAN_VOLT("vref_vadc", 0,
+ SCALE_HW_CALIB_DEFAULT)
[ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
SCALE_HW_CALIB_DEFAULT)
[ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
@@ -611,18 +621,18 @@ static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
SCALE_HW_CALIB_THERM_100K_PULLUP)
};
-static int adc5_get_dt_channel_data(struct adc5_chip *adc,
+static int adc5_get_fw_channel_data(struct adc5_chip *adc,
struct adc5_channel_prop *prop,
- struct device_node *node,
+ struct fwnode_handle *fwnode,
const struct adc5_data *data)
{
- const char *name = node->name, *channel_name;
+ const char *name = fwnode_get_name(fwnode), *channel_name;
u32 chan, value, varr[2];
u32 sid = 0;
int ret;
struct device *dev = adc->dev;
- ret = of_property_read_u32(node, "reg", &chan);
+ ret = fwnode_property_read_u32(fwnode, "reg", &chan);
if (ret) {
dev_err(dev, "invalid channel number %s\n", name);
return ret;
@@ -647,15 +657,13 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
prop->channel = chan;
prop->sid = sid;
- channel_name = of_get_property(node,
- "label", NULL) ? : node->name;
- if (!channel_name) {
- dev_err(dev, "Invalid channel name\n");
- return -EINVAL;
- }
+ ret = fwnode_property_read_string(fwnode, "label", &channel_name);
+ if (ret)
+ channel_name = name;
+
prop->datasheet_name = channel_name;
- ret = of_property_read_u32(node, "qcom,decimation", &value);
+ ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &value);
if (!ret) {
ret = qcom_adc5_decimation_from_dt(value, data->decimation);
if (ret < 0) {
@@ -668,7 +676,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
prop->decimation = ADC5_DECIMATION_DEFAULT;
}
- ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
+ ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2);
if (!ret) {
ret = qcom_adc5_prescaling_from_dt(varr[0], varr[1]);
if (ret < 0) {
@@ -682,7 +690,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
adc->data->adc_chans[prop->channel].prescale_index;
}
- ret = of_property_read_u32(node, "qcom,hw-settle-time", &value);
+ ret = fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value);
if (!ret) {
u8 dig_version[2];
@@ -713,7 +721,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
}
- ret = of_property_read_u32(node, "qcom,avg-samples", &value);
+ ret = fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value);
if (!ret) {
ret = qcom_adc5_avg_samples_from_dt(value);
if (ret < 0) {
@@ -726,7 +734,7 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
prop->avg_samples = VADC_DEF_AVG_SAMPLES;
}
- if (of_property_read_bool(node, "qcom,ratiometric"))
+ if (fwnode_property_read_bool(fwnode, "qcom,ratiometric"))
prop->cal_method = ADC5_RATIOMETRIC_CAL;
else
prop->cal_method = ADC5_ABSOLUTE_CAL;
@@ -801,16 +809,16 @@ static const struct of_device_id adc5_match_table[] = {
};
MODULE_DEVICE_TABLE(of, adc5_match_table);
-static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
+static int adc5_get_fw_data(struct adc5_chip *adc)
{
const struct adc5_channels *adc_chan;
struct iio_chan_spec *iio_chan;
struct adc5_channel_prop prop, *chan_props;
- struct device_node *child;
+ struct fwnode_handle *child;
unsigned int index = 0;
int ret;
- adc->nchannels = of_get_available_child_count(node);
+ adc->nchannels = device_get_child_node_count(adc->dev);
if (!adc->nchannels)
return -EINVAL;
@@ -826,14 +834,14 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
chan_props = adc->chan_props;
iio_chan = adc->iio_chans;
- adc->data = of_device_get_match_data(adc->dev);
+ adc->data = device_get_match_data(adc->dev);
if (!adc->data)
adc->data = &adc5_data_pmic;
- for_each_available_child_of_node(node, child) {
- ret = adc5_get_dt_channel_data(adc, &prop, child, adc->data);
+ device_for_each_child_node(adc->dev, child) {
+ ret = adc5_get_fw_channel_data(adc, &prop, child, adc->data);
if (ret) {
- of_node_put(child);
+ fwnode_handle_put(child);
return ret;
}
@@ -858,7 +866,6 @@ static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
static int adc5_probe(struct platform_device *pdev)
{
- struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct iio_dev *indio_dev;
struct adc5_chip *adc;
@@ -870,7 +877,7 @@ static int adc5_probe(struct platform_device *pdev)
if (!regmap)
return -ENODEV;
- ret = of_property_read_u32(node, "reg", &reg);
+ ret = device_property_read_u32(dev, "reg", &reg);
if (ret < 0)
return ret;
@@ -886,7 +893,7 @@ static int adc5_probe(struct platform_device *pdev)
init_completion(&adc->complete);
mutex_init(&adc->lock);
- ret = adc5_get_dt_data(adc, node);
+ ret = adc5_get_fw_data(adc);
if (ret) {
dev_err(dev, "adc get dt data failed\n");
return ret;
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index 34202ba52469..bcff0f62b70e 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -13,8 +13,9 @@
#include <linux/kernel.h>
#include <linux/math64.h>
#include <linux/module.h>
-#include <linux/of.h>
+#include <linux/mod_devicetable.h>
#include <linux/platform_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/log2.h>
@@ -481,8 +482,8 @@ static int vadc_read_raw(struct iio_dev *indio_dev,
return ret;
}
-static int vadc_of_xlate(struct iio_dev *indio_dev,
- const struct of_phandle_args *iiospec)
+static int vadc_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
{
struct vadc_priv *vadc = iio_priv(indio_dev);
unsigned int i;
@@ -496,7 +497,7 @@ static int vadc_of_xlate(struct iio_dev *indio_dev,
static const struct iio_info vadc_info = {
.read_raw = vadc_read_raw,
- .of_xlate = vadc_of_xlate,
+ .fwnode_xlate = vadc_fwnode_xlate,
};
struct vadc_channels {
@@ -647,15 +648,15 @@ static const struct vadc_channels vadc_chans[] = {
VADC_CHAN_NO_SCALE(LR_MUX3_BUF_PU1_PU2_XO_THERM, 0)
};
-static int vadc_get_dt_channel_data(struct device *dev,
+static int vadc_get_fw_channel_data(struct device *dev,
struct vadc_channel_prop *prop,
- struct device_node *node)
+ struct fwnode_handle *fwnode)
{
- const char *name = node->name;
+ const char *name = fwnode_get_name(fwnode);
u32 chan, value, varr[2];
int ret;
- ret = of_property_read_u32(node, "reg", &chan);
+ ret = fwnode_property_read_u32(fwnode, "reg", &chan);
if (ret) {
dev_err(dev, "invalid channel number %s\n", name);
return ret;
@@ -669,7 +670,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
/* the channel has DT description */
prop->channel = chan;
- ret = of_property_read_u32(node, "qcom,decimation", &value);
+ ret = fwnode_property_read_u32(fwnode, "qcom,decimation", &value);
if (!ret) {
ret = qcom_vadc_decimation_from_dt(value);
if (ret < 0) {
@@ -682,7 +683,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
prop->decimation = VADC_DEF_DECIMATION;
}
- ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
+ ret = fwnode_property_read_u32_array(fwnode, "qcom,pre-scaling", varr, 2);
if (!ret) {
ret = vadc_prescaling_from_dt(varr[0], varr[1]);
if (ret < 0) {
@@ -695,7 +696,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
prop->prescale = vadc_chans[prop->channel].prescale_index;
}
- ret = of_property_read_u32(node, "qcom,hw-settle-time", &value);
+ ret = fwnode_property_read_u32(fwnode, "qcom,hw-settle-time", &value);
if (!ret) {
ret = vadc_hw_settle_time_from_dt(value);
if (ret < 0) {
@@ -708,7 +709,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
}
- ret = of_property_read_u32(node, "qcom,avg-samples", &value);
+ ret = fwnode_property_read_u32(fwnode, "qcom,avg-samples", &value);
if (!ret) {
ret = vadc_avg_samples_from_dt(value);
if (ret < 0) {
@@ -721,7 +722,7 @@ static int vadc_get_dt_channel_data(struct device *dev,
prop->avg_samples = VADC_DEF_AVG_SAMPLES;
}
- if (of_property_read_bool(node, "qcom,ratiometric"))
+ if (fwnode_property_read_bool(fwnode, "qcom,ratiometric"))
prop->calibration = VADC_CALIB_RATIOMETRIC;
else
prop->calibration = VADC_CALIB_ABSOLUTE;
@@ -731,16 +732,16 @@ static int vadc_get_dt_channel_data(struct device *dev,
return 0;
}
-static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
+static int vadc_get_fw_data(struct vadc_priv *vadc)
{
const struct vadc_channels *vadc_chan;
struct iio_chan_spec *iio_chan;
struct vadc_channel_prop prop;
- struct device_node *child;
+ struct fwnode_handle *child;
unsigned int index = 0;
int ret;
- vadc->nchannels = of_get_available_child_count(node);
+ vadc->nchannels = device_get_child_node_count(vadc->dev);
if (!vadc->nchannels)
return -EINVAL;
@@ -756,10 +757,10 @@ static int vadc_get_dt_data(struct vadc_priv *vadc, struct device_node *node)
iio_chan = vadc->iio_chans;
- for_each_available_child_of_node(node, child) {
- ret = vadc_get_dt_channel_data(vadc->dev, &prop, child);
+ device_for_each_child_node(vadc->dev, child) {
+ ret = vadc_get_fw_channel_data(vadc->dev, &prop, child);
if (ret) {
- of_node_put(child);
+ fwnode_handle_put(child);
return ret;
}
@@ -848,7 +849,6 @@ static int vadc_check_revision(struct vadc_priv *vadc)
static int vadc_probe(struct platform_device *pdev)
{
- struct device_node *node = pdev->dev.of_node;
struct device *dev = &pdev->dev;
struct iio_dev *indio_dev;
struct vadc_priv *vadc;
@@ -860,7 +860,7 @@ static int vadc_probe(struct platform_device *pdev)
if (!regmap)
return -ENODEV;
- ret = of_property_read_u32(node, "reg", &reg);
+ ret = device_property_read_u32(dev, "reg", &reg);
if (ret < 0)
return ret;
@@ -880,7 +880,7 @@ static int vadc_probe(struct platform_device *pdev)
if (ret)
return ret;
- ret = vadc_get_dt_data(vadc, node);
+ ret = vadc_get_fw_data(vadc);
if (ret)
return ret;
diff --git a/drivers/iio/adc/rtq6056.c b/drivers/iio/adc/rtq6056.c
new file mode 100644
index 000000000000..c1b2e8dc9a26
--- /dev/null
+++ b/drivers/iio/adc/rtq6056.c
@@ -0,0 +1,661 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2022 Richtek Technology Corp.
+ *
+ * ChiYuan Huang <cy_huang@richtek.com>
+ */
+
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/types.h>
+#include <linux/util_macros.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 RTQ6056_REG_CONFIG 0x00
+#define RTQ6056_REG_SHUNTVOLT 0x01
+#define RTQ6056_REG_BUSVOLT 0x02
+#define RTQ6056_REG_POWER 0x03
+#define RTQ6056_REG_CURRENT 0x04
+#define RTQ6056_REG_CALIBRATION 0x05
+#define RTQ6056_REG_MASKENABLE 0x06
+#define RTQ6056_REG_ALERTLIMIT 0x07
+#define RTQ6056_REG_MANUFACTID 0xFE
+#define RTQ6056_REG_DIEID 0xFF
+
+#define RTQ6056_VENDOR_ID 0x1214
+#define RTQ6056_DEFAULT_CONFIG 0x4127
+#define RTQ6056_CONT_ALLON 7
+
+enum {
+ RTQ6056_CH_VSHUNT = 0,
+ RTQ6056_CH_VBUS,
+ RTQ6056_CH_POWER,
+ RTQ6056_CH_CURRENT,
+ RTQ6056_MAX_CHANNEL
+};
+
+enum {
+ F_OPMODE = 0,
+ F_VSHUNTCT,
+ F_VBUSCT,
+ F_AVG,
+ F_RESET,
+ F_MAX_FIELDS
+};
+
+struct rtq6056_priv {
+ struct device *dev;
+ struct regmap *regmap;
+ struct regmap_field *rm_fields[F_MAX_FIELDS];
+ u32 shunt_resistor_uohm;
+ int vshuntct_us;
+ int vbusct_us;
+ int avg_sample;
+};
+
+static const struct reg_field rtq6056_reg_fields[F_MAX_FIELDS] = {
+ [F_OPMODE] = REG_FIELD(RTQ6056_REG_CONFIG, 0, 2),
+ [F_VSHUNTCT] = REG_FIELD(RTQ6056_REG_CONFIG, 3, 5),
+ [F_VBUSCT] = REG_FIELD(RTQ6056_REG_CONFIG, 6, 8),
+ [F_AVG] = REG_FIELD(RTQ6056_REG_CONFIG, 9, 11),
+ [F_RESET] = REG_FIELD(RTQ6056_REG_CONFIG, 15, 15),
+};
+
+static const struct iio_chan_spec rtq6056_channels[RTQ6056_MAX_CHANNEL + 1] = {
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .address = RTQ6056_REG_SHUNTVOLT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .address = RTQ6056_REG_BUSVOLT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_POWER,
+ .indexed = 1,
+ .channel = 2,
+ .address = RTQ6056_REG_POWER,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 2,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_CURRENT,
+ .indexed = 1,
+ .channel = 3,
+ .address = RTQ6056_REG_CURRENT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 3,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(RTQ6056_MAX_CHANNEL),
+};
+
+static int rtq6056_adc_read_channel(struct rtq6056_priv *priv,
+ struct iio_chan_spec const *ch,
+ int *val)
+{
+ struct device *dev = priv->dev;
+ unsigned int addr = ch->address;
+ unsigned int regval;
+ int ret;
+
+ pm_runtime_get_sync(dev);
+ ret = regmap_read(priv->regmap, addr, &regval);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put(dev);
+ if (ret)
+ return ret;
+
+ /* Power and VBUS is unsigned 16-bit, others are signed 16-bit */
+ if (addr == RTQ6056_REG_BUSVOLT || addr == RTQ6056_REG_POWER)
+ *val = regval;
+ else
+ *val = sign_extend32(regval, 16);
+
+ return IIO_VAL_INT;
+}
+
+static int rtq6056_adc_read_scale(struct iio_chan_spec const *ch, int *val,
+ int *val2)
+{
+ switch (ch->address) {
+ case RTQ6056_REG_SHUNTVOLT:
+ /* VSHUNT lsb 2.5uV */
+ *val = 2500;
+ *val2 = 1000000;
+ return IIO_VAL_FRACTIONAL;
+ case RTQ6056_REG_BUSVOLT:
+ /* VBUS lsb 1.25mV */
+ *val = 1250;
+ *val2 = 1000;
+ return IIO_VAL_FRACTIONAL;
+ case RTQ6056_REG_POWER:
+ /* Power lsb 25mW */
+ *val = 25;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+/*
+ * Sample frequency for channel VSHUNT and VBUS. The indices correspond
+ * with the bit value expected by the chip. And it can be found at
+ * https://www.richtek.com/assets/product_file/RTQ6056/DSQ6056-00.pdf
+ */
+static const int rtq6056_samp_freq_list[] = {
+ 7194, 4926, 3717, 1904, 964, 485, 243, 122,
+};
+
+static int rtq6056_adc_set_samp_freq(struct rtq6056_priv *priv,
+ struct iio_chan_spec const *ch, int val)
+{
+ struct regmap_field *rm_field;
+ unsigned int selector;
+ int *ct, ret;
+
+ if (val > 7194 || val < 122)
+ return -EINVAL;
+
+ if (ch->address == RTQ6056_REG_SHUNTVOLT) {
+ rm_field = priv->rm_fields[F_VSHUNTCT];
+ ct = &priv->vshuntct_us;
+ } else if (ch->address == RTQ6056_REG_BUSVOLT) {
+ rm_field = priv->rm_fields[F_VBUSCT];
+ ct = &priv->vbusct_us;
+ } else
+ return -EINVAL;
+
+ selector = find_closest_descending(val, rtq6056_samp_freq_list,
+ ARRAY_SIZE(rtq6056_samp_freq_list));
+
+ ret = regmap_field_write(rm_field, selector);
+ if (ret)
+ return ret;
+
+ *ct = 1000000 / rtq6056_samp_freq_list[selector];
+
+ return 0;
+}
+
+/*
+ * Available averaging rate for rtq6056. The indices correspond with the bit
+ * value expected by the chip. And it can be found at
+ * https://www.richtek.com/assets/product_file/RTQ6056/DSQ6056-00.pdf
+ */
+static const int rtq6056_avg_sample_list[] = {
+ 1, 4, 16, 64, 128, 256, 512, 1024,
+};
+
+static int rtq6056_adc_set_average(struct rtq6056_priv *priv, int val)
+{
+ unsigned int selector;
+ int ret;
+
+ if (val > 1024 || val < 1)
+ return -EINVAL;
+
+ selector = find_closest(val, rtq6056_avg_sample_list,
+ ARRAY_SIZE(rtq6056_avg_sample_list));
+
+ ret = regmap_field_write(priv->rm_fields[F_AVG], selector);
+ if (ret)
+ return ret;
+
+ priv->avg_sample = rtq6056_avg_sample_list[selector];
+
+ return 0;
+}
+
+static int rtq6056_adc_get_sample_freq(struct rtq6056_priv *priv,
+ struct iio_chan_spec const *ch, int *val)
+{
+ int sample_time;
+
+ if (ch->address == RTQ6056_REG_SHUNTVOLT)
+ sample_time = priv->vshuntct_us;
+ else if (ch->address == RTQ6056_REG_BUSVOLT)
+ sample_time = priv->vbusct_us;
+ else {
+ sample_time = priv->vshuntct_us + priv->vbusct_us;
+ sample_time *= priv->avg_sample;
+ }
+
+ *val = 1000000 / sample_time;
+
+ return IIO_VAL_INT;
+}
+
+static int rtq6056_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct rtq6056_priv *priv = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return rtq6056_adc_read_channel(priv, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ return rtq6056_adc_read_scale(chan, val, val2);
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = priv->avg_sample;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return rtq6056_adc_get_sample_freq(priv, chan, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rtq6056_adc_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = rtq6056_samp_freq_list;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(rtq6056_samp_freq_list);
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = rtq6056_avg_sample_list;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(rtq6056_avg_sample_list);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rtq6056_adc_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct rtq6056_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = rtq6056_adc_set_samp_freq(priv, chan, val);
+ break;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ ret = rtq6056_adc_set_average(priv, val);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+}
+
+static const char *rtq6056_channel_labels[RTQ6056_MAX_CHANNEL] = {
+ [RTQ6056_CH_VSHUNT] = "Vshunt",
+ [RTQ6056_CH_VBUS] = "Vbus",
+ [RTQ6056_CH_POWER] = "Power",
+ [RTQ6056_CH_CURRENT] = "Current",
+};
+
+static int rtq6056_adc_read_label(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ char *label)
+{
+ return sysfs_emit(label, "%s\n", rtq6056_channel_labels[chan->channel]);
+}
+
+static int rtq6056_set_shunt_resistor(struct rtq6056_priv *priv,
+ int resistor_uohm)
+{
+ unsigned int calib_val;
+ int ret;
+
+ if (resistor_uohm <= 0) {
+ dev_err(priv->dev, "Invalid resistor [%d]\n", resistor_uohm);
+ return -EINVAL;
+ }
+
+ /* calibration = 5120000 / (Rshunt (uOhm) * current lsb (1mA)) */
+ calib_val = 5120000 / resistor_uohm;
+ ret = regmap_write(priv->regmap, RTQ6056_REG_CALIBRATION, calib_val);
+ if (ret)
+ return ret;
+
+ priv->shunt_resistor_uohm = resistor_uohm;
+
+ return 0;
+}
+
+static ssize_t shunt_resistor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct rtq6056_priv *priv = iio_priv(dev_to_iio_dev(dev));
+ int vals[2] = { priv->shunt_resistor_uohm, 1000000 };
+
+ return iio_format_value(buf, IIO_VAL_FRACTIONAL, 1, vals);
+}
+
+static ssize_t shunt_resistor_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 rtq6056_priv *priv = iio_priv(indio_dev);
+ int val, val_fract, ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = iio_str_to_fixpoint(buf, 100000, &val, &val_fract);
+ if (ret)
+ goto out_store;
+
+ ret = rtq6056_set_shunt_resistor(priv, val * 1000000 + val_fract);
+
+out_store:
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret ?: len;
+}
+
+static IIO_DEVICE_ATTR_RW(shunt_resistor, 0);
+
+static struct attribute *rtq6056_attributes[] = {
+ &iio_dev_attr_shunt_resistor.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group rtq6056_attribute_group = {
+ .attrs = rtq6056_attributes,
+};
+
+static const struct iio_info rtq6056_info = {
+ .attrs = &rtq6056_attribute_group,
+ .read_raw = rtq6056_adc_read_raw,
+ .read_avail = rtq6056_adc_read_avail,
+ .write_raw = rtq6056_adc_write_raw,
+ .read_label = rtq6056_adc_read_label,
+};
+
+static irqreturn_t rtq6056_buffer_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct rtq6056_priv *priv = iio_priv(indio_dev);
+ struct device *dev = priv->dev;
+ struct {
+ u16 vals[RTQ6056_MAX_CHANNEL];
+ s64 timestamp __aligned(8);
+ } data;
+ unsigned int raw;
+ int i = 0, bit, ret;
+
+ memset(&data, 0, sizeof(data));
+
+ pm_runtime_get_sync(dev);
+
+ for_each_set_bit(bit, indio_dev->active_scan_mask, indio_dev->masklength) {
+ unsigned int addr = rtq6056_channels[bit].address;
+
+ ret = regmap_read(priv->regmap, addr, &raw);
+ if (ret)
+ goto out;
+
+ data.vals[i++] = raw;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &data, iio_get_time_ns(indio_dev));
+
+out:
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put(dev);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void rtq6056_enter_shutdown_state(void *dev)
+{
+ struct rtq6056_priv *priv = dev_get_drvdata(dev);
+
+ /* Enter shutdown state */
+ regmap_field_write(priv->rm_fields[F_OPMODE], 0);
+}
+
+static bool rtq6056_is_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RTQ6056_REG_CONFIG ... RTQ6056_REG_ALERTLIMIT:
+ case RTQ6056_REG_MANUFACTID ... RTQ6056_REG_DIEID:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rtq6056_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RTQ6056_REG_CONFIG:
+ case RTQ6056_REG_CALIBRATION ... RTQ6056_REG_ALERTLIMIT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config rtq6056_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .val_format_endian = REGMAP_ENDIAN_BIG,
+ .max_register = RTQ6056_REG_DIEID,
+ .readable_reg = rtq6056_is_readable_reg,
+ .writeable_reg = rtq6056_is_writeable_reg,
+};
+
+static int rtq6056_probe(struct i2c_client *i2c)
+{
+ struct iio_dev *indio_dev;
+ struct rtq6056_priv *priv;
+ struct device *dev = &i2c->dev;
+ struct regmap *regmap;
+ unsigned int vendor_id, shunt_resistor_uohm;
+ int ret;
+
+ if (!i2c_check_functionality(i2c->adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->dev = dev;
+ priv->vshuntct_us = priv->vbusct_us = 1037;
+ priv->avg_sample = 1;
+ i2c_set_clientdata(i2c, priv);
+
+ regmap = devm_regmap_init_i2c(i2c, &rtq6056_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to init regmap\n");
+
+ priv->regmap = regmap;
+
+ ret = regmap_read(regmap, RTQ6056_REG_MANUFACTID, &vendor_id);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get manufacturer info\n");
+
+ if (vendor_id != RTQ6056_VENDOR_ID)
+ return dev_err_probe(dev, -ENODEV,
+ "Invalid vendor id 0x%04x\n", vendor_id);
+
+ ret = devm_regmap_field_bulk_alloc(dev, regmap, priv->rm_fields,
+ rtq6056_reg_fields, F_MAX_FIELDS);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to init regmap field\n");
+
+ /*
+ * By default, configure average sample as 1, bus and shunt conversion
+ * time as 1037 microsecond, and operating mode to all on.
+ */
+ ret = regmap_write(regmap, RTQ6056_REG_CONFIG, RTQ6056_DEFAULT_CONFIG);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to enable continuous sensing\n");
+
+ ret = devm_add_action_or_reset(dev, rtq6056_enter_shutdown_state, dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_set_autosuspend_delay(dev, MSEC_PER_SEC);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_mark_last_busy(dev);
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to enable pm_runtime\n");
+
+ /* By default, use 2000 micro-Ohm resistor */
+ shunt_resistor_uohm = 2000;
+ device_property_read_u32(dev, "shunt-resistor-micro-ohms",
+ &shunt_resistor_uohm);
+
+ ret = rtq6056_set_shunt_resistor(priv, shunt_resistor_uohm);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to init shunt resistor\n");
+
+ indio_dev->name = "rtq6056";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = rtq6056_channels;
+ indio_dev->num_channels = ARRAY_SIZE(rtq6056_channels);
+ indio_dev->info = &rtq6056_info;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ rtq6056_buffer_trigger_handler,
+ NULL);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to allocate iio trigger buffer\n");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static int rtq6056_runtime_suspend(struct device *dev)
+{
+ struct rtq6056_priv *priv = dev_get_drvdata(dev);
+
+ /* Configure to shutdown mode */
+ return regmap_field_write(priv->rm_fields[F_OPMODE], 0);
+}
+
+static int rtq6056_runtime_resume(struct device *dev)
+{
+ struct rtq6056_priv *priv = dev_get_drvdata(dev);
+ int sample_rdy_time_us, ret;
+
+ ret = regmap_field_write(priv->rm_fields[F_OPMODE], RTQ6056_CONT_ALLON);
+ if (ret)
+ return ret;
+
+ sample_rdy_time_us = priv->vbusct_us + priv->vshuntct_us;
+ sample_rdy_time_us *= priv->avg_sample;
+
+ usleep_range(sample_rdy_time_us, sample_rdy_time_us + 100);
+
+ return 0;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(rtq6056_pm_ops, rtq6056_runtime_suspend,
+ rtq6056_runtime_resume, NULL);
+
+static const struct of_device_id rtq6056_device_match[] = {
+ { .compatible = "richtek,rtq6056" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, rtq6056_device_match);
+
+static struct i2c_driver rtq6056_driver = {
+ .driver = {
+ .name = "rtq6056",
+ .of_match_table = rtq6056_device_match,
+ .pm = pm_ptr(&rtq6056_pm_ops),
+ },
+ .probe_new = rtq6056_probe,
+};
+module_i2c_driver(rtq6056_driver);
+
+MODULE_AUTHOR("ChiYuan Huang <cy_huang@richtek.com>");
+MODULE_DESCRIPTION("Richtek RTQ6056 Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index 1ce52af3fe8b..81d5db91c67b 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -9,6 +9,7 @@
*
*/
+#include <linux/bitfield.h>
#include <linux/clk.h>
#include <linux/interrupt.h>
#include <linux/irqchip/chained_irq.h>
@@ -62,6 +63,7 @@ struct stm32_adc_priv;
* @regs: common registers for all instances
* @clk_sel: clock selection routine
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
+ * @ipid: adc identification number
* @has_syscfg: SYSCFG capability flags
* @num_irqs: number of interrupt lines
* @num_adcs: maximum number of ADC instances in the common registers
@@ -70,6 +72,7 @@ struct stm32_adc_priv_cfg {
const struct stm32_adc_common_regs *regs;
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
u32 max_clk_rate_hz;
+ u32 ipid;
unsigned int has_syscfg;
unsigned int num_irqs;
unsigned int num_adcs;
@@ -78,6 +81,7 @@ struct stm32_adc_priv_cfg {
/**
* struct stm32_adc_priv - stm32 ADC core private data
* @irq: irq(s) for ADC block
+ * @nb_adc_max: actual maximum number of instance per ADC block
* @domain: irq domain reference
* @aclk: clock reference for the analog circuitry
* @bclk: bus clock common for all ADCs, depends on part used
@@ -95,6 +99,7 @@ struct stm32_adc_priv_cfg {
*/
struct stm32_adc_priv {
int irq[STM32_ADC_MAX_ADCS];
+ unsigned int nb_adc_max;
struct irq_domain *domain;
struct clk *aclk;
struct clk *bclk;
@@ -354,7 +359,7 @@ static void stm32_adc_irq_handler(struct irq_desc *desc)
* before invoking the interrupt handler (e.g. call ISR only for
* IRQ-enabled ADCs).
*/
- for (i = 0; i < priv->cfg->num_adcs; i++) {
+ for (i = 0; i < priv->nb_adc_max; i++) {
if ((status & priv->cfg->regs->eoc_msk[i] &&
stm32_adc_eoc_enabled(priv, i)) ||
(status & priv->cfg->regs->ovr_msk[i]))
@@ -424,7 +429,7 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
int hwirq;
unsigned int i;
- for (hwirq = 0; hwirq < STM32_ADC_MAX_ADCS; hwirq++)
+ for (hwirq = 0; hwirq < priv->nb_adc_max; hwirq++)
irq_dispose_mapping(irq_find_mapping(priv->domain, hwirq));
irq_domain_remove(priv->domain);
@@ -642,6 +647,49 @@ static int stm32_adc_core_switches_probe(struct device *dev,
return 0;
}
+static int stm32_adc_probe_identification(struct platform_device *pdev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct device_node *child;
+ const char *compat;
+ int ret, count = 0;
+ u32 id, val;
+
+ if (!priv->cfg->ipid)
+ return 0;
+
+ id = FIELD_GET(STM32MP1_IPIDR_MASK,
+ readl_relaxed(priv->common.base + STM32MP1_ADC_IPDR));
+ if (id != priv->cfg->ipid) {
+ dev_err(&pdev->dev, "Unexpected IP version: 0x%x", id);
+ return -EINVAL;
+ }
+
+ for_each_child_of_node(np, child) {
+ ret = of_property_read_string(child, "compatible", &compat);
+ if (ret)
+ continue;
+ /* Count child nodes with stm32 adc compatible */
+ if (strstr(compat, "st,stm32") && strstr(compat, "adc"))
+ count++;
+ }
+
+ val = readl_relaxed(priv->common.base + STM32MP1_ADC_HWCFGR0);
+ priv->nb_adc_max = FIELD_GET(STM32MP1_ADCNUM_MASK, val);
+ if (count > priv->nb_adc_max) {
+ dev_err(&pdev->dev, "Unexpected child number: %d", count);
+ return -EINVAL;
+ }
+
+ val = readl_relaxed(priv->common.base + STM32MP1_ADC_VERR);
+ dev_dbg(&pdev->dev, "ADC version: %lu.%lu\n",
+ FIELD_GET(STM32MP1_MAJREV_MASK, val),
+ FIELD_GET(STM32MP1_MINREV_MASK, val));
+
+ return 0;
+}
+
static int stm32_adc_probe(struct platform_device *pdev)
{
struct stm32_adc_priv *priv;
@@ -661,6 +709,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
priv->cfg = (const struct stm32_adc_priv_cfg *)
of_match_device(dev->driver->of_match_table, dev)->data;
+ priv->nb_adc_max = priv->cfg->num_adcs;
spin_lock_init(&priv->common.lock);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -703,6 +752,10 @@ static int stm32_adc_probe(struct platform_device *pdev)
if (ret)
goto err_pm_stop;
+ ret = stm32_adc_probe_identification(pdev, priv);
+ if (ret < 0)
+ goto err_hw_stop;
+
ret = regulator_get_voltage(priv->vref);
if (ret < 0) {
dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
@@ -811,8 +864,8 @@ static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
.clk_sel = stm32h7_adc_clk_sel,
.max_clk_rate_hz = 36000000,
.has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD,
+ .ipid = STM32MP15_IPIDR_NUMBER,
.num_irqs = 2,
- .num_adcs = 2,
};
static const struct of_device_id stm32_adc_of_match[] = {
diff --git a/drivers/iio/adc/stm32-adc-core.h b/drivers/iio/adc/stm32-adc-core.h
index faedf7a49555..2118ef63843d 100644
--- a/drivers/iio/adc/stm32-adc-core.h
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -24,6 +24,7 @@
* | 0x300 | Master & Slave common regs |
* --------------------------------------------------------
*/
+/* Maximum ADC instances number per ADC block for all supported SoCs */
#define STM32_ADC_MAX_ADCS 3
#define STM32_ADC_OFFSET 0x100
#define STM32_ADCX_COMN_OFFSET 0x300
@@ -105,6 +106,12 @@
/* STM32MP1 - ADC2 instance option register */
#define STM32MP1_ADC2_OR 0xD0
+/* STM32MP1 - Identification registers */
+#define STM32MP1_ADC_HWCFGR0 0x3F0
+#define STM32MP1_ADC_VERR 0x3F4
+#define STM32MP1_ADC_IPDR 0x3F8
+#define STM32MP1_ADC_SIDR 0x3FC
+
/* STM32H7 - common registers for all ADC instances */
#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
@@ -181,6 +188,30 @@ enum stm32h7_adc_dmngt {
/* STM32MP1_ADC2_OR - bit fields */
#define STM32MP1_VDDCOREEN BIT(0)
+/* STM32MP1_ADC_HWCFGR0 - bit fields */
+#define STM32MP1_ADCNUM_SHIFT 0
+#define STM32MP1_ADCNUM_MASK GENMASK(3, 0)
+#define STM32MP1_MULPIPE_SHIFT 4
+#define STM32MP1_MULPIPE_MASK GENMASK(7, 4)
+#define STM32MP1_OPBITS_SHIFT 8
+#define STM32MP1_OPBITS_MASK GENMASK(11, 8)
+#define STM32MP1_IDLEVALUE_SHIFT 12
+#define STM32MP1_IDLEVALUE_MASK GENMASK(15, 12)
+
+/* STM32MP1_ADC_VERR - bit fields */
+#define STM32MP1_MINREV_SHIFT 0
+#define STM32MP1_MINREV_MASK GENMASK(3, 0)
+#define STM32MP1_MAJREV_SHIFT 4
+#define STM32MP1_MAJREV_MASK GENMASK(7, 4)
+
+/* STM32MP1_ADC_IPDR - bit fields */
+#define STM32MP1_IPIDR_MASK GENMASK(31, 0)
+
+/* STM32MP1_ADC_SIDR - bit fields */
+#define STM32MP1_SIDR_MASK GENMASK(31, 0)
+
+#define STM32MP15_IPIDR_NUMBER 0x00110005
+
/**
* struct stm32_adc_common - stm32 ADC driver common data (for all instances)
* @base: control registers base cpu addr
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 130e8dd6f0c8..6256977eb7f7 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -21,11 +21,11 @@
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/module.h>
+#include <linux/mod_devicetable.h>
#include <linux/nvmem-consumer.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
+#include <linux/property.h>
#include "stm32-adc-core.h"
@@ -241,6 +241,7 @@ struct stm32_adc_cfg {
* @chan_name: channel name array
* @num_diff: number of differential channels
* @int_ch: internal channel indexes array
+ * @nsmps: number of channels with optional sample time
*/
struct stm32_adc {
struct stm32_adc_common *common;
@@ -267,6 +268,7 @@ struct stm32_adc {
char chan_name[STM32_ADC_CH_MAX][STM32_ADC_CH_SZ];
u32 num_diff;
int int_ch[STM32_ADC_INT_CH_NB];
+ int nsmps;
};
struct stm32_adc_diff_channel {
@@ -1520,8 +1522,8 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
return ret;
}
-static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
- const struct of_phandle_args *iiospec)
+static int stm32_adc_fwnode_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
{
int i;
@@ -1575,7 +1577,7 @@ static const struct iio_info stm32_adc_iio_info = {
.hwfifo_set_watermark = stm32_adc_set_watermark,
.update_scan_mode = stm32_adc_update_scan_mode,
.debugfs_reg_access = stm32_adc_debugfs_reg_access,
- .of_xlate = stm32_adc_of_xlate,
+ .fwnode_xlate = stm32_adc_fwnode_xlate,
};
static unsigned int stm32_adc_dma_residue(struct stm32_adc *adc)
@@ -1772,14 +1774,14 @@ static const struct iio_chan_spec_ext_info stm32_adc_ext_info[] = {
{},
};
-static int stm32_adc_of_get_resolution(struct iio_dev *indio_dev)
+static int stm32_adc_fw_get_resolution(struct iio_dev *indio_dev)
{
- struct device_node *node = indio_dev->dev.of_node;
+ struct device *dev = &indio_dev->dev;
struct stm32_adc *adc = iio_priv(indio_dev);
unsigned int i;
u32 res;
- if (of_property_read_u32(node, "assigned-resolution-bits", &res))
+ if (device_property_read_u32(dev, "assigned-resolution-bits", &res))
res = adc->cfg->adc_info->resolutions[0];
for (i = 0; i < adc->cfg->adc_info->num_res; i++)
@@ -1863,11 +1865,11 @@ static void stm32_adc_chan_init_one(struct iio_dev *indio_dev,
static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm32_adc *adc)
{
- struct device_node *node = indio_dev->dev.of_node;
+ struct device *dev = &indio_dev->dev;
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
int num_channels = 0, ret;
- ret = of_property_count_u32_elems(node, "st,adc-channels");
+ ret = device_property_count_u32(dev, "st,adc-channels");
if (ret > adc_info->max_channels) {
dev_err(&indio_dev->dev, "Bad st,adc-channels?\n");
return -EINVAL;
@@ -1875,8 +1877,15 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
num_channels += ret;
}
- ret = of_property_count_elems_of_size(node, "st,adc-diff-channels",
- sizeof(struct stm32_adc_diff_channel));
+ /*
+ * each st,adc-diff-channels is a group of 2 u32 so we divide @ret
+ * to get the *real* number of channels.
+ */
+ ret = device_property_count_u32(dev, "st,adc-diff-channels");
+ if (ret < 0)
+ return ret;
+
+ ret /= (int)(sizeof(struct stm32_adc_diff_channel) / sizeof(u32));
if (ret > adc_info->max_channels) {
dev_err(&indio_dev->dev, "Bad st,adc-diff-channels?\n");
return -EINVAL;
@@ -1886,8 +1895,8 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
}
/* Optional sample time is provided either for each, or all channels */
- ret = of_property_count_u32_elems(node, "st,min-sample-time-nsecs");
- if (ret > 1 && ret != num_channels) {
+ adc->nsmps = device_property_count_u32(dev, "st,min-sample-time-nsecs");
+ if (adc->nsmps > 1 && adc->nsmps != num_channels) {
dev_err(&indio_dev->dev, "Invalid st,min-sample-time-nsecs\n");
return -EINVAL;
}
@@ -1897,21 +1906,20 @@ static int stm32_adc_get_legacy_chan_count(struct iio_dev *indio_dev, struct stm
static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
struct stm32_adc *adc,
- struct iio_chan_spec *channels)
+ struct iio_chan_spec *channels,
+ int nchans)
{
- struct device_node *node = indio_dev->dev.of_node;
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
struct stm32_adc_diff_channel diff[STM32_ADC_CH_MAX];
+ struct device *dev = &indio_dev->dev;
u32 num_diff = adc->num_diff;
int size = num_diff * sizeof(*diff) / sizeof(u32);
- int scan_index = 0, val, ret, i;
- struct property *prop;
- const __be32 *cur;
- u32 smp = 0;
+ int scan_index = 0, ret, i, c;
+ u32 smp = 0, smps[STM32_ADC_CH_MAX], chans[STM32_ADC_CH_MAX];
if (num_diff) {
- ret = of_property_read_u32_array(node, "st,adc-diff-channels",
- (u32 *)diff, size);
+ ret = device_property_read_u32_array(dev, "st,adc-diff-channels",
+ (u32 *)diff, size);
if (ret) {
dev_err(&indio_dev->dev, "Failed to get diff channels %d\n", ret);
return ret;
@@ -1932,32 +1940,47 @@ static int stm32_adc_legacy_chan_init(struct iio_dev *indio_dev,
}
}
- of_property_for_each_u32(node, "st,adc-channels", prop, cur, val) {
- if (val >= adc_info->max_channels) {
- dev_err(&indio_dev->dev, "Invalid channel %d\n", val);
+ ret = device_property_read_u32_array(dev, "st,adc-channels", chans,
+ nchans);
+ if (ret)
+ return ret;
+
+ for (c = 0; c < nchans; c++) {
+ if (chans[c] >= adc_info->max_channels) {
+ dev_err(&indio_dev->dev, "Invalid channel %d\n",
+ chans[c]);
return -EINVAL;
}
/* Channel can't be configured both as single-ended & diff */
for (i = 0; i < num_diff; i++) {
- if (val == diff[i].vinp) {
- dev_err(&indio_dev->dev, "channel %d misconfigured\n", val);
+ if (chans[c] == diff[i].vinp) {
+ dev_err(&indio_dev->dev, "channel %d misconfigured\n", chans[c]);
return -EINVAL;
}
}
- stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
- 0, scan_index, false);
+ stm32_adc_chan_init_one(indio_dev, &channels[scan_index],
+ chans[c], 0, scan_index, false);
scan_index++;
}
+ if (adc->nsmps > 0) {
+ ret = device_property_read_u32_array(dev, "st,min-sample-time-nsecs",
+ smps, adc->nsmps);
+ if (ret)
+ return ret;
+ }
+
for (i = 0; i < scan_index; i++) {
/*
- * Using of_property_read_u32_index(), smp value will only be
- * modified if valid u32 value can be decoded. This allows to
- * get either no value, 1 shared value for all indexes, or one
- * value per channel.
+ * This check is used with the above logic so that smp value
+ * will only be modified if valid u32 value can be decoded. This
+ * allows to get either no value, 1 shared value for all indexes,
+ * or one value per channel. The point is to have the same
+ * behavior as 'of_property_read_u32_index()'.
*/
- of_property_read_u32_index(node, "st,min-sample-time-nsecs", i, &smp);
+ if (i < adc->nsmps)
+ smp = smps[i];
/* Prepare sampling time settings */
stm32_adc_smpr_init(adc, channels[i].channel, smp);
@@ -2005,22 +2028,21 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
struct stm32_adc *adc,
struct iio_chan_spec *channels)
{
- struct device_node *node = indio_dev->dev.of_node;
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
- struct device_node *child;
+ struct fwnode_handle *child;
const char *name;
int val, scan_index = 0, ret;
bool differential;
u32 vin[2];
- for_each_available_child_of_node(node, child) {
- ret = of_property_read_u32(child, "reg", &val);
+ device_for_each_child_node(&indio_dev->dev, child) {
+ ret = fwnode_property_read_u32(child, "reg", &val);
if (ret) {
dev_err(&indio_dev->dev, "Missing channel index %d\n", ret);
goto err;
}
- ret = of_property_read_string(child, "label", &name);
+ ret = fwnode_property_read_string(child, "label", &name);
/* label is optional */
if (!ret) {
if (strlen(name) >= STM32_ADC_CH_SZ) {
@@ -2047,7 +2069,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
}
differential = false;
- ret = of_property_read_u32_array(child, "diff-channels", vin, 2);
+ ret = fwnode_property_read_u32_array(child, "diff-channels", vin, 2);
/* diff-channels is optional */
if (!ret) {
differential = true;
@@ -2064,7 +2086,7 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
stm32_adc_chan_init_one(indio_dev, &channels[scan_index], val,
vin[1], scan_index, differential);
- ret = of_property_read_u32(child, "st,min-sample-time-ns", &val);
+ ret = fwnode_property_read_u32(child, "st,min-sample-time-ns", &val);
/* st,min-sample-time-ns is optional */
if (!ret) {
stm32_adc_smpr_init(adc, channels[scan_index].channel, val);
@@ -2082,14 +2104,13 @@ static int stm32_adc_generic_chan_init(struct iio_dev *indio_dev,
return scan_index;
err:
- of_node_put(child);
+ fwnode_handle_put(child);
return ret;
}
-static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
+static int stm32_adc_chan_fw_init(struct iio_dev *indio_dev, bool timestamping)
{
- struct device_node *node = indio_dev->dev.of_node;
struct stm32_adc *adc = iio_priv(indio_dev);
const struct stm32_adc_info *adc_info = adc->cfg->adc_info;
struct iio_chan_spec *channels;
@@ -2099,7 +2120,7 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
for (i = 0; i < STM32_ADC_INT_CH_NB; i++)
adc->int_ch[i] = STM32_ADC_INT_CH_NONE;
- num_channels = of_get_available_child_count(node);
+ num_channels = device_get_child_node_count(&indio_dev->dev);
/* If no channels have been found, fallback to channels legacy properties. */
if (!num_channels) {
legacy = true;
@@ -2130,7 +2151,8 @@ static int stm32_adc_chan_of_init(struct iio_dev *indio_dev, bool timestamping)
return -ENOMEM;
if (legacy)
- ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels);
+ ret = stm32_adc_legacy_chan_init(indio_dev, adc, channels,
+ num_channels);
else
ret = stm32_adc_generic_chan_init(indio_dev, adc, channels);
if (ret < 0)
@@ -2212,9 +2234,6 @@ static int stm32_adc_probe(struct platform_device *pdev)
bool timestamping = false;
int ret;
- if (!pdev->dev.of_node)
- return -ENODEV;
-
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*adc));
if (!indio_dev)
return -ENOMEM;
@@ -2223,17 +2242,16 @@ static int stm32_adc_probe(struct platform_device *pdev)
adc->common = dev_get_drvdata(pdev->dev.parent);
spin_lock_init(&adc->lock);
init_completion(&adc->completion);
- adc->cfg = (const struct stm32_adc_cfg *)
- of_match_device(dev->driver->of_match_table, dev)->data;
+ adc->cfg = device_get_match_data(dev);
indio_dev->name = dev_name(&pdev->dev);
- indio_dev->dev.of_node = pdev->dev.of_node;
+ device_set_node(&indio_dev->dev, dev_fwnode(&pdev->dev));
indio_dev->info = &stm32_adc_iio_info;
indio_dev->modes = INDIO_DIRECT_MODE | INDIO_HARDWARE_TRIGGERED;
platform_set_drvdata(pdev, indio_dev);
- ret = of_property_read_u32(pdev->dev.of_node, "reg", &adc->offset);
+ ret = device_property_read_u32(dev, "reg", &adc->offset);
if (ret != 0) {
dev_err(&pdev->dev, "missing reg property\n");
return -EINVAL;
@@ -2262,7 +2280,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
}
}
- ret = stm32_adc_of_get_resolution(indio_dev);
+ ret = stm32_adc_fw_get_resolution(indio_dev);
if (ret < 0)
return ret;
@@ -2279,7 +2297,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
timestamping = true;
}
- ret = stm32_adc_chan_of_init(indio_dev, timestamping);
+ ret = stm32_adc_chan_fw_init(indio_dev, timestamping);
if (ret < 0)
goto err_dma_disable;
diff --git a/drivers/iio/adc/ti-ads131e08.c b/drivers/iio/adc/ti-ads131e08.c
index 32237cacc9a3..5235a93f28bc 100644
--- a/drivers/iio/adc/ti-ads131e08.c
+++ b/drivers/iio/adc/ti-ads131e08.c
@@ -797,13 +797,6 @@ static void ads131e08_regulator_disable(void *data)
regulator_disable(st->vref_reg);
}
-static void ads131e08_clk_disable(void *data)
-{
- struct ads131e08_state *st = data;
-
- clk_disable_unprepare(st->adc_clk);
-}
-
static int ads131e08_probe(struct spi_device *spi)
{
const struct ads131e08_info *info;
@@ -896,21 +889,11 @@ static int ads131e08_probe(struct spi_device *spi)
st->vref_reg = NULL;
}
- st->adc_clk = devm_clk_get(&spi->dev, "adc-clk");
+ st->adc_clk = devm_clk_get_enabled(&spi->dev, "adc-clk");
if (IS_ERR(st->adc_clk))
return dev_err_probe(&spi->dev, PTR_ERR(st->adc_clk),
"failed to get the ADC clock\n");
- ret = clk_prepare_enable(st->adc_clk);
- if (ret) {
- dev_err(&spi->dev, "failed to prepare/enable the ADC clock\n");
- return ret;
- }
-
- ret = devm_add_action_or_reset(&spi->dev, ads131e08_clk_disable, st);
- if (ret)
- return ret;
-
adc_clk_hz = clk_get_rate(st->adc_clk);
if (!adc_clk_hz) {
dev_err(&spi->dev, "failed to get the ADC clock rate\n");
diff --git a/drivers/iio/adc/ti-tsc2046.c b/drivers/iio/adc/ti-tsc2046.c
index 0d9436a69cbf..1bbb51a6683c 100644
--- a/drivers/iio/adc/ti-tsc2046.c
+++ b/drivers/iio/adc/ti-tsc2046.c
@@ -8,7 +8,9 @@
#include <linux/bitfield.h>
#include <linux/delay.h>
#include <linux/module.h>
+#include <linux/regulator/consumer.h>
#include <linux/spi/spi.h>
+#include <linux/units.h>
#include <asm/unaligned.h>
@@ -139,6 +141,7 @@ enum tsc2046_state {
struct tsc2046_adc_priv {
struct spi_device *spi;
const struct tsc2046_adc_dcfg *dcfg;
+ struct regulator *vref_reg;
struct iio_trigger *trig;
struct hrtimer trig_timer;
@@ -173,6 +176,7 @@ struct tsc2046_adc_priv {
u32 scan_interval_us;
u32 time_per_scan_us;
u32 time_per_bit_ns;
+ unsigned int vref_mv;
struct tsc2046_adc_ch_cfg ch_cfg[TI_TSC2046_MAX_CHAN];
};
@@ -252,7 +256,9 @@ static u8 tsc2046_adc_get_cmd(struct tsc2046_adc_priv *priv, int ch_idx,
case TI_TSC2046_ADDR_AUX:
case TI_TSC2046_ADDR_VBAT:
case TI_TSC2046_ADDR_TEMP0:
- pd |= TI_TSC2046_SER | TI_TSC2046_PD1_VREF_ON;
+ pd |= TI_TSC2046_SER;
+ if (!priv->vref_reg)
+ pd |= TI_TSC2046_PD1_VREF_ON;
}
return TI_TSC2046_START | FIELD_PREP(TI_TSC2046_ADDR, ch_idx) | pd;
@@ -468,7 +474,7 @@ static int tsc2046_adc_read_raw(struct iio_dev *indio_dev,
* So, it is better to use external voltage-divider driver
* instead, which is calculating complete chain.
*/
- *val = TI_TSC2046_INT_VREF;
+ *val = priv->vref_mv;
*val2 = chan->scan_type.realbits;
return IIO_VAL_FRACTIONAL_LOG2;
}
@@ -740,6 +746,49 @@ static void tsc2046_adc_parse_fwnode(struct tsc2046_adc_priv *priv)
}
}
+static void tsc2046_adc_regulator_disable(void *data)
+{
+ struct tsc2046_adc_priv *priv = data;
+
+ regulator_disable(priv->vref_reg);
+}
+
+static int tsc2046_adc_configure_regulator(struct tsc2046_adc_priv *priv)
+{
+ struct device *dev = &priv->spi->dev;
+ int ret;
+
+ priv->vref_reg = devm_regulator_get_optional(dev, "vref");
+ if (IS_ERR(priv->vref_reg)) {
+ /* If regulator exists but can't be get, return an error */
+ if (PTR_ERR(priv->vref_reg) != -ENODEV)
+ return PTR_ERR(priv->vref_reg);
+ priv->vref_reg = NULL;
+ }
+ if (!priv->vref_reg) {
+ /* Use internal reference */
+ priv->vref_mv = TI_TSC2046_INT_VREF;
+ return 0;
+ }
+
+ ret = regulator_enable(priv->vref_reg);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, tsc2046_adc_regulator_disable,
+ priv);
+ if (ret)
+ return ret;
+
+ ret = regulator_get_voltage(priv->vref_reg);
+ if (ret < 0)
+ return ret;
+
+ priv->vref_mv = ret / MILLI;
+
+ return 0;
+}
+
static int tsc2046_adc_probe(struct spi_device *spi)
{
const struct tsc2046_adc_dcfg *dcfg;
@@ -756,6 +805,11 @@ static int tsc2046_adc_probe(struct spi_device *spi)
}
dcfg = device_get_match_data(dev);
+ if (!dcfg) {
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ dcfg = (const struct tsc2046_adc_dcfg *)id->driver_data;
+ }
if (!dcfg)
return -EINVAL;
@@ -781,6 +835,10 @@ static int tsc2046_adc_probe(struct spi_device *spi)
indio_dev->num_channels = dcfg->num_channels;
indio_dev->info = &tsc2046_adc_info;
+ ret = tsc2046_adc_configure_regulator(priv);
+ if (ret)
+ return ret;
+
tsc2046_adc_parse_fwnode(priv);
ret = tsc2046_adc_setup_spi_msg(priv);
@@ -833,11 +891,18 @@ static const struct of_device_id ads7950_of_table[] = {
};
MODULE_DEVICE_TABLE(of, ads7950_of_table);
+static const struct spi_device_id tsc2046_adc_spi_ids[] = {
+ { "tsc2046e-adc", (unsigned long)&tsc2046_adc_dcfg_tsc2046e },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, tsc2046_adc_spi_ids);
+
static struct spi_driver tsc2046_adc_driver = {
.driver = {
.name = "tsc2046",
.of_match_table = ads7950_of_table,
},
+ .id_table = tsc2046_adc_spi_ids,
.probe = tsc2046_adc_probe,
};
module_spi_driver(tsc2046_adc_driver);
diff --git a/drivers/iio/adc/xilinx-ams.c b/drivers/iio/adc/xilinx-ams.c
index 9cd2713146e5..5b4bdf3a26bb 100644
--- a/drivers/iio/adc/xilinx-ams.c
+++ b/drivers/iio/adc/xilinx-ams.c
@@ -1351,11 +1351,6 @@ static const struct of_device_id ams_of_match_table[] = {
};
MODULE_DEVICE_TABLE(of, ams_of_match_table);
-static void ams_clk_disable_unprepare(void *data)
-{
- clk_disable_unprepare(data);
-}
-
static int ams_probe(struct platform_device *pdev)
{
struct iio_dev *indio_dev;
@@ -1380,18 +1375,10 @@ static int ams_probe(struct platform_device *pdev)
if (IS_ERR(ams->base))
return PTR_ERR(ams->base);
- ams->clk = devm_clk_get(&pdev->dev, NULL);
+ ams->clk = devm_clk_get_enabled(&pdev->dev, NULL);
if (IS_ERR(ams->clk))
return PTR_ERR(ams->clk);
- ret = clk_prepare_enable(ams->clk);
- if (ret < 0)
- return ret;
-
- ret = devm_add_action_or_reset(&pdev->dev, ams_clk_disable_unprepare, ams->clk);
- if (ret < 0)
- return ret;
-
ret = devm_delayed_work_autocancel(&pdev->dev, &ams->ams_unmask_work,
ams_unmask_worker);
if (ret < 0)
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index 1b247722ba25..292f2892d223 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -1296,13 +1296,6 @@ static const char * const xadc_type_names[] = {
[XADC_TYPE_US] = "xilinx-system-monitor",
};
-static void xadc_clk_disable_unprepare(void *data)
-{
- struct clk *clk = data;
-
- clk_disable_unprepare(clk);
-}
-
static void xadc_cancel_delayed_work(void *data)
{
struct delayed_work *work = data;
@@ -1374,19 +1367,10 @@ static int xadc_probe(struct platform_device *pdev)
}
}
- xadc->clk = devm_clk_get(dev, NULL);
+ xadc->clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(xadc->clk))
return PTR_ERR(xadc->clk);
- ret = clk_prepare_enable(xadc->clk);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(dev,
- xadc_clk_disable_unprepare, xadc->clk);
- if (ret)
- return ret;
-
/*
* Make sure not to exceed the maximum samplerate since otherwise the
* resulting interrupt storm will soft-lock the system.
diff --git a/drivers/iio/addac/Kconfig b/drivers/iio/addac/Kconfig
index 138492362f20..fcf6d2269bfc 100644
--- a/drivers/iio/addac/Kconfig
+++ b/drivers/iio/addac/Kconfig
@@ -17,4 +17,20 @@ config AD74413R
To compile this driver as a module, choose M here: the
module will be called ad74413r.
+config STX104
+ tristate "Apex Embedded Systems STX104 driver"
+ depends on PC104 && X86
+ select ISA_BUS_API
+ select GPIOLIB
+ help
+ Say yes here to build support for the Apex Embedded Systems STX104
+ integrated analog PC/104 card.
+
+ This driver supports the 16 channels of single-ended (8 channels of
+ differential) analog inputs, 2 channels of analog output, 4 digital
+ inputs, and 4 digital outputs provided by the STX104.
+
+ The base port addresses for the devices may be configured via the base
+ array module parameter.
+
endmenu
diff --git a/drivers/iio/addac/Makefile b/drivers/iio/addac/Makefile
index cfd4bbe64ad3..17de20ef0d8e 100644
--- a/drivers/iio/addac/Makefile
+++ b/drivers/iio/addac/Makefile
@@ -5,3 +5,4 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD74413R) += ad74413r.o
+obj-$(CONFIG_STX104) += stx104.o
diff --git a/drivers/iio/adc/stx104.c b/drivers/iio/addac/stx104.c
index 48a91a95e597..48a91a95e597 100644
--- a/drivers/iio/adc/stx104.c
+++ b/drivers/iio/addac/stx104.c
diff --git a/drivers/iio/cdc/Kconfig b/drivers/iio/cdc/Kconfig
index 5e3319a3ff48..e0a5ce66a984 100644
--- a/drivers/iio/cdc/Kconfig
+++ b/drivers/iio/cdc/Kconfig
@@ -14,4 +14,14 @@ config AD7150
To compile this driver as a module, choose M here: the
module will be called ad7150.
+config AD7746
+ tristate "Analog Devices AD7745, AD7746 AD7747 capacitive sensor driver"
+ depends on I2C
+ help
+ Say yes here to build support for Analog Devices capacitive sensors.
+ (AD7745, AD7746, AD7747) Provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7746.
+
endmenu
diff --git a/drivers/iio/cdc/Makefile b/drivers/iio/cdc/Makefile
index ee490637b032..41db756d8020 100644
--- a/drivers/iio/cdc/Makefile
+++ b/drivers/iio/cdc/Makefile
@@ -4,3 +4,4 @@
#
obj-$(CONFIG_AD7150) += ad7150.o
+obj-$(CONFIG_AD7746) += ad7746.o
diff --git a/drivers/iio/cdc/ad7746.c b/drivers/iio/cdc/ad7746.c
new file mode 100644
index 000000000000..b266f5328140
--- /dev/null
+++ b/drivers/iio/cdc/ad7746.c
@@ -0,0 +1,820 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7746 capacitive sensor driver supporting AD7745, AD7746 and AD7747
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/stat.h>
+#include <linux/sysfs.h>
+
+#include <asm/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/* AD7746 Register Definition */
+
+#define AD7746_REG_STATUS 0
+#define AD7746_REG_CAP_DATA_HIGH 1
+#define AD7746_REG_VT_DATA_HIGH 4
+#define AD7746_REG_CAP_SETUP 7
+#define AD7746_REG_VT_SETUP 8
+#define AD7746_REG_EXC_SETUP 9
+#define AD7746_REG_CFG 10
+#define AD7746_REG_CAPDACA 11
+#define AD7746_REG_CAPDACB 12
+#define AD7746_REG_CAP_OFFH 13
+#define AD7746_REG_CAP_GAINH 15
+#define AD7746_REG_VOLT_GAINH 17
+
+/* Status Register Bit Designations (AD7746_REG_STATUS) */
+#define AD7746_STATUS_EXCERR BIT(3)
+#define AD7746_STATUS_RDY BIT(2)
+#define AD7746_STATUS_RDYVT BIT(1)
+#define AD7746_STATUS_RDYCAP BIT(0)
+
+/* Capacitive Channel Setup Register Bit Designations (AD7746_REG_CAP_SETUP) */
+#define AD7746_CAPSETUP_CAPEN BIT(7)
+#define AD7746_CAPSETUP_CIN2 BIT(6) /* AD7746 only */
+#define AD7746_CAPSETUP_CAPDIFF BIT(5)
+#define AD7746_CAPSETUP_CACHOP BIT(0)
+
+/* Voltage/Temperature Setup Register Bit Designations (AD7746_REG_VT_SETUP) */
+#define AD7746_VTSETUP_VTEN BIT(7)
+#define AD7746_VTSETUP_VTMD_MASK GENMASK(6, 5)
+#define AD7746_VTSETUP_VTMD_INT_TEMP 0
+#define AD7746_VTSETUP_VTMD_EXT_TEMP 1
+#define AD7746_VTSETUP_VTMD_VDD_MON 2
+#define AD7746_VTSETUP_VTMD_EXT_VIN 3
+#define AD7746_VTSETUP_EXTREF BIT(4)
+#define AD7746_VTSETUP_VTSHORT BIT(1)
+#define AD7746_VTSETUP_VTCHOP BIT(0)
+
+/* Excitation Setup Register Bit Designations (AD7746_REG_EXC_SETUP) */
+#define AD7746_EXCSETUP_CLKCTRL BIT(7)
+#define AD7746_EXCSETUP_EXCON BIT(6)
+#define AD7746_EXCSETUP_EXCB BIT(5)
+#define AD7746_EXCSETUP_NEXCB BIT(4)
+#define AD7746_EXCSETUP_EXCA BIT(3)
+#define AD7746_EXCSETUP_NEXCA BIT(2)
+#define AD7746_EXCSETUP_EXCLVL_MASK GENMASK(1, 0)
+
+/* Config Register Bit Designations (AD7746_REG_CFG) */
+#define AD7746_CONF_VTFS_MASK GENMASK(7, 6)
+#define AD7746_CONF_CAPFS_MASK GENMASK(5, 3)
+#define AD7746_CONF_MODE_MASK GENMASK(2, 0)
+#define AD7746_CONF_MODE_IDLE 0
+#define AD7746_CONF_MODE_CONT_CONV 1
+#define AD7746_CONF_MODE_SINGLE_CONV 2
+#define AD7746_CONF_MODE_PWRDN 3
+#define AD7746_CONF_MODE_OFFS_CAL 5
+#define AD7746_CONF_MODE_GAIN_CAL 6
+
+/* CAPDAC Register Bit Designations (AD7746_REG_CAPDACx) */
+#define AD7746_CAPDAC_DACEN BIT(7)
+#define AD7746_CAPDAC_DACP_MASK GENMASK(6, 0)
+
+struct ad7746_chip_info {
+ struct i2c_client *client;
+ struct mutex lock; /* protect sensor state */
+ /*
+ * Capacitive channel digital filter setup;
+ * conversion time/update rate setup per channel
+ */
+ u8 config;
+ u8 cap_setup;
+ u8 vt_setup;
+ u8 capdac[2][2];
+ s8 capdac_set;
+};
+
+enum ad7746_chan {
+ VIN,
+ VIN_VDD,
+ TEMP_INT,
+ TEMP_EXT,
+ CIN1,
+ CIN1_DIFF,
+ CIN2,
+ CIN2_DIFF,
+};
+
+struct ad7746_chan_info {
+ u8 addr;
+ union {
+ u8 vtmd;
+ struct { /* CAP SETUP fields */
+ unsigned int cin2 : 1;
+ unsigned int capdiff : 1;
+ };
+ };
+};
+
+static const struct ad7746_chan_info ad7746_chan_info[] = {
+ [VIN] = {
+ .addr = AD7746_REG_VT_DATA_HIGH,
+ .vtmd = AD7746_VTSETUP_VTMD_EXT_VIN,
+ },
+ [VIN_VDD] = {
+ .addr = AD7746_REG_VT_DATA_HIGH,
+ .vtmd = AD7746_VTSETUP_VTMD_VDD_MON,
+ },
+ [TEMP_INT] = {
+ .addr = AD7746_REG_VT_DATA_HIGH,
+ .vtmd = AD7746_VTSETUP_VTMD_INT_TEMP,
+ },
+ [TEMP_EXT] = {
+ .addr = AD7746_REG_VT_DATA_HIGH,
+ .vtmd = AD7746_VTSETUP_VTMD_EXT_TEMP,
+ },
+ [CIN1] = {
+ .addr = AD7746_REG_CAP_DATA_HIGH,
+ },
+ [CIN1_DIFF] = {
+ .addr = AD7746_REG_CAP_DATA_HIGH,
+ .capdiff = 1,
+ },
+ [CIN2] = {
+ .addr = AD7746_REG_CAP_DATA_HIGH,
+ .cin2 = 1,
+ },
+ [CIN2_DIFF] = {
+ .addr = AD7746_REG_CAP_DATA_HIGH,
+ .cin2 = 1,
+ .capdiff = 1,
+ },
+};
+
+static const struct iio_chan_spec ad7746_channels[] = {
+ [VIN] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .address = VIN,
+ },
+ [VIN_VDD] = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .channel = 1,
+ .extend_name = "supply",
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .address = VIN_VDD,
+ },
+ [TEMP_INT] = {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .address = TEMP_INT,
+ },
+ [TEMP_EXT] = {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .address = TEMP_EXT,
+ },
+ [CIN1] = {
+ .type = IIO_CAPACITANCE,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .address = CIN1,
+ },
+ [CIN1_DIFF] = {
+ .type = IIO_CAPACITANCE,
+ .differential = 1,
+ .indexed = 1,
+ .channel = 0,
+ .channel2 = 2,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .address = CIN1_DIFF,
+ },
+ [CIN2] = {
+ .type = IIO_CAPACITANCE,
+ .indexed = 1,
+ .channel = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_OFFSET),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .address = CIN2,
+ },
+ [CIN2_DIFF] = {
+ .type = IIO_CAPACITANCE,
+ .differential = 1,
+ .indexed = 1,
+ .channel = 1,
+ .channel2 = 3,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE) | BIT(IIO_CHAN_INFO_ZEROPOINT),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_SCALE) | BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .info_mask_shared_by_type_available = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .address = CIN2_DIFF,
+ }
+};
+
+/* Values are Update Rate (Hz), Conversion Time (ms) + 1*/
+static const unsigned char ad7746_vt_filter_rate_table[][2] = {
+ { 50, 20 + 1 }, { 31, 32 + 1 }, { 16, 62 + 1 }, { 8, 122 + 1 },
+};
+
+static const unsigned char ad7746_cap_filter_rate_table[][2] = {
+ { 91, 11 + 1 }, { 84, 12 + 1 }, { 50, 20 + 1 }, { 26, 38 + 1 },
+ { 16, 62 + 1 }, { 13, 77 + 1 }, { 11, 92 + 1 }, { 9, 110 + 1 },
+};
+
+static int ad7746_set_capdac(struct ad7746_chip_info *chip, int channel)
+{
+ int ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_CAPDACA,
+ chip->capdac[channel][0]);
+ if (ret < 0)
+ return ret;
+
+ return i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_CAPDACB,
+ chip->capdac[channel][1]);
+}
+
+static int ad7746_select_channel(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ u8 vt_setup, cap_setup;
+ int ret, delay, idx;
+
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ cap_setup = FIELD_PREP(AD7746_CAPSETUP_CIN2,
+ ad7746_chan_info[chan->address].cin2) |
+ FIELD_PREP(AD7746_CAPSETUP_CAPDIFF,
+ ad7746_chan_info[chan->address].capdiff) |
+ FIELD_PREP(AD7746_CAPSETUP_CAPEN, 1);
+ vt_setup = chip->vt_setup & ~AD7746_VTSETUP_VTEN;
+ idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config);
+ delay = ad7746_cap_filter_rate_table[idx][1];
+
+ ret = ad7746_set_capdac(chip, chan->channel);
+ if (ret < 0)
+ return ret;
+
+ if (chip->capdac_set != chan->channel)
+ chip->capdac_set = chan->channel;
+ break;
+ case IIO_VOLTAGE:
+ case IIO_TEMP:
+ vt_setup = FIELD_PREP(AD7746_VTSETUP_VTMD_MASK,
+ ad7746_chan_info[chan->address].vtmd) |
+ FIELD_PREP(AD7746_VTSETUP_VTEN, 1);
+ cap_setup = chip->cap_setup & ~AD7746_CAPSETUP_CAPEN;
+ idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config);
+ delay = ad7746_cap_filter_rate_table[idx][1];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (chip->cap_setup != cap_setup) {
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_CAP_SETUP,
+ cap_setup);
+ if (ret < 0)
+ return ret;
+
+ chip->cap_setup = cap_setup;
+ }
+
+ if (chip->vt_setup != vt_setup) {
+ ret = i2c_smbus_write_byte_data(chip->client,
+ AD7746_REG_VT_SETUP,
+ vt_setup);
+ if (ret < 0)
+ return ret;
+
+ chip->vt_setup = vt_setup;
+ }
+
+ return delay;
+}
+
+static inline ssize_t ad7746_start_calib(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len,
+ u8 regval)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ int ret, timeout = 10;
+ bool doit;
+
+ ret = kstrtobool(buf, &doit);
+ if (ret < 0)
+ return ret;
+
+ if (!doit)
+ return 0;
+
+ mutex_lock(&chip->lock);
+ regval |= chip->config;
+ ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
+ if (ret < 0)
+ goto unlock;
+
+ do {
+ msleep(20);
+ ret = i2c_smbus_read_byte_data(chip->client, AD7746_REG_CFG);
+ if (ret < 0)
+ goto unlock;
+
+ } while ((ret == regval) && timeout--);
+
+ mutex_unlock(&chip->lock);
+
+ return len;
+
+unlock:
+ mutex_unlock(&chip->lock);
+ return ret;
+}
+
+static ssize_t ad7746_start_offset_calib(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int ret = ad7746_select_channel(indio_dev,
+ &ad7746_channels[to_iio_dev_attr(attr)->address]);
+ if (ret < 0)
+ return ret;
+
+ return ad7746_start_calib(dev, attr, buf, len,
+ FIELD_PREP(AD7746_CONF_MODE_MASK,
+ AD7746_CONF_MODE_OFFS_CAL));
+}
+
+static ssize_t ad7746_start_gain_calib(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf,
+ size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ int ret = ad7746_select_channel(indio_dev,
+ &ad7746_channels[to_iio_dev_attr(attr)->address]);
+ if (ret < 0)
+ return ret;
+
+ return ad7746_start_calib(dev, attr, buf, len,
+ FIELD_PREP(AD7746_CONF_MODE_MASK,
+ AD7746_CONF_MODE_GAIN_CAL));
+}
+
+static IIO_DEVICE_ATTR(in_capacitance0_calibbias_calibration,
+ 0200, NULL, ad7746_start_offset_calib, CIN1);
+static IIO_DEVICE_ATTR(in_capacitance1_calibbias_calibration,
+ 0200, NULL, ad7746_start_offset_calib, CIN2);
+static IIO_DEVICE_ATTR(in_capacitance0_calibscale_calibration,
+ 0200, NULL, ad7746_start_gain_calib, CIN1);
+static IIO_DEVICE_ATTR(in_capacitance1_calibscale_calibration,
+ 0200, NULL, ad7746_start_gain_calib, CIN2);
+static IIO_DEVICE_ATTR(in_voltage0_calibscale_calibration,
+ 0200, NULL, ad7746_start_gain_calib, VIN);
+
+static int ad7746_store_cap_filter_rate_setup(struct ad7746_chip_info *chip,
+ int val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ad7746_cap_filter_rate_table); i++)
+ if (val >= ad7746_cap_filter_rate_table[i][0])
+ break;
+
+ if (i >= ARRAY_SIZE(ad7746_cap_filter_rate_table))
+ i = ARRAY_SIZE(ad7746_cap_filter_rate_table) - 1;
+
+ chip->config &= ~AD7746_CONF_CAPFS_MASK;
+ chip->config |= FIELD_PREP(AD7746_CONF_CAPFS_MASK, i);
+
+ return 0;
+}
+
+static int ad7746_store_vt_filter_rate_setup(struct ad7746_chip_info *chip,
+ int val)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(ad7746_vt_filter_rate_table); i++)
+ if (val >= ad7746_vt_filter_rate_table[i][0])
+ break;
+
+ if (i >= ARRAY_SIZE(ad7746_vt_filter_rate_table))
+ i = ARRAY_SIZE(ad7746_vt_filter_rate_table) - 1;
+
+ chip->config &= ~AD7746_CONF_VTFS_MASK;
+ chip->config |= FIELD_PREP(AD7746_CONF_VTFS_MASK, i);
+
+ return 0;
+}
+
+static struct attribute *ad7746_attributes[] = {
+ &iio_dev_attr_in_capacitance0_calibbias_calibration.dev_attr.attr,
+ &iio_dev_attr_in_capacitance0_calibscale_calibration.dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_calibscale_calibration.dev_attr.attr,
+ &iio_dev_attr_in_capacitance1_calibbias_calibration.dev_attr.attr,
+ &iio_dev_attr_in_voltage0_calibscale_calibration.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7746_attribute_group = {
+ .attrs = ad7746_attributes,
+};
+
+static int ad7746_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ int ret, reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_CALIBSCALE:
+ if (val != 1)
+ return -EINVAL;
+
+ val = (val2 * 1024) / 15625;
+
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ reg = AD7746_REG_CAP_GAINH;
+ break;
+ case IIO_VOLTAGE:
+ reg = AD7746_REG_VOLT_GAINH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ ret = i2c_smbus_write_word_swapped(chip->client, reg, val);
+ mutex_unlock(&chip->lock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ if (val < 0 || val > 0xFFFF)
+ return -EINVAL;
+
+ mutex_lock(&chip->lock);
+ ret = i2c_smbus_write_word_swapped(chip->client,
+ AD7746_REG_CAP_OFFH, val);
+ mutex_unlock(&chip->lock);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+ case IIO_CHAN_INFO_OFFSET:
+ case IIO_CHAN_INFO_ZEROPOINT:
+ if (val < 0 || val > 43008000) /* 21pF */
+ return -EINVAL;
+
+ /*
+ * CAPDAC Scale = 21pF_typ / 127
+ * CIN Scale = 8.192pF / 2^24
+ * Offset Scale = CAPDAC Scale / CIN Scale = 338646
+ */
+
+ val /= 338646;
+ mutex_lock(&chip->lock);
+ chip->capdac[chan->channel][chan->differential] = val > 0 ?
+ FIELD_PREP(AD7746_CAPDAC_DACP_MASK, val) | AD7746_CAPDAC_DACEN : 0;
+
+ ret = ad7746_set_capdac(chip, chan->channel);
+ if (ret < 0) {
+ mutex_unlock(&chip->lock);
+ return ret;
+ }
+
+ chip->capdac_set = chan->channel;
+ mutex_unlock(&chip->lock);
+
+ return 0;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val2)
+ return -EINVAL;
+
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ mutex_lock(&chip->lock);
+ ret = ad7746_store_cap_filter_rate_setup(chip, val);
+ mutex_unlock(&chip->lock);
+ return ret;
+ case IIO_VOLTAGE:
+ mutex_lock(&chip->lock);
+ ret = ad7746_store_vt_filter_rate_setup(chip, val);
+ mutex_unlock(&chip->lock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static const int ad7746_v_samp_freq[] = { 50, 31, 16, 8, };
+static const int ad7746_cap_samp_freq[] = { 91, 84, 50, 26, 16, 13, 11, 9, };
+
+static int ad7746_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, const int **vals,
+ int *type, int *length, long mask)
+{
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ *vals = ad7746_v_samp_freq;
+ *length = ARRAY_SIZE(ad7746_v_samp_freq);
+ break;
+ case IIO_CAPACITANCE:
+ *vals = ad7746_cap_samp_freq;
+ *length = ARRAY_SIZE(ad7746_cap_samp_freq);
+ break;
+ default:
+ return -EINVAL;
+ }
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+}
+
+static int ad7746_read_channel(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ int ret, delay;
+ u8 data[3];
+ u8 regval;
+
+ ret = ad7746_select_channel(indio_dev, chan);
+ if (ret < 0)
+ return ret;
+ delay = ret;
+
+ regval = chip->config | FIELD_PREP(AD7746_CONF_MODE_MASK,
+ AD7746_CONF_MODE_SINGLE_CONV);
+ ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_CFG, regval);
+ if (ret < 0)
+ return ret;
+
+ msleep(delay);
+ /* Now read the actual register */
+ ret = i2c_smbus_read_i2c_block_data(chip->client,
+ ad7746_chan_info[chan->address].addr,
+ sizeof(data), data);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Offset applied internally becaue the _offset userspace interface is
+ * needed for the CAP DACs which apply a controllable offset.
+ */
+ *val = get_unaligned_be24(data) - 0x800000;
+
+ return 0;
+}
+
+static int ad7746_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2,
+ long mask)
+{
+ struct ad7746_chip_info *chip = iio_priv(indio_dev);
+ int ret, idx;
+ u8 reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&chip->lock);
+ ret = ad7746_read_channel(indio_dev, chan, val);
+ mutex_unlock(&chip->lock);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ reg = AD7746_REG_CAP_GAINH;
+ break;
+ case IIO_VOLTAGE:
+ reg = AD7746_REG_VOLT_GAINH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ mutex_lock(&chip->lock);
+ ret = i2c_smbus_read_word_swapped(chip->client, reg);
+ mutex_unlock(&chip->lock);
+ if (ret < 0)
+ return ret;
+ /* 1 + gain_val / 2^16 */
+ *val = 1;
+ *val2 = (15625 * ret) / 1024;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_CALIBBIAS:
+ mutex_lock(&chip->lock);
+ ret = i2c_smbus_read_word_swapped(chip->client,
+ AD7746_REG_CAP_OFFH);
+ mutex_unlock(&chip->lock);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ case IIO_CHAN_INFO_ZEROPOINT:
+ *val = FIELD_GET(AD7746_CAPDAC_DACP_MASK,
+ chip->capdac[chan->channel][chan->differential]) * 338646;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ /* 8.192pf / 2^24 */
+ *val = 0;
+ *val2 = 488;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_VOLTAGE:
+ /* 1170mV / 2^23 */
+ *val = 1170;
+ if (chan->channel == 1)
+ *val *= 6;
+ *val2 = 23;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_TEMP:
+ *val = 125;
+ *val2 = 8;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->type) {
+ case IIO_CAPACITANCE:
+ idx = FIELD_GET(AD7746_CONF_CAPFS_MASK, chip->config);
+ *val = ad7746_cap_filter_rate_table[idx][0];
+ return IIO_VAL_INT;
+ case IIO_VOLTAGE:
+ idx = FIELD_GET(AD7746_CONF_VTFS_MASK, chip->config);
+ *val = ad7746_vt_filter_rate_table[idx][0];
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ad7746_info = {
+ .attrs = &ad7746_attribute_group,
+ .read_raw = ad7746_read_raw,
+ .read_avail = ad7746_read_avail,
+ .write_raw = ad7746_write_raw,
+};
+
+static int ad7746_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct ad7746_chip_info *chip;
+ struct iio_dev *indio_dev;
+ unsigned char regval = 0;
+ unsigned int vdd_permille;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*chip));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ chip = iio_priv(indio_dev);
+ mutex_init(&chip->lock);
+
+ chip->client = client;
+ chip->capdac_set = -1;
+
+ indio_dev->name = id->name;
+ indio_dev->info = &ad7746_info;
+ indio_dev->channels = ad7746_channels;
+ if (id->driver_data == 7746)
+ indio_dev->num_channels = ARRAY_SIZE(ad7746_channels);
+ else
+ indio_dev->num_channels = ARRAY_SIZE(ad7746_channels) - 2;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (device_property_read_bool(dev, "adi,exca-output-en")) {
+ if (device_property_read_bool(dev, "adi,exca-output-invert"))
+ regval |= AD7746_EXCSETUP_NEXCA;
+ else
+ regval |= AD7746_EXCSETUP_EXCA;
+ }
+
+ if (device_property_read_bool(dev, "adi,excb-output-en")) {
+ if (device_property_read_bool(dev, "adi,excb-output-invert"))
+ regval |= AD7746_EXCSETUP_NEXCB;
+ else
+ regval |= AD7746_EXCSETUP_EXCB;
+ }
+
+ ret = device_property_read_u32(dev, "adi,excitation-vdd-permille",
+ &vdd_permille);
+ if (!ret) {
+ switch (vdd_permille) {
+ case 125:
+ regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 0);
+ break;
+ case 250:
+ regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 1);
+ break;
+ case 375:
+ regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 2);
+ break;
+ case 500:
+ regval |= FIELD_PREP(AD7746_EXCSETUP_EXCLVL_MASK, 3);
+ break;
+ default:
+ break;
+ }
+ }
+
+ ret = i2c_smbus_write_byte_data(chip->client, AD7746_REG_EXC_SETUP,
+ regval);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(indio_dev->dev.parent, indio_dev);
+}
+
+static const struct i2c_device_id ad7746_id[] = {
+ { "ad7745", 7745 },
+ { "ad7746", 7746 },
+ { "ad7747", 7747 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ad7746_id);
+
+static const struct of_device_id ad7746_of_match[] = {
+ { .compatible = "adi,ad7745" },
+ { .compatible = "adi,ad7746" },
+ { .compatible = "adi,ad7747" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7746_of_match);
+
+static struct i2c_driver ad7746_driver = {
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .of_match_table = ad7746_of_match,
+ },
+ .probe = ad7746_probe,
+ .id_table = ad7746_id,
+};
+module_i2c_driver(ad7746_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7746/5/7 capacitive sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/scmi_sensors/scmi_iio.c b/drivers/iio/common/scmi_sensors/scmi_iio.c
index 793d628db55f..54ccf19ab2bb 100644
--- a/drivers/iio/common/scmi_sensors/scmi_iio.c
+++ b/drivers/iio/common/scmi_sensors/scmi_iio.c
@@ -18,6 +18,7 @@
#include <linux/scmi_protocol.h>
#include <linux/time.h>
#include <linux/types.h>
+#include <linux/units.h>
#define SCMI_IIO_NUM_OF_AXIS 3
@@ -130,7 +131,6 @@ static const struct iio_buffer_setup_ops scmi_iio_buffer_ops = {
static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
{
struct scmi_iio_priv *sensor = iio_priv(iio_dev);
- const unsigned long UHZ_PER_HZ = 1000000UL;
u64 sec, mult, uHz, sf;
u32 sensor_config;
char buf[32];
@@ -145,7 +145,7 @@ static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
return err;
}
- uHz = val * UHZ_PER_HZ + val2;
+ uHz = val * MICROHZ_PER_HZ + val2;
/*
* The seconds field in the sensor interval in SCMI is 16 bits long
@@ -156,10 +156,10 @@ static int scmi_iio_set_odr_val(struct iio_dev *iio_dev, int val, int val2)
* count the number of characters
*/
sf = (u64)uHz * 0xFFFF;
- do_div(sf, UHZ_PER_HZ);
+ do_div(sf, MICROHZ_PER_HZ);
mult = scnprintf(buf, sizeof(buf), "%llu", sf) - 1;
- sec = int_pow(10, mult) * UHZ_PER_HZ;
+ sec = int_pow(10, mult) * MICROHZ_PER_HZ;
do_div(sec, uHz);
if (sec == 0) {
dev_err(&iio_dev->dev,
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 9910ba1da085..35720c64fea8 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -354,7 +354,7 @@ void st_sensors_dev_name_probe(struct device *dev, char *name, int len)
return;
/* The name from the match takes precedence if present */
- strlcpy(name, match, len);
+ strscpy(name, match, len);
}
EXPORT_SYMBOL_NS(st_sensors_dev_name_probe, IIO_ST_SENSORS);
diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c
index 92be661034a6..8e5e014e0c28 100644
--- a/drivers/iio/dac/ad5593r.c
+++ b/drivers/iio/dac/ad5593r.c
@@ -13,6 +13,8 @@
#include <linux/module.h>
#include <linux/mod_devicetable.h>
+#include <asm/unaligned.h>
+
#define AD5593R_MODE_CONF (0 << 4)
#define AD5593R_MODE_DAC_WRITE (1 << 4)
#define AD5593R_MODE_ADC_READBACK (4 << 4)
@@ -20,6 +22,24 @@
#define AD5593R_MODE_GPIO_READBACK (6 << 4)
#define AD5593R_MODE_REG_READBACK (7 << 4)
+static int ad5593r_read_word(struct i2c_client *i2c, u8 reg, u16 *value)
+{
+ int ret;
+ u8 buf[2];
+
+ ret = i2c_smbus_write_byte(i2c, reg);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_master_recv(i2c, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ *value = get_unaligned_be16(buf);
+
+ return 0;
+}
+
static int ad5593r_write_dac(struct ad5592r_state *st, unsigned chan, u16 value)
{
struct i2c_client *i2c = to_i2c_client(st->dev);
@@ -38,13 +58,7 @@ static int ad5593r_read_adc(struct ad5592r_state *st, unsigned chan, u16 *value)
if (val < 0)
return (int) val;
- val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_ADC_READBACK);
- if (val < 0)
- return (int) val;
-
- *value = (u16) val;
-
- return 0;
+ return ad5593r_read_word(i2c, AD5593R_MODE_ADC_READBACK, value);
}
static int ad5593r_reg_write(struct ad5592r_state *st, u8 reg, u16 value)
@@ -58,25 +72,19 @@ static int ad5593r_reg_write(struct ad5592r_state *st, u8 reg, u16 value)
static int ad5593r_reg_read(struct ad5592r_state *st, u8 reg, u16 *value)
{
struct i2c_client *i2c = to_i2c_client(st->dev);
- s32 val;
-
- val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_REG_READBACK | reg);
- if (val < 0)
- return (int) val;
- *value = (u16) val;
-
- return 0;
+ return ad5593r_read_word(i2c, AD5593R_MODE_REG_READBACK | reg, value);
}
static int ad5593r_gpio_read(struct ad5592r_state *st, u8 *value)
{
struct i2c_client *i2c = to_i2c_client(st->dev);
- s32 val;
+ u16 val;
+ int ret;
- val = i2c_smbus_read_word_swapped(i2c, AD5593R_MODE_GPIO_READBACK);
- if (val < 0)
- return (int) val;
+ ret = ad5593r_read_word(i2c, AD5593R_MODE_GPIO_READBACK, &val);
+ if (ret)
+ return ret;
*value = (u8) val;
@@ -94,6 +102,10 @@ static const struct ad5592r_rw_ops ad5593r_rw_ops = {
static int ad5593r_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
+ if (!i2c_check_functionality(i2c->adapter,
+ I2C_FUNC_SMBUS_BYTE | I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
return ad5592r_probe(&i2c->dev, id->name, &ad5593r_rw_ops);
}
diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c
index 135c8cedc33d..b27088464826 100644
--- a/drivers/iio/frequency/adf4371.c
+++ b/drivers/iio/frequency/adf4371.c
@@ -540,13 +540,6 @@ static int adf4371_setup(struct adf4371_state *st)
return regmap_bulk_write(st->regmap, ADF4371_REG(0x30), st->buf, 5);
}
-static void adf4371_clk_disable(void *data)
-{
- struct adf4371_state *st = data;
-
- clk_disable_unprepare(st->clkin);
-}
-
static int adf4371_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
@@ -579,18 +572,10 @@ static int adf4371_probe(struct spi_device *spi)
indio_dev->channels = st->chip_info->channels;
indio_dev->num_channels = st->chip_info->num_channels;
- st->clkin = devm_clk_get(&spi->dev, "clkin");
+ st->clkin = devm_clk_get_enabled(&spi->dev, "clkin");
if (IS_ERR(st->clkin))
return PTR_ERR(st->clkin);
- ret = clk_prepare_enable(st->clkin);
- if (ret < 0)
- return ret;
-
- ret = devm_add_action_or_reset(&spi->dev, adf4371_clk_disable, st);
- if (ret)
- return ret;
-
st->clkin_freq = clk_get_rate(st->clkin);
ret = adf4371_setup(st);
diff --git a/drivers/iio/frequency/admv1014.c b/drivers/iio/frequency/admv1014.c
index 865addd10db4..bb5e1feef42b 100644
--- a/drivers/iio/frequency/admv1014.c
+++ b/drivers/iio/frequency/admv1014.c
@@ -669,8 +669,7 @@ static int admv1014_init(struct admv1014_state *st)
chip_id = FIELD_GET(ADMV1014_CHIP_ID_MSK, chip_id);
if (chip_id != ADMV1014_CHIP_ID) {
dev_err(&spi->dev, "Invalid Chip ID.\n");
- ret = -EINVAL;
- return ret;
+ return -EINVAL;
}
ret = __admv1014_spi_update_bits(st, ADMV1014_REG_QUAD,
diff --git a/drivers/iio/frequency/adrf6780.c b/drivers/iio/frequency/adrf6780.c
index 21878bad0909..b4defb82f37e 100644
--- a/drivers/iio/frequency/adrf6780.c
+++ b/drivers/iio/frequency/adrf6780.c
@@ -441,11 +441,6 @@ static void adrf6780_properties_parse(struct adrf6780_state *st)
st->vdet_out_en = device_property_read_bool(&spi->dev, "adi,vdet-out-en");
}
-static void adrf6780_clk_disable(void *data)
-{
- clk_disable_unprepare(data);
-}
-
static void adrf6780_powerdown(void *data)
{
/* Disable all components in the Enable Register */
@@ -473,20 +468,11 @@ static int adrf6780_probe(struct spi_device *spi)
adrf6780_properties_parse(st);
- st->clkin = devm_clk_get(&spi->dev, "lo_in");
+ st->clkin = devm_clk_get_enabled(&spi->dev, "lo_in");
if (IS_ERR(st->clkin))
return dev_err_probe(&spi->dev, PTR_ERR(st->clkin),
"failed to get the LO input clock\n");
- ret = clk_prepare_enable(st->clkin);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(&spi->dev, adrf6780_clk_disable,
- st->clkin);
- if (ret)
- return ret;
-
mutex_init(&st->lock);
ret = adrf6780_init(st);
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 001ca2c3ff95..f1d7d4b5e222 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -52,6 +52,7 @@ config ADIS16480
ADIS16485, ADIS16488 inertial sensors.
source "drivers/iio/imu/bmi160/Kconfig"
+source "drivers/iio/imu/bno055/Kconfig"
config FXOS8700
tristate
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index c82748096c77..6eb612034722 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -15,6 +15,7 @@ adis_lib-$(CONFIG_IIO_ADIS_LIB_BUFFER) += adis_buffer.o
obj-$(CONFIG_IIO_ADIS_LIB) += adis_lib.o
obj-y += bmi160/
+obj-y += bno055/
obj-$(CONFIG_FXOS8700) += fxos8700_core.o
obj-$(CONFIG_FXOS8700_I2C) += fxos8700_i2c.o
diff --git a/drivers/iio/imu/adis16475.c b/drivers/iio/imu/adis16475.c
index ff2b0fab840a..aec55f7e1f26 100644
--- a/drivers/iio/imu/adis16475.c
+++ b/drivers/iio/imu/adis16475.c
@@ -1120,11 +1120,6 @@ check_burst32:
return IRQ_HANDLED;
}
-static void adis16475_disable_clk(void *data)
-{
- clk_disable_unprepare((struct clk *)data);
-}
-
static int adis16475_config_sync_mode(struct adis16475 *st)
{
int ret;
@@ -1150,19 +1145,11 @@ static int adis16475_config_sync_mode(struct adis16475 *st)
/* All the other modes require external input signal */
if (sync->sync_mode != ADIS16475_SYNC_OUTPUT) {
- struct clk *clk = devm_clk_get(dev, NULL);
+ struct clk *clk = devm_clk_get_enabled(dev, NULL);
if (IS_ERR(clk))
return PTR_ERR(clk);
- ret = clk_prepare_enable(clk);
- if (ret)
- return ret;
-
- ret = devm_add_action_or_reset(dev, adis16475_disable_clk, clk);
- if (ret)
- return ret;
-
st->clk_freq = clk_get_rate(clk);
if (st->clk_freq < sync->min_rate ||
st->clk_freq > sync->max_rate) {
diff --git a/drivers/iio/imu/bno055/Kconfig b/drivers/iio/imu/bno055/Kconfig
new file mode 100644
index 000000000000..fa79b1ac4f85
--- /dev/null
+++ b/drivers/iio/imu/bno055/Kconfig
@@ -0,0 +1,25 @@
+# SPDX-License-Identifier: GPL-2.0
+
+config BOSCH_BNO055
+ tristate
+
+config BOSCH_BNO055_SERIAL
+ tristate "Bosch BNO055 attached via UART"
+ depends on SERIAL_DEV_BUS
+ select BOSCH_BNO055
+ help
+ Enable this to support Bosch BNO055 IMUs attached via UART.
+
+ This driver can also be built as a module. If so, the module will be
+ called bno055_sl.
+
+config BOSCH_BNO055_I2C
+ tristate "Bosch BNO055 attached via I2C bus"
+ depends on I2C
+ select REGMAP_I2C
+ select BOSCH_BNO055
+ help
+ Enable this to support Bosch BNO055 IMUs attached via I2C bus.
+
+ This driver can also be built as a module. If so, the module will be
+ called bno055_i2c.
diff --git a/drivers/iio/imu/bno055/Makefile b/drivers/iio/imu/bno055/Makefile
new file mode 100644
index 000000000000..98c624730dae
--- /dev/null
+++ b/drivers/iio/imu/bno055/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_BOSCH_BNO055) += bno055.o
+obj-$(CONFIG_BOSCH_BNO055_SERIAL) += bno055_ser.o
+bno055_ser-y := bno055_ser_core.o
+# define_trace.h needs to know how to find our header
+CFLAGS_bno055_ser_trace.o := -I$(src)
+bno055_ser-$(CONFIG_TRACING) += bno055_ser_trace.o
+
+obj-$(CONFIG_BOSCH_BNO055_I2C) += bno055_i2c.o
diff --git a/drivers/iio/imu/bno055/bno055.c b/drivers/iio/imu/bno055/bno055.c
new file mode 100644
index 000000000000..307557a609e3
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055.c
@@ -0,0 +1,1685 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * IIO driver for Bosch BNO055 IMU
+ *
+ * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <andrea.merello@iit.it>
+ *
+ * Portions of this driver are taken from the BNO055 driver patch
+ * from Vlad Dogaru which is Copyright (c) 2016, Intel Corporation.
+ *
+ * This driver is also based on BMI160 driver, which is:
+ * Copyright (c) 2016, Intel Corporation.
+ * Copyright (c) 2019, Martin Kelly.
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bitmap.h>
+#include <linux/clk.h>
+#include <linux/debugfs.h>
+#include <linux/device.h>
+#include <linux/firmware.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/util_macros.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>
+
+#include "bno055.h"
+
+#define BNO055_FW_UID_FMT "bno055-caldata-%*phN.dat"
+#define BNO055_FW_GENERIC_NAME "bno055-caldata.dat"
+
+/* common registers */
+#define BNO055_PAGESEL_REG 0x7
+
+/* page 0 registers */
+#define BNO055_CHIP_ID_REG 0x0
+#define BNO055_CHIP_ID_MAGIC 0xA0
+#define BNO055_SW_REV_LSB_REG 0x4
+#define BNO055_SW_REV_MSB_REG 0x5
+#define BNO055_ACC_DATA_X_LSB_REG 0x8
+#define BNO055_ACC_DATA_Y_LSB_REG 0xA
+#define BNO055_ACC_DATA_Z_LSB_REG 0xC
+#define BNO055_MAG_DATA_X_LSB_REG 0xE
+#define BNO055_MAG_DATA_Y_LSB_REG 0x10
+#define BNO055_MAG_DATA_Z_LSB_REG 0x12
+#define BNO055_GYR_DATA_X_LSB_REG 0x14
+#define BNO055_GYR_DATA_Y_LSB_REG 0x16
+#define BNO055_GYR_DATA_Z_LSB_REG 0x18
+#define BNO055_EUL_DATA_X_LSB_REG 0x1A
+#define BNO055_EUL_DATA_Y_LSB_REG 0x1C
+#define BNO055_EUL_DATA_Z_LSB_REG 0x1E
+#define BNO055_QUAT_DATA_W_LSB_REG 0x20
+#define BNO055_LIA_DATA_X_LSB_REG 0x28
+#define BNO055_LIA_DATA_Y_LSB_REG 0x2A
+#define BNO055_LIA_DATA_Z_LSB_REG 0x2C
+#define BNO055_GRAVITY_DATA_X_LSB_REG 0x2E
+#define BNO055_GRAVITY_DATA_Y_LSB_REG 0x30
+#define BNO055_GRAVITY_DATA_Z_LSB_REG 0x32
+#define BNO055_SCAN_CH_COUNT ((BNO055_GRAVITY_DATA_Z_LSB_REG - BNO055_ACC_DATA_X_LSB_REG) / 2)
+#define BNO055_TEMP_REG 0x34
+#define BNO055_CALIB_STAT_REG 0x35
+#define BNO055_CALIB_STAT_MAGN_SHIFT 0
+#define BNO055_CALIB_STAT_ACCEL_SHIFT 2
+#define BNO055_CALIB_STAT_GYRO_SHIFT 4
+#define BNO055_CALIB_STAT_SYS_SHIFT 6
+#define BNO055_SYS_ERR_REG 0x3A
+#define BNO055_POWER_MODE_REG 0x3E
+#define BNO055_POWER_MODE_NORMAL 0
+#define BNO055_SYS_TRIGGER_REG 0x3F
+#define BNO055_SYS_TRIGGER_RST_SYS BIT(5)
+#define BNO055_SYS_TRIGGER_CLK_SEL BIT(7)
+#define BNO055_OPR_MODE_REG 0x3D
+#define BNO055_OPR_MODE_CONFIG 0x0
+#define BNO055_OPR_MODE_AMG 0x7
+#define BNO055_OPR_MODE_FUSION_FMC_OFF 0xB
+#define BNO055_OPR_MODE_FUSION 0xC
+#define BNO055_UNIT_SEL_REG 0x3B
+/* Android orientation mode means: pitch value decreases turning clockwise */
+#define BNO055_UNIT_SEL_ANDROID BIT(7)
+#define BNO055_UNIT_SEL_GYR_RPS BIT(1)
+#define BNO055_CALDATA_START 0x55
+#define BNO055_CALDATA_END 0x6A
+#define BNO055_CALDATA_LEN 22
+
+/*
+ * The difference in address between the register that contains the
+ * value and the register that contains the offset. This applies for
+ * accel, gyro and magn channels.
+ */
+#define BNO055_REG_OFFSET_ADDR 0x4D
+
+/* page 1 registers */
+#define BNO055_PG1(x) ((x) | 0x80)
+#define BNO055_ACC_CONFIG_REG BNO055_PG1(0x8)
+#define BNO055_ACC_CONFIG_LPF_MASK GENMASK(4, 2)
+#define BNO055_ACC_CONFIG_RANGE_MASK GENMASK(1, 0)
+#define BNO055_MAG_CONFIG_REG BNO055_PG1(0x9)
+#define BNO055_MAG_CONFIG_HIGHACCURACY 0x18
+#define BNO055_MAG_CONFIG_ODR_MASK GENMASK(2, 0)
+#define BNO055_GYR_CONFIG_REG BNO055_PG1(0xA)
+#define BNO055_GYR_CONFIG_RANGE_MASK GENMASK(2, 0)
+#define BNO055_GYR_CONFIG_LPF_MASK GENMASK(5, 3)
+#define BNO055_GYR_AM_SET_REG BNO055_PG1(0x1F)
+#define BNO055_UID_LOWER_REG BNO055_PG1(0x50)
+#define BNO055_UID_HIGHER_REG BNO055_PG1(0x5F)
+#define BNO055_UID_LEN 16
+
+struct bno055_sysfs_attr {
+ int *vals;
+ int len;
+ int *fusion_vals;
+ int *hw_xlate;
+ int type;
+};
+
+static int bno055_acc_lpf_vals[] = {
+ 7, 810000, 15, 630000, 31, 250000, 62, 500000,
+ 125, 0, 250, 0, 500, 0, 1000, 0,
+};
+
+static struct bno055_sysfs_attr bno055_acc_lpf = {
+ .vals = bno055_acc_lpf_vals,
+ .len = ARRAY_SIZE(bno055_acc_lpf_vals),
+ .fusion_vals = (int[]){62, 500000},
+ .type = IIO_VAL_INT_PLUS_MICRO,
+};
+
+static int bno055_acc_range_vals[] = {
+ /* G: 2, 4, 8, 16 */
+ 1962, 3924, 7848, 15696
+};
+
+static struct bno055_sysfs_attr bno055_acc_range = {
+ .vals = bno055_acc_range_vals,
+ .len = ARRAY_SIZE(bno055_acc_range_vals),
+ .fusion_vals = (int[]){3924}, /* 4G */
+ .type = IIO_VAL_INT,
+};
+
+/*
+ * Theoretically the IMU should return data in a given (i.e. fixed) unit
+ * regardless of the range setting. This happens for the accelerometer, but not
+ * for the gyroscope; the gyroscope range setting affects the scale.
+ * This is probably due to this[0] bug.
+ * For this reason we map the internal range setting onto the standard IIO scale
+ * attribute for gyro.
+ * Since the bug[0] may be fixed in future, we check for the IMU FW version and
+ * eventually warn the user.
+ * Currently we just don't care about "range" attributes for gyro.
+ *
+ * [0] https://community.bosch-sensortec.com/t5/MEMS-sensors-forum/BNO055-Wrong-sensitivity-resolution-in-datasheet/td-p/10266
+ */
+
+/*
+ * dps = hwval * (dps_range/2^15)
+ * rps = hwval * (rps_range/2^15)
+ * = hwval * (dps_range/(2^15 * k))
+ * where k is rad-to-deg factor
+ */
+static int bno055_gyr_scale_vals[] = {
+ 125, 1877467, 250, 1877467, 500, 1877467,
+ 1000, 1877467, 2000, 1877467,
+};
+
+static struct bno055_sysfs_attr bno055_gyr_scale = {
+ .vals = bno055_gyr_scale_vals,
+ .len = ARRAY_SIZE(bno055_gyr_scale_vals),
+ .fusion_vals = (int[]){1, 900},
+ .hw_xlate = (int[]){4, 3, 2, 1, 0},
+ .type = IIO_VAL_FRACTIONAL,
+};
+
+static int bno055_gyr_lpf_vals[] = {12, 23, 32, 47, 64, 116, 230, 523};
+static struct bno055_sysfs_attr bno055_gyr_lpf = {
+ .vals = bno055_gyr_lpf_vals,
+ .len = ARRAY_SIZE(bno055_gyr_lpf_vals),
+ .fusion_vals = (int[]){32},
+ .hw_xlate = (int[]){5, 4, 7, 3, 6, 2, 1, 0},
+ .type = IIO_VAL_INT,
+};
+
+static int bno055_mag_odr_vals[] = {2, 6, 8, 10, 15, 20, 25, 30};
+static struct bno055_sysfs_attr bno055_mag_odr = {
+ .vals = bno055_mag_odr_vals,
+ .len = ARRAY_SIZE(bno055_mag_odr_vals),
+ .fusion_vals = (int[]){20},
+ .type = IIO_VAL_INT,
+};
+
+struct bno055_priv {
+ struct regmap *regmap;
+ struct device *dev;
+ struct clk *clk;
+ int operation_mode;
+ int xfer_burst_break_thr;
+ struct mutex lock;
+ u8 uid[BNO055_UID_LEN];
+ struct gpio_desc *reset_gpio;
+ bool sw_reset;
+ struct {
+ __le16 chans[BNO055_SCAN_CH_COUNT];
+ s64 timestamp __aligned(8);
+ } buf;
+ struct dentry *debugfs;
+};
+
+static bool bno055_regmap_volatile(struct device *dev, unsigned int reg)
+{
+ /* data and status registers */
+ if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG)
+ return true;
+
+ /* when in fusion mode, config is updated by chip */
+ if (reg == BNO055_MAG_CONFIG_REG ||
+ reg == BNO055_ACC_CONFIG_REG ||
+ reg == BNO055_GYR_CONFIG_REG)
+ return true;
+
+ /* calibration data may be updated by the IMU */
+ if (reg >= BNO055_CALDATA_START && reg <= BNO055_CALDATA_END)
+ return true;
+
+ return false;
+}
+
+static bool bno055_regmap_readable(struct device *dev, unsigned int reg)
+{
+ /* unnamed PG0 reserved areas */
+ if ((reg < BNO055_PG1(0) && reg > BNO055_CALDATA_END) ||
+ reg == 0x3C)
+ return false;
+
+ /* unnamed PG1 reserved areas */
+ if (reg > BNO055_PG1(BNO055_UID_HIGHER_REG) ||
+ (reg < BNO055_PG1(BNO055_UID_LOWER_REG) && reg > BNO055_PG1(BNO055_GYR_AM_SET_REG)) ||
+ reg == BNO055_PG1(0xE) ||
+ (reg < BNO055_PG1(BNO055_PAGESEL_REG) && reg >= BNO055_PG1(0x0)))
+ return false;
+ return true;
+}
+
+static bool bno055_regmap_writeable(struct device *dev, unsigned int reg)
+{
+ /*
+ * Unreadable registers are indeed reserved; there are no WO regs
+ * (except for a single bit in SYS_TRIGGER register)
+ */
+ if (!bno055_regmap_readable(dev, reg))
+ return false;
+
+ /* data and status registers */
+ if (reg >= BNO055_ACC_DATA_X_LSB_REG && reg <= BNO055_SYS_ERR_REG)
+ return false;
+
+ /* ID areas */
+ if (reg < BNO055_PAGESEL_REG ||
+ (reg <= BNO055_UID_HIGHER_REG && reg >= BNO055_UID_LOWER_REG))
+ return false;
+
+ return true;
+}
+
+static const struct regmap_range_cfg bno055_regmap_ranges[] = {
+ {
+ .range_min = 0,
+ .range_max = 0x7f * 2,
+ .selector_reg = BNO055_PAGESEL_REG,
+ .selector_mask = GENMASK(7, 0),
+ .selector_shift = 0,
+ .window_start = 0,
+ .window_len = 0x80,
+ },
+};
+
+const struct regmap_config bno055_regmap_config = {
+ .name = "bno055",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .ranges = bno055_regmap_ranges,
+ .num_ranges = 1,
+ .volatile_reg = bno055_regmap_volatile,
+ .max_register = 0x80 * 2,
+ .writeable_reg = bno055_regmap_writeable,
+ .readable_reg = bno055_regmap_readable,
+ .cache_type = REGCACHE_RBTREE,
+};
+EXPORT_SYMBOL_NS_GPL(bno055_regmap_config, IIO_BNO055);
+
+/* must be called in configuration mode */
+static int bno055_calibration_load(struct bno055_priv *priv, const u8 *data, int len)
+{
+ if (len != BNO055_CALDATA_LEN) {
+ dev_dbg(priv->dev, "Invalid calibration file size %d (expected %d)",
+ len, BNO055_CALDATA_LEN);
+ return -EINVAL;
+ }
+
+ dev_dbg(priv->dev, "loading cal data: %*ph", BNO055_CALDATA_LEN, data);
+ return regmap_bulk_write(priv->regmap, BNO055_CALDATA_START,
+ data, BNO055_CALDATA_LEN);
+}
+
+static int bno055_operation_mode_do_set(struct bno055_priv *priv,
+ int operation_mode)
+{
+ int ret;
+
+ ret = regmap_write(priv->regmap, BNO055_OPR_MODE_REG,
+ operation_mode);
+ if (ret)
+ return ret;
+
+ /* Following datasheet specifications: sensor takes 7mS up to 19 mS to switch mode */
+ msleep(20);
+
+ return 0;
+}
+
+static int bno055_system_reset(struct bno055_priv *priv)
+{
+ int ret;
+
+ if (priv->reset_gpio) {
+ gpiod_set_value_cansleep(priv->reset_gpio, 0);
+ usleep_range(5000, 10000);
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+ } else if (priv->sw_reset) {
+ ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG,
+ BNO055_SYS_TRIGGER_RST_SYS);
+ if (ret)
+ return ret;
+ } else {
+ return 0;
+ }
+
+ regcache_drop_region(priv->regmap, 0x0, 0xff);
+ usleep_range(650000, 700000);
+
+ return 0;
+}
+
+static int bno055_init(struct bno055_priv *priv, const u8 *caldata, int len)
+{
+ int ret;
+
+ ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, BNO055_POWER_MODE_REG,
+ BNO055_POWER_MODE_NORMAL);
+ if (ret)
+ return ret;
+
+ ret = regmap_write(priv->regmap, BNO055_SYS_TRIGGER_REG,
+ priv->clk ? BNO055_SYS_TRIGGER_CLK_SEL : 0);
+ if (ret)
+ return ret;
+
+ /* use standard SI units */
+ ret = regmap_write(priv->regmap, BNO055_UNIT_SEL_REG,
+ BNO055_UNIT_SEL_ANDROID | BNO055_UNIT_SEL_GYR_RPS);
+ if (ret)
+ return ret;
+
+ if (caldata) {
+ ret = bno055_calibration_load(priv, caldata, len);
+ if (ret)
+ dev_warn(priv->dev, "failed to load calibration data with error %d\n",
+ ret);
+ }
+
+ return 0;
+}
+
+static ssize_t bno055_operation_mode_set(struct bno055_priv *priv,
+ int operation_mode)
+{
+ u8 caldata[BNO055_CALDATA_LEN];
+ int ret;
+
+ mutex_lock(&priv->lock);
+
+ ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+ if (ret)
+ goto exit_unlock;
+
+ if (operation_mode == BNO055_OPR_MODE_FUSION ||
+ operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF) {
+ /* for entering fusion mode, reset the chip to clear the algo state */
+ ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, caldata,
+ BNO055_CALDATA_LEN);
+ if (ret)
+ goto exit_unlock;
+
+ ret = bno055_system_reset(priv);
+ if (ret)
+ goto exit_unlock;
+
+ ret = bno055_init(priv, caldata, BNO055_CALDATA_LEN);
+ if (ret)
+ goto exit_unlock;
+ }
+
+ ret = bno055_operation_mode_do_set(priv, operation_mode);
+ if (ret)
+ goto exit_unlock;
+
+ priv->operation_mode = operation_mode;
+
+exit_unlock:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static void bno055_uninit(void *arg)
+{
+ struct bno055_priv *priv = arg;
+
+ /* stop the IMU */
+ bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+}
+
+#define BNO055_CHANNEL(_type, _axis, _index, _address, _sep, _sh, _avail) { \
+ .address = _address, \
+ .type = _type, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | (_sep), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | (_sh), \
+ .info_mask_shared_by_type_available = _avail, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ .repeat = IIO_MOD_##_axis == IIO_MOD_QUATERNION ? 4 : 0, \
+ }, \
+}
+
+/* scan indexes follow DATA register order */
+enum bno055_scan_axis {
+ BNO055_SCAN_ACCEL_X,
+ BNO055_SCAN_ACCEL_Y,
+ BNO055_SCAN_ACCEL_Z,
+ BNO055_SCAN_MAGN_X,
+ BNO055_SCAN_MAGN_Y,
+ BNO055_SCAN_MAGN_Z,
+ BNO055_SCAN_GYRO_X,
+ BNO055_SCAN_GYRO_Y,
+ BNO055_SCAN_GYRO_Z,
+ BNO055_SCAN_YAW,
+ BNO055_SCAN_ROLL,
+ BNO055_SCAN_PITCH,
+ BNO055_SCAN_QUATERNION,
+ BNO055_SCAN_LIA_X,
+ BNO055_SCAN_LIA_Y,
+ BNO055_SCAN_LIA_Z,
+ BNO055_SCAN_GRAVITY_X,
+ BNO055_SCAN_GRAVITY_Y,
+ BNO055_SCAN_GRAVITY_Z,
+ BNO055_SCAN_TIMESTAMP,
+ _BNO055_SCAN_MAX
+};
+
+static const struct iio_chan_spec bno055_channels[] = {
+ /* accelerometer */
+ BNO055_CHANNEL(IIO_ACCEL, X, BNO055_SCAN_ACCEL_X,
+ BNO055_ACC_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+ BNO055_CHANNEL(IIO_ACCEL, Y, BNO055_SCAN_ACCEL_Y,
+ BNO055_ACC_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+ BNO055_CHANNEL(IIO_ACCEL, Z, BNO055_SCAN_ACCEL_Z,
+ BNO055_ACC_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY)),
+ /* gyroscope */
+ BNO055_CHANNEL(IIO_ANGL_VEL, X, BNO055_SCAN_GYRO_X,
+ BNO055_GYR_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+ BIT(IIO_CHAN_INFO_SCALE)),
+ BNO055_CHANNEL(IIO_ANGL_VEL, Y, BNO055_SCAN_GYRO_Y,
+ BNO055_GYR_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+ BIT(IIO_CHAN_INFO_SCALE)),
+ BNO055_CHANNEL(IIO_ANGL_VEL, Z, BNO055_SCAN_GYRO_Z,
+ BNO055_GYR_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) |
+ BIT(IIO_CHAN_INFO_SCALE)),
+ /* magnetometer */
+ BNO055_CHANNEL(IIO_MAGN, X, BNO055_SCAN_MAGN_X,
+ BNO055_MAG_DATA_X_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+ BNO055_CHANNEL(IIO_MAGN, Y, BNO055_SCAN_MAGN_Y,
+ BNO055_MAG_DATA_Y_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+ BNO055_CHANNEL(IIO_MAGN, Z, BNO055_SCAN_MAGN_Z,
+ BNO055_MAG_DATA_Z_LSB_REG, BIT(IIO_CHAN_INFO_OFFSET),
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), BIT(IIO_CHAN_INFO_SAMP_FREQ)),
+ /* euler angle */
+ BNO055_CHANNEL(IIO_ROT, YAW, BNO055_SCAN_YAW,
+ BNO055_EUL_DATA_X_LSB_REG, 0, 0, 0),
+ BNO055_CHANNEL(IIO_ROT, ROLL, BNO055_SCAN_ROLL,
+ BNO055_EUL_DATA_Y_LSB_REG, 0, 0, 0),
+ BNO055_CHANNEL(IIO_ROT, PITCH, BNO055_SCAN_PITCH,
+ BNO055_EUL_DATA_Z_LSB_REG, 0, 0, 0),
+ /* quaternion */
+ BNO055_CHANNEL(IIO_ROT, QUATERNION, BNO055_SCAN_QUATERNION,
+ BNO055_QUAT_DATA_W_LSB_REG, 0, 0, 0),
+
+ /* linear acceleration */
+ BNO055_CHANNEL(IIO_ACCEL, LINEAR_X, BNO055_SCAN_LIA_X,
+ BNO055_LIA_DATA_X_LSB_REG, 0, 0, 0),
+ BNO055_CHANNEL(IIO_ACCEL, LINEAR_Y, BNO055_SCAN_LIA_Y,
+ BNO055_LIA_DATA_Y_LSB_REG, 0, 0, 0),
+ BNO055_CHANNEL(IIO_ACCEL, LINEAR_Z, BNO055_SCAN_LIA_Z,
+ BNO055_LIA_DATA_Z_LSB_REG, 0, 0, 0),
+
+ /* gravity vector */
+ BNO055_CHANNEL(IIO_GRAVITY, X, BNO055_SCAN_GRAVITY_X,
+ BNO055_GRAVITY_DATA_X_LSB_REG, 0, 0, 0),
+ BNO055_CHANNEL(IIO_GRAVITY, Y, BNO055_SCAN_GRAVITY_Y,
+ BNO055_GRAVITY_DATA_Y_LSB_REG, 0, 0, 0),
+ BNO055_CHANNEL(IIO_GRAVITY, Z, BNO055_SCAN_GRAVITY_Z,
+ BNO055_GRAVITY_DATA_Z_LSB_REG, 0, 0, 0),
+
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .scan_index = -1,
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(BNO055_SCAN_TIMESTAMP),
+};
+
+static int bno055_get_regmask(struct bno055_priv *priv, int *val, int *val2,
+ int reg, int mask, struct bno055_sysfs_attr *attr)
+{
+ const int shift = __ffs(mask);
+ int hwval, idx;
+ int ret;
+ int i;
+
+ ret = regmap_read(priv->regmap, reg, &hwval);
+ if (ret)
+ return ret;
+
+ idx = (hwval & mask) >> shift;
+ if (attr->hw_xlate)
+ for (i = 0; i < attr->len; i++)
+ if (attr->hw_xlate[i] == idx) {
+ idx = i;
+ break;
+ }
+ if (attr->type == IIO_VAL_INT) {
+ *val = attr->vals[idx];
+ } else { /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL */
+ *val = attr->vals[idx * 2];
+ *val2 = attr->vals[idx * 2 + 1];
+ }
+
+ return attr->type;
+}
+
+static int bno055_set_regmask(struct bno055_priv *priv, int val, int val2,
+ int reg, int mask, struct bno055_sysfs_attr *attr)
+{
+ const int shift = __ffs(mask);
+ int best_delta;
+ int req_val;
+ int tbl_val;
+ bool first;
+ int delta;
+ int hwval;
+ int ret;
+ int len;
+ int i;
+
+ /*
+ * The closest value the HW supports is only one in fusion mode,
+ * and it is autoselected, so don't do anything, just return OK,
+ * as the closest possible value has been (virtually) selected
+ */
+ if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+ return 0;
+
+ len = attr->len;
+
+ /*
+ * We always get a request in INT_PLUS_MICRO, but we
+ * take care of the micro part only when we really have
+ * non-integer tables. This prevents 32-bit overflow with
+ * larger integers contained in integer tables.
+ */
+ req_val = val;
+ if (attr->type != IIO_VAL_INT) {
+ len /= 2;
+ req_val = min(val, 2147) * 1000000 + val2;
+ }
+
+ first = true;
+ for (i = 0; i < len; i++) {
+ switch (attr->type) {
+ case IIO_VAL_INT:
+ tbl_val = attr->vals[i];
+ break;
+ case IIO_VAL_INT_PLUS_MICRO:
+ WARN_ON(attr->vals[i * 2] > 2147);
+ tbl_val = attr->vals[i * 2] * 1000000 +
+ attr->vals[i * 2 + 1];
+ break;
+ case IIO_VAL_FRACTIONAL:
+ WARN_ON(attr->vals[i * 2] > 4294);
+ tbl_val = attr->vals[i * 2] * 1000000 /
+ attr->vals[i * 2 + 1];
+ break;
+ default:
+ return -EINVAL;
+ }
+ delta = abs(tbl_val - req_val);
+ if (delta < best_delta || first) {
+ best_delta = delta;
+ hwval = i;
+ first = false;
+ }
+ }
+
+ if (attr->hw_xlate)
+ hwval = attr->hw_xlate[hwval];
+
+ ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+ if (ret)
+ return ret;
+
+ ret = regmap_update_bits(priv->regmap, reg, mask, hwval << shift);
+ if (ret)
+ return ret;
+
+ return bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_AMG);
+}
+
+static int bno055_read_simple_chan(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct bno055_priv *priv = iio_priv(indio_dev);
+ __le16 raw_val;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = regmap_bulk_read(priv->regmap, chan->address,
+ &raw_val, sizeof(raw_val));
+ if (ret < 0)
+ return ret;
+ *val = sign_extend32(le16_to_cpu(raw_val), 15);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+ *val = 0;
+ } else {
+ ret = regmap_bulk_read(priv->regmap,
+ chan->address +
+ BNO055_REG_OFFSET_ADDR,
+ &raw_val, sizeof(raw_val));
+ if (ret < 0)
+ return ret;
+ /*
+ * IMU reports sensor offsets; IIO wants correction
+ * offsets, thus we need the 'minus' here.
+ */
+ *val = -sign_extend32(le16_to_cpu(raw_val), 15);
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1;
+ switch (chan->type) {
+ case IIO_GRAVITY:
+ /* Table 3-35: 1 m/s^2 = 100 LSB */
+ case IIO_ACCEL:
+ /* Table 3-17: 1 m/s^2 = 100 LSB */
+ *val2 = 100;
+ break;
+ case IIO_MAGN:
+ /*
+ * Table 3-19: 1 uT = 16 LSB. But we need
+ * Gauss: 1G = 0.1 uT.
+ */
+ *val2 = 160;
+ break;
+ case IIO_ANGL_VEL:
+ /*
+ * Table 3-22: 1 Rps = 900 LSB
+ * .. but this is not exactly true. See comment at the
+ * beginning of this file.
+ */
+ if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+ *val = bno055_gyr_scale.fusion_vals[0];
+ *val2 = bno055_gyr_scale.fusion_vals[1];
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return bno055_get_regmask(priv, val, val2,
+ BNO055_GYR_CONFIG_REG,
+ BNO055_GYR_CONFIG_RANGE_MASK,
+ &bno055_gyr_scale);
+ break;
+ case IIO_ROT:
+ /* Table 3-28: 1 degree = 16 LSB */
+ *val2 = 16;
+ break;
+ default:
+ return -EINVAL;
+ }
+ return IIO_VAL_FRACTIONAL;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (chan->type != IIO_MAGN)
+ return -EINVAL;
+
+ return bno055_get_regmask(priv, val, val2,
+ BNO055_MAG_CONFIG_REG,
+ BNO055_MAG_CONFIG_ODR_MASK,
+ &bno055_mag_odr);
+
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ return bno055_get_regmask(priv, val, val2,
+ BNO055_GYR_CONFIG_REG,
+ BNO055_GYR_CONFIG_LPF_MASK,
+ &bno055_gyr_lpf);
+ case IIO_ACCEL:
+ return bno055_get_regmask(priv, val, val2,
+ BNO055_ACC_CONFIG_REG,
+ BNO055_ACC_CONFIG_LPF_MASK,
+ &bno055_acc_lpf);
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bno055_sysfs_attr_avail(struct bno055_priv *priv, struct bno055_sysfs_attr *attr,
+ const int **vals, int *length)
+{
+ if (priv->operation_mode != BNO055_OPR_MODE_AMG) {
+ /* locked when fusion enabled */
+ *vals = attr->fusion_vals;
+ if (attr->type == IIO_VAL_INT)
+ *length = 1;
+ else
+ *length = 2; /* IIO_VAL_INT_PLUS_MICRO or IIO_VAL_FRACTIONAL*/
+ } else {
+ *vals = attr->vals;
+ *length = attr->len;
+ }
+
+ return attr->type;
+}
+
+static int bno055_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct bno055_priv *priv = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *type = bno055_sysfs_attr_avail(priv, &bno055_gyr_scale,
+ vals, length);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *type = bno055_sysfs_attr_avail(priv, &bno055_gyr_lpf,
+ vals, length);
+ return IIO_AVAIL_LIST;
+ case IIO_ACCEL:
+ *type = bno055_sysfs_attr_avail(priv, &bno055_acc_lpf,
+ vals, length);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->type) {
+ case IIO_MAGN:
+ *type = bno055_sysfs_attr_avail(priv, &bno055_mag_odr,
+ vals, length);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bno055_read_temp_chan(struct iio_dev *indio_dev, int *val)
+{
+ struct bno055_priv *priv = iio_priv(indio_dev);
+ unsigned int raw_val;
+ int ret;
+
+ ret = regmap_read(priv->regmap, BNO055_TEMP_REG, &raw_val);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Tables 3-36 and 3-37: one byte of priv, signed, 1 LSB = 1C.
+ * ABI wants milliC.
+ */
+ *val = raw_val * 1000;
+
+ return IIO_VAL_INT;
+}
+
+static int bno055_read_quaternion(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int size, int *vals, int *val_len,
+ long mask)
+{
+ struct bno055_priv *priv = iio_priv(indio_dev);
+ __le16 raw_vals[4];
+ int i, ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (size < 4)
+ return -EINVAL;
+ ret = regmap_bulk_read(priv->regmap,
+ BNO055_QUAT_DATA_W_LSB_REG,
+ raw_vals, sizeof(raw_vals));
+ if (ret < 0)
+ return ret;
+ for (i = 0; i < 4; i++)
+ vals[i] = sign_extend32(le16_to_cpu(raw_vals[i]), 15);
+ *val_len = 4;
+ return IIO_VAL_INT_MULTIPLE;
+ case IIO_CHAN_INFO_SCALE:
+ /* Table 3-31: 1 quaternion = 2^14 LSB */
+ if (size < 2)
+ return -EINVAL;
+ vals[0] = 1;
+ vals[1] = 14;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static bool bno055_is_chan_readable(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan)
+{
+ struct bno055_priv *priv = iio_priv(indio_dev);
+
+ if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+ return true;
+
+ switch (chan->type) {
+ case IIO_GRAVITY:
+ case IIO_ROT:
+ return false;
+ case IIO_ACCEL:
+ if (chan->channel2 == IIO_MOD_LINEAR_X ||
+ chan->channel2 == IIO_MOD_LINEAR_Y ||
+ chan->channel2 == IIO_MOD_LINEAR_Z)
+ return false;
+ return true;
+ default:
+ return true;
+ }
+}
+
+static int _bno055_read_raw_multi(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int size, int *vals, int *val_len,
+ long mask)
+{
+ if (!bno055_is_chan_readable(indio_dev, chan))
+ return -EBUSY;
+
+ switch (chan->type) {
+ case IIO_MAGN:
+ case IIO_ACCEL:
+ case IIO_ANGL_VEL:
+ case IIO_GRAVITY:
+ if (size < 2)
+ return -EINVAL;
+ *val_len = 2;
+ return bno055_read_simple_chan(indio_dev, chan,
+ &vals[0], &vals[1],
+ mask);
+ case IIO_TEMP:
+ *val_len = 1;
+ return bno055_read_temp_chan(indio_dev, &vals[0]);
+ case IIO_ROT:
+ /*
+ * Rotation is exposed as either a quaternion or three
+ * Euler angles.
+ */
+ if (chan->channel2 == IIO_MOD_QUATERNION)
+ return bno055_read_quaternion(indio_dev, chan,
+ size, vals,
+ val_len, mask);
+ if (size < 2)
+ return -EINVAL;
+ *val_len = 2;
+ return bno055_read_simple_chan(indio_dev, chan,
+ &vals[0], &vals[1],
+ mask);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bno055_read_raw_multi(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int size, int *vals, int *val_len,
+ long mask)
+{
+ struct bno055_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&priv->lock);
+ ret = _bno055_read_raw_multi(indio_dev, chan, size,
+ vals, val_len, mask);
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static int _bno055_write_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct bno055_priv *priv = iio_priv(iio_dev);
+
+ switch (chan->type) {
+ case IIO_MAGN:
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return bno055_set_regmask(priv, val, val2,
+ BNO055_MAG_CONFIG_REG,
+ BNO055_MAG_CONFIG_ODR_MASK,
+ &bno055_mag_odr);
+ default:
+ return -EINVAL;
+ }
+ case IIO_ACCEL:
+ switch (mask) {
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return bno055_set_regmask(priv, val, val2,
+ BNO055_ACC_CONFIG_REG,
+ BNO055_ACC_CONFIG_LPF_MASK,
+ &bno055_acc_lpf);
+
+ default:
+ return -EINVAL;
+ }
+ case IIO_ANGL_VEL:
+ switch (mask) {
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return bno055_set_regmask(priv, val, val2,
+ BNO055_GYR_CONFIG_REG,
+ BNO055_GYR_CONFIG_LPF_MASK,
+ &bno055_gyr_lpf);
+ case IIO_CHAN_INFO_SCALE:
+ return bno055_set_regmask(priv, val, val2,
+ BNO055_GYR_CONFIG_REG,
+ BNO055_GYR_CONFIG_RANGE_MASK,
+ &bno055_gyr_scale);
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bno055_write_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct bno055_priv *priv = iio_priv(iio_dev);
+ int ret;
+
+ mutex_lock(&priv->lock);
+ ret = _bno055_write_raw(iio_dev, chan, val, val2, mask);
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+static ssize_t in_accel_range_raw_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+ int len = 0;
+ int i;
+
+ if (priv->operation_mode != BNO055_OPR_MODE_AMG)
+ return sysfs_emit(buf, "%d\n", bno055_acc_range.fusion_vals[0]);
+
+ for (i = 0; i < bno055_acc_range.len; i++)
+ len += sysfs_emit_at(buf, len, "%d ", bno055_acc_range.vals[i]);
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t fusion_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+ return sysfs_emit(buf, "%d\n",
+ priv->operation_mode != BNO055_OPR_MODE_AMG);
+}
+
+static ssize_t fusion_enable_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 bno055_priv *priv = iio_priv(indio_dev);
+ bool en;
+ int ret;
+
+ if (indio_dev->active_scan_mask &&
+ !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX))
+ return -EBUSY;
+
+ ret = kstrtobool(buf, &en);
+ if (ret)
+ return -EINVAL;
+
+ if (!en)
+ return bno055_operation_mode_set(priv, BNO055_OPR_MODE_AMG) ?: len;
+
+ /*
+ * Coming from AMG means the FMC was off, just switch to fusion but
+ * don't change anything that doesn't belong to us (i.e let FMC stay off).
+ * Coming from any other fusion mode means we don't need to do anything.
+ */
+ if (priv->operation_mode == BNO055_OPR_MODE_AMG)
+ return bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF) ?: len;
+
+ return len;
+}
+
+static ssize_t in_magn_calibration_fast_enable_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+ return sysfs_emit(buf, "%d\n",
+ priv->operation_mode == BNO055_OPR_MODE_FUSION);
+}
+
+static ssize_t in_magn_calibration_fast_enable_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 bno055_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ if (indio_dev->active_scan_mask &&
+ !bitmap_empty(indio_dev->active_scan_mask, _BNO055_SCAN_MAX))
+ return -EBUSY;
+
+ if (sysfs_streq(buf, "0")) {
+ if (priv->operation_mode == BNO055_OPR_MODE_FUSION) {
+ ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION_FMC_OFF);
+ if (ret)
+ return ret;
+ }
+ } else {
+ if (priv->operation_mode == BNO055_OPR_MODE_AMG)
+ return -EINVAL;
+
+ if (priv->operation_mode != BNO055_OPR_MODE_FUSION) {
+ ret = bno055_operation_mode_set(priv, BNO055_OPR_MODE_FUSION);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return len;
+}
+
+static ssize_t in_accel_range_raw_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+ int val;
+ int ret;
+
+ ret = bno055_get_regmask(priv, &val, NULL,
+ BNO055_ACC_CONFIG_REG,
+ BNO055_ACC_CONFIG_RANGE_MASK,
+ &bno055_acc_range);
+ if (ret < 0)
+ return ret;
+
+ return sysfs_emit(buf, "%d\n", val);
+}
+
+static ssize_t in_accel_range_raw_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+ unsigned long val;
+ int ret;
+
+ ret = kstrtoul(buf, 10, &val);
+ if (ret)
+ return ret;
+
+ mutex_lock(&priv->lock);
+ ret = bno055_set_regmask(priv, val, 0,
+ BNO055_ACC_CONFIG_REG,
+ BNO055_ACC_CONFIG_RANGE_MASK,
+ &bno055_acc_range);
+ mutex_unlock(&priv->lock);
+
+ return ret ?: len;
+}
+
+static ssize_t bno055_get_calib_status(struct device *dev, char *buf, int which)
+{
+ struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+ int calib;
+ int ret;
+ int val;
+
+ if (priv->operation_mode == BNO055_OPR_MODE_AMG ||
+ (priv->operation_mode == BNO055_OPR_MODE_FUSION_FMC_OFF &&
+ which == BNO055_CALIB_STAT_MAGN_SHIFT)) {
+ calib = 0;
+ } else {
+ mutex_lock(&priv->lock);
+ ret = regmap_read(priv->regmap, BNO055_CALIB_STAT_REG, &val);
+ mutex_unlock(&priv->lock);
+
+ if (ret)
+ return -EIO;
+
+ calib = ((val >> which) & GENMASK(1, 0)) + 1;
+ }
+
+ return sysfs_emit(buf, "%d\n", calib);
+}
+
+static ssize_t serialnumber_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct bno055_priv *priv = iio_priv(dev_to_iio_dev(dev));
+
+ return sysfs_emit(buf, "%*ph\n", BNO055_UID_LEN, priv->uid);
+}
+
+static ssize_t calibration_data_read(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr, char *buf,
+ loff_t pos, size_t count)
+{
+ struct bno055_priv *priv = iio_priv(dev_to_iio_dev(kobj_to_dev(kobj)));
+ u8 data[BNO055_CALDATA_LEN];
+ int ret;
+
+ /*
+ * Calibration data is volatile; reading it in chunks will possibly
+ * results in inconsistent data. We require the user to read the whole
+ * blob in a single chunk
+ */
+ if (count < BNO055_CALDATA_LEN || pos)
+ return -EINVAL;
+
+ mutex_lock(&priv->lock);
+ ret = bno055_operation_mode_do_set(priv, BNO055_OPR_MODE_CONFIG);
+ if (ret)
+ goto exit_unlock;
+
+ ret = regmap_bulk_read(priv->regmap, BNO055_CALDATA_START, data,
+ BNO055_CALDATA_LEN);
+ if (ret)
+ goto exit_unlock;
+
+ ret = bno055_operation_mode_do_set(priv, priv->operation_mode);
+ if (ret)
+ goto exit_unlock;
+
+ memcpy(buf, data, BNO055_CALDATA_LEN);
+
+ ret = BNO055_CALDATA_LEN;
+exit_unlock:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static ssize_t sys_calibration_auto_status_show(struct device *dev,
+ struct device_attribute *a,
+ char *buf)
+{
+ return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_SYS_SHIFT);
+}
+
+static ssize_t in_accel_calibration_auto_status_show(struct device *dev,
+ struct device_attribute *a,
+ char *buf)
+{
+ return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_ACCEL_SHIFT);
+}
+
+static ssize_t in_gyro_calibration_auto_status_show(struct device *dev,
+ struct device_attribute *a,
+ char *buf)
+{
+ return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_GYRO_SHIFT);
+}
+
+static ssize_t in_magn_calibration_auto_status_show(struct device *dev,
+ struct device_attribute *a,
+ char *buf)
+{
+ return bno055_get_calib_status(dev, buf, BNO055_CALIB_STAT_MAGN_SHIFT);
+}
+
+static int bno055_debugfs_reg_access(struct iio_dev *iio_dev, unsigned int reg,
+ unsigned int writeval, unsigned int *readval)
+{
+ struct bno055_priv *priv = iio_priv(iio_dev);
+
+ if (readval)
+ return regmap_read(priv->regmap, reg, readval);
+ else
+ return regmap_write(priv->regmap, reg, writeval);
+}
+
+static ssize_t bno055_show_fw_version(struct file *file, char __user *userbuf,
+ size_t count, loff_t *ppos)
+{
+ struct bno055_priv *priv = file->private_data;
+ int rev, ver;
+ char *buf;
+ int ret;
+
+ ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
+ if (ret)
+ return ret;
+
+ buf = kasprintf(GFP_KERNEL, "ver: 0x%x, rev: 0x%x\n", ver, rev);
+ if (!buf)
+ return -ENOMEM;
+
+ ret = simple_read_from_buffer(userbuf, count, ppos, buf, strlen(buf));
+ kfree(buf);
+
+ return ret;
+}
+
+static const struct file_operations bno055_fw_version_ops = {
+ .open = simple_open,
+ .read = bno055_show_fw_version,
+ .llseek = default_llseek,
+ .owner = THIS_MODULE,
+};
+
+static void bno055_debugfs_remove(void *_priv)
+{
+ struct bno055_priv *priv = _priv;
+
+ debugfs_remove(priv->debugfs);
+ priv->debugfs = NULL;
+}
+
+static void bno055_debugfs_init(struct iio_dev *iio_dev)
+{
+ struct bno055_priv *priv = iio_priv(iio_dev);
+
+ priv->debugfs = debugfs_create_file("firmware_version", 0400,
+ iio_get_debugfs_dentry(iio_dev),
+ priv, &bno055_fw_version_ops);
+ if (!IS_ERR(priv->debugfs))
+ devm_add_action_or_reset(priv->dev, bno055_debugfs_remove,
+ priv);
+ if (IS_ERR_OR_NULL(priv->debugfs))
+ dev_warn(priv->dev, "failed to setup debugfs");
+}
+
+static IIO_DEVICE_ATTR_RW(fusion_enable, 0);
+static IIO_DEVICE_ATTR_RW(in_magn_calibration_fast_enable, 0);
+static IIO_DEVICE_ATTR_RW(in_accel_range_raw, 0);
+
+static IIO_DEVICE_ATTR_RO(in_accel_range_raw_available, 0);
+static IIO_DEVICE_ATTR_RO(sys_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_accel_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_gyro_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(in_magn_calibration_auto_status, 0);
+static IIO_DEVICE_ATTR_RO(serialnumber, 0);
+
+static struct attribute *bno055_attrs[] = {
+ &iio_dev_attr_in_accel_range_raw_available.dev_attr.attr,
+ &iio_dev_attr_in_accel_range_raw.dev_attr.attr,
+ &iio_dev_attr_fusion_enable.dev_attr.attr,
+ &iio_dev_attr_in_magn_calibration_fast_enable.dev_attr.attr,
+ &iio_dev_attr_sys_calibration_auto_status.dev_attr.attr,
+ &iio_dev_attr_in_accel_calibration_auto_status.dev_attr.attr,
+ &iio_dev_attr_in_gyro_calibration_auto_status.dev_attr.attr,
+ &iio_dev_attr_in_magn_calibration_auto_status.dev_attr.attr,
+ &iio_dev_attr_serialnumber.dev_attr.attr,
+ NULL
+};
+
+static BIN_ATTR_RO(calibration_data, BNO055_CALDATA_LEN);
+
+static struct bin_attribute *bno055_bin_attrs[] = {
+ &bin_attr_calibration_data,
+ NULL
+};
+
+static const struct attribute_group bno055_attrs_group = {
+ .attrs = bno055_attrs,
+ .bin_attrs = bno055_bin_attrs,
+};
+
+static const struct iio_info bno055_info = {
+ .read_raw_multi = bno055_read_raw_multi,
+ .read_avail = bno055_read_avail,
+ .write_raw = bno055_write_raw,
+ .attrs = &bno055_attrs_group,
+ .debugfs_reg_access = bno055_debugfs_reg_access,
+};
+
+/*
+ * Reads len samples from the HW, stores them in buf starting from buf_idx,
+ * and applies mask to cull (skip) unneeded samples.
+ * Updates buf_idx incrementing with the number of stored samples.
+ * Samples from HW are transferred into buf, then in-place copy on buf is
+ * performed in order to cull samples that need to be skipped.
+ * This avoids copies of the first samples until we hit the 1st sample to skip,
+ * and also avoids having an extra bounce buffer.
+ * buf must be able to contain len elements in spite of how many samples we are
+ * going to cull.
+ */
+static int bno055_scan_xfer(struct bno055_priv *priv,
+ int start_ch, int len, unsigned long mask,
+ __le16 *buf, int *buf_idx)
+{
+ const int base = BNO055_ACC_DATA_X_LSB_REG;
+ bool quat_in_read = false;
+ int buf_base = *buf_idx;
+ __le16 *dst, *src;
+ int offs_fixup = 0;
+ int xfer_len = len;
+ int ret;
+ int i, n;
+
+ if (!mask)
+ return 0;
+
+ /*
+ * All channels are made up 1 16-bit sample, except for quaternion that
+ * is made up 4 16-bit values.
+ * For us the quaternion CH is just like 4 regular CHs.
+ * If our read starts past the quaternion make sure to adjust the
+ * starting offset; if the quaternion is contained in our scan then make
+ * sure to adjust the read len.
+ */
+ if (start_ch > BNO055_SCAN_QUATERNION) {
+ start_ch += 3;
+ } else if ((start_ch <= BNO055_SCAN_QUATERNION) &&
+ ((start_ch + len) > BNO055_SCAN_QUATERNION)) {
+ quat_in_read = true;
+ xfer_len += 3;
+ }
+
+ ret = regmap_bulk_read(priv->regmap,
+ base + start_ch * sizeof(__le16),
+ buf + buf_base,
+ xfer_len * sizeof(__le16));
+ if (ret)
+ return ret;
+
+ for_each_set_bit(i, &mask, len) {
+ if (quat_in_read && ((start_ch + i) > BNO055_SCAN_QUATERNION))
+ offs_fixup = 3;
+
+ dst = buf + *buf_idx;
+ src = buf + buf_base + offs_fixup + i;
+
+ n = (start_ch + i == BNO055_SCAN_QUATERNION) ? 4 : 1;
+
+ if (dst != src)
+ memcpy(dst, src, n * sizeof(__le16));
+
+ *buf_idx += n;
+ }
+ return 0;
+}
+
+static irqreturn_t bno055_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *iio_dev = pf->indio_dev;
+ struct bno055_priv *priv = iio_priv(iio_dev);
+ int xfer_start, start, end, prev_end;
+ unsigned long mask;
+ int quat_extra_len;
+ bool first = true;
+ int buf_idx = 0;
+ bool thr_hit;
+ int ret;
+
+ mutex_lock(&priv->lock);
+
+ /*
+ * Walk the bitmap and eventually perform several transfers.
+ * Bitmap ones-fields that are separated by gaps <= xfer_burst_break_thr
+ * will be included in same transfer.
+ * Every time the bitmap contains a gap wider than xfer_burst_break_thr
+ * then we split the transfer, skipping the gap.
+ */
+ for_each_set_bitrange(start, end, iio_dev->active_scan_mask,
+ iio_dev->masklength) {
+ /*
+ * First transfer will start from the beginning of the first
+ * ones-field in the bitmap
+ */
+ if (first) {
+ xfer_start = start;
+ } else {
+ /*
+ * We found the next ones-field; check whether to
+ * include it in * the current transfer or not (i.e.
+ * let's perform the current * transfer and prepare for
+ * another one).
+ */
+
+ /*
+ * In case the zeros-gap contains the quaternion bit,
+ * then its length is actually 4 words instead of 1
+ * (i.e. +3 wrt other channels).
+ */
+ quat_extra_len = ((start > BNO055_SCAN_QUATERNION) &&
+ (prev_end <= BNO055_SCAN_QUATERNION)) ? 3 : 0;
+
+ /* If the gap is wider than xfer_burst_break_thr then.. */
+ thr_hit = (start - prev_end + quat_extra_len) >
+ priv->xfer_burst_break_thr;
+
+ /*
+ * .. transfer all the data up to the gap. Then set the
+ * next transfer start index at right after the gap
+ * (i.e. at the start of this ones-field).
+ */
+ if (thr_hit) {
+ mask = *iio_dev->active_scan_mask >> xfer_start;
+ ret = bno055_scan_xfer(priv, xfer_start,
+ prev_end - xfer_start,
+ mask, priv->buf.chans, &buf_idx);
+ if (ret)
+ goto done;
+ xfer_start = start;
+ }
+ }
+ first = false;
+ prev_end = end;
+ }
+
+ /*
+ * We finished walking the bitmap; no more gaps to check for. Just
+ * perform the current transfer.
+ */
+ mask = *iio_dev->active_scan_mask >> xfer_start;
+ ret = bno055_scan_xfer(priv, xfer_start,
+ prev_end - xfer_start,
+ mask, priv->buf.chans, &buf_idx);
+
+ if (!ret)
+ iio_push_to_buffers_with_timestamp(iio_dev,
+ &priv->buf, pf->timestamp);
+done:
+ mutex_unlock(&priv->lock);
+ iio_trigger_notify_done(iio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int bno055_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct bno055_priv *priv = iio_priv(indio_dev);
+ const unsigned long fusion_mask =
+ BIT(BNO055_SCAN_YAW) |
+ BIT(BNO055_SCAN_ROLL) |
+ BIT(BNO055_SCAN_PITCH) |
+ BIT(BNO055_SCAN_QUATERNION) |
+ BIT(BNO055_SCAN_LIA_X) |
+ BIT(BNO055_SCAN_LIA_Y) |
+ BIT(BNO055_SCAN_LIA_Z) |
+ BIT(BNO055_SCAN_GRAVITY_X) |
+ BIT(BNO055_SCAN_GRAVITY_Y) |
+ BIT(BNO055_SCAN_GRAVITY_Z);
+
+ if (priv->operation_mode == BNO055_OPR_MODE_AMG &&
+ bitmap_intersects(indio_dev->active_scan_mask, &fusion_mask,
+ _BNO055_SCAN_MAX))
+ return -EBUSY;
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops bno055_buffer_setup_ops = {
+ .preenable = bno055_buffer_preenable,
+};
+
+int bno055_probe(struct device *dev, struct regmap *regmap,
+ int xfer_burst_break_thr, bool sw_reset)
+{
+ const struct firmware *caldata = NULL;
+ struct bno055_priv *priv;
+ struct iio_dev *iio_dev;
+ char *fw_name_buf;
+ unsigned int val;
+ int rev, ver;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ iio_dev->name = "bno055";
+ priv = iio_priv(iio_dev);
+ mutex_init(&priv->lock);
+ priv->regmap = regmap;
+ priv->dev = dev;
+ priv->xfer_burst_break_thr = xfer_burst_break_thr;
+ priv->sw_reset = sw_reset;
+
+ priv->reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->reset_gpio))
+ return dev_err_probe(dev, PTR_ERR(priv->reset_gpio), "Failed to get reset GPIO\n");
+
+ priv->clk = devm_clk_get_optional_enabled(dev, "clk");
+ if (IS_ERR(priv->clk))
+ return dev_err_probe(dev, PTR_ERR(priv->clk), "Failed to get CLK\n");
+
+ if (priv->reset_gpio) {
+ usleep_range(5000, 10000);
+ gpiod_set_value_cansleep(priv->reset_gpio, 1);
+ usleep_range(650000, 750000);
+ } else if (!sw_reset) {
+ dev_warn(dev, "No usable reset method; IMU may be unreliable\n");
+ }
+
+ ret = regmap_read(priv->regmap, BNO055_CHIP_ID_REG, &val);
+ if (ret)
+ return ret;
+
+ if (val != BNO055_CHIP_ID_MAGIC)
+ dev_warn(dev, "Unrecognized chip ID 0x%x\n", val);
+
+ /*
+ * In case we haven't a HW reset pin, we can still reset the chip via
+ * register write. This is probably nonsense in case we can't even
+ * communicate with the chip or the chip isn't the one we expect (i.e.
+ * we don't write to unknown chips), so we perform SW reset only after
+ * chip magic ID check
+ */
+ if (!priv->reset_gpio) {
+ ret = bno055_system_reset(priv);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_read(priv->regmap, BNO055_SW_REV_LSB_REG, &rev);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(priv->regmap, BNO055_SW_REV_MSB_REG, &ver);
+ if (ret)
+ return ret;
+
+ /*
+ * The stock FW version contains a bug (see comment at the beginning of
+ * this file) that causes the anglvel scale to be changed depending on
+ * the chip range setting. We workaround this, but we don't know what
+ * other FW versions might do.
+ */
+ if (ver != 0x3 || rev != 0x11)
+ dev_warn(dev, "Untested firmware version. Anglvel scale may not work as expected\n");
+
+ ret = regmap_bulk_read(priv->regmap, BNO055_UID_LOWER_REG,
+ priv->uid, BNO055_UID_LEN);
+ if (ret)
+ return ret;
+
+ /* Sensor calibration data */
+ fw_name_buf = kasprintf(GFP_KERNEL, BNO055_FW_UID_FMT,
+ BNO055_UID_LEN, priv->uid);
+ if (!fw_name_buf)
+ return -ENOMEM;
+
+ ret = request_firmware(&caldata, fw_name_buf, dev);
+ kfree(fw_name_buf);
+ if (ret)
+ ret = request_firmware(&caldata, BNO055_FW_GENERIC_NAME, dev);
+ if (ret) {
+ dev_notice(dev, "Calibration file load failed. See instruction in kernel Documentation/iio/bno055.rst\n");
+ ret = bno055_init(priv, NULL, 0);
+ } else {
+ ret = bno055_init(priv, caldata->data, caldata->size);
+ release_firmware(caldata);
+ }
+ if (ret)
+ return ret;
+
+ priv->operation_mode = BNO055_OPR_MODE_FUSION;
+ ret = bno055_operation_mode_do_set(priv, priv->operation_mode);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, bno055_uninit, priv);
+ if (ret)
+ return ret;
+
+ iio_dev->channels = bno055_channels;
+ iio_dev->num_channels = ARRAY_SIZE(bno055_channels);
+ iio_dev->info = &bno055_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = devm_iio_triggered_buffer_setup(dev, iio_dev,
+ iio_pollfunc_store_time,
+ bno055_trigger_handler,
+ &bno055_buffer_setup_ops);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(dev, iio_dev);
+ if (ret)
+ return ret;
+
+ bno055_debugfs_init(iio_dev);
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(bno055_probe, IIO_BNO055);
+
+MODULE_AUTHOR("Andrea Merello <andrea.merello@iit.it>");
+MODULE_DESCRIPTION("Bosch BNO055 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/bno055/bno055.h b/drivers/iio/imu/bno055/bno055.h
new file mode 100644
index 000000000000..64f9fc95cebc
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __BNO055_H__
+#define __BNO055_H__
+
+#include <linux/regmap.h>
+#include <linux/types.h>
+
+struct device;
+int bno055_probe(struct device *dev, struct regmap *regmap,
+ int xfer_burst_break_thr, bool sw_reset);
+extern const struct regmap_config bno055_regmap_config;
+
+#endif
diff --git a/drivers/iio/imu/bno055/bno055_i2c.c b/drivers/iio/imu/bno055/bno055_i2c.c
new file mode 100644
index 000000000000..c1bbc0fe34f9
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_i2c.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for I2C-interfaced Bosch BNO055 IMU.
+ *
+ * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <andrea.merello@iit.it>
+ */
+
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "bno055.h"
+
+#define BNO055_I2C_XFER_BURST_BREAK_THRESHOLD 3
+
+static int bno055_i2c_probe(struct i2c_client *client)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &bno055_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(regmap),
+ "Unable to init register map");
+
+ return bno055_probe(&client->dev, regmap,
+ BNO055_I2C_XFER_BURST_BREAK_THRESHOLD, true);
+}
+
+static const struct i2c_device_id bno055_i2c_id[] = {
+ {"bno055", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bno055_i2c_id);
+
+static const struct of_device_id __maybe_unused bno055_i2c_of_match[] = {
+ { .compatible = "bosch,bno055" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bno055_i2c_of_match);
+
+static struct i2c_driver bno055_driver = {
+ .driver = {
+ .name = "bno055-i2c",
+ .of_match_table = bno055_i2c_of_match,
+ },
+ .probe_new = bno055_i2c_probe,
+ .id_table = bno055_i2c_id,
+};
+module_i2c_driver(bno055_driver);
+
+MODULE_AUTHOR("Andrea Merello");
+MODULE_DESCRIPTION("Bosch BNO055 I2C interface");
+MODULE_IMPORT_NS(IIO_BNO055);
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/bno055/bno055_ser_core.c b/drivers/iio/imu/bno055/bno055_ser_core.c
new file mode 100644
index 000000000000..57728a568471
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_ser_core.c
@@ -0,0 +1,560 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Serial line interface for Bosh BNO055 IMU (via serdev).
+ * This file implements serial communication up to the register read/write
+ * level.
+ *
+ * Copyright (C) 2021-2022 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ * Written by Andrea Merello <andrea.merello@iit.it>
+ *
+ * This driver is based on
+ * Plantower PMS7003 particulate matter sensor driver
+ * Which is
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/regmap.h>
+#include <linux/serdev.h>
+
+#include "bno055_ser_trace.h"
+#include "bno055.h"
+
+/*
+ * Register writes cmd have the following format
+ * +------+------+-----+-----+----- ... ----+
+ * | 0xAA | 0xOO | REG | LEN | payload[LEN] |
+ * +------+------+-----+-----+----- ... ----+
+ *
+ * Register write responses have the following format
+ * +------+----------+
+ * | 0xEE | ERROCODE |
+ * +------+----------+
+ *
+ * .. except when writing the SYS_RST bit (i.e. triggering a system reset); in
+ * case the IMU accepts the command, then it resets without responding. We don't
+ * handle this (yet) here (so we inform the common bno055 code not to perform
+ * sw resets - bno055 on serial bus basically requires the hw reset pin).
+ *
+ * Register read have the following format
+ * +------+------+-----+-----+
+ * | 0xAA | 0xO1 | REG | LEN |
+ * +------+------+-----+-----+
+ *
+ * Successful register read response have the following format
+ * +------+-----+----- ... ----+
+ * | 0xBB | LEN | payload[LEN] |
+ * +------+-----+----- ... ----+
+ *
+ * Failed register read response have the following format
+ * +------+--------+
+ * | 0xEE | ERRCODE| (ERRCODE always > 1)
+ * +------+--------+
+ *
+ * Error codes are
+ * 01: OK
+ * 02: read/write FAIL
+ * 04: invalid address
+ * 05: write on RO
+ * 06: wrong start byte
+ * 07: bus overrun
+ * 08: len too high
+ * 09: len too low
+ * 10: bus RX byte timeout (timeout is 30mS)
+ *
+ *
+ * **WORKAROUND ALERT**
+ *
+ * Serial communication seems very fragile: the BNO055 buffer seems to overflow
+ * very easy; BNO055 seems able to sink few bytes, then it needs a brief pause.
+ * On the other hand, it is also picky on timeout: if there is a pause > 30mS in
+ * between two bytes then the transaction fails (IMU internal RX FSM resets).
+ *
+ * BNO055 has been seen also failing to process commands in case we send them
+ * too close each other (or if it is somehow busy?)
+ *
+ * In particular I saw these scenarios:
+ * 1) If we send 2 bytes per time, then the IMU never(?) overflows.
+ * 2) If we send 4 bytes per time (i.e. the full header), then the IMU could
+ * overflow, but it seem to sink all 4 bytes, then it returns error.
+ * 3) If we send more than 4 bytes, the IMU could overflow, and I saw it sending
+ * error after 4 bytes are sent; we have troubles in synchronizing again,
+ * because we are still sending data, and the IMU interprets it as the 1st
+ * byte of a new command.
+ *
+ * While we must avoid case 3, we could send 4 bytes per time and eventually
+ * retry in case of failure; this seemed convenient for reads (which requires
+ * TXing exactly 4 bytes), however it has been seen that, depending by the IMU
+ * settings (e.g. LPF), failures became less or more frequent; in certain IMU
+ * configurations they are very rare, but in certain others we keeps failing
+ * even after like 30 retries.
+ *
+ * So, we just split TXes in [2-bytes + delay] steps, and still keep an eye on
+ * the IMU response; in case it overflows (which is now unlikely), we retry.
+ */
+
+/*
+ * Read operation overhead:
+ * 4 bytes req + 2byte resp hdr.
+ * 6 bytes = 60 bit (considering 1start + 1stop bits).
+ * 60/115200 = ~520uS + about 2500mS delay -> ~3mS
+ * In 3mS we could read back about 34 bytes that means 17 samples, this means
+ * that in case of scattered reads in which the gap is 17 samples or less it is
+ * still convenient to go for a burst.
+ * We have to take into account also IMU response time - IMU seems to be often
+ * reasonably quick to respond, but sometimes it seems to be in some "critical
+ * section" in which it delays handling of serial protocol. Because of this we
+ * round-up to 22, which is the max number of samples, always bursting indeed.
+ */
+#define BNO055_SER_XFER_BURST_BREAK_THRESHOLD 22
+
+struct bno055_ser_priv {
+ enum {
+ CMD_NONE,
+ CMD_READ,
+ CMD_WRITE,
+ } expect_response;
+ int expected_data_len;
+ u8 *response_buf;
+
+ /**
+ * enum cmd_status - represent the status of a command sent to the HW.
+ * @STATUS_CRIT: The command failed: the serial communication failed.
+ * @STATUS_OK: The command executed successfully.
+ * @STATUS_FAIL: The command failed: HW responded with an error.
+ */
+ enum {
+ STATUS_CRIT = -1,
+ STATUS_OK = 0,
+ STATUS_FAIL = 1,
+ } cmd_status;
+
+ /*
+ * Protects all the above fields, which are accessed in behalf of both
+ * the serdev RX callback and the regmap side
+ */
+ struct mutex lock;
+
+ /* Only accessed in serdev RX callback context*/
+ struct {
+ enum {
+ RX_IDLE,
+ RX_START,
+ RX_DATA,
+ } state;
+ int databuf_count;
+ int expected_len;
+ int type;
+ } rx;
+
+ /* Never accessed in behalf of serdev RX callback context */
+ bool cmd_stale;
+
+ struct completion cmd_complete;
+ struct serdev_device *serdev;
+};
+
+static int bno055_ser_send_chunk(struct bno055_ser_priv *priv, const u8 *data, int len)
+{
+ int ret;
+
+ trace_send_chunk(len, data);
+ ret = serdev_device_write(priv->serdev, data, len, msecs_to_jiffies(25));
+ if (ret < 0)
+ return ret;
+
+ if (ret < len)
+ return -EIO;
+
+ return 0;
+}
+
+/*
+ * Send a read or write command.
+ * 'data' can be NULL (used in read case). 'len' parameter is always valid; in
+ * case 'data' is non-NULL then it must match 'data' size.
+ */
+static int bno055_ser_do_send_cmd(struct bno055_ser_priv *priv,
+ bool read, int addr, int len, const u8 *data)
+{
+ u8 hdr[] = {0xAA, read, addr, len};
+ int chunk_len;
+ int ret;
+
+ ret = bno055_ser_send_chunk(priv, hdr, 2);
+ if (ret)
+ goto fail;
+ usleep_range(2000, 3000);
+ ret = bno055_ser_send_chunk(priv, hdr + 2, 2);
+ if (ret)
+ goto fail;
+
+ if (read)
+ return 0;
+
+ while (len) {
+ chunk_len = min(len, 2);
+ usleep_range(2000, 3000);
+ ret = bno055_ser_send_chunk(priv, data, chunk_len);
+ if (ret)
+ goto fail;
+ data += chunk_len;
+ len -= chunk_len;
+ }
+
+ return 0;
+fail:
+ /* waiting more than 30mS should clear the BNO055 internal state */
+ usleep_range(40000, 50000);
+ return ret;
+}
+
+static int bno055_ser_send_cmd(struct bno055_ser_priv *priv,
+ bool read, int addr, int len, const u8 *data)
+{
+ const int retry_max = 5;
+ int retry = retry_max;
+ int ret = 0;
+
+ /*
+ * In case previous command was interrupted we still need to wait it to
+ * complete before we can issue new commands
+ */
+ if (priv->cmd_stale) {
+ ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete,
+ msecs_to_jiffies(100));
+ if (ret == -ERESTARTSYS)
+ return -ERESTARTSYS;
+
+ priv->cmd_stale = false;
+ /* if serial protocol broke, bail out */
+ if (priv->cmd_status == STATUS_CRIT)
+ return -EIO;
+ }
+
+ /*
+ * Try to convince the IMU to cooperate.. as explained in the comments
+ * at the top of this file, the IMU could also refuse the command (i.e.
+ * it is not ready yet); retry in this case.
+ */
+ do {
+ mutex_lock(&priv->lock);
+ priv->expect_response = read ? CMD_READ : CMD_WRITE;
+ reinit_completion(&priv->cmd_complete);
+ mutex_unlock(&priv->lock);
+
+ if (retry != retry_max)
+ trace_cmd_retry(read, addr, retry_max - retry);
+ ret = bno055_ser_do_send_cmd(priv, read, addr, len, data);
+ if (ret)
+ continue;
+
+ ret = wait_for_completion_interruptible_timeout(&priv->cmd_complete,
+ msecs_to_jiffies(100));
+ if (ret == -ERESTARTSYS) {
+ priv->cmd_stale = true;
+ return -ERESTARTSYS;
+ }
+
+ if (!ret)
+ return -ETIMEDOUT;
+
+ if (priv->cmd_status == STATUS_OK)
+ return 0;
+ if (priv->cmd_status == STATUS_CRIT)
+ return -EIO;
+
+ /* loop in case priv->cmd_status == STATUS_FAIL */
+ } while (--retry);
+
+ if (ret < 0)
+ return ret;
+ if (priv->cmd_status == STATUS_FAIL)
+ return -EINVAL;
+ return 0;
+}
+
+static int bno055_ser_write_reg(void *context, const void *_data, size_t count)
+{
+ const u8 *data = _data;
+ struct bno055_ser_priv *priv = context;
+
+ if (count < 2) {
+ dev_err(&priv->serdev->dev, "Invalid write count %zu", count);
+ return -EINVAL;
+ }
+
+ trace_write_reg(data[0], data[1]);
+ return bno055_ser_send_cmd(priv, 0, data[0], count - 1, data + 1);
+}
+
+static int bno055_ser_read_reg(void *context,
+ const void *_reg, size_t reg_size,
+ void *val, size_t val_size)
+{
+ int ret;
+ int reg_addr;
+ const u8 *reg = _reg;
+ struct bno055_ser_priv *priv = context;
+
+ if (val_size > 128) {
+ dev_err(&priv->serdev->dev, "Invalid read valsize %zu", val_size);
+ return -EINVAL;
+ }
+
+ reg_addr = *reg;
+ trace_read_reg(reg_addr, val_size);
+ mutex_lock(&priv->lock);
+ priv->expected_data_len = val_size;
+ priv->response_buf = val;
+ mutex_unlock(&priv->lock);
+
+ ret = bno055_ser_send_cmd(priv, 1, reg_addr, val_size, NULL);
+
+ mutex_lock(&priv->lock);
+ priv->response_buf = NULL;
+ mutex_unlock(&priv->lock);
+
+ return ret;
+}
+
+/*
+ * Handler for received data; this is called from the receiver callback whenever
+ * it got some packet from the serial bus. The status tells us whether the
+ * packet is valid (i.e. header ok && received payload len consistent wrt the
+ * header). It's now our responsibility to check whether this is what we
+ * expected, of whether we got some unexpected, yet valid, packet.
+ */
+static void bno055_ser_handle_rx(struct bno055_ser_priv *priv, int status)
+{
+ mutex_lock(&priv->lock);
+ switch (priv->expect_response) {
+ case CMD_NONE:
+ dev_warn(&priv->serdev->dev, "received unexpected, yet valid, data from sensor");
+ mutex_unlock(&priv->lock);
+ return;
+
+ case CMD_READ:
+ priv->cmd_status = status;
+ if (status == STATUS_OK &&
+ priv->rx.databuf_count != priv->expected_data_len) {
+ /*
+ * If we got here, then the lower layer serial protocol
+ * seems consistent with itself; if we got an unexpected
+ * amount of data then signal it as a non critical error
+ */
+ priv->cmd_status = STATUS_FAIL;
+ dev_warn(&priv->serdev->dev,
+ "received an unexpected amount of, yet valid, data from sensor");
+ }
+ break;
+
+ case CMD_WRITE:
+ priv->cmd_status = status;
+ break;
+ }
+
+ priv->expect_response = CMD_NONE;
+ mutex_unlock(&priv->lock);
+ complete(&priv->cmd_complete);
+}
+
+/*
+ * Serdev receiver FSM. This tracks the serial communication and parse the
+ * header. It pushes packets to bno055_ser_handle_rx(), eventually communicating
+ * failures (i.e. malformed packets).
+ * Ideally it doesn't know anything about upper layer (i.e. if this is the
+ * packet we were really expecting), but since we copies the payload into the
+ * receiver buffer (that is not valid when i.e. we don't expect data), we
+ * snoop a bit in the upper layer..
+ * Also, we assume to RX one pkt per time (i.e. the HW doesn't send anything
+ * unless we require to AND we don't queue more than one request per time).
+ */
+static int bno055_ser_receive_buf(struct serdev_device *serdev,
+ const unsigned char *buf, size_t size)
+{
+ int status;
+ struct bno055_ser_priv *priv = serdev_device_get_drvdata(serdev);
+ int remaining = size;
+
+ if (size == 0)
+ return 0;
+
+ trace_recv(size, buf);
+ switch (priv->rx.state) {
+ case RX_IDLE:
+ /*
+ * New packet.
+ * Check for its 1st byte that identifies the pkt type.
+ */
+ if (buf[0] != 0xEE && buf[0] != 0xBB) {
+ dev_err(&priv->serdev->dev,
+ "Invalid packet start %x", buf[0]);
+ bno055_ser_handle_rx(priv, STATUS_CRIT);
+ break;
+ }
+ priv->rx.type = buf[0];
+ priv->rx.state = RX_START;
+ remaining--;
+ buf++;
+ priv->rx.databuf_count = 0;
+ fallthrough;
+
+ case RX_START:
+ /*
+ * Packet RX in progress, we expect either 1-byte len or 1-byte
+ * status depending by the packet type.
+ */
+ if (remaining == 0)
+ break;
+
+ if (priv->rx.type == 0xEE) {
+ if (remaining > 1) {
+ dev_err(&priv->serdev->dev, "EE pkt. Extra data received");
+ status = STATUS_CRIT;
+ } else {
+ status = (buf[0] == 1) ? STATUS_OK : STATUS_FAIL;
+ }
+ bno055_ser_handle_rx(priv, status);
+ priv->rx.state = RX_IDLE;
+ break;
+
+ } else {
+ /*priv->rx.type == 0xBB */
+ priv->rx.state = RX_DATA;
+ priv->rx.expected_len = buf[0];
+ remaining--;
+ buf++;
+ }
+ fallthrough;
+
+ case RX_DATA:
+ /* Header parsed; now receiving packet data payload */
+ if (remaining == 0)
+ break;
+
+ if (priv->rx.databuf_count + remaining > priv->rx.expected_len) {
+ /*
+ * This is an inconsistency in serial protocol, we lost
+ * sync and we don't know how to handle further data
+ */
+ dev_err(&priv->serdev->dev, "BB pkt. Extra data received");
+ bno055_ser_handle_rx(priv, STATUS_CRIT);
+ priv->rx.state = RX_IDLE;
+ break;
+ }
+
+ mutex_lock(&priv->lock);
+ /*
+ * NULL e.g. when read cmd is stale or when no read cmd is
+ * actually pending.
+ */
+ if (priv->response_buf &&
+ /*
+ * Snoop on the upper layer protocol stuff to make sure not
+ * to write to an invalid memory. Apart for this, let's the
+ * upper layer manage any inconsistency wrt expected data
+ * len (as long as the serial protocol is consistent wrt
+ * itself (i.e. response header is consistent with received
+ * response len.
+ */
+ (priv->rx.databuf_count + remaining <= priv->expected_data_len))
+ memcpy(priv->response_buf + priv->rx.databuf_count,
+ buf, remaining);
+ mutex_unlock(&priv->lock);
+
+ priv->rx.databuf_count += remaining;
+
+ /*
+ * Reached expected len advertised by the IMU for the current
+ * packet. Pass it to the upper layer (for us it is just valid).
+ */
+ if (priv->rx.databuf_count == priv->rx.expected_len) {
+ bno055_ser_handle_rx(priv, STATUS_OK);
+ priv->rx.state = RX_IDLE;
+ }
+ break;
+ }
+
+ return size;
+}
+
+static const struct serdev_device_ops bno055_ser_serdev_ops = {
+ .receive_buf = bno055_ser_receive_buf,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+static struct regmap_bus bno055_ser_regmap_bus = {
+ .write = bno055_ser_write_reg,
+ .read = bno055_ser_read_reg,
+};
+
+static int bno055_ser_probe(struct serdev_device *serdev)
+{
+ struct bno055_ser_priv *priv;
+ struct regmap *regmap;
+ int ret;
+
+ priv = devm_kzalloc(&serdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ serdev_device_set_drvdata(serdev, priv);
+ priv->serdev = serdev;
+ mutex_init(&priv->lock);
+ init_completion(&priv->cmd_complete);
+
+ serdev_device_set_client_ops(serdev, &bno055_ser_serdev_ops);
+ ret = devm_serdev_device_open(&serdev->dev, serdev);
+ if (ret)
+ return ret;
+
+ if (serdev_device_set_baudrate(serdev, 115200) != 115200) {
+ dev_err(&serdev->dev, "Cannot set required baud rate");
+ return -EIO;
+ }
+
+ ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
+ if (ret) {
+ dev_err(&serdev->dev, "Cannot set required parity setting");
+ return ret;
+ }
+ serdev_device_set_flow_control(serdev, false);
+
+ regmap = devm_regmap_init(&serdev->dev, &bno055_ser_regmap_bus,
+ priv, &bno055_regmap_config);
+ if (IS_ERR(regmap))
+ return dev_err_probe(&serdev->dev, PTR_ERR(regmap),
+ "Unable to init register map");
+
+ return bno055_probe(&serdev->dev, regmap,
+ BNO055_SER_XFER_BURST_BREAK_THRESHOLD, false);
+}
+
+static const struct of_device_id bno055_ser_of_match[] = {
+ { .compatible = "bosch,bno055" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bno055_ser_of_match);
+
+static struct serdev_device_driver bno055_ser_driver = {
+ .driver = {
+ .name = "bno055-ser",
+ .of_match_table = bno055_ser_of_match,
+ },
+ .probe = bno055_ser_probe,
+};
+module_serdev_device_driver(bno055_ser_driver);
+
+MODULE_AUTHOR("Andrea Merello <andrea.merello@iit.it>");
+MODULE_DESCRIPTION("Bosch BNO055 serdev interface");
+MODULE_IMPORT_NS(IIO_BNO055);
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.c b/drivers/iio/imu/bno055/bno055_ser_trace.c
new file mode 100644
index 000000000000..48397b66daef
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_ser_trace.c
@@ -0,0 +1,14 @@
+//SPDX-License-Identifier: GPL-2.0
+
+/*
+ * bno055_ser Trace Support
+ * Copyright (C) 2022 Istituto Italiano di Tecnologia
+ * Electronic Design Laboratory
+ *
+ * Based on:
+ * Device core Trace Support
+ * Copyright (C) 2021, Intel Corporation
+ */
+
+#define CREATE_TRACE_POINTS
+#include "bno055_ser_trace.h"
diff --git a/drivers/iio/imu/bno055/bno055_ser_trace.h b/drivers/iio/imu/bno055/bno055_ser_trace.h
new file mode 100644
index 000000000000..7d9eae166eec
--- /dev/null
+++ b/drivers/iio/imu/bno055/bno055_ser_trace.h
@@ -0,0 +1,104 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#if !defined(__BNO055_SERDEV_TRACE_H__) || defined(TRACE_HEADER_MULTI_READ)
+#define __BNO055_SERDEV_TRACE_H__
+
+#include <linux/tracepoint.h>
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM bno055_ser
+
+TRACE_EVENT(send_chunk,
+ TP_PROTO(int len, const u8 *data),
+ TP_ARGS(len, data),
+ TP_STRUCT__entry(
+ __field(int, len)
+ __dynamic_array(u8, chunk, len)
+ ),
+ TP_fast_assign(
+ __entry->len = len;
+ memcpy(__get_dynamic_array(chunk),
+ data, __entry->len);
+ ),
+ TP_printk("len: %d, data: = %*ph",
+ __entry->len, __entry->len, __get_dynamic_array(chunk)
+ )
+);
+
+TRACE_EVENT(cmd_retry,
+ TP_PROTO(bool read, int addr, int retry),
+ TP_ARGS(read, addr, retry),
+ TP_STRUCT__entry(
+ __field(bool, read)
+ __field(int, addr)
+ __field(int, retry)
+ ),
+ TP_fast_assign(
+ __entry->read = read;
+ __entry->addr = addr;
+ __entry->retry = retry;
+ ),
+ TP_printk("%s addr 0x%x retry #%d",
+ __entry->read ? "read" : "write",
+ __entry->addr, __entry->retry
+ )
+);
+
+TRACE_EVENT(write_reg,
+ TP_PROTO(u8 addr, u8 value),
+ TP_ARGS(addr, value),
+ TP_STRUCT__entry(
+ __field(u8, addr)
+ __field(u8, value)
+ ),
+ TP_fast_assign(
+ __entry->addr = addr;
+ __entry->value = value;
+ ),
+ TP_printk("reg 0x%x = 0x%x",
+ __entry->addr, __entry->value
+ )
+);
+
+TRACE_EVENT(read_reg,
+ TP_PROTO(int addr, size_t len),
+ TP_ARGS(addr, len),
+ TP_STRUCT__entry(
+ __field(int, addr)
+ __field(size_t, len)
+ ),
+ TP_fast_assign(
+ __entry->addr = addr;
+ __entry->len = len;
+ ),
+ TP_printk("reg 0x%x (len %zu)",
+ __entry->addr, __entry->len
+ )
+);
+
+TRACE_EVENT(recv,
+ TP_PROTO(size_t len, const unsigned char *buf),
+ TP_ARGS(len, buf),
+ TP_STRUCT__entry(
+ __field(size_t, len)
+ __dynamic_array(unsigned char, buf, len)
+ ),
+ TP_fast_assign(
+ __entry->len = len;
+ memcpy(__get_dynamic_array(buf),
+ buf, __entry->len);
+ ),
+ TP_printk("len: %zu, data: = %*ph",
+ __entry->len, (int)__entry->len, __get_dynamic_array(buf)
+ )
+);
+
+#endif /* __BNO055_SERDEV_TRACE_H__ || TRACE_HEADER_MULTI_READ */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE bno055_ser_trace
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
index 9b4298095d3f..f7bce428d9eb 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
@@ -65,7 +65,7 @@ static int asus_acpi_get_sensor_info(struct acpi_device *adev,
sub_elem = &elem->package.elements[j];
if (sub_elem->type == ACPI_TYPE_STRING)
- strlcpy(info->type, sub_elem->string.pointer,
+ strscpy(info->type, sub_elem->string.pointer,
sizeof(info->type));
else if (sub_elem->type == ACPI_TYPE_INTEGER) {
if (sub_elem->integer.value != client->addr) {
@@ -158,7 +158,7 @@ int inv_mpu_acpi_create_mux_client(struct i2c_client *client)
char *name;
info.addr = secondary;
- strlcpy(info.type, dev_name(&adev->dev),
+ strscpy(info.type, dev_name(&adev->dev),
sizeof(info.type));
name = strchr(info.type, ':');
if (name)
diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig
index fefd0b939100..2ed2b3f40c0b 100644
--- a/drivers/iio/imu/st_lsm6dsx/Kconfig
+++ b/drivers/iio/imu/st_lsm6dsx/Kconfig
@@ -12,7 +12,7 @@ config IIO_ST_LSM6DSX
Say yes here to build support for STMicroelectronics LSM6DSx imu
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
ism330dlc, lsm6dso, lsm6dsox, asm330lhh, asm330lhhx, lsm6dsr,
- lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop,
+ lsm6ds3tr-c, ism330dhcx, lsm6dsrx, lsm6ds0, lsm6dsop, lsm6dstx,
the accelerometer/gyroscope of lsm9ds1 and lsm6dst.
To compile this driver as a module, choose M here: the module
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index a86dd29a4738..6b57d47be69e 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -32,6 +32,7 @@
#define ST_LSM6DST_DEV_NAME "lsm6dst"
#define ST_LSM6DSOP_DEV_NAME "lsm6dsop"
#define ST_ASM330LHHX_DEV_NAME "asm330lhhx"
+#define ST_LSM6DSTX_DEV_NAME "lsm6dstx"
enum st_lsm6dsx_hw_id {
ST_LSM6DS3_ID,
@@ -51,6 +52,7 @@ enum st_lsm6dsx_hw_id {
ST_LSM6DST_ID,
ST_LSM6DSOP_ID,
ST_ASM330LHHX_ID,
+ ST_LSM6DSTX_ID,
ST_LSM6DSX_MAX_ID,
};
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index c7d3730ab1c5..e49f2d120ed3 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -15,7 +15,7 @@
* value of the decimation factor and ODR set for each FIFO data set.
*
* LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/LSM6DSRX/ISM330DHCX/
- * LSM6DST/LSM6DSOP:
+ * LSM6DST/LSM6DSOP/LSM6DSTX:
* The FIFO buffer can be configured to store data from gyroscope and
* accelerometer. Each sample is queued with a tag (1B) indicating data
* source (gyroscope, accelerometer, hw timer).
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index b5e4a4113652..f8bbb005718e 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -26,7 +26,8 @@
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
* - FIFO size: 4KB
*
- * - LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP:
+ * - LSM6DSO/LSM6DSOX/ASM330LHH/ASM330LHHX/LSM6DSR/ISM330DHCX/LSM6DST/LSM6DSOP/
+ * LSM6DSTX:
* - Accelerometer/Gyroscope supported ODR [Hz]: 12.5, 26, 52, 104, 208, 416,
* 833
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
@@ -791,6 +792,10 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.hw_id = ST_ASM330LHHX_ID,
.name = ST_ASM330LHHX_DEV_NAME,
.wai = 0x6b,
+ }, {
+ .hw_id = ST_LSM6DSTX_ID,
+ .name = ST_LSM6DSTX_DEV_NAME,
+ .wai = 0x6d,
},
},
.channels = {
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
index 2ea34c0d3a8c..307c8c436862 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
@@ -105,6 +105,10 @@ static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
.compatible = "st,asm330lhhx",
.data = (void *)ST_ASM330LHHX_ID,
},
+ {
+ .compatible = "st,lsm6dstx",
+ .data = (void *)ST_LSM6DSTX_ID,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
@@ -127,6 +131,7 @@ static const struct i2c_device_id st_lsm6dsx_i2c_id_table[] = {
{ ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID },
{ ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID },
{ ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID },
+ { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID },
{},
};
MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
index 6a8883f022a8..6a4eecf4bb05 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
@@ -105,6 +105,10 @@ static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
.compatible = "st,asm330lhhx",
.data = (void *)ST_ASM330LHHX_ID,
},
+ {
+ .compatible = "st,lsm6dstx",
+ .data = (void *)ST_LSM6DSTX_ID,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
@@ -127,6 +131,7 @@ static const struct spi_device_id st_lsm6dsx_spi_id_table[] = {
{ ST_LSM6DST_DEV_NAME, ST_LSM6DST_ID },
{ ST_LSM6DSOP_DEV_NAME, ST_LSM6DSOP_ID },
{ ST_ASM330LHHX_DEV_NAME, ST_ASM330LHHX_ID },
+ { ST_LSM6DSTX_DEV_NAME, ST_LSM6DSTX_ID },
{},
};
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index acc2b6c05d57..228598b82a2f 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -843,8 +843,8 @@ static int iio_verify_update(struct iio_dev *indio_dev,
* to verify.
*/
if (remove_buffer && !insert_buffer &&
- list_is_singular(&iio_dev_opaque->buffer_list))
- return 0;
+ list_is_singular(&iio_dev_opaque->buffer_list))
+ return 0;
modes = indio_dev->modes;
@@ -940,6 +940,7 @@ struct iio_demux_table {
static void iio_buffer_demux_free(struct iio_buffer *buffer)
{
struct iio_demux_table *p, *q;
+
list_for_each_entry_safe(p, q, &buffer->demux_list, l) {
list_del(&p->l);
kfree(p);
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index 0f4dbda3b9d3..151ff3993354 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -134,6 +134,12 @@ static const char * const iio_modifier_names[] = {
[IIO_MOD_ETHANOL] = "ethanol",
[IIO_MOD_H2] = "h2",
[IIO_MOD_O2] = "o2",
+ [IIO_MOD_LINEAR_X] = "linear_x",
+ [IIO_MOD_LINEAR_Y] = "linear_y",
+ [IIO_MOD_LINEAR_Z] = "linear_z",
+ [IIO_MOD_PITCH] = "pitch",
+ [IIO_MOD_YAW] = "yaw",
+ [IIO_MOD_ROLL] = "roll",
};
/* relies on pairs of these shared then separate */
@@ -168,6 +174,7 @@ static const char * const iio_chan_info_postfix[] = {
[IIO_CHAN_INFO_OVERSAMPLING_RATIO] = "oversampling_ratio",
[IIO_CHAN_INFO_THERMOCOUPLE_TYPE] = "thermocouple_type",
[IIO_CHAN_INFO_CALIBAMBIENT] = "calibambient",
+ [IIO_CHAN_INFO_ZEROPOINT] = "zeropoint",
};
/**
* iio_device_id() - query the unique ID for the device
@@ -236,6 +243,7 @@ static int iio_sysfs_match_string_with_gaps(const char * const *array, size_t n,
struct dentry *iio_get_debugfs_dentry(struct iio_dev *indio_dev)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+
return iio_dev_opaque->debugfs_dentry;
}
EXPORT_SYMBOL_GPL(iio_get_debugfs_dentry);
@@ -447,6 +455,7 @@ static const struct file_operations iio_debugfs_reg_fops = {
static void iio_device_unregister_debugfs(struct iio_dev *indio_dev)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
+
debugfs_remove_recursive(iio_dev_opaque->debugfs_dentry);
}
@@ -1021,6 +1030,7 @@ int __iio_device_attr_init(struct device_attribute *dev_attr,
int ret = 0;
char *name = NULL;
char *full_postfix;
+
sysfs_attr_init(&dev_attr->attr);
/* Build up postfix of <extend_name>_<modifier>_postfix */
@@ -1299,8 +1309,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
IIO_SEPARATE,
- &chan->
- info_mask_separate_available);
+ &chan->info_mask_separate_available);
if (ret < 0)
return ret;
attrcount += ret;
@@ -1314,8 +1323,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
ret = iio_device_add_info_mask_type_avail(indio_dev, chan,
IIO_SHARED_BY_TYPE,
- &chan->
- info_mask_shared_by_type_available);
+ &chan->info_mask_shared_by_type_available);
if (ret < 0)
return ret;
attrcount += ret;
@@ -1355,6 +1363,7 @@ static int iio_device_add_channel_sysfs(struct iio_dev *indio_dev,
if (chan->ext_info) {
unsigned int i = 0;
+
for (ext_info = chan->ext_info; ext_info->name; ext_info++) {
ret = __iio_add_chan_devattr(ext_info->name,
chan,
@@ -1403,6 +1412,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+
return sysfs_emit(buf, "%s\n", indio_dev->name);
}
@@ -1412,6 +1422,7 @@ static ssize_t label_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+
return sysfs_emit(buf, "%s\n", indio_dev->label);
}
@@ -1565,7 +1576,7 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
ret = -ENOMEM;
goto error_clear_attrs;
}
- /* Copy across original attributes */
+ /* Copy across original attributes, and point to original binary attributes */
if (indio_dev->info->attrs) {
memcpy(iio_dev_opaque->chan_attr_group.attrs,
indio_dev->info->attrs->attrs,
@@ -1573,6 +1584,8 @@ static int iio_device_register_sysfs(struct iio_dev *indio_dev)
*attrcount_orig);
iio_dev_opaque->chan_attr_group.is_visible =
indio_dev->info->attrs->is_visible;
+ iio_dev_opaque->chan_attr_group.bin_attrs =
+ indio_dev->info->attrs->bin_attrs;
}
attrn = attrcount_orig;
/* Add all elements from the list. */
@@ -1621,6 +1634,8 @@ static void iio_dev_release(struct device *device)
iio_device_detach_buffers(indio_dev);
+ lockdep_unregister_key(&iio_dev_opaque->mlock_key);
+
ida_free(&iio_ida, iio_dev_opaque->id);
kfree(iio_dev_opaque);
}
@@ -1680,6 +1695,9 @@ struct iio_dev *iio_device_alloc(struct device *parent, int sizeof_priv)
INIT_LIST_HEAD(&iio_dev_opaque->buffer_list);
INIT_LIST_HEAD(&iio_dev_opaque->ioctl_handlers);
+ lockdep_register_key(&iio_dev_opaque->mlock_key);
+ lockdep_set_class(&indio_dev->mlock, &iio_dev_opaque->mlock_key);
+
return indio_dev;
}
EXPORT_SYMBOL(iio_device_alloc);
@@ -1777,6 +1795,7 @@ static int iio_chrdev_release(struct inode *inode, struct file *filp)
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, &iio_dev_opaque->flags);
iio_device_put(indio_dev);
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index b5e059e15b0a..3d78da2531a9 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -231,12 +231,15 @@ static const char * const iio_ev_type_text[] = {
[IIO_EV_TYPE_MAG_ADAPTIVE] = "mag_adaptive",
[IIO_EV_TYPE_CHANGE] = "change",
[IIO_EV_TYPE_MAG_REFERENCED] = "mag_referenced",
+ [IIO_EV_TYPE_GESTURE] = "gesture",
};
static const char * const iio_ev_dir_text[] = {
[IIO_EV_DIR_EITHER] = "either",
[IIO_EV_DIR_RISING] = "rising",
- [IIO_EV_DIR_FALLING] = "falling"
+ [IIO_EV_DIR_FALLING] = "falling",
+ [IIO_EV_DIR_SINGLETAP] = "singletap",
+ [IIO_EV_DIR_DOUBLETAP] = "doubletap",
};
static const char * const iio_ev_info_text[] = {
@@ -247,6 +250,8 @@ static const char * const iio_ev_info_text[] = {
[IIO_EV_INFO_HIGH_PASS_FILTER_3DB] = "high_pass_filter_3db",
[IIO_EV_INFO_LOW_PASS_FILTER_3DB] = "low_pass_filter_3db",
[IIO_EV_INFO_TIMEOUT] = "timeout",
+ [IIO_EV_INFO_RESET_TIMEOUT] = "reset_timeout",
+ [IIO_EV_INFO_TAP2_MIN_DELAY] = "tap2_min_delay",
};
static enum iio_event_direction iio_ev_attr_dir(struct iio_dev_attr *attr)
@@ -354,9 +359,10 @@ static int iio_device_add_event(struct iio_dev *indio_dev,
enum iio_shared_by shared_by, const unsigned long *mask)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev);
- ssize_t (*show)(struct device *, struct device_attribute *, char *);
- ssize_t (*store)(struct device *, struct device_attribute *,
- const char *, size_t);
+ ssize_t (*show)(struct device *dev, struct device_attribute *attr,
+ char *buf);
+ ssize_t (*store)(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t len);
unsigned int attrcount = 0;
unsigned int i;
char *postfix;
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index b78814d869b7..6885a186fe27 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -50,6 +50,7 @@ static ssize_t name_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct iio_trigger *trig = to_iio_trigger(dev);
+
return sysfs_emit(buf, "%s\n", trig->name);
}
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index df74765d33dc..872fd5c24147 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -5,9 +5,9 @@
*/
#include <linux/err.h>
#include <linux/export.h>
+#include <linux/property.h>
#include <linux/slab.h>
#include <linux/mutex.h>
-#include <linux/of.h>
#include <linux/iio/iio.h>
#include <linux/iio/iio-opaque.h>
@@ -45,13 +45,13 @@ int iio_map_array_register(struct iio_dev *indio_dev, struct iio_map *maps)
int i = 0, ret = 0;
struct iio_map_internal *mapi;
- if (maps == NULL)
+ if (!maps)
return 0;
mutex_lock(&iio_map_list_lock);
- while (maps[i].consumer_dev_name != NULL) {
+ while (maps[i].consumer_dev_name) {
mapi = kzalloc(sizeof(*mapi), GFP_KERNEL);
- if (mapi == NULL) {
+ if (!mapi) {
ret = -ENOMEM;
goto error_ret;
}
@@ -69,7 +69,6 @@ error_ret:
}
EXPORT_SYMBOL_GPL(iio_map_array_register);
-
/*
* Remove all map entries associated with the given iio device
*/
@@ -117,15 +116,8 @@ static const struct iio_chan_spec
return chan;
}
-#ifdef CONFIG_OF
-
-static int iio_dev_node_match(struct device *dev, const void *data)
-{
- return dev->of_node == data && dev->type == &iio_device_type;
-}
-
/**
- * __of_iio_simple_xlate - translate iiospec to the IIO channel index
+ * __fwnode_iio_simple_xlate - translate iiospec to the IIO channel index
* @indio_dev: pointer to the iio_dev structure
* @iiospec: IIO specifier as found in the device tree
*
@@ -134,14 +126,14 @@ static int iio_dev_node_match(struct device *dev, const void *data)
* whether IIO index is less than num_channels (that is specified in the
* iio_dev).
*/
-static int __of_iio_simple_xlate(struct iio_dev *indio_dev,
- const struct of_phandle_args *iiospec)
+static int __fwnode_iio_simple_xlate(struct iio_dev *indio_dev,
+ const struct fwnode_reference_args *iiospec)
{
- if (!iiospec->args_count)
+ if (!iiospec->nargs)
return 0;
if (iiospec->args[0] >= indio_dev->num_channels) {
- dev_err(&indio_dev->dev, "invalid channel index %u\n",
+ dev_err(&indio_dev->dev, "invalid channel index %llu\n",
iiospec->args[0]);
return -EINVAL;
}
@@ -149,32 +141,33 @@ static int __of_iio_simple_xlate(struct iio_dev *indio_dev,
return iiospec->args[0];
}
-static int __of_iio_channel_get(struct iio_channel *channel,
- struct device_node *np, int index)
+static int __fwnode_iio_channel_get(struct iio_channel *channel,
+ struct fwnode_handle *fwnode, int index)
{
+ struct fwnode_reference_args iiospec;
struct device *idev;
struct iio_dev *indio_dev;
int err;
- struct of_phandle_args iiospec;
- err = of_parse_phandle_with_args(np, "io-channels",
- "#io-channel-cells",
- index, &iiospec);
+ err = fwnode_property_get_reference_args(fwnode, "io-channels",
+ "#io-channel-cells", 0,
+ index, &iiospec);
if (err)
return err;
- idev = bus_find_device(&iio_bus_type, NULL, iiospec.np,
- iio_dev_node_match);
- of_node_put(iiospec.np);
- if (idev == NULL)
+ idev = bus_find_device_by_fwnode(&iio_bus_type, iiospec.fwnode);
+ if (!idev) {
+ fwnode_handle_put(iiospec.fwnode);
return -EPROBE_DEFER;
+ }
indio_dev = dev_to_iio_dev(idev);
channel->indio_dev = indio_dev;
- if (indio_dev->info->of_xlate)
- index = indio_dev->info->of_xlate(indio_dev, &iiospec);
+ if (indio_dev->info->fwnode_xlate)
+ index = indio_dev->info->fwnode_xlate(indio_dev, &iiospec);
else
- index = __of_iio_simple_xlate(indio_dev, &iiospec);
+ index = __fwnode_iio_simple_xlate(indio_dev, &iiospec);
+ fwnode_handle_put(iiospec.fwnode);
if (index < 0)
goto err_put;
channel->channel = &indio_dev->channels[index];
@@ -186,7 +179,8 @@ err_put:
return index;
}
-static struct iio_channel *of_iio_channel_get(struct device_node *np, int index)
+static struct iio_channel *fwnode_iio_channel_get(struct fwnode_handle *fwnode,
+ int index)
{
struct iio_channel *channel;
int err;
@@ -195,10 +189,10 @@ static struct iio_channel *of_iio_channel_get(struct device_node *np, int index)
return ERR_PTR(-EINVAL);
channel = kzalloc(sizeof(*channel), GFP_KERNEL);
- if (channel == NULL)
+ if (!channel)
return ERR_PTR(-ENOMEM);
- err = __of_iio_channel_get(channel, np, index);
+ err = __fwnode_iio_channel_get(channel, fwnode, index);
if (err)
goto err_free_channel;
@@ -209,74 +203,116 @@ err_free_channel:
return ERR_PTR(err);
}
-struct iio_channel *of_iio_channel_get_by_name(struct device_node *np,
- const char *name)
+static struct iio_channel *
+__fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode, const char *name)
{
- struct iio_channel *chan = NULL;
-
- /* Walk up the tree of devices looking for a matching iio channel */
- while (np) {
- int index = 0;
-
+ struct iio_channel *chan;
+ int index = 0;
+
+ /*
+ * For named iio channels, first look up the name in the
+ * "io-channel-names" property. If it cannot be found, the
+ * index will be an error code, and fwnode_iio_channel_get()
+ * will fail.
+ */
+ if (name)
+ index = fwnode_property_match_string(fwnode, "io-channel-names",
+ name);
+
+ chan = fwnode_iio_channel_get(fwnode, index);
+ if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER)
+ return chan;
+ if (name) {
+ if (index >= 0) {
+ pr_err("ERROR: could not get IIO channel %pfw:%s(%i)\n",
+ fwnode, name, index);
+ /*
+ * In this case, we found 'name' in 'io-channel-names'
+ * but somehow we still fail so that we should not proceed
+ * with any other lookup. Hence, explicitly return -EINVAL
+ * (maybe not the better error code) so that the caller
+ * won't do a system lookup.
+ */
+ return ERR_PTR(-EINVAL);
+ }
/*
- * For named iio channels, first look up the name in the
- * "io-channel-names" property. If it cannot be found, the
- * index will be an error code, and of_iio_channel_get()
- * will fail.
+ * If index < 0, then fwnode_property_get_reference_args() fails
+ * with -EINVAL or -ENOENT (ACPI case) which is expected. We
+ * should not proceed if we get any other error.
*/
- if (name)
- index = of_property_match_string(np, "io-channel-names",
- name);
- chan = of_iio_channel_get(np, index);
- if (!IS_ERR(chan) || PTR_ERR(chan) == -EPROBE_DEFER)
- break;
- else if (name && index >= 0) {
- pr_err("ERROR: could not get IIO channel %pOF:%s(%i)\n",
- np, name ? name : "", index);
- return NULL;
- }
-
+ if (PTR_ERR(chan) != -EINVAL && PTR_ERR(chan) != -ENOENT)
+ return chan;
+ } else if (PTR_ERR(chan) != -ENOENT) {
/*
- * No matching IIO channel found on this node.
- * If the parent node has a "io-channel-ranges" property,
- * then we can try one of its channels.
+ * if !name, then we should only proceed the lookup if
+ * fwnode_property_get_reference_args() returns -ENOENT.
*/
- np = np->parent;
- if (np && !of_get_property(np, "io-channel-ranges", NULL))
- return NULL;
+ return chan;
}
- return chan;
+ /* so we continue the lookup */
+ return ERR_PTR(-ENODEV);
}
-EXPORT_SYMBOL_GPL(of_iio_channel_get_by_name);
-static struct iio_channel *of_iio_channel_get_all(struct device *dev)
+struct iio_channel *fwnode_iio_channel_get_by_name(struct fwnode_handle *fwnode,
+ const char *name)
{
+ struct fwnode_handle *parent;
+ struct iio_channel *chan;
+
+ /* Walk up the tree of devices looking for a matching iio channel */
+ chan = __fwnode_iio_channel_get_by_name(fwnode, name);
+ if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV)
+ return chan;
+
+ /*
+ * No matching IIO channel found on this node.
+ * If the parent node has a "io-channel-ranges" property,
+ * then we can try one of its channels.
+ */
+ fwnode_for_each_parent_node(fwnode, parent) {
+ if (!fwnode_property_present(parent, "io-channel-ranges")) {
+ fwnode_handle_put(parent);
+ return ERR_PTR(-ENODEV);
+ }
+
+ chan = __fwnode_iio_channel_get_by_name(fwnode, name);
+ if (!IS_ERR(chan) || PTR_ERR(chan) != -ENODEV) {
+ fwnode_handle_put(parent);
+ return chan;
+ }
+ }
+
+ return ERR_PTR(-ENODEV);
+}
+EXPORT_SYMBOL_GPL(fwnode_iio_channel_get_by_name);
+
+static struct iio_channel *fwnode_iio_channel_get_all(struct device *dev)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(dev);
struct iio_channel *chans;
int i, mapind, nummaps = 0;
int ret;
do {
- ret = of_parse_phandle_with_args(dev->of_node,
- "io-channels",
- "#io-channel-cells",
- nummaps, NULL);
+ ret = fwnode_property_get_reference_args(fwnode, "io-channels",
+ "#io-channel-cells", 0,
+ nummaps, NULL);
if (ret < 0)
break;
} while (++nummaps);
- if (nummaps == 0) /* no error, return NULL to search map table */
- return NULL;
+ if (nummaps == 0)
+ return ERR_PTR(-ENODEV);
/* NULL terminated array to save passing size */
chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
- if (chans == NULL)
+ if (!chans)
return ERR_PTR(-ENOMEM);
- /* Search for OF matches */
+ /* Search for FW matches */
for (mapind = 0; mapind < nummaps; mapind++) {
- ret = __of_iio_channel_get(&chans[mapind], dev->of_node,
- mapind);
+ ret = __fwnode_iio_channel_get(&chans[mapind], fwnode, mapind);
if (ret)
goto error_free_chans;
}
@@ -289,15 +325,6 @@ error_free_chans:
return ERR_PTR(ret);
}
-#else /* CONFIG_OF */
-
-static inline struct iio_channel *of_iio_channel_get_all(struct device *dev)
-{
- return NULL;
-}
-
-#endif /* CONFIG_OF */
-
static struct iio_channel *iio_channel_get_sys(const char *name,
const char *channel_name)
{
@@ -305,7 +332,7 @@ static struct iio_channel *iio_channel_get_sys(const char *name,
struct iio_channel *channel;
int err;
- if (name == NULL && channel_name == NULL)
+ if (!(name || channel_name))
return ERR_PTR(-ENODEV);
/* first find matching entry the channel map */
@@ -320,11 +347,11 @@ static struct iio_channel *iio_channel_get_sys(const char *name,
break;
}
mutex_unlock(&iio_map_list_lock);
- if (c == NULL)
+ if (!c)
return ERR_PTR(-ENODEV);
channel = kzalloc(sizeof(*channel), GFP_KERNEL);
- if (channel == NULL) {
+ if (!channel) {
err = -ENOMEM;
goto error_no_mem;
}
@@ -336,7 +363,7 @@ static struct iio_channel *iio_channel_get_sys(const char *name,
iio_chan_spec_from_name(channel->indio_dev,
c->map->adc_channel_label);
- if (channel->channel == NULL) {
+ if (!channel->channel) {
err = -EINVAL;
goto error_no_chan;
}
@@ -358,9 +385,9 @@ struct iio_channel *iio_channel_get(struct device *dev,
struct iio_channel *channel;
if (dev) {
- channel = of_iio_channel_get_by_name(dev->of_node,
- channel_name);
- if (channel != NULL)
+ channel = fwnode_iio_channel_get_by_name(dev_fwnode(dev),
+ channel_name);
+ if (!IS_ERR(channel) || PTR_ERR(channel) != -ENODEV)
return channel;
}
@@ -400,14 +427,14 @@ struct iio_channel *devm_iio_channel_get(struct device *dev,
}
EXPORT_SYMBOL_GPL(devm_iio_channel_get);
-struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev,
- struct device_node *np,
- const char *channel_name)
+struct iio_channel *devm_fwnode_iio_channel_get_by_name(struct device *dev,
+ struct fwnode_handle *fwnode,
+ const char *channel_name)
{
struct iio_channel *channel;
int ret;
- channel = of_iio_channel_get_by_name(np, channel_name);
+ channel = fwnode_iio_channel_get_by_name(fwnode, channel_name);
if (IS_ERR(channel))
return channel;
@@ -417,7 +444,7 @@ struct iio_channel *devm_of_iio_channel_get_by_name(struct device *dev,
return channel;
}
-EXPORT_SYMBOL_GPL(devm_of_iio_channel_get_by_name);
+EXPORT_SYMBOL_GPL(devm_fwnode_iio_channel_get_by_name);
struct iio_channel *iio_channel_get_all(struct device *dev)
{
@@ -428,11 +455,15 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
int mapind = 0;
int i, ret;
- if (dev == NULL)
+ if (!dev)
return ERR_PTR(-EINVAL);
- chans = of_iio_channel_get_all(dev);
- if (chans)
+ chans = fwnode_iio_channel_get_all(dev);
+ /*
+ * We only want to carry on if the error is -ENODEV. Anything else
+ * should be reported up the stack.
+ */
+ if (!IS_ERR(chans) || PTR_ERR(chans) != -ENODEV)
return chans;
name = dev_name(dev);
@@ -452,7 +483,7 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
/* NULL terminated array to save passing size */
chans = kcalloc(nummaps + 1, sizeof(*chans), GFP_KERNEL);
- if (chans == NULL) {
+ if (!chans) {
ret = -ENOMEM;
goto error_ret;
}
@@ -466,7 +497,7 @@ struct iio_channel *iio_channel_get_all(struct device *dev)
chans[mapind].channel =
iio_chan_spec_from_name(chans[mapind].indio_dev,
c->map->adc_channel_label);
- if (chans[mapind].channel == NULL) {
+ if (!chans[mapind].channel) {
ret = -EINVAL;
goto error_free_chans;
}
@@ -528,14 +559,14 @@ struct iio_channel *devm_iio_channel_get_all(struct device *dev)
EXPORT_SYMBOL_GPL(devm_iio_channel_get_all);
static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
- enum iio_chan_info_enum info)
+ enum iio_chan_info_enum info)
{
int unused;
int vals[INDIO_MAX_RAW_ELEMENTS];
int ret;
int val_len = 2;
- if (val2 == NULL)
+ if (!val2)
val2 = &unused;
if (!iio_channel_has_info(chan->channel, info))
@@ -547,9 +578,10 @@ static int iio_channel_read(struct iio_channel *chan, int *val, int *val2,
vals, &val_len, info);
*val = vals[0];
*val2 = vals[1];
- } else
+ } else {
ret = chan->indio_dev->info->read_raw(chan->indio_dev,
chan->channel, val, val2, info);
+ }
return ret;
}
@@ -560,7 +592,7 @@ int iio_read_channel_raw(struct iio_channel *chan, int *val)
int ret;
mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (chan->indio_dev->info == NULL) {
+ if (!chan->indio_dev->info) {
ret = -ENODEV;
goto err_unlock;
}
@@ -579,7 +611,7 @@ int iio_read_channel_average_raw(struct iio_channel *chan, int *val)
int ret;
mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (chan->indio_dev->info == NULL) {
+ if (!chan->indio_dev->info) {
ret = -ENODEV;
goto err_unlock;
}
@@ -593,7 +625,8 @@ err_unlock:
EXPORT_SYMBOL_GPL(iio_read_channel_average_raw);
static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
- int raw, int *processed, unsigned int scale)
+ int raw, int *processed,
+ unsigned int scale)
{
int scale_type, scale_val, scale_val2;
int offset_type, offset_val, offset_val2;
@@ -626,7 +659,7 @@ static int iio_convert_raw_to_processed_unlocked(struct iio_channel *chan,
}
scale_type = iio_channel_read(chan, &scale_val, &scale_val2,
- IIO_CHAN_INFO_SCALE);
+ IIO_CHAN_INFO_SCALE);
if (scale_type < 0) {
/*
* If no channel scaling is available apply consumer scale to
@@ -671,19 +704,19 @@ 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)
+ int *processed, unsigned int scale)
{
struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(chan->indio_dev);
int ret;
mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (chan->indio_dev->info == NULL) {
+ if (!chan->indio_dev->info) {
ret = -ENODEV;
goto err_unlock;
}
ret = iio_convert_raw_to_processed_unlocked(chan, raw, processed,
- scale);
+ scale);
err_unlock:
mutex_unlock(&iio_dev_opaque->info_exist_lock);
@@ -698,7 +731,7 @@ int iio_read_channel_attribute(struct iio_channel *chan, int *val, int *val2,
int ret;
mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (chan->indio_dev->info == NULL) {
+ if (!chan->indio_dev->info) {
ret = -ENODEV;
goto err_unlock;
}
@@ -724,7 +757,7 @@ int iio_read_channel_processed_scale(struct iio_channel *chan, int *val,
int ret;
mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (chan->indio_dev->info == NULL) {
+ if (!chan->indio_dev->info) {
ret = -ENODEV;
goto err_unlock;
}
@@ -802,7 +835,7 @@ int iio_read_avail_channel_raw(struct iio_channel *chan,
int type;
ret = iio_read_avail_channel_attribute(chan, vals, &type, length,
- IIO_CHAN_INFO_RAW);
+ IIO_CHAN_INFO_RAW);
if (ret >= 0 && type != IIO_VAL_INT)
/* raw values are assumed to be IIO_VAL_INT */
@@ -886,7 +919,7 @@ int iio_get_channel_type(struct iio_channel *chan, enum iio_chan_type *type)
/* Need to verify underlying driver has not gone away */
mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (chan->indio_dev->info == NULL) {
+ if (!chan->indio_dev->info) {
ret = -ENODEV;
goto err_unlock;
}
@@ -913,7 +946,7 @@ int iio_write_channel_attribute(struct iio_channel *chan, int val, int val2,
int ret;
mutex_lock(&iio_dev_opaque->info_exist_lock);
- if (chan->indio_dev->info == NULL) {
+ if (!chan->indio_dev->info) {
ret = -ENODEV;
goto err_unlock;
}
@@ -947,9 +980,8 @@ unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan)
}
EXPORT_SYMBOL_GPL(iio_get_channel_ext_info_count);
-static const struct iio_chan_spec_ext_info *iio_lookup_ext_info(
- const struct iio_channel *chan,
- const char *attr)
+static const struct iio_chan_spec_ext_info *
+iio_lookup_ext_info(const struct iio_channel *chan, const char *attr)
{
const struct iio_chan_spec_ext_info *ext_info;
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index 8537e88f02e3..7cf6e8490123 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -331,6 +331,17 @@ config LTR501
This driver can also be built as a module. If so, the module
will be called ltr501.
+config LTRF216A
+ tristate "Liteon LTRF216A Light Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say Y or M here, you get support for Liteon LTRF216A
+ Ambient Light Sensor.
+
+ If built as a dynamically linked module, it will be called
+ ltrf216a.
+
config LV0104CS
tristate "LV0104CS Ambient Light Sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index d10912faf964..6f23817fae6f 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -31,6 +31,7 @@ obj-$(CONFIG_ISL29125) += isl29125.o
obj-$(CONFIG_JSA1212) += jsa1212.o
obj-$(CONFIG_SENSORS_LM3533) += lm3533-als.o
obj-$(CONFIG_LTR501) += ltr501.o
+obj-$(CONFIG_LTRF216A) += ltrf216a.o
obj-$(CONFIG_LV0104CS) += lv0104cs.o
obj-$(CONFIG_MAX44000) += max44000.o
obj-$(CONFIG_MAX44009) += max44009.o
diff --git a/drivers/iio/light/ltrf216a.c b/drivers/iio/light/ltrf216a.c
new file mode 100644
index 000000000000..4b8ef36b6912
--- /dev/null
+++ b/drivers/iio/light/ltrf216a.c
@@ -0,0 +1,550 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * LTRF216A Ambient Light Sensor
+ *
+ * Copyright (C) 2022 Collabora, Ltd.
+ * Author: Shreeya Patel <shreeya.patel@collabora.com>
+ *
+ * Copyright (C) 2021 Lite-On Technology Corp (Singapore)
+ * Author: Shi Zhigang <Zhigang.Shi@liteon.com>
+ *
+ * IIO driver for LTRF216A (7-bit I2C slave address 0x53).
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/iopoll.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/iio.h>
+
+#include <asm/unaligned.h>
+
+#define LTRF216A_ALS_RESET_MASK BIT(4)
+#define LTRF216A_ALS_DATA_STATUS BIT(3)
+#define LTRF216A_ALS_ENABLE_MASK BIT(1)
+#define LTRF216A_MAIN_CTRL 0x00
+#define LTRF216A_ALS_MEAS_RES 0x04
+#define LTRF216A_ALS_GAIN 0x05
+#define LTRF216A_PART_ID 0x06
+#define LTRF216A_MAIN_STATUS 0x07
+#define LTRF216A_ALS_CLEAR_DATA_0 0x0a
+#define LTRF216A_ALS_CLEAR_DATA_1 0x0b
+#define LTRF216A_ALS_CLEAR_DATA_2 0x0c
+#define LTRF216A_ALS_DATA_0 0x0d
+#define LTRF216A_ALS_DATA_1 0x0e
+#define LTRF216A_ALS_DATA_2 0x0f
+#define LTRF216A_INT_CFG 0x19
+#define LTRF216A_INT_PST 0x1a
+#define LTRF216A_ALS_THRES_UP_0 0x21
+#define LTRF216A_ALS_THRES_UP_1 0x22
+#define LTRF216A_ALS_THRES_UP_2 0x23
+#define LTRF216A_ALS_THRES_LOW_0 0x24
+#define LTRF216A_ALS_THRES_LOW_1 0x25
+#define LTRF216A_ALS_THRES_LOW_2 0x26
+#define LTRF216A_ALS_READ_DATA_DELAY_US 20000
+
+static const int ltrf216a_int_time_available[][2] = {
+ { 0, 400000 },
+ { 0, 200000 },
+ { 0, 100000 },
+ { 0, 50000 },
+ { 0, 25000 },
+};
+
+static const int ltrf216a_int_time_reg[][2] = {
+ { 400, 0x03 },
+ { 200, 0x13 },
+ { 100, 0x22 },
+ { 50, 0x31 },
+ { 25, 0x40 },
+};
+
+/*
+ * Window Factor is needed when the device is under Window glass
+ * with coated tinted ink. This is to compensate for the light loss
+ * due to the lower transmission rate of the window glass and helps
+ * in calculating lux.
+ */
+#define LTRF216A_WIN_FAC 1
+
+struct ltrf216a_data {
+ struct regmap *regmap;
+ struct i2c_client *client;
+ u32 int_time;
+ u16 int_time_fac;
+ u8 als_gain_fac;
+ /*
+ * Protects regmap accesses and makes sure integration time
+ * remains constant during the measurement of lux.
+ */
+ struct mutex lock;
+};
+
+static const struct iio_chan_spec ltrf216a_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ .info_mask_separate_available =
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+};
+
+static void ltrf216a_reset(struct iio_dev *indio_dev)
+{
+ struct ltrf216a_data *data = iio_priv(indio_dev);
+
+ /* reset sensor, chip fails to respond to this, so ignore any errors */
+ regmap_write(data->regmap, LTRF216A_MAIN_CTRL, LTRF216A_ALS_RESET_MASK);
+
+ /* reset time */
+ usleep_range(1000, 2000);
+}
+
+static int ltrf216a_enable(struct iio_dev *indio_dev)
+{
+ struct ltrf216a_data *data = iio_priv(indio_dev);
+ struct device *dev = &data->client->dev;
+ int ret;
+
+ /* enable sensor */
+ ret = regmap_set_bits(data->regmap,
+ LTRF216A_MAIN_CTRL, LTRF216A_ALS_ENABLE_MASK);
+ if (ret) {
+ dev_err(dev, "failed to enable sensor: %d\n", ret);
+ return ret;
+ }
+
+ /* sleep for one integration cycle after enabling the device */
+ msleep(ltrf216a_int_time_reg[0][0]);
+
+ return 0;
+}
+
+static int ltrf216a_disable(struct iio_dev *indio_dev)
+{
+ struct ltrf216a_data *data = iio_priv(indio_dev);
+ struct device *dev = &data->client->dev;
+ int ret;
+
+ ret = regmap_write(data->regmap, LTRF216A_MAIN_CTRL, 0);
+ if (ret)
+ dev_err(dev, "failed to disable sensor: %d\n", ret);
+
+ return ret;
+}
+
+static void ltrf216a_cleanup(void *data)
+{
+ struct iio_dev *indio_dev = data;
+
+ ltrf216a_disable(indio_dev);
+}
+
+static int ltrf216a_set_int_time(struct ltrf216a_data *data, int itime)
+{
+ struct device *dev = &data->client->dev;
+ unsigned int i;
+ u8 reg_val;
+ int ret;
+
+ for (i = 0; i < ARRAY_SIZE(ltrf216a_int_time_available); i++) {
+ if (ltrf216a_int_time_available[i][1] == itime)
+ break;
+ }
+ if (i == ARRAY_SIZE(ltrf216a_int_time_available))
+ return -EINVAL;
+
+ reg_val = ltrf216a_int_time_reg[i][1];
+
+ ret = regmap_write(data->regmap, LTRF216A_ALS_MEAS_RES, reg_val);
+ if (ret) {
+ dev_err(dev, "failed to set integration time: %d\n", ret);
+ return ret;
+ }
+
+ data->int_time_fac = ltrf216a_int_time_reg[i][0];
+ data->int_time = itime;
+
+ return 0;
+}
+
+static int ltrf216a_get_int_time(struct ltrf216a_data *data,
+ int *val, int *val2)
+{
+ *val = 0;
+ *val2 = data->int_time;
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int ltrf216a_set_power_state(struct ltrf216a_data *data, bool on)
+{
+ struct device *dev = &data->client->dev;
+ int ret = 0;
+
+ if (on) {
+ ret = pm_runtime_resume_and_get(dev);
+ if (ret) {
+ dev_err(dev, "failed to resume runtime PM: %d\n", ret);
+ return ret;
+ }
+ } else {
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+ }
+
+ return ret;
+}
+
+static int ltrf216a_read_data(struct ltrf216a_data *data, u8 addr)
+{
+ struct device *dev = &data->client->dev;
+ int ret, val;
+ u8 buf[3];
+
+ ret = regmap_read_poll_timeout(data->regmap, LTRF216A_MAIN_STATUS,
+ val, val & LTRF216A_ALS_DATA_STATUS,
+ LTRF216A_ALS_READ_DATA_DELAY_US,
+ LTRF216A_ALS_READ_DATA_DELAY_US * 50);
+ if (ret) {
+ dev_err(dev, "failed to wait for measurement data: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_bulk_read(data->regmap, addr, buf, sizeof(buf));
+ if (ret) {
+ dev_err(dev, "failed to read measurement data: %d\n", ret);
+ return ret;
+ }
+
+ return get_unaligned_le24(&buf[0]);
+}
+
+static int ltrf216a_get_lux(struct ltrf216a_data *data)
+{
+ int ret, greendata;
+ u64 lux, div;
+
+ ret = ltrf216a_set_power_state(data, true);
+ if (ret)
+ return ret;
+
+ greendata = ltrf216a_read_data(data, LTRF216A_ALS_DATA_0);
+ if (greendata < 0)
+ return greendata;
+
+ ltrf216a_set_power_state(data, false);
+
+ lux = greendata * 45 * LTRF216A_WIN_FAC * 100;
+ div = data->als_gain_fac * data->int_time_fac * 100;
+
+ return div_u64(lux, div);
+}
+
+static int ltrf216a_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct ltrf216a_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ltrf216a_set_power_state(data, true);
+ if (ret)
+ return ret;
+ mutex_lock(&data->lock);
+ ret = ltrf216a_read_data(data, LTRF216A_ALS_DATA_0);
+ mutex_unlock(&data->lock);
+ ltrf216a_set_power_state(data, false);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_PROCESSED:
+ mutex_lock(&data->lock);
+ ret = ltrf216a_get_lux(data);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_INT_TIME:
+ mutex_lock(&data->lock);
+ ret = ltrf216a_get_int_time(data, val, val2);
+ mutex_unlock(&data->lock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltrf216a_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct ltrf216a_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val != 0)
+ return -EINVAL;
+ mutex_lock(&data->lock);
+ ret = ltrf216a_set_int_time(data, val2);
+ mutex_unlock(&data->lock);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltrf216a_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(ltrf216a_int_time_available) * 2;
+ *vals = (const int *)ltrf216a_int_time_available;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ltrf216a_info = {
+ .read_raw = ltrf216a_read_raw,
+ .write_raw = ltrf216a_write_raw,
+ .read_avail = ltrf216a_read_available,
+};
+
+static bool ltrf216a_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTRF216A_MAIN_CTRL:
+ case LTRF216A_ALS_MEAS_RES:
+ case LTRF216A_ALS_GAIN:
+ case LTRF216A_PART_ID:
+ case LTRF216A_MAIN_STATUS:
+ case LTRF216A_ALS_CLEAR_DATA_0:
+ case LTRF216A_ALS_CLEAR_DATA_1:
+ case LTRF216A_ALS_CLEAR_DATA_2:
+ case LTRF216A_ALS_DATA_0:
+ case LTRF216A_ALS_DATA_1:
+ case LTRF216A_ALS_DATA_2:
+ case LTRF216A_INT_CFG:
+ case LTRF216A_INT_PST:
+ case LTRF216A_ALS_THRES_UP_0:
+ case LTRF216A_ALS_THRES_UP_1:
+ case LTRF216A_ALS_THRES_UP_2:
+ case LTRF216A_ALS_THRES_LOW_0:
+ case LTRF216A_ALS_THRES_LOW_1:
+ case LTRF216A_ALS_THRES_LOW_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ltrf216a_writable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTRF216A_MAIN_CTRL:
+ case LTRF216A_ALS_MEAS_RES:
+ case LTRF216A_ALS_GAIN:
+ case LTRF216A_INT_CFG:
+ case LTRF216A_INT_PST:
+ case LTRF216A_ALS_THRES_UP_0:
+ case LTRF216A_ALS_THRES_UP_1:
+ case LTRF216A_ALS_THRES_UP_2:
+ case LTRF216A_ALS_THRES_LOW_0:
+ case LTRF216A_ALS_THRES_LOW_1:
+ case LTRF216A_ALS_THRES_LOW_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ltrf216a_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case LTRF216A_MAIN_STATUS:
+ case LTRF216A_ALS_CLEAR_DATA_0:
+ case LTRF216A_ALS_CLEAR_DATA_1:
+ case LTRF216A_ALS_CLEAR_DATA_2:
+ case LTRF216A_ALS_DATA_0:
+ case LTRF216A_ALS_DATA_1:
+ case LTRF216A_ALS_DATA_2:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool ltrf216a_precious_reg(struct device *dev, unsigned int reg)
+{
+ return reg == LTRF216A_MAIN_STATUS;
+}
+
+static const struct regmap_config ltrf216a_regmap_config = {
+ .name = "ltrf216a",
+ .reg_bits = 8,
+ .val_bits = 8,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = LTRF216A_ALS_THRES_LOW_2,
+ .readable_reg = ltrf216a_readable_reg,
+ .writeable_reg = ltrf216a_writable_reg,
+ .volatile_reg = ltrf216a_volatile_reg,
+ .precious_reg = ltrf216a_precious_reg,
+ .disable_locking = true,
+};
+
+static int ltrf216a_probe(struct i2c_client *client)
+{
+ struct ltrf216a_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+
+ data->regmap = devm_regmap_init_i2c(client, &ltrf216a_regmap_config);
+ if (IS_ERR(data->regmap))
+ return dev_err_probe(&client->dev, PTR_ERR(data->regmap),
+ "regmap initialization failed\n");
+
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ mutex_init(&data->lock);
+
+ indio_dev->info = &ltrf216a_info;
+ indio_dev->name = "ltrf216a";
+ indio_dev->channels = ltrf216a_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ltrf216a_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret)
+ return ret;
+
+ /* reset sensor, chip fails to respond to this, so ignore any errors */
+ ltrf216a_reset(indio_dev);
+
+ ret = regmap_reinit_cache(data->regmap, &ltrf216a_regmap_config);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to reinit regmap cache\n");
+
+ ret = ltrf216a_enable(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&client->dev, ltrf216a_cleanup,
+ indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_pm_runtime_enable(&client->dev);
+ if (ret)
+ return dev_err_probe(&client->dev, ret,
+ "failed to enable runtime PM\n");
+
+ pm_runtime_set_autosuspend_delay(&client->dev, 1000);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ data->int_time = 100000;
+ data->int_time_fac = 100;
+ data->als_gain_fac = 3;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static int ltrf216a_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct ltrf216a_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = ltrf216a_disable(indio_dev);
+ if (ret)
+ return ret;
+
+ regcache_cache_only(data->regmap, true);
+
+ return 0;
+}
+
+static int ltrf216a_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct ltrf216a_data *data = iio_priv(indio_dev);
+ int ret;
+
+ regcache_cache_only(data->regmap, false);
+ regcache_mark_dirty(data->regmap);
+ ret = regcache_sync(data->regmap);
+ if (ret)
+ goto cache_only;
+
+ ret = ltrf216a_enable(indio_dev);
+ if (ret)
+ goto cache_only;
+
+ return 0;
+
+cache_only:
+ regcache_cache_only(data->regmap, true);
+
+ return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(ltrf216a_pm_ops, ltrf216a_runtime_suspend,
+ ltrf216a_runtime_resume, NULL);
+
+static const struct i2c_device_id ltrf216a_id[] = {
+ { "ltrf216a" },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, ltrf216a_id);
+
+static const struct of_device_id ltrf216a_of_match[] = {
+ { .compatible = "liteon,ltrf216a" },
+ { .compatible = "ltr,ltrf216a" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ltrf216a_of_match);
+
+static struct i2c_driver ltrf216a_driver = {
+ .driver = {
+ .name = "ltrf216a",
+ .pm = pm_ptr(&ltrf216a_pm_ops),
+ .of_match_table = ltrf216a_of_match,
+ },
+ .probe_new = ltrf216a_probe,
+ .id_table = ltrf216a_id,
+};
+module_i2c_driver(ltrf216a_driver);
+
+MODULE_AUTHOR("Shreeya Patel <shreeya.patel@collabora.com>");
+MODULE_AUTHOR("Shi Zhigang <Zhigang.Shi@liteon.com>");
+MODULE_DESCRIPTION("LTRF216A ambient light sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c
index 3d4cc1180b6a..c737d3e193ae 100644
--- a/drivers/iio/light/st_uvis25_core.c
+++ b/drivers/iio/light/st_uvis25_core.c
@@ -325,7 +325,7 @@ int st_uvis25_probe(struct device *dev, int irq, struct regmap *regmap)
}
EXPORT_SYMBOL_NS(st_uvis25_probe, IIO_UVIS25);
-static int __maybe_unused st_uvis25_suspend(struct device *dev)
+static int st_uvis25_suspend(struct device *dev)
{
struct iio_dev *iio_dev = dev_get_drvdata(dev);
struct st_uvis25_hw *hw = iio_priv(iio_dev);
@@ -334,7 +334,7 @@ static int __maybe_unused st_uvis25_suspend(struct device *dev)
ST_UVIS25_REG_ODR_MASK, 0);
}
-static int __maybe_unused st_uvis25_resume(struct device *dev)
+static int st_uvis25_resume(struct device *dev)
{
struct iio_dev *iio_dev = dev_get_drvdata(dev);
struct st_uvis25_hw *hw = iio_priv(iio_dev);
@@ -346,10 +346,7 @@ static int __maybe_unused st_uvis25_resume(struct device *dev)
return 0;
}
-const struct dev_pm_ops st_uvis25_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(st_uvis25_suspend, st_uvis25_resume)
-};
-EXPORT_SYMBOL_NS(st_uvis25_pm_ops, IIO_UVIS25);
+EXPORT_NS_SIMPLE_DEV_PM_OPS(st_uvis25_pm_ops, st_uvis25_suspend, st_uvis25_resume, IIO_UVIS25);
MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>");
MODULE_DESCRIPTION("STMicroelectronics uvis25 sensor driver");
diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c
index b06d09af28a3..c982b0b255cf 100644
--- a/drivers/iio/light/st_uvis25_i2c.c
+++ b/drivers/iio/light/st_uvis25_i2c.c
@@ -55,7 +55,7 @@ MODULE_DEVICE_TABLE(i2c, st_uvis25_i2c_id_table);
static struct i2c_driver st_uvis25_driver = {
.driver = {
.name = "st_uvis25_i2c",
- .pm = &st_uvis25_pm_ops,
+ .pm = pm_sleep_ptr(&st_uvis25_pm_ops),
.of_match_table = st_uvis25_i2c_of_match,
},
.probe = st_uvis25_i2c_probe,
diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c
index 3a4dc6d7180c..86a232320d7d 100644
--- a/drivers/iio/light/st_uvis25_spi.c
+++ b/drivers/iio/light/st_uvis25_spi.c
@@ -55,7 +55,7 @@ MODULE_DEVICE_TABLE(spi, st_uvis25_spi_id_table);
static struct spi_driver st_uvis25_driver = {
.driver = {
.name = "st_uvis25_spi",
- .pm = &st_uvis25_pm_ops,
+ .pm = pm_sleep_ptr(&st_uvis25_pm_ops),
.of_match_table = st_uvis25_spi_of_match,
},
.probe = st_uvis25_spi_probe,
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index 07eb619bcfe8..b91fc5e6a26e 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -216,8 +216,8 @@ config YAMAHA_YAS530
select IIO_TRIGGERED_BUFFER
help
Say Y here to add support for the Yamaha YAS530 series of
- 3-Axis Magnetometers. Right now YAS530, YAS532 and YAS533 are
- fully supported.
+ 3-Axis Magnetometers. YAS530, YAS532, YAS533 and YAS537 are
+ supported.
This driver can also be compiled as a module.
To compile this driver as a module, choose M here: the module
diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h
index 9120c8bbf3dd..60fbb5431c88 100644
--- a/drivers/iio/magnetometer/hmc5843.h
+++ b/drivers/iio/magnetometer/hmc5843.h
@@ -52,16 +52,5 @@ int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
enum hmc5843_ids id, const char *name);
void hmc5843_common_remove(struct device *dev);
-int hmc5843_common_suspend(struct device *dev);
-int hmc5843_common_resume(struct device *dev);
-
-#ifdef CONFIG_PM_SLEEP
-static __maybe_unused SIMPLE_DEV_PM_OPS(hmc5843_pm_ops,
- hmc5843_common_suspend,
- hmc5843_common_resume);
-#define HMC5843_PM_OPS (&hmc5843_pm_ops)
-#else
-#define HMC5843_PM_OPS NULL
-#endif
-
+extern const struct dev_pm_ops hmc5843_pm_ops;
#endif /* HMC5843_CORE_H */
diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c
index 4a63b2da9df0..c5521d61da29 100644
--- a/drivers/iio/magnetometer/hmc5843_core.c
+++ b/drivers/iio/magnetometer/hmc5843_core.c
@@ -603,19 +603,19 @@ static const struct iio_info hmc5843_info = {
static const unsigned long hmc5843_scan_masks[] = {0x7, 0};
-int hmc5843_common_suspend(struct device *dev)
+static int hmc5843_common_suspend(struct device *dev)
{
return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
HMC5843_MODE_SLEEP);
}
-EXPORT_SYMBOL_NS(hmc5843_common_suspend, IIO_HMC5843);
-int hmc5843_common_resume(struct device *dev)
+static int hmc5843_common_resume(struct device *dev)
{
return hmc5843_set_mode(iio_priv(dev_get_drvdata(dev)),
HMC5843_MODE_CONVERSION_CONTINUOUS);
}
-EXPORT_SYMBOL_NS(hmc5843_common_resume, IIO_HMC5843);
+EXPORT_NS_SIMPLE_DEV_PM_OPS(hmc5843_pm_ops, hmc5843_common_suspend,
+ hmc5843_common_resume, IIO_HMC5843);
int hmc5843_common_probe(struct device *dev, struct regmap *regmap,
enum hmc5843_ids id, const char *name)
diff --git a/drivers/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c
index fe5e8415b2f2..18a13dd51296 100644
--- a/drivers/iio/magnetometer/hmc5843_i2c.c
+++ b/drivers/iio/magnetometer/hmc5843_i2c.c
@@ -91,7 +91,7 @@ MODULE_DEVICE_TABLE(of, hmc5843_of_match);
static struct i2c_driver hmc5843_driver = {
.driver = {
.name = "hmc5843",
- .pm = HMC5843_PM_OPS,
+ .pm = pm_sleep_ptr(&hmc5843_pm_ops),
.of_match_table = hmc5843_of_match,
},
.id_table = hmc5843_id,
diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c
index 8403f09aba39..c42d2e2a6a6c 100644
--- a/drivers/iio/magnetometer/hmc5843_spi.c
+++ b/drivers/iio/magnetometer/hmc5843_spi.c
@@ -86,13 +86,13 @@ static const struct spi_device_id hmc5843_id[] = {
MODULE_DEVICE_TABLE(spi, hmc5843_id);
static struct spi_driver hmc5843_driver = {
- .driver = {
- .name = "hmc5843",
- .pm = HMC5843_PM_OPS,
- },
- .id_table = hmc5843_id,
- .probe = hmc5843_spi_probe,
- .remove = hmc5843_spi_remove,
+ .driver = {
+ .name = "hmc5843",
+ .pm = pm_sleep_ptr(&hmc5843_pm_ops),
+ },
+ .id_table = hmc5843_id,
+ .probe = hmc5843_spi_probe,
+ .remove = hmc5843_spi_remove,
};
module_spi_driver(hmc5843_driver);
diff --git a/drivers/iio/magnetometer/yamaha-yas530.c b/drivers/iio/magnetometer/yamaha-yas530.c
index c3a10942654e..801c760feb4d 100644
--- a/drivers/iio/magnetometer/yamaha-yas530.c
+++ b/drivers/iio/magnetometer/yamaha-yas530.c
@@ -10,13 +10,16 @@
* (YAS534 is a magnetic switch, not handled)
* YAS535 MS-6C
* YAS536 MS-3W
- * YAS537 MS-3T (2015 Samsung Galaxy S6, Note 5, Xiaomi)
+ * YAS537 MS-3T (2015 Samsung Galaxy S6, Note 5, Galaxy S7)
* YAS539 MS-3S (2018 Samsung Galaxy A7 SM-A750FN)
*
* Code functions found in the MPU3050 YAS530 and YAS532 drivers
* named "inv_compass" in the Tegra Android kernel tree.
* Copyright (C) 2012 InvenSense Corporation
*
+ * Code functions for YAS537 based on Yamaha Android kernel driver.
+ * Copyright (c) 2014 Yamaha Corporation
+ *
* Author: Linus Walleij <linus.walleij@linaro.org>
*/
#include <linux/bitfield.h>
@@ -29,9 +32,11 @@
#include <linux/mod_devicetable.h>
#include <linux/mutex.h>
#include <linux/pm_runtime.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/random.h>
+#include <linux/units.h>
#include <linux/iio/buffer.h>
#include <linux/iio/iio.h>
@@ -40,20 +45,39 @@
#include <asm/unaligned.h>
-/* This register map covers YAS530 and YAS532 but differs in YAS 537 and YAS539 */
+/* Commonly used registers */
#define YAS5XX_DEVICE_ID 0x80
-#define YAS5XX_ACTUATE_INIT_COIL 0x81
-#define YAS5XX_MEASURE 0x82
-#define YAS5XX_CONFIG 0x83
-#define YAS5XX_MEASURE_INTERVAL 0x84
-#define YAS5XX_OFFSET_X 0x85 /* [-31 .. 31] */
-#define YAS5XX_OFFSET_Y1 0x86 /* [-31 .. 31] */
-#define YAS5XX_OFFSET_Y2 0x87 /* [-31 .. 31] */
-#define YAS5XX_TEST1 0x88
-#define YAS5XX_TEST2 0x89
-#define YAS5XX_CAL 0x90
#define YAS5XX_MEASURE_DATA 0xB0
+/* These registers are used by YAS530, YAS532 and YAS533 */
+#define YAS530_ACTUATE_INIT_COIL 0x81
+#define YAS530_MEASURE 0x82
+#define YAS530_CONFIG 0x83
+#define YAS530_MEASURE_INTERVAL 0x84
+#define YAS530_OFFSET_X 0x85 /* [-31 .. 31] */
+#define YAS530_OFFSET_Y1 0x86 /* [-31 .. 31] */
+#define YAS530_OFFSET_Y2 0x87 /* [-31 .. 31] */
+#define YAS530_TEST1 0x88
+#define YAS530_TEST2 0x89
+#define YAS530_CAL 0x90
+
+/* Registers used by YAS537 */
+#define YAS537_MEASURE 0x81 /* Originally YAS537_REG_CMDR */
+#define YAS537_CONFIG 0x82 /* Originally YAS537_REG_CONFR */
+#define YAS537_MEASURE_INTERVAL 0x83 /* Originally YAS537_REG_INTRVLR */
+#define YAS537_OFFSET_X 0x84 /* Originally YAS537_REG_OXR */
+#define YAS537_OFFSET_Y1 0x85 /* Originally YAS537_REG_OY1R */
+#define YAS537_OFFSET_Y2 0x86 /* Originally YAS537_REG_OY2R */
+#define YAS537_AVR 0x87
+#define YAS537_HCK 0x88
+#define YAS537_LCK 0x89
+#define YAS537_SRST 0x90
+#define YAS537_ADCCAL 0x91
+#define YAS537_MTC 0x93
+#define YAS537_OC 0x9E
+#define YAS537_TRM 0x9F
+#define YAS537_CAL 0xC0
+
/* Bits in the YAS5xx config register */
#define YAS5XX_CONFIG_INTON BIT(0) /* Interrupt on? */
#define YAS5XX_CONFIG_INTHACT BIT(1) /* Interrupt active high? */
@@ -65,6 +89,7 @@
#define YAS5XX_MEASURE_LDTC BIT(1)
#define YAS5XX_MEASURE_FORS BIT(2)
#define YAS5XX_MEASURE_DLYMES BIT(4)
+#define YAS5XX_MEASURE_CONT BIT(5)
/* Bits in the measure data register */
#define YAS5XX_MEASURE_DATA_BUSY BIT(7)
@@ -88,33 +113,101 @@
#define YAS532_DATA_BITS 13
#define YAS532_DATA_CENTER BIT(YAS532_DATA_BITS - 1)
#define YAS532_DATA_OVERFLOW (BIT(YAS532_DATA_BITS) - 1)
-#define YAS532_20DEGREES 390 /* Looks like Kelvin */
-/* These variant IDs are known from code dumps */
#define YAS537_DEVICE_ID 0x07 /* YAS537 (MS-3T) */
-#define YAS539_DEVICE_ID 0x08 /* YAS539 (MS-3S) */
+#define YAS537_VERSION_0 0 /* Version naming unknown */
+#define YAS537_VERSION_1 1 /* Version naming unknown */
+#define YAS537_MAG_AVERAGE_32_MASK GENMASK(6, 4)
+#define YAS537_MEASURE_TIME_WORST_US 1500
+#define YAS537_DEFAULT_SENSOR_DELAY_MS 50
+#define YAS537_MAG_RCOIL_TIME_US 65
+#define YAS537_MTC3_MASK_PREP GENMASK(7, 0)
+#define YAS537_MTC3_MASK_GET GENMASK(7, 5)
+#define YAS537_MTC3_ADD_BIT BIT(4)
+#define YAS537_HCK_MASK_PREP GENMASK(4, 0)
+#define YAS537_HCK_MASK_GET GENMASK(7, 4)
+#define YAS537_LCK_MASK_PREP GENMASK(4, 0)
+#define YAS537_LCK_MASK_GET GENMASK(3, 0)
+#define YAS537_OC_MASK_GET GENMASK(5, 0)
/* Turn off device regulators etc after 5 seconds of inactivity */
#define YAS5XX_AUTOSUSPEND_DELAY_MS 5000
+enum chip_ids {
+ yas530,
+ yas532,
+ yas533,
+ yas537,
+};
+
+static const int yas530_volatile_reg[] = {
+ YAS530_ACTUATE_INIT_COIL,
+ YAS530_MEASURE,
+};
+
+static const int yas537_volatile_reg[] = {
+ YAS537_MEASURE,
+};
+
struct yas5xx_calibration {
/* Linearization calibration x, y1, y2 */
s32 r[3];
u32 f[3];
/* Temperature compensation calibration */
- s32 Cx, Cy1, Cy2;
+ s16 Cx, Cy1, Cy2;
/* Misc calibration coefficients */
- s32 a2, a3, a4, a5, a6, a7, a8, a9, k;
+ s8 a2, a3, a4, a6, a7, a8;
+ s16 a5, a9;
+ u8 k;
/* clock divider */
u8 dck;
};
+struct yas5xx;
+
+/**
+ * struct yas5xx_chip_info - device-specific data and function pointers
+ * @devid: device ID number
+ * @product_name: product name of the YAS variant
+ * @version_names: version letters or namings
+ * @volatile_reg: device-specific volatile registers
+ * @volatile_reg_qty: quantity of device-specific volatile registers
+ * @scaling_val2: scaling value for IIO_CHAN_INFO_SCALE
+ * @t_ref: number of counts at reference temperature 20 °C
+ * @min_temp_x10: starting point of temperature counting in 1/10:s degrees Celsius
+ * @get_measure: function pointer to get a measurement
+ * @get_calibration_data: function pointer to get calibration data
+ * @dump_calibration: function pointer to dump calibration for debugging
+ * @measure_offsets: function pointer to measure the offsets
+ * @power_on: function pointer to power-on procedure
+ *
+ * The "t_ref" value for YAS532/533 is known from the Android driver.
+ * For YAS530 and YAS537 it was approximately measured.
+ *
+ * The temperatures "min_temp_x10" are derived from the temperature resolutions
+ * given in the data sheets.
+ */
+struct yas5xx_chip_info {
+ unsigned int devid;
+ const char *product_name;
+ const char *version_names[2];
+ const int *volatile_reg;
+ int volatile_reg_qty;
+ u32 scaling_val2;
+ u16 t_ref;
+ s16 min_temp_x10;
+ int (*get_measure)(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo);
+ int (*get_calibration_data)(struct yas5xx *yas5xx);
+ void (*dump_calibration)(struct yas5xx *yas5xx);
+ int (*measure_offsets)(struct yas5xx *yas5xx);
+ int (*power_on)(struct yas5xx *yas5xx);
+};
+
/**
* struct yas5xx - state container for the YAS5xx driver
* @dev: parent device pointer
- * @devid: device ID number
+ * @chip_info: device-specific data and function pointers
* @version: device version
- * @name: device name
* @calibration: calibration settings from the OTP storage
* @hard_offsets: offsets for each axis measured with initcoil actuated
* @orientation: mounting matrix, flipped axis etc
@@ -128,11 +221,10 @@ struct yas5xx_calibration {
*/
struct yas5xx {
struct device *dev;
- unsigned int devid;
+ const struct yas5xx_chip_info *chip_info;
unsigned int version;
- char name[16];
struct yas5xx_calibration calibration;
- u8 hard_offsets[3];
+ s8 hard_offsets[3];
struct iio_mount_matrix orientation;
struct regmap *map;
struct regulator_bulk_data regs[2];
@@ -179,23 +271,26 @@ static u16 yas532_extract_axis(u8 *data)
}
/**
- * yas5xx_measure() - Make a measure from the hardware
+ * yas530_measure() - Make a measure from the hardware
* @yas5xx: The device state
* @t: the raw temperature measurement
* @x: the raw x axis measurement
* @y1: the y1 axis measurement
* @y2: the y2 axis measurement
* @return: 0 on success or error code
+ *
+ * Used by YAS530, YAS532 and YAS533.
*/
-static int yas5xx_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y2)
+static int yas530_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y2)
{
+ const struct yas5xx_chip_info *ci = yas5xx->chip_info;
unsigned int busy;
u8 data[8];
int ret;
u16 val;
mutex_lock(&yas5xx->lock);
- ret = regmap_write(yas5xx->map, YAS5XX_MEASURE, YAS5XX_MEASURE_START);
+ ret = regmap_write(yas5xx->map, YAS530_MEASURE, YAS5XX_MEASURE_START);
if (ret < 0)
goto out_unlock;
@@ -219,7 +314,7 @@ static int yas5xx_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y
mutex_unlock(&yas5xx->lock);
- switch (yas5xx->devid) {
+ switch (ci->devid) {
case YAS530_DEVICE_ID:
/*
* The t value is 9 bits in big endian format
@@ -261,8 +356,81 @@ out_unlock:
return ret;
}
-static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis)
+/**
+ * yas537_measure() - Make a measure from the hardware
+ * @yas5xx: The device state
+ * @t: the raw temperature measurement
+ * @x: the raw x axis measurement
+ * @y1: the y1 axis measurement
+ * @y2: the y2 axis measurement
+ * @return: 0 on success or error code
+ */
+static int yas537_measure(struct yas5xx *yas5xx, u16 *t, u16 *x, u16 *y1, u16 *y2)
+{
+ struct yas5xx_calibration *c = &yas5xx->calibration;
+ unsigned int busy;
+ u8 data[8];
+ u16 xy1y2[3];
+ s32 h[3], s[3];
+ int i, ret;
+
+ mutex_lock(&yas5xx->lock);
+
+ /* Contrary to YAS530/532, also a "cont" bit is set, meaning unknown */
+ ret = regmap_write(yas5xx->map, YAS537_MEASURE, YAS5XX_MEASURE_START |
+ YAS5XX_MEASURE_CONT);
+ if (ret < 0)
+ goto out_unlock;
+
+ /* Use same timeout like YAS530/532 but the bit is in data row 2 */
+ ret = regmap_read_poll_timeout(yas5xx->map, YAS5XX_MEASURE_DATA + 2, busy,
+ !(busy & YAS5XX_MEASURE_DATA_BUSY),
+ 500, 20000);
+ if (ret) {
+ dev_err(yas5xx->dev, "timeout waiting for measurement\n");
+ goto out_unlock;
+ }
+
+ ret = regmap_bulk_read(yas5xx->map, YAS5XX_MEASURE_DATA,
+ data, sizeof(data));
+ if (ret)
+ goto out_unlock;
+
+ mutex_unlock(&yas5xx->lock);
+
+ *t = get_unaligned_be16(&data[0]);
+ xy1y2[0] = FIELD_GET(GENMASK(13, 0), get_unaligned_be16(&data[2]));
+ xy1y2[1] = get_unaligned_be16(&data[4]);
+ xy1y2[2] = get_unaligned_be16(&data[6]);
+
+ /* The second version of YAS537 needs to include calibration coefficients */
+ if (yas5xx->version == YAS537_VERSION_1) {
+ for (i = 0; i < 3; i++)
+ s[i] = xy1y2[i] - BIT(13);
+ h[0] = (c->k * (128 * s[0] + c->a2 * s[1] + c->a3 * s[2])) / BIT(13);
+ h[1] = (c->k * (c->a4 * s[0] + c->a5 * s[1] + c->a6 * s[2])) / BIT(13);
+ h[2] = (c->k * (c->a7 * s[0] + c->a8 * s[1] + c->a9 * s[2])) / BIT(13);
+ for (i = 0; i < 3; i++) {
+ clamp_val(h[i], -BIT(13), BIT(13) - 1);
+ xy1y2[i] = h[i] + BIT(13);
+ }
+ }
+
+ *x = xy1y2[0];
+ *y1 = xy1y2[1];
+ *y2 = xy1y2[2];
+
+ return 0;
+
+out_unlock:
+ mutex_unlock(&yas5xx->lock);
+ return ret;
+}
+
+/* Used by YAS530, YAS532 and YAS533 */
+static s32 yas530_linearize(struct yas5xx *yas5xx, u16 val, int axis)
{
+ const struct yas5xx_chip_info *ci = yas5xx->chip_info;
struct yas5xx_calibration *c = &yas5xx->calibration;
static const s32 yas532ac_coef[] = {
YAS532_VERSION_AC_COEF_X,
@@ -272,7 +440,7 @@ static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis)
s32 coef;
/* Select coefficients */
- switch (yas5xx->devid) {
+ switch (ci->devid) {
case YAS530_DEVICE_ID:
if (yas5xx->version == YAS530_VERSION_A)
coef = YAS530_VERSION_A_COEF;
@@ -302,8 +470,24 @@ static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis)
(yas5xx->hard_offsets[axis] - c->r[axis]) * coef;
}
+static s32 yas5xx_calc_temperature(struct yas5xx *yas5xx, u16 t)
+{
+ const struct yas5xx_chip_info *ci = yas5xx->chip_info;
+ s32 to;
+ u16 t_ref;
+ s16 min_temp_x10;
+ int ref_temp_x10;
+
+ t_ref = ci->t_ref;
+ min_temp_x10 = ci->min_temp_x10;
+ ref_temp_x10 = 200;
+
+ to = (min_temp_x10 + ((ref_temp_x10 - min_temp_x10) * t / t_ref)) * 100;
+ return to;
+}
+
/**
- * yas5xx_get_measure() - Measure a sample of all axis and process
+ * yas530_get_measure() - Measure a sample of all axis and process
* @yas5xx: The device state
* @to: Temperature out
* @xo: X axis out
@@ -311,36 +495,50 @@ static s32 yas5xx_linearize(struct yas5xx *yas5xx, u16 val, int axis)
* @zo: Z axis out
* @return: 0 on success or error code
*
- * Returned values are in nanotesla according to some code.
+ * Used by YAS530, YAS532 and YAS533.
*/
-static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo)
+static int yas530_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo)
{
+ const struct yas5xx_chip_info *ci = yas5xx->chip_info;
struct yas5xx_calibration *c = &yas5xx->calibration;
- u16 t, x, y1, y2;
- /* These are "signed x, signed y1 etc */
+ u16 t_ref, t_comp, t, x, y1, y2;
+ /* These are signed x, signed y1 etc */
s32 sx, sy1, sy2, sy, sz;
int ret;
/* We first get raw data that needs to be translated to [x,y,z] */
- ret = yas5xx_measure(yas5xx, &t, &x, &y1, &y2);
+ ret = yas530_measure(yas5xx, &t, &x, &y1, &y2);
if (ret)
return ret;
/* Do some linearization if available */
- sx = yas5xx_linearize(yas5xx, x, 0);
- sy1 = yas5xx_linearize(yas5xx, y1, 1);
- sy2 = yas5xx_linearize(yas5xx, y2, 2);
+ sx = yas530_linearize(yas5xx, x, 0);
+ sy1 = yas530_linearize(yas5xx, y1, 1);
+ sy2 = yas530_linearize(yas5xx, y2, 2);
+
+ /*
+ * Set the temperature for compensation (unit: counts):
+ * YAS532/YAS533 version AC uses the temperature deviation as a
+ * multiplier. YAS530 and YAS532 version AB use solely the t value.
+ */
+ t_ref = ci->t_ref;
+ if (ci->devid == YAS532_DEVICE_ID &&
+ yas5xx->version == YAS532_VERSION_AC) {
+ t_comp = t - t_ref;
+ } else {
+ t_comp = t;
+ }
/*
* Temperature compensation for x, y1, y2 respectively:
*
- * Cx * t
- * x' = x - ------
- * 100
+ * Cx * t_comp
+ * x' = x - -----------
+ * 100
*/
- sx = sx - (c->Cx * t) / 100;
- sy1 = sy1 - (c->Cy1 * t) / 100;
- sy2 = sy2 - (c->Cy2 * t) / 100;
+ sx = sx - (c->Cx * t_comp) / 100;
+ sy1 = sy1 - (c->Cy1 * t_comp) / 100;
+ sy2 = sy2 - (c->Cy2 * t_comp) / 100;
/*
* Break y1 and y2 into y and z, y1 and y2 are apparently encoding
@@ -349,11 +547,9 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo,
sy = sy1 - sy2;
sz = -sy1 - sy2;
- /*
- * FIXME: convert to Celsius? Just guessing this is given
- * as 1/10:s of degrees so multiply by 100 to get millicentigrades.
- */
- *to = t * 100;
+ /* Calculate temperature readout */
+ *to = yas5xx_calc_temperature(yas5xx, t);
+
/*
* Calibrate [x,y,z] with some formulas like this:
*
@@ -376,19 +572,56 @@ static int yas5xx_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo,
return 0;
}
+/**
+ * yas537_get_measure() - Measure a sample of all axis and process
+ * @yas5xx: The device state
+ * @to: Temperature out
+ * @xo: X axis out
+ * @yo: Y axis out
+ * @zo: Z axis out
+ * @return: 0 on success or error code
+ */
+static int yas537_get_measure(struct yas5xx *yas5xx, s32 *to, s32 *xo, s32 *yo, s32 *zo)
+{
+ u16 t, x, y1, y2;
+ int ret;
+
+ /* We first get raw data that needs to be translated to [x,y,z] */
+ ret = yas537_measure(yas5xx, &t, &x, &y1, &y2);
+ if (ret)
+ return ret;
+
+ /* Calculate temperature readout */
+ *to = yas5xx_calc_temperature(yas5xx, t);
+
+ /*
+ * Unfortunately, no linearization or temperature compensation formulas
+ * are known for YAS537.
+ */
+
+ /* Calculate x, y, z from x, y1, y2 */
+ *xo = (x - BIT(13)) * 300;
+ *yo = (y1 - y2) * 1732 / 10;
+ *zo = (-y1 - y2 + BIT(14)) * 300;
+
+ return 0;
+}
+
static int yas5xx_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2,
long mask)
{
struct yas5xx *yas5xx = iio_priv(indio_dev);
+ const struct yas5xx_chip_info *ci = yas5xx->chip_info;
s32 t, x, y, z;
int ret;
switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
case IIO_CHAN_INFO_RAW:
pm_runtime_get_sync(yas5xx->dev);
- ret = yas5xx_get_measure(yas5xx, &t, &x, &y, &z);
+ ret = ci->get_measure(yas5xx, &t, &x, &y, &z);
pm_runtime_mark_last_busy(yas5xx->dev);
pm_runtime_put_autosuspend(yas5xx->dev);
if (ret)
@@ -412,19 +645,8 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev,
}
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
- if (chan->address == 0) {
- /* Temperature is unscaled */
- *val = 1;
- return IIO_VAL_INT;
- }
- /*
- * The axis values are in nanotesla according to the vendor
- * drivers, but is clearly in microtesla according to
- * experiments. Since 1 uT = 0.01 Gauss, we need to divide
- * by 100000000 (10^8) to get to Gauss from the raw value.
- */
*val = 1;
- *val2 = 100000000;
+ *val2 = ci->scaling_val2;
return IIO_VAL_FRACTIONAL;
default:
/* Unknown request */
@@ -435,11 +657,12 @@ static int yas5xx_read_raw(struct iio_dev *indio_dev,
static void yas5xx_fill_buffer(struct iio_dev *indio_dev)
{
struct yas5xx *yas5xx = iio_priv(indio_dev);
+ const struct yas5xx_chip_info *ci = yas5xx->chip_info;
s32 t, x, y, z;
int ret;
pm_runtime_get_sync(yas5xx->dev);
- ret = yas5xx_get_measure(yas5xx, &t, &x, &y, &z);
+ ret = ci->get_measure(yas5xx, &t, &x, &y, &z);
pm_runtime_mark_last_busy(yas5xx->dev);
pm_runtime_put_autosuspend(yas5xx->dev);
if (ret) {
@@ -505,7 +728,7 @@ static const struct iio_chan_spec yas5xx_channels[] = {
.address = 0,
.scan_index = 0,
.scan_type = {
- .sign = 'u',
+ .sign = 's',
.realbits = 32,
.storagebits = 32,
.endianness = IIO_CPU,
@@ -525,9 +748,26 @@ static const struct iio_info yas5xx_info = {
static bool yas5xx_volatile_reg(struct device *dev, unsigned int reg)
{
- return reg == YAS5XX_ACTUATE_INIT_COIL ||
- reg == YAS5XX_MEASURE ||
- (reg >= YAS5XX_MEASURE_DATA && reg <= YAS5XX_MEASURE_DATA + 8);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct yas5xx *yas5xx = iio_priv(indio_dev);
+ const struct yas5xx_chip_info *ci = yas5xx->chip_info;
+ int reg_qty;
+ int i;
+
+ if (reg >= YAS5XX_MEASURE_DATA && reg < YAS5XX_MEASURE_DATA + 8)
+ return true;
+
+ /*
+ * YAS versions share different registers on the same address,
+ * need to differentiate.
+ */
+ reg_qty = ci->volatile_reg_qty;
+ for (i = 0; i < reg_qty; i++) {
+ if (reg == ci->volatile_reg[i])
+ return true;
+ }
+
+ return false;
}
/* TODO: enable regmap cache, using mark dirty and sync at runtime resume */
@@ -539,11 +779,13 @@ static const struct regmap_config yas5xx_regmap_config = {
};
/**
- * yas53x_extract_calibration() - extracts the a2-a9 and k calibration
+ * yas530_extract_calibration() - extracts the a2-a9 and k calibration
* @data: the bitfield to use
* @c: the calibration to populate
+ *
+ * Used by YAS530, YAS532 and YAS533.
*/
-static void yas53x_extract_calibration(u8 *data, struct yas5xx_calibration *c)
+static void yas530_extract_calibration(u8 *data, struct yas5xx_calibration *c)
{
u64 val = get_unaligned_be64(data);
@@ -581,24 +823,27 @@ static int yas530_get_calibration_data(struct yas5xx *yas5xx)
int ret;
/* Dummy read, first read is ALWAYS wrong */
- ret = regmap_bulk_read(yas5xx->map, YAS5XX_CAL, data, sizeof(data));
+ ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data));
if (ret)
return ret;
/* Actual calibration readout */
- ret = regmap_bulk_read(yas5xx->map, YAS5XX_CAL, data, sizeof(data));
+ ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data));
if (ret)
return ret;
- dev_dbg(yas5xx->dev, "calibration data: %*ph\n", 14, data);
+ dev_dbg(yas5xx->dev, "calibration data: %16ph\n", data);
+ /* Contribute calibration data to the input pool for kernel entropy */
add_device_randomness(data, sizeof(data));
+
+ /* Extract version */
yas5xx->version = data[15] & GENMASK(1, 0);
/* Extract the calibration from the bitfield */
c->Cx = data[0] * 6 - 768;
c->Cy1 = data[1] * 6 - 768;
c->Cy2 = data[2] * 6 - 768;
- yas53x_extract_calibration(&data[3], c);
+ yas530_extract_calibration(&data[3], c);
/*
* Extract linearization:
@@ -618,6 +863,7 @@ static int yas530_get_calibration_data(struct yas5xx *yas5xx)
c->r[0] = sign_extend32(FIELD_GET(GENMASK(28, 23), val), 5);
c->r[1] = sign_extend32(FIELD_GET(GENMASK(20, 15), val), 5);
c->r[2] = sign_extend32(FIELD_GET(GENMASK(12, 7), val), 5);
+
return 0;
}
@@ -629,22 +875,22 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx)
int ret;
/* Dummy read, first read is ALWAYS wrong */
- ret = regmap_bulk_read(yas5xx->map, YAS5XX_CAL, data, sizeof(data));
+ ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data));
if (ret)
return ret;
/* Actual calibration readout */
- ret = regmap_bulk_read(yas5xx->map, YAS5XX_CAL, data, sizeof(data));
+ ret = regmap_bulk_read(yas5xx->map, YAS530_CAL, data, sizeof(data));
if (ret)
return ret;
- dev_dbg(yas5xx->dev, "calibration data: %*ph\n", 14, data);
+ dev_dbg(yas5xx->dev, "calibration data: %14ph\n", data);
/* Sanity check, is this all zeroes? */
- if (memchr_inv(data, 0x00, 13) == NULL) {
- if (!(data[13] & BIT(7)))
- dev_warn(yas5xx->dev, "calibration is blank!\n");
- }
+ if (!memchr_inv(data, 0x00, 13) && !(data[13] & BIT(7)))
+ dev_warn(yas5xx->dev, "calibration is blank!\n");
+ /* Contribute calibration data to the input pool for kernel entropy */
add_device_randomness(data, sizeof(data));
+
/* Only one bit of version info reserved here as far as we know */
yas5xx->version = data[13] & BIT(0);
@@ -652,7 +898,8 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx)
c->Cx = data[0] * 10 - 1280;
c->Cy1 = data[1] * 10 - 1280;
c->Cy2 = data[2] * 10 - 1280;
- yas53x_extract_calibration(&data[3], c);
+ yas530_extract_calibration(&data[3], c);
+
/*
* Extract linearization:
* Linearization layout in the 32 bits at byte 10:
@@ -675,7 +922,204 @@ static int yas532_get_calibration_data(struct yas5xx *yas5xx)
return 0;
}
-static void yas5xx_dump_calibration(struct yas5xx *yas5xx)
+static int yas537_get_calibration_data(struct yas5xx *yas5xx)
+{
+ struct yas5xx_calibration *c = &yas5xx->calibration;
+ u8 data[17];
+ u32 val1, val2, val3, val4;
+ int i, ret;
+
+ /* Writing SRST register */
+ ret = regmap_write(yas5xx->map, YAS537_SRST, BIT(1));
+ if (ret)
+ return ret;
+
+ /* Calibration readout, YAS537 needs one readout only */
+ ret = regmap_bulk_read(yas5xx->map, YAS537_CAL, data, sizeof(data));
+ if (ret)
+ return ret;
+ dev_dbg(yas5xx->dev, "calibration data: %17ph\n", data);
+
+ /* Sanity check, is this all zeroes? */
+ if (!memchr_inv(data, 0x00, 16) && !FIELD_GET(GENMASK(5, 0), data[16]))
+ dev_warn(yas5xx->dev, "calibration is blank!\n");
+
+ /* Contribute calibration data to the input pool for kernel entropy */
+ add_device_randomness(data, sizeof(data));
+
+ /* Extract version information */
+ yas5xx->version = FIELD_GET(GENMASK(7, 6), data[16]);
+
+ /* There are two versions of YAS537 behaving differently */
+ switch (yas5xx->version) {
+ case YAS537_VERSION_0:
+ /*
+ * The first version simply writes data back into registers:
+ *
+ * data[0] YAS537_MTC 0x93
+ * data[1] 0x94
+ * data[2] 0x95
+ * data[3] 0x96
+ * data[4] 0x97
+ * data[5] 0x98
+ * data[6] 0x99
+ * data[7] 0x9a
+ * data[8] 0x9b
+ * data[9] 0x9c
+ * data[10] 0x9d
+ * data[11] YAS537_OC 0x9e
+ *
+ * data[12] YAS537_OFFSET_X 0x84
+ * data[13] YAS537_OFFSET_Y1 0x85
+ * data[14] YAS537_OFFSET_Y2 0x86
+ *
+ * data[15] YAS537_HCK 0x88
+ * data[16] YAS537_LCK 0x89
+ */
+ for (i = 0; i < 12; i++) {
+ ret = regmap_write(yas5xx->map, YAS537_MTC + i,
+ data[i]);
+ if (ret)
+ return ret;
+ }
+ for (i = 0; i < 3; i++) {
+ ret = regmap_write(yas5xx->map, YAS537_OFFSET_X + i,
+ data[i + 12]);
+ if (ret)
+ return ret;
+ yas5xx->hard_offsets[i] = data[i + 12];
+ }
+ for (i = 0; i < 2; i++) {
+ ret = regmap_write(yas5xx->map, YAS537_HCK + i,
+ data[i + 15]);
+ if (ret)
+ return ret;
+ }
+ break;
+ case YAS537_VERSION_1:
+ /*
+ * The second version writes some data into registers but also
+ * extracts calibration coefficients.
+ *
+ * Registers being written:
+ *
+ * data[0] YAS537_MTC 0x93
+ * data[1] YAS537_MTC+1 0x94
+ * data[2] YAS537_MTC+2 0x95
+ * data[3] YAS537_MTC+3 (partially) 0x96
+ *
+ * data[12] YAS537_OFFSET_X 0x84
+ * data[13] YAS537_OFFSET_Y1 0x85
+ * data[14] YAS537_OFFSET_Y2 0x86
+ *
+ * data[15] YAS537_HCK (partially) 0x88
+ * YAS537_LCK (partially) 0x89
+ * data[16] YAS537_OC (partially) 0x9e
+ */
+ for (i = 0; i < 3; i++) {
+ ret = regmap_write(yas5xx->map, YAS537_MTC + i,
+ data[i]);
+ if (ret)
+ return ret;
+ }
+ for (i = 0; i < 3; i++) {
+ ret = regmap_write(yas5xx->map, YAS537_OFFSET_X + i,
+ data[i + 12]);
+ if (ret)
+ return ret;
+ yas5xx->hard_offsets[i] = data[i + 12];
+ }
+ /*
+ * Visualization of partially taken data:
+ *
+ * data[3] n 7 6 5 4 3 2 1 0
+ * YAS537_MTC+3 x x x 1 0 0 0 0
+ *
+ * data[15] n 7 6 5 4 3 2 1 0
+ * YAS537_HCK x x x x 0
+ *
+ * data[15] n 7 6 5 4 3 2 1 0
+ * YAS537_LCK x x x x 0
+ *
+ * data[16] n 7 6 5 4 3 2 1 0
+ * YAS537_OC x x x x x x
+ */
+ ret = regmap_write(yas5xx->map, YAS537_MTC + 3,
+ FIELD_PREP(YAS537_MTC3_MASK_PREP,
+ FIELD_GET(YAS537_MTC3_MASK_GET, data[3])) |
+ YAS537_MTC3_ADD_BIT);
+ if (ret)
+ return ret;
+ ret = regmap_write(yas5xx->map, YAS537_HCK,
+ FIELD_PREP(YAS537_HCK_MASK_PREP,
+ FIELD_GET(YAS537_HCK_MASK_GET, data[15])));
+ if (ret)
+ return ret;
+ ret = regmap_write(yas5xx->map, YAS537_LCK,
+ FIELD_PREP(YAS537_LCK_MASK_PREP,
+ FIELD_GET(YAS537_LCK_MASK_GET, data[15])));
+ if (ret)
+ return ret;
+ ret = regmap_write(yas5xx->map, YAS537_OC,
+ FIELD_GET(YAS537_OC_MASK_GET, data[16]));
+ if (ret)
+ return ret;
+ /*
+ * For data extraction, build some blocks. Four 32-bit blocks
+ * look appropriate.
+ *
+ * n 7 6 5 4 3 2 1 0
+ * data[0] 0 [ Cx Cx Cx Cx Cx Cx Cx Cx ] bits 31 .. 24
+ * data[1] 1 [ Cx C1 C1 C1 C1 C1 C1 C1 ] bits 23 .. 16
+ * data[2] 2 [ C1 C1 C2 C2 C2 C2 C2 C2 ] bits 15 .. 8
+ * data[3] 3 [ C2 C2 C2 ] bits 7 .. 0
+ *
+ * n 7 6 5 4 3 2 1 0
+ * data[3] 0 [ a2 a2 a2 a2 a2 ] bits 31 .. 24
+ * data[4] 1 [ a2 a2 a3 a3 a3 a3 a3 a3 ] bits 23 .. 16
+ * data[5] 2 [ a3 a4 a4 a4 a4 a4 a4 a4 ] bits 15 .. 8
+ * data[6] 3 [ a4 ] bits 7 .. 0
+ *
+ * n 7 6 5 4 3 2 1 0
+ * data[6] 0 [ a5 a5 a5 a5 a5 a5 a5 ] bits 31 .. 24
+ * data[7] 1 [ a5 a5 a6 a6 a6 a6 a6 a6 ] bits 23 .. 16
+ * data[8] 2 [ a6 a7 a7 a7 a7 a7 a7 a7 ] bits 15 .. 8
+ * data[9] 3 [ a7 ] bits 7 .. 0
+ *
+ * n 7 6 5 4 3 2 1 0
+ * data[9] 0 [ a8 a8 a8 a8 a8 a8 a8 ] bits 31 .. 24
+ * data[10] 1 [ a9 a9 a9 a9 a9 a9 a9 a9 ] bits 23 .. 16
+ * data[11] 2 [ a9 k k k k k k k ] bits 15 .. 8
+ * data[12] 3 [ ] bits 7 .. 0
+ */
+ val1 = get_unaligned_be32(&data[0]);
+ val2 = get_unaligned_be32(&data[3]);
+ val3 = get_unaligned_be32(&data[6]);
+ val4 = get_unaligned_be32(&data[9]);
+ /* Extract calibration coefficients and modify */
+ c->Cx = FIELD_GET(GENMASK(31, 23), val1) - 256;
+ c->Cy1 = FIELD_GET(GENMASK(22, 14), val1) - 256;
+ c->Cy2 = FIELD_GET(GENMASK(13, 5), val1) - 256;
+ c->a2 = FIELD_GET(GENMASK(28, 22), val2) - 64;
+ c->a3 = FIELD_GET(GENMASK(21, 15), val2) - 64;
+ c->a4 = FIELD_GET(GENMASK(14, 7), val2) - 128;
+ c->a5 = FIELD_GET(GENMASK(30, 22), val3) - 112;
+ c->a6 = FIELD_GET(GENMASK(21, 15), val3) - 64;
+ c->a7 = FIELD_GET(GENMASK(14, 7), val3) - 128;
+ c->a8 = FIELD_GET(GENMASK(30, 24), val4) - 64;
+ c->a9 = FIELD_GET(GENMASK(23, 15), val4) - 112;
+ c->k = FIELD_GET(GENMASK(14, 8), val4);
+ break;
+ default:
+ dev_err(yas5xx->dev, "unknown version of YAS537\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Used by YAS530, YAS532 and YAS533 */
+static void yas530_dump_calibration(struct yas5xx *yas5xx)
{
struct yas5xx_calibration *c = &yas5xx->calibration;
@@ -698,20 +1142,42 @@ static void yas5xx_dump_calibration(struct yas5xx *yas5xx)
dev_dbg(yas5xx->dev, "dck = %d\n", c->dck);
}
-static int yas5xx_set_offsets(struct yas5xx *yas5xx, s8 ox, s8 oy1, s8 oy2)
+static void yas537_dump_calibration(struct yas5xx *yas5xx)
+{
+ struct yas5xx_calibration *c = &yas5xx->calibration;
+
+ if (yas5xx->version == YAS537_VERSION_1) {
+ dev_dbg(yas5xx->dev, "Cx = %d\n", c->Cx);
+ dev_dbg(yas5xx->dev, "Cy1 = %d\n", c->Cy1);
+ dev_dbg(yas5xx->dev, "Cy2 = %d\n", c->Cy2);
+ dev_dbg(yas5xx->dev, "a2 = %d\n", c->a2);
+ dev_dbg(yas5xx->dev, "a3 = %d\n", c->a3);
+ dev_dbg(yas5xx->dev, "a4 = %d\n", c->a4);
+ dev_dbg(yas5xx->dev, "a5 = %d\n", c->a5);
+ dev_dbg(yas5xx->dev, "a6 = %d\n", c->a6);
+ dev_dbg(yas5xx->dev, "a7 = %d\n", c->a7);
+ dev_dbg(yas5xx->dev, "a8 = %d\n", c->a8);
+ dev_dbg(yas5xx->dev, "a9 = %d\n", c->a9);
+ dev_dbg(yas5xx->dev, "k = %d\n", c->k);
+ }
+}
+
+/* Used by YAS530, YAS532 and YAS533 */
+static int yas530_set_offsets(struct yas5xx *yas5xx, s8 ox, s8 oy1, s8 oy2)
{
int ret;
- ret = regmap_write(yas5xx->map, YAS5XX_OFFSET_X, ox);
+ ret = regmap_write(yas5xx->map, YAS530_OFFSET_X, ox);
if (ret)
return ret;
- ret = regmap_write(yas5xx->map, YAS5XX_OFFSET_Y1, oy1);
+ ret = regmap_write(yas5xx->map, YAS530_OFFSET_Y1, oy1);
if (ret)
return ret;
- return regmap_write(yas5xx->map, YAS5XX_OFFSET_Y2, oy2);
+ return regmap_write(yas5xx->map, YAS530_OFFSET_Y2, oy2);
}
-static s8 yas5xx_adjust_offset(s8 old, int bit, u16 center, u16 measure)
+/* Used by YAS530, YAS532 and YAS533 */
+static s8 yas530_adjust_offset(s8 old, int bit, u16 center, u16 measure)
{
if (measure > center)
return old + BIT(bit);
@@ -720,8 +1186,10 @@ static s8 yas5xx_adjust_offset(s8 old, int bit, u16 center, u16 measure)
return old;
}
-static int yas5xx_meaure_offsets(struct yas5xx *yas5xx)
+/* Used by YAS530, YAS532 and YAS533 */
+static int yas530_measure_offsets(struct yas5xx *yas5xx)
{
+ const struct yas5xx_chip_info *ci = yas5xx->chip_info;
int ret;
u16 center;
u16 t, x, y1, y2;
@@ -729,12 +1197,12 @@ static int yas5xx_meaure_offsets(struct yas5xx *yas5xx)
int i;
/* Actuate the init coil and measure offsets */
- ret = regmap_write(yas5xx->map, YAS5XX_ACTUATE_INIT_COIL, 0);
+ ret = regmap_write(yas5xx->map, YAS530_ACTUATE_INIT_COIL, 0);
if (ret)
return ret;
/* When the initcoil is active this should be around the center */
- switch (yas5xx->devid) {
+ switch (ci->devid) {
case YAS530_DEVICE_ID:
center = YAS530_DATA_CENTER;
break;
@@ -763,26 +1231,26 @@ static int yas5xx_meaure_offsets(struct yas5xx *yas5xx)
oy2 = 0;
for (i = 4; i >= 0; i--) {
- ret = yas5xx_set_offsets(yas5xx, ox, oy1, oy2);
+ ret = yas530_set_offsets(yas5xx, ox, oy1, oy2);
if (ret)
return ret;
- ret = yas5xx_measure(yas5xx, &t, &x, &y1, &y2);
+ ret = yas530_measure(yas5xx, &t, &x, &y1, &y2);
if (ret)
return ret;
dev_dbg(yas5xx->dev, "measurement %d: x=%d, y1=%d, y2=%d\n",
5-i, x, y1, y2);
- ox = yas5xx_adjust_offset(ox, i, center, x);
- oy1 = yas5xx_adjust_offset(oy1, i, center, y1);
- oy2 = yas5xx_adjust_offset(oy2, i, center, y2);
+ ox = yas530_adjust_offset(ox, i, center, x);
+ oy1 = yas530_adjust_offset(oy1, i, center, y1);
+ oy2 = yas530_adjust_offset(oy2, i, center, y2);
}
/* Needed for calibration algorithm */
yas5xx->hard_offsets[0] = ox;
yas5xx->hard_offsets[1] = oy1;
yas5xx->hard_offsets[2] = oy2;
- ret = yas5xx_set_offsets(yas5xx, ox, oy1, oy2);
+ ret = yas530_set_offsets(yas5xx, ox, oy1, oy2);
if (ret)
return ret;
@@ -791,35 +1259,139 @@ static int yas5xx_meaure_offsets(struct yas5xx *yas5xx)
return 0;
}
-static int yas5xx_power_on(struct yas5xx *yas5xx)
+/* Used by YAS530, YAS532 and YAS533 */
+static int yas530_power_on(struct yas5xx *yas5xx)
{
unsigned int val;
int ret;
/* Zero the test registers */
- ret = regmap_write(yas5xx->map, YAS5XX_TEST1, 0);
+ ret = regmap_write(yas5xx->map, YAS530_TEST1, 0);
if (ret)
return ret;
- ret = regmap_write(yas5xx->map, YAS5XX_TEST2, 0);
+ ret = regmap_write(yas5xx->map, YAS530_TEST2, 0);
if (ret)
return ret;
/* Set up for no interrupts, calibrated clock divider */
val = FIELD_PREP(YAS5XX_CONFIG_CCK_MASK, yas5xx->calibration.dck);
- ret = regmap_write(yas5xx->map, YAS5XX_CONFIG, val);
+ ret = regmap_write(yas5xx->map, YAS530_CONFIG, val);
if (ret)
return ret;
/* Measure interval 0 (back-to-back?) */
- return regmap_write(yas5xx->map, YAS5XX_MEASURE_INTERVAL, 0);
+ return regmap_write(yas5xx->map, YAS530_MEASURE_INTERVAL, 0);
}
+static int yas537_power_on(struct yas5xx *yas5xx)
+{
+ __be16 buf;
+ int ret;
+ u8 intrvl;
+
+ /* Writing ADCCAL and TRM registers */
+ buf = cpu_to_be16(GENMASK(9, 3));
+ ret = regmap_bulk_write(yas5xx->map, YAS537_ADCCAL, &buf, sizeof(buf));
+ if (ret)
+ return ret;
+ ret = regmap_write(yas5xx->map, YAS537_TRM, GENMASK(7, 0));
+ if (ret)
+ return ret;
+
+ /* The interval value is static in regular operation */
+ intrvl = (YAS537_DEFAULT_SENSOR_DELAY_MS * MILLI
+ - YAS537_MEASURE_TIME_WORST_US) / 4100;
+ ret = regmap_write(yas5xx->map, YAS537_MEASURE_INTERVAL, intrvl);
+ if (ret)
+ return ret;
+
+ /* The average value is also static in regular operation */
+ ret = regmap_write(yas5xx->map, YAS537_AVR, YAS537_MAG_AVERAGE_32_MASK);
+ if (ret)
+ return ret;
+
+ /* Perform the "rcoil" part but skip the "last_after_rcoil" read */
+ ret = regmap_write(yas5xx->map, YAS537_CONFIG, BIT(3));
+ if (ret)
+ return ret;
+
+ /* Wait until the coil has ramped up */
+ usleep_range(YAS537_MAG_RCOIL_TIME_US, YAS537_MAG_RCOIL_TIME_US + 100);
+
+ return 0;
+}
+
+static const struct yas5xx_chip_info yas5xx_chip_info_tbl[] = {
+ [yas530] = {
+ .devid = YAS530_DEVICE_ID,
+ .product_name = "YAS530 MS-3E",
+ .version_names = { "A", "B" },
+ .volatile_reg = yas530_volatile_reg,
+ .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg),
+ .scaling_val2 = 100000000, /* picotesla to Gauss */
+ .t_ref = 182, /* counts */
+ .min_temp_x10 = -620, /* 1/10:s degrees Celsius */
+ .get_measure = yas530_get_measure,
+ .get_calibration_data = yas530_get_calibration_data,
+ .dump_calibration = yas530_dump_calibration,
+ .measure_offsets = yas530_measure_offsets,
+ .power_on = yas530_power_on,
+ },
+ [yas532] = {
+ .devid = YAS532_DEVICE_ID,
+ .product_name = "YAS532 MS-3R",
+ .version_names = { "AB", "AC" },
+ .volatile_reg = yas530_volatile_reg,
+ .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg),
+ .scaling_val2 = 100000, /* nanotesla to Gauss */
+ .t_ref = 390, /* counts */
+ .min_temp_x10 = -500, /* 1/10:s degrees Celsius */
+ .get_measure = yas530_get_measure,
+ .get_calibration_data = yas532_get_calibration_data,
+ .dump_calibration = yas530_dump_calibration,
+ .measure_offsets = yas530_measure_offsets,
+ .power_on = yas530_power_on,
+ },
+ [yas533] = {
+ .devid = YAS532_DEVICE_ID,
+ .product_name = "YAS533 MS-3F",
+ .version_names = { "AB", "AC" },
+ .volatile_reg = yas530_volatile_reg,
+ .volatile_reg_qty = ARRAY_SIZE(yas530_volatile_reg),
+ .scaling_val2 = 100000, /* nanotesla to Gauss */
+ .t_ref = 390, /* counts */
+ .min_temp_x10 = -500, /* 1/10:s degrees Celsius */
+ .get_measure = yas530_get_measure,
+ .get_calibration_data = yas532_get_calibration_data,
+ .dump_calibration = yas530_dump_calibration,
+ .measure_offsets = yas530_measure_offsets,
+ .power_on = yas530_power_on,
+ },
+ [yas537] = {
+ .devid = YAS537_DEVICE_ID,
+ .product_name = "YAS537 MS-3T",
+ .version_names = { "v0", "v1" }, /* version naming unknown */
+ .volatile_reg = yas537_volatile_reg,
+ .volatile_reg_qty = ARRAY_SIZE(yas537_volatile_reg),
+ .scaling_val2 = 100000, /* nanotesla to Gauss */
+ .t_ref = 8120, /* counts */
+ .min_temp_x10 = -3860, /* 1/10:s degrees Celsius */
+ .get_measure = yas537_get_measure,
+ .get_calibration_data = yas537_get_calibration_data,
+ .dump_calibration = yas537_dump_calibration,
+ /* .measure_offets is not needed for yas537 */
+ .power_on = yas537_power_on,
+ },
+};
+
static int yas5xx_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct iio_dev *indio_dev;
struct device *dev = &i2c->dev;
struct yas5xx *yas5xx;
+ const struct yas5xx_chip_info *ci;
+ int id_check;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*yas5xx));
@@ -843,10 +1415,8 @@ static int yas5xx_probe(struct i2c_client *i2c,
return dev_err_probe(dev, ret, "cannot get regulators\n");
ret = regulator_bulk_enable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs);
- if (ret) {
- dev_err(dev, "cannot enable regulators\n");
- return ret;
- }
+ if (ret)
+ return dev_err_probe(dev, ret, "cannot enable regulators\n");
/* See comment in runtime resume callback */
usleep_range(31000, 40000);
@@ -854,57 +1424,55 @@ static int yas5xx_probe(struct i2c_client *i2c,
/* This will take the device out of reset if need be */
yas5xx->reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
if (IS_ERR(yas5xx->reset)) {
- ret = dev_err_probe(dev, PTR_ERR(yas5xx->reset),
- "failed to get reset line\n");
+ ret = dev_err_probe(dev, PTR_ERR(yas5xx->reset), "failed to get reset line\n");
goto reg_off;
}
yas5xx->map = devm_regmap_init_i2c(i2c, &yas5xx_regmap_config);
if (IS_ERR(yas5xx->map)) {
- dev_err(dev, "failed to allocate register map\n");
- ret = PTR_ERR(yas5xx->map);
+ ret = dev_err_probe(dev, PTR_ERR(yas5xx->map), "failed to allocate register map\n");
goto assert_reset;
}
- ret = regmap_read(yas5xx->map, YAS5XX_DEVICE_ID, &yas5xx->devid);
+ ci = device_get_match_data(dev);
+ if (!ci)
+ ci = (const struct yas5xx_chip_info *)id->driver_data;
+ yas5xx->chip_info = ci;
+
+ ret = regmap_read(yas5xx->map, YAS5XX_DEVICE_ID, &id_check);
if (ret)
goto assert_reset;
- switch (yas5xx->devid) {
- case YAS530_DEVICE_ID:
- ret = yas530_get_calibration_data(yas5xx);
- if (ret)
- goto assert_reset;
- dev_info(dev, "detected YAS530 MS-3E %s",
- yas5xx->version ? "B" : "A");
- strncpy(yas5xx->name, "yas530", sizeof(yas5xx->name));
- break;
- case YAS532_DEVICE_ID:
- ret = yas532_get_calibration_data(yas5xx);
- if (ret)
- goto assert_reset;
- dev_info(dev, "detected YAS532/YAS533 MS-3R/F %s",
- yas5xx->version ? "AC" : "AB");
- strncpy(yas5xx->name, "yas532", sizeof(yas5xx->name));
- break;
- default:
- ret = -ENODEV;
- dev_err(dev, "unhandled device ID %02x\n", yas5xx->devid);
+ if (id_check != ci->devid) {
+ ret = dev_err_probe(dev, -ENODEV,
+ "device ID %02x doesn't match %s\n",
+ id_check, id->name);
goto assert_reset;
}
- yas5xx_dump_calibration(yas5xx);
- ret = yas5xx_power_on(yas5xx);
+ ret = ci->get_calibration_data(yas5xx);
if (ret)
goto assert_reset;
- ret = yas5xx_meaure_offsets(yas5xx);
+
+ dev_info(dev, "detected %s %s\n", ci->product_name,
+ ci->version_names[yas5xx->version]);
+
+ ci->dump_calibration(yas5xx);
+
+ ret = ci->power_on(yas5xx);
if (ret)
goto assert_reset;
+ if (ci->measure_offsets) {
+ ret = ci->measure_offsets(yas5xx);
+ if (ret)
+ goto assert_reset;
+ }
+
indio_dev->info = &yas5xx_info;
indio_dev->available_scan_masks = yas5xx_scan_masks;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->name = yas5xx->name;
+ indio_dev->name = id->name;
indio_dev->channels = yas5xx_channels;
indio_dev->num_channels = ARRAY_SIZE(yas5xx_channels);
@@ -912,13 +1480,13 @@ static int yas5xx_probe(struct i2c_client *i2c,
yas5xx_handle_trigger,
NULL);
if (ret) {
- dev_err(dev, "triggered buffer setup failed\n");
+ dev_err_probe(dev, ret, "triggered buffer setup failed\n");
goto assert_reset;
}
ret = iio_device_register(indio_dev);
if (ret) {
- dev_err(dev, "device register failed\n");
+ dev_err_probe(dev, ret, "device register failed\n");
goto cleanup_buffer;
}
@@ -978,6 +1546,7 @@ static int yas5xx_runtime_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct yas5xx *yas5xx = iio_priv(indio_dev);
+ const struct yas5xx_chip_info *ci = yas5xx->chip_info;
int ret;
ret = regulator_bulk_enable(ARRAY_SIZE(yas5xx->regs), yas5xx->regs);
@@ -994,7 +1563,7 @@ static int yas5xx_runtime_resume(struct device *dev)
usleep_range(31000, 40000);
gpiod_set_value_cansleep(yas5xx->reset, 0);
- ret = yas5xx_power_on(yas5xx);
+ ret = ci->power_on(yas5xx);
if (ret) {
dev_err(dev, "cannot power on\n");
goto out_reset;
@@ -1013,17 +1582,19 @@ static DEFINE_RUNTIME_DEV_PM_OPS(yas5xx_dev_pm_ops, yas5xx_runtime_suspend,
yas5xx_runtime_resume, NULL);
static const struct i2c_device_id yas5xx_id[] = {
- {"yas530", },
- {"yas532", },
- {"yas533", },
+ {"yas530", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas530] },
+ {"yas532", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas532] },
+ {"yas533", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas533] },
+ {"yas537", (kernel_ulong_t)&yas5xx_chip_info_tbl[yas537] },
{}
};
MODULE_DEVICE_TABLE(i2c, yas5xx_id);
static const struct of_device_id yas5xx_of_match[] = {
- { .compatible = "yamaha,yas530", },
- { .compatible = "yamaha,yas532", },
- { .compatible = "yamaha,yas533", },
+ { .compatible = "yamaha,yas530", &yas5xx_chip_info_tbl[yas530] },
+ { .compatible = "yamaha,yas532", &yas5xx_chip_info_tbl[yas532] },
+ { .compatible = "yamaha,yas533", &yas5xx_chip_info_tbl[yas533] },
+ { .compatible = "yamaha,yas537", &yas5xx_chip_info_tbl[yas537] },
{}
};
MODULE_DEVICE_TABLE(of, yas5xx_of_match);
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index 0ff756cea63a..c9453389e4f7 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -17,14 +17,14 @@ config ABP060MG
will be called abp060mg.
config BMP280
- tristate "Bosch Sensortec BMP180/BMP280 pressure sensor I2C driver"
+ tristate "Bosch Sensortec BMP180/BMP280/BMP380 pressure sensor I2C driver"
depends on (I2C || SPI_MASTER)
select REGMAP
select BMP280_I2C if (I2C)
select BMP280_SPI if (SPI_MASTER)
help
- Say yes here to build support for Bosch Sensortec BMP180 and BMP280
- pressure and temperature sensors. Also supports the BME280 with
+ Say yes here to build support for Bosch Sensortec BMP180, BMP280 and
+ BMP380 pressure and temperature sensors. Also supports the BME280 with
an additional humidity sensor channel.
To compile this driver as a module, choose M here: the core module
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index fe7aa81e7cc9..c0aff78489b4 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -9,13 +9,22 @@
* Driver for Bosch Sensortec BMP180 and BMP280 digital pressure sensor.
*
* Datasheet:
- * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP180-DS000-121.pdf
- * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP280-DS001-12.pdf
- * https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-11.pdf
+ * https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
+ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf
+ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf
+ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf
+ *
+ * Notice:
+ * The link to the bmp180 datasheet points to an outdated version missing these changes:
+ * - Changed document referral from ANP015 to BST-MPS-AN004-00 on page 26
+ * - Updated equation for B3 param on section 3.5 to ((((long)AC1 * 4 + X3) << oss) + 2) / 4
+ * - Updated RoHS directive to 2011/65/EU effective 8 June 2011 on page 26
*/
#define pr_fmt(fmt) "bmp280: " fmt
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
#include <linux/device.h>
#include <linux/module.h>
#include <linux/regmap.h>
@@ -30,6 +39,8 @@
#include <linux/pm_runtime.h>
#include <linux/random.h>
+#include <asm/unaligned.h>
+
#include "bmp280.h"
/*
@@ -74,12 +85,51 @@ struct bmp280_calib {
s8 H6;
};
+/* See datasheet Section 3.11.1. */
+struct bmp380_calib {
+ u16 T1;
+ u16 T2;
+ s8 T3;
+ s16 P1;
+ s16 P2;
+ s8 P3;
+ s8 P4;
+ u16 P5;
+ u16 P6;
+ s8 P7;
+ s8 P8;
+ s16 P9;
+ s8 P10;
+ s8 P11;
+};
+
static const char *const bmp280_supply_names[] = {
"vddd", "vdda"
};
#define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names)
+enum bmp380_odr {
+ BMP380_ODR_200HZ,
+ BMP380_ODR_100HZ,
+ BMP380_ODR_50HZ,
+ BMP380_ODR_25HZ,
+ BMP380_ODR_12_5HZ,
+ BMP380_ODR_6_25HZ,
+ BMP380_ODR_3_125HZ,
+ BMP380_ODR_1_5625HZ,
+ BMP380_ODR_0_78HZ,
+ BMP380_ODR_0_39HZ,
+ BMP380_ODR_0_2HZ,
+ BMP380_ODR_0_1HZ,
+ BMP380_ODR_0_05HZ,
+ BMP380_ODR_0_02HZ,
+ BMP380_ODR_0_01HZ,
+ BMP380_ODR_0_006HZ,
+ BMP380_ODR_0_003HZ,
+ BMP380_ODR_0_0015HZ,
+};
+
struct bmp280_data {
struct device *dev;
struct mutex lock;
@@ -90,6 +140,7 @@ struct bmp280_data {
union {
struct bmp180_calib bmp180;
struct bmp280_calib bmp280;
+ struct bmp380_calib bmp380;
} calib;
struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES];
unsigned int start_up_time; /* in microseconds */
@@ -98,36 +149,99 @@ struct bmp280_data {
u8 oversampling_press;
u8 oversampling_temp;
u8 oversampling_humid;
+ u8 iir_filter_coeff;
+
+ /*
+ * BMP380 devices introduce sampling frequency configuration. See
+ * datasheet sections 3.3.3. and 4.3.19 for more details.
+ *
+ * BMx280 devices allowed indirect configuration of sampling frequency
+ * changing the t_standby duration between measurements, as detailed on
+ * section 3.6.3 of the datasheet.
+ */
+ int sampling_freq;
/*
* Carryover value from temperature conversion, used in pressure
* calculation.
*/
s32 t_fine;
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ /* Sensor data buffer */
+ u8 buf[3];
+ /* Calibration data buffers */
+ __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / 2];
+ __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / 2];
+ u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT];
+ /* Miscellaneous, endianess-aware data buffers */
+ __le16 le16;
+ __be16 be16;
+ } __aligned(IIO_DMA_MINALIGN);
};
struct bmp280_chip_info {
+ unsigned int id_reg;
+
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ unsigned int start_up_time;
+
const int *oversampling_temp_avail;
int num_oversampling_temp_avail;
+ int oversampling_temp_default;
const int *oversampling_press_avail;
int num_oversampling_press_avail;
+ int oversampling_press_default;
const int *oversampling_humid_avail;
int num_oversampling_humid_avail;
+ int oversampling_humid_default;
+
+ const int *iir_filter_coeffs_avail;
+ int num_iir_filter_coeffs_avail;
+ int iir_filter_coeff_default;
+
+ const int (*sampling_freq_avail)[2];
+ int num_sampling_freq_avail;
+ int sampling_freq_default;
int (*chip_config)(struct bmp280_data *);
int (*read_temp)(struct bmp280_data *, int *);
int (*read_press)(struct bmp280_data *, int *, int *);
int (*read_humid)(struct bmp280_data *, int *, int *);
+ int (*read_calib)(struct bmp280_data *);
};
/*
* These enums are used for indexing into the array of compensation
* parameters for BMP280.
*/
-enum { T1, T2, T3 };
-enum { P1, P2, P3, P4, P5, P6, P7, P8, P9 };
+enum { T1, T2, T3, P1, P2, P3, P4, P5, P6, P7, P8, P9 };
+
+enum {
+ /* Temperature calib indexes */
+ BMP380_T1 = 0,
+ BMP380_T2 = 2,
+ BMP380_T3 = 4,
+ /* Pressure calib indexes */
+ BMP380_P1 = 5,
+ BMP380_P2 = 7,
+ BMP380_P3 = 9,
+ BMP380_P4 = 10,
+ BMP380_P5 = 11,
+ BMP380_P6 = 13,
+ BMP380_P7 = 15,
+ BMP380_P8 = 16,
+ BMP380_P9 = 17,
+ BMP380_P10 = 19,
+ BMP380_P11 = 20,
+};
static const struct iio_chan_spec bmp280_channels[] = {
{
@@ -147,56 +261,81 @@ static const struct iio_chan_spec bmp280_channels[] = {
},
};
-static int bmp280_read_calib(struct bmp280_data *data,
- struct bmp280_calib *calib,
- unsigned int chip)
+static const struct iio_chan_spec bmp380_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ },
+ {
+ .type = IIO_HUMIDITYRELATIVE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ },
+};
+
+static int bmp280_read_calib(struct bmp280_data *data)
{
+ struct bmp280_calib *calib = &data->calib.bmp280;
int ret;
- unsigned int tmp;
- __le16 l16;
- __be16 b16;
- struct device *dev = data->dev;
- __le16 t_buf[BMP280_COMP_TEMP_REG_COUNT / 2];
- __le16 p_buf[BMP280_COMP_PRESS_REG_COUNT / 2];
- /* Read temperature calibration values. */
+
+ /* Read temperature and pressure calibration values. */
ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START,
- t_buf, BMP280_COMP_TEMP_REG_COUNT);
+ data->bmp280_cal_buf, sizeof(data->bmp280_cal_buf));
if (ret < 0) {
dev_err(data->dev,
- "failed to read temperature calibration parameters\n");
+ "failed to read temperature and pressure calibration parameters\n");
return ret;
}
- /* Toss the temperature calibration data into the entropy pool */
- add_device_randomness(t_buf, sizeof(t_buf));
+ /* Toss the temperature and pressure calibration data into the entropy pool */
+ add_device_randomness(data->bmp280_cal_buf, sizeof(data->bmp280_cal_buf));
+
+ /* Parse temperature calibration values. */
+ calib->T1 = le16_to_cpu(data->bmp280_cal_buf[T1]);
+ calib->T2 = le16_to_cpu(data->bmp280_cal_buf[T2]);
+ calib->T3 = le16_to_cpu(data->bmp280_cal_buf[T3]);
+
+ /* Parse pressure calibration values. */
+ calib->P1 = le16_to_cpu(data->bmp280_cal_buf[P1]);
+ calib->P2 = le16_to_cpu(data->bmp280_cal_buf[P2]);
+ calib->P3 = le16_to_cpu(data->bmp280_cal_buf[P3]);
+ calib->P4 = le16_to_cpu(data->bmp280_cal_buf[P4]);
+ calib->P5 = le16_to_cpu(data->bmp280_cal_buf[P5]);
+ calib->P6 = le16_to_cpu(data->bmp280_cal_buf[P6]);
+ calib->P7 = le16_to_cpu(data->bmp280_cal_buf[P7]);
+ calib->P8 = le16_to_cpu(data->bmp280_cal_buf[P8]);
+ calib->P9 = le16_to_cpu(data->bmp280_cal_buf[P9]);
- calib->T1 = le16_to_cpu(t_buf[T1]);
- calib->T2 = le16_to_cpu(t_buf[T2]);
- calib->T3 = le16_to_cpu(t_buf[T3]);
+ return 0;
+}
- /* Read pressure calibration values. */
- ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_PRESS_START,
- p_buf, BMP280_COMP_PRESS_REG_COUNT);
- if (ret < 0) {
- dev_err(data->dev,
- "failed to read pressure calibration parameters\n");
+static int bme280_read_calib(struct bmp280_data *data)
+{
+ struct bmp280_calib *calib = &data->calib.bmp280;
+ struct device *dev = data->dev;
+ unsigned int tmp;
+ int ret;
+
+ /* Load shared calibration params with bmp280 first */
+ ret = bmp280_read_calib(data);
+ if (ret < 0) {
+ dev_err(dev, "failed to read common bmp280 calibration parameters\n");
return ret;
}
- /* Toss the pressure calibration data into the entropy pool */
- add_device_randomness(p_buf, sizeof(p_buf));
-
- calib->P1 = le16_to_cpu(p_buf[P1]);
- calib->P2 = le16_to_cpu(p_buf[P2]);
- calib->P3 = le16_to_cpu(p_buf[P3]);
- calib->P4 = le16_to_cpu(p_buf[P4]);
- calib->P5 = le16_to_cpu(p_buf[P5]);
- calib->P6 = le16_to_cpu(p_buf[P6]);
- calib->P7 = le16_to_cpu(p_buf[P7]);
- calib->P8 = le16_to_cpu(p_buf[P8]);
- calib->P9 = le16_to_cpu(p_buf[P9]);
-
/*
* Read humidity calibration values.
* Due to some odd register addressing we cannot just
@@ -204,8 +343,6 @@ static int bmp280_read_calib(struct bmp280_data *data,
* value separately and sometimes do some bit shifting...
* Humidity data is only available on BME280.
*/
- if (chip != BME280_CHIP_ID)
- return 0;
ret = regmap_read(data->regmap, BMP280_REG_COMP_H1, &tmp);
if (ret < 0) {
@@ -214,12 +351,13 @@ static int bmp280_read_calib(struct bmp280_data *data,
}
calib->H1 = tmp;
- ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2, &l16, 2);
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H2,
+ &data->le16, sizeof(data->le16));
if (ret < 0) {
dev_err(dev, "failed to read H2 comp value\n");
return ret;
}
- calib->H2 = sign_extend32(le16_to_cpu(l16), 15);
+ calib->H2 = sign_extend32(le16_to_cpu(data->le16), 15);
ret = regmap_read(data->regmap, BMP280_REG_COMP_H3, &tmp);
if (ret < 0) {
@@ -228,20 +366,22 @@ static int bmp280_read_calib(struct bmp280_data *data,
}
calib->H3 = tmp;
- ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4, &b16, 2);
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H4,
+ &data->be16, sizeof(data->be16));
if (ret < 0) {
dev_err(dev, "failed to read H4 comp value\n");
return ret;
}
- calib->H4 = sign_extend32(((be16_to_cpu(b16) >> 4) & 0xff0) |
- (be16_to_cpu(b16) & 0xf), 11);
+ calib->H4 = sign_extend32(((be16_to_cpu(data->be16) >> 4) & 0xff0) |
+ (be16_to_cpu(data->be16) & 0xf), 11);
- ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5, &l16, 2);
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_H5,
+ &data->le16, sizeof(data->le16));
if (ret < 0) {
dev_err(dev, "failed to read H5 comp value\n");
return ret;
}
- calib->H5 = sign_extend32(((le16_to_cpu(l16) >> 4) & 0xfff), 11);
+ calib->H5 = sign_extend32(FIELD_GET(BMP280_COMP_H5_MASK, le16_to_cpu(data->le16)), 11);
ret = regmap_read(data->regmap, BMP280_REG_COMP_H6, &tmp);
if (ret < 0) {
@@ -261,8 +401,8 @@ static int bmp280_read_calib(struct bmp280_data *data,
static u32 bmp280_compensate_humidity(struct bmp280_data *data,
s32 adc_humidity)
{
- s32 var;
struct bmp280_calib *calib = &data->calib.bmp280;
+ s32 var;
var = ((s32)data->t_fine) - (s32)76800;
var = ((((adc_humidity << 14) - (calib->H4 << 20) - (calib->H5 * var))
@@ -286,8 +426,8 @@ static u32 bmp280_compensate_humidity(struct bmp280_data *data,
static s32 bmp280_compensate_temp(struct bmp280_data *data,
s32 adc_temp)
{
- s32 var1, var2;
struct bmp280_calib *calib = &data->calib.bmp280;
+ s32 var1, var2;
var1 = (((adc_temp >> 3) - ((s32)calib->T1 << 1)) *
((s32)calib->T2)) >> 11;
@@ -309,8 +449,8 @@ static s32 bmp280_compensate_temp(struct bmp280_data *data,
static u32 bmp280_compensate_press(struct bmp280_data *data,
s32 adc_press)
{
- s64 var1, var2, p;
struct bmp280_calib *calib = &data->calib.bmp280;
+ s64 var1, var2, p;
var1 = ((s64)data->t_fine) - 128000;
var2 = var1 * var1 * (s64)calib->P6;
@@ -335,17 +475,17 @@ static u32 bmp280_compensate_press(struct bmp280_data *data,
static int bmp280_read_temp(struct bmp280_data *data,
int *val)
{
- int ret;
- __be32 tmp = 0;
s32 adc_temp, comp_temp;
+ int ret;
- ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB, &tmp, 3);
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB,
+ data->buf, sizeof(data->buf));
if (ret < 0) {
dev_err(data->dev, "failed to read temperature\n");
return ret;
}
- adc_temp = be32_to_cpu(tmp) >> 12;
+ adc_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(data->buf));
if (adc_temp == BMP280_TEMP_SKIPPED) {
/* reading was skipped */
dev_err(data->dev, "reading temperature skipped\n");
@@ -368,23 +508,23 @@ static int bmp280_read_temp(struct bmp280_data *data,
static int bmp280_read_press(struct bmp280_data *data,
int *val, int *val2)
{
- int ret;
- __be32 tmp = 0;
- s32 adc_press;
u32 comp_press;
+ s32 adc_press;
+ int ret;
/* Read and compensate temperature so we get a reading of t_fine. */
ret = bmp280_read_temp(data, NULL);
if (ret < 0)
return ret;
- ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB, &tmp, 3);
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB,
+ data->buf, sizeof(data->buf));
if (ret < 0) {
dev_err(data->dev, "failed to read pressure\n");
return ret;
}
- adc_press = be32_to_cpu(tmp) >> 12;
+ adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(data->buf));
if (adc_press == BMP280_PRESS_SKIPPED) {
/* reading was skipped */
dev_err(data->dev, "reading pressure skipped\n");
@@ -400,23 +540,23 @@ static int bmp280_read_press(struct bmp280_data *data,
static int bmp280_read_humid(struct bmp280_data *data, int *val, int *val2)
{
- __be16 tmp;
- int ret;
- s32 adc_humidity;
u32 comp_humidity;
+ s32 adc_humidity;
+ int ret;
/* Read and compensate temperature so we get a reading of t_fine. */
ret = bmp280_read_temp(data, NULL);
if (ret < 0)
return ret;
- ret = regmap_bulk_read(data->regmap, BMP280_REG_HUMIDITY_MSB, &tmp, 2);
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_HUMIDITY_MSB,
+ &data->be16, sizeof(data->be16));
if (ret < 0) {
dev_err(data->dev, "failed to read humidity\n");
return ret;
}
- adc_humidity = be16_to_cpu(tmp);
+ adc_humidity = be16_to_cpu(data->be16);
if (adc_humidity == BMP280_HUMIDITY_SKIPPED) {
/* reading was skipped */
dev_err(data->dev, "reading humidity skipped\n");
@@ -433,8 +573,8 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
- int ret;
struct bmp280_data *data = iio_priv(indio_dev);
+ int ret;
pm_runtime_get_sync(data->dev);
mutex_lock(&data->lock);
@@ -475,6 +615,25 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
break;
}
break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (!data->chip_info->sampling_freq_avail) {
+ ret = -EINVAL;
+ break;
+ }
+
+ *val = data->chip_info->sampling_freq_avail[data->sampling_freq][0];
+ *val2 = data->chip_info->sampling_freq_avail[data->sampling_freq][1];
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ if (!data->chip_info->iir_filter_coeffs_avail) {
+ ret = -EINVAL;
+ break;
+ }
+
+ *val = (1 << data->iir_filter_coeff) - 1;
+ ret = IIO_VAL_INT;
+ break;
default:
ret = -EINVAL;
break;
@@ -490,15 +649,23 @@ static int bmp280_read_raw(struct iio_dev *indio_dev,
static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data,
int val)
{
- int i;
const int *avail = data->chip_info->oversampling_humid_avail;
const int n = data->chip_info->num_oversampling_humid_avail;
+ int ret, prev;
+ int i;
for (i = 0; i < n; i++) {
if (avail[i] == val) {
+ prev = data->oversampling_humid;
data->oversampling_humid = ilog2(val);
- return data->chip_info->chip_config(data);
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->oversampling_humid = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+ }
+ return 0;
}
}
return -EINVAL;
@@ -507,15 +674,23 @@ static int bmp280_write_oversampling_ratio_humid(struct bmp280_data *data,
static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data,
int val)
{
- int i;
const int *avail = data->chip_info->oversampling_temp_avail;
const int n = data->chip_info->num_oversampling_temp_avail;
+ int ret, prev;
+ int i;
for (i = 0; i < n; i++) {
if (avail[i] == val) {
+ prev = data->oversampling_temp;
data->oversampling_temp = ilog2(val);
- return data->chip_info->chip_config(data);
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->oversampling_temp = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+ }
+ return 0;
}
}
return -EINVAL;
@@ -524,15 +699,73 @@ static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data,
static int bmp280_write_oversampling_ratio_press(struct bmp280_data *data,
int val)
{
- int i;
const int *avail = data->chip_info->oversampling_press_avail;
const int n = data->chip_info->num_oversampling_press_avail;
+ int ret, prev;
+ int i;
for (i = 0; i < n; i++) {
if (avail[i] == val) {
+ prev = data->oversampling_press;
data->oversampling_press = ilog2(val);
- return data->chip_info->chip_config(data);
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->oversampling_press = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int bmp280_write_sampling_frequency(struct bmp280_data *data,
+ int val, int val2)
+{
+ const int (*avail)[2] = data->chip_info->sampling_freq_avail;
+ const int n = data->chip_info->num_sampling_freq_avail;
+ int ret, prev;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (avail[i][0] == val && avail[i][1] == val2) {
+ prev = data->sampling_freq;
+ data->sampling_freq = i;
+
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->sampling_freq = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int bmp280_write_iir_filter_coeffs(struct bmp280_data *data, int val)
+{
+ const int *avail = data->chip_info->iir_filter_coeffs_avail;
+ const int n = data->chip_info->num_iir_filter_coeffs_avail;
+ int ret, prev;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (avail[i] - 1 == val) {
+ prev = data->iir_filter_coeff;
+ data->iir_filter_coeff = i;
+
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->iir_filter_coeff = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+
+ }
+ return 0;
}
}
return -EINVAL;
@@ -542,9 +775,15 @@ static int bmp280_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
- int ret = 0;
struct bmp280_data *data = iio_priv(indio_dev);
+ int ret = 0;
+ /*
+ * Helper functions to update sensor running configuration.
+ * If an error happens applying new settings, will try restore
+ * previous parameters to ensure the sensor is left in a known
+ * working configuration.
+ */
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
pm_runtime_get_sync(data->dev);
@@ -567,6 +806,22 @@ static int bmp280_write_raw(struct iio_dev *indio_dev,
pm_runtime_mark_last_busy(data->dev);
pm_runtime_put_autosuspend(data->dev);
break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ pm_runtime_get_sync(data->dev);
+ mutex_lock(&data->lock);
+ ret = bmp280_write_sampling_frequency(data, val, val2);
+ mutex_unlock(&data->lock);
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+ break;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ pm_runtime_get_sync(data->dev);
+ mutex_lock(&data->lock);
+ ret = bmp280_write_iir_filter_coeffs(data, val);
+ mutex_unlock(&data->lock);
+ pm_runtime_mark_last_busy(data->dev);
+ pm_runtime_put_autosuspend(data->dev);
+ break;
default:
return -EINVAL;
}
@@ -597,6 +852,17 @@ static int bmp280_read_avail(struct iio_dev *indio_dev,
}
*type = IIO_VAL_INT;
return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)data->chip_info->sampling_freq_avail;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ /* Values are stored in a 2D matrix */
+ *length = data->chip_info->num_sampling_freq_avail;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ *vals = data->chip_info->iir_filter_coeffs_avail;
+ *type = IIO_VAL_INT;
+ *length = data->chip_info->num_iir_filter_coeffs_avail;
+ return IIO_AVAIL_LIST;
default:
return -EINVAL;
}
@@ -610,9 +876,9 @@ static const struct iio_info bmp280_info = {
static int bmp280_chip_config(struct bmp280_data *data)
{
+ u8 osrs = FIELD_PREP(BMP280_OSRS_TEMP_MASK, data->oversampling_temp + 1) |
+ FIELD_PREP(BMP280_OSRS_PRESS_MASK, data->oversampling_press + 1);
int ret;
- u8 osrs = BMP280_OSRS_TEMP_X(data->oversampling_temp + 1) |
- BMP280_OSRS_PRESS_X(data->oversampling_press + 1);
ret = regmap_write_bits(data->regmap, BMP280_REG_CTRL_MEAS,
BMP280_OSRS_TEMP_MASK |
@@ -640,21 +906,39 @@ static int bmp280_chip_config(struct bmp280_data *data)
static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 };
static const struct bmp280_chip_info bmp280_chip_info = {
+ .id_reg = BMP280_REG_ID,
+ .start_up_time = 2000,
+ .channels = bmp280_channels,
+ .num_channels = 2,
+
.oversampling_temp_avail = bmp280_oversampling_avail,
.num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ /*
+ * Oversampling config values on BMx280 have one additional setting
+ * that other generations of the family don't:
+ * The value 0 means the measurement is bypassed instead of
+ * oversampling set to x1.
+ *
+ * To account for this difference, and preserve the same common
+ * config logic, this is handled later on chip_config callback
+ * incrementing one unit the oversampling setting.
+ */
+ .oversampling_temp_default = BMP280_OSRS_TEMP_2X - 1,
.oversampling_press_avail = bmp280_oversampling_avail,
.num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ .oversampling_press_default = BMP280_OSRS_PRESS_16X - 1,
.chip_config = bmp280_chip_config,
.read_temp = bmp280_read_temp,
.read_press = bmp280_read_press,
+ .read_calib = bmp280_read_calib,
};
static int bme280_chip_config(struct bmp280_data *data)
{
+ u8 osrs = FIELD_PREP(BMP280_OSRS_HUMIDITY_MASK, data->oversampling_humid + 1);
int ret;
- u8 osrs = BMP280_OSRS_HUMIDITIY_X(data->oversampling_humid + 1);
/*
* Oversampling of humidity must be set before oversampling of
@@ -670,27 +954,405 @@ static int bme280_chip_config(struct bmp280_data *data)
}
static const struct bmp280_chip_info bme280_chip_info = {
+ .id_reg = BMP280_REG_ID,
+ .start_up_time = 2000,
+ .channels = bmp280_channels,
+ .num_channels = 3,
+
.oversampling_temp_avail = bmp280_oversampling_avail,
.num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ .oversampling_temp_default = BMP280_OSRS_TEMP_2X - 1,
.oversampling_press_avail = bmp280_oversampling_avail,
.num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ .oversampling_press_default = BMP280_OSRS_PRESS_16X - 1,
.oversampling_humid_avail = bmp280_oversampling_avail,
.num_oversampling_humid_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ .oversampling_humid_default = BMP280_OSRS_HUMIDITY_16X - 1,
.chip_config = bme280_chip_config,
.read_temp = bmp280_read_temp,
.read_press = bmp280_read_press,
.read_humid = bmp280_read_humid,
+ .read_calib = bme280_read_calib,
};
-static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas)
+/*
+ * Helper function to send a command to BMP3XX sensors.
+ *
+ * Sensor processes commands written to the CMD register and signals
+ * execution result through "cmd_rdy" and "cmd_error" flags available on
+ * STATUS and ERROR registers.
+ */
+static int bmp380_cmd(struct bmp280_data *data, u8 cmd)
+{
+ unsigned int reg;
+ int ret;
+
+ /* Check if device is ready to process a command */
+ ret = regmap_read(data->regmap, BMP380_REG_STATUS, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to read error register\n");
+ return ret;
+ }
+ if (!(reg & BMP380_STATUS_CMD_RDY_MASK)) {
+ dev_err(data->dev, "device is not ready to accept commands\n");
+ return -EBUSY;
+ }
+
+ /* Send command to process */
+ ret = regmap_write(data->regmap, BMP380_REG_CMD, cmd);
+ if (ret) {
+ dev_err(data->dev, "failed to send command to device\n");
+ return ret;
+ }
+ /* Wait for 2ms for command to be processed */
+ usleep_range(data->start_up_time, data->start_up_time + 100);
+ /* Check for command processing error */
+ ret = regmap_read(data->regmap, BMP380_REG_ERROR, &reg);
+ if (ret) {
+ dev_err(data->dev, "error reading ERROR reg\n");
+ return ret;
+ }
+ if (reg & BMP380_ERR_CMD_MASK) {
+ dev_err(data->dev, "error processing command 0x%X\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Returns temperature in Celsius dregrees, resolution is 0.01º C. Output value of
+ * "5123" equals 51.2º C. t_fine carries fine temperature as global value.
+ *
+ * Taken from datasheet, Section Appendix 9, "Compensation formula" and repo
+ * https://github.com/BoschSensortec/BMP3-Sensor-API.
+ */
+static s32 bmp380_compensate_temp(struct bmp280_data *data, u32 adc_temp)
+{
+ s64 var1, var2, var3, var4, var5, var6, comp_temp;
+ struct bmp380_calib *calib = &data->calib.bmp380;
+
+ var1 = ((s64) adc_temp) - (((s64) calib->T1) << 8);
+ var2 = var1 * ((s64) calib->T2);
+ var3 = var1 * var1;
+ var4 = var3 * ((s64) calib->T3);
+ var5 = (var2 << 18) + var4;
+ var6 = var5 >> 32;
+ data->t_fine = (s32) var6;
+ comp_temp = (var6 * 25) >> 14;
+
+ comp_temp = clamp_val(comp_temp, BMP380_MIN_TEMP, BMP380_MAX_TEMP);
+ return (s32) comp_temp;
+}
+
+/*
+ * Returns pressure in Pa as an unsigned 32 bit integer in fractional Pascal.
+ * Output value of "9528709" represents 9528709/100 = 95287.09 Pa = 952.8709 hPa.
+ *
+ * Taken from datasheet, Section 9.3. "Pressure compensation" and repository
+ * https://github.com/BoschSensortec/BMP3-Sensor-API.
+ */
+static u32 bmp380_compensate_press(struct bmp280_data *data, u32 adc_press)
+{
+ s64 var1, var2, var3, var4, var5, var6, offset, sensitivity;
+ struct bmp380_calib *calib = &data->calib.bmp380;
+ u32 comp_press;
+
+ var1 = (s64)data->t_fine * (s64)data->t_fine;
+ var2 = var1 >> 6;
+ var3 = (var2 * ((s64) data->t_fine)) >> 8;
+ var4 = ((s64)calib->P8 * var3) >> 5;
+ var5 = ((s64)calib->P7 * var1) << 4;
+ var6 = ((s64)calib->P6 * (s64)data->t_fine) << 22;
+ offset = ((s64)calib->P5 << 47) + var4 + var5 + var6;
+ var2 = ((s64)calib->P4 * var3) >> 5;
+ var4 = ((s64)calib->P3 * var1) << 2;
+ var5 = ((s64)calib->P2 - ((s64)1 << 14)) *
+ ((s64)data->t_fine << 21);
+ sensitivity = (((s64) calib->P1 - ((s64) 1 << 14)) << 46) +
+ var2 + var4 + var5;
+ var1 = (sensitivity >> 24) * (s64)adc_press;
+ var2 = (s64)calib->P10 * (s64)data->t_fine;
+ var3 = var2 + ((s64)calib->P9 << 16);
+ var4 = (var3 * (s64)adc_press) >> 13;
+
+ /*
+ * Dividing by 10 followed by multiplying by 10 to avoid
+ * possible overflow caused by (uncomp_data->pressure * partial_data4).
+ */
+ var5 = ((s64)adc_press * div_s64(var4, 10)) >> 9;
+ var5 *= 10;
+ var6 = (s64)adc_press * (s64)adc_press;
+ var2 = ((s64)calib->P11 * var6) >> 16;
+ var3 = (var2 * (s64)adc_press) >> 7;
+ var4 = (offset >> 2) + var1 + var5 + var3;
+ comp_press = ((u64)var4 * 25) >> 40;
+
+ comp_press = clamp_val(comp_press, BMP380_MIN_PRES, BMP380_MAX_PRES);
+ return comp_press;
+}
+
+static int bmp380_read_temp(struct bmp280_data *data, int *val)
+{
+ s32 comp_temp;
+ u32 adc_temp;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP380_REG_TEMP_XLSB,
+ data->buf, sizeof(data->buf));
+ if (ret) {
+ dev_err(data->dev, "failed to read temperature\n");
+ return ret;
+ }
+
+ adc_temp = get_unaligned_le24(data->buf);
+ if (adc_temp == BMP380_TEMP_SKIPPED) {
+ dev_err(data->dev, "reading temperature skipped\n");
+ return -EIO;
+ }
+ comp_temp = bmp380_compensate_temp(data, adc_temp);
+
+ /*
+ * Val might be NULL if we're called by the read_press routine,
+ * who only cares about the carry over t_fine value.
+ */
+ if (val) {
+ /* IIO reports temperatures in milli Celsius */
+ *val = comp_temp * 10;
+ return IIO_VAL_INT;
+ }
+
+ return 0;
+}
+
+static int bmp380_read_press(struct bmp280_data *data, int *val, int *val2)
+{
+ s32 comp_press;
+ u32 adc_press;
+ int ret;
+
+ /* Read and compensate for temperature so we get a reading of t_fine */
+ ret = bmp380_read_temp(data, NULL);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP380_REG_PRESS_XLSB,
+ data->buf, sizeof(data->buf));
+ if (ret) {
+ dev_err(data->dev, "failed to read pressure\n");
+ return ret;
+ }
+
+ adc_press = get_unaligned_le24(data->buf);
+ if (adc_press == BMP380_PRESS_SKIPPED) {
+ dev_err(data->dev, "reading pressure skipped\n");
+ return -EIO;
+ }
+ comp_press = bmp380_compensate_press(data, adc_press);
+
+ *val = comp_press;
+ /* Compensated pressure is in cPa (centipascals) */
+ *val2 = 100000;
+
+ return IIO_VAL_FRACTIONAL;
+}
+
+static int bmp380_read_calib(struct bmp280_data *data)
+{
+ struct bmp380_calib *calib = &data->calib.bmp380;
+ int ret;
+
+ /* Read temperature and pressure calibration data */
+ ret = regmap_bulk_read(data->regmap, BMP380_REG_CALIB_TEMP_START,
+ data->bmp380_cal_buf, sizeof(data->bmp380_cal_buf));
+ if (ret) {
+ dev_err(data->dev,
+ "failed to read temperature calibration parameters\n");
+ return ret;
+ }
+
+ /* Toss the temperature calibration data into the entropy pool */
+ add_device_randomness(data->bmp380_cal_buf, sizeof(data->bmp380_cal_buf));
+
+ /* Parse calibration values */
+ calib->T1 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_T1]);
+ calib->T2 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_T2]);
+ calib->T3 = data->bmp380_cal_buf[BMP380_T3];
+ calib->P1 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P1]);
+ calib->P2 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P2]);
+ calib->P3 = data->bmp380_cal_buf[BMP380_P3];
+ calib->P4 = data->bmp380_cal_buf[BMP380_P4];
+ calib->P5 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P5]);
+ calib->P6 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P6]);
+ calib->P7 = data->bmp380_cal_buf[BMP380_P7];
+ calib->P8 = data->bmp380_cal_buf[BMP380_P8];
+ calib->P9 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P9]);
+ calib->P10 = data->bmp380_cal_buf[BMP380_P10];
+ calib->P11 = data->bmp380_cal_buf[BMP380_P11];
+
+ return 0;
+}
+
+static const int bmp380_odr_table[][2] = {
+ [BMP380_ODR_200HZ] = {200, 0},
+ [BMP380_ODR_100HZ] = {100, 0},
+ [BMP380_ODR_50HZ] = {50, 0},
+ [BMP380_ODR_25HZ] = {25, 0},
+ [BMP380_ODR_12_5HZ] = {12, 500000},
+ [BMP380_ODR_6_25HZ] = {6, 250000},
+ [BMP380_ODR_3_125HZ] = {3, 125000},
+ [BMP380_ODR_1_5625HZ] = {1, 562500},
+ [BMP380_ODR_0_78HZ] = {0, 781250},
+ [BMP380_ODR_0_39HZ] = {0, 390625},
+ [BMP380_ODR_0_2HZ] = {0, 195313},
+ [BMP380_ODR_0_1HZ] = {0, 97656},
+ [BMP380_ODR_0_05HZ] = {0, 48828},
+ [BMP380_ODR_0_02HZ] = {0, 24414},
+ [BMP380_ODR_0_01HZ] = {0, 12207},
+ [BMP380_ODR_0_006HZ] = {0, 6104},
+ [BMP380_ODR_0_003HZ] = {0, 3052},
+ [BMP380_ODR_0_0015HZ] = {0, 1526},
+};
+
+static int bmp380_chip_config(struct bmp280_data *data)
{
+ bool change = false, aux;
+ unsigned int tmp;
+ u8 osrs;
int ret;
+
+ /* Configure power control register */
+ ret = regmap_update_bits(data->regmap, BMP380_REG_POWER_CONTROL,
+ BMP380_CTRL_SENSORS_MASK,
+ BMP380_CTRL_SENSORS_PRESS_EN |
+ BMP380_CTRL_SENSORS_TEMP_EN);
+ if (ret) {
+ dev_err(data->dev,
+ "failed to write operation control register\n");
+ return ret;
+ }
+
+ /* Configure oversampling */
+ osrs = FIELD_PREP(BMP380_OSRS_TEMP_MASK, data->oversampling_temp) |
+ FIELD_PREP(BMP380_OSRS_PRESS_MASK, data->oversampling_press);
+
+ ret = regmap_update_bits_check(data->regmap, BMP380_REG_OSR,
+ BMP380_OSRS_TEMP_MASK |
+ BMP380_OSRS_PRESS_MASK,
+ osrs, &aux);
+ if (ret) {
+ dev_err(data->dev, "failed to write oversampling register\n");
+ return ret;
+ }
+ change = change || aux;
+
+ /* Configure output data rate */
+ ret = regmap_update_bits_check(data->regmap, BMP380_REG_ODR,
+ BMP380_ODRS_MASK, data->sampling_freq, &aux);
+ if (ret) {
+ dev_err(data->dev, "failed to write ODR selection register\n");
+ return ret;
+ }
+ change = change || aux;
+
+ /* Set filter data */
+ ret = regmap_update_bits_check(data->regmap, BMP380_REG_CONFIG, BMP380_FILTER_MASK,
+ FIELD_PREP(BMP380_FILTER_MASK, data->iir_filter_coeff),
+ &aux);
+ if (ret) {
+ dev_err(data->dev, "failed to write config register\n");
+ return ret;
+ }
+ change = change || aux;
+
+ if (change) {
+ /*
+ * The configurations errors are detected on the fly during a measurement
+ * cycle. If the sampling frequency is too low, it's faster to reset
+ * the measurement loop than wait until the next measurement is due.
+ *
+ * Resets sensor measurement loop toggling between sleep and normal
+ * operating modes.
+ */
+ ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL,
+ BMP380_MODE_MASK,
+ FIELD_PREP(BMP380_MODE_MASK, BMP380_MODE_SLEEP));
+ if (ret) {
+ dev_err(data->dev, "failed to set sleep mode\n");
+ return ret;
+ }
+ usleep_range(2000, 2500);
+ ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL,
+ BMP380_MODE_MASK,
+ FIELD_PREP(BMP380_MODE_MASK, BMP380_MODE_NORMAL));
+ if (ret) {
+ dev_err(data->dev, "failed to set normal mode\n");
+ return ret;
+ }
+ /*
+ * Waits for measurement before checking configuration error flag.
+ * Selected longest measure time indicated in section 3.9.1
+ * in the datasheet.
+ */
+ msleep(80);
+
+ /* Check config error flag */
+ ret = regmap_read(data->regmap, BMP380_REG_ERROR, &tmp);
+ if (ret) {
+ dev_err(data->dev,
+ "failed to read error register\n");
+ return ret;
+ }
+ if (tmp & BMP380_ERR_CONF_MASK) {
+ dev_warn(data->dev,
+ "sensor flagged configuration as incompatible\n");
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 };
+static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128};
+
+static const struct bmp280_chip_info bmp380_chip_info = {
+ .id_reg = BMP380_REG_ID,
+ .start_up_time = 2000,
+ .channels = bmp380_channels,
+ .num_channels = 2,
+
+ .oversampling_temp_avail = bmp380_oversampling_avail,
+ .num_oversampling_temp_avail = ARRAY_SIZE(bmp380_oversampling_avail),
+ .oversampling_temp_default = ilog2(1),
+
+ .oversampling_press_avail = bmp380_oversampling_avail,
+ .num_oversampling_press_avail = ARRAY_SIZE(bmp380_oversampling_avail),
+ .oversampling_press_default = ilog2(4),
+
+ .sampling_freq_avail = bmp380_odr_table,
+ .num_sampling_freq_avail = ARRAY_SIZE(bmp380_odr_table) * 2,
+ .sampling_freq_default = BMP380_ODR_50HZ,
+
+ .iir_filter_coeffs_avail = bmp380_iir_filter_coeffs_avail,
+ .num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail),
+ .iir_filter_coeff_default = 2,
+
+ .chip_config = bmp380_chip_config,
+ .read_temp = bmp380_read_temp,
+ .read_press = bmp380_read_press,
+ .read_calib = bmp380_read_calib,
+};
+
+static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas)
+{
const int conversion_time_max[] = { 4500, 7500, 13500, 25500 };
unsigned int delay_us;
unsigned int ctrl;
+ int ret;
if (data->use_eoc)
reinit_completion(&data->done);
@@ -710,7 +1372,7 @@ static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas)
if (!ret)
dev_err(data->dev, "timeout waiting for completion\n");
} else {
- if (ctrl_meas == BMP180_MEAS_TEMP)
+ if (FIELD_GET(BMP180_MEAS_CTRL_MASK, ctrl_meas) == BMP180_MEAS_TEMP)
delay_us = 4500;
else
delay_us =
@@ -732,55 +1394,57 @@ static int bmp180_measure(struct bmp280_data *data, u8 ctrl_meas)
static int bmp180_read_adc_temp(struct bmp280_data *data, int *val)
{
- __be16 tmp;
int ret;
- ret = bmp180_measure(data, BMP180_MEAS_TEMP);
+ ret = bmp180_measure(data,
+ FIELD_PREP(BMP180_MEAS_CTRL_MASK, BMP180_MEAS_TEMP) |
+ BMP180_MEAS_SCO);
if (ret)
return ret;
- ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, &tmp, 2);
+ ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB,
+ &data->be16, sizeof(data->be16));
if (ret)
return ret;
- *val = be16_to_cpu(tmp);
+ *val = be16_to_cpu(data->be16);
return 0;
}
-static int bmp180_read_calib(struct bmp280_data *data,
- struct bmp180_calib *calib)
+static int bmp180_read_calib(struct bmp280_data *data)
{
+ struct bmp180_calib *calib = &data->calib.bmp180;
int ret;
int i;
- __be16 buf[BMP180_REG_CALIB_COUNT / 2];
- ret = regmap_bulk_read(data->regmap, BMP180_REG_CALIB_START, buf,
- sizeof(buf));
+ ret = regmap_bulk_read(data->regmap, BMP180_REG_CALIB_START,
+ data->bmp180_cal_buf, sizeof(data->bmp180_cal_buf));
if (ret < 0)
return ret;
/* None of the words has the value 0 or 0xFFFF */
- for (i = 0; i < ARRAY_SIZE(buf); i++) {
- if (buf[i] == cpu_to_be16(0) || buf[i] == cpu_to_be16(0xffff))
+ for (i = 0; i < ARRAY_SIZE(data->bmp180_cal_buf); i++) {
+ if (data->bmp180_cal_buf[i] == cpu_to_be16(0) ||
+ data->bmp180_cal_buf[i] == cpu_to_be16(0xffff))
return -EIO;
}
/* Toss the calibration data into the entropy pool */
- add_device_randomness(buf, sizeof(buf));
-
- calib->AC1 = be16_to_cpu(buf[AC1]);
- calib->AC2 = be16_to_cpu(buf[AC2]);
- calib->AC3 = be16_to_cpu(buf[AC3]);
- calib->AC4 = be16_to_cpu(buf[AC4]);
- calib->AC5 = be16_to_cpu(buf[AC5]);
- calib->AC6 = be16_to_cpu(buf[AC6]);
- calib->B1 = be16_to_cpu(buf[B1]);
- calib->B2 = be16_to_cpu(buf[B2]);
- calib->MB = be16_to_cpu(buf[MB]);
- calib->MC = be16_to_cpu(buf[MC]);
- calib->MD = be16_to_cpu(buf[MD]);
+ add_device_randomness(data->bmp180_cal_buf, sizeof(data->bmp180_cal_buf));
+
+ calib->AC1 = be16_to_cpu(data->bmp180_cal_buf[AC1]);
+ calib->AC2 = be16_to_cpu(data->bmp180_cal_buf[AC2]);
+ calib->AC3 = be16_to_cpu(data->bmp180_cal_buf[AC3]);
+ calib->AC4 = be16_to_cpu(data->bmp180_cal_buf[AC4]);
+ calib->AC5 = be16_to_cpu(data->bmp180_cal_buf[AC5]);
+ calib->AC6 = be16_to_cpu(data->bmp180_cal_buf[AC6]);
+ calib->B1 = be16_to_cpu(data->bmp180_cal_buf[B1]);
+ calib->B2 = be16_to_cpu(data->bmp180_cal_buf[B2]);
+ calib->MB = be16_to_cpu(data->bmp180_cal_buf[MB]);
+ calib->MC = be16_to_cpu(data->bmp180_cal_buf[MC]);
+ calib->MD = be16_to_cpu(data->bmp180_cal_buf[MD]);
return 0;
}
@@ -793,8 +1457,8 @@ static int bmp180_read_calib(struct bmp280_data *data,
*/
static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp)
{
- s32 x1, x2;
struct bmp180_calib *calib = &data->calib.bmp180;
+ s32 x1, x2;
x1 = ((adc_temp - calib->AC6) * calib->AC5) >> 15;
x2 = (calib->MC << 11) / (x1 + calib->MD);
@@ -805,8 +1469,8 @@ static s32 bmp180_compensate_temp(struct bmp280_data *data, s32 adc_temp)
static int bmp180_read_temp(struct bmp280_data *data, int *val)
{
- int ret;
s32 adc_temp, comp_temp;
+ int ret;
ret = bmp180_read_adc_temp(data, &adc_temp);
if (ret)
@@ -828,19 +1492,22 @@ static int bmp180_read_temp(struct bmp280_data *data, int *val)
static int bmp180_read_adc_press(struct bmp280_data *data, int *val)
{
- int ret;
- __be32 tmp = 0;
u8 oss = data->oversampling_press;
+ int ret;
- ret = bmp180_measure(data, BMP180_MEAS_PRESS_X(oss));
+ ret = bmp180_measure(data,
+ FIELD_PREP(BMP180_MEAS_CTRL_MASK, BMP180_MEAS_PRESS) |
+ FIELD_PREP(BMP180_OSRS_PRESS_MASK, oss) |
+ BMP180_MEAS_SCO);
if (ret)
return ret;
- ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB, &tmp, 3);
+ ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB,
+ data->buf, sizeof(data->buf));
if (ret)
return ret;
- *val = (be32_to_cpu(tmp) >> 8) >> (8 - oss);
+ *val = get_unaligned_be24(data->buf) >> (8 - oss);
return 0;
}
@@ -852,11 +1519,11 @@ static int bmp180_read_adc_press(struct bmp280_data *data, int *val)
*/
static u32 bmp180_compensate_press(struct bmp280_data *data, s32 adc_press)
{
+ struct bmp180_calib *calib = &data->calib.bmp180;
+ s32 oss = data->oversampling_press;
s32 x1, x2, x3, p;
s32 b3, b6;
u32 b4, b7;
- s32 oss = data->oversampling_press;
- struct bmp180_calib *calib = &data->calib.bmp180;
b6 = data->t_fine - 4000;
x1 = (calib->B2 * (b6 * b6 >> 12)) >> 11;
@@ -883,9 +1550,9 @@ static u32 bmp180_compensate_press(struct bmp280_data *data, s32 adc_press)
static int bmp180_read_press(struct bmp280_data *data,
int *val, int *val2)
{
- int ret;
- s32 adc_press;
u32 comp_press;
+ s32 adc_press;
+ int ret;
/* Read and compensate temperature so we get a reading of t_fine. */
ret = bmp180_read_temp(data, NULL);
@@ -913,17 +1580,25 @@ static const int bmp180_oversampling_temp_avail[] = { 1 };
static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 };
static const struct bmp280_chip_info bmp180_chip_info = {
+ .id_reg = BMP280_REG_ID,
+ .start_up_time = 2000,
+ .channels = bmp280_channels,
+ .num_channels = 2,
+
.oversampling_temp_avail = bmp180_oversampling_temp_avail,
.num_oversampling_temp_avail =
ARRAY_SIZE(bmp180_oversampling_temp_avail),
+ .oversampling_temp_default = 0,
.oversampling_press_avail = bmp180_oversampling_press_avail,
.num_oversampling_press_avail =
ARRAY_SIZE(bmp180_oversampling_press_avail),
+ .oversampling_press_default = BMP180_MEAS_PRESS_8X,
.chip_config = bmp180_chip_config,
.read_temp = bmp180_read_temp,
.read_press = bmp180_read_press,
+ .read_calib = bmp180_read_calib,
};
static irqreturn_t bmp085_eoc_irq(int irq, void *d)
@@ -990,11 +1665,12 @@ int bmp280_common_probe(struct device *dev,
const char *name,
int irq)
{
- int ret;
+ const struct bmp280_chip_info *chip_info;
struct iio_dev *indio_dev;
struct bmp280_data *data;
- unsigned int chip_id;
struct gpio_desc *gpiod;
+ unsigned int chip_id;
+ int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
@@ -1005,36 +1681,36 @@ int bmp280_common_probe(struct device *dev,
data->dev = dev;
indio_dev->name = name;
- indio_dev->channels = bmp280_channels;
indio_dev->info = &bmp280_info;
indio_dev->modes = INDIO_DIRECT_MODE;
switch (chip) {
case BMP180_CHIP_ID:
- indio_dev->num_channels = 2;
- data->chip_info = &bmp180_chip_info;
- data->oversampling_press = ilog2(8);
- data->oversampling_temp = ilog2(1);
- data->start_up_time = 10000;
+ chip_info = &bmp180_chip_info;
break;
case BMP280_CHIP_ID:
- indio_dev->num_channels = 2;
- data->chip_info = &bmp280_chip_info;
- data->oversampling_press = ilog2(16);
- data->oversampling_temp = ilog2(2);
- data->start_up_time = 2000;
+ chip_info = &bmp280_chip_info;
break;
case BME280_CHIP_ID:
- indio_dev->num_channels = 3;
- data->chip_info = &bme280_chip_info;
- data->oversampling_press = ilog2(16);
- data->oversampling_humid = ilog2(16);
- data->oversampling_temp = ilog2(2);
- data->start_up_time = 2000;
+ chip_info = &bme280_chip_info;
+ break;
+ case BMP380_CHIP_ID:
+ chip_info = &bmp380_chip_info;
break;
default:
return -EINVAL;
}
+ data->chip_info = chip_info;
+
+ /* Apply initial values from chip info structure */
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = chip_info->num_channels;
+ data->oversampling_press = chip_info->oversampling_press_default;
+ data->oversampling_humid = chip_info->oversampling_humid_default;
+ data->oversampling_temp = chip_info->oversampling_temp_default;
+ data->iir_filter_coeff = chip_info->iir_filter_coeff_default;
+ data->sampling_freq = chip_info->sampling_freq_default;
+ data->start_up_time = chip_info->start_up_time;
/* Bring up regulators */
regulator_bulk_set_supply_names(data->supplies,
@@ -1071,7 +1747,8 @@ int bmp280_common_probe(struct device *dev,
}
data->regmap = regmap;
- ret = regmap_read(regmap, BMP280_REG_ID, &chip_id);
+
+ ret = regmap_read(regmap, data->chip_info->id_reg, &chip_id);
if (ret < 0)
return ret;
if (chip_id != chip) {
@@ -1080,6 +1757,13 @@ int bmp280_common_probe(struct device *dev,
return -EINVAL;
}
+ /* BMP3xx requires soft-reset as part of initialization */
+ if (chip_id == BMP380_CHIP_ID) {
+ ret = bmp380_cmd(data, BMP380_CMD_SOFT_RESET);
+ if (ret < 0)
+ return ret;
+ }
+
ret = data->chip_info->chip_config(data);
if (ret < 0)
return ret;
@@ -1091,21 +1775,11 @@ int bmp280_common_probe(struct device *dev,
* non-volatile memory during production". Let's read them out at probe
* time once. They will not change.
*/
- if (chip_id == BMP180_CHIP_ID) {
- ret = bmp180_read_calib(data, &data->calib.bmp180);
- if (ret < 0) {
- dev_err(data->dev,
- "failed to read calibration coefficients\n");
- return ret;
- }
- } else if (chip_id == BMP280_CHIP_ID || chip_id == BME280_CHIP_ID) {
- ret = bmp280_read_calib(data, &data->calib.bmp280, chip_id);
- if (ret < 0) {
- dev_err(data->dev,
- "failed to read calibration coefficients\n");
- return ret;
- }
- }
+
+ ret = data->chip_info->read_calib(data);
+ if (ret < 0)
+ return dev_err_probe(data->dev, ret,
+ "failed to read calibration coefficients\n");
/*
* Attempt to grab an optional EOC IRQ - only the BMP085 has this
diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c
index bf4a7a617537..0c27211f3ea0 100644
--- a/drivers/iio/pressure/bmp280-i2c.c
+++ b/drivers/iio/pressure/bmp280-i2c.c
@@ -19,6 +19,9 @@ static int bmp280_i2c_probe(struct i2c_client *client,
case BME280_CHIP_ID:
regmap_config = &bmp280_regmap_config;
break;
+ case BMP380_CHIP_ID:
+ regmap_config = &bmp380_regmap_config;
+ break;
default:
return -EINVAL;
}
@@ -37,19 +40,21 @@ static int bmp280_i2c_probe(struct i2c_client *client,
}
static const struct of_device_id bmp280_of_i2c_match[] = {
- { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID },
- { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID },
- { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID },
{ .compatible = "bosch,bmp085", .data = (void *)BMP180_CHIP_ID },
+ { .compatible = "bosch,bmp180", .data = (void *)BMP180_CHIP_ID },
+ { .compatible = "bosch,bmp280", .data = (void *)BMP280_CHIP_ID },
+ { .compatible = "bosch,bme280", .data = (void *)BME280_CHIP_ID },
+ { .compatible = "bosch,bmp380", .data = (void *)BMP380_CHIP_ID },
{ },
};
MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match);
static const struct i2c_device_id bmp280_i2c_id[] = {
- {"bmp280", BMP280_CHIP_ID },
- {"bmp180", BMP180_CHIP_ID },
{"bmp085", BMP180_CHIP_ID },
+ {"bmp180", BMP180_CHIP_ID },
+ {"bmp280", BMP280_CHIP_ID },
{"bme280", BME280_CHIP_ID },
+ {"bmp380", BMP380_CHIP_ID },
{ },
};
MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id);
diff --git a/drivers/iio/pressure/bmp280-regmap.c b/drivers/iio/pressure/bmp280-regmap.c
index 969698518984..c98c67970265 100644
--- a/drivers/iio/pressure/bmp280-regmap.c
+++ b/drivers/iio/pressure/bmp280-regmap.c
@@ -72,6 +72,49 @@ static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg)
}
}
+static bool bmp380_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP380_REG_CMD:
+ case BMP380_REG_CONFIG:
+ case BMP380_REG_FIFO_CONFIG_1:
+ case BMP380_REG_FIFO_CONFIG_2:
+ case BMP380_REG_FIFO_WATERMARK_LSB:
+ case BMP380_REG_FIFO_WATERMARK_MSB:
+ case BMP380_REG_POWER_CONTROL:
+ case BMP380_REG_INT_CONTROL:
+ case BMP380_REG_IF_CONFIG:
+ case BMP380_REG_ODR:
+ case BMP380_REG_OSR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bmp380_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP380_REG_TEMP_XLSB:
+ case BMP380_REG_TEMP_LSB:
+ case BMP380_REG_TEMP_MSB:
+ case BMP380_REG_PRESS_XLSB:
+ case BMP380_REG_PRESS_LSB:
+ case BMP380_REG_PRESS_MSB:
+ case BMP380_REG_SENSOR_TIME_XLSB:
+ case BMP380_REG_SENSOR_TIME_LSB:
+ case BMP380_REG_SENSOR_TIME_MSB:
+ case BMP380_REG_INT_STATUS:
+ case BMP380_REG_FIFO_DATA:
+ case BMP380_REG_STATUS:
+ case BMP380_REG_ERROR:
+ case BMP380_REG_EVENT:
+ return true;
+ default:
+ return false;
+ }
+}
+
const struct regmap_config bmp280_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
@@ -83,3 +126,15 @@ const struct regmap_config bmp280_regmap_config = {
.volatile_reg = bmp280_is_volatile_reg,
};
EXPORT_SYMBOL_NS(bmp280_regmap_config, IIO_BMP280);
+
+const struct regmap_config bmp380_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BMP380_REG_CMD,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = bmp380_is_writeable_reg,
+ .volatile_reg = bmp380_is_volatile_reg,
+};
+EXPORT_SYMBOL_NS(bmp380_regmap_config, IIO_BMP280);
diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c
index 4cfaf3e869b8..011c68e07ebf 100644
--- a/drivers/iio/pressure/bmp280-spi.c
+++ b/drivers/iio/pressure/bmp280-spi.c
@@ -66,6 +66,9 @@ static int bmp280_spi_probe(struct spi_device *spi)
case BME280_CHIP_ID:
regmap_config = &bmp280_regmap_config;
break;
+ case BMP380_CHIP_ID:
+ regmap_config = &bmp380_regmap_config;
+ break;
default:
return -EINVAL;
}
@@ -92,6 +95,7 @@ static const struct of_device_id bmp280_of_spi_match[] = {
{ .compatible = "bosch,bmp181", },
{ .compatible = "bosch,bmp280", },
{ .compatible = "bosch,bme280", },
+ { .compatible = "bosch,bmp380", },
{ },
};
MODULE_DEVICE_TABLE(of, bmp280_of_spi_match);
@@ -101,6 +105,7 @@ static const struct spi_device_id bmp280_spi_id[] = {
{ "bmp181", BMP180_CHIP_ID },
{ "bmp280", BMP280_CHIP_ID },
{ "bme280", BME280_CHIP_ID },
+ { "bmp380", BMP380_CHIP_ID },
{ }
};
MODULE_DEVICE_TABLE(spi, bmp280_spi_id);
diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h
index 57ba0e85db91..c791325c7416 100644
--- a/drivers/iio/pressure/bmp280.h
+++ b/drivers/iio/pressure/bmp280.h
@@ -3,6 +3,87 @@
#include <linux/device.h>
#include <linux/regmap.h>
+/* BMP380 specific registers */
+#define BMP380_REG_CMD 0x7E
+#define BMP380_REG_CONFIG 0x1F
+#define BMP380_REG_ODR 0x1D
+#define BMP380_REG_OSR 0x1C
+#define BMP380_REG_POWER_CONTROL 0x1B
+#define BMP380_REG_IF_CONFIG 0x1A
+#define BMP380_REG_INT_CONTROL 0x19
+#define BMP380_REG_INT_STATUS 0x11
+#define BMP380_REG_EVENT 0x10
+#define BMP380_REG_STATUS 0x03
+#define BMP380_REG_ERROR 0x02
+#define BMP380_REG_ID 0x00
+
+#define BMP380_REG_FIFO_CONFIG_1 0x18
+#define BMP380_REG_FIFO_CONFIG_2 0x17
+#define BMP380_REG_FIFO_WATERMARK_MSB 0x16
+#define BMP380_REG_FIFO_WATERMARK_LSB 0x15
+#define BMP380_REG_FIFO_DATA 0x14
+#define BMP380_REG_FIFO_LENGTH_MSB 0x13
+#define BMP380_REG_FIFO_LENGTH_LSB 0x12
+
+#define BMP380_REG_SENSOR_TIME_MSB 0x0E
+#define BMP380_REG_SENSOR_TIME_LSB 0x0D
+#define BMP380_REG_SENSOR_TIME_XLSB 0x0C
+
+#define BMP380_REG_TEMP_MSB 0x09
+#define BMP380_REG_TEMP_LSB 0x08
+#define BMP380_REG_TEMP_XLSB 0x07
+
+#define BMP380_REG_PRESS_MSB 0x06
+#define BMP380_REG_PRESS_LSB 0x05
+#define BMP380_REG_PRESS_XLSB 0x04
+
+#define BMP380_REG_CALIB_TEMP_START 0x31
+#define BMP380_CALIB_REG_COUNT 21
+
+#define BMP380_FILTER_MASK GENMASK(3, 1)
+#define BMP380_FILTER_OFF 0
+#define BMP380_FILTER_1X 1
+#define BMP380_FILTER_3X 2
+#define BMP380_FILTER_7X 3
+#define BMP380_FILTER_15X 4
+#define BMP380_FILTER_31X 5
+#define BMP380_FILTER_63X 6
+#define BMP380_FILTER_127X 7
+
+#define BMP380_OSRS_TEMP_MASK GENMASK(5, 3)
+#define BMP380_OSRS_PRESS_MASK GENMASK(2, 0)
+
+#define BMP380_ODRS_MASK GENMASK(4, 0)
+
+#define BMP380_CTRL_SENSORS_MASK GENMASK(1, 0)
+#define BMP380_CTRL_SENSORS_PRESS_EN BIT(0)
+#define BMP380_CTRL_SENSORS_TEMP_EN BIT(1)
+#define BMP380_MODE_MASK GENMASK(5, 4)
+#define BMP380_MODE_SLEEP 0
+#define BMP380_MODE_FORCED 1
+#define BMP380_MODE_NORMAL 3
+
+#define BMP380_MIN_TEMP -4000
+#define BMP380_MAX_TEMP 8500
+#define BMP380_MIN_PRES 3000000
+#define BMP380_MAX_PRES 12500000
+
+#define BMP380_CMD_NOOP 0x00
+#define BMP380_CMD_EXTMODE_EN_MID 0x34
+#define BMP380_CMD_FIFO_FLUSH 0xB0
+#define BMP380_CMD_SOFT_RESET 0xB6
+
+#define BMP380_STATUS_CMD_RDY_MASK BIT(4)
+#define BMP380_STATUS_DRDY_PRESS_MASK BIT(5)
+#define BMP380_STATUS_DRDY_TEMP_MASK BIT(6)
+
+#define BMP380_ERR_FATAL_MASK BIT(0)
+#define BMP380_ERR_CMD_MASK BIT(1)
+#define BMP380_ERR_CONF_MASK BIT(2)
+
+#define BMP380_TEMP_SKIPPED 0x800000
+#define BMP380_PRESS_SKIPPED 0x800000
+
/* BMP280 specific registers */
#define BMP280_REG_HUMIDITY_LSB 0xFE
#define BMP280_REG_HUMIDITY_MSB 0xFD
@@ -13,6 +94,9 @@
#define BMP280_REG_PRESS_LSB 0xF8
#define BMP280_REG_PRESS_MSB 0xF7
+/* Helper mask to truncate excess 4 bits on pressure and temp readings */
+#define BMP280_MEAS_TRIM_MASK GENMASK(24, 4)
+
#define BMP280_REG_CONFIG 0xF5
#define BMP280_REG_CTRL_MEAS 0xF4
#define BMP280_REG_STATUS 0xF3
@@ -32,44 +116,46 @@
#define BMP280_REG_COMP_PRESS_START 0x8E
#define BMP280_COMP_PRESS_REG_COUNT 18
-#define BMP280_FILTER_MASK (BIT(4) | BIT(3) | BIT(2))
+#define BMP280_COMP_H5_MASK GENMASK(15, 4)
+
+#define BMP280_CONTIGUOUS_CALIB_REGS (BMP280_COMP_TEMP_REG_COUNT + \
+ BMP280_COMP_PRESS_REG_COUNT)
+
+#define BMP280_FILTER_MASK GENMASK(4, 2)
#define BMP280_FILTER_OFF 0
-#define BMP280_FILTER_2X BIT(2)
-#define BMP280_FILTER_4X BIT(3)
-#define BMP280_FILTER_8X (BIT(3) | BIT(2))
-#define BMP280_FILTER_16X BIT(4)
+#define BMP280_FILTER_2X 1
+#define BMP280_FILTER_4X 2
+#define BMP280_FILTER_8X 3
+#define BMP280_FILTER_16X 4
-#define BMP280_OSRS_HUMIDITY_MASK (BIT(2) | BIT(1) | BIT(0))
-#define BMP280_OSRS_HUMIDITIY_X(osrs_h) ((osrs_h) << 0)
+#define BMP280_OSRS_HUMIDITY_MASK GENMASK(2, 0)
#define BMP280_OSRS_HUMIDITY_SKIP 0
-#define BMP280_OSRS_HUMIDITY_1X BMP280_OSRS_HUMIDITIY_X(1)
-#define BMP280_OSRS_HUMIDITY_2X BMP280_OSRS_HUMIDITIY_X(2)
-#define BMP280_OSRS_HUMIDITY_4X BMP280_OSRS_HUMIDITIY_X(3)
-#define BMP280_OSRS_HUMIDITY_8X BMP280_OSRS_HUMIDITIY_X(4)
-#define BMP280_OSRS_HUMIDITY_16X BMP280_OSRS_HUMIDITIY_X(5)
+#define BMP280_OSRS_HUMIDITY_1X 1
+#define BMP280_OSRS_HUMIDITY_2X 2
+#define BMP280_OSRS_HUMIDITY_4X 3
+#define BMP280_OSRS_HUMIDITY_8X 4
+#define BMP280_OSRS_HUMIDITY_16X 5
-#define BMP280_OSRS_TEMP_MASK (BIT(7) | BIT(6) | BIT(5))
+#define BMP280_OSRS_TEMP_MASK GENMASK(7, 5)
#define BMP280_OSRS_TEMP_SKIP 0
-#define BMP280_OSRS_TEMP_X(osrs_t) ((osrs_t) << 5)
-#define BMP280_OSRS_TEMP_1X BMP280_OSRS_TEMP_X(1)
-#define BMP280_OSRS_TEMP_2X BMP280_OSRS_TEMP_X(2)
-#define BMP280_OSRS_TEMP_4X BMP280_OSRS_TEMP_X(3)
-#define BMP280_OSRS_TEMP_8X BMP280_OSRS_TEMP_X(4)
-#define BMP280_OSRS_TEMP_16X BMP280_OSRS_TEMP_X(5)
-
-#define BMP280_OSRS_PRESS_MASK (BIT(4) | BIT(3) | BIT(2))
+#define BMP280_OSRS_TEMP_1X 1
+#define BMP280_OSRS_TEMP_2X 2
+#define BMP280_OSRS_TEMP_4X 3
+#define BMP280_OSRS_TEMP_8X 4
+#define BMP280_OSRS_TEMP_16X 5
+
+#define BMP280_OSRS_PRESS_MASK GENMASK(4, 2)
#define BMP280_OSRS_PRESS_SKIP 0
-#define BMP280_OSRS_PRESS_X(osrs_p) ((osrs_p) << 2)
-#define BMP280_OSRS_PRESS_1X BMP280_OSRS_PRESS_X(1)
-#define BMP280_OSRS_PRESS_2X BMP280_OSRS_PRESS_X(2)
-#define BMP280_OSRS_PRESS_4X BMP280_OSRS_PRESS_X(3)
-#define BMP280_OSRS_PRESS_8X BMP280_OSRS_PRESS_X(4)
-#define BMP280_OSRS_PRESS_16X BMP280_OSRS_PRESS_X(5)
-
-#define BMP280_MODE_MASK (BIT(1) | BIT(0))
+#define BMP280_OSRS_PRESS_1X 1
+#define BMP280_OSRS_PRESS_2X 2
+#define BMP280_OSRS_PRESS_4X 3
+#define BMP280_OSRS_PRESS_8X 4
+#define BMP280_OSRS_PRESS_16X 5
+
+#define BMP280_MODE_MASK GENMASK(1, 0)
#define BMP280_MODE_SLEEP 0
-#define BMP280_MODE_FORCED BIT(0)
-#define BMP280_MODE_NORMAL (BIT(1) | BIT(0))
+#define BMP280_MODE_FORCED 1
+#define BMP280_MODE_NORMAL 3
/* BMP180 specific registers */
#define BMP180_REG_OUT_XLSB 0xF8
@@ -79,19 +165,22 @@
#define BMP180_REG_CALIB_START 0xAA
#define BMP180_REG_CALIB_COUNT 22
+#define BMP180_MEAS_CTRL_MASK GENMASK(4, 0)
+#define BMP180_MEAS_TEMP 0x0E
+#define BMP180_MEAS_PRESS 0x14
#define BMP180_MEAS_SCO BIT(5)
-#define BMP180_MEAS_TEMP (0x0E | BMP180_MEAS_SCO)
-#define BMP180_MEAS_PRESS_X(oss) ((oss) << 6 | 0x14 | BMP180_MEAS_SCO)
-#define BMP180_MEAS_PRESS_1X BMP180_MEAS_PRESS_X(0)
-#define BMP180_MEAS_PRESS_2X BMP180_MEAS_PRESS_X(1)
-#define BMP180_MEAS_PRESS_4X BMP180_MEAS_PRESS_X(2)
-#define BMP180_MEAS_PRESS_8X BMP180_MEAS_PRESS_X(3)
+#define BMP180_OSRS_PRESS_MASK GENMASK(7, 6)
+#define BMP180_MEAS_PRESS_1X 0
+#define BMP180_MEAS_PRESS_2X 1
+#define BMP180_MEAS_PRESS_4X 2
+#define BMP180_MEAS_PRESS_8X 3
/* BMP180 and BMP280 common registers */
#define BMP280_REG_CTRL_MEAS 0xF4
#define BMP280_REG_RESET 0xE0
#define BMP280_REG_ID 0xD0
+#define BMP380_CHIP_ID 0x50
#define BMP180_CHIP_ID 0x55
#define BMP280_CHIP_ID 0x58
#define BME280_CHIP_ID 0x60
@@ -105,6 +194,7 @@
/* Regmap configurations */
extern const struct regmap_config bmp180_regmap_config;
extern const struct regmap_config bmp280_regmap_config;
+extern const struct regmap_config bmp380_regmap_config;
/* Probe called from different transports */
int bmp280_common_probe(struct device *dev,
diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c
index 5f6bb3603a8b..f0b0d198c6d4 100644
--- a/drivers/iio/pressure/dlhl60d.c
+++ b/drivers/iio/pressure/dlhl60d.c
@@ -129,9 +129,8 @@ static int dlh_read_direct(struct dlh_state *st,
if (ret)
return ret;
- *pressure = get_unaligned_be32(&st->rx_buf[1]) >> 8;
- *temperature = get_unaligned_be32(&st->rx_buf[3]) &
- GENMASK(DLH_NUM_TEMP_BITS - 1, 0);
+ *pressure = get_unaligned_be24(&st->rx_buf[1]);
+ *temperature = get_unaligned_be24(&st->rx_buf[4]);
return 0;
}
diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c
index 36fb7ae0d0a9..984a3f511a1a 100644
--- a/drivers/iio/pressure/dps310.c
+++ b/drivers/iio/pressure/dps310.c
@@ -89,6 +89,7 @@ struct dps310_data {
s32 c00, c10, c20, c30, c01, c11, c21;
s32 pressure_raw;
s32 temp_raw;
+ bool timeout_recovery_failed;
};
static const struct iio_chan_spec dps310_channels[] = {
@@ -159,6 +160,102 @@ static int dps310_get_coefs(struct dps310_data *data)
return 0;
}
+/*
+ * Some versions of the chip will read temperatures in the ~60C range when
+ * it's actually ~20C. This is the manufacturer recommended workaround
+ * to correct the issue. The registers used below are undocumented.
+ */
+static int dps310_temp_workaround(struct dps310_data *data)
+{
+ int rc;
+ int reg;
+
+ rc = regmap_read(data->regmap, 0x32, &reg);
+ if (rc)
+ return rc;
+
+ /*
+ * If bit 1 is set then the device is okay, and the workaround does not
+ * need to be applied
+ */
+ if (reg & BIT(1))
+ return 0;
+
+ rc = regmap_write(data->regmap, 0x0e, 0xA5);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(data->regmap, 0x0f, 0x96);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(data->regmap, 0x62, 0x02);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(data->regmap, 0x0e, 0x00);
+ if (rc)
+ return rc;
+
+ return regmap_write(data->regmap, 0x0f, 0x00);
+}
+
+static int dps310_startup(struct dps310_data *data)
+{
+ int rc;
+ int ready;
+
+ /*
+ * Set up pressure sensor in single sample, one measurement per second
+ * mode
+ */
+ rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0);
+ if (rc)
+ return rc;
+
+ /*
+ * Set up external (MEMS) temperature sensor in single sample, one
+ * measurement per second mode
+ */
+ rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT);
+ if (rc)
+ return rc;
+
+ /* Temp and pressure shifts are disabled when PRC <= 8 */
+ rc = regmap_write_bits(data->regmap, DPS310_CFG_REG,
+ DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0);
+ if (rc)
+ return rc;
+
+ /* MEAS_CFG doesn't update correctly unless first written with 0 */
+ rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
+ DPS310_MEAS_CTRL_BITS, 0);
+ if (rc)
+ return rc;
+
+ /* Turn on temperature and pressure measurement in the background */
+ rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
+ DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN |
+ DPS310_TEMP_EN | DPS310_BACKGROUND);
+ if (rc)
+ return rc;
+
+ /*
+ * Calibration coefficients required for reporting temperature.
+ * They are available 40ms after the device has started
+ */
+ rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
+ ready & DPS310_COEF_RDY, 10000, 40000);
+ if (rc)
+ return rc;
+
+ rc = dps310_get_coefs(data);
+ if (rc)
+ return rc;
+
+ return dps310_temp_workaround(data);
+}
+
static int dps310_get_pres_precision(struct dps310_data *data)
{
int rc;
@@ -297,11 +394,69 @@ static int dps310_get_temp_k(struct dps310_data *data)
return scale_factors[ilog2(rc)];
}
+static int dps310_reset_wait(struct dps310_data *data)
+{
+ int rc;
+
+ rc = regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC);
+ if (rc)
+ return rc;
+
+ /* Wait for device chip access: 2.5ms in specification */
+ usleep_range(2500, 12000);
+ return 0;
+}
+
+static int dps310_reset_reinit(struct dps310_data *data)
+{
+ int rc;
+
+ rc = dps310_reset_wait(data);
+ if (rc)
+ return rc;
+
+ return dps310_startup(data);
+}
+
+static int dps310_ready_status(struct dps310_data *data, int ready_bit, int timeout)
+{
+ int sleep = DPS310_POLL_SLEEP_US(timeout);
+ int ready;
+
+ return regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, ready & ready_bit,
+ sleep, timeout);
+}
+
+static int dps310_ready(struct dps310_data *data, int ready_bit, int timeout)
+{
+ int rc;
+
+ rc = dps310_ready_status(data, ready_bit, timeout);
+ if (rc) {
+ if (rc == -ETIMEDOUT && !data->timeout_recovery_failed) {
+ /* Reset and reinitialize the chip. */
+ if (dps310_reset_reinit(data)) {
+ data->timeout_recovery_failed = true;
+ } else {
+ /* Try again to get sensor ready status. */
+ if (dps310_ready_status(data, ready_bit, timeout))
+ data->timeout_recovery_failed = true;
+ else
+ return 0;
+ }
+ }
+
+ return rc;
+ }
+
+ data->timeout_recovery_failed = false;
+ return 0;
+}
+
static int dps310_read_pres_raw(struct dps310_data *data)
{
int rc;
int rate;
- int ready;
int timeout;
s32 raw;
u8 val[3];
@@ -313,9 +468,7 @@ static int dps310_read_pres_raw(struct dps310_data *data)
timeout = DPS310_POLL_TIMEOUT_US(rate);
/* Poll for sensor readiness; base the timeout upon the sample rate. */
- rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
- ready & DPS310_PRS_RDY,
- DPS310_POLL_SLEEP_US(timeout), timeout);
+ rc = dps310_ready(data, DPS310_PRS_RDY, timeout);
if (rc)
goto done;
@@ -352,7 +505,6 @@ static int dps310_read_temp_raw(struct dps310_data *data)
{
int rc;
int rate;
- int ready;
int timeout;
if (mutex_lock_interruptible(&data->lock))
@@ -362,10 +514,8 @@ static int dps310_read_temp_raw(struct dps310_data *data)
timeout = DPS310_POLL_TIMEOUT_US(rate);
/* Poll for sensor readiness; base the timeout upon the sample rate. */
- rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
- ready & DPS310_TMP_RDY,
- DPS310_POLL_SLEEP_US(timeout), timeout);
- if (rc < 0)
+ rc = dps310_ready(data, DPS310_TMP_RDY, timeout);
+ if (rc)
goto done;
rc = dps310_read_temp_ready(data);
@@ -660,7 +810,7 @@ static void dps310_reset(void *action_data)
{
struct dps310_data *data = action_data;
- regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC);
+ dps310_reset_wait(data);
}
static const struct regmap_config dps310_regmap_config = {
@@ -677,52 +827,12 @@ static const struct iio_info dps310_info = {
.write_raw = dps310_write_raw,
};
-/*
- * Some verions of chip will read temperatures in the ~60C range when
- * its actually ~20C. This is the manufacturer recommended workaround
- * to correct the issue. The registers used below are undocumented.
- */
-static int dps310_temp_workaround(struct dps310_data *data)
-{
- int rc;
- int reg;
-
- rc = regmap_read(data->regmap, 0x32, &reg);
- if (rc < 0)
- return rc;
-
- /*
- * If bit 1 is set then the device is okay, and the workaround does not
- * need to be applied
- */
- if (reg & BIT(1))
- return 0;
-
- rc = regmap_write(data->regmap, 0x0e, 0xA5);
- if (rc < 0)
- return rc;
-
- rc = regmap_write(data->regmap, 0x0f, 0x96);
- if (rc < 0)
- return rc;
-
- rc = regmap_write(data->regmap, 0x62, 0x02);
- if (rc < 0)
- return rc;
-
- rc = regmap_write(data->regmap, 0x0e, 0x00);
- if (rc < 0)
- return rc;
-
- return regmap_write(data->regmap, 0x0f, 0x00);
-}
-
static int dps310_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct dps310_data *data;
struct iio_dev *iio;
- int rc, ready;
+ int rc;
iio = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!iio)
@@ -747,54 +857,8 @@ static int dps310_probe(struct i2c_client *client,
if (rc)
return rc;
- /*
- * Set up pressure sensor in single sample, one measurement per second
- * mode
- */
- rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0);
-
- /*
- * Set up external (MEMS) temperature sensor in single sample, one
- * measurement per second mode
- */
- rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT);
- if (rc < 0)
- return rc;
-
- /* Temp and pressure shifts are disabled when PRC <= 8 */
- rc = regmap_write_bits(data->regmap, DPS310_CFG_REG,
- DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0);
- if (rc < 0)
- return rc;
-
- /* MEAS_CFG doesn't update correctly unless first written with 0 */
- rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
- DPS310_MEAS_CTRL_BITS, 0);
- if (rc < 0)
- return rc;
-
- /* Turn on temperature and pressure measurement in the background */
- rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
- DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN |
- DPS310_TEMP_EN | DPS310_BACKGROUND);
- if (rc < 0)
- return rc;
-
- /*
- * Calibration coefficients required for reporting temperature.
- * They are available 40ms after the device has started
- */
- rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
- ready & DPS310_COEF_RDY, 10000, 40000);
- if (rc < 0)
- return rc;
-
- rc = dps310_get_coefs(data);
- if (rc < 0)
- return rc;
-
- rc = dps310_temp_workaround(data);
- if (rc < 0)
+ rc = dps310_startup(data);
+ if (rc)
return rc;
rc = devm_iio_device_register(&client->dev, iio);
diff --git a/drivers/iio/pressure/icp10100.c b/drivers/iio/pressure/icp10100.c
index af4621eaa6b5..b62f28585db5 100644
--- a/drivers/iio/pressure/icp10100.c
+++ b/drivers/iio/pressure/icp10100.c
@@ -595,7 +595,7 @@ static int icp10100_probe(struct i2c_client *client,
return devm_iio_device_register(&client->dev, indio_dev);
}
-static int __maybe_unused icp10100_suspend(struct device *dev)
+static int icp10100_suspend(struct device *dev)
{
struct icp10100_state *st = iio_priv(dev_get_drvdata(dev));
int ret;
@@ -607,7 +607,7 @@ static int __maybe_unused icp10100_suspend(struct device *dev)
return ret;
}
-static int __maybe_unused icp10100_resume(struct device *dev)
+static int icp10100_resume(struct device *dev)
{
struct icp10100_state *st = iio_priv(dev_get_drvdata(dev));
int ret;
@@ -626,8 +626,8 @@ out_unlock:
return ret;
}
-static UNIVERSAL_DEV_PM_OPS(icp10100_pm, icp10100_suspend, icp10100_resume,
- NULL);
+static DEFINE_RUNTIME_DEV_PM_OPS(icp10100_pm, icp10100_suspend, icp10100_resume,
+ NULL);
static const struct of_device_id icp10100_of_match[] = {
{
@@ -646,7 +646,7 @@ MODULE_DEVICE_TABLE(i2c, icp10100_id);
static struct i2c_driver icp10100_driver = {
.driver = {
.name = "icp10100",
- .pm = &icp10100_pm,
+ .pm = pm_ptr(&icp10100_pm),
.of_match_table = icp10100_of_match,
},
.probe = icp10100_probe,
diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h
index 156e6a72dc5c..6e11bea784fa 100644
--- a/drivers/iio/pressure/st_pressure.h
+++ b/drivers/iio/pressure/st_pressure.h
@@ -22,6 +22,7 @@ enum st_press_type {
LPS33HW,
LPS35HW,
LPS22HH,
+ LPS22DF,
ST_PRESS_MAX,
};
@@ -32,6 +33,7 @@ enum st_press_type {
#define LPS33HW_PRESS_DEV_NAME "lps33hw"
#define LPS35HW_PRESS_DEV_NAME "lps35hw"
#define LPS22HH_PRESS_DEV_NAME "lps22hh"
+#define LPS22DF_PRESS_DEV_NAME "lps22df"
/**
* struct st_sensors_platform_data - default press platform data
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
index 76913a2028d2..80176e3083af 100644
--- a/drivers/iio/pressure/st_pressure_core.c
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -552,6 +552,76 @@ static const struct st_sensor_settings st_press_sensors_settings[] = {
.multi_read_bit = false,
.bootime = 2,
},
+ {
+ /*
+ * CUSTOM VALUES FOR LPS22DF SENSOR
+ * See LPS22DF datasheet:
+ * http://www.st.com/resource/en/datasheet/lps22df.pdf
+ */
+ .wai = 0xb4,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LPS22DF_PRESS_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_press_lps22hb_channels,
+ .num_ch = ARRAY_SIZE(st_press_lps22hb_channels),
+ .odr = {
+ .addr = 0x10,
+ .mask = 0x78,
+ .odr_avl = {
+ { .hz = 1, .value = 0x01 },
+ { .hz = 4, .value = 0x02 },
+ { .hz = 10, .value = 0x03 },
+ { .hz = 25, .value = 0x04 },
+ { .hz = 50, .value = 0x05 },
+ { .hz = 75, .value = 0x06 },
+ { .hz = 100, .value = 0x07 },
+ { .hz = 200, .value = 0x08 },
+ },
+ },
+ .pw = {
+ .addr = 0x10,
+ .mask = 0x78,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .fs = {
+ .fs_avl = {
+ /*
+ * Pressure and temperature sensitivity values
+ * as defined in table 2 of LPS22DF datasheet.
+ */
+ [0] = {
+ .num = ST_PRESS_FS_AVL_1260MB,
+ .gain = ST_PRESS_KPASCAL_NANO_SCALE,
+ .gain2 = ST_PRESS_LPS22HB_LSB_PER_CELSIUS,
+ },
+ },
+ },
+ .bdu = {
+ .addr = 0x11,
+ .mask = BIT(3),
+ },
+ .drdy_irq = {
+ .int1 = {
+ .addr = 0x13,
+ .mask = BIT(5),
+ .addr_od = 0x12,
+ .mask_od = BIT(1),
+ },
+ .addr_ihl = 0x12,
+ .mask_ihl = BIT(3),
+ .stat_drdy = {
+ .addr = ST_SENSORS_DEFAULT_STAT_ADDR,
+ .mask = 0x03,
+ },
+ },
+ .sim = {
+ .addr = 0x0E,
+ .value = BIT(5),
+ },
+ .multi_read_bit = false,
+ .bootime = 2,
+ },
};
static int st_press_write_raw(struct iio_dev *indio_dev,
diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c
index 7035777fd988..58fede861891 100644
--- a/drivers/iio/pressure/st_pressure_i2c.c
+++ b/drivers/iio/pressure/st_pressure_i2c.c
@@ -47,6 +47,10 @@ static const struct of_device_id st_press_of_match[] = {
.compatible = "st,lps22hh",
.data = LPS22HH_PRESS_DEV_NAME,
},
+ {
+ .compatible = "st,lps22df",
+ .data = LPS22DF_PRESS_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_press_of_match);
@@ -67,6 +71,7 @@ static const struct i2c_device_id st_press_id_table[] = {
{ LPS33HW_PRESS_DEV_NAME, LPS33HW },
{ LPS35HW_PRESS_DEV_NAME, LPS35HW },
{ LPS22HH_PRESS_DEV_NAME, LPS22HH },
+ { LPS22DF_PRESS_DEV_NAME, LPS22DF },
{},
};
MODULE_DEVICE_TABLE(i2c, st_press_id_table);
diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c
index bfab8e7fb061..25cca5ad7c55 100644
--- a/drivers/iio/pressure/st_pressure_spi.c
+++ b/drivers/iio/pressure/st_pressure_spi.c
@@ -51,6 +51,10 @@ static const struct of_device_id st_press_of_match[] = {
.compatible = "st,lps22hh",
.data = LPS22HH_PRESS_DEV_NAME,
},
+ {
+ .compatible = "st,lps22df",
+ .data = LPS22DF_PRESS_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_press_of_match);
@@ -97,6 +101,7 @@ static const struct spi_device_id st_press_id_table[] = {
{ LPS33HW_PRESS_DEV_NAME },
{ LPS35HW_PRESS_DEV_NAME },
{ LPS22HH_PRESS_DEV_NAME },
+ { LPS22DF_PRESS_DEV_NAME },
{ "lps001wp-press" },
{ "lps25h-press", },
{ "lps331ap-press" },
diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c
index 05015351a34a..faf2f806ce80 100644
--- a/drivers/iio/proximity/srf04.c
+++ b/drivers/iio/proximity/srf04.c
@@ -359,7 +359,7 @@ static int srf04_remove(struct platform_device *pdev)
return 0;
}
-static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev)
+static int srf04_pm_runtime_suspend(struct device *dev)
{
struct platform_device *pdev = container_of(dev,
struct platform_device, dev);
@@ -371,7 +371,7 @@ static int __maybe_unused srf04_pm_runtime_suspend(struct device *dev)
return 0;
}
-static int __maybe_unused srf04_pm_runtime_resume(struct device *dev)
+static int srf04_pm_runtime_resume(struct device *dev)
{
struct platform_device *pdev = container_of(dev,
struct platform_device, dev);
@@ -385,8 +385,8 @@ static int __maybe_unused srf04_pm_runtime_resume(struct device *dev)
}
static const struct dev_pm_ops srf04_pm_ops = {
- SET_RUNTIME_PM_OPS(srf04_pm_runtime_suspend,
- srf04_pm_runtime_resume, NULL)
+ RUNTIME_PM_OPS(srf04_pm_runtime_suspend,
+ srf04_pm_runtime_resume, NULL)
};
static struct platform_driver srf04_driver = {
@@ -395,7 +395,7 @@ static struct platform_driver srf04_driver = {
.driver = {
.name = "srf04-gpio",
.of_match_table = of_srf04_match,
- .pm = &srf04_pm_ops,
+ .pm = pm_ptr(&srf04_pm_ops),
},
};
diff --git a/drivers/iio/proximity/sx9310.c b/drivers/iio/proximity/sx9310.c
index ea7318b508ea..0e4747ccd3cf 100644
--- a/drivers/iio/proximity/sx9310.c
+++ b/drivers/iio/proximity/sx9310.c
@@ -965,7 +965,7 @@ static int sx9310_probe(struct i2c_client *client)
return sx_common_probe(client, &sx9310_chip_info, &sx9310_regmap_config);
}
-static int __maybe_unused sx9310_suspend(struct device *dev)
+static int sx9310_suspend(struct device *dev)
{
struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
u8 ctrl0;
@@ -991,7 +991,7 @@ out:
return ret;
}
-static int __maybe_unused sx9310_resume(struct device *dev)
+static int sx9310_resume(struct device *dev)
{
struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
int ret;
@@ -1013,7 +1013,7 @@ out:
return 0;
}
-static SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(sx9310_pm_ops, sx9310_suspend, sx9310_resume);
static const struct acpi_device_id sx9310_acpi_match[] = {
{ "STH9310", SX9310_WHOAMI_VALUE },
@@ -1041,7 +1041,7 @@ static struct i2c_driver sx9310_driver = {
.name = "sx9310",
.acpi_match_table = sx9310_acpi_match,
.of_match_table = sx9310_of_match,
- .pm = &sx9310_pm_ops,
+ .pm = pm_sleep_ptr(&sx9310_pm_ops),
/*
* Lots of i2c transfers in probe + over 200 ms waiting in
diff --git a/drivers/iio/proximity/sx9324.c b/drivers/iio/proximity/sx9324.c
index edb5a2ce4e27..977cf17cec52 100644
--- a/drivers/iio/proximity/sx9324.c
+++ b/drivers/iio/proximity/sx9324.c
@@ -1073,7 +1073,7 @@ static int sx9324_probe(struct i2c_client *client)
return sx_common_probe(client, &sx9324_chip_info, &sx9324_regmap_config);
}
-static int __maybe_unused sx9324_suspend(struct device *dev)
+static int sx9324_suspend(struct device *dev)
{
struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
unsigned int regval;
@@ -1098,7 +1098,7 @@ out:
return ret;
}
-static int __maybe_unused sx9324_resume(struct device *dev)
+static int sx9324_resume(struct device *dev)
{
struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
int ret;
@@ -1114,7 +1114,7 @@ static int __maybe_unused sx9324_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(sx9324_pm_ops, sx9324_suspend, sx9324_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(sx9324_pm_ops, sx9324_suspend, sx9324_resume);
static const struct acpi_device_id sx9324_acpi_match[] = {
{ "STH9324", SX9324_WHOAMI_VALUE },
@@ -1139,7 +1139,7 @@ static struct i2c_driver sx9324_driver = {
.name = "sx9324",
.acpi_match_table = sx9324_acpi_match,
.of_match_table = sx9324_of_match,
- .pm = &sx9324_pm_ops,
+ .pm = pm_sleep_ptr(&sx9324_pm_ops),
/*
* Lots of i2c transfers in probe + over 200 ms waiting in
diff --git a/drivers/iio/proximity/sx9360.c b/drivers/iio/proximity/sx9360.c
index d9a12e6be6ca..7fa2213d23ba 100644
--- a/drivers/iio/proximity/sx9360.c
+++ b/drivers/iio/proximity/sx9360.c
@@ -819,7 +819,7 @@ static int sx9360_probe(struct i2c_client *client)
return sx_common_probe(client, &sx9360_chip_info, &sx9360_regmap_config);
}
-static int __maybe_unused sx9360_suspend(struct device *dev)
+static int sx9360_suspend(struct device *dev)
{
struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
unsigned int regval;
@@ -844,7 +844,7 @@ out:
return ret;
}
-static int __maybe_unused sx9360_resume(struct device *dev)
+static int sx9360_resume(struct device *dev)
{
struct sx_common_data *data = iio_priv(dev_get_drvdata(dev));
int ret;
@@ -861,7 +861,7 @@ static int __maybe_unused sx9360_resume(struct device *dev)
return 0;
}
-static SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume);
+static DEFINE_SIMPLE_DEV_PM_OPS(sx9360_pm_ops, sx9360_suspend, sx9360_resume);
static const struct acpi_device_id sx9360_acpi_match[] = {
{ "STH9360", SX9360_WHOAMI_VALUE },
@@ -886,7 +886,7 @@ static struct i2c_driver sx9360_driver = {
.name = "sx9360",
.acpi_match_table = sx9360_acpi_match,
.of_match_table = sx9360_of_match,
- .pm = &sx9360_pm_ops,
+ .pm = pm_sleep_ptr(&sx9360_pm_ops),
/*
* Lots of i2c transfers in probe + over 200 ms waiting in
diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
index 0808bb865928..8eb0f962ed25 100644
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -79,16 +79,15 @@ struct mlx90614_data {
/* Bandwidth values for IIR filtering */
static const int mlx90614_iir_values[] = {77, 31, 20, 15, 723, 153, 110, 86};
-static IIO_CONST_ATTR(in_temp_object_filter_low_pass_3db_frequency_available,
- "0.15 0.20 0.31 0.77 0.86 1.10 1.53 7.23");
-
-static struct attribute *mlx90614_attributes[] = {
- &iio_const_attr_in_temp_object_filter_low_pass_3db_frequency_available.dev_attr.attr,
- NULL,
-};
-
-static const struct attribute_group mlx90614_attr_group = {
- .attrs = mlx90614_attributes,
+static const int mlx90614_freqs[][2] = {
+ {0, 150000},
+ {0, 200000},
+ {0, 310000},
+ {0, 770000},
+ {0, 860000},
+ {1, 100000},
+ {1, 530000},
+ {7, 230000}
};
/*
@@ -373,6 +372,22 @@ static int mlx90614_write_raw_get_fmt(struct iio_dev *indio_dev,
}
}
+static int mlx90614_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_LOW_PASS_FILTER_3DB_FREQUENCY:
+ *vals = (int *)mlx90614_freqs;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = 2 * ARRAY_SIZE(mlx90614_freqs);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
static const struct iio_chan_spec mlx90614_channels[] = {
{
.type = IIO_TEMP,
@@ -389,6 +404,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ .info_mask_separate_available =
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
},
@@ -401,6 +418,8 @@ static const struct iio_chan_spec mlx90614_channels[] = {
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
BIT(IIO_CHAN_INFO_CALIBEMISSIVITY) |
BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ .info_mask_separate_available =
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
BIT(IIO_CHAN_INFO_SCALE),
},
@@ -410,7 +429,7 @@ static const struct iio_info mlx90614_info = {
.read_raw = mlx90614_read_raw,
.write_raw = mlx90614_write_raw,
.write_raw_get_fmt = mlx90614_write_raw_get_fmt,
- .attrs = &mlx90614_attr_group,
+ .read_avail = mlx90614_read_avail,
};
#ifdef CONFIG_PM
diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c
index e8ef47147e2b..f6dec0e5f097 100644
--- a/drivers/iio/temperature/mlx90632.c
+++ b/drivers/iio/temperature/mlx90632.c
@@ -18,6 +18,7 @@
#include <linux/math64.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -128,6 +129,7 @@
* calculations
* @object_ambient_temperature: Ambient temperature at object (might differ of
* the ambient temperature of sensor.
+ * @regulator: Regulator of the device
*/
struct mlx90632_data {
struct i2c_client *client;
@@ -136,6 +138,7 @@ struct mlx90632_data {
u16 emissivity;
u8 mtyp;
u32 object_ambient_temperature;
+ struct regulator *regulator;
};
static const struct regmap_range mlx90632_volatile_reg_range[] = {
@@ -208,6 +211,15 @@ static s32 mlx90632_pwr_continuous(struct regmap *regmap)
}
/**
+ * mlx90632_reset_delay() - Give the mlx90632 some time to reset properly
+ * If this is not done, the following I2C command(s) will not be accepted.
+ */
+static void mlx90632_reset_delay(void)
+{
+ usleep_range(150, 200);
+}
+
+/**
* mlx90632_perform_measurement() - Trigger and retrieve current measurement cycle
* @data: pointer to mlx90632_data object containing regmap information
*
@@ -248,11 +260,7 @@ static int mlx90632_set_meas_type(struct regmap *regmap, u8 type)
if (ret < 0)
return ret;
- /*
- * Give the mlx90632 some time to reset properly before sending a new I2C command
- * if this is not done, the following I2C command(s) will not be accepted.
- */
- usleep_range(150, 200);
+ mlx90632_reset_delay();
ret = regmap_write_bits(regmap, MLX90632_REG_CONTROL,
(MLX90632_CFG_MTYP_MASK | MLX90632_CFG_PWR_MASK),
@@ -841,6 +849,32 @@ static int mlx90632_wakeup(struct mlx90632_data *data)
return mlx90632_pwr_continuous(data->regmap);
}
+static void mlx90632_disable_regulator(void *_data)
+{
+ struct mlx90632_data *data = _data;
+ int ret;
+
+ ret = regulator_disable(data->regulator);
+ if (ret < 0)
+ dev_err(regmap_get_device(data->regmap),
+ "Failed to disable power regulator: %d\n", ret);
+}
+
+static int mlx90632_enable_regulator(struct mlx90632_data *data)
+{
+ int ret;
+
+ ret = regulator_enable(data->regulator);
+ if (ret < 0) {
+ dev_err(regmap_get_device(data->regmap), "Failed to enable power regulator!\n");
+ return ret;
+ }
+
+ mlx90632_reset_delay();
+
+ return ret;
+}
+
static int mlx90632_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -876,6 +910,23 @@ static int mlx90632_probe(struct i2c_client *client,
indio_dev->channels = mlx90632_channels;
indio_dev->num_channels = ARRAY_SIZE(mlx90632_channels);
+ mlx90632->regulator = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(mlx90632->regulator))
+ return dev_err_probe(&client->dev, PTR_ERR(mlx90632->regulator),
+ "failed to get vdd regulator");
+
+ ret = mlx90632_enable_regulator(mlx90632);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&client->dev, mlx90632_disable_regulator,
+ mlx90632);
+ if (ret < 0) {
+ dev_err(&client->dev, "Failed to setup regulator cleanup action %d\n",
+ ret);
+ return ret;
+ }
+
ret = mlx90632_wakeup(mlx90632);
if (ret < 0) {
dev_err(&client->dev, "Wakeup failed: %d\n", ret);
diff --git a/drivers/iio/test/iio-test-rescale.c b/drivers/iio/test/iio-test-rescale.c
index cc782ccff880..31ee55a6faed 100644
--- a/drivers/iio/test/iio-test-rescale.c
+++ b/drivers/iio/test/iio-test-rescale.c
@@ -29,7 +29,7 @@ struct rescale_tc_data {
const char *expected_off;
};
-const struct rescale_tc_data scale_cases[] = {
+static const struct rescale_tc_data scale_cases[] = {
/*
* Typical use cases
*/
@@ -477,7 +477,7 @@ const struct rescale_tc_data scale_cases[] = {
},
};
-const struct rescale_tc_data offset_cases[] = {
+static const struct rescale_tc_data offset_cases[] = {
/*
* Typical use cases
*/