diff options
author | Jacek Anaszewski <j.anaszewski@samsung.com> | 2013-06-21 10:52:31 +0200 |
---|---|---|
committer | Chanho Park <chanho61.park@samsung.com> | 2014-11-18 11:43:11 +0900 |
commit | a435d23fea3c64f815109752dbe1ff13c29c5c06 (patch) | |
tree | 29d1c918e8fa02b0e9b42e81f4ddd665cc1fc437 | |
parent | 516de39ab361d4af576e5e4629f62ef06a3f9ac8 (diff) | |
download | linux-3.10-a435d23fea3c64f815109752dbe1ff13c29c5c06.tar.gz linux-3.10-a435d23fea3c64f815109752dbe1ff13c29c5c06.tar.bz2 linux-3.10-a435d23fea3c64f815109752dbe1ff13c29c5c06.zip |
iio: Add driver for the LPS331AP barometer sensor
Add new driver for the barometer device. The driver is
compliant with IIO framework, and exposes two channels
for reading the pressure and the temperature. The output
data can be read either in 'one shot' mode or by exploiting
IIO events.
Signed-off-by: Jacek Anaszewski <j.anaszewski@samsung.com>
Reviewed-by: Sylwester Nawrocki <s.nawrocki@samsung.com>
Signed-off-by: Kyungmin Park <kyungmin.park@samsung.com>
-rw-r--r-- | Documentation/devicetree/bindings/iio/barometer/lps331ap.txt | 40 | ||||
-rw-r--r-- | drivers/iio/Kconfig | 1 | ||||
-rw-r--r-- | drivers/iio/Makefile | 1 | ||||
-rw-r--r-- | drivers/iio/barometer/Kconfig | 12 | ||||
-rw-r--r-- | drivers/iio/barometer/Makefile | 7 | ||||
-rw-r--r-- | drivers/iio/barometer/lps331ap.c | 1252 |
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"); |