Re: [PATCH v4 1/2] iio: humidity: add support to hts221 rh/temp combo device

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



> On 13/10/16 21:06, Lorenzo Bianconi wrote:
>> Add support to STM HTS221 humidity + temperature sensor
>>
>> http://www.st.com/resource/en/datasheet/hts221.pdf
>>
>> - continuous mode support
>> - i2c support
>> - spi support
>> - trigger mode support
>>
>> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>
> Couple of really really trivial things inline. I've fixed up.
>
> Applied to the togreg branch of iio.git and pushed out as testing
> for the autobuilders to play with it.  This certainly ended up
> much simpler than it started after you chased down how the hardware
> was actually working!
>
> Thanks,
>
> Jonathan
>
> p.s. Just discovered I didn't send this last weekend when I actually applied the patch!
> Sorry about that.

No worries ;)

>> ---
>>  drivers/iio/humidity/Kconfig         |  22 ++
>>  drivers/iio/humidity/Makefile        |   7 +
>>  drivers/iio/humidity/hts221.h        |  73 ++++
>>  drivers/iio/humidity/hts221_buffer.c | 168 +++++++++
>>  drivers/iio/humidity/hts221_core.c   | 687 +++++++++++++++++++++++++++++++++++
>>  drivers/iio/humidity/hts221_i2c.c    | 110 ++++++
>>  drivers/iio/humidity/hts221_spi.c    | 125 +++++++
>>  7 files changed, 1192 insertions(+)
>>  create mode 100644 drivers/iio/humidity/hts221.h
>>  create mode 100644 drivers/iio/humidity/hts221_buffer.c
>>  create mode 100644 drivers/iio/humidity/hts221_core.c
>>  create mode 100644 drivers/iio/humidity/hts221_i2c.c
>>  create mode 100644 drivers/iio/humidity/hts221_spi.c
>>
>> diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig
>> index b17e2e2..6fd11d0 100644
>> --- a/drivers/iio/humidity/Kconfig
>> +++ b/drivers/iio/humidity/Kconfig
>> @@ -34,6 +34,28 @@ config HDC100X
>>         To compile this driver as a module, choose M here: the module
>>         will be called hdc100x.
>>
>> +config HTS221
>> +     tristate "STMicroelectronics HTS221 sensor Driver"
>> +     depends on (I2C || SPI)
>> +     select IIO_BUFFER
>> +     select IIO_TRIGGERED_BUFFER
>> +     select HTS221_I2C if (I2C)
>> +     select HTS221_SPI if (SPI_MASTER)
>> +     help
>> +       Say yes here to build support for STMicroelectronics HTS221
>> +       temperature-humidity sensor
>> +
>> +       To compile this driver as a module, choose M here: the module
>> +       will be called hts221.
>> +
>> +config HTS221_I2C
>> +     tristate
>> +     depends on HTS221
>> +
>> +config HTS221_SPI
>> +     tristate
>> +     depends on HTS221
>> +
>>  config HTU21
>>       tristate "Measurement Specialties HTU21 humidity & temperature sensor"
>>       depends on I2C
>> diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile
>> index 4a73442..a6850e4 100644
>> --- a/drivers/iio/humidity/Makefile
>> +++ b/drivers/iio/humidity/Makefile
>> @@ -5,6 +5,13 @@
>>  obj-$(CONFIG_AM2315) += am2315.o
>>  obj-$(CONFIG_DHT11) += dht11.o
>>  obj-$(CONFIG_HDC100X) += hdc100x.o
>> +
>> +hts221-y := hts221_core.o \
>> +         hts221_buffer.o
>> +obj-$(CONFIG_HTS221) += hts221.o
>> +obj-$(CONFIG_HTS221_I2C) += hts221_i2c.o
>> +obj-$(CONFIG_HTS221_SPI) += hts221_spi.o
>> +
>>  obj-$(CONFIG_HTU21) += htu21.o
>>  obj-$(CONFIG_SI7005) += si7005.o
>>  obj-$(CONFIG_SI7020) += si7020.o
>> diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h
>> new file mode 100644
>> index 0000000..0d2993c
>> --- /dev/null
>> +++ b/drivers/iio/humidity/hts221.h
>> @@ -0,0 +1,73 @@
>> +/*
>> + * STMicroelectronics hts221 sensor driver
>> + *
>> + * Copyright 2016 STMicroelectronics Inc.
>> + *
>> + * Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#ifndef HTS221_H
>> +#define HTS221_H
>> +
>> +#define HTS221_DEV_NAME              "hts221"
>> +
>> +#include <linux/iio/iio.h>
>> +
>> +#define HTS221_RX_MAX_LENGTH 8
>> +#define HTS221_TX_MAX_LENGTH 8
>> +
>> +#define HTS221_DATA_SIZE     2
>> +
>> +struct hts221_transfer_buffer {
>> +     u8 rx_buf[HTS221_RX_MAX_LENGTH];
>> +     u8 tx_buf[HTS221_TX_MAX_LENGTH] ____cacheline_aligned;
>> +};
>> +
>> +struct hts221_transfer_function {
>> +     int (*read)(struct device *dev, u8 addr, int len, u8 *data);
>> +     int (*write)(struct device *dev, u8 addr, int len, u8 *data);
>> +};
>> +
>> +#define HTS221_AVG_DEPTH     8
>> +struct hts221_avg_avl {
>> +     u16 avg;
>> +     u8 val;
>> +};
>> +
>> +enum hts221_sensor_type {
>> +     HTS221_SENSOR_H,
>> +     HTS221_SENSOR_T,
>> +     HTS221_SENSOR_MAX,
>> +};
>> +
>> +struct hts221_sensor {
>> +     u8 cur_avg_idx;
>> +     int slope, b_gen;
>> +};
>> +
>> +struct hts221_hw {
>> +     const char *name;
>> +     struct device *dev;
>> +
>> +     struct mutex lock;
>> +     struct iio_trigger *trig;
>> +     int irq;
>> +
>> +     struct hts221_sensor sensors[HTS221_SENSOR_MAX];
>> +
>> +     u8 odr;
>> +
>> +     const struct hts221_transfer_function *tf;
>> +     struct hts221_transfer_buffer tb;
>> +};
>> +
>> +int hts221_config_drdy(struct hts221_hw *hw, bool enable);
>> +int hts221_probe(struct iio_dev *iio_dev);
>> +int hts221_power_on(struct hts221_hw *hw);
>> +int hts221_power_off(struct hts221_hw *hw);
>> +int hts221_allocate_buffers(struct hts221_hw *hw);
>> +int hts221_allocate_triggers(struct hts221_hw *hw);
>> +
>> +#endif /* HTS221_H */
>> diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c
>> new file mode 100644
>> index 0000000..6b174fd
>> --- /dev/null
>> +++ b/drivers/iio/humidity/hts221_buffer.c
>> @@ -0,0 +1,168 @@
>> +/*
>> + * STMicroelectronics hts221 sensor driver
>> + *
>> + * Copyright 2016 STMicroelectronics Inc.
>> + *
>> + * Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/device.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/irqreturn.h>
>> +
>> +#include <linux/iio/iio.h>
>> +#include <linux/iio/trigger.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/iio/events.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> +#include <linux/iio/buffer.h>
>> +
>> +#include "hts221.h"
>> +
>> +#define HTS221_REG_STATUS_ADDR               0x27
>> +#define HTS221_RH_DRDY_MASK          BIT(1)
>> +#define HTS221_TEMP_DRDY_MASK                BIT(0)
>> +
>> +static int hts221_trig_set_state(struct iio_trigger *trig, bool state)
>> +{
>> +     struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig);
>> +     struct hts221_hw *hw = iio_priv(iio_dev);
>> +
>> +     return hts221_config_drdy(hw, state);
>> +}
>> +
>> +static const struct iio_trigger_ops hts221_trigger_ops = {
>> +     .owner = THIS_MODULE,
>> +     .set_trigger_state = hts221_trig_set_state,
>> +};
>> +
>> +static irqreturn_t hts221_trigger_handler_thread(int irq, void *private)
>> +{
>> +     struct hts221_hw *hw = (struct hts221_hw *)private;
>> +     u8 status;
>> +     int err;
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status),
>> +                        &status);
>> +     if (err < 0)
>> +             return IRQ_HANDLED;
>> +
> Not kernel style multi line comment. I'll fix it up if I take this version.

Yes, sorry. I am used to net style.

>> +     /* H_DA bit (humidity data available) is routed to DRDY line.
>> +      * Humidity sample is computed after temperature one.
>> +      * Here we can assume data channels are both available if H_DA bit
>> +      * is set in status register
>> +      */
>> +     if (!(status & HTS221_RH_DRDY_MASK))
>> +             return IRQ_NONE;
>> +
>> +     iio_trigger_poll_chained(hw->trig);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +int hts221_allocate_triggers(struct hts221_hw *hw)
> Nitpick of the day - there is now only one ;)

Leftover of v1 :)

>> +{
>> +     struct iio_dev *iio_dev = iio_priv_to_dev(hw);
>> +     unsigned long irq_type;
>> +     int err;
>> +
>> +     irq_type = irqd_get_trigger_type(irq_get_irq_data(hw->irq));
>> +
>> +     switch (irq_type) {
>> +     case IRQF_TRIGGER_HIGH:
>> +     case IRQF_TRIGGER_RISING:
>> +             break;
>> +     default:
>> +             dev_info(hw->dev,
>> +                      "mode %lx unsupported, using IRQF_TRIGGER_RISING\n",
>> +                      irq_type);
>> +             irq_type = IRQF_TRIGGER_RISING;
>> +             break;
>> +     }
>> +
>> +     err = devm_request_threaded_irq(hw->dev, hw->irq, NULL,
>> +                                     hts221_trigger_handler_thread,
>> +                                     irq_type | IRQF_ONESHOT,
>> +                                     hw->name, hw);
>> +     if (err) {
>> +             dev_err(hw->dev, "failed to request trigger irq %d\n",
>> +                     hw->irq);
>> +             return err;
>> +     }
>> +
>> +     hw->trig = devm_iio_trigger_alloc(hw->dev, "%s-trigger",
>> +                                       iio_dev->name);
>> +     if (!hw->trig)
>> +             return -ENOMEM;
>> +
>> +     iio_trigger_set_drvdata(hw->trig, iio_dev);
>> +     hw->trig->ops = &hts221_trigger_ops;
>> +     hw->trig->dev.parent = hw->dev;
>> +     iio_dev->trig = iio_trigger_get(hw->trig);
>> +
>> +     return devm_iio_trigger_register(hw->dev, hw->trig);
>> +}
>> +
>> +static int hts221_buffer_preenable(struct iio_dev *iio_dev)
>> +{
>> +     return hts221_power_on(iio_priv(iio_dev));
>> +}
>> +
>> +static int hts221_buffer_postdisable(struct iio_dev *iio_dev)
>> +{
>> +     return hts221_power_off(iio_priv(iio_dev));
>> +}
>> +
>> +static const struct iio_buffer_setup_ops hts221_buffer_ops = {
>> +     .preenable = hts221_buffer_preenable,
>> +     .postenable = iio_triggered_buffer_postenable,
>> +     .predisable = iio_triggered_buffer_predisable,
>> +     .postdisable = hts221_buffer_postdisable,
>> +};
>> +
>> +static irqreturn_t hts221_buffer_handler_thread(int irq, void *p)
>> +{
>> +     u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)];
>> +     struct iio_poll_func *pf = p;
>> +     struct iio_dev *iio_dev = pf->indio_dev;
>> +     struct hts221_hw *hw = iio_priv(iio_dev);
>> +     struct iio_chan_spec const *ch;
>> +     int err;
>> +
>> +     /* humidity data */
>> +     ch = &iio_dev->channels[HTS221_SENSOR_H];
>> +     err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
>> +                        buffer);
>> +     if (err < 0)
>> +             goto out;
>> +
>> +     /* temperature data */
>> +     ch = &iio_dev->channels[HTS221_SENSOR_T];
>> +     err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE,
>> +                        buffer + HTS221_DATA_SIZE);
>> +     if (err < 0)
>> +             goto out;
>> +
>> +     iio_push_to_buffers_with_timestamp(iio_dev, buffer,
>> +                                        iio_get_time_ns(iio_dev));
>> +
>> +out:
>> +     iio_trigger_notify_done(hw->trig);
>> +
>> +     return IRQ_HANDLED;
>> +}
>> +
>> +int hts221_allocate_buffers(struct hts221_hw *hw)
>> +{
>> +     return devm_iio_triggered_buffer_setup(hw->dev, iio_priv_to_dev(hw),
>> +                                     NULL, hts221_buffer_handler_thread,
>> +                                     &hts221_buffer_ops);
>> +}
>> +
>> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>");
>> +MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c
>> new file mode 100644
>> index 0000000..5a099dd
>> --- /dev/null
>> +++ b/drivers/iio/humidity/hts221_core.c
>> @@ -0,0 +1,687 @@
>> +/*
>> + * STMicroelectronics hts221 sensor driver
>> + *
>> + * Copyright 2016 STMicroelectronics Inc.
>> + *
>> + * Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/device.h>
>> +#include <linux/iio/sysfs.h>
>> +#include <linux/delay.h>
>> +#include <asm/unaligned.h>
>> +
>> +#include "hts221.h"
>> +
>> +#define HTS221_REG_WHOAMI_ADDR               0x0f
>> +#define HTS221_REG_WHOAMI_VAL                0xbc
>> +
>> +#define HTS221_REG_CNTRL1_ADDR               0x20
>> +#define HTS221_REG_CNTRL2_ADDR               0x21
>> +#define HTS221_REG_CNTRL3_ADDR               0x22
>> +
>> +#define HTS221_REG_AVG_ADDR          0x10
>> +#define HTS221_REG_H_OUT_L           0x28
>> +#define HTS221_REG_T_OUT_L           0x2a
>> +
>> +#define HTS221_HUMIDITY_AVG_MASK     0x07
>> +#define HTS221_TEMP_AVG_MASK         0x38
>> +
>> +#define HTS221_ODR_MASK                      0x87
>> +#define HTS221_BDU_MASK                      BIT(2)
>> +
>> +#define HTS221_DRDY_MASK             BIT(2)
>> +
>> +#define HTS221_ENABLE_SENSOR         BIT(7)
>> +
>> +#define HTS221_HUMIDITY_AVG_4                0x00 /* 0.4 %RH */
>> +#define HTS221_HUMIDITY_AVG_8                0x01 /* 0.3 %RH */
>> +#define HTS221_HUMIDITY_AVG_16               0x02 /* 0.2 %RH */
>> +#define HTS221_HUMIDITY_AVG_32               0x03 /* 0.15 %RH */
>> +#define HTS221_HUMIDITY_AVG_64               0x04 /* 0.1 %RH */
>> +#define HTS221_HUMIDITY_AVG_128              0x05 /* 0.07 %RH */
>> +#define HTS221_HUMIDITY_AVG_256              0x06 /* 0.05 %RH */
>> +#define HTS221_HUMIDITY_AVG_512              0x07 /* 0.03 %RH */
>> +
>> +#define HTS221_TEMP_AVG_2            0x00 /* 0.08 degC */
>> +#define HTS221_TEMP_AVG_4            0x08 /* 0.05 degC */
>> +#define HTS221_TEMP_AVG_8            0x10 /* 0.04 degC */
>> +#define HTS221_TEMP_AVG_16           0x18 /* 0.03 degC */
>> +#define HTS221_TEMP_AVG_32           0x20 /* 0.02 degC */
>> +#define HTS221_TEMP_AVG_64           0x28 /* 0.015 degC */
>> +#define HTS221_TEMP_AVG_128          0x30 /* 0.01 degC */
>> +#define HTS221_TEMP_AVG_256          0x38 /* 0.007 degC */
>> +
>> +/* calibration registers */
>> +#define HTS221_REG_0RH_CAL_X_H               0x36
>> +#define HTS221_REG_1RH_CAL_X_H               0x3a
>> +#define HTS221_REG_0RH_CAL_Y_H               0x30
>> +#define HTS221_REG_1RH_CAL_Y_H               0x31
>> +#define HTS221_REG_0T_CAL_X_L                0x3c
>> +#define HTS221_REG_1T_CAL_X_L                0x3e
>> +#define HTS221_REG_0T_CAL_Y_H                0x32
>> +#define HTS221_REG_1T_CAL_Y_H                0x33
>> +#define HTS221_REG_T1_T0_CAL_Y_H     0x35
>> +
>> +struct hts221_odr {
>> +     u8 hz;
>> +     u8 val;
>> +};
>> +
>> +struct hts221_avg {
>> +     u8 addr;
>> +     u8 mask;
>> +     struct hts221_avg_avl avg_avl[HTS221_AVG_DEPTH];
>> +};
>> +
>> +static const struct hts221_odr hts221_odr_table[] = {
>> +     {  1, 0x01 },   /* 1Hz */
>> +     {  7, 0x02 },   /* 7Hz */
>> +     { 13, 0x03 },   /* 12.5Hz */
>> +};
>> +
>> +static const struct hts221_avg hts221_avg_list[] = {
>> +     {
>> +             .addr = HTS221_REG_AVG_ADDR,
>> +             .mask = HTS221_HUMIDITY_AVG_MASK,
>> +             .avg_avl = {
>> +                     { 4, HTS221_HUMIDITY_AVG_4 },
>> +                     { 8, HTS221_HUMIDITY_AVG_8 },
>> +                     { 16, HTS221_HUMIDITY_AVG_16 },
>> +                     { 32, HTS221_HUMIDITY_AVG_32 },
>> +                     { 64, HTS221_HUMIDITY_AVG_64 },
>> +                     { 128, HTS221_HUMIDITY_AVG_128 },
>> +                     { 256, HTS221_HUMIDITY_AVG_256 },
>> +                     { 512, HTS221_HUMIDITY_AVG_512 },
>> +             },
>> +     },
>> +     {
>> +             .addr = HTS221_REG_AVG_ADDR,
>> +             .mask = HTS221_TEMP_AVG_MASK,
>> +             .avg_avl = {
>> +                     { 2, HTS221_TEMP_AVG_2 },
>> +                     { 4, HTS221_TEMP_AVG_4 },
>> +                     { 8, HTS221_TEMP_AVG_8 },
>> +                     { 16, HTS221_TEMP_AVG_16 },
>> +                     { 32, HTS221_TEMP_AVG_32 },
>> +                     { 64, HTS221_TEMP_AVG_64 },
>> +                     { 128, HTS221_TEMP_AVG_128 },
>> +                     { 256, HTS221_TEMP_AVG_256 },
>> +             },
>> +     },
>> +};
>> +
>> +static const struct iio_chan_spec hts221_channels[] = {
>> +     {
>> +             .type = IIO_HUMIDITYRELATIVE,
>> +             .address = HTS221_REG_H_OUT_L,
>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>> +                                   BIT(IIO_CHAN_INFO_SCALE) |
>> +                                   BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
>> +             .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
>> +             .scan_index = 0,
>> +             .scan_type = {
>> +                     .sign = 's',
>> +                     .realbits = 16,
>> +                     .storagebits = 16,
>> +                     .endianness = IIO_LE,
>> +             },
>> +     },
>> +     {
>> +             .type = IIO_TEMP,
>> +             .address = HTS221_REG_T_OUT_L,
>> +             .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |
>> +                                   BIT(IIO_CHAN_INFO_OFFSET) |
>> +                                   BIT(IIO_CHAN_INFO_SCALE) |
>> +                                   BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO),
>> +             .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
>> +             .scan_index = 1,
>> +             .scan_type = {
>> +                     .sign = 's',
>> +                     .realbits = 16,
>> +                     .storagebits = 16,
>> +                     .endianness = IIO_LE,
>> +             },
>> +     },
>> +     IIO_CHAN_SOFT_TIMESTAMP(2),
>> +};
>> +
>> +static int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask,
>> +                               u8 val)
>> +{
>> +     u8 data;
>> +     int err;
>> +
>> +     mutex_lock(&hw->lock);
>> +
>> +     err = hw->tf->read(hw->dev, addr, sizeof(data), &data);
>> +     if (err < 0) {
>> +             dev_err(hw->dev, "failed to read %02x register\n", addr);
>> +             goto unlock;
>> +     }
>> +
>> +     data = (data & ~mask) | (val & mask);
>> +
>> +     err = hw->tf->write(hw->dev, addr, sizeof(data), &data);
>> +     if (err < 0)
>> +             dev_err(hw->dev, "failed to write %02x register\n", addr);
>> +
>> +unlock:
>> +     mutex_unlock(&hw->lock);
>> +
>> +     return err;
>> +}
>> +
>> +static int hts221_check_whoami(struct hts221_hw *hw)
>> +{
>> +     u8 data;
>> +     int err;
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data),
>> +                        &data);
>> +     if (err < 0) {
>> +             dev_err(hw->dev, "failed to read whoami register\n");
>> +             return err;
>> +     }
>> +
>> +     if (data != HTS221_REG_WHOAMI_VAL) {
>> +             dev_err(hw->dev, "wrong whoami {%02x vs %02x}\n",
>> +                     data, HTS221_REG_WHOAMI_VAL);
>> +             return -ENODEV;
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +int hts221_config_drdy(struct hts221_hw *hw, bool enable)
>> +{
>> +     u8 val = enable ? BIT(2) : 0;
>> +     int err;
>> +
>> +     err = hts221_write_with_mask(hw, HTS221_REG_CNTRL3_ADDR,
>> +                                  HTS221_DRDY_MASK, val);
>> +
>> +     return err < 0 ? err : 0;
>> +}
>> +
>> +static int hts221_update_odr(struct hts221_hw *hw, u8 odr)
>> +{
>> +     int i, err;
>> +     u8 val;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
>> +             if (hts221_odr_table[i].hz == odr)
>> +                     break;
>> +
>> +     if (i == ARRAY_SIZE(hts221_odr_table))
>> +             return -EINVAL;
>> +
>> +     val = HTS221_ENABLE_SENSOR | HTS221_BDU_MASK | hts221_odr_table[i].val;
>> +     err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR,
>> +                                  HTS221_ODR_MASK, val);
>> +     if (err < 0)
>> +             return err;
>> +
>> +     hw->odr = odr;
>> +
>> +     return 0;
>> +}
>> +
>> +static int hts221_update_avg(struct hts221_hw *hw,
>> +                          enum hts221_sensor_type type,
>> +                          u16 val)
>> +{
>> +     int i, err;
>> +     const struct hts221_avg *avg = &hts221_avg_list[type];
>> +
>> +     for (i = 0; i < HTS221_AVG_DEPTH; i++)
>> +             if (avg->avg_avl[i].avg == val)
>> +                     break;
>> +
>> +     if (i == HTS221_AVG_DEPTH)
>> +             return -EINVAL;
>> +
>> +     err = hts221_write_with_mask(hw, avg->addr, avg->mask,
>> +                                  avg->avg_avl[i].val);
>> +     if (err < 0)
>> +             return err;
>> +
>> +     hw->sensors[type].cur_avg_idx = i;
>> +
>> +     return 0;
>> +}
>> +
>> +static ssize_t hts221_sysfs_sampling_freq(struct device *dev,
>> +                                       struct device_attribute *attr,
>> +                                       char *buf)
>> +{
>> +     int i;
>> +     ssize_t len = 0;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(hts221_odr_table); i++)
>> +             len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
>> +                              hts221_odr_table[i].hz);
>> +     buf[len - 1] = '\n';
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t
>> +hts221_sysfs_rh_oversampling_avail(struct device *dev,
>> +                                struct device_attribute *attr,
>> +                                char *buf)
>> +{
>> +     const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_H];
>> +     ssize_t len = 0;
>> +     int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
>> +             len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
>> +                              avg->avg_avl[i].avg);
>> +     buf[len - 1] = '\n';
>> +
>> +     return len;
>> +}
>> +
>> +static ssize_t
>> +hts221_sysfs_temp_oversampling_avail(struct device *dev,
>> +                                  struct device_attribute *attr,
>> +                                  char *buf)
>> +{
>> +     const struct hts221_avg *avg = &hts221_avg_list[HTS221_SENSOR_T];
>> +     ssize_t len = 0;
>> +     int i;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(avg->avg_avl); i++)
>> +             len += scnprintf(buf + len, PAGE_SIZE - len, "%d ",
>> +                              avg->avg_avl[i].avg);
>> +     buf[len - 1] = '\n';
>> +
>> +     return len;
>> +}
>> +
>> +int hts221_power_on(struct hts221_hw *hw)
>> +{
>> +     return hts221_update_odr(hw, hw->odr);
>> +}
>> +
>> +int hts221_power_off(struct hts221_hw *hw)
>> +{
>> +     u8 data[] = {0x00, 0x00};
>> +
>> +     return hw->tf->write(hw->dev, HTS221_REG_CNTRL1_ADDR, sizeof(data),
>> +                          data);
>> +}
>> +
>> +static int hts221_parse_temp_caldata(struct hts221_hw *hw)
>> +{
>> +     int err, *slope, *b_gen;
>> +     s16 cal_x0, cal_x1, cal_y0, cal_y1;
>> +     u8 cal0, cal1;
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H,
>> +                        sizeof(cal0), &cal0);
>> +     if (err < 0)
>> +             return err;
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H,
>> +                        sizeof(cal1), &cal1);
>> +     if (err < 0)
>> +             return err;
>> +     cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0;
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H,
>> +                        sizeof(cal0), &cal0);
>> +     if (err < 0)
>> +             return err;
>> +     cal_y1 = (((cal1 & 0xc) >> 2) << 8) | cal0;
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(cal_x0),
>> +                        (u8 *)&cal_x0);
>> +     if (err < 0)
>> +             return err;
>> +     cal_x0 = le16_to_cpu(cal_x0);
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1),
>> +                        (u8 *)&cal_x1);
>> +     if (err < 0)
>> +             return err;
>> +     cal_x1 = le16_to_cpu(cal_x1);
>> +
>> +     slope = &hw->sensors[HTS221_SENSOR_T].slope;
>> +     b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen;
>> +
>> +     *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
>> +     *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
>> +              (cal_x1 - cal_x0);
>> +     *b_gen *= 8;
>> +
>> +     return 0;
>> +}
>> +
>> +static int hts221_parse_rh_caldata(struct hts221_hw *hw)
>> +{
>> +     int err, *slope, *b_gen;
>> +     s16 cal_x0, cal_x1, cal_y0, cal_y1;
>> +     u8 data;
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data),
>> +                        &data);
>> +     if (err < 0)
>> +             return err;
>> +     cal_y0 = data;
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data),
>> +                        &data);
>> +     if (err < 0)
>> +             return err;
>> +     cal_y1 = data;
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0),
>> +                        (u8 *)&cal_x0);
>> +     if (err < 0)
>> +             return err;
>> +     cal_x0 = le16_to_cpu(cal_x0);
>> +
>> +     err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1),
>> +                        (u8 *)&cal_x1);
>> +     if (err < 0)
>> +             return err;
>> +     cal_x1 = le16_to_cpu(cal_x1);
>> +
>> +     slope = &hw->sensors[HTS221_SENSOR_H].slope;
>> +     b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen;
>> +
>> +     *slope = ((cal_y1 - cal_y0) * 8000) / (cal_x1 - cal_x0);
>> +     *b_gen = (((s32)cal_x1 * cal_y0 - (s32)cal_x0 * cal_y1) * 1000) /
>> +              (cal_x1 - cal_x0);
>> +     *b_gen *= 8;
>> +
>> +     return 0;
>> +}
>> +
>> +static int hts221_get_sensor_scale(struct hts221_hw *hw,
>> +                                enum iio_chan_type ch_type,
>> +                                int *val, int *val2)
>> +{
>> +     s64 tmp;
>> +     s32 rem, div, data;
>> +
>> +     switch (ch_type) {
>> +     case IIO_HUMIDITYRELATIVE:
>> +             data = hw->sensors[HTS221_SENSOR_H].slope;
>> +             div = (1 << 4) * 1000;
>> +             break;
>> +     case IIO_TEMP:
>> +             data = hw->sensors[HTS221_SENSOR_T].slope;
>> +             div = (1 << 6) * 1000;
>> +             break;
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     tmp = div_s64(data * 1000000000LL, div);
>> +     tmp = div_s64_rem(tmp, 1000000000LL, &rem);
>> +
>> +     *val = tmp;
>> +     *val2 = rem;
>> +
>> +     return IIO_VAL_INT_PLUS_NANO;
>> +}
>> +
>> +static int hts221_get_sensor_offset(struct hts221_hw *hw,
>> +                                 enum iio_chan_type ch_type,
>> +                                 int *val, int *val2)
>> +{
>> +     s64 tmp;
>> +     s32 rem, div, data;
>> +
>> +     switch (ch_type) {
>> +     case IIO_HUMIDITYRELATIVE:
>> +             data = hw->sensors[HTS221_SENSOR_H].b_gen;
>> +             div = hw->sensors[HTS221_SENSOR_H].slope;
>> +             break;
>> +     case IIO_TEMP:
>> +             data = hw->sensors[HTS221_SENSOR_T].b_gen;
>> +             div = hw->sensors[HTS221_SENSOR_T].slope;
>> +             break;
>> +     default:
>> +             return -EINVAL;
>> +     }
>> +
>> +     tmp = div_s64(data * 1000000000LL, div);
>> +     tmp = div_s64_rem(tmp, 1000000000LL, &rem);
>> +
>> +     *val = tmp;
>> +     *val2 = rem;
>> +
>> +     return IIO_VAL_INT_PLUS_NANO;
>> +}
>> +
>> +static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val)
>> +{
>> +     u8 data[HTS221_DATA_SIZE];
>> +     int err;
>> +
>> +     err = hts221_power_on(hw);
>> +     if (err < 0)
>> +             return err;
>> +
>> +     msleep(50);
>> +
>> +     err = hw->tf->read(hw->dev, addr, sizeof(data), data);
>> +     if (err < 0)
>> +             return err;
>> +
>> +     hts221_power_off(hw);
>> +
>> +     *val = (s16)get_unaligned_le16(data);
>> +
>> +     return IIO_VAL_INT;
>> +}
>> +
>> +static int hts221_read_raw(struct iio_dev *iio_dev,
>> +                        struct iio_chan_spec const *ch,
>> +                        int *val, int *val2, long mask)
>> +{
>> +     struct hts221_hw *hw = iio_priv(iio_dev);
>> +     int ret;
>> +
>> +     ret = iio_device_claim_direct_mode(iio_dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +     switch (mask) {
>> +     case IIO_CHAN_INFO_RAW:
>> +             ret = hts221_read_oneshot(hw, ch->address, val);
>> +             break;
>> +     case IIO_CHAN_INFO_SCALE:
>> +             ret = hts221_get_sensor_scale(hw, ch->type, val, val2);
>> +             break;
>> +     case IIO_CHAN_INFO_OFFSET:
>> +             ret = hts221_get_sensor_offset(hw, ch->type, val, val2);
>> +             break;
>> +     case IIO_CHAN_INFO_SAMP_FREQ:
>> +             *val = hw->odr;
>> +             ret = IIO_VAL_INT;
>> +             break;
>> +     case IIO_CHAN_INFO_OVERSAMPLING_RATIO: {
>> +             u8 idx;
>> +             const struct hts221_avg *avg;
>> +
>> +             switch (ch->type) {
>> +             case IIO_HUMIDITYRELATIVE:
>> +                     avg = &hts221_avg_list[HTS221_SENSOR_H];
>> +                     idx = hw->sensors[HTS221_SENSOR_H].cur_avg_idx;
>> +                     *val = avg->avg_avl[idx].avg;
>> +                     ret = IIO_VAL_INT;
>> +                     break;
>> +             case IIO_TEMP:
>> +                     avg = &hts221_avg_list[HTS221_SENSOR_T];
>> +                     idx = hw->sensors[HTS221_SENSOR_T].cur_avg_idx;
>> +                     *val = avg->avg_avl[idx].avg;
>> +                     ret = IIO_VAL_INT;
>> +                     break;
>> +             default:
>> +                     ret = -EINVAL;
>> +                     break;
>> +             }
>> +             break;
>> +     }
>> +     default:
>> +             ret = -EINVAL;
>> +             break;
>> +     }
>> +
>> +     iio_device_release_direct_mode(iio_dev);
>> +
>> +     return ret;
>> +}
>> +
>> +static int hts221_write_raw(struct iio_dev *iio_dev,
>> +                         struct iio_chan_spec const *chan,
>> +                         int val, int val2, long mask)
>> +{
>> +     struct hts221_hw *hw = iio_priv(iio_dev);
>> +     int ret;
>> +
>> +     ret = iio_device_claim_direct_mode(iio_dev);
>> +     if (ret)
>> +             return ret;
>> +
>> +     switch (mask) {
>> +     case IIO_CHAN_INFO_SAMP_FREQ:
>> +             ret = hts221_update_odr(hw, val);
>> +             break;
>> +     case IIO_CHAN_INFO_OVERSAMPLING_RATIO:
>> +             switch (chan->type) {
>> +             case IIO_HUMIDITYRELATIVE:
>> +                     ret = hts221_update_avg(hw, HTS221_SENSOR_H, val);
>> +                     break;
>> +             case IIO_TEMP:
>> +                     ret = hts221_update_avg(hw, HTS221_SENSOR_T, val);
>> +                     break;
>> +             default:
>> +                     ret = -EINVAL;
>> +                     break;
>> +             }
>> +             break;
>> +     default:
>> +             ret = -EINVAL;
>> +             break;
>> +     }
>> +
>> +     iio_device_release_direct_mode(iio_dev);
>> +
>> +     return ret;
>> +}
>> +
>> +static int hts221_validate_trigger(struct iio_dev *iio_dev,
>> +                                struct iio_trigger *trig)
>> +{
>> +     struct hts221_hw *hw = iio_priv(iio_dev);
>> +
>> +     return hw->trig == trig ? 0 : -EINVAL;
>> +}
>> +
>> +static IIO_DEVICE_ATTR(in_humidity_oversampling_ratio_available, S_IRUGO,
>> +                    hts221_sysfs_rh_oversampling_avail, NULL, 0);
>> +static IIO_DEVICE_ATTR(in_temp_oversampling_ratio_available, S_IRUGO,
>> +                    hts221_sysfs_temp_oversampling_avail, NULL, 0);
>> +static IIO_DEV_ATTR_SAMP_FREQ_AVAIL(hts221_sysfs_sampling_freq);
>> +
>> +static struct attribute *hts221_attributes[] = {
>> +     &iio_dev_attr_sampling_frequency_available.dev_attr.attr,
>> +     &iio_dev_attr_in_humidity_oversampling_ratio_available.dev_attr.attr,
>> +     &iio_dev_attr_in_temp_oversampling_ratio_available.dev_attr.attr,
>> +     NULL,
>> +};
>> +
>> +static const struct attribute_group hts221_attribute_group = {
>> +     .attrs = hts221_attributes,
>> +};
>> +
>> +static const struct iio_info hts221_info = {
>> +     .driver_module = THIS_MODULE,
>> +     .attrs = &hts221_attribute_group,
>> +     .read_raw = hts221_read_raw,
>> +     .write_raw = hts221_write_raw,
>> +     .validate_trigger = hts221_validate_trigger,
>> +};
>> +
>> +static const unsigned long hts221_scan_masks[] = {0x3, 0x0};
>> +
>> +int hts221_probe(struct iio_dev *iio_dev)
>> +{
>> +     struct hts221_hw *hw = iio_priv(iio_dev);
>> +     int err;
>> +     u8 data;
>> +
>> +     mutex_init(&hw->lock);
>> +
>> +     err = hts221_check_whoami(hw);
>> +     if (err < 0)
>> +             return err;
>> +
>> +     hw->odr = hts221_odr_table[0].hz;
>> +
>> +     iio_dev->modes = INDIO_DIRECT_MODE;
>> +     iio_dev->dev.parent = hw->dev;
>> +     iio_dev->available_scan_masks = hts221_scan_masks;
>> +     iio_dev->channels = hts221_channels;
>> +     iio_dev->num_channels = ARRAY_SIZE(hts221_channels);
>> +     iio_dev->name = HTS221_DEV_NAME;
>> +     iio_dev->info = &hts221_info;
>> +
>> +     /* configure humidity sensor */
>> +     err = hts221_parse_rh_caldata(hw);
>> +     if (err < 0) {
>> +             dev_err(hw->dev, "failed to get rh calibration data\n");
>> +             return err;
>> +     }
>> +
>> +     data = hts221_avg_list[HTS221_SENSOR_H].avg_avl[3].avg;
>> +     err = hts221_update_avg(hw, HTS221_SENSOR_H, data);
>> +     if (err < 0) {
>> +             dev_err(hw->dev, "failed to set rh oversampling ratio\n");
>> +             return err;
>> +     }
>> +
>> +     /* configure temperature sensor */
>> +     err = hts221_parse_temp_caldata(hw);
>> +     if (err < 0) {
>> +             dev_err(hw->dev,
>> +                     "failed to get temperature calibration data\n");
>> +             return err;
>> +     }
>> +
>> +     data = hts221_avg_list[HTS221_SENSOR_T].avg_avl[3].avg;
>> +     err = hts221_update_avg(hw, HTS221_SENSOR_T, data);
>> +     if (err < 0) {
>> +             dev_err(hw->dev,
>> +                     "failed to set temperature oversampling ratio\n");
>> +             return err;
>> +     }
>> +
>> +     if (hw->irq > 0) {
>> +             err = hts221_allocate_buffers(hw);
>> +             if (err < 0)
>> +                     return err;
>> +
>> +             err = hts221_allocate_triggers(hw);
>> +             if (err)
>> +                     return err;
>> +     }
>> +
>> +     return devm_iio_device_register(hw->dev, iio_dev);
>> +}
>> +EXPORT_SYMBOL(hts221_probe);
>> +
>> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>");
>> +MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c
>> new file mode 100644
>> index 0000000..367ecd5
>> --- /dev/null
>> +++ b/drivers/iio/humidity/hts221_i2c.c
>> @@ -0,0 +1,110 @@
>> +/*
>> + * STMicroelectronics hts221 i2c driver
>> + *
>> + * Copyright 2016 STMicroelectronics Inc.
>> + *
>> + * Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/i2c.h>
>> +#include <linux/slab.h>
>> +#include "hts221.h"
>> +
>> +#define I2C_AUTO_INCREMENT   0x80
>> +
>> +static int hts221_i2c_read(struct device *dev, u8 addr, int len, u8 *data)
>> +{
>> +     struct i2c_msg msg[2];
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +
>> +     if (len > 1)
>> +             addr |= I2C_AUTO_INCREMENT;
>> +
>> +     msg[0].addr = client->addr;
>> +     msg[0].flags = client->flags;
>> +     msg[0].len = 1;
>> +     msg[0].buf = &addr;
>> +
>> +     msg[1].addr = client->addr;
>> +     msg[1].flags = client->flags | I2C_M_RD;
>> +     msg[1].len = len;
>> +     msg[1].buf = data;
>> +
>> +     return i2c_transfer(client->adapter, msg, 2);
>> +}
>> +
>> +static int hts221_i2c_write(struct device *dev, u8 addr, int len, u8 *data)
>> +{
>> +     u8 send[len + 1];
>> +     struct i2c_msg msg;
>> +     struct i2c_client *client = to_i2c_client(dev);
>> +
>> +     if (len > 1)
>> +             addr |= I2C_AUTO_INCREMENT;
>> +
>> +     send[0] = addr;
>> +     memcpy(&send[1], data, len * sizeof(u8));
>> +
>> +     msg.addr = client->addr;
>> +     msg.flags = client->flags;
>> +     msg.len = len + 1;
>> +     msg.buf = send;
>> +
>> +     return i2c_transfer(client->adapter, &msg, 1);
>> +}
>> +
>> +static const struct hts221_transfer_function hts221_transfer_fn = {
>> +     .read = hts221_i2c_read,
>> +     .write = hts221_i2c_write,
>> +};
>> +
>> +static int hts221_i2c_probe(struct i2c_client *client,
>> +                         const struct i2c_device_id *id)
>> +{
>> +     struct hts221_hw *hw;
>> +     struct iio_dev *iio_dev;
>> +
>> +     iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*hw));
>> +     if (!iio_dev)
>> +             return -ENOMEM;
>> +
>> +     i2c_set_clientdata(client, iio_dev);
>> +
>> +     hw = iio_priv(iio_dev);
>> +     hw->name = client->name;
>> +     hw->dev = &client->dev;
>> +     hw->irq = client->irq;
>> +     hw->tf = &hts221_transfer_fn;
>> +
>> +     return hts221_probe(iio_dev);
>> +}
>> +
>> +static const struct of_device_id hts221_i2c_of_match[] = {
>> +     { .compatible = "st,hts221", },
>> +     {},
>> +};
>> +MODULE_DEVICE_TABLE(of, hts221_i2c_of_match);
>> +
>> +static const struct i2c_device_id hts221_i2c_id_table[] = {
>> +     { HTS221_DEV_NAME },
>> +     {},
>> +};
>> +MODULE_DEVICE_TABLE(i2c, hts221_i2c_id_table);
>> +
>> +static struct i2c_driver hts221_driver = {
>> +     .driver = {
>> +             .name = "hts221_i2c",
>> +             .of_match_table = of_match_ptr(hts221_i2c_of_match),
>> +     },
>> +     .probe = hts221_i2c_probe,
>> +     .id_table = hts221_i2c_id_table,
>> +};
>> +module_i2c_driver(hts221_driver);
>> +
>> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>");
>> +MODULE_DESCRIPTION("STMicroelectronics hts221 i2c driver");
>> +MODULE_LICENSE("GPL v2");
>> diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c
>> new file mode 100644
>> index 0000000..70df5e7
>> --- /dev/null
>> +++ b/drivers/iio/humidity/hts221_spi.c
>> @@ -0,0 +1,125 @@
>> +/*
>> + * STMicroelectronics hts221 spi driver
>> + *
>> + * Copyright 2016 STMicroelectronics Inc.
>> + *
>> + * Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>
>> + *
>> + * Licensed under the GPL-2.
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/spi/spi.h>
>> +#include <linux/slab.h>
>> +#include "hts221.h"
>> +
>> +#define SENSORS_SPI_READ     0x80
>> +#define SPI_AUTO_INCREMENT   0x40
>> +
>> +static int hts221_spi_read(struct device *dev, u8 addr, int len, u8 *data)
>> +{
>> +     int err;
>> +     struct spi_device *spi = to_spi_device(dev);
>> +     struct iio_dev *iio_dev = spi_get_drvdata(spi);
>> +     struct hts221_hw *hw = iio_priv(iio_dev);
>> +
>> +     struct spi_transfer xfers[] = {
>> +             {
>> +                     .tx_buf = hw->tb.tx_buf,
>> +                     .bits_per_word = 8,
>> +                     .len = 1,
>> +             },
>> +             {
>> +                     .rx_buf = hw->tb.rx_buf,
>> +                     .bits_per_word = 8,
>> +                     .len = len,
>> +             }
>> +     };
>> +
>> +     if (len > 1)
>> +             addr |= SPI_AUTO_INCREMENT;
>> +     hw->tb.tx_buf[0] = addr | SENSORS_SPI_READ;
>> +
>> +     err = spi_sync_transfer(spi, xfers,  ARRAY_SIZE(xfers));
>> +     if (err < 0)
>> +             return err;
>> +
>> +     memcpy(data, hw->tb.rx_buf, len * sizeof(u8));
>> +
>> +     return len;
>> +}
>> +
>> +static int hts221_spi_write(struct device *dev, u8 addr, int len, u8 *data)
>> +{
>> +     struct spi_device *spi = to_spi_device(dev);
>> +     struct iio_dev *iio_dev = spi_get_drvdata(spi);
>> +     struct hts221_hw *hw = iio_priv(iio_dev);
>> +
>> +     struct spi_transfer xfers = {
>> +             .tx_buf = hw->tb.tx_buf,
>> +             .bits_per_word = 8,
>> +             .len = len + 1,
>> +     };
>> +
>> +     if (len >= HTS221_TX_MAX_LENGTH)
>> +             return -ENOMEM;
>> +
>> +     if (len > 1)
>> +             addr |= SPI_AUTO_INCREMENT;
>> +     hw->tb.tx_buf[0] = addr;
>> +     memcpy(&hw->tb.tx_buf[1], data, len);
>> +
>> +     return spi_sync_transfer(spi, &xfers, 1);
>> +}
>> +
>> +static const struct hts221_transfer_function hts221_transfer_fn = {
>> +     .read = hts221_spi_read,
>> +     .write = hts221_spi_write,
>> +};
>> +
>> +static int hts221_spi_probe(struct spi_device *spi)
>> +{
>> +     struct hts221_hw *hw;
>> +     struct iio_dev *iio_dev;
>> +
>> +     iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*hw));
>> +     if (!iio_dev)
>> +             return -ENOMEM;
>> +
>> +     spi_set_drvdata(spi, iio_dev);
>> +
>> +     hw = iio_priv(iio_dev);
>> +     hw->name = spi->modalias;
>> +     hw->dev = &spi->dev;
>> +     hw->irq = spi->irq;
>> +     hw->tf = &hts221_transfer_fn;
>> +
>> +     return hts221_probe(iio_dev);
>> +}
>> +
>> +static const struct of_device_id hts221_spi_of_match[] = {
>> +     { .compatible = "st,hts221", },
>> +     {},
>> +};
>> +MODULE_DEVICE_TABLE(of, hts221_spi_of_match);
>> +
>> +static const struct spi_device_id hts221_spi_id_table[] = {
>> +     { HTS221_DEV_NAME },
>> +     {},
>> +};
>> +MODULE_DEVICE_TABLE(spi, hts221_spi_id_table);
>> +
>> +static struct spi_driver hts221_driver = {
>> +     .driver = {
>> +             .name = "hts221_spi",
>> +             .of_match_table = of_match_ptr(hts221_spi_of_match),
>> +     },
>> +     .probe = hts221_spi_probe,
>> +     .id_table = hts221_spi_id_table,
>> +};
>> +module_spi_driver(hts221_driver);
>> +
>> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>");
>> +MODULE_DESCRIPTION("STMicroelectronics hts221 spi driver");
>> +MODULE_LICENSE("GPL v2");
>>
>

Regards,
Lorenzo

-- 
UNIX is Sexy: who | grep -i blonde | talk; cd ~; wine; talk; touch;
unzip; touch; strip; gasp; finger; gasp; mount; fsck; more; yes; gasp;
umount; make clean; sleep
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux