diff options
-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"); |