summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/iio/barometer/lps331ap.txt40
-rw-r--r--drivers/iio/Kconfig1
-rw-r--r--drivers/iio/Makefile1
-rw-r--r--drivers/iio/barometer/Kconfig12
-rw-r--r--drivers/iio/barometer/Makefile7
-rw-r--r--drivers/iio/barometer/lps331ap.c1252
6 files changed, 1313 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/iio/barometer/lps331ap.txt b/Documentation/devicetree/bindings/iio/barometer/lps331ap.txt
new file mode 100644
index 00000000000..f0e44bcc088
--- /dev/null
+++ b/Documentation/devicetree/bindings/iio/barometer/lps331ap.txt
@@ -0,0 +1,40 @@
+* STMicroelectronics LPS331AP barometer sensor
+
+Required properties:
+
+ - compatible : should be "st,lps331ap"
+ - reg : the I2C address of the barometer
+
+Optional properties:
+
+ - interrupt-parent : phandle to the interrupt map subnode
+ - interrupts : interrupt mapping for LPS331AP interrupt sources:
+ 2 sources: 0 - INT1, 1 - INT2
+ - irq-map : irq sub-node defining interrupt map
+ (all properties listed below are required):
+ - #interrupt-cells : should be 1
+ - #address-cells : should be 0
+ - #size-cells : should be 0
+ - interrupt-map : table of entries consisting of three child elements:
+ - unit_interrupt_specifier - 0 : INT1, 1 : INT2
+ - interrupt parent phandle
+ - parent unit interrupt specifier consisiting of two elements:
+ - index of the interrupt within the controller
+ - flags : should be 0
+
+Example:
+
+lps331ap@5d {
+ compatible = "lps331ap";
+ reg = <0x5d>;
+ interrupt-parent = <&irq_map>;
+ interrupts = <0>, <1>;
+
+ irq_map: irq-map {
+ #interrupt-cells = <1>;
+ #address-cells = <0>;
+ #size-cells = <0>;
+ interrupt-map = <0 &gpf0 5 0>,
+ <1 &gpx0 3 0>;
+ };
+};
diff --git a/drivers/iio/Kconfig b/drivers/iio/Kconfig
index b2f963be399..edf69ff2894 100644
--- a/drivers/iio/Kconfig
+++ b/drivers/iio/Kconfig
@@ -63,6 +63,7 @@ config IIO_CONSUMERS_PER_TRIGGER
source "drivers/iio/accel/Kconfig"
source "drivers/iio/adc/Kconfig"
source "drivers/iio/amplifiers/Kconfig"
+source "drivers/iio/barometer/Kconfig"
source "drivers/iio/common/Kconfig"
source "drivers/iio/dac/Kconfig"
source "drivers/iio/frequency/Kconfig"
diff --git a/drivers/iio/Makefile b/drivers/iio/Makefile
index a0e8cdd67e4..cfcc81238c5 100644
--- a/drivers/iio/Makefile
+++ b/drivers/iio/Makefile
@@ -14,6 +14,7 @@ obj-$(CONFIG_IIO_KFIFO_BUF) += kfifo_buf.o
obj-y += accel/
obj-y += adc/
obj-y += amplifiers/
+obj-y += barometer/
obj-y += common/
obj-y += dac/
obj-y += gyro/
diff --git a/drivers/iio/barometer/Kconfig b/drivers/iio/barometer/Kconfig
new file mode 100644
index 00000000000..927c06c0c51
--- /dev/null
+++ b/drivers/iio/barometer/Kconfig
@@ -0,0 +1,12 @@
+#
+# Magnetometer sensors
+#
+menu "Barometer sensors"
+
+config SENSORS_LPS331
+ tristate "STMicro LPS331 driver"
+ depends on I2C
+ help
+ Driver for STMicro LPS331
+
+endmenu
diff --git a/drivers/iio/barometer/Makefile b/drivers/iio/barometer/Makefile
new file mode 100644
index 00000000000..8beba1c700a
--- /dev/null
+++ b/drivers/iio/barometer/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for the sensors drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_SENSORS_LPS331) += lps331ap.o
diff --git a/drivers/iio/barometer/lps331ap.c b/drivers/iio/barometer/lps331ap.c
new file mode 100644
index 00000000000..1e7cf07ce63
--- /dev/null
+++ b/drivers/iio/barometer/lps331ap.c
@@ -0,0 +1,1252 @@
+/*
+* STMicroelectronics LPS331AP Pressure / Temperature Sensor module driver
+*
+* Copyright (C) 2013 Samsung Electronics Co., Ltd.
+* Author: Jacek Anaszewski <j.anaszewski@samsung.com>
+*
+* Copyright (C) 2010 STMicroelectronics- MSH - Motion Mems BU - Application Team
+* Authors:
+* Matteo Dameno <matteo.dameno@st.com>
+* Carmine Iascone <carmine.iascone@st.com>
+*
+* Both authors are willing to be considered the contact and update points for
+* the driver.
+*
+* The device can be controlled by IIO sysfs interface from the location
+* /sys/bus/iio/devices/iio:deviceN, where N is the variable IIO device
+* number. The LPS331AP device can be identified by reading the 'name'
+* attribute.
+* Output data from the device can be read in two ways - on demand and by
+* initiating IIO events for the device. The former mode allows for power
+* saving as it instructs the device to do a 'one shot' measurement and return
+* to the power down mode, whereas the latter enables cyclic measurements
+* with the period managed through the 'odr' attribute, where possible settings
+* are: 40ms, 80ms, 143ms and 1000ms. The value has to be given in miliseconds.
+* Every integer value is accepted - the driver will round it to the nearest
+* bottom odr setting.
+*
+* Initialization of 'one shot' measurement:
+* - read in_pressure_raw or in_temp_raw sysfs attribute
+* Managing cyclic measurements:
+* 1) enable:
+* - write 1 to the events/in_pressure_mag_either_en and/or
+* events/in_temp_mag_either_en attribute
+* 2) read:
+* - perform periodic read of events/in_pressure_mag_either_value
+* and/or events/in_temp_mag_either_value atrributes
+* 3) alter output data rate:
+* - write value of 40, 80, 143 or 1000 to the 'odr' attribute
+* 1) disable:
+* - write 0 to the events/in_pressure_mag_either_en and/or
+* events/in_temp_mag_either_en attribute
+*
+* Read pressures and temperatures output can be converted in units of
+* measurement by dividing them respectively by in_pressure_scale and
+* in_temp_scale. Temperature values must then be added by the constant float
+* in_temp_offset expressed as Celsius degrees.
+*
+* Obtained values are then expessed as
+* mbar (=0.1 kPa) and Celsius degrees.
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* version 2 as published by the Free Software Foundation.
+*
+* This program is distributed in the hope that it will be useful, but
+* WITHOUT ANY WARRANTY; without even the implied warranty of
+* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+* General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+* 02110-1301 USA
+*/
+
+#include <linux/module.h>
+#include <linux/ratelimit.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/interrupt.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_irq.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/events.h>
+#include <linux/bitops.h>
+
+#define LPS331AP_VENDOR "STM"
+#define LPS331AP_CHIP_ID "LPS331"
+
+#define PR_ABS_MAX 8388607 /* 24 bit 2'compl */
+#define PR_ABS_MIN -8388608
+
+#define WHOAMI_LPS331AP_PRS 0xBB /* Expected content for WAI */
+
+/* Control registers */
+#define REF_P_XL 0x08 /* pressure reference */
+#define REF_P_L 0x09 /* pressure reference */
+#define REF_P_H 0x0a /* pressure reference */
+#define REF_T_L 0x0b /* temperature reference */
+#define REF_T_H 0x0c /* temperature reference */
+
+#define WHO_AM_I 0x0f /* WhoAmI register */
+#define TP_RESOL 0x10 /* Pres Temp resolution set */
+#define DGAIN_L 0x18 /* Dig Gain (3 regs) */
+
+#define CTRL_REG1 0x20 /* power / ODR control reg */
+#define CTRL_REG2 0x21 /* boot reg */
+#define CTRL_REG3 0x22 /* interrupt control reg */
+#define INT_CFG_REG 0x23 /* 2nterrupt config reg */
+#define INT_SRC_REG 0x24 /* interrupt source reg */
+#define THS_P_L 0x25 /* pressure threshold */
+#define THS_P_H 0x26 /* pressure threshold */
+#define STATUS_REG 0X27 /* status reg */
+
+#define PRESS_OUT_XL 0x28 /* press output (3 regs) */
+#define TEMP_OUT_L 0x2b /* temper output (2 regs) */
+#define DELTAREG_1 0x3c /* deltaPressure reg1 */
+#define DELTAREG_2 0x3d /* deltaPressure reg2 */
+#define DELTAREG_3 0x3e /* deltaPressure reg3 */
+
+/* Register aliases */
+#define P_REF_INDATA_REG REF_P_XL
+#define T_REF_INDATA_REG REF_T_L
+#define P_THS_INDATA_REG THS_P_L
+#define P_OUTDATA_REG PRESS_OUT_XL
+#define T_OUTDATA_REG TEMP_OUT_L
+#define OUTDATA_REG PRESS_OUT_XL
+
+/* Register masks */
+#define LPS331AP_PRS_ENABLE_MASK 0x80 /* ctrl_reg1 */
+#define LPS331AP_PRS_ODR_MASK 0x70 /* ctrl_reg1 */
+#define LPS331AP_PRS_DIFF_MASK 0x08 /* ctrl_reg1 */
+#define LPS331AP_PRS_BDU_MASK 0x04 /* ctrl_reg1 */
+#define LPS331AP_PRS_DELTA_EN_MASK 0x02 /* ctrl_reg1 */
+#define LPS331AP_PRS_BOOT_MASK 0x80 /* ctrl_reg2 */
+#define LPS331AP_PRS_SWRESET_MASK 0x04 /* ctrl_reg2 */
+#define LPS331AP_PRS_AUTOZ_MASK 0x02 /* ctrl_reg2 */
+#define LPS331AP_PRS_ONE_SHOT 0x01 /* ctrl_reg2 */
+#define LPS331AP_PRS_INT1_MASK 0x07 /* ctrl_reg3 */
+#define LPS331AP_PRS_INT2_MASK 0x38 /* ctrl_reg3 */
+#define LPS331AP_PRS_PP_OD_MASK 0x40 /* ctrl_reg3 */
+
+#define LPS331AP_PRS_PM_NORMAL 0x80 /* Power Normal Mode */
+#define LPS331AP_PRS_PM_OFF 0x00 /* Power Down */
+
+#define LPS331AP_PRS_DIFF_ON 0x08 /* En Difference circuitry */
+#define LPS331AP_PRS_DIFF_OFF 0x00 /* Dis Difference circuitry */
+
+#define LPS331AP_PRS_AUTOZ_ON 0x02 /* En AutoZero Function */
+#define LPS331AP_PRS_AUTOZ_OFF 0x00 /* Dis Difference Function */
+
+#define LPS331AP_PRS_BDU_ON 0x04 /* En BDU Block Data Upd */
+#define LPS331AP_PRS_DELTA_EN_ON 0x02 /* En Delta Press registers */
+#define LPS331AP_PRS_DIFF_EN 0x08 /* En diff pressure computing */
+#define LPS331AP_PRS_RES_AVGTEMP_064 0x60
+#define LPS331AP_PRS_RES_AVGTEMP_128 0x70
+#define LPS331AP_PRS_RES_AVGPRES_512 0x0a
+#define LPS331AP_PRS_RES_AVGPRES_384 0x09
+
+#define LPS331AP_PRS_RES_MAX (LPS331AP_PRS_RES_AVGTEMP_128 | \
+ LPS331AP_PRS_RES_AVGPRES_512)
+ /* Max Resol. for 1/7/12,5Hz */
+
+#define LPS331AP_PRS_RES_25HZ (LPS331AP_PRS_RES_AVGTEMP_128 | \
+ LPS331AP_PRS_RES_AVGPRES_384)
+ /* Max Resol. for 25Hz */
+#define LPS331AP_PRS_DRDY_INT1 0x04 /* En INT1 'data ready' */
+#define LPS331AP_PRS_DRDY_INT2 0x20 /* En INT2 'data ready' */
+#define LPS331AP_PRS_P_HIGH_INT1 0x01 /* En INT1 P_high */
+#define LPS331AP_PRS_P_HIGH_INT2 0x08 /* En INT2 P_high */
+#define LPS331AP_PRS_P_LOW_INT1 0x02 /* En INT1 P_low */
+#define LPS331AP_PRS_P_LOW_INT2 0x10 /* En INT2 P_low */
+#define LPS331AP_PRS_PH_E 0x01 /* En prs high evt interrupt */
+#define LPS331AP_PRS_PL_E 0x02 /* En prs low evt interrupt */
+
+#define LPS331AP_PRS_INT_FLAG_PH 0x01 /* Diff press high int flag */
+#define LPS331AP_PRS_INT_FLAG_PL 0x02 /* Diff press low int flag */
+#define LPS331AP_PRS_INT_FLAG_IA 0x04 /* Any int flag */
+
+#define LPS331AP_PRESS_SCALE 4096
+#define LPS331AP_TEMP_SCALE 480
+#define LPS331AP_TEMP_OFFSET_INT 42
+#define LPS331AP_TEMP_OFFSET_FRACT 500000
+
+#define I2C_AUTO_INCREMENT 0x80
+
+/* Register cache indices */
+#define LPS331AP_RES_REF_P_XL 0
+#define LPS331AP_RES_REF_P_L 1
+#define LPS331AP_RES_REF_P_H 2
+#define LPS331AP_RES_REF_T_L 3
+#define LPS331AP_RES_REF_T_H 4
+#define LPS331AP_RES_TP_RESOL 5
+#define LPS331AP_RES_CTRL_REG1 6
+#define LPS331AP_RES_CTRL_REG2 7
+#define LPS331AP_RES_CTRL_REG3 8
+#define LPS331AP_RES_INT_CFG_REG 9
+#define LPS331AP_RES_THS_P_L 10
+#define LPS331AP_RES_THS_P_H 11
+#define REG_ENTRIES 12
+
+/* Poll delays */
+#define LPS331AP_ODR_DELAY_DEFAULT 200
+#define LPS331AP_ODR_DELAY_MINIMUM 40
+#define LPS331AP_DATA_READY_TIMEOUT (HZ * 2)
+#define LPS331AP_DATA_READY_POLL_TIME 10
+
+#define LPS331AP_PRS_DEV_NAME "lps331ap"
+
+/* Barometer and Termometer output data rate (ODR) */
+#define LPS331AP_PRS_ODR_ONESH 0x00 /* one shot both */
+#define LPS331AP_PRS_ODR_1_1 0x10 /* 1 Hz baro, 1 Hz term ODR */
+#define LPS331AP_PRS_ODR_7_7 0x50 /* 7 Hz baro, 7 Hz term ODR */
+#define LPS331AP_PRS_ODR_12_12 0x60 /* 12.5Hz baro, 12.5Hz term ODR */
+#define LPS331AP_PRS_ODR_25_25 0x70 /* 25 Hz baro, 25 Hz term ODR */
+
+enum {
+ LPS331AP_LPS331AP_VENDOR,
+ LPS331AP_NAME,
+ LPS331AP_ODR,
+ LPS331AP_PRESSURE_REF_LEVEL,
+};
+
+enum {
+ LPS331AP_INTERRUPT_SRC_NONE,
+ LPS331AP_INTERRUPT_SRC_INT1,
+ LPS331AP_INTERRUPT_SRC_INT2
+};
+
+enum prs_state {
+ FL_HW_ENABLED,
+ FL_HW_INITIALIZED,
+ FL_PRESS_EV_ENABLED,
+ FL_TEMP_EV_ENABLED
+};
+
+static const struct {
+ unsigned int cutoff_ms;
+ unsigned int mask;
+} lps331ap_odr_table[] = {
+ { 40, LPS331AP_PRS_ODR_25_25 },
+ { 80, LPS331AP_PRS_ODR_12_12 },
+ { 143, LPS331AP_PRS_ODR_7_7 },
+ { 1000, LPS331AP_PRS_ODR_1_1 }
+};
+
+struct outputdata {
+ unsigned int press;
+ int temperature;
+};
+
+struct lps331ap_data {
+ struct i2c_client *client;
+ struct mutex lock;
+ struct outputdata press_temp;
+
+ unsigned int output_data_rate;
+ unsigned long flags;
+ u8 reg_cache[REG_ENTRIES];
+
+ int lps_irq;
+ int lps_irq_src;
+};
+
+static int lps331ap_i2c_read(struct lps331ap_data *prs,
+ u8 *buf, int len)
+{
+ int err;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = prs->client->addr,
+ .flags = prs->client->flags & I2C_M_TEN,
+ .len = 1,
+ .buf = buf,
+ },
+ {
+ .addr = prs->client->addr,
+ .flags = (prs->client->flags & I2C_M_TEN) | I2C_M_RD,
+ .len = len,
+ .buf = buf,
+ },
+ };
+
+ err = i2c_transfer(prs->client->adapter, msgs, 2);
+ if (err != 2) {
+ dev_err(&prs->client->dev, "read transfer error = %d\n", err);
+ err = -EIO;
+ }
+ return err;
+}
+
+static int lps331ap_i2c_write(struct lps331ap_data *prs,
+ u8 *buf, int len)
+{
+ int err;
+ struct i2c_msg msgs[] = {
+ {
+ .addr = prs->client->addr,
+ .flags = prs->client->flags & I2C_M_TEN,
+ .len = len + 1,
+ .buf = buf,
+ },
+ };
+
+ err = i2c_transfer(prs->client->adapter, msgs, 1);
+ if (err != 1) {
+ dev_err(&prs->client->dev, "write transfer error\n");
+ err = -EIO;
+ }
+ return err;
+}
+
+static int lps331ap_hw_init(struct lps331ap_data *prs)
+{
+ int err;
+ u8 buf[6];
+
+ dev_dbg(&prs->client->dev, "hw init start\n");
+
+ buf[0] = WHO_AM_I;
+ err = lps331ap_i2c_read(prs, buf, 1);
+ if (err < 0) {
+ dev_err(&prs->client->dev,
+ "error reading WHO_AM_I: is device available/working?\n");
+ goto err_hw_init;
+ }
+
+ if (buf[0] != WHOAMI_LPS331AP_PRS) {
+ err = -ENODEV;
+ dev_err(&prs->client->dev,
+ "device unknown. Expected: 0x%02x, Replies: 0x%02x\n",
+ WHOAMI_LPS331AP_PRS, buf[0]);
+ goto err_hw_init;
+ }
+
+ buf[0] = (I2C_AUTO_INCREMENT | P_REF_INDATA_REG);
+ buf[1] = prs->reg_cache[LPS331AP_RES_REF_P_XL];
+ buf[2] = prs->reg_cache[LPS331AP_RES_REF_P_L];
+ buf[3] = prs->reg_cache[LPS331AP_RES_REF_P_H];
+ buf[4] = prs->reg_cache[LPS331AP_RES_REF_T_L];
+ buf[5] = prs->reg_cache[LPS331AP_RES_REF_T_H];
+ err = lps331ap_i2c_write(prs, buf, 5);
+ if (err < 0)
+ goto err_hw_init;
+
+ buf[0] = TP_RESOL;
+ buf[1] = prs->reg_cache[LPS331AP_RES_TP_RESOL];
+ err = lps331ap_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto err_hw_init;
+
+ buf[0] = (I2C_AUTO_INCREMENT | P_THS_INDATA_REG);
+ buf[1] = prs->reg_cache[LPS331AP_RES_THS_P_L];
+ buf[2] = prs->reg_cache[LPS331AP_RES_THS_P_H];
+ err = lps331ap_i2c_write(prs, buf, 2);
+ if (err < 0)
+ goto err_hw_init;
+
+ /* clear INT_ACK flag */
+ buf[0] = INT_SRC_REG;
+ err = lps331ap_i2c_read(prs, buf, 1);
+ if (err < 0)
+ return err;
+
+ /* CTRL_REG3 register has to be initialized before powering on */
+ buf[0] = CTRL_REG3;
+ buf[1] = prs->reg_cache[LPS331AP_RES_CTRL_REG3];
+ err = lps331ap_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto err_hw_init;
+
+ buf[0] = (I2C_AUTO_INCREMENT | CTRL_REG1);
+ buf[1] = prs->reg_cache[LPS331AP_RES_CTRL_REG1];
+ buf[2] = prs->reg_cache[LPS331AP_RES_CTRL_REG2];
+ err = lps331ap_i2c_write(prs, buf, 2);
+ if (err < 0)
+ goto err_hw_init;
+
+ set_bit(FL_HW_INITIALIZED, &prs->flags);
+ dev_dbg(&prs->client->dev, "hw init done\n");
+
+ return 0;
+
+err_hw_init:
+ clear_bit(FL_HW_INITIALIZED, &prs->flags);
+ dev_err(&prs->client->dev, "hw init error 0x%02x,0x%02x: %d\n",
+ buf[0], buf[1], err);
+ return err;
+}
+
+static int lps331ap_device_power_off(struct lps331ap_data *prs)
+{
+ int err;
+ u8 buf[2];
+
+ prs->reg_cache[LPS331AP_RES_CTRL_REG1]
+ &= ~LPS331AP_PRS_PM_NORMAL;
+
+ buf[0] = CTRL_REG1;
+ buf[1] = prs->reg_cache[LPS331AP_RES_CTRL_REG1];
+
+ err = lps331ap_i2c_write(prs, buf, 1);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+static int lps331ap_device_power_on(struct lps331ap_data *prs)
+{
+ u8 buf[2];
+ int err;
+
+ prs->reg_cache[LPS331AP_RES_CTRL_REG1] |= LPS331AP_PRS_PM_NORMAL;
+
+ if (!test_bit(FL_HW_INITIALIZED, &prs->flags)) {
+ err = lps331ap_hw_init(prs);
+ lps331ap_device_power_off(prs);
+ } else {
+ buf[0] = CTRL_REG1;
+ buf[1] = prs->reg_cache[LPS331AP_RES_CTRL_REG1];
+ err = lps331ap_i2c_write(prs, buf, 1);
+ }
+
+ return err;
+}
+
+static int lps331ap_update_odr(struct lps331ap_data *prs, int delay_ms)
+{
+ int err = -1;
+ int i;
+
+ u8 buf[2];
+ u8 init_val, updated_val;
+ u8 curr_val, new_val;
+ u8 mask = LPS331AP_PRS_ODR_MASK;
+ u8 resol = LPS331AP_PRS_RES_MAX;
+
+ /*
+ * Following, looks for the longest possible odr interval scrolling the
+ * odr_table vector from the end (longest period) backward (shortest
+ * period), to support the poll_interval requested by the system.
+ * It must be the longest period shorter then the set poll period.
+ */
+ for (i = ARRAY_SIZE(lps331ap_odr_table) - 1; i >= 0; i--) {
+ if ((lps331ap_odr_table[i].cutoff_ms <= delay_ms)
+ || (i == 0))
+ break;
+ }
+
+ prs->output_data_rate = lps331ap_odr_table[i].cutoff_ms;
+
+ new_val = lps331ap_odr_table[i].mask;
+ if (new_val == LPS331AP_PRS_ODR_25_25)
+ resol = LPS331AP_PRS_RES_25HZ;
+
+ if (!test_bit(FL_HW_ENABLED, &prs->flags))
+ return 0;
+
+ buf[0] = CTRL_REG1;
+ err = lps331ap_i2c_read(prs, buf, 1);
+ if (err < 0)
+ goto error;
+ /* work on all but ENABLE bits */
+ init_val = buf[0];
+ prs->reg_cache[LPS331AP_RES_CTRL_REG1] = init_val;
+
+ /* disable */
+ curr_val = ((LPS331AP_PRS_ENABLE_MASK & LPS331AP_PRS_PM_OFF)
+ | ((~LPS331AP_PRS_ENABLE_MASK) & init_val));
+ buf[0] = CTRL_REG1;
+ buf[1] = curr_val;
+ err = lps331ap_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto error;
+
+ buf[0] = CTRL_REG1;
+ updated_val = ((mask & new_val) | ((~mask) & curr_val));
+
+ buf[0] = CTRL_REG1;
+ buf[1] = updated_val;
+ err = lps331ap_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto error;
+
+ /* enable */
+ curr_val = ((LPS331AP_PRS_ENABLE_MASK & LPS331AP_PRS_PM_NORMAL)
+ | ((~LPS331AP_PRS_ENABLE_MASK) & updated_val));
+ buf[0] = CTRL_REG1;
+ buf[1] = curr_val;
+ err = lps331ap_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto error;
+
+ buf[0] = TP_RESOL;
+ buf[1] = resol;
+ err = lps331ap_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto error;
+
+ prs->reg_cache[LPS331AP_RES_CTRL_REG1] = curr_val;
+ prs->reg_cache[LPS331AP_RES_TP_RESOL] = resol;
+
+ return err;
+
+error:
+ dev_err(&prs->client->dev, "update odr failed 0x%02x,0x%02x: %d\n",
+ buf[0], buf[1], err);
+
+ return err;
+}
+
+static int lps331ap_set_press_reference(struct lps331ap_data *prs,
+ s32 new_reference)
+{
+ u8 buf[4], *ref;
+ __le32 new_ref;
+ int err;
+
+ new_ref = cpu_to_le32(new_reference);
+ ref = (u8 *)&new_ref;
+
+ buf[0] = (I2C_AUTO_INCREMENT | P_REF_INDATA_REG);
+ buf[1] = ref[0];
+ buf[2] = ref[1];
+ buf[3] = ref[2];
+
+ err = lps331ap_i2c_write(prs, buf, 3);
+ if (err < 0)
+ return err;
+
+ prs->reg_cache[LPS331AP_RES_REF_P_XL] = ref[0];
+ prs->reg_cache[LPS331AP_RES_REF_P_L] = ref[1];
+ prs->reg_cache[LPS331AP_RES_REF_P_H] = ref[2];
+
+ return 0;
+}
+
+static int lps331ap_get_press_reference(struct lps331ap_data *prs,
+ s32 *reference)
+{
+ u8 buf[4];
+ int err;
+
+ memset(buf, 0, sizeof(buf));
+ buf[0] = (I2C_AUTO_INCREMENT | P_REF_INDATA_REG);
+ err = lps331ap_i2c_read(prs, buf, 3);
+
+ if (err < 0)
+ return err;
+
+ *reference = le32_to_cpup((__le32 *) buf);
+
+ return 0;
+}
+
+static int lps331ap_enable(struct lps331ap_data *prs)
+{
+ int err;
+
+ if (!test_bit(FL_HW_ENABLED, &prs->flags)) {
+ err = lps331ap_device_power_on(prs);
+ if (err < 0) {
+ clear_bit(FL_HW_ENABLED, &prs->flags);
+ return err;
+ }
+ set_bit(FL_HW_ENABLED, &prs->flags);
+ }
+
+ return 0;
+}
+
+static int lps331ap_disable(struct lps331ap_data *prs)
+{
+ int err;
+
+ if (!test_bit(FL_HW_ENABLED, &prs->flags))
+ return 0;
+
+ err = lps331ap_device_power_off(prs);
+ clear_bit(FL_HW_ENABLED, &prs->flags);
+
+ return err;
+}
+
+static int lps331ap_get_presstemp_data(struct lps331ap_data *prs,
+ struct outputdata *out)
+{
+ /*
+ * Data bytes from hardware:
+ * PRESS_OUT_XL, PRESS_OUT_L, PRESS_OUT_H,
+ * TEMP_OUT_L, TEMP_OUT_H.
+ */
+ u8 prs_data[5] = { 0, };
+ int err = 0;
+
+ s32 pressure = 0;
+ s16 temperature = 0;
+
+ int reg_to_read = 5;
+
+ prs_data[0] = (I2C_AUTO_INCREMENT | OUTDATA_REG);
+ err = lps331ap_i2c_read(prs, prs_data, reg_to_read);
+ if (err < 0)
+ return err;
+
+ dev_dbg(&prs->client->dev,
+ "temp out tH = 0x%02x, tL = 0x%02x",
+ prs_data[4], prs_data[3]);
+ dev_dbg(&prs->client->dev,
+ "press_out: pH = 0x%02x, pL = 0x%02x, pXL= 0x%02x\n",
+ prs_data[2], prs_data[1], prs_data[0]);
+
+ pressure = (s32) ((((s8) prs_data[2]) << 16) |
+ (prs_data[1] << 8) | (prs_data[0]));
+ temperature = (s16) ((((s8) prs_data[4]) << 8) | (prs_data[3]));
+
+ out->press = pressure;
+ out->temperature = temperature;
+
+ return 0;
+}
+
+static ssize_t lps331ap_get_press_ref(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct lps331ap_data *prs = iio_priv(dev_info);
+ s32 val;
+ int err;
+
+ mutex_lock(&prs->lock);
+ err = lps331ap_get_press_reference(prs, &val);
+ mutex_unlock(&prs->lock);
+ if (err < 0)
+ return err;
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t lps331ap_set_press_ref(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t size)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct lps331ap_data *prs = iio_priv(dev_info);
+ long val;
+ int err;
+
+ if (kstrtol(buf, 10, &val))
+ return -EINVAL;
+
+ if (val < PR_ABS_MIN || val > PR_ABS_MAX)
+ return -EINVAL;
+
+ mutex_lock(&prs->lock);
+ err = lps331ap_set_press_reference(prs, val);
+ mutex_unlock(&prs->lock);
+
+ return err < 0 ? err : size;
+}
+static ssize_t lps331ap_get_odr(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int val;
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct lps331ap_data *prs = iio_priv(dev_info);
+
+ mutex_lock(&prs->lock);
+ val = prs->output_data_rate;
+ mutex_unlock(&prs->lock);
+
+ return sprintf(buf, "%d\n", val);
+}
+
+static ssize_t lps331ap_set_odr(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t size)
+{
+ struct iio_dev *dev_info = dev_to_iio_dev(dev);
+ struct lps331ap_data *prs = iio_priv(dev_info);
+ unsigned long delay_ms = 0;
+ unsigned int delay_min = LPS331AP_ODR_DELAY_MINIMUM;
+
+ if (kstrtoul(buf, 10, &delay_ms))
+ return -EINVAL;
+ if (!delay_ms)
+ return -EINVAL;
+
+ dev_dbg(&prs->client->dev, "delay_ms passed = %ld\n", delay_ms);
+ delay_ms = max_t(unsigned int, (unsigned int)delay_ms, delay_min);
+
+ mutex_lock(&prs->lock);
+ prs->output_data_rate = delay_ms;
+ lps331ap_update_odr(prs, delay_ms);
+
+ if (delay_ms == LPS331AP_ODR_DELAY_MINIMUM)
+ dev_dbg(&prs->client->dev, "delay limited to 40ms\n");
+
+ mutex_unlock(&prs->lock);
+
+ return size;
+}
+
+static ssize_t lps331_vendor_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", LPS331AP_VENDOR);
+}
+
+static ssize_t lps331_name_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%s\n", LPS331AP_CHIP_ID);
+}
+
+static int lps331ap_wait_one_shot_ready_polled(struct iio_dev *indio_dev)
+{
+ struct lps331ap_data *prs = iio_priv(indio_dev);
+ u32 timeout_ms = LPS331AP_DATA_READY_TIMEOUT;
+ u8 buf;
+ int err;
+
+ /* Wait for the conversion to complete. */
+ while (timeout_ms) {
+ msleep(LPS331AP_DATA_READY_POLL_TIME);
+ buf = CTRL_REG2;
+ err = lps331ap_i2c_read(prs, &buf, 1);
+ if (err < 0)
+ return err;
+ if (!(buf & LPS331AP_PRS_ONE_SHOT))
+ break;
+ timeout_ms -= LPS331AP_DATA_READY_POLL_TIME;
+ }
+ if (!timeout_ms) {
+ dev_err(&prs->client->dev, "Conversion timeout occurred.\n");
+ return -ETIME;
+ }
+
+ return err;
+}
+
+static u8 get_drdy_reg_mask(struct lps331ap_data *prs)
+{
+ if (prs->lps_irq_src == LPS331AP_INTERRUPT_SRC_INT1)
+ return LPS331AP_PRS_DRDY_INT1;
+ else
+ return LPS331AP_PRS_DRDY_INT2;
+}
+
+static int lps331ap_read_presstemp(struct iio_dev *indio_dev, int index,
+ int *val)
+{
+ struct lps331ap_data *prs = iio_priv(indio_dev);
+ u8 drdy_reg_mask;
+ int enabled;
+ char buf[2], curr_odr;
+ int err;
+
+ mutex_lock(&prs->lock);
+
+ drdy_reg_mask = get_drdy_reg_mask(prs);
+ /*
+ * Set 'one shot' mode if either device is in power down mode
+ * or it is powered up but 'data ready' interrupt isn't enabled.
+ * If this condition evaluates to false then it means that the recent
+ * output is being cached periodically by 'data ready' interrupt
+ * handler, so we can return the cached value.
+ */
+ enabled = test_bit(FL_HW_ENABLED, &prs->flags);
+ if (!enabled ||
+ !(prs->reg_cache[LPS331AP_RES_CTRL_REG3] & drdy_reg_mask)) {
+ /* ensure device is in power down mode */
+ lps331ap_disable(prs);
+
+ /* set ODR configuration to 'one shot' */
+ curr_odr = prs->reg_cache[LPS331AP_RES_CTRL_REG1]
+ & LPS331AP_PRS_ODR_MASK;
+ prs->reg_cache[LPS331AP_RES_CTRL_REG1]
+ &= ~LPS331AP_PRS_ODR_MASK;
+ buf[0] = CTRL_REG1;
+ buf[1] = prs->reg_cache[LPS331AP_RES_CTRL_REG1];
+ err = lps331ap_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto exit;
+
+ /* power on the device */
+ err = lps331ap_enable(prs);
+ if (err < 0)
+ goto exit;
+
+ /* set ONE_SHOT mode */
+ buf[0] = CTRL_REG2;
+ buf[1] = prs->reg_cache[LPS331AP_RES_CTRL_REG2]
+ | LPS331AP_PRS_ONE_SHOT;
+ err = lps331ap_i2c_write(prs, buf, 1);
+
+ if (err < 0)
+ goto exit;
+
+ /* wait for the end of coversion */
+ err = lps331ap_wait_one_shot_ready_polled(indio_dev);
+ if (err < 0)
+ goto exit;
+
+ /* read output data */
+ err = lps331ap_get_presstemp_data(prs, &prs->press_temp);
+ if (err < 0)
+ goto exit;
+
+ /* bring back previous ODR settings */
+ prs->reg_cache[LPS331AP_RES_CTRL_REG1]
+ |= curr_odr;
+ buf[0] = CTRL_REG1;
+ buf[1] = prs->reg_cache[LPS331AP_RES_CTRL_REG1];
+ err = lps331ap_i2c_write(prs, buf, 1);
+
+ if (!enabled)
+ lps331ap_disable(prs);
+ }
+
+ *val = index == 0 ? prs->press_temp.press : prs->press_temp.temperature;
+
+ mutex_unlock(&prs->lock);
+
+ return IIO_VAL_INT;
+
+exit:
+ mutex_unlock(&prs->lock);
+ return -EINVAL;
+}
+
+static irqreturn_t lps331ap_irq_handler(int irq, void *data)
+{
+ struct iio_dev *indio_dev = data;
+ struct lps331ap_data *prs = iio_priv(indio_dev);
+ int err;
+
+ if (test_bit(FL_PRESS_EV_ENABLED, &prs->flags))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_PRESSURE, 0,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns());
+ if (test_bit(FL_TEMP_EV_ENABLED, &prs->flags))
+ iio_push_event(indio_dev,
+ IIO_UNMOD_EVENT_CODE(IIO_TEMP, 1,
+ IIO_EV_TYPE_MAG,
+ IIO_EV_DIR_EITHER),
+ iio_get_time_ns());
+
+ err = lps331ap_get_presstemp_data(prs, &prs->press_temp);
+ if (err < 0)
+ dev_err_ratelimited(&prs->client->dev,
+ "get_presstemp_data failed\n");
+
+ return IRQ_HANDLED;
+}
+
+static int lps331ap_setup_irq(struct iio_dev *indio_dev)
+{
+ struct lps331ap_data *prs = iio_priv(indio_dev);
+ struct i2c_client *client = prs->client;
+ int err;
+
+ err = request_threaded_irq(prs->lps_irq, NULL,
+ lps331ap_irq_handler,
+ IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+ dev_name(&client->dev), indio_dev);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "irq %d request failed: %d\n",
+ prs->lps_irq, err);
+ return err;
+ }
+
+ return 0;
+}
+
+static int lps331ap_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 lps331ap_read_presstemp(indio_dev, chan->address, val);
+ case IIO_CHAN_INFO_SCALE:
+ switch (chan->type) {
+ case IIO_PRESSURE:
+ *val = LPS331AP_PRESS_SCALE;
+ return IIO_VAL_INT;
+ case IIO_TEMP:
+ *val = LPS331AP_TEMP_SCALE;
+ return IIO_VAL_INT;
+ default:
+ return -EINVAL;
+ }
+ case IIO_CHAN_INFO_OFFSET:
+ /* Only the temperature channel has an offset. */
+ *val = LPS331AP_TEMP_OFFSET_INT;
+ *val2 = LPS331AP_TEMP_OFFSET_FRACT;
+ return IIO_VAL_INT_PLUS_MICRO;
+ }
+
+ return -EINVAL;
+}
+
+static int lps331ap_read_event_val(struct iio_dev *indio_dev, u64 event_code,
+ int *val)
+{
+ struct lps331ap_data *prs = iio_priv(indio_dev);
+ int chan_type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code);
+ int event_type = IIO_EVENT_CODE_EXTRACT_TYPE(event_code);
+ u8 drdy_reg_mask;
+
+ drdy_reg_mask = get_drdy_reg_mask(prs);
+
+ if (event_type == IIO_EV_TYPE_MAG) {
+ /*
+ * If events are enabled the output data is being
+ * cached in the interrupt handler and thus we
+ * can return it here.
+ */
+ if (prs->reg_cache[LPS331AP_RES_CTRL_REG3]
+ & drdy_reg_mask)
+ switch (chan_type) {
+ case IIO_PRESSURE:
+ *val = prs->press_temp.press;
+ break;
+ case IIO_TEMP:
+ *val = prs->press_temp.temperature;
+ break;
+ } else
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int lps331ap_write_event_config(struct iio_dev *indio_dev,
+ u64 event_code, int state)
+{
+ struct lps331ap_data *prs = iio_priv(indio_dev);
+ int event_type = IIO_EVENT_CODE_EXTRACT_TYPE(event_code);
+ int chan_type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code);
+ u8 ctrl_reg3 = prs->reg_cache[LPS331AP_RES_CTRL_REG3];
+ u8 drdy_reg_mask;
+ char buf[2];
+ enum prs_state event_state = (chan_type == IIO_PRESSURE) ?
+ FL_PRESS_EV_ENABLED : FL_TEMP_EV_ENABLED;
+ int err = 0;
+
+ if (prs->lps_irq_src == LPS331AP_INTERRUPT_SRC_NONE) {
+ dev_err(&prs->client->dev,
+ "current platform doesn't support LPS331 interrupts\n");
+ return -EINVAL;
+ }
+
+ mutex_lock(&prs->lock);
+
+ drdy_reg_mask = get_drdy_reg_mask(prs);
+
+ if (state) {
+ /* Don't do anything if the event is already enabled. */
+ if (test_bit(event_state, &prs->flags))
+ goto done;
+ switch (event_type) {
+ case IIO_EV_TYPE_MAG:
+ ctrl_reg3 |= drdy_reg_mask;
+ set_bit(event_state, &prs->flags);
+ break;
+ default:
+ goto err_unsupported_event_type;
+ }
+ } else {
+ switch (event_type) {
+ case IIO_EV_TYPE_MAG:
+ clear_bit(event_state, &prs->flags);
+ /*
+ * Disable 'data ready' interrupt only if
+ * there is no enabled event.
+ */
+ if (!test_bit(FL_PRESS_EV_ENABLED, &prs->flags) &&
+ !test_bit(FL_TEMP_EV_ENABLED, &prs->flags))
+ ctrl_reg3 &= ~drdy_reg_mask;
+ break;
+ default:
+ goto err_unsupported_event_type;
+ }
+ }
+
+ /* Setup new interrupt configuration. */
+ prs->reg_cache[LPS331AP_RES_CTRL_REG3] = ctrl_reg3;
+
+ buf[0] = CTRL_REG3;
+ buf[1] = prs->reg_cache[LPS331AP_RES_CTRL_REG3];
+ err = lps331ap_i2c_write(prs, buf, 1);
+ if (err < 0)
+ goto err_write_event_config;
+
+ /*
+ * The device should be powered on only
+ * if at least one event is enabled.
+ */
+ if (ctrl_reg3 & drdy_reg_mask) {
+ err = lps331ap_enable(prs);
+ if (err < 0)
+ goto err_write_event_config;
+ } else {
+ err = lps331ap_disable(prs);
+ if (err < 0)
+ goto err_write_event_config;
+ }
+
+ /* clear INT_ACK flag */
+ buf[0] = INT_SRC_REG;
+ err = lps331ap_i2c_read(prs, buf, 1);
+ if (err < 0)
+ goto err_write_event_config;
+done:
+ mutex_unlock(&prs->lock);
+
+ return 0;
+
+err_unsupported_event_type:
+ err = -EINVAL;
+ dev_err(&prs->client->dev, "unsupported event type\n");
+err_write_event_config:
+ clear_bit(event_state, &prs->flags);
+ mutex_unlock(&prs->lock);
+ dev_err(&prs->client->dev, "writing event config failed\n");
+
+ return err;
+}
+
+static int lps331ap_read_event_config(struct iio_dev *indio_dev,
+ u64 event_code)
+{
+ struct lps331ap_data *prs = iio_priv(indio_dev);
+ int event_type = IIO_EVENT_CODE_EXTRACT_TYPE(event_code);
+ int chan_type = IIO_EVENT_CODE_EXTRACT_CHAN_TYPE(event_code);
+ enum prs_state event_state = (chan_type == IIO_PRESSURE) ?
+ FL_PRESS_EV_ENABLED : FL_TEMP_EV_ENABLED;
+ int val;
+
+ if (prs->lps_irq_src == LPS331AP_INTERRUPT_SRC_NONE)
+ return -EINVAL;
+
+ switch (event_type) {
+ case IIO_EV_TYPE_MAG:
+ val = test_bit(event_state, &prs->flags);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return val;
+}
+
+static IIO_DEVICE_ATTR(odr, 0664,
+ lps331ap_get_odr, lps331ap_set_odr, LPS331AP_ODR);
+static IIO_DEVICE_ATTR(pressure_reference_level, 0664,
+ lps331ap_get_press_ref,
+ lps331ap_set_press_ref, LPS331AP_PRESSURE_REF_LEVEL);
+static IIO_DEVICE_ATTR(vendor, 0644, lps331_vendor_show, NULL,
+ LPS331AP_LPS331AP_VENDOR);
+static IIO_DEVICE_ATTR(name, 0644, lps331_name_show, NULL, LPS331AP_NAME);
+
+static struct attribute *lps331ap_attributes[] = {
+ &iio_dev_attr_vendor.dev_attr.attr,
+ &iio_dev_attr_name.dev_attr.attr,
+ &iio_dev_attr_odr.dev_attr.attr,
+ &iio_dev_attr_pressure_reference_level.dev_attr.attr,
+ NULL
+};
+
+static struct attribute_group lps331ap_attribute_group = {
+ .attrs = lps331ap_attributes,
+};
+
+#define LPS331AP_PRESSURE_CHANNEL(index) \
+ { \
+ .channel = index, \
+ .address = index, \
+ .type = IIO_PRESSURE, \
+ .modified = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE), \
+ .event_mask = IIO_EV_BIT(IIO_EV_TYPE_MAG, \
+ IIO_EV_DIR_EITHER), \
+ }
+
+#define LPS331AP_TEMP_CHANNEL(index) \
+ { \
+ .channel = index, \
+ .address = index, \
+ .type = IIO_TEMP, \
+ .modified = 0, \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | \
+ BIT(IIO_CHAN_INFO_SCALE) | \
+ BIT(IIO_CHAN_INFO_OFFSET), \
+ .event_mask = IIO_EV_BIT(IIO_EV_TYPE_MAG, \
+ IIO_EV_DIR_EITHER), \
+ }
+
+static const struct iio_chan_spec lps331ap_channels[] = {
+ LPS331AP_PRESSURE_CHANNEL(0), LPS331AP_TEMP_CHANNEL(1),
+};
+
+static const struct iio_info lps331ap_info = {
+ .attrs = &lps331ap_attribute_group,
+ .read_raw = &lps331ap_read_raw,
+ .read_event_value = &lps331ap_read_event_val,
+ .read_event_config = &lps331ap_read_event_config,
+ .write_event_config = &lps331ap_write_event_config,
+ .driver_module = THIS_MODULE,
+};
+
+static int lps331ap_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct lps331ap_data *prs;
+ struct iio_dev *indio_dev;
+ int int1_src, int2_src;
+ int err = -EINVAL;
+
+ indio_dev = iio_device_alloc(sizeof(struct lps331ap_data));
+ if (indio_dev == NULL)
+ return -ENOMEM;
+
+ prs = iio_priv(indio_dev);
+
+ if (client->dev.of_node) {
+ int1_src = irq_of_parse_and_map(client->dev.of_node, 0);
+ int2_src = irq_of_parse_and_map(client->dev.of_node, 1);
+
+ if (int1_src) {
+ prs->lps_irq = int1_src;
+ prs->lps_irq_src = LPS331AP_INTERRUPT_SRC_INT1;
+ } else if (int2_src) {
+ prs->lps_irq = int2_src;
+ prs->lps_irq_src = LPS331AP_INTERRUPT_SRC_INT2;
+ } else {
+ prs->lps_irq_src = LPS331AP_INTERRUPT_SRC_NONE;
+ }
+ } else {
+ prs->lps_irq = -EINVAL;
+ }
+
+ prs->output_data_rate = LPS331AP_ODR_DELAY_DEFAULT;
+ prs->client = client;
+ mutex_init(&prs->lock);
+ indio_dev->dev.parent = &client->dev;
+ indio_dev->channels = lps331ap_channels;
+ indio_dev->num_channels = ARRAY_SIZE(lps331ap_channels);
+ indio_dev->info = &lps331ap_info;
+ indio_dev->modes = INDIO_DIRECT_MODE;
+
+ if (prs->lps_irq) {
+ err = lps331ap_setup_irq(indio_dev);
+ if (err < 0) {
+ dev_err(&client->dev,
+ "Error setting data ready interrupt\n");
+ goto err_free_data;
+ }
+ }
+
+ i2c_set_clientdata(client, indio_dev);
+
+ memset(prs->reg_cache, 0, sizeof(prs->reg_cache));
+
+ /* Do not update output registers until MSB and LSB reading. */
+ prs->reg_cache[LPS331AP_RES_CTRL_REG1] = LPS331AP_PRS_BDU_ON;
+
+ /* Perform hw init. */
+ err = lps331ap_device_power_on(prs);
+ if (err < 0) {
+ dev_err(&client->dev, "power on failed: %d\n", err);
+ goto err_free_data;
+ }
+
+ set_bit(FL_HW_ENABLED, &prs->flags);
+
+ err = lps331ap_update_odr(prs, prs->output_data_rate);
+ if (err < 0) {
+ dev_err(&client->dev, "update_odr failed\n");
+ goto err_power_off;
+ }
+
+ lps331ap_device_power_off(prs);
+
+ clear_bit(FL_HW_ENABLED, &prs->flags);
+
+ err = iio_device_register(indio_dev);
+ if (err < 0)
+ goto err_free_data;
+
+ return 0;
+
+err_power_off:
+ lps331ap_device_power_off(prs);
+err_free_data:
+ if (prs->lps_irq)
+ free_irq(prs->lps_irq, indio_dev);
+ iio_device_free(indio_dev);
+
+ return err;
+}
+
+static int lps331ap_remove(struct i2c_client *client)
+{
+ struct iio_dev *indio_dev = i2c_get_clientdata(client);
+ struct lps331ap_data *prs = iio_priv(indio_dev);
+
+ if (prs->lps_irq)
+ free_irq(prs->lps_irq, indio_dev);
+
+ lps331ap_device_power_off(prs);
+
+ iio_device_unregister(indio_dev);
+ iio_device_free(indio_dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id lps331ap_id[] = {
+ { LPS331AP_PRS_DEV_NAME, 0 },
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, lps331ap_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id lps331ap_of_match[] = {
+ { .compatible = "st,lps331ap" },
+ { .compatible = "lps331ap" },
+ { }
+};
+#endif
+
+static struct i2c_driver lps331ap_driver = {
+ .driver = {
+ .name = LPS331AP_PRS_DEV_NAME,
+ .of_match_table = of_match_ptr(lps331ap_of_match),
+ },
+ .probe = lps331ap_probe,
+ .remove = lps331ap_remove,
+ .id_table = lps331ap_id,
+};
+
+module_i2c_driver(lps331ap_driver);
+
+MODULE_DESCRIPTION("STMicrolectronics LPS331AP pressure sensor IIO driver");
+MODULE_AUTHOR("Jacek Anaszewski <j.anaszewski@samsung.com>");
+MODULE_AUTHOR("Matteo Dameno, STMicroelectronic <matteo.dameno@st.com>");
+MODULE_AUTHOR("Carmine Iascone, STMicroelectronic <carmine.iascone@st.com>");
+MODULE_LICENSE("GPL");