> >> Add support to STM HTS221 humidity + temperature sensor > > > > comments below > > this sensor won't fit in the ST IIO framework? > Thanks for the review. > HTS221 does not fit well in st framework since it is a > humidity/temperature combo device and for calibration/oversampling > features. > I have merged your suggestions, just few comments inline. and my response below as well > >> http://www.st.com/resource/en/datasheet/hts221.pdf > >> > >> - continuos mode support continuous > >> - i2c support > >> - spi support > >> - trigger mode support > >> > >> Signed-off-by: Lorenzo Bianconi <lorenzo.bianconi@xxxxxx> > >> --- > >> drivers/iio/humidity/Kconfig | 2 + > >> drivers/iio/humidity/Makefile | 1 + > >> drivers/iio/humidity/hts221/Kconfig | 23 + > >> drivers/iio/humidity/hts221/Makefile | 6 + > >> drivers/iio/humidity/hts221/hts221.h | 100 ++++ > >> drivers/iio/humidity/hts221/hts221_buffer.c | 191 ++++++++ > >> drivers/iio/humidity/hts221/hts221_core.c | 687 ++++++++++++++++++++++++++++ > >> drivers/iio/humidity/hts221/hts221_i2c.c | 119 +++++ > >> drivers/iio/humidity/hts221/hts221_spi.c | 134 ++++++ > >> 9 files changed, 1263 insertions(+) > >> create mode 100644 drivers/iio/humidity/hts221/Kconfig > >> create mode 100644 drivers/iio/humidity/hts221/Makefile > >> create mode 100644 drivers/iio/humidity/hts221/hts221.h > >> create mode 100644 drivers/iio/humidity/hts221/hts221_buffer.c > >> create mode 100644 drivers/iio/humidity/hts221/hts221_core.c > >> create mode 100644 drivers/iio/humidity/hts221/hts221_i2c.c > >> create mode 100644 drivers/iio/humidity/hts221/hts221_spi.c > >> > >> diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig > >> index b17e2e2..4ce75da 100644 > >> --- a/drivers/iio/humidity/Kconfig > >> +++ b/drivers/iio/humidity/Kconfig > >> @@ -69,4 +69,6 @@ config SI7020 > >> To compile this driver as a module, choose M here: the module > >> will be called si7020. > >> > >> +source "drivers/iio/humidity/hts221/Kconfig" > >> + > >> endmenu > >> diff --git a/drivers/iio/humidity/Makefile b/drivers/iio/humidity/Makefile > >> index 4a73442..bdd5cd0 100644 > >> --- a/drivers/iio/humidity/Makefile > >> +++ b/drivers/iio/humidity/Makefile > >> @@ -8,3 +8,4 @@ obj-$(CONFIG_HDC100X) += hdc100x.o > >> obj-$(CONFIG_HTU21) += htu21.o > >> obj-$(CONFIG_SI7005) += si7005.o > >> obj-$(CONFIG_SI7020) += si7020.o > >> +obj-$(CONFIG_IIO_HTS221) += hts221/ > > > > should be alphabetic; do we need the IIO_ prefix > > > >> diff --git a/drivers/iio/humidity/hts221/Kconfig b/drivers/iio/humidity/hts221/Kconfig > >> new file mode 100644 > >> index 0000000..95b40e0 > >> --- /dev/null > >> +++ b/drivers/iio/humidity/hts221/Kconfig > >> @@ -0,0 +1,23 @@ > >> + > >> +config IIO_HTS221 > >> + tristate "STMicroelectronics HTS221 sensor Driver" > >> + depends on (I2C || SPI) && SYSFS > > > > SYSFS is a prerequisite of IIO > > > >> + select IIO_BUFFER > >> + select IIO_TRIGGERED_BUFFER > >> + select IIO_HTS221_I2C if (I2C) > >> + select IIO_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 IIO_HTS221_I2C > >> + tristate > >> + depends on IIO_HTS221 > >> + > >> +config IIO_HTS221_SPI > >> + tristate > >> + depends on IIO_HTS221 > >> + > >> diff --git a/drivers/iio/humidity/hts221/Makefile b/drivers/iio/humidity/hts221/Makefile > >> new file mode 100644 > >> index 0000000..6fdfc6a > >> --- /dev/null > >> +++ b/drivers/iio/humidity/hts221/Makefile > >> @@ -0,0 +1,6 @@ > >> +hts221-y := hts221_core.o > >> +hts221-$(CONFIG_IIO_BUFFER) += hts221_buffer.o > >> + > >> +obj-$(CONFIG_IIO_HTS221) += hts221.o > >> +obj-$(CONFIG_IIO_HTS221_I2C) += hts221_i2c.o > >> +obj-$(CONFIG_IIO_HTS221_SPI) += hts221_spi.o > >> diff --git a/drivers/iio/humidity/hts221/hts221.h b/drivers/iio/humidity/hts221/hts221.h > >> new file mode 100644 > >> index 0000000..ee8d2fb > >> --- /dev/null > >> +++ b/drivers/iio/humidity/hts221/hts221.h > >> @@ -0,0 +1,100 @@ > >> +/* > >> + * 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 500 > >> +#define HTS221_TX_MAX_LENGTH 500 > > > > really 500 bytes transfer size? > > > >> + > >> +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 { > >> + struct hts221_dev *dev; > >> + > >> + enum hts221_sensor_type type; > >> + u8 cur_avg_idx; > >> + int slope, b_gen; > >> +}; > >> + > >> +struct hts221_dev { > >> + const char *name; > >> + struct device *dev; > >> + > >> + struct mutex lock; > >> + > >> + u8 buffer[4]; > > > > le16 buffer[2] > > since we can avoid memcpy in hts221_buffer_handler_thread() and insert > timestamp sample in dev->buffer, maybe it is clearer to do something > like: > > #define HTS221_DATA_SIZE 2 > > u8 buffer[ALIGN(2 * HTS221_DATA_SIZE, sizeof(s64)) + sizeof(s64)]; > > and use HTS221_DATA_SIZE as i2c/spi read len for output registers > > > > >> + struct iio_trigger *trig; > >> + int irq; > >> + > >> + struct hts221_sensor sensors[HTS221_SENSOR_MAX]; > >> + > >> + s64 hw_timestamp; > >> + u8 odr; > >> + > >> + const struct hts221_transfer_function *tf; > >> + struct hts221_transfer_buffer tb; > >> +}; > >> + > >> +int hts221_config_drdy(struct hts221_dev *dev, bool enable); > >> +int hts221_probe(struct hts221_dev *dev); > >> +int hts221_remove(struct hts221_dev *dev); > >> +int hts221_dev_power_on(struct hts221_dev *dev); > >> +int hts221_dev_power_off(struct hts221_dev *dev); > >> +#ifdef CONFIG_IIO_BUFFER > >> +int hts221_allocate_buffers(struct hts221_dev *dev); > >> +void hts221_deallocate_buffers(struct hts221_dev *dev); > >> +int hts221_allocate_triggers(struct hts221_dev *dev); > >> +void hts221_deallocate_triggers(struct hts221_dev *dev); > >> +#else > >> +static inline int hts221_allocate_buffers(struct hts221_dev *dev) > >> +{ > >> + return 0; > >> +} > >> + > >> +static inline void hts221_deallocate_buffers(struct hts221_dev *dev) > >> +{ > >> +} > >> + > >> +static inline int hts221_allocate_triggers(struct hts221_dev *dev) > >> +{ > >> + return 0; > >> +} > >> + > >> +static inline void hts221_deallocate_triggers(struct hts221_dev *dev) > >> +{ > >> +} > >> +#endif /* CONFIG_IIO_BUFFER */ > >> + > >> +#endif /* HTS221_H */ > >> diff --git a/drivers/iio/humidity/hts221/hts221_buffer.c b/drivers/iio/humidity/hts221/hts221_buffer.c > >> new file mode 100644 > >> index 0000000..ab77e98 > >> --- /dev/null > >> +++ b/drivers/iio/humidity/hts221/hts221_buffer.c > >> @@ -0,0 +1,191 @@ > >> +/* > >> + * 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 0x2 > >> +#define HTS221_TEMP_DRDY_MASK 0x1 > > > > could use BIT() for the _MASKs > > > >> + > >> +int hts221_trig_set_state(struct iio_trigger *trig, bool state) > > > > static? > > > >> +{ > >> + struct iio_dev *iio_dev = iio_trigger_get_drvdata(trig); > >> + struct hts221_dev *dev = iio_priv(iio_dev); > >> + > >> + return hts221_config_drdy(dev, 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_irq(int irq, void *private) > >> +{ > >> + struct hts221_dev *dev = (struct hts221_dev *)private; > >> + struct iio_dev *iio_dev = iio_priv_to_dev(dev); > >> + > >> + dev->hw_timestamp = iio_get_time_ns(iio_dev); > >> + > >> + return IRQ_WAKE_THREAD; > >> +} > >> + > >> +static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) > >> +{ > >> + struct hts221_dev *dev = (struct hts221_dev *)private; > >> + struct iio_dev *iio_dev = iio_priv_to_dev(dev); > >> + struct iio_chan_spec const *ch; > >> + int err, count = 0; > >> + u8 status; > >> + > >> + err = dev->tf->read(dev->dev, HTS221_REG_STATUS_ADDR, 1, &status); > >> + if (err < 0) > >> + return IRQ_HANDLED; > >> + > >> + /* humidity data */ > >> + if (status & HTS221_RH_DRDY_MASK) { > >> + ch = &iio_dev->channels[HTS221_SENSOR_H]; > >> + err = dev->tf->read(dev->dev, ch->address, 2, > > > > 2 is sizeof(le16) > > we can use HTS221_DATA_SIZE > > > > >> + dev->buffer); > >> + if (err < 0) > >> + return IRQ_HANDLED; > >> + > >> + count++; > >> + } > >> + > >> + /* temp data */ > > > > temperature, to avoid confusion with temporary > > > >> + if (status & HTS221_TEMP_DRDY_MASK) { > >> + ch = &iio_dev->channels[HTS221_SENSOR_T]; > >> + err = dev->tf->read(dev->dev, ch->address, 2, > >> + dev->buffer + 2 * count); > >> + if (err < 0) > >> + return IRQ_HANDLED; > >> + > >> + count++; > >> + } > >> + > >> + if (count > 0) { > >> + iio_trigger_poll_chained(dev->trig); > >> + return IRQ_HANDLED; > >> + } else { > >> + return IRQ_NONE; > >> + } > >> +} > >> + > >> +int hts221_allocate_triggers(struct hts221_dev *dev) > >> +{ > >> + struct iio_dev *iio_dev = iio_priv_to_dev(dev); > >> + unsigned long irq_type; > >> + int err; > >> + > >> + irq_type = irqd_get_trigger_type(irq_get_irq_data(dev->irq)); > >> + > >> + switch (irq_type) { > >> + case IRQF_TRIGGER_HIGH: > >> + case IRQF_TRIGGER_RISING: > >> + break; > >> + default: > >> + dev_info(dev->dev, > >> + "mode %lx unsupported, using IRQF_TRIGGER_RISING\n", > >> + irq_type); > >> + irq_type = IRQF_TRIGGER_RISING; > >> + break; > >> + } > >> + > >> + err = devm_request_threaded_irq(dev->dev, dev->irq, > >> + hts221_trigger_handler_irq, > >> + hts221_trigger_handler_thread, > >> + irq_type | IRQF_ONESHOT, > >> + dev->name, dev); > >> + if (err) { > >> + dev_err(dev->dev, "failed to request trigger irq %d\n", > >> + dev->irq); > >> + return err; > >> + } > >> + > >> + dev->trig = devm_iio_trigger_alloc(dev->dev, "%s-trigger", > >> + iio_dev->name); > >> + if (!dev->trig) > >> + return -ENOMEM; > >> + > >> + iio_trigger_set_drvdata(dev->trig, iio_dev); > >> + dev->trig->ops = &hts221_trigger_ops; > >> + dev->trig->dev.parent = dev->dev; > >> + iio_dev->trig = iio_trigger_get(dev->trig); > >> + > >> + return iio_trigger_register(dev->trig); > >> +} > >> + > >> +void hts221_deallocate_triggers(struct hts221_dev *dev) > >> +{ > >> + iio_trigger_unregister(dev->trig); > >> +} > >> + > >> +static int hts221_buffer_preenable(struct iio_dev *iio_dev) > >> +{ > >> + return hts221_dev_power_on(iio_priv(iio_dev)); > >> +} > >> + > >> +static int hts221_buffer_postdisable(struct iio_dev *iio_dev) > >> +{ > >> + return hts221_dev_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) > >> +{ > >> + struct iio_poll_func *pf = p; > >> + struct iio_dev *iio_dev = pf->indio_dev; > >> + struct hts221_dev *dev = iio_priv(iio_dev); > >> + u8 out_data[ALIGN(4, sizeof(s64)) + sizeof(s64)]; > >> + > >> + memcpy(out_data, dev->buffer, sizeof(dev->buffer)); > > > > couldn't dev->buffer be made large enough to avoid the copy operation? > > > > ack > > >> + iio_push_to_buffers_with_timestamp(iio_dev, out_data, > >> + dev->hw_timestamp); > >> + > >> + iio_trigger_notify_done(dev->trig); > >> + > >> + return IRQ_HANDLED; > >> +} > >> + > >> +int hts221_allocate_buffers(struct hts221_dev *dev) > >> +{ > >> + return iio_triggered_buffer_setup(iio_priv_to_dev(dev), NULL, > >> + hts221_buffer_handler_thread, > >> + &hts221_buffer_ops); > >> +} > >> + > >> +void hts221_deallocate_buffers(struct hts221_dev *dev) > >> +{ > >> + iio_triggered_buffer_cleanup(iio_priv_to_dev(dev)); > >> +} > >> + > >> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>"); > >> +MODULE_DESCRIPTION("STMicroelectronics hts221 buffer driver"); > >> +MODULE_LICENSE("GPL v2"); > >> diff --git a/drivers/iio/humidity/hts221/hts221_core.c b/drivers/iio/humidity/hts221/hts221_core.c > >> new file mode 100644 > >> index 0000000..f6f9c20 > >> --- /dev/null > >> +++ b/drivers/iio/humidity/hts221/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 > >> + > > > > maybe mention that H, T is humidity, temperature resp. > > > >> +#define HTS221_REG_H_AVG_ADDR 0x10 > >> +#define HTS221_REG_T_AVG_ADDR 0x10 > > > > same address twice? > > > >> +#define HTS221_REG_H_OUT_L 0x28 > >> +#define HTS221_REG_T_OUT_L 0x2a > >> + > >> +#define HTS221_H_AVG_MASK 0x07 > >> +#define HTS221_T_AVG_MASK 0x38 > >> + > >> +#define ODR_MASK 0x87 > >> +#define BDU_MASK 0x04 > > > > HTS221_ prefix > > > >> + > >> +#define DRDY_MASK 0x04 > >> + > >> +#define ENABLE_SENSOR 0x80 > > > > prefix, use BIT() > > > >> + > >> +#define HTS221_H_AVG_4 0x00 /* 0.4 %RH */ > >> +#define HTS221_H_AVG_8 0x01 /* 0.3 %RH */ > >> +#define HTS221_H_AVG_16 0x02 /* 0.2 %RH */ > >> +#define HTS221_H_AVG_32 0x03 /* 0.15 %RH */ > >> +#define HTS221_H_AVG_64 0x04 /* 0.1 %RH */ > >> +#define HTS221_H_AVG_128 0x05 /* 0.07 %RH */ > >> +#define HTS221_H_AVG_256 0x06 /* 0.05 %RH */ > >> +#define HTS221_H_AVG_512 0x07 /* 0.03 %RH */ > >> + > >> +#define HTS221_T_AVG_2 0x00 /* 0.08 degC */ > >> +#define HTS221_T_AVG_4 0x08 /* 0.05 degC */ > >> +#define HTS221_T_AVG_8 0x10 /* 0.04 degC */ > >> +#define HTS221_T_AVG_16 0x18 /* 0.03 degC */ > >> +#define HTS221_T_AVG_32 0x20 /* 0.02 degC */ > >> +#define HTS221_T_AVG_64 0x28 /* 0.015 degC */ > >> +#define HTS221_T_AVG_128 0x30 /* 0.01 degC */ > >> +#define HTS221_T_AVG_256 0x38 /* 0.007 degC */ > >> + > >> +/* caldata registers */ > > > > calibration data? > > > >> +#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 { > >> + u32 hz; > > > > maybe int, not u32 > > why int? hz can't be negative u32 hints that the hardware wants 32-bit unsigned (which is not the case) int (or rather unsigned int) hints that it is just a value... just an arguable detail > > > >> + 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.5 HZ */ > > > > HZ or Hz; > > consistent space > > > >> +}; > >> + > >> +static const struct hts221_avg hts221_avg_list[] = { > >> + { > >> + .addr = HTS221_REG_H_AVG_ADDR, > >> + .mask = HTS221_H_AVG_MASK, > >> + .avg_avl = { > >> + { 4, HTS221_H_AVG_4 }, > >> + { 8, HTS221_H_AVG_8 }, > >> + { 16, HTS221_H_AVG_16 }, > >> + { 32, HTS221_H_AVG_32 }, > >> + { 64, HTS221_H_AVG_64 }, > >> + { 128, HTS221_H_AVG_128 }, > >> + { 256, HTS221_H_AVG_256 }, > >> + { 512, HTS221_H_AVG_512 }, > >> + }, > >> + }, > >> + { > >> + .addr = HTS221_REG_T_AVG_ADDR, > >> + .mask = HTS221_T_AVG_MASK, > >> + .avg_avl = { > >> + { 2, HTS221_T_AVG_2 }, > >> + { 4, HTS221_T_AVG_4 }, > >> + { 8, HTS221_T_AVG_8 }, > >> + { 16, HTS221_T_AVG_16 }, > >> + { 32, HTS221_T_AVG_32 }, > >> + { 64, HTS221_T_AVG_64 }, > >> + { 128, HTS221_T_AVG_128 }, > >> + { 256, HTS221_T_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_dev *dev, u8 addr, u8 mask, > >> + u8 val) > >> +{ > >> + u8 data; > >> + int err; > >> + > >> + mutex_lock(&dev->lock); > >> + > >> + err = dev->tf->read(dev->dev, addr, 1, &data); > >> + if (err < 0) { > >> + dev_err(dev->dev, "failed to read %02x register\n", addr); > >> + mutex_unlock(&dev->lock); > >> + > >> + return err; > >> + } > >> + > >> + data = (data & ~mask) | (val & mask); > >> + > >> + err = dev->tf->write(dev->dev, addr, 1, &data); > >> + if (err < 0) { > >> + dev_err(dev->dev, "failed to write %02x register\n", addr); > >> + mutex_unlock(&dev->lock); > >> + > >> + return err; > >> + } > >> + > >> + mutex_unlock(&dev->lock); > >> + > >> + return 0; > >> +} > >> + > >> +static int hts221_check_whoami(struct hts221_dev *dev) > >> +{ > >> + u8 data; > >> + int err; > >> + > >> + err = dev->tf->read(dev->dev, HTS221_REG_WHOAMI_ADDR, 1, &data); > > > > 1 is sizeof(data) > > > >> + if (err < 0) { > >> + dev_err(dev->dev, "failed to read whoami register\n"); > >> + return err; > >> + } > >> + > >> + if (data != HTS221_REG_WHOAMI_VAL) { > >> + dev_err(dev->dev, "wrong whoami {%02x-%02x}\n", > > > > %02x vs %02x > > or > > %02x != %02x > > would be a lot clearer > > > >> + data, HTS221_REG_WHOAMI_VAL); > >> + return -ENODEV; > >> + } > >> + > >> + return 0; > >> +} > >> + > >> +int hts221_config_drdy(struct hts221_dev *dev, bool enable) > >> +{ > >> + u8 val = (enable) ? 0x04 : 0; > > > > parenthesis not needed; could use BIT() or #define for 0x04 > > > >> + > >> + return hts221_write_with_mask(dev, HTS221_REG_CNTRL3_ADDR, > >> + DRDY_MASK, val); > >> +} > >> + > >> +static int hts221_update_odr(struct hts221_dev *dev, u8 odr) > >> +{ > >> + int i, err; > >> + > >> + 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; > >> + > >> + err = hts221_write_with_mask(dev, HTS221_REG_CNTRL1_ADDR, ODR_MASK, > >> + ENABLE_SENSOR | BDU_MASK | > >> + hts221_odr_table[i].val); > >> + if (err < 0) > >> + return err; > >> + > >> + dev->odr = odr; > >> + > >> + return 0; > >> +} > >> + > >> +static int hts221_update_avg(struct hts221_sensor *sensor, u16 val) > >> +{ > >> + int i, err; > >> + const struct hts221_avg *avg = &hts221_avg_list[sensor->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(sensor->dev, avg->addr, avg->mask, > >> + avg->avg_avl[i].val); > >> + if (err < 0) > >> + return err; > >> + > >> + sensor->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) > >> +{ > >> + struct hts221_avg const *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) > >> +{ > >> + struct hts221_avg const *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_dev_power_on(struct hts221_dev *dev) > >> +{ > >> + struct hts221_sensor *sensor; > >> + int i, err, val; > >> + > >> + for (i = 0; i < HTS221_SENSOR_MAX; i++) { > >> + sensor = &dev->sensors[i]; > >> + val = hts221_avg_list[i].avg_avl[sensor->cur_avg_idx].avg; > >> + > >> + err = hts221_update_avg(sensor, val); > >> + if (err < 0) > >> + return err; > >> + } > >> + > >> + err = hts221_update_odr(dev, dev->odr); > >> + if (err < 0) > >> + return err; > >> + > >> + return 0; > >> +} > >> + > >> +int hts221_dev_power_off(struct hts221_dev *dev) > >> +{ > >> + u8 data[] = {0x00, 0x00}; > >> + > >> + return dev->tf->write(dev->dev, HTS221_REG_CNTRL1_ADDR, 2, data); > > > > sizeof(data) > > > >> +} > >> + > >> +static int hts221_parse_caldata(struct hts221_sensor *sensor) > >> +{ > >> + int err, *slope, *b_gen; > >> + u8 addr_x0, addr_x1; > >> + s16 cal_x0, cal_x1, cal_y0, cal_y1; > >> + struct hts221_dev *dev = sensor->dev; > >> + > >> + switch (sensor->type) { > >> + case HTS221_SENSOR_H: > >> + addr_x0 = HTS221_REG_0RH_CAL_X_H; > >> + addr_x1 = HTS221_REG_1RH_CAL_X_H; > >> + > >> + cal_y1 = 0; > >> + cal_y0 = 0; > >> + err = dev->tf->read(dev->dev, HTS221_REG_0RH_CAL_Y_H, 1, > >> + (u8 *)&cal_y0); > >> + if (err < 0) > >> + return err; > > > > endianness? > > this wouldn't work on BE > > right, fixed in v3 > > > > >> + > >> + err = dev->tf->read(dev->dev, HTS221_REG_1RH_CAL_Y_H, 1, > >> + (u8 *)&cal_y1); > >> + if (err < 0) > >> + return err; > >> + break; > >> + case HTS221_SENSOR_T: { > >> + u8 cal0, cal1; > >> + > >> + addr_x0 = HTS221_REG_0T_CAL_X_L; > >> + addr_x1 = HTS221_REG_1T_CAL_X_L; > >> + > >> + err = dev->tf->read(dev->dev, HTS221_REG_0T_CAL_Y_H, 1, > >> + &cal0); > >> + if (err < 0) > >> + return err; > >> + > >> + err = dev->tf->read(dev->dev, HTS221_REG_T1_T0_CAL_Y_H, 1, > >> + &cal1); > >> + if (err < 0) > >> + return err; > >> + cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0; > > > > cal1 is a byte and is endianness corrected?! > > le16_to_cpu not needed > > ack > > > > >> + > >> + err = dev->tf->read(dev->dev, HTS221_REG_1T_CAL_Y_H, 1, > >> + &cal0); > >> + if (err < 0) > >> + return err; > >> + > >> + err = dev->tf->read(dev->dev, HTS221_REG_T1_T0_CAL_Y_H, 1, > >> + &cal1); > >> + if (err < 0) > >> + return err; > >> + cal_y1 = (le16_to_cpu((cal1 & 0xc) >> 2) << 8) | cal0; > >> + break; > >> + } > >> + default: > >> + return -ENODEV; > >> + } > >> + > >> + err = dev->tf->read(dev->dev, addr_x0, 2, (u8 *)&cal_x0); > >> + if (err < 0) > >> + return err; > > > > cal_x0 is s16, maybe use le16 > > why le32_to_cpu() then? > > ack > > > > >> + cal_x0 = le32_to_cpu(cal_x0); > >> + > >> + err = dev->tf->read(dev->dev, addr_x1, 2, (u8 *)&cal_x1); > >> + if (err < 0) > >> + return err; > >> + cal_x1 = le32_to_cpu(cal_x1); > >> + > >> + slope = &sensor->slope; > >> + b_gen = &sensor->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_read_raw(struct iio_dev *iio_dev, > >> + struct iio_chan_spec const *ch, > >> + int *val, int *val2, long mask) > >> +{ > >> + struct hts221_dev *dev = 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: { > >> + u8 data[2]; > > > > le16 data > > > >> + > >> + ret = hts221_dev_power_on(dev); > >> + if (ret < 0) > >> + goto out; > >> + > >> + msleep(50); > >> + > >> + ret = dev->tf->read(dev->dev, ch->address, 2, data); > > > > sizeof(le16) > > same as in hts221_trigger_handler_thread() > > > > >> + if (ret < 0) > >> + goto out; > >> + > >> + ret = hts221_dev_power_off(dev); > >> + if (ret < 0) > >> + goto out; > >> + > >> + *val = (s16)get_unaligned_le16(data); > >> + ret = IIO_VAL_INT; > >> + > >> + break; > >> + } > >> + case IIO_CHAN_INFO_SCALE: { > >> + s64 tmp; > >> + s32 rem, div, data; > >> + > >> + switch (ch->type) { > >> + case IIO_HUMIDITYRELATIVE: > >> + data = dev->sensors[HTS221_SENSOR_H].slope; > >> + div = (1 << 4) * 1000; > >> + break; > >> + case IIO_TEMP: > >> + data = dev->sensors[HTS221_SENSOR_T].slope; > >> + div = (1 << 6) * 1000; > >> + break; > >> + default: > >> + goto out; > >> + } > >> + > >> + tmp = div_s64(data * 1000000000LL, div); > >> + tmp = div_s64_rem(tmp, 1000000000LL, &rem); > > > > common code, could split out in separate function > > is it worth defining a function for a couple of lines of code? just a suggestion; if you find a nice, descriptive name for the function it would help understand what's going on otherweise just leave as is > > > >> + > >> + *val = tmp; > >> + *val2 = rem; > >> + ret = IIO_VAL_INT_PLUS_NANO; > >> + break; > >> + } > >> + case IIO_CHAN_INFO_OFFSET: { > >> + s64 tmp; > >> + s32 rem, div, data; > >> + > >> + switch (ch->type) { > >> + case IIO_HUMIDITYRELATIVE: > >> + data = dev->sensors[HTS221_SENSOR_H].b_gen; > >> + div = dev->sensors[HTS221_SENSOR_H].slope; > >> + break; > >> + case IIO_TEMP: > >> + data = dev->sensors[HTS221_SENSOR_T].b_gen; > >> + div = dev->sensors[HTS221_SENSOR_T].slope; > >> + break; > >> + default: > >> + goto out; > >> + } > >> + > >> + tmp = div_s64(data * 1000000000LL, div); > >> + tmp = div_s64_rem(tmp, 1000000000LL, &rem); > >> + > >> + *val = tmp; > >> + *val2 = abs(rem); > >> + ret = IIO_VAL_INT_PLUS_NANO; > >> + break; > >> + } > >> + case IIO_CHAN_INFO_SAMP_FREQ: > >> + *val = dev->odr; > >> + ret = IIO_VAL_INT; > >> + break; > >> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { > >> + u8 idx; > >> + struct hts221_avg const *avg; > > > > const struct is more popular > > > >> + > >> + switch (ch->type) { > >> + case IIO_HUMIDITYRELATIVE: > >> + avg = &hts221_avg_list[HTS221_SENSOR_H]; > >> + idx = dev->sensors[HTS221_SENSOR_H].cur_avg_idx; > >> + break; > >> + case IIO_TEMP: > >> + avg = &hts221_avg_list[HTS221_SENSOR_T]; > >> + idx = dev->sensors[HTS221_SENSOR_T].cur_avg_idx; > >> + break; > >> + default: > >> + goto out; > >> + } > >> + > >> + *val = avg->avg_avl[idx].avg; > >> + ret = IIO_VAL_INT; > >> + break; > >> + } > >> + default: > >> + ret = -EINVAL; > >> + break; > >> + } > >> + > >> +out: > >> + 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_dev *dev = 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(dev, val); > >> + break; > >> + case IIO_CHAN_INFO_OVERSAMPLING_RATIO: { > >> + enum hts221_sensor_type type; > >> + > >> + switch (chan->type) { > >> + case IIO_HUMIDITYRELATIVE: > >> + type = HTS221_SENSOR_H; > >> + break; > >> + case IIO_TEMP: > >> + type = HTS221_SENSOR_T; > >> + break; > >> + default: > >> + ret = -EINVAL; > >> + goto out; > >> + } > >> + > >> + ret = hts221_update_avg(&dev->sensors[type], val); > >> + break; > >> + } > >> + default: > >> + ret = -EINVAL; > >> + break; > >> + } > >> + > >> +out: > >> + iio_device_release_direct_mode(iio_dev); > >> + > >> + return ret; > >> +} > >> + > >> +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, > >> +}; > >> + > >> +static const unsigned long hts221_scan_masks[] = {0x3, 0x0}; > >> + > >> +int hts221_probe(struct hts221_dev *dev) > >> +{ > >> + struct iio_dev *iio_dev = iio_priv_to_dev(dev); > >> + int i, err; > >> + > >> + mutex_init(&dev->lock); > >> + > >> + err = hts221_check_whoami(dev); > >> + if (err < 0) > >> + return err; > >> + > >> + err = hts221_update_odr(dev, 1); > >> + if (err < 0) > >> + return err; > >> + > >> + dev->odr = hts221_odr_table[0].hz; > >> + > >> + iio_dev->modes = INDIO_DIRECT_MODE; > >> + iio_dev->dev.parent = dev->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; > >> + > >> + for (i = 0; i < HTS221_SENSOR_MAX; i++) { > >> + dev->sensors[i].type = i; > >> + dev->sensors[i].dev = dev; > >> + > >> + err = hts221_update_avg(&dev->sensors[i], > >> + hts221_avg_list[i].avg_avl[3].avg); > >> + if (err < 0) > >> + goto power_off; > >> + > >> + err = hts221_parse_caldata(&dev->sensors[i]); > >> + if (err < 0) > >> + goto power_off; > >> + } > >> + > >> + err = hts221_dev_power_off(dev); > >> + if (err < 0) > >> + return err; > >> + > >> + if (dev->irq > 0) { > >> + err = hts221_allocate_buffers(dev); > >> + if (err < 0) > >> + return err; > >> + > >> + err = hts221_allocate_triggers(dev); > >> + if (err) { > >> + hts221_deallocate_buffers(dev); > >> + return err; > >> + } > >> + } > >> + > >> + return iio_device_register(iio_dev); > >> + > >> +power_off: > >> + hts221_dev_power_off(dev); > >> + > >> + return err; > >> +} > >> +EXPORT_SYMBOL(hts221_probe); > >> + > >> +int hts221_remove(struct hts221_dev *dev) > >> +{ > >> + struct iio_dev *iio_dev = iio_priv_to_dev(dev); > >> + int err; > >> + > >> + err = hts221_dev_power_off(dev); > >> + > >> + iio_device_unregister(iio_dev); > > > > first unregister, then power off > > mirror the calls in _probe in reverse order > > > >> + > >> + if (dev->irq > 0) { > >> + hts221_deallocate_triggers(dev); > >> + hts221_deallocate_buffers(dev); > >> + } > >> + > >> + return err; > >> +} > >> +EXPORT_SYMBOL(hts221_remove); > >> + > >> +MODULE_AUTHOR("Lorenzo Bianconi <lorenzo.bianconi@xxxxxx>"); > >> +MODULE_DESCRIPTION("STMicroelectronics hts221 sensor driver"); > >> +MODULE_LICENSE("GPL v2"); > >> diff --git a/drivers/iio/humidity/hts221/hts221_i2c.c b/drivers/iio/humidity/hts221/hts221_i2c.c > >> new file mode 100644 > >> index 0000000..28ea781 > >> --- /dev/null > >> +++ b/drivers/iio/humidity/hts221/hts221_i2c.c > >> @@ -0,0 +1,119 @@ > >> +/* > >> + * 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_dev *dev; > >> + struct iio_dev *iio_dev; > >> + > >> + iio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev)); > >> + if (!iio_dev) > >> + return -ENOMEM; > >> + > >> + i2c_set_clientdata(client, iio_dev); > >> + > >> + dev = iio_priv(iio_dev); > >> + dev->name = client->name; > >> + dev->dev = &client->dev; > >> + dev->irq = client->irq; > >> + dev->tf = &hts221_transfer_fn; > >> + > >> + return hts221_probe(dev); > >> +} > >> + > >> +static int hts221_i2c_remove(struct i2c_client *client) > >> +{ > >> + struct iio_dev *iio_dev = i2c_get_clientdata(client); > >> + struct hts221_dev *dev = iio_priv(iio_dev); > >> + > >> + return hts221_remove(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, > >> + .remove = hts221_i2c_remove, > >> + .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/hts221_spi.c b/drivers/iio/humidity/hts221/hts221_spi.c > >> new file mode 100644 > >> index 0000000..f0f649e > >> --- /dev/null > >> +++ b/drivers/iio/humidity/hts221/hts221_spi.c > >> @@ -0,0 +1,134 @@ > >> +/* > >> + * 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 *device, u8 addr, int len, u8 *data) > >> +{ > >> + int err; > >> + struct spi_device *spi = to_spi_device(device); > >> + struct iio_dev *iio_dev = spi_get_drvdata(spi); > >> + struct hts221_dev *dev = iio_priv(iio_dev); > >> + > >> + struct spi_transfer xfers[] = { > >> + { > >> + .tx_buf = dev->tb.tx_buf, > >> + .bits_per_word = 8, > >> + .len = 1, > >> + }, > >> + { > >> + .rx_buf = dev->tb.rx_buf, > >> + .bits_per_word = 8, > >> + .len = len, > >> + } > >> + }; > >> + > >> + if (len > 1) > >> + addr |= SPI_AUTO_INCREMENT; > >> + dev->tb.tx_buf[0] = addr | SENSORS_SPI_READ; > >> + > >> + err = spi_sync_transfer(spi, xfers, ARRAY_SIZE(xfers)); > >> + if (err < 0) > >> + return err; > >> + > >> + memcpy(data, dev->tb.rx_buf, len * sizeof(u8)); > >> + > >> + return len; > >> +} > >> + > >> +static int hts221_spi_write(struct device *device, u8 addr, int len, u8 *data) > >> +{ > >> + struct spi_device *spi = to_spi_device(device); > >> + struct iio_dev *iio_dev = spi_get_drvdata(spi); > >> + struct hts221_dev *dev = iio_priv(iio_dev); > >> + > >> + struct spi_transfer xfers = { > >> + .tx_buf = dev->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; > >> + dev->tb.tx_buf[0] = addr; > >> + memcpy(&dev->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_dev *dev; > >> + struct iio_dev *iio_dev; > >> + > >> + iio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*dev)); > >> + if (!iio_dev) > >> + return -ENOMEM; > >> + > >> + spi_set_drvdata(spi, iio_dev); > >> + > >> + dev = iio_priv(iio_dev); > >> + dev->name = spi->modalias; > >> + dev->dev = &spi->dev; > >> + dev->irq = spi->irq; > >> + dev->tf = &hts221_transfer_fn; > >> + > >> + return hts221_probe(dev); > >> +} > >> + > >> +static int hts221_spi_remove(struct spi_device *spi) > >> +{ > >> + struct iio_dev *iio_dev = spi_get_drvdata(spi); > >> + struct hts221_dev *dev = iio_priv(iio_dev); > >> + > >> + return hts221_remove(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, > >> + .remove = hts221_spi_remove, > >> + .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"); > >> > > > > -- > > > > Peter Meerwald-Stadler > > +43-664-2444418 (mobile) > > > > -- Peter Meerwald-Stadler +43-664-2444418 (mobile) -- 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