aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/drivers/iio
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/iio')
-rw-r--r--drivers/iio/accel/Kconfig2
-rw-r--r--drivers/iio/accel/hid-sensor-accel-3d.c5
-rw-r--r--drivers/iio/accel/kxcjk-1013.c3
-rw-r--r--drivers/iio/accel/st_accel.h1
-rw-r--r--drivers/iio/accel/st_accel_core.c1
-rw-r--r--drivers/iio/accel/st_accel_i2c.c5
-rw-r--r--drivers/iio/accel/st_accel_spi.c5
-rw-r--r--drivers/iio/adc/Kconfig23
-rw-r--r--drivers/iio/adc/Makefile2
-rw-r--r--drivers/iio/adc/ad7124.c684
-rw-r--r--drivers/iio/adc/ad7949.c347
-rw-r--r--drivers/iio/adc/ad_sigma_delta.c22
-rw-r--r--drivers/iio/adc/exynos_adc.c14
-rw-r--r--drivers/iio/adc/ina2xx-adc.c2
-rw-r--r--drivers/iio/adc/max11100.c5
-rw-r--r--drivers/iio/adc/max9611.c5
-rw-r--r--drivers/iio/adc/meson_saradc.c243
-rw-r--r--drivers/iio/adc/qcom-spmi-adc5.c58
-rw-r--r--drivers/iio/adc/rcar-gyroadc.c11
-rw-r--r--drivers/iio/adc/sc27xx_adc.c12
-rw-r--r--drivers/iio/adc/stm32-adc-core.c182
-rw-r--r--drivers/iio/adc/stm32-adc.c303
-rw-r--r--drivers/iio/adc/ti-adc128s052.c37
-rw-r--r--drivers/iio/common/hid-sensors/hid-sensor-attributes.c2
-rw-r--r--drivers/iio/common/ssp_sensors/ssp_dev.c20
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_core.c3
-rw-r--r--drivers/iio/common/st_sensors/st_sensors_trigger.c4
-rw-r--r--drivers/iio/dac/Kconfig9
-rw-r--r--drivers/iio/dac/Makefile1
-rw-r--r--drivers/iio/dac/ad5686-spi.c21
-rw-r--r--drivers/iio/dac/ad5686.c19
-rw-r--r--drivers/iio/dac/ad5686.h7
-rw-r--r--drivers/iio/dac/dpot-dac.c4
-rw-r--r--drivers/iio/dac/ti-dac7311.c338
-rw-r--r--drivers/iio/gyro/hid-sensor-gyro-3d.c5
-rw-r--r--drivers/iio/humidity/hid-sensor-humidity.c3
-rw-r--r--drivers/iio/imu/bmi160/bmi160.h1
-rw-r--r--drivers/iio/imu/bmi160/bmi160_core.c38
-rw-r--r--drivers/iio/imu/bmi160/bmi160_i2c.c8
-rw-r--r--drivers/iio/imu/bmi160/bmi160_spi.c8
-rw-r--r--drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c16
-rw-r--r--drivers/iio/imu/st_lsm6dsx/Makefile3
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h167
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c165
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c283
-rw-r--r--drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c779
-rw-r--r--drivers/iio/industrialio-core.c3
-rw-r--r--drivers/iio/light/Kconfig13
-rw-r--r--drivers/iio/light/Makefile1
-rw-r--r--drivers/iio/light/hid-sensor-als.c8
-rw-r--r--drivers/iio/light/hid-sensor-prox.c8
-rw-r--r--drivers/iio/light/vcnl4035.c676
-rw-r--r--drivers/iio/magnetometer/Kconfig29
-rw-r--r--drivers/iio/magnetometer/Makefile4
-rw-r--r--drivers/iio/magnetometer/ak8975.c1
-rw-r--r--drivers/iio/magnetometer/hid-sensor-magn-3d.c8
-rw-r--r--drivers/iio/magnetometer/rm3100-core.c616
-rw-r--r--drivers/iio/magnetometer/rm3100-i2c.c54
-rw-r--r--drivers/iio/magnetometer/rm3100-spi.c64
-rw-r--r--drivers/iio/magnetometer/rm3100.h17
-rw-r--r--drivers/iio/magnetometer/st_magn.h1
-rw-r--r--drivers/iio/magnetometer/st_magn_buffer.c12
-rw-r--r--drivers/iio/magnetometer/st_magn_core.c11
-rw-r--r--drivers/iio/magnetometer/st_magn_i2c.c5
-rw-r--r--drivers/iio/magnetometer/st_magn_spi.c7
-rw-r--r--drivers/iio/orientation/hid-sensor-incl-3d.c8
-rw-r--r--drivers/iio/potentiometer/Kconfig12
-rw-r--r--drivers/iio/potentiometer/Makefile1
-rw-r--r--drivers/iio/potentiometer/mcp41010.c203
-rw-r--r--drivers/iio/potentiometer/mcp4131.c11
-rw-r--r--drivers/iio/potentiometer/tpl0102.c42
-rw-r--r--drivers/iio/pressure/hid-sensor-press.c8
-rw-r--r--drivers/iio/resolver/Kconfig10
-rw-r--r--drivers/iio/resolver/Makefile1
-rw-r--r--drivers/iio/resolver/ad2s90.c131
-rw-r--r--drivers/iio/temperature/hid-sensor-temperature.c3
76 files changed, 5354 insertions, 490 deletions
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 7993a67bd351..898839ca164a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -223,7 +223,7 @@ config IIO_ST_ACCEL_3AXIS
Say yes here to build support for STMicroelectronics accelerometers:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
- LNG2DM
+ LNG2DM, LIS3DE
This driver can also be built as a module. If so, these modules
will be created:
diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
index 41d97faf5013..38ff374a3ca4 100644
--- a/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -149,6 +149,7 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
+ s32 min;
struct hid_sensor_hub_device *hsdev =
accel_state->common_attributes.hsdev;
@@ -158,12 +159,14 @@ static int accel_3d_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&accel_state->common_attributes, true);
report_id = accel_state->accel[chan->scan_index].report_id;
+ min = accel_state->accel[chan->scan_index].logical_minimum;
address = accel_3d_addresses[chan->scan_index];
if (report_id >= 0)
*val = sensor_hub_input_attr_get_raw_value(
accel_state->common_attributes.hsdev,
hsdev->usage, address, report_id,
- SENSOR_HUB_SYNC);
+ SENSOR_HUB_SYNC,
+ min < 0);
else {
*val = 0;
hid_sensor_power_state(&accel_state->common_attributes,
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index af53a1084ee5..7096e577b23f 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -1489,8 +1489,11 @@ static const struct acpi_device_id kx_acpi_match[] = {
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXCJ9000", KXCJ91008},
+ {"KIOX0009", KXTJ21009},
{"KIOX000A", KXCJ91008},
+ {"KIOX010A", KXCJ91008}, /* KXCJ91008 inside the display of a 2-in-1 */
{"KXTJ1009", KXTJ21009},
+ {"KXJ2109", KXTJ21009},
{"SMO8500", KXCJ91008},
{ },
};
diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
index 2f931e4837e5..fd53258656ca 100644
--- a/drivers/iio/accel/st_accel.h
+++ b/drivers/iio/accel/st_accel.h
@@ -56,6 +56,7 @@ enum st_accel_type {
#define LNG2DM_ACCEL_DEV_NAME "lng2dm"
#define LIS2DW12_ACCEL_DEV_NAME "lis2dw12"
#define LIS3DHH_ACCEL_DEV_NAME "lis3dhh"
+#define LIS3DE_ACCEL_DEV_NAME "lis3de"
/**
* struct st_sensors_platform_data - default accel platform data
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index 3e6fd5a8ac5b..f7b471121508 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -103,6 +103,7 @@ static const struct st_sensor_settings st_accel_sensors_settings[] = {
[4] = LSM330DLC_ACCEL_DEV_NAME,
[5] = LSM303AGR_ACCEL_DEV_NAME,
[6] = LIS2DH12_ACCEL_DEV_NAME,
+ [7] = LIS3DE_ACCEL_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_accel_12bit_channels,
.odr = {
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index 2ca5d1f6ade0..de8ae4327094 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -98,6 +98,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lis2dw12",
.data = LIS2DW12_ACCEL_DEV_NAME,
},
+ {
+ .compatible = "st,lis3de",
+ .data = LIS3DE_ACCEL_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -135,6 +139,7 @@ static const struct i2c_device_id st_accel_id_table[] = {
{ LIS331DL_ACCEL_DEV_NAME },
{ LIS3LV02DL_ACCEL_DEV_NAME },
{ LIS2DW12_ACCEL_DEV_NAME },
+ { LIS3DE_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
index dcc9bd243a52..73bfb5d04e2b 100644
--- a/drivers/iio/accel/st_accel_spi.c
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -90,6 +90,10 @@ static const struct of_device_id st_accel_of_match[] = {
.compatible = "st,lis3dhh",
.data = LIS3DHH_ACCEL_DEV_NAME,
},
+ {
+ .compatible = "st,lis3de",
+ .data = LIS3DE_ACCEL_DEV_NAME,
+ },
{}
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -143,6 +147,7 @@ static const struct spi_device_id st_accel_id_table[] = {
{ LIS3LV02DL_ACCEL_DEV_NAME },
{ LIS2DW12_ACCEL_DEV_NAME },
{ LIS3DHH_ACCEL_DEV_NAME },
+ { LIS3DE_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_accel_id_table);
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index a52fea8749a9..7a3ca4ec0cb7 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -10,6 +10,17 @@ config AD_SIGMA_DELTA
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
+config AD7124
+ tristate "Analog Devices AD7124 and similar sigma-delta ADCs driver"
+ depends on SPI_MASTER
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7124-4 and AD7124-8
+ SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7124.
+
config AD7266
tristate "Analog Devices AD7265/AD7266 ADC driver"
depends on SPI_MASTER
@@ -116,6 +127,16 @@ config AD7923
To compile this driver as a module, choose M here: the
module will be called ad7923.
+config AD7949
+ tristate "Analog Devices AD7949 and similar ADCs driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices
+ AD7949, AD7682, AD7689 8 Channel ADCs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7949.
+
config AD799X
tristate "Analog Devices AD799x ADC driver"
depends on I2C
@@ -274,7 +295,7 @@ config EP93XX_ADC
config EXYNOS_ADC
tristate "Exynos ADC driver support"
- depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || (OF && COMPILE_TEST)
+ depends on ARCH_EXYNOS || ARCH_S3C24XX || ARCH_S3C64XX || ARCH_S5PV210 || (OF && COMPILE_TEST)
depends on HAS_IOMEM
help
Core support for the ADC block found in the Samsung EXYNOS series
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index a6e6a0b659e2..07df37f621bd 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -5,6 +5,7 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD_SIGMA_DELTA) += ad_sigma_delta.o
+obj-$(CONFIG_AD7124) += ad7124.o
obj-$(CONFIG_AD7266) += ad7266.o
obj-$(CONFIG_AD7291) += ad7291.o
obj-$(CONFIG_AD7298) += ad7298.o
@@ -14,6 +15,7 @@ obj-$(CONFIG_AD7766) += ad7766.o
obj-$(CONFIG_AD7791) += ad7791.o
obj-$(CONFIG_AD7793) += ad7793.o
obj-$(CONFIG_AD7887) += ad7887.o
+obj-$(CONFIG_AD7949) += ad7949.o
obj-$(CONFIG_AD799X) += ad799x.o
obj-$(CONFIG_ASPEED_ADC) += aspeed_adc.o
obj-$(CONFIG_AT91_ADC) += at91_adc.o
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
new file mode 100644
index 000000000000..7d5e5311d8de
--- /dev/null
+++ b/drivers/iio/adc/ad7124.c
@@ -0,0 +1,684 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * AD7124 SPI ADC driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/kernel.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>
+#include <linux/iio/sysfs.h>
+
+/* AD7124 registers */
+#define AD7124_COMMS 0x00
+#define AD7124_STATUS 0x00
+#define AD7124_ADC_CONTROL 0x01
+#define AD7124_DATA 0x02
+#define AD7124_IO_CONTROL_1 0x03
+#define AD7124_IO_CONTROL_2 0x04
+#define AD7124_ID 0x05
+#define AD7124_ERROR 0x06
+#define AD7124_ERROR_EN 0x07
+#define AD7124_MCLK_COUNT 0x08
+#define AD7124_CHANNEL(x) (0x09 + (x))
+#define AD7124_CONFIG(x) (0x19 + (x))
+#define AD7124_FILTER(x) (0x21 + (x))
+#define AD7124_OFFSET(x) (0x29 + (x))
+#define AD7124_GAIN(x) (0x31 + (x))
+
+/* AD7124_STATUS */
+#define AD7124_STATUS_POR_FLAG_MSK BIT(4)
+
+/* AD7124_ADC_CONTROL */
+#define AD7124_ADC_CTRL_PWR_MSK GENMASK(7, 6)
+#define AD7124_ADC_CTRL_PWR(x) FIELD_PREP(AD7124_ADC_CTRL_PWR_MSK, x)
+#define AD7124_ADC_CTRL_MODE_MSK GENMASK(5, 2)
+#define AD7124_ADC_CTRL_MODE(x) FIELD_PREP(AD7124_ADC_CTRL_MODE_MSK, x)
+
+/* AD7124_CHANNEL_X */
+#define AD7124_CHANNEL_EN_MSK BIT(15)
+#define AD7124_CHANNEL_EN(x) FIELD_PREP(AD7124_CHANNEL_EN_MSK, x)
+#define AD7124_CHANNEL_SETUP_MSK GENMASK(14, 12)
+#define AD7124_CHANNEL_SETUP(x) FIELD_PREP(AD7124_CHANNEL_SETUP_MSK, x)
+#define AD7124_CHANNEL_AINP_MSK GENMASK(9, 5)
+#define AD7124_CHANNEL_AINP(x) FIELD_PREP(AD7124_CHANNEL_AINP_MSK, x)
+#define AD7124_CHANNEL_AINM_MSK GENMASK(4, 0)
+#define AD7124_CHANNEL_AINM(x) FIELD_PREP(AD7124_CHANNEL_AINM_MSK, x)
+
+/* AD7124_CONFIG_X */
+#define AD7124_CONFIG_BIPOLAR_MSK BIT(11)
+#define AD7124_CONFIG_BIPOLAR(x) FIELD_PREP(AD7124_CONFIG_BIPOLAR_MSK, x)
+#define AD7124_CONFIG_REF_SEL_MSK GENMASK(4, 3)
+#define AD7124_CONFIG_REF_SEL(x) FIELD_PREP(AD7124_CONFIG_REF_SEL_MSK, x)
+#define AD7124_CONFIG_PGA_MSK GENMASK(2, 0)
+#define AD7124_CONFIG_PGA(x) FIELD_PREP(AD7124_CONFIG_PGA_MSK, x)
+
+/* AD7124_FILTER_X */
+#define AD7124_FILTER_FS_MSK GENMASK(10, 0)
+#define AD7124_FILTER_FS(x) FIELD_PREP(AD7124_FILTER_FS_MSK, x)
+
+enum ad7124_ids {
+ ID_AD7124_4,
+ ID_AD7124_8,
+};
+
+enum ad7124_ref_sel {
+ AD7124_REFIN1,
+ AD7124_REFIN2,
+ AD7124_INT_REF,
+ AD7124_AVDD_REF,
+};
+
+enum ad7124_power_mode {
+ AD7124_LOW_POWER,
+ AD7124_MID_POWER,
+ AD7124_FULL_POWER,
+};
+
+static const unsigned int ad7124_gain[8] = {
+ 1, 2, 4, 8, 16, 32, 64, 128
+};
+
+static const int ad7124_master_clk_freq_hz[3] = {
+ [AD7124_LOW_POWER] = 76800,
+ [AD7124_MID_POWER] = 153600,
+ [AD7124_FULL_POWER] = 614400,
+};
+
+static const char * const ad7124_ref_names[] = {
+ [AD7124_REFIN1] = "refin1",
+ [AD7124_REFIN2] = "refin2",
+ [AD7124_INT_REF] = "int",
+ [AD7124_AVDD_REF] = "avdd",
+};
+
+struct ad7124_chip_info {
+ unsigned int num_inputs;
+};
+
+struct ad7124_channel_config {
+ enum ad7124_ref_sel refsel;
+ bool bipolar;
+ unsigned int ain;
+ unsigned int vref_mv;
+ unsigned int pga_bits;
+ unsigned int odr;
+};
+
+struct ad7124_state {
+ const struct ad7124_chip_info *chip_info;
+ struct ad_sigma_delta sd;
+ struct ad7124_channel_config channel_config[4];
+ struct regulator *vref[4];
+ struct clk *mclk;
+ unsigned int adc_control;
+ unsigned int num_channels;
+};
+
+static const struct iio_chan_spec ad7124_channel_template = {
+ .type = IIO_VOLTAGE,
+ .indexed = 1,
+ .differential = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+};
+
+static struct ad7124_chip_info ad7124_chip_info_tbl[] = {
+ [ID_AD7124_4] = {
+ .num_inputs = 8,
+ },
+ [ID_AD7124_8] = {
+ .num_inputs = 16,
+ },
+};
+
+static int ad7124_find_closest_match(const int *array,
+ unsigned int size, int val)
+{
+ int i, idx;
+ unsigned int diff_new, diff_old;
+
+ diff_old = U32_MAX;
+ idx = 0;
+
+ for (i = 0; i < size; i++) {
+ diff_new = abs(val - array[i]);
+ if (diff_new < diff_old) {
+ diff_old = diff_new;
+ idx = i;
+ }
+ }
+
+ return idx;
+}
+
+static int ad7124_spi_write_mask(struct ad7124_state *st,
+ unsigned int addr,
+ unsigned long mask,
+ unsigned int val,
+ unsigned int bytes)
+{
+ unsigned int readval;
+ int ret;
+
+ ret = ad_sd_read_reg(&st->sd, addr, bytes, &readval);
+ if (ret < 0)
+ return ret;
+
+ readval &= ~mask;
+ readval |= val;
+
+ return ad_sd_write_reg(&st->sd, addr, bytes, readval);
+}
+
+static int ad7124_set_mode(struct ad_sigma_delta *sd,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
+
+ st->adc_control &= ~AD7124_ADC_CTRL_MODE_MSK;
+ st->adc_control |= AD7124_ADC_CTRL_MODE(mode);
+
+ return ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
+}
+
+static int ad7124_set_channel(struct ad_sigma_delta *sd, unsigned int channel)
+{
+ struct ad7124_state *st = container_of(sd, struct ad7124_state, sd);
+ unsigned int val;
+
+ val = st->channel_config[channel].ain | AD7124_CHANNEL_EN(1) |
+ AD7124_CHANNEL_SETUP(channel);
+
+ return ad_sd_write_reg(&st->sd, AD7124_CHANNEL(channel), 2, val);
+}
+
+static const struct ad_sigma_delta_info ad7124_sigma_delta_info = {
+ .set_channel = ad7124_set_channel,
+ .set_mode = ad7124_set_mode,
+ .has_registers = true,
+ .addr_shift = 0,
+ .read_mask = BIT(6),
+ .data_reg = AD7124_DATA,
+};
+
+static int ad7124_set_channel_odr(struct ad7124_state *st,
+ unsigned int channel,
+ unsigned int odr)
+{
+ unsigned int fclk, odr_sel_bits;
+ int ret;
+
+ fclk = clk_get_rate(st->mclk);
+ /*
+ * FS[10:0] = fCLK / (fADC x 32) where:
+ * fADC is the output data rate
+ * fCLK is the master clock frequency
+ * FS[10:0] are the bits in the filter register
+ * FS[10:0] can have a value from 1 to 2047
+ */
+ odr_sel_bits = DIV_ROUND_CLOSEST(fclk, odr * 32);
+ if (odr_sel_bits < 1)
+ odr_sel_bits = 1;
+ else if (odr_sel_bits > 2047)
+ odr_sel_bits = 2047;
+
+ ret = ad7124_spi_write_mask(st, AD7124_FILTER(channel),
+ AD7124_FILTER_FS_MSK,
+ AD7124_FILTER_FS(odr_sel_bits), 3);
+ if (ret < 0)
+ return ret;
+ /* fADC = fCLK / (FS[10:0] x 32) */
+ st->channel_config[channel].odr =
+ DIV_ROUND_CLOSEST(fclk, odr_sel_bits * 32);
+
+ return 0;
+}
+
+static int ad7124_set_channel_gain(struct ad7124_state *st,
+ unsigned int channel,
+ unsigned int gain)
+{
+ unsigned int res;
+ int ret;
+
+ res = ad7124_find_closest_match(ad7124_gain,
+ ARRAY_SIZE(ad7124_gain), gain);
+ ret = ad7124_spi_write_mask(st, AD7124_CONFIG(channel),
+ AD7124_CONFIG_PGA_MSK,
+ AD7124_CONFIG_PGA(res), 2);
+ if (ret < 0)
+ return ret;
+
+ st->channel_config[channel].pga_bits = res;
+
+ return 0;
+}
+
+static int ad7124_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ int idx, ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ if (ret < 0)
+ return ret;
+
+ /* After the conversion is performed, disable the channel */
+ ret = ad_sd_write_reg(&st->sd,
+ AD7124_CHANNEL(chan->address), 2,
+ st->channel_config[chan->address].ain |
+ AD7124_CHANNEL_EN(0));
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ idx = st->channel_config[chan->address].pga_bits;
+ *val = st->channel_config[chan->address].vref_mv;
+ if (st->channel_config[chan->address].bipolar)
+ *val2 = chan->scan_type.realbits - 1 + idx;
+ else
+ *val2 = chan->scan_type.realbits + idx;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ if (st->channel_config[chan->address].bipolar)
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ else
+ *val = 0;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->channel_config[chan->address].odr;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ad7124_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ unsigned int res, gain, full_scale, vref;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val2 != 0)
+ return -EINVAL;
+
+ return ad7124_set_channel_odr(st, chan->address, val);
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+
+ if (st->channel_config[chan->address].bipolar)
+ full_scale = 1 << (chan->scan_type.realbits - 1);
+ else
+ full_scale = 1 << chan->scan_type.realbits;
+
+ vref = st->channel_config[chan->address].vref_mv * 1000000LL;
+ res = DIV_ROUND_CLOSEST(vref, full_scale);
+ gain = DIV_ROUND_CLOSEST(res, val2);
+
+ return ad7124_set_channel_gain(st, chan->address, gain);
+ default:
+ return -EINVAL;
+ }
+}
+
+static IIO_CONST_ATTR(in_voltage_scale_available,
+ "0.000001164 0.000002328 0.000004656 0.000009313 0.000018626 0.000037252 0.000074505 0.000149011 0.000298023");
+
+static struct attribute *ad7124_attributes[] = {
+ &iio_const_attr_in_voltage_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7124_attrs_group = {
+ .attrs = ad7124_attributes,
+};
+
+static const struct iio_info ad7124_info = {
+ .read_raw = ad7124_read_raw,
+ .write_raw = ad7124_write_raw,
+ .validate_trigger = ad_sd_validate_trigger,
+ .attrs = &ad7124_attrs_group,
+};
+
+static int ad7124_soft_reset(struct ad7124_state *st)
+{
+ unsigned int readval, timeout;
+ int ret;
+
+ ret = ad_sd_reset(&st->sd, 64);
+ if (ret < 0)
+ return ret;
+
+ timeout = 100;
+ do {
+ ret = ad_sd_read_reg(&st->sd, AD7124_STATUS, 1, &readval);
+ if (ret < 0)
+ return ret;
+
+ if (!(readval & AD7124_STATUS_POR_FLAG_MSK))
+ return 0;
+
+ /* The AD7124 requires typically 2ms to power up and settle */
+ usleep_range(100, 2000);
+ } while (--timeout);
+
+ dev_err(&st->sd.spi->dev, "Soft reset failed\n");
+
+ return -EIO;
+}
+
+static int ad7124_init_channel_vref(struct ad7124_state *st,
+ unsigned int channel_number)
+{
+ unsigned int refsel = st->channel_config[channel_number].refsel;
+
+ switch (refsel) {
+ case AD7124_REFIN1:
+ case AD7124_REFIN2:
+ case AD7124_AVDD_REF:
+ if (IS_ERR(st->vref[refsel])) {
+ dev_err(&st->sd.spi->dev,
+ "Error, trying to use external voltage reference without a %s regulator.\n",
+ ad7124_ref_names[refsel]);
+ return PTR_ERR(st->vref[refsel]);
+ }
+ st->channel_config[channel_number].vref_mv =
+ regulator_get_voltage(st->vref[refsel]);
+ /* Conversion from uV to mV */
+ st->channel_config[channel_number].vref_mv /= 1000;
+ break;
+ case AD7124_INT_REF:
+ st->channel_config[channel_number].vref_mv = 2500;
+ break;
+ default:
+ dev_err(&st->sd.spi->dev, "Invalid reference %d\n", refsel);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ad7124_of_parse_channel_config(struct iio_dev *indio_dev,
+ struct device_node *np)
+{
+ struct ad7124_state *st = iio_priv(indio_dev);
+ struct device_node *child;
+ struct iio_chan_spec *chan;
+ unsigned int ain[2], channel = 0, tmp;
+ int ret;
+
+ st->num_channels = of_get_available_child_count(np);
+ if (!st->num_channels) {
+ dev_err(indio_dev->dev.parent, "no channel children\n");
+ return -ENODEV;
+ }
+
+ chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
+ sizeof(*chan), GFP_KERNEL);
+ if (!chan)
+ return -ENOMEM;
+
+ indio_dev->channels = chan;
+ indio_dev->num_channels = st->num_channels;
+
+ for_each_available_child_of_node(np, child) {
+ ret = of_property_read_u32(child, "reg", &channel);
+ if (ret)
+ goto err;
+
+ ret = of_property_read_u32_array(child, "diff-channels",
+ ain, 2);
+ if (ret)
+ goto err;
+
+ if (ain[0] >= st->chip_info->num_inputs ||
+ ain[1] >= st->chip_info->num_inputs) {
+ dev_err(indio_dev->dev.parent,
+ "Input pin number out of range.\n");
+ ret = -EINVAL;
+ goto err;
+ }
+ st->channel_config[channel].ain = AD7124_CHANNEL_AINP(ain[0]) |
+ AD7124_CHANNEL_AINM(ain[1]);
+ st->channel_config[channel].bipolar =
+ of_property_read_bool(child, "bipolar");
+
+ ret = of_property_read_u32(child, "adi,reference-select", &tmp);
+ if (ret)
+ st->channel_config[channel].refsel = AD7124_INT_REF;
+ else
+ st->channel_config[channel].refsel = tmp;
+
+ *chan = ad7124_channel_template;
+ chan->address = channel;
+ chan->scan_index = channel;
+ chan->channel = ain[0];
+ chan->channel2 = ain[1];
+
+ chan++;
+ }
+
+ return 0;
+err:
+ of_node_put(child);
+
+ return ret;
+}
+
+static int ad7124_setup(struct ad7124_state *st)
+{
+ unsigned int val, fclk, power_mode;
+ int i, ret;
+
+ fclk = clk_get_rate(st->mclk);
+ if (!fclk)
+ return -EINVAL;
+
+ /* The power mode changes the master clock frequency */
+ power_mode = ad7124_find_closest_match(ad7124_master_clk_freq_hz,
+ ARRAY_SIZE(ad7124_master_clk_freq_hz),
+ fclk);
+ if (fclk != ad7124_master_clk_freq_hz[power_mode]) {
+ ret = clk_set_rate(st->mclk, fclk);
+ if (ret)
+ return ret;
+ }
+
+ /* Set the power mode */
+ st->adc_control &= ~AD7124_ADC_CTRL_PWR_MSK;
+ st->adc_control |= AD7124_ADC_CTRL_PWR(power_mode);
+ ret = ad_sd_write_reg(&st->sd, AD7124_ADC_CONTROL, 2, st->adc_control);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < st->num_channels; i++) {
+ val = st->channel_config[i].ain | AD7124_CHANNEL_SETUP(i);
+ ret = ad_sd_write_reg(&st->sd, AD7124_CHANNEL(i), 2, val);
+ if (ret < 0)
+ return ret;
+
+ ret = ad7124_init_channel_vref(st, i);
+ if (ret < 0)
+ return ret;
+
+ val = AD7124_CONFIG_BIPOLAR(st->channel_config[i].bipolar) |
+ AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel);
+ ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(i), 2, val);
+ if (ret < 0)
+ return ret;
+ /*
+ * 9.38 SPS is the minimum output data rate supported
+ * regardless of the selected power mode. Round it up to 10 and
+ * set all the enabled channels to this default value.
+ */
+ ret = ad7124_set_channel_odr(st, i, 10);
+ }
+
+ return ret;
+}
+
+static int ad7124_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id;
+ struct ad7124_state *st;
+ struct iio_dev *indio_dev;
+ int i, ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+
+ id = spi_get_device_id(spi);
+ st->chip_info = &ad7124_chip_info_tbl[id->driver_data];
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7124_sigma_delta_info);
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &ad7124_info;
+
+ ret = ad7124_of_parse_channel_config(indio_dev, spi->dev.of_node);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; i < ARRAY_SIZE(st->vref); i++) {
+ if (i == AD7124_INT_REF)
+ continue;
+
+ st->vref[i] = devm_regulator_get_optional(&spi->dev,
+ ad7124_ref_names[i]);
+ if (PTR_ERR(st->vref[i]) == -ENODEV)
+ continue;
+ else if (IS_ERR(st->vref[i]))
+ return PTR_ERR(st->vref[i]);
+
+ ret = regulator_enable(st->vref[i]);
+ if (ret)
+ return ret;
+ }
+
+ st->mclk = devm_clk_get(&spi->dev, "mclk");
+ if (IS_ERR(st->mclk)) {
+ ret = PTR_ERR(st->mclk);
+ goto error_regulator_disable;
+ }
+
+ ret = clk_prepare_enable(st->mclk);
+ if (ret < 0)
+ goto error_regulator_disable;
+
+ ret = ad7124_soft_reset(st);
+ if (ret < 0)
+ goto error_clk_disable_unprepare;
+
+ ret = ad7124_setup(st);
+ if (ret < 0)
+ goto error_clk_disable_unprepare;
+
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+ if (ret < 0)
+ goto error_clk_disable_unprepare;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register iio device\n");
+ goto error_remove_trigger;
+ }
+
+ return 0;
+
+error_remove_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_clk_disable_unprepare:
+ clk_disable_unprepare(st->mclk);
+error_regulator_disable:
+ for (i = ARRAY_SIZE(st->vref) - 1; i >= 0; i--) {
+ if (!IS_ERR_OR_NULL(st->vref[i]))
+ regulator_disable(st->vref[i]);
+ }
+
+ return ret;
+}
+
+static int ad7124_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7124_state *st = iio_priv(indio_dev);
+ int i;
+
+ iio_device_unregister(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+ clk_disable_unprepare(st->mclk);
+
+ for (i = ARRAY_SIZE(st->vref) - 1; i >= 0; i--) {
+ if (!IS_ERR_OR_NULL(st->vref[i]))
+ regulator_disable(st->vref[i]);
+ }
+
+ return 0;
+}
+
+static const struct spi_device_id ad7124_id_table[] = {
+ { "ad7124-4", ID_AD7124_4 },
+ { "ad7124-8", ID_AD7124_8 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7124_id_table);
+
+static const struct of_device_id ad7124_of_match[] = {
+ { .compatible = "adi,ad7124-4" },
+ { .compatible = "adi,ad7124-8" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7124_of_match);
+
+static struct spi_driver ad71124_driver = {
+ .driver = {
+ .name = "ad7124",
+ .of_match_table = ad7124_of_match,
+ },
+ .probe = ad7124_probe,
+ .remove = ad7124_remove,
+ .id_table = ad7124_id_table,
+};
+module_spi_driver(ad71124_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7124 SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c
new file mode 100644
index 000000000000..ac0ffff6c5ae
--- /dev/null
+++ b/drivers/iio/adc/ad7949.c
@@ -0,0 +1,347 @@
+// SPDX-License-Identifier: GPL-2.0
+/* ad7949.c - Analog Devices ADC driver 14/16 bits 4/8 channels
+ *
+ * Copyright (C) 2018 CMC NV
+ *
+ * http://www.analog.com/media/en/technical-documentation/data-sheets/AD7949.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define AD7949_MASK_CHANNEL_SEL GENMASK(9, 7)
+#define AD7949_MASK_TOTAL GENMASK(13, 0)
+#define AD7949_OFFSET_CHANNEL_SEL 7
+#define AD7949_CFG_READ_BACK 0x1
+#define AD7949_CFG_REG_SIZE_BITS 14
+
+enum {
+ ID_AD7949 = 0,
+ ID_AD7682,
+ ID_AD7689,
+};
+
+struct ad7949_adc_spec {
+ u8 num_channels;
+ u8 resolution;
+};
+
+static const struct ad7949_adc_spec ad7949_adc_spec[] = {
+ [ID_AD7949] = { .num_channels = 8, .resolution = 14 },
+ [ID_AD7682] = { .num_channels = 4, .resolution = 16 },
+ [ID_AD7689] = { .num_channels = 8, .resolution = 16 },
+};
+
+/**
+ * struct ad7949_adc_chip - AD ADC chip
+ * @lock: protects write sequences
+ * @vref: regulator generating Vref
+ * @iio_dev: reference to iio structure
+ * @spi: reference to spi structure
+ * @resolution: resolution of the chip
+ * @cfg: copy of the configuration register
+ * @current_channel: current channel in use
+ * @buffer: buffer to send / receive data to / from device
+ */
+struct ad7949_adc_chip {
+ struct mutex lock;
+ struct regulator *vref;
+ struct iio_dev *indio_dev;
+ struct spi_device *spi;
+ u8 resolution;
+ u16 cfg;
+ unsigned int current_channel;
+ u32 buffer ____cacheline_aligned;
+};
+
+static bool ad7949_spi_cfg_is_read_back(struct ad7949_adc_chip *ad7949_adc)
+{
+ if (!(ad7949_adc->cfg & AD7949_CFG_READ_BACK))
+ return true;
+
+ return false;
+}
+
+static int ad7949_spi_bits_per_word(struct ad7949_adc_chip *ad7949_adc)
+{
+ int ret = ad7949_adc->resolution;
+
+ if (ad7949_spi_cfg_is_read_back(ad7949_adc))
+ ret += AD7949_CFG_REG_SIZE_BITS;
+
+ return ret;
+}
+
+static int ad7949_spi_write_cfg(struct ad7949_adc_chip *ad7949_adc, u16 val,
+ u16 mask)
+{
+ int ret;
+ int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
+ int shift = bits_per_word - AD7949_CFG_REG_SIZE_BITS;
+ struct spi_message msg;
+ struct spi_transfer tx[] = {
+ {
+ .tx_buf = &ad7949_adc->buffer,
+ .len = 4,
+ .bits_per_word = bits_per_word,
+ },
+ };
+
+ ad7949_adc->cfg = (val & mask) | (ad7949_adc->cfg & ~mask);
+ ad7949_adc->buffer = ad7949_adc->cfg << shift;
+ spi_message_init_with_transfers(&msg, tx, 1);
+ ret = spi_sync(ad7949_adc->spi, &msg);
+
+ /*
+ * This delay is to avoid a new request before the required time to
+ * send a new command to the device
+ */
+ udelay(2);
+ return ret;
+}
+
+static int ad7949_spi_read_channel(struct ad7949_adc_chip *ad7949_adc, int *val,
+ unsigned int channel)
+{
+ int ret;
+ int bits_per_word = ad7949_spi_bits_per_word(ad7949_adc);
+ int mask = GENMASK(ad7949_adc->resolution, 0);
+ struct spi_message msg;
+ struct spi_transfer tx[] = {
+ {
+ .rx_buf = &ad7949_adc->buffer,
+ .len = 4,
+ .bits_per_word = bits_per_word,
+ },
+ };
+
+ ret = ad7949_spi_write_cfg(ad7949_adc,
+ channel << AD7949_OFFSET_CHANNEL_SEL,
+ AD7949_MASK_CHANNEL_SEL);
+ if (ret)
+ return ret;
+
+ ad7949_adc->buffer = 0;
+ spi_message_init_with_transfers(&msg, tx, 1);
+ ret = spi_sync(ad7949_adc->spi, &msg);
+ if (ret)
+ return ret;
+
+ /*
+ * This delay is to avoid a new request before the required time to
+ * send a new command to the device
+ */
+ udelay(2);
+
+ ad7949_adc->current_channel = channel;
+
+ if (ad7949_spi_cfg_is_read_back(ad7949_adc))
+ *val = (ad7949_adc->buffer >> AD7949_CFG_REG_SIZE_BITS) & mask;
+ else
+ *val = ad7949_adc->buffer & mask;
+
+ return 0;
+}
+
+#define AD7949_ADC_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec ad7949_adc_channels[] = {
+ AD7949_ADC_CHANNEL(0),
+ AD7949_ADC_CHANNEL(1),
+ AD7949_ADC_CHANNEL(2),
+ AD7949_ADC_CHANNEL(3),
+ AD7949_ADC_CHANNEL(4),
+ AD7949_ADC_CHANNEL(5),
+ AD7949_ADC_CHANNEL(6),
+ AD7949_ADC_CHANNEL(7),
+};
+
+static int ad7949_spi_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
+ int ret;
+
+ if (!val)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&ad7949_adc->lock);
+ ret = ad7949_spi_read_channel(ad7949_adc, val, chan->channel);
+ mutex_unlock(&ad7949_adc->lock);
+
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(ad7949_adc->vref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 5000;
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7949_spi_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg, unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
+ int ret = 0;
+
+ if (readval)
+ *readval = ad7949_adc->cfg;
+ else
+ ret = ad7949_spi_write_cfg(ad7949_adc,
+ writeval & AD7949_MASK_TOTAL, AD7949_MASK_TOTAL);
+
+ return ret;
+}
+
+static const struct iio_info ad7949_spi_info = {
+ .read_raw = ad7949_spi_read_raw,
+ .debugfs_reg_access = ad7949_spi_reg_access,
+};
+
+static int ad7949_spi_init(struct ad7949_adc_chip *ad7949_adc)
+{
+ int ret;
+ int val;
+
+ /* Sequencer disabled, CFG readback disabled, IN0 as default channel */
+ ad7949_adc->current_channel = 0;
+ ret = ad7949_spi_write_cfg(ad7949_adc, 0x3C79, AD7949_MASK_TOTAL);
+
+ /*
+ * Do two dummy conversions to apply the first configuration setting.
+ * Required only after the start up of the device.
+ */
+ ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel);
+ ad7949_spi_read_channel(ad7949_adc, &val, ad7949_adc->current_channel);
+
+ return ret;
+}
+
+static int ad7949_spi_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ const struct ad7949_adc_spec *spec;
+ struct ad7949_adc_chip *ad7949_adc;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*ad7949_adc));
+ if (!indio_dev) {
+ dev_err(dev, "can not allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = dev->of_node;
+ indio_dev->info = &ad7949_spi_info;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ad7949_adc_channels;
+ spi_set_drvdata(spi, indio_dev);
+
+ ad7949_adc = iio_priv(indio_dev);
+ ad7949_adc->indio_dev = indio_dev;
+ ad7949_adc->spi = spi;
+
+ spec = &ad7949_adc_spec[spi_get_device_id(spi)->driver_data];
+ indio_dev->num_channels = spec->num_channels;
+ ad7949_adc->resolution = spec->resolution;
+
+ ad7949_adc->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(ad7949_adc->vref)) {
+ dev_err(dev, "fail to request regulator\n");
+ return PTR_ERR(ad7949_adc->vref);
+ }
+
+ ret = regulator_enable(ad7949_adc->vref);
+ if (ret < 0) {
+ dev_err(dev, "fail to enable regulator\n");
+ return ret;
+ }
+
+ mutex_init(&ad7949_adc->lock);
+
+ ret = ad7949_spi_init(ad7949_adc);
+ if (ret) {
+ dev_err(dev, "enable to init this device: %d\n", ret);
+ goto err;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "fail to register iio device: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ mutex_destroy(&ad7949_adc->lock);
+ regulator_disable(ad7949_adc->vref);
+
+ return ret;
+}
+
+static int ad7949_spi_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7949_adc_chip *ad7949_adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ mutex_destroy(&ad7949_adc->lock);
+ regulator_disable(ad7949_adc->vref);
+
+ return 0;
+}
+
+static const struct of_device_id ad7949_spi_of_id[] = {
+ { .compatible = "adi,ad7949" },
+ { .compatible = "adi,ad7682" },
+ { .compatible = "adi,ad7689" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ad7949_spi_of_id);
+
+static const struct spi_device_id ad7949_spi_id[] = {
+ { "ad7949", ID_AD7949 },
+ { "ad7682", ID_AD7682 },
+ { "ad7689", ID_AD7689 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ad7949_spi_id);
+
+static struct spi_driver ad7949_spi_driver = {
+ .driver = {
+ .name = "ad7949",
+ .of_match_table = ad7949_spi_of_id,
+ },
+ .probe = ad7949_spi_probe,
+ .remove = ad7949_spi_remove,
+ .id_table = ad7949_spi_id,
+};
+module_spi_driver(ad7949_spi_driver);
+
+MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@essensium.com>");
+MODULE_DESCRIPTION("Analog Devices 14/16-bit 8-channel ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index fc9510716ac7..ff5f2da2e1b1 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -278,6 +278,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
{
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
unsigned int sample, raw_sample;
+ unsigned int data_reg;
int ret = 0;
if (iio_buffer_enabled(indio_dev))
@@ -305,7 +306,12 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev,
if (ret < 0)
goto out;
- ret = ad_sd_read_reg(sigma_delta, AD_SD_REG_DATA,
+ if (sigma_delta->info->data_reg != 0)
+ data_reg = sigma_delta->info->data_reg;
+ else
+ data_reg = AD_SD_REG_DATA;
+
+ ret = ad_sd_read_reg(sigma_delta, data_reg,
DIV_ROUND_UP(chan->scan_type.realbits + chan->scan_type.shift, 8),
&raw_sample);
@@ -392,6 +398,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
struct iio_dev *indio_dev = pf->indio_dev;
struct ad_sigma_delta *sigma_delta = iio_device_get_drvdata(indio_dev);
unsigned int reg_size;
+ unsigned int data_reg;
uint8_t data[16];
int ret;
@@ -401,18 +408,23 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p)
indio_dev->channels[0].scan_type.shift;
reg_size = DIV_ROUND_UP(reg_size, 8);
+ if (sigma_delta->info->data_reg != 0)
+ data_reg = sigma_delta->info->data_reg;
+ else
+ data_reg = AD_SD_REG_DATA;
+
switch (reg_size) {
case 4:
case 2:
case 1:
- ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
- reg_size, &data[0]);
+ ret = ad_sd_read_reg_raw(sigma_delta, data_reg, reg_size,
+ &data[0]);
break;
case 3:
/* We store 24 bit samples in a 32 bit word. Keep the upper
* byte set to zero. */
- ret = ad_sd_read_reg_raw(sigma_delta, AD_SD_REG_DATA,
- reg_size, &data[1]);
+ ret = ad_sd_read_reg_raw(sigma_delta, data_reg, reg_size,
+ &data[1]);
break;
}
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index f10443f92e4c..fa2d2b5767f3 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -115,6 +115,7 @@
#define MAX_ADC_V2_CHANNELS 10
#define MAX_ADC_V1_CHANNELS 8
#define MAX_EXYNOS3250_ADC_CHANNELS 2
+#define MAX_S5PV210_ADC_CHANNELS 10
/* Bit definitions common for ADC_V1 and ADC_V2 */
#define ADC_CON_EN_START (1u << 0)
@@ -282,6 +283,16 @@ static const struct exynos_adc_data exynos_adc_v1_data = {
.start_conv = exynos_adc_v1_start_conv,
};
+static const struct exynos_adc_data exynos_adc_s5pv210_data = {
+ .num_channels = MAX_S5PV210_ADC_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+
+ .init_hw = exynos_adc_v1_init_hw,
+ .exit_hw = exynos_adc_v1_exit_hw,
+ .clear_irq = exynos_adc_v1_clear_irq,
+ .start_conv = exynos_adc_v1_start_conv,
+};
+
static void exynos_adc_s3c2416_start_conv(struct exynos_adc *info,
unsigned long addr)
{
@@ -479,6 +490,9 @@ static const struct of_device_id exynos_adc_match[] = {
.compatible = "samsung,s3c6410-adc",
.data = &exynos_adc_s3c64xx_data,
}, {
+ .compatible = "samsung,s5pv210-adc",
+ .data = &exynos_adc_s5pv210_data,
+ }, {
.compatible = "samsung,exynos-adc-v1",
.data = &exynos_adc_v1_data,
}, {
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index d1239624187d..bdd7cba6f6b0 100644
--- a/drivers/iio/adc/ina2xx-adc.c
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -250,6 +250,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
*val2 = chip->shunt_resistor_uohm;
return IIO_VAL_FRACTIONAL;
}
+ return -EINVAL;
case IIO_CHAN_INFO_HARDWAREGAIN:
switch (chan->address) {
@@ -262,6 +263,7 @@ static int ina2xx_read_raw(struct iio_dev *indio_dev,
*val = chip->range_vbus == 32 ? 1 : 2;
return IIO_VAL_INT;
}
+ return -EINVAL;
}
return -EINVAL;
diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c
index af59ab2e650c..3440539cfdba 100644
--- a/drivers/iio/adc/max11100.c
+++ b/drivers/iio/adc/max11100.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* iio/adc/max11100.c
* Maxim max11100 ADC Driver with IIO interface
*
* Copyright (C) 2016-17 Renesas Electronics Corporation
* Copyright (C) 2016-17 Jacopo Mondi
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
#include <linux/delay.h>
#include <linux/kernel.h>
diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c
index 643a4e66eb80..917223d5ff5b 100644
--- a/drivers/iio/adc/max9611.c
+++ b/drivers/iio/adc/max9611.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* iio/adc/max9611.c
*
@@ -5,10 +6,6 @@
* 12-bit ADC interface.
*
* Copyright (C) 2017 Jacopo Mondi
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
/*
diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c
index 028ccd218f82..729becb2d3d9 100644
--- a/drivers/iio/adc/meson_saradc.c
+++ b/drivers/iio/adc/meson_saradc.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/iio/iio.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
@@ -165,6 +166,14 @@
#define MESON_SAR_ADC_MAX_FIFO_SIZE 32
#define MESON_SAR_ADC_TIMEOUT 100 /* ms */
+#define MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL 6
+#define MESON_SAR_ADC_TEMP_OFFSET 27
+
+/* temperature sensor calibration information in eFuse */
+#define MESON_SAR_ADC_EFUSE_BYTES 4
+#define MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL GENMASK(6, 0)
+#define MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED BIT(7)
+
/* for use with IIO_VAL_INT_PLUS_MICRO */
#define MILLION 1000000
@@ -175,16 +184,25 @@
.address = _chan, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
- BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
BIT(IIO_CHAN_INFO_CALIBSCALE), \
.datasheet_name = "SAR_ADC_CH"#_chan, \
}
-/*
- * TODO: the hardware supports IIO_TEMP for channel 6 as well which is
- * currently not supported by this driver.
- */
+#define MESON_SAR_ADC_TEMP_CHAN(_chan) { \
+ .type = IIO_TEMP, \
+ .channel = _chan, \
+ .address = MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_AVERAGE_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_CALIBBIAS) | \
+ BIT(IIO_CHAN_INFO_CALIBSCALE), \
+ .datasheet_name = "TEMP_SENSOR", \
+}
+
static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
MESON_SAR_ADC_CHAN(0),
MESON_SAR_ADC_CHAN(1),
@@ -197,6 +215,19 @@ static const struct iio_chan_spec meson_sar_adc_iio_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(8),
};
+static const struct iio_chan_spec meson_sar_adc_and_temp_iio_channels[] = {
+ MESON_SAR_ADC_CHAN(0),
+ MESON_SAR_ADC_CHAN(1),
+ MESON_SAR_ADC_CHAN(2),
+ MESON_SAR_ADC_CHAN(3),
+ MESON_SAR_ADC_CHAN(4),
+ MESON_SAR_ADC_CHAN(5),
+ MESON_SAR_ADC_CHAN(6),
+ MESON_SAR_ADC_CHAN(7),
+ MESON_SAR_ADC_TEMP_CHAN(8),
+ IIO_CHAN_SOFT_TIMESTAMP(9),
+};
+
enum meson_sar_adc_avg_mode {
NO_AVERAGING = 0x0,
MEAN_AVERAGING = 0x1,
@@ -225,6 +256,9 @@ struct meson_sar_adc_param {
u32 bandgap_reg;
unsigned int resolution;
const struct regmap_config *regmap_config;
+ u8 temperature_trimming_bits;
+ unsigned int temperature_multiplier;
+ unsigned int temperature_divider;
};
struct meson_sar_adc_data {
@@ -246,6 +280,9 @@ struct meson_sar_adc_priv {
struct completion done;
int calibbias;
int calibscale;
+ bool temperature_sensor_calibrated;
+ u8 temperature_sensor_coefficient;
+ u16 temperature_sensor_adc_val;
};
static const struct regmap_config meson_sar_adc_regmap_config_gxbb = {
@@ -389,9 +426,16 @@ static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
regval);
- if (chan->address == 6)
- regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
- MESON_SAR_ADC_DELTA_10_TEMP_SEL, 0);
+ if (chan->address == MESON_SAR_ADC_VOLTAGE_AND_TEMP_CHANNEL) {
+ if (chan->type == IIO_TEMP)
+ regval = MESON_SAR_ADC_DELTA_10_TEMP_SEL;
+ else
+ regval = 0;
+
+ regmap_update_bits(priv->regmap,
+ MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TEMP_SEL, regval);
+ }
}
static void meson_sar_adc_set_chan7_mux(struct iio_dev *indio_dev,
@@ -506,8 +550,12 @@ static int meson_sar_adc_get_sample(struct iio_dev *indio_dev,
enum meson_sar_adc_num_samples avg_samples,
int *val)
{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
int ret;
+ if (chan->type == IIO_TEMP && !priv->temperature_sensor_calibrated)
+ return -ENOTSUPP;
+
ret = meson_sar_adc_lock(indio_dev);
if (ret)
return ret;
@@ -555,17 +603,31 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
break;
case IIO_CHAN_INFO_SCALE:
- ret = regulator_get_voltage(priv->vref);
- if (ret < 0) {
- dev_err(indio_dev->dev.parent,
- "failed to get vref voltage: %d\n", ret);
- return ret;
+ if (chan->type == IIO_VOLTAGE) {
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "failed to get vref voltage: %d\n",
+ ret);
+ return ret;
+ }
+
+ *val = ret / 1000;
+ *val2 = priv->param->resolution;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ } else if (chan->type == IIO_TEMP) {
+ /* SoC specific multiplier and divider */
+ *val = priv->param->temperature_multiplier;
+ *val2 = priv->param->temperature_divider;
+
+ /* celsius to millicelsius */
+ *val *= 1000;
+
+ return IIO_VAL_FRACTIONAL;
+ } else {
+ return -EINVAL;
}
- *val = ret / 1000;
- *val2 = priv->param->resolution;
- return IIO_VAL_FRACTIONAL_LOG2;
-
case IIO_CHAN_INFO_CALIBBIAS:
*val = priv->calibbias;
return IIO_VAL_INT;
@@ -575,6 +637,13 @@ static int meson_sar_adc_iio_info_read_raw(struct iio_dev *indio_dev,
*val2 = priv->calibscale % MILLION;
return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = DIV_ROUND_CLOSEST(MESON_SAR_ADC_TEMP_OFFSET *
+ priv->param->temperature_divider,
+ priv->param->temperature_multiplier);
+ *val -= priv->temperature_sensor_adc_val;
+ return IIO_VAL_INT;
+
default:
return -EINVAL;
}
@@ -587,8 +656,11 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
struct clk_init_data init;
const char *clk_parents[1];
- init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_div",
- indio_dev->dev.of_node);
+ init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_div",
+ dev_name(indio_dev->dev.parent));
+ if (!init.name)
+ return -ENOMEM;
+
init.flags = 0;
init.ops = &clk_divider_ops;
clk_parents[0] = __clk_get_name(priv->clkin);
@@ -606,8 +678,11 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
if (WARN_ON(IS_ERR(priv->adc_div_clk)))
return PTR_ERR(priv->adc_div_clk);
- init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%pOF#adc_en",
- indio_dev->dev.of_node);
+ init.name = devm_kasprintf(&indio_dev->dev, GFP_KERNEL, "%s#adc_en",
+ dev_name(indio_dev->dev.parent));
+ if (!init.name)
+ return -ENOMEM;
+
init.flags = CLK_SET_RATE_PARENT;
init.ops = &clk_gate_ops;
clk_parents[0] = __clk_get_name(priv->adc_div_clk);
@@ -625,6 +700,65 @@ static int meson_sar_adc_clk_init(struct iio_dev *indio_dev,
return 0;
}
+static int meson_sar_adc_temp_sensor_init(struct iio_dev *indio_dev)
+{
+ struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
+ u8 *buf, trimming_bits, trimming_mask, upper_adc_val;
+ struct nvmem_cell *temperature_calib;
+ size_t read_len;
+ int ret;
+
+ temperature_calib = devm_nvmem_cell_get(&indio_dev->dev,
+ "temperature_calib");
+ if (IS_ERR(temperature_calib)) {
+ ret = PTR_ERR(temperature_calib);
+
+ /*
+ * leave the temperature sensor disabled if no calibration data
+ * was passed via nvmem-cells.
+ */
+ if (ret == -ENODEV)
+ return 0;
+
+ if (ret != -EPROBE_DEFER)
+ dev_err(indio_dev->dev.parent,
+ "failed to get temperature_calib cell\n");
+
+ return ret;
+ }
+
+ read_len = MESON_SAR_ADC_EFUSE_BYTES;
+ buf = nvmem_cell_read(temperature_calib, &read_len);
+ if (IS_ERR(buf)) {
+ dev_err(indio_dev->dev.parent,
+ "failed to read temperature_calib cell\n");
+ return PTR_ERR(buf);
+ } else if (read_len != MESON_SAR_ADC_EFUSE_BYTES) {
+ kfree(buf);
+ dev_err(indio_dev->dev.parent,
+ "invalid read size of temperature_calib cell\n");
+ return -EINVAL;
+ }
+
+ trimming_bits = priv->param->temperature_trimming_bits;
+ trimming_mask = BIT(trimming_bits) - 1;
+
+ priv->temperature_sensor_calibrated =
+ buf[3] & MESON_SAR_ADC_EFUSE_BYTE3_IS_CALIBRATED;
+ priv->temperature_sensor_coefficient = buf[2] & trimming_mask;
+
+ upper_adc_val = FIELD_GET(MESON_SAR_ADC_EFUSE_BYTE3_UPPER_ADC_VAL,
+ buf[3]);
+
+ priv->temperature_sensor_adc_val = buf[2];
+ priv->temperature_sensor_adc_val |= upper_adc_val << BITS_PER_BYTE;
+ priv->temperature_sensor_adc_val >>= trimming_bits;
+
+ kfree(buf);
+
+ return 0;
+}
+
static int meson_sar_adc_init(struct iio_dev *indio_dev)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
@@ -649,10 +783,12 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
meson_sar_adc_stop_sample_engine(indio_dev);
- /* update the channel 6 MUX to select the temperature sensor */
+ /*
+ * disable this bit as seems to be only relevant for Meson6 (based
+ * on the vendor driver), which we don't support at the moment.
+ */
regmap_update_bits(priv->regmap, MESON_SAR_ADC_REG0,
- MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL,
- MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL);
+ MESON_SAR_ADC_REG0_ADC_TEMP_SEN_SEL, 0);
/* disable all channels by default */
regmap_write(priv->regmap, MESON_SAR_ADC_CHAN_LIST, 0x0);
@@ -709,6 +845,29 @@ static int meson_sar_adc_init(struct iio_dev *indio_dev)
regval |= MESON_SAR_ADC_AUX_SW_XP_DRIVE_SW;
regmap_write(priv->regmap, MESON_SAR_ADC_AUX_SW, regval);
+ if (priv->temperature_sensor_calibrated) {
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE1,
+ MESON_SAR_ADC_DELTA_10_TS_REVE1);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE0,
+ MESON_SAR_ADC_DELTA_10_TS_REVE0);
+
+ /*
+ * set bits [3:0] of the TSC (temperature sensor coefficient)
+ * to get the correct values when reading the temperature.
+ */
+ regval = FIELD_PREP(MESON_SAR_ADC_DELTA_10_TS_C_MASK,
+ priv->temperature_sensor_coefficient);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_C_MASK, regval);
+ } else {
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE1, 0);
+ regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELTA_10,
+ MESON_SAR_ADC_DELTA_10_TS_REVE0, 0);
+ }
+
ret = clk_set_parent(priv->adc_sel_clk, priv->clkin);
if (ret) {
dev_err(indio_dev->dev.parent,
@@ -894,6 +1053,17 @@ static const struct meson_sar_adc_param meson_sar_adc_meson8_param = {
.bandgap_reg = MESON_SAR_ADC_DELTA_10,
.regmap_config = &meson_sar_adc_regmap_config_meson8,
.resolution = 10,
+ .temperature_trimming_bits = 4,
+ .temperature_multiplier = 18 * 10000,
+ .temperature_divider = 1024 * 10 * 85,
+};
+
+static const struct meson_sar_adc_param meson_sar_adc_meson8b_param = {
+ .has_bl30_integration = false,
+ .clock_rate = 1150000,
+ .bandgap_reg = MESON_SAR_ADC_DELTA_10,
+ .regmap_config = &meson_sar_adc_regmap_config_meson8,
+ .resolution = 10,
};
static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {
@@ -918,12 +1088,12 @@ static const struct meson_sar_adc_data meson_sar_adc_meson8_data = {
};
static const struct meson_sar_adc_data meson_sar_adc_meson8b_data = {
- .param = &meson_sar_adc_meson8_param,
+ .param = &meson_sar_adc_meson8b_param,
.name = "meson-meson8b-saradc",
};
static const struct meson_sar_adc_data meson_sar_adc_meson8m2_data = {
- .param = &meson_sar_adc_meson8_param,
+ .param = &meson_sar_adc_meson8b_param,
.name = "meson-meson8m2-saradc",
};
@@ -1009,9 +1179,6 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &meson_sar_adc_iio_info;
- indio_dev->channels = meson_sar_adc_iio_channels;
- indio_dev->num_channels = ARRAY_SIZE(meson_sar_adc_iio_channels);
-
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(base))
@@ -1078,6 +1245,22 @@ static int meson_sar_adc_probe(struct platform_device *pdev)
priv->calibscale = MILLION;
+ if (priv->param->temperature_trimming_bits) {
+ ret = meson_sar_adc_temp_sensor_init(indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ if (priv->temperature_sensor_calibrated) {
+ indio_dev->channels = meson_sar_adc_and_temp_iio_channels;
+ indio_dev->num_channels =
+ ARRAY_SIZE(meson_sar_adc_and_temp_iio_channels);
+ } else {
+ indio_dev->channels = meson_sar_adc_iio_channels;
+ indio_dev->num_channels =
+ ARRAY_SIZE(meson_sar_adc_iio_channels);
+ }
+
ret = meson_sar_adc_init(indio_dev);
if (ret)
goto err;
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
index f9af6b082916..6a866cc187f7 100644
--- a/drivers/iio/adc/qcom-spmi-adc5.c
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -423,6 +423,7 @@ struct adc5_channels {
enum vadc_scale_fn_type scale_fn_type;
};
+/* In these definitions, _pre refers to an index into adc5_prescale_ratios. */
#define ADC5_CHAN(_dname, _type, _mask, _pre, _scale) \
{ \
.datasheet_name = _dname, \
@@ -443,63 +444,63 @@ struct adc5_channels {
_pre, _scale) \
static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
- [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1,
+ [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1,
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3,
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1,
+ [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", 1,
+ [ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 0,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 16,
+ [ADC5_USB_IN_V_16] = ADC5_CHAN_VOLT("usb_in_v_div_16", 8,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 1,
+ [ADC5_CHG_TEMP] = ADC5_CHAN_TEMP("chg_temp", 0,
SCALE_HW_CALIB_PM5_CHG_TEMP)
/* Charger prescales SBUx and MID_CHG to fit within 1.8V upper unit */
- [ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 3,
+ [ADC5_SBUx] = ADC5_CHAN_VOLT("chg_sbux", 1,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 6,
+ [ADC5_MID_CHG_DIV6] = ADC5_CHAN_VOLT("chg_mid_chg", 3,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 1,
+ [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm", 0,
SCALE_HW_CALIB_XOTHERM)
- [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1,
+ [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
- [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1,
+ [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
- [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1,
+ [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
- [ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 1,
+ [ADC5_AMUX_THM2] = ADC5_CHAN_TEMP("amux_thm2", 0,
SCALE_HW_CALIB_PM5_SMB_TEMP)
};
static const struct adc5_channels adc5_chans_rev2[ADC5_MAX_CHANNEL] = {
- [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 1,
+ [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 1,
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 3,
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 3,
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 3,
+ [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 1,
SCALE_HW_CALIB_DEFAULT)
- [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 1,
+ [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
SCALE_HW_CALIB_PMIC_THERM)
- [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 1,
+ [ADC5_AMUX_THM1_100K_PU] = ADC5_CHAN_TEMP("amux_thm1_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
- [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 1,
+ [ADC5_AMUX_THM2_100K_PU] = ADC5_CHAN_TEMP("amux_thm2_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
- [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 1,
+ [ADC5_AMUX_THM3_100K_PU] = ADC5_CHAN_TEMP("amux_thm3_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
- [ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 1,
+ [ADC5_AMUX_THM4_100K_PU] = ADC5_CHAN_TEMP("amux_thm4_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
- [ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 1,
+ [ADC5_AMUX_THM5_100K_PU] = ADC5_CHAN_TEMP("amux_thm5_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
- [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 1,
+ [ADC5_XO_THERM_100K_PU] = ADC5_CHAN_TEMP("xo_therm_100k_pu", 0,
SCALE_HW_CALIB_THERM_100K_PULLUP)
};
@@ -558,6 +559,9 @@ static int adc5_get_dt_channel_data(struct adc5_chip *adc,
return ret;
}
prop->prescale = ret;
+ } else {
+ prop->prescale =
+ adc->data->adc_chans[prop->channel].prescale_index;
}
ret = of_property_read_u32(node, "qcom,hw-settle-time", &value);
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
index 4e982b51bcda..2c0d0316d149 100644
--- a/drivers/iio/adc/rcar-gyroadc.c
+++ b/drivers/iio/adc/rcar-gyroadc.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* Renesas R-Car GyroADC driver
*
* Copyright 2016 Marek Vasut <marek.vasut@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
*/
#include <linux/module.h>
diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c
index 7940b23dcad9..f7f7a18904b4 100644
--- a/drivers/iio/adc/sc27xx_adc.c
+++ b/drivers/iio/adc/sc27xx_adc.c
@@ -52,6 +52,9 @@
/* Timeout (ms) for the trylock of hardware spinlocks */
#define SC27XX_ADC_HWLOCK_TIMEOUT 5000
+/* Timeout (ms) for ADC data conversion according to ADC datasheet */
+#define SC27XX_ADC_RDY_TIMEOUT 100
+
/* Maximum ADC channel number */
#define SC27XX_ADC_CHANNEL_MAX 32
@@ -223,7 +226,14 @@ static int sc27xx_adc_read(struct sc27xx_adc_data *data, int channel,
if (ret)
goto disable_adc;
- wait_for_completion(&data->completion);
+ ret = wait_for_completion_timeout(&data->completion,
+ msecs_to_jiffies(SC27XX_ADC_RDY_TIMEOUT));
+ if (!ret) {
+ dev_err(data->dev, "read ADC data timeout\n");
+ ret = -ETIMEDOUT;
+ } else {
+ ret = 0;
+ }
disable_adc:
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index ca432e7b6ff1..2327ec18b40c 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -16,6 +16,7 @@
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
@@ -48,15 +49,19 @@
#define STM32H7_CKMODE_SHIFT 16
#define STM32H7_CKMODE_MASK GENMASK(17, 16)
+#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000
+
/**
* stm32_adc_common_regs - stm32 common registers, compatible dependent data
* @csr: common status register offset
+ * @ccr: common control register offset
* @eoc1: adc1 end of conversion flag in @csr
* @eoc2: adc2 end of conversion flag in @csr
* @eoc3: adc3 end of conversion flag in @csr
*/
struct stm32_adc_common_regs {
u32 csr;
+ u32 ccr;
u32 eoc1_msk;
u32 eoc2_msk;
u32 eoc3_msk;
@@ -85,6 +90,7 @@ struct stm32_adc_priv_cfg {
* @vref: regulator reference
* @cfg: compatible configuration data
* @common: common data for all ADC instances
+ * @ccr_bak: backup CCR in low power mode
*/
struct stm32_adc_priv {
int irq[STM32_ADC_MAX_ADCS];
@@ -94,6 +100,7 @@ struct stm32_adc_priv {
struct regulator *vref;
const struct stm32_adc_priv_cfg *cfg;
struct stm32_adc_common common;
+ u32 ccr_bak;
};
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
@@ -265,6 +272,7 @@ out:
/* STM32F4 common registers definitions */
static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
.csr = STM32F4_ADC_CSR,
+ .ccr = STM32F4_ADC_CCR,
.eoc1_msk = STM32F4_EOC1,
.eoc2_msk = STM32F4_EOC2,
.eoc3_msk = STM32F4_EOC3,
@@ -273,6 +281,7 @@ static const struct stm32_adc_common_regs stm32f4_adc_common_regs = {
/* STM32H7 common registers definitions */
static const struct stm32_adc_common_regs stm32h7_adc_common_regs = {
.csr = STM32H7_ADC_CSR,
+ .ccr = STM32H7_ADC_CCR,
.eoc1_msk = STM32H7_EOC_MST,
.eoc2_msk = STM32H7_EOC_SLV,
};
@@ -379,6 +388,61 @@ static void stm32_adc_irq_remove(struct platform_device *pdev,
}
}
+static int stm32_adc_core_hw_start(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+ int ret;
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref enable failed\n");
+ return ret;
+ }
+
+ if (priv->bclk) {
+ ret = clk_prepare_enable(priv->bclk);
+ if (ret < 0) {
+ dev_err(dev, "bus clk enable failed\n");
+ goto err_regulator_disable;
+ }
+ }
+
+ if (priv->aclk) {
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret < 0) {
+ dev_err(dev, "adc clk enable failed\n");
+ goto err_bclk_disable;
+ }
+ }
+
+ writel_relaxed(priv->ccr_bak, priv->common.base + priv->cfg->regs->ccr);
+
+ return 0;
+
+err_bclk_disable:
+ if (priv->bclk)
+ clk_disable_unprepare(priv->bclk);
+err_regulator_disable:
+ regulator_disable(priv->vref);
+
+ return ret;
+}
+
+static void stm32_adc_core_hw_stop(struct device *dev)
+{
+ struct stm32_adc_common *common = dev_get_drvdata(dev);
+ struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+
+ /* Backup CCR that may be lost (depends on power state to achieve) */
+ priv->ccr_bak = readl_relaxed(priv->common.base + priv->cfg->regs->ccr);
+ if (priv->aclk)
+ clk_disable_unprepare(priv->aclk);
+ if (priv->bclk)
+ clk_disable_unprepare(priv->bclk);
+ regulator_disable(priv->vref);
+}
+
static int stm32_adc_probe(struct platform_device *pdev)
{
struct stm32_adc_priv *priv;
@@ -393,6 +457,7 @@ static int stm32_adc_probe(struct platform_device *pdev)
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
if (!priv)
return -ENOMEM;
+ platform_set_drvdata(pdev, &priv->common);
priv->cfg = (const struct stm32_adc_priv_cfg *)
of_match_device(dev->driver->of_match_table, dev)->data;
@@ -410,67 +475,51 @@ static int stm32_adc_probe(struct platform_device *pdev)
return ret;
}
- ret = regulator_enable(priv->vref);
- if (ret < 0) {
- dev_err(&pdev->dev, "vref enable failed\n");
- return ret;
- }
-
- ret = regulator_get_voltage(priv->vref);
- if (ret < 0) {
- dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
- goto err_regulator_disable;
- }
- priv->common.vref_mv = ret / 1000;
- dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
-
priv->aclk = devm_clk_get(&pdev->dev, "adc");
if (IS_ERR(priv->aclk)) {
ret = PTR_ERR(priv->aclk);
- if (ret == -ENOENT) {
- priv->aclk = NULL;
- } else {
+ if (ret != -ENOENT) {
dev_err(&pdev->dev, "Can't get 'adc' clock\n");
- goto err_regulator_disable;
- }
- }
-
- if (priv->aclk) {
- ret = clk_prepare_enable(priv->aclk);
- if (ret < 0) {
- dev_err(&pdev->dev, "adc clk enable failed\n");
- goto err_regulator_disable;
+ return ret;
}
+ priv->aclk = NULL;
}
priv->bclk = devm_clk_get(&pdev->dev, "bus");
if (IS_ERR(priv->bclk)) {
ret = PTR_ERR(priv->bclk);
- if (ret == -ENOENT) {
- priv->bclk = NULL;
- } else {
+ if (ret != -ENOENT) {
dev_err(&pdev->dev, "Can't get 'bus' clock\n");
- goto err_aclk_disable;
+ return ret;
}
+ priv->bclk = NULL;
}
- if (priv->bclk) {
- ret = clk_prepare_enable(priv->bclk);
- if (ret < 0) {
- dev_err(&pdev->dev, "adc clk enable failed\n");
- goto err_aclk_disable;
- }
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_CORE_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_adc_core_hw_start(dev);
+ if (ret)
+ goto err_pm_stop;
+
+ ret = regulator_get_voltage(priv->vref);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "vref get voltage failed, %d\n", ret);
+ goto err_hw_stop;
}
+ priv->common.vref_mv = ret / 1000;
+ dev_dbg(&pdev->dev, "vref+=%dmV\n", priv->common.vref_mv);
ret = priv->cfg->clk_sel(pdev, priv);
if (ret < 0)
- goto err_bclk_disable;
+ goto err_hw_stop;
ret = stm32_adc_irq_probe(pdev, priv);
if (ret < 0)
- goto err_bclk_disable;
-
- platform_set_drvdata(pdev, &priv->common);
+ goto err_hw_stop;
ret = of_platform_populate(np, NULL, NULL, &pdev->dev);
if (ret < 0) {
@@ -478,21 +527,19 @@ static int stm32_adc_probe(struct platform_device *pdev)
goto err_irq_remove;
}
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
err_irq_remove:
stm32_adc_irq_remove(pdev, priv);
-
-err_bclk_disable:
- if (priv->bclk)
- clk_disable_unprepare(priv->bclk);
-
-err_aclk_disable:
- if (priv->aclk)
- clk_disable_unprepare(priv->aclk);
-
-err_regulator_disable:
- regulator_disable(priv->vref);
+err_hw_stop:
+ stm32_adc_core_hw_stop(dev);
+err_pm_stop:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
return ret;
}
@@ -502,17 +549,39 @@ static int stm32_adc_remove(struct platform_device *pdev)
struct stm32_adc_common *common = platform_get_drvdata(pdev);
struct stm32_adc_priv *priv = to_stm32_adc_priv(common);
+ pm_runtime_get_sync(&pdev->dev);
of_platform_depopulate(&pdev->dev);
stm32_adc_irq_remove(pdev, priv);
- if (priv->bclk)
- clk_disable_unprepare(priv->bclk);
- if (priv->aclk)
- clk_disable_unprepare(priv->aclk);
- regulator_disable(priv->vref);
+ stm32_adc_core_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
return 0;
}
+#if defined(CONFIG_PM)
+static int stm32_adc_core_runtime_suspend(struct device *dev)
+{
+ stm32_adc_core_hw_stop(dev);
+
+ return 0;
+}
+
+static int stm32_adc_core_runtime_resume(struct device *dev)
+{
+ return stm32_adc_core_hw_start(dev);
+}
+#endif
+
+static const struct dev_pm_ops stm32_adc_core_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(stm32_adc_core_runtime_suspend,
+ stm32_adc_core_runtime_resume,
+ NULL)
+};
+
static const struct stm32_adc_priv_cfg stm32f4_adc_priv_cfg = {
.regs = &stm32f4_adc_common_regs,
.clk_sel = stm32f4_adc_clk_sel,
@@ -552,6 +621,7 @@ static struct platform_driver stm32_adc_driver = {
.driver = {
.name = "stm32-adc-core",
.of_match_table = stm32_adc_of_match,
+ .pm = &stm32_adc_core_pm_ops,
},
};
module_platform_driver(stm32_adc_driver);
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 378411853d75..205e1699f954 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -22,6 +22,7 @@
#include <linux/iopoll.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_device.h>
@@ -148,6 +149,7 @@ enum stm32h7_adc_dmngt {
#define STM32_ADC_MAX_SMP 7 /* SMPx range is [0..7] */
#define STM32_ADC_TIMEOUT_US 100000
#define STM32_ADC_TIMEOUT (msecs_to_jiffies(STM32_ADC_TIMEOUT_US / 1000))
+#define STM32_ADC_HW_STOP_DELAY_MS 100
#define STM32_DMA_BUFFER_SIZE PAGE_SIZE
@@ -199,11 +201,13 @@ struct stm32_adc_trig_info {
* @calfact_s: Calibration offset for single ended channels
* @calfact_d: Calibration offset in differential
* @lincalfact: Linearity calibration factor
+ * @calibrated: Indicates calibration status
*/
struct stm32_adc_calib {
u32 calfact_s;
u32 calfact_d;
u32 lincalfact[STM32H7_LINCALFACT_NUM];
+ bool calibrated;
};
/**
@@ -251,7 +255,6 @@ struct stm32_adc;
* @trigs: external trigger sources
* @clk_required: clock is required
* @has_vregready: vregready status flag presence
- * @selfcalib: optional routine for self-calibration
* @prepare: optional prepare routine (power-up, enable)
* @start_conv: routine to start conversions
* @stop_conv: routine to stop conversions
@@ -264,7 +267,6 @@ struct stm32_adc_cfg {
struct stm32_adc_trig_info *trigs;
bool clk_required;
bool has_vregready;
- int (*selfcalib)(struct stm32_adc *);
int (*prepare)(struct stm32_adc *);
void (*start_conv)(struct stm32_adc *, bool dma);
void (*stop_conv)(struct stm32_adc *);
@@ -623,6 +625,47 @@ static void stm32_adc_set_res(struct stm32_adc *adc)
stm32_adc_writel(adc, res->reg, val);
}
+static int stm32_adc_hw_stop(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+
+ if (adc->cfg->unprepare)
+ adc->cfg->unprepare(adc);
+
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+ return 0;
+}
+
+static int stm32_adc_hw_start(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+ int ret;
+
+ if (adc->clk) {
+ ret = clk_prepare_enable(adc->clk);
+ if (ret)
+ return ret;
+ }
+
+ stm32_adc_set_res(adc);
+
+ if (adc->cfg->prepare) {
+ ret = adc->cfg->prepare(adc);
+ if (ret)
+ goto err_clk_dis;
+ }
+
+ return 0;
+
+err_clk_dis:
+ if (adc->clk)
+ clk_disable_unprepare(adc->clk);
+
+ return ret;
+}
+
/**
* stm32f4_adc_start_conv() - Start conversions for regular channels.
* @adc: stm32 adc instance
@@ -777,6 +820,7 @@ static void stm32h7_adc_disable(struct stm32_adc *adc)
/**
* stm32h7_adc_read_selfcalib() - read calibration shadow regs, save result
* @adc: stm32 adc instance
+ * Note: Must be called once ADC is enabled, so LINCALRDYW[1..6] are writable
*/
static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
{
@@ -784,11 +828,6 @@ static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
int i, ret;
u32 lincalrdyw_mask, val;
- /* Enable adc so LINCALRDYW1..6 bits are writable */
- ret = stm32h7_adc_enable(adc);
- if (ret)
- return ret;
-
/* Read linearity calibration */
lincalrdyw_mask = STM32H7_LINCALRDYW6;
for (i = STM32H7_LINCALFACT_NUM - 1; i >= 0; i--) {
@@ -801,7 +840,7 @@ static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
100, STM32_ADC_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "Failed to read calfact\n");
- goto disable;
+ return ret;
}
val = stm32_adc_readl(adc, STM32H7_ADC_CALFACT2);
@@ -817,11 +856,9 @@ static int stm32h7_adc_read_selfcalib(struct stm32_adc *adc)
adc->cal.calfact_s >>= STM32H7_CALFACT_S_SHIFT;
adc->cal.calfact_d = (val & STM32H7_CALFACT_D_MASK);
adc->cal.calfact_d >>= STM32H7_CALFACT_D_SHIFT;
+ adc->cal.calibrated = true;
-disable:
- stm32h7_adc_disable(adc);
-
- return ret;
+ return 0;
}
/**
@@ -898,9 +935,9 @@ static int stm32h7_adc_restore_selfcalib(struct stm32_adc *adc)
#define STM32H7_ADC_CALIB_TIMEOUT_US 100000
/**
- * stm32h7_adc_selfcalib() - Procedure to calibrate ADC (from power down)
+ * stm32h7_adc_selfcalib() - Procedure to calibrate ADC
* @adc: stm32 adc instance
- * Exit from power down, calibrate ADC, then return to power down.
+ * Note: Must be called once ADC is out of power down.
*/
static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
{
@@ -908,9 +945,8 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
int ret;
u32 val;
- ret = stm32h7_adc_exit_pwr_down(adc);
- if (ret)
- return ret;
+ if (adc->cal.calibrated)
+ return true;
/*
* Select calibration mode:
@@ -927,7 +963,7 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
STM32H7_ADC_CALIB_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "calibration failed\n");
- goto pwr_dwn;
+ goto out;
}
/*
@@ -944,18 +980,13 @@ static int stm32h7_adc_selfcalib(struct stm32_adc *adc)
STM32H7_ADC_CALIB_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "calibration failed\n");
- goto pwr_dwn;
+ goto out;
}
+out:
stm32_adc_clr_bits(adc, STM32H7_ADC_CR,
STM32H7_ADCALDIF | STM32H7_ADCALLIN);
- /* Read calibration result for future reference */
- ret = stm32h7_adc_read_selfcalib(adc);
-
-pwr_dwn:
- stm32h7_adc_enter_pwr_down(adc);
-
return ret;
}
@@ -972,19 +1003,28 @@ pwr_dwn:
*/
static int stm32h7_adc_prepare(struct stm32_adc *adc)
{
- int ret;
+ int calib, ret;
ret = stm32h7_adc_exit_pwr_down(adc);
if (ret)
return ret;
+ ret = stm32h7_adc_selfcalib(adc);
+ if (ret < 0)
+ goto pwr_dwn;
+ calib = ret;
+
stm32_adc_writel(adc, STM32H7_ADC_DIFSEL, adc->difsel);
ret = stm32h7_adc_enable(adc);
if (ret)
goto pwr_dwn;
- ret = stm32h7_adc_restore_selfcalib(adc);
+ /* Either restore or read calibration result for future reference */
+ if (calib)
+ ret = stm32h7_adc_restore_selfcalib(adc);
+ else
+ ret = stm32h7_adc_read_selfcalib(adc);
if (ret)
goto disable;
@@ -1174,6 +1214,7 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
int *res)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
const struct stm32_adc_regspec *regs = adc->cfg->regs;
long timeout;
u32 val;
@@ -1183,10 +1224,10 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
adc->bufi = 0;
- if (adc->cfg->prepare) {
- ret = adc->cfg->prepare(adc);
- if (ret)
- return ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
}
/* Apply sampling time settings */
@@ -1224,8 +1265,8 @@ static int stm32_adc_single_conv(struct iio_dev *indio_dev,
stm32_adc_conv_irq_disable(adc);
- if (adc->cfg->unprepare)
- adc->cfg->unprepare(adc);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
@@ -1333,15 +1374,22 @@ static int stm32_adc_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
int ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
+
adc->num_conv = bitmap_weight(scan_mask, indio_dev->masklength);
ret = stm32_adc_conf_scan_seq(indio_dev, scan_mask);
- if (ret)
- return ret;
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
- return 0;
+ return ret;
}
static int stm32_adc_of_xlate(struct iio_dev *indio_dev,
@@ -1371,12 +1419,23 @@ static int stm32_adc_debugfs_reg_access(struct iio_dev *indio_dev,
unsigned *readval)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
+ }
if (!readval)
stm32_adc_writel(adc, reg, writeval);
else
*readval = stm32_adc_readl(adc, reg);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
}
@@ -1459,21 +1518,22 @@ static int stm32_adc_dma_start(struct iio_dev *indio_dev)
return 0;
}
-static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
+static int __stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
{
struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
int ret;
- if (adc->cfg->prepare) {
- ret = adc->cfg->prepare(adc);
- if (ret)
- return ret;
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0) {
+ pm_runtime_put_noidle(dev);
+ return ret;
}
ret = stm32_adc_set_trig(indio_dev, indio_dev->trig);
if (ret) {
dev_err(&indio_dev->dev, "Can't set trigger\n");
- goto err_unprepare;
+ goto err_pm_put;
}
ret = stm32_adc_dma_start(indio_dev);
@@ -1482,10 +1542,6 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
goto err_clr_trig;
}
- ret = iio_triggered_buffer_postenable(indio_dev);
- if (ret < 0)
- goto err_stop_dma;
-
/* Reset adc buffer index */
adc->bufi = 0;
@@ -1496,39 +1552,58 @@ static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
return 0;
-err_stop_dma:
- if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
err_clr_trig:
stm32_adc_set_trig(indio_dev, NULL);
-err_unprepare:
- if (adc->cfg->unprepare)
- adc->cfg->unprepare(adc);
+err_pm_put:
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
return ret;
}
-static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
+static int stm32_adc_buffer_postenable(struct iio_dev *indio_dev)
{
- struct stm32_adc *adc = iio_priv(indio_dev);
int ret;
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = __stm32_adc_buffer_postenable(indio_dev);
+ if (ret < 0)
+ iio_triggered_buffer_predisable(indio_dev);
+
+ return ret;
+}
+
+static void __stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct stm32_adc *adc = iio_priv(indio_dev);
+ struct device *dev = indio_dev->dev.parent;
+
adc->cfg->stop_conv(adc);
if (!adc->dma_chan)
stm32_adc_conv_irq_disable(adc);
- ret = iio_triggered_buffer_predisable(indio_dev);
- if (ret < 0)
- dev_err(&indio_dev->dev, "predisable failed\n");
-
if (adc->dma_chan)
dmaengine_terminate_all(adc->dma_chan);
if (stm32_adc_set_trig(indio_dev, NULL))
dev_err(&indio_dev->dev, "Can't clear trigger\n");
- if (adc->cfg->unprepare)
- adc->cfg->unprepare(adc);
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+}
+
+static int stm32_adc_buffer_predisable(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ __stm32_adc_buffer_predisable(indio_dev);
+
+ ret = iio_triggered_buffer_predisable(indio_dev);
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "predisable failed\n");
return ret;
}
@@ -1867,32 +1942,17 @@ static int stm32_adc_probe(struct platform_device *pdev)
}
}
- if (adc->clk) {
- ret = clk_prepare_enable(adc->clk);
- if (ret < 0) {
- dev_err(&pdev->dev, "clk enable failed\n");
- return ret;
- }
- }
-
ret = stm32_adc_of_get_resolution(indio_dev);
if (ret < 0)
- goto err_clk_disable;
- stm32_adc_set_res(adc);
-
- if (adc->cfg->selfcalib) {
- ret = adc->cfg->selfcalib(adc);
- if (ret)
- goto err_clk_disable;
- }
+ return ret;
ret = stm32_adc_chan_of_init(indio_dev);
if (ret < 0)
- goto err_clk_disable;
+ return ret;
ret = stm32_adc_dma_request(indio_dev);
if (ret < 0)
- goto err_clk_disable;
+ return ret;
ret = iio_triggered_buffer_setup(indio_dev,
&iio_pollfunc_store_time,
@@ -1903,15 +1963,35 @@ static int stm32_adc_probe(struct platform_device *pdev)
goto err_dma_disable;
}
+ /* Get stm32-adc-core PM online */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_set_autosuspend_delay(dev, STM32_ADC_HW_STOP_DELAY_MS);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_enable(dev);
+
+ ret = stm32_adc_hw_start(dev);
+ if (ret)
+ goto err_buffer_cleanup;
+
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&pdev->dev, "iio dev register failed\n");
- goto err_buffer_cleanup;
+ goto err_hw_stop;
}
+ pm_runtime_mark_last_busy(dev);
+ pm_runtime_put_autosuspend(dev);
+
return 0;
+err_hw_stop:
+ stm32_adc_hw_stop(dev);
+
err_buffer_cleanup:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
iio_triggered_buffer_cleanup(indio_dev);
err_dma_disable:
@@ -1921,9 +2001,6 @@ err_dma_disable:
adc->rx_buf, adc->rx_dma_buf);
dma_release_channel(adc->dma_chan);
}
-err_clk_disable:
- if (adc->clk)
- clk_disable_unprepare(adc->clk);
return ret;
}
@@ -1933,7 +2010,12 @@ static int stm32_adc_remove(struct platform_device *pdev)
struct stm32_adc *adc = platform_get_drvdata(pdev);
struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ pm_runtime_get_sync(&pdev->dev);
iio_device_unregister(indio_dev);
+ stm32_adc_hw_stop(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
iio_triggered_buffer_cleanup(indio_dev);
if (adc->dma_chan) {
dma_free_coherent(adc->dma_chan->device->dev,
@@ -1941,12 +2023,62 @@ static int stm32_adc_remove(struct platform_device *pdev)
adc->rx_buf, adc->rx_dma_buf);
dma_release_channel(adc->dma_chan);
}
- if (adc->clk)
- clk_disable_unprepare(adc->clk);
return 0;
}
+#if defined(CONFIG_PM_SLEEP)
+static int stm32_adc_suspend(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ if (iio_buffer_enabled(indio_dev))
+ __stm32_adc_buffer_predisable(indio_dev);
+
+ return pm_runtime_force_suspend(dev);
+}
+
+static int stm32_adc_resume(struct device *dev)
+{
+ struct stm32_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ int ret;
+
+ ret = pm_runtime_force_resume(dev);
+ if (ret < 0)
+ return ret;
+
+ if (!iio_buffer_enabled(indio_dev))
+ return 0;
+
+ ret = stm32_adc_update_scan_mode(indio_dev,
+ indio_dev->active_scan_mask);
+ if (ret < 0)
+ return ret;
+
+ return __stm32_adc_buffer_postenable(indio_dev);
+}
+#endif
+
+#if defined(CONFIG_PM)
+static int stm32_adc_runtime_suspend(struct device *dev)
+{
+ return stm32_adc_hw_stop(dev);
+}
+
+static int stm32_adc_runtime_resume(struct device *dev)
+{
+ return stm32_adc_hw_start(dev);
+}
+#endif
+
+static const struct dev_pm_ops stm32_adc_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_adc_suspend, stm32_adc_resume)
+ SET_RUNTIME_PM_OPS(stm32_adc_runtime_suspend, stm32_adc_runtime_resume,
+ NULL)
+};
+
static const struct stm32_adc_cfg stm32f4_adc_cfg = {
.regs = &stm32f4_adc_regspec,
.adc_info = &stm32f4_adc_info,
@@ -1961,7 +2093,6 @@ static const struct stm32_adc_cfg stm32h7_adc_cfg = {
.regs = &stm32h7_adc_regspec,
.adc_info = &stm32h7_adc_info,
.trigs = stm32h7_adc_trigs,
- .selfcalib = stm32h7_adc_selfcalib,
.start_conv = stm32h7_adc_start_conv,
.stop_conv = stm32h7_adc_stop_conv,
.prepare = stm32h7_adc_prepare,
@@ -1974,7 +2105,6 @@ static const struct stm32_adc_cfg stm32mp1_adc_cfg = {
.adc_info = &stm32h7_adc_info,
.trigs = stm32h7_adc_trigs,
.has_vregready = true,
- .selfcalib = stm32h7_adc_selfcalib,
.start_conv = stm32h7_adc_start_conv,
.stop_conv = stm32h7_adc_stop_conv,
.prepare = stm32h7_adc_prepare,
@@ -1996,6 +2126,7 @@ static struct platform_driver stm32_adc_driver = {
.driver = {
.name = "stm32-adc",
.of_match_table = stm32_adc_of_match,
+ .pm = &stm32_adc_pm_ops,
},
};
module_platform_driver(stm32_adc_driver);
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index 7cf39b3e2416..1e5a936b5b6a 100644
--- a/drivers/iio/adc/ti-adc128s052.c
+++ b/drivers/iio/adc/ti-adc128s052.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2014 Angelo Compagnucci <angelo.compagnucci@gmail.com>
*
@@ -6,16 +7,14 @@
* http://www.ti.com/lit/ds/symlink/adc128s052.pdf
* http://www.ti.com/lit/ds/symlink/adc122s021.pdf
* http://www.ti.com/lit/ds/symlink/adc124s021.pdf
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
*/
+#include <linux/acpi.h>
#include <linux/err.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/iio/iio.h>
+#include <linux/property.h>
#include <linux/regulator/consumer.h>
struct adc128_configuration {
@@ -135,10 +134,15 @@ static const struct iio_info adc128_info = {
static int adc128_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
+ unsigned int config;
struct adc128 *adc;
- int config = spi_get_device_id(spi)->driver_data;
int ret;
+ if (dev_fwnode(&spi->dev))
+ config = (unsigned long) device_get_match_data(&spi->dev);
+ else
+ config = spi_get_device_id(spi)->driver_data;
+
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
if (!indio_dev)
return -ENOMEM;
@@ -186,23 +190,40 @@ static int adc128_remove(struct spi_device *spi)
static const struct of_device_id adc128_of_match[] = {
{ .compatible = "ti,adc128s052", },
{ .compatible = "ti,adc122s021", },
+ { .compatible = "ti,adc122s051", },
+ { .compatible = "ti,adc122s101", },
{ .compatible = "ti,adc124s021", },
+ { .compatible = "ti,adc124s051", },
+ { .compatible = "ti,adc124s101", },
{ /* sentinel */ },
};
MODULE_DEVICE_TABLE(of, adc128_of_match);
static const struct spi_device_id adc128_id[] = {
- { "adc128s052", 0}, /* index into adc128_config */
- { "adc122s021", 1},
- { "adc124s021", 2},
+ { "adc128s052", 0 }, /* index into adc128_config */
+ { "adc122s021", 1 },
+ { "adc122s051", 1 },
+ { "adc122s101", 1 },
+ { "adc124s021", 2 },
+ { "adc124s051", 2 },
+ { "adc124s101", 2 },
{ }
};
MODULE_DEVICE_TABLE(spi, adc128_id);
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id adc128_acpi_match[] = {
+ { "AANT1280", 2 }, /* ADC124S021 compatible ACPI ID */
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, adc128_acpi_match);
+#endif
+
static struct spi_driver adc128_driver = {
.driver = {
.name = "adc128s052",
.of_match_table = of_match_ptr(adc128_of_match),
+ .acpi_match_table = ACPI_PTR(adc128_acpi_match),
},
.probe = adc128_probe,
.remove = adc128_remove,
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index ed3849d6fc6a..b2143d7b4ccb 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -336,7 +336,7 @@ static void adjust_exponent_nano(int *val0, int *val1, int scale0,
scale1 = scale1 % pow_10(8 - i);
}
*val0 += res;
- *val1 = scale1 * pow_10(exp);
+ *val1 = scale1 * pow_10(exp);
} else if (exp < 0) {
exp = abs(exp);
if (exp > 9) {
diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c
index af3aa38f67cd..9e13be2c0cb9 100644
--- a/drivers/iio/common/ssp_sensors/ssp_dev.c
+++ b/drivers/iio/common/ssp_sensors/ssp_dev.c
@@ -462,43 +462,35 @@ static struct ssp_data *ssp_parse_dt(struct device *dev)
data->mcu_ap_gpio = of_get_named_gpio(node, "mcu-ap-gpios", 0);
if (data->mcu_ap_gpio < 0)
- goto err_free_pd;
+ return NULL;
data->ap_mcu_gpio = of_get_named_gpio(node, "ap-mcu-gpios", 0);
if (data->ap_mcu_gpio < 0)
- goto err_free_pd;
+ return NULL;
data->mcu_reset_gpio = of_get_named_gpio(node, "mcu-reset-gpios", 0);
if (data->mcu_reset_gpio < 0)
- goto err_free_pd;
+ return NULL;
ret = devm_gpio_request_one(dev, data->ap_mcu_gpio, GPIOF_OUT_INIT_HIGH,
"ap-mcu-gpios");
if (ret)
- goto err_free_pd;
+ return NULL;
ret = devm_gpio_request_one(dev, data->mcu_reset_gpio,
GPIOF_OUT_INIT_HIGH, "mcu-reset-gpios");
if (ret)
- goto err_ap_mcu;
+ return NULL;
match = of_match_node(ssp_of_match, node);
if (!match)
- goto err_mcu_reset_gpio;
+ return NULL;
data->sensorhub_info = match->data;
dev_set_drvdata(dev, data);
return data;
-
-err_mcu_reset_gpio:
- devm_gpio_free(dev, data->mcu_reset_gpio);
-err_ap_mcu:
- devm_gpio_free(dev, data->ap_mcu_gpio);
-err_free_pd:
- devm_kfree(dev, data);
- return NULL;
}
#else
static struct ssp_data *ssp_parse_dt(struct device *pdev)
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 26fbd1bd9413..e50c975250e9 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -133,7 +133,7 @@ static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings,
for (i = 0; i < ST_SENSORS_FULLSCALE_AVL_MAX; i++) {
if (sensor_settings->fs.fs_avl[i].num == 0)
- goto st_sensors_match_odr_error;
+ return ret;
if (sensor_settings->fs.fs_avl[i].num == fs) {
*index_fs_avl = i;
@@ -142,7 +142,6 @@ static int st_sensors_match_fs(struct st_sensor_settings *sensor_settings,
}
}
-st_sensors_match_odr_error:
return ret;
}
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
index fdcc5a891958..224596b0e189 100644
--- a/drivers/iio/common/st_sensors/st_sensors_trigger.c
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -104,7 +104,7 @@ static irqreturn_t st_sensors_irq_thread(int irq, void *p)
return IRQ_HANDLED;
/*
- * If we are using egde IRQs, new samples arrived while processing
+ * If we are using edge IRQs, new samples arrived while processing
* the IRQ and those may be missed unless we pick them here, so poll
* again. If the sensor delivery frequency is very high, this thread
* turns into a polled loop handler.
@@ -148,7 +148,7 @@ int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
if (!sdata->sensor_settings->drdy_irq.addr_ihl) {
dev_err(&indio_dev->dev,
"falling/low specified for IRQ "
- "but hardware only support rising/high: "
+ "but hardware supports only rising/high: "
"will request rising/high\n");
if (irq_trig == IRQF_TRIGGER_FALLING)
irq_trig = IRQF_TRIGGER_RISING;
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index bb2057fd1b6f..851b61eaf3da 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -366,6 +366,15 @@ config TI_DAC5571
If compiled as a module, it will be called ti-dac5571.
+config TI_DAC7311
+ tristate "Texas Instruments 8/10/12-bit 1-channel DAC driver"
+ depends on SPI
+ help
+ Driver for the Texas Instruments
+ DAC7311, DAC6311, DAC5311.
+
+ If compiled as a module, it will be called ti-dac7311.
+
config VF610_DAC
tristate "Vybrid vf610 DAC driver"
depends on OF
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index 2ac93cc4a389..f0a37c93de8e 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -40,4 +40,5 @@ obj-$(CONFIG_STM32_DAC_CORE) += stm32-dac-core.o
obj-$(CONFIG_STM32_DAC) += stm32-dac.o
obj-$(CONFIG_TI_DAC082S085) += ti-dac082s085.o
obj-$(CONFIG_TI_DAC5571) += ti-dac5571.o
+obj-$(CONFIG_TI_DAC7311) += ti-dac7311.o
obj-$(CONFIG_VF610_DAC) += vf610_dac.o
diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c
index 1df9143f55e9..665fa6bd9ced 100644
--- a/drivers/iio/dac/ad5686-spi.c
+++ b/drivers/iio/dac/ad5686-spi.c
@@ -19,6 +19,12 @@ static int ad5686_spi_write(struct ad5686_state *st,
u8 tx_len, *buf;
switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ st->data[0].d16 = cpu_to_be16(AD5310_CMD(cmd) |
+ val);
+ buf = &st->data[0].d8[0];
+ tx_len = 2;
+ break;
case AD5683_REGMAP:
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
AD5683_DATA(val));
@@ -56,10 +62,18 @@ static int ad5686_spi_read(struct ad5686_state *st, u8 addr)
u8 cmd = 0;
int ret;
- if (st->chip_info->regmap_type == AD5686_REGMAP)
- cmd = AD5686_CMD_READBACK_ENABLE;
- else if (st->chip_info->regmap_type == AD5683_REGMAP)
+ switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ return -ENOTSUPP;
+ case AD5683_REGMAP:
cmd = AD5686_CMD_READBACK_ENABLE_V2;
+ break;
+ case AD5686_REGMAP:
+ cmd = AD5686_CMD_READBACK_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
st->data[0].d32 = cpu_to_be32(AD5686_CMD(cmd) |
AD5686_ADDR(addr));
@@ -86,6 +100,7 @@ static int ad5686_spi_remove(struct spi_device *spi)
}
static const struct spi_device_id ad5686_spi_id[] = {
+ {"ad5310r", ID_AD5310R},
{"ad5672r", ID_AD5672R},
{"ad5676", ID_AD5676},
{"ad5676r", ID_AD5676R},
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 0e134b13967a..a332b93ca2c4 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -83,6 +83,10 @@ static ssize_t ad5686_write_dac_powerdown(struct iio_dev *indio_dev,
st->pwr_down_mask &= ~(0x3 << (chan->channel * 2));
switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ shift = 9;
+ ref_bit_msk = AD5310_REF_BIT_MSK;
+ break;
case AD5683_REGMAP:
shift = 13;
ref_bit_msk = AD5683_REF_BIT_MSK;
@@ -124,7 +128,8 @@ static int ad5686_read_raw(struct iio_dev *indio_dev,
mutex_unlock(&indio_dev->mlock);
if (ret < 0)
return ret;
- *val = ret;
+ *val = (ret >> chan->scan_type.shift) &
+ GENMASK(chan->scan_type.realbits - 1, 0);
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = st->vref_mv;
@@ -221,6 +226,7 @@ static struct iio_chan_spec name[] = { \
AD5868_CHANNEL(7, 7, bits, _shift), \
}
+DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2);
DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6);
DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4);
DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0);
@@ -232,6 +238,12 @@ DECLARE_AD5693_CHANNELS(ad5692r_channels, 14, 2);
DECLARE_AD5693_CHANNELS(ad5691r_channels, 12, 4);
static const struct ad5686_chip_info ad5686_chip_info_tbl[] = {
+ [ID_AD5310R] = {
+ .channels = ad5310r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 1,
+ .regmap_type = AD5310_REGMAP,
+ },
[ID_AD5311R] = {
.channels = ad5311r_channels,
.int_vref_mv = 2500,
@@ -419,6 +431,11 @@ int ad5686_probe(struct device *dev,
indio_dev->num_channels = st->chip_info->num_channels;
switch (st->chip_info->regmap_type) {
+ case AD5310_REGMAP:
+ cmd = AD5686_CMD_CONTROL_REG;
+ ref_bit_msk = AD5310_REF_BIT_MSK;
+ st->use_internal_vref = !voltage_uv;
+ break;
case AD5683_REGMAP:
cmd = AD5686_CMD_CONTROL_REG;
ref_bit_msk = AD5683_REF_BIT_MSK;
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index 57b3c61bfb91..19f6917d4738 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -13,7 +13,10 @@
#include <linux/mutex.h>
#include <linux/kernel.h>
+#define AD5310_CMD(x) ((x) << 12)
+
#define AD5683_DATA(x) ((x) << 4)
+
#define AD5686_ADDR(x) ((x) << 16)
#define AD5686_CMD(x) ((x) << 20)
@@ -38,6 +41,8 @@
#define AD5686_CMD_CONTROL_REG 0x4
#define AD5686_CMD_READBACK_ENABLE_V2 0x5
+
+#define AD5310_REF_BIT_MSK BIT(8)
#define AD5683_REF_BIT_MSK BIT(12)
#define AD5693_REF_BIT_MSK BIT(12)
@@ -45,6 +50,7 @@
* ad5686_supported_device_ids:
*/
enum ad5686_supported_device_ids {
+ ID_AD5310R,
ID_AD5311R,
ID_AD5671R,
ID_AD5672R,
@@ -72,6 +78,7 @@ enum ad5686_supported_device_ids {
};
enum ad5686_regmap_type {
+ AD5310_REGMAP,
AD5683_REGMAP,
AD5686_REGMAP,
AD5693_REGMAP
diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c
index a791d0a09d3b..4a6111b7e86c 100644
--- a/drivers/iio/dac/dpot-dac.c
+++ b/drivers/iio/dac/dpot-dac.c
@@ -74,11 +74,11 @@ static int dpot_dac_read_raw(struct iio_dev *indio_dev,
case IIO_VAL_INT:
/*
* Convert integer scale to fractional scale by
- * setting the denominator (val2) to one...
+ * setting the denominator (val2) to one, and...
*/
*val2 = 1;
ret = IIO_VAL_FRACTIONAL;
- /* ...and fall through. */
+ /* fall through */
case IIO_VAL_FRACTIONAL:
*val *= regulator_get_voltage(dac->vref) / 1000;
*val2 *= dac->max_ohms;
diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c
new file mode 100644
index 000000000000..6f5df1a30a1c
--- /dev/null
+++ b/drivers/iio/dac/ti-dac7311.c
@@ -0,0 +1,338 @@
+// SPDX-License-Identifier: GPL-2.0
+/* ti-dac7311.c - Texas Instruments 8/10/12-bit 1-channel DAC driver
+ *
+ * Copyright (C) 2018 CMC NV
+ *
+ * http://www.ti.com/lit/ds/symlink/dac7311.pdf
+ */
+
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+enum {
+ ID_DAC5311 = 0,
+ ID_DAC6311,
+ ID_DAC7311,
+};
+
+enum {
+ POWER_1KOHM_TO_GND = 0,
+ POWER_100KOHM_TO_GND,
+ POWER_TRI_STATE,
+};
+
+struct ti_dac_spec {
+ u8 resolution;
+};
+
+static const struct ti_dac_spec ti_dac_spec[] = {
+ [ID_DAC5311] = { .resolution = 8 },
+ [ID_DAC6311] = { .resolution = 10 },
+ [ID_DAC7311] = { .resolution = 12 },
+};
+
+/**
+ * struct ti_dac_chip - TI DAC chip
+ * @lock: protects write sequences
+ * @vref: regulator generating Vref
+ * @spi: SPI device to send data to the device
+ * @val: cached value
+ * @powerdown: whether the chip is powered down
+ * @powerdown_mode: selected by the user
+ * @resolution: resolution of the chip
+ * @buf: buffer for transfer data
+ */
+struct ti_dac_chip {
+ struct mutex lock;
+ struct regulator *vref;
+ struct spi_device *spi;
+ u16 val;
+ bool powerdown;
+ u8 powerdown_mode;
+ u8 resolution;
+ u8 buf[2] ____cacheline_aligned;
+};
+
+static u8 ti_dac_get_power(struct ti_dac_chip *ti_dac, bool powerdown)
+{
+ if (powerdown)
+ return ti_dac->powerdown_mode + 1;
+
+ return 0;
+}
+
+static int ti_dac_cmd(struct ti_dac_chip *ti_dac, u8 power, u16 val)
+{
+ u8 shift = 14 - ti_dac->resolution;
+
+ ti_dac->buf[0] = (val << shift) & 0xFF;
+ ti_dac->buf[1] = (power << 6) | (val >> (8 - shift));
+ return spi_write(ti_dac->spi, ti_dac->buf, 2);
+}
+
+static const char * const ti_dac_powerdown_modes[] = {
+ "1kohm_to_gnd",
+ "100kohm_to_gnd",
+ "three_state",
+};
+
+static int ti_dac_get_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ return ti_dac->powerdown_mode;
+}
+
+static int ti_dac_set_powerdown_mode(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ unsigned int mode)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ ti_dac->powerdown_mode = mode;
+ return 0;
+}
+
+static const struct iio_enum ti_dac_powerdown_mode = {
+ .items = ti_dac_powerdown_modes,
+ .num_items = ARRAY_SIZE(ti_dac_powerdown_modes),
+ .get = ti_dac_get_powerdown_mode,
+ .set = ti_dac_set_powerdown_mode,
+};
+
+static ssize_t ti_dac_read_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", ti_dac->powerdown);
+}
+
+static ssize_t ti_dac_write_powerdown(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ bool powerdown;
+ u8 power;
+ int ret;
+
+ ret = strtobool(buf, &powerdown);
+ if (ret)
+ return ret;
+
+ power = ti_dac_get_power(ti_dac, powerdown);
+
+ mutex_lock(&ti_dac->lock);
+ ret = ti_dac_cmd(ti_dac, power, 0);
+ if (!ret)
+ ti_dac->powerdown = powerdown;
+ mutex_unlock(&ti_dac->lock);
+
+ return ret ? ret : len;
+}
+
+static const struct iio_chan_spec_ext_info ti_dac_ext_info[] = {
+ {
+ .name = "powerdown",
+ .read = ti_dac_read_powerdown,
+ .write = ti_dac_write_powerdown,
+ .shared = IIO_SHARED_BY_TYPE,
+ },
+ IIO_ENUM("powerdown_mode", IIO_SHARED_BY_TYPE, &ti_dac_powerdown_mode),
+ IIO_ENUM_AVAILABLE("powerdown_mode", &ti_dac_powerdown_mode),
+ { },
+};
+
+#define TI_DAC_CHANNEL(chan) { \
+ .type = IIO_VOLTAGE, \
+ .channel = (chan), \
+ .output = true, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .ext_info = ti_dac_ext_info, \
+}
+
+static const struct iio_chan_spec ti_dac_channels[] = {
+ TI_DAC_CHANNEL(0),
+};
+
+static int ti_dac_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = ti_dac->val;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ ret = regulator_get_voltage(ti_dac->vref);
+ if (ret < 0)
+ return ret;
+
+ *val = ret / 1000;
+ *val2 = ti_dac->resolution;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ }
+
+ return -EINVAL;
+}
+
+static int ti_dac_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+ u8 power = ti_dac_get_power(ti_dac, ti_dac->powerdown);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (ti_dac->val == val)
+ return 0;
+
+ if (val >= (1 << ti_dac->resolution) || val < 0)
+ return -EINVAL;
+
+ if (ti_dac->powerdown)
+ return -EBUSY;
+
+ mutex_lock(&ti_dac->lock);
+ ret = ti_dac_cmd(ti_dac, power, val);
+ if (!ret)
+ ti_dac->val = val;
+ mutex_unlock(&ti_dac->lock);
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static int ti_dac_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, long mask)
+{
+ return IIO_VAL_INT;
+}
+
+static const struct iio_info ti_dac_info = {
+ .read_raw = ti_dac_read_raw,
+ .write_raw = ti_dac_write_raw,
+ .write_raw_get_fmt = ti_dac_write_raw_get_fmt,
+};
+
+static int ti_dac_probe(struct spi_device *spi)
+{
+ struct device *dev = &spi->dev;
+ const struct ti_dac_spec *spec;
+ struct ti_dac_chip *ti_dac;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*ti_dac));
+ if (!indio_dev) {
+ dev_err(dev, "can not allocate iio device\n");
+ return -ENOMEM;
+ }
+
+ spi->mode = SPI_MODE_1;
+ spi->bits_per_word = 16;
+ spi_setup(spi);
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = spi->dev.of_node;
+ indio_dev->info = &ti_dac_info;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ti_dac_channels;
+ spi_set_drvdata(spi, indio_dev);
+
+ ti_dac = iio_priv(indio_dev);
+ ti_dac->powerdown = false;
+ ti_dac->spi = spi;
+
+ spec = &ti_dac_spec[spi_get_device_id(spi)->driver_data];
+ indio_dev->num_channels = 1;
+ ti_dac->resolution = spec->resolution;
+
+ ti_dac->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(ti_dac->vref)) {
+ dev_err(dev, "error to get regulator\n");
+ return PTR_ERR(ti_dac->vref);
+ }
+
+ ret = regulator_enable(ti_dac->vref);
+ if (ret < 0) {
+ dev_err(dev, "can not enable regulator\n");
+ return ret;
+ }
+
+ mutex_init(&ti_dac->lock);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(dev, "fail to register iio device: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ mutex_destroy(&ti_dac->lock);
+ regulator_disable(ti_dac->vref);
+ return ret;
+}
+
+static int ti_dac_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ti_dac_chip *ti_dac = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ mutex_destroy(&ti_dac->lock);
+ regulator_disable(ti_dac->vref);
+ return 0;
+}
+
+static const struct of_device_id ti_dac_of_id[] = {
+ { .compatible = "ti,dac5311" },
+ { .compatible = "ti,dac6311" },
+ { .compatible = "ti,dac7311" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ti_dac_of_id);
+
+static const struct spi_device_id ti_dac_spi_id[] = {
+ { "dac5311", ID_DAC5311 },
+ { "dac6311", ID_DAC6311 },
+ { "dac7311", ID_DAC7311 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ti_dac_spi_id);
+
+static struct spi_driver ti_dac_driver = {
+ .driver = {
+ .name = "ti-dac7311",
+ .of_match_table = ti_dac_of_id,
+ },
+ .probe = ti_dac_probe,
+ .remove = ti_dac_remove,
+ .id_table = ti_dac_spi_id,
+};
+module_spi_driver(ti_dac_driver);
+
+MODULE_AUTHOR("Charles-Antoine Couret <charles-antoine.couret@essensium.com>");
+MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1-channel DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
index 36941e69f959..88e857c4baf4 100644
--- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -111,6 +111,7 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
+ s32 min;
*val = 0;
*val2 = 0;
@@ -118,13 +119,15 @@ static int gyro_3d_read_raw(struct iio_dev *indio_dev,
case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&gyro_state->common_attributes, true);
report_id = gyro_state->gyro[chan->scan_index].report_id;
+ min = gyro_state->gyro[chan->scan_index].logical_minimum;
address = gyro_3d_addresses[chan->scan_index];
if (report_id >= 0)
*val = sensor_hub_input_attr_get_raw_value(
gyro_state->common_attributes.hsdev,
HID_USAGE_SENSOR_GYRO_3D, address,
report_id,
- SENSOR_HUB_SYNC);
+ SENSOR_HUB_SYNC,
+ min < 0);
else {
*val = 0;
hid_sensor_power_state(&gyro_state->common_attributes,
diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c
index beab6d6fd6e1..4bc95f31c730 100644
--- a/drivers/iio/humidity/hid-sensor-humidity.c
+++ b/drivers/iio/humidity/hid-sensor-humidity.c
@@ -75,7 +75,8 @@ static int humidity_read_raw(struct iio_dev *indio_dev,
HID_USAGE_SENSOR_HUMIDITY,
HID_USAGE_SENSOR_ATMOSPHERIC_HUMIDITY,
humid_st->humidity_attr.report_id,
- SENSOR_HUB_SYNC);
+ SENSOR_HUB_SYNC,
+ humid_st->humidity_attr.logical_minimum < 0);
hid_sensor_power_state(&humid_st->common_attributes, false);
return IIO_VAL_INT;
diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h
index e7b11e74fd1d..2351049d930b 100644
--- a/drivers/iio/imu/bmi160/bmi160.h
+++ b/drivers/iio/imu/bmi160/bmi160.h
@@ -6,6 +6,5 @@ extern const struct regmap_config bmi160_regmap_config;
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
const char *name, bool use_spi);
-void bmi160_core_remove(struct device *dev);
#endif /* BMI160_H_ */
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index c85659ca9507..b10330b0f93f 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -542,10 +542,12 @@ static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
return 0;
}
-static void bmi160_chip_uninit(struct bmi160_data *data)
+static void bmi160_chip_uninit(void *data)
{
- bmi160_set_mode(data, BMI160_GYRO, false);
- bmi160_set_mode(data, BMI160_ACCEL, false);
+ struct bmi160_data *bmi_data = data;
+
+ bmi160_set_mode(bmi_data, BMI160_GYRO, false);
+ bmi160_set_mode(bmi_data, BMI160_ACCEL, false);
}
int bmi160_core_probe(struct device *dev, struct regmap *regmap,
@@ -567,6 +569,10 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
if (ret < 0)
return ret;
+ ret = devm_add_action_or_reset(dev, bmi160_chip_uninit, data);
+ if (ret < 0)
+ return ret;
+
if (!name && ACPI_HANDLE(dev))
name = bmi160_match_acpi_device(dev);
@@ -577,35 +583,19 @@ int bmi160_core_probe(struct device *dev, struct regmap *regmap,
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &bmi160_info;
- ret = iio_triggered_buffer_setup(indio_dev, NULL,
- bmi160_trigger_handler, NULL);
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ bmi160_trigger_handler, NULL);
if (ret < 0)
- goto uninit;
+ return ret;
- ret = iio_device_register(indio_dev);
+ ret = devm_iio_device_register(dev, indio_dev);
if (ret < 0)
- goto buffer_cleanup;
+ return ret;
return 0;
-buffer_cleanup:
- iio_triggered_buffer_cleanup(indio_dev);
-uninit:
- bmi160_chip_uninit(data);
- return ret;
}
EXPORT_SYMBOL_GPL(bmi160_core_probe);
-void bmi160_core_remove(struct device *dev)
-{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct bmi160_data *data = iio_priv(indio_dev);
-
- iio_device_unregister(indio_dev);
- iio_triggered_buffer_cleanup(indio_dev);
- bmi160_chip_uninit(data);
-}
-EXPORT_SYMBOL_GPL(bmi160_core_remove);
-
MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com");
MODULE_DESCRIPTION("Bosch BMI160 driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/imu/bmi160/bmi160_i2c.c b/drivers/iio/imu/bmi160/bmi160_i2c.c
index 155a31f72445..5b1f7e6af651 100644
--- a/drivers/iio/imu/bmi160/bmi160_i2c.c
+++ b/drivers/iio/imu/bmi160/bmi160_i2c.c
@@ -38,13 +38,6 @@ static int bmi160_i2c_probe(struct i2c_client *client,
return bmi160_core_probe(&client->dev, regmap, name, false);
}
-static int bmi160_i2c_remove(struct i2c_client *client)
-{
- bmi160_core_remove(&client->dev);
-
- return 0;
-}
-
static const struct i2c_device_id bmi160_i2c_id[] = {
{"bmi160", 0},
{}
@@ -72,7 +65,6 @@ static struct i2c_driver bmi160_i2c_driver = {
.of_match_table = of_match_ptr(bmi160_of_match),
},
.probe = bmi160_i2c_probe,
- .remove = bmi160_i2c_remove,
.id_table = bmi160_i2c_id,
};
module_i2c_driver(bmi160_i2c_driver);
diff --git a/drivers/iio/imu/bmi160/bmi160_spi.c b/drivers/iio/imu/bmi160/bmi160_spi.c
index d34dfdfd1a7d..e521ad14eeac 100644
--- a/drivers/iio/imu/bmi160/bmi160_spi.c
+++ b/drivers/iio/imu/bmi160/bmi160_spi.c
@@ -29,13 +29,6 @@ static int bmi160_spi_probe(struct spi_device *spi)
return bmi160_core_probe(&spi->dev, regmap, id->name, true);
}
-static int bmi160_spi_remove(struct spi_device *spi)
-{
- bmi160_core_remove(&spi->dev);
-
- return 0;
-}
-
static const struct spi_device_id bmi160_spi_id[] = {
{"bmi160", 0},
{}
@@ -58,7 +51,6 @@ MODULE_DEVICE_TABLE(of, bmi160_of_match);
static struct spi_driver bmi160_spi_driver = {
.probe = bmi160_spi_probe,
- .remove = bmi160_spi_remove,
.id_table = bmi160_spi_id,
.driver = {
.acpi_match_table = ACPI_PTR(bmi160_acpi_match),
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
index d78a10403bac..a961b5a06fe6 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
@@ -91,18 +91,14 @@ static int asus_acpi_get_sensor_info(struct acpi_device *adev,
static int acpi_i2c_check_resource(struct acpi_resource *ares, void *data)
{
+ struct acpi_resource_i2c_serialbus *sb;
u32 *addr = data;
- if (ares->type == ACPI_RESOURCE_TYPE_SERIAL_BUS) {
- struct acpi_resource_i2c_serialbus *sb;
-
- sb = &ares->data.i2c_serial_bus;
- if (sb->type == ACPI_RESOURCE_SERIAL_TYPE_I2C) {
- if (*addr)
- *addr |= (sb->slave_address << 16);
- else
- *addr = sb->slave_address;
- }
+ if (i2c_acpi_get_i2c_resource(ares, &sb)) {
+ if (*addr)
+ *addr |= (sb->slave_address << 16);
+ else
+ *addr = sb->slave_address;
}
/* Tell the ACPI core that we already copied this address */
diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile
index 35919febea2a..e5f733ce6e11 100644
--- a/drivers/iio/imu/st_lsm6dsx/Makefile
+++ b/drivers/iio/imu/st_lsm6dsx/Makefile
@@ -1,4 +1,5 @@
-st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
+st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o \
+ st_lsm6dsx_shub.o
obj-$(CONFIG_IIO_ST_LSM6DSX) += st_lsm6dsx.o
obj-$(CONFIG_IIO_ST_LSM6DSX_I2C) += st_lsm6dsx_i2c.o
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index ef73519a0fb6..d1d8d07a0714 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -43,6 +43,24 @@ enum st_lsm6dsx_hw_id {
* ST_LSM6DSX_TAGGED_SAMPLE_SIZE)
#define ST_LSM6DSX_SHIFT_VAL(val, mask) (((val) << __ffs(mask)) & (mask))
+#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
+{ \
+ .type = chan_type, \
+ .address = addr, \
+ .modified = 1, \
+ .channel2 = mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = scan_idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_LE, \
+ }, \
+}
+
struct st_lsm6dsx_reg {
u8 addr;
u8 mask;
@@ -50,6 +68,28 @@ struct st_lsm6dsx_reg {
struct st_lsm6dsx_hw;
+struct st_lsm6dsx_odr {
+ u16 hz;
+ u8 val;
+};
+
+#define ST_LSM6DSX_ODR_LIST_SIZE 6
+struct st_lsm6dsx_odr_table_entry {
+ struct st_lsm6dsx_reg reg;
+ struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
+};
+
+struct st_lsm6dsx_fs {
+ u32 gain;
+ u8 val;
+};
+
+#define ST_LSM6DSX_FS_LIST_SIZE 4
+struct st_lsm6dsx_fs_table_entry {
+ struct st_lsm6dsx_reg reg;
+ struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
+};
+
/**
* struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings
* @read_fifo: Read FIFO callback.
@@ -85,6 +125,70 @@ struct st_lsm6dsx_hw_ts_settings {
};
/**
+ * struct st_lsm6dsx_shub_settings - ST IMU hw i2c controller settings
+ * @page_mux: register page mux info (addr + mask).
+ * @master_en: master config register info (addr + mask).
+ * @pullup_en: i2c controller pull-up register info (addr + mask).
+ * @aux_sens: aux sensor register info (addr + mask).
+ * @wr_once: write_once register info (addr + mask).
+ * @shub_out: sensor hub first output register info.
+ * @slv0_addr: slave0 address in secondary page.
+ * @dw_slv0_addr: slave0 write register address in secondary page.
+ * @batch_en: Enable/disable FIFO batching.
+ */
+struct st_lsm6dsx_shub_settings {
+ struct st_lsm6dsx_reg page_mux;
+ struct st_lsm6dsx_reg master_en;
+ struct st_lsm6dsx_reg pullup_en;
+ struct st_lsm6dsx_reg aux_sens;
+ struct st_lsm6dsx_reg wr_once;
+ u8 shub_out;
+ u8 slv0_addr;
+ u8 dw_slv0_addr;
+ u8 batch_en;
+};
+
+enum st_lsm6dsx_ext_sensor_id {
+ ST_LSM6DSX_ID_MAGN,
+};
+
+/**
+ * struct st_lsm6dsx_ext_dev_settings - i2c controller slave settings
+ * @i2c_addr: I2c slave address list.
+ * @wai: Wai address info.
+ * @id: external sensor id.
+ * @odr: Output data rate of the sensor [Hz].
+ * @gain: Configured sensor sensitivity.
+ * @temp_comp: Temperature compensation register info (addr + mask).
+ * @pwr_table: Power on register info (addr + mask).
+ * @off_canc: Offset cancellation register info (addr + mask).
+ * @bdu: Block data update register info (addr + mask).
+ * @out: Output register info.
+ */
+struct st_lsm6dsx_ext_dev_settings {
+ u8 i2c_addr[2];
+ struct {
+ u8 addr;
+ u8 val;
+ } wai;
+ enum st_lsm6dsx_ext_sensor_id id;
+ struct st_lsm6dsx_odr_table_entry odr_table;
+ struct st_lsm6dsx_fs_table_entry fs_table;
+ struct st_lsm6dsx_reg temp_comp;
+ struct {
+ struct st_lsm6dsx_reg reg;
+ u8 off_val;
+ u8 on_val;
+ } pwr_table;
+ struct st_lsm6dsx_reg off_canc;
+ struct st_lsm6dsx_reg bdu;
+ struct {
+ u8 addr;
+ u8 len;
+ } out;
+};
+
+/**
* struct st_lsm6dsx_settings - ST IMU sensor settings
* @wai: Sensor WhoAmI default value.
* @max_fifo_size: Sensor max fifo length in FIFO words.
@@ -93,6 +197,7 @@ struct st_lsm6dsx_hw_ts_settings {
* @batch: List of FIFO batching register info (addr + mask).
* @fifo_ops: Sensor hw FIFO parameters.
* @ts_settings: Hw timer related settings.
+ * @shub_settings: i2c controller related settings.
*/
struct st_lsm6dsx_settings {
u8 wai;
@@ -102,11 +207,15 @@ struct st_lsm6dsx_settings {
struct st_lsm6dsx_reg batch[ST_LSM6DSX_MAX_ID];
struct st_lsm6dsx_fifo_ops fifo_ops;
struct st_lsm6dsx_hw_ts_settings ts_settings;
+ struct st_lsm6dsx_shub_settings shub_settings;
};
enum st_lsm6dsx_sensor_id {
- ST_LSM6DSX_ID_ACC,
ST_LSM6DSX_ID_GYRO,
+ ST_LSM6DSX_ID_ACC,
+ ST_LSM6DSX_ID_EXT0,
+ ST_LSM6DSX_ID_EXT1,
+ ST_LSM6DSX_ID_EXT2,
ST_LSM6DSX_ID_MAX,
};
@@ -126,6 +235,7 @@ enum st_lsm6dsx_fifo_mode {
* @sip: Number of samples in a given pattern.
* @decimator: FIFO decimation factor.
* @ts_ref: Sensor timestamp reference for hw one.
+ * @ext_info: Sensor settings if it is connected to i2c controller
*/
struct st_lsm6dsx_sensor {
char name[32];
@@ -139,6 +249,11 @@ struct st_lsm6dsx_sensor {
u8 sip;
u8 decimator;
s64 ts_ref;
+
+ struct {
+ const struct st_lsm6dsx_ext_dev_settings *settings;
+ u8 addr;
+ } ext_info;
};
/**
@@ -148,6 +263,7 @@ struct st_lsm6dsx_sensor {
* @irq: Device interrupt line (I2C or SPI).
* @fifo_lock: Mutex to prevent concurrent access to the hw FIFO.
* @conf_lock: Mutex to prevent concurrent FIFO configuration update.
+ * @page_lock: Mutex to prevent concurrent memory page configuration.
* @fifo_mode: FIFO operating mode supported by the device.
* @enable_mask: Enabled sensor bitmask.
* @ts_sip: Total number of timestamp samples in a given pattern.
@@ -163,6 +279,7 @@ struct st_lsm6dsx_hw {
struct mutex fifo_lock;
struct mutex conf_lock;
+ struct mutex page_lock;
enum st_lsm6dsx_fifo_mode fifo_mode;
u8 enable_mask;
@@ -176,13 +293,15 @@ struct st_lsm6dsx_hw {
const struct st_lsm6dsx_settings *settings;
};
+static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
extern const struct dev_pm_ops st_lsm6dsx_pm_ops;
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
struct regmap *regmap);
-int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor);
-int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor);
+int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
+ bool enable);
int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw);
+int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val);
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor,
u16 watermark);
int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw);
@@ -191,5 +310,47 @@ int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw);
int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw);
int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val);
+int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name);
+int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable);
+int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable);
+
+static inline int
+st_lsm6dsx_update_bits_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
+ unsigned int mask, unsigned int val)
+{
+ int err;
+
+ mutex_lock(&hw->page_lock);
+ err = regmap_update_bits(hw->regmap, addr, mask, val);
+ mutex_unlock(&hw->page_lock);
+
+ return err;
+}
+
+static inline int
+st_lsm6dsx_read_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
+ void *val, unsigned int len)
+{
+ int err;
+
+ mutex_lock(&hw->page_lock);
+ err = regmap_bulk_read(hw->regmap, addr, val, len);
+ mutex_unlock(&hw->page_lock);
+
+ return err;
+}
+
+static inline int
+st_lsm6dsx_write_locked(struct st_lsm6dsx_hw *hw, unsigned int addr,
+ unsigned int val)
+{
+ int err;
+
+ mutex_lock(&hw->page_lock);
+ err = regmap_write(hw->regmap, addr, val);
+ mutex_unlock(&hw->page_lock);
+
+ return err;
+}
#endif /* ST_LSM6DSX_H */
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
index b5263fc522ca..2c0d3763405a 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -68,6 +68,9 @@ enum st_lsm6dsx_fifo_tag {
ST_LSM6DSX_GYRO_TAG = 0x01,
ST_LSM6DSX_ACC_TAG = 0x02,
ST_LSM6DSX_TS_TAG = 0x04,
+ ST_LSM6DSX_EXT0_TAG = 0x0f,
+ ST_LSM6DSX_EXT1_TAG = 0x10,
+ ST_LSM6DSX_EXT2_TAG = 0x11,
};
static const
@@ -102,6 +105,9 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw,
*max_odr = 0, *min_odr = ~0;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ if (!hw->iio_devs[i])
+ continue;
+
sensor = iio_priv(hw->iio_devs[i]);
if (!(hw->enable_mask & BIT(sensor->id)))
@@ -125,6 +131,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
const struct st_lsm6dsx_reg *dec_reg;
+ if (!hw->iio_devs[i])
+ continue;
+
sensor = iio_priv(hw->iio_devs[i]);
/* update fifo decimators and sample in pattern */
if (hw->enable_mask & BIT(sensor->id)) {
@@ -142,8 +151,9 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
if (dec_reg->addr) {
int val = ST_LSM6DSX_SHIFT_VAL(data, dec_reg->mask);
- err = regmap_update_bits(hw->regmap, dec_reg->addr,
- dec_reg->mask, val);
+ err = st_lsm6dsx_update_bits_locked(hw, dec_reg->addr,
+ dec_reg->mask,
+ val);
if (err < 0)
return err;
}
@@ -162,8 +172,8 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
int val, ts_dec = !!hw->ts_sip;
val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask);
- err = regmap_update_bits(hw->regmap, ts_dec_reg->addr,
- ts_dec_reg->mask, val);
+ err = st_lsm6dsx_update_bits_locked(hw, ts_dec_reg->addr,
+ ts_dec_reg->mask, val);
}
return err;
}
@@ -171,12 +181,12 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw)
int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
enum st_lsm6dsx_fifo_mode fifo_mode)
{
+ unsigned int data;
int err;
- err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
- ST_LSM6DSX_FIFO_MODE_MASK,
- FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK,
- fifo_mode));
+ data = FIELD_PREP(ST_LSM6DSX_FIFO_MODE_MASK, fifo_mode);
+ err = st_lsm6dsx_update_bits_locked(hw, ST_LSM6DSX_REG_FIFO_MODE_ADDR,
+ ST_LSM6DSX_FIFO_MODE_MASK, data);
if (err < 0)
return err;
@@ -207,15 +217,15 @@ static int st_lsm6dsx_set_fifo_odr(struct st_lsm6dsx_sensor *sensor,
data = 0;
}
val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask);
- return regmap_update_bits(hw->regmap, batch_reg->addr,
- batch_reg->mask, val);
+ return st_lsm6dsx_update_bits_locked(hw, batch_reg->addr,
+ batch_reg->mask, val);
} else {
data = hw->enable_mask ? ST_LSM6DSX_MAX_FIFO_ODR_VAL : 0;
- return regmap_update_bits(hw->regmap,
- ST_LSM6DSX_REG_FIFO_MODE_ADDR,
- ST_LSM6DSX_FIFO_ODR_MASK,
- FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK,
- data));
+ return st_lsm6dsx_update_bits_locked(hw,
+ ST_LSM6DSX_REG_FIFO_MODE_ADDR,
+ ST_LSM6DSX_FIFO_ODR_MASK,
+ FIELD_PREP(ST_LSM6DSX_FIFO_ODR_MASK,
+ data));
}
}
@@ -231,6 +241,9 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
return 0;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ if (!hw->iio_devs[i])
+ continue;
+
cur_sensor = iio_priv(hw->iio_devs[i]);
if (!(hw->enable_mask & BIT(cur_sensor->id)))
@@ -246,19 +259,23 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
fifo_watermark = (fifo_watermark / hw->sip) * hw->sip;
fifo_watermark = fifo_watermark * hw->settings->fifo_ops.th_wl;
+ mutex_lock(&hw->page_lock);
err = regmap_read(hw->regmap, hw->settings->fifo_ops.fifo_th.addr + 1,
&data);
if (err < 0)
- return err;
+ goto out;
fifo_th_mask = hw->settings->fifo_ops.fifo_th.mask;
fifo_watermark = ((data << 8) & ~fifo_th_mask) |
(fifo_watermark & fifo_th_mask);
wdata = cpu_to_le16(fifo_watermark);
- return regmap_bulk_write(hw->regmap,
- hw->settings->fifo_ops.fifo_th.addr,
- &wdata, sizeof(wdata));
+ err = regmap_bulk_write(hw->regmap,
+ hw->settings->fifo_ops.fifo_th.addr,
+ &wdata, sizeof(wdata));
+out:
+ mutex_unlock(&hw->page_lock);
+ return err;
}
static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
@@ -267,12 +284,15 @@ static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw)
int i, err;
/* reset hw ts counter */
- err = regmap_write(hw->regmap, ST_LSM6DSX_REG_TS_RESET_ADDR,
- ST_LSM6DSX_TS_RESET_VAL);
+ err = st_lsm6dsx_write_locked(hw, ST_LSM6DSX_REG_TS_RESET_ADDR,
+ ST_LSM6DSX_TS_RESET_VAL);
if (err < 0)
return err;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ if (!hw->iio_devs[i])
+ continue;
+
sensor = iio_priv(hw->iio_devs[i]);
/*
* store enable buffer timestamp as reference for
@@ -297,8 +317,8 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr,
while (read_len < data_len) {
word_len = min_t(unsigned int, data_len - read_len,
max_word_len);
- err = regmap_bulk_read(hw->regmap, addr, data + read_len,
- word_len);
+ err = st_lsm6dsx_read_locked(hw, addr, data + read_len,
+ word_len);
if (err < 0)
return err;
read_len += word_len;
@@ -328,9 +348,9 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
__le16 fifo_status;
s64 ts = 0;
- err = regmap_bulk_read(hw->regmap,
- hw->settings->fifo_ops.fifo_diff.addr,
- &fifo_status, sizeof(fifo_status));
+ err = st_lsm6dsx_read_locked(hw,
+ hw->settings->fifo_ops.fifo_diff.addr,
+ &fifo_status, sizeof(fifo_status));
if (err < 0) {
dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
err);
@@ -436,6 +456,55 @@ int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
return read_len;
}
+static int
+st_lsm6dsx_push_tagged_data(struct st_lsm6dsx_hw *hw, u8 tag,
+ u8 *data, s64 ts)
+{
+ struct st_lsm6dsx_sensor *sensor;
+ struct iio_dev *iio_dev;
+
+ /*
+ * EXT_TAG are managed in FIFO fashion so ST_LSM6DSX_EXT0_TAG
+ * corresponds to the first enabled channel, ST_LSM6DSX_EXT1_TAG
+ * to the second one and ST_LSM6DSX_EXT2_TAG to the last enabled
+ * channel
+ */
+ switch (tag) {
+ case ST_LSM6DSX_GYRO_TAG:
+ iio_dev = hw->iio_devs[ST_LSM6DSX_ID_GYRO];
+ break;
+ case ST_LSM6DSX_ACC_TAG:
+ iio_dev = hw->iio_devs[ST_LSM6DSX_ID_ACC];
+ break;
+ case ST_LSM6DSX_EXT0_TAG:
+ if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0))
+ iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT0];
+ else if (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1))
+ iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
+ else
+ iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
+ break;
+ case ST_LSM6DSX_EXT1_TAG:
+ if ((hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT0)) &&
+ (hw->enable_mask & BIT(ST_LSM6DSX_ID_EXT1)))
+ iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT1];
+ else
+ iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
+ break;
+ case ST_LSM6DSX_EXT2_TAG:
+ iio_dev = hw->iio_devs[ST_LSM6DSX_ID_EXT2];
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ sensor = iio_priv(iio_dev);
+ iio_push_to_buffers_with_timestamp(iio_dev, data,
+ ts + sensor->ts_ref);
+
+ return 0;
+}
+
/**
* st_lsm6dsx_read_tagged_fifo() - LSM6DSO read FIFO routine
* @hw: Pointer to instance of struct st_lsm6dsx_hw.
@@ -455,9 +524,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
__le16 fifo_status;
s64 ts = 0;
- err = regmap_bulk_read(hw->regmap,
- hw->settings->fifo_ops.fifo_diff.addr,
- &fifo_status, sizeof(fifo_status));
+ err = st_lsm6dsx_read_locked(hw,
+ hw->settings->fifo_ops.fifo_diff.addr,
+ &fifo_status, sizeof(fifo_status));
if (err < 0) {
dev_err(hw->dev, "failed to read fifo status (err=%d)\n",
err);
@@ -491,8 +560,7 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
ST_LSM6DSX_SAMPLE_SIZE);
tag = hw->buff[i] >> 3;
- switch (tag) {
- case ST_LSM6DSX_TS_TAG:
+ if (tag == ST_LSM6DSX_TS_TAG) {
/*
* hw timestamp is 4B long and it is stored
* in FIFO according to this schema:
@@ -509,19 +577,9 @@ int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
if (!reset_ts && ts >= 0xffff0000)
reset_ts = true;
ts *= ST_LSM6DSX_TS_SENSITIVITY;
- break;
- case ST_LSM6DSX_GYRO_TAG:
- iio_push_to_buffers_with_timestamp(
- hw->iio_devs[ST_LSM6DSX_ID_GYRO],
- iio_buff, gyro_sensor->ts_ref + ts);
- break;
- case ST_LSM6DSX_ACC_TAG:
- iio_push_to_buffers_with_timestamp(
- hw->iio_devs[ST_LSM6DSX_ID_ACC],
- iio_buff, acc_sensor->ts_ref + ts);
- break;
- default:
- break;
+ } else {
+ st_lsm6dsx_push_tagged_data(hw, tag, iio_buff,
+ ts);
}
}
}
@@ -562,19 +620,21 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
goto out;
}
- if (enable) {
- err = st_lsm6dsx_sensor_enable(sensor);
+ if (sensor->id == ST_LSM6DSX_ID_EXT0 ||
+ sensor->id == ST_LSM6DSX_ID_EXT1 ||
+ sensor->id == ST_LSM6DSX_ID_EXT2) {
+ err = st_lsm6dsx_shub_set_enable(sensor, enable);
if (err < 0)
goto out;
} else {
- err = st_lsm6dsx_sensor_disable(sensor);
+ err = st_lsm6dsx_sensor_set_enable(sensor, enable);
if (err < 0)
goto out;
- }
- err = st_lsm6dsx_set_fifo_odr(sensor, enable);
- if (err < 0)
- goto out;
+ err = st_lsm6dsx_set_fifo_odr(sensor, enable);
+ if (err < 0)
+ goto out;
+ }
err = st_lsm6dsx_update_decimators(hw);
if (err < 0)
@@ -690,6 +750,9 @@ int st_lsm6dsx_fifo_setup(struct st_lsm6dsx_hw *hw)
}
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ if (!hw->iio_devs[i])
+ continue;
+
buffer = devm_iio_kfifo_allocate(hw->dev);
if (!buffer)
return -ENOMEM;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
index 2ad3c610e4b6..12e29dda9b98 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -56,6 +56,7 @@
#define ST_LSM6DSX_REG_WHOAMI_ADDR 0x0f
#define ST_LSM6DSX_REG_RESET_ADDR 0x12
#define ST_LSM6DSX_REG_RESET_MASK BIT(0)
+#define ST_LSM6DSX_REG_BOOT_MASK BIT(7)
#define ST_LSM6DSX_REG_BDU_ADDR 0x12
#define ST_LSM6DSX_REG_BDU_MASK BIT(6)
#define ST_LSM6DSX_REG_INT2_ON_INT1_ADDR 0x13
@@ -87,17 +88,6 @@
#define ST_LSM6DSX_GYRO_FS_1000_GAIN IIO_DEGREE_TO_RAD(35000)
#define ST_LSM6DSX_GYRO_FS_2000_GAIN IIO_DEGREE_TO_RAD(70000)
-struct st_lsm6dsx_odr {
- u16 hz;
- u8 val;
-};
-
-#define ST_LSM6DSX_ODR_LIST_SIZE 6
-struct st_lsm6dsx_odr_table_entry {
- struct st_lsm6dsx_reg reg;
- struct st_lsm6dsx_odr odr_avl[ST_LSM6DSX_ODR_LIST_SIZE];
-};
-
static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
[ST_LSM6DSX_ID_ACC] = {
.reg = {
@@ -125,17 +115,6 @@ static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
}
};
-struct st_lsm6dsx_fs {
- u32 gain;
- u8 val;
-};
-
-#define ST_LSM6DSX_FS_LIST_SIZE 4
-struct st_lsm6dsx_fs_table_entry {
- struct st_lsm6dsx_reg reg;
- struct st_lsm6dsx_fs fs_avl[ST_LSM6DSX_FS_LIST_SIZE];
-};
-
static const struct st_lsm6dsx_fs_table_entry st_lsm6dsx_fs_table[] = {
[ST_LSM6DSX_ID_ACC] = {
.reg = {
@@ -341,27 +320,35 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
.mask = GENMASK(7, 6),
},
},
+ .shub_settings = {
+ .page_mux = {
+ .addr = 0x01,
+ .mask = BIT(6),
+ },
+ .master_en = {
+ .addr = 0x14,
+ .mask = BIT(2),
+ },
+ .pullup_en = {
+ .addr = 0x14,
+ .mask = BIT(3),
+ },
+ .aux_sens = {
+ .addr = 0x14,
+ .mask = GENMASK(1, 0),
+ },
+ .wr_once = {
+ .addr = 0x14,
+ .mask = BIT(6),
+ },
+ .shub_out = 0x02,
+ .slv0_addr = 0x15,
+ .dw_slv0_addr = 0x21,
+ .batch_en = BIT(3),
+ }
},
};
-#define ST_LSM6DSX_CHANNEL(chan_type, addr, mod, scan_idx) \
-{ \
- .type = chan_type, \
- .address = addr, \
- .modified = 1, \
- .channel2 = mod, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_SCALE), \
- .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
- .scan_index = scan_idx, \
- .scan_type = { \
- .sign = 's', \
- .realbits = 16, \
- .storagebits = 16, \
- .endianness = IIO_LE, \
- }, \
-}
-
static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR,
IIO_MOD_X, 0),
@@ -382,6 +369,21 @@ static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
IIO_CHAN_SOFT_TIMESTAMP(3),
};
+int st_lsm6dsx_set_page(struct st_lsm6dsx_hw *hw, bool enable)
+{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
+ unsigned int data;
+ int err;
+
+ hub_settings = &hw->settings->shub_settings;
+ data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->page_mux.mask);
+ err = regmap_update_bits(hw->regmap, hub_settings->page_mux.addr,
+ hub_settings->page_mux.mask, data);
+ usleep_range(100, 150);
+
+ return err;
+}
+
static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
{
int err, i, j, data;
@@ -421,6 +423,7 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
{
struct st_lsm6dsx_hw *hw = sensor->hw;
const struct st_lsm6dsx_reg *reg;
+ unsigned int data;
int i, err;
u8 val;
@@ -433,8 +436,8 @@ static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
val = st_lsm6dsx_fs_table[sensor->id].fs_avl[i].val;
reg = &st_lsm6dsx_fs_table[sensor->id].reg;
- err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
- ST_LSM6DSX_SHIFT_VAL(val, reg->mask));
+ data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
+ err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
if (err < 0)
return err;
@@ -448,7 +451,11 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
int i;
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
- if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz == odr)
+ /*
+ * ext devices can run at different odr respect to
+ * accel sensor
+ */
+ if (st_lsm6dsx_odr_table[sensor->id].odr_avl[i].hz >= odr)
break;
if (i == ST_LSM6DSX_ODR_LIST_SIZE)
@@ -459,48 +466,86 @@ int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
return 0;
}
-static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
+static u16 st_lsm6dsx_check_odr_dependency(struct st_lsm6dsx_hw *hw, u16 odr,
+ enum st_lsm6dsx_sensor_id id)
{
- struct st_lsm6dsx_hw *hw = sensor->hw;
- const struct st_lsm6dsx_reg *reg;
- int err;
- u8 val;
-
- err = st_lsm6dsx_check_odr(sensor, odr, &val);
- if (err < 0)
- return err;
-
- reg = &st_lsm6dsx_odr_table[sensor->id].reg;
- return regmap_update_bits(hw->regmap, reg->addr, reg->mask,
- ST_LSM6DSX_SHIFT_VAL(val, reg->mask));
+ struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]);
+
+ if (odr > 0) {
+ if (hw->enable_mask & BIT(id))
+ return max_t(u16, ref->odr, odr);
+ else
+ return odr;
+ } else {
+ return (hw->enable_mask & BIT(id)) ? ref->odr : 0;
+ }
}
-int st_lsm6dsx_sensor_enable(struct st_lsm6dsx_sensor *sensor)
+static int st_lsm6dsx_set_odr(struct st_lsm6dsx_sensor *sensor, u16 req_odr)
{
+ struct st_lsm6dsx_sensor *ref_sensor = sensor;
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+ const struct st_lsm6dsx_reg *reg;
+ unsigned int data;
+ u8 val = 0;
int err;
- err = st_lsm6dsx_set_odr(sensor, sensor->odr);
- if (err < 0)
- return err;
+ switch (sensor->id) {
+ case ST_LSM6DSX_ID_EXT0:
+ case ST_LSM6DSX_ID_EXT1:
+ case ST_LSM6DSX_ID_EXT2:
+ case ST_LSM6DSX_ID_ACC: {
+ u16 odr;
+ int i;
+
+ /*
+ * i2c embedded controller relies on the accelerometer sensor as
+ * bus read/write trigger so we need to enable accel device
+ * at odr = max(accel_odr, ext_odr) in order to properly
+ * communicate with i2c slave devices
+ */
+ ref_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+ for (i = ST_LSM6DSX_ID_ACC; i < ST_LSM6DSX_ID_MAX; i++) {
+ if (!hw->iio_devs[i] || i == sensor->id)
+ continue;
+
+ odr = st_lsm6dsx_check_odr_dependency(hw, req_odr, i);
+ if (odr != req_odr)
+ /* device already configured */
+ return 0;
+ }
+ break;
+ }
+ default:
+ break;
+ }
- sensor->hw->enable_mask |= BIT(sensor->id);
+ if (req_odr > 0) {
+ err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val);
+ if (err < 0)
+ return err;
+ }
- return 0;
+ reg = &st_lsm6dsx_odr_table[ref_sensor->id].reg;
+ data = ST_LSM6DSX_SHIFT_VAL(val, reg->mask);
+ return st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask, data);
}
-int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
+int st_lsm6dsx_sensor_set_enable(struct st_lsm6dsx_sensor *sensor,
+ bool enable)
{
struct st_lsm6dsx_hw *hw = sensor->hw;
- const struct st_lsm6dsx_reg *reg;
+ u16 odr = enable ? sensor->odr : 0;
int err;
- reg = &st_lsm6dsx_odr_table[sensor->id].reg;
- err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
- ST_LSM6DSX_SHIFT_VAL(0, reg->mask));
+ err = st_lsm6dsx_set_odr(sensor, odr);
if (err < 0)
return err;
- sensor->hw->enable_mask &= ~BIT(sensor->id);
+ if (enable)
+ hw->enable_mask |= BIT(sensor->id);
+ else
+ hw->enable_mask &= ~BIT(sensor->id);
return 0;
}
@@ -512,18 +557,18 @@ static int st_lsm6dsx_read_oneshot(struct st_lsm6dsx_sensor *sensor,
int err, delay;
__le16 data;
- err = st_lsm6dsx_sensor_enable(sensor);
+ err = st_lsm6dsx_sensor_set_enable(sensor, true);
if (err < 0)
return err;
delay = 1000000 / sensor->odr;
usleep_range(delay, 2 * delay);
- err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data));
+ err = st_lsm6dsx_read_locked(hw, addr, &data, sizeof(data));
if (err < 0)
return err;
- st_lsm6dsx_sensor_disable(sensor);
+ st_lsm6dsx_sensor_set_enable(sensor, false);
*val = (s16)le16_to_cpu(data);
@@ -596,7 +641,7 @@ static int st_lsm6dsx_write_raw(struct iio_dev *iio_dev,
return err;
}
-static int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
+int st_lsm6dsx_set_watermark(struct iio_dev *iio_dev, unsigned int val)
{
struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
@@ -692,8 +737,6 @@ static const struct iio_info st_lsm6dsx_gyro_info = {
.hwfifo_set_watermark = st_lsm6dsx_set_watermark,
};
-static const unsigned long st_lsm6dsx_available_scan_masks[] = {0x7, 0x0};
-
static int st_lsm6dsx_of_get_drdy_pin(struct st_lsm6dsx_hw *hw, int *drdy_pin)
{
struct device_node *np = hw->dev->of_node;
@@ -732,6 +775,51 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg)
return err;
}
+static int st_lsm6dsx_init_shub(struct st_lsm6dsx_hw *hw)
+{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
+ struct device_node *np = hw->dev->of_node;
+ struct st_sensors_platform_data *pdata;
+ unsigned int data;
+ int err = 0;
+
+ hub_settings = &hw->settings->shub_settings;
+
+ pdata = (struct st_sensors_platform_data *)hw->dev->platform_data;
+ if ((np && of_property_read_bool(np, "st,pullups")) ||
+ (pdata && pdata->pullups)) {
+ err = st_lsm6dsx_set_page(hw, true);
+ if (err < 0)
+ return err;
+
+ data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->pullup_en.mask);
+ err = regmap_update_bits(hw->regmap,
+ hub_settings->pullup_en.addr,
+ hub_settings->pullup_en.mask, data);
+
+ st_lsm6dsx_set_page(hw, false);
+
+ if (err < 0)
+ return err;
+ }
+
+ if (hub_settings->aux_sens.addr) {
+ /* configure aux sensors */
+ err = st_lsm6dsx_set_page(hw, true);
+ if (err < 0)
+ return err;
+
+ data = ST_LSM6DSX_SHIFT_VAL(3, hub_settings->aux_sens.mask);
+ err = regmap_update_bits(hw->regmap,
+ hub_settings->aux_sens.addr,
+ hub_settings->aux_sens.mask, data);
+
+ st_lsm6dsx_set_page(hw, false);
+ }
+
+ return err;
+}
+
static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw)
{
const struct st_lsm6dsx_hw_ts_settings *ts_settings;
@@ -775,12 +863,23 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
u8 drdy_int_reg;
int err;
- err = regmap_write(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
- ST_LSM6DSX_REG_RESET_MASK);
+ /* device sw reset */
+ err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
+ ST_LSM6DSX_REG_RESET_MASK,
+ FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1));
+ if (err < 0)
+ return err;
+
+ msleep(50);
+
+ /* reload trimming parameter */
+ err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_RESET_ADDR,
+ ST_LSM6DSX_REG_BOOT_MASK,
+ FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1));
if (err < 0)
return err;
- msleep(200);
+ msleep(50);
/* enable Block Data Update */
err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR,
@@ -801,6 +900,10 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw)
if (err < 0)
return err;
+ err = st_lsm6dsx_init_shub(hw);
+ if (err < 0)
+ return err;
+
return st_lsm6dsx_init_hw_timer(hw);
}
@@ -854,6 +957,7 @@ static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw,
int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
struct regmap *regmap)
{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
struct st_lsm6dsx_hw *hw;
int i, err;
@@ -865,6 +969,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
mutex_init(&hw->fifo_lock);
mutex_init(&hw->conf_lock);
+ mutex_init(&hw->page_lock);
hw->buff = devm_kzalloc(dev, ST_LSM6DSX_BUFF_SIZE, GFP_KERNEL);
if (!hw->buff)
@@ -878,7 +983,7 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
if (err < 0)
return err;
- for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ for (i = 0; i < ST_LSM6DSX_ID_EXT0; i++) {
hw->iio_devs[i] = st_lsm6dsx_alloc_iiodev(hw, i, name);
if (!hw->iio_devs[i])
return -ENOMEM;
@@ -888,6 +993,13 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
if (err < 0)
return err;
+ hub_settings = &hw->settings->shub_settings;
+ if (hub_settings->master_en.addr) {
+ err = st_lsm6dsx_shub_probe(hw, name);
+ if (err < 0)
+ return err;
+ }
+
if (hw->irq > 0) {
err = st_lsm6dsx_fifo_setup(hw);
if (err < 0)
@@ -895,6 +1007,9 @@ int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
}
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ if (!hw->iio_devs[i])
+ continue;
+
err = devm_iio_device_register(hw->dev, hw->iio_devs[i]);
if (err)
return err;
@@ -909,16 +1024,21 @@ static int __maybe_unused st_lsm6dsx_suspend(struct device *dev)
struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev);
struct st_lsm6dsx_sensor *sensor;
const struct st_lsm6dsx_reg *reg;
+ unsigned int data;
int i, err = 0;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ if (!hw->iio_devs[i])
+ continue;
+
sensor = iio_priv(hw->iio_devs[i]);
if (!(hw->enable_mask & BIT(sensor->id)))
continue;
reg = &st_lsm6dsx_odr_table[sensor->id].reg;
- err = regmap_update_bits(hw->regmap, reg->addr, reg->mask,
- ST_LSM6DSX_SHIFT_VAL(0, reg->mask));
+ data = ST_LSM6DSX_SHIFT_VAL(0, reg->mask);
+ err = st_lsm6dsx_update_bits_locked(hw, reg->addr, reg->mask,
+ data);
if (err < 0)
return err;
}
@@ -936,6 +1056,9 @@ static int __maybe_unused st_lsm6dsx_resume(struct device *dev)
int i, err = 0;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
+ if (!hw->iio_devs[i])
+ continue;
+
sensor = iio_priv(hw->iio_devs[i]);
if (!(hw->enable_mask & BIT(sensor->id)))
continue;
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
new file mode 100644
index 000000000000..8e47dccdd40f
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
@@ -0,0 +1,779 @@
+/*
+ * STMicroelectronics st_lsm6dsx i2c controller driver
+ *
+ * i2c controller embedded in lsm6dx series can connect up to four
+ * slave devices using accelerometer sensor as trigger for i2c
+ * read/write operations. Current implementation relies on SLV0 channel
+ * for slave configuration and SLV{1,2,3} to read data and push them into
+ * the hw FIFO
+ *
+ * Copyright (C) 2018 Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/bitfield.h>
+
+#include "st_lsm6dsx.h"
+
+#define ST_LSM6DSX_MAX_SLV_NUM 3
+#define ST_LSM6DSX_SLV_ADDR(n, base) ((base) + (n) * 3)
+#define ST_LSM6DSX_SLV_SUB_ADDR(n, base) ((base) + 1 + (n) * 3)
+#define ST_LSM6DSX_SLV_CONFIG(n, base) ((base) + 2 + (n) * 3)
+
+#define ST_LS6DSX_READ_OP_MASK GENMASK(2, 0)
+
+static const struct st_lsm6dsx_ext_dev_settings st_lsm6dsx_ext_dev_table[] = {
+ /* LIS2MDL */
+ {
+ .i2c_addr = { 0x1e },
+ .wai = {
+ .addr = 0x4f,
+ .val = 0x40,
+ },
+ .id = ST_LSM6DSX_ID_MAGN,
+ .odr_table = {
+ .reg = {
+ .addr = 0x60,
+ .mask = GENMASK(3, 2),
+ },
+ .odr_avl[0] = { 10, 0x0 },
+ .odr_avl[1] = { 20, 0x1 },
+ .odr_avl[2] = { 50, 0x2 },
+ .odr_avl[3] = { 100, 0x3 },
+ },
+ .fs_table = {
+ .fs_avl[0] = {
+ .gain = 1500,
+ .val = 0x0,
+ }, /* 1500 uG/LSB */
+ },
+ .temp_comp = {
+ .addr = 0x60,
+ .mask = BIT(7),
+ },
+ .pwr_table = {
+ .reg = {
+ .addr = 0x60,
+ .mask = GENMASK(1, 0),
+ },
+ .off_val = 0x2,
+ .on_val = 0x0,
+ },
+ .off_canc = {
+ .addr = 0x61,
+ .mask = BIT(1),
+ },
+ .bdu = {
+ .addr = 0x62,
+ .mask = BIT(4),
+ },
+ .out = {
+ .addr = 0x68,
+ .len = 6,
+ },
+ },
+};
+
+static void st_lsm6dsx_shub_wait_complete(struct st_lsm6dsx_hw *hw)
+{
+ struct st_lsm6dsx_sensor *sensor;
+
+ sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+ msleep((2000U / sensor->odr) + 1);
+}
+
+/**
+ * st_lsm6dsx_shub_read_reg - read i2c controller register
+ *
+ * Read st_lsm6dsx i2c controller register
+ */
+static int st_lsm6dsx_shub_read_reg(struct st_lsm6dsx_hw *hw, u8 addr,
+ u8 *data, int len)
+{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
+ int err;
+
+ mutex_lock(&hw->page_lock);
+
+ hub_settings = &hw->settings->shub_settings;
+ err = st_lsm6dsx_set_page(hw, true);
+ if (err < 0)
+ goto out;
+
+ err = regmap_bulk_read(hw->regmap, addr, data, len);
+
+ st_lsm6dsx_set_page(hw, false);
+out:
+ mutex_unlock(&hw->page_lock);
+
+ return err;
+}
+
+/**
+ * st_lsm6dsx_shub_write_reg - write i2c controller register
+ *
+ * Write st_lsm6dsx i2c controller register
+ */
+static int st_lsm6dsx_shub_write_reg(struct st_lsm6dsx_hw *hw, u8 addr,
+ u8 *data, int len)
+{
+ int err;
+
+ mutex_lock(&hw->page_lock);
+ err = st_lsm6dsx_set_page(hw, true);
+ if (err < 0)
+ goto out;
+
+ err = regmap_bulk_write(hw->regmap, addr, data, len);
+
+ st_lsm6dsx_set_page(hw, false);
+out:
+ mutex_unlock(&hw->page_lock);
+
+ return err;
+}
+
+static int
+st_lsm6dsx_shub_write_reg_with_mask(struct st_lsm6dsx_hw *hw, u8 addr,
+ u8 mask, u8 val)
+{
+ int err;
+
+ mutex_lock(&hw->page_lock);
+ err = st_lsm6dsx_set_page(hw, true);
+ if (err < 0)
+ goto out;
+
+ err = regmap_update_bits(hw->regmap, addr, mask, val);
+
+ st_lsm6dsx_set_page(hw, false);
+out:
+ mutex_unlock(&hw->page_lock);
+
+ return err;
+}
+
+static int st_lsm6dsx_shub_master_enable(struct st_lsm6dsx_sensor *sensor,
+ bool enable)
+{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+ unsigned int data;
+ int err;
+
+ /* enable acc sensor as trigger */
+ err = st_lsm6dsx_sensor_set_enable(sensor, enable);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&hw->page_lock);
+
+ hub_settings = &hw->settings->shub_settings;
+ err = st_lsm6dsx_set_page(hw, true);
+ if (err < 0)
+ goto out;
+
+ data = ST_LSM6DSX_SHIFT_VAL(enable, hub_settings->master_en.mask);
+ err = regmap_update_bits(hw->regmap, hub_settings->master_en.addr,
+ hub_settings->master_en.mask, data);
+
+ st_lsm6dsx_set_page(hw, false);
+out:
+ mutex_unlock(&hw->page_lock);
+
+ return err;
+}
+
+/**
+ * st_lsm6dsx_shub_read - read data from slave device register
+ *
+ * Read data from slave device register. SLV0 is used for
+ * one-shot read operation
+ */
+static int
+st_lsm6dsx_shub_read(struct st_lsm6dsx_sensor *sensor, u8 addr,
+ u8 *data, int len)
+{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+ u8 config[3], slv_addr;
+ int err;
+
+ hub_settings = &hw->settings->shub_settings;
+ slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
+
+ config[0] = (sensor->ext_info.addr << 1) | 1;
+ config[1] = addr;
+ config[2] = len & ST_LS6DSX_READ_OP_MASK;
+
+ err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+ sizeof(config));
+ if (err < 0)
+ return err;
+
+ err = st_lsm6dsx_shub_master_enable(sensor, true);
+ if (err < 0)
+ return err;
+
+ st_lsm6dsx_shub_wait_complete(hw);
+
+ err = st_lsm6dsx_shub_read_reg(hw, hub_settings->shub_out, data,
+ len & ST_LS6DSX_READ_OP_MASK);
+
+ st_lsm6dsx_shub_master_enable(sensor, false);
+
+ memset(config, 0, sizeof(config));
+ return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+ sizeof(config));
+}
+
+/**
+ * st_lsm6dsx_shub_write - write data to slave device register
+ *
+ * Write data from slave device register. SLV0 is used for
+ * one-shot write operation
+ */
+static int
+st_lsm6dsx_shub_write(struct st_lsm6dsx_sensor *sensor, u8 addr,
+ u8 *data, int len)
+{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+ u8 config[2], slv_addr;
+ int err, i;
+
+ hub_settings = &hw->settings->shub_settings;
+ if (hub_settings->wr_once.addr) {
+ unsigned int data;
+
+ data = ST_LSM6DSX_SHIFT_VAL(1, hub_settings->wr_once.mask);
+ err = st_lsm6dsx_shub_write_reg_with_mask(hw,
+ hub_settings->wr_once.addr,
+ hub_settings->wr_once.mask,
+ data);
+ if (err < 0)
+ return err;
+ }
+
+ slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
+ config[0] = sensor->ext_info.addr << 1;
+ for (i = 0 ; i < len; i++) {
+ config[1] = addr + i;
+
+ err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+ sizeof(config));
+ if (err < 0)
+ return err;
+
+ err = st_lsm6dsx_shub_write_reg(hw, hub_settings->dw_slv0_addr,
+ &data[i], 1);
+ if (err < 0)
+ return err;
+
+ err = st_lsm6dsx_shub_master_enable(sensor, true);
+ if (err < 0)
+ return err;
+
+ st_lsm6dsx_shub_wait_complete(hw);
+
+ st_lsm6dsx_shub_master_enable(sensor, false);
+ }
+
+ memset(config, 0, sizeof(config));
+ return st_lsm6dsx_shub_write_reg(hw, slv_addr, config, sizeof(config));
+}
+
+static int
+st_lsm6dsx_shub_write_with_mask(struct st_lsm6dsx_sensor *sensor,
+ u8 addr, u8 mask, u8 val)
+{
+ int err;
+ u8 data;
+
+ err = st_lsm6dsx_shub_read(sensor, addr, &data, sizeof(data));
+ if (err < 0)
+ return err;
+
+ data = ((data & ~mask) | (val << __ffs(mask) & mask));
+
+ return st_lsm6dsx_shub_write(sensor, addr, &data, sizeof(data));
+}
+
+static int
+st_lsm6dsx_shub_get_odr_val(struct st_lsm6dsx_sensor *sensor,
+ u16 odr, u16 *val)
+{
+ const struct st_lsm6dsx_ext_dev_settings *settings;
+ int i;
+
+ settings = sensor->ext_info.settings;
+ for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
+ if (settings->odr_table.odr_avl[i].hz == odr)
+ break;
+
+ if (i == ST_LSM6DSX_ODR_LIST_SIZE)
+ return -EINVAL;
+
+ *val = settings->odr_table.odr_avl[i].val;
+ return 0;
+}
+
+static int
+st_lsm6dsx_shub_set_odr(struct st_lsm6dsx_sensor *sensor, u16 odr)
+{
+ const struct st_lsm6dsx_ext_dev_settings *settings;
+ u16 val;
+ int err;
+
+ err = st_lsm6dsx_shub_get_odr_val(sensor, odr, &val);
+ if (err < 0)
+ return err;
+
+ settings = sensor->ext_info.settings;
+ return st_lsm6dsx_shub_write_with_mask(sensor,
+ settings->odr_table.reg.addr,
+ settings->odr_table.reg.mask,
+ val);
+}
+
+/* use SLV{1,2,3} for FIFO read operations */
+static int
+st_lsm6dsx_shub_config_channels(struct st_lsm6dsx_sensor *sensor,
+ bool enable)
+{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
+ const struct st_lsm6dsx_ext_dev_settings *settings;
+ u8 config[9] = {}, enable_mask, slv_addr;
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+ struct st_lsm6dsx_sensor *cur_sensor;
+ int i, j = 0;
+
+ hub_settings = &hw->settings->shub_settings;
+ if (enable)
+ enable_mask = hw->enable_mask | BIT(sensor->id);
+ else
+ enable_mask = hw->enable_mask & ~BIT(sensor->id);
+
+ for (i = ST_LSM6DSX_ID_EXT0; i <= ST_LSM6DSX_ID_EXT2; i++) {
+ if (!hw->iio_devs[i])
+ continue;
+
+ cur_sensor = iio_priv(hw->iio_devs[i]);
+ if (!(enable_mask & BIT(cur_sensor->id)))
+ continue;
+
+ settings = cur_sensor->ext_info.settings;
+ config[j] = (sensor->ext_info.addr << 1) | 1;
+ config[j + 1] = settings->out.addr;
+ config[j + 2] = (settings->out.len & ST_LS6DSX_READ_OP_MASK) |
+ hub_settings->batch_en;
+ j += 3;
+ }
+
+ slv_addr = ST_LSM6DSX_SLV_ADDR(1, hub_settings->slv0_addr);
+ return st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+ sizeof(config));
+}
+
+int st_lsm6dsx_shub_set_enable(struct st_lsm6dsx_sensor *sensor, bool enable)
+{
+ const struct st_lsm6dsx_ext_dev_settings *settings;
+ int err;
+
+ err = st_lsm6dsx_shub_config_channels(sensor, enable);
+ if (err < 0)
+ return err;
+
+ settings = sensor->ext_info.settings;
+ if (enable) {
+ err = st_lsm6dsx_shub_set_odr(sensor, sensor->odr);
+ if (err < 0)
+ return err;
+ } else {
+ err = st_lsm6dsx_shub_write_with_mask(sensor,
+ settings->odr_table.reg.addr,
+ settings->odr_table.reg.mask, 0);
+ if (err < 0)
+ return err;
+ }
+
+ if (settings->pwr_table.reg.addr) {
+ u8 val;
+
+ val = enable ? settings->pwr_table.on_val
+ : settings->pwr_table.off_val;
+ err = st_lsm6dsx_shub_write_with_mask(sensor,
+ settings->pwr_table.reg.addr,
+ settings->pwr_table.reg.mask, val);
+ if (err < 0)
+ return err;
+ }
+
+ return st_lsm6dsx_shub_master_enable(sensor, enable);
+}
+
+static int
+st_lsm6dsx_shub_read_oneshot(struct st_lsm6dsx_sensor *sensor,
+ struct iio_chan_spec const *ch,
+ int *val)
+{
+ int err, delay, len;
+ u8 data[4];
+
+ err = st_lsm6dsx_shub_set_enable(sensor, true);
+ if (err < 0)
+ return err;
+
+ delay = 1000000 / sensor->odr;
+ usleep_range(delay, 2 * delay);
+
+ len = min_t(int, sizeof(data), ch->scan_type.realbits >> 3);
+ err = st_lsm6dsx_shub_read(sensor, ch->address, data, len);
+
+ st_lsm6dsx_shub_set_enable(sensor, false);
+
+ if (err < 0)
+ return err;
+
+ switch (len) {
+ case 2:
+ *val = (s16)le16_to_cpu(*((__le16 *)data));
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_INT;
+}
+
+static int
+st_lsm6dsx_shub_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *ch,
+ int *val, int *val2, long mask)
+{
+ struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(iio_dev);
+ if (ret)
+ break;
+
+ ret = st_lsm6dsx_shub_read_oneshot(sensor, ch, val);
+ iio_device_release_direct_mode(iio_dev);
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = sensor->odr;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = sensor->gain;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int
+st_lsm6dsx_shub_write_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+ int err;
+
+ err = iio_device_claim_direct_mode(iio_dev);
+ if (err)
+ return err;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ: {
+ u16 data;
+
+ err = st_lsm6dsx_shub_get_odr_val(sensor, val, &data);
+ if (!err)
+ sensor->odr = val;
+ break;
+ }
+ default:
+ err = -EINVAL;
+ break;
+ }
+
+ iio_device_release_direct_mode(iio_dev);
+
+ return err;
+}
+
+static ssize_t
+st_lsm6dsx_shub_sampling_freq_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+ const struct st_lsm6dsx_ext_dev_settings *settings;
+ int i, len = 0;
+
+ settings = sensor->ext_info.settings;
+ for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++) {
+ u16 val = settings->odr_table.odr_avl[i].hz;
+
+ if (val > 0)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
+ val);
+ }
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t st_lsm6dsx_shub_scale_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
+ const struct st_lsm6dsx_ext_dev_settings *settings;
+ int i, len = 0;
+
+ settings = sensor->ext_info.settings;
+ for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++) {
+ u16 val = settings->fs_table.fs_avl[i].gain;
+
+ if (val > 0)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
+ val);
+ }
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(st_lsm6dsx_shub_sampling_freq_avail);
+static IIO_DEVICE_ATTR(in_scale_available, 0444,
+ st_lsm6dsx_shub_scale_avail, NULL, 0);
+static struct attribute *st_lsm6dsx_ext_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_in_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group st_lsm6dsx_ext_attribute_group = {
+ .attrs = st_lsm6dsx_ext_attributes,
+};
+
+static const struct iio_info st_lsm6dsx_ext_info = {
+ .attrs = &st_lsm6dsx_ext_attribute_group,
+ .read_raw = st_lsm6dsx_shub_read_raw,
+ .write_raw = st_lsm6dsx_shub_write_raw,
+ .hwfifo_set_watermark = st_lsm6dsx_set_watermark,
+};
+
+static struct iio_dev *
+st_lsm6dsx_shub_alloc_iiodev(struct st_lsm6dsx_hw *hw,
+ enum st_lsm6dsx_sensor_id id,
+ const struct st_lsm6dsx_ext_dev_settings *info,
+ u8 i2c_addr, const char *name)
+{
+ struct iio_chan_spec *ext_channels;
+ struct st_lsm6dsx_sensor *sensor;
+ struct iio_dev *iio_dev;
+
+ iio_dev = devm_iio_device_alloc(hw->dev, sizeof(*sensor));
+ if (!iio_dev)
+ return NULL;
+
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->dev.parent = hw->dev;
+ iio_dev->info = &st_lsm6dsx_ext_info;
+
+ sensor = iio_priv(iio_dev);
+ sensor->id = id;
+ sensor->hw = hw;
+ sensor->odr = info->odr_table.odr_avl[0].hz;
+ sensor->gain = info->fs_table.fs_avl[0].gain;
+ sensor->ext_info.settings = info;
+ sensor->ext_info.addr = i2c_addr;
+ sensor->watermark = 1;
+
+ switch (info->id) {
+ case ST_LSM6DSX_ID_MAGN: {
+ const struct iio_chan_spec magn_channels[] = {
+ ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr,
+ IIO_MOD_X, 0),
+ ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 2,
+ IIO_MOD_Y, 1),
+ ST_LSM6DSX_CHANNEL(IIO_MAGN, info->out.addr + 4,
+ IIO_MOD_Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+ };
+
+ ext_channels = devm_kzalloc(hw->dev, sizeof(magn_channels),
+ GFP_KERNEL);
+ if (!ext_channels)
+ return NULL;
+
+ memcpy(ext_channels, magn_channels, sizeof(magn_channels));
+ iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
+ iio_dev->channels = ext_channels;
+ iio_dev->num_channels = ARRAY_SIZE(magn_channels);
+
+ scnprintf(sensor->name, sizeof(sensor->name), "%s_magn",
+ name);
+ break;
+ }
+ default:
+ return NULL;
+ }
+ iio_dev->name = sensor->name;
+
+ return iio_dev;
+}
+
+static int st_lsm6dsx_shub_init_device(struct st_lsm6dsx_sensor *sensor)
+{
+ const struct st_lsm6dsx_ext_dev_settings *settings;
+ int err;
+
+ settings = sensor->ext_info.settings;
+ if (settings->bdu.addr) {
+ err = st_lsm6dsx_shub_write_with_mask(sensor,
+ settings->bdu.addr,
+ settings->bdu.mask, 1);
+ if (err < 0)
+ return err;
+ }
+
+ if (settings->temp_comp.addr) {
+ err = st_lsm6dsx_shub_write_with_mask(sensor,
+ settings->temp_comp.addr,
+ settings->temp_comp.mask, 1);
+ if (err < 0)
+ return err;
+ }
+
+ if (settings->off_canc.addr) {
+ err = st_lsm6dsx_shub_write_with_mask(sensor,
+ settings->off_canc.addr,
+ settings->off_canc.mask, 1);
+ if (err < 0)
+ return err;
+ }
+
+ return 0;
+}
+
+static int
+st_lsm6dsx_shub_check_wai(struct st_lsm6dsx_hw *hw, u8 *i2c_addr,
+ const struct st_lsm6dsx_ext_dev_settings *settings)
+{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
+ struct st_lsm6dsx_sensor *sensor;
+ u8 config[3], data, slv_addr;
+ bool found = false;
+ int i, err;
+
+ hub_settings = &hw->settings->shub_settings;
+ slv_addr = ST_LSM6DSX_SLV_ADDR(0, hub_settings->slv0_addr);
+ sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+
+ for (i = 0; i < ARRAY_SIZE(settings->i2c_addr); i++) {
+ if (!settings->i2c_addr[i])
+ continue;
+
+ /* read wai slave register */
+ config[0] = (settings->i2c_addr[i] << 1) | 0x1;
+ config[1] = settings->wai.addr;
+ config[2] = 0x1;
+
+ err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+ sizeof(config));
+ if (err < 0)
+ return err;
+
+ err = st_lsm6dsx_shub_master_enable(sensor, true);
+ if (err < 0)
+ return err;
+
+ st_lsm6dsx_shub_wait_complete(hw);
+
+ err = st_lsm6dsx_shub_read_reg(hw,
+ hub_settings->shub_out,
+ &data, sizeof(data));
+
+ st_lsm6dsx_shub_master_enable(sensor, false);
+
+ if (err < 0)
+ return err;
+
+ if (data != settings->wai.val)
+ continue;
+
+ *i2c_addr = settings->i2c_addr[i];
+ found = true;
+ break;
+ }
+
+ /* reset SLV0 channel */
+ memset(config, 0, sizeof(config));
+ err = st_lsm6dsx_shub_write_reg(hw, slv_addr, config,
+ sizeof(config));
+ if (err < 0)
+ return err;
+
+ return found ? 0 : -ENODEV;
+}
+
+int st_lsm6dsx_shub_probe(struct st_lsm6dsx_hw *hw, const char *name)
+{
+ enum st_lsm6dsx_sensor_id id = ST_LSM6DSX_ID_EXT0;
+ struct st_lsm6dsx_sensor *sensor;
+ int err, i, num_ext_dev = 0;
+ u8 i2c_addr = 0;
+
+ for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_ext_dev_table); i++) {
+ err = st_lsm6dsx_shub_check_wai(hw, &i2c_addr,
+ &st_lsm6dsx_ext_dev_table[i]);
+ if (err == -ENODEV)
+ continue;
+ else if (err < 0)
+ return err;
+
+ hw->iio_devs[id] = st_lsm6dsx_shub_alloc_iiodev(hw, id,
+ &st_lsm6dsx_ext_dev_table[i],
+ i2c_addr, name);
+ if (!hw->iio_devs[id])
+ return -ENOMEM;
+
+ sensor = iio_priv(hw->iio_devs[id]);
+ err = st_lsm6dsx_shub_init_device(sensor);
+ if (err < 0)
+ return err;
+
+ if (++num_ext_dev >= ST_LSM6DSX_MAX_SLV_NUM)
+ break;
+ id++;
+ }
+
+ return 0;
+}
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index a062cfddc5af..4f5cd9f60870 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1671,6 +1671,9 @@ int __iio_device_register(struct iio_dev *indio_dev, struct module *this_mod)
if (ret < 0)
return ret;
+ if (!indio_dev->info)
+ return -EINVAL;
+
/* configure elements for the chrdev */
indio_dev->dev.devt = MKDEV(MAJOR(iio_devt), indio_dev->id);
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index d66ea754ffff..36f458433480 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -460,6 +460,19 @@ config VCNL4000
To compile this driver as a module, choose M here: the
module will be called vcnl4000.
+config VCNL4035
+ tristate "VCNL4035 combined ALS and proximity sensor"
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP_I2C
+ depends on I2C
+ help
+ Say Y here if you want to build a driver for the Vishay VCNL4035,
+ combined ambient light (ALS) and proximity sensor. Currently only ALS
+ function is available.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vcnl4035.
+
config VEML6070
tristate "VEML6070 UV A light sensor"
depends on I2C
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 86337b114bc4..286bf3975372 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -45,6 +45,7 @@ obj-$(CONFIG_TSL2772) += tsl2772.o
obj-$(CONFIG_TSL4531) += tsl4531.o
obj-$(CONFIG_US5182D) += us5182d.o
obj-$(CONFIG_VCNL4000) += vcnl4000.o
+obj-$(CONFIG_VCNL4035) += vcnl4035.o
obj-$(CONFIG_VEML6070) += veml6070.o
obj-$(CONFIG_VL6180) += vl6180.o
obj-$(CONFIG_ZOPT2201) += zopt2201.o
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index 406caaee9a3c..94f33250ba5a 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -93,6 +93,7 @@ static int als_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
+ s32 min;
*val = 0;
*val2 = 0;
@@ -102,8 +103,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
case CHANNEL_SCAN_INDEX_INTENSITY:
case CHANNEL_SCAN_INDEX_ILLUM:
report_id = als_state->als_illum.report_id;
- address =
- HID_USAGE_SENSOR_LIGHT_ILLUM;
+ min = als_state->als_illum.logical_minimum;
+ address = HID_USAGE_SENSOR_LIGHT_ILLUM;
break;
default:
report_id = -1;
@@ -116,7 +117,8 @@ static int als_read_raw(struct iio_dev *indio_dev,
als_state->common_attributes.hsdev,
HID_USAGE_SENSOR_ALS, address,
report_id,
- SENSOR_HUB_SYNC);
+ SENSOR_HUB_SYNC,
+ min < 0);
hid_sensor_power_state(&als_state->common_attributes,
false);
} else {
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index 45107f7537b5..cf5a0c242609 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -73,6 +73,7 @@ static int prox_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
+ s32 min;
*val = 0;
*val2 = 0;
@@ -81,8 +82,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
switch (chan->scan_index) {
case CHANNEL_SCAN_INDEX_PRESENCE:
report_id = prox_state->prox_attr.report_id;
- address =
- HID_USAGE_SENSOR_HUMAN_PRESENCE;
+ min = prox_state->prox_attr.logical_minimum;
+ address = HID_USAGE_SENSOR_HUMAN_PRESENCE;
break;
default:
report_id = -1;
@@ -95,7 +96,8 @@ static int prox_read_raw(struct iio_dev *indio_dev,
prox_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PROX, address,
report_id,
- SENSOR_HUB_SYNC);
+ SENSOR_HUB_SYNC,
+ min < 0);
hid_sensor_power_state(&prox_state->common_attributes,
false);
} else {
diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c
new file mode 100644
index 000000000000..cca4db312bd3
--- /dev/null
+++ b/drivers/iio/light/vcnl4035.c
@@ -0,0 +1,676 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * VCNL4035 Ambient Light and Proximity Sensor - 7-bit I2C slave address 0x60
+ *
+ * Copyright (c) 2018, DENX Software Engineering GmbH
+ * Author: Parthiban Nallathambi <pn@denx.de>
+ *
+ * TODO: Proximity
+ */
+#include <linux/bitops.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.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 VCNL4035_DRV_NAME "vcnl4035"
+#define VCNL4035_IRQ_NAME "vcnl4035_event"
+#define VCNL4035_REGMAP_NAME "vcnl4035_regmap"
+
+/* Device registers */
+#define VCNL4035_ALS_CONF 0x00
+#define VCNL4035_ALS_THDH 0x01
+#define VCNL4035_ALS_THDL 0x02
+#define VCNL4035_ALS_DATA 0x0B
+#define VCNL4035_WHITE_DATA 0x0C
+#define VCNL4035_INT_FLAG 0x0D
+#define VCNL4035_DEV_ID 0x0E
+
+/* Register masks */
+#define VCNL4035_MODE_ALS_MASK BIT(0)
+#define VCNL4035_MODE_ALS_WHITE_CHAN BIT(8)
+#define VCNL4035_MODE_ALS_INT_MASK BIT(1)
+#define VCNL4035_ALS_IT_MASK GENMASK(7, 5)
+#define VCNL4035_ALS_PERS_MASK GENMASK(3, 2)
+#define VCNL4035_INT_ALS_IF_H_MASK BIT(12)
+#define VCNL4035_INT_ALS_IF_L_MASK BIT(13)
+
+/* Default values */
+#define VCNL4035_MODE_ALS_ENABLE BIT(0)
+#define VCNL4035_MODE_ALS_DISABLE 0x00
+#define VCNL4035_MODE_ALS_INT_ENABLE BIT(1)
+#define VCNL4035_MODE_ALS_INT_DISABLE 0
+#define VCNL4035_DEV_ID_VAL 0x80
+#define VCNL4035_ALS_IT_DEFAULT 0x01
+#define VCNL4035_ALS_PERS_DEFAULT 0x00
+#define VCNL4035_ALS_THDH_DEFAULT 5000
+#define VCNL4035_ALS_THDL_DEFAULT 100
+#define VCNL4035_SLEEP_DELAY_MS 2000
+
+struct vcnl4035_data {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ unsigned int als_it_val;
+ unsigned int als_persistence;
+ unsigned int als_thresh_low;
+ unsigned int als_thresh_high;
+ struct iio_trigger *drdy_trigger0;
+};
+
+static inline bool vcnl4035_is_triggered(struct vcnl4035_data *data)
+{
+ int ret;
+ int reg;
+
+ ret = regmap_read(data->regmap, VCNL4035_INT_FLAG, &reg);
+ if (ret < 0)
+ return false;
+
+ return !!(reg &
+ (VCNL4035_INT_ALS_IF_H_MASK | VCNL4035_INT_ALS_IF_L_MASK));
+}
+
+static irqreturn_t vcnl4035_drdy_irq_thread(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+
+ if (vcnl4035_is_triggered(data)) {
+ iio_push_event(indio_dev, IIO_UNMOD_EVENT_CODE(IIO_LIGHT,
+ 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns(indio_dev));
+ iio_trigger_poll_chained(data->drdy_trigger0);
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+/* Triggered buffer */
+static irqreturn_t vcnl4035_trigger_consumer_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+ u8 buffer[ALIGN(sizeof(u16), sizeof(s64)) + sizeof(s64)];
+ int ret;
+
+ ret = regmap_read(data->regmap, VCNL4035_ALS_DATA, (int *)buffer);
+ if (ret < 0) {
+ dev_err(&data->client->dev,
+ "Trigger consumer can't read from sensor.\n");
+ goto fail_read;
+ }
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ iio_get_time_ns(indio_dev));
+
+fail_read:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int vcnl4035_als_drdy_set_state(struct iio_trigger *trigger,
+ bool enable_drdy)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trigger);
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+ int val = enable_drdy ? VCNL4035_MODE_ALS_INT_ENABLE :
+ VCNL4035_MODE_ALS_INT_DISABLE;
+
+ return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
+ VCNL4035_MODE_ALS_INT_MASK,
+ val);
+}
+
+static const struct iio_trigger_ops vcnl4035_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+ .set_trigger_state = vcnl4035_als_drdy_set_state,
+};
+
+static int vcnl4035_set_pm_runtime_state(struct vcnl4035_data *data, bool on)
+{
+ int ret;
+ struct device *dev = &data->client->dev;
+
+ if (on) {
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ pm_runtime_put_noidle(dev);
+ } else {
+ pm_runtime_mark_last_busy(dev);
+ ret = pm_runtime_put_autosuspend(dev);
+ }
+
+ return ret;
+}
+
+/*
+ * Device IT INT Time (ms) Scale (lux/step)
+ * 000 50 0.064
+ * 001 100 0.032
+ * 010 200 0.016
+ * 100 400 0.008
+ * 101 - 111 800 0.004
+ * Values are proportional, so ALS INT is selected for input due to
+ * simplicity reason. Integration time value and scaling is
+ * calculated based on device INT value
+ *
+ * Raw value needs to be scaled using ALS steps
+ */
+static int vcnl4035_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+ int ret;
+ int raw_data;
+ unsigned int reg;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = vcnl4035_set_pm_runtime_state(data, true);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (!ret) {
+ if (chan->channel)
+ reg = VCNL4035_ALS_DATA;
+ else
+ reg = VCNL4035_WHITE_DATA;
+ ret = regmap_read(data->regmap, reg, &raw_data);
+ iio_device_release_direct_mode(indio_dev);
+ if (!ret) {
+ *val = raw_data;
+ ret = IIO_VAL_INT;
+ }
+ }
+ vcnl4035_set_pm_runtime_state(data, false);
+ return ret;
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 50;
+ if (data->als_it_val)
+ *val = data->als_it_val * 100;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 64;
+ if (!data->als_it_val)
+ *val2 = 1000;
+ else
+ *val2 = data->als_it_val * 2 * 1000;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int vcnl4035_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret;
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val <= 0 || val > 800)
+ return -EINVAL;
+
+ ret = vcnl4035_set_pm_runtime_state(data, true);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
+ VCNL4035_ALS_IT_MASK,
+ val / 100);
+ if (!ret)
+ data->als_it_val = val / 100;
+
+ vcnl4035_set_pm_runtime_state(data, false);
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+/* No direct ABI for persistence and threshold, so eventing */
+static int vcnl4035_read_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info,
+ int *val, int *val2)
+{
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ *val = data->als_thresh_high;
+ return IIO_VAL_INT;
+ case IIO_EV_DIR_FALLING:
+ *val = data->als_thresh_low;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_EV_INFO_PERIOD:
+ *val = data->als_persistence;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+}
+
+static int vcnl4035_write_thresh(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, enum iio_event_type type,
+ enum iio_event_direction dir, enum iio_event_info info, int val,
+ int val2)
+{
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_EV_INFO_VALUE:
+ /* 16 bit threshold range 0 - 65535 */
+ if (val < 0 || val > 65535)
+ return -EINVAL;
+ if (dir == IIO_EV_DIR_RISING) {
+ if (val < data->als_thresh_low)
+ return -EINVAL;
+ ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,
+ val);
+ if (ret)
+ return ret;
+ data->als_thresh_high = val;
+ } else {
+ if (val > data->als_thresh_high)
+ return -EINVAL;
+ ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,
+ val);
+ if (ret)
+ return ret;
+ data->als_thresh_low = val;
+ }
+ return ret;
+ case IIO_EV_INFO_PERIOD:
+ /* allow only 1 2 4 8 as persistence value */
+ if (val < 0 || val > 8 || hweight8(val) != 1)
+ return -EINVAL;
+ ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
+ VCNL4035_ALS_PERS_MASK, val);
+ if (!ret)
+ data->als_persistence = val;
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+static IIO_CONST_ATTR_INT_TIME_AVAIL("50 100 200 400 800");
+
+static struct attribute *vcnl4035_attributes[] = {
+ &iio_const_attr_integration_time_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group vcnl4035_attribute_group = {
+ .attrs = vcnl4035_attributes,
+};
+
+static const struct iio_info vcnl4035_info = {
+ .read_raw = vcnl4035_read_raw,
+ .write_raw = vcnl4035_write_raw,
+ .read_event_value = vcnl4035_read_thresh,
+ .write_event_value = vcnl4035_write_thresh,
+ .attrs = &vcnl4035_attribute_group,
+};
+
+static const struct iio_event_spec vcnl4035_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE),
+ }, {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_EITHER,
+ .mask_separate = BIT(IIO_EV_INFO_PERIOD),
+ },
+};
+
+enum vcnl4035_scan_index_order {
+ VCNL4035_CHAN_INDEX_LIGHT,
+ VCNL4035_CHAN_INDEX_WHITE_LED,
+};
+
+static const struct iio_buffer_setup_ops iio_triggered_buffer_setup_ops = {
+ .validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
+static const struct iio_chan_spec vcnl4035_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_INT_TIME) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .event_spec = vcnl4035_event_spec,
+ .num_event_specs = ARRAY_SIZE(vcnl4035_event_spec),
+ .scan_index = VCNL4035_CHAN_INDEX_LIGHT,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ },
+ {
+ .type = IIO_INTENSITY,
+ .channel = 1,
+ .modified = 1,
+ .channel2 = IIO_MOD_LIGHT_BOTH,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .scan_index = VCNL4035_CHAN_INDEX_WHITE_LED,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ },
+};
+
+static int vcnl4035_set_als_power_state(struct vcnl4035_data *data, u8 status)
+{
+ return regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
+ VCNL4035_MODE_ALS_MASK,
+ status);
+}
+
+static int vcnl4035_init(struct vcnl4035_data *data)
+{
+ int ret;
+ int id;
+
+ ret = regmap_read(data->regmap, VCNL4035_DEV_ID, &id);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "Failed to read DEV_ID register\n");
+ return ret;
+ }
+
+ if (id != VCNL4035_DEV_ID_VAL) {
+ dev_err(&data->client->dev, "Wrong id, got %x, expected %x\n",
+ id, VCNL4035_DEV_ID_VAL);
+ return -ENODEV;
+ }
+
+ ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* ALS white channel enable */
+ ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
+ VCNL4035_MODE_ALS_WHITE_CHAN,
+ 1);
+ if (ret) {
+ dev_err(&data->client->dev, "set white channel enable %d\n",
+ ret);
+ return ret;
+ }
+
+ /* set default integration time - 100 ms for ALS */
+ ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
+ VCNL4035_ALS_IT_MASK,
+ VCNL4035_ALS_IT_DEFAULT);
+ if (ret) {
+ dev_err(&data->client->dev, "set default ALS IT returned %d\n",
+ ret);
+ return ret;
+ }
+ data->als_it_val = VCNL4035_ALS_IT_DEFAULT;
+
+ /* set default persistence time - 1 for ALS */
+ ret = regmap_update_bits(data->regmap, VCNL4035_ALS_CONF,
+ VCNL4035_ALS_PERS_MASK,
+ VCNL4035_ALS_PERS_DEFAULT);
+ if (ret) {
+ dev_err(&data->client->dev, "set default PERS returned %d\n",
+ ret);
+ return ret;
+ }
+ data->als_persistence = VCNL4035_ALS_PERS_DEFAULT;
+
+ /* set default HIGH threshold for ALS */
+ ret = regmap_write(data->regmap, VCNL4035_ALS_THDH,
+ VCNL4035_ALS_THDH_DEFAULT);
+ if (ret) {
+ dev_err(&data->client->dev, "set default THDH returned %d\n",
+ ret);
+ return ret;
+ }
+ data->als_thresh_high = VCNL4035_ALS_THDH_DEFAULT;
+
+ /* set default LOW threshold for ALS */
+ ret = regmap_write(data->regmap, VCNL4035_ALS_THDL,
+ VCNL4035_ALS_THDL_DEFAULT);
+ if (ret) {
+ dev_err(&data->client->dev, "set default THDL returned %d\n",
+ ret);
+ return ret;
+ }
+ data->als_thresh_low = VCNL4035_ALS_THDL_DEFAULT;
+
+ return 0;
+}
+
+static bool vcnl4035_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case VCNL4035_ALS_CONF:
+ case VCNL4035_DEV_ID:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static const struct regmap_config vcnl4035_regmap_config = {
+ .name = VCNL4035_REGMAP_NAME,
+ .reg_bits = 8,
+ .val_bits = 16,
+ .max_register = VCNL4035_DEV_ID,
+ .cache_type = REGCACHE_RBTREE,
+ .volatile_reg = vcnl4035_is_volatile_reg,
+ .val_format_endian = REGMAP_ENDIAN_LITTLE,
+};
+
+static int vcnl4035_probe_trigger(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+
+ data->drdy_trigger0 = devm_iio_trigger_alloc(
+ indio_dev->dev.parent,
+ "%s-dev%d", indio_dev->name, indio_dev->id);
+ if (!data->drdy_trigger0)
+ return -ENOMEM;
+
+ data->drdy_trigger0->dev.parent = indio_dev->dev.parent;
+ data->drdy_trigger0->ops = &vcnl4035_trigger_ops;
+ iio_trigger_set_drvdata(data->drdy_trigger0, indio_dev);
+ ret = devm_iio_trigger_register(indio_dev->dev.parent,
+ data->drdy_trigger0);
+ if (ret) {
+ dev_err(&data->client->dev, "iio trigger register failed\n");
+ return ret;
+ }
+
+ /* Trigger setup */
+ ret = devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
+ NULL, vcnl4035_trigger_consumer_handler,
+ &iio_triggered_buffer_setup_ops);
+ if (ret < 0) {
+ dev_err(&data->client->dev, "iio triggered buffer setup failed\n");
+ return ret;
+ }
+
+ /* IRQ to trigger mapping */
+ ret = devm_request_threaded_irq(&data->client->dev, data->client->irq,
+ NULL, vcnl4035_drdy_irq_thread,
+ IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ VCNL4035_IRQ_NAME, indio_dev);
+ if (ret < 0)
+ dev_err(&data->client->dev, "request irq %d for trigger0 failed\n",
+ data->client->irq);
+ return ret;
+}
+
+static int vcnl4035_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct vcnl4035_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(client, &vcnl4035_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "regmap_init failed!\n");
+ return PTR_ERR(regmap);
+ }
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ data->regmap = regmap;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &vcnl4035_info;
+ indio_dev->name = VCNL4035_DRV_NAME;
+ indio_dev->channels = vcnl4035_channels;
+ indio_dev->num_channels = ARRAY_SIZE(vcnl4035_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = vcnl4035_init(data);
+ if (ret < 0) {
+ dev_err(&client->dev, "vcnl4035 chip init failed\n");
+ return ret;
+ }
+
+ if (client->irq > 0) {
+ ret = vcnl4035_probe_trigger(indio_dev);
+ if (ret < 0) {
+ dev_err(&client->dev, "vcnl4035 unable init trigger\n");
+ goto fail_poweroff;
+ }
+ }
+
+ ret = pm_runtime_set_active(&client->dev);
+ if (ret < 0)
+ goto fail_poweroff;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto fail_poweroff;
+
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, VCNL4035_SLEEP_DELAY_MS);
+ pm_runtime_use_autosuspend(&client->dev);
+
+ return 0;
+
+fail_poweroff:
+ vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);
+ return ret;
+}
+
+static int vcnl4035_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+
+ pm_runtime_dont_use_autosuspend(&client->dev);
+ pm_runtime_disable(&client->dev);
+ iio_device_unregister(indio_dev);
+ pm_runtime_set_suspended(&client->dev);
+
+ return vcnl4035_set_als_power_state(iio_priv(indio_dev),
+ VCNL4035_MODE_ALS_DISABLE);
+}
+
+static int __maybe_unused vcnl4035_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_DISABLE);
+ regcache_mark_dirty(data->regmap);
+
+ return ret;
+}
+
+static int __maybe_unused vcnl4035_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+ struct vcnl4035_data *data = iio_priv(indio_dev);
+ int ret;
+
+ regcache_sync(data->regmap);
+ ret = vcnl4035_set_als_power_state(data, VCNL4035_MODE_ALS_ENABLE);
+ if (ret < 0)
+ return ret;
+
+ /* wait for 1 ALS integration cycle */
+ msleep(data->als_it_val * 100);
+
+ return 0;
+}
+
+static const struct dev_pm_ops vcnl4035_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(vcnl4035_runtime_suspend,
+ vcnl4035_runtime_resume, NULL)
+};
+
+static const struct of_device_id vcnl4035_of_match[] = {
+ { .compatible = "vishay,vcnl4035", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, vcnl4035_of_match);
+
+static struct i2c_driver vcnl4035_driver = {
+ .driver = {
+ .name = VCNL4035_DRV_NAME,
+ .pm = &vcnl4035_pm_ops,
+ .of_match_table = vcnl4035_of_match,
+ },
+ .probe = vcnl4035_probe,
+ .remove = vcnl4035_remove,
+};
+
+module_i2c_driver(vcnl4035_driver);
+
+MODULE_AUTHOR("Parthiban Nallathambi <pn@denx.de>");
+MODULE_DESCRIPTION("VCNL4035 Ambient Light Sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index ed9d776d01af..8a63cbbca4b7 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -175,4 +175,33 @@ config SENSORS_HMC5843_SPI
- hmc5843_core (core functions)
- hmc5843_spi (support for HMC5983)
+config SENSORS_RM3100
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config SENSORS_RM3100_I2C
+ tristate "PNI RM3100 3-Axis Magnetometer (I2C)"
+ depends on I2C
+ select SENSORS_RM3100
+ select REGMAP_I2C
+ help
+ Say Y here to add support for the PNI RM3100 3-Axis Magnetometer.
+
+ This driver can also be compiled as a module.
+ To compile this driver as a module, choose M here: the module
+ will be called rm3100-i2c.
+
+config SENSORS_RM3100_SPI
+ tristate "PNI RM3100 3-Axis Magnetometer (SPI)"
+ depends on SPI_MASTER
+ select SENSORS_RM3100
+ select REGMAP_SPI
+ help
+ Say Y here to add support for the PNI RM3100 3-Axis Magnetometer.
+
+ This driver can also be compiled as a module.
+ To compile this driver as a module, choose M here: the module
+ will be called rm3100-spi.
+
endmenu
diff --git a/drivers/iio/magnetometer/Makefile b/drivers/iio/magnetometer/Makefile
index 664b2f866472..ba1bc34b82fa 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -24,3 +24,7 @@ obj-$(CONFIG_IIO_ST_MAGN_SPI_3AXIS) += st_magn_spi.o
obj-$(CONFIG_SENSORS_HMC5843) += hmc5843_core.o
obj-$(CONFIG_SENSORS_HMC5843_I2C) += hmc5843_i2c.o
obj-$(CONFIG_SENSORS_HMC5843_SPI) += hmc5843_spi.o
+
+obj-$(CONFIG_SENSORS_RM3100) += rm3100-core.o
+obj-$(CONFIG_SENSORS_RM3100_I2C) += rm3100-i2c.o
+obj-$(CONFIG_SENSORS_RM3100_SPI) += rm3100-spi.o
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index 42a827a66512..d430b80808ef 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -790,6 +790,7 @@ static const struct acpi_device_id ak_acpi_match[] = {
{"INVN6500", AK8963},
{"AK009911", AK09911},
{"AK09911", AK09911},
+ {"AKM9911", AK09911},
{"AK09912", AK09912},
{ },
};
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
index d55c4885211a..f3c0d41e5a8c 100644
--- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -163,21 +163,23 @@ static int magn_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
+ s32 min;
*val = 0;
*val2 = 0;
switch (mask) {
case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&magn_state->magn_flux_attributes, true);
- report_id =
- magn_state->magn[chan->address].report_id;
+ report_id = magn_state->magn[chan->address].report_id;
+ min = magn_state->magn[chan->address].logical_minimum;
address = magn_3d_addresses[chan->address];
if (report_id >= 0)
*val = sensor_hub_input_attr_get_raw_value(
magn_state->magn_flux_attributes.hsdev,
HID_USAGE_SENSOR_COMPASS_3D, address,
report_id,
- SENSOR_HUB_SYNC);
+ SENSOR_HUB_SYNC,
+ min < 0);
else {
*val = 0;
hid_sensor_power_state(
diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
new file mode 100644
index 000000000000..7c20918d8108
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-core.c
@@ -0,0 +1,616 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * PNI RM3100 3-axis geomagnetic sensor driver core.
+ *
+ * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
+ *
+ * User Manual available at
+ * <https://www.pnicorp.com/download/rm3100-user-manual/>
+ *
+ * TODO: event generation, pm.
+ */
+
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include "rm3100.h"
+
+/* Cycle Count Registers. */
+#define RM3100_REG_CC_X 0x05
+#define RM3100_REG_CC_Y 0x07
+#define RM3100_REG_CC_Z 0x09
+
+/* Poll Measurement Mode register. */
+#define RM3100_REG_POLL 0x00
+#define RM3100_POLL_X BIT(4)
+#define RM3100_POLL_Y BIT(5)
+#define RM3100_POLL_Z BIT(6)
+
+/* Continuous Measurement Mode register. */
+#define RM3100_REG_CMM 0x01
+#define RM3100_CMM_START BIT(0)
+#define RM3100_CMM_X BIT(4)
+#define RM3100_CMM_Y BIT(5)
+#define RM3100_CMM_Z BIT(6)
+
+/* TiMe Rate Configuration register. */
+#define RM3100_REG_TMRC 0x0B
+#define RM3100_TMRC_OFFSET 0x92
+
+/* Result Status register. */
+#define RM3100_REG_STATUS 0x34
+#define RM3100_STATUS_DRDY BIT(7)
+
+/* Measurement result registers. */
+#define RM3100_REG_MX2 0x24
+#define RM3100_REG_MY2 0x27
+#define RM3100_REG_MZ2 0x2a
+
+#define RM3100_W_REG_START RM3100_REG_POLL
+#define RM3100_W_REG_END RM3100_REG_TMRC
+#define RM3100_R_REG_START RM3100_REG_POLL
+#define RM3100_R_REG_END RM3100_REG_STATUS
+#define RM3100_V_REG_START RM3100_REG_POLL
+#define RM3100_V_REG_END RM3100_REG_STATUS
+
+/*
+ * This is computed by hand, is the sum of channel storage bits and padding
+ * bits, which is 4+4+4+12=24 in here.
+ */
+#define RM3100_SCAN_BYTES 24
+
+#define RM3100_CMM_AXIS_SHIFT 4
+
+struct rm3100_data {
+ struct regmap *regmap;
+ struct completion measuring_done;
+ bool use_interrupt;
+ int conversion_time;
+ int scale;
+ u8 buffer[RM3100_SCAN_BYTES];
+ struct iio_trigger *drdy_trig;
+
+ /*
+ * This lock is for protecting the consistency of series of i2c
+ * operations, that is, to make sure a measurement process will
+ * not be interrupted by a set frequency operation, which should
+ * be taken where a series of i2c operation starts, released where
+ * the operation ends.
+ */
+ struct mutex lock;
+};
+
+static const struct regmap_range rm3100_readable_ranges[] = {
+ regmap_reg_range(RM3100_R_REG_START, RM3100_R_REG_END),
+};
+
+const struct regmap_access_table rm3100_readable_table = {
+ .yes_ranges = rm3100_readable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_readable_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_readable_table);
+
+static const struct regmap_range rm3100_writable_ranges[] = {
+ regmap_reg_range(RM3100_W_REG_START, RM3100_W_REG_END),
+};
+
+const struct regmap_access_table rm3100_writable_table = {
+ .yes_ranges = rm3100_writable_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_writable_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_writable_table);
+
+static const struct regmap_range rm3100_volatile_ranges[] = {
+ regmap_reg_range(RM3100_V_REG_START, RM3100_V_REG_END),
+};
+
+const struct regmap_access_table rm3100_volatile_table = {
+ .yes_ranges = rm3100_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(rm3100_volatile_ranges),
+};
+EXPORT_SYMBOL_GPL(rm3100_volatile_table);
+
+static irqreturn_t rm3100_thread_fn(int irq, void *d)
+{
+ struct iio_dev *indio_dev = d;
+ struct rm3100_data *data = iio_priv(indio_dev);
+
+ /*
+ * Write operation to any register or read operation
+ * to first byte of results will clear the interrupt.
+ */
+ regmap_write(data->regmap, RM3100_REG_POLL, 0);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t rm3100_irq_handler(int irq, void *d)
+{
+ struct iio_dev *indio_dev = d;
+ struct rm3100_data *data = iio_priv(indio_dev);
+
+ switch (indio_dev->currentmode) {
+ case INDIO_DIRECT_MODE:
+ complete(&data->measuring_done);
+ break;
+ case INDIO_BUFFER_TRIGGERED:
+ iio_trigger_poll(data->drdy_trig);
+ break;
+ default:
+ dev_err(indio_dev->dev.parent,
+ "device mode out of control, current mode: %d",
+ indio_dev->currentmode);
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+static int rm3100_wait_measurement(struct rm3100_data *data)
+{
+ struct regmap *regmap = data->regmap;
+ unsigned int val;
+ int tries = 20;
+ int ret;
+
+ /*
+ * A read cycle of 400kbits i2c bus is about 20us, plus the time
+ * used for scheduling, a read cycle of fast mode of this device
+ * can reach 1.7ms, it may be possible for data to arrive just
+ * after we check the RM3100_REG_STATUS. In this case, irq_handler is
+ * called before measuring_done is reinitialized, it will wait
+ * forever for data that has already been ready.
+ * Reinitialize measuring_done before looking up makes sure we
+ * will always capture interrupt no matter when it happens.
+ */
+ if (data->use_interrupt)
+ reinit_completion(&data->measuring_done);
+
+ ret = regmap_read(regmap, RM3100_REG_STATUS, &val);
+ if (ret < 0)
+ return ret;
+
+ if ((val & RM3100_STATUS_DRDY) != RM3100_STATUS_DRDY) {
+ if (data->use_interrupt) {
+ ret = wait_for_completion_timeout(&data->measuring_done,
+ msecs_to_jiffies(data->conversion_time));
+ if (!ret)
+ return -ETIMEDOUT;
+ } else {
+ do {
+ usleep_range(1000, 5000);
+
+ ret = regmap_read(regmap, RM3100_REG_STATUS,
+ &val);
+ if (ret < 0)
+ return ret;
+
+ if (val & RM3100_STATUS_DRDY)
+ break;
+ } while (--tries);
+ if (!tries)
+ return -ETIMEDOUT;
+ }
+ }
+ return 0;
+}
+
+static int rm3100_read_mag(struct rm3100_data *data, int idx, int *val)
+{
+ struct regmap *regmap = data->regmap;
+ u8 buffer[3];
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_write(regmap, RM3100_REG_POLL, BIT(4 + idx));
+ if (ret < 0)
+ goto unlock_return;
+
+ ret = rm3100_wait_measurement(data);
+ if (ret < 0)
+ goto unlock_return;
+
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * idx, buffer, 3);
+ if (ret < 0)
+ goto unlock_return;
+ mutex_unlock(&data->lock);
+
+ *val = sign_extend32((buffer[0] << 16) | (buffer[1] << 8) | buffer[2],
+ 23);
+
+ return IIO_VAL_INT;
+
+unlock_return:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+#define RM3100_CHANNEL(axis, idx) \
+ { \
+ .type = IIO_MAGN, \
+ .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), \
+ .scan_index = idx, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 24, \
+ .storagebits = 32, \
+ .shift = 8, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec rm3100_channels[] = {
+ RM3100_CHANNEL(X, 0),
+ RM3100_CHANNEL(Y, 1),
+ RM3100_CHANNEL(Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(
+ "600 300 150 75 37 18 9 4.5 2.3 1.2 0.6 0.3 0.015 0.075"
+);
+
+static struct attribute *rm3100_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group rm3100_attribute_group = {
+ .attrs = rm3100_attributes,
+};
+
+#define RM3100_SAMP_NUM 14
+
+/*
+ * Frequency : rm3100_samp_rates[][0].rm3100_samp_rates[][1]Hz.
+ * Time between reading: rm3100_sam_rates[][2]ms.
+ * The first one is actually 1.7ms.
+ */
+static const int rm3100_samp_rates[RM3100_SAMP_NUM][3] = {
+ {600, 0, 2}, {300, 0, 3}, {150, 0, 7}, {75, 0, 13}, {37, 0, 27},
+ {18, 0, 55}, {9, 0, 110}, {4, 500000, 220}, {2, 300000, 440},
+ {1, 200000, 800}, {0, 600000, 1600}, {0, 300000, 3300},
+ {0, 15000, 6700}, {0, 75000, 13000}
+};
+
+static int rm3100_get_samp_freq(struct rm3100_data *data, int *val, int *val2)
+{
+ unsigned int tmp;
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_read(data->regmap, RM3100_REG_TMRC, &tmp);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+ *val = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][0];
+ *val2 = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][1];
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int rm3100_set_cycle_count(struct rm3100_data *data, int val)
+{
+ int ret;
+ u8 i;
+
+ for (i = 0; i < 3; i++) {
+ ret = regmap_write(data->regmap, RM3100_REG_CC_X + 2 * i, val);
+ if (ret < 0)
+ return ret;
+ }
+
+ /*
+ * The scale of this sensor depends on the cycle count value, these
+ * three values are corresponding to the cycle count value 50, 100,
+ * 200. scale = output / gain * 10^4.
+ */
+ switch (val) {
+ case 50:
+ data->scale = 500;
+ break;
+ case 100:
+ data->scale = 263;
+ break;
+ /*
+ * case 200:
+ * This function will never be called by users' code, so here we
+ * assume that it will never get a wrong parameter.
+ */
+ default:
+ data->scale = 133;
+ }
+
+ return 0;
+}
+
+static int rm3100_set_samp_freq(struct iio_dev *indio_dev, int val, int val2)
+{
+ struct rm3100_data *data = iio_priv(indio_dev);
+ struct regmap *regmap = data->regmap;
+ unsigned int cycle_count;
+ int ret;
+ int i;
+
+ mutex_lock(&data->lock);
+ /* All cycle count registers use the same value. */
+ ret = regmap_read(regmap, RM3100_REG_CC_X, &cycle_count);
+ if (ret < 0)
+ goto unlock_return;
+
+ for (i = 0; i < RM3100_SAMP_NUM; i++) {
+ if (val == rm3100_samp_rates[i][0] &&
+ val2 == rm3100_samp_rates[i][1])
+ break;
+ }
+ if (i == RM3100_SAMP_NUM) {
+ ret = -EINVAL;
+ goto unlock_return;
+ }
+
+ ret = regmap_write(regmap, RM3100_REG_TMRC, i + RM3100_TMRC_OFFSET);
+ if (ret < 0)
+ goto unlock_return;
+
+ /* Checking if cycle count registers need changing. */
+ if (val == 600 && cycle_count == 200) {
+ ret = rm3100_set_cycle_count(data, 100);
+ if (ret < 0)
+ goto unlock_return;
+ } else if (val != 600 && cycle_count == 100) {
+ ret = rm3100_set_cycle_count(data, 200);
+ if (ret < 0)
+ goto unlock_return;
+ }
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ /* Writing TMRC registers requires CMM reset. */
+ ret = regmap_write(regmap, RM3100_REG_CMM, 0);
+ if (ret < 0)
+ goto unlock_return;
+ ret = regmap_write(data->regmap, RM3100_REG_CMM,
+ (*indio_dev->active_scan_mask & 0x7) <<
+ RM3100_CMM_AXIS_SHIFT | RM3100_CMM_START);
+ if (ret < 0)
+ goto unlock_return;
+ }
+ mutex_unlock(&data->lock);
+
+ data->conversion_time = rm3100_samp_rates[i][2] * 2;
+ return 0;
+
+unlock_return:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int rm3100_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct rm3100_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = rm3100_read_mag(data, chan->scan_index, val);
+ iio_device_release_direct_mode(indio_dev);
+
+ return ret;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = data->scale;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return rm3100_get_samp_freq(data, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int rm3100_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_SAMP_FREQ:
+ return rm3100_set_samp_freq(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info rm3100_info = {
+ .attrs = &rm3100_attribute_group,
+ .read_raw = rm3100_read_raw,
+ .write_raw = rm3100_write_raw,
+};
+
+static int rm3100_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct rm3100_data *data = iio_priv(indio_dev);
+
+ /* Starting channels enabled. */
+ return regmap_write(data->regmap, RM3100_REG_CMM,
+ (*indio_dev->active_scan_mask & 0x7) << RM3100_CMM_AXIS_SHIFT |
+ RM3100_CMM_START);
+}
+
+static int rm3100_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct rm3100_data *data = iio_priv(indio_dev);
+
+ return regmap_write(data->regmap, RM3100_REG_CMM, 0);
+}
+
+static const struct iio_buffer_setup_ops rm3100_buffer_ops = {
+ .preenable = rm3100_buffer_preenable,
+ .postenable = iio_triggered_buffer_postenable,
+ .predisable = iio_triggered_buffer_predisable,
+ .postdisable = rm3100_buffer_postdisable,
+};
+
+static irqreturn_t rm3100_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ unsigned long scan_mask = *indio_dev->active_scan_mask;
+ unsigned int mask_len = indio_dev->masklength;
+ struct rm3100_data *data = iio_priv(indio_dev);
+ struct regmap *regmap = data->regmap;
+ int ret, i, bit;
+
+ mutex_lock(&data->lock);
+ switch (scan_mask) {
+ case BIT(0) | BIT(1) | BIT(2):
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+ /* Convert XXXYYYZZZxxx to XXXxYYYxZZZx. x for paddings. */
+ for (i = 2; i > 0; i--)
+ memmove(data->buffer + i * 4, data->buffer + i * 3, 3);
+ break;
+ case BIT(0) | BIT(1):
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 6);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+ memmove(data->buffer + 4, data->buffer + 3, 3);
+ break;
+ case BIT(1) | BIT(2):
+ ret = regmap_bulk_read(regmap, RM3100_REG_MY2, data->buffer, 6);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+ memmove(data->buffer + 4, data->buffer + 3, 3);
+ break;
+ case BIT(0) | BIT(2):
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2, data->buffer, 9);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ goto done;
+ memmove(data->buffer + 4, data->buffer + 6, 3);
+ break;
+ default:
+ for_each_set_bit(bit, &scan_mask, mask_len) {
+ ret = regmap_bulk_read(regmap, RM3100_REG_MX2 + 3 * bit,
+ data->buffer, 3);
+ if (ret < 0) {
+ mutex_unlock(&data->lock);
+ goto done;
+ }
+ }
+ mutex_unlock(&data->lock);
+ }
+ /*
+ * Always using the same buffer so that we wouldn't need to set the
+ * paddings to 0 in case of leaking any data.
+ */
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ pf->timestamp);
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq)
+{
+ struct iio_dev *indio_dev;
+ struct rm3100_data *data;
+ unsigned int tmp;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->regmap = regmap;
+
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = dev;
+ indio_dev->name = "rm3100";
+ indio_dev->info = &rm3100_info;
+ indio_dev->channels = rm3100_channels;
+ indio_dev->num_channels = ARRAY_SIZE(rm3100_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
+ indio_dev->currentmode = INDIO_DIRECT_MODE;
+
+ if (!irq)
+ data->use_interrupt = false;
+ else {
+ data->use_interrupt = true;
+
+ init_completion(&data->measuring_done);
+ ret = devm_request_threaded_irq(dev,
+ irq,
+ rm3100_irq_handler,
+ rm3100_thread_fn,
+ IRQF_TRIGGER_HIGH |
+ IRQF_ONESHOT,
+ indio_dev->name,
+ indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "request irq line failed.\n");
+ return ret;
+ }
+
+ data->drdy_trig = devm_iio_trigger_alloc(dev, "%s-drdy%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->drdy_trig)
+ return -ENOMEM;
+
+ data->drdy_trig->dev.parent = dev;
+ ret = devm_iio_trigger_register(dev, data->drdy_trig);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ rm3100_trigger_handler,
+ &rm3100_buffer_ops);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_read(regmap, RM3100_REG_TMRC, &tmp);
+ if (ret < 0)
+ return ret;
+ /* Initializing max wait time, which is double conversion time. */
+ data->conversion_time = rm3100_samp_rates[tmp - RM3100_TMRC_OFFSET][2]
+ * 2;
+
+ /* Cycle count values may not be what we want. */
+ if ((tmp - RM3100_TMRC_OFFSET) == 0)
+ rm3100_set_cycle_count(data, 100);
+ else
+ rm3100_set_cycle_count(data, 200);
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_GPL(rm3100_common_probe);
+
+MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
+MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/rm3100-i2c.c b/drivers/iio/magnetometer/rm3100-i2c.c
new file mode 100644
index 000000000000..1ac622c6d6c9
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-i2c.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for PNI RM3100 3-axis geomagnetic sensor on a i2c bus.
+ *
+ * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
+ *
+ * i2c slave address: 0x20 + SA1 << 1 + SA0.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include "rm3100.h"
+
+static const struct regmap_config rm3100_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .rd_table = &rm3100_readable_table,
+ .wr_table = &rm3100_writable_table,
+ .volatile_table = &rm3100_volatile_table,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int rm3100_probe(struct i2c_client *client)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &rm3100_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rm3100_common_probe(&client->dev, regmap, client->irq);
+}
+
+static const struct of_device_id rm3100_dt_match[] = {
+ { .compatible = "pni,rm3100", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rm3100_dt_match);
+
+static struct i2c_driver rm3100_driver = {
+ .driver = {
+ .name = "rm3100-i2c",
+ .of_match_table = rm3100_dt_match,
+ },
+ .probe_new = rm3100_probe,
+};
+module_i2c_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
+MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer i2c driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/rm3100-spi.c b/drivers/iio/magnetometer/rm3100-spi.c
new file mode 100644
index 000000000000..65d5eb9e4f5e
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100-spi.c
@@ -0,0 +1,64 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for PNI RM3100 3-axis geomagnetic sensor on a spi bus.
+ *
+ * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "rm3100.h"
+
+static const struct regmap_config rm3100_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .rd_table = &rm3100_readable_table,
+ .wr_table = &rm3100_writable_table,
+ .volatile_table = &rm3100_volatile_table,
+
+ .read_flag_mask = 0x80,
+
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int rm3100_probe(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ int ret;
+
+ /* Actually this device supports both mode 0 and mode 3. */
+ spi->mode = SPI_MODE_0;
+ /* Data rates cannot exceed 1Mbits. */
+ spi->max_speed_hz = 1000000;
+ spi->bits_per_word = 8;
+ ret = spi_setup(spi);
+ if (ret)
+ return ret;
+
+ regmap = devm_regmap_init_spi(spi, &rm3100_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return rm3100_common_probe(&spi->dev, regmap, spi->irq);
+}
+
+static const struct of_device_id rm3100_dt_match[] = {
+ { .compatible = "pni,rm3100", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rm3100_dt_match);
+
+static struct spi_driver rm3100_driver = {
+ .driver = {
+ .name = "rm3100-spi",
+ .of_match_table = rm3100_dt_match,
+ },
+ .probe = rm3100_probe,
+};
+module_spi_driver(rm3100_driver);
+
+MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
+MODULE_DESCRIPTION("PNI RM3100 3-axis magnetometer spi driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/magnetometer/rm3100.h b/drivers/iio/magnetometer/rm3100.h
new file mode 100644
index 000000000000..c3508218bc77
--- /dev/null
+++ b/drivers/iio/magnetometer/rm3100.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
+ */
+
+#ifndef RM3100_CORE_H
+#define RM3100_CORE_H
+
+#include <linux/regmap.h>
+
+extern const struct regmap_access_table rm3100_readable_table;
+extern const struct regmap_access_table rm3100_writable_table;
+extern const struct regmap_access_table rm3100_volatile_table;
+
+int rm3100_common_probe(struct device *dev, struct regmap *regmap, int irq);
+
+#endif /* RM3100_CORE_H */
diff --git a/drivers/iio/magnetometer/st_magn.h b/drivers/iio/magnetometer/st_magn.h
index 8fe51ce427bd..bc14ad4f1b26 100644
--- a/drivers/iio/magnetometer/st_magn.h
+++ b/drivers/iio/magnetometer/st_magn.h
@@ -20,6 +20,7 @@
#define LIS3MDL_MAGN_DEV_NAME "lis3mdl"
#define LSM303AGR_MAGN_DEV_NAME "lsm303agr_magn"
#define LIS2MDL_MAGN_DEV_NAME "lis2mdl"
+#define LSM9DS1_MAGN_DEV_NAME "lsm9ds1_magn"
int st_magn_common_probe(struct iio_dev *indio_dev);
void st_magn_common_remove(struct iio_dev *indio_dev);
diff --git a/drivers/iio/magnetometer/st_magn_buffer.c b/drivers/iio/magnetometer/st_magn_buffer.c
index 0a9e8fadfa9d..37ab30566464 100644
--- a/drivers/iio/magnetometer/st_magn_buffer.c
+++ b/drivers/iio/magnetometer/st_magn_buffer.c
@@ -30,11 +30,6 @@ int st_magn_trig_set_state(struct iio_trigger *trig, bool state)
return st_sensors_set_dataready_irq(indio_dev, state);
}
-static int st_magn_buffer_preenable(struct iio_dev *indio_dev)
-{
- return st_sensors_set_enable(indio_dev, true);
-}
-
static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
{
int err;
@@ -50,7 +45,7 @@ static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
if (err < 0)
goto st_magn_buffer_postenable_error;
- return err;
+ return st_sensors_set_enable(indio_dev, true);
st_magn_buffer_postenable_error:
kfree(mdata->buffer_data);
@@ -63,11 +58,11 @@ static int st_magn_buffer_predisable(struct iio_dev *indio_dev)
int err;
struct st_sensor_data *mdata = iio_priv(indio_dev);
- err = iio_triggered_buffer_predisable(indio_dev);
+ err = st_sensors_set_enable(indio_dev, false);
if (err < 0)
goto st_magn_buffer_predisable_error;
- err = st_sensors_set_enable(indio_dev, false);
+ err = iio_triggered_buffer_predisable(indio_dev);
st_magn_buffer_predisable_error:
kfree(mdata->buffer_data);
@@ -75,7 +70,6 @@ st_magn_buffer_predisable_error:
}
static const struct iio_buffer_setup_ops st_magn_buffer_setup_ops = {
- .preenable = &st_magn_buffer_preenable,
.postenable = &st_magn_buffer_postenable,
.predisable = &st_magn_buffer_predisable,
};
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index 72f6d1335a04..5d056bdb3b37 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -29,9 +29,9 @@
#define ST_MAGN_NUMBER_DATA_CHANNELS 3
/* DEFAULT VALUE FOR SENSORS */
-#define ST_MAGN_DEFAULT_OUT_X_H_ADDR 0X03
-#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR 0X07
-#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR 0X05
+#define ST_MAGN_DEFAULT_OUT_X_H_ADDR 0x03
+#define ST_MAGN_DEFAULT_OUT_Y_H_ADDR 0x07
+#define ST_MAGN_DEFAULT_OUT_Z_H_ADDR 0x05
/* FULLSCALE */
#define ST_MAGN_FS_AVL_1300MG 1300
@@ -267,6 +267,7 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
.wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
.sensors_supported = {
[0] = LIS3MDL_MAGN_DEV_NAME,
+ [1] = LSM9DS1_MAGN_DEV_NAME,
},
.ch = (struct iio_chan_spec *)st_magn_2_16bit_channels,
.odr = {
@@ -315,6 +316,10 @@ static const struct st_sensor_settings st_magn_sensors_settings[] = {
},
},
},
+ .bdu = {
+ .addr = 0x24,
+ .mask = 0x40,
+ },
.drdy_irq = {
/* drdy line is routed drdy pin */
.stat_drdy = {
diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c
index feaa28cf6a77..68650f5f5c19 100644
--- a/drivers/iio/magnetometer/st_magn_i2c.c
+++ b/drivers/iio/magnetometer/st_magn_i2c.c
@@ -44,6 +44,10 @@ static const struct of_device_id st_magn_of_match[] = {
.compatible = "st,lis2mdl",
.data = LIS2MDL_MAGN_DEV_NAME,
},
+ {
+ .compatible = "st,lsm9ds1-magn",
+ .data = LSM9DS1_MAGN_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_magn_of_match);
@@ -90,6 +94,7 @@ static const struct i2c_device_id st_magn_id_table[] = {
{ LIS3MDL_MAGN_DEV_NAME },
{ LSM303AGR_MAGN_DEV_NAME },
{ LIS2MDL_MAGN_DEV_NAME },
+ { LSM9DS1_MAGN_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_magn_id_table);
diff --git a/drivers/iio/magnetometer/st_magn_spi.c b/drivers/iio/magnetometer/st_magn_spi.c
index 7b7cd08fcc32..cb05fcd9ddfe 100644
--- a/drivers/iio/magnetometer/st_magn_spi.c
+++ b/drivers/iio/magnetometer/st_magn_spi.c
@@ -23,6 +23,8 @@
* For new single-chip sensors use <device_name> as compatible string.
* For old single-chip devices keep <device_name>-magn to maintain
* compatibility
+ * For multi-chip devices, use <device_name>-magn to distinguish which
+ * capability is being used
*/
static const struct of_device_id st_magn_of_match[] = {
{
@@ -37,6 +39,10 @@ static const struct of_device_id st_magn_of_match[] = {
.compatible = "st,lis2mdl",
.data = LIS2MDL_MAGN_DEV_NAME,
},
+ {
+ .compatible = "st,lsm9ds1-magn",
+ .data = LSM9DS1_MAGN_DEV_NAME,
+ },
{}
};
MODULE_DEVICE_TABLE(of, st_magn_of_match);
@@ -79,6 +85,7 @@ static const struct spi_device_id st_magn_id_table[] = {
{ LIS3MDL_MAGN_DEV_NAME },
{ LSM303AGR_MAGN_DEV_NAME },
{ LIS2MDL_MAGN_DEV_NAME },
+ { LSM9DS1_MAGN_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_magn_id_table);
diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c
index 1e5451d1ff88..bdc5e4554ee4 100644
--- a/drivers/iio/orientation/hid-sensor-incl-3d.c
+++ b/drivers/iio/orientation/hid-sensor-incl-3d.c
@@ -111,21 +111,23 @@ static int incl_3d_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
+ s32 min;
*val = 0;
*val2 = 0;
switch (mask) {
case IIO_CHAN_INFO_RAW:
hid_sensor_power_state(&incl_state->common_attributes, true);
- report_id =
- incl_state->incl[chan->scan_index].report_id;
+ report_id = incl_state->incl[chan->scan_index].report_id;
+ min = incl_state->incl[chan->scan_index].logical_minimum;
address = incl_3d_addresses[chan->scan_index];
if (report_id >= 0)
*val = sensor_hub_input_attr_get_raw_value(
incl_state->common_attributes.hsdev,
HID_USAGE_SENSOR_INCLINOMETER_3D, address,
report_id,
- SENSOR_HUB_SYNC);
+ SENSOR_HUB_SYNC,
+ min < 0);
else {
hid_sensor_power_state(&incl_state->common_attributes,
false);
diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig
index 79ec2eba4969..6303cbe79903 100644
--- a/drivers/iio/potentiometer/Kconfig
+++ b/drivers/iio/potentiometer/Kconfig
@@ -90,6 +90,18 @@ config MCP4531
To compile this driver as a module, choose M here: the
module will be called mcp4531.
+config MCP41010
+ tristate "Microchip MCP41xxx/MCP42xxx Digital Potentiometer driver"
+ depends on SPI
+ help
+ Say yes here to build support for the Microchip
+ MCP41010, MCP41050, MCP41100,
+ MCP42010, MCP42050, MCP42100
+ digital potentiometer chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mcp41010.
+
config TPL0102
tristate "Texas Instruments digital potentiometer driver"
depends on I2C
diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile
index 4af657883c3f..8ff55138cf12 100644
--- a/drivers/iio/potentiometer/Makefile
+++ b/drivers/iio/potentiometer/Makefile
@@ -11,4 +11,5 @@ obj-$(CONFIG_MAX5487) += max5487.o
obj-$(CONFIG_MCP4018) += mcp4018.o
obj-$(CONFIG_MCP4131) += mcp4131.o
obj-$(CONFIG_MCP4531) += mcp4531.o
+obj-$(CONFIG_MCP41010) += mcp41010.o
obj-$(CONFIG_TPL0102) += tpl0102.o
diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c
new file mode 100644
index 000000000000..2368b39debf5
--- /dev/null
+++ b/drivers/iio/potentiometer/mcp41010.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Industrial I/O driver for Microchip digital potentiometers
+ *
+ * Copyright (c) 2018 Chris Coffey <cmc@babblebit.net>
+ * Based on: Slawomir Stepien's code from mcp4131.c
+ *
+ * Datasheet: http://ww1.microchip.com/downloads/en/devicedoc/11195c.pdf
+ *
+ * DEVID #Wipers #Positions Resistance (kOhm)
+ * mcp41010 1 256 10
+ * mcp41050 1 256 50
+ * mcp41100 1 256 100
+ * mcp42010 2 256 10
+ * mcp42050 2 256 50
+ * mcp42100 2 256 100
+ */
+
+#include <linux/cache.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+
+#define MCP41010_MAX_WIPERS 2
+#define MCP41010_WRITE BIT(4)
+#define MCP41010_WIPER_MAX 255
+#define MCP41010_WIPER_CHANNEL BIT(0)
+
+struct mcp41010_cfg {
+ char name[16];
+ int wipers;
+ int kohms;
+};
+
+enum mcp41010_type {
+ MCP41010,
+ MCP41050,
+ MCP41100,
+ MCP42010,
+ MCP42050,
+ MCP42100,
+};
+
+static const struct mcp41010_cfg mcp41010_cfg[] = {
+ [MCP41010] = { .name = "mcp41010", .wipers = 1, .kohms = 10, },
+ [MCP41050] = { .name = "mcp41050", .wipers = 1, .kohms = 50, },
+ [MCP41100] = { .name = "mcp41100", .wipers = 1, .kohms = 100, },
+ [MCP42010] = { .name = "mcp42010", .wipers = 2, .kohms = 10, },
+ [MCP42050] = { .name = "mcp42050", .wipers = 2, .kohms = 50, },
+ [MCP42100] = { .name = "mcp42100", .wipers = 2, .kohms = 100, },
+};
+
+struct mcp41010_data {
+ struct spi_device *spi;
+ const struct mcp41010_cfg *cfg;
+ struct mutex lock; /* Protect write sequences */
+ unsigned int value[MCP41010_MAX_WIPERS]; /* Cache wiper values */
+ u8 buf[2] ____cacheline_aligned;
+};
+
+#define MCP41010_CHANNEL(ch) { \
+ .type = IIO_RESISTANCE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = (ch), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mcp41010_channels[] = {
+ MCP41010_CHANNEL(0),
+ MCP41010_CHANNEL(1),
+};
+
+static int mcp41010_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mcp41010_data *data = iio_priv(indio_dev);
+ int channel = chan->channel;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = data->value[channel];
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1000 * data->cfg->kohms;
+ *val2 = MCP41010_WIPER_MAX;
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+}
+
+static int mcp41010_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int err;
+ struct mcp41010_data *data = iio_priv(indio_dev);
+ int channel = chan->channel;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ if (val > MCP41010_WIPER_MAX || val < 0)
+ return -EINVAL;
+
+ mutex_lock(&data->lock);
+
+ data->buf[0] = MCP41010_WIPER_CHANNEL << channel;
+ data->buf[0] |= MCP41010_WRITE;
+ data->buf[1] = val & 0xff;
+
+ err = spi_write(data->spi, data->buf, sizeof(data->buf));
+ if (!err)
+ data->value[channel] = val;
+
+ mutex_unlock(&data->lock);
+
+ return err;
+}
+
+static const struct iio_info mcp41010_info = {
+ .read_raw = mcp41010_read_raw,
+ .write_raw = mcp41010_write_raw,
+};
+
+static int mcp41010_probe(struct spi_device *spi)
+{
+ int err;
+ struct device *dev = &spi->dev;
+ struct mcp41010_data *data;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ data->spi = spi;
+ data->cfg = of_device_get_match_data(&spi->dev);
+ if (!data->cfg)
+ data->cfg = &mcp41010_cfg[spi_get_device_id(spi)->driver_data];
+
+ mutex_init(&data->lock);
+
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &mcp41010_info;
+ indio_dev->channels = mcp41010_channels;
+ indio_dev->num_channels = data->cfg->wipers;
+ indio_dev->name = data->cfg->name;
+
+ err = devm_iio_device_register(dev, indio_dev);
+ if (err)
+ dev_info(&spi->dev, "Unable to register %s\n", indio_dev->name);
+
+ return err;
+}
+
+static const struct of_device_id mcp41010_match[] = {
+ { .compatible = "microchip,mcp41010", .data = &mcp41010_cfg[MCP41010] },
+ { .compatible = "microchip,mcp41050", .data = &mcp41010_cfg[MCP41050] },
+ { .compatible = "microchip,mcp41100", .data = &mcp41010_cfg[MCP41100] },
+ { .compatible = "microchip,mcp42010", .data = &mcp41010_cfg[MCP42010] },
+ { .compatible = "microchip,mcp42050", .data = &mcp41010_cfg[MCP42050] },
+ { .compatible = "microchip,mcp42100", .data = &mcp41010_cfg[MCP42100] },
+ {}
+};
+MODULE_DEVICE_TABLE(of, mcp41010_match);
+
+static const struct spi_device_id mcp41010_id[] = {
+ { "mcp41010", MCP41010 },
+ { "mcp41050", MCP41050 },
+ { "mcp41100", MCP41100 },
+ { "mcp42010", MCP42010 },
+ { "mcp42050", MCP42050 },
+ { "mcp42100", MCP42100 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, mcp41010_id);
+
+static struct spi_driver mcp41010_driver = {
+ .driver = {
+ .name = "mcp41010",
+ .of_match_table = mcp41010_match,
+ },
+ .probe = mcp41010_probe,
+ .id_table = mcp41010_id,
+};
+
+module_spi_driver(mcp41010_driver);
+
+MODULE_AUTHOR("Chris Coffey <cmc@babblebit.net>");
+MODULE_DESCRIPTION("MCP41010 digital potentiometer");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/potentiometer/mcp4131.c b/drivers/iio/potentiometer/mcp4131.c
index b3e30db246cc..efe035ce010d 100644
--- a/drivers/iio/potentiometer/mcp4131.c
+++ b/drivers/iio/potentiometer/mcp4131.c
@@ -42,6 +42,7 @@
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/spi/spi.h>
#define MCP4131_WRITE (0x00 << 2)
@@ -243,7 +244,7 @@ static int mcp4131_probe(struct spi_device *spi)
{
int err;
struct device *dev = &spi->dev;
- unsigned long devid = spi_get_device_id(spi)->driver_data;
+ unsigned long devid;
struct mcp4131_data *data;
struct iio_dev *indio_dev;
@@ -254,7 +255,11 @@ static int mcp4131_probe(struct spi_device *spi)
data = iio_priv(indio_dev);
spi_set_drvdata(spi, indio_dev);
data->spi = spi;
- data->cfg = &mcp4131_cfg[devid];
+ data->cfg = of_device_get_match_data(&spi->dev);
+ if (!data->cfg) {
+ devid = spi_get_device_id(spi)->driver_data;
+ data->cfg = &mcp4131_cfg[devid];
+ }
mutex_init(&data->lock);
@@ -273,7 +278,6 @@ static int mcp4131_probe(struct spi_device *spi)
return 0;
}
-#if defined(CONFIG_OF)
static const struct of_device_id mcp4131_dt_ids[] = {
{ .compatible = "microchip,mcp4131-502",
.data = &mcp4131_cfg[MCP413x_502] },
@@ -406,7 +410,6 @@ static const struct of_device_id mcp4131_dt_ids[] = {
{}
};
MODULE_DEVICE_TABLE(of, mcp4131_dt_ids);
-#endif /* CONFIG_OF */
static const struct spi_device_id mcp4131_id[] = {
{ "mcp4131-502", MCP413x_502 },
diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c
index ca1cce58fe20..a0a07e47f13f 100644
--- a/drivers/iio/potentiometer/tpl0102.c
+++ b/drivers/iio/potentiometer/tpl0102.c
@@ -15,7 +15,7 @@
struct tpl0102_cfg {
int wipers;
- int max_pos;
+ int avail[3];
int kohms;
};
@@ -28,16 +28,16 @@ enum tpl0102_type {
static const struct tpl0102_cfg tpl0102_cfg[] = {
/* on-semiconductor parts */
- [CAT5140_503] = { .wipers = 1, .max_pos = 256, .kohms = 50, },
- [CAT5140_104] = { .wipers = 1, .max_pos = 256, .kohms = 100, },
+ [CAT5140_503] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 50, },
+ [CAT5140_104] = { .wipers = 1, .avail = { 0, 1, 255 }, .kohms = 100, },
/* ti parts */
- [TPL0102_104] = { .wipers = 2, .max_pos = 256, .kohms = 100 },
- [TPL0401_103] = { .wipers = 1, .max_pos = 128, .kohms = 10, },
+ [TPL0102_104] = { .wipers = 2, .avail = { 0, 1, 255 }, .kohms = 100 },
+ [TPL0401_103] = { .wipers = 1, .avail = { 0, 1, 127 }, .kohms = 10, },
};
struct tpl0102_data {
struct regmap *regmap;
- unsigned long devid;
+ const struct tpl0102_cfg *cfg;
};
static const struct regmap_config tpl0102_regmap_config = {
@@ -52,6 +52,7 @@ static const struct regmap_config tpl0102_regmap_config = {
.channel = (ch), \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), \
}
static const struct iio_chan_spec tpl0102_channels[] = {
@@ -72,14 +73,32 @@ static int tpl0102_read_raw(struct iio_dev *indio_dev,
return ret ? ret : IIO_VAL_INT;
}
case IIO_CHAN_INFO_SCALE:
- *val = 1000 * tpl0102_cfg[data->devid].kohms;
- *val2 = tpl0102_cfg[data->devid].max_pos;
+ *val = 1000 * data->cfg->kohms;
+ *val2 = data->cfg->avail[2] + 1;
return IIO_VAL_FRACTIONAL;
}
return -EINVAL;
}
+static int tpl0102_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct tpl0102_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *length = ARRAY_SIZE(data->cfg->avail);
+ *vals = data->cfg->avail;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_RANGE;
+ }
+
+ return -EINVAL;
+}
+
static int tpl0102_write_raw(struct iio_dev *indio_dev,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
@@ -89,7 +108,7 @@ static int tpl0102_write_raw(struct iio_dev *indio_dev,
if (mask != IIO_CHAN_INFO_RAW)
return -EINVAL;
- if (val >= tpl0102_cfg[data->devid].max_pos || val < 0)
+ if (val > data->cfg->avail[2] || val < 0)
return -EINVAL;
return regmap_write(data->regmap, chan->channel, val);
@@ -97,6 +116,7 @@ static int tpl0102_write_raw(struct iio_dev *indio_dev,
static const struct iio_info tpl0102_info = {
.read_raw = tpl0102_read_raw,
+ .read_avail = tpl0102_read_avail,
.write_raw = tpl0102_write_raw,
};
@@ -113,7 +133,7 @@ static int tpl0102_probe(struct i2c_client *client,
data = iio_priv(indio_dev);
i2c_set_clientdata(client, indio_dev);
- data->devid = id->driver_data;
+ data->cfg = &tpl0102_cfg[id->driver_data];
data->regmap = devm_regmap_init_i2c(client, &tpl0102_regmap_config);
if (IS_ERR(data->regmap)) {
dev_err(dev, "regmap initialization failed\n");
@@ -123,7 +143,7 @@ static int tpl0102_probe(struct i2c_client *client,
indio_dev->dev.parent = dev;
indio_dev->info = &tpl0102_info;
indio_dev->channels = tpl0102_channels;
- indio_dev->num_channels = tpl0102_cfg[data->devid].wipers;
+ indio_dev->num_channels = data->cfg->wipers;
indio_dev->name = client->name;
return devm_iio_device_register(dev, indio_dev);
diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c
index 4c437918f1d2..d7b1c00ceb4d 100644
--- a/drivers/iio/pressure/hid-sensor-press.c
+++ b/drivers/iio/pressure/hid-sensor-press.c
@@ -77,6 +77,7 @@ static int press_read_raw(struct iio_dev *indio_dev,
int report_id = -1;
u32 address;
int ret_type;
+ s32 min;
*val = 0;
*val2 = 0;
@@ -85,8 +86,8 @@ static int press_read_raw(struct iio_dev *indio_dev,
switch (chan->scan_index) {
case CHANNEL_SCAN_INDEX_PRESSURE:
report_id = press_state->press_attr.report_id;
- address =
- HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE;
+ min = press_state->press_attr.logical_minimum;
+ address = HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE;
break;
default:
report_id = -1;
@@ -99,7 +100,8 @@ static int press_read_raw(struct iio_dev *indio_dev,
press_state->common_attributes.hsdev,
HID_USAGE_SENSOR_PRESSURE, address,
report_id,
- SENSOR_HUB_SYNC);
+ SENSOR_HUB_SYNC,
+ min < 0);
hid_sensor_power_state(&press_state->common_attributes,
false);
} else {
diff --git a/drivers/iio/resolver/Kconfig b/drivers/iio/resolver/Kconfig
index 2ced9f22aa70..786801be54f6 100644
--- a/drivers/iio/resolver/Kconfig
+++ b/drivers/iio/resolver/Kconfig
@@ -3,6 +3,16 @@
#
menu "Resolver to digital converters"
+config AD2S90
+ tristate "Analog Devices ad2s90 driver"
+ depends on SPI
+ help
+ Say yes here to build support for Analog Devices spi resolver
+ to digital converters, ad2s90, provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad2s90.
+
config AD2S1200
tristate "Analog Devices ad2s1200/ad2s1205 driver"
depends on SPI
diff --git a/drivers/iio/resolver/Makefile b/drivers/iio/resolver/Makefile
index 4e1dccae07e7..398d82d50028 100644
--- a/drivers/iio/resolver/Makefile
+++ b/drivers/iio/resolver/Makefile
@@ -2,4 +2,5 @@
# Makefile for Resolver/Synchro drivers
#
+obj-$(CONFIG_AD2S90) += ad2s90.o
obj-$(CONFIG_AD2S1200) += ad2s1200.o
diff --git a/drivers/iio/resolver/ad2s90.c b/drivers/iio/resolver/ad2s90.c
new file mode 100644
index 000000000000..a41f5cb10da5
--- /dev/null
+++ b/drivers/iio/resolver/ad2s90.c
@@ -0,0 +1,131 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ad2s90.c simple support for the ADI Resolver to Digital Converters: AD2S90
+ *
+ * Copyright (c) 2010-2010 Analog Devices Inc.
+ */
+#include <linux/types.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/*
+ * Although chip's max frequency is 2Mhz, it needs 600ns between CS and the
+ * first falling edge of SCLK, so frequency should be at most 1 / (2 * 6e-7)
+ */
+#define AD2S90_MAX_SPI_FREQ_HZ 830000
+
+struct ad2s90_state {
+ struct mutex lock; /* lock to protect rx buffer */
+ struct spi_device *sdev;
+ u8 rx[2] ____cacheline_aligned;
+};
+
+static int ad2s90_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret;
+ struct ad2s90_state *st = iio_priv(indio_dev);
+
+ if (chan->type != IIO_ANGL)
+ return -EINVAL;
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ /* 2 * Pi / 2^12 */
+ *val = 6283; /* mV */
+ *val2 = 12;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&st->lock);
+ ret = spi_read(st->sdev, st->rx, 2);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ *val = (((u16)(st->rx[0])) << 4) | ((st->rx[1] & 0xF0) >> 4);
+
+ mutex_unlock(&st->lock);
+
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info ad2s90_info = {
+ .read_raw = ad2s90_read_raw,
+};
+
+static const struct iio_chan_spec ad2s90_chan = {
+ .type = IIO_ANGL,
+ .indexed = 1,
+ .channel = 0,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+};
+
+static int ad2s90_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ad2s90_state *st;
+
+ if (spi->max_speed_hz > AD2S90_MAX_SPI_FREQ_HZ) {
+ dev_err(&spi->dev, "SPI CLK, %d Hz exceeds %d Hz\n",
+ spi->max_speed_hz, AD2S90_MAX_SPI_FREQ_HZ);
+ return -EINVAL;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+
+ mutex_init(&st->lock);
+ st->sdev = spi;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = &ad2s90_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = &ad2s90_chan;
+ indio_dev->num_channels = 1;
+ indio_dev->name = spi_get_device_id(spi)->name;
+
+ return devm_iio_device_register(indio_dev->dev.parent, indio_dev);
+}
+
+static const struct of_device_id ad2s90_of_match[] = {
+ { .compatible = "adi,ad2s90", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ad2s90_of_match);
+
+static const struct spi_device_id ad2s90_id[] = {
+ { "ad2s90" },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad2s90_id);
+
+static struct spi_driver ad2s90_driver = {
+ .driver = {
+ .name = "ad2s90",
+ .of_match_table = ad2s90_of_match,
+ },
+ .probe = ad2s90_probe,
+ .id_table = ad2s90_id,
+};
+module_spi_driver(ad2s90_driver);
+
+MODULE_AUTHOR("Graff Yang <graff.yang@gmail.com>");
+MODULE_DESCRIPTION("Analog Devices AD2S90 Resolver to Digital SPI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c
index beaf6fd3e337..b592fc4f007e 100644
--- a/drivers/iio/temperature/hid-sensor-temperature.c
+++ b/drivers/iio/temperature/hid-sensor-temperature.c
@@ -76,7 +76,8 @@ static int temperature_read_raw(struct iio_dev *indio_dev,
HID_USAGE_SENSOR_TEMPERATURE,
HID_USAGE_SENSOR_DATA_ENVIRONMENTAL_TEMPERATURE,
temp_st->temperature_attr.report_id,
- SENSOR_HUB_SYNC);
+ SENSOR_HUB_SYNC,
+ temp_st->temperature_attr.logical_minimum < 0);
hid_sensor_power_state(
&temp_st->common_attributes,
false);