Update Linux to v5.4.2
Change-Id: Idf6911045d9d382da2cfe01b1edff026404ac8fd
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index d08aeb4..5bd5185 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -1,10 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Industrial I/O subsystem configuration
#
menuconfig IIO
tristate "Industrial I/O support"
- select ANON_INODES
help
The industrial I/O subsystem provides a unified framework for
drivers for many different types of embedded sensors using a
@@ -28,7 +28,7 @@
help
This allows configuring various IIO bits through configfs
(e.g. software triggers). For more info see
- Documentation/iio/iio_configfs.txt.
+ Documentation/iio/iio_configfs.rst.
config IIO_TRIGGER
bool "Enable triggered sampling support"
@@ -39,28 +39,28 @@
data now' interrupt.
config IIO_CONSUMERS_PER_TRIGGER
- int "Maximum number of consumers per trigger"
- depends on IIO_TRIGGER
- default "2"
- help
- This value controls the maximum number of consumers that a
- given trigger may handle. Default is 2.
+ int "Maximum number of consumers per trigger"
+ depends on IIO_TRIGGER
+ default "2"
+ help
+ This value controls the maximum number of consumers that a
+ given trigger may handle. Default is 2.
config IIO_SW_DEVICE
tristate "Enable software IIO device support"
select IIO_CONFIGFS
help
- Provides IIO core support for software devices. A software
- device can be created via configfs or directly by a driver
- using the API provided.
+ Provides IIO core support for software devices. A software
+ device can be created via configfs or directly by a driver
+ using the API provided.
config IIO_SW_TRIGGER
tristate "Enable software triggers support"
select IIO_CONFIGFS
help
- Provides IIO core support for software triggers. A software
- trigger can be created via configfs or directly by a driver
- using the API provided.
+ Provides IIO core support for software triggers. A software
+ trigger can be created via configfs or directly by a driver
+ using the API provided.
config IIO_TRIGGERED_EVENT
tristate
@@ -74,7 +74,6 @@
source "drivers/iio/amplifiers/Kconfig"
source "drivers/iio/chemical/Kconfig"
source "drivers/iio/common/Kconfig"
-source "drivers/iio/counter/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/dummy/Kconfig"
source "drivers/iio/frequency/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index cb59932..bff682a 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -20,7 +20,6 @@
obj-y += buffer/
obj-y += chemical/
obj-y += common/
-obj-y += counter/
obj-y += dac/
obj-y += dummy/
obj-y += gyro/
diff --git a/drivers/iio/accel/Kconfig b/drivers/iio/accel/Kconfig
index 829dc96..d4ef35a 100644
--- a/drivers/iio/accel/Kconfig
+++ b/drivers/iio/accel/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Accelerometer drivers
#
@@ -6,28 +7,28 @@
menu "Accelerometers"
config ADIS16201
- tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
- depends on SPI
- select IIO_ADIS_LIB
- select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
- help
- Say Y here to build support for Analog Devices adis16201 dual-axis
- digital inclinometer and accelerometer.
+ tristate "Analog Devices ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16201 dual-axis
+ digital inclinometer and accelerometer.
- To compile this driver as a module, say M here: the module will
- be called adis16201.
+ To compile this driver as a module, say M here: the module will
+ be called adis16201.
config ADIS16209
- tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
- depends on SPI
- select IIO_ADIS_LIB
- select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
- help
- Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer
- and accelerometer.
+ tristate "Analog Devices ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say Y here to build support for Analog Devices adis16209 dual-axis digital inclinometer
+ and accelerometer.
- To compile this driver as a module, say M here: the module will be
- called adis16209.
+ To compile this driver as a module, say M here: the module will be
+ called adis16209.
config ADXL345
tristate
@@ -60,6 +61,33 @@
will be called adxl345_spi and you will also get adxl345_core
for the core module.
+config ADXL372
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config ADXL372_SPI
+ tristate "Analog Devices ADXL372 3-Axis Accelerometer SPI Driver"
+ depends on SPI
+ select ADXL372
+ select REGMAP_SPI
+ help
+ Say yes here to add support for the Analog Devices ADXL372 triaxial
+ acceleration sensor.
+ To compile this driver as a module, choose M here: the
+ module will be called adxl372_spi.
+
+config ADXL372_I2C
+ tristate "Analog Devices ADXL372 3-Axis Accelerometer I2C Driver"
+ depends on I2C
+ select ADXL372
+ select REGMAP_I2C
+ help
+ Say yes here to add support for the Analog Devices ADXL372 triaxial
+ acceleration sensor.
+ To compile this driver as a module, choose M here: the
+ module will be called adxl372_i2c.
+
config BMA180
tristate "Bosch BMA180/BMA250 3-Axis Accelerometer Driver"
depends on I2C
@@ -73,16 +101,16 @@
module will be called bma180.
config BMA220
- tristate "Bosch BMA220 3-Axis Accelerometer Driver"
+ tristate "Bosch BMA220 3-Axis Accelerometer Driver"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
- help
- Say yes here to add support for the Bosch BMA220 triaxial
- acceleration sensor.
+ help
+ Say yes here to add support for the Bosch BMA220 triaxial
+ acceleration sensor.
- To compile this driver as a module, choose M here: the
- module will be called bma220_spi.
+ To compile this driver as a module, choose M here: the
+ module will be called bma220_spi.
config BMC150_ACCEL
tristate "Bosch BMC150 Accelerometer Driver"
@@ -174,9 +202,7 @@
config IIO_CROS_EC_ACCEL_LEGACY
tristate "ChromeOS EC Legacy Accelerometer Sensor"
- select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
- select CROS_EC_LPC_REGISTER_DEVICE
+ depends on IIO_CROS_EC_SENSORS_CORE
help
Say yes here to get support for accelerometers on Chromebook using
legacy EC firmware.
@@ -196,7 +222,7 @@
Say yes here to build support for STMicroelectronics accelerometers:
LSM303DLH, LSM303DLHC, LIS3DH, LSM330D, LSM330DL, LSM330DLC,
LIS331DLH, LSM303DL, LSM303DLM, LSM330, LIS2DH12, H3LIS331DL,
- LNG2DM
+ LNG2DM, LIS3DE, LIS2DE12
This driver can also be built as a module. If so, these modules
will be created:
diff --git a/drivers/iio/accel/Makefile b/drivers/iio/accel/Makefile
index 636d4d1..56bd021 100644
--- a/drivers/iio/accel/Makefile
+++ b/drivers/iio/accel/Makefile
@@ -9,6 +9,9 @@
obj-$(CONFIG_ADXL345) += adxl345_core.o
obj-$(CONFIG_ADXL345_I2C) += adxl345_i2c.o
obj-$(CONFIG_ADXL345_SPI) += adxl345_spi.o
+obj-$(CONFIG_ADXL372) += adxl372.o
+obj-$(CONFIG_ADXL372_I2C) += adxl372_i2c.o
+obj-$(CONFIG_ADXL372_SPI) += adxl372_spi.o
obj-$(CONFIG_BMA180) += bma180.o
obj-$(CONFIG_BMA220) += bma220_spi.o
obj-$(CONFIG_BMC150_ACCEL) += bmc150-accel-core.o
diff --git a/drivers/iio/accel/adis16201.c b/drivers/iio/accel/adis16201.c
index 4c1d482..c4810c7 100644
--- a/drivers/iio/accel/adis16201.c
+++ b/drivers/iio/accel/adis16201.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADIS16201 Dual-Axis Digital Inclinometer and Accelerometer
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/device.h>
@@ -71,7 +70,7 @@
#define ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT 2
/* Power supply above 3.625 V */
#define ADIS16201_DIAG_STAT_POWER_HIGH_BIT 1
-/* Power supply below 3.15 V */
+/* Power supply below 2.975 V */
#define ADIS16201_DIAG_STAT_POWER_LOW_BIT 0
/* System Command Register Definition */
@@ -231,7 +230,7 @@
[ADIS16201_DIAG_STAT_SPI_FAIL_BIT] = "SPI failure",
[ADIS16201_DIAG_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed",
[ADIS16201_DIAG_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
- [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+ [ADIS16201_DIAG_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
};
static const struct adis_data adis16201_data = {
diff --git a/drivers/iio/accel/adis16209.c b/drivers/iio/accel/adis16209.c
index f2dc3a5..98d77af 100644
--- a/drivers/iio/accel/adis16209.c
+++ b/drivers/iio/accel/adis16209.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADIS16209 Dual-Axis Digital Inclinometer and Accelerometer
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/device.h>
@@ -73,7 +72,7 @@
#define ADIS16209_STAT_FLASH_UPT_FAIL_BIT 2
/* Power supply above 3.625 V */
#define ADIS16209_STAT_POWER_HIGH_BIT 1
-/* Power supply below 3.15 V */
+/* Power supply below 2.975 V */
#define ADIS16209_STAT_POWER_LOW_BIT 0
#define ADIS16209_CMD_REG 0x3E
@@ -241,7 +240,7 @@
[ADIS16209_STAT_SPI_FAIL_BIT] = "SPI failure",
[ADIS16209_STAT_FLASH_UPT_FAIL_BIT] = "Flash update failed",
[ADIS16209_STAT_POWER_HIGH_BIT] = "Power supply above 3.625V",
- [ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 3.15V",
+ [ADIS16209_STAT_POWER_LOW_BIT] = "Power supply below 2.975V",
};
static const struct adis_data adis16209_data = {
diff --git a/drivers/iio/accel/adxl345.h b/drivers/iio/accel/adxl345.h
index ccd63de..3844977 100644
--- a/drivers/iio/accel/adxl345.h
+++ b/drivers/iio/accel/adxl345.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* ADXL345 3-Axis Digital Accelerometer
*
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
*/
#ifndef _ADXL345_H_
diff --git a/drivers/iio/accel/adxl345_core.c b/drivers/iio/accel/adxl345_core.c
index 780f87f..9c26979 100644
--- a/drivers/iio/accel/adxl345_core.c
+++ b/drivers/iio/accel/adxl345_core.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADXL345 3-Axis Digital Accelerometer IIO core driver
*
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/ADXL345.pdf
*/
@@ -150,8 +147,8 @@
}
static int adxl345_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
{
struct adxl345_data *data = iio_priv(indio_dev);
s64 n;
diff --git a/drivers/iio/accel/adxl345_i2c.c b/drivers/iio/accel/adxl345_i2c.c
index 785c89d..1561364 100644
--- a/drivers/iio/accel/adxl345_i2c.c
+++ b/drivers/iio/accel/adxl345_i2c.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADXL345 3-Axis Digital Accelerometer I2C driver
*
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* 7-bit I2C slave address: 0x1D (ALT ADDRESS pin tied to VDDIO) or
* 0x53 (ALT ADDRESS pin grounded)
*/
@@ -27,6 +24,9 @@
{
struct regmap *regmap;
+ if (!id)
+ return -ENODEV;
+
regmap = devm_regmap_init_i2c(client, &adxl345_i2c_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&client->dev, "Error initializing i2c regmap: %ld\n",
@@ -35,7 +35,7 @@
}
return adxl345_core_probe(&client->dev, regmap, id->driver_data,
- id ? id->name : NULL);
+ id->name);
}
static int adxl345_i2c_remove(struct i2c_client *client)
diff --git a/drivers/iio/accel/adxl345_spi.c b/drivers/iio/accel/adxl345_spi.c
index 67b7c66..da4591c 100644
--- a/drivers/iio/accel/adxl345_spi.c
+++ b/drivers/iio/accel/adxl345_spi.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADXL345 3-Axis Digital Accelerometer SPI driver
*
* Copyright (c) 2017 Eva Rachel Retuya <eraretuya@gmail.com>
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
*/
#include <linux/module.h>
diff --git a/drivers/iio/accel/adxl372.c b/drivers/iio/accel/adxl372.c
new file mode 100644
index 0000000..67b8817
--- /dev/null
+++ b/drivers/iio/accel/adxl372.c
@@ -0,0 +1,992 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ADXL372 3-Axis Digital Accelerometer core driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+
+#include <linux/bitops.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "adxl372.h"
+
+/* ADXL372 registers definition */
+#define ADXL372_DEVID 0x00
+#define ADXL372_DEVID_MST 0x01
+#define ADXL372_PARTID 0x02
+#define ADXL372_STATUS_1 0x04
+#define ADXL372_STATUS_2 0x05
+#define ADXL372_FIFO_ENTRIES_2 0x06
+#define ADXL372_FIFO_ENTRIES_1 0x07
+#define ADXL372_X_DATA_H 0x08
+#define ADXL372_X_DATA_L 0x09
+#define ADXL372_Y_DATA_H 0x0A
+#define ADXL372_Y_DATA_L 0x0B
+#define ADXL372_Z_DATA_H 0x0C
+#define ADXL372_Z_DATA_L 0x0D
+#define ADXL372_X_MAXPEAK_H 0x15
+#define ADXL372_X_MAXPEAK_L 0x16
+#define ADXL372_Y_MAXPEAK_H 0x17
+#define ADXL372_Y_MAXPEAK_L 0x18
+#define ADXL372_Z_MAXPEAK_H 0x19
+#define ADXL372_Z_MAXPEAK_L 0x1A
+#define ADXL372_OFFSET_X 0x20
+#define ADXL372_OFFSET_Y 0x21
+#define ADXL372_OFFSET_Z 0x22
+#define ADXL372_X_THRESH_ACT_H 0x23
+#define ADXL372_X_THRESH_ACT_L 0x24
+#define ADXL372_Y_THRESH_ACT_H 0x25
+#define ADXL372_Y_THRESH_ACT_L 0x26
+#define ADXL372_Z_THRESH_ACT_H 0x27
+#define ADXL372_Z_THRESH_ACT_L 0x28
+#define ADXL372_TIME_ACT 0x29
+#define ADXL372_X_THRESH_INACT_H 0x2A
+#define ADXL372_X_THRESH_INACT_L 0x2B
+#define ADXL372_Y_THRESH_INACT_H 0x2C
+#define ADXL372_Y_THRESH_INACT_L 0x2D
+#define ADXL372_Z_THRESH_INACT_H 0x2E
+#define ADXL372_Z_THRESH_INACT_L 0x2F
+#define ADXL372_TIME_INACT_H 0x30
+#define ADXL372_TIME_INACT_L 0x31
+#define ADXL372_X_THRESH_ACT2_H 0x32
+#define ADXL372_X_THRESH_ACT2_L 0x33
+#define ADXL372_Y_THRESH_ACT2_H 0x34
+#define ADXL372_Y_THRESH_ACT2_L 0x35
+#define ADXL372_Z_THRESH_ACT2_H 0x36
+#define ADXL372_Z_THRESH_ACT2_L 0x37
+#define ADXL372_HPF 0x38
+#define ADXL372_FIFO_SAMPLES 0x39
+#define ADXL372_FIFO_CTL 0x3A
+#define ADXL372_INT1_MAP 0x3B
+#define ADXL372_INT2_MAP 0x3C
+#define ADXL372_TIMING 0x3D
+#define ADXL372_MEASURE 0x3E
+#define ADXL372_POWER_CTL 0x3F
+#define ADXL372_SELF_TEST 0x40
+#define ADXL372_RESET 0x41
+#define ADXL372_FIFO_DATA 0x42
+
+#define ADXL372_DEVID_VAL 0xAD
+#define ADXL372_PARTID_VAL 0xFA
+#define ADXL372_RESET_CODE 0x52
+
+/* ADXL372_POWER_CTL */
+#define ADXL372_POWER_CTL_MODE_MSK GENMASK_ULL(1, 0)
+#define ADXL372_POWER_CTL_MODE(x) (((x) & 0x3) << 0)
+
+/* ADXL372_MEASURE */
+#define ADXL372_MEASURE_LINKLOOP_MSK GENMASK_ULL(5, 4)
+#define ADXL372_MEASURE_LINKLOOP_MODE(x) (((x) & 0x3) << 4)
+#define ADXL372_MEASURE_BANDWIDTH_MSK GENMASK_ULL(2, 0)
+#define ADXL372_MEASURE_BANDWIDTH_MODE(x) (((x) & 0x7) << 0)
+
+/* ADXL372_TIMING */
+#define ADXL372_TIMING_ODR_MSK GENMASK_ULL(7, 5)
+#define ADXL372_TIMING_ODR_MODE(x) (((x) & 0x7) << 5)
+
+/* ADXL372_FIFO_CTL */
+#define ADXL372_FIFO_CTL_FORMAT_MSK GENMASK(5, 3)
+#define ADXL372_FIFO_CTL_FORMAT_MODE(x) (((x) & 0x7) << 3)
+#define ADXL372_FIFO_CTL_MODE_MSK GENMASK(2, 1)
+#define ADXL372_FIFO_CTL_MODE_MODE(x) (((x) & 0x3) << 1)
+#define ADXL372_FIFO_CTL_SAMPLES_MSK BIT(1)
+#define ADXL372_FIFO_CTL_SAMPLES_MODE(x) (((x) > 0xFF) ? 1 : 0)
+
+/* ADXL372_STATUS_1 */
+#define ADXL372_STATUS_1_DATA_RDY(x) (((x) >> 0) & 0x1)
+#define ADXL372_STATUS_1_FIFO_RDY(x) (((x) >> 1) & 0x1)
+#define ADXL372_STATUS_1_FIFO_FULL(x) (((x) >> 2) & 0x1)
+#define ADXL372_STATUS_1_FIFO_OVR(x) (((x) >> 3) & 0x1)
+#define ADXL372_STATUS_1_USR_NVM_BUSY(x) (((x) >> 5) & 0x1)
+#define ADXL372_STATUS_1_AWAKE(x) (((x) >> 6) & 0x1)
+#define ADXL372_STATUS_1_ERR_USR_REGS(x) (((x) >> 7) & 0x1)
+
+/* ADXL372_INT1_MAP */
+#define ADXL372_INT1_MAP_DATA_RDY_MSK BIT(0)
+#define ADXL372_INT1_MAP_DATA_RDY_MODE(x) (((x) & 0x1) << 0)
+#define ADXL372_INT1_MAP_FIFO_RDY_MSK BIT(1)
+#define ADXL372_INT1_MAP_FIFO_RDY_MODE(x) (((x) & 0x1) << 1)
+#define ADXL372_INT1_MAP_FIFO_FULL_MSK BIT(2)
+#define ADXL372_INT1_MAP_FIFO_FULL_MODE(x) (((x) & 0x1) << 2)
+#define ADXL372_INT1_MAP_FIFO_OVR_MSK BIT(3)
+#define ADXL372_INT1_MAP_FIFO_OVR_MODE(x) (((x) & 0x1) << 3)
+#define ADXL372_INT1_MAP_INACT_MSK BIT(4)
+#define ADXL372_INT1_MAP_INACT_MODE(x) (((x) & 0x1) << 4)
+#define ADXL372_INT1_MAP_ACT_MSK BIT(5)
+#define ADXL372_INT1_MAP_ACT_MODE(x) (((x) & 0x1) << 5)
+#define ADXL372_INT1_MAP_AWAKE_MSK BIT(6)
+#define ADXL372_INT1_MAP_AWAKE_MODE(x) (((x) & 0x1) << 6)
+#define ADXL372_INT1_MAP_LOW_MSK BIT(7)
+#define ADXL372_INT1_MAP_LOW_MODE(x) (((x) & 0x1) << 7)
+
+/* The ADXL372 includes a deep, 512 sample FIFO buffer */
+#define ADXL372_FIFO_SIZE 512
+
+/*
+ * At +/- 200g with 12-bit resolution, scale is computed as:
+ * (200 + 200) * 9.81 / (2^12 - 1) = 0.958241
+ */
+#define ADXL372_USCALE 958241
+
+enum adxl372_op_mode {
+ ADXL372_STANDBY,
+ ADXL372_WAKE_UP,
+ ADXL372_INSTANT_ON,
+ ADXL372_FULL_BW_MEASUREMENT,
+};
+
+enum adxl372_act_proc_mode {
+ ADXL372_DEFAULT,
+ ADXL372_LINKED,
+ ADXL372_LOOPED,
+};
+
+enum adxl372_th_activity {
+ ADXL372_ACTIVITY,
+ ADXL372_ACTIVITY2,
+ ADXL372_INACTIVITY,
+};
+
+enum adxl372_odr {
+ ADXL372_ODR_400HZ,
+ ADXL372_ODR_800HZ,
+ ADXL372_ODR_1600HZ,
+ ADXL372_ODR_3200HZ,
+ ADXL372_ODR_6400HZ,
+};
+
+enum adxl372_bandwidth {
+ ADXL372_BW_200HZ,
+ ADXL372_BW_400HZ,
+ ADXL372_BW_800HZ,
+ ADXL372_BW_1600HZ,
+ ADXL372_BW_3200HZ,
+};
+
+static const unsigned int adxl372_th_reg_high_addr[3] = {
+ [ADXL372_ACTIVITY] = ADXL372_X_THRESH_ACT_H,
+ [ADXL372_ACTIVITY2] = ADXL372_X_THRESH_ACT2_H,
+ [ADXL372_INACTIVITY] = ADXL372_X_THRESH_INACT_H,
+};
+
+enum adxl372_fifo_format {
+ ADXL372_XYZ_FIFO,
+ ADXL372_X_FIFO,
+ ADXL372_Y_FIFO,
+ ADXL372_XY_FIFO,
+ ADXL372_Z_FIFO,
+ ADXL372_XZ_FIFO,
+ ADXL372_YZ_FIFO,
+ ADXL372_XYZ_PEAK_FIFO,
+};
+
+enum adxl372_fifo_mode {
+ ADXL372_FIFO_BYPASSED,
+ ADXL372_FIFO_STREAMED,
+ ADXL372_FIFO_TRIGGERED,
+ ADXL372_FIFO_OLD_SAVED
+};
+
+static const int adxl372_samp_freq_tbl[5] = {
+ 400, 800, 1600, 3200, 6400,
+};
+
+static const int adxl372_bw_freq_tbl[5] = {
+ 200, 400, 800, 1600, 3200,
+};
+
+struct adxl372_axis_lookup {
+ unsigned int bits;
+ enum adxl372_fifo_format fifo_format;
+};
+
+static const struct adxl372_axis_lookup adxl372_axis_lookup_table[] = {
+ { BIT(0), ADXL372_X_FIFO },
+ { BIT(1), ADXL372_Y_FIFO },
+ { BIT(2), ADXL372_Z_FIFO },
+ { BIT(0) | BIT(1), ADXL372_XY_FIFO },
+ { BIT(0) | BIT(2), ADXL372_XZ_FIFO },
+ { BIT(1) | BIT(2), ADXL372_YZ_FIFO },
+ { BIT(0) | BIT(1) | BIT(2), ADXL372_XYZ_FIFO },
+};
+
+#define ADXL372_ACCEL_CHANNEL(index, reg, axis) { \
+ .type = IIO_ACCEL, \
+ .address = reg, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY), \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 12, \
+ .storagebits = 16, \
+ .shift = 4, \
+ }, \
+}
+
+static const struct iio_chan_spec adxl372_channels[] = {
+ ADXL372_ACCEL_CHANNEL(0, ADXL372_X_DATA_H, X),
+ ADXL372_ACCEL_CHANNEL(1, ADXL372_Y_DATA_H, Y),
+ ADXL372_ACCEL_CHANNEL(2, ADXL372_Z_DATA_H, Z),
+};
+
+struct adxl372_state {
+ int irq;
+ struct device *dev;
+ struct regmap *regmap;
+ struct iio_trigger *dready_trig;
+ enum adxl372_fifo_mode fifo_mode;
+ enum adxl372_fifo_format fifo_format;
+ enum adxl372_op_mode op_mode;
+ enum adxl372_act_proc_mode act_proc_mode;
+ enum adxl372_odr odr;
+ enum adxl372_bandwidth bw;
+ u32 act_time_ms;
+ u32 inact_time_ms;
+ u8 fifo_set_size;
+ u8 int1_bitmask;
+ u8 int2_bitmask;
+ u16 watermark;
+ __be16 fifo_buf[ADXL372_FIFO_SIZE];
+};
+
+static const unsigned long adxl372_channel_masks[] = {
+ BIT(0), BIT(1), BIT(2),
+ BIT(0) | BIT(1),
+ BIT(0) | BIT(2),
+ BIT(1) | BIT(2),
+ BIT(0) | BIT(1) | BIT(2),
+ 0
+};
+
+static int adxl372_read_axis(struct adxl372_state *st, u8 addr)
+{
+ __be16 regval;
+ int ret;
+
+ ret = regmap_bulk_read(st->regmap, addr, ®val, sizeof(regval));
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu(regval);
+}
+
+static int adxl372_set_op_mode(struct adxl372_state *st,
+ enum adxl372_op_mode op_mode)
+{
+ int ret;
+
+ ret = regmap_update_bits(st->regmap, ADXL372_POWER_CTL,
+ ADXL372_POWER_CTL_MODE_MSK,
+ ADXL372_POWER_CTL_MODE(op_mode));
+ if (ret < 0)
+ return ret;
+
+ st->op_mode = op_mode;
+
+ return ret;
+}
+
+static int adxl372_set_odr(struct adxl372_state *st,
+ enum adxl372_odr odr)
+{
+ int ret;
+
+ ret = regmap_update_bits(st->regmap, ADXL372_TIMING,
+ ADXL372_TIMING_ODR_MSK,
+ ADXL372_TIMING_ODR_MODE(odr));
+ if (ret < 0)
+ return ret;
+
+ st->odr = odr;
+
+ return ret;
+}
+
+static int adxl372_find_closest_match(const int *array,
+ unsigned int size, int val)
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ if (val <= array[i])
+ return i;
+ }
+
+ return size - 1;
+}
+
+static int adxl372_set_bandwidth(struct adxl372_state *st,
+ enum adxl372_bandwidth bw)
+{
+ int ret;
+
+ ret = regmap_update_bits(st->regmap, ADXL372_MEASURE,
+ ADXL372_MEASURE_BANDWIDTH_MSK,
+ ADXL372_MEASURE_BANDWIDTH_MODE(bw));
+ if (ret < 0)
+ return ret;
+
+ st->bw = bw;
+
+ return ret;
+}
+
+static int adxl372_set_act_proc_mode(struct adxl372_state *st,
+ enum adxl372_act_proc_mode mode)
+{
+ int ret;
+
+ ret = regmap_update_bits(st->regmap,
+ ADXL372_MEASURE,
+ ADXL372_MEASURE_LINKLOOP_MSK,
+ ADXL372_MEASURE_LINKLOOP_MODE(mode));
+ if (ret < 0)
+ return ret;
+
+ st->act_proc_mode = mode;
+
+ return ret;
+}
+
+static int adxl372_set_activity_threshold(struct adxl372_state *st,
+ enum adxl372_th_activity act,
+ bool ref_en, bool enable,
+ unsigned int threshold)
+{
+ unsigned char buf[6];
+ unsigned char th_reg_high_val, th_reg_low_val, th_reg_high_addr;
+
+ /* scale factor is 100 mg/code */
+ th_reg_high_val = (threshold / 100) >> 3;
+ th_reg_low_val = ((threshold / 100) << 5) | (ref_en << 1) | enable;
+ th_reg_high_addr = adxl372_th_reg_high_addr[act];
+
+ buf[0] = th_reg_high_val;
+ buf[1] = th_reg_low_val;
+ buf[2] = th_reg_high_val;
+ buf[3] = th_reg_low_val;
+ buf[4] = th_reg_high_val;
+ buf[5] = th_reg_low_val;
+
+ return regmap_bulk_write(st->regmap, th_reg_high_addr,
+ buf, ARRAY_SIZE(buf));
+}
+
+static int adxl372_set_activity_time_ms(struct adxl372_state *st,
+ unsigned int act_time_ms)
+{
+ unsigned int reg_val, scale_factor;
+ int ret;
+
+ /*
+ * 3.3 ms per code is the scale factor of the TIME_ACT register for
+ * ODR = 6400 Hz. It is 6.6 ms per code for ODR = 3200 Hz and below.
+ */
+ if (st->odr == ADXL372_ODR_6400HZ)
+ scale_factor = 3300;
+ else
+ scale_factor = 6600;
+
+ reg_val = DIV_ROUND_CLOSEST(act_time_ms * 1000, scale_factor);
+
+ /* TIME_ACT register is 8 bits wide */
+ if (reg_val > 0xFF)
+ reg_val = 0xFF;
+
+ ret = regmap_write(st->regmap, ADXL372_TIME_ACT, reg_val);
+ if (ret < 0)
+ return ret;
+
+ st->act_time_ms = act_time_ms;
+
+ return ret;
+}
+
+static int adxl372_set_inactivity_time_ms(struct adxl372_state *st,
+ unsigned int inact_time_ms)
+{
+ unsigned int reg_val_h, reg_val_l, res, scale_factor;
+ int ret;
+
+ /*
+ * 13 ms per code is the scale factor of the TIME_INACT register for
+ * ODR = 6400 Hz. It is 26 ms per code for ODR = 3200 Hz and below.
+ */
+ if (st->odr == ADXL372_ODR_6400HZ)
+ scale_factor = 13;
+ else
+ scale_factor = 26;
+
+ res = DIV_ROUND_CLOSEST(inact_time_ms, scale_factor);
+ reg_val_h = (res >> 8) & 0xFF;
+ reg_val_l = res & 0xFF;
+
+ ret = regmap_write(st->regmap, ADXL372_TIME_INACT_H, reg_val_h);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(st->regmap, ADXL372_TIME_INACT_L, reg_val_l);
+ if (ret < 0)
+ return ret;
+
+ st->inact_time_ms = inact_time_ms;
+
+ return ret;
+}
+
+static int adxl372_set_interrupts(struct adxl372_state *st,
+ unsigned char int1_bitmask,
+ unsigned char int2_bitmask)
+{
+ int ret;
+
+ ret = regmap_write(st->regmap, ADXL372_INT1_MAP, int1_bitmask);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(st->regmap, ADXL372_INT2_MAP, int2_bitmask);
+}
+
+static int adxl372_configure_fifo(struct adxl372_state *st)
+{
+ unsigned int fifo_samples, fifo_ctl;
+ int ret;
+
+ /* FIFO must be configured while in standby mode */
+ ret = adxl372_set_op_mode(st, ADXL372_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * watermark stores the number of sets; we need to write the FIFO
+ * registers with the number of samples
+ */
+ fifo_samples = (st->watermark * st->fifo_set_size);
+ fifo_ctl = ADXL372_FIFO_CTL_FORMAT_MODE(st->fifo_format) |
+ ADXL372_FIFO_CTL_MODE_MODE(st->fifo_mode) |
+ ADXL372_FIFO_CTL_SAMPLES_MODE(fifo_samples);
+
+ ret = regmap_write(st->regmap,
+ ADXL372_FIFO_SAMPLES, fifo_samples & 0xFF);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_write(st->regmap, ADXL372_FIFO_CTL, fifo_ctl);
+ if (ret < 0)
+ return ret;
+
+ return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT);
+}
+
+static int adxl372_get_status(struct adxl372_state *st,
+ u8 *status1, u8 *status2,
+ u16 *fifo_entries)
+{
+ __be32 buf;
+ u32 val;
+ int ret;
+
+ /* STATUS1, STATUS2, FIFO_ENTRIES2 and FIFO_ENTRIES are adjacent regs */
+ ret = regmap_bulk_read(st->regmap, ADXL372_STATUS_1,
+ &buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ val = be32_to_cpu(buf);
+
+ *status1 = (val >> 24) & 0x0F;
+ *status2 = (val >> 16) & 0x0F;
+ /*
+ * FIFO_ENTRIES contains the least significant byte, and FIFO_ENTRIES2
+ * contains the two most significant bits
+ */
+ *fifo_entries = val & 0x3FF;
+
+ return ret;
+}
+
+static irqreturn_t adxl372_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adxl372_state *st = iio_priv(indio_dev);
+ u8 status1, status2;
+ u16 fifo_entries;
+ int i, ret;
+
+ ret = adxl372_get_status(st, &status1, &status2, &fifo_entries);
+ if (ret < 0)
+ goto err;
+
+ if (st->fifo_mode != ADXL372_FIFO_BYPASSED &&
+ ADXL372_STATUS_1_FIFO_FULL(status1)) {
+ /*
+ * When reading data from multiple axes from the FIFO,
+ * to ensure that data is not overwritten and stored out
+ * of order at least one sample set must be left in the
+ * FIFO after every read.
+ */
+ fifo_entries -= st->fifo_set_size;
+
+ /* Read data from the FIFO */
+ ret = regmap_noinc_read(st->regmap, ADXL372_FIFO_DATA,
+ st->fifo_buf,
+ fifo_entries * sizeof(u16));
+ if (ret < 0)
+ goto err;
+
+ /* Each sample is 2 bytes */
+ for (i = 0; i < fifo_entries; i += st->fifo_set_size)
+ iio_push_to_buffers(indio_dev, &st->fifo_buf[i]);
+ }
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int adxl372_setup(struct adxl372_state *st)
+{
+ unsigned int regval;
+ int ret;
+
+ ret = regmap_read(st->regmap, ADXL372_DEVID, ®val);
+ if (ret < 0)
+ return ret;
+
+ if (regval != ADXL372_DEVID_VAL) {
+ dev_err(st->dev, "Invalid chip id %x\n", regval);
+ return -ENODEV;
+ }
+
+ /*
+ * Perform a software reset to make sure the device is in a consistent
+ * state after start up.
+ */
+ ret = regmap_write(st->regmap, ADXL372_RESET, ADXL372_RESET_CODE);
+ if (ret < 0)
+ return ret;
+
+ ret = adxl372_set_op_mode(st, ADXL372_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ /* Set threshold for activity detection to 1g */
+ ret = adxl372_set_activity_threshold(st, ADXL372_ACTIVITY,
+ true, true, 1000);
+ if (ret < 0)
+ return ret;
+
+ /* Set threshold for inactivity detection to 100mg */
+ ret = adxl372_set_activity_threshold(st, ADXL372_INACTIVITY,
+ true, true, 100);
+ if (ret < 0)
+ return ret;
+
+ /* Set activity processing in Looped mode */
+ ret = adxl372_set_act_proc_mode(st, ADXL372_LOOPED);
+ if (ret < 0)
+ return ret;
+
+ ret = adxl372_set_odr(st, ADXL372_ODR_6400HZ);
+ if (ret < 0)
+ return ret;
+
+ ret = adxl372_set_bandwidth(st, ADXL372_BW_3200HZ);
+ if (ret < 0)
+ return ret;
+
+ /* Set activity timer to 1ms */
+ ret = adxl372_set_activity_time_ms(st, 1);
+ if (ret < 0)
+ return ret;
+
+ /* Set inactivity timer to 10s */
+ ret = adxl372_set_inactivity_time_ms(st, 10000);
+ if (ret < 0)
+ return ret;
+
+ /* Set the mode of operation to full bandwidth measurement mode */
+ return adxl372_set_op_mode(st, ADXL372_FULL_BW_MEASUREMENT);
+}
+
+static int adxl372_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct adxl372_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+ else
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static int adxl372_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct adxl372_state *st = iio_priv(indio_dev);
+ int ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = adxl372_read_axis(st, chan->address);
+ iio_device_release_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ *val = sign_extend32(ret >> chan->scan_type.shift,
+ chan->scan_type.realbits - 1);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = ADXL372_USCALE;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = adxl372_samp_freq_tbl[st->odr];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ *val = adxl372_bw_freq_tbl[st->bw];
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int adxl372_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct adxl372_state *st = iio_priv(indio_dev);
+ int odr_index, bw_index, ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ odr_index = adxl372_find_closest_match(adxl372_samp_freq_tbl,
+ ARRAY_SIZE(adxl372_samp_freq_tbl),
+ val);
+ ret = adxl372_set_odr(st, odr_index);
+ if (ret < 0)
+ return ret;
+ /*
+ * The timer period depends on the ODR selected.
+ * At 3200 Hz and below, it is 6.6 ms; at 6400 Hz, it is 3.3 ms
+ */
+ ret = adxl372_set_activity_time_ms(st, st->act_time_ms);
+ if (ret < 0)
+ return ret;
+ /*
+ * The timer period depends on the ODR selected.
+ * At 3200 Hz and below, it is 26 ms; at 6400 Hz, it is 13 ms
+ */
+ ret = adxl372_set_inactivity_time_ms(st, st->inact_time_ms);
+ if (ret < 0)
+ return ret;
+ /*
+ * The maximum bandwidth is constrained to at most half of
+ * the ODR to ensure that the Nyquist criteria is not violated
+ */
+ if (st->bw > odr_index)
+ ret = adxl372_set_bandwidth(st, odr_index);
+
+ return ret;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ bw_index = adxl372_find_closest_match(adxl372_bw_freq_tbl,
+ ARRAY_SIZE(adxl372_bw_freq_tbl),
+ val);
+ return adxl372_set_bandwidth(st, bw_index);
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t adxl372_show_filter_freq_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct adxl372_state *st = iio_priv(indio_dev);
+ int i;
+ size_t len = 0;
+
+ for (i = 0; i <= st->odr; i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ "%d ", adxl372_bw_freq_tbl[i]);
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t adxl372_get_fifo_enabled(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct adxl372_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->fifo_mode);
+}
+
+static ssize_t adxl372_get_fifo_watermark(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct adxl372_state *st = iio_priv(indio_dev);
+
+ return sprintf(buf, "%d\n", st->watermark);
+}
+
+static IIO_CONST_ATTR(hwfifo_watermark_min, "1");
+static IIO_CONST_ATTR(hwfifo_watermark_max,
+ __stringify(ADXL372_FIFO_SIZE));
+static IIO_DEVICE_ATTR(hwfifo_watermark, 0444,
+ adxl372_get_fifo_watermark, NULL, 0);
+static IIO_DEVICE_ATTR(hwfifo_enabled, 0444,
+ adxl372_get_fifo_enabled, NULL, 0);
+
+static const struct attribute *adxl372_fifo_attributes[] = {
+ &iio_const_attr_hwfifo_watermark_min.dev_attr.attr,
+ &iio_const_attr_hwfifo_watermark_max.dev_attr.attr,
+ &iio_dev_attr_hwfifo_watermark.dev_attr.attr,
+ &iio_dev_attr_hwfifo_enabled.dev_attr.attr,
+ NULL,
+};
+
+static int adxl372_set_watermark(struct iio_dev *indio_dev, unsigned int val)
+{
+ struct adxl372_state *st = iio_priv(indio_dev);
+
+ if (val > ADXL372_FIFO_SIZE)
+ val = ADXL372_FIFO_SIZE;
+
+ st->watermark = val;
+
+ return 0;
+}
+
+static int adxl372_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct adxl372_state *st = iio_priv(indio_dev);
+ unsigned int mask;
+ int i, ret;
+
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = adxl372_set_interrupts(st, ADXL372_INT1_MAP_FIFO_FULL_MSK, 0);
+ if (ret < 0)
+ goto err;
+
+ mask = *indio_dev->active_scan_mask;
+
+ for (i = 0; i < ARRAY_SIZE(adxl372_axis_lookup_table); i++) {
+ if (mask == adxl372_axis_lookup_table[i].bits)
+ break;
+ }
+
+ if (i == ARRAY_SIZE(adxl372_axis_lookup_table)) {
+ ret = -EINVAL;
+ goto err;
+ }
+
+ st->fifo_format = adxl372_axis_lookup_table[i].fifo_format;
+ st->fifo_set_size = bitmap_weight(indio_dev->active_scan_mask,
+ indio_dev->masklength);
+ /*
+ * The 512 FIFO samples can be allotted in several ways, such as:
+ * 170 sample sets of concurrent 3-axis data
+ * 256 sample sets of concurrent 2-axis data (user selectable)
+ * 512 sample sets of single-axis data
+ */
+ if ((st->watermark * st->fifo_set_size) > ADXL372_FIFO_SIZE)
+ st->watermark = (ADXL372_FIFO_SIZE / st->fifo_set_size);
+
+ st->fifo_mode = ADXL372_FIFO_STREAMED;
+
+ ret = adxl372_configure_fifo(st);
+ if (ret < 0) {
+ st->fifo_mode = ADXL372_FIFO_BYPASSED;
+ adxl372_set_interrupts(st, 0, 0);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ iio_triggered_buffer_predisable(indio_dev);
+ return ret;
+}
+
+static int adxl372_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct adxl372_state *st = iio_priv(indio_dev);
+
+ adxl372_set_interrupts(st, 0, 0);
+ st->fifo_mode = ADXL372_FIFO_BYPASSED;
+ adxl372_configure_fifo(st);
+
+ return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops adxl372_buffer_ops = {
+ .postenable = adxl372_buffer_postenable,
+ .predisable = adxl372_buffer_predisable,
+};
+
+static int adxl372_dready_trig_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct adxl372_state *st = iio_priv(indio_dev);
+ unsigned long int mask = 0;
+
+ if (state)
+ mask = ADXL372_INT1_MAP_FIFO_FULL_MSK;
+
+ return adxl372_set_interrupts(st, mask, 0);
+}
+
+static int adxl372_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct adxl372_state *st = iio_priv(indio_dev);
+
+ if (st->dready_trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct iio_trigger_ops adxl372_trigger_ops = {
+ .validate_device = &iio_trigger_validate_own_device,
+ .set_trigger_state = adxl372_dready_trig_set_state,
+};
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("400 800 1600 3200 6400");
+static IIO_DEVICE_ATTR(in_accel_filter_low_pass_3db_frequency_available,
+ 0444, adxl372_show_filter_freq_avail, NULL, 0);
+
+static struct attribute *adxl372_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_dev_attr_in_accel_filter_low_pass_3db_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group adxl372_attrs_group = {
+ .attrs = adxl372_attributes,
+};
+
+static const struct iio_info adxl372_info = {
+ .validate_trigger = &adxl372_validate_trigger,
+ .attrs = &adxl372_attrs_group,
+ .read_raw = adxl372_read_raw,
+ .write_raw = adxl372_write_raw,
+ .debugfs_reg_access = &adxl372_reg_access,
+ .hwfifo_set_watermark = adxl372_set_watermark,
+};
+
+bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg)
+{
+ return (reg == ADXL372_FIFO_DATA);
+}
+EXPORT_SYMBOL_GPL(adxl372_readable_noinc_reg);
+
+int adxl372_probe(struct device *dev, struct regmap *regmap,
+ int irq, const char *name)
+{
+ struct iio_dev *indio_dev;
+ struct adxl372_state *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+
+ st->dev = dev;
+ st->regmap = regmap;
+ st->irq = irq;
+
+ indio_dev->channels = adxl372_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adxl372_channels);
+ indio_dev->available_scan_masks = adxl372_channel_masks;
+ indio_dev->dev.parent = dev;
+ indio_dev->name = name;
+ indio_dev->info = &adxl372_info;
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+
+ ret = adxl372_setup(st);
+ if (ret < 0) {
+ dev_err(dev, "ADXL372 setup failed\n");
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev,
+ indio_dev, NULL,
+ adxl372_trigger_handler,
+ &adxl372_buffer_ops);
+ if (ret < 0)
+ return ret;
+
+ iio_buffer_set_attrs(indio_dev->buffer, adxl372_fifo_attributes);
+
+ if (st->irq) {
+ st->dready_trig = devm_iio_trigger_alloc(dev,
+ "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (st->dready_trig == NULL)
+ return -ENOMEM;
+
+ st->dready_trig->ops = &adxl372_trigger_ops;
+ st->dready_trig->dev.parent = dev;
+ iio_trigger_set_drvdata(st->dready_trig, indio_dev);
+ ret = devm_iio_trigger_register(dev, st->dready_trig);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(st->dready_trig);
+
+ ret = devm_request_threaded_irq(dev, st->irq,
+ iio_trigger_generic_data_rdy_poll,
+ NULL,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ indio_dev->name, st->dready_trig);
+ if (ret < 0)
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_GPL(adxl372_probe);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/adxl372.h b/drivers/iio/accel/adxl372.h
new file mode 100644
index 0000000..80a0aa9
--- /dev/null
+++ b/drivers/iio/accel/adxl372.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * ADXL372 3-Axis Digital Accelerometer
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+
+#ifndef _ADXL372_H_
+#define _ADXL372_H_
+
+#define ADXL372_REVID 0x03
+
+int adxl372_probe(struct device *dev, struct regmap *regmap,
+ int irq, const char *name);
+bool adxl372_readable_noinc_reg(struct device *dev, unsigned int reg);
+
+#endif /* _ADXL372_H_ */
diff --git a/drivers/iio/accel/adxl372_i2c.c b/drivers/iio/accel/adxl372_i2c.c
new file mode 100644
index 0000000..e1affe4
--- /dev/null
+++ b/drivers/iio/accel/adxl372_i2c.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ADXL372 3-Axis Digital Accelerometer I2C driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "adxl372.h"
+
+static const struct regmap_config adxl372_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .readable_noinc_reg = adxl372_readable_noinc_reg,
+};
+
+static int adxl372_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct regmap *regmap;
+ unsigned int regval;
+ int ret;
+
+ regmap = devm_regmap_init_i2c(client, &adxl372_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ ret = regmap_read(regmap, ADXL372_REVID, ®val);
+ if (ret < 0)
+ return ret;
+
+ /* Starting with the 3rd revision an I2C chip bug was fixed */
+ if (regval < 3)
+ dev_warn(&client->dev,
+ "I2C might not work properly with other devices on the bus");
+
+ return adxl372_probe(&client->dev, regmap, client->irq, id->name);
+}
+
+static const struct i2c_device_id adxl372_i2c_id[] = {
+ { "adxl372", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, adxl372_i2c_id);
+
+static struct i2c_driver adxl372_i2c_driver = {
+ .driver = {
+ .name = "adxl372_i2c",
+ },
+ .probe = adxl372_i2c_probe,
+ .id_table = adxl372_i2c_id,
+};
+
+module_i2c_driver(adxl372_i2c_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer I2C driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/adxl372_spi.c b/drivers/iio/accel/adxl372_spi.c
new file mode 100644
index 0000000..3ef7e3a
--- /dev/null
+++ b/drivers/iio/accel/adxl372_spi.c
@@ -0,0 +1,61 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ADXL372 3-Axis Digital Accelerometer SPI driver
+ *
+ * Copyright 2018 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+
+#include "adxl372.h"
+
+static const struct regmap_config adxl372_spi_regmap_config = {
+ .reg_bits = 7,
+ .pad_bits = 1,
+ .val_bits = 8,
+ .read_flag_mask = BIT(0),
+ .readable_noinc_reg = adxl372_readable_noinc_reg,
+};
+
+static int adxl372_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &adxl372_spi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
+ return adxl372_probe(&spi->dev, regmap, spi->irq, id->name);
+}
+
+static const struct spi_device_id adxl372_spi_id[] = {
+ { "adxl372", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adxl372_spi_id);
+
+static const struct of_device_id adxl372_of_match[] = {
+ { .compatible = "adi,adxl372" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adxl372_of_match);
+
+static struct spi_driver adxl372_spi_driver = {
+ .driver = {
+ .name = "adxl372_spi",
+ .of_match_table = adxl372_of_match,
+ },
+ .probe = adxl372_spi_probe,
+ .id_table = adxl372_spi_id,
+};
+
+module_spi_driver(adxl372_spi_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADXL372 3-axis accelerometer SPI driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/accel/bma180.c b/drivers/iio/accel/bma180.c
index cb9765a..1574e46 100644
--- a/drivers/iio/accel/bma180.c
+++ b/drivers/iio/accel/bma180.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* bma180.c - IIO driver for Bosch BMA180 triaxial acceleration sensor
*
@@ -5,10 +6,6 @@
*
* Support for BMA250 (c) Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* SPI is not supported by driver
* BMA180: 7-bit I2C slave address 0x40 or 0x41
* BMA250: 7-bit I2C slave address 0x18 or 0x19
@@ -116,6 +113,7 @@
struct i2c_client *client;
struct iio_trigger *trig;
const struct bma180_part_info *part_info;
+ struct iio_mount_matrix orientation;
struct mutex mutex;
bool sleep_state;
int scale;
@@ -561,6 +559,15 @@
return ret;
}
+static const struct iio_mount_matrix *
+bma180_accel_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct bma180_data *data = iio_priv(indio_dev);
+
+ return &data->orientation;
+}
+
static const struct iio_enum bma180_power_mode_enum = {
.items = bma180_power_modes,
.num_items = ARRAY_SIZE(bma180_power_modes),
@@ -571,7 +578,8 @@
static const struct iio_chan_spec_ext_info bma180_ext_info[] = {
IIO_ENUM("power_mode", true, &bma180_power_mode_enum),
IIO_ENUM_AVAILABLE("power_mode", &bma180_power_mode_enum),
- { },
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bma180_accel_get_mount_matrix),
+ { }
};
#define BMA180_ACC_CHANNEL(_axis, _bits) { \
@@ -722,6 +730,11 @@
chip = id->driver_data;
data->part_info = &bma180_part_info[chip];
+ ret = iio_read_mount_matrix(&client->dev, "mount-matrix",
+ &data->orientation);
+ if (ret)
+ return ret;
+
ret = data->part_info->chip_config(data);
if (ret < 0)
goto err_chip_disable;
diff --git a/drivers/iio/accel/bma220_spi.c b/drivers/iio/accel/bma220_spi.c
index e25d91c..cae9050 100644
--- a/drivers/iio/accel/bma220_spi.c
+++ b/drivers/iio/accel/bma220_spi.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* BMA220 Digital triaxial acceleration sensor driver
*
* Copyright (c) 2016, Intel Corporation.
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
*/
#include <linux/acpi.h>
diff --git a/drivers/iio/accel/bmc150-accel-core.c b/drivers/iio/accel/bmc150-accel-core.c
index 383c802..121b4e8 100644
--- a/drivers/iio/accel/bmc150-accel-core.c
+++ b/drivers/iio/accel/bmc150-accel-core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 3-axis accelerometer driver supporting following Bosch-Sensortec chips:
* - BMC150
@@ -8,15 +9,6 @@
* - BMA280
*
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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>
@@ -125,7 +117,7 @@
#define BMC150_ACCEL_SLEEP_1_SEC 0x0F
#define BMC150_ACCEL_REG_TEMP 0x08
-#define BMC150_ACCEL_TEMP_CENTER_VAL 24
+#define BMC150_ACCEL_TEMP_CENTER_VAL 23
#define BMC150_ACCEL_AXIS_TO_REG(axis) (BMC150_ACCEL_REG_XOUT_L + (axis * 2))
#define BMC150_AUTO_SUSPEND_DELAY_MS 2000
@@ -204,6 +196,7 @@
int ev_enable_state;
int64_t timestamp, old_timestamp; /* Only used in hw fifo mode. */
const struct bmc150_accel_chip_info *chip_info;
+ struct iio_mount_matrix orientation;
};
static const struct {
@@ -393,7 +386,7 @@
if (ret < 0) {
dev_err(dev,
- "Failed: bmc150_accel_set_power_state for %d\n", on);
+ "Failed: %s for %d\n", __func__, on);
if (on)
pm_runtime_put_noidle(dev);
@@ -796,6 +789,20 @@
return sprintf(buf, "%d\n", state);
}
+static const struct iio_mount_matrix *
+bmc150_accel_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct bmc150_accel_data *data = iio_priv(indio_dev);
+
+ return &data->orientation;
+}
+
+static const struct iio_chan_spec_ext_info bmc150_accel_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bmc150_accel_get_mount_matrix),
+ { }
+};
+
static IIO_CONST_ATTR(hwfifo_watermark_min, "1");
static IIO_CONST_ATTR(hwfifo_watermark_max,
__stringify(BMC150_ACCEL_FIFO_LENGTH));
@@ -978,6 +985,7 @@
.shift = 16 - (bits), \
.endianness = IIO_LE, \
}, \
+ .ext_info = bmc150_accel_ext_info, \
.event_spec = &bmc150_accel_event, \
.num_event_specs = 1 \
}
@@ -1555,6 +1563,11 @@
data->regmap = regmap;
+ ret = iio_read_mount_matrix(dev, "mount-matrix",
+ &data->orientation);
+ if (ret)
+ return ret;
+
ret = bmc150_accel_chip_init(data);
if (ret < 0)
return ret;
diff --git a/drivers/iio/accel/bmc150-accel-i2c.c b/drivers/iio/accel/bmc150-accel-i2c.c
index 8ffc308..06021c8 100644
--- a/drivers/iio/accel/bmc150-accel-i2c.c
+++ b/drivers/iio/accel/bmc150-accel-i2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 3-axis accelerometer driver supporting following I2C Bosch-Sensortec chips:
* - BMC150
@@ -8,15 +9,6 @@
* - BMA280
*
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/device.h>
diff --git a/drivers/iio/accel/bmc150-accel-spi.c b/drivers/iio/accel/bmc150-accel-spi.c
index 006794a..2a8c311 100644
--- a/drivers/iio/accel/bmc150-accel-spi.c
+++ b/drivers/iio/accel/bmc150-accel-spi.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* 3-axis accelerometer driver supporting SPI Bosch-Sensortec accelerometer chip
* Copyright © 2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/device.h>
diff --git a/drivers/iio/accel/cros_ec_accel_legacy.c b/drivers/iio/accel/cros_ec_accel_legacy.c
index 063e89e..fcc3f99 100644
--- a/drivers/iio/accel/cros_ec_accel_legacy.c
+++ b/drivers/iio/accel/cros_ec_accel_legacy.c
@@ -1,193 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Driver for older Chrome OS EC accelerometer
*
* Copyright 2017 Google, Inc
*
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- *
* This driver uses the memory mapper cros-ec interface to communicate
- * with the Chrome OS EC about accelerometer data.
+ * with the Chrome OS EC about accelerometer data or older commands.
* Accelerometer access is presented through iio sysfs.
*/
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/common/cros_ec_sensors_core.h>
#include <linux/iio/iio.h>
#include <linux/iio/kfifo_buf.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/sysfs.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#define DRV_NAME "cros-ec-accel-legacy"
+#define CROS_EC_SENSOR_LEGACY_NUM 2
/*
* Sensor scale hard coded at 10 bits per g, computed as:
* g / (2^10 - 1) = 0.009586168; with g = 9.80665 m.s^-2
*/
#define ACCEL_LEGACY_NSCALE 9586168
-/* Indices for EC sensor values. */
-enum {
- X,
- Y,
- Z,
- MAX_AXIS,
-};
-
-/* State data for cros_ec_accel_legacy iio driver. */
-struct cros_ec_accel_legacy_state {
- struct cros_ec_device *ec;
-
- /*
- * Array holding data from a single capture. 2 bytes per channel
- * for the 3 channels plus the timestamp which is always last and
- * 8-bytes aligned.
- */
- s16 capture_data[8];
- s8 sign[MAX_AXIS];
- u8 sensor_num;
-};
-
-static int ec_cmd_read_u8(struct cros_ec_device *ec, unsigned int offset,
- u8 *dest)
+static int cros_ec_accel_legacy_read_cmd(struct iio_dev *indio_dev,
+ unsigned long scan_mask, s16 *data)
{
- return ec->cmd_readmem(ec, offset, 1, dest);
-}
-
-static int ec_cmd_read_u16(struct cros_ec_device *ec, unsigned int offset,
- u16 *dest)
-{
- __le16 tmp;
- int ret = ec->cmd_readmem(ec, offset, 2, &tmp);
-
- *dest = le16_to_cpu(tmp);
-
- return ret;
-}
-
-/**
- * read_ec_until_not_busy() - Read from EC status byte until it reads not busy.
- * @st: Pointer to state information for device.
- *
- * This function reads EC status until its busy bit gets cleared. It does not
- * wait indefinitely and returns -EIO if the EC status is still busy after a
- * few hundreds milliseconds.
- *
- * Return: 8-bit status if ok, -EIO on error
- */
-static int read_ec_until_not_busy(struct cros_ec_accel_legacy_state *st)
-{
- struct cros_ec_device *ec = st->ec;
- u8 status;
- int attempts = 0;
-
- ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
- while (status & EC_MEMMAP_ACC_STATUS_BUSY_BIT) {
- /* Give up after enough attempts, return error. */
- if (attempts++ >= 50)
- return -EIO;
-
- /* Small delay every so often. */
- if (attempts % 5 == 0)
- msleep(25);
-
- ec_cmd_read_u8(ec, EC_MEMMAP_ACC_STATUS, &status);
- }
-
- return status;
-}
-
-/**
- * read_ec_accel_data_unsafe() - Read acceleration data from EC shared memory.
- * @st: Pointer to state information for device.
- * @scan_mask: Bitmap of the sensor indices to scan.
- * @data: Location to store data.
- *
- * This is the unsafe function for reading the EC data. It does not guarantee
- * that the EC will not modify the data as it is being read in.
- */
-static void read_ec_accel_data_unsafe(struct cros_ec_accel_legacy_state *st,
- unsigned long scan_mask, s16 *data)
-{
- int i = 0;
- int num_enabled = bitmap_weight(&scan_mask, MAX_AXIS);
-
- /* Read all sensors enabled in scan_mask. Each value is 2 bytes. */
- while (num_enabled--) {
- i = find_next_bit(&scan_mask, MAX_AXIS, i);
- ec_cmd_read_u16(st->ec,
- EC_MEMMAP_ACC_DATA +
- sizeof(s16) *
- (1 + i + st->sensor_num * MAX_AXIS),
- data);
- *data *= st->sign[i];
- i++;
- data++;
- }
-}
-
-/**
- * read_ec_accel_data() - Read acceleration data from EC shared memory.
- * @st: Pointer to state information for device.
- * @scan_mask: Bitmap of the sensor indices to scan.
- * @data: Location to store data.
- *
- * This is the safe function for reading the EC data. It guarantees that
- * the data sampled was not modified by the EC while being read.
- *
- * Return: 0 if ok, -ve on error
- */
-static int read_ec_accel_data(struct cros_ec_accel_legacy_state *st,
- unsigned long scan_mask, s16 *data)
-{
- u8 samp_id = 0xff;
- u8 status = 0;
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
int ret;
- int attempts = 0;
+ unsigned int i;
+ u8 sensor_num;
/*
- * Continually read all data from EC until the status byte after
- * all reads reflects that the EC is not busy and the sample id
- * matches the sample id from before all reads. This guarantees
- * that data read in was not modified by the EC while reading.
+ * Read all sensor data through a command.
+ * Save sensor_num, it is assumed to stay.
*/
- while ((status & (EC_MEMMAP_ACC_STATUS_BUSY_BIT |
- EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK)) != samp_id) {
- /* If we have tried to read too many times, return error. */
- if (attempts++ >= 5)
- return -EIO;
+ sensor_num = st->param.info.sensor_num;
+ st->param.cmd = MOTIONSENSE_CMD_DUMP;
+ st->param.dump.max_sensor_count = CROS_EC_SENSOR_LEGACY_NUM;
+ ret = cros_ec_motion_send_host_cmd(st,
+ sizeof(st->resp->dump) + CROS_EC_SENSOR_LEGACY_NUM *
+ sizeof(struct ec_response_motion_sensor_data));
+ st->param.info.sensor_num = sensor_num;
+ if (ret != 0) {
+ dev_warn(&indio_dev->dev, "Unable to read sensor data\n");
+ return ret;
+ }
- /* Read status byte until EC is not busy. */
- ret = read_ec_until_not_busy(st);
- if (ret < 0)
- return ret;
- status = ret;
-
- /*
- * Store the current sample id so that we can compare to the
- * sample id after reading the data.
- */
- samp_id = status & EC_MEMMAP_ACC_STATUS_SAMPLE_ID_MASK;
-
- /* Read all EC data, format it, and store it into data. */
- read_ec_accel_data_unsafe(st, scan_mask, data);
-
- /* Read status byte. */
- ec_cmd_read_u8(st->ec, EC_MEMMAP_ACC_STATUS, &status);
+ for_each_set_bit(i, &scan_mask, indio_dev->masklength) {
+ *data = st->resp->dump.sensor[sensor_num].data[i] *
+ st->sign[i];
+ data++;
}
return 0;
@@ -197,28 +71,40 @@
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
- struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
s16 data = 0;
- int ret = IIO_VAL_INT;
+ int ret;
+ int idx = chan->scan_index;
+
+ mutex_lock(&st->cmd_lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
- ret = read_ec_accel_data(st, (1 << chan->scan_index), &data);
- if (ret)
- return ret;
+ ret = st->read_ec_sensors_data(indio_dev, 1 << idx, &data);
+ if (ret < 0)
+ break;
+ ret = IIO_VAL_INT;
*val = data;
- return IIO_VAL_INT;
+ break;
case IIO_CHAN_INFO_SCALE:
+ WARN_ON(st->type != MOTIONSENSE_TYPE_ACCEL);
*val = 0;
*val2 = ACCEL_LEGACY_NSCALE;
- return IIO_VAL_INT_PLUS_NANO;
+ ret = IIO_VAL_INT_PLUS_NANO;
+ break;
case IIO_CHAN_INFO_CALIBBIAS:
/* Calibration not supported. */
*val = 0;
- return IIO_VAL_INT;
+ ret = IIO_VAL_INT;
+ break;
default:
- return -EINVAL;
+ ret = cros_ec_sensors_core_read(st, chan, val, val2,
+ mask);
+ break;
}
+ mutex_unlock(&st->cmd_lock);
+
+ return ret;
}
static int cros_ec_accel_legacy_write(struct iio_dev *indio_dev,
@@ -240,86 +126,14 @@
.write_raw = &cros_ec_accel_legacy_write,
};
-/**
- * cros_ec_accel_legacy_capture() - The trigger handler function
- * @irq: The interrupt number.
- * @p: Private data - always a pointer to the poll func.
- *
- * On a trigger event occurring, if the pollfunc is attached then this
- * handler is called as a threaded interrupt (and hence may sleep). It
- * is responsible for grabbing data from the device and pushing it into
- * the associated buffer.
- *
- * Return: IRQ_HANDLED
+/*
+ * Present the channel using HTML5 standard:
+ * need to invert X and Y and invert some lid axis.
*/
-static irqreturn_t cros_ec_accel_legacy_capture(int irq, void *p)
-{
- struct iio_poll_func *pf = p;
- struct iio_dev *indio_dev = pf->indio_dev;
- struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
-
- /* Clear capture data. */
- memset(st->capture_data, 0, sizeof(st->capture_data));
-
- /*
- * Read data based on which channels are enabled in scan mask. Note
- * that on a capture we are always reading the calibrated data.
- */
- read_ec_accel_data(st, *indio_dev->active_scan_mask, st->capture_data);
-
- iio_push_to_buffers_with_timestamp(indio_dev, (void *)st->capture_data,
- iio_get_time_ns(indio_dev));
-
- /*
- * Tell the core we are done with this trigger and ready for the
- * next one.
- */
- iio_trigger_notify_done(indio_dev->trig);
-
- return IRQ_HANDLED;
-}
-
-static char *cros_ec_accel_legacy_loc_strings[] = {
- [MOTIONSENSE_LOC_BASE] = "base",
- [MOTIONSENSE_LOC_LID] = "lid",
- [MOTIONSENSE_LOC_MAX] = "unknown",
-};
-
-static ssize_t cros_ec_accel_legacy_loc(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- char *buf)
-{
- struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
-
- return sprintf(buf, "%s\n",
- cros_ec_accel_legacy_loc_strings[st->sensor_num +
- MOTIONSENSE_LOC_BASE]);
-}
-
-static ssize_t cros_ec_accel_legacy_id(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- char *buf)
-{
- struct cros_ec_accel_legacy_state *st = iio_priv(indio_dev);
-
- return sprintf(buf, "%d\n", st->sensor_num);
-}
-
-static const struct iio_chan_spec_ext_info cros_ec_accel_legacy_ext_info[] = {
- {
- .name = "id",
- .shared = IIO_SHARED_BY_ALL,
- .read = cros_ec_accel_legacy_id,
- },
- {
- .name = "location",
- .shared = IIO_SHARED_BY_ALL,
- .read = cros_ec_accel_legacy_loc,
- },
- { }
-};
+#define CROS_EC_ACCEL_ROTATE_AXIS(_axis) \
+ ((_axis) == CROS_EC_SENSOR_Z ? CROS_EC_SENSOR_Z : \
+ ((_axis) == CROS_EC_SENSOR_X ? CROS_EC_SENSOR_Y : \
+ CROS_EC_SENSOR_X))
#define CROS_EC_ACCEL_LEGACY_CHAN(_axis) \
{ \
@@ -328,81 +142,63 @@
.modified = 1, \
.info_mask_separate = \
BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_SCALE) | \
BIT(IIO_CHAN_INFO_CALIBBIAS), \
.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE), \
- .ext_info = cros_ec_accel_legacy_ext_info, \
+ .ext_info = cros_ec_sensors_ext_info, \
.scan_type = { \
.sign = 's', \
- .realbits = 16, \
- .storagebits = 16, \
+ .realbits = CROS_EC_SENSOR_BITS, \
+ .storagebits = CROS_EC_SENSOR_BITS, \
}, \
+ .scan_index = CROS_EC_ACCEL_ROTATE_AXIS(_axis), \
} \
-static struct iio_chan_spec ec_accel_channels[] = {
- CROS_EC_ACCEL_LEGACY_CHAN(X),
- CROS_EC_ACCEL_LEGACY_CHAN(Y),
- CROS_EC_ACCEL_LEGACY_CHAN(Z),
- IIO_CHAN_SOFT_TIMESTAMP(MAX_AXIS)
+static const struct iio_chan_spec cros_ec_accel_legacy_channels[] = {
+ CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_X),
+ CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Y),
+ CROS_EC_ACCEL_LEGACY_CHAN(CROS_EC_SENSOR_Z),
+ IIO_CHAN_SOFT_TIMESTAMP(CROS_EC_SENSOR_MAX_AXIS)
};
static int cros_ec_accel_legacy_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct cros_ec_dev *ec = dev_get_drvdata(dev->parent);
- struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
struct iio_dev *indio_dev;
- struct cros_ec_accel_legacy_state *state;
- int ret, i;
+ struct cros_ec_sensors_core_state *state;
+ int ret;
if (!ec || !ec->ec_dev) {
dev_warn(&pdev->dev, "No EC device found.\n");
return -EINVAL;
}
- if (!ec->ec_dev->cmd_readmem) {
- dev_warn(&pdev->dev, "EC does not support direct reads.\n");
- return -EINVAL;
- }
-
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*state));
if (!indio_dev)
return -ENOMEM;
- platform_set_drvdata(pdev, indio_dev);
- state = iio_priv(indio_dev);
- state->ec = ec->ec_dev;
- state->sensor_num = sensor_platform->sensor_num;
+ ret = cros_ec_sensors_core_init(pdev, indio_dev, true);
+ if (ret)
+ return ret;
- indio_dev->dev.parent = dev;
- indio_dev->name = pdev->name;
- indio_dev->channels = ec_accel_channels;
- /*
- * Present the channel using HTML5 standard:
- * need to invert X and Y and invert some lid axis.
- */
- for (i = X ; i < MAX_AXIS; i++) {
- switch (i) {
- case X:
- ec_accel_channels[X].scan_index = Y;
- case Y:
- ec_accel_channels[Y].scan_index = X;
- case Z:
- ec_accel_channels[Z].scan_index = Z;
- }
- if (state->sensor_num == MOTIONSENSE_LOC_LID && i != Y)
- state->sign[i] = -1;
- else
- state->sign[i] = 1;
- }
- indio_dev->num_channels = ARRAY_SIZE(ec_accel_channels);
- indio_dev->dev.parent = &pdev->dev;
indio_dev->info = &cros_ec_accel_legacy_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
+ state = iio_priv(indio_dev);
+
+ if (state->ec->cmd_readmem != NULL)
+ state->read_ec_sensors_data = cros_ec_sensors_read_lpc;
+ else
+ state->read_ec_sensors_data = cros_ec_accel_legacy_read_cmd;
+
+ indio_dev->channels = cros_ec_accel_legacy_channels;
+ indio_dev->num_channels = ARRAY_SIZE(cros_ec_accel_legacy_channels);
+ /* The lid sensor needs to be presented inverted. */
+ if (state->loc == MOTIONSENSE_LOC_LID) {
+ state->sign[CROS_EC_SENSOR_X] = -1;
+ state->sign[CROS_EC_SENSOR_Z] = -1;
+ }
ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
- cros_ec_accel_legacy_capture,
- NULL);
+ cros_ec_sensors_capture, NULL);
if (ret)
return ret;
@@ -419,5 +215,5 @@
MODULE_DESCRIPTION("ChromeOS EC legacy accelerometer driver");
MODULE_AUTHOR("Gwendal Grignou <gwendal@chromium.org>");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/drivers/iio/accel/da280.c b/drivers/iio/accel/da280.c
index d4b5552..227bea2 100644
--- a/drivers/iio/accel/da280.c
+++ b/drivers/iio/accel/da280.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* IIO driver for the MiraMEMS DA280 3-axis accelerometer and
* IIO driver for the MiraMEMS DA226 2-axis accelerometer
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/module.h>
diff --git a/drivers/iio/accel/da311.c b/drivers/iio/accel/da311.c
index aa64bca..c209792 100644
--- a/drivers/iio/accel/da311.c
+++ b/drivers/iio/accel/da311.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* IIO driver for the MiraMEMS DA311 3-axis accelerometer
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
* Copyright (c) 2011-2013 MiraMEMS Sensing Technology Co., Ltd.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/module.h>
diff --git a/drivers/iio/accel/dmard06.c b/drivers/iio/accel/dmard06.c
index d87e2c7..2bf210f 100644
--- a/drivers/iio/accel/dmard06.c
+++ b/drivers/iio/accel/dmard06.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO driver for Domintech DMARD06 accelerometer
*
* Copyright (C) 2016 Aleksei Mamlin <mamlinav@gmail.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/module.h>
diff --git a/drivers/iio/accel/dmard09.c b/drivers/iio/accel/dmard09.c
index 16a7e74..2d666cd 100644
--- a/drivers/iio/accel/dmard09.c
+++ b/drivers/iio/accel/dmard09.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO driver for the 3-axis accelerometer Domintech DMARD09.
*
* Copyright (c) 2016, Jelle van der Waa <jelle@vdwaa.nl>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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 <asm/unaligned.h>
diff --git a/drivers/iio/accel/dmard10.c b/drivers/iio/accel/dmard10.c
index 9518ea0..71c852b 100644
--- a/drivers/iio/accel/dmard10.c
+++ b/drivers/iio/accel/dmard10.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* IIO driver for the 3-axis accelerometer Domintech ARD10.
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.com>
* Copyright (c) 2012 Domintech Technology Co., Ltd
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
*/
#include <linux/module.h>
diff --git a/drivers/iio/accel/hid-sensor-accel-3d.c b/drivers/iio/accel/hid-sensor-accel-3d.c
index 38ff374..0d9e2de 100644
--- a/drivers/iio/accel/hid-sensor-accel-3d.c
+++ b/drivers/iio/accel/hid-sensor-accel-3d.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <linux/device.h>
#include <linux/platform_device.h>
diff --git a/drivers/iio/accel/kxcjk-1013.c b/drivers/iio/accel/kxcjk-1013.c
index af53a10..fee535d 100644
--- a/drivers/iio/accel/kxcjk-1013.c
+++ b/drivers/iio/accel/kxcjk-1013.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* KXCJK-1013 3-axis accelerometer driver
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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>
@@ -451,7 +443,7 @@
}
if (ret < 0) {
dev_err(&data->client->dev,
- "Failed: kxcjk1013_set_power_state for %d\n", on);
+ "Failed: %s for %d\n", __func__, on);
if (on)
pm_runtime_put_noidle(&data->client->dev);
return ret;
@@ -1437,6 +1429,8 @@
mutex_lock(&data->mutex);
ret = kxcjk1013_set_mode(data, OPERATION);
+ if (ret == 0)
+ ret = kxcjk1013_set_range(data, data->range);
mutex_unlock(&data->mutex);
return ret;
@@ -1489,8 +1483,13 @@
{"KXCJ1013", KXCJK1013},
{"KXCJ1008", KXCJ91008},
{"KXCJ9000", KXCJ91008},
+ {"KIOX0008", KXCJ91008},
+ {"KIOX0009", KXTJ21009},
{"KIOX000A", KXCJ91008},
+ {"KIOX010A", KXCJ91008}, /* KXCJ91008 in the display of a yoga 2-in-1 */
+ {"KIOX020A", KXCJ91008}, /* KXCJ91008 in the base of a yoga 2-in-1 */
{"KXTJ1009", KXTJ21009},
+ {"KXJ2109", KXTJ21009},
{"SMO8500", KXCJ91008},
{ },
};
@@ -1507,10 +1506,20 @@
MODULE_DEVICE_TABLE(i2c, kxcjk1013_id);
+static const struct of_device_id kxcjk1013_of_match[] = {
+ { .compatible = "kionix,kxcjk1013", },
+ { .compatible = "kionix,kxcj91008", },
+ { .compatible = "kionix,kxtj21009", },
+ { .compatible = "kionix,kxtf9", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, kxcjk1013_of_match);
+
static struct i2c_driver kxcjk1013_driver = {
.driver = {
.name = KXCJK1013_DRV_NAME,
.acpi_match_table = ACPI_PTR(kx_acpi_match),
+ .of_match_table = kxcjk1013_of_match,
.pm = &kxcjk1013_pm_ops,
},
.probe = kxcjk1013_probe,
diff --git a/drivers/iio/accel/kxsd9-spi.c b/drivers/iio/accel/kxsd9-spi.c
index b7d0078..7971ec1 100644
--- a/drivers/iio/accel/kxsd9-spi.c
+++ b/drivers/iio/accel/kxsd9-spi.c
@@ -1,5 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/device.h>
#include <linux/kernel.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/module.h>
#include <linux/slab.h>
@@ -40,10 +43,17 @@
};
MODULE_DEVICE_TABLE(spi, kxsd9_spi_id);
+static const struct of_device_id kxsd9_of_match[] = {
+ { .compatible = "kionix,kxsd9" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, kxsd9_of_match);
+
static struct spi_driver kxsd9_spi_driver = {
.driver = {
.name = "kxsd9",
.pm = &kxsd9_dev_pm_ops,
+ .of_match_table = kxsd9_of_match,
},
.probe = kxsd9_spi_probe,
.remove = kxsd9_spi_remove,
diff --git a/drivers/iio/accel/kxsd9.c b/drivers/iio/accel/kxsd9.c
index 0c0df4f..0b876b2 100644
--- a/drivers/iio/accel/kxsd9.c
+++ b/drivers/iio/accel/kxsd9.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* kxsd9.c simple support for the Kionix KXSD9 3D
* accelerometer.
*
* Copyright (c) 2008-2009 Jonathan Cameron <jic23@kernel.org>
*
- * 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.
- *
* The i2c interface is very similar, so shouldn't be a problem once
* I have a suitable wire made up.
*
@@ -420,9 +417,7 @@
indio_dev->available_scan_masks = kxsd9_scan_masks;
/* Read the mounting matrix, if present */
- ret = of_iio_read_mount_matrix(dev,
- "mount-matrix",
- &st->orientation);
+ ret = iio_read_mount_matrix(dev, "mount-matrix", &st->orientation);
if (ret)
return ret;
diff --git a/drivers/iio/accel/mc3230.c b/drivers/iio/accel/mc3230.c
index 8b11604..02b3d25 100644
--- a/drivers/iio/accel/mc3230.c
+++ b/drivers/iio/accel/mc3230.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/**
* mCube MC3230 3-Axis Accelerometer
*
* Copyright (c) 2016 Hans de Goede <hdegoede@redhat.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.
- *
* IIO driver for mCube MC3230; 7-bit I2C address: 0x4c.
*/
diff --git a/drivers/iio/accel/mma7455.h b/drivers/iio/accel/mma7455.h
index 2b1152c..4e3fa98 100644
--- a/drivers/iio/accel/mma7455.h
+++ b/drivers/iio/accel/mma7455.h
@@ -1,10 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* IIO accel driver for Freescale MMA7455L 3-axis 10-bit accelerometer
* Copyright 2015 Joachim Eastwood <manabian@gmail.com>
- *
- * 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.
*/
#ifndef __MMA7455_H
diff --git a/drivers/iio/accel/mma7455_core.c b/drivers/iio/accel/mma7455_core.c
index da0ceaa..8b5a6af 100644
--- a/drivers/iio/accel/mma7455_core.c
+++ b/drivers/iio/accel/mma7455_core.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO accel core driver for Freescale MMA7455L 3-axis 10-bit accelerometer
* Copyright 2015 Joachim Eastwood <manabian@gmail.com>
*
- * 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.
- *
* UNSUPPORTED hardware features:
* - 8-bit mode with different scales
* - INT1/INT2 interrupts
diff --git a/drivers/iio/accel/mma7455_i2c.c b/drivers/iio/accel/mma7455_i2c.c
index 73bf81a..cddeaa9 100644
--- a/drivers/iio/accel/mma7455_i2c.c
+++ b/drivers/iio/accel/mma7455_i2c.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO accel I2C driver for Freescale MMA7455L 3-axis 10-bit accelerometer
* Copyright 2015 Joachim Eastwood <manabian@gmail.com>
- *
- * 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/i2c.h>
diff --git a/drivers/iio/accel/mma7455_spi.c b/drivers/iio/accel/mma7455_spi.c
index 79df8f2..eb82cdf 100644
--- a/drivers/iio/accel/mma7455_spi.c
+++ b/drivers/iio/accel/mma7455_spi.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO accel SPI driver for Freescale MMA7455L 3-axis 10-bit accelerometer
* Copyright 2015 Joachim Eastwood <manabian@gmail.com>
- *
- * 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/module.h>
diff --git a/drivers/iio/accel/mma7660.c b/drivers/iio/accel/mma7660.c
index f1a1372..7faf6d8 100644
--- a/drivers/iio/accel/mma7660.c
+++ b/drivers/iio/accel/mma7660.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Freescale MMA7660FC 3-Axis Accelerometer
*
* Copyright (c) 2016, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for Freescale MMA7660FC; 7-bit I2C address: 0x4c.
*/
diff --git a/drivers/iio/accel/mma8452.c b/drivers/iio/accel/mma8452.c
index 421a0a8..00e100f 100644
--- a/drivers/iio/accel/mma8452.c
+++ b/drivers/iio/accel/mma8452.c
@@ -31,6 +31,7 @@
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pm_runtime.h>
+#include <linux/regulator/consumer.h>
#define MMA8452_STATUS 0x00
#define MMA8452_STATUS_DRDY (BIT(2) | BIT(1) | BIT(0))
@@ -107,6 +108,8 @@
u8 data_cfg;
const struct mma_chip_info *chip_info;
int sleep_val;
+ struct regulator *vdd_reg;
+ struct regulator *vddio_reg;
};
/**
@@ -1534,9 +1537,39 @@
mutex_init(&data->lock);
data->chip_info = match->data;
+ data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(data->vdd_reg)) {
+ if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_err(&client->dev, "failed to get VDD regulator!\n");
+ return PTR_ERR(data->vdd_reg);
+ }
+
+ data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
+ if (IS_ERR(data->vddio_reg)) {
+ if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_err(&client->dev, "failed to get VDDIO regulator!\n");
+ return PTR_ERR(data->vddio_reg);
+ }
+
+ ret = regulator_enable(data->vdd_reg);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable VDD regulator!\n");
+ return ret;
+ }
+
+ ret = regulator_enable(data->vddio_reg);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
+ goto disable_regulator_vdd;
+ }
+
ret = i2c_smbus_read_byte_data(client, MMA8452_WHO_AM_I);
if (ret < 0)
- return ret;
+ goto disable_regulators;
switch (ret) {
case MMA8451_DEVICE_ID:
@@ -1547,9 +1580,10 @@
case FXLS8471_DEVICE_ID:
if (ret == data->chip_info->chip_id)
break;
- /* else: fall through */
+ /* fall through */
default:
- return -ENODEV;
+ ret = -ENODEV;
+ goto disable_regulators;
}
dev_info(&client->dev, "registering %s accelerometer; ID 0x%x\n",
@@ -1566,13 +1600,13 @@
ret = mma8452_reset(client);
if (ret < 0)
- return ret;
+ goto disable_regulators;
data->data_cfg = MMA8452_DATA_CFG_FS_2G;
ret = i2c_smbus_write_byte_data(client, MMA8452_DATA_CFG,
data->data_cfg);
if (ret < 0)
- return ret;
+ goto disable_regulators;
/*
* By default set transient threshold to max to avoid events if
@@ -1581,7 +1615,7 @@
ret = i2c_smbus_write_byte_data(client, MMA8452_TRANSIENT_THS,
MMA8452_TRANSIENT_THS_MASK);
if (ret < 0)
- return ret;
+ goto disable_regulators;
if (client->irq) {
int irq2;
@@ -1595,7 +1629,7 @@
MMA8452_CTRL_REG5,
data->chip_info->all_events);
if (ret < 0)
- return ret;
+ goto disable_regulators;
dev_dbg(&client->dev, "using interrupt line INT1\n");
}
@@ -1604,11 +1638,11 @@
MMA8452_CTRL_REG4,
data->chip_info->enabled_events);
if (ret < 0)
- return ret;
+ goto disable_regulators;
ret = mma8452_trigger_setup(indio_dev);
if (ret < 0)
- return ret;
+ goto disable_regulators;
}
data->ctrl_reg1 = MMA8452_CTRL_ACTIVE |
@@ -1661,12 +1695,19 @@
trigger_cleanup:
mma8452_trigger_cleanup(indio_dev);
+disable_regulators:
+ regulator_disable(data->vddio_reg);
+
+disable_regulator_vdd:
+ regulator_disable(data->vdd_reg);
+
return ret;
}
static int mma8452_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mma8452_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
@@ -1678,6 +1719,9 @@
mma8452_trigger_cleanup(indio_dev);
mma8452_standby(iio_priv(indio_dev));
+ regulator_disable(data->vddio_reg);
+ regulator_disable(data->vdd_reg);
+
return 0;
}
@@ -1696,6 +1740,18 @@
return -EAGAIN;
}
+ ret = regulator_disable(data->vddio_reg);
+ if (ret) {
+ dev_err(dev, "failed to disable VDDIO regulator\n");
+ return ret;
+ }
+
+ ret = regulator_disable(data->vdd_reg);
+ if (ret) {
+ dev_err(dev, "failed to disable VDD regulator\n");
+ return ret;
+ }
+
return 0;
}
@@ -1705,9 +1761,22 @@
struct mma8452_data *data = iio_priv(indio_dev);
int ret, sleep_val;
+ ret = regulator_enable(data->vdd_reg);
+ if (ret) {
+ dev_err(dev, "failed to enable VDD regulator\n");
+ return ret;
+ }
+
+ ret = regulator_enable(data->vddio_reg);
+ if (ret) {
+ dev_err(dev, "failed to enable VDDIO regulator\n");
+ regulator_disable(data->vdd_reg);
+ return ret;
+ }
+
ret = mma8452_active(data);
if (ret < 0)
- return ret;
+ goto runtime_resume_failed;
ret = mma8452_get_odr_index(data);
sleep_val = 1000 / mma8452_samp_freq[ret][0];
@@ -1717,25 +1786,17 @@
msleep_interruptible(sleep_val);
return 0;
-}
-#endif
-#ifdef CONFIG_PM_SLEEP
-static int mma8452_suspend(struct device *dev)
-{
- return mma8452_standby(iio_priv(i2c_get_clientdata(
- to_i2c_client(dev))));
-}
+runtime_resume_failed:
+ regulator_disable(data->vddio_reg);
+ regulator_disable(data->vdd_reg);
-static int mma8452_resume(struct device *dev)
-{
- return mma8452_active(iio_priv(i2c_get_clientdata(
- to_i2c_client(dev))));
+ return ret;
}
#endif
static const struct dev_pm_ops mma8452_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(mma8452_suspend, mma8452_resume)
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, pm_runtime_force_resume)
SET_RUNTIME_PM_OPS(mma8452_runtime_suspend,
mma8452_runtime_resume, NULL)
};
diff --git a/drivers/iio/accel/mma9551.c b/drivers/iio/accel/mma9551.c
index da7c215..99e4a21 100644
--- a/drivers/iio/accel/mma9551.c
+++ b/drivers/iio/accel/mma9551.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Freescale MMA9551L Intelligent Motion-Sensing Platform driver
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/accel/mma9551_core.c b/drivers/iio/accel/mma9551_core.c
index c34c5ce..666e7a0 100644
--- a/drivers/iio/accel/mma9551_core.c
+++ b/drivers/iio/accel/mma9551_core.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Common code for Freescale MMA955x Intelligent Sensor Platform drivers
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/accel/mma9551_core.h b/drivers/iio/accel/mma9551_core.h
index 5e88e64..543454a 100644
--- a/drivers/iio/accel/mma9551_core.h
+++ b/drivers/iio/accel/mma9551_core.h
@@ -1,15 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Common code for Freescale MMA955x Intelligent Sensor Platform drivers
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
*/
#ifndef _MMA9551_CORE_H_
diff --git a/drivers/iio/accel/mma9553.c b/drivers/iio/accel/mma9553.c
index b52a3f1..312070d 100644
--- a/drivers/iio/accel/mma9553.c
+++ b/drivers/iio/accel/mma9553.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Freescale MMA9553L Intelligent Pedometer driver
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/accel/mxc4005.c b/drivers/iio/accel/mxc4005.c
index 58099e4..3d5bea6 100644
--- a/drivers/iio/accel/mxc4005.c
+++ b/drivers/iio/accel/mxc4005.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 3-axis accelerometer driver for MXC4005XC Memsic sensor
*
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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>
@@ -432,7 +424,7 @@
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &mxc4005_info;
- ret = iio_triggered_buffer_setup(indio_dev,
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
iio_pollfunc_store_time,
mxc4005_trigger_handler,
NULL);
@@ -460,7 +452,7 @@
if (ret) {
dev_err(&client->dev,
"failed to init threaded irq\n");
- goto err_buffer_cleanup;
+ return ret;
}
data->dready_trig->dev.parent = &client->dev;
@@ -468,43 +460,16 @@
iio_trigger_set_drvdata(data->dready_trig, indio_dev);
indio_dev->trig = data->dready_trig;
iio_trigger_get(indio_dev->trig);
- ret = iio_trigger_register(data->dready_trig);
+ ret = devm_iio_trigger_register(&client->dev,
+ data->dready_trig);
if (ret) {
dev_err(&client->dev,
"failed to register trigger\n");
- goto err_trigger_unregister;
+ return ret;
}
}
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&client->dev,
- "unable to register iio device %d\n", ret);
- goto err_buffer_cleanup;
- }
-
- return 0;
-
-err_trigger_unregister:
- iio_trigger_unregister(data->dready_trig);
-err_buffer_cleanup:
- iio_triggered_buffer_cleanup(indio_dev);
-
- return ret;
-}
-
-static int mxc4005_remove(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
- struct mxc4005_data *data = iio_priv(indio_dev);
-
- iio_device_unregister(indio_dev);
-
- iio_triggered_buffer_cleanup(indio_dev);
- if (data->dready_trig)
- iio_trigger_unregister(data->dready_trig);
-
- return 0;
+ return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct acpi_device_id mxc4005_acpi_match[] = {
@@ -525,7 +490,6 @@
.acpi_match_table = ACPI_PTR(mxc4005_acpi_match),
},
.probe = mxc4005_probe,
- .remove = mxc4005_remove,
.id_table = mxc4005_id,
};
diff --git a/drivers/iio/accel/mxc6255.c b/drivers/iio/accel/mxc6255.c
index ddd50d1..f532f86 100644
--- a/drivers/iio/accel/mxc6255.c
+++ b/drivers/iio/accel/mxc6255.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MXC6255 - MEMSIC orientation sensing accelerometer
*
* Copyright (c) 2015, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for MXC6255 (7-bit I2C slave address 0x15).
*/
diff --git a/drivers/iio/accel/sca3000.c b/drivers/iio/accel/sca3000.c
index 4964561..66d768d 100644
--- a/drivers/iio/accel/sca3000.c
+++ b/drivers/iio/accel/sca3000.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* sca3000_core.c -- support VTI sca3000 series accelerometers via SPI
*
- * 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.
- *
* Copyright (c) 2009 Jonathan Cameron <jic23@kernel.org>
*
* See industrialio/accels/sca3000.h for comments.
@@ -114,7 +111,7 @@
/* Currently unsupported */
#define SCA3000_MD_CTRL_AND_Y BIT(3)
#define SCA3000_MD_CTRL_AND_X BIT(4)
-#define SAC3000_MD_CTRL_AND_Z BIT(5)
+#define SCA3000_MD_CTRL_AND_Z BIT(5)
/*
* Some control registers of complex access methods requiring this register to
@@ -872,8 +869,9 @@
enum iio_event_info info,
int *val, int *val2)
{
- int ret, i;
struct sca3000_state *st = iio_priv(indio_dev);
+ long ret;
+ int i;
switch (info) {
case IIO_EV_INFO_VALUE:
@@ -885,11 +883,11 @@
return ret;
*val = 0;
if (chan->channel2 == IIO_MOD_Y)
- for_each_set_bit(i, (unsigned long *)&ret,
+ for_each_set_bit(i, &ret,
ARRAY_SIZE(st->info->mot_det_mult_y))
*val += st->info->mot_det_mult_y[i];
else
- for_each_set_bit(i, (unsigned long *)&ret,
+ for_each_set_bit(i, &ret,
ARRAY_SIZE(st->info->mot_det_mult_xz))
*val += st->info->mot_det_mult_xz[i];
diff --git a/drivers/iio/accel/ssp_accel_sensor.c b/drivers/iio/accel/ssp_accel_sensor.c
index dd6ece8..c32647a 100644
--- a/drivers/iio/accel/ssp_accel_sensor.c
+++ b/drivers/iio/accel/ssp_accel_sensor.c
@@ -1,16 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
- *
- * 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/iio/common/ssp_sensors.h>
diff --git a/drivers/iio/accel/st_accel.h b/drivers/iio/accel/st_accel.h
index 2f931e4..af09943 100644
--- a/drivers/iio/accel/st_accel.h
+++ b/drivers/iio/accel/st_accel.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics accelerometers driver
*
@@ -5,7 +6,6 @@
*
* Denis Ciocca <denis.ciocca@st.com>
* v. 1.0.0
- * Licensed under the GPL-2.
*/
#ifndef ST_ACCEL_H
@@ -34,6 +34,7 @@
LIS3LV02DL,
LIS2DW12,
LIS3DHH,
+ LIS2DE12,
ST_ACCEL_MAX,
};
@@ -56,6 +57,8 @@
#define LNG2DM_ACCEL_DEV_NAME "lng2dm"
#define LIS2DW12_ACCEL_DEV_NAME "lis2dw12"
#define LIS3DHH_ACCEL_DEV_NAME "lis3dhh"
+#define LIS3DE_ACCEL_DEV_NAME "lis3de"
+#define LIS2DE12_ACCEL_DEV_NAME "lis2de12"
/**
* struct st_sensors_platform_data - default accel platform data
@@ -65,6 +68,7 @@
.drdy_int_pin = 1,
};
+const struct st_sensor_settings *st_accel_get_settings(const char *name);
int st_accel_common_probe(struct iio_dev *indio_dev);
void st_accel_common_remove(struct iio_dev *indio_dev);
diff --git a/drivers/iio/accel/st_accel_buffer.c b/drivers/iio/accel/st_accel_buffer.c
index 7fddc13..9f2b404 100644
--- a/drivers/iio/accel/st_accel_buffer.c
+++ b/drivers/iio/accel/st_accel_buffer.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics accelerometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -30,61 +29,51 @@
return st_sensors_set_dataready_irq(indio_dev, state);
}
-static int st_accel_buffer_preenable(struct iio_dev *indio_dev)
-{
- return st_sensors_set_enable(indio_dev, true);
-}
-
static int st_accel_buffer_postenable(struct iio_dev *indio_dev)
{
int err;
- struct st_sensor_data *adata = iio_priv(indio_dev);
-
- adata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
- if (adata->buffer_data == NULL) {
- err = -ENOMEM;
- goto allocate_memory_error;
- }
-
- err = st_sensors_set_axis_enable(indio_dev,
- (u8)indio_dev->active_scan_mask[0]);
- if (err < 0)
- goto st_accel_buffer_postenable_error;
err = iio_triggered_buffer_postenable(indio_dev);
if (err < 0)
- goto st_accel_buffer_postenable_error;
+ return err;
- return err;
+ err = st_sensors_set_axis_enable(indio_dev,
+ (u8)indio_dev->active_scan_mask[0]);
+ if (err < 0)
+ goto st_accel_buffer_predisable;
-st_accel_buffer_postenable_error:
- kfree(adata->buffer_data);
-allocate_memory_error:
+ err = st_sensors_set_enable(indio_dev, true);
+ if (err < 0)
+ goto st_accel_buffer_enable_all_axis;
+
+ return 0;
+
+st_accel_buffer_enable_all_axis:
+ st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
+st_accel_buffer_predisable:
+ iio_triggered_buffer_predisable(indio_dev);
return err;
}
static int st_accel_buffer_predisable(struct iio_dev *indio_dev)
{
- int err;
- struct st_sensor_data *adata = iio_priv(indio_dev);
-
- err = iio_triggered_buffer_predisable(indio_dev);
- if (err < 0)
- goto st_accel_buffer_predisable_error;
-
- err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
- if (err < 0)
- goto st_accel_buffer_predisable_error;
+ int err, err2;
err = st_sensors_set_enable(indio_dev, false);
+ if (err < 0)
+ goto st_accel_buffer_predisable;
-st_accel_buffer_predisable_error:
- kfree(adata->buffer_data);
+ err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
+
+st_accel_buffer_predisable:
+ err2 = iio_triggered_buffer_predisable(indio_dev);
+ if (!err)
+ err = err2;
+
return err;
}
static const struct iio_buffer_setup_ops st_accel_buffer_setup_ops = {
- .preenable = &st_accel_buffer_preenable,
.postenable = &st_accel_buffer_postenable,
.predisable = &st_accel_buffer_predisable,
};
diff --git a/drivers/iio/accel/st_accel_core.c b/drivers/iio/accel/st_accel_core.c
index 3e6fd5a..2e37f8a 100644
--- a/drivers/iio/accel/st_accel_core.c
+++ b/drivers/iio/accel/st_accel_core.c
@@ -1,19 +1,18 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics accelerometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/acpi.h>
#include <linux/errno.h>
#include <linux/types.h>
-#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
@@ -103,6 +102,7 @@
[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 = {
@@ -829,6 +829,82 @@
.multi_read_bit = false,
.bootime = 2,
},
+ {
+ .wai = 0x33,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LIS2DE12_ACCEL_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_accel_8bit_channels,
+ .odr = {
+ .addr = 0x20,
+ .mask = 0xf0,
+ .odr_avl = {
+ { .hz = 1, .value = 0x01, },
+ { .hz = 10, .value = 0x02, },
+ { .hz = 25, .value = 0x03, },
+ { .hz = 50, .value = 0x04, },
+ { .hz = 100, .value = 0x05, },
+ { .hz = 200, .value = 0x06, },
+ { .hz = 400, .value = 0x07, },
+ { .hz = 1620, .value = 0x08, },
+ { .hz = 5376, .value = 0x09, },
+ },
+ },
+ .pw = {
+ .addr = 0x20,
+ .mask = 0xf0,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .enable_axis = {
+ .addr = ST_SENSORS_DEFAULT_AXIS_ADDR,
+ .mask = ST_SENSORS_DEFAULT_AXIS_MASK,
+ },
+ .fs = {
+ .addr = 0x23,
+ .mask = 0x30,
+ .fs_avl = {
+ [0] = {
+ .num = ST_ACCEL_FS_AVL_2G,
+ .value = 0x00,
+ .gain = IIO_G_TO_M_S_2(15600),
+ },
+ [1] = {
+ .num = ST_ACCEL_FS_AVL_4G,
+ .value = 0x01,
+ .gain = IIO_G_TO_M_S_2(31200),
+ },
+ [2] = {
+ .num = ST_ACCEL_FS_AVL_8G,
+ .value = 0x02,
+ .gain = IIO_G_TO_M_S_2(62500),
+ },
+ [3] = {
+ .num = ST_ACCEL_FS_AVL_16G,
+ .value = 0x03,
+ .gain = IIO_G_TO_M_S_2(187500),
+ },
+ },
+ },
+ .drdy_irq = {
+ .int1 = {
+ .addr = 0x22,
+ .mask = 0x10,
+ },
+ .addr_ihl = 0x25,
+ .mask_ihl = 0x02,
+ .stat_drdy = {
+ .addr = ST_SENSORS_DEFAULT_STAT_ADDR,
+ .mask = 0x07,
+ },
+ },
+ .sim = {
+ .addr = 0x23,
+ .value = BIT(0),
+ },
+ .multi_read_bit = true,
+ .bootime = 2,
+ },
};
static int st_accel_read_raw(struct iio_dev *indio_dev,
@@ -917,33 +993,214 @@
#define ST_ACCEL_TRIGGER_OPS NULL
#endif
+static const struct iio_mount_matrix *
+get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct st_sensor_data *adata = iio_priv(indio_dev);
+
+ return adata->mount_matrix;
+}
+
+static const struct iio_chan_spec_ext_info mount_matrix_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_ALL, get_mount_matrix),
+ { },
+};
+
+/* Read ST-specific _ONT orientation data from ACPI and generate an
+ * appropriate mount matrix.
+ */
+static int apply_acpi_orientation(struct iio_dev *indio_dev,
+ struct iio_chan_spec *channels)
+{
+#ifdef CONFIG_ACPI
+ struct st_sensor_data *adata = iio_priv(indio_dev);
+ struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
+ struct acpi_device *adev;
+ union acpi_object *ont;
+ union acpi_object *elements;
+ acpi_status status;
+ int ret = -EINVAL;
+ unsigned int val;
+ int i, j;
+ int final_ont[3][3] = { { 0 }, };
+
+ /* For some reason, ST's _ONT translation does not apply directly
+ * to the data read from the sensor. Another translation must be
+ * performed first, as described by the matrix below. Perhaps
+ * ST required this specific translation for the first product
+ * where the device was mounted?
+ */
+ const int default_ont[3][3] = {
+ { 0, 1, 0 },
+ { -1, 0, 0 },
+ { 0, 0, -1 },
+ };
+
+
+ adev = ACPI_COMPANION(adata->dev);
+ if (!adev)
+ return 0;
+
+ /* Read _ONT data, which should be a package of 6 integers. */
+ status = acpi_evaluate_object(adev->handle, "_ONT", NULL, &buffer);
+ if (status == AE_NOT_FOUND) {
+ return 0;
+ } else if (ACPI_FAILURE(status)) {
+ dev_warn(&indio_dev->dev, "failed to execute _ONT: %d\n",
+ status);
+ return status;
+ }
+
+ ont = buffer.pointer;
+ if (ont->type != ACPI_TYPE_PACKAGE || ont->package.count != 6)
+ goto out;
+
+ /* The first 3 integers provide axis order information.
+ * e.g. 0 1 2 would indicate normal X,Y,Z ordering.
+ * e.g. 1 0 2 indicates that data arrives in order Y,X,Z.
+ */
+ elements = ont->package.elements;
+ for (i = 0; i < 3; i++) {
+ if (elements[i].type != ACPI_TYPE_INTEGER)
+ goto out;
+
+ val = elements[i].integer.value;
+ if (val > 2)
+ goto out;
+
+ /* Avoiding full matrix multiplication, we simply reorder the
+ * columns in the default_ont matrix according to the
+ * ordering provided by _ONT.
+ */
+ final_ont[0][i] = default_ont[0][val];
+ final_ont[1][i] = default_ont[1][val];
+ final_ont[2][i] = default_ont[2][val];
+ }
+
+ /* The final 3 integers provide sign flip information.
+ * 0 means no change, 1 means flip.
+ * e.g. 0 0 1 means that Z data should be sign-flipped.
+ * This is applied after the axis reordering from above.
+ */
+ elements += 3;
+ for (i = 0; i < 3; i++) {
+ if (elements[i].type != ACPI_TYPE_INTEGER)
+ goto out;
+
+ val = elements[i].integer.value;
+ if (val != 0 && val != 1)
+ goto out;
+ if (!val)
+ continue;
+
+ /* Flip the values in the indicated column */
+ final_ont[0][i] *= -1;
+ final_ont[1][i] *= -1;
+ final_ont[2][i] *= -1;
+ }
+
+ /* Convert our integer matrix to a string-based iio_mount_matrix */
+ adata->mount_matrix = devm_kmalloc(&indio_dev->dev,
+ sizeof(*adata->mount_matrix),
+ GFP_KERNEL);
+ if (!adata->mount_matrix) {
+ ret = -ENOMEM;
+ goto out;
+ }
+
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ int matrix_val = final_ont[i][j];
+ char *str_value;
+
+ switch (matrix_val) {
+ case -1:
+ str_value = "-1";
+ break;
+ case 0:
+ str_value = "0";
+ break;
+ case 1:
+ str_value = "1";
+ break;
+ default:
+ goto out;
+ }
+ adata->mount_matrix->rotation[i * 3 + j] = str_value;
+ }
+ }
+
+ /* Expose the mount matrix via ext_info */
+ for (i = 0; i < indio_dev->num_channels; i++)
+ channels[i].ext_info = mount_matrix_ext_info;
+
+ ret = 0;
+ dev_info(&indio_dev->dev, "computed mount matrix from ACPI\n");
+
+out:
+ kfree(buffer.pointer);
+ return ret;
+#else /* !CONFIG_ACPI */
+ return 0;
+#endif
+}
+
+/*
+ * st_accel_get_settings() - get sensor settings from device name
+ * @name: device name buffer reference.
+ *
+ * Return: valid reference on success, NULL otherwise.
+ */
+const struct st_sensor_settings *st_accel_get_settings(const char *name)
+{
+ int index = st_sensors_get_settings_index(name,
+ st_accel_sensors_settings,
+ ARRAY_SIZE(st_accel_sensors_settings));
+ if (index < 0)
+ return NULL;
+
+ return &st_accel_sensors_settings[index];
+}
+EXPORT_SYMBOL(st_accel_get_settings);
+
int st_accel_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *adata = iio_priv(indio_dev);
struct st_sensors_platform_data *pdata =
(struct st_sensors_platform_data *)adata->dev->platform_data;
- int irq = adata->get_irq_data_ready(indio_dev);
+ struct iio_chan_spec *channels;
+ size_t channels_size;
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &accel_info;
- mutex_init(&adata->tb.buf_lock);
err = st_sensors_power_enable(indio_dev);
if (err)
return err;
- err = st_sensors_check_device_support(indio_dev,
- ARRAY_SIZE(st_accel_sensors_settings),
- st_accel_sensors_settings);
+ err = st_sensors_verify_id(indio_dev);
if (err < 0)
goto st_accel_power_off;
adata->num_data_channels = ST_ACCEL_NUMBER_DATA_CHANNELS;
- adata->multiread_bit = adata->sensor_settings->multi_read_bit;
- indio_dev->channels = adata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
+ channels_size = indio_dev->num_channels * sizeof(struct iio_chan_spec);
+ channels = devm_kmemdup(&indio_dev->dev,
+ adata->sensor_settings->ch,
+ channels_size, GFP_KERNEL);
+ if (!channels) {
+ err = -ENOMEM;
+ goto st_accel_power_off;
+ }
+
+ if (apply_acpi_orientation(indio_dev, channels))
+ dev_warn(&indio_dev->dev,
+ "failed to apply ACPI orientation data: %d\n", err);
+
+ indio_dev->channels = channels;
adata->current_fullscale = (struct st_sensor_fullscale_avl *)
&adata->sensor_settings->fs.fs_avl[0];
adata->odr = adata->sensor_settings->odr.odr_avl[0].hz;
@@ -959,7 +1216,7 @@
if (err < 0)
goto st_accel_power_off;
- if (irq > 0) {
+ if (adata->irq > 0) {
err = st_sensors_allocate_trigger(indio_dev,
ST_ACCEL_TRIGGER_OPS);
if (err < 0)
@@ -976,7 +1233,7 @@
return 0;
st_accel_device_register_error:
- if (irq > 0)
+ if (adata->irq > 0)
st_sensors_deallocate_trigger(indio_dev);
st_accel_probe_trigger_error:
st_accel_deallocate_ring(indio_dev);
@@ -994,7 +1251,7 @@
st_sensors_power_disable(indio_dev);
iio_device_unregister(indio_dev);
- if (adata->get_irq_data_ready(indio_dev) > 0)
+ if (adata->irq > 0)
st_sensors_deallocate_trigger(indio_dev);
st_accel_deallocate_ring(indio_dev);
diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c
index 2ca5d1f..50fa0fc 100644
--- a/drivers/iio/accel/st_accel_i2c.c
+++ b/drivers/iio/accel/st_accel_i2c.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics accelerometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -98,6 +97,14 @@
.compatible = "st,lis2dw12",
.data = LIS2DW12_ACCEL_DEV_NAME,
},
+ {
+ .compatible = "st,lis3de",
+ .data = LIS3DE_ACCEL_DEV_NAME,
+ },
+ {
+ .compatible = "st,lis2de12",
+ .data = LIS2DE12_ACCEL_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -135,28 +142,41 @@
{ LIS331DL_ACCEL_DEV_NAME },
{ LIS3LV02DL_ACCEL_DEV_NAME },
{ LIS2DW12_ACCEL_DEV_NAME },
+ { LIS3DE_ACCEL_DEV_NAME },
+ { LIS2DE12_ACCEL_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(i2c, st_accel_id_table);
static int st_accel_i2c_probe(struct i2c_client *client)
{
- struct iio_dev *indio_dev;
+ const struct st_sensor_settings *settings;
struct st_sensor_data *adata;
+ struct iio_dev *indio_dev;
const char *match;
int ret;
+ match = device_get_match_data(&client->dev);
+ if (match)
+ strlcpy(client->name, match, sizeof(client->name));
+
+ settings = st_accel_get_settings(client->name);
+ if (!settings) {
+ dev_err(&client->dev, "device name %s not recognized.\n",
+ client->name);
+ return -ENODEV;
+ }
+
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*adata));
if (!indio_dev)
return -ENOMEM;
adata = iio_priv(indio_dev);
+ adata->sensor_settings = (struct st_sensor_settings *)settings;
- match = device_get_match_data(&client->dev);
- if (match)
- strlcpy(client->name, match, sizeof(client->name));
-
- st_sensors_i2c_configure(indio_dev, client, adata);
+ ret = st_sensors_i2c_configure(indio_dev, client);
+ if (ret < 0)
+ return ret;
ret = st_accel_common_probe(indio_dev);
if (ret < 0)
diff --git a/drivers/iio/accel/st_accel_spi.c b/drivers/iio/accel/st_accel_spi.c
index dcc9bd2..8af7027 100644
--- a/drivers/iio/accel/st_accel_spi.c
+++ b/drivers/iio/accel/st_accel_spi.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics accelerometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -90,6 +89,10 @@
.compatible = "st,lis3dhh",
.data = LIS3DHH_ACCEL_DEV_NAME,
},
+ {
+ .compatible = "st,lis3de",
+ .data = LIS3DE_ACCEL_DEV_NAME,
+ },
{}
};
MODULE_DEVICE_TABLE(of, st_accel_of_match);
@@ -99,19 +102,31 @@
static int st_accel_spi_probe(struct spi_device *spi)
{
- struct iio_dev *indio_dev;
+ const struct st_sensor_settings *settings;
struct st_sensor_data *adata;
+ struct iio_dev *indio_dev;
int err;
+ st_sensors_of_name_probe(&spi->dev, st_accel_of_match,
+ spi->modalias, sizeof(spi->modalias));
+
+ settings = st_accel_get_settings(spi->modalias);
+ if (!settings) {
+ dev_err(&spi->dev, "device name %s not recognized.\n",
+ spi->modalias);
+ return -ENODEV;
+ }
+
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adata));
if (!indio_dev)
return -ENOMEM;
adata = iio_priv(indio_dev);
+ adata->sensor_settings = (struct st_sensor_settings *)settings;
- st_sensors_of_name_probe(&spi->dev, st_accel_of_match,
- spi->modalias, sizeof(spi->modalias));
- st_sensors_spi_configure(indio_dev, spi, adata);
+ err = st_sensors_spi_configure(indio_dev, spi);
+ if (err < 0)
+ return err;
err = st_accel_common_probe(indio_dev);
if (err < 0)
@@ -143,6 +158,7 @@
{ 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/accel/stk8312.c b/drivers/iio/accel/stk8312.c
index cacc0da..58c160c 100644
--- a/drivers/iio/accel/stk8312.c
+++ b/drivers/iio/accel/stk8312.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Sensortek STK8312 3-Axis Accelerometer
*
* Copyright (c) 2015, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for STK8312; 7-bit I2C address: 0x3D.
*/
diff --git a/drivers/iio/accel/stk8ba50.c b/drivers/iio/accel/stk8ba50.c
index 576b6b1..c70ddec 100644
--- a/drivers/iio/accel/stk8ba50.c
+++ b/drivers/iio/accel/stk8ba50.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Sensortek STK8BA50 3-Axis Accelerometer
*
* Copyright (c) 2015, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* STK8BA50 7-bit I2C address: 0x18.
*/
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 4a75492..f0af3a4 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# ADC drivers
#
@@ -10,6 +11,17 @@
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
@@ -46,18 +58,48 @@
module will be called ad7298.
config AD7476
- tristate "Analog Devices AD7476 and similar 1-channel ADCs driver"
+ tristate "Analog Devices AD7476 1-channel ADCs driver and other similar devices from AD an TI"
depends on SPI
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
- Say yes here to build support for Analog Devices AD7273, AD7274, AD7276,
- AD7277, AD7278, AD7475, AD7476, AD7477, AD7478, AD7466, AD7467, AD7468,
- AD7495, AD7910, AD7920, AD7920 SPI analog to digital converters (ADC).
+ Say yes here to build support for the following SPI analog to
+ digital converters (ADCs):
+ Analog Devices: AD7273, AD7274, AD7276, AD7277, AD7278, AD7475,
+ AD7476, AD7477, AD7478, AD7466, AD7467, AD7468, AD7495, AD7910,
+ AD7920.
+ Texas Instruments: ADS7866, ADS7867, ADS7868.
To compile this driver as a module, choose M here: the
module will be called ad7476.
+config AD7606
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config AD7606_IFACE_PARALLEL
+ tristate "Analog Devices AD7606 ADC driver with parallel interface support"
+ depends on HAS_IOMEM
+ select AD7606
+ help
+ Say yes here to build parallel interface support for Analog Devices:
+ ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7606_parallel.
+
+config AD7606_IFACE_SPI
+ tristate "Analog Devices AD7606 ADC driver with spi interface support"
+ depends on SPI
+ select AD7606
+ help
+ Say yes here to build spi interface support for Analog Devices:
+ ad7605-4, ad7606, ad7606-6, ad7606-4 analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7606_spi.
+
config AD7766
tristate "Analog Devices AD7766/AD7767 ADC driver"
depends on SPI_MASTER
@@ -70,6 +112,31 @@
To compile this driver as a module, choose M here: the module will be
called ad7766.
+config AD7768_1
+ tristate "Analog Devices AD7768-1 ADC driver"
+ depends on SPI
+ select IIO_BUFFER
+ select IIO_TRIGGER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Analog Devices AD7768-1 SPI
+ simultaneously sampling sigma-delta analog to digital converter (ADC).
+
+ To compile this driver as a module, choose M here: the module will be
+ called ad7768-1.
+
+config AD7780
+ tristate "Analog Devices AD7780 and similar ADCs driver"
+ depends on SPI
+ depends on GPIOLIB || COMPILE_TEST
+ select AD_SIGMA_DELTA
+ help
+ Say yes here to build support for Analog Devices AD7170, AD7171,
+ AD7780 and AD7781 SPI analog to digital converters (ADC).
+
+ To compile this driver as a module, choose M here: the
+ module will be called ad7780.
+
config AD7791
tristate "Analog Devices AD7791 ADC driver"
depends on SPI
@@ -116,6 +183,16 @@
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 +351,7 @@
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
@@ -326,7 +403,7 @@
This driver uses two GPIOs, one acts as the clock and controls the
channel selection and gain, the other one is used for the measurement
- data
+ data
Currently the raw value is read from the chip and delivered.
To get an actual weight one needs to subtract the
@@ -346,6 +423,15 @@
Say yes here to build support for TI INA2xx family of Power Monitors.
This driver is mutually exclusive with the HWMON version.
+config INGENIC_ADC
+ tristate "Ingenic JZ47xx SoCs ADC driver"
+ depends on MIPS || COMPILE_TEST
+ help
+ Say yes here to build support for the Ingenic JZ47xx SoCs ADC unit.
+
+ This driver can also be built as a module. If so, the module will be
+ called ingenic_adc.
+
config IMX7D_ADC
tristate "Freescale IMX7D ADC driver"
depends on ARCH_MXC || COMPILE_TEST
@@ -468,7 +554,7 @@
To compile this driver as a module, choose M here: the module will be
called max1363.
-config MAX9611
+config MAX9611
tristate "Maxim max9611/max9612 ADC driver"
depends on I2C
help
@@ -501,18 +587,28 @@
This driver can also be built as a module. If so, the module will be
called mcp3422.
+config MCP3911
+ tristate "Microchip Technology MCP3911 driver"
+ depends on SPI
+ help
+ Say yes here to build support for Microchip Technology's MCP3911
+ analog to digital converter.
+
+ This driver can also be built as a module. If so, the module will be
+ called mcp3911.
+
config MEDIATEK_MT6577_AUXADC
- tristate "MediaTek AUXADC driver"
- depends on ARCH_MEDIATEK || COMPILE_TEST
- depends on HAS_IOMEM
- help
- Say yes here to enable support for MediaTek mt65xx AUXADC.
+ tristate "MediaTek AUXADC driver"
+ depends on ARCH_MEDIATEK || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to enable support for MediaTek mt65xx AUXADC.
- The driver supports immediate mode operation to read from one of sixteen
- channels (external or internal).
+ The driver supports immediate mode operation to read from one of sixteen
+ channels (external or internal).
- This driver can also be built as a module. If so, the module will be
- called mt6577_auxadc.
+ This driver can also be built as a module. If so, the module will be
+ called mt6577_auxadc.
config MEN_Z188_ADC
tristate "MEN 16z188 ADC IP Core support"
@@ -545,6 +641,16 @@
To compile this driver as a module, choose M here: the
module will be called nau7802.
+config NPCM_ADC
+ tristate "Nuvoton NPCM ADC driver"
+ depends on ARCH_NPCM || COMPILE_TEST
+ depends on HAS_IOMEM
+ help
+ Say yes here to build support for Nuvoton NPCM ADC.
+
+ This driver can also be built as a module. If so, the module
+ will be called npcm_adc.
+
config PALMAS_GPADC
tristate "TI Palmas General Purpose ADC"
depends on MFD_PALMAS
@@ -596,6 +702,26 @@
To compile this driver as a module, choose M here: the module will
be called qcom-spmi-vadc.
+config QCOM_SPMI_ADC5
+ tristate "Qualcomm Technologies Inc. SPMI PMIC5 ADC"
+ depends on SPMI
+ select REGMAP_SPMI
+ select QCOM_VADC_COMMON
+ help
+ This is the IIO Voltage PMIC5 ADC driver for Qualcomm Technologies Inc.
+
+ The driver supports multiple channels read. The ADC is a 16-bit
+ sigma-delta ADC. The hardware supports calibrated results for
+ conversion requests and clients include reading voltage phone
+ power, on board system thermistors connected to the PMIC ADC,
+ PMIC die temperature, charger temperature, battery current, USB voltage
+ input, voltage signals connected to supported PMIC GPIO inputs. The
+ hardware supports internal pull-up for thermistors and can choose between
+ a 100k, 30k and 400k pull up using the ADC channels.
+
+ To compile this driver as a module, choose M here: the module will
+ be called qcom-spmi-adc5.
+
config RCAR_GYRO_ADC
tristate "Renesas R-Car GyroADC driver"
depends on ARCH_RCAR_GEN2 || COMPILE_TEST
@@ -696,7 +822,9 @@
depends on (ARCH_STM32 && OF) || COMPILE_TEST
select STM32_DFSDM_CORE
select REGMAP_MMIO
+ select IIO_BUFFER
select IIO_BUFFER_HW_CONSUMER
+ select IIO_TRIGGERED_BUFFER
help
Select this option to support ADCSigma delta modulator for
STMicroelectronics STM32 digital filter for sigma delta converter.
@@ -704,6 +832,13 @@
This driver can also be built as a module. If so, the module
will be called stm32-dfsdm-adc.
+config STMPE_ADC
+ tristate "STMicroelectronics STMPE ADC driver"
+ depends on OF && MFD_STMPE
+ help
+ Say yes here to build support for ST Microelectronics STMPE
+ built-in ADC block (stmpe811).
+
config STX104
tristate "Apex Embedded Systems STX104 driver"
depends on PC104 && X86
@@ -823,7 +958,7 @@
config TI_ADS1015
tristate "Texas Instruments ADS1015 ADC"
- depends on I2C && !SENSORS_ADS1015
+ depends on I2C
select REGMAP_I2C
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
@@ -836,7 +971,7 @@
config TI_ADS7950
tristate "Texas Instruments ADS7950 ADC driver"
- depends on SPI
+ depends on SPI && GPIOLIB
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
@@ -847,6 +982,16 @@
To compile this driver as a module, choose M here: the
module will be called ti-ads7950.
+config TI_ADS8344
+ tristate "Texas Instruments ADS8344"
+ depends on SPI && OF
+ help
+ If you say yes here you get support for Texas Instruments ADS8344
+ ADC chips
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads8344.
+
config TI_ADS8688
tristate "Texas Instruments ADS8688"
depends on SPI && OF
@@ -857,6 +1002,16 @@
This driver can also be built as a module. If so, the module will be
called ti-ads8688.
+config TI_ADS124S08
+ tristate "Texas Instruments ADS124S08"
+ depends on SPI && OF
+ help
+ If you say yes here you get support for Texas Instruments ADS124S08
+ and ADS124S06 ADC chips
+
+ This driver can also be built as a module. If so, the module will be
+ called ti-ads124s08.
+
config TI_AM335X_ADC
tristate "TI's AM335X ADC driver"
depends on MFD_TI_AM335X_TSCADC && HAS_DMA
@@ -930,7 +1085,6 @@
config XILINX_XADC
tristate "Xilinx XADC driver"
- depends on ARCH_ZYNQ || MICROBLAZE || COMPILE_TEST
depends on HAS_IOMEM
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 03db7b5..ef9cc48 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -5,15 +5,22 @@
# 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
obj-$(CONFIG_AD7923) += ad7923.o
obj-$(CONFIG_AD7476) += ad7476.o
+obj-$(CONFIG_AD7606_IFACE_PARALLEL) += ad7606_par.o
+obj-$(CONFIG_AD7606_IFACE_SPI) += ad7606_spi.o
+obj-$(CONFIG_AD7606) += ad7606.o
obj-$(CONFIG_AD7766) += ad7766.o
+obj-$(CONFIG_AD7768_1) += ad7768-1.o
+obj-$(CONFIG_AD7780) += ad7780.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
@@ -34,6 +41,7 @@
obj-$(CONFIG_HX711) += hx711.o
obj-$(CONFIG_IMX7D_ADC) += imx7d_adc.o
obj-$(CONFIG_INA2XX_ADC) += ina2xx-adc.o
+obj-$(CONFIG_INGENIC_ADC) += ingenic-adc.o
obj-$(CONFIG_LP8788_ADC) += lp8788_adc.o
obj-$(CONFIG_LPC18XX_ADC) += lpc18xx_adc.o
obj-$(CONFIG_LPC32XX_ADC) += lpc32xx_adc.o
@@ -47,12 +55,15 @@
obj-$(CONFIG_MAX9611) += max9611.o
obj-$(CONFIG_MCP320X) += mcp320x.o
obj-$(CONFIG_MCP3422) += mcp3422.o
+obj-$(CONFIG_MCP3911) += mcp3911.o
obj-$(CONFIG_MEDIATEK_MT6577_AUXADC) += mt6577_auxadc.o
obj-$(CONFIG_MEN_Z188_ADC) += men_z188_adc.o
obj-$(CONFIG_MESON_SARADC) += meson_saradc.o
obj-$(CONFIG_MXS_LRADC_ADC) += mxs-lradc-adc.o
obj-$(CONFIG_NAU7802) += nau7802.o
+obj-$(CONFIG_NPCM_ADC) += npcm_adc.o
obj-$(CONFIG_PALMAS_GPADC) += palmas_gpadc.o
+obj-$(CONFIG_QCOM_SPMI_ADC5) += qcom-spmi-adc5.o
obj-$(CONFIG_QCOM_SPMI_IADC) += qcom-spmi-iadc.o
obj-$(CONFIG_QCOM_VADC_COMMON) += qcom-vadc-common.o
obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
@@ -67,6 +78,7 @@
obj-$(CONFIG_STM32_ADC) += stm32-adc.o
obj-$(CONFIG_STM32_DFSDM_CORE) += stm32-dfsdm-core.o
obj-$(CONFIG_STM32_DFSDM_ADC) += stm32-dfsdm-adc.o
+obj-$(CONFIG_STMPE_ADC) += stmpe-adc.o
obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
obj-$(CONFIG_TI_ADC0832) += ti-adc0832.o
obj-$(CONFIG_TI_ADC084S021) += ti-adc084s021.o
@@ -76,7 +88,9 @@
obj-$(CONFIG_TI_ADC161S626) += ti-adc161s626.o
obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o
+obj-$(CONFIG_TI_ADS8344) += ti-ads8344.o
obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
+obj-$(CONFIG_TI_ADS124S08) += ti-ads124s08.o
obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o
obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c
new file mode 100644
index 0000000..edc6f1c
--- /dev/null
+++ b/drivers/iio/adc/ad7124.c
@@ -0,0 +1,697 @@
+// 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)
+#define AD7124_CONFIG_IN_BUFF_MSK GENMASK(7, 6)
+#define AD7124_CONFIG_IN_BUFF(x) FIELD_PREP(AD7124_CONFIG_IN_BUFF_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;
+ bool buf_positive;
+ bool buf_negative;
+ 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;
+ 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;
+ struct ad7124_channel_config *chan_config;
+ 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;
+
+ chan_config = devm_kcalloc(indio_dev->dev.parent, st->num_channels,
+ sizeof(*chan_config), GFP_KERNEL);
+ if (!chan_config)
+ return -ENOMEM;
+
+ indio_dev->channels = chan;
+ indio_dev->num_channels = st->num_channels;
+ st->channel_config = chan_config;
+
+ 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;
+
+ 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;
+
+ st->channel_config[channel].buf_positive =
+ of_property_read_bool(child, "adi,buffered-positive");
+ st->channel_config[channel].buf_negative =
+ of_property_read_bool(child, "adi,buffered-negative");
+
+ *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, tmp;
+
+ 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;
+
+ tmp = (st->channel_config[i].buf_positive << 1) +
+ st->channel_config[i].buf_negative;
+
+ val = AD7124_CONFIG_BIPOLAR(st->channel_config[i].bipolar) |
+ AD7124_CONFIG_REF_SEL(st->channel_config[i].refsel) |
+ AD7124_CONFIG_IN_BUFF(tmp);
+ ret = ad_sd_write_reg(&st->sd, AD7124_CONFIG(i), 2, val);
+ if (ret < 0)
+ return ret;
+ /*
+ * 9.38 SPS is the minimum output data rate supported
+ * regardless of the selected power mode. Round it up to 10 and
+ * set all the enabled channels to this default value.
+ */
+ 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/ad7266.c b/drivers/iio/adc/ad7266.c
index 605eb5e..c31b8ea 100644
--- a/drivers/iio/adc/ad7266.c
+++ b/drivers/iio/adc/ad7266.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7266/65 SPI ADC driver
*
* Copyright 2012 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
diff --git a/drivers/iio/adc/ad7291.c b/drivers/iio/adc/ad7291.c
index a862b5d..b2b137f 100644
--- a/drivers/iio/adc/ad7291.c
+++ b/drivers/iio/adc/ad7291.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD7291 8-Channel, I2C, 12-Bit SAR ADC with Temperature Sensor
*
* Copyright 2010-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/device.h>
diff --git a/drivers/iio/adc/ad7298.c b/drivers/iio/adc/ad7298.c
index 2b20c6c..dc8d8c5 100644
--- a/drivers/iio/adc/ad7298.c
+++ b/drivers/iio/adc/ad7298.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7298 SPI ADC driver
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -385,6 +384,6 @@
};
module_spi_driver(ad7298_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7298 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c
index fbaae47..7674748 100644
--- a/drivers/iio/adc/ad7476.c
+++ b/drivers/iio/adc/ad7476.c
@@ -59,6 +59,9 @@
ID_ADC081S,
ID_ADC101S,
ID_ADC121S,
+ ID_ADS7866,
+ ID_ADS7867,
+ ID_ADS7868,
};
static irqreturn_t ad7476_trigger_handler(int irq, void *p)
@@ -157,6 +160,8 @@
#define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \
BIT(IIO_CHAN_INFO_RAW))
#define AD7091R_CHAN(bits) _AD7476_CHAN((bits), 16 - (bits), 0)
+#define ADS786X_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \
+ BIT(IIO_CHAN_INFO_RAW))
static const struct ad7476_chip_info ad7476_chip_info_tbl[] = {
[ID_AD7091R] = {
@@ -209,6 +214,18 @@
.channel[0] = ADC081S_CHAN(12),
.channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
},
+ [ID_ADS7866] = {
+ .channel[0] = ADS786X_CHAN(12),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADS7867] = {
+ .channel[0] = ADS786X_CHAN(10),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
+ [ID_ADS7868] = {
+ .channel[0] = ADS786X_CHAN(8),
+ .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1),
+ },
};
static const struct iio_info ad7476_info = {
@@ -314,6 +331,9 @@
{"adc081s", ID_ADC081S},
{"adc101s", ID_ADC101S},
{"adc121s", ID_ADC121S},
+ {"ads7866", ID_ADS7866},
+ {"ads7867", ID_ADS7867},
+ {"ads7868", ID_ADS7868},
{}
};
MODULE_DEVICE_TABLE(spi, ad7476_id);
@@ -328,6 +348,6 @@
};
module_spi_driver(ad7476_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7476 and similar 1-channel ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
new file mode 100644
index 0000000..f5ba94c
--- /dev/null
+++ b/drivers/iio/adc/ad7606.c
@@ -0,0 +1,736 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/util_macros.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#include "ad7606.h"
+
+/*
+ * Scales are computed as 5000/32768 and 10000/32768 respectively,
+ * so that when applied to the raw values they provide mV values
+ */
+static const unsigned int ad7606_scale_avail[2] = {
+ 152588, 305176
+};
+
+
+static const unsigned int ad7616_sw_scale_avail[3] = {
+ 76293, 152588, 305176
+};
+
+static const unsigned int ad7606_oversampling_avail[7] = {
+ 1, 2, 4, 8, 16, 32, 64,
+};
+
+static const unsigned int ad7616_oversampling_avail[8] = {
+ 1, 2, 4, 8, 16, 32, 64, 128,
+};
+
+static int ad7606_reset(struct ad7606_state *st)
+{
+ if (st->gpio_reset) {
+ gpiod_set_value(st->gpio_reset, 1);
+ ndelay(100); /* t_reset >= 100ns */
+ gpiod_set_value(st->gpio_reset, 0);
+ return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int ad7606_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ if (readval) {
+ ret = st->bops->reg_read(st, reg);
+ if (ret < 0)
+ goto err_unlock;
+ *readval = ret;
+ ret = 0;
+ } else {
+ ret = st->bops->reg_write(st, reg, writeval);
+ }
+err_unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static int ad7606_read_samples(struct ad7606_state *st)
+{
+ unsigned int num = st->chip_info->num_channels;
+ u16 *data = st->data;
+ int ret;
+
+ /*
+ * The frstdata signal is set to high while and after reading the sample
+ * of the first channel and low for all other channels. This can be used
+ * to check that the incoming data is correctly aligned. During normal
+ * operation the data should never become unaligned, but some glitch or
+ * electrostatic discharge might cause an extra read or clock cycle.
+ * Monitoring the frstdata signal allows to recover from such failure
+ * situations.
+ */
+
+ if (st->gpio_frstdata) {
+ ret = st->bops->read_block(st->dev, 1, data);
+ if (ret)
+ return ret;
+
+ if (!gpiod_get_value(st->gpio_frstdata)) {
+ ad7606_reset(st);
+ return -EIO;
+ }
+
+ data++;
+ num--;
+ }
+
+ return st->bops->read_block(st->dev, num, data);
+}
+
+static irqreturn_t ad7606_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = ad7606_read_samples(st);
+ if (ret == 0)
+ iio_push_to_buffers_with_timestamp(indio_dev, st->data,
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+ /* The rising edge of the CONVST signal starts a new conversion. */
+ gpiod_set_value(st->gpio_convst, 1);
+
+ mutex_unlock(&st->lock);
+
+ return IRQ_HANDLED;
+}
+
+static int ad7606_scan_direct(struct iio_dev *indio_dev, unsigned int ch)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int ret;
+
+ gpiod_set_value(st->gpio_convst, 1);
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(1000));
+ if (!ret) {
+ ret = -ETIMEDOUT;
+ goto error_ret;
+ }
+
+ ret = ad7606_read_samples(st);
+ if (ret == 0)
+ ret = st->data[ch];
+
+error_ret:
+ gpiod_set_value(st->gpio_convst, 0);
+
+ return ret;
+}
+
+static int ad7606_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ int ret, ch = 0;
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7606_scan_direct(indio_dev, chan->address);
+ iio_device_release_direct_mode(indio_dev);
+
+ if (ret < 0)
+ return ret;
+ *val = (short)ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (st->sw_mode_en)
+ ch = chan->address;
+ *val = 0;
+ *val2 = st->scale_avail[st->range[ch]];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *val = st->oversampling;
+ return IIO_VAL_INT;
+ }
+ return -EINVAL;
+}
+
+static ssize_t ad7606_show_avail(char *buf, const unsigned int *vals,
+ unsigned int n, bool micros)
+{
+ size_t len = 0;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ len += scnprintf(buf + len, PAGE_SIZE - len,
+ micros ? "0.%06u " : "%u ", vals[i]);
+ }
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static ssize_t in_voltage_scale_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_show_avail(buf, st->scale_avail, st->num_scales, true);
+}
+
+static IIO_DEVICE_ATTR_RO(in_voltage_scale_available, 0);
+
+static int ad7606_write_scale_hw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ gpiod_set_value(st->gpio_range, val);
+
+ return 0;
+}
+
+static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ DECLARE_BITMAP(values, 3);
+
+ values[0] = val;
+
+ gpiod_set_array_value(ARRAY_SIZE(values), st->gpio_os->desc,
+ st->gpio_os->info, values);
+
+ /* AD7616 requires a reset to update value */
+ if (st->chip_info->os_req_reset)
+ ad7606_reset(st);
+
+ return 0;
+}
+
+static int ad7606_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ int i, ret, ch = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ mutex_lock(&st->lock);
+ i = find_closest(val2, st->scale_avail, st->num_scales);
+ if (st->sw_mode_en)
+ ch = chan->address;
+ ret = st->write_scale(indio_dev, ch, i);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ st->range[ch] = i;
+ mutex_unlock(&st->lock);
+
+ return 0;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ if (val2)
+ return -EINVAL;
+ i = find_closest(val, st->oversampling_avail,
+ st->num_os_ratios);
+ mutex_lock(&st->lock);
+ ret = st->write_os(indio_dev, i);
+ if (ret < 0) {
+ mutex_unlock(&st->lock);
+ return ret;
+ }
+ st->oversampling = st->oversampling_avail[i];
+ mutex_unlock(&st->lock);
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static ssize_t ad7606_oversampling_ratio_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_show_avail(buf, st->oversampling_avail,
+ st->num_os_ratios, false);
+}
+
+static IIO_DEVICE_ATTR(oversampling_ratio_available, 0444,
+ ad7606_oversampling_ratio_avail, NULL, 0);
+
+static struct attribute *ad7606_attributes_os_and_range[] = {
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ &iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_os_and_range = {
+ .attrs = ad7606_attributes_os_and_range,
+};
+
+static struct attribute *ad7606_attributes_os[] = {
+ &iio_dev_attr_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_os = {
+ .attrs = ad7606_attributes_os,
+};
+
+static struct attribute *ad7606_attributes_range[] = {
+ &iio_dev_attr_in_voltage_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ad7606_attribute_group_range = {
+ .attrs = ad7606_attributes_range,
+};
+
+static const struct iio_chan_spec ad7605_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+ AD7605_CHANNEL(0),
+ AD7605_CHANNEL(1),
+ AD7605_CHANNEL(2),
+ AD7605_CHANNEL(3),
+};
+
+static const struct iio_chan_spec ad7606_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ AD7606_CHANNEL(0),
+ AD7606_CHANNEL(1),
+ AD7606_CHANNEL(2),
+ AD7606_CHANNEL(3),
+ AD7606_CHANNEL(4),
+ AD7606_CHANNEL(5),
+ AD7606_CHANNEL(6),
+ AD7606_CHANNEL(7),
+};
+
+/*
+ * The current assumption that this driver makes for AD7616, is that it's
+ * working in Hardware Mode with Serial, Burst and Sequencer modes activated.
+ * To activate them, following pins must be pulled high:
+ * -SER/PAR
+ * -SEQEN
+ * And following pins must be pulled low:
+ * -WR/BURST
+ * -DB4/SER1W
+ */
+static const struct iio_chan_spec ad7616_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(16),
+ AD7606_CHANNEL(0),
+ AD7606_CHANNEL(1),
+ AD7606_CHANNEL(2),
+ AD7606_CHANNEL(3),
+ AD7606_CHANNEL(4),
+ AD7606_CHANNEL(5),
+ AD7606_CHANNEL(6),
+ AD7606_CHANNEL(7),
+ AD7606_CHANNEL(8),
+ AD7606_CHANNEL(9),
+ AD7606_CHANNEL(10),
+ AD7606_CHANNEL(11),
+ AD7606_CHANNEL(12),
+ AD7606_CHANNEL(13),
+ AD7606_CHANNEL(14),
+ AD7606_CHANNEL(15),
+};
+
+static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
+ /* More devices added in future */
+ [ID_AD7605_4] = {
+ .channels = ad7605_channels,
+ .num_channels = 5,
+ },
+ [ID_AD7606_8] = {
+ .channels = ad7606_channels,
+ .num_channels = 9,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7606_6] = {
+ .channels = ad7606_channels,
+ .num_channels = 7,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7606_4] = {
+ .channels = ad7606_channels,
+ .num_channels = 5,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7606B] = {
+ .channels = ad7606_channels,
+ .num_channels = 9,
+ .oversampling_avail = ad7606_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7606_oversampling_avail),
+ },
+ [ID_AD7616] = {
+ .channels = ad7616_channels,
+ .num_channels = 17,
+ .oversampling_avail = ad7616_oversampling_avail,
+ .oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
+ .os_req_reset = true,
+ .init_delay_ms = 15,
+ },
+};
+
+static int ad7606_request_gpios(struct ad7606_state *st)
+{
+ struct device *dev = st->dev;
+
+ st->gpio_convst = devm_gpiod_get(dev, "adi,conversion-start",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_convst))
+ return PTR_ERR(st->gpio_convst);
+
+ st->gpio_reset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_reset))
+ return PTR_ERR(st->gpio_reset);
+
+ st->gpio_range = devm_gpiod_get_optional(dev, "adi,range",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_range))
+ return PTR_ERR(st->gpio_range);
+
+ st->gpio_standby = devm_gpiod_get_optional(dev, "standby",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gpio_standby))
+ return PTR_ERR(st->gpio_standby);
+
+ st->gpio_frstdata = devm_gpiod_get_optional(dev, "adi,first-data",
+ GPIOD_IN);
+ if (IS_ERR(st->gpio_frstdata))
+ return PTR_ERR(st->gpio_frstdata);
+
+ if (!st->chip_info->oversampling_num)
+ return 0;
+
+ st->gpio_os = devm_gpiod_get_array_optional(dev,
+ "adi,oversampling-ratio",
+ GPIOD_OUT_LOW);
+ return PTR_ERR_OR_ZERO(st->gpio_os);
+}
+
+/*
+ * The BUSY signal indicates when conversions are in progress, so when a rising
+ * edge of CONVST is applied, BUSY goes logic high and transitions low at the
+ * end of the entire conversion process. The falling edge of the BUSY signal
+ * triggers this interrupt.
+ */
+static irqreturn_t ad7606_interrupt(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev)) {
+ gpiod_set_value(st->gpio_convst, 0);
+ iio_trigger_poll_chained(st->trig);
+ } else {
+ complete(&st->completion);
+ }
+
+ return IRQ_HANDLED;
+};
+
+static int ad7606_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (st->trig != trig)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ad7606_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ iio_triggered_buffer_postenable(indio_dev);
+ gpiod_set_value(st->gpio_convst, 1);
+
+ return 0;
+}
+
+static int ad7606_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ gpiod_set_value(st->gpio_convst, 0);
+
+ return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops ad7606_buffer_ops = {
+ .postenable = &ad7606_buffer_postenable,
+ .predisable = &ad7606_buffer_predisable,
+};
+
+static const struct iio_info ad7606_info_no_os_or_range = {
+ .read_raw = &ad7606_read_raw,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_os_and_range = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .attrs = &ad7606_attribute_group_os_and_range,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_os_range_and_debug = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .debugfs_reg_access = &ad7606_reg_access,
+ .attrs = &ad7606_attribute_group_os_and_range,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_os = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .attrs = &ad7606_attribute_group_os,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_info ad7606_info_range = {
+ .read_raw = &ad7606_read_raw,
+ .write_raw = &ad7606_write_raw,
+ .attrs = &ad7606_attribute_group_range,
+ .validate_trigger = &ad7606_validate_trigger,
+};
+
+static const struct iio_trigger_ops ad7606_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static void ad7606_regulator_disable(void *data)
+{
+ struct ad7606_state *st = data;
+
+ regulator_disable(st->reg);
+}
+
+int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
+ const char *name, unsigned int id,
+ const struct ad7606_bus_ops *bops)
+{
+ struct ad7606_state *st;
+ int ret;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+
+ st->dev = dev;
+ mutex_init(&st->lock);
+ st->bops = bops;
+ st->base_address = base_address;
+ /* tied to logic low, analog input range is +/- 5V */
+ st->range[0] = 0;
+ st->oversampling = 1;
+ st->scale_avail = ad7606_scale_avail;
+ st->num_scales = ARRAY_SIZE(ad7606_scale_avail);
+
+ st->reg = devm_regulator_get(dev, "avcc");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(dev, "Failed to enable specified AVcc supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, ad7606_regulator_disable, st);
+ if (ret)
+ return ret;
+
+ st->chip_info = &ad7606_chip_info_tbl[id];
+
+ if (st->chip_info->oversampling_num) {
+ st->oversampling_avail = st->chip_info->oversampling_avail;
+ st->num_os_ratios = st->chip_info->oversampling_num;
+ }
+
+ ret = ad7606_request_gpios(st);
+ if (ret)
+ return ret;
+
+ indio_dev->dev.parent = dev;
+ if (st->gpio_os) {
+ if (st->gpio_range)
+ indio_dev->info = &ad7606_info_os_and_range;
+ else
+ indio_dev->info = &ad7606_info_os;
+ } else {
+ if (st->gpio_range)
+ indio_dev->info = &ad7606_info_range;
+ else
+ indio_dev->info = &ad7606_info_no_os_or_range;
+ }
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = name;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ init_completion(&st->completion);
+
+ ret = ad7606_reset(st);
+ if (ret)
+ dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
+
+ /* AD7616 requires al least 15ms to reconfigure after a reset */
+ if (st->chip_info->init_delay_ms) {
+ if (msleep_interruptible(st->chip_info->init_delay_ms))
+ return -ERESTARTSYS;
+ }
+
+ st->write_scale = ad7606_write_scale_hw;
+ st->write_os = ad7606_write_os_hw;
+
+ if (st->bops->sw_mode_config)
+ st->sw_mode_en = device_property_present(st->dev,
+ "adi,sw-mode");
+
+ if (st->sw_mode_en) {
+ /* Scale of 0.076293 is only available in sw mode */
+ st->scale_avail = ad7616_sw_scale_avail;
+ st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
+
+ /* After reset, in software mode, ±10 V is set by default */
+ memset32(st->range, 2, ARRAY_SIZE(st->range));
+ indio_dev->info = &ad7606_info_os_range_and_debug;
+
+ ret = st->bops->sw_mode_config(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ st->trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+ if (!st->trig)
+ return -ENOMEM;
+
+ st->trig->ops = &ad7606_trigger_ops;
+ st->trig->dev.parent = dev;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = devm_iio_trigger_register(dev, st->trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(st->trig);
+
+ ret = devm_request_threaded_irq(dev, irq,
+ NULL,
+ &ad7606_interrupt,
+ IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+ name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad7606_trigger_handler,
+ &ad7606_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_GPL(ad7606_probe);
+
+#ifdef CONFIG_PM_SLEEP
+
+static int ad7606_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (st->gpio_standby) {
+ gpiod_set_value(st->gpio_range, 1);
+ gpiod_set_value(st->gpio_standby, 0);
+ }
+
+ return 0;
+}
+
+static int ad7606_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ if (st->gpio_standby) {
+ gpiod_set_value(st->gpio_range, st->range[0]);
+ gpiod_set_value(st->gpio_standby, 1);
+ ad7606_reset(st);
+ }
+
+ return 0;
+}
+
+SIMPLE_DEV_PM_OPS(ad7606_pm_ops, ad7606_suspend, ad7606_resume);
+EXPORT_SYMBOL_GPL(ad7606_pm_ops);
+
+#endif
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
new file mode 100644
index 0000000..9350ef1
--- /dev/null
+++ b/drivers/iio/adc/ad7606.h
@@ -0,0 +1,172 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * AD7606 ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#ifndef IIO_ADC_AD7606_H_
+#define IIO_ADC_AD7606_H_
+
+#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = num, \
+ .address = num, \
+ .info_mask_separate = mask_sep, \
+ .info_mask_shared_by_type = mask_type, \
+ .info_mask_shared_by_all = mask_all, \
+ .scan_index = num, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+#define AD7605_CHANNEL(num) \
+ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
+ BIT(IIO_CHAN_INFO_SCALE), 0)
+
+#define AD7606_CHANNEL(num) \
+ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW), \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+
+#define AD7616_CHANNEL(num) \
+ AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
+ 0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+
+/**
+ * struct ad7606_chip_info - chip specific information
+ * @channels: channel specification
+ * @num_channels: number of channels
+ * @oversampling_avail pointer to the array which stores the available
+ * oversampling ratios.
+ * @oversampling_num number of elements stored in oversampling_avail array
+ * @os_req_reset some devices require a reset to update oversampling
+ * @init_delay_ms required delay in miliseconds for initialization
+ * after a restart
+ */
+struct ad7606_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+ const unsigned int *oversampling_avail;
+ unsigned int oversampling_num;
+ bool os_req_reset;
+ unsigned long init_delay_ms;
+};
+
+/**
+ * struct ad7606_state - driver instance specific data
+ * @dev pointer to kernel device
+ * @chip_info entry in the table of chips that describes this device
+ * @reg regulator info for the the power supply of the device
+ * @bops bus operations (SPI or parallel)
+ * @range voltage range selection, selects which scale to apply
+ * @oversampling oversampling selection
+ * @base_address address from where to read data in parallel operation
+ * @sw_mode_en software mode enabled
+ * @scale_avail pointer to the array which stores the available scales
+ * @num_scales number of elements stored in the scale_avail array
+ * @oversampling_avail pointer to the array which stores the available
+ * oversampling ratios.
+ * @num_os_ratios number of elements stored in oversampling_avail array
+ * @write_scale pointer to the function which writes the scale
+ * @write_os pointer to the function which writes the os
+ * @lock protect sensor state from concurrent accesses to GPIOs
+ * @gpio_convst GPIO descriptor for conversion start signal (CONVST)
+ * @gpio_reset GPIO descriptor for device hard-reset
+ * @gpio_range GPIO descriptor for range selection
+ * @gpio_standby GPIO descriptor for stand-by signal (STBY),
+ * controls power-down mode of device
+ * @gpio_frstdata GPIO descriptor for reading from device when data
+ * is being read on the first channel
+ * @gpio_os GPIO descriptors to control oversampling on the device
+ * @complete completion to indicate end of conversion
+ * @trig The IIO trigger associated with the device.
+ * @data buffer for reading data from the device
+ * @d16 be16 buffer for reading data from the device
+ */
+struct ad7606_state {
+ struct device *dev;
+ const struct ad7606_chip_info *chip_info;
+ struct regulator *reg;
+ const struct ad7606_bus_ops *bops;
+ unsigned int range[16];
+ unsigned int oversampling;
+ void __iomem *base_address;
+ bool sw_mode_en;
+ const unsigned int *scale_avail;
+ unsigned int num_scales;
+ const unsigned int *oversampling_avail;
+ unsigned int num_os_ratios;
+ int (*write_scale)(struct iio_dev *indio_dev, int ch, int val);
+ int (*write_os)(struct iio_dev *indio_dev, int val);
+
+ struct mutex lock; /* protect sensor state */
+ struct gpio_desc *gpio_convst;
+ struct gpio_desc *gpio_reset;
+ struct gpio_desc *gpio_range;
+ struct gpio_desc *gpio_standby;
+ struct gpio_desc *gpio_frstdata;
+ struct gpio_descs *gpio_os;
+ struct iio_trigger *trig;
+ struct completion completion;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ * 16 * 16-bit samples + 64-bit timestamp
+ */
+ unsigned short data[20] ____cacheline_aligned;
+ __be16 d16[2];
+};
+
+/**
+ * struct ad7606_bus_ops - driver bus operations
+ * @read_block function pointer for reading blocks of data
+ * @sw_mode_config: pointer to a function which configured the device
+ * for software mode
+ * @reg_read function pointer for reading spi register
+ * @reg_write function pointer for writing spi register
+ * @write_mask function pointer for write spi register with mask
+ * @rd_wr_cmd pointer to the function which calculates the spi address
+ */
+struct ad7606_bus_ops {
+ /* more methods added in future? */
+ int (*read_block)(struct device *dev, int num, void *data);
+ int (*sw_mode_config)(struct iio_dev *indio_dev);
+ int (*reg_read)(struct ad7606_state *st, unsigned int addr);
+ int (*reg_write)(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned int val);
+ int (*write_mask)(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned long mask,
+ unsigned int val);
+ u16 (*rd_wr_cmd)(int addr, char isWriteOp);
+};
+
+int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
+ const char *name, unsigned int id,
+ const struct ad7606_bus_ops *bops);
+
+enum ad7606_supported_device_ids {
+ ID_AD7605_4,
+ ID_AD7606_8,
+ ID_AD7606_6,
+ ID_AD7606_4,
+ ID_AD7606B,
+ ID_AD7616,
+};
+
+#ifdef CONFIG_PM_SLEEP
+extern const struct dev_pm_ops ad7606_pm_ops;
+#define AD7606_PM_OPS (&ad7606_pm_ops)
+#else
+#define AD7606_PM_OPS NULL
+#endif
+
+#endif /* IIO_ADC_AD7606_H_ */
diff --git a/drivers/iio/adc/ad7606_par.c b/drivers/iio/adc/ad7606_par.c
new file mode 100644
index 0000000..f732b3a
--- /dev/null
+++ b/drivers/iio/adc/ad7606_par.c
@@ -0,0 +1,103 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7606 Parallel Interface ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/types.h>
+#include <linux/err.h>
+#include <linux/io.h>
+
+#include <linux/iio/iio.h>
+#include "ad7606.h"
+
+static int ad7606_par16_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ insw((unsigned long)st->base_address, buf, count);
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par16_bops = {
+ .read_block = ad7606_par16_read_block,
+};
+
+static int ad7606_par8_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ insb((unsigned long)st->base_address, buf, count * 2);
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_par8_bops = {
+ .read_block = ad7606_par8_read_block,
+};
+
+static int ad7606_par_probe(struct platform_device *pdev)
+{
+ const struct platform_device_id *id = platform_get_device_id(pdev);
+ struct resource *res;
+ void __iomem *addr;
+ resource_size_t remap_size;
+ int irq;
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ addr = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(addr))
+ return PTR_ERR(addr);
+
+ remap_size = resource_size(res);
+
+ return ad7606_probe(&pdev->dev, irq, addr,
+ id->name, id->driver_data,
+ remap_size > 1 ? &ad7606_par16_bops :
+ &ad7606_par8_bops);
+}
+
+static const struct platform_device_id ad7606_driver_ids[] = {
+ { .name = "ad7605-4", .driver_data = ID_AD7605_4, },
+ { .name = "ad7606-4", .driver_data = ID_AD7606_4, },
+ { .name = "ad7606-6", .driver_data = ID_AD7606_6, },
+ { .name = "ad7606-8", .driver_data = ID_AD7606_8, },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, ad7606_driver_ids);
+
+static const struct of_device_id ad7606_of_match[] = {
+ { .compatible = "adi,ad7605-4" },
+ { .compatible = "adi,ad7606-4" },
+ { .compatible = "adi,ad7606-6" },
+ { .compatible = "adi,ad7606-8" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7606_of_match);
+
+static struct platform_driver ad7606_driver = {
+ .probe = ad7606_par_probe,
+ .id_table = ad7606_driver_ids,
+ .driver = {
+ .name = "ad7606",
+ .pm = AD7606_PM_OPS,
+ .of_match_table = ad7606_of_match,
+ },
+};
+module_platform_driver(ad7606_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
new file mode 100644
index 0000000..29945ad
--- /dev/null
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -0,0 +1,364 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7606 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+#include <linux/err.h>
+
+#include <linux/iio/iio.h>
+#include "ad7606.h"
+
+#define MAX_SPI_FREQ_HZ 23500000 /* VDRIVE above 4.75 V */
+
+#define AD7616_CONFIGURATION_REGISTER 0x02
+#define AD7616_OS_MASK GENMASK(4, 2)
+#define AD7616_BURST_MODE BIT(6)
+#define AD7616_SEQEN_MODE BIT(5)
+#define AD7616_RANGE_CH_A_ADDR_OFF 0x04
+#define AD7616_RANGE_CH_B_ADDR_OFF 0x06
+/*
+ * Range of channels from a group are stored in 2 registers.
+ * 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
+ * For channels from second group(8-15) the order is the same, only with
+ * an offset of 2 for register address.
+ */
+#define AD7616_RANGE_CH_ADDR(ch) ((ch) >> 2)
+/* The range of the channel is stored in 2 bits */
+#define AD7616_RANGE_CH_MSK(ch) (0b11 << (((ch) & 0b11) * 2))
+#define AD7616_RANGE_CH_MODE(ch, mode) ((mode) << ((((ch) & 0b11)) * 2))
+
+#define AD7606_CONFIGURATION_REGISTER 0x02
+#define AD7606_SINGLE_DOUT 0x00
+
+/*
+ * Range for AD7606B channels are stored in registers starting with address 0x3.
+ * Each register stores range for 2 channels(4 bits per channel).
+ */
+#define AD7606_RANGE_CH_MSK(ch) (GENMASK(3, 0) << (4 * ((ch) & 0x1)))
+#define AD7606_RANGE_CH_MODE(ch, mode) \
+ ((GENMASK(3, 0) & mode) << (4 * ((ch) & 0x1)))
+#define AD7606_RANGE_CH_ADDR(ch) (0x03 + ((ch) >> 1))
+#define AD7606_OS_MODE 0x08
+
+static const struct iio_chan_spec ad7616_sw_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(16),
+ AD7616_CHANNEL(0),
+ AD7616_CHANNEL(1),
+ AD7616_CHANNEL(2),
+ AD7616_CHANNEL(3),
+ AD7616_CHANNEL(4),
+ AD7616_CHANNEL(5),
+ AD7616_CHANNEL(6),
+ AD7616_CHANNEL(7),
+ AD7616_CHANNEL(8),
+ AD7616_CHANNEL(9),
+ AD7616_CHANNEL(10),
+ AD7616_CHANNEL(11),
+ AD7616_CHANNEL(12),
+ AD7616_CHANNEL(13),
+ AD7616_CHANNEL(14),
+ AD7616_CHANNEL(15),
+};
+
+static const struct iio_chan_spec ad7606b_sw_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(8),
+ AD7616_CHANNEL(0),
+ AD7616_CHANNEL(1),
+ AD7616_CHANNEL(2),
+ AD7616_CHANNEL(3),
+ AD7616_CHANNEL(4),
+ AD7616_CHANNEL(5),
+ AD7616_CHANNEL(6),
+ AD7616_CHANNEL(7),
+};
+
+static const unsigned int ad7606B_oversampling_avail[9] = {
+ 1, 2, 4, 8, 16, 32, 64, 128, 256
+};
+
+static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
+{
+ /*
+ * The address of register consist of one w/r bit
+ * 6 bits of address followed by one reserved bit.
+ */
+ return ((addr & 0x7F) << 1) | ((isWriteOp & 0x1) << 7);
+}
+
+static u16 ad7606B_spi_rd_wr_cmd(int addr, char is_write_op)
+{
+ /*
+ * The address of register consists of one bit which
+ * specifies a read command placed in bit 6, followed by
+ * 6 bits of address.
+ */
+ return (addr & 0x3F) | (((~is_write_op) & 0x1) << 6);
+}
+
+static int ad7606_spi_read_block(struct device *dev,
+ int count, void *buf)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ int i, ret;
+ unsigned short *data = buf;
+ __be16 *bdata = buf;
+
+ ret = spi_read(spi, buf, count * 2);
+ if (ret < 0) {
+ dev_err(&spi->dev, "SPI read error\n");
+ return ret;
+ }
+
+ for (i = 0; i < count; i++)
+ data[i] = be16_to_cpu(bdata[i]);
+
+ return 0;
+}
+
+static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &st->d16[0],
+ .len = 2,
+ .cs_change = 0,
+ }, {
+ .rx_buf = &st->d16[1],
+ .len = 2,
+ },
+ };
+ int ret;
+
+ st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8);
+
+ ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ return be16_to_cpu(st->d16[1]);
+}
+
+static int ad7606_spi_reg_write(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned int val)
+{
+ struct spi_device *spi = to_spi_device(st->dev);
+
+ st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) |
+ (val & 0x1FF));
+
+ return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
+}
+
+static int ad7606_spi_write_mask(struct ad7606_state *st,
+ unsigned int addr,
+ unsigned long mask,
+ unsigned int val)
+{
+ int readval;
+
+ readval = st->bops->reg_read(st, addr);
+ if (readval < 0)
+ return readval;
+
+ readval &= ~mask;
+ readval |= val;
+
+ return st->bops->reg_write(st, addr, readval);
+}
+
+static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned int ch_addr, mode, ch_index;
+
+
+ /*
+ * Ad7616 has 16 channels divided in group A and group B.
+ * The range of channels from A are stored in registers with address 4
+ * while channels from B are stored in register with address 6.
+ * The last bit from channels determines if it is from group A or B
+ * because the order of channels in iio is 0A, 0B, 1A, 1B...
+ */
+ ch_index = ch >> 1;
+
+ ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
+
+ if ((ch & 0x1) == 0) /* channel A */
+ ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
+ else /* channel B */
+ ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
+
+ /* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
+ mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
+ return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
+ mode);
+}
+
+static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
+ AD7616_OS_MASK, val << 2);
+}
+
+static int ad7606_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_spi_write_mask(st,
+ AD7606_RANGE_CH_ADDR(ch),
+ AD7606_RANGE_CH_MSK(ch),
+ AD7606_RANGE_CH_MODE(ch, val));
+}
+
+static int ad7606_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ return ad7606_spi_reg_write(st, AD7606_OS_MODE, val);
+}
+
+static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+
+ /*
+ * Scale can be configured individually for each channel
+ * in software mode.
+ */
+ indio_dev->channels = ad7616_sw_channels;
+
+ st->write_scale = ad7616_write_scale_sw;
+ st->write_os = &ad7616_write_os_sw;
+
+ /* Activate Burst mode and SEQEN MODE */
+ return st->bops->write_mask(st,
+ AD7616_CONFIGURATION_REGISTER,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE,
+ AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+}
+
+static int ad7606B_sw_mode_config(struct iio_dev *indio_dev)
+{
+ struct ad7606_state *st = iio_priv(indio_dev);
+ unsigned long os[3] = {1};
+
+ /*
+ * Software mode is enabled when all three oversampling
+ * pins are set to high. If oversampling gpios are defined
+ * in the device tree, then they need to be set to high,
+ * otherwise, they must be hardwired to VDD
+ */
+ if (st->gpio_os) {
+ gpiod_set_array_value(ARRAY_SIZE(os),
+ st->gpio_os->desc, st->gpio_os->info, os);
+ }
+ /* OS of 128 and 256 are available only in software mode */
+ st->oversampling_avail = ad7606B_oversampling_avail;
+ st->num_os_ratios = ARRAY_SIZE(ad7606B_oversampling_avail);
+
+ st->write_scale = ad7606_write_scale_sw;
+ st->write_os = &ad7606_write_os_sw;
+
+ /* Configure device spi to output on a single channel */
+ st->bops->reg_write(st,
+ AD7606_CONFIGURATION_REGISTER,
+ AD7606_SINGLE_DOUT);
+
+ /*
+ * Scale can be configured individually for each channel
+ * in software mode.
+ */
+ indio_dev->channels = ad7606b_sw_channels;
+
+ return 0;
+}
+
+static const struct ad7606_bus_ops ad7606_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+};
+
+static const struct ad7606_bus_ops ad7616_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+ .reg_read = ad7606_spi_reg_read,
+ .reg_write = ad7606_spi_reg_write,
+ .write_mask = ad7606_spi_write_mask,
+ .rd_wr_cmd = ad7616_spi_rd_wr_cmd,
+ .sw_mode_config = ad7616_sw_mode_config,
+};
+
+static const struct ad7606_bus_ops ad7606B_spi_bops = {
+ .read_block = ad7606_spi_read_block,
+ .reg_read = ad7606_spi_reg_read,
+ .reg_write = ad7606_spi_reg_write,
+ .write_mask = ad7606_spi_write_mask,
+ .rd_wr_cmd = ad7606B_spi_rd_wr_cmd,
+ .sw_mode_config = ad7606B_sw_mode_config,
+};
+
+static int ad7606_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ const struct ad7606_bus_ops *bops;
+
+ switch (id->driver_data) {
+ case ID_AD7616:
+ bops = &ad7616_spi_bops;
+ break;
+ case ID_AD7606B:
+ bops = &ad7606B_spi_bops;
+ break;
+ default:
+ bops = &ad7606_spi_bops;
+ break;
+ }
+
+ return ad7606_probe(&spi->dev, spi->irq, NULL,
+ id->name, id->driver_data,
+ bops);
+}
+
+static const struct spi_device_id ad7606_id_table[] = {
+ { "ad7605-4", ID_AD7605_4 },
+ { "ad7606-4", ID_AD7606_4 },
+ { "ad7606-6", ID_AD7606_6 },
+ { "ad7606-8", ID_AD7606_8 },
+ { "ad7606b", ID_AD7606B },
+ { "ad7616", ID_AD7616 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7606_id_table);
+
+static const struct of_device_id ad7606_of_match[] = {
+ { .compatible = "adi,ad7605-4" },
+ { .compatible = "adi,ad7606-4" },
+ { .compatible = "adi,ad7606-6" },
+ { .compatible = "adi,ad7606-8" },
+ { .compatible = "adi,ad7606b" },
+ { .compatible = "adi,ad7616" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7606_of_match);
+
+static struct spi_driver ad7606_driver = {
+ .driver = {
+ .name = "ad7606",
+ .of_match_table = ad7606_of_match,
+ .pm = AD7606_PM_OPS,
+ },
+ .probe = ad7606_spi_probe,
+ .id_table = ad7606_id_table,
+};
+module_spi_driver(ad7606_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7606 ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7766.c b/drivers/iio/adc/ad7766.c
index 3ae14fc..bc388ea 100644
--- a/drivers/iio/adc/ad7766.c
+++ b/drivers/iio/adc/ad7766.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD7766/AD7767 SPI ADC driver
*
* Copyright 2016 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/clk.h>
diff --git a/drivers/iio/adc/ad7768-1.c b/drivers/iio/adc/ad7768-1.c
new file mode 100644
index 0000000..0d13270
--- /dev/null
+++ b/drivers/iio/adc/ad7768-1.c
@@ -0,0 +1,655 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices AD7768-1 SPI ADC driver
+ *
+ * Copyright 2017 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/gpio/consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.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>
+
+/* AD7768 registers definition */
+#define AD7768_REG_CHIP_TYPE 0x3
+#define AD7768_REG_PROD_ID_L 0x4
+#define AD7768_REG_PROD_ID_H 0x5
+#define AD7768_REG_CHIP_GRADE 0x6
+#define AD7768_REG_SCRATCH_PAD 0x0A
+#define AD7768_REG_VENDOR_L 0x0C
+#define AD7768_REG_VENDOR_H 0x0D
+#define AD7768_REG_INTERFACE_FORMAT 0x14
+#define AD7768_REG_POWER_CLOCK 0x15
+#define AD7768_REG_ANALOG 0x16
+#define AD7768_REG_ANALOG2 0x17
+#define AD7768_REG_CONVERSION 0x18
+#define AD7768_REG_DIGITAL_FILTER 0x19
+#define AD7768_REG_SINC3_DEC_RATE_MSB 0x1A
+#define AD7768_REG_SINC3_DEC_RATE_LSB 0x1B
+#define AD7768_REG_DUTY_CYCLE_RATIO 0x1C
+#define AD7768_REG_SYNC_RESET 0x1D
+#define AD7768_REG_GPIO_CONTROL 0x1E
+#define AD7768_REG_GPIO_WRITE 0x1F
+#define AD7768_REG_GPIO_READ 0x20
+#define AD7768_REG_OFFSET_HI 0x21
+#define AD7768_REG_OFFSET_MID 0x22
+#define AD7768_REG_OFFSET_LO 0x23
+#define AD7768_REG_GAIN_HI 0x24
+#define AD7768_REG_GAIN_MID 0x25
+#define AD7768_REG_GAIN_LO 0x26
+#define AD7768_REG_SPI_DIAG_ENABLE 0x28
+#define AD7768_REG_ADC_DIAG_ENABLE 0x29
+#define AD7768_REG_DIG_DIAG_ENABLE 0x2A
+#define AD7768_REG_ADC_DATA 0x2C
+#define AD7768_REG_MASTER_STATUS 0x2D
+#define AD7768_REG_SPI_DIAG_STATUS 0x2E
+#define AD7768_REG_ADC_DIAG_STATUS 0x2F
+#define AD7768_REG_DIG_DIAG_STATUS 0x30
+#define AD7768_REG_MCLK_COUNTER 0x31
+
+/* AD7768_REG_POWER_CLOCK */
+#define AD7768_PWR_MCLK_DIV_MSK GENMASK(5, 4)
+#define AD7768_PWR_MCLK_DIV(x) FIELD_PREP(AD7768_PWR_MCLK_DIV_MSK, x)
+#define AD7768_PWR_PWRMODE_MSK GENMASK(1, 0)
+#define AD7768_PWR_PWRMODE(x) FIELD_PREP(AD7768_PWR_PWRMODE_MSK, x)
+
+/* AD7768_REG_DIGITAL_FILTER */
+#define AD7768_DIG_FIL_FIL_MSK GENMASK(6, 4)
+#define AD7768_DIG_FIL_FIL(x) FIELD_PREP(AD7768_DIG_FIL_FIL_MSK, x)
+#define AD7768_DIG_FIL_DEC_MSK GENMASK(2, 0)
+#define AD7768_DIG_FIL_DEC_RATE(x) FIELD_PREP(AD7768_DIG_FIL_DEC_MSK, x)
+
+/* AD7768_REG_CONVERSION */
+#define AD7768_CONV_MODE_MSK GENMASK(2, 0)
+#define AD7768_CONV_MODE(x) FIELD_PREP(AD7768_CONV_MODE_MSK, x)
+
+#define AD7768_RD_FLAG_MSK(x) (BIT(6) | ((x) & 0x3F))
+#define AD7768_WR_FLAG_MSK(x) ((x) & 0x3F)
+
+enum ad7768_conv_mode {
+ AD7768_CONTINUOUS,
+ AD7768_ONE_SHOT,
+ AD7768_SINGLE,
+ AD7768_PERIODIC,
+ AD7768_STANDBY
+};
+
+enum ad7768_pwrmode {
+ AD7768_ECO_MODE = 0,
+ AD7768_MED_MODE = 2,
+ AD7768_FAST_MODE = 3
+};
+
+enum ad7768_mclk_div {
+ AD7768_MCLK_DIV_16,
+ AD7768_MCLK_DIV_8,
+ AD7768_MCLK_DIV_4,
+ AD7768_MCLK_DIV_2
+};
+
+enum ad7768_dec_rate {
+ AD7768_DEC_RATE_32 = 0,
+ AD7768_DEC_RATE_64 = 1,
+ AD7768_DEC_RATE_128 = 2,
+ AD7768_DEC_RATE_256 = 3,
+ AD7768_DEC_RATE_512 = 4,
+ AD7768_DEC_RATE_1024 = 5,
+ AD7768_DEC_RATE_8 = 9,
+ AD7768_DEC_RATE_16 = 10
+};
+
+struct ad7768_clk_configuration {
+ enum ad7768_mclk_div mclk_div;
+ enum ad7768_dec_rate dec_rate;
+ unsigned int clk_div;
+ enum ad7768_pwrmode pwrmode;
+};
+
+static const struct ad7768_clk_configuration ad7768_clk_config[] = {
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_8, 16, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_16, 32, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_32, 64, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_64, 128, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_2, AD7768_DEC_RATE_128, 256, AD7768_FAST_MODE },
+ { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_128, 512, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_256, 1024, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_512, 2048, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_4, AD7768_DEC_RATE_1024, 4096, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_8, AD7768_DEC_RATE_1024, 8192, AD7768_MED_MODE },
+ { AD7768_MCLK_DIV_16, AD7768_DEC_RATE_1024, 16384, AD7768_ECO_MODE },
+};
+
+static const struct iio_chan_spec ad7768_channels[] = {
+ {
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .indexed = 1,
+ .channel = 0,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+ },
+};
+
+struct ad7768_state {
+ struct spi_device *spi;
+ struct regulator *vref;
+ struct mutex lock;
+ struct clk *mclk;
+ unsigned int mclk_freq;
+ unsigned int samp_freq;
+ struct completion completion;
+ struct iio_trigger *trig;
+ struct gpio_desc *gpio_sync_in;
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ __be32 d32;
+ u8 d8[2];
+ } data ____cacheline_aligned;
+};
+
+static int ad7768_spi_reg_read(struct ad7768_state *st, unsigned int addr,
+ unsigned int len)
+{
+ unsigned int shift;
+ int ret;
+
+ shift = 32 - (8 * len);
+ st->data.d8[0] = AD7768_RD_FLAG_MSK(addr);
+
+ ret = spi_write_then_read(st->spi, st->data.d8, 1,
+ &st->data.d32, len);
+ if (ret < 0)
+ return ret;
+
+ return (be32_to_cpu(st->data.d32) >> shift);
+}
+
+static int ad7768_spi_reg_write(struct ad7768_state *st,
+ unsigned int addr,
+ unsigned int val)
+{
+ st->data.d8[0] = AD7768_WR_FLAG_MSK(addr);
+ st->data.d8[1] = val & 0xFF;
+
+ return spi_write(st->spi, st->data.d8, 2);
+}
+
+static int ad7768_set_mode(struct ad7768_state *st,
+ enum ad7768_conv_mode mode)
+{
+ int regval;
+
+ regval = ad7768_spi_reg_read(st, AD7768_REG_CONVERSION, 1);
+ if (regval < 0)
+ return regval;
+
+ regval &= ~AD7768_CONV_MODE_MSK;
+ regval |= AD7768_CONV_MODE(mode);
+
+ return ad7768_spi_reg_write(st, AD7768_REG_CONVERSION, regval);
+}
+
+static int ad7768_scan_direct(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int readval, ret;
+
+ reinit_completion(&st->completion);
+
+ ret = ad7768_set_mode(st, AD7768_ONE_SHOT);
+ if (ret < 0)
+ return ret;
+
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(1000));
+ if (!ret)
+ return -ETIMEDOUT;
+
+ readval = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
+ if (readval < 0)
+ return readval;
+ /*
+ * Any SPI configuration of the AD7768-1 can only be
+ * performed in continuous conversion mode.
+ */
+ ret = ad7768_set_mode(st, AD7768_CONTINUOUS);
+ if (ret < 0)
+ return ret;
+
+ return readval;
+}
+
+static int ad7768_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+ if (readval) {
+ ret = ad7768_spi_reg_read(st, reg, 1);
+ if (ret < 0)
+ goto err_unlock;
+ *readval = ret;
+ ret = 0;
+ } else {
+ ret = ad7768_spi_reg_write(st, reg, writeval);
+ }
+err_unlock:
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int ad7768_set_dig_fil(struct ad7768_state *st,
+ enum ad7768_dec_rate dec_rate)
+{
+ unsigned int mode;
+ int ret;
+
+ if (dec_rate == AD7768_DEC_RATE_8 || dec_rate == AD7768_DEC_RATE_16)
+ mode = AD7768_DIG_FIL_FIL(dec_rate);
+ else
+ mode = AD7768_DIG_FIL_DEC_RATE(dec_rate);
+
+ ret = ad7768_spi_reg_write(st, AD7768_REG_DIGITAL_FILTER, mode);
+ if (ret < 0)
+ return ret;
+
+ /* A sync-in pulse is required every time the filter dec rate changes */
+ gpiod_set_value(st->gpio_sync_in, 1);
+ gpiod_set_value(st->gpio_sync_in, 0);
+
+ return 0;
+}
+
+static int ad7768_set_freq(struct ad7768_state *st,
+ unsigned int freq)
+{
+ unsigned int diff_new, diff_old, pwr_mode, i, idx;
+ int res, ret;
+
+ diff_old = U32_MAX;
+ idx = 0;
+
+ res = DIV_ROUND_CLOSEST(st->mclk_freq, freq);
+
+ /* Find the closest match for the desired sampling frequency */
+ for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
+ diff_new = abs(res - ad7768_clk_config[i].clk_div);
+ if (diff_new < diff_old) {
+ diff_old = diff_new;
+ idx = i;
+ }
+ }
+
+ /*
+ * Set both the mclk_div and pwrmode with a single write to the
+ * POWER_CLOCK register
+ */
+ pwr_mode = AD7768_PWR_MCLK_DIV(ad7768_clk_config[idx].mclk_div) |
+ AD7768_PWR_PWRMODE(ad7768_clk_config[idx].pwrmode);
+ ret = ad7768_spi_reg_write(st, AD7768_REG_POWER_CLOCK, pwr_mode);
+ if (ret < 0)
+ return ret;
+
+ ret = ad7768_set_dig_fil(st, ad7768_clk_config[idx].dec_rate);
+ if (ret < 0)
+ return ret;
+
+ st->samp_freq = DIV_ROUND_CLOSEST(st->mclk_freq,
+ ad7768_clk_config[idx].clk_div);
+
+ return 0;
+}
+
+static ssize_t ad7768_sampling_freq_avail(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ad7768_state *st = iio_priv(indio_dev);
+ unsigned int freq;
+ int i, len = 0;
+
+ for (i = 0; i < ARRAY_SIZE(ad7768_clk_config); i++) {
+ freq = DIV_ROUND_CLOSEST(st->mclk_freq,
+ ad7768_clk_config[i].clk_div);
+ len += scnprintf(buf + len, PAGE_SIZE - len, "%d ", freq);
+ }
+
+ buf[len - 1] = '\n';
+
+ return len;
+}
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ad7768_sampling_freq_avail);
+
+static int ad7768_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long info)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int scale_uv, ret;
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
+ ret = ad7768_scan_direct(indio_dev);
+ if (ret >= 0)
+ *val = ret;
+
+ iio_device_release_direct_mode(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ scale_uv = regulator_get_voltage(st->vref);
+ if (scale_uv < 0)
+ return scale_uv;
+
+ *val = (scale_uv * 2) / 1000;
+ *val2 = chan->scan_type.realbits;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->samp_freq;
+
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7768_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long info)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return ad7768_set_freq(st, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static struct attribute *ad7768_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group ad7768_group = {
+ .attrs = ad7768_attributes,
+};
+
+static const struct iio_info ad7768_info = {
+ .attrs = &ad7768_group,
+ .read_raw = &ad7768_read_raw,
+ .write_raw = &ad7768_write_raw,
+ .debugfs_reg_access = &ad7768_reg_access,
+};
+
+static int ad7768_setup(struct ad7768_state *st)
+{
+ int ret;
+
+ /*
+ * Two writes to the SPI_RESET[1:0] bits are required to initiate
+ * a software reset. The bits must first be set to 11, and then
+ * to 10. When the sequence is detected, the reset occurs.
+ * See the datasheet, page 70.
+ */
+ ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x3);
+ if (ret)
+ return ret;
+
+ ret = ad7768_spi_reg_write(st, AD7768_REG_SYNC_RESET, 0x2);
+ if (ret)
+ return ret;
+
+ st->gpio_sync_in = devm_gpiod_get(&st->spi->dev, "adi,sync-in",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->gpio_sync_in))
+ return PTR_ERR(st->gpio_sync_in);
+
+ /* Set the default sampling frequency to 32000 kSPS */
+ return ad7768_set_freq(st, 32000);
+}
+
+static irqreturn_t ad7768_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = spi_read(st->spi, &st->data.d32, 3);
+ if (ret < 0)
+ goto err_unlock;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &st->data.d32,
+ iio_get_time_ns(indio_dev));
+
+ iio_trigger_notify_done(indio_dev->trig);
+err_unlock:
+ mutex_unlock(&st->lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t ad7768_interrupt(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ if (iio_buffer_enabled(indio_dev))
+ iio_trigger_poll(st->trig);
+ else
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+};
+
+static int ad7768_buffer_postenable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+
+ iio_triggered_buffer_postenable(indio_dev);
+ /*
+ * Write a 1 to the LSB of the INTERFACE_FORMAT register to enter
+ * continuous read mode. Subsequent data reads do not require an
+ * initial 8-bit write to query the ADC_DATA register.
+ */
+ return ad7768_spi_reg_write(st, AD7768_REG_INTERFACE_FORMAT, 0x01);
+}
+
+static int ad7768_buffer_predisable(struct iio_dev *indio_dev)
+{
+ struct ad7768_state *st = iio_priv(indio_dev);
+ int ret;
+
+ /*
+ * To exit continuous read mode, perform a single read of the ADC_DATA
+ * reg (0x2C), which allows further configuration of the device.
+ */
+ ret = ad7768_spi_reg_read(st, AD7768_REG_ADC_DATA, 3);
+ if (ret < 0)
+ return ret;
+
+ return iio_triggered_buffer_predisable(indio_dev);
+}
+
+static const struct iio_buffer_setup_ops ad7768_buffer_ops = {
+ .postenable = &ad7768_buffer_postenable,
+ .predisable = &ad7768_buffer_predisable,
+};
+
+static const struct iio_trigger_ops ad7768_trigger_ops = {
+ .validate_device = iio_trigger_validate_own_device,
+};
+
+static void ad7768_regulator_disable(void *data)
+{
+ struct ad7768_state *st = data;
+
+ regulator_disable(st->vref);
+}
+
+static void ad7768_clk_disable(void *data)
+{
+ struct ad7768_state *st = data;
+
+ clk_disable_unprepare(st->mclk);
+}
+
+static int ad7768_probe(struct spi_device *spi)
+{
+ struct ad7768_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->spi = spi;
+
+ st->vref = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(st->vref))
+ return PTR_ERR(st->vref);
+
+ ret = regulator_enable(st->vref);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified vref supply\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7768_regulator_disable, st);
+ if (ret)
+ return ret;
+
+ st->mclk = devm_clk_get(&spi->dev, "mclk");
+ if (IS_ERR(st->mclk))
+ return PTR_ERR(st->mclk);
+
+ ret = clk_prepare_enable(st->mclk);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, ad7768_clk_disable, st);
+ if (ret)
+ return ret;
+
+ st->mclk_freq = clk_get_rate(st->mclk);
+
+ spi_set_drvdata(spi, indio_dev);
+ mutex_init(&st->lock);
+
+ indio_dev->channels = ad7768_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad7768_channels);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->info = &ad7768_info;
+ indio_dev->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_TRIGGERED;
+
+ ret = ad7768_setup(st);
+ if (ret < 0) {
+ dev_err(&spi->dev, "AD7768 setup failed\n");
+ return ret;
+ }
+
+ st->trig = devm_iio_trigger_alloc(&spi->dev, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+ if (!st->trig)
+ return -ENOMEM;
+
+ st->trig->ops = &ad7768_trigger_ops;
+ st->trig->dev.parent = &spi->dev;
+ iio_trigger_set_drvdata(st->trig, indio_dev);
+ ret = devm_iio_trigger_register(&spi->dev, st->trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(st->trig);
+
+ init_completion(&st->completion);
+
+ ret = devm_request_irq(&spi->dev, spi->irq,
+ &ad7768_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &ad7768_trigger_handler,
+ &ad7768_buffer_ops);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ad7768_id_table[] = {
+ { "ad7768-1", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7768_id_table);
+
+static const struct of_device_id ad7768_of_match[] = {
+ { .compatible = "adi,ad7768-1" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad7768_of_match);
+
+static struct spi_driver ad7768_driver = {
+ .driver = {
+ .name = "ad7768-1",
+ .of_match_table = ad7768_of_match,
+ },
+ .probe = ad7768_probe,
+ .id_table = ad7768_id_table,
+};
+module_spi_driver(ad7768_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7768-1 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7780.c b/drivers/iio/adc/ad7780.c
new file mode 100644
index 0000000..217a5a5
--- /dev/null
+++ b/drivers/iio/adc/ad7780.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * AD7170/AD7171 and AD7780/AD7781 SPI ADC driver
+ *
+ * Copyright 2011 Analog Devices Inc.
+ * Copyright 2019 Renato Lui Geh
+ */
+
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+#include <linux/regulator/consumer.h>
+#include <linux/err.h>
+#include <linux/sched.h>
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <linux/bits.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/adc/ad_sigma_delta.h>
+
+#define AD7780_RDY BIT(7)
+#define AD7780_FILTER BIT(6)
+#define AD7780_ERR BIT(5)
+#define AD7780_ID1 BIT(4)
+#define AD7780_ID0 BIT(3)
+#define AD7780_GAIN BIT(2)
+
+#define AD7170_ID 0
+#define AD7171_ID 1
+#define AD7780_ID 1
+#define AD7781_ID 0
+
+#define AD7780_ID_MASK (AD7780_ID0 | AD7780_ID1)
+
+#define AD7780_PATTERN_GOOD 1
+#define AD7780_PATTERN_MASK GENMASK(1, 0)
+
+#define AD7170_PATTERN_GOOD 5
+#define AD7170_PATTERN_MASK GENMASK(2, 0)
+
+#define AD7780_GAIN_MIDPOINT 64
+#define AD7780_FILTER_MIDPOINT 13350
+
+static const unsigned int ad778x_gain[2] = { 1, 128 };
+static const unsigned int ad778x_odr_avail[2] = { 10000, 16700 };
+
+struct ad7780_chip_info {
+ struct iio_chan_spec channel;
+ unsigned int pattern_mask;
+ unsigned int pattern;
+ bool is_ad778x;
+};
+
+struct ad7780_state {
+ const struct ad7780_chip_info *chip_info;
+ struct regulator *reg;
+ struct gpio_desc *powerdown_gpio;
+ struct gpio_desc *gain_gpio;
+ struct gpio_desc *filter_gpio;
+ unsigned int gain;
+ unsigned int odr;
+ unsigned int int_vref_mv;
+
+ struct ad_sigma_delta sd;
+};
+
+enum ad7780_supported_device_ids {
+ ID_AD7170,
+ ID_AD7171,
+ ID_AD7780,
+ ID_AD7781,
+};
+
+static struct ad7780_state *ad_sigma_delta_to_ad7780(struct ad_sigma_delta *sd)
+{
+ return container_of(sd, struct ad7780_state, sd);
+}
+
+static int ad7780_set_mode(struct ad_sigma_delta *sigma_delta,
+ enum ad_sigma_delta_mode mode)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ unsigned int val;
+
+ switch (mode) {
+ case AD_SD_MODE_SINGLE:
+ case AD_SD_MODE_CONTINUOUS:
+ val = 1;
+ break;
+ default:
+ val = 0;
+ break;
+ }
+
+ gpiod_set_value(st->powerdown_gpio, val);
+
+ return 0;
+}
+
+static int ad7780_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ad7780_state *st = iio_priv(indio_dev);
+ int voltage_uv;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ return ad_sigma_delta_single_conversion(indio_dev, chan, val);
+ case IIO_CHAN_INFO_SCALE:
+ voltage_uv = regulator_get_voltage(st->reg);
+ if (voltage_uv < 0)
+ return voltage_uv;
+ voltage_uv /= 1000;
+ *val = voltage_uv * st->gain;
+ *val2 = chan->scan_type.realbits - 1;
+ st->int_vref_mv = voltage_uv;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -(1 << (chan->scan_type.realbits - 1));
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = st->odr;
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static int ad7780_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long m)
+{
+ struct ad7780_state *st = iio_priv(indio_dev);
+ const struct ad7780_chip_info *chip_info = st->chip_info;
+ unsigned long long vref;
+ unsigned int full_scale, gain;
+
+ if (!chip_info->is_ad778x)
+ return -EINVAL;
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ if (val != 0)
+ return -EINVAL;
+
+ vref = st->int_vref_mv * 1000000LL;
+ full_scale = 1 << (chip_info->channel.scan_type.realbits - 1);
+ gain = DIV_ROUND_CLOSEST_ULL(vref, full_scale);
+ gain = DIV_ROUND_CLOSEST(gain, val2);
+ st->gain = gain;
+ if (gain < AD7780_GAIN_MIDPOINT)
+ gain = 0;
+ else
+ gain = 1;
+ gpiod_set_value(st->gain_gpio, gain);
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (1000*val + val2/1000 < AD7780_FILTER_MIDPOINT)
+ val = 0;
+ else
+ val = 1;
+ st->odr = ad778x_odr_avail[val];
+ gpiod_set_value(st->filter_gpio, val);
+ break;
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int ad7780_postprocess_sample(struct ad_sigma_delta *sigma_delta,
+ unsigned int raw_sample)
+{
+ struct ad7780_state *st = ad_sigma_delta_to_ad7780(sigma_delta);
+ const struct ad7780_chip_info *chip_info = st->chip_info;
+
+ if ((raw_sample & AD7780_ERR) ||
+ ((raw_sample & chip_info->pattern_mask) != chip_info->pattern))
+ return -EIO;
+
+ if (chip_info->is_ad778x) {
+ st->gain = ad778x_gain[raw_sample & AD7780_GAIN];
+ st->odr = ad778x_odr_avail[raw_sample & AD7780_FILTER];
+ }
+
+ return 0;
+}
+
+static const struct ad_sigma_delta_info ad7780_sigma_delta_info = {
+ .set_mode = ad7780_set_mode,
+ .postprocess_sample = ad7780_postprocess_sample,
+ .has_registers = false,
+};
+
+#define AD7780_CHANNEL(bits, wordsize) \
+ AD_SD_CHANNEL(1, 0, 0, bits, 32, (wordsize) - (bits))
+#define AD7170_CHANNEL(bits, wordsize) \
+ AD_SD_CHANNEL_NO_SAMP_FREQ(1, 0, 0, bits, 32, (wordsize) - (bits))
+
+static const struct ad7780_chip_info ad7780_chip_info_tbl[] = {
+ [ID_AD7170] = {
+ .channel = AD7170_CHANNEL(12, 24),
+ .pattern = AD7170_PATTERN_GOOD,
+ .pattern_mask = AD7170_PATTERN_MASK,
+ .is_ad778x = false,
+ },
+ [ID_AD7171] = {
+ .channel = AD7170_CHANNEL(16, 24),
+ .pattern = AD7170_PATTERN_GOOD,
+ .pattern_mask = AD7170_PATTERN_MASK,
+ .is_ad778x = false,
+ },
+ [ID_AD7780] = {
+ .channel = AD7780_CHANNEL(24, 32),
+ .pattern = AD7780_PATTERN_GOOD,
+ .pattern_mask = AD7780_PATTERN_MASK,
+ .is_ad778x = true,
+ },
+ [ID_AD7781] = {
+ .channel = AD7780_CHANNEL(20, 32),
+ .pattern = AD7780_PATTERN_GOOD,
+ .pattern_mask = AD7780_PATTERN_MASK,
+ .is_ad778x = true,
+ },
+};
+
+static const struct iio_info ad7780_info = {
+ .read_raw = ad7780_read_raw,
+ .write_raw = ad7780_write_raw,
+};
+
+static int ad7780_init_gpios(struct device *dev, struct ad7780_state *st)
+{
+ int ret;
+
+ st->powerdown_gpio = devm_gpiod_get_optional(dev,
+ "powerdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(st->powerdown_gpio)) {
+ ret = PTR_ERR(st->powerdown_gpio);
+ dev_err(dev, "Failed to request powerdown GPIO: %d\n", ret);
+ return ret;
+ }
+
+ if (!st->chip_info->is_ad778x)
+ return 0;
+
+
+ st->gain_gpio = devm_gpiod_get_optional(dev,
+ "adi,gain",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gain_gpio)) {
+ ret = PTR_ERR(st->gain_gpio);
+ dev_err(dev, "Failed to request gain GPIO: %d\n", ret);
+ return ret;
+ }
+
+ st->filter_gpio = devm_gpiod_get_optional(dev,
+ "adi,filter",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->filter_gpio)) {
+ ret = PTR_ERR(st->filter_gpio);
+ dev_err(dev, "Failed to request filter GPIO: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ad7780_probe(struct spi_device *spi)
+{
+ struct ad7780_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ st->gain = 1;
+
+ ad_sd_init(&st->sd, indio_dev, spi, &ad7780_sigma_delta_info);
+
+ st->chip_info =
+ &ad7780_chip_info_tbl[spi_get_device_id(spi)->driver_data];
+
+ 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->channels = &st->chip_info->channel;
+ indio_dev->num_channels = 1;
+ indio_dev->info = &ad7780_info;
+
+ ret = ad7780_init_gpios(&spi->dev, st);
+ if (ret)
+ goto error_cleanup_buffer_and_trigger;
+
+ st->reg = devm_regulator_get(&spi->dev, "avdd");
+ if (IS_ERR(st->reg))
+ return PTR_ERR(st->reg);
+
+ ret = regulator_enable(st->reg);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to enable specified AVdd supply\n");
+ return ret;
+ }
+
+ ret = ad_sd_setup_buffer_and_trigger(indio_dev);
+ if (ret)
+ goto error_disable_reg;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer_and_trigger;
+
+ return 0;
+
+error_cleanup_buffer_and_trigger:
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+error_disable_reg:
+ regulator_disable(st->reg);
+
+ return ret;
+}
+
+static int ad7780_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ad7780_state *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ ad_sd_cleanup_buffer_and_trigger(indio_dev);
+
+ regulator_disable(st->reg);
+
+ return 0;
+}
+
+static const struct spi_device_id ad7780_id[] = {
+ {"ad7170", ID_AD7170},
+ {"ad7171", ID_AD7171},
+ {"ad7780", ID_AD7780},
+ {"ad7781", ID_AD7781},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, ad7780_id);
+
+static struct spi_driver ad7780_driver = {
+ .driver = {
+ .name = "ad7780",
+ },
+ .probe = ad7780_probe,
+ .remove = ad7780_remove,
+ .id_table = ad7780_id,
+};
+module_spi_driver(ad7780_driver);
+
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD7780 and similar ADCs");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c
index a9ff069..54025ea 100644
--- a/drivers/iio/adc/ad7791.c
+++ b/drivers/iio/adc/ad7791.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7787/AD7788/AD7789/AD7790/AD7791 SPI ADC driver
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c
index d4bbe5b..bbc41ec 100644
--- a/drivers/iio/adc/ad7793.c
+++ b/drivers/iio/adc/ad7793.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7785/AD7792/AD7793/AD7794/AD7795 SPI ADC driver
*
* Copyright 2011-2012 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
@@ -822,6 +821,6 @@
};
module_spi_driver(ad7793_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7793 and similar ADCs");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7887.c b/drivers/iio/adc/ad7887.c
index 205c0f1..6223043 100644
--- a/drivers/iio/adc/ad7887.c
+++ b/drivers/iio/adc/ad7887.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7887 SPI ADC driver
*
* Copyright 2010-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -362,6 +361,6 @@
};
module_spi_driver(ad7887_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD7887 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7923.c b/drivers/iio/adc/ad7923.c
index ffb7e08..3212eb4 100644
--- a/drivers/iio/adc/ad7923.c
+++ b/drivers/iio/adc/ad7923.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7904/AD7914/AD7923/AD7924 SPI ADC driver
*
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
* Copyright 2012 CS Systemes d'Information
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -24,9 +23,9 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
-#define AD7923_WRITE_CR (1 << 11) /* write control register */
-#define AD7923_RANGE (1 << 1) /* range to REFin */
-#define AD7923_CODING (1 << 0) /* coding is straight binary */
+#define AD7923_WRITE_CR BIT(11) /* write control register */
+#define AD7923_RANGE BIT(1) /* range to REFin */
+#define AD7923_CODING BIT(0) /* coding is straight binary */
#define AD7923_PM_MODE_AS (1) /* auto shutdown */
#define AD7923_PM_MODE_FS (2) /* full shutdown */
#define AD7923_PM_MODE_OPS (3) /* normal operation */
@@ -40,16 +39,16 @@
#define AD7923_MAX_CHAN 4
-#define AD7923_PM_MODE_WRITE(mode) (mode << 4) /* write mode */
-#define AD7923_CHANNEL_WRITE(channel) (channel << 6) /* write channel */
-#define AD7923_SEQUENCE_WRITE(sequence) (((sequence & 1) << 3) \
- + ((sequence & 2) << 9))
+#define AD7923_PM_MODE_WRITE(mode) ((mode) << 4) /* write mode */
+#define AD7923_CHANNEL_WRITE(channel) ((channel) << 6) /* write channel */
+#define AD7923_SEQUENCE_WRITE(sequence) ((((sequence) & 1) << 3) \
+ + (((sequence) & 2) << 9))
/* write sequence fonction */
/* left shift for CR : bit 11 transmit in first */
#define AD7923_SHIFT_REGISTER 4
/* val = value, dec = left shift, bits = number of bits of the mask */
-#define EXTRACT(val, dec, bits) ((val >> dec) & ((1 << bits) - 1))
+#define EXTRACT(val, dec, bits) (((val) >> (dec)) & ((1 << (bits)) - 1))
struct ad7923_state {
struct spi_device *spi;
@@ -130,7 +129,7 @@
* ad7923_update_scan_mode() setup the spi transfer buffer for the new scan mask
**/
static int ad7923_update_scan_mode(struct iio_dev *indio_dev,
- const unsigned long *active_scan_mask)
+ const unsigned long *active_scan_mask)
{
struct ad7923_state *st = iio_priv(indio_dev);
int i, cmd, len;
@@ -181,7 +180,7 @@
goto done;
iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
- iio_get_time_ns(indio_dev));
+ iio_get_time_ns(indio_dev));
done:
iio_trigger_notify_done(indio_dev->trig);
@@ -272,7 +271,7 @@
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
- if (indio_dev == NULL)
+ if (!indio_dev)
return -ENOMEM;
st = iio_priv(indio_dev);
@@ -314,7 +313,7 @@
return ret;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
- &ad7923_trigger_handler, NULL);
+ &ad7923_trigger_handler, NULL);
if (ret)
goto error_disable_reg;
@@ -363,7 +362,7 @@
};
module_spi_driver(ad7923_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_AUTHOR("Patrick Vasseur <patrick.vasseur@c-s.fr>");
MODULE_DESCRIPTION("Analog Devices AD7904/AD7914/AD7923/AD7924 ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad7949.c b/drivers/iio/adc/ad7949.c
new file mode 100644
index 0000000..ac0ffff
--- /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/ad799x.c b/drivers/iio/adc/ad799x.c
index e1da67d..f658012 100644
--- a/drivers/iio/adc/ad799x.c
+++ b/drivers/iio/adc/ad799x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* iio/adc/ad799x.c
* Copyright (C) 2010-2011 Michael Hennerich, Analog Devices Inc.
@@ -11,15 +12,10 @@
* based on linux/drivers/acron/char/pcf8583.c
* Copyright (C) 2000 Russell King
*
- * 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.
- *
* ad799x.c
*
* Support for ad7991, ad7995, ad7999, ad7992, ad7993, ad7994, ad7997,
* ad7998 and similar chips.
- *
*/
#include <linux/interrupt.h>
@@ -814,10 +810,10 @@
ret = ad799x_write_config(st, st->chip_config->default_config);
if (ret < 0)
- goto error_disable_reg;
+ goto error_disable_vref;
ret = ad799x_read_config(st);
if (ret < 0)
- goto error_disable_reg;
+ goto error_disable_vref;
st->config = ret;
ret = iio_triggered_buffer_setup(indio_dev, NULL,
@@ -892,6 +888,6 @@
};
module_i2c_driver(ad799x_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD799x ADC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c
index fc95107..2640b75 100644
--- a/drivers/iio/adc/ad_sigma_delta.c
+++ b/drivers/iio/adc/ad_sigma_delta.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Support code for Analog Devices Sigma-Delta ADCs
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
@@ -62,7 +61,7 @@
struct spi_transfer t = {
.tx_buf = data,
.len = size + 1,
- .cs_change = sigma_delta->bus_locked,
+ .cs_change = sigma_delta->keep_cs_asserted,
};
struct spi_message m;
int ret;
@@ -121,6 +120,7 @@
if (sigma_delta->info->has_registers) {
data[0] = reg << sigma_delta->info->addr_shift;
data[0] |= sigma_delta->info->read_mask;
+ data[0] |= sigma_delta->comm;
spi_message_add_tail(&t[0], &m);
}
spi_message_add_tail(&t[1], &m);
@@ -217,6 +217,7 @@
spi_bus_lock(sigma_delta->spi->master);
sigma_delta->bus_locked = true;
+ sigma_delta->keep_cs_asserted = true;
reinit_completion(&sigma_delta->completion);
ret = ad_sigma_delta_set_mode(sigma_delta, mode);
@@ -234,9 +235,10 @@
ret = 0;
}
out:
+ sigma_delta->keep_cs_asserted = false;
+ ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
sigma_delta->bus_locked = false;
spi_bus_unlock(sigma_delta->spi->master);
- ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
return ret;
}
@@ -278,6 +280,7 @@
{
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))
@@ -288,6 +291,7 @@
spi_bus_lock(sigma_delta->spi->master);
sigma_delta->bus_locked = true;
+ sigma_delta->keep_cs_asserted = true;
reinit_completion(&sigma_delta->completion);
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE);
@@ -297,15 +301,17 @@
ret = wait_for_completion_interruptible_timeout(
&sigma_delta->completion, HZ);
- sigma_delta->bus_locked = false;
- spi_bus_unlock(sigma_delta->spi->master);
-
if (ret == 0)
ret = -EIO;
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);
@@ -315,7 +321,10 @@
sigma_delta->irq_dis = true;
}
+ sigma_delta->keep_cs_asserted = false;
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
+ sigma_delta->bus_locked = false;
+ spi_bus_unlock(sigma_delta->spi->master);
mutex_unlock(&indio_dev->mlock);
if (ret)
@@ -348,10 +357,12 @@
ret = ad_sigma_delta_set_channel(sigma_delta,
indio_dev->channels[channel].address);
if (ret)
- goto err_predisable;
+ return ret;
spi_bus_lock(sigma_delta->spi->master);
sigma_delta->bus_locked = true;
+ sigma_delta->keep_cs_asserted = true;
+
ret = ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_CONTINUOUS);
if (ret)
goto err_unlock;
@@ -363,7 +374,6 @@
err_unlock:
spi_bus_unlock(sigma_delta->spi->master);
-err_predisable:
return ret;
}
@@ -380,6 +390,7 @@
sigma_delta->irq_dis = true;
}
+ sigma_delta->keep_cs_asserted = false;
ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE);
sigma_delta->bus_locked = false;
@@ -392,6 +403,7 @@
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 +413,23 @@
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/aspeed_adc.c b/drivers/iio/adc/aspeed_adc.c
index 9515ca1..d3fc39d 100644
--- a/drivers/iio/adc/aspeed_adc.c
+++ b/drivers/iio/adc/aspeed_adc.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Aspeed AST2400/2500 ADC
*
* Copyright (C) 2017 Google, Inc.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
*/
#include <linux/clk.h>
diff --git a/drivers/iio/adc/at91-sama5d2_adc.c b/drivers/iio/adc/at91-sama5d2_adc.c
index d5ea84c..a2837a0 100644
--- a/drivers/iio/adc/at91-sama5d2_adc.c
+++ b/drivers/iio/adc/at91-sama5d2_adc.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Atmel ADC driver for SAMA5D2 devices and compatible.
*
* Copyright (C) 2015 Atmel,
* 2015 Ludovic Desroches <ludovic.desroches@atmel.com>
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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/bitops.h>
@@ -1586,8 +1578,7 @@
static ssize_t at91_adc_get_fifo_state(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct iio_dev *indio_dev =
- platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", !!st->dma_st.dma_chan);
@@ -1596,8 +1587,7 @@
static ssize_t at91_adc_get_watermark(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct iio_dev *indio_dev =
- platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
return scnprintf(buf, PAGE_SIZE, "%d\n", st->dma_st.watermark);
@@ -1849,8 +1839,7 @@
static __maybe_unused int at91_adc_suspend(struct device *dev)
{
- struct iio_dev *indio_dev =
- platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
/*
@@ -1870,8 +1859,7 @@
static __maybe_unused int at91_adc_resume(struct device *dev)
{
- struct iio_dev *indio_dev =
- platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(indio_dev);
int ret;
diff --git a/drivers/iio/adc/at91_adc.c b/drivers/iio/adc/at91_adc.c
index 75d2f73..abe9985 100644
--- a/drivers/iio/adc/at91_adc.c
+++ b/drivers/iio/adc/at91_adc.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the ADC present in the Atmel AT91 evaluation boards.
*
* Copyright 2011 Free Electrons
- *
- * Licensed under the GPLv2 or later.
*/
#include <linux/bitmap.h>
@@ -704,23 +703,29 @@
ret = wait_event_interruptible_timeout(st->wq_data_avail,
st->done,
msecs_to_jiffies(1000));
- if (ret == 0)
- ret = -ETIMEDOUT;
- if (ret < 0) {
- mutex_unlock(&st->lock);
- return ret;
- }
- *val = st->last_value;
-
+ /* Disable interrupts, regardless if adc conversion was
+ * successful or not
+ */
at91_adc_writel(st, AT91_ADC_CHDR,
AT91_ADC_CH(chan->channel));
at91_adc_writel(st, AT91_ADC_IDR, BIT(chan->channel));
- st->last_value = 0;
- st->done = false;
+ if (ret > 0) {
+ /* a valid conversion took place */
+ *val = st->last_value;
+ st->last_value = 0;
+ st->done = false;
+ ret = IIO_VAL_INT;
+ } else if (ret == 0) {
+ /* conversion timeout */
+ dev_err(&idev->dev, "ADC Channel %d timeout.\n",
+ chan->channel);
+ ret = -ETIMEDOUT;
+ }
+
mutex_unlock(&st->lock);
- return IIO_VAL_INT;
+ return ret;
case IIO_CHAN_INFO_SCALE:
*val = st->vref_mv;
@@ -1174,10 +1179,8 @@
idev->info = &at91_adc_info;
st->irq = platform_get_irq(pdev, 0);
- if (st->irq < 0) {
- dev_err(&pdev->dev, "No IRQ ID is designated\n");
+ if (st->irq < 0)
return -ENODEV;
- }
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
@@ -1354,7 +1357,7 @@
#ifdef CONFIG_PM_SLEEP
static int at91_adc_suspend(struct device *dev)
{
- struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *idev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(idev);
pinctrl_pm_select_sleep_state(dev);
@@ -1365,7 +1368,7 @@
static int at91_adc_resume(struct device *dev)
{
- struct iio_dev *idev = platform_get_drvdata(to_platform_device(dev));
+ struct iio_dev *idev = dev_get_drvdata(dev);
struct at91_adc_state *st = iio_priv(idev);
clk_prepare_enable(st->clk);
diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c
index 5be7892..8805948 100644
--- a/drivers/iio/adc/axp20x_adc.c
+++ b/drivers/iio/adc/axp20x_adc.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* ADC driver for AXP20X and AXP22X PMICs
*
* Copyright (c) 2016 Free Electrons NextThing Co.
* Quentin Schulz <quentin.schulz@free-electrons.com>
- *
- * 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/completion.h>
diff --git a/drivers/iio/adc/axp288_adc.c b/drivers/iio/adc/axp288_adc.c
index 031d568..8ea2aed 100644
--- a/drivers/iio/adc/axp288_adc.c
+++ b/drivers/iio/adc/axp288_adc.c
@@ -1,21 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* axp288_adc.c - X-Powers AXP288 PMIC ADC Driver
*
* Copyright (C) 2014 Intel Corporation
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- * 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; version 2 of the License.
- *
- * 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/dmi.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
@@ -27,9 +19,23 @@
#include <linux/iio/machine.h>
#include <linux/iio/driver.h>
-#define AXP288_ADC_EN_MASK 0xF1
-#define AXP288_ADC_TS_PIN_GPADC 0xF2
-#define AXP288_ADC_TS_PIN_ON 0xF3
+/*
+ * This mask enables all ADCs except for the battery temp-sensor (TS), that is
+ * left as-is to avoid breaking charging on devices without a temp-sensor.
+ */
+#define AXP288_ADC_EN_MASK 0xF0
+#define AXP288_ADC_TS_ENABLE 0x01
+
+#define AXP288_ADC_TS_BIAS_MASK GENMASK(5, 4)
+#define AXP288_ADC_TS_BIAS_20UA (0 << 4)
+#define AXP288_ADC_TS_BIAS_40UA (1 << 4)
+#define AXP288_ADC_TS_BIAS_60UA (2 << 4)
+#define AXP288_ADC_TS_BIAS_80UA (3 << 4)
+#define AXP288_ADC_TS_CURRENT_ON_OFF_MASK GENMASK(1, 0)
+#define AXP288_ADC_TS_CURRENT_OFF (0 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_WHEN_CHARGING (1 << 0)
+#define AXP288_ADC_TS_CURRENT_ON_ONDEMAND (2 << 0)
+#define AXP288_ADC_TS_CURRENT_ON (3 << 0)
enum axp288_adc_id {
AXP288_ADC_TS,
@@ -44,6 +50,7 @@
struct axp288_adc_info {
int irq;
struct regmap *regmap;
+ bool ts_enabled;
};
static const struct iio_chan_spec axp288_adc_channels[] = {
@@ -115,21 +122,33 @@
return IIO_VAL_INT;
}
-static int axp288_adc_set_ts(struct regmap *regmap, unsigned int mode,
- unsigned long address)
+/*
+ * The current-source used for the battery temp-sensor (TS) is shared
+ * with the GPADC. For proper fuel-gauge and charger operation the TS
+ * current-source needs to be permanently on. But to read the GPADC we
+ * need to temporary switch the TS current-source to ondemand, so that
+ * the GPADC can use it, otherwise we will always read an all 0 value.
+ */
+static int axp288_adc_set_ts(struct axp288_adc_info *info,
+ unsigned int mode, unsigned long address)
{
int ret;
- /* channels other than GPADC do not need to switch TS pin */
+ /* No need to switch the current-source if the TS pin is disabled */
+ if (!info->ts_enabled)
+ return 0;
+
+ /* Channels other than GPADC do not need the current source */
if (address != AXP288_GP_ADC_H)
return 0;
- ret = regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, mode);
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK, mode);
if (ret)
return ret;
/* When switching to the GPADC pin give things some time to settle */
- if (mode == AXP288_ADC_TS_PIN_GPADC)
+ if (mode == AXP288_ADC_TS_CURRENT_ON_ONDEMAND)
usleep_range(6000, 10000);
return 0;
@@ -145,14 +164,14 @@
mutex_lock(&indio_dev->mlock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_GPADC,
+ if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON_ONDEMAND,
chan->address)) {
dev_err(&indio_dev->dev, "GPADC mode\n");
ret = -EINVAL;
break;
}
ret = axp288_adc_read_channel(val, chan->address, info->regmap);
- if (axp288_adc_set_ts(info->regmap, AXP288_ADC_TS_PIN_ON,
+ if (axp288_adc_set_ts(info, AXP288_ADC_TS_CURRENT_ON,
chan->address))
dev_err(&indio_dev->dev, "TS pin restore\n");
break;
@@ -164,13 +183,61 @@
return ret;
}
-static int axp288_adc_set_state(struct regmap *regmap)
-{
- /* ADC should be always enabled for internal FG to function */
- if (regmap_write(regmap, AXP288_ADC_TS_PIN_CTRL, AXP288_ADC_TS_PIN_ON))
- return -EIO;
+/*
+ * We rely on the machine's firmware to correctly setup the TS pin bias current
+ * at boot. This lists systems with broken fw where we need to set it ourselves.
+ */
+static const struct dmi_system_id axp288_adc_ts_bias_override[] = {
+ {
+ /* Lenovo Ideapad 100S (11 inch) */
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo ideapad 100S-11IBY"),
+ },
+ .driver_data = (void *)(uintptr_t)AXP288_ADC_TS_BIAS_80UA,
+ },
+ {}
+};
- return regmap_write(regmap, AXP20X_ADC_EN1, AXP288_ADC_EN_MASK);
+static int axp288_adc_initialize(struct axp288_adc_info *info)
+{
+ const struct dmi_system_id *bias_override;
+ int ret, adc_enable_val;
+
+ bias_override = dmi_first_match(axp288_adc_ts_bias_override);
+ if (bias_override) {
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_BIAS_MASK,
+ (uintptr_t)bias_override->driver_data);
+ if (ret)
+ return ret;
+ }
+
+ /*
+ * Determine if the TS pin is enabled and set the TS current-source
+ * accordingly.
+ */
+ ret = regmap_read(info->regmap, AXP20X_ADC_EN1, &adc_enable_val);
+ if (ret)
+ return ret;
+
+ if (adc_enable_val & AXP288_ADC_TS_ENABLE) {
+ info->ts_enabled = true;
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+ AXP288_ADC_TS_CURRENT_ON);
+ } else {
+ info->ts_enabled = false;
+ ret = regmap_update_bits(info->regmap, AXP288_ADC_TS_PIN_CTRL,
+ AXP288_ADC_TS_CURRENT_ON_OFF_MASK,
+ AXP288_ADC_TS_CURRENT_OFF);
+ }
+ if (ret)
+ return ret;
+
+ /* Turn on the ADC for all channels except TS, leave TS as is */
+ return regmap_update_bits(info->regmap, AXP20X_ADC_EN1,
+ AXP288_ADC_EN_MASK, AXP288_ADC_EN_MASK);
}
static const struct iio_info axp288_adc_iio_info = {
@@ -190,17 +257,15 @@
info = iio_priv(indio_dev);
info->irq = platform_get_irq(pdev, 0);
- if (info->irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (info->irq < 0)
return info->irq;
- }
platform_set_drvdata(pdev, indio_dev);
info->regmap = axp20x->regmap;
/*
* Set ADC to enabled state at all time, including system suspend.
* otherwise internal fuel gauge functionality may be affected.
*/
- ret = axp288_adc_set_state(axp20x->regmap);
+ ret = axp288_adc_initialize(info);
if (ret) {
dev_err(&pdev->dev, "unable to enable ADC device\n");
return ret;
diff --git a/drivers/iio/adc/bcm_iproc_adc.c b/drivers/iio/adc/bcm_iproc_adc.c
index 7af59a4..646ebdc 100644
--- a/drivers/iio/adc/bcm_iproc_adc.c
+++ b/drivers/iio/adc/bcm_iproc_adc.c
@@ -1,17 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2016 Broadcom
- *
- * 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 (the "GPL").
- *
- * 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 version 2 (GPLv2) for more details.
- *
- * You should have received a copy of the GNU General Public License
- * version 2 (GPLv2) along with this source code.
*/
#include <linux/module.h>
@@ -551,11 +540,8 @@
}
adc_priv->irqno = platform_get_irq(pdev, 0);
- if (adc_priv->irqno <= 0) {
- dev_err(&pdev->dev, "platform_get_irq failed\n");
- ret = -ENODEV;
- return ret;
- }
+ if (adc_priv->irqno <= 0)
+ return -ENODEV;
ret = regmap_update_bits(adc_priv->regmap, IPROC_REGCTL2,
IPROC_ADC_AUXIN_SCAN_ENA, 0);
diff --git a/drivers/iio/adc/cc10001_adc.c b/drivers/iio/adc/cc10001_adc.c
index 707d8b2..f93f1d9 100644
--- a/drivers/iio/adc/cc10001_adc.c
+++ b/drivers/iio/adc/cc10001_adc.c
@@ -1,10 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014-2015 Imagination Technologies Ltd.
- *
- * 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/clk.h>
diff --git a/drivers/iio/adc/cpcap-adc.c b/drivers/iio/adc/cpcap-adc.c
index 9ad6042..2d616ca 100644
--- a/drivers/iio/adc/cpcap-adc.c
+++ b/drivers/iio/adc/cpcap-adc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2017 Tony Lindgren <tony@atomide.com>
*
@@ -5,15 +6,6 @@
* earlier driver found in the Motorola Linux kernel:
*
* Copyright (C) 2009-2010 Motorola, Inc.
- *
- * 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.
- *
- * 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/delay.h>
diff --git a/drivers/iio/adc/da9150-gpadc.c b/drivers/iio/adc/da9150-gpadc.c
index 0a5d9ce..ae8bcc3 100644
--- a/drivers/iio/adc/da9150-gpadc.c
+++ b/drivers/iio/adc/da9150-gpadc.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DA9150 GPADC Driver
*
* Copyright (c) 2014 Dialog Semiconductor
*
* Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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.
*/
#include <linux/kernel.h>
@@ -341,10 +337,8 @@
init_completion(&gpadc->complete);
irq = platform_get_irq_byname(pdev, "GPADC");
- if (irq < 0) {
- dev_err(dev, "Failed to get IRQ: %d\n", irq);
+ if (irq < 0)
return irq;
- }
ret = devm_request_threaded_irq(dev, irq, NULL, da9150_gpadc_irq,
IRQF_ONESHOT, "GPADC", gpadc);
diff --git a/drivers/iio/adc/dln2-adc.c b/drivers/iio/adc/dln2-adc.c
index c64c667..5fa78c2 100644
--- a/drivers/iio/adc/dln2-adc.c
+++ b/drivers/iio/adc/dln2-adc.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Diolan DLN-2 USB-ADC adapter
*
* Copyright (c) 2017 Jack Andersen
- *
- * 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, version 2.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/adc/envelope-detector.c b/drivers/iio/adc/envelope-detector.c
index 4ebda8a..28f3d67 100644
--- a/drivers/iio/adc/envelope-detector.c
+++ b/drivers/iio/adc/envelope-detector.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Driver for an envelope detector using a DAC and a comparator
*
* Copyright (C) 2016 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
- *
- * 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.
*/
/*
@@ -360,11 +357,8 @@
}
env->comp_irq = platform_get_irq_byname(pdev, "comp");
- if (env->comp_irq < 0) {
- if (env->comp_irq != -EPROBE_DEFER)
- dev_err(dev, "failed to get compare interrupt\n");
+ if (env->comp_irq < 0)
return env->comp_irq;
- }
ret = devm_request_irq(dev, env->comp_irq, envelope_detector_comp_isr,
0, "envelope-detector", env);
diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c
index 5036c39..5c97e8a 100644
--- a/drivers/iio/adc/ep93xx_adc.c
+++ b/drivers/iio/adc/ep93xx_adc.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for ADC module on the Cirrus Logic EP93xx series of SoCs
*
* Copyright (C) 2015 Alexander Sverdlin
*
- * 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.
- *
* The driver uses polling to get the conversion status. According to EP93xx
* datasheets, reading ADCResult register starts the conversion, but user is also
* responsible for ensuring that delay between adjacent conversion triggers is
diff --git a/drivers/iio/adc/exynos_adc.c b/drivers/iio/adc/exynos_adc.c
index f10443f..42a3ced 100644
--- a/drivers/iio/adc/exynos_adc.c
+++ b/drivers/iio/adc/exynos_adc.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* exynos_adc.c - Support for ADC in EXYNOS SoCs
*
* 8 ~ 10 channel, 10/12-bit ADC
*
* Copyright (C) 2013 Naveen Krishna Chatradhi <ch.naveen@samsung.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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
@@ -115,6 +102,8 @@
#define MAX_ADC_V2_CHANNELS 10
#define MAX_ADC_V1_CHANNELS 8
#define MAX_EXYNOS3250_ADC_CHANNELS 2
+#define MAX_EXYNOS4212_ADC_CHANNELS 4
+#define MAX_S5PV210_ADC_CHANNELS 10
/* Bit definitions common for ADC_V1 and ADC_V2 */
#define ADC_CON_EN_START (1u << 0)
@@ -270,6 +259,19 @@
writel(con1 | ADC_CON_EN_START, ADC_V1_CON(info->regs));
}
+/* Exynos4212 and 4412 is like ADCv1 but with four channels only */
+static const struct exynos_adc_data exynos4212_adc_data = {
+ .num_channels = MAX_EXYNOS4212_ADC_CHANNELS,
+ .mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
+ .needs_adc_phy = true,
+ .phy_offset = EXYNOS_ADCV1_PHY_OFFSET,
+
+ .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 const struct exynos_adc_data exynos_adc_v1_data = {
.num_channels = MAX_ADC_V1_CHANNELS,
.mask = ADC_DATX_MASK, /* 12 bit ADC resolution */
@@ -282,6 +284,16 @@
.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 +491,12 @@
.compatible = "samsung,s3c6410-adc",
.data = &exynos_adc_s3c64xx_data,
}, {
+ .compatible = "samsung,s5pv210-adc",
+ .data = &exynos_adc_s5pv210_data,
+ }, {
+ .compatible = "samsung,exynos4212-adc",
+ .data = &exynos4212_adc_data,
+ }, {
.compatible = "samsung,exynos-adc-v1",
.data = &exynos_adc_v1_data,
}, {
@@ -787,10 +805,8 @@
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (irq < 0)
return irq;
- }
info->irq = irq;
irq = platform_get_irq(pdev, 1);
@@ -915,7 +931,7 @@
struct iio_dev *indio_dev = platform_get_drvdata(pdev);
struct exynos_adc *info = iio_priv(indio_dev);
- if (IS_REACHABLE(CONFIG_INPUT)) {
+ if (IS_REACHABLE(CONFIG_INPUT) && info->input) {
free_irq(info->tsirq, info);
input_unregister_device(info->input);
}
diff --git a/drivers/iio/adc/fsl-imx25-gcq.c b/drivers/iio/adc/fsl-imx25-gcq.c
index 929c617..fa71489 100644
--- a/drivers/iio/adc/fsl-imx25-gcq.c
+++ b/drivers/iio/adc/fsl-imx25-gcq.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2014-2015 Pengutronix, Markus Pargmann <mpa@pengutronix.de>
*
- * 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.
- *
* This is the driver for the imx25 GCQ (Generic Conversion Queue)
* connected to the imx25 ADC.
*/
@@ -343,7 +340,6 @@
priv->irq = platform_get_irq(pdev, 0);
if (priv->irq <= 0) {
- dev_err(dev, "Failed to get IRQ\n");
ret = priv->irq;
if (!ret)
ret = -ENXIO;
diff --git a/drivers/iio/adc/hi8435.c b/drivers/iio/adc/hi8435.c
index 6f6c9a3..8da45bf 100644
--- a/drivers/iio/adc/hi8435.c
+++ b/drivers/iio/adc/hi8435.c
@@ -1,13 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Holt Integrated Circuits HI-8435 threshold detector driver
*
* Copyright (C) 2015 Zodiac Inflight Innovations
* Copyright (C) 2015 Cogent Embedded, Inc.
- *
- * 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.
*/
#include <linux/delay.h>
@@ -460,6 +456,11 @@
return IRQ_HANDLED;
}
+static void hi8435_triggered_event_cleanup(void *data)
+{
+ iio_triggered_event_cleanup(data);
+}
+
static int hi8435_probe(struct spi_device *spi)
{
struct iio_dev *idev;
@@ -481,7 +482,7 @@
hi8435_writeb(priv, HI8435_CTRL_REG, 0);
} else {
udelay(5);
- gpiod_set_value(reset_gpio, 1);
+ gpiod_set_value_cansleep(reset_gpio, 1);
}
spi_set_drvdata(spi, idev);
@@ -517,27 +518,13 @@
if (ret)
return ret;
- ret = iio_device_register(idev);
- if (ret < 0) {
- dev_err(&spi->dev, "unable to register device\n");
- goto unregister_triggered_event;
- }
+ ret = devm_add_action_or_reset(&spi->dev,
+ hi8435_triggered_event_cleanup,
+ idev);
+ if (ret)
+ return ret;
- return 0;
-
-unregister_triggered_event:
- iio_triggered_event_cleanup(idev);
- return ret;
-}
-
-static int hi8435_remove(struct spi_device *spi)
-{
- struct iio_dev *idev = spi_get_drvdata(spi);
-
- iio_device_unregister(idev);
- iio_triggered_event_cleanup(idev);
-
- return 0;
+ return devm_iio_device_register(&spi->dev, idev);
}
static const struct of_device_id hi8435_dt_ids[] = {
@@ -558,7 +545,6 @@
.of_match_table = of_match_ptr(hi8435_dt_ids),
},
.probe = hi8435_probe,
- .remove = hi8435_remove,
.id_table = hi8435_id,
};
module_spi_driver(hi8435_driver);
diff --git a/drivers/iio/adc/hx711.c b/drivers/iio/adc/hx711.c
index 36b59d8..62e6c8b 100644
--- a/drivers/iio/adc/hx711.c
+++ b/drivers/iio/adc/hx711.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* HX711: analog to digital converter for weight sensor module
*
* Copyright (c) 2016 Andreas Klinger <ak@it-klinger.de>
- *
- * 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/err.h>
#include <linux/kernel.h>
@@ -109,14 +100,14 @@
static int hx711_cycle(struct hx711_data *hx711_data)
{
- int val;
+ unsigned long flags;
/*
* if preempted for more then 60us while PD_SCK is high:
* hx711 is going in reset
* ==> measuring is false
*/
- preempt_disable();
+ local_irq_save(flags);
gpiod_set_value(hx711_data->gpiod_pd_sck, 1);
/*
@@ -126,7 +117,6 @@
*/
ndelay(hx711_data->data_ready_delay_ns);
- val = gpiod_get_value(hx711_data->gpiod_dout);
/*
* here we are not waiting for 0.2 us as suggested by the datasheet,
* because the oscilloscope showed in a test scenario
@@ -134,7 +124,7 @@
* and 0.56 us for PD_SCK low on TI Sitara with 800 MHz
*/
gpiod_set_value(hx711_data->gpiod_pd_sck, 0);
- preempt_enable();
+ local_irq_restore(flags);
/*
* make it a square wave for addressing cases with capacitance on
@@ -142,7 +132,8 @@
*/
ndelay(hx711_data->data_ready_delay_ns);
- return val;
+ /* sample as late as possible */
+ return gpiod_get_value(hx711_data->gpiod_dout);
}
static int hx711_read(struct hx711_data *hx711_data)
diff --git a/drivers/iio/adc/imx7d_adc.c b/drivers/iio/adc/imx7d_adc.c
index ad6764f..2a2fbf7 100644
--- a/drivers/iio/adc/imx7d_adc.c
+++ b/drivers/iio/adc/imx7d_adc.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale i.MX7D ADC driver
*
* Copyright (C) 2015 Freescale Semiconductor, Inc.
- *
- * 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.
*/
#include <linux/clk.h>
@@ -82,6 +78,7 @@
#define IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT 0xf0000
#define IMX7D_ADC_TIMEOUT msecs_to_jiffies(100)
+#define IMX7D_ADC_INPUT_CLK 24000000
enum imx7d_adc_clk_pre_div {
IMX7D_ADC_ANALOG_CLK_PRE_DIV_4,
@@ -104,8 +101,6 @@
enum imx7d_adc_average_num avg_num;
u32 core_time_unit; /* impact the sample rate */
-
- bool average_en;
};
struct imx7d_adc {
@@ -183,7 +178,6 @@
info->adc_feature.clk_pre_div = IMX7D_ADC_ANALOG_CLK_PRE_DIV_4;
info->adc_feature.avg_num = IMX7D_ADC_AVERAGE_NUM_32;
info->adc_feature.core_time_unit = 1;
- info->adc_feature.average_en = true;
}
static void imx7d_adc_sample_rate_set(struct imx7d_adc *info)
@@ -244,9 +238,8 @@
/* the channel choose single conversion, and enable average mode */
cfg1 |= (IMX7D_REG_ADC_CH_CFG1_CHANNEL_EN |
- IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE);
- if (info->adc_feature.average_en)
- cfg1 |= IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN;
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_SINGLE |
+ IMX7D_REG_ADC_CH_CFG1_CHANNEL_AVG_EN);
/*
* physical channel 0 chose logical channel A
@@ -276,13 +269,11 @@
static u32 imx7d_adc_get_sample_rate(struct imx7d_adc *info)
{
- /* input clock is always 24MHz */
- u32 input_clk = 24000000;
u32 analogue_core_clk;
u32 core_time_unit = info->adc_feature.core_time_unit;
u32 tmp;
- analogue_core_clk = input_clk / info->pre_div_num;
+ analogue_core_clk = IMX7D_ADC_INPUT_CLK / info->pre_div_num;
tmp = (core_time_unit + 1) * 6;
return analogue_core_clk / tmp;
@@ -388,8 +379,9 @@
* timeout flags.
*/
if (status & IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT) {
- pr_err("%s: ADC got conversion time out interrupt: 0x%08x\n",
- dev_name(info->dev), status);
+ dev_err(info->dev,
+ "ADC got conversion time out interrupt: 0x%08x\n",
+ status);
status &= ~IMX7D_REG_ADC_INT_STATUS_CHANNEL_CONV_TIME_OUT;
writel(status, info->regs + IMX7D_REG_ADC_INT_STATUS);
}
@@ -433,136 +425,7 @@
writel(adc_cfg, info->regs + IMX7D_REG_ADC_ADC_CFG);
}
-static int imx7d_adc_probe(struct platform_device *pdev)
-{
- struct imx7d_adc *info;
- struct iio_dev *indio_dev;
- struct resource *mem;
- int irq;
- int ret;
-
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
- if (!indio_dev) {
- dev_err(&pdev->dev, "Failed allocating iio device\n");
- return -ENOMEM;
- }
-
- info = iio_priv(indio_dev);
- info->dev = &pdev->dev;
-
- mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- info->regs = devm_ioremap_resource(&pdev->dev, mem);
- if (IS_ERR(info->regs)) {
- ret = PTR_ERR(info->regs);
- dev_err(&pdev->dev,
- "Failed to remap adc memory, err = %d\n", ret);
- return ret;
- }
-
- irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "No irq resource?\n");
- return irq;
- }
-
- info->clk = devm_clk_get(&pdev->dev, "adc");
- if (IS_ERR(info->clk)) {
- ret = PTR_ERR(info->clk);
- dev_err(&pdev->dev, "Failed getting clock, err = %d\n", ret);
- return ret;
- }
-
- info->vref = devm_regulator_get(&pdev->dev, "vref");
- if (IS_ERR(info->vref)) {
- ret = PTR_ERR(info->vref);
- dev_err(&pdev->dev,
- "Failed getting reference voltage, err = %d\n", ret);
- return ret;
- }
-
- ret = regulator_enable(info->vref);
- if (ret) {
- dev_err(&pdev->dev,
- "Can't enable adc reference top voltage, err = %d\n",
- ret);
- return ret;
- }
-
- platform_set_drvdata(pdev, indio_dev);
-
- init_completion(&info->completion);
-
- indio_dev->name = dev_name(&pdev->dev);
- indio_dev->dev.parent = &pdev->dev;
- indio_dev->info = &imx7d_adc_iio_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = imx7d_adc_iio_channels;
- indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
-
- ret = clk_prepare_enable(info->clk);
- if (ret) {
- dev_err(&pdev->dev,
- "Could not prepare or enable the clock.\n");
- goto error_adc_clk_enable;
- }
-
- ret = devm_request_irq(info->dev, irq,
- imx7d_adc_isr, 0,
- dev_name(&pdev->dev), info);
- if (ret < 0) {
- dev_err(&pdev->dev, "Failed requesting irq, irq = %d\n", irq);
- goto error_iio_device_register;
- }
-
- imx7d_adc_feature_config(info);
- imx7d_adc_hw_init(info);
-
- ret = iio_device_register(indio_dev);
- if (ret) {
- imx7d_adc_power_down(info);
- dev_err(&pdev->dev, "Couldn't register the device.\n");
- goto error_iio_device_register;
- }
-
- return 0;
-
-error_iio_device_register:
- clk_disable_unprepare(info->clk);
-error_adc_clk_enable:
- regulator_disable(info->vref);
-
- return ret;
-}
-
-static int imx7d_adc_remove(struct platform_device *pdev)
-{
- struct iio_dev *indio_dev = platform_get_drvdata(pdev);
- struct imx7d_adc *info = iio_priv(indio_dev);
-
- iio_device_unregister(indio_dev);
-
- imx7d_adc_power_down(info);
-
- clk_disable_unprepare(info->clk);
- regulator_disable(info->vref);
-
- return 0;
-}
-
-static int __maybe_unused imx7d_adc_suspend(struct device *dev)
-{
- struct iio_dev *indio_dev = dev_get_drvdata(dev);
- struct imx7d_adc *info = iio_priv(indio_dev);
-
- imx7d_adc_power_down(info);
-
- clk_disable_unprepare(info->clk);
- regulator_disable(info->vref);
-
- return 0;
-}
-
-static int __maybe_unused imx7d_adc_resume(struct device *dev)
+static int imx7d_adc_enable(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct imx7d_adc *info = iio_priv(indio_dev);
@@ -589,11 +452,105 @@
return 0;
}
-static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_suspend, imx7d_adc_resume);
+static int imx7d_adc_disable(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct imx7d_adc *info = iio_priv(indio_dev);
+
+ imx7d_adc_power_down(info);
+
+ clk_disable_unprepare(info->clk);
+ regulator_disable(info->vref);
+
+ return 0;
+}
+
+static void __imx7d_adc_disable(void *data)
+{
+ imx7d_adc_disable(data);
+}
+
+static int imx7d_adc_probe(struct platform_device *pdev)
+{
+ struct imx7d_adc *info;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ int irq;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*info));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "Failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ info->dev = dev;
+
+ info->regs = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
+ info->clk = devm_clk_get(dev, "adc");
+ if (IS_ERR(info->clk)) {
+ ret = PTR_ERR(info->clk);
+ dev_err(dev, "Failed getting clock, err = %d\n", ret);
+ return ret;
+ }
+
+ info->vref = devm_regulator_get(dev, "vref");
+ if (IS_ERR(info->vref)) {
+ ret = PTR_ERR(info->vref);
+ dev_err(dev,
+ "Failed getting reference voltage, err = %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ init_completion(&info->completion);
+
+ indio_dev->name = dev_name(dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &imx7d_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = imx7d_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(imx7d_adc_iio_channels);
+
+ ret = devm_request_irq(dev, irq, imx7d_adc_isr, 0, dev_name(dev), info);
+ if (ret < 0) {
+ dev_err(dev, "Failed requesting irq, irq = %d\n", irq);
+ return ret;
+ }
+
+ imx7d_adc_feature_config(info);
+
+ ret = imx7d_adc_enable(&indio_dev->dev);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, __imx7d_adc_disable,
+ &indio_dev->dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(imx7d_adc_pm_ops, imx7d_adc_disable, imx7d_adc_enable);
static struct platform_driver imx7d_adc_driver = {
.probe = imx7d_adc_probe,
- .remove = imx7d_adc_remove,
.driver = {
.name = "imx7d_adc",
.of_match_table = imx7d_adc_match,
diff --git a/drivers/iio/adc/ina2xx-adc.c b/drivers/iio/adc/ina2xx-adc.c
index d123962..bdd7cba 100644
--- a/drivers/iio/adc/ina2xx-adc.c
+++ b/drivers/iio/adc/ina2xx-adc.c
@@ -250,6 +250,7 @@
*val2 = chip->shunt_resistor_uohm;
return IIO_VAL_FRACTIONAL;
}
+ return -EINVAL;
case IIO_CHAN_INFO_HARDWAREGAIN:
switch (chan->address) {
@@ -262,6 +263,7 @@
*val = chip->range_vbus == 32 ? 1 : 2;
return IIO_VAL_INT;
}
+ return -EINVAL;
}
return -EINVAL;
diff --git a/drivers/iio/adc/ingenic-adc.c b/drivers/iio/adc/ingenic-adc.c
new file mode 100644
index 0000000..e234970
--- /dev/null
+++ b/drivers/iio/adc/ingenic-adc.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ADC driver for the Ingenic JZ47xx SoCs
+ * Copyright (c) 2019 Artur Rojek <contact@artur-rojek.eu>
+ *
+ * based on drivers/mfd/jz4740-adc.c
+ */
+
+#include <dt-bindings/iio/adc/ingenic,adc.h>
+#include <linux/clk.h>
+#include <linux/iio/iio.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+
+#define JZ_ADC_REG_ENABLE 0x00
+#define JZ_ADC_REG_CFG 0x04
+#define JZ_ADC_REG_CTRL 0x08
+#define JZ_ADC_REG_STATUS 0x0c
+#define JZ_ADC_REG_ADTCH 0x18
+#define JZ_ADC_REG_ADBDAT 0x1c
+#define JZ_ADC_REG_ADSDAT 0x20
+#define JZ_ADC_REG_ADCLK 0x28
+
+#define JZ_ADC_REG_CFG_BAT_MD BIT(4)
+#define JZ_ADC_REG_ADCLK_CLKDIV_LSB 0
+#define JZ_ADC_REG_ADCLK_CLKDIV10US_LSB 16
+
+#define JZ_ADC_AUX_VREF 3300
+#define JZ_ADC_AUX_VREF_BITS 12
+#define JZ_ADC_BATTERY_LOW_VREF 2500
+#define JZ_ADC_BATTERY_LOW_VREF_BITS 12
+#define JZ4725B_ADC_BATTERY_HIGH_VREF 7500
+#define JZ4725B_ADC_BATTERY_HIGH_VREF_BITS 10
+#define JZ4740_ADC_BATTERY_HIGH_VREF (7500 * 0.986)
+#define JZ4740_ADC_BATTERY_HIGH_VREF_BITS 12
+
+struct ingenic_adc;
+
+struct ingenic_adc_soc_data {
+ unsigned int battery_high_vref;
+ unsigned int battery_high_vref_bits;
+ const int *battery_raw_avail;
+ size_t battery_raw_avail_size;
+ const int *battery_scale_avail;
+ size_t battery_scale_avail_size;
+ int (*init_clk_div)(struct device *dev, struct ingenic_adc *adc);
+};
+
+struct ingenic_adc {
+ void __iomem *base;
+ struct clk *clk;
+ struct mutex lock;
+ const struct ingenic_adc_soc_data *soc_data;
+ bool low_vref_mode;
+};
+
+static void ingenic_adc_set_config(struct ingenic_adc *adc,
+ uint32_t mask,
+ uint32_t val)
+{
+ uint32_t cfg;
+
+ clk_enable(adc->clk);
+ mutex_lock(&adc->lock);
+
+ cfg = readl(adc->base + JZ_ADC_REG_CFG) & ~mask;
+ cfg |= val;
+ writel(cfg, adc->base + JZ_ADC_REG_CFG);
+
+ mutex_unlock(&adc->lock);
+ clk_disable(adc->clk);
+}
+
+static void ingenic_adc_enable(struct ingenic_adc *adc,
+ int engine,
+ bool enabled)
+{
+ u8 val;
+
+ mutex_lock(&adc->lock);
+ val = readb(adc->base + JZ_ADC_REG_ENABLE);
+
+ if (enabled)
+ val |= BIT(engine);
+ else
+ val &= ~BIT(engine);
+
+ writeb(val, adc->base + JZ_ADC_REG_ENABLE);
+ mutex_unlock(&adc->lock);
+}
+
+static int ingenic_adc_capture(struct ingenic_adc *adc,
+ int engine)
+{
+ u8 val;
+ int ret;
+
+ ingenic_adc_enable(adc, engine, true);
+ ret = readb_poll_timeout(adc->base + JZ_ADC_REG_ENABLE, val,
+ !(val & BIT(engine)), 250, 1000);
+ if (ret)
+ ingenic_adc_enable(adc, engine, false);
+
+ return ret;
+}
+
+static int ingenic_adc_write_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long m)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case INGENIC_ADC_BATTERY:
+ if (val > JZ_ADC_BATTERY_LOW_VREF) {
+ ingenic_adc_set_config(adc,
+ JZ_ADC_REG_CFG_BAT_MD,
+ 0);
+ adc->low_vref_mode = false;
+ } else {
+ ingenic_adc_set_config(adc,
+ JZ_ADC_REG_CFG_BAT_MD,
+ JZ_ADC_REG_CFG_BAT_MD);
+ adc->low_vref_mode = true;
+ }
+ return 0;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static const int jz4725b_adc_battery_raw_avail[] = {
+ 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
+};
+
+static const int jz4725b_adc_battery_scale_avail[] = {
+ JZ4725B_ADC_BATTERY_HIGH_VREF, JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
+ JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
+};
+
+static const int jz4740_adc_battery_raw_avail[] = {
+ 0, 1, (1 << JZ_ADC_BATTERY_LOW_VREF_BITS) - 1,
+};
+
+static const int jz4740_adc_battery_scale_avail[] = {
+ JZ4740_ADC_BATTERY_HIGH_VREF, JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
+ JZ_ADC_BATTERY_LOW_VREF, JZ_ADC_BATTERY_LOW_VREF_BITS,
+};
+
+static int jz4725b_adc_init_clk_div(struct device *dev, struct ingenic_adc *adc)
+{
+ struct clk *parent_clk;
+ unsigned long parent_rate, rate;
+ unsigned int div_main, div_10us;
+
+ parent_clk = clk_get_parent(adc->clk);
+ if (!parent_clk) {
+ dev_err(dev, "ADC clock has no parent\n");
+ return -ENODEV;
+ }
+ parent_rate = clk_get_rate(parent_clk);
+
+ /*
+ * The JZ4725B ADC works at 500 kHz to 8 MHz.
+ * We pick the highest rate possible.
+ * In practice we typically get 6 MHz, half of the 12 MHz EXT clock.
+ */
+ div_main = DIV_ROUND_UP(parent_rate, 8000000);
+ div_main = clamp(div_main, 1u, 64u);
+ rate = parent_rate / div_main;
+ if (rate < 500000 || rate > 8000000) {
+ dev_err(dev, "No valid divider for ADC main clock\n");
+ return -EINVAL;
+ }
+
+ /* We also need a divider that produces a 10us clock. */
+ div_10us = DIV_ROUND_UP(rate, 100000);
+
+ writel(((div_10us - 1) << JZ_ADC_REG_ADCLK_CLKDIV10US_LSB) |
+ (div_main - 1) << JZ_ADC_REG_ADCLK_CLKDIV_LSB,
+ adc->base + JZ_ADC_REG_ADCLK);
+
+ return 0;
+}
+
+static const struct ingenic_adc_soc_data jz4725b_adc_soc_data = {
+ .battery_high_vref = JZ4725B_ADC_BATTERY_HIGH_VREF,
+ .battery_high_vref_bits = JZ4725B_ADC_BATTERY_HIGH_VREF_BITS,
+ .battery_raw_avail = jz4725b_adc_battery_raw_avail,
+ .battery_raw_avail_size = ARRAY_SIZE(jz4725b_adc_battery_raw_avail),
+ .battery_scale_avail = jz4725b_adc_battery_scale_avail,
+ .battery_scale_avail_size = ARRAY_SIZE(jz4725b_adc_battery_scale_avail),
+ .init_clk_div = jz4725b_adc_init_clk_div,
+};
+
+static const struct ingenic_adc_soc_data jz4740_adc_soc_data = {
+ .battery_high_vref = JZ4740_ADC_BATTERY_HIGH_VREF,
+ .battery_high_vref_bits = JZ4740_ADC_BATTERY_HIGH_VREF_BITS,
+ .battery_raw_avail = jz4740_adc_battery_raw_avail,
+ .battery_raw_avail_size = ARRAY_SIZE(jz4740_adc_battery_raw_avail),
+ .battery_scale_avail = jz4740_adc_battery_scale_avail,
+ .battery_scale_avail_size = ARRAY_SIZE(jz4740_adc_battery_scale_avail),
+ .init_clk_div = NULL, /* no ADCLK register on JZ4740 */
+};
+
+static int ingenic_adc_read_avail(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type,
+ int *length,
+ long m)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ *type = IIO_VAL_INT;
+ *length = adc->soc_data->battery_raw_avail_size;
+ *vals = adc->soc_data->battery_raw_avail;
+ return IIO_AVAIL_RANGE;
+ case IIO_CHAN_INFO_SCALE:
+ *type = IIO_VAL_FRACTIONAL_LOG2;
+ *length = adc->soc_data->battery_scale_avail_size;
+ *vals = adc->soc_data->battery_scale_avail;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ };
+}
+
+static int ingenic_adc_read_raw(struct iio_dev *iio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long m)
+{
+ struct ingenic_adc *adc = iio_priv(iio_dev);
+ int ret;
+
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ clk_enable(adc->clk);
+ ret = ingenic_adc_capture(adc, chan->channel);
+ if (ret) {
+ clk_disable(adc->clk);
+ return ret;
+ }
+
+ switch (chan->channel) {
+ case INGENIC_ADC_AUX:
+ *val = readw(adc->base + JZ_ADC_REG_ADSDAT);
+ break;
+ case INGENIC_ADC_BATTERY:
+ *val = readw(adc->base + JZ_ADC_REG_ADBDAT);
+ break;
+ }
+
+ clk_disable(adc->clk);
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel) {
+ case INGENIC_ADC_AUX:
+ *val = JZ_ADC_AUX_VREF;
+ *val2 = JZ_ADC_AUX_VREF_BITS;
+ break;
+ case INGENIC_ADC_BATTERY:
+ if (adc->low_vref_mode) {
+ *val = JZ_ADC_BATTERY_LOW_VREF;
+ *val2 = JZ_ADC_BATTERY_LOW_VREF_BITS;
+ } else {
+ *val = adc->soc_data->battery_high_vref;
+ *val2 = adc->soc_data->battery_high_vref_bits;
+ }
+ break;
+ }
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static void ingenic_adc_clk_cleanup(void *data)
+{
+ clk_unprepare(data);
+}
+
+static const struct iio_info ingenic_adc_info = {
+ .write_raw = ingenic_adc_write_raw,
+ .read_raw = ingenic_adc_read_raw,
+ .read_avail = ingenic_adc_read_avail,
+};
+
+static const struct iio_chan_spec ingenic_channels[] = {
+ {
+ .extend_name = "aux",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_AUX,
+ },
+ {
+ .extend_name = "battery",
+ .type = IIO_VOLTAGE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .indexed = 1,
+ .channel = INGENIC_ADC_BATTERY,
+ },
+};
+
+static int ingenic_adc_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *iio_dev;
+ struct ingenic_adc *adc;
+ struct resource *mem_base;
+ const struct ingenic_adc_soc_data *soc_data;
+ int ret;
+
+ soc_data = device_get_match_data(dev);
+ if (!soc_data)
+ return -EINVAL;
+
+ iio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(iio_dev);
+ mutex_init(&adc->lock);
+ adc->soc_data = soc_data;
+
+ mem_base = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ adc->base = devm_ioremap_resource(dev, mem_base);
+ if (IS_ERR(adc->base))
+ return PTR_ERR(adc->base);
+
+ adc->clk = devm_clk_get(dev, "adc");
+ if (IS_ERR(adc->clk)) {
+ dev_err(dev, "Unable to get clock\n");
+ return PTR_ERR(adc->clk);
+ }
+
+ ret = clk_prepare_enable(adc->clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock\n");
+ return ret;
+ }
+
+ /* Set clock dividers. */
+ if (soc_data->init_clk_div) {
+ ret = soc_data->init_clk_div(dev, adc);
+ if (ret) {
+ clk_disable_unprepare(adc->clk);
+ return ret;
+ }
+ }
+
+ /* Put hardware in a known passive state. */
+ writeb(0x00, adc->base + JZ_ADC_REG_ENABLE);
+ writeb(0xff, adc->base + JZ_ADC_REG_CTRL);
+ clk_disable(adc->clk);
+
+ ret = devm_add_action_or_reset(dev, ingenic_adc_clk_cleanup, adc->clk);
+ if (ret) {
+ dev_err(dev, "Unable to add action\n");
+ return ret;
+ }
+
+ iio_dev->dev.parent = dev;
+ iio_dev->name = "jz-adc";
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->channels = ingenic_channels;
+ iio_dev->num_channels = ARRAY_SIZE(ingenic_channels);
+ iio_dev->info = &ingenic_adc_info;
+
+ ret = devm_iio_device_register(dev, iio_dev);
+ if (ret)
+ dev_err(dev, "Unable to register IIO device\n");
+
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id ingenic_adc_of_match[] = {
+ { .compatible = "ingenic,jz4725b-adc", .data = &jz4725b_adc_soc_data, },
+ { .compatible = "ingenic,jz4740-adc", .data = &jz4740_adc_soc_data, },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ingenic_adc_of_match);
+#endif
+
+static struct platform_driver ingenic_adc_driver = {
+ .driver = {
+ .name = "ingenic-adc",
+ .of_match_table = of_match_ptr(ingenic_adc_of_match),
+ },
+ .probe = ingenic_adc_probe,
+};
+module_platform_driver(ingenic_adc_driver);
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/lp8788_adc.c b/drivers/iio/adc/lp8788_adc.c
index 3bc4df9..c1fc1b6 100644
--- a/drivers/iio/adc/lp8788_adc.c
+++ b/drivers/iio/adc/lp8788_adc.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI LP8788 MFD - ADC driver
*
* Copyright 2012 Texas Instruments
*
* Author: Milo(Woogyom) Kim <milo.kim@ti.com>
- *
- * 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>
diff --git a/drivers/iio/adc/lpc18xx_adc.c b/drivers/iio/adc/lpc18xx_adc.c
index 041dc4a..e400a95 100644
--- a/drivers/iio/adc/lpc18xx_adc.c
+++ b/drivers/iio/adc/lpc18xx_adc.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO ADC driver for NXP LPC18xx ADC
*
* Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
*
- * 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.
- *
* UNSUPPORTED hardware features:
* - Hardware triggers
* - Burst mode
diff --git a/drivers/iio/adc/lpc32xx_adc.c b/drivers/iio/adc/lpc32xx_adc.c
index 20b3669..b896f7f 100644
--- a/drivers/iio/adc/lpc32xx_adc.c
+++ b/drivers/iio/adc/lpc32xx_adc.c
@@ -1,39 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0+
/*
* lpc32xx_adc.c - Support for ADC in LPC32XX
*
* 3-channel, 10-bit ADC
*
* Copyright (C) 2011, 2012 Roland Stigge <stigge@antcom.de>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
+#include <linux/clk.h>
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/kernel.h>
-#include <linux/slab.h>
-#include <linux/io.h>
-#include <linux/clk.h>
-#include <linux/err.h>
-#include <linux/completion.h>
-#include <linux/of.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/sysfs.h>
+#include <linux/regulator/consumer.h>
/*
* LPC32XX registers definitions
@@ -65,6 +47,7 @@
void __iomem *adc_base;
struct clk *clk;
struct completion completion;
+ struct regulator *vref;
u32 value;
};
@@ -77,7 +60,9 @@
{
struct lpc32xx_adc_state *st = iio_priv(indio_dev);
int ret;
- if (mask == IIO_CHAN_INFO_RAW) {
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
mutex_lock(&indio_dev->mlock);
ret = clk_prepare_enable(st->clk);
if (ret) {
@@ -97,22 +82,36 @@
mutex_unlock(&indio_dev->mlock);
return IIO_VAL_INT;
- }
- return -EINVAL;
+ case IIO_CHAN_INFO_SCALE:
+ *val = regulator_get_voltage(st->vref) / 1000;
+ *val2 = 10;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
}
static const struct iio_info lpc32xx_adc_iio_info = {
.read_raw = &lpc32xx_read_raw,
};
-#define LPC32XX_ADC_CHANNEL(_index) { \
+#define LPC32XX_ADC_CHANNEL_BASE(_index) \
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _index, \
.info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
.address = LPC32XXAD_IN * _index, \
- .scan_index = _index, \
+ .scan_index = _index,
+
+#define LPC32XX_ADC_CHANNEL(_index) { \
+ LPC32XX_ADC_CHANNEL_BASE(_index) \
+}
+
+#define LPC32XX_ADC_SCALE_CHANNEL(_index) { \
+ LPC32XX_ADC_CHANNEL_BASE(_index) \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) \
}
static const struct iio_chan_spec lpc32xx_adc_iio_channels[] = {
@@ -121,6 +120,12 @@
LPC32XX_ADC_CHANNEL(2),
};
+static const struct iio_chan_spec lpc32xx_adc_iio_scale_channels[] = {
+ LPC32XX_ADC_SCALE_CHANNEL(0),
+ LPC32XX_ADC_SCALE_CHANNEL(1),
+ LPC32XX_ADC_SCALE_CHANNEL(2),
+};
+
static irqreturn_t lpc32xx_adc_isr(int irq, void *dev_id)
{
struct lpc32xx_adc_state *st = dev_id;
@@ -167,10 +172,8 @@
}
irq = platform_get_irq(pdev, 0);
- if (irq <= 0) {
- dev_err(&pdev->dev, "failed getting interrupt resource\n");
+ if (irq <= 0)
return -ENXIO;
- }
retval = devm_request_irq(&pdev->dev, irq, lpc32xx_adc_isr, 0,
LPC32XXAD_NAME, st);
@@ -179,6 +182,15 @@
return retval;
}
+ st->vref = devm_regulator_get(&pdev->dev, "vref");
+ if (IS_ERR(st->vref)) {
+ iodev->channels = lpc32xx_adc_iio_channels;
+ dev_info(&pdev->dev,
+ "Missing vref regulator: No scaling available\n");
+ } else {
+ iodev->channels = lpc32xx_adc_iio_scale_channels;
+ }
+
platform_set_drvdata(pdev, iodev);
init_completion(&st->completion);
@@ -187,7 +199,6 @@
iodev->dev.parent = &pdev->dev;
iodev->info = &lpc32xx_adc_iio_info;
iodev->modes = INDIO_DIRECT_MODE;
- iodev->channels = lpc32xx_adc_iio_channels;
iodev->num_channels = ARRAY_SIZE(lpc32xx_adc_iio_channels);
retval = devm_iio_device_register(&pdev->dev, iodev);
diff --git a/drivers/iio/adc/ltc2471.c b/drivers/iio/adc/ltc2471.c
index b88102b..55fab61 100644
--- a/drivers/iio/adc/ltc2471.c
+++ b/drivers/iio/adc/ltc2471.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for Linear Technology LTC2471 and LTC2473 voltage monitors
* The LTC2473 is identical to the 2471, but reports a differential signal.
*
* Copyright (C) 2017 Topic Embedded Products
* Author: Mike Looijmans <mike.looijmans@topic.nl>
- *
- * License: GPLv2
*/
#include <linux/err.h>
diff --git a/drivers/iio/adc/ltc2485.c b/drivers/iio/adc/ltc2485.c
index b24c140..c418466 100644
--- a/drivers/iio/adc/ltc2485.c
+++ b/drivers/iio/adc/ltc2485.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ltc2485.c - Driver for Linear Technology LTC2485 ADC
*
* Copyright (C) 2016 Alison Schofield <amsfield22@gmail.com>
*
- * 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.
- *
* Datasheet: http://cds.linear.com/docs/en/datasheet/2485fd.pdf
*/
diff --git a/drivers/iio/adc/ltc2497.c b/drivers/iio/adc/ltc2497.c
index f1f7cdf..4704060 100644
--- a/drivers/iio/adc/ltc2497.c
+++ b/drivers/iio/adc/ltc2497.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ltc2497.c - Driver for Analog Devices/Linear Technology LTC2497 ADC
*
* Copyright (C) 2017 Analog Devices Inc.
*
- * Licensed under the GPL-2.
- *
* Datasheet: http://cds.linear.com/docs/en/datasheet/2497fd.pdf
*/
diff --git a/drivers/iio/adc/max1027.c b/drivers/iio/adc/max1027.c
index 311c1a8..2148834 100644
--- a/drivers/iio/adc/max1027.c
+++ b/drivers/iio/adc/max1027.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* iio/adc/max1027.c
* Copyright (C) 2014 Philippe Reynes
@@ -6,10 +7,6 @@
* Copyright 2011 Analog Devices Inc (from AD7923 Driver)
* Copyright 2012 CS Systemes d'Information
*
- * 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.
- *
* max1027.c
*
* Partial support for max1027 and similar chips.
@@ -430,8 +427,9 @@
return -ENOMEM;
}
- ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
- &max1027_trigger_handler, NULL);
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ &iio_pollfunc_store_time,
+ &max1027_trigger_handler, NULL);
if (ret < 0) {
dev_err(&indio_dev->dev, "Failed to setup buffer\n");
return ret;
@@ -442,7 +440,7 @@
if (st->trig == NULL) {
ret = -ENOMEM;
dev_err(&indio_dev->dev, "Failed to allocate iio trigger\n");
- goto fail_trigger_alloc;
+ return ret;
}
st->trig->ops = &max1027_trigger_ops;
@@ -457,7 +455,7 @@
spi->dev.driver->name, st->trig);
if (ret < 0) {
dev_err(&indio_dev->dev, "Failed to allocate IRQ.\n");
- goto fail_dev_register;
+ return ret;
}
/* Disable averaging */
@@ -465,34 +463,10 @@
ret = spi_write(st->spi, &st->reg, 1);
if (ret < 0) {
dev_err(&indio_dev->dev, "Failed to configure averaging register\n");
- goto fail_dev_register;
+ return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&indio_dev->dev, "Failed to register iio device\n");
- goto fail_dev_register;
- }
-
- return 0;
-
-fail_dev_register:
-fail_trigger_alloc:
- iio_triggered_buffer_cleanup(indio_dev);
-
- return ret;
-}
-
-static int max1027_remove(struct spi_device *spi)
-{
- struct iio_dev *indio_dev = spi_get_drvdata(spi);
-
- pr_debug("%s: remove(spi = 0x%p)\n", __func__, spi);
-
- iio_device_unregister(indio_dev);
- iio_triggered_buffer_cleanup(indio_dev);
-
- return 0;
+ return devm_iio_device_register(&spi->dev, indio_dev);
}
static struct spi_driver max1027_driver = {
@@ -501,7 +475,6 @@
.of_match_table = of_match_ptr(max1027_adc_dt_ids),
},
.probe = max1027_probe,
- .remove = max1027_remove,
.id_table = max1027_id,
};
module_spi_driver(max1027_driver);
diff --git a/drivers/iio/adc/max11100.c b/drivers/iio/adc/max11100.c
index af59ab2..3440539 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/max1118.c b/drivers/iio/adc/max1118.c
index 49db9e9..3b6f3b9 100644
--- a/drivers/iio/adc/max1118.c
+++ b/drivers/iio/adc/max1118.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MAX1117/MAX1118/MAX1119 8-bit, dual-channel ADCs driver
*
* Copyright (c) 2017 Akinobu Mita <akinobu.mita@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX1117-MAX1119.pdf
*
* SPI interface connections
diff --git a/drivers/iio/adc/max1363.c b/drivers/iio/adc/max1363.c
index a8d35ae..5c2cc61 100644
--- a/drivers/iio/adc/max1363.c
+++ b/drivers/iio/adc/max1363.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* iio/adc/max1363.c
* Copyright (C) 2008-2010 Jonathan Cameron
@@ -9,10 +10,6 @@
* Copyright (C) 2000 Russell King
*
* Driver for max1363 and similar chips.
- *
- * 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/interrupt.h>
diff --git a/drivers/iio/adc/max9611.c b/drivers/iio/adc/max9611.c
index 0538ff8..da073d7 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.
*/
/*
@@ -86,7 +83,7 @@
#define MAX9611_TEMP_MAX_POS 0x7f80
#define MAX9611_TEMP_MAX_NEG 0xff80
#define MAX9611_TEMP_MIN_NEG 0xd980
-#define MAX9611_TEMP_MASK GENMASK(7, 15)
+#define MAX9611_TEMP_MASK GENMASK(15, 7)
#define MAX9611_TEMP_SHIFT 0x07
#define MAX9611_TEMP_RAW(_r) ((_r) >> MAX9611_TEMP_SHIFT)
#define MAX9611_TEMP_SCALE_NUM 1000000
@@ -289,7 +286,7 @@
return ret;
if (*adc_raw > 0) {
- *csa_gain = gain_selectors[i];
+ *csa_gain = (enum max9611_csa_gain)gain_selectors[i];
return 0;
}
}
@@ -483,7 +480,7 @@
if (ret)
return ret;
- regval = ret & MAX9611_TEMP_MASK;
+ regval &= MAX9611_TEMP_MASK;
if ((regval > MAX9611_TEMP_MAX_POS &&
regval < MAX9611_TEMP_MIN_NEG) ||
diff --git a/drivers/iio/adc/mcp320x.c b/drivers/iio/adc/mcp320x.c
index a04856d..38bf100 100644
--- a/drivers/iio/adc/mcp320x.c
+++ b/drivers/iio/adc/mcp320x.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Oskar Andero <oskar.andero@gmail.com>
* Copyright (C) 2014 Rose Technology
@@ -34,10 +35,6 @@
* http://ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
* http://ww1.microchip.com/downloads/en/DeviceDoc/21700E.pdf mcp3301
* http://ww1.microchip.com/downloads/en/DeviceDoc/21950D.pdf mcp3550/1/3
- *
- * 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/err.h>
diff --git a/drivers/iio/adc/mcp3422.c b/drivers/iio/adc/mcp3422.c
index 819f260..ea24d7c 100644
--- a/drivers/iio/adc/mcp3422.c
+++ b/drivers/iio/adc/mcp3422.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* mcp3422.c - driver for the Microchip mcp3421/2/3/4/5/6/7/8 chip family
*
@@ -10,11 +11,6 @@
*
* This driver exports the value of analog input voltage to sysfs, the
* voltage unit is nV.
- *
- * 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.
*/
#include <linux/err.h>
diff --git a/drivers/iio/adc/mcp3911.c b/drivers/iio/adc/mcp3911.c
new file mode 100644
index 0000000..dd52f08
--- /dev/null
+++ b/drivers/iio/adc/mcp3911.c
@@ -0,0 +1,363 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Microchip MCP3911, Two-channel Analog Front End
+ *
+ * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
+ * Copyright (C) 2018 Kent Gustavsson <kent@minoris.se>
+ */
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define MCP3911_REG_CHANNEL0 0x00
+#define MCP3911_REG_CHANNEL1 0x03
+#define MCP3911_REG_MOD 0x06
+#define MCP3911_REG_PHASE 0x07
+#define MCP3911_REG_GAIN 0x09
+
+#define MCP3911_REG_STATUSCOM 0x0a
+#define MCP3911_STATUSCOM_CH1_24WIDTH BIT(4)
+#define MCP3911_STATUSCOM_CH0_24WIDTH BIT(3)
+#define MCP3911_STATUSCOM_EN_OFFCAL BIT(2)
+#define MCP3911_STATUSCOM_EN_GAINCAL BIT(1)
+
+#define MCP3911_REG_CONFIG 0x0c
+#define MCP3911_CONFIG_CLKEXT BIT(1)
+#define MCP3911_CONFIG_VREFEXT BIT(2)
+
+#define MCP3911_REG_OFFCAL_CH0 0x0e
+#define MCP3911_REG_GAINCAL_CH0 0x11
+#define MCP3911_REG_OFFCAL_CH1 0x14
+#define MCP3911_REG_GAINCAL_CH1 0x17
+#define MCP3911_REG_VREFCAL 0x1a
+
+#define MCP3911_CHANNEL(x) (MCP3911_REG_CHANNEL0 + x * 3)
+#define MCP3911_OFFCAL(x) (MCP3911_REG_OFFCAL_CH0 + x * 6)
+
+/* Internal voltage reference in uV */
+#define MCP3911_INT_VREF_UV 1200000
+
+#define MCP3911_REG_READ(reg, id) ((((reg) << 1) | ((id) << 5) | (1 << 0)) & 0xff)
+#define MCP3911_REG_WRITE(reg, id) ((((reg) << 1) | ((id) << 5) | (0 << 0)) & 0xff)
+
+#define MCP3911_NUM_CHANNELS 2
+
+struct mcp3911 {
+ struct spi_device *spi;
+ struct mutex lock;
+ struct regulator *vref;
+ struct clk *clki;
+ u32 dev_addr;
+};
+
+static int mcp3911_read(struct mcp3911 *adc, u8 reg, u32 *val, u8 len)
+{
+ int ret;
+
+ reg = MCP3911_REG_READ(reg, adc->dev_addr);
+ ret = spi_write_then_read(adc->spi, ®, 1, val, len);
+ if (ret < 0)
+ return ret;
+
+ be32_to_cpus(val);
+ *val >>= ((4 - len) * 8);
+ dev_dbg(&adc->spi->dev, "reading 0x%x from register 0x%x\n", *val,
+ reg >> 1);
+ return ret;
+}
+
+static int mcp3911_write(struct mcp3911 *adc, u8 reg, u32 val, u8 len)
+{
+ dev_dbg(&adc->spi->dev, "writing 0x%x to register 0x%x\n", val, reg);
+
+ val <<= (3 - len) * 8;
+ cpu_to_be32s(&val);
+ val |= MCP3911_REG_WRITE(reg, adc->dev_addr);
+
+ return spi_write(adc->spi, &val, len + 1);
+}
+
+static int mcp3911_update(struct mcp3911 *adc, u8 reg, u32 mask,
+ u32 val, u8 len)
+{
+ u32 tmp;
+ int ret;
+
+ ret = mcp3911_read(adc, reg, &tmp, len);
+ if (ret)
+ return ret;
+
+ val &= mask;
+ val |= tmp & ~mask;
+ return mcp3911_write(adc, reg, val, len);
+}
+
+static int mcp3911_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&adc->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = mcp3911_read(adc,
+ MCP3911_CHANNEL(channel->channel), val, 3);
+ if (ret)
+ goto out;
+
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_OFFSET:
+ ret = mcp3911_read(adc,
+ MCP3911_OFFCAL(channel->channel), val, 3);
+ if (ret)
+ goto out;
+
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ if (adc->vref) {
+ ret = regulator_get_voltage(adc->vref);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "failed to get vref voltage: %d\n",
+ ret);
+ goto out;
+ }
+
+ *val = ret / 1000;
+ } else {
+ *val = MCP3911_INT_VREF_UV;
+ }
+
+ *val2 = 24;
+ ret = IIO_VAL_FRACTIONAL_LOG2;
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+static int mcp3911_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int val,
+ int val2, long mask)
+{
+ struct mcp3911 *adc = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ mutex_lock(&adc->lock);
+ switch (mask) {
+ case IIO_CHAN_INFO_OFFSET:
+ if (val2 != 0) {
+ ret = -EINVAL;
+ goto out;
+ }
+
+ /* Write offset */
+ ret = mcp3911_write(adc, MCP3911_OFFCAL(channel->channel), val,
+ 3);
+ if (ret)
+ goto out;
+
+ /* Enable offset*/
+ ret = mcp3911_update(adc, MCP3911_REG_STATUSCOM,
+ MCP3911_STATUSCOM_EN_OFFCAL,
+ MCP3911_STATUSCOM_EN_OFFCAL, 2);
+ break;
+ }
+
+out:
+ mutex_unlock(&adc->lock);
+ return ret;
+}
+
+#define MCP3911_CHAN(idx) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = idx, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_OFFSET) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec mcp3911_channels[] = {
+ MCP3911_CHAN(0),
+ MCP3911_CHAN(1),
+};
+
+static const struct iio_info mcp3911_info = {
+ .read_raw = mcp3911_read_raw,
+ .write_raw = mcp3911_write_raw,
+};
+
+static int mcp3911_config(struct mcp3911 *adc, struct device_node *of_node)
+{
+ u32 configreg;
+ int ret;
+
+ of_property_read_u32(of_node, "device-addr", &adc->dev_addr);
+ if (adc->dev_addr > 3) {
+ dev_err(&adc->spi->dev,
+ "invalid device address (%i). Must be in range 0-3.\n",
+ adc->dev_addr);
+ return -EINVAL;
+ }
+ dev_dbg(&adc->spi->dev, "use device address %i\n", adc->dev_addr);
+
+ ret = mcp3911_read(adc, MCP3911_REG_CONFIG, &configreg, 2);
+ if (ret)
+ return ret;
+
+ if (adc->vref) {
+ dev_dbg(&adc->spi->dev, "use external voltage reference\n");
+ configreg |= MCP3911_CONFIG_VREFEXT;
+ } else {
+ dev_dbg(&adc->spi->dev,
+ "use internal voltage reference (1.2V)\n");
+ configreg &= ~MCP3911_CONFIG_VREFEXT;
+ }
+
+ if (adc->clki) {
+ dev_dbg(&adc->spi->dev, "use external clock as clocksource\n");
+ configreg |= MCP3911_CONFIG_CLKEXT;
+ } else {
+ dev_dbg(&adc->spi->dev,
+ "use crystal oscillator as clocksource\n");
+ configreg &= ~MCP3911_CONFIG_CLKEXT;
+ }
+
+ return mcp3911_write(adc, MCP3911_REG_CONFIG, configreg, 2);
+}
+
+static int mcp3911_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct mcp3911 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+
+ adc->vref = devm_regulator_get_optional(&adc->spi->dev, "vref");
+ if (IS_ERR(adc->vref)) {
+ if (PTR_ERR(adc->vref) == -ENODEV) {
+ adc->vref = NULL;
+ } else {
+ dev_err(&adc->spi->dev,
+ "failed to get regulator (%ld)\n",
+ PTR_ERR(adc->vref));
+ return PTR_ERR(adc->vref);
+ }
+
+ } else {
+ ret = regulator_enable(adc->vref);
+ if (ret)
+ return ret;
+ }
+
+ adc->clki = devm_clk_get(&adc->spi->dev, NULL);
+ if (IS_ERR(adc->clki)) {
+ if (PTR_ERR(adc->clki) == -ENOENT) {
+ adc->clki = NULL;
+ } else {
+ dev_err(&adc->spi->dev,
+ "failed to get adc clk (%ld)\n",
+ PTR_ERR(adc->clki));
+ ret = PTR_ERR(adc->clki);
+ goto reg_disable;
+ }
+ } else {
+ ret = clk_prepare_enable(adc->clki);
+ if (ret < 0) {
+ dev_err(&adc->spi->dev,
+ "Failed to enable clki: %d\n", ret);
+ goto reg_disable;
+ }
+ }
+
+ ret = mcp3911_config(adc, spi->dev.of_node);
+ if (ret)
+ goto clk_disable;
+
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->dev.of_node = spi->dev.of_node;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &mcp3911_info;
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->channels = mcp3911_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mcp3911_channels);
+
+ mutex_init(&adc->lock);
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto clk_disable;
+
+ return ret;
+
+clk_disable:
+ clk_disable_unprepare(adc->clki);
+reg_disable:
+ if (adc->vref)
+ regulator_disable(adc->vref);
+
+ return ret;
+}
+
+static int mcp3911_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct mcp3911 *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ clk_disable_unprepare(adc->clki);
+ if (adc->vref)
+ regulator_disable(adc->vref);
+
+ return 0;
+}
+
+static const struct of_device_id mcp3911_dt_ids[] = {
+ { .compatible = "microchip,mcp3911" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mcp3911_dt_ids);
+
+static const struct spi_device_id mcp3911_id[] = {
+ { "mcp3911", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mcp3911_id);
+
+static struct spi_driver mcp3911_driver = {
+ .driver = {
+ .name = "mcp3911",
+ .of_match_table = mcp3911_dt_ids,
+ },
+ .probe = mcp3911_probe,
+ .remove = mcp3911_remove,
+ .id_table = mcp3911_id,
+};
+module_spi_driver(mcp3911_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_AUTHOR("Kent Gustavsson <kent@minoris.se>");
+MODULE_DESCRIPTION("Microchip Technology MCP3911");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/men_z188_adc.c b/drivers/iio/adc/men_z188_adc.c
index c802617..3b2fbb7 100644
--- a/drivers/iio/adc/men_z188_adc.c
+++ b/drivers/iio/adc/men_z188_adc.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MEN 16z188 Analog to Digial Converter
*
* Copyright (C) 2014 MEN Mikroelektronik GmbH (www.men.de)
* Author: Johannes Thumshirn <johannes.thumshirn@men.de>
- *
- * 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; version 2 of the License.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/adc/meson_saradc.c b/drivers/iio/adc/meson_saradc.c
index da2d16d..7b27306 100644
--- a/drivers/iio/adc/meson_saradc.c
+++ b/drivers/iio/adc/meson_saradc.c
@@ -1,14 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Amlogic Meson Successive Approximation Register (SAR) A/D Converter
*
* Copyright (C) 2017 Martin Blumenstingl <martin.blumenstingl@googlemail.com>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <linux/bitfield.h>
@@ -18,6 +12,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>
@@ -25,6 +20,7 @@
#include <linux/platform_device.h>
#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
+#include <linux/mfd/syscon.h>
#define MESON_SAR_ADC_REG0 0x00
#define MESON_SAR_ADC_REG0_PANEL_DETECT BIT(31)
@@ -148,7 +144,6 @@
#define MESON_SAR_ADC_DELTA_10_TS_REVE1 BIT(26)
#define MESON_SAR_ADC_DELTA_10_CHAN1_DELTA_VALUE_MASK GENMASK(25, 16)
#define MESON_SAR_ADC_DELTA_10_TS_REVE0 BIT(15)
- #define MESON_SAR_ADC_DELTA_10_TS_C_SHIFT 11
#define MESON_SAR_ADC_DELTA_10_TS_C_MASK GENMASK(14, 11)
#define MESON_SAR_ADC_DELTA_10_TS_VBG_EN BIT(10)
#define MESON_SAR_ADC_DELTA_10_CHAN0_DELTA_VALUE_MASK GENMASK(9, 0)
@@ -166,6 +161,17 @@
#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)
+
+#define MESON_HHI_DPLL_TOP_0 0x318
+#define MESON_HHI_DPLL_TOP_0_TSC_BIT4 BIT(9)
+
/* for use with IIO_VAL_INT_PLUS_MICRO */
#define MILLION 1000000
@@ -173,18 +179,28 @@
.type = IIO_VOLTAGE, \
.indexed = 1, \
.channel = _chan, \
+ .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 +213,19 @@
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 +254,9 @@
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 {
@@ -235,7 +267,7 @@
struct meson_sar_adc_priv {
struct regmap *regmap;
struct regulator *vref;
- const struct meson_sar_adc_data *data;
+ const struct meson_sar_adc_param *param;
struct clk *clkin;
struct clk *core_clk;
struct clk *adc_sel_clk;
@@ -246,6 +278,10 @@
struct completion done;
int calibbias;
int calibscale;
+ struct regmap *tsc_regmap;
+ bool temperature_sensor_calibrated;
+ u8 temperature_sensor_coefficient;
+ u16 temperature_sensor_adc_val;
};
static const struct regmap_config meson_sar_adc_regmap_config_gxbb = {
@@ -280,7 +316,7 @@
/* use val_calib = scale * val_raw + offset calibration function */
tmp = div_s64((s64)val * priv->calibscale, MILLION) + priv->calibbias;
- return clamp(tmp, 0, (1 << priv->data->param->resolution) - 1);
+ return clamp(tmp, 0, (1 << priv->param->resolution) - 1);
}
static int meson_sar_adc_wait_busy_clear(struct iio_dev *indio_dev)
@@ -324,15 +360,15 @@
regmap_read(priv->regmap, MESON_SAR_ADC_FIFO_RD, ®val);
fifo_chan = FIELD_GET(MESON_SAR_ADC_FIFO_RD_CHAN_ID_MASK, regval);
- if (fifo_chan != chan->channel) {
+ if (fifo_chan != chan->address) {
dev_err(&indio_dev->dev,
- "ADC FIFO entry belongs to channel %d instead of %d\n",
- fifo_chan, chan->channel);
+ "ADC FIFO entry belongs to channel %d instead of %lu\n",
+ fifo_chan, chan->address);
return -EINVAL;
}
fifo_val = FIELD_GET(MESON_SAR_ADC_FIFO_RD_SAMPLE_VALUE_MASK, regval);
- fifo_val &= GENMASK(priv->data->param->resolution - 1, 0);
+ fifo_val &= GENMASK(priv->param->resolution - 1, 0);
*val = meson_sar_adc_calib_val(indio_dev, fifo_val);
return 0;
@@ -344,16 +380,16 @@
enum meson_sar_adc_num_samples samples)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- int val, channel = chan->channel;
+ int val, address = chan->address;
- val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(channel);
+ val = samples << MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_SHIFT(address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
- MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(channel),
+ MESON_SAR_ADC_AVG_CNTL_NUM_SAMPLES_MASK(address),
val);
- val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(channel);
+ val = mode << MESON_SAR_ADC_AVG_CNTL_AVG_MODE_SHIFT(address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_AVG_CNTL,
- MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(channel), val);
+ MESON_SAR_ADC_AVG_CNTL_AVG_MODE_MASK(address), val);
}
static void meson_sar_adc_enable_channel(struct iio_dev *indio_dev,
@@ -373,25 +409,32 @@
/* map channel index 0 to the channel which we want to read */
regval = FIELD_PREP(MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0),
- chan->channel);
+ chan->address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_CHAN_LIST,
MESON_SAR_ADC_CHAN_LIST_ENTRY_MASK(0), regval);
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
- chan->channel);
+ chan->address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
MESON_SAR_ADC_DETECT_IDLE_SW_DETECT_MUX_MASK,
regval);
regval = FIELD_PREP(MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
- chan->channel);
+ chan->address);
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DETECT_IDLE_SW,
MESON_SAR_ADC_DETECT_IDLE_SW_IDLE_MUX_SEL_MASK,
regval);
- if (chan->channel == 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,
@@ -451,7 +494,7 @@
mutex_lock(&indio_dev->mlock);
- if (priv->data->param->has_bl30_integration) {
+ if (priv->param->has_bl30_integration) {
/* prevent BL30 from using the SAR ADC while we are using it */
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
MESON_SAR_ADC_DELAY_KERNEL_BUSY,
@@ -479,7 +522,7 @@
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- if (priv->data->param->has_bl30_integration)
+ if (priv->param->has_bl30_integration)
/* allow BL30 to use the SAR ADC again */
regmap_update_bits(priv->regmap, MESON_SAR_ADC_DELAY,
MESON_SAR_ADC_DELAY_KERNEL_BUSY, 0);
@@ -506,8 +549,12 @@
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;
@@ -527,8 +574,8 @@
if (ret) {
dev_warn(indio_dev->dev.parent,
- "failed to read sample for channel %d: %d\n",
- chan->channel, ret);
+ "failed to read sample for channel %lu: %d\n",
+ chan->address, ret);
return ret;
}
@@ -555,16 +602,30 @@
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->data->param->resolution;
- return IIO_VAL_FRACTIONAL_LOG2;
+ *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;
+ }
case IIO_CHAN_INFO_CALIBBIAS:
*val = priv->calibbias;
@@ -575,6 +636,13 @@
*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 +655,11 @@
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 +677,11 @@
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 +699,74 @@
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;
+ }
+
+ priv->tsc_regmap =
+ syscon_regmap_lookup_by_phandle(indio_dev->dev.parent->of_node,
+ "amlogic,hhi-sysctrl");
+ if (IS_ERR(priv->tsc_regmap)) {
+ dev_err(indio_dev->dev.parent,
+ "failed to get amlogic,hhi-sysctrl regmap\n");
+ return PTR_ERR(priv->tsc_regmap);
+ }
+
+ 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);
@@ -636,7 +778,7 @@
*/
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_CH7_INPUT);
- if (priv->data->param->has_bl30_integration) {
+ if (priv->param->has_bl30_integration) {
/*
* leave sampling delay and the input clocks as configured by
* BL30 to make sure BL30 gets the values it expects when
@@ -649,10 +791,12 @@
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 +853,45 @@
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);
+
+ if (priv->param->temperature_trimming_bits == 5) {
+ if (priv->temperature_sensor_coefficient & BIT(4))
+ regval = MESON_HHI_DPLL_TOP_0_TSC_BIT4;
+ else
+ regval = 0;
+
+ /*
+ * bit [4] (the 5th bit when starting to count at 1)
+ * of the TSC is located in the HHI register area.
+ */
+ regmap_update_bits(priv->tsc_regmap,
+ MESON_HHI_DPLL_TOP_0,
+ MESON_HHI_DPLL_TOP_0_TSC_BIT4,
+ 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,
@@ -716,7 +899,7 @@
return ret;
}
- ret = clk_set_rate(priv->adc_clk, priv->data->param->clock_rate);
+ ret = clk_set_rate(priv->adc_clk, priv->param->clock_rate);
if (ret) {
dev_err(indio_dev->dev.parent,
"failed to set adc clock rate\n");
@@ -729,7 +912,7 @@
static void meson_sar_adc_set_bandgap(struct iio_dev *indio_dev, bool on_off)
{
struct meson_sar_adc_priv *priv = iio_priv(indio_dev);
- const struct meson_sar_adc_param *param = priv->data->param;
+ const struct meson_sar_adc_param *param = priv->param;
u32 enable_mask;
if (param->bandgap_reg == MESON_SAR_ADC_REG11)
@@ -849,13 +1032,13 @@
int ret, nominal0, nominal1, value0, value1;
/* use points 25% and 75% for calibration */
- nominal0 = (1 << priv->data->param->resolution) / 4;
- nominal1 = (1 << priv->data->param->resolution) * 3 / 4;
+ nominal0 = (1 << priv->param->resolution) / 4;
+ nominal1 = (1 << priv->param->resolution) * 3 / 4;
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
- &meson_sar_adc_iio_channels[7],
+ &indio_dev->channels[7],
MEAN_AVERAGING, EIGHT_SAMPLES, &value0);
if (ret < 0)
goto out;
@@ -863,7 +1046,7 @@
meson_sar_adc_set_chan7_mux(indio_dev, CHAN7_MUX_VDD_MUL3_DIV4);
usleep_range(10, 20);
ret = meson_sar_adc_get_sample(indio_dev,
- &meson_sar_adc_iio_channels[7],
+ &indio_dev->channels[7],
MEAN_AVERAGING, EIGHT_SAMPLES, &value1);
if (ret < 0)
goto out;
@@ -894,6 +1077,20 @@
.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,
+ .temperature_trimming_bits = 5,
+ .temperature_multiplier = 10,
+ .temperature_divider = 32,
};
static const struct meson_sar_adc_param meson_sar_adc_gxbb_param = {
@@ -918,12 +1115,12 @@
};
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",
};
@@ -947,6 +1144,11 @@
.name = "meson-axg-saradc",
};
+static const struct meson_sar_adc_data meson_sar_adc_g12a_data = {
+ .param = &meson_sar_adc_gxl_param,
+ .name = "meson-g12a-saradc",
+};
+
static const struct of_device_id meson_sar_adc_of_match[] = {
{
.compatible = "amlogic,meson8-saradc",
@@ -972,6 +1174,9 @@
}, {
.compatible = "amlogic,meson-axg-saradc",
.data = &meson_sar_adc_axg_data,
+ }, {
+ .compatible = "amlogic,meson-g12a-saradc",
+ .data = &meson_sar_adc_g12a_data,
},
{},
};
@@ -979,11 +1184,11 @@
static int meson_sar_adc_probe(struct platform_device *pdev)
{
+ const struct meson_sar_adc_data *match_data;
struct meson_sar_adc_priv *priv;
struct iio_dev *indio_dev;
struct resource *res;
void __iomem *base;
- const struct of_device_id *match;
int irq, ret;
indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
@@ -995,28 +1200,30 @@
priv = iio_priv(indio_dev);
init_completion(&priv->done);
- match = of_match_device(meson_sar_adc_of_match, &pdev->dev);
- if (!match) {
- dev_err(&pdev->dev, "failed to match device\n");
+ match_data = of_device_get_match_data(&pdev->dev);
+ if (!match_data) {
+ dev_err(&pdev->dev, "failed to get match data\n");
return -ENODEV;
}
- priv->data = match->data;
+ priv->param = match_data->param;
- indio_dev->name = priv->data->name;
+ indio_dev->name = match_data->name;
indio_dev->dev.parent = &pdev->dev;
indio_dev->dev.of_node = pdev->dev.of_node;
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))
return PTR_ERR(base);
+ priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ priv->param->regmap_config);
+ if (IS_ERR(priv->regmap))
+ return PTR_ERR(priv->regmap);
+
irq = irq_of_parse_and_map(pdev->dev.of_node, 0);
if (!irq)
return -EINVAL;
@@ -1026,11 +1233,6 @@
if (ret)
return ret;
- priv->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- priv->data->param->regmap_config);
- if (IS_ERR(priv->regmap))
- return PTR_ERR(priv->regmap);
-
priv->clkin = devm_clk_get(&pdev->dev, "clkin");
if (IS_ERR(priv->clkin)) {
dev_err(&pdev->dev, "failed to get clkin\n");
@@ -1078,6 +1280,22 @@
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/mt6577_auxadc.c b/drivers/iio/adc/mt6577_auxadc.c
index 95d76ab..7bbb64c 100644
--- a/drivers/iio/adc/mt6577_auxadc.c
+++ b/drivers/iio/adc/mt6577_auxadc.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016 MediaTek Inc.
* Author: Zhiyong Tao <zhiyong.tao@mediatek.com>
- *
- * 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.
- *
- * 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/clk.h>
@@ -42,10 +34,26 @@
#define MT6577_AUXADC_POWER_READY_MS 1
#define MT6577_AUXADC_SAMPLE_READY_US 25
+struct mtk_auxadc_compatible {
+ bool sample_data_cali;
+ bool check_global_idle;
+};
+
struct mt6577_auxadc_device {
void __iomem *reg_base;
struct clk *adc_clk;
struct mutex lock;
+ const struct mtk_auxadc_compatible *dev_comp;
+};
+
+static const struct mtk_auxadc_compatible mt8173_compat = {
+ .sample_data_cali = false,
+ .check_global_idle = true,
+};
+
+static const struct mtk_auxadc_compatible mt6765_compat = {
+ .sample_data_cali = true,
+ .check_global_idle = false,
};
#define MT6577_AUXADC_CHANNEL(idx) { \
@@ -74,6 +82,11 @@
MT6577_AUXADC_CHANNEL(15),
};
+static int mt_auxadc_get_cali_data(int rawdata, bool enable_cali)
+{
+ return rawdata;
+}
+
static inline void mt6577_auxadc_mod_reg(void __iomem *reg,
u32 or_mask, u32 and_mask)
{
@@ -120,15 +133,17 @@
/* we must delay here for hardware sample channel data */
udelay(MT6577_AUXADC_SAMPLE_READY_US);
- /* check MTK_AUXADC_CON2 if auxadc is idle */
- ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2, val,
- ((val & MT6577_AUXADC_STA) == 0),
- MT6577_AUXADC_SLEEP_US,
- MT6577_AUXADC_TIMEOUT_US);
- if (ret < 0) {
- dev_err(indio_dev->dev.parent,
- "wait for auxadc idle time out\n");
- goto err_timeout;
+ if (adc_dev->dev_comp->check_global_idle) {
+ /* check MTK_AUXADC_CON2 if auxadc is idle */
+ ret = readl_poll_timeout(adc_dev->reg_base + MT6577_AUXADC_CON2,
+ val, ((val & MT6577_AUXADC_STA) == 0),
+ MT6577_AUXADC_SLEEP_US,
+ MT6577_AUXADC_TIMEOUT_US);
+ if (ret < 0) {
+ dev_err(indio_dev->dev.parent,
+ "wait for auxadc idle time out\n");
+ goto err_timeout;
+ }
}
/* read channel and make sure ready bit == 1 */
@@ -163,6 +178,8 @@
int *val2,
long info)
{
+ struct mt6577_auxadc_device *adc_dev = iio_priv(indio_dev);
+
switch (info) {
case IIO_CHAN_INFO_PROCESSED:
*val = mt6577_auxadc_read(indio_dev, chan);
@@ -172,6 +189,8 @@
chan->channel);
return *val;
}
+ if (adc_dev->dev_comp->sample_data_cali)
+ *val = mt_auxadc_get_cali_data(*val, true);
return IIO_VAL_INT;
default:
@@ -304,10 +323,11 @@
mt6577_auxadc_resume);
static const struct of_device_id mt6577_auxadc_of_match[] = {
- { .compatible = "mediatek,mt2701-auxadc", },
- { .compatible = "mediatek,mt2712-auxadc", },
- { .compatible = "mediatek,mt7622-auxadc", },
- { .compatible = "mediatek,mt8173-auxadc", },
+ { .compatible = "mediatek,mt2701-auxadc", .data = &mt8173_compat},
+ { .compatible = "mediatek,mt2712-auxadc", .data = &mt8173_compat},
+ { .compatible = "mediatek,mt7622-auxadc", .data = &mt8173_compat},
+ { .compatible = "mediatek,mt8173-auxadc", .data = &mt8173_compat},
+ { .compatible = "mediatek,mt6765-auxadc", .data = &mt6765_compat},
{ }
};
MODULE_DEVICE_TABLE(of, mt6577_auxadc_of_match);
diff --git a/drivers/iio/adc/mxs-lradc-adc.c b/drivers/iio/adc/mxs-lradc-adc.c
index c627513..9d2f74c 100644
--- a/drivers/iio/adc/mxs-lradc-adc.c
+++ b/drivers/iio/adc/mxs-lradc-adc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale MXS LRADC ADC driver
*
@@ -7,16 +8,6 @@
* Authors:
* Marek Vasut <marex@denx.de>
* Ksenija Stanojevic <ksenija.stanojevic@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/completion.h>
@@ -465,6 +456,8 @@
trig = devm_iio_trigger_alloc(&iio->dev, "%s-dev%i", iio->name,
iio->id);
+ if (!trig)
+ return -ENOMEM;
trig->dev.parent = adc->dev;
iio_trigger_set_drvdata(trig, iio);
diff --git a/drivers/iio/adc/nau7802.c b/drivers/iio/adc/nau7802.c
index 8997e74..5725791 100644
--- a/drivers/iio/adc/nau7802.c
+++ b/drivers/iio/adc/nau7802.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Driver for the Nuvoton NAU7802 ADC
*
* Copyright 2013 Free Electrons
- *
- * Licensed under the GPLv2 or later.
*/
#include <linux/delay.h>
diff --git a/drivers/iio/adc/npcm_adc.c b/drivers/iio/adc/npcm_adc.c
new file mode 100644
index 0000000..910f358
--- /dev/null
+++ b/drivers/iio/adc/npcm_adc.c
@@ -0,0 +1,334 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) 2019 Nuvoton Technology corporation.
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/mfd/syscon.h>
+#include <linux/io.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+
+struct npcm_adc {
+ bool int_status;
+ u32 adc_sample_hz;
+ struct device *dev;
+ void __iomem *regs;
+ struct clk *adc_clk;
+ wait_queue_head_t wq;
+ struct regulator *vref;
+ struct regmap *rst_regmap;
+};
+
+/* NPCM7xx reset module */
+#define NPCM7XX_IPSRST1_OFFSET 0x020
+#define NPCM7XX_IPSRST1_ADC_RST BIT(27)
+
+/* ADC registers */
+#define NPCM_ADCCON 0x00
+#define NPCM_ADCDATA 0x04
+
+/* ADCCON Register Bits */
+#define NPCM_ADCCON_ADC_INT_EN BIT(21)
+#define NPCM_ADCCON_REFSEL BIT(19)
+#define NPCM_ADCCON_ADC_INT_ST BIT(18)
+#define NPCM_ADCCON_ADC_EN BIT(17)
+#define NPCM_ADCCON_ADC_RST BIT(16)
+#define NPCM_ADCCON_ADC_CONV BIT(13)
+
+#define NPCM_ADCCON_CH_MASK GENMASK(27, 24)
+#define NPCM_ADCCON_CH(x) ((x) << 24)
+#define NPCM_ADCCON_DIV_SHIFT 1
+#define NPCM_ADCCON_DIV_MASK GENMASK(8, 1)
+#define NPCM_ADC_DATA_MASK(x) ((x) & GENMASK(9, 0))
+
+#define NPCM_ADC_ENABLE (NPCM_ADCCON_ADC_EN | NPCM_ADCCON_ADC_INT_EN)
+
+/* ADC General Definition */
+#define NPCM_RESOLUTION_BITS 10
+#define NPCM_INT_VREF_MV 2000
+
+#define NPCM_ADC_CHAN(ch) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = ch, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+}
+
+static const struct iio_chan_spec npcm_adc_iio_channels[] = {
+ NPCM_ADC_CHAN(0),
+ NPCM_ADC_CHAN(1),
+ NPCM_ADC_CHAN(2),
+ NPCM_ADC_CHAN(3),
+ NPCM_ADC_CHAN(4),
+ NPCM_ADC_CHAN(5),
+ NPCM_ADC_CHAN(6),
+ NPCM_ADC_CHAN(7),
+};
+
+static irqreturn_t npcm_adc_isr(int irq, void *data)
+{
+ u32 regtemp;
+ struct iio_dev *indio_dev = data;
+ struct npcm_adc *info = iio_priv(indio_dev);
+
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ if (regtemp & NPCM_ADCCON_ADC_INT_ST) {
+ iowrite32(regtemp, info->regs + NPCM_ADCCON);
+ wake_up_interruptible(&info->wq);
+ info->int_status = true;
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int npcm_adc_read(struct npcm_adc *info, int *val, u8 channel)
+{
+ int ret;
+ u32 regtemp;
+
+ /* Select ADC channel */
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ regtemp &= ~NPCM_ADCCON_CH_MASK;
+ info->int_status = false;
+ iowrite32(regtemp | NPCM_ADCCON_CH(channel) |
+ NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+ ret = wait_event_interruptible_timeout(info->wq, info->int_status,
+ msecs_to_jiffies(10));
+ if (ret == 0) {
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ if ((regtemp & NPCM_ADCCON_ADC_CONV) && info->rst_regmap) {
+ /* if conversion failed - reset ADC module */
+ regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
+ NPCM7XX_IPSRST1_ADC_RST);
+ msleep(100);
+ regmap_write(info->rst_regmap, NPCM7XX_IPSRST1_OFFSET,
+ 0x0);
+ msleep(100);
+
+ /* Enable ADC and start conversion module */
+ iowrite32(NPCM_ADC_ENABLE | NPCM_ADCCON_ADC_CONV,
+ info->regs + NPCM_ADCCON);
+ dev_err(info->dev, "RESET ADC Complete\n");
+ }
+ return -ETIMEDOUT;
+ }
+ if (ret < 0)
+ return ret;
+
+ *val = NPCM_ADC_DATA_MASK(ioread32(info->regs + NPCM_ADCDATA));
+
+ return 0;
+}
+
+static int npcm_adc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret;
+ int vref_uv;
+ struct npcm_adc *info = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = npcm_adc_read(info, val, chan->channel);
+ mutex_unlock(&indio_dev->mlock);
+ if (ret) {
+ dev_err(info->dev, "NPCM ADC read failed\n");
+ return ret;
+ }
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ if (!IS_ERR(info->vref)) {
+ vref_uv = regulator_get_voltage(info->vref);
+ *val = vref_uv / 1000;
+ } else {
+ *val = NPCM_INT_VREF_MV;
+ }
+ *val2 = NPCM_RESOLUTION_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = info->adc_sample_hz;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct iio_info npcm_adc_iio_info = {
+ .read_raw = &npcm_adc_read_raw,
+};
+
+static const struct of_device_id npcm_adc_match[] = {
+ { .compatible = "nuvoton,npcm750-adc", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, npcm_adc_match);
+
+static int npcm_adc_probe(struct platform_device *pdev)
+{
+ int ret;
+ int irq;
+ u32 div;
+ u32 reg_con;
+ struct resource *res;
+ struct npcm_adc *info;
+ struct iio_dev *indio_dev;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = pdev->dev.of_node;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*info));
+ if (!indio_dev)
+ return -ENOMEM;
+ info = iio_priv(indio_dev);
+
+ info->dev = &pdev->dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ info->regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(info->regs))
+ return PTR_ERR(info->regs);
+
+ info->adc_clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(info->adc_clk)) {
+ dev_warn(&pdev->dev, "ADC clock failed: can't read clk\n");
+ return PTR_ERR(info->adc_clk);
+ }
+
+ /* calculate ADC clock sample rate */
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ div = reg_con & NPCM_ADCCON_DIV_MASK;
+ div = div >> NPCM_ADCCON_DIV_SHIFT;
+ info->adc_sample_hz = clk_get_rate(info->adc_clk) / ((div + 1) * 2);
+
+ if (of_device_is_compatible(np, "nuvoton,npcm750-adc")) {
+ info->rst_regmap = syscon_regmap_lookup_by_compatible
+ ("nuvoton,npcm750-rst");
+ if (IS_ERR(info->rst_regmap)) {
+ dev_err(&pdev->dev, "Failed to find nuvoton,npcm750-rst\n");
+ ret = PTR_ERR(info->rst_regmap);
+ goto err_disable_clk;
+ }
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq <= 0) {
+ ret = -EINVAL;
+ goto err_disable_clk;
+ }
+
+ ret = devm_request_irq(&pdev->dev, irq, npcm_adc_isr, 0,
+ "NPCM_ADC", indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "failed requesting interrupt\n");
+ goto err_disable_clk;
+ }
+
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ info->vref = devm_regulator_get_optional(&pdev->dev, "vref");
+ if (!IS_ERR(info->vref)) {
+ ret = regulator_enable(info->vref);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't enable ADC reference voltage\n");
+ goto err_disable_clk;
+ }
+
+ iowrite32(reg_con & ~NPCM_ADCCON_REFSEL,
+ info->regs + NPCM_ADCCON);
+ } else {
+ /*
+ * Any error which is not ENODEV indicates the regulator
+ * has been specified and so is a failure case.
+ */
+ if (PTR_ERR(info->vref) != -ENODEV) {
+ ret = PTR_ERR(info->vref);
+ goto err_disable_clk;
+ }
+
+ /* Use internal reference */
+ iowrite32(reg_con | NPCM_ADCCON_REFSEL,
+ info->regs + NPCM_ADCCON);
+ }
+
+ init_waitqueue_head(&info->wq);
+
+ reg_con = ioread32(info->regs + NPCM_ADCCON);
+ reg_con |= NPCM_ADC_ENABLE;
+
+ /* Enable the ADC Module */
+ iowrite32(reg_con, info->regs + NPCM_ADCCON);
+
+ /* Start ADC conversion */
+ iowrite32(reg_con | NPCM_ADCCON_ADC_CONV, info->regs + NPCM_ADCCON);
+
+ platform_set_drvdata(pdev, indio_dev);
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &npcm_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = npcm_adc_iio_channels;
+ indio_dev->num_channels = ARRAY_SIZE(npcm_adc_iio_channels);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "Couldn't register the device.\n");
+ goto err_iio_register;
+ }
+
+ pr_info("NPCM ADC driver probed\n");
+
+ return 0;
+
+err_iio_register:
+ iowrite32(reg_con & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
+ if (!IS_ERR(info->vref))
+ regulator_disable(info->vref);
+err_disable_clk:
+ clk_disable_unprepare(info->adc_clk);
+
+ return ret;
+}
+
+static int npcm_adc_remove(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct npcm_adc *info = iio_priv(indio_dev);
+ u32 regtemp;
+
+ iio_device_unregister(indio_dev);
+
+ regtemp = ioread32(info->regs + NPCM_ADCCON);
+ iowrite32(regtemp & ~NPCM_ADCCON_ADC_EN, info->regs + NPCM_ADCCON);
+ if (!IS_ERR(info->vref))
+ regulator_disable(info->vref);
+ clk_disable_unprepare(info->adc_clk);
+
+ return 0;
+}
+
+static struct platform_driver npcm_adc_driver = {
+ .probe = npcm_adc_probe,
+ .remove = npcm_adc_remove,
+ .driver = {
+ .name = "npcm_adc",
+ .of_match_table = npcm_adc_match,
+ },
+};
+
+module_platform_driver(npcm_adc_driver);
+
+MODULE_DESCRIPTION("Nuvoton NPCM ADC Driver");
+MODULE_AUTHOR("Tomer Maimon <tomer.maimon@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/palmas_gpadc.c b/drivers/iio/adc/palmas_gpadc.c
index 69b9aff..46e595e 100644
--- a/drivers/iio/adc/palmas_gpadc.c
+++ b/drivers/iio/adc/palmas_gpadc.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* palmas-adc.c -- TI PALMAS GPADC.
*
* Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
*
* Author: Pradeep Goudagunta <pgoudagunta@nvidia.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 version 2.
*/
#include <linux/module.h>
diff --git a/drivers/iio/adc/qcom-pm8xxx-xoadc.c b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
index b093ecd..c599ffa 100644
--- a/drivers/iio/adc/qcom-pm8xxx-xoadc.c
+++ b/drivers/iio/adc/qcom-pm8xxx-xoadc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Qualcomm PM8xxx PMIC XOADC driver
*
@@ -423,18 +424,14 @@
static struct pm8xxx_chan_info *
pm8xxx_get_channel(struct pm8xxx_xoadc *adc, u8 chan)
{
- struct pm8xxx_chan_info *ch;
int i;
for (i = 0; i < adc->nchans; i++) {
- ch = &adc->chans[i];
+ struct pm8xxx_chan_info *ch = &adc->chans[i];
if (ch->hwchan->amux_channel == chan)
- break;
+ return ch;
}
- if (i == adc->nchans)
- return NULL;
-
- return ch;
+ return NULL;
}
static int pm8xxx_read_channel_rsv(struct pm8xxx_xoadc *adc,
@@ -708,8 +705,8 @@
* mux.
*/
if (iiospec->args_count != 2) {
- dev_err(&indio_dev->dev, "wrong number of arguments for %s need 2 got %d\n",
- iiospec->np->name,
+ dev_err(&indio_dev->dev, "wrong number of arguments for %pOFn need 2 got %d\n",
+ iiospec->np,
iiospec->args_count);
return -EINVAL;
}
diff --git a/drivers/iio/adc/qcom-spmi-adc5.c b/drivers/iio/adc/qcom-spmi-adc5.c
new file mode 100644
index 0000000..21fdcde
--- /dev/null
+++ b/drivers/iio/adc/qcom-spmi-adc5.c
@@ -0,0 +1,798 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018, The Linux Foundation. All rights reserved.
+ */
+
+#include <linux/bitops.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/iio/qcom,spmi-vadc.h>
+#include "qcom-vadc-common.h"
+
+#define ADC5_USR_REVISION1 0x0
+#define ADC5_USR_STATUS1 0x8
+#define ADC5_USR_STATUS1_REQ_STS BIT(1)
+#define ADC5_USR_STATUS1_EOC BIT(0)
+#define ADC5_USR_STATUS1_REQ_STS_EOC_MASK 0x3
+
+#define ADC5_USR_STATUS2 0x9
+#define ADC5_USR_STATUS2_CONV_SEQ_MASK 0x70
+#define ADC5_USR_STATUS2_CONV_SEQ_MASK_SHIFT 0x5
+
+#define ADC5_USR_IBAT_MEAS 0xf
+#define ADC5_USR_IBAT_MEAS_SUPPORTED BIT(0)
+
+#define ADC5_USR_DIG_PARAM 0x42
+#define ADC5_USR_DIG_PARAM_CAL_VAL BIT(6)
+#define ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT 6
+#define ADC5_USR_DIG_PARAM_CAL_SEL 0x30
+#define ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT 4
+#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL 0xc
+#define ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT 2
+
+#define ADC5_USR_FAST_AVG_CTL 0x43
+#define ADC5_USR_FAST_AVG_CTL_EN BIT(7)
+#define ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK 0x7
+
+#define ADC5_USR_CH_SEL_CTL 0x44
+
+#define ADC5_USR_DELAY_CTL 0x45
+#define ADC5_USR_HW_SETTLE_DELAY_MASK 0xf
+
+#define ADC5_USR_EN_CTL1 0x46
+#define ADC5_USR_EN_CTL1_ADC_EN BIT(7)
+
+#define ADC5_USR_CONV_REQ 0x47
+#define ADC5_USR_CONV_REQ_REQ BIT(7)
+
+#define ADC5_USR_DATA0 0x50
+
+#define ADC5_USR_DATA1 0x51
+
+#define ADC5_USR_IBAT_DATA0 0x52
+
+#define ADC5_USR_IBAT_DATA1 0x53
+
+/*
+ * Conversion time varies based on the decimation, clock rate, fast average
+ * samples and measurements queued across different VADC peripherals.
+ * Set the timeout to a max of 100ms.
+ */
+#define ADC5_CONV_TIME_MIN_US 263
+#define ADC5_CONV_TIME_MAX_US 264
+#define ADC5_CONV_TIME_RETRY 400
+#define ADC5_CONV_TIMEOUT msecs_to_jiffies(100)
+
+/* Digital version >= 5.3 supports hw_settle_2 */
+#define ADC5_HW_SETTLE_DIFF_MINOR 3
+#define ADC5_HW_SETTLE_DIFF_MAJOR 5
+
+enum adc5_cal_method {
+ ADC5_NO_CAL = 0,
+ ADC5_RATIOMETRIC_CAL,
+ ADC5_ABSOLUTE_CAL
+};
+
+enum adc5_cal_val {
+ ADC5_TIMER_CAL = 0,
+ ADC5_NEW_CAL
+};
+
+/**
+ * struct adc5_channel_prop - ADC channel property.
+ * @channel: channel number, refer to the channel list.
+ * @cal_method: calibration method.
+ * @cal_val: calibration value
+ * @decimation: sampling rate supported for the channel.
+ * @prescale: channel scaling performed on the input signal.
+ * @hw_settle_time: the time between AMUX being configured and the
+ * start of conversion.
+ * @avg_samples: ability to provide single result from the ADC
+ * that is an average of multiple measurements.
+ * @scale_fn_type: Represents the scaling function to convert voltage
+ * physical units desired by the client for the channel.
+ * @datasheet_name: Channel name used in device tree.
+ */
+struct adc5_channel_prop {
+ unsigned int channel;
+ enum adc5_cal_method cal_method;
+ enum adc5_cal_val cal_val;
+ unsigned int decimation;
+ unsigned int prescale;
+ unsigned int hw_settle_time;
+ unsigned int avg_samples;
+ enum vadc_scale_fn_type scale_fn_type;
+ const char *datasheet_name;
+};
+
+/**
+ * struct adc5_chip - ADC private structure.
+ * @regmap: SPMI ADC5 peripheral register map field.
+ * @dev: SPMI ADC5 device.
+ * @base: base address for the ADC peripheral.
+ * @nchannels: number of ADC channels.
+ * @chan_props: array of ADC channel properties.
+ * @iio_chans: array of IIO channels specification.
+ * @poll_eoc: use polling instead of interrupt.
+ * @complete: ADC result notification after interrupt is received.
+ * @lock: ADC lock for access to the peripheral.
+ * @data: software configuration data.
+ */
+struct adc5_chip {
+ struct regmap *regmap;
+ struct device *dev;
+ u16 base;
+ unsigned int nchannels;
+ struct adc5_channel_prop *chan_props;
+ struct iio_chan_spec *iio_chans;
+ bool poll_eoc;
+ struct completion complete;
+ struct mutex lock;
+ const struct adc5_data *data;
+};
+
+static const struct vadc_prescale_ratio adc5_prescale_ratios[] = {
+ {.num = 1, .den = 1},
+ {.num = 1, .den = 3},
+ {.num = 1, .den = 4},
+ {.num = 1, .den = 6},
+ {.num = 1, .den = 20},
+ {.num = 1, .den = 8},
+ {.num = 10, .den = 81},
+ {.num = 1, .den = 10},
+ {.num = 1, .den = 16}
+};
+
+static int adc5_read(struct adc5_chip *adc, u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_read(adc->regmap, adc->base + offset, data, len);
+}
+
+static int adc5_write(struct adc5_chip *adc, u16 offset, u8 *data, int len)
+{
+ return regmap_bulk_write(adc->regmap, adc->base + offset, data, len);
+}
+
+static int adc5_prescaling_from_dt(u32 num, u32 den)
+{
+ unsigned int pre;
+
+ for (pre = 0; pre < ARRAY_SIZE(adc5_prescale_ratios); pre++)
+ if (adc5_prescale_ratios[pre].num == num &&
+ adc5_prescale_ratios[pre].den == den)
+ break;
+
+ if (pre == ARRAY_SIZE(adc5_prescale_ratios))
+ return -EINVAL;
+
+ return pre;
+}
+
+static int adc5_hw_settle_time_from_dt(u32 value,
+ const unsigned int *hw_settle)
+{
+ unsigned int i;
+
+ for (i = 0; i < VADC_HW_SETTLE_SAMPLES_MAX; i++) {
+ if (value == hw_settle[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int adc5_avg_samples_from_dt(u32 value)
+{
+ if (!is_power_of_2(value) || value > ADC5_AVG_SAMPLES_MAX)
+ return -EINVAL;
+
+ return __ffs(value);
+}
+
+static int adc5_decimation_from_dt(u32 value,
+ const unsigned int *decimation)
+{
+ unsigned int i;
+
+ for (i = 0; i < ADC5_DECIMATION_SAMPLES_MAX; i++) {
+ if (value == decimation[i])
+ return i;
+ }
+
+ return -EINVAL;
+}
+
+static int adc5_read_voltage_data(struct adc5_chip *adc, u16 *data)
+{
+ int ret;
+ u8 rslt_lsb, rslt_msb;
+
+ ret = adc5_read(adc, ADC5_USR_DATA0, &rslt_lsb, sizeof(rslt_lsb));
+ if (ret)
+ return ret;
+
+ ret = adc5_read(adc, ADC5_USR_DATA1, &rslt_msb, sizeof(rslt_lsb));
+ if (ret)
+ return ret;
+
+ *data = (rslt_msb << 8) | rslt_lsb;
+
+ if (*data == ADC5_USR_DATA_CHECK) {
+ pr_err("Invalid data:0x%x\n", *data);
+ return -EINVAL;
+ }
+
+ pr_debug("voltage raw code:0x%x\n", *data);
+
+ return 0;
+}
+
+static int adc5_poll_wait_eoc(struct adc5_chip *adc)
+{
+ unsigned int count, retry = ADC5_CONV_TIME_RETRY;
+ u8 status1;
+ int ret;
+
+ for (count = 0; count < retry; count++) {
+ ret = adc5_read(adc, ADC5_USR_STATUS1, &status1,
+ sizeof(status1));
+ if (ret)
+ return ret;
+
+ status1 &= ADC5_USR_STATUS1_REQ_STS_EOC_MASK;
+ if (status1 == ADC5_USR_STATUS1_EOC)
+ return 0;
+
+ usleep_range(ADC5_CONV_TIME_MIN_US, ADC5_CONV_TIME_MAX_US);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void adc5_update_dig_param(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop, u8 *data)
+{
+ /* Update calibration value */
+ *data &= ~ADC5_USR_DIG_PARAM_CAL_VAL;
+ *data |= (prop->cal_val << ADC5_USR_DIG_PARAM_CAL_VAL_SHIFT);
+
+ /* Update calibration select */
+ *data &= ~ADC5_USR_DIG_PARAM_CAL_SEL;
+ *data |= (prop->cal_method << ADC5_USR_DIG_PARAM_CAL_SEL_SHIFT);
+
+ /* Update decimation ratio select */
+ *data &= ~ADC5_USR_DIG_PARAM_DEC_RATIO_SEL;
+ *data |= (prop->decimation << ADC5_USR_DIG_PARAM_DEC_RATIO_SEL_SHIFT);
+}
+
+static int adc5_configure(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop)
+{
+ int ret;
+ u8 buf[6];
+
+ /* Read registers 0x42 through 0x46 */
+ ret = adc5_read(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ /* Digital param selection */
+ adc5_update_dig_param(adc, prop, &buf[0]);
+
+ /* Update fast average sample value */
+ buf[1] &= (u8) ~ADC5_USR_FAST_AVG_CTL_SAMPLES_MASK;
+ buf[1] |= prop->avg_samples;
+
+ /* Select ADC channel */
+ buf[2] = prop->channel;
+
+ /* Select HW settle delay for channel */
+ buf[3] &= (u8) ~ADC5_USR_HW_SETTLE_DELAY_MASK;
+ buf[3] |= prop->hw_settle_time;
+
+ /* Select ADC enable */
+ buf[4] |= ADC5_USR_EN_CTL1_ADC_EN;
+
+ /* Select CONV request */
+ buf[5] |= ADC5_USR_CONV_REQ_REQ;
+
+ if (!adc->poll_eoc)
+ reinit_completion(&adc->complete);
+
+ return adc5_write(adc, ADC5_USR_DIG_PARAM, buf, sizeof(buf));
+}
+
+static int adc5_do_conversion(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct iio_chan_spec const *chan,
+ u16 *data_volt, u16 *data_cur)
+{
+ int ret;
+
+ mutex_lock(&adc->lock);
+
+ ret = adc5_configure(adc, prop);
+ if (ret) {
+ pr_err("ADC configure failed with %d\n", ret);
+ goto unlock;
+ }
+
+ if (adc->poll_eoc) {
+ ret = adc5_poll_wait_eoc(adc);
+ if (ret < 0) {
+ pr_err("EOC bit not set\n");
+ goto unlock;
+ }
+ } else {
+ ret = wait_for_completion_timeout(&adc->complete,
+ ADC5_CONV_TIMEOUT);
+ if (!ret) {
+ pr_debug("Did not get completion timeout.\n");
+ ret = adc5_poll_wait_eoc(adc);
+ if (ret < 0) {
+ pr_err("EOC bit not set\n");
+ goto unlock;
+ }
+ }
+ }
+
+ ret = adc5_read_voltage_data(adc, data_volt);
+unlock:
+ mutex_unlock(&adc->lock);
+
+ return ret;
+}
+
+static irqreturn_t adc5_isr(int irq, void *dev_id)
+{
+ struct adc5_chip *adc = dev_id;
+
+ complete(&adc->complete);
+
+ return IRQ_HANDLED;
+}
+
+static int adc5_of_xlate(struct iio_dev *indio_dev,
+ const struct of_phandle_args *iiospec)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ int i;
+
+ for (i = 0; i < adc->nchannels; i++)
+ if (adc->chan_props[i].channel == iiospec->args[0])
+ return i;
+
+ return -EINVAL;
+}
+
+static int adc5_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2,
+ long mask)
+{
+ struct adc5_chip *adc = iio_priv(indio_dev);
+ struct adc5_channel_prop *prop;
+ u16 adc_code_volt, adc_code_cur;
+ int ret;
+
+ prop = &adc->chan_props[chan->address];
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = adc5_do_conversion(adc, prop, chan,
+ &adc_code_volt, &adc_code_cur);
+ if (ret)
+ return ret;
+
+ ret = qcom_adc5_hw_scale(prop->scale_fn_type,
+ &adc5_prescale_ratios[prop->prescale],
+ adc->data,
+ adc_code_volt, val);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct iio_info adc5_info = {
+ .read_raw = adc5_read_raw,
+ .of_xlate = adc5_of_xlate,
+};
+
+struct adc5_channels {
+ const char *datasheet_name;
+ unsigned int prescale_index;
+ enum iio_chan_type type;
+ long info_mask;
+ 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, \
+ .prescale_index = _pre, \
+ .type = _type, \
+ .info_mask = _mask, \
+ .scale_fn_type = _scale, \
+ }, \
+
+#define ADC5_CHAN_TEMP(_dname, _pre, _scale) \
+ ADC5_CHAN(_dname, IIO_TEMP, \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre, _scale) \
+
+#define ADC5_CHAN_VOLT(_dname, _pre, _scale) \
+ ADC5_CHAN(_dname, IIO_VOLTAGE, \
+ BIT(IIO_CHAN_INFO_PROCESSED), \
+ _pre, _scale) \
+
+static const struct adc5_channels adc5_chans_pmic[ADC5_MAX_CHANNEL] = {
+ [ADC5_REF_GND] = ADC5_CHAN_VOLT("ref_gnd", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
+ SCALE_HW_CALIB_PMIC_THERM)
+ [ADC5_USB_IN_I] = ADC5_CHAN_VOLT("usb_in_i_uv", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [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", 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", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [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", 0,
+ SCALE_HW_CALIB_XOTHERM)
+ [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", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [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", 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", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_1P25VREF] = ADC5_CHAN_VOLT("vref_1p25", 0,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VPH_PWR] = ADC5_CHAN_VOLT("vph_pwr", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VBAT_SNS] = ADC5_CHAN_VOLT("vbat_sns", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_VCOIN] = ADC5_CHAN_VOLT("vcoin", 1,
+ SCALE_HW_CALIB_DEFAULT)
+ [ADC5_DIE_TEMP] = ADC5_CHAN_TEMP("die_temp", 0,
+ SCALE_HW_CALIB_PMIC_THERM)
+ [ADC5_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", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [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", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+ [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", 0,
+ SCALE_HW_CALIB_THERM_100K_PULLUP)
+};
+
+static int adc5_get_dt_channel_data(struct adc5_chip *adc,
+ struct adc5_channel_prop *prop,
+ struct device_node *node,
+ const struct adc5_data *data)
+{
+ const char *name = node->name, *channel_name;
+ u32 chan, value, varr[2];
+ int ret;
+ struct device *dev = adc->dev;
+
+ ret = of_property_read_u32(node, "reg", &chan);
+ if (ret) {
+ dev_err(dev, "invalid channel number %s\n", name);
+ return ret;
+ }
+
+ if (chan > ADC5_PARALLEL_ISENSE_VBAT_IDATA ||
+ !data->adc_chans[chan].datasheet_name) {
+ dev_err(dev, "%s invalid channel number %d\n", name, chan);
+ return -EINVAL;
+ }
+
+ /* the channel has DT description */
+ prop->channel = chan;
+
+ channel_name = of_get_property(node,
+ "label", NULL) ? : node->name;
+ if (!channel_name) {
+ pr_err("Invalid channel name\n");
+ return -EINVAL;
+ }
+ prop->datasheet_name = channel_name;
+
+ ret = of_property_read_u32(node, "qcom,decimation", &value);
+ if (!ret) {
+ ret = adc5_decimation_from_dt(value, data->decimation);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid decimation %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->decimation = ret;
+ } else {
+ prop->decimation = ADC5_DECIMATION_DEFAULT;
+ }
+
+ ret = of_property_read_u32_array(node, "qcom,pre-scaling", varr, 2);
+ if (!ret) {
+ ret = adc5_prescaling_from_dt(varr[0], varr[1]);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid pre-scaling <%d %d>\n",
+ chan, varr[0], varr[1]);
+ 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);
+ if (!ret) {
+ u8 dig_version[2];
+
+ ret = adc5_read(adc, ADC5_USR_REVISION1, dig_version,
+ sizeof(dig_version));
+ if (ret < 0) {
+ dev_err(dev, "Invalid dig version read %d\n", ret);
+ return ret;
+ }
+
+ pr_debug("dig_ver:minor:%d, major:%d\n", dig_version[0],
+ dig_version[1]);
+ /* Digital controller >= 5.3 have hw_settle_2 option */
+ if (dig_version[0] >= ADC5_HW_SETTLE_DIFF_MINOR &&
+ dig_version[1] >= ADC5_HW_SETTLE_DIFF_MAJOR)
+ ret = adc5_hw_settle_time_from_dt(value,
+ data->hw_settle_2);
+ else
+ ret = adc5_hw_settle_time_from_dt(value,
+ data->hw_settle_1);
+
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid hw-settle-time %d us\n",
+ chan, value);
+ return ret;
+ }
+ prop->hw_settle_time = ret;
+ } else {
+ prop->hw_settle_time = VADC_DEF_HW_SETTLE_TIME;
+ }
+
+ ret = of_property_read_u32(node, "qcom,avg-samples", &value);
+ if (!ret) {
+ ret = adc5_avg_samples_from_dt(value);
+ if (ret < 0) {
+ dev_err(dev, "%02x invalid avg-samples %d\n",
+ chan, value);
+ return ret;
+ }
+ prop->avg_samples = ret;
+ } else {
+ prop->avg_samples = VADC_DEF_AVG_SAMPLES;
+ }
+
+ if (of_property_read_bool(node, "qcom,ratiometric"))
+ prop->cal_method = ADC5_RATIOMETRIC_CAL;
+ else
+ prop->cal_method = ADC5_ABSOLUTE_CAL;
+
+ /*
+ * Default to using timer calibration. Using a fresh calibration value
+ * for every conversion will increase the overall time for a request.
+ */
+ prop->cal_val = ADC5_TIMER_CAL;
+
+ dev_dbg(dev, "%02x name %s\n", chan, name);
+
+ return 0;
+}
+
+static const struct adc5_data adc5_data_pmic = {
+ .full_scale_code_volt = 0x70e4,
+ .full_scale_code_cur = 0x2710,
+ .adc_chans = adc5_chans_pmic,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ {250, 420, 840},
+ .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 800, 900, 1, 2, 4, 6, 8, 10},
+ .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 1, 2, 4, 8, 16, 32, 64, 128},
+};
+
+static const struct adc5_data adc5_data_pmic_rev2 = {
+ .full_scale_code_volt = 0x4000,
+ .full_scale_code_cur = 0x1800,
+ .adc_chans = adc5_chans_rev2,
+ .decimation = (unsigned int [ADC5_DECIMATION_SAMPLES_MAX])
+ {256, 512, 1024},
+ .hw_settle_1 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {0, 100, 200, 300, 400, 500, 600, 700,
+ 800, 900, 1, 2, 4, 6, 8, 10},
+ .hw_settle_2 = (unsigned int [VADC_HW_SETTLE_SAMPLES_MAX])
+ {15, 100, 200, 300, 400, 500, 600, 700,
+ 1, 2, 4, 8, 16, 32, 64, 128},
+};
+
+static const struct of_device_id adc5_match_table[] = {
+ {
+ .compatible = "qcom,spmi-adc5",
+ .data = &adc5_data_pmic,
+ },
+ {
+ .compatible = "qcom,spmi-adc-rev2",
+ .data = &adc5_data_pmic_rev2,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adc5_match_table);
+
+static int adc5_get_dt_data(struct adc5_chip *adc, struct device_node *node)
+{
+ const struct adc5_channels *adc_chan;
+ struct iio_chan_spec *iio_chan;
+ struct adc5_channel_prop prop, *chan_props;
+ struct device_node *child;
+ unsigned int index = 0;
+ const struct of_device_id *id;
+ const struct adc5_data *data;
+ int ret;
+
+ adc->nchannels = of_get_available_child_count(node);
+ if (!adc->nchannels)
+ return -EINVAL;
+
+ adc->iio_chans = devm_kcalloc(adc->dev, adc->nchannels,
+ sizeof(*adc->iio_chans), GFP_KERNEL);
+ if (!adc->iio_chans)
+ return -ENOMEM;
+
+ adc->chan_props = devm_kcalloc(adc->dev, adc->nchannels,
+ sizeof(*adc->chan_props), GFP_KERNEL);
+ if (!adc->chan_props)
+ return -ENOMEM;
+
+ chan_props = adc->chan_props;
+ iio_chan = adc->iio_chans;
+ id = of_match_node(adc5_match_table, node);
+ if (id)
+ data = id->data;
+ else
+ data = &adc5_data_pmic;
+ adc->data = data;
+
+ for_each_available_child_of_node(node, child) {
+ ret = adc5_get_dt_channel_data(adc, &prop, child, data);
+ if (ret) {
+ of_node_put(child);
+ return ret;
+ }
+
+ prop.scale_fn_type =
+ data->adc_chans[prop.channel].scale_fn_type;
+ *chan_props = prop;
+ adc_chan = &data->adc_chans[prop.channel];
+
+ iio_chan->channel = prop.channel;
+ iio_chan->datasheet_name = prop.datasheet_name;
+ iio_chan->extend_name = prop.datasheet_name;
+ iio_chan->info_mask_separate = adc_chan->info_mask;
+ iio_chan->type = adc_chan->type;
+ iio_chan->address = index;
+ iio_chan++;
+ chan_props++;
+ index++;
+ }
+
+ return 0;
+}
+
+static int adc5_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct adc5_chip *adc;
+ struct regmap *regmap;
+ int ret, irq_eoc;
+ u32 reg;
+
+ regmap = dev_get_regmap(dev->parent, NULL);
+ if (!regmap)
+ return -ENODEV;
+
+ ret = of_property_read_u32(node, "reg", ®);
+ if (ret < 0)
+ return ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->regmap = regmap;
+ adc->dev = dev;
+ adc->base = reg;
+ init_completion(&adc->complete);
+ mutex_init(&adc->lock);
+
+ ret = adc5_get_dt_data(adc, node);
+ if (ret) {
+ pr_err("adc get dt data failed\n");
+ return ret;
+ }
+
+ irq_eoc = platform_get_irq(pdev, 0);
+ if (irq_eoc < 0) {
+ if (irq_eoc == -EPROBE_DEFER || irq_eoc == -EINVAL)
+ return irq_eoc;
+ adc->poll_eoc = true;
+ } else {
+ ret = devm_request_irq(dev, irq_eoc, adc5_isr, 0,
+ "pm-adc5", adc);
+ if (ret)
+ return ret;
+ }
+
+ indio_dev->dev.parent = dev;
+ indio_dev->dev.of_node = node;
+ indio_dev->name = pdev->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &adc5_info;
+ indio_dev->channels = adc->iio_chans;
+ indio_dev->num_channels = adc->nchannels;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static struct platform_driver adc5_driver = {
+ .driver = {
+ .name = "qcom-spmi-adc5.c",
+ .of_match_table = adc5_match_table,
+ },
+ .probe = adc5_probe,
+};
+module_platform_driver(adc5_driver);
+
+MODULE_ALIAS("platform:qcom-spmi-adc5");
+MODULE_DESCRIPTION("Qualcomm Technologies Inc. PMIC5 ADC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/qcom-spmi-iadc.c b/drivers/iio/adc/qcom-spmi-iadc.c
index 3f062cd..46858ed 100644
--- a/drivers/iio/adc/qcom-spmi-iadc.c
+++ b/drivers/iio/adc/qcom-spmi-iadc.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2014, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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/bitops.h>
diff --git a/drivers/iio/adc/qcom-spmi-vadc.c b/drivers/iio/adc/qcom-spmi-vadc.c
index 3680e0d..203ad59 100644
--- a/drivers/iio/adc/qcom-spmi-vadc.c
+++ b/drivers/iio/adc/qcom-spmi-vadc.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2016, The Linux Foundation. All rights reserved.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License version 2 and
- * only version 2 as published by the Free Software Foundation.
- *
- * 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/bitops.h>
diff --git a/drivers/iio/adc/qcom-vadc-common.c b/drivers/iio/adc/qcom-vadc-common.c
index fe3d782..dcd7fb5 100644
--- a/drivers/iio/adc/qcom-vadc-common.c
+++ b/drivers/iio/adc/qcom-vadc-common.c
@@ -47,8 +47,79 @@
{44, 125}
};
+/*
+ * Voltage to temperature table for 100k pull up for NTCG104EF104 with
+ * 1.875V reference.
+ */
+static const struct vadc_map_pt adcmap_100k_104ef_104fb_1875_vref[] = {
+ { 1831, -40000 },
+ { 1814, -35000 },
+ { 1791, -30000 },
+ { 1761, -25000 },
+ { 1723, -20000 },
+ { 1675, -15000 },
+ { 1616, -10000 },
+ { 1545, -5000 },
+ { 1463, 0 },
+ { 1370, 5000 },
+ { 1268, 10000 },
+ { 1160, 15000 },
+ { 1049, 20000 },
+ { 937, 25000 },
+ { 828, 30000 },
+ { 726, 35000 },
+ { 630, 40000 },
+ { 544, 45000 },
+ { 467, 50000 },
+ { 399, 55000 },
+ { 340, 60000 },
+ { 290, 65000 },
+ { 247, 70000 },
+ { 209, 75000 },
+ { 179, 80000 },
+ { 153, 85000 },
+ { 130, 90000 },
+ { 112, 95000 },
+ { 96, 100000 },
+ { 82, 105000 },
+ { 71, 110000 },
+ { 62, 115000 },
+ { 53, 120000 },
+ { 46, 125000 },
+};
+
+static int qcom_vadc_scale_hw_calib_volt(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_uv);
+static int qcom_vadc_scale_hw_calib_therm(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_smb_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_chg5_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+static int qcom_vadc_scale_hw_calib_die_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+
+static struct qcom_adc5_scale_type scale_adc5_fn[] = {
+ [SCALE_HW_CALIB_DEFAULT] = {qcom_vadc_scale_hw_calib_volt},
+ [SCALE_HW_CALIB_THERM_100K_PULLUP] = {qcom_vadc_scale_hw_calib_therm},
+ [SCALE_HW_CALIB_XOTHERM] = {qcom_vadc_scale_hw_calib_therm},
+ [SCALE_HW_CALIB_PMIC_THERM] = {qcom_vadc_scale_hw_calib_die_temp},
+ [SCALE_HW_CALIB_PM5_CHG_TEMP] = {qcom_vadc_scale_hw_chg5_temp},
+ [SCALE_HW_CALIB_PM5_SMB_TEMP] = {qcom_vadc_scale_hw_smb_temp},
+};
+
static int qcom_vadc_map_voltage_temp(const struct vadc_map_pt *pts,
- u32 tablesize, s32 input, s64 *output)
+ u32 tablesize, s32 input, int *output)
{
bool descending = 1;
u32 i = 0;
@@ -128,7 +199,7 @@
bool absolute, u16 adc_code,
int *result_mdec)
{
- s64 voltage = 0, result = 0;
+ s64 voltage = 0;
int ret;
qcom_vadc_scale_calib(calib_graph, adc_code, absolute, &voltage);
@@ -138,12 +209,11 @@
ret = qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb,
ARRAY_SIZE(adcmap_100k_104ef_104fb),
- voltage, &result);
+ voltage, result_mdec);
if (ret)
return ret;
- result *= 1000;
- *result_mdec = result;
+ *result_mdec *= 1000;
return 0;
}
@@ -191,6 +261,99 @@
return 0;
}
+static int qcom_vadc_scale_code_voltage_factor(u16 adc_code,
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ unsigned int factor)
+{
+ s64 voltage, temp, adc_vdd_ref_mv = 1875;
+
+ /*
+ * The normal data range is between 0V to 1.875V. On cases where
+ * we read low voltage values, the ADC code can go beyond the
+ * range and the scale result is incorrect so we clamp the values
+ * for the cases where the code represents a value below 0V
+ */
+ if (adc_code > VADC5_MAX_CODE)
+ adc_code = 0;
+
+ /* (ADC code * vref_vadc (1.875V)) / full_scale_code */
+ voltage = (s64) adc_code * adc_vdd_ref_mv * 1000;
+ voltage = div64_s64(voltage, data->full_scale_code_volt);
+ if (voltage > 0) {
+ voltage *= prescale->den;
+ temp = prescale->num * factor;
+ voltage = div64_s64(voltage, temp);
+ } else {
+ voltage = 0;
+ }
+
+ return (int) voltage;
+}
+
+static int qcom_vadc_scale_hw_calib_volt(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_uv)
+{
+ *result_uv = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 1);
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_calib_therm(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ int voltage;
+
+ voltage = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 1000);
+
+ /* Map voltage to temperature from look-up table */
+ return qcom_vadc_map_voltage_temp(adcmap_100k_104ef_104fb_1875_vref,
+ ARRAY_SIZE(adcmap_100k_104ef_104fb_1875_vref),
+ voltage, result_mdec);
+}
+
+static int qcom_vadc_scale_hw_calib_die_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 2);
+ *result_mdec -= KELVINMIL_CELSIUSMIL;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_smb_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code * 100,
+ prescale, data, PMIC5_SMB_TEMP_SCALE_FACTOR);
+ *result_mdec = PMIC5_SMB_TEMP_CONSTANT - *result_mdec;
+
+ return 0;
+}
+
+static int qcom_vadc_scale_hw_chg5_temp(
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec)
+{
+ *result_mdec = qcom_vadc_scale_code_voltage_factor(adc_code,
+ prescale, data, 4);
+ *result_mdec = PMIC5_CHG_TEMP_SCALE_FACTOR - *result_mdec;
+
+ return 0;
+}
+
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
const struct vadc_linear_graph *calib_graph,
const struct vadc_prescale_ratio *prescale,
@@ -221,6 +384,22 @@
}
EXPORT_SYMBOL(qcom_vadc_scale);
+int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result)
+{
+ if (!(scaletype >= SCALE_HW_CALIB_DEFAULT &&
+ scaletype < SCALE_HW_CALIB_INVALID)) {
+ pr_err("Invalid scale type %d\n", scaletype);
+ return -EINVAL;
+ }
+
+ return scale_adc5_fn[scaletype].scale_fn(prescale, data,
+ adc_code, result);
+}
+EXPORT_SYMBOL(qcom_adc5_hw_scale);
+
int qcom_vadc_decimation_from_dt(u32 value)
{
if (!is_power_of_2(value) || value < VADC_DECIMATION_MIN ||
diff --git a/drivers/iio/adc/qcom-vadc-common.h b/drivers/iio/adc/qcom-vadc-common.h
index 1d5354f..bbb1fa0 100644
--- a/drivers/iio/adc/qcom-vadc-common.h
+++ b/drivers/iio/adc/qcom-vadc-common.h
@@ -25,15 +25,31 @@
#define VADC_DECIMATION_MIN 512
#define VADC_DECIMATION_MAX 4096
+#define ADC5_DEF_VBAT_PRESCALING 1 /* 1:3 */
+#define ADC5_DECIMATION_SHORT 250
+#define ADC5_DECIMATION_MEDIUM 420
+#define ADC5_DECIMATION_LONG 840
+/* Default decimation - 1024 for rev2, 840 for pmic5 */
+#define ADC5_DECIMATION_DEFAULT 2
+#define ADC5_DECIMATION_SAMPLES_MAX 3
#define VADC_HW_SETTLE_DELAY_MAX 10000
+#define VADC_HW_SETTLE_SAMPLES_MAX 16
#define VADC_AVG_SAMPLES_MAX 512
+#define ADC5_AVG_SAMPLES_MAX 16
#define KELVINMIL_CELSIUSMIL 273150
+#define PMIC5_CHG_TEMP_SCALE_FACTOR 377500
+#define PMIC5_SMB_TEMP_CONSTANT 419400
+#define PMIC5_SMB_TEMP_SCALE_FACTOR 356
#define PMI_CHG_SCALE_1 -138890
#define PMI_CHG_SCALE_2 391750000000LL
+#define VADC5_MAX_CODE 0x7fff
+#define ADC5_FULL_SCALE_CODE 0x70e4
+#define ADC5_USR_DATA_CHECK 0x8000
+
/**
* struct vadc_map_pt - Map the graph representation for ADC channel
* @x: Represent the ADC digitized code.
@@ -89,6 +105,18 @@
* SCALE_PMIC_THERM: Returns result in milli degree's Centigrade.
* SCALE_XOTHERM: Returns XO thermistor voltage in millidegC.
* SCALE_PMI_CHG_TEMP: Conversion for PMI CHG temp
+ * SCALE_HW_CALIB_DEFAULT: Default scaling to convert raw adc code to
+ * voltage (uV) with hardware applied offset/slope values to adc code.
+ * SCALE_HW_CALIB_THERM_100K_PULLUP: Returns temperature in millidegC using
+ * lookup table. The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_XOTHERM: Returns XO thermistor voltage in millidegC using
+ * 100k pullup. The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_PMIC_THERM: Returns result in milli degree's Centigrade.
+ * The hardware applies offset/slope to adc code.
+ * SCALE_HW_CALIB_PM5_CHG_TEMP: Returns result in millidegrees for PMIC5
+ * charger temperature.
+ * SCALE_HW_CALIB_PM5_SMB_TEMP: Returns result in millidegrees for PMIC5
+ * SMB1390 temperature.
*/
enum vadc_scale_fn_type {
SCALE_DEFAULT = 0,
@@ -96,6 +124,22 @@
SCALE_PMIC_THERM,
SCALE_XOTHERM,
SCALE_PMI_CHG_TEMP,
+ SCALE_HW_CALIB_DEFAULT,
+ SCALE_HW_CALIB_THERM_100K_PULLUP,
+ SCALE_HW_CALIB_XOTHERM,
+ SCALE_HW_CALIB_PMIC_THERM,
+ SCALE_HW_CALIB_PM5_CHG_TEMP,
+ SCALE_HW_CALIB_PM5_SMB_TEMP,
+ SCALE_HW_CALIB_INVALID,
+};
+
+struct adc5_data {
+ const u32 full_scale_code_volt;
+ const u32 full_scale_code_cur;
+ const struct adc5_channels *adc_chans;
+ unsigned int *decimation;
+ unsigned int *hw_settle_1;
+ unsigned int *hw_settle_2;
};
int qcom_vadc_scale(enum vadc_scale_fn_type scaletype,
@@ -104,6 +148,16 @@
bool absolute,
u16 adc_code, int *result_mdec);
+struct qcom_adc5_scale_type {
+ int (*scale_fn)(const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data, u16 adc_code, int *result);
+};
+
+int qcom_adc5_hw_scale(enum vadc_scale_fn_type scaletype,
+ const struct vadc_prescale_ratio *prescale,
+ const struct adc5_data *data,
+ u16 adc_code, int *result_mdec);
+
int qcom_vadc_decimation_from_dt(u32 value);
#endif /* QCOM_VADC_COMMON_H */
diff --git a/drivers/iio/adc/rcar-gyroadc.c b/drivers/iio/adc/rcar-gyroadc.c
index dcb5017..c37f201 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>
@@ -343,8 +334,8 @@
for_each_child_of_node(np, child) {
of_id = of_match_node(rcar_gyroadc_child_match, child);
if (!of_id) {
- dev_err(dev, "Ignoring unsupported ADC \"%s\".",
- child->name);
+ dev_err(dev, "Ignoring unsupported ADC \"%pOFn\".",
+ child);
continue;
}
@@ -381,17 +372,17 @@
ret = of_property_read_u32(child, "reg", ®);
if (ret) {
dev_err(dev,
- "Failed to get child reg property of ADC \"%s\".\n",
- child->name);
+ "Failed to get child reg property of ADC \"%pOFn\".\n",
+ child);
return ret;
}
/* Channel number is too high. */
if (reg >= num_channels) {
dev_err(dev,
- "Only %i channels supported with %s, but reg = <%i>.\n",
- num_channels, child->name, reg);
- return ret;
+ "Only %i channels supported with %pOFn, but reg = <%i>.\n",
+ num_channels, child, reg);
+ return -EINVAL;
}
}
@@ -400,7 +391,7 @@
dev_err(dev,
"Channel %i uses different ADC mode than the rest.\n",
reg);
- return ret;
+ return -EINVAL;
}
/* Channel is valid, grab the regulator. */
@@ -494,10 +485,8 @@
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
- if (!indio_dev) {
- dev_err(dev, "Failed to allocate IIO device.\n");
+ if (!indio_dev)
return -ENOMEM;
- }
priv = iio_priv(indio_dev);
priv->dev = dev;
diff --git a/drivers/iio/adc/rockchip_saradc.c b/drivers/iio/adc/rockchip_saradc.c
index 1f98566..582ba04 100644
--- a/drivers/iio/adc/rockchip_saradc.c
+++ b/drivers/iio/adc/rockchip_saradc.c
@@ -1,16 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Rockchip Successive Approximation Register (SAR) A/D Converter
* Copyright (C) 2014 ROCKCHIP, Inc.
- *
- * 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>
@@ -253,10 +244,8 @@
init_completion(&info->completion);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(&pdev->dev, irq, rockchip_saradc_isr,
0, dev_name(&pdev->dev), info);
diff --git a/drivers/iio/adc/sc27xx_adc.c b/drivers/iio/adc/sc27xx_adc.c
index 2b60efe..a6c0465 100644
--- a/drivers/iio/adc/sc27xx_adc.c
+++ b/drivers/iio/adc/sc27xx_adc.c
@@ -3,12 +3,13 @@
#include <linux/hwspinlock.h>
#include <linux/iio/iio.h>
-#include <linux/interrupt.h>
#include <linux/module.h>
+#include <linux/nvmem-consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/slab.h>
/* PMIC global registers definition */
#define SC27XX_MODULE_EN 0xc08
@@ -44,12 +45,19 @@
/* Bits definitions for SC27XX_ADC_INT_CLR registers */
#define SC27XX_ADC_IRQ_CLR BIT(0)
+/* Bits definitions for SC27XX_ADC_INT_RAW registers */
+#define SC27XX_ADC_IRQ_RAW BIT(0)
+
/* Mask definition for SC27XX_ADC_DATA register */
#define SC27XX_ADC_DATA_MASK GENMASK(11, 0)
/* Timeout (ms) for the trylock of hardware spinlocks */
#define SC27XX_ADC_HWLOCK_TIMEOUT 5000
+/* Timeout (us) for ADC data conversion according to ADC datasheet */
+#define SC27XX_ADC_RDY_TIMEOUT 1000000
+#define SC27XX_ADC_POLL_RAW_STATUS 500
+
/* Maximum ADC channel number */
#define SC27XX_ADC_CHANNEL_MAX 32
@@ -67,10 +75,8 @@
* subsystems which will access the unique ADC controller.
*/
struct hwspinlock *hwlock;
- struct completion completion;
int channel_scale[SC27XX_ADC_CHANNEL_MAX];
u32 base;
- int value;
int irq;
};
@@ -87,16 +93,73 @@
* should use the small-scale graph, and if more than 1.2v, we should use the
* big-scale graph.
*/
-static const struct sc27xx_adc_linear_graph big_scale_graph = {
+static struct sc27xx_adc_linear_graph big_scale_graph = {
4200, 3310,
3600, 2832,
};
-static const struct sc27xx_adc_linear_graph small_scale_graph = {
+static struct sc27xx_adc_linear_graph small_scale_graph = {
1000, 3413,
100, 341,
};
+static const struct sc27xx_adc_linear_graph big_scale_graph_calib = {
+ 4200, 856,
+ 3600, 733,
+};
+
+static const struct sc27xx_adc_linear_graph small_scale_graph_calib = {
+ 1000, 833,
+ 100, 80,
+};
+
+static int sc27xx_adc_get_calib_data(u32 calib_data, int calib_adc)
+{
+ return ((calib_data & 0xff) + calib_adc - 128) * 4;
+}
+
+static int sc27xx_adc_scale_calibration(struct sc27xx_adc_data *data,
+ bool big_scale)
+{
+ const struct sc27xx_adc_linear_graph *calib_graph;
+ struct sc27xx_adc_linear_graph *graph;
+ struct nvmem_cell *cell;
+ const char *cell_name;
+ u32 calib_data = 0;
+ void *buf;
+ size_t len;
+
+ if (big_scale) {
+ calib_graph = &big_scale_graph_calib;
+ graph = &big_scale_graph;
+ cell_name = "big_scale_calib";
+ } else {
+ calib_graph = &small_scale_graph_calib;
+ graph = &small_scale_graph;
+ cell_name = "small_scale_calib";
+ }
+
+ cell = nvmem_cell_get(data->dev, cell_name);
+ if (IS_ERR(cell))
+ return PTR_ERR(cell);
+
+ buf = nvmem_cell_read(cell, &len);
+ nvmem_cell_put(cell);
+
+ if (IS_ERR(buf))
+ return PTR_ERR(buf);
+
+ memcpy(&calib_data, buf, min(len, sizeof(u32)));
+
+ /* Only need to calibrate the adc values in the linear graph. */
+ graph->adc0 = sc27xx_adc_get_calib_data(calib_data, calib_graph->adc0);
+ graph->adc1 = sc27xx_adc_get_calib_data(calib_data >> 8,
+ calib_graph->adc1);
+
+ kfree(buf);
+ return 0;
+}
+
static int sc27xx_adc_get_ratio(int channel, int scale)
{
switch (channel) {
@@ -126,9 +189,7 @@
int scale, int *val)
{
int ret;
- u32 tmp;
-
- reinit_completion(&data->completion);
+ u32 tmp, value, status;
ret = hwspin_lock_timeout_raw(data->hwlock, SC27XX_ADC_HWLOCK_TIMEOUT);
if (ret) {
@@ -141,6 +202,11 @@
if (ret)
goto unlock_adc;
+ ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
+ SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR);
+ if (ret)
+ goto disable_adc;
+
/* Configure the channel id and scale */
tmp = (scale << SC27XX_ADC_SCALE_SHIFT) & SC27XX_ADC_SCALE_MASK;
tmp |= channel & SC27XX_ADC_CHN_ID_MASK;
@@ -164,7 +230,21 @@
if (ret)
goto disable_adc;
- wait_for_completion(&data->completion);
+ ret = regmap_read_poll_timeout(data->regmap,
+ data->base + SC27XX_ADC_INT_RAW,
+ status, (status & SC27XX_ADC_IRQ_RAW),
+ SC27XX_ADC_POLL_RAW_STATUS,
+ SC27XX_ADC_RDY_TIMEOUT);
+ if (ret) {
+ dev_err(data->dev, "read adc timeout, status = 0x%x\n", status);
+ goto disable_adc;
+ }
+
+ ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA, &value);
+ if (ret)
+ goto disable_adc;
+
+ value &= SC27XX_ADC_DATA_MASK;
disable_adc:
regmap_update_bits(data->regmap, data->base + SC27XX_ADC_CTL,
@@ -173,32 +253,11 @@
hwspin_unlock_raw(data->hwlock);
if (!ret)
- *val = data->value;
+ *val = value;
return ret;
}
-static irqreturn_t sc27xx_adc_isr(int irq, void *dev_id)
-{
- struct sc27xx_adc_data *data = dev_id;
- int ret;
-
- ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_CLR,
- SC27XX_ADC_IRQ_CLR, SC27XX_ADC_IRQ_CLR);
- if (ret)
- return IRQ_RETVAL(ret);
-
- ret = regmap_read(data->regmap, data->base + SC27XX_ADC_DATA,
- &data->value);
- if (ret)
- return IRQ_RETVAL(ret);
-
- data->value &= SC27XX_ADC_DATA_MASK;
- complete(&data->completion);
-
- return IRQ_HANDLED;
-}
-
static void sc27xx_adc_volt_ratio(struct sc27xx_adc_data *data,
int channel, int scale,
u32 *div_numerator, u32 *div_denominator)
@@ -209,7 +268,7 @@
*div_denominator = ratio & SC27XX_RATIO_DENOMINATOR_MASK;
}
-static int sc27xx_adc_to_volt(const struct sc27xx_adc_linear_graph *graph,
+static int sc27xx_adc_to_volt(struct sc27xx_adc_linear_graph *graph,
int raw_adc)
{
int tmp;
@@ -273,6 +332,17 @@
int ret, tmp;
switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&indio_dev->mlock);
+ ret = sc27xx_adc_read(data, chan->channel, scale, &tmp);
+ mutex_unlock(&indio_dev->mlock);
+
+ if (ret)
+ return ret;
+
+ *val = tmp;
+ return IIO_VAL_INT;
+
case IIO_CHAN_INFO_PROCESSED:
mutex_lock(&indio_dev->mlock);
ret = sc27xx_adc_read_processed(data, chan->channel, scale,
@@ -315,48 +385,47 @@
.write_raw = &sc27xx_adc_write_raw,
};
-#define SC27XX_ADC_CHANNEL(index) { \
+#define SC27XX_ADC_CHANNEL(index, mask) { \
.type = IIO_VOLTAGE, \
.channel = index, \
- .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | \
- BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = mask | BIT(IIO_CHAN_INFO_SCALE), \
.datasheet_name = "CH##index", \
.indexed = 1, \
}
static const struct iio_chan_spec sc27xx_channels[] = {
- SC27XX_ADC_CHANNEL(0),
- SC27XX_ADC_CHANNEL(1),
- SC27XX_ADC_CHANNEL(2),
- SC27XX_ADC_CHANNEL(3),
- SC27XX_ADC_CHANNEL(4),
- SC27XX_ADC_CHANNEL(5),
- SC27XX_ADC_CHANNEL(6),
- SC27XX_ADC_CHANNEL(7),
- SC27XX_ADC_CHANNEL(8),
- SC27XX_ADC_CHANNEL(9),
- SC27XX_ADC_CHANNEL(10),
- SC27XX_ADC_CHANNEL(11),
- SC27XX_ADC_CHANNEL(12),
- SC27XX_ADC_CHANNEL(13),
- SC27XX_ADC_CHANNEL(14),
- SC27XX_ADC_CHANNEL(15),
- SC27XX_ADC_CHANNEL(16),
- SC27XX_ADC_CHANNEL(17),
- SC27XX_ADC_CHANNEL(18),
- SC27XX_ADC_CHANNEL(19),
- SC27XX_ADC_CHANNEL(20),
- SC27XX_ADC_CHANNEL(21),
- SC27XX_ADC_CHANNEL(22),
- SC27XX_ADC_CHANNEL(23),
- SC27XX_ADC_CHANNEL(24),
- SC27XX_ADC_CHANNEL(25),
- SC27XX_ADC_CHANNEL(26),
- SC27XX_ADC_CHANNEL(27),
- SC27XX_ADC_CHANNEL(28),
- SC27XX_ADC_CHANNEL(29),
- SC27XX_ADC_CHANNEL(30),
- SC27XX_ADC_CHANNEL(31),
+ SC27XX_ADC_CHANNEL(0, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(1, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(2, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(3, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(4, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(5, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(6, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(7, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(8, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(9, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(10, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(11, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(12, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(13, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(14, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(15, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(16, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(17, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(18, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(19, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(20, BIT(IIO_CHAN_INFO_RAW)),
+ SC27XX_ADC_CHANNEL(21, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(22, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(23, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(24, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(25, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(26, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(27, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(28, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(29, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(30, BIT(IIO_CHAN_INFO_PROCESSED)),
+ SC27XX_ADC_CHANNEL(31, BIT(IIO_CHAN_INFO_PROCESSED)),
};
static int sc27xx_adc_enable(struct sc27xx_adc_data *data)
@@ -375,8 +444,12 @@
if (ret)
goto disable_adc;
- ret = regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
- SC27XX_ADC_IRQ_EN, SC27XX_ADC_IRQ_EN);
+ /* ADC channel scales' calibration from nvmem device */
+ ret = sc27xx_adc_scale_calibration(data, true);
+ if (ret)
+ goto disable_clk;
+
+ ret = sc27xx_adc_scale_calibration(data, false);
if (ret)
goto disable_clk;
@@ -396,9 +469,6 @@
{
struct sc27xx_adc_data *data = _data;
- regmap_update_bits(data->regmap, data->base + SC27XX_ADC_INT_EN,
- SC27XX_ADC_IRQ_EN, 0);
-
/* Disable ADC work clock and controller clock */
regmap_update_bits(data->regmap, SC27XX_ARM_CLK_EN,
SC27XX_CLK_ADC_EN | SC27XX_CLK_ADC_CLK_EN, 0);
@@ -416,88 +486,76 @@
static int sc27xx_adc_probe(struct platform_device *pdev)
{
- struct device_node *np = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
struct sc27xx_adc_data *sc27xx_data;
struct iio_dev *indio_dev;
int ret;
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*sc27xx_data));
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*sc27xx_data));
if (!indio_dev)
return -ENOMEM;
sc27xx_data = iio_priv(indio_dev);
- sc27xx_data->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+ sc27xx_data->regmap = dev_get_regmap(dev->parent, NULL);
if (!sc27xx_data->regmap) {
- dev_err(&pdev->dev, "failed to get ADC regmap\n");
+ dev_err(dev, "failed to get ADC regmap\n");
return -ENODEV;
}
ret = of_property_read_u32(np, "reg", &sc27xx_data->base);
if (ret) {
- dev_err(&pdev->dev, "failed to get ADC base address\n");
+ dev_err(dev, "failed to get ADC base address\n");
return ret;
}
sc27xx_data->irq = platform_get_irq(pdev, 0);
- if (sc27xx_data->irq < 0) {
- dev_err(&pdev->dev, "failed to get ADC irq number\n");
+ if (sc27xx_data->irq < 0)
return sc27xx_data->irq;
- }
ret = of_hwspin_lock_get_id(np, 0);
if (ret < 0) {
- dev_err(&pdev->dev, "failed to get hwspinlock id\n");
+ dev_err(dev, "failed to get hwspinlock id\n");
return ret;
}
sc27xx_data->hwlock = hwspin_lock_request_specific(ret);
if (!sc27xx_data->hwlock) {
- dev_err(&pdev->dev, "failed to request hwspinlock\n");
+ dev_err(dev, "failed to request hwspinlock\n");
return -ENXIO;
}
- ret = devm_add_action(&pdev->dev, sc27xx_adc_free_hwlock,
+ ret = devm_add_action_or_reset(dev, sc27xx_adc_free_hwlock,
sc27xx_data->hwlock);
if (ret) {
- sc27xx_adc_free_hwlock(sc27xx_data->hwlock);
- dev_err(&pdev->dev, "failed to add hwspinlock action\n");
+ dev_err(dev, "failed to add hwspinlock action\n");
return ret;
}
- init_completion(&sc27xx_data->completion);
- sc27xx_data->dev = &pdev->dev;
+ sc27xx_data->dev = dev;
ret = sc27xx_adc_enable(sc27xx_data);
if (ret) {
- dev_err(&pdev->dev, "failed to enable ADC module\n");
+ dev_err(dev, "failed to enable ADC module\n");
return ret;
}
- ret = devm_add_action(&pdev->dev, sc27xx_adc_disable, sc27xx_data);
+ ret = devm_add_action_or_reset(dev, sc27xx_adc_disable, sc27xx_data);
if (ret) {
- sc27xx_adc_disable(sc27xx_data);
- dev_err(&pdev->dev, "failed to add ADC disable action\n");
+ dev_err(dev, "failed to add ADC disable action\n");
return ret;
}
- ret = devm_request_threaded_irq(&pdev->dev, sc27xx_data->irq, NULL,
- sc27xx_adc_isr, IRQF_ONESHOT,
- pdev->name, sc27xx_data);
- if (ret) {
- dev_err(&pdev->dev, "failed to request ADC irq\n");
- return ret;
- }
-
- indio_dev->dev.parent = &pdev->dev;
- indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = dev;
+ indio_dev->name = dev_name(dev);
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &sc27xx_info;
indio_dev->channels = sc27xx_channels;
indio_dev->num_channels = ARRAY_SIZE(sc27xx_channels);
- ret = devm_iio_device_register(&pdev->dev, indio_dev);
+ ret = devm_iio_device_register(dev, indio_dev);
if (ret)
- dev_err(&pdev->dev, "could not register iio (ADC)");
+ dev_err(dev, "could not register iio (ADC)");
return ret;
}
diff --git a/drivers/iio/adc/spear_adc.c b/drivers/iio/adc/spear_adc.c
index b1da2c4..592b97c 100644
--- a/drivers/iio/adc/spear_adc.c
+++ b/drivers/iio/adc/spear_adc.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ST SPEAr ADC driver
*
* Copyright 2012 Stefan Roese <sr@denx.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -302,7 +301,6 @@
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
- dev_err(dev, "failed getting interrupt resource\n");
ret = -EINVAL;
goto errout2;
}
diff --git a/drivers/iio/adc/stm32-adc-core.c b/drivers/iio/adc/stm32-adc-core.c
index ca432e7..93a096a 100644
--- a/drivers/iio/adc/stm32-adc-core.c
+++ b/drivers/iio/adc/stm32-adc-core.c
@@ -14,52 +14,47 @@
#include <linux/irqchip/chained_irq.h>
#include <linux/irqdesc.h>
#include <linux/irqdomain.h>
+#include <linux/mfd/syscon.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include "stm32-adc-core.h"
-/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
-#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
-#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+#define STM32_ADC_CORE_SLEEP_DELAY_MS 2000
-/* STM32F4_ADC_CSR - bit fields */
-#define STM32F4_EOC3 BIT(17)
-#define STM32F4_EOC2 BIT(9)
-#define STM32F4_EOC1 BIT(1)
+/* SYSCFG registers */
+#define STM32MP1_SYSCFG_PMCSETR 0x04
+#define STM32MP1_SYSCFG_PMCCLRR 0x44
-/* STM32F4_ADC_CCR - bit fields */
-#define STM32F4_ADC_ADCPRE_SHIFT 16
-#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+/* SYSCFG bit fields */
+#define STM32MP1_SYSCFG_ANASWVDD_MASK BIT(9)
-/* STM32H7 - common registers for all ADC instances */
-#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
-#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
-
-/* STM32H7_ADC_CSR - bit fields */
-#define STM32H7_EOC_SLV BIT(18)
-#define STM32H7_EOC_MST BIT(2)
-
-/* STM32H7_ADC_CCR - bit fields */
-#define STM32H7_PRESC_SHIFT 18
-#define STM32H7_PRESC_MASK GENMASK(21, 18)
-#define STM32H7_CKMODE_SHIFT 16
-#define STM32H7_CKMODE_MASK GENMASK(17, 16)
+/* SYSCFG capability flags */
+#define HAS_VBOOSTER BIT(0)
+#define HAS_ANASWVDD BIT(1)
/**
* 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
+ * @ier: interrupt enable register offset for each adc
+ * @eocie_msk: end of conversion interrupt enable mask in @ier
*/
struct stm32_adc_common_regs {
u32 csr;
+ u32 ccr;
u32 eoc1_msk;
u32 eoc2_msk;
u32 eoc3_msk;
+ u32 ier;
+ u32 eocie_msk;
};
struct stm32_adc_priv;
@@ -69,11 +64,13 @@
* @regs: common registers for all instances
* @clk_sel: clock selection routine
* @max_clk_rate_hz: maximum analog clock rate (Hz, from datasheet)
+ * @has_syscfg: SYSCFG capability flags
*/
struct stm32_adc_priv_cfg {
const struct stm32_adc_common_regs *regs;
int (*clk_sel)(struct platform_device *, struct stm32_adc_priv *);
u32 max_clk_rate_hz;
+ unsigned int has_syscfg;
};
/**
@@ -82,18 +79,32 @@
* @domain: irq domain reference
* @aclk: clock reference for the analog circuitry
* @bclk: bus clock common for all ADCs, depends on part used
+ * @booster: booster supply reference
+ * @vdd: vdd supply reference
+ * @vdda: vdda analog supply reference
* @vref: regulator reference
+ * @vdd_uv: vdd supply voltage (microvolts)
+ * @vdda_uv: vdda supply voltage (microvolts)
* @cfg: compatible configuration data
* @common: common data for all ADC instances
+ * @ccr_bak: backup CCR in low power mode
+ * @syscfg: reference to syscon, system control registers
*/
struct stm32_adc_priv {
int irq[STM32_ADC_MAX_ADCS];
struct irq_domain *domain;
struct clk *aclk;
struct clk *bclk;
+ struct regulator *booster;
+ struct regulator *vdd;
+ struct regulator *vdda;
struct regulator *vref;
+ int vdd_uv;
+ int vdda_uv;
const struct stm32_adc_priv_cfg *cfg;
struct stm32_adc_common common;
+ u32 ccr_bak;
+ struct regmap *syscfg;
};
static struct stm32_adc_priv *to_stm32_adc_priv(struct stm32_adc_common *com)
@@ -265,18 +276,38 @@
/* 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,
+ .ier = STM32F4_ADC_CR1,
+ .eocie_msk = STM32F4_EOCIE,
};
/* 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,
+ .ier = STM32H7_ADC_IER,
+ .eocie_msk = STM32H7_EOCIE,
};
+static const unsigned int stm32_adc_offset[STM32_ADC_MAX_ADCS] = {
+ 0, STM32_ADC_OFFSET, STM32_ADC_OFFSET * 2,
+};
+
+static unsigned int stm32_adc_eoc_enabled(struct stm32_adc_priv *priv,
+ unsigned int adc)
+{
+ u32 ier, offset = stm32_adc_offset[adc];
+
+ ier = readl_relaxed(priv->common.base + offset + priv->cfg->regs->ier);
+
+ return ier & priv->cfg->regs->eocie_msk;
+}
+
/* ADC common interrupt for all instances */
static void stm32_adc_irq_handler(struct irq_desc *desc)
{
@@ -287,13 +318,28 @@
chained_irq_enter(chip, desc);
status = readl_relaxed(priv->common.base + priv->cfg->regs->csr);
- if (status & priv->cfg->regs->eoc1_msk)
+ /*
+ * End of conversion may be handled by using IRQ or DMA. There may be a
+ * race here when two conversions complete at the same time on several
+ * ADCs. EOC may be read 'set' for several ADCs, with:
+ * - an ADC configured to use DMA (EOC triggers the DMA request, and
+ * is then automatically cleared by DR read in hardware)
+ * - an ADC configured to use IRQs (EOCIE bit is set. The handler must
+ * be called in this case)
+ * So both EOC status bit in CSR and EOCIE control bit must be checked
+ * before invoking the interrupt handler (e.g. call ISR only for
+ * IRQ-enabled ADCs).
+ */
+ if (status & priv->cfg->regs->eoc1_msk &&
+ stm32_adc_eoc_enabled(priv, 0))
generic_handle_irq(irq_find_mapping(priv->domain, 0));
- if (status & priv->cfg->regs->eoc2_msk)
+ if (status & priv->cfg->regs->eoc2_msk &&
+ stm32_adc_eoc_enabled(priv, 1))
generic_handle_irq(irq_find_mapping(priv->domain, 1));
- if (status & priv->cfg->regs->eoc3_msk)
+ if (status & priv->cfg->regs->eoc3_msk &&
+ stm32_adc_eoc_enabled(priv, 2))
generic_handle_irq(irq_find_mapping(priv->domain, 2));
chained_irq_exit(chip, desc);
@@ -338,7 +384,6 @@
*/
if (i && priv->irq[i] == -ENXIO)
continue;
- dev_err(&pdev->dev, "failed to get irq\n");
return priv->irq[i];
}
@@ -379,6 +424,230 @@
}
}
+static int stm32_adc_core_switches_supply_en(struct stm32_adc_priv *priv,
+ struct device *dev)
+{
+ int ret;
+
+ /*
+ * On STM32H7 and STM32MP1, the ADC inputs are multiplexed with analog
+ * switches (via PCSEL) which have reduced performances when their
+ * supply is below 2.7V (vdda by default):
+ * - Voltage booster can be used, to get full ADC performances
+ * (increases power consumption).
+ * - Vdd can be used to supply them, if above 2.7V (STM32MP1 only).
+ *
+ * Recommended settings for ANASWVDD and EN_BOOSTER:
+ * - vdda < 2.7V but vdd > 2.7V: ANASWVDD = 1, EN_BOOSTER = 0 (stm32mp1)
+ * - vdda < 2.7V and vdd < 2.7V: ANASWVDD = 0, EN_BOOSTER = 1
+ * - vdda >= 2.7V: ANASWVDD = 0, EN_BOOSTER = 0 (default)
+ */
+ if (priv->vdda_uv < 2700000) {
+ if (priv->syscfg && priv->vdd_uv > 2700000) {
+ ret = regulator_enable(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_write(priv->syscfg,
+ STM32MP1_SYSCFG_PMCSETR,
+ STM32MP1_SYSCFG_ANASWVDD_MASK);
+ if (ret < 0) {
+ regulator_disable(priv->vdd);
+ dev_err(dev, "vdd select failed, %d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev, "analog switches supplied by vdd\n");
+
+ return 0;
+ }
+
+ if (priv->booster) {
+ /*
+ * This is optional, as this is a trade-off between
+ * analog performance and power consumption.
+ */
+ ret = regulator_enable(priv->booster);
+ if (ret < 0) {
+ dev_err(dev, "booster enable failed %d\n", ret);
+ return ret;
+ }
+ dev_dbg(dev, "analog switches supplied by booster\n");
+
+ return 0;
+ }
+ }
+
+ /* Fallback using vdda (default), nothing to do */
+ dev_dbg(dev, "analog switches supplied by vdda (%d uV)\n",
+ priv->vdda_uv);
+
+ return 0;
+}
+
+static void stm32_adc_core_switches_supply_dis(struct stm32_adc_priv *priv)
+{
+ if (priv->vdda_uv < 2700000) {
+ if (priv->syscfg && priv->vdd_uv > 2700000) {
+ regmap_write(priv->syscfg, STM32MP1_SYSCFG_PMCCLRR,
+ STM32MP1_SYSCFG_ANASWVDD_MASK);
+ regulator_disable(priv->vdd);
+ return;
+ }
+ if (priv->booster)
+ regulator_disable(priv->booster);
+ }
+}
+
+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->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vdda);
+ if (ret < 0) {
+ dev_err(dev, "vdda get voltage failed, %d\n", ret);
+ goto err_vdda_disable;
+ }
+ priv->vdda_uv = ret;
+
+ ret = stm32_adc_core_switches_supply_en(priv, dev);
+ if (ret < 0)
+ goto err_vdda_disable;
+
+ ret = regulator_enable(priv->vref);
+ if (ret < 0) {
+ dev_err(dev, "vref enable failed\n");
+ goto err_switches_dis;
+ }
+
+ 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);
+err_switches_dis:
+ stm32_adc_core_switches_supply_dis(priv);
+err_vdda_disable:
+ regulator_disable(priv->vdda);
+
+ 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);
+ stm32_adc_core_switches_supply_dis(priv);
+ regulator_disable(priv->vdda);
+}
+
+static int stm32_adc_core_switches_probe(struct device *dev,
+ struct stm32_adc_priv *priv)
+{
+ struct device_node *np = dev->of_node;
+ int ret;
+
+ /* Analog switches supply can be controlled by syscfg (optional) */
+ priv->syscfg = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
+ if (IS_ERR(priv->syscfg)) {
+ ret = PTR_ERR(priv->syscfg);
+ if (ret != -ENODEV) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Can't probe syscfg: %d\n", ret);
+ return ret;
+ }
+ priv->syscfg = NULL;
+ }
+
+ /* Booster can be used to supply analog switches (optional) */
+ if (priv->cfg->has_syscfg & HAS_VBOOSTER &&
+ of_property_read_bool(np, "booster-supply")) {
+ priv->booster = devm_regulator_get_optional(dev, "booster");
+ if (IS_ERR(priv->booster)) {
+ ret = PTR_ERR(priv->booster);
+ if (ret != -ENODEV) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "can't get booster %d\n",
+ ret);
+ return ret;
+ }
+ priv->booster = NULL;
+ }
+ }
+
+ /* Vdd can be used to supply analog switches (optional) */
+ if (priv->cfg->has_syscfg & HAS_ANASWVDD &&
+ of_property_read_bool(np, "vdd-supply")) {
+ priv->vdd = devm_regulator_get_optional(dev, "vdd");
+ if (IS_ERR(priv->vdd)) {
+ ret = PTR_ERR(priv->vdd);
+ if (ret != -ENODEV) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "can't get vdd %d\n", ret);
+ return ret;
+ }
+ priv->vdd = NULL;
+ }
+ }
+
+ if (priv->vdd) {
+ ret = regulator_enable(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd enable failed %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_get_voltage(priv->vdd);
+ if (ret < 0) {
+ dev_err(dev, "vdd get voltage failed %d\n", ret);
+ regulator_disable(priv->vdd);
+ return ret;
+ }
+ priv->vdd_uv = ret;
+
+ regulator_disable(priv->vdd);
+ }
+
+ return 0;
+}
+
static int stm32_adc_probe(struct platform_device *pdev)
{
struct stm32_adc_priv *priv;
@@ -393,6 +662,7 @@
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;
@@ -403,6 +673,14 @@
return PTR_ERR(priv->common.base);
priv->common.phys_base = res->start;
+ priv->vdda = devm_regulator_get(&pdev->dev, "vdda");
+ if (IS_ERR(priv->vdda)) {
+ ret = PTR_ERR(priv->vdda);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "vdda get failed, %d\n", ret);
+ return ret;
+ }
+
priv->vref = devm_regulator_get(&pdev->dev, "vref");
if (IS_ERR(priv->vref)) {
ret = PTR_ERR(priv->vref);
@@ -410,67 +688,55 @@
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;
+ return ret;
}
- }
-
- 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;
- }
+ 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;
- }
+ ret = stm32_adc_core_switches_probe(dev, priv);
+ if (ret)
+ return ret;
+
+ 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 +744,19 @@
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 +766,39 @@
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,
@@ -523,12 +809,14 @@
.regs = &stm32h7_adc_common_regs,
.clk_sel = stm32h7_adc_clk_sel,
.max_clk_rate_hz = 36000000,
+ .has_syscfg = HAS_VBOOSTER,
};
static const struct stm32_adc_priv_cfg stm32mp1_adc_priv_cfg = {
.regs = &stm32h7_adc_common_regs,
.clk_sel = stm32h7_adc_clk_sel,
.max_clk_rate_hz = 40000000,
+ .has_syscfg = HAS_VBOOSTER | HAS_ANASWVDD,
};
static const struct of_device_id stm32_adc_of_match[] = {
@@ -552,6 +840,7 @@
.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-core.h b/drivers/iio/adc/stm32-adc-core.h
index 8af507b..2579d51 100644
--- a/drivers/iio/adc/stm32-adc-core.h
+++ b/drivers/iio/adc/stm32-adc-core.h
@@ -25,8 +25,145 @@
* --------------------------------------------------------
*/
#define STM32_ADC_MAX_ADCS 3
+#define STM32_ADC_OFFSET 0x100
#define STM32_ADCX_COMN_OFFSET 0x300
+/* STM32F4 - Registers for each ADC instance */
+#define STM32F4_ADC_SR 0x00
+#define STM32F4_ADC_CR1 0x04
+#define STM32F4_ADC_CR2 0x08
+#define STM32F4_ADC_SMPR1 0x0C
+#define STM32F4_ADC_SMPR2 0x10
+#define STM32F4_ADC_HTR 0x24
+#define STM32F4_ADC_LTR 0x28
+#define STM32F4_ADC_SQR1 0x2C
+#define STM32F4_ADC_SQR2 0x30
+#define STM32F4_ADC_SQR3 0x34
+#define STM32F4_ADC_JSQR 0x38
+#define STM32F4_ADC_JDR1 0x3C
+#define STM32F4_ADC_JDR2 0x40
+#define STM32F4_ADC_JDR3 0x44
+#define STM32F4_ADC_JDR4 0x48
+#define STM32F4_ADC_DR 0x4C
+
+/* STM32F4 - common registers for all ADC instances: 1, 2 & 3 */
+#define STM32F4_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32F4_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x04)
+
+/* STM32F4_ADC_SR - bit fields */
+#define STM32F4_STRT BIT(4)
+#define STM32F4_EOC BIT(1)
+
+/* STM32F4_ADC_CR1 - bit fields */
+#define STM32F4_RES_SHIFT 24
+#define STM32F4_RES_MASK GENMASK(25, 24)
+#define STM32F4_SCAN BIT(8)
+#define STM32F4_EOCIE BIT(5)
+
+/* STM32F4_ADC_CR2 - bit fields */
+#define STM32F4_SWSTART BIT(30)
+#define STM32F4_EXTEN_SHIFT 28
+#define STM32F4_EXTEN_MASK GENMASK(29, 28)
+#define STM32F4_EXTSEL_SHIFT 24
+#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
+#define STM32F4_EOCS BIT(10)
+#define STM32F4_DDS BIT(9)
+#define STM32F4_DMA BIT(8)
+#define STM32F4_ADON BIT(0)
+
+/* STM32F4_ADC_CSR - bit fields */
+#define STM32F4_EOC3 BIT(17)
+#define STM32F4_EOC2 BIT(9)
+#define STM32F4_EOC1 BIT(1)
+
+/* STM32F4_ADC_CCR - bit fields */
+#define STM32F4_ADC_ADCPRE_SHIFT 16
+#define STM32F4_ADC_ADCPRE_MASK GENMASK(17, 16)
+
+/* STM32H7 - Registers for each ADC instance */
+#define STM32H7_ADC_ISR 0x00
+#define STM32H7_ADC_IER 0x04
+#define STM32H7_ADC_CR 0x08
+#define STM32H7_ADC_CFGR 0x0C
+#define STM32H7_ADC_SMPR1 0x14
+#define STM32H7_ADC_SMPR2 0x18
+#define STM32H7_ADC_PCSEL 0x1C
+#define STM32H7_ADC_SQR1 0x30
+#define STM32H7_ADC_SQR2 0x34
+#define STM32H7_ADC_SQR3 0x38
+#define STM32H7_ADC_SQR4 0x3C
+#define STM32H7_ADC_DR 0x40
+#define STM32H7_ADC_DIFSEL 0xC0
+#define STM32H7_ADC_CALFACT 0xC4
+#define STM32H7_ADC_CALFACT2 0xC8
+
+/* STM32H7 - common registers for all ADC instances */
+#define STM32H7_ADC_CSR (STM32_ADCX_COMN_OFFSET + 0x00)
+#define STM32H7_ADC_CCR (STM32_ADCX_COMN_OFFSET + 0x08)
+
+/* STM32H7_ADC_ISR - bit fields */
+#define STM32MP1_VREGREADY BIT(12)
+#define STM32H7_EOC BIT(2)
+#define STM32H7_ADRDY BIT(0)
+
+/* STM32H7_ADC_IER - bit fields */
+#define STM32H7_EOCIE STM32H7_EOC
+
+/* STM32H7_ADC_CR - bit fields */
+#define STM32H7_ADCAL BIT(31)
+#define STM32H7_ADCALDIF BIT(30)
+#define STM32H7_DEEPPWD BIT(29)
+#define STM32H7_ADVREGEN BIT(28)
+#define STM32H7_LINCALRDYW6 BIT(27)
+#define STM32H7_LINCALRDYW5 BIT(26)
+#define STM32H7_LINCALRDYW4 BIT(25)
+#define STM32H7_LINCALRDYW3 BIT(24)
+#define STM32H7_LINCALRDYW2 BIT(23)
+#define STM32H7_LINCALRDYW1 BIT(22)
+#define STM32H7_ADCALLIN BIT(16)
+#define STM32H7_BOOST BIT(8)
+#define STM32H7_ADSTP BIT(4)
+#define STM32H7_ADSTART BIT(2)
+#define STM32H7_ADDIS BIT(1)
+#define STM32H7_ADEN BIT(0)
+
+/* STM32H7_ADC_CFGR bit fields */
+#define STM32H7_EXTEN_SHIFT 10
+#define STM32H7_EXTEN_MASK GENMASK(11, 10)
+#define STM32H7_EXTSEL_SHIFT 5
+#define STM32H7_EXTSEL_MASK GENMASK(9, 5)
+#define STM32H7_RES_SHIFT 2
+#define STM32H7_RES_MASK GENMASK(4, 2)
+#define STM32H7_DMNGT_SHIFT 0
+#define STM32H7_DMNGT_MASK GENMASK(1, 0)
+
+enum stm32h7_adc_dmngt {
+ STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */
+ STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */
+ STM32H7_DMNGT_DFSDM, /* DFSDM mode */
+ STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */
+};
+
+/* STM32H7_ADC_CALFACT - bit fields */
+#define STM32H7_CALFACT_D_SHIFT 16
+#define STM32H7_CALFACT_D_MASK GENMASK(26, 16)
+#define STM32H7_CALFACT_S_SHIFT 0
+#define STM32H7_CALFACT_S_MASK GENMASK(10, 0)
+
+/* STM32H7_ADC_CALFACT2 - bit fields */
+#define STM32H7_LINCALFACT_SHIFT 0
+#define STM32H7_LINCALFACT_MASK GENMASK(29, 0)
+
+/* STM32H7_ADC_CSR - bit fields */
+#define STM32H7_EOC_SLV BIT(18)
+#define STM32H7_EOC_MST BIT(2)
+
+/* STM32H7_ADC_CCR - bit fields */
+#define STM32H7_PRESC_SHIFT 18
+#define STM32H7_PRESC_MASK GENMASK(21, 18)
+#define STM32H7_CKMODE_SHIFT 16
+#define STM32H7_CKMODE_MASK GENMASK(17, 16)
+
/**
* struct stm32_adc_common - stm32 ADC driver common data (for all instances)
* @base: control registers base cpu addr
diff --git a/drivers/iio/adc/stm32-adc.c b/drivers/iio/adc/stm32-adc.c
index 3784118..73aee59 100644
--- a/drivers/iio/adc/stm32-adc.c
+++ b/drivers/iio/adc/stm32-adc.c
@@ -22,120 +22,12 @@
#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>
#include "stm32-adc-core.h"
-/* STM32F4 - Registers for each ADC instance */
-#define STM32F4_ADC_SR 0x00
-#define STM32F4_ADC_CR1 0x04
-#define STM32F4_ADC_CR2 0x08
-#define STM32F4_ADC_SMPR1 0x0C
-#define STM32F4_ADC_SMPR2 0x10
-#define STM32F4_ADC_HTR 0x24
-#define STM32F4_ADC_LTR 0x28
-#define STM32F4_ADC_SQR1 0x2C
-#define STM32F4_ADC_SQR2 0x30
-#define STM32F4_ADC_SQR3 0x34
-#define STM32F4_ADC_JSQR 0x38
-#define STM32F4_ADC_JDR1 0x3C
-#define STM32F4_ADC_JDR2 0x40
-#define STM32F4_ADC_JDR3 0x44
-#define STM32F4_ADC_JDR4 0x48
-#define STM32F4_ADC_DR 0x4C
-
-/* STM32F4_ADC_SR - bit fields */
-#define STM32F4_STRT BIT(4)
-#define STM32F4_EOC BIT(1)
-
-/* STM32F4_ADC_CR1 - bit fields */
-#define STM32F4_RES_SHIFT 24
-#define STM32F4_RES_MASK GENMASK(25, 24)
-#define STM32F4_SCAN BIT(8)
-#define STM32F4_EOCIE BIT(5)
-
-/* STM32F4_ADC_CR2 - bit fields */
-#define STM32F4_SWSTART BIT(30)
-#define STM32F4_EXTEN_SHIFT 28
-#define STM32F4_EXTEN_MASK GENMASK(29, 28)
-#define STM32F4_EXTSEL_SHIFT 24
-#define STM32F4_EXTSEL_MASK GENMASK(27, 24)
-#define STM32F4_EOCS BIT(10)
-#define STM32F4_DDS BIT(9)
-#define STM32F4_DMA BIT(8)
-#define STM32F4_ADON BIT(0)
-
-/* STM32H7 - Registers for each ADC instance */
-#define STM32H7_ADC_ISR 0x00
-#define STM32H7_ADC_IER 0x04
-#define STM32H7_ADC_CR 0x08
-#define STM32H7_ADC_CFGR 0x0C
-#define STM32H7_ADC_SMPR1 0x14
-#define STM32H7_ADC_SMPR2 0x18
-#define STM32H7_ADC_PCSEL 0x1C
-#define STM32H7_ADC_SQR1 0x30
-#define STM32H7_ADC_SQR2 0x34
-#define STM32H7_ADC_SQR3 0x38
-#define STM32H7_ADC_SQR4 0x3C
-#define STM32H7_ADC_DR 0x40
-#define STM32H7_ADC_DIFSEL 0xC0
-#define STM32H7_ADC_CALFACT 0xC4
-#define STM32H7_ADC_CALFACT2 0xC8
-
-/* STM32H7_ADC_ISR - bit fields */
-#define STM32MP1_VREGREADY BIT(12)
-#define STM32H7_EOC BIT(2)
-#define STM32H7_ADRDY BIT(0)
-
-/* STM32H7_ADC_IER - bit fields */
-#define STM32H7_EOCIE STM32H7_EOC
-
-/* STM32H7_ADC_CR - bit fields */
-#define STM32H7_ADCAL BIT(31)
-#define STM32H7_ADCALDIF BIT(30)
-#define STM32H7_DEEPPWD BIT(29)
-#define STM32H7_ADVREGEN BIT(28)
-#define STM32H7_LINCALRDYW6 BIT(27)
-#define STM32H7_LINCALRDYW5 BIT(26)
-#define STM32H7_LINCALRDYW4 BIT(25)
-#define STM32H7_LINCALRDYW3 BIT(24)
-#define STM32H7_LINCALRDYW2 BIT(23)
-#define STM32H7_LINCALRDYW1 BIT(22)
-#define STM32H7_ADCALLIN BIT(16)
-#define STM32H7_BOOST BIT(8)
-#define STM32H7_ADSTP BIT(4)
-#define STM32H7_ADSTART BIT(2)
-#define STM32H7_ADDIS BIT(1)
-#define STM32H7_ADEN BIT(0)
-
-/* STM32H7_ADC_CFGR bit fields */
-#define STM32H7_EXTEN_SHIFT 10
-#define STM32H7_EXTEN_MASK GENMASK(11, 10)
-#define STM32H7_EXTSEL_SHIFT 5
-#define STM32H7_EXTSEL_MASK GENMASK(9, 5)
-#define STM32H7_RES_SHIFT 2
-#define STM32H7_RES_MASK GENMASK(4, 2)
-#define STM32H7_DMNGT_SHIFT 0
-#define STM32H7_DMNGT_MASK GENMASK(1, 0)
-
-enum stm32h7_adc_dmngt {
- STM32H7_DMNGT_DR_ONLY, /* Regular data in DR only */
- STM32H7_DMNGT_DMA_ONESHOT, /* DMA one shot mode */
- STM32H7_DMNGT_DFSDM, /* DFSDM mode */
- STM32H7_DMNGT_DMA_CIRC, /* DMA circular mode */
-};
-
-/* STM32H7_ADC_CALFACT - bit fields */
-#define STM32H7_CALFACT_D_SHIFT 16
-#define STM32H7_CALFACT_D_MASK GENMASK(26, 16)
-#define STM32H7_CALFACT_S_SHIFT 0
-#define STM32H7_CALFACT_S_MASK GENMASK(10, 0)
-
-/* STM32H7_ADC_CALFACT2 - bit fields */
-#define STM32H7_LINCALFACT_SHIFT 0
-#define STM32H7_LINCALFACT_MASK GENMASK(29, 0)
-
/* Number of linear calibration shadow registers / LINCALRDYW control bits */
#define STM32H7_LINCALFACT_NUM 6
@@ -148,6 +40,7 @@
#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 +92,13 @@
* @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 +146,6 @@
* @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 +158,6 @@
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 +516,47 @@
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 +711,7 @@
/**
* 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 +719,6 @@
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 +731,7 @@
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 +747,9 @@
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 +826,9 @@
#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 +836,8 @@
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 +854,7 @@
STM32H7_ADC_CALIB_TIMEOUT_US);
if (ret) {
dev_err(&indio_dev->dev, "calibration failed\n");
- goto pwr_dwn;
+ goto out;
}
/*
@@ -944,18 +871,13 @@
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 +894,28 @@
*/
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 +1105,7 @@
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 +1115,10 @@
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 +1156,8 @@
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 +1265,22 @@
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 +1310,23 @@
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;
}
@@ -1449,7 +1399,7 @@
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
if (ret) {
- dmaengine_terminate_all(adc->dma_chan);
+ dmaengine_terminate_sync(adc->dma_chan);
return ret;
}
@@ -1459,21 +1409,22 @@
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 +1433,6 @@
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 +1443,58 @@
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)
+{
+ 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);
- int ret;
+ 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);
+ dmaengine_terminate_sync(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;
}
@@ -1844,10 +1810,8 @@
}
adc->irq = platform_get_irq(pdev, 0);
- if (adc->irq < 0) {
- dev_err(&pdev->dev, "failed to get irq\n");
+ if (adc->irq < 0)
return adc->irq;
- }
ret = devm_request_irq(&pdev->dev, adc->irq, stm32_adc_isr,
0, pdev->name, adc);
@@ -1867,32 +1831,17 @@
}
}
- 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 +1852,35 @@
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 +1890,6 @@
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 +1899,12 @@
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 +1912,62 @@
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 +1982,6 @@
.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 +1994,6 @@
.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 +2015,7 @@
.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/stm32-dfsdm-adc.c b/drivers/iio/adc/stm32-dfsdm-adc.c
index fcd4a1c..e493242 100644
--- a/drivers/iio/adc/stm32-dfsdm-adc.c
+++ b/drivers/iio/adc/stm32-dfsdm-adc.c
@@ -12,6 +12,11 @@
#include <linux/iio/buffer.h>
#include <linux/iio/hw-consumer.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/timer/stm32-lptim-trigger.h>
+#include <linux/iio/timer/stm32-timer-trigger.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
@@ -34,9 +39,21 @@
#define DFSDM_MAX_INT_OVERSAMPLING 256
#define DFSDM_MAX_FL_OVERSAMPLING 1024
-/* Max sample resolutions */
-#define DFSDM_MAX_RES BIT(31)
-#define DFSDM_DATA_RES BIT(23)
+/* Limit filter output resolution to 31 bits. (i.e. sample range is +/-2^30) */
+#define DFSDM_DATA_MAX BIT(30)
+/*
+ * Data are output as two's complement data in a 24 bit field.
+ * Data from filters are in the range +/-2^(n-1)
+ * 2^(n-1) maximum positive value cannot be coded in 2's complement n bits
+ * An extra bit is required to avoid wrap-around of the binary code for 2^(n-1)
+ * So, the resolution of samples from filter is actually limited to 23 bits
+ */
+#define DFSDM_DATA_RES 24
+
+/* Filter configuration */
+#define DFSDM_CR1_CFG_MASK (DFSDM_CR1_RCH_MASK | DFSDM_CR1_RCONT_MASK | \
+ DFSDM_CR1_RSYNC_MASK | DFSDM_CR1_JSYNC_MASK | \
+ DFSDM_CR1_JSCAN_MASK)
enum sd_converter_type {
DFSDM_AUDIO,
@@ -54,6 +71,8 @@
struct stm32_dfsdm *dfsdm;
const struct stm32_dfsdm_dev_data *dev_data;
unsigned int fl_id;
+ unsigned int nconv;
+ unsigned long smask;
/* ADC specific */
unsigned int oversamp;
@@ -114,14 +133,70 @@
return -EINVAL;
}
-static int stm32_dfsdm_set_osrs(struct stm32_dfsdm_filter *fl,
- unsigned int fast, unsigned int oversamp)
+/**
+ * struct stm32_dfsdm_trig_info - DFSDM trigger info
+ * @name: name of the trigger, corresponding to its source
+ * @jextsel: trigger signal selection
+ */
+struct stm32_dfsdm_trig_info {
+ const char *name;
+ unsigned int jextsel;
+};
+
+/* hardware injected trigger enable, edge selection */
+enum stm32_dfsdm_jexten {
+ STM32_DFSDM_JEXTEN_DISABLED,
+ STM32_DFSDM_JEXTEN_RISING_EDGE,
+ STM32_DFSDM_JEXTEN_FALLING_EDGE,
+ STM32_DFSDM_EXTEN_BOTH_EDGES,
+};
+
+static const struct stm32_dfsdm_trig_info stm32_dfsdm_trigs[] = {
+ { TIM1_TRGO, 0 },
+ { TIM1_TRGO2, 1 },
+ { TIM8_TRGO, 2 },
+ { TIM8_TRGO2, 3 },
+ { TIM3_TRGO, 4 },
+ { TIM4_TRGO, 5 },
+ { TIM16_OC1, 6 },
+ { TIM6_TRGO, 7 },
+ { TIM7_TRGO, 8 },
+ { LPTIM1_OUT, 26 },
+ { LPTIM2_OUT, 27 },
+ { LPTIM3_OUT, 28 },
+ {},
+};
+
+static int stm32_dfsdm_get_jextsel(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ int i;
+
+ /* lookup triggers registered by stm32 timer trigger driver */
+ for (i = 0; stm32_dfsdm_trigs[i].name; i++) {
+ /**
+ * Checking both stm32 timer trigger type and trig name
+ * should be safe against arbitrary trigger names.
+ */
+ if ((is_stm32_timer_trigger(trig) ||
+ is_stm32_lptim_trigger(trig)) &&
+ !strcmp(stm32_dfsdm_trigs[i].name, trig->name)) {
+ return stm32_dfsdm_trigs[i].jextsel;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int stm32_dfsdm_compute_osrs(struct stm32_dfsdm_filter *fl,
+ unsigned int fast, unsigned int oversamp)
{
unsigned int i, d, fosr, iosr;
- u64 res;
- s64 delta;
+ u64 res, max;
+ int bits, shift;
unsigned int m = 1; /* multiplication factor */
unsigned int p = fl->ford; /* filter order (ford) */
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fast];
pr_debug("%s: Requested oversampling: %d\n", __func__, oversamp);
/*
@@ -140,11 +215,8 @@
/*
* Look for filter and integrator oversampling ratios which allows
- * to reach 24 bits data output resolution.
- * Leave as soon as if exact resolution if reached.
- * Otherwise the higher resolution below 32 bits is kept.
+ * to maximize data output resolution.
*/
- fl->res = 0;
for (fosr = 1; fosr <= DFSDM_MAX_FL_OVERSAMPLING; fosr++) {
for (iosr = 1; iosr <= DFSDM_MAX_INT_OVERSAMPLING; iosr++) {
if (fast)
@@ -169,50 +241,128 @@
res = fosr;
for (i = p - 1; i > 0; i--) {
res = res * (u64)fosr;
- if (res > DFSDM_MAX_RES)
+ if (res > DFSDM_DATA_MAX)
break;
}
- if (res > DFSDM_MAX_RES)
+ if (res > DFSDM_DATA_MAX)
continue;
+
res = res * (u64)m * (u64)iosr;
- if (res > DFSDM_MAX_RES)
+ if (res > DFSDM_DATA_MAX)
continue;
- delta = res - DFSDM_DATA_RES;
+ if (res >= flo->res) {
+ flo->res = res;
+ flo->fosr = fosr;
+ flo->iosr = iosr;
- if (res >= fl->res) {
- fl->res = res;
- fl->fosr = fosr;
- fl->iosr = iosr;
- fl->fast = fast;
- pr_debug("%s: fosr = %d, iosr = %d\n",
- __func__, fl->fosr, fl->iosr);
+ bits = fls(flo->res);
+ /* 8 LBSs in data register contain chan info */
+ max = flo->res << 8;
+
+ /* if resolution is not a power of two */
+ if (flo->res > BIT(bits - 1))
+ bits++;
+ else
+ max--;
+
+ shift = DFSDM_DATA_RES - bits;
+ /*
+ * Compute right/left shift
+ * Right shift is performed by hardware
+ * when transferring samples to data register.
+ * Left shift is done by software on buffer
+ */
+ if (shift > 0) {
+ /* Resolution is lower than 24 bits */
+ flo->rshift = 0;
+ flo->lshift = shift;
+ } else {
+ /*
+ * If resolution is 24 bits or more,
+ * max positive value may be ambiguous
+ * (equal to max negative value as sign
+ * bit is dropped).
+ * Reduce resolution to 23 bits (rshift)
+ * to keep the sign on bit 23 and treat
+ * saturation before rescaling on 24
+ * bits (lshift).
+ */
+ flo->rshift = 1 - shift;
+ flo->lshift = 1;
+ max >>= flo->rshift;
+ }
+ flo->max = (s32)max;
+
+ pr_debug("%s: fast %d, fosr %d, iosr %d, res 0x%llx/%d bits, rshift %d, lshift %d\n",
+ __func__, fast, flo->fosr, flo->iosr,
+ flo->res, bits, flo->rshift,
+ flo->lshift);
}
-
- if (!delta)
- return 0;
}
}
- if (!fl->res)
+ if (!flo->res)
return -EINVAL;
return 0;
}
-static int stm32_dfsdm_start_channel(struct stm32_dfsdm *dfsdm,
- unsigned int ch_id)
+static int stm32_dfsdm_compute_all_osrs(struct iio_dev *indio_dev,
+ unsigned int oversamp)
{
- return regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
- DFSDM_CHCFGR1_CHEN_MASK,
- DFSDM_CHCFGR1_CHEN(1));
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ int ret0, ret1;
+
+ memset(&fl->flo[0], 0, sizeof(fl->flo[0]));
+ memset(&fl->flo[1], 0, sizeof(fl->flo[1]));
+
+ ret0 = stm32_dfsdm_compute_osrs(fl, 0, oversamp);
+ ret1 = stm32_dfsdm_compute_osrs(fl, 1, oversamp);
+ if (ret0 < 0 && ret1 < 0) {
+ dev_err(&indio_dev->dev,
+ "Filter parameters not found: errors %d/%d\n",
+ ret0, ret1);
+ return -EINVAL;
+ }
+
+ return 0;
}
-static void stm32_dfsdm_stop_channel(struct stm32_dfsdm *dfsdm,
- unsigned int ch_id)
+static int stm32_dfsdm_start_channel(struct stm32_dfsdm_adc *adc)
{
- regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(ch_id),
- DFSDM_CHCFGR1_CHEN_MASK, DFSDM_CHCFGR1_CHEN(0));
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+ int ret;
+
+ for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ ret = regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
+ DFSDM_CHCFGR1_CHEN_MASK,
+ DFSDM_CHCFGR1_CHEN(1));
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static void stm32_dfsdm_stop_channel(struct stm32_dfsdm_adc *adc)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+
+ for_each_set_bit(bit, &adc->smask, sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ regmap_update_bits(regmap, DFSDM_CHCFGR1(chan->channel),
+ DFSDM_CHCFGR1_CHEN_MASK,
+ DFSDM_CHCFGR1_CHEN(0));
+ }
}
static int stm32_dfsdm_chan_configure(struct stm32_dfsdm *dfsdm,
@@ -237,9 +387,11 @@
DFSDM_CHCFGR1_CHINSEL(ch->alt_si));
}
-static int stm32_dfsdm_start_filter(struct stm32_dfsdm *dfsdm,
- unsigned int fl_id)
+static int stm32_dfsdm_start_filter(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
{
+ struct stm32_dfsdm *dfsdm = adc->dfsdm;
int ret;
/* Enable filter */
@@ -248,7 +400,11 @@
if (ret < 0)
return ret;
- /* Start conversion */
+ /* Nothing more to do for injected (scan mode/triggered) conversions */
+ if (adc->nconv > 1 || trig)
+ return 0;
+
+ /* Software start (single or continuous) regular conversion */
return regmap_update_bits(dfsdm->regmap, DFSDM_CR1(fl_id),
DFSDM_CR1_RSWSTART_MASK,
DFSDM_CR1_RSWSTART(1));
@@ -262,22 +418,101 @@
DFSDM_CR1_DFEN_MASK, DFSDM_CR1_DFEN(0));
}
-static int stm32_dfsdm_filter_configure(struct stm32_dfsdm *dfsdm,
- unsigned int fl_id, unsigned int ch_id)
+static int stm32_dfsdm_filter_set_trig(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
{
- struct regmap *regmap = dfsdm->regmap;
- struct stm32_dfsdm_filter *fl = &dfsdm->fl_list[fl_id];
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ u32 jextsel = 0, jexten = STM32_DFSDM_JEXTEN_DISABLED;
+ int ret;
+
+ if (trig) {
+ ret = stm32_dfsdm_get_jextsel(indio_dev, trig);
+ if (ret < 0)
+ return ret;
+
+ /* set trigger source and polarity (default to rising edge) */
+ jextsel = ret;
+ jexten = STM32_DFSDM_JEXTEN_RISING_EDGE;
+ }
+
+ ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_JEXTSEL_MASK | DFSDM_CR1_JEXTEN_MASK,
+ DFSDM_CR1_JEXTSEL(jextsel) |
+ DFSDM_CR1_JEXTEN(jexten));
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static int stm32_dfsdm_channels_configure(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[0];
+ const struct iio_chan_spec *chan;
+ unsigned int bit;
+ int ret;
+
+ fl->fast = 0;
+
+ /*
+ * In continuous mode, use fast mode configuration,
+ * if it provides a better resolution.
+ */
+ if (adc->nconv == 1 && !trig &&
+ (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)) {
+ if (fl->flo[1].res >= fl->flo[0].res) {
+ fl->fast = 1;
+ flo = &fl->flo[1];
+ }
+ }
+
+ if (!flo->res)
+ return -EINVAL;
+
+ for_each_set_bit(bit, &adc->smask,
+ sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+
+ ret = regmap_update_bits(regmap,
+ DFSDM_CHCFGR2(chan->channel),
+ DFSDM_CHCFGR2_DTRBS_MASK,
+ DFSDM_CHCFGR2_DTRBS(flo->rshift));
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stm32_dfsdm_filter_configure(struct stm32_dfsdm_adc *adc,
+ unsigned int fl_id,
+ struct iio_trigger *trig)
+{
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ struct regmap *regmap = adc->dfsdm->regmap;
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ u32 cr1;
+ const struct iio_chan_spec *chan;
+ unsigned int bit, jchg = 0;
int ret;
/* Average integrator oversampling */
ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_IOSR_MASK,
- DFSDM_FCR_IOSR(fl->iosr - 1));
+ DFSDM_FCR_IOSR(flo->iosr - 1));
if (ret)
return ret;
/* Filter order and Oversampling */
ret = regmap_update_bits(regmap, DFSDM_FCR(fl_id), DFSDM_FCR_FOSR_MASK,
- DFSDM_FCR_FOSR(fl->fosr - 1));
+ DFSDM_FCR_FOSR(flo->fosr - 1));
if (ret)
return ret;
@@ -286,15 +521,74 @@
if (ret)
return ret;
- /* No scan mode supported for the moment */
- ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_RCH_MASK,
- DFSDM_CR1_RCH(ch_id));
+ ret = stm32_dfsdm_filter_set_trig(adc, fl_id, trig);
if (ret)
return ret;
- return regmap_update_bits(regmap, DFSDM_CR1(fl_id),
- DFSDM_CR1_RSYNC_MASK,
- DFSDM_CR1_RSYNC(fl->sync_mode));
+ ret = regmap_update_bits(regmap, DFSDM_CR1(fl_id),
+ DFSDM_CR1_FAST_MASK,
+ DFSDM_CR1_FAST(fl->fast));
+ if (ret)
+ return ret;
+
+ /*
+ * DFSDM modes configuration W.R.T audio/iio type modes
+ * ----------------------------------------------------------------
+ * Modes | regular | regular | injected | injected |
+ * | | continuous | | + scan |
+ * --------------|---------|--------------|----------|------------|
+ * single conv | x | | | |
+ * (1 chan) | | | | |
+ * --------------|---------|--------------|----------|------------|
+ * 1 Audio chan | | sample freq | | |
+ * | | or sync_mode | | |
+ * --------------|---------|--------------|----------|------------|
+ * 1 IIO chan | | sample freq | trigger | |
+ * | | or sync_mode | | |
+ * --------------|---------|--------------|----------|------------|
+ * 2+ IIO chans | | | | trigger or |
+ * | | | | sync_mode |
+ * ----------------------------------------------------------------
+ */
+ if (adc->nconv == 1 && !trig) {
+ bit = __ffs(adc->smask);
+ chan = indio_dev->channels + bit;
+
+ /* Use regular conversion for single channel without trigger */
+ cr1 = DFSDM_CR1_RCH(chan->channel);
+
+ /* Continuous conversions triggered by SPI clk in buffer mode */
+ if (indio_dev->currentmode & INDIO_BUFFER_SOFTWARE)
+ cr1 |= DFSDM_CR1_RCONT(1);
+
+ cr1 |= DFSDM_CR1_RSYNC(fl->sync_mode);
+ } else {
+ /* Use injected conversion for multiple channels */
+ for_each_set_bit(bit, &adc->smask,
+ sizeof(adc->smask) * BITS_PER_BYTE) {
+ chan = indio_dev->channels + bit;
+ jchg |= BIT(chan->channel);
+ }
+ ret = regmap_write(regmap, DFSDM_JCHGR(fl_id), jchg);
+ if (ret < 0)
+ return ret;
+
+ /* Use scan mode for multiple channels */
+ cr1 = DFSDM_CR1_JSCAN((adc->nconv > 1) ? 1 : 0);
+
+ /*
+ * Continuous conversions not supported in injected mode,
+ * either use:
+ * - conversions in sync with filter 0
+ * - triggered conversions
+ */
+ if (!fl->sync_mode && !trig)
+ return -EINVAL;
+ cr1 |= DFSDM_CR1_JSYNC(fl->sync_mode);
+ }
+
+ return regmap_update_bits(regmap, DFSDM_CR1(fl_id), DFSDM_CR1_CFG_MASK,
+ cr1);
}
static int stm32_dfsdm_channel_parse_of(struct stm32_dfsdm *dfsdm,
@@ -378,13 +672,36 @@
return snprintf(buf, PAGE_SIZE, "%d\n", adc->spi_freq);
}
+static int dfsdm_adc_set_samp_freq(struct iio_dev *indio_dev,
+ unsigned int sample_freq,
+ unsigned int spi_freq)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ unsigned int oversamp;
+ int ret;
+
+ oversamp = DIV_ROUND_CLOSEST(spi_freq, sample_freq);
+ if (spi_freq % sample_freq)
+ dev_dbg(&indio_dev->dev,
+ "Rate not accurate. requested (%u), actual (%u)\n",
+ sample_freq, spi_freq / oversamp);
+
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, oversamp);
+ if (ret < 0)
+ return ret;
+
+ adc->sample_freq = spi_freq / oversamp;
+ adc->oversamp = oversamp;
+
+ return 0;
+}
+
static ssize_t dfsdm_adc_audio_set_spiclk(struct iio_dev *indio_dev,
uintptr_t priv,
const struct iio_chan_spec *chan,
const char *buf, size_t len)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
unsigned int sample_freq = adc->sample_freq;
unsigned int spi_freq;
@@ -403,17 +720,9 @@
return -EINVAL;
if (sample_freq) {
- if (spi_freq % sample_freq)
- dev_warn(&indio_dev->dev,
- "Sampling rate not accurate (%d)\n",
- spi_freq / (spi_freq / sample_freq));
-
- ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / sample_freq));
- if (ret < 0) {
- dev_err(&indio_dev->dev,
- "No filter parameters that match!\n");
+ ret = dfsdm_adc_set_samp_freq(indio_dev, sample_freq, spi_freq);
+ if (ret < 0)
return ret;
- }
}
adc->spi_freq = spi_freq;
@@ -421,72 +730,48 @@
}
static int stm32_dfsdm_start_conv(struct stm32_dfsdm_adc *adc,
- const struct iio_chan_spec *chan,
- bool dma)
+ struct iio_trigger *trig)
{
struct regmap *regmap = adc->dfsdm->regmap;
int ret;
- unsigned int dma_en = 0, cont_en = 0;
- ret = stm32_dfsdm_start_channel(adc->dfsdm, chan->channel);
+ ret = stm32_dfsdm_channels_configure(adc, adc->fl_id, trig);
if (ret < 0)
return ret;
- ret = stm32_dfsdm_filter_configure(adc->dfsdm, adc->fl_id,
- chan->channel);
+ ret = stm32_dfsdm_start_channel(adc);
+ if (ret < 0)
+ return ret;
+
+ ret = stm32_dfsdm_filter_configure(adc, adc->fl_id, trig);
if (ret < 0)
goto stop_channels;
- if (dma) {
- /* Enable DMA transfer*/
- dma_en = DFSDM_CR1_RDMAEN(1);
- /* Enable conversion triggered by SPI clock*/
- cont_en = DFSDM_CR1_RCONT(1);
- }
- /* Enable DMA transfer*/
- ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, dma_en);
+ ret = stm32_dfsdm_start_filter(adc, adc->fl_id, trig);
if (ret < 0)
- goto stop_channels;
-
- /* Enable conversion triggered by SPI clock*/
- ret = regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, cont_en);
- if (ret < 0)
- goto stop_channels;
-
- ret = stm32_dfsdm_start_filter(adc->dfsdm, adc->fl_id);
- if (ret < 0)
- goto stop_channels;
+ goto filter_unconfigure;
return 0;
+filter_unconfigure:
+ regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_CFG_MASK, 0);
stop_channels:
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, 0);
-
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, 0);
- stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
+ stm32_dfsdm_stop_channel(adc);
return ret;
}
-static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc,
- const struct iio_chan_spec *chan)
+static void stm32_dfsdm_stop_conv(struct stm32_dfsdm_adc *adc)
{
struct regmap *regmap = adc->dfsdm->regmap;
stm32_dfsdm_stop_filter(adc->dfsdm, adc->fl_id);
- /* Clean conversion options */
regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RDMAEN_MASK, 0);
+ DFSDM_CR1_CFG_MASK, 0);
- regmap_update_bits(regmap, DFSDM_CR1(adc->fl_id),
- DFSDM_CR1_RCONT_MASK, 0);
-
- stm32_dfsdm_stop_channel(adc->dfsdm, chan->channel);
+ stm32_dfsdm_stop_channel(adc);
}
static int stm32_dfsdm_set_watermark(struct iio_dev *indio_dev,
@@ -494,6 +779,7 @@
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
unsigned int watermark = DFSDM_DMA_BUFFER_SIZE / 2;
+ unsigned int rx_buf_sz = DFSDM_DMA_BUFFER_SIZE;
/*
* DMA cyclic transfers are used, buffer is split into two periods.
@@ -502,7 +788,7 @@
* - one buffer (period) driver pushed to ASoC side.
*/
watermark = min(watermark, val * (unsigned int)(sizeof(u32)));
- adc->buf_sz = watermark * 2;
+ adc->buf_sz = min(rx_buf_sz, watermark * 2 * adc->nconv);
return 0;
}
@@ -532,13 +818,67 @@
return 0;
}
-static void stm32_dfsdm_audio_dma_buffer_done(void *data)
+static inline void stm32_dfsdm_process_data(struct stm32_dfsdm_adc *adc,
+ s32 *buffer)
+{
+ struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
+ struct stm32_dfsdm_filter_osr *flo = &fl->flo[fl->fast];
+ unsigned int i = adc->nconv;
+ s32 *ptr = buffer;
+
+ while (i--) {
+ /* Mask 8 LSB that contains the channel ID */
+ *ptr &= 0xFFFFFF00;
+ /* Convert 2^(n-1) sample to 2^(n-1)-1 to avoid wrap-around */
+ if (*ptr > flo->max)
+ *ptr -= 1;
+ /*
+ * Samples from filter are retrieved with 23 bits resolution
+ * or less. Shift left to align MSB on 24 bits.
+ */
+ *ptr <<= flo->lshift;
+
+ ptr++;
+ }
+}
+
+static irqreturn_t stm32_dfsdm_adc_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ int available = stm32_dfsdm_adc_dma_residue(adc);
+
+ while (available >= indio_dev->scan_bytes) {
+ s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];
+
+ stm32_dfsdm_process_data(adc, buffer);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ pf->timestamp);
+ available -= indio_dev->scan_bytes;
+ adc->bufi += indio_dev->scan_bytes;
+ if (adc->bufi >= adc->buf_sz)
+ adc->bufi = 0;
+ }
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static void stm32_dfsdm_dma_buffer_done(void *data)
{
struct iio_dev *indio_dev = data;
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
int available = stm32_dfsdm_adc_dma_residue(adc);
size_t old_pos;
+ if (indio_dev->currentmode & INDIO_BUFFER_TRIGGERED) {
+ iio_trigger_poll_chained(indio_dev->trig);
+ return;
+ }
+
/*
* FIXME: In Kernel interface does not support cyclic DMA buffer,and
* offers only an interface to push data samples per samples.
@@ -553,10 +893,10 @@
old_pos = adc->bufi;
while (available >= indio_dev->scan_bytes) {
- u32 *buffer = (u32 *)&adc->rx_buf[adc->bufi];
+ s32 *buffer = (s32 *)&adc->rx_buf[adc->bufi];
- /* Mask 8 LSB that contains the channel ID */
- *buffer = (*buffer & 0xFFFFFF00) << 8;
+ stm32_dfsdm_process_data(adc, buffer);
+
available -= indio_dev->scan_bytes;
adc->bufi += indio_dev->scan_bytes;
if (adc->bufi >= adc->buf_sz) {
@@ -566,6 +906,9 @@
adc->bufi = 0;
old_pos = 0;
}
+ /* regular iio buffer without trigger */
+ if (adc->dev_data->type == DFSDM_IIO)
+ iio_push_to_buffers(indio_dev, buffer);
}
if (adc->cb)
adc->cb(&adc->rx_buf[old_pos], adc->bufi - old_pos,
@@ -575,6 +918,15 @@
static int stm32_dfsdm_adc_dma_start(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+ /*
+ * The DFSDM supports half-word transfers. However, for 16 bits record,
+ * 4 bytes buswidth is kept, to avoid losing samples LSBs when left
+ * shift is required.
+ */
+ struct dma_slave_config config = {
+ .src_addr = (dma_addr_t)adc->dfsdm->phys_base,
+ .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+ };
struct dma_async_tx_descriptor *desc;
dma_cookie_t cookie;
int ret;
@@ -585,6 +937,14 @@
dev_dbg(&indio_dev->dev, "%s size=%d watermark=%d\n", __func__,
adc->buf_sz, adc->buf_sz / 2);
+ if (adc->nconv == 1 && !indio_dev->trig)
+ config.src_addr += DFSDM_RDATAR(adc->fl_id);
+ else
+ config.src_addr += DFSDM_JDATAR(adc->fl_id);
+ ret = dmaengine_slave_config(adc->dma_chan, &config);
+ if (ret)
+ return ret;
+
/* Prepare a DMA cyclic transaction */
desc = dmaengine_prep_dma_cyclic(adc->dma_chan,
adc->dma_buf,
@@ -594,71 +954,154 @@
if (!desc)
return -EBUSY;
- desc->callback = stm32_dfsdm_audio_dma_buffer_done;
+ desc->callback = stm32_dfsdm_dma_buffer_done;
desc->callback_param = indio_dev;
cookie = dmaengine_submit(desc);
ret = dma_submit_error(cookie);
- if (ret) {
- dmaengine_terminate_all(adc->dma_chan);
- return ret;
- }
+ if (ret)
+ goto err_stop_dma;
/* Issue pending DMA requests */
dma_async_issue_pending(adc->dma_chan);
+ if (adc->nconv == 1 && !indio_dev->trig) {
+ /* Enable regular DMA transfer*/
+ ret = regmap_update_bits(adc->dfsdm->regmap,
+ DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_RDMAEN_MASK,
+ DFSDM_CR1_RDMAEN_MASK);
+ } else {
+ /* Enable injected DMA transfer*/
+ ret = regmap_update_bits(adc->dfsdm->regmap,
+ DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_JDMAEN_MASK,
+ DFSDM_CR1_JDMAEN_MASK);
+ }
+
+ if (ret < 0)
+ goto err_stop_dma;
+
+ return 0;
+
+err_stop_dma:
+ dmaengine_terminate_all(adc->dma_chan);
+
+ return ret;
+}
+
+static void stm32_dfsdm_adc_dma_stop(struct iio_dev *indio_dev)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ if (!adc->dma_chan)
+ return;
+
+ regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR1(adc->fl_id),
+ DFSDM_CR1_RDMAEN_MASK | DFSDM_CR1_JDMAEN_MASK, 0);
+ dmaengine_terminate_all(adc->dma_chan);
+}
+
+static int stm32_dfsdm_update_scan_mode(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
+
+ adc->nconv = bitmap_weight(scan_mask, indio_dev->masklength);
+ adc->smask = *scan_mask;
+
+ dev_dbg(&indio_dev->dev, "nconv=%d mask=%lx\n", adc->nconv, *scan_mask);
+
return 0;
}
-static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+static int __stm32_dfsdm_postenable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- const struct iio_chan_spec *chan = &indio_dev->channels[0];
int ret;
/* Reset adc buffer index */
adc->bufi = 0;
+ if (adc->hwc) {
+ ret = iio_hw_consumer_enable(adc->hwc);
+ if (ret < 0)
+ return ret;
+ }
+
ret = stm32_dfsdm_start_dfsdm(adc->dfsdm);
if (ret < 0)
- return ret;
+ goto err_stop_hwc;
- ret = stm32_dfsdm_start_conv(adc, chan, true);
+ ret = stm32_dfsdm_adc_dma_start(indio_dev);
if (ret) {
- dev_err(&indio_dev->dev, "Can't start conversion\n");
+ dev_err(&indio_dev->dev, "Can't start DMA\n");
goto stop_dfsdm;
}
- if (adc->dma_chan) {
- ret = stm32_dfsdm_adc_dma_start(indio_dev);
- if (ret) {
- dev_err(&indio_dev->dev, "Can't start DMA\n");
- goto err_stop_conv;
- }
+ ret = stm32_dfsdm_start_conv(adc, indio_dev->trig);
+ if (ret) {
+ dev_err(&indio_dev->dev, "Can't start conversion\n");
+ goto err_stop_dma;
}
return 0;
-err_stop_conv:
- stm32_dfsdm_stop_conv(adc, chan);
+err_stop_dma:
+ stm32_dfsdm_adc_dma_stop(indio_dev);
stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+err_stop_hwc:
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
return ret;
}
-static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+static int stm32_dfsdm_postenable(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED) {
+ ret = iio_triggered_buffer_postenable(indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = __stm32_dfsdm_postenable(indio_dev);
+ if (ret < 0)
+ goto err_predisable;
+
+ return 0;
+
+err_predisable:
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ iio_triggered_buffer_predisable(indio_dev);
+
+ return ret;
+}
+
+static void __stm32_dfsdm_predisable(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- const struct iio_chan_spec *chan = &indio_dev->channels[0];
- if (adc->dma_chan)
- dmaengine_terminate_all(adc->dma_chan);
+ stm32_dfsdm_stop_conv(adc);
- stm32_dfsdm_stop_conv(adc, chan);
+ stm32_dfsdm_adc_dma_stop(indio_dev);
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
+ if (adc->hwc)
+ iio_hw_consumer_disable(adc->hwc);
+}
+
+static int stm32_dfsdm_predisable(struct iio_dev *indio_dev)
+{
+ __stm32_dfsdm_predisable(indio_dev);
+
+ if (indio_dev->currentmode == INDIO_BUFFER_TRIGGERED)
+ iio_triggered_buffer_predisable(indio_dev);
+
return 0;
}
@@ -736,7 +1179,9 @@
if (ret < 0)
goto stop_dfsdm;
- ret = stm32_dfsdm_start_conv(adc, chan, false);
+ adc->nconv = 1;
+ adc->smask = BIT(chan->scan_index);
+ ret = stm32_dfsdm_start_conv(adc, NULL);
if (ret < 0) {
regmap_update_bits(adc->dfsdm->regmap, DFSDM_CR2(adc->fl_id),
DFSDM_CR2_REOCIE_MASK, DFSDM_CR2_REOCIE(0));
@@ -757,7 +1202,7 @@
else
ret = IIO_VAL_INT;
- stm32_dfsdm_stop_conv(adc, chan);
+ stm32_dfsdm_stop_conv(adc);
stop_dfsdm:
stm32_dfsdm_stop_dfsdm(adc->dfsdm);
@@ -770,23 +1215,29 @@
int val, int val2, long mask)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- struct stm32_dfsdm_filter *fl = &adc->dfsdm->fl_list[adc->fl_id];
struct stm32_dfsdm_channel *ch = &adc->dfsdm->ch_list[chan->channel];
unsigned int spi_freq;
int ret = -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
- ret = stm32_dfsdm_set_osrs(fl, 0, val);
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, val);
if (!ret)
adc->oversamp = val;
-
+ iio_device_release_direct_mode(indio_dev);
return ret;
case IIO_CHAN_INFO_SAMP_FREQ:
if (!val)
return -EINVAL;
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
+
switch (ch->src) {
case DFSDM_CHANNEL_SPI_CLOCK_INTERNAL:
spi_freq = adc->dfsdm->spi_master_freq;
@@ -799,20 +1250,9 @@
spi_freq = adc->spi_freq;
}
- if (spi_freq % val)
- dev_warn(&indio_dev->dev,
- "Sampling rate not accurate (%d)\n",
- spi_freq / (spi_freq / val));
-
- ret = stm32_dfsdm_set_osrs(fl, 0, (spi_freq / val));
- if (ret < 0) {
- dev_err(&indio_dev->dev,
- "Not able to find parameter that match!\n");
- return ret;
- }
- adc->sample_freq = val;
-
- return 0;
+ ret = dfsdm_adc_set_samp_freq(indio_dev, val, spi_freq);
+ iio_device_release_direct_mode(indio_dev);
+ return ret;
}
return -EINVAL;
@@ -827,11 +1267,15 @@
switch (mask) {
case IIO_CHAN_INFO_RAW:
+ ret = iio_device_claim_direct_mode(indio_dev);
+ if (ret)
+ return ret;
ret = iio_hw_consumer_enable(adc->hwc);
if (ret < 0) {
dev_err(&indio_dev->dev,
"%s: IIO enable failed (channel %d)\n",
__func__, chan->channel);
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
ret = stm32_dfsdm_single_conv(indio_dev, chan, val);
@@ -840,8 +1284,10 @@
dev_err(&indio_dev->dev,
"%s: Conversion failed (channel %d)\n",
__func__, chan->channel);
+ iio_device_release_direct_mode(indio_dev);
return ret;
}
+ iio_device_release_direct_mode(indio_dev);
return IIO_VAL_INT;
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
@@ -858,15 +1304,25 @@
return -EINVAL;
}
+static int stm32_dfsdm_validate_trigger(struct iio_dev *indio_dev,
+ struct iio_trigger *trig)
+{
+ return stm32_dfsdm_get_jextsel(indio_dev, trig) < 0 ? -EINVAL : 0;
+}
+
static const struct iio_info stm32_dfsdm_info_audio = {
.hwfifo_set_watermark = stm32_dfsdm_set_watermark,
.read_raw = stm32_dfsdm_read_raw,
.write_raw = stm32_dfsdm_write_raw,
+ .update_scan_mode = stm32_dfsdm_update_scan_mode,
};
static const struct iio_info stm32_dfsdm_info_adc = {
+ .hwfifo_set_watermark = stm32_dfsdm_set_watermark,
.read_raw = stm32_dfsdm_read_raw,
.write_raw = stm32_dfsdm_write_raw,
+ .update_scan_mode = stm32_dfsdm_update_scan_mode,
+ .validate_trigger = stm32_dfsdm_validate_trigger,
};
static irqreturn_t stm32_dfsdm_irq(int irq, void *arg)
@@ -926,12 +1382,6 @@
static int stm32_dfsdm_dma_request(struct iio_dev *indio_dev)
{
struct stm32_dfsdm_adc *adc = iio_priv(indio_dev);
- struct dma_slave_config config = {
- .src_addr = (dma_addr_t)adc->dfsdm->phys_base +
- DFSDM_RDATAR(adc->fl_id),
- .src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
- };
- int ret;
adc->dma_chan = dma_request_slave_channel(&indio_dev->dev, "rx");
if (!adc->dma_chan)
@@ -941,23 +1391,14 @@
DFSDM_DMA_BUFFER_SIZE,
&adc->dma_buf, GFP_KERNEL);
if (!adc->rx_buf) {
- ret = -ENOMEM;
- goto err_release;
+ dma_release_channel(adc->dma_chan);
+ return -ENOMEM;
}
- ret = dmaengine_slave_config(adc->dma_chan, &config);
- if (ret)
- goto err_free;
+ indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
+ indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
return 0;
-
-err_free:
- dma_free_coherent(adc->dma_chan->device->dev, DFSDM_DMA_BUFFER_SIZE,
- adc->rx_buf, adc->dma_buf);
-err_release:
- dma_release_channel(adc->dma_chan);
-
- return ret;
}
static int stm32_dfsdm_adc_chan_init_one(struct iio_dev *indio_dev,
@@ -978,14 +1419,15 @@
* IIO_CHAN_INFO_OVERSAMPLING_RATIO: used to set oversampling
*/
ch->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
- ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO);
+ ch->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
if (adc->dev_data->type == DFSDM_AUDIO) {
- ch->scan_type.sign = 's';
ch->ext_info = dfsdm_adc_audio_ext_info;
} else {
- ch->scan_type.sign = 'u';
+ ch->scan_type.shift = 8;
}
+ ch->scan_type.sign = 's';
ch->scan_type.realbits = 24;
ch->scan_type.storagebits = 32;
@@ -1000,9 +1442,6 @@
struct stm32_dfsdm_channel *d_ch;
int ret;
- indio_dev->modes |= INDIO_BUFFER_SOFTWARE;
- indio_dev->setup_ops = &stm32_dfsdm_buffer_setup_ops;
-
ch = devm_kzalloc(&indio_dev->dev, sizeof(*ch), GFP_KERNEL);
if (!ch)
return -ENOMEM;
@@ -1034,8 +1473,7 @@
int ret, chan_idx;
adc->oversamp = DFSDM_DEFAULT_OVERSAMPLING;
- ret = stm32_dfsdm_set_osrs(&adc->dfsdm->fl_list[adc->fl_id], 0,
- adc->oversamp);
+ ret = stm32_dfsdm_compute_all_osrs(indio_dev, adc->oversamp);
if (ret < 0)
return ret;
@@ -1070,6 +1508,25 @@
init_completion(&adc->completion);
+ /* Optionally request DMA */
+ if (stm32_dfsdm_dma_request(indio_dev)) {
+ dev_dbg(&indio_dev->dev, "No DMA support\n");
+ return 0;
+ }
+
+ ret = iio_triggered_buffer_setup(indio_dev,
+ &iio_pollfunc_store_time,
+ &stm32_dfsdm_adc_trigger_handler,
+ &stm32_dfsdm_buffer_setup_ops);
+ if (ret) {
+ stm32_dfsdm_dma_release(indio_dev);
+ dev_err(&indio_dev->dev, "buffer setup failed\n");
+ return ret;
+ }
+
+ /* lptimer/timer hardware triggers */
+ indio_dev->modes |= INDIO_HARDWARE_TRIGGERED;
+
return 0;
}
@@ -1117,7 +1574,7 @@
iio->dev.parent = dev;
iio->dev.of_node = np;
- iio->modes = INDIO_DIRECT_MODE | INDIO_BUFFER_SOFTWARE;
+ iio->modes = INDIO_DIRECT_MODE;
platform_set_drvdata(pdev, adc);
@@ -1144,6 +1601,9 @@
* So IRQ associated to filter instance 0 is dedicated to the Filter 0.
*/
irq = platform_get_irq(pdev, 0);
+ if (irq < 0)
+ return irq;
+
ret = devm_request_irq(dev, irq, stm32_dfsdm_irq,
0, pdev->name, adc);
if (ret < 0) {
@@ -1203,10 +1663,48 @@
return 0;
}
+static int __maybe_unused stm32_dfsdm_adc_suspend(struct device *dev)
+{
+ struct stm32_dfsdm_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+
+ if (iio_buffer_enabled(indio_dev))
+ __stm32_dfsdm_predisable(indio_dev);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_dfsdm_adc_resume(struct device *dev)
+{
+ struct stm32_dfsdm_adc *adc = dev_get_drvdata(dev);
+ struct iio_dev *indio_dev = iio_priv_to_dev(adc);
+ const struct iio_chan_spec *chan;
+ struct stm32_dfsdm_channel *ch;
+ int i, ret;
+
+ /* restore channels configuration */
+ for (i = 0; i < indio_dev->num_channels; i++) {
+ chan = indio_dev->channels + i;
+ ch = &adc->dfsdm->ch_list[chan->channel];
+ ret = stm32_dfsdm_chan_configure(adc->dfsdm, ch);
+ if (ret)
+ return ret;
+ }
+
+ if (iio_buffer_enabled(indio_dev))
+ __stm32_dfsdm_postenable(indio_dev);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stm32_dfsdm_adc_pm_ops,
+ stm32_dfsdm_adc_suspend, stm32_dfsdm_adc_resume);
+
static struct platform_driver stm32_dfsdm_adc_driver = {
.driver = {
.name = "stm32-dfsdm-adc",
.of_match_table = stm32_dfsdm_adc_match,
+ .pm = &stm32_dfsdm_adc_pm_ops,
},
.probe = stm32_dfsdm_adc_probe,
.remove = stm32_dfsdm_adc_remove,
diff --git a/drivers/iio/adc/stm32-dfsdm-core.c b/drivers/iio/adc/stm32-dfsdm-core.c
index bf089f5..26e2011 100644
--- a/drivers/iio/adc/stm32-dfsdm-core.c
+++ b/drivers/iio/adc/stm32-dfsdm-core.c
@@ -12,6 +12,8 @@
#include <linux/interrupt.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pm_runtime.h>
#include <linux/regmap.h>
#include <linux/slab.h>
@@ -90,6 +92,36 @@
struct clk *aclk; /* audio clock */
};
+static inline struct dfsdm_priv *to_stm32_dfsdm_priv(struct stm32_dfsdm *dfsdm)
+{
+ return container_of(dfsdm, struct dfsdm_priv, dfsdm);
+}
+
+static int stm32_dfsdm_clk_prepare_enable(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = clk_prepare_enable(priv->clk);
+ if (ret || !priv->aclk)
+ return ret;
+
+ ret = clk_prepare_enable(priv->aclk);
+ if (ret)
+ clk_disable_unprepare(priv->clk);
+
+ return ret;
+}
+
+static void stm32_dfsdm_clk_disable_unprepare(struct stm32_dfsdm *dfsdm)
+{
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+
+ if (priv->aclk)
+ clk_disable_unprepare(priv->aclk);
+ clk_disable_unprepare(priv->clk);
+}
+
/**
* stm32_dfsdm_start_dfsdm - start global dfsdm interface.
*
@@ -98,24 +130,17 @@
*/
int stm32_dfsdm_start_dfsdm(struct stm32_dfsdm *dfsdm)
{
- struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
struct device *dev = &priv->pdev->dev;
unsigned int clk_div = priv->spi_clk_out_div, clk_src;
int ret;
if (atomic_inc_return(&priv->n_active_ch) == 1) {
- ret = clk_prepare_enable(priv->clk);
+ ret = pm_runtime_get_sync(dev);
if (ret < 0) {
- dev_err(dev, "Failed to start clock\n");
+ pm_runtime_put_noidle(dev);
goto error_ret;
}
- if (priv->aclk) {
- ret = clk_prepare_enable(priv->aclk);
- if (ret < 0) {
- dev_err(dev, "Failed to start audio clock\n");
- goto disable_clk;
- }
- }
/* select clock source, e.g. 0 for "dfsdm" or 1 for "audio" */
clk_src = priv->aclk ? 1 : 0;
@@ -123,21 +148,21 @@
DFSDM_CHCFGR1_CKOUTSRC_MASK,
DFSDM_CHCFGR1_CKOUTSRC(clk_src));
if (ret < 0)
- goto disable_aclk;
+ goto pm_put;
/* Output the SPI CLKOUT (if clk_div == 0 clock if OFF) */
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
DFSDM_CHCFGR1_CKOUTDIV_MASK,
DFSDM_CHCFGR1_CKOUTDIV(clk_div));
if (ret < 0)
- goto disable_aclk;
+ goto pm_put;
/* Global enable of DFSDM interface */
ret = regmap_update_bits(dfsdm->regmap, DFSDM_CHCFGR1(0),
DFSDM_CHCFGR1_DFSDMEN_MASK,
DFSDM_CHCFGR1_DFSDMEN(1));
if (ret < 0)
- goto disable_aclk;
+ goto pm_put;
}
dev_dbg(dev, "%s: n_active_ch %d\n", __func__,
@@ -145,11 +170,8 @@
return 0;
-disable_aclk:
- clk_disable_unprepare(priv->aclk);
-disable_clk:
- clk_disable_unprepare(priv->clk);
-
+pm_put:
+ pm_runtime_put_sync(dev);
error_ret:
atomic_dec(&priv->n_active_ch);
@@ -165,7 +187,7 @@
*/
int stm32_dfsdm_stop_dfsdm(struct stm32_dfsdm *dfsdm)
{
- struct dfsdm_priv *priv = container_of(dfsdm, struct dfsdm_priv, dfsdm);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
int ret;
if (atomic_dec_and_test(&priv->n_active_ch)) {
@@ -183,9 +205,7 @@
if (ret < 0)
return ret;
- clk_disable_unprepare(priv->clk);
- if (priv->aclk)
- clk_disable_unprepare(priv->aclk);
+ pm_runtime_put_sync(&priv->pdev->dev);
}
dev_dbg(&priv->pdev->dev, "%s: n_active_ch %d\n", __func__,
atomic_read(&priv->n_active_ch));
@@ -199,7 +219,7 @@
{
struct device_node *node = pdev->dev.of_node;
struct resource *res;
- unsigned long clk_freq;
+ unsigned long clk_freq, divider;
unsigned int spi_freq, rem;
int ret;
@@ -213,6 +233,8 @@
}
priv->dfsdm.phys_base = res->start;
priv->dfsdm.base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(priv->dfsdm.base))
+ return PTR_ERR(priv->dfsdm.base);
/*
* "dfsdm" clock is mandatory for DFSDM peripheral clocking.
@@ -222,8 +244,10 @@
*/
priv->clk = devm_clk_get(&pdev->dev, "dfsdm");
if (IS_ERR(priv->clk)) {
- dev_err(&pdev->dev, "No stm32_dfsdm_clk clock found\n");
- return -EINVAL;
+ ret = PTR_ERR(priv->clk);
+ if (ret != -EPROBE_DEFER)
+ dev_err(&pdev->dev, "Failed to get clock (%d)\n", ret);
+ return ret;
}
priv->aclk = devm_clk_get(&pdev->dev, "audio");
@@ -243,13 +267,20 @@
return 0;
}
- priv->spi_clk_out_div = div_u64_rem(clk_freq, spi_freq, &rem) - 1;
- if (!priv->spi_clk_out_div) {
- /* spi_clk_out_div == 0 means ckout is OFF */
+ divider = div_u64_rem(clk_freq, spi_freq, &rem);
+ /* Round up divider when ckout isn't precise, not to exceed spi_freq */
+ if (rem)
+ divider++;
+
+ /* programmable divider is in range of [2:256] */
+ if (divider < 2 || divider > 256) {
dev_err(&pdev->dev, "spi-max-frequency not achievable\n");
return -EINVAL;
}
- priv->dfsdm.spi_master_freq = spi_freq;
+
+ /* SPI clock output divider is: divider = CKOUTDIV + 1 */
+ priv->spi_clk_out_div = divider - 1;
+ priv->dfsdm.spi_master_freq = clk_freq / (priv->spi_clk_out_div + 1);
if (rem) {
dev_warn(&pdev->dev, "SPI clock not accurate\n");
@@ -318,14 +349,111 @@
platform_set_drvdata(pdev, dfsdm);
- return devm_of_platform_populate(&pdev->dev);
+ ret = stm32_dfsdm_clk_prepare_enable(dfsdm);
+ if (ret) {
+ dev_err(&pdev->dev, "Failed to start clock\n");
+ return ret;
+ }
+
+ pm_runtime_get_noresume(&pdev->dev);
+ pm_runtime_set_active(&pdev->dev);
+ pm_runtime_enable(&pdev->dev);
+
+ ret = of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);
+ if (ret)
+ goto pm_put;
+
+ pm_runtime_put(&pdev->dev);
+
+ return 0;
+
+pm_put:
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return ret;
}
+static int stm32_dfsdm_core_remove(struct platform_device *pdev)
+{
+ struct stm32_dfsdm *dfsdm = platform_get_drvdata(pdev);
+
+ pm_runtime_get_sync(&pdev->dev);
+ of_platform_depopulate(&pdev->dev);
+ pm_runtime_disable(&pdev->dev);
+ pm_runtime_set_suspended(&pdev->dev);
+ pm_runtime_put_noidle(&pdev->dev);
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_dfsdm_core_suspend(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = pm_runtime_force_suspend(dev);
+ if (ret)
+ return ret;
+
+ /* Balance devm_regmap_init_mmio_clk() clk_prepare() */
+ clk_unprepare(priv->clk);
+
+ return pinctrl_pm_select_sleep_state(dev);
+}
+
+static int __maybe_unused stm32_dfsdm_core_resume(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+ struct dfsdm_priv *priv = to_stm32_dfsdm_priv(dfsdm);
+ int ret;
+
+ ret = pinctrl_pm_select_default_state(dev);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare(priv->clk);
+ if (ret)
+ return ret;
+
+ return pm_runtime_force_resume(dev);
+}
+
+static int __maybe_unused stm32_dfsdm_core_runtime_suspend(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+
+ stm32_dfsdm_clk_disable_unprepare(dfsdm);
+
+ return 0;
+}
+
+static int __maybe_unused stm32_dfsdm_core_runtime_resume(struct device *dev)
+{
+ struct stm32_dfsdm *dfsdm = dev_get_drvdata(dev);
+
+ return stm32_dfsdm_clk_prepare_enable(dfsdm);
+}
+
+static const struct dev_pm_ops stm32_dfsdm_core_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(stm32_dfsdm_core_suspend,
+ stm32_dfsdm_core_resume)
+ SET_RUNTIME_PM_OPS(stm32_dfsdm_core_runtime_suspend,
+ stm32_dfsdm_core_runtime_resume,
+ NULL)
+};
+
static struct platform_driver stm32_dfsdm_driver = {
.probe = stm32_dfsdm_probe,
+ .remove = stm32_dfsdm_core_remove,
.driver = {
.name = "stm32-dfsdm",
.of_match_table = stm32_dfsdm_of_match,
+ .pm = &stm32_dfsdm_core_pm_ops,
},
};
diff --git a/drivers/iio/adc/stm32-dfsdm.h b/drivers/iio/adc/stm32-dfsdm.h
index 8708394..5dbdae4 100644
--- a/drivers/iio/adc/stm32-dfsdm.h
+++ b/drivers/iio/adc/stm32-dfsdm.h
@@ -243,19 +243,33 @@
};
/**
- * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * struct stm32_dfsdm_filter_osr - DFSDM filter settings linked to oversampling
* @iosr: integrator oversampling
* @fosr: filter oversampling
- * @ford: filter order
+ * @rshift: output sample right shift (hardware shift)
+ * @lshift: output sample left shift (software shift)
* @res: output sample resolution
+ * @max: output sample maximum positive value
+ */
+struct stm32_dfsdm_filter_osr {
+ unsigned int iosr;
+ unsigned int fosr;
+ unsigned int rshift;
+ unsigned int lshift;
+ u64 res;
+ s32 max;
+};
+
+/**
+ * struct stm32_dfsdm_filter - structure relative to stm32 FDSDM filter
+ * @ford: filter order
+ * @flo: filter oversampling data table indexed by fast mode flag
* @sync_mode: filter synchronized with filter 0
* @fast: filter fast mode
*/
struct stm32_dfsdm_filter {
- unsigned int iosr;
- unsigned int fosr;
enum stm32_dfsdm_sinc_order ford;
- u64 res;
+ struct stm32_dfsdm_filter_osr flo[2];
unsigned int sync_mode;
unsigned int fast;
};
diff --git a/drivers/iio/adc/stmpe-adc.c b/drivers/iio/adc/stmpe-adc.c
new file mode 100644
index 0000000..bd72727
--- /dev/null
+++ b/drivers/iio/adc/stmpe-adc.c
@@ -0,0 +1,366 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * STMicroelectronics STMPE811 IIO ADC Driver
+ *
+ * 4 channel, 10/12-bit ADC
+ *
+ * Copyright (C) 2013-2018 Toradex AG <stefan.agner@toradex.com>
+ */
+
+#include <linux/completion.h>
+#include <linux/err.h>
+#include <linux/iio/iio.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/mfd/stmpe.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+
+#define STMPE_REG_INT_STA 0x0B
+#define STMPE_REG_ADC_INT_EN 0x0E
+#define STMPE_REG_ADC_INT_STA 0x0F
+
+#define STMPE_REG_ADC_CTRL1 0x20
+#define STMPE_REG_ADC_CTRL2 0x21
+#define STMPE_REG_ADC_CAPT 0x22
+#define STMPE_REG_ADC_DATA_CH(channel) (0x30 + 2 * (channel))
+
+#define STMPE_REG_TEMP_CTRL 0x60
+#define STMPE_TEMP_CTRL_ENABLE BIT(0)
+#define STMPE_TEMP_CTRL_ACQ BIT(1)
+#define STMPE_TEMP_CTRL_THRES_EN BIT(3)
+#define STMPE_START_ONE_TEMP_CONV (STMPE_TEMP_CTRL_ENABLE | \
+ STMPE_TEMP_CTRL_ACQ | \
+ STMPE_TEMP_CTRL_THRES_EN)
+#define STMPE_REG_TEMP_DATA 0x61
+#define STMPE_REG_TEMP_TH 0x63
+#define STMPE_ADC_LAST_NR 7
+#define STMPE_TEMP_CHANNEL (STMPE_ADC_LAST_NR + 1)
+
+#define STMPE_ADC_CH(channel) ((1 << (channel)) & 0xff)
+
+#define STMPE_ADC_TIMEOUT msecs_to_jiffies(1000)
+
+struct stmpe_adc {
+ struct stmpe *stmpe;
+ struct clk *clk;
+ struct device *dev;
+ struct mutex lock;
+
+ /* We are allocating plus one for the temperature channel */
+ struct iio_chan_spec stmpe_adc_iio_channels[STMPE_ADC_LAST_NR + 2];
+
+ struct completion completion;
+
+ u8 channel;
+ u32 value;
+};
+
+static int stmpe_read_voltage(struct stmpe_adc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ long ret;
+
+ mutex_lock(&info->lock);
+
+ reinit_completion(&info->completion);
+
+ info->channel = (u8)chan->channel;
+
+ if (info->channel > STMPE_ADC_LAST_NR) {
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_CAPT,
+ STMPE_ADC_CH(info->channel));
+
+ ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT);
+
+ if (ret <= 0) {
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA,
+ STMPE_ADC_CH(info->channel));
+ mutex_unlock(&info->lock);
+ return -ETIMEDOUT;
+ }
+
+ *val = info->value;
+
+ mutex_unlock(&info->lock);
+
+ return 0;
+}
+
+static int stmpe_read_temp(struct stmpe_adc *info,
+ struct iio_chan_spec const *chan, int *val)
+{
+ long ret;
+
+ mutex_lock(&info->lock);
+
+ reinit_completion(&info->completion);
+
+ info->channel = (u8)chan->channel;
+
+ if (info->channel != STMPE_TEMP_CHANNEL) {
+ mutex_unlock(&info->lock);
+ return -EINVAL;
+ }
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_TEMP_CTRL,
+ STMPE_START_ONE_TEMP_CONV);
+
+ ret = wait_for_completion_timeout(&info->completion, STMPE_ADC_TIMEOUT);
+
+ if (ret <= 0) {
+ mutex_unlock(&info->lock);
+ return -ETIMEDOUT;
+ }
+
+ /*
+ * absolute temp = +V3.3 * value /7.51 [K]
+ * scale to [milli °C]
+ */
+ *val = ((449960l * info->value) / 1024l) - 273150;
+
+ mutex_unlock(&info->lock);
+
+ return 0;
+}
+
+static int stmpe_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct stmpe_adc *info = iio_priv(indio_dev);
+ long ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+
+ switch (chan->type) {
+ case IIO_VOLTAGE:
+ ret = stmpe_read_voltage(info, chan, val);
+ break;
+
+ case IIO_TEMP:
+ ret = stmpe_read_temp(info, chan, val);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = 3300;
+ *val2 = info->stmpe->mod_12b ? 12 : 10;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ break;
+ }
+
+ return -EINVAL;
+}
+
+static irqreturn_t stmpe_adc_isr(int irq, void *dev_id)
+{
+ struct stmpe_adc *info = (struct stmpe_adc *)dev_id;
+ u16 data;
+
+ if (info->channel <= STMPE_ADC_LAST_NR) {
+ int int_sta;
+
+ int_sta = stmpe_reg_read(info->stmpe, STMPE_REG_ADC_INT_STA);
+
+ /* Is the interrupt relevant */
+ if (!(int_sta & STMPE_ADC_CH(info->channel)))
+ return IRQ_NONE;
+
+ /* Read value */
+ stmpe_block_read(info->stmpe,
+ STMPE_REG_ADC_DATA_CH(info->channel), 2, (u8 *) &data);
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA, int_sta);
+ } else if (info->channel == STMPE_TEMP_CHANNEL) {
+ /* Read value */
+ stmpe_block_read(info->stmpe, STMPE_REG_TEMP_DATA, 2,
+ (u8 *) &data);
+ } else {
+ return IRQ_NONE;
+ }
+
+ info->value = (u32) be16_to_cpu(data);
+ complete(&info->completion);
+
+ return IRQ_HANDLED;
+}
+
+static const struct iio_info stmpe_adc_iio_info = {
+ .read_raw = &stmpe_read_raw,
+};
+
+static void stmpe_adc_voltage_chan(struct iio_chan_spec *ics, int chan)
+{
+ ics->type = IIO_VOLTAGE;
+ ics->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ ics->info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE);
+ ics->indexed = 1;
+ ics->channel = chan;
+}
+
+static void stmpe_adc_temp_chan(struct iio_chan_spec *ics, int chan)
+{
+ ics->type = IIO_TEMP;
+ ics->info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED);
+ ics->indexed = 1;
+ ics->channel = chan;
+}
+
+static int stmpe_adc_init_hw(struct stmpe_adc *adc)
+{
+ int ret;
+ struct stmpe *stmpe = adc->stmpe;
+
+ ret = stmpe_enable(stmpe, STMPE_BLOCK_ADC);
+ if (ret) {
+ dev_err(stmpe->dev, "Could not enable clock for ADC\n");
+ return ret;
+ }
+
+ ret = stmpe811_adc_common_init(stmpe);
+ if (ret) {
+ stmpe_disable(stmpe, STMPE_BLOCK_ADC);
+ return ret;
+ }
+
+ /* use temp irq for each conversion completion */
+ stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH, 0);
+ stmpe_reg_write(stmpe, STMPE_REG_TEMP_TH + 1, 0);
+
+ return 0;
+}
+
+static int stmpe_adc_probe(struct platform_device *pdev)
+{
+ struct iio_dev *indio_dev;
+ struct stmpe_adc *info;
+ struct device_node *np;
+ u32 norequest_mask = 0;
+ int irq_temp, irq_adc;
+ int num_chan = 0;
+ int i = 0;
+ int ret;
+
+ irq_adc = platform_get_irq_byname(pdev, "STMPE_ADC");
+ if (irq_adc < 0)
+ return irq_adc;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct stmpe_adc));
+ if (!indio_dev) {
+ dev_err(&pdev->dev, "failed allocating iio device\n");
+ return -ENOMEM;
+ }
+
+ info = iio_priv(indio_dev);
+ mutex_init(&info->lock);
+
+ init_completion(&info->completion);
+ ret = devm_request_threaded_irq(&pdev->dev, irq_adc, NULL,
+ stmpe_adc_isr, IRQF_ONESHOT,
+ "stmpe-adc", info);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "failed requesting irq, irq = %d\n",
+ irq_adc);
+ return ret;
+ }
+
+ irq_temp = platform_get_irq_byname(pdev, "STMPE_TEMP_SENS");
+ if (irq_temp >= 0) {
+ ret = devm_request_threaded_irq(&pdev->dev, irq_temp, NULL,
+ stmpe_adc_isr, IRQF_ONESHOT,
+ "stmpe-adc", info);
+ if (ret < 0)
+ dev_warn(&pdev->dev, "failed requesting irq for"
+ " temp sensor, irq = %d\n", irq_temp);
+ }
+
+ platform_set_drvdata(pdev, indio_dev);
+
+ indio_dev->name = dev_name(&pdev->dev);
+ indio_dev->dev.parent = &pdev->dev;
+ indio_dev->info = &stmpe_adc_iio_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ info->stmpe = dev_get_drvdata(pdev->dev.parent);
+
+ np = pdev->dev.of_node;
+
+ if (!np)
+ dev_err(&pdev->dev, "no device tree node found\n");
+
+ of_property_read_u32(np, "st,norequest-mask", &norequest_mask);
+
+ for_each_clear_bit(i, (unsigned long *) &norequest_mask,
+ (STMPE_ADC_LAST_NR + 1)) {
+ stmpe_adc_voltage_chan(&info->stmpe_adc_iio_channels[num_chan], i);
+ num_chan++;
+ }
+ stmpe_adc_temp_chan(&info->stmpe_adc_iio_channels[num_chan], i);
+ num_chan++;
+ indio_dev->channels = info->stmpe_adc_iio_channels;
+ indio_dev->num_channels = num_chan;
+
+ ret = stmpe_adc_init_hw(info);
+ if (ret)
+ return ret;
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_EN,
+ ~(norequest_mask & 0xFF));
+
+ stmpe_reg_write(info->stmpe, STMPE_REG_ADC_INT_STA,
+ ~(norequest_mask & 0xFF));
+
+ return devm_iio_device_register(&pdev->dev, indio_dev);
+}
+
+static int __maybe_unused stmpe_adc_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct stmpe_adc *info = iio_priv(indio_dev);
+
+ stmpe_adc_init_hw(info);
+
+ return 0;
+}
+
+static SIMPLE_DEV_PM_OPS(stmpe_adc_pm_ops, NULL, stmpe_adc_resume);
+
+static struct platform_driver stmpe_adc_driver = {
+ .probe = stmpe_adc_probe,
+ .driver = {
+ .name = "stmpe-adc",
+ .pm = &stmpe_adc_pm_ops,
+ },
+};
+module_platform_driver(stmpe_adc_driver);
+
+static const struct of_device_id stmpe_adc_ids[] = {
+ { .compatible = "st,stmpe-adc", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, stmpe_adc_ids);
+
+MODULE_AUTHOR("Stefan Agner <stefan.agner@toradex.com>");
+MODULE_DESCRIPTION("STMPEXXX ADC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:stmpe-adc");
diff --git a/drivers/iio/adc/stx104.c b/drivers/iio/adc/stx104.c
index 0662ca1..f87bbc7 100644
--- a/drivers/iio/adc/stx104.c
+++ b/drivers/iio/adc/stx104.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO driver for the Apex Embedded Systems STX104
* Copyright (C) 2016 William Breathitt Gray
- *
- * 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.
- *
- * 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/bitops.h>
#include <linux/device.h>
diff --git a/drivers/iio/adc/sun4i-gpadc-iio.c b/drivers/iio/adc/sun4i-gpadc-iio.c
index 04d7147..176e1cb 100644
--- a/drivers/iio/adc/sun4i-gpadc-iio.c
+++ b/drivers/iio/adc/sun4i-gpadc-iio.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/* ADC driver for sunxi platforms' (A10, A13 and A31) GPADC
*
* Copyright (c) 2016 Quentin Schulz <quentin.schulz@free-electrons.com>
*
- * 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.
- *
* The Allwinner SoCs all have an ADC that can also act as a touchscreen
* controller and a thermal sensor.
* The thermal sensor works only when the ADC acts as a touchscreen controller
@@ -463,10 +460,8 @@
atomic_set(atomic, 1);
ret = platform_get_irq_byname(pdev, name);
- if (ret < 0) {
- dev_err(&pdev->dev, "no %s interrupt registered\n", name);
+ if (ret < 0)
return ret;
- }
ret = regmap_irq_get_virq(mfd_dev->regmap_irqc, ret);
if (ret < 0) {
diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
index 405e377..0235863 100644
--- a/drivers/iio/adc/ti-adc081c.c
+++ b/drivers/iio/adc/ti-adc081c.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver
*
* Copyright (C) 2012 Avionic Design GmbH
* Copyright (C) 2016 Intel
*
- * 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.
- *
* Datasheets:
* http://www.ti.com/lit/ds/symlink/adc081c021.pdf
* http://www.ti.com/lit/ds/symlink/adc101c021.pdf
diff --git a/drivers/iio/adc/ti-adc0832.c b/drivers/iio/adc/ti-adc0832.c
index 188dae7..6ea39f4 100644
--- a/drivers/iio/adc/ti-adc0832.c
+++ b/drivers/iio/adc/ti-adc0832.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADC0831/ADC0832/ADC0834/ADC0838 8-bit ADC driver
*
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Datasheet: http://www.ti.com/lit/ds/symlink/adc0832-n.pdf
*/
diff --git a/drivers/iio/adc/ti-adc084s021.c b/drivers/iio/adc/ti-adc084s021.c
index 2550464..bdedf45 100644
--- a/drivers/iio/adc/ti-adc084s021.c
+++ b/drivers/iio/adc/ti-adc084s021.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Copyright (C) 2017 Axis Communications AB
*
* Driver for Texas Instruments' ADC084S021 ADC chip.
* Datasheets can be found here:
* http://www.ti.com/lit/ds/symlink/adc084s021.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/err.h>
diff --git a/drivers/iio/adc/ti-adc108s102.c b/drivers/iio/adc/ti-adc108s102.c
index 841203e..de9aaeb 100644
--- a/drivers/iio/adc/ti-adc108s102.c
+++ b/drivers/iio/adc/ti-adc108s102.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI ADC108S102 SPI ADC driver
*
* Copyright (c) 2013-2015 Intel Corporation.
* Copyright (c) 2017 Siemens AG
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
* This IIO device driver is designed to work with the following
* analog to digital converters from Texas Instruments:
* ADC108S102
diff --git a/drivers/iio/adc/ti-adc12138.c b/drivers/iio/adc/ti-adc12138.c
index 703d68a..68a9dcb 100644
--- a/drivers/iio/adc/ti-adc12138.c
+++ b/drivers/iio/adc/ti-adc12138.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADC12130/ADC12132/ADC12138 12-bit plus sign ADC driver
*
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Datasheet: http://www.ti.com/lit/ds/symlink/adc12138.pdf
*/
diff --git a/drivers/iio/adc/ti-adc128s052.c b/drivers/iio/adc/ti-adc128s052.c
index 7cf39b3..1e5a936 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 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 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/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
index 6a114dc..a550b13 100644
--- a/drivers/iio/adc/ti-ads1015.c
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADS1015 - Texas Instruments Analog-to-Digital Converter
*
* Copyright (c) 2016, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for ADS1015 ADC 7-bit I2C slave address:
* * 0x48 - ADDR connected to Ground
* * 0x49 - ADDR connected to Vdd
diff --git a/drivers/iio/adc/ti-ads124s08.c b/drivers/iio/adc/ti-ads124s08.c
new file mode 100644
index 0000000..552c2be
--- /dev/null
+++ b/drivers/iio/adc/ti-ads124s08.c
@@ -0,0 +1,376 @@
+// SPDX-License-Identifier: GPL-2.0
+/* TI ADS124S0X chip family driver
+ * Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com/
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+
+#include <linux/gpio/consumer.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/sysfs.h>
+
+/* Commands */
+#define ADS124S08_CMD_NOP 0x00
+#define ADS124S08_CMD_WAKEUP 0x02
+#define ADS124S08_CMD_PWRDWN 0x04
+#define ADS124S08_CMD_RESET 0x06
+#define ADS124S08_CMD_START 0x08
+#define ADS124S08_CMD_STOP 0x0a
+#define ADS124S08_CMD_SYOCAL 0x16
+#define ADS124S08_CMD_SYGCAL 0x17
+#define ADS124S08_CMD_SFOCAL 0x19
+#define ADS124S08_CMD_RDATA 0x12
+#define ADS124S08_CMD_RREG 0x20
+#define ADS124S08_CMD_WREG 0x40
+
+/* Registers */
+#define ADS124S08_ID_REG 0x00
+#define ADS124S08_STATUS 0x01
+#define ADS124S08_INPUT_MUX 0x02
+#define ADS124S08_PGA 0x03
+#define ADS124S08_DATA_RATE 0x04
+#define ADS124S08_REF 0x05
+#define ADS124S08_IDACMAG 0x06
+#define ADS124S08_IDACMUX 0x07
+#define ADS124S08_VBIAS 0x08
+#define ADS124S08_SYS 0x09
+#define ADS124S08_OFCAL0 0x0a
+#define ADS124S08_OFCAL1 0x0b
+#define ADS124S08_OFCAL2 0x0c
+#define ADS124S08_FSCAL0 0x0d
+#define ADS124S08_FSCAL1 0x0e
+#define ADS124S08_FSCAL2 0x0f
+#define ADS124S08_GPIODAT 0x10
+#define ADS124S08_GPIOCON 0x11
+
+/* ADS124S0x common channels */
+#define ADS124S08_AIN0 0x00
+#define ADS124S08_AIN1 0x01
+#define ADS124S08_AIN2 0x02
+#define ADS124S08_AIN3 0x03
+#define ADS124S08_AIN4 0x04
+#define ADS124S08_AIN5 0x05
+#define ADS124S08_AINCOM 0x0c
+/* ADS124S08 only channels */
+#define ADS124S08_AIN6 0x06
+#define ADS124S08_AIN7 0x07
+#define ADS124S08_AIN8 0x08
+#define ADS124S08_AIN9 0x09
+#define ADS124S08_AIN10 0x0a
+#define ADS124S08_AIN11 0x0b
+#define ADS124S08_MAX_CHANNELS 12
+
+#define ADS124S08_POS_MUX_SHIFT 0x04
+#define ADS124S08_INT_REF 0x09
+
+#define ADS124S08_START_REG_MASK 0x1f
+#define ADS124S08_NUM_BYTES_MASK 0x1f
+
+#define ADS124S08_START_CONV 0x01
+#define ADS124S08_STOP_CONV 0x00
+
+enum ads124s_id {
+ ADS124S08_ID,
+ ADS124S06_ID,
+};
+
+struct ads124s_chip_info {
+ const struct iio_chan_spec *channels;
+ unsigned int num_channels;
+};
+
+struct ads124s_private {
+ const struct ads124s_chip_info *chip_info;
+ struct gpio_desc *reset_gpio;
+ struct spi_device *spi;
+ struct mutex lock;
+ u8 data[5] ____cacheline_aligned;
+};
+
+#define ADS124S08_CHAN(index) \
+{ \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = index, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_index = index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 32, \
+ .storagebits = 32, \
+ }, \
+}
+
+static const struct iio_chan_spec ads124s06_channels[] = {
+ ADS124S08_CHAN(0),
+ ADS124S08_CHAN(1),
+ ADS124S08_CHAN(2),
+ ADS124S08_CHAN(3),
+ ADS124S08_CHAN(4),
+ ADS124S08_CHAN(5),
+};
+
+static const struct iio_chan_spec ads124s08_channels[] = {
+ ADS124S08_CHAN(0),
+ ADS124S08_CHAN(1),
+ ADS124S08_CHAN(2),
+ ADS124S08_CHAN(3),
+ ADS124S08_CHAN(4),
+ ADS124S08_CHAN(5),
+ ADS124S08_CHAN(6),
+ ADS124S08_CHAN(7),
+ ADS124S08_CHAN(8),
+ ADS124S08_CHAN(9),
+ ADS124S08_CHAN(10),
+ ADS124S08_CHAN(11),
+};
+
+static const struct ads124s_chip_info ads124s_chip_info_tbl[] = {
+ [ADS124S08_ID] = {
+ .channels = ads124s08_channels,
+ .num_channels = ARRAY_SIZE(ads124s08_channels),
+ },
+ [ADS124S06_ID] = {
+ .channels = ads124s06_channels,
+ .num_channels = ARRAY_SIZE(ads124s06_channels),
+ },
+};
+
+static int ads124s_write_cmd(struct iio_dev *indio_dev, u8 command)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+
+ priv->data[0] = command;
+
+ return spi_write(priv->spi, &priv->data[0], 1);
+}
+
+static int ads124s_write_reg(struct iio_dev *indio_dev, u8 reg, u8 data)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+
+ priv->data[0] = ADS124S08_CMD_WREG | reg;
+ priv->data[1] = 0x0;
+ priv->data[2] = data;
+
+ return spi_write(priv->spi, &priv->data[0], 3);
+}
+
+static int ads124s_reset(struct iio_dev *indio_dev)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+
+ if (priv->reset_gpio) {
+ gpiod_set_value(priv->reset_gpio, 0);
+ udelay(200);
+ gpiod_set_value(priv->reset_gpio, 1);
+ } else {
+ return ads124s_write_cmd(indio_dev, ADS124S08_CMD_RESET);
+ }
+
+ return 0;
+};
+
+static int ads124s_read(struct iio_dev *indio_dev, unsigned int chan)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+ int ret;
+ u32 tmp;
+ struct spi_transfer t[] = {
+ {
+ .tx_buf = &priv->data[0],
+ .len = 4,
+ .cs_change = 1,
+ }, {
+ .tx_buf = &priv->data[1],
+ .rx_buf = &priv->data[1],
+ .len = 4,
+ },
+ };
+
+ priv->data[0] = ADS124S08_CMD_RDATA;
+ memset(&priv->data[1], ADS124S08_CMD_NOP, sizeof(priv->data) - 1);
+
+ ret = spi_sync_transfer(priv->spi, t, ARRAY_SIZE(t));
+ if (ret < 0)
+ return ret;
+
+ tmp = priv->data[2] << 16 | priv->data[3] << 8 | priv->data[4];
+
+ return tmp;
+}
+
+static int ads124s_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long m)
+{
+ struct ads124s_private *priv = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&priv->lock);
+ switch (m) {
+ case IIO_CHAN_INFO_RAW:
+ ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
+ chan->channel);
+ if (ret) {
+ dev_err(&priv->spi->dev, "Set ADC CH failed\n");
+ goto out;
+ }
+
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
+ if (ret) {
+ dev_err(&priv->spi->dev, "Start conversions failed\n");
+ goto out;
+ }
+
+ ret = ads124s_read(indio_dev, chan->channel);
+ if (ret < 0) {
+ dev_err(&priv->spi->dev, "Read ADC failed\n");
+ goto out;
+ }
+
+ *val = ret;
+
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
+ if (ret) {
+ dev_err(&priv->spi->dev, "Stop conversions failed\n");
+ goto out;
+ }
+
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+out:
+ mutex_unlock(&priv->lock);
+ return ret;
+}
+
+static const struct iio_info ads124s_info = {
+ .read_raw = &ads124s_read_raw,
+};
+
+static irqreturn_t ads124s_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ads124s_private *priv = iio_priv(indio_dev);
+ u32 buffer[ADS124S08_MAX_CHANNELS + sizeof(s64)/sizeof(u16)];
+ int scan_index, j = 0;
+ int ret;
+
+ for_each_set_bit(scan_index, indio_dev->active_scan_mask,
+ indio_dev->masklength) {
+ ret = ads124s_write_reg(indio_dev, ADS124S08_INPUT_MUX,
+ scan_index);
+ if (ret)
+ dev_err(&priv->spi->dev, "Set ADC CH failed\n");
+
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_START_CONV);
+ if (ret)
+ dev_err(&priv->spi->dev, "Start ADC conversions failed\n");
+
+ buffer[j] = ads124s_read(indio_dev, scan_index);
+ ret = ads124s_write_cmd(indio_dev, ADS124S08_STOP_CONV);
+ if (ret)
+ dev_err(&priv->spi->dev, "Stop ADC conversions failed\n");
+
+ j++;
+ }
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ads124s_probe(struct spi_device *spi)
+{
+ struct ads124s_private *ads124s_priv;
+ struct iio_dev *indio_dev;
+ const struct spi_device_id *spi_id = spi_get_device_id(spi);
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ads124s_priv));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ ads124s_priv = iio_priv(indio_dev);
+
+ ads124s_priv->reset_gpio = devm_gpiod_get_optional(&spi->dev,
+ "reset", GPIOD_OUT_LOW);
+ if (IS_ERR(ads124s_priv->reset_gpio))
+ dev_info(&spi->dev, "Reset GPIO not defined\n");
+
+ ads124s_priv->chip_info = &ads124s_chip_info_tbl[spi_id->driver_data];
+
+ spi_set_drvdata(spi, indio_dev);
+
+ ads124s_priv->spi = spi;
+
+ indio_dev->name = spi_id->name;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->dev.of_node = spi->dev.of_node;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ads124s_priv->chip_info->channels;
+ indio_dev->num_channels = ads124s_priv->chip_info->num_channels;
+ indio_dev->info = &ads124s_info;
+
+ mutex_init(&ads124s_priv->lock);
+
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, NULL,
+ ads124s_trigger_handler, NULL);
+ if (ret) {
+ dev_err(&spi->dev, "iio triggered buffer setup failed\n");
+ return ret;
+ }
+
+ ads124s_reset(indio_dev);
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id ads124s_id[] = {
+ { "ads124s06", ADS124S06_ID },
+ { "ads124s08", ADS124S08_ID },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ads124s_id);
+
+static const struct of_device_id ads124s_of_table[] = {
+ { .compatible = "ti,ads124s06" },
+ { .compatible = "ti,ads124s08" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ads124s_of_table);
+
+static struct spi_driver ads124s_driver = {
+ .driver = {
+ .name = "ads124s08",
+ .of_match_table = ads124s_of_table,
+ },
+ .probe = ads124s_probe,
+ .id_table = ads124s_id,
+};
+module_spi_driver(ads124s_driver);
+
+MODULE_AUTHOR("Dan Murphy <dmuprhy@ti.com>");
+MODULE_DESCRIPTION("TI TI_ADS12S0X ADC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-ads7950.c b/drivers/iio/adc/ti-ads7950.c
index a5bd594..2e66e4d 100644
--- a/drivers/iio/adc/ti-ads7950.c
+++ b/drivers/iio/adc/ti-ads7950.c
@@ -17,6 +17,7 @@
#include <linux/bitops.h>
#include <linux/device.h>
#include <linux/err.h>
+#include <linux/gpio/driver.h>
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
@@ -36,12 +37,15 @@
*/
#define TI_ADS7950_VA_MV_ACPI_DEFAULT 5000
+#define TI_ADS7950_CR_GPIO BIT(14)
#define TI_ADS7950_CR_MANUAL BIT(12)
#define TI_ADS7950_CR_WRITE BIT(11)
#define TI_ADS7950_CR_CHAN(ch) ((ch) << 7)
#define TI_ADS7950_CR_RANGE_5V BIT(6)
+#define TI_ADS7950_CR_GPIO_DATA BIT(4)
#define TI_ADS7950_MAX_CHAN 16
+#define TI_ADS7950_NUM_GPIOS 4
#define TI_ADS7950_TIMESTAMP_SIZE (sizeof(int64_t) / sizeof(__be16))
@@ -49,27 +53,63 @@
#define TI_ADS7950_EXTRACT(val, dec, bits) \
(((val) >> (dec)) & ((1 << (bits)) - 1))
+#define TI_ADS7950_MAN_CMD(cmd) (TI_ADS7950_CR_MANUAL | (cmd))
+#define TI_ADS7950_GPIO_CMD(cmd) (TI_ADS7950_CR_GPIO | (cmd))
+
+/* Manual mode configuration */
+#define TI_ADS7950_MAN_CMD_SETTINGS(st) \
+ (TI_ADS7950_MAN_CMD(TI_ADS7950_CR_WRITE | st->cmd_settings_bitmask))
+/* GPIO mode configuration */
+#define TI_ADS7950_GPIO_CMD_SETTINGS(st) \
+ (TI_ADS7950_GPIO_CMD(st->gpio_cmd_settings_bitmask))
+
struct ti_ads7950_state {
struct spi_device *spi;
- struct spi_transfer ring_xfer[TI_ADS7950_MAX_CHAN + 2];
+ struct spi_transfer ring_xfer;
struct spi_transfer scan_single_xfer[3];
struct spi_message ring_msg;
struct spi_message scan_single_msg;
+ /* Lock to protect the spi xfer buffers */
+ struct mutex slock;
+ struct gpio_chip chip;
+
struct regulator *reg;
unsigned int vref_mv;
- unsigned int settings;
+ /*
+ * Bitmask of lower 7 bits used for configuration
+ * These bits only can be written when TI_ADS7950_CR_WRITE
+ * is set, otherwise it retains its original state.
+ * [0-3] GPIO signal
+ * [4] Set following frame to return GPIO signal values
+ * [5] Powers down device
+ * [6] Sets Vref range1(2.5v) or range2(5v)
+ *
+ * Bits present on Manual/Auto1/Auto2 commands
+ */
+ unsigned int cmd_settings_bitmask;
+
+ /*
+ * Bitmask of GPIO command
+ * [0-3] GPIO direction
+ * [4-6] Different GPIO alarm mode configurations
+ * [7] GPIO 2 as device range input
+ * [8] GPIO 3 as device power down input
+ * [9] Reset all registers
+ * [10-11] N/A
+ */
+ unsigned int gpio_cmd_settings_bitmask;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
*/
- __be16 rx_buf[TI_ADS7950_MAX_CHAN + TI_ADS7950_TIMESTAMP_SIZE]
+ u16 rx_buf[TI_ADS7950_MAX_CHAN + 2 + TI_ADS7950_TIMESTAMP_SIZE]
____cacheline_aligned;
- __be16 tx_buf[TI_ADS7950_MAX_CHAN];
- __be16 single_tx;
- __be16 single_rx;
+ u16 tx_buf[TI_ADS7950_MAX_CHAN + 2];
+ u16 single_tx;
+ u16 single_rx;
};
@@ -108,7 +148,7 @@
.realbits = bits, \
.storagebits = 16, \
.shift = 12 - (bits), \
- .endianness = IIO_BE, \
+ .endianness = IIO_CPU, \
}, \
}
@@ -248,24 +288,15 @@
len = 0;
for_each_set_bit(i, active_scan_mask, indio_dev->num_channels) {
- cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(i) | st->settings;
- st->tx_buf[len++] = cpu_to_be16(cmd);
+ cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(i));
+ st->tx_buf[len++] = cmd;
}
/* Data for the 1st channel is not returned until the 3rd transfer */
- len += 2;
- for (i = 0; i < len; i++) {
- if ((i + 2) < len)
- st->ring_xfer[i].tx_buf = &st->tx_buf[i];
- if (i >= 2)
- st->ring_xfer[i].rx_buf = &st->rx_buf[i - 2];
- st->ring_xfer[i].len = 2;
- st->ring_xfer[i].cs_change = 1;
- }
- /* make sure last transfer's cs_change is not set */
- st->ring_xfer[len - 1].cs_change = 0;
+ st->tx_buf[len++] = 0;
+ st->tx_buf[len++] = 0;
- spi_message_init_with_transfers(&st->ring_msg, st->ring_xfer, len);
+ st->ring_xfer.len = len * 2;
return 0;
}
@@ -277,14 +308,16 @@
struct ti_ads7950_state *st = iio_priv(indio_dev);
int ret;
+ mutex_lock(&st->slock);
ret = spi_sync(st->spi, &st->ring_msg);
if (ret < 0)
goto out;
- iio_push_to_buffers_with_timestamp(indio_dev, st->rx_buf,
+ iio_push_to_buffers_with_timestamp(indio_dev, &st->rx_buf[2],
iio_get_time_ns(indio_dev));
out:
+ mutex_unlock(&st->slock);
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
@@ -295,19 +328,18 @@
struct ti_ads7950_state *st = iio_priv(indio_dev);
int ret, cmd;
- mutex_lock(&indio_dev->mlock);
-
- cmd = TI_ADS7950_CR_WRITE | TI_ADS7950_CR_CHAN(ch) | st->settings;
- st->single_tx = cpu_to_be16(cmd);
+ mutex_lock(&st->slock);
+ cmd = TI_ADS7950_MAN_CMD(TI_ADS7950_CR_CHAN(ch));
+ st->single_tx = cmd;
ret = spi_sync(st->spi, &st->scan_single_msg);
if (ret)
goto out;
- ret = be16_to_cpu(st->single_rx);
+ ret = st->single_rx;
out:
- mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->slock);
return ret;
}
@@ -326,7 +358,7 @@
vref /= 1000;
}
- if (st->settings & TI_ADS7950_CR_RANGE_5V)
+ if (st->cmd_settings_bitmask & TI_ADS7950_CR_RANGE_5V)
vref *= 2;
return vref;
@@ -371,6 +403,132 @@
.update_scan_mode = ti_ads7950_update_scan_mode,
};
+static void ti_ads7950_set(struct gpio_chip *chip, unsigned int offset,
+ int value)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+
+ mutex_lock(&st->slock);
+
+ if (value)
+ st->cmd_settings_bitmask |= BIT(offset);
+ else
+ st->cmd_settings_bitmask &= ~BIT(offset);
+
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ spi_sync(st->spi, &st->scan_single_msg);
+
+ mutex_unlock(&st->slock);
+}
+
+static int ti_ads7950_get(struct gpio_chip *chip, unsigned int offset)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+ int ret;
+
+ mutex_lock(&st->slock);
+
+ /* If set as output, return the output */
+ if (st->gpio_cmd_settings_bitmask & BIT(offset)) {
+ ret = st->cmd_settings_bitmask & BIT(offset);
+ goto out;
+ }
+
+ /* GPIO data bit sets SDO bits 12-15 to GPIO input */
+ st->cmd_settings_bitmask |= TI_ADS7950_CR_GPIO_DATA;
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+ ret = ((st->single_rx >> 12) & BIT(offset)) ? 1 : 0;
+
+ /* Revert back to original settings */
+ st->cmd_settings_bitmask &= ~TI_ADS7950_CR_GPIO_DATA;
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
+static int ti_ads7950_get_direction(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+
+ /* Bitmask is inverted from GPIO framework 0=input/1=output */
+ return !(st->gpio_cmd_settings_bitmask & BIT(offset));
+}
+
+static int _ti_ads7950_set_direction(struct gpio_chip *chip, int offset,
+ int input)
+{
+ struct ti_ads7950_state *st = gpiochip_get_data(chip);
+ int ret = 0;
+
+ mutex_lock(&st->slock);
+
+ /* Only change direction if needed */
+ if (input && (st->gpio_cmd_settings_bitmask & BIT(offset)))
+ st->gpio_cmd_settings_bitmask &= ~BIT(offset);
+ else if (!input && !(st->gpio_cmd_settings_bitmask & BIT(offset)))
+ st->gpio_cmd_settings_bitmask |= BIT(offset);
+ else
+ goto out;
+
+ st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
+static int ti_ads7950_direction_input(struct gpio_chip *chip,
+ unsigned int offset)
+{
+ return _ti_ads7950_set_direction(chip, offset, 1);
+}
+
+static int ti_ads7950_direction_output(struct gpio_chip *chip,
+ unsigned int offset, int value)
+{
+ ti_ads7950_set(chip, offset, value);
+
+ return _ti_ads7950_set_direction(chip, offset, 0);
+}
+
+static int ti_ads7950_init_hw(struct ti_ads7950_state *st)
+{
+ int ret = 0;
+
+ mutex_lock(&st->slock);
+
+ /* Settings for Manual/Auto1/Auto2 commands */
+ /* Default to 5v ref */
+ st->cmd_settings_bitmask = TI_ADS7950_CR_RANGE_5V;
+ st->single_tx = TI_ADS7950_MAN_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+ if (ret)
+ goto out;
+
+ /* Settings for GPIO command */
+ st->gpio_cmd_settings_bitmask = 0x0;
+ st->single_tx = TI_ADS7950_GPIO_CMD_SETTINGS(st);
+ ret = spi_sync(st->spi, &st->scan_single_msg);
+
+out:
+ mutex_unlock(&st->slock);
+
+ return ret;
+}
+
static int ti_ads7950_probe(struct spi_device *spi)
{
struct ti_ads7950_state *st;
@@ -378,6 +536,14 @@
const struct ti_ads7950_chip_info *info;
int ret;
+ spi->bits_per_word = 16;
+ spi->mode |= SPI_CS_WORD;
+ ret = spi_setup(spi);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Error in spi setup\n");
+ return ret;
+ }
+
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
if (!indio_dev)
return -ENOMEM;
@@ -387,7 +553,6 @@
spi_set_drvdata(spi, indio_dev);
st->spi = spi;
- st->settings = TI_ADS7950_CR_MANUAL | TI_ADS7950_CR_RANGE_5V;
info = &ti_ads7950_chip_info[spi_get_device_id(spi)->driver_data];
@@ -398,6 +563,16 @@
indio_dev->num_channels = info->num_channels;
indio_dev->info = &ti_ads7950_info;
+ /* build spi ring message */
+ spi_message_init(&st->ring_msg);
+
+ st->ring_xfer.tx_buf = &st->tx_buf[0];
+ st->ring_xfer.rx_buf = &st->rx_buf[0];
+ /* len will be set later */
+ st->ring_xfer.cs_change = true;
+
+ spi_message_add_tail(&st->ring_xfer, &st->ring_msg);
+
/*
* Setup default message. The sample is read at the end of the first
* transfer, then it takes one full cycle to convert the sample and one
@@ -423,16 +598,19 @@
if (ACPI_COMPANION(&spi->dev))
st->vref_mv = TI_ADS7950_VA_MV_ACPI_DEFAULT;
+ mutex_init(&st->slock);
+
st->reg = devm_regulator_get(&spi->dev, "vref");
if (IS_ERR(st->reg)) {
dev_err(&spi->dev, "Failed get get regulator \"vref\"\n");
- return PTR_ERR(st->reg);
+ ret = PTR_ERR(st->reg);
+ goto error_destroy_mutex;
}
ret = regulator_enable(st->reg);
if (ret) {
dev_err(&spi->dev, "Failed to enable regulator \"vref\"\n");
- return ret;
+ goto error_destroy_mutex;
}
ret = iio_triggered_buffer_setup(indio_dev, NULL,
@@ -442,18 +620,46 @@
goto error_disable_reg;
}
+ ret = ti_ads7950_init_hw(st);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to init adc chip\n");
+ goto error_cleanup_ring;
+ }
+
ret = iio_device_register(indio_dev);
if (ret) {
dev_err(&spi->dev, "Failed to register iio device\n");
goto error_cleanup_ring;
}
+ /* Add GPIO chip */
+ st->chip.label = dev_name(&st->spi->dev);
+ st->chip.parent = &st->spi->dev;
+ st->chip.owner = THIS_MODULE;
+ st->chip.base = -1;
+ st->chip.ngpio = TI_ADS7950_NUM_GPIOS;
+ st->chip.get_direction = ti_ads7950_get_direction;
+ st->chip.direction_input = ti_ads7950_direction_input;
+ st->chip.direction_output = ti_ads7950_direction_output;
+ st->chip.get = ti_ads7950_get;
+ st->chip.set = ti_ads7950_set;
+
+ ret = gpiochip_add_data(&st->chip, st);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to init GPIOs\n");
+ goto error_iio_device;
+ }
+
return 0;
+error_iio_device:
+ iio_device_unregister(indio_dev);
error_cleanup_ring:
iio_triggered_buffer_cleanup(indio_dev);
error_disable_reg:
regulator_disable(st->reg);
+error_destroy_mutex:
+ mutex_destroy(&st->slock);
return ret;
}
@@ -463,9 +669,11 @@
struct iio_dev *indio_dev = spi_get_drvdata(spi);
struct ti_ads7950_state *st = iio_priv(indio_dev);
+ gpiochip_remove(&st->chip);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
regulator_disable(st->reg);
+ mutex_destroy(&st->slock);
return 0;
}
diff --git a/drivers/iio/adc/ti-ads8344.c b/drivers/iio/adc/ti-ads8344.c
new file mode 100644
index 0000000..9a46080
--- /dev/null
+++ b/drivers/iio/adc/ti-ads8344.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ADS8344 16-bit 8-Channel ADC driver
+ *
+ * Author: Gregory CLEMENT <gregory.clement@bootlin.com>
+ *
+ * Datasheet: http://www.ti.com/lit/ds/symlink/ads8344.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/spi/spi.h>
+
+#define ADS8344_START BIT(7)
+#define ADS8344_SINGLE_END BIT(2)
+#define ADS8344_CHANNEL(channel) ((channel) << 4)
+#define ADS8344_CLOCK_INTERNAL 0x2 /* PD1 = 1 and PD0 = 0 */
+
+struct ads8344 {
+ struct spi_device *spi;
+ struct regulator *reg;
+ /*
+ * Lock protecting access to adc->tx_buff and rx_buff,
+ * especially from concurrent read on sysfs file.
+ */
+ struct mutex lock;
+
+ u8 tx_buf ____cacheline_aligned;
+ u16 rx_buf;
+};
+
+#define ADS8344_VOLTAGE_CHANNEL(chan, si) \
+ { \
+ .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), \
+ }
+
+#define ADS8344_VOLTAGE_CHANNEL_DIFF(chan1, chan2, si) \
+ { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .channel = (chan1), \
+ .channel2 = (chan2), \
+ .differential = 1, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ }
+
+static const struct iio_chan_spec ads8344_channels[] = {
+ ADS8344_VOLTAGE_CHANNEL(0, 0),
+ ADS8344_VOLTAGE_CHANNEL(1, 4),
+ ADS8344_VOLTAGE_CHANNEL(2, 1),
+ ADS8344_VOLTAGE_CHANNEL(3, 5),
+ ADS8344_VOLTAGE_CHANNEL(4, 2),
+ ADS8344_VOLTAGE_CHANNEL(5, 6),
+ ADS8344_VOLTAGE_CHANNEL(6, 3),
+ ADS8344_VOLTAGE_CHANNEL(7, 7),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(0, 1, 8),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(2, 3, 9),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(4, 5, 10),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(6, 7, 11),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(1, 0, 12),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(3, 2, 13),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(5, 4, 14),
+ ADS8344_VOLTAGE_CHANNEL_DIFF(7, 6, 15),
+};
+
+static int ads8344_adc_conversion(struct ads8344 *adc, int channel,
+ bool differential)
+{
+ struct spi_device *spi = adc->spi;
+ int ret;
+
+ adc->tx_buf = ADS8344_START;
+ if (!differential)
+ adc->tx_buf |= ADS8344_SINGLE_END;
+ adc->tx_buf |= ADS8344_CHANNEL(channel);
+ adc->tx_buf |= ADS8344_CLOCK_INTERNAL;
+
+ ret = spi_write(spi, &adc->tx_buf, 1);
+ if (ret)
+ return ret;
+
+ udelay(9);
+
+ ret = spi_read(spi, &adc->rx_buf, 2);
+ if (ret)
+ return ret;
+
+ return adc->rx_buf;
+}
+
+static int ads8344_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *channel, int *value,
+ int *shift, long mask)
+{
+ struct ads8344 *adc = iio_priv(iio);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&adc->lock);
+ *value = ads8344_adc_conversion(adc, channel->scan_index,
+ channel->differential);
+ mutex_unlock(&adc->lock);
+ if (*value < 0)
+ return *value;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *value = regulator_get_voltage(adc->reg);
+ if (*value < 0)
+ return *value;
+
+ /* convert regulator output voltage to mV */
+ *value /= 1000;
+ *shift = 16;
+
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info ads8344_info = {
+ .read_raw = ads8344_read_raw,
+};
+
+static int ads8344_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ads8344 *adc;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*adc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ adc = iio_priv(indio_dev);
+ adc->spi = spi;
+ mutex_init(&adc->lock);
+
+ indio_dev->name = dev_name(&spi->dev);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->dev.of_node = spi->dev.of_node;
+ indio_dev->info = &ads8344_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ads8344_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ads8344_channels);
+
+ adc->reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(adc->reg))
+ return PTR_ERR(adc->reg);
+
+ ret = regulator_enable(adc->reg);
+ if (ret)
+ return ret;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ regulator_disable(adc->reg);
+ return ret;
+ }
+
+ return 0;
+}
+
+static int ads8344_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ads8344 *adc = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(adc->reg);
+
+ return 0;
+}
+
+static const struct of_device_id ads8344_of_match[] = {
+ { .compatible = "ti,ads8344", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, ads8344_of_match);
+
+static struct spi_driver ads8344_driver = {
+ .driver = {
+ .name = "ads8344",
+ .of_match_table = ads8344_of_match,
+ },
+ .probe = ads8344_probe,
+ .remove = ads8344_remove,
+};
+module_spi_driver(ads8344_driver);
+
+MODULE_AUTHOR("Gregory CLEMENT <gregory.clement@bootlin.com>");
+MODULE_DESCRIPTION("ADS8344 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/adc/ti-ads8688.c b/drivers/iio/adc/ti-ads8688.c
index 184d686..14fe7c3 100644
--- a/drivers/iio/adc/ti-ads8688.c
+++ b/drivers/iio/adc/ti-ads8688.c
@@ -1,9 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Prevas A/S
- *
- * 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/device.h>
@@ -41,6 +38,7 @@
#define ADS8688_VREF_MV 4096
#define ADS8688_REALBITS 16
+#define ADS8688_MAX_CHANNELS 8
/*
* enum ads8688_range - ADS8688 reference voltage range
@@ -385,7 +383,7 @@
{
struct iio_poll_func *pf = p;
struct iio_dev *indio_dev = pf->indio_dev;
- u16 buffer[8];
+ u16 buffer[ADS8688_MAX_CHANNELS + sizeof(s64)/sizeof(u16)];
int i, j = 0;
for (i = 0; i < indio_dev->masklength; i++) {
@@ -396,7 +394,7 @@
}
iio_push_to_buffers_with_timestamp(indio_dev, buffer,
- pf->timestamp);
+ iio_get_time_ns(indio_dev));
iio_trigger_notify_done(indio_dev->trig);
@@ -522,6 +520,6 @@
};
module_spi_driver(ads8688_driver);
-MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>");
+MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.dk>");
MODULE_DESCRIPTION("Texas Instruments ADS8688 driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/adc/ti-tlc4541.c b/drivers/iio/adc/ti-tlc4541.c
index 2290024..4965246 100644
--- a/drivers/iio/adc/ti-tlc4541.c
+++ b/drivers/iio/adc/ti-tlc4541.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TI tlc4541 ADC Driver
*
@@ -7,10 +8,6 @@
* http://www.ti.com/lit/gpn/tlc3541
* http://www.ti.com/lit/gpn/tlc4541
*
- * 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.
- *
* The tlc4541 requires 24 clock cycles to start a transfer.
* Conversion then takes 2.94us to complete before data is ready
* Data is returned MSB first.
diff --git a/drivers/iio/adc/ti_am335x_adc.c b/drivers/iio/adc/ti_am335x_adc.c
index 80df5a3..9d984f2 100644
--- a/drivers/iio/adc/ti_am335x_adc.c
+++ b/drivers/iio/adc/ti_am335x_adc.c
@@ -142,7 +142,10 @@
stepconfig |= STEPCONFIG_MODE_SWCNT;
tiadc_writel(adc_dev, REG_STEPCONFIG(steps),
- stepconfig | STEPCONFIG_INP(chan));
+ stepconfig | STEPCONFIG_INP(chan) |
+ STEPCONFIG_INM_ADCREFM |
+ STEPCONFIG_RFP_VREFP |
+ STEPCONFIG_RFM_VREFN);
if (adc_dev->open_delay[i] > STEPDELAY_OPEN_MASK) {
dev_warn(dev, "chan %d open delay truncating to 0x3FFFF\n",
@@ -693,16 +696,12 @@
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
struct tiadc_device *adc_dev = iio_priv(indio_dev);
- struct ti_tscadc_dev *tscadc_dev;
unsigned int idle;
- tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev));
- if (!device_may_wakeup(tscadc_dev->dev)) {
- idle = tiadc_readl(adc_dev, REG_CTRL);
- idle &= ~(CNTRLREG_TSCSSENB);
- tiadc_writel(adc_dev, REG_CTRL, (idle |
- CNTRLREG_POWERDOWN));
- }
+ idle = tiadc_readl(adc_dev, REG_CTRL);
+ idle &= ~(CNTRLREG_TSCSSENB);
+ tiadc_writel(adc_dev, REG_CTRL, (idle |
+ CNTRLREG_POWERDOWN));
return 0;
}
diff --git a/drivers/iio/adc/twl4030-madc.c b/drivers/iio/adc/twl4030-madc.c
index 8c019bb..55c5119 100644
--- a/drivers/iio/adc/twl4030-madc.c
+++ b/drivers/iio/adc/twl4030-madc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
*
* TWL4030 MADC module driver-This driver monitors the real time
@@ -12,21 +13,6 @@
* Mikko Ylinen <mikko.k.ylinen@nokia.com>
*
* Amit Kucheria <amit.kucheria@canonical.com>
- *
- * 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.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#include <linux/device.h>
diff --git a/drivers/iio/adc/twl6030-gpadc.c b/drivers/iio/adc/twl6030-gpadc.c
index e470510..f24148b 100644
--- a/drivers/iio/adc/twl6030-gpadc.c
+++ b/drivers/iio/adc/twl6030-gpadc.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* TWL6030 GPADC module driver
*
@@ -12,21 +13,6 @@
* Based on twl4030-madc.c
* Copyright (C) 2008 Nokia Corporation
* Mikko Ylinen <mikko.k.ylinen@nokia.com>
- *
- * 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.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
*/
#include <linux/interrupt.h>
#include <linux/kernel.h>
@@ -919,10 +905,8 @@
}
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "failed to get irq\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_threaded_irq(dev, irq, NULL,
twl6030_gpadc_irq_handler,
diff --git a/drivers/iio/adc/vf610_adc.c b/drivers/iio/adc/vf610_adc.c
index bbcb7a4..98b3047 100644
--- a/drivers/iio/adc/vf610_adc.c
+++ b/drivers/iio/adc/vf610_adc.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale Vybrid vf610 ADC driver
*
* Copyright 2013 Freescale Semiconductor, Inc.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
@@ -834,10 +821,8 @@
return PTR_ERR(info->regs);
irq = platform_get_irq(pdev, 0);
- if (irq < 0) {
- dev_err(&pdev->dev, "no irq resource?\n");
+ if (irq < 0)
return irq;
- }
ret = devm_request_irq(info->dev, irq,
vf610_adc_isr, 0,
diff --git a/drivers/iio/adc/viperboard_adc.c b/drivers/iio/adc/viperboard_adc.c
index 53eb5a4..1d2aeb0 100644
--- a/drivers/iio/adc/viperboard_adc.c
+++ b/drivers/iio/adc/viperboard_adc.c
@@ -1,15 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Nano River Technologies viperboard IIO ADC driver
*
* (C) 2012 by Lemonage GmbH
* Author: Lars Poeschel <poeschel@lemonage.de>
* All rights reserved.
- *
- * 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.
- *
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/adc/xilinx-xadc-core.c b/drivers/iio/adc/xilinx-xadc-core.c
index 3f6be5a..4fd3896 100644
--- a/drivers/iio/adc/xilinx-xadc-core.c
+++ b/drivers/iio/adc/xilinx-xadc-core.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Xilinx XADC driver
*
* Copyright 2013-2014 Analog Devices Inc.
* Author: Lars-Peter Clauen <lars@metafoo.de>
*
- * Licensed under the GPL-2.
- *
* Documentation for the parts can be found at:
* - XADC hardmacro: Xilinx UG480
* - ZYNQ XADC interface: Xilinx UG585
@@ -1273,8 +1272,10 @@
xadc->threshold[i] = 0xffff;
else
xadc->threshold[i] = 0;
- xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
+ ret = xadc_write_adc_reg(xadc, XADC_REG_THRESHOLD(i),
xadc->threshold[i]);
+ if (ret)
+ goto err_free_irq;
}
/* Go to non-buffered mode */
@@ -1290,6 +1291,7 @@
err_free_irq:
free_irq(xadc->irq, indio_dev);
+ cancel_delayed_work_sync(&xadc->zynq_unmask_work);
err_clk_disable_unprepare:
clk_disable_unprepare(xadc->clk);
err_free_samplerate_trigger:
@@ -1319,8 +1321,8 @@
iio_triggered_buffer_cleanup(indio_dev);
}
free_irq(xadc->irq, indio_dev);
+ cancel_delayed_work_sync(&xadc->zynq_unmask_work);
clk_disable_unprepare(xadc->clk);
- cancel_delayed_work(&xadc->zynq_unmask_work);
kfree(xadc->data);
kfree(indio_dev->channels);
diff --git a/drivers/iio/adc/xilinx-xadc-events.c b/drivers/iio/adc/xilinx-xadc-events.c
index dc06703..dbfd5da 100644
--- a/drivers/iio/adc/xilinx-xadc-events.c
+++ b/drivers/iio/adc/xilinx-xadc-events.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Xilinx XADC driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clauen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/iio/events.h>
diff --git a/drivers/iio/adc/xilinx-xadc.h b/drivers/iio/adc/xilinx-xadc.h
index 8c00095..4017f18 100644
--- a/drivers/iio/adc/xilinx-xadc.h
+++ b/drivers/iio/adc/xilinx-xadc.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Xilinx XADC driver
*
* Copyright 2013 Analog Devices Inc.
* Author: Lars-Peter Clauen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#ifndef __IIO_XILINX_XADC__
diff --git a/drivers/iio/afe/Kconfig b/drivers/iio/afe/Kconfig
index c91eef0..4fa3978 100644
--- a/drivers/iio/afe/Kconfig
+++ b/drivers/iio/afe/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Analog Front End drivers
#
diff --git a/drivers/iio/afe/Makefile b/drivers/iio/afe/Makefile
index 5fabb7b..4c56c8e 100644
--- a/drivers/iio/afe/Makefile
+++ b/drivers/iio/afe/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for industrial I/O Analog Front Ends (AFE)
#
diff --git a/drivers/iio/amplifiers/Kconfig b/drivers/iio/amplifiers/Kconfig
index e9c5f2c..da7f126 100644
--- a/drivers/iio/amplifiers/Kconfig
+++ b/drivers/iio/amplifiers/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0
#
# Gain Amplifiers, etc.
#
@@ -6,12 +7,17 @@
menu "Amplifiers"
config AD8366
- tristate "Analog Devices AD8366 VGA"
+ tristate "Analog Devices AD8366 and similar Gain Amplifiers"
depends on SPI
+ depends on GPIOLIB
select BITREVERSE
help
- Say yes here to build support for Analog Devices AD8366
- SPI Dual-Digital Variable Gain Amplifier (VGA).
+ Say yes here to build support for Analog Devices AD8366 and similar
+ gain amplifiers. This driver supports the following gain amplifiers
+ from Analog Devices:
+ AD8366 Dual-Digital Variable Gain Amplifier (VGA)
+ ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
+ ADL5240 Digitally controlled variable gain amplifier (VGA)
To compile this driver as a module, choose M here: the
module will be called ad8366.
diff --git a/drivers/iio/amplifiers/Makefile b/drivers/iio/amplifiers/Makefile
index 8da4b78..9abef2e 100644
--- a/drivers/iio/amplifiers/Makefile
+++ b/drivers/iio/amplifiers/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile iio/amplifiers
#
diff --git a/drivers/iio/amplifiers/ad8366.c b/drivers/iio/amplifiers/ad8366.c
index 0138337..0176d3d 100644
--- a/drivers/iio/amplifiers/ad8366.c
+++ b/drivers/iio/amplifiers/ad8366.c
@@ -1,9 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0
/*
- * AD8366 SPI Dual-Digital Variable Gain Amplifier (VGA)
+ * AD8366 and similar Gain Amplifiers
+ * This driver supports the following gain amplifiers:
+ * AD8366 Dual-Digital Variable Gain Amplifier (VGA)
+ * ADA4961 BiCMOS RF Digital Gain Amplifier (DGA)
+ * ADL5240 Digitally controlled variable gain amplifier (VGA)
*
- * Copyright 2012 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
+ * Copyright 2012-2019 Analog Devices Inc.
*/
#include <linux/device.h>
@@ -12,6 +15,7 @@
#include <linux/sysfs.h>
#include <linux/spi/spi.h>
#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
#include <linux/err.h>
#include <linux/module.h>
#include <linux/bitrev.h>
@@ -19,10 +23,25 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+enum ad8366_type {
+ ID_AD8366,
+ ID_ADA4961,
+ ID_ADL5240,
+};
+
+struct ad8366_info {
+ int gain_min;
+ int gain_max;
+};
+
struct ad8366_state {
struct spi_device *spi;
struct regulator *reg;
+ struct mutex lock; /* protect sensor state */
+ struct gpio_desc *reset_gpio;
unsigned char ch[2];
+ enum ad8366_type type;
+ struct ad8366_info *info;
/*
* DMA (thus cache coherency maintenance) requires the
* transfer buffers to live in their own cache lines.
@@ -30,19 +49,44 @@
unsigned char data[2] ____cacheline_aligned;
};
+static struct ad8366_info ad8366_infos[] = {
+ [ID_AD8366] = {
+ .gain_min = 4500,
+ .gain_max = 20500,
+ },
+ [ID_ADA4961] = {
+ .gain_min = -6000,
+ .gain_max = 15000,
+ },
+ [ID_ADL5240] = {
+ .gain_min = -11500,
+ .gain_max = 20000,
+ },
+};
+
static int ad8366_write(struct iio_dev *indio_dev,
unsigned char ch_a, unsigned char ch_b)
{
struct ad8366_state *st = iio_priv(indio_dev);
int ret;
- ch_a = bitrev8(ch_a & 0x3F);
- ch_b = bitrev8(ch_b & 0x3F);
+ switch (st->type) {
+ case ID_AD8366:
+ ch_a = bitrev8(ch_a & 0x3F);
+ ch_b = bitrev8(ch_b & 0x3F);
- st->data[0] = ch_b >> 4;
- st->data[1] = (ch_b << 4) | (ch_a >> 2);
+ st->data[0] = ch_b >> 4;
+ st->data[1] = (ch_b << 4) | (ch_a >> 2);
+ break;
+ case ID_ADA4961:
+ st->data[0] = ch_a & 0x1F;
+ break;
+ case ID_ADL5240:
+ st->data[0] = (ch_a & 0x3F);
+ break;
+ }
- ret = spi_write(st->spi, st->data, ARRAY_SIZE(st->data));
+ ret = spi_write(st->spi, st->data, indio_dev->num_channels);
if (ret < 0)
dev_err(&indio_dev->dev, "write failed (%d)", ret);
@@ -57,24 +101,35 @@
{
struct ad8366_state *st = iio_priv(indio_dev);
int ret;
- unsigned code;
+ int code, gain = 0;
- mutex_lock(&indio_dev->mlock);
+ mutex_lock(&st->lock);
switch (m) {
case IIO_CHAN_INFO_HARDWAREGAIN:
code = st->ch[chan->channel];
+ switch (st->type) {
+ case ID_AD8366:
+ gain = code * 253 + 4500;
+ break;
+ case ID_ADA4961:
+ gain = 15000 - code * 1000;
+ break;
+ case ID_ADL5240:
+ gain = 20000 - 31500 + code * 500;
+ break;
+ }
+
/* Values in dB */
- code = code * 253 + 4500;
- *val = code / 1000;
- *val2 = (code % 1000) * 1000;
+ *val = gain / 1000;
+ *val2 = (gain % 1000) * 1000;
ret = IIO_VAL_INT_PLUS_MICRO_DB;
break;
default:
ret = -EINVAL;
}
- mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->lock);
return ret;
};
@@ -86,21 +141,32 @@
long mask)
{
struct ad8366_state *st = iio_priv(indio_dev);
- unsigned code;
+ struct ad8366_info *inf = st->info;
+ int code = 0, gain;
int ret;
- if (val < 0 || val2 < 0)
- return -EINVAL;
-
/* Values in dB */
- code = (((u8)val * 1000) + ((u32)val2 / 1000));
+ if (val < 0)
+ gain = (val * 1000) - (val2 / 1000);
+ else
+ gain = (val * 1000) + (val2 / 1000);
- if (code > 20500 || code < 4500)
+ if (gain > inf->gain_max || gain < inf->gain_min)
return -EINVAL;
- code = (code - 4500) / 253;
+ switch (st->type) {
+ case ID_AD8366:
+ code = (gain - 4500) / 253;
+ break;
+ case ID_ADA4961:
+ code = (15000 - gain) / 1000;
+ break;
+ case ID_ADL5240:
+ code = ((gain - 500 - 20000) / 500) & 0x3F;
+ break;
+ }
- mutex_lock(&indio_dev->mlock);
+ mutex_lock(&st->lock);
switch (mask) {
case IIO_CHAN_INFO_HARDWAREGAIN:
st->ch[chan->channel] = code;
@@ -109,7 +175,7 @@
default:
ret = -EINVAL;
}
- mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->lock);
return ret;
}
@@ -132,6 +198,10 @@
AD8366_CHAN(1),
};
+static const struct iio_chan_spec ada4961_channels[] = {
+ AD8366_CHAN(0),
+};
+
static int ad8366_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
@@ -152,14 +222,33 @@
}
spi_set_drvdata(spi, indio_dev);
+ mutex_init(&st->lock);
st->spi = spi;
+ st->type = spi_get_device_id(spi)->driver_data;
+ switch (st->type) {
+ case ID_AD8366:
+ indio_dev->channels = ad8366_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ad8366_channels);
+ break;
+ case ID_ADA4961:
+ case ID_ADL5240:
+ st->reset_gpio = devm_gpiod_get(&spi->dev, "reset",
+ GPIOD_OUT_HIGH);
+ indio_dev->channels = ada4961_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ada4961_channels);
+ break;
+ default:
+ dev_err(&spi->dev, "Invalid device ID\n");
+ ret = -EINVAL;
+ goto error_disable_reg;
+ }
+
+ st->info = &ad8366_infos[st->type];
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi_get_device_id(spi)->name;
indio_dev->info = &ad8366_info;
indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->channels = ad8366_channels;
- indio_dev->num_channels = ARRAY_SIZE(ad8366_channels);
ret = ad8366_write(indio_dev, 0 , 0);
if (ret < 0)
@@ -193,7 +282,9 @@
}
static const struct spi_device_id ad8366_id[] = {
- {"ad8366", 0},
+ {"ad8366", ID_AD8366},
+ {"ada4961", ID_ADA4961},
+ {"adl5240", ID_ADL5240},
{}
};
MODULE_DEVICE_TABLE(spi, ad8366_id);
@@ -209,6 +300,6 @@
module_spi_driver(ad8366_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
-MODULE_DESCRIPTION("Analog Devices AD8366 VGA");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
+MODULE_DESCRIPTION("Analog Devices AD8366 and similar Gain Amplifiers");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/buffer/Kconfig b/drivers/iio/buffer/Kconfig
index 338774c..63f265c 100644
--- a/drivers/iio/buffer/Kconfig
+++ b/drivers/iio/buffer/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Industrial I/O generic buffer implementations
#
diff --git a/drivers/iio/buffer/industrialio-buffer-cb.c b/drivers/iio/buffer/industrialio-buffer-cb.c
index ea63c83..47c96f7 100644
--- a/drivers/iio/buffer/industrialio-buffer-cb.c
+++ b/drivers/iio/buffer/industrialio-buffer-cb.c
@@ -1,8 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* The industrial I/O callback buffer
- *
- * 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/kernel.h>
@@ -36,7 +33,8 @@
static void iio_buffer_cb_release(struct iio_buffer *buffer)
{
struct iio_cb_buffer *cb_buff = buffer_to_cb_buffer(buffer);
- kfree(cb_buff->buffer.scan_mask);
+
+ bitmap_free(cb_buff->buffer.scan_mask);
kfree(cb_buff);
}
@@ -74,9 +72,8 @@
}
cb_buff->indio_dev = cb_buff->channels[0].indio_dev;
- cb_buff->buffer.scan_mask
- = kcalloc(BITS_TO_LONGS(cb_buff->indio_dev->masklength),
- sizeof(long), GFP_KERNEL);
+ cb_buff->buffer.scan_mask = bitmap_zalloc(cb_buff->indio_dev->masklength,
+ GFP_KERNEL);
if (cb_buff->buffer.scan_mask == NULL) {
ret = -ENOMEM;
goto error_release_channels;
@@ -95,7 +92,7 @@
return cb_buff;
error_free_scan_mask:
- kfree(cb_buff->buffer.scan_mask);
+ bitmap_free(cb_buff->buffer.scan_mask);
error_release_channels:
iio_channel_release_all(cb_buff->channels);
error_free_cb_buff:
diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c
index b32bf57..90cf6e5 100644
--- a/drivers/iio/buffer/industrialio-buffer-dma.c
+++ b/drivers/iio/buffer/industrialio-buffer-dma.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2013-2015 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/slab.h>
diff --git a/drivers/iio/buffer/industrialio-buffer-dmaengine.c b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
index 2b5a320..bea4a75 100644
--- a/drivers/iio/buffer/industrialio-buffer-dmaengine.c
+++ b/drivers/iio/buffer/industrialio-buffer-dmaengine.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright 2014-2015 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/slab.h>
diff --git a/drivers/iio/buffer/industrialio-triggered-buffer.c b/drivers/iio/buffer/industrialio-triggered-buffer.c
index d3db1fc..cb322b2 100644
--- a/drivers/iio/buffer/industrialio-triggered-buffer.c
+++ b/drivers/iio/buffer/industrialio-triggered-buffer.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012 Analog Devices, Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * 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/kernel.h>
diff --git a/drivers/iio/buffer/kfifo_buf.c b/drivers/iio/buffer/kfifo_buf.c
index 70c302a..e78fc08 100644
--- a/drivers/iio/buffer/kfifo_buf.c
+++ b/drivers/iio/buffer/kfifo_buf.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/iio/chemical/Kconfig b/drivers/iio/chemical/Kconfig
index b8e005b..fa45860 100644
--- a/drivers/iio/chemical/Kconfig
+++ b/drivers/iio/chemical/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Chemical sensors
#
@@ -12,14 +13,14 @@
select IIO_TRIGGERED_BUFFER
select IRQ_WORK
help
- Say Y here to build I2C interface support for the following
- Atlas Scientific OEM SM sensors:
+ Say Y here to build I2C interface support for the following
+ Atlas Scientific OEM SM sensors:
* pH SM sensor
* EC SM sensor
* ORP SM sensor
- To compile this driver as module, choose M here: the
- module will be called atlas-ph-sensor.
+ To compile this driver as module, choose M here: the
+ module will be called atlas-ph-sensor.
config BME680
tristate "Bosch Sensortec BME680 sensor driver"
@@ -47,8 +48,8 @@
config CCS811
tristate "AMS CCS811 VOC sensor"
depends on I2C
- select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
help
Say Y here to build I2C interface support for the AMS
CCS811 VOC (Volatile Organic Compounds) sensor
@@ -61,6 +62,41 @@
iAQ-Core Continuous/Pulsed VOC (Volatile Organic Compounds)
sensors
+config PMS7003
+ tristate "Plantower PMS7003 particulate matter sensor"
+ depends on SERIAL_DEV_BUS
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say Y here to build support for the Plantower PMS7003 particulate
+ matter sensor.
+
+ To compile this driver as a module, choose M here: the module will
+ be called pms7003.
+
+config SENSIRION_SGP30
+ tristate "Sensirion SGPxx gas sensors"
+ depends on I2C
+ select CRC8
+ help
+ Say Y here to build I2C interface support for the following
+ Sensirion SGP gas sensors:
+ * SGP30 gas sensor
+ * SGPC3 low power gas sensor
+
+ To compile this driver as module, choose M here: the
+ module will be called sgp30.
+
+config SPS30
+ tristate "SPS30 particulate matter sensor"
+ depends on I2C
+ select CRC8
+ help
+ Say Y here to build support for the Sensirion SPS30 particulate
+ matter sensor.
+
+ To compile this driver as a module, choose M here: the module will
+ be called sps30.
+
config VZ89X
tristate "SGX Sensortech MiCS VZ89X VOC sensor"
depends on I2C
diff --git a/drivers/iio/chemical/Makefile b/drivers/iio/chemical/Makefile
index 2f4c4ba..f97270b 100644
--- a/drivers/iio/chemical/Makefile
+++ b/drivers/iio/chemical/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for IIO chemical sensors
#
@@ -9,4 +10,7 @@
obj-$(CONFIG_BME680_SPI) += bme680_spi.o
obj-$(CONFIG_CCS811) += ccs811.o
obj-$(CONFIG_IAQCORE) += ams-iaq-core.o
+obj-$(CONFIG_PMS7003) += pms7003.o
+obj-$(CONFIG_SENSIRION_SGP30) += sgp30.o
+obj-$(CONFIG_SPS30) += sps30.o
obj-$(CONFIG_VZ89X) += vz89x.o
diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c
index a406ad3..3a20cb5 100644
--- a/drivers/iio/chemical/atlas-ph-sensor.c
+++ b/drivers/iio/chemical/atlas-ph-sensor.c
@@ -444,9 +444,8 @@
case IIO_CHAN_INFO_SCALE:
switch (chan->type) {
case IIO_TEMP:
- *val = 1; /* 0.01 */
- *val2 = 100;
- break;
+ *val = 10;
+ return IIO_VAL_INT;
case IIO_PH:
*val = 1; /* 0.001 */
*val2 = 1000;
@@ -477,7 +476,7 @@
int val, int val2, long mask)
{
struct atlas_data *data = iio_priv(indio_dev);
- __be32 reg = cpu_to_be32(val);
+ __be32 reg = cpu_to_be32(val / 10);
if (val2 != 0 || val < 0 || val > 20000)
return -EINVAL;
diff --git a/drivers/iio/chemical/bme680.h b/drivers/iio/chemical/bme680.h
index e049323..4edc5d2 100644
--- a/drivers/iio/chemical/bme680.h
+++ b/drivers/iio/chemical/bme680.h
@@ -2,12 +2,10 @@
#ifndef BME680_H_
#define BME680_H_
-#define BME680_REG_CHIP_I2C_ID 0xD0
-#define BME680_REG_CHIP_SPI_ID 0x50
-#define BME680_CHIP_ID_VAL 0x61
-#define BME680_REG_SOFT_RESET_I2C 0xE0
-#define BME680_REG_SOFT_RESET_SPI 0x60
-#define BME680_CMD_SOFTRESET 0xB6
+#define BME680_REG_CHIP_ID 0xD0
+#define BME680_CHIP_ID_VAL 0x61
+#define BME680_REG_SOFT_RESET 0xE0
+#define BME680_CMD_SOFTRESET 0xB6
#define BME680_REG_STATUS 0x73
#define BME680_SPI_MEM_PAGE_BIT BIT(4)
#define BME680_SPI_MEM_PAGE_1_VAL 1
@@ -18,6 +16,7 @@
#define BME680_REG_GAS_MSB 0x2A
#define BME680_REG_GAS_R_LSB 0x2B
#define BME680_GAS_STAB_BIT BIT(4)
+#define BME680_GAS_RANGE_MASK GENMASK(3, 0)
#define BME680_REG_CTRL_HUMIDITY 0x72
#define BME680_OSRS_HUMIDITY_MASK GENMASK(2, 0)
@@ -26,9 +25,8 @@
#define BME680_OSRS_TEMP_MASK GENMASK(7, 5)
#define BME680_OSRS_PRESS_MASK GENMASK(4, 2)
#define BME680_MODE_MASK GENMASK(1, 0)
-
-#define BME680_MODE_FORCED 1
-#define BME680_MODE_SLEEP 0
+#define BME680_MODE_FORCED 1
+#define BME680_MODE_SLEEP 0
#define BME680_REG_CONFIG 0x75
#define BME680_FILTER_MASK GENMASK(4, 2)
@@ -39,24 +37,21 @@
#define BME680_MAX_OVERFLOW_VAL 0x40000000
#define BME680_HUM_REG_SHIFT_VAL 4
-#define BME680_BIT_H1_DATA_MSK 0x0F
+#define BME680_BIT_H1_DATA_MASK GENMASK(3, 0)
#define BME680_REG_RES_HEAT_RANGE 0x02
-#define BME680_RHRANGE_MSK 0x30
+#define BME680_RHRANGE_MASK GENMASK(5, 4)
#define BME680_REG_RES_HEAT_VAL 0x00
#define BME680_REG_RANGE_SW_ERR 0x04
-#define BME680_RSERROR_MSK 0xF0
+#define BME680_RSERROR_MASK GENMASK(7, 4)
#define BME680_REG_RES_HEAT_0 0x5A
#define BME680_REG_GAS_WAIT_0 0x64
-#define BME680_GAS_RANGE_MASK 0x0F
#define BME680_ADC_GAS_RES_SHIFT 6
#define BME680_AMB_TEMP 25
#define BME680_REG_CTRL_GAS_1 0x71
#define BME680_RUN_GAS_MASK BIT(4)
#define BME680_NB_CONV_MASK GENMASK(3, 0)
-#define BME680_RUN_GAS_EN_BIT BIT(4)
-#define BME680_NB_CONV_0_VAL 0
#define BME680_REG_MEAS_STAT_0 0x1D
#define BME680_GAS_MEAS_BIT BIT(6)
diff --git a/drivers/iio/chemical/bme680_core.c b/drivers/iio/chemical/bme680_core.c
index 7d9bb62..ccde4c6 100644
--- a/drivers/iio/chemical/bme680_core.c
+++ b/drivers/iio/chemical/bme680_core.c
@@ -63,9 +63,23 @@
s32 t_fine;
};
+static const struct regmap_range bme680_volatile_ranges[] = {
+ regmap_reg_range(BME680_REG_MEAS_STAT_0, BME680_REG_GAS_R_LSB),
+ regmap_reg_range(BME680_REG_STATUS, BME680_REG_STATUS),
+ regmap_reg_range(BME680_T2_LSB_REG, BME680_GH3_REG),
+};
+
+static const struct regmap_access_table bme680_volatile_table = {
+ .yes_ranges = bme680_volatile_ranges,
+ .n_yes_ranges = ARRAY_SIZE(bme680_volatile_ranges),
+};
+
const struct regmap_config bme680_regmap_config = {
.reg_bits = 8,
.val_bits = 8,
+ .max_register = 0xef,
+ .volatile_table = &bme680_volatile_table,
+ .cache_type = REGCACHE_RBTREE,
};
EXPORT_SYMBOL(bme680_regmap_config);
@@ -91,8 +105,6 @@
},
};
-static const int bme680_oversampling_avail[] = { 1, 2, 4, 8, 16 };
-
static int bme680_read_calib(struct bme680_data *data,
struct bme680_calib *calib)
{
@@ -102,16 +114,14 @@
__le16 buf;
/* Temperature related coefficients */
- ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG,
- (u8 *) &buf, 2);
+ ret = regmap_bulk_read(data->regmap, BME680_T1_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_T1_LSB_REG\n");
return ret;
}
calib->par_t1 = le16_to_cpu(buf);
- ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG,
- (u8 *) &buf, 2);
+ ret = regmap_bulk_read(data->regmap, BME680_T2_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_T2_LSB_REG\n");
return ret;
@@ -126,16 +136,14 @@
calib->par_t3 = tmp;
/* Pressure related coefficients */
- ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG,
- (u8 *) &buf, 2);
+ ret = regmap_bulk_read(data->regmap, BME680_P1_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P1_LSB_REG\n");
return ret;
}
calib->par_p1 = le16_to_cpu(buf);
- ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG,
- (u8 *) &buf, 2);
+ ret = regmap_bulk_read(data->regmap, BME680_P2_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P2_LSB_REG\n");
return ret;
@@ -149,16 +157,14 @@
}
calib->par_p3 = tmp;
- ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG,
- (u8 *) &buf, 2);
+ ret = regmap_bulk_read(data->regmap, BME680_P4_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P4_LSB_REG\n");
return ret;
}
calib->par_p4 = le16_to_cpu(buf);
- ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG,
- (u8 *) &buf, 2);
+ ret = regmap_bulk_read(data->regmap, BME680_P5_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P5_LSB_REG\n");
return ret;
@@ -179,16 +185,14 @@
}
calib->par_p7 = tmp;
- ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG,
- (u8 *) &buf, 2);
+ ret = regmap_bulk_read(data->regmap, BME680_P8_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P8_LSB_REG\n");
return ret;
}
calib->par_p8 = le16_to_cpu(buf);
- ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG,
- (u8 *) &buf, 2);
+ ret = regmap_bulk_read(data->regmap, BME680_P9_LSB_REG, (u8 *) &buf, 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_P9_LSB_REG\n");
return ret;
@@ -208,30 +212,26 @@
dev_err(dev, "failed to read BME680_H1_MSB_REG\n");
return ret;
}
-
ret = regmap_read(data->regmap, BME680_H1_LSB_REG, &tmp_lsb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H1_LSB_REG\n");
return ret;
}
-
calib->par_h1 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
- (tmp_lsb & BME680_BIT_H1_DATA_MSK);
+ (tmp_lsb & BME680_BIT_H1_DATA_MASK);
ret = regmap_read(data->regmap, BME680_H2_MSB_REG, &tmp_msb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H2_MSB_REG\n");
return ret;
}
-
ret = regmap_read(data->regmap, BME680_H2_LSB_REG, &tmp_lsb);
if (ret < 0) {
dev_err(dev, "failed to read BME680_H2_LSB_REG\n");
return ret;
}
-
calib->par_h2 = (tmp_msb << BME680_HUM_REG_SHIFT_VAL) |
- (tmp_lsb >> BME680_HUM_REG_SHIFT_VAL);
+ (tmp_lsb >> BME680_HUM_REG_SHIFT_VAL);
ret = regmap_read(data->regmap, BME680_H3_REG, &tmp);
if (ret < 0) {
@@ -276,8 +276,8 @@
}
calib->par_gh1 = tmp;
- ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG,
- (u8 *) &buf, 2);
+ ret = regmap_bulk_read(data->regmap, BME680_GH2_LSB_REG, (u8 *) &buf,
+ 2);
if (ret < 0) {
dev_err(dev, "failed to read BME680_GH2_LSB_REG\n");
return ret;
@@ -297,7 +297,7 @@
dev_err(dev, "failed to read resistance heat range\n");
return ret;
}
- calib->res_heat_range = (tmp & BME680_RHRANGE_MSK) / 16;
+ calib->res_heat_range = FIELD_GET(BME680_RHRANGE_MASK, tmp);
ret = regmap_read(data->regmap, BME680_REG_RES_HEAT_VAL, &tmp);
if (ret < 0) {
@@ -311,7 +311,7 @@
dev_err(dev, "failed to read range software error\n");
return ret;
}
- calib->range_sw_err = (tmp & BME680_RSERROR_MSK) / 16;
+ calib->range_sw_err = FIELD_GET(BME680_RSERROR_MASK, tmp);
return 0;
}
@@ -330,6 +330,10 @@
s64 var1, var2, var3;
s16 calc_temp;
+ /* If the calibration is invalid, attempt to reload it */
+ if (!calib->par_t2)
+ bme680_read_calib(data, calib);
+
var1 = (adc_temp >> 3) - (calib->par_t1 << 1);
var2 = (var1 * calib->par_t2) >> 11;
var3 = ((var1 >> 1) * (var1 >> 1)) >> 12;
@@ -408,10 +412,7 @@
var6 = (var4 * var5) >> 1;
calc_hum = (((var3 + var6) >> 10) * 1000) >> 12;
- if (calc_hum > 100000) /* Cap at 100%rH */
- calc_hum = 100000;
- else if (calc_hum < 0)
- calc_hum = 0;
+ calc_hum = clamp(calc_hum, 0, 100000); /* clamp between 0-100 %rH */
return calc_hum;
}
@@ -518,12 +519,20 @@
return ret;
}
+static u8 bme680_oversampling_to_reg(u8 val)
+{
+ return ilog2(val) + 1;
+}
+
static int bme680_chip_config(struct bme680_data *data)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
- u8 osrs = FIELD_PREP(BME680_OSRS_HUMIDITY_MASK,
- data->oversampling_humid + 1);
+ u8 osrs;
+
+ osrs = FIELD_PREP(
+ BME680_OSRS_HUMIDITY_MASK,
+ bme680_oversampling_to_reg(data->oversampling_humid));
/*
* Highly recommended to set oversampling of humidity before
* temperature/pressure oversampling.
@@ -544,12 +553,12 @@
return ret;
}
- osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK, data->oversampling_temp + 1) |
- FIELD_PREP(BME680_OSRS_PRESS_MASK, data->oversampling_press + 1);
-
+ osrs = FIELD_PREP(BME680_OSRS_TEMP_MASK,
+ bme680_oversampling_to_reg(data->oversampling_temp)) |
+ FIELD_PREP(BME680_OSRS_PRESS_MASK,
+ bme680_oversampling_to_reg(data->oversampling_press));
ret = regmap_write_bits(data->regmap, BME680_REG_CTRL_MEAS,
- BME680_OSRS_TEMP_MASK |
- BME680_OSRS_PRESS_MASK,
+ BME680_OSRS_TEMP_MASK | BME680_OSRS_PRESS_MASK,
osrs);
if (ret < 0)
dev_err(dev, "failed to write ctrl_meas register\n");
@@ -577,22 +586,22 @@
/* set target heating duration */
ret = regmap_write(data->regmap, BME680_REG_GAS_WAIT_0, heatr_dur);
if (ret < 0) {
- dev_err(dev, "failted to write gas_wait_0 register\n");
+ dev_err(dev, "failed to write gas_wait_0 register\n");
return ret;
}
- /* Selecting the runGas and NB conversion settings for the sensor */
+ /* Enable the gas sensor and select heater profile set-point 0 */
ret = regmap_update_bits(data->regmap, BME680_REG_CTRL_GAS_1,
BME680_RUN_GAS_MASK | BME680_NB_CONV_MASK,
- BME680_RUN_GAS_EN_BIT | BME680_NB_CONV_0_VAL);
+ FIELD_PREP(BME680_RUN_GAS_MASK, 1) |
+ FIELD_PREP(BME680_NB_CONV_MASK, 0));
if (ret < 0)
dev_err(dev, "failed to write ctrl_gas_1 register\n");
return ret;
}
-static int bme680_read_temp(struct bme680_data *data,
- int *val, int *val2)
+static int bme680_read_temp(struct bme680_data *data, int *val)
{
struct device *dev = regmap_get_device(data->regmap);
int ret;
@@ -625,10 +634,9 @@
* compensate_press/compensate_humid to get compensated
* pressure/humidity readings.
*/
- if (val && val2) {
- *val = comp_temp;
- *val2 = 100;
- return IIO_VAL_FRACTIONAL;
+ if (val) {
+ *val = comp_temp * 10; /* Centidegrees to millidegrees */
+ return IIO_VAL_INT;
}
return ret;
@@ -643,7 +651,7 @@
s32 adc_press;
/* Read and compensate temperature to get a reading of t_fine */
- ret = bme680_read_temp(data, NULL, NULL);
+ ret = bme680_read_temp(data, NULL);
if (ret < 0)
return ret;
@@ -676,7 +684,7 @@
u32 comp_humidity;
/* Read and compensate temperature to get a reading of t_fine */
- ret = bme680_read_temp(data, NULL, NULL);
+ ret = bme680_read_temp(data, NULL);
if (ret < 0)
return ret;
@@ -769,7 +777,7 @@
case IIO_CHAN_INFO_PROCESSED:
switch (chan->type) {
case IIO_TEMP:
- return bme680_read_temp(data, val, val2);
+ return bme680_read_temp(data, val);
case IIO_PRESSURE:
return bme680_read_press(data, val, val2);
case IIO_HUMIDITYRELATIVE:
@@ -782,13 +790,13 @@
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
switch (chan->type) {
case IIO_TEMP:
- *val = 1 << data->oversampling_temp;
+ *val = data->oversampling_temp;
return IIO_VAL_INT;
case IIO_PRESSURE:
- *val = 1 << data->oversampling_press;
+ *val = data->oversampling_press;
return IIO_VAL_INT;
case IIO_HUMIDITYRELATIVE:
- *val = 1 << data->oversampling_humid;
+ *val = data->oversampling_humid;
return IIO_VAL_INT;
default:
return -EINVAL;
@@ -798,52 +806,9 @@
}
}
-static int bme680_write_oversampling_ratio_temp(struct bme680_data *data,
- int val)
+static bool bme680_is_valid_oversampling(int rate)
{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
- if (bme680_oversampling_avail[i] == val) {
- data->oversampling_temp = ilog2(val);
-
- return bme680_chip_config(data);
- }
- }
-
- return -EINVAL;
-}
-
-static int bme680_write_oversampling_ratio_press(struct bme680_data *data,
- int val)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
- if (bme680_oversampling_avail[i] == val) {
- data->oversampling_press = ilog2(val);
-
- return bme680_chip_config(data);
- }
- }
-
- return -EINVAL;
-}
-
-static int bme680_write_oversampling_ratio_humid(struct bme680_data *data,
- int val)
-{
- int i;
-
- for (i = 0; i < ARRAY_SIZE(bme680_oversampling_avail); i++) {
- if (bme680_oversampling_avail[i] == val) {
- data->oversampling_humid = ilog2(val);
-
- return bme680_chip_config(data);
- }
- }
-
- return -EINVAL;
+ return (rate > 0 && rate <= 16 && is_power_of_2(rate));
}
static int bme680_write_raw(struct iio_dev *indio_dev,
@@ -852,18 +817,31 @@
{
struct bme680_data *data = iio_priv(indio_dev);
+ if (val2 != 0)
+ return -EINVAL;
+
switch (mask) {
case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ {
+ if (!bme680_is_valid_oversampling(val))
+ return -EINVAL;
+
switch (chan->type) {
case IIO_TEMP:
- return bme680_write_oversampling_ratio_temp(data, val);
+ data->oversampling_temp = val;
+ break;
case IIO_PRESSURE:
- return bme680_write_oversampling_ratio_press(data, val);
+ data->oversampling_press = val;
+ break;
case IIO_HUMIDITYRELATIVE:
- return bme680_write_oversampling_ratio_humid(data, val);
+ data->oversampling_humid = val;
+ break;
default:
return -EINVAL;
}
+
+ return bme680_chip_config(data);
+ }
default:
return -EINVAL;
}
@@ -905,8 +883,28 @@
{
struct iio_dev *indio_dev;
struct bme680_data *data;
+ unsigned int val;
int ret;
+ ret = regmap_write(regmap, BME680_REG_SOFT_RESET,
+ BME680_CMD_SOFTRESET);
+ if (ret < 0) {
+ dev_err(dev, "Failed to reset chip\n");
+ return ret;
+ }
+
+ ret = regmap_read(regmap, BME680_REG_CHIP_ID, &val);
+ if (ret < 0) {
+ dev_err(dev, "Error reading chip ID\n");
+ return ret;
+ }
+
+ if (val != BME680_CHIP_ID_VAL) {
+ dev_err(dev, "Wrong chip ID, got %x expected %x\n",
+ val, BME680_CHIP_ID_VAL);
+ return -ENODEV;
+ }
+
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
@@ -925,9 +923,9 @@
indio_dev->modes = INDIO_DIRECT_MODE;
/* default values for the sensor */
- data->oversampling_humid = ilog2(2); /* 2X oversampling rate */
- data->oversampling_press = ilog2(4); /* 4X oversampling rate */
- data->oversampling_temp = ilog2(8); /* 8X oversampling rate */
+ data->oversampling_humid = 2; /* 2X oversampling rate */
+ data->oversampling_press = 4; /* 4X oversampling rate */
+ data->oversampling_temp = 8; /* 8X oversampling rate */
data->heater_temp = 320; /* degree Celsius */
data->heater_dur = 150; /* milliseconds */
diff --git a/drivers/iio/chemical/bme680_i2c.c b/drivers/iio/chemical/bme680_i2c.c
index 06d4be5..de9c9e3 100644
--- a/drivers/iio/chemical/bme680_i2c.c
+++ b/drivers/iio/chemical/bme680_i2c.c
@@ -23,8 +23,6 @@
{
struct regmap *regmap;
const char *name = NULL;
- unsigned int val;
- int ret;
regmap = devm_regmap_init_i2c(client, &bme680_regmap_config);
if (IS_ERR(regmap)) {
@@ -33,25 +31,6 @@
return PTR_ERR(regmap);
}
- ret = regmap_write(regmap, BME680_REG_SOFT_RESET_I2C,
- BME680_CMD_SOFTRESET);
- if (ret < 0) {
- dev_err(&client->dev, "Failed to reset chip\n");
- return ret;
- }
-
- ret = regmap_read(regmap, BME680_REG_CHIP_I2C_ID, &val);
- if (ret < 0) {
- dev_err(&client->dev, "Error reading I2C chip ID\n");
- return ret;
- }
-
- if (val != BME680_CHIP_ID_VAL) {
- dev_err(&client->dev, "Wrong chip ID, got %x expected %x\n",
- val, BME680_CHIP_ID_VAL);
- return -ENODEV;
- }
-
if (id)
name = id->name;
@@ -70,10 +49,17 @@
};
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
+static const struct of_device_id bme680_of_i2c_match[] = {
+ { .compatible = "bosch,bme680", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bme680_of_i2c_match);
+
static struct i2c_driver bme680_i2c_driver = {
.driver = {
.name = "bme680_i2c",
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
+ .of_match_table = bme680_of_i2c_match,
},
.probe = bme680_i2c_probe,
.id_table = bme680_i2c_id,
diff --git a/drivers/iio/chemical/bme680_spi.c b/drivers/iio/chemical/bme680_spi.c
index c9fb05e..3b83806 100644
--- a/drivers/iio/chemical/bme680_spi.c
+++ b/drivers/iio/chemical/bme680_spi.c
@@ -6,33 +6,99 @@
*/
#include <linux/acpi.h>
#include <linux/module.h>
+#include <linux/of.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include "bme680.h"
+struct bme680_spi_bus_context {
+ struct spi_device *spi;
+ u8 current_page;
+};
+
+/*
+ * In SPI mode there are only 7 address bits, a "page" register determines
+ * which part of the 8-bit range is active. This function looks at the address
+ * and writes the page selection bit if needed
+ */
+static int bme680_regmap_spi_select_page(
+ struct bme680_spi_bus_context *ctx, u8 reg)
+{
+ struct spi_device *spi = ctx->spi;
+ int ret;
+ u8 buf[2];
+ u8 page = (reg & 0x80) ? 0 : 1; /* Page "1" is low range */
+
+ if (page == ctx->current_page)
+ return 0;
+
+ /*
+ * Data sheet claims we're only allowed to change bit 4, so we must do
+ * a read-modify-write on each and every page select
+ */
+ buf[0] = BME680_REG_STATUS;
+ ret = spi_write_then_read(spi, buf, 1, buf + 1, 1);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to set page %u\n", page);
+ return ret;
+ }
+
+ buf[0] = BME680_REG_STATUS;
+ if (page)
+ buf[1] |= BME680_SPI_MEM_PAGE_BIT;
+ else
+ buf[1] &= ~BME680_SPI_MEM_PAGE_BIT;
+
+ ret = spi_write(spi, buf, 2);
+ if (ret < 0) {
+ dev_err(&spi->dev, "failed to set page %u\n", page);
+ return ret;
+ }
+
+ ctx->current_page = page;
+
+ return 0;
+}
+
static int bme680_regmap_spi_write(void *context, const void *data,
size_t count)
{
- struct spi_device *spi = context;
+ struct bme680_spi_bus_context *ctx = context;
+ struct spi_device *spi = ctx->spi;
+ int ret;
u8 buf[2];
memcpy(buf, data, 2);
+
+ ret = bme680_regmap_spi_select_page(ctx, buf[0]);
+ if (ret)
+ return ret;
+
/*
* The SPI register address (= full register address without bit 7)
* and the write command (bit7 = RW = '0')
*/
buf[0] &= ~0x80;
- return spi_write_then_read(spi, buf, 2, NULL, 0);
+ return spi_write(spi, buf, 2);
}
static int bme680_regmap_spi_read(void *context, const void *reg,
size_t reg_size, void *val, size_t val_size)
{
- struct spi_device *spi = context;
+ struct bme680_spi_bus_context *ctx = context;
+ struct spi_device *spi = ctx->spi;
+ int ret;
+ u8 addr = *(const u8 *)reg;
- return spi_write_then_read(spi, reg, reg_size, val, val_size);
+ ret = bme680_regmap_spi_select_page(ctx, addr);
+ if (ret)
+ return ret;
+
+ addr |= 0x80; /* bit7 = RW = '1' */
+
+ return spi_write_then_read(spi, &addr, 1, val, val_size);
}
static struct regmap_bus bme680_regmap_bus = {
@@ -45,8 +111,8 @@
static int bme680_spi_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
+ struct bme680_spi_bus_context *bus_context;
struct regmap *regmap;
- unsigned int val;
int ret;
spi->bits_per_word = 8;
@@ -56,45 +122,21 @@
return ret;
}
+ bus_context = devm_kzalloc(&spi->dev, sizeof(*bus_context), GFP_KERNEL);
+ if (!bus_context)
+ return -ENOMEM;
+
+ bus_context->spi = spi;
+ bus_context->current_page = 0xff; /* Undefined on warm boot */
+
regmap = devm_regmap_init(&spi->dev, &bme680_regmap_bus,
- &spi->dev, &bme680_regmap_config);
+ bus_context, &bme680_regmap_config);
if (IS_ERR(regmap)) {
dev_err(&spi->dev, "Failed to register spi regmap %d\n",
(int)PTR_ERR(regmap));
return PTR_ERR(regmap);
}
- ret = regmap_write(regmap, BME680_REG_SOFT_RESET_SPI,
- BME680_CMD_SOFTRESET);
- if (ret < 0) {
- dev_err(&spi->dev, "Failed to reset chip\n");
- return ret;
- }
-
- /* after power-on reset, Page 0(0x80-0xFF) of spi_mem_page is active */
- ret = regmap_read(regmap, BME680_REG_CHIP_SPI_ID, &val);
- if (ret < 0) {
- dev_err(&spi->dev, "Error reading SPI chip ID\n");
- return ret;
- }
-
- if (val != BME680_CHIP_ID_VAL) {
- dev_err(&spi->dev, "Wrong chip ID, got %x expected %x\n",
- val, BME680_CHIP_ID_VAL);
- return -ENODEV;
- }
- /*
- * select Page 1 of spi_mem_page to enable access to
- * to registers from address 0x00 to 0x7F.
- */
- ret = regmap_write_bits(regmap, BME680_REG_STATUS,
- BME680_SPI_MEM_PAGE_BIT,
- BME680_SPI_MEM_PAGE_1_VAL);
- if (ret < 0) {
- dev_err(&spi->dev, "failed to set page 1 of spi_mem_page\n");
- return ret;
- }
-
return bme680_core_probe(&spi->dev, regmap, id->name);
}
@@ -110,10 +152,17 @@
};
MODULE_DEVICE_TABLE(acpi, bme680_acpi_match);
+static const struct of_device_id bme680_of_spi_match[] = {
+ { .compatible = "bosch,bme680", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, bme680_of_spi_match);
+
static struct spi_driver bme680_spi_driver = {
.driver = {
.name = "bme680_spi",
.acpi_match_table = ACPI_PTR(bme680_acpi_match),
+ .of_match_table = bme680_of_spi_match,
},
.probe = bme680_spi_probe,
.id_table = bme680_spi_id,
diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c
index b4a46eb..2ebdfc3 100644
--- a/drivers/iio/chemical/ccs811.c
+++ b/drivers/iio/chemical/ccs811.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ccs811.c - Support for AMS CCS811 VOC Sensor
*
@@ -5,10 +6,6 @@
*
* Datasheet: ams.com/content/download/951091/2269479/CCS811_DS000459_3-00.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.
- *
* IIO driver for AMS CCS811 (I2C address 0x5A/0x5B set by ADDR Low/High)
*
* TODO:
diff --git a/drivers/iio/chemical/pms7003.c b/drivers/iio/chemical/pms7003.c
new file mode 100644
index 0000000..23c9ab2
--- /dev/null
+++ b/drivers/iio/chemical/pms7003.c
@@ -0,0 +1,345 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Plantower PMS7003 particulate matter sensor driver
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
+ */
+
+#include <asm/unaligned.h>
+#include <linux/completion.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/serdev.h>
+
+#define PMS7003_DRIVER_NAME "pms7003"
+
+#define PMS7003_MAGIC 0x424d
+/* last 2 data bytes hold frame checksum */
+#define PMS7003_MAX_DATA_LENGTH 28
+#define PMS7003_CHECKSUM_LENGTH 2
+#define PMS7003_PM10_OFFSET 10
+#define PMS7003_PM2P5_OFFSET 8
+#define PMS7003_PM1_OFFSET 6
+
+#define PMS7003_TIMEOUT msecs_to_jiffies(6000)
+#define PMS7003_CMD_LENGTH 7
+#define PMS7003_PM_MAX 1000
+#define PMS7003_PM_MIN 0
+
+enum {
+ PM1,
+ PM2P5,
+ PM10,
+};
+
+enum pms7003_cmd {
+ CMD_WAKEUP,
+ CMD_ENTER_PASSIVE_MODE,
+ CMD_READ_PASSIVE,
+ CMD_SLEEP,
+};
+
+/*
+ * commands have following format:
+ *
+ * +------+------+-----+------+-----+-----------+-----------+
+ * | 0x42 | 0x4d | cmd | 0x00 | arg | cksum msb | cksum lsb |
+ * +------+------+-----+------+-----+-----------+-----------+
+ */
+static const u8 pms7003_cmd_tbl[][PMS7003_CMD_LENGTH] = {
+ [CMD_WAKEUP] = { 0x42, 0x4d, 0xe4, 0x00, 0x01, 0x01, 0x74 },
+ [CMD_ENTER_PASSIVE_MODE] = { 0x42, 0x4d, 0xe1, 0x00, 0x00, 0x01, 0x70 },
+ [CMD_READ_PASSIVE] = { 0x42, 0x4d, 0xe2, 0x00, 0x00, 0x01, 0x71 },
+ [CMD_SLEEP] = { 0x42, 0x4d, 0xe4, 0x00, 0x00, 0x01, 0x73 },
+};
+
+struct pms7003_frame {
+ u8 data[PMS7003_MAX_DATA_LENGTH];
+ u16 expected_length;
+ u16 length;
+};
+
+struct pms7003_state {
+ struct serdev_device *serdev;
+ struct pms7003_frame frame;
+ struct completion frame_ready;
+ struct mutex lock; /* must be held whenever state gets touched */
+};
+
+static int pms7003_do_cmd(struct pms7003_state *state, enum pms7003_cmd cmd)
+{
+ int ret;
+
+ ret = serdev_device_write(state->serdev, pms7003_cmd_tbl[cmd],
+ PMS7003_CMD_LENGTH, PMS7003_TIMEOUT);
+ if (ret < PMS7003_CMD_LENGTH)
+ return ret < 0 ? ret : -EIO;
+
+ ret = wait_for_completion_interruptible_timeout(&state->frame_ready,
+ PMS7003_TIMEOUT);
+ if (!ret)
+ ret = -ETIMEDOUT;
+
+ return ret < 0 ? ret : 0;
+}
+
+static u16 pms7003_get_pm(const u8 *data)
+{
+ return clamp_val(get_unaligned_be16(data),
+ PMS7003_PM_MIN, PMS7003_PM_MAX);
+}
+
+static irqreturn_t pms7003_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct pms7003_state *state = iio_priv(indio_dev);
+ struct pms7003_frame *frame = &state->frame;
+ u16 data[3 + 1 + 4]; /* PM1, PM2P5, PM10, padding, timestamp */
+ int ret;
+
+ mutex_lock(&state->lock);
+ ret = pms7003_do_cmd(state, CMD_READ_PASSIVE);
+ if (ret) {
+ mutex_unlock(&state->lock);
+ goto err;
+ }
+
+ data[PM1] = pms7003_get_pm(frame->data + PMS7003_PM1_OFFSET);
+ data[PM2P5] = pms7003_get_pm(frame->data + PMS7003_PM2P5_OFFSET);
+ data[PM10] = pms7003_get_pm(frame->data + PMS7003_PM10_OFFSET);
+ mutex_unlock(&state->lock);
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data,
+ iio_get_time_ns(indio_dev));
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int pms7003_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct pms7003_state *state = iio_priv(indio_dev);
+ struct pms7003_frame *frame = &state->frame;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_MASSCONCENTRATION:
+ mutex_lock(&state->lock);
+ ret = pms7003_do_cmd(state, CMD_READ_PASSIVE);
+ if (ret) {
+ mutex_unlock(&state->lock);
+ return ret;
+ }
+
+ *val = pms7003_get_pm(frame->data + chan->address);
+ mutex_unlock(&state->lock);
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info pms7003_info = {
+ .read_raw = pms7003_read_raw,
+};
+
+#define PMS7003_CHAN(_index, _mod, _addr) { \
+ .type = IIO_MASSCONCENTRATION, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## _mod, \
+ .address = _addr, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 10, \
+ .storagebits = 16, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+static const struct iio_chan_spec pms7003_channels[] = {
+ PMS7003_CHAN(0, PM1, PMS7003_PM1_OFFSET),
+ PMS7003_CHAN(1, PM2P5, PMS7003_PM2P5_OFFSET),
+ PMS7003_CHAN(2, PM10, PMS7003_PM10_OFFSET),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static u16 pms7003_calc_checksum(struct pms7003_frame *frame)
+{
+ u16 checksum = (PMS7003_MAGIC >> 8) + (u8)(PMS7003_MAGIC & 0xff) +
+ (frame->length >> 8) + (u8)frame->length;
+ int i;
+
+ for (i = 0; i < frame->length - PMS7003_CHECKSUM_LENGTH; i++)
+ checksum += frame->data[i];
+
+ return checksum;
+}
+
+static bool pms7003_frame_is_okay(struct pms7003_frame *frame)
+{
+ int offset = frame->length - PMS7003_CHECKSUM_LENGTH;
+ u16 checksum = get_unaligned_be16(frame->data + offset);
+
+ return checksum == pms7003_calc_checksum(frame);
+}
+
+static int pms7003_receive_buf(struct serdev_device *serdev,
+ const unsigned char *buf, size_t size)
+{
+ struct iio_dev *indio_dev = serdev_device_get_drvdata(serdev);
+ struct pms7003_state *state = iio_priv(indio_dev);
+ struct pms7003_frame *frame = &state->frame;
+ int num;
+
+ if (!frame->expected_length) {
+ u16 magic;
+
+ /* wait for SOF and data length */
+ if (size < 4)
+ return 0;
+
+ magic = get_unaligned_be16(buf);
+ if (magic != PMS7003_MAGIC)
+ return 2;
+
+ num = get_unaligned_be16(buf + 2);
+ if (num <= PMS7003_MAX_DATA_LENGTH) {
+ frame->expected_length = num;
+ frame->length = 0;
+ }
+
+ return 4;
+ }
+
+ num = min(size, (size_t)(frame->expected_length - frame->length));
+ memcpy(frame->data + frame->length, buf, num);
+ frame->length += num;
+
+ if (frame->length == frame->expected_length) {
+ if (pms7003_frame_is_okay(frame))
+ complete(&state->frame_ready);
+
+ frame->expected_length = 0;
+ }
+
+ return num;
+}
+
+static const struct serdev_device_ops pms7003_serdev_ops = {
+ .receive_buf = pms7003_receive_buf,
+ .write_wakeup = serdev_device_write_wakeup,
+};
+
+static void pms7003_stop(void *data)
+{
+ struct pms7003_state *state = data;
+
+ pms7003_do_cmd(state, CMD_SLEEP);
+}
+
+static const unsigned long pms7003_scan_masks[] = { 0x07, 0x00 };
+
+static int pms7003_probe(struct serdev_device *serdev)
+{
+ struct pms7003_state *state;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&serdev->dev, sizeof(*state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ state = iio_priv(indio_dev);
+ serdev_device_set_drvdata(serdev, indio_dev);
+ state->serdev = serdev;
+ indio_dev->dev.parent = &serdev->dev;
+ indio_dev->info = &pms7003_info;
+ indio_dev->name = PMS7003_DRIVER_NAME;
+ indio_dev->channels = pms7003_channels,
+ indio_dev->num_channels = ARRAY_SIZE(pms7003_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->available_scan_masks = pms7003_scan_masks;
+
+ mutex_init(&state->lock);
+ init_completion(&state->frame_ready);
+
+ serdev_device_set_client_ops(serdev, &pms7003_serdev_ops);
+ ret = devm_serdev_device_open(&serdev->dev, serdev);
+ if (ret)
+ return ret;
+
+ serdev_device_set_baudrate(serdev, 9600);
+ serdev_device_set_flow_control(serdev, false);
+
+ ret = serdev_device_set_parity(serdev, SERDEV_PARITY_NONE);
+ if (ret)
+ return ret;
+
+ ret = pms7003_do_cmd(state, CMD_WAKEUP);
+ if (ret) {
+ dev_err(&serdev->dev, "failed to wakeup sensor\n");
+ return ret;
+ }
+
+ ret = pms7003_do_cmd(state, CMD_ENTER_PASSIVE_MODE);
+ if (ret) {
+ dev_err(&serdev->dev, "failed to enter passive mode\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&serdev->dev, pms7003_stop, state);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&serdev->dev, indio_dev, NULL,
+ pms7003_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&serdev->dev, indio_dev);
+}
+
+static const struct of_device_id pms7003_of_match[] = {
+ { .compatible = "plantower,pms1003" },
+ { .compatible = "plantower,pms3003" },
+ { .compatible = "plantower,pms5003" },
+ { .compatible = "plantower,pms6003" },
+ { .compatible = "plantower,pms7003" },
+ { .compatible = "plantower,pmsa003" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pms7003_of_match);
+
+static struct serdev_device_driver pms7003_driver = {
+ .driver = {
+ .name = PMS7003_DRIVER_NAME,
+ .of_match_table = pms7003_of_match,
+ },
+ .probe = pms7003_probe,
+};
+module_serdev_device_driver(pms7003_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
+MODULE_DESCRIPTION("Plantower PMS7003 particulate matter sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/chemical/sgp30.c b/drivers/iio/chemical/sgp30.c
new file mode 100644
index 0000000..8cc8fe5
--- /dev/null
+++ b/drivers/iio/chemical/sgp30.c
@@ -0,0 +1,591 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * sgp30.c - Support for Sensirion SGP Gas Sensors
+ *
+ * Copyright (C) 2018 Andreas Brauchli <andreas.brauchli@sensirion.com>
+ *
+ * I2C slave address: 0x58
+ *
+ * Datasheets:
+ * https://www.sensirion.com/file/datasheet_sgp30
+ * https://www.sensirion.com/file/datasheet_sgpc3
+ *
+ * TODO:
+ * - baseline support
+ * - humidity compensation
+ * - power mode switching (SGPC3)
+ */
+
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define SGP_WORD_LEN 2
+#define SGP_CRC8_POLYNOMIAL 0x31
+#define SGP_CRC8_INIT 0xff
+#define SGP_CRC8_LEN 1
+#define SGP_CMD(cmd_word) cpu_to_be16(cmd_word)
+#define SGP_CMD_DURATION_US 12000
+#define SGP_MEASUREMENT_DURATION_US 50000
+#define SGP_CMD_LEN SGP_WORD_LEN
+#define SGP_CMD_MAX_BUF_SIZE (SGP_CMD_LEN + 2 * SGP_WORD_LEN)
+#define SGP_MEASUREMENT_LEN 2
+#define SGP30_MEASURE_INTERVAL_HZ 1
+#define SGPC3_MEASURE_INTERVAL_HZ 2
+#define SGP_VERS_PRODUCT(data) ((((data)->feature_set) & 0xf000) >> 12)
+#define SGP_VERS_RESERVED(data) ((((data)->feature_set) & 0x0800) >> 11)
+#define SGP_VERS_GEN(data) ((((data)->feature_set) & 0x0600) >> 9)
+#define SGP_VERS_ENG_BIT(data) ((((data)->feature_set) & 0x0100) >> 8)
+#define SGP_VERS_MAJOR(data) ((((data)->feature_set) & 0x00e0) >> 5)
+#define SGP_VERS_MINOR(data) (((data)->feature_set) & 0x001f)
+
+DECLARE_CRC8_TABLE(sgp_crc8_table);
+
+enum sgp_product_id {
+ SGP30 = 0,
+ SGPC3,
+};
+
+enum sgp30_channel_idx {
+ SGP30_IAQ_TVOC_IDX = 0,
+ SGP30_IAQ_CO2EQ_IDX,
+ SGP30_SIG_ETOH_IDX,
+ SGP30_SIG_H2_IDX,
+};
+
+enum sgpc3_channel_idx {
+ SGPC3_IAQ_TVOC_IDX = 10,
+ SGPC3_SIG_ETOH_IDX,
+};
+
+enum sgp_cmd {
+ SGP_CMD_IAQ_INIT = SGP_CMD(0x2003),
+ SGP_CMD_IAQ_MEASURE = SGP_CMD(0x2008),
+ SGP_CMD_GET_FEATURE_SET = SGP_CMD(0x202f),
+ SGP_CMD_GET_SERIAL_ID = SGP_CMD(0x3682),
+
+ SGP30_CMD_MEASURE_SIGNAL = SGP_CMD(0x2050),
+
+ SGPC3_CMD_MEASURE_RAW = SGP_CMD(0x2046),
+};
+
+struct sgp_version {
+ u8 major;
+ u8 minor;
+};
+
+struct sgp_crc_word {
+ __be16 value;
+ u8 crc8;
+} __attribute__((__packed__));
+
+union sgp_reading {
+ u8 start;
+ struct sgp_crc_word raw_words[4];
+};
+
+enum _iaq_buffer_state {
+ IAQ_BUFFER_EMPTY = 0,
+ IAQ_BUFFER_DEFAULT_VALS,
+ IAQ_BUFFER_VALID,
+};
+
+struct sgp_data {
+ struct i2c_client *client;
+ struct task_struct *iaq_thread;
+ struct mutex data_lock;
+ unsigned long iaq_init_start_jiffies;
+ unsigned long iaq_defval_skip_jiffies;
+ u16 product_id;
+ u16 feature_set;
+ unsigned long measure_interval_jiffies;
+ enum sgp_cmd iaq_init_cmd;
+ enum sgp_cmd measure_iaq_cmd;
+ enum sgp_cmd measure_gas_signals_cmd;
+ union sgp_reading buffer;
+ union sgp_reading iaq_buffer;
+ enum _iaq_buffer_state iaq_buffer_state;
+};
+
+struct sgp_device {
+ const struct iio_chan_spec *channels;
+ int num_channels;
+};
+
+static const struct sgp_version supported_versions_sgp30[] = {
+ {
+ .major = 1,
+ .minor = 0,
+ },
+};
+
+static const struct sgp_version supported_versions_sgpc3[] = {
+ {
+ .major = 0,
+ .minor = 4,
+ },
+};
+
+static const struct iio_chan_spec sgp30_channels[] = {
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_VOC,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = SGP30_IAQ_TVOC_IDX,
+ },
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_CO2,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = SGP30_IAQ_CO2EQ_IDX,
+ },
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_ETHANOL,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = SGP30_SIG_ETOH_IDX,
+ },
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_H2,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = SGP30_SIG_H2_IDX,
+ },
+};
+
+static const struct iio_chan_spec sgpc3_channels[] = {
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_VOC,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .address = SGPC3_IAQ_TVOC_IDX,
+ },
+ {
+ .type = IIO_CONCENTRATION,
+ .channel2 = IIO_MOD_ETHANOL,
+ .modified = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .address = SGPC3_SIG_ETOH_IDX,
+ },
+};
+
+static const struct sgp_device sgp_devices[] = {
+ [SGP30] = {
+ .channels = sgp30_channels,
+ .num_channels = ARRAY_SIZE(sgp30_channels),
+ },
+ [SGPC3] = {
+ .channels = sgpc3_channels,
+ .num_channels = ARRAY_SIZE(sgpc3_channels),
+ },
+};
+
+/**
+ * sgp_verify_buffer() - verify the checksums of the data buffer words
+ *
+ * @data: SGP data
+ * @buf: Raw data buffer
+ * @word_count: Num data words stored in the buffer, excluding CRC bytes
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+static int sgp_verify_buffer(const struct sgp_data *data,
+ union sgp_reading *buf, size_t word_count)
+{
+ size_t size = word_count * (SGP_WORD_LEN + SGP_CRC8_LEN);
+ int i;
+ u8 crc;
+ u8 *data_buf = &buf->start;
+
+ for (i = 0; i < size; i += SGP_WORD_LEN + SGP_CRC8_LEN) {
+ crc = crc8(sgp_crc8_table, &data_buf[i], SGP_WORD_LEN,
+ SGP_CRC8_INIT);
+ if (crc != data_buf[i + SGP_WORD_LEN]) {
+ dev_err(&data->client->dev, "CRC error\n");
+ return -EIO;
+ }
+ }
+
+ return 0;
+}
+
+/**
+ * sgp_read_cmd() - reads data from sensor after issuing a command
+ * The caller must hold data->data_lock for the duration of the call.
+ * @data: SGP data
+ * @cmd: SGP Command to issue
+ * @buf: Raw data buffer to use
+ * @word_count: Num words to read, excluding CRC bytes
+ *
+ * Return: 0 on success, negative error otherwise.
+ */
+static int sgp_read_cmd(struct sgp_data *data, enum sgp_cmd cmd,
+ union sgp_reading *buf, size_t word_count,
+ unsigned long duration_us)
+{
+ int ret;
+ struct i2c_client *client = data->client;
+ size_t size = word_count * (SGP_WORD_LEN + SGP_CRC8_LEN);
+ u8 *data_buf;
+
+ ret = i2c_master_send(client, (const char *)&cmd, SGP_CMD_LEN);
+ if (ret != SGP_CMD_LEN)
+ return -EIO;
+ usleep_range(duration_us, duration_us + 1000);
+
+ if (word_count == 0)
+ return 0;
+
+ data_buf = &buf->start;
+ ret = i2c_master_recv(client, data_buf, size);
+ if (ret < 0)
+ return ret;
+ if (ret != size)
+ return -EIO;
+
+ return sgp_verify_buffer(data, buf, word_count);
+}
+
+/**
+ * sgp_measure_iaq() - measure and retrieve IAQ values from sensor
+ * The caller must hold data->data_lock for the duration of the call.
+ * @data: SGP data
+ *
+ * Return: 0 on success, -EBUSY on default values, negative error
+ * otherwise.
+ */
+
+static int sgp_measure_iaq(struct sgp_data *data)
+{
+ int ret;
+ /* data contains default values */
+ bool default_vals = !time_after(jiffies, data->iaq_init_start_jiffies +
+ data->iaq_defval_skip_jiffies);
+
+ ret = sgp_read_cmd(data, data->measure_iaq_cmd, &data->iaq_buffer,
+ SGP_MEASUREMENT_LEN, SGP_MEASUREMENT_DURATION_US);
+ if (ret < 0)
+ return ret;
+
+ data->iaq_buffer_state = IAQ_BUFFER_DEFAULT_VALS;
+
+ if (default_vals)
+ return -EBUSY;
+
+ data->iaq_buffer_state = IAQ_BUFFER_VALID;
+
+ return 0;
+}
+
+static void sgp_iaq_thread_sleep_until(const struct sgp_data *data,
+ unsigned long sleep_jiffies)
+{
+ const long IAQ_POLL = 50000;
+
+ while (!time_after(jiffies, sleep_jiffies)) {
+ usleep_range(IAQ_POLL, IAQ_POLL + 10000);
+ if (kthread_should_stop() || data->iaq_init_start_jiffies == 0)
+ return;
+ }
+}
+
+static int sgp_iaq_threadfn(void *p)
+{
+ struct sgp_data *data = (struct sgp_data *)p;
+ unsigned long next_update_jiffies;
+ int ret;
+
+ while (!kthread_should_stop()) {
+ mutex_lock(&data->data_lock);
+ if (data->iaq_init_start_jiffies == 0) {
+ ret = sgp_read_cmd(data, data->iaq_init_cmd, NULL, 0,
+ SGP_CMD_DURATION_US);
+ if (ret < 0)
+ goto unlock_sleep_continue;
+ data->iaq_init_start_jiffies = jiffies;
+ }
+
+ ret = sgp_measure_iaq(data);
+ if (ret && ret != -EBUSY) {
+ dev_warn(&data->client->dev,
+ "IAQ measurement error [%d]\n", ret);
+ }
+unlock_sleep_continue:
+ next_update_jiffies = jiffies + data->measure_interval_jiffies;
+ mutex_unlock(&data->data_lock);
+ sgp_iaq_thread_sleep_until(data, next_update_jiffies);
+ }
+
+ return 0;
+}
+
+static int sgp_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct sgp_data *data = iio_priv(indio_dev);
+ struct sgp_crc_word *words;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ mutex_lock(&data->data_lock);
+ if (data->iaq_buffer_state != IAQ_BUFFER_VALID) {
+ mutex_unlock(&data->data_lock);
+ return -EBUSY;
+ }
+ words = data->iaq_buffer.raw_words;
+ switch (chan->address) {
+ case SGP30_IAQ_TVOC_IDX:
+ case SGPC3_IAQ_TVOC_IDX:
+ *val = 0;
+ *val2 = be16_to_cpu(words[1].value);
+ ret = IIO_VAL_INT_PLUS_NANO;
+ break;
+ case SGP30_IAQ_CO2EQ_IDX:
+ *val = 0;
+ *val2 = be16_to_cpu(words[0].value);
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&data->data_lock);
+ break;
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->data_lock);
+ if (chan->address == SGPC3_SIG_ETOH_IDX) {
+ if (data->iaq_buffer_state == IAQ_BUFFER_EMPTY)
+ ret = -EBUSY;
+ else
+ ret = 0;
+ words = data->iaq_buffer.raw_words;
+ } else {
+ ret = sgp_read_cmd(data, data->measure_gas_signals_cmd,
+ &data->buffer, SGP_MEASUREMENT_LEN,
+ SGP_MEASUREMENT_DURATION_US);
+ words = data->buffer.raw_words;
+ }
+ if (ret) {
+ mutex_unlock(&data->data_lock);
+ return ret;
+ }
+
+ switch (chan->address) {
+ case SGP30_SIG_ETOH_IDX:
+ *val = be16_to_cpu(words[1].value);
+ ret = IIO_VAL_INT;
+ break;
+ case SGPC3_SIG_ETOH_IDX:
+ case SGP30_SIG_H2_IDX:
+ *val = be16_to_cpu(words[0].value);
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&data->data_lock);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static int sgp_check_compat(struct sgp_data *data,
+ unsigned int product_id)
+{
+ const struct sgp_version *supported_versions;
+ u16 ix, num_fs;
+ u16 product, generation, major, minor;
+
+ /* driver does not match product */
+ generation = SGP_VERS_GEN(data);
+ if (generation != 0) {
+ dev_err(&data->client->dev,
+ "incompatible product generation %d != 0", generation);
+ return -ENODEV;
+ }
+
+ product = SGP_VERS_PRODUCT(data);
+ if (product != product_id) {
+ dev_err(&data->client->dev,
+ "sensor reports a different product: 0x%04hx\n",
+ product);
+ return -ENODEV;
+ }
+
+ if (SGP_VERS_RESERVED(data))
+ dev_warn(&data->client->dev, "reserved bit is set\n");
+
+ /* engineering samples are not supported: no interface guarantees */
+ if (SGP_VERS_ENG_BIT(data))
+ return -ENODEV;
+
+ switch (product) {
+ case SGP30:
+ supported_versions = supported_versions_sgp30;
+ num_fs = ARRAY_SIZE(supported_versions_sgp30);
+ break;
+ case SGPC3:
+ supported_versions = supported_versions_sgpc3;
+ num_fs = ARRAY_SIZE(supported_versions_sgpc3);
+ break;
+ default:
+ return -ENODEV;
+ }
+
+ major = SGP_VERS_MAJOR(data);
+ minor = SGP_VERS_MINOR(data);
+ for (ix = 0; ix < num_fs; ix++) {
+ if (major == supported_versions[ix].major &&
+ minor >= supported_versions[ix].minor)
+ return 0;
+ }
+ dev_err(&data->client->dev, "unsupported sgp version: %d.%d\n",
+ major, minor);
+
+ return -ENODEV;
+}
+
+static void sgp_init(struct sgp_data *data)
+{
+ data->iaq_init_cmd = SGP_CMD_IAQ_INIT;
+ data->iaq_init_start_jiffies = 0;
+ data->iaq_buffer_state = IAQ_BUFFER_EMPTY;
+ switch (SGP_VERS_PRODUCT(data)) {
+ case SGP30:
+ data->measure_interval_jiffies = SGP30_MEASURE_INTERVAL_HZ * HZ;
+ data->measure_iaq_cmd = SGP_CMD_IAQ_MEASURE;
+ data->measure_gas_signals_cmd = SGP30_CMD_MEASURE_SIGNAL;
+ data->product_id = SGP30;
+ data->iaq_defval_skip_jiffies = 15 * HZ;
+ break;
+ case SGPC3:
+ data->measure_interval_jiffies = SGPC3_MEASURE_INTERVAL_HZ * HZ;
+ data->measure_iaq_cmd = SGPC3_CMD_MEASURE_RAW;
+ data->measure_gas_signals_cmd = SGPC3_CMD_MEASURE_RAW;
+ data->product_id = SGPC3;
+ data->iaq_defval_skip_jiffies =
+ 43 * data->measure_interval_jiffies;
+ break;
+ };
+}
+
+static const struct iio_info sgp_info = {
+ .read_raw = sgp_read_raw,
+};
+
+static const struct of_device_id sgp_dt_ids[] = {
+ { .compatible = "sensirion,sgp30", .data = (void *)SGP30 },
+ { .compatible = "sensirion,sgpc3", .data = (void *)SGPC3 },
+ { }
+};
+
+static int sgp_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct sgp_data *data;
+ const struct of_device_id *of_id;
+ unsigned long product_id;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ of_id = of_match_device(sgp_dt_ids, &client->dev);
+ if (of_id)
+ product_id = (unsigned long)of_id->data;
+ else
+ product_id = id->driver_data;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ crc8_populate_msb(sgp_crc8_table, SGP_CRC8_POLYNOMIAL);
+ mutex_init(&data->data_lock);
+
+ /* get feature set version and write it to client data */
+ ret = sgp_read_cmd(data, SGP_CMD_GET_FEATURE_SET, &data->buffer, 1,
+ SGP_CMD_DURATION_US);
+ if (ret < 0)
+ return ret;
+
+ data->feature_set = be16_to_cpu(data->buffer.raw_words[0].value);
+
+ ret = sgp_check_compat(data, product_id);
+ if (ret)
+ return ret;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &sgp_info;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = sgp_devices[product_id].channels;
+ indio_dev->num_channels = sgp_devices[product_id].num_channels;
+
+ sgp_init(data);
+
+ ret = devm_iio_device_register(&client->dev, indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "failed to register iio device\n");
+ return ret;
+ }
+
+ data->iaq_thread = kthread_run(sgp_iaq_threadfn, data,
+ "%s-iaq", data->client->name);
+
+ return 0;
+}
+
+static int sgp_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct sgp_data *data = iio_priv(indio_dev);
+
+ if (data->iaq_thread)
+ kthread_stop(data->iaq_thread);
+
+ return 0;
+}
+
+static const struct i2c_device_id sgp_id[] = {
+ { "sgp30", SGP30 },
+ { "sgpc3", SGPC3 },
+ { }
+};
+
+MODULE_DEVICE_TABLE(i2c, sgp_id);
+MODULE_DEVICE_TABLE(of, sgp_dt_ids);
+
+static struct i2c_driver sgp_driver = {
+ .driver = {
+ .name = "sgp30",
+ .of_match_table = of_match_ptr(sgp_dt_ids),
+ },
+ .probe = sgp_probe,
+ .remove = sgp_remove,
+ .id_table = sgp_id,
+};
+module_i2c_driver(sgp_driver);
+
+MODULE_AUTHOR("Andreas Brauchli <andreas.brauchli@sensirion.com>");
+MODULE_AUTHOR("Pascal Sachs <pascal.sachs@sensirion.com>");
+MODULE_DESCRIPTION("Sensirion SGP gas sensors");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/chemical/sps30.c b/drivers/iio/chemical/sps30.c
new file mode 100644
index 0000000..edbb956
--- /dev/null
+++ b/drivers/iio/chemical/sps30.c
@@ -0,0 +1,548 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Sensirion SPS30 particulate matter sensor driver
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
+ *
+ * I2C slave address: 0x69
+ */
+
+#include <asm/unaligned.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+
+#define SPS30_CRC8_POLYNOMIAL 0x31
+/* max number of bytes needed to store PM measurements or serial string */
+#define SPS30_MAX_READ_SIZE 48
+/* sensor measures reliably up to 3000 ug / m3 */
+#define SPS30_MAX_PM 3000
+/* minimum and maximum self cleaning periods in seconds */
+#define SPS30_AUTO_CLEANING_PERIOD_MIN 0
+#define SPS30_AUTO_CLEANING_PERIOD_MAX 604800
+
+/* SPS30 commands */
+#define SPS30_START_MEAS 0x0010
+#define SPS30_STOP_MEAS 0x0104
+#define SPS30_RESET 0xd304
+#define SPS30_READ_DATA_READY_FLAG 0x0202
+#define SPS30_READ_DATA 0x0300
+#define SPS30_READ_SERIAL 0xd033
+#define SPS30_START_FAN_CLEANING 0x5607
+#define SPS30_AUTO_CLEANING_PERIOD 0x8004
+/* not a sensor command per se, used only to distinguish write from read */
+#define SPS30_READ_AUTO_CLEANING_PERIOD 0x8005
+
+enum {
+ PM1,
+ PM2P5,
+ PM4,
+ PM10,
+};
+
+enum {
+ RESET,
+ MEASURING,
+};
+
+struct sps30_state {
+ struct i2c_client *client;
+ /*
+ * Guards against concurrent access to sensor registers.
+ * Must be held whenever sequence of commands is to be executed.
+ */
+ struct mutex lock;
+ int state;
+};
+
+DECLARE_CRC8_TABLE(sps30_crc8_table);
+
+static int sps30_write_then_read(struct sps30_state *state, u8 *txbuf,
+ int txsize, u8 *rxbuf, int rxsize)
+{
+ int ret;
+
+ /*
+ * Sensor does not support repeated start so instead of
+ * sending two i2c messages in a row we just send one by one.
+ */
+ ret = i2c_master_send(state->client, txbuf, txsize);
+ if (ret != txsize)
+ return ret < 0 ? ret : -EIO;
+
+ if (!rxbuf)
+ return 0;
+
+ ret = i2c_master_recv(state->client, rxbuf, rxsize);
+ if (ret != rxsize)
+ return ret < 0 ? ret : -EIO;
+
+ return 0;
+}
+
+static int sps30_do_cmd(struct sps30_state *state, u16 cmd, u8 *data, int size)
+{
+ /*
+ * Internally sensor stores measurements in a following manner:
+ *
+ * PM1: upper two bytes, crc8, lower two bytes, crc8
+ * PM2P5: upper two bytes, crc8, lower two bytes, crc8
+ * PM4: upper two bytes, crc8, lower two bytes, crc8
+ * PM10: upper two bytes, crc8, lower two bytes, crc8
+ *
+ * What follows next are number concentration measurements and
+ * typical particle size measurement which we omit.
+ */
+ u8 buf[SPS30_MAX_READ_SIZE] = { cmd >> 8, cmd };
+ int i, ret = 0;
+
+ switch (cmd) {
+ case SPS30_START_MEAS:
+ buf[2] = 0x03;
+ buf[3] = 0x00;
+ buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE);
+ ret = sps30_write_then_read(state, buf, 5, NULL, 0);
+ break;
+ case SPS30_STOP_MEAS:
+ case SPS30_RESET:
+ case SPS30_START_FAN_CLEANING:
+ ret = sps30_write_then_read(state, buf, 2, NULL, 0);
+ break;
+ case SPS30_READ_AUTO_CLEANING_PERIOD:
+ buf[0] = SPS30_AUTO_CLEANING_PERIOD >> 8;
+ buf[1] = (u8)SPS30_AUTO_CLEANING_PERIOD;
+ /* fall through */
+ case SPS30_READ_DATA_READY_FLAG:
+ case SPS30_READ_DATA:
+ case SPS30_READ_SERIAL:
+ /* every two data bytes are checksummed */
+ size += size / 2;
+ ret = sps30_write_then_read(state, buf, 2, buf, size);
+ break;
+ case SPS30_AUTO_CLEANING_PERIOD:
+ buf[2] = data[0];
+ buf[3] = data[1];
+ buf[4] = crc8(sps30_crc8_table, &buf[2], 2, CRC8_INIT_VALUE);
+ buf[5] = data[2];
+ buf[6] = data[3];
+ buf[7] = crc8(sps30_crc8_table, &buf[5], 2, CRC8_INIT_VALUE);
+ ret = sps30_write_then_read(state, buf, 8, NULL, 0);
+ break;
+ }
+
+ if (ret)
+ return ret;
+
+ /* validate received data and strip off crc bytes */
+ for (i = 0; i < size; i += 3) {
+ u8 crc = crc8(sps30_crc8_table, &buf[i], 2, CRC8_INIT_VALUE);
+
+ if (crc != buf[i + 2]) {
+ dev_err(&state->client->dev,
+ "data integrity check failed\n");
+ return -EIO;
+ }
+
+ *data++ = buf[i];
+ *data++ = buf[i + 1];
+ }
+
+ return 0;
+}
+
+static s32 sps30_float_to_int_clamped(const u8 *fp)
+{
+ int val = get_unaligned_be32(fp);
+ int mantissa = val & GENMASK(22, 0);
+ /* this is fine since passed float is always non-negative */
+ int exp = val >> 23;
+ int fraction, shift;
+
+ /* special case 0 */
+ if (!exp && !mantissa)
+ return 0;
+
+ exp -= 127;
+ if (exp < 0) {
+ /* return values ranging from 1 to 99 */
+ return ((((1 << 23) + mantissa) * 100) >> 23) >> (-exp);
+ }
+
+ /* return values ranging from 100 to 300000 */
+ shift = 23 - exp;
+ val = (1 << exp) + (mantissa >> shift);
+ if (val >= SPS30_MAX_PM)
+ return SPS30_MAX_PM * 100;
+
+ fraction = mantissa & GENMASK(shift - 1, 0);
+
+ return val * 100 + ((fraction * 100) >> shift);
+}
+
+static int sps30_do_meas(struct sps30_state *state, s32 *data, int size)
+{
+ int i, ret, tries = 5;
+ u8 tmp[16];
+
+ if (state->state == RESET) {
+ ret = sps30_do_cmd(state, SPS30_START_MEAS, NULL, 0);
+ if (ret)
+ return ret;
+
+ state->state = MEASURING;
+ }
+
+ while (tries--) {
+ ret = sps30_do_cmd(state, SPS30_READ_DATA_READY_FLAG, tmp, 2);
+ if (ret)
+ return -EIO;
+
+ /* new measurements ready to be read */
+ if (tmp[1] == 1)
+ break;
+
+ msleep_interruptible(300);
+ }
+
+ if (tries == -1)
+ return -ETIMEDOUT;
+
+ ret = sps30_do_cmd(state, SPS30_READ_DATA, tmp, sizeof(int) * size);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < size; i++)
+ data[i] = sps30_float_to_int_clamped(&tmp[4 * i]);
+
+ return 0;
+}
+
+static irqreturn_t sps30_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct sps30_state *state = iio_priv(indio_dev);
+ int ret;
+ s32 data[4 + 2]; /* PM1, PM2P5, PM4, PM10, timestamp */
+
+ mutex_lock(&state->lock);
+ ret = sps30_do_meas(state, data, 4);
+ mutex_unlock(&state->lock);
+ if (ret)
+ goto err;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data,
+ iio_get_time_ns(indio_dev));
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int sps30_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct sps30_state *state = iio_priv(indio_dev);
+ int data[4], ret = -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_MASSCONCENTRATION:
+ mutex_lock(&state->lock);
+ /* read up to the number of bytes actually needed */
+ switch (chan->channel2) {
+ case IIO_MOD_PM1:
+ ret = sps30_do_meas(state, data, 1);
+ break;
+ case IIO_MOD_PM2P5:
+ ret = sps30_do_meas(state, data, 2);
+ break;
+ case IIO_MOD_PM4:
+ ret = sps30_do_meas(state, data, 3);
+ break;
+ case IIO_MOD_PM10:
+ ret = sps30_do_meas(state, data, 4);
+ break;
+ }
+ mutex_unlock(&state->lock);
+ if (ret)
+ return ret;
+
+ *val = data[chan->address] / 100;
+ *val2 = (data[chan->address] % 100) * 10000;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_MASSCONCENTRATION:
+ switch (chan->channel2) {
+ case IIO_MOD_PM1:
+ case IIO_MOD_PM2P5:
+ case IIO_MOD_PM4:
+ case IIO_MOD_PM10:
+ *val = 0;
+ *val2 = 10000;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static int sps30_do_cmd_reset(struct sps30_state *state)
+{
+ int ret;
+
+ ret = sps30_do_cmd(state, SPS30_RESET, NULL, 0);
+ msleep(300);
+ /*
+ * Power-on-reset causes sensor to produce some glitch on i2c bus and
+ * some controllers end up in error state. Recover simply by placing
+ * some data on the bus, for example STOP_MEAS command, which
+ * is NOP in this case.
+ */
+ sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
+ state->state = RESET;
+
+ return ret;
+}
+
+static ssize_t start_cleaning_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sps30_state *state = iio_priv(indio_dev);
+ int val, ret;
+
+ if (kstrtoint(buf, 0, &val) || val != 1)
+ return -EINVAL;
+
+ mutex_lock(&state->lock);
+ ret = sps30_do_cmd(state, SPS30_START_FAN_CLEANING, NULL, 0);
+ mutex_unlock(&state->lock);
+ if (ret)
+ return ret;
+
+ return len;
+}
+
+static ssize_t cleaning_period_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sps30_state *state = iio_priv(indio_dev);
+ u8 tmp[4];
+ int ret;
+
+ mutex_lock(&state->lock);
+ ret = sps30_do_cmd(state, SPS30_READ_AUTO_CLEANING_PERIOD, tmp, 4);
+ mutex_unlock(&state->lock);
+ if (ret)
+ return ret;
+
+ return sprintf(buf, "%d\n", get_unaligned_be32(tmp));
+}
+
+static ssize_t cleaning_period_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t len)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct sps30_state *state = iio_priv(indio_dev);
+ int val, ret;
+ u8 tmp[4];
+
+ if (kstrtoint(buf, 0, &val))
+ return -EINVAL;
+
+ if ((val < SPS30_AUTO_CLEANING_PERIOD_MIN) ||
+ (val > SPS30_AUTO_CLEANING_PERIOD_MAX))
+ return -EINVAL;
+
+ put_unaligned_be32(val, tmp);
+
+ mutex_lock(&state->lock);
+ ret = sps30_do_cmd(state, SPS30_AUTO_CLEANING_PERIOD, tmp, 0);
+ if (ret) {
+ mutex_unlock(&state->lock);
+ return ret;
+ }
+
+ msleep(20);
+
+ /*
+ * sensor requires reset in order to return up to date self cleaning
+ * period
+ */
+ ret = sps30_do_cmd_reset(state);
+ if (ret)
+ dev_warn(dev,
+ "period changed but reads will return the old value\n");
+
+ mutex_unlock(&state->lock);
+
+ return len;
+}
+
+static ssize_t cleaning_period_available_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "[%d %d %d]\n",
+ SPS30_AUTO_CLEANING_PERIOD_MIN, 1,
+ SPS30_AUTO_CLEANING_PERIOD_MAX);
+}
+
+static IIO_DEVICE_ATTR_WO(start_cleaning, 0);
+static IIO_DEVICE_ATTR_RW(cleaning_period, 0);
+static IIO_DEVICE_ATTR_RO(cleaning_period_available, 0);
+
+static struct attribute *sps30_attrs[] = {
+ &iio_dev_attr_start_cleaning.dev_attr.attr,
+ &iio_dev_attr_cleaning_period.dev_attr.attr,
+ &iio_dev_attr_cleaning_period_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group sps30_attr_group = {
+ .attrs = sps30_attrs,
+};
+
+static const struct iio_info sps30_info = {
+ .attrs = &sps30_attr_group,
+ .read_raw = sps30_read_raw,
+};
+
+#define SPS30_CHAN(_index, _mod) { \
+ .type = IIO_MASSCONCENTRATION, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_ ## _mod, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .address = _mod, \
+ .scan_index = _index, \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = 19, \
+ .storagebits = 32, \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+static const struct iio_chan_spec sps30_channels[] = {
+ SPS30_CHAN(0, PM1),
+ SPS30_CHAN(1, PM2P5),
+ SPS30_CHAN(2, PM4),
+ SPS30_CHAN(3, PM10),
+ IIO_CHAN_SOFT_TIMESTAMP(4),
+};
+
+static void sps30_stop_meas(void *data)
+{
+ struct sps30_state *state = data;
+
+ sps30_do_cmd(state, SPS30_STOP_MEAS, NULL, 0);
+}
+
+static const unsigned long sps30_scan_masks[] = { 0x0f, 0x00 };
+
+static int sps30_probe(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev;
+ struct sps30_state *state;
+ u8 buf[32];
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ state = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ state->client = client;
+ state->state = RESET;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &sps30_info;
+ indio_dev->name = client->name;
+ indio_dev->channels = sps30_channels;
+ indio_dev->num_channels = ARRAY_SIZE(sps30_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->available_scan_masks = sps30_scan_masks;
+
+ mutex_init(&state->lock);
+ crc8_populate_msb(sps30_crc8_table, SPS30_CRC8_POLYNOMIAL);
+
+ ret = sps30_do_cmd_reset(state);
+ if (ret) {
+ dev_err(&client->dev, "failed to reset device\n");
+ return ret;
+ }
+
+ ret = sps30_do_cmd(state, SPS30_READ_SERIAL, buf, sizeof(buf));
+ if (ret) {
+ dev_err(&client->dev, "failed to read serial number\n");
+ return ret;
+ }
+ /* returned serial number is already NUL terminated */
+ dev_info(&client->dev, "serial number: %s\n", buf);
+
+ ret = devm_add_action_or_reset(&client->dev, sps30_stop_meas, state);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev, NULL,
+ sps30_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id sps30_id[] = {
+ { "sps30" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sps30_id);
+
+static const struct of_device_id sps30_of_match[] = {
+ { .compatible = "sensirion,sps30" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sps30_of_match);
+
+static struct i2c_driver sps30_driver = {
+ .driver = {
+ .name = "sps30",
+ .of_match_table = sps30_of_match,
+ },
+ .id_table = sps30_id,
+ .probe_new = sps30_probe,
+};
+module_i2c_driver(sps30_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
+MODULE_DESCRIPTION("Sensirion SPS30 particulate matter sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/Kconfig b/drivers/iio/common/Kconfig
index e108996..2b9ee91 100644
--- a/drivers/iio/common/Kconfig
+++ b/drivers/iio/common/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# IIO common modules
#
diff --git a/drivers/iio/common/cros_ec_sensors/Kconfig b/drivers/iio/common/cros_ec_sensors/Kconfig
index 135f682..cdbb29c 100644
--- a/drivers/iio/common/cros_ec_sensors/Kconfig
+++ b/drivers/iio/common/cros_ec_sensors/Kconfig
@@ -1,9 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Chrome OS Embedded Controller managed sensors library
#
config IIO_CROS_EC_SENSORS_CORE
tristate "ChromeOS EC Sensors Core"
- depends on SYSFS && MFD_CROS_EC
+ depends on SYSFS && CROS_EC
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
@@ -20,3 +21,12 @@
Accelerometers, Gyroscope and Magnetometer that are
presented by the ChromeOS EC Sensor hub.
Creates an IIO device for each functions.
+
+config IIO_CROS_EC_SENSORS_LID_ANGLE
+ tristate "ChromeOS EC Sensor for lid angle"
+ depends on IIO_CROS_EC_SENSORS_CORE
+ help
+ Module to report the angle between lid and base for some
+ convertible devices.
+ This module is loaded when the EC can calculate the angle between the base
+ and the lid.
diff --git a/drivers/iio/common/cros_ec_sensors/Makefile b/drivers/iio/common/cros_ec_sensors/Makefile
index ec716ff..e0a33ab 100644
--- a/drivers/iio/common/cros_ec_sensors/Makefile
+++ b/drivers/iio/common/cros_ec_sensors/Makefile
@@ -1,6 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for sensors seen through the ChromeOS EC sensor hub.
#
obj-$(CONFIG_IIO_CROS_EC_SENSORS_CORE) += cros_ec_sensors_core.o
obj-$(CONFIG_IIO_CROS_EC_SENSORS) += cros_ec_sensors.o
+obj-$(CONFIG_IIO_CROS_EC_SENSORS_LID_ANGLE) += cros_ec_lid_angle.o
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c
new file mode 100644
index 0000000..1dcc2a1
--- /dev/null
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_lid_angle.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+
+/*
+ * cros_ec_lid_angle - Driver for CrOS EC lid angle sensor.
+ *
+ * Copyright 2018 Google, Inc
+ *
+ * This driver uses the cros-ec interface to communicate with the Chrome OS
+ * EC about counter sensors. Counters are presented through
+ * iio sysfs.
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/common/cros_ec_sensors_core.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/kfifo_buf.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#define DRV_NAME "cros-ec-lid-angle"
+
+/*
+ * One channel for the lid angle, the other for timestamp.
+ */
+static const struct iio_chan_spec cros_ec_lid_angle_channels[] = {
+ {
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .scan_type.realbits = CROS_EC_SENSOR_BITS,
+ .scan_type.storagebits = CROS_EC_SENSOR_BITS,
+ .scan_type.sign = 'u',
+ .type = IIO_ANGL
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1)
+};
+
+/* State data for ec_sensors iio driver. */
+struct cros_ec_lid_angle_state {
+ /* Shared by all sensors */
+ struct cros_ec_sensors_core_state core;
+};
+
+static int cros_ec_sensors_read_lid_angle(struct iio_dev *indio_dev,
+ unsigned long scan_mask, s16 *data)
+{
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+ int ret;
+
+ st->param.cmd = MOTIONSENSE_CMD_LID_ANGLE;
+ ret = cros_ec_motion_send_host_cmd(st, sizeof(st->resp->lid_angle));
+ if (ret) {
+ dev_warn(&indio_dev->dev, "Unable to read lid angle\n");
+ return ret;
+ }
+
+ *data = st->resp->lid_angle.value;
+ return 0;
+}
+
+static int cros_ec_lid_angle_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cros_ec_lid_angle_state *st = iio_priv(indio_dev);
+ s16 data;
+ int ret;
+
+ mutex_lock(&st->core.cmd_lock);
+ ret = cros_ec_sensors_read_lid_angle(indio_dev, 1, &data);
+ if (ret == 0) {
+ *val = data;
+ ret = IIO_VAL_INT;
+ }
+ mutex_unlock(&st->core.cmd_lock);
+ return ret;
+}
+
+static const struct iio_info cros_ec_lid_angle_info = {
+ .read_raw = &cros_ec_lid_angle_read,
+};
+
+static int cros_ec_lid_angle_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct iio_dev *indio_dev;
+ struct cros_ec_lid_angle_state *state;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ ret = cros_ec_sensors_core_init(pdev, indio_dev, false);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &cros_ec_lid_angle_info;
+ state = iio_priv(indio_dev);
+ indio_dev->channels = cros_ec_lid_angle_channels;
+ indio_dev->num_channels = ARRAY_SIZE(cros_ec_lid_angle_channels);
+
+ state->core.read_ec_sensors_data = cros_ec_sensors_read_lid_angle;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ cros_ec_sensors_capture, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct platform_device_id cros_ec_lid_angle_ids[] = {
+ {
+ .name = DRV_NAME,
+ },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_lid_angle_ids);
+
+static struct platform_driver cros_ec_lid_angle_platform_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &cros_ec_sensors_pm_ops,
+ },
+ .probe = cros_ec_lid_angle_probe,
+ .id_table = cros_ec_lid_angle_ids,
+};
+module_platform_driver(cros_ec_lid_angle_platform_driver);
+
+MODULE_DESCRIPTION("ChromeOS EC driver for reporting convertible lid angle.");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
index 89cb006..a698772 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors.c
@@ -1,22 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* cros_ec_sensors - Driver for Chrome OS Embedded Controller sensors.
*
* Copyright (C) 2016 Google, Inc
*
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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.
- *
* This driver uses the cros-ec interface to communicate with the Chrome OS
* EC about sensors data. Data access is presented through iio sysfs.
*/
-#include <linux/delay.h>
#include <linux/device.h>
#include <linux/iio/buffer.h>
#include <linux/iio/common/cros_ec_sensors_core.h>
@@ -26,11 +17,11 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/sysfs.h>
#define CROS_EC_SENSORS_MAX_CHANNELS 4
@@ -73,10 +64,35 @@
/* Save values */
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
- st->core.calib[i] =
+ st->core.calib[i].offset =
st->core.resp->sensor_offset.offset[i];
ret = IIO_VAL_INT;
- *val = st->core.calib[idx];
+ *val = st->core.calib[idx].offset;
+ break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_SCALE;
+ st->core.param.sensor_offset.flags = 0;
+
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ if (ret == -EPROTO) {
+ /* Reading calibscale is not supported on older EC. */
+ *val = 1;
+ *val2 = 0;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ } else if (ret) {
+ break;
+ }
+
+ /* Save values */
+ for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
+ st->core.calib[i].scale =
+ st->core.resp->sensor_scale.scale[i];
+
+ *val = st->core.calib[idx].scale >> 15;
+ *val2 = ((st->core.calib[idx].scale & 0x7FFF) * 1000000LL) /
+ MOTION_SENSE_DEFAULT_SCALE;
+ ret = IIO_VAL_INT_PLUS_MICRO;
break;
case IIO_CHAN_INFO_SCALE:
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
@@ -103,9 +119,10 @@
* Do not use IIO_DEGREE_TO_RAD to avoid precision
* loss. Round to the nearest integer.
*/
- *val = div_s64(val64 * 314159 + 9000000ULL, 1000);
- *val2 = 18000 << (CROS_EC_SENSOR_BITS - 1);
- ret = IIO_VAL_FRACTIONAL;
+ *val = 0;
+ *val2 = div_s64(val64 * 3141592653ULL,
+ 180 << (CROS_EC_SENSOR_BITS - 1));
+ ret = IIO_VAL_INT_PLUS_NANO;
break;
case MOTIONSENSE_TYPE_MAG:
/*
@@ -143,7 +160,7 @@
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
- st->core.calib[idx] = val;
+ st->core.calib[idx].offset = val;
/* Send to EC for each axis, even if not complete */
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
@@ -151,12 +168,27 @@
MOTION_SENSE_SET_OFFSET;
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
st->core.param.sensor_offset.offset[i] =
- st->core.calib[i];
+ st->core.calib[i].offset;
st->core.param.sensor_offset.temp =
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
ret = cros_ec_motion_send_host_cmd(&st->core, 0);
break;
+ case IIO_CHAN_INFO_CALIBSCALE:
+ st->core.calib[idx].scale = val;
+ /* Send to EC for each axis, even if not complete */
+
+ st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_SCALE;
+ st->core.param.sensor_offset.flags =
+ MOTION_SENSE_SET_OFFSET;
+ for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
+ st->core.param.sensor_scale.scale[i] =
+ st->core.calib[i].scale;
+ st->core.param.sensor_scale.temp =
+ EC_MOTION_SENSE_INVALID_CALIB_TEMP;
+
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ break;
case IIO_CHAN_INFO_SCALE:
if (st->core.type == MOTIONSENSE_TYPE_MAG) {
ret = -EINVAL;
@@ -184,6 +216,7 @@
static const struct iio_info ec_sensors_info = {
.read_raw = &cros_ec_sensors_read,
.write_raw = &cros_ec_sensors_write,
+ .read_avail = &cros_ec_sensors_core_read_avail,
};
static int cros_ec_sensors_probe(struct platform_device *pdev)
@@ -215,11 +248,14 @@
/* Common part */
channel->info_mask_separate =
BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_CALIBBIAS);
+ BIT(IIO_CHAN_INFO_CALIBBIAS) |
+ BIT(IIO_CHAN_INFO_CALIBSCALE);
channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SCALE) |
BIT(IIO_CHAN_INFO_FREQUENCY) |
BIT(IIO_CHAN_INFO_SAMP_FREQ);
+ channel->info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
channel->scan_index = i;
diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
index 414cc43..d2609e6 100644
--- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
+++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* cros_ec_sensors_core - Common function for Chrome OS EC sensor driver.
*
* Copyright (C) 2016 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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/delay.h>
@@ -22,10 +14,10 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
#include <linux/slab.h>
-#include <linux/sysfs.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
static char *cros_ec_loc[] = {
@@ -34,6 +26,62 @@
[MOTIONSENSE_LOC_MAX] = "unknown",
};
+static int cros_ec_get_host_cmd_version_mask(struct cros_ec_device *ec_dev,
+ u16 cmd_offset, u16 cmd, u32 *mask)
+{
+ int ret;
+ struct {
+ struct cros_ec_command msg;
+ union {
+ struct ec_params_get_cmd_versions params;
+ struct ec_response_get_cmd_versions resp;
+ };
+ } __packed buf = {
+ .msg = {
+ .command = EC_CMD_GET_CMD_VERSIONS + cmd_offset,
+ .insize = sizeof(struct ec_response_get_cmd_versions),
+ .outsize = sizeof(struct ec_params_get_cmd_versions)
+ },
+ .params = {.cmd = cmd}
+ };
+
+ ret = cros_ec_cmd_xfer_status(ec_dev, &buf.msg);
+ if (ret >= 0)
+ *mask = buf.resp.version_mask;
+ return ret;
+}
+
+static void get_default_min_max_freq(enum motionsensor_type type,
+ u32 *min_freq,
+ u32 *max_freq)
+{
+ switch (type) {
+ case MOTIONSENSE_TYPE_ACCEL:
+ case MOTIONSENSE_TYPE_GYRO:
+ *min_freq = 12500;
+ *max_freq = 100000;
+ break;
+ case MOTIONSENSE_TYPE_MAG:
+ *min_freq = 5000;
+ *max_freq = 25000;
+ break;
+ case MOTIONSENSE_TYPE_PROX:
+ case MOTIONSENSE_TYPE_LIGHT:
+ *min_freq = 100;
+ *max_freq = 50000;
+ break;
+ case MOTIONSENSE_TYPE_BARO:
+ *min_freq = 250;
+ *max_freq = 20000;
+ break;
+ case MOTIONSENSE_TYPE_ACTIVITY:
+ default:
+ *min_freq = 0;
+ *max_freq = 0;
+ break;
+ }
+}
+
int cros_ec_sensors_core_init(struct platform_device *pdev,
struct iio_dev *indio_dev,
bool physical_device)
@@ -42,6 +90,8 @@
struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
struct cros_ec_dev *ec = dev_get_drvdata(pdev->dev.parent);
struct cros_ec_sensor_platform *sensor_platform = dev_get_platdata(dev);
+ u32 ver_mask;
+ int ret, i;
platform_set_drvdata(pdev, indio_dev);
@@ -56,8 +106,15 @@
mutex_init(&state->cmd_lock);
+ ret = cros_ec_get_host_cmd_version_mask(state->ec,
+ ec->cmd_offset,
+ EC_CMD_MOTION_SENSE_CMD,
+ &ver_mask);
+ if (ret < 0)
+ return ret;
+
/* Set up the host command structure. */
- state->msg->version = 2;
+ state->msg->version = fls(ver_mask) - 1;
state->msg->command = EC_CMD_MOTION_SENSE_CMD + ec->cmd_offset;
state->msg->outsize = sizeof(struct ec_params_motion_sense);
@@ -69,12 +126,32 @@
state->param.cmd = MOTIONSENSE_CMD_INFO;
state->param.info.sensor_num = sensor_platform->sensor_num;
- if (cros_ec_motion_send_host_cmd(state, 0)) {
+ ret = cros_ec_motion_send_host_cmd(state, 0);
+ if (ret) {
dev_warn(dev, "Can not access sensor info\n");
- return -EIO;
+ return ret;
}
state->type = state->resp->info.type;
state->loc = state->resp->info.location;
+
+ /* Set sign vector, only used for backward compatibility. */
+ memset(state->sign, 1, CROS_EC_SENSOR_MAX_AXIS);
+
+ for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
+ state->calib[i].scale = MOTION_SENSE_DEFAULT_SCALE;
+
+ /* 0 is a correct value used to stop the device */
+ state->frequencies[0] = 0;
+ if (state->msg->version < 3) {
+ get_default_min_max_freq(state->resp->info.type,
+ &state->frequencies[1],
+ &state->frequencies[2]);
+ } else {
+ state->frequencies[1] =
+ state->resp->info_3.min_frequency;
+ state->frequencies[2] =
+ state->resp->info_3.max_frequency;
+ }
}
return 0;
@@ -95,7 +172,7 @@
ret = cros_ec_cmd_xfer_status(state->ec, state->msg);
if (ret < 0)
- return -EIO;
+ return ret;
if (ret &&
state->resp != (struct ec_response_motion_sense *)state->msg->data)
@@ -127,13 +204,22 @@
} else {
/* Save values */
for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++)
- st->calib[i] = st->resp->perform_calib.offset[i];
+ st->calib[i].offset = st->resp->perform_calib.offset[i];
}
mutex_unlock(&st->cmd_lock);
return ret ? ret : len;
}
+static ssize_t cros_ec_sensors_id(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan, char *buf)
+{
+ struct cros_ec_sensors_core_state *st = iio_priv(indio_dev);
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", st->param.info.sensor_num);
+}
+
static ssize_t cros_ec_sensors_loc(struct iio_dev *indio_dev,
uintptr_t private, const struct iio_chan_spec *chan,
char *buf)
@@ -150,6 +236,11 @@
.write = cros_ec_sensors_calibrate
},
{
+ .name = "id",
+ .shared = IIO_SHARED_BY_ALL,
+ .read = cros_ec_sensors_id
+ },
+ {
.name = "location",
.shared = IIO_SHARED_BY_ALL,
.read = cros_ec_sensors_loc
@@ -263,12 +354,24 @@
if (ret < 0)
return ret;
+ *data *= st->sign[i];
data++;
}
return 0;
}
+/**
+ * cros_ec_sensors_read_lpc() - read acceleration data from EC shared memory.
+ * @indio_dev: pointer to IIO device.
+ * @scan_mask: bitmap of the sensor indices to scan.
+ * @data: location to store data.
+ *
+ * Note: this is the safe function for reading the EC data. It guarantees
+ * that the data sampled was not modified by the EC while being read.
+ *
+ * Return: 0 on success, -errno on failure.
+ */
int cros_ec_sensors_read_lpc(struct iio_dev *indio_dev,
unsigned long scan_mask, s16 *data)
{
@@ -380,7 +483,7 @@
struct iio_chan_spec const *chan,
int *val, int *val2, long mask)
{
- int ret = IIO_VAL_INT;
+ int ret;
switch (mask) {
case IIO_CHAN_INFO_SAMP_FREQ:
@@ -388,22 +491,27 @@
st->param.ec_rate.data =
EC_MOTION_SENSE_NO_VALUE;
- if (cros_ec_motion_send_host_cmd(st, 0))
- ret = -EIO;
- else
- *val = st->resp->ec_rate.ret;
+ ret = cros_ec_motion_send_host_cmd(st, 0);
+ if (ret)
+ break;
+
+ *val = st->resp->ec_rate.ret;
+ ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_FREQUENCY:
st->param.cmd = MOTIONSENSE_CMD_SENSOR_ODR;
st->param.sensor_odr.data =
EC_MOTION_SENSE_NO_VALUE;
- if (cros_ec_motion_send_host_cmd(st, 0))
- ret = -EIO;
- else
- *val = st->resp->sensor_odr.ret;
+ ret = cros_ec_motion_send_host_cmd(st, 0);
+ if (ret)
+ break;
+
+ *val = st->resp->sensor_odr.ret;
+ ret = IIO_VAL_INT;
break;
default:
+ ret = -EINVAL;
break;
}
@@ -411,11 +519,32 @@
}
EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read);
+int cros_ec_sensors_core_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals,
+ int *type,
+ int *length,
+ long mask)
+{
+ struct cros_ec_sensors_core_state *state = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *length = ARRAY_SIZE(state->frequencies);
+ *vals = (const int *)&state->frequencies;
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(cros_ec_sensors_core_read_avail);
+
int cros_ec_sensors_core_write(struct cros_ec_sensors_core_state *st,
struct iio_chan_spec const *chan,
int val, int val2, long mask)
{
- int ret = 0;
+ int ret;
switch (mask) {
case IIO_CHAN_INFO_FREQUENCY:
@@ -425,17 +554,16 @@
/* Always roundup, so caller gets at least what it asks for. */
st->param.sensor_odr.roundup = 1;
- if (cros_ec_motion_send_host_cmd(st, 0))
- ret = -EIO;
+ ret = cros_ec_motion_send_host_cmd(st, 0);
break;
case IIO_CHAN_INFO_SAMP_FREQ:
st->param.cmd = MOTIONSENSE_CMD_EC_RATE;
st->param.ec_rate.data = val;
- if (cros_ec_motion_send_host_cmd(st, 0))
- ret = -EIO;
- else
- st->curr_sampl_freq = val;
+ ret = cros_ec_motion_send_host_cmd(st, 0);
+ if (ret)
+ break;
+ st->curr_sampl_freq = val;
break;
default:
ret = -EINVAL;
diff --git a/drivers/iio/common/hid-sensors/Kconfig b/drivers/iio/common/hid-sensors/Kconfig
index 8010537..24d4925 100644
--- a/drivers/iio/common/hid-sensors/Kconfig
+++ b/drivers/iio/common/hid-sensors/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Hid Sensor common modules
#
diff --git a/drivers/iio/common/hid-sensors/Makefile b/drivers/iio/common/hid-sensors/Makefile
index 22e7c5a..64b01a8 100644
--- a/drivers/iio/common/hid-sensors/Makefile
+++ b/drivers/iio/common/hid-sensors/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the Hid sensor common modules.
#
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
index ed3849d..442ff78 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-attributes.c
@@ -1,31 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
+#include <linux/kernel.h>
#include <linux/slab.h>
+#include <linux/time.h>
+
#include <linux/hid-sensor-hub.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+#define HZ_PER_MHZ 1000000L
+
static struct {
u32 usage_id;
int unit; /* 0 for default others from HID sensor spec */
@@ -81,16 +73,6 @@
{HID_USAGE_SENSOR_HUMIDITY, 0, 1000, 0},
};
-static int pow_10(unsigned power)
-{
- int i;
- int ret = 1;
- for (i = 0; i < power; ++i)
- ret = ret * 10;
-
- return ret;
-}
-
static void simple_div(int dividend, int divisor, int *whole,
int *micro_frac)
{
@@ -109,14 +91,16 @@
rem *= 10;
exp++;
}
- *micro_frac = (rem / divisor) * pow_10(6-exp);
+ *micro_frac = (rem / divisor) * int_pow(10, 6 - exp);
}
}
static void split_micro_fraction(unsigned int no, int exp, int *val1, int *val2)
{
- *val1 = no/pow_10(exp);
- *val2 = no%pow_10(exp) * pow_10(6-exp);
+ int divisor = int_pow(10, exp);
+
+ *val1 = no / divisor;
+ *val2 = no % divisor * int_pow(10, 6 - exp);
}
/*
@@ -138,7 +122,7 @@
}
exp = hid_sensor_convert_exponent(exp);
if (exp >= 0) {
- *val1 = sign * value * pow_10(exp);
+ *val1 = sign * value * int_pow(10, exp);
*val2 = 0;
} else {
split_micro_fraction(value, -exp, val1, val2);
@@ -151,6 +135,7 @@
static u32 convert_to_vtf_format(int size, int exp, int val1, int val2)
{
+ int divisor;
u32 value;
int sign = 1;
@@ -158,10 +143,13 @@
sign = -1;
exp = hid_sensor_convert_exponent(exp);
if (exp < 0) {
- value = abs(val1) * pow_10(-exp);
- value += abs(val2) / pow_10(6+exp);
- } else
- value = abs(val1) / pow_10(exp);
+ divisor = int_pow(10, 6 + exp);
+ value = abs(val1) * int_pow(10, -exp);
+ value += abs(val2) / divisor;
+ } else {
+ divisor = int_pow(10, exp);
+ value = abs(val1) / divisor;
+ }
if (sign < 0)
value = ((1LL << (size * 8)) - value);
@@ -224,12 +212,12 @@
if (val1 < 0 || val2 < 0)
return -EINVAL;
- value = val1 * pow_10(6) + val2;
+ value = val1 * HZ_PER_MHZ + val2;
if (value) {
if (st->poll.units == HID_USAGE_SENSOR_UNITS_MILLISECOND)
- value = pow_10(9)/value;
+ value = NSEC_PER_SEC / value;
else if (st->poll.units == HID_USAGE_SENSOR_UNITS_SECOND)
- value = pow_10(6)/value;
+ value = USEC_PER_SEC / value;
else
value = 0;
}
@@ -318,40 +306,44 @@
static void adjust_exponent_nano(int *val0, int *val1, int scale0,
int scale1, int exp)
{
+ int divisor;
int i;
int x;
int res;
int rem;
if (exp > 0) {
- *val0 = scale0 * pow_10(exp);
+ *val0 = scale0 * int_pow(10, exp);
res = 0;
if (exp > 9) {
*val1 = 0;
return;
}
for (i = 0; i < exp; ++i) {
- x = scale1 / pow_10(8 - i);
- res += (pow_10(exp - 1 - i) * x);
- scale1 = scale1 % pow_10(8 - i);
+ divisor = int_pow(10, 8 - i);
+ x = scale1 / divisor;
+ res += int_pow(10, exp - 1 - i) * x;
+ scale1 = scale1 % divisor;
}
*val0 += res;
- *val1 = scale1 * pow_10(exp);
+ *val1 = scale1 * int_pow(10, exp);
} else if (exp < 0) {
exp = abs(exp);
if (exp > 9) {
*val0 = *val1 = 0;
return;
}
- *val0 = scale0 / pow_10(exp);
- rem = scale0 % pow_10(exp);
+ divisor = int_pow(10, exp);
+ *val0 = scale0 / divisor;
+ rem = scale0 % divisor;
res = 0;
for (i = 0; i < (9 - exp); ++i) {
- x = scale1 / pow_10(8 - i);
- res += (pow_10(8 - exp - i) * x);
- scale1 = scale1 % pow_10(8 - i);
+ divisor = int_pow(10, 8 - i);
+ x = scale1 / divisor;
+ res += int_pow(10, 8 - exp - i) * x;
+ scale1 = scale1 % divisor;
}
- *val1 = rem * pow_10(9 - exp) + res;
+ *val1 = rem * int_pow(10, 9 - exp) + res;
} else {
*val0 = scale0;
*val1 = scale1;
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
index 1e10c0a..906d877 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <linux/device.h>
#include <linux/platform_device.h>
diff --git a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
index 9f4713f..f47b940 100644
--- a/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
+++ b/drivers/iio/common/hid-sensors/hid-sensor-trigger.h
@@ -1,20 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#ifndef _HID_SENSOR_TRIGGER_H
#define _HID_SENSOR_TRIGGER_H
diff --git a/drivers/iio/common/ms_sensors/Kconfig b/drivers/iio/common/ms_sensors/Kconfig
index b28a92b..45012b7 100644
--- a/drivers/iio/common/ms_sensors/Kconfig
+++ b/drivers/iio/common/ms_sensors/Kconfig
@@ -1,6 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Measurements Specialties sensors common library
#
config IIO_MS_SENSORS_I2C
- tristate
+ tristate
diff --git a/drivers/iio/common/ms_sensors/Makefile b/drivers/iio/common/ms_sensors/Makefile
index 7846428..028573b 100644
--- a/drivers/iio/common/ms_sensors/Makefile
+++ b/drivers/iio/common/ms_sensors/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for the Measurement Specialties sensor common modules.
#
diff --git a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c
index 125b5ff..b52cba1 100644
--- a/drivers/iio/common/ms_sensors/ms_sensors_i2c.c
+++ b/drivers/iio/common/ms_sensors/ms_sensors_i2c.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Measurements Specialties driver common i2c functions
*
* Copyright (c) 2015 Measurement-Specialties
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
diff --git a/drivers/iio/common/ms_sensors/ms_sensors_i2c.h b/drivers/iio/common/ms_sensors/ms_sensors_i2c.h
index 7b614ad..bad09c8 100644
--- a/drivers/iio/common/ms_sensors/ms_sensors_i2c.h
+++ b/drivers/iio/common/ms_sensors/ms_sensors_i2c.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Measurements Specialties common sensor driver
*
* Copyright (c) 2015 Measurement-Specialties
- *
- * 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.
*/
#ifndef _MS_SENSORS_I2C_H
diff --git a/drivers/iio/common/ssp_sensors/Kconfig b/drivers/iio/common/ssp_sensors/Kconfig
index 0ea4faf..5262409 100644
--- a/drivers/iio/common/ssp_sensors/Kconfig
+++ b/drivers/iio/common/ssp_sensors/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# SSP sensor drivers and commons configuration
#
diff --git a/drivers/iio/common/ssp_sensors/Makefile b/drivers/iio/common/ssp_sensors/Makefile
index 1e0389e..ba83142 100644
--- a/drivers/iio/common/ssp_sensors/Makefile
+++ b/drivers/iio/common/ssp_sensors/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for SSP sensor drivers and commons.
#
diff --git a/drivers/iio/common/ssp_sensors/ssp.h b/drivers/iio/common/ssp_sensors/ssp.h
index 82a01ad..0a381bb 100644
--- a/drivers/iio/common/ssp_sensors/ssp.h
+++ b/drivers/iio/common/ssp_sensors/ssp.h
@@ -1,16 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
- *
- * 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.
- *
*/
#ifndef __SSP_SENSORHUB_H__
diff --git a/drivers/iio/common/ssp_sensors/ssp_dev.c b/drivers/iio/common/ssp_sensors/ssp_dev.c
index af3aa38..9c70553 100644
--- a/drivers/iio/common/ssp_sensors/ssp_dev.c
+++ b/drivers/iio/common/ssp_sensors/ssp_dev.c
@@ -1,16 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
- *
- * 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/iio/iio.h>
@@ -462,43 +452,35 @@
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/ssp_sensors/ssp_iio.c b/drivers/iio/common/ssp_sensors/ssp_iio.c
index 645f2e3..5336db8 100644
--- a/drivers/iio/common/ssp_sensors/ssp_iio.c
+++ b/drivers/iio/common/ssp_sensors/ssp_iio.c
@@ -1,16 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
- *
- * 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/iio/common/ssp_sensors.h>
@@ -81,7 +71,7 @@
unsigned int len, int64_t timestamp)
{
__le32 time;
- int64_t calculated_time;
+ int64_t calculated_time = 0;
struct ssp_sensor_data *spd = iio_priv(indio_dev);
if (indio_dev->scan_bytes == 0)
diff --git a/drivers/iio/common/ssp_sensors/ssp_spi.c b/drivers/iio/common/ssp_sensors/ssp_spi.c
index 2ab106b..7db3d58 100644
--- a/drivers/iio/common/ssp_sensors/ssp_spi.c
+++ b/drivers/iio/common/ssp_sensors/ssp_spi.c
@@ -1,16 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
- *
- * 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 "ssp.h"
diff --git a/drivers/iio/common/st_sensors/Kconfig b/drivers/iio/common/st_sensors/Kconfig
index 865f1ca..9364ec7 100644
--- a/drivers/iio/common/st_sensors/Kconfig
+++ b/drivers/iio/common/st_sensors/Kconfig
@@ -1,12 +1,15 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# STMicroelectronics sensors common library
#
config IIO_ST_SENSORS_I2C
tristate
+ select REGMAP_I2C
config IIO_ST_SENSORS_SPI
tristate
+ select REGMAP_SPI
config IIO_ST_SENSORS_CORE
tristate
diff --git a/drivers/iio/common/st_sensors/st_sensors_buffer.c b/drivers/iio/common/st_sensors/st_sensors_buffer.c
index df40452..eee3013 100644
--- a/drivers/iio/common/st_sensors/st_sensors_buffer.c
+++ b/drivers/iio/common/st_sensors/st_sensors_buffer.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics sensors buffer library driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -18,15 +17,16 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/irqreturn.h>
+#include <linux/regmap.h>
#include <linux/iio/common/st_sensors.h>
static int st_sensors_get_buffer_element(struct iio_dev *indio_dev, u8 *buf)
{
- int i;
struct st_sensor_data *sdata = iio_priv(indio_dev);
unsigned int num_data_channels = sdata->num_data_channels;
+ int i;
for_each_set_bit(i, indio_dev->active_scan_mask, num_data_channels) {
const struct iio_chan_spec *channel = &indio_dev->channels[i];
@@ -37,11 +37,8 @@
channel->scan_type.storagebits >> 3;
buf = PTR_ALIGN(buf, storage_bytes);
- if (sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
- channel->address,
- bytes_to_read, buf,
- sdata->multiread_bit) <
- bytes_to_read)
+ if (regmap_bulk_read(sdata->regmap, channel->address,
+ buf, bytes_to_read) < 0)
return -EIO;
/* Advance the buffer pointer */
diff --git a/drivers/iio/common/st_sensors/st_sensors_core.c b/drivers/iio/common/st_sensors/st_sensors_core.c
index 26fbd1b..4a3064f 100644
--- a/drivers/iio/common/st_sensors/st_sensors_core.c
+++ b/drivers/iio/common/st_sensors/st_sensors_core.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics sensors core library driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -16,6 +15,7 @@
#include <linux/regulator/consumer.h>
#include <linux/of.h>
#include <linux/of_device.h>
+#include <linux/regmap.h>
#include <asm/unaligned.h>
#include <linux/iio/common/st_sensors.h>
@@ -29,19 +29,10 @@
int st_sensors_write_data_with_mask(struct iio_dev *indio_dev,
u8 reg_addr, u8 mask, u8 data)
{
- int err;
- u8 new_data;
struct st_sensor_data *sdata = iio_priv(indio_dev);
- err = sdata->tf->read_byte(&sdata->tb, sdata->dev, reg_addr, &new_data);
- if (err < 0)
- goto st_sensors_write_data_with_mask_error;
-
- new_data = ((new_data & (~mask)) | ((data << __ffs(mask)) & mask));
- err = sdata->tf->write_byte(&sdata->tb, sdata->dev, reg_addr, new_data);
-
-st_sensors_write_data_with_mask_error:
- return err;
+ return regmap_update_bits(sdata->regmap,
+ reg_addr, mask, data << __ffs(mask));
}
int st_sensors_debugfs_reg_access(struct iio_dev *indio_dev,
@@ -49,19 +40,15 @@
unsigned *readval)
{
struct st_sensor_data *sdata = iio_priv(indio_dev);
- u8 readdata;
int err;
if (!readval)
- return sdata->tf->write_byte(&sdata->tb, sdata->dev,
- (u8)reg, (u8)writeval);
+ return regmap_write(sdata->regmap, reg, writeval);
- err = sdata->tf->read_byte(&sdata->tb, sdata->dev, (u8)reg, &readdata);
+ err = regmap_read(sdata->regmap, reg, readval);
if (err < 0)
return err;
- *readval = (unsigned)readdata;
-
return 0;
}
EXPORT_SYMBOL(st_sensors_debugfs_reg_access);
@@ -133,7 +120,7 @@
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 +129,6 @@
}
}
-st_sensors_match_odr_error:
return ret;
}
@@ -547,7 +533,7 @@
EXPORT_SYMBOL(st_sensors_set_fullscale_by_gain);
static int st_sensors_read_axis_data(struct iio_dev *indio_dev,
- struct iio_chan_spec const *ch, int *data)
+ struct iio_chan_spec const *ch, int *data)
{
int err;
u8 *outdata;
@@ -556,13 +542,12 @@
byte_for_channel = DIV_ROUND_UP(ch->scan_type.realbits +
ch->scan_type.shift, 8);
- outdata = kmalloc(byte_for_channel, GFP_KERNEL);
+ outdata = kmalloc(byte_for_channel, GFP_DMA | GFP_KERNEL);
if (!outdata)
return -ENOMEM;
- err = sdata->tf->read_multiple_byte(&sdata->tb, sdata->dev,
- ch->address, byte_for_channel,
- outdata, sdata->multiread_bit);
+ err = regmap_bulk_read(sdata->regmap, ch->address,
+ outdata, byte_for_channel);
if (err < 0)
goto st_sensors_free_memory;
@@ -610,69 +595,55 @@
}
EXPORT_SYMBOL(st_sensors_read_info_raw);
-static int st_sensors_init_interface_mode(struct iio_dev *indio_dev,
- const struct st_sensor_settings *sensor_settings)
+/*
+ * st_sensors_get_settings_index() - get index of the sensor settings for a
+ * specific device from list of settings
+ * @name: device name buffer reference.
+ * @list: sensor settings list.
+ * @list_length: length of sensor settings list.
+ *
+ * Return: non negative number on success (valid index),
+ * negative error code otherwise.
+ */
+int st_sensors_get_settings_index(const char *name,
+ const struct st_sensor_settings *list,
+ const int list_length)
{
- struct st_sensor_data *sdata = iio_priv(indio_dev);
- struct device_node *np = sdata->dev->of_node;
- struct st_sensors_platform_data *pdata;
+ int i, n;
- pdata = (struct st_sensors_platform_data *)sdata->dev->platform_data;
- if (((np && of_property_read_bool(np, "spi-3wire")) ||
- (pdata && pdata->spi_3wire)) && sensor_settings->sim.addr) {
- int err;
-
- err = sdata->tf->write_byte(&sdata->tb, sdata->dev,
- sensor_settings->sim.addr,
- sensor_settings->sim.value);
- if (err < 0) {
- dev_err(&indio_dev->dev,
- "failed to init interface mode\n");
- return err;
- }
- }
-
- return 0;
-}
-
-int st_sensors_check_device_support(struct iio_dev *indio_dev,
- int num_sensors_list,
- const struct st_sensor_settings *sensor_settings)
-{
- int i, n, err = 0;
- u8 wai;
- struct st_sensor_data *sdata = iio_priv(indio_dev);
-
- for (i = 0; i < num_sensors_list; i++) {
+ for (i = 0; i < list_length; i++) {
for (n = 0; n < ST_SENSORS_MAX_4WAI; n++) {
- if (strcmp(indio_dev->name,
- sensor_settings[i].sensors_supported[n]) == 0) {
- break;
- }
+ if (strcmp(name, list[i].sensors_supported[n]) == 0)
+ return i;
}
- if (n < ST_SENSORS_MAX_4WAI)
- break;
- }
- if (i == num_sensors_list) {
- dev_err(&indio_dev->dev, "device name %s not recognized.\n",
- indio_dev->name);
- return -ENODEV;
}
- err = st_sensors_init_interface_mode(indio_dev, &sensor_settings[i]);
- if (err < 0)
- return err;
+ return -ENODEV;
+}
+EXPORT_SYMBOL(st_sensors_get_settings_index);
- if (sensor_settings[i].wai_addr) {
- err = sdata->tf->read_byte(&sdata->tb, sdata->dev,
- sensor_settings[i].wai_addr, &wai);
+/*
+ * st_sensors_verify_id() - verify sensor ID (WhoAmI) is matching with the
+ * expected value
+ * @indio_dev: IIO device reference.
+ *
+ * Return: 0 on success (valid sensor ID), else a negative error code.
+ */
+int st_sensors_verify_id(struct iio_dev *indio_dev)
+{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ int wai, err;
+
+ if (sdata->sensor_settings->wai_addr) {
+ err = regmap_read(sdata->regmap,
+ sdata->sensor_settings->wai_addr, &wai);
if (err < 0) {
dev_err(&indio_dev->dev,
"failed to read Who-Am-I register.\n");
return err;
}
- if (sensor_settings[i].wai != wai) {
+ if (sdata->sensor_settings->wai != wai) {
dev_err(&indio_dev->dev,
"%s: WhoAmI mismatch (0x%x).\n",
indio_dev->name, wai);
@@ -680,12 +651,9 @@
}
}
- sdata->sensor_settings =
- (struct st_sensor_settings *)&sensor_settings[i];
-
- return i;
+ return 0;
}
-EXPORT_SYMBOL(st_sensors_check_device_support);
+EXPORT_SYMBOL(st_sensors_verify_id);
ssize_t st_sensors_sysfs_sampling_frequency_avail(struct device *dev,
struct device_attribute *attr, char *buf)
diff --git a/drivers/iio/common/st_sensors/st_sensors_i2c.c b/drivers/iio/common/st_sensors/st_sensors_i2c.c
index b81e48e..aa89d54 100644
--- a/drivers/iio/common/st_sensors/st_sensors_i2c.c
+++ b/drivers/iio/common/st_sensors/st_sensors_i2c.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics sensors i2c library driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -14,68 +13,58 @@
#include <linux/iio/iio.h>
#include <linux/of_device.h>
#include <linux/acpi.h>
+#include <linux/regmap.h>
#include <linux/iio/common/st_sensors_i2c.h>
#define ST_SENSORS_I2C_MULTIREAD 0x80
-static unsigned int st_sensors_i2c_get_irq(struct iio_dev *indio_dev)
-{
- struct st_sensor_data *sdata = iio_priv(indio_dev);
-
- return to_i2c_client(sdata->dev)->irq;
-}
-
-static int st_sensors_i2c_read_byte(struct st_sensor_transfer_buffer *tb,
- struct device *dev, u8 reg_addr, u8 *res_byte)
-{
- int err;
-
- err = i2c_smbus_read_byte_data(to_i2c_client(dev), reg_addr);
- if (err < 0)
- goto st_accel_i2c_read_byte_error;
-
- *res_byte = err & 0xff;
-
-st_accel_i2c_read_byte_error:
- return err < 0 ? err : 0;
-}
-
-static int st_sensors_i2c_read_multiple_byte(
- struct st_sensor_transfer_buffer *tb, struct device *dev,
- u8 reg_addr, int len, u8 *data, bool multiread_bit)
-{
- if (multiread_bit)
- reg_addr |= ST_SENSORS_I2C_MULTIREAD;
-
- return i2c_smbus_read_i2c_block_data_or_emulated(to_i2c_client(dev),
- reg_addr, len, data);
-}
-
-static int st_sensors_i2c_write_byte(struct st_sensor_transfer_buffer *tb,
- struct device *dev, u8 reg_addr, u8 data)
-{
- return i2c_smbus_write_byte_data(to_i2c_client(dev), reg_addr, data);
-}
-
-static const struct st_sensor_transfer_function st_sensors_tf_i2c = {
- .read_byte = st_sensors_i2c_read_byte,
- .write_byte = st_sensors_i2c_write_byte,
- .read_multiple_byte = st_sensors_i2c_read_multiple_byte,
+static const struct regmap_config st_sensors_i2c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
};
-void st_sensors_i2c_configure(struct iio_dev *indio_dev,
- struct i2c_client *client, struct st_sensor_data *sdata)
+static const struct regmap_config st_sensors_i2c_regmap_multiread_bit_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = ST_SENSORS_I2C_MULTIREAD,
+};
+
+/*
+ * st_sensors_i2c_configure() - configure I2C interface
+ * @indio_dev: IIO device reference.
+ * @client: i2c client reference.
+ *
+ * Return: 0 on success, else a negative error code.
+ */
+int st_sensors_i2c_configure(struct iio_dev *indio_dev,
+ struct i2c_client *client)
{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ const struct regmap_config *config;
+
+ if (sdata->sensor_settings->multi_read_bit)
+ config = &st_sensors_i2c_regmap_multiread_bit_config;
+ else
+ config = &st_sensors_i2c_regmap_config;
+
+ sdata->regmap = devm_regmap_init_i2c(client, config);
+ if (IS_ERR(sdata->regmap)) {
+ dev_err(&client->dev, "Failed to register i2c regmap (%d)\n",
+ (int)PTR_ERR(sdata->regmap));
+ return PTR_ERR(sdata->regmap);
+ }
+
i2c_set_clientdata(client, indio_dev);
indio_dev->dev.parent = &client->dev;
indio_dev->name = client->name;
sdata->dev = &client->dev;
- sdata->tf = &st_sensors_tf_i2c;
- sdata->get_irq_data_ready = st_sensors_i2c_get_irq;
+ sdata->irq = client->irq;
+
+ return 0;
}
EXPORT_SYMBOL(st_sensors_i2c_configure);
diff --git a/drivers/iio/common/st_sensors/st_sensors_spi.c b/drivers/iio/common/st_sensors/st_sensors_spi.c
index 5b37737..2262f81 100644
--- a/drivers/iio/common/st_sensors/st_sensors_spi.c
+++ b/drivers/iio/common/st_sensors/st_sensors_spi.c
@@ -1,119 +1,118 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics sensors spi library driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>
+#include <linux/regmap.h>
#include <linux/iio/common/st_sensors_spi.h>
-
+#include "st_sensors_core.h"
#define ST_SENSORS_SPI_MULTIREAD 0xc0
-#define ST_SENSORS_SPI_READ 0x80
-static unsigned int st_sensors_spi_get_irq(struct iio_dev *indio_dev)
-{
- struct st_sensor_data *sdata = iio_priv(indio_dev);
-
- return to_spi_device(sdata->dev)->irq;
-}
-
-static int st_sensors_spi_read(struct st_sensor_transfer_buffer *tb,
- struct device *dev, u8 reg_addr, int len, u8 *data, bool multiread_bit)
-{
- int err;
-
- struct spi_transfer xfers[] = {
- {
- .tx_buf = tb->tx_buf,
- .bits_per_word = 8,
- .len = 1,
- },
- {
- .rx_buf = tb->rx_buf,
- .bits_per_word = 8,
- .len = len,
- }
- };
-
- mutex_lock(&tb->buf_lock);
- if ((multiread_bit) && (len > 1))
- tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_MULTIREAD;
- else
- tb->tx_buf[0] = reg_addr | ST_SENSORS_SPI_READ;
-
- err = spi_sync_transfer(to_spi_device(dev), xfers, ARRAY_SIZE(xfers));
- if (err)
- goto acc_spi_read_error;
-
- memcpy(data, tb->rx_buf, len);
- mutex_unlock(&tb->buf_lock);
- return len;
-
-acc_spi_read_error:
- mutex_unlock(&tb->buf_lock);
- return err;
-}
-
-static int st_sensors_spi_read_byte(struct st_sensor_transfer_buffer *tb,
- struct device *dev, u8 reg_addr, u8 *res_byte)
-{
- return st_sensors_spi_read(tb, dev, reg_addr, 1, res_byte, false);
-}
-
-static int st_sensors_spi_read_multiple_byte(
- struct st_sensor_transfer_buffer *tb, struct device *dev,
- u8 reg_addr, int len, u8 *data, bool multiread_bit)
-{
- return st_sensors_spi_read(tb, dev, reg_addr, len, data, multiread_bit);
-}
-
-static int st_sensors_spi_write_byte(struct st_sensor_transfer_buffer *tb,
- struct device *dev, u8 reg_addr, u8 data)
-{
- int err;
-
- struct spi_transfer xfers = {
- .tx_buf = tb->tx_buf,
- .bits_per_word = 8,
- .len = 2,
- };
-
- mutex_lock(&tb->buf_lock);
- tb->tx_buf[0] = reg_addr;
- tb->tx_buf[1] = data;
-
- err = spi_sync_transfer(to_spi_device(dev), &xfers, 1);
- mutex_unlock(&tb->buf_lock);
-
- return err;
-}
-
-static const struct st_sensor_transfer_function st_sensors_tf_spi = {
- .read_byte = st_sensors_spi_read_byte,
- .write_byte = st_sensors_spi_write_byte,
- .read_multiple_byte = st_sensors_spi_read_multiple_byte,
+static const struct regmap_config st_sensors_spi_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
};
-void st_sensors_spi_configure(struct iio_dev *indio_dev,
- struct spi_device *spi, struct st_sensor_data *sdata)
+static const struct regmap_config st_sensors_spi_regmap_multiread_bit_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .read_flag_mask = ST_SENSORS_SPI_MULTIREAD,
+};
+
+/*
+ * st_sensors_is_spi_3_wire() - check if SPI 3-wire mode has been selected
+ * @spi: spi device reference.
+ *
+ * Return: true if SPI 3-wire mode is selected, false otherwise.
+ */
+static bool st_sensors_is_spi_3_wire(struct spi_device *spi)
{
+ struct device_node *np = spi->dev.of_node;
+ struct st_sensors_platform_data *pdata;
+
+ pdata = (struct st_sensors_platform_data *)spi->dev.platform_data;
+ if ((np && of_property_read_bool(np, "spi-3wire")) ||
+ (pdata && pdata->spi_3wire)) {
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * st_sensors_configure_spi_3_wire() - configure SPI 3-wire if needed
+ * @spi: spi device reference.
+ * @settings: sensor specific settings reference.
+ *
+ * Return: 0 on success, else a negative error code.
+ */
+static int st_sensors_configure_spi_3_wire(struct spi_device *spi,
+ struct st_sensor_settings *settings)
+{
+ if (settings->sim.addr) {
+ u8 buffer[] = {
+ settings->sim.addr,
+ settings->sim.value
+ };
+
+ return spi_write(spi, buffer, 2);
+ }
+
+ return 0;
+}
+
+/*
+ * st_sensors_spi_configure() - configure SPI interface
+ * @indio_dev: IIO device reference.
+ * @spi: spi device reference.
+ *
+ * Return: 0 on success, else a negative error code.
+ */
+int st_sensors_spi_configure(struct iio_dev *indio_dev,
+ struct spi_device *spi)
+{
+ struct st_sensor_data *sdata = iio_priv(indio_dev);
+ const struct regmap_config *config;
+ int err;
+
+ if (st_sensors_is_spi_3_wire(spi)) {
+ err = st_sensors_configure_spi_3_wire(spi,
+ sdata->sensor_settings);
+ if (err < 0)
+ return err;
+ }
+
+ if (sdata->sensor_settings->multi_read_bit)
+ config = &st_sensors_spi_regmap_multiread_bit_config;
+ else
+ config = &st_sensors_spi_regmap_config;
+
+ sdata->regmap = devm_regmap_init_spi(spi, config);
+ if (IS_ERR(sdata->regmap)) {
+ dev_err(&spi->dev, "Failed to register spi regmap (%d)\n",
+ (int)PTR_ERR(sdata->regmap));
+ return PTR_ERR(sdata->regmap);
+ }
+
spi_set_drvdata(spi, indio_dev);
indio_dev->dev.parent = &spi->dev;
indio_dev->name = spi->modalias;
sdata->dev = &spi->dev;
- sdata->tf = &st_sensors_tf_spi;
- sdata->get_irq_data_ready = st_sensors_spi_get_irq;
+ sdata->irq = spi->irq;
+
+ return 0;
}
EXPORT_SYMBOL(st_sensors_spi_configure);
diff --git a/drivers/iio/common/st_sensors/st_sensors_trigger.c b/drivers/iio/common/st_sensors/st_sensors_trigger.c
index fdcc5a8..4a2efa0 100644
--- a/drivers/iio/common/st_sensors/st_sensors_trigger.c
+++ b/drivers/iio/common/st_sensors/st_sensors_trigger.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics sensors trigger library driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -14,6 +13,7 @@
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
#include <linux/interrupt.h>
+#include <linux/regmap.h>
#include <linux/iio/common/st_sensors.h>
#include "st_sensors_core.h"
@@ -27,8 +27,7 @@
static int st_sensors_new_samples_available(struct iio_dev *indio_dev,
struct st_sensor_data *sdata)
{
- u8 status;
- int ret;
+ int ret, status;
/* How would I know if I can't check it? */
if (!sdata->sensor_settings->drdy_irq.stat_drdy.addr)
@@ -38,9 +37,9 @@
if (!indio_dev->active_scan_mask)
return 0;
- ret = sdata->tf->read_byte(&sdata->tb, sdata->dev,
- sdata->sensor_settings->drdy_irq.stat_drdy.addr,
- &status);
+ ret = regmap_read(sdata->regmap,
+ sdata->sensor_settings->drdy_irq.stat_drdy.addr,
+ &status);
if (ret < 0) {
dev_err(sdata->dev,
"error checking samples available\n");
@@ -104,7 +103,7 @@
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.
@@ -122,9 +121,9 @@
int st_sensors_allocate_trigger(struct iio_dev *indio_dev,
const struct iio_trigger_ops *trigger_ops)
{
- int err, irq;
struct st_sensor_data *sdata = iio_priv(indio_dev);
unsigned long irq_trig;
+ int err;
sdata->trig = iio_trigger_alloc("%s-trigger", indio_dev->name);
if (sdata->trig == NULL) {
@@ -136,8 +135,7 @@
sdata->trig->ops = trigger_ops;
sdata->trig->dev.parent = sdata->dev;
- irq = sdata->get_irq_data_ready(indio_dev);
- irq_trig = irqd_get_trigger_type(irq_get_irq_data(irq));
+ irq_trig = irqd_get_trigger_type(irq_get_irq_data(sdata->irq));
/*
* If the IRQ is triggered on falling edge, we need to mark the
* interrupt as active low, if the hardware supports this.
@@ -148,7 +146,7 @@
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;
@@ -207,12 +205,12 @@
sdata->sensor_settings->drdy_irq.stat_drdy.addr)
irq_trig |= IRQF_SHARED;
- err = request_threaded_irq(sdata->get_irq_data_ready(indio_dev),
- st_sensors_irq_handler,
- st_sensors_irq_thread,
- irq_trig,
- sdata->trig->name,
- sdata->trig);
+ err = request_threaded_irq(sdata->irq,
+ st_sensors_irq_handler,
+ st_sensors_irq_thread,
+ irq_trig,
+ sdata->trig->name,
+ sdata->trig);
if (err) {
dev_err(&indio_dev->dev, "failed to request trigger IRQ.\n");
goto iio_trigger_free;
@@ -228,7 +226,7 @@
return 0;
iio_trigger_register_error:
- free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+ free_irq(sdata->irq, sdata->trig);
iio_trigger_free:
iio_trigger_free(sdata->trig);
return err;
@@ -240,7 +238,7 @@
struct st_sensor_data *sdata = iio_priv(indio_dev);
iio_trigger_unregister(sdata->trig);
- free_irq(sdata->get_irq_data_ready(indio_dev), sdata->trig);
+ free_irq(sdata->irq, sdata->trig);
iio_trigger_free(sdata->trig);
}
EXPORT_SYMBOL(st_sensors_deallocate_trigger);
diff --git a/drivers/iio/counter/104-quad-8.c b/drivers/iio/counter/104-quad-8.c
deleted file mode 100644
index 92be8d0..0000000
--- a/drivers/iio/counter/104-quad-8.c
+++ /dev/null
@@ -1,631 +0,0 @@
-/*
- * IIO driver for the ACCES 104-QUAD-8
- * Copyright (C) 2016 William Breathitt Gray
- *
- * 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.
- *
- * 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.
- *
- * This driver supports the ACCES 104-QUAD-8 and ACCES 104-QUAD-4.
- */
-#include <linux/bitops.h>
-#include <linux/device.h>
-#include <linux/errno.h>
-#include <linux/iio/iio.h>
-#include <linux/iio/types.h>
-#include <linux/io.h>
-#include <linux/ioport.h>
-#include <linux/isa.h>
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/types.h>
-
-#define QUAD8_EXTENT 32
-
-static unsigned int base[max_num_isa_dev(QUAD8_EXTENT)];
-static unsigned int num_quad8;
-module_param_array(base, uint, &num_quad8, 0);
-MODULE_PARM_DESC(base, "ACCES 104-QUAD-8 base addresses");
-
-#define QUAD8_NUM_COUNTERS 8
-
-/**
- * struct quad8_iio - IIO device private data structure
- * @preset: array of preset values
- * @count_mode: array of count mode configurations
- * @quadrature_mode: array of quadrature mode configurations
- * @quadrature_scale: array of quadrature mode scale configurations
- * @ab_enable: array of A and B inputs enable configurations
- * @preset_enable: array of set_to_preset_on_index attribute configurations
- * @synchronous_mode: array of index function synchronous mode configurations
- * @index_polarity: array of index function polarity configurations
- * @base: base port address of the IIO device
- */
-struct quad8_iio {
- unsigned int preset[QUAD8_NUM_COUNTERS];
- unsigned int count_mode[QUAD8_NUM_COUNTERS];
- unsigned int quadrature_mode[QUAD8_NUM_COUNTERS];
- unsigned int quadrature_scale[QUAD8_NUM_COUNTERS];
- unsigned int ab_enable[QUAD8_NUM_COUNTERS];
- unsigned int preset_enable[QUAD8_NUM_COUNTERS];
- unsigned int synchronous_mode[QUAD8_NUM_COUNTERS];
- unsigned int index_polarity[QUAD8_NUM_COUNTERS];
- unsigned int base;
-};
-
-#define QUAD8_REG_CHAN_OP 0x11
-#define QUAD8_REG_INDEX_INPUT_LEVELS 0x16
-/* Borrow Toggle flip-flop */
-#define QUAD8_FLAG_BT BIT(0)
-/* Carry Toggle flip-flop */
-#define QUAD8_FLAG_CT BIT(1)
-/* Error flag */
-#define QUAD8_FLAG_E BIT(4)
-/* Up/Down flag */
-#define QUAD8_FLAG_UD BIT(5)
-/* Reset and Load Signal Decoders */
-#define QUAD8_CTR_RLD 0x00
-/* Counter Mode Register */
-#define QUAD8_CTR_CMR 0x20
-/* Input / Output Control Register */
-#define QUAD8_CTR_IOR 0x40
-/* Index Control Register */
-#define QUAD8_CTR_IDR 0x60
-/* Reset Byte Pointer (three byte data pointer) */
-#define QUAD8_RLD_RESET_BP 0x01
-/* Reset Counter */
-#define QUAD8_RLD_RESET_CNTR 0x02
-/* Reset Borrow Toggle, Carry Toggle, Compare Toggle, and Sign flags */
-#define QUAD8_RLD_RESET_FLAGS 0x04
-/* Reset Error flag */
-#define QUAD8_RLD_RESET_E 0x06
-/* Preset Register to Counter */
-#define QUAD8_RLD_PRESET_CNTR 0x08
-/* Transfer Counter to Output Latch */
-#define QUAD8_RLD_CNTR_OUT 0x10
-#define QUAD8_CHAN_OP_ENABLE_COUNTERS 0x00
-#define QUAD8_CHAN_OP_RESET_COUNTERS 0x01
-
-static int quad8_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, int *val, int *val2, long mask)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- const int base_offset = priv->base + 2 * chan->channel;
- unsigned int flags;
- unsigned int borrow;
- unsigned int carry;
- int i;
-
- switch (mask) {
- case IIO_CHAN_INFO_RAW:
- if (chan->type == IIO_INDEX) {
- *val = !!(inb(priv->base + QUAD8_REG_INDEX_INPUT_LEVELS)
- & BIT(chan->channel));
- return IIO_VAL_INT;
- }
-
- flags = inb(base_offset + 1);
- borrow = flags & QUAD8_FLAG_BT;
- carry = !!(flags & QUAD8_FLAG_CT);
-
- /* Borrow XOR Carry effectively doubles count range */
- *val = (borrow ^ carry) << 24;
-
- /* Reset Byte Pointer; transfer Counter to Output Latch */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP | QUAD8_RLD_CNTR_OUT,
- base_offset + 1);
-
- for (i = 0; i < 3; i++)
- *val |= (unsigned int)inb(base_offset) << (8 * i);
-
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_ENABLE:
- *val = priv->ab_enable[chan->channel];
- return IIO_VAL_INT;
- case IIO_CHAN_INFO_SCALE:
- *val = 1;
- *val2 = priv->quadrature_scale[chan->channel];
- return IIO_VAL_FRACTIONAL_LOG2;
- }
-
- return -EINVAL;
-}
-
-static int quad8_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan, int val, int val2, long mask)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- const int base_offset = priv->base + 2 * chan->channel;
- int i;
- unsigned int ior_cfg;
-
- switch (mask) {
- case IIO_CHAN_INFO_RAW:
- if (chan->type == IIO_INDEX)
- return -EINVAL;
-
- /* Only 24-bit values are supported */
- if ((unsigned int)val > 0xFFFFFF)
- return -EINVAL;
-
- /* Reset Byte Pointer */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
-
- /* Counter can only be set via Preset Register */
- for (i = 0; i < 3; i++)
- outb(val >> (8 * i), base_offset);
-
- /* Transfer Preset Register to Counter */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_PRESET_CNTR, base_offset + 1);
-
- /* Reset Byte Pointer */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
-
- /* Set Preset Register back to original value */
- val = priv->preset[chan->channel];
- for (i = 0; i < 3; i++)
- outb(val >> (8 * i), base_offset);
-
- /* Reset Borrow, Carry, Compare, and Sign flags */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
- /* Reset Error flag */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
-
- return 0;
- case IIO_CHAN_INFO_ENABLE:
- /* only boolean values accepted */
- if (val < 0 || val > 1)
- return -EINVAL;
-
- priv->ab_enable[chan->channel] = val;
-
- ior_cfg = val | priv->preset_enable[chan->channel] << 1;
-
- /* Load I/O control configuration */
- outb(QUAD8_CTR_IOR | ior_cfg, base_offset + 1);
-
- return 0;
- case IIO_CHAN_INFO_SCALE:
- /* Quadrature scaling only available in quadrature mode */
- if (!priv->quadrature_mode[chan->channel] && (val2 || val != 1))
- return -EINVAL;
-
- /* Only three gain states (1, 0.5, 0.25) */
- if (val == 1 && !val2)
- priv->quadrature_scale[chan->channel] = 0;
- else if (!val)
- switch (val2) {
- case 500000:
- priv->quadrature_scale[chan->channel] = 1;
- break;
- case 250000:
- priv->quadrature_scale[chan->channel] = 2;
- break;
- default:
- return -EINVAL;
- }
- else
- return -EINVAL;
-
- return 0;
- }
-
- return -EINVAL;
-}
-
-static const struct iio_info quad8_info = {
- .read_raw = quad8_read_raw,
- .write_raw = quad8_write_raw
-};
-
-static ssize_t quad8_read_preset(struct iio_dev *indio_dev, uintptr_t private,
- const struct iio_chan_spec *chan, char *buf)
-{
- const struct quad8_iio *const priv = iio_priv(indio_dev);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset[chan->channel]);
-}
-
-static ssize_t quad8_write_preset(struct iio_dev *indio_dev, uintptr_t private,
- const struct iio_chan_spec *chan, const char *buf, size_t len)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- const int base_offset = priv->base + 2 * chan->channel;
- unsigned int preset;
- int ret;
- int i;
-
- ret = kstrtouint(buf, 0, &preset);
- if (ret)
- return ret;
-
- /* Only 24-bit values are supported */
- if (preset > 0xFFFFFF)
- return -EINVAL;
-
- priv->preset[chan->channel] = preset;
-
- /* Reset Byte Pointer */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
-
- /* Set Preset Register */
- for (i = 0; i < 3; i++)
- outb(preset >> (8 * i), base_offset);
-
- return len;
-}
-
-static ssize_t quad8_read_set_to_preset_on_index(struct iio_dev *indio_dev,
- uintptr_t private, const struct iio_chan_spec *chan, char *buf)
-{
- const struct quad8_iio *const priv = iio_priv(indio_dev);
-
- return snprintf(buf, PAGE_SIZE, "%u\n",
- !priv->preset_enable[chan->channel]);
-}
-
-static ssize_t quad8_write_set_to_preset_on_index(struct iio_dev *indio_dev,
- uintptr_t private, const struct iio_chan_spec *chan, const char *buf,
- size_t len)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- const int base_offset = priv->base + 2 * chan->channel + 1;
- bool preset_enable;
- int ret;
- unsigned int ior_cfg;
-
- ret = kstrtobool(buf, &preset_enable);
- if (ret)
- return ret;
-
- /* Preset enable is active low in Input/Output Control register */
- preset_enable = !preset_enable;
-
- priv->preset_enable[chan->channel] = preset_enable;
-
- ior_cfg = priv->ab_enable[chan->channel] |
- (unsigned int)preset_enable << 1;
-
- /* Load I/O control configuration to Input / Output Control Register */
- outb(QUAD8_CTR_IOR | ior_cfg, base_offset);
-
- return len;
-}
-
-static const char *const quad8_noise_error_states[] = {
- "No excessive noise is present at the count inputs",
- "Excessive noise is present at the count inputs"
-};
-
-static int quad8_get_noise_error(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- const int base_offset = priv->base + 2 * chan->channel + 1;
-
- return !!(inb(base_offset) & QUAD8_FLAG_E);
-}
-
-static const struct iio_enum quad8_noise_error_enum = {
- .items = quad8_noise_error_states,
- .num_items = ARRAY_SIZE(quad8_noise_error_states),
- .get = quad8_get_noise_error
-};
-
-static const char *const quad8_count_direction_states[] = {
- "down",
- "up"
-};
-
-static int quad8_get_count_direction(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- const int base_offset = priv->base + 2 * chan->channel + 1;
-
- return !!(inb(base_offset) & QUAD8_FLAG_UD);
-}
-
-static const struct iio_enum quad8_count_direction_enum = {
- .items = quad8_count_direction_states,
- .num_items = ARRAY_SIZE(quad8_count_direction_states),
- .get = quad8_get_count_direction
-};
-
-static const char *const quad8_count_modes[] = {
- "normal",
- "range limit",
- "non-recycle",
- "modulo-n"
-};
-
-static int quad8_set_count_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan, unsigned int count_mode)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- unsigned int mode_cfg = count_mode << 1;
- const int base_offset = priv->base + 2 * chan->channel + 1;
-
- priv->count_mode[chan->channel] = count_mode;
-
- /* Add quadrature mode configuration */
- if (priv->quadrature_mode[chan->channel])
- mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
-
- /* Load mode configuration to Counter Mode Register */
- outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
-
- return 0;
-}
-
-static int quad8_get_count_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- const struct quad8_iio *const priv = iio_priv(indio_dev);
-
- return priv->count_mode[chan->channel];
-}
-
-static const struct iio_enum quad8_count_mode_enum = {
- .items = quad8_count_modes,
- .num_items = ARRAY_SIZE(quad8_count_modes),
- .set = quad8_set_count_mode,
- .get = quad8_get_count_mode
-};
-
-static const char *const quad8_synchronous_modes[] = {
- "non-synchronous",
- "synchronous"
-};
-
-static int quad8_set_synchronous_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan, unsigned int synchronous_mode)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- const unsigned int idr_cfg = synchronous_mode |
- priv->index_polarity[chan->channel] << 1;
- const int base_offset = priv->base + 2 * chan->channel + 1;
-
- /* Index function must be non-synchronous in non-quadrature mode */
- if (synchronous_mode && !priv->quadrature_mode[chan->channel])
- return -EINVAL;
-
- priv->synchronous_mode[chan->channel] = synchronous_mode;
-
- /* Load Index Control configuration to Index Control Register */
- outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
-
- return 0;
-}
-
-static int quad8_get_synchronous_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- const struct quad8_iio *const priv = iio_priv(indio_dev);
-
- return priv->synchronous_mode[chan->channel];
-}
-
-static const struct iio_enum quad8_synchronous_mode_enum = {
- .items = quad8_synchronous_modes,
- .num_items = ARRAY_SIZE(quad8_synchronous_modes),
- .set = quad8_set_synchronous_mode,
- .get = quad8_get_synchronous_mode
-};
-
-static const char *const quad8_quadrature_modes[] = {
- "non-quadrature",
- "quadrature"
-};
-
-static int quad8_set_quadrature_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan, unsigned int quadrature_mode)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- unsigned int mode_cfg = priv->count_mode[chan->channel] << 1;
- const int base_offset = priv->base + 2 * chan->channel + 1;
-
- if (quadrature_mode)
- mode_cfg |= (priv->quadrature_scale[chan->channel] + 1) << 3;
- else {
- /* Quadrature scaling only available in quadrature mode */
- priv->quadrature_scale[chan->channel] = 0;
-
- /* Synchronous function not supported in non-quadrature mode */
- if (priv->synchronous_mode[chan->channel])
- quad8_set_synchronous_mode(indio_dev, chan, 0);
- }
-
- priv->quadrature_mode[chan->channel] = quadrature_mode;
-
- /* Load mode configuration to Counter Mode Register */
- outb(QUAD8_CTR_CMR | mode_cfg, base_offset);
-
- return 0;
-}
-
-static int quad8_get_quadrature_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- const struct quad8_iio *const priv = iio_priv(indio_dev);
-
- return priv->quadrature_mode[chan->channel];
-}
-
-static const struct iio_enum quad8_quadrature_mode_enum = {
- .items = quad8_quadrature_modes,
- .num_items = ARRAY_SIZE(quad8_quadrature_modes),
- .set = quad8_set_quadrature_mode,
- .get = quad8_get_quadrature_mode
-};
-
-static const char *const quad8_index_polarity_modes[] = {
- "negative",
- "positive"
-};
-
-static int quad8_set_index_polarity(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan, unsigned int index_polarity)
-{
- struct quad8_iio *const priv = iio_priv(indio_dev);
- const unsigned int idr_cfg = priv->synchronous_mode[chan->channel] |
- index_polarity << 1;
- const int base_offset = priv->base + 2 * chan->channel + 1;
-
- priv->index_polarity[chan->channel] = index_polarity;
-
- /* Load Index Control configuration to Index Control Register */
- outb(QUAD8_CTR_IDR | idr_cfg, base_offset);
-
- return 0;
-}
-
-static int quad8_get_index_polarity(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- const struct quad8_iio *const priv = iio_priv(indio_dev);
-
- return priv->index_polarity[chan->channel];
-}
-
-static const struct iio_enum quad8_index_polarity_enum = {
- .items = quad8_index_polarity_modes,
- .num_items = ARRAY_SIZE(quad8_index_polarity_modes),
- .set = quad8_set_index_polarity,
- .get = quad8_get_index_polarity
-};
-
-static const struct iio_chan_spec_ext_info quad8_count_ext_info[] = {
- {
- .name = "preset",
- .shared = IIO_SEPARATE,
- .read = quad8_read_preset,
- .write = quad8_write_preset
- },
- {
- .name = "set_to_preset_on_index",
- .shared = IIO_SEPARATE,
- .read = quad8_read_set_to_preset_on_index,
- .write = quad8_write_set_to_preset_on_index
- },
- IIO_ENUM("noise_error", IIO_SEPARATE, &quad8_noise_error_enum),
- IIO_ENUM_AVAILABLE("noise_error", &quad8_noise_error_enum),
- IIO_ENUM("count_direction", IIO_SEPARATE, &quad8_count_direction_enum),
- IIO_ENUM_AVAILABLE("count_direction", &quad8_count_direction_enum),
- IIO_ENUM("count_mode", IIO_SEPARATE, &quad8_count_mode_enum),
- IIO_ENUM_AVAILABLE("count_mode", &quad8_count_mode_enum),
- IIO_ENUM("quadrature_mode", IIO_SEPARATE, &quad8_quadrature_mode_enum),
- IIO_ENUM_AVAILABLE("quadrature_mode", &quad8_quadrature_mode_enum),
- {}
-};
-
-static const struct iio_chan_spec_ext_info quad8_index_ext_info[] = {
- IIO_ENUM("synchronous_mode", IIO_SEPARATE,
- &quad8_synchronous_mode_enum),
- IIO_ENUM_AVAILABLE("synchronous_mode", &quad8_synchronous_mode_enum),
- IIO_ENUM("index_polarity", IIO_SEPARATE, &quad8_index_polarity_enum),
- IIO_ENUM_AVAILABLE("index_polarity", &quad8_index_polarity_enum),
- {}
-};
-
-#define QUAD8_COUNT_CHAN(_chan) { \
- .type = IIO_COUNT, \
- .channel = (_chan), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
- BIT(IIO_CHAN_INFO_ENABLE) | BIT(IIO_CHAN_INFO_SCALE), \
- .ext_info = quad8_count_ext_info, \
- .indexed = 1 \
-}
-
-#define QUAD8_INDEX_CHAN(_chan) { \
- .type = IIO_INDEX, \
- .channel = (_chan), \
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
- .ext_info = quad8_index_ext_info, \
- .indexed = 1 \
-}
-
-static const struct iio_chan_spec quad8_channels[] = {
- QUAD8_COUNT_CHAN(0), QUAD8_INDEX_CHAN(0),
- QUAD8_COUNT_CHAN(1), QUAD8_INDEX_CHAN(1),
- QUAD8_COUNT_CHAN(2), QUAD8_INDEX_CHAN(2),
- QUAD8_COUNT_CHAN(3), QUAD8_INDEX_CHAN(3),
- QUAD8_COUNT_CHAN(4), QUAD8_INDEX_CHAN(4),
- QUAD8_COUNT_CHAN(5), QUAD8_INDEX_CHAN(5),
- QUAD8_COUNT_CHAN(6), QUAD8_INDEX_CHAN(6),
- QUAD8_COUNT_CHAN(7), QUAD8_INDEX_CHAN(7)
-};
-
-static int quad8_probe(struct device *dev, unsigned int id)
-{
- struct iio_dev *indio_dev;
- struct quad8_iio *priv;
- int i, j;
- unsigned int base_offset;
-
- indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
- if (!indio_dev)
- return -ENOMEM;
-
- if (!devm_request_region(dev, base[id], QUAD8_EXTENT,
- dev_name(dev))) {
- dev_err(dev, "Unable to lock port addresses (0x%X-0x%X)\n",
- base[id], base[id] + QUAD8_EXTENT);
- return -EBUSY;
- }
-
- indio_dev->info = &quad8_info;
- indio_dev->modes = INDIO_DIRECT_MODE;
- indio_dev->num_channels = ARRAY_SIZE(quad8_channels);
- indio_dev->channels = quad8_channels;
- indio_dev->name = dev_name(dev);
- indio_dev->dev.parent = dev;
-
- priv = iio_priv(indio_dev);
- priv->base = base[id];
-
- /* Reset all counters and disable interrupt function */
- outb(QUAD8_CHAN_OP_RESET_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
- /* Set initial configuration for all counters */
- for (i = 0; i < QUAD8_NUM_COUNTERS; i++) {
- base_offset = base[id] + 2 * i;
- /* Reset Byte Pointer */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_BP, base_offset + 1);
- /* Reset Preset Register */
- for (j = 0; j < 3; j++)
- outb(0x00, base_offset);
- /* Reset Borrow, Carry, Compare, and Sign flags */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_FLAGS, base_offset + 1);
- /* Reset Error flag */
- outb(QUAD8_CTR_RLD | QUAD8_RLD_RESET_E, base_offset + 1);
- /* Binary encoding; Normal count; non-quadrature mode */
- outb(QUAD8_CTR_CMR, base_offset + 1);
- /* Disable A and B inputs; preset on index; FLG1 as Carry */
- outb(QUAD8_CTR_IOR, base_offset + 1);
- /* Disable index function; negative index polarity */
- outb(QUAD8_CTR_IDR, base_offset + 1);
- }
- /* Enable all counters */
- outb(QUAD8_CHAN_OP_ENABLE_COUNTERS, base[id] + QUAD8_REG_CHAN_OP);
-
- return devm_iio_device_register(dev, indio_dev);
-}
-
-static struct isa_driver quad8_driver = {
- .probe = quad8_probe,
- .driver = {
- .name = "104-quad-8"
- }
-};
-
-module_isa_driver(quad8_driver, num_quad8);
-
-MODULE_AUTHOR("William Breathitt Gray <vilhelm.gray@gmail.com>");
-MODULE_DESCRIPTION("ACCES 104-QUAD-8 IIO driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/counter/Kconfig b/drivers/iio/counter/Kconfig
deleted file mode 100644
index bf1e559..0000000
--- a/drivers/iio/counter/Kconfig
+++ /dev/null
@@ -1,34 +0,0 @@
-#
-# Counter devices
-#
-# When adding new entries keep the list in alphabetical order
-
-menu "Counters"
-
-config 104_QUAD_8
- tristate "ACCES 104-QUAD-8 driver"
- depends on PC104 && X86
- select ISA_BUS_API
- help
- Say yes here to build support for the ACCES 104-QUAD-8 quadrature
- encoder counter/interface device family (104-QUAD-8, 104-QUAD-4).
-
- Performing a write to a counter's IIO_CHAN_INFO_RAW sets the counter and
- also clears the counter's respective error flag. Although the counters
- have a 25-bit range, only the lower 24 bits may be set, either directly
- or via a counter's preset attribute. Interrupts are not supported by
- this driver.
-
- The base port addresses for the devices may be configured via the base
- array module parameter.
-
-config STM32_LPTIMER_CNT
- tristate "STM32 LP Timer encoder counter driver"
- depends on MFD_STM32_LPTIMER || COMPILE_TEST
- help
- Select this option to enable STM32 Low-Power Timer quadrature encoder
- and counter driver.
-
- To compile this driver as a module, choose M here: the
- module will be called stm32-lptimer-cnt.
-endmenu
diff --git a/drivers/iio/counter/Makefile b/drivers/iio/counter/Makefile
deleted file mode 100644
index 1b9a896..0000000
--- a/drivers/iio/counter/Makefile
+++ /dev/null
@@ -1,8 +0,0 @@
-#
-# Makefile for IIO counter devices
-#
-
-# When adding new entries keep the list in alphabetical order
-
-obj-$(CONFIG_104_QUAD_8) += 104-quad-8.o
-obj-$(CONFIG_STM32_LPTIMER_CNT) += stm32-lptimer-cnt.o
diff --git a/drivers/iio/counter/stm32-lptimer-cnt.c b/drivers/iio/counter/stm32-lptimer-cnt.c
deleted file mode 100644
index 42fb8ba..0000000
--- a/drivers/iio/counter/stm32-lptimer-cnt.c
+++ /dev/null
@@ -1,382 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * STM32 Low-Power Timer Encoder and Counter driver
- *
- * Copyright (C) STMicroelectronics 2017
- *
- * Author: Fabrice Gasnier <fabrice.gasnier@st.com>
- *
- * Inspired by 104-quad-8 and stm32-timer-trigger drivers.
- *
- */
-
-#include <linux/bitfield.h>
-#include <linux/iio/iio.h>
-#include <linux/mfd/stm32-lptimer.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-
-struct stm32_lptim_cnt {
- struct device *dev;
- struct regmap *regmap;
- struct clk *clk;
- u32 preset;
- u32 polarity;
- u32 quadrature_mode;
-};
-
-static int stm32_lptim_is_enabled(struct stm32_lptim_cnt *priv)
-{
- u32 val;
- int ret;
-
- ret = regmap_read(priv->regmap, STM32_LPTIM_CR, &val);
- if (ret)
- return ret;
-
- return FIELD_GET(STM32_LPTIM_ENABLE, val);
-}
-
-static int stm32_lptim_set_enable_state(struct stm32_lptim_cnt *priv,
- int enable)
-{
- int ret;
- u32 val;
-
- val = FIELD_PREP(STM32_LPTIM_ENABLE, enable);
- ret = regmap_write(priv->regmap, STM32_LPTIM_CR, val);
- if (ret)
- return ret;
-
- if (!enable) {
- clk_disable(priv->clk);
- return 0;
- }
-
- /* LP timer must be enabled before writing CMP & ARR */
- ret = regmap_write(priv->regmap, STM32_LPTIM_ARR, priv->preset);
- if (ret)
- return ret;
-
- ret = regmap_write(priv->regmap, STM32_LPTIM_CMP, 0);
- if (ret)
- return ret;
-
- /* ensure CMP & ARR registers are properly written */
- ret = regmap_read_poll_timeout(priv->regmap, STM32_LPTIM_ISR, val,
- (val & STM32_LPTIM_CMPOK_ARROK),
- 100, 1000);
- if (ret)
- return ret;
-
- ret = regmap_write(priv->regmap, STM32_LPTIM_ICR,
- STM32_LPTIM_CMPOKCF_ARROKCF);
- if (ret)
- return ret;
-
- ret = clk_enable(priv->clk);
- if (ret) {
- regmap_write(priv->regmap, STM32_LPTIM_CR, 0);
- return ret;
- }
-
- /* Start LP timer in continuous mode */
- return regmap_update_bits(priv->regmap, STM32_LPTIM_CR,
- STM32_LPTIM_CNTSTRT, STM32_LPTIM_CNTSTRT);
-}
-
-static int stm32_lptim_setup(struct stm32_lptim_cnt *priv, int enable)
-{
- u32 mask = STM32_LPTIM_ENC | STM32_LPTIM_COUNTMODE |
- STM32_LPTIM_CKPOL | STM32_LPTIM_PRESC;
- u32 val;
-
- /* Setup LP timer encoder/counter and polarity, without prescaler */
- if (priv->quadrature_mode)
- val = enable ? STM32_LPTIM_ENC : 0;
- else
- val = enable ? STM32_LPTIM_COUNTMODE : 0;
- val |= FIELD_PREP(STM32_LPTIM_CKPOL, enable ? priv->polarity : 0);
-
- return regmap_update_bits(priv->regmap, STM32_LPTIM_CFGR, mask, val);
-}
-
-static int stm32_lptim_write_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int val, int val2, long mask)
-{
- struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
- int ret;
-
- switch (mask) {
- case IIO_CHAN_INFO_ENABLE:
- if (val < 0 || val > 1)
- return -EINVAL;
-
- /* Check nobody uses the timer, or already disabled/enabled */
- ret = stm32_lptim_is_enabled(priv);
- if ((ret < 0) || (!ret && !val))
- return ret;
- if (val && ret)
- return -EBUSY;
-
- ret = stm32_lptim_setup(priv, val);
- if (ret)
- return ret;
- return stm32_lptim_set_enable_state(priv, val);
-
- default:
- return -EINVAL;
- }
-}
-
-static int stm32_lptim_read_raw(struct iio_dev *indio_dev,
- struct iio_chan_spec const *chan,
- int *val, int *val2, long mask)
-{
- struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
- u32 dat;
- int ret;
-
- switch (mask) {
- case IIO_CHAN_INFO_RAW:
- ret = regmap_read(priv->regmap, STM32_LPTIM_CNT, &dat);
- if (ret)
- return ret;
- *val = dat;
- return IIO_VAL_INT;
-
- case IIO_CHAN_INFO_ENABLE:
- ret = stm32_lptim_is_enabled(priv);
- if (ret < 0)
- return ret;
- *val = ret;
- return IIO_VAL_INT;
-
- case IIO_CHAN_INFO_SCALE:
- /* Non-quadrature mode: scale = 1 */
- *val = 1;
- *val2 = 0;
- if (priv->quadrature_mode) {
- /*
- * Quadrature encoder mode:
- * - both edges, quarter cycle, scale is 0.25
- * - either rising/falling edge scale is 0.5
- */
- if (priv->polarity > 1)
- *val2 = 2;
- else
- *val2 = 1;
- }
- return IIO_VAL_FRACTIONAL_LOG2;
-
- default:
- return -EINVAL;
- }
-}
-
-static const struct iio_info stm32_lptim_cnt_iio_info = {
- .read_raw = stm32_lptim_read_raw,
- .write_raw = stm32_lptim_write_raw,
-};
-
-static const char *const stm32_lptim_quadrature_modes[] = {
- "non-quadrature",
- "quadrature",
-};
-
-static int stm32_lptim_get_quadrature_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
-
- return priv->quadrature_mode;
-}
-
-static int stm32_lptim_set_quadrature_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- unsigned int type)
-{
- struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
-
- if (stm32_lptim_is_enabled(priv))
- return -EBUSY;
-
- priv->quadrature_mode = type;
-
- return 0;
-}
-
-static const struct iio_enum stm32_lptim_quadrature_mode_en = {
- .items = stm32_lptim_quadrature_modes,
- .num_items = ARRAY_SIZE(stm32_lptim_quadrature_modes),
- .get = stm32_lptim_get_quadrature_mode,
- .set = stm32_lptim_set_quadrature_mode,
-};
-
-static const char * const stm32_lptim_cnt_polarity[] = {
- "rising-edge", "falling-edge", "both-edges",
-};
-
-static int stm32_lptim_cnt_get_polarity(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
-
- return priv->polarity;
-}
-
-static int stm32_lptim_cnt_set_polarity(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- unsigned int type)
-{
- struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
-
- if (stm32_lptim_is_enabled(priv))
- return -EBUSY;
-
- priv->polarity = type;
-
- return 0;
-}
-
-static const struct iio_enum stm32_lptim_cnt_polarity_en = {
- .items = stm32_lptim_cnt_polarity,
- .num_items = ARRAY_SIZE(stm32_lptim_cnt_polarity),
- .get = stm32_lptim_cnt_get_polarity,
- .set = stm32_lptim_cnt_set_polarity,
-};
-
-static ssize_t stm32_lptim_cnt_get_preset(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- char *buf)
-{
- struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
-
- return snprintf(buf, PAGE_SIZE, "%u\n", priv->preset);
-}
-
-static ssize_t stm32_lptim_cnt_set_preset(struct iio_dev *indio_dev,
- uintptr_t private,
- const struct iio_chan_spec *chan,
- const char *buf, size_t len)
-{
- struct stm32_lptim_cnt *priv = iio_priv(indio_dev);
- int ret;
-
- if (stm32_lptim_is_enabled(priv))
- return -EBUSY;
-
- ret = kstrtouint(buf, 0, &priv->preset);
- if (ret)
- return ret;
-
- if (priv->preset > STM32_LPTIM_MAX_ARR)
- return -EINVAL;
-
- return len;
-}
-
-/* LP timer with encoder */
-static const struct iio_chan_spec_ext_info stm32_lptim_enc_ext_info[] = {
- {
- .name = "preset",
- .shared = IIO_SEPARATE,
- .read = stm32_lptim_cnt_get_preset,
- .write = stm32_lptim_cnt_set_preset,
- },
- IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
- IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
- IIO_ENUM("quadrature_mode", IIO_SEPARATE,
- &stm32_lptim_quadrature_mode_en),
- IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_lptim_quadrature_mode_en),
- {}
-};
-
-static const struct iio_chan_spec stm32_lptim_enc_channels = {
- .type = IIO_COUNT,
- .channel = 0,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_ENABLE) |
- BIT(IIO_CHAN_INFO_SCALE),
- .ext_info = stm32_lptim_enc_ext_info,
- .indexed = 1,
-};
-
-/* LP timer without encoder (counter only) */
-static const struct iio_chan_spec_ext_info stm32_lptim_cnt_ext_info[] = {
- {
- .name = "preset",
- .shared = IIO_SEPARATE,
- .read = stm32_lptim_cnt_get_preset,
- .write = stm32_lptim_cnt_set_preset,
- },
- IIO_ENUM("polarity", IIO_SEPARATE, &stm32_lptim_cnt_polarity_en),
- IIO_ENUM_AVAILABLE("polarity", &stm32_lptim_cnt_polarity_en),
- {}
-};
-
-static const struct iio_chan_spec stm32_lptim_cnt_channels = {
- .type = IIO_COUNT,
- .channel = 0,
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
- BIT(IIO_CHAN_INFO_ENABLE) |
- BIT(IIO_CHAN_INFO_SCALE),
- .ext_info = stm32_lptim_cnt_ext_info,
- .indexed = 1,
-};
-
-static int stm32_lptim_cnt_probe(struct platform_device *pdev)
-{
- struct stm32_lptimer *ddata = dev_get_drvdata(pdev->dev.parent);
- struct stm32_lptim_cnt *priv;
- struct iio_dev *indio_dev;
-
- if (IS_ERR_OR_NULL(ddata))
- return -EINVAL;
-
- indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(*priv));
- if (!indio_dev)
- return -ENOMEM;
-
- priv = iio_priv(indio_dev);
- priv->dev = &pdev->dev;
- priv->regmap = ddata->regmap;
- priv->clk = ddata->clk;
- priv->preset = STM32_LPTIM_MAX_ARR;
-
- indio_dev->name = dev_name(&pdev->dev);
- indio_dev->dev.parent = &pdev->dev;
- indio_dev->dev.of_node = pdev->dev.of_node;
- indio_dev->info = &stm32_lptim_cnt_iio_info;
- if (ddata->has_encoder)
- indio_dev->channels = &stm32_lptim_enc_channels;
- else
- indio_dev->channels = &stm32_lptim_cnt_channels;
- indio_dev->num_channels = 1;
-
- platform_set_drvdata(pdev, priv);
-
- return devm_iio_device_register(&pdev->dev, indio_dev);
-}
-
-static const struct of_device_id stm32_lptim_cnt_of_match[] = {
- { .compatible = "st,stm32-lptimer-counter", },
- {},
-};
-MODULE_DEVICE_TABLE(of, stm32_lptim_cnt_of_match);
-
-static struct platform_driver stm32_lptim_cnt_driver = {
- .probe = stm32_lptim_cnt_probe,
- .driver = {
- .name = "stm32-lptimer-counter",
- .of_match_table = stm32_lptim_cnt_of_match,
- },
-};
-module_platform_driver(stm32_lptim_cnt_driver);
-
-MODULE_AUTHOR("Fabrice Gasnier <fabrice.gasnier@st.com>");
-MODULE_ALIAS("platform:stm32-lptimer-counter");
-MODULE_DESCRIPTION("STMicroelectronics STM32 LPTIM counter driver");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
index 80beb64..cc42219 100644
--- a/drivers/iio/dac/Kconfig
+++ b/drivers/iio/dac/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# DAC drivers
#
@@ -120,6 +121,16 @@
Say yes here to build support for Analog Devices AD5624R, AD5644R and
AD5664R converters (DAC). This driver uses the common SPI interface.
+config LTC1660
+ tristate "Linear Technology LTC1660/LTC1665 DAC SPI driver"
+ depends on SPI
+ help
+ Say yes here to build support for Linear Technology
+ LTC1660 and LTC1665 Digital to Analog Converters.
+
+ To compile this driver as a module, choose M here: the
+ module will be called ltc1660.
+
config LTC2632
tristate "Linear Technology LTC2632-12/10/8 DAC spi driver"
depends on SPI
@@ -138,9 +149,9 @@
depends on SPI
select AD5686
help
- Say yes here to build support for Analog Devices AD5672R, AD5676,
- AD5676R, AD5684, AD5684R, AD5684R, AD5685R, AD5686, AD5686R.
- Voltage Output Digital to Analog Converter.
+ Say yes here to build support for Analog Devices AD5672R, AD5674R,
+ AD5676, AD5676R, AD5679R, AD5684, AD5684R, AD5684R, AD5685R, AD5686,
+ AD5686R Voltage Output Digital to Analog Converter.
To compile this driver as a module, choose M here: the
module will be called ad5686.
@@ -356,6 +367,25 @@
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 TI_DAC7612
+ tristate "Texas Instruments 12-bit 2-channel DAC driver"
+ depends on SPI_MASTER && GPIOLIB
+ help
+ Driver for the Texas Instruments DAC7612, DAC7612U, DAC7612UB
+ The driver hand drive the load pin automatically, otherwise
+ it needs to be toggled manually.
+
+ If compiled as a module, it will be called ti-dac7612.
+
config VF610_DAC
tristate "Vybrid vf610 DAC driver"
depends on OF
diff --git a/drivers/iio/dac/Makefile b/drivers/iio/dac/Makefile
index a1b37cf..1369fa1 100644
--- a/drivers/iio/dac/Makefile
+++ b/drivers/iio/dac/Makefile
@@ -29,6 +29,7 @@
obj-$(CONFIG_DPOT_DAC) += dpot-dac.o
obj-$(CONFIG_DS4424) += ds4424.o
obj-$(CONFIG_LPC18XX_DAC) += lpc18xx_dac.o
+obj-$(CONFIG_LTC1660) += ltc1660.o
obj-$(CONFIG_LTC2632) += ltc2632.o
obj-$(CONFIG_M62332) += m62332.o
obj-$(CONFIG_MAX517) += max517.o
@@ -39,4 +40,6 @@
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_TI_DAC7612) += ti-dac7612.o
obj-$(CONFIG_VF610_DAC) += vf610_dac.o
diff --git a/drivers/iio/dac/ad5064.c b/drivers/iio/dac/ad5064.c
index 2f98cb2..d33642d 100644
--- a/drivers/iio/dac/ad5064.c
+++ b/drivers/iio/dac/ad5064.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5024, AD5025, AD5044, AD5045, AD5064, AD5064-1, AD5065, AD5625, AD5625R,
* AD5627, AD5627R, AD5628, AD5629R, AD5645R, AD5647R, AD5648, AD5665, AD5665R,
@@ -6,8 +7,6 @@
* Digital to analog converters driver
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -112,6 +111,8 @@
bool use_internal_vref;
ad5064_write_func write;
+ /* Lock used to maintain consistency between cached and dev state */
+ struct mutex lock;
/*
* DMA (thus cache coherency maintenance) requires the
@@ -248,11 +249,11 @@
struct ad5064_state *st = iio_priv(indio_dev);
int ret;
- mutex_lock(&indio_dev->mlock);
+ mutex_lock(&st->lock);
st->pwr_down_mode[chan->channel] = mode + 1;
ret = ad5064_sync_powerdown_mode(st, chan);
- mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->lock);
return ret;
}
@@ -291,11 +292,11 @@
if (ret)
return ret;
- mutex_lock(&indio_dev->mlock);
+ mutex_lock(&st->lock);
st->pwr_down[chan->channel] = pwr_down;
ret = ad5064_sync_powerdown_mode(st, chan);
- mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->lock);
return ret ? ret : len;
}
@@ -349,12 +350,12 @@
if (val >= (1 << chan->scan_type.realbits) || val < 0)
return -EINVAL;
- mutex_lock(&indio_dev->mlock);
+ mutex_lock(&st->lock);
ret = ad5064_write(st, AD5064_CMD_WRITE_INPUT_N_UPDATE_N,
chan->address, val, chan->scan_type.shift);
if (ret == 0)
st->dac_cache[chan->channel] = val;
- mutex_unlock(&indio_dev->mlock);
+ mutex_unlock(&st->lock);
break;
default:
ret = -EINVAL;
@@ -856,6 +857,7 @@
return -ENOMEM;
st = iio_priv(indio_dev);
+ mutex_init(&st->lock);
dev_set_drvdata(dev, indio_dev);
st->chip_info = &ad5064_chip_info_tbl[type];
diff --git a/drivers/iio/dac/ad5360.c b/drivers/iio/dac/ad5360.c
index 0209316..2ac428b 100644
--- a/drivers/iio/dac/ad5360.c
+++ b/drivers/iio/dac/ad5360.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog devices AD5360, AD5361, AD5362, AD5363, AD5370, AD5371, AD5373
* multi-channel Digital to Analog Converters driver
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
diff --git a/drivers/iio/dac/ad5380.c b/drivers/iio/dac/ad5380.c
index 873c2bf..2ebe083 100644
--- a/drivers/iio/dac/ad5380.c
+++ b/drivers/iio/dac/ad5380.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog devices AD5380, AD5381, AD5382, AD5383, AD5390, AD5391, AD5392
* multi-channel Digital to Analog Converters driver
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -221,7 +220,7 @@
if (ret)
return ret;
*val >>= chan->scan_type.shift;
- val -= (1 << chan->scan_type.realbits) / 2;
+ *val -= (1 << chan->scan_type.realbits) / 2;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 2 * st->vref;
diff --git a/drivers/iio/dac/ad5421.c b/drivers/iio/dac/ad5421.c
index 8e9633d..63063e8 100644
--- a/drivers/iio/dac/ad5421.c
+++ b/drivers/iio/dac/ad5421.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5421 Digital to analog converters driver
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
diff --git a/drivers/iio/dac/ad5446.c b/drivers/iio/dac/ad5446.c
index fd26a42..7df8b4c 100644
--- a/drivers/iio/dac/ad5446.c
+++ b/drivers/iio/dac/ad5446.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* AD5446 SPI DAC driver
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/interrupt.h>
@@ -628,6 +627,6 @@
}
module_exit(ad5446_exit);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5444/AD5446 DAC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5449.c b/drivers/iio/dac/ad5449.c
index 317a741..fed3eba 100644
--- a/drivers/iio/dac/ad5449.c
+++ b/drivers/iio/dac/ad5449.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5415, AD5426, AD5429, AD5432, AD5439, AD5443, AD5449 Digital to Analog
* Converter driver.
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
diff --git a/drivers/iio/dac/ad5504.c b/drivers/iio/dac/ad5504.c
index d9037ea..c64e689 100644
--- a/drivers/iio/dac/ad5504.c
+++ b/drivers/iio/dac/ad5504.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5504, AD5501 High Voltage Digital to Analog Converter
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
@@ -369,6 +368,6 @@
};
module_spi_driver(ad5504_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5501/AD5501 DAC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5592r-base.c b/drivers/iio/dac/ad5592r-base.c
index 095530c..2d897e6 100644
--- a/drivers/iio/dac/ad5592r-base.c
+++ b/drivers/iio/dac/ad5592r-base.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5592R Digital <-> Analog converters driver
*
* Copyright 2014-2016 Analog Devices Inc.
* Author: Paul Cercueil <paul.cercueil@analog.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/bitops.h>
diff --git a/drivers/iio/dac/ad5592r-base.h b/drivers/iio/dac/ad5592r-base.h
index 841457e..4774e4c 100644
--- a/drivers/iio/dac/ad5592r-base.h
+++ b/drivers/iio/dac/ad5592r-base.h
@@ -1,10 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AD5592R / AD5593R Digital <-> Analog converters driver
*
* Copyright 2015-2016 Analog Devices Inc.
* Author: Paul Cercueil <paul.cercueil@analog.com>
- *
- * Licensed under the GPL-2.
*/
#ifndef __DRIVERS_IIO_DAC_AD5592R_BASE_H__
diff --git a/drivers/iio/dac/ad5592r.c b/drivers/iio/dac/ad5592r.c
index 6a12a3c..34ba059 100644
--- a/drivers/iio/dac/ad5592r.c
+++ b/drivers/iio/dac/ad5592r.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5592R Digital <-> Analog converters driver
*
* Copyright 2015-2016 Analog Devices Inc.
* Author: Paul Cercueil <paul.cercueil@analog.com>
- *
- * Licensed under the GPL-2.
*/
#include "ad5592r-base.h"
diff --git a/drivers/iio/dac/ad5593r.c b/drivers/iio/dac/ad5593r.c
index fc11ea0..44ea3b8 100644
--- a/drivers/iio/dac/ad5593r.c
+++ b/drivers/iio/dac/ad5593r.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5593R Digital <-> Analog converters driver
*
* Copyright 2015-2016 Analog Devices Inc.
* Author: Paul Cercueil <paul.cercueil@analog.com>
- *
- * Licensed under the GPL-2.
*/
#include "ad5592r-base.h"
diff --git a/drivers/iio/dac/ad5624r.h b/drivers/iio/dac/ad5624r.h
index 5dca302..13964f3 100644
--- a/drivers/iio/dac/ad5624r.h
+++ b/drivers/iio/dac/ad5624r.h
@@ -1,9 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AD5624R SPI DAC driver
*
* Copyright 2010-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#ifndef SPI_AD5624R_H_
#define SPI_AD5624R_H_
diff --git a/drivers/iio/dac/ad5624r_spi.c b/drivers/iio/dac/ad5624r_spi.c
index 13fdb4d..e6c022e 100644
--- a/drivers/iio/dac/ad5624r_spi.c
+++ b/drivers/iio/dac/ad5624r_spi.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5624R, AD5644R, AD5664R Digital to analog convertors spi driver
*
* Copyright 2010-2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
diff --git a/drivers/iio/dac/ad5686-spi.c b/drivers/iio/dac/ad5686-spi.c
index 1df9143..0188ded 100644
--- a/drivers/iio/dac/ad5686-spi.c
+++ b/drivers/iio/dac/ad5686-spi.c
@@ -1,7 +1,8 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0
/*
- * AD5672R, AD5676, AD5676R, AD5681R, AD5682R, AD5683, AD5683R,
- * AD5684, AD5684R, AD5685R, AD5686, AD5686R
+ * AD5672R, AD5674R, AD5676, AD5676R, AD5679R,
+ * AD5681R, AD5682R, AD5683, AD5683R, AD5684,
+ * AD5684R, AD5685R, AD5686, AD5686R
* Digital to analog converters driver
*
* Copyright 2018 Analog Devices Inc.
@@ -19,6 +20,12 @@
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 +63,18 @@
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,9 +101,12 @@
}
static const struct spi_device_id ad5686_spi_id[] = {
+ {"ad5310r", ID_AD5310R},
{"ad5672r", ID_AD5672R},
+ {"ad5674r", ID_AD5674R},
{"ad5676", ID_AD5676},
{"ad5676r", ID_AD5676R},
+ {"ad5679r", ID_AD5679R},
{"ad5681r", ID_AD5681R},
{"ad5682r", ID_AD5682R},
{"ad5683", ID_AD5683},
diff --git a/drivers/iio/dac/ad5686.c b/drivers/iio/dac/ad5686.c
index 2ddbfc3..e06b29c 100644
--- a/drivers/iio/dac/ad5686.c
+++ b/drivers/iio/dac/ad5686.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0
/*
* AD5686R, AD5685R, AD5684R Digital to analog converters driver
*
@@ -71,7 +71,7 @@
int ret;
struct ad5686_state *st = iio_priv(indio_dev);
unsigned int val, ref_bit_msk;
- u8 shift;
+ u8 shift, address = 0;
ret = strtobool(buf, &readin);
if (ret)
@@ -83,6 +83,10 @@
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;
@@ -90,6 +94,9 @@
case AD5686_REGMAP:
shift = 0;
ref_bit_msk = 0;
+ /* AD5674R/AD5679R have 16 channels and 2 powerdown registers */
+ if (chan->channel > 0x7)
+ address = 0x8;
break;
case AD5693_REGMAP:
shift = 13;
@@ -103,7 +110,8 @@
if (!st->use_internal_vref)
val |= ref_bit_msk;
- ret = st->write(st, AD5686_CMD_POWERDOWN_DAC, 0, val);
+ ret = st->write(st, AD5686_CMD_POWERDOWN_DAC,
+ address, val >> (address * 2));
return ret ? ret : len;
}
@@ -124,7 +132,8 @@
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,9 +230,32 @@
AD5868_CHANNEL(7, 7, bits, _shift), \
}
+#define DECLARE_AD5679_CHANNELS(name, bits, _shift) \
+static struct iio_chan_spec name[] = { \
+ AD5868_CHANNEL(0, 0, bits, _shift), \
+ AD5868_CHANNEL(1, 1, bits, _shift), \
+ AD5868_CHANNEL(2, 2, bits, _shift), \
+ AD5868_CHANNEL(3, 3, bits, _shift), \
+ AD5868_CHANNEL(4, 4, bits, _shift), \
+ AD5868_CHANNEL(5, 5, bits, _shift), \
+ AD5868_CHANNEL(6, 6, bits, _shift), \
+ AD5868_CHANNEL(7, 7, bits, _shift), \
+ AD5868_CHANNEL(8, 8, bits, _shift), \
+ AD5868_CHANNEL(9, 9, bits, _shift), \
+ AD5868_CHANNEL(10, 10, bits, _shift), \
+ AD5868_CHANNEL(11, 11, bits, _shift), \
+ AD5868_CHANNEL(12, 12, bits, _shift), \
+ AD5868_CHANNEL(13, 13, bits, _shift), \
+ AD5868_CHANNEL(14, 14, bits, _shift), \
+ AD5868_CHANNEL(15, 15, bits, _shift), \
+}
+
+DECLARE_AD5693_CHANNELS(ad5310r_channels, 10, 2);
DECLARE_AD5693_CHANNELS(ad5311r_channels, 10, 6);
DECLARE_AD5676_CHANNELS(ad5672_channels, 12, 4);
+DECLARE_AD5679_CHANNELS(ad5674r_channels, 12, 4);
DECLARE_AD5676_CHANNELS(ad5676_channels, 16, 0);
+DECLARE_AD5679_CHANNELS(ad5679r_channels, 16, 0);
DECLARE_AD5686_CHANNELS(ad5684_channels, 12, 4);
DECLARE_AD5686_CHANNELS(ad5685r_channels, 14, 2);
DECLARE_AD5686_CHANNELS(ad5686_channels, 16, 0);
@@ -232,6 +264,12 @@
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,
@@ -250,6 +288,12 @@
.num_channels = 8,
.regmap_type = AD5686_REGMAP,
},
+ [ID_AD5674R] = {
+ .channels = ad5674r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 16,
+ .regmap_type = AD5686_REGMAP,
+ },
[ID_AD5675R] = {
.channels = ad5676_channels,
.int_vref_mv = 2500,
@@ -267,6 +311,12 @@
.num_channels = 8,
.regmap_type = AD5686_REGMAP,
},
+ [ID_AD5679R] = {
+ .channels = ad5679r_channels,
+ .int_vref_mv = 2500,
+ .num_channels = 16,
+ .regmap_type = AD5686_REGMAP,
+ },
[ID_AD5681R] = {
.channels = ad5691r_channels,
.int_vref_mv = 2500,
@@ -419,6 +469,11 @@
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;
@@ -470,6 +525,6 @@
}
EXPORT_SYMBOL_GPL(ad5686_remove);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5686/85/84 DAC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad5686.h b/drivers/iio/dac/ad5686.h
index 57b3c61..70a7799 100644
--- a/drivers/iio/dac/ad5686.h
+++ b/drivers/iio/dac/ad5686.h
@@ -1,4 +1,4 @@
-/* SPDX-License-Identifier: GPL-2.0+ */
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* This file is part of AD5686 DAC driver
*
@@ -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,12 +50,15 @@
* ad5686_supported_device_ids:
*/
enum ad5686_supported_device_ids {
+ ID_AD5310R,
ID_AD5311R,
ID_AD5671R,
ID_AD5672R,
+ ID_AD5674R,
ID_AD5675R,
ID_AD5676,
ID_AD5676R,
+ ID_AD5679R,
ID_AD5681R,
ID_AD5682R,
ID_AD5683,
@@ -72,6 +80,7 @@
};
enum ad5686_regmap_type {
+ AD5310_REGMAP,
AD5683_REGMAP,
AD5686_REGMAP,
AD5693_REGMAP
diff --git a/drivers/iio/dac/ad5696-i2c.c b/drivers/iio/dac/ad5696-i2c.c
index 7350d98..ccf794c 100644
--- a/drivers/iio/dac/ad5696-i2c.c
+++ b/drivers/iio/dac/ad5696-i2c.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0
/*
* AD5671R, AD5675R, AD5691R, AD5692R, AD5693, AD5693R,
* AD5694, AD5694R, AD5695R, AD5696, AD5696R
diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
index 2d03cc8..b9175fb 100644
--- a/drivers/iio/dac/ad5755.c
+++ b/drivers/iio/dac/ad5755.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5755, AD5755-1, AD5757, AD5735, AD5737 Digital to analog converters driver
*
* Copyright 2012 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
diff --git a/drivers/iio/dac/ad5758.c b/drivers/iio/dac/ad5758.c
index bd36333..475646c 100644
--- a/drivers/iio/dac/ad5758.c
+++ b/drivers/iio/dac/ad5758.c
@@ -1,4 +1,4 @@
-// SPDX-License-Identifier: GPL-2.0+
+// SPDX-License-Identifier: GPL-2.0
/*
* AD5758 Digital to analog converters driver
*
@@ -11,7 +11,10 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/property.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -71,8 +74,6 @@
#define AD5758_DCDC_CONFIG1_DCDC_VPROG_MODE(x) (((x) & 0x1F) << 0)
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MSK GENMASK(6, 5)
#define AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(x) (((x) & 0x3) << 5)
-#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK BIT(7)
-#define AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(x) (((x) & 0x1) << 7)
/* AD5758_DCDC_CONFIG2 */
#define AD5758_DCDC_CONFIG2_ILIMIT_MSK GENMASK(3, 1)
@@ -83,6 +84,10 @@
/* AD5758_DIGITAL_DIAG_RESULTS */
#define AD5758_CAL_MEM_UNREFRESHED_MSK BIT(15)
+/* AD5758_ADC_CONFIG */
+#define AD5758_ADC_CONFIG_PPC_BUF_EN(x) (((x) & 0x1) << 11)
+#define AD5758_ADC_CONFIG_PPC_BUF_MSK BIT(11)
+
#define AD5758_WR_FLAG_MSK(x) (0x80 | ((x) & 0x1F))
#define AD5758_FULL_SCALE_MICRO 65535000000ULL
@@ -108,6 +113,7 @@
struct ad5758_state {
struct spi_device *spi;
struct mutex lock;
+ struct gpio_desc *gpio_reset;
struct ad5758_range out_range;
unsigned int dc_dc_mode;
unsigned int dc_dc_ilim;
@@ -313,6 +319,18 @@
{
int ret;
+ /*
+ * The ENABLE_PPC_BUFFERS bit must be set prior to enabling PPC current
+ * mode.
+ */
+ if (mode == AD5758_DCDC_MODE_PPC_CURRENT) {
+ ret = ad5758_spi_write_mask(st, AD5758_ADC_CONFIG,
+ AD5758_ADC_CONFIG_PPC_BUF_MSK,
+ AD5758_ADC_CONFIG_PPC_BUF_EN(1));
+ if (ret < 0)
+ return ret;
+ }
+
ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
AD5758_DCDC_CONFIG1_DCDC_MODE_MSK,
AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(mode));
@@ -442,23 +460,6 @@
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
-static int ad5758_fault_prot_switch_en(struct ad5758_state *st, bool enable)
-{
- int ret;
-
- ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
- AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK,
- AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(enable));
- if (ret < 0)
- return ret;
- /*
- * Poll the BUSY_3WI bit in the DCDC_CONFIG2 register until it is 0.
- * This allows the 3-wire interface communication to complete.
- */
- return ad5758_wait_for_task_complete(st, AD5758_DCDC_CONFIG2,
- AD5758_DCDC_CONFIG2_BUSY_3WI_MSK);
-}
-
static int ad5758_internal_buffers_en(struct ad5758_state *st, bool enable)
{
int ret;
@@ -474,6 +475,21 @@
AD5758_CAL_MEM_UNREFRESHED_MSK);
}
+static int ad5758_reset(struct ad5758_state *st)
+{
+ if (st->gpio_reset) {
+ gpiod_set_value(st->gpio_reset, 0);
+ usleep_range(100, 1000);
+ gpiod_set_value(st->gpio_reset, 1);
+ usleep_range(100, 1000);
+
+ return 0;
+ } else {
+ /* Perform a software reset */
+ return ad5758_soft_reset(st);
+ }
+}
+
static int ad5758_reg_access(struct iio_dev *indio_dev,
unsigned int reg,
unsigned int writeval,
@@ -568,8 +584,8 @@
{
struct ad5758_state *st = iio_priv(indio_dev);
bool pwr_down;
- unsigned int dcdc_config1_mode, dc_dc_mode, dac_config_mode, val;
- unsigned long int dcdc_config1_msk, dac_config_msk;
+ unsigned int dac_config_mode, val;
+ unsigned long int dac_config_msk;
int ret;
ret = kstrtobool(buf, &pwr_down);
@@ -577,24 +593,10 @@
return ret;
mutex_lock(&st->lock);
- if (pwr_down) {
- dc_dc_mode = AD5758_DCDC_MODE_POWER_OFF;
+ if (pwr_down)
val = 0;
- } else {
- dc_dc_mode = st->dc_dc_mode;
+ else
val = 1;
- }
-
- dcdc_config1_mode = AD5758_DCDC_CONFIG1_DCDC_MODE_MODE(dc_dc_mode) |
- AD5758_DCDC_CONFIG1_PROT_SW_EN_MODE(val);
- dcdc_config1_msk = AD5758_DCDC_CONFIG1_DCDC_MODE_MSK |
- AD5758_DCDC_CONFIG1_PROT_SW_EN_MSK;
-
- ret = ad5758_spi_write_mask(st, AD5758_DCDC_CONFIG1,
- dcdc_config1_msk,
- dcdc_config1_mode);
- if (ret < 0)
- goto err_unlock;
dac_config_mode = AD5758_DAC_CONFIG_OUT_EN_MODE(val) |
AD5758_DAC_CONFIG_INT_EN_MODE(val);
@@ -768,13 +770,18 @@
{
int regval, ret;
+ st->gpio_reset = devm_gpiod_get_optional(&st->spi->dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(st->gpio_reset))
+ return PTR_ERR(st->gpio_reset);
+
/* Disable CRC checks */
ret = ad5758_crc_disable(st);
if (ret < 0)
return ret;
- /* Perform a software reset */
- ret = ad5758_soft_reset(st);
+ /* Perform a reset */
+ ret = ad5758_reset(st);
if (ret < 0)
return ret;
@@ -819,11 +826,6 @@
return ret;
}
- /* Enable the VIOUT fault protection switch (FPS is closed) */
- ret = ad5758_fault_prot_switch_en(st, 1);
- if (ret < 0)
- return ret;
-
/* Power up the DAC and internal (INT) amplifiers */
ret = ad5758_internal_buffers_en(st, 1);
if (ret < 0)
@@ -882,9 +884,16 @@
};
MODULE_DEVICE_TABLE(spi, ad5758_id);
+static const struct of_device_id ad5758_of_match[] = {
+ { .compatible = "adi,ad5758" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, ad5758_of_match);
+
static struct spi_driver ad5758_driver = {
.driver = {
.name = KBUILD_MODNAME,
+ .of_match_table = ad5758_of_match,
},
.probe = ad5758_probe,
.id_table = ad5758_id,
diff --git a/drivers/iio/dac/ad5761.c b/drivers/iio/dac/ad5761.c
index 05017c8..4fb42b7 100644
--- a/drivers/iio/dac/ad5761.c
+++ b/drivers/iio/dac/ad5761.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5721, AD5721R, AD5761, AD5761R, Voltage Output Digital to Analog Converter
*
* Copyright 2016 Qtechnology A/S
* 2016 Ricardo Ribalda <ricardo.ribalda@gmail.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/iio/dac/ad5764.c b/drivers/iio/dac/ad5764.c
index 9333177..f7ab211 100644
--- a/drivers/iio/dac/ad5764.c
+++ b/drivers/iio/dac/ad5764.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Analog devices AD5764, AD5764R, AD5744, AD5744R quad-channel
* Digital to Analog Converters driver
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
diff --git a/drivers/iio/dac/ad5791.c b/drivers/iio/dac/ad5791.c
index 7569bf6..1d11f39 100644
--- a/drivers/iio/dac/ad5791.c
+++ b/drivers/iio/dac/ad5791.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD5760, AD5780, AD5781, AD5790, AD5791 Voltage Output Digital to Analog
* Converter
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
@@ -467,6 +466,6 @@
};
module_spi_driver(ad5791_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD5760/AD5780/AD5781/AD5790/AD5791 DAC");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ad7303.c b/drivers/iio/dac/ad7303.c
index 8f3bd19..8de9f40 100644
--- a/drivers/iio/dac/ad7303.c
+++ b/drivers/iio/dac/ad7303.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD7303 Digital to analog converters driver
*
* Copyright 2013 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/err.h>
diff --git a/drivers/iio/dac/ad8801.c b/drivers/iio/dac/ad8801.c
index aef5808..0789c91 100644
--- a/drivers/iio/dac/ad8801.c
+++ b/drivers/iio/dac/ad8801.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO DAC driver for Analog Devices AD8801 DAC
*
* Copyright (C) 2016 Gwenhael Goavec-Merou
- * 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.
- *
- * 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/iio/iio.h>
diff --git a/drivers/iio/dac/cio-dac.c b/drivers/iio/dac/cio-dac.c
index 6898b0c..8167779 100644
--- a/drivers/iio/dac/cio-dac.c
+++ b/drivers/iio/dac/cio-dac.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO driver for the Measurement Computing CIO-DAC
* Copyright (C) 2016 William Breathitt Gray
*
- * 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.
- *
- * 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.
- *
* This driver supports the following Measurement Computing devices: CIO-DAC16,
* CIO-DAC06, and PC104-DAC06.
*/
diff --git a/drivers/iio/dac/dpot-dac.c b/drivers/iio/dac/dpot-dac.c
index aaa2103..4a6111b 100644
--- a/drivers/iio/dac/dpot-dac.c
+++ b/drivers/iio/dac/dpot-dac.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* IIO DAC emulation driver using a digital potentiometer
*
* Copyright (C) 2016 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
- *
- * 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.
*/
/*
@@ -77,11 +74,11 @@
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/ds4424.c b/drivers/iio/dac/ds4424.c
index 883a475..26d2066 100644
--- a/drivers/iio/dac/ds4424.c
+++ b/drivers/iio/dac/ds4424.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Maxim Integrated
* 7-bit, Multi-Channel Sink/Source Current DAC Driver
* Copyright (C) 2017 Maxim Integrated
- *
- * 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/kernel.h>
@@ -166,7 +163,7 @@
{
int ret, val;
- ret = ds4424_get_value(indio_dev, &val, DS4424_DAC_ADDR(0));
+ ret = ds4424_get_value(indio_dev, &val, 0);
if (ret < 0)
dev_err(&indio_dev->dev,
"%s failed. ret: %d\n", __func__, ret);
@@ -236,12 +233,6 @@
indio_dev->dev.of_node = client->dev.of_node;
indio_dev->dev.parent = &client->dev;
- if (!client->dev.of_node) {
- dev_err(&client->dev,
- "Not found DT.\n");
- return -ENODEV;
- }
-
data->vcc_reg = devm_regulator_get(&client->dev, "vcc");
if (IS_ERR(data->vcc_reg)) {
dev_err(&client->dev,
diff --git a/drivers/iio/dac/lpc18xx_dac.c b/drivers/iio/dac/lpc18xx_dac.c
index 7036f77..883e84e 100644
--- a/drivers/iio/dac/lpc18xx_dac.c
+++ b/drivers/iio/dac/lpc18xx_dac.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO DAC driver for NXP LPC18xx DAC
*
* Copyright (C) 2016 Joachim Eastwood <manabian@gmail.com>
*
- * 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.
- *
* UNSUPPORTED hardware features:
* - Interrupts
* - DMA
diff --git a/drivers/iio/dac/ltc1660.c b/drivers/iio/dac/ltc1660.c
new file mode 100644
index 0000000..1086683
--- /dev/null
+++ b/drivers/iio/dac/ltc1660.c
@@ -0,0 +1,250 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for Linear Technology LTC1665/LTC1660, 8 channels DAC
+ *
+ * Copyright (C) 2018 Marcus Folkesson <marcus.folkesson@gmail.com>
+ */
+#include <linux/bitops.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/regulator/consumer.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#define LTC1660_REG_WAKE 0x0
+#define LTC1660_REG_DAC_A 0x1
+#define LTC1660_REG_DAC_B 0x2
+#define LTC1660_REG_DAC_C 0x3
+#define LTC1660_REG_DAC_D 0x4
+#define LTC1660_REG_DAC_E 0x5
+#define LTC1660_REG_DAC_F 0x6
+#define LTC1660_REG_DAC_G 0x7
+#define LTC1660_REG_DAC_H 0x8
+#define LTC1660_REG_SLEEP 0xe
+
+#define LTC1660_NUM_CHANNELS 8
+
+static const struct regmap_config ltc1660_regmap_config = {
+ .reg_bits = 4,
+ .val_bits = 12,
+};
+
+enum ltc1660_supported_device_ids {
+ ID_LTC1660,
+ ID_LTC1665,
+};
+
+struct ltc1660_priv {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct regulator *vref_reg;
+ unsigned int value[LTC1660_NUM_CHANNELS];
+ unsigned int vref_mv;
+};
+
+static int ltc1660_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ struct ltc1660_priv *priv = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ *val = priv->value[chan->channel];
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = regulator_get_voltage(priv->vref_reg);
+ if (*val < 0) {
+ dev_err(&priv->spi->dev, "failed to read vref regulator: %d\n",
+ *val);
+ return *val;
+ }
+
+ /* Convert to mV */
+ *val /= 1000;
+ *val2 = chan->scan_type.realbits;
+ return IIO_VAL_FRACTIONAL_LOG2;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ltc1660_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ struct ltc1660_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (val2 != 0)
+ return -EINVAL;
+
+ if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
+ return -EINVAL;
+
+ ret = regmap_write(priv->regmap, chan->channel,
+ (val << chan->scan_type.shift));
+ if (!ret)
+ priv->value[chan->channel] = val;
+
+ return ret;
+ default:
+ return -EINVAL;
+ }
+}
+
+#define LTC1660_CHAN(chan, bits) { \
+ .type = IIO_VOLTAGE, \
+ .indexed = 1, \
+ .output = 1, \
+ .channel = chan, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (bits), \
+ .storagebits = 16, \
+ .shift = 12 - (bits), \
+ }, \
+}
+
+#define LTC1660_OCTAL_CHANNELS(bits) { \
+ LTC1660_CHAN(LTC1660_REG_DAC_A, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_B, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_C, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_D, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_E, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_F, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_G, bits), \
+ LTC1660_CHAN(LTC1660_REG_DAC_H, bits), \
+}
+
+static const struct iio_chan_spec ltc1660_channels[][LTC1660_NUM_CHANNELS] = {
+ [ID_LTC1660] = LTC1660_OCTAL_CHANNELS(10),
+ [ID_LTC1665] = LTC1660_OCTAL_CHANNELS(8),
+};
+
+static const struct iio_info ltc1660_info = {
+ .read_raw = <c1660_read_raw,
+ .write_raw = <c1660_write_raw,
+};
+
+static int __maybe_unused ltc1660_suspend(struct device *dev)
+{
+ struct ltc1660_priv *priv = iio_priv(spi_get_drvdata(
+ to_spi_device(dev)));
+ return regmap_write(priv->regmap, LTC1660_REG_SLEEP, 0x00);
+}
+
+static int __maybe_unused ltc1660_resume(struct device *dev)
+{
+ struct ltc1660_priv *priv = iio_priv(spi_get_drvdata(
+ to_spi_device(dev)));
+ return regmap_write(priv->regmap, LTC1660_REG_WAKE, 0x00);
+}
+static SIMPLE_DEV_PM_OPS(ltc1660_pm_ops, ltc1660_suspend, ltc1660_resume);
+
+static int ltc1660_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct ltc1660_priv *priv;
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->regmap = devm_regmap_init_spi(spi, <c1660_regmap_config);
+ if (IS_ERR(priv->regmap)) {
+ dev_err(&spi->dev, "failed to register spi regmap %ld\n",
+ PTR_ERR(priv->regmap));
+ return PTR_ERR(priv->regmap);
+ }
+
+ priv->vref_reg = devm_regulator_get(&spi->dev, "vref");
+ if (IS_ERR(priv->vref_reg)) {
+ dev_err(&spi->dev, "vref regulator not specified\n");
+ return PTR_ERR(priv->vref_reg);
+ }
+
+ ret = regulator_enable(priv->vref_reg);
+ if (ret) {
+ dev_err(&spi->dev, "failed to enable vref regulator: %d\n",
+ ret);
+ return ret;
+ }
+
+ priv->spi = spi;
+ spi_set_drvdata(spi, indio_dev);
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->info = <c1660_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ltc1660_channels[id->driver_data];
+ indio_dev->num_channels = LTC1660_NUM_CHANNELS;
+ indio_dev->name = id->name;
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&spi->dev, "failed to register iio device: %d\n",
+ ret);
+ goto error_disable_reg;
+ }
+
+ return 0;
+
+error_disable_reg:
+ regulator_disable(priv->vref_reg);
+
+ return ret;
+}
+
+static int ltc1660_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct ltc1660_priv *priv = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ regulator_disable(priv->vref_reg);
+
+ return 0;
+}
+
+static const struct of_device_id ltc1660_dt_ids[] = {
+ { .compatible = "lltc,ltc1660", .data = (void *)ID_LTC1660 },
+ { .compatible = "lltc,ltc1665", .data = (void *)ID_LTC1665 },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ltc1660_dt_ids);
+
+static const struct spi_device_id ltc1660_id[] = {
+ {"ltc1660", ID_LTC1660},
+ {"ltc1665", ID_LTC1665},
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(spi, ltc1660_id);
+
+static struct spi_driver ltc1660_driver = {
+ .driver = {
+ .name = "ltc1660",
+ .of_match_table = ltc1660_dt_ids,
+ .pm = <c1660_pm_ops,
+ },
+ .probe = ltc1660_probe,
+ .remove = ltc1660_remove,
+ .id_table = ltc1660_id,
+};
+module_spi_driver(ltc1660_driver);
+
+MODULE_AUTHOR("Marcus Folkesson <marcus.folkesson@gmail.com>");
+MODULE_DESCRIPTION("Linear Technology LTC1660/LTC1665 DAC");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ltc2632.c b/drivers/iio/dac/ltc2632.c
index 15d498f..643d1ce 100644
--- a/drivers/iio/dac/ltc2632.c
+++ b/drivers/iio/dac/ltc2632.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* LTC2632 Digital to analog convertors spi driver
*
* Copyright 2017 Maxime Roussin-Bélanger
* expanded by Silvan Murer <silvan.murer@gmail.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
diff --git a/drivers/iio/dac/m62332.c b/drivers/iio/dac/m62332.c
index 1903194..3205ca9 100644
--- a/drivers/iio/dac/m62332.c
+++ b/drivers/iio/dac/m62332.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* m62332.c - Support for Mitsubishi m62332 DAC
*
@@ -5,16 +6,6 @@
*
* Based on max517 driver:
* Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
- *
- * 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/dac/max517.c b/drivers/iio/dac/max517.c
index 1d85324..7e01838 100644
--- a/drivers/iio/dac/max517.c
+++ b/drivers/iio/dac/max517.c
@@ -1,21 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* max517.c - Support for Maxim MAX517, MAX518 and MAX519
*
* Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de>
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
@@ -113,15 +100,14 @@
return ret;
}
-#ifdef CONFIG_PM_SLEEP
-static int max517_suspend(struct device *dev)
+static int __maybe_unused max517_suspend(struct device *dev)
{
u8 outbuf = COMMAND_PD;
return i2c_master_send(to_i2c_client(dev), &outbuf, 1);
}
-static int max517_resume(struct device *dev)
+static int __maybe_unused max517_resume(struct device *dev)
{
u8 outbuf = 0;
@@ -129,10 +115,6 @@
}
static SIMPLE_DEV_PM_OPS(max517_pm_ops, max517_suspend, max517_resume);
-#define MAX517_PM_OPS (&max517_pm_ops)
-#else
-#define MAX517_PM_OPS NULL
-#endif
static const struct iio_info max517_info = {
.read_raw = max517_read_raw,
@@ -229,7 +211,7 @@
static struct i2c_driver max517_driver = {
.driver = {
.name = MAX517_DRV_NAME,
- .pm = MAX517_PM_OPS,
+ .pm = &max517_pm_ops,
},
.probe = max517_probe,
.remove = max517_remove,
diff --git a/drivers/iio/dac/max5821.c b/drivers/iio/dac/max5821.c
index d0ecc1f..2da086e 100644
--- a/drivers/iio/dac/max5821.c
+++ b/drivers/iio/dac/max5821.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* iio/dac/max5821.c
* Copyright (C) 2014 Philippe Reynes
- *
- * 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/kernel.h>
@@ -270,8 +267,7 @@
}
}
-#ifdef CONFIG_PM_SLEEP
-static int max5821_suspend(struct device *dev)
+static int __maybe_unused max5821_suspend(struct device *dev)
{
u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
MAX5821_EXTENDED_DAC_A |
@@ -281,7 +277,7 @@
return i2c_master_send(to_i2c_client(dev), outbuf, 2);
}
-static int max5821_resume(struct device *dev)
+static int __maybe_unused max5821_resume(struct device *dev)
{
u8 outbuf[2] = { MAX5821_EXTENDED_COMMAND_MODE,
MAX5821_EXTENDED_DAC_A |
@@ -292,10 +288,6 @@
}
static SIMPLE_DEV_PM_OPS(max5821_pm_ops, max5821_suspend, max5821_resume);
-#define MAX5821_PM_OPS (&max5821_pm_ops)
-#else
-#define MAX5821_PM_OPS NULL
-#endif /* CONFIG_PM_SLEEP */
static const struct iio_info max5821_info = {
.read_raw = max5821_read_raw,
@@ -392,7 +384,7 @@
.driver = {
.name = "max5821",
.of_match_table = max5821_of_match,
- .pm = MAX5821_PM_OPS,
+ .pm = &max5821_pm_ops,
},
.probe = max5821_probe,
.remove = max5821_remove,
diff --git a/drivers/iio/dac/mcp4725.c b/drivers/iio/dac/mcp4725.c
index 8b5aad4..ed455e8 100644
--- a/drivers/iio/dac/mcp4725.c
+++ b/drivers/iio/dac/mcp4725.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* mcp4725.c - Support for Microchip MCP4725/6
*
@@ -5,10 +6,6 @@
*
* Based on max517 by Roland Stigge <stigge@antcom.de>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* driver for the Microchip I2C 12-bit digital-to-analog converter (DAC)
* (7-bit I2C slave address 0x60, the three LSBs can be configured in
* hardware)
@@ -45,7 +42,7 @@
struct regulator *vref_reg;
};
-static int mcp4725_suspend(struct device *dev)
+static int __maybe_unused mcp4725_suspend(struct device *dev)
{
struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
to_i2c_client(dev)));
@@ -58,7 +55,7 @@
return i2c_master_send(data->client, outbuf, 2);
}
-static int mcp4725_resume(struct device *dev)
+static int __maybe_unused mcp4725_resume(struct device *dev)
{
struct mcp4725_data *data = iio_priv(i2c_get_clientdata(
to_i2c_client(dev)));
@@ -71,13 +68,7 @@
return i2c_master_send(data->client, outbuf, 2);
}
-
-#ifdef CONFIG_PM_SLEEP
static SIMPLE_DEV_PM_OPS(mcp4725_pm_ops, mcp4725_suspend, mcp4725_resume);
-#define MCP4725_PM_OPS (&mcp4725_pm_ops)
-#else
-#define MCP4725_PM_OPS NULL
-#endif
static ssize_t mcp4725_store_eeprom(struct device *dev,
struct device_attribute *attr, const char *buf, size_t len)
@@ -98,6 +89,7 @@
inoutbuf[0] = 0x60; /* write EEPROM */
inoutbuf[0] |= data->ref_mode << 3;
+ inoutbuf[0] |= data->powerdown ? ((data->powerdown_mode + 1) << 1) : 0;
inoutbuf[1] = data->dac_value >> 4;
inoutbuf[2] = (data->dac_value & 0xf) << 4;
@@ -547,7 +539,7 @@
.driver = {
.name = MCP4725_DRV_NAME,
.of_match_table = of_match_ptr(mcp4725_of_match),
- .pm = MCP4725_PM_OPS,
+ .pm = &mcp4725_pm_ops,
},
.probe = mcp4725_probe,
.remove = mcp4725_remove,
diff --git a/drivers/iio/dac/mcp4922.c b/drivers/iio/dac/mcp4922.c
index bf9aa3f..f9194b3 100644
--- a/drivers/iio/dac/mcp4922.c
+++ b/drivers/iio/dac/mcp4922.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* mcp4922.c
*
@@ -5,17 +6,6 @@
* Supports MCP4902, MCP4912, and MCP4922.
*
* Copyright (c) 2014 EMAC Inc.
- *
- * 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>
@@ -94,17 +84,22 @@
long mask)
{
struct mcp4922_state *state = iio_priv(indio_dev);
+ int ret;
if (val2 != 0)
return -EINVAL;
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (val > GENMASK(chan->scan_type.realbits-1, 0))
+ if (val < 0 || val > GENMASK(chan->scan_type.realbits - 1, 0))
return -EINVAL;
val <<= chan->scan_type.shift;
- state->value[chan->channel] = val;
- return mcp4922_spi_write(state, chan->channel, val);
+
+ ret = mcp4922_spi_write(state, chan->channel, val);
+ if (!ret)
+ state->value[chan->channel] = val;
+ return ret;
+
default:
return -EINVAL;
}
diff --git a/drivers/iio/dac/ti-dac082s085.c b/drivers/iio/dac/ti-dac082s085.c
index 4e1e283..57b498d 100644
--- a/drivers/iio/dac/ti-dac082s085.c
+++ b/drivers/iio/dac/ti-dac082s085.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ti-dac082s085.c - Texas Instruments 8/10/12-bit 2/4-channel DAC driver
*
@@ -9,10 +10,6 @@
* http://www.ti.com/lit/ds/symlink/dac084s085.pdf
* http://www.ti.com/lit/ds/symlink/dac104s085.pdf
* http://www.ti.com/lit/ds/symlink/dac124s085.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/iio/iio.h>
diff --git a/drivers/iio/dac/ti-dac5571.c b/drivers/iio/dac/ti-dac5571.c
index e39d1e9..3a2bb0e 100644
--- a/drivers/iio/dac/ti-dac5571.c
+++ b/drivers/iio/dac/ti-dac5571.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ti-dac5571.c - Texas Instruments 8/10/12-bit 1/4-channel DAC driver
*
@@ -12,10 +13,6 @@
* http://www.ti.com/lit/ds/symlink/dac5573.pdf
* http://www.ti.com/lit/ds/symlink/dac6573.pdf
* http://www.ti.com/lit/ds/symlink/dac7573.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/iio/iio.h>
@@ -421,6 +418,7 @@
static struct i2c_driver dac5571_driver = {
.driver = {
.name = "ti-dac5571",
+ .of_match_table = of_match_ptr(dac5571_of_id),
},
.probe = dac5571_probe,
.remove = dac5571_remove,
@@ -428,6 +426,6 @@
};
module_i2c_driver(dac5571_driver);
-MODULE_AUTHOR("Sean Nyekjaer <sean.nyekjaer@prevas.dk>");
+MODULE_AUTHOR("Sean Nyekjaer <sean@geanix.dk>");
MODULE_DESCRIPTION("Texas Instruments 8/10/12-bit 1/4-channel DAC driver");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/ti-dac7311.c b/drivers/iio/dac/ti-dac7311.c
new file mode 100644
index 0000000..6f5df1a
--- /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/dac/ti-dac7612.c b/drivers/iio/dac/ti-dac7612.c
new file mode 100644
index 0000000..c468051
--- /dev/null
+++ b/drivers/iio/dac/ti-dac7612.c
@@ -0,0 +1,184 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * DAC7612 Dual, 12-Bit Serial input Digital-to-Analog Converter
+ *
+ * Copyright 2019 Qtechnology A/S
+ * 2019 Ricardo Ribalda <ricardo@ribalda.com>
+ *
+ * Licensed under the GPL-2.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/gpio/consumer.h>
+#include <linux/iio/iio.h>
+
+#define DAC7612_RESOLUTION 12
+#define DAC7612_ADDRESS 4
+#define DAC7612_START 5
+
+struct dac7612 {
+ struct spi_device *spi;
+ struct gpio_desc *loaddacs;
+ uint16_t cache[2];
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ uint8_t data[2] ____cacheline_aligned;
+};
+
+static int dac7612_cmd_single(struct dac7612 *priv, int channel, u16 val)
+{
+ int ret;
+
+ priv->data[0] = BIT(DAC7612_START) | (channel << DAC7612_ADDRESS);
+ priv->data[0] |= val >> 8;
+ priv->data[1] = val & 0xff;
+
+ priv->cache[channel] = val;
+
+ ret = spi_write(priv->spi, priv->data, sizeof(priv->data));
+ if (ret)
+ return ret;
+
+ gpiod_set_value(priv->loaddacs, 1);
+ gpiod_set_value(priv->loaddacs, 0);
+
+ return 0;
+}
+
+#define dac7612_CHANNEL(chan, name) { \
+ .type = IIO_VOLTAGE, \
+ .channel = (chan), \
+ .indexed = 1, \
+ .output = 1, \
+ .datasheet_name = name, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+}
+
+static const struct iio_chan_spec dac7612_channels[] = {
+ dac7612_CHANNEL(0, "OUTA"),
+ dac7612_CHANNEL(1, "OUTB"),
+};
+
+static int dac7612_read_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct dac7612 *priv;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ priv = iio_priv(iio_dev);
+ *val = priv->cache[chan->channel];
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dac7612_write_raw(struct iio_dev *iio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long mask)
+{
+ struct dac7612 *priv = iio_priv(iio_dev);
+ int ret;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ if ((val >= BIT(DAC7612_RESOLUTION)) || val < 0 || val2)
+ return -EINVAL;
+
+ if (val == priv->cache[chan->channel])
+ return 0;
+
+ mutex_lock(&iio_dev->mlock);
+ ret = dac7612_cmd_single(priv, chan->channel, val);
+ mutex_unlock(&iio_dev->mlock);
+
+ return ret;
+}
+
+static const struct iio_info dac7612_info = {
+ .read_raw = dac7612_read_raw,
+ .write_raw = dac7612_write_raw,
+};
+
+static int dac7612_probe(struct spi_device *spi)
+{
+ struct iio_dev *iio_dev;
+ struct dac7612 *priv;
+ int i;
+ int ret;
+
+ iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*priv));
+ if (!iio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(iio_dev);
+ /*
+ * LOADDACS pin can be controlled by the driver or externally.
+ * When controlled by the driver, the DAC value is updated after
+ * every write.
+ * When the driver does not control the PIN, the user or an external
+ * event can change the value of all DACs by pulsing down the LOADDACs
+ * pin.
+ */
+ priv->loaddacs = devm_gpiod_get_optional(&spi->dev, "ti,loaddacs",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(priv->loaddacs))
+ return PTR_ERR(priv->loaddacs);
+ priv->spi = spi;
+ spi_set_drvdata(spi, iio_dev);
+ iio_dev->dev.parent = &spi->dev;
+ iio_dev->info = &dac7612_info;
+ iio_dev->modes = INDIO_DIRECT_MODE;
+ iio_dev->channels = dac7612_channels;
+ iio_dev->num_channels = ARRAY_SIZE(priv->cache);
+ iio_dev->name = spi_get_device_id(spi)->name;
+
+ for (i = 0; i < ARRAY_SIZE(priv->cache); i++) {
+ ret = dac7612_cmd_single(priv, i, 0);
+ if (ret)
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, iio_dev);
+}
+
+static const struct spi_device_id dac7612_id[] = {
+ {"ti-dac7612"},
+ {}
+};
+MODULE_DEVICE_TABLE(spi, dac7612_id);
+
+static const struct of_device_id dac7612_of_match[] = {
+ { .compatible = "ti,dac7612" },
+ { .compatible = "ti,dac7612u" },
+ { .compatible = "ti,dac7612ub" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, dac7612_of_match);
+
+static struct spi_driver dac7612_driver = {
+ .driver = {
+ .name = "ti-dac7612",
+ .of_match_table = dac7612_of_match,
+ },
+ .probe = dac7612_probe,
+ .id_table = dac7612_id,
+};
+module_spi_driver(dac7612_driver);
+
+MODULE_AUTHOR("Ricardo Ribalda <ricardo@ribalda.com>");
+MODULE_DESCRIPTION("Texas Instruments DAC7612 DAC driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/dac/vf610_dac.c b/drivers/iio/dac/vf610_dac.c
index 5dccdd1..0ec4d26 100644
--- a/drivers/iio/dac/vf610_dac.c
+++ b/drivers/iio/dac/vf610_dac.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Freescale Vybrid vf610 DAC driver
*
* Copyright 2016 Toradex AG
- *
- * 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/clk.h>
diff --git a/drivers/iio/dummy/Kconfig b/drivers/iio/dummy/Kconfig
index c4fd108..5c5c2f8 100644
--- a/drivers/iio/dummy/Kconfig
+++ b/drivers/iio/dummy/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Industrial I/O subsystem Dummy Driver configuration
#
diff --git a/drivers/iio/dummy/iio_dummy_evgen.c b/drivers/iio/dummy/iio_dummy_evgen.c
index efd0005..a6edf30 100644
--- a/drivers/iio/dummy/iio_dummy_evgen.c
+++ b/drivers/iio/dummy/iio_dummy_evgen.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Copyright (c) 2011 Jonathan Cameron
*
- * 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.
- *
* Companion module to the iio simple dummy example driver.
* The purpose of this is to generate 'fake' event interrupts thus
* allowing that driver's code to be as close as possible to that of
@@ -196,7 +193,10 @@
return ret;
device_initialize(&iio_evgen_dev);
dev_set_name(&iio_evgen_dev, "iio_evgen");
- return device_add(&iio_evgen_dev);
+ ret = device_add(&iio_evgen_dev);
+ if (ret)
+ put_device(&iio_evgen_dev);
+ return ret;
}
module_init(iio_dummy_evgen_init);
diff --git a/drivers/iio/dummy/iio_simple_dummy.c b/drivers/iio/dummy/iio_simple_dummy.c
index 6205247..6cb0229 100644
--- a/drivers/iio/dummy/iio_simple_dummy.c
+++ b/drivers/iio/dummy/iio_simple_dummy.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Copyright (c) 2011 Jonathan Cameron
*
- * 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.
- *
* A reference industrial I/O driver to illustrate the functionality available.
*
* There are numerous real drivers to illustrate the finer points.
@@ -696,9 +693,9 @@
* Varies depending on bus type of the device. As there is no device
* here, call probe directly. For information on device registration
* i2c:
- * Documentation/i2c/writing-clients
+ * Documentation/i2c/writing-clients.rst
* spi:
- * Documentation/spi/spi-summary
+ * Documentation/spi/spi-summary.rst
*/
static const struct iio_sw_device_ops iio_dummy_device_ops = {
.probe = iio_dummy_probe,
diff --git a/drivers/iio/dummy/iio_simple_dummy.h b/drivers/iio/dummy/iio_simple_dummy.h
index f7005c3..a91622a 100644
--- a/drivers/iio/dummy/iio_simple_dummy.h
+++ b/drivers/iio/dummy/iio_simple_dummy.h
@@ -1,10 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/**
* Copyright (c) 2011 Jonathan Cameron
*
- * 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.
- *
* Join together the various functionality of iio_simple_dummy driver
*/
diff --git a/drivers/iio/dummy/iio_simple_dummy_buffer.c b/drivers/iio/dummy/iio_simple_dummy_buffer.c
index 744ca92..17606ec 100644
--- a/drivers/iio/dummy/iio_simple_dummy_buffer.c
+++ b/drivers/iio/dummy/iio_simple_dummy_buffer.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Copyright (c) 2011 Jonathan Cameron
*
- * 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.
- *
* Buffer handling elements of industrial I/O reference driver.
* Uses the kfifo buffer.
*
diff --git a/drivers/iio/dummy/iio_simple_dummy_events.c b/drivers/iio/dummy/iio_simple_dummy_events.c
index 7ec2a0b..b3abaac 100644
--- a/drivers/iio/dummy/iio_simple_dummy_events.c
+++ b/drivers/iio/dummy/iio_simple_dummy_events.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Copyright (c) 2011 Jonathan Cameron
*
- * 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.
- *
* Event handling elements of industrial I/O reference driver.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/frequency/Kconfig b/drivers/iio/frequency/Kconfig
index dc5e0b7..240b815 100644
--- a/drivers/iio/frequency/Kconfig
+++ b/drivers/iio/frequency/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Frequency
# Direct Digital Synthesis drivers (DDS)
@@ -38,5 +39,15 @@
To compile this driver as a module, choose M here: the
module will be called adf4350.
+config ADF4371
+ tristate "Analog Devices ADF4371/ADF4372 Wideband Synthesizers"
+ depends on SPI
+ select REGMAP_SPI
+ help
+ Say yes here to build support for Analog Devices ADF4371 and ADF4372
+ Wideband Synthesizers. The driver provides direct access via sysfs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called adf4371.
endmenu
endmenu
diff --git a/drivers/iio/frequency/Makefile b/drivers/iio/frequency/Makefile
index 2bca03f..518b1e5 100644
--- a/drivers/iio/frequency/Makefile
+++ b/drivers/iio/frequency/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile iio/frequency
#
@@ -5,3 +6,4 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD9523) += ad9523.o
obj-$(CONFIG_ADF4350) += adf4350.o
+obj-$(CONFIG_ADF4371) += adf4371.o
diff --git a/drivers/iio/frequency/ad9523.c b/drivers/iio/frequency/ad9523.c
index f4a5081..a732218 100644
--- a/drivers/iio/frequency/ad9523.c
+++ b/drivers/iio/frequency/ad9523.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AD9523 SPI Low Jitter Clock Generator
*
* Copyright 2012 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -862,9 +861,11 @@
if (ret < 0)
return ret;
- st->vco_freq = (pdata->vcxo_freq * (pdata->pll2_freq_doubler_en ? 2 : 1)
- / pdata->pll2_r2_div) * AD9523_PLL2_FB_NDIV(pdata->
- pll2_ndiv_a_cnt, pdata->pll2_ndiv_b_cnt);
+ st->vco_freq = div_u64((unsigned long long)pdata->vcxo_freq *
+ (pdata->pll2_freq_doubler_en ? 2 : 1) *
+ AD9523_PLL2_FB_NDIV(pdata->pll2_ndiv_a_cnt,
+ pdata->pll2_ndiv_b_cnt),
+ pdata->pll2_r2_div);
ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_CTRL,
AD9523_PLL2_VCO_CALIBRATE);
@@ -872,22 +873,22 @@
return ret;
ret = ad9523_write(indio_dev, AD9523_PLL2_VCO_DIVIDER,
- AD9523_PLL2_VCO_DIV_M1(pdata->pll2_vco_diff_m1) |
- AD9523_PLL2_VCO_DIV_M2(pdata->pll2_vco_diff_m2) |
- AD_IFE(pll2_vco_diff_m1, 0,
+ AD9523_PLL2_VCO_DIV_M1(pdata->pll2_vco_div_m1) |
+ AD9523_PLL2_VCO_DIV_M2(pdata->pll2_vco_div_m2) |
+ AD_IFE(pll2_vco_div_m1, 0,
AD9523_PLL2_VCO_DIV_M1_PWR_DOWN_EN) |
- AD_IFE(pll2_vco_diff_m2, 0,
+ AD_IFE(pll2_vco_div_m2, 0,
AD9523_PLL2_VCO_DIV_M2_PWR_DOWN_EN));
if (ret < 0)
return ret;
- if (pdata->pll2_vco_diff_m1)
+ if (pdata->pll2_vco_div_m1)
st->vco_out_freq[AD9523_VCO1] =
- st->vco_freq / pdata->pll2_vco_diff_m1;
+ st->vco_freq / pdata->pll2_vco_div_m1;
- if (pdata->pll2_vco_diff_m2)
+ if (pdata->pll2_vco_div_m2)
st->vco_out_freq[AD9523_VCO2] =
- st->vco_freq / pdata->pll2_vco_diff_m2;
+ st->vco_freq / pdata->pll2_vco_div_m2;
st->vco_out_freq[AD9523_VCXO] = pdata->vcxo_freq;
@@ -943,11 +944,14 @@
}
}
- for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN)
- ad9523_write(indio_dev,
+ for_each_clear_bit(i, &active_mask, AD9523_NUM_CHAN) {
+ ret = ad9523_write(indio_dev,
AD9523_CHANNEL_CLOCK_DIST(i),
AD9523_CLK_DIST_DRIVER_MODE(TRISTATE) |
AD9523_CLK_DIST_PWR_DOWN_EN);
+ if (ret < 0)
+ return ret;
+ }
ret = ad9523_write(indio_dev, AD9523_POWER_DOWN_CTRL, 0);
if (ret < 0)
@@ -1078,6 +1082,6 @@
};
module_spi_driver(ad9523_driver);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Analog Devices AD9523 CLOCKDIST/PLL");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/frequency/adf4350.c b/drivers/iio/frequency/adf4350.c
index 6d76843..ae0ca09 100644
--- a/drivers/iio/frequency/adf4350.c
+++ b/drivers/iio/frequency/adf4350.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADF4350/ADF4351 SPI Wideband Synthesizer driver
*
* Copyright 2012-2013 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/device.h>
@@ -388,7 +387,7 @@
if (!pdata)
return NULL;
- strncpy(&pdata->name[0], np->name, SPI_NAME_SIZE - 1);
+ snprintf(&pdata->name[0], SPI_NAME_SIZE - 1, "%pOFn", np);
tmp = 10000;
of_property_read_u32(np, "adi,channel-spacing", &tmp);
diff --git a/drivers/iio/frequency/adf4371.c b/drivers/iio/frequency/adf4371.c
new file mode 100644
index 0000000..ff82863
--- /dev/null
+++ b/drivers/iio/frequency/adf4371.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Analog Devices ADF4371 SPI Wideband Synthesizer driver
+ *
+ * Copyright 2019 Analog Devices Inc.
+ */
+#include <linux/bitfield.h>
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/gcd.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/sysfs.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+
+/* Registers address macro */
+#define ADF4371_REG(x) (x)
+
+/* ADF4371_REG0 */
+#define ADF4371_ADDR_ASC_MSK BIT(2)
+#define ADF4371_ADDR_ASC(x) FIELD_PREP(ADF4371_ADDR_ASC_MSK, x)
+#define ADF4371_ADDR_ASC_R_MSK BIT(5)
+#define ADF4371_ADDR_ASC_R(x) FIELD_PREP(ADF4371_ADDR_ASC_R_MSK, x)
+#define ADF4371_RESET_CMD 0x81
+
+/* ADF4371_REG17 */
+#define ADF4371_FRAC2WORD_L_MSK GENMASK(7, 1)
+#define ADF4371_FRAC2WORD_L(x) FIELD_PREP(ADF4371_FRAC2WORD_L_MSK, x)
+#define ADF4371_FRAC1WORD_MSK BIT(0)
+#define ADF4371_FRAC1WORD(x) FIELD_PREP(ADF4371_FRAC1WORD_MSK, x)
+
+/* ADF4371_REG18 */
+#define ADF4371_FRAC2WORD_H_MSK GENMASK(6, 0)
+#define ADF4371_FRAC2WORD_H(x) FIELD_PREP(ADF4371_FRAC2WORD_H_MSK, x)
+
+/* ADF4371_REG1A */
+#define ADF4371_MOD2WORD_MSK GENMASK(5, 0)
+#define ADF4371_MOD2WORD(x) FIELD_PREP(ADF4371_MOD2WORD_MSK, x)
+
+/* ADF4371_REG24 */
+#define ADF4371_RF_DIV_SEL_MSK GENMASK(6, 4)
+#define ADF4371_RF_DIV_SEL(x) FIELD_PREP(ADF4371_RF_DIV_SEL_MSK, x)
+
+/* ADF4371_REG25 */
+#define ADF4371_MUTE_LD_MSK BIT(7)
+#define ADF4371_MUTE_LD(x) FIELD_PREP(ADF4371_MUTE_LD_MSK, x)
+
+/* ADF4371_REG32 */
+#define ADF4371_TIMEOUT_MSK GENMASK(1, 0)
+#define ADF4371_TIMEOUT(x) FIELD_PREP(ADF4371_TIMEOUT_MSK, x)
+
+/* ADF4371_REG34 */
+#define ADF4371_VCO_ALC_TOUT_MSK GENMASK(4, 0)
+#define ADF4371_VCO_ALC_TOUT(x) FIELD_PREP(ADF4371_VCO_ALC_TOUT_MSK, x)
+
+/* Specifications */
+#define ADF4371_MIN_VCO_FREQ 4000000000ULL /* 4000 MHz */
+#define ADF4371_MAX_VCO_FREQ 8000000000ULL /* 8000 MHz */
+#define ADF4371_MAX_OUT_RF8_FREQ ADF4371_MAX_VCO_FREQ /* Hz */
+#define ADF4371_MIN_OUT_RF8_FREQ (ADF4371_MIN_VCO_FREQ / 64) /* Hz */
+#define ADF4371_MAX_OUT_RF16_FREQ (ADF4371_MAX_VCO_FREQ * 2) /* Hz */
+#define ADF4371_MIN_OUT_RF16_FREQ (ADF4371_MIN_VCO_FREQ * 2) /* Hz */
+#define ADF4371_MAX_OUT_RF32_FREQ (ADF4371_MAX_VCO_FREQ * 4) /* Hz */
+#define ADF4371_MIN_OUT_RF32_FREQ (ADF4371_MIN_VCO_FREQ * 4) /* Hz */
+
+#define ADF4371_MAX_FREQ_PFD 250000000UL /* Hz */
+#define ADF4371_MAX_FREQ_REFIN 600000000UL /* Hz */
+
+/* MOD1 is a 24-bit primary modulus with fixed value of 2^25 */
+#define ADF4371_MODULUS1 33554432ULL
+/* MOD2 is the programmable, 14-bit auxiliary fractional modulus */
+#define ADF4371_MAX_MODULUS2 BIT(14)
+
+#define ADF4371_CHECK_RANGE(freq, range) \
+ ((freq > ADF4371_MAX_ ## range) || (freq < ADF4371_MIN_ ## range))
+
+enum {
+ ADF4371_FREQ,
+ ADF4371_POWER_DOWN,
+ ADF4371_CHANNEL_NAME
+};
+
+enum {
+ ADF4371_CH_RF8,
+ ADF4371_CH_RFAUX8,
+ ADF4371_CH_RF16,
+ ADF4371_CH_RF32
+};
+
+enum adf4371_variant {
+ ADF4371,
+ ADF4372
+};
+
+struct adf4371_pwrdown {
+ unsigned int reg;
+ unsigned int bit;
+};
+
+static const char * const adf4371_ch_names[] = {
+ "RF8x", "RFAUX8x", "RF16x", "RF32x"
+};
+
+static const struct adf4371_pwrdown adf4371_pwrdown_ch[4] = {
+ [ADF4371_CH_RF8] = { ADF4371_REG(0x25), 2 },
+ [ADF4371_CH_RFAUX8] = { ADF4371_REG(0x72), 3 },
+ [ADF4371_CH_RF16] = { ADF4371_REG(0x25), 3 },
+ [ADF4371_CH_RF32] = { ADF4371_REG(0x25), 4 },
+};
+
+static const struct reg_sequence adf4371_reg_defaults[] = {
+ { ADF4371_REG(0x0), 0x18 },
+ { ADF4371_REG(0x12), 0x40 },
+ { ADF4371_REG(0x1E), 0x48 },
+ { ADF4371_REG(0x20), 0x14 },
+ { ADF4371_REG(0x22), 0x00 },
+ { ADF4371_REG(0x23), 0x00 },
+ { ADF4371_REG(0x24), 0x80 },
+ { ADF4371_REG(0x25), 0x07 },
+ { ADF4371_REG(0x27), 0xC5 },
+ { ADF4371_REG(0x28), 0x83 },
+ { ADF4371_REG(0x2C), 0x44 },
+ { ADF4371_REG(0x2D), 0x11 },
+ { ADF4371_REG(0x2E), 0x12 },
+ { ADF4371_REG(0x2F), 0x94 },
+ { ADF4371_REG(0x32), 0x04 },
+ { ADF4371_REG(0x35), 0xFA },
+ { ADF4371_REG(0x36), 0x30 },
+ { ADF4371_REG(0x39), 0x07 },
+ { ADF4371_REG(0x3A), 0x55 },
+ { ADF4371_REG(0x3E), 0x0C },
+ { ADF4371_REG(0x3F), 0x80 },
+ { ADF4371_REG(0x40), 0x50 },
+ { ADF4371_REG(0x41), 0x28 },
+ { ADF4371_REG(0x47), 0xC0 },
+ { ADF4371_REG(0x52), 0xF4 },
+ { ADF4371_REG(0x70), 0x03 },
+ { ADF4371_REG(0x71), 0x60 },
+ { ADF4371_REG(0x72), 0x32 },
+};
+
+static const struct regmap_config adf4371_regmap_config = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .read_flag_mask = BIT(7),
+};
+
+struct adf4371_chip_info {
+ unsigned int num_channels;
+ const struct iio_chan_spec *channels;
+};
+
+struct adf4371_state {
+ struct spi_device *spi;
+ struct regmap *regmap;
+ struct clk *clkin;
+ /*
+ * Lock for accessing device registers. Some operations require
+ * multiple consecutive R/W operations, during which the device
+ * shouldn't be interrupted. The buffers are also shared across
+ * all operations so need to be protected on stand alone reads and
+ * writes.
+ */
+ struct mutex lock;
+ const struct adf4371_chip_info *chip_info;
+ unsigned long clkin_freq;
+ unsigned long fpfd;
+ unsigned int integer;
+ unsigned int fract1;
+ unsigned int fract2;
+ unsigned int mod2;
+ unsigned int rf_div_sel;
+ unsigned int ref_div_factor;
+ u8 buf[10] ____cacheline_aligned;
+};
+
+static unsigned long long adf4371_pll_fract_n_get_rate(struct adf4371_state *st,
+ u32 channel)
+{
+ unsigned long long val, tmp;
+ unsigned int ref_div_sel;
+
+ val = (((u64)st->integer * ADF4371_MODULUS1) + st->fract1) * st->fpfd;
+ tmp = (u64)st->fract2 * st->fpfd;
+ do_div(tmp, st->mod2);
+ val += tmp + ADF4371_MODULUS1 / 2;
+
+ if (channel == ADF4371_CH_RF8 || channel == ADF4371_CH_RFAUX8)
+ ref_div_sel = st->rf_div_sel;
+ else
+ ref_div_sel = 0;
+
+ do_div(val, ADF4371_MODULUS1 * (1 << ref_div_sel));
+
+ if (channel == ADF4371_CH_RF16)
+ val <<= 1;
+ else if (channel == ADF4371_CH_RF32)
+ val <<= 2;
+
+ return val;
+}
+
+static void adf4371_pll_fract_n_compute(unsigned long long vco,
+ unsigned long long pfd,
+ unsigned int *integer,
+ unsigned int *fract1,
+ unsigned int *fract2,
+ unsigned int *mod2)
+{
+ unsigned long long tmp;
+ u32 gcd_div;
+
+ tmp = do_div(vco, pfd);
+ tmp = tmp * ADF4371_MODULUS1;
+ *fract2 = do_div(tmp, pfd);
+
+ *integer = vco;
+ *fract1 = tmp;
+
+ *mod2 = pfd;
+
+ while (*mod2 > ADF4371_MAX_MODULUS2) {
+ *mod2 >>= 1;
+ *fract2 >>= 1;
+ }
+
+ gcd_div = gcd(*fract2, *mod2);
+ *mod2 /= gcd_div;
+ *fract2 /= gcd_div;
+}
+
+static int adf4371_set_freq(struct adf4371_state *st, unsigned long long freq,
+ unsigned int channel)
+{
+ u32 cp_bleed;
+ u8 int_mode = 0;
+ int ret;
+
+ switch (channel) {
+ case ADF4371_CH_RF8:
+ case ADF4371_CH_RFAUX8:
+ if (ADF4371_CHECK_RANGE(freq, OUT_RF8_FREQ))
+ return -EINVAL;
+
+ st->rf_div_sel = 0;
+
+ while (freq < ADF4371_MIN_VCO_FREQ) {
+ freq <<= 1;
+ st->rf_div_sel++;
+ }
+ break;
+ case ADF4371_CH_RF16:
+ /* ADF4371 RF16 8000...16000 MHz */
+ if (ADF4371_CHECK_RANGE(freq, OUT_RF16_FREQ))
+ return -EINVAL;
+
+ freq >>= 1;
+ break;
+ case ADF4371_CH_RF32:
+ /* ADF4371 RF32 16000...32000 MHz */
+ if (ADF4371_CHECK_RANGE(freq, OUT_RF32_FREQ))
+ return -EINVAL;
+
+ freq >>= 2;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ adf4371_pll_fract_n_compute(freq, st->fpfd, &st->integer, &st->fract1,
+ &st->fract2, &st->mod2);
+ st->buf[0] = st->integer >> 8;
+ st->buf[1] = 0x40; /* REG12 default */
+ st->buf[2] = 0x00;
+ st->buf[3] = st->fract1 & 0xFF;
+ st->buf[4] = st->fract1 >> 8;
+ st->buf[5] = st->fract1 >> 16;
+ st->buf[6] = ADF4371_FRAC2WORD_L(st->fract2 & 0x7F) |
+ ADF4371_FRAC1WORD(st->fract1 >> 24);
+ st->buf[7] = ADF4371_FRAC2WORD_H(st->fract2 >> 7);
+ st->buf[8] = st->mod2 & 0xFF;
+ st->buf[9] = ADF4371_MOD2WORD(st->mod2 >> 8);
+
+ ret = regmap_bulk_write(st->regmap, ADF4371_REG(0x11), st->buf, 10);
+ if (ret < 0)
+ return ret;
+ /*
+ * The R counter allows the input reference frequency to be
+ * divided down to produce the reference clock to the PFD
+ */
+ ret = regmap_write(st->regmap, ADF4371_REG(0x1F), st->ref_div_factor);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_update_bits(st->regmap, ADF4371_REG(0x24),
+ ADF4371_RF_DIV_SEL_MSK,
+ ADF4371_RF_DIV_SEL(st->rf_div_sel));
+ if (ret < 0)
+ return ret;
+
+ cp_bleed = DIV_ROUND_UP(400 * 1750, st->integer * 375);
+ cp_bleed = clamp(cp_bleed, 1U, 255U);
+ ret = regmap_write(st->regmap, ADF4371_REG(0x26), cp_bleed);
+ if (ret < 0)
+ return ret;
+ /*
+ * Set to 1 when in INT mode (when FRAC1 = FRAC2 = 0),
+ * and set to 0 when in FRAC mode.
+ */
+ if (st->fract1 == 0 && st->fract2 == 0)
+ int_mode = 0x01;
+
+ ret = regmap_write(st->regmap, ADF4371_REG(0x2B), int_mode);
+ if (ret < 0)
+ return ret;
+
+ return regmap_write(st->regmap, ADF4371_REG(0x10), st->integer & 0xFF);
+}
+
+static ssize_t adf4371_read(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ char *buf)
+{
+ struct adf4371_state *st = iio_priv(indio_dev);
+ unsigned long long val = 0;
+ unsigned int readval, reg, bit;
+ int ret;
+
+ switch ((u32)private) {
+ case ADF4371_FREQ:
+ val = adf4371_pll_fract_n_get_rate(st, chan->channel);
+ ret = regmap_read(st->regmap, ADF4371_REG(0x7C), &readval);
+ if (ret < 0)
+ break;
+
+ if (readval == 0x00) {
+ dev_dbg(&st->spi->dev, "PLL un-locked\n");
+ ret = -EBUSY;
+ }
+ break;
+ case ADF4371_POWER_DOWN:
+ reg = adf4371_pwrdown_ch[chan->channel].reg;
+ bit = adf4371_pwrdown_ch[chan->channel].bit;
+
+ ret = regmap_read(st->regmap, reg, &readval);
+ if (ret < 0)
+ break;
+
+ val = !(readval & BIT(bit));
+ break;
+ case ADF4371_CHANNEL_NAME:
+ return sprintf(buf, "%s\n", adf4371_ch_names[chan->channel]);
+ default:
+ ret = -EINVAL;
+ val = 0;
+ break;
+ }
+
+ return ret < 0 ? ret : sprintf(buf, "%llu\n", val);
+}
+
+static ssize_t adf4371_write(struct iio_dev *indio_dev,
+ uintptr_t private,
+ const struct iio_chan_spec *chan,
+ const char *buf, size_t len)
+{
+ struct adf4371_state *st = iio_priv(indio_dev);
+ unsigned long long freq;
+ bool power_down;
+ unsigned int bit, readval, reg;
+ int ret;
+
+ mutex_lock(&st->lock);
+ switch ((u32)private) {
+ case ADF4371_FREQ:
+ ret = kstrtoull(buf, 10, &freq);
+ if (ret)
+ break;
+
+ ret = adf4371_set_freq(st, freq, chan->channel);
+ break;
+ case ADF4371_POWER_DOWN:
+ ret = kstrtobool(buf, &power_down);
+ if (ret)
+ break;
+
+ reg = adf4371_pwrdown_ch[chan->channel].reg;
+ bit = adf4371_pwrdown_ch[chan->channel].bit;
+ ret = regmap_read(st->regmap, reg, &readval);
+ if (ret < 0)
+ break;
+
+ readval &= ~BIT(bit);
+ readval |= (!power_down << bit);
+
+ ret = regmap_write(st->regmap, reg, readval);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+ mutex_unlock(&st->lock);
+
+ return ret ? ret : len;
+}
+
+#define _ADF4371_EXT_INFO(_name, _ident) { \
+ .name = _name, \
+ .read = adf4371_read, \
+ .write = adf4371_write, \
+ .private = _ident, \
+ .shared = IIO_SEPARATE, \
+}
+
+static const struct iio_chan_spec_ext_info adf4371_ext_info[] = {
+ /*
+ * Ideally we use IIO_CHAN_INFO_FREQUENCY, but there are
+ * values > 2^32 in order to support the entire frequency range
+ * in Hz. Using scale is a bit ugly.
+ */
+ _ADF4371_EXT_INFO("frequency", ADF4371_FREQ),
+ _ADF4371_EXT_INFO("powerdown", ADF4371_POWER_DOWN),
+ _ADF4371_EXT_INFO("name", ADF4371_CHANNEL_NAME),
+ { },
+};
+
+#define ADF4371_CHANNEL(index) { \
+ .type = IIO_ALTVOLTAGE, \
+ .output = 1, \
+ .channel = index, \
+ .ext_info = adf4371_ext_info, \
+ .indexed = 1, \
+ }
+
+static const struct iio_chan_spec adf4371_chan[] = {
+ ADF4371_CHANNEL(ADF4371_CH_RF8),
+ ADF4371_CHANNEL(ADF4371_CH_RFAUX8),
+ ADF4371_CHANNEL(ADF4371_CH_RF16),
+ ADF4371_CHANNEL(ADF4371_CH_RF32),
+};
+
+static const struct adf4371_chip_info adf4371_chip_info[] = {
+ [ADF4371] = {
+ .channels = adf4371_chan,
+ .num_channels = 4,
+ },
+ [ADF4372] = {
+ .channels = adf4371_chan,
+ .num_channels = 3,
+ }
+};
+
+static int adf4371_reg_access(struct iio_dev *indio_dev,
+ unsigned int reg,
+ unsigned int writeval,
+ unsigned int *readval)
+{
+ struct adf4371_state *st = iio_priv(indio_dev);
+
+ if (readval)
+ return regmap_read(st->regmap, reg, readval);
+ else
+ return regmap_write(st->regmap, reg, writeval);
+}
+
+static const struct iio_info adf4371_info = {
+ .debugfs_reg_access = &adf4371_reg_access,
+};
+
+static int adf4371_setup(struct adf4371_state *st)
+{
+ unsigned int synth_timeout = 2, timeout = 1, vco_alc_timeout = 1;
+ unsigned int vco_band_div, tmp;
+ int ret;
+
+ /* Perform a software reset */
+ ret = regmap_write(st->regmap, ADF4371_REG(0x0), ADF4371_RESET_CMD);
+ if (ret < 0)
+ return ret;
+
+ ret = regmap_multi_reg_write(st->regmap, adf4371_reg_defaults,
+ ARRAY_SIZE(adf4371_reg_defaults));
+ if (ret < 0)
+ return ret;
+
+ /* Mute to Lock Detect */
+ if (device_property_read_bool(&st->spi->dev, "adi,mute-till-lock-en")) {
+ ret = regmap_update_bits(st->regmap, ADF4371_REG(0x25),
+ ADF4371_MUTE_LD_MSK,
+ ADF4371_MUTE_LD(1));
+ if (ret < 0)
+ return ret;
+ }
+
+ /* Set address in ascending order, so the bulk_write() will work */
+ ret = regmap_update_bits(st->regmap, ADF4371_REG(0x0),
+ ADF4371_ADDR_ASC_MSK | ADF4371_ADDR_ASC_R_MSK,
+ ADF4371_ADDR_ASC(1) | ADF4371_ADDR_ASC_R(1));
+ if (ret < 0)
+ return ret;
+ /*
+ * Calculate and maximize PFD frequency
+ * fPFD = REFIN × ((1 + D)/(R × (1 + T)))
+ * Where D is the REFIN doubler bit, T is the reference divide by 2,
+ * R is the reference division factor
+ * TODO: it is assumed D and T equal 0.
+ */
+ do {
+ st->ref_div_factor++;
+ st->fpfd = st->clkin_freq / st->ref_div_factor;
+ } while (st->fpfd > ADF4371_MAX_FREQ_PFD);
+
+ /* Calculate Timeouts */
+ vco_band_div = DIV_ROUND_UP(st->fpfd, 2400000U);
+
+ tmp = DIV_ROUND_CLOSEST(st->fpfd, 1000000U);
+ do {
+ timeout++;
+ if (timeout > 1023) {
+ timeout = 2;
+ synth_timeout++;
+ }
+ } while (synth_timeout * 1024 + timeout <= 20 * tmp);
+
+ do {
+ vco_alc_timeout++;
+ } while (vco_alc_timeout * 1024 - timeout <= 50 * tmp);
+
+ st->buf[0] = vco_band_div;
+ st->buf[1] = timeout & 0xFF;
+ st->buf[2] = ADF4371_TIMEOUT(timeout >> 8) | 0x04;
+ st->buf[3] = synth_timeout;
+ st->buf[4] = ADF4371_VCO_ALC_TOUT(vco_alc_timeout);
+
+ return regmap_bulk_write(st->regmap, ADF4371_REG(0x30), st->buf, 5);
+}
+
+static void adf4371_clk_disable(void *data)
+{
+ struct adf4371_state *st = data;
+
+ clk_disable_unprepare(st->clkin);
+}
+
+static int adf4371_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct adf4371_state *st;
+ struct regmap *regmap;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_spi(spi, &adf4371_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "Error initializing spi regmap: %ld\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ st = iio_priv(indio_dev);
+ spi_set_drvdata(spi, indio_dev);
+ st->spi = spi;
+ st->regmap = regmap;
+ mutex_init(&st->lock);
+
+ st->chip_info = &adf4371_chip_info[id->driver_data];
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = id->name;
+ indio_dev->info = &adf4371_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+
+ st->clkin = devm_clk_get(&spi->dev, "clkin");
+ if (IS_ERR(st->clkin))
+ return PTR_ERR(st->clkin);
+
+ ret = clk_prepare_enable(st->clkin);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(&spi->dev, adf4371_clk_disable, st);
+ if (ret)
+ return ret;
+
+ st->clkin_freq = clk_get_rate(st->clkin);
+
+ ret = adf4371_setup(st);
+ if (ret < 0) {
+ dev_err(&spi->dev, "ADF4371 setup failed\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id adf4371_id_table[] = {
+ { "adf4371", ADF4371 },
+ { "adf4372", ADF4372 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adf4371_id_table);
+
+static const struct of_device_id adf4371_of_match[] = {
+ { .compatible = "adi,adf4371" },
+ { .compatible = "adi,adf4372" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adf4371_of_match);
+
+static struct spi_driver adf4371_driver = {
+ .driver = {
+ .name = "adf4371",
+ .of_match_table = adf4371_of_match,
+ },
+ .probe = adf4371_probe,
+ .id_table = adf4371_id_table,
+};
+module_spi_driver(adf4371_driver);
+
+MODULE_AUTHOR("Stefan Popa <stefan.popa@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADF4371 SPI PLL");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/gyro/Kconfig b/drivers/iio/gyro/Kconfig
index 3126cf0..95e6f96 100644
--- a/drivers/iio/gyro/Kconfig
+++ b/drivers/iio/gyro/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# IIO Digital Gyroscope Sensor drivers configuration
#
@@ -73,6 +74,28 @@
tristate
select REGMAP_SPI
+config FXAS21002C
+ tristate "NXP FXAS21002C Gyro Sensor"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select FXAS21002C_I2C if (I2C)
+ select FXAS21002C_SPI if (SPI)
+ depends on (I2C || SPI_MASTER)
+ help
+ Say yes here to build support for NXP FXAS21002C Tri-axis Gyro
+ Sensor driver connected via I2C or SPI.
+
+ This driver can also be built as a module. If so, the module
+ will be called fxas21002c_i2c or fxas21002c_spi.
+
+config FXAS21002C_I2C
+ tristate
+ select REGMAP_I2C
+
+config FXAS21002C_SPI
+ tristate
+ select REGMAP_SPI
+
config HID_SENSOR_GYRO_3D
depends on HID_SENSOR_HUB
select IIO_BUFFER
diff --git a/drivers/iio/gyro/Makefile b/drivers/iio/gyro/Makefile
index 295ec78..45cbd5d 100644
--- a/drivers/iio/gyro/Makefile
+++ b/drivers/iio/gyro/Makefile
@@ -12,6 +12,9 @@
obj-$(CONFIG_BMG160) += bmg160_core.o
obj-$(CONFIG_BMG160_I2C) += bmg160_i2c.o
obj-$(CONFIG_BMG160_SPI) += bmg160_spi.o
+obj-$(CONFIG_FXAS21002C) += fxas21002c_core.o
+obj-$(CONFIG_FXAS21002C_I2C) += fxas21002c_i2c.o
+obj-$(CONFIG_FXAS21002C_SPI) += fxas21002c_spi.o
obj-$(CONFIG_HID_SENSOR_GYRO_3D) += hid-sensor-gyro-3d.o
diff --git a/drivers/iio/gyro/adis16080.c b/drivers/iio/gyro/adis16080.c
index a551ebd..236220d 100644
--- a/drivers/iio/gyro/adis16080.c
+++ b/drivers/iio/gyro/adis16080.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADIS16080/100 Yaw Rate Gyroscope with SPI driver
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/delay.h>
#include <linux/mutex.h>
diff --git a/drivers/iio/gyro/adis16130.c b/drivers/iio/gyro/adis16130.c
index aea80ab..de3f66f 100644
--- a/drivers/iio/gyro/adis16130.c
+++ b/drivers/iio/gyro/adis16130.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADIS16130 Digital Output, High Precision Angular Rate Sensor driver
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/mutex.h>
diff --git a/drivers/iio/gyro/adis16136.c b/drivers/iio/gyro/adis16136.c
index 605eee2..5bec7ad 100644
--- a/drivers/iio/gyro/adis16136.c
+++ b/drivers/iio/gyro/adis16136.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADIS16133/ADIS16135/ADIS16136 gyroscope driver
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
diff --git a/drivers/iio/gyro/adis16260.c b/drivers/iio/gyro/adis16260.c
index a8cb1ca..207a0ce 100644
--- a/drivers/iio/gyro/adis16260.c
+++ b/drivers/iio/gyro/adis16260.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ADIS16260/ADIS16265 Programmable Digital Gyroscope Sensor Driver
*
* Copyright 2010 Analog Devices Inc.
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/interrupt.h>
diff --git a/drivers/iio/gyro/adxrs450.c b/drivers/iio/gyro/adxrs450.c
index 5d39fd0..b00c0eb 100644
--- a/drivers/iio/gyro/adxrs450.c
+++ b/drivers/iio/gyro/adxrs450.c
@@ -1,9 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADXRS450/ADXRS453 Digital Output Gyroscope Driver
*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
*/
#include <linux/interrupt.h>
diff --git a/drivers/iio/gyro/bmg160_core.c b/drivers/iio/gyro/bmg160_core.c
index 63ca316..428ddfc 100644
--- a/drivers/iio/gyro/bmg160_core.c
+++ b/drivers/iio/gyro/bmg160_core.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* BMG160 Gyro Sensor driver
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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>
@@ -102,6 +94,7 @@
struct regmap *regmap;
struct iio_trigger *dready_trig;
struct iio_trigger *motion_trig;
+ struct iio_mount_matrix orientation;
struct mutex mutex;
s16 buffer[8];
u32 dps_range;
@@ -582,11 +575,10 @@
case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
return bmg160_get_filter(data, val);
case IIO_CHAN_INFO_SCALE:
- *val = 0;
switch (chan->type) {
case IIO_TEMP:
- *val2 = 500000;
- return IIO_VAL_INT_PLUS_MICRO;
+ *val = 500;
+ return IIO_VAL_INT;
case IIO_ANGL_VEL:
{
int i;
@@ -594,6 +586,7 @@
for (i = 0; i < ARRAY_SIZE(bmg160_scale_table); ++i) {
if (bmg160_scale_table[i].dps_range ==
data->dps_range) {
+ *val = 0;
*val2 = bmg160_scale_table[i].scale;
return IIO_VAL_INT_PLUS_MICRO;
}
@@ -794,6 +787,20 @@
return 0;
}
+static const struct iio_mount_matrix *
+bmg160_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct bmg160_data *data = iio_priv(indio_dev);
+
+ return &data->orientation;
+}
+
+static const struct iio_chan_spec_ext_info bmg160_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bmg160_get_mount_matrix),
+ { }
+};
+
static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("100 200 400 1000 2000");
static IIO_CONST_ATTR(in_anglvel_scale_available,
@@ -831,6 +838,7 @@
.storagebits = 16, \
.endianness = IIO_LE, \
}, \
+ .ext_info = bmg160_ext_info, \
.event_spec = &bmg160_event, \
.num_event_specs = 1 \
}
@@ -1075,6 +1083,11 @@
data->irq = irq;
data->regmap = regmap;
+ ret = iio_read_mount_matrix(dev, "mount-matrix",
+ &data->orientation);
+ if (ret)
+ return ret;
+
ret = bmg160_chip_init(data);
if (ret < 0)
return ret;
diff --git a/drivers/iio/gyro/bmg160_i2c.c b/drivers/iio/gyro/bmg160_i2c.c
index 90126a5..4fc9c6a 100644
--- a/drivers/iio/gyro/bmg160_i2c.c
+++ b/drivers/iio/gyro/bmg160_i2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/i2c.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
@@ -54,10 +55,19 @@
MODULE_DEVICE_TABLE(i2c, bmg160_i2c_id);
+static const struct of_device_id bmg160_of_match[] = {
+ { .compatible = "bosch,bmg160" },
+ { .compatible = "bosch,bmi055_gyro" },
+ { }
+};
+
+MODULE_DEVICE_TABLE(of, bmg160_of_match);
+
static struct i2c_driver bmg160_i2c_driver = {
.driver = {
.name = "bmg160_i2c",
.acpi_match_table = ACPI_PTR(bmg160_acpi_match),
+ .of_match_table = bmg160_of_match,
.pm = &bmg160_pm_ops,
},
.probe = bmg160_i2c_probe,
diff --git a/drivers/iio/gyro/bmg160_spi.c b/drivers/iio/gyro/bmg160_spi.c
index 021ea5f..182a59c 100644
--- a/drivers/iio/gyro/bmg160_spi.c
+++ b/drivers/iio/gyro/bmg160_spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/spi/spi.h>
#include <linux/regmap.h>
#include <linux/iio/iio.h>
diff --git a/drivers/iio/gyro/fxas21002c.h b/drivers/iio/gyro/fxas21002c.h
new file mode 100644
index 0000000..566d92d
--- /dev/null
+++ b/drivers/iio/gyro/fxas21002c.h
@@ -0,0 +1,150 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Driver for NXP FXAS21002C Gyroscope - Header
+ *
+ * Copyright (C) 2019 Linaro Ltd.
+ */
+
+#ifndef FXAS21002C_H_
+#define FXAS21002C_H_
+
+#include <linux/regmap.h>
+
+#define FXAS21002C_REG_STATUS 0x00
+#define FXAS21002C_REG_OUT_X_MSB 0x01
+#define FXAS21002C_REG_OUT_X_LSB 0x02
+#define FXAS21002C_REG_OUT_Y_MSB 0x03
+#define FXAS21002C_REG_OUT_Y_LSB 0x04
+#define FXAS21002C_REG_OUT_Z_MSB 0x05
+#define FXAS21002C_REG_OUT_Z_LSB 0x06
+#define FXAS21002C_REG_DR_STATUS 0x07
+#define FXAS21002C_REG_F_STATUS 0x08
+#define FXAS21002C_REG_F_SETUP 0x09
+#define FXAS21002C_REG_F_EVENT 0x0A
+#define FXAS21002C_REG_INT_SRC_FLAG 0x0B
+#define FXAS21002C_REG_WHO_AM_I 0x0C
+#define FXAS21002C_REG_CTRL0 0x0D
+#define FXAS21002C_REG_RT_CFG 0x0E
+#define FXAS21002C_REG_RT_SRC 0x0F
+#define FXAS21002C_REG_RT_THS 0x10
+#define FXAS21002C_REG_RT_COUNT 0x11
+#define FXAS21002C_REG_TEMP 0x12
+#define FXAS21002C_REG_CTRL1 0x13
+#define FXAS21002C_REG_CTRL2 0x14
+#define FXAS21002C_REG_CTRL3 0x15
+
+enum fxas21002c_fields {
+ F_DR_STATUS,
+ F_OUT_X_MSB,
+ F_OUT_X_LSB,
+ F_OUT_Y_MSB,
+ F_OUT_Y_LSB,
+ F_OUT_Z_MSB,
+ F_OUT_Z_LSB,
+ /* DR_STATUS */
+ F_ZYX_OW, F_Z_OW, F_Y_OW, F_X_OW, F_ZYX_DR, F_Z_DR, F_Y_DR, F_X_DR,
+ /* F_STATUS */
+ F_OVF, F_WMKF, F_CNT,
+ /* F_SETUP */
+ F_MODE, F_WMRK,
+ /* F_EVENT */
+ F_EVENT, FE_TIME,
+ /* INT_SOURCE_FLAG */
+ F_BOOTEND, F_SRC_FIFO, F_SRC_RT, F_SRC_DRDY,
+ /* WHO_AM_I */
+ F_WHO_AM_I,
+ /* CTRL_REG0 */
+ F_BW, F_SPIW, F_SEL, F_HPF_EN, F_FS,
+ /* RT_CFG */
+ F_ELE, F_ZTEFE, F_YTEFE, F_XTEFE,
+ /* RT_SRC */
+ F_EA, F_ZRT, F_ZRT_POL, F_YRT, F_YRT_POL, F_XRT, F_XRT_POL,
+ /* RT_THS */
+ F_DBCNTM, F_THS,
+ /* RT_COUNT */
+ F_RT_COUNT,
+ /* TEMP */
+ F_TEMP,
+ /* CTRL_REG1 */
+ F_RST, F_ST, F_DR, F_ACTIVE, F_READY,
+ /* CTRL_REG2 */
+ F_INT_CFG_FIFO, F_INT_EN_FIFO, F_INT_CFG_RT, F_INT_EN_RT,
+ F_INT_CFG_DRDY, F_INT_EN_DRDY, F_IPOL, F_PP_OD,
+ /* CTRL_REG3 */
+ F_WRAPTOONE, F_EXTCTRLEN, F_FS_DOUBLE,
+ /* MAX FIELDS */
+ F_MAX_FIELDS,
+};
+
+static const struct reg_field fxas21002c_reg_fields[] = {
+ [F_DR_STATUS] = REG_FIELD(FXAS21002C_REG_STATUS, 0, 7),
+ [F_OUT_X_MSB] = REG_FIELD(FXAS21002C_REG_OUT_X_MSB, 0, 7),
+ [F_OUT_X_LSB] = REG_FIELD(FXAS21002C_REG_OUT_X_LSB, 0, 7),
+ [F_OUT_Y_MSB] = REG_FIELD(FXAS21002C_REG_OUT_Y_MSB, 0, 7),
+ [F_OUT_Y_LSB] = REG_FIELD(FXAS21002C_REG_OUT_Y_LSB, 0, 7),
+ [F_OUT_Z_MSB] = REG_FIELD(FXAS21002C_REG_OUT_Z_MSB, 0, 7),
+ [F_OUT_Z_LSB] = REG_FIELD(FXAS21002C_REG_OUT_Z_LSB, 0, 7),
+ [F_ZYX_OW] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 7, 7),
+ [F_Z_OW] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 6, 6),
+ [F_Y_OW] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 5, 5),
+ [F_X_OW] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 4, 4),
+ [F_ZYX_DR] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 3, 3),
+ [F_Z_DR] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 2, 2),
+ [F_Y_DR] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 1, 1),
+ [F_X_DR] = REG_FIELD(FXAS21002C_REG_DR_STATUS, 0, 0),
+ [F_OVF] = REG_FIELD(FXAS21002C_REG_F_STATUS, 7, 7),
+ [F_WMKF] = REG_FIELD(FXAS21002C_REG_F_STATUS, 6, 6),
+ [F_CNT] = REG_FIELD(FXAS21002C_REG_F_STATUS, 0, 5),
+ [F_MODE] = REG_FIELD(FXAS21002C_REG_F_SETUP, 6, 7),
+ [F_WMRK] = REG_FIELD(FXAS21002C_REG_F_SETUP, 0, 5),
+ [F_EVENT] = REG_FIELD(FXAS21002C_REG_F_EVENT, 5, 5),
+ [FE_TIME] = REG_FIELD(FXAS21002C_REG_F_EVENT, 0, 4),
+ [F_BOOTEND] = REG_FIELD(FXAS21002C_REG_INT_SRC_FLAG, 3, 3),
+ [F_SRC_FIFO] = REG_FIELD(FXAS21002C_REG_INT_SRC_FLAG, 2, 2),
+ [F_SRC_RT] = REG_FIELD(FXAS21002C_REG_INT_SRC_FLAG, 1, 1),
+ [F_SRC_DRDY] = REG_FIELD(FXAS21002C_REG_INT_SRC_FLAG, 0, 0),
+ [F_WHO_AM_I] = REG_FIELD(FXAS21002C_REG_WHO_AM_I, 0, 7),
+ [F_BW] = REG_FIELD(FXAS21002C_REG_CTRL0, 6, 7),
+ [F_SPIW] = REG_FIELD(FXAS21002C_REG_CTRL0, 5, 5),
+ [F_SEL] = REG_FIELD(FXAS21002C_REG_CTRL0, 3, 4),
+ [F_HPF_EN] = REG_FIELD(FXAS21002C_REG_CTRL0, 2, 2),
+ [F_FS] = REG_FIELD(FXAS21002C_REG_CTRL0, 0, 1),
+ [F_ELE] = REG_FIELD(FXAS21002C_REG_RT_CFG, 3, 3),
+ [F_ZTEFE] = REG_FIELD(FXAS21002C_REG_RT_CFG, 2, 2),
+ [F_YTEFE] = REG_FIELD(FXAS21002C_REG_RT_CFG, 1, 1),
+ [F_XTEFE] = REG_FIELD(FXAS21002C_REG_RT_CFG, 0, 0),
+ [F_EA] = REG_FIELD(FXAS21002C_REG_RT_SRC, 6, 6),
+ [F_ZRT] = REG_FIELD(FXAS21002C_REG_RT_SRC, 5, 5),
+ [F_ZRT_POL] = REG_FIELD(FXAS21002C_REG_RT_SRC, 4, 4),
+ [F_YRT] = REG_FIELD(FXAS21002C_REG_RT_SRC, 3, 3),
+ [F_YRT_POL] = REG_FIELD(FXAS21002C_REG_RT_SRC, 2, 2),
+ [F_XRT] = REG_FIELD(FXAS21002C_REG_RT_SRC, 1, 1),
+ [F_XRT_POL] = REG_FIELD(FXAS21002C_REG_RT_SRC, 0, 0),
+ [F_DBCNTM] = REG_FIELD(FXAS21002C_REG_RT_THS, 7, 7),
+ [F_THS] = REG_FIELD(FXAS21002C_REG_RT_SRC, 0, 6),
+ [F_RT_COUNT] = REG_FIELD(FXAS21002C_REG_RT_COUNT, 0, 7),
+ [F_TEMP] = REG_FIELD(FXAS21002C_REG_TEMP, 0, 7),
+ [F_RST] = REG_FIELD(FXAS21002C_REG_CTRL1, 6, 6),
+ [F_ST] = REG_FIELD(FXAS21002C_REG_CTRL1, 5, 5),
+ [F_DR] = REG_FIELD(FXAS21002C_REG_CTRL1, 2, 4),
+ [F_ACTIVE] = REG_FIELD(FXAS21002C_REG_CTRL1, 1, 1),
+ [F_READY] = REG_FIELD(FXAS21002C_REG_CTRL1, 0, 0),
+ [F_INT_CFG_FIFO] = REG_FIELD(FXAS21002C_REG_CTRL2, 7, 7),
+ [F_INT_EN_FIFO] = REG_FIELD(FXAS21002C_REG_CTRL2, 6, 6),
+ [F_INT_CFG_RT] = REG_FIELD(FXAS21002C_REG_CTRL2, 5, 5),
+ [F_INT_EN_RT] = REG_FIELD(FXAS21002C_REG_CTRL2, 4, 4),
+ [F_INT_CFG_DRDY] = REG_FIELD(FXAS21002C_REG_CTRL2, 3, 3),
+ [F_INT_EN_DRDY] = REG_FIELD(FXAS21002C_REG_CTRL2, 2, 2),
+ [F_IPOL] = REG_FIELD(FXAS21002C_REG_CTRL2, 1, 1),
+ [F_PP_OD] = REG_FIELD(FXAS21002C_REG_CTRL2, 0, 0),
+ [F_WRAPTOONE] = REG_FIELD(FXAS21002C_REG_CTRL3, 3, 3),
+ [F_EXTCTRLEN] = REG_FIELD(FXAS21002C_REG_CTRL3, 2, 2),
+ [F_FS_DOUBLE] = REG_FIELD(FXAS21002C_REG_CTRL3, 0, 0),
+};
+
+extern const struct dev_pm_ops fxas21002c_pm_ops;
+
+int fxas21002c_core_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name);
+void fxas21002c_core_remove(struct device *dev);
+#endif
diff --git a/drivers/iio/gyro/fxas21002c_core.c b/drivers/iio/gyro/fxas21002c_core.c
new file mode 100644
index 0000000..89d2bb2
--- /dev/null
+++ b/drivers/iio/gyro/fxas21002c_core.c
@@ -0,0 +1,1004 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for NXP FXAS21002C Gyroscope - Core
+ *
+ * Copyright (C) 2019 Linaro Ltd.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "fxas21002c.h"
+
+#define FXAS21002C_CHIP_ID_1 0xD6
+#define FXAS21002C_CHIP_ID_2 0xD7
+
+enum fxas21002c_mode_state {
+ FXAS21002C_MODE_STANDBY,
+ FXAS21002C_MODE_READY,
+ FXAS21002C_MODE_ACTIVE,
+};
+
+#define FXAS21002C_STANDBY_ACTIVE_TIME_MS 62
+#define FXAS21002C_READY_ACTIVE_TIME_MS 7
+
+#define FXAS21002C_ODR_LIST_MAX 10
+
+#define FXAS21002C_SCALE_FRACTIONAL 32
+#define FXAS21002C_RANGE_LIMIT_DOUBLE 2000
+
+#define FXAS21002C_AXIS_TO_REG(axis) (FXAS21002C_REG_OUT_X_MSB + ((axis) * 2))
+
+static const int fxas21002c_odr_values[] = {
+ 800, 400, 200, 100, 50, 25, 12, 12
+};
+
+/*
+ * These values are taken from the low-pass filter cutoff frequency calculated
+ * ODR * 0.lpf_values. So, for ODR = 800Hz with a lpf value = 0.32
+ * => LPF cutoff frequency = 800 * 0.32 = 256 Hz
+ */
+static const int fxas21002c_lpf_values[] = {
+ 32, 16, 8
+};
+
+/*
+ * These values are taken from the high-pass filter cutoff frequency calculated
+ * ODR * 0.0hpf_values. So, for ODR = 800Hz with a hpf value = 0.018750
+ * => HPF cutoff frequency = 800 * 0.018750 = 15 Hz
+ */
+static const int fxas21002c_hpf_values[] = {
+ 18750, 9625, 4875, 2475
+};
+
+static const int fxas21002c_range_values[] = {
+ 4000, 2000, 1000, 500, 250
+};
+
+struct fxas21002c_data {
+ u8 chip_id;
+ enum fxas21002c_mode_state mode;
+ enum fxas21002c_mode_state prev_mode;
+
+ struct mutex lock; /* serialize data access */
+ struct regmap *regmap;
+ struct regmap_field *regmap_fields[F_MAX_FIELDS];
+ struct iio_trigger *dready_trig;
+ s64 timestamp;
+ int irq;
+
+ struct regulator *vdd;
+ struct regulator *vddio;
+
+ /*
+ * DMA (thus cache coherency maintenance) requires the
+ * transfer buffers to live in their own cache lines.
+ */
+ s16 buffer[8] ____cacheline_aligned;
+};
+
+enum fxas21002c_channel_index {
+ CHANNEL_SCAN_INDEX_X,
+ CHANNEL_SCAN_INDEX_Y,
+ CHANNEL_SCAN_INDEX_Z,
+ CHANNEL_SCAN_MAX,
+};
+
+static int fxas21002c_odr_hz_from_value(struct fxas21002c_data *data, u8 value)
+{
+ int odr_value_max = ARRAY_SIZE(fxas21002c_odr_values) - 1;
+
+ value = min_t(u8, value, odr_value_max);
+
+ return fxas21002c_odr_values[value];
+}
+
+static int fxas21002c_odr_value_from_hz(struct fxas21002c_data *data,
+ unsigned int hz)
+{
+ int odr_table_size = ARRAY_SIZE(fxas21002c_odr_values);
+ int i;
+
+ for (i = 0; i < odr_table_size; i++)
+ if (fxas21002c_odr_values[i] == hz)
+ return i;
+
+ return -EINVAL;
+}
+
+static int fxas21002c_lpf_bw_from_value(struct fxas21002c_data *data, u8 value)
+{
+ int lpf_value_max = ARRAY_SIZE(fxas21002c_lpf_values) - 1;
+
+ value = min_t(u8, value, lpf_value_max);
+
+ return fxas21002c_lpf_values[value];
+}
+
+static int fxas21002c_lpf_value_from_bw(struct fxas21002c_data *data,
+ unsigned int hz)
+{
+ int lpf_table_size = ARRAY_SIZE(fxas21002c_lpf_values);
+ int i;
+
+ for (i = 0; i < lpf_table_size; i++)
+ if (fxas21002c_lpf_values[i] == hz)
+ return i;
+
+ return -EINVAL;
+}
+
+static int fxas21002c_hpf_sel_from_value(struct fxas21002c_data *data, u8 value)
+{
+ int hpf_value_max = ARRAY_SIZE(fxas21002c_hpf_values) - 1;
+
+ value = min_t(u8, value, hpf_value_max);
+
+ return fxas21002c_hpf_values[value];
+}
+
+static int fxas21002c_hpf_value_from_sel(struct fxas21002c_data *data,
+ unsigned int hz)
+{
+ int hpf_table_size = ARRAY_SIZE(fxas21002c_hpf_values);
+ int i;
+
+ for (i = 0; i < hpf_table_size; i++)
+ if (fxas21002c_hpf_values[i] == hz)
+ return i;
+
+ return -EINVAL;
+}
+
+static int fxas21002c_range_fs_from_value(struct fxas21002c_data *data,
+ u8 value)
+{
+ int range_value_max = ARRAY_SIZE(fxas21002c_range_values) - 1;
+ unsigned int fs_double;
+ int ret;
+
+ /* We need to check if FS_DOUBLE is enabled to offset the value */
+ ret = regmap_field_read(data->regmap_fields[F_FS_DOUBLE], &fs_double);
+ if (ret < 0)
+ return ret;
+
+ if (!fs_double)
+ value += 1;
+
+ value = min_t(u8, value, range_value_max);
+
+ return fxas21002c_range_values[value];
+}
+
+static int fxas21002c_range_value_from_fs(struct fxas21002c_data *data,
+ unsigned int range)
+{
+ int range_table_size = ARRAY_SIZE(fxas21002c_range_values);
+ bool found = false;
+ int fs_double = 0;
+ int ret;
+ int i;
+
+ for (i = 0; i < range_table_size; i++)
+ if (fxas21002c_range_values[i] == range) {
+ found = true;
+ break;
+ }
+
+ if (!found)
+ return -EINVAL;
+
+ if (range > FXAS21002C_RANGE_LIMIT_DOUBLE)
+ fs_double = 1;
+
+ ret = regmap_field_write(data->regmap_fields[F_FS_DOUBLE], fs_double);
+ if (ret < 0)
+ return ret;
+
+ return i;
+}
+
+static int fxas21002c_mode_get(struct fxas21002c_data *data)
+{
+ unsigned int active;
+ unsigned int ready;
+ int ret;
+
+ ret = regmap_field_read(data->regmap_fields[F_ACTIVE], &active);
+ if (ret < 0)
+ return ret;
+ if (active)
+ return FXAS21002C_MODE_ACTIVE;
+
+ ret = regmap_field_read(data->regmap_fields[F_READY], &ready);
+ if (ret < 0)
+ return ret;
+ if (ready)
+ return FXAS21002C_MODE_READY;
+
+ return FXAS21002C_MODE_STANDBY;
+}
+
+static int fxas21002c_mode_set(struct fxas21002c_data *data,
+ enum fxas21002c_mode_state mode)
+{
+ int ret;
+
+ if (mode == data->mode)
+ return 0;
+
+ if (mode == FXAS21002C_MODE_READY)
+ ret = regmap_field_write(data->regmap_fields[F_READY], 1);
+ else
+ ret = regmap_field_write(data->regmap_fields[F_READY], 0);
+ if (ret < 0)
+ return ret;
+
+ if (mode == FXAS21002C_MODE_ACTIVE)
+ ret = regmap_field_write(data->regmap_fields[F_ACTIVE], 1);
+ else
+ ret = regmap_field_write(data->regmap_fields[F_ACTIVE], 0);
+ if (ret < 0)
+ return ret;
+
+ /* if going to active wait the setup times */
+ if (mode == FXAS21002C_MODE_ACTIVE &&
+ data->mode == FXAS21002C_MODE_STANDBY)
+ msleep_interruptible(FXAS21002C_STANDBY_ACTIVE_TIME_MS);
+
+ if (data->mode == FXAS21002C_MODE_READY)
+ msleep_interruptible(FXAS21002C_READY_ACTIVE_TIME_MS);
+
+ data->prev_mode = data->mode;
+ data->mode = mode;
+
+ return ret;
+}
+
+static int fxas21002c_write(struct fxas21002c_data *data,
+ enum fxas21002c_fields field, int bits)
+{
+ int actual_mode;
+ int ret;
+
+ mutex_lock(&data->lock);
+
+ actual_mode = fxas21002c_mode_get(data);
+ if (actual_mode < 0) {
+ ret = actual_mode;
+ goto out_unlock;
+ }
+
+ ret = fxas21002c_mode_set(data, FXAS21002C_MODE_READY);
+ if (ret < 0)
+ goto out_unlock;
+
+ ret = regmap_field_write(data->regmap_fields[field], bits);
+ if (ret < 0)
+ goto out_unlock;
+
+ ret = fxas21002c_mode_set(data, data->prev_mode);
+
+out_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int fxas21002c_pm_get(struct fxas21002c_data *data)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ int ret;
+
+ ret = pm_runtime_get_sync(dev);
+ if (ret < 0)
+ pm_runtime_put_noidle(dev);
+
+ return ret;
+}
+
+static int fxas21002c_pm_put(struct fxas21002c_data *data)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+
+ pm_runtime_mark_last_busy(dev);
+
+ return pm_runtime_put_autosuspend(dev);
+}
+
+static int fxas21002c_temp_get(struct fxas21002c_data *data, int *val)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ unsigned int temp;
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = fxas21002c_pm_get(data);
+ if (ret < 0)
+ goto data_unlock;
+
+ ret = regmap_field_read(data->regmap_fields[F_TEMP], &temp);
+ if (ret < 0) {
+ dev_err(dev, "failed to read temp: %d\n", ret);
+ goto data_unlock;
+ }
+
+ *val = sign_extend32(temp, 7);
+
+ ret = fxas21002c_pm_put(data);
+ if (ret < 0)
+ goto data_unlock;
+
+ ret = IIO_VAL_INT;
+
+data_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int fxas21002c_axis_get(struct fxas21002c_data *data,
+ int index, int *val)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ __be16 axis_be;
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = fxas21002c_pm_get(data);
+ if (ret < 0)
+ goto data_unlock;
+
+ ret = regmap_bulk_read(data->regmap, FXAS21002C_AXIS_TO_REG(index),
+ &axis_be, sizeof(axis_be));
+ if (ret < 0) {
+ dev_err(dev, "failed to read axis: %d: %d\n", index, ret);
+ goto data_unlock;
+ }
+
+ *val = sign_extend32(be16_to_cpu(axis_be), 15);
+
+ ret = fxas21002c_pm_put(data);
+ if (ret < 0)
+ goto data_unlock;
+
+ ret = IIO_VAL_INT;
+
+data_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int fxas21002c_odr_get(struct fxas21002c_data *data, int *odr)
+{
+ unsigned int odr_bits;
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_field_read(data->regmap_fields[F_DR], &odr_bits);
+ if (ret < 0)
+ goto data_unlock;
+
+ *odr = fxas21002c_odr_hz_from_value(data, odr_bits);
+
+ ret = IIO_VAL_INT;
+
+data_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int fxas21002c_odr_set(struct fxas21002c_data *data, int odr)
+{
+ int odr_bits;
+
+ odr_bits = fxas21002c_odr_value_from_hz(data, odr);
+ if (odr_bits < 0)
+ return odr_bits;
+
+ return fxas21002c_write(data, F_DR, odr_bits);
+}
+
+static int fxas21002c_lpf_get(struct fxas21002c_data *data, int *val2)
+{
+ unsigned int bw_bits;
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_field_read(data->regmap_fields[F_BW], &bw_bits);
+ if (ret < 0)
+ goto data_unlock;
+
+ *val2 = fxas21002c_lpf_bw_from_value(data, bw_bits) * 10000;
+
+ ret = IIO_VAL_INT_PLUS_MICRO;
+
+data_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int fxas21002c_lpf_set(struct fxas21002c_data *data, int bw)
+{
+ int bw_bits;
+ int odr;
+ int ret;
+
+ bw_bits = fxas21002c_lpf_value_from_bw(data, bw);
+ if (bw_bits < 0)
+ return bw_bits;
+
+ /*
+ * From table 33 of the device spec, for ODR = 25Hz and 12.5 value 0.08
+ * is not allowed and for ODR = 12.5 value 0.16 is also not allowed
+ */
+ ret = fxas21002c_odr_get(data, &odr);
+ if (ret < 0)
+ return -EINVAL;
+
+ if ((odr == 25 && bw_bits > 0x01) || (odr == 12 && bw_bits > 0))
+ return -EINVAL;
+
+ return fxas21002c_write(data, F_BW, bw_bits);
+}
+
+static int fxas21002c_hpf_get(struct fxas21002c_data *data, int *val2)
+{
+ unsigned int sel_bits;
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_field_read(data->regmap_fields[F_SEL], &sel_bits);
+ if (ret < 0)
+ goto data_unlock;
+
+ *val2 = fxas21002c_hpf_sel_from_value(data, sel_bits);
+
+ ret = IIO_VAL_INT_PLUS_MICRO;
+
+data_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int fxas21002c_hpf_set(struct fxas21002c_data *data, int sel)
+{
+ int sel_bits;
+
+ sel_bits = fxas21002c_hpf_value_from_sel(data, sel);
+ if (sel_bits < 0)
+ return sel_bits;
+
+ return fxas21002c_write(data, F_SEL, sel_bits);
+}
+
+static int fxas21002c_scale_get(struct fxas21002c_data *data, int *val)
+{
+ int fs_bits;
+ int scale;
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_field_read(data->regmap_fields[F_FS], &fs_bits);
+ if (ret < 0)
+ goto data_unlock;
+
+ scale = fxas21002c_range_fs_from_value(data, fs_bits);
+ if (scale < 0) {
+ ret = scale;
+ goto data_unlock;
+ }
+
+ *val = scale;
+
+data_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static int fxas21002c_scale_set(struct fxas21002c_data *data, int range)
+{
+ int fs_bits;
+
+ fs_bits = fxas21002c_range_value_from_fs(data, range);
+ if (fs_bits < 0)
+ return fs_bits;
+
+ return fxas21002c_write(data, F_FS, fs_bits);
+}
+
+static int fxas21002c_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct fxas21002c_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_TEMP:
+ return fxas21002c_temp_get(data, val);
+ case IIO_ANGL_VEL:
+ return fxas21002c_axis_get(data, chan->scan_index, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val2 = FXAS21002C_SCALE_FRACTIONAL;
+ ret = fxas21002c_scale_get(data, val);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ *val = 0;
+ return fxas21002c_lpf_get(data, val2);
+ case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
+ *val = 0;
+ return fxas21002c_hpf_get(data, val2);
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val2 = 0;
+ return fxas21002c_odr_get(data, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int fxas21002c_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct fxas21002c_data *data = iio_priv(indio_dev);
+ int range;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val2)
+ return -EINVAL;
+
+ return fxas21002c_odr_set(data, val);
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ if (val)
+ return -EINVAL;
+
+ val2 = val2 / 10000;
+ return fxas21002c_lpf_set(data, val2);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ range = (((val * 1000 + val2 / 1000) *
+ FXAS21002C_SCALE_FRACTIONAL) / 1000);
+ return fxas21002c_scale_set(data, range);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY:
+ return fxas21002c_hpf_set(data, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("12.5 25 50 100 200 400 800");
+
+static IIO_CONST_ATTR(in_anglvel_filter_low_pass_3db_frequency_available,
+ "0.32 0.16 0.08");
+
+static IIO_CONST_ATTR(in_anglvel_filter_high_pass_3db_frequency_available,
+ "0.018750 0.009625 0.004875 0.002475");
+
+static IIO_CONST_ATTR(in_anglvel_scale_available,
+ "125.0 62.5 31.25 15.625 7.8125");
+
+static struct attribute *fxas21002c_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ &iio_const_attr_in_anglvel_filter_low_pass_3db_frequency_available.dev_attr.attr,
+ &iio_const_attr_in_anglvel_filter_high_pass_3db_frequency_available.dev_attr.attr,
+ &iio_const_attr_in_anglvel_scale_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group fxas21002c_attrs_group = {
+ .attrs = fxas21002c_attributes,
+};
+
+#define FXAS21002C_CHANNEL(_axis) { \
+ .type = IIO_ANGL_VEL, \
+ .modified = 1, \
+ .channel2 = IIO_MOD_##_axis, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY) | \
+ BIT(IIO_CHAN_INFO_HIGH_PASS_FILTER_3DB_FREQUENCY) | \
+ BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .scan_index = CHANNEL_SCAN_INDEX_##_axis, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+}
+
+static const struct iio_chan_spec fxas21002c_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .scan_index = -1,
+ },
+ FXAS21002C_CHANNEL(X),
+ FXAS21002C_CHANNEL(Y),
+ FXAS21002C_CHANNEL(Z),
+};
+
+static const struct iio_info fxas21002c_info = {
+ .attrs = &fxas21002c_attrs_group,
+ .read_raw = &fxas21002c_read_raw,
+ .write_raw = &fxas21002c_write_raw,
+};
+
+static irqreturn_t fxas21002c_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct fxas21002c_data *data = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = regmap_bulk_read(data->regmap, FXAS21002C_REG_OUT_X_MSB,
+ data->buffer, CHANNEL_SCAN_MAX * sizeof(s16));
+ if (ret < 0)
+ goto out_unlock;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+ data->timestamp);
+
+out_unlock:
+ mutex_unlock(&data->lock);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int fxas21002c_chip_init(struct fxas21002c_data *data)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ unsigned int chip_id;
+ int ret;
+
+ ret = regmap_field_read(data->regmap_fields[F_WHO_AM_I], &chip_id);
+ if (ret < 0)
+ return ret;
+
+ if (chip_id != FXAS21002C_CHIP_ID_1 &&
+ chip_id != FXAS21002C_CHIP_ID_2) {
+ dev_err(dev, "chip id 0x%02x is not supported\n", chip_id);
+ return -EINVAL;
+ }
+
+ data->chip_id = chip_id;
+
+ ret = fxas21002c_mode_set(data, FXAS21002C_MODE_STANDBY);
+ if (ret < 0)
+ return ret;
+
+ /* Set ODR to 200HZ as default */
+ ret = fxas21002c_odr_set(data, 200);
+ if (ret < 0)
+ dev_err(dev, "failed to set ODR: %d\n", ret);
+
+ return ret;
+}
+
+static int fxas21002c_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct fxas21002c_data *data = iio_priv(indio_dev);
+
+ return regmap_field_write(data->regmap_fields[F_INT_EN_DRDY], state);
+}
+
+static const struct iio_trigger_ops fxas21002c_trigger_ops = {
+ .set_trigger_state = &fxas21002c_data_rdy_trigger_set_state,
+};
+
+static irqreturn_t fxas21002c_data_rdy_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct fxas21002c_data *data = iio_priv(indio_dev);
+
+ data->timestamp = iio_get_time_ns(indio_dev);
+
+ return IRQ_WAKE_THREAD;
+}
+
+static irqreturn_t fxas21002c_data_rdy_thread(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct fxas21002c_data *data = iio_priv(indio_dev);
+ unsigned int data_ready;
+ int ret;
+
+ ret = regmap_field_read(data->regmap_fields[F_SRC_DRDY], &data_ready);
+ if (ret < 0)
+ return IRQ_NONE;
+
+ if (!data_ready)
+ return IRQ_NONE;
+
+ iio_trigger_poll_chained(data->dready_trig);
+
+ return IRQ_HANDLED;
+}
+
+static int fxas21002c_trigger_probe(struct fxas21002c_data *data)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct device_node *np = indio_dev->dev.of_node;
+ unsigned long irq_trig;
+ bool irq_open_drain;
+ int irq1;
+ int ret;
+
+ if (!data->irq)
+ return 0;
+
+ irq1 = of_irq_get_byname(np, "INT1");
+
+ if (irq1 == data->irq) {
+ dev_info(dev, "using interrupt line INT1\n");
+ ret = regmap_field_write(data->regmap_fields[F_INT_CFG_DRDY],
+ 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ dev_info(dev, "using interrupt line INT2\n");
+
+ irq_open_drain = of_property_read_bool(np, "drive-open-drain");
+
+ data->dready_trig = devm_iio_trigger_alloc(dev, "%s-dev%d",
+ indio_dev->name,
+ indio_dev->id);
+ if (!data->dready_trig)
+ return -ENOMEM;
+
+ irq_trig = irqd_get_trigger_type(irq_get_irq_data(data->irq));
+
+ if (irq_trig == IRQF_TRIGGER_RISING) {
+ ret = regmap_field_write(data->regmap_fields[F_IPOL], 1);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (irq_open_drain)
+ irq_trig |= IRQF_SHARED;
+
+ ret = devm_request_threaded_irq(dev, data->irq,
+ fxas21002c_data_rdy_handler,
+ fxas21002c_data_rdy_thread,
+ irq_trig, "fxas21002c_data_ready",
+ indio_dev);
+ if (ret < 0)
+ return ret;
+
+ data->dready_trig->dev.parent = dev;
+ data->dready_trig->ops = &fxas21002c_trigger_ops;
+ iio_trigger_set_drvdata(data->dready_trig, indio_dev);
+
+ return devm_iio_trigger_register(dev, data->dready_trig);
+}
+
+static int fxas21002c_power_enable(struct fxas21002c_data *data)
+{
+ int ret;
+
+ ret = regulator_enable(data->vdd);
+ if (ret < 0)
+ return ret;
+
+ ret = regulator_enable(data->vddio);
+ if (ret < 0) {
+ regulator_disable(data->vdd);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void fxas21002c_power_disable(struct fxas21002c_data *data)
+{
+ regulator_disable(data->vdd);
+ regulator_disable(data->vddio);
+}
+
+static void fxas21002c_power_disable_action(void *_data)
+{
+ struct fxas21002c_data *data = _data;
+
+ fxas21002c_power_disable(data);
+}
+
+static int fxas21002c_regulators_get(struct fxas21002c_data *data)
+{
+ struct device *dev = regmap_get_device(data->regmap);
+
+ data->vdd = devm_regulator_get(dev->parent, "vdd");
+ if (IS_ERR(data->vdd))
+ return PTR_ERR(data->vdd);
+
+ data->vddio = devm_regulator_get(dev->parent, "vddio");
+
+ return PTR_ERR_OR_ZERO(data->vddio);
+}
+
+int fxas21002c_core_probe(struct device *dev, struct regmap *regmap, int irq,
+ const char *name)
+{
+ struct fxas21002c_data *data;
+ struct iio_dev *indio_dev;
+ struct regmap_field *f;
+ int i;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ dev_set_drvdata(dev, indio_dev);
+ data->irq = irq;
+ data->regmap = regmap;
+
+ for (i = 0; i < F_MAX_FIELDS; i++) {
+ f = devm_regmap_field_alloc(dev, data->regmap,
+ fxas21002c_reg_fields[i]);
+ if (IS_ERR(f))
+ return PTR_ERR(f);
+
+ data->regmap_fields[i] = f;
+ }
+
+ mutex_init(&data->lock);
+
+ ret = fxas21002c_regulators_get(data);
+ if (ret < 0)
+ return ret;
+
+ ret = fxas21002c_power_enable(data);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, fxas21002c_power_disable_action,
+ data);
+ if (ret < 0)
+ return ret;
+
+ ret = fxas21002c_chip_init(data);
+ if (ret < 0)
+ return ret;
+
+ indio_dev->dev.parent = dev;
+ indio_dev->channels = fxas21002c_channels;
+ indio_dev->num_channels = ARRAY_SIZE(fxas21002c_channels);
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &fxas21002c_info;
+
+ ret = fxas21002c_trigger_probe(data);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ fxas21002c_trigger_handler, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ return ret;
+
+ pm_runtime_enable(dev);
+ pm_runtime_set_autosuspend_delay(dev, 2000);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto pm_disable;
+
+ return 0;
+
+pm_disable:
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(fxas21002c_core_probe);
+
+void fxas21002c_core_remove(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+
+ iio_device_unregister(indio_dev);
+
+ pm_runtime_disable(dev);
+ pm_runtime_set_suspended(dev);
+ pm_runtime_put_noidle(dev);
+}
+EXPORT_SYMBOL_GPL(fxas21002c_core_remove);
+
+static int __maybe_unused fxas21002c_suspend(struct device *dev)
+{
+ struct fxas21002c_data *data = iio_priv(dev_get_drvdata(dev));
+
+ fxas21002c_mode_set(data, FXAS21002C_MODE_STANDBY);
+ fxas21002c_power_disable(data);
+
+ return 0;
+}
+
+static int __maybe_unused fxas21002c_resume(struct device *dev)
+{
+ struct fxas21002c_data *data = iio_priv(dev_get_drvdata(dev));
+ int ret;
+
+ ret = fxas21002c_power_enable(data);
+ if (ret < 0)
+ return ret;
+
+ return fxas21002c_mode_set(data, data->prev_mode);
+}
+
+static int __maybe_unused fxas21002c_runtime_suspend(struct device *dev)
+{
+ struct fxas21002c_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return fxas21002c_mode_set(data, FXAS21002C_MODE_READY);
+}
+
+static int __maybe_unused fxas21002c_runtime_resume(struct device *dev)
+{
+ struct fxas21002c_data *data = iio_priv(dev_get_drvdata(dev));
+
+ return fxas21002c_mode_set(data, FXAS21002C_MODE_ACTIVE);
+}
+
+const struct dev_pm_ops fxas21002c_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(fxas21002c_suspend, fxas21002c_resume)
+ SET_RUNTIME_PM_OPS(fxas21002c_runtime_suspend,
+ fxas21002c_runtime_resume, NULL)
+};
+EXPORT_SYMBOL_GPL(fxas21002c_pm_ops);
+
+MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("FXAS21002C Gyro driver");
diff --git a/drivers/iio/gyro/fxas21002c_i2c.c b/drivers/iio/gyro/fxas21002c_i2c.c
new file mode 100644
index 0000000..a7807fd
--- /dev/null
+++ b/drivers/iio/gyro/fxas21002c_i2c.c
@@ -0,0 +1,69 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for NXP FXAS21002C Gyroscope - I2C
+ *
+ * Copyright (C) 2018 Linaro Ltd.
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "fxas21002c.h"
+
+static const struct regmap_config fxas21002c_regmap_i2c_conf = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = FXAS21002C_REG_CTRL3,
+};
+
+static int fxas21002c_i2c_probe(struct i2c_client *i2c)
+{
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(i2c, &fxas21002c_regmap_i2c_conf);
+ if (IS_ERR(regmap)) {
+ dev_err(&i2c->dev, "Failed to register i2c regmap: %ld\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ return fxas21002c_core_probe(&i2c->dev, regmap, i2c->irq, i2c->name);
+}
+
+static int fxas21002c_i2c_remove(struct i2c_client *i2c)
+{
+ fxas21002c_core_remove(&i2c->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id fxas21002c_i2c_id[] = {
+ { "fxas21002c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, fxas21002c_i2c_id);
+
+static const struct of_device_id fxas21002c_i2c_of_match[] = {
+ { .compatible = "nxp,fxas21002c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, fxas21002c_i2c_of_match);
+
+static struct i2c_driver fxas21002c_i2c_driver = {
+ .driver = {
+ .name = "fxas21002c_i2c",
+ .pm = &fxas21002c_pm_ops,
+ .of_match_table = fxas21002c_i2c_of_match,
+ },
+ .probe_new = fxas21002c_i2c_probe,
+ .remove = fxas21002c_i2c_remove,
+ .id_table = fxas21002c_i2c_id,
+};
+module_i2c_driver(fxas21002c_i2c_driver);
+
+MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("FXAS21002C I2C Gyro driver");
diff --git a/drivers/iio/gyro/fxas21002c_spi.c b/drivers/iio/gyro/fxas21002c_spi.c
new file mode 100644
index 0000000..77ceebe
--- /dev/null
+++ b/drivers/iio/gyro/fxas21002c_spi.c
@@ -0,0 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Driver for NXP Fxas21002c Gyroscope - SPI
+ *
+ * Copyright (C) 2019 Linaro Ltd.
+ */
+
+#include <linux/err.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "fxas21002c.h"
+
+static const struct regmap_config fxas21002c_regmap_spi_conf = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = FXAS21002C_REG_CTRL3,
+};
+
+static int fxas21002c_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_spi(spi, &fxas21002c_regmap_spi_conf);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "Failed to register spi regmap: %ld\n",
+ PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ return fxas21002c_core_probe(&spi->dev, regmap, spi->irq, id->name);
+}
+
+static int fxas21002c_spi_remove(struct spi_device *spi)
+{
+ fxas21002c_core_remove(&spi->dev);
+
+ return 0;
+}
+
+static const struct spi_device_id fxas21002c_spi_id[] = {
+ { "fxas21002c", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, fxas21002c_spi_id);
+
+static const struct of_device_id fxas21002c_spi_of_match[] = {
+ { .compatible = "nxp,fxas21002c", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, fxas21002c_spi_of_match);
+
+static struct spi_driver fxas21002c_spi_driver = {
+ .driver = {
+ .name = "fxas21002c_spi",
+ .pm = &fxas21002c_pm_ops,
+ .of_match_table = fxas21002c_spi_of_match,
+ },
+ .probe = fxas21002c_spi_probe,
+ .remove = fxas21002c_spi_remove,
+ .id_table = fxas21002c_spi_id,
+};
+module_spi_driver(fxas21002c_spi_driver);
+
+MODULE_AUTHOR("Rui Miguel Silva <rui.silva@linaro.org>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("FXAS21002C SPI Gyro driver");
diff --git a/drivers/iio/gyro/hid-sensor-gyro-3d.c b/drivers/iio/gyro/hid-sensor-gyro-3d.c
index 88e857c..08cacbb 100644
--- a/drivers/iio/gyro/hid-sensor-gyro-3d.c
+++ b/drivers/iio/gyro/hid-sensor-gyro-3d.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <linux/device.h>
#include <linux/platform_device.h>
diff --git a/drivers/iio/gyro/itg3200_buffer.c b/drivers/iio/gyro/itg3200_buffer.c
index 59770e5..d3fbe9d 100644
--- a/drivers/iio/gyro/itg3200_buffer.c
+++ b/drivers/iio/gyro/itg3200_buffer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* itg3200_buffer.c -- support InvenSense ITG3200
* Digital 3-Axis Gyroscope driver
@@ -5,10 +6,6 @@
* Copyright (c) 2011 Christian Strobel <christian.strobel@iis.fraunhofer.de>
* Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
* Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de>
- *
- * 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/slab.h>
diff --git a/drivers/iio/gyro/itg3200_core.c b/drivers/iio/gyro/itg3200_core.c
index 7adecb5..998fb8d 100644
--- a/drivers/iio/gyro/itg3200_core.c
+++ b/drivers/iio/gyro/itg3200_core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* itg3200_core.c -- support InvenSense ITG3200
* Digital 3-Axis Gyroscope driver
@@ -6,10 +7,6 @@
* Copyright (c) 2011 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
* Copyright (c) 2012 Thorsten Nowak <thorsten.nowak@iis.fraunhofer.de>
*
- * 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.
- *
* TODO:
* - Support digital low pass filter
* - Support power management
@@ -242,6 +239,20 @@
return ret;
}
+static const struct iio_mount_matrix *
+itg3200_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct itg3200 *data = iio_priv(indio_dev);
+
+ return &data->orientation;
+}
+
+static const struct iio_chan_spec_ext_info itg3200_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, itg3200_get_mount_matrix),
+ { }
+};
+
#define ITG3200_ST \
{ .sign = 's', .realbits = 16, .storagebits = 16, .endianness = IIO_BE }
@@ -255,6 +266,7 @@
.address = ITG3200_REG_GYRO_ ## _mod ## OUT_H, \
.scan_index = ITG3200_SCAN_GYRO_ ## _mod, \
.scan_type = ITG3200_ST, \
+ .ext_info = itg3200_ext_info, \
}
static const struct iio_chan_spec itg3200_channels[] = {
@@ -297,6 +309,11 @@
st = iio_priv(indio_dev);
+ ret = iio_read_mount_matrix(&client->dev, "mount-matrix",
+ &st->orientation);
+ if (ret)
+ return ret;
+
i2c_set_clientdata(client, indio_dev);
st->i2c = client;
diff --git a/drivers/iio/gyro/mpu3050-core.c b/drivers/iio/gyro/mpu3050-core.c
index 77fac81..80154bc 100644
--- a/drivers/iio/gyro/mpu3050-core.c
+++ b/drivers/iio/gyro/mpu3050-core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MPU3050 gyroscope driver
*
@@ -29,7 +30,8 @@
#include "mpu3050.h"
-#define MPU3050_CHIP_ID 0x69
+#define MPU3050_CHIP_ID 0x68
+#define MPU3050_CHIP_ID_MASK 0x7E
/*
* Register map: anything suffixed *_H is a big-endian high byte and always
@@ -864,7 +866,7 @@
dev_err(mpu3050->dev, "error setting power mode\n");
return ret;
}
- msleep(10);
+ usleep_range(10000, 20000);
return 0;
}
@@ -1149,8 +1151,7 @@
mpu3050->divisor = 99;
/* Read the mounting matrix, if present */
- ret = of_iio_read_mount_matrix(dev, "mount-matrix",
- &mpu3050->orientation);
+ ret = iio_read_mount_matrix(dev, "mount-matrix", &mpu3050->orientation);
if (ret)
return ret;
@@ -1176,8 +1177,9 @@
goto err_power_down;
}
- if (val != MPU3050_CHIP_ID) {
- dev_err(dev, "unsupported chip id %02x\n", (u8)val);
+ if ((val & MPU3050_CHIP_ID_MASK) != MPU3050_CHIP_ID) {
+ dev_err(dev, "unsupported chip id %02x\n",
+ (u8)(val & MPU3050_CHIP_ID_MASK));
ret = -ENODEV;
goto err_power_down;
}
diff --git a/drivers/iio/gyro/mpu3050-i2c.c b/drivers/iio/gyro/mpu3050-i2c.c
index 93f08b3..afa8018 100644
--- a/drivers/iio/gyro/mpu3050-i2c.c
+++ b/drivers/iio/gyro/mpu3050-i2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
diff --git a/drivers/iio/gyro/ssp_gyro_sensor.c b/drivers/iio/gyro/ssp_gyro_sensor.c
index 2dacd8e..4e4ee41 100644
--- a/drivers/iio/gyro/ssp_gyro_sensor.c
+++ b/drivers/iio/gyro/ssp_gyro_sensor.c
@@ -1,16 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2014, Samsung Electronics Co. Ltd. All Rights Reserved.
- *
- * 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/iio/common/ssp_sensors.h>
diff --git a/drivers/iio/gyro/st_gyro.h b/drivers/iio/gyro/st_gyro.h
index 48923ae..592f6b3 100644
--- a/drivers/iio/gyro/st_gyro.h
+++ b/drivers/iio/gyro/st_gyro.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics gyroscopes driver
*
@@ -5,7 +6,6 @@
*
* Denis Ciocca <denis.ciocca@st.com>
* v. 1.0.0
- * Licensed under the GPL-2.
*/
#ifndef ST_GYRO_H
@@ -32,6 +32,7 @@
.drdy_int_pin = 2,
};
+const struct st_sensor_settings *st_gyro_get_settings(const char *name);
int st_gyro_common_probe(struct iio_dev *indio_dev);
void st_gyro_common_remove(struct iio_dev *indio_dev);
diff --git a/drivers/iio/gyro/st_gyro_buffer.c b/drivers/iio/gyro/st_gyro_buffer.c
index a537704..7465ad6 100644
--- a/drivers/iio/gyro/st_gyro_buffer.c
+++ b/drivers/iio/gyro/st_gyro_buffer.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics gyroscopes driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -30,61 +29,51 @@
return st_sensors_set_dataready_irq(indio_dev, state);
}
-static int st_gyro_buffer_preenable(struct iio_dev *indio_dev)
-{
- return st_sensors_set_enable(indio_dev, true);
-}
-
static int st_gyro_buffer_postenable(struct iio_dev *indio_dev)
{
int err;
- struct st_sensor_data *gdata = iio_priv(indio_dev);
-
- gdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
- if (gdata->buffer_data == NULL) {
- err = -ENOMEM;
- goto allocate_memory_error;
- }
-
- err = st_sensors_set_axis_enable(indio_dev,
- (u8)indio_dev->active_scan_mask[0]);
- if (err < 0)
- goto st_gyro_buffer_postenable_error;
err = iio_triggered_buffer_postenable(indio_dev);
if (err < 0)
- goto st_gyro_buffer_postenable_error;
+ return err;
- return err;
+ err = st_sensors_set_axis_enable(indio_dev,
+ (u8)indio_dev->active_scan_mask[0]);
+ if (err < 0)
+ goto st_gyro_buffer_predisable;
-st_gyro_buffer_postenable_error:
- kfree(gdata->buffer_data);
-allocate_memory_error:
+ err = st_sensors_set_enable(indio_dev, true);
+ if (err < 0)
+ goto st_gyro_buffer_enable_all_axis;
+
+ return 0;
+
+st_gyro_buffer_enable_all_axis:
+ st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
+st_gyro_buffer_predisable:
+ iio_triggered_buffer_predisable(indio_dev);
return err;
}
static int st_gyro_buffer_predisable(struct iio_dev *indio_dev)
{
- int err;
- struct st_sensor_data *gdata = iio_priv(indio_dev);
-
- err = iio_triggered_buffer_predisable(indio_dev);
- if (err < 0)
- goto st_gyro_buffer_predisable_error;
-
- err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
- if (err < 0)
- goto st_gyro_buffer_predisable_error;
+ int err, err2;
err = st_sensors_set_enable(indio_dev, false);
+ if (err < 0)
+ goto st_gyro_buffer_predisable;
-st_gyro_buffer_predisable_error:
- kfree(gdata->buffer_data);
+ err = st_sensors_set_axis_enable(indio_dev, ST_SENSORS_ENABLE_ALL_AXIS);
+
+st_gyro_buffer_predisable:
+ err2 = iio_triggered_buffer_predisable(indio_dev);
+ if (!err)
+ err = err2;
+
return err;
}
static const struct iio_buffer_setup_ops st_gyro_buffer_setup_ops = {
- .preenable = &st_gyro_buffer_preenable,
.postenable = &st_gyro_buffer_postenable,
.predisable = &st_gyro_buffer_predisable,
};
diff --git a/drivers/iio/gyro/st_gyro_core.c b/drivers/iio/gyro/st_gyro_core.c
index b31064b..c0acbb5 100644
--- a/drivers/iio/gyro/st_gyro_core.c
+++ b/drivers/iio/gyro/st_gyro_core.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics gyroscopes driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -13,7 +12,6 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/types.h>
-#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
@@ -369,28 +367,41 @@
#define ST_GYRO_TRIGGER_OPS NULL
#endif
+/*
+ * st_gyro_get_settings() - get sensor settings from device name
+ * @name: device name buffer reference.
+ *
+ * Return: valid reference on success, NULL otherwise.
+ */
+const struct st_sensor_settings *st_gyro_get_settings(const char *name)
+{
+ int index = st_sensors_get_settings_index(name,
+ st_gyro_sensors_settings,
+ ARRAY_SIZE(st_gyro_sensors_settings));
+ if (index < 0)
+ return NULL;
+
+ return &st_gyro_sensors_settings[index];
+}
+EXPORT_SYMBOL(st_gyro_get_settings);
+
int st_gyro_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *gdata = iio_priv(indio_dev);
- int irq = gdata->get_irq_data_ready(indio_dev);
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &gyro_info;
- mutex_init(&gdata->tb.buf_lock);
err = st_sensors_power_enable(indio_dev);
if (err)
return err;
- err = st_sensors_check_device_support(indio_dev,
- ARRAY_SIZE(st_gyro_sensors_settings),
- st_gyro_sensors_settings);
+ err = st_sensors_verify_id(indio_dev);
if (err < 0)
goto st_gyro_power_off;
gdata->num_data_channels = ST_GYRO_NUMBER_DATA_CHANNELS;
- gdata->multiread_bit = gdata->sensor_settings->multi_read_bit;
indio_dev->channels = gdata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
@@ -407,7 +418,7 @@
if (err < 0)
goto st_gyro_power_off;
- if (irq > 0) {
+ if (gdata->irq > 0) {
err = st_sensors_allocate_trigger(indio_dev,
ST_GYRO_TRIGGER_OPS);
if (err < 0)
@@ -424,7 +435,7 @@
return 0;
st_gyro_device_register_error:
- if (irq > 0)
+ if (gdata->irq > 0)
st_sensors_deallocate_trigger(indio_dev);
st_gyro_probe_trigger_error:
st_gyro_deallocate_ring(indio_dev);
@@ -442,7 +453,7 @@
st_sensors_power_disable(indio_dev);
iio_device_unregister(indio_dev);
- if (gdata->get_irq_data_ready(indio_dev) > 0)
+ if (gdata->irq > 0)
st_sensors_deallocate_trigger(indio_dev);
st_gyro_deallocate_ring(indio_dev);
diff --git a/drivers/iio/gyro/st_gyro_i2c.c b/drivers/iio/gyro/st_gyro_i2c.c
index b405b82..05a1a08 100644
--- a/drivers/iio/gyro/st_gyro_i2c.c
+++ b/drivers/iio/gyro/st_gyro_i2c.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics gyroscopes driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -64,21 +63,33 @@
#endif
static int st_gyro_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
- struct iio_dev *indio_dev;
+ const struct st_sensor_settings *settings;
struct st_sensor_data *gdata;
+ struct iio_dev *indio_dev;
int err;
+ st_sensors_of_name_probe(&client->dev, st_gyro_of_match,
+ client->name, sizeof(client->name));
+
+ settings = st_gyro_get_settings(client->name);
+ if (!settings) {
+ dev_err(&client->dev, "device name %s not recognized.\n",
+ client->name);
+ return -ENODEV;
+ }
+
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*gdata));
if (!indio_dev)
return -ENOMEM;
gdata = iio_priv(indio_dev);
- st_sensors_of_name_probe(&client->dev, st_gyro_of_match,
- client->name, sizeof(client->name));
+ gdata->sensor_settings = (struct st_sensor_settings *)settings;
- st_sensors_i2c_configure(indio_dev, client, gdata);
+ err = st_sensors_i2c_configure(indio_dev, client);
+ if (err < 0)
+ return err;
err = st_gyro_common_probe(indio_dev);
if (err < 0)
diff --git a/drivers/iio/gyro/st_gyro_spi.c b/drivers/iio/gyro/st_gyro_spi.c
index 0b52ed5..b5c6242 100644
--- a/drivers/iio/gyro/st_gyro_spi.c
+++ b/drivers/iio/gyro/st_gyro_spi.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics gyroscopes driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -70,19 +69,31 @@
static int st_gyro_spi_probe(struct spi_device *spi)
{
- struct iio_dev *indio_dev;
+ const struct st_sensor_settings *settings;
struct st_sensor_data *gdata;
+ struct iio_dev *indio_dev;
int err;
+ st_sensors_of_name_probe(&spi->dev, st_gyro_of_match,
+ spi->modalias, sizeof(spi->modalias));
+
+ settings = st_gyro_get_settings(spi->modalias);
+ if (!settings) {
+ dev_err(&spi->dev, "device name %s not recognized.\n",
+ spi->modalias);
+ return -ENODEV;
+ }
+
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*gdata));
if (!indio_dev)
return -ENOMEM;
gdata = iio_priv(indio_dev);
+ gdata->sensor_settings = (struct st_sensor_settings *)settings;
- st_sensors_of_name_probe(&spi->dev, st_gyro_of_match,
- spi->modalias, sizeof(spi->modalias));
- st_sensors_spi_configure(indio_dev, spi, gdata);
+ err = st_sensors_spi_configure(indio_dev, spi);
+ if (err < 0)
+ return err;
err = st_gyro_common_probe(indio_dev);
if (err < 0)
diff --git a/drivers/iio/health/Kconfig b/drivers/iio/health/Kconfig
index a2ecb4c..a89f3ab 100644
--- a/drivers/iio/health/Kconfig
+++ b/drivers/iio/health/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Health sensors
#
diff --git a/drivers/iio/health/Makefile b/drivers/iio/health/Makefile
index 3558f9d..9108171 100644
--- a/drivers/iio/health/Makefile
+++ b/drivers/iio/health/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for IIO Health sensors
#
diff --git a/drivers/iio/health/afe4403.c b/drivers/iio/health/afe4403.c
index a739fff..dc22dc3 100644
--- a/drivers/iio/health/afe4403.c
+++ b/drivers/iio/health/afe4403.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AFE4403 Heart Rate Monitors and Low-Cost Pulse Oximeters
*
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.com>
- *
- * 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.
- *
- * 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/device.h>
diff --git a/drivers/iio/health/afe4404.c b/drivers/iio/health/afe4404.c
index 1191092..e728bbb 100644
--- a/drivers/iio/health/afe4404.c
+++ b/drivers/iio/health/afe4404.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AFE4404 Heart Rate Monitors and Low-Cost Pulse Oximeters
*
* Copyright (C) 2015-2016 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.com>
- *
- * 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.
- *
- * 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/device.h>
diff --git a/drivers/iio/health/afe440x.h b/drivers/iio/health/afe440x.h
index 1a0f247..7829c4f 100644
--- a/drivers/iio/health/afe440x.h
+++ b/drivers/iio/health/afe440x.h
@@ -1,17 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* AFE440X Heart Rate Monitors and Low-Cost Pulse Oximeters
*
* Copyright (C) 2015 Texas Instruments Incorporated - http://www.ti.com/
* Andrew F. Davis <afd@ti.com>
- *
- * 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.
- *
- * 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.
*/
#ifndef _AFE440X_H
diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c
index 15ccadc..74fc260 100644
--- a/drivers/iio/health/max30102.c
+++ b/drivers/iio/health/max30102.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* max30102.c - Support for MAX30102 heart rate and pulse oximeter sensor
*
@@ -6,16 +7,6 @@
* Support for MAX30105 optical particle sensor
* Copyright (C) 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
*
- * 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.
- *
* 7-bit I2C chip address: 0x57
* TODO: proximity power saving feature
*/
@@ -282,9 +273,11 @@
switch (measurements) {
case 3:
MAX30102_COPY_DATA(2);
- case 2: /* fall-through */
+ /* fall through */
+ case 2:
MAX30102_COPY_DATA(1);
- case 1: /* fall-through */
+ /* fall through */
+ case 1:
MAX30102_COPY_DATA(0);
break;
default:
diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
index 1a0d458..6c5507a 100644
--- a/drivers/iio/humidity/Kconfig
+++ b/drivers/iio/humidity/Kconfig
@@ -1,19 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# humidity sensor drivers
#
menu "Humidity sensors"
config AM2315
- tristate "Aosong AM2315 relative humidity and temperature sensor"
- depends on I2C
- select IIO_BUFFER
- select IIO_TRIGGERED_BUFFER
- help
- If you say yes here you get support for the Aosong AM2315
- relative humidity and ambient temperature sensor.
+ tristate "Aosong AM2315 relative humidity and temperature sensor"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ If you say yes here you get support for the Aosong AM2315
+ relative humidity and ambient temperature sensor.
- This driver can also be built as a module. If so, the module will
- be called am2315.
+ This driver can also be built as a module. If so, the module will
+ be called am2315.
config DHT11
tristate "DHT11 (and compatible sensors) driver"
@@ -78,7 +79,7 @@
config HTU21
tristate "Measurement Specialties HTU21 humidity & temperature sensor"
depends on I2C
- select IIO_MS_SENSORS_I2C
+ select IIO_MS_SENSORS_I2C
help
If you say yes here you get support for the Measurement Specialties
HTU21 humidity and temperature sensor.
diff --git a/drivers/iio/humidity/am2315.c b/drivers/iio/humidity/am2315.c
index 7d8669d..3bac98e 100644
--- a/drivers/iio/humidity/am2315.c
+++ b/drivers/iio/humidity/am2315.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Aosong AM2315 relative humidity and temperature
*
* Copyright (c) 2016, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* 7-bit I2C address: 0x5C.
*/
@@ -243,32 +240,15 @@
indio_dev->channels = am2315_channels;
indio_dev->num_channels = ARRAY_SIZE(am2315_channels);
- ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
+ ret = devm_iio_triggered_buffer_setup(&client->dev,
+ indio_dev, iio_pollfunc_store_time,
am2315_trigger_handler, NULL);
if (ret < 0) {
dev_err(&client->dev, "iio triggered buffer setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret < 0)
- goto err_buffer_cleanup;
-
- return 0;
-
-err_buffer_cleanup:
- iio_triggered_buffer_cleanup(indio_dev);
- return ret;
-}
-
-static int am2315_remove(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
-
- iio_device_unregister(indio_dev);
- iio_triggered_buffer_cleanup(indio_dev);
-
- return 0;
+ return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id am2315_i2c_id[] = {
@@ -290,7 +270,6 @@
.acpi_match_table = ACPI_PTR(am2315_acpi_id),
},
.probe = am2315_probe,
- .remove = am2315_remove,
.id_table = am2315_i2c_id,
};
diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c
index 1a9f8f4..b459600 100644
--- a/drivers/iio/humidity/dht11.c
+++ b/drivers/iio/humidity/dht11.c
@@ -1,17 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* DHT11/DHT22 bit banging GPIO driver
*
* Copyright (c) Harald Geyer <harald@ccbib.org>
- *
- * 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/err.h>
@@ -31,8 +22,7 @@
#include <linux/completion.h>
#include <linux/mutex.h>
#include <linux/delay.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
#include <linux/timekeeping.h>
#include <linux/iio/iio.h>
@@ -81,7 +71,7 @@
struct dht11 {
struct device *dev;
- int gpio;
+ struct gpio_desc *gpiod;
int irq;
struct completion completion;
@@ -158,7 +148,7 @@
return -EIO;
}
- dht11->timestamp = ktime_get_boot_ns();
+ dht11->timestamp = ktime_get_boottime_ns();
if (hum_int < 4) { /* DHT22: 100000 = (3*256+232)*100 */
dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) *
((temp_int & 0x80) ? -100 : 100);
@@ -186,9 +176,9 @@
/* TODO: Consider making the handler safe for IRQ sharing */
if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) {
- dht11->edges[dht11->num_edges].ts = ktime_get_boot_ns();
+ dht11->edges[dht11->num_edges].ts = ktime_get_boottime_ns();
dht11->edges[dht11->num_edges++].value =
- gpio_get_value(dht11->gpio);
+ gpiod_get_value(dht11->gpiod);
if (dht11->num_edges >= DHT11_EDGES_PER_READ)
complete(&dht11->completion);
@@ -205,7 +195,7 @@
int ret, timeres, offset;
mutex_lock(&dht11->lock);
- if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_boot_ns()) {
+ if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_boottime_ns()) {
timeres = ktime_get_resolution_ns();
dev_dbg(dht11->dev, "current timeresolution: %dns\n", timeres);
if (timeres > DHT11_MIN_TIMERES) {
@@ -226,12 +216,12 @@
reinit_completion(&dht11->completion);
dht11->num_edges = 0;
- ret = gpio_direction_output(dht11->gpio, 0);
+ ret = gpiod_direction_output(dht11->gpiod, 0);
if (ret)
goto err;
usleep_range(DHT11_START_TRANSMISSION_MIN,
DHT11_START_TRANSMISSION_MAX);
- ret = gpio_direction_input(dht11->gpio);
+ ret = gpiod_direction_input(dht11->gpiod);
if (ret)
goto err;
@@ -303,10 +293,8 @@
static int dht11_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
- struct device_node *node = dev->of_node;
struct dht11 *dht11;
struct iio_dev *iio;
- int ret;
iio = devm_iio_device_alloc(dev, sizeof(*dht11));
if (!iio) {
@@ -316,22 +304,17 @@
dht11 = iio_priv(iio);
dht11->dev = dev;
+ dht11->gpiod = devm_gpiod_get(dev, NULL, GPIOD_IN);
+ if (IS_ERR(dht11->gpiod))
+ return PTR_ERR(dht11->gpiod);
- ret = of_get_gpio(node, 0);
- if (ret < 0)
- return ret;
- dht11->gpio = ret;
- ret = devm_gpio_request_one(dev, dht11->gpio, GPIOF_IN, pdev->name);
- if (ret)
- return ret;
-
- dht11->irq = gpio_to_irq(dht11->gpio);
+ dht11->irq = gpiod_to_irq(dht11->gpiod);
if (dht11->irq < 0) {
- dev_err(dev, "GPIO %d has no interrupt\n", dht11->gpio);
+ dev_err(dev, "GPIO %d has no interrupt\n", desc_to_gpio(dht11->gpiod));
return -EINVAL;
}
- dht11->timestamp = ktime_get_boot_ns() - DHT11_DATA_VALID_TIME - 1;
+ dht11->timestamp = ktime_get_boottime_ns() - DHT11_DATA_VALID_TIME - 1;
dht11->num_edges = -1;
platform_set_drvdata(pdev, iio);
diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c
index 066e05f..bfe1cdb 100644
--- a/drivers/iio/humidity/hdc100x.c
+++ b/drivers/iio/humidity/hdc100x.c
@@ -385,28 +385,16 @@
hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]);
hdc100x_update_config(data, HDC100X_REG_CONFIG_ACQ_MODE, 0);
- ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ ret = devm_iio_triggered_buffer_setup(&client->dev,
+ indio_dev, NULL,
hdc100x_trigger_handler,
&hdc_buffer_setup_ops);
if (ret < 0) {
dev_err(&client->dev, "iio triggered buffer setup failed\n");
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret < 0)
- iio_triggered_buffer_cleanup(indio_dev);
- return ret;
-}
-
-static int hdc100x_remove(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
-
- iio_device_unregister(indio_dev);
- iio_triggered_buffer_cleanup(indio_dev);
-
- return 0;
+ return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id hdc100x_id[] = {
@@ -436,7 +424,6 @@
.of_match_table = of_match_ptr(hdc100x_dt_ids),
},
.probe = hdc100x_probe,
- .remove = hdc100x_remove,
.id_table = hdc100x_id,
};
module_i2c_driver(hdc100x_driver);
diff --git a/drivers/iio/humidity/hid-sensor-humidity.c b/drivers/iio/humidity/hid-sensor-humidity.c
index 4bc95f3..c99b54b 100644
--- a/drivers/iio/humidity/hid-sensor-humidity.c
+++ b/drivers/iio/humidity/hid-sensor-humidity.c
@@ -1,18 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2017, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.
*/
#include <linux/device.h>
#include <linux/hid-sensor-hub.h>
diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h
index e41a3d8..7d6771f 100644
--- a/drivers/iio/humidity/hts221.h
+++ b/drivers/iio/humidity/hts221.h
@@ -1,11 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
- *
- * Licensed under the GPL-2.
*/
#ifndef HTS221_H
diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c
index 1a94b0b..81d50a8 100644
--- a/drivers/iio/humidity/hts221_buffer.c
+++ b/drivers/iio/humidity/hts221_buffer.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
#include <linux/module.h>
diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c
index 166946d..4922444 100644
--- a/drivers/iio/humidity/hts221_core.c
+++ b/drivers/iio/humidity/hts221_core.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics hts221 sensor driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c
index b5b3f40..4272b70 100644
--- a/drivers/iio/humidity/hts221_i2c.c
+++ b/drivers/iio/humidity/hts221_i2c.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics hts221 i2c driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c
index 9c005f0..055dba8 100644
--- a/drivers/iio/humidity/hts221_spi.c
+++ b/drivers/iio/humidity/hts221_spi.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics hts221 spi driver
*
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/humidity/htu21.c b/drivers/iio/humidity/htu21.c
index f5a2701..d4c0589 100644
--- a/drivers/iio/humidity/htu21.c
+++ b/drivers/iio/humidity/htu21.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* htu21.c - Support for Measurement-Specialties
* htu21 temperature & humidity sensor
@@ -5,8 +6,6 @@
*
* Copyright (c) 2014 Measurement-Specialties
*
- * Licensed under the GPL-2.
- *
* (7-bit I2C slave address 0x40)
*
* Datasheet:
diff --git a/drivers/iio/humidity/si7005.c b/drivers/iio/humidity/si7005.c
index 1fd19f0..d5aef0b 100644
--- a/drivers/iio/humidity/si7005.c
+++ b/drivers/iio/humidity/si7005.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* si7005.c - Support for Silabs Si7005 humidity and temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* (7-bit I2C slave address 0x40)
*
* TODO: heater, fast mode, processed mode (temp. / linearity compensation)
diff --git a/drivers/iio/humidity/si7020.c b/drivers/iio/humidity/si7020.c
index 1b2ec8d..b938f07 100644
--- a/drivers/iio/humidity/si7020.c
+++ b/drivers/iio/humidity/si7020.c
@@ -1,16 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* si7020.c - Silicon Labs Si7013/20/21 Relative Humidity and Temp Sensors
* Copyright (c) 2013,2014 Uplogix, Inc.
* David Barksdale <dbarksdale@uplogix.com>
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * 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.
*/
/*
diff --git a/drivers/iio/iio_core.h b/drivers/iio/iio_core.h
index c775fed..159ea3f 100644
--- a/drivers/iio/iio_core.h
+++ b/drivers/iio/iio_core.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/* The industrial I/O core function defs.
*
* Copyright (c) 2008 Jonathan Cameron
*
- * 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.
- *
* These definitions are meant for use only within the IIO core, not individual
* drivers.
*/
diff --git a/drivers/iio/iio_core_trigger.h b/drivers/iio/iio_core_trigger.h
index 1fdb1e4..e59fe2f 100644
--- a/drivers/iio/iio_core_trigger.h
+++ b/drivers/iio/iio_core_trigger.h
@@ -1,11 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/* The industrial I/O core, trigger consumer handling functions
*
* Copyright (c) 2008 Jonathan Cameron
- *
- * 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.
*/
#ifdef CONFIG_IIO_TRIGGER
diff --git a/drivers/iio/imu/Kconfig b/drivers/iio/imu/Kconfig
index 156630a..f3c7282 100644
--- a/drivers/iio/imu/Kconfig
+++ b/drivers/iio/imu/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# IIO imu drivers configuration
#
@@ -16,6 +17,18 @@
adis16365, adis16400 and adis16405 triaxial inertial sensors
(adis16400 series also have magnetometers).
+config ADIS16460
+ tristate "Analog Devices ADIS16460 and similar IMU driver"
+ depends on SPI
+ select IIO_ADIS_LIB
+ select IIO_ADIS_LIB_BUFFER if IIO_BUFFER
+ help
+ Say yes here to build support for Analog Devices ADIS16460 inertial
+ sensor.
+
+ To compile this driver as a module, choose M here: the module will be
+ called adis16460.
+
config ADIS16480
tristate "Analog Devices ADIS16480 and similar IMU driver"
depends on SPI
diff --git a/drivers/iio/imu/Makefile b/drivers/iio/imu/Makefile
index 68629c6..4a69588 100644
--- a/drivers/iio/imu/Makefile
+++ b/drivers/iio/imu/Makefile
@@ -4,9 +4,8 @@
#
# When adding new entries keep the list in alphabetical order
-adis16400-y := adis16400_core.o
-adis16400-$(CONFIG_IIO_BUFFER) += adis16400_buffer.o
obj-$(CONFIG_ADIS16400) += adis16400.o
+obj-$(CONFIG_ADIS16460) += adis16460.o
obj-$(CONFIG_ADIS16480) += adis16480.o
adis_lib-y += adis.o
diff --git a/drivers/iio/imu/adis.c b/drivers/iio/imu/adis.c
index c771ae6..1631c25 100644
--- a/drivers/iio/imu/adis.c
+++ b/drivers/iio/imu/adis.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Common library for ADIS16XXX devices
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/delay.h>
@@ -40,18 +39,24 @@
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->write_delay,
+ .cs_change_delay = adis->data->cs_change_delay,
+ .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 2,
.bits_per_word = 8,
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->write_delay,
+ .cs_change_delay = adis->data->cs_change_delay,
+ .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 4,
.bits_per_word = 8,
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->write_delay,
+ .cs_change_delay = adis->data->cs_change_delay,
+ .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 6,
.bits_per_word = 8,
@@ -134,12 +139,16 @@
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->write_delay,
+ .cs_change_delay = adis->data->cs_change_delay,
+ .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 2,
.bits_per_word = 8,
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->read_delay,
+ .cs_change_delay = adis->data->cs_change_delay,
+ .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
}, {
.tx_buf = adis->tx + 4,
.rx_buf = adis->rx,
@@ -147,6 +156,8 @@
.len = 2,
.cs_change = 1,
.delay_usecs = adis->data->read_delay,
+ .cs_change_delay = adis->data->cs_change_delay,
+ .cs_change_delay_unit = SPI_DELAY_UNIT_USECS,
}, {
.rx_buf = adis->rx + 2,
.bits_per_word = 8,
diff --git a/drivers/iio/imu/adis16400_core.c b/drivers/iio/imu/adis16400.c
similarity index 77%
rename from drivers/iio/imu/adis16400_core.c
rename to drivers/iio/imu/adis16400.c
index 46a5690..0575ff7 100644
--- a/drivers/iio/imu/adis16400_core.c
+++ b/drivers/iio/imu/adis16400.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* adis16400.c support Analog Devices ADIS16400/5
* 3d 2g Linear Accelerometers,
@@ -7,11 +8,6 @@
* Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
* Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
* Copyright (c) 2011 Analog Devices Inc.
- *
- * 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/interrupt.h>
@@ -31,8 +27,183 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/imu/adis.h>
-#include "adis16400.h"
+#define ADIS16400_STARTUP_DELAY 290 /* ms */
+#define ADIS16400_MTEST_DELAY 90 /* ms */
+
+#define ADIS16400_FLASH_CNT 0x00 /* Flash memory write count */
+#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */
+#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */
+#define ADIS16400_YGYRO_OUT 0x06 /* Y-axis gyroscope output */
+#define ADIS16400_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */
+#define ADIS16400_XACCL_OUT 0x0A /* X-axis accelerometer output */
+#define ADIS16400_YACCL_OUT 0x0C /* Y-axis accelerometer output */
+#define ADIS16400_ZACCL_OUT 0x0E /* Z-axis accelerometer output */
+#define ADIS16400_XMAGN_OUT 0x10 /* X-axis magnetometer measurement */
+#define ADIS16400_YMAGN_OUT 0x12 /* Y-axis magnetometer measurement */
+#define ADIS16400_ZMAGN_OUT 0x14 /* Z-axis magnetometer measurement */
+#define ADIS16400_TEMP_OUT 0x16 /* Temperature output */
+#define ADIS16400_AUX_ADC 0x18 /* Auxiliary ADC measurement */
+
+#define ADIS16350_XTEMP_OUT 0x10 /* X-axis gyroscope temperature measurement */
+#define ADIS16350_YTEMP_OUT 0x12 /* Y-axis gyroscope temperature measurement */
+#define ADIS16350_ZTEMP_OUT 0x14 /* Z-axis gyroscope temperature measurement */
+
+#define ADIS16300_PITCH_OUT 0x12 /* X axis inclinometer output measurement */
+#define ADIS16300_ROLL_OUT 0x14 /* Y axis inclinometer output measurement */
+#define ADIS16300_AUX_ADC 0x16 /* Auxiliary ADC measurement */
+
+#define ADIS16448_BARO_OUT 0x16 /* Barometric pressure output */
+#define ADIS16448_TEMP_OUT 0x18 /* Temperature output */
+
+/* Calibration parameters */
+#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */
+#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */
+#define ADIS16400_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */
+#define ADIS16400_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */
+#define ADIS16400_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */
+#define ADIS16400_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */
+#define ADIS16400_XMAGN_HIF 0x26 /* X-axis magnetometer, hard-iron factor */
+#define ADIS16400_YMAGN_HIF 0x28 /* Y-axis magnetometer, hard-iron factor */
+#define ADIS16400_ZMAGN_HIF 0x2A /* Z-axis magnetometer, hard-iron factor */
+#define ADIS16400_XMAGN_SIF 0x2C /* X-axis magnetometer, soft-iron factor */
+#define ADIS16400_YMAGN_SIF 0x2E /* Y-axis magnetometer, soft-iron factor */
+#define ADIS16400_ZMAGN_SIF 0x30 /* Z-axis magnetometer, soft-iron factor */
+
+#define ADIS16400_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */
+#define ADIS16400_MSC_CTRL 0x34 /* Miscellaneous control */
+#define ADIS16400_SMPL_PRD 0x36 /* Internal sample period (rate) control */
+#define ADIS16400_SENS_AVG 0x38 /* Dynamic range and digital filter control */
+#define ADIS16400_SLP_CNT 0x3A /* Sleep mode control */
+#define ADIS16400_DIAG_STAT 0x3C /* System status */
+
+/* Alarm functions */
+#define ADIS16400_GLOB_CMD 0x3E /* System command */
+#define ADIS16400_ALM_MAG1 0x40 /* Alarm 1 amplitude threshold */
+#define ADIS16400_ALM_MAG2 0x42 /* Alarm 2 amplitude threshold */
+#define ADIS16400_ALM_SMPL1 0x44 /* Alarm 1 sample size */
+#define ADIS16400_ALM_SMPL2 0x46 /* Alarm 2 sample size */
+#define ADIS16400_ALM_CTRL 0x48 /* Alarm control */
+#define ADIS16400_AUX_DAC 0x4A /* Auxiliary DAC data */
+
+#define ADIS16334_LOT_ID1 0x52 /* Lot identification code 1 */
+#define ADIS16334_LOT_ID2 0x54 /* Lot identification code 2 */
+#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */
+#define ADIS16334_SERIAL_NUMBER 0x58 /* Serial number, lot specific */
+
+#define ADIS16400_ERROR_ACTIVE (1<<14)
+#define ADIS16400_NEW_DATA (1<<14)
+
+/* MSC_CTRL */
+#define ADIS16400_MSC_CTRL_MEM_TEST (1<<11)
+#define ADIS16400_MSC_CTRL_INT_SELF_TEST (1<<10)
+#define ADIS16400_MSC_CTRL_NEG_SELF_TEST (1<<9)
+#define ADIS16400_MSC_CTRL_POS_SELF_TEST (1<<8)
+#define ADIS16400_MSC_CTRL_GYRO_BIAS (1<<7)
+#define ADIS16400_MSC_CTRL_ACCL_ALIGN (1<<6)
+#define ADIS16400_MSC_CTRL_DATA_RDY_EN (1<<2)
+#define ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1)
+#define ADIS16400_MSC_CTRL_DATA_RDY_DIO2 (1<<0)
+
+/* SMPL_PRD */
+#define ADIS16400_SMPL_PRD_TIME_BASE (1<<7)
+#define ADIS16400_SMPL_PRD_DIV_MASK 0x7F
+
+/* DIAG_STAT */
+#define ADIS16400_DIAG_STAT_ZACCL_FAIL 15
+#define ADIS16400_DIAG_STAT_YACCL_FAIL 14
+#define ADIS16400_DIAG_STAT_XACCL_FAIL 13
+#define ADIS16400_DIAG_STAT_XGYRO_FAIL 12
+#define ADIS16400_DIAG_STAT_YGYRO_FAIL 11
+#define ADIS16400_DIAG_STAT_ZGYRO_FAIL 10
+#define ADIS16400_DIAG_STAT_ALARM2 9
+#define ADIS16400_DIAG_STAT_ALARM1 8
+#define ADIS16400_DIAG_STAT_FLASH_CHK 6
+#define ADIS16400_DIAG_STAT_SELF_TEST 5
+#define ADIS16400_DIAG_STAT_OVERFLOW 4
+#define ADIS16400_DIAG_STAT_SPI_FAIL 3
+#define ADIS16400_DIAG_STAT_FLASH_UPT 2
+#define ADIS16400_DIAG_STAT_POWER_HIGH 1
+#define ADIS16400_DIAG_STAT_POWER_LOW 0
+
+/* GLOB_CMD */
+#define ADIS16400_GLOB_CMD_SW_RESET (1<<7)
+#define ADIS16400_GLOB_CMD_P_AUTO_NULL (1<<4)
+#define ADIS16400_GLOB_CMD_FLASH_UPD (1<<3)
+#define ADIS16400_GLOB_CMD_DAC_LATCH (1<<2)
+#define ADIS16400_GLOB_CMD_FAC_CALIB (1<<1)
+#define ADIS16400_GLOB_CMD_AUTO_NULL (1<<0)
+
+/* SLP_CNT */
+#define ADIS16400_SLP_CNT_POWER_OFF (1<<8)
+
+#define ADIS16334_RATE_DIV_SHIFT 8
+#define ADIS16334_RATE_INT_CLK BIT(0)
+
+#define ADIS16400_SPI_SLOW (u32)(300 * 1000)
+#define ADIS16400_SPI_BURST (u32)(1000 * 1000)
+#define ADIS16400_SPI_FAST (u32)(2000 * 1000)
+
+#define ADIS16400_HAS_PROD_ID BIT(0)
+#define ADIS16400_NO_BURST BIT(1)
+#define ADIS16400_HAS_SLOW_MODE BIT(2)
+#define ADIS16400_HAS_SERIAL_NUMBER BIT(3)
+#define ADIS16400_BURST_DIAG_STAT BIT(4)
+
+struct adis16400_state;
+
+struct adis16400_chip_info {
+ const struct iio_chan_spec *channels;
+ const int num_channels;
+ const long flags;
+ unsigned int gyro_scale_micro;
+ unsigned int accel_scale_micro;
+ int temp_scale_nano;
+ int temp_offset;
+ int (*set_freq)(struct adis16400_state *st, unsigned int freq);
+ int (*get_freq)(struct adis16400_state *st);
+};
+
+/**
+ * struct adis16400_state - device instance specific data
+ * @variant: chip variant info
+ * @filt_int: integer part of requested filter frequency
+ * @adis: adis device
+ **/
+struct adis16400_state {
+ struct adis16400_chip_info *variant;
+ int filt_int;
+
+ struct adis adis;
+ unsigned long avail_scan_mask[2];
+};
+
+/* At the moment triggers are only used for ring buffer
+ * filling. This may change!
+ */
+
+enum {
+ ADIS16400_SCAN_SUPPLY,
+ ADIS16400_SCAN_GYRO_X,
+ ADIS16400_SCAN_GYRO_Y,
+ ADIS16400_SCAN_GYRO_Z,
+ ADIS16400_SCAN_ACC_X,
+ ADIS16400_SCAN_ACC_Y,
+ ADIS16400_SCAN_ACC_Z,
+ ADIS16400_SCAN_MAGN_X,
+ ADIS16400_SCAN_MAGN_Y,
+ ADIS16400_SCAN_MAGN_Z,
+ ADIS16400_SCAN_BARO,
+ ADIS16350_SCAN_TEMP_X,
+ ADIS16350_SCAN_TEMP_Y,
+ ADIS16350_SCAN_TEMP_Z,
+ ADIS16300_SCAN_INCLI_X,
+ ADIS16300_SCAN_INCLI_Y,
+ ADIS16400_SCAN_ADC,
+ ADIS16400_SCAN_TIMESTAMP,
+};
#ifdef CONFIG_DEBUG_FS
@@ -145,6 +316,11 @@
ADIS16448,
};
+static struct adis_burst adis16400_burst = {
+ .en = true,
+ .reg_cmd = ADIS16400_GLOB_CMD,
+};
+
static int adis16334_get_freq(struct adis16400_state *st)
{
int ret;
@@ -465,6 +641,51 @@
}
}
+#if IS_ENABLED(CONFIG_IIO_BUFFER)
+static irqreturn_t adis16400_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adis16400_state *st = iio_priv(indio_dev);
+ struct adis *adis = &st->adis;
+ u32 old_speed_hz = st->adis.spi->max_speed_hz;
+ void *buffer;
+ int ret;
+
+ if (!adis->buffer)
+ return -ENOMEM;
+
+ if (!(st->variant->flags & ADIS16400_NO_BURST) &&
+ st->adis.spi->max_speed_hz > ADIS16400_SPI_BURST) {
+ st->adis.spi->max_speed_hz = ADIS16400_SPI_BURST;
+ spi_setup(st->adis.spi);
+ }
+
+ ret = spi_sync(adis->spi, &adis->msg);
+ if (ret)
+ dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret);
+
+ if (!(st->variant->flags & ADIS16400_NO_BURST)) {
+ st->adis.spi->max_speed_hz = old_speed_hz;
+ spi_setup(st->adis.spi);
+ }
+
+ if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
+ buffer = adis->buffer + sizeof(u16);
+ else
+ buffer = adis->buffer;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer,
+ pf->timestamp);
+
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+#else
+#define adis16400_trigger_handler NULL
+#endif /* IS_ENABLED(CONFIG_IIO_BUFFER) */
+
#define ADIS16400_VOLTAGE_CHAN(addr, bits, name, si, chn) { \
.type = IIO_VOLTAGE, \
.indexed = 1, \
@@ -835,7 +1056,7 @@
static const struct iio_info adis16400_info = {
.read_raw = &adis16400_read_raw,
.write_raw = &adis16400_write_raw,
- .update_scan_mode = adis16400_update_scan_mode,
+ .update_scan_mode = adis_update_scan_mode,
.debugfs_reg_access = adis_debugfs_reg_access,
};
@@ -926,6 +1147,9 @@
if (!(st->variant->flags & ADIS16400_NO_BURST)) {
adis16400_setup_chan_mask(st);
indio_dev->available_scan_masks = st->avail_scan_mask;
+ st->adis.burst = &adis16400_burst;
+ if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
+ st->adis.burst->extra_len = sizeof(u16);
}
ret = adis_init(&st->adis, indio_dev, spi, &adis16400_data);
diff --git a/drivers/iio/imu/adis16400.h b/drivers/iio/imu/adis16400.h
deleted file mode 100644
index 73b189c..0000000
--- a/drivers/iio/imu/adis16400.h
+++ /dev/null
@@ -1,215 +0,0 @@
-/*
- * adis16400.h support Analog Devices ADIS16400
- * 3d 18g accelerometers,
- * 3d gyroscopes,
- * 3d 2.5gauss magnetometers via SPI
- *
- * Copyright (c) 2009 Manuel Stahl <manuel.stahl@iis.fraunhofer.de>
- * Copyright (c) 2007 Jonathan Cameron <jic23@kernel.org>
- *
- * Loosely based upon lis3l02dq.h
- *
- * 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.
- */
-
-#ifndef SPI_ADIS16400_H_
-#define SPI_ADIS16400_H_
-
-#include <linux/iio/imu/adis.h>
-
-#define ADIS16400_STARTUP_DELAY 290 /* ms */
-#define ADIS16400_MTEST_DELAY 90 /* ms */
-
-#define ADIS16400_FLASH_CNT 0x00 /* Flash memory write count */
-#define ADIS16400_SUPPLY_OUT 0x02 /* Power supply measurement */
-#define ADIS16400_XGYRO_OUT 0x04 /* X-axis gyroscope output */
-#define ADIS16400_YGYRO_OUT 0x06 /* Y-axis gyroscope output */
-#define ADIS16400_ZGYRO_OUT 0x08 /* Z-axis gyroscope output */
-#define ADIS16400_XACCL_OUT 0x0A /* X-axis accelerometer output */
-#define ADIS16400_YACCL_OUT 0x0C /* Y-axis accelerometer output */
-#define ADIS16400_ZACCL_OUT 0x0E /* Z-axis accelerometer output */
-#define ADIS16400_XMAGN_OUT 0x10 /* X-axis magnetometer measurement */
-#define ADIS16400_YMAGN_OUT 0x12 /* Y-axis magnetometer measurement */
-#define ADIS16400_ZMAGN_OUT 0x14 /* Z-axis magnetometer measurement */
-#define ADIS16400_TEMP_OUT 0x16 /* Temperature output */
-#define ADIS16400_AUX_ADC 0x18 /* Auxiliary ADC measurement */
-
-#define ADIS16350_XTEMP_OUT 0x10 /* X-axis gyroscope temperature measurement */
-#define ADIS16350_YTEMP_OUT 0x12 /* Y-axis gyroscope temperature measurement */
-#define ADIS16350_ZTEMP_OUT 0x14 /* Z-axis gyroscope temperature measurement */
-
-#define ADIS16300_PITCH_OUT 0x12 /* X axis inclinometer output measurement */
-#define ADIS16300_ROLL_OUT 0x14 /* Y axis inclinometer output measurement */
-#define ADIS16300_AUX_ADC 0x16 /* Auxiliary ADC measurement */
-
-#define ADIS16448_BARO_OUT 0x16 /* Barometric pressure output */
-#define ADIS16448_TEMP_OUT 0x18 /* Temperature output */
-
-/* Calibration parameters */
-#define ADIS16400_XGYRO_OFF 0x1A /* X-axis gyroscope bias offset factor */
-#define ADIS16400_YGYRO_OFF 0x1C /* Y-axis gyroscope bias offset factor */
-#define ADIS16400_ZGYRO_OFF 0x1E /* Z-axis gyroscope bias offset factor */
-#define ADIS16400_XACCL_OFF 0x20 /* X-axis acceleration bias offset factor */
-#define ADIS16400_YACCL_OFF 0x22 /* Y-axis acceleration bias offset factor */
-#define ADIS16400_ZACCL_OFF 0x24 /* Z-axis acceleration bias offset factor */
-#define ADIS16400_XMAGN_HIF 0x26 /* X-axis magnetometer, hard-iron factor */
-#define ADIS16400_YMAGN_HIF 0x28 /* Y-axis magnetometer, hard-iron factor */
-#define ADIS16400_ZMAGN_HIF 0x2A /* Z-axis magnetometer, hard-iron factor */
-#define ADIS16400_XMAGN_SIF 0x2C /* X-axis magnetometer, soft-iron factor */
-#define ADIS16400_YMAGN_SIF 0x2E /* Y-axis magnetometer, soft-iron factor */
-#define ADIS16400_ZMAGN_SIF 0x30 /* Z-axis magnetometer, soft-iron factor */
-
-#define ADIS16400_GPIO_CTRL 0x32 /* Auxiliary digital input/output control */
-#define ADIS16400_MSC_CTRL 0x34 /* Miscellaneous control */
-#define ADIS16400_SMPL_PRD 0x36 /* Internal sample period (rate) control */
-#define ADIS16400_SENS_AVG 0x38 /* Dynamic range and digital filter control */
-#define ADIS16400_SLP_CNT 0x3A /* Sleep mode control */
-#define ADIS16400_DIAG_STAT 0x3C /* System status */
-
-/* Alarm functions */
-#define ADIS16400_GLOB_CMD 0x3E /* System command */
-#define ADIS16400_ALM_MAG1 0x40 /* Alarm 1 amplitude threshold */
-#define ADIS16400_ALM_MAG2 0x42 /* Alarm 2 amplitude threshold */
-#define ADIS16400_ALM_SMPL1 0x44 /* Alarm 1 sample size */
-#define ADIS16400_ALM_SMPL2 0x46 /* Alarm 2 sample size */
-#define ADIS16400_ALM_CTRL 0x48 /* Alarm control */
-#define ADIS16400_AUX_DAC 0x4A /* Auxiliary DAC data */
-
-#define ADIS16334_LOT_ID1 0x52 /* Lot identification code 1 */
-#define ADIS16334_LOT_ID2 0x54 /* Lot identification code 2 */
-#define ADIS16400_PRODUCT_ID 0x56 /* Product identifier */
-#define ADIS16334_SERIAL_NUMBER 0x58 /* Serial number, lot specific */
-
-#define ADIS16400_ERROR_ACTIVE (1<<14)
-#define ADIS16400_NEW_DATA (1<<14)
-
-/* MSC_CTRL */
-#define ADIS16400_MSC_CTRL_MEM_TEST (1<<11)
-#define ADIS16400_MSC_CTRL_INT_SELF_TEST (1<<10)
-#define ADIS16400_MSC_CTRL_NEG_SELF_TEST (1<<9)
-#define ADIS16400_MSC_CTRL_POS_SELF_TEST (1<<8)
-#define ADIS16400_MSC_CTRL_GYRO_BIAS (1<<7)
-#define ADIS16400_MSC_CTRL_ACCL_ALIGN (1<<6)
-#define ADIS16400_MSC_CTRL_DATA_RDY_EN (1<<2)
-#define ADIS16400_MSC_CTRL_DATA_RDY_POL_HIGH (1<<1)
-#define ADIS16400_MSC_CTRL_DATA_RDY_DIO2 (1<<0)
-
-/* SMPL_PRD */
-#define ADIS16400_SMPL_PRD_TIME_BASE (1<<7)
-#define ADIS16400_SMPL_PRD_DIV_MASK 0x7F
-
-/* DIAG_STAT */
-#define ADIS16400_DIAG_STAT_ZACCL_FAIL 15
-#define ADIS16400_DIAG_STAT_YACCL_FAIL 14
-#define ADIS16400_DIAG_STAT_XACCL_FAIL 13
-#define ADIS16400_DIAG_STAT_XGYRO_FAIL 12
-#define ADIS16400_DIAG_STAT_YGYRO_FAIL 11
-#define ADIS16400_DIAG_STAT_ZGYRO_FAIL 10
-#define ADIS16400_DIAG_STAT_ALARM2 9
-#define ADIS16400_DIAG_STAT_ALARM1 8
-#define ADIS16400_DIAG_STAT_FLASH_CHK 6
-#define ADIS16400_DIAG_STAT_SELF_TEST 5
-#define ADIS16400_DIAG_STAT_OVERFLOW 4
-#define ADIS16400_DIAG_STAT_SPI_FAIL 3
-#define ADIS16400_DIAG_STAT_FLASH_UPT 2
-#define ADIS16400_DIAG_STAT_POWER_HIGH 1
-#define ADIS16400_DIAG_STAT_POWER_LOW 0
-
-/* GLOB_CMD */
-#define ADIS16400_GLOB_CMD_SW_RESET (1<<7)
-#define ADIS16400_GLOB_CMD_P_AUTO_NULL (1<<4)
-#define ADIS16400_GLOB_CMD_FLASH_UPD (1<<3)
-#define ADIS16400_GLOB_CMD_DAC_LATCH (1<<2)
-#define ADIS16400_GLOB_CMD_FAC_CALIB (1<<1)
-#define ADIS16400_GLOB_CMD_AUTO_NULL (1<<0)
-
-/* SLP_CNT */
-#define ADIS16400_SLP_CNT_POWER_OFF (1<<8)
-
-#define ADIS16334_RATE_DIV_SHIFT 8
-#define ADIS16334_RATE_INT_CLK BIT(0)
-
-#define ADIS16400_SPI_SLOW (u32)(300 * 1000)
-#define ADIS16400_SPI_BURST (u32)(1000 * 1000)
-#define ADIS16400_SPI_FAST (u32)(2000 * 1000)
-
-#define ADIS16400_HAS_PROD_ID BIT(0)
-#define ADIS16400_NO_BURST BIT(1)
-#define ADIS16400_HAS_SLOW_MODE BIT(2)
-#define ADIS16400_HAS_SERIAL_NUMBER BIT(3)
-#define ADIS16400_BURST_DIAG_STAT BIT(4)
-
-struct adis16400_state;
-
-struct adis16400_chip_info {
- const struct iio_chan_spec *channels;
- const int num_channels;
- const long flags;
- unsigned int gyro_scale_micro;
- unsigned int accel_scale_micro;
- int temp_scale_nano;
- int temp_offset;
- int (*set_freq)(struct adis16400_state *st, unsigned int freq);
- int (*get_freq)(struct adis16400_state *st);
-};
-
-/**
- * struct adis16400_state - device instance specific data
- * @variant: chip variant info
- * @filt_int: integer part of requested filter frequency
- * @adis: adis device
- **/
-struct adis16400_state {
- struct adis16400_chip_info *variant;
- int filt_int;
-
- struct adis adis;
- unsigned long avail_scan_mask[2];
-};
-
-/* At the moment triggers are only used for ring buffer
- * filling. This may change!
- */
-
-enum {
- ADIS16400_SCAN_SUPPLY,
- ADIS16400_SCAN_GYRO_X,
- ADIS16400_SCAN_GYRO_Y,
- ADIS16400_SCAN_GYRO_Z,
- ADIS16400_SCAN_ACC_X,
- ADIS16400_SCAN_ACC_Y,
- ADIS16400_SCAN_ACC_Z,
- ADIS16400_SCAN_MAGN_X,
- ADIS16400_SCAN_MAGN_Y,
- ADIS16400_SCAN_MAGN_Z,
- ADIS16400_SCAN_BARO,
- ADIS16350_SCAN_TEMP_X,
- ADIS16350_SCAN_TEMP_Y,
- ADIS16350_SCAN_TEMP_Z,
- ADIS16300_SCAN_INCLI_X,
- ADIS16300_SCAN_INCLI_Y,
- ADIS16400_SCAN_ADC,
- ADIS16400_SCAN_TIMESTAMP,
-};
-
-#ifdef CONFIG_IIO_BUFFER
-
-ssize_t adis16400_read_data_from_ring(struct device *dev,
- struct device_attribute *attr,
- char *buf);
-
-
-int adis16400_update_scan_mode(struct iio_dev *indio_dev,
- const unsigned long *scan_mask);
-irqreturn_t adis16400_trigger_handler(int irq, void *p);
-
-#else /* CONFIG_IIO_BUFFER */
-
-#define adis16400_update_scan_mode NULL
-#define adis16400_trigger_handler NULL
-
-#endif /* CONFIG_IIO_BUFFER */
-
-#endif /* SPI_ADIS16400_H_ */
diff --git a/drivers/iio/imu/adis16400_buffer.c b/drivers/iio/imu/adis16400_buffer.c
deleted file mode 100644
index e70a533..0000000
--- a/drivers/iio/imu/adis16400_buffer.c
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <linux/kernel.h>
-#include <linux/spi/spi.h>
-#include <linux/slab.h>
-#include <linux/bitops.h>
-#include <linux/export.h>
-
-#include <linux/iio/iio.h>
-#include <linux/iio/buffer.h>
-#include <linux/iio/triggered_buffer.h>
-#include <linux/iio/trigger_consumer.h>
-
-#include "adis16400.h"
-
-int adis16400_update_scan_mode(struct iio_dev *indio_dev,
- const unsigned long *scan_mask)
-{
- struct adis16400_state *st = iio_priv(indio_dev);
- struct adis *adis = &st->adis;
- unsigned int burst_length;
- u8 *tx;
-
- if (st->variant->flags & ADIS16400_NO_BURST)
- return adis_update_scan_mode(indio_dev, scan_mask);
-
- kfree(adis->xfer);
- kfree(adis->buffer);
-
- /* All but the timestamp channel */
- burst_length = (indio_dev->num_channels - 1) * sizeof(u16);
- if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
- burst_length += sizeof(u16);
-
- adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL);
- if (!adis->xfer)
- return -ENOMEM;
-
- adis->buffer = kzalloc(burst_length + sizeof(u16), GFP_KERNEL);
- if (!adis->buffer)
- return -ENOMEM;
-
- tx = adis->buffer + burst_length;
- tx[0] = ADIS_READ_REG(ADIS16400_GLOB_CMD);
- tx[1] = 0;
-
- adis->xfer[0].tx_buf = tx;
- adis->xfer[0].bits_per_word = 8;
- adis->xfer[0].len = 2;
- adis->xfer[1].rx_buf = adis->buffer;
- adis->xfer[1].bits_per_word = 8;
- adis->xfer[1].len = burst_length;
-
- spi_message_init(&adis->msg);
- spi_message_add_tail(&adis->xfer[0], &adis->msg);
- spi_message_add_tail(&adis->xfer[1], &adis->msg);
-
- return 0;
-}
-
-irqreturn_t adis16400_trigger_handler(int irq, void *p)
-{
- struct iio_poll_func *pf = p;
- struct iio_dev *indio_dev = pf->indio_dev;
- struct adis16400_state *st = iio_priv(indio_dev);
- struct adis *adis = &st->adis;
- u32 old_speed_hz = st->adis.spi->max_speed_hz;
- void *buffer;
- int ret;
-
- if (!adis->buffer)
- return -ENOMEM;
-
- if (!(st->variant->flags & ADIS16400_NO_BURST) &&
- st->adis.spi->max_speed_hz > ADIS16400_SPI_BURST) {
- st->adis.spi->max_speed_hz = ADIS16400_SPI_BURST;
- spi_setup(st->adis.spi);
- }
-
- ret = spi_sync(adis->spi, &adis->msg);
- if (ret)
- dev_err(&adis->spi->dev, "Failed to read data: %d\n", ret);
-
- if (!(st->variant->flags & ADIS16400_NO_BURST)) {
- st->adis.spi->max_speed_hz = old_speed_hz;
- spi_setup(st->adis.spi);
- }
-
- if (st->variant->flags & ADIS16400_BURST_DIAG_STAT)
- buffer = adis->buffer + sizeof(u16);
- else
- buffer = adis->buffer;
-
- iio_push_to_buffers_with_timestamp(indio_dev, buffer,
- pf->timestamp);
-
- iio_trigger_notify_done(indio_dev->trig);
-
- return IRQ_HANDLED;
-}
diff --git a/drivers/iio/imu/adis16460.c b/drivers/iio/imu/adis16460.c
new file mode 100644
index 0000000..6aed9e8
--- /dev/null
+++ b/drivers/iio/imu/adis16460.c
@@ -0,0 +1,489 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * ADIS16460 IMU driver
+ *
+ * Copyright 2019 Analog Devices Inc.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/imu/adis.h>
+
+#include <linux/debugfs.h>
+
+#define ADIS16460_REG_FLASH_CNT 0x00
+#define ADIS16460_REG_DIAG_STAT 0x02
+#define ADIS16460_REG_X_GYRO_LOW 0x04
+#define ADIS16460_REG_X_GYRO_OUT 0x06
+#define ADIS16460_REG_Y_GYRO_LOW 0x08
+#define ADIS16460_REG_Y_GYRO_OUT 0x0A
+#define ADIS16460_REG_Z_GYRO_LOW 0x0C
+#define ADIS16460_REG_Z_GYRO_OUT 0x0E
+#define ADIS16460_REG_X_ACCL_LOW 0x10
+#define ADIS16460_REG_X_ACCL_OUT 0x12
+#define ADIS16460_REG_Y_ACCL_LOW 0x14
+#define ADIS16460_REG_Y_ACCL_OUT 0x16
+#define ADIS16460_REG_Z_ACCL_LOW 0x18
+#define ADIS16460_REG_Z_ACCL_OUT 0x1A
+#define ADIS16460_REG_SMPL_CNTR 0x1C
+#define ADIS16460_REG_TEMP_OUT 0x1E
+#define ADIS16460_REG_X_DELT_ANG 0x24
+#define ADIS16460_REG_Y_DELT_ANG 0x26
+#define ADIS16460_REG_Z_DELT_ANG 0x28
+#define ADIS16460_REG_X_DELT_VEL 0x2A
+#define ADIS16460_REG_Y_DELT_VEL 0x2C
+#define ADIS16460_REG_Z_DELT_VEL 0x2E
+#define ADIS16460_REG_MSC_CTRL 0x32
+#define ADIS16460_REG_SYNC_SCAL 0x34
+#define ADIS16460_REG_DEC_RATE 0x36
+#define ADIS16460_REG_FLTR_CTRL 0x38
+#define ADIS16460_REG_GLOB_CMD 0x3E
+#define ADIS16460_REG_X_GYRO_OFF 0x40
+#define ADIS16460_REG_Y_GYRO_OFF 0x42
+#define ADIS16460_REG_Z_GYRO_OFF 0x44
+#define ADIS16460_REG_X_ACCL_OFF 0x46
+#define ADIS16460_REG_Y_ACCL_OFF 0x48
+#define ADIS16460_REG_Z_ACCL_OFF 0x4A
+#define ADIS16460_REG_LOT_ID1 0x52
+#define ADIS16460_REG_LOT_ID2 0x54
+#define ADIS16460_REG_PROD_ID 0x56
+#define ADIS16460_REG_SERIAL_NUM 0x58
+#define ADIS16460_REG_CAL_SGNTR 0x60
+#define ADIS16460_REG_CAL_CRC 0x62
+#define ADIS16460_REG_CODE_SGNTR 0x64
+#define ADIS16460_REG_CODE_CRC 0x66
+
+struct adis16460_chip_info {
+ unsigned int num_channels;
+ const struct iio_chan_spec *channels;
+ unsigned int gyro_max_val;
+ unsigned int gyro_max_scale;
+ unsigned int accel_max_val;
+ unsigned int accel_max_scale;
+};
+
+struct adis16460 {
+ const struct adis16460_chip_info *chip_info;
+ struct adis adis;
+};
+
+#ifdef CONFIG_DEBUG_FS
+
+static int adis16460_show_serial_number(void *arg, u64 *val)
+{
+ struct adis16460 *adis16460 = arg;
+ u16 serial;
+ int ret;
+
+ ret = adis_read_reg_16(&adis16460->adis, ADIS16460_REG_SERIAL_NUM,
+ &serial);
+ if (ret < 0)
+ return ret;
+
+ *val = serial;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16460_serial_number_fops,
+ adis16460_show_serial_number, NULL, "0x%.4llx\n");
+
+static int adis16460_show_product_id(void *arg, u64 *val)
+{
+ struct adis16460 *adis16460 = arg;
+ u16 prod_id;
+ int ret;
+
+ ret = adis_read_reg_16(&adis16460->adis, ADIS16460_REG_PROD_ID,
+ &prod_id);
+ if (ret < 0)
+ return ret;
+
+ *val = prod_id;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16460_product_id_fops,
+ adis16460_show_product_id, NULL, "%llu\n");
+
+static int adis16460_show_flash_count(void *arg, u64 *val)
+{
+ struct adis16460 *adis16460 = arg;
+ u32 flash_count;
+ int ret;
+
+ ret = adis_read_reg_32(&adis16460->adis, ADIS16460_REG_FLASH_CNT,
+ &flash_count);
+ if (ret < 0)
+ return ret;
+
+ *val = flash_count;
+
+ return 0;
+}
+DEFINE_SIMPLE_ATTRIBUTE(adis16460_flash_count_fops,
+ adis16460_show_flash_count, NULL, "%lld\n");
+
+static int adis16460_debugfs_init(struct iio_dev *indio_dev)
+{
+ struct adis16460 *adis16460 = iio_priv(indio_dev);
+
+ debugfs_create_file("serial_number", 0400, indio_dev->debugfs_dentry,
+ adis16460, &adis16460_serial_number_fops);
+ debugfs_create_file("product_id", 0400, indio_dev->debugfs_dentry,
+ adis16460, &adis16460_product_id_fops);
+ debugfs_create_file("flash_count", 0400, indio_dev->debugfs_dentry,
+ adis16460, &adis16460_flash_count_fops);
+
+ return 0;
+}
+
+#else
+
+static int adis16460_debugfs_init(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+
+#endif
+
+static int adis16460_set_freq(struct iio_dev *indio_dev, int val, int val2)
+{
+ struct adis16460 *st = iio_priv(indio_dev);
+ int t;
+
+ t = val * 1000 + val2 / 1000;
+ if (t <= 0)
+ return -EINVAL;
+
+ t = 2048000 / t;
+ if (t > 2048)
+ t = 2048;
+
+ if (t != 0)
+ t--;
+
+ return adis_write_reg_16(&st->adis, ADIS16460_REG_DEC_RATE, t);
+}
+
+static int adis16460_get_freq(struct iio_dev *indio_dev, int *val, int *val2)
+{
+ struct adis16460 *st = iio_priv(indio_dev);
+ uint16_t t;
+ int ret;
+ unsigned int freq;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16460_REG_DEC_RATE, &t);
+ if (ret < 0)
+ return ret;
+
+ freq = 2048000 / (t + 1);
+ *val = freq / 1000;
+ *val2 = (freq % 1000) * 1000;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+}
+
+static int adis16460_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int *val, int *val2, long info)
+{
+ struct adis16460 *st = iio_priv(indio_dev);
+
+ switch (info) {
+ case IIO_CHAN_INFO_RAW:
+ return adis_single_conversion(indio_dev, chan, 0, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_ANGL_VEL:
+ *val = st->chip_info->gyro_max_scale;
+ *val2 = st->chip_info->gyro_max_val;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_ACCEL:
+ *val = st->chip_info->accel_max_scale;
+ *val2 = st->chip_info->accel_max_val;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_TEMP:
+ *val = 50; /* 50 milli degrees Celsius/LSB */
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ *val = 500; /* 25 degrees Celsius = 0x0000 */
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return adis16460_get_freq(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int adis16460_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan, int val, int val2, long info)
+{
+ switch (info) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return adis16460_set_freq(indio_dev, val, val2);
+ default:
+ return -EINVAL;
+ }
+}
+
+enum {
+ ADIS16460_SCAN_GYRO_X,
+ ADIS16460_SCAN_GYRO_Y,
+ ADIS16460_SCAN_GYRO_Z,
+ ADIS16460_SCAN_ACCEL_X,
+ ADIS16460_SCAN_ACCEL_Y,
+ ADIS16460_SCAN_ACCEL_Z,
+ ADIS16460_SCAN_TEMP,
+};
+
+#define ADIS16460_MOD_CHANNEL(_type, _mod, _address, _si, _bits) \
+ { \
+ .type = (_type), \
+ .modified = 1, \
+ .channel2 = (_mod), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .address = (_address), \
+ .scan_index = (_si), \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = (_bits), \
+ .storagebits = (_bits), \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+#define ADIS16460_GYRO_CHANNEL(_mod) \
+ ADIS16460_MOD_CHANNEL(IIO_ANGL_VEL, IIO_MOD_ ## _mod, \
+ ADIS16460_REG_ ## _mod ## _GYRO_LOW, ADIS16460_SCAN_GYRO_ ## _mod, \
+ 32)
+
+#define ADIS16460_ACCEL_CHANNEL(_mod) \
+ ADIS16460_MOD_CHANNEL(IIO_ACCEL, IIO_MOD_ ## _mod, \
+ ADIS16460_REG_ ## _mod ## _ACCL_LOW, ADIS16460_SCAN_ACCEL_ ## _mod, \
+ 32)
+
+#define ADIS16460_TEMP_CHANNEL() { \
+ .type = IIO_TEMP, \
+ .indexed = 1, \
+ .channel = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), \
+ .address = ADIS16460_REG_TEMP_OUT, \
+ .scan_index = ADIS16460_SCAN_TEMP, \
+ .scan_type = { \
+ .sign = 's', \
+ .realbits = 16, \
+ .storagebits = 16, \
+ .endianness = IIO_BE, \
+ }, \
+ }
+
+static const struct iio_chan_spec adis16460_channels[] = {
+ ADIS16460_GYRO_CHANNEL(X),
+ ADIS16460_GYRO_CHANNEL(Y),
+ ADIS16460_GYRO_CHANNEL(Z),
+ ADIS16460_ACCEL_CHANNEL(X),
+ ADIS16460_ACCEL_CHANNEL(Y),
+ ADIS16460_ACCEL_CHANNEL(Z),
+ ADIS16460_TEMP_CHANNEL(),
+ IIO_CHAN_SOFT_TIMESTAMP(7)
+};
+
+static const struct adis16460_chip_info adis16460_chip_info = {
+ .channels = adis16460_channels,
+ .num_channels = ARRAY_SIZE(adis16460_channels),
+ /*
+ * storing the value in rad/degree and the scale in degree
+ * gives us the result in rad and better precession than
+ * storing the scale directly in rad.
+ */
+ .gyro_max_val = IIO_RAD_TO_DEGREE(200 << 16),
+ .gyro_max_scale = 1,
+ .accel_max_val = IIO_M_S_2_TO_G(20000 << 16),
+ .accel_max_scale = 5,
+};
+
+static const struct iio_info adis16460_info = {
+ .read_raw = &adis16460_read_raw,
+ .write_raw = &adis16460_write_raw,
+ .update_scan_mode = adis_update_scan_mode,
+ .debugfs_reg_access = adis_debugfs_reg_access,
+};
+
+static int adis16460_enable_irq(struct adis *adis, bool enable)
+{
+ /*
+ * There is no way to gate the data-ready signal internally inside the
+ * ADIS16460 :(
+ */
+ if (enable)
+ enable_irq(adis->spi->irq);
+ else
+ disable_irq(adis->spi->irq);
+
+ return 0;
+}
+
+static int adis16460_initial_setup(struct iio_dev *indio_dev)
+{
+ struct adis16460 *st = iio_priv(indio_dev);
+ uint16_t prod_id;
+ unsigned int device_id;
+ int ret;
+
+ adis_reset(&st->adis);
+ msleep(222);
+
+ ret = adis_write_reg_16(&st->adis, ADIS16460_REG_GLOB_CMD, BIT(1));
+ if (ret)
+ return ret;
+ msleep(75);
+
+ ret = adis_check_status(&st->adis);
+ if (ret)
+ return ret;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16460_REG_PROD_ID, &prod_id);
+ if (ret)
+ return ret;
+
+ ret = sscanf(indio_dev->name, "adis%u\n", &device_id);
+ if (ret != 1)
+ return -EINVAL;
+
+ if (prod_id != device_id)
+ dev_warn(&indio_dev->dev, "Device ID(%u) and product ID(%u) do not match.",
+ device_id, prod_id);
+
+ return 0;
+}
+
+#define ADIS16460_DIAG_STAT_IN_CLK_OOS 7
+#define ADIS16460_DIAG_STAT_FLASH_MEM 6
+#define ADIS16460_DIAG_STAT_SELF_TEST 5
+#define ADIS16460_DIAG_STAT_OVERRANGE 4
+#define ADIS16460_DIAG_STAT_SPI_COMM 3
+#define ADIS16460_DIAG_STAT_FLASH_UPT 2
+
+static const char * const adis16460_status_error_msgs[] = {
+ [ADIS16460_DIAG_STAT_IN_CLK_OOS] = "Input clock out of sync",
+ [ADIS16460_DIAG_STAT_FLASH_MEM] = "Flash memory failure",
+ [ADIS16460_DIAG_STAT_SELF_TEST] = "Self test diagnostic failure",
+ [ADIS16460_DIAG_STAT_OVERRANGE] = "Sensor overrange",
+ [ADIS16460_DIAG_STAT_SPI_COMM] = "SPI communication failure",
+ [ADIS16460_DIAG_STAT_FLASH_UPT] = "Flash update failure",
+};
+
+static const struct adis_data adis16460_data = {
+ .diag_stat_reg = ADIS16460_REG_DIAG_STAT,
+ .glob_cmd_reg = ADIS16460_REG_GLOB_CMD,
+ .has_paging = false,
+ .read_delay = 5,
+ .write_delay = 5,
+ .cs_change_delay = 16,
+ .status_error_msgs = adis16460_status_error_msgs,
+ .status_error_mask = BIT(ADIS16460_DIAG_STAT_IN_CLK_OOS) |
+ BIT(ADIS16460_DIAG_STAT_FLASH_MEM) |
+ BIT(ADIS16460_DIAG_STAT_SELF_TEST) |
+ BIT(ADIS16460_DIAG_STAT_OVERRANGE) |
+ BIT(ADIS16460_DIAG_STAT_SPI_COMM) |
+ BIT(ADIS16460_DIAG_STAT_FLASH_UPT),
+ .enable_irq = adis16460_enable_irq,
+};
+
+static int adis16460_probe(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev;
+ struct adis16460 *st;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ st = iio_priv(indio_dev);
+
+ st->chip_info = &adis16460_chip_info;
+ indio_dev->dev.parent = &spi->dev;
+ indio_dev->name = spi_get_device_id(spi)->name;
+ indio_dev->channels = st->chip_info->channels;
+ indio_dev->num_channels = st->chip_info->num_channels;
+ indio_dev->info = &adis16460_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = adis_init(&st->adis, indio_dev, spi, &adis16460_data);
+ if (ret)
+ return ret;
+
+ ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
+ if (ret)
+ return ret;
+
+ adis16460_enable_irq(&st->adis, 0);
+
+ ret = adis16460_initial_setup(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer;
+
+ ret = iio_device_register(indio_dev);
+ if (ret)
+ goto error_cleanup_buffer;
+
+ adis16460_debugfs_init(indio_dev);
+
+ return 0;
+
+error_cleanup_buffer:
+ adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+ return ret;
+}
+
+static int adis16460_remove(struct spi_device *spi)
+{
+ struct iio_dev *indio_dev = spi_get_drvdata(spi);
+ struct adis16460 *st = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+
+ adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+
+ return 0;
+}
+
+static const struct spi_device_id adis16460_ids[] = {
+ { "adis16460", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(spi, adis16460_ids);
+
+static const struct of_device_id adis16460_of_match[] = {
+ { .compatible = "adi,adis16460" },
+ {}
+};
+MODULE_DEVICE_TABLE(of, adis16460_of_match);
+
+static struct spi_driver adis16460_driver = {
+ .driver = {
+ .name = "adis16460",
+ .of_match_table = adis16460_of_match,
+ },
+ .id_table = adis16460_ids,
+ .probe = adis16460_probe,
+ .remove = adis16460_remove,
+};
+module_spi_driver(adis16460_driver);
+
+MODULE_AUTHOR("Dragos Bogdan <dragos.bogdan@analog.com>");
+MODULE_DESCRIPTION("Analog Devices ADIS16460 IMU driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/imu/adis16480.c b/drivers/iio/imu/adis16480.c
index a27fe20..8743b2f 100644
--- a/drivers/iio/imu/adis16480.c
+++ b/drivers/iio/imu/adis16480.c
@@ -1,14 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ADIS16480 and similar IMUs driver
*
* Copyright 2012 Analog Devices Inc.
- *
- * 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/clk.h>
+#include <linux/bitfield.h>
+#include <linux/of_irq.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/mutex.h>
@@ -97,6 +96,12 @@
#define ADIS16480_REG_FIRM_DM ADIS16480_REG(0x03, 0x7A)
#define ADIS16480_REG_FIRM_Y ADIS16480_REG(0x03, 0x7C)
+/*
+ * External clock scaling in PPS mode.
+ * Available only for ADIS1649x devices
+ */
+#define ADIS16495_REG_SYNC_SCALE ADIS16480_REG(0x03, 0x10)
+
#define ADIS16480_REG_SERIAL_NUM ADIS16480_REG(0x04, 0x20)
/* Each filter coefficent bank spans two pages */
@@ -107,6 +112,20 @@
#define ADIS16480_FIR_COEF_C(x) ADIS16480_FIR_COEF(0x09, (x))
#define ADIS16480_FIR_COEF_D(x) ADIS16480_FIR_COEF(0x0B, (x))
+/* ADIS16480_REG_FNCTIO_CTRL */
+#define ADIS16480_DRDY_SEL_MSK GENMASK(1, 0)
+#define ADIS16480_DRDY_SEL(x) FIELD_PREP(ADIS16480_DRDY_SEL_MSK, x)
+#define ADIS16480_DRDY_POL_MSK BIT(2)
+#define ADIS16480_DRDY_POL(x) FIELD_PREP(ADIS16480_DRDY_POL_MSK, x)
+#define ADIS16480_DRDY_EN_MSK BIT(3)
+#define ADIS16480_DRDY_EN(x) FIELD_PREP(ADIS16480_DRDY_EN_MSK, x)
+#define ADIS16480_SYNC_SEL_MSK GENMASK(5, 4)
+#define ADIS16480_SYNC_SEL(x) FIELD_PREP(ADIS16480_SYNC_SEL_MSK, x)
+#define ADIS16480_SYNC_EN_MSK BIT(7)
+#define ADIS16480_SYNC_EN(x) FIELD_PREP(ADIS16480_SYNC_EN_MSK, x)
+#define ADIS16480_SYNC_MODE_MSK BIT(8)
+#define ADIS16480_SYNC_MODE(x) FIELD_PREP(ADIS16480_SYNC_MODE_MSK, x)
+
struct adis16480_chip_info {
unsigned int num_channels;
const struct iio_chan_spec *channels;
@@ -114,12 +133,40 @@
unsigned int gyro_max_scale;
unsigned int accel_max_val;
unsigned int accel_max_scale;
+ unsigned int temp_scale;
+ unsigned int int_clk;
+ unsigned int max_dec_rate;
+ const unsigned int *filter_freqs;
+ bool has_pps_clk_mode;
+};
+
+enum adis16480_int_pin {
+ ADIS16480_PIN_DIO1,
+ ADIS16480_PIN_DIO2,
+ ADIS16480_PIN_DIO3,
+ ADIS16480_PIN_DIO4
+};
+
+enum adis16480_clock_mode {
+ ADIS16480_CLK_SYNC,
+ ADIS16480_CLK_PPS,
+ ADIS16480_CLK_INT
};
struct adis16480 {
const struct adis16480_chip_info *chip_info;
struct adis adis;
+ struct clk *ext_clk;
+ enum adis16480_clock_mode clk_mode;
+ unsigned int clk_freq;
+};
+
+static const char * const adis16480_int_pin_names[4] = {
+ [ADIS16480_PIN_DIO1] = "DIO1",
+ [ADIS16480_PIN_DIO2] = "DIO2",
+ [ADIS16480_PIN_DIO3] = "DIO3",
+ [ADIS16480_PIN_DIO4] = "DIO4",
};
#ifdef CONFIG_DEBUG_FS
@@ -268,20 +315,37 @@
static int adis16480_set_freq(struct iio_dev *indio_dev, int val, int val2)
{
struct adis16480 *st = iio_priv(indio_dev);
- unsigned int t;
+ unsigned int t, reg;
- t = val * 1000 + val2 / 1000;
- if (t <= 0)
+ if (val < 0 || val2 < 0)
return -EINVAL;
- t = 2460000 / t;
- if (t > 2048)
- t = 2048;
+ t = val * 1000 + val2 / 1000;
+ if (t == 0)
+ return -EINVAL;
- if (t != 0)
+ /*
+ * When using PPS mode, the rate of data collection is equal to the
+ * product of the external clock frequency and the scale factor in the
+ * SYNC_SCALE register.
+ * When using sync mode, or internal clock, the output data rate is
+ * equal with the clock frequency divided by DEC_RATE + 1.
+ */
+ if (st->clk_mode == ADIS16480_CLK_PPS) {
+ t = t / st->clk_freq;
+ reg = ADIS16495_REG_SYNC_SCALE;
+ } else {
+ t = st->clk_freq / t;
+ reg = ADIS16480_REG_DEC_RATE;
+ }
+
+ if (t > st->chip_info->max_dec_rate)
+ t = st->chip_info->max_dec_rate;
+
+ if ((t != 0) && (st->clk_mode != ADIS16480_CLK_PPS))
t--;
- return adis_write_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, t);
+ return adis_write_reg_16(&st->adis, reg, t);
}
static int adis16480_get_freq(struct iio_dev *indio_dev, int *val, int *val2)
@@ -290,12 +354,29 @@
uint16_t t;
int ret;
unsigned freq;
+ unsigned int reg;
- ret = adis_read_reg_16(&st->adis, ADIS16480_REG_DEC_RATE, &t);
+ if (st->clk_mode == ADIS16480_CLK_PPS)
+ reg = ADIS16495_REG_SYNC_SCALE;
+ else
+ reg = ADIS16480_REG_DEC_RATE;
+
+ ret = adis_read_reg_16(&st->adis, reg, &t);
if (ret < 0)
return ret;
- freq = 2460000 / (t + 1);
+ /*
+ * When using PPS mode, the rate of data collection is equal to the
+ * product of the external clock frequency and the scale factor in the
+ * SYNC_SCALE register.
+ * When using sync mode, or internal clock, the output data rate is
+ * equal with the clock frequency divided by DEC_RATE + 1.
+ */
+ if (st->clk_mode == ADIS16480_CLK_PPS)
+ freq = st->clk_freq * t;
+ else
+ freq = st->clk_freq / (t + 1);
+
*val = freq / 1000;
*val2 = (freq % 1000) * 1000;
@@ -425,6 +506,13 @@
63,
};
+static const unsigned int adis16495_def_filter_freqs[] = {
+ 300,
+ 100,
+ 300,
+ 100,
+};
+
static const unsigned int ad16480_filter_data[][2] = {
[ADIS16480_SCAN_GYRO_X] = { ADIS16480_REG_FILTER_BNK0, 0 },
[ADIS16480_SCAN_GYRO_Y] = { ADIS16480_REG_FILTER_BNK0, 3 },
@@ -456,7 +544,7 @@
if (!(val & enable_mask))
*freq = 0;
else
- *freq = adis16480_def_filter_freqs[(val >> offset) & 0x3];
+ *freq = st->chip_info->filter_freqs[(val >> offset) & 0x3];
return IIO_VAL_INT;
}
@@ -483,10 +571,10 @@
val &= ~enable_mask;
} else {
best_freq = 0;
- best_diff = 310;
+ best_diff = st->chip_info->filter_freqs[0];
for (i = 0; i < ARRAY_SIZE(adis16480_def_filter_freqs); i++) {
- if (adis16480_def_filter_freqs[i] >= freq) {
- diff = adis16480_def_filter_freqs[i] - freq;
+ if (st->chip_info->filter_freqs[i] >= freq) {
+ diff = st->chip_info->filter_freqs[i] - freq;
if (diff < best_diff) {
best_diff = diff;
best_freq = i;
@@ -506,6 +594,7 @@
const struct iio_chan_spec *chan, int *val, int *val2, long info)
{
struct adis16480 *st = iio_priv(indio_dev);
+ unsigned int temp;
switch (info) {
case IIO_CHAN_INFO_RAW:
@@ -525,8 +614,13 @@
*val2 = 100; /* 0.0001 gauss */
return IIO_VAL_INT_PLUS_MICRO;
case IIO_TEMP:
- *val = 5;
- *val2 = 650000; /* 5.65 milli degree Celsius */
+ /*
+ * +85 degrees Celsius = temp_max_scale
+ * +25 degrees Celsius = 0
+ * LSB, 25 degrees Celsius = 60 / temp_max_scale
+ */
+ *val = st->chip_info->temp_scale / 1000;
+ *val2 = (st->chip_info->temp_scale % 1000) * 1000;
return IIO_VAL_INT_PLUS_MICRO;
case IIO_PRESSURE:
*val = 0;
@@ -537,7 +631,8 @@
}
case IIO_CHAN_INFO_OFFSET:
/* Only the temperature channel has a offset */
- *val = 4425; /* 25 degree Celsius = 0x0000 */
+ temp = 25 * 1000000LL; /* 25 degree Celsius = 0x0000 */
+ *val = DIV_ROUND_CLOSEST_ULL(temp, st->chip_info->temp_scale);
return IIO_VAL_INT;
case IIO_CHAN_INFO_CALIBBIAS:
return adis16480_get_calibbias(indio_dev, chan, val);
@@ -678,6 +773,12 @@
ADIS16480,
ADIS16485,
ADIS16488,
+ ADIS16495_1,
+ ADIS16495_2,
+ ADIS16495_3,
+ ADIS16497_1,
+ ADIS16497_2,
+ ADIS16497_3,
};
static const struct adis16480_chip_info adis16480_chip_info[] = {
@@ -693,6 +794,10 @@
.gyro_max_scale = 300,
.accel_max_val = IIO_M_S_2_TO_G(21973),
.accel_max_scale = 18,
+ .temp_scale = 5650, /* 5.65 milli degree Celsius */
+ .int_clk = 2460000,
+ .max_dec_rate = 2048,
+ .filter_freqs = adis16480_def_filter_freqs,
},
[ADIS16480] = {
.channels = adis16480_channels,
@@ -701,6 +806,10 @@
.gyro_max_scale = 450,
.accel_max_val = IIO_M_S_2_TO_G(12500),
.accel_max_scale = 10,
+ .temp_scale = 5650, /* 5.65 milli degree Celsius */
+ .int_clk = 2460000,
+ .max_dec_rate = 2048,
+ .filter_freqs = adis16480_def_filter_freqs,
},
[ADIS16485] = {
.channels = adis16485_channels,
@@ -709,6 +818,10 @@
.gyro_max_scale = 450,
.accel_max_val = IIO_M_S_2_TO_G(20000),
.accel_max_scale = 5,
+ .temp_scale = 5650, /* 5.65 milli degree Celsius */
+ .int_clk = 2460000,
+ .max_dec_rate = 2048,
+ .filter_freqs = adis16480_def_filter_freqs,
},
[ADIS16488] = {
.channels = adis16480_channels,
@@ -717,6 +830,88 @@
.gyro_max_scale = 450,
.accel_max_val = IIO_M_S_2_TO_G(22500),
.accel_max_scale = 18,
+ .temp_scale = 5650, /* 5.65 milli degree Celsius */
+ .int_clk = 2460000,
+ .max_dec_rate = 2048,
+ .filter_freqs = adis16480_def_filter_freqs,
+ },
+ [ADIS16495_1] = {
+ .channels = adis16485_channels,
+ .num_channels = ARRAY_SIZE(adis16485_channels),
+ .gyro_max_val = IIO_RAD_TO_DEGREE(20000),
+ .gyro_max_scale = 125,
+ .accel_max_val = IIO_M_S_2_TO_G(32000),
+ .accel_max_scale = 8,
+ .temp_scale = 12500, /* 12.5 milli degree Celsius */
+ .int_clk = 4250000,
+ .max_dec_rate = 4250,
+ .filter_freqs = adis16495_def_filter_freqs,
+ .has_pps_clk_mode = true,
+ },
+ [ADIS16495_2] = {
+ .channels = adis16485_channels,
+ .num_channels = ARRAY_SIZE(adis16485_channels),
+ .gyro_max_val = IIO_RAD_TO_DEGREE(18000),
+ .gyro_max_scale = 450,
+ .accel_max_val = IIO_M_S_2_TO_G(32000),
+ .accel_max_scale = 8,
+ .temp_scale = 12500, /* 12.5 milli degree Celsius */
+ .int_clk = 4250000,
+ .max_dec_rate = 4250,
+ .filter_freqs = adis16495_def_filter_freqs,
+ .has_pps_clk_mode = true,
+ },
+ [ADIS16495_3] = {
+ .channels = adis16485_channels,
+ .num_channels = ARRAY_SIZE(adis16485_channels),
+ .gyro_max_val = IIO_RAD_TO_DEGREE(20000),
+ .gyro_max_scale = 2000,
+ .accel_max_val = IIO_M_S_2_TO_G(32000),
+ .accel_max_scale = 8,
+ .temp_scale = 12500, /* 12.5 milli degree Celsius */
+ .int_clk = 4250000,
+ .max_dec_rate = 4250,
+ .filter_freqs = adis16495_def_filter_freqs,
+ .has_pps_clk_mode = true,
+ },
+ [ADIS16497_1] = {
+ .channels = adis16485_channels,
+ .num_channels = ARRAY_SIZE(adis16485_channels),
+ .gyro_max_val = IIO_RAD_TO_DEGREE(20000),
+ .gyro_max_scale = 125,
+ .accel_max_val = IIO_M_S_2_TO_G(32000),
+ .accel_max_scale = 40,
+ .temp_scale = 12500, /* 12.5 milli degree Celsius */
+ .int_clk = 4250000,
+ .max_dec_rate = 4250,
+ .filter_freqs = adis16495_def_filter_freqs,
+ .has_pps_clk_mode = true,
+ },
+ [ADIS16497_2] = {
+ .channels = adis16485_channels,
+ .num_channels = ARRAY_SIZE(adis16485_channels),
+ .gyro_max_val = IIO_RAD_TO_DEGREE(18000),
+ .gyro_max_scale = 450,
+ .accel_max_val = IIO_M_S_2_TO_G(32000),
+ .accel_max_scale = 40,
+ .temp_scale = 12500, /* 12.5 milli degree Celsius */
+ .int_clk = 4250000,
+ .max_dec_rate = 4250,
+ .filter_freqs = adis16495_def_filter_freqs,
+ .has_pps_clk_mode = true,
+ },
+ [ADIS16497_3] = {
+ .channels = adis16485_channels,
+ .num_channels = ARRAY_SIZE(adis16485_channels),
+ .gyro_max_val = IIO_RAD_TO_DEGREE(20000),
+ .gyro_max_scale = 2000,
+ .accel_max_val = IIO_M_S_2_TO_G(32000),
+ .accel_max_scale = 40,
+ .temp_scale = 12500, /* 12.5 milli degree Celsius */
+ .int_clk = 4250000,
+ .max_dec_rate = 4250,
+ .filter_freqs = adis16495_def_filter_freqs,
+ .has_pps_clk_mode = true,
},
};
@@ -741,8 +936,17 @@
static int adis16480_enable_irq(struct adis *adis, bool enable)
{
- return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL,
- enable ? BIT(3) : 0);
+ uint16_t val;
+ int ret;
+
+ ret = adis_read_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, &val);
+ if (ret < 0)
+ return ret;
+
+ val &= ~ADIS16480_DRDY_EN_MSK;
+ val |= ADIS16480_DRDY_EN(enable);
+
+ return adis_write_reg_16(adis, ADIS16480_REG_FNCTIO_CTRL, val);
}
static int adis16480_initial_setup(struct iio_dev *indio_dev)
@@ -826,6 +1030,156 @@
.enable_irq = adis16480_enable_irq,
};
+static int adis16480_config_irq_pin(struct device_node *of_node,
+ struct adis16480 *st)
+{
+ struct irq_data *desc;
+ enum adis16480_int_pin pin;
+ unsigned int irq_type;
+ uint16_t val;
+ int i, irq = 0;
+
+ desc = irq_get_irq_data(st->adis.spi->irq);
+ if (!desc) {
+ dev_err(&st->adis.spi->dev, "Could not find IRQ %d\n", irq);
+ return -EINVAL;
+ }
+
+ /* Disable data ready since the default after reset is on */
+ val = ADIS16480_DRDY_EN(0);
+
+ /*
+ * Get the interrupt from the devicetre by reading the interrupt-names
+ * property. If it is not specified, use DIO1 pin as default.
+ * According to the datasheet, the factory default assigns DIO2 as data
+ * ready signal. However, in the previous versions of the driver, DIO1
+ * pin was used. So, we should leave it as is since some devices might
+ * be expecting the interrupt on the wrong physical pin.
+ */
+ pin = ADIS16480_PIN_DIO1;
+ for (i = 0; i < ARRAY_SIZE(adis16480_int_pin_names); i++) {
+ irq = of_irq_get_byname(of_node, adis16480_int_pin_names[i]);
+ if (irq > 0) {
+ pin = i;
+ break;
+ }
+ }
+
+ val |= ADIS16480_DRDY_SEL(pin);
+
+ /*
+ * Get the interrupt line behaviour. The data ready polarity can be
+ * configured as positive or negative, corresponding to
+ * IRQF_TRIGGER_RISING or IRQF_TRIGGER_FALLING respectively.
+ */
+ irq_type = irqd_get_trigger_type(desc);
+ if (irq_type == IRQF_TRIGGER_RISING) { /* Default */
+ val |= ADIS16480_DRDY_POL(1);
+ } else if (irq_type == IRQF_TRIGGER_FALLING) {
+ val |= ADIS16480_DRDY_POL(0);
+ } else {
+ dev_err(&st->adis.spi->dev,
+ "Invalid interrupt type 0x%x specified\n", irq_type);
+ return -EINVAL;
+ }
+ /* Write the data ready configuration to the FNCTIO_CTRL register */
+ return adis_write_reg_16(&st->adis, ADIS16480_REG_FNCTIO_CTRL, val);
+}
+
+static int adis16480_of_get_ext_clk_pin(struct adis16480 *st,
+ struct device_node *of_node)
+{
+ const char *ext_clk_pin;
+ enum adis16480_int_pin pin;
+ int i;
+
+ pin = ADIS16480_PIN_DIO2;
+ if (of_property_read_string(of_node, "adi,ext-clk-pin", &ext_clk_pin))
+ goto clk_input_not_found;
+
+ for (i = 0; i < ARRAY_SIZE(adis16480_int_pin_names); i++) {
+ if (strcasecmp(ext_clk_pin, adis16480_int_pin_names[i]) == 0)
+ return i;
+ }
+
+clk_input_not_found:
+ dev_info(&st->adis.spi->dev,
+ "clk input line not specified, using DIO2\n");
+ return pin;
+}
+
+static int adis16480_ext_clk_config(struct adis16480 *st,
+ struct device_node *of_node,
+ bool enable)
+{
+ unsigned int mode, mask;
+ enum adis16480_int_pin pin;
+ uint16_t val;
+ int ret;
+
+ ret = adis_read_reg_16(&st->adis, ADIS16480_REG_FNCTIO_CTRL, &val);
+ if (ret < 0)
+ return ret;
+
+ pin = adis16480_of_get_ext_clk_pin(st, of_node);
+ /*
+ * Each DIOx pin supports only one function at a time. When a single pin
+ * has two assignments, the enable bit for a lower priority function
+ * automatically resets to zero (disabling the lower priority function).
+ */
+ if (pin == ADIS16480_DRDY_SEL(val))
+ dev_warn(&st->adis.spi->dev,
+ "DIO%x pin supports only one function at a time\n",
+ pin + 1);
+
+ mode = ADIS16480_SYNC_EN(enable) | ADIS16480_SYNC_SEL(pin);
+ mask = ADIS16480_SYNC_EN_MSK | ADIS16480_SYNC_SEL_MSK;
+ /* Only ADIS1649x devices support pps ext clock mode */
+ if (st->chip_info->has_pps_clk_mode) {
+ mode |= ADIS16480_SYNC_MODE(st->clk_mode);
+ mask |= ADIS16480_SYNC_MODE_MSK;
+ }
+
+ val &= ~mask;
+ val |= mode;
+
+ ret = adis_write_reg_16(&st->adis, ADIS16480_REG_FNCTIO_CTRL, val);
+ if (ret < 0)
+ return ret;
+
+ return clk_prepare_enable(st->ext_clk);
+}
+
+static int adis16480_get_ext_clocks(struct adis16480 *st)
+{
+ st->clk_mode = ADIS16480_CLK_INT;
+ st->ext_clk = devm_clk_get(&st->adis.spi->dev, "sync");
+ if (!IS_ERR_OR_NULL(st->ext_clk)) {
+ st->clk_mode = ADIS16480_CLK_SYNC;
+ return 0;
+ }
+
+ if (PTR_ERR(st->ext_clk) != -ENOENT) {
+ dev_err(&st->adis.spi->dev, "failed to get ext clk\n");
+ return PTR_ERR(st->ext_clk);
+ }
+
+ if (st->chip_info->has_pps_clk_mode) {
+ st->ext_clk = devm_clk_get(&st->adis.spi->dev, "pps");
+ if (!IS_ERR_OR_NULL(st->ext_clk)) {
+ st->clk_mode = ADIS16480_CLK_PPS;
+ return 0;
+ }
+
+ if (PTR_ERR(st->ext_clk) != -ENOENT) {
+ dev_err(&st->adis.spi->dev, "failed to get ext clk\n");
+ return PTR_ERR(st->ext_clk);
+ }
+ }
+
+ return 0;
+}
+
static int adis16480_probe(struct spi_device *spi)
{
const struct spi_device_id *id = spi_get_device_id(spi);
@@ -853,10 +1207,29 @@
if (ret)
return ret;
- ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
+ ret = adis16480_config_irq_pin(spi->dev.of_node, st);
if (ret)
return ret;
+ ret = adis16480_get_ext_clocks(st);
+ if (ret)
+ return ret;
+
+ if (!IS_ERR_OR_NULL(st->ext_clk)) {
+ ret = adis16480_ext_clk_config(st, spi->dev.of_node, true);
+ if (ret)
+ return ret;
+
+ st->clk_freq = clk_get_rate(st->ext_clk);
+ st->clk_freq *= 1000; /* micro */
+ } else {
+ st->clk_freq = st->chip_info->int_clk;
+ }
+
+ ret = adis_setup_buffer_and_trigger(&st->adis, indio_dev, NULL);
+ if (ret)
+ goto error_clk_disable_unprepare;
+
ret = adis16480_initial_setup(indio_dev);
if (ret)
goto error_cleanup_buffer;
@@ -873,6 +1246,8 @@
adis16480_stop_device(indio_dev);
error_cleanup_buffer:
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+error_clk_disable_unprepare:
+ clk_disable_unprepare(st->ext_clk);
return ret;
}
@@ -885,6 +1260,7 @@
adis16480_stop_device(indio_dev);
adis_cleanup_buffer_and_trigger(&st->adis, indio_dev);
+ clk_disable_unprepare(st->ext_clk);
return 0;
}
@@ -894,13 +1270,35 @@
{ "adis16480", ADIS16480 },
{ "adis16485", ADIS16485 },
{ "adis16488", ADIS16488 },
+ { "adis16495-1", ADIS16495_1 },
+ { "adis16495-2", ADIS16495_2 },
+ { "adis16495-3", ADIS16495_3 },
+ { "adis16497-1", ADIS16497_1 },
+ { "adis16497-2", ADIS16497_2 },
+ { "adis16497-3", ADIS16497_3 },
{ }
};
MODULE_DEVICE_TABLE(spi, adis16480_ids);
+static const struct of_device_id adis16480_of_match[] = {
+ { .compatible = "adi,adis16375" },
+ { .compatible = "adi,adis16480" },
+ { .compatible = "adi,adis16485" },
+ { .compatible = "adi,adis16488" },
+ { .compatible = "adi,adis16495-1" },
+ { .compatible = "adi,adis16495-2" },
+ { .compatible = "adi,adis16495-3" },
+ { .compatible = "adi,adis16497-1" },
+ { .compatible = "adi,adis16497-2" },
+ { .compatible = "adi,adis16497-3" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, adis16480_of_match);
+
static struct spi_driver adis16480_driver = {
.driver = {
.name = "adis16480",
+ .of_match_table = adis16480_of_match,
},
.id_table = adis16480_ids,
.probe = adis16480_probe,
diff --git a/drivers/iio/imu/adis_buffer.c b/drivers/iio/imu/adis_buffer.c
index 76643c5..4998a89 100644
--- a/drivers/iio/imu/adis_buffer.c
+++ b/drivers/iio/imu/adis_buffer.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Common library for ADIS16XXX devices
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/export.h>
@@ -20,6 +19,46 @@
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/imu/adis.h>
+static int adis_update_scan_mode_burst(struct iio_dev *indio_dev,
+ const unsigned long *scan_mask)
+{
+ struct adis *adis = iio_device_get_drvdata(indio_dev);
+ unsigned int burst_length;
+ u8 *tx;
+
+ /* All but the timestamp channel */
+ burst_length = (indio_dev->num_channels - 1) * sizeof(u16);
+ burst_length += adis->burst->extra_len;
+
+ adis->xfer = kcalloc(2, sizeof(*adis->xfer), GFP_KERNEL);
+ if (!adis->xfer)
+ return -ENOMEM;
+
+ adis->buffer = kzalloc(burst_length + sizeof(u16), GFP_KERNEL);
+ if (!adis->buffer) {
+ kfree(adis->xfer);
+ adis->xfer = NULL;
+ return -ENOMEM;
+ }
+
+ tx = adis->buffer + burst_length;
+ tx[0] = ADIS_READ_REG(adis->burst->reg_cmd);
+ tx[1] = 0;
+
+ adis->xfer[0].tx_buf = tx;
+ adis->xfer[0].bits_per_word = 8;
+ adis->xfer[0].len = 2;
+ adis->xfer[1].rx_buf = adis->buffer;
+ adis->xfer[1].bits_per_word = 8;
+ adis->xfer[1].len = burst_length;
+
+ spi_message_init(&adis->msg);
+ spi_message_add_tail(&adis->xfer[0], &adis->msg);
+ spi_message_add_tail(&adis->xfer[1], &adis->msg);
+
+ return 0;
+}
+
int adis_update_scan_mode(struct iio_dev *indio_dev,
const unsigned long *scan_mask)
{
@@ -32,6 +71,9 @@
kfree(adis->xfer);
kfree(adis->buffer);
+ if (adis->burst && adis->burst->en)
+ return adis_update_scan_mode_burst(indio_dev, scan_mask);
+
scan_count = indio_dev->scan_bytes / 2;
adis->xfer = kcalloc(scan_count + 1, sizeof(*adis->xfer), GFP_KERNEL);
@@ -39,8 +81,11 @@
return -ENOMEM;
adis->buffer = kcalloc(indio_dev->scan_bytes, 2, GFP_KERNEL);
- if (!adis->buffer)
+ if (!adis->buffer) {
+ kfree(adis->xfer);
+ adis->xfer = NULL;
return -ENOMEM;
+ }
rx = adis->buffer;
tx = rx + scan_count;
diff --git a/drivers/iio/imu/adis_trigger.c b/drivers/iio/imu/adis_trigger.c
index 457372f..8b9cd02 100644
--- a/drivers/iio/imu/adis_trigger.c
+++ b/drivers/iio/imu/adis_trigger.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Common library for ADIS16XXX devices
*
* Copyright 2012 Analog Devices Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
- *
- * Licensed under the GPL-2 or later.
*/
#include <linux/interrupt.h>
diff --git a/drivers/iio/imu/bmi160/Kconfig b/drivers/iio/imu/bmi160/Kconfig
index 005c17c..9d14d85 100644
--- a/drivers/iio/imu/bmi160/Kconfig
+++ b/drivers/iio/imu/bmi160/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# BMI160 IMU driver
#
diff --git a/drivers/iio/imu/bmi160/Makefile b/drivers/iio/imu/bmi160/Makefile
index 10365e4..fdcfedd 100644
--- a/drivers/iio/imu/bmi160/Makefile
+++ b/drivers/iio/imu/bmi160/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for Bosch BMI160 IMU
#
diff --git a/drivers/iio/imu/bmi160/bmi160.h b/drivers/iio/imu/bmi160/bmi160.h
index e7b11e7..621f530 100644
--- a/drivers/iio/imu/bmi160/bmi160.h
+++ b/drivers/iio/imu/bmi160/bmi160.h
@@ -2,10 +2,20 @@
#ifndef BMI160_H_
#define BMI160_H_
+#include <linux/iio/iio.h>
+
+struct bmi160_data {
+ struct regmap *regmap;
+ struct iio_trigger *trig;
+};
+
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);
+
+int bmi160_enable_irq(struct regmap *regmap, bool enable);
+
+int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type);
#endif /* BMI160_H_ */
diff --git a/drivers/iio/imu/bmi160/bmi160_core.c b/drivers/iio/imu/bmi160/bmi160_core.c
index c85659c..6af65d6 100644
--- a/drivers/iio/imu/bmi160/bmi160_core.c
+++ b/drivers/iio/imu/bmi160/bmi160_core.c
@@ -1,26 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* BMI160 - Bosch IMU (accel, gyro plus external magnetometer)
*
* Copyright (c) 2016, Intel Corporation.
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
+ * Copyright (c) 2019, Martin Kelly.
*
* IIO core driver for BMI160, with support for I2C/SPI busses
*
- * TODO: magnetometer, interrupts, hardware FIFO
+ * TODO: magnetometer, hardware FIFO
*/
#include <linux/module.h>
#include <linux/regmap.h>
#include <linux/acpi.h>
#include <linux/delay.h>
+#include <linux/irq.h>
+#include <linux/of_irq.h>
#include <linux/iio/iio.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/iio/trigger_consumer.h>
#include <linux/iio/buffer.h>
#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
#include "bmi160.h"
@@ -64,8 +65,32 @@
#define BMI160_CMD_GYRO_PM_FAST_STARTUP 0x17
#define BMI160_CMD_SOFTRESET 0xB6
+#define BMI160_REG_INT_EN 0x51
+#define BMI160_DRDY_INT_EN BIT(4)
+
+#define BMI160_REG_INT_OUT_CTRL 0x53
+#define BMI160_INT_OUT_CTRL_MASK 0x0f
+#define BMI160_INT1_OUT_CTRL_SHIFT 0
+#define BMI160_INT2_OUT_CTRL_SHIFT 4
+#define BMI160_EDGE_TRIGGERED BIT(0)
+#define BMI160_ACTIVE_HIGH BIT(1)
+#define BMI160_OPEN_DRAIN BIT(2)
+#define BMI160_OUTPUT_EN BIT(3)
+
+#define BMI160_REG_INT_LATCH 0x54
+#define BMI160_INT1_LATCH_MASK BIT(4)
+#define BMI160_INT2_LATCH_MASK BIT(5)
+
+/* INT1 and INT2 are in the opposite order as in INT_OUT_CTRL! */
+#define BMI160_REG_INT_MAP 0x56
+#define BMI160_INT1_MAP_DRDY_EN 0x80
+#define BMI160_INT2_MAP_DRDY_EN 0x08
+
#define BMI160_REG_DUMMY 0x7F
+#define BMI160_NORMAL_WRITE_USLEEP 2
+#define BMI160_SUSPENDED_WRITE_USLEEP 450
+
#define BMI160_ACCEL_PMU_MIN_USLEEP 3800
#define BMI160_GYRO_PMU_MIN_USLEEP 80000
#define BMI160_SOFTRESET_USLEEP 1000
@@ -108,8 +133,9 @@
BMI160_NUM_SENSORS /* must be last */
};
-struct bmi160_data {
- struct regmap *regmap;
+enum bmi160_int_pin {
+ BMI160_PIN_INT1,
+ BMI160_PIN_INT2
};
const struct regmap_config bmi160_regmap_config = {
@@ -273,7 +299,7 @@
cmd = bmi160_regs[t].pmu_cmd_suspend;
ret = regmap_write(data->regmap, BMI160_REG_CMD, cmd);
- if (ret < 0)
+ if (ret)
return ret;
usleep_range(bmi160_pmu_time[t], bmi160_pmu_time[t] + 1000);
@@ -305,7 +331,7 @@
int i, ret, val;
ret = regmap_read(data->regmap, bmi160_regs[t].range, &val);
- if (ret < 0)
+ if (ret)
return ret;
for (i = 0; i < bmi160_scale_table[t].num; i++)
@@ -328,7 +354,7 @@
reg = bmi160_regs[t].data + (axis - IIO_MOD_X) * sizeof(sample);
ret = regmap_bulk_read(data->regmap, reg, &sample, sizeof(sample));
- if (ret < 0)
+ if (ret)
return ret;
*val = sign_extend32(le16_to_cpu(sample), 15);
@@ -362,7 +388,7 @@
int i, val, ret;
ret = regmap_read(data->regmap, bmi160_regs[t].config, &val);
- if (ret < 0)
+ if (ret)
return ret;
val &= bmi160_regs[t].config_odr_mask;
@@ -394,13 +420,12 @@
indio_dev->masklength) {
ret = regmap_bulk_read(data->regmap, base + i * sizeof(sample),
&sample, sizeof(sample));
- if (ret < 0)
+ if (ret)
goto done;
buf[j++] = sample;
}
- iio_push_to_buffers_with_timestamp(indio_dev, buf,
- iio_get_time_ns(indio_dev));
+ iio_push_to_buffers_with_timestamp(indio_dev, buf, pf->timestamp);
done:
iio_trigger_notify_done(indio_dev->trig);
return IRQ_HANDLED;
@@ -416,18 +441,18 @@
switch (mask) {
case IIO_CHAN_INFO_RAW:
ret = bmi160_get_data(data, chan->type, chan->channel2, val);
- if (ret < 0)
+ if (ret)
return ret;
return IIO_VAL_INT;
case IIO_CHAN_INFO_SCALE:
*val = 0;
ret = bmi160_get_scale(data,
bmi160_to_sensor(chan->type), val2);
- return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
+ return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
case IIO_CHAN_INFO_SAMP_FREQ:
ret = bmi160_get_odr(data, bmi160_to_sensor(chan->type),
val, val2);
- return ret < 0 ? ret : IIO_VAL_INT_PLUS_MICRO;
+ return ret ? ret : IIO_VAL_INT_PLUS_MICRO;
default:
return -EINVAL;
}
@@ -498,6 +523,186 @@
return dev_name(dev);
}
+static int bmi160_write_conf_reg(struct regmap *regmap, unsigned int reg,
+ unsigned int mask, unsigned int bits,
+ unsigned int write_usleep)
+{
+ int ret;
+ unsigned int val;
+
+ ret = regmap_read(regmap, reg, &val);
+ if (ret)
+ return ret;
+
+ val = (val & ~mask) | bits;
+
+ ret = regmap_write(regmap, reg, val);
+ if (ret)
+ return ret;
+
+ /*
+ * We need to wait after writing before we can write again. See the
+ * datasheet, page 93.
+ */
+ usleep_range(write_usleep, write_usleep + 1000);
+
+ return 0;
+}
+
+static int bmi160_config_pin(struct regmap *regmap, enum bmi160_int_pin pin,
+ bool open_drain, u8 irq_mask,
+ unsigned long write_usleep)
+{
+ int ret;
+ struct device *dev = regmap_get_device(regmap);
+ u8 int_out_ctrl_shift;
+ u8 int_latch_mask;
+ u8 int_map_mask;
+ u8 int_out_ctrl_mask;
+ u8 int_out_ctrl_bits;
+ const char *pin_name;
+
+ switch (pin) {
+ case BMI160_PIN_INT1:
+ int_out_ctrl_shift = BMI160_INT1_OUT_CTRL_SHIFT;
+ int_latch_mask = BMI160_INT1_LATCH_MASK;
+ int_map_mask = BMI160_INT1_MAP_DRDY_EN;
+ break;
+ case BMI160_PIN_INT2:
+ int_out_ctrl_shift = BMI160_INT2_OUT_CTRL_SHIFT;
+ int_latch_mask = BMI160_INT2_LATCH_MASK;
+ int_map_mask = BMI160_INT2_MAP_DRDY_EN;
+ break;
+ }
+ int_out_ctrl_mask = BMI160_INT_OUT_CTRL_MASK << int_out_ctrl_shift;
+
+ /*
+ * Enable the requested pin with the right settings:
+ * - Push-pull/open-drain
+ * - Active low/high
+ * - Edge/level triggered
+ */
+ int_out_ctrl_bits = BMI160_OUTPUT_EN;
+ if (open_drain)
+ /* Default is push-pull. */
+ int_out_ctrl_bits |= BMI160_OPEN_DRAIN;
+ int_out_ctrl_bits |= irq_mask;
+ int_out_ctrl_bits <<= int_out_ctrl_shift;
+
+ ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_OUT_CTRL,
+ int_out_ctrl_mask, int_out_ctrl_bits,
+ write_usleep);
+ if (ret)
+ return ret;
+
+ /* Set the pin to input mode with no latching. */
+ ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_LATCH,
+ int_latch_mask, int_latch_mask,
+ write_usleep);
+ if (ret)
+ return ret;
+
+ /* Map interrupts to the requested pin. */
+ ret = bmi160_write_conf_reg(regmap, BMI160_REG_INT_MAP,
+ int_map_mask, int_map_mask,
+ write_usleep);
+ if (ret) {
+ switch (pin) {
+ case BMI160_PIN_INT1:
+ pin_name = "INT1";
+ break;
+ case BMI160_PIN_INT2:
+ pin_name = "INT2";
+ break;
+ }
+ dev_err(dev, "Failed to configure %s IRQ pin", pin_name);
+ }
+
+ return ret;
+}
+
+int bmi160_enable_irq(struct regmap *regmap, bool enable)
+{
+ unsigned int enable_bit = 0;
+
+ if (enable)
+ enable_bit = BMI160_DRDY_INT_EN;
+
+ return bmi160_write_conf_reg(regmap, BMI160_REG_INT_EN,
+ BMI160_DRDY_INT_EN, enable_bit,
+ BMI160_NORMAL_WRITE_USLEEP);
+}
+EXPORT_SYMBOL(bmi160_enable_irq);
+
+static int bmi160_get_irq(struct device_node *of_node, enum bmi160_int_pin *pin)
+{
+ int irq;
+
+ /* Use INT1 if possible, otherwise fall back to INT2. */
+ irq = of_irq_get_byname(of_node, "INT1");
+ if (irq > 0) {
+ *pin = BMI160_PIN_INT1;
+ return irq;
+ }
+
+ irq = of_irq_get_byname(of_node, "INT2");
+ if (irq > 0)
+ *pin = BMI160_PIN_INT2;
+
+ return irq;
+}
+
+static int bmi160_config_device_irq(struct iio_dev *indio_dev, int irq_type,
+ enum bmi160_int_pin pin)
+{
+ bool open_drain;
+ u8 irq_mask;
+ struct bmi160_data *data = iio_priv(indio_dev);
+ struct device *dev = regmap_get_device(data->regmap);
+
+ /* Level-triggered, active-low is the default if we set all zeroes. */
+ if (irq_type == IRQF_TRIGGER_RISING)
+ irq_mask = BMI160_ACTIVE_HIGH | BMI160_EDGE_TRIGGERED;
+ else if (irq_type == IRQF_TRIGGER_FALLING)
+ irq_mask = BMI160_EDGE_TRIGGERED;
+ else if (irq_type == IRQF_TRIGGER_HIGH)
+ irq_mask = BMI160_ACTIVE_HIGH;
+ else if (irq_type == IRQF_TRIGGER_LOW)
+ irq_mask = 0;
+ else {
+ dev_err(&indio_dev->dev,
+ "Invalid interrupt type 0x%x specified\n", irq_type);
+ return -EINVAL;
+ }
+
+ open_drain = of_property_read_bool(dev->of_node, "drive-open-drain");
+
+ return bmi160_config_pin(data->regmap, pin, open_drain, irq_mask,
+ BMI160_NORMAL_WRITE_USLEEP);
+}
+
+static int bmi160_setup_irq(struct iio_dev *indio_dev, int irq,
+ enum bmi160_int_pin pin)
+{
+ struct irq_data *desc;
+ u32 irq_type;
+ int ret;
+
+ desc = irq_get_irq_data(irq);
+ if (!desc) {
+ dev_err(&indio_dev->dev, "Could not find IRQ %d\n", irq);
+ return -EINVAL;
+ }
+
+ irq_type = irqd_get_trigger_type(desc);
+
+ ret = bmi160_config_device_irq(indio_dev, irq_type, pin);
+ if (ret)
+ return ret;
+
+ return bmi160_probe_trigger(indio_dev, irq, irq_type);
+}
+
static int bmi160_chip_init(struct bmi160_data *data, bool use_spi)
{
int ret;
@@ -505,7 +710,7 @@
struct device *dev = regmap_get_device(data->regmap);
ret = regmap_write(data->regmap, BMI160_REG_CMD, BMI160_CMD_SOFTRESET);
- if (ret < 0)
+ if (ret)
return ret;
usleep_range(BMI160_SOFTRESET_USLEEP, BMI160_SOFTRESET_USLEEP + 1);
@@ -516,12 +721,12 @@
*/
if (use_spi) {
ret = regmap_read(data->regmap, BMI160_REG_DUMMY, &val);
- if (ret < 0)
+ if (ret)
return ret;
}
ret = regmap_read(data->regmap, BMI160_REG_CHIP_ID, &val);
- if (ret < 0) {
+ if (ret) {
dev_err(dev, "Error reading chip id\n");
return ret;
}
@@ -532,20 +737,65 @@
}
ret = bmi160_set_mode(data, BMI160_ACCEL, true);
- if (ret < 0)
+ if (ret)
return ret;
ret = bmi160_set_mode(data, BMI160_GYRO, true);
- if (ret < 0)
+ if (ret)
return ret;
return 0;
}
-static void bmi160_chip_uninit(struct bmi160_data *data)
+static int bmi160_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool enable)
{
- bmi160_set_mode(data, BMI160_GYRO, false);
- bmi160_set_mode(data, BMI160_ACCEL, false);
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct bmi160_data *data = iio_priv(indio_dev);
+
+ return bmi160_enable_irq(data->regmap, enable);
+}
+
+static const struct iio_trigger_ops bmi160_trigger_ops = {
+ .set_trigger_state = &bmi160_data_rdy_trigger_set_state,
+};
+
+int bmi160_probe_trigger(struct iio_dev *indio_dev, int irq, u32 irq_type)
+{
+ struct bmi160_data *data = iio_priv(indio_dev);
+ int ret;
+
+ data->trig = devm_iio_trigger_alloc(&indio_dev->dev, "%s-dev%d",
+ indio_dev->name, indio_dev->id);
+
+ if (data->trig == NULL)
+ return -ENOMEM;
+
+ ret = devm_request_irq(&indio_dev->dev, irq,
+ &iio_trigger_generic_data_rdy_poll,
+ irq_type, "bmi160", data->trig);
+ if (ret)
+ return ret;
+
+ data->trig->dev.parent = regmap_get_device(data->regmap);
+ data->trig->ops = &bmi160_trigger_ops;
+ iio_trigger_set_drvdata(data->trig, indio_dev);
+
+ ret = devm_iio_trigger_register(&indio_dev->dev, data->trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(data->trig);
+
+ return 0;
+}
+
+static void bmi160_chip_uninit(void *data)
+{
+ 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,
@@ -553,6 +803,8 @@
{
struct iio_dev *indio_dev;
struct bmi160_data *data;
+ int irq;
+ enum bmi160_int_pin int_pin;
int ret;
indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
@@ -564,7 +816,11 @@
data->regmap = regmap;
ret = bmi160_chip_init(data, use_spi);
- if (ret < 0)
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(dev, bmi160_chip_uninit, data);
+ if (ret)
return ret;
if (!name && ACPI_HANDLE(dev))
@@ -577,35 +833,26 @@
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &bmi160_info;
- ret = iio_triggered_buffer_setup(indio_dev, NULL,
- bmi160_trigger_handler, NULL);
- if (ret < 0)
- goto uninit;
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time,
+ bmi160_trigger_handler, NULL);
+ if (ret)
+ return ret;
- ret = iio_device_register(indio_dev);
- if (ret < 0)
- goto buffer_cleanup;
+ irq = bmi160_get_irq(dev->of_node, &int_pin);
+ if (irq > 0) {
+ ret = bmi160_setup_irq(indio_dev, irq, int_pin);
+ if (ret)
+ dev_err(&indio_dev->dev, "Failed to setup IRQ %d\n",
+ irq);
+ } else {
+ dev_info(&indio_dev->dev, "Not setting up IRQ trigger\n");
+ }
- return 0;
-buffer_cleanup:
- iio_triggered_buffer_cleanup(indio_dev);
-uninit:
- bmi160_chip_uninit(data);
- return ret;
+ return devm_iio_device_register(dev, indio_dev);
}
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 155a31f..e36f5e8 100644
--- a/drivers/iio/imu/bmi160/bmi160_i2c.c
+++ b/drivers/iio/imu/bmi160/bmi160_i2c.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* BMI160 - Bosch IMU, I2C bits
*
* Copyright (c) 2016, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* 7-bit I2C slave address is:
* - 0x68 if SDO is pulled to GND
* - 0x69 if SDO is pulled to VDDIO
@@ -38,13 +35,6 @@
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 +62,6 @@
.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 d34dfdf..c19e3df 100644
--- a/drivers/iio/imu/bmi160/bmi160_spi.c
+++ b/drivers/iio/imu/bmi160/bmi160_spi.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* BMI160 - Bosch IMU, SPI bits
*
* Copyright (c) 2016, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
*/
#include <linux/acpi.h>
#include <linux/module.h>
@@ -29,13 +27,6 @@
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 +49,6 @@
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/Kconfig b/drivers/iio/imu/inv_mpu6050/Kconfig
index 5483b2e..e4c4c12 100644
--- a/drivers/iio/imu/inv_mpu6050/Kconfig
+++ b/drivers/iio/imu/inv_mpu6050/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# inv-mpu6050 drivers for Invensense MPU devices and combos
#
@@ -13,8 +14,9 @@
select INV_MPU6050_IIO
select REGMAP_I2C
help
- This driver supports the Invensense MPU6050/6500/9150 and ICM20608
- motion tracking devices over I2C.
+ This driver supports the Invensense MPU6000/6050/6500/6515,
+ MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
+ over I2C.
This driver can be built as a module. The module will be called
inv-mpu6050-i2c.
@@ -24,7 +26,8 @@
select INV_MPU6050_IIO
select REGMAP_SPI
help
- This driver supports the Invensense MPU6050/6500/9150 and ICM20608
- motion tracking devices over SPI.
+ This driver supports the Invensense MPU6000/6050/6500/6515,
+ MPU9150/9250/9255 and ICM20608/20602 motion tracking devices
+ over SPI.
This driver can be built as a module. The module will be called
inv-mpu6050-spi.
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
index d78a104..2f8560b 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_acpi.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* inv_mpu_acpi: ACPI processing for creating client devices
* Copyright (c) 2015, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
*/
#ifdef CONFIG_ACPI
@@ -91,18 +83,14 @@
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/inv_mpu6050/inv_mpu_core.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
index d80ef46..868281b 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_core.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Invensense, Inc.
-*
-* This software is licensed under the terms of the GNU General Public
-* License version 2, as published by the Free Software Foundation, and
-* may be copied, distributed, and modified under those terms.
-*
-* 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>
@@ -23,6 +15,7 @@
#include <linux/iio/iio.h>
#include <linux/acpi.h>
#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
#include "inv_mpu_iio.h"
/*
@@ -37,6 +30,29 @@
*/
static const int accel_scale[] = {598, 1196, 2392, 4785};
+static const struct inv_mpu6050_reg_map reg_set_icm20602 = {
+ .sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
+ .lpf = INV_MPU6050_REG_CONFIG,
+ .accel_lpf = INV_MPU6500_REG_ACCEL_CONFIG_2,
+ .user_ctrl = INV_MPU6050_REG_USER_CTRL,
+ .fifo_en = INV_MPU6050_REG_FIFO_EN,
+ .gyro_config = INV_MPU6050_REG_GYRO_CONFIG,
+ .accl_config = INV_MPU6050_REG_ACCEL_CONFIG,
+ .fifo_count_h = INV_MPU6050_REG_FIFO_COUNT_H,
+ .fifo_r_w = INV_MPU6050_REG_FIFO_R_W,
+ .raw_gyro = INV_MPU6050_REG_RAW_GYRO,
+ .raw_accl = INV_MPU6050_REG_RAW_ACCEL,
+ .temperature = INV_MPU6050_REG_TEMPERATURE,
+ .int_enable = INV_MPU6050_REG_INT_ENABLE,
+ .int_status = INV_MPU6050_REG_INT_STATUS,
+ .pwr_mgmt_1 = INV_MPU6050_REG_PWR_MGMT_1,
+ .pwr_mgmt_2 = INV_MPU6050_REG_PWR_MGMT_2,
+ .int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
+ .accl_offset = INV_MPU6500_REG_ACCEL_OFFSET,
+ .gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
+ .i2c_if = INV_ICM20602_REG_I2C_IF,
+};
+
static const struct inv_mpu6050_reg_map reg_set_6500 = {
.sample_rate_div = INV_MPU6050_REG_SAMPLE_RATE_DIV,
.lpf = INV_MPU6050_REG_CONFIG,
@@ -57,6 +73,7 @@
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
.accl_offset = INV_MPU6500_REG_ACCEL_OFFSET,
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
+ .i2c_if = 0,
};
static const struct inv_mpu6050_reg_map reg_set_6050 = {
@@ -77,6 +94,7 @@
.int_pin_cfg = INV_MPU6050_REG_INT_PIN_CFG,
.accl_offset = INV_MPU6050_REG_ACCEL_OFFSET,
.gyro_offset = INV_MPU6050_REG_GYRO_OFFSET,
+ .i2c_if = 0,
};
static const struct inv_mpu6050_chip_config chip_config_6050 = {
@@ -96,48 +114,63 @@
.name = "MPU6050",
.reg = ®_set_6050,
.config = &chip_config_6050,
+ .fifo_size = 1024,
},
{
.whoami = INV_MPU6500_WHOAMI_VALUE,
.name = "MPU6500",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
},
{
.whoami = INV_MPU6515_WHOAMI_VALUE,
.name = "MPU6515",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
},
{
.whoami = INV_MPU6000_WHOAMI_VALUE,
.name = "MPU6000",
.reg = ®_set_6050,
.config = &chip_config_6050,
+ .fifo_size = 1024,
},
{
.whoami = INV_MPU9150_WHOAMI_VALUE,
.name = "MPU9150",
.reg = ®_set_6050,
.config = &chip_config_6050,
+ .fifo_size = 1024,
},
{
.whoami = INV_MPU9250_WHOAMI_VALUE,
.name = "MPU9250",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
},
{
.whoami = INV_MPU9255_WHOAMI_VALUE,
.name = "MPU9255",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
},
{
.whoami = INV_ICM20608_WHOAMI_VALUE,
.name = "ICM20608",
.reg = ®_set_6500,
.config = &chip_config_6050,
+ .fifo_size = 512,
+ },
+ {
+ .whoami = INV_ICM20602_WHOAMI_VALUE,
+ .name = "ICM20602",
+ .reg = ®_set_icm20602,
+ .config = &chip_config_6050,
+ .fifo_size = 1008,
},
};
@@ -439,7 +472,10 @@
return IIO_VAL_INT_PLUS_MICRO;
case IIO_TEMP:
*val = 0;
- *val2 = INV_MPU6050_TEMP_SCALE;
+ if (st->chip_type == INV_ICM20602)
+ *val2 = INV_ICM20602_TEMP_SCALE;
+ else
+ *val2 = INV_MPU6050_TEMP_SCALE;
return IIO_VAL_INT_PLUS_MICRO;
default:
@@ -448,7 +484,10 @@
case IIO_CHAN_INFO_OFFSET:
switch (chan->type) {
case IIO_TEMP:
- *val = INV_MPU6050_TEMP_OFFSET;
+ if (st->chip_type == INV_ICM20602)
+ *val = INV_ICM20602_TEMP_OFFSET;
+ else
+ *val = INV_MPU6050_TEMP_OFFSET;
return IIO_VAL_INT;
default:
@@ -764,12 +803,14 @@
inv_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
- return &((struct inv_mpu6050_state *)iio_priv(indio_dev))->orientation;
+ struct inv_mpu6050_state *data = iio_priv(indio_dev);
+
+ return &data->orientation;
}
static const struct iio_chan_spec_ext_info inv_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_TYPE, inv_get_mount_matrix),
- { },
+ { }
};
#define INV_MPU6050_CHAN(_type, _channel2, _index) \
@@ -813,6 +854,73 @@
INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_MPU6050_SCAN_ACCL_Z),
};
+static const unsigned long inv_mpu_scan_masks[] = {
+ /* 3-axis accel */
+ BIT(INV_MPU6050_SCAN_ACCL_X)
+ | BIT(INV_MPU6050_SCAN_ACCL_Y)
+ | BIT(INV_MPU6050_SCAN_ACCL_Z),
+ /* 3-axis gyro */
+ BIT(INV_MPU6050_SCAN_GYRO_X)
+ | BIT(INV_MPU6050_SCAN_GYRO_Y)
+ | BIT(INV_MPU6050_SCAN_GYRO_Z),
+ /* 6-axis accel + gyro */
+ BIT(INV_MPU6050_SCAN_ACCL_X)
+ | BIT(INV_MPU6050_SCAN_ACCL_Y)
+ | BIT(INV_MPU6050_SCAN_ACCL_Z)
+ | BIT(INV_MPU6050_SCAN_GYRO_X)
+ | BIT(INV_MPU6050_SCAN_GYRO_Y)
+ | BIT(INV_MPU6050_SCAN_GYRO_Z),
+ 0,
+};
+
+static const struct iio_chan_spec inv_icm20602_channels[] = {
+ IIO_CHAN_SOFT_TIMESTAMP(INV_ICM20602_SCAN_TIMESTAMP),
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW)
+ | BIT(IIO_CHAN_INFO_OFFSET)
+ | BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = INV_ICM20602_SCAN_TEMP,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .shift = 0,
+ .endianness = IIO_BE,
+ },
+ },
+
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_X, INV_ICM20602_SCAN_GYRO_X),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Y, INV_ICM20602_SCAN_GYRO_Y),
+ INV_MPU6050_CHAN(IIO_ANGL_VEL, IIO_MOD_Z, INV_ICM20602_SCAN_GYRO_Z),
+
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Y, INV_ICM20602_SCAN_ACCL_Y),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_X, INV_ICM20602_SCAN_ACCL_X),
+ INV_MPU6050_CHAN(IIO_ACCEL, IIO_MOD_Z, INV_ICM20602_SCAN_ACCL_Z),
+};
+
+static const unsigned long inv_icm20602_scan_masks[] = {
+ /* 3-axis accel + temp (mandatory) */
+ BIT(INV_ICM20602_SCAN_ACCL_X)
+ | BIT(INV_ICM20602_SCAN_ACCL_Y)
+ | BIT(INV_ICM20602_SCAN_ACCL_Z)
+ | BIT(INV_ICM20602_SCAN_TEMP),
+ /* 3-axis gyro + temp (mandatory) */
+ BIT(INV_ICM20602_SCAN_GYRO_X)
+ | BIT(INV_ICM20602_SCAN_GYRO_Y)
+ | BIT(INV_ICM20602_SCAN_GYRO_Z)
+ | BIT(INV_ICM20602_SCAN_TEMP),
+ /* 6-axis accel + gyro + temp (mandatory) */
+ BIT(INV_ICM20602_SCAN_ACCL_X)
+ | BIT(INV_ICM20602_SCAN_ACCL_Y)
+ | BIT(INV_ICM20602_SCAN_ACCL_Z)
+ | BIT(INV_ICM20602_SCAN_GYRO_X)
+ | BIT(INV_ICM20602_SCAN_GYRO_Y)
+ | BIT(INV_ICM20602_SCAN_GYRO_Z)
+ | BIT(INV_ICM20602_SCAN_TEMP),
+ 0,
+};
+
/*
* The user can choose any frequency between INV_MPU6050_MIN_FIFO_RATE and
* INV_MPU6050_MAX_FIFO_RATE, but only these frequencies are matched by the
@@ -926,6 +1034,39 @@
return result;
}
+static int inv_mpu_core_enable_regulator(struct inv_mpu6050_state *st)
+{
+ int result;
+
+ result = regulator_enable(st->vddio_supply);
+ if (result) {
+ dev_err(regmap_get_device(st->map),
+ "Failed to enable regulator: %d\n", result);
+ } else {
+ /* Give the device a little bit of time to start up. */
+ usleep_range(35000, 70000);
+ }
+
+ return result;
+}
+
+static int inv_mpu_core_disable_regulator(struct inv_mpu6050_state *st)
+{
+ int result;
+
+ result = regulator_disable(st->vddio_supply);
+ if (result)
+ dev_err(regmap_get_device(st->map),
+ "Failed to disable regulator: %d\n", result);
+
+ return result;
+}
+
+static void inv_mpu_core_disable_regulator_action(void *_data)
+{
+ inv_mpu_core_disable_regulator(_data);
+}
+
int inv_mpu_core_probe(struct regmap *regmap, int irq, const char *name,
int (*inv_mpu_bus_setup)(struct iio_dev *), int chip_type)
{
@@ -956,8 +1097,8 @@
pdata = dev_get_platdata(dev);
if (!pdata) {
- result = of_iio_read_mount_matrix(dev, "mount-matrix",
- &st->orientation);
+ result = iio_read_mount_matrix(dev, "mount-matrix",
+ &st->orientation);
if (result) {
dev_err(dev, "Failed to retrieve mounting matrix %d\n",
result);
@@ -992,6 +1133,27 @@
return -EINVAL;
}
+ st->vddio_supply = devm_regulator_get(dev, "vddio");
+ if (IS_ERR(st->vddio_supply)) {
+ if (PTR_ERR(st->vddio_supply) != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get vddio regulator %d\n",
+ (int)PTR_ERR(st->vddio_supply));
+
+ return PTR_ERR(st->vddio_supply);
+ }
+
+ result = inv_mpu_core_enable_regulator(st);
+ if (result)
+ return result;
+
+ result = devm_add_action_or_reset(dev, inv_mpu_core_disable_regulator_action,
+ st);
+ if (result) {
+ dev_err(dev, "Failed to setup regulator cleanup action %d\n",
+ result);
+ return result;
+ }
+
/* power is turned on inside check chip type*/
result = inv_check_and_setup_chip(st);
if (result)
@@ -1013,8 +1175,16 @@
indio_dev->name = name;
else
indio_dev->name = dev_name(dev);
- indio_dev->channels = inv_mpu_channels;
- indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+
+ if (chip_type == INV_ICM20602) {
+ indio_dev->channels = inv_icm20602_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_icm20602_channels);
+ indio_dev->available_scan_masks = inv_icm20602_scan_masks;
+ } else {
+ indio_dev->channels = inv_mpu_channels;
+ indio_dev->num_channels = ARRAY_SIZE(inv_mpu_channels);
+ indio_dev->available_scan_masks = inv_mpu_scan_masks;
+ }
indio_dev->info = &mpu_info;
indio_dev->modes = INDIO_BUFFER_TRIGGERED;
@@ -1051,7 +1221,12 @@
int result;
mutex_lock(&st->lock);
+ result = inv_mpu_core_enable_regulator(st);
+ if (result)
+ goto out_unlock;
+
result = inv_mpu6050_set_power_itg(st, true);
+out_unlock:
mutex_unlock(&st->lock);
return result;
@@ -1064,6 +1239,7 @@
mutex_lock(&st->lock);
result = inv_mpu6050_set_power_itg(st, false);
+ inv_mpu_core_disable_regulator(st);
mutex_unlock(&st->lock);
return result;
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
index dd758e3..4b8b5a8 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_i2c.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Invensense, Inc.
-*
-* This software is licensed under the terms of the GNU General Public
-* License version 2, as published by the Free Software Foundation, and
-* may be copied, distributed, and modified under those terms.
-*
-* 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/acpi.h>
@@ -127,6 +119,7 @@
st = iio_priv(dev_get_drvdata(&client->dev));
switch (st->chip_type) {
case INV_ICM20608:
+ case INV_ICM20602:
/* no i2c auxiliary bus on the chip */
break;
default:
@@ -179,6 +172,7 @@
{"mpu9250", INV_MPU9250},
{"mpu9255", INV_MPU9255},
{"icm20608", INV_ICM20608},
+ {"icm20602", INV_ICM20602},
{}
};
@@ -213,6 +207,10 @@
.compatible = "invensense,icm20608",
.data = (void *)INV_ICM20608
},
+ {
+ .compatible = "invensense,icm20602",
+ .data = (void *)INV_ICM20602
+ },
{ }
};
MODULE_DEVICE_TABLE(of, inv_of_match);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
index e69a596..5123567 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_iio.h
@@ -1,14 +1,6 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2012 Invensense, Inc.
-*
-* This software is licensed under the terms of the GNU General Public
-* License version 2, as published by the Free Software Foundation, and
-* may be copied, distributed, and modified under those terms.
-*
-* 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/i2c.h>
#include <linux/i2c-mux.h>
@@ -44,6 +36,7 @@
* @int_pin_cfg; Controls interrupt pin configuration.
* @accl_offset: Controls the accelerometer calibration offset.
* @gyro_offset: Controls the gyroscope calibration offset.
+ * @i2c_if: Controls the i2c interface
*/
struct inv_mpu6050_reg_map {
u8 sample_rate_div;
@@ -65,6 +58,7 @@
u8 int_pin_cfg;
u8 accl_offset;
u8 gyro_offset;
+ u8 i2c_if;
};
/*device enum */
@@ -77,6 +71,7 @@
INV_MPU9250,
INV_MPU9255,
INV_ICM20608,
+ INV_ICM20602,
INV_NUM_PARTS
};
@@ -105,12 +100,14 @@
* @name: name of the chip.
* @reg: register map of the chip.
* @config: configuration of the chip.
+ * @fifo_size: size of the FIFO in bytes.
*/
struct inv_mpu6050_hw {
u8 whoami;
u8 *name;
const struct inv_mpu6050_reg_map *reg;
const struct inv_mpu6050_chip_config *config;
+ size_t fifo_size;
};
/*
@@ -129,6 +126,7 @@
* @chip_period: chip internal period estimation (~1kHz).
* @it_timestamp: timestamp from previous interrupt.
* @data_timestamp: timestamp for next data sample.
+ * @vddio_supply voltage regulator for the chip.
*/
struct inv_mpu6050_state {
struct mutex lock;
@@ -149,6 +147,7 @@
s64 chip_period;
s64 it_timestamp;
s64 data_timestamp;
+ struct regulator *vddio_supply;
};
/*register and associated bit definition*/
@@ -193,12 +192,19 @@
#define INV_MPU6050_BIT_PWR_ACCL_STBY 0x38
#define INV_MPU6050_BIT_PWR_GYRO_STBY 0x07
+/* ICM20602 register */
+#define INV_ICM20602_REG_I2C_IF 0x70
+#define INV_ICM20602_BIT_I2C_IF_DIS 0x40
+
#define INV_MPU6050_REG_FIFO_COUNT_H 0x72
#define INV_MPU6050_REG_FIFO_R_W 0x74
#define INV_MPU6050_BYTES_PER_3AXIS_SENSOR 6
#define INV_MPU6050_FIFO_COUNT_BYTE 2
+/* ICM20602 FIFO samples include temperature readings */
+#define INV_ICM20602_BYTES_PER_TEMP_SENSOR 2
+
/* mpu6500 registers */
#define INV_MPU6500_REG_ACCEL_CONFIG_2 0x1D
#define INV_MPU6500_REG_ACCEL_OFFSET 0x77
@@ -220,6 +226,9 @@
#define INV_MPU6050_GYRO_CONFIG_FSR_SHIFT 3
#define INV_MPU6050_ACCL_CONFIG_FSR_SHIFT 3
+#define INV_ICM20602_TEMP_OFFSET 8170
+#define INV_ICM20602_TEMP_SCALE 3060
+
/* 6 + 6 round up and plus 8 */
#define INV_MPU6050_OUTPUT_DATA_SIZE 24
@@ -259,8 +268,9 @@
#define INV_MPU9255_WHOAMI_VALUE 0x73
#define INV_MPU6515_WHOAMI_VALUE 0x74
#define INV_ICM20608_WHOAMI_VALUE 0xAF
+#define INV_ICM20602_WHOAMI_VALUE 0x12
-/* scan element definition */
+/* scan element definition for generic MPU6xxx devices */
enum inv_mpu6050_scan {
INV_MPU6050_SCAN_ACCL_X,
INV_MPU6050_SCAN_ACCL_Y,
@@ -271,6 +281,18 @@
INV_MPU6050_SCAN_TIMESTAMP,
};
+/* scan element definition for ICM20602, which includes temperature */
+enum inv_icm20602_scan {
+ INV_ICM20602_SCAN_ACCL_X,
+ INV_ICM20602_SCAN_ACCL_Y,
+ INV_ICM20602_SCAN_ACCL_Z,
+ INV_ICM20602_SCAN_TEMP,
+ INV_ICM20602_SCAN_GYRO_X,
+ INV_ICM20602_SCAN_GYRO_Y,
+ INV_ICM20602_SCAN_GYRO_Z,
+ INV_ICM20602_SCAN_TIMESTAMP,
+};
+
enum inv_mpu6050_filter_e {
INV_MPU6050_FILTER_256HZ_NOLPF2 = 0,
INV_MPU6050_FILTER_188HZ,
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
index 548e042..72d8c57 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_ring.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Invensense, Inc.
-*
-* This software is licensed under the terms of the GNU General Public
-* License version 2, as published by the Free Software Foundation, and
-* may be copied, distributed, and modified under those terms.
-*
-* 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>
@@ -188,9 +180,6 @@
"failed to ack interrupt\n");
goto flush_fifo;
}
- /* handle fifo overflow by reseting fifo */
- if (int_status & INV_MPU6050_BIT_FIFO_OVERFLOW_INT)
- goto flush_fifo;
if (!(int_status & INV_MPU6050_BIT_RAW_DATA_RDY_INT)) {
dev_warn(regmap_get_device(st->map),
"spurious interrupt with status 0x%x\n", int_status);
@@ -207,6 +196,9 @@
if (st->chip_config.gyro_fifo_enable)
bytes_per_datum += INV_MPU6050_BYTES_PER_3AXIS_SENSOR;
+ if (st->chip_type == INV_ICM20602)
+ bytes_per_datum += INV_ICM20602_BYTES_PER_TEMP_SENSOR;
+
/*
* read fifo_count register to know how many bytes are inside the FIFO
* right now
@@ -216,6 +208,18 @@
if (result)
goto end_session;
fifo_count = get_unaligned_be16(&data[0]);
+
+ /*
+ * Handle fifo overflow by resetting fifo.
+ * Reset if there is only 3 data set free remaining to mitigate
+ * possible delay between reading fifo count and fifo data.
+ */
+ nb = 3 * bytes_per_datum;
+ if (fifo_count >= st->hw->fifo_size - nb) {
+ dev_warn(regmap_get_device(st->map), "fifo overflow reset\n");
+ goto flush_fifo;
+ }
+
/* compute and process all complete datum */
nb = fifo_count / bytes_per_datum;
inv_mpu6050_update_period(st, pf->timestamp, nb);
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
index 227f50a..142692f 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_spi.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2015 Intel Corporation Inc.
-*
-* This software is licensed under the terms of the GNU General Public
-* License version 2, as published by the Free Software Foundation, and
-* may be copied, distributed, and modified under those terms.
-*
-* 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>
#include <linux/acpi.h>
@@ -31,9 +23,14 @@
if (ret)
return ret;
- st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
- ret = regmap_write(st->map, st->reg->user_ctrl,
- st->chip_config.user_ctrl);
+ if (st->reg->i2c_if) {
+ ret = regmap_write(st->map, st->reg->i2c_if,
+ INV_ICM20602_BIT_I2C_IF_DIS);
+ } else {
+ st->chip_config.user_ctrl |= INV_MPU6050_BIT_I2C_IF_DIS;
+ ret = regmap_write(st->map, st->reg->user_ctrl,
+ st->chip_config.user_ctrl);
+ }
if (ret) {
inv_mpu6050_set_power_itg(st, false);
return ret;
@@ -81,6 +78,7 @@
{"mpu9250", INV_MPU9250},
{"mpu9255", INV_MPU9255},
{"icm20608", INV_ICM20608},
+ {"icm20602", INV_ICM20602},
{}
};
diff --git a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
index 6c3e165..dd55e70 100644
--- a/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
+++ b/drivers/iio/imu/inv_mpu6050/inv_mpu_trigger.c
@@ -1,14 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2012 Invensense, Inc.
-*
-* This software is licensed under the terms of the GNU General Public
-* License version 2, as published by the Free Software Foundation, and
-* may be copied, distributed, and modified under those terms.
-*
-* 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 "inv_mpu_iio.h"
diff --git a/drivers/iio/imu/kmx61.c b/drivers/iio/imu/kmx61.c
index 44b3f53..e674661 100644
--- a/drivers/iio/imu/kmx61.c
+++ b/drivers/iio/imu/kmx61.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* KMX61 - Kionix 6-axis Accelerometer/Magnetometer
*
* Copyright (c) 2014, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for KMX61 (7-bit I2C slave address 0x0E or 0x0F).
- *
*/
#include <linux/module.h>
diff --git a/drivers/iio/imu/st_lsm6dsx/Kconfig b/drivers/iio/imu/st_lsm6dsx/Kconfig
index ccc817e..77aa0e7 100644
--- a/drivers/iio/imu/st_lsm6dsx/Kconfig
+++ b/drivers/iio/imu/st_lsm6dsx/Kconfig
@@ -1,15 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0-only
config IIO_ST_LSM6DSX
tristate "ST_LSM6DSx driver for STM 6-axis IMU MEMS sensors"
- depends on (I2C || SPI)
+ depends on (I2C || SPI || I3C)
select IIO_BUFFER
select IIO_KFIFO_BUF
select IIO_ST_LSM6DSX_I2C if (I2C)
select IIO_ST_LSM6DSX_SPI if (SPI_MASTER)
+ select IIO_ST_LSM6DSX_I3C if (I3C)
help
Say yes here to build support for STMicroelectronics LSM6DSx imu
sensor. Supported devices: lsm6ds3, lsm6ds3h, lsm6dsl, lsm6dsm,
- ism330dlc
+ ism330dlc, lsm6dso, lsm6dsox, asm330lhh, lsm6dsr, lsm6ds3tr-c,
+ ism330dhcx and the accelerometer/gyroscope of lsm9ds1.
To compile this driver as a module, choose M here: the module
will be called st_lsm6dsx.
@@ -23,3 +26,8 @@
tristate
depends on IIO_ST_LSM6DSX
select REGMAP_SPI
+
+config IIO_ST_LSM6DSX_I3C
+ tristate
+ depends on IIO_ST_LSM6DSX
+ select REGMAP_I3C
diff --git a/drivers/iio/imu/st_lsm6dsx/Makefile b/drivers/iio/imu/st_lsm6dsx/Makefile
index 35919fe..57cbcd6 100644
--- a/drivers/iio/imu/st_lsm6dsx/Makefile
+++ b/drivers/iio/imu/st_lsm6dsx/Makefile
@@ -1,5 +1,8 @@
-st_lsm6dsx-y := st_lsm6dsx_core.o st_lsm6dsx_buffer.o
+# SPDX-License-Identifier: GPL-2.0-only
+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
obj-$(CONFIG_IIO_ST_LSM6DSX_SPI) += st_lsm6dsx_spi.o
+obj-$(CONFIG_IIO_ST_LSM6DSX_I3C) += st_lsm6dsx_i3c.o
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
index edcd838..0fe6999 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics st_lsm6dsx sensor driver
*
@@ -5,8 +6,6 @@
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#ifndef ST_LSM6DSX_H
@@ -19,6 +18,13 @@
#define ST_LSM6DSL_DEV_NAME "lsm6dsl"
#define ST_LSM6DSM_DEV_NAME "lsm6dsm"
#define ST_ISM330DLC_DEV_NAME "ism330dlc"
+#define ST_LSM6DSO_DEV_NAME "lsm6dso"
+#define ST_ASM330LHH_DEV_NAME "asm330lhh"
+#define ST_LSM6DSOX_DEV_NAME "lsm6dsox"
+#define ST_LSM6DSR_DEV_NAME "lsm6dsr"
+#define ST_LSM6DS3TRC_DEV_NAME "lsm6ds3tr-c"
+#define ST_ISM330DHCX_DEV_NAME "ism330dhcx"
+#define ST_LSM9DS1_DEV_NAME "lsm9ds1-imu"
enum st_lsm6dsx_hw_id {
ST_LSM6DS3_ID,
@@ -26,28 +32,89 @@
ST_LSM6DSL_ID,
ST_LSM6DSM_ID,
ST_ISM330DLC_ID,
+ ST_LSM6DSO_ID,
+ ST_ASM330LHH_ID,
+ ST_LSM6DSOX_ID,
+ ST_LSM6DSR_ID,
+ ST_LSM6DS3TRC_ID,
+ ST_ISM330DHCX_ID,
+ ST_LSM9DS1_ID,
ST_LSM6DSX_MAX_ID,
};
-#define ST_LSM6DSX_BUFF_SIZE 400
+#define ST_LSM6DSX_BUFF_SIZE 512
#define ST_LSM6DSX_CHAN_SIZE 2
#define ST_LSM6DSX_SAMPLE_SIZE 6
+#define ST_LSM6DSX_TAG_SIZE 1
+#define ST_LSM6DSX_TAGGED_SAMPLE_SIZE (ST_LSM6DSX_SAMPLE_SIZE + \
+ ST_LSM6DSX_TAG_SIZE)
#define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \
ST_LSM6DSX_SAMPLE_SIZE)
+#define ST_LSM6DSX_MAX_TAGGED_WORD_LEN ((32 / ST_LSM6DSX_TAGGED_SAMPLE_SIZE) \
+ * 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), \
+ .info_mask_shared_by_type = 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;
};
+struct st_lsm6dsx_sensor;
+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];
+ int fs_len;
+};
+
/**
* struct st_lsm6dsx_fifo_ops - ST IMU FIFO settings
+ * @update_fifo: Update FIFO configuration callback.
+ * @read_fifo: Read FIFO callback.
* @fifo_th: FIFO threshold register info (addr + mask).
* @fifo_diff: FIFO diff status register info (addr + mask).
* @th_wl: FIFO threshold word length.
*/
struct st_lsm6dsx_fifo_ops {
+ int (*update_fifo)(struct st_lsm6dsx_sensor *sensor, bool enable);
+ int (*read_fifo)(struct st_lsm6dsx_hw *hw);
struct {
u8 addr;
u16 mask;
@@ -74,26 +141,115 @@
};
/**
+ * 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.
+ * @int1_addr: Control Register address for INT1
+ * @int2_addr: Control Register address for INT2
+ * @reset_addr: register address for reset/reboot
* @max_fifo_size: Sensor max fifo length in FIFO words.
- * @id: List of hw id supported by the driver configuration.
+ * @id: List of hw id/device name supported by the driver configuration.
+ * @channels: IIO channels supported by the device.
+ * @odr_table: Hw sensors odr table (Hz + val).
+ * @fs_table: Hw sensors gain table (gain + val).
* @decimator: List of decimator register info (addr + mask).
+ * @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;
+ u8 int1_addr;
+ u8 int2_addr;
+ u8 reset_addr;
u16 max_fifo_size;
- enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID];
+ struct {
+ enum st_lsm6dsx_hw_id hw_id;
+ const char *name;
+ } id[ST_LSM6DSX_MAX_ID];
+ struct {
+ const struct iio_chan_spec *chan;
+ int len;
+ } channels[2];
+ struct st_lsm6dsx_odr_table_entry odr_table[2];
+ struct st_lsm6dsx_fs_table_entry fs_table[2];
struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID];
+ 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,
};
@@ -113,6 +269,7 @@
* @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];
@@ -126,6 +283,11 @@
u8 sip;
u8 decimator;
s64 ts_ref;
+
+ struct {
+ const struct st_lsm6dsx_ext_dev_settings *settings;
+ u8 addr;
+ } ext_info;
};
/**
@@ -135,7 +297,9 @@
* @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.
+ * @suspend_mask: Suspended sensor bitmask.
* @enable_mask: Enabled sensor bitmask.
* @ts_sip: Total number of timestamp samples in a given pattern.
* @sip: Total number of samples (acc/gyro/ts) in a given pattern.
@@ -150,8 +314,10 @@
struct mutex fifo_lock;
struct mutex conf_lock;
+ struct mutex page_lock;
enum st_lsm6dsx_fifo_mode fifo_mode;
+ u8 suspend_mask;
u8 enable_mask;
u8 ts_sip;
u8 sip;
@@ -163,17 +329,65 @@
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,
+int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
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_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable);
int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw);
int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw,
enum st_lsm6dsx_fifo_mode fifo_mode);
+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 631360b..b0f3da1 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c
@@ -1,10 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics st_lsm6dsx FIFO buffer library driver
*
- * LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC: The FIFO buffer can be
- * configured to store data from gyroscope and accelerometer. Samples are
- * queued without any tag according to a specific pattern based on
- * 'FIFO data sets' (6 bytes each):
+ * LSM6DS3/LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C:
+ * The FIFO buffer can be configured to store data from gyroscope and
+ * accelerometer. Samples are queued without any tag according to a
+ * specific pattern based on 'FIFO data sets' (6 bytes each):
* - 1st data set is reserved for gyroscope data
* - 2nd data set is reserved for accelerometer data
* The FIFO pattern changes depending on the ODRs and decimation factors
@@ -12,6 +13,12 @@
* buffer contains the data of all the enabled FIFO data sets
* (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated depending on the
* value of the decimation factor and ODR set for each FIFO data set.
+ *
+ * LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR/ISM330DHCX: The FIFO buffer can be
+ * configured to store data from gyroscope and accelerometer. Each sample
+ * is queued with a tag (1B) indicating data source (gyroscope, accelerometer,
+ * hw timer).
+ *
* FIFO supported modes:
* - BYPASS: FIFO disabled
* - CONTINUOUS: FIFO enabled. When the buffer is full, the FIFO index
@@ -21,8 +28,6 @@
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
@@ -46,6 +51,7 @@
#define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3)
#define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12)
#define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e
+#define ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR 0x78
#define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42
#define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08
@@ -58,6 +64,15 @@
u8 val;
};
+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
struct st_lsm6dsx_decimator_entry st_lsm6dsx_decimator_table[] = {
{ 0, 0x0 },
@@ -90,6 +105,9 @@
*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)))
@@ -113,6 +131,9 @@
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)) {
@@ -130,8 +151,9 @@
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;
}
@@ -150,8 +172,8 @@
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;
}
@@ -159,12 +181,12 @@
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;
@@ -177,12 +199,34 @@
bool enable)
{
struct st_lsm6dsx_hw *hw = sensor->hw;
+ const struct st_lsm6dsx_reg *batch_reg;
u8 data;
- 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));
+ batch_reg = &hw->settings->batch[sensor->id];
+ if (batch_reg->addr) {
+ int val;
+
+ if (enable) {
+ int err;
+
+ err = st_lsm6dsx_check_odr(sensor, sensor->odr,
+ &data);
+ if (err < 0)
+ return err;
+ } else {
+ data = 0;
+ }
+ val = ST_LSM6DSX_SHIFT_VAL(data, batch_reg->mask);
+ 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 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));
+ }
}
int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark)
@@ -197,6 +241,9 @@
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)))
@@ -212,19 +259,23 @@
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)
@@ -233,12 +284,15 @@
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
@@ -250,21 +304,21 @@
}
/*
- * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid
- * a kmalloc for each bus access
+ * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN/ST_LSM6DSX_MAX_TAGGED_WORD_LEN
+ * in order to avoid a kmalloc for each bus access
*/
-static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data,
- unsigned int data_len)
+static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 addr,
+ u8 *data, unsigned int data_len,
+ unsigned int max_word_len)
{
unsigned int word_len, read_len = 0;
int err;
while (read_len < data_len) {
word_len = min_t(unsigned int, data_len - read_len,
- ST_LSM6DSX_MAX_WORD_LEN);
- err = regmap_bulk_read(hw->regmap,
- ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
- data + read_len, word_len);
+ max_word_len);
+ err = st_lsm6dsx_read_locked(hw, addr, data + read_len,
+ word_len);
if (err < 0)
return err;
read_len += word_len;
@@ -282,7 +336,7 @@
*
* Return: Number of bytes read from the FIFO
*/
-static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
+int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw)
{
u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE;
u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask;
@@ -294,9 +348,9 @@
__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);
@@ -314,7 +368,9 @@
gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]);
for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
- err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len);
+ err = st_lsm6dsx_read_block(hw, ST_LSM6DSX_REG_FIFO_OUTL_ADDR,
+ hw->buff, pattern_len,
+ ST_LSM6DSX_MAX_WORD_LEN);
if (err < 0) {
dev_err(hw->dev,
"failed to read pattern from fifo (err=%d)\n",
@@ -400,13 +456,145 @@
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() - tagged hw FIFO read routine
+ * @hw: Pointer to instance of struct st_lsm6dsx_hw.
+ *
+ * Read samples from the hw FIFO and push them to IIO buffers.
+ *
+ * Return: Number of bytes read from the FIFO
+ */
+int st_lsm6dsx_read_tagged_fifo(struct st_lsm6dsx_hw *hw)
+{
+ u16 pattern_len = hw->sip * ST_LSM6DSX_TAGGED_SAMPLE_SIZE;
+ u16 fifo_len, fifo_diff_mask;
+ u8 iio_buff[ST_LSM6DSX_IIO_BUFF_SIZE], tag;
+ bool reset_ts = false;
+ int i, err, read_len;
+ __le16 fifo_status;
+ s64 ts = 0;
+
+ 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);
+ return err;
+ }
+
+ fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask;
+ fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) *
+ ST_LSM6DSX_TAGGED_SAMPLE_SIZE;
+ if (!fifo_len)
+ return 0;
+
+ for (read_len = 0; read_len < fifo_len; read_len += pattern_len) {
+ err = st_lsm6dsx_read_block(hw,
+ ST_LSM6DSX_REG_FIFO_OUT_TAG_ADDR,
+ hw->buff, pattern_len,
+ ST_LSM6DSX_MAX_TAGGED_WORD_LEN);
+ if (err < 0) {
+ dev_err(hw->dev,
+ "failed to read pattern from fifo (err=%d)\n",
+ err);
+ return err;
+ }
+
+ for (i = 0; i < pattern_len;
+ i += ST_LSM6DSX_TAGGED_SAMPLE_SIZE) {
+ memcpy(iio_buff, &hw->buff[i + ST_LSM6DSX_TAG_SIZE],
+ ST_LSM6DSX_SAMPLE_SIZE);
+
+ tag = hw->buff[i] >> 3;
+ if (tag == ST_LSM6DSX_TS_TAG) {
+ /*
+ * hw timestamp is 4B long and it is stored
+ * in FIFO according to this schema:
+ * B0 = ts[7:0], B1 = ts[15:8], B2 = ts[23:16],
+ * B3 = ts[31:24]
+ */
+ ts = le32_to_cpu(*((__le32 *)iio_buff));
+ /*
+ * check if hw timestamp engine is going to
+ * reset (the sensor generates an interrupt
+ * to signal the hw timestamp will reset in
+ * 1.638s)
+ */
+ if (!reset_ts && ts >= 0xffff0000)
+ reset_ts = true;
+ ts *= ST_LSM6DSX_TS_SENSITIVITY;
+ } else {
+ st_lsm6dsx_push_tagged_data(hw, tag, iio_buff,
+ ts);
+ }
+ }
+ }
+
+ if (unlikely(reset_ts)) {
+ err = st_lsm6dsx_reset_hw_ts(hw);
+ if (err < 0)
+ return err;
+ }
+ return read_len;
+}
+
int st_lsm6dsx_flush_fifo(struct st_lsm6dsx_hw *hw)
{
int err;
mutex_lock(&hw->fifo_lock);
- st_lsm6dsx_read_fifo(hw);
+ hw->settings->fifo_ops.read_fifo(hw);
err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_BYPASS);
mutex_unlock(&hw->fifo_lock);
@@ -414,9 +602,8 @@
return err;
}
-static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable)
+int st_lsm6dsx_update_fifo(struct st_lsm6dsx_sensor *sensor, bool enable)
{
- struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
struct st_lsm6dsx_hw *hw = sensor->hw;
int err;
@@ -428,20 +615,22 @@
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)
goto out;
@@ -478,20 +667,32 @@
int count;
mutex_lock(&hw->fifo_lock);
- count = st_lsm6dsx_read_fifo(hw);
+ count = hw->settings->fifo_ops.read_fifo(hw);
mutex_unlock(&hw->fifo_lock);
- return !count ? IRQ_NONE : IRQ_HANDLED;
+ return count ? IRQ_HANDLED : IRQ_NONE;
}
static int st_lsm6dsx_buffer_preenable(struct iio_dev *iio_dev)
{
- return st_lsm6dsx_update_fifo(iio_dev, true);
+ struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+
+ if (!hw->settings->fifo_ops.update_fifo)
+ return -ENOTSUPP;
+
+ return hw->settings->fifo_ops.update_fifo(sensor, true);
}
static int st_lsm6dsx_buffer_postdisable(struct iio_dev *iio_dev)
{
- return st_lsm6dsx_update_fifo(iio_dev, false);
+ struct st_lsm6dsx_sensor *sensor = iio_priv(iio_dev);
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+
+ if (!hw->settings->fifo_ops.update_fifo)
+ return -ENOTSUPP;
+
+ return hw->settings->fifo_ops.update_fifo(sensor, false);
}
static const struct iio_buffer_setup_ops st_lsm6dsx_buffer_ops = {
@@ -556,6 +757,9 @@
}
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 aebbe0d..fd5ebe1 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics st_lsm6dsx sensor driver
*
@@ -9,6 +10,8 @@
* +-125/+-245/+-500/+-1000/+-2000 dps
* LSM6DSx series has an integrated First-In-First-Out (FIFO) buffer
* allowing dynamic batching of sensor data.
+ * LSM9DSx series is similar but includes an additional magnetometer, handled
+ * by a different driver.
*
* Supported sensors:
* - LSM6DS3:
@@ -17,18 +20,29 @@
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
* - FIFO size: 8KB
*
- * - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC:
+ * - LSM6DS3H/LSM6DSL/LSM6DSM/ISM330DLC/LSM6DS3TR-C:
* - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
* - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
* - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
* - FIFO size: 4KB
*
+ * - LSM6DSO/LSM6DSOX/ASM330LHH/LSM6DSR/ISM330DHCX:
+ * - Accelerometer/Gyroscope supported ODR [Hz]: 13, 26, 52, 104, 208, 416
+ * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
+ * - Gyroscope supported full-scale [dps]: +-125/+-245/+-500/+-1000/+-2000
+ * - FIFO size: 3KB
+ *
+ * - LSM9DS1:
+ * - Accelerometer supported ODR [Hz]: 10, 50, 119, 238, 476, 952
+ * - Accelerometer supported full-scale [g]: +-2/+-4/+-8/+-16
+ * - Gyroscope supported ODR [Hz]: 15, 60, 119, 238, 476, 952
+ * - Gyroscope supported full-scale [dps]: +-245/+-500/+-2000
+ * - FIFO size: 32
+ *
* Copyright 2016 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -44,121 +58,178 @@
#include "st_lsm6dsx.h"
-#define ST_LSM6DSX_REG_INT1_ADDR 0x0d
-#define ST_LSM6DSX_REG_INT2_ADDR 0x0e
#define ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK BIT(3)
#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
-#define ST_LSM6DSX_REG_INT2_ON_INT1_MASK BIT(5)
-#define ST_LSM6DSX_REG_ACC_ODR_ADDR 0x10
-#define ST_LSM6DSX_REG_ACC_ODR_MASK GENMASK(7, 4)
-#define ST_LSM6DSX_REG_ACC_FS_ADDR 0x10
-#define ST_LSM6DSX_REG_ACC_FS_MASK GENMASK(3, 2)
-#define ST_LSM6DSX_REG_ACC_OUT_X_L_ADDR 0x28
-#define ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR 0x2a
-#define ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR 0x2c
-
-#define ST_LSM6DSX_REG_GYRO_ODR_ADDR 0x11
-#define ST_LSM6DSX_REG_GYRO_ODR_MASK GENMASK(7, 4)
-#define ST_LSM6DSX_REG_GYRO_FS_ADDR 0x11
-#define ST_LSM6DSX_REG_GYRO_FS_MASK GENMASK(3, 2)
-#define ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR 0x22
-#define ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR 0x24
-#define ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR 0x26
-
-#define ST_LSM6DSX_ACC_FS_2G_GAIN IIO_G_TO_M_S_2(61)
-#define ST_LSM6DSX_ACC_FS_4G_GAIN IIO_G_TO_M_S_2(122)
-#define ST_LSM6DSX_ACC_FS_8G_GAIN IIO_G_TO_M_S_2(244)
-#define ST_LSM6DSX_ACC_FS_16G_GAIN IIO_G_TO_M_S_2(488)
-
-#define ST_LSM6DSX_GYRO_FS_245_GAIN IIO_DEGREE_TO_RAD(8750)
-#define ST_LSM6DSX_GYRO_FS_500_GAIN IIO_DEGREE_TO_RAD(17500)
-#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;
+static const struct iio_chan_spec st_lsm6dsx_acc_channels[] = {
+ ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x28, IIO_MOD_X, 0),
+ ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2a, IIO_MOD_Y, 1),
+ ST_LSM6DSX_CHANNEL(IIO_ACCEL, 0x2c, IIO_MOD_Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
};
-#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 iio_chan_spec st_lsm6dsx_gyro_channels[] = {
+ ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x22, IIO_MOD_X, 0),
+ ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x24, IIO_MOD_Y, 1),
+ ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x26, IIO_MOD_Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
};
-static const struct st_lsm6dsx_odr_table_entry st_lsm6dsx_odr_table[] = {
- [ST_LSM6DSX_ID_ACC] = {
- .reg = {
- .addr = ST_LSM6DSX_REG_ACC_ODR_ADDR,
- .mask = ST_LSM6DSX_REG_ACC_ODR_MASK,
- },
- .odr_avl[0] = { 13, 0x01 },
- .odr_avl[1] = { 26, 0x02 },
- .odr_avl[2] = { 52, 0x03 },
- .odr_avl[3] = { 104, 0x04 },
- .odr_avl[4] = { 208, 0x05 },
- .odr_avl[5] = { 416, 0x06 },
- },
- [ST_LSM6DSX_ID_GYRO] = {
- .reg = {
- .addr = ST_LSM6DSX_REG_GYRO_ODR_ADDR,
- .mask = ST_LSM6DSX_REG_GYRO_ODR_MASK,
- },
- .odr_avl[0] = { 13, 0x01 },
- .odr_avl[1] = { 26, 0x02 },
- .odr_avl[2] = { 52, 0x03 },
- .odr_avl[3] = { 104, 0x04 },
- .odr_avl[4] = { 208, 0x05 },
- .odr_avl[5] = { 416, 0x06 },
- }
-};
-
-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 = {
- .addr = ST_LSM6DSX_REG_ACC_FS_ADDR,
- .mask = ST_LSM6DSX_REG_ACC_FS_MASK,
- },
- .fs_avl[0] = { ST_LSM6DSX_ACC_FS_2G_GAIN, 0x0 },
- .fs_avl[1] = { ST_LSM6DSX_ACC_FS_4G_GAIN, 0x2 },
- .fs_avl[2] = { ST_LSM6DSX_ACC_FS_8G_GAIN, 0x3 },
- .fs_avl[3] = { ST_LSM6DSX_ACC_FS_16G_GAIN, 0x1 },
- },
- [ST_LSM6DSX_ID_GYRO] = {
- .reg = {
- .addr = ST_LSM6DSX_REG_GYRO_FS_ADDR,
- .mask = ST_LSM6DSX_REG_GYRO_FS_MASK,
- },
- .fs_avl[0] = { ST_LSM6DSX_GYRO_FS_245_GAIN, 0x0 },
- .fs_avl[1] = { ST_LSM6DSX_GYRO_FS_500_GAIN, 0x1 },
- .fs_avl[2] = { ST_LSM6DSX_GYRO_FS_1000_GAIN, 0x2 },
- .fs_avl[3] = { ST_LSM6DSX_GYRO_FS_2000_GAIN, 0x3 },
- }
+static const struct iio_chan_spec st_lsm6ds0_gyro_channels[] = {
+ ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x18, IIO_MOD_X, 0),
+ ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x1a, IIO_MOD_Y, 1),
+ ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, 0x1c, IIO_MOD_Z, 2),
+ IIO_CHAN_SOFT_TIMESTAMP(3),
};
static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = {
{
+ .wai = 0x68,
+ .int1_addr = 0x0c,
+ .int2_addr = 0x0d,
+ .reset_addr = 0x22,
+ .max_fifo_size = 32,
+ .id = {
+ {
+ .hw_id = ST_LSM9DS1_ID,
+ .name = ST_LSM9DS1_DEV_NAME,
+ },
+ },
+ .channels = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .chan = st_lsm6dsx_acc_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .chan = st_lsm6ds0_gyro_channels,
+ .len = ARRAY_SIZE(st_lsm6ds0_gyro_channels),
+ },
+ },
+ .odr_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x20,
+ .mask = GENMASK(7, 5),
+ },
+ .odr_avl[0] = { 10, 0x01 },
+ .odr_avl[1] = { 50, 0x02 },
+ .odr_avl[2] = { 119, 0x03 },
+ .odr_avl[3] = { 238, 0x04 },
+ .odr_avl[4] = { 476, 0x05 },
+ .odr_avl[5] = { 952, 0x06 },
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(7, 5),
+ },
+ .odr_avl[0] = { 15, 0x01 },
+ .odr_avl[1] = { 60, 0x02 },
+ .odr_avl[2] = { 119, 0x03 },
+ .odr_avl[3] = { 238, 0x04 },
+ .odr_avl[4] = { 476, 0x05 },
+ .odr_avl[5] = { 952, 0x06 },
+ },
+ },
+ .fs_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x20,
+ .mask = GENMASK(4, 3),
+ },
+ .fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
+ .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
+ .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
+ .fs_avl[3] = { IIO_G_TO_M_S_2(732), 0x1 },
+ .fs_len = 4,
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(4, 3),
+ },
+ .fs_avl[0] = { IIO_DEGREE_TO_RAD(245), 0x0 },
+ .fs_avl[1] = { IIO_DEGREE_TO_RAD(500), 0x1 },
+ .fs_avl[2] = { IIO_DEGREE_TO_RAD(2000), 0x3 },
+ .fs_len = 3,
+ },
+ },
+ },
+ {
.wai = 0x69,
+ .int1_addr = 0x0d,
+ .int2_addr = 0x0e,
+ .reset_addr = 0x12,
.max_fifo_size = 1365,
.id = {
- [0] = ST_LSM6DS3_ID,
+ {
+ .hw_id = ST_LSM6DS3_ID,
+ .name = ST_LSM6DS3_DEV_NAME,
+ },
+ },
+ .channels = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .chan = st_lsm6dsx_acc_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .chan = st_lsm6dsx_gyro_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
+ },
+ },
+ .odr_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ },
+ .fs_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
+ .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
+ .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
+ .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
+ .fs_len = 4,
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
+ .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
+ .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
+ .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
+ .fs_len = 4,
+ },
},
.decimator = {
[ST_LSM6DSX_ID_ACC] = {
@@ -171,6 +242,8 @@
},
},
.fifo_ops = {
+ .update_fifo = st_lsm6dsx_update_fifo,
+ .read_fifo = st_lsm6dsx_read_fifo,
.fifo_th = {
.addr = 0x06,
.mask = GENMASK(11, 0),
@@ -202,9 +275,75 @@
},
{
.wai = 0x69,
+ .int1_addr = 0x0d,
+ .int2_addr = 0x0e,
+ .reset_addr = 0x12,
.max_fifo_size = 682,
.id = {
- [0] = ST_LSM6DS3H_ID,
+ {
+ .hw_id = ST_LSM6DS3H_ID,
+ .name = ST_LSM6DS3H_DEV_NAME,
+ },
+ },
+ .channels = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .chan = st_lsm6dsx_acc_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .chan = st_lsm6dsx_gyro_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
+ },
+ },
+ .odr_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ },
+ .fs_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
+ .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
+ .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
+ .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
+ .fs_len = 4,
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
+ .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
+ .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
+ .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
+ .fs_len = 4,
+ },
},
.decimator = {
[ST_LSM6DSX_ID_ACC] = {
@@ -217,6 +356,8 @@
},
},
.fifo_ops = {
+ .update_fifo = st_lsm6dsx_update_fifo,
+ .read_fifo = st_lsm6dsx_read_fifo,
.fifo_th = {
.addr = 0x06,
.mask = GENMASK(11, 0),
@@ -248,11 +389,84 @@
},
{
.wai = 0x6a,
+ .int1_addr = 0x0d,
+ .int2_addr = 0x0e,
+ .reset_addr = 0x12,
.max_fifo_size = 682,
.id = {
- [0] = ST_LSM6DSL_ID,
- [1] = ST_LSM6DSM_ID,
- [2] = ST_ISM330DLC_ID,
+ {
+ .hw_id = ST_LSM6DSL_ID,
+ .name = ST_LSM6DSL_DEV_NAME,
+ }, {
+ .hw_id = ST_LSM6DSM_ID,
+ .name = ST_LSM6DSM_DEV_NAME,
+ }, {
+ .hw_id = ST_ISM330DLC_ID,
+ .name = ST_ISM330DLC_DEV_NAME,
+ }, {
+ .hw_id = ST_LSM6DS3TRC_ID,
+ .name = ST_LSM6DS3TRC_DEV_NAME,
+ },
+ },
+ .channels = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .chan = st_lsm6dsx_acc_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .chan = st_lsm6dsx_gyro_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
+ },
+ },
+ .odr_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ },
+ .fs_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
+ .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
+ .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
+ .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
+ .fs_len = 4,
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
+ .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
+ .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
+ .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
+ .fs_len = 4,
+ },
},
.decimator = {
[ST_LSM6DSX_ID_ACC] = {
@@ -265,6 +479,8 @@
},
},
.fifo_ops = {
+ .update_fifo = st_lsm6dsx_update_fifo,
+ .read_fifo = st_lsm6dsx_read_fifo,
.fifo_th = {
.addr = 0x06,
.mask = GENMASK(10, 0),
@@ -294,53 +510,407 @@
},
},
},
+ {
+ .wai = 0x6c,
+ .int1_addr = 0x0d,
+ .int2_addr = 0x0e,
+ .reset_addr = 0x12,
+ .max_fifo_size = 512,
+ .id = {
+ {
+ .hw_id = ST_LSM6DSO_ID,
+ .name = ST_LSM6DSO_DEV_NAME,
+ }, {
+ .hw_id = ST_LSM6DSOX_ID,
+ .name = ST_LSM6DSOX_DEV_NAME,
+ },
+ },
+ .channels = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .chan = st_lsm6dsx_acc_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .chan = st_lsm6dsx_gyro_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
+ },
+ },
+ .odr_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ },
+ .fs_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
+ .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
+ .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
+ .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
+ .fs_len = 4,
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
+ .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
+ .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
+ .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
+ .fs_len = 4,
+ },
+ },
+ .batch = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .addr = 0x09,
+ .mask = GENMASK(3, 0),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .addr = 0x09,
+ .mask = GENMASK(7, 4),
+ },
+ },
+ .fifo_ops = {
+ .update_fifo = st_lsm6dsx_update_fifo,
+ .read_fifo = st_lsm6dsx_read_tagged_fifo,
+ .fifo_th = {
+ .addr = 0x07,
+ .mask = GENMASK(8, 0),
+ },
+ .fifo_diff = {
+ .addr = 0x3a,
+ .mask = GENMASK(9, 0),
+ },
+ .th_wl = 1,
+ },
+ .ts_settings = {
+ .timer_en = {
+ .addr = 0x19,
+ .mask = BIT(5),
+ },
+ .decimator = {
+ .addr = 0x0a,
+ .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),
+ }
+ },
+ {
+ .wai = 0x6b,
+ .int1_addr = 0x0d,
+ .int2_addr = 0x0e,
+ .reset_addr = 0x12,
+ .max_fifo_size = 512,
+ .id = {
+ {
+ .hw_id = ST_ASM330LHH_ID,
+ .name = ST_ASM330LHH_DEV_NAME,
+ },
+ },
+ .channels = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .chan = st_lsm6dsx_acc_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .chan = st_lsm6dsx_gyro_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
+ },
+ },
+ .odr_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ },
+ .fs_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
+ .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
+ .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
+ .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
+ .fs_len = 4,
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
+ .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
+ .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
+ .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
+ .fs_len = 4,
+ },
+ },
+ .batch = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .addr = 0x09,
+ .mask = GENMASK(3, 0),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .addr = 0x09,
+ .mask = GENMASK(7, 4),
+ },
+ },
+ .fifo_ops = {
+ .update_fifo = st_lsm6dsx_update_fifo,
+ .read_fifo = st_lsm6dsx_read_tagged_fifo,
+ .fifo_th = {
+ .addr = 0x07,
+ .mask = GENMASK(8, 0),
+ },
+ .fifo_diff = {
+ .addr = 0x3a,
+ .mask = GENMASK(9, 0),
+ },
+ .th_wl = 1,
+ },
+ .ts_settings = {
+ .timer_en = {
+ .addr = 0x19,
+ .mask = BIT(5),
+ },
+ .decimator = {
+ .addr = 0x0a,
+ .mask = GENMASK(7, 6),
+ },
+ },
+ },
+ {
+ .wai = 0x6b,
+ .int1_addr = 0x0d,
+ .int2_addr = 0x0e,
+ .reset_addr = 0x12,
+ .max_fifo_size = 512,
+ .id = {
+ {
+ .hw_id = ST_LSM6DSR_ID,
+ .name = ST_LSM6DSR_DEV_NAME,
+ }, {
+ .hw_id = ST_ISM330DHCX_ID,
+ .name = ST_ISM330DHCX_DEV_NAME,
+ },
+ },
+ .channels = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .chan = st_lsm6dsx_acc_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_acc_channels),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .chan = st_lsm6dsx_gyro_channels,
+ .len = ARRAY_SIZE(st_lsm6dsx_gyro_channels),
+ },
+ },
+ .odr_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(7, 4),
+ },
+ .odr_avl[0] = { 13, 0x01 },
+ .odr_avl[1] = { 26, 0x02 },
+ .odr_avl[2] = { 52, 0x03 },
+ .odr_avl[3] = { 104, 0x04 },
+ .odr_avl[4] = { 208, 0x05 },
+ .odr_avl[5] = { 416, 0x06 },
+ },
+ },
+ .fs_table = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .reg = {
+ .addr = 0x10,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_G_TO_M_S_2(61), 0x0 },
+ .fs_avl[1] = { IIO_G_TO_M_S_2(122), 0x2 },
+ .fs_avl[2] = { IIO_G_TO_M_S_2(244), 0x3 },
+ .fs_avl[3] = { IIO_G_TO_M_S_2(488), 0x1 },
+ .fs_len = 4,
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .reg = {
+ .addr = 0x11,
+ .mask = GENMASK(3, 2),
+ },
+ .fs_avl[0] = { IIO_DEGREE_TO_RAD(8750), 0x0 },
+ .fs_avl[1] = { IIO_DEGREE_TO_RAD(17500), 0x1 },
+ .fs_avl[2] = { IIO_DEGREE_TO_RAD(35000), 0x2 },
+ .fs_avl[3] = { IIO_DEGREE_TO_RAD(70000), 0x3 },
+ .fs_len = 4,
+ },
+ },
+ .batch = {
+ [ST_LSM6DSX_ID_ACC] = {
+ .addr = 0x09,
+ .mask = GENMASK(3, 0),
+ },
+ [ST_LSM6DSX_ID_GYRO] = {
+ .addr = 0x09,
+ .mask = GENMASK(7, 4),
+ },
+ },
+ .fifo_ops = {
+ .update_fifo = st_lsm6dsx_update_fifo,
+ .read_fifo = st_lsm6dsx_read_tagged_fifo,
+ .fifo_th = {
+ .addr = 0x07,
+ .mask = GENMASK(8, 0),
+ },
+ .fifo_diff = {
+ .addr = 0x3a,
+ .mask = GENMASK(9, 0),
+ },
+ .th_wl = 1,
+ },
+ .ts_settings = {
+ .timer_en = {
+ .addr = 0x19,
+ .mask = BIT(5),
+ },
+ .decimator = {
+ .addr = 0x0a,
+ .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, \
- }, \
+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 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),
- ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Y_L_ADDR,
- IIO_MOD_Y, 1),
- ST_LSM6DSX_CHANNEL(IIO_ACCEL, ST_LSM6DSX_REG_ACC_OUT_Z_L_ADDR,
- IIO_MOD_Z, 2),
- IIO_CHAN_SOFT_TIMESTAMP(3),
-};
-
-static const struct iio_chan_spec st_lsm6dsx_gyro_channels[] = {
- ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_X_L_ADDR,
- IIO_MOD_X, 0),
- ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Y_L_ADDR,
- IIO_MOD_Y, 1),
- ST_LSM6DSX_CHANNEL(IIO_ANGL_VEL, ST_LSM6DSX_REG_GYRO_OUT_Z_L_ADDR,
- IIO_MOD_Z, 2),
- IIO_CHAN_SOFT_TIMESTAMP(3),
-};
-
-static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id)
+static int st_lsm6dsx_check_whoami(struct st_lsm6dsx_hw *hw, int id,
+ const char **name)
{
int err, i, j, data;
for (i = 0; i < ARRAY_SIZE(st_lsm6dsx_sensor_settings); i++) {
for (j = 0; j < ST_LSM6DSX_MAX_ID; j++) {
- if (id == st_lsm6dsx_sensor_settings[i].id[j])
+ if (id == st_lsm6dsx_sensor_settings[i].id[j].hw_id)
break;
}
if (j < ST_LSM6DSX_MAX_ID)
@@ -363,6 +933,7 @@
return -ENODEV;
}
+ *name = st_lsm6dsx_sensor_settings[i].id[j].name;
hw->settings = &st_lsm6dsx_sensor_settings[i];
return 0;
@@ -371,22 +942,23 @@
static int st_lsm6dsx_set_full_scale(struct st_lsm6dsx_sensor *sensor,
u32 gain)
{
- struct st_lsm6dsx_hw *hw = sensor->hw;
- const struct st_lsm6dsx_reg *reg;
+ const struct st_lsm6dsx_fs_table_entry *fs_table;
+ unsigned int data;
int i, err;
- u8 val;
- for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
- if (st_lsm6dsx_fs_table[sensor->id].fs_avl[i].gain == gain)
+ fs_table = &sensor->hw->settings->fs_table[sensor->id];
+ for (i = 0; i < fs_table->fs_len; i++) {
+ if (fs_table->fs_avl[i].gain == gain)
break;
+ }
- if (i == ST_LSM6DSX_FS_LIST_SIZE)
+ if (i == fs_table->fs_len)
return -EINVAL;
- 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(fs_table->fs_avl[i].val,
+ fs_table->reg.mask);
+ err = st_lsm6dsx_update_bits_locked(sensor->hw, fs_table->reg.addr,
+ fs_table->reg.mask, data);
if (err < 0)
return err;
@@ -395,65 +967,108 @@
return 0;
}
-static int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr,
- u8 *val)
+int st_lsm6dsx_check_odr(struct st_lsm6dsx_sensor *sensor, u16 odr, u8 *val)
{
+ const struct st_lsm6dsx_odr_table_entry *odr_table;
int i;
+ odr_table = &sensor->hw->settings->odr_table[sensor->id];
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 (odr_table->odr_avl[i].hz >= odr)
break;
if (i == ST_LSM6DSX_ODR_LIST_SIZE)
return -EINVAL;
- *val = st_lsm6dsx_odr_table[sensor->id].odr_avl[i].val;
+ *val = odr_table->odr_avl[i].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;
+ struct st_lsm6dsx_sensor *ref = iio_priv(hw->iio_devs[id]);
- 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));
+ 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)
{
- int err;
-
- err = st_lsm6dsx_set_odr(sensor, sensor->odr);
- if (err < 0)
- return err;
-
- sensor->hw->enable_mask |= BIT(sensor->id);
-
- return 0;
-}
-
-int st_lsm6dsx_sensor_disable(struct st_lsm6dsx_sensor *sensor)
-{
+ 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;
- 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));
+ 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;
+ }
+
+ if (req_odr > 0) {
+ err = st_lsm6dsx_check_odr(ref_sensor, req_odr, &val);
+ if (err < 0)
+ return err;
+ }
+
+ reg = &hw->settings->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_set_enable(struct st_lsm6dsx_sensor *sensor,
+ bool enable)
+{
+ struct st_lsm6dsx_hw *hw = sensor->hw;
+ u16 odr = enable ? sensor->odr : 0;
+ int err;
+
+ 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;
}
@@ -465,18 +1080,18 @@
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);
@@ -549,7 +1164,7 @@
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;
@@ -579,11 +1194,12 @@
{
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
enum st_lsm6dsx_sensor_id id = sensor->id;
+ struct st_lsm6dsx_hw *hw = sensor->hw;
int i, len = 0;
for (i = 0; i < ST_LSM6DSX_ODR_LIST_SIZE; i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
- st_lsm6dsx_odr_table[id].odr_avl[i].hz);
+ hw->settings->odr_table[id].odr_avl[i].hz);
buf[len - 1] = '\n';
return len;
@@ -594,12 +1210,14 @@
char *buf)
{
struct st_lsm6dsx_sensor *sensor = iio_priv(dev_get_drvdata(dev));
- enum st_lsm6dsx_sensor_id id = sensor->id;
+ const struct st_lsm6dsx_fs_table_entry *fs_table;
+ struct st_lsm6dsx_hw *hw = sensor->hw;
int i, len = 0;
- for (i = 0; i < ST_LSM6DSX_FS_LIST_SIZE; i++)
+ fs_table = &hw->settings->fs_table[sensor->id];
+ for (i = 0; i < fs_table->fs_len; i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
- st_lsm6dsx_fs_table[id].fs_avl[i].gain);
+ fs_table->fs_avl[i].gain);
buf[len - 1] = '\n';
return len;
@@ -645,8 +1263,6 @@
.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;
@@ -671,10 +1287,10 @@
switch (drdy_pin) {
case 1:
- *drdy_reg = ST_LSM6DSX_REG_INT1_ADDR;
+ *drdy_reg = hw->settings->int1_addr;
break;
case 2:
- *drdy_reg = ST_LSM6DSX_REG_INT2_ADDR;
+ *drdy_reg = hw->settings->int2_addr;
break;
default:
dev_err(hw->dev, "unsupported data ready pin\n");
@@ -685,6 +1301,51 @@
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;
@@ -728,12 +1389,23 @@
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, hw->settings->reset_addr,
+ ST_LSM6DSX_REG_RESET_MASK,
+ FIELD_PREP(ST_LSM6DSX_REG_RESET_MASK, 1));
if (err < 0)
return err;
- msleep(200);
+ msleep(50);
+
+ /* reload trimming parameter */
+ err = regmap_update_bits(hw->regmap, hw->settings->reset_addr,
+ ST_LSM6DSX_REG_BOOT_MASK,
+ FIELD_PREP(ST_LSM6DSX_REG_BOOT_MASK, 1));
+ if (err < 0)
+ return err;
+
+ msleep(50);
/* enable Block Data Update */
err = regmap_update_bits(hw->regmap, ST_LSM6DSX_REG_BDU_ADDR,
@@ -754,6 +1426,10 @@
if (err < 0)
return err;
+ err = st_lsm6dsx_init_shub(hw);
+ if (err < 0)
+ return err;
+
return st_lsm6dsx_init_hw_timer(hw);
}
@@ -771,28 +1447,24 @@
iio_dev->modes = INDIO_DIRECT_MODE;
iio_dev->dev.parent = hw->dev;
iio_dev->available_scan_masks = st_lsm6dsx_available_scan_masks;
+ iio_dev->channels = hw->settings->channels[id].chan;
+ iio_dev->num_channels = hw->settings->channels[id].len;
sensor = iio_priv(iio_dev);
sensor->id = id;
sensor->hw = hw;
- sensor->odr = st_lsm6dsx_odr_table[id].odr_avl[0].hz;
- sensor->gain = st_lsm6dsx_fs_table[id].fs_avl[0].gain;
+ sensor->odr = hw->settings->odr_table[id].odr_avl[0].hz;
+ sensor->gain = hw->settings->fs_table[id].fs_avl[0].gain;
sensor->watermark = 1;
switch (id) {
case ST_LSM6DSX_ID_ACC:
- iio_dev->channels = st_lsm6dsx_acc_channels;
- iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_acc_channels);
iio_dev->info = &st_lsm6dsx_acc_info;
-
scnprintf(sensor->name, sizeof(sensor->name), "%s_accel",
name);
break;
case ST_LSM6DSX_ID_GYRO:
- iio_dev->channels = st_lsm6dsx_gyro_channels;
- iio_dev->num_channels = ARRAY_SIZE(st_lsm6dsx_gyro_channels);
iio_dev->info = &st_lsm6dsx_gyro_info;
-
scnprintf(sensor->name, sizeof(sensor->name), "%s_gyro",
name);
break;
@@ -804,10 +1476,12 @@
return iio_dev;
}
-int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id, const char *name,
+int st_lsm6dsx_probe(struct device *dev, int irq, int hw_id,
struct regmap *regmap)
{
+ const struct st_lsm6dsx_shub_settings *hub_settings;
struct st_lsm6dsx_hw *hw;
+ const char *name = NULL;
int i, err;
hw = devm_kzalloc(dev, sizeof(*hw), GFP_KERNEL);
@@ -818,6 +1492,7 @@
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)
@@ -827,11 +1502,11 @@
hw->irq = irq;
hw->regmap = regmap;
- err = st_lsm6dsx_check_whoami(hw, hw_id);
+ err = st_lsm6dsx_check_whoami(hw, hw_id, &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;
@@ -841,6 +1516,13 @@
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)
@@ -848,6 +1530,9 @@
}
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;
@@ -861,19 +1546,26 @@
{
struct st_lsm6dsx_hw *hw = dev_get_drvdata(dev);
struct st_lsm6dsx_sensor *sensor;
- const struct st_lsm6dsx_reg *reg;
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));
+ 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, false);
+ else
+ err = st_lsm6dsx_sensor_set_enable(sensor, false);
if (err < 0)
return err;
+
+ hw->suspend_mask |= BIT(sensor->id);
}
if (hw->fifo_mode != ST_LSM6DSX_FIFO_BYPASS)
@@ -889,13 +1581,23 @@
int i, err = 0;
for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) {
- sensor = iio_priv(hw->iio_devs[i]);
- if (!(hw->enable_mask & BIT(sensor->id)))
+ if (!hw->iio_devs[i])
continue;
- err = st_lsm6dsx_set_odr(sensor, sensor->odr);
+ sensor = iio_priv(hw->iio_devs[i]);
+ if (!(hw->suspend_mask & BIT(sensor->id)))
+ continue;
+
+ 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, true);
+ else
+ err = st_lsm6dsx_sensor_set_enable(sensor, true);
if (err < 0)
return err;
+
+ hw->suspend_mask &= ~BIT(sensor->id);
}
if (hw->enable_mask)
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
index 377c4e9..f525110 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics st_lsm6dsx i2c driver
*
@@ -5,8 +6,6 @@
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -36,8 +35,7 @@
return PTR_ERR(regmap);
}
- return st_lsm6dsx_probe(&client->dev, client->irq,
- hw_id, id->name, regmap);
+ return st_lsm6dsx_probe(&client->dev, client->irq, hw_id, regmap);
}
static const struct of_device_id st_lsm6dsx_i2c_of_match[] = {
@@ -61,6 +59,34 @@
.compatible = "st,ism330dlc",
.data = (void *)ST_ISM330DLC_ID,
},
+ {
+ .compatible = "st,lsm6dso",
+ .data = (void *)ST_LSM6DSO_ID,
+ },
+ {
+ .compatible = "st,asm330lhh",
+ .data = (void *)ST_ASM330LHH_ID,
+ },
+ {
+ .compatible = "st,lsm6dsox",
+ .data = (void *)ST_LSM6DSOX_ID,
+ },
+ {
+ .compatible = "st,lsm6dsr",
+ .data = (void *)ST_LSM6DSR_ID,
+ },
+ {
+ .compatible = "st,lsm6ds3tr-c",
+ .data = (void *)ST_LSM6DS3TRC_ID,
+ },
+ {
+ .compatible = "st,ism330dhcx",
+ .data = (void *)ST_ISM330DHCX_ID,
+ },
+ {
+ .compatible = "st,lsm9ds1-imu",
+ .data = (void *)ST_LSM9DS1_ID,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_lsm6dsx_i2c_of_match);
@@ -71,6 +97,13 @@
{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
{ ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID },
+ { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID },
+ { ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID },
+ { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID },
+ { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID },
+ { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID },
+ { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID },
+ { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID },
{},
};
MODULE_DEVICE_TABLE(i2c, st_lsm6dsx_i2c_id_table);
diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
new file mode 100644
index 0000000..57e6331
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_i3c.c
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2018 Synopsys, Inc. and/or its affiliates.
+ *
+ * Author: Vitor Soares <vitor.soares@synopsys.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i3c/device.h>
+#include <linux/i3c/master.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+
+#include "st_lsm6dsx.h"
+
+static const struct i3c_device_id st_lsm6dsx_i3c_ids[] = {
+ I3C_DEVICE(0x0104, 0x006C, (void *)ST_LSM6DSO_ID),
+ I3C_DEVICE(0x0104, 0x006B, (void *)ST_LSM6DSR_ID),
+ { /* sentinel */ },
+};
+MODULE_DEVICE_TABLE(i3c, st_lsm6dsx_i3c_ids);
+
+static int st_lsm6dsx_i3c_probe(struct i3c_device *i3cdev)
+{
+ struct regmap_config st_lsm6dsx_i3c_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ };
+ const struct i3c_device_id *id = i3c_device_match_id(i3cdev,
+ st_lsm6dsx_i3c_ids);
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i3c(i3cdev, &st_lsm6dsx_i3c_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&i3cdev->dev, "Failed to register i3c regmap %d\n",
+ (int)PTR_ERR(regmap));
+ return PTR_ERR(regmap);
+ }
+
+ return st_lsm6dsx_probe(&i3cdev->dev, 0, (uintptr_t)id->data, regmap);
+}
+
+static struct i3c_driver st_lsm6dsx_driver = {
+ .driver = {
+ .name = "st_lsm6dsx_i3c",
+ .pm = &st_lsm6dsx_pm_ops,
+ },
+ .probe = st_lsm6dsx_i3c_probe,
+ .id_table = st_lsm6dsx_i3c_ids,
+};
+module_i3c_driver(st_lsm6dsx_driver);
+
+MODULE_AUTHOR("Vitor Soares <vitor.soares@synopsys.com>");
+MODULE_DESCRIPTION("STMicroelectronics st_lsm6dsx i3c driver");
+MODULE_LICENSE("GPL v2");
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 0000000..ea472cf
--- /dev/null
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_shub.c
@@ -0,0 +1,776 @@
+/*
+ * 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 */
+ .fs_len = 1,
+ },
+ .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;
+ u16 odr;
+
+ sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]);
+ odr = (hw->enable_mask & BIT(ST_LSM6DSX_ID_ACC)) ? sensor->odr : 13;
+ msleep((2000U / 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)
+{
+ int err;
+
+ mutex_lock(&hw->page_lock);
+
+ 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 < settings->fs_table.fs_len; i++)
+ len += scnprintf(buf + len, PAGE_SIZE - len, "0.%06u ",
+ settings->fs_table.fs_avl[i].gain);
+ 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/imu/st_lsm6dsx/st_lsm6dsx_spi.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
index fec5c6c..344b28d 100644
--- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
+++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics st_lsm6dsx spi driver
*
@@ -5,8 +6,6 @@
*
* Lorenzo Bianconi <lorenzo.bianconi@st.com>
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -36,8 +35,7 @@
return PTR_ERR(regmap);
}
- return st_lsm6dsx_probe(&spi->dev, spi->irq,
- hw_id, id->name, regmap);
+ return st_lsm6dsx_probe(&spi->dev, spi->irq, hw_id, regmap);
}
static const struct of_device_id st_lsm6dsx_spi_of_match[] = {
@@ -61,6 +59,34 @@
.compatible = "st,ism330dlc",
.data = (void *)ST_ISM330DLC_ID,
},
+ {
+ .compatible = "st,lsm6dso",
+ .data = (void *)ST_LSM6DSO_ID,
+ },
+ {
+ .compatible = "st,asm330lhh",
+ .data = (void *)ST_ASM330LHH_ID,
+ },
+ {
+ .compatible = "st,lsm6dsox",
+ .data = (void *)ST_LSM6DSOX_ID,
+ },
+ {
+ .compatible = "st,lsm6dsr",
+ .data = (void *)ST_LSM6DSR_ID,
+ },
+ {
+ .compatible = "st,lsm6ds3tr-c",
+ .data = (void *)ST_LSM6DS3TRC_ID,
+ },
+ {
+ .compatible = "st,ism330dhcx",
+ .data = (void *)ST_ISM330DHCX_ID,
+ },
+ {
+ .compatible = "st,lsm9ds1-imu",
+ .data = (void *)ST_LSM9DS1_ID,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_lsm6dsx_spi_of_match);
@@ -71,6 +97,13 @@
{ ST_LSM6DSL_DEV_NAME, ST_LSM6DSL_ID },
{ ST_LSM6DSM_DEV_NAME, ST_LSM6DSM_ID },
{ ST_ISM330DLC_DEV_NAME, ST_ISM330DLC_ID },
+ { ST_LSM6DSO_DEV_NAME, ST_LSM6DSO_ID },
+ { ST_ASM330LHH_DEV_NAME, ST_ASM330LHH_ID },
+ { ST_LSM6DSOX_DEV_NAME, ST_LSM6DSOX_ID },
+ { ST_LSM6DSR_DEV_NAME, ST_LSM6DSR_ID },
+ { ST_LSM6DS3TRC_DEV_NAME, ST_LSM6DS3TRC_ID },
+ { ST_ISM330DHCX_DEV_NAME, ST_ISM330DHCX_ID },
+ { ST_LSM9DS1_DEV_NAME, ST_LSM9DS1_ID },
{},
};
MODULE_DEVICE_TABLE(spi, st_lsm6dsx_spi_id_table);
diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c
index cd5bfe3..c193d64 100644
--- a/drivers/iio/industrialio-buffer.c
+++ b/drivers/iio/industrialio-buffer.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* The industrial I/O core
*
* Copyright (c) 2008 Jonathan Cameron
*
- * 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.
- *
* Handling of buffer allocation / resizing.
*
- *
* Things to look at here.
* - Better memory allocation techniques?
* - Alternative access techniques?
@@ -320,9 +316,8 @@
const unsigned long *mask;
unsigned long *trialmask;
- trialmask = kmalloc_array(BITS_TO_LONGS(indio_dev->masklength),
- sizeof(*trialmask),
- GFP_KERNEL);
+ trialmask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
+ sizeof(*trialmask), GFP_KERNEL);
if (trialmask == NULL)
return -ENOMEM;
if (!indio_dev->masklength) {
@@ -344,12 +339,12 @@
}
bitmap_copy(buffer->scan_mask, trialmask, indio_dev->masklength);
- kfree(trialmask);
+ bitmap_free(trialmask);
return 0;
err_invalid_mask:
- kfree(trialmask);
+ bitmap_free(trialmask);
return -EINVAL;
}
@@ -666,7 +661,7 @@
{
/* If the mask is dynamically allocated free it, otherwise do nothing */
if (!indio_dev->available_scan_masks)
- kfree(mask);
+ bitmap_free(mask);
}
struct iio_device_config {
@@ -736,8 +731,7 @@
}
/* What scan mask do we actually have? */
- compound_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
- sizeof(long), GFP_KERNEL);
+ compound_mask = bitmap_zalloc(indio_dev->masklength, GFP_KERNEL);
if (compound_mask == NULL)
return -ENOMEM;
@@ -762,7 +756,7 @@
indio_dev->masklength,
compound_mask,
strict_scanmask);
- kfree(compound_mask);
+ bitmap_free(compound_mask);
if (scan_mask == NULL)
return -EINVAL;
} else {
@@ -1303,9 +1297,8 @@
channels[i].scan_index;
}
if (indio_dev->masklength && buffer->scan_mask == NULL) {
- buffer->scan_mask = kcalloc(BITS_TO_LONGS(indio_dev->masklength),
- sizeof(*buffer->scan_mask),
- GFP_KERNEL);
+ buffer->scan_mask = bitmap_zalloc(indio_dev->masklength,
+ GFP_KERNEL);
if (buffer->scan_mask == NULL) {
ret = -ENOMEM;
goto error_cleanup_dynamic;
@@ -1334,7 +1327,7 @@
return 0;
error_free_scan_mask:
- kfree(buffer->scan_mask);
+ bitmap_free(buffer->scan_mask);
error_cleanup_dynamic:
iio_free_chan_devattr_list(&buffer->scan_el_dev_attr_list);
kfree(indio_dev->buffer->buffer_group.attrs);
@@ -1347,7 +1340,7 @@
if (!indio_dev->buffer)
return;
- kfree(indio_dev->buffer->scan_mask);
+ bitmap_free(indio_dev->buffer->scan_mask);
kfree(indio_dev->buffer->buffer_group.attrs);
kfree(indio_dev->buffer->scan_el_group.attrs);
iio_free_chan_devattr_list(&indio_dev->buffer->scan_el_dev_attr_list);
diff --git a/drivers/iio/industrialio-configfs.c b/drivers/iio/industrialio-configfs.c
index 5a0aae1..47900de 100644
--- a/drivers/iio/industrialio-configfs.c
+++ b/drivers/iio/industrialio-configfs.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Industrial I/O configfs bits
*
* Copyright (c) 2015 Intel Corporation
- *
- * 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/configfs.h>
diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c
index a062cfd..524a686 100644
--- a/drivers/iio/industrialio-core.c
+++ b/drivers/iio/industrialio-core.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* The industrial I/O core
*
* Copyright (c) 2008 Jonathan Cameron
*
- * 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.
- *
* Based on elements of hwmon and input subsystems.
*/
@@ -19,6 +16,7 @@
#include <linux/device.h>
#include <linux/fs.h>
#include <linux/poll.h>
+#include <linux/property.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/cdev.h>
@@ -87,6 +85,7 @@
[IIO_GRAVITY] = "gravity",
[IIO_POSITIONRELATIVE] = "positionrelative",
[IIO_PHASE] = "phase",
+ [IIO_MASSCONCENTRATION] = "massconcentration",
};
static const char * const iio_modifier_names[] = {
@@ -127,6 +126,10 @@
[IIO_MOD_Q] = "q",
[IIO_MOD_CO2] = "co2",
[IIO_MOD_VOC] = "voc",
+ [IIO_MOD_PM1] = "pm1",
+ [IIO_MOD_PM2P5] = "pm2p5",
+ [IIO_MOD_PM4] = "pm4",
+ [IIO_MOD_PM10] = "pm10",
};
/* relies on pairs of these shared then separate */
@@ -225,9 +228,9 @@
ktime_get_coarse_ts64(&tp);
return timespec64_to_ns(&tp);
case CLOCK_BOOTTIME:
- return ktime_get_boot_ns();
+ return ktime_get_boottime_ns();
case CLOCK_TAI:
- return ktime_get_tai_ns();
+ return ktime_get_clocktai_ns();
default:
BUG();
}
@@ -363,39 +366,25 @@
debugfs_remove_recursive(indio_dev->debugfs_dentry);
}
-static int iio_device_register_debugfs(struct iio_dev *indio_dev)
+static void iio_device_register_debugfs(struct iio_dev *indio_dev)
{
- struct dentry *d;
-
if (indio_dev->info->debugfs_reg_access == NULL)
- return 0;
+ return;
if (!iio_debugfs_dentry)
- return 0;
+ return;
indio_dev->debugfs_dentry =
debugfs_create_dir(dev_name(&indio_dev->dev),
iio_debugfs_dentry);
- if (indio_dev->debugfs_dentry == NULL) {
- dev_warn(indio_dev->dev.parent,
- "Failed to create debugfs directory\n");
- return -EFAULT;
- }
- d = debugfs_create_file("direct_reg_access", 0644,
- indio_dev->debugfs_dentry,
- indio_dev, &iio_debugfs_reg_fops);
- if (!d) {
- iio_device_unregister_debugfs(indio_dev);
- return -ENOMEM;
- }
-
- return 0;
+ debugfs_create_file("direct_reg_access", 0644,
+ indio_dev->debugfs_dentry, indio_dev,
+ &iio_debugfs_reg_fops);
}
#else
-static int iio_device_register_debugfs(struct iio_dev *indio_dev)
+static void iio_device_register_debugfs(struct iio_dev *indio_dev)
{
- return 0;
}
static void iio_device_unregister_debugfs(struct iio_dev *indio_dev)
@@ -525,8 +514,8 @@
EXPORT_SYMBOL_GPL(iio_show_mount_matrix);
/**
- * of_iio_read_mount_matrix() - retrieve iio device mounting matrix from
- * device-tree "mount-matrix" property
+ * iio_read_mount_matrix() - retrieve iio device mounting matrix from
+ * device "mount-matrix" property
* @dev: device the mounting matrix property is assigned to
* @propname: device specific mounting matrix property name
* @matrix: where to store retrieved matrix
@@ -536,40 +525,29 @@
*
* Return: 0 if success, or a negative error code on failure.
*/
-#ifdef CONFIG_OF
-int of_iio_read_mount_matrix(const struct device *dev,
- const char *propname,
- struct iio_mount_matrix *matrix)
+int iio_read_mount_matrix(struct device *dev, const char *propname,
+ struct iio_mount_matrix *matrix)
{
- if (dev->of_node) {
- int err = of_property_read_string_array(dev->of_node,
- propname, matrix->rotation,
- ARRAY_SIZE(iio_mount_idmatrix.rotation));
+ size_t len = ARRAY_SIZE(iio_mount_idmatrix.rotation);
+ int err;
- if (err == ARRAY_SIZE(iio_mount_idmatrix.rotation))
- return 0;
+ err = device_property_read_string_array(dev, propname,
+ matrix->rotation, len);
+ if (err == len)
+ return 0;
- if (err >= 0)
- /* Invalid number of matrix entries. */
- return -EINVAL;
+ if (err >= 0)
+ /* Invalid number of matrix entries. */
+ return -EINVAL;
- if (err != -EINVAL)
- /* Invalid matrix declaration format. */
- return err;
- }
+ if (err != -EINVAL)
+ /* Invalid matrix declaration format. */
+ return err;
/* Matrix was not declared at all: fallback to identity. */
return iio_setup_mount_idmatrix(dev, matrix);
}
-#else
-int of_iio_read_mount_matrix(const struct device *dev,
- const char *propname,
- struct iio_mount_matrix *matrix)
-{
- return iio_setup_mount_idmatrix(dev, matrix);
-}
-#endif
-EXPORT_SYMBOL(of_iio_read_mount_matrix);
+EXPORT_SYMBOL(iio_read_mount_matrix);
static ssize_t __iio_format_value(char *buf, size_t len, unsigned int type,
int size, const int *vals)
@@ -1112,6 +1090,8 @@
char *avail_postfix;
for_each_set_bit(i, infomask, sizeof(*infomask) * 8) {
+ if (i >= ARRAY_SIZE(iio_chan_info_postfix))
+ return -EINVAL;
avail_postfix = kasprintf(GFP_KERNEL,
"%s_available",
iio_chan_info_postfix[i]);
@@ -1671,15 +1651,13 @@
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);
- ret = iio_device_register_debugfs(indio_dev);
- if (ret) {
- dev_err(indio_dev->dev.parent,
- "Failed to register debugfs interfaces\n");
- return ret;
- }
+ iio_device_register_debugfs(indio_dev);
ret = iio_buffer_alloc_sysfs_and_mask(indio_dev);
if (ret) {
@@ -1735,10 +1713,10 @@
**/
void iio_device_unregister(struct iio_dev *indio_dev)
{
- mutex_lock(&indio_dev->info_exist_lock);
-
cdev_device_del(&indio_dev->chrdev, &indio_dev->dev);
+ mutex_lock(&indio_dev->info_exist_lock);
+
iio_device_unregister_debugfs(indio_dev);
iio_disable_all_buffers(indio_dev);
diff --git a/drivers/iio/industrialio-event.c b/drivers/iio/industrialio-event.c
index c6dfdf0..5b17c92 100644
--- a/drivers/iio/industrialio-event.c
+++ b/drivers/iio/industrialio-event.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* Industrial I/O event handling
*
* Copyright (c) 2008 Jonathan Cameron
*
- * 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.
- *
* Based on elements of hwmon and input subsystems.
*/
diff --git a/drivers/iio/industrialio-sw-device.c b/drivers/iio/industrialio-sw-device.c
index 90df97c..49f775f 100644
--- a/drivers/iio/industrialio-sw-device.c
+++ b/drivers/iio/industrialio-sw-device.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* The Industrial I/O core, software IIO devices functions
*
* Copyright (c) 2016 Intel Corporation
- *
- * 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/module.h>
diff --git a/drivers/iio/industrialio-sw-trigger.c b/drivers/iio/industrialio-sw-trigger.c
index bc6b7fb..9ae793a 100644
--- a/drivers/iio/industrialio-sw-trigger.c
+++ b/drivers/iio/industrialio-sw-trigger.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* The Industrial I/O core, software trigger functions
*
* Copyright (c) 2015 Intel Corporation
- *
- * 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/module.h>
diff --git a/drivers/iio/industrialio-trigger.c b/drivers/iio/industrialio-trigger.c
index ce66699..3908a9a 100644
--- a/drivers/iio/industrialio-trigger.c
+++ b/drivers/iio/industrialio-trigger.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* The industrial I/O core, trigger handling functions
*
* Copyright (c) 2008 Jonathan Cameron
- *
- * 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/kernel.h>
@@ -254,8 +251,11 @@
/* Get irq number */
pf->irq = iio_trigger_get_irq(trig);
- if (pf->irq < 0)
+ if (pf->irq < 0) {
+ pr_err("Could not find an available irq for trigger %s, CONFIG_IIO_CONSUMERS_PER_TRIGGER=%d limit might be exceeded\n",
+ trig->name, CONFIG_IIO_CONSUMERS_PER_TRIGGER);
goto out_put_module;
+ }
/* Request irq */
ret = request_threaded_irq(pf->irq, pf->h, pf->thread,
diff --git a/drivers/iio/industrialio-triggered-event.c b/drivers/iio/industrialio-triggered-event.c
index 8cc254f..53da9ab 100644
--- a/drivers/iio/industrialio-triggered-event.c
+++ b/drivers/iio/industrialio-triggered-event.c
@@ -1,10 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2015 Cogent Embedded, Inc.
- *
- * 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.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c
index 06ca3f7..5a8351c 100644
--- a/drivers/iio/inkern.c
+++ b/drivers/iio/inkern.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/* The industrial I/O core in kernel channel mapping
*
* Copyright (c) 2011 Jonathan Cameron
- *
- * 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/err.h>
#include <linux/export.h>
@@ -93,7 +90,7 @@
#ifdef CONFIG_OF
-static int iio_dev_node_match(struct device *dev, void *data)
+static int iio_dev_node_match(struct device *dev, const void *data)
{
return dev->of_node == data && dev->type == &iio_device_type;
}
@@ -733,11 +730,11 @@
vals, type, length, info);
}
-int iio_read_avail_channel_raw(struct iio_channel *chan,
- const int **vals, int *length)
+int iio_read_avail_channel_attribute(struct iio_channel *chan,
+ const int **vals, int *type, int *length,
+ enum iio_chan_info_enum attribute)
{
int ret;
- int type;
mutex_lock(&chan->indio_dev->info_exist_lock);
if (!chan->indio_dev->info) {
@@ -745,11 +742,23 @@
goto err_unlock;
}
- ret = iio_channel_read_avail(chan,
- vals, &type, length, IIO_CHAN_INFO_RAW);
+ ret = iio_channel_read_avail(chan, vals, type, length, attribute);
err_unlock:
mutex_unlock(&chan->indio_dev->info_exist_lock);
+ return ret;
+}
+EXPORT_SYMBOL_GPL(iio_read_avail_channel_attribute);
+
+int iio_read_avail_channel_raw(struct iio_channel *chan,
+ const int **vals, int *length)
+{
+ int ret;
+ int type;
+
+ ret = iio_read_avail_channel_attribute(chan, vals, &type, length,
+ IIO_CHAN_INFO_RAW);
+
if (ret >= 0 && type != IIO_VAL_INT)
/* raw values are assumed to be IIO_VAL_INT */
ret = -EINVAL;
diff --git a/drivers/iio/light/Kconfig b/drivers/iio/light/Kconfig
index d66ea75..4a1a883 100644
--- a/drivers/iio/light/Kconfig
+++ b/drivers/iio/light/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Light sensors
@@ -13,11 +14,11 @@
select IIO_TRIGGERED_BUFFER
select IIO_KFIFO_BUF
help
- Say Y here if you want to build a driver for the ACPI0008
- Ambient Light Sensor.
+ Say Y here if you want to build a driver for the ACPI0008
+ Ambient Light Sensor.
- To compile this driver as a module, choose M here: the module will
- be called acpi-als.
+ To compile this driver as a module, choose M here: the module will
+ be called acpi-als.
config ADJD_S311
tristate "ADJD-S311-CR999 digital color sensor"
@@ -25,31 +26,31 @@
select IIO_TRIGGERED_BUFFER
depends on I2C
help
- If you say yes here you get support for the Avago ADJD-S311-CR999
- digital color light sensor.
+ If you say yes here you get support for the Avago ADJD-S311-CR999
+ digital color light sensor.
- This driver can also be built as a module. If so, the module
- will be called adjd_s311.
+ This driver can also be built as a module. If so, the module
+ will be called adjd_s311.
config AL3320A
tristate "AL3320A ambient light sensor"
depends on I2C
help
- Say Y here if you want to build a driver for the Dyna Image AL3320A
- ambient light sensor.
+ Say Y here if you want to build a driver for the Dyna Image AL3320A
+ ambient light sensor.
- To compile this driver as a module, choose M here: the
- module will be called al3320a.
+ To compile this driver as a module, choose M here: the
+ module will be called al3320a.
config APDS9300
tristate "APDS9300 ambient light sensor"
depends on I2C
help
- Say Y here if you want to build a driver for the Avago APDS9300
- ambient light sensor.
+ Say Y here if you want to build a driver for the Avago APDS9300
+ ambient light sensor.
- To compile this driver as a module, choose M here: the
- module will be called apds9300.
+ To compile this driver as a module, choose M here: the
+ module will be called apds9300.
config APDS9960
tristate "Avago APDS9960 gesture/RGB/ALS/proximity sensor"
@@ -68,74 +69,74 @@
tristate "ROHM BH1750 ambient light sensor"
depends on I2C
help
- Say Y here to build support for the ROHM BH1710, BH1715, BH1721,
- BH1750, BH1751 ambient light sensors.
+ Say Y here to build support for the ROHM BH1710, BH1715, BH1721,
+ BH1750, BH1751 ambient light sensors.
- To compile this driver as a module, choose M here: the module will
- be called bh1750.
+ To compile this driver as a module, choose M here: the module will
+ be called bh1750.
config BH1780
tristate "ROHM BH1780 ambient light sensor"
depends on I2C
help
- Say Y here to build support for the ROHM BH1780GLI ambient
- light sensor.
+ Say Y here to build support for the ROHM BH1780GLI ambient
+ light sensor.
- To compile this driver as a module, choose M here: the module will
- be called bh1780.
+ To compile this driver as a module, choose M here: the module will
+ be called bh1780.
config CM32181
depends on I2C
tristate "CM32181 driver"
help
- Say Y here if you use cm32181.
- This option enables ambient light sensor using
- Capella cm32181 device driver.
+ Say Y here if you use cm32181.
+ This option enables ambient light sensor using
+ Capella cm32181 device driver.
- To compile this driver as a module, choose M here:
- the module will be called cm32181.
+ To compile this driver as a module, choose M here:
+ the module will be called cm32181.
config CM3232
depends on I2C
tristate "CM3232 ambient light sensor"
help
- Say Y here if you use cm3232.
- This option enables ambient light sensor using
- Capella Microsystems cm3232 device driver.
+ Say Y here if you use cm3232.
+ This option enables ambient light sensor using
+ Capella Microsystems cm3232 device driver.
- To compile this driver as a module, choose M here:
- the module will be called cm3232.
+ To compile this driver as a module, choose M here:
+ the module will be called cm3232.
config CM3323
depends on I2C
tristate "Capella CM3323 color light sensor"
help
- Say Y here if you want to build a driver for Capella CM3323
- color sensor.
+ Say Y here if you want to build a driver for Capella CM3323
+ color sensor.
- To compile this driver as a module, choose M here: the module will
- be called cm3323.
+ To compile this driver as a module, choose M here: the module will
+ be called cm3323.
config CM3605
tristate "Capella CM3605 ambient light and proximity sensor"
depends on OF
help
- Say Y here if you want to build a driver for Capella CM3605
- ambient light and short range proximity sensor.
+ Say Y here if you want to build a driver for Capella CM3605
+ ambient light and short range proximity sensor.
- To compile this driver as a module, choose M here: the module will
- be called cm3605.
+ To compile this driver as a module, choose M here: the module will
+ be called cm3605.
config CM36651
depends on I2C
tristate "CM36651 driver"
help
- Say Y here if you use cm36651.
- This option enables proximity & RGB sensor using
- Capella cm36651 device driver.
+ Say Y here if you use cm36651.
+ This option enables proximity & RGB sensor using
+ Capella cm36651 device driver.
- To compile this driver as a module, choose M here:
- the module will be called cm36651.
+ To compile this driver as a module, choose M here:
+ the module will be called cm36651.
config IIO_CROS_EC_LIGHT_PROX
tristate "ChromeOS EC Light and Proximity Sensors"
@@ -167,21 +168,21 @@
select REGMAP_I2C
default n
help
- If you say yes here you get support for ambient light sensing and
- proximity infrared sensing from Intersil ISL29018.
- This driver will provide the measurements of ambient light intensity
- in lux, proximity infrared sensing and normal infrared sensing.
- Data from sensor is accessible via sysfs.
+ If you say yes here you get support for ambient light sensing and
+ proximity infrared sensing from Intersil ISL29018.
+ This driver will provide the measurements of ambient light intensity
+ in lux, proximity infrared sensing and normal infrared sensing.
+ Data from sensor is accessible via sysfs.
config SENSORS_ISL29028
tristate "Intersil ISL29028 Concurrent Light and Proximity Sensor"
depends on I2C
select REGMAP_I2C
help
- Provides driver for the Intersil's ISL29028 device.
- This driver supports the sysfs interface to get the ALS, IR intensity,
- Proximity value via iio. The ISL29028 provides the concurrent sensing
- of ambient light and proximity.
+ Provides driver for the Intersil's ISL29028 device.
+ This driver supports the sysfs interface to get the ALS, IR intensity,
+ Proximity value via iio. The ISL29028 provides the concurrent sensing
+ of ambient light and proximity.
config ISL29125
tristate "Intersil ISL29125 digital color light sensor"
@@ -228,22 +229,22 @@
depends on I2C
select REGMAP_I2C
help
- Say Y here if you want to build a IIO driver for JSA1212
- proximity & ALS sensor device.
+ Say Y here if you want to build a IIO driver for JSA1212
+ proximity & ALS sensor device.
- To compile this driver as a module, choose M here:
- the module will be called jsa1212.
+ To compile this driver as a module, choose M here:
+ the module will be called jsa1212.
config RPR0521
tristate "ROHM RPR0521 ALS and proximity sensor driver"
depends on I2C
select REGMAP_I2C
help
- Say Y here if you want to build support for ROHM's RPR0521
- ambient light and proximity sensor device.
+ Say Y here if you want to build support for ROHM's RPR0521
+ ambient light and proximity sensor device.
- To compile this driver as a module, choose M here:
- the module will be called rpr0521.
+ To compile this driver as a module, choose M here:
+ the module will be called rpr0521.
config SENSORS_LM3533
tristate "LM3533 ambient light sensor"
@@ -269,22 +270,22 @@
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
- If you say yes here you get support for the Lite-On LTR-501ALS-01
- ambient light and proximity sensor. This driver also supports LTR-559
- ALS/PS or LTR-301 ALS sensors.
+ If you say yes here you get support for the Lite-On LTR-501ALS-01
+ ambient light and proximity sensor. This driver also supports LTR-559
+ ALS/PS or LTR-301 ALS sensors.
- This driver can also be built as a module. If so, the module
- will be called ltr501.
+ This driver can also be built as a module. If so, the module
+ will be called ltr501.
config LV0104CS
tristate "LV0104CS Ambient Light Sensor"
depends on I2C
help
- Say Y here if you want to build support for the On Semiconductor
- LV0104CS ambient light sensor.
+ Say Y here if you want to build support for the On Semiconductor
+ LV0104CS ambient light sensor.
- To compile this driver as a module, choose M here:
- the module will be called lv0104cs.
+ To compile this driver as a module, choose M here:
+ the module will be called lv0104cs.
config MAX44000
tristate "MAX44000 Ambient and Infrared Proximity Sensor"
@@ -293,11 +294,33 @@
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
+ Say Y here if you want to build support for Maxim Integrated's
+ MAX44000 ambient and infrared proximity sensor device.
+
+ To compile this driver as a module, choose M here:
+ the module will be called max44000.
+
+config MAX44009
+ tristate "MAX44009 Ambient Light Sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
Say Y here if you want to build support for Maxim Integrated's
- MAX44000 ambient and infrared proximity sensor device.
+ MAX44009 ambient light sensor device.
To compile this driver as a module, choose M here:
- the module will be called max44000.
+ the module will be called max44009.
+
+config NOA1305
+ tristate "ON Semiconductor NOA1305 ambient light sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say Y here if you want to build support for the ON Semiconductor
+ NOA1305 ambient light sensor.
+
+ To compile this driver as a module, choose M here:
+ The module will be called noa1305.
config OPT3001
tristate "Texas Instruments OPT3001 Light Sensor"
@@ -310,15 +333,15 @@
opt3001.
config PA12203001
- tristate "TXC PA12203001 light and proximity sensor"
- depends on I2C
- select REGMAP_I2C
- help
- If you say yes here you get support for the TXC PA12203001
- ambient light and proximity sensor.
+ tristate "TXC PA12203001 light and proximity sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ If you say yes here you get support for the TXC PA12203001
+ ambient light and proximity sensor.
- This driver can also be built as a module. If so, the module
- will be called pa12203001.
+ This driver can also be built as a module. If so, the module
+ will be called pa12203001.
config SI1133
tristate "SI1133 UV Index Sensor and Ambient Light Sensor"
@@ -349,12 +372,12 @@
depends on I2C
select REGMAP_I2C
help
- Say yes here to get support for the Sensortek STK3310 ambient light
- and proximity sensor. The STK3311 model is also supported by this
- driver.
+ Say yes here to get support for the Sensortek STK3310 ambient light
+ and proximity sensor. The STK3311 model is also supported by this
+ driver.
- Choosing M will build the driver as a module. If so, the module
- will be called stk3310.
+ Choosing M will build the driver as a module. If so, the module
+ will be called stk3310.
config ST_UVIS25
tristate "STMicroelectronics UVIS25 sensor driver"
@@ -386,11 +409,11 @@
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
- If you say yes here you get support for the TAOS TCS3414
- family of digital color sensors.
+ If you say yes here you get support for the TAOS TCS3414
+ family of digital color sensors.
- This driver can also be built as a module. If so, the module
- will be called tcs3414.
+ This driver can also be built as a module. If so, the module
+ will be called tcs3414.
config TCS3472
tristate "TAOS TCS3472 color light-to-digital converter"
@@ -398,96 +421,109 @@
select IIO_BUFFER
select IIO_TRIGGERED_BUFFER
help
- If you say yes here you get support for the TAOS TCS3472
- family of color light-to-digital converters with IR filter.
+ If you say yes here you get support for the TAOS TCS3472
+ family of color light-to-digital converters with IR filter.
- This driver can also be built as a module. If so, the module
- will be called tcs3472.
+ This driver can also be built as a module. If so, the module
+ will be called tcs3472.
config SENSORS_TSL2563
tristate "TAOS TSL2560, TSL2561, TSL2562 and TSL2563 ambient light sensors"
depends on I2C
help
- If you say yes here you get support for the Taos TSL2560,
- TSL2561, TSL2562 and TSL2563 ambient light sensors.
+ If you say yes here you get support for the Taos TSL2560,
+ TSL2561, TSL2562 and TSL2563 ambient light sensors.
- This driver can also be built as a module. If so, the module
- will be called tsl2563.
+ This driver can also be built as a module. If so, the module
+ will be called tsl2563.
config TSL2583
tristate "TAOS TSL2580, TSL2581 and TSL2583 light-to-digital converters"
depends on I2C
help
- Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
- Access ALS data via iio, sysfs.
+ Provides support for the TAOS tsl2580, tsl2581 and tsl2583 devices.
+ Access ALS data via iio, sysfs.
config TSL2772
tristate "TAOS TSL/TMD2x71 and TSL/TMD2x72 Family of light and proximity sensors"
depends on I2C
help
- Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672,
- tmd2672, tsl2772, tmd2772 devices.
- Provides iio_events and direct access via sysfs.
+ Support for: tsl2571, tsl2671, tmd2671, tsl2771, tmd2771, tsl2572, tsl2672,
+ tmd2672, tsl2772, tmd2772 devices.
+ Provides iio_events and direct access via sysfs.
config TSL4531
tristate "TAOS TSL4531 ambient light sensors"
depends on I2C
help
- Say Y here if you want to build a driver for the TAOS TSL4531 family
- of ambient light sensors with direct lux output.
+ Say Y here if you want to build a driver for the TAOS TSL4531 family
+ of ambient light sensors with direct lux output.
- To compile this driver as a module, choose M here: the
- module will be called tsl4531.
+ To compile this driver as a module, choose M here: the
+ module will be called tsl4531.
config US5182D
tristate "UPISEMI light and proximity sensor"
depends on I2C
help
- If you say yes here you get support for the UPISEMI US5182D
- ambient light and proximity sensor.
+ If you say yes here you get support for the UPISEMI US5182D
+ ambient light and proximity sensor.
- This driver can also be built as a module. If so, the module
- will be called us5182d.
+ This driver can also be built as a module. If so, the module
+ will be called us5182d.
config VCNL4000
tristate "VCNL4000/4010/4020/4200 combined ALS and proximity sensor"
depends on I2C
help
- Say Y here if you want to build a driver for the Vishay VCNL4000,
- VCNL4010, VCNL4020, VCNL4200 combined ambient light and proximity
- sensor.
+ Say Y here if you want to build a driver for the Vishay VCNL4000,
+ VCNL4010, VCNL4020, VCNL4200 combined ambient light and proximity
+ sensor.
- To compile this driver as a module, choose M here: the
- module will be called 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
help
- Say Y here if you want to build a driver for the Vishay VEML6070 UV A
- light sensor.
+ Say Y here if you want to build a driver for the Vishay VEML6070 UV A
+ light sensor.
- To compile this driver as a module, choose M here: the
- module will be called veml6070.
+ To compile this driver as a module, choose M here: the
+ module will be called veml6070.
config VL6180
tristate "VL6180 ALS, range and proximity sensor"
depends on I2C
help
- Say Y here if you want to build a driver for the STMicroelectronics
- VL6180 combined ambient light, range and proximity sensor.
+ Say Y here if you want to build a driver for the STMicroelectronics
+ VL6180 combined ambient light, range and proximity sensor.
- To compile this driver as a module, choose M here: the
- module will be called vl6180.
+ To compile this driver as a module, choose M here: the
+ module will be called vl6180.
config ZOPT2201
tristate "ZOPT2201 ALS and UV B sensor"
depends on I2C
help
- Say Y here if you want to build a driver for the IDT
- ZOPT2201 ambient light and UV B sensor.
+ Say Y here if you want to build a driver for the IDT
+ ZOPT2201 ambient light and UV B sensor.
- To compile this driver as a module, choose M here: the
- module will be called zopt2201.
+ To compile this driver as a module, choose M here: the
+ module will be called zopt2201.
endmenu
diff --git a/drivers/iio/light/Makefile b/drivers/iio/light/Makefile
index 86337b1..00d1f9b 100644
--- a/drivers/iio/light/Makefile
+++ b/drivers/iio/light/Makefile
@@ -28,6 +28,8 @@
obj-$(CONFIG_LTR501) += ltr501.o
obj-$(CONFIG_LV0104CS) += lv0104cs.o
obj-$(CONFIG_MAX44000) += max44000.o
+obj-$(CONFIG_MAX44009) += max44009.o
+obj-$(CONFIG_NOA1305) += noa1305.o
obj-$(CONFIG_OPT3001) += opt3001.o
obj-$(CONFIG_PA12203001) += pa12203001.o
obj-$(CONFIG_RPR0521) += rpr0521.o
@@ -45,6 +47,7 @@
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/acpi-als.c b/drivers/iio/light/acpi-als.c
index c35e2f8..1eafd0b 100644
--- a/drivers/iio/light/acpi-als.c
+++ b/drivers/iio/light/acpi-als.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ACPI Ambient Light Sensor Driver
*
@@ -10,20 +11,6 @@
* Final cleanup and debugging:
* Copyright (C) 2013-2014 Marek Vasut <marex@denx.de>
* Copyright (C) 2015 Gabriele Mazzotta <gabriele.mzt@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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include <linux/module.h>
diff --git a/drivers/iio/light/adjd_s311.c b/drivers/iio/light/adjd_s311.c
index e45bb6a..d3269cd 100644
--- a/drivers/iio/light/adjd_s311.c
+++ b/drivers/iio/light/adjd_s311.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* adjd_s311.c - Support for ADJD-S311-CR999 digital color sensor
*
* Copyright (C) 2012 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* driver for ADJD-S311-CR999 digital color sensor (10-bit channels for
* red, green, blue, clear); 7-bit I2C slave address 0x74
*
diff --git a/drivers/iio/light/al3320a.c b/drivers/iio/light/al3320a.c
index 66623fa..a21aa99 100644
--- a/drivers/iio/light/al3320a.c
+++ b/drivers/iio/light/al3320a.c
@@ -1,16 +1,12 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* AL3320A - Dyna Image Ambient Light Sensor
*
* Copyright (c) 2014, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for AL3320A (7-bit I2C slave address 0x1C).
*
* TODO: interrupt support, thresholds
- *
*/
#include <linux/module.h>
diff --git a/drivers/iio/light/apds9300.c b/drivers/iio/light/apds9300.c
index 5c15736..856b6c4 100644
--- a/drivers/iio/light/apds9300.c
+++ b/drivers/iio/light/apds9300.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* apds9300.c - IIO driver for Avago APDS9300 ambient light sensor
*
* Copyright 2013 Oleksandr Kravchenko <o.v.kravchenko@globallogic.com>
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
*/
#include <linux/module.h>
diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c
index 1f112ae..c5dfb9a 100644
--- a/drivers/iio/light/apds9960.c
+++ b/drivers/iio/light/apds9960.c
@@ -206,7 +206,8 @@
.name = APDS9960_REGMAP_NAME,
.reg_bits = 8,
.val_bits = 8,
- .use_single_rw = 1,
+ .use_single_read = true,
+ .use_single_write = true,
.volatile_table = &apds9960_volatile_table,
.precious_table = &apds9960_precious_table,
@@ -1134,5 +1135,5 @@
module_i2c_driver(apds9960_driver);
MODULE_AUTHOR("Matt Ranostay <matt.ranostay@konsulko.com>");
-MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor");
+MODULE_DESCRIPTION("APDS9960 Gesture/RGB/ALS/Proximity sensor");
MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/bh1750.c b/drivers/iio/light/bh1750.c
index a814828..28347df 100644
--- a/drivers/iio/light/bh1750.c
+++ b/drivers/iio/light/bh1750.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* ROHM BH1710/BH1715/BH1721/BH1750/BH1751 ambient light sensor driver
*
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
*
- * 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.
- *
* Data sheets:
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1710fvc-e.pdf
* http://rohmfs.rohm.com/en/products/databook/datasheet/ic/sensor/light/bh1715fvc-e.pdf
@@ -281,8 +278,7 @@
return 0;
}
-#ifdef CONFIG_PM_SLEEP
-static int bh1750_suspend(struct device *dev)
+static int __maybe_unused bh1750_suspend(struct device *dev)
{
int ret;
struct bh1750_data *data =
@@ -300,10 +296,6 @@
}
static SIMPLE_DEV_PM_OPS(bh1750_pm_ops, bh1750_suspend, NULL);
-#define BH1750_PM_OPS (&bh1750_pm_ops)
-#else
-#define BH1750_PM_OPS NULL
-#endif
static const struct i2c_device_id bh1750_id[] = {
{ "bh1710", BH1710 },
@@ -315,10 +307,21 @@
};
MODULE_DEVICE_TABLE(i2c, bh1750_id);
+static const struct of_device_id bh1750_of_match[] = {
+ { .compatible = "rohm,bh1710", },
+ { .compatible = "rohm,bh1715", },
+ { .compatible = "rohm,bh1721", },
+ { .compatible = "rohm,bh1750", },
+ { .compatible = "rohm,bh1751", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bh1750_of_match);
+
static struct i2c_driver bh1750_driver = {
.driver = {
.name = "bh1750",
- .pm = BH1750_PM_OPS,
+ .of_match_table = bh1750_of_match,
+ .pm = &bh1750_pm_ops,
},
.probe = bh1750_probe,
.remove = bh1750_remove,
diff --git a/drivers/iio/light/bh1780.c b/drivers/iio/light/bh1780.c
index 036f3bb..a836100 100644
--- a/drivers/iio/light/bh1780.c
+++ b/drivers/iio/light/bh1780.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ROHM 1780GLI Ambient Light Sensor Driver
*
@@ -145,7 +146,7 @@
{
int ret;
struct bh1780_data *bh1780;
- struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
+ struct i2c_adapter *adapter = client->adapter;
struct iio_dev *indio_dev;
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE))
diff --git a/drivers/iio/light/cm32181.c b/drivers/iio/light/cm32181.c
index aebf7dd..5f4fb56 100644
--- a/drivers/iio/light/cm32181.c
+++ b/drivers/iio/light/cm32181.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Capella Microsystems Inc.
* Author: Kevin Tsai <ktsai@capellamicro.com>
- *
- * 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>
diff --git a/drivers/iio/light/cm3232.c b/drivers/iio/light/cm3232.c
index c639cf2..cd3cfb7 100644
--- a/drivers/iio/light/cm3232.c
+++ b/drivers/iio/light/cm3232.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* CM3232 Ambient Light Sensor
*
* Copyright (C) 2014-2015 Capella Microsystems Inc.
* Author: Kevin Tsai <ktsai@capellamicro.com>
*
- * 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.
- *
* IIO driver for CM3232 (7-bit I2C slave address 0x10).
*/
diff --git a/drivers/iio/light/cm3323.c b/drivers/iio/light/cm3323.c
index 83b08b6..0443861 100644
--- a/drivers/iio/light/cm3323.c
+++ b/drivers/iio/light/cm3323.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* CM3323 - Capella Color Light Sensor
*
* Copyright (c) 2015, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for CM3323 (7-bit I2C slave address 0x10)
*
* TODO: calibscale to correct the lens factor
@@ -104,15 +101,16 @@
return 0;
}
-static void cm3323_disable(struct iio_dev *indio_dev)
+static void cm3323_disable(void *data)
{
int ret;
- struct cm3323_data *data = iio_priv(indio_dev);
+ struct iio_dev *indio_dev = data;
+ struct cm3323_data *cm_data = iio_priv(indio_dev);
- ret = i2c_smbus_write_word_data(data->client, CM3323_CMD_CONF,
+ ret = i2c_smbus_write_word_data(cm_data->client, CM3323_CMD_CONF,
CM3323_CONF_SD_BIT);
if (ret < 0)
- dev_err(&data->client->dev, "Error writing reg_conf\n");
+ dev_err(&cm_data->client->dev, "Error writing reg_conf\n");
}
static int cm3323_set_it_bits(struct cm3323_data *data, int val, int val2)
@@ -246,26 +244,11 @@
return ret;
}
- ret = iio_device_register(indio_dev);
- if (ret < 0) {
- dev_err(&client->dev, "failed to register iio dev\n");
- goto err_init;
- }
+ ret = devm_add_action_or_reset(&client->dev, cm3323_disable, indio_dev);
+ if (ret < 0)
+ return ret;
- return 0;
-err_init:
- cm3323_disable(indio_dev);
- return ret;
-}
-
-static int cm3323_remove(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
-
- iio_device_unregister(indio_dev);
- cm3323_disable(indio_dev);
-
- return 0;
+ return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id cm3323_id[] = {
@@ -279,7 +262,6 @@
.name = CM3323_DRV_NAME,
},
.probe = cm3323_probe,
- .remove = cm3323_remove,
.id_table = cm3323_id,
};
diff --git a/drivers/iio/light/cm3605.c b/drivers/iio/light/cm3605.c
index e454bc6..964ede4 100644
--- a/drivers/iio/light/cm3605.c
+++ b/drivers/iio/light/cm3605.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* CM3605 Ambient Light and Proximity Sensor
*
diff --git a/drivers/iio/light/cm36651.c b/drivers/iio/light/cm36651.c
index 1dd8ed0..1019d62 100644
--- a/drivers/iio/light/cm36651.c
+++ b/drivers/iio/light/cm36651.c
@@ -1,10 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Beomho Seo <beomho.seo@samsung.com>
- *
- * 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>
@@ -649,18 +646,18 @@
i2c_set_clientdata(client, indio_dev);
cm36651->client = client;
- cm36651->ps_client = i2c_new_dummy(client->adapter,
+ cm36651->ps_client = i2c_new_dummy_device(client->adapter,
CM36651_I2C_ADDR_PS);
- if (!cm36651->ps_client) {
+ if (IS_ERR(cm36651->ps_client)) {
dev_err(&client->dev, "%s: new i2c device failed\n", __func__);
- ret = -ENODEV;
+ ret = PTR_ERR(cm36651->ps_client);
goto error_disable_reg;
}
- cm36651->ara_client = i2c_new_dummy(client->adapter, CM36651_ARA);
- if (!cm36651->ara_client) {
+ cm36651->ara_client = i2c_new_dummy_device(client->adapter, CM36651_ARA);
+ if (IS_ERR(cm36651->ara_client)) {
dev_err(&client->dev, "%s: new i2c device failed\n", __func__);
- ret = -ENODEV;
+ ret = PTR_ERR(cm36651->ara_client);
goto error_i2c_unregister_ps;
}
diff --git a/drivers/iio/light/cros_ec_light_prox.c b/drivers/iio/light/cros_ec_light_prox.c
index fd1609e..c5263b5 100644
--- a/drivers/iio/light/cros_ec_light_prox.c
+++ b/drivers/iio/light/cros_ec_light_prox.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* cros_ec_light_prox - Driver for light and prox sensors behing CrosEC.
*
* Copyright (C) 2017 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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/delay.h>
#include <linux/device.h>
#include <linux/iio/buffer.h>
#include <linux/iio/common/cros_ec_sensors_core.h>
@@ -24,11 +15,11 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
-#include <linux/sysfs.h>
/*
* We only represent one entry for light or proximity. EC is merging different
@@ -52,7 +43,7 @@
struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
u16 data = 0;
s64 val64;
- int ret = IIO_VAL_INT;
+ int ret;
int idx = chan->scan_index;
mutex_lock(&st->core.cmd_lock);
@@ -60,23 +51,22 @@
switch (mask) {
case IIO_CHAN_INFO_RAW:
if (chan->type == IIO_PROXIMITY) {
- if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
- (s16 *)&data) < 0) {
- ret = -EIO;
+ ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
+ (s16 *)&data);
+ if (ret)
break;
- }
*val = data;
+ ret = IIO_VAL_INT;
} else {
ret = -EINVAL;
}
break;
case IIO_CHAN_INFO_PROCESSED:
if (chan->type == IIO_LIGHT) {
- if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
- (s16 *)&data) < 0) {
- ret = -EIO;
+ ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
+ (s16 *)&data);
+ if (ret)
break;
- }
/*
* The data coming from the light sensor is
* pre-processed and represents the ambient light
@@ -92,15 +82,16 @@
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
st->core.param.sensor_offset.flags = 0;
- if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
- ret = -EIO;
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ if (ret)
break;
- }
/* Save values */
- st->core.calib[0] = st->core.resp->sensor_offset.offset[0];
+ st->core.calib[0].offset =
+ st->core.resp->sensor_offset.offset[0];
- *val = st->core.calib[idx];
+ *val = st->core.calib[idx].offset;
+ ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_CALIBSCALE:
/*
@@ -111,10 +102,9 @@
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
- if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
- ret = -EIO;
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ if (ret)
break;
- }
val64 = st->core.resp->sensor_range.ret;
*val = val64 >> 16;
@@ -137,28 +127,27 @@
int val, int val2, long mask)
{
struct cros_ec_light_prox_state *st = iio_priv(indio_dev);
- int ret = 0;
+ int ret;
int idx = chan->scan_index;
mutex_lock(&st->core.cmd_lock);
switch (mask) {
case IIO_CHAN_INFO_CALIBBIAS:
- st->core.calib[idx] = val;
+ st->core.calib[idx].offset = val;
/* Send to EC for each axis, even if not complete */
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_OFFSET;
st->core.param.sensor_offset.flags = MOTION_SENSE_SET_OFFSET;
- st->core.param.sensor_offset.offset[0] = st->core.calib[0];
+ st->core.param.sensor_offset.offset[0] =
+ st->core.calib[0].offset;
st->core.param.sensor_offset.temp =
EC_MOTION_SENSE_INVALID_CALIB_TEMP;
- if (cros_ec_motion_send_host_cmd(&st->core, 0))
- ret = -EIO;
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
break;
case IIO_CHAN_INFO_CALIBSCALE:
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
st->core.param.sensor_range.data = (val << 16) | (val2 / 100);
- if (cros_ec_motion_send_host_cmd(&st->core, 0))
- ret = -EIO;
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
break;
default:
ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,
@@ -174,6 +163,7 @@
static const struct iio_info cros_ec_light_prox_info = {
.read_raw = &cros_ec_light_prox_read,
.write_raw = &cros_ec_light_prox_write,
+ .read_avail = &cros_ec_sensors_core_read_avail,
};
static int cros_ec_light_prox_probe(struct platform_device *pdev)
@@ -208,6 +198,8 @@
channel->info_mask_shared_by_all =
BIT(IIO_CHAN_INFO_SAMP_FREQ) |
BIT(IIO_CHAN_INFO_FREQUENCY);
+ channel->info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_SAMP_FREQ);
channel->scan_type.realbits = CROS_EC_SENSOR_BITS;
channel->scan_type.storagebits = CROS_EC_SENSOR_BITS;
channel->scan_type.shift = 0;
@@ -215,8 +207,6 @@
channel->ext_info = cros_ec_sensors_ext_info;
channel->scan_type.sign = 'u';
- state->core.calib[0] = 0;
-
/* Sensor specific */
switch (state->core.type) {
case MOTIONSENSE_TYPE_LIGHT:
diff --git a/drivers/iio/light/gp2ap020a00f.c b/drivers/iio/light/gp2ap020a00f.c
index 44b13fb..4d70c5b 100644
--- a/drivers/iio/light/gp2ap020a00f.c
+++ b/drivers/iio/light/gp2ap020a00f.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (C) 2013 Samsung Electronics Co., Ltd.
* Author: Jacek Anaszewski <j.anaszewski@samsung.com>
@@ -28,10 +29,6 @@
* with any triggers or illuminance events. Enabling/disabling
* one of the proximity events automatically enables/disables
* the other one.
- *
- * 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/debugfs.h>
diff --git a/drivers/iio/light/hid-sensor-als.c b/drivers/iio/light/hid-sensor-als.c
index 94f3325..b6cd299 100644
--- a/drivers/iio/light/hid-sensor-als.c
+++ b/drivers/iio/light/hid-sensor-als.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <linux/device.h>
#include <linux/platform_device.h>
diff --git a/drivers/iio/light/hid-sensor-prox.c b/drivers/iio/light/hid-sensor-prox.c
index cf5a0c2..7e1030a 100644
--- a/drivers/iio/light/hid-sensor-prox.c
+++ b/drivers/iio/light/hid-sensor-prox.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.
- *
*/
#include <linux/device.h>
#include <linux/platform_device.h>
diff --git a/drivers/iio/light/isl29018.c b/drivers/iio/light/isl29018.c
index b45400f..805a74f 100644
--- a/drivers/iio/light/isl29018.c
+++ b/drivers/iio/light/isl29018.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* A iio driver for the light sensor ISL 29018/29023/29035.
*
@@ -5,16 +6,6 @@
* sensing and infrared sensing.
*
* Copyright (c) 2010, NVIDIA Corporation.
- *
- * 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>
@@ -23,6 +14,7 @@
#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
@@ -95,6 +87,7 @@
struct isl29018_scale scale;
int prox_scheme;
bool suspended;
+ struct regulator *vcc_reg;
};
static int isl29018_set_integration_time(struct isl29018_chip *chip,
@@ -708,6 +701,16 @@
return dev_name(dev);
}
+static void isl29018_disable_regulator_action(void *_data)
+{
+ struct isl29018_chip *chip = _data;
+ int err;
+
+ err = regulator_disable(chip->vcc_reg);
+ if (err)
+ pr_err("failed to disable isl29018's VCC regulator!\n");
+}
+
static int isl29018_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -742,6 +745,27 @@
chip->scale = isl29018_scales[chip->int_time][0];
chip->suspended = false;
+ chip->vcc_reg = devm_regulator_get(&client->dev, "vcc");
+ if (IS_ERR(chip->vcc_reg)) {
+ err = PTR_ERR(chip->vcc_reg);
+ if (err != -EPROBE_DEFER)
+ dev_err(&client->dev, "failed to get VCC regulator!\n");
+ return err;
+ }
+
+ err = regulator_enable(chip->vcc_reg);
+ if (err) {
+ dev_err(&client->dev, "failed to enable VCC regulator!\n");
+ return err;
+ }
+
+ err = devm_add_action_or_reset(&client->dev, isl29018_disable_regulator_action,
+ chip);
+ if (err) {
+ dev_err(&client->dev, "failed to setup regulator cleanup action!\n");
+ return err;
+ }
+
chip->regmap = devm_regmap_init_i2c(client,
isl29018_chip_info_tbl[dev_id].regmap_cfg);
if (IS_ERR(chip->regmap)) {
@@ -768,6 +792,7 @@
static int isl29018_suspend(struct device *dev)
{
struct isl29018_chip *chip = iio_priv(dev_get_drvdata(dev));
+ int ret;
mutex_lock(&chip->lock);
@@ -777,10 +802,13 @@
* So we do not have much to do here.
*/
chip->suspended = true;
+ ret = regulator_disable(chip->vcc_reg);
+ if (ret)
+ dev_err(dev, "failed to disable VCC regulator\n");
mutex_unlock(&chip->lock);
- return 0;
+ return ret;
}
static int isl29018_resume(struct device *dev)
@@ -790,6 +818,13 @@
mutex_lock(&chip->lock);
+ err = regulator_enable(chip->vcc_reg);
+ if (err) {
+ dev_err(dev, "failed to enable VCC regulator\n");
+ mutex_unlock(&chip->lock);
+ return err;
+ }
+
err = isl29018_chip_init(chip);
if (!err)
chip->suspended = false;
diff --git a/drivers/iio/light/isl29028.c b/drivers/iio/light/isl29028.c
index f9912ab..4d220c8 100644
--- a/drivers/iio/light/isl29028.c
+++ b/drivers/iio/light/isl29028.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* IIO driver for the light sensor ISL29028.
* ISL29028 is Concurrent Ambient Light and Proximity Sensor
@@ -5,18 +6,6 @@
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
* Copyright (c) 2016-2017 Brian Masney <masneyb@onstation.org>
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- *
* Datasheets:
* - http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29028.pdf
* - http://www.intersil.com/content/dam/Intersil/documents/isl2/isl29030.pdf
diff --git a/drivers/iio/light/isl29125.c b/drivers/iio/light/isl29125.c
index ed38edc..e37894f 100644
--- a/drivers/iio/light/isl29125.c
+++ b/drivers/iio/light/isl29125.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* isl29125.c - Support for Intersil ISL29125 RGB light sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* RGB light sensor with 16-bit channels for red, green, blue);
* 7-bit I2C slave address 0x44
*
diff --git a/drivers/iio/light/jsa1212.c b/drivers/iio/light/jsa1212.c
index 811505d..13deeeb 100644
--- a/drivers/iio/light/jsa1212.c
+++ b/drivers/iio/light/jsa1212.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* JSA1212 Ambient Light & Proximity Sensor Driver
*
* Copyright (c) 2014, Intel Corporation.
*
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
* JSA1212 I2C slave address: 0x44(ADDR tied to GND), 0x45(ADDR tied to VDD)
*
* TODO: Interrupt support, thresholds, range support.
diff --git a/drivers/iio/light/lm3533-als.c b/drivers/iio/light/lm3533-als.c
index ff5a332..6733b52 100644
--- a/drivers/iio/light/lm3533-als.c
+++ b/drivers/iio/light/lm3533-als.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* lm3533-als.c -- LM3533 Ambient Light Sensor driver
*
* Copyright (C) 2011-2012 Texas Instruments
*
* Author: Johan Hovold <jhovold@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.
*/
#include <linux/atomic.h>
diff --git a/drivers/iio/light/ltr501.c b/drivers/iio/light/ltr501.c
index 830a2d4..71f99d2 100644
--- a/drivers/iio/light/ltr501.c
+++ b/drivers/iio/light/ltr501.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ltr501.c - Support for Lite-On LTR501 ambient light and proximity sensor
*
* Copyright 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* 7-bit I2C slave address 0x23
*
* TODO: IR LED characteristics
diff --git a/drivers/iio/light/max44000.c b/drivers/iio/light/max44000.c
index bcdb0eb..d6d8007 100644
--- a/drivers/iio/light/max44000.c
+++ b/drivers/iio/light/max44000.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MAX44000 Ambient and Infrared Proximity Sensor
*
* Copyright (c) 2016, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Data sheet: https://datasheets.maximintegrated.com/en/ds/MAX44000.pdf
*
* 7-bit I2C slave address 0x4a
@@ -99,7 +96,6 @@
* Handling this internally is also required for buffer support because the
* channel's scan_type can't be modified dynamically.
*/
-static const int max44000_alstim_shift[] = {0, 2, 4, 6};
#define MAX44000_ALSTIM_SHIFT(alstim) (2 * (alstim))
/* Available integration times with pretty manual alignment: */
@@ -473,17 +469,18 @@
}
static const struct regmap_config max44000_regmap_config = {
- .reg_bits = 8,
- .val_bits = 8,
+ .reg_bits = 8,
+ .val_bits = 8,
- .max_register = MAX44000_REG_PRX_DATA,
- .readable_reg = max44000_readable_reg,
- .writeable_reg = max44000_writeable_reg,
- .volatile_reg = max44000_volatile_reg,
- .precious_reg = max44000_precious_reg,
+ .max_register = MAX44000_REG_PRX_DATA,
+ .readable_reg = max44000_readable_reg,
+ .writeable_reg = max44000_writeable_reg,
+ .volatile_reg = max44000_volatile_reg,
+ .precious_reg = max44000_precious_reg,
- .use_single_rw = 1,
- .cache_type = REGCACHE_RBTREE,
+ .use_single_read = true,
+ .use_single_write = true,
+ .cache_type = REGCACHE_RBTREE,
};
static irqreturn_t max44000_trigger_handler(int irq, void *p)
diff --git a/drivers/iio/light/max44009.c b/drivers/iio/light/max44009.c
new file mode 100644
index 0000000..00ba154
--- /dev/null
+++ b/drivers/iio/light/max44009.c
@@ -0,0 +1,555 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * max44009.c - Support for MAX44009 Ambient Light Sensor
+ *
+ * Copyright (c) 2019 Robert Eshleman <bobbyeshleman@gmail.com>
+ *
+ * Datasheet: https://datasheets.maximintegrated.com/en/ds/MAX44009.pdf
+ *
+ * TODO: Support continuous mode and configuring from manual mode to
+ * automatic mode.
+ *
+ * Default I2C address: 0x4a
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/bits.h>
+#include <linux/i2c.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/util_macros.h>
+
+#define MAX44009_DRV_NAME "max44009"
+
+/* Registers in datasheet order */
+#define MAX44009_REG_INT_STATUS 0x0
+#define MAX44009_REG_INT_EN 0x1
+#define MAX44009_REG_CFG 0x2
+#define MAX44009_REG_LUX_HI 0x3
+#define MAX44009_REG_LUX_LO 0x4
+#define MAX44009_REG_UPPER_THR 0x5
+#define MAX44009_REG_LOWER_THR 0x6
+#define MAX44009_REG_THR_TIMER 0x7
+
+#define MAX44009_CFG_TIM_MASK GENMASK(2, 0)
+#define MAX44009_CFG_MAN_MODE_MASK BIT(6)
+
+/* The maximum rising threshold for the max44009 */
+#define MAX44009_MAXIMUM_THRESHOLD 7520256
+
+#define MAX44009_THRESH_EXP_MASK (0xf << 4)
+#define MAX44009_THRESH_EXP_RSHIFT 4
+#define MAX44009_THRESH_MANT_LSHIFT 4
+#define MAX44009_THRESH_MANT_MASK 0xf
+
+#define MAX44009_UPPER_THR_MINIMUM 15
+
+/* The max44009 always scales raw readings by 0.045 and is non-configurable */
+#define MAX44009_SCALE_NUMERATOR 45
+#define MAX44009_SCALE_DENOMINATOR 1000
+
+/* The fixed-point fractional multiplier for de-scaling threshold values */
+#define MAX44009_FRACT_MULT 1000000
+
+static const u32 max44009_int_time_ns_array[] = {
+ 800000000,
+ 400000000,
+ 200000000,
+ 100000000,
+ 50000000, /* Manual mode only */
+ 25000000, /* Manual mode only */
+ 12500000, /* Manual mode only */
+ 6250000, /* Manual mode only */
+};
+
+static const char max44009_int_time_str[] =
+ "0.8 "
+ "0.4 "
+ "0.2 "
+ "0.1 "
+ "0.05 "
+ "0.025 "
+ "0.0125 "
+ "0.00625";
+
+struct max44009_data {
+ struct i2c_client *client;
+ struct mutex lock;
+};
+
+static const struct iio_event_spec max44009_event_spec[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_FALLING,
+ .mask_separate = BIT(IIO_EV_INFO_VALUE) |
+ BIT(IIO_EV_INFO_ENABLE),
+ },
+};
+
+static const struct iio_chan_spec max44009_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ .event_spec = max44009_event_spec,
+ .num_event_specs = ARRAY_SIZE(max44009_event_spec),
+ },
+};
+
+static int max44009_read_int_time(struct max44009_data *data)
+{
+
+ int ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_CFG);
+
+ if (ret < 0)
+ return ret;
+
+ return max44009_int_time_ns_array[ret & MAX44009_CFG_TIM_MASK];
+}
+
+static int max44009_write_int_time(struct max44009_data *data,
+ int val, int val2)
+{
+ struct i2c_client *client = data->client;
+ int ret, int_time, config;
+ s64 ns;
+
+ ns = val * NSEC_PER_SEC + val2;
+ int_time = find_closest_descending(
+ ns,
+ max44009_int_time_ns_array,
+ ARRAY_SIZE(max44009_int_time_ns_array));
+
+ ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
+ if (ret < 0)
+ return ret;
+
+ config = ret;
+ config &= int_time;
+
+ /*
+ * To set the integration time, the device must also be in manual
+ * mode.
+ */
+ config |= MAX44009_CFG_MAN_MODE_MASK;
+
+ return i2c_smbus_write_byte_data(client, MAX44009_REG_CFG, config);
+}
+
+static int max44009_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ struct max44009_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (mask == IIO_CHAN_INFO_INT_TIME && chan->type == IIO_LIGHT) {
+ mutex_lock(&data->lock);
+ ret = max44009_write_int_time(data, val, val2);
+ mutex_unlock(&data->lock);
+ return ret;
+ }
+ return -EINVAL;
+}
+
+static int max44009_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ return IIO_VAL_INT_PLUS_NANO;
+}
+
+static int max44009_lux_raw(u8 hi, u8 lo)
+{
+ int mantissa;
+ int exponent;
+
+ /*
+ * The mantissa consists of the low nibble of the Lux High Byte
+ * and the low nibble of the Lux Low Byte.
+ */
+ mantissa = ((hi & 0xf) << 4) | (lo & 0xf);
+
+ /* The exponent byte is just the upper nibble of the Lux High Byte */
+ exponent = (hi >> 4) & 0xf;
+
+ /*
+ * The exponent value is base 2 to the power of the raw exponent byte.
+ */
+ exponent = 1 << exponent;
+
+ return exponent * mantissa;
+}
+
+#define MAX44009_READ_LUX_XFER_LEN (4)
+
+static int max44009_read_lux_raw(struct max44009_data *data)
+{
+ int ret;
+ u8 hireg = MAX44009_REG_LUX_HI;
+ u8 loreg = MAX44009_REG_LUX_LO;
+ u8 lo = 0;
+ u8 hi = 0;
+
+ struct i2c_msg msgs[] = {
+ {
+ .addr = data->client->addr,
+ .flags = 0,
+ .len = sizeof(hireg),
+ .buf = &hireg,
+ },
+ {
+ .addr = data->client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(hi),
+ .buf = &hi,
+ },
+ {
+ .addr = data->client->addr,
+ .flags = 0,
+ .len = sizeof(loreg),
+ .buf = &loreg,
+ },
+ {
+ .addr = data->client->addr,
+ .flags = I2C_M_RD,
+ .len = sizeof(lo),
+ .buf = &lo,
+ }
+ };
+
+ /*
+ * Use i2c_transfer instead of smbus read because i2c_transfer
+ * does NOT use a stop bit between address write and data read.
+ * Using a stop bit causes disjoint upper/lower byte reads and
+ * reduces accuracy.
+ */
+ ret = i2c_transfer(data->client->adapter,
+ msgs, MAX44009_READ_LUX_XFER_LEN);
+
+ if (ret != MAX44009_READ_LUX_XFER_LEN)
+ return -EIO;
+
+ return max44009_lux_raw(hi, lo);
+}
+
+static int max44009_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct max44009_data *data = iio_priv(indio_dev);
+ int lux_raw;
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = max44009_read_lux_raw(data);
+ if (ret < 0)
+ return ret;
+ lux_raw = ret;
+
+ *val = lux_raw * MAX44009_SCALE_NUMERATOR;
+ *val2 = MAX44009_SCALE_DENOMINATOR;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_INT_TIME:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = max44009_read_int_time(data);
+ if (ret < 0)
+ return ret;
+
+ *val2 = ret;
+ *val = 0;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static IIO_CONST_ATTR(illuminance_integration_time_available,
+ max44009_int_time_str);
+
+static struct attribute *max44009_attributes[] = {
+ &iio_const_attr_illuminance_integration_time_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group max44009_attribute_group = {
+ .attrs = max44009_attributes,
+};
+
+static int max44009_threshold_byte_from_fraction(int integral, int fractional)
+{
+ int mantissa, exp;
+
+ if ((integral <= 0 && fractional <= 0) ||
+ integral > MAX44009_MAXIMUM_THRESHOLD ||
+ (integral == MAX44009_MAXIMUM_THRESHOLD && fractional != 0))
+ return -EINVAL;
+
+ /* Reverse scaling of fixed-point integral */
+ mantissa = integral * MAX44009_SCALE_DENOMINATOR;
+ mantissa /= MAX44009_SCALE_NUMERATOR;
+
+ /* Reverse scaling of fixed-point fractional */
+ mantissa += fractional / MAX44009_FRACT_MULT *
+ (MAX44009_SCALE_DENOMINATOR / MAX44009_SCALE_NUMERATOR);
+
+ for (exp = 0; mantissa > 0xff; exp++)
+ mantissa >>= 1;
+
+ mantissa >>= 4;
+ mantissa &= 0xf;
+ exp <<= 4;
+
+ return exp | mantissa;
+}
+
+static int max44009_get_thr_reg(enum iio_event_direction dir)
+{
+ switch (dir) {
+ case IIO_EV_DIR_RISING:
+ return MAX44009_REG_UPPER_THR;
+ case IIO_EV_DIR_FALLING:
+ return MAX44009_REG_LOWER_THR;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int max44009_write_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int val, int val2)
+{
+ struct max44009_data *data = iio_priv(indio_dev);
+ int reg, threshold;
+
+ if (info != IIO_EV_INFO_VALUE || chan->type != IIO_LIGHT)
+ return -EINVAL;
+
+ threshold = max44009_threshold_byte_from_fraction(val, val2);
+ if (threshold < 0)
+ return threshold;
+
+ reg = max44009_get_thr_reg(dir);
+ if (reg < 0)
+ return reg;
+
+ return i2c_smbus_write_byte_data(data->client, reg, threshold);
+}
+
+static int max44009_read_threshold(struct iio_dev *indio_dev,
+ enum iio_event_direction dir)
+{
+ struct max44009_data *data = iio_priv(indio_dev);
+ int byte, reg;
+ int mantissa, exponent;
+
+ reg = max44009_get_thr_reg(dir);
+ if (reg < 0)
+ return reg;
+
+ byte = i2c_smbus_read_byte_data(data->client, reg);
+ if (byte < 0)
+ return byte;
+
+ mantissa = byte & MAX44009_THRESH_MANT_MASK;
+ mantissa <<= MAX44009_THRESH_MANT_LSHIFT;
+
+ /*
+ * To get the upper threshold, always adds the minimum upper threshold
+ * value to the shifted byte value (see datasheet).
+ */
+ if (dir == IIO_EV_DIR_RISING)
+ mantissa += MAX44009_UPPER_THR_MINIMUM;
+
+ /*
+ * Exponent is base 2 to the power of the threshold exponent byte
+ * value
+ */
+ exponent = byte & MAX44009_THRESH_EXP_MASK;
+ exponent >>= MAX44009_THRESH_EXP_RSHIFT;
+
+ return (1 << exponent) * mantissa;
+}
+
+static int max44009_read_event_value(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ enum iio_event_info info,
+ int *val, int *val2)
+{
+ int ret;
+ int threshold;
+
+ if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ ret = max44009_read_threshold(indio_dev, dir);
+ if (ret < 0)
+ return ret;
+ threshold = ret;
+
+ *val = threshold * MAX44009_SCALE_NUMERATOR;
+ *val2 = MAX44009_SCALE_DENOMINATOR;
+
+ return IIO_VAL_FRACTIONAL;
+}
+
+static int max44009_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ int state)
+{
+ struct max44009_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(data->client,
+ MAX44009_REG_INT_EN, state);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Set device to trigger interrupt immediately upon exceeding
+ * the threshold limit.
+ */
+ return i2c_smbus_write_byte_data(data->client,
+ MAX44009_REG_THR_TIMER, 0);
+}
+
+static int max44009_read_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir)
+{
+ struct max44009_data *data = iio_priv(indio_dev);
+
+ if (chan->type != IIO_LIGHT || type != IIO_EV_TYPE_THRESH)
+ return -EINVAL;
+
+ return i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_EN);
+}
+
+static const struct iio_info max44009_info = {
+ .read_raw = max44009_read_raw,
+ .write_raw = max44009_write_raw,
+ .write_raw_get_fmt = max44009_write_raw_get_fmt,
+ .read_event_value = max44009_read_event_value,
+ .read_event_config = max44009_read_event_config,
+ .write_event_value = max44009_write_event_value,
+ .write_event_config = max44009_write_event_config,
+ .attrs = &max44009_attribute_group,
+};
+
+static irqreturn_t max44009_threaded_irq_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct max44009_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(data->client, MAX44009_REG_INT_STATUS);
+ if (ret) {
+ 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));
+
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int max44009_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct max44009_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &max44009_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->name = MAX44009_DRV_NAME;
+ indio_dev->channels = max44009_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max44009_channels);
+ mutex_init(&data->lock);
+
+ /* Clear any stale interrupt bit */
+ ret = i2c_smbus_read_byte_data(client, MAX44009_REG_CFG);
+ if (ret < 0)
+ return ret;
+
+ if (client->irq > 0) {
+ ret = devm_request_threaded_irq(&client->dev, client->irq,
+ NULL,
+ max44009_threaded_irq_handler,
+ IRQF_TRIGGER_FALLING |
+ IRQF_ONESHOT | IRQF_SHARED,
+ "max44009_event",
+ indio_dev);
+ if (ret < 0)
+ return ret;
+ }
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id max44009_id[] = {
+ { "max44009", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max44009_id);
+
+static struct i2c_driver max44009_driver = {
+ .driver = {
+ .name = MAX44009_DRV_NAME,
+ },
+ .probe = max44009_probe,
+ .id_table = max44009_id,
+};
+module_i2c_driver(max44009_driver);
+
+static const struct of_device_id max44009_of_match[] = {
+ { .compatible = "maxim,max44009" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max44009_of_match);
+
+MODULE_AUTHOR("Robert Eshleman <bobbyeshleman@gmail.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MAX44009 ambient light sensor driver");
diff --git a/drivers/iio/light/noa1305.c b/drivers/iio/light/noa1305.c
new file mode 100644
index 0000000..5ebfbc5
--- /dev/null
+++ b/drivers/iio/light/noa1305.c
@@ -0,0 +1,313 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Support for ON Semiconductor NOA1305 ambient light sensor
+ *
+ * Copyright (C) 2016 Emcraft Systems
+ * Copyright (C) 2019 Collabora Ltd.
+ */
+
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#define NOA1305_REG_POWER_CONTROL 0x0
+#define NOA1305_POWER_CONTROL_DOWN 0x00
+#define NOA1305_POWER_CONTROL_ON 0x08
+#define NOA1305_REG_RESET 0x1
+#define NOA1305_RESET_RESET 0x10
+#define NOA1305_REG_INTEGRATION_TIME 0x2
+#define NOA1305_INTEGR_TIME_800MS 0x00
+#define NOA1305_INTEGR_TIME_400MS 0x01
+#define NOA1305_INTEGR_TIME_200MS 0x02
+#define NOA1305_INTEGR_TIME_100MS 0x03
+#define NOA1305_INTEGR_TIME_50MS 0x04
+#define NOA1305_INTEGR_TIME_25MS 0x05
+#define NOA1305_INTEGR_TIME_12_5MS 0x06
+#define NOA1305_INTEGR_TIME_6_25MS 0x07
+#define NOA1305_REG_INT_SELECT 0x3
+#define NOA1305_INT_SEL_ACTIVE_HIGH 0x01
+#define NOA1305_INT_SEL_ACTIVE_LOW 0x02
+#define NOA1305_INT_SEL_INACTIVE 0x03
+#define NOA1305_REG_INT_THRESH_LSB 0x4
+#define NOA1305_REG_INT_THRESH_MSB 0x5
+#define NOA1305_REG_ALS_DATA_LSB 0x6
+#define NOA1305_REG_ALS_DATA_MSB 0x7
+#define NOA1305_REG_DEVICE_ID_LSB 0x8
+#define NOA1305_REG_DEVICE_ID_MSB 0x9
+
+#define NOA1305_DEVICE_ID 0x0519
+#define NOA1305_DRIVER_NAME "noa1305"
+
+struct noa1305_priv {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct regulator *vin_reg;
+};
+
+static int noa1305_measure(struct noa1305_priv *priv)
+{
+ __le16 data;
+ int ret;
+
+ ret = regmap_bulk_read(priv->regmap, NOA1305_REG_ALS_DATA_LSB, &data,
+ 2);
+ if (ret < 0)
+ return ret;
+
+ return le16_to_cpu(data);
+}
+
+static int noa1305_scale(struct noa1305_priv *priv, int *val, int *val2)
+{
+ int data;
+ int ret;
+
+ ret = regmap_read(priv->regmap, NOA1305_REG_INTEGRATION_TIME, &data);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Lux = count / (<Integration Constant> * <Integration Time>)
+ *
+ * Integration Constant = 7.7
+ * Integration Time in Seconds
+ */
+ switch (data) {
+ case NOA1305_INTEGR_TIME_800MS:
+ *val = 100;
+ *val2 = 77 * 8;
+ break;
+ case NOA1305_INTEGR_TIME_400MS:
+ *val = 100;
+ *val2 = 77 * 4;
+ break;
+ case NOA1305_INTEGR_TIME_200MS:
+ *val = 100;
+ *val2 = 77 * 2;
+ break;
+ case NOA1305_INTEGR_TIME_100MS:
+ *val = 100;
+ *val2 = 77;
+ break;
+ case NOA1305_INTEGR_TIME_50MS:
+ *val = 1000;
+ *val2 = 77 * 5;
+ break;
+ case NOA1305_INTEGR_TIME_25MS:
+ *val = 10000;
+ *val2 = 77 * 25;
+ break;
+ case NOA1305_INTEGR_TIME_12_5MS:
+ *val = 100000;
+ *val2 = 77 * 125;
+ break;
+ case NOA1305_INTEGR_TIME_6_25MS:
+ *val = 1000000;
+ *val2 = 77 * 625;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return IIO_VAL_FRACTIONAL;
+}
+
+static const struct iio_chan_spec noa1305_channels[] = {
+ {
+ .type = IIO_LIGHT,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ }
+};
+
+static int noa1305_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret = -EINVAL;
+ struct noa1305_priv *priv = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ ret = noa1305_measure(priv);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ default:
+ break;
+ }
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_LIGHT:
+ return noa1305_scale(priv, val, val2);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+static const struct iio_info noa1305_info = {
+ .read_raw = noa1305_read_raw,
+};
+
+static bool noa1305_writable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NOA1305_REG_POWER_CONTROL:
+ case NOA1305_REG_RESET:
+ case NOA1305_REG_INTEGRATION_TIME:
+ case NOA1305_REG_INT_SELECT:
+ case NOA1305_REG_INT_THRESH_LSB:
+ case NOA1305_REG_INT_THRESH_MSB:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config noa1305_regmap_config = {
+ .name = NOA1305_DRIVER_NAME,
+ .reg_bits = 8,
+ .val_bits = 8,
+ .max_register = NOA1305_REG_DEVICE_ID_MSB,
+ .writeable_reg = noa1305_writable_reg,
+};
+
+static void noa1305_reg_remove(void *data)
+{
+ struct noa1305_priv *priv = data;
+
+ regulator_disable(priv->vin_reg);
+}
+
+static int noa1305_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct noa1305_priv *priv;
+ struct iio_dev *indio_dev;
+ struct regmap *regmap;
+ __le16 data;
+ unsigned int dev_id;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ regmap = devm_regmap_init_i2c(client, &noa1305_regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "Regmap initialization failed.\n");
+ return PTR_ERR(regmap);
+ }
+
+ priv = iio_priv(indio_dev);
+
+ priv->vin_reg = devm_regulator_get(&client->dev, "vin");
+ if (IS_ERR(priv->vin_reg)) {
+ dev_err(&client->dev, "get regulator vin failed\n");
+ return PTR_ERR(priv->vin_reg);
+ }
+
+ ret = regulator_enable(priv->vin_reg);
+ if (ret) {
+ dev_err(&client->dev, "enable regulator vin failed\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&client->dev, noa1305_reg_remove, priv);
+ if (ret) {
+ dev_err(&client->dev, "addition of devm action failed\n");
+ return ret;
+ }
+
+ i2c_set_clientdata(client, indio_dev);
+ priv->client = client;
+ priv->regmap = regmap;
+
+ ret = regmap_bulk_read(regmap, NOA1305_REG_DEVICE_ID_LSB, &data, 2);
+ if (ret < 0) {
+ dev_err(&client->dev, "ID reading failed: %d\n", ret);
+ return ret;
+ }
+
+ dev_id = le16_to_cpu(data);
+ if (dev_id != NOA1305_DEVICE_ID) {
+ dev_err(&client->dev, "Unknown device ID: 0x%x\n", dev_id);
+ return -ENODEV;
+ }
+
+ ret = regmap_write(regmap, NOA1305_REG_POWER_CONTROL,
+ NOA1305_POWER_CONTROL_ON);
+ if (ret < 0) {
+ dev_err(&client->dev, "Enabling power control failed\n");
+ return ret;
+ }
+
+ ret = regmap_write(regmap, NOA1305_REG_RESET, NOA1305_RESET_RESET);
+ if (ret < 0) {
+ dev_err(&client->dev, "Device reset failed\n");
+ return ret;
+ }
+
+ ret = regmap_write(regmap, NOA1305_REG_INTEGRATION_TIME,
+ NOA1305_INTEGR_TIME_800MS);
+ if (ret < 0) {
+ dev_err(&client->dev, "Setting integration time failed\n");
+ return ret;
+ }
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->info = &noa1305_info;
+ indio_dev->channels = noa1305_channels;
+ indio_dev->num_channels = ARRAY_SIZE(noa1305_channels);
+ indio_dev->name = NOA1305_DRIVER_NAME;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = devm_iio_device_register(&client->dev, indio_dev);
+ if (ret)
+ dev_err(&client->dev, "registering device failed\n");
+
+ return ret;
+}
+
+static const struct of_device_id noa1305_of_match[] = {
+ { .compatible = "onnn,noa1305" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, noa1305_of_match);
+
+static const struct i2c_device_id noa1305_ids[] = {
+ { "noa1305", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, noa1305_ids);
+
+static struct i2c_driver noa1305_driver = {
+ .driver = {
+ .name = NOA1305_DRIVER_NAME,
+ .of_match_table = noa1305_of_match,
+ },
+ .probe = noa1305_probe,
+ .id_table = noa1305_ids,
+};
+
+module_i2c_driver(noa1305_driver);
+
+MODULE_AUTHOR("Sergei Miroshnichenko <sergeimir@emcraft.com>");
+MODULE_AUTHOR("Martyn Welch <martyn.welch@collabora.com");
+MODULE_DESCRIPTION("ON Semiconductor NOA1305 ambient light sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/light/opt3001.c b/drivers/iio/light/opt3001.c
index 54d88b6..92004a2 100644
--- a/drivers/iio/light/opt3001.c
+++ b/drivers/iio/light/opt3001.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* opt3001.c - Texas Instruments OPT3001 Light Sensor
*
@@ -5,15 +6,6 @@
*
* Author: Andreas Dannenberg <dannenberg@ti.com>
* Based on previous work from: Felipe Balbi <balbi@ti.com>
- *
- * This program is free software: you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 of the License
- * as published by the Free Software Foundation.
- *
- * 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/bitops.h>
@@ -694,6 +686,7 @@
struct iio_dev *iio = _iio;
struct opt3001 *opt = iio_priv(iio);
int ret;
+ bool wake_result_ready_queue = false;
if (!opt->ok_to_ignore_lock)
mutex_lock(&opt->lock);
@@ -728,13 +721,16 @@
}
opt->result = ret;
opt->result_ready = true;
- wake_up(&opt->result_ready_queue);
+ wake_result_ready_queue = true;
}
out:
if (!opt->ok_to_ignore_lock)
mutex_unlock(&opt->lock);
+ if (wake_result_ready_queue)
+ wake_up(&opt->result_ready_queue);
+
return IRQ_HANDLED;
}
diff --git a/drivers/iio/light/pa12203001.c b/drivers/iio/light/pa12203001.c
index 30ea1a0..0295783 100644
--- a/drivers/iio/light/pa12203001.c
+++ b/drivers/iio/light/pa12203001.c
@@ -1,11 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 Intel Corporation
*
* Driver for TXC PA12203001 Proximity and Ambient Light Sensor.
*
- * 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.
* To do: Interrupt support.
*/
diff --git a/drivers/iio/light/rpr0521.c b/drivers/iio/light/rpr0521.c
index ffe9ce7..a0a7aea 100644
--- a/drivers/iio/light/rpr0521.c
+++ b/drivers/iio/light/rpr0521.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* RPR-0521 ROHM Ambient Light and Proximity Sensor
*
* Copyright (c) 2015, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for RPR-0521RS (7-bit I2C slave address 0x38).
*
* TODO: illuminance channel
diff --git a/drivers/iio/light/si1145.c b/drivers/iio/light/si1145.c
index 76f16f9..982bba0 100644
--- a/drivers/iio/light/si1145.c
+++ b/drivers/iio/light/si1145.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* si1145.c - Support for Silabs SI1132 and SI1141/2/3/5/6/7 combined ambient
* light, UV index and proximity sensors
@@ -5,10 +6,6 @@
* Copyright 2014-16 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
* Copyright 2016 Crestez Dan Leonard <leonard.crestez@intel.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* SI1132 (7-bit I2C slave address 0x60)
* SI1141/2/3 (7-bit I2C slave address 0x5a)
* SI1145/6/6 (7-bit I2C slave address 0x60)
@@ -1264,7 +1261,7 @@
return ret;
}
- ret = iio_trigger_register(trig);
+ ret = devm_iio_trigger_register(&client->dev, trig);
if (ret)
return ret;
@@ -1274,16 +1271,6 @@
return 0;
}
-static void si1145_remove_trigger(struct iio_dev *indio_dev)
-{
- struct si1145_data *data = iio_priv(indio_dev);
-
- if (data->trig) {
- iio_trigger_unregister(data->trig);
- data->trig = NULL;
- }
-}
-
static int si1145_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
@@ -1335,7 +1322,8 @@
if (ret < 0)
return ret;
- ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ ret = devm_iio_triggered_buffer_setup(&client->dev,
+ indio_dev, NULL,
si1145_trigger_handler, &si1145_buffer_setup_ops);
if (ret < 0)
return ret;
@@ -1343,23 +1331,12 @@
if (client->irq) {
ret = si1145_probe_trigger(indio_dev);
if (ret < 0)
- goto error_free_buffer;
+ return ret;
} else {
dev_info(&client->dev, "no irq, using polling\n");
}
- ret = iio_device_register(indio_dev);
- if (ret < 0)
- goto error_free_trigger;
-
- return 0;
-
-error_free_trigger:
- si1145_remove_trigger(indio_dev);
-error_free_buffer:
- iio_triggered_buffer_cleanup(indio_dev);
-
- return ret;
+ return devm_iio_device_register(&client->dev, indio_dev);
}
static const struct i2c_device_id si1145_ids[] = {
@@ -1374,23 +1351,11 @@
};
MODULE_DEVICE_TABLE(i2c, si1145_ids);
-static int si1145_remove(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
-
- iio_device_unregister(indio_dev);
- si1145_remove_trigger(indio_dev);
- iio_triggered_buffer_cleanup(indio_dev);
-
- return 0;
-}
-
static struct i2c_driver si1145_driver = {
.driver = {
.name = "si1145",
},
.probe = si1145_probe,
- .remove = si1145_remove,
.id_table = si1145_ids,
};
diff --git a/drivers/iio/light/st_uvis25.h b/drivers/iio/light/st_uvis25.h
index 5e970ab..78bc56a 100644
--- a/drivers/iio/light/st_uvis25.h
+++ b/drivers/iio/light/st_uvis25.h
@@ -1,11 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics uvis25 sensor driver
*
* Copyright 2017 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
- *
- * Licensed under the GPL-2.
*/
#ifndef ST_UVIS25_H
diff --git a/drivers/iio/light/st_uvis25_core.c b/drivers/iio/light/st_uvis25_core.c
index 3026358..d262c25 100644
--- a/drivers/iio/light/st_uvis25_core.c
+++ b/drivers/iio/light/st_uvis25_core.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics uvis25 sensor driver
*
* Copyright 2017 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/light/st_uvis25_i2c.c b/drivers/iio/light/st_uvis25_i2c.c
index afd6eb0..dacbac6 100644
--- a/drivers/iio/light/st_uvis25_i2c.c
+++ b/drivers/iio/light/st_uvis25_i2c.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics uvis25 i2c driver
*
* Copyright 2017 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/light/st_uvis25_spi.c b/drivers/iio/light/st_uvis25_spi.c
index cdfee5e..a9ceae4 100644
--- a/drivers/iio/light/st_uvis25_spi.c
+++ b/drivers/iio/light/st_uvis25_spi.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics uvis25 spi driver
*
* Copyright 2017 STMicroelectronics Inc.
*
* Lorenzo Bianconi <lorenzo.bianconi83@gmail.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
diff --git a/drivers/iio/light/stk3310.c b/drivers/iio/light/stk3310.c
index 6e2a169..185c24a 100644
--- a/drivers/iio/light/stk3310.c
+++ b/drivers/iio/light/stk3310.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* Sensortek STK3310/STK3311 Ambient Light and Proximity Sensor
*
* Copyright (c) 2015, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for STK3310/STK3311. 7-bit I2C address: 0x48.
*/
@@ -40,6 +37,7 @@
#define STK3310_CHIP_ID_VAL 0x13
#define STK3311_CHIP_ID_VAL 0x1D
+#define STK3335_CHIP_ID_VAL 0x51
#define STK3310_PSINT_EN 0x01
#define STK3310_PS_MAX_VAL 0xFFFF
@@ -454,7 +452,8 @@
return ret;
if (chipid != STK3310_CHIP_ID_VAL &&
- chipid != STK3311_CHIP_ID_VAL) {
+ chipid != STK3311_CHIP_ID_VAL &&
+ chipid != STK3335_CHIP_ID_VAL) {
dev_err(&client->dev, "invalid chip id: 0x%x\n", chipid);
return -ENODEV;
}
@@ -666,6 +665,7 @@
static const struct i2c_device_id stk3310_i2c_id[] = {
{"STK3310", 0},
{"STK3311", 0},
+ {"STK3335", 0},
{}
};
MODULE_DEVICE_TABLE(i2c, stk3310_i2c_id);
@@ -673,14 +673,24 @@
static const struct acpi_device_id stk3310_acpi_id[] = {
{"STK3310", 0},
{"STK3311", 0},
+ {"STK3335", 0},
{}
};
MODULE_DEVICE_TABLE(acpi, stk3310_acpi_id);
+static const struct of_device_id stk3310_of_match[] = {
+ { .compatible = "sensortek,stk3310", },
+ { .compatible = "sensortek,stk3311", },
+ { .compatible = "sensortek,stk3335", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, stk3310_of_match);
+
static struct i2c_driver stk3310_driver = {
.driver = {
.name = "stk3310",
+ .of_match_table = stk3310_of_match,
.pm = STK3310_PM_OPS,
.acpi_match_table = ACPI_PTR(stk3310_acpi_id),
},
diff --git a/drivers/iio/light/tcs3414.c b/drivers/iio/light/tcs3414.c
index 205e565..7c0291c 100644
--- a/drivers/iio/light/tcs3414.c
+++ b/drivers/iio/light/tcs3414.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tcs3414.c - Support for TAOS TCS3414 digital color sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Digital color sensor with 16-bit channels for red, green, blue, clear);
* 7-bit I2C slave address 0x39 (TCS3414) or 0x29, 0x49, 0x59 (TCS3413,
* TCS3415, TCS3416, resp.)
diff --git a/drivers/iio/light/tcs3472.c b/drivers/iio/light/tcs3472.c
index e7923b5..12ad344 100644
--- a/drivers/iio/light/tcs3472.c
+++ b/drivers/iio/light/tcs3472.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tcs3472.c - Support for TAOS TCS3472 color light-to-digital converter
*
* Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Color light sensor with 16-bit channels for red, green, blue, clear);
* 7-bit I2C slave address 0x39 (TCS34721, TCS34723) or 0x29 (TCS34725,
* TCS34727)
diff --git a/drivers/iio/light/tsl2563.c b/drivers/iio/light/tsl2563.c
index 6bbb0b1..d8c40a8 100644
--- a/drivers/iio/light/tsl2563.c
+++ b/drivers/iio/light/tsl2563.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* drivers/iio/light/tsl2563.c
*
@@ -8,20 +9,6 @@
*
* Converted to IIO driver
* Amit Kucheria <amit.kucheria@verdurent.com>
- *
- * 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.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
*/
#include <linux/module.h>
diff --git a/drivers/iio/light/tsl2583.c b/drivers/iio/light/tsl2583.c
index 4b5d998..a760d14 100644
--- a/drivers/iio/light/tsl2583.c
+++ b/drivers/iio/light/tsl2583.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Device driver for monitoring ambient light intensity (lux)
* within the TAOS tsl258x family of devices (tsl2580, tsl2581, tsl2583).
*
* Copyright (c) 2011, TAOS Corporation.
* Copyright (c) 2016-2017 Brian Masney <masneyb@onstation.org>
- *
- * 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/kernel.h>
diff --git a/drivers/iio/light/tsl2772.c b/drivers/iio/light/tsl2772.c
index df5b2a0..be37fcb 100644
--- a/drivers/iio/light/tsl2772.c
+++ b/drivers/iio/light/tsl2772.c
@@ -20,6 +20,7 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
#include <linux/platform_data/tsl2772.h>
+#include <linux/regulator/consumer.h>
/* Cal defs */
#define PROX_STAT_CAL 0
@@ -107,6 +108,11 @@
#define TSL2772_ALS_GAIN_TRIM_MIN 250
#define TSL2772_ALS_GAIN_TRIM_MAX 4000
+#define TSL2772_MAX_PROX_LEDS 2
+
+#define TSL2772_BOOT_MIN_SLEEP_TIME 10000
+#define TSL2772_BOOT_MAX_SLEEP_TIME 28000
+
/* Device family members */
enum {
tsl2571,
@@ -118,7 +124,8 @@
tsl2672,
tmd2672,
tsl2772,
- tmd2772
+ tmd2772,
+ apds9930,
};
enum {
@@ -127,6 +134,12 @@
TSL2772_CHIP_SUSPENDED = 2
};
+enum {
+ TSL2772_SUPPLY_VDD = 0,
+ TSL2772_SUPPLY_VDDIO = 1,
+ TSL2772_NUM_SUPPLIES = 2
+};
+
/* Per-device data */
struct tsl2772_als_info {
u16 als_ch0;
@@ -141,11 +154,20 @@
const struct iio_info *info;
};
+static const int tsl2772_led_currents[][2] = {
+ { 100000, TSL2772_100_mA },
+ { 50000, TSL2772_50_mA },
+ { 25000, TSL2772_25_mA },
+ { 13000, TSL2772_13_mA },
+ { 0, 0 }
+};
+
struct tsl2772_chip {
kernel_ulong_t id;
struct mutex prox_mutex;
struct mutex als_mutex;
struct i2c_client *client;
+ struct regulator_bulk_data supplies[TSL2772_NUM_SUPPLIES];
u16 prox_data;
struct tsl2772_als_info als_cur_info;
struct tsl2772_settings settings;
@@ -197,6 +219,12 @@
{ 0, 0 },
};
+static const struct tsl2772_lux apds9930_lux_table[TSL2772_DEF_LUX_TABLE_SZ] = {
+ { 52000, 96824 },
+ { 38792, 67132 },
+ { 0, 0 },
+};
+
static const struct tsl2772_lux *tsl2772_default_lux_table_group[] = {
[tsl2571] = tsl2x71_lux_table,
[tsl2671] = tsl2x71_lux_table,
@@ -208,6 +236,7 @@
[tmd2672] = tmd2x72_lux_table,
[tsl2772] = tsl2x72_lux_table,
[tmd2772] = tmd2x72_lux_table,
+ [apds9930] = apds9930_lux_table,
};
static const struct tsl2772_settings tsl2772_default_settings = {
@@ -258,6 +287,7 @@
[tmd2672] = { 0, 2730, 0, 2730, 0, 699000 },
[tsl2772] = { 0, 2730, 0, 2730, 0, 699000 },
[tmd2772] = { 0, 2730, 0, 2730, 0, 699000 },
+ [apds9930] = { 0, 2730, 0, 2730, 0, 699000 },
};
static int tsl2772_int_calibscale_avail[] = { 1, 8, 16, 120 };
@@ -283,7 +313,8 @@
[tsl2672] = PRX2,
[tmd2672] = PRX2,
[tsl2772] = ALSPRX2,
- [tmd2772] = ALSPRX2
+ [tmd2772] = ALSPRX2,
+ [apds9930] = ALSPRX2,
};
static int tsl2772_read_status(struct tsl2772_chip *chip)
@@ -497,6 +528,7 @@
case tmd2672:
case tsl2772:
case tmd2772:
+ case apds9930:
if (!(ret & TSL2772_STA_PRX_VALID)) {
ret = -EINVAL;
goto prox_poll_err;
@@ -515,6 +547,75 @@
return ret;
}
+static int tsl2772_read_prox_led_current(struct tsl2772_chip *chip)
+{
+ struct device_node *of_node = chip->client->dev.of_node;
+ int ret, tmp, i;
+
+ ret = of_property_read_u32(of_node, "led-max-microamp", &tmp);
+ if (ret < 0)
+ return ret;
+
+ for (i = 0; tsl2772_led_currents[i][0] != 0; i++) {
+ if (tmp == tsl2772_led_currents[i][0]) {
+ chip->settings.prox_power = tsl2772_led_currents[i][1];
+ return 0;
+ }
+ }
+
+ dev_err(&chip->client->dev, "Invalid value %d for led-max-microamp\n",
+ tmp);
+
+ return -EINVAL;
+
+}
+
+static int tsl2772_read_prox_diodes(struct tsl2772_chip *chip)
+{
+ struct device_node *of_node = chip->client->dev.of_node;
+ int i, ret, num_leds, prox_diode_mask;
+ u32 leds[TSL2772_MAX_PROX_LEDS];
+
+ ret = of_property_count_u32_elems(of_node, "amstaos,proximity-diodes");
+ if (ret < 0)
+ return ret;
+
+ num_leds = ret;
+ if (num_leds > TSL2772_MAX_PROX_LEDS)
+ num_leds = TSL2772_MAX_PROX_LEDS;
+
+ ret = of_property_read_u32_array(of_node, "amstaos,proximity-diodes",
+ leds, num_leds);
+ if (ret < 0) {
+ dev_err(&chip->client->dev,
+ "Invalid value for amstaos,proximity-diodes: %d.\n",
+ ret);
+ return ret;
+ }
+
+ prox_diode_mask = 0;
+ for (i = 0; i < num_leds; i++) {
+ if (leds[i] == 0)
+ prox_diode_mask |= TSL2772_DIODE0;
+ else if (leds[i] == 1)
+ prox_diode_mask |= TSL2772_DIODE1;
+ else {
+ dev_err(&chip->client->dev,
+ "Invalid value %d in amstaos,proximity-diodes.\n",
+ leds[i]);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static void tsl2772_parse_dt(struct tsl2772_chip *chip)
+{
+ tsl2772_read_prox_led_current(chip);
+ tsl2772_read_prox_diodes(chip);
+}
+
/**
* tsl2772_defaults() - Populates the device nominal operating parameters
* with those provided by a 'platform' data struct or
@@ -541,6 +642,8 @@
memcpy(chip->tsl2772_device_lux,
tsl2772_default_lux_table_group[chip->id],
TSL2772_DEFAULT_TABLE_BYTES);
+
+ tsl2772_parse_dt(chip);
}
/**
@@ -595,6 +698,13 @@
return ret;
}
+static void tsl2772_disable_regulators_action(void *_data)
+{
+ struct tsl2772_chip *chip = _data;
+
+ regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies);
+}
+
static int tsl2772_chip_on(struct iio_dev *indio_dev)
{
struct tsl2772_chip *chip = iio_priv(indio_dev);
@@ -716,6 +826,13 @@
return tsl2772_write_control_reg(chip, 0x00);
}
+static void tsl2772_chip_off_action(void *data)
+{
+ struct iio_dev *indio_dev = data;
+
+ tsl2772_chip_off(indio_dev);
+}
+
/**
* tsl2772_invoke_change - power cycle the device to implement the user
* parameters
@@ -1260,6 +1377,7 @@
case tmd2672:
case tsl2772:
case tmd2772:
+ case apds9930:
return (id & 0xf0) == SWORDFISH_ID;
}
@@ -1652,6 +1770,39 @@
chip->client = clientp;
i2c_set_clientdata(clientp, indio_dev);
+ chip->supplies[TSL2772_SUPPLY_VDD].supply = "vdd";
+ chip->supplies[TSL2772_SUPPLY_VDDIO].supply = "vddio";
+
+ ret = devm_regulator_bulk_get(&clientp->dev,
+ ARRAY_SIZE(chip->supplies),
+ chip->supplies);
+ if (ret < 0) {
+ if (ret != -EPROBE_DEFER)
+ dev_err(&clientp->dev,
+ "Failed to get regulators: %d\n",
+ ret);
+
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies);
+ if (ret < 0) {
+ dev_err(&clientp->dev, "Failed to enable regulators: %d\n",
+ ret);
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(&clientp->dev,
+ tsl2772_disable_regulators_action,
+ chip);
+ if (ret < 0) {
+ dev_err(&clientp->dev, "Failed to setup regulator cleanup action %d\n",
+ ret);
+ return ret;
+ }
+
+ usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME);
+
ret = i2c_smbus_read_byte_data(chip->client,
TSL2772_CMD_REG | TSL2772_CHIPID);
if (ret < 0)
@@ -1711,42 +1862,42 @@
if (ret < 0)
return ret;
- ret = iio_device_register(indio_dev);
- if (ret) {
- tsl2772_chip_off(indio_dev);
- dev_err(&clientp->dev,
- "%s: iio registration failed\n", __func__);
+ ret = devm_add_action_or_reset(&clientp->dev,
+ tsl2772_chip_off_action,
+ indio_dev);
+ if (ret < 0)
return ret;
- }
- return 0;
+ return devm_iio_device_register(&clientp->dev, indio_dev);
}
static int tsl2772_suspend(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2772_chip *chip = iio_priv(indio_dev);
+ int ret;
- return tsl2772_chip_off(indio_dev);
+ ret = tsl2772_chip_off(indio_dev);
+ regulator_bulk_disable(ARRAY_SIZE(chip->supplies), chip->supplies);
+
+ return ret;
}
static int tsl2772_resume(struct device *dev)
{
struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct tsl2772_chip *chip = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(chip->supplies), chip->supplies);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(TSL2772_BOOT_MIN_SLEEP_TIME, TSL2772_BOOT_MAX_SLEEP_TIME);
return tsl2772_chip_on(indio_dev);
}
-static int tsl2772_remove(struct i2c_client *client)
-{
- struct iio_dev *indio_dev = i2c_get_clientdata(client);
-
- tsl2772_chip_off(indio_dev);
-
- iio_device_unregister(indio_dev);
-
- return 0;
-}
-
static const struct i2c_device_id tsl2772_idtable[] = {
{ "tsl2571", tsl2571 },
{ "tsl2671", tsl2671 },
@@ -1758,6 +1909,7 @@
{ "tmd2672", tmd2672 },
{ "tsl2772", tsl2772 },
{ "tmd2772", tmd2772 },
+ { "apds9930", apds9930},
{}
};
@@ -1774,6 +1926,7 @@
{ .compatible = "amstaos,tmd2672" },
{ .compatible = "amstaos,tsl2772" },
{ .compatible = "amstaos,tmd2772" },
+ { .compatible = "avago,apds9930" },
{}
};
MODULE_DEVICE_TABLE(of, tsl2772_of_match);
@@ -1791,7 +1944,6 @@
},
.id_table = tsl2772_idtable,
.probe = tsl2772_probe,
- .remove = tsl2772_remove,
};
module_i2c_driver(tsl2772_driver);
diff --git a/drivers/iio/light/tsl4531.c b/drivers/iio/light/tsl4531.c
index 06171cb..0dfc664 100644
--- a/drivers/iio/light/tsl4531.c
+++ b/drivers/iio/light/tsl4531.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tsl4531.c - Support for TAOS TSL4531 ambient light sensor
*
* Copyright 2013 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for the TSL4531x family
* TSL45311/TSL45313: 7-bit I2C slave address 0x39
* TSL45315/TSL45317: 7-bit I2C slave address 0x29
diff --git a/drivers/iio/light/us5182d.c b/drivers/iio/light/us5182d.c
index 68e5294..b995f21 100644
--- a/drivers/iio/light/us5182d.c
+++ b/drivers/iio/light/us5182d.c
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2015 Intel Corporation
*
* Driver for UPISEMI us5182d Proximity and Ambient Light Sensor.
*
- * 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.
- *
- * This program is distributed in the hope 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.
- *
* To do: Interrupt support.
*/
diff --git a/drivers/iio/light/vcnl4000.c b/drivers/iio/light/vcnl4000.c
index 04fd0d4..16dacea 100644
--- a/drivers/iio/light/vcnl4000.c
+++ b/drivers/iio/light/vcnl4000.c
@@ -1,22 +1,21 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
- * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4200 combined ambient
+ * vcnl4000.c - Support for Vishay VCNL4000/4010/4020/4040/4200 combined ambient
* light and proximity sensor
*
* Copyright 2012 Peter Meerwald <pmeerw@pmeerw.net>
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
+ * Copyright 2019 Pursim SPC
*
* IIO driver for:
* VCNL4000/10/20 (7-bit I2C slave address 0x13)
+ * VCNL4040 (7-bit I2C slave address 0x60)
* VCNL4200 (7-bit I2C slave address 0x51)
*
* TODO:
* allow to adjust IR current
* proximity threshold and event handling
* periodic ALS/proximity measurement (VCNL4010/20)
- * interrupts (VCNL4010/20, VCNL4200)
+ * interrupts (VCNL4010/20/40, VCNL4200)
*/
#include <linux/module.h>
@@ -30,6 +29,7 @@
#define VCNL4000_DRV_NAME "vcnl4000"
#define VCNL4000_PROD_ID 0x01
#define VCNL4010_PROD_ID 0x02 /* for VCNL4020, VCNL4010 */
+#define VCNL4040_PROD_ID 0x86
#define VCNL4200_PROD_ID 0x58
#define VCNL4000_COMMAND 0x80 /* Command register */
@@ -49,6 +49,8 @@
#define VCNL4200_AL_DATA 0x09 /* Ambient light data */
#define VCNL4200_DEV_ID 0x0e /* Device ID, slave address and version */
+#define VCNL4040_DEV_ID 0x0c /* Device ID and version */
+
/* Bit masks for COMMAND register */
#define VCNL4000_AL_RDY BIT(6) /* ALS data ready? */
#define VCNL4000_PS_RDY BIT(5) /* proximity data ready? */
@@ -58,6 +60,7 @@
enum vcnl4000_device_ids {
VCNL4000,
VCNL4010,
+ VCNL4040,
VCNL4200,
};
@@ -90,6 +93,7 @@
{ "vcnl4000", VCNL4000 },
{ "vcnl4010", VCNL4010 },
{ "vcnl4020", VCNL4010 },
+ { "vcnl4040", VCNL4040 },
{ "vcnl4200", VCNL4200 },
{ }
};
@@ -128,31 +132,53 @@
static int vcnl4200_init(struct vcnl4000_data *data)
{
- int ret;
+ int ret, id;
ret = i2c_smbus_read_word_data(data->client, VCNL4200_DEV_ID);
if (ret < 0)
return ret;
- if ((ret & 0xff) != VCNL4200_PROD_ID)
- return -ENODEV;
+ id = ret & 0xff;
+
+ if (id != VCNL4200_PROD_ID) {
+ ret = i2c_smbus_read_word_data(data->client, VCNL4040_DEV_ID);
+ if (ret < 0)
+ return ret;
+
+ id = ret & 0xff;
+
+ if (id != VCNL4040_PROD_ID)
+ return -ENODEV;
+ }
+
+ dev_dbg(&data->client->dev, "device id 0x%x", id);
data->rev = (ret >> 8) & 0xf;
/* Set defaults and enable both channels */
- ret = i2c_smbus_write_byte_data(data->client, VCNL4200_AL_CONF, 0x00);
+ ret = i2c_smbus_write_word_data(data->client, VCNL4200_AL_CONF, 0);
if (ret < 0)
return ret;
- ret = i2c_smbus_write_byte_data(data->client, VCNL4200_PS_CONF1, 0x00);
+ ret = i2c_smbus_write_word_data(data->client, VCNL4200_PS_CONF1, 0);
if (ret < 0)
return ret;
data->al_scale = 24000;
data->vcnl4200_al.reg = VCNL4200_AL_DATA;
data->vcnl4200_ps.reg = VCNL4200_PS_DATA;
- /* Integration time is 50ms, but the experiments show 54ms in total. */
- data->vcnl4200_al.sampling_rate = ktime_set(0, 54000 * 1000);
- data->vcnl4200_ps.sampling_rate = ktime_set(0, 4200 * 1000);
+ switch (id) {
+ case VCNL4200_PROD_ID:
+ /* Integration time is 50ms, but the experiments */
+ /* show 54ms in total. */
+ data->vcnl4200_al.sampling_rate = ktime_set(0, 54000 * 1000);
+ data->vcnl4200_ps.sampling_rate = ktime_set(0, 4200 * 1000);
+ break;
+ case VCNL4040_PROD_ID:
+ /* Integration time is 80ms, add 10ms. */
+ data->vcnl4200_al.sampling_rate = ktime_set(0, 100000 * 1000);
+ data->vcnl4200_ps.sampling_rate = ktime_set(0, 100000 * 1000);
+ break;
+ }
data->vcnl4200_al.last_measurement = ktime_set(0, 0);
data->vcnl4200_ps.last_measurement = ktime_set(0, 0);
mutex_init(&data->vcnl4200_al.lock);
@@ -271,6 +297,12 @@
.measure_light = vcnl4000_measure_light,
.measure_proximity = vcnl4000_measure_proximity,
},
+ [VCNL4040] = {
+ .prod = "VCNL4040",
+ .init = vcnl4200_init,
+ .measure_light = vcnl4200_measure_light,
+ .measure_proximity = vcnl4200_measure_proximity,
+ },
[VCNL4200] = {
.prod = "VCNL4200",
.init = vcnl4200_init,
@@ -363,9 +395,35 @@
return devm_iio_device_register(&client->dev, indio_dev);
}
+static const struct of_device_id vcnl_4000_of_match[] = {
+ {
+ .compatible = "vishay,vcnl4000",
+ .data = (void *)VCNL4000,
+ },
+ {
+ .compatible = "vishay,vcnl4010",
+ .data = (void *)VCNL4010,
+ },
+ {
+ .compatible = "vishay,vcnl4020",
+ .data = (void *)VCNL4010,
+ },
+ {
+ .compatible = "vishay,vcnl4040",
+ .data = (void *)VCNL4040,
+ },
+ {
+ .compatible = "vishay,vcnl4200",
+ .data = (void *)VCNL4200,
+ },
+ {},
+};
+MODULE_DEVICE_TABLE(of, vcnl_4000_of_match);
+
static struct i2c_driver vcnl4000_driver = {
.driver = {
.name = VCNL4000_DRV_NAME,
+ .of_match_table = vcnl_4000_of_match,
},
.probe = vcnl4000_probe,
.id_table = vcnl4000_id,
diff --git a/drivers/iio/light/vcnl4035.c b/drivers/iio/light/vcnl4035.c
new file mode 100644
index 0000000..cca4db3
--- /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, ®);
+ 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/light/veml6070.c b/drivers/iio/light/veml6070.c
index f4bf3c5..0be553a 100644
--- a/drivers/iio/light/veml6070.c
+++ b/drivers/iio/light/veml6070.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* veml6070.c - Support for Vishay VEML6070 UV A light sensor
*
* Copyright 2016 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for VEML6070 (7-bit I2C slave addresses 0x38 and 0x39)
*
* TODO: integration time, ACK signal
@@ -161,10 +158,10 @@
indio_dev->name = VEML6070_DRV_NAME;
indio_dev->modes = INDIO_DIRECT_MODE;
- data->client2 = i2c_new_dummy(client->adapter, VEML6070_ADDR_DATA_LSB);
- if (!data->client2) {
+ data->client2 = i2c_new_dummy_device(client->adapter, VEML6070_ADDR_DATA_LSB);
+ if (IS_ERR(data->client2)) {
dev_err(&client->dev, "i2c device for second chip address failed\n");
- return -ENODEV;
+ return PTR_ERR(data->client2);
}
data->config = VEML6070_IT_10 | VEML6070_COMMAND_RSRVD |
diff --git a/drivers/iio/light/vl6180.c b/drivers/iio/light/vl6180.c
index 192c77e..d9533a7 100644
--- a/drivers/iio/light/vl6180.c
+++ b/drivers/iio/light/vl6180.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* vl6180.c - Support for STMicroelectronics VL6180 ALS, range and proximity
* sensor
@@ -5,10 +6,6 @@
* Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
* Copyright 2017 Manivannan Sadhasivam <manivannanece23@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for VL6180 (7-bit I2C slave address 0x29)
*
* Range: 0 to 100mm
diff --git a/drivers/iio/light/zopt2201.c b/drivers/iio/light/zopt2201.c
index 041ac9e..5f54f39 100644
--- a/drivers/iio/light/zopt2201.c
+++ b/drivers/iio/light/zopt2201.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* zopt2201.c - Support for IDT ZOPT2201 ambient light and UV B sensor
*
* Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Datasheet: https://www.idt.com/document/dst/zopt2201-datasheet
* 7-bit I2C slave addresses 0x53 (default) or 0x52 (programmed)
*
diff --git a/drivers/iio/magnetometer/Kconfig b/drivers/iio/magnetometer/Kconfig
index ed9d776..1697a8c 100644
--- a/drivers/iio/magnetometer/Kconfig
+++ b/drivers/iio/magnetometer/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Magnetometer sensors
#
@@ -175,4 +176,33 @@
- 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 664b2f8..ba1bc34 100644
--- a/drivers/iio/magnetometer/Makefile
+++ b/drivers/iio/magnetometer/Makefile
@@ -24,3 +24,7 @@
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/ak8974.c b/drivers/iio/magnetometer/ak8974.c
index 93be1f4..fc7e910 100644
--- a/drivers/iio/magnetometer/ak8974.c
+++ b/drivers/iio/magnetometer/ak8974.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Driver for the Asahi Kasei EMD Corporation AK8974
* and Aichi Steel AMI305 magnetometer chips.
@@ -733,9 +734,8 @@
ak8974->i2c = i2c;
mutex_init(&ak8974->lock);
- ret = of_iio_read_mount_matrix(&i2c->dev,
- "mount-matrix",
- &ak8974->orientation);
+ ret = iio_read_mount_matrix(&i2c->dev, "mount-matrix",
+ &ak8974->orientation);
if (ret)
return ret;
diff --git a/drivers/iio/magnetometer/ak8975.c b/drivers/iio/magnetometer/ak8975.c
index 42a827a..893bec5 100644
--- a/drivers/iio/magnetometer/ak8975.c
+++ b/drivers/iio/magnetometer/ak8975.c
@@ -1,23 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* A sensor driver for the magnetometer AK8975.
*
* Magnetic compass sensor driver for monitoring magnetic flux information.
*
* Copyright (c) 2010, NVIDIA Corporation.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <linux/module.h>
@@ -746,12 +733,14 @@
ak8975_get_mount_matrix(const struct iio_dev *indio_dev,
const struct iio_chan_spec *chan)
{
- return &((struct ak8975_data *)iio_priv(indio_dev))->orientation;
+ struct ak8975_data *data = iio_priv(indio_dev);
+
+ return &data->orientation;
}
static const struct iio_chan_spec_ext_info ak8975_ext_info[] = {
IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, ak8975_get_mount_matrix),
- { },
+ { }
};
#define AK8975_CHANNEL(axis, index) \
@@ -790,8 +779,9 @@
{"INVN6500", AK8963},
{"AK009911", AK09911},
{"AK09911", AK09911},
+ {"AKM9911", AK09911},
{"AK09912", AK09912},
- { },
+ { }
};
MODULE_DEVICE_TABLE(acpi, ak_acpi_match);
#endif
@@ -910,9 +900,8 @@
data->eoc_irq = 0;
if (!pdata) {
- err = of_iio_read_mount_matrix(&client->dev,
- "mount-matrix",
- &data->orientation);
+ err = iio_read_mount_matrix(&client->dev, "mount-matrix",
+ &data->orientation);
if (err)
return err;
} else
diff --git a/drivers/iio/magnetometer/bmc150_magn.c b/drivers/iio/magnetometer/bmc150_magn.c
index d91cb84..d4de167 100644
--- a/drivers/iio/magnetometer/bmc150_magn.c
+++ b/drivers/iio/magnetometer/bmc150_magn.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Bosch BMC150 three-axis magnetic field sensor driver
*
@@ -6,15 +7,6 @@
* This code is based on bmm050_api.c authored by contact@bosch.sensortec.com:
*
* (C) Copyright 2011~2014 Bosch Sensortec GmbH All Rights Reserved
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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>
@@ -143,6 +135,7 @@
*/
struct mutex mutex;
struct regmap *regmap;
+ struct iio_mount_matrix orientation;
/* 4 x 32 bits for x, y z, 4 bytes align, 64 bits timestamp */
s32 buffer[6];
struct iio_trigger *dready_trig;
@@ -612,6 +605,20 @@
return len;
}
+static const struct iio_mount_matrix *
+bmc150_magn_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct bmc150_magn_data *data = iio_priv(indio_dev);
+
+ return &data->orientation;
+}
+
+static const struct iio_chan_spec_ext_info bmc150_magn_ext_info[] = {
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, bmc150_magn_get_mount_matrix),
+ { }
+};
+
static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(bmc150_magn_show_samp_freq_avail);
static struct attribute *bmc150_magn_attributes[] = {
@@ -638,6 +645,7 @@
.storagebits = 32, \
.endianness = IIO_LE \
}, \
+ .ext_info = bmc150_magn_ext_info, \
}
static const struct iio_chan_spec bmc150_magn_channels[] = {
@@ -861,6 +869,11 @@
data->irq = irq;
data->dev = dev;
+ ret = iio_read_mount_matrix(dev, "mount-matrix",
+ &data->orientation);
+ if (ret)
+ return ret;
+
if (!name && ACPI_HANDLE(dev))
name = bmc150_magn_match_acpi_device(dev);
diff --git a/drivers/iio/magnetometer/bmc150_magn_i2c.c b/drivers/iio/magnetometer/bmc150_magn_i2c.c
index 57e40dd..fb45b63 100644
--- a/drivers/iio/magnetometer/bmc150_magn_i2c.c
+++ b/drivers/iio/magnetometer/bmc150_magn_i2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 3-axis magnetometer driver supporting following I2C Bosch-Sensortec chips:
* - BMC150
@@ -5,15 +6,6 @@
* - BMM150
*
* Copyright (c) 2016, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/device.h>
#include <linux/mod_devicetable.h>
diff --git a/drivers/iio/magnetometer/bmc150_magn_spi.c b/drivers/iio/magnetometer/bmc150_magn_spi.c
index 7d4152d..ed9be04 100644
--- a/drivers/iio/magnetometer/bmc150_magn_spi.c
+++ b/drivers/iio/magnetometer/bmc150_magn_spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* 3-axis magnetometer driver support following SPI Bosch-Sensortec chips:
* - BMC150
@@ -5,10 +6,6 @@
* - BMM150
*
* Copyright (c) 2016, Intel Corporation.
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
*/
#include <linux/module.h>
#include <linux/mod_devicetable.h>
diff --git a/drivers/iio/magnetometer/hid-sensor-magn-3d.c b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
index f3c0d41..25e60b2 100644
--- a/drivers/iio/magnetometer/hid-sensor-magn-3d.c
+++ b/drivers/iio/magnetometer/hid-sensor-magn-3d.c
@@ -1,20 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2012, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
- *
*/
#include <linux/device.h>
#include <linux/platform_device.h>
diff --git a/drivers/iio/magnetometer/hmc5843.h b/drivers/iio/magnetometer/hmc5843.h
index 76a5d74..b0dee87 100644
--- a/drivers/iio/magnetometer/hmc5843.h
+++ b/drivers/iio/magnetometer/hmc5843.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Header file for hmc5843 driver
*
* Split from hmc5843.c
* Copyright (C) Josef Gajdusek <atx@atx.name>
- *
- * 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.
*/
#ifndef HMC5843_CORE_H
@@ -31,7 +28,7 @@
};
/**
- * struct hcm5843_data - device specific data
+ * struct hmc5843_data - device specific data
* @dev: actual device
* @lock: update and read regmap data
* @regmap: hardware access register maps
@@ -43,6 +40,7 @@
struct mutex lock;
struct regmap *regmap;
const struct hmc5843_chip_info *variant;
+ struct iio_mount_matrix orientation;
__be16 buffer[8];
};
diff --git a/drivers/iio/magnetometer/hmc5843_core.c b/drivers/iio/magnetometer/hmc5843_core.c
index ada142f..c44a429 100644
--- a/drivers/iio/magnetometer/hmc5843_core.c
+++ b/drivers/iio/magnetometer/hmc5843_core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Device driver for the the HMC5843 multi-chip module designed
* for low field magnetic sensing.
@@ -8,16 +9,6 @@
* Acknowledgment: Jonathan Cameron <jic23@kernel.org> for valuable inputs.
* Support for HMC5883 and HMC5883L by Peter Meerwald <pmeerw@pmeerw.net>.
* Split to multiple files by Josef Gajdusek <atx@atx.name> - 2014
- *
- * 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>
@@ -237,6 +228,15 @@
return hmc5843_set_meas_conf(data, meas_conf);
}
+static const struct iio_mount_matrix *
+hmc5843_get_mount_matrix(const struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan)
+{
+ struct hmc5843_data *data = iio_priv(indio_dev);
+
+ return &data->orientation;
+}
+
static const struct iio_enum hmc5843_meas_conf_enum = {
.items = hmc5843_meas_conf_modes,
.num_items = ARRAY_SIZE(hmc5843_meas_conf_modes),
@@ -247,7 +247,8 @@
static const struct iio_chan_spec_ext_info hmc5843_ext_info[] = {
IIO_ENUM("meas_conf", true, &hmc5843_meas_conf_enum),
IIO_ENUM_AVAILABLE("meas_conf", &hmc5843_meas_conf_enum),
- { },
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, hmc5843_get_mount_matrix),
+ { }
};
static const struct iio_enum hmc5983_meas_conf_enum = {
@@ -260,7 +261,8 @@
static const struct iio_chan_spec_ext_info hmc5983_ext_info[] = {
IIO_ENUM("meas_conf", true, &hmc5983_meas_conf_enum),
IIO_ENUM_AVAILABLE("meas_conf", &hmc5983_meas_conf_enum),
- { },
+ IIO_MOUNT_MATRIX(IIO_SHARED_BY_DIR, hmc5843_get_mount_matrix),
+ { }
};
static
@@ -635,6 +637,11 @@
data->variant = &hmc5843_chip_info_tbl[id];
mutex_init(&data->lock);
+ ret = iio_read_mount_matrix(dev, "mount-matrix",
+ &data->orientation);
+ if (ret)
+ return ret;
+
indio_dev->dev.parent = dev;
indio_dev->name = name;
indio_dev->info = &hmc5843_info;
diff --git a/drivers/iio/magnetometer/hmc5843_i2c.c b/drivers/iio/magnetometer/hmc5843_i2c.c
index 3de7f44..67fe657 100644
--- a/drivers/iio/magnetometer/hmc5843_i2c.c
+++ b/drivers/iio/magnetometer/hmc5843_i2c.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* i2c driver for hmc5843/5843/5883/5883l/5983
*
* Split from hmc5843.c
* Copyright (C) Josef Gajdusek <atx@atx.name>
- *
- * 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/module.h>
@@ -58,8 +55,13 @@
static int hmc5843_i2c_probe(struct i2c_client *cli,
const struct i2c_device_id *id)
{
+ struct regmap *regmap = devm_regmap_init_i2c(cli,
+ &hmc5843_i2c_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
return hmc5843_common_probe(&cli->dev,
- devm_regmap_init_i2c(cli, &hmc5843_i2c_regmap_config),
+ regmap,
id->driver_data, id->name);
}
diff --git a/drivers/iio/magnetometer/hmc5843_spi.c b/drivers/iio/magnetometer/hmc5843_spi.c
index 535f03a..d827554 100644
--- a/drivers/iio/magnetometer/hmc5843_spi.c
+++ b/drivers/iio/magnetometer/hmc5843_spi.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SPI driver for hmc5983
*
* Copyright (C) Josef Gajdusek <atx@atx.name>
- *
- * 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/module.h>
@@ -58,6 +55,7 @@
static int hmc5843_spi_probe(struct spi_device *spi)
{
int ret;
+ struct regmap *regmap;
const struct spi_device_id *id = spi_get_device_id(spi);
spi->mode = SPI_MODE_3;
@@ -67,8 +65,12 @@
if (ret)
return ret;
+ regmap = devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config);
+ if (IS_ERR(regmap))
+ return PTR_ERR(regmap);
+
return hmc5843_common_probe(&spi->dev,
- devm_regmap_init_spi(spi, &hmc5843_spi_regmap_config),
+ regmap,
id->driver_data, id->name);
}
diff --git a/drivers/iio/magnetometer/mag3110.c b/drivers/iio/magnetometer/mag3110.c
index f063355..fb16cfd 100644
--- a/drivers/iio/magnetometer/mag3110.c
+++ b/drivers/iio/magnetometer/mag3110.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* mag3110.c - Support for Freescale MAG3110 magnetometer sensor
*
* Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* (7-bit I2C slave address 0x0e)
*
* TODO: irq, user offset, oversampling, continuous mode
@@ -20,6 +17,7 @@
#include <linux/iio/buffer.h>
#include <linux/iio/triggered_buffer.h>
#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
#define MAG3110_STATUS 0x00
#define MAG3110_OUT_X 0x01 /* MSB first */
@@ -56,6 +54,8 @@
struct mutex lock;
u8 ctrl_reg1;
int sleep_val;
+ struct regulator *vdd_reg;
+ struct regulator *vddio_reg;
};
static int mag3110_request(struct mag3110_data *data)
@@ -469,17 +469,50 @@
struct iio_dev *indio_dev;
int ret;
- ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
- if (ret < 0)
- return ret;
- if (ret != MAG3110_DEVICE_ID)
- return -ENODEV;
-
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
if (!indio_dev)
return -ENOMEM;
data = iio_priv(indio_dev);
+
+ data->vdd_reg = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(data->vdd_reg)) {
+ if (PTR_ERR(data->vdd_reg) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_err(&client->dev, "failed to get VDD regulator!\n");
+ return PTR_ERR(data->vdd_reg);
+ }
+
+ data->vddio_reg = devm_regulator_get(&client->dev, "vddio");
+ if (IS_ERR(data->vddio_reg)) {
+ if (PTR_ERR(data->vddio_reg) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ dev_err(&client->dev, "failed to get VDDIO regulator!\n");
+ return PTR_ERR(data->vddio_reg);
+ }
+
+ ret = regulator_enable(data->vdd_reg);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable VDD regulator!\n");
+ return ret;
+ }
+
+ ret = regulator_enable(data->vddio_reg);
+ if (ret) {
+ dev_err(&client->dev, "failed to enable VDDIO regulator!\n");
+ goto disable_regulator_vdd;
+ }
+
+ ret = i2c_smbus_read_byte_data(client, MAG3110_WHO_AM_I);
+ if (ret < 0)
+ goto disable_regulators;
+ if (ret != MAG3110_DEVICE_ID) {
+ ret = -ENODEV;
+ goto disable_regulators;
+ }
+
data->client = client;
mutex_init(&data->lock);
@@ -499,7 +532,7 @@
ret = mag3110_change_config(data, MAG3110_CTRL_REG1, data->ctrl_reg1);
if (ret < 0)
- return ret;
+ goto disable_regulators;
ret = i2c_smbus_write_byte_data(client, MAG3110_CTRL_REG2,
MAG3110_CTRL_AUTO_MRST_EN);
@@ -520,16 +553,24 @@
iio_triggered_buffer_cleanup(indio_dev);
standby_on_error:
mag3110_standby(iio_priv(indio_dev));
+disable_regulators:
+ regulator_disable(data->vddio_reg);
+disable_regulator_vdd:
+ regulator_disable(data->vdd_reg);
+
return ret;
}
static int mag3110_remove(struct i2c_client *client)
{
struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct mag3110_data *data = iio_priv(indio_dev);
iio_device_unregister(indio_dev);
iio_triggered_buffer_cleanup(indio_dev);
mag3110_standby(iio_priv(indio_dev));
+ regulator_disable(data->vddio_reg);
+ regulator_disable(data->vdd_reg);
return 0;
}
@@ -537,14 +578,48 @@
#ifdef CONFIG_PM_SLEEP
static int mag3110_suspend(struct device *dev)
{
- return mag3110_standby(iio_priv(i2c_get_clientdata(
+ struct mag3110_data *data = iio_priv(i2c_get_clientdata(
+ to_i2c_client(dev)));
+ int ret;
+
+ ret = mag3110_standby(iio_priv(i2c_get_clientdata(
to_i2c_client(dev))));
+ if (ret)
+ return ret;
+
+ ret = regulator_disable(data->vddio_reg);
+ if (ret) {
+ dev_err(dev, "failed to disable VDDIO regulator\n");
+ return ret;
+ }
+
+ ret = regulator_disable(data->vdd_reg);
+ if (ret) {
+ dev_err(dev, "failed to disable VDD regulator\n");
+ return ret;
+ }
+
+ return 0;
}
static int mag3110_resume(struct device *dev)
{
struct mag3110_data *data = iio_priv(i2c_get_clientdata(
to_i2c_client(dev)));
+ int ret;
+
+ ret = regulator_enable(data->vdd_reg);
+ if (ret) {
+ dev_err(dev, "failed to enable VDD regulator\n");
+ return ret;
+ }
+
+ ret = regulator_enable(data->vddio_reg);
+ if (ret) {
+ dev_err(dev, "failed to enable VDDIO regulator\n");
+ regulator_disable(data->vdd_reg);
+ return ret;
+ }
return i2c_smbus_write_byte_data(data->client, MAG3110_CTRL_REG1,
data->ctrl_reg1);
diff --git a/drivers/iio/magnetometer/mmc35240.c b/drivers/iio/magnetometer/mmc35240.c
index 6b640c6..425cdd0 100644
--- a/drivers/iio/magnetometer/mmc35240.c
+++ b/drivers/iio/magnetometer/mmc35240.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* MMC35240 - MEMSIC 3-axis Magnetic Sensor
*
* Copyright (c) 2015, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* IIO driver for MMC35240 (7-bit I2C slave address 0x30).
*
* TODO: offset, ACPI, continuous measurement mode, PM
@@ -56,7 +53,7 @@
#define MMC35240_CTRL1_BW_SHIFT 0
#define MMC35240_WAIT_CHARGE_PUMP 50000 /* us */
-#define MMC53240_WAIT_SET_RESET 1000 /* us */
+#define MMC35240_WAIT_SET_RESET 1000 /* us */
/*
* Memsic OTP process code piece is put here for reference:
@@ -228,7 +225,7 @@
ret = mmc35240_hw_set(data, true);
if (ret < 0)
return ret;
- usleep_range(MMC53240_WAIT_SET_RESET, MMC53240_WAIT_SET_RESET + 1);
+ usleep_range(MMC35240_WAIT_SET_RESET, MMC35240_WAIT_SET_RESET + 1);
ret = mmc35240_hw_set(data, false);
if (ret < 0)
diff --git a/drivers/iio/magnetometer/rm3100-core.c b/drivers/iio/magnetometer/rm3100-core.c
new file mode 100644
index 0000000..7c20918
--- /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 0000000..1ac622c
--- /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 0000000..65d5eb9
--- /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 0000000..c350821
--- /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 8fe51ce..204b285 100644
--- a/drivers/iio/magnetometer/st_magn.h
+++ b/drivers/iio/magnetometer/st_magn.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics magnetometers driver
*
@@ -5,7 +6,6 @@
*
* Denis Ciocca <denis.ciocca@st.com>
* v. 1.0.0
- * Licensed under the GPL-2.
*/
#ifndef ST_MAGN_H
@@ -20,7 +20,9 @@
#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"
+const struct st_sensor_settings *st_magn_get_settings(const char *name);
int st_magn_common_probe(struct iio_dev *indio_dev);
void st_magn_common_remove(struct iio_dev *indio_dev);
diff --git a/drivers/iio/magnetometer/st_magn_buffer.c b/drivers/iio/magnetometer/st_magn_buffer.c
index 37ab305..bb425c1 100644
--- a/drivers/iio/magnetometer/st_magn_buffer.c
+++ b/drivers/iio/magnetometer/st_magn_buffer.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics magnetometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -33,39 +32,32 @@
static int st_magn_buffer_postenable(struct iio_dev *indio_dev)
{
int err;
- struct st_sensor_data *mdata = iio_priv(indio_dev);
-
- mdata->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
- if (mdata->buffer_data == NULL) {
- err = -ENOMEM;
- goto allocate_memory_error;
- }
err = iio_triggered_buffer_postenable(indio_dev);
if (err < 0)
- goto st_magn_buffer_postenable_error;
+ return err;
- return st_sensors_set_enable(indio_dev, true);
+ err = st_sensors_set_enable(indio_dev, true);
+ if (err < 0)
+ goto st_magn_buffer_predisable;
-st_magn_buffer_postenable_error:
- kfree(mdata->buffer_data);
-allocate_memory_error:
+ return 0;
+
+st_magn_buffer_predisable:
+ iio_triggered_buffer_predisable(indio_dev);
return err;
}
static int st_magn_buffer_predisable(struct iio_dev *indio_dev)
{
- int err;
- struct st_sensor_data *mdata = iio_priv(indio_dev);
+ int err, err2;
err = st_sensors_set_enable(indio_dev, false);
- if (err < 0)
- goto st_magn_buffer_predisable_error;
- err = iio_triggered_buffer_predisable(indio_dev);
+ err2 = iio_triggered_buffer_predisable(indio_dev);
+ if (!err)
+ err = err2;
-st_magn_buffer_predisable_error:
- kfree(mdata->buffer_data);
return err;
}
diff --git a/drivers/iio/magnetometer/st_magn_core.c b/drivers/iio/magnetometer/st_magn_core.c
index 72f6d13..a3a268e 100644
--- a/drivers/iio/magnetometer/st_magn_core.c
+++ b/drivers/iio/magnetometer/st_magn_core.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics magnetometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -13,7 +12,6 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/types.h>
-#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
@@ -29,9 +27,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 +265,7 @@
.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 +314,10 @@
},
},
},
+ .bdu = {
+ .addr = 0x24,
+ .mask = 0x40,
+ },
.drdy_irq = {
/* drdy line is routed drdy pin */
.stat_drdy = {
@@ -466,28 +469,41 @@
#define ST_MAGN_TRIGGER_OPS NULL
#endif
+/*
+ * st_magn_get_settings() - get sensor settings from device name
+ * @name: device name buffer reference.
+ *
+ * Return: valid reference on success, NULL otherwise.
+ */
+const struct st_sensor_settings *st_magn_get_settings(const char *name)
+{
+ int index = st_sensors_get_settings_index(name,
+ st_magn_sensors_settings,
+ ARRAY_SIZE(st_magn_sensors_settings));
+ if (index < 0)
+ return NULL;
+
+ return &st_magn_sensors_settings[index];
+}
+EXPORT_SYMBOL(st_magn_get_settings);
+
int st_magn_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *mdata = iio_priv(indio_dev);
- int irq = mdata->get_irq_data_ready(indio_dev);
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &magn_info;
- mutex_init(&mdata->tb.buf_lock);
err = st_sensors_power_enable(indio_dev);
if (err)
return err;
- err = st_sensors_check_device_support(indio_dev,
- ARRAY_SIZE(st_magn_sensors_settings),
- st_magn_sensors_settings);
+ err = st_sensors_verify_id(indio_dev);
if (err < 0)
goto st_magn_power_off;
mdata->num_data_channels = ST_MAGN_NUMBER_DATA_CHANNELS;
- mdata->multiread_bit = mdata->sensor_settings->multi_read_bit;
indio_dev->channels = mdata->sensor_settings->ch;
indio_dev->num_channels = ST_SENSORS_NUMBER_ALL_CHANNELS;
@@ -503,7 +519,7 @@
if (err < 0)
goto st_magn_power_off;
- if (irq > 0) {
+ if (mdata->irq > 0) {
err = st_sensors_allocate_trigger(indio_dev,
ST_MAGN_TRIGGER_OPS);
if (err < 0)
@@ -520,7 +536,7 @@
return 0;
st_magn_device_register_error:
- if (irq > 0)
+ if (mdata->irq > 0)
st_sensors_deallocate_trigger(indio_dev);
st_magn_probe_trigger_error:
st_magn_deallocate_ring(indio_dev);
@@ -538,7 +554,7 @@
st_sensors_power_disable(indio_dev);
iio_device_unregister(indio_dev);
- if (mdata->get_irq_data_ready(indio_dev) > 0)
+ if (mdata->irq > 0)
st_sensors_deallocate_trigger(indio_dev);
st_magn_deallocate_ring(indio_dev);
diff --git a/drivers/iio/magnetometer/st_magn_i2c.c b/drivers/iio/magnetometer/st_magn_i2c.c
index feaa28c..fdba480 100644
--- a/drivers/iio/magnetometer/st_magn_i2c.c
+++ b/drivers/iio/magnetometer/st_magn_i2c.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics magnetometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -44,6 +43,10 @@
.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);
@@ -52,21 +55,33 @@
#endif
static int st_magn_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
- struct iio_dev *indio_dev;
+ const struct st_sensor_settings *settings;
struct st_sensor_data *mdata;
+ struct iio_dev *indio_dev;
int err;
+ st_sensors_of_name_probe(&client->dev, st_magn_of_match,
+ client->name, sizeof(client->name));
+
+ settings = st_magn_get_settings(client->name);
+ if (!settings) {
+ dev_err(&client->dev, "device name %s not recognized.\n",
+ client->name);
+ return -ENODEV;
+ }
+
indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mdata));
if (!indio_dev)
return -ENOMEM;
mdata = iio_priv(indio_dev);
- st_sensors_of_name_probe(&client->dev, st_magn_of_match,
- client->name, sizeof(client->name));
+ mdata->sensor_settings = (struct st_sensor_settings *)settings;
- st_sensors_i2c_configure(indio_dev, client, mdata);
+ err = st_sensors_i2c_configure(indio_dev, client);
+ if (err < 0)
+ return err;
err = st_magn_common_probe(indio_dev);
if (err < 0)
@@ -90,6 +105,7 @@
{ 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 7b7cd08..fbf909b 100644
--- a/drivers/iio/magnetometer/st_magn_spi.c
+++ b/drivers/iio/magnetometer/st_magn_spi.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics magnetometers driver
*
* Copyright 2012-2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -23,6 +22,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 +38,10 @@
.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);
@@ -46,19 +51,31 @@
static int st_magn_spi_probe(struct spi_device *spi)
{
- struct iio_dev *indio_dev;
+ const struct st_sensor_settings *settings;
struct st_sensor_data *mdata;
+ struct iio_dev *indio_dev;
int err;
+ st_sensors_of_name_probe(&spi->dev, st_magn_of_match,
+ spi->modalias, sizeof(spi->modalias));
+
+ settings = st_magn_get_settings(spi->modalias);
+ if (!settings) {
+ dev_err(&spi->dev, "device name %s not recognized.\n",
+ spi->modalias);
+ return -ENODEV;
+ }
+
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*mdata));
if (!indio_dev)
return -ENOMEM;
mdata = iio_priv(indio_dev);
+ mdata->sensor_settings = (struct st_sensor_settings *)settings;
- st_sensors_of_name_probe(&spi->dev, st_magn_of_match,
- spi->modalias, sizeof(spi->modalias));
- st_sensors_spi_configure(indio_dev, spi, mdata);
+ err = st_sensors_spi_configure(indio_dev, spi);
+ if (err < 0)
+ return err;
err = st_magn_common_probe(indio_dev);
if (err < 0)
@@ -79,6 +96,7 @@
{ 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/multiplexer/Kconfig b/drivers/iio/multiplexer/Kconfig
index 735a7b0..a1e1332 100644
--- a/drivers/iio/multiplexer/Kconfig
+++ b/drivers/iio/multiplexer/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Multiplexer drivers
#
diff --git a/drivers/iio/multiplexer/Makefile b/drivers/iio/multiplexer/Makefile
index 68be3c4..f069ab7 100644
--- a/drivers/iio/multiplexer/Makefile
+++ b/drivers/iio/multiplexer/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for industrial I/O multiplexer drivers
#
diff --git a/drivers/iio/multiplexer/iio-mux.c b/drivers/iio/multiplexer/iio-mux.c
index e1f44ce..0422ef5 100644
--- a/drivers/iio/multiplexer/iio-mux.c
+++ b/drivers/iio/multiplexer/iio-mux.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* IIO multiplexer driver
*
* Copyright (C) 2017 Axentia Technologies AB
*
* Author: Peter Rosin <peda@axentia.se>
- *
- * 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/err.h>
diff --git a/drivers/iio/orientation/Kconfig b/drivers/iio/orientation/Kconfig
index e3aa1e5..a505583 100644
--- a/drivers/iio/orientation/Kconfig
+++ b/drivers/iio/orientation/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Inclinometer sensors
#
diff --git a/drivers/iio/orientation/Makefile b/drivers/iio/orientation/Makefile
index 4734dab..7800ed2 100644
--- a/drivers/iio/orientation/Makefile
+++ b/drivers/iio/orientation/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for industrial I/O Inclinometer sensor drivers
#
diff --git a/drivers/iio/orientation/hid-sensor-incl-3d.c b/drivers/iio/orientation/hid-sensor-incl-3d.c
index bdc5e45..00af687 100644
--- a/drivers/iio/orientation/hid-sensor-incl-3d.c
+++ b/drivers/iio/orientation/hid-sensor-incl-3d.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2013, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc.
- *
*/
#include <linux/device.h>
diff --git a/drivers/iio/orientation/hid-sensor-rotation.c b/drivers/iio/orientation/hid-sensor-rotation.c
index a69db20..64ae7d0 100644
--- a/drivers/iio/orientation/hid-sensor-rotation.c
+++ b/drivers/iio/orientation/hid-sensor-rotation.c
@@ -1,15 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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/device.h>
diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig
index 79ec2eb..4cac017 100644
--- a/drivers/iio/potentiometer/Kconfig
+++ b/drivers/iio/potentiometer/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Potentiometer drivers
#
@@ -25,27 +26,38 @@
To compile this driver as a module, choose M here: the
module will be called ds1803.
-config MAX5481
- tristate "Maxim MAX5481-MAX5484 Digital Potentiometer driver"
- depends on SPI
- help
- Say yes here to build support for the Maxim
- MAX5481, MAX5482, MAX5483, MAX5484 digital potentiometer
- chips.
+config MAX5432
+ tristate "Maxim MAX5432-MAX5435 Digital Potentiometer driver"
+ depends on I2C
+ help
+ Say yes here to build support for the Maxim
+ MAX5432, MAX5433, MAX5434 and MAX5435 digital
+ potentiometer chips.
- To compile this driver as a module, choose M here: the
- module will be called max5481.
+ To compile this driver as a module, choose M here: the
+ module will be called max5432.
+
+config MAX5481
+ tristate "Maxim MAX5481-MAX5484 Digital Potentiometer driver"
+ depends on SPI
+ help
+ Say yes here to build support for the Maxim
+ MAX5481, MAX5482, MAX5483, MAX5484 digital potentiometer
+ chips.
+
+ To compile this driver as a module, choose M here: the
+ module will be called max5481.
config MAX5487
- tristate "Maxim MAX5487/MAX5488/MAX5489 Digital Potentiometer driver"
- depends on SPI
- help
- Say yes here to build support for the Maxim
- MAX5487, MAX5488, MAX5489 digital potentiometer
- chips.
+ tristate "Maxim MAX5487/MAX5488/MAX5489 Digital Potentiometer driver"
+ depends on SPI
+ help
+ Say yes here to build support for the Maxim
+ MAX5487, MAX5488, MAX5489 digital potentiometer
+ chips.
- To compile this driver as a module, choose M here: the
- module will be called max5487.
+ To compile this driver as a module, choose M here: the
+ module will be called max5487.
config MCP4018
tristate "Microchip MCP4017/18/19 Digital Potentiometer driver"
@@ -90,6 +102,18 @@
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 4af6578..091adf3 100644
--- a/drivers/iio/potentiometer/Makefile
+++ b/drivers/iio/potentiometer/Makefile
@@ -6,9 +6,11 @@
# When adding new entries keep the list in alphabetical order
obj-$(CONFIG_AD5272) += ad5272.o
obj-$(CONFIG_DS1803) += ds1803.o
+obj-$(CONFIG_MAX5432) += max5432.o
obj-$(CONFIG_MAX5481) += max5481.o
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/ds1803.c b/drivers/iio/potentiometer/ds1803.c
index 6bf12c9..d0de782 100644
--- a/drivers/iio/potentiometer/ds1803.c
+++ b/drivers/iio/potentiometer/ds1803.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Maxim Integrated DS1803 digital potentiometer driver
* Copyright (c) 2016 Slawomir Stepien
@@ -6,10 +7,6 @@
*
* DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address
* ds1803 2 256 10, 50, 100 0101xxx
- *
- * 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/err.h>
diff --git a/drivers/iio/potentiometer/max5432.c b/drivers/iio/potentiometer/max5432.c
new file mode 100644
index 0000000..641b182
--- /dev/null
+++ b/drivers/iio/potentiometer/max5432.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Maxim Integrated MAX5432-MAX5435 digital potentiometer driver
+ * Copyright (C) 2019 Martin Kaiser <martin@kaiser.cx>
+ *
+ * Datasheet:
+ * https://datasheets.maximintegrated.com/en/ds/MAX5432-MAX5435.pdf
+ */
+
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+
+/* All chip variants have 32 wiper positions. */
+#define MAX5432_MAX_POS 31
+
+#define MAX5432_OHM_50K (50 * 1000)
+#define MAX5432_OHM_100K (100 * 1000)
+
+/* Update the volatile (currently active) setting. */
+#define MAX5432_CMD_VREG 0x11
+
+struct max5432_data {
+ struct i2c_client *client;
+ unsigned long ohm;
+};
+
+static const struct iio_chan_spec max5432_channels[] = {
+ {
+ .type = IIO_RESISTANCE,
+ .indexed = 1,
+ .output = 1,
+ .channel = 0,
+ .address = MAX5432_CMD_VREG,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ }
+};
+
+static int max5432_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max5432_data *data = iio_priv(indio_dev);
+
+ if (mask != IIO_CHAN_INFO_SCALE)
+ return -EINVAL;
+
+ if (unlikely(data->ohm > INT_MAX))
+ return -ERANGE;
+
+ *val = data->ohm;
+ *val2 = MAX5432_MAX_POS;
+
+ return IIO_VAL_FRACTIONAL;
+}
+
+static int max5432_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct max5432_data *data = iio_priv(indio_dev);
+ u8 data_byte;
+
+ if (mask != IIO_CHAN_INFO_RAW)
+ return -EINVAL;
+
+ if (val < 0 || val > MAX5432_MAX_POS)
+ return -EINVAL;
+
+ if (val2 != 0)
+ return -EINVAL;
+
+ /* Wiper position is in bits D7-D3. (D2-D0 are don't care bits.) */
+ data_byte = val << 3;
+ return i2c_smbus_write_byte_data(data->client, chan->address,
+ data_byte);
+}
+
+static const struct iio_info max5432_info = {
+ .read_raw = max5432_read_raw,
+ .write_raw = max5432_write_raw,
+};
+
+static int max5432_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct max5432_data *data;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(struct max5432_data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ data->ohm = (unsigned long)of_device_get_match_data(dev);
+
+ indio_dev->dev.parent = dev;
+ indio_dev->info = &max5432_info;
+ indio_dev->channels = max5432_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max5432_channels);
+ indio_dev->name = client->name;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id max5432_dt_ids[] = {
+ { .compatible = "maxim,max5432", .data = (void *)MAX5432_OHM_50K },
+ { .compatible = "maxim,max5433", .data = (void *)MAX5432_OHM_100K },
+ { .compatible = "maxim,max5434", .data = (void *)MAX5432_OHM_50K },
+ { .compatible = "maxim,max5435", .data = (void *)MAX5432_OHM_100K },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, max5432_dt_ids);
+
+static struct i2c_driver max5432_driver = {
+ .driver = {
+ .name = "max5432",
+ .of_match_table = of_match_ptr(max5432_dt_ids),
+ },
+ .probe = max5432_probe,
+};
+
+module_i2c_driver(max5432_driver);
+
+MODULE_AUTHOR("Martin Kaiser <martin@kaiser.cx>");
+MODULE_DESCRIPTION("max5432-max5435 digital potentiometers");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/potentiometer/max5481.c b/drivers/iio/potentiometer/max5481.c
index ffe2761..732375b 100644
--- a/drivers/iio/potentiometer/max5481.c
+++ b/drivers/iio/potentiometer/max5481.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Maxim Integrated MAX5481-MAX5484 digital potentiometer driver
* Copyright 2016 Rockwell Collins
*
* Datasheet:
* http://datasheets.maximintegrated.com/en/ds/MAX5481-MAX5484.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>
@@ -137,7 +133,6 @@
struct iio_dev *indio_dev;
struct max5481_data *data;
const struct spi_device_id *id = spi_get_device_id(spi);
- const struct of_device_id *match;
int ret;
indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
@@ -149,10 +144,8 @@
data->spi = spi;
- match = of_match_device(of_match_ptr(max5481_match), &spi->dev);
- if (match)
- data->cfg = of_device_get_match_data(&spi->dev);
- else
+ data->cfg = of_device_get_match_data(&spi->dev);
+ if (!data->cfg)
data->cfg = &max5481_cfg[id->driver_data];
indio_dev->name = id->name;
diff --git a/drivers/iio/potentiometer/max5487.c b/drivers/iio/potentiometer/max5487.c
index 5042d3e..68ff806 100644
--- a/drivers/iio/potentiometer/max5487.c
+++ b/drivers/iio/potentiometer/max5487.c
@@ -1,12 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* max5487.c - Support for MAX5487, MAX5488, MAX5489 digital potentiometers
*
* Copyright (C) 2016 Cristina-Gabriela Moraru <cristina.moraru09@gmail.com>
- *
- * 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/module.h>
#include <linux/spi/spi.h>
diff --git a/drivers/iio/potentiometer/mcp4018.c b/drivers/iio/potentiometer/mcp4018.c
index 320a7c9..62151b2 100644
--- a/drivers/iio/potentiometer/mcp4018.c
+++ b/drivers/iio/potentiometer/mcp4018.c
@@ -147,7 +147,6 @@
struct device *dev = &client->dev;
struct mcp4018_data *data;
struct iio_dev *indio_dev;
- const struct of_device_id *match;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_BYTE)) {
@@ -162,10 +161,8 @@
i2c_set_clientdata(client, indio_dev);
data->client = client;
- match = of_match_device(of_match_ptr(mcp4018_of_match), dev);
- if (match)
- data->cfg = of_device_get_match_data(dev);
- else
+ data->cfg = of_device_get_match_data(dev);
+ if (!data->cfg)
data->cfg = &mcp4018_cfg[i2c_match_id(mcp4018_id, client)->driver_data];
indio_dev->dev.parent = dev;
@@ -190,4 +187,4 @@
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_DESCRIPTION("MCP4018 digital potentiometer");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/potentiometer/mcp41010.c b/drivers/iio/potentiometer/mcp41010.c
new file mode 100644
index 0000000..2368b39
--- /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 b3e30db..98df91e 100644
--- a/drivers/iio/potentiometer/mcp4131.c
+++ b/drivers/iio/potentiometer/mcp4131.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Industrial I/O driver for Microchip digital potentiometers
*
@@ -23,10 +24,6 @@
* mcp4252 2 257 5, 10, 50, 100
* mcp4261 2 257 5, 10, 50, 100
* mcp4262 2 257 5, 10, 50, 100
- *
- * 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.
*/
/*
@@ -42,6 +39,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 +241,7 @@
{
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 +252,11 @@
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 +275,6 @@
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 +407,6 @@
{}
};
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/mcp4531.c b/drivers/iio/potentiometer/mcp4531.c
index df894af..d71a22d 100644
--- a/drivers/iio/potentiometer/mcp4531.c
+++ b/drivers/iio/potentiometer/mcp4531.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* Industrial I/O driver for Microchip digital potentiometers
* Copyright (c) 2015 Axentia Technologies AB
@@ -22,10 +23,6 @@
* mcp4652 2 257 5, 10, 50, 100 01011xx
* mcp4661 2 257 5, 10, 50, 100 0101xxx
* mcp4662 2 257 5, 10, 50, 100 01011xx
- *
- * 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/module.h>
@@ -360,7 +357,6 @@
struct device *dev = &client->dev;
struct mcp4531_data *data;
struct iio_dev *indio_dev;
- const struct of_device_id *match;
if (!i2c_check_functionality(client->adapter,
I2C_FUNC_SMBUS_WORD_DATA)) {
@@ -375,10 +371,8 @@
i2c_set_clientdata(client, indio_dev);
data->client = client;
- match = of_match_device(of_match_ptr(mcp4531_of_match), dev);
- if (match)
- data->cfg = of_device_get_match_data(dev);
- else
+ data->cfg = of_device_get_match_data(dev);
+ if (!data->cfg)
data->cfg = &mcp4531_cfg[i2c_match_id(mcp4531_id, client)->driver_data];
indio_dev->dev.parent = dev;
@@ -403,4 +397,4 @@
MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
MODULE_DESCRIPTION("MCP4531 digital potentiometer");
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c
index ca1cce5..a0a07e4 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 @@
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 @@
.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 @@
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 @@
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 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 @@
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 @@
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/potentiostat/Kconfig b/drivers/iio/potentiostat/Kconfig
index 1e3baf2..72501bf 100644
--- a/drivers/iio/potentiostat/Kconfig
+++ b/drivers/iio/potentiostat/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Potentiostat drivers
#
diff --git a/drivers/iio/potentiostat/Makefile b/drivers/iio/potentiostat/Makefile
index 64d315e..be78b46 100644
--- a/drivers/iio/potentiostat/Makefile
+++ b/drivers/iio/potentiostat/Makefile
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for industrial I/O potentiostat drivers
#
diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c
index 90e895a..a0e5f53 100644
--- a/drivers/iio/potentiostat/lmp91000.c
+++ b/drivers/iio/potentiostat/lmp91000.c
@@ -113,7 +113,7 @@
return -EINVAL;
/* delay till first temperature reading is complete */
- if ((state != channel) && (channel == LMP91000_REG_MODECN_TEMP))
+ if (state != channel && channel == LMP91000_REG_MODECN_TEMP)
usleep_range(3000, 4000);
data->chan_select = channel != LMP91000_REG_MODECN_3LEAD;
@@ -211,12 +211,11 @@
ret = of_property_read_u32(np, "ti,tia-gain-ohm", &val);
if (ret) {
- if (of_property_read_bool(np, "ti,external-tia-resistor"))
- val = 0;
- else {
- dev_err(dev, "no ti,tia-gain-ohm defined");
+ if (!of_property_read_bool(np, "ti,external-tia-resistor")) {
+ dev_err(dev, "no ti,tia-gain-ohm defined and external resistor not specified\n");
return ret;
}
+ val = 0;
}
ret = -EINVAL;
@@ -255,8 +254,8 @@
regmap_write(data->regmap, LMP91000_REG_LOCK, 0);
regmap_write(data->regmap, LMP91000_REG_TIACN, reg);
- regmap_write(data->regmap, LMP91000_REG_REFCN, LMP91000_REG_REFCN_EXT_REF
- | LMP91000_REG_REFCN_50_ZERO);
+ regmap_write(data->regmap, LMP91000_REG_REFCN,
+ LMP91000_REG_REFCN_EXT_REF | LMP91000_REG_REFCN_50_ZERO);
regmap_write(data->regmap, LMP91000_REG_LOCK, 1);
return 0;
@@ -276,7 +275,6 @@
static const struct iio_trigger_ops lmp91000_trigger_ops = {
};
-
static int lmp91000_buffer_preenable(struct iio_dev *indio_dev)
{
struct lmp91000_data *data = iio_priv(indio_dev);
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
index eaa7cfc..ba420e4 100644
--- a/drivers/iio/pressure/Kconfig
+++ b/drivers/iio/pressure/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Pressure drivers
#
@@ -52,6 +53,17 @@
To compile this driver as a module, choose M here: the module
will be called cros_ec_baro.
+config DPS310
+ tristate "Infineon DPS310 pressure and temperature sensor"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Support for the Infineon DPS310 digital barometric pressure sensor.
+ It can be accessed over I2C bus.
+
+ This driver can also be built as a module. If so, the module will be
+ called dps310.
+
config HID_SENSOR_PRESS
depends on HID_SENSOR_HUB
select IIO_BUFFER
@@ -165,7 +177,7 @@
select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
help
Say yes here to build support for STMicroelectronics pressure
- sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB.
+ sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB, LPS22HH.
This driver can also be built as a module. If so, these modules
will be created:
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
index c2058d7..d8f5ace 100644
--- a/drivers/iio/pressure/Makefile
+++ b/drivers/iio/pressure/Makefile
@@ -9,6 +9,7 @@
bmp280-objs := bmp280-core.o bmp280-regmap.o
obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
+obj-$(CONFIG_DPS310) += dps310.o
obj-$(CONFIG_IIO_CROS_EC_BARO) += cros_ec_baro.o
obj-$(CONFIG_HID_SENSOR_PRESS) += hid-sensor-press.o
obj-$(CONFIG_HP03) += hp03.o
diff --git a/drivers/iio/pressure/abp060mg.c b/drivers/iio/pressure/abp060mg.c
index 46a220c..267aad8 100644
--- a/drivers/iio/pressure/abp060mg.c
+++ b/drivers/iio/pressure/abp060mg.c
@@ -1,15 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2016 - Marcin Malagowski <mrc@bourne.st>
- *
- * 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/delay.h>
#include <linux/device.h>
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
index fe87d27..8d0f15f 100644
--- a/drivers/iio/pressure/bmp280-core.c
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
* Copyright (c) 2012 Bosch Sensortec GmbH
@@ -7,10 +8,6 @@
*
* Driver for Bosch Sensortec BMP180 and BMP280 digital pressure sensor.
*
- * 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.
- *
* Datasheet:
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP180-DS000-121.pdf
* https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BMP280-DS001-12.pdf
@@ -164,6 +161,9 @@
return ret;
}
+ /* Toss the temperature calibration data into the entropy pool */
+ add_device_randomness(t_buf, sizeof(t_buf));
+
calib->T1 = le16_to_cpu(t_buf[T1]);
calib->T2 = le16_to_cpu(t_buf[T2]);
calib->T3 = le16_to_cpu(t_buf[T3]);
@@ -177,6 +177,9 @@
return ret;
}
+ /* Toss the pressure calibration data into the entropy pool */
+ add_device_randomness(p_buf, sizeof(p_buf));
+
calib->P1 = le16_to_cpu(p_buf[P1]);
calib->P2 = le16_to_cpu(p_buf[P2]);
calib->P3 = le16_to_cpu(p_buf[P3]);
diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c
index 03742b1..acd9a37 100644
--- a/drivers/iio/pressure/bmp280-i2c.c
+++ b/drivers/iio/pressure/bmp280-i2c.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/acpi.h>
diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c
index 17bc955..9d57b7a 100644
--- a/drivers/iio/pressure/bmp280-spi.c
+++ b/drivers/iio/pressure/bmp280-spi.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* SPI interface for the BMP280 driver
*
diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
index 87c07af..2354302 100644
--- a/drivers/iio/pressure/cros_ec_baro.c
+++ b/drivers/iio/pressure/cros_ec_baro.c
@@ -1,19 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* cros_ec_baro - Driver for barometer sensor behind CrosEC.
*
* Copyright (C) 2017 Google, Inc
- *
- * This software is licensed under the terms of the GNU General Public
- * License version 2, as published by the Free Software Foundation, and
- * may be copied, distributed, and modified under those terms.
- *
- * 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/delay.h>
#include <linux/device.h>
#include <linux/iio/buffer.h>
#include <linux/iio/common/cros_ec_sensors_core.h>
@@ -24,9 +15,10 @@
#include <linux/iio/trigger_consumer.h>
#include <linux/kernel.h>
#include <linux/mfd/cros_ec.h>
-#include <linux/mfd/cros_ec_commands.h>
#include <linux/module.h>
#include <linux/slab.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
#include <linux/platform_device.h>
/*
@@ -48,26 +40,29 @@
{
struct cros_ec_baro_state *st = iio_priv(indio_dev);
u16 data = 0;
- int ret = IIO_VAL_INT;
+ int ret;
int idx = chan->scan_index;
mutex_lock(&st->core.cmd_lock);
switch (mask) {
case IIO_CHAN_INFO_RAW:
- if (cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
- (s16 *)&data) < 0)
- ret = -EIO;
+ ret = cros_ec_sensors_read_cmd(indio_dev, 1 << idx,
+ (s16 *)&data);
+ if (ret)
+ break;
+
*val = data;
+ ret = IIO_VAL_INT;
break;
case IIO_CHAN_INFO_SCALE:
st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
st->core.param.sensor_range.data = EC_MOTION_SENSE_NO_VALUE;
- if (cros_ec_motion_send_host_cmd(&st->core, 0)) {
- ret = -EIO;
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ if (ret)
break;
- }
+
*val = st->core.resp->sensor_range.ret;
/* scale * in_pressure_raw --> kPa */
@@ -161,8 +156,6 @@
channel->ext_info = cros_ec_sensors_ext_info;
channel->scan_type.sign = 'u';
- state->core.calib[0] = 0;
-
/* Sensor specific */
switch (state->core.type) {
case MOTIONSENSE_TYPE_BARO:
diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c
new file mode 100644
index 0000000..2c1943b
--- /dev/null
+++ b/drivers/iio/pressure/dps310.c
@@ -0,0 +1,827 @@
+// SPDX-License-Identifier: GPL-2.0+
+// Copyright IBM Corp 2019
+/*
+ * The DPS310 is a barometric pressure and temperature sensor.
+ * Currently only reading a single temperature is supported by
+ * this driver.
+ *
+ * https://www.infineon.com/dgdl/?fileId=5546d462576f34750157750826c42242
+ *
+ * Temperature calculation:
+ * c0 * 0.5 + c1 * T_raw / kT °C
+ *
+ * TODO:
+ * - Optionally support the FIFO
+ */
+
+#include <linux/i2c.h>
+#include <linux/limits.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+#define DPS310_DEV_NAME "dps310"
+
+#define DPS310_PRS_B0 0x00
+#define DPS310_PRS_B1 0x01
+#define DPS310_PRS_B2 0x02
+#define DPS310_TMP_B0 0x03
+#define DPS310_TMP_B1 0x04
+#define DPS310_TMP_B2 0x05
+#define DPS310_PRS_CFG 0x06
+#define DPS310_PRS_RATE_BITS GENMASK(6, 4)
+#define DPS310_PRS_PRC_BITS GENMASK(3, 0)
+#define DPS310_TMP_CFG 0x07
+#define DPS310_TMP_RATE_BITS GENMASK(6, 4)
+#define DPS310_TMP_PRC_BITS GENMASK(3, 0)
+#define DPS310_TMP_EXT BIT(7)
+#define DPS310_MEAS_CFG 0x08
+#define DPS310_MEAS_CTRL_BITS GENMASK(2, 0)
+#define DPS310_PRS_EN BIT(0)
+#define DPS310_TEMP_EN BIT(1)
+#define DPS310_BACKGROUND BIT(2)
+#define DPS310_PRS_RDY BIT(4)
+#define DPS310_TMP_RDY BIT(5)
+#define DPS310_SENSOR_RDY BIT(6)
+#define DPS310_COEF_RDY BIT(7)
+#define DPS310_CFG_REG 0x09
+#define DPS310_INT_HL BIT(7)
+#define DPS310_TMP_SHIFT_EN BIT(3)
+#define DPS310_PRS_SHIFT_EN BIT(4)
+#define DPS310_FIFO_EN BIT(5)
+#define DPS310_SPI_EN BIT(6)
+#define DPS310_RESET 0x0c
+#define DPS310_RESET_MAGIC 0x09
+#define DPS310_COEF_BASE 0x10
+
+/* Make sure sleep time is <= 20ms for usleep_range */
+#define DPS310_POLL_SLEEP_US(t) min(20000, (t) / 8)
+/* Silently handle error in rate value here */
+#define DPS310_POLL_TIMEOUT_US(rc) ((rc) <= 0 ? 1000000 : 1000000 / (rc))
+
+#define DPS310_PRS_BASE DPS310_PRS_B0
+#define DPS310_TMP_BASE DPS310_TMP_B0
+
+/*
+ * These values (defined in the spec) indicate how to scale the raw register
+ * values for each level of precision available.
+ */
+static const int scale_factors[] = {
+ 524288,
+ 1572864,
+ 3670016,
+ 7864320,
+ 253952,
+ 516096,
+ 1040384,
+ 2088960,
+};
+
+struct dps310_data {
+ struct i2c_client *client;
+ struct regmap *regmap;
+ struct mutex lock; /* Lock for sequential HW access functions */
+
+ s32 c0, c1;
+ s32 c00, c10, c20, c30, c01, c11, c21;
+ s32 pressure_raw;
+ s32 temp_raw;
+};
+
+static const struct iio_chan_spec dps310_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+};
+
+/* To be called after checking the COEF_RDY bit in MEAS_CFG */
+static int dps310_get_coefs(struct dps310_data *data)
+{
+ int rc;
+ u8 coef[18];
+ u32 c0, c1;
+ u32 c00, c10, c20, c30, c01, c11, c21;
+
+ /* Read all sensor calibration coefficients from the COEF registers. */
+ rc = regmap_bulk_read(data->regmap, DPS310_COEF_BASE, coef,
+ sizeof(coef));
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Calculate temperature calibration coefficients c0 and c1. The
+ * numbers are 12-bit 2's complement numbers.
+ */
+ c0 = (coef[0] << 4) | (coef[1] >> 4);
+ data->c0 = sign_extend32(c0, 11);
+
+ c1 = ((coef[1] & GENMASK(3, 0)) << 8) | coef[2];
+ data->c1 = sign_extend32(c1, 11);
+
+ /*
+ * Calculate pressure calibration coefficients. c00 and c10 are 20 bit
+ * 2's complement numbers, while the rest are 16 bit 2's complement
+ * numbers.
+ */
+ c00 = (coef[3] << 12) | (coef[4] << 4) | (coef[5] >> 4);
+ data->c00 = sign_extend32(c00, 19);
+
+ c10 = ((coef[5] & GENMASK(3, 0)) << 16) | (coef[6] << 8) | coef[7];
+ data->c10 = sign_extend32(c10, 19);
+
+ c01 = (coef[8] << 8) | coef[9];
+ data->c01 = sign_extend32(c01, 15);
+
+ c11 = (coef[10] << 8) | coef[11];
+ data->c11 = sign_extend32(c11, 15);
+
+ c20 = (coef[12] << 8) | coef[13];
+ data->c20 = sign_extend32(c20, 15);
+
+ c21 = (coef[14] << 8) | coef[15];
+ data->c21 = sign_extend32(c21, 15);
+
+ c30 = (coef[16] << 8) | coef[17];
+ data->c30 = sign_extend32(c30, 15);
+
+ return 0;
+}
+
+static int dps310_get_pres_precision(struct dps310_data *data)
+{
+ int rc;
+ int val;
+
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
+ if (rc < 0)
+ return rc;
+
+ return BIT(val & GENMASK(2, 0));
+}
+
+static int dps310_get_temp_precision(struct dps310_data *data)
+{
+ int rc;
+ int val;
+
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Scale factor is bottom 4 bits of the register, but 1111 is
+ * reserved so just grab bottom three
+ */
+ return BIT(val & GENMASK(2, 0));
+}
+
+/* Called with lock held */
+static int dps310_set_pres_precision(struct dps310_data *data, int val)
+{
+ int rc;
+ u8 shift_en;
+
+ if (val < 0 || val > 128)
+ return -EINVAL;
+
+ shift_en = val >= 16 ? DPS310_PRS_SHIFT_EN : 0;
+ rc = regmap_write_bits(data->regmap, DPS310_CFG_REG,
+ DPS310_PRS_SHIFT_EN, shift_en);
+ if (rc)
+ return rc;
+
+ return regmap_update_bits(data->regmap, DPS310_PRS_CFG,
+ DPS310_PRS_PRC_BITS, ilog2(val));
+}
+
+/* Called with lock held */
+static int dps310_set_temp_precision(struct dps310_data *data, int val)
+{
+ int rc;
+ u8 shift_en;
+
+ if (val < 0 || val > 128)
+ return -EINVAL;
+
+ shift_en = val >= 16 ? DPS310_TMP_SHIFT_EN : 0;
+ rc = regmap_write_bits(data->regmap, DPS310_CFG_REG,
+ DPS310_TMP_SHIFT_EN, shift_en);
+ if (rc)
+ return rc;
+
+ return regmap_update_bits(data->regmap, DPS310_TMP_CFG,
+ DPS310_TMP_PRC_BITS, ilog2(val));
+}
+
+/* Called with lock held */
+static int dps310_set_pres_samp_freq(struct dps310_data *data, int freq)
+{
+ u8 val;
+
+ if (freq < 0 || freq > 128)
+ return -EINVAL;
+
+ val = ilog2(freq) << 4;
+
+ return regmap_update_bits(data->regmap, DPS310_PRS_CFG,
+ DPS310_PRS_RATE_BITS, val);
+}
+
+/* Called with lock held */
+static int dps310_set_temp_samp_freq(struct dps310_data *data, int freq)
+{
+ u8 val;
+
+ if (freq < 0 || freq > 128)
+ return -EINVAL;
+
+ val = ilog2(freq) << 4;
+
+ return regmap_update_bits(data->regmap, DPS310_TMP_CFG,
+ DPS310_TMP_RATE_BITS, val);
+}
+
+static int dps310_get_pres_samp_freq(struct dps310_data *data)
+{
+ int rc;
+ int val;
+
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &val);
+ if (rc < 0)
+ return rc;
+
+ return BIT((val & DPS310_PRS_RATE_BITS) >> 4);
+}
+
+static int dps310_get_temp_samp_freq(struct dps310_data *data)
+{
+ int rc;
+ int val;
+
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &val);
+ if (rc < 0)
+ return rc;
+
+ return BIT((val & DPS310_TMP_RATE_BITS) >> 4);
+}
+
+static int dps310_get_pres_k(struct dps310_data *data)
+{
+ int rc = dps310_get_pres_precision(data);
+
+ if (rc < 0)
+ return rc;
+
+ return scale_factors[ilog2(rc)];
+}
+
+static int dps310_get_temp_k(struct dps310_data *data)
+{
+ int rc = dps310_get_temp_precision(data);
+
+ if (rc < 0)
+ return rc;
+
+ return scale_factors[ilog2(rc)];
+}
+
+static int dps310_read_pres_raw(struct dps310_data *data)
+{
+ int rc;
+ int rate;
+ int ready;
+ int timeout;
+ s32 raw;
+ u8 val[3];
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -EINTR;
+
+ rate = dps310_get_pres_samp_freq(data);
+ timeout = DPS310_POLL_TIMEOUT_US(rate);
+
+ /* Poll for sensor readiness; base the timeout upon the sample rate. */
+ rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
+ ready & DPS310_PRS_RDY,
+ DPS310_POLL_SLEEP_US(timeout), timeout);
+ if (rc)
+ goto done;
+
+ rc = regmap_bulk_read(data->regmap, DPS310_PRS_BASE, val, sizeof(val));
+ if (rc < 0)
+ goto done;
+
+ raw = (val[0] << 16) | (val[1] << 8) | val[2];
+ data->pressure_raw = sign_extend32(raw, 23);
+
+done:
+ mutex_unlock(&data->lock);
+ return rc;
+}
+
+/* Called with lock held */
+static int dps310_read_temp_ready(struct dps310_data *data)
+{
+ int rc;
+ u8 val[3];
+ s32 raw;
+
+ rc = regmap_bulk_read(data->regmap, DPS310_TMP_BASE, val, sizeof(val));
+ if (rc < 0)
+ return rc;
+
+ raw = (val[0] << 16) | (val[1] << 8) | val[2];
+ data->temp_raw = sign_extend32(raw, 23);
+
+ return 0;
+}
+
+static int dps310_read_temp_raw(struct dps310_data *data)
+{
+ int rc;
+ int rate;
+ int ready;
+ int timeout;
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -EINTR;
+
+ rate = dps310_get_temp_samp_freq(data);
+ timeout = DPS310_POLL_TIMEOUT_US(rate);
+
+ /* Poll for sensor readiness; base the timeout upon the sample rate. */
+ rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
+ ready & DPS310_TMP_RDY,
+ DPS310_POLL_SLEEP_US(timeout), timeout);
+ if (rc < 0)
+ goto done;
+
+ rc = dps310_read_temp_ready(data);
+
+done:
+ mutex_unlock(&data->lock);
+ return rc;
+}
+
+static bool dps310_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DPS310_PRS_CFG:
+ case DPS310_TMP_CFG:
+ case DPS310_MEAS_CFG:
+ case DPS310_CFG_REG:
+ case DPS310_RESET:
+ /* No documentation available on the registers below */
+ case 0x0e:
+ case 0x0f:
+ case 0x62:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool dps310_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case DPS310_PRS_B0:
+ case DPS310_PRS_B1:
+ case DPS310_PRS_B2:
+ case DPS310_TMP_B0:
+ case DPS310_TMP_B1:
+ case DPS310_TMP_B2:
+ case DPS310_MEAS_CFG:
+ case 0x32: /* No documentation available on this register */
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int dps310_write_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *chan, int val,
+ int val2, long mask)
+{
+ int rc;
+ struct dps310_data *data = iio_priv(iio);
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -EINTR;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ rc = dps310_set_pres_samp_freq(data, val);
+ break;
+
+ case IIO_TEMP:
+ rc = dps310_set_temp_samp_freq(data, val);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ break;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ rc = dps310_set_pres_precision(data, val);
+ break;
+
+ case IIO_TEMP:
+ rc = dps310_set_temp_precision(data, val);
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+ break;
+
+ default:
+ rc = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&data->lock);
+ return rc;
+}
+
+static int dps310_calculate_pressure(struct dps310_data *data)
+{
+ int i;
+ int rc;
+ int t_ready;
+ int kpi = dps310_get_pres_k(data);
+ int kti = dps310_get_temp_k(data);
+ s64 rem = 0ULL;
+ s64 pressure = 0ULL;
+ s64 p;
+ s64 t;
+ s64 denoms[7];
+ s64 nums[7];
+ s64 rems[7];
+ s64 kp;
+ s64 kt;
+
+ if (kpi < 0)
+ return kpi;
+
+ if (kti < 0)
+ return kti;
+
+ kp = (s64)kpi;
+ kt = (s64)kti;
+
+ /* Refresh temp if it's ready, otherwise just use the latest value */
+ if (mutex_trylock(&data->lock)) {
+ rc = regmap_read(data->regmap, DPS310_MEAS_CFG, &t_ready);
+ if (rc >= 0 && t_ready & DPS310_TMP_RDY)
+ dps310_read_temp_ready(data);
+
+ mutex_unlock(&data->lock);
+ }
+
+ p = (s64)data->pressure_raw;
+ t = (s64)data->temp_raw;
+
+ /* Section 4.9.1 of the DPS310 spec; algebra'd to avoid underflow */
+ nums[0] = (s64)data->c00;
+ denoms[0] = 1LL;
+ nums[1] = p * (s64)data->c10;
+ denoms[1] = kp;
+ nums[2] = p * p * (s64)data->c20;
+ denoms[2] = kp * kp;
+ nums[3] = p * p * p * (s64)data->c30;
+ denoms[3] = kp * kp * kp;
+ nums[4] = t * (s64)data->c01;
+ denoms[4] = kt;
+ nums[5] = t * p * (s64)data->c11;
+ denoms[5] = kp * kt;
+ nums[6] = t * p * p * (s64)data->c21;
+ denoms[6] = kp * kp * kt;
+
+ /* Kernel lacks a div64_s64_rem function; denoms are all positive */
+ for (i = 0; i < 7; ++i) {
+ u64 irem;
+
+ if (nums[i] < 0LL) {
+ pressure -= div64_u64_rem(-nums[i], denoms[i], &irem);
+ rems[i] = -irem;
+ } else {
+ pressure += div64_u64_rem(nums[i], denoms[i], &irem);
+ rems[i] = (s64)irem;
+ }
+ }
+
+ /* Increase precision and calculate the remainder sum */
+ for (i = 0; i < 7; ++i)
+ rem += div64_s64((s64)rems[i] * 1000000000LL, denoms[i]);
+
+ pressure += div_s64(rem, 1000000000LL);
+ if (pressure < 0LL)
+ return -ERANGE;
+
+ return (int)min_t(s64, pressure, INT_MAX);
+}
+
+static int dps310_read_pressure(struct dps310_data *data, int *val, int *val2,
+ long mask)
+{
+ int rc;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ rc = dps310_get_pres_samp_freq(data);
+ if (rc < 0)
+ return rc;
+
+ *val = rc;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_PROCESSED:
+ rc = dps310_read_pres_raw(data);
+ if (rc)
+ return rc;
+
+ rc = dps310_calculate_pressure(data);
+ if (rc < 0)
+ return rc;
+
+ *val = rc;
+ *val2 = 1000; /* Convert Pa to KPa per IIO ABI */
+ return IIO_VAL_FRACTIONAL;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ rc = dps310_get_pres_precision(data);
+ if (rc < 0)
+ return rc;
+
+ *val = rc;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dps310_calculate_temp(struct dps310_data *data)
+{
+ s64 c0;
+ s64 t;
+ int kt = dps310_get_temp_k(data);
+
+ if (kt < 0)
+ return kt;
+
+ /* Obtain inverse-scaled offset */
+ c0 = div_s64((s64)kt * (s64)data->c0, 2);
+
+ /* Add the offset to the unscaled temperature */
+ t = c0 + ((s64)data->temp_raw * (s64)data->c1);
+
+ /* Convert to milliCelsius and scale the temperature */
+ return (int)div_s64(t * 1000LL, kt);
+}
+
+static int dps310_read_temp(struct dps310_data *data, int *val, int *val2,
+ long mask)
+{
+ int rc;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ rc = dps310_get_temp_samp_freq(data);
+ if (rc < 0)
+ return rc;
+
+ *val = rc;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_PROCESSED:
+ rc = dps310_read_temp_raw(data);
+ if (rc)
+ return rc;
+
+ rc = dps310_calculate_temp(data);
+ if (rc < 0)
+ return rc;
+
+ *val = rc;
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ rc = dps310_get_temp_precision(data);
+ if (rc < 0)
+ return rc;
+
+ *val = rc;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dps310_read_raw(struct iio_dev *iio,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct dps310_data *data = iio_priv(iio);
+
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ return dps310_read_pressure(data, val, val2, mask);
+
+ case IIO_TEMP:
+ return dps310_read_temp(data, val, val2, mask);
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static void dps310_reset(void *action_data)
+{
+ struct dps310_data *data = action_data;
+
+ regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC);
+}
+
+static const struct regmap_config dps310_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = dps310_is_writeable_reg,
+ .volatile_reg = dps310_is_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+ .max_register = 0x62, /* No documentation available on this register */
+};
+
+static const struct iio_info dps310_info = {
+ .read_raw = dps310_read_raw,
+ .write_raw = dps310_write_raw,
+};
+
+/*
+ * Some verions of chip will read temperatures in the ~60C range when
+ * its actually ~20C. This is the manufacturer recommended workaround
+ * to correct the issue. The registers used below are undocumented.
+ */
+static int dps310_temp_workaround(struct dps310_data *data)
+{
+ int rc;
+ int reg;
+
+ rc = regmap_read(data->regmap, 0x32, ®);
+ if (rc < 0)
+ return rc;
+
+ /*
+ * If bit 1 is set then the device is okay, and the workaround does not
+ * need to be applied
+ */
+ if (reg & BIT(1))
+ return 0;
+
+ rc = regmap_write(data->regmap, 0x0e, 0xA5);
+ if (rc < 0)
+ return rc;
+
+ rc = regmap_write(data->regmap, 0x0f, 0x96);
+ if (rc < 0)
+ return rc;
+
+ rc = regmap_write(data->regmap, 0x62, 0x02);
+ if (rc < 0)
+ return rc;
+
+ rc = regmap_write(data->regmap, 0x0e, 0x00);
+ if (rc < 0)
+ return rc;
+
+ return regmap_write(data->regmap, 0x0f, 0x00);
+}
+
+static int dps310_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct dps310_data *data;
+ struct iio_dev *iio;
+ int rc, ready;
+
+ iio = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!iio)
+ return -ENOMEM;
+
+ data = iio_priv(iio);
+ data->client = client;
+ mutex_init(&data->lock);
+
+ iio->dev.parent = &client->dev;
+ iio->name = id->name;
+ iio->channels = dps310_channels;
+ iio->num_channels = ARRAY_SIZE(dps310_channels);
+ iio->info = &dps310_info;
+ iio->modes = INDIO_DIRECT_MODE;
+
+ data->regmap = devm_regmap_init_i2c(client, &dps310_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
+ /* Register to run the device reset when the device is removed */
+ rc = devm_add_action_or_reset(&client->dev, dps310_reset, data);
+ if (rc)
+ return rc;
+
+ /*
+ * Set up pressure sensor in single sample, one measurement per second
+ * mode
+ */
+ rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0);
+
+ /*
+ * Set up external (MEMS) temperature sensor in single sample, one
+ * measurement per second mode
+ */
+ rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT);
+ if (rc < 0)
+ return rc;
+
+ /* Temp and pressure shifts are disabled when PRC <= 8 */
+ rc = regmap_write_bits(data->regmap, DPS310_CFG_REG,
+ DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0);
+ if (rc < 0)
+ return rc;
+
+ /* MEAS_CFG doesn't update correctly unless first written with 0 */
+ rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
+ DPS310_MEAS_CTRL_BITS, 0);
+ if (rc < 0)
+ return rc;
+
+ /* Turn on temperature and pressure measurement in the background */
+ rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
+ DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN |
+ DPS310_TEMP_EN | DPS310_BACKGROUND);
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Calibration coefficients required for reporting temperature.
+ * They are available 40ms after the device has started
+ */
+ rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
+ ready & DPS310_COEF_RDY, 10000, 40000);
+ if (rc < 0)
+ return rc;
+
+ rc = dps310_get_coefs(data);
+ if (rc < 0)
+ return rc;
+
+ rc = dps310_temp_workaround(data);
+ if (rc < 0)
+ return rc;
+
+ rc = devm_iio_device_register(&client->dev, iio);
+ if (rc)
+ return rc;
+
+ i2c_set_clientdata(client, iio);
+
+ return 0;
+}
+
+static const struct i2c_device_id dps310_id[] = {
+ { DPS310_DEV_NAME, 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, dps310_id);
+
+static struct i2c_driver dps310_driver = {
+ .driver = {
+ .name = DPS310_DEV_NAME,
+ },
+ .probe = dps310_probe,
+ .id_table = dps310_id,
+};
+module_i2c_driver(dps310_driver);
+
+MODULE_AUTHOR("Joel Stanley <joel@jms.id.au>");
+MODULE_DESCRIPTION("Infineon DPS310 pressure and temperature sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/hid-sensor-press.c b/drivers/iio/pressure/hid-sensor-press.c
index d7b1c00..9532350 100644
--- a/drivers/iio/pressure/hid-sensor-press.c
+++ b/drivers/iio/pressure/hid-sensor-press.c
@@ -1,19 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2014, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.
- *
*/
#include <linux/device.h>
#include <linux/platform_device.h>
diff --git a/drivers/iio/pressure/hp03.c b/drivers/iio/pressure/hp03.c
index 406934e..026ba15 100644
--- a/drivers/iio/pressure/hp03.c
+++ b/drivers/iio/pressure/hp03.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2016 Marek Vasut <marex@denx.de>
*
* Driver for Hope RF HP03 digital temperature and pressure sensor.
- *
- * 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.
*/
#define pr_fmt(fmt) "hp03: " fmt
@@ -246,10 +243,10 @@
* which has it's dedicated I2C address and contains
* the calibration constants for the sensor.
*/
- priv->eeprom_client = i2c_new_dummy(client->adapter, HP03_EEPROM_ADDR);
- if (!priv->eeprom_client) {
+ priv->eeprom_client = i2c_new_dummy_device(client->adapter, HP03_EEPROM_ADDR);
+ if (IS_ERR(priv->eeprom_client)) {
dev_err(dev, "New EEPROM I2C device failed\n");
- return -ENODEV;
+ return PTR_ERR(priv->eeprom_client);
}
priv->eeprom_regmap = regmap_init_i2c(priv->eeprom_client,
diff --git a/drivers/iio/pressure/hp206c.c b/drivers/iio/pressure/hp206c.c
index c38c196..3ac3632 100644
--- a/drivers/iio/pressure/hp206c.c
+++ b/drivers/iio/pressure/hp206c.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* hp206c.c - HOPERF HP206C precision barometer and altimeter sensor
*
* Copyright (c) 2016, Intel Corporation.
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* (7-bit I2C slave address 0x76)
*
* Datasheet:
diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c
index ab4786d..ca81a3d 100644
--- a/drivers/iio/pressure/mpl115.c
+++ b/drivers/iio/pressure/mpl115.c
@@ -1,14 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* mpl115.c - Support for Freescale MPL115A pressure/temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* TODO: shutdown pin
- *
*/
#include <linux/module.h>
diff --git a/drivers/iio/pressure/mpl115.h b/drivers/iio/pressure/mpl115.h
index 01b6527..57d55eb 100644
--- a/drivers/iio/pressure/mpl115.h
+++ b/drivers/iio/pressure/mpl115.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Freescale MPL115A pressure/temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
- *
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
*/
#ifndef _MPL115_H_
diff --git a/drivers/iio/pressure/mpl115_i2c.c b/drivers/iio/pressure/mpl115_i2c.c
index 1a29be4..ac1f12b 100644
--- a/drivers/iio/pressure/mpl115_i2c.c
+++ b/drivers/iio/pressure/mpl115_i2c.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Freescale MPL115A2 pressure/temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* (7-bit I2C slave address 0x60)
*
* Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A2.pdf
diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c
index 9ebf55f..4d064f9 100644
--- a/drivers/iio/pressure/mpl115_spi.c
+++ b/drivers/iio/pressure/mpl115_spi.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Freescale MPL115A1 pressure/temperature sensor
*
* Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A1.pdf
*/
diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c
index 7537547..d066f3c 100644
--- a/drivers/iio/pressure/mpl3115.c
+++ b/drivers/iio/pressure/mpl3115.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* mpl3115.c - Support for Freescale MPL3115A2 pressure/temperature sensor
*
* Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* (7-bit I2C slave address 0x60)
*
* TODO: FIFO buffer, altimeter mode, oversampling, continuous mode,
diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h
index ead9e9f..bc06271 100644
--- a/drivers/iio/pressure/ms5611.h
+++ b/drivers/iio/pressure/ms5611.h
@@ -1,12 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
/*
* MS5611 pressure and temperature sensor driver
*
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
*
- * 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.
- *
*/
#ifndef _MS5611_H
diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
index f950cfd..2f598ad 100644
--- a/drivers/iio/pressure/ms5611_core.c
+++ b/drivers/iio/pressure/ms5611_core.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* MS5611 pressure and temperature sensor driver
*
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
*
- * 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.
- *
* Data sheet:
* http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
* http://www.meas-spec.com/downloads/MS5607-02BA03.pdf
diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c
index 55fb5fc..8089c59 100644
--- a/drivers/iio/pressure/ms5611_i2c.c
+++ b/drivers/iio/pressure/ms5611_i2c.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* MS5611 pressure and temperature sensor driver (I2C bus)
*
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
*
- * 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.
- *
* 7-bit I2C slave addresses:
*
* 0x77 (CSB pin low)
@@ -117,9 +114,7 @@
#if defined(CONFIG_OF)
static const struct of_device_id ms5611_i2c_matches[] = {
{ .compatible = "meas,ms5611" },
- { .compatible = "ms5611" },
{ .compatible = "meas,ms5607" },
- { .compatible = "ms5607" },
{ }
};
MODULE_DEVICE_TABLE(of, ms5611_i2c_matches);
diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c
index 932e050..b463eaa 100644
--- a/drivers/iio/pressure/ms5611_spi.c
+++ b/drivers/iio/pressure/ms5611_spi.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
/*
* MS5611 pressure and temperature sensor driver (SPI bus)
*
* Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
*
- * 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>
@@ -119,9 +116,7 @@
#if defined(CONFIG_OF)
static const struct of_device_id ms5611_spi_matches[] = {
{ .compatible = "meas,ms5611" },
- { .compatible = "ms5611" },
{ .compatible = "meas,ms5607" },
- { .compatible = "ms5607" },
{ }
};
MODULE_DEVICE_TABLE(of, ms5611_spi_matches);
diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c
index e2f73e6..f49c700 100644
--- a/drivers/iio/pressure/ms5637.c
+++ b/drivers/iio/pressure/ms5637.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ms5637.c - Support for Measurement-Specialties MS5637, MS5805
* MS5837 and MS8607 pressure & temperature sensor
*
* Copyright (c) 2015 Measurement-Specialties
*
- * Licensed under the GPL-2.
- *
* (7-bit I2C slave address 0x76)
*
* Datasheet:
diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h
index e67eb0d..c2e47a6 100644
--- a/drivers/iio/pressure/st_pressure.h
+++ b/drivers/iio/pressure/st_pressure.h
@@ -1,3 +1,4 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* STMicroelectronics pressures driver
*
@@ -5,7 +6,6 @@
*
* Denis Ciocca <denis.ciocca@st.com>
* v. 1.0.0
- * Licensed under the GPL-2.
*/
#ifndef ST_PRESS_H
@@ -21,6 +21,7 @@
LPS22HB,
LPS33HW,
LPS35HW,
+ LPS22HH,
ST_PRESS_MAX,
};
@@ -30,6 +31,7 @@
#define LPS22HB_PRESS_DEV_NAME "lps22hb"
#define LPS33HW_PRESS_DEV_NAME "lps33hw"
#define LPS35HW_PRESS_DEV_NAME "lps35hw"
+#define LPS22HH_PRESS_DEV_NAME "lps22hh"
/**
* struct st_sensors_platform_data - default press platform data
@@ -39,6 +41,7 @@
.drdy_int_pin = 1,
};
+const struct st_sensor_settings *st_press_get_settings(const char *name);
int st_press_common_probe(struct iio_dev *indio_dev);
void st_press_common_remove(struct iio_dev *indio_dev);
diff --git a/drivers/iio/pressure/st_pressure_buffer.c b/drivers/iio/pressure/st_pressure_buffer.c
index 99468d0..418dbf9 100644
--- a/drivers/iio/pressure/st_pressure_buffer.c
+++ b/drivers/iio/pressure/st_pressure_buffer.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics pressures driver
*
* Copyright 2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/module.h>
@@ -30,52 +29,39 @@
return st_sensors_set_dataready_irq(indio_dev, state);
}
-static int st_press_buffer_preenable(struct iio_dev *indio_dev)
-{
- return st_sensors_set_enable(indio_dev, true);
-}
-
static int st_press_buffer_postenable(struct iio_dev *indio_dev)
{
int err;
- struct st_sensor_data *press_data = iio_priv(indio_dev);
-
- press_data->buffer_data = kmalloc(indio_dev->scan_bytes, GFP_KERNEL);
- if (press_data->buffer_data == NULL) {
- err = -ENOMEM;
- goto allocate_memory_error;
- }
err = iio_triggered_buffer_postenable(indio_dev);
if (err < 0)
- goto st_press_buffer_postenable_error;
+ return err;
- return err;
+ err = st_sensors_set_enable(indio_dev, true);
+ if (err < 0)
+ goto st_press_buffer_predisable;
-st_press_buffer_postenable_error:
- kfree(press_data->buffer_data);
-allocate_memory_error:
+ return 0;
+
+st_press_buffer_predisable:
+ iio_triggered_buffer_predisable(indio_dev);
return err;
}
static int st_press_buffer_predisable(struct iio_dev *indio_dev)
{
- int err;
- struct st_sensor_data *press_data = iio_priv(indio_dev);
-
- err = iio_triggered_buffer_predisable(indio_dev);
- if (err < 0)
- goto st_press_buffer_predisable_error;
+ int err, err2;
err = st_sensors_set_enable(indio_dev, false);
-st_press_buffer_predisable_error:
- kfree(press_data->buffer_data);
+ err2 = iio_triggered_buffer_predisable(indio_dev);
+ if (!err)
+ err = err2;
+
return err;
}
static const struct iio_buffer_setup_ops st_press_buffer_setup_ops = {
- .preenable = &st_press_buffer_preenable,
.postenable = &st_press_buffer_postenable,
.predisable = &st_press_buffer_predisable,
};
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
index 4ddb6cf..ca6863b 100644
--- a/drivers/iio/pressure/st_pressure_core.c
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics pressures driver
*
* Copyright 2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -13,7 +12,6 @@
#include <linux/slab.h>
#include <linux/errno.h>
#include <linux/types.h>
-#include <linux/mutex.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/gpio.h>
@@ -492,6 +490,75 @@
.multi_read_bit = false,
.bootime = 2,
},
+ {
+ /*
+ * CUSTOM VALUES FOR LPS22HH SENSOR
+ * See LPS22HH datasheet:
+ * http://www2.st.com/resource/en/datasheet/lps22hh.pdf
+ */
+ .wai = 0xb3,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LPS22HH_PRESS_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_press_lps22hb_channels,
+ .num_ch = ARRAY_SIZE(st_press_lps22hb_channels),
+ .odr = {
+ .addr = 0x10,
+ .mask = 0x70,
+ .odr_avl = {
+ { .hz = 1, .value = 0x01 },
+ { .hz = 10, .value = 0x02 },
+ { .hz = 25, .value = 0x03 },
+ { .hz = 50, .value = 0x04 },
+ { .hz = 75, .value = 0x05 },
+ { .hz = 100, .value = 0x06 },
+ { .hz = 200, .value = 0x07 },
+ },
+ },
+ .pw = {
+ .addr = 0x10,
+ .mask = 0x70,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .fs = {
+ .fs_avl = {
+ /*
+ * Pressure and temperature sensitivity values
+ * as defined in table 3 of LPS22HH datasheet.
+ */
+ [0] = {
+ .num = ST_PRESS_FS_AVL_1260MB,
+ .gain = ST_PRESS_KPASCAL_NANO_SCALE,
+ .gain2 = ST_PRESS_LPS22HB_LSB_PER_CELSIUS,
+ },
+ },
+ },
+ .bdu = {
+ .addr = 0x10,
+ .mask = BIT(1),
+ },
+ .drdy_irq = {
+ .int1 = {
+ .addr = 0x12,
+ .mask = BIT(2),
+ .addr_od = 0x11,
+ .mask_od = BIT(5),
+ },
+ .addr_ihl = 0x11,
+ .mask_ihl = BIT(6),
+ .stat_drdy = {
+ .addr = ST_SENSORS_DEFAULT_STAT_ADDR,
+ .mask = 0x03,
+ },
+ },
+ .sim = {
+ .addr = 0x10,
+ .value = BIT(0),
+ },
+ .multi_read_bit = false,
+ .bootime = 2,
+ },
};
static int st_press_write_raw(struct iio_dev *indio_dev,
@@ -596,25 +663,39 @@
#define ST_PRESS_TRIGGER_OPS NULL
#endif
+/*
+ * st_press_get_settings() - get sensor settings from device name
+ * @name: device name buffer reference.
+ *
+ * Return: valid reference on success, NULL otherwise.
+ */
+const struct st_sensor_settings *st_press_get_settings(const char *name)
+{
+ int index = st_sensors_get_settings_index(name,
+ st_press_sensors_settings,
+ ARRAY_SIZE(st_press_sensors_settings));
+ if (index < 0)
+ return NULL;
+
+ return &st_press_sensors_settings[index];
+}
+EXPORT_SYMBOL(st_press_get_settings);
+
int st_press_common_probe(struct iio_dev *indio_dev)
{
struct st_sensor_data *press_data = iio_priv(indio_dev);
struct st_sensors_platform_data *pdata =
(struct st_sensors_platform_data *)press_data->dev->platform_data;
- int irq = press_data->get_irq_data_ready(indio_dev);
int err;
indio_dev->modes = INDIO_DIRECT_MODE;
indio_dev->info = &press_info;
- mutex_init(&press_data->tb.buf_lock);
err = st_sensors_power_enable(indio_dev);
if (err)
return err;
- err = st_sensors_check_device_support(indio_dev,
- ARRAY_SIZE(st_press_sensors_settings),
- st_press_sensors_settings);
+ err = st_sensors_verify_id(indio_dev);
if (err < 0)
goto st_press_power_off;
@@ -625,7 +706,6 @@
* element.
*/
press_data->num_data_channels = press_data->sensor_settings->num_ch - 1;
- press_data->multiread_bit = press_data->sensor_settings->multi_read_bit;
indio_dev->channels = press_data->sensor_settings->ch;
indio_dev->num_channels = press_data->sensor_settings->num_ch;
@@ -648,7 +728,7 @@
if (err < 0)
goto st_press_power_off;
- if (irq > 0) {
+ if (press_data->irq > 0) {
err = st_sensors_allocate_trigger(indio_dev,
ST_PRESS_TRIGGER_OPS);
if (err < 0)
@@ -665,7 +745,7 @@
return err;
st_press_device_register_error:
- if (irq > 0)
+ if (press_data->irq > 0)
st_sensors_deallocate_trigger(indio_dev);
st_press_probe_trigger_error:
st_press_deallocate_ring(indio_dev);
@@ -683,7 +763,7 @@
st_sensors_power_disable(indio_dev);
iio_device_unregister(indio_dev);
- if (press_data->get_irq_data_ready(indio_dev) > 0)
+ if (press_data->irq > 0)
st_sensors_deallocate_trigger(indio_dev);
st_press_deallocate_ring(indio_dev);
diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c
index 2026a10..71d2ed6 100644
--- a/drivers/iio/pressure/st_pressure_i2c.c
+++ b/drivers/iio/pressure/st_pressure_i2c.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics pressures driver
*
* Copyright 2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -45,6 +44,10 @@
.compatible = "st,lps35hw",
.data = LPS35HW_PRESS_DEV_NAME,
},
+ {
+ .compatible = "st,lps22hh",
+ .data = LPS22HH_PRESS_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_press_of_match);
@@ -69,23 +72,19 @@
{ LPS22HB_PRESS_DEV_NAME, LPS22HB },
{ LPS33HW_PRESS_DEV_NAME, LPS33HW },
{ LPS35HW_PRESS_DEV_NAME, LPS35HW },
+ { LPS22HH_PRESS_DEV_NAME, LPS22HH },
{},
};
MODULE_DEVICE_TABLE(i2c, st_press_id_table);
static int st_press_i2c_probe(struct i2c_client *client,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
- struct iio_dev *indio_dev;
+ const struct st_sensor_settings *settings;
struct st_sensor_data *press_data;
+ struct iio_dev *indio_dev;
int ret;
- indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*press_data));
- if (!indio_dev)
- return -ENOMEM;
-
- press_data = iio_priv(indio_dev);
-
if (client->dev.of_node) {
st_sensors_of_name_probe(&client->dev, st_press_of_match,
client->name, sizeof(client->name));
@@ -95,11 +94,27 @@
return -ENODEV;
strlcpy(client->name, st_press_id_table[ret].name,
- sizeof(client->name));
+ sizeof(client->name));
} else if (!id)
return -ENODEV;
- st_sensors_i2c_configure(indio_dev, client, press_data);
+ settings = st_press_get_settings(client->name);
+ if (!settings) {
+ dev_err(&client->dev, "device name %s not recognized.\n",
+ client->name);
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*press_data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ press_data = iio_priv(indio_dev);
+ press_data->sensor_settings = (struct st_sensor_settings *)settings;
+
+ ret = st_sensors_i2c_configure(indio_dev, client);
+ if (ret < 0)
+ return ret;
ret = st_press_common_probe(indio_dev);
if (ret < 0)
diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c
index 9a3441b..7c8b702 100644
--- a/drivers/iio/pressure/st_pressure_spi.c
+++ b/drivers/iio/pressure/st_pressure_spi.c
@@ -1,11 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* STMicroelectronics pressures driver
*
* Copyright 2013 STMicroelectronics Inc.
*
* Denis Ciocca <denis.ciocca@st.com>
- *
- * Licensed under the GPL-2.
*/
#include <linux/kernel.h>
@@ -49,6 +48,10 @@
.compatible = "st,lps35hw",
.data = LPS35HW_PRESS_DEV_NAME,
},
+ {
+ .compatible = "st,lps22hh",
+ .data = LPS22HH_PRESS_DEV_NAME,
+ },
{},
};
MODULE_DEVICE_TABLE(of, st_press_of_match);
@@ -58,19 +61,31 @@
static int st_press_spi_probe(struct spi_device *spi)
{
- struct iio_dev *indio_dev;
+ const struct st_sensor_settings *settings;
struct st_sensor_data *press_data;
+ struct iio_dev *indio_dev;
int err;
- indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*press_data));
- if (indio_dev == NULL)
- return -ENOMEM;
-
- press_data = iio_priv(indio_dev);
-
st_sensors_of_name_probe(&spi->dev, st_press_of_match,
spi->modalias, sizeof(spi->modalias));
- st_sensors_spi_configure(indio_dev, spi, press_data);
+
+ settings = st_press_get_settings(spi->modalias);
+ if (!settings) {
+ dev_err(&spi->dev, "device name %s not recognized.\n",
+ spi->modalias);
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*press_data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ press_data = iio_priv(indio_dev);
+ press_data->sensor_settings = (struct st_sensor_settings *)settings;
+
+ err = st_sensors_spi_configure(indio_dev, spi);
+ if (err < 0)
+ return err;
err = st_press_common_probe(indio_dev);
if (err < 0)
@@ -93,6 +108,7 @@
{ LPS22HB_PRESS_DEV_NAME },
{ LPS33HW_PRESS_DEV_NAME },
{ LPS35HW_PRESS_DEV_NAME },
+ { LPS22HH_PRESS_DEV_NAME },
{},
};
MODULE_DEVICE_TABLE(spi, st_press_id_table);
diff --git a/drivers/iio/pressure/t5403.c b/drivers/iio/pressure/t5403.c
index 92c00f6..22abd28 100644
--- a/drivers/iio/pressure/t5403.c
+++ b/drivers/iio/pressure/t5403.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* t5403.c - Support for EPCOS T5403 pressure/temperature sensor
*
* Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* (7-bit I2C slave address 0x77)
*
* TODO: end-of-conversion irq
diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c
index 81d8f24..9d0d079 100644
--- a/drivers/iio/pressure/zpa2326.c
+++ b/drivers/iio/pressure/zpa2326.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Murata ZPA2326 pressure and temperature sensor IIO driver
*
* Copyright (c) 2016 Parrot S.A.
*
* Author: Gregor Boirie <gregor.boirie@parrot.com>
- *
- * 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.
- *
- * 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.
*/
/**
diff --git a/drivers/iio/pressure/zpa2326.h b/drivers/iio/pressure/zpa2326.h
index 05d3e1e..45bd790 100644
--- a/drivers/iio/pressure/zpa2326.h
+++ b/drivers/iio/pressure/zpa2326.h
@@ -1,18 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Murata ZPA2326 pressure and temperature sensor IIO driver
*
* Copyright (c) 2016 Parrot S.A.
*
* Author: Gregor Boirie <gregor.boirie@parrot.com>
- *
- * 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.
- *
- * 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.
*/
#ifndef _ZPA2326_H
diff --git a/drivers/iio/pressure/zpa2326_i2c.c b/drivers/iio/pressure/zpa2326_i2c.c
index e4d27dd..1a65791 100644
--- a/drivers/iio/pressure/zpa2326_i2c.c
+++ b/drivers/iio/pressure/zpa2326_i2c.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Murata ZPA2326 I2C pressure and temperature sensor driver
*
* Copyright (c) 2016 Parrot S.A.
*
* Author: Gregor Boirie <gregor.boirie@parrot.com>
- *
- * 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.
- *
- * 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/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c
index bd2c1c3..f37a4c7 100644
--- a/drivers/iio/pressure/zpa2326_spi.c
+++ b/drivers/iio/pressure/zpa2326_spi.c
@@ -1,18 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Murata ZPA2326 SPI pressure and temperature sensor driver
*
* Copyright (c) 2016 Parrot S.A.
*
* Author: Gregor Boirie <gregor.boirie@parrot.com>
- *
- * 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.
- *
- * 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/proximity/Kconfig b/drivers/iio/proximity/Kconfig
index 388ef70..d536014 100644
--- a/drivers/iio/proximity/Kconfig
+++ b/drivers/iio/proximity/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Proximity sensors
#
@@ -45,23 +46,42 @@
To compile this driver as a module, choose M here: the
module will be called pulsedlight-lite-v2
+config MB1232
+ tristate "MaxSonar I2CXL family ultrasonic sensors"
+ depends on I2C
+ help
+ Say Y to build a driver for the ultrasonic sensors I2CXL of
+ MaxBotix which have an i2c interface. It can be used to measure
+ the distance of objects. Supported types are mb1202, mb1212,
+ mb1222, mb1232, mb1242, mb7040, mb7137
+
+ To compile this driver as a module, choose M here: the
+ module will be called mb1232.
+
config RFD77402
tristate "RFD77402 ToF sensor"
depends on I2C
help
- Say Y to build a driver for the RFD77420 Time-of-Flight (distance)
+ Say Y to build a driver for the RFD77402 Time-of-Flight (distance)
sensor module with I2C interface.
To compile this driver as a module, choose M here: the
module will be called rfd77402.
config SRF04
- tristate "Devantech SRF04 ultrasonic ranger sensor"
+ tristate "GPIO bitbanged ultrasonic ranger sensor (SRF04, MB1000)"
depends on GPIOLIB
help
- Say Y here to build a driver for Devantech SRF04 ultrasonic
+ Say Y here to build a driver for GPIO bitbanged ultrasonic
ranger sensor. This driver can be used to measure the distance
of objects. It is using two GPIOs.
+ Actually Supported types are:
+ - Devantech SRF04
+ - Maxbotix mb1000
+ - Maxbotix mb1010
+ - Maxbotix mb1020
+ - Maxbotix mb1030
+ - Maxbotix mb1040
To compile this driver as a module, choose M here: the
module will be called srf04.
@@ -92,4 +112,15 @@
To compile this driver as a module, choose M here: the
module will be called srf08.
+config VL53L0X_I2C
+ tristate "STMicroelectronics VL53L0X ToF ranger sensor (I2C)"
+ depends on I2C
+ help
+ Say Y here to build a driver for STMicroelectronics VL53L0X
+ ToF ranger sensors with i2c interface.
+ This driver can be used to measure the distance of objects.
+
+ To compile this driver as a module, choose M here: the
+ module will be called vl53l0x-i2c.
+
endmenu
diff --git a/drivers/iio/proximity/Makefile b/drivers/iio/proximity/Makefile
index cac3d7d..0bb5f9d 100644
--- a/drivers/iio/proximity/Makefile
+++ b/drivers/iio/proximity/Makefile
@@ -7,7 +7,10 @@
obj-$(CONFIG_AS3935) += as3935.o
obj-$(CONFIG_ISL29501) += isl29501.o
obj-$(CONFIG_LIDAR_LITE_V2) += pulsedlight-lidar-lite-v2.o
+obj-$(CONFIG_MB1232) += mb1232.o
obj-$(CONFIG_RFD77402) += rfd77402.o
obj-$(CONFIG_SRF04) += srf04.o
obj-$(CONFIG_SRF08) += srf08.o
obj-$(CONFIG_SX9500) += sx9500.o
+obj-$(CONFIG_VL53L0X_I2C) += vl53l0x-i2c.o
+
diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c
index f130388..b591c63 100644
--- a/drivers/iio/proximity/as3935.c
+++ b/drivers/iio/proximity/as3935.c
@@ -345,6 +345,14 @@
#define AS3935_PM_OPS NULL
#endif
+static void as3935_stop_work(void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct as3935_state *st = iio_priv(indio_dev);
+
+ cancel_delayed_work_sync(&st->work);
+}
+
static int as3935_probe(struct spi_device *spi)
{
struct iio_dev *indio_dev;
@@ -368,7 +376,6 @@
spi_set_drvdata(spi, indio_dev);
mutex_init(&st->lock);
- INIT_DELAYED_WORK(&st->work, as3935_event_work);
ret = of_property_read_u32(np,
"ams,tuning-capacitor-pf", &st->tune_cap);
@@ -414,22 +421,28 @@
iio_trigger_set_drvdata(trig, indio_dev);
trig->ops = &iio_interrupt_trigger_ops;
- ret = iio_trigger_register(trig);
+ ret = devm_iio_trigger_register(&spi->dev, trig);
if (ret) {
dev_err(&spi->dev, "failed to register trigger\n");
return ret;
}
- ret = iio_triggered_buffer_setup(indio_dev, iio_pollfunc_store_time,
- &as3935_trigger_handler, NULL);
+ ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev,
+ iio_pollfunc_store_time,
+ as3935_trigger_handler, NULL);
if (ret) {
dev_err(&spi->dev, "cannot setup iio trigger\n");
- goto unregister_trigger;
+ return ret;
}
calibrate_as3935(st);
+ INIT_DELAYED_WORK(&st->work, as3935_event_work);
+ ret = devm_add_action(&spi->dev, as3935_stop_work, indio_dev);
+ if (ret)
+ return ret;
+
ret = devm_request_irq(&spi->dev, spi->irq,
&as3935_interrupt_handler,
IRQF_TRIGGER_RISING,
@@ -438,35 +451,15 @@
if (ret) {
dev_err(&spi->dev, "unable to request irq\n");
- goto unregister_buffer;
+ return ret;
}
- ret = iio_device_register(indio_dev);
+ ret = devm_iio_device_register(&spi->dev, indio_dev);
if (ret < 0) {
dev_err(&spi->dev, "unable to register device\n");
- goto unregister_buffer;
+ return ret;
}
return 0;
-
-unregister_buffer:
- iio_triggered_buffer_cleanup(indio_dev);
-
-unregister_trigger:
- iio_trigger_unregister(st->trig);
-
- return ret;
-}
-
-static int as3935_remove(struct spi_device *spi)
-{
- struct iio_dev *indio_dev = spi_get_drvdata(spi);
- struct as3935_state *st = iio_priv(indio_dev);
-
- iio_device_unregister(indio_dev);
- iio_triggered_buffer_cleanup(indio_dev);
- iio_trigger_unregister(st->trig);
-
- return 0;
}
static const struct of_device_id as3935_of_match[] = {
@@ -488,7 +481,6 @@
.pm = AS3935_PM_OPS,
},
.probe = as3935_probe,
- .remove = as3935_remove,
.id_table = as3935_id,
};
module_spi_driver(as3935_driver);
diff --git a/drivers/iio/proximity/isl29501.c b/drivers/iio/proximity/isl29501.c
index e5e9454..5ae5490 100644
--- a/drivers/iio/proximity/isl29501.c
+++ b/drivers/iio/proximity/isl29501.c
@@ -232,7 +232,6 @@
u32 value)
{
const struct isl29501_register_desc *reg = &isl29501_registers[name];
- u8 msb, lsb;
int ret;
if (!reg->msb && value > U8_MAX)
@@ -241,22 +240,15 @@
if (value > U16_MAX)
return -ERANGE;
- if (!reg->msb) {
- lsb = value & 0xFF;
- } else {
- msb = (value >> 8) & 0xFF;
- lsb = value & 0xFF;
- }
-
mutex_lock(&isl29501->lock);
if (reg->msb) {
ret = i2c_smbus_write_byte_data(isl29501->client,
- reg->msb, msb);
+ reg->msb, value >> 8);
if (ret < 0)
goto err;
}
- ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, lsb);
+ ret = i2c_smbus_write_byte_data(isl29501->client, reg->lsb, value);
err:
mutex_unlock(&isl29501->lock);
diff --git a/drivers/iio/proximity/mb1232.c b/drivers/iio/proximity/mb1232.c
new file mode 100644
index 0000000..166b3e6
--- /dev/null
+++ b/drivers/iio/proximity/mb1232.c
@@ -0,0 +1,272 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * mb1232.c - Support for MaxBotix I2CXL-MaxSonar-EZ series ultrasonic
+ * ranger with i2c interface
+ * actually tested with mb1232 type
+ *
+ * Copyright (c) 2019 Andreas Klinger <ak@it-klinger.de>
+ *
+ * For details about the device see:
+ * https://www.maxbotix.com/documents/I2CXL-MaxSonar-EZ_Datasheet.pdf
+ */
+
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/of_irq.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/bitops.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+/* registers of MaxSonar device */
+#define MB1232_RANGE_COMMAND 0x51 /* Command for reading range */
+#define MB1232_ADDR_UNLOCK_1 0xAA /* Command 1 for changing address */
+#define MB1232_ADDR_UNLOCK_2 0xA5 /* Command 2 for changing address */
+
+struct mb1232_data {
+ struct i2c_client *client;
+
+ struct mutex lock;
+
+ /*
+ * optionally a gpio can be used to announce when ranging has
+ * finished
+ * since we are just using the falling trigger of it we request
+ * only the interrupt for announcing when data is ready to be read
+ */
+ struct completion ranging;
+ int irqnr;
+};
+
+static irqreturn_t mb1232_handle_irq(int irq, void *dev_id)
+{
+ struct iio_dev *indio_dev = dev_id;
+ struct mb1232_data *data = iio_priv(indio_dev);
+
+ complete(&data->ranging);
+
+ return IRQ_HANDLED;
+}
+
+static s16 mb1232_read_distance(struct mb1232_data *data)
+{
+ struct i2c_client *client = data->client;
+ int ret;
+ s16 distance;
+ __be16 buf;
+
+ mutex_lock(&data->lock);
+
+ reinit_completion(&data->ranging);
+
+ ret = i2c_smbus_write_byte(client, MB1232_RANGE_COMMAND);
+ if (ret < 0) {
+ dev_err(&client->dev, "write command - err: %d\n", ret);
+ goto error_unlock;
+ }
+
+ if (data->irqnr >= 0) {
+ /* it cannot take more than 100 ms */
+ ret = wait_for_completion_killable_timeout(&data->ranging,
+ HZ/10);
+ if (ret < 0)
+ goto error_unlock;
+ else if (ret == 0) {
+ ret = -ETIMEDOUT;
+ goto error_unlock;
+ }
+ } else {
+ /* use simple sleep if announce irq is not connected */
+ msleep(15);
+ }
+
+ ret = i2c_master_recv(client, (char *)&buf, sizeof(buf));
+ if (ret < 0) {
+ dev_err(&client->dev, "i2c_master_recv: ret=%d\n", ret);
+ goto error_unlock;
+ }
+
+ distance = __be16_to_cpu(buf);
+ /* check for not returning misleading error codes */
+ if (distance < 0) {
+ dev_err(&client->dev, "distance=%d\n", distance);
+ ret = -EINVAL;
+ goto error_unlock;
+ }
+
+ mutex_unlock(&data->lock);
+
+ return distance;
+
+error_unlock:
+ mutex_unlock(&data->lock);
+
+ return ret;
+}
+
+static irqreturn_t mb1232_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mb1232_data *data = iio_priv(indio_dev);
+ /*
+ * triggered buffer
+ * 16-bit channel + 48-bit padding + 64-bit timestamp
+ */
+ s16 buffer[8] = { 0 };
+
+ buffer[0] = mb1232_read_distance(data);
+ if (buffer[0] < 0)
+ goto err;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, buffer, pf->timestamp);
+
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static int mb1232_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct mb1232_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (channel->type != IIO_DISTANCE)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = mb1232_read_distance(data);
+ if (ret < 0)
+ return ret;
+ *val = ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ /* 1 LSB is 1 cm */
+ *val = 0;
+ *val2 = 10000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec mb1232_channels[] = {
+ {
+ .type = IIO_DISTANCE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static const struct iio_info mb1232_info = {
+ .read_raw = mb1232_read_raw,
+};
+
+static int mb1232_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct iio_dev *indio_dev;
+ struct mb1232_data *data;
+ int ret;
+ struct device *dev = &client->dev;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_BYTE |
+ I2C_FUNC_SMBUS_WRITE_BYTE))
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ data->client = client;
+
+ indio_dev->info = &mb1232_info;
+ indio_dev->name = id->name;
+ indio_dev->dev.parent = dev;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mb1232_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mb1232_channels);
+
+ mutex_init(&data->lock);
+
+ init_completion(&data->ranging);
+
+ data->irqnr = irq_of_parse_and_map(dev->of_node, 0);
+ if (data->irqnr <= 0) {
+ /* usage of interrupt is optional */
+ data->irqnr = -1;
+ } else {
+ ret = devm_request_irq(dev, data->irqnr, mb1232_handle_irq,
+ IRQF_TRIGGER_FALLING, id->name, indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "request_irq: %d\n", ret);
+ return ret;
+ }
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
+ iio_pollfunc_store_time, mb1232_trigger_handler, NULL);
+ if (ret < 0) {
+ dev_err(dev, "setup of iio triggered buffer failed\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+
+static const struct of_device_id of_mb1232_match[] = {
+ { .compatible = "maxbotix,mb1202", },
+ { .compatible = "maxbotix,mb1212", },
+ { .compatible = "maxbotix,mb1222", },
+ { .compatible = "maxbotix,mb1232", },
+ { .compatible = "maxbotix,mb1242", },
+ { .compatible = "maxbotix,mb7040", },
+ { .compatible = "maxbotix,mb7137", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_mb1232_match);
+
+static const struct i2c_device_id mb1232_id[] = {
+ { "maxbotix-mb1202", },
+ { "maxbotix-mb1212", },
+ { "maxbotix-mb1222", },
+ { "maxbotix-mb1232", },
+ { "maxbotix-mb1242", },
+ { "maxbotix-mb7040", },
+ { "maxbotix-mb7137", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mb1232_id);
+
+static struct i2c_driver mb1232_driver = {
+ .driver = {
+ .name = "maxbotix-mb1232",
+ .of_match_table = of_mb1232_match,
+ },
+ .probe = mb1232_probe,
+ .id_table = mb1232_id,
+};
+module_i2c_driver(mb1232_driver);
+
+MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
+MODULE_DESCRIPTION("Maxbotix I2CXL-MaxSonar i2c ultrasonic ranger driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/proximity/rfd77402.c b/drivers/iio/proximity/rfd77402.c
index fe29fb1..36480c0 100644
--- a/drivers/iio/proximity/rfd77402.c
+++ b/drivers/iio/proximity/rfd77402.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* rfd77402.c - Support for RF Digital RFD77402 Time-of-Flight (distance) sensor
*
* Copyright 2017 Peter Meerwald-Stadler <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* 7-bit I2C slave address 0x4c
*
* TODO: interrupt
diff --git a/drivers/iio/proximity/srf04.c b/drivers/iio/proximity/srf04.c
index 09c7b9c..01eb8cc 100644
--- a/drivers/iio/proximity/srf04.c
+++ b/drivers/iio/proximity/srf04.c
@@ -1,18 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
/*
* SRF04: ultrasonic sensor for distance measuring by using GPIOs
*
* Copyright (c) 2017 Andreas Klinger <ak@it-klinger.de>
*
- * 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.
- *
* For details about the device see:
* http://www.robot-electronics.co.uk/htm/srf04tech.htm
*
@@ -23,7 +14,7 @@
* trig: --+ +------------------------------------------------------
* ^ ^
* |<->|
- * udelay(10)
+ * udelay(trigger_pulse_us)
*
* ultra +-+ +-+ +-+
* sonic | | | | | |
@@ -48,6 +39,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/property.h>
#include <linux/sched.h>
@@ -56,6 +48,10 @@
#include <linux/iio/iio.h>
#include <linux/iio/sysfs.h>
+struct srf04_cfg {
+ unsigned long trigger_pulse_us;
+};
+
struct srf04_data {
struct device *dev;
struct gpio_desc *gpiod_trig;
@@ -66,6 +62,15 @@
ktime_t ts_falling;
struct completion rising;
struct completion falling;
+ const struct srf04_cfg *cfg;
+};
+
+static const struct srf04_cfg srf04_cfg = {
+ .trigger_pulse_us = 10,
+};
+
+static const struct srf04_cfg mb_lv_cfg = {
+ .trigger_pulse_us = 20,
};
static irqreturn_t srf04_handle_irq(int irq, void *dev_id)
@@ -102,10 +107,10 @@
reinit_completion(&data->falling);
gpiod_set_value(data->gpiod_trig, 1);
- udelay(10);
+ udelay(data->cfg->trigger_pulse_us);
gpiod_set_value(data->gpiod_trig, 0);
- /* it cannot take more than 20 ms */
+ /* it should not take more than 20 ms until echo is rising */
ret = wait_for_completion_killable_timeout(&data->rising, HZ/50);
if (ret < 0) {
mutex_unlock(&data->lock);
@@ -115,7 +120,8 @@
return -ETIMEDOUT;
}
- ret = wait_for_completion_killable_timeout(&data->falling, HZ/50);
+ /* it cannot take more than 50 ms until echo is falling */
+ ret = wait_for_completion_killable_timeout(&data->falling, HZ/20);
if (ret < 0) {
mutex_unlock(&data->lock);
return ret;
@@ -130,19 +136,19 @@
dt_ns = ktime_to_ns(ktime_dt);
/*
- * measuring more than 3 meters is beyond the capabilities of
- * the sensor
+ * measuring more than 6,45 meters is beyond the capabilities of
+ * the supported sensors
* ==> filter out invalid results for not measuring echos of
* another us sensor
*
* formula:
- * distance 3 m
- * time = ---------- = --------- = 9404389 ns
- * speed 319 m/s
+ * distance 6,45 * 2 m
+ * time = ---------- = ------------ = 40438871 ns
+ * speed 319 m/s
*
* using a minimum speed at -20 °C of 319 m/s
*/
- if (dt_ns > 9404389)
+ if (dt_ns > 40438871)
return -EIO;
time_ns = dt_ns;
@@ -154,20 +160,20 @@
* with Temp in °C
* and speed in m/s
*
- * use 343 m/s as ultrasonic speed at 20 °C here in absence of the
+ * use 343,5 m/s as ultrasonic speed at 20 °C here in absence of the
* temperature
*
* therefore:
- * time 343
- * distance = ------ * -----
- * 10^6 2
+ * time 343,5 time * 106
+ * distance = ------ * ------- = ------------
+ * 10^6 2 617176
* with time in ns
* and distance in mm (one way)
*
- * because we limit to 3 meters the multiplication with 343 just
+ * because we limit to 6,45 meters the multiplication with 106 just
* fits into 32 bit
*/
- distance_mm = time_ns * 343 / 2000000;
+ distance_mm = time_ns * 106 / 617176;
return distance_mm;
}
@@ -215,6 +221,18 @@
},
};
+static const struct of_device_id of_srf04_match[] = {
+ { .compatible = "devantech,srf04", .data = &srf04_cfg},
+ { .compatible = "maxbotix,mb1000", .data = &mb_lv_cfg},
+ { .compatible = "maxbotix,mb1010", .data = &mb_lv_cfg},
+ { .compatible = "maxbotix,mb1020", .data = &mb_lv_cfg},
+ { .compatible = "maxbotix,mb1030", .data = &mb_lv_cfg},
+ { .compatible = "maxbotix,mb1040", .data = &mb_lv_cfg},
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, of_srf04_match);
+
static int srf04_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -230,6 +248,7 @@
data = iio_priv(indio_dev);
data->dev = dev;
+ data->cfg = of_match_device(of_srf04_match, dev)->data;
mutex_init(&data->lock);
init_completion(&data->rising);
@@ -280,13 +299,6 @@
return devm_iio_device_register(dev, indio_dev);
}
-static const struct of_device_id of_srf04_match[] = {
- { .compatible = "devantech,srf04", },
- {},
-};
-
-MODULE_DEVICE_TABLE(of, of_srf04_match);
-
static struct platform_driver srf04_driver = {
.probe = srf04_probe,
.driver = {
diff --git a/drivers/iio/proximity/srf08.c b/drivers/iio/proximity/srf08.c
index f2bf783..b23ce44 100644
--- a/drivers/iio/proximity/srf08.c
+++ b/drivers/iio/proximity/srf08.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* srf08.c - Support for Devantech SRFxx ultrasonic ranger
* with i2c interface
@@ -5,10 +6,6 @@
*
* Copyright (c) 2016, 2017 Andreas Klinger <ak@it-klinger.de>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* For details about the device see:
* http://www.robot-electronics.co.uk/htm/srf08tech.html
* http://www.robot-electronics.co.uk/htm/srf10tech.htm
diff --git a/drivers/iio/proximity/sx9500.c b/drivers/iio/proximity/sx9500.c
index ff80409..612f79c 100644
--- a/drivers/iio/proximity/sx9500.c
+++ b/drivers/iio/proximity/sx9500.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2014 Intel Corporation
*
* Driver for Semtech's SX9500 capacitive proximity/button solution.
* Datasheet available at
* <http://www.semtech.com/images/datasheet/sx9500.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/kernel.h>
diff --git a/drivers/iio/proximity/vl53l0x-i2c.c b/drivers/iio/proximity/vl53l0x-i2c.c
new file mode 100644
index 0000000..b48216c
--- /dev/null
+++ b/drivers/iio/proximity/vl53l0x-i2c.c
@@ -0,0 +1,164 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Support for ST VL53L0X FlightSense ToF Ranging Sensor on a i2c bus.
+ *
+ * Copyright (C) 2016 STMicroelectronics Imaging Division.
+ * Copyright (C) 2018 Song Qiang <songqiang1304521@gmail.com>
+ *
+ * Datasheet available at
+ * <https://www.st.com/resource/en/datasheet/vl53l0x.pdf>
+ *
+ * Default 7-bit i2c slave address 0x29.
+ *
+ * TODO: FIFO buffer, continuous mode, interrupts, range selection,
+ * sensor ID check.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include <linux/iio/iio.h>
+
+#define VL_REG_SYSRANGE_START 0x00
+
+#define VL_REG_SYSRANGE_MODE_MASK GENMASK(3, 0)
+#define VL_REG_SYSRANGE_MODE_SINGLESHOT 0x00
+#define VL_REG_SYSRANGE_MODE_START_STOP BIT(0)
+#define VL_REG_SYSRANGE_MODE_BACKTOBACK BIT(1)
+#define VL_REG_SYSRANGE_MODE_TIMED BIT(2)
+#define VL_REG_SYSRANGE_MODE_HISTOGRAM BIT(3)
+
+#define VL_REG_RESULT_INT_STATUS 0x13
+#define VL_REG_RESULT_RANGE_STATUS 0x14
+#define VL_REG_RESULT_RANGE_STATUS_COMPLETE BIT(0)
+
+struct vl53l0x_data {
+ struct i2c_client *client;
+};
+
+static int vl53l0x_read_proximity(struct vl53l0x_data *data,
+ const struct iio_chan_spec *chan,
+ int *val)
+{
+ struct i2c_client *client = data->client;
+ u16 tries = 20;
+ u8 buffer[12];
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(client, VL_REG_SYSRANGE_START, 1);
+ if (ret < 0)
+ return ret;
+
+ do {
+ ret = i2c_smbus_read_byte_data(client,
+ VL_REG_RESULT_RANGE_STATUS);
+ if (ret < 0)
+ return ret;
+
+ if (ret & VL_REG_RESULT_RANGE_STATUS_COMPLETE)
+ break;
+
+ usleep_range(1000, 5000);
+ } while (--tries);
+ if (!tries)
+ return -ETIMEDOUT;
+
+ ret = i2c_smbus_read_i2c_block_data(client, VL_REG_RESULT_RANGE_STATUS,
+ 12, buffer);
+ if (ret < 0)
+ return ret;
+ else if (ret != 12)
+ return -EREMOTEIO;
+
+ /* Values should be between 30~1200 in millimeters. */
+ *val = (buffer[10] << 8) + buffer[11];
+
+ return 0;
+}
+
+static const struct iio_chan_spec vl53l0x_channels[] = {
+ {
+ .type = IIO_DISTANCE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int vl53l0x_read_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int *val, int *val2, long mask)
+{
+ struct vl53l0x_data *data = iio_priv(indio_dev);
+ int ret;
+
+ if (chan->type != IIO_DISTANCE)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = vl53l0x_read_proximity(data, chan, val);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 0;
+ *val2 = 1000;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info vl53l0x_info = {
+ .read_raw = vl53l0x_read_raw,
+};
+
+static int vl53l0x_probe(struct i2c_client *client)
+{
+ struct vl53l0x_data *data;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ i2c_set_clientdata(client, indio_dev);
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK |
+ I2C_FUNC_SMBUS_BYTE_DATA))
+ return -EOPNOTSUPP;
+
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->name = "vl53l0x";
+ indio_dev->info = &vl53l0x_info;
+ indio_dev->channels = vl53l0x_channels;
+ indio_dev->num_channels = ARRAY_SIZE(vl53l0x_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct of_device_id st_vl53l0x_dt_match[] = {
+ { .compatible = "st,vl53l0x", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, st_vl53l0x_dt_match);
+
+static struct i2c_driver vl53l0x_driver = {
+ .driver = {
+ .name = "vl53l0x-i2c",
+ .of_match_table = st_vl53l0x_dt_match,
+ },
+ .probe_new = vl53l0x_probe,
+};
+module_i2c_driver(vl53l0x_driver);
+
+MODULE_AUTHOR("Song Qiang <songqiang1304521@gmail.com>");
+MODULE_DESCRIPTION("ST vl53l0x ToF ranging sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/resolver/Kconfig b/drivers/iio/resolver/Kconfig
index 2ced9f2..47dbfea 100644
--- a/drivers/iio/resolver/Kconfig
+++ b/drivers/iio/resolver/Kconfig
@@ -1,8 +1,19 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Resolver/Synchro drivers
#
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 4e1dcca..fa55813 100644
--- a/drivers/iio/resolver/Makefile
+++ b/drivers/iio/resolver/Makefile
@@ -1,5 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Makefile for Resolver/Synchro drivers
#
+obj-$(CONFIG_AD2S90) += ad2s90.o
obj-$(CONFIG_AD2S1200) += ad2s1200.o
diff --git a/drivers/iio/resolver/ad2s1200.c b/drivers/iio/resolver/ad2s1200.c
index 28e618a..17b8962 100644
--- a/drivers/iio/resolver/ad2s1200.c
+++ b/drivers/iio/resolver/ad2s1200.c
@@ -1,13 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* ad2s1200.c simple support for the ADI Resolver to Digital Converters:
* AD2S1200/1205
*
* Copyright (c) 2018-2018 David Veenstra <davidjulianveenstra@gmail.com>
* Copyright (c) 2010-2010 Analog Devices Inc.
- *
- * 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/bitops.h>
diff --git a/drivers/iio/resolver/ad2s90.c b/drivers/iio/resolver/ad2s90.c
new file mode 100644
index 0000000..a41f5cb
--- /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/Kconfig b/drivers/iio/temperature/Kconfig
index 82e4a62..737faa0 100644
--- a/drivers/iio/temperature/Kconfig
+++ b/drivers/iio/temperature/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Temperature sensor drivers
#
@@ -66,14 +67,14 @@
be called tmp006.
config TMP007
- tristate "TMP007 infrared thermopile sensor with Integrated Math Engine"
- depends on I2C
- help
- If you say yes here you get support for the Texas Instruments
- TMP007 infrared thermopile sensor with Integrated Math Engine.
+ tristate "TMP007 infrared thermopile sensor with Integrated Math Engine"
+ depends on I2C
+ help
+ If you say yes here you get support for the Texas Instruments
+ TMP007 infrared thermopile sensor with Integrated Math Engine.
- This driver can also be built as a module. If so, the module will
- be called tmp007.
+ This driver can also be built as a module. If so, the module will
+ be called tmp007.
config TSYS01
tristate "Measurement Specialties TSYS01 temperature sensor using I2C bus connection"
@@ -97,4 +98,14 @@
This driver can also be built as a module. If so, the module will
be called tsys02d.
+config MAX31856
+ tristate "MAX31856 thermocouple sensor"
+ depends on SPI
+ help
+ If you say yes here you get support for MAX31856
+ thermocouple sensor chip connected via SPI.
+
+ This driver can also be built as a module. If so, the module
+ will be called max31856.
+
endmenu
diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile
index 34a31db..baca477 100644
--- a/drivers/iio/temperature/Makefile
+++ b/drivers/iio/temperature/Makefile
@@ -5,6 +5,7 @@
obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o
obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o
+obj-$(CONFIG_MAX31856) += max31856.o
obj-$(CONFIG_MLX90614) += mlx90614.o
obj-$(CONFIG_MLX90632) += mlx90632.o
obj-$(CONFIG_TMP006) += tmp006.o
diff --git a/drivers/iio/temperature/hid-sensor-temperature.c b/drivers/iio/temperature/hid-sensor-temperature.c
index b592fc4..eda55b9 100644
--- a/drivers/iio/temperature/hid-sensor-temperature.c
+++ b/drivers/iio/temperature/hid-sensor-temperature.c
@@ -1,18 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* HID Sensors Driver
* Copyright (c) 2017, Intel Corporation.
- *
- * This program is free software; you can redistribute it and/or modify it
- * under the terms and conditions of the GNU General Public License,
- * version 2, as published by the Free Software Foundation.
- *
- * This program is distributed in the hope 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.
- *
- * You should have received a copy of the GNU General Public License along with
- * this program.
*/
#include <linux/device.h>
#include <linux/hid-sensor-hub.h>
diff --git a/drivers/iio/temperature/max31856.c b/drivers/iio/temperature/max31856.c
new file mode 100644
index 0000000..f184ba5
--- /dev/null
+++ b/drivers/iio/temperature/max31856.c
@@ -0,0 +1,356 @@
+// SPDX-License-Identifier: GPL-2.0
+/* max31856.c
+ *
+ * Maxim MAX31856 thermocouple sensor driver
+ *
+ * Copyright (C) 2018-2019 Rockwell Collins
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <dt-bindings/iio/temperature/thermocouple.h>
+/*
+ * The MSB of the register value determines whether the following byte will
+ * be written or read. If it is 0, one or more byte reads will follow.
+ */
+#define MAX31856_RD_WR_BIT BIT(7)
+
+#define MAX31856_CR0_AUTOCONVERT BIT(7)
+#define MAX31856_CR0_1SHOT BIT(6)
+#define MAX31856_CR0_OCFAULT BIT(4)
+#define MAX31856_CR0_OCFAULT_MASK GENMASK(5, 4)
+#define MAX31856_TC_TYPE_MASK GENMASK(3, 0)
+#define MAX31856_FAULT_OVUV BIT(1)
+#define MAX31856_FAULT_OPEN BIT(0)
+
+/* The MAX31856 registers */
+#define MAX31856_CR0_REG 0x00
+#define MAX31856_CR1_REG 0x01
+#define MAX31856_MASK_REG 0x02
+#define MAX31856_CJHF_REG 0x03
+#define MAX31856_CJLF_REG 0x04
+#define MAX31856_LTHFTH_REG 0x05
+#define MAX31856_LTHFTL_REG 0x06
+#define MAX31856_LTLFTH_REG 0x07
+#define MAX31856_LTLFTL_REG 0x08
+#define MAX31856_CJTO_REG 0x09
+#define MAX31856_CJTH_REG 0x0A
+#define MAX31856_CJTL_REG 0x0B
+#define MAX31856_LTCBH_REG 0x0C
+#define MAX31856_LTCBM_REG 0x0D
+#define MAX31856_LTCBL_REG 0x0E
+#define MAX31856_SR_REG 0x0F
+
+static const struct iio_chan_spec max31856_channels[] = {
+ { /* Thermocouple Temperature */
+ .type = IIO_TEMP,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ },
+ { /* Cold Junction Temperature */
+ .type = IIO_TEMP,
+ .channel2 = IIO_MOD_TEMP_AMBIENT,
+ .modified = 1,
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+struct max31856_data {
+ struct spi_device *spi;
+ u32 thermocouple_type;
+};
+
+static int max31856_read(struct max31856_data *data, u8 reg,
+ u8 val[], unsigned int read_size)
+{
+ return spi_write_then_read(data->spi, ®, 1, val, read_size);
+}
+
+static int max31856_write(struct max31856_data *data, u8 reg,
+ unsigned int val)
+{
+ u8 buf[2];
+
+ buf[0] = reg | (MAX31856_RD_WR_BIT);
+ buf[1] = val;
+
+ return spi_write(data->spi, buf, 2);
+}
+
+static int max31856_init(struct max31856_data *data)
+{
+ int ret;
+ u8 reg_cr0_val, reg_cr1_val;
+
+ /* Start by changing to Off mode before making changes as
+ * some settings are recommended to be set only when the device
+ * is off
+ */
+ ret = max31856_read(data, MAX31856_CR0_REG, ®_cr0_val, 1);
+ if (ret)
+ return ret;
+
+ reg_cr0_val &= ~MAX31856_CR0_AUTOCONVERT;
+ ret = max31856_write(data, MAX31856_CR0_REG, reg_cr0_val);
+ if (ret)
+ return ret;
+
+ /* Set thermocouple type based on dts property */
+ ret = max31856_read(data, MAX31856_CR1_REG, ®_cr1_val, 1);
+ if (ret)
+ return ret;
+
+ reg_cr1_val &= ~MAX31856_TC_TYPE_MASK;
+ reg_cr1_val |= data->thermocouple_type;
+ ret = max31856_write(data, MAX31856_CR1_REG, reg_cr1_val);
+ if (ret)
+ return ret;
+
+ /*
+ * Enable Open circuit fault detection
+ * Read datasheet for more information: Table 4.
+ * Value 01 means : Enabled (Once every 16 conversions)
+ */
+ reg_cr0_val &= ~MAX31856_CR0_OCFAULT_MASK;
+ reg_cr0_val |= MAX31856_CR0_OCFAULT;
+
+ /* Set Auto Conversion Mode */
+ reg_cr0_val &= ~MAX31856_CR0_1SHOT;
+ reg_cr0_val |= MAX31856_CR0_AUTOCONVERT;
+
+ return max31856_write(data, MAX31856_CR0_REG, reg_cr0_val);
+}
+
+static int max31856_thermocouple_read(struct max31856_data *data,
+ struct iio_chan_spec const *chan,
+ int *val)
+{
+ int ret, offset_cjto;
+ u8 reg_val[3];
+
+ switch (chan->channel2) {
+ case IIO_NO_MOD:
+ /*
+ * Multibyte Read
+ * MAX31856_LTCBH_REG, MAX31856_LTCBM_REG, MAX31856_LTCBL_REG
+ */
+ ret = max31856_read(data, MAX31856_LTCBH_REG, reg_val, 3);
+ if (ret)
+ return ret;
+ /* Skip last 5 dead bits of LTCBL */
+ *val = (reg_val[0] << 16 | reg_val[1] << 8 | reg_val[2]) >> 5;
+ /* Check 7th bit of LTCBH reg. value for sign*/
+ if (reg_val[0] & 0x80)
+ *val -= 0x80000;
+ break;
+
+ case IIO_MOD_TEMP_AMBIENT:
+ /*
+ * Multibyte Read
+ * MAX31856_CJTO_REG, MAX31856_CJTH_REG, MAX31856_CJTL_REG
+ */
+ ret = max31856_read(data, MAX31856_CJTO_REG, reg_val, 3);
+ if (ret)
+ return ret;
+ /* Get Cold Junction Temp. offset register value */
+ offset_cjto = reg_val[0];
+ /* Get CJTH and CJTL value and skip last 2 dead bits of CJTL */
+ *val = (reg_val[1] << 8 | reg_val[2]) >> 2;
+ /* As per datasheet add offset into CJTH and CJTL */
+ *val += offset_cjto;
+ /* Check 7th bit of CJTH reg. value for sign */
+ if (reg_val[1] & 0x80)
+ *val -= 0x4000;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = max31856_read(data, MAX31856_SR_REG, reg_val, 1);
+ if (ret)
+ return ret;
+ /* Check for over/under voltage or open circuit fault */
+ if (reg_val[0] & (MAX31856_FAULT_OVUV | MAX31856_FAULT_OPEN))
+ return -EIO;
+
+ return ret;
+}
+
+static int max31856_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct max31856_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = max31856_thermocouple_read(data, chan, val);
+ if (ret)
+ return ret;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->channel2) {
+ case IIO_MOD_TEMP_AMBIENT:
+ /* Cold junction Temp. Data resolution is 0.015625 */
+ *val = 15;
+ *val2 = 625000; /* 1000 * 0.015625 */
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ /* Thermocouple Temp. Data resolution is 0.0078125 */
+ *val = 7;
+ *val2 = 812500; /* 1000 * 0.0078125) */
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static ssize_t show_fault(struct device *dev, u8 faultbit, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct max31856_data *data = iio_priv(indio_dev);
+ u8 reg_val;
+ int ret;
+ bool fault;
+
+ ret = max31856_read(data, MAX31856_SR_REG, ®_val, 1);
+ if (ret)
+ return ret;
+
+ fault = reg_val & faultbit;
+
+ return sprintf(buf, "%d\n", fault);
+}
+
+static ssize_t show_fault_ovuv(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return show_fault(dev, MAX31856_FAULT_OVUV, buf);
+}
+
+static ssize_t show_fault_oc(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ return show_fault(dev, MAX31856_FAULT_OPEN, buf);
+}
+
+static IIO_DEVICE_ATTR(fault_ovuv, 0444, show_fault_ovuv, NULL, 0);
+static IIO_DEVICE_ATTR(fault_oc, 0444, show_fault_oc, NULL, 0);
+
+static struct attribute *max31856_attributes[] = {
+ &iio_dev_attr_fault_ovuv.dev_attr.attr,
+ &iio_dev_attr_fault_oc.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group max31856_group = {
+ .attrs = max31856_attributes,
+};
+
+static const struct iio_info max31856_info = {
+ .read_raw = max31856_read_raw,
+ .attrs = &max31856_group,
+};
+
+static int max31856_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ struct iio_dev *indio_dev;
+ struct max31856_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->spi = spi;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ indio_dev->info = &max31856_info;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = max31856_channels;
+ indio_dev->num_channels = ARRAY_SIZE(max31856_channels);
+
+ ret = of_property_read_u32(spi->dev.of_node, "thermocouple-type",
+ &data->thermocouple_type);
+
+ if (ret) {
+ dev_info(&spi->dev,
+ "Could not read thermocouple type DT property, configuring as a K-Type\n");
+ data->thermocouple_type = THERMOCOUPLE_TYPE_K;
+ }
+
+ /*
+ * no need to translate values as the supported types
+ * have the same value as the #defines
+ */
+ switch (data->thermocouple_type) {
+ case THERMOCOUPLE_TYPE_B:
+ case THERMOCOUPLE_TYPE_E:
+ case THERMOCOUPLE_TYPE_J:
+ case THERMOCOUPLE_TYPE_K:
+ case THERMOCOUPLE_TYPE_N:
+ case THERMOCOUPLE_TYPE_R:
+ case THERMOCOUPLE_TYPE_S:
+ case THERMOCOUPLE_TYPE_T:
+ break;
+ default:
+ dev_err(&spi->dev,
+ "error: thermocouple-type %u not supported by max31856\n"
+ , data->thermocouple_type);
+ return -EINVAL;
+ }
+
+ ret = max31856_init(data);
+ if (ret) {
+ dev_err(&spi->dev, "error: Failed to configure max31856\n");
+ return ret;
+ }
+
+ return devm_iio_device_register(&spi->dev, indio_dev);
+}
+
+static const struct spi_device_id max31856_id[] = {
+ { "max31856", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, max31856_id);
+
+static const struct of_device_id max31856_of_match[] = {
+ { .compatible = "maxim,max31856" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max31856_of_match);
+
+static struct spi_driver max31856_driver = {
+ .driver = {
+ .name = "max31856",
+ .of_match_table = max31856_of_match,
+ },
+ .probe = max31856_probe,
+ .id_table = max31856_id,
+};
+module_spi_driver(max31856_driver);
+
+MODULE_AUTHOR("Paresh Chaudhary <paresh.chaudhary@rockwellcollins.com>");
+MODULE_AUTHOR("Patrick Havelange <patrick.havelange@essensium.com>");
+MODULE_DESCRIPTION("Maxim MAX31856 thermocouple sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c
index c31b963..2ab6828 100644
--- a/drivers/iio/temperature/maxim_thermocouple.c
+++ b/drivers/iio/temperature/maxim_thermocouple.c
@@ -10,6 +10,8 @@
#include <linux/init.h>
#include <linux/mutex.h>
#include <linux/err.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/spi/spi.h>
#include <linux/iio/iio.h>
#include <linux/iio/trigger.h>
@@ -228,31 +230,13 @@
data->spi = spi;
data->chip = chip;
- ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ ret = devm_iio_triggered_buffer_setup(&spi->dev,
+ indio_dev, NULL,
maxim_thermocouple_trigger_handler, NULL);
if (ret)
return ret;
- ret = iio_device_register(indio_dev);
- if (ret)
- goto error_unreg_buffer;
-
- return 0;
-
-error_unreg_buffer:
- iio_triggered_buffer_cleanup(indio_dev);
-
- return ret;
-}
-
-static int maxim_thermocouple_remove(struct spi_device *spi)
-{
- struct iio_dev *indio_dev = spi_get_drvdata(spi);
-
- iio_device_unregister(indio_dev);
- iio_triggered_buffer_cleanup(indio_dev);
-
- return 0;
+ return devm_iio_device_register(&spi->dev, indio_dev);
}
static const struct spi_device_id maxim_thermocouple_id[] = {
@@ -262,12 +246,19 @@
};
MODULE_DEVICE_TABLE(spi, maxim_thermocouple_id);
+static const struct of_device_id maxim_thermocouple_of_match[] = {
+ { .compatible = "maxim,max6675" },
+ { .compatible = "maxim,max31855" },
+ { },
+};
+MODULE_DEVICE_TABLE(of, maxim_thermocouple_of_match);
+
static struct spi_driver maxim_thermocouple_driver = {
.driver = {
.name = MAXIM_THERMOCOUPLE_DRV_NAME,
+ .of_match_table = maxim_thermocouple_of_match,
},
.probe = maxim_thermocouple_probe,
- .remove = maxim_thermocouple_remove,
.id_table = maxim_thermocouple_id,
};
module_spi_driver(maxim_thermocouple_driver);
diff --git a/drivers/iio/temperature/mlx90614.c b/drivers/iio/temperature/mlx90614.c
index 13a4cec..b7c56dd 100644
--- a/drivers/iio/temperature/mlx90614.c
+++ b/drivers/iio/temperature/mlx90614.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* mlx90614.c - Support for Melexis MLX90614 contactless IR temperature sensor
*
@@ -5,10 +6,6 @@
* Copyright (c) 2015 Essensium NV
* Copyright (c) 2015 Melexis
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Driver for the Melexis MLX90614 I2C 16-bit IR thermopile sensor
*
* (7-bit I2C slave address 0x5a, 100KHz bus speed only!)
@@ -20,7 +17,6 @@
* i2c adapter is locked since it cannot be used by other clients. The SCL line
* always has a pull-up so we do not need an extra GPIO to drive it high. If
* the "wakeup" GPIO is not given, power management will be disabled.
- *
*/
#include <linux/err.h>
diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c
index 9851311..eaca6ba 100644
--- a/drivers/iio/temperature/mlx90632.c
+++ b/drivers/iio/temperature/mlx90632.c
@@ -81,6 +81,8 @@
/* Magic constants */
#define MLX90632_ID_MEDICAL 0x0105 /* EEPROM DSPv5 Medical device id */
#define MLX90632_ID_CONSUMER 0x0205 /* EEPROM DSPv5 Consumer device id */
+#define MLX90632_DSP_VERSION 5 /* DSP version */
+#define MLX90632_DSP_MASK GENMASK(7, 0) /* DSP version in EE_VERSION */
#define MLX90632_RESET_CMD 0x0006 /* Reset sensor (address or global) */
#define MLX90632_REF_12 12LL /**< ResCtrlRef value of Ch 1 or Ch 2 */
#define MLX90632_REF_3 12LL /**< ResCtrlRef value of Channel 3 */
@@ -140,7 +142,8 @@
.rd_table = &mlx90632_readable_regs_tbl,
.wr_table = &mlx90632_writeable_regs_tbl,
- .use_single_rw = true,
+ .use_single_read = true,
+ .use_single_write = true,
.reg_format_endian = REGMAP_ENDIAN_BIG,
.val_format_endian = REGMAP_ENDIAN_BIG,
.cache_type = REGCACHE_RBTREE,
@@ -666,10 +669,13 @@
} else if (read == MLX90632_ID_CONSUMER) {
dev_dbg(&client->dev,
"Detected Consumer EEPROM calibration %x\n", read);
+ } else if ((read & MLX90632_DSP_MASK) == MLX90632_DSP_VERSION) {
+ dev_dbg(&client->dev,
+ "Detected Unknown EEPROM calibration %x\n", read);
} else {
dev_err(&client->dev,
- "EEPROM version mismatch %x (expected %x or %x)\n",
- read, MLX90632_ID_CONSUMER, MLX90632_ID_MEDICAL);
+ "Wrong DSP version %x (expected %x)\n",
+ read, MLX90632_DSP_VERSION);
return -EPROTONOSUPPORT;
}
diff --git a/drivers/iio/temperature/tmp006.c b/drivers/iio/temperature/tmp006.c
index a9b5b7c..cc45d83 100644
--- a/drivers/iio/temperature/tmp006.c
+++ b/drivers/iio/temperature/tmp006.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tmp006.c - Support for TI TMP006 IR thermopile sensor
*
* Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Driver for the Texas Instruments I2C 16-bit IR thermopile sensor
*
* (7-bit I2C slave address 0x40, changeable via ADR pins)
diff --git a/drivers/iio/temperature/tmp007.c b/drivers/iio/temperature/tmp007.c
index 0e3f2d4..7df234d 100644
--- a/drivers/iio/temperature/tmp007.c
+++ b/drivers/iio/temperature/tmp007.c
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tmp007.c - Support for TI TMP007 IR thermopile sensor with integrated math engine
*
* Copyright (c) 2017 Manivannan Sadhasivam <manivannanece23@gmail.com>
*
- * This file is subject to the terms and conditions of version 2 of
- * the GNU General Public License. See the file COPYING in the main
- * directory of this archive for more details.
- *
* Driver for the Texas Instruments I2C 16-bit IR thermopile sensor
*
* (7-bit I2C slave address (0x40 - 0x47), changeable via ADR pins)
@@ -15,7 +12,6 @@
* 1. This driver assumes that the sensor has been calibrated beforehand
* 2. Limit threshold events are enabled at the start
* 3. Operating mode: INT
- *
*/
#include <linux/err.h>
diff --git a/drivers/iio/temperature/tsys01.c b/drivers/iio/temperature/tsys01.c
index 3799d00..d41f050 100644
--- a/drivers/iio/temperature/tsys01.c
+++ b/drivers/iio/temperature/tsys01.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tsys01.c - Support for Measurement-Specialties tsys01 temperature sensor
*
* Copyright (c) 2015 Measurement-Specialties
*
- * Licensed under the GPL-2.
- *
* Datasheet:
* http://www.meas-spec.com/downloads/TSYS01_Digital_Temperature_Sensor.pdf
*/
diff --git a/drivers/iio/temperature/tsys02d.c b/drivers/iio/temperature/tsys02d.c
index 9b2e56f..6735af4 100644
--- a/drivers/iio/temperature/tsys02d.c
+++ b/drivers/iio/temperature/tsys02d.c
@@ -1,10 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* tsys02d.c - Support for Measurement-Specialties tsys02d temperature sensor
*
* Copyright (c) 2015 Measurement-Specialties
*
- * Licensed under the GPL-2.
- *
* (7-bit I2C slave address 0x40)
*
* Datasheet:
diff --git a/drivers/iio/trigger/Kconfig b/drivers/iio/trigger/Kconfig
index a633d2c..8cef2f7 100644
--- a/drivers/iio/trigger/Kconfig
+++ b/drivers/iio/trigger/Kconfig
@@ -1,3 +1,4 @@
+# SPDX-License-Identifier: GPL-2.0-only
#
# Industrial I/O standalone triggers
#
diff --git a/drivers/iio/trigger/iio-trig-hrtimer.c b/drivers/iio/trigger/iio-trig-hrtimer.c
index 7accd01..a5e6707 100644
--- a/drivers/iio/trigger/iio-trig-hrtimer.c
+++ b/drivers/iio/trigger/iio-trig-hrtimer.c
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0-only
/**
* The industrial I/O periodic hrtimer trigger driver
*
@@ -6,11 +7,6 @@
* Copyright (C) 2012, Analog Device Inc.
* Author: Lars-Peter Clausen <lars@metafoo.de>
* Copyright (C) 2015, Intel Corporation
- *
- * 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/kernel.h>
#include <linux/slab.h>
diff --git a/drivers/iio/trigger/iio-trig-interrupt.c b/drivers/iio/trigger/iio-trig-interrupt.c
index 171c4ed..94a487c 100644
--- a/drivers/iio/trigger/iio-trig-interrupt.c
+++ b/drivers/iio/trigger/iio-trig-interrupt.c
@@ -1,11 +1,8 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Industrial I/O - generic interrupt based trigger support
*
* Copyright (c) 2008-2013 Jonathan Cameron
- *
- * 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/kernel.h>
diff --git a/drivers/iio/trigger/iio-trig-loop.c b/drivers/iio/trigger/iio-trig-loop.c
index 94a90e0..4a00668 100644
--- a/drivers/iio/trigger/iio-trig-loop.c
+++ b/drivers/iio/trigger/iio-trig-loop.c
@@ -1,8 +1,7 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2016 Jonathan Cameron <jic23@kernel.org>
*
- * Licensed under the GPL-2.
- *
* Based on a mashup of the hrtimer trigger and continuous sampling proposal of
* Gregor Boirie <gregor.boirie@parrot.com>
*
@@ -60,7 +59,7 @@
if (state) {
loop_trig->task = kthread_run(iio_loop_thread,
trig, trig->name);
- if (unlikely(IS_ERR(loop_trig->task))) {
+ if (IS_ERR(loop_trig->task)) {
dev_err(&trig->dev,
"failed to create trigger loop thread\n");
return PTR_ERR(loop_trig->task);
diff --git a/drivers/iio/trigger/iio-trig-sysfs.c b/drivers/iio/trigger/iio-trig-sysfs.c
index 3f0dc9a..e09e580 100644
--- a/drivers/iio/trigger/iio-trig-sysfs.c
+++ b/drivers/iio/trigger/iio-trig-sysfs.c
@@ -1,8 +1,6 @@
+// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright 2011 Analog Devices Inc.
- *
- * Licensed under the GPL-2.
- *
*/
#include <linux/kernel.h>
@@ -222,7 +220,7 @@
}
module_exit(iio_sysfs_trig_exit);
-MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_AUTHOR("Michael Hennerich <michael.hennerich@analog.com>");
MODULE_DESCRIPTION("Sysfs based trigger for the iio subsystem");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:iio-trig-sysfs");
diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c
index ccf1ce6..a5dfe65 100644
--- a/drivers/iio/trigger/stm32-timer-trigger.c
+++ b/drivers/iio/trigger/stm32-timer-trigger.c
@@ -608,86 +608,6 @@
.get = stm32_get_enable_mode
};
-static const char *const stm32_quadrature_modes[] = {
- "channel_A",
- "channel_B",
- "quadrature",
-};
-
-static int stm32_set_quadrature_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- unsigned int mode)
-{
- struct stm32_timer_trigger *priv = iio_priv(indio_dev);
-
- regmap_update_bits(priv->regmap, TIM_SMCR, TIM_SMCR_SMS, mode + 1);
-
- return 0;
-}
-
-static int stm32_get_quadrature_mode(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- struct stm32_timer_trigger *priv = iio_priv(indio_dev);
- u32 smcr;
- int mode;
-
- regmap_read(priv->regmap, TIM_SMCR, &smcr);
- mode = (smcr & TIM_SMCR_SMS) - 1;
- if ((mode < 0) || (mode > ARRAY_SIZE(stm32_quadrature_modes)))
- return -EINVAL;
-
- return mode;
-}
-
-static const struct iio_enum stm32_quadrature_mode_enum = {
- .items = stm32_quadrature_modes,
- .num_items = ARRAY_SIZE(stm32_quadrature_modes),
- .set = stm32_set_quadrature_mode,
- .get = stm32_get_quadrature_mode
-};
-
-static const char *const stm32_count_direction_states[] = {
- "up",
- "down"
-};
-
-static int stm32_set_count_direction(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan,
- unsigned int dir)
-{
- struct stm32_timer_trigger *priv = iio_priv(indio_dev);
- u32 val;
- int mode;
-
- /* In encoder mode, direction is RO (given by TI1/TI2 signals) */
- regmap_read(priv->regmap, TIM_SMCR, &val);
- mode = (val & TIM_SMCR_SMS) - 1;
- if ((mode >= 0) || (mode < ARRAY_SIZE(stm32_quadrature_modes)))
- return -EBUSY;
-
- return regmap_update_bits(priv->regmap, TIM_CR1, TIM_CR1_DIR,
- dir ? TIM_CR1_DIR : 0);
-}
-
-static int stm32_get_count_direction(struct iio_dev *indio_dev,
- const struct iio_chan_spec *chan)
-{
- struct stm32_timer_trigger *priv = iio_priv(indio_dev);
- u32 cr1;
-
- regmap_read(priv->regmap, TIM_CR1, &cr1);
-
- return ((cr1 & TIM_CR1_DIR) ? 1 : 0);
-}
-
-static const struct iio_enum stm32_count_direction_enum = {
- .items = stm32_count_direction_states,
- .num_items = ARRAY_SIZE(stm32_count_direction_states),
- .set = stm32_set_count_direction,
- .get = stm32_get_count_direction
-};
-
static ssize_t stm32_count_get_preset(struct iio_dev *indio_dev,
uintptr_t private,
const struct iio_chan_spec *chan,
@@ -728,10 +648,6 @@
.read = stm32_count_get_preset,
.write = stm32_count_set_preset
},
- IIO_ENUM("count_direction", IIO_SEPARATE, &stm32_count_direction_enum),
- IIO_ENUM_AVAILABLE("count_direction", &stm32_count_direction_enum),
- IIO_ENUM("quadrature_mode", IIO_SEPARATE, &stm32_quadrature_mode_enum),
- IIO_ENUM_AVAILABLE("quadrature_mode", &stm32_quadrature_mode_enum),
IIO_ENUM("enable_mode", IIO_SEPARATE, &stm32_enable_mode_enum),
IIO_ENUM_AVAILABLE("enable_mode", &stm32_enable_mode_enum),
IIO_ENUM("trigger_mode", IIO_SEPARATE, &stm32_trigger_mode_enum),