summaryrefslogtreecommitdiffstats
path: root/drivers/iio/pressure
diff options
context:
space:
mode:
authorDonggeun Yoo <donggeunyoo.kernel@gmail.com>2026-09-02 00:21:26 +0900
committerJonathan Cameron <jonathan.cameron@oss.qualcomm.com>2026-09-07 02:24:53 +0100
commite24328974a88aa8541e94e8bb8dfa2ade1b3c45d (patch)
tree43a2533acc955d11f5cb8be1ecba0ab8ba4cbb1e /drivers/iio/pressure
downloadlinux-stable-e24328974a88aa8541e94e8bb8dfa2ade1b3c45d.tar.gz
linux-stable-e24328974a88aa8541e94e8bb8dfa2ade1b3c45d.zip
iio: proximity: vl53l0x-i2c: claim direct mode for raw readsgrafted
vl53l0x_read_raw() starts a single-shot ranging measurement and reads back the result. Once the triggered buffer is enabled the sensor runs in continuous mode and its data-ready interrupt is routed to the trigger, so a concurrent in_distance_raw read disturbs the streaming setup and never gets its completion, returning -ETIMEDOUT. The original submission claimed direct mode here, but it was dropped during review because the driver had no buffer support at the time [1]. Continuous (buffered) mode was later added without restoring the claim [2], reintroducing the conflict. Reject direct reads while buffered capture is active by claiming direct mode around the measurement, as the vl53l1x sibling already does. Fixes: 762186c6e7b1 ("iio: proximity: vl53l0x-i2c: Added continuous mode support") Link: https://lore.kernel.org/linux-iio/20180911160300.GA9212@himanshu-Vostro-3559/ [1] Link: https://lore.kernel.org/linux-iio/20240909101508.263085-3-abhashkumarjha123@gmail.com/ [2] Signed-off-by: Donggeun Yoo <donggeunyoo.kernel@gmail.com> Cc: stable@vger.kernel.org Signed-off-by: Jonathan Cameron <jonathan.cameron@oss.qualcomm.com>
Diffstat (limited to 'drivers/iio/pressure')
-rw-r--r--drivers/iio/pressure/Kconfig387
-rw-r--r--drivers/iio/pressure/Makefile47
-rw-r--r--drivers/iio/pressure/abp060mg.c287
-rw-r--r--drivers/iio/pressure/abp2030pa.c543
-rw-r--r--drivers/iio/pressure/abp2030pa.h73
-rw-r--r--drivers/iio/pressure/abp2030pa_i2c.c89
-rw-r--r--drivers/iio/pressure/abp2030pa_spi.c66
-rw-r--r--drivers/iio/pressure/adp810.c224
-rw-r--r--drivers/iio/pressure/bmp280-core.c3361
-rw-r--r--drivers/iio/pressure/bmp280-i2c.c65
-rw-r--r--drivers/iio/pressure/bmp280-regmap.c239
-rw-r--r--drivers/iio/pressure/bmp280-spi.c145
-rw-r--r--drivers/iio/pressure/bmp280.h551
-rw-r--r--drivers/iio/pressure/cros_ec_baro.c203
-rw-r--r--drivers/iio/pressure/dlhl60d.c359
-rw-r--r--drivers/iio/pressure/dps310.c912
-rw-r--r--drivers/iio/pressure/hid-sensor-press.c348
-rw-r--r--drivers/iio/pressure/hp03.c292
-rw-r--r--drivers/iio/pressure/hp206c.c421
-rw-r--r--drivers/iio/pressure/hsc030pa.c540
-rw-r--r--drivers/iio/pressure/hsc030pa.h81
-rw-r--r--drivers/iio/pressure/hsc030pa_i2c.c78
-rw-r--r--drivers/iio/pressure/hsc030pa_spi.c60
-rw-r--r--drivers/iio/pressure/icp10100.c653
-rw-r--r--drivers/iio/pressure/mpl115.c255
-rw-r--r--drivers/iio/pressure/mpl115.h26
-rw-r--r--drivers/iio/pressure/mpl115_i2c.c66
-rw-r--r--drivers/iio/pressure/mpl115_spi.c105
-rw-r--r--drivers/iio/pressure/mpl3115.c811
-rw-r--r--drivers/iio/pressure/mprls0025pa.c443
-rw-r--r--drivers/iio/pressure/mprls0025pa.h90
-rw-r--r--drivers/iio/pressure/mprls0025pa_i2c.c89
-rw-r--r--drivers/iio/pressure/mprls0025pa_spi.c80
-rw-r--r--drivers/iio/pressure/ms5611.h58
-rw-r--r--drivers/iio/pressure/ms5611_core.c454
-rw-r--r--drivers/iio/pressure/ms5611_i2c.c134
-rw-r--r--drivers/iio/pressure/ms5611_spi.c135
-rw-r--r--drivers/iio/pressure/ms5637.c250
-rw-r--r--drivers/iio/pressure/rohm-bm1390.c922
-rw-r--r--drivers/iio/pressure/sdp500.c155
-rw-r--r--drivers/iio/pressure/st_pressure.h58
-rw-r--r--drivers/iio/pressure/st_pressure_buffer.c45
-rw-r--r--drivers/iio/pressure/st_pressure_core.c798
-rw-r--r--drivers/iio/pressure/st_pressure_i2c.c123
-rw-r--r--drivers/iio/pressure/st_pressure_spi.c125
-rw-r--r--drivers/iio/pressure/t5403.c270
-rw-r--r--drivers/iio/pressure/zpa2326.c1708
-rw-r--r--drivers/iio/pressure/zpa2326.h81
-rw-r--r--drivers/iio/pressure/zpa2326_i2c.c86
-rw-r--r--drivers/iio/pressure/zpa2326_spi.c89
50 files changed, 17480 insertions, 0 deletions
diff --git a/drivers/iio/pressure/Kconfig b/drivers/iio/pressure/Kconfig
new file mode 100644
index 000000000..838a8340c
--- /dev/null
+++ b/drivers/iio/pressure/Kconfig
@@ -0,0 +1,387 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Pressure drivers
+#
+# When adding new entries keep the list in alphabetical order
+
+menu "Pressure sensors"
+
+config ABP060MG
+ tristate "Honeywell ABP pressure sensor driver"
+ depends on I2C
+ help
+ Say yes here to build support for the Honeywell ABP pressure
+ sensors.
+
+ To compile this driver as a module, choose M here: the module
+ will be called abp060mg.
+
+config ABP2030PA
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config ABP2030PA_I2C
+ tristate "Honeywell ABP2 pressure sensor series I2C driver"
+ depends on I2C
+ select ABP2030PA
+ help
+ Say Y here to build I2C bus support for the Honeywell ABP2
+ series pressure and temperature digital sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called abp2030pa_i2c and you will also get abp2030pa
+ for the core module.
+
+config ABP2030PA_SPI
+ tristate "Honeywell ABP2 pressure sensor series SPI driver"
+ depends on SPI_MASTER
+ select ABP2030PA
+ help
+ Say Y here to build SPI bus support for the Honeywell ABP2
+ series pressure and temperature digital sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called abp2030pa_spi and you will also get abp2030pa
+ for the core module.
+
+config ROHM_BM1390
+ tristate "ROHM BM1390GLV-Z pressure sensor driver"
+ depends on I2C
+ select REGMAP_I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Support for the ROHM BM1390 pressure sensor. The BM1390GLV-Z
+ can measure pressures ranging from 300 hPa to 1300 hPa with
+ configurable measurement averaging and internal FIFO. The
+ sensor does also provide temperature measurements.
+
+config BMP280
+ tristate "Bosch Sensortec BMP180/BMP280/BMP380/BMP580 pressure sensor driver"
+ depends on (I2C || SPI_MASTER)
+ select REGMAP
+ select BMP280_I2C if (I2C)
+ select BMP280_SPI if (SPI_MASTER)
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for Bosch Sensortec BMP180, BMP280, BMP380
+ and BMP580 pressure and temperature sensors. Also supports the BME280 with
+ an additional humidity sensor channel.
+
+ To compile this driver as a module, choose M here: the core module
+ will be called bmp280 and you will also get bmp280-i2c for I2C
+ and/or bmp280-spi for SPI support.
+
+config BMP280_I2C
+ tristate
+ depends on BMP280
+ depends on I2C
+ select REGMAP_I2C
+
+config BMP280_SPI
+ tristate
+ depends on BMP280
+ depends on SPI_MASTER
+ select REGMAP
+
+config IIO_CROS_EC_BARO
+ tristate "ChromeOS EC Barometer Sensor"
+ depends on IIO_CROS_EC_SENSORS_CORE
+ help
+ Say yes here to build support for the Barometer sensor when
+ presented by the ChromeOS EC Sensor hub.
+
+ To compile this driver as a module, choose M here: the module
+ will be called cros_ec_baro.
+
+config DLHL60D
+ tristate "All Sensors DLHL60D and DLHL60G low voltage digital pressure sensors"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the All Sensors DLH series
+ pressure sensors driver.
+
+ To compile this driver as a module, choose M here: the module
+ will be called dlhl60d.
+
+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
+ select HID_SENSOR_IIO_COMMON
+ select HID_SENSOR_IIO_TRIGGER
+ tristate "HID PRESS"
+ help
+ Say yes here to build support for the HID SENSOR
+ Pressure driver
+
+ To compile this driver as a module, choose M here: the module
+ will be called hid-sensor-press.
+
+config HP03
+ tristate "Hope RF HP03 temperature and pressure sensor driver"
+ depends on I2C
+ select REGMAP_I2C
+ help
+ Say yes here to build support for Hope RF HP03 pressure and
+ temperature sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called hp03.
+
+config HSC030PA
+ tristate "Honeywell HSC/SSC TruStability pressure sensor series"
+ depends on (I2C || SPI_MASTER)
+ select HSC030PA_I2C if I2C
+ select HSC030PA_SPI if SPI_MASTER
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say Y here to build support for the Honeywell TruStability
+ HSC and SSC pressure and temperature sensor series.
+
+ To compile this driver as a module, choose M here: the module
+ will be called hsc030pa.
+
+config HSC030PA_I2C
+ tristate
+ depends on HSC030PA
+ depends on I2C
+
+config HSC030PA_SPI
+ tristate
+ depends on HSC030PA
+ depends on SPI_MASTER
+
+config ICP10100
+ tristate "InvenSense ICP-101xx pressure and temperature sensor"
+ depends on I2C
+ select CRC8
+ help
+ Say yes here to build support for InvenSense ICP-101xx barometric
+ pressure and temperature sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called icp10100.
+
+config MPL115
+ tristate
+
+config MPL115_I2C
+ tristate "Freescale MPL115A2 pressure sensor driver"
+ depends on I2C
+ select MPL115
+ help
+ Say yes here to build support for the Freescale MPL115A2
+ pressure sensor connected via I2C.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mpl115_i2c.
+
+config MPL115_SPI
+ tristate "Freescale MPL115A1 pressure sensor driver"
+ depends on SPI_MASTER
+ select MPL115
+ help
+ Say yes here to build support for the Freescale MPL115A1
+ pressure sensor connected via SPI.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mpl115_spi.
+
+config MPL3115
+ tristate "Freescale MPL3115A2 pressure sensor driver"
+ depends on I2C
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say yes here to build support for the Freescale MPL3115A2
+ pressure sensor / altimeter.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mpl3115.
+
+config MPRLS0025PA
+ tristate
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+
+config MPRLS0025PA_I2C
+ tristate "Honeywell MPR pressure sensor series I2C driver"
+ depends on I2C
+ select MPRLS0025PA
+ help
+ Say Y here to build I2C bus support for the Honeywell MicroPressure
+ series sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mprls0025pa_i2c and you will also get mprls0025pa
+ for the core module.
+
+config MPRLS0025PA_SPI
+ tristate "Honeywell MPR pressure sensor series SPI driver"
+ depends on SPI_MASTER
+ select MPRLS0025PA
+ help
+ Say Y here to build SPI bus support for the Honeywell MicroPressure
+ series sensor.
+
+ To compile this driver as a module, choose M here: the module
+ will be called mprls0025pa_spi and you will also get mprls0025pa
+ for the core module.
+
+config MS5611
+ tristate "Measurement Specialties MS5611 pressure sensor driver"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ help
+ Say Y here to build support for the Measurement Specialties
+ MS5611, MS5607 pressure and temperature sensors.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ms5611_core.
+
+config MS5611_I2C
+ tristate "support I2C bus connection"
+ depends on I2C && MS5611
+ help
+ Say Y here to build I2C bus support for MS5611.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ms5611_i2c.
+
+config MS5611_SPI
+ tristate "support SPI bus connection"
+ depends on SPI_MASTER && MS5611
+ help
+ Say Y here to build SPI bus support for MS5611.
+
+ To compile this driver as a module, choose M here: the module will
+ be called ms5611_spi.
+
+config MS5637
+ tristate "Measurement Specialties MS5637 pressure & temperature sensor"
+ depends on I2C
+ select IIO_MS_SENSORS_I2C
+ help
+ If you say yes here you get support for the Measurement Specialties
+ MS5637 pressure and temperature sensor.
+ This driver is also used for MS8607 temperature, pressure & humidity
+ sensor
+
+ This driver can also be built as a module. If so, the module will
+ be called ms5637.
+
+config SDP500
+ tristate "Sensirion SDP500 differential pressure sensor I2C driver"
+ depends on I2C
+ select CRC8
+ help
+ Say Y here to build support for Sensirion SDP500 differential pressure
+ sensor I2C driver.
+ To compile this driver as a module, choose M here: the core module
+ will be called sdp500.
+
+config IIO_ST_PRESS
+ tristate "STMicroelectronics pressure sensor Driver"
+ depends on (I2C || SPI_MASTER) && SYSFS
+ select IIO_ST_SENSORS_CORE
+ select IIO_TRIGGERED_BUFFER if (IIO_BUFFER)
+ help
+ Say yes here to build support for STMicroelectronics pressure
+ sensors: LPS001WP, LPS25H, LPS331AP, LPS22HB, LPS22HH.
+
+ Also need to enable at least one of I2C and SPI interface drivers
+ below.
+
+config IIO_ST_PRESS_I2C
+ tristate "STMicroelectronics pressure sensor I2C Interface"
+ depends on I2C && IIO_ST_PRESS
+ default I2C && IIO_ST_PRESS
+ select IIO_ST_SENSORS_I2C
+ help
+ Build support for STMicroelectronics pressure sensor I2C interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_pressure_i2c.
+
+config IIO_ST_PRESS_SPI
+ tristate "STMicroelectronics pressure sensor SPI Interface"
+ depends on SPI_MASTER && IIO_ST_PRESS
+ default SPI_MASTER && IIO_ST_PRESS
+ select IIO_ST_SENSORS_SPI
+ help
+ Build support for STMicroelectronics pressure sensor SPI interface.
+
+ To compile this driver as a module, choose M here. The module
+ will be called st_pressure_spi.
+
+config T5403
+ tristate "EPCOS T5403 digital barometric pressure sensor driver"
+ depends on I2C
+ help
+ Say yes here to build support for the EPCOS T5403 pressure sensor
+ connected via I2C.
+
+ To compile this driver as a module, choose M here: the module
+ will be called t5403.
+
+config HP206C
+ tristate "HOPERF HP206C precision barometer and altimeter sensor"
+ depends on I2C
+ help
+ Say yes here to build support for the HOPREF HP206C precision
+ barometer and altimeter sensor.
+
+ This driver can also be built as a module. If so, the module will
+ be called hp206c.
+
+config ZPA2326
+ tristate "Murata ZPA2326 pressure sensor driver"
+ select IIO_BUFFER
+ select IIO_TRIGGERED_BUFFER
+ select REGMAP
+ select ZPA2326_I2C if I2C
+ select ZPA2326_SPI if SPI_MASTER
+ help
+ Say Y here to build support for the Murata ZPA2326 pressure and
+ temperature sensor.
+
+ To compile this driver as a module, choose M here: the module will
+ be called zpa2326.
+
+config ZPA2326_I2C
+ tristate
+ select REGMAP_I2C
+
+config ZPA2326_SPI
+ tristate
+ select REGMAP_SPI
+
+config ADP810
+ tristate "Aosong adp810 differential pressure and temperature sensor"
+ depends on I2C
+ select CRC8
+ help
+ Say yes here to build adp810 differential pressure and temperature
+ sensor driver. ADP810 can measure pressure range up to 500Pa.
+ It supports an I2C interface for data communication.
+
+ To compile this driver as a module, choose M here: the module will
+ be called adp810
+
+endmenu
diff --git a/drivers/iio/pressure/Makefile b/drivers/iio/pressure/Makefile
new file mode 100644
index 000000000..bc0d11a20
--- /dev/null
+++ b/drivers/iio/pressure/Makefile
@@ -0,0 +1,47 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for industrial I/O pressure drivers
+#
+
+# When adding new entries keep the list in alphabetical order
+obj-$(CONFIG_ABP060MG) += abp060mg.o
+obj-$(CONFIG_ABP2030PA) += abp2030pa.o
+obj-$(CONFIG_ABP2030PA_I2C) += abp2030pa_i2c.o
+obj-$(CONFIG_ABP2030PA_SPI) += abp2030pa_spi.o
+obj-$(CONFIG_ADP810) += adp810.o
+obj-$(CONFIG_ROHM_BM1390) += rohm-bm1390.o
+obj-$(CONFIG_BMP280) += bmp280.o
+bmp280-objs := bmp280-core.o bmp280-regmap.o
+obj-$(CONFIG_BMP280_I2C) += bmp280-i2c.o
+obj-$(CONFIG_BMP280_SPI) += bmp280-spi.o
+obj-$(CONFIG_DLHL60D) += dlhl60d.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
+obj-$(CONFIG_HP206C) += hp206c.o
+obj-$(CONFIG_HSC030PA) += hsc030pa.o
+obj-$(CONFIG_HSC030PA_I2C) += hsc030pa_i2c.o
+obj-$(CONFIG_HSC030PA_SPI) += hsc030pa_spi.o
+obj-$(CONFIG_ICP10100) += icp10100.o
+obj-$(CONFIG_MPL115) += mpl115.o
+obj-$(CONFIG_MPL115_I2C) += mpl115_i2c.o
+obj-$(CONFIG_MPL115_SPI) += mpl115_spi.o
+obj-$(CONFIG_MPL3115) += mpl3115.o
+obj-$(CONFIG_MPRLS0025PA) += mprls0025pa.o
+obj-$(CONFIG_MPRLS0025PA_I2C) += mprls0025pa_i2c.o
+obj-$(CONFIG_MPRLS0025PA_SPI) += mprls0025pa_spi.o
+obj-$(CONFIG_MS5611) += ms5611_core.o
+obj-$(CONFIG_MS5611_I2C) += ms5611_i2c.o
+obj-$(CONFIG_MS5611_SPI) += ms5611_spi.o
+obj-$(CONFIG_MS5637) += ms5637.o
+obj-$(CONFIG_SDP500) += sdp500.o
+obj-$(CONFIG_IIO_ST_PRESS) += st_pressure.o
+st_pressure-y := st_pressure_core.o
+st_pressure-$(CONFIG_IIO_BUFFER) += st_pressure_buffer.o
+obj-$(CONFIG_IIO_ST_PRESS_I2C) += st_pressure_i2c.o
+obj-$(CONFIG_IIO_ST_PRESS_SPI) += st_pressure_spi.o
+obj-$(CONFIG_T5403) += t5403.o
+obj-$(CONFIG_ZPA2326) += zpa2326.o
+obj-$(CONFIG_ZPA2326_I2C) += zpa2326_i2c.o
+obj-$(CONFIG_ZPA2326_SPI) += zpa2326_spi.o
diff --git a/drivers/iio/pressure/abp060mg.c b/drivers/iio/pressure/abp060mg.c
new file mode 100644
index 000000000..fd48bed35
--- /dev/null
+++ b/drivers/iio/pressure/abp060mg.c
@@ -0,0 +1,287 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 - Marcin Malagowski <mrc@bourne.st>
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/i2c.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+
+#define ABP060MG_ERROR_MASK 0xC000
+#define ABP060MG_RESP_TIME_MS 40
+#define ABP060MG_MIN_COUNTS 1638 /* = 0x0666 (10% of u14) */
+#define ABP060MG_MAX_COUNTS 14745 /* = 0x3999 (90% of u14) */
+#define ABP060MG_NUM_COUNTS (ABP060MG_MAX_COUNTS - ABP060MG_MIN_COUNTS)
+
+enum abp_variant {
+ /* gage [kPa] */
+ ABP006KG, ABP010KG, ABP016KG, ABP025KG, ABP040KG, ABP060KG, ABP100KG,
+ ABP160KG, ABP250KG, ABP400KG, ABP600KG, ABP001GG,
+ /* differential [kPa] */
+ ABP006KD, ABP010KD, ABP016KD, ABP025KD, ABP040KD, ABP060KD, ABP100KD,
+ ABP160KD, ABP250KD, ABP400KD,
+ /* gage [psi] */
+ ABP001PG, ABP005PG, ABP015PG, ABP030PG, ABP060PG, ABP100PG, ABP150PG,
+ /* differential [psi] */
+ ABP001PD, ABP005PD, ABP015PD, ABP030PD, ABP060PD,
+};
+
+struct abp_config {
+ int min;
+ int max;
+};
+
+static const struct abp_config abp_config[] = {
+ /* mbar & kPa variants */
+ [ABP006KG] = { .min = 0, .max = 6000 },
+ [ABP010KG] = { .min = 0, .max = 10000 },
+ [ABP016KG] = { .min = 0, .max = 16000 },
+ [ABP025KG] = { .min = 0, .max = 25000 },
+ [ABP040KG] = { .min = 0, .max = 40000 },
+ [ABP060KG] = { .min = 0, .max = 60000 },
+ [ABP100KG] = { .min = 0, .max = 100000 },
+ [ABP160KG] = { .min = 0, .max = 160000 },
+ [ABP250KG] = { .min = 0, .max = 250000 },
+ [ABP400KG] = { .min = 0, .max = 400000 },
+ [ABP600KG] = { .min = 0, .max = 600000 },
+ [ABP001GG] = { .min = 0, .max = 1000000 },
+ [ABP006KD] = { .min = -6000, .max = 6000 },
+ [ABP010KD] = { .min = -10000, .max = 10000 },
+ [ABP016KD] = { .min = -16000, .max = 16000 },
+ [ABP025KD] = { .min = -25000, .max = 25000 },
+ [ABP040KD] = { .min = -40000, .max = 40000 },
+ [ABP060KD] = { .min = -60000, .max = 60000 },
+ [ABP100KD] = { .min = -100000, .max = 100000 },
+ [ABP160KD] = { .min = -160000, .max = 160000 },
+ [ABP250KD] = { .min = -250000, .max = 250000 },
+ [ABP400KD] = { .min = -400000, .max = 400000 },
+ /* psi variants (1 psi ~ 6895 Pa) */
+ [ABP001PG] = { .min = 0, .max = 6985 },
+ [ABP005PG] = { .min = 0, .max = 34474 },
+ [ABP015PG] = { .min = 0, .max = 103421 },
+ [ABP030PG] = { .min = 0, .max = 206843 },
+ [ABP060PG] = { .min = 0, .max = 413686 },
+ [ABP100PG] = { .min = 0, .max = 689476 },
+ [ABP150PG] = { .min = 0, .max = 1034214 },
+ [ABP001PD] = { .min = -6895, .max = 6895 },
+ [ABP005PD] = { .min = -34474, .max = 34474 },
+ [ABP015PD] = { .min = -103421, .max = 103421 },
+ [ABP030PD] = { .min = -206843, .max = 206843 },
+ [ABP060PD] = { .min = -413686, .max = 413686 },
+};
+
+struct abp_state {
+ struct i2c_client *client;
+ struct mutex lock;
+
+ /*
+ * bus-dependent MEASURE_REQUEST length.
+ * If no SMBUS_QUICK support, need to send dummy byte
+ */
+ int mreq_len;
+
+ /* model-dependent values (calculated on probe) */
+ int scale;
+ int offset;
+};
+
+static const struct iio_chan_spec abp060mg_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int abp060mg_get_measurement(struct abp_state *state, int *val)
+{
+ struct i2c_client *client = state->client;
+ __be16 buf[2];
+ u16 pressure;
+ int ret;
+
+ buf[0] = 0;
+ ret = i2c_master_send(client, (u8 *)&buf, state->mreq_len);
+ if (ret < 0)
+ return ret;
+
+ msleep_interruptible(ABP060MG_RESP_TIME_MS);
+
+ ret = i2c_master_recv(client, (u8 *)&buf, sizeof(buf));
+ if (ret < 0)
+ return ret;
+
+ pressure = be16_to_cpu(buf[0]);
+ if (pressure & ABP060MG_ERROR_MASK)
+ return -EIO;
+
+ if (pressure < ABP060MG_MIN_COUNTS || pressure > ABP060MG_MAX_COUNTS)
+ return -EIO;
+
+ *val = pressure;
+
+ return IIO_VAL_INT;
+}
+
+static int abp060mg_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ struct abp_state *state = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&state->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = abp060mg_get_measurement(state, val);
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = state->offset;
+ ret = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = state->scale;
+ *val2 = ABP060MG_NUM_COUNTS * 1000; /* to kPa */
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ mutex_unlock(&state->lock);
+ return ret;
+}
+
+static const struct iio_info abp060mg_info = {
+ .read_raw = abp060mg_read_raw,
+};
+
+static void abp060mg_init_device(struct iio_dev *indio_dev, unsigned long id)
+{
+ struct abp_state *state = iio_priv(indio_dev);
+ const struct abp_config *cfg = &abp_config[id];
+
+ state->scale = cfg->max - cfg->min;
+ state->offset = -ABP060MG_MIN_COUNTS;
+
+ if (cfg->min < 0) /* differential */
+ state->offset -= ABP060MG_NUM_COUNTS >> 1;
+}
+
+static int abp060mg_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct iio_dev *indio_dev;
+ struct abp_state *state;
+ unsigned long cfg_id = id->driver_data;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ state = iio_priv(indio_dev);
+ i2c_set_clientdata(client, state);
+ state->client = client;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_QUICK))
+ state->mreq_len = 1;
+
+ abp060mg_init_device(indio_dev, cfg_id);
+
+ indio_dev->name = dev_name(&client->dev);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &abp060mg_info;
+
+ indio_dev->channels = abp060mg_channels;
+ indio_dev->num_channels = ARRAY_SIZE(abp060mg_channels);
+
+ mutex_init(&state->lock);
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id abp060mg_id_table[] = {
+ /* mbar & kPa variants (abp060m [60 mbar] == abp006k [6 kPa]) */
+ /* gage: */
+ { .name = "abp060mg", .driver_data = ABP006KG },
+ { .name = "abp006kg", .driver_data = ABP006KG },
+ { .name = "abp100mg", .driver_data = ABP010KG },
+ { .name = "abp010kg", .driver_data = ABP010KG },
+ { .name = "abp160mg", .driver_data = ABP016KG },
+ { .name = "abp016kg", .driver_data = ABP016KG },
+ { .name = "abp250mg", .driver_data = ABP025KG },
+ { .name = "abp025kg", .driver_data = ABP025KG },
+ { .name = "abp400mg", .driver_data = ABP040KG },
+ { .name = "abp040kg", .driver_data = ABP040KG },
+ { .name = "abp600mg", .driver_data = ABP060KG },
+ { .name = "abp060kg", .driver_data = ABP060KG },
+ { .name = "abp001bg", .driver_data = ABP100KG },
+ { .name = "abp100kg", .driver_data = ABP100KG },
+ { .name = "abp1_6bg", .driver_data = ABP160KG },
+ { .name = "abp160kg", .driver_data = ABP160KG },
+ { .name = "abp2_5bg", .driver_data = ABP250KG },
+ { .name = "abp250kg", .driver_data = ABP250KG },
+ { .name = "abp004bg", .driver_data = ABP400KG },
+ { .name = "abp400kg", .driver_data = ABP400KG },
+ { .name = "abp006bg", .driver_data = ABP600KG },
+ { .name = "abp600kg", .driver_data = ABP600KG },
+ { .name = "abp010bg", .driver_data = ABP001GG },
+ { .name = "abp001gg", .driver_data = ABP001GG },
+ /* differential: */
+ { .name = "abp060md", .driver_data = ABP006KD },
+ { .name = "abp006kd", .driver_data = ABP006KD },
+ { .name = "abp100md", .driver_data = ABP010KD },
+ { .name = "abp010kd", .driver_data = ABP010KD },
+ { .name = "abp160md", .driver_data = ABP016KD },
+ { .name = "abp016kd", .driver_data = ABP016KD },
+ { .name = "abp250md", .driver_data = ABP025KD },
+ { .name = "abp025kd", .driver_data = ABP025KD },
+ { .name = "abp400md", .driver_data = ABP040KD },
+ { .name = "abp040kd", .driver_data = ABP040KD },
+ { .name = "abp600md", .driver_data = ABP060KD },
+ { .name = "abp060kd", .driver_data = ABP060KD },
+ { .name = "abp001bd", .driver_data = ABP100KD },
+ { .name = "abp100kd", .driver_data = ABP100KD },
+ { .name = "abp1_6bd", .driver_data = ABP160KD },
+ { .name = "abp160kd", .driver_data = ABP160KD },
+ { .name = "abp2_5bd", .driver_data = ABP250KD },
+ { .name = "abp250kd", .driver_data = ABP250KD },
+ { .name = "abp004bd", .driver_data = ABP400KD },
+ { .name = "abp400kd", .driver_data = ABP400KD },
+ /* psi variants */
+ /* gage: */
+ { .name = "abp001pg", .driver_data = ABP001PG },
+ { .name = "abp005pg", .driver_data = ABP005PG },
+ { .name = "abp015pg", .driver_data = ABP015PG },
+ { .name = "abp030pg", .driver_data = ABP030PG },
+ { .name = "abp060pg", .driver_data = ABP060PG },
+ { .name = "abp100pg", .driver_data = ABP100PG },
+ { .name = "abp150pg", .driver_data = ABP150PG },
+ /* differential: */
+ { .name = "abp001pd", .driver_data = ABP001PD },
+ { .name = "abp005pd", .driver_data = ABP005PD },
+ { .name = "abp015pd", .driver_data = ABP015PD },
+ { .name = "abp030pd", .driver_data = ABP030PD },
+ { .name = "abp060pd", .driver_data = ABP060PD },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, abp060mg_id_table);
+
+static struct i2c_driver abp060mg_driver = {
+ .driver = {
+ .name = "abp060mg",
+ },
+ .probe = abp060mg_probe,
+ .id_table = abp060mg_id_table,
+};
+module_i2c_driver(abp060mg_driver);
+
+MODULE_AUTHOR("Marcin Malagowski <mrc@bourne.st>");
+MODULE_DESCRIPTION("Honeywell ABP pressure sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/abp2030pa.c b/drivers/iio/pressure/abp2030pa.c
new file mode 100644
index 000000000..41395036e
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa.c
@@ -0,0 +1,543 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ *
+ * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/basic-abp2-series/documents/sps-siot-abp2-series-datasheet-32350268-en.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/dev_printk.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+#include <linux/units.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include "abp2030pa.h"
+
+/* Status byte flags */
+#define ABP2_ST_POWER BIT(6) /* 1 if device is powered */
+#define ABP2_ST_BUSY BIT(5) /* 1 if device is busy */
+
+#define ABP2_CMD_NOP 0xf0
+#define ABP2_CMD_SYNC 0xaa
+#define ABP2_PKT_SYNC_LEN 3
+#define ABP2_PKT_NOP_LEN ABP2_MEASUREMENT_RD_SIZE
+
+struct abp2_func_spec {
+ u32 output_min;
+ u32 output_max;
+};
+
+/* transfer function A: 10% to 90% of 2^24 */
+static const struct abp2_func_spec abp2_func_spec[] = {
+ [ABP2_FUNCTION_A] = { .output_min = 1677722, .output_max = 15099494 },
+};
+
+enum abp2_variants {
+ ABP2001BA, ABP21_6BA, ABP22_5BA, ABP2004BA, ABP2006BA, ABP2008BA,
+ ABP2010BA, ABP2012BA, ABP2001BD, ABP21_6BD, ABP22_5BD, ABP2004BD,
+ ABP2001BG, ABP21_6BG, ABP22_5BG, ABP2004BG, ABP2006BG, ABP2008BG,
+ ABP2010BG, ABP2012BG, ABP2001GG, ABP21_2GG, ABP2100KA, ABP2160KA,
+ ABP2250KA, ABP2001KD, ABP21_6KD, ABP22_5KD, ABP2004KD, ABP2006KD,
+ ABP2010KD, ABP2016KD, ABP2025KD, ABP2040KD, ABP2060KD, ABP2100KD,
+ ABP2160KD, ABP2250KD, ABP2400KD, ABP2001KG, ABP21_6KG, ABP22_5KG,
+ ABP2004KG, ABP2006KG, ABP2010KG, ABP2016KG, ABP2025KG, ABP2040KG,
+ ABP2060KG, ABP2100KG, ABP2160KG, ABP2250KG, ABP2400KG, ABP2600KG,
+ ABP2800KG, ABP2250LD, ABP2600LD, ABP2600LG, ABP22_5MD, ABP2006MD,
+ ABP2010MD, ABP2016MD, ABP2025MD, ABP2040MD, ABP2060MD, ABP2100MD,
+ ABP2160MD, ABP2250MD, ABP2400MD, ABP2600MD, ABP2006MG, ABP2010MG,
+ ABP2016MG, ABP2025MG, ABP2040MG, ABP2060MG, ABP2100MG, ABP2160MG,
+ ABP2250MG, ABP2400MG, ABP2600MG, ABP2001ND, ABP2002ND, ABP2004ND,
+ ABP2005ND, ABP2010ND, ABP2020ND, ABP2030ND, ABP2002NG, ABP2004NG,
+ ABP2005NG, ABP2010NG, ABP2020NG, ABP2030NG, ABP2015PA, ABP2030PA,
+ ABP2060PA, ABP2100PA, ABP2150PA, ABP2175PA, ABP2001PD, ABP2005PD,
+ ABP2015PD, ABP2030PD, ABP2060PD, ABP2001PG, ABP2005PG, ABP2015PG,
+ ABP2030PG, ABP2060PG, ABP2100PG, ABP2150PG, ABP2175PG,
+};
+
+static const char * const abp2_triplet_variants[] = {
+ [ABP2001BA] = "001BA", [ABP21_6BA] = "1.6BA", [ABP22_5BA] = "2.5BA",
+ [ABP2004BA] = "004BA", [ABP2006BA] = "006BA", [ABP2008BA] = "008BA",
+ [ABP2010BA] = "010BA", [ABP2012BA] = "012BA", [ABP2001BD] = "001BD",
+ [ABP21_6BD] = "1.6BD", [ABP22_5BD] = "2.5BD", [ABP2004BD] = "004BD",
+ [ABP2001BG] = "001BG", [ABP21_6BG] = "1.6BG", [ABP22_5BG] = "2.5BG",
+ [ABP2004BG] = "004BG", [ABP2006BG] = "006BG", [ABP2008BG] = "008BG",
+ [ABP2010BG] = "010BG", [ABP2012BG] = "012BG", [ABP2001GG] = "001GG",
+ [ABP21_2GG] = "1.2GG", [ABP2100KA] = "100KA", [ABP2160KA] = "160KA",
+ [ABP2250KA] = "250KA", [ABP2001KD] = "001KD", [ABP21_6KD] = "1.6KD",
+ [ABP22_5KD] = "2.5KD", [ABP2004KD] = "004KD", [ABP2006KD] = "006KD",
+ [ABP2010KD] = "010KD", [ABP2016KD] = "016KD", [ABP2025KD] = "025KD",
+ [ABP2040KD] = "040KD", [ABP2060KD] = "060KD", [ABP2100KD] = "100KD",
+ [ABP2160KD] = "160KD", [ABP2250KD] = "250KD", [ABP2400KD] = "400KD",
+ [ABP2001KG] = "001KG", [ABP21_6KG] = "1.6KG", [ABP22_5KG] = "2.5KG",
+ [ABP2004KG] = "004KG", [ABP2006KG] = "006KG", [ABP2010KG] = "010KG",
+ [ABP2016KG] = "016KG", [ABP2025KG] = "025KG", [ABP2040KG] = "040KG",
+ [ABP2060KG] = "060KG", [ABP2100KG] = "100KG", [ABP2160KG] = "160KG",
+ [ABP2250KG] = "250KG", [ABP2400KG] = "400KG", [ABP2600KG] = "600KG",
+ [ABP2800KG] = "800KG", [ABP2250LD] = "250LD", [ABP2600LD] = "600LD",
+ [ABP2600LG] = "600LG", [ABP22_5MD] = "2.5MD", [ABP2006MD] = "006MD",
+ [ABP2010MD] = "010MD", [ABP2016MD] = "016MD", [ABP2025MD] = "025MD",
+ [ABP2040MD] = "040MD", [ABP2060MD] = "060MD", [ABP2100MD] = "100MD",
+ [ABP2160MD] = "160MD", [ABP2250MD] = "250MD", [ABP2400MD] = "400MD",
+ [ABP2600MD] = "600MD", [ABP2006MG] = "006MG", [ABP2010MG] = "010MG",
+ [ABP2016MG] = "016MG", [ABP2025MG] = "025MG", [ABP2040MG] = "040MG",
+ [ABP2060MG] = "060MG", [ABP2100MG] = "100MG", [ABP2160MG] = "160MG",
+ [ABP2250MG] = "250MG", [ABP2400MG] = "400MG", [ABP2600MG] = "600MG",
+ [ABP2001ND] = "001ND", [ABP2002ND] = "002ND", [ABP2004ND] = "004ND",
+ [ABP2005ND] = "005ND", [ABP2010ND] = "010ND", [ABP2020ND] = "020ND",
+ [ABP2030ND] = "030ND", [ABP2002NG] = "002NG", [ABP2004NG] = "004NG",
+ [ABP2005NG] = "005NG", [ABP2010NG] = "010NG", [ABP2020NG] = "020NG",
+ [ABP2030NG] = "030NG", [ABP2015PA] = "015PA", [ABP2030PA] = "030PA",
+ [ABP2060PA] = "060PA", [ABP2100PA] = "100PA", [ABP2150PA] = "150PA",
+ [ABP2175PA] = "175PA", [ABP2001PD] = "001PD", [ABP2005PD] = "005PD",
+ [ABP2015PD] = "015PD", [ABP2030PD] = "030PD", [ABP2060PD] = "060PD",
+ [ABP2001PG] = "001PG", [ABP2005PG] = "005PG", [ABP2015PG] = "015PG",
+ [ABP2030PG] = "030PG", [ABP2060PG] = "060PG", [ABP2100PG] = "100PG",
+ [ABP2150PG] = "150PG", [ABP2175PG] = "175PG",
+};
+
+/**
+ * struct abp2_range_config - list of pressure ranges based on nomenclature
+ * @pmin: lowest pressure that can be measured
+ * @pmax: highest pressure that can be measured
+ */
+struct abp2_range_config {
+ s32 pmin;
+ s32 pmax;
+};
+
+/* All min max limits have been converted to pascals */
+static const struct abp2_range_config abp2_range_config[] = {
+ [ABP2001BA] = { .pmin = 0, .pmax = 100000 },
+ [ABP21_6BA] = { .pmin = 0, .pmax = 160000 },
+ [ABP22_5BA] = { .pmin = 0, .pmax = 250000 },
+ [ABP2004BA] = { .pmin = 0, .pmax = 400000 },
+ [ABP2006BA] = { .pmin = 0, .pmax = 600000 },
+ [ABP2008BA] = { .pmin = 0, .pmax = 800000 },
+ [ABP2010BA] = { .pmin = 0, .pmax = 1000000 },
+ [ABP2012BA] = { .pmin = 0, .pmax = 1200000 },
+ [ABP2001BD] = { .pmin = -100000, .pmax = 100000 },
+ [ABP21_6BD] = { .pmin = -160000, .pmax = 160000 },
+ [ABP22_5BD] = { .pmin = -250000, .pmax = 250000 },
+ [ABP2004BD] = { .pmin = -400000, .pmax = 400000 },
+ [ABP2001BG] = { .pmin = 0, .pmax = 100000 },
+ [ABP21_6BG] = { .pmin = 0, .pmax = 160000 },
+ [ABP22_5BG] = { .pmin = 0, .pmax = 250000 },
+ [ABP2004BG] = { .pmin = 0, .pmax = 400000 },
+ [ABP2006BG] = { .pmin = 0, .pmax = 600000 },
+ [ABP2008BG] = { .pmin = 0, .pmax = 800000 },
+ [ABP2010BG] = { .pmin = 0, .pmax = 1000000 },
+ [ABP2012BG] = { .pmin = 0, .pmax = 1200000 },
+ [ABP2001GG] = { .pmin = 0, .pmax = 1000000 },
+ [ABP21_2GG] = { .pmin = 0, .pmax = 1200000 },
+ [ABP2100KA] = { .pmin = 0, .pmax = 100000 },
+ [ABP2160KA] = { .pmin = 0, .pmax = 160000 },
+ [ABP2250KA] = { .pmin = 0, .pmax = 250000 },
+ [ABP2001KD] = { .pmin = -1000, .pmax = 1000 },
+ [ABP21_6KD] = { .pmin = -1600, .pmax = 1600 },
+ [ABP22_5KD] = { .pmin = -2500, .pmax = 2500 },
+ [ABP2004KD] = { .pmin = -4000, .pmax = 4000 },
+ [ABP2006KD] = { .pmin = -6000, .pmax = 6000 },
+ [ABP2010KD] = { .pmin = -10000, .pmax = 10000 },
+ [ABP2016KD] = { .pmin = -16000, .pmax = 16000 },
+ [ABP2025KD] = { .pmin = -25000, .pmax = 25000 },
+ [ABP2040KD] = { .pmin = -40000, .pmax = 40000 },
+ [ABP2060KD] = { .pmin = -60000, .pmax = 60000 },
+ [ABP2100KD] = { .pmin = -100000, .pmax = 100000 },
+ [ABP2160KD] = { .pmin = -160000, .pmax = 160000 },
+ [ABP2250KD] = { .pmin = -250000, .pmax = 250000 },
+ [ABP2400KD] = { .pmin = -400000, .pmax = 400000 },
+ [ABP2001KG] = { .pmin = 0, .pmax = 1000 },
+ [ABP21_6KG] = { .pmin = 0, .pmax = 1600 },
+ [ABP22_5KG] = { .pmin = 0, .pmax = 2500 },
+ [ABP2004KG] = { .pmin = 0, .pmax = 4000 },
+ [ABP2006KG] = { .pmin = 0, .pmax = 6000 },
+ [ABP2010KG] = { .pmin = 0, .pmax = 10000 },
+ [ABP2016KG] = { .pmin = 0, .pmax = 16000 },
+ [ABP2025KG] = { .pmin = 0, .pmax = 25000 },
+ [ABP2040KG] = { .pmin = 0, .pmax = 40000 },
+ [ABP2060KG] = { .pmin = 0, .pmax = 60000 },
+ [ABP2100KG] = { .pmin = 0, .pmax = 100000 },
+ [ABP2160KG] = { .pmin = 0, .pmax = 160000 },
+ [ABP2250KG] = { .pmin = 0, .pmax = 250000 },
+ [ABP2400KG] = { .pmin = 0, .pmax = 400000 },
+ [ABP2600KG] = { .pmin = 0, .pmax = 600000 },
+ [ABP2800KG] = { .pmin = 0, .pmax = 800000 },
+ [ABP2250LD] = { .pmin = -250, .pmax = 250 },
+ [ABP2600LD] = { .pmin = -600, .pmax = 600 },
+ [ABP2600LG] = { .pmin = 0, .pmax = 600 },
+ [ABP22_5MD] = { .pmin = -250, .pmax = 250 },
+ [ABP2006MD] = { .pmin = -600, .pmax = 600 },
+ [ABP2010MD] = { .pmin = -1000, .pmax = 1000 },
+ [ABP2016MD] = { .pmin = -1600, .pmax = 1600 },
+ [ABP2025MD] = { .pmin = -2500, .pmax = 2500 },
+ [ABP2040MD] = { .pmin = -4000, .pmax = 4000 },
+ [ABP2060MD] = { .pmin = -6000, .pmax = 6000 },
+ [ABP2100MD] = { .pmin = -10000, .pmax = 10000 },
+ [ABP2160MD] = { .pmin = -16000, .pmax = 16000 },
+ [ABP2250MD] = { .pmin = -25000, .pmax = 25000 },
+ [ABP2400MD] = { .pmin = -40000, .pmax = 40000 },
+ [ABP2600MD] = { .pmin = -60000, .pmax = 60000 },
+ [ABP2006MG] = { .pmin = 0, .pmax = 600 },
+ [ABP2010MG] = { .pmin = 0, .pmax = 1000 },
+ [ABP2016MG] = { .pmin = 0, .pmax = 1600 },
+ [ABP2025MG] = { .pmin = 0, .pmax = 2500 },
+ [ABP2040MG] = { .pmin = 0, .pmax = 4000 },
+ [ABP2060MG] = { .pmin = 0, .pmax = 6000 },
+ [ABP2100MG] = { .pmin = 0, .pmax = 10000 },
+ [ABP2160MG] = { .pmin = 0, .pmax = 16000 },
+ [ABP2250MG] = { .pmin = 0, .pmax = 25000 },
+ [ABP2400MG] = { .pmin = 0, .pmax = 40000 },
+ [ABP2600MG] = { .pmin = 0, .pmax = 60000 },
+ [ABP2001ND] = { .pmin = -249, .pmax = 249 },
+ [ABP2002ND] = { .pmin = -498, .pmax = 498 },
+ [ABP2004ND] = { .pmin = -996, .pmax = 996 },
+ [ABP2005ND] = { .pmin = -1245, .pmax = 1245 },
+ [ABP2010ND] = { .pmin = -2491, .pmax = 2491 },
+ [ABP2020ND] = { .pmin = -4982, .pmax = 4982 },
+ [ABP2030ND] = { .pmin = -7473, .pmax = 7473 },
+ [ABP2002NG] = { .pmin = 0, .pmax = 498 },
+ [ABP2004NG] = { .pmin = 0, .pmax = 996 },
+ [ABP2005NG] = { .pmin = 0, .pmax = 1245 },
+ [ABP2010NG] = { .pmin = 0, .pmax = 2491 },
+ [ABP2020NG] = { .pmin = 0, .pmax = 4982 },
+ [ABP2030NG] = { .pmin = 0, .pmax = 7473 },
+ [ABP2015PA] = { .pmin = 0, .pmax = 103421 },
+ [ABP2030PA] = { .pmin = 0, .pmax = 206843 },
+ [ABP2060PA] = { .pmin = 0, .pmax = 413685 },
+ [ABP2100PA] = { .pmin = 0, .pmax = 689476 },
+ [ABP2150PA] = { .pmin = 0, .pmax = 1034214 },
+ [ABP2175PA] = { .pmin = 0, .pmax = 1206583 },
+ [ABP2001PD] = { .pmin = -6895, .pmax = 6895 },
+ [ABP2005PD] = { .pmin = -34474, .pmax = 34474 },
+ [ABP2015PD] = { .pmin = -103421, .pmax = 103421 },
+ [ABP2030PD] = { .pmin = -206843, .pmax = 206843 },
+ [ABP2060PD] = { .pmin = -413685, .pmax = 413685 },
+ [ABP2001PG] = { .pmin = 0, .pmax = 6895 },
+ [ABP2005PG] = { .pmin = 0, .pmax = 34474 },
+ [ABP2015PG] = { .pmin = 0, .pmax = 103421 },
+ [ABP2030PG] = { .pmin = 0, .pmax = 206843 },
+ [ABP2060PG] = { .pmin = 0, .pmax = 413685 },
+ [ABP2100PG] = { .pmin = 0, .pmax = 689476 },
+ [ABP2150PG] = { .pmin = 0, .pmax = 1034214 },
+ [ABP2175PG] = { .pmin = 0, .pmax = 1206583 },
+};
+
+static_assert(ARRAY_SIZE(abp2_triplet_variants) == ARRAY_SIZE(abp2_range_config));
+
+static int abp2_get_measurement(struct abp2_data *data)
+{
+ struct device *dev = data->dev;
+ int ret;
+
+ reinit_completion(&data->completion);
+
+ ret = data->ops->write(data, ABP2_CMD_SYNC, ABP2_PKT_SYNC_LEN);
+ if (ret < 0)
+ return ret;
+
+ if (data->irq > 0) {
+ ret = wait_for_completion_timeout(&data->completion, HZ);
+ if (!ret) {
+ dev_err(dev, "timeout waiting for EOC interrupt\n");
+ return -ETIMEDOUT;
+ }
+ } else {
+ fsleep(5 * USEC_PER_MSEC);
+ }
+
+ memset(data->rx_buf, 0, sizeof(data->rx_buf));
+ ret = data->ops->read(data, ABP2_CMD_NOP, ABP2_PKT_NOP_LEN);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Status byte flags
+ * bit7 SANITY_CHK - must always be 0
+ * bit6 ABP2_ST_POWER - 1 if device is powered
+ * bit5 ABP2_ST_BUSY - 1 if device has no new conversion ready
+ * bit4 SANITY_CHK - must always be 0
+ * bit3 SANITY_CHK - must always be 0
+ * bit2 MEMORY_ERR - 1 if integrity test has failed
+ * bit1 SANITY_CHK - must always be 0
+ * bit0 MATH_ERR - 1 during internal math saturation error
+ */
+
+ if (data->rx_buf[0] == (ABP2_ST_POWER | ABP2_ST_BUSY))
+ return -EBUSY;
+
+ /*
+ * The ABP2 sensor series seem to have a noticeable latch-up sensitivity.
+ * A partial latch-up condition manifests as either
+ * - output of invalid status bytes
+ * - zeroed out conversions (despite a normal status byte)
+ * - the MOSI line being pulled low randomly in sync with the SCLK
+ * signal (visible during the ABP2_CMD_NOP command).
+ * https://e2e.ti.com/support/processors-group/processors/f/processors-forum/1588325/am3358-spi-tx-data-corruption
+ */
+
+ if (data->rx_buf[0] != ABP2_ST_POWER) {
+ dev_err(data->dev,
+ "unexpected status byte 0x%02x\n", data->rx_buf[0]);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static irqreturn_t abp2_eoc_handler(int irq, void *private)
+{
+ struct abp2_data *data = private;
+
+ complete(&data->completion);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t abp2_trigger_handler(int irq, void *private)
+{
+ int ret;
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct abp2_data *data = iio_priv(indio_dev);
+
+ ret = abp2_get_measurement(data);
+ if (ret < 0)
+ goto out_notify_done;
+
+ data->scan.chan[0] = get_unaligned_be24(&data->rx_buf[1]);
+ data->scan.chan[1] = get_unaligned_be24(&data->rx_buf[4]);
+
+ iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan),
+ iio_get_time_ns(indio_dev));
+
+out_notify_done:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * IIO ABI expects
+ * value = (conv + offset) * scale
+ *
+ * temp[C] = conv * a + b
+ * where a = 200/16777215; b = -50
+ *
+ * temp[C] = (conv + (b/a)) * a * (1000)
+ * =>
+ * scale = a * 1000 = .0000119209296 * 1000 = .01192092966562
+ * offset = b/a = -50 * 16777215 / 200 = -4194303.75
+ *
+ * pressure = (conv - Omin) * Q + Pmin =
+ * ((conv - Omin) + Pmin/Q) * Q
+ * =>
+ * scale = Q = (Pmax - Pmin) / (Omax - Omin)
+ * offset = Pmin/Q - Omin = Pmin * (Omax - Omin) / (Pmax - Pmin) - Omin
+ */
+static int abp2_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct abp2_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = abp2_get_measurement(data);
+ if (ret < 0)
+ return ret;
+
+ switch (channel->type) {
+ case IIO_PRESSURE:
+ *val = get_unaligned_be24(&data->rx_buf[1]);
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = get_unaligned_be24(&data->rx_buf[4]);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (channel->type) {
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = 11920929;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_PRESSURE:
+ *val = data->p_scale;
+ *val2 = data->p_scale_dec;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_OFFSET:
+ switch (channel->type) {
+ case IIO_TEMP:
+ *val = -4194304;
+ return IIO_VAL_INT;
+ case IIO_PRESSURE:
+ *val = data->p_offset;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info abp2_info = {
+ .read_raw = &abp2_read_raw,
+};
+
+static const unsigned long abp2_scan_masks[] = {0x3, 0};
+
+static const struct iio_chan_spec abp2_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+int abp2_common_probe(struct device *dev, const struct abp2_ops *ops, int irq)
+{
+ int ret;
+ struct abp2_data *data;
+ struct iio_dev *indio_dev;
+ const char *triplet;
+ s32 tmp;
+ s64 odelta, pdelta;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->dev = dev;
+ data->ops = ops;
+ data->irq = irq;
+
+ init_completion(&data->completion);
+
+ indio_dev->name = "abp2030pa";
+ indio_dev->info = &abp2_info;
+ indio_dev->channels = abp2_channels;
+ indio_dev->num_channels = ARRAY_SIZE(abp2_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->available_scan_masks = abp2_scan_masks;
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "can't get and enable vdd supply\n");
+
+ ret = device_property_read_string(dev, "honeywell,pressure-triplet",
+ &triplet);
+ if (ret) {
+ ret = device_property_read_u32(dev, "honeywell,pmin-pascal",
+ &data->pmin);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmin-pascal could not be read\n");
+
+ ret = device_property_read_u32(dev, "honeywell,pmax-pascal",
+ &data->pmax);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmax-pascal could not be read\n");
+ } else {
+ ret = device_property_match_property_string(dev,
+ "honeywell,pressure-triplet",
+ abp2_triplet_variants,
+ ARRAY_SIZE(abp2_triplet_variants));
+ if (ret < 0)
+ return dev_err_probe(dev, -EINVAL, "honeywell,pressure-triplet is invalid\n");
+
+ data->pmin = abp2_range_config[ret].pmin;
+ data->pmax = abp2_range_config[ret].pmax;
+ }
+
+ if (data->pmin >= data->pmax)
+ return dev_err_probe(dev, -EINVAL, "pressure limits are invalid\n");
+
+ data->outmin = abp2_func_spec[data->function].output_min;
+ data->outmax = abp2_func_spec[data->function].output_max;
+
+ odelta = data->outmax - data->outmin;
+ pdelta = data->pmax - data->pmin;
+
+ data->p_scale = div_s64_rem(div_s64(pdelta * NANO, odelta), NANO, &tmp);
+ data->p_scale_dec = tmp;
+
+ data->p_offset = div_s64(odelta * data->pmin, pdelta) - data->outmin;
+
+ if (data->irq > 0) {
+ ret = devm_request_irq(dev, irq, abp2_eoc_handler, 0,
+ dev_name(dev), data);
+ if (ret)
+ return ret;
+ }
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ abp2_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret, "iio triggered buffer setup failed\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "unable to register iio device\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(abp2_common_probe, "IIO_HONEYWELL_ABP2030PA");
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/abp2030pa.h b/drivers/iio/pressure/abp2030pa.h
new file mode 100644
index 000000000..57e5ed784
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa.h
@@ -0,0 +1,73 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#ifndef _ABP2030PA_H
+#define _ABP2030PA_H
+
+#include <linux/completion.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define ABP2_MEASUREMENT_RD_SIZE 7
+
+struct device;
+
+struct abp2_data;
+struct abp2_ops;
+
+enum abp2_func_id {
+ ABP2_FUNCTION_A,
+};
+
+/**
+ * struct abp2_data
+ * @dev: current device structure
+ * @ops: pointers for bus specific read and write functions
+ * @pmin: minimal pressure in pascal
+ * @pmax: maximal pressure in pascal
+ * @outmin: minimum raw pressure in counts (based on transfer function)
+ * @outmax: maximum raw pressure in counts (based on transfer function)
+ * @function: transfer function
+ * @p_scale: pressure scale
+ * @p_scale_dec: pressure scale, decimal number
+ * @p_offset: pressure offset
+ * @irq: end of conversion - applies only to the i2c sensor
+ * @completion: handshake from irq to read
+ * @scan: channel values for buffered mode
+ * @tx_buf: transmit buffer used during the SPI communication
+ * @rx_buf: raw data provided by sensor
+ */
+struct abp2_data {
+ struct device *dev;
+ const struct abp2_ops *ops;
+ s32 pmin;
+ s32 pmax;
+ u32 outmin;
+ u32 outmax;
+ enum abp2_func_id function;
+ int p_scale;
+ int p_scale_dec;
+ int p_offset;
+ int irq;
+ struct completion completion;
+ struct {
+ u32 chan[2];
+ aligned_s64 timestamp;
+ } scan;
+ u8 rx_buf[ABP2_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
+ u8 tx_buf[ABP2_MEASUREMENT_RD_SIZE];
+};
+
+struct abp2_ops {
+ int (*read)(struct abp2_data *data, u8 cmd, u8 nbytes);
+ int (*write)(struct abp2_data *data, u8 cmd, u8 nbytes);
+};
+
+int abp2_common_probe(struct device *dev, const struct abp2_ops *ops, int irq);
+
+#endif
diff --git a/drivers/iio/pressure/abp2030pa_i2c.c b/drivers/iio/pressure/abp2030pa_i2c.c
new file mode 100644
index 000000000..fa4d290cf
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa_i2c.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "abp2030pa.h"
+
+static int abp2_i2c_read(struct abp2_data *data, u8 unused, u8 nbytes)
+{
+ struct i2c_client *client = to_i2c_client(data->dev);
+ int ret;
+
+ if (nbytes > ABP2_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ ret = i2c_master_recv(client, data->rx_buf, nbytes);
+ if (ret < 0)
+ return ret;
+ if (ret != nbytes)
+ return -EIO;
+
+ return 0;
+}
+
+static int abp2_i2c_write(struct abp2_data *data, u8 cmd, u8 nbytes)
+{
+ struct i2c_client *client = to_i2c_client(data->dev);
+ int ret;
+
+ if (nbytes > ABP2_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ data->tx_buf[0] = cmd;
+ ret = i2c_master_send(client, data->tx_buf, nbytes);
+ if (ret < 0)
+ return ret;
+ if (ret != nbytes)
+ return -EIO;
+
+ return 0;
+}
+
+static const struct abp2_ops abp2_i2c_ops = {
+ .read = abp2_i2c_read,
+ .write = abp2_i2c_write,
+};
+
+static int abp2_i2c_probe(struct i2c_client *client)
+{
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ return abp2_common_probe(&client->dev, &abp2_i2c_ops, client->irq);
+}
+
+static const struct of_device_id abp2_i2c_match[] = {
+ { .compatible = "honeywell,abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, abp2_i2c_match);
+
+static const struct i2c_device_id abp2_i2c_id[] = {
+ { .name = "abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, abp2_i2c_id);
+
+static struct i2c_driver abp2_i2c_driver = {
+ .driver = {
+ .name = "abp2030pa",
+ .of_match_table = abp2_i2c_match,
+ },
+ .probe = abp2_i2c_probe,
+ .id_table = abp2_i2c_id,
+};
+module_i2c_driver(abp2_i2c_driver);
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor i2c driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_ABP2030PA");
diff --git a/drivers/iio/pressure/abp2030pa_spi.c b/drivers/iio/pressure/abp2030pa_spi.c
new file mode 100644
index 000000000..60d078574
--- /dev/null
+++ b/drivers/iio/pressure/abp2030pa_spi.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Honeywell ABP2 series pressure sensor driver
+ *
+ * Copyright (c) 2025 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/types.h>
+
+#include "abp2030pa.h"
+
+static int abp2_spi_xfer(struct abp2_data *data, u8 cmd, u8 nbytes)
+{
+ struct spi_device *spi = to_spi_device(data->dev);
+ struct spi_transfer xfer = { };
+
+ if (nbytes > ABP2_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ data->tx_buf[0] = cmd;
+ xfer.tx_buf = data->tx_buf;
+ xfer.rx_buf = data->rx_buf;
+ xfer.len = nbytes;
+
+ return spi_sync_transfer(spi, &xfer, 1);
+}
+
+static const struct abp2_ops abp2_spi_ops = {
+ .read = abp2_spi_xfer,
+ .write = abp2_spi_xfer,
+};
+
+static int abp2_spi_probe(struct spi_device *spi)
+{
+ return abp2_common_probe(&spi->dev, &abp2_spi_ops, spi->irq);
+}
+
+static const struct of_device_id abp2_spi_match[] = {
+ { .compatible = "honeywell,abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, abp2_spi_match);
+
+static const struct spi_device_id abp2_spi_id[] = {
+ { .name = "abp2030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, abp2_spi_id);
+
+static struct spi_driver abp2_spi_driver = {
+ .driver = {
+ .name = "abp2030pa",
+ .of_match_table = abp2_spi_match,
+ },
+ .probe = abp2_spi_probe,
+ .id_table = abp2_spi_id,
+};
+module_spi_driver(abp2_spi_driver);
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell ABP2 pressure sensor spi driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_ABP2030PA");
diff --git a/drivers/iio/pressure/adp810.c b/drivers/iio/pressure/adp810.c
new file mode 100644
index 000000000..82aa43354
--- /dev/null
+++ b/drivers/iio/pressure/adp810.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2025 Akhilesh Patil <akhilesh@ee.iitb.ac.in>
+ *
+ * Driver for adp810 pressure and temperature sensor
+ * Datasheet:
+ * https://aosong.com/userfiles/files/media/Datasheet%20ADP810-Digital.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/cleanup.h>
+#include <linux/crc8.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dev_printk.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+#include <linux/unaligned.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/types.h>
+
+/*
+ * Refer section 5.4 checksum calculation from datasheet.
+ * This sensor uses CRC polynomial x^8 + x^5 + x^4 + 1 (0x31)
+ */
+#define ADP810_CRC8_POLYNOMIAL 0x31
+
+DECLARE_CRC8_TABLE(crc_table);
+
+/*
+ * Buffer declaration which holds 9 bytes of measurement data read
+ * from the sensor. Use __packed to avoid any paddings, as data sent
+ * from the sensor is strictly contiguous 9 bytes.
+ */
+struct adp810_read_buf {
+ __be16 dp;
+ u8 dp_crc;
+ __be16 tmp;
+ u8 tmp_crc;
+ __be16 sf;
+ u8 sf_crc;
+} __packed;
+
+struct adp810_data {
+ struct i2c_client *client;
+ /* Use lock to synchronize access to device during read sequence */
+ struct mutex lock;
+};
+
+static int adp810_measure(struct adp810_data *data, struct adp810_read_buf *buf)
+{
+ struct i2c_client *client = data->client;
+ struct device *dev = &client->dev;
+ int ret;
+ u8 trig_cmd[2] = {0x37, 0x2d};
+
+ /* Send trigger command to the sensor for measurement */
+ ret = i2c_master_send(client, trig_cmd, sizeof(trig_cmd));
+ if (ret < 0) {
+ dev_err(dev, "Error sending trigger command\n");
+ return ret;
+ }
+ if (ret != sizeof(trig_cmd))
+ return -EIO;
+
+ /*
+ * Wait for the sensor to acquire data. As per datasheet section 5.3.1,
+ * at least 10ms delay before reading from the sensor is recommended.
+ * Here, we wait for 20ms to have some safe margin on the top
+ * of recommendation and to compensate for any possible variations.
+ */
+ msleep(20);
+
+ /* Read sensor values */
+ ret = i2c_master_recv(client, (char *)buf, sizeof(*buf));
+ if (ret < 0) {
+ dev_err(dev, "Error reading from sensor\n");
+ return ret;
+ }
+ if (ret != sizeof(*buf))
+ return -EIO;
+
+ /* CRC checks */
+ crc8_populate_msb(crc_table, ADP810_CRC8_POLYNOMIAL);
+ if (buf->dp_crc != crc8(crc_table, (u8 *)&buf->dp, 0x2, CRC8_INIT_VALUE)) {
+ dev_err(dev, "CRC error for pressure\n");
+ return -EIO;
+ }
+
+ if (buf->tmp_crc != crc8(crc_table, (u8 *)&buf->tmp, 0x2, CRC8_INIT_VALUE)) {
+ dev_err(dev, "CRC error for temperature\n");
+ return -EIO;
+ }
+
+ if (buf->sf_crc != crc8(crc_table, (u8 *)&buf->sf, 0x2, CRC8_INIT_VALUE)) {
+ dev_err(dev, "CRC error for scale\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int adp810_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct adp810_data *data = iio_priv(indio_dev);
+ struct device *dev = &data->client->dev;
+ struct adp810_read_buf buf = { };
+ int ret;
+
+ scoped_guard(mutex, &data->lock) {
+ ret = adp810_measure(data, &buf);
+ if (ret) {
+ dev_err(dev, "Failed to read from device\n");
+ return ret;
+ }
+ }
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ *val = get_unaligned_be16(&buf.dp);
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = get_unaligned_be16(&buf.tmp);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ *val = get_unaligned_be16(&buf.sf);
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = 200;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info adp810_info = {
+ .read_raw = adp810_read_raw,
+};
+
+static const struct iio_chan_spec adp810_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int adp810_probe(struct i2c_client *client)
+{
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct adp810_data *data;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+
+ ret = devm_mutex_init(dev, &data->lock);
+ if (ret)
+ return ret;
+
+ indio_dev->name = "adp810";
+ indio_dev->channels = adp810_channels;
+ indio_dev->num_channels = ARRAY_SIZE(adp810_channels);
+ indio_dev->info = &adp810_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register IIO device\n");
+
+ return 0;
+}
+
+static const struct i2c_device_id adp810_id_table[] = {
+ { .name = "adp810" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, adp810_id_table);
+
+static const struct of_device_id adp810_of_table[] = {
+ { .compatible = "aosong,adp810" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adp810_of_table);
+
+static struct i2c_driver adp810_driver = {
+ .driver = {
+ .name = "adp810",
+ .of_match_table = adp810_of_table,
+ },
+ .probe = adp810_probe,
+ .id_table = adp810_id_table,
+};
+module_i2c_driver(adp810_driver);
+
+MODULE_AUTHOR("Akhilesh Patil <akhilesh@ee.iitb.ac.in>");
+MODULE_DESCRIPTION("Driver for Aosong ADP810 sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/bmp280-core.c b/drivers/iio/pressure/bmp280-core.c
new file mode 100644
index 000000000..3940b8e61
--- /dev/null
+++ b/drivers/iio/pressure/bmp280-core.c
@@ -0,0 +1,3361 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2010 Christoph Mair <christoph.mair@gmail.com>
+ * Copyright (c) 2012 Bosch Sensortec GmbH
+ * Copyright (c) 2012 Unixphere AB
+ * Copyright (c) 2014 Intel Corporation
+ * Copyright (c) 2016 Linus Walleij <linus.walleij@linaro.org>
+ *
+ * Driver for Bosch Sensortec BMP180 and BMP280 digital pressure sensor.
+ *
+ * Datasheet:
+ * https://cdn-shop.adafruit.com/datasheets/BST-BMP180-DS000-09.pdf
+ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp280-ds001.pdf
+ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf
+ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp388-ds001.pdf
+ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp390-ds002.pdf
+ * https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bmp581-ds004.pdf
+ *
+ * Sensor API:
+ * https://github.com/boschsensortec/BME280_SensorAPI
+ * https://github.com/boschsensortec/BMP3_SensorAPI
+ * https://github.com/boschsensortec/BMP5_SensorAPI
+ *
+ * Notice:
+ * The link to the bmp180 datasheet points to an outdated version missing these changes:
+ * - Changed document referral from ANP015 to BST-MPS-AN004-00 on page 26
+ * - Updated equation for B3 param on section 3.5 to ((((long)AC1 * 4 + X3) << oss) + 2) / 4
+ * - Updated RoHS directive to 2011/65/EU effective 8 June 2011 on page 26
+ */
+
+#define pr_fmt(fmt) "bmp280: " fmt
+
+#include <linux/bitops.h>
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio/consumer.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h> /* For irq_get_irq_data() */
+#include <linux/module.h>
+#include <linux/nvmem-provider.h>
+#include <linux/pm_runtime.h>
+#include <linux/property.h>
+#include <linux/random.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <linux/types.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/unaligned.h>
+
+#include "bmp280.h"
+
+/*
+ * These enums are used for indexing into the array of calibration
+ * coefficients for BMP180.
+ */
+enum { AC1, AC2, AC3, AC4, AC5, AC6, B1, B2, MB, MC, MD };
+
+enum bmp380_odr {
+ BMP380_ODR_200HZ,
+ BMP380_ODR_100HZ,
+ BMP380_ODR_50HZ,
+ BMP380_ODR_25HZ,
+ BMP380_ODR_12_5HZ,
+ BMP380_ODR_6_25HZ,
+ BMP380_ODR_3_125HZ,
+ BMP380_ODR_1_5625HZ,
+ BMP380_ODR_0_78HZ,
+ BMP380_ODR_0_39HZ,
+ BMP380_ODR_0_2HZ,
+ BMP380_ODR_0_1HZ,
+ BMP380_ODR_0_05HZ,
+ BMP380_ODR_0_02HZ,
+ BMP380_ODR_0_01HZ,
+ BMP380_ODR_0_006HZ,
+ BMP380_ODR_0_003HZ,
+ BMP380_ODR_0_0015HZ,
+};
+
+enum bmp580_odr {
+ BMP580_ODR_240HZ,
+ BMP580_ODR_218HZ,
+ BMP580_ODR_199HZ,
+ BMP580_ODR_179HZ,
+ BMP580_ODR_160HZ,
+ BMP580_ODR_149HZ,
+ BMP580_ODR_140HZ,
+ BMP580_ODR_129HZ,
+ BMP580_ODR_120HZ,
+ BMP580_ODR_110HZ,
+ BMP580_ODR_100HZ,
+ BMP580_ODR_89HZ,
+ BMP580_ODR_80HZ,
+ BMP580_ODR_70HZ,
+ BMP580_ODR_60HZ,
+ BMP580_ODR_50HZ,
+ BMP580_ODR_45HZ,
+ BMP580_ODR_40HZ,
+ BMP580_ODR_35HZ,
+ BMP580_ODR_30HZ,
+ BMP580_ODR_25HZ,
+ BMP580_ODR_20HZ,
+ BMP580_ODR_15HZ,
+ BMP580_ODR_10HZ,
+ BMP580_ODR_5HZ,
+ BMP580_ODR_4HZ,
+ BMP580_ODR_3HZ,
+ BMP580_ODR_2HZ,
+ BMP580_ODR_1HZ,
+ BMP580_ODR_0_5HZ,
+ BMP580_ODR_0_25HZ,
+ BMP580_ODR_0_125HZ,
+};
+
+/*
+ * These enums are used for indexing into the array of compensation
+ * parameters for BMP280.
+ */
+enum { T1, T2, T3, P1, P2, P3, P4, P5, P6, P7, P8, P9 };
+
+enum {
+ /* Temperature calib indexes */
+ BMP380_T1 = 0,
+ BMP380_T2 = 2,
+ BMP380_T3 = 4,
+ /* Pressure calib indexes */
+ BMP380_P1 = 5,
+ BMP380_P2 = 7,
+ BMP380_P3 = 9,
+ BMP380_P4 = 10,
+ BMP380_P5 = 11,
+ BMP380_P6 = 13,
+ BMP380_P7 = 15,
+ BMP380_P8 = 16,
+ BMP380_P9 = 17,
+ BMP380_P10 = 19,
+ BMP380_P11 = 20,
+};
+
+enum bmp280_scan {
+ BMP280_PRESS,
+ BMP280_TEMP,
+ BME280_HUMID,
+};
+
+static const struct iio_chan_spec bmp280_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ /* PROCESSED maintained for ABI backwards compatibility */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ /* PROCESSED maintained for ABI backwards compatibility */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const struct iio_chan_spec bme280_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ /* PROCESSED maintained for ABI backwards compatibility */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ /* PROCESSED maintained for ABI backwards compatibility */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_HUMIDITYRELATIVE,
+ /* PROCESSED maintained for ABI backwards compatibility */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 2,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(3),
+};
+
+static const struct iio_chan_spec bmp380_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ /* PROCESSED maintained for ABI backwards compatibility */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ /* PROCESSED maintained for ABI backwards compatibility */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const struct iio_chan_spec bmp580_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ /* PROCESSED maintained for ABI backwards compatibility */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ /* PROCESSED maintained for ABI backwards compatibility */
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static int bmp280_read_calib(struct bmp280_data *data)
+{
+ struct bmp280_calib *calib = &data->calib.bmp280;
+ int ret;
+
+ /* Read temperature and pressure calibration values. */
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_COMP_TEMP_START,
+ data->bmp280_cal_buf,
+ sizeof(data->bmp280_cal_buf));
+ if (ret) {
+ dev_err(data->dev,
+ "failed to read calibration parameters\n");
+ return ret;
+ }
+
+ /* Toss calibration data into the entropy pool */
+ add_device_randomness(data->bmp280_cal_buf,
+ sizeof(data->bmp280_cal_buf));
+
+ /* Parse temperature calibration values. */
+ calib->T1 = le16_to_cpu(data->bmp280_cal_buf[T1]);
+ calib->T2 = le16_to_cpu(data->bmp280_cal_buf[T2]);
+ calib->T3 = le16_to_cpu(data->bmp280_cal_buf[T3]);
+
+ /* Parse pressure calibration values. */
+ calib->P1 = le16_to_cpu(data->bmp280_cal_buf[P1]);
+ calib->P2 = le16_to_cpu(data->bmp280_cal_buf[P2]);
+ calib->P3 = le16_to_cpu(data->bmp280_cal_buf[P3]);
+ calib->P4 = le16_to_cpu(data->bmp280_cal_buf[P4]);
+ calib->P5 = le16_to_cpu(data->bmp280_cal_buf[P5]);
+ calib->P6 = le16_to_cpu(data->bmp280_cal_buf[P6]);
+ calib->P7 = le16_to_cpu(data->bmp280_cal_buf[P7]);
+ calib->P8 = le16_to_cpu(data->bmp280_cal_buf[P8]);
+ calib->P9 = le16_to_cpu(data->bmp280_cal_buf[P9]);
+
+ return 0;
+}
+
+/*
+ * These enums are used for indexing into the array of humidity parameters
+ * for BME280. Due to some weird indexing, unaligned BE/LE accesses co-exist in
+ * order to prepare the FIELD_{GET/PREP}() fields. Table 16 in Section 4.2.2 of
+ * the datasheet.
+ */
+enum { H2 = 0, H3 = 2, H4 = 3, H5 = 4, H6 = 6 };
+
+static int bme280_read_calib(struct bmp280_data *data)
+{
+ struct bmp280_calib *calib = &data->calib.bmp280;
+ struct device *dev = data->dev;
+ s16 h4_upper, h4_lower, tmp_1, tmp_2, tmp_3;
+ unsigned int tmp;
+ int ret;
+
+ /* Load shared calibration params with bmp280 first */
+ ret = bmp280_read_calib(data);
+ if (ret)
+ return ret;
+
+ ret = regmap_read(data->regmap, BME280_REG_COMP_H1, &tmp);
+ if (ret) {
+ dev_err(dev, "failed to read H1 comp value\n");
+ return ret;
+ }
+ calib->H1 = tmp;
+
+ ret = regmap_bulk_read(data->regmap, BME280_REG_COMP_H2,
+ data->bme280_humid_cal_buf,
+ sizeof(data->bme280_humid_cal_buf));
+ if (ret) {
+ dev_err(dev, "failed to read humidity calibration values\n");
+ return ret;
+ }
+
+ calib->H2 = get_unaligned_le16(&data->bme280_humid_cal_buf[H2]);
+ calib->H3 = data->bme280_humid_cal_buf[H3];
+ tmp_1 = get_unaligned_be16(&data->bme280_humid_cal_buf[H4]);
+ tmp_2 = FIELD_GET(BME280_COMP_H4_GET_MASK_UP, tmp_1);
+ h4_upper = FIELD_PREP(BME280_COMP_H4_PREP_MASK_UP, tmp_2);
+ h4_lower = FIELD_GET(BME280_COMP_H4_MASK_LOW, tmp_1);
+ calib->H4 = sign_extend32(h4_upper | h4_lower, 11);
+ tmp_3 = get_unaligned_le16(&data->bme280_humid_cal_buf[H5]);
+ calib->H5 = FIELD_GET_SIGNED(BME280_COMP_H5_MASK, tmp_3);
+ calib->H6 = data->bme280_humid_cal_buf[H6];
+
+ return 0;
+}
+
+static int bme280_read_humid_adc(struct bmp280_data *data, u16 *adc_humidity)
+{
+ u16 value_humidity;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BME280_REG_HUMIDITY_MSB,
+ &data->be16, BME280_NUM_HUMIDITY_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to read humidity\n");
+ return ret;
+ }
+
+ value_humidity = be16_to_cpu(data->be16);
+ if (value_humidity == BMP280_HUMIDITY_SKIPPED) {
+ dev_err(data->dev, "reading humidity skipped\n");
+ return -EIO;
+ }
+ *adc_humidity = value_humidity;
+
+ return 0;
+}
+
+/*
+ * Returns humidity in percent, resolution is 0.01 percent. Output value of
+ * "47445" represents 47445/1024 = 46.333 %RH.
+ *
+ * Taken from BME280 datasheet, Section 4.2.3, "Compensation formula".
+ */
+static u32 bme280_compensate_humidity(struct bmp280_data *data,
+ u16 adc_humidity, s32 t_fine)
+{
+ struct bmp280_calib *calib = &data->calib.bmp280;
+ s32 var;
+
+ var = t_fine - (s32)76800;
+ var = (((((s32)adc_humidity << 14) - (calib->H4 << 20) - (calib->H5 * var))
+ + (s32)16384) >> 15) * (((((((var * calib->H6) >> 10)
+ * (((var * (s32)calib->H3) >> 11) + (s32)32768)) >> 10)
+ + (s32)2097152) * calib->H2 + 8192) >> 14);
+ var -= ((((var >> 15) * (var >> 15)) >> 7) * (s32)calib->H1) >> 4;
+
+ var = clamp_val(var, 0, 419430400);
+
+ return var >> 12;
+}
+
+static int bmp280_read_temp_adc(struct bmp280_data *data, u32 *adc_temp)
+{
+ u32 value_temp;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_TEMP_MSB,
+ data->buf, BMP280_NUM_TEMP_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to read temperature\n");
+ return ret;
+ }
+
+ value_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(data->buf));
+ if (value_temp == BMP280_TEMP_SKIPPED) {
+ dev_err(data->dev, "reading temperature skipped\n");
+ return -EIO;
+ }
+ *adc_temp = value_temp;
+
+ return 0;
+}
+
+/*
+ * Returns temperature in DegC, resolution is 0.01 DegC. Output value of
+ * "5123" equals 51.23 DegC. t_fine carries fine temperature as global
+ * value.
+ *
+ * Taken from datasheet, Section 3.11.3, "Compensation formula".
+ */
+static s32 bmp280_calc_t_fine(struct bmp280_data *data, u32 adc_temp)
+{
+ struct bmp280_calib *calib = &data->calib.bmp280;
+ s32 var1, var2;
+
+ var1 = (((((s32)adc_temp) >> 3) - ((s32)calib->T1 << 1)) *
+ ((s32)calib->T2)) >> 11;
+ var2 = (((((((s32)adc_temp) >> 4) - ((s32)calib->T1)) *
+ ((((s32)adc_temp >> 4) - ((s32)calib->T1))) >> 12) *
+ ((s32)calib->T3))) >> 14;
+ return var1 + var2; /* t_fine = var1 + var2 */
+}
+
+static int bmp280_get_t_fine(struct bmp280_data *data, s32 *t_fine)
+{
+ u32 adc_temp;
+ int ret;
+
+ ret = bmp280_read_temp_adc(data, &adc_temp);
+ if (ret)
+ return ret;
+
+ *t_fine = bmp280_calc_t_fine(data, adc_temp);
+
+ return 0;
+}
+
+static s32 bmp280_compensate_temp(struct bmp280_data *data, u32 adc_temp)
+{
+ return (bmp280_calc_t_fine(data, adc_temp) * 5 + 128) / 256;
+}
+
+static int bmp280_read_press_adc(struct bmp280_data *data, u32 *adc_press)
+{
+ u32 value_press;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB,
+ data->buf, BMP280_NUM_PRESS_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to read pressure\n");
+ return ret;
+ }
+
+ value_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(data->buf));
+ if (value_press == BMP280_PRESS_SKIPPED) {
+ dev_err(data->dev, "reading pressure skipped\n");
+ return -EIO;
+ }
+ *adc_press = value_press;
+
+ return 0;
+}
+
+/*
+ * Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24
+ * integer bits and 8 fractional bits). Output value of "24674867"
+ * represents 24674867/256 = 96386.2 Pa = 963.862 hPa
+ *
+ * Taken from datasheet, Section 3.11.3, "Compensation formula".
+ */
+static u32 bmp280_compensate_press(struct bmp280_data *data,
+ u32 adc_press, s32 t_fine)
+{
+ struct bmp280_calib *calib = &data->calib.bmp280;
+ s64 var1, var2, p;
+
+ var1 = ((s64)t_fine) - 128000;
+ var2 = var1 * var1 * (s64)calib->P6;
+ var2 += (var1 * (s64)calib->P5) << 17;
+ var2 += ((s64)calib->P4) << 35;
+ var1 = ((var1 * var1 * (s64)calib->P3) >> 8) +
+ ((var1 * (s64)calib->P2) << 12);
+ var1 = ((((s64)1) << 47) + var1) * ((s64)calib->P1) >> 33;
+
+ if (var1 == 0)
+ return 0;
+
+ p = ((((s64)1048576 - (s32)adc_press) << 31) - var2) * 3125;
+ p = div64_s64(p, var1);
+ var1 = (((s64)calib->P9) * (p >> 13) * (p >> 13)) >> 25;
+ var2 = ((s64)(calib->P8) * p) >> 19;
+ p = ((p + var1 + var2) >> 8) + (((s64)calib->P7) << 4);
+
+ return (u32)p;
+}
+
+static int bmp280_read_temp(struct bmp280_data *data, s32 *comp_temp)
+{
+ u32 adc_temp;
+ int ret;
+
+ ret = bmp280_read_temp_adc(data, &adc_temp);
+ if (ret)
+ return ret;
+
+ *comp_temp = bmp280_compensate_temp(data, adc_temp);
+
+ return 0;
+}
+
+static int bmp280_read_press(struct bmp280_data *data, u32 *comp_press)
+{
+ u32 adc_press;
+ s32 t_fine;
+ int ret;
+
+ ret = bmp280_get_t_fine(data, &t_fine);
+ if (ret)
+ return ret;
+
+ ret = bmp280_read_press_adc(data, &adc_press);
+ if (ret)
+ return ret;
+
+ *comp_press = bmp280_compensate_press(data, adc_press, t_fine);
+
+ return 0;
+}
+
+static int bme280_read_humid(struct bmp280_data *data, u32 *comp_humidity)
+{
+ u16 adc_humidity;
+ s32 t_fine;
+ int ret;
+
+ ret = bmp280_get_t_fine(data, &t_fine);
+ if (ret)
+ return ret;
+
+ ret = bme280_read_humid_adc(data, &adc_humidity);
+ if (ret)
+ return ret;
+
+ *comp_humidity = bme280_compensate_humidity(data, adc_humidity, t_fine);
+
+ return 0;
+}
+
+static int bmp280_read_raw_impl(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct bmp280_data *data = iio_priv(indio_dev);
+ int chan_value;
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = data->chip_info->set_mode(data, BMP280_FORCED);
+ if (ret)
+ return ret;
+
+ ret = data->chip_info->wait_conv(data);
+ if (ret)
+ return ret;
+
+ switch (chan->type) {
+ case IIO_HUMIDITYRELATIVE:
+ ret = data->chip_info->read_humid(data, &chan_value);
+ if (ret)
+ return ret;
+
+ *val = data->chip_info->humid_coeffs[0] * chan_value;
+ *val2 = data->chip_info->humid_coeffs[1];
+ return data->chip_info->humid_coeffs_type;
+ case IIO_PRESSURE:
+ ret = data->chip_info->read_press(data, &chan_value);
+ if (ret)
+ return ret;
+
+ *val = data->chip_info->press_coeffs[0] * chan_value;
+ *val2 = data->chip_info->press_coeffs[1];
+ return data->chip_info->press_coeffs_type;
+ case IIO_TEMP:
+ ret = data->chip_info->read_temp(data, &chan_value);
+ if (ret)
+ return ret;
+
+ *val = data->chip_info->temp_coeffs[0] * chan_value;
+ *val2 = data->chip_info->temp_coeffs[1];
+ return data->chip_info->temp_coeffs_type;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_RAW:
+ ret = data->chip_info->set_mode(data, BMP280_FORCED);
+ if (ret)
+ return ret;
+
+ ret = data->chip_info->wait_conv(data);
+ if (ret)
+ return ret;
+
+ switch (chan->type) {
+ case IIO_HUMIDITYRELATIVE:
+ ret = data->chip_info->read_humid(data, &chan_value);
+ if (ret)
+ return ret;
+
+ *val = chan_value;
+ return IIO_VAL_INT;
+ case IIO_PRESSURE:
+ ret = data->chip_info->read_press(data, &chan_value);
+ if (ret)
+ return ret;
+
+ *val = chan_value;
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ ret = data->chip_info->read_temp(data, &chan_value);
+ if (ret)
+ return ret;
+
+ *val = chan_value;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_HUMIDITYRELATIVE:
+ *val = data->chip_info->humid_coeffs[0];
+ *val2 = data->chip_info->humid_coeffs[1];
+ return data->chip_info->humid_coeffs_type;
+ case IIO_PRESSURE:
+ *val = data->chip_info->press_coeffs[0];
+ *val2 = data->chip_info->press_coeffs[1];
+ return data->chip_info->press_coeffs_type;
+ case IIO_TEMP:
+ *val = data->chip_info->temp_coeffs[0];
+ *val2 = data->chip_info->temp_coeffs[1];
+ return data->chip_info->temp_coeffs_type;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_HUMIDITYRELATIVE:
+ *val = 1 << data->oversampling_humid;
+ return IIO_VAL_INT;
+ case IIO_PRESSURE:
+ *val = 1 << data->oversampling_press;
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = 1 << data->oversampling_temp;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (!data->chip_info->sampling_freq_avail)
+ return -EINVAL;
+
+ *val = data->chip_info->sampling_freq_avail[data->sampling_freq][0];
+ *val2 = data->chip_info->sampling_freq_avail[data->sampling_freq][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ if (!data->chip_info->iir_filter_coeffs_avail)
+ return -EINVAL;
+
+ *val = (1 << data->iir_filter_coeff) - 1;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bmp280_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct bmp280_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = bmp280_read_raw_impl(indio_dev, chan, val, val2, mask);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return ret;
+}
+
+static int bme280_write_oversampling_ratio_humid(struct bmp280_data *data,
+ int val)
+{
+ const int *avail = data->chip_info->oversampling_humid_avail;
+ const int n = data->chip_info->num_oversampling_humid_avail;
+ int ret, prev;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (avail[i] == val) {
+ prev = data->oversampling_humid;
+ data->oversampling_humid = ilog2(val);
+
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->oversampling_humid = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int bmp280_write_oversampling_ratio_temp(struct bmp280_data *data,
+ int val)
+{
+ const int *avail = data->chip_info->oversampling_temp_avail;
+ const int n = data->chip_info->num_oversampling_temp_avail;
+ int ret, prev;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (avail[i] == val) {
+ prev = data->oversampling_temp;
+ data->oversampling_temp = ilog2(val);
+
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->oversampling_temp = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int bmp280_write_oversampling_ratio_press(struct bmp280_data *data,
+ int val)
+{
+ const int *avail = data->chip_info->oversampling_press_avail;
+ const int n = data->chip_info->num_oversampling_press_avail;
+ int ret, prev;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (avail[i] == val) {
+ prev = data->oversampling_press;
+ data->oversampling_press = ilog2(val);
+
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->oversampling_press = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int bmp280_write_sampling_frequency(struct bmp280_data *data,
+ int val, int val2)
+{
+ const int (*avail)[2] = data->chip_info->sampling_freq_avail;
+ const int n = data->chip_info->num_sampling_freq_avail / 2;
+ int ret, prev;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (avail[i][0] == val && avail[i][1] == val2) {
+ prev = data->sampling_freq;
+ data->sampling_freq = i;
+
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->sampling_freq = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int bmp280_write_iir_filter_coeffs(struct bmp280_data *data, int val)
+{
+ const int *avail = data->chip_info->iir_filter_coeffs_avail;
+ const int n = data->chip_info->num_iir_filter_coeffs_avail;
+ int ret, prev;
+ int i;
+
+ for (i = 0; i < n; i++) {
+ if (avail[i] - 1 == val) {
+ prev = data->iir_filter_coeff;
+ data->iir_filter_coeff = i;
+
+ ret = data->chip_info->chip_config(data);
+ if (ret) {
+ data->iir_filter_coeff = prev;
+ data->chip_info->chip_config(data);
+ return ret;
+
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int bmp280_write_raw_impl(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct bmp280_data *data = iio_priv(indio_dev);
+
+ guard(mutex)(&data->lock);
+
+ /*
+ * Helper functions to update sensor running configuration.
+ * If an error happens applying new settings, will try restore
+ * previous parameters to ensure the sensor is left in a known
+ * working configuration.
+ */
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_HUMIDITYRELATIVE:
+ return bme280_write_oversampling_ratio_humid(data, val);
+ case IIO_PRESSURE:
+ return bmp280_write_oversampling_ratio_press(data, val);
+ case IIO_TEMP:
+ return bmp280_write_oversampling_ratio_temp(data, val);
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ return bmp280_write_sampling_frequency(data, val, val2);
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ return bmp280_write_iir_filter_coeffs(data, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int bmp280_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct bmp280_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = bmp280_write_raw_impl(indio_dev, chan, val, val2, mask);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return ret;
+}
+
+static int bmp280_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ struct bmp280_data *data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ *vals = data->chip_info->oversampling_press_avail;
+ *length = data->chip_info->num_oversampling_press_avail;
+ break;
+ case IIO_TEMP:
+ *vals = data->chip_info->oversampling_temp_avail;
+ *length = data->chip_info->num_oversampling_temp_avail;
+ break;
+ default:
+ return -EINVAL;
+ }
+ *type = IIO_VAL_INT;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *vals = (const int *)data->chip_info->sampling_freq_avail;
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ /* Values are stored in a 2D matrix */
+ *length = data->chip_info->num_sampling_freq_avail;
+ return IIO_AVAIL_LIST;
+ case IIO_CHAN_INFO_LOW_PASS_FILTER_3DB_FREQUENCY:
+ *vals = data->chip_info->iir_filter_coeffs_avail;
+ *type = IIO_VAL_INT;
+ *length = data->chip_info->num_iir_filter_coeffs_avail;
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info bmp280_info = {
+ .read_raw = &bmp280_read_raw,
+ .read_avail = &bmp280_read_avail,
+ .write_raw = &bmp280_write_raw,
+};
+
+static const unsigned long bmp280_avail_scan_masks[] = {
+ BIT(BMP280_TEMP) | BIT(BMP280_PRESS),
+ 0
+};
+
+static const unsigned long bme280_avail_scan_masks[] = {
+ BIT(BME280_HUMID) | BIT(BMP280_TEMP) | BIT(BMP280_PRESS),
+ 0
+};
+
+static int bmp280_preinit(struct bmp280_data *data)
+{
+ struct device *dev = data->dev;
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_write(data->regmap, BMP280_REG_RESET, BMP280_RST_SOFT_CMD);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to reset device.\n");
+
+ /*
+ * According to the datasheet in Chapter 1: Specification, Table 2,
+ * after resetting, the device uses the complete power-on sequence so
+ * it needs to wait for the defined start-up time.
+ */
+ fsleep(data->start_up_time_us);
+
+ ret = regmap_read(data->regmap, BMP280_REG_STATUS, &reg);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to read status register.\n");
+
+ if (reg & BMP280_REG_STATUS_IM_UPDATE)
+ return dev_err_probe(dev, -EIO, "Failed to copy NVM contents.\n");
+
+ return 0;
+}
+
+static const u8 bmp280_operation_mode[] = {
+ [BMP280_SLEEP] = BMP280_MODE_SLEEP,
+ [BMP280_FORCED] = BMP280_MODE_FORCED,
+ [BMP280_NORMAL] = BMP280_MODE_NORMAL,
+};
+
+static int bmp280_set_mode(struct bmp280_data *data, enum bmp280_op_mode mode)
+{
+ int ret;
+
+ ret = regmap_write_bits(data->regmap, BMP280_REG_CTRL_MEAS,
+ BMP280_MODE_MASK, bmp280_operation_mode[mode]);
+ if (ret) {
+ dev_err(data->dev, "failed to write ctrl_meas register.\n");
+ return ret;
+ }
+
+ data->op_mode = mode;
+
+ return 0;
+}
+
+static int bmp280_wait_conv(struct bmp280_data *data)
+{
+ unsigned int reg, meas_time_us;
+ int ret;
+
+ /* Constant part of the measurement time */
+ meas_time_us = BMP280_MEAS_OFFSET;
+
+ /*
+ * Check if we are using a BME280 device,
+ * Humidity measurement time
+ */
+ if (data->chip_info->oversampling_humid_avail)
+ meas_time_us += BMP280_PRESS_HUMID_MEAS_OFFSET +
+ BIT(data->oversampling_humid) * BMP280_MEAS_DUR;
+
+ /* Pressure measurement time */
+ meas_time_us += BMP280_PRESS_HUMID_MEAS_OFFSET +
+ BIT(data->oversampling_press) * BMP280_MEAS_DUR;
+
+ /* Temperature measurement time */
+ meas_time_us += BIT(data->oversampling_temp) * BMP280_MEAS_DUR;
+
+ /* Waiting time according to the BM(P/E)2 Sensor API */
+ fsleep(meas_time_us);
+
+ ret = regmap_read(data->regmap, BMP280_REG_STATUS, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to read status register.\n");
+ return ret;
+ }
+
+ if (reg & BMP280_REG_STATUS_MEAS_BIT) {
+ dev_err(data->dev, "Measurement cycle didn't complete.\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int bmp280_chip_config(struct bmp280_data *data)
+{
+ u8 osrs = FIELD_PREP(BMP280_OSRS_TEMP_MASK, data->oversampling_temp + 1) |
+ FIELD_PREP(BMP280_OSRS_PRESS_MASK, data->oversampling_press + 1);
+ int ret;
+
+ ret = regmap_write_bits(data->regmap, BMP280_REG_CTRL_MEAS,
+ BMP280_OSRS_TEMP_MASK |
+ BMP280_OSRS_PRESS_MASK |
+ BMP280_MODE_MASK,
+ osrs | BMP280_MODE_SLEEP);
+ if (ret) {
+ dev_err(data->dev, "failed to write ctrl_meas register\n");
+ return ret;
+ }
+
+ ret = regmap_update_bits(data->regmap, BMP280_REG_CONFIG,
+ BMP280_FILTER_MASK,
+ BMP280_FILTER_4X);
+ if (ret) {
+ dev_err(data->dev, "failed to write config register\n");
+ return ret;
+ }
+
+ return ret;
+}
+
+static irqreturn_t bmp280_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bmp280_data *data = iio_priv(indio_dev);
+ u32 adc_temp, adc_press;
+ s32 t_fine;
+ struct {
+ u32 comp_press;
+ s32 comp_temp;
+ aligned_s64 timestamp;
+ } buffer;
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ /* Burst read data registers */
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB,
+ data->buf, BMP280_BURST_READ_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to burst read sensor data\n");
+ goto out;
+ }
+
+ /* Temperature calculations */
+ adc_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[3]));
+ if (adc_temp == BMP280_TEMP_SKIPPED) {
+ dev_err(data->dev, "reading temperature skipped\n");
+ goto out;
+ }
+
+ buffer.comp_temp = bmp280_compensate_temp(data, adc_temp);
+
+ /* Pressure calculations */
+ adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[0]));
+ if (adc_press == BMP280_PRESS_SKIPPED) {
+ dev_err(data->dev, "reading pressure skipped\n");
+ goto out;
+ }
+
+ t_fine = bmp280_calc_t_fine(data, adc_temp);
+ buffer.comp_press = bmp280_compensate_press(data, adc_press, t_fine);
+
+ iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer),
+ iio_get_time_ns(indio_dev));
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const int bmp280_oversampling_avail[] = { 1, 2, 4, 8, 16 };
+static const u8 bmp280_chip_ids[] = { BMP280_CHIP_ID };
+static const int bmp280_temp_coeffs[] = { 10, 1 };
+static const int bmp280_press_coeffs[] = { 1, 256000 };
+
+const struct bmp280_chip_info bmp280_chip_info = {
+ .id_reg = BMP280_REG_ID,
+ .chip_id = bmp280_chip_ids,
+ .num_chip_id = ARRAY_SIZE(bmp280_chip_ids),
+ .regmap_config = &bmp280_regmap_config,
+ .start_up_time_us = 2000,
+ .channels = bmp280_channels,
+ .num_channels = ARRAY_SIZE(bmp280_channels),
+ .avail_scan_masks = bmp280_avail_scan_masks,
+
+ .oversampling_temp_avail = bmp280_oversampling_avail,
+ .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ /*
+ * Oversampling config values on BMx280 have one additional setting
+ * that other generations of the family don't:
+ * The value 0 means the measurement is bypassed instead of
+ * oversampling set to x1.
+ *
+ * To account for this difference, and preserve the same common
+ * config logic, this is handled later on chip_config callback
+ * incrementing one unit the oversampling setting.
+ */
+ .oversampling_temp_default = BMP280_OSRS_TEMP_2X - 1,
+
+ .oversampling_press_avail = bmp280_oversampling_avail,
+ .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ .oversampling_press_default = BMP280_OSRS_PRESS_16X - 1,
+
+ .temp_coeffs = bmp280_temp_coeffs,
+ .temp_coeffs_type = IIO_VAL_FRACTIONAL,
+ .press_coeffs = bmp280_press_coeffs,
+ .press_coeffs_type = IIO_VAL_FRACTIONAL,
+
+ .chip_config = bmp280_chip_config,
+ .read_temp = bmp280_read_temp,
+ .read_press = bmp280_read_press,
+ .read_calib = bmp280_read_calib,
+ .set_mode = bmp280_set_mode,
+ .wait_conv = bmp280_wait_conv,
+ .preinit = bmp280_preinit,
+
+ .trigger_handler = bmp280_trigger_handler,
+};
+EXPORT_SYMBOL_NS(bmp280_chip_info, "IIO_BMP280");
+
+static int bme280_chip_config(struct bmp280_data *data)
+{
+ u8 osrs = FIELD_PREP(BME280_OSRS_HUMIDITY_MASK, data->oversampling_humid + 1);
+ int ret;
+
+ /*
+ * Oversampling of humidity must be set before oversampling of
+ * temperature/pressure is set to become effective.
+ */
+ ret = regmap_update_bits(data->regmap, BME280_REG_CTRL_HUMIDITY,
+ BME280_OSRS_HUMIDITY_MASK, osrs);
+ if (ret) {
+ dev_err(data->dev, "failed to set humidity oversampling");
+ return ret;
+ }
+
+ return bmp280_chip_config(data);
+}
+
+static irqreturn_t bme280_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bmp280_data *data = iio_priv(indio_dev);
+ u32 adc_temp, adc_press, adc_humidity;
+ s32 t_fine;
+ struct {
+ u32 comp_press;
+ s32 comp_temp;
+ u32 comp_humidity;
+ aligned_s64 timestamp;
+ } buffer = { }; /* Don't leak uninitialized stack to userspace. */
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ /* Burst read data registers */
+ ret = regmap_bulk_read(data->regmap, BMP280_REG_PRESS_MSB,
+ data->buf, BME280_BURST_READ_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to burst read sensor data\n");
+ goto out;
+ }
+
+ /* Temperature calculations */
+ adc_temp = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[3]));
+ if (adc_temp == BMP280_TEMP_SKIPPED) {
+ dev_err(data->dev, "reading temperature skipped\n");
+ goto out;
+ }
+
+ buffer.comp_temp = bmp280_compensate_temp(data, adc_temp);
+
+ /* Pressure calculations */
+ adc_press = FIELD_GET(BMP280_MEAS_TRIM_MASK, get_unaligned_be24(&data->buf[0]));
+ if (adc_press == BMP280_PRESS_SKIPPED) {
+ dev_err(data->dev, "reading pressure skipped\n");
+ goto out;
+ }
+
+ t_fine = bmp280_calc_t_fine(data, adc_temp);
+ buffer.comp_press = bmp280_compensate_press(data, adc_press, t_fine);
+
+ /* Humidity calculations */
+ adc_humidity = get_unaligned_be16(&data->buf[6]);
+
+ if (adc_humidity == BMP280_HUMIDITY_SKIPPED) {
+ dev_err(data->dev, "reading humidity skipped\n");
+ goto out;
+ }
+
+ buffer.comp_humidity = bme280_compensate_humidity(data, adc_humidity,
+ t_fine);
+
+ iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer),
+ iio_get_time_ns(indio_dev));
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int __bmp280_trigger_probe(struct iio_dev *indio_dev,
+ const struct iio_trigger_ops *trigger_ops,
+ int (*int_pin_config)(struct bmp280_data *data),
+ irq_handler_t irq_thread_handler)
+{
+ struct bmp280_data *data = iio_priv(indio_dev);
+ struct device *dev = data->dev;
+ u32 irq_type;
+ int ret, irq;
+
+ irq = fwnode_irq_get(dev_fwnode(dev), 0);
+ if (irq < 0)
+ return dev_err_probe(dev, irq, "No interrupt found.\n");
+
+ irq_type = irq_get_trigger_type(irq);
+ switch (irq_type) {
+ case IRQF_TRIGGER_RISING:
+ data->trig_active_high = true;
+ break;
+ case IRQF_TRIGGER_FALLING:
+ data->trig_active_high = false;
+ break;
+ default:
+ return dev_err_probe(dev, -EINVAL, "Invalid interrupt type specified.\n");
+ }
+
+ data->trig_open_drain =
+ fwnode_property_read_bool(dev_fwnode(dev), "int-open-drain");
+
+ ret = int_pin_config(data);
+ if (ret)
+ return ret;
+
+ data->trig = devm_iio_trigger_alloc(data->dev, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!data->trig)
+ return -ENOMEM;
+
+ data->trig->ops = trigger_ops;
+ iio_trigger_set_drvdata(data->trig, data);
+
+ ret = devm_request_threaded_irq(data->dev, irq, NULL,
+ irq_thread_handler, IRQF_ONESHOT,
+ indio_dev->name, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_trigger_register(data->dev, data->trig);
+ if (ret)
+ return dev_err_probe(dev, ret, "iio trigger register failed.\n");
+
+ indio_dev->trig = iio_trigger_get(data->trig);
+
+ return 0;
+}
+
+static const u8 bme280_chip_ids[] = { BME280_CHIP_ID };
+static const int bme280_humid_coeffs[] = { 1000, 1024 };
+
+const struct bmp280_chip_info bme280_chip_info = {
+ .id_reg = BMP280_REG_ID,
+ .chip_id = bme280_chip_ids,
+ .num_chip_id = ARRAY_SIZE(bme280_chip_ids),
+ .regmap_config = &bme280_regmap_config,
+ .start_up_time_us = 2000,
+ .channels = bme280_channels,
+ .num_channels = ARRAY_SIZE(bme280_channels),
+ .avail_scan_masks = bme280_avail_scan_masks,
+
+ .oversampling_temp_avail = bmp280_oversampling_avail,
+ .num_oversampling_temp_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ .oversampling_temp_default = BMP280_OSRS_TEMP_2X - 1,
+
+ .oversampling_press_avail = bmp280_oversampling_avail,
+ .num_oversampling_press_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ .oversampling_press_default = BMP280_OSRS_PRESS_16X - 1,
+
+ .oversampling_humid_avail = bmp280_oversampling_avail,
+ .num_oversampling_humid_avail = ARRAY_SIZE(bmp280_oversampling_avail),
+ .oversampling_humid_default = BME280_OSRS_HUMIDITY_16X - 1,
+
+ .temp_coeffs = bmp280_temp_coeffs,
+ .temp_coeffs_type = IIO_VAL_FRACTIONAL,
+ .press_coeffs = bmp280_press_coeffs,
+ .press_coeffs_type = IIO_VAL_FRACTIONAL,
+ .humid_coeffs = bme280_humid_coeffs,
+ .humid_coeffs_type = IIO_VAL_FRACTIONAL,
+
+ .chip_config = bme280_chip_config,
+ .read_temp = bmp280_read_temp,
+ .read_press = bmp280_read_press,
+ .read_humid = bme280_read_humid,
+ .read_calib = bme280_read_calib,
+ .set_mode = bmp280_set_mode,
+ .wait_conv = bmp280_wait_conv,
+ .preinit = bmp280_preinit,
+
+ .trigger_handler = bme280_trigger_handler,
+};
+EXPORT_SYMBOL_NS(bme280_chip_info, "IIO_BMP280");
+
+/*
+ * Helper function to send a command to BMP3XX sensors.
+ *
+ * Sensor processes commands written to the CMD register and signals
+ * execution result through "cmd_rdy" and "cmd_error" flags available on
+ * STATUS and ERROR registers.
+ */
+static int bmp380_cmd(struct bmp280_data *data, u8 cmd)
+{
+ unsigned int reg;
+ int ret;
+
+ /* Check if device is ready to process a command */
+ ret = regmap_read(data->regmap, BMP380_REG_STATUS, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to read error register\n");
+ return ret;
+ }
+ if (!(reg & BMP380_STATUS_CMD_RDY_MASK)) {
+ dev_err(data->dev, "device is not ready to accept commands\n");
+ return -EBUSY;
+ }
+
+ /* Send command to process */
+ ret = regmap_write(data->regmap, BMP380_REG_CMD, cmd);
+ if (ret) {
+ dev_err(data->dev, "failed to send command to device\n");
+ return ret;
+ }
+ /* Wait for 2ms for command to be processed */
+ fsleep(data->start_up_time_us);
+ /* Check for command processing error */
+ ret = regmap_read(data->regmap, BMP380_REG_ERROR, &reg);
+ if (ret) {
+ dev_err(data->dev, "error reading ERROR reg\n");
+ return ret;
+ }
+ if (reg & BMP380_ERR_CMD_MASK) {
+ dev_err(data->dev, "error processing command 0x%X\n", cmd);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int bmp380_read_temp_adc(struct bmp280_data *data, u32 *adc_temp)
+{
+ u32 value_temp;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP380_REG_TEMP_XLSB,
+ data->buf, BMP280_NUM_TEMP_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to read temperature\n");
+ return ret;
+ }
+
+ value_temp = get_unaligned_le24(data->buf);
+ if (value_temp == BMP380_TEMP_SKIPPED) {
+ dev_err(data->dev, "reading temperature skipped\n");
+ return -EIO;
+ }
+ *adc_temp = value_temp;
+
+ return 0;
+}
+
+/*
+ * Returns temperature in Celsius degrees, resolution is 0.01º C. Output value
+ * of "5123" equals 51.2º C. t_fine carries fine temperature as global value.
+ *
+ * Taken from datasheet, Section Appendix 9, "Compensation formula" and repo
+ * https://github.com/BoschSensortec/BMP3-Sensor-API.
+ */
+static s32 bmp380_calc_t_fine(struct bmp280_data *data, u32 adc_temp)
+{
+ s64 var1, var2, var3, var4, var5, var6;
+ struct bmp380_calib *calib = &data->calib.bmp380;
+
+ var1 = ((s64) adc_temp) - (((s64) calib->T1) << 8);
+ var2 = var1 * ((s64) calib->T2);
+ var3 = var1 * var1;
+ var4 = var3 * ((s64) calib->T3);
+ var5 = (var2 << 18) + var4;
+ var6 = var5 >> 32;
+ return (s32)var6; /* t_fine = var6 */
+}
+
+static int bmp380_get_t_fine(struct bmp280_data *data, s32 *t_fine)
+{
+ s32 adc_temp;
+ int ret;
+
+ ret = bmp380_read_temp_adc(data, &adc_temp);
+ if (ret)
+ return ret;
+
+ *t_fine = bmp380_calc_t_fine(data, adc_temp);
+
+ return 0;
+}
+
+static int bmp380_compensate_temp(struct bmp280_data *data, u32 adc_temp)
+{
+ s64 comp_temp;
+ s32 var6;
+
+ var6 = bmp380_calc_t_fine(data, adc_temp);
+ comp_temp = (var6 * 25) >> 14;
+
+ comp_temp = clamp_val(comp_temp, BMP380_MIN_TEMP, BMP380_MAX_TEMP);
+ return (s32) comp_temp;
+}
+
+static int bmp380_read_press_adc(struct bmp280_data *data, u32 *adc_press)
+{
+ u32 value_press;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP380_REG_PRESS_XLSB,
+ data->buf, BMP280_NUM_PRESS_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to read pressure\n");
+ return ret;
+ }
+
+ value_press = get_unaligned_le24(data->buf);
+ if (value_press == BMP380_PRESS_SKIPPED) {
+ dev_err(data->dev, "reading pressure skipped\n");
+ return -EIO;
+ }
+ *adc_press = value_press;
+
+ return 0;
+}
+
+/*
+ * Returns pressure in Pa as an unsigned 32 bit integer in fractional Pascal.
+ * Output value of "9528709" represents 9528709/100 = 95287.09 Pa = 952.8709 hPa.
+ *
+ * Taken from datasheet, Section 9.3. "Pressure compensation" and repository
+ * https://github.com/BoschSensortec/BMP3-Sensor-API.
+ */
+static u32 bmp380_compensate_press(struct bmp280_data *data,
+ u32 adc_press, s32 t_fine)
+{
+ s64 var1, var2, var3, var4, var5, var6, offset, sensitivity;
+ struct bmp380_calib *calib = &data->calib.bmp380;
+ u32 comp_press;
+
+ var1 = (s64)t_fine * (s64)t_fine;
+ var2 = var1 >> 6;
+ var3 = (var2 * ((s64)t_fine)) >> 8;
+ var4 = ((s64)calib->P8 * var3) >> 5;
+ var5 = ((s64)calib->P7 * var1) << 4;
+ var6 = ((s64)calib->P6 * (s64)t_fine) << 22;
+ offset = ((s64)calib->P5 << 47) + var4 + var5 + var6;
+ var2 = ((s64)calib->P4 * var3) >> 5;
+ var4 = ((s64)calib->P3 * var1) << 2;
+ var5 = ((s64)calib->P2 - ((s64)1 << 14)) *
+ ((s64)t_fine << 21);
+ sensitivity = (((s64) calib->P1 - ((s64) 1 << 14)) << 46) +
+ var2 + var4 + var5;
+ var1 = (sensitivity >> 24) * (s64)adc_press;
+ var2 = (s64)calib->P10 * (s64)t_fine;
+ var3 = var2 + ((s64)calib->P9 << 16);
+ var4 = (var3 * (s64)adc_press) >> 13;
+
+ /*
+ * Dividing by 10 followed by multiplying by 10 to avoid
+ * possible overflow caused by (uncomp_data->pressure * partial_data4).
+ */
+ var5 = ((s64)adc_press * div_s64(var4, 10)) >> 9;
+ var5 *= 10;
+ var6 = (s64)adc_press * (s64)adc_press;
+ var2 = ((s64)calib->P11 * var6) >> 16;
+ var3 = (var2 * (s64)adc_press) >> 7;
+ var4 = (offset >> 2) + var1 + var5 + var3;
+ comp_press = ((u64)var4 * 25) >> 40;
+
+ comp_press = clamp_val(comp_press, BMP380_MIN_PRES, BMP380_MAX_PRES);
+ return comp_press;
+}
+
+static int bmp380_read_temp(struct bmp280_data *data, s32 *comp_temp)
+{
+ u32 adc_temp;
+ int ret;
+
+ ret = bmp380_read_temp_adc(data, &adc_temp);
+ if (ret)
+ return ret;
+
+ *comp_temp = bmp380_compensate_temp(data, adc_temp);
+
+ return 0;
+}
+
+static int bmp380_read_press(struct bmp280_data *data, u32 *comp_press)
+{
+ u32 adc_press, t_fine;
+ int ret;
+
+ ret = bmp380_get_t_fine(data, &t_fine);
+ if (ret)
+ return ret;
+
+ ret = bmp380_read_press_adc(data, &adc_press);
+ if (ret)
+ return ret;
+
+ *comp_press = bmp380_compensate_press(data, adc_press, t_fine);
+
+ return 0;
+}
+
+static int bmp380_read_calib(struct bmp280_data *data)
+{
+ struct bmp380_calib *calib = &data->calib.bmp380;
+ int ret;
+
+ /* Read temperature and pressure calibration data */
+ ret = regmap_bulk_read(data->regmap, BMP380_REG_CALIB_TEMP_START,
+ data->bmp380_cal_buf,
+ sizeof(data->bmp380_cal_buf));
+ if (ret) {
+ dev_err(data->dev,
+ "failed to read calibration parameters\n");
+ return ret;
+ }
+
+ /* Toss the temperature calibration data into the entropy pool */
+ add_device_randomness(data->bmp380_cal_buf,
+ sizeof(data->bmp380_cal_buf));
+
+ /* Parse calibration values */
+ calib->T1 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_T1]);
+ calib->T2 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_T2]);
+ calib->T3 = data->bmp380_cal_buf[BMP380_T3];
+ calib->P1 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P1]);
+ calib->P2 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P2]);
+ calib->P3 = data->bmp380_cal_buf[BMP380_P3];
+ calib->P4 = data->bmp380_cal_buf[BMP380_P4];
+ calib->P5 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P5]);
+ calib->P6 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P6]);
+ calib->P7 = data->bmp380_cal_buf[BMP380_P7];
+ calib->P8 = data->bmp380_cal_buf[BMP380_P8];
+ calib->P9 = get_unaligned_le16(&data->bmp380_cal_buf[BMP380_P9]);
+ calib->P10 = data->bmp380_cal_buf[BMP380_P10];
+ calib->P11 = data->bmp380_cal_buf[BMP380_P11];
+
+ return 0;
+}
+
+static const int bmp380_odr_table[][2] = {
+ [BMP380_ODR_200HZ] = {200, 0},
+ [BMP380_ODR_100HZ] = {100, 0},
+ [BMP380_ODR_50HZ] = {50, 0},
+ [BMP380_ODR_25HZ] = {25, 0},
+ [BMP380_ODR_12_5HZ] = {12, 500000},
+ [BMP380_ODR_6_25HZ] = {6, 250000},
+ [BMP380_ODR_3_125HZ] = {3, 125000},
+ [BMP380_ODR_1_5625HZ] = {1, 562500},
+ [BMP380_ODR_0_78HZ] = {0, 781250},
+ [BMP380_ODR_0_39HZ] = {0, 390625},
+ [BMP380_ODR_0_2HZ] = {0, 195313},
+ [BMP380_ODR_0_1HZ] = {0, 97656},
+ [BMP380_ODR_0_05HZ] = {0, 48828},
+ [BMP380_ODR_0_02HZ] = {0, 24414},
+ [BMP380_ODR_0_01HZ] = {0, 12207},
+ [BMP380_ODR_0_006HZ] = {0, 6104},
+ [BMP380_ODR_0_003HZ] = {0, 3052},
+ [BMP380_ODR_0_0015HZ] = {0, 1526},
+};
+
+static int bmp380_preinit(struct bmp280_data *data)
+{
+ /* BMP3xx requires soft-reset as part of initialization */
+ return bmp380_cmd(data, BMP380_CMD_SOFT_RESET);
+}
+
+static const u8 bmp380_operation_mode[] = {
+ [BMP280_SLEEP] = BMP380_MODE_SLEEP,
+ [BMP280_FORCED] = BMP380_MODE_FORCED,
+ [BMP280_NORMAL] = BMP380_MODE_NORMAL,
+};
+
+static int bmp380_set_mode(struct bmp280_data *data, enum bmp280_op_mode mode)
+{
+ int ret;
+
+ ret = regmap_write_bits(data->regmap, BMP380_REG_POWER_CONTROL,
+ BMP380_MODE_MASK,
+ FIELD_PREP(BMP380_MODE_MASK,
+ bmp380_operation_mode[mode]));
+ if (ret) {
+ dev_err(data->dev, "failed to write power control register.\n");
+ return ret;
+ }
+
+ data->op_mode = mode;
+
+ return 0;
+}
+
+static int bmp380_wait_conv(struct bmp280_data *data)
+{
+ unsigned int reg;
+ int ret, meas_time_us;
+
+ /* Offset measurement time */
+ meas_time_us = BMP380_MEAS_OFFSET;
+
+ /* Pressure measurement time */
+ meas_time_us += BMP380_PRESS_MEAS_OFFSET +
+ BIT(data->oversampling_press) * BMP380_MEAS_DUR;
+
+ /* Temperature measurement time */
+ meas_time_us += BMP380_TEMP_MEAS_OFFSET +
+ BIT(data->oversampling_temp) * BMP380_MEAS_DUR;
+
+ /* Measurement time defined in Datasheet Section 3.9.2 */
+ fsleep(meas_time_us);
+
+ ret = regmap_read(data->regmap, BMP380_REG_STATUS, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to read status register.\n");
+ return ret;
+ }
+
+ if (!((reg & BMP380_STATUS_DRDY_PRESS_MASK) &&
+ (reg & BMP380_STATUS_DRDY_TEMP_MASK))) {
+ dev_err(data->dev, "Measurement cycle didn't complete.\n");
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int bmp380_chip_config(struct bmp280_data *data)
+{
+ bool change = false, aux;
+ unsigned int tmp;
+ u8 osrs;
+ int ret;
+
+ /* Configure power control register */
+ ret = regmap_update_bits(data->regmap, BMP380_REG_POWER_CONTROL,
+ BMP380_CTRL_SENSORS_MASK,
+ BMP380_CTRL_SENSORS_PRESS_EN |
+ BMP380_CTRL_SENSORS_TEMP_EN);
+ if (ret) {
+ dev_err(data->dev,
+ "failed to write operation control register\n");
+ return ret;
+ }
+
+ /* Configure oversampling */
+ osrs = FIELD_PREP(BMP380_OSRS_TEMP_MASK, data->oversampling_temp) |
+ FIELD_PREP(BMP380_OSRS_PRESS_MASK, data->oversampling_press);
+
+ ret = regmap_update_bits_check(data->regmap, BMP380_REG_OSR,
+ BMP380_OSRS_TEMP_MASK |
+ BMP380_OSRS_PRESS_MASK,
+ osrs, &aux);
+ if (ret) {
+ dev_err(data->dev, "failed to write oversampling register\n");
+ return ret;
+ }
+ change = change || aux;
+
+ /* Configure output data rate */
+ ret = regmap_update_bits_check(data->regmap, BMP380_REG_ODR,
+ BMP380_ODRS_MASK, data->sampling_freq,
+ &aux);
+ if (ret) {
+ dev_err(data->dev, "failed to write ODR selection register\n");
+ return ret;
+ }
+ change = change || aux;
+
+ /* Set filter data */
+ ret = regmap_update_bits(data->regmap, BMP380_REG_CONFIG, BMP380_FILTER_MASK,
+ FIELD_PREP(BMP380_FILTER_MASK, data->iir_filter_coeff));
+ if (ret) {
+ dev_err(data->dev, "failed to write config register\n");
+ return ret;
+ }
+
+ if (change) {
+ /*
+ * The configurations errors are detected on the fly during a
+ * measurement cycle. If the sampling frequency is too low, it's
+ * faster to reset the measurement loop than wait until the next
+ * measurement is due.
+ *
+ * Resets sensor measurement loop toggling between sleep and
+ * normal operating modes.
+ */
+ ret = bmp380_set_mode(data, BMP280_SLEEP);
+ if (ret) {
+ dev_err(data->dev, "failed to set sleep mode\n");
+ return ret;
+ }
+
+ /*
+ * According to the BMP3 Sensor API, the sensor needs 5ms
+ * in order to go to the sleep mode.
+ */
+ fsleep(5 * USEC_PER_MSEC);
+
+ ret = bmp380_set_mode(data, BMP280_NORMAL);
+ if (ret) {
+ dev_err(data->dev, "failed to set normal mode\n");
+ return ret;
+ }
+ /*
+ * Waits for measurement before checking configuration error
+ * flag. Selected longest measurement time, calculated from
+ * formula in datasheet section 3.9.2 with an offset of ~+15%
+ * as it seen as well in table 3.9.1.
+ */
+ fsleep(150 * USEC_PER_MSEC);
+
+ /* Check config error flag */
+ ret = regmap_read(data->regmap, BMP380_REG_ERROR, &tmp);
+ if (ret) {
+ dev_err(data->dev, "failed to read error register\n");
+ return ret;
+ }
+ if (tmp & BMP380_ERR_CONF_MASK) {
+ dev_warn(data->dev,
+ "sensor flagged configuration as incompatible\n");
+ return -EINVAL;
+ }
+ }
+
+ /* Dummy read to empty data registers. */
+ ret = bmp380_read_press(data, &tmp);
+ if (ret)
+ return ret;
+
+ ret = bmp380_set_mode(data, BMP280_SLEEP);
+ if (ret)
+ dev_err(data->dev, "failed to set sleep mode.\n");
+
+ return ret;
+}
+
+static int bmp380_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct bmp280_data *data = iio_trigger_get_drvdata(trig);
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ ret = regmap_update_bits(data->regmap, BMP380_REG_INT_CONTROL,
+ BMP380_INT_CTRL_DRDY_EN,
+ FIELD_PREP(BMP380_INT_CTRL_DRDY_EN, !!state));
+ if (ret)
+ dev_err(data->dev,
+ "Could not %s interrupt.\n", str_enable_disable(state));
+ return ret;
+}
+
+static const struct iio_trigger_ops bmp380_trigger_ops = {
+ .set_trigger_state = &bmp380_data_rdy_trigger_set_state,
+};
+
+static int bmp380_int_pin_config(struct bmp280_data *data)
+{
+ int pin_drive_cfg = FIELD_PREP(BMP380_INT_CTRL_OPEN_DRAIN,
+ data->trig_open_drain);
+ int pin_level_cfg = FIELD_PREP(BMP380_INT_CTRL_LEVEL,
+ data->trig_active_high);
+ int ret, int_pin_cfg = pin_drive_cfg | pin_level_cfg;
+
+ ret = regmap_update_bits(data->regmap, BMP380_REG_INT_CONTROL,
+ BMP380_INT_CTRL_SETTINGS_MASK, int_pin_cfg);
+ if (ret)
+ dev_err(data->dev, "Could not set interrupt settings.\n");
+
+ return ret;
+}
+
+static irqreturn_t bmp380_irq_thread_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct bmp280_data *data = iio_priv(indio_dev);
+ unsigned int int_ctrl;
+ int ret;
+
+ ret = regmap_read(data->regmap, BMP380_REG_INT_STATUS, &int_ctrl);
+ if (ret)
+ return IRQ_NONE;
+
+ if (FIELD_GET(BMP380_INT_STATUS_DRDY, int_ctrl))
+ iio_trigger_poll_nested(data->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int bmp380_trigger_probe(struct iio_dev *indio_dev)
+{
+ return __bmp280_trigger_probe(indio_dev, &bmp380_trigger_ops,
+ bmp380_int_pin_config,
+ bmp380_irq_thread_handler);
+}
+
+static irqreturn_t bmp380_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bmp280_data *data = iio_priv(indio_dev);
+ u32 adc_temp, adc_press;
+ s32 t_fine;
+ struct {
+ u32 comp_press;
+ s32 comp_temp;
+ aligned_s64 timestamp;
+ } buffer = { };
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ /* Burst read data registers */
+ ret = regmap_bulk_read(data->regmap, BMP380_REG_PRESS_XLSB,
+ data->buf, BMP280_BURST_READ_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to burst read sensor data\n");
+ goto out;
+ }
+
+ /* Temperature calculations */
+ adc_temp = get_unaligned_le24(&data->buf[3]);
+ if (adc_temp == BMP380_TEMP_SKIPPED) {
+ dev_err(data->dev, "reading temperature skipped\n");
+ goto out;
+ }
+
+ buffer.comp_temp = bmp380_compensate_temp(data, adc_temp);
+
+ /* Pressure calculations */
+ adc_press = get_unaligned_le24(&data->buf[0]);
+ if (adc_press == BMP380_PRESS_SKIPPED) {
+ dev_err(data->dev, "reading pressure skipped\n");
+ goto out;
+ }
+
+ t_fine = bmp380_calc_t_fine(data, adc_temp);
+ buffer.comp_press = bmp380_compensate_press(data, adc_press, t_fine);
+
+ iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer),
+ iio_get_time_ns(indio_dev));
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const int bmp380_oversampling_avail[] = { 1, 2, 4, 8, 16, 32 };
+static const int bmp380_iir_filter_coeffs_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128};
+static const u8 bmp380_chip_ids[] = { BMP380_CHIP_ID, BMP390_CHIP_ID };
+static const int bmp380_temp_coeffs[] = { 10, 1 };
+static const int bmp380_press_coeffs[] = { 1, 100000 };
+
+const struct bmp280_chip_info bmp380_chip_info = {
+ .id_reg = BMP380_REG_ID,
+ .chip_id = bmp380_chip_ids,
+ .num_chip_id = ARRAY_SIZE(bmp380_chip_ids),
+ .regmap_config = &bmp380_regmap_config,
+ .spi_read_extra_byte = true,
+ .start_up_time_us = 2000,
+ .channels = bmp380_channels,
+ .num_channels = ARRAY_SIZE(bmp380_channels),
+ .avail_scan_masks = bmp280_avail_scan_masks,
+
+ .oversampling_temp_avail = bmp380_oversampling_avail,
+ .num_oversampling_temp_avail = ARRAY_SIZE(bmp380_oversampling_avail),
+ .oversampling_temp_default = ilog2(1),
+
+ .oversampling_press_avail = bmp380_oversampling_avail,
+ .num_oversampling_press_avail = ARRAY_SIZE(bmp380_oversampling_avail),
+ .oversampling_press_default = ilog2(4),
+
+ .sampling_freq_avail = bmp380_odr_table,
+ .num_sampling_freq_avail = ARRAY_SIZE(bmp380_odr_table) * 2,
+ .sampling_freq_default = BMP380_ODR_50HZ,
+
+ .iir_filter_coeffs_avail = bmp380_iir_filter_coeffs_avail,
+ .num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail),
+ .iir_filter_coeff_default = 2,
+
+ .temp_coeffs = bmp380_temp_coeffs,
+ .temp_coeffs_type = IIO_VAL_FRACTIONAL,
+ .press_coeffs = bmp380_press_coeffs,
+ .press_coeffs_type = IIO_VAL_FRACTIONAL,
+
+ .chip_config = bmp380_chip_config,
+ .read_temp = bmp380_read_temp,
+ .read_press = bmp380_read_press,
+ .read_calib = bmp380_read_calib,
+ .set_mode = bmp380_set_mode,
+ .wait_conv = bmp380_wait_conv,
+ .preinit = bmp380_preinit,
+
+ .trigger_probe = bmp380_trigger_probe,
+ .trigger_handler = bmp380_trigger_handler,
+};
+EXPORT_SYMBOL_NS(bmp380_chip_info, "IIO_BMP280");
+
+static int bmp580_soft_reset(struct bmp280_data *data)
+{
+ unsigned int reg;
+ int ret;
+
+ ret = regmap_write(data->regmap, BMP580_REG_CMD, BMP580_CMD_SOFT_RESET);
+ if (ret) {
+ dev_err(data->dev, "failed to send reset command to device\n");
+ return ret;
+ }
+ /* From datasheet's table 4: electrical characteristics */
+ fsleep(2000);
+
+ /* Dummy read of chip_id */
+ ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to reestablish comms after reset\n");
+ return ret;
+ }
+
+ ret = regmap_read(data->regmap, BMP580_REG_INT_STATUS, &reg);
+ if (ret) {
+ dev_err(data->dev, "error reading interrupt status register\n");
+ return ret;
+ }
+ if (!(reg & BMP580_INT_STATUS_POR_MASK)) {
+ dev_err(data->dev, "error resetting sensor\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/**
+ * bmp580_nvm_operation() - Helper function to commit NVM memory operations
+ * @data: sensor data struct
+ * @is_write: flag to signal write operation
+ */
+static int bmp580_nvm_operation(struct bmp280_data *data, bool is_write)
+{
+ unsigned long timeout, poll;
+ unsigned int reg;
+ int ret;
+
+ /* Check NVM ready flag */
+ ret = regmap_read(data->regmap, BMP580_REG_STATUS, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to check nvm status\n");
+ return ret;
+ }
+ if (!(reg & BMP580_STATUS_NVM_RDY_MASK)) {
+ dev_err(data->dev, "sensor's nvm is not ready\n");
+ return -EIO;
+ }
+
+ /* Start NVM operation sequence */
+ ret = regmap_write(data->regmap, BMP580_REG_CMD,
+ BMP580_CMD_NVM_OP_SEQ_0);
+ if (ret) {
+ dev_err(data->dev,
+ "failed to send nvm operation's first sequence\n");
+ return ret;
+ }
+ if (is_write) {
+ /* Send NVM write sequence */
+ ret = regmap_write(data->regmap, BMP580_REG_CMD,
+ BMP580_CMD_NVM_WRITE_SEQ_1);
+ if (ret) {
+ dev_err(data->dev,
+ "failed to send nvm write sequence\n");
+ return ret;
+ }
+ /* Datasheet says on 4.8.1.2 it takes approximately 10ms */
+ poll = 2000;
+ timeout = 12000;
+ } else {
+ /* Send NVM read sequence */
+ ret = regmap_write(data->regmap, BMP580_REG_CMD,
+ BMP580_CMD_NVM_READ_SEQ_1);
+ if (ret) {
+ dev_err(data->dev,
+ "failed to send nvm read sequence\n");
+ return ret;
+ }
+ /* Datasheet says on 4.8.1.1 it takes approximately 200us */
+ poll = 50;
+ timeout = 400;
+ }
+
+ /* Wait until NVM is ready again */
+ ret = regmap_read_poll_timeout(data->regmap, BMP580_REG_STATUS, reg,
+ (reg & BMP580_STATUS_NVM_RDY_MASK),
+ poll, timeout);
+ if (ret) {
+ dev_err(data->dev, "error checking nvm operation status\n");
+ return ret;
+ }
+
+ /* Check NVM error flags */
+ if ((reg & BMP580_STATUS_NVM_ERR_MASK) || (reg & BMP580_STATUS_NVM_CMD_ERR_MASK)) {
+ dev_err(data->dev, "error processing nvm operation\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+/*
+ * Contrary to previous sensors families, compensation algorithm is builtin.
+ * We are only required to read the register raw data and adapt the ranges
+ * for what is expected on IIO ABI.
+ */
+
+static int bmp580_read_temp(struct bmp280_data *data, s32 *raw_temp)
+{
+ s32 value_temp;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP580_REG_TEMP_XLSB,
+ data->buf, BMP280_NUM_TEMP_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to read temperature\n");
+ return ret;
+ }
+
+ value_temp = get_unaligned_le24(data->buf);
+ if (value_temp == BMP580_TEMP_SKIPPED) {
+ dev_err(data->dev, "reading temperature skipped\n");
+ return -EIO;
+ }
+ *raw_temp = sign_extend32(value_temp, 23);
+
+ return 0;
+}
+
+static int bmp580_read_press(struct bmp280_data *data, u32 *raw_press)
+{
+ u32 value_press;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP580_REG_PRESS_XLSB,
+ data->buf, BMP280_NUM_PRESS_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to read pressure\n");
+ return ret;
+ }
+
+ value_press = get_unaligned_le24(data->buf);
+ if (value_press == BMP580_PRESS_SKIPPED) {
+ dev_err(data->dev, "reading pressure skipped\n");
+ return -EIO;
+ }
+ *raw_press = value_press;
+
+ return 0;
+}
+
+static const int bmp580_odr_table[][2] = {
+ [BMP580_ODR_240HZ] = {240, 0},
+ [BMP580_ODR_218HZ] = {218, 0},
+ [BMP580_ODR_199HZ] = {199, 0},
+ [BMP580_ODR_179HZ] = {179, 0},
+ [BMP580_ODR_160HZ] = {160, 0},
+ [BMP580_ODR_149HZ] = {149, 0},
+ [BMP580_ODR_140HZ] = {140, 0},
+ [BMP580_ODR_129HZ] = {129, 0},
+ [BMP580_ODR_120HZ] = {120, 0},
+ [BMP580_ODR_110HZ] = {110, 0},
+ [BMP580_ODR_100HZ] = {100, 0},
+ [BMP580_ODR_89HZ] = {89, 0},
+ [BMP580_ODR_80HZ] = {80, 0},
+ [BMP580_ODR_70HZ] = {70, 0},
+ [BMP580_ODR_60HZ] = {60, 0},
+ [BMP580_ODR_50HZ] = {50, 0},
+ [BMP580_ODR_45HZ] = {45, 0},
+ [BMP580_ODR_40HZ] = {40, 0},
+ [BMP580_ODR_35HZ] = {35, 0},
+ [BMP580_ODR_30HZ] = {30, 0},
+ [BMP580_ODR_25HZ] = {25, 0},
+ [BMP580_ODR_20HZ] = {20, 0},
+ [BMP580_ODR_15HZ] = {15, 0},
+ [BMP580_ODR_10HZ] = {10, 0},
+ [BMP580_ODR_5HZ] = {5, 0},
+ [BMP580_ODR_4HZ] = {4, 0},
+ [BMP580_ODR_3HZ] = {3, 0},
+ [BMP580_ODR_2HZ] = {2, 0},
+ [BMP580_ODR_1HZ] = {1, 0},
+ [BMP580_ODR_0_5HZ] = {0, 500000},
+ [BMP580_ODR_0_25HZ] = {0, 250000},
+ [BMP580_ODR_0_125HZ] = {0, 125000},
+};
+
+static const int bmp580_nvmem_addrs[] = { 0x20, 0x21, 0x22 };
+
+static int bmp580_nvmem_read_impl(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct bmp280_data *data = priv;
+ u16 *dst = val;
+ int ret, addr;
+
+ guard(mutex)(&data->lock);
+
+ /* Set sensor in standby mode */
+ ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG,
+ BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS,
+ BMP580_ODR_DEEPSLEEP_DIS |
+ FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP));
+ if (ret) {
+ dev_err(data->dev, "failed to change sensor to standby mode\n");
+ goto exit;
+ }
+ /* Wait standby transition time */
+ fsleep(2500);
+
+ while (bytes >= sizeof(*dst)) {
+ addr = bmp580_nvmem_addrs[offset / sizeof(*dst)];
+
+ ret = regmap_write(data->regmap, BMP580_REG_NVM_ADDR,
+ FIELD_PREP(BMP580_NVM_ROW_ADDR_MASK, addr));
+ if (ret) {
+ dev_err(data->dev, "error writing nvm address\n");
+ goto exit;
+ }
+
+ ret = bmp580_nvm_operation(data, false);
+ if (ret)
+ goto exit;
+
+ ret = regmap_bulk_read(data->regmap, BMP580_REG_NVM_DATA_LSB,
+ &data->le16, sizeof(data->le16));
+ if (ret) {
+ dev_err(data->dev, "error reading nvm data regs\n");
+ goto exit;
+ }
+
+ *dst++ = le16_to_cpu(data->le16);
+ bytes -= sizeof(*dst);
+ offset += sizeof(*dst);
+ }
+exit:
+ /* Restore chip config */
+ data->chip_info->chip_config(data);
+ return ret;
+}
+
+static int bmp580_nvmem_read(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct bmp280_data *data = priv;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = bmp580_nvmem_read_impl(priv, offset, val, bytes);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return ret;
+}
+
+static int bmp580_nvmem_write_impl(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct bmp280_data *data = priv;
+ u16 *buf = val;
+ int ret, addr;
+
+ guard(mutex)(&data->lock);
+
+ /* Set sensor in standby mode */
+ ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG,
+ BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS,
+ BMP580_ODR_DEEPSLEEP_DIS |
+ FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP));
+ if (ret) {
+ dev_err(data->dev, "failed to change sensor to standby mode\n");
+ goto exit;
+ }
+ /* Wait standby transition time */
+ fsleep(2500);
+
+ while (bytes >= sizeof(*buf)) {
+ addr = bmp580_nvmem_addrs[offset / sizeof(*buf)];
+
+ ret = regmap_write(data->regmap, BMP580_REG_NVM_ADDR,
+ BMP580_NVM_PROG_EN |
+ FIELD_PREP(BMP580_NVM_ROW_ADDR_MASK, addr));
+ if (ret) {
+ dev_err(data->dev, "error writing nvm address\n");
+ goto exit;
+ }
+ data->le16 = cpu_to_le16(*buf++);
+
+ ret = regmap_bulk_write(data->regmap, BMP580_REG_NVM_DATA_LSB,
+ &data->le16, sizeof(data->le16));
+ if (ret) {
+ dev_err(data->dev, "error writing LSB NVM data regs\n");
+ goto exit;
+ }
+
+ ret = bmp580_nvm_operation(data, true);
+ if (ret)
+ goto exit;
+
+ /* Disable programming mode bit */
+ ret = regmap_clear_bits(data->regmap, BMP580_REG_NVM_ADDR,
+ BMP580_NVM_PROG_EN);
+ if (ret) {
+ dev_err(data->dev, "error resetting nvm write\n");
+ goto exit;
+ }
+
+ bytes -= sizeof(*buf);
+ offset += sizeof(*buf);
+ }
+exit:
+ /* Restore chip config */
+ data->chip_info->chip_config(data);
+ return ret;
+}
+
+static int bmp580_nvmem_write(void *priv, unsigned int offset, void *val,
+ size_t bytes)
+{
+ struct bmp280_data *data = priv;
+ int ret;
+
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = bmp580_nvmem_write_impl(priv, offset, val, bytes);
+ pm_runtime_put_autosuspend(data->dev);
+
+ return ret;
+}
+
+static int bmp580_preinit(struct bmp280_data *data)
+{
+ struct nvmem_config config = {
+ .dev = data->dev,
+ .priv = data,
+ .name = "bmp580_nvmem",
+ .word_size = sizeof(u16),
+ .stride = sizeof(u16),
+ .size = 3 * sizeof(u16),
+ .reg_read = bmp580_nvmem_read,
+ .reg_write = bmp580_nvmem_write,
+ };
+ unsigned int reg;
+ int ret;
+
+ /* Issue soft-reset command */
+ ret = bmp580_soft_reset(data);
+ if (ret)
+ return ret;
+
+ /* Post powerup sequence */
+ ret = regmap_read(data->regmap, BMP580_REG_CHIP_ID, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to establish comms with the chip\n");
+ return ret;
+ }
+
+ /* Print warn message if we don't know the chip id */
+ if (reg != BMP580_CHIP_ID && reg != BMP580_CHIP_ID_ALT)
+ dev_warn(data->dev, "unexpected chip_id\n");
+
+ ret = regmap_read(data->regmap, BMP580_REG_STATUS, &reg);
+ if (ret) {
+ dev_err(data->dev, "failed to read nvm status\n");
+ return ret;
+ }
+
+ /* Check nvm status */
+ if (!(reg & BMP580_STATUS_NVM_RDY_MASK) || (reg & BMP580_STATUS_NVM_ERR_MASK)) {
+ dev_err(data->dev, "nvm error on powerup sequence\n");
+ return -EIO;
+ }
+
+ /* Register nvmem device */
+ return PTR_ERR_OR_ZERO(devm_nvmem_register(config.dev, &config));
+}
+
+static const u8 bmp580_operation_mode[] = {
+ [BMP280_SLEEP] = BMP580_MODE_SLEEP,
+ [BMP280_FORCED] = BMP580_MODE_FORCED,
+ [BMP280_NORMAL] = BMP580_MODE_NORMAL,
+};
+
+static int bmp580_set_mode(struct bmp280_data *data, enum bmp280_op_mode mode)
+{
+ struct device *dev = data->dev;
+ int ret;
+
+ if (mode == BMP280_FORCED) {
+ ret = regmap_set_bits(data->regmap, BMP580_REG_DSP_CONFIG,
+ BMP580_DSP_IIR_FORCED_FLUSH);
+ if (ret) {
+ dev_err(dev, "Could not flush IIR filter constants.\n");
+ return ret;
+ }
+ }
+
+ ret = regmap_write_bits(data->regmap, BMP580_REG_ODR_CONFIG,
+ BMP580_MODE_MASK,
+ FIELD_PREP(BMP580_MODE_MASK,
+ bmp580_operation_mode[mode]));
+ if (ret) {
+ dev_err(dev, "failed to write power control register.\n");
+ return ret;
+ }
+
+ data->op_mode = mode;
+
+ return 0;
+}
+
+static int bmp580_wait_conv(struct bmp280_data *data)
+{
+ /*
+ * Taken from datasheet, Section 2 "Specification, Table 3 "Electrical
+ * characteristics.
+ */
+ static const int time_conv_press[] = {
+ 0, 1050, 1785, 3045, 5670, 10920, 21420, 42420,
+ 84420,
+ };
+ static const int time_conv_temp[] = {
+ 0, 1050, 1105, 1575, 2205, 3465, 6090, 11340,
+ 21840,
+ };
+ int meas_time_us;
+
+ meas_time_us = 4 * USEC_PER_MSEC +
+ time_conv_temp[data->oversampling_temp] +
+ time_conv_press[data->oversampling_press];
+
+ /*
+ * Measurement time mentioned in Chapter 2, Table 4 of the datasheet.
+ * The extra 4ms is the required mode change to start of measurement
+ * time.
+ */
+ fsleep(meas_time_us);
+
+ return 0;
+}
+
+static int bmp580_chip_config(struct bmp280_data *data)
+{
+ bool change = false, aux;
+ unsigned int tmp;
+ u8 reg_val;
+ int ret;
+
+ /* Sets sensor in standby mode */
+ ret = regmap_update_bits(data->regmap, BMP580_REG_ODR_CONFIG,
+ BMP580_MODE_MASK | BMP580_ODR_DEEPSLEEP_DIS,
+ BMP580_ODR_DEEPSLEEP_DIS |
+ FIELD_PREP(BMP580_MODE_MASK, BMP580_MODE_SLEEP));
+ if (ret) {
+ dev_err(data->dev, "failed to change sensor to standby mode\n");
+ return ret;
+ }
+ /* From datasheet's table 4: electrical characteristics */
+ fsleep(2500);
+
+ /* Set default DSP mode settings */
+ reg_val = FIELD_PREP(BMP580_DSP_COMP_MASK, BMP580_DSP_PRESS_TEMP_COMP_EN) |
+ BMP580_DSP_SHDW_IIR_TEMP_EN | BMP580_DSP_SHDW_IIR_PRESS_EN;
+
+ ret = regmap_update_bits(data->regmap, BMP580_REG_DSP_CONFIG,
+ BMP580_DSP_COMP_MASK |
+ BMP580_DSP_SHDW_IIR_TEMP_EN |
+ BMP580_DSP_SHDW_IIR_PRESS_EN, reg_val);
+ if (ret) {
+ dev_err(data->dev, "failed to change DSP mode settings\n");
+ return ret;
+ }
+
+ /* Configure oversampling */
+ reg_val = FIELD_PREP(BMP580_OSR_TEMP_MASK, data->oversampling_temp) |
+ FIELD_PREP(BMP580_OSR_PRESS_MASK, data->oversampling_press) |
+ BMP580_OSR_PRESS_EN;
+
+ ret = regmap_update_bits_check(data->regmap, BMP580_REG_OSR_CONFIG,
+ BMP580_OSR_TEMP_MASK |
+ BMP580_OSR_PRESS_MASK |
+ BMP580_OSR_PRESS_EN,
+ reg_val, &aux);
+ if (ret) {
+ dev_err(data->dev, "failed to write oversampling register\n");
+ return ret;
+ }
+ change = change || aux;
+
+ /* Configure output data rate */
+ ret = regmap_update_bits_check(data->regmap, BMP580_REG_ODR_CONFIG, BMP580_ODR_MASK,
+ FIELD_PREP(BMP580_ODR_MASK, data->sampling_freq),
+ &aux);
+ if (ret) {
+ dev_err(data->dev, "failed to write ODR configuration register\n");
+ return ret;
+ }
+ change = change || aux;
+
+ /* Set filter data */
+ reg_val = FIELD_PREP(BMP580_DSP_IIR_PRESS_MASK, data->iir_filter_coeff) |
+ FIELD_PREP(BMP580_DSP_IIR_TEMP_MASK, data->iir_filter_coeff);
+
+ ret = regmap_update_bits(data->regmap, BMP580_REG_DSP_IIR,
+ BMP580_DSP_IIR_PRESS_MASK | BMP580_DSP_IIR_TEMP_MASK,
+ reg_val);
+ if (ret) {
+ dev_err(data->dev, "failed to write config register\n");
+ return ret;
+ }
+
+ if (change) {
+ /*
+ * Check if ODR and OSR settings are valid or we are
+ * operating in a degraded mode.
+ */
+ ret = regmap_read(data->regmap, BMP580_REG_EFF_OSR, &tmp);
+ if (ret) {
+ dev_err(data->dev,
+ "error reading effective OSR register\n");
+ return ret;
+ }
+ if (!(tmp & BMP580_EFF_OSR_VALID_ODR)) {
+ dev_warn(data->dev, "OSR and ODR incompatible settings detected\n");
+ /* Set current OSR settings from data on effective OSR */
+ data->oversampling_temp = FIELD_GET(BMP580_EFF_OSR_TEMP_MASK, tmp);
+ data->oversampling_press = FIELD_GET(BMP580_EFF_OSR_PRESS_MASK, tmp);
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int bmp580_data_rdy_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct bmp280_data *data = iio_trigger_get_drvdata(trig);
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ ret = regmap_update_bits(data->regmap, BMP580_REG_INT_CONFIG,
+ BMP580_INT_CONFIG_INT_EN,
+ FIELD_PREP(BMP580_INT_CONFIG_INT_EN, !!state));
+ if (ret)
+ dev_err(data->dev,
+ "Could not %s interrupt.\n", str_enable_disable(state));
+ return ret;
+}
+
+static const struct iio_trigger_ops bmp580_trigger_ops = {
+ .set_trigger_state = &bmp580_data_rdy_trigger_set_state,
+};
+
+static int bmp580_int_pin_config(struct bmp280_data *data)
+{
+ int pin_drive_cfg = FIELD_PREP(BMP580_INT_CONFIG_OPEN_DRAIN,
+ data->trig_open_drain);
+ int pin_level_cfg = FIELD_PREP(BMP580_INT_CONFIG_LEVEL,
+ data->trig_active_high);
+ int ret, int_pin_cfg = pin_drive_cfg | pin_level_cfg;
+
+ ret = regmap_update_bits(data->regmap, BMP580_REG_INT_CONFIG,
+ BMP580_INT_CONFIG_MASK, int_pin_cfg);
+ if (ret) {
+ dev_err(data->dev, "Could not set interrupt settings.\n");
+ return ret;
+ }
+
+ ret = regmap_set_bits(data->regmap, BMP580_REG_INT_SOURCE,
+ BMP580_INT_SOURCE_DRDY);
+ if (ret)
+ dev_err(data->dev, "Could not set interrupt source.\n");
+
+ return ret;
+}
+
+static irqreturn_t bmp580_irq_thread_handler(int irq, void *p)
+{
+ struct iio_dev *indio_dev = p;
+ struct bmp280_data *data = iio_priv(indio_dev);
+ unsigned int int_ctrl;
+ int ret;
+
+ ret = regmap_read(data->regmap, BMP580_REG_INT_STATUS, &int_ctrl);
+ if (ret)
+ return IRQ_NONE;
+
+ if (FIELD_GET(BMP580_INT_STATUS_DRDY_MASK, int_ctrl))
+ iio_trigger_poll_nested(data->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int bmp580_trigger_probe(struct iio_dev *indio_dev)
+{
+ return __bmp280_trigger_probe(indio_dev, &bmp580_trigger_ops,
+ bmp580_int_pin_config,
+ bmp580_irq_thread_handler);
+}
+
+static irqreturn_t bmp580_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bmp280_data *data = iio_priv(indio_dev);
+ struct {
+ __le32 comp_temp;
+ __le32 comp_press;
+ aligned_s64 timestamp;
+ } buffer = { };
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ /* Burst read data registers */
+ ret = regmap_bulk_read(data->regmap, BMP580_REG_TEMP_XLSB,
+ data->buf, BMP280_BURST_READ_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to burst read sensor data\n");
+ goto out;
+ }
+
+ /* Pressure calculations */
+ memcpy(&buffer.comp_press, &data->buf[3], 3);
+
+ /* Temperature calculations */
+ memcpy(&buffer.comp_temp, &data->buf[0], 3);
+
+ iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer),
+ iio_get_time_ns(indio_dev));
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const int bmp580_oversampling_avail[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
+static const u8 bmp580_chip_ids[] = { BMP580_CHIP_ID, BMP580_CHIP_ID_ALT };
+/* Instead of { 1000, 16 } we do this, to avoid overflow issues */
+static const int bmp580_temp_coeffs[] = { 125, 13 };
+static const int bmp580_press_coeffs[] = { 1, 64000};
+
+const struct bmp280_chip_info bmp580_chip_info = {
+ .id_reg = BMP580_REG_CHIP_ID,
+ .chip_id = bmp580_chip_ids,
+ .num_chip_id = ARRAY_SIZE(bmp580_chip_ids),
+ .regmap_config = &bmp580_regmap_config,
+ .start_up_time_us = 2000,
+ .channels = bmp580_channels,
+ .num_channels = ARRAY_SIZE(bmp580_channels),
+ .avail_scan_masks = bmp280_avail_scan_masks,
+
+ .oversampling_temp_avail = bmp580_oversampling_avail,
+ .num_oversampling_temp_avail = ARRAY_SIZE(bmp580_oversampling_avail),
+ .oversampling_temp_default = ilog2(1),
+
+ .oversampling_press_avail = bmp580_oversampling_avail,
+ .num_oversampling_press_avail = ARRAY_SIZE(bmp580_oversampling_avail),
+ .oversampling_press_default = ilog2(4),
+
+ .sampling_freq_avail = bmp580_odr_table,
+ .num_sampling_freq_avail = ARRAY_SIZE(bmp580_odr_table) * 2,
+ .sampling_freq_default = BMP580_ODR_50HZ,
+
+ .iir_filter_coeffs_avail = bmp380_iir_filter_coeffs_avail,
+ .num_iir_filter_coeffs_avail = ARRAY_SIZE(bmp380_iir_filter_coeffs_avail),
+ .iir_filter_coeff_default = 2,
+
+ .temp_coeffs = bmp580_temp_coeffs,
+ .temp_coeffs_type = IIO_VAL_FRACTIONAL_LOG2,
+ .press_coeffs = bmp580_press_coeffs,
+ .press_coeffs_type = IIO_VAL_FRACTIONAL,
+
+ .chip_config = bmp580_chip_config,
+ .read_temp = bmp580_read_temp,
+ .read_press = bmp580_read_press,
+ .set_mode = bmp580_set_mode,
+ .wait_conv = bmp580_wait_conv,
+ .preinit = bmp580_preinit,
+
+ .trigger_probe = bmp580_trigger_probe,
+ .trigger_handler = bmp580_trigger_handler,
+};
+EXPORT_SYMBOL_NS(bmp580_chip_info, "IIO_BMP280");
+
+static int bmp180_wait_for_eoc(struct bmp280_data *data, u8 ctrl_meas)
+{
+ static const int conversion_time_max[] = { 4500, 7500, 13500, 25500 };
+ unsigned int delay_us;
+ unsigned int ctrl;
+ int ret;
+
+ if (data->use_eoc)
+ reinit_completion(&data->done);
+
+ ret = regmap_write(data->regmap, BMP280_REG_CTRL_MEAS, ctrl_meas);
+ if (ret) {
+ dev_err(data->dev, "failed to write crtl_meas register\n");
+ return ret;
+ }
+
+ if (data->use_eoc) {
+ /*
+ * If we have a completion interrupt, use it, wait up to
+ * 100ms. The longest conversion time listed is 76.5 ms for
+ * advanced resolution mode.
+ */
+ ret = wait_for_completion_timeout(&data->done,
+ 1 + msecs_to_jiffies(100));
+ if (!ret)
+ dev_err(data->dev, "timeout waiting for completion\n");
+ } else {
+ if (FIELD_GET(BMP180_MEAS_CTRL_MASK, ctrl_meas) == BMP180_MEAS_TEMP)
+ delay_us = 4500;
+ else
+ delay_us =
+ conversion_time_max[data->oversampling_press];
+
+ fsleep(delay_us);
+ }
+
+ ret = regmap_read(data->regmap, BMP280_REG_CTRL_MEAS, &ctrl);
+ if (ret) {
+ dev_err(data->dev, "failed to read ctrl_meas register\n");
+ return ret;
+ }
+
+ /* The value of this bit reset to "0" after conversion is complete */
+ if (ctrl & BMP180_MEAS_SCO) {
+ dev_err(data->dev, "conversion didn't complete\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int bmp180_read_temp_adc(struct bmp280_data *data, u32 *adc_temp)
+{
+ int ret;
+
+ ret = bmp180_wait_for_eoc(data,
+ FIELD_PREP(BMP180_MEAS_CTRL_MASK, BMP180_MEAS_TEMP) |
+ BMP180_MEAS_SCO);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB,
+ &data->be16, sizeof(data->be16));
+ if (ret) {
+ dev_err(data->dev, "failed to read temperature\n");
+ return ret;
+ }
+
+ *adc_temp = be16_to_cpu(data->be16);
+
+ return 0;
+}
+
+static int bmp180_read_calib(struct bmp280_data *data)
+{
+ struct bmp180_calib *calib = &data->calib.bmp180;
+ int ret;
+ int i;
+
+ ret = regmap_bulk_read(data->regmap, BMP180_REG_CALIB_START,
+ data->bmp180_cal_buf, sizeof(data->bmp180_cal_buf));
+ if (ret) {
+ dev_err(data->dev, "failed to read calibration parameters\n");
+ return ret;
+ }
+
+ /* None of the words has the value 0 or 0xFFFF */
+ for (i = 0; i < ARRAY_SIZE(data->bmp180_cal_buf); i++) {
+ if (data->bmp180_cal_buf[i] == cpu_to_be16(0) ||
+ data->bmp180_cal_buf[i] == cpu_to_be16(0xffff))
+ return -EIO;
+ }
+
+ /* Toss the calibration data into the entropy pool */
+ add_device_randomness(data->bmp180_cal_buf,
+ sizeof(data->bmp180_cal_buf));
+
+ calib->AC1 = be16_to_cpu(data->bmp180_cal_buf[AC1]);
+ calib->AC2 = be16_to_cpu(data->bmp180_cal_buf[AC2]);
+ calib->AC3 = be16_to_cpu(data->bmp180_cal_buf[AC3]);
+ calib->AC4 = be16_to_cpu(data->bmp180_cal_buf[AC4]);
+ calib->AC5 = be16_to_cpu(data->bmp180_cal_buf[AC5]);
+ calib->AC6 = be16_to_cpu(data->bmp180_cal_buf[AC6]);
+ calib->B1 = be16_to_cpu(data->bmp180_cal_buf[B1]);
+ calib->B2 = be16_to_cpu(data->bmp180_cal_buf[B2]);
+ calib->MB = be16_to_cpu(data->bmp180_cal_buf[MB]);
+ calib->MC = be16_to_cpu(data->bmp180_cal_buf[MC]);
+ calib->MD = be16_to_cpu(data->bmp180_cal_buf[MD]);
+
+ return 0;
+}
+
+/*
+ * Returns temperature in DegC, resolution is 0.1 DegC.
+ * t_fine carries fine temperature as global value.
+ *
+ * Taken from datasheet, Section 3.5, "Calculating pressure and temperature".
+ */
+
+static s32 bmp180_calc_t_fine(struct bmp280_data *data, u32 adc_temp)
+{
+ struct bmp180_calib *calib = &data->calib.bmp180;
+ s32 x1, x2;
+
+ x1 = ((((s32)adc_temp) - calib->AC6) * calib->AC5) >> 15;
+ x2 = (calib->MC << 11) / (x1 + calib->MD);
+ return x1 + x2; /* t_fine = x1 + x2; */
+}
+
+static int bmp180_get_t_fine(struct bmp280_data *data, s32 *t_fine)
+{
+ s32 adc_temp;
+ int ret;
+
+ ret = bmp180_read_temp_adc(data, &adc_temp);
+ if (ret)
+ return ret;
+
+ *t_fine = bmp180_calc_t_fine(data, adc_temp);
+
+ return 0;
+}
+
+static s32 bmp180_compensate_temp(struct bmp280_data *data, u32 adc_temp)
+{
+ return (bmp180_calc_t_fine(data, adc_temp) + 8) / 16;
+}
+
+static int bmp180_read_temp(struct bmp280_data *data, s32 *comp_temp)
+{
+ u32 adc_temp;
+ int ret;
+
+ ret = bmp180_read_temp_adc(data, &adc_temp);
+ if (ret)
+ return ret;
+
+ *comp_temp = bmp180_compensate_temp(data, adc_temp);
+
+ return 0;
+}
+
+static int bmp180_read_press_adc(struct bmp280_data *data, u32 *adc_press)
+{
+ u8 oss = data->oversampling_press;
+ int ret;
+
+ ret = bmp180_wait_for_eoc(data,
+ FIELD_PREP(BMP180_MEAS_CTRL_MASK, BMP180_MEAS_PRESS) |
+ FIELD_PREP(BMP180_OSRS_PRESS_MASK, oss) |
+ BMP180_MEAS_SCO);
+ if (ret)
+ return ret;
+
+ ret = regmap_bulk_read(data->regmap, BMP180_REG_OUT_MSB,
+ data->buf, BMP280_NUM_PRESS_BYTES);
+ if (ret) {
+ dev_err(data->dev, "failed to read pressure\n");
+ return ret;
+ }
+
+ *adc_press = get_unaligned_be24(data->buf) >> (8 - oss);
+
+ return 0;
+}
+
+/*
+ * Returns pressure in Pa, resolution is 1 Pa.
+ *
+ * Taken from datasheet, Section 3.5, "Calculating pressure and temperature".
+ */
+static u32 bmp180_compensate_press(struct bmp280_data *data, u32 adc_press,
+ s32 t_fine)
+{
+ struct bmp180_calib *calib = &data->calib.bmp180;
+ s32 oss = data->oversampling_press;
+ s32 x1, x2, x3, p;
+ s32 b3, b6;
+ u32 b4, b7;
+
+ b6 = t_fine - 4000;
+ x1 = (calib->B2 * (b6 * b6 >> 12)) >> 11;
+ x2 = calib->AC2 * b6 >> 11;
+ x3 = x1 + x2;
+ b3 = ((((s32)calib->AC1 * 4 + x3) << oss) + 2) / 4;
+ x1 = calib->AC3 * b6 >> 13;
+ x2 = (calib->B1 * ((b6 * b6) >> 12)) >> 16;
+ x3 = (x1 + x2 + 2) >> 2;
+ b4 = calib->AC4 * (u32)(x3 + 32768) >> 15;
+ b7 = (adc_press - b3) * (50000 >> oss);
+ if (b7 < 0x80000000)
+ p = (b7 * 2) / b4;
+ else
+ p = (b7 / b4) * 2;
+
+ x1 = (p >> 8) * (p >> 8);
+ x1 = (x1 * 3038) >> 16;
+ x2 = (-7357 * p) >> 16;
+
+ return p + ((x1 + x2 + 3791) >> 4);
+}
+
+static int bmp180_read_press(struct bmp280_data *data, u32 *comp_press)
+{
+ u32 adc_press;
+ s32 t_fine;
+ int ret;
+
+ ret = bmp180_get_t_fine(data, &t_fine);
+ if (ret)
+ return ret;
+
+ ret = bmp180_read_press_adc(data, &adc_press);
+ if (ret)
+ return ret;
+
+ *comp_press = bmp180_compensate_press(data, adc_press, t_fine);
+
+ return 0;
+}
+
+/* Keep compatibility with newer generations of the sensor */
+static int bmp180_set_mode(struct bmp280_data *data, enum bmp280_op_mode mode)
+{
+ return 0;
+}
+
+/* Keep compatibility with newer generations of the sensor */
+static int bmp180_wait_conv(struct bmp280_data *data)
+{
+ return 0;
+}
+
+/* Keep compatibility with newer generations of the sensor */
+static int bmp180_chip_config(struct bmp280_data *data)
+{
+ return 0;
+}
+
+static irqreturn_t bmp180_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct bmp280_data *data = iio_priv(indio_dev);
+ struct {
+ u32 comp_press;
+ s32 comp_temp;
+ aligned_s64 timestamp;
+ } buffer;
+ int ret;
+
+ guard(mutex)(&data->lock);
+
+ ret = bmp180_read_temp(data, &buffer.comp_temp);
+ if (ret)
+ goto out;
+
+
+ ret = bmp180_read_press(data, &buffer.comp_press);
+ if (ret)
+ goto out;
+
+ iio_push_to_buffers_with_ts(indio_dev, &buffer, sizeof(buffer),
+ iio_get_time_ns(indio_dev));
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static const int bmp180_oversampling_temp_avail[] = { 1 };
+static const int bmp180_oversampling_press_avail[] = { 1, 2, 4, 8 };
+static const u8 bmp180_chip_ids[] = { BMP180_CHIP_ID };
+static const int bmp180_temp_coeffs[] = { 100, 1 };
+static const int bmp180_press_coeffs[] = { 1, 1000 };
+
+const struct bmp280_chip_info bmp180_chip_info = {
+ .id_reg = BMP280_REG_ID,
+ .chip_id = bmp180_chip_ids,
+ .num_chip_id = ARRAY_SIZE(bmp180_chip_ids),
+ .regmap_config = &bmp180_regmap_config,
+ .start_up_time_us = 2000,
+ .channels = bmp280_channels,
+ .num_channels = ARRAY_SIZE(bmp280_channels),
+ .avail_scan_masks = bmp280_avail_scan_masks,
+
+ .oversampling_temp_avail = bmp180_oversampling_temp_avail,
+ .num_oversampling_temp_avail =
+ ARRAY_SIZE(bmp180_oversampling_temp_avail),
+ .oversampling_temp_default = 0,
+
+ .oversampling_press_avail = bmp180_oversampling_press_avail,
+ .num_oversampling_press_avail =
+ ARRAY_SIZE(bmp180_oversampling_press_avail),
+ .oversampling_press_default = BMP180_MEAS_PRESS_8X,
+
+ .temp_coeffs = bmp180_temp_coeffs,
+ .temp_coeffs_type = IIO_VAL_FRACTIONAL,
+ .press_coeffs = bmp180_press_coeffs,
+ .press_coeffs_type = IIO_VAL_FRACTIONAL,
+
+ .chip_config = bmp180_chip_config,
+ .read_temp = bmp180_read_temp,
+ .read_press = bmp180_read_press,
+ .read_calib = bmp180_read_calib,
+ .set_mode = bmp180_set_mode,
+ .wait_conv = bmp180_wait_conv,
+
+ .trigger_handler = bmp180_trigger_handler,
+};
+EXPORT_SYMBOL_NS(bmp180_chip_info, "IIO_BMP280");
+
+static irqreturn_t bmp085_eoc_irq(int irq, void *d)
+{
+ struct bmp280_data *data = d;
+
+ complete(&data->done);
+
+ return IRQ_HANDLED;
+}
+
+static int bmp085_trigger_probe(struct iio_dev *indio_dev)
+{
+ struct bmp280_data *data = iio_priv(indio_dev);
+ struct device *dev = data->dev;
+ unsigned long irq_trig;
+ int ret, irq;
+
+ irq = fwnode_irq_get(dev_fwnode(dev), 0);
+ if (irq < 0)
+ return dev_err_probe(dev, irq, "No interrupt found.\n");
+
+ irq_trig = irq_get_trigger_type(irq);
+ if (irq_trig != IRQF_TRIGGER_RISING) {
+ dev_err(dev, "non-rising trigger given for EOC interrupt, trying to enforce it\n");
+ irq_trig = IRQF_TRIGGER_RISING;
+ }
+
+ init_completion(&data->done);
+
+ ret = devm_request_irq(dev, irq, bmp085_eoc_irq, irq_trig,
+ indio_dev->name, data);
+ if (ret) {
+ /* Bail out without IRQ but keep the driver in place */
+ dev_err(dev, "unable to request DRDY IRQ\n");
+ return 0;
+ }
+
+ data->use_eoc = true;
+
+ return 0;
+}
+
+/* Identical to bmp180_chip_info + bmp085_trigger_probe */
+const struct bmp280_chip_info bmp085_chip_info = {
+ .id_reg = BMP280_REG_ID,
+ .chip_id = bmp180_chip_ids,
+ .num_chip_id = ARRAY_SIZE(bmp180_chip_ids),
+ .regmap_config = &bmp180_regmap_config,
+ .start_up_time_us = 2000,
+ .channels = bmp280_channels,
+ .num_channels = ARRAY_SIZE(bmp280_channels),
+ .avail_scan_masks = bmp280_avail_scan_masks,
+
+ .oversampling_temp_avail = bmp180_oversampling_temp_avail,
+ .num_oversampling_temp_avail =
+ ARRAY_SIZE(bmp180_oversampling_temp_avail),
+ .oversampling_temp_default = 0,
+
+ .oversampling_press_avail = bmp180_oversampling_press_avail,
+ .num_oversampling_press_avail =
+ ARRAY_SIZE(bmp180_oversampling_press_avail),
+ .oversampling_press_default = BMP180_MEAS_PRESS_8X,
+
+ .temp_coeffs = bmp180_temp_coeffs,
+ .temp_coeffs_type = IIO_VAL_FRACTIONAL,
+ .press_coeffs = bmp180_press_coeffs,
+ .press_coeffs_type = IIO_VAL_FRACTIONAL,
+
+ .chip_config = bmp180_chip_config,
+ .read_temp = bmp180_read_temp,
+ .read_press = bmp180_read_press,
+ .read_calib = bmp180_read_calib,
+ .set_mode = bmp180_set_mode,
+ .wait_conv = bmp180_wait_conv,
+
+ .trigger_probe = bmp085_trigger_probe,
+ .trigger_handler = bmp180_trigger_handler,
+};
+EXPORT_SYMBOL_NS(bmp085_chip_info, "IIO_BMP280");
+
+static int bmp280_buffer_preenable(struct iio_dev *indio_dev)
+{
+ struct bmp280_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = pm_runtime_resume_and_get(data->dev);
+ if (ret < 0)
+ return ret;
+
+ ret = data->chip_info->set_mode(data, BMP280_NORMAL);
+ if (ret)
+ pm_runtime_put_autosuspend(data->dev);
+
+ return ret;
+}
+
+static int bmp280_buffer_postdisable(struct iio_dev *indio_dev)
+{
+ struct bmp280_data *data = iio_priv(indio_dev);
+
+ pm_runtime_put_autosuspend(data->dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops bmp280_buffer_setup_ops = {
+ .preenable = bmp280_buffer_preenable,
+ .postdisable = bmp280_buffer_postdisable,
+};
+
+static void bmp280_pm_disable(void *data)
+{
+ struct device *dev = data;
+
+ pm_runtime_get_sync(dev);
+ pm_runtime_put_noidle(dev);
+ pm_runtime_disable(dev);
+}
+
+static void bmp280_regulators_disable(void *data)
+{
+ struct regulator_bulk_data *supplies = data;
+
+ regulator_bulk_disable(BMP280_NUM_SUPPLIES, supplies);
+}
+
+int bmp280_common_probe(struct device *dev,
+ struct regmap *regmap,
+ const struct bmp280_chip_info *chip_info,
+ const char *name,
+ int irq)
+{
+ struct iio_dev *indio_dev;
+ struct bmp280_data *data;
+ struct gpio_desc *gpiod;
+ unsigned int chip_id;
+ unsigned int i;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ mutex_init(&data->lock);
+ data->dev = dev;
+
+ indio_dev->name = name;
+ indio_dev->info = &bmp280_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ data->chip_info = chip_info;
+
+ /* Apply initial values from chip info structure */
+ indio_dev->channels = chip_info->channels;
+ indio_dev->num_channels = chip_info->num_channels;
+ indio_dev->available_scan_masks = chip_info->avail_scan_masks;
+ data->oversampling_press = chip_info->oversampling_press_default;
+ data->oversampling_humid = chip_info->oversampling_humid_default;
+ data->oversampling_temp = chip_info->oversampling_temp_default;
+ data->iir_filter_coeff = chip_info->iir_filter_coeff_default;
+ data->sampling_freq = chip_info->sampling_freq_default;
+ data->start_up_time_us = chip_info->start_up_time_us;
+
+ /* Bring up regulators */
+ regulator_bulk_set_supply_names(data->supplies,
+ bmp280_supply_names,
+ BMP280_NUM_SUPPLIES);
+
+ ret = devm_regulator_bulk_get(dev,
+ BMP280_NUM_SUPPLIES, data->supplies);
+ if (ret) {
+ dev_err(dev, "failed to get regulators\n");
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(BMP280_NUM_SUPPLIES, data->supplies);
+ if (ret) {
+ dev_err(dev, "failed to enable regulators\n");
+ return ret;
+ }
+
+ ret = devm_add_action_or_reset(dev, bmp280_regulators_disable,
+ data->supplies);
+ if (ret)
+ return ret;
+
+ /* Wait to make sure we started up properly */
+ fsleep(data->start_up_time_us);
+
+ /* Bring chip out of reset if there is an assigned GPIO line */
+ gpiod = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
+ if (IS_ERR(gpiod))
+ return dev_err_probe(dev, PTR_ERR(gpiod), "failed to get reset GPIO\n");
+
+ /* Deassert the signal */
+ gpiod_set_value_cansleep(gpiod, 0);
+
+ data->regmap = regmap;
+
+ ret = regmap_read(regmap, data->chip_info->id_reg, &chip_id);
+ if (ret) {
+ dev_err(data->dev, "failed to read chip id\n");
+ return ret;
+ }
+
+ for (i = 0; i < data->chip_info->num_chip_id; i++) {
+ if (chip_id == data->chip_info->chip_id[i]) {
+ dev_info(dev, "0x%x is a known chip id for %s\n", chip_id, name);
+ break;
+ }
+ }
+
+ if (i == data->chip_info->num_chip_id)
+ dev_warn(dev, "bad chip id: 0x%x is not a known chip id\n", chip_id);
+
+ if (data->chip_info->preinit) {
+ ret = data->chip_info->preinit(data);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "error running preinit tasks\n");
+ }
+
+ ret = data->chip_info->chip_config(data);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(dev, indio_dev);
+
+ /*
+ * Some chips have calibration parameters "programmed into the devices'
+ * non-volatile memory during production". Let's read them out at probe
+ * time once. They will not change.
+ */
+
+ if (data->chip_info->read_calib) {
+ ret = data->chip_info->read_calib(data);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "failed to read calibration coefficients\n");
+ }
+
+ ret = devm_iio_triggered_buffer_setup(data->dev, indio_dev,
+ iio_pollfunc_store_time,
+ data->chip_info->trigger_handler,
+ &bmp280_buffer_setup_ops);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "iio triggered buffer setup failed\n");
+
+ /*
+ * Attempt to grab an optional EOC IRQ - only the BMP085 has this
+ * however as it happens, the BMP085 shares the chip ID of BMP180
+ * so we look for an IRQ if we have that.
+ */
+ if (irq > 0) {
+ if (data->chip_info->trigger_probe)
+ ret = data->chip_info->trigger_probe(indio_dev);
+ if (ret)
+ return ret;
+ }
+
+ ret = data->chip_info->set_mode(data, BMP280_SLEEP);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to set sleep mode\n");
+
+ /* Enable runtime PM */
+ pm_runtime_get_noresume(dev);
+ pm_runtime_set_active(dev);
+ pm_runtime_enable(dev);
+ /*
+ * Set autosuspend to two orders of magnitude larger than the
+ * start-up time.
+ */
+ pm_runtime_set_autosuspend_delay(dev, data->start_up_time_us / 10);
+ pm_runtime_use_autosuspend(dev);
+ pm_runtime_put(dev);
+
+ ret = devm_add_action_or_reset(dev, bmp280_pm_disable, dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS(bmp280_common_probe, "IIO_BMP280");
+
+static int bmp280_runtime_suspend(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct bmp280_data *data = iio_priv(indio_dev);
+
+ data->chip_info->set_mode(data, BMP280_SLEEP);
+
+ fsleep(data->start_up_time_us);
+ return regulator_bulk_disable(BMP280_NUM_SUPPLIES, data->supplies);
+}
+
+static int bmp280_runtime_resume(struct device *dev)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(dev);
+ struct bmp280_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = regulator_bulk_enable(BMP280_NUM_SUPPLIES, data->supplies);
+ if (ret)
+ return ret;
+
+ fsleep(data->start_up_time_us);
+
+ ret = data->chip_info->chip_config(data);
+ if (ret)
+ return ret;
+
+ return data->chip_info->set_mode(data, data->op_mode);
+}
+
+EXPORT_RUNTIME_DEV_PM_OPS(bmp280_dev_pm_ops, bmp280_runtime_suspend,
+ bmp280_runtime_resume, NULL);
+
+MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
+MODULE_DESCRIPTION("Driver for Bosch Sensortec BMP180/BMP280 pressure and temperature sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/bmp280-i2c.c b/drivers/iio/pressure/bmp280-i2c.c
new file mode 100644
index 000000000..3f6e0723a
--- /dev/null
+++ b/drivers/iio/pressure/bmp280-i2c.c
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "bmp280.h"
+
+static int bmp280_i2c_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ const struct bmp280_chip_info *chip_info;
+ struct regmap *regmap;
+
+ chip_info = i2c_get_match_data(client);
+
+ regmap = devm_regmap_init_i2c(client, chip_info->regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "failed to allocate register map\n");
+ return PTR_ERR(regmap);
+ }
+
+ return bmp280_common_probe(&client->dev,
+ regmap,
+ chip_info,
+ id->name,
+ client->irq);
+}
+
+static const struct of_device_id bmp280_of_i2c_match[] = {
+ { .compatible = "bosch,bmp085", .data = &bmp085_chip_info },
+ { .compatible = "bosch,bmp180", .data = &bmp180_chip_info },
+ { .compatible = "bosch,bmp280", .data = &bmp280_chip_info },
+ { .compatible = "bosch,bme280", .data = &bme280_chip_info },
+ { .compatible = "bosch,bmp380", .data = &bmp380_chip_info },
+ { .compatible = "bosch,bmp580", .data = &bmp580_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bmp280_of_i2c_match);
+
+static const struct i2c_device_id bmp280_i2c_id[] = {
+ { .name = "bmp085", .driver_data = (kernel_ulong_t)&bmp085_chip_info },
+ { .name = "bmp180", .driver_data = (kernel_ulong_t)&bmp180_chip_info },
+ { .name = "bmp280", .driver_data = (kernel_ulong_t)&bmp280_chip_info },
+ { .name = "bme280", .driver_data = (kernel_ulong_t)&bme280_chip_info },
+ { .name = "bmp380", .driver_data = (kernel_ulong_t)&bmp380_chip_info },
+ { .name = "bmp580", .driver_data = (kernel_ulong_t)&bmp580_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bmp280_i2c_id);
+
+static struct i2c_driver bmp280_i2c_driver = {
+ .driver = {
+ .name = "bmp280",
+ .of_match_table = bmp280_of_i2c_match,
+ .pm = pm_ptr(&bmp280_dev_pm_ops),
+ },
+ .probe = bmp280_i2c_probe,
+ .id_table = bmp280_i2c_id,
+};
+module_i2c_driver(bmp280_i2c_driver);
+
+MODULE_AUTHOR("Vlad Dogaru <vlad.dogaru@intel.com>");
+MODULE_DESCRIPTION("Driver for Bosch Sensortec BMP180/BMP280 pressure and temperature sensor");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_BMP280");
diff --git a/drivers/iio/pressure/bmp280-regmap.c b/drivers/iio/pressure/bmp280-regmap.c
new file mode 100644
index 000000000..b6a7b417c
--- /dev/null
+++ b/drivers/iio/pressure/bmp280-regmap.c
@@ -0,0 +1,239 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#include "bmp280.h"
+
+static bool bmp180_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP280_REG_CTRL_MEAS:
+ case BMP280_REG_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bmp180_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP180_REG_OUT_XLSB:
+ case BMP180_REG_OUT_LSB:
+ case BMP180_REG_OUT_MSB:
+ case BMP280_REG_CTRL_MEAS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config bmp180_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BMP180_REG_OUT_XLSB,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = bmp180_is_writeable_reg,
+ .volatile_reg = bmp180_is_volatile_reg,
+};
+EXPORT_SYMBOL_NS(bmp180_regmap_config, "IIO_BMP280");
+
+static bool bme280_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP280_REG_CONFIG:
+ case BME280_REG_CTRL_HUMIDITY:
+ case BMP280_REG_CTRL_MEAS:
+ case BMP280_REG_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bmp280_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP280_REG_CONFIG:
+ case BMP280_REG_CTRL_MEAS:
+ case BMP280_REG_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bmp280_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP280_REG_TEMP_XLSB:
+ case BMP280_REG_TEMP_LSB:
+ case BMP280_REG_TEMP_MSB:
+ case BMP280_REG_PRESS_XLSB:
+ case BMP280_REG_PRESS_LSB:
+ case BMP280_REG_PRESS_MSB:
+ case BMP280_REG_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bme280_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BME280_REG_HUMIDITY_LSB:
+ case BME280_REG_HUMIDITY_MSB:
+ case BMP280_REG_TEMP_XLSB:
+ case BMP280_REG_TEMP_LSB:
+ case BMP280_REG_TEMP_MSB:
+ case BMP280_REG_PRESS_XLSB:
+ case BMP280_REG_PRESS_LSB:
+ case BMP280_REG_PRESS_MSB:
+ case BMP280_REG_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+static bool bmp380_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP380_REG_CMD:
+ case BMP380_REG_CONFIG:
+ case BMP380_REG_FIFO_CONFIG_1:
+ case BMP380_REG_FIFO_CONFIG_2:
+ case BMP380_REG_FIFO_WATERMARK_LSB:
+ case BMP380_REG_FIFO_WATERMARK_MSB:
+ case BMP380_REG_POWER_CONTROL:
+ case BMP380_REG_INT_CONTROL:
+ case BMP380_REG_IF_CONFIG:
+ case BMP380_REG_ODR:
+ case BMP380_REG_OSR:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bmp380_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP380_REG_TEMP_XLSB:
+ case BMP380_REG_TEMP_LSB:
+ case BMP380_REG_TEMP_MSB:
+ case BMP380_REG_PRESS_XLSB:
+ case BMP380_REG_PRESS_LSB:
+ case BMP380_REG_PRESS_MSB:
+ case BMP380_REG_SENSOR_TIME_XLSB:
+ case BMP380_REG_SENSOR_TIME_LSB:
+ case BMP380_REG_SENSOR_TIME_MSB:
+ case BMP380_REG_INT_STATUS:
+ case BMP380_REG_FIFO_DATA:
+ case BMP380_REG_STATUS:
+ case BMP380_REG_ERROR:
+ case BMP380_REG_EVENT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bmp580_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP580_REG_NVM_DATA_MSB:
+ case BMP580_REG_NVM_DATA_LSB:
+ case BMP580_REG_NVM_ADDR:
+ case BMP580_REG_ODR_CONFIG:
+ case BMP580_REG_OSR_CONFIG:
+ case BMP580_REG_INT_SOURCE:
+ case BMP580_REG_INT_CONFIG:
+ case BMP580_REG_OOR_THR_MSB:
+ case BMP580_REG_OOR_THR_LSB:
+ case BMP580_REG_OOR_CONFIG:
+ case BMP580_REG_OOR_RANGE:
+ case BMP580_REG_IF_CONFIG:
+ case BMP580_REG_FIFO_CONFIG:
+ case BMP580_REG_FIFO_SEL:
+ case BMP580_REG_DSP_CONFIG:
+ case BMP580_REG_DSP_IIR:
+ case BMP580_REG_CMD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool bmp580_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case BMP580_REG_NVM_DATA_MSB:
+ case BMP580_REG_NVM_DATA_LSB:
+ case BMP580_REG_FIFO_COUNT:
+ case BMP580_REG_INT_STATUS:
+ case BMP580_REG_PRESS_XLSB:
+ case BMP580_REG_PRESS_LSB:
+ case BMP580_REG_PRESS_MSB:
+ case BMP580_REG_FIFO_DATA:
+ case BMP580_REG_TEMP_XLSB:
+ case BMP580_REG_TEMP_LSB:
+ case BMP580_REG_TEMP_MSB:
+ case BMP580_REG_EFF_OSR:
+ case BMP580_REG_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+const struct regmap_config bmp280_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BMP280_REG_TEMP_XLSB,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = bmp280_is_writeable_reg,
+ .volatile_reg = bmp280_is_volatile_reg,
+};
+EXPORT_SYMBOL_NS(bmp280_regmap_config, "IIO_BMP280");
+
+const struct regmap_config bme280_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BME280_REG_HUMIDITY_LSB,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = bme280_is_writeable_reg,
+ .volatile_reg = bme280_is_volatile_reg,
+};
+EXPORT_SYMBOL_NS(bme280_regmap_config, "IIO_BMP280");
+
+const struct regmap_config bmp380_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BMP380_REG_CMD,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = bmp380_is_writeable_reg,
+ .volatile_reg = bmp380_is_volatile_reg,
+};
+EXPORT_SYMBOL_NS(bmp380_regmap_config, "IIO_BMP280");
+
+const struct regmap_config bmp580_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = BMP580_REG_CMD,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = bmp580_is_writeable_reg,
+ .volatile_reg = bmp580_is_volatile_reg,
+};
+EXPORT_SYMBOL_NS(bmp580_regmap_config, "IIO_BMP280");
diff --git a/drivers/iio/pressure/bmp280-spi.c b/drivers/iio/pressure/bmp280-spi.c
new file mode 100644
index 000000000..a02a621b8
--- /dev/null
+++ b/drivers/iio/pressure/bmp280-spi.c
@@ -0,0 +1,145 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * SPI interface for the BMP280 driver
+ *
+ * Inspired by the older BMP085 driver drivers/misc/bmp085-spi.c
+ */
+#include <linux/bits.h>
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+
+#include "bmp280.h"
+
+static int bmp280_regmap_spi_write(void *context, const void *data,
+ size_t count)
+{
+ struct spi_device *spi = to_spi_device(context);
+ u8 buf[2];
+
+ memcpy(buf, data, 2);
+ /*
+ * 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);
+}
+
+static int bmp280_regmap_spi_read(void *context, const void *reg,
+ size_t reg_size, void *val, size_t val_size)
+{
+ struct spi_device *spi = to_spi_device(context);
+
+ return spi_write_then_read(spi, reg, reg_size, val, val_size);
+}
+
+static int bmp380_regmap_spi_read(void *context, const void *reg,
+ size_t reg_size, void *val, size_t val_size)
+{
+ struct spi_device *spi = to_spi_device(context);
+ u8 rx_buf[BME280_BURST_READ_BYTES + 1];
+ ssize_t status;
+
+ if (val_size > BME280_BURST_READ_BYTES)
+ return -EINVAL;
+
+ /*
+ * According to the BMP3xx datasheets, for a basic SPI read operation,
+ * the first byte needs to be dropped and the rest are the requested
+ * data.
+ */
+ status = spi_write_then_read(spi, reg, 1, rx_buf, val_size + 1);
+ if (status)
+ return status;
+
+ memcpy(val, rx_buf + 1, val_size);
+
+ return 0;
+}
+
+static const struct regmap_bus bmp280_regmap_bus = {
+ .write = bmp280_regmap_spi_write,
+ .read = bmp280_regmap_spi_read,
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static const struct regmap_bus bmp380_regmap_bus = {
+ .write = bmp280_regmap_spi_write,
+ .read = bmp380_regmap_spi_read,
+ .read_flag_mask = BIT(7),
+ .reg_format_endian_default = REGMAP_ENDIAN_BIG,
+ .val_format_endian_default = REGMAP_ENDIAN_BIG,
+};
+
+static int bmp280_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+ const struct bmp280_chip_info *chip_info;
+ struct regmap_bus const *bmp_regmap_bus;
+ struct regmap *regmap;
+
+ chip_info = spi_get_device_match_data(spi);
+
+ if (chip_info->spi_read_extra_byte)
+ bmp_regmap_bus = &bmp380_regmap_bus;
+ else
+ bmp_regmap_bus = &bmp280_regmap_bus;
+
+ regmap = devm_regmap_init(&spi->dev,
+ bmp_regmap_bus,
+ &spi->dev,
+ chip_info->regmap_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "failed to allocate register map\n");
+ return PTR_ERR(regmap);
+ }
+
+ return bmp280_common_probe(&spi->dev,
+ regmap,
+ chip_info,
+ id->name,
+ spi->irq);
+}
+
+static const struct of_device_id bmp280_of_spi_match[] = {
+ { .compatible = "bosch,bmp085", .data = &bmp085_chip_info },
+ { .compatible = "bosch,bmp180", .data = &bmp180_chip_info },
+ { .compatible = "bosch,bmp181", .data = &bmp180_chip_info },
+ { .compatible = "bosch,bmp280", .data = &bmp280_chip_info },
+ { .compatible = "bosch,bme280", .data = &bme280_chip_info },
+ { .compatible = "bosch,bmp380", .data = &bmp380_chip_info },
+ { .compatible = "bosch,bmp580", .data = &bmp580_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bmp280_of_spi_match);
+
+static const struct spi_device_id bmp280_spi_id[] = {
+ { .name = "bmp085", .driver_data = (kernel_ulong_t)&bmp085_chip_info },
+ { .name = "bmp180", .driver_data = (kernel_ulong_t)&bmp180_chip_info },
+ { .name = "bmp181", .driver_data = (kernel_ulong_t)&bmp180_chip_info },
+ { .name = "bmp280", .driver_data = (kernel_ulong_t)&bmp280_chip_info },
+ { .name = "bme280", .driver_data = (kernel_ulong_t)&bme280_chip_info },
+ { .name = "bmp380", .driver_data = (kernel_ulong_t)&bmp380_chip_info },
+ { .name = "bmp580", .driver_data = (kernel_ulong_t)&bmp580_chip_info },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, bmp280_spi_id);
+
+static struct spi_driver bmp280_spi_driver = {
+ .driver = {
+ .name = "bmp280",
+ .of_match_table = bmp280_of_spi_match,
+ .pm = pm_ptr(&bmp280_dev_pm_ops),
+ },
+ .id_table = bmp280_spi_id,
+ .probe = bmp280_spi_probe,
+};
+module_spi_driver(bmp280_spi_driver);
+
+MODULE_DESCRIPTION("BMP280 SPI bus driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_BMP280");
diff --git a/drivers/iio/pressure/bmp280.h b/drivers/iio/pressure/bmp280.h
new file mode 100644
index 000000000..25bb9c743
--- /dev/null
+++ b/drivers/iio/pressure/bmp280.h
@@ -0,0 +1,551 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+
+/* BMP580 specific registers */
+#define BMP580_REG_CMD 0x7E
+#define BMP580_REG_EFF_OSR 0x38
+#define BMP580_REG_ODR_CONFIG 0x37
+#define BMP580_REG_OSR_CONFIG 0x36
+#define BMP580_REG_IF_CONFIG 0x13
+#define BMP580_REG_REV_ID 0x02
+#define BMP580_REG_CHIP_ID 0x01
+/* OOR allows to configure a pressure alarm */
+#define BMP580_REG_OOR_CONFIG 0x35
+#define BMP580_REG_OOR_RANGE 0x34
+#define BMP580_REG_OOR_THR_MSB 0x33
+#define BMP580_REG_OOR_THR_LSB 0x32
+/* DSP registers (IIR filters) */
+#define BMP580_REG_DSP_IIR 0x31
+#define BMP580_REG_DSP_CONFIG 0x30
+/* NVM access registers */
+#define BMP580_REG_NVM_DATA_MSB 0x2D
+#define BMP580_REG_NVM_DATA_LSB 0x2C
+#define BMP580_REG_NVM_ADDR 0x2B
+/* Status registers */
+#define BMP580_REG_STATUS 0x28
+#define BMP580_REG_INT_STATUS 0x27
+#define BMP580_REG_CHIP_STATUS 0x11
+/* Data registers */
+#define BMP580_REG_FIFO_DATA 0x29
+#define BMP580_REG_PRESS_MSB 0x22
+#define BMP580_REG_PRESS_LSB 0x21
+#define BMP580_REG_PRESS_XLSB 0x20
+#define BMP580_REG_TEMP_MSB 0x1F
+#define BMP580_REG_TEMP_LSB 0x1E
+#define BMP580_REG_TEMP_XLSB 0x1D
+/* FIFO config registers */
+#define BMP580_REG_FIFO_SEL 0x18
+#define BMP580_REG_FIFO_COUNT 0x17
+#define BMP580_REG_FIFO_CONFIG 0x16
+/* Interruptions config registers */
+#define BMP580_REG_INT_SOURCE 0x15
+#define BMP580_REG_INT_CONFIG 0x14
+
+#define BMP580_CMD_NOOP 0x00
+#define BMP580_CMD_EXTMODE_SEQ_0 0x73
+#define BMP580_CMD_EXTMODE_SEQ_1 0xB4
+#define BMP580_CMD_EXTMODE_SEQ_2 0x69
+#define BMP580_CMD_NVM_OP_SEQ_0 0x5D
+#define BMP580_CMD_NVM_READ_SEQ_1 0xA5
+#define BMP580_CMD_NVM_WRITE_SEQ_1 0xA0
+#define BMP580_CMD_SOFT_RESET 0xB6
+
+#define BMP580_INT_STATUS_DRDY_MASK BIT(0)
+#define BMP580_INT_STATUS_POR_MASK BIT(4)
+
+#define BMP580_INT_SOURCE_DRDY BIT(0)
+
+#define BMP580_INT_CONFIG_MASK GENMASK(3, 0)
+#define BMP580_INT_CONFIG_LATCH BIT(0)
+#define BMP580_INT_CONFIG_LEVEL BIT(1)
+#define BMP580_INT_CONFIG_OPEN_DRAIN BIT(2)
+#define BMP580_INT_CONFIG_INT_EN BIT(3)
+
+#define BMP580_STATUS_CORE_RDY_MASK BIT(0)
+#define BMP580_STATUS_NVM_RDY_MASK BIT(1)
+#define BMP580_STATUS_NVM_ERR_MASK BIT(2)
+#define BMP580_STATUS_NVM_CMD_ERR_MASK BIT(3)
+
+#define BMP580_OSR_PRESS_MASK GENMASK(5, 3)
+#define BMP580_OSR_TEMP_MASK GENMASK(2, 0)
+#define BMP580_OSR_PRESS_EN BIT(6)
+#define BMP580_EFF_OSR_PRESS_MASK GENMASK(5, 3)
+#define BMP580_EFF_OSR_TEMP_MASK GENMASK(2, 0)
+#define BMP580_EFF_OSR_VALID_ODR BIT(7)
+
+#define BMP580_ODR_MASK GENMASK(6, 2)
+#define BMP580_MODE_MASK GENMASK(1, 0)
+#define BMP580_MODE_SLEEP 0
+#define BMP580_MODE_NORMAL 1
+#define BMP580_MODE_FORCED 2
+#define BMP580_MODE_CONTINOUS 3
+#define BMP580_ODR_DEEPSLEEP_DIS BIT(7)
+
+#define BMP580_DSP_COMP_MASK GENMASK(1, 0)
+#define BMP580_DSP_COMP_DIS 0
+#define BMP580_DSP_TEMP_COMP_EN 1
+/*
+ * In section 7.27 of datasheet, modes 2 and 3 are technically the same.
+ * Pressure compensation means also enabling temperature compensation
+ */
+#define BMP580_DSP_PRESS_COMP_EN 2
+#define BMP580_DSP_PRESS_TEMP_COMP_EN 3
+#define BMP580_DSP_IIR_FORCED_FLUSH BIT(2)
+#define BMP580_DSP_SHDW_IIR_TEMP_EN BIT(3)
+#define BMP580_DSP_FIFO_IIR_TEMP_EN BIT(4)
+#define BMP580_DSP_SHDW_IIR_PRESS_EN BIT(5)
+#define BMP580_DSP_FIFO_IIR_PRESS_EN BIT(6)
+#define BMP580_DSP_OOR_IIR_PRESS_EN BIT(7)
+
+#define BMP580_DSP_IIR_PRESS_MASK GENMASK(5, 3)
+#define BMP580_DSP_IIR_TEMP_MASK GENMASK(2, 0)
+#define BMP580_FILTER_OFF 0
+#define BMP580_FILTER_1X 1
+#define BMP580_FILTER_3X 2
+#define BMP580_FILTER_7X 3
+#define BMP580_FILTER_15X 4
+#define BMP580_FILTER_31X 5
+#define BMP580_FILTER_63X 6
+#define BMP580_FILTER_127X 7
+
+#define BMP580_NVM_ROW_ADDR_MASK GENMASK(5, 0)
+#define BMP580_NVM_PROG_EN BIT(6)
+
+#define BMP580_TEMP_SKIPPED 0x7f7f7f
+#define BMP580_PRESS_SKIPPED 0x7f7f7f
+
+/* BMP380 specific registers */
+#define BMP380_REG_CMD 0x7E
+#define BMP380_REG_CONFIG 0x1F
+#define BMP380_REG_ODR 0x1D
+#define BMP380_REG_OSR 0x1C
+#define BMP380_REG_POWER_CONTROL 0x1B
+#define BMP380_REG_IF_CONFIG 0x1A
+#define BMP380_REG_INT_CONTROL 0x19
+#define BMP380_REG_INT_STATUS 0x11
+#define BMP380_REG_EVENT 0x10
+#define BMP380_REG_STATUS 0x03
+#define BMP380_REG_ERROR 0x02
+#define BMP380_REG_ID 0x00
+
+#define BMP380_REG_FIFO_CONFIG_1 0x18
+#define BMP380_REG_FIFO_CONFIG_2 0x17
+#define BMP380_REG_FIFO_WATERMARK_MSB 0x16
+#define BMP380_REG_FIFO_WATERMARK_LSB 0x15
+#define BMP380_REG_FIFO_DATA 0x14
+#define BMP380_REG_FIFO_LENGTH_MSB 0x13
+#define BMP380_REG_FIFO_LENGTH_LSB 0x12
+
+#define BMP380_REG_SENSOR_TIME_MSB 0x0E
+#define BMP380_REG_SENSOR_TIME_LSB 0x0D
+#define BMP380_REG_SENSOR_TIME_XLSB 0x0C
+
+#define BMP380_REG_TEMP_MSB 0x09
+#define BMP380_REG_TEMP_LSB 0x08
+#define BMP380_REG_TEMP_XLSB 0x07
+
+#define BMP380_REG_PRESS_MSB 0x06
+#define BMP380_REG_PRESS_LSB 0x05
+#define BMP380_REG_PRESS_XLSB 0x04
+
+#define BMP380_REG_CALIB_TEMP_START 0x31
+#define BMP380_CALIB_REG_COUNT 21
+
+#define BMP380_FILTER_MASK GENMASK(3, 1)
+#define BMP380_FILTER_OFF 0
+#define BMP380_FILTER_1X 1
+#define BMP380_FILTER_3X 2
+#define BMP380_FILTER_7X 3
+#define BMP380_FILTER_15X 4
+#define BMP380_FILTER_31X 5
+#define BMP380_FILTER_63X 6
+#define BMP380_FILTER_127X 7
+
+#define BMP380_OSRS_TEMP_MASK GENMASK(5, 3)
+#define BMP380_OSRS_PRESS_MASK GENMASK(2, 0)
+
+#define BMP380_ODRS_MASK GENMASK(4, 0)
+
+#define BMP380_CTRL_SENSORS_MASK GENMASK(1, 0)
+#define BMP380_CTRL_SENSORS_PRESS_EN BIT(0)
+#define BMP380_CTRL_SENSORS_TEMP_EN BIT(1)
+#define BMP380_MODE_MASK GENMASK(5, 4)
+#define BMP380_MODE_SLEEP 0
+#define BMP380_MODE_FORCED 1
+#define BMP380_MODE_NORMAL 3
+
+#define BMP380_MEAS_OFFSET 234
+#define BMP380_MEAS_DUR 2020
+#define BMP380_TEMP_MEAS_OFFSET 163
+#define BMP380_PRESS_MEAS_OFFSET 392
+
+#define BMP380_INT_STATUS_DRDY BIT(3)
+
+#define BMP380_INT_CTRL_SETTINGS_MASK GENMASK(2, 0)
+#define BMP380_INT_CTRL_OPEN_DRAIN BIT(0)
+#define BMP380_INT_CTRL_LEVEL BIT(1)
+#define BMP380_INT_CTRL_LATCH BIT(2)
+#define BMP380_INT_CTRL_DRDY_EN BIT(6)
+
+#define BMP380_MIN_TEMP -4000
+#define BMP380_MAX_TEMP 8500
+#define BMP380_MIN_PRES 3000000
+#define BMP380_MAX_PRES 12500000
+
+#define BMP380_CMD_NOOP 0x00
+#define BMP380_CMD_EXTMODE_EN_MID 0x34
+#define BMP380_CMD_FIFO_FLUSH 0xB0
+#define BMP380_CMD_SOFT_RESET 0xB6
+
+#define BMP380_STATUS_CMD_RDY_MASK BIT(4)
+#define BMP380_STATUS_DRDY_PRESS_MASK BIT(5)
+#define BMP380_STATUS_DRDY_TEMP_MASK BIT(6)
+
+#define BMP380_ERR_FATAL_MASK BIT(0)
+#define BMP380_ERR_CMD_MASK BIT(1)
+#define BMP380_ERR_CONF_MASK BIT(2)
+
+#define BMP380_TEMP_SKIPPED 0x800000
+#define BMP380_PRESS_SKIPPED 0x800000
+
+/* BMP280 specific registers */
+#define BMP280_REG_TEMP_XLSB 0xFC
+#define BMP280_REG_TEMP_LSB 0xFB
+#define BMP280_REG_TEMP_MSB 0xFA
+#define BMP280_REG_PRESS_XLSB 0xF9
+#define BMP280_REG_PRESS_LSB 0xF8
+#define BMP280_REG_PRESS_MSB 0xF7
+
+/* Helper mask to truncate excess 4 bits on pressure and temp readings */
+#define BMP280_MEAS_TRIM_MASK GENMASK(24, 4)
+
+#define BMP280_REG_CONFIG 0xF5
+#define BMP280_REG_CTRL_MEAS 0xF4
+#define BMP280_REG_STATUS 0xF3
+#define BMP280_REG_STATUS_IM_UPDATE BIT(0)
+#define BMP280_REG_STATUS_MEAS_BIT BIT(3)
+#define BMP280_REG_RESET 0xE0
+#define BMP280_RST_SOFT_CMD 0xB6
+
+#define BMP280_REG_COMP_TEMP_START 0x88
+#define BMP280_COMP_TEMP_REG_COUNT 6
+
+#define BMP280_REG_COMP_PRESS_START 0x8E
+#define BMP280_COMP_PRESS_REG_COUNT 18
+
+#define BMP280_CONTIGUOUS_CALIB_REGS (BMP280_COMP_TEMP_REG_COUNT + \
+ BMP280_COMP_PRESS_REG_COUNT)
+
+#define BMP280_FILTER_MASK GENMASK(4, 2)
+#define BMP280_FILTER_OFF 0
+#define BMP280_FILTER_2X 1
+#define BMP280_FILTER_4X 2
+#define BMP280_FILTER_8X 3
+#define BMP280_FILTER_16X 4
+
+#define BMP280_OSRS_TEMP_MASK GENMASK(7, 5)
+#define BMP280_OSRS_TEMP_SKIP 0
+#define BMP280_OSRS_TEMP_1X 1
+#define BMP280_OSRS_TEMP_2X 2
+#define BMP280_OSRS_TEMP_4X 3
+#define BMP280_OSRS_TEMP_8X 4
+#define BMP280_OSRS_TEMP_16X 5
+
+#define BMP280_OSRS_PRESS_MASK GENMASK(4, 2)
+#define BMP280_OSRS_PRESS_SKIP 0
+#define BMP280_OSRS_PRESS_1X 1
+#define BMP280_OSRS_PRESS_2X 2
+#define BMP280_OSRS_PRESS_4X 3
+#define BMP280_OSRS_PRESS_8X 4
+#define BMP280_OSRS_PRESS_16X 5
+
+#define BMP280_MODE_MASK GENMASK(1, 0)
+#define BMP280_MODE_SLEEP 0
+#define BMP280_MODE_FORCED 1
+#define BMP280_MODE_NORMAL 3
+
+#define BMP280_MEAS_OFFSET 1250
+#define BMP280_MEAS_DUR 2300
+#define BMP280_PRESS_HUMID_MEAS_OFFSET 575
+
+/* BME280 specific registers */
+#define BME280_REG_HUMIDITY_LSB 0xFE
+#define BME280_REG_HUMIDITY_MSB 0xFD
+
+#define BME280_REG_CTRL_HUMIDITY 0xF2
+
+/* Due to non linear mapping, and data sizes we can't do a bulk read */
+#define BME280_REG_COMP_H1 0xA1
+#define BME280_REG_COMP_H2 0xE1
+#define BME280_REG_COMP_H3 0xE3
+#define BME280_REG_COMP_H4 0xE4
+#define BME280_REG_COMP_H5 0xE5
+#define BME280_REG_COMP_H6 0xE7
+
+#define BME280_COMP_H4_GET_MASK_UP GENMASK(15, 8)
+#define BME280_COMP_H4_PREP_MASK_UP GENMASK(11, 4)
+#define BME280_COMP_H4_MASK_LOW GENMASK(3, 0)
+#define BME280_COMP_H5_MASK GENMASK(15, 4)
+
+#define BME280_CONTIGUOUS_CALIB_REGS 7
+
+#define BME280_OSRS_HUMIDITY_MASK GENMASK(2, 0)
+#define BME280_OSRS_HUMIDITY_SKIP 0
+#define BME280_OSRS_HUMIDITY_1X 1
+#define BME280_OSRS_HUMIDITY_2X 2
+#define BME280_OSRS_HUMIDITY_4X 3
+#define BME280_OSRS_HUMIDITY_8X 4
+#define BME280_OSRS_HUMIDITY_16X 5
+
+/* BMP180 specific registers */
+#define BMP180_REG_OUT_XLSB 0xF8
+#define BMP180_REG_OUT_LSB 0xF7
+#define BMP180_REG_OUT_MSB 0xF6
+
+#define BMP180_REG_CALIB_START 0xAA
+#define BMP180_REG_CALIB_COUNT 22
+
+#define BMP180_MEAS_CTRL_MASK GENMASK(4, 0)
+#define BMP180_MEAS_TEMP 0x0E
+#define BMP180_MEAS_PRESS 0x14
+#define BMP180_MEAS_SCO BIT(5)
+#define BMP180_OSRS_PRESS_MASK GENMASK(7, 6)
+#define BMP180_MEAS_PRESS_1X 0
+#define BMP180_MEAS_PRESS_2X 1
+#define BMP180_MEAS_PRESS_4X 2
+#define BMP180_MEAS_PRESS_8X 3
+
+/* BMP180 and BMP280 common registers */
+#define BMP280_REG_CTRL_MEAS 0xF4
+#define BMP280_REG_RESET 0xE0
+#define BMP280_REG_ID 0xD0
+
+#define BMP380_CHIP_ID 0x50
+#define BMP580_CHIP_ID 0x50
+#define BMP580_CHIP_ID_ALT 0x51
+#define BMP180_CHIP_ID 0x55
+#define BMP280_CHIP_ID 0x58
+#define BMP390_CHIP_ID 0x60
+#define BME280_CHIP_ID 0x60
+#define BMP280_SOFT_RESET_VAL 0xB6
+
+/* BMP280 register skipped special values */
+#define BMP280_TEMP_SKIPPED 0x80000
+#define BMP280_PRESS_SKIPPED 0x80000
+#define BMP280_HUMIDITY_SKIPPED 0x8000
+
+/* Number of bytes for each value */
+#define BMP280_NUM_PRESS_BYTES 3
+#define BMP280_NUM_TEMP_BYTES 3
+#define BME280_NUM_HUMIDITY_BYTES 2
+#define BMP280_BURST_READ_BYTES (BMP280_NUM_PRESS_BYTES + \
+ BMP280_NUM_TEMP_BYTES)
+#define BME280_BURST_READ_BYTES (BMP280_NUM_PRESS_BYTES + \
+ BMP280_NUM_TEMP_BYTES + \
+ BME280_NUM_HUMIDITY_BYTES)
+
+/* Core exported structs */
+
+static const char *const bmp280_supply_names[] = {
+ "vddd", "vdda"
+};
+
+#define BMP280_NUM_SUPPLIES ARRAY_SIZE(bmp280_supply_names)
+
+struct bmp180_calib {
+ s16 AC1;
+ s16 AC2;
+ s16 AC3;
+ u16 AC4;
+ u16 AC5;
+ u16 AC6;
+ s16 B1;
+ s16 B2;
+ s16 MB;
+ s16 MC;
+ s16 MD;
+};
+
+/* See datasheet Section 4.2.2. */
+struct bmp280_calib {
+ u16 T1;
+ s16 T2;
+ s16 T3;
+ u16 P1;
+ s16 P2;
+ s16 P3;
+ s16 P4;
+ s16 P5;
+ s16 P6;
+ s16 P7;
+ s16 P8;
+ s16 P9;
+ u8 H1;
+ s16 H2;
+ u8 H3;
+ s16 H4;
+ s16 H5;
+ s8 H6;
+};
+
+/* See datasheet Section 3.11.1. */
+struct bmp380_calib {
+ u16 T1;
+ u16 T2;
+ s8 T3;
+ s16 P1;
+ s16 P2;
+ s8 P3;
+ s8 P4;
+ u16 P5;
+ u16 P6;
+ s8 P7;
+ s8 P8;
+ s16 P9;
+ s8 P10;
+ s8 P11;
+};
+
+enum bmp280_op_mode {
+ BMP280_SLEEP,
+ BMP280_FORCED,
+ BMP280_NORMAL,
+};
+
+struct bmp280_data {
+ struct device *dev;
+ struct mutex lock;
+ struct regmap *regmap;
+ struct completion done;
+ bool use_eoc;
+ bool trig_open_drain;
+ bool trig_active_high;
+ struct iio_trigger *trig;
+ const struct bmp280_chip_info *chip_info;
+ union {
+ struct bmp180_calib bmp180;
+ struct bmp280_calib bmp280;
+ struct bmp380_calib bmp380;
+ } calib;
+ struct regulator_bulk_data supplies[BMP280_NUM_SUPPLIES];
+ unsigned int start_up_time_us;
+
+ /* log of base 2 of oversampling rate */
+ u8 oversampling_press;
+ u8 oversampling_temp;
+ u8 oversampling_humid;
+ u8 iir_filter_coeff;
+
+ /*
+ * BMP380 devices introduce sampling frequency configuration. See
+ * datasheet sections 3.3.3. and 4.3.19 for more details.
+ *
+ * BMx280 devices allowed indirect configuration of sampling frequency
+ * changing the t_standby duration between measurements, as detailed on
+ * section 3.6.3 of the datasheet.
+ */
+ int sampling_freq;
+
+ /* Value to hold the current operation mode of the device */
+ enum bmp280_op_mode op_mode;
+
+ /*
+ * DMA (thus cache coherency maintenance) may require the
+ * transfer buffers to live in their own cache lines.
+ */
+ union {
+ /* Sensor data buffer */
+ u8 buf[BME280_BURST_READ_BYTES];
+ /* Calibration data buffers */
+ __le16 bmp280_cal_buf[BMP280_CONTIGUOUS_CALIB_REGS / sizeof(__le16)];
+ __be16 bmp180_cal_buf[BMP180_REG_CALIB_COUNT / sizeof(__be16)];
+ u8 bme280_humid_cal_buf[BME280_CONTIGUOUS_CALIB_REGS];
+ u8 bmp380_cal_buf[BMP380_CALIB_REG_COUNT];
+ /* Miscellaneous, endianness-aware data buffers */
+ __le16 le16;
+ __be16 be16;
+ } __aligned(IIO_DMA_MINALIGN);
+};
+
+struct bmp280_chip_info {
+ unsigned int id_reg;
+ const u8 *chip_id;
+ int num_chip_id;
+
+ const struct regmap_config *regmap_config;
+ bool spi_read_extra_byte;
+
+ const struct iio_chan_spec *channels;
+ int num_channels;
+ unsigned int start_up_time_us;
+ const unsigned long *avail_scan_masks;
+
+ const int *oversampling_temp_avail;
+ int num_oversampling_temp_avail;
+ int oversampling_temp_default;
+
+ const int *oversampling_press_avail;
+ int num_oversampling_press_avail;
+ int oversampling_press_default;
+
+ const int *oversampling_humid_avail;
+ int num_oversampling_humid_avail;
+ int oversampling_humid_default;
+
+ const int *iir_filter_coeffs_avail;
+ int num_iir_filter_coeffs_avail;
+ int iir_filter_coeff_default;
+
+ const int (*sampling_freq_avail)[2];
+ int num_sampling_freq_avail;
+ int sampling_freq_default;
+
+ const int *temp_coeffs;
+ const int temp_coeffs_type;
+ const int *press_coeffs;
+ const int press_coeffs_type;
+ const int *humid_coeffs;
+ const int humid_coeffs_type;
+
+ int (*chip_config)(struct bmp280_data *data);
+ int (*read_temp)(struct bmp280_data *data, s32 *adc_temp);
+ int (*read_press)(struct bmp280_data *data, u32 *adc_press);
+ int (*read_humid)(struct bmp280_data *data, u32 *adc_humidity);
+ int (*read_calib)(struct bmp280_data *data);
+ int (*preinit)(struct bmp280_data *data);
+ int (*set_mode)(struct bmp280_data *data, enum bmp280_op_mode mode);
+ int (*wait_conv)(struct bmp280_data *data);
+
+ int (*trigger_probe)(struct iio_dev *indio_dev);
+ irqreturn_t (*trigger_handler)(int irq, void *p);
+};
+
+/* Chip infos for each variant */
+extern const struct bmp280_chip_info bmp085_chip_info;
+extern const struct bmp280_chip_info bmp180_chip_info;
+extern const struct bmp280_chip_info bmp280_chip_info;
+extern const struct bmp280_chip_info bme280_chip_info;
+extern const struct bmp280_chip_info bmp380_chip_info;
+extern const struct bmp280_chip_info bmp580_chip_info;
+
+/* Regmap configurations */
+extern const struct regmap_config bmp180_regmap_config;
+extern const struct regmap_config bmp280_regmap_config;
+extern const struct regmap_config bme280_regmap_config;
+extern const struct regmap_config bmp380_regmap_config;
+extern const struct regmap_config bmp580_regmap_config;
+
+/* Probe called from different transports */
+int bmp280_common_probe(struct device *dev,
+ struct regmap *regmap,
+ const struct bmp280_chip_info *chip_info,
+ const char *name,
+ int irq);
+
+/* PM ops */
+extern const struct dev_pm_ops bmp280_dev_pm_ops;
diff --git a/drivers/iio/pressure/cros_ec_baro.c b/drivers/iio/pressure/cros_ec_baro.c
new file mode 100644
index 000000000..6a567b607
--- /dev/null
+++ b/drivers/iio/pressure/cros_ec_baro.c
@@ -0,0 +1,203 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cros_ec_baro - Driver for barometer sensor behind CrosEC.
+ *
+ * Copyright (C) 2017 Google, Inc
+ */
+
+#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/slab.h>
+#include <linux/platform_data/cros_ec_commands.h>
+#include <linux/platform_data/cros_ec_proto.h>
+#include <linux/platform_device.h>
+
+/*
+ * One channel for pressure, the other for timestamp.
+ */
+#define CROS_EC_BARO_MAX_CHANNELS (1 + 1)
+
+/* State data for ec_sensors iio driver. */
+struct cros_ec_baro_state {
+ /* Shared by all sensors */
+ struct cros_ec_sensors_core_state core;
+
+ struct iio_chan_spec channels[CROS_EC_BARO_MAX_CHANNELS];
+};
+
+static int cros_ec_baro_read(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct cros_ec_baro_state *st = iio_priv(indio_dev);
+ u16 data = 0;
+ int ret;
+ int idx = chan->scan_index;
+
+ mutex_lock(&st->core.cmd_lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ 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;
+
+ 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 */
+ *val2 = 10 << CROS_EC_SENSOR_BITS;
+ ret = IIO_VAL_FRACTIONAL;
+ break;
+ default:
+ ret = cros_ec_sensors_core_read(&st->core, chan, val, val2,
+ mask);
+ break;
+ }
+
+ mutex_unlock(&st->core.cmd_lock);
+
+ return ret;
+}
+
+static int cros_ec_baro_write(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct cros_ec_baro_state *st = iio_priv(indio_dev);
+ int ret = 0;
+
+ mutex_lock(&st->core.cmd_lock);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ st->core.param.cmd = MOTIONSENSE_CMD_SENSOR_RANGE;
+ st->core.param.sensor_range.data = val;
+
+ /* Always roundup, so caller gets at least what it asks for. */
+ st->core.param.sensor_range.roundup = 1;
+
+ ret = cros_ec_motion_send_host_cmd(&st->core, 0);
+ if (ret == 0) {
+ st->core.range_updated = true;
+ st->core.curr_range = val;
+ }
+ break;
+ default:
+ ret = cros_ec_sensors_core_write(&st->core, chan, val, val2,
+ mask);
+ break;
+ }
+
+ mutex_unlock(&st->core.cmd_lock);
+
+ return ret;
+}
+
+static const struct iio_info cros_ec_baro_info = {
+ .read_raw = &cros_ec_baro_read,
+ .write_raw = &cros_ec_baro_write,
+ .read_avail = &cros_ec_sensors_core_read_avail,
+};
+
+static int cros_ec_baro_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct cros_ec_dev *ec_dev = dev_get_drvdata(dev->parent);
+ struct iio_dev *indio_dev;
+ struct cros_ec_baro_state *state;
+ struct iio_chan_spec *channel;
+ int ret;
+
+ if (!ec_dev || !ec_dev->ec_dev) {
+ dev_warn(dev, "No CROS EC device found.\n");
+ return -EINVAL;
+ }
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*state));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ ret = cros_ec_sensors_core_init(pdev, indio_dev, true,
+ cros_ec_sensors_capture);
+ if (ret)
+ return ret;
+
+ indio_dev->info = &cros_ec_baro_info;
+ state = iio_priv(indio_dev);
+ channel = state->channels;
+ /* Common part */
+ channel->info_mask_separate = BIT(IIO_CHAN_INFO_RAW);
+ channel->info_mask_shared_by_all =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ 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_type.shift = 0;
+ channel->scan_index = 0;
+ channel->ext_info = cros_ec_sensors_ext_info;
+ channel->scan_type.sign = 'u';
+
+ /* Sensor specific */
+ switch (state->core.type) {
+ case MOTIONSENSE_TYPE_BARO:
+ channel->type = IIO_PRESSURE;
+ break;
+ default:
+ dev_warn(dev, "Unknown motion sensor\n");
+ return -EINVAL;
+ }
+
+ channel++;
+ *channel = IIO_CHAN_SOFT_TIMESTAMP(1);
+
+ indio_dev->channels = state->channels;
+ indio_dev->num_channels = CROS_EC_BARO_MAX_CHANNELS;
+
+ state->core.read_ec_sensors_data = cros_ec_sensors_read_cmd;
+
+ return cros_ec_sensors_core_register(dev, indio_dev,
+ cros_ec_sensors_push_data);
+}
+
+static const struct platform_device_id cros_ec_baro_ids[] = {
+ {
+ .name = "cros-ec-baro",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, cros_ec_baro_ids);
+
+static struct platform_driver cros_ec_baro_platform_driver = {
+ .driver = {
+ .name = "cros-ec-baro",
+ .pm = &cros_ec_sensors_pm_ops,
+ },
+ .probe = cros_ec_baro_probe,
+ .id_table = cros_ec_baro_ids,
+};
+module_platform_driver(cros_ec_baro_platform_driver);
+
+MODULE_DESCRIPTION("ChromeOS EC barometer sensor driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/dlhl60d.c b/drivers/iio/pressure/dlhl60d.c
new file mode 100644
index 000000000..961888fd0
--- /dev/null
+++ b/drivers/iio/pressure/dlhl60d.c
@@ -0,0 +1,359 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * All Sensors DLH series low voltage digital pressure sensors
+ *
+ * Copyright (c) 2019 AVL DiTEST GmbH
+ * Tomislav Denis <tomislav.denis@avl.com>
+ *
+ * Datasheet: https://www.allsensors.com/cad/DS-0355_Rev_B.PDF
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/i2c.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/unaligned.h>
+
+/* Commands */
+#define DLH_START_SINGLE 0xAA
+
+/* Status bits */
+#define DLH_STATUS_OK 0x40
+
+/* DLH data format */
+#define DLH_NUM_READ_BYTES 7
+#define DLH_NUM_DATA_BYTES 3
+#define DLH_NUM_PR_BITS 24
+#define DLH_NUM_TEMP_BITS 24
+
+/* DLH timings */
+#define DLH_SINGLE_DUT_MS 5
+
+struct dlh_info {
+ const char *name; /* chip name */
+ u8 osdig; /* digital offset factor */
+ unsigned int fss; /* full scale span (inch H2O) */
+};
+
+struct dlh_state {
+ struct i2c_client *client;
+ const struct dlh_info *info;
+ bool use_interrupt;
+ struct completion completion;
+ u8 rx_buf[DLH_NUM_READ_BYTES];
+};
+
+static const struct dlh_info dlhl60d_info = {
+ .name = "dlhl60d",
+ .osdig = 2,
+ .fss = 120,
+};
+
+static const struct dlh_info dlhl60g_info = {
+ .name = "dlhl60g",
+ .osdig = 10,
+ .fss = 60,
+};
+
+static int dlh_cmd_start_single(struct dlh_state *st)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte(st->client, DLH_START_SINGLE);
+ if (ret)
+ dev_err(&st->client->dev,
+ "%s: I2C write byte failed\n", __func__);
+
+ return ret;
+}
+
+static int dlh_cmd_read_data(struct dlh_state *st)
+{
+ int ret;
+
+ ret = i2c_master_recv(st->client, st->rx_buf, DLH_NUM_READ_BYTES);
+ if (ret < 0) {
+ dev_err(&st->client->dev,
+ "%s: I2C read block failed\n", __func__);
+ return ret;
+ }
+
+ if (st->rx_buf[0] != DLH_STATUS_OK) {
+ dev_err(&st->client->dev,
+ "%s: invalid status 0x%02x\n", __func__, st->rx_buf[0]);
+ return -EBUSY;
+ }
+
+ return 0;
+}
+
+static int dlh_start_capture_and_read(struct dlh_state *st)
+{
+ int ret;
+
+ if (st->use_interrupt)
+ reinit_completion(&st->completion);
+
+ ret = dlh_cmd_start_single(st);
+ if (ret)
+ return ret;
+
+ if (st->use_interrupt) {
+ ret = wait_for_completion_timeout(&st->completion,
+ msecs_to_jiffies(DLH_SINGLE_DUT_MS));
+ if (!ret) {
+ dev_err(&st->client->dev,
+ "%s: conversion timed out\n", __func__);
+ return -ETIMEDOUT;
+ }
+ } else {
+ mdelay(DLH_SINGLE_DUT_MS);
+ }
+
+ return dlh_cmd_read_data(st);
+}
+
+static int dlh_read_direct(struct dlh_state *st,
+ unsigned int *pressure, unsigned int *temperature)
+{
+ int ret;
+
+ ret = dlh_start_capture_and_read(st);
+ if (ret)
+ return ret;
+
+ *pressure = get_unaligned_be24(&st->rx_buf[1]);
+ *temperature = get_unaligned_be24(&st->rx_buf[4]);
+
+ return 0;
+}
+
+static int dlh_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *value,
+ int *value2, long mask)
+{
+ struct dlh_state *st = iio_priv(indio_dev);
+ unsigned int pressure, temperature;
+ int ret;
+ s64 tmp;
+ s32 rem;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = dlh_read_direct(st, &pressure, &temperature);
+ iio_device_release_direct(indio_dev);
+ if (ret)
+ return ret;
+
+ switch (channel->type) {
+ case IIO_PRESSURE:
+ *value = pressure;
+ return IIO_VAL_INT;
+
+ case IIO_TEMP:
+ *value = temperature;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (channel->type) {
+ case IIO_PRESSURE:
+ tmp = div_s64(125LL * st->info->fss * 24909 * 100,
+ 1 << DLH_NUM_PR_BITS);
+ tmp = div_s64_rem(tmp, 1000000000LL, &rem);
+ *value = tmp;
+ *value2 = rem;
+ return IIO_VAL_INT_PLUS_NANO;
+
+ case IIO_TEMP:
+ *value = 125 * 1000;
+ *value2 = DLH_NUM_TEMP_BITS;
+ return IIO_VAL_FRACTIONAL_LOG2;
+
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ switch (channel->type) {
+ case IIO_PRESSURE:
+ *value = -125 * st->info->fss * 24909;
+ *value2 = 100 * st->info->osdig * 100000;
+ return IIO_VAL_FRACTIONAL;
+
+ case IIO_TEMP:
+ *value = -40 * 1000;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info dlh_info = {
+ .read_raw = dlh_read_raw,
+};
+
+static const struct iio_chan_spec dlh_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = DLH_NUM_PR_BITS,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+ }, {
+ .type = IIO_TEMP,
+ .indexed = 1,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = DLH_NUM_TEMP_BITS,
+ .storagebits = 32,
+ .shift = 8,
+ .endianness = IIO_BE,
+ },
+ }
+};
+
+static irqreturn_t dlh_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct dlh_state *st = iio_priv(indio_dev);
+ int ret;
+ unsigned int chn, i = 0;
+ __be32 tmp_buf[2] = { };
+
+ ret = dlh_start_capture_and_read(st);
+ if (ret)
+ goto out;
+
+ iio_for_each_active_channel(indio_dev, chn) {
+ memcpy(&tmp_buf[i++],
+ &st->rx_buf[1] + chn * DLH_NUM_DATA_BYTES,
+ DLH_NUM_DATA_BYTES);
+ }
+
+ iio_push_to_buffers(indio_dev, tmp_buf);
+
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t dlh_interrupt(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct dlh_state *st = iio_priv(indio_dev);
+
+ complete(&st->completion);
+
+ return IRQ_HANDLED;
+};
+
+static int dlh_probe(struct i2c_client *client)
+{
+ struct dlh_state *st;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE)) {
+ dev_err(&client->dev,
+ "adapter doesn't support required i2c functionality\n");
+ return -EOPNOTSUPP;
+ }
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, indio_dev);
+
+ st = iio_priv(indio_dev);
+ st->info = i2c_get_match_data(client);
+ st->client = client;
+ st->use_interrupt = false;
+
+ indio_dev->name = st->info->name;
+ indio_dev->info = &dlh_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = dlh_channels;
+ indio_dev->num_channels = ARRAY_SIZE(dlh_channels);
+
+ if (client->irq > 0) {
+ ret = devm_request_irq(&client->dev, client->irq, dlh_interrupt,
+ IRQF_TRIGGER_RISING | IRQF_NO_THREAD,
+ st->info->name, indio_dev);
+ if (ret)
+ return ret;
+
+ st->use_interrupt = true;
+ init_completion(&st->completion);
+ }
+
+ ret = devm_iio_triggered_buffer_setup(&client->dev, indio_dev,
+ NULL, &dlh_trigger_handler, NULL);
+ if (ret) {
+ dev_err(&client->dev, "failed to setup iio buffer\n");
+ return ret;
+ }
+
+ ret = devm_iio_device_register(&client->dev, indio_dev);
+ if (ret)
+ dev_err(&client->dev, "failed to register iio device\n");
+
+ return ret;
+}
+
+static const struct of_device_id dlh_of_match[] = {
+ { .compatible = "asc,dlhl60d", .data = &dlhl60d_info },
+ { .compatible = "asc,dlhl60g", .data = &dlhl60g_info },
+ { }
+};
+MODULE_DEVICE_TABLE(of, dlh_of_match);
+
+static const struct i2c_device_id dlh_id[] = {
+ { .name = "dlhl60d", .driver_data = (kernel_ulong_t)&dlhl60d_info },
+ { .name = "dlhl60g", .driver_data = (kernel_ulong_t)&dlhl60g_info },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, dlh_id);
+
+static struct i2c_driver dlh_driver = {
+ .driver = {
+ .name = "dlhl60d",
+ .of_match_table = dlh_of_match,
+ },
+ .probe = dlh_probe,
+ .id_table = dlh_id,
+};
+module_i2c_driver(dlh_driver);
+
+MODULE_AUTHOR("Tomislav Denis <tomislav.denis@avl.com>");
+MODULE_DESCRIPTION("Driver for All Sensors DLH series pressure sensors");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/dps310.c b/drivers/iio/pressure/dps310.c
new file mode 100644
index 000000000..45bdb8c76
--- /dev/null
+++ b/drivers/iio/pressure/dps310.c
@@ -0,0 +1,912 @@
+// 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 <= 30ms for usleep_range */
+#define DPS310_POLL_SLEEP_US(t) min(30000, (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;
+ bool timeout_recovery_failed;
+};
+
+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;
+}
+
+/*
+ * Some versions of the chip will read temperatures in the ~60C range when
+ * it's actually ~20C. This is the manufacturer recommended workaround
+ * to correct the issue. The registers used below are undocumented.
+ */
+static int dps310_temp_workaround(struct dps310_data *data)
+{
+ int rc;
+ int reg;
+
+ rc = regmap_read(data->regmap, 0x32, &reg);
+ if (rc < 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)
+ return rc;
+
+ rc = regmap_write(data->regmap, 0x0f, 0x96);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(data->regmap, 0x62, 0x02);
+ if (rc)
+ return rc;
+
+ rc = regmap_write(data->regmap, 0x0e, 0x00);
+ if (rc)
+ return rc;
+
+ return regmap_write(data->regmap, 0x0f, 0x00);
+}
+
+static int dps310_startup(struct dps310_data *data)
+{
+ int rc;
+ int ready;
+
+ /*
+ * Set up pressure sensor in single sample, one measurement per second
+ * mode
+ */
+ rc = regmap_write(data->regmap, DPS310_PRS_CFG, 0);
+ if (rc)
+ return rc;
+
+ /*
+ * Set up external (MEMS) temperature sensor in single sample, one
+ * measurement per second mode
+ */
+ rc = regmap_write(data->regmap, DPS310_TMP_CFG, DPS310_TMP_EXT);
+ if (rc)
+ return rc;
+
+ /* Temp and pressure shifts are disabled when PRC <= 8 */
+ rc = regmap_write_bits(data->regmap, DPS310_CFG_REG,
+ DPS310_PRS_SHIFT_EN | DPS310_TMP_SHIFT_EN, 0);
+ if (rc)
+ return rc;
+
+ /* MEAS_CFG doesn't update correctly unless first written with 0 */
+ rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
+ DPS310_MEAS_CTRL_BITS, 0);
+ if (rc)
+ return rc;
+
+ /* Turn on temperature and pressure measurement in the background */
+ rc = regmap_write_bits(data->regmap, DPS310_MEAS_CFG,
+ DPS310_MEAS_CTRL_BITS, DPS310_PRS_EN |
+ DPS310_TEMP_EN | DPS310_BACKGROUND);
+ if (rc)
+ return rc;
+
+ /*
+ * Calibration coefficients required for reporting temperature.
+ * They are available 40ms after the device has started
+ */
+ rc = regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready,
+ ready & DPS310_COEF_RDY, 10000, 40000);
+ if (rc)
+ return rc;
+
+ rc = dps310_get_coefs(data);
+ if (rc)
+ return rc;
+
+ return dps310_temp_workaround(data);
+}
+
+static int dps310_get_pres_precision(struct dps310_data *data, int *val)
+{
+ int reg_val, rc;
+
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &reg_val);
+ if (rc < 0)
+ return rc;
+
+ *val = BIT(reg_val & GENMASK(2, 0));
+
+ return 0;
+}
+
+static int dps310_get_temp_precision(struct dps310_data *data, int *val)
+{
+ int reg_val, rc;
+
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &reg_val);
+ if (rc < 0)
+ return rc;
+
+ /*
+ * Scale factor is bottom 4 bits of the register, but 1111 is
+ * reserved so just grab bottom three
+ */
+ *val = BIT(reg_val & GENMASK(2, 0));
+
+ return 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 *val)
+{
+ int reg_val, rc;
+
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &reg_val);
+ if (rc < 0)
+ return rc;
+
+ *val = BIT((reg_val & DPS310_PRS_RATE_BITS) >> 4);
+
+ return 0;
+}
+
+static int dps310_get_temp_samp_freq(struct dps310_data *data, int *val)
+{
+ int reg_val, rc;
+
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &reg_val);
+ if (rc < 0)
+ return rc;
+
+ *val = BIT((reg_val & DPS310_TMP_RATE_BITS) >> 4);
+
+ return 0;
+}
+
+static int dps310_get_pres_k(struct dps310_data *data, int *val)
+{
+ int reg_val, rc;
+
+ rc = regmap_read(data->regmap, DPS310_PRS_CFG, &reg_val);
+ if (rc < 0)
+ return rc;
+
+ *val = scale_factors[reg_val & GENMASK(2, 0)];
+
+ return 0;
+}
+
+static int dps310_get_temp_k(struct dps310_data *data, int *val)
+{
+ int reg_val, rc;
+
+ rc = regmap_read(data->regmap, DPS310_TMP_CFG, &reg_val);
+ if (rc < 0)
+ return rc;
+
+ *val = scale_factors[reg_val & GENMASK(2, 0)];
+
+ return 0;
+}
+
+static int dps310_reset_wait(struct dps310_data *data)
+{
+ int rc;
+
+ rc = regmap_write(data->regmap, DPS310_RESET, DPS310_RESET_MAGIC);
+ if (rc)
+ return rc;
+
+ /* Wait for device chip access: 15ms in specification */
+ usleep_range(15000, 55000);
+ return 0;
+}
+
+static int dps310_reset_reinit(struct dps310_data *data)
+{
+ int rc;
+
+ rc = dps310_reset_wait(data);
+ if (rc)
+ return rc;
+
+ return dps310_startup(data);
+}
+
+static int dps310_ready_status(struct dps310_data *data, int ready_bit, int timeout)
+{
+ int sleep = DPS310_POLL_SLEEP_US(timeout);
+ int ready;
+
+ return regmap_read_poll_timeout(data->regmap, DPS310_MEAS_CFG, ready, ready & ready_bit,
+ sleep, timeout);
+}
+
+static int dps310_ready(struct dps310_data *data, int ready_bit, int timeout)
+{
+ int rc;
+
+ rc = dps310_ready_status(data, ready_bit, timeout);
+ if (rc) {
+ if (rc == -ETIMEDOUT && !data->timeout_recovery_failed) {
+ /* Reset and reinitialize the chip. */
+ if (dps310_reset_reinit(data)) {
+ data->timeout_recovery_failed = true;
+ } else {
+ /* Try again to get sensor ready status. */
+ if (dps310_ready_status(data, ready_bit, timeout))
+ data->timeout_recovery_failed = true;
+ else
+ return 0;
+ }
+ }
+
+ return rc;
+ }
+
+ data->timeout_recovery_failed = false;
+ return 0;
+}
+
+static int dps310_read_pres_raw(struct dps310_data *data)
+{
+ int rc;
+ int rate;
+ int timeout;
+ s32 raw;
+ u8 val[3];
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -EINTR;
+
+ rc = dps310_get_pres_samp_freq(data, &rate);
+ if (rc)
+ goto done;
+
+ timeout = DPS310_POLL_TIMEOUT_US(rate);
+
+ /* Poll for sensor readiness; base the timeout upon the sample rate. */
+ rc = dps310_ready(data, DPS310_PRS_RDY, 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 timeout;
+
+ if (mutex_lock_interruptible(&data->lock))
+ return -EINTR;
+
+ rc = dps310_get_temp_samp_freq(data, &rate);
+ if (rc)
+ goto done;
+
+ timeout = DPS310_POLL_TIMEOUT_US(rate);
+
+ /* Poll for sensor readiness; base the timeout upon the sample rate. */
+ rc = dps310_ready(data, DPS310_TMP_RDY, timeout);
+ if (rc)
+ 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 *val)
+{
+ int i;
+ int rc;
+ int t_ready;
+ int kpi;
+ int kti;
+ s64 rem = 0ULL;
+ s64 pressure = 0ULL;
+ s64 p;
+ s64 t;
+ s64 denoms[7];
+ s64 nums[7];
+ s64 rems[7];
+ s64 kp;
+ s64 kt;
+
+ rc = dps310_get_pres_k(data, &kpi);
+ if (rc)
+ return rc;
+
+ rc = dps310_get_temp_k(data, &kti);
+ if (rc)
+ return rc;
+
+ 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;
+
+ *val = (int)min_t(s64, pressure, INT_MAX);
+
+ return 0;
+}
+
+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, val);
+ if (rc)
+ return 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, val);
+ if (rc)
+ return 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, val);
+ if (rc)
+ return rc;
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int dps310_calculate_temp(struct dps310_data *data, int *val)
+{
+ s64 c0;
+ s64 t;
+ int kt, rc;
+
+ rc = dps310_get_temp_k(data, &kt);
+ if (rc)
+ return rc;
+
+ /* 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 */
+ *val = (int)div_s64(t * 1000LL, kt);
+
+ return 0;
+}
+
+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, val);
+ if (rc)
+ return 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, val);
+ if (rc)
+ return rc;
+
+ return IIO_VAL_INT;
+
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ rc = dps310_get_temp_precision(data, val);
+ if (rc)
+ return 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;
+
+ dps310_reset_wait(data);
+}
+
+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,
+};
+
+static int dps310_probe(struct i2c_client *client)
+{
+ struct dps310_data *data;
+ struct iio_dev *iio;
+ int rc;
+
+ 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->name = DPS310_DEV_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;
+
+ rc = dps310_startup(data);
+ if (rc)
+ 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[] = {
+ { .name = DPS310_DEV_NAME },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, dps310_id);
+
+static const struct acpi_device_id dps310_acpi_match[] = {
+ { "IFX3100" },
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, dps310_acpi_match);
+
+static struct i2c_driver dps310_driver = {
+ .driver = {
+ .name = DPS310_DEV_NAME,
+ .acpi_match_table = dps310_acpi_match,
+ },
+ .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
new file mode 100644
index 000000000..e688b0776
--- /dev/null
+++ b/drivers/iio/pressure/hid-sensor-press.c
@@ -0,0 +1,348 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * HID Sensors Driver
+ * Copyright (c) 2014, Intel Corporation.
+ */
+#include <linux/bitops.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/hid-sensor-hub.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include "../common/hid-sensors/hid-sensor-trigger.h"
+
+enum {
+ CHANNEL_SCAN_INDEX_PRESSURE,
+ CHANNEL_SCAN_INDEX_TIMESTAMP,
+};
+
+struct press_state {
+ struct hid_sensor_hub_callbacks callbacks;
+ struct hid_sensor_common common_attributes;
+ struct hid_sensor_hub_attribute_info press_attr;
+ struct {
+ u32 press_data;
+ aligned_s64 timestamp;
+ } scan;
+ int scale_pre_decml;
+ int scale_post_decml;
+ int scale_precision;
+ int value_offset;
+ s64 timestamp;
+};
+
+static const u32 press_sensitivity_addresses[] = {
+ HID_USAGE_SENSOR_DATA_ATMOSPHERIC_PRESSURE,
+ HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE
+};
+
+/* Channel definitions */
+static const struct iio_chan_spec press_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_OFFSET) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_SAMP_FREQ) |
+ BIT(IIO_CHAN_INFO_HYSTERESIS),
+ .scan_index = CHANNEL_SCAN_INDEX_PRESSURE,
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(CHANNEL_SCAN_INDEX_TIMESTAMP)
+
+};
+
+/* Channel read_raw handler */
+static int press_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct press_state *press_state = iio_priv(indio_dev);
+ int report_id = -1;
+ u32 address;
+ int ret_type;
+ s32 min;
+
+ *val = 0;
+ *val2 = 0;
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->scan_index) {
+ case CHANNEL_SCAN_INDEX_PRESSURE:
+ report_id = press_state->press_attr.report_id;
+ min = press_state->press_attr.logical_minimum;
+ address = HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE;
+ break;
+ default:
+ report_id = -1;
+ break;
+ }
+ if (report_id >= 0) {
+ hid_sensor_power_state(&press_state->common_attributes,
+ true);
+ *val = sensor_hub_input_attr_get_raw_value(
+ press_state->common_attributes.hsdev,
+ HID_USAGE_SENSOR_PRESSURE, address,
+ report_id,
+ SENSOR_HUB_SYNC,
+ min < 0);
+ hid_sensor_power_state(&press_state->common_attributes,
+ false);
+ } else {
+ *val = 0;
+ return -EINVAL;
+ }
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ *val = press_state->scale_pre_decml;
+ *val2 = press_state->scale_post_decml;
+ ret_type = press_state->scale_precision;
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = press_state->value_offset;
+ ret_type = IIO_VAL_INT;
+ break;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret_type = hid_sensor_read_samp_freq_value(
+ &press_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret_type = hid_sensor_read_raw_hyst_value(
+ &press_state->common_attributes, val, val2);
+ break;
+ default:
+ ret_type = -EINVAL;
+ break;
+ }
+
+ return ret_type;
+}
+
+/* Channel write_raw handler */
+static int press_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct press_state *press_state = iio_priv(indio_dev);
+ int ret = 0;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = hid_sensor_write_samp_freq_value(
+ &press_state->common_attributes, val, val2);
+ break;
+ case IIO_CHAN_INFO_HYSTERESIS:
+ ret = hid_sensor_write_raw_hyst_value(
+ &press_state->common_attributes, val, val2);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
+static const struct iio_info press_info = {
+ .read_raw = &press_read_raw,
+ .write_raw = &press_write_raw,
+};
+
+/* Callback handler to send event after all samples are received and captured */
+static int press_proc_event(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id, void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct press_state *press_state = iio_priv(indio_dev);
+
+ dev_dbg(&indio_dev->dev, "press_proc_event\n");
+ if (atomic_read(&press_state->common_attributes.data_ready)) {
+ if (!press_state->timestamp)
+ press_state->timestamp = iio_get_time_ns(indio_dev);
+
+ iio_push_to_buffers_with_ts(indio_dev, &press_state->scan,
+ sizeof(press_state->scan),
+ press_state->timestamp);
+ }
+
+ return 0;
+}
+
+/* Capture samples in local storage */
+static int press_capture_sample(struct hid_sensor_hub_device *hsdev,
+ u32 usage_id,
+ size_t raw_len, char *raw_data,
+ void *priv)
+{
+ struct iio_dev *indio_dev = platform_get_drvdata(priv);
+ struct press_state *press_state = iio_priv(indio_dev);
+ int ret = -EINVAL;
+
+ switch (usage_id) {
+ case HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE:
+ press_state->scan.press_data = *(u32 *)raw_data;
+ ret = 0;
+ break;
+ case HID_USAGE_SENSOR_TIME_TIMESTAMP:
+ press_state->timestamp = hid_sensor_convert_timestamp(
+ &press_state->common_attributes, *(s64 *)raw_data);
+ break;
+ default:
+ break;
+ }
+
+ return ret;
+}
+
+/* Parse report which is specific to an usage id*/
+static int press_parse_report(struct platform_device *pdev,
+ struct hid_sensor_hub_device *hsdev,
+ struct iio_chan_spec *channels,
+ u32 usage_id,
+ struct press_state *st)
+{
+ int ret;
+
+ ret = sensor_hub_input_get_attribute_info(hsdev, HID_INPUT_REPORT,
+ usage_id,
+ HID_USAGE_SENSOR_ATMOSPHERIC_PRESSURE,
+ &st->press_attr);
+ if (ret < 0)
+ return ret;
+ channels[CHANNEL_SCAN_INDEX_PRESSURE].scan_type = (struct iio_scan_type) {
+ .format = 's',
+ .realbits = BYTES_TO_BITS(st->press_attr.size),
+ .storagebits = BITS_PER_TYPE(u32),
+ };
+
+ dev_dbg(&pdev->dev, "press %x:%x\n", st->press_attr.index,
+ st->press_attr.report_id);
+
+ st->scale_precision = hid_sensor_format_scale(
+ HID_USAGE_SENSOR_PRESSURE,
+ &st->press_attr,
+ &st->scale_pre_decml, &st->scale_post_decml);
+
+ return ret;
+}
+
+/* Function to initialize the processing for usage id */
+static int hid_press_probe(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
+ int ret = 0;
+ static const char *name = "press";
+ struct iio_dev *indio_dev;
+ struct press_state *press_state;
+
+ indio_dev = devm_iio_device_alloc(&pdev->dev, sizeof(struct press_state));
+ if (!indio_dev)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, indio_dev);
+
+ press_state = iio_priv(indio_dev);
+ press_state->common_attributes.hsdev = hsdev;
+ press_state->common_attributes.pdev = pdev;
+
+ ret = hid_sensor_parse_common_attributes(hsdev,
+ HID_USAGE_SENSOR_PRESSURE,
+ &press_state->common_attributes,
+ press_sensitivity_addresses,
+ ARRAY_SIZE(press_sensitivity_addresses));
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup common attributes\n");
+ return ret;
+ }
+
+ indio_dev->channels = devm_kmemdup(&pdev->dev, press_channels,
+ sizeof(press_channels), GFP_KERNEL);
+ if (!indio_dev->channels) {
+ dev_err(&pdev->dev, "failed to duplicate channels\n");
+ return -ENOMEM;
+ }
+
+ ret = press_parse_report(pdev, hsdev,
+ (struct iio_chan_spec *)indio_dev->channels,
+ HID_USAGE_SENSOR_PRESSURE, press_state);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to setup attributes\n");
+ return ret;
+ }
+
+ indio_dev->num_channels =
+ ARRAY_SIZE(press_channels);
+ indio_dev->info = &press_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ atomic_set(&press_state->common_attributes.data_ready, 0);
+
+ ret = hid_sensor_setup_trigger(indio_dev, name,
+ &press_state->common_attributes);
+ if (ret) {
+ dev_err(&pdev->dev, "trigger setup failed\n");
+ return ret;
+ }
+
+ press_state->callbacks.send_event = press_proc_event;
+ press_state->callbacks.capture_sample = press_capture_sample;
+ press_state->callbacks.pdev = pdev;
+ ret = sensor_hub_register_callback(hsdev, HID_USAGE_SENSOR_PRESSURE,
+ &press_state->callbacks);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "callback reg failed\n");
+ goto error_remove_trigger;
+ }
+
+ ret = iio_device_register(indio_dev);
+ if (ret) {
+ dev_err(&pdev->dev, "device register failed\n");
+ goto error_remove_callback;
+ }
+
+ return ret;
+
+error_remove_callback:
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
+error_remove_trigger:
+ hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
+ return ret;
+}
+
+/* Function to deinitialize the processing for usage id */
+static void hid_press_remove(struct platform_device *pdev)
+{
+ struct hid_sensor_hub_device *hsdev = dev_get_platdata(&pdev->dev);
+ struct iio_dev *indio_dev = platform_get_drvdata(pdev);
+ struct press_state *press_state = iio_priv(indio_dev);
+
+ iio_device_unregister(indio_dev);
+ sensor_hub_remove_callback(hsdev, HID_USAGE_SENSOR_PRESSURE);
+ hid_sensor_remove_trigger(indio_dev, &press_state->common_attributes);
+}
+
+static const struct platform_device_id hid_press_ids[] = {
+ {
+ /* Format: HID-SENSOR-usage_id_in_hex_lowercase */
+ .name = "HID-SENSOR-200031",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(platform, hid_press_ids);
+
+static struct platform_driver hid_press_platform_driver = {
+ .id_table = hid_press_ids,
+ .driver = {
+ .name = KBUILD_MODNAME,
+ .pm = &hid_sensor_pm_ops,
+ },
+ .probe = hid_press_probe,
+ .remove = hid_press_remove,
+};
+module_platform_driver(hid_press_platform_driver);
+
+MODULE_DESCRIPTION("HID Sensor Pressure");
+MODULE_AUTHOR("Archana Patni <archana.patni@intel.com>");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HID");
diff --git a/drivers/iio/pressure/hp03.c b/drivers/iio/pressure/hp03.c
new file mode 100644
index 000000000..424523345
--- /dev/null
+++ b/drivers/iio/pressure/hp03.c
@@ -0,0 +1,292 @@
+// 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.
+ */
+
+#define pr_fmt(fmt) "hp03: " fmt
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+
+/*
+ * The HP03 sensor occupies two fixed I2C addresses:
+ * 0x50 ... read-only EEPROM with calibration data
+ * 0x77 ... read-write ADC for pressure and temperature
+ */
+#define HP03_EEPROM_ADDR 0x50
+#define HP03_ADC_ADDR 0x77
+
+#define HP03_EEPROM_CX_OFFSET 0x10
+#define HP03_EEPROM_AB_OFFSET 0x1e
+#define HP03_EEPROM_CD_OFFSET 0x20
+
+#define HP03_ADC_WRITE_REG 0xff
+#define HP03_ADC_READ_REG 0xfd
+#define HP03_ADC_READ_PRESSURE 0xf0 /* D1 in datasheet */
+#define HP03_ADC_READ_TEMP 0xe8 /* D2 in datasheet */
+
+struct hp03_priv {
+ struct i2c_client *client;
+ struct mutex lock;
+ struct gpio_desc *xclr_gpio;
+
+ struct i2c_client *eeprom_client;
+ struct regmap *eeprom_regmap;
+
+ s32 pressure; /* kPa */
+ s32 temp; /* Deg. C */
+};
+
+static const struct iio_chan_spec hp03_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static bool hp03_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static bool hp03_is_volatile_reg(struct device *dev, unsigned int reg)
+{
+ return false;
+}
+
+static const struct regmap_config hp03_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = HP03_EEPROM_CD_OFFSET + 1,
+ .cache_type = REGCACHE_RBTREE,
+
+ .writeable_reg = hp03_is_writeable_reg,
+ .volatile_reg = hp03_is_volatile_reg,
+};
+
+static int hp03_get_temp_pressure(struct hp03_priv *priv, const u8 reg)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(priv->client, HP03_ADC_WRITE_REG, reg);
+ if (ret < 0)
+ return ret;
+
+ msleep(50); /* Wait for conversion to finish */
+
+ return i2c_smbus_read_word_data(priv->client, HP03_ADC_READ_REG);
+}
+
+static int hp03_update_temp_pressure(struct hp03_priv *priv)
+{
+ struct device *dev = &priv->client->dev;
+ u8 coefs[18];
+ u16 cx_val[7];
+ int ab_val, d1_val, d2_val, diff_val, dut, off, sens, x;
+ int i, ret;
+
+ /* Sample coefficients from EEPROM */
+ ret = regmap_bulk_read(priv->eeprom_regmap, HP03_EEPROM_CX_OFFSET,
+ coefs, sizeof(coefs));
+ if (ret < 0) {
+ dev_err(dev, "Failed to read EEPROM (reg=%02x)\n",
+ HP03_EEPROM_CX_OFFSET);
+ return ret;
+ }
+
+ /* Sample Temperature and Pressure */
+ gpiod_set_value_cansleep(priv->xclr_gpio, 1);
+
+ ret = hp03_get_temp_pressure(priv, HP03_ADC_READ_PRESSURE);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read pressure\n");
+ goto err_adc;
+ }
+ d1_val = ret;
+
+ ret = hp03_get_temp_pressure(priv, HP03_ADC_READ_TEMP);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read temperature\n");
+ goto err_adc;
+ }
+ d2_val = ret;
+
+ gpiod_set_value_cansleep(priv->xclr_gpio, 0);
+
+ /* The Cx coefficients and Temp/Pressure values are MSB first. */
+ for (i = 0; i < 7; i++)
+ cx_val[i] = (coefs[2 * i] << 8) | (coefs[(2 * i) + 1] << 0);
+ d1_val = ((d1_val >> 8) & 0xff) | ((d1_val & 0xff) << 8);
+ d2_val = ((d2_val >> 8) & 0xff) | ((d2_val & 0xff) << 8);
+
+ /* Coefficient voodoo from the HP03 datasheet. */
+ if (d2_val >= cx_val[4])
+ ab_val = coefs[14]; /* A-value */
+ else
+ ab_val = coefs[15]; /* B-value */
+
+ diff_val = d2_val - cx_val[4];
+ dut = (ab_val * (diff_val >> 7) * (diff_val >> 7)) >> coefs[16];
+ dut = diff_val - dut;
+
+ off = (cx_val[1] + (((cx_val[3] - 1024) * dut) >> 14)) * 4;
+ sens = cx_val[0] + ((cx_val[2] * dut) >> 10);
+ x = ((sens * (d1_val - 7168)) >> 14) - off;
+
+ priv->pressure = ((x * 100) >> 5) + (cx_val[6] * 10);
+ priv->temp = 250 + ((dut * cx_val[5]) >> 16) - (dut >> coefs[17]);
+
+ return 0;
+
+err_adc:
+ gpiod_set_value_cansleep(priv->xclr_gpio, 0);
+ return ret;
+}
+
+static int hp03_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct hp03_priv *priv = iio_priv(indio_dev);
+ int ret;
+
+ mutex_lock(&priv->lock);
+ ret = hp03_update_temp_pressure(priv);
+ mutex_unlock(&priv->lock);
+
+ if (ret)
+ return ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ *val = priv->pressure;
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = priv->temp;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ *val = 0;
+ *val2 = 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = 10;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+static const struct iio_info hp03_info = {
+ .read_raw = &hp03_read_raw,
+};
+
+static int hp03_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct device *dev = &client->dev;
+ struct iio_dev *indio_dev;
+ struct hp03_priv *priv;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*priv));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+ priv->client = client;
+ mutex_init(&priv->lock);
+
+ indio_dev->name = id->name;
+ indio_dev->channels = hp03_channels;
+ indio_dev->num_channels = ARRAY_SIZE(hp03_channels);
+ indio_dev->info = &hp03_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ priv->xclr_gpio = devm_gpiod_get_index(dev, "xclr", 0, GPIOD_OUT_HIGH);
+ if (IS_ERR(priv->xclr_gpio)) {
+ dev_err(dev, "Failed to claim XCLR GPIO\n");
+ ret = PTR_ERR(priv->xclr_gpio);
+ return ret;
+ }
+
+ /*
+ * Allocate another device for the on-sensor EEPROM,
+ * which has it's dedicated I2C address and contains
+ * the calibration constants for the sensor.
+ */
+ priv->eeprom_client = devm_i2c_new_dummy_device(dev, client->adapter,
+ HP03_EEPROM_ADDR);
+ if (IS_ERR(priv->eeprom_client)) {
+ dev_err(dev, "New EEPROM I2C device failed\n");
+ return PTR_ERR(priv->eeprom_client);
+ }
+
+ priv->eeprom_regmap = devm_regmap_init_i2c(priv->eeprom_client,
+ &hp03_regmap_config);
+ if (IS_ERR(priv->eeprom_regmap)) {
+ dev_err(dev, "Failed to allocate EEPROM regmap\n");
+ return PTR_ERR(priv->eeprom_regmap);
+ }
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret) {
+ dev_err(dev, "Failed to register IIO device\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct i2c_device_id hp03_id[] = {
+ { .name = "hp03" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, hp03_id);
+
+static const struct of_device_id hp03_of_match[] = {
+ { .compatible = "hoperf,hp03" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hp03_of_match);
+
+static struct i2c_driver hp03_driver = {
+ .driver = {
+ .name = "hp03",
+ .of_match_table = hp03_of_match,
+ },
+ .probe = hp03_probe,
+ .id_table = hp03_id,
+};
+module_i2c_driver(hp03_driver);
+
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_DESCRIPTION("Driver for Hope RF HP03 pressure and temperature sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/hp206c.c b/drivers/iio/pressure/hp206c.c
new file mode 100644
index 000000000..ee34c8908
--- /dev/null
+++ b/drivers/iio/pressure/hp206c.c
@@ -0,0 +1,421 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * hp206c.c - HOPERF HP206C precision barometer and altimeter sensor
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * (7-bit I2C slave address 0x76)
+ *
+ * Datasheet:
+ * http://www.hoperf.com/upload/sensor/HP206C_DataSheet_EN_V2.0.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/delay.h>
+#include <linux/util_macros.h>
+
+#include <linux/unaligned.h>
+
+/* I2C commands: */
+#define HP206C_CMD_SOFT_RST 0x06
+
+#define HP206C_CMD_ADC_CVT 0x40
+
+#define HP206C_CMD_ADC_CVT_OSR_4096 0x00
+#define HP206C_CMD_ADC_CVT_OSR_2048 0x04
+#define HP206C_CMD_ADC_CVT_OSR_1024 0x08
+#define HP206C_CMD_ADC_CVT_OSR_512 0x0c
+#define HP206C_CMD_ADC_CVT_OSR_256 0x10
+#define HP206C_CMD_ADC_CVT_OSR_128 0x14
+
+#define HP206C_CMD_ADC_CVT_CHNL_PT 0x00
+#define HP206C_CMD_ADC_CVT_CHNL_T 0x02
+
+#define HP206C_CMD_READ_P 0x30
+#define HP206C_CMD_READ_T 0x32
+
+#define HP206C_CMD_READ_REG 0x80
+#define HP206C_CMD_WRITE_REG 0xc0
+
+#define HP206C_REG_INT_EN 0x0b
+#define HP206C_REG_INT_CFG 0x0c
+
+#define HP206C_REG_INT_SRC 0x0d
+#define HP206C_FLAG_DEV_RDY 0x40
+
+#define HP206C_REG_PARA 0x0f
+#define HP206C_FLAG_CMPS_EN 0x80
+
+/* Maximum spin for DEV_RDY */
+#define HP206C_MAX_DEV_RDY_WAIT_COUNT 20
+#define HP206C_DEV_RDY_WAIT_US 20000
+
+struct hp206c_data {
+ struct mutex mutex;
+ struct i2c_client *client;
+ int temp_osr_index;
+ int pres_osr_index;
+};
+
+struct hp206c_osr_setting {
+ u8 osr_mask;
+ unsigned int temp_conv_time_us;
+ unsigned int pres_conv_time_us;
+};
+
+/* Data from Table 5 in datasheet. */
+static const struct hp206c_osr_setting hp206c_osr_settings[] = {
+ { HP206C_CMD_ADC_CVT_OSR_4096, 65600, 131100 },
+ { HP206C_CMD_ADC_CVT_OSR_2048, 32800, 65600 },
+ { HP206C_CMD_ADC_CVT_OSR_1024, 16400, 32800 },
+ { HP206C_CMD_ADC_CVT_OSR_512, 8200, 16400 },
+ { HP206C_CMD_ADC_CVT_OSR_256, 4100, 8200 },
+ { HP206C_CMD_ADC_CVT_OSR_128, 2100, 4100 },
+};
+static const int hp206c_osr_rates[] = { 4096, 2048, 1024, 512, 256, 128 };
+static const char hp206c_osr_rates_str[] = "4096 2048 1024 512 256 128";
+
+static inline int hp206c_read_reg(struct i2c_client *client, u8 reg)
+{
+ return i2c_smbus_read_byte_data(client, HP206C_CMD_READ_REG | reg);
+}
+
+static inline int hp206c_write_reg(struct i2c_client *client, u8 reg, u8 val)
+{
+ return i2c_smbus_write_byte_data(client,
+ HP206C_CMD_WRITE_REG | reg, val);
+}
+
+static int hp206c_read_20bit(struct i2c_client *client, u8 cmd)
+{
+ int ret;
+ u8 values[3];
+
+ ret = i2c_smbus_read_i2c_block_data(client, cmd, sizeof(values), values);
+ if (ret < 0)
+ return ret;
+ if (ret != sizeof(values))
+ return -EIO;
+ return get_unaligned_be24(&values[0]) & GENMASK(19, 0);
+}
+
+/* Spin for max 160ms until DEV_RDY is 1, or return error. */
+static int hp206c_wait_dev_rdy(struct iio_dev *indio_dev)
+{
+ int ret;
+ int count = 0;
+ struct hp206c_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+
+ while (++count <= HP206C_MAX_DEV_RDY_WAIT_COUNT) {
+ ret = hp206c_read_reg(client, HP206C_REG_INT_SRC);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed READ_REG INT_SRC: %d\n", ret);
+ return ret;
+ }
+ if (ret & HP206C_FLAG_DEV_RDY)
+ return 0;
+ usleep_range(HP206C_DEV_RDY_WAIT_US, HP206C_DEV_RDY_WAIT_US * 3 / 2);
+ }
+ return -ETIMEDOUT;
+}
+
+static int hp206c_set_compensation(struct i2c_client *client, bool enabled)
+{
+ int val;
+
+ val = hp206c_read_reg(client, HP206C_REG_PARA);
+ if (val < 0)
+ return val;
+ if (enabled)
+ val |= HP206C_FLAG_CMPS_EN;
+ else
+ val &= ~HP206C_FLAG_CMPS_EN;
+
+ return hp206c_write_reg(client, HP206C_REG_PARA, val);
+}
+
+/* Do a soft reset */
+static int hp206c_soft_reset(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct hp206c_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+
+ ret = i2c_smbus_write_byte(client, HP206C_CMD_SOFT_RST);
+ if (ret) {
+ dev_err(&client->dev, "Failed to reset device: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(400, 600);
+
+ ret = hp206c_wait_dev_rdy(indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "Device not ready after soft reset: %d\n", ret);
+ return ret;
+ }
+
+ ret = hp206c_set_compensation(client, true);
+ if (ret)
+ dev_err(&client->dev, "Failed to enable compensation: %d\n", ret);
+ return ret;
+}
+
+static int hp206c_conv_and_read(struct iio_dev *indio_dev,
+ u8 conv_cmd, u8 read_cmd,
+ unsigned int sleep_us)
+{
+ int ret;
+ struct hp206c_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = data->client;
+
+ ret = hp206c_wait_dev_rdy(indio_dev);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Device not ready: %d\n", ret);
+ return ret;
+ }
+
+ ret = i2c_smbus_write_byte(client, conv_cmd);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Failed convert: %d\n", ret);
+ return ret;
+ }
+
+ usleep_range(sleep_us, sleep_us * 3 / 2);
+
+ ret = hp206c_wait_dev_rdy(indio_dev);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "Device not ready: %d\n", ret);
+ return ret;
+ }
+
+ ret = hp206c_read_20bit(client, read_cmd);
+ if (ret < 0)
+ dev_err(&indio_dev->dev, "Failed read: %d\n", ret);
+
+ return ret;
+}
+
+static int hp206c_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val,
+ int *val2, long mask)
+{
+ int ret;
+ struct hp206c_data *data = iio_priv(indio_dev);
+ const struct hp206c_osr_setting *osr_setting;
+ u8 conv_cmd;
+
+ mutex_lock(&data->mutex);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = hp206c_osr_rates[data->temp_osr_index];
+ ret = IIO_VAL_INT;
+ break;
+
+ case IIO_PRESSURE:
+ *val = hp206c_osr_rates[data->pres_osr_index];
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+
+ case IIO_CHAN_INFO_RAW:
+ switch (chan->type) {
+ case IIO_TEMP:
+ osr_setting = &hp206c_osr_settings[data->temp_osr_index];
+ conv_cmd = HP206C_CMD_ADC_CVT |
+ osr_setting->osr_mask |
+ HP206C_CMD_ADC_CVT_CHNL_T;
+ ret = hp206c_conv_and_read(indio_dev,
+ conv_cmd,
+ HP206C_CMD_READ_T,
+ osr_setting->temp_conv_time_us);
+ if (ret >= 0) {
+ /* 20 significant bits are provided.
+ * Extend sign over the rest.
+ */
+ *val = sign_extend32(ret, 19);
+ ret = IIO_VAL_INT;
+ }
+ break;
+
+ case IIO_PRESSURE:
+ osr_setting = &hp206c_osr_settings[data->pres_osr_index];
+ conv_cmd = HP206C_CMD_ADC_CVT |
+ osr_setting->osr_mask |
+ HP206C_CMD_ADC_CVT_CHNL_PT;
+ ret = hp206c_conv_and_read(indio_dev,
+ conv_cmd,
+ HP206C_CMD_READ_P,
+ osr_setting->pres_conv_time_us);
+ if (ret >= 0) {
+ *val = ret;
+ ret = IIO_VAL_INT;
+ }
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = 10000;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+
+ case IIO_PRESSURE:
+ *val = 0;
+ *val2 = 1000;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ break;
+
+ default:
+ ret = -EINVAL;
+ }
+
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static int hp206c_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ int ret = 0;
+ struct hp206c_data *data = iio_priv(indio_dev);
+
+ if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return -EINVAL;
+ mutex_lock(&data->mutex);
+ switch (chan->type) {
+ case IIO_TEMP:
+ data->temp_osr_index = find_closest_descending(val,
+ hp206c_osr_rates, ARRAY_SIZE(hp206c_osr_rates));
+ break;
+ case IIO_PRESSURE:
+ data->pres_osr_index = find_closest_descending(val,
+ hp206c_osr_rates, ARRAY_SIZE(hp206c_osr_rates));
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ mutex_unlock(&data->mutex);
+ return ret;
+}
+
+static const struct iio_chan_spec hp206c_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ },
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ }
+};
+
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(hp206c_osr_rates_str);
+
+static struct attribute *hp206c_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group hp206c_attribute_group = {
+ .attrs = hp206c_attributes,
+};
+
+static const struct iio_info hp206c_info = {
+ .attrs = &hp206c_attribute_group,
+ .read_raw = hp206c_read_raw,
+ .write_raw = hp206c_write_raw,
+};
+
+static int hp206c_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct iio_dev *indio_dev;
+ struct hp206c_data *data;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_BYTE |
+ I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ dev_err(&client->dev, "Adapter does not support "
+ "all required i2c functionality\n");
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ mutex_init(&data->mutex);
+
+ indio_dev->info = &hp206c_info;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = hp206c_channels;
+ indio_dev->num_channels = ARRAY_SIZE(hp206c_channels);
+
+ i2c_set_clientdata(client, indio_dev);
+
+ /* Do a soft reset on probe */
+ ret = hp206c_soft_reset(indio_dev);
+ if (ret) {
+ dev_err(&client->dev, "Failed to reset on startup: %d\n", ret);
+ return -ENODEV;
+ }
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id hp206c_id[] = {
+ { .name = "hp206c" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, hp206c_id);
+
+static const struct acpi_device_id hp206c_acpi_match[] = {
+ {"HOP206C", 0},
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, hp206c_acpi_match);
+
+static struct i2c_driver hp206c_driver = {
+ .probe = hp206c_probe,
+ .id_table = hp206c_id,
+ .driver = {
+ .name = "hp206c",
+ .acpi_match_table = hp206c_acpi_match,
+ },
+};
+
+module_i2c_driver(hp206c_driver);
+
+MODULE_DESCRIPTION("HOPERF HP206C precision barometer and altimeter sensor");
+MODULE_AUTHOR("Leonard Crestez <leonard.crestez@intel.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/hsc030pa.c b/drivers/iio/pressure/hsc030pa.c
new file mode 100644
index 000000000..0374d406b
--- /dev/null
+++ b/drivers/iio/pressure/hsc030pa.c
@@ -0,0 +1,540 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Honeywell TruStability HSC Series pressure/temperature sensor
+ *
+ * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro>
+ *
+ * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/trustability-hsc-series/documents/sps-siot-trustability-hsc-series-high-accuracy-board-mount-pressure-sensors-50099148-a-en-ciid-151133.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/init.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/printk.h>
+#include <linux/property.h>
+#include <linux/regulator/consumer.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/units.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/unaligned.h>
+
+#include "hsc030pa.h"
+
+/*
+ * HSC_PRESSURE_TRIPLET_LEN - length for the string that defines the
+ * pressure range, measurement unit and type as per the part nomenclature.
+ * Consult honeywell,pressure-triplet in the bindings file for details.
+ */
+#define HSC_PRESSURE_TRIPLET_LEN 6
+#define HSC_STATUS_MASK GENMASK(7, 6)
+#define HSC_TEMPERATURE_MASK GENMASK(15, 5)
+#define HSC_PRESSURE_MASK GENMASK(29, 16)
+
+struct hsc_func_spec {
+ u32 output_min;
+ u32 output_max;
+};
+
+/*
+ * function A: 10% - 90% of 2^14
+ * function B: 5% - 95% of 2^14
+ * function C: 5% - 85% of 2^14
+ * function F: 4% - 94% of 2^14
+ */
+static const struct hsc_func_spec hsc_func_spec[] = {
+ [HSC_FUNCTION_A] = { .output_min = 1638, .output_max = 14746 },
+ [HSC_FUNCTION_B] = { .output_min = 819, .output_max = 15565 },
+ [HSC_FUNCTION_C] = { .output_min = 819, .output_max = 13926 },
+ [HSC_FUNCTION_F] = { .output_min = 655, .output_max = 15401 },
+};
+
+enum hsc_variants {
+ HSC001BA = 0x00, HSC1_6BA = 0x01, HSC2_5BA = 0x02, HSC004BA = 0x03,
+ HSC006BA = 0x04, HSC010BA = 0x05, HSC1_6MD = 0x06, HSC2_5MD = 0x07,
+ HSC004MD = 0x08, HSC006MD = 0x09, HSC010MD = 0x0a, HSC016MD = 0x0b,
+ HSC025MD = 0x0c, HSC040MD = 0x0d, HSC060MD = 0x0e, HSC100MD = 0x0f,
+ HSC160MD = 0x10, HSC250MD = 0x11, HSC400MD = 0x12, HSC600MD = 0x13,
+ HSC001BD = 0x14, HSC1_6BD = 0x15, HSC2_5BD = 0x16, HSC004BD = 0x17,
+ HSC2_5MG = 0x18, HSC004MG = 0x19, HSC006MG = 0x1a, HSC010MG = 0x1b,
+ HSC016MG = 0x1c, HSC025MG = 0x1d, HSC040MG = 0x1e, HSC060MG = 0x1f,
+ HSC100MG = 0x20, HSC160MG = 0x21, HSC250MG = 0x22, HSC400MG = 0x23,
+ HSC600MG = 0x24, HSC001BG = 0x25, HSC1_6BG = 0x26, HSC2_5BG = 0x27,
+ HSC004BG = 0x28, HSC006BG = 0x29, HSC010BG = 0x2a, HSC100KA = 0x2b,
+ HSC160KA = 0x2c, HSC250KA = 0x2d, HSC400KA = 0x2e, HSC600KA = 0x2f,
+ HSC001GA = 0x30, HSC160LD = 0x31, HSC250LD = 0x32, HSC400LD = 0x33,
+ HSC600LD = 0x34, HSC001KD = 0x35, HSC1_6KD = 0x36, HSC2_5KD = 0x37,
+ HSC004KD = 0x38, HSC006KD = 0x39, HSC010KD = 0x3a, HSC016KD = 0x3b,
+ HSC025KD = 0x3c, HSC040KD = 0x3d, HSC060KD = 0x3e, HSC100KD = 0x3f,
+ HSC160KD = 0x40, HSC250KD = 0x41, HSC400KD = 0x42, HSC250LG = 0x43,
+ HSC400LG = 0x44, HSC600LG = 0x45, HSC001KG = 0x46, HSC1_6KG = 0x47,
+ HSC2_5KG = 0x48, HSC004KG = 0x49, HSC006KG = 0x4a, HSC010KG = 0x4b,
+ HSC016KG = 0x4c, HSC025KG = 0x4d, HSC040KG = 0x4e, HSC060KG = 0x4f,
+ HSC100KG = 0x50, HSC160KG = 0x51, HSC250KG = 0x52, HSC400KG = 0x53,
+ HSC600KG = 0x54, HSC001GG = 0x55, HSC015PA = 0x56, HSC030PA = 0x57,
+ HSC060PA = 0x58, HSC100PA = 0x59, HSC150PA = 0x5a, HSC0_5ND = 0x5b,
+ HSC001ND = 0x5c, HSC002ND = 0x5d, HSC004ND = 0x5e, HSC005ND = 0x5f,
+ HSC010ND = 0x60, HSC020ND = 0x61, HSC030ND = 0x62, HSC001PD = 0x63,
+ HSC005PD = 0x64, HSC015PD = 0x65, HSC030PD = 0x66, HSC060PD = 0x67,
+ HSC001NG = 0x68, HSC002NG = 0x69, HSC004NG = 0x6a, HSC005NG = 0x6b,
+ HSC010NG = 0x6c, HSC020NG = 0x6d, HSC030NG = 0x6e, HSC001PG = 0x6f,
+ HSC005PG = 0x70, HSC015PG = 0x71, HSC030PG = 0x72, HSC060PG = 0x73,
+ HSC100PG = 0x74, HSC150PG = 0x75, HSC_VARIANTS_MAX
+};
+
+static const char * const hsc_triplet_variants[HSC_VARIANTS_MAX] = {
+ [HSC001BA] = "001BA", [HSC1_6BA] = "1.6BA", [HSC2_5BA] = "2.5BA",
+ [HSC004BA] = "004BA", [HSC006BA] = "006BA", [HSC010BA] = "010BA",
+ [HSC1_6MD] = "1.6MD", [HSC2_5MD] = "2.5MD", [HSC004MD] = "004MD",
+ [HSC006MD] = "006MD", [HSC010MD] = "010MD", [HSC016MD] = "016MD",
+ [HSC025MD] = "025MD", [HSC040MD] = "040MD", [HSC060MD] = "060MD",
+ [HSC100MD] = "100MD", [HSC160MD] = "160MD", [HSC250MD] = "250MD",
+ [HSC400MD] = "400MD", [HSC600MD] = "600MD", [HSC001BD] = "001BD",
+ [HSC1_6BD] = "1.6BD", [HSC2_5BD] = "2.5BD", [HSC004BD] = "004BD",
+ [HSC2_5MG] = "2.5MG", [HSC004MG] = "004MG", [HSC006MG] = "006MG",
+ [HSC010MG] = "010MG", [HSC016MG] = "016MG", [HSC025MG] = "025MG",
+ [HSC040MG] = "040MG", [HSC060MG] = "060MG", [HSC100MG] = "100MG",
+ [HSC160MG] = "160MG", [HSC250MG] = "250MG", [HSC400MG] = "400MG",
+ [HSC600MG] = "600MG", [HSC001BG] = "001BG", [HSC1_6BG] = "1.6BG",
+ [HSC2_5BG] = "2.5BG", [HSC004BG] = "004BG", [HSC006BG] = "006BG",
+ [HSC010BG] = "010BG", [HSC100KA] = "100KA", [HSC160KA] = "160KA",
+ [HSC250KA] = "250KA", [HSC400KA] = "400KA", [HSC600KA] = "600KA",
+ [HSC001GA] = "001GA", [HSC160LD] = "160LD", [HSC250LD] = "250LD",
+ [HSC400LD] = "400LD", [HSC600LD] = "600LD", [HSC001KD] = "001KD",
+ [HSC1_6KD] = "1.6KD", [HSC2_5KD] = "2.5KD", [HSC004KD] = "004KD",
+ [HSC006KD] = "006KD", [HSC010KD] = "010KD", [HSC016KD] = "016KD",
+ [HSC025KD] = "025KD", [HSC040KD] = "040KD", [HSC060KD] = "060KD",
+ [HSC100KD] = "100KD", [HSC160KD] = "160KD", [HSC250KD] = "250KD",
+ [HSC400KD] = "400KD", [HSC250LG] = "250LG", [HSC400LG] = "400LG",
+ [HSC600LG] = "600LG", [HSC001KG] = "001KG", [HSC1_6KG] = "1.6KG",
+ [HSC2_5KG] = "2.5KG", [HSC004KG] = "004KG", [HSC006KG] = "006KG",
+ [HSC010KG] = "010KG", [HSC016KG] = "016KG", [HSC025KG] = "025KG",
+ [HSC040KG] = "040KG", [HSC060KG] = "060KG", [HSC100KG] = "100KG",
+ [HSC160KG] = "160KG", [HSC250KG] = "250KG", [HSC400KG] = "400KG",
+ [HSC600KG] = "600KG", [HSC001GG] = "001GG", [HSC015PA] = "015PA",
+ [HSC030PA] = "030PA", [HSC060PA] = "060PA", [HSC100PA] = "100PA",
+ [HSC150PA] = "150PA", [HSC0_5ND] = "0.5ND", [HSC001ND] = "001ND",
+ [HSC002ND] = "002ND", [HSC004ND] = "004ND", [HSC005ND] = "005ND",
+ [HSC010ND] = "010ND", [HSC020ND] = "020ND", [HSC030ND] = "030ND",
+ [HSC001PD] = "001PD", [HSC005PD] = "005PD", [HSC015PD] = "015PD",
+ [HSC030PD] = "030PD", [HSC060PD] = "060PD", [HSC001NG] = "001NG",
+ [HSC002NG] = "002NG", [HSC004NG] = "004NG", [HSC005NG] = "005NG",
+ [HSC010NG] = "010NG", [HSC020NG] = "020NG", [HSC030NG] = "030NG",
+ [HSC001PG] = "001PG", [HSC005PG] = "005PG", [HSC015PG] = "015PG",
+ [HSC030PG] = "030PG", [HSC060PG] = "060PG", [HSC100PG] = "100PG",
+ [HSC150PG] = "150PG",
+};
+
+/**
+ * struct hsc_range_config - list of pressure ranges based on nomenclature
+ * @pmin: lowest pressure that can be measured
+ * @pmax: highest pressure that can be measured
+ */
+struct hsc_range_config {
+ const s32 pmin;
+ const s32 pmax;
+};
+
+/* All min max limits have been converted to pascals */
+static const struct hsc_range_config hsc_range_config[HSC_VARIANTS_MAX] = {
+ [HSC001BA] = { .pmin = 0, .pmax = 100000 },
+ [HSC1_6BA] = { .pmin = 0, .pmax = 160000 },
+ [HSC2_5BA] = { .pmin = 0, .pmax = 250000 },
+ [HSC004BA] = { .pmin = 0, .pmax = 400000 },
+ [HSC006BA] = { .pmin = 0, .pmax = 600000 },
+ [HSC010BA] = { .pmin = 0, .pmax = 1000000 },
+ [HSC1_6MD] = { .pmin = -160, .pmax = 160 },
+ [HSC2_5MD] = { .pmin = -250, .pmax = 250 },
+ [HSC004MD] = { .pmin = -400, .pmax = 400 },
+ [HSC006MD] = { .pmin = -600, .pmax = 600 },
+ [HSC010MD] = { .pmin = -1000, .pmax = 1000 },
+ [HSC016MD] = { .pmin = -1600, .pmax = 1600 },
+ [HSC025MD] = { .pmin = -2500, .pmax = 2500 },
+ [HSC040MD] = { .pmin = -4000, .pmax = 4000 },
+ [HSC060MD] = { .pmin = -6000, .pmax = 6000 },
+ [HSC100MD] = { .pmin = -10000, .pmax = 10000 },
+ [HSC160MD] = { .pmin = -16000, .pmax = 16000 },
+ [HSC250MD] = { .pmin = -25000, .pmax = 25000 },
+ [HSC400MD] = { .pmin = -40000, .pmax = 40000 },
+ [HSC600MD] = { .pmin = -60000, .pmax = 60000 },
+ [HSC001BD] = { .pmin = -100000, .pmax = 100000 },
+ [HSC1_6BD] = { .pmin = -160000, .pmax = 160000 },
+ [HSC2_5BD] = { .pmin = -250000, .pmax = 250000 },
+ [HSC004BD] = { .pmin = -400000, .pmax = 400000 },
+ [HSC2_5MG] = { .pmin = 0, .pmax = 250 },
+ [HSC004MG] = { .pmin = 0, .pmax = 400 },
+ [HSC006MG] = { .pmin = 0, .pmax = 600 },
+ [HSC010MG] = { .pmin = 0, .pmax = 1000 },
+ [HSC016MG] = { .pmin = 0, .pmax = 1600 },
+ [HSC025MG] = { .pmin = 0, .pmax = 2500 },
+ [HSC040MG] = { .pmin = 0, .pmax = 4000 },
+ [HSC060MG] = { .pmin = 0, .pmax = 6000 },
+ [HSC100MG] = { .pmin = 0, .pmax = 10000 },
+ [HSC160MG] = { .pmin = 0, .pmax = 16000 },
+ [HSC250MG] = { .pmin = 0, .pmax = 25000 },
+ [HSC400MG] = { .pmin = 0, .pmax = 40000 },
+ [HSC600MG] = { .pmin = 0, .pmax = 60000 },
+ [HSC001BG] = { .pmin = 0, .pmax = 100000 },
+ [HSC1_6BG] = { .pmin = 0, .pmax = 160000 },
+ [HSC2_5BG] = { .pmin = 0, .pmax = 250000 },
+ [HSC004BG] = { .pmin = 0, .pmax = 400000 },
+ [HSC006BG] = { .pmin = 0, .pmax = 600000 },
+ [HSC010BG] = { .pmin = 0, .pmax = 1000000 },
+ [HSC100KA] = { .pmin = 0, .pmax = 100000 },
+ [HSC160KA] = { .pmin = 0, .pmax = 160000 },
+ [HSC250KA] = { .pmin = 0, .pmax = 250000 },
+ [HSC400KA] = { .pmin = 0, .pmax = 400000 },
+ [HSC600KA] = { .pmin = 0, .pmax = 600000 },
+ [HSC001GA] = { .pmin = 0, .pmax = 1000000 },
+ [HSC160LD] = { .pmin = -160, .pmax = 160 },
+ [HSC250LD] = { .pmin = -250, .pmax = 250 },
+ [HSC400LD] = { .pmin = -400, .pmax = 400 },
+ [HSC600LD] = { .pmin = -600, .pmax = 600 },
+ [HSC001KD] = { .pmin = -1000, .pmax = 1000 },
+ [HSC1_6KD] = { .pmin = -1600, .pmax = 1600 },
+ [HSC2_5KD] = { .pmin = -2500, .pmax = 2500 },
+ [HSC004KD] = { .pmin = -4000, .pmax = 4000 },
+ [HSC006KD] = { .pmin = -6000, .pmax = 6000 },
+ [HSC010KD] = { .pmin = -10000, .pmax = 10000 },
+ [HSC016KD] = { .pmin = -16000, .pmax = 16000 },
+ [HSC025KD] = { .pmin = -25000, .pmax = 25000 },
+ [HSC040KD] = { .pmin = -40000, .pmax = 40000 },
+ [HSC060KD] = { .pmin = -60000, .pmax = 60000 },
+ [HSC100KD] = { .pmin = -100000, .pmax = 100000 },
+ [HSC160KD] = { .pmin = -160000, .pmax = 160000 },
+ [HSC250KD] = { .pmin = -250000, .pmax = 250000 },
+ [HSC400KD] = { .pmin = -400000, .pmax = 400000 },
+ [HSC250LG] = { .pmin = 0, .pmax = 250 },
+ [HSC400LG] = { .pmin = 0, .pmax = 400 },
+ [HSC600LG] = { .pmin = 0, .pmax = 600 },
+ [HSC001KG] = { .pmin = 0, .pmax = 1000 },
+ [HSC1_6KG] = { .pmin = 0, .pmax = 1600 },
+ [HSC2_5KG] = { .pmin = 0, .pmax = 2500 },
+ [HSC004KG] = { .pmin = 0, .pmax = 4000 },
+ [HSC006KG] = { .pmin = 0, .pmax = 6000 },
+ [HSC010KG] = { .pmin = 0, .pmax = 10000 },
+ [HSC016KG] = { .pmin = 0, .pmax = 16000 },
+ [HSC025KG] = { .pmin = 0, .pmax = 25000 },
+ [HSC040KG] = { .pmin = 0, .pmax = 40000 },
+ [HSC060KG] = { .pmin = 0, .pmax = 60000 },
+ [HSC100KG] = { .pmin = 0, .pmax = 100000 },
+ [HSC160KG] = { .pmin = 0, .pmax = 160000 },
+ [HSC250KG] = { .pmin = 0, .pmax = 250000 },
+ [HSC400KG] = { .pmin = 0, .pmax = 400000 },
+ [HSC600KG] = { .pmin = 0, .pmax = 600000 },
+ [HSC001GG] = { .pmin = 0, .pmax = 1000000 },
+ [HSC015PA] = { .pmin = 0, .pmax = 103421 },
+ [HSC030PA] = { .pmin = 0, .pmax = 206843 },
+ [HSC060PA] = { .pmin = 0, .pmax = 413685 },
+ [HSC100PA] = { .pmin = 0, .pmax = 689476 },
+ [HSC150PA] = { .pmin = 0, .pmax = 1034214 },
+ [HSC0_5ND] = { .pmin = -125, .pmax = 125 },
+ [HSC001ND] = { .pmin = -249, .pmax = 249 },
+ [HSC002ND] = { .pmin = -498, .pmax = 498 },
+ [HSC004ND] = { .pmin = -996, .pmax = 996 },
+ [HSC005ND] = { .pmin = -1245, .pmax = 1245 },
+ [HSC010ND] = { .pmin = -2491, .pmax = 2491 },
+ [HSC020ND] = { .pmin = -4982, .pmax = 4982 },
+ [HSC030ND] = { .pmin = -7473, .pmax = 7473 },
+ [HSC001PD] = { .pmin = -6895, .pmax = 6895 },
+ [HSC005PD] = { .pmin = -34474, .pmax = 34474 },
+ [HSC015PD] = { .pmin = -103421, .pmax = 103421 },
+ [HSC030PD] = { .pmin = -206843, .pmax = 206843 },
+ [HSC060PD] = { .pmin = -413685, .pmax = 413685 },
+ [HSC001NG] = { .pmin = 0, .pmax = 249 },
+ [HSC002NG] = { .pmin = 0, .pmax = 498 },
+ [HSC004NG] = { .pmin = 0, .pmax = 996 },
+ [HSC005NG] = { .pmin = 0, .pmax = 1245 },
+ [HSC010NG] = { .pmin = 0, .pmax = 2491 },
+ [HSC020NG] = { .pmin = 0, .pmax = 4982 },
+ [HSC030NG] = { .pmin = 0, .pmax = 7473 },
+ [HSC001PG] = { .pmin = 0, .pmax = 6895 },
+ [HSC005PG] = { .pmin = 0, .pmax = 34474 },
+ [HSC015PG] = { .pmin = 0, .pmax = 103421 },
+ [HSC030PG] = { .pmin = 0, .pmax = 206843 },
+ [HSC060PG] = { .pmin = 0, .pmax = 413685 },
+ [HSC100PG] = { .pmin = 0, .pmax = 689476 },
+ [HSC150PG] = { .pmin = 0, .pmax = 1034214 },
+};
+
+/**
+ * hsc_measurement_is_valid() - validate last conversion via status bits
+ * @data: structure containing instantiated sensor data
+ * Return: true only if both status bits are zero
+ *
+ * The two MSB from the first transferred byte contain a status code
+ * 00 - normal operation, valid data
+ * 01 - device in factory programming mode
+ * 10 - stale data
+ * 11 - diagnostic condition
+ */
+static bool hsc_measurement_is_valid(struct hsc_data *data)
+{
+ return !(data->buffer[0] & HSC_STATUS_MASK);
+}
+
+static int hsc_get_measurement(struct hsc_data *data)
+{
+ const struct hsc_chip_data *chip = data->chip;
+ int ret;
+
+ ret = data->recv_cb(data);
+ if (ret < 0)
+ return ret;
+
+ data->is_valid = chip->valid(data);
+ if (!data->is_valid)
+ return -EAGAIN;
+
+ return 0;
+}
+
+static irqreturn_t hsc_trigger_handler(int irq, void *private)
+{
+ struct iio_poll_func *pf = private;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct hsc_data *data = iio_priv(indio_dev);
+ int ret;
+
+ ret = hsc_get_measurement(data);
+ if (ret)
+ goto error;
+
+ memcpy(&data->scan.chan[0], &data->buffer[0], 2);
+ memcpy(&data->scan.chan[1], &data->buffer[2], 2);
+
+ iio_push_to_buffers_with_ts(indio_dev, &data->scan, sizeof(data->scan),
+ iio_get_time_ns(indio_dev));
+
+error:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * IIO ABI expects
+ * value = (conv + offset) * scale
+ *
+ * datasheet provides the following formula for determining the temperature
+ * temp[C] = conv * a + b
+ * where a = 200/2047; b = -50
+ *
+ * temp[C] = (conv + (b/a)) * a * (1000)
+ * =>
+ * scale = a * 1000 = .097703957 * 1000 = 97.703957
+ * offset = b/a = -50 / .097703957 = -50000000 / 97704
+ *
+ * based on the datasheet
+ * pressure = (conv - Omin) * Q + Pmin =
+ * ((conv - Omin) + Pmin/Q) * Q
+ * =>
+ * scale = Q = (Pmax - Pmin) / (Omax - Omin)
+ * offset = Pmin/Q - Omin = Pmin * (Omax - Omin) / (Pmax - Pmin) - Omin
+ */
+static int hsc_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ struct hsc_data *data = iio_priv(indio_dev);
+ int ret;
+ u32 recvd;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = hsc_get_measurement(data);
+ if (ret)
+ return ret;
+
+ recvd = get_unaligned_be32(data->buffer);
+ switch (channel->type) {
+ case IIO_PRESSURE:
+ *val = FIELD_GET(HSC_PRESSURE_MASK, recvd);
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = FIELD_GET(HSC_TEMPERATURE_MASK, recvd);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (channel->type) {
+ case IIO_TEMP:
+ *val = 97;
+ *val2 = 703957;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_PRESSURE:
+ *val = data->p_scale;
+ *val2 = data->p_scale_dec;
+ return IIO_VAL_INT_PLUS_NANO;
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_OFFSET:
+ switch (channel->type) {
+ case IIO_TEMP:
+ *val = -50000000;
+ *val2 = 97704;
+ return IIO_VAL_FRACTIONAL;
+ case IIO_PRESSURE:
+ *val = data->p_offset;
+ *val2 = data->p_offset_dec;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec hsc_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 14,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 11,
+ .storagebits = 16,
+ .shift = 5,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const struct iio_info hsc_info = {
+ .read_raw = hsc_read_raw,
+};
+
+static const struct hsc_chip_data hsc_chip = {
+ .valid = hsc_measurement_is_valid,
+ .channels = hsc_channels,
+ .num_channels = ARRAY_SIZE(hsc_channels),
+};
+
+int hsc_common_probe(struct device *dev, hsc_recv_fn recv)
+{
+ struct hsc_data *hsc;
+ struct iio_dev *indio_dev;
+ const char *triplet;
+ s64 tmp;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*hsc));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ hsc = iio_priv(indio_dev);
+
+ hsc->chip = &hsc_chip;
+ hsc->recv_cb = recv;
+ hsc->dev = dev;
+
+ ret = device_property_read_u32(dev, "honeywell,transfer-function",
+ &hsc->function);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,transfer-function could not be read\n");
+ if (hsc->function > HSC_FUNCTION_F)
+ return dev_err_probe(dev, -EINVAL,
+ "honeywell,transfer-function %d invalid\n",
+ hsc->function);
+
+ ret = device_property_read_string(dev, "honeywell,pressure-triplet",
+ &triplet);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pressure-triplet could not be read\n");
+
+ if (str_has_prefix(triplet, "NA")) {
+ ret = device_property_read_u32(dev, "honeywell,pmin-pascal",
+ &hsc->pmin);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmin-pascal could not be read\n");
+
+ ret = device_property_read_u32(dev, "honeywell,pmax-pascal",
+ &hsc->pmax);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmax-pascal could not be read\n");
+ } else {
+ ret = device_property_match_property_string(dev,
+ "honeywell,pressure-triplet",
+ hsc_triplet_variants,
+ HSC_VARIANTS_MAX);
+ if (ret < 0)
+ return dev_err_probe(dev, -EINVAL,
+ "honeywell,pressure-triplet is invalid\n");
+
+ hsc->pmin = hsc_range_config[ret].pmin;
+ hsc->pmax = hsc_range_config[ret].pmax;
+ }
+
+ if (hsc->pmin >= hsc->pmax)
+ return dev_err_probe(dev, -EINVAL,
+ "pressure limits are invalid\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "can't get vdd supply\n");
+
+ hsc->outmin = hsc_func_spec[hsc->function].output_min;
+ hsc->outmax = hsc_func_spec[hsc->function].output_max;
+
+ tmp = div_s64(((s64)(hsc->pmax - hsc->pmin)) * MICRO,
+ hsc->outmax - hsc->outmin);
+ hsc->p_scale = div_s64_rem(tmp, NANO, &hsc->p_scale_dec);
+ tmp = div_s64(((s64)hsc->pmin * (s64)(hsc->outmax - hsc->outmin)) * MICRO,
+ hsc->pmax - hsc->pmin);
+ tmp -= (s64)hsc->outmin * MICRO;
+ hsc->p_offset = div_s64_rem(tmp, MICRO, &hsc->p_offset_dec);
+
+ indio_dev->name = "hsc030pa";
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &hsc_info;
+ indio_dev->channels = hsc->chip->channels;
+ indio_dev->num_channels = hsc->chip->num_channels;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ hsc_trigger_handler, NULL);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS(hsc_common_probe, "IIO_HONEYWELL_HSC030PA");
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell HSC and SSC pressure sensor core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/hsc030pa.h b/drivers/iio/pressure/hsc030pa.h
new file mode 100644
index 000000000..5db46784f
--- /dev/null
+++ b/drivers/iio/pressure/hsc030pa.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Honeywell TruStability HSC Series pressure/temperature sensor
+ *
+ * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro>
+ */
+
+#ifndef _HSC030PA_H
+#define _HSC030PA_H
+
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define HSC_REG_MEASUREMENT_RD_SIZE 4
+#define HSC_RESP_TIME_MS 2
+
+struct device;
+
+struct iio_chan_spec;
+struct iio_dev;
+
+struct hsc_data;
+struct hsc_chip_data;
+
+typedef int (*hsc_recv_fn)(struct hsc_data *);
+
+/**
+ * struct hsc_data
+ * @dev: current device structure
+ * @chip: structure containing chip's channel properties
+ * @recv_cb: function that implements the chip reads
+ * @is_valid: true if last transfer has been validated
+ * @pmin: minimum measurable pressure limit
+ * @pmax: maximum measurable pressure limit
+ * @outmin: minimum raw pressure in counts (based on transfer function)
+ * @outmax: maximum raw pressure in counts (based on transfer function)
+ * @function: transfer function
+ * @p_scale: pressure scale
+ * @p_scale_dec: pressure scale, decimal places
+ * @p_offset: pressure offset
+ * @p_offset_dec: pressure offset, decimal places
+ * @buffer: raw conversion data
+ */
+struct hsc_data {
+ struct device *dev;
+ const struct hsc_chip_data *chip;
+ hsc_recv_fn recv_cb;
+ bool is_valid;
+ s32 pmin;
+ s32 pmax;
+ u32 outmin;
+ u32 outmax;
+ u32 function;
+ s64 p_scale;
+ s32 p_scale_dec;
+ s64 p_offset;
+ s32 p_offset_dec;
+ struct {
+ __be16 chan[2];
+ aligned_s64 timestamp;
+ } scan;
+ u8 buffer[HSC_REG_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
+};
+
+struct hsc_chip_data {
+ bool (*valid)(struct hsc_data *data);
+ const struct iio_chan_spec *channels;
+ u8 num_channels;
+};
+
+enum hsc_func_id {
+ HSC_FUNCTION_A,
+ HSC_FUNCTION_B,
+ HSC_FUNCTION_C,
+ HSC_FUNCTION_F,
+};
+
+int hsc_common_probe(struct device *dev, hsc_recv_fn recv);
+
+#endif
diff --git a/drivers/iio/pressure/hsc030pa_i2c.c b/drivers/iio/pressure/hsc030pa_i2c.c
new file mode 100644
index 000000000..050cacd9e
--- /dev/null
+++ b/drivers/iio/pressure/hsc030pa_i2c.c
@@ -0,0 +1,78 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Honeywell TruStability HSC Series pressure/temperature sensor
+ *
+ * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro>
+ *
+ * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/common/documents/sps-siot-i2c-comms-digital-output-pressure-sensors-tn-008201-3-en-ciid-45841.pdf
+ * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/common/documents/sps-siot-sleep-mode-technical-note-008286-1-en-ciid-155793.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#include "hsc030pa.h"
+
+static int hsc_i2c_recv(struct hsc_data *data)
+{
+ struct i2c_client *client = to_i2c_client(data->dev);
+ struct i2c_msg msg;
+ int ret;
+
+ msleep_interruptible(HSC_RESP_TIME_MS);
+
+ msg.addr = client->addr;
+ msg.flags = client->flags | I2C_M_RD;
+ msg.len = HSC_REG_MEASUREMENT_RD_SIZE;
+ msg.buf = data->buffer;
+
+ ret = i2c_transfer(client->adapter, &msg, 1);
+ if (ret < 0)
+ return ret;
+
+ if (ret != 1)
+ return -EIO;
+
+ return 0;
+}
+
+static int hsc_i2c_probe(struct i2c_client *client)
+{
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
+ return -EOPNOTSUPP;
+
+ return hsc_common_probe(&client->dev, hsc_i2c_recv);
+}
+
+static const struct of_device_id hsc_i2c_match[] = {
+ { .compatible = "honeywell,hsc030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hsc_i2c_match);
+
+static const struct i2c_device_id hsc_i2c_id[] = {
+ { .name = "hsc030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, hsc_i2c_id);
+
+static struct i2c_driver hsc_i2c_driver = {
+ .driver = {
+ .name = "hsc030pa",
+ .of_match_table = hsc_i2c_match,
+ },
+ .probe = hsc_i2c_probe,
+ .id_table = hsc_i2c_id,
+};
+module_i2c_driver(hsc_i2c_driver);
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell HSC and SSC pressure sensor i2c driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_HSC030PA");
diff --git a/drivers/iio/pressure/hsc030pa_spi.c b/drivers/iio/pressure/hsc030pa_spi.c
new file mode 100644
index 000000000..02021b6f7
--- /dev/null
+++ b/drivers/iio/pressure/hsc030pa_spi.c
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Honeywell TruStability HSC Series pressure/temperature sensor
+ *
+ * Copyright (c) 2023 Petre Rodan <petre.rodan@subdimension.ro>
+ *
+ * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/common/documents/sps-siot-spi-comms-digital-ouptu-pressure-sensors-tn-008202-3-en-ciid-45843.pdf
+ * Datasheet: https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/common/documents/sps-siot-sleep-mode-technical-note-008286-1-en-ciid-155793.pdf
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#include "hsc030pa.h"
+
+static int hsc_spi_recv(struct hsc_data *data)
+{
+ struct spi_device *spi = to_spi_device(data->dev);
+
+ msleep_interruptible(HSC_RESP_TIME_MS);
+ return spi_read(spi, data->buffer, HSC_REG_MEASUREMENT_RD_SIZE);
+}
+
+static int hsc_spi_probe(struct spi_device *spi)
+{
+ return hsc_common_probe(&spi->dev, hsc_spi_recv);
+}
+
+static const struct of_device_id hsc_spi_match[] = {
+ { .compatible = "honeywell,hsc030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, hsc_spi_match);
+
+static const struct spi_device_id hsc_spi_id[] = {
+ { .name = "hsc030pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, hsc_spi_id);
+
+static struct spi_driver hsc_spi_driver = {
+ .driver = {
+ .name = "hsc030pa",
+ .of_match_table = hsc_spi_match,
+ },
+ .probe = hsc_spi_probe,
+ .id_table = hsc_spi_id,
+};
+module_spi_driver(hsc_spi_driver);
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell HSC and SSC pressure sensor spi driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_HSC030PA");
diff --git a/drivers/iio/pressure/icp10100.c b/drivers/iio/pressure/icp10100.c
new file mode 100644
index 000000000..82824e3e8
--- /dev/null
+++ b/drivers/iio/pressure/icp10100.c
@@ -0,0 +1,653 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020 InvenSense, Inc.
+ *
+ * Driver for InvenSense ICP-1010xx barometric pressure and temperature sensor.
+ *
+ * Datasheet:
+ * http://www.invensense.com/wp-content/uploads/2018/01/DS-000186-ICP-101xx-v1.2.pdf
+ */
+
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/pm_runtime.h>
+#include <linux/crc8.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/log2.h>
+#include <linux/math64.h>
+#include <linux/regulator/consumer.h>
+#include <linux/iio/iio.h>
+
+#define ICP10100_ID_REG_GET(_reg) ((_reg) & 0x003F)
+#define ICP10100_ID_REG 0x08
+#define ICP10100_RESPONSE_WORD_LENGTH 3
+#define ICP10100_CRC8_WORD_LENGTH 2
+#define ICP10100_CRC8_POLYNOMIAL 0x31
+#define ICP10100_CRC8_INIT 0xFF
+
+enum icp10100_mode {
+ ICP10100_MODE_LP, /* Low power mode: 1x sampling */
+ ICP10100_MODE_N, /* Normal mode: 2x sampling */
+ ICP10100_MODE_LN, /* Low noise mode: 4x sampling */
+ ICP10100_MODE_ULN, /* Ultra low noise mode: 8x sampling */
+ ICP10100_MODE_NB,
+};
+
+struct icp10100_state {
+ struct mutex lock;
+ struct i2c_client *client;
+ struct regulator *vdd;
+ enum icp10100_mode mode;
+ int16_t cal[4];
+};
+
+struct icp10100_command {
+ __be16 cmd;
+ unsigned long wait_us;
+ unsigned long wait_max_us;
+ size_t response_word_nb;
+};
+
+static const struct icp10100_command icp10100_cmd_soft_reset = {
+ .cmd = cpu_to_be16(0x805D),
+ .wait_us = 170,
+ .wait_max_us = 200,
+ .response_word_nb = 0,
+};
+
+static const struct icp10100_command icp10100_cmd_read_id = {
+ .cmd = cpu_to_be16(0xEFC8),
+ .wait_us = 0,
+ .response_word_nb = 1,
+};
+
+static const struct icp10100_command icp10100_cmd_read_otp = {
+ .cmd = cpu_to_be16(0xC7F7),
+ .wait_us = 0,
+ .response_word_nb = 1,
+};
+
+static const struct icp10100_command icp10100_cmd_measure[] = {
+ [ICP10100_MODE_LP] = {
+ .cmd = cpu_to_be16(0x401A),
+ .wait_us = 1800,
+ .wait_max_us = 2000,
+ .response_word_nb = 3,
+ },
+ [ICP10100_MODE_N] = {
+ .cmd = cpu_to_be16(0x48A3),
+ .wait_us = 6300,
+ .wait_max_us = 6500,
+ .response_word_nb = 3,
+ },
+ [ICP10100_MODE_LN] = {
+ .cmd = cpu_to_be16(0x5059),
+ .wait_us = 23800,
+ .wait_max_us = 24000,
+ .response_word_nb = 3,
+ },
+ [ICP10100_MODE_ULN] = {
+ .cmd = cpu_to_be16(0x58E0),
+ .wait_us = 94500,
+ .wait_max_us = 94700,
+ .response_word_nb = 3,
+ },
+};
+
+static const uint8_t icp10100_switch_mode_otp[] =
+ {0xC5, 0x95, 0x00, 0x66, 0x9c};
+
+DECLARE_CRC8_TABLE(icp10100_crc8_table);
+
+static inline int icp10100_i2c_xfer(struct i2c_adapter *adap,
+ struct i2c_msg *msgs, int num)
+{
+ int ret;
+
+ ret = i2c_transfer(adap, msgs, num);
+ if (ret < 0)
+ return ret;
+
+ if (ret != num)
+ return -EIO;
+
+ return 0;
+}
+
+static int icp10100_send_cmd(struct icp10100_state *st,
+ const struct icp10100_command *cmd,
+ __be16 *buf, size_t buf_len)
+{
+ size_t size = cmd->response_word_nb * ICP10100_RESPONSE_WORD_LENGTH;
+ uint8_t data[16];
+ uint8_t *ptr;
+ uint8_t *buf_ptr = (uint8_t *)buf;
+ struct i2c_msg msgs[2] = {
+ {
+ .addr = st->client->addr,
+ .flags = 0,
+ .len = 2,
+ .buf = (uint8_t *)&cmd->cmd,
+ }, {
+ .addr = st->client->addr,
+ .flags = I2C_M_RD,
+ .len = size,
+ .buf = data,
+ },
+ };
+ uint8_t crc;
+ unsigned int i;
+ int ret;
+
+ if (size > sizeof(data))
+ return -EINVAL;
+
+ if (cmd->response_word_nb > 0 &&
+ (buf == NULL || buf_len < (cmd->response_word_nb * 2)))
+ return -EINVAL;
+
+ dev_dbg(&st->client->dev, "sending cmd %#x\n", be16_to_cpu(cmd->cmd));
+
+ if (cmd->response_word_nb > 0 && cmd->wait_us == 0) {
+ /* direct command-response without waiting */
+ ret = icp10100_i2c_xfer(st->client->adapter, msgs,
+ ARRAY_SIZE(msgs));
+ if (ret)
+ return ret;
+ } else {
+ /* transfer command write */
+ ret = icp10100_i2c_xfer(st->client->adapter, &msgs[0], 1);
+ if (ret)
+ return ret;
+ if (cmd->wait_us > 0)
+ usleep_range(cmd->wait_us, cmd->wait_max_us);
+ /* transfer response read if needed */
+ if (cmd->response_word_nb > 0) {
+ ret = icp10100_i2c_xfer(st->client->adapter, &msgs[1], 1);
+ if (ret)
+ return ret;
+ } else {
+ return 0;
+ }
+ }
+
+ /* process read words with crc checking */
+ for (i = 0; i < cmd->response_word_nb; ++i) {
+ ptr = &data[i * ICP10100_RESPONSE_WORD_LENGTH];
+ crc = crc8(icp10100_crc8_table, ptr, ICP10100_CRC8_WORD_LENGTH,
+ ICP10100_CRC8_INIT);
+ if (crc != ptr[ICP10100_CRC8_WORD_LENGTH]) {
+ dev_err(&st->client->dev, "crc error recv=%#x calc=%#x\n",
+ ptr[ICP10100_CRC8_WORD_LENGTH], crc);
+ return -EIO;
+ }
+ *buf_ptr++ = ptr[0];
+ *buf_ptr++ = ptr[1];
+ }
+
+ return 0;
+}
+
+static int icp10100_read_cal_otp(struct icp10100_state *st)
+{
+ __be16 val;
+ int i;
+ int ret;
+
+ /* switch into OTP read mode */
+ ret = i2c_master_send(st->client, icp10100_switch_mode_otp,
+ ARRAY_SIZE(icp10100_switch_mode_otp));
+ if (ret < 0)
+ return ret;
+ if (ret != ARRAY_SIZE(icp10100_switch_mode_otp))
+ return -EIO;
+
+ /* read 4 calibration values */
+ for (i = 0; i < 4; ++i) {
+ ret = icp10100_send_cmd(st, &icp10100_cmd_read_otp,
+ &val, sizeof(val));
+ if (ret)
+ return ret;
+ st->cal[i] = be16_to_cpu(val);
+ dev_dbg(&st->client->dev, "cal[%d] = %d\n", i, st->cal[i]);
+ }
+
+ return 0;
+}
+
+static int icp10100_init_chip(struct icp10100_state *st)
+{
+ __be16 val;
+ uint16_t id;
+ int ret;
+
+ /* read and check id */
+ ret = icp10100_send_cmd(st, &icp10100_cmd_read_id, &val, sizeof(val));
+ if (ret)
+ return ret;
+ id = ICP10100_ID_REG_GET(be16_to_cpu(val));
+ if (id != ICP10100_ID_REG) {
+ dev_err(&st->client->dev, "invalid id %#x\n", id);
+ return -ENODEV;
+ }
+
+ /* read calibration data from OTP */
+ ret = icp10100_read_cal_otp(st);
+ if (ret)
+ return ret;
+
+ /* reset chip */
+ return icp10100_send_cmd(st, &icp10100_cmd_soft_reset, NULL, 0);
+}
+
+static int icp10100_get_measures(struct icp10100_state *st,
+ uint32_t *pressure, uint16_t *temperature)
+{
+ const struct icp10100_command *cmd;
+ __be16 measures[3];
+ int ret;
+
+ ret = pm_runtime_resume_and_get(&st->client->dev);
+ if (ret < 0)
+ return ret;
+
+ mutex_lock(&st->lock);
+ cmd = &icp10100_cmd_measure[st->mode];
+ ret = icp10100_send_cmd(st, cmd, measures, sizeof(measures));
+ mutex_unlock(&st->lock);
+ if (ret)
+ goto error_measure;
+
+ *pressure = (be16_to_cpu(measures[0]) << 8) |
+ (be16_to_cpu(measures[1]) >> 8);
+ *temperature = be16_to_cpu(measures[2]);
+
+error_measure:
+ pm_runtime_put_autosuspend(&st->client->dev);
+ return ret;
+}
+
+static uint32_t icp10100_get_pressure(struct icp10100_state *st,
+ uint32_t raw_pressure, uint16_t raw_temp)
+{
+ static int32_t p_calib[] = {45000, 80000, 105000};
+ static int32_t lut_lower = 3670016;
+ static int32_t lut_upper = 12058624;
+ static int32_t inv_quadr_factor = 16777216;
+ static int32_t offset_factor = 2048;
+ int64_t val1, val2;
+ int32_t p_lut[3];
+ int32_t t, t_square;
+ int64_t a, b, c;
+ uint32_t pressure_mPa;
+
+ dev_dbg(&st->client->dev, "raw: pressure = %u, temp = %u\n",
+ raw_pressure, raw_temp);
+
+ /* compute p_lut values */
+ t = (int32_t)raw_temp - 32768;
+ t_square = t * t;
+ val1 = (int64_t)st->cal[0] * (int64_t)t_square;
+ p_lut[0] = lut_lower + (int32_t)div_s64(val1, inv_quadr_factor);
+ val1 = (int64_t)st->cal[1] * (int64_t)t_square;
+ p_lut[1] = offset_factor * st->cal[3] +
+ (int32_t)div_s64(val1, inv_quadr_factor);
+ val1 = (int64_t)st->cal[2] * (int64_t)t_square;
+ p_lut[2] = lut_upper + (int32_t)div_s64(val1, inv_quadr_factor);
+ dev_dbg(&st->client->dev, "p_lut = [%d, %d, %d]\n",
+ p_lut[0], p_lut[1], p_lut[2]);
+
+ /* compute a, b, c factors */
+ val1 = (int64_t)p_lut[0] * (int64_t)p_lut[1] *
+ (int64_t)(p_calib[0] - p_calib[1]) +
+ (int64_t)p_lut[1] * (int64_t)p_lut[2] *
+ (int64_t)(p_calib[1] - p_calib[2]) +
+ (int64_t)p_lut[2] * (int64_t)p_lut[0] *
+ (int64_t)(p_calib[2] - p_calib[0]);
+ val2 = (int64_t)p_lut[2] * (int64_t)(p_calib[0] - p_calib[1]) +
+ (int64_t)p_lut[0] * (int64_t)(p_calib[1] - p_calib[2]) +
+ (int64_t)p_lut[1] * (int64_t)(p_calib[2] - p_calib[0]);
+ c = div64_s64(val1, val2);
+ dev_dbg(&st->client->dev, "val1 = %lld, val2 = %lld, c = %lld\n",
+ val1, val2, c);
+ val1 = (int64_t)p_calib[0] * (int64_t)p_lut[0] -
+ (int64_t)p_calib[1] * (int64_t)p_lut[1] -
+ (int64_t)(p_calib[1] - p_calib[0]) * c;
+ val2 = (int64_t)p_lut[0] - (int64_t)p_lut[1];
+ a = div64_s64(val1, val2);
+ dev_dbg(&st->client->dev, "val1 = %lld, val2 = %lld, a = %lld\n",
+ val1, val2, a);
+ b = ((int64_t)p_calib[0] - a) * ((int64_t)p_lut[0] + c);
+ dev_dbg(&st->client->dev, "b = %lld\n", b);
+
+ /*
+ * pressure_Pa = a + (b / (c + raw_pressure))
+ * pressure_mPa = 1000 * pressure_Pa
+ */
+ pressure_mPa = 1000LL * a + div64_s64(1000LL * b, c + raw_pressure);
+
+ return pressure_mPa;
+}
+
+static int icp10100_read_raw_measures(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2)
+{
+ struct icp10100_state *st = iio_priv(indio_dev);
+ uint32_t raw_pressure;
+ uint16_t raw_temp;
+ uint32_t pressure_mPa;
+ int ret;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = icp10100_get_measures(st, &raw_pressure, &raw_temp);
+ if (ret)
+ goto error_release;
+
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ pressure_mPa = icp10100_get_pressure(st, raw_pressure,
+ raw_temp);
+ /* mPa to kPa */
+ *val = pressure_mPa / 1000000;
+ *val2 = pressure_mPa % 1000000;
+ ret = IIO_VAL_INT_PLUS_MICRO;
+ break;
+ case IIO_TEMP:
+ *val = raw_temp;
+ ret = IIO_VAL_INT;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+error_release:
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int icp10100_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct icp10100_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ case IIO_CHAN_INFO_PROCESSED:
+ return icp10100_read_raw_measures(indio_dev, chan, val, val2);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_TEMP:
+ /* 1000 * 175°C / 65536 in m°C */
+ *val = 2;
+ *val2 = 670288;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ /* 1000 * -45°C in m°C */
+ *val = -45000;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ break;
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ mutex_lock(&st->lock);
+ *val = 1 << st->mode;
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int icp10100_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ static int oversamplings[] = {1, 2, 4, 8};
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ *vals = oversamplings;
+ *type = IIO_VAL_INT;
+ *length = ARRAY_SIZE(oversamplings);
+ return IIO_AVAIL_LIST;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int icp10100_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct icp10100_state *st = iio_priv(indio_dev);
+ unsigned int mode;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ /* oversampling is always positive and a power of 2 */
+ if (val <= 0 || !is_power_of_2(val))
+ return -EINVAL;
+ mode = ilog2(val);
+ if (mode >= ICP10100_MODE_NB)
+ return -EINVAL;
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+ mutex_lock(&st->lock);
+ st->mode = mode;
+ mutex_unlock(&st->lock);
+ iio_device_release_direct(indio_dev);
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int icp10100_write_raw_get_fmt(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info icp10100_info = {
+ .read_raw = icp10100_read_raw,
+ .read_avail = icp10100_read_avail,
+ .write_raw = icp10100_write_raw,
+ .write_raw_get_fmt = icp10100_write_raw_get_fmt,
+};
+
+static const struct iio_chan_spec icp10100_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_all =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ }, {
+ .type = IIO_TEMP,
+ .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_OVERSAMPLING_RATIO),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ },
+};
+
+static int icp10100_enable_regulator(struct icp10100_state *st)
+{
+ int ret;
+
+ ret = regulator_enable(st->vdd);
+ if (ret)
+ return ret;
+ msleep(100);
+
+ return 0;
+}
+
+static void icp10100_disable_regulator_action(void *data)
+{
+ struct icp10100_state *st = data;
+ int ret;
+
+ ret = regulator_disable(st->vdd);
+ if (ret)
+ dev_err(&st->client->dev, "error %d disabling vdd\n", ret);
+}
+
+static void icp10100_pm_disable(void *data)
+{
+ struct device *dev = data;
+
+ pm_runtime_disable(dev);
+}
+
+static int icp10100_probe(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev;
+ struct icp10100_state *st;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+ dev_err(&client->dev, "plain i2c transactions not supported\n");
+ return -ENODEV;
+ }
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->name = client->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = icp10100_channels;
+ indio_dev->num_channels = ARRAY_SIZE(icp10100_channels);
+ indio_dev->info = &icp10100_info;
+
+ st = iio_priv(indio_dev);
+ mutex_init(&st->lock);
+ st->client = client;
+ st->mode = ICP10100_MODE_N;
+
+ st->vdd = devm_regulator_get(&client->dev, "vdd");
+ if (IS_ERR(st->vdd))
+ return PTR_ERR(st->vdd);
+
+ ret = icp10100_enable_regulator(st);
+ if (ret)
+ return ret;
+
+ ret = devm_add_action_or_reset(&client->dev,
+ icp10100_disable_regulator_action, st);
+ if (ret)
+ return ret;
+
+ /* has to be done before the first i2c communication */
+ crc8_populate_msb(icp10100_crc8_table, ICP10100_CRC8_POLYNOMIAL);
+
+ ret = icp10100_init_chip(st);
+ if (ret) {
+ dev_err(&client->dev, "init chip error %d\n", ret);
+ return ret;
+ }
+
+ /* enable runtime pm with autosuspend delay of 2s */
+ pm_runtime_get_noresume(&client->dev);
+ pm_runtime_set_active(&client->dev);
+ pm_runtime_enable(&client->dev);
+ pm_runtime_set_autosuspend_delay(&client->dev, 2000);
+ pm_runtime_use_autosuspend(&client->dev);
+ pm_runtime_put(&client->dev);
+ ret = devm_add_action_or_reset(&client->dev, icp10100_pm_disable,
+ &client->dev);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static int icp10100_suspend(struct device *dev)
+{
+ struct icp10100_state *st = iio_priv(dev_get_drvdata(dev));
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = regulator_disable(st->vdd);
+ mutex_unlock(&st->lock);
+
+ return ret;
+}
+
+static int icp10100_resume(struct device *dev)
+{
+ struct icp10100_state *st = iio_priv(dev_get_drvdata(dev));
+ int ret;
+
+ mutex_lock(&st->lock);
+
+ ret = icp10100_enable_regulator(st);
+ if (ret)
+ goto out_unlock;
+
+ /* reset chip */
+ ret = icp10100_send_cmd(st, &icp10100_cmd_soft_reset, NULL, 0);
+
+out_unlock:
+ mutex_unlock(&st->lock);
+ return ret;
+}
+
+static DEFINE_RUNTIME_DEV_PM_OPS(icp10100_pm, icp10100_suspend, icp10100_resume,
+ NULL);
+
+static const struct of_device_id icp10100_of_match[] = {
+ {
+ .compatible = "invensense,icp10100",
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, icp10100_of_match);
+
+static const struct i2c_device_id icp10100_id[] = {
+ { .name = "icp10100" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, icp10100_id);
+
+static struct i2c_driver icp10100_driver = {
+ .driver = {
+ .name = "icp10100",
+ .pm = pm_ptr(&icp10100_pm),
+ .of_match_table = icp10100_of_match,
+ },
+ .probe = icp10100_probe,
+ .id_table = icp10100_id,
+};
+module_i2c_driver(icp10100_driver);
+
+MODULE_AUTHOR("InvenSense, Inc.");
+MODULE_DESCRIPTION("InvenSense icp10100 driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/mpl115.c b/drivers/iio/pressure/mpl115.c
new file mode 100644
index 000000000..2929ea8e4
--- /dev/null
+++ b/drivers/iio/pressure/mpl115.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mpl115.c - Support for Freescale MPL115A pressure/temperature sensor
+ *
+ * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * TODO: synchronization with system suspend
+ */
+
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+
+#include "mpl115.h"
+
+#define MPL115_PADC 0x00 /* pressure ADC output value, MSB first, 10 bit */
+#define MPL115_TADC 0x02 /* temperature ADC output value, MSB first, 10 bit */
+#define MPL115_A0 0x04 /* 12 bit integer, 3 bit fraction */
+#define MPL115_B1 0x06 /* 2 bit integer, 13 bit fraction */
+#define MPL115_B2 0x08 /* 1 bit integer, 14 bit fraction */
+#define MPL115_C12 0x0a /* 0 bit integer, 13 bit fraction */
+#define MPL115_CONVERT 0x12 /* convert temperature and pressure */
+
+struct mpl115_data {
+ struct device *dev;
+ struct mutex lock;
+ s16 a0;
+ s16 b1, b2;
+ s16 c12;
+ struct gpio_desc *shutdown;
+ const struct mpl115_ops *ops;
+};
+
+static int mpl115_request(struct mpl115_data *data)
+{
+ int ret = data->ops->write(data->dev, MPL115_CONVERT, 0);
+
+ if (ret < 0)
+ return ret;
+
+ usleep_range(3000, 4000);
+
+ return 0;
+}
+
+static int mpl115_comp_pressure(struct mpl115_data *data, int *val, int *val2)
+{
+ int ret;
+ u16 padc, tadc;
+ int a1, y1, pcomp;
+ unsigned kpa;
+
+ mutex_lock(&data->lock);
+ ret = mpl115_request(data);
+ if (ret < 0)
+ goto done;
+
+ ret = data->ops->read(data->dev, MPL115_PADC);
+ if (ret < 0)
+ goto done;
+ padc = ret >> 6;
+
+ ret = data->ops->read(data->dev, MPL115_TADC);
+ if (ret < 0)
+ goto done;
+ tadc = ret >> 6;
+
+ /* see Freescale AN3785 */
+ a1 = data->b1 + ((data->c12 * tadc) >> 11);
+ y1 = (data->a0 << 10) + a1 * padc;
+
+ /* compensated pressure with 4 fractional bits */
+ pcomp = (y1 + ((data->b2 * (int) tadc) >> 1)) >> 9;
+
+ kpa = pcomp * (115 - 50) / 1023 + (50 << 4);
+ *val = kpa >> 4;
+ *val2 = (kpa & 15) * (1000000 >> 4);
+done:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int mpl115_read_temp(struct mpl115_data *data)
+{
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = mpl115_request(data);
+ if (ret < 0)
+ goto done;
+ ret = data->ops->read(data->dev, MPL115_TADC);
+done:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int mpl115_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mpl115_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ pm_runtime_get_sync(data->dev);
+ ret = mpl115_comp_pressure(data, val, val2);
+ pm_runtime_put_autosuspend(data->dev);
+ if (ret < 0)
+ return ret;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_RAW:
+ pm_runtime_get_sync(data->dev);
+ /* temperature -5.35 C / LSB, 472 LSB is 25 C */
+ ret = mpl115_read_temp(data);
+ pm_runtime_put_autosuspend(data->dev);
+ if (ret < 0)
+ return ret;
+ *val = ret >> 6;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = -605;
+ *val2 = 750000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_CHAN_INFO_SCALE:
+ *val = -186;
+ *val2 = 915888;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+}
+
+static const struct iio_chan_spec mpl115_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type =
+ BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static const struct iio_info mpl115_info = {
+ .read_raw = &mpl115_read_raw,
+};
+
+int mpl115_probe(struct device *dev, const char *name,
+ const struct mpl115_ops *ops)
+{
+ struct mpl115_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->dev = dev;
+ data->ops = ops;
+ mutex_init(&data->lock);
+
+ indio_dev->info = &mpl115_info;
+ indio_dev->name = name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mpl115_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mpl115_channels);
+
+ ret = data->ops->init(data->dev);
+ if (ret)
+ return ret;
+
+ dev_set_drvdata(dev, indio_dev);
+
+ ret = data->ops->read(data->dev, MPL115_A0);
+ if (ret < 0)
+ return ret;
+ data->a0 = ret;
+ ret = data->ops->read(data->dev, MPL115_B1);
+ if (ret < 0)
+ return ret;
+ data->b1 = ret;
+ ret = data->ops->read(data->dev, MPL115_B2);
+ if (ret < 0)
+ return ret;
+ data->b2 = ret;
+ ret = data->ops->read(data->dev, MPL115_C12);
+ if (ret < 0)
+ return ret;
+ data->c12 = ret;
+
+ data->shutdown = devm_gpiod_get_optional(dev, "shutdown",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(data->shutdown))
+ return dev_err_probe(dev, PTR_ERR(data->shutdown),
+ "cannot get shutdown gpio\n");
+
+ if (data->shutdown) {
+ /* Enable runtime PM */
+ ret = pm_runtime_set_active(dev);
+ if (ret)
+ return ret;
+
+ /*
+ * As the device takes 3 ms to come up with a fresh
+ * reading after power-on and 5 ms to actually power-on,
+ * do not shut it down unnecessarily. Set autosuspend to
+ * 2000 ms.
+ */
+ pm_runtime_set_autosuspend_delay(dev, 2000);
+ pm_runtime_use_autosuspend(dev);
+
+ ret = devm_pm_runtime_enable(dev);
+ if (ret)
+ return ret;
+
+ dev_dbg(dev, "low-power mode enabled");
+ } else
+ dev_dbg(dev, "low-power mode disabled");
+
+ return devm_iio_device_register(dev, indio_dev);
+}
+EXPORT_SYMBOL_NS_GPL(mpl115_probe, "IIO_MPL115");
+
+static int mpl115_runtime_suspend(struct device *dev)
+{
+ struct mpl115_data *data = iio_priv(dev_get_drvdata(dev));
+
+ gpiod_set_value(data->shutdown, 1);
+
+ return 0;
+}
+
+static int mpl115_runtime_resume(struct device *dev)
+{
+ struct mpl115_data *data = iio_priv(dev_get_drvdata(dev));
+
+ gpiod_set_value(data->shutdown, 0);
+ usleep_range(5000, 6000);
+
+ return 0;
+}
+
+EXPORT_NS_RUNTIME_DEV_PM_OPS(mpl115_dev_pm_ops, mpl115_runtime_suspend,
+ mpl115_runtime_resume, NULL, IIO_MPL115);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("Freescale MPL115 pressure/temperature driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/mpl115.h b/drivers/iio/pressure/mpl115.h
new file mode 100644
index 000000000..78a0068a1
--- /dev/null
+++ b/drivers/iio/pressure/mpl115.h
@@ -0,0 +1,26 @@
+/* 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>
+ */
+
+#include <linux/pm_runtime.h>
+
+#ifndef _MPL115_H_
+#define _MPL115_H_
+
+struct mpl115_ops {
+ int (*init)(struct device *);
+ int (*read)(struct device *, u8);
+ int (*write)(struct device *, u8, u8);
+};
+
+int mpl115_probe(struct device *dev, const char *name,
+ const struct mpl115_ops *ops);
+
+/*PM ops */
+extern const struct dev_pm_ops mpl115_dev_pm_ops;
+
+#endif
diff --git a/drivers/iio/pressure/mpl115_i2c.c b/drivers/iio/pressure/mpl115_i2c.c
new file mode 100644
index 000000000..4a43eba07
--- /dev/null
+++ b/drivers/iio/pressure/mpl115_i2c.c
@@ -0,0 +1,66 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Freescale MPL115A2 pressure/temperature sensor
+ *
+ * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * (7-bit I2C slave address 0x60)
+ *
+ * Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A2.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+
+#include "mpl115.h"
+
+static int mpl115_i2c_init(struct device *dev)
+{
+ return 0;
+}
+
+static int mpl115_i2c_read(struct device *dev, u8 address)
+{
+ return i2c_smbus_read_word_swapped(to_i2c_client(dev), address);
+}
+
+static int mpl115_i2c_write(struct device *dev, u8 address, u8 value)
+{
+ return i2c_smbus_write_byte_data(to_i2c_client(dev), address, value);
+}
+
+static const struct mpl115_ops mpl115_i2c_ops = {
+ .init = mpl115_i2c_init,
+ .read = mpl115_i2c_read,
+ .write = mpl115_i2c_write,
+};
+
+static int mpl115_i2c_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
+ return -EOPNOTSUPP;
+
+ return mpl115_probe(&client->dev, id->name, &mpl115_i2c_ops);
+}
+
+static const struct i2c_device_id mpl115_i2c_id[] = {
+ { .name = "mpl115" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mpl115_i2c_id);
+
+static struct i2c_driver mpl115_i2c_driver = {
+ .driver = {
+ .name = "mpl115",
+ .pm = pm_ptr(&mpl115_dev_pm_ops),
+ },
+ .probe = mpl115_i2c_probe,
+ .id_table = mpl115_i2c_id,
+};
+module_i2c_driver(mpl115_i2c_driver);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("Freescale MPL115A2 pressure/temperature driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_MPL115");
diff --git a/drivers/iio/pressure/mpl115_spi.c b/drivers/iio/pressure/mpl115_spi.c
new file mode 100644
index 000000000..3cff616e8
--- /dev/null
+++ b/drivers/iio/pressure/mpl115_spi.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Freescale MPL115A1 pressure/temperature sensor
+ *
+ * Copyright (c) 2016 Akinobu Mita <akinobu.mita@gmail.com>
+ *
+ * Datasheet: http://www.nxp.com/files/sensors/doc/data_sheet/MPL115A1.pdf
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include "mpl115.h"
+
+#define MPL115_SPI_WRITE(address) ((address) << 1)
+#define MPL115_SPI_READ(address) (0x80 | (address) << 1)
+
+struct mpl115_spi_buf {
+ u8 tx[4];
+ u8 rx[4];
+};
+
+static int mpl115_spi_init(struct device *dev)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct mpl115_spi_buf *buf;
+
+ buf = devm_kzalloc(dev, sizeof(*buf), GFP_KERNEL);
+ if (!buf)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, buf);
+
+ return 0;
+}
+
+static int mpl115_spi_read(struct device *dev, u8 address)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct mpl115_spi_buf *buf = spi_get_drvdata(spi);
+ struct spi_transfer xfer = {
+ .tx_buf = buf->tx,
+ .rx_buf = buf->rx,
+ .len = 4,
+ };
+ int ret;
+
+ buf->tx[0] = MPL115_SPI_READ(address);
+ buf->tx[2] = MPL115_SPI_READ(address + 1);
+
+ ret = spi_sync_transfer(spi, &xfer, 1);
+ if (ret)
+ return ret;
+
+ return (buf->rx[1] << 8) | buf->rx[3];
+}
+
+static int mpl115_spi_write(struct device *dev, u8 address, u8 value)
+{
+ struct spi_device *spi = to_spi_device(dev);
+ struct mpl115_spi_buf *buf = spi_get_drvdata(spi);
+ struct spi_transfer xfer = {
+ .tx_buf = buf->tx,
+ .len = 2,
+ };
+
+ buf->tx[0] = MPL115_SPI_WRITE(address);
+ buf->tx[1] = value;
+
+ return spi_sync_transfer(spi, &xfer, 1);
+}
+
+static const struct mpl115_ops mpl115_spi_ops = {
+ .init = mpl115_spi_init,
+ .read = mpl115_spi_read,
+ .write = mpl115_spi_write,
+};
+
+static int mpl115_spi_probe(struct spi_device *spi)
+{
+ const struct spi_device_id *id = spi_get_device_id(spi);
+
+ return mpl115_probe(&spi->dev, id->name, &mpl115_spi_ops);
+}
+
+static const struct spi_device_id mpl115_spi_ids[] = {
+ { .name = "mpl115" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mpl115_spi_ids);
+
+static struct spi_driver mpl115_spi_driver = {
+ .driver = {
+ .name = "mpl115",
+ .pm = pm_ptr(&mpl115_dev_pm_ops),
+ },
+ .probe = mpl115_spi_probe,
+ .id_table = mpl115_spi_ids,
+};
+module_spi_driver(mpl115_spi_driver);
+
+MODULE_AUTHOR("Akinobu Mita <akinobu.mita@gmail.com>");
+MODULE_DESCRIPTION("Freescale MPL115A1 pressure/temperature driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_MPL115");
diff --git a/drivers/iio/pressure/mpl3115.c b/drivers/iio/pressure/mpl3115.c
new file mode 100644
index 000000000..f20fa6ad1
--- /dev/null
+++ b/drivers/iio/pressure/mpl3115.c
@@ -0,0 +1,811 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * mpl3115.c - Support for Freescale MPL3115A2 pressure/temperature sensor
+ *
+ * Copyright (c) 2013 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * (7-bit I2C slave address 0x60)
+ *
+ * TODO: FIFO buffer, altimeter mode, oversampling, continuous mode,
+ * user offset correction, raw mode
+ */
+
+#include <linux/bitfield.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/limits.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/unaligned.h>
+
+#include <linux/iio/buffer.h>
+#include <linux/iio/events.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/trigger.h>
+
+#define MPL3115_STATUS 0x00
+#define MPL3115_OUT_PRESS 0x01 /* MSB first, 20 bit */
+#define MPL3115_OUT_TEMP 0x04 /* MSB first, 12 bit */
+#define MPL3115_WHO_AM_I 0x0c
+#define MPL3115_INT_SOURCE 0x12
+#define MPL3115_PT_DATA_CFG 0x13
+#define MPL3115_PRESS_TGT 0x16 /* MSB first, 16 bit */
+#define MPL3115_TEMP_TGT 0x18
+#define MPL3115_CTRL_REG1 0x26
+#define MPL3115_CTRL_REG2 0x27
+#define MPL3115_CTRL_REG3 0x28
+#define MPL3115_CTRL_REG4 0x29
+#define MPL3115_CTRL_REG5 0x2a
+
+#define MPL3115_DEVICE_ID 0xc4
+
+#define MPL3115_STATUS_PRESS_RDY BIT(2)
+#define MPL3115_STATUS_TEMP_RDY BIT(1)
+
+#define MPL3115_INT_SRC_DRDY BIT(7)
+#define MPL3115_INT_SRC_PTH BIT(3)
+#define MPL3115_INT_SRC_TTH BIT(2)
+
+#define MPL3115_PT_DATA_EVENT_ALL GENMASK(2, 0)
+
+#define MPL3115_CTRL1_RESET BIT(2) /* software reset */
+#define MPL3115_CTRL1_OST BIT(1) /* initiate measurement */
+#define MPL3115_CTRL1_ACTIVE BIT(0) /* continuous measurement */
+#define MPL3115_CTRL1_OS_258MS GENMASK(5, 4) /* 64x oversampling */
+
+#define MPL3115_CTRL2_ST GENMASK(3, 0)
+
+#define MPL3115_CTRL3_IPOL1 BIT(5)
+#define MPL3115_CTRL3_IPOL2 BIT(1)
+
+#define MPL3115_CTRL4_INT_EN_DRDY BIT(7)
+#define MPL3115_CTRL4_INT_EN_PTH BIT(3)
+#define MPL3115_CTRL4_INT_EN_TTH BIT(2)
+
+#define MPL3115_CTRL5_INT_CFG_DRDY BIT(7)
+
+static const unsigned int mpl3115_samp_freq_table[][2] = {
+ { 1, 0 },
+ { 0, 500000 },
+ { 0, 250000 },
+ { 0, 125000 },
+ { 0, 62500 },
+ { 0, 31250 },
+ { 0, 15625 },
+ { 0, 7812 },
+ { 0, 3906 },
+ { 0, 1953 },
+ { 0, 976 },
+ { 0, 488 },
+ { 0, 244 },
+ { 0, 122 },
+ { 0, 61 },
+ { 0, 30 },
+};
+
+struct mpl3115_data {
+ struct i2c_client *client;
+ struct iio_trigger *drdy_trig;
+ struct mutex lock;
+ u8 ctrl_reg1;
+ u8 ctrl_reg4;
+};
+
+enum mpl3115_irq_pin {
+ MPL3115_IRQ_INT1,
+ MPL3115_IRQ_INT2,
+};
+
+static int mpl3115_request(struct mpl3115_data *data)
+{
+ int ret, tries = 15;
+
+ /* trigger measurement */
+ ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
+ data->ctrl_reg1 | MPL3115_CTRL1_OST);
+ if (ret < 0)
+ return ret;
+
+ while (tries-- > 0) {
+ ret = i2c_smbus_read_byte_data(data->client, MPL3115_CTRL_REG1);
+ if (ret < 0)
+ return ret;
+ /* wait for data ready, i.e. OST cleared */
+ if (!(ret & MPL3115_CTRL1_OST))
+ break;
+ msleep(20);
+ }
+
+ if (tries < 0) {
+ dev_err(&data->client->dev, "data not ready\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int mpl3115_read_info_raw(struct mpl3115_data *data,
+ struct iio_chan_spec const *chan, int *val)
+{
+ int ret;
+
+ switch (chan->type) {
+ case IIO_PRESSURE: { /* in 0.25 pascal / LSB */
+ u8 press_be24[3];
+
+ guard(mutex)(&data->lock);
+ ret = mpl3115_request(data);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ MPL3115_OUT_PRESS,
+ sizeof(press_be24),
+ press_be24);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The pressure channel shift is applied in the case where the
+ * data (24-bit big endian) is read into a 32-bit buffer. Here
+ * the data is stored in a 24-bit buffer, so the shift is 4.
+ */
+ *val = get_unaligned_be24(press_be24) >> 4;
+ return IIO_VAL_INT;
+ }
+ case IIO_TEMP: { /* in 0.0625 celsius / LSB */
+ __be16 tmp;
+
+ guard(mutex)(&data->lock);
+ ret = mpl3115_request(data);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ MPL3115_OUT_TEMP,
+ 2, (u8 *) &tmp);
+ if (ret < 0)
+ return ret;
+
+ *val = sign_extend32(be16_to_cpu(tmp) >> chan->scan_type.shift,
+ chan->scan_type.realbits - 1);
+ return IIO_VAL_INT;
+ }
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mpl3115_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct mpl3115_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = mpl3115_read_info_raw(data, chan, val);
+ iio_device_release_direct(indio_dev);
+ return ret;
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ *val = 0;
+ *val2 = 250; /* want kilopascal */
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ *val = 0;
+ *val2 = 62500;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ ret = i2c_smbus_read_byte_data(data->client, MPL3115_CTRL_REG2);
+ if (ret < 0)
+ return ret;
+
+ ret = FIELD_GET(MPL3115_CTRL2_ST, ret);
+
+ *val = mpl3115_samp_freq_table[ret][0];
+ *val2 = mpl3115_samp_freq_table[ret][1];
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+ return -EINVAL;
+}
+
+static int mpl3115_read_avail(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ const int **vals, int *type, int *length,
+ long mask)
+{
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ *type = IIO_VAL_INT_PLUS_MICRO;
+ *length = ARRAY_SIZE(mpl3115_samp_freq_table) * 2;
+ *vals = (int *)mpl3115_samp_freq_table;
+ return IIO_AVAIL_LIST;
+}
+
+static int mpl3115_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val, int val2, long mask)
+{
+ struct mpl3115_data *data = iio_priv(indio_dev);
+ int i, ret;
+
+ if (mask != IIO_CHAN_INFO_SAMP_FREQ)
+ return -EINVAL;
+
+ for (i = 0; i < ARRAY_SIZE(mpl3115_samp_freq_table); i++)
+ if (val == mpl3115_samp_freq_table[i][0] &&
+ val2 == mpl3115_samp_freq_table[i][1])
+ break;
+
+ if (i == ARRAY_SIZE(mpl3115_samp_freq_table))
+ return -EINVAL;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG2,
+ FIELD_PREP(MPL3115_CTRL2_ST, i));
+ iio_device_release_direct(indio_dev);
+ return ret;
+}
+
+static int mpl3115_fill_trig_buffer(struct iio_dev *indio_dev, u8 *buffer)
+{
+ struct mpl3115_data *data = iio_priv(indio_dev);
+ int ret, pos = 0;
+
+ if (!(data->ctrl_reg1 & MPL3115_CTRL1_ACTIVE)) {
+ ret = mpl3115_request(data);
+ if (ret < 0)
+ return ret;
+ }
+
+ if (test_bit(0, indio_dev->active_scan_mask)) {
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ MPL3115_OUT_PRESS, 3, &buffer[pos]);
+ if (ret < 0)
+ return ret;
+ pos += 4;
+ }
+
+ if (test_bit(1, indio_dev->active_scan_mask)) {
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ MPL3115_OUT_TEMP, 2, &buffer[pos]);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static irqreturn_t mpl3115_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mpl3115_data *data = iio_priv(indio_dev);
+ /*
+ * 32-bit channel + 16-bit channel + padding + ts
+ * Note that it is possible for only one of the first 2
+ * channels to be enabled. If that happens, the first element
+ * of the buffer may be either 16 or 32-bits. As such we cannot
+ * use a simple structure definition to express this data layout.
+ */
+ u8 buffer[16] __aligned(8) = { };
+ int ret;
+
+ mutex_lock(&data->lock);
+ ret = mpl3115_fill_trig_buffer(indio_dev, buffer);
+ mutex_unlock(&data->lock);
+ if (ret)
+ goto done;
+
+ iio_push_to_buffers_with_ts(indio_dev, buffer, sizeof(buffer),
+ iio_get_time_ns(indio_dev));
+
+done:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}
+
+static const struct iio_event_spec mpl3115_temp_press_event[] = {
+ {
+ .type = IIO_EV_TYPE_THRESH,
+ .dir = IIO_EV_DIR_RISING,
+ .mask_separate = BIT(IIO_EV_INFO_ENABLE) |
+ BIT(IIO_EV_INFO_VALUE),
+ },
+};
+
+static const struct iio_chan_spec mpl3115_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .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),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 20,
+ .storagebits = 32,
+ .shift = 12,
+ .endianness = IIO_BE,
+ },
+ .event_spec = mpl3115_temp_press_event,
+ .num_event_specs = ARRAY_SIZE(mpl3115_temp_press_event),
+ },
+ {
+ .type = IIO_TEMP,
+ .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),
+ .info_mask_shared_by_all_available =
+ BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 12,
+ .storagebits = 16,
+ .shift = 4,
+ .endianness = IIO_BE,
+ },
+ .event_spec = mpl3115_temp_press_event,
+ .num_event_specs = ARRAY_SIZE(mpl3115_temp_press_event),
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static irqreturn_t mpl3115_interrupt_handler(int irq, void *private)
+{
+ struct iio_dev *indio_dev = private;
+ struct mpl3115_data *data = iio_priv(indio_dev);
+ int ret;
+ u8 val_press[3];
+ __be16 val_temp;
+
+ ret = i2c_smbus_read_byte_data(data->client, MPL3115_INT_SOURCE);
+ if (ret < 0)
+ return IRQ_HANDLED;
+
+ if (!(ret & (MPL3115_INT_SRC_TTH | MPL3115_INT_SRC_PTH |
+ MPL3115_INT_SRC_DRDY)))
+ return IRQ_NONE;
+
+ if (ret & MPL3115_INT_SRC_DRDY)
+ iio_trigger_poll_nested(data->drdy_trig);
+
+ if (ret & MPL3115_INT_SRC_PTH) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PRESSURE, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+
+ /* Reset the SRC_PTH bit in INT_SOURCE */
+ i2c_smbus_read_i2c_block_data(data->client,
+ MPL3115_OUT_PRESS,
+ sizeof(val_press), val_press);
+ }
+
+ if (ret & MPL3115_INT_SRC_TTH) {
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP, 0,
+ IIO_EV_TYPE_THRESH,
+ IIO_EV_DIR_RISING),
+ iio_get_time_ns(indio_dev));
+
+ /* Reset the SRC_TTH bit in INT_SOURCE */
+ i2c_smbus_read_i2c_block_data(data->client,
+ MPL3115_OUT_TEMP,
+ sizeof(val_temp),
+ (u8 *)&val_temp);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int mpl3115_config_interrupt(struct mpl3115_data *data,
+ u8 ctrl_reg1, u8 ctrl_reg4)
+{
+ int ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
+ ctrl_reg1);
+ if (ret < 0)
+ return ret;
+
+ ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG4,
+ ctrl_reg4);
+ if (ret < 0)
+ goto reg1_cleanup;
+
+ data->ctrl_reg1 = ctrl_reg1;
+ data->ctrl_reg4 = ctrl_reg4;
+
+ return 0;
+
+reg1_cleanup:
+ i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
+ data->ctrl_reg1);
+ return ret;
+}
+
+static int mpl3115_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+ struct mpl3115_data *data = iio_priv(indio_dev);
+ u8 ctrl_reg1, ctrl_reg4;
+
+ guard(mutex)(&data->lock);
+
+ ctrl_reg1 = data->ctrl_reg1;
+ ctrl_reg4 = data->ctrl_reg4;
+
+ if (state) {
+ ctrl_reg1 |= MPL3115_CTRL1_ACTIVE;
+ ctrl_reg4 |= MPL3115_CTRL4_INT_EN_DRDY;
+ } else {
+ ctrl_reg4 &= ~MPL3115_CTRL4_INT_EN_DRDY;
+
+ if (!ctrl_reg4)
+ ctrl_reg1 &= ~MPL3115_CTRL1_ACTIVE;
+ }
+
+ return mpl3115_config_interrupt(data, ctrl_reg1, ctrl_reg4);
+}
+
+static const struct iio_trigger_ops mpl3115_trigger_ops = {
+ .set_trigger_state = mpl3115_set_trigger_state,
+};
+
+static int mpl3115_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 mpl3115_data *data = iio_priv(indio_dev);
+
+ if (chan->type == IIO_PRESSURE)
+ return !!(data->ctrl_reg4 & MPL3115_CTRL4_INT_EN_PTH);
+
+ if (chan->type == IIO_TEMP)
+ return !!(data->ctrl_reg4 & MPL3115_CTRL4_INT_EN_TTH);
+
+ return -EINVAL;
+}
+
+static int mpl3115_write_event_config(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ enum iio_event_type type,
+ enum iio_event_direction dir,
+ bool state)
+{
+ struct mpl3115_data *data = iio_priv(indio_dev);
+ u8 int_en_mask;
+ u8 ctrl_reg1, ctrl_reg4;
+
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ int_en_mask = MPL3115_CTRL4_INT_EN_PTH;
+ break;
+ case IIO_TEMP:
+ int_en_mask = MPL3115_CTRL4_INT_EN_TTH;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ guard(mutex)(&data->lock);
+
+ ctrl_reg1 = data->ctrl_reg1;
+ ctrl_reg4 = data->ctrl_reg4;
+
+ if (state) {
+ ctrl_reg1 |= MPL3115_CTRL1_ACTIVE;
+ ctrl_reg4 |= int_en_mask;
+ } else {
+ ctrl_reg4 &= ~int_en_mask;
+
+ if (!ctrl_reg4)
+ ctrl_reg1 &= ~MPL3115_CTRL1_ACTIVE;
+ }
+
+ return mpl3115_config_interrupt(data, ctrl_reg1, ctrl_reg4);
+}
+
+static int mpl3115_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 mpl3115_data *data = iio_priv(indio_dev);
+ int ret;
+ __be16 press_tgt;
+
+ if (info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ ret = i2c_smbus_read_i2c_block_data(data->client,
+ MPL3115_PRESS_TGT,
+ sizeof(press_tgt),
+ (u8 *)&press_tgt);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Target value for the pressure is 16-bit unsigned value,
+ * expressed in 2 Pa units
+ */
+ *val = be16_to_cpu(press_tgt) << 1;
+
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ ret = i2c_smbus_read_byte_data(data->client, MPL3115_TEMP_TGT);
+ if (ret < 0)
+ return ret;
+
+ /* Target value for the temperature is 8-bit 2's complement */
+ *val = sign_extend32(ret, 7);
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int mpl3115_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 mpl3115_data *data = iio_priv(indio_dev);
+ __be16 press_tgt;
+
+ if (info != IIO_EV_INFO_VALUE)
+ return -EINVAL;
+
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ val >>= 1;
+
+ if (val < 0 || val > U16_MAX)
+ return -EINVAL;
+
+ press_tgt = cpu_to_be16(val);
+
+ return i2c_smbus_write_i2c_block_data(data->client,
+ MPL3115_PRESS_TGT,
+ sizeof(press_tgt),
+ (u8 *)&press_tgt);
+ case IIO_TEMP:
+ if (val < S8_MIN || val > S8_MAX)
+ return -EINVAL;
+
+ return i2c_smbus_write_byte_data(data->client,
+ MPL3115_TEMP_TGT, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mpl3115_info = {
+ .read_raw = &mpl3115_read_raw,
+ .read_avail = &mpl3115_read_avail,
+ .write_raw = &mpl3115_write_raw,
+ .read_event_config = mpl3115_read_event_config,
+ .write_event_config = mpl3115_write_event_config,
+ .read_event_value = mpl3115_read_thresh,
+ .write_event_value = mpl3115_write_thresh,
+};
+
+static int mpl3115_trigger_probe(struct mpl3115_data *data,
+ struct iio_dev *indio_dev)
+{
+ struct fwnode_handle *fwnode = dev_fwnode(&data->client->dev);
+ int ret, irq, irq_type, irq_pin = MPL3115_IRQ_INT1;
+
+ irq = fwnode_irq_get_byname(fwnode, "INT1");
+ if (irq < 0) {
+ irq = fwnode_irq_get_byname(fwnode, "INT2");
+ if (irq < 0)
+ return 0;
+
+ irq_pin = MPL3115_IRQ_INT2;
+ }
+
+ irq_type = irq_get_trigger_type(irq);
+ if (irq_type != IRQF_TRIGGER_RISING && irq_type != IRQF_TRIGGER_FALLING)
+ return -EINVAL;
+
+ ret = i2c_smbus_write_byte_data(data->client, MPL3115_PT_DATA_CFG,
+ MPL3115_PT_DATA_EVENT_ALL);
+ if (ret < 0)
+ return ret;
+
+ if (irq_pin == MPL3115_IRQ_INT1) {
+ ret = i2c_smbus_write_byte_data(data->client,
+ MPL3115_CTRL_REG5,
+ MPL3115_CTRL5_INT_CFG_DRDY);
+ if (ret)
+ return ret;
+
+ if (irq_type == IRQF_TRIGGER_RISING) {
+ ret = i2c_smbus_write_byte_data(data->client,
+ MPL3115_CTRL_REG3,
+ MPL3115_CTRL3_IPOL1);
+ if (ret)
+ return ret;
+ }
+ } else if (irq_type == IRQF_TRIGGER_RISING) {
+ ret = i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG3,
+ MPL3115_CTRL3_IPOL2);
+ if (ret)
+ return ret;
+ }
+
+ data->drdy_trig = devm_iio_trigger_alloc(&data->client->dev,
+ "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!data->drdy_trig)
+ return -ENOMEM;
+
+ data->drdy_trig->ops = &mpl3115_trigger_ops;
+ iio_trigger_set_drvdata(data->drdy_trig, indio_dev);
+
+ ret = devm_request_threaded_irq(&data->client->dev, irq, NULL,
+ mpl3115_interrupt_handler,
+ IRQF_ONESHOT,
+ "mpl3115_irq", indio_dev);
+ if (ret)
+ return ret;
+
+ ret = devm_iio_trigger_register(&data->client->dev, data->drdy_trig);
+ if (ret)
+ return ret;
+
+ indio_dev->trig = iio_trigger_get(data->drdy_trig);
+
+ return 0;
+}
+
+static int mpl3115_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct mpl3115_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ ret = i2c_smbus_read_byte_data(client, MPL3115_WHO_AM_I);
+ if (ret < 0)
+ return ret;
+ if (ret != MPL3115_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->client = client;
+ mutex_init(&data->lock);
+
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->info = &mpl3115_info;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = mpl3115_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mpl3115_channels);
+
+ /* software reset, I2C transfer is aborted (fails) */
+ i2c_smbus_write_byte_data(client, MPL3115_CTRL_REG1,
+ MPL3115_CTRL1_RESET);
+ msleep(50);
+
+ data->ctrl_reg1 = MPL3115_CTRL1_OS_258MS;
+ ret = i2c_smbus_write_byte_data(client, MPL3115_CTRL_REG1,
+ data->ctrl_reg1);
+ if (ret < 0)
+ return ret;
+
+ ret = mpl3115_trigger_probe(data, indio_dev);
+ if (ret)
+ return ret;
+
+ ret = iio_triggered_buffer_setup(indio_dev, NULL,
+ mpl3115_trigger_handler, NULL);
+ if (ret < 0)
+ return ret;
+
+ ret = iio_device_register(indio_dev);
+ if (ret < 0)
+ goto buffer_cleanup;
+ return 0;
+
+buffer_cleanup:
+ iio_triggered_buffer_cleanup(indio_dev);
+ return ret;
+}
+
+static int mpl3115_standby(struct mpl3115_data *data)
+{
+ return i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
+ data->ctrl_reg1 & ~MPL3115_CTRL1_ACTIVE);
+}
+
+static void mpl3115_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);
+ mpl3115_standby(iio_priv(indio_dev));
+}
+
+static int mpl3115_suspend(struct device *dev)
+{
+ return mpl3115_standby(iio_priv(i2c_get_clientdata(
+ to_i2c_client(dev))));
+}
+
+static int mpl3115_resume(struct device *dev)
+{
+ struct mpl3115_data *data = iio_priv(i2c_get_clientdata(
+ to_i2c_client(dev)));
+
+ return i2c_smbus_write_byte_data(data->client, MPL3115_CTRL_REG1,
+ data->ctrl_reg1);
+}
+
+static DEFINE_SIMPLE_DEV_PM_OPS(mpl3115_pm_ops, mpl3115_suspend,
+ mpl3115_resume);
+
+static const struct i2c_device_id mpl3115_id[] = {
+ { .name = "mpl3115" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mpl3115_id);
+
+static const struct of_device_id mpl3115_of_match[] = {
+ { .compatible = "fsl,mpl3115" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mpl3115_of_match);
+
+static struct i2c_driver mpl3115_driver = {
+ .driver = {
+ .name = "mpl3115",
+ .of_match_table = mpl3115_of_match,
+ .pm = pm_sleep_ptr(&mpl3115_pm_ops),
+ },
+ .probe = mpl3115_probe,
+ .remove = mpl3115_remove,
+ .id_table = mpl3115_id,
+};
+module_i2c_driver(mpl3115_driver);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("Freescale MPL3115 pressure/temperature driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/mprls0025pa.c b/drivers/iio/pressure/mprls0025pa.c
new file mode 100644
index 000000000..c21f0a050
--- /dev/null
+++ b/drivers/iio/pressure/mprls0025pa.c
@@ -0,0 +1,443 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MPRLS0025PA - Honeywell MicroPressure pressure sensor series driver
+ *
+ * Copyright (c) Andreas Klinger <ak@it-klinger.de>
+ * Copyright (c) 2023-2025 Petre Rodan <petre.rodan@subdimension.ro>
+ *
+ * Data sheet:
+ * https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
+ *
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/completion.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/export.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/math64.h>
+#include <linux/module.h>
+#include <linux/property.h>
+#include <linux/string.h>
+#include <linux/time.h>
+#include <linux/units.h>
+
+#include <linux/gpio/consumer.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/regulator/consumer.h>
+
+#include <linux/unaligned.h>
+
+#include "mprls0025pa.h"
+
+/* bits in status byte */
+#define MPR_ST_POWER BIT(6) /* device is powered */
+#define MPR_ST_BUSY BIT(5) /* device is busy */
+
+/*
+ * support _RAW sysfs interface:
+ *
+ * Calculation formula from the datasheet:
+ * pressure = (press_cnt - outputmin) * scale + pmin
+ * with:
+ * * pressure - measured pressure in Pascal
+ * * press_cnt - raw value read from sensor
+ * * pmin - minimum pressure range value of sensor (data->pmin)
+ * * pmax - maximum pressure range value of sensor (data->pmax)
+ * * outputmin - minimum numerical range raw value delivered by sensor
+ * (mpr_func_spec.output_min)
+ * * outputmax - maximum numerical range raw value delivered by sensor
+ * (mpr_func_spec.output_max)
+ * * scale - (pmax - pmin) / (outputmax - outputmin)
+ *
+ * formula of the userspace:
+ * pressure = (raw + offset) * scale
+ *
+ * Values given to the userspace in sysfs interface:
+ * * raw - press_cnt
+ * * offset - (-1 * outputmin) + pmin / scale
+ * note: With all sensors from the datasheet pmin = 0
+ * which reduces the offset to (-1 * outputmin)
+ */
+
+/*
+ * transfer function A: 10% to 90% of 2^24
+ * transfer function B: 2.5% to 22.5% of 2^24
+ * transfer function C: 20% to 80% of 2^24
+ */
+struct mpr_func_spec {
+ u32 output_min;
+ u32 output_max;
+};
+
+static const struct mpr_func_spec mpr_func_spec[] = {
+ [MPR_FUNCTION_A] = { .output_min = 1677722, .output_max = 15099494 },
+ [MPR_FUNCTION_B] = { .output_min = 419430, .output_max = 3774874 },
+ [MPR_FUNCTION_C] = { .output_min = 3355443, .output_max = 13421773 },
+};
+
+enum mpr_variants {
+ MPR0001BA = 0x00, MPR01_6BA = 0x01, MPR02_5BA = 0x02, MPR0060MG = 0x03,
+ MPR0100MG = 0x04, MPR0160MG = 0x05, MPR0250MG = 0x06, MPR0400MG = 0x07,
+ MPR0600MG = 0x08, MPR0001BG = 0x09, MPR01_6BG = 0x0a, MPR02_5BG = 0x0b,
+ MPR0100KA = 0x0c, MPR0160KA = 0x0d, MPR0250KA = 0x0e, MPR0006KG = 0x0f,
+ MPR0010KG = 0x10, MPR0016KG = 0x11, MPR0025KG = 0x12, MPR0040KG = 0x13,
+ MPR0060KG = 0x14, MPR0100KG = 0x15, MPR0160KG = 0x16, MPR0250KG = 0x17,
+ MPR0015PA = 0x18, MPR0025PA = 0x19, MPR0030PA = 0x1a, MPR0001PG = 0x1b,
+ MPR0005PG = 0x1c, MPR0015PG = 0x1d, MPR0030PG = 0x1e, MPR0300YG = 0x1f,
+ MPR_VARIANTS_MAX
+};
+
+static const char * const mpr_triplet_variants[MPR_VARIANTS_MAX] = {
+ [MPR0001BA] = "0001BA", [MPR01_6BA] = "01.6BA", [MPR02_5BA] = "02.5BA",
+ [MPR0060MG] = "0060MG", [MPR0100MG] = "0100MG", [MPR0160MG] = "0160MG",
+ [MPR0250MG] = "0250MG", [MPR0400MG] = "0400MG", [MPR0600MG] = "0600MG",
+ [MPR0001BG] = "0001BG", [MPR01_6BG] = "01.6BG", [MPR02_5BG] = "02.5BG",
+ [MPR0100KA] = "0100KA", [MPR0160KA] = "0160KA", [MPR0250KA] = "0250KA",
+ [MPR0006KG] = "0006KG", [MPR0010KG] = "0010KG", [MPR0016KG] = "0016KG",
+ [MPR0025KG] = "0025KG", [MPR0040KG] = "0040KG", [MPR0060KG] = "0060KG",
+ [MPR0100KG] = "0100KG", [MPR0160KG] = "0160KG", [MPR0250KG] = "0250KG",
+ [MPR0015PA] = "0015PA", [MPR0025PA] = "0025PA", [MPR0030PA] = "0030PA",
+ [MPR0001PG] = "0001PG", [MPR0005PG] = "0005PG", [MPR0015PG] = "0015PG",
+ [MPR0030PG] = "0030PG", [MPR0300YG] = "0300YG"
+};
+
+/**
+ * struct mpr_range_config - list of pressure ranges based on nomenclature
+ * @pmin: lowest pressure that can be measured
+ * @pmax: highest pressure that can be measured
+ */
+struct mpr_range_config {
+ const s32 pmin;
+ const s32 pmax;
+};
+
+/* All min max limits have been converted to pascals */
+static const struct mpr_range_config mpr_range_config[MPR_VARIANTS_MAX] = {
+ [MPR0001BA] = { .pmin = 0, .pmax = 100000 },
+ [MPR01_6BA] = { .pmin = 0, .pmax = 160000 },
+ [MPR02_5BA] = { .pmin = 0, .pmax = 250000 },
+ [MPR0060MG] = { .pmin = 0, .pmax = 6000 },
+ [MPR0100MG] = { .pmin = 0, .pmax = 10000 },
+ [MPR0160MG] = { .pmin = 0, .pmax = 16000 },
+ [MPR0250MG] = { .pmin = 0, .pmax = 25000 },
+ [MPR0400MG] = { .pmin = 0, .pmax = 40000 },
+ [MPR0600MG] = { .pmin = 0, .pmax = 60000 },
+ [MPR0001BG] = { .pmin = 0, .pmax = 100000 },
+ [MPR01_6BG] = { .pmin = 0, .pmax = 160000 },
+ [MPR02_5BG] = { .pmin = 0, .pmax = 250000 },
+ [MPR0100KA] = { .pmin = 0, .pmax = 100000 },
+ [MPR0160KA] = { .pmin = 0, .pmax = 160000 },
+ [MPR0250KA] = { .pmin = 0, .pmax = 250000 },
+ [MPR0006KG] = { .pmin = 0, .pmax = 6000 },
+ [MPR0010KG] = { .pmin = 0, .pmax = 10000 },
+ [MPR0016KG] = { .pmin = 0, .pmax = 16000 },
+ [MPR0025KG] = { .pmin = 0, .pmax = 25000 },
+ [MPR0040KG] = { .pmin = 0, .pmax = 40000 },
+ [MPR0060KG] = { .pmin = 0, .pmax = 60000 },
+ [MPR0100KG] = { .pmin = 0, .pmax = 100000 },
+ [MPR0160KG] = { .pmin = 0, .pmax = 160000 },
+ [MPR0250KG] = { .pmin = 0, .pmax = 250000 },
+ [MPR0015PA] = { .pmin = 0, .pmax = 103421 },
+ [MPR0025PA] = { .pmin = 0, .pmax = 172369 },
+ [MPR0030PA] = { .pmin = 0, .pmax = 206843 },
+ [MPR0001PG] = { .pmin = 0, .pmax = 6895 },
+ [MPR0005PG] = { .pmin = 0, .pmax = 34474 },
+ [MPR0015PG] = { .pmin = 0, .pmax = 103421 },
+ [MPR0030PG] = { .pmin = 0, .pmax = 206843 },
+ [MPR0300YG] = { .pmin = 0, .pmax = 39997 }
+};
+
+static const struct iio_chan_spec mpr_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OFFSET),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(1),
+};
+
+static void mpr_reset(struct mpr_data *data)
+{
+ if (data->gpiod_reset) {
+ gpiod_set_value(data->gpiod_reset, 0);
+ udelay(10);
+ gpiod_set_value(data->gpiod_reset, 1);
+ }
+}
+
+/**
+ * mpr_read_pressure() - Read pressure value from sensor
+ * @data: Pointer to private data struct.
+ * @press: Output value read from sensor.
+ *
+ * Reading from the sensor by sending and receiving telegrams.
+ *
+ * If there is an end of conversion (EOC) interrupt registered the function
+ * waits for a maximum of one second for the interrupt.
+ *
+ * Context: The function can sleep and data->lock should be held when calling it
+ * Return:
+ * * 0 - OK, the pressure value could be read
+ * * -EBUSY - Sensor does not have a new conversion ready
+ * * -ETIMEDOUT - Timeout while waiting for the EOC interrupt
+ * * -EIO - Invalid status byte received from sensor
+ */
+static int mpr_read_pressure(struct mpr_data *data, s32 *press)
+{
+ struct device *dev = data->dev;
+ int ret;
+
+ reinit_completion(&data->completion);
+
+ ret = data->ops->write(data, MPR_CMD_SYNC, MPR_PKT_SYNC_LEN);
+ if (ret < 0) {
+ dev_err(dev, "error while writing ret: %d\n", ret);
+ return ret;
+ }
+
+ if (data->irq > 0) {
+ ret = wait_for_completion_timeout(&data->completion, HZ);
+ if (!ret) {
+ dev_err(dev, "timeout while waiting for eoc irq\n");
+ return -ETIMEDOUT;
+ }
+ } else {
+ fsleep(5 * USEC_PER_MSEC);
+ }
+
+ memset(data->rx_buf, 0, sizeof(data->rx_buf));
+ ret = data->ops->read(data, MPR_CMD_NOP, MPR_PKT_NOP_LEN);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * Status byte flags
+ * bit7 SANITY_CHK - must always be 0
+ * bit6 MPR_ST_POWER - 1 if device is powered
+ * bit5 MPR_ST_BUSY - 1 if device has no new conversion ready
+ * bit4 SANITY_CHK - must always be 0
+ * bit3 SANITY_CHK - must always be 0
+ * bit2 MEMORY_ERR - 1 if integrity test has failed
+ * bit1 SANITY_CHK - must always be 0
+ * bit0 MATH_ERR - 1 during internal math saturation error
+ */
+
+ if (data->rx_buf[0] == (MPR_ST_POWER | MPR_ST_BUSY))
+ return -EBUSY;
+
+ if (data->rx_buf[0] != MPR_ST_POWER) {
+ dev_err(data->dev,
+ "unexpected status byte 0x%02x\n", data->rx_buf[0]);
+ return -EIO;
+ }
+
+ *press = get_unaligned_be24(&data->rx_buf[1]);
+
+ dev_dbg(dev, "received: %*ph cnt: %d\n", ret, data->rx_buf, *press);
+
+ return 0;
+}
+
+static irqreturn_t mpr_eoc_handler(int irq, void *p)
+{
+ struct mpr_data *data = p;
+
+ complete(&data->completion);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t mpr_trigger_handler(int irq, void *p)
+{
+ int ret;
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct mpr_data *data = iio_priv(indio_dev);
+
+ mutex_lock(&data->lock);
+ ret = mpr_read_pressure(data, &data->chan.pres);
+ if (ret < 0)
+ goto err;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &data->chan,
+ iio_get_time_ns(indio_dev));
+
+err:
+ mutex_unlock(&data->lock);
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int mpr_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan, int *val, int *val2, long mask)
+{
+ int ret;
+ s32 pressure;
+ struct mpr_data *data = iio_priv(indio_dev);
+
+ if (chan->type != IIO_PRESSURE)
+ return -EINVAL;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ mutex_lock(&data->lock);
+ ret = mpr_read_pressure(data, &pressure);
+ mutex_unlock(&data->lock);
+ if (ret < 0)
+ return ret;
+ *val = pressure;
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = data->scale;
+ *val2 = data->scale2;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_CHAN_INFO_OFFSET:
+ *val = data->offset;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info mpr_info = {
+ .read_raw = &mpr_read_raw,
+};
+
+int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq)
+{
+ int ret;
+ struct mpr_data *data;
+ struct iio_dev *indio_dev;
+ const char *triplet;
+ s64 odelta, pdelta;
+ u32 func;
+ s32 tmp;
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->dev = dev;
+ data->ops = ops;
+ data->irq = irq;
+
+ mutex_init(&data->lock);
+ init_completion(&data->completion);
+
+ indio_dev->name = "mprls0025pa";
+ indio_dev->info = &mpr_info;
+ indio_dev->channels = mpr_channels;
+ indio_dev->num_channels = ARRAY_SIZE(mpr_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "can't get and enable vdd supply\n");
+
+ ret = device_property_read_u32(dev,
+ "honeywell,transfer-function", &func);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,transfer-function could not be read\n");
+ data->function = func - 1;
+ if (data->function > MPR_FUNCTION_C)
+ return dev_err_probe(dev, -EINVAL,
+ "honeywell,transfer-function %d invalid\n",
+ data->function);
+
+ ret = device_property_read_string(dev, "honeywell,pressure-triplet",
+ &triplet);
+ if (ret) {
+ ret = device_property_read_u32(dev, "honeywell,pmin-pascal",
+ &data->pmin);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmin-pascal could not be read\n");
+
+ ret = device_property_read_u32(dev, "honeywell,pmax-pascal",
+ &data->pmax);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "honeywell,pmax-pascal could not be read\n");
+ } else {
+ ret = device_property_match_property_string(dev,
+ "honeywell,pressure-triplet",
+ mpr_triplet_variants,
+ MPR_VARIANTS_MAX);
+ if (ret < 0)
+ return dev_err_probe(dev, -EINVAL,
+ "honeywell,pressure-triplet is invalid\n");
+
+ data->pmin = mpr_range_config[ret].pmin;
+ data->pmax = mpr_range_config[ret].pmax;
+ }
+
+ if (data->pmin >= data->pmax)
+ return dev_err_probe(dev, -EINVAL,
+ "pressure limits are invalid\n");
+
+ data->outmin = mpr_func_spec[data->function].output_min;
+ data->outmax = mpr_func_spec[data->function].output_max;
+
+ odelta = data->outmax - data->outmin;
+ pdelta = data->pmax - data->pmin;
+
+ data->scale = div_s64_rem(div_s64(pdelta * NANO, odelta), NANO, &tmp);
+ data->scale2 = tmp;
+
+ data->offset = div_s64(odelta * data->pmin, pdelta) - data->outmin;
+
+ if (data->irq > 0) {
+ ret = devm_request_irq(dev, data->irq, mpr_eoc_handler, 0,
+ dev_name(dev), data);
+ if (ret)
+ return ret;
+ }
+
+ data->gpiod_reset = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_HIGH);
+ if (IS_ERR(data->gpiod_reset))
+ return dev_err_probe(dev, PTR_ERR(data->gpiod_reset),
+ "request reset-gpio failed\n");
+
+ mpr_reset(data);
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ mpr_trigger_handler, NULL);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "iio triggered buffer setup failed\n");
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "unable to register iio device\n");
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(mpr_common_probe, "IIO_HONEYWELL_MPRLS0025PA");
+
+MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
+MODULE_DESCRIPTION("Honeywell MPR pressure sensor core driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/mprls0025pa.h b/drivers/iio/pressure/mprls0025pa.h
new file mode 100644
index 000000000..9f43273e6
--- /dev/null
+++ b/drivers/iio/pressure/mprls0025pa.h
@@ -0,0 +1,90 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MPRLS0025PA - Honeywell MicroPressure pressure sensor series driver
+ *
+ * Copyright (c) Andreas Klinger <ak@it-klinger.de>
+ *
+ * Data sheet:
+ * https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
+ */
+
+#ifndef _MPRLS0025PA_H
+#define _MPRLS0025PA_H
+
+#include <linux/completion.h>
+#include <linux/mutex.h>
+#include <linux/types.h>
+
+#include <linux/iio/iio.h>
+
+#define MPR_MEASUREMENT_RD_SIZE 4
+#define MPR_CMD_NOP 0xf0
+#define MPR_CMD_SYNC 0xaa
+#define MPR_PKT_NOP_LEN MPR_MEASUREMENT_RD_SIZE
+#define MPR_PKT_SYNC_LEN 3
+
+struct device;
+
+struct mpr_data;
+struct mpr_ops;
+
+enum mpr_func_id {
+ MPR_FUNCTION_A,
+ MPR_FUNCTION_B,
+ MPR_FUNCTION_C,
+};
+
+/**
+ * struct mpr_data
+ * @dev: current device structure
+ * @ops: functions that implement the sensor reads/writes, bus init
+ * @lock: access to device during read
+ * @pmin: minimal pressure in pascal
+ * @pmax: maximal pressure in pascal
+ * @function: transfer function
+ * @outmin: minimum raw pressure in counts (based on transfer function)
+ * @outmax: maximum raw pressure in counts (based on transfer function)
+ * @scale: pressure scale
+ * @scale2: pressure scale, decimal number
+ * @offset: pressure offset
+ * @gpiod_reset: reset
+ * @irq: end of conversion irq. used to distinguish between irq mode and
+ * reading in a loop until data is ready
+ * @completion: handshake from irq to read
+ * @chan: channel values for buffered mode
+ * @chan.pres: pressure value
+ * @chan.ts: timestamp
+ * @rx_buf: raw conversion data
+ * @tx_buf: output buffer
+ */
+struct mpr_data {
+ struct device *dev;
+ const struct mpr_ops *ops;
+ struct mutex lock;
+ u32 pmin;
+ u32 pmax;
+ enum mpr_func_id function;
+ u32 outmin;
+ u32 outmax;
+ int scale;
+ int scale2;
+ int offset;
+ struct gpio_desc *gpiod_reset;
+ int irq;
+ struct completion completion;
+ struct {
+ s32 pres;
+ aligned_s64 ts;
+ } chan;
+ u8 rx_buf[MPR_MEASUREMENT_RD_SIZE] __aligned(IIO_DMA_MINALIGN);
+ u8 tx_buf[MPR_MEASUREMENT_RD_SIZE];
+};
+
+struct mpr_ops {
+ int (*read)(struct mpr_data *data, const u8 cmd, const u8 cnt);
+ int (*write)(struct mpr_data *data, const u8 cmd, const u8 cnt);
+};
+
+int mpr_common_probe(struct device *dev, const struct mpr_ops *ops, int irq);
+
+#endif
diff --git a/drivers/iio/pressure/mprls0025pa_i2c.c b/drivers/iio/pressure/mprls0025pa_i2c.c
new file mode 100644
index 000000000..06907b64a
--- /dev/null
+++ b/drivers/iio/pressure/mprls0025pa_i2c.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * MPRLS0025PA - Honeywell MicroPressure pressure sensor series driver
+ *
+ * Copyright (c) Andreas Klinger <ak@it-klinger.de>
+ *
+ * Data sheet:
+ * https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
+ */
+
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/types.h>
+
+#include "mprls0025pa.h"
+
+static int mpr_i2c_read(struct mpr_data *data, const u8 unused, const u8 cnt)
+{
+ int ret;
+ struct i2c_client *client = to_i2c_client(data->dev);
+
+ if (cnt > MPR_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ ret = i2c_master_recv(client, data->rx_buf, cnt);
+ if (ret < 0)
+ return ret;
+ else if (ret != cnt)
+ return -EIO;
+
+ return 0;
+}
+
+static int mpr_i2c_write(struct mpr_data *data, const u8 cmd, const u8 unused)
+{
+ int ret;
+ struct i2c_client *client = to_i2c_client(data->dev);
+
+ data->tx_buf[0] = cmd;
+ ret = i2c_master_send(client, data->tx_buf, MPR_PKT_SYNC_LEN);
+ if (ret < 0)
+ return ret;
+ else if (ret != MPR_PKT_SYNC_LEN)
+ return -EIO;
+
+ return 0;
+}
+
+static const struct mpr_ops mpr_i2c_ops = {
+ .read = mpr_i2c_read,
+ .write = mpr_i2c_write,
+};
+
+static int mpr_i2c_probe(struct i2c_client *client)
+{
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_BYTE))
+ return -EOPNOTSUPP;
+
+ return mpr_common_probe(&client->dev, &mpr_i2c_ops, client->irq);
+}
+
+static const struct of_device_id mpr_i2c_match[] = {
+ { .compatible = "honeywell,mprls0025pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mpr_i2c_match);
+
+static const struct i2c_device_id mpr_i2c_id[] = {
+ { .name = "mprls0025pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, mpr_i2c_id);
+
+static struct i2c_driver mpr_i2c_driver = {
+ .probe = mpr_i2c_probe,
+ .id_table = mpr_i2c_id,
+ .driver = {
+ .name = "mprls0025pa",
+ .of_match_table = mpr_i2c_match,
+ },
+};
+module_i2c_driver(mpr_i2c_driver);
+
+MODULE_AUTHOR("Andreas Klinger <ak@it-klinger.de>");
+MODULE_DESCRIPTION("Honeywell MPR pressure sensor i2c driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_MPRLS0025PA");
diff --git a/drivers/iio/pressure/mprls0025pa_spi.c b/drivers/iio/pressure/mprls0025pa_spi.c
new file mode 100644
index 000000000..627037038
--- /dev/null
+++ b/drivers/iio/pressure/mprls0025pa_spi.c
@@ -0,0 +1,80 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * MPRLS0025PA - Honeywell MicroPressure MPR series SPI sensor driver
+ *
+ * Copyright (c) 2024 Petre Rodan <petre.rodan@subdimension.ro>
+ *
+ * Data sheet:
+ * https://prod-edam.honeywell.com/content/dam/honeywell-edam/sps/siot/en-us/products/sensors/pressure-sensors/board-mount-pressure-sensors/micropressure-mpr-series/documents/sps-siot-mpr-series-datasheet-32332628-ciid-172626.pdf
+ */
+
+#include <linux/array_size.h>
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/stddef.h>
+#include <linux/types.h>
+
+#include "mprls0025pa.h"
+
+static int mpr_spi_xfer(struct mpr_data *data, const u8 cmd, const u8 pkt_len)
+{
+ struct spi_device *spi = to_spi_device(data->dev);
+ struct spi_transfer xfers[2] = { };
+
+ if (pkt_len > MPR_MEASUREMENT_RD_SIZE)
+ return -EOVERFLOW;
+
+ data->tx_buf[0] = cmd;
+
+ /*
+ * Dummy transfer with no data, just cause a 2.5us+ delay between the CS assert
+ * and the first clock edge as per the datasheet tHDSS timing requirement.
+ */
+ xfers[0].delay.value = 2500;
+ xfers[0].delay.unit = SPI_DELAY_UNIT_NSECS;
+
+ xfers[1].tx_buf = data->tx_buf;
+ xfers[1].rx_buf = data->rx_buf;
+ xfers[1].len = pkt_len;
+
+ return spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers));
+}
+
+static const struct mpr_ops mpr_spi_ops = {
+ .read = mpr_spi_xfer,
+ .write = mpr_spi_xfer,
+};
+
+static int mpr_spi_probe(struct spi_device *spi)
+{
+ return mpr_common_probe(&spi->dev, &mpr_spi_ops, spi->irq);
+}
+
+static const struct of_device_id mpr_spi_match[] = {
+ { .compatible = "honeywell,mprls0025pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, mpr_spi_match);
+
+static const struct spi_device_id mpr_spi_id[] = {
+ { .name = "mprls0025pa" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, mpr_spi_id);
+
+static struct spi_driver mpr_spi_driver = {
+ .driver = {
+ .name = "mprls0025pa",
+ .of_match_table = mpr_spi_match,
+ },
+ .probe = mpr_spi_probe,
+ .id_table = mpr_spi_id,
+};
+module_spi_driver(mpr_spi_driver);
+
+MODULE_AUTHOR("Petre Rodan <petre.rodan@subdimension.ro>");
+MODULE_DESCRIPTION("Honeywell MPR pressure sensor spi driver");
+MODULE_LICENSE("GPL");
+MODULE_IMPORT_NS("IIO_HONEYWELL_MPRLS0025PA");
diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h
new file mode 100644
index 000000000..a78acd3fb
--- /dev/null
+++ b/drivers/iio/pressure/ms5611.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * MS5611 pressure and temperature sensor driver
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
+ *
+ */
+
+#ifndef _MS5611_H
+#define _MS5611_H
+
+#include <linux/device.h>
+#include <linux/iio/iio.h>
+#include <linux/mutex.h>
+
+#define MS5611_RESET 0x1e
+#define MS5611_READ_ADC 0x00
+#define MS5611_READ_PROM_WORD 0xA0
+#define MS5611_PROM_WORDS_NB 8
+
+enum {
+ MS5611,
+ MS5607,
+};
+
+/*
+ * OverSampling Rate descriptor.
+ * Warning: cmd MUST be kept aligned on a word boundary (see
+ * m5611_spi_read_adc_temp_and_pressure in ms5611_spi.c).
+ */
+struct ms5611_osr {
+ unsigned long conv_usec;
+ u8 cmd;
+ unsigned short rate;
+};
+
+struct ms5611_state {
+ void *client;
+ struct mutex lock;
+
+ const struct ms5611_osr *pressure_osr;
+ const struct ms5611_osr *temp_osr;
+
+ u16 prom[MS5611_PROM_WORDS_NB];
+
+ int (*reset)(struct ms5611_state *st);
+ int (*read_prom_word)(struct ms5611_state *st, int index, u16 *word);
+ int (*read_adc_temp_and_pressure)(struct ms5611_state *st,
+ s32 *temp, s32 *pressure);
+
+ int (*compensate_temp_and_pressure)(struct ms5611_state *st, s32 *temp,
+ s32 *pressure);
+};
+
+int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
+ const char *name, int type);
+
+#endif /* _MS5611_H */
diff --git a/drivers/iio/pressure/ms5611_core.c b/drivers/iio/pressure/ms5611_core.c
new file mode 100644
index 000000000..bdac27bd5
--- /dev/null
+++ b/drivers/iio/pressure/ms5611_core.c
@@ -0,0 +1,454 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MS5611 pressure and temperature sensor driver
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
+ *
+ * Data sheet:
+ * http://www.meas-spec.com/downloads/MS5611-01BA03.pdf
+ * http://www.meas-spec.com/downloads/MS5607-02BA03.pdf
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/iio/iio.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include "ms5611.h"
+
+#define MS5611_INIT_OSR(_cmd, _conv_usec, _rate) \
+ { .cmd = _cmd, .conv_usec = _conv_usec, .rate = _rate }
+
+static const struct ms5611_osr ms5611_avail_pressure_osr[] = {
+ MS5611_INIT_OSR(0x40, 600, 256),
+ MS5611_INIT_OSR(0x42, 1170, 512),
+ MS5611_INIT_OSR(0x44, 2280, 1024),
+ MS5611_INIT_OSR(0x46, 4540, 2048),
+ MS5611_INIT_OSR(0x48, 9040, 4096)
+};
+
+static const struct ms5611_osr ms5611_avail_temp_osr[] = {
+ MS5611_INIT_OSR(0x50, 600, 256),
+ MS5611_INIT_OSR(0x52, 1170, 512),
+ MS5611_INIT_OSR(0x54, 2280, 1024),
+ MS5611_INIT_OSR(0x56, 4540, 2048),
+ MS5611_INIT_OSR(0x58, 9040, 4096)
+};
+
+static const char ms5611_show_osr[] = "256 512 1024 2048 4096";
+
+static IIO_CONST_ATTR(oversampling_ratio_available, ms5611_show_osr);
+
+static struct attribute *ms5611_attributes[] = {
+ &iio_const_attr_oversampling_ratio_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ms5611_attribute_group = {
+ .attrs = ms5611_attributes,
+};
+
+static bool ms5611_prom_is_valid(u16 *prom, size_t len)
+{
+ int i, j;
+ uint16_t crc = 0, crc_orig = prom[7] & 0x000F;
+
+ prom[7] &= 0xFF00;
+
+ for (i = 0; i < len * 2; i++) {
+ if (i % 2 == 1)
+ crc ^= prom[i >> 1] & 0x00FF;
+ else
+ crc ^= prom[i >> 1] >> 8;
+
+ for (j = 0; j < 8; j++) {
+ if (crc & 0x8000)
+ crc = (crc << 1) ^ 0x3000;
+ else
+ crc <<= 1;
+ }
+ }
+
+ crc = (crc >> 12) & 0x000F;
+
+ return crc == crc_orig;
+}
+
+static int ms5611_read_prom(struct iio_dev *indio_dev)
+{
+ int ret, i;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ for (i = 0; i < MS5611_PROM_WORDS_NB; i++) {
+ ret = st->read_prom_word(st, i, &st->prom[i]);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "failed to read prom at %d\n", i);
+ return ret;
+ }
+ }
+
+ if (!ms5611_prom_is_valid(st->prom, MS5611_PROM_WORDS_NB)) {
+ dev_err(&indio_dev->dev, "PROM integrity check failed\n");
+ return -ENODEV;
+ }
+
+ return 0;
+}
+
+static int ms5611_read_temp_and_pressure(struct iio_dev *indio_dev,
+ s32 *temp, s32 *pressure)
+{
+ int ret;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ ret = st->read_adc_temp_and_pressure(st, temp, pressure);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev,
+ "failed to read temperature and pressure\n");
+ return ret;
+ }
+
+ return st->compensate_temp_and_pressure(st, temp, pressure);
+}
+
+static int ms5611_temp_and_pressure_compensate(struct ms5611_state *st,
+ s32 *temp, s32 *pressure)
+{
+ s32 t = *temp, p = *pressure;
+ s64 off, sens, dt;
+
+ dt = t - (st->prom[5] << 8);
+ off = ((s64)st->prom[2] << 16) + ((st->prom[4] * dt) >> 7);
+ sens = ((s64)st->prom[1] << 15) + ((st->prom[3] * dt) >> 8);
+
+ t = 2000 + ((st->prom[6] * dt) >> 23);
+ if (t < 2000) {
+ s64 off2, sens2, t2;
+
+ t2 = (dt * dt) >> 31;
+ off2 = (5 * (t - 2000) * (t - 2000)) >> 1;
+ sens2 = off2 >> 1;
+
+ if (t < -1500) {
+ s64 tmp = (t + 1500) * (t + 1500);
+
+ off2 += 7 * tmp;
+ sens2 += (11 * tmp) >> 1;
+ }
+
+ t -= t2;
+ off -= off2;
+ sens -= sens2;
+ }
+
+ *temp = t;
+ *pressure = (((p * sens) >> 21) - off) >> 15;
+
+ return 0;
+}
+
+static int ms5607_temp_and_pressure_compensate(struct ms5611_state *st,
+ s32 *temp, s32 *pressure)
+{
+ s32 t = *temp, p = *pressure;
+ s64 off, sens, dt;
+
+ dt = t - (st->prom[5] << 8);
+ off = ((s64)st->prom[2] << 17) + ((st->prom[4] * dt) >> 6);
+ sens = ((s64)st->prom[1] << 16) + ((st->prom[3] * dt) >> 7);
+
+ t = 2000 + ((st->prom[6] * dt) >> 23);
+ if (t < 2000) {
+ s64 off2, sens2, t2, tmp;
+
+ t2 = (dt * dt) >> 31;
+ tmp = (t - 2000) * (t - 2000);
+ off2 = (61 * tmp) >> 4;
+ sens2 = tmp << 1;
+
+ if (t < -1500) {
+ tmp = (t + 1500) * (t + 1500);
+ off2 += 15 * tmp;
+ sens2 += 8 * tmp;
+ }
+
+ t -= t2;
+ off -= off2;
+ sens -= sens2;
+ }
+
+ *temp = t;
+ *pressure = (((p * sens) >> 21) - off) >> 15;
+
+ return 0;
+}
+
+static int ms5611_reset(struct iio_dev *indio_dev)
+{
+ int ret;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ ret = st->reset(st);
+ if (ret < 0) {
+ dev_err(&indio_dev->dev, "failed to reset device\n");
+ return ret;
+ }
+
+ usleep_range(3000, 4000);
+
+ return 0;
+}
+
+static irqreturn_t ms5611_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct ms5611_state *st = iio_priv(indio_dev);
+ /* Ensure buffer elements are naturally aligned */
+ struct {
+ s32 channels[2];
+ aligned_s64 ts;
+ } scan;
+ int ret;
+
+ mutex_lock(&st->lock);
+ ret = ms5611_read_temp_and_pressure(indio_dev, &scan.channels[1],
+ &scan.channels[0]);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ goto err;
+
+ iio_push_to_buffers_with_timestamp(indio_dev, &scan,
+ iio_get_time_ns(indio_dev));
+
+err:
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+static int ms5611_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ s32 temp, pressure;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ mutex_lock(&st->lock);
+ ret = ms5611_read_temp_and_pressure(indio_dev,
+ &temp, &pressure);
+ mutex_unlock(&st->lock);
+ if (ret < 0)
+ return ret;
+
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = temp * 10;
+ return IIO_VAL_INT;
+ case IIO_PRESSURE:
+ *val = pressure / 1000;
+ *val2 = (pressure % 1000) * 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = 10;
+ return IIO_VAL_INT;
+ case IIO_PRESSURE:
+ *val = 0;
+ *val2 = 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
+ if (chan->type != IIO_TEMP && chan->type != IIO_PRESSURE)
+ break;
+ mutex_lock(&st->lock);
+ if (chan->type == IIO_TEMP)
+ *val = (int)st->temp_osr->rate;
+ else
+ *val = (int)st->pressure_osr->rate;
+ mutex_unlock(&st->lock);
+ return IIO_VAL_INT;
+ }
+
+ return -EINVAL;
+}
+
+static const struct ms5611_osr *ms5611_find_osr(int rate,
+ const struct ms5611_osr *osr,
+ size_t count)
+{
+ unsigned int r;
+
+ for (r = 0; r < count; r++)
+ if ((unsigned short)rate == osr[r].rate)
+ break;
+ if (r >= count)
+ return NULL;
+ return &osr[r];
+}
+
+static int ms5611_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ms5611_state *st = iio_priv(indio_dev);
+ const struct ms5611_osr *osr = NULL;
+
+ if (mask != IIO_CHAN_INFO_OVERSAMPLING_RATIO)
+ return -EINVAL;
+
+ if (chan->type == IIO_TEMP)
+ osr = ms5611_find_osr(val, ms5611_avail_temp_osr,
+ ARRAY_SIZE(ms5611_avail_temp_osr));
+ else if (chan->type == IIO_PRESSURE)
+ osr = ms5611_find_osr(val, ms5611_avail_pressure_osr,
+ ARRAY_SIZE(ms5611_avail_pressure_osr));
+ if (!osr)
+ return -EINVAL;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ mutex_lock(&st->lock);
+
+ if (chan->type == IIO_TEMP)
+ st->temp_osr = osr;
+ else
+ st->pressure_osr = osr;
+
+ mutex_unlock(&st->lock);
+ iio_device_release_direct(indio_dev);
+
+ return 0;
+}
+
+static const unsigned long ms5611_scan_masks[] = {0x3, 0};
+
+static const struct iio_chan_spec ms5611_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_SCALE) |
+ BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 32,
+ .storagebits = 32,
+ .endianness = IIO_CPU,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const struct iio_info ms5611_info = {
+ .read_raw = &ms5611_read_raw,
+ .write_raw = &ms5611_write_raw,
+ .attrs = &ms5611_attribute_group,
+};
+
+static int ms5611_init(struct iio_dev *indio_dev)
+{
+ int ret;
+
+ /* Enable attached regulator if any. */
+ ret = devm_regulator_get_enable(indio_dev->dev.parent, "vdd");
+ if (ret)
+ return ret;
+
+ ret = ms5611_reset(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = ms5611_read_prom(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+int ms5611_probe(struct iio_dev *indio_dev, struct device *dev,
+ const char *name, int type)
+{
+ int ret;
+ struct ms5611_state *st = iio_priv(indio_dev);
+
+ mutex_init(&st->lock);
+
+ switch (type) {
+ case MS5611:
+ st->compensate_temp_and_pressure =
+ ms5611_temp_and_pressure_compensate;
+ break;
+ case MS5607:
+ st->compensate_temp_and_pressure =
+ ms5607_temp_and_pressure_compensate;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ st->temp_osr =
+ &ms5611_avail_temp_osr[ARRAY_SIZE(ms5611_avail_temp_osr) - 1];
+ st->pressure_osr =
+ &ms5611_avail_pressure_osr[ARRAY_SIZE(ms5611_avail_pressure_osr)
+ - 1];
+ indio_dev->name = name;
+ indio_dev->info = &ms5611_info;
+ indio_dev->channels = ms5611_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ms5611_channels);
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->available_scan_masks = ms5611_scan_masks;
+
+ ret = ms5611_init(indio_dev);
+ if (ret < 0)
+ return ret;
+
+ ret = devm_iio_triggered_buffer_setup(dev, indio_dev, NULL,
+ ms5611_trigger_handler, NULL);
+ if (ret < 0) {
+ dev_err(dev, "iio triggered buffer setup failed\n");
+ return ret;
+ }
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret < 0) {
+ dev_err(dev, "unable to register iio device\n");
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_NS(ms5611_probe, "IIO_MS5611");
+
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
+MODULE_DESCRIPTION("MS5611 core driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/ms5611_i2c.c b/drivers/iio/pressure/ms5611_i2c.c
new file mode 100644
index 000000000..c57ad473b
--- /dev/null
+++ b/drivers/iio/pressure/ms5611_i2c.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MS5611 pressure and temperature sensor driver (I2C bus)
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
+ *
+ * 7-bit I2C slave addresses:
+ *
+ * 0x77 (CSB pin low)
+ * 0x76 (CSB pin high)
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+
+#include <linux/unaligned.h>
+
+#include "ms5611.h"
+
+static int ms5611_i2c_reset(struct ms5611_state *st)
+{
+ return i2c_smbus_write_byte(st->client, MS5611_RESET);
+}
+
+static int ms5611_i2c_read_prom_word(struct ms5611_state *st, int index,
+ u16 *word)
+{
+ int ret;
+
+ ret = i2c_smbus_read_word_swapped(st->client,
+ MS5611_READ_PROM_WORD + (index << 1));
+ if (ret < 0)
+ return ret;
+
+ *word = ret;
+
+ return 0;
+}
+
+static int ms5611_i2c_read_adc(struct ms5611_state *st, s32 *val)
+{
+ int ret;
+ u8 buf[3];
+
+ ret = i2c_smbus_read_i2c_block_data(st->client, MS5611_READ_ADC,
+ 3, buf);
+ if (ret < 0)
+ return ret;
+
+ *val = get_unaligned_be24(&buf[0]);
+
+ return 0;
+}
+
+static int ms5611_i2c_read_adc_temp_and_pressure(struct ms5611_state *st,
+ s32 *temp, s32 *pressure)
+{
+ int ret;
+ const struct ms5611_osr *osr = st->temp_osr;
+
+ ret = i2c_smbus_write_byte(st->client, osr->cmd);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL));
+ ret = ms5611_i2c_read_adc(st, temp);
+ if (ret < 0)
+ return ret;
+
+ osr = st->pressure_osr;
+ ret = i2c_smbus_write_byte(st->client, osr->cmd);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL));
+ return ms5611_i2c_read_adc(st, pressure);
+}
+
+static int ms5611_i2c_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct ms5611_state *st;
+ struct iio_dev *indio_dev;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_WRITE_BYTE |
+ I2C_FUNC_SMBUS_READ_WORD_DATA |
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK))
+ return -EOPNOTSUPP;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ st = iio_priv(indio_dev);
+ i2c_set_clientdata(client, indio_dev);
+ st->reset = ms5611_i2c_reset;
+ st->read_prom_word = ms5611_i2c_read_prom_word;
+ st->read_adc_temp_and_pressure = ms5611_i2c_read_adc_temp_and_pressure;
+ st->client = client;
+
+ return ms5611_probe(indio_dev, &client->dev, id->name, id->driver_data);
+}
+
+static const struct of_device_id ms5611_i2c_matches[] = {
+ { .compatible = "meas,ms5611" },
+ { .compatible = "meas,ms5607" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ms5611_i2c_matches);
+
+static const struct i2c_device_id ms5611_id[] = {
+ { .name = "ms5611", .driver_data = MS5611 },
+ { .name = "ms5607", .driver_data = MS5607 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ms5611_id);
+
+static struct i2c_driver ms5611_driver = {
+ .driver = {
+ .name = "ms5611",
+ .of_match_table = ms5611_i2c_matches,
+ },
+ .id_table = ms5611_id,
+ .probe = ms5611_i2c_probe,
+};
+module_i2c_driver(ms5611_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
+MODULE_DESCRIPTION("MS5611 i2c driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_MS5611");
diff --git a/drivers/iio/pressure/ms5611_spi.c b/drivers/iio/pressure/ms5611_spi.c
new file mode 100644
index 000000000..7133e6744
--- /dev/null
+++ b/drivers/iio/pressure/ms5611_spi.c
@@ -0,0 +1,135 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * MS5611 pressure and temperature sensor driver (SPI bus)
+ *
+ * Copyright (c) Tomasz Duszynski <tduszyns@gmail.com>
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+
+#include <linux/unaligned.h>
+
+#include "ms5611.h"
+
+static int ms5611_spi_reset(struct ms5611_state *st)
+{
+ u8 cmd = MS5611_RESET;
+
+ return spi_write_then_read(st->client, &cmd, 1, NULL, 0);
+}
+
+static int ms5611_spi_read_prom_word(struct ms5611_state *st, int index,
+ u16 *word)
+{
+ int ret;
+
+ ret = spi_w8r16be(st->client, MS5611_READ_PROM_WORD + (index << 1));
+ if (ret < 0)
+ return ret;
+
+ *word = ret;
+
+ return 0;
+}
+
+static int ms5611_spi_read_adc(struct ms5611_state *st, s32 *val)
+{
+ int ret;
+ u8 buf[3] = { MS5611_READ_ADC };
+
+ ret = spi_write_then_read(st->client, buf, 1, buf, 3);
+ if (ret < 0)
+ return ret;
+
+ *val = get_unaligned_be24(&buf[0]);
+
+ return 0;
+}
+
+static int ms5611_spi_read_adc_temp_and_pressure(struct ms5611_state *st,
+ s32 *temp, s32 *pressure)
+{
+ int ret;
+ const struct ms5611_osr *osr = st->temp_osr;
+
+ /*
+ * Warning: &osr->cmd MUST be aligned on a word boundary since used as
+ * 2nd argument (void*) of spi_write_then_read.
+ */
+ ret = spi_write_then_read(st->client, &osr->cmd, 1, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL));
+ ret = ms5611_spi_read_adc(st, temp);
+ if (ret < 0)
+ return ret;
+
+ osr = st->pressure_osr;
+ ret = spi_write_then_read(st->client, &osr->cmd, 1, NULL, 0);
+ if (ret < 0)
+ return ret;
+
+ usleep_range(osr->conv_usec, osr->conv_usec + (osr->conv_usec / 10UL));
+ return ms5611_spi_read_adc(st, pressure);
+}
+
+static int ms5611_spi_probe(struct spi_device *spi)
+{
+ int ret;
+ struct ms5611_state *st;
+ struct iio_dev *indio_dev;
+
+ indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ spi_set_drvdata(spi, indio_dev);
+
+ spi->mode = SPI_MODE_0;
+ spi->max_speed_hz = min(spi->max_speed_hz, 20000000U);
+ ret = spi_setup(spi);
+ if (ret < 0)
+ return ret;
+
+ st = iio_priv(indio_dev);
+ st->reset = ms5611_spi_reset;
+ st->read_prom_word = ms5611_spi_read_prom_word;
+ st->read_adc_temp_and_pressure = ms5611_spi_read_adc_temp_and_pressure;
+ st->client = spi;
+
+ return ms5611_probe(indio_dev, &spi->dev, spi_get_device_id(spi)->name,
+ spi_get_device_id(spi)->driver_data);
+}
+
+static const struct of_device_id ms5611_spi_matches[] = {
+ { .compatible = "meas,ms5611" },
+ { .compatible = "meas,ms5607" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ms5611_spi_matches);
+
+static const struct spi_device_id ms5611_id[] = {
+ { .name = "ms5611", .driver_data = MS5611 },
+ { .name = "ms5607", .driver_data = MS5607 },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, ms5611_id);
+
+static struct spi_driver ms5611_driver = {
+ .driver = {
+ .name = "ms5611",
+ .of_match_table = ms5611_spi_matches
+ },
+ .id_table = ms5611_id,
+ .probe = ms5611_spi_probe,
+};
+module_spi_driver(ms5611_driver);
+
+MODULE_AUTHOR("Tomasz Duszynski <tduszyns@gmail.com>");
+MODULE_DESCRIPTION("MS5611 spi driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_MS5611");
diff --git a/drivers/iio/pressure/ms5637.c b/drivers/iio/pressure/ms5637.c
new file mode 100644
index 000000000..be8921644
--- /dev/null
+++ b/drivers/iio/pressure/ms5637.c
@@ -0,0 +1,250 @@
+// 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
+ *
+ * (7-bit I2C slave address 0x76)
+ *
+ * Datasheet:
+ * http://www.meas-spec.com/downloads/MS5637-02BA03.pdf
+ * Datasheet:
+ * http://www.meas-spec.com/downloads/MS5805-02BA01.pdf
+ * Datasheet:
+ * http://www.meas-spec.com/downloads/MS5837-30BA.pdf
+ * Datasheet:
+ * http://www.meas-spec.com/downloads/MS8607-02BA01.pdf
+ */
+
+#include <linux/init.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/stat.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/mutex.h>
+
+#include "../common/ms_sensors/ms_sensors_i2c.h"
+
+struct ms_tp_data {
+ const char *name;
+ const struct ms_tp_hw_data *hw;
+};
+
+static const int ms5637_samp_freq[6] = { 960, 480, 240, 120, 60, 30 };
+
+static ssize_t ms5637_show_samp_freq(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *indio_dev = dev_to_iio_dev(dev);
+ struct ms_tp_dev *dev_data = iio_priv(indio_dev);
+ int i, len = 0;
+
+ for (i = 0; i <= dev_data->hw->max_res_index; i++)
+ len += sysfs_emit_at(buf, len, "%u ", ms5637_samp_freq[i]);
+ sysfs_emit_at(buf, len - 1, "\n");
+
+ return len;
+}
+
+static int ms5637_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *channel, int *val,
+ int *val2, long mask)
+{
+ int ret;
+ int temperature;
+ unsigned int pressure;
+ struct ms_tp_dev *dev_data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ ret = ms_sensors_read_temp_and_pressure(dev_data,
+ &temperature,
+ &pressure);
+ if (ret)
+ return ret;
+
+ switch (channel->type) {
+ case IIO_TEMP: /* in milli °C */
+ *val = temperature;
+
+ return IIO_VAL_INT;
+ case IIO_PRESSURE: /* in kPa */
+ *val = pressure / 1000;
+ *val2 = (pressure % 1000) * 1000;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = ms5637_samp_freq[dev_data->res_index];
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int ms5637_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct ms_tp_dev *dev_data = iio_priv(indio_dev);
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ i = ARRAY_SIZE(ms5637_samp_freq);
+ while (i-- > 0)
+ if (val == ms5637_samp_freq[i])
+ break;
+ if (i < 0)
+ return -EINVAL;
+ dev_data->res_index = i;
+
+ return 0;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec ms5637_channels[] = {
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ },
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ }
+};
+
+static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(ms5637_show_samp_freq);
+
+static struct attribute *ms5637_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group ms5637_attribute_group = {
+ .attrs = ms5637_attributes,
+};
+
+static const struct iio_info ms5637_info = {
+ .read_raw = ms5637_read_raw,
+ .write_raw = ms5637_write_raw,
+ .attrs = &ms5637_attribute_group,
+};
+
+static int ms5637_probe(struct i2c_client *client)
+{
+ const struct ms_tp_data *data;
+ struct ms_tp_dev *dev_data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter,
+ I2C_FUNC_SMBUS_READ_WORD_DATA |
+ I2C_FUNC_SMBUS_WRITE_BYTE |
+ I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ dev_err(&client->dev,
+ "Adapter does not support some i2c transaction\n");
+ return -EOPNOTSUPP;
+ }
+
+ data = i2c_get_match_data(client);
+ if (!data)
+ return -EINVAL;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ dev_data = iio_priv(indio_dev);
+ dev_data->client = client;
+ dev_data->res_index = data->hw->max_res_index;
+ dev_data->hw = data->hw;
+ mutex_init(&dev_data->lock);
+
+ indio_dev->info = &ms5637_info;
+ indio_dev->name = data->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = ms5637_channels;
+ indio_dev->num_channels = ARRAY_SIZE(ms5637_channels);
+
+ i2c_set_clientdata(client, indio_dev);
+
+ ret = ms_sensors_reset(client, 0x1E, 3000);
+ if (ret)
+ return ret;
+
+ ret = ms_sensors_tp_read_prom(dev_data);
+ if (ret)
+ return ret;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct ms_tp_hw_data ms5637_hw_data = {
+ .prom_len = 7,
+ .max_res_index = 5
+};
+
+static const struct ms_tp_hw_data ms5803_hw_data = {
+ .prom_len = 8,
+ .max_res_index = 4
+};
+
+static const struct ms_tp_data ms5637_data = { .name = "ms5637", .hw = &ms5637_hw_data };
+
+static const struct ms_tp_data ms5803_data = { .name = "ms5803", .hw = &ms5803_hw_data };
+
+static const struct ms_tp_data ms5805_data = { .name = "ms5805", .hw = &ms5637_hw_data };
+
+static const struct ms_tp_data ms5837_data = { .name = "ms5837", .hw = &ms5637_hw_data };
+
+static const struct ms_tp_data ms8607_data = {
+ .name = "ms8607-temppressure",
+ .hw = &ms5637_hw_data,
+};
+
+static const struct i2c_device_id ms5637_id[] = {
+ { .name = "ms5637", .driver_data = (kernel_ulong_t)&ms5637_data },
+ { .name = "ms5805", .driver_data = (kernel_ulong_t)&ms5805_data },
+ { .name = "ms5837", .driver_data = (kernel_ulong_t)&ms5837_data },
+ { .name = "ms8607-temppressure", .driver_data = (kernel_ulong_t)&ms8607_data },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, ms5637_id);
+
+static const struct of_device_id ms5637_of_match[] = {
+ { .compatible = "meas,ms5637", .data = &ms5637_data },
+ { .compatible = "meas,ms5803", .data = &ms5803_data },
+ { .compatible = "meas,ms5805", .data = &ms5805_data },
+ { .compatible = "meas,ms5837", .data = &ms5837_data },
+ { .compatible = "meas,ms8607-temppressure", .data = &ms8607_data },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ms5637_of_match);
+
+static struct i2c_driver ms5637_driver = {
+ .probe = ms5637_probe,
+ .id_table = ms5637_id,
+ .driver = {
+ .name = "ms5637",
+ .of_match_table = ms5637_of_match,
+ },
+};
+
+module_i2c_driver(ms5637_driver);
+
+MODULE_DESCRIPTION("Measurement-Specialties ms5637 temperature & pressure driver");
+MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
+MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_MEAS_SPEC_SENSORS");
diff --git a/drivers/iio/pressure/rohm-bm1390.c b/drivers/iio/pressure/rohm-bm1390.c
new file mode 100644
index 000000000..f9deb48e6
--- /dev/null
+++ b/drivers/iio/pressure/rohm-bm1390.c
@@ -0,0 +1,922 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * BM1390 ROHM pressure sensor
+ *
+ * Copyright (c) 2023, ROHM Semiconductor.
+ * https://fscdn.rohm.com/en/products/databook/datasheet/ic/sensor/pressure/bm1390glv-z-e.pdf
+ */
+
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/device.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+
+#define BM1390_REG_MANUFACT_ID 0x0f
+#define BM1390_REG_PART_ID 0x10
+#define BM1390_REG_POWER 0x12
+#define BM1390_MASK_POWER BIT(0)
+#define BM1390_POWER_ON BM1390_MASK_POWER
+#define BM1390_POWER_OFF 0x00
+#define BM1390_REG_RESET 0x13
+#define BM1390_MASK_RESET BIT(0)
+#define BM1390_RESET_RELEASE BM1390_MASK_RESET
+#define BM1390_RESET 0x00
+#define BM1390_REG_MODE_CTRL 0x14
+#define BM1390_MASK_MEAS_MODE GENMASK(1, 0)
+#define BM1390_MASK_DRDY_EN BIT(4)
+#define BM1390_MASK_WMI_EN BIT(2)
+#define BM1390_MASK_AVE_NUM GENMASK(7, 5)
+
+/*
+ * Data-sheet states that when the IIR is used, the AVE_NUM must be set to
+ * value 110b
+ */
+#define BM1390_IIR_AVE_NUM 0x06
+#define BM1390_REG_FIFO_CTRL 0x15
+#define BM1390_MASK_IIR_MODE GENMASK(1, 0)
+#define BM1390_IIR_MODE_OFF 0x0
+#define BM1390_IIR_MODE_WEAK 0x1
+#define BM1390_IIR_MODE_MID 0x2
+#define BM1390_IIR_MODE_STRONG 0x3
+
+#define BM1390_MASK_FIFO_LEN BIT(6)
+#define BM1390_MASK_FIFO_EN BIT(7)
+#define BM1390_WMI_MIN 2
+#define BM1390_WMI_MAX 3
+
+#define BM1390_REG_FIFO_LVL 0x18
+#define BM1390_MASK_FIFO_LVL GENMASK(2, 0)
+#define BM1390_REG_STATUS 0x19
+#define BM1390_REG_PRESSURE_BASE 0x1a
+#define BM1390_REG_TEMP_HI 0x1d
+#define BM1390_REG_TEMP_LO 0x1e
+#define BM1390_MAX_REGISTER BM1390_REG_TEMP_LO
+
+#define BM1390_ID 0x34
+
+/* Regmap configs */
+static const struct regmap_range bm1390_volatile_ranges[] = {
+ {
+ .range_min = BM1390_REG_STATUS,
+ .range_max = BM1390_REG_STATUS,
+ },
+ {
+ .range_min = BM1390_REG_FIFO_LVL,
+ .range_max = BM1390_REG_TEMP_LO,
+ },
+};
+
+static const struct regmap_access_table bm1390_volatile_regs = {
+ .yes_ranges = &bm1390_volatile_ranges[0],
+ .n_yes_ranges = ARRAY_SIZE(bm1390_volatile_ranges),
+};
+
+static const struct regmap_range bm1390_precious_ranges[] = {
+ {
+ .range_min = BM1390_REG_STATUS,
+ .range_max = BM1390_REG_STATUS,
+ },
+};
+
+static const struct regmap_access_table bm1390_precious_regs = {
+ .yes_ranges = &bm1390_precious_ranges[0],
+ .n_yes_ranges = ARRAY_SIZE(bm1390_precious_ranges),
+};
+
+static const struct regmap_range bm1390_read_only_ranges[] = {
+ {
+ .range_min = BM1390_REG_MANUFACT_ID,
+ .range_max = BM1390_REG_PART_ID,
+ }, {
+ .range_min = BM1390_REG_FIFO_LVL,
+ .range_max = BM1390_REG_TEMP_LO,
+ },
+};
+
+static const struct regmap_access_table bm1390_ro_regs = {
+ .no_ranges = &bm1390_read_only_ranges[0],
+ .n_no_ranges = ARRAY_SIZE(bm1390_read_only_ranges),
+};
+
+static const struct regmap_range bm1390_noinc_read_ranges[] = {
+ {
+ .range_min = BM1390_REG_PRESSURE_BASE,
+ .range_max = BM1390_REG_TEMP_LO,
+ },
+};
+
+static const struct regmap_access_table bm1390_nir_regs = {
+ .yes_ranges = &bm1390_noinc_read_ranges[0],
+ .n_yes_ranges = ARRAY_SIZE(bm1390_noinc_read_ranges),
+};
+
+static const struct regmap_config bm1390_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .volatile_table = &bm1390_volatile_regs,
+ .wr_table = &bm1390_ro_regs,
+ .rd_noinc_table = &bm1390_nir_regs,
+ .precious_table = &bm1390_precious_regs,
+ .max_register = BM1390_MAX_REGISTER,
+ .cache_type = REGCACHE_RBTREE,
+ .disable_locking = true,
+};
+
+enum {
+ BM1390_STATE_SAMPLE,
+ BM1390_STATE_FIFO,
+};
+
+struct bm1390_data_buf {
+ u32 pressure;
+ __be16 temp;
+ aligned_s64 ts;
+};
+
+/* BM1390 has FIFO for 4 pressure samples */
+#define BM1390_FIFO_LENGTH 4
+
+struct bm1390_data {
+ s64 timestamp, old_timestamp;
+ struct iio_trigger *trig;
+ struct regmap *regmap;
+ struct device *dev;
+ struct bm1390_data_buf buf;
+ int irq;
+ unsigned int state;
+ bool trigger_enabled;
+ u8 watermark;
+
+ /* Prevent accessing sensor during FIFO read sequence */
+ struct mutex mutex;
+};
+
+enum {
+ BM1390_CHAN_PRESSURE,
+ BM1390_CHAN_TEMP,
+};
+
+static const struct iio_chan_spec bm1390_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ /*
+ * When IIR is used, we must fix amount of averaged samples.
+ * Thus we don't allow setting oversampling ratio.
+ */
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = BM1390_CHAN_PRESSURE,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 22,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
+ .scan_index = BM1390_CHAN_TEMP,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_BE,
+ },
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+/*
+ * We can't skip reading the pressure because the watermark IRQ is acked
+ * only when the pressure data is read from the FIFO.
+ */
+static const unsigned long bm1390_scan_masks[] = {
+ BIT(BM1390_CHAN_PRESSURE),
+ BIT(BM1390_CHAN_PRESSURE) | BIT(BM1390_CHAN_TEMP),
+ 0
+};
+
+static int bm1390_read_temp(struct bm1390_data *data, int *temp)
+{
+ __be16 temp_raw;
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BM1390_REG_TEMP_HI, &temp_raw,
+ sizeof(temp_raw));
+ if (ret)
+ return ret;
+
+ *temp = be16_to_cpu(temp_raw);
+
+ return 0;
+}
+
+static int bm1390_pressure_read(struct bm1390_data *data, u32 *pressure)
+{
+ /* Pressure data is in 3 8-bit registers */
+ u8 raw[3];
+ int ret;
+
+ ret = regmap_bulk_read(data->regmap, BM1390_REG_PRESSURE_BASE,
+ raw, sizeof(raw));
+ if (ret < 0)
+ return ret;
+
+ *pressure = (u32)(raw[2] >> 2 | raw[1] << 6 | raw[0] << 14);
+
+ return 0;
+}
+
+ /* The enum values map directly to register bits */
+enum bm1390_meas_mode {
+ BM1390_MEAS_MODE_STOP = 0x0,
+ BM1390_MEAS_MODE_1SHOT = 0x1,
+ BM1390_MEAS_MODE_CONTINUOUS = 0x2,
+};
+
+static int bm1390_meas_set(struct bm1390_data *data, enum bm1390_meas_mode mode)
+{
+ return regmap_update_bits(data->regmap, BM1390_REG_MODE_CTRL,
+ BM1390_MASK_MEAS_MODE, mode);
+}
+
+/*
+ * If the trigger is not used we just wait until the measurement has
+ * completed. The data-sheet says maximum measurement cycle (regardless
+ * the AVE_NUM) is 200 mS so let's just sleep at least that long. If speed
+ * is needed the trigger should be used.
+ */
+#define BM1390_MAX_MEAS_TIME_MS 205
+
+static int bm1390_read_data(struct bm1390_data *data,
+ struct iio_chan_spec const *chan, int *val, int *val2)
+{
+ int ret, warn;
+
+ guard(mutex)(&data->mutex);
+ /*
+ * We use 'continuous mode' even for raw read because according to the
+ * data-sheet an one-shot mode can't be used with IIR filter.
+ */
+ ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS);
+ if (ret)
+ return ret;
+
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ msleep(BM1390_MAX_MEAS_TIME_MS);
+ ret = bm1390_pressure_read(data, val);
+ break;
+ case IIO_TEMP:
+ msleep(BM1390_MAX_MEAS_TIME_MS);
+ ret = bm1390_read_temp(data, val);
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ warn = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP);
+ if (warn)
+ dev_warn(data->dev, "Failed to stop measurement (%d)\n", warn);
+
+ return ret;
+}
+
+static int bm1390_read_raw(struct iio_dev *idev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct bm1390_data *data = iio_priv(idev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_SCALE:
+ if (chan->type == IIO_TEMP) {
+ *val = 31;
+ *val2 = 250000;
+
+ return IIO_VAL_INT_PLUS_MICRO;
+ } else if (chan->type == IIO_PRESSURE) {
+ /*
+ * pressure in hPa is register value divided by 2048.
+ * This means kPa is 1/20480 times the register value,
+ */
+ *val = 1;
+ *val2 = 2048;
+
+ return IIO_VAL_FRACTIONAL;
+ }
+
+ return -EINVAL;
+ case IIO_CHAN_INFO_RAW:
+ if (!iio_device_claim_direct(idev))
+ return -EBUSY;
+
+ ret = bm1390_read_data(data, chan, val, val2);
+ iio_device_release_direct(idev);
+ if (ret)
+ return ret;
+
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int __bm1390_fifo_flush(struct iio_dev *idev, unsigned int samples,
+ s64 timestamp)
+{
+ /* BM1390_FIFO_LENGTH is small so we shouldn't run out of stack */
+ struct bm1390_data_buf buffer[BM1390_FIFO_LENGTH];
+ struct bm1390_data *data = iio_priv(idev);
+ int smp_lvl, ret, i, warn, dummy;
+ u64 sample_period;
+ __be16 temp = 0;
+
+ ret = regmap_read(data->regmap, BM1390_REG_FIFO_LVL, &smp_lvl);
+ if (ret)
+ return ret;
+
+ smp_lvl = FIELD_GET(BM1390_MASK_FIFO_LVL, smp_lvl);
+ if (!smp_lvl)
+ return 0;
+
+ if (smp_lvl > BM1390_FIFO_LENGTH) {
+ /*
+ * The fifo holds maximum of 4 samples so valid values
+ * should be 0, 1, 2, 3, 4 - rest are probably bit errors
+ * in I2C line. Don't overflow if this happens.
+ */
+ dev_err(data->dev, "bad FIFO level %d\n", smp_lvl);
+ smp_lvl = BM1390_FIFO_LENGTH;
+ }
+
+ sample_period = timestamp - data->old_timestamp;
+ do_div(sample_period, smp_lvl);
+
+ if (samples && smp_lvl > samples)
+ smp_lvl = samples;
+
+
+ /*
+ * After some testing it appears that the temperature is not readable
+ * until the FIFO access has been done after the WMI. Thus, we need
+ * to read the all pressure values to memory and read the temperature
+ * only after that.
+ */
+ for (i = 0; i < smp_lvl; i++) {
+ /*
+ * When we start reading data from the FIFO the sensor goes to
+ * special FIFO reading mode. If any other register is accessed
+ * during the FIFO read, samples can be dropped. Prevent access
+ * until FIFO_LVL is read. We have mutex locked and we do also
+ * go performing reading of FIFO_LVL even if this read fails.
+ */
+ if (test_bit(BM1390_CHAN_PRESSURE, idev->active_scan_mask)) {
+ ret = bm1390_pressure_read(data, &buffer[i].pressure);
+ if (ret)
+ break;
+ }
+
+ /*
+ * Old timestamp is either the previous sample IRQ time,
+ * previous flush-time or, if this was first sample, the enable
+ * time. When we add a sample period to that we should get the
+ * best approximation of the time-stamp we are handling.
+ *
+ * Idea is to always keep the "old_timestamp" matching the
+ * timestamp which we are currently handling.
+ */
+ data->old_timestamp += sample_period;
+ buffer[i].ts = data->old_timestamp;
+ }
+ /* Reading the FIFO_LVL closes the FIFO access sequence */
+ warn = regmap_read(data->regmap, BM1390_REG_FIFO_LVL, &dummy);
+ if (warn)
+ dev_warn(data->dev, "Closing FIFO sequence failed\n");
+
+ if (ret)
+ return ret;
+
+ if (test_bit(BM1390_CHAN_TEMP, idev->active_scan_mask)) {
+ ret = regmap_bulk_read(data->regmap, BM1390_REG_TEMP_HI, &temp,
+ sizeof(temp));
+ if (ret)
+ return ret;
+ }
+
+ for (i = 0; i < smp_lvl; i++) {
+ buffer[i].temp = temp;
+ iio_push_to_buffers(idev, &buffer[i]);
+ }
+
+ return smp_lvl;
+}
+
+static int bm1390_fifo_flush(struct iio_dev *idev, unsigned int samples)
+{
+ struct bm1390_data *data = iio_priv(idev);
+ s64 timestamp;
+ int ret;
+
+ /*
+ * If fifo_flush is being called from IRQ handler we know the stored
+ * timestamp is fairly accurate for the last stored sample. If we are
+ * called as a result of a read operation from userspace and hence
+ * before the watermark interrupt was triggered, take a timestamp
+ * now. We can fall anywhere in between two samples so the error in this
+ * case is at most one sample period.
+ * We need to have the IRQ disabled or we risk of messing-up
+ * the timestamps. If we are ran from IRQ, then the
+ * IRQF_ONESHOT has us covered - but if we are ran by the
+ * user-space read we need to disable the IRQ to be on a safe
+ * side. We do this using synchronous disable so that if the
+ * IRQ thread is being ran on other CPU we wait for it to be
+ * finished.
+ */
+
+ timestamp = iio_get_time_ns(idev);
+ mutex_lock(&data->mutex);
+ ret = __bm1390_fifo_flush(idev, samples, timestamp);
+ mutex_unlock(&data->mutex);
+
+ return ret;
+}
+
+static int bm1390_set_watermark(struct iio_dev *idev, unsigned int val)
+{
+ struct bm1390_data *data = iio_priv(idev);
+
+ if (val < BM1390_WMI_MIN || val > BM1390_WMI_MAX)
+ return -EINVAL;
+
+ mutex_lock(&data->mutex);
+ data->watermark = val;
+ mutex_unlock(&data->mutex);
+
+ return 0;
+}
+
+static const struct iio_info bm1390_noirq_info = {
+ .read_raw = &bm1390_read_raw,
+};
+
+static const struct iio_info bm1390_info = {
+ .read_raw = &bm1390_read_raw,
+ .hwfifo_set_watermark = bm1390_set_watermark,
+ .hwfifo_flush_to_buffer = bm1390_fifo_flush,
+};
+
+static int bm1390_chip_init(struct bm1390_data *data)
+{
+ int ret;
+
+ ret = regmap_write_bits(data->regmap, BM1390_REG_POWER,
+ BM1390_MASK_POWER, BM1390_POWER_ON);
+ if (ret)
+ return ret;
+
+ msleep(1);
+
+ ret = regmap_write_bits(data->regmap, BM1390_REG_RESET,
+ BM1390_MASK_RESET, BM1390_RESET);
+ if (ret)
+ return ret;
+
+ msleep(1);
+
+ ret = regmap_write_bits(data->regmap, BM1390_REG_RESET,
+ BM1390_MASK_RESET, BM1390_RESET_RELEASE);
+ if (ret)
+ return ret;
+
+ msleep(1);
+
+ ret = regmap_reinit_cache(data->regmap, &bm1390_regmap);
+ if (ret) {
+ dev_err(data->dev, "Failed to reinit reg cache\n");
+ return ret;
+ }
+
+ /*
+ * Default to use IIR filter in "middle" mode. Also the AVE_NUM must
+ * be fixed when IIR is in use.
+ */
+ ret = regmap_update_bits(data->regmap, BM1390_REG_MODE_CTRL,
+ BM1390_MASK_AVE_NUM, BM1390_IIR_AVE_NUM);
+ if (ret)
+ return ret;
+
+ return regmap_update_bits(data->regmap, BM1390_REG_FIFO_CTRL,
+ BM1390_MASK_IIR_MODE, BM1390_IIR_MODE_MID);
+}
+
+static int bm1390_fifo_set_wmi(struct bm1390_data *data)
+{
+ u8 regval;
+
+ regval = FIELD_PREP(BM1390_MASK_FIFO_LEN,
+ data->watermark - BM1390_WMI_MIN);
+
+ return regmap_update_bits(data->regmap, BM1390_REG_FIFO_CTRL,
+ BM1390_MASK_FIFO_LEN, regval);
+}
+
+static int bm1390_fifo_enable(struct iio_dev *idev)
+{
+ struct bm1390_data *data = iio_priv(idev);
+ int ret;
+
+ /* We can't do buffered stuff without IRQ as we never get WMI */
+ if (data->irq <= 0)
+ return -EINVAL;
+
+ guard(mutex)(&data->mutex);
+
+ if (data->trigger_enabled)
+ return -EBUSY;
+
+ /* Update watermark to HW */
+ ret = bm1390_fifo_set_wmi(data);
+ if (ret)
+ return ret;
+
+ /* Enable WMI_IRQ */
+ ret = regmap_set_bits(data->regmap, BM1390_REG_MODE_CTRL,
+ BM1390_MASK_WMI_EN);
+ if (ret)
+ return ret;
+
+ /* Enable FIFO */
+ ret = regmap_set_bits(data->regmap, BM1390_REG_FIFO_CTRL,
+ BM1390_MASK_FIFO_EN);
+ if (ret)
+ return ret;
+
+ data->state = BM1390_STATE_FIFO;
+
+ data->old_timestamp = iio_get_time_ns(idev);
+
+ return bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS);
+}
+
+static int bm1390_fifo_disable(struct iio_dev *idev)
+{
+ struct bm1390_data *data = iio_priv(idev);
+ int ret;
+
+ msleep(1);
+
+ guard(mutex)(&data->mutex);
+ ret = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP);
+ if (ret)
+ return ret;
+
+ /* Disable FIFO */
+ ret = regmap_clear_bits(data->regmap, BM1390_REG_FIFO_CTRL,
+ BM1390_MASK_FIFO_EN);
+ if (ret)
+ return ret;
+
+ data->state = BM1390_STATE_SAMPLE;
+
+ /* Disable WMI_IRQ */
+ return regmap_clear_bits(data->regmap, BM1390_REG_MODE_CTRL,
+ BM1390_MASK_WMI_EN);
+}
+
+static int bm1390_buffer_postenable(struct iio_dev *idev)
+{
+ /*
+ * If we use data-ready trigger, then the IRQ masks should be handled by
+ * trigger enable and the hardware buffer is not used but we just update
+ * results to the IIO FIFO when data-ready triggers.
+ */
+ if (iio_device_get_current_mode(idev) == INDIO_BUFFER_TRIGGERED)
+ return 0;
+
+ return bm1390_fifo_enable(idev);
+}
+
+static int bm1390_buffer_predisable(struct iio_dev *idev)
+{
+ if (iio_device_get_current_mode(idev) == INDIO_BUFFER_TRIGGERED)
+ return 0;
+
+ return bm1390_fifo_disable(idev);
+}
+
+static const struct iio_buffer_setup_ops bm1390_buffer_ops = {
+ .postenable = bm1390_buffer_postenable,
+ .predisable = bm1390_buffer_predisable,
+};
+
+static bool bm1390_handle_trigger(struct iio_dev *idev)
+{
+ struct bm1390_data *data = iio_priv(idev);
+ int ret, status;
+
+ /* DRDY is acked by reading status reg */
+ ret = regmap_read(data->regmap, BM1390_REG_STATUS, &status);
+ if (ret || !status)
+ return false;
+
+ dev_dbg(data->dev, "DRDY trig status 0x%x\n", status);
+
+ if (test_bit(BM1390_CHAN_PRESSURE, idev->active_scan_mask)) {
+ ret = bm1390_pressure_read(data, &data->buf.pressure);
+ if (ret) {
+ dev_warn(data->dev, "sample read failed %d\n", ret);
+ return false;
+ }
+ }
+
+ if (test_bit(BM1390_CHAN_TEMP, idev->active_scan_mask)) {
+ ret = regmap_bulk_read(data->regmap, BM1390_REG_TEMP_HI,
+ &data->buf.temp, sizeof(data->buf.temp));
+ if (ret) {
+ dev_warn(data->dev, "temp read failed %d\n", ret);
+ return true;
+ }
+ }
+
+ iio_push_to_buffers_with_ts(idev, &data->buf, sizeof(data->buf),
+ data->timestamp);
+
+ return true;
+}
+
+static irqreturn_t bm1390_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *idev = pf->indio_dev;
+ bool result;
+
+ result = bm1390_handle_trigger(idev);
+ iio_trigger_notify_done(idev->trig);
+
+ return IRQ_RETVAL(result);
+}
+
+/* Get timestamps and wake the thread if we need to read data */
+static irqreturn_t bm1390_irq_handler(int irq, void *private)
+{
+ struct iio_dev *idev = private;
+ struct bm1390_data *data = iio_priv(idev);
+
+ data->timestamp = iio_get_time_ns(idev);
+
+ if (data->state == BM1390_STATE_FIFO || data->trigger_enabled)
+ return IRQ_WAKE_THREAD;
+
+ return IRQ_NONE;
+}
+
+static irqreturn_t bm1390_irq_thread_handler(int irq, void *private)
+{
+ struct iio_dev *idev = private;
+ struct bm1390_data *data = iio_priv(idev);
+
+ guard(mutex)(&data->mutex);
+
+ if (data->trigger_enabled) {
+ iio_trigger_poll_nested(data->trig);
+ return IRQ_HANDLED;
+ }
+
+ if (data->state == BM1390_STATE_FIFO) {
+ int ok;
+
+ ok = __bm1390_fifo_flush(idev, BM1390_FIFO_LENGTH,
+ data->timestamp);
+ if (ok > 0)
+ return IRQ_HANDLED;
+ }
+
+ return IRQ_NONE;
+}
+
+static int bm1390_set_drdy_irq(struct bm1390_data *data, bool en)
+{
+ if (en)
+ return regmap_set_bits(data->regmap, BM1390_REG_MODE_CTRL,
+ BM1390_MASK_DRDY_EN);
+ return regmap_clear_bits(data->regmap, BM1390_REG_MODE_CTRL,
+ BM1390_MASK_DRDY_EN);
+}
+
+static int bm1390_trigger_set_state(struct iio_trigger *trig,
+ bool state)
+{
+ struct bm1390_data *data = iio_trigger_get_drvdata(trig);
+ int ret;
+
+ guard(mutex)(&data->mutex);
+
+ if (data->trigger_enabled == state)
+ return 0;
+
+ if (data->state == BM1390_STATE_FIFO) {
+ dev_warn(data->dev, "Can't set trigger when FIFO enabled\n");
+ return -EBUSY;
+ }
+
+ data->trigger_enabled = state;
+
+ if (state) {
+ ret = bm1390_meas_set(data, BM1390_MEAS_MODE_CONTINUOUS);
+ if (ret)
+ return ret;
+ } else {
+ int dummy;
+
+ ret = bm1390_meas_set(data, BM1390_MEAS_MODE_STOP);
+ if (ret)
+ return ret;
+
+ /*
+ * We need to read the status register in order to ACK the
+ * data-ready which may have been generated just before we
+ * disabled the measurement.
+ */
+ ret = regmap_read(data->regmap, BM1390_REG_STATUS, &dummy);
+ if (ret)
+ dev_warn(data->dev, "status read failed\n");
+ }
+
+ return bm1390_set_drdy_irq(data, state);
+}
+
+static const struct iio_trigger_ops bm1390_trigger_ops = {
+ .set_trigger_state = bm1390_trigger_set_state,
+};
+
+static int bm1390_setup_buffer(struct bm1390_data *data, struct iio_dev *idev)
+{
+ int ret;
+
+ ret = devm_iio_triggered_buffer_setup(data->dev, idev,
+ &iio_pollfunc_store_time,
+ &bm1390_trigger_handler,
+ &bm1390_buffer_ops);
+
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "iio_triggered_buffer_setup FAIL\n");
+
+ idev->available_scan_masks = bm1390_scan_masks;
+
+ return 0;
+}
+
+static int bm1390_setup_trigger(struct bm1390_data *data, struct iio_dev *idev,
+ int irq)
+{
+ struct iio_trigger *itrig;
+ char *name;
+ int ret;
+
+ itrig = devm_iio_trigger_alloc(data->dev, "%sdata-rdy-dev%d", idev->name,
+ iio_device_id(idev));
+ if (!itrig)
+ return -ENOMEM;
+
+ data->trig = itrig;
+
+ itrig->ops = &bm1390_trigger_ops;
+ iio_trigger_set_drvdata(itrig, data);
+
+ name = devm_kasprintf(data->dev, GFP_KERNEL, "%s-bm1390",
+ dev_name(data->dev));
+ if (name == NULL)
+ return -ENOMEM;
+
+ ret = devm_request_threaded_irq(data->dev, irq, bm1390_irq_handler,
+ &bm1390_irq_thread_handler,
+ IRQF_ONESHOT, name, idev);
+ if (ret)
+ return ret;
+
+
+ ret = devm_iio_trigger_register(data->dev, itrig);
+ if (ret)
+ return dev_err_probe(data->dev, ret,
+ "Trigger registration failed\n");
+
+ return 0;
+}
+
+static int bm1390_probe(struct i2c_client *i2c)
+{
+ struct bm1390_data *data;
+ struct regmap *regmap;
+ struct iio_dev *idev;
+ struct device *dev;
+ unsigned int part_id;
+ int ret;
+
+ dev = &i2c->dev;
+
+ regmap = devm_regmap_init_i2c(i2c, &bm1390_regmap);
+ if (IS_ERR(regmap))
+ return dev_err_probe(dev, PTR_ERR(regmap),
+ "Failed to initialize Regmap\n");
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to get regulator\n");
+
+ ret = regmap_read(regmap, BM1390_REG_PART_ID, &part_id);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to access sensor\n");
+
+ if (part_id != BM1390_ID)
+ dev_warn(dev, "unknown device 0x%x\n", part_id);
+
+ idev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!idev)
+ return -ENOMEM;
+
+ data = iio_priv(idev);
+ data->regmap = regmap;
+ data->dev = dev;
+ data->irq = i2c->irq;
+ /*
+ * For now we just allow BM1390_WMI_MIN to BM1390_WMI_MAX and
+ * discard every other configuration when triggered mode is not used.
+ */
+ data->watermark = BM1390_WMI_MAX;
+ mutex_init(&data->mutex);
+
+ idev->channels = bm1390_channels;
+ idev->num_channels = ARRAY_SIZE(bm1390_channels);
+ idev->name = "bm1390";
+ idev->modes = INDIO_DIRECT_MODE;
+
+ ret = bm1390_chip_init(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "sensor init failed\n");
+
+ ret = bm1390_setup_buffer(data, idev);
+ if (ret)
+ return ret;
+
+ /* No trigger if we don't have IRQ for data-ready and WMI */
+ if (i2c->irq > 0) {
+ idev->info = &bm1390_info;
+ idev->modes |= INDIO_BUFFER_SOFTWARE;
+ ret = bm1390_setup_trigger(data, idev, i2c->irq);
+ if (ret)
+ return ret;
+ } else {
+ idev->info = &bm1390_noirq_info;
+ }
+
+ ret = devm_iio_device_register(dev, idev);
+ if (ret < 0)
+ return dev_err_probe(dev, ret,
+ "Unable to register iio device\n");
+
+ return 0;
+}
+
+static const struct of_device_id bm1390_of_match[] = {
+ { .compatible = "rohm,bm1390glv-z" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, bm1390_of_match);
+
+static const struct i2c_device_id bm1390_id[] = {
+ { .name = "bm1390glv-z" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, bm1390_id);
+
+static struct i2c_driver bm1390_driver = {
+ .driver = {
+ .name = "bm1390",
+ .of_match_table = bm1390_of_match,
+ /*
+ * Probing explicitly requires a few millisecond of sleep.
+ * Enabling the VDD regulator may include ramp up rates.
+ */
+ .probe_type = PROBE_PREFER_ASYNCHRONOUS,
+ },
+ .probe = bm1390_probe,
+ .id_table = bm1390_id,
+};
+module_i2c_driver(bm1390_driver);
+
+MODULE_AUTHOR("Matti Vaittinen <mazziesaccount@gmail.com>");
+MODULE_DESCRIPTION("Driver for ROHM BM1390 pressure sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/sdp500.c b/drivers/iio/pressure/sdp500.c
new file mode 100644
index 000000000..153f335eb
--- /dev/null
+++ b/drivers/iio/pressure/sdp500.c
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Driver for Sensirion sdp500 and sdp510 pressure sensors
+ *
+ * Datasheet: https://sensirion.com/resource/datasheet/sdp600
+ */
+
+#include <linux/i2c.h>
+#include <linux/crc8.h>
+#include <linux/iio/iio.h>
+#include <linux/regulator/consumer.h>
+#include <linux/unaligned.h>
+
+#define SDP500_CRC8_POLYNOMIAL 0x31 /* x8+x5+x4+1 (normalized to 0x31) */
+#define SDP500_READ_SIZE 3
+
+#define SDP500_I2C_START_MEAS 0xF1
+
+struct sdp500_data {
+ struct device *dev;
+};
+
+DECLARE_CRC8_TABLE(sdp500_crc8_table);
+
+static int sdp500_start_measurement(struct sdp500_data *data)
+{
+ struct i2c_client *client = to_i2c_client(data->dev);
+
+ return i2c_smbus_write_byte(client, SDP500_I2C_START_MEAS);
+}
+
+static const struct iio_chan_spec sdp500_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+};
+
+static int sdp500_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ int ret;
+ u8 rxbuf[SDP500_READ_SIZE];
+ u8 received_crc, calculated_crc;
+ struct sdp500_data *data = iio_priv(indio_dev);
+ struct i2c_client *client = to_i2c_client(data->dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ ret = i2c_master_recv(client, rxbuf, SDP500_READ_SIZE);
+ if (ret < 0) {
+ dev_err(data->dev, "Failed to receive data");
+ return ret;
+ }
+ if (ret != SDP500_READ_SIZE) {
+ dev_err(data->dev, "Data is received wrongly");
+ return -EIO;
+ }
+
+ received_crc = rxbuf[2];
+ calculated_crc = crc8(sdp500_crc8_table, rxbuf,
+ sizeof(rxbuf) - 1, 0x00);
+ if (received_crc != calculated_crc) {
+ dev_err(data->dev,
+ "calculated crc = 0x%.2X, received 0x%.2X",
+ calculated_crc, received_crc);
+ return -EIO;
+ }
+
+ *val = get_unaligned_be16(rxbuf);
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ *val = 1;
+ *val2 = 60;
+
+ return IIO_VAL_FRACTIONAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_info sdp500_info = {
+ .read_raw = &sdp500_read_raw,
+};
+
+static int sdp500_probe(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev;
+ struct sdp500_data *data;
+ struct device *dev = &client->dev;
+ int ret;
+ u8 rxbuf[SDP500_READ_SIZE];
+
+ ret = devm_regulator_get_enable(dev, "vdd");
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "Failed to get and enable regulator\n");
+
+ indio_dev = devm_iio_device_alloc(dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ /* has to be done before the first i2c communication */
+ crc8_populate_msb(sdp500_crc8_table, SDP500_CRC8_POLYNOMIAL);
+
+ data = iio_priv(indio_dev);
+ data->dev = dev;
+
+ indio_dev->name = "sdp500";
+ indio_dev->channels = sdp500_channels;
+ indio_dev->info = &sdp500_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->num_channels = ARRAY_SIZE(sdp500_channels);
+
+ ret = sdp500_start_measurement(data);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to start measurement");
+
+ /* First measurement is not correct, read it out to get rid of it */
+ i2c_master_recv(client, rxbuf, SDP500_READ_SIZE);
+
+ ret = devm_iio_device_register(dev, indio_dev);
+ if (ret)
+ return dev_err_probe(dev, ret, "Failed to register indio_dev");
+
+ return 0;
+}
+
+static const struct i2c_device_id sdp500_id[] = {
+ { .name = "sdp500" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, sdp500_id);
+
+static const struct of_device_id sdp500_of_match[] = {
+ { .compatible = "sensirion,sdp500" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sdp500_of_match);
+
+static struct i2c_driver sdp500_driver = {
+ .driver = {
+ .name = "sensirion,sdp500",
+ .of_match_table = sdp500_of_match,
+ },
+ .probe = sdp500_probe,
+ .id_table = sdp500_id,
+};
+module_i2c_driver(sdp500_driver);
+
+MODULE_AUTHOR("Thomas Sioutas <thomas.sioutas@prodrive-technologies.com>");
+MODULE_DESCRIPTION("Driver for Sensirion SDP500 differential pressure sensor");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/st_pressure.h b/drivers/iio/pressure/st_pressure.h
new file mode 100644
index 000000000..6e11bea78
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure.h
@@ -0,0 +1,58 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ * v. 1.0.0
+ */
+
+#ifndef ST_PRESS_H
+#define ST_PRESS_H
+
+#include <linux/types.h>
+#include <linux/iio/common/st_sensors.h>
+
+enum st_press_type {
+ LPS001WP,
+ LPS25H,
+ LPS331AP,
+ LPS22HB,
+ LPS33HW,
+ LPS35HW,
+ LPS22HH,
+ LPS22DF,
+ ST_PRESS_MAX,
+};
+
+#define LPS001WP_PRESS_DEV_NAME "lps001wp"
+#define LPS25H_PRESS_DEV_NAME "lps25h"
+#define LPS331AP_PRESS_DEV_NAME "lps331ap"
+#define LPS22HB_PRESS_DEV_NAME "lps22hb"
+#define LPS33HW_PRESS_DEV_NAME "lps33hw"
+#define LPS35HW_PRESS_DEV_NAME "lps35hw"
+#define LPS22HH_PRESS_DEV_NAME "lps22hh"
+#define LPS22DF_PRESS_DEV_NAME "lps22df"
+
+/**
+ * struct st_sensors_platform_data - default press platform data
+ * @drdy_int_pin: default press DRDY is available on INT1 pin.
+ */
+static __maybe_unused const struct st_sensors_platform_data default_press_pdata = {
+ .drdy_int_pin = 1,
+};
+
+#ifdef CONFIG_IIO_BUFFER
+int st_press_allocate_ring(struct iio_dev *indio_dev);
+int st_press_trig_set_state(struct iio_trigger *trig, bool state);
+#define ST_PRESS_TRIGGER_SET_STATE (&st_press_trig_set_state)
+#else /* CONFIG_IIO_BUFFER */
+static inline int st_press_allocate_ring(struct iio_dev *indio_dev)
+{
+ return 0;
+}
+#define ST_PRESS_TRIGGER_SET_STATE NULL
+#endif /* CONFIG_IIO_BUFFER */
+
+#endif /* ST_PRESS_H */
diff --git a/drivers/iio/pressure/st_pressure_buffer.c b/drivers/iio/pressure/st_pressure_buffer.c
new file mode 100644
index 000000000..0dbf357c2
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure_buffer.c
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/triggered_buffer.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include "st_pressure.h"
+
+int st_press_trig_set_state(struct iio_trigger *trig, bool state)
+{
+ struct iio_dev *indio_dev = iio_trigger_get_drvdata(trig);
+
+ return st_sensors_set_dataready_irq(indio_dev, state);
+}
+
+static int st_press_buffer_postenable(struct iio_dev *indio_dev)
+{
+ return st_sensors_set_enable(indio_dev, true);
+}
+
+static int st_press_buffer_predisable(struct iio_dev *indio_dev)
+{
+ return st_sensors_set_enable(indio_dev, false);
+}
+
+static const struct iio_buffer_setup_ops st_press_buffer_setup_ops = {
+ .postenable = &st_press_buffer_postenable,
+ .predisable = &st_press_buffer_predisable,
+};
+
+int st_press_allocate_ring(struct iio_dev *indio_dev)
+{
+ return devm_iio_triggered_buffer_setup(indio_dev->dev.parent, indio_dev,
+ NULL, &st_sensors_trigger_handler, &st_press_buffer_setup_ops);
+}
diff --git a/drivers/iio/pressure/st_pressure_core.c b/drivers/iio/pressure/st_pressure_core.c
new file mode 100644
index 000000000..b70d1cee8
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure_core.c
@@ -0,0 +1,798 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/sysfs.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/trigger.h>
+#include <linux/unaligned.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include "st_pressure.h"
+
+/*
+ * About determining pressure scaling factors
+ * ------------------------------------------
+ *
+ * Datasheets specify typical pressure sensitivity so that pressure is computed
+ * according to the following equation :
+ * pressure[mBar] = raw / sensitivity
+ * where :
+ * raw the 24 bits long raw sampled pressure
+ * sensitivity a scaling factor specified by the datasheet in LSB/mBar
+ *
+ * IIO ABI expects pressure to be expressed as kPascal, hence pressure should be
+ * computed according to :
+ * pressure[kPascal] = pressure[mBar] / 10
+ * = raw / (sensitivity * 10) (1)
+ *
+ * Finally, st_press_read_raw() returns pressure scaling factor as an
+ * IIO_VAL_INT_PLUS_NANO with a zero integral part and "gain" as decimal part.
+ * Therefore, from (1), "gain" becomes :
+ * gain = 10^9 / (sensitivity * 10)
+ * = 10^8 / sensitivity
+ *
+ * About determining temperature scaling factors and offsets
+ * ---------------------------------------------------------
+ *
+ * Datasheets specify typical temperature sensitivity and offset so that
+ * temperature is computed according to the following equation :
+ * temp[Celsius] = offset[Celsius] + (raw / sensitivity)
+ * where :
+ * raw the 16 bits long raw sampled temperature
+ * offset a constant specified by the datasheet in degree Celsius
+ * (sometimes zero)
+ * sensitivity a scaling factor specified by the datasheet in LSB/Celsius
+ *
+ * IIO ABI expects temperature to be expressed as milli degree Celsius such as
+ * user space should compute temperature according to :
+ * temp[mCelsius] = temp[Celsius] * 10^3
+ * = (offset[Celsius] + (raw / sensitivity)) * 10^3
+ * = ((offset[Celsius] * sensitivity) + raw) *
+ * (10^3 / sensitivity) (2)
+ *
+ * IIO ABI expects user space to apply offset and scaling factors to raw samples
+ * according to :
+ * temp[mCelsius] = (OFFSET + raw) * SCALE
+ * where :
+ * OFFSET an arbitrary constant exposed by device
+ * SCALE an arbitrary scaling factor exposed by device
+ *
+ * Matching OFFSET and SCALE with members of (2) gives :
+ * OFFSET = offset[Celsius] * sensitivity (3)
+ * SCALE = 10^3 / sensitivity (4)
+ *
+ * st_press_read_raw() returns temperature scaling factor as an
+ * IIO_VAL_FRACTIONAL with a 10^3 numerator and "gain2" as denominator.
+ * Therefore, from (3), "gain2" becomes :
+ * gain2 = sensitivity
+ *
+ * When declared within channel, i.e. for a non zero specified offset,
+ * st_press_read_raw() will return the latter as an IIO_VAL_FRACTIONAL such as :
+ * numerator = OFFSET * 10^3
+ * denominator = 10^3
+ * giving from (4):
+ * numerator = offset[Celsius] * 10^3 * sensitivity
+ * = offset[mCelsius] * gain2
+ */
+
+#define MCELSIUS_PER_CELSIUS 1000
+
+/* Default pressure sensitivity */
+#define ST_PRESS_LSB_PER_MBAR 4096UL
+#define ST_PRESS_KPASCAL_NANO_SCALE (100000000UL / \
+ ST_PRESS_LSB_PER_MBAR)
+
+/* Default temperature sensitivity */
+#define ST_PRESS_LSB_PER_CELSIUS 480UL
+#define ST_PRESS_MILLI_CELSIUS_OFFSET 42500UL
+
+/* FULLSCALE */
+#define ST_PRESS_FS_AVL_1100MB 1100
+#define ST_PRESS_FS_AVL_1260MB 1260
+
+#define ST_PRESS_1_OUT_XL_ADDR 0x28
+#define ST_TEMP_1_OUT_L_ADDR 0x2b
+
+/* LPS001WP pressure resolution */
+#define ST_PRESS_LPS001WP_LSB_PER_MBAR 16UL
+/* LPS001WP temperature resolution */
+#define ST_PRESS_LPS001WP_LSB_PER_CELSIUS 64UL
+/* LPS001WP pressure gain */
+#define ST_PRESS_LPS001WP_FS_AVL_PRESS_GAIN \
+ (100000000UL / ST_PRESS_LPS001WP_LSB_PER_MBAR)
+/* LPS001WP pressure and temp L addresses */
+#define ST_PRESS_LPS001WP_OUT_L_ADDR 0x28
+#define ST_TEMP_LPS001WP_OUT_L_ADDR 0x2a
+
+/* LPS25H pressure and temp L addresses */
+#define ST_PRESS_LPS25H_OUT_XL_ADDR 0x28
+#define ST_TEMP_LPS25H_OUT_L_ADDR 0x2b
+
+/* LPS22HB temperature sensitivity */
+#define ST_PRESS_LPS22HB_LSB_PER_CELSIUS 100UL
+
+static const struct iio_chan_spec st_press_1_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .address = ST_PRESS_1_OUT_XL_ADDR,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ },
+ {
+ .type = IIO_TEMP,
+ .address = ST_TEMP_1_OUT_L_ADDR,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ .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),
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2)
+};
+
+static const struct iio_chan_spec st_press_lps001wp_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .address = ST_PRESS_LPS001WP_OUT_L_ADDR,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+ {
+ .type = IIO_TEMP,
+ .address = ST_TEMP_LPS001WP_OUT_L_ADDR,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2)
+};
+
+static const struct iio_chan_spec st_press_lps22hb_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .address = ST_PRESS_1_OUT_XL_ADDR,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ },
+ {
+ .type = IIO_TEMP,
+ .address = ST_TEMP_1_OUT_L_ADDR,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate =
+ BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ },
+ IIO_CHAN_SOFT_TIMESTAMP(2)
+};
+
+static const struct st_sensor_settings st_press_sensors_settings[] = {
+ {
+ /*
+ * CUSTOM VALUES FOR LPS331AP SENSOR
+ * See LPS331AP datasheet:
+ * http://www2.st.com/resource/en/datasheet/lps331ap.pdf
+ */
+ .wai = 0xbb,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LPS331AP_PRESS_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_press_1_channels,
+ .num_ch = ARRAY_SIZE(st_press_1_channels),
+ .odr = {
+ .addr = 0x20,
+ .mask = 0x70,
+ .odr_avl = {
+ { .hz = 1, .value = 0x01 },
+ { .hz = 7, .value = 0x05 },
+ { .hz = 13, .value = 0x06 },
+ { .hz = 25, .value = 0x07 },
+ },
+ },
+ .pw = {
+ .addr = 0x20,
+ .mask = 0x80,
+ .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .fs = {
+ .addr = 0x23,
+ .mask = 0x30,
+ .fs_avl = {
+ /*
+ * Pressure and temperature sensitivity values
+ * as defined in table 3 of LPS331AP datasheet.
+ */
+ [0] = {
+ .num = ST_PRESS_FS_AVL_1260MB,
+ .gain = ST_PRESS_KPASCAL_NANO_SCALE,
+ .gain2 = ST_PRESS_LSB_PER_CELSIUS,
+ },
+ },
+ },
+ .bdu = {
+ .addr = 0x20,
+ .mask = 0x04,
+ },
+ .drdy_irq = {
+ .int1 = {
+ .addr = 0x22,
+ .mask = 0x04,
+ .addr_od = 0x22,
+ .mask_od = 0x40,
+ },
+ .int2 = {
+ .addr = 0x22,
+ .mask = 0x20,
+ .addr_od = 0x22,
+ .mask_od = 0x40,
+ },
+ .addr_ihl = 0x22,
+ .mask_ihl = 0x80,
+ .stat_drdy = {
+ .addr = ST_SENSORS_DEFAULT_STAT_ADDR,
+ .mask = 0x03,
+ },
+ },
+ .sim = {
+ .addr = 0x20,
+ .value = BIT(0),
+ },
+ .multi_read_bit = true,
+ .bootime = 2,
+ },
+ {
+ /*
+ * CUSTOM VALUES FOR LPS001WP SENSOR
+ */
+ .wai = 0xba,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LPS001WP_PRESS_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_press_lps001wp_channels,
+ .num_ch = ARRAY_SIZE(st_press_lps001wp_channels),
+ .odr = {
+ .addr = 0x20,
+ .mask = 0x30,
+ .odr_avl = {
+ { .hz = 1, .value = 0x01 },
+ { .hz = 7, .value = 0x02 },
+ { .hz = 13, .value = 0x03 },
+ },
+ },
+ .pw = {
+ .addr = 0x20,
+ .mask = 0x40,
+ .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .fs = {
+ .fs_avl = {
+ /*
+ * Pressure and temperature resolution values
+ * as defined in table 3 of LPS001WP datasheet.
+ */
+ [0] = {
+ .num = ST_PRESS_FS_AVL_1100MB,
+ .gain = ST_PRESS_LPS001WP_FS_AVL_PRESS_GAIN,
+ .gain2 = ST_PRESS_LPS001WP_LSB_PER_CELSIUS,
+ },
+ },
+ },
+ .bdu = {
+ .addr = 0x20,
+ .mask = 0x04,
+ },
+ .sim = {
+ .addr = 0x20,
+ .value = BIT(0),
+ },
+ .multi_read_bit = true,
+ .bootime = 2,
+ },
+ {
+ /*
+ * CUSTOM VALUES FOR LPS25H SENSOR
+ * See LPS25H datasheet:
+ * http://www2.st.com/resource/en/datasheet/lps25h.pdf
+ */
+ .wai = 0xbd,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LPS25H_PRESS_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_press_1_channels,
+ .num_ch = ARRAY_SIZE(st_press_1_channels),
+ .odr = {
+ .addr = 0x20,
+ .mask = 0x70,
+ .odr_avl = {
+ { .hz = 1, .value = 0x01 },
+ { .hz = 7, .value = 0x02 },
+ { .hz = 13, .value = 0x03 },
+ { .hz = 25, .value = 0x04 },
+ },
+ },
+ .pw = {
+ .addr = 0x20,
+ .mask = 0x80,
+ .value_on = ST_SENSORS_DEFAULT_POWER_ON_VALUE,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .fs = {
+ .fs_avl = {
+ /*
+ * Pressure and temperature sensitivity values
+ * as defined in table 3 of LPS25H datasheet.
+ */
+ [0] = {
+ .num = ST_PRESS_FS_AVL_1260MB,
+ .gain = ST_PRESS_KPASCAL_NANO_SCALE,
+ .gain2 = ST_PRESS_LSB_PER_CELSIUS,
+ },
+ },
+ },
+ .bdu = {
+ .addr = 0x20,
+ .mask = 0x04,
+ },
+ .drdy_irq = {
+ .int1 = {
+ .addr = 0x23,
+ .mask = 0x01,
+ .addr_od = 0x22,
+ .mask_od = 0x40,
+ },
+ .addr_ihl = 0x22,
+ .mask_ihl = 0x80,
+ .stat_drdy = {
+ .addr = ST_SENSORS_DEFAULT_STAT_ADDR,
+ .mask = 0x03,
+ },
+ },
+ .sim = {
+ .addr = 0x20,
+ .value = BIT(0),
+ },
+ .multi_read_bit = true,
+ .bootime = 2,
+ },
+ {
+ /*
+ * CUSTOM VALUES FOR LPS22HB SENSOR
+ * See LPS22HB datasheet:
+ * http://www2.st.com/resource/en/datasheet/lps22hb.pdf
+ */
+ .wai = 0xb1,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LPS22HB_PRESS_DEV_NAME,
+ [1] = LPS33HW_PRESS_DEV_NAME,
+ [2] = LPS35HW_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 },
+ },
+ },
+ .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 LPS22HB 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 = 0x02,
+ },
+ .drdy_irq = {
+ .int1 = {
+ .addr = 0x12,
+ .mask = 0x04,
+ .addr_od = 0x12,
+ .mask_od = 0x40,
+ },
+ .addr_ihl = 0x12,
+ .mask_ihl = 0x80,
+ .stat_drdy = {
+ .addr = ST_SENSORS_DEFAULT_STAT_ADDR,
+ .mask = 0x03,
+ },
+ },
+ .sim = {
+ .addr = 0x10,
+ .value = BIT(0),
+ },
+ .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,
+ },
+ {
+ /*
+ * CUSTOM VALUES FOR LPS22DF SENSOR
+ * See LPS22DF datasheet:
+ * http://www.st.com/resource/en/datasheet/lps22df.pdf
+ */
+ .wai = 0xb4,
+ .wai_addr = ST_SENSORS_DEFAULT_WAI_ADDRESS,
+ .sensors_supported = {
+ [0] = LPS22DF_PRESS_DEV_NAME,
+ },
+ .ch = (struct iio_chan_spec *)st_press_lps22hb_channels,
+ .num_ch = ARRAY_SIZE(st_press_lps22hb_channels),
+ .odr = {
+ .addr = 0x10,
+ .mask = 0x78,
+ .odr_avl = {
+ { .hz = 1, .value = 0x01 },
+ { .hz = 4, .value = 0x02 },
+ { .hz = 10, .value = 0x03 },
+ { .hz = 25, .value = 0x04 },
+ { .hz = 50, .value = 0x05 },
+ { .hz = 75, .value = 0x06 },
+ { .hz = 100, .value = 0x07 },
+ { .hz = 200, .value = 0x08 },
+ },
+ },
+ .pw = {
+ .addr = 0x10,
+ .mask = 0x78,
+ .value_off = ST_SENSORS_DEFAULT_POWER_OFF_VALUE,
+ },
+ .fs = {
+ .fs_avl = {
+ /*
+ * Pressure and temperature sensitivity values
+ * as defined in table 2 of LPS22DF datasheet.
+ */
+ [0] = {
+ .num = ST_PRESS_FS_AVL_1260MB,
+ .gain = ST_PRESS_KPASCAL_NANO_SCALE,
+ .gain2 = ST_PRESS_LPS22HB_LSB_PER_CELSIUS,
+ },
+ },
+ },
+ .bdu = {
+ .addr = 0x11,
+ .mask = BIT(3),
+ },
+ .drdy_irq = {
+ .int1 = {
+ .addr = 0x13,
+ .mask = BIT(5),
+ .addr_od = 0x12,
+ .mask_od = BIT(1),
+ },
+ .addr_ihl = 0x12,
+ .mask_ihl = BIT(3),
+ .stat_drdy = {
+ .addr = ST_SENSORS_DEFAULT_STAT_ADDR,
+ .mask = 0x03,
+ },
+ },
+ .sim = {
+ .addr = 0x0E,
+ .value = BIT(5),
+ },
+ .multi_read_bit = false,
+ .bootime = 2,
+ },
+};
+
+static int st_press_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *ch,
+ int val,
+ int val2,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ if (val2)
+ return -EINVAL;
+
+ return st_sensors_set_odr(indio_dev, val);
+ default:
+ return -EINVAL;
+ }
+}
+
+static int st_press_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *ch, int *val,
+ int *val2, long mask)
+{
+ int err;
+ struct st_sensor_data *press_data = iio_priv(indio_dev);
+
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ err = st_sensors_read_info_raw(indio_dev, ch, val);
+ if (err < 0)
+ goto read_error;
+
+ return IIO_VAL_INT;
+ case IIO_CHAN_INFO_SCALE:
+ switch (ch->type) {
+ case IIO_PRESSURE:
+ *val = 0;
+ *val2 = press_data->current_fullscale->gain;
+ return IIO_VAL_INT_PLUS_NANO;
+ case IIO_TEMP:
+ *val = MCELSIUS_PER_CELSIUS;
+ *val2 = press_data->current_fullscale->gain2;
+ return IIO_VAL_FRACTIONAL;
+ default:
+ err = -EINVAL;
+ goto read_error;
+ }
+
+ case IIO_CHAN_INFO_OFFSET:
+ switch (ch->type) {
+ case IIO_TEMP:
+ *val = ST_PRESS_MILLI_CELSIUS_OFFSET *
+ press_data->current_fullscale->gain2;
+ *val2 = MCELSIUS_PER_CELSIUS;
+ break;
+ default:
+ err = -EINVAL;
+ goto read_error;
+ }
+
+ return IIO_VAL_FRACTIONAL;
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = press_data->odr;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+
+read_error:
+ return err;
+}
+
+static ST_SENSORS_DEV_ATTR_SAMP_FREQ_AVAIL();
+
+static struct attribute *st_press_attributes[] = {
+ &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
+ NULL,
+};
+
+static const struct attribute_group st_press_attribute_group = {
+ .attrs = st_press_attributes,
+};
+
+static const struct iio_info press_info = {
+ .attrs = &st_press_attribute_group,
+ .read_raw = &st_press_read_raw,
+ .write_raw = &st_press_write_raw,
+ .debugfs_reg_access = &st_sensors_debugfs_reg_access,
+};
+
+#ifdef CONFIG_IIO_TRIGGER
+static const struct iio_trigger_ops st_press_trigger_ops = {
+ .set_trigger_state = ST_PRESS_TRIGGER_SET_STATE,
+ .validate_device = st_sensors_validate_device,
+};
+#define ST_PRESS_TRIGGER_OPS (&st_press_trigger_ops)
+#else
+#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_NS(st_press_get_settings, "IIO_ST_SENSORS");
+
+int st_press_common_probe(struct iio_dev *indio_dev)
+{
+ struct st_sensor_data *press_data = iio_priv(indio_dev);
+ struct device *parent = indio_dev->dev.parent;
+ struct st_sensors_platform_data *pdata = dev_get_platdata(parent);
+ int err;
+
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->info = &press_info;
+
+ err = st_sensors_verify_id(indio_dev);
+ if (err < 0)
+ return err;
+
+ /*
+ * Skip timestamping channel while declaring available channels to
+ * common st_sensor layer. Look at st_sensors_get_buffer_element() to
+ * see how timestamps are explicitly pushed as last samples block
+ * element.
+ */
+ press_data->num_data_channels = press_data->sensor_settings->num_ch - 1;
+ indio_dev->channels = press_data->sensor_settings->ch;
+ indio_dev->num_channels = press_data->sensor_settings->num_ch;
+
+ press_data->current_fullscale = &press_data->sensor_settings->fs.fs_avl[0];
+
+ press_data->odr = press_data->sensor_settings->odr.odr_avl[0].hz;
+
+ /* Some devices don't support a data ready pin. */
+ if (!pdata && (press_data->sensor_settings->drdy_irq.int1.addr ||
+ press_data->sensor_settings->drdy_irq.int2.addr))
+ pdata = (struct st_sensors_platform_data *)&default_press_pdata;
+
+ err = st_sensors_init_sensor(indio_dev, pdata);
+ if (err < 0)
+ return err;
+
+ err = st_press_allocate_ring(indio_dev);
+ if (err < 0)
+ return err;
+
+ if (press_data->irq > 0) {
+ err = st_sensors_allocate_trigger(indio_dev,
+ ST_PRESS_TRIGGER_OPS);
+ if (err < 0)
+ return err;
+ }
+
+ return devm_iio_device_register(parent, indio_dev);
+}
+EXPORT_SYMBOL_NS(st_press_common_probe, "IIO_ST_SENSORS");
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics pressures driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_ST_SENSORS");
diff --git a/drivers/iio/pressure/st_pressure_i2c.c b/drivers/iio/pressure/st_pressure_i2c.c
new file mode 100644
index 000000000..2b7c84b7e
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure_i2c.c
@@ -0,0 +1,123 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include <linux/iio/common/st_sensors_i2c.h>
+#include "st_pressure.h"
+
+static const struct of_device_id st_press_of_match[] = {
+ {
+ .compatible = "st,lps001wp-press",
+ .data = LPS001WP_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps25h-press",
+ .data = LPS25H_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps331ap-press",
+ .data = LPS331AP_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps22hb-press",
+ .data = LPS22HB_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps33hw",
+ .data = LPS33HW_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps35hw",
+ .data = LPS35HW_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps22hh",
+ .data = LPS22HH_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps22df",
+ .data = LPS22DF_PRESS_DEV_NAME,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, st_press_of_match);
+
+static const struct acpi_device_id st_press_acpi_match[] = {
+ {"SNO9210", LPS22HB},
+ { }
+};
+MODULE_DEVICE_TABLE(acpi, st_press_acpi_match);
+
+static const struct i2c_device_id st_press_id_table[] = {
+ { .name = LPS001WP_PRESS_DEV_NAME, .driver_data = LPS001WP },
+ { .name = LPS25H_PRESS_DEV_NAME, .driver_data = LPS25H },
+ { .name = LPS331AP_PRESS_DEV_NAME, .driver_data = LPS331AP },
+ { .name = LPS22HB_PRESS_DEV_NAME, .driver_data = LPS22HB },
+ { .name = LPS33HW_PRESS_DEV_NAME, .driver_data = LPS33HW },
+ { .name = LPS35HW_PRESS_DEV_NAME, .driver_data = LPS35HW },
+ { .name = LPS22HH_PRESS_DEV_NAME, .driver_data = LPS22HH },
+ { .name = LPS22DF_PRESS_DEV_NAME, .driver_data = LPS22DF },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, st_press_id_table);
+
+static int st_press_i2c_probe(struct i2c_client *client)
+{
+ const struct st_sensor_settings *settings;
+ struct st_sensor_data *press_data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ st_sensors_dev_name_probe(&client->dev, client->name, sizeof(client->name));
+
+ 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_sensors_power_enable(indio_dev);
+ if (ret)
+ return ret;
+
+ return st_press_common_probe(indio_dev);
+}
+
+static struct i2c_driver st_press_driver = {
+ .driver = {
+ .name = "st-press-i2c",
+ .of_match_table = st_press_of_match,
+ .acpi_match_table = st_press_acpi_match,
+ },
+ .probe = st_press_i2c_probe,
+ .id_table = st_press_id_table,
+};
+module_i2c_driver(st_press_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics pressures i2c driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_ST_SENSORS");
diff --git a/drivers/iio/pressure/st_pressure_spi.c b/drivers/iio/pressure/st_pressure_spi.c
new file mode 100644
index 000000000..8add0c392
--- /dev/null
+++ b/drivers/iio/pressure/st_pressure_spi.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * STMicroelectronics pressures driver
+ *
+ * Copyright 2013 STMicroelectronics Inc.
+ *
+ * Denis Ciocca <denis.ciocca@st.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/iio/iio.h>
+
+#include <linux/iio/common/st_sensors.h>
+#include <linux/iio/common/st_sensors_spi.h>
+#include "st_pressure.h"
+
+/*
+ * For new single-chip sensors use <device_name> as compatible string.
+ * For old single-chip devices keep <device_name>-press to maintain
+ * compatibility
+ */
+static const struct of_device_id st_press_of_match[] = {
+ {
+ .compatible = "st,lps001wp-press",
+ .data = LPS001WP_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps25h-press",
+ .data = LPS25H_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps331ap-press",
+ .data = LPS331AP_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps22hb-press",
+ .data = LPS22HB_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps33hw",
+ .data = LPS33HW_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps35hw",
+ .data = LPS35HW_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps22hh",
+ .data = LPS22HH_PRESS_DEV_NAME,
+ },
+ {
+ .compatible = "st,lps22df",
+ .data = LPS22DF_PRESS_DEV_NAME,
+ },
+ { }
+};
+MODULE_DEVICE_TABLE(of, st_press_of_match);
+
+static int st_press_spi_probe(struct spi_device *spi)
+{
+ const struct st_sensor_settings *settings;
+ struct st_sensor_data *press_data;
+ struct iio_dev *indio_dev;
+ int err;
+
+ st_sensors_dev_name_probe(&spi->dev, spi->modalias, sizeof(spi->modalias));
+
+ 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_sensors_power_enable(indio_dev);
+ if (err)
+ return err;
+
+ return st_press_common_probe(indio_dev);
+}
+
+static const struct spi_device_id st_press_id_table[] = {
+ { .name = LPS001WP_PRESS_DEV_NAME },
+ { .name = LPS25H_PRESS_DEV_NAME },
+ { .name = LPS331AP_PRESS_DEV_NAME },
+ { .name = LPS22HB_PRESS_DEV_NAME },
+ { .name = LPS33HW_PRESS_DEV_NAME },
+ { .name = LPS35HW_PRESS_DEV_NAME },
+ { .name = LPS22HH_PRESS_DEV_NAME },
+ { .name = LPS22DF_PRESS_DEV_NAME },
+ { .name = "lps001wp-press" },
+ { .name = "lps25h-press" },
+ { .name = "lps331ap-press" },
+ { .name = "lps22hb-press" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, st_press_id_table);
+
+static struct spi_driver st_press_driver = {
+ .driver = {
+ .name = "st-press-spi",
+ .of_match_table = st_press_of_match,
+ },
+ .probe = st_press_spi_probe,
+ .id_table = st_press_id_table,
+};
+module_spi_driver(st_press_driver);
+
+MODULE_AUTHOR("Denis Ciocca <denis.ciocca@st.com>");
+MODULE_DESCRIPTION("STMicroelectronics pressures spi driver");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_ST_SENSORS");
diff --git a/drivers/iio/pressure/t5403.c b/drivers/iio/pressure/t5403.c
new file mode 100644
index 000000000..ab30f6745
--- /dev/null
+++ b/drivers/iio/pressure/t5403.c
@@ -0,0 +1,270 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * t5403.c - Support for EPCOS T5403 pressure/temperature sensor
+ *
+ * Copyright (c) 2014 Peter Meerwald <pmeerw@pmeerw.net>
+ *
+ * (7-bit I2C slave address 0x77)
+ *
+ * TODO: end-of-conversion irq
+ */
+
+#include <linux/module.h>
+#include <linux/i2c.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/delay.h>
+
+#define T5403_DATA 0xf5 /* data, LSB first, 16 bit */
+#define T5403_CALIB_DATA 0x8e /* 10 calibration coeff., LSB first, 16 bit */
+#define T5403_SLAVE_ADDR 0x88 /* I2C slave address, 0x77 */
+#define T5403_COMMAND 0xf1
+
+/* command bits */
+#define T5403_MODE_SHIFT 3 /* conversion time: 2, 8, 16, 66 ms */
+#define T5403_PT BIT(1) /* 0 .. pressure, 1 .. temperature measurement */
+#define T5403_SCO BIT(0) /* start conversion */
+
+#define T5403_MODE_LOW 0
+#define T5403_MODE_STANDARD 1
+#define T5403_MODE_HIGH 2
+#define T5403_MODE_ULTRA_HIGH 3
+
+#define T5403_I2C_MASK (~BIT(7))
+#define T5403_I2C_ADDR 0x77
+
+static const int t5403_pressure_conv_ms[] = {2, 8, 16, 66};
+
+struct t5403_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ int mode;
+ __le16 c[10];
+};
+
+#define T5403_C_U16(i) le16_to_cpu(data->c[(i) - 1])
+#define T5403_C(i) sign_extend32(T5403_C_U16(i), 15)
+
+static int t5403_read(struct t5403_data *data, bool pressure)
+{
+ int wait_time = 3; /* wakeup time in ms */
+
+ int ret = i2c_smbus_write_byte_data(data->client, T5403_COMMAND,
+ (pressure ? (data->mode << T5403_MODE_SHIFT) : T5403_PT) |
+ T5403_SCO);
+ if (ret < 0)
+ return ret;
+
+ wait_time += pressure ? t5403_pressure_conv_ms[data->mode] : 2;
+
+ msleep(wait_time);
+
+ return i2c_smbus_read_word_data(data->client, T5403_DATA);
+}
+
+static int t5403_comp_pressure(struct t5403_data *data, int *val, int *val2)
+{
+ int ret;
+ s16 t_r;
+ u16 p_r;
+ s32 S, O, X;
+
+ mutex_lock(&data->lock);
+
+ ret = t5403_read(data, false);
+ if (ret < 0)
+ goto done;
+ t_r = ret;
+
+ ret = t5403_read(data, true);
+ if (ret < 0)
+ goto done;
+ p_r = ret;
+
+ /* see EPCOS application note */
+ S = T5403_C_U16(3) + (s32) T5403_C_U16(4) * t_r / 0x20000 +
+ T5403_C(5) * t_r / 0x8000 * t_r / 0x80000 +
+ T5403_C(9) * t_r / 0x8000 * t_r / 0x8000 * t_r / 0x10000;
+
+ O = T5403_C(6) * 0x4000 + T5403_C(7) * t_r / 8 +
+ T5403_C(8) * t_r / 0x8000 * t_r / 16 +
+ T5403_C(9) * t_r / 0x8000 * t_r / 0x10000 * t_r;
+
+ X = (S * p_r + O) / 0x4000;
+
+ X += ((X - 75000) * (X - 75000) / 0x10000 - 9537) *
+ T5403_C(10) / 0x10000;
+
+ *val = X / 1000;
+ *val2 = (X % 1000) * 1000;
+
+done:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int t5403_comp_temp(struct t5403_data *data, int *val)
+{
+ int ret;
+ s16 t_r;
+
+ mutex_lock(&data->lock);
+ ret = t5403_read(data, false);
+ if (ret < 0)
+ goto done;
+ t_r = ret;
+
+ /* see EPCOS application note */
+ *val = ((s32) T5403_C_U16(1) * t_r / 0x100 +
+ (s32) T5403_C_U16(2) * 0x40) * 1000 / 0x10000;
+
+done:
+ mutex_unlock(&data->lock);
+ return ret;
+}
+
+static int t5403_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val, int *val2, long mask)
+{
+ struct t5403_data *data = iio_priv(indio_dev);
+ int ret;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_PROCESSED:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ ret = t5403_comp_pressure(data, val, val2);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT_PLUS_MICRO;
+ case IIO_TEMP:
+ ret = t5403_comp_temp(data, val);
+ if (ret < 0)
+ return ret;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_INT_TIME:
+ *val = 0;
+ *val2 = t5403_pressure_conv_ms[data->mode] * 1000;
+ return IIO_VAL_INT_PLUS_MICRO;
+ default:
+ return -EINVAL;
+ }
+}
+
+static int t5403_write_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int val, int val2, long mask)
+{
+ struct t5403_data *data = iio_priv(indio_dev);
+ int i;
+
+ switch (mask) {
+ case IIO_CHAN_INFO_INT_TIME:
+ if (val != 0)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(t5403_pressure_conv_ms); i++)
+ if (val2 == t5403_pressure_conv_ms[i] * 1000) {
+ mutex_lock(&data->lock);
+ data->mode = i;
+ mutex_unlock(&data->lock);
+ return 0;
+ }
+ return -EINVAL;
+ default:
+ return -EINVAL;
+ }
+}
+
+static const struct iio_chan_spec t5403_channels[] = {
+ {
+ .type = IIO_PRESSURE,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) |
+ BIT(IIO_CHAN_INFO_INT_TIME),
+ },
+ {
+ .type = IIO_TEMP,
+ .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED),
+ },
+};
+
+static IIO_CONST_ATTR_INT_TIME_AVAIL("0.002 0.008 0.016 0.066");
+
+static struct attribute *t5403_attributes[] = {
+ &iio_const_attr_integration_time_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group t5403_attribute_group = {
+ .attrs = t5403_attributes,
+};
+
+static const struct iio_info t5403_info = {
+ .read_raw = &t5403_read_raw,
+ .write_raw = &t5403_write_raw,
+ .attrs = &t5403_attribute_group,
+};
+
+static int t5403_probe(struct i2c_client *client)
+{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct t5403_data *data;
+ struct iio_dev *indio_dev;
+ int ret;
+
+ if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK))
+ return -EOPNOTSUPP;
+
+ ret = i2c_smbus_read_byte_data(client, T5403_SLAVE_ADDR);
+ if (ret < 0)
+ return ret;
+ if ((ret & T5403_I2C_MASK) != T5403_I2C_ADDR)
+ return -ENODEV;
+
+ indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+ if (!indio_dev)
+ return -ENOMEM;
+
+ data = iio_priv(indio_dev);
+ data->client = client;
+ mutex_init(&data->lock);
+
+ i2c_set_clientdata(client, indio_dev);
+ indio_dev->info = &t5403_info;
+ indio_dev->name = id->name;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = t5403_channels;
+ indio_dev->num_channels = ARRAY_SIZE(t5403_channels);
+
+ data->mode = T5403_MODE_STANDARD;
+
+ ret = i2c_smbus_read_i2c_block_data(data->client, T5403_CALIB_DATA,
+ sizeof(data->c), (u8 *) data->c);
+ if (ret < 0)
+ return ret;
+
+ return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static const struct i2c_device_id t5403_id[] = {
+ { .name = "t5403" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, t5403_id);
+
+static struct i2c_driver t5403_driver = {
+ .driver = {
+ .name = "t5403",
+ },
+ .probe = t5403_probe,
+ .id_table = t5403_id,
+};
+module_i2c_driver(t5403_driver);
+
+MODULE_AUTHOR("Peter Meerwald <pmeerw@pmeerw.net>");
+MODULE_DESCRIPTION("EPCOS T5403 pressure/temperature sensor driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/iio/pressure/zpa2326.c b/drivers/iio/pressure/zpa2326.c
new file mode 100644
index 000000000..b38493ff3
--- /dev/null
+++ b/drivers/iio/pressure/zpa2326.c
@@ -0,0 +1,1708 @@
+// 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>
+ */
+
+/**
+ * DOC: ZPA2326 theory of operations
+ *
+ * This driver supports %INDIO_DIRECT_MODE and %INDIO_BUFFER_TRIGGERED IIO
+ * modes.
+ * A internal hardware trigger is also implemented to dispatch registered IIO
+ * trigger consumers upon "sample ready" interrupts.
+ *
+ * ZPA2326 hardware supports 2 sampling mode: one shot and continuous.
+ *
+ * A complete one shot sampling cycle gets device out of low power mode,
+ * performs pressure and temperature measurements, then automatically switches
+ * back to low power mode. It is meant for on demand sampling with optimal power
+ * saving at the cost of lower sampling rate and higher software overhead.
+ * This is a natural candidate for IIO read_raw hook implementation
+ * (%INDIO_DIRECT_MODE). It is also used for triggered buffering support to
+ * ensure explicit synchronization with external trigger events
+ * (%INDIO_BUFFER_TRIGGERED).
+ *
+ * The continuous mode works according to a periodic hardware measurement
+ * process continuously pushing samples into an internal hardware FIFO (for
+ * pressure samples only). Measurement cycle completion may be signaled by a
+ * "sample ready" interrupt.
+ * Typical software sequence of operations :
+ * - get device out of low power mode,
+ * - setup hardware sampling period,
+ * - at end of period, upon data ready interrupt: pop pressure samples out of
+ * hardware FIFO and fetch temperature sample
+ * - when no longer needed, stop sampling process by putting device into
+ * low power mode.
+ * This mode is used to implement %INDIO_BUFFER_TRIGGERED mode if device tree
+ * declares a valid interrupt line. In this case, the internal hardware trigger
+ * drives acquisition.
+ *
+ * Note that hardware sampling frequency is taken into account only when
+ * internal hardware trigger is attached as the highest sampling rate seems to
+ * be the most energy efficient.
+ *
+ * TODO:
+ * preset pressure threshold crossing / IIO events ;
+ * differential pressure sampling ;
+ * hardware samples averaging.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/unaligned.h>
+#include "zpa2326.h"
+
+/* 200 ms should be enough for the longest conversion time in one-shot mode. */
+#define ZPA2326_CONVERSION_JIFFIES (HZ / 5)
+
+/* There should be a 1 ms delay (Tpup) after getting out of reset. */
+#define ZPA2326_TPUP_USEC_MIN (1000)
+#define ZPA2326_TPUP_USEC_MAX (2000)
+
+/**
+ * struct zpa2326_frequency - Hardware sampling frequency descriptor
+ * @hz : Frequency in Hertz.
+ * @odr: Output Data Rate word as expected by %ZPA2326_CTRL_REG3_REG.
+ */
+struct zpa2326_frequency {
+ int hz;
+ u16 odr;
+};
+
+/*
+ * Keep these in strict ascending order: last array entry is expected to
+ * correspond to the highest sampling frequency.
+ */
+static const struct zpa2326_frequency zpa2326_sampling_frequencies[] = {
+ { .hz = 1, .odr = 1 << ZPA2326_CTRL_REG3_ODR_SHIFT },
+ { .hz = 5, .odr = 5 << ZPA2326_CTRL_REG3_ODR_SHIFT },
+ { .hz = 11, .odr = 6 << ZPA2326_CTRL_REG3_ODR_SHIFT },
+ { .hz = 23, .odr = 7 << ZPA2326_CTRL_REG3_ODR_SHIFT },
+};
+
+/* Return the highest hardware sampling frequency available. */
+static const struct zpa2326_frequency *zpa2326_highest_frequency(void)
+{
+ return &zpa2326_sampling_frequencies[
+ ARRAY_SIZE(zpa2326_sampling_frequencies) - 1];
+}
+
+/**
+ * struct zpa2326_private - Per-device internal private state
+ * @timestamp: Buffered samples ready datum.
+ * @regmap: Underlying I2C / SPI bus adapter used to abstract slave register
+ * accesses.
+ * @result: Allows sampling logic to get completion status of operations
+ * that interrupt handlers perform asynchronously.
+ * @data_ready: Interrupt handler uses this to wake user context up at sampling
+ * operation completion.
+ * @trigger: Optional hardware / interrupt driven trigger used to notify
+ * external devices a new sample is ready.
+ * @waken: Flag indicating whether or not device has just been powered on.
+ * @irq: Optional interrupt line: negative or zero if not declared into
+ * DT, in which case sampling logic keeps polling status register
+ * to detect completion.
+ * @frequency: Current hardware sampling frequency.
+ * @vref: Power / voltage reference.
+ * @vdd: Power supply.
+ */
+struct zpa2326_private {
+ s64 timestamp;
+ struct regmap *regmap;
+ int result;
+ struct completion data_ready;
+ struct iio_trigger *trigger;
+ bool waken;
+ int irq;
+ const struct zpa2326_frequency *frequency;
+ struct regulator *vref;
+ struct regulator *vdd;
+};
+
+#define zpa2326_err(idev, fmt, ...) \
+ dev_err(idev->dev.parent, fmt "\n", ##__VA_ARGS__)
+
+#define zpa2326_warn(idev, fmt, ...) \
+ dev_warn(idev->dev.parent, fmt "\n", ##__VA_ARGS__)
+
+#define zpa2326_dbg(idev, fmt, ...) \
+ dev_dbg(idev->dev.parent, fmt "\n", ##__VA_ARGS__)
+
+bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ZPA2326_REF_P_XL_REG:
+ case ZPA2326_REF_P_L_REG:
+ case ZPA2326_REF_P_H_REG:
+ case ZPA2326_RES_CONF_REG:
+ case ZPA2326_CTRL_REG0_REG:
+ case ZPA2326_CTRL_REG1_REG:
+ case ZPA2326_CTRL_REG2_REG:
+ case ZPA2326_CTRL_REG3_REG:
+ case ZPA2326_THS_P_LOW_REG:
+ case ZPA2326_THS_P_HIGH_REG:
+ return true;
+
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(zpa2326_isreg_writeable, "IIO_ZPA2326");
+
+bool zpa2326_isreg_readable(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ZPA2326_REF_P_XL_REG:
+ case ZPA2326_REF_P_L_REG:
+ case ZPA2326_REF_P_H_REG:
+ case ZPA2326_DEVICE_ID_REG:
+ case ZPA2326_RES_CONF_REG:
+ case ZPA2326_CTRL_REG0_REG:
+ case ZPA2326_CTRL_REG1_REG:
+ case ZPA2326_CTRL_REG2_REG:
+ case ZPA2326_CTRL_REG3_REG:
+ case ZPA2326_INT_SOURCE_REG:
+ case ZPA2326_THS_P_LOW_REG:
+ case ZPA2326_THS_P_HIGH_REG:
+ case ZPA2326_STATUS_REG:
+ case ZPA2326_PRESS_OUT_XL_REG:
+ case ZPA2326_PRESS_OUT_L_REG:
+ case ZPA2326_PRESS_OUT_H_REG:
+ case ZPA2326_TEMP_OUT_L_REG:
+ case ZPA2326_TEMP_OUT_H_REG:
+ return true;
+
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(zpa2326_isreg_readable, "IIO_ZPA2326");
+
+bool zpa2326_isreg_precious(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case ZPA2326_INT_SOURCE_REG:
+ case ZPA2326_PRESS_OUT_H_REG:
+ return true;
+
+ default:
+ return false;
+ }
+}
+EXPORT_SYMBOL_NS_GPL(zpa2326_isreg_precious, "IIO_ZPA2326");
+
+/**
+ * zpa2326_enable_device() - Enable device, i.e. get out of low power mode.
+ * @indio_dev: The IIO device associated with the hardware to enable.
+ *
+ * Required to access complete register space and to perform any sampling
+ * or control operations.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_enable_device(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = regmap_write(((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap,
+ ZPA2326_CTRL_REG0_REG, ZPA2326_CTRL_REG0_ENABLE);
+ if (err) {
+ zpa2326_err(indio_dev, "failed to enable device (%d)", err);
+ return err;
+ }
+
+ zpa2326_dbg(indio_dev, "enabled");
+
+ return 0;
+}
+
+/**
+ * zpa2326_sleep() - Disable device, i.e. switch to low power mode.
+ * @indio_dev: The IIO device associated with the hardware to disable.
+ *
+ * Only %ZPA2326_DEVICE_ID_REG and %ZPA2326_CTRL_REG0_REG registers may be
+ * accessed once device is in the disabled state.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_sleep(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = regmap_write(((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap,
+ ZPA2326_CTRL_REG0_REG, 0);
+ if (err) {
+ zpa2326_err(indio_dev, "failed to sleep (%d)", err);
+ return err;
+ }
+
+ zpa2326_dbg(indio_dev, "sleeping");
+
+ return 0;
+}
+
+/**
+ * zpa2326_reset_device() - Reset device to default hardware state.
+ * @indio_dev: The IIO device associated with the hardware to reset.
+ *
+ * Disable sampling and empty hardware FIFO.
+ * Device must be enabled before reset, i.e. not in low power mode.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_reset_device(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = regmap_write(((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap,
+ ZPA2326_CTRL_REG2_REG, ZPA2326_CTRL_REG2_SWRESET);
+ if (err) {
+ zpa2326_err(indio_dev, "failed to reset device (%d)", err);
+ return err;
+ }
+
+ usleep_range(ZPA2326_TPUP_USEC_MIN, ZPA2326_TPUP_USEC_MAX);
+
+ zpa2326_dbg(indio_dev, "reset");
+
+ return 0;
+}
+
+/**
+ * zpa2326_start_oneshot() - Start a single sampling cycle, i.e. in one shot
+ * mode.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Device must have been previously enabled and configured for one shot mode.
+ * Device will be switched back to low power mode at end of cycle.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_start_oneshot(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = regmap_write(((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap,
+ ZPA2326_CTRL_REG0_REG,
+ ZPA2326_CTRL_REG0_ENABLE |
+ ZPA2326_CTRL_REG0_ONE_SHOT);
+ if (err) {
+ zpa2326_err(indio_dev, "failed to start one shot cycle (%d)",
+ err);
+ return err;
+ }
+
+ zpa2326_dbg(indio_dev, "one shot cycle started");
+
+ return 0;
+}
+
+/**
+ * zpa2326_power_on() - Power on device to allow subsequent configuration.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ *
+ * Sampling will be disabled, preventing strange things from happening in our
+ * back. Hardware FIFO content will be cleared.
+ * When successful, device will be left in the enabled state to allow further
+ * configuration.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_power_on(const struct iio_dev *indio_dev,
+ const struct zpa2326_private *private)
+{
+ int err;
+
+ err = regulator_enable(private->vref);
+ if (err)
+ return err;
+
+ err = regulator_enable(private->vdd);
+ if (err)
+ goto vref;
+
+ zpa2326_dbg(indio_dev, "powered on");
+
+ err = zpa2326_enable_device(indio_dev);
+ if (err)
+ goto vdd;
+
+ err = zpa2326_reset_device(indio_dev);
+ if (err)
+ goto sleep;
+
+ return 0;
+
+sleep:
+ zpa2326_sleep(indio_dev);
+vdd:
+ regulator_disable(private->vdd);
+vref:
+ regulator_disable(private->vref);
+
+ zpa2326_dbg(indio_dev, "powered off");
+
+ return err;
+}
+
+/**
+ * zpa2326_power_off() - Power off device, i.e. disable attached power
+ * regulators.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static void zpa2326_power_off(const struct iio_dev *indio_dev,
+ const struct zpa2326_private *private)
+{
+ regulator_disable(private->vdd);
+ regulator_disable(private->vref);
+
+ zpa2326_dbg(indio_dev, "powered off");
+}
+
+/**
+ * zpa2326_config_oneshot() - Setup device for one shot / on demand mode.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @irq: Optional interrupt line the hardware uses to notify new data
+ * samples are ready. Negative or zero values indicate no interrupts
+ * are available, meaning polling is required.
+ *
+ * Output Data Rate is configured for the highest possible rate so that
+ * conversion time and power consumption are reduced to a minimum.
+ * Note that hardware internal averaging machinery (not implemented in this
+ * driver) is not applicable in this mode.
+ *
+ * Device must have been previously enabled before calling
+ * zpa2326_config_oneshot().
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_config_oneshot(const struct iio_dev *indio_dev,
+ int irq)
+{
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ const struct zpa2326_frequency *freq = zpa2326_highest_frequency();
+ int err;
+
+ /* Setup highest available Output Data Rate for one shot mode. */
+ err = regmap_write(regs, ZPA2326_CTRL_REG3_REG, freq->odr);
+ if (err)
+ return err;
+
+ if (irq > 0) {
+ /* Request interrupt when new sample is available. */
+ err = regmap_write(regs, ZPA2326_CTRL_REG1_REG,
+ (u8)~ZPA2326_CTRL_REG1_MASK_DATA_READY);
+
+ if (err) {
+ dev_err(indio_dev->dev.parent,
+ "failed to setup one shot mode (%d)", err);
+ return err;
+ }
+ }
+
+ zpa2326_dbg(indio_dev, "one shot mode setup @%dHz", freq->hz);
+
+ return 0;
+}
+
+/**
+ * zpa2326_clear_fifo() - Clear remaining entries in hardware FIFO.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @min_count: Number of samples present within hardware FIFO.
+ *
+ * @min_count argument is a hint corresponding to the known minimum number of
+ * samples currently living in the FIFO. This allows to reduce the number of bus
+ * accesses by skipping status register read operation as long as we know for
+ * sure there are still entries left.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_clear_fifo(const struct iio_dev *indio_dev,
+ unsigned int min_count)
+{
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ int err;
+ unsigned int val;
+
+ if (!min_count) {
+ /*
+ * No hint: read status register to determine whether FIFO is
+ * empty or not.
+ */
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+
+ if (err < 0)
+ goto err;
+
+ if (val & ZPA2326_STATUS_FIFO_E)
+ /* Fifo is empty: nothing to trash. */
+ return 0;
+ }
+
+ /* Clear FIFO. */
+ do {
+ /*
+ * A single fetch from pressure MSB register is enough to pop
+ * values out of FIFO.
+ */
+ err = regmap_read(regs, ZPA2326_PRESS_OUT_H_REG, &val);
+ if (err < 0)
+ goto err;
+
+ if (min_count) {
+ /*
+ * We know for sure there are at least min_count entries
+ * left in FIFO. Skip status register read.
+ */
+ min_count--;
+ continue;
+ }
+
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+ if (err < 0)
+ goto err;
+
+ } while (!(val & ZPA2326_STATUS_FIFO_E));
+
+ zpa2326_dbg(indio_dev, "FIFO cleared");
+
+ return 0;
+
+err:
+ zpa2326_err(indio_dev, "failed to clear FIFO (%d)", err);
+
+ return err;
+}
+
+/**
+ * zpa2326_dequeue_pressure() - Retrieve the most recent pressure sample from
+ * hardware FIFO.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @pressure: Sampled pressure output.
+ *
+ * Note that ZPA2326 hardware FIFO stores pressure samples only.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_dequeue_pressure(const struct iio_dev *indio_dev,
+ u32 *pressure)
+{
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ unsigned int val;
+ int err;
+ int cleared = -1;
+
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+ if (err < 0)
+ return err;
+
+ *pressure = 0;
+
+ if (val & ZPA2326_STATUS_P_OR) {
+ /*
+ * Fifo overrun : first sample dequeued from FIFO is the
+ * newest.
+ */
+ zpa2326_warn(indio_dev, "FIFO overflow");
+
+ err = regmap_bulk_read(regs, ZPA2326_PRESS_OUT_XL_REG, pressure,
+ 3);
+ if (err)
+ return err;
+
+#define ZPA2326_FIFO_DEPTH (16U)
+ /* Hardware FIFO may hold no more than 16 pressure samples. */
+ return zpa2326_clear_fifo(indio_dev, ZPA2326_FIFO_DEPTH - 1);
+ }
+
+ /*
+ * Fifo has not overflown : retrieve newest sample. We need to pop
+ * values out until FIFO is empty : last fetched pressure is the newest.
+ * In nominal cases, we should find a single queued sample only.
+ */
+ do {
+ err = regmap_bulk_read(regs, ZPA2326_PRESS_OUT_XL_REG, pressure,
+ 3);
+ if (err)
+ return err;
+
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+ if (err < 0)
+ return err;
+
+ cleared++;
+ } while (!(val & ZPA2326_STATUS_FIFO_E));
+
+ if (cleared)
+ /*
+ * Samples were pushed by hardware during previous rounds but we
+ * didn't consume them fast enough: inform user.
+ */
+ zpa2326_dbg(indio_dev, "cleared %d FIFO entries", cleared);
+
+ return 0;
+}
+
+/**
+ * zpa2326_fill_sample_buffer() - Enqueue new channel samples to IIO buffer.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_fill_sample_buffer(struct iio_dev *indio_dev,
+ const struct zpa2326_private *private)
+{
+ struct {
+ u32 pressure;
+ u16 temperature;
+ aligned_s64 timestamp;
+ } sample = { };
+ int err;
+
+ if (test_bit(0, indio_dev->active_scan_mask)) {
+ /* Get current pressure from hardware FIFO. */
+ err = zpa2326_dequeue_pressure(indio_dev, &sample.pressure);
+ if (err) {
+ zpa2326_warn(indio_dev, "failed to fetch pressure (%d)",
+ err);
+ return err;
+ }
+ }
+
+ if (test_bit(1, indio_dev->active_scan_mask)) {
+ /* Get current temperature. */
+ err = regmap_bulk_read(private->regmap, ZPA2326_TEMP_OUT_L_REG,
+ &sample.temperature, 2);
+ if (err) {
+ zpa2326_warn(indio_dev,
+ "failed to fetch temperature (%d)", err);
+ return err;
+ }
+ }
+
+ /*
+ * Now push samples using timestamp stored either :
+ * - by hardware interrupt handler if interrupt is available: see
+ * zpa2326_handle_irq(),
+ * - or oneshot completion polling machinery : see
+ * zpa2326_trigger_handler().
+ */
+ zpa2326_dbg(indio_dev, "filling raw samples buffer");
+
+ iio_push_to_buffers_with_ts(indio_dev, &sample, sizeof(sample),
+ private->timestamp);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int zpa2326_runtime_suspend(struct device *parent)
+{
+ const struct iio_dev *indio_dev = dev_get_drvdata(parent);
+
+ if (pm_runtime_autosuspend_expiration(parent))
+ /* Userspace changed autosuspend delay. */
+ return -EAGAIN;
+
+ zpa2326_power_off(indio_dev, iio_priv(indio_dev));
+
+ return 0;
+}
+
+static int zpa2326_runtime_resume(struct device *parent)
+{
+ const struct iio_dev *indio_dev = dev_get_drvdata(parent);
+
+ return zpa2326_power_on(indio_dev, iio_priv(indio_dev));
+}
+
+const struct dev_pm_ops zpa2326_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend,
+ pm_runtime_force_resume)
+ SET_RUNTIME_PM_OPS(zpa2326_runtime_suspend, zpa2326_runtime_resume,
+ NULL)
+};
+EXPORT_SYMBOL_NS_GPL(zpa2326_pm_ops, "IIO_ZPA2326");
+
+/**
+ * zpa2326_resume() - Request the PM layer to power supply the device.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Return:
+ * < 0 - a negative error code meaning failure ;
+ * 0 - success, device has just been powered up ;
+ * 1 - success, device was already powered.
+ */
+static int zpa2326_resume(const struct iio_dev *indio_dev)
+{
+ int err;
+
+ err = pm_runtime_get_sync(indio_dev->dev.parent);
+ if (err < 0) {
+ pm_runtime_put(indio_dev->dev.parent);
+ return err;
+ }
+
+ if (err > 0) {
+ /*
+ * Device was already power supplied: get it out of low power
+ * mode and inform caller.
+ */
+ zpa2326_enable_device(indio_dev);
+ return 1;
+ }
+
+ /* Inform caller device has just been brought back to life. */
+ return 0;
+}
+
+/**
+ * zpa2326_suspend() - Schedule a power down using autosuspend feature of PM
+ * layer.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Device is switched to low power mode at first to save power even when
+ * attached regulator is a "dummy" one.
+ */
+static void zpa2326_suspend(struct iio_dev *indio_dev)
+{
+ struct device *parent = indio_dev->dev.parent;
+
+ zpa2326_sleep(indio_dev);
+
+ pm_runtime_put_autosuspend(parent);
+}
+
+static void zpa2326_init_runtime(struct device *parent)
+{
+ pm_runtime_get_noresume(parent);
+ pm_runtime_set_active(parent);
+ pm_runtime_enable(parent);
+ pm_runtime_set_autosuspend_delay(parent, 1000);
+ pm_runtime_use_autosuspend(parent);
+ pm_runtime_put_autosuspend(parent);
+}
+
+static void zpa2326_fini_runtime(struct device *parent)
+{
+ pm_runtime_disable(parent);
+ pm_runtime_set_suspended(parent);
+}
+#else /* !CONFIG_PM */
+static int zpa2326_resume(const struct iio_dev *indio_dev)
+{
+ zpa2326_enable_device(indio_dev);
+
+ return 0;
+}
+
+static void zpa2326_suspend(struct iio_dev *indio_dev)
+{
+ zpa2326_sleep(indio_dev);
+}
+
+#define zpa2326_init_runtime(_parent)
+#define zpa2326_fini_runtime(_parent)
+#endif /* !CONFIG_PM */
+
+/**
+ * zpa2326_handle_irq() - Process hardware interrupts.
+ * @irq: Interrupt line the hardware uses to notify new data has arrived.
+ * @data: The IIO device associated with the sampling hardware.
+ *
+ * Timestamp buffered samples as soon as possible then schedule threaded bottom
+ * half.
+ *
+ * Return: Always successful.
+ */
+static irqreturn_t zpa2326_handle_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+
+ if (iio_buffer_enabled(indio_dev)) {
+ /* Timestamping needed for buffered sampling only. */
+ ((struct zpa2326_private *)
+ iio_priv(indio_dev))->timestamp = iio_get_time_ns(indio_dev);
+ }
+
+ return IRQ_WAKE_THREAD;
+}
+
+/**
+ * zpa2326_handle_threaded_irq() - Interrupt bottom-half handler.
+ * @irq: Interrupt line the hardware uses to notify new data has arrived.
+ * @data: The IIO device associated with the sampling hardware.
+ *
+ * Mainly ensures interrupt is caused by a real "new sample available"
+ * condition. This relies upon the ability to perform blocking / sleeping bus
+ * accesses to slave's registers. This is why zpa2326_handle_threaded_irq() is
+ * called from within a thread, i.e. not called from hard interrupt context.
+ *
+ * When device is using its own internal hardware trigger in continuous sampling
+ * mode, data are available into hardware FIFO once interrupt has occurred. All
+ * we have to do is to dispatch the trigger, which in turn will fetch data and
+ * fill IIO buffer.
+ *
+ * When not using its own internal hardware trigger, the device has been
+ * configured in one-shot mode either by an external trigger or the IIO read_raw
+ * hook. This means one of the latter is currently waiting for sampling
+ * completion, in which case we must simply wake it up.
+ *
+ * See zpa2326_trigger_handler().
+ *
+ * Return:
+ * %IRQ_NONE - no consistent interrupt happened ;
+ * %IRQ_HANDLED - there was new samples available.
+ */
+static irqreturn_t zpa2326_handle_threaded_irq(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct zpa2326_private *priv = iio_priv(indio_dev);
+ unsigned int val;
+ bool cont;
+ irqreturn_t ret = IRQ_NONE;
+
+ /*
+ * Are we using our own internal trigger in triggered buffer mode, i.e.,
+ * currently working in continuous sampling mode ?
+ */
+ cont = (iio_buffer_enabled(indio_dev) &&
+ iio_trigger_using_own(indio_dev));
+
+ /*
+ * Device works according to a level interrupt scheme: reading interrupt
+ * status de-asserts interrupt line.
+ */
+ priv->result = regmap_read(priv->regmap, ZPA2326_INT_SOURCE_REG, &val);
+ if (priv->result < 0) {
+ if (cont)
+ return IRQ_NONE;
+
+ goto complete;
+ }
+
+ /* Data ready is the only interrupt source we requested. */
+ if (!(val & ZPA2326_INT_SOURCE_DATA_READY)) {
+ /*
+ * Interrupt happened but no new sample available: likely caused
+ * by spurious interrupts, in which case, returning IRQ_NONE
+ * allows to benefit from the generic spurious interrupts
+ * handling.
+ */
+ zpa2326_warn(indio_dev, "unexpected interrupt status %02x",
+ val);
+
+ if (cont)
+ return IRQ_NONE;
+
+ priv->result = -ENODATA;
+ goto complete;
+ }
+
+ /* New sample available: dispatch internal trigger consumers. */
+ iio_trigger_poll_nested(priv->trigger);
+
+ if (cont)
+ /*
+ * Internal hardware trigger has been scheduled above : it will
+ * fetch data on its own.
+ */
+ return IRQ_HANDLED;
+
+ ret = IRQ_HANDLED;
+
+complete:
+ /*
+ * Wake up direct or externally triggered buffer mode waiters: see
+ * zpa2326_sample_oneshot() and zpa2326_trigger_handler().
+ */
+ complete(&priv->data_ready);
+
+ return ret;
+}
+
+/**
+ * zpa2326_wait_oneshot_completion() - Wait for oneshot data ready interrupt.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_wait_oneshot_completion(const struct iio_dev *indio_dev,
+ struct zpa2326_private *private)
+{
+ unsigned int val;
+ long time_left;
+
+ zpa2326_dbg(indio_dev, "waiting for one shot completion interrupt");
+
+ time_left = wait_for_completion_interruptible_timeout(
+ &private->data_ready, ZPA2326_CONVERSION_JIFFIES);
+ if (time_left > 0)
+ /*
+ * Interrupt handler completed before timeout: return operation
+ * status.
+ */
+ return private->result;
+
+ /* Clear all interrupts just to be sure. */
+ regmap_read(private->regmap, ZPA2326_INT_SOURCE_REG, &val);
+
+ if (!time_left) {
+ /* Timed out. */
+ zpa2326_warn(indio_dev, "no one shot interrupt occurred (%ld)",
+ time_left);
+ return -ETIME;
+ }
+
+ zpa2326_warn(indio_dev, "wait for one shot interrupt cancelled");
+ return -ERESTARTSYS;
+}
+
+static int zpa2326_init_managed_irq(struct device *parent,
+ struct iio_dev *indio_dev,
+ struct zpa2326_private *private,
+ int irq)
+{
+ int err;
+
+ private->irq = irq;
+
+ if (irq <= 0) {
+ /*
+ * Platform declared no interrupt line: device will be polled
+ * for data availability.
+ */
+ dev_info(parent, "no interrupt found, running in polling mode");
+ return 0;
+ }
+
+ init_completion(&private->data_ready);
+
+ /* Request handler to be scheduled into threaded interrupt context. */
+ err = devm_request_threaded_irq(parent, irq, zpa2326_handle_irq,
+ zpa2326_handle_threaded_irq,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(parent), indio_dev);
+ if (err)
+ return err;
+
+ dev_info(parent, "using interrupt %d", irq);
+
+ return 0;
+}
+
+/**
+ * zpa2326_poll_oneshot_completion() - Actively poll for one shot data ready.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Loop over registers content to detect end of sampling cycle. Used when DT
+ * declared no valid interrupt lines.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_poll_oneshot_completion(const struct iio_dev *indio_dev)
+{
+ unsigned long tmout = jiffies + ZPA2326_CONVERSION_JIFFIES;
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ unsigned int val;
+ int err;
+
+ zpa2326_dbg(indio_dev, "polling for one shot completion");
+
+ /*
+ * At least, 100 ms is needed for the device to complete its one-shot
+ * cycle.
+ */
+ if (msleep_interruptible(100))
+ return -ERESTARTSYS;
+
+ /* Poll for conversion completion in hardware. */
+ while (true) {
+ err = regmap_read(regs, ZPA2326_CTRL_REG0_REG, &val);
+ if (err < 0)
+ goto err;
+
+ if (!(val & ZPA2326_CTRL_REG0_ONE_SHOT))
+ /* One-shot bit self clears at conversion end. */
+ break;
+
+ if (time_after(jiffies, tmout)) {
+ /* Prevent from waiting forever : let's time out. */
+ err = -ETIME;
+ goto err;
+ }
+
+ usleep_range(10000, 20000);
+ }
+
+ /*
+ * In oneshot mode, pressure sample availability guarantees that
+ * temperature conversion has also completed : just check pressure
+ * status bit to keep things simple.
+ */
+ err = regmap_read(regs, ZPA2326_STATUS_REG, &val);
+ if (err < 0)
+ goto err;
+
+ if (!(val & ZPA2326_STATUS_P_DA)) {
+ /* No sample available. */
+ err = -ENODATA;
+ goto err;
+ }
+
+ return 0;
+
+err:
+ zpa2326_warn(indio_dev, "failed to poll one shot completion (%d)", err);
+
+ return err;
+}
+
+/**
+ * zpa2326_fetch_raw_sample() - Retrieve a raw sample and convert it to CPU
+ * endianness.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @type: Type of measurement / channel to fetch from.
+ * @value: Sample output.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_fetch_raw_sample(const struct iio_dev *indio_dev,
+ enum iio_chan_type type,
+ int *value)
+{
+ struct regmap *regs = ((struct zpa2326_private *)
+ iio_priv(indio_dev))->regmap;
+ int err;
+ u8 v[3];
+
+ switch (type) {
+ case IIO_PRESSURE:
+ zpa2326_dbg(indio_dev, "fetching raw pressure sample");
+
+ err = regmap_bulk_read(regs, ZPA2326_PRESS_OUT_XL_REG, v, sizeof(v));
+ if (err) {
+ zpa2326_warn(indio_dev, "failed to fetch pressure (%d)",
+ err);
+ return err;
+ }
+
+ *value = get_unaligned_le24(&v[0]);
+
+ return IIO_VAL_INT;
+
+ case IIO_TEMP:
+ zpa2326_dbg(indio_dev, "fetching raw temperature sample");
+
+ err = regmap_bulk_read(regs, ZPA2326_TEMP_OUT_L_REG, value, 2);
+ if (err) {
+ zpa2326_warn(indio_dev,
+ "failed to fetch temperature (%d)", err);
+ return err;
+ }
+
+ /* Temperature is a 16 bits wide little-endian signed int. */
+ *value = (int)le16_to_cpup((__le16 *)value);
+
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+/**
+ * zpa2326_sample_oneshot() - Perform a complete one shot sampling cycle.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @type: Type of measurement / channel to fetch from.
+ * @value: Sample output.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_sample_oneshot(struct iio_dev *indio_dev,
+ enum iio_chan_type type,
+ int *value)
+{
+ int ret;
+ struct zpa2326_private *priv;
+
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ ret = zpa2326_resume(indio_dev);
+ if (ret < 0)
+ goto release;
+
+ priv = iio_priv(indio_dev);
+
+ if (ret > 0) {
+ /*
+ * We were already power supplied. Just clear hardware FIFO to
+ * get rid of samples acquired during previous rounds (if any).
+ * Sampling operation always generates both temperature and
+ * pressure samples. The latter are always enqueued into
+ * hardware FIFO. This may lead to situations were pressure
+ * samples still sit into FIFO when previous cycle(s) fetched
+ * temperature data only.
+ * Hence, we need to clear hardware FIFO content to prevent from
+ * getting outdated values at the end of current cycle.
+ */
+ if (type == IIO_PRESSURE) {
+ ret = zpa2326_clear_fifo(indio_dev, 0);
+ if (ret)
+ goto suspend;
+ }
+ } else {
+ /*
+ * We have just been power supplied, i.e. device is in default
+ * "out of reset" state, meaning we need to reconfigure it
+ * entirely.
+ */
+ ret = zpa2326_config_oneshot(indio_dev, priv->irq);
+ if (ret)
+ goto suspend;
+ }
+
+ /* Start a sampling cycle in oneshot mode. */
+ ret = zpa2326_start_oneshot(indio_dev);
+ if (ret)
+ goto suspend;
+
+ /* Wait for sampling cycle to complete. */
+ if (priv->irq > 0)
+ ret = zpa2326_wait_oneshot_completion(indio_dev, priv);
+ else
+ ret = zpa2326_poll_oneshot_completion(indio_dev);
+
+ if (ret)
+ goto suspend;
+
+ /* Retrieve raw sample value and convert it to CPU endianness. */
+ ret = zpa2326_fetch_raw_sample(indio_dev, type, value);
+
+suspend:
+ zpa2326_suspend(indio_dev);
+release:
+ iio_device_release_direct(indio_dev);
+
+ return ret;
+}
+
+/**
+ * zpa2326_trigger_handler() - Perform an IIO buffered sampling round in one
+ * shot mode.
+ * @irq: The software interrupt assigned to @data
+ * @data: The IIO poll function dispatched by external trigger our device is
+ * attached to.
+ *
+ * Bottom-half handler called by the IIO trigger to which our device is
+ * currently attached. Allows us to synchronize this device buffered sampling
+ * either with external events (such as timer expiration, external device sample
+ * ready, etc...) or with its own interrupt (internal hardware trigger).
+ *
+ * When using an external trigger, basically run the same sequence of operations
+ * as for zpa2326_sample_oneshot() with the following hereafter. Hardware FIFO
+ * is not cleared since already done at buffering enable time and samples
+ * dequeueing always retrieves the most recent value.
+ *
+ * Otherwise, when internal hardware trigger has dispatched us, just fetch data
+ * from hardware FIFO.
+ *
+ * Fetched data will pushed unprocessed to IIO buffer since samples conversion
+ * is delegated to userspace in buffered mode (endianness, etc...).
+ *
+ * Return:
+ * %IRQ_NONE - no consistent interrupt happened ;
+ * %IRQ_HANDLED - there was new samples available.
+ */
+static irqreturn_t zpa2326_trigger_handler(int irq, void *data)
+{
+ struct iio_dev *indio_dev = ((struct iio_poll_func *)
+ data)->indio_dev;
+ struct zpa2326_private *priv = iio_priv(indio_dev);
+ bool cont;
+
+ /*
+ * We have been dispatched, meaning we are in triggered buffer mode.
+ * Using our own internal trigger implies we are currently in continuous
+ * hardware sampling mode.
+ */
+ cont = iio_trigger_using_own(indio_dev);
+
+ if (!cont) {
+ /* On demand sampling : start a one shot cycle. */
+ if (zpa2326_start_oneshot(indio_dev))
+ goto out;
+
+ /* Wait for sampling cycle to complete. */
+ if (priv->irq <= 0) {
+ /* No interrupt available: poll for completion. */
+ if (zpa2326_poll_oneshot_completion(indio_dev))
+ goto out;
+
+ /* Only timestamp sample once it is ready. */
+ priv->timestamp = iio_get_time_ns(indio_dev);
+ } else {
+ /* Interrupt handlers will timestamp for us. */
+ if (zpa2326_wait_oneshot_completion(indio_dev, priv))
+ goto out;
+ }
+ }
+
+ /* Enqueue to IIO buffer / userspace. */
+ zpa2326_fill_sample_buffer(indio_dev, priv);
+
+out:
+ if (!cont)
+ /* Don't switch to low power if sampling continuously. */
+ zpa2326_sleep(indio_dev);
+
+ /* Inform attached trigger we are done. */
+ iio_trigger_notify_done(indio_dev->trig);
+
+ return IRQ_HANDLED;
+}
+
+/**
+ * zpa2326_preenable_buffer() - Prepare device for configuring triggered
+ * sampling
+ * modes.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Basically power up device.
+ * Called with IIO device's lock held.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_preenable_buffer(struct iio_dev *indio_dev)
+{
+ int ret = zpa2326_resume(indio_dev);
+
+ if (ret < 0)
+ return ret;
+
+ /* Tell zpa2326_postenable_buffer() if we have just been powered on. */
+ ((struct zpa2326_private *)
+ iio_priv(indio_dev))->waken = iio_priv(indio_dev);
+
+ return 0;
+}
+
+/**
+ * zpa2326_postenable_buffer() - Configure device for triggered sampling.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ *
+ * Basically setup one-shot mode if plugging external trigger.
+ * Otherwise, let internal trigger configure continuous sampling :
+ * see zpa2326_set_trigger_state().
+ *
+ * If an error is returned, IIO layer will call our postdisable hook for us,
+ * i.e. no need to explicitly power device off here.
+ * Called with IIO device's lock held.
+ *
+ * Called with IIO device's lock held.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_postenable_buffer(struct iio_dev *indio_dev)
+{
+ const struct zpa2326_private *priv = iio_priv(indio_dev);
+ int err;
+
+ if (!priv->waken) {
+ /*
+ * We were already power supplied. Just clear hardware FIFO to
+ * get rid of samples acquired during previous rounds (if any).
+ */
+ err = zpa2326_clear_fifo(indio_dev, 0);
+ if (err) {
+ zpa2326_err(indio_dev,
+ "failed to enable buffering (%d)", err);
+ return err;
+ }
+ }
+
+ if (!iio_trigger_using_own(indio_dev) && priv->waken) {
+ /*
+ * We are using an external trigger and we have just been
+ * powered up: reconfigure one-shot mode.
+ */
+ err = zpa2326_config_oneshot(indio_dev, priv->irq);
+ if (err) {
+ zpa2326_err(indio_dev,
+ "failed to enable buffering (%d)", err);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+static int zpa2326_postdisable_buffer(struct iio_dev *indio_dev)
+{
+ zpa2326_suspend(indio_dev);
+
+ return 0;
+}
+
+static const struct iio_buffer_setup_ops zpa2326_buffer_setup_ops = {
+ .preenable = zpa2326_preenable_buffer,
+ .postenable = zpa2326_postenable_buffer,
+ .postdisable = zpa2326_postdisable_buffer
+};
+
+/**
+ * zpa2326_set_trigger_state() - Start / stop continuous sampling.
+ * @trig: The trigger being attached to IIO device associated with the sampling
+ * hardware.
+ * @state: Tell whether to start (true) or stop (false)
+ *
+ * Basically enable / disable hardware continuous sampling mode.
+ *
+ * Called with IIO device's lock held at postenable() or predisable() time.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_set_trigger_state(struct iio_trigger *trig, bool state)
+{
+ const struct iio_dev *indio_dev = dev_get_drvdata(
+ trig->dev.parent);
+ const struct zpa2326_private *priv = iio_priv(indio_dev);
+ int err;
+
+ if (!state) {
+ /*
+ * Switch trigger off : in case of failure, interrupt is left
+ * disabled in order to prevent handler from accessing released
+ * resources.
+ */
+ unsigned int val;
+
+ /*
+ * As device is working in continuous mode, handlers may be
+ * accessing resources we are currently freeing...
+ * Prevent this by disabling interrupt handlers and ensure
+ * the device will generate no more interrupts unless explicitly
+ * required to, i.e. by restoring back to default one shot mode.
+ */
+ disable_irq(priv->irq);
+
+ /*
+ * Disable continuous sampling mode to restore settings for
+ * one shot / direct sampling operations.
+ */
+ err = regmap_write(priv->regmap, ZPA2326_CTRL_REG3_REG,
+ zpa2326_highest_frequency()->odr);
+ if (err)
+ return err;
+
+ /*
+ * Now that device won't generate interrupts on its own,
+ * acknowledge any currently active interrupts (may happen on
+ * rare occasions while stopping continuous mode).
+ */
+ err = regmap_read(priv->regmap, ZPA2326_INT_SOURCE_REG, &val);
+ if (err < 0)
+ return err;
+
+ /*
+ * Re-enable interrupts only if we can guarantee the device will
+ * generate no more interrupts to prevent handlers from
+ * accessing released resources.
+ */
+ enable_irq(priv->irq);
+
+ zpa2326_dbg(indio_dev, "continuous mode stopped");
+ } else {
+ /*
+ * Switch trigger on : start continuous sampling at required
+ * frequency.
+ */
+
+ if (priv->waken) {
+ /* Enable interrupt if getting out of reset. */
+ err = regmap_write(priv->regmap, ZPA2326_CTRL_REG1_REG,
+ (u8)
+ ~ZPA2326_CTRL_REG1_MASK_DATA_READY);
+ if (err)
+ return err;
+ }
+
+ /* Enable continuous sampling at specified frequency. */
+ err = regmap_write(priv->regmap, ZPA2326_CTRL_REG3_REG,
+ ZPA2326_CTRL_REG3_ENABLE_MEAS |
+ priv->frequency->odr);
+ if (err)
+ return err;
+
+ zpa2326_dbg(indio_dev, "continuous mode setup @%dHz",
+ priv->frequency->hz);
+ }
+
+ return 0;
+}
+
+static const struct iio_trigger_ops zpa2326_trigger_ops = {
+ .set_trigger_state = zpa2326_set_trigger_state,
+};
+
+/**
+ * zpa2326_init_managed_trigger() - Create interrupt driven / hardware trigger
+ * allowing to notify external devices a new sample is
+ * ready.
+ * @parent: Hardware sampling device @indio_dev is a child of.
+ * @indio_dev: The IIO device associated with the sampling hardware.
+ * @private: Internal private state related to @indio_dev.
+ * @irq: Optional interrupt line the hardware uses to notify new data
+ * samples are ready. Negative or zero values indicate no interrupts
+ * are available, meaning polling is required.
+ *
+ * Only relevant when DT declares a valid interrupt line.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+static int zpa2326_init_managed_trigger(struct device *parent,
+ struct iio_dev *indio_dev,
+ struct zpa2326_private *private,
+ int irq)
+{
+ struct iio_trigger *trigger;
+ int ret;
+
+ if (irq <= 0)
+ return 0;
+
+ trigger = devm_iio_trigger_alloc(parent, "%s-dev%d",
+ indio_dev->name,
+ iio_device_id(indio_dev));
+ if (!trigger)
+ return -ENOMEM;
+
+ /* Basic setup. */
+ trigger->ops = &zpa2326_trigger_ops;
+
+ private->trigger = trigger;
+
+ /* Register to triggers space. */
+ ret = devm_iio_trigger_register(parent, trigger);
+ if (ret)
+ dev_err(parent, "failed to register hardware trigger (%d)",
+ ret);
+
+ return ret;
+}
+
+static int zpa2326_get_frequency(const struct iio_dev *indio_dev)
+{
+ return ((struct zpa2326_private *)iio_priv(indio_dev))->frequency->hz;
+}
+
+static int zpa2326_set_frequency(struct iio_dev *indio_dev, int hz)
+{
+ struct zpa2326_private *priv = iio_priv(indio_dev);
+ int freq;
+
+ /* Check if requested frequency is supported. */
+ for (freq = 0; freq < ARRAY_SIZE(zpa2326_sampling_frequencies); freq++)
+ if (zpa2326_sampling_frequencies[freq].hz == hz)
+ break;
+ if (freq == ARRAY_SIZE(zpa2326_sampling_frequencies))
+ return -EINVAL;
+
+ /* Don't allow changing frequency if buffered sampling is ongoing. */
+ if (!iio_device_claim_direct(indio_dev))
+ return -EBUSY;
+
+ priv->frequency = &zpa2326_sampling_frequencies[freq];
+
+ iio_device_release_direct(indio_dev);
+
+ return 0;
+}
+
+/* Expose supported hardware sampling frequencies (Hz) through sysfs. */
+static IIO_CONST_ATTR_SAMP_FREQ_AVAIL("1 5 11 23");
+
+static struct attribute *zpa2326_attributes[] = {
+ &iio_const_attr_sampling_frequency_available.dev_attr.attr,
+ NULL
+};
+
+static const struct attribute_group zpa2326_attribute_group = {
+ .attrs = zpa2326_attributes,
+};
+
+static int zpa2326_read_raw(struct iio_dev *indio_dev,
+ struct iio_chan_spec const *chan,
+ int *val,
+ int *val2,
+ long mask)
+{
+ switch (mask) {
+ case IIO_CHAN_INFO_RAW:
+ return zpa2326_sample_oneshot(indio_dev, chan->type, val);
+
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ /*
+ * Pressure resolution is 1/64 Pascal. Scale to kPascal
+ * as required by IIO ABI.
+ */
+ *val = 1;
+ *val2 = 64000;
+ return IIO_VAL_FRACTIONAL;
+
+ case IIO_TEMP:
+ /*
+ * Temperature follows the equation:
+ * Temp[degC] = Tempcode * 0.00649 - 176.83
+ * where:
+ * Tempcode is composed the raw sampled 16 bits.
+ *
+ * Hence, to produce a temperature in milli-degrees
+ * Celsius according to IIO ABI, we need to apply the
+ * following equation to raw samples:
+ * Temp[milli degC] = (Tempcode + Offset) * Scale
+ * where:
+ * Offset = -176.83 / 0.00649
+ * Scale = 0.00649 * 1000
+ */
+ *val = 6;
+ *val2 = 490000;
+ return IIO_VAL_INT_PLUS_MICRO;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_OFFSET:
+ switch (chan->type) {
+ case IIO_TEMP:
+ *val = -17683000;
+ *val2 = 649;
+ return IIO_VAL_FRACTIONAL;
+
+ default:
+ return -EINVAL;
+ }
+
+ case IIO_CHAN_INFO_SAMP_FREQ:
+ *val = zpa2326_get_frequency(indio_dev);
+ return IIO_VAL_INT;
+
+ default:
+ return -EINVAL;
+ }
+}
+
+static int zpa2326_write_raw(struct iio_dev *indio_dev,
+ const struct iio_chan_spec *chan,
+ int val,
+ int val2,
+ long mask)
+{
+ if ((mask != IIO_CHAN_INFO_SAMP_FREQ) || val2)
+ return -EINVAL;
+
+ return zpa2326_set_frequency(indio_dev, val);
+}
+
+static const struct iio_chan_spec zpa2326_channels[] = {
+ [0] = {
+ .type = IIO_PRESSURE,
+ .scan_index = 0,
+ .scan_type = {
+ .sign = 'u',
+ .realbits = 24,
+ .storagebits = 32,
+ .endianness = IIO_LE,
+ },
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
+ BIT(IIO_CHAN_INFO_SCALE),
+ .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
+ },
+ [1] = {
+ .type = IIO_TEMP,
+ .scan_index = 1,
+ .scan_type = {
+ .sign = 's',
+ .realbits = 16,
+ .storagebits = 16,
+ .endianness = IIO_LE,
+ },
+ .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),
+ },
+ [2] = IIO_CHAN_SOFT_TIMESTAMP(2),
+};
+
+static const struct iio_info zpa2326_info = {
+ .attrs = &zpa2326_attribute_group,
+ .read_raw = zpa2326_read_raw,
+ .write_raw = zpa2326_write_raw,
+};
+
+static struct iio_dev *zpa2326_create_managed_iiodev(struct device *device,
+ const char *name,
+ struct regmap *regmap)
+{
+ struct iio_dev *indio_dev;
+
+ /* Allocate space to hold IIO device internal state. */
+ indio_dev = devm_iio_device_alloc(device,
+ sizeof(struct zpa2326_private));
+ if (!indio_dev)
+ return NULL;
+
+ /* Setup for userspace synchronous on demand sampling. */
+ indio_dev->modes = INDIO_DIRECT_MODE;
+ indio_dev->channels = zpa2326_channels;
+ indio_dev->num_channels = ARRAY_SIZE(zpa2326_channels);
+ indio_dev->name = name;
+ indio_dev->info = &zpa2326_info;
+
+ return indio_dev;
+}
+
+int zpa2326_probe(struct device *parent,
+ const char *name,
+ int irq,
+ unsigned int hwid,
+ struct regmap *regmap)
+{
+ struct iio_dev *indio_dev;
+ struct zpa2326_private *priv;
+ int err;
+ unsigned int id;
+
+ indio_dev = zpa2326_create_managed_iiodev(parent, name, regmap);
+ if (!indio_dev)
+ return -ENOMEM;
+
+ priv = iio_priv(indio_dev);
+
+ priv->vref = devm_regulator_get(parent, "vref");
+ if (IS_ERR(priv->vref))
+ return PTR_ERR(priv->vref);
+
+ priv->vdd = devm_regulator_get(parent, "vdd");
+ if (IS_ERR(priv->vdd))
+ return PTR_ERR(priv->vdd);
+
+ /* Set default hardware sampling frequency to highest rate supported. */
+ priv->frequency = zpa2326_highest_frequency();
+
+ /*
+ * Plug device's underlying bus abstraction : this MUST be set before
+ * registering interrupt handlers since an interrupt might happen if
+ * power up sequence is not properly applied.
+ */
+ priv->regmap = regmap;
+
+ err = devm_iio_triggered_buffer_setup(parent, indio_dev, NULL,
+ zpa2326_trigger_handler,
+ &zpa2326_buffer_setup_ops);
+ if (err)
+ return err;
+
+ err = zpa2326_init_managed_trigger(parent, indio_dev, priv, irq);
+ if (err)
+ return err;
+
+ err = zpa2326_init_managed_irq(parent, indio_dev, priv, irq);
+ if (err)
+ return err;
+
+ /* Power up to check device ID and perform initial hardware setup. */
+ err = zpa2326_power_on(indio_dev, priv);
+ if (err)
+ return err;
+
+ /* Read id register to check we are talking to the right slave. */
+ err = regmap_read(regmap, ZPA2326_DEVICE_ID_REG, &id);
+ if (err)
+ goto sleep;
+
+ if (id != hwid) {
+ dev_err(parent, "found device with unexpected id %02x", id);
+ err = -ENODEV;
+ goto sleep;
+ }
+
+ err = zpa2326_config_oneshot(indio_dev, irq);
+ if (err)
+ goto sleep;
+
+ /* Setup done : go sleeping. Device will be awaken upon user request. */
+ err = zpa2326_sleep(indio_dev);
+ if (err)
+ goto poweroff;
+
+ dev_set_drvdata(parent, indio_dev);
+
+ zpa2326_init_runtime(parent);
+
+ err = iio_device_register(indio_dev);
+ if (err) {
+ zpa2326_fini_runtime(parent);
+ goto poweroff;
+ }
+
+ return 0;
+
+sleep:
+ /* Put to sleep just in case power regulators are "dummy" ones. */
+ zpa2326_sleep(indio_dev);
+poweroff:
+ zpa2326_power_off(indio_dev, priv);
+
+ return err;
+}
+EXPORT_SYMBOL_NS_GPL(zpa2326_probe, "IIO_ZPA2326");
+
+void zpa2326_remove(const struct device *parent)
+{
+ struct iio_dev *indio_dev = dev_get_drvdata(parent);
+
+ iio_device_unregister(indio_dev);
+ zpa2326_fini_runtime(indio_dev->dev.parent);
+ zpa2326_sleep(indio_dev);
+ zpa2326_power_off(indio_dev, iio_priv(indio_dev));
+}
+EXPORT_SYMBOL_NS_GPL(zpa2326_remove, "IIO_ZPA2326");
+
+MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
+MODULE_DESCRIPTION("Core driver for Murata ZPA2326 pressure sensor");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/iio/pressure/zpa2326.h b/drivers/iio/pressure/zpa2326.h
new file mode 100644
index 000000000..45bd79009
--- /dev/null
+++ b/drivers/iio/pressure/zpa2326.h
@@ -0,0 +1,81 @@
+/* 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>
+ */
+
+#ifndef _ZPA2326_H
+#define _ZPA2326_H
+
+/* Register map. */
+#define ZPA2326_REF_P_XL_REG (0x8)
+#define ZPA2326_REF_P_L_REG (0x9)
+#define ZPA2326_REF_P_H_REG (0xa)
+#define ZPA2326_DEVICE_ID_REG (0xf)
+#define ZPA2326_DEVICE_ID (0xb9)
+#define ZPA2326_RES_CONF_REG (0x10)
+#define ZPA2326_CTRL_REG0_REG (0x20)
+#define ZPA2326_CTRL_REG0_ONE_SHOT BIT(0)
+#define ZPA2326_CTRL_REG0_ENABLE BIT(1)
+#define ZPA2326_CTRL_REG1_REG (0x21)
+#define ZPA2326_CTRL_REG1_MASK_DATA_READY BIT(2)
+#define ZPA2326_CTRL_REG2_REG (0x22)
+#define ZPA2326_CTRL_REG2_SWRESET BIT(2)
+#define ZPA2326_CTRL_REG3_REG (0x23)
+#define ZPA2326_CTRL_REG3_ODR_SHIFT (4)
+#define ZPA2326_CTRL_REG3_ENABLE_MEAS BIT(7)
+#define ZPA2326_INT_SOURCE_REG (0x24)
+#define ZPA2326_INT_SOURCE_DATA_READY BIT(2)
+#define ZPA2326_THS_P_LOW_REG (0x25)
+#define ZPA2326_THS_P_HIGH_REG (0x26)
+#define ZPA2326_STATUS_REG (0x27)
+#define ZPA2326_STATUS_P_DA BIT(1)
+#define ZPA2326_STATUS_FIFO_E BIT(2)
+#define ZPA2326_STATUS_P_OR BIT(5)
+#define ZPA2326_PRESS_OUT_XL_REG (0x28)
+#define ZPA2326_PRESS_OUT_L_REG (0x29)
+#define ZPA2326_PRESS_OUT_H_REG (0x2a)
+#define ZPA2326_TEMP_OUT_L_REG (0x2b)
+#define ZPA2326_TEMP_OUT_H_REG (0x2c)
+
+struct device;
+struct regmap;
+
+bool zpa2326_isreg_writeable(struct device *dev, unsigned int reg);
+bool zpa2326_isreg_readable(struct device *dev, unsigned int reg);
+bool zpa2326_isreg_precious(struct device *dev, unsigned int reg);
+
+/**
+ * zpa2326_probe() - Instantiate and register core ZPA2326 IIO device
+ * @parent: Hardware sampling device the created IIO device will be a child of.
+ * @name: Arbitrary name to identify the device.
+ * @irq: Interrupt line, negative if none.
+ * @hwid: Expected device hardware id.
+ * @regmap: Registers map used to abstract underlying bus accesses.
+ *
+ * Return: Zero when successful, a negative error code otherwise.
+ */
+int zpa2326_probe(struct device *parent,
+ const char *name,
+ int irq,
+ unsigned int hwid,
+ struct regmap *regmap);
+
+/**
+ * zpa2326_remove() - Unregister and destroy core ZPA2326 IIO device.
+ * @parent: Hardware sampling device the IIO device to remove is a child of.
+ */
+void zpa2326_remove(const struct device *parent);
+
+#ifdef CONFIG_PM
+#include <linux/pm.h>
+extern const struct dev_pm_ops zpa2326_pm_ops;
+#define ZPA2326_PM_OPS (&zpa2326_pm_ops)
+#else
+#define ZPA2326_PM_OPS (NULL)
+#endif
+
+#endif
diff --git a/drivers/iio/pressure/zpa2326_i2c.c b/drivers/iio/pressure/zpa2326_i2c.c
new file mode 100644
index 000000000..e04a61c23
--- /dev/null
+++ b/drivers/iio/pressure/zpa2326_i2c.c
@@ -0,0 +1,86 @@
+// 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>
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include "zpa2326.h"
+
+/*
+ * read_flag_mask:
+ * - address bit 7 must be set to request a register read operation
+ */
+static const struct regmap_config zpa2326_regmap_i2c_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = zpa2326_isreg_writeable,
+ .readable_reg = zpa2326_isreg_readable,
+ .precious_reg = zpa2326_isreg_precious,
+ .max_register = ZPA2326_TEMP_OUT_H_REG,
+ .read_flag_mask = BIT(7),
+};
+
+static unsigned int zpa2326_i2c_hwid(const struct i2c_client *client)
+{
+#define ZPA2326_SA0(_addr) (_addr & BIT(0))
+#define ZPA2326_DEVICE_ID_SA0_SHIFT (1)
+
+ /* Identification register bit 1 mirrors device address bit 0. */
+ return (ZPA2326_DEVICE_ID |
+ (ZPA2326_SA0(client->addr) << ZPA2326_DEVICE_ID_SA0_SHIFT));
+}
+
+static int zpa2326_probe_i2c(struct i2c_client *client)
+{
+ const struct i2c_device_id *i2c_id = i2c_client_get_device_id(client);
+ struct regmap *regmap;
+
+ regmap = devm_regmap_init_i2c(client, &zpa2326_regmap_i2c_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&client->dev, "failed to init registers map");
+ return PTR_ERR(regmap);
+ }
+
+ return zpa2326_probe(&client->dev, i2c_id->name, client->irq,
+ zpa2326_i2c_hwid(client), regmap);
+}
+
+static void zpa2326_remove_i2c(struct i2c_client *client)
+{
+ zpa2326_remove(&client->dev);
+}
+
+static const struct i2c_device_id zpa2326_i2c_ids[] = {
+ { .name = "zpa2326" },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, zpa2326_i2c_ids);
+
+static const struct of_device_id zpa2326_i2c_matches[] = {
+ { .compatible = "murata,zpa2326" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, zpa2326_i2c_matches);
+
+static struct i2c_driver zpa2326_i2c_driver = {
+ .driver = {
+ .name = "zpa2326-i2c",
+ .of_match_table = zpa2326_i2c_matches,
+ .pm = ZPA2326_PM_OPS,
+ },
+ .probe = zpa2326_probe_i2c,
+ .remove = zpa2326_remove_i2c,
+ .id_table = zpa2326_i2c_ids,
+};
+module_i2c_driver(zpa2326_i2c_driver);
+
+MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
+MODULE_DESCRIPTION("I2C driver for Murata ZPA2326 pressure sensor");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_ZPA2326");
diff --git a/drivers/iio/pressure/zpa2326_spi.c b/drivers/iio/pressure/zpa2326_spi.c
new file mode 100644
index 000000000..dfbb74a58
--- /dev/null
+++ b/drivers/iio/pressure/zpa2326_spi.c
@@ -0,0 +1,89 @@
+// 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>
+ */
+
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/spi/spi.h>
+#include "zpa2326.h"
+
+/*
+ * read_flag_mask:
+ * - address bit 7 must be set to request a register read operation
+ * - address bit 6 must be set to request register address auto increment
+ */
+static const struct regmap_config zpa2326_regmap_spi_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .writeable_reg = zpa2326_isreg_writeable,
+ .readable_reg = zpa2326_isreg_readable,
+ .precious_reg = zpa2326_isreg_precious,
+ .max_register = ZPA2326_TEMP_OUT_H_REG,
+ .read_flag_mask = BIT(7) | BIT(6),
+};
+
+static int zpa2326_probe_spi(struct spi_device *spi)
+{
+ struct regmap *regmap;
+ int err;
+
+ regmap = devm_regmap_init_spi(spi, &zpa2326_regmap_spi_config);
+ if (IS_ERR(regmap)) {
+ dev_err(&spi->dev, "failed to init registers map");
+ return PTR_ERR(regmap);
+ }
+
+ /*
+ * Enforce SPI slave settings to prevent from DT misconfiguration.
+ *
+ * Clock is idle high. Sampling happens on trailing edge, i.e., rising
+ * edge. Maximum bus frequency is 1 MHz. Registers are 8 bits wide.
+ */
+ spi->mode = SPI_MODE_3;
+ spi->max_speed_hz = min(spi->max_speed_hz, 1000000U);
+ err = spi_setup(spi);
+ if (err < 0)
+ return err;
+
+ return zpa2326_probe(&spi->dev, spi_get_device_id(spi)->name,
+ spi->irq, ZPA2326_DEVICE_ID, regmap);
+}
+
+static void zpa2326_remove_spi(struct spi_device *spi)
+{
+ zpa2326_remove(&spi->dev);
+}
+
+static const struct spi_device_id zpa2326_spi_ids[] = {
+ { .name = "zpa2326" },
+ { }
+};
+MODULE_DEVICE_TABLE(spi, zpa2326_spi_ids);
+
+static const struct of_device_id zpa2326_spi_matches[] = {
+ { .compatible = "murata,zpa2326" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, zpa2326_spi_matches);
+
+static struct spi_driver zpa2326_spi_driver = {
+ .driver = {
+ .name = "zpa2326-spi",
+ .of_match_table = zpa2326_spi_matches,
+ .pm = ZPA2326_PM_OPS,
+ },
+ .probe = zpa2326_probe_spi,
+ .remove = zpa2326_remove_spi,
+ .id_table = zpa2326_spi_ids,
+};
+module_spi_driver(zpa2326_spi_driver);
+
+MODULE_AUTHOR("Gregor Boirie <gregor.boirie@parrot.com>");
+MODULE_DESCRIPTION("SPI driver for Murata ZPA2326 pressure sensor");
+MODULE_LICENSE("GPL v2");
+MODULE_IMPORT_NS("IIO_ZPA2326");