From cb60610af54b143a74991c70448157baf5bfc345 Mon Sep 17 00:00:00 2001 From: Xiongfeng Wang Date: Fri, 12 Jan 2018 15:45:38 +0800 Subject: iio: accel: use strlcpy() instead of strncpy() gcc-8 reports drivers/iio/accel/st_accel_i2c.c: In function 'st_accel_i2c_probe': ./include/linux/string.h:245:9: warning: '__builtin_strncpy' specified bound 20 equals destination size [-Wstringop-truncation] The compiler require that the length of the dest string is greater than the length we want to copy to make sure the dest string is nul-terminated. We can just use strlcpy() to avoid this warning. Signed-off-by: Xiongfeng Wang Signed-off-by: Jonathan Cameron --- drivers/iio/accel/st_accel_i2c.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/accel/st_accel_i2c.c b/drivers/iio/accel/st_accel_i2c.c index 363429b5686c..6bdec8c451e0 100644 --- a/drivers/iio/accel/st_accel_i2c.c +++ b/drivers/iio/accel/st_accel_i2c.c @@ -159,9 +159,8 @@ static int st_accel_i2c_probe(struct i2c_client *client, if ((ret < 0) || (ret >= ST_ACCEL_MAX)) return -ENODEV; - strncpy(client->name, st_accel_id_table[ret].name, + strlcpy(client->name, st_accel_id_table[ret].name, sizeof(client->name)); - client->name[sizeof(client->name) - 1] = '\0'; } else if (!id) return -ENODEV; -- cgit v1.2.3 From bf388e3a9429b9a477ba9487c29fcf91c504d8d9 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 8 Jan 2018 23:12:28 +0100 Subject: iio: humidity: hts221: remove warnings in hts221_parse_{temp,rh}_caldata() Remove following sparse warnings in hts221_parse_temp_caldata() and in hts221_parse_rh_caldata(): drivers/iio/humidity/hts221_core.c:302:19: warning: cast to restricted __le16 drivers/iio/humidity/hts221_core.c:314:18: warning: cast to restricted __le16 drivers/iio/humidity/hts221_core.c:320:18: warning: cast to restricted __le16 drivers/iio/humidity/hts221_core.c:355:18: warning: cast to restricted __le16 drivers/iio/humidity/hts221_core.c:361:18: warning: cast to restricted __le16 Fixes: e4a70e3e7d84 ("iio: humidity: add support to hts221 rh/temp combo device") Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221_core.c | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index d3f7904766bd..b662afb2b35c 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -289,6 +289,7 @@ 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; + __le16 val; err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H, sizeof(cal0), &cal0); @@ -299,7 +300,7 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw) sizeof(cal1), &cal1); if (err < 0) return err; - cal_y0 = (le16_to_cpu(cal1 & 0x3) << 8) | cal0; + cal_y0 = ((cal1 & 0x3) << 8) | cal0; err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H, sizeof(cal0), &cal0); @@ -307,17 +308,17 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw) 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); + err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_X_L, sizeof(val), + (u8 *)&val); if (err < 0) return err; - cal_x0 = le16_to_cpu(cal_x0); + cal_x0 = le16_to_cpu(val); - err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(cal_x1), - (u8 *)&cal_x1); + err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(val), + (u8 *)&val); if (err < 0) return err; - cal_x1 = le16_to_cpu(cal_x1); + cal_x1 = le16_to_cpu(val); slope = &hw->sensors[HTS221_SENSOR_T].slope; b_gen = &hw->sensors[HTS221_SENSOR_T].b_gen; @@ -334,6 +335,7 @@ static int hts221_parse_rh_caldata(struct hts221_hw *hw) { int err, *slope, *b_gen; s16 cal_x0, cal_x1, cal_y0, cal_y1; + __le16 val; u8 data; err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data), @@ -348,17 +350,17 @@ static int hts221_parse_rh_caldata(struct hts221_hw *hw) return err; cal_y1 = data; - err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(cal_x0), - (u8 *)&cal_x0); + err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(val), + (u8 *)&val); if (err < 0) return err; - cal_x0 = le16_to_cpu(cal_x0); + cal_x0 = le16_to_cpu(val); - err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(cal_x1), - (u8 *)&cal_x1); + err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(val), + (u8 *)&val); if (err < 0) return err; - cal_x1 = le16_to_cpu(cal_x1); + cal_x1 = le16_to_cpu(val); slope = &hw->sensors[HTS221_SENSOR_H].slope; b_gen = &hw->sensors[HTS221_SENSOR_H].b_gen; -- cgit v1.2.3 From b62c4a96a6627793b2ac2ad3530e37a1cec29559 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 8 Jan 2018 23:12:29 +0100 Subject: iio: humidity: hts221: remove trailing whitespace from a comment Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221_buffer.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index e971ea425268..f9522ee62170 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -61,7 +61,7 @@ static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) if (err < 0) return IRQ_HANDLED; - /* + /* * 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 -- cgit v1.2.3 From 621779220bfc5d2f3e3790e491ae9f91372a12e7 Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 8 Jan 2018 23:12:30 +0100 Subject: iio: humidity: hts221: add regmap API support Introduce regmap API support to access to i2c/spi bus instead of using a custom support. Remove lock mutex since concurrency is already managed by regmap API Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/Kconfig | 2 + drivers/iio/humidity/hts221.h | 21 +------ drivers/iio/humidity/hts221_buffer.c | 37 +++++------ drivers/iio/humidity/hts221_core.c | 115 +++++++++++++---------------------- drivers/iio/humidity/hts221_i2c.c | 64 ++++++------------- drivers/iio/humidity/hts221_spi.c | 81 ++++++------------------ 6 files changed, 103 insertions(+), 217 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/Kconfig b/drivers/iio/humidity/Kconfig index 2c0fc9a400b8..1a0d458e4f4e 100644 --- a/drivers/iio/humidity/Kconfig +++ b/drivers/iio/humidity/Kconfig @@ -68,10 +68,12 @@ config HTS221 config HTS221_I2C tristate depends on HTS221 + select REGMAP_I2C config HTS221_SPI tristate depends on HTS221 + select REGMAP_SPI config HTU21 tristate "Measurement Specialties HTU21 humidity & temperature sensor" diff --git a/drivers/iio/humidity/hts221.h b/drivers/iio/humidity/hts221.h index c581af8c0f5d..e41a3d83e95d 100644 --- a/drivers/iio/humidity/hts221.h +++ b/drivers/iio/humidity/hts221.h @@ -15,21 +15,8 @@ #include -#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); -}; - enum hts221_sensor_type { HTS221_SENSOR_H, HTS221_SENSOR_T, @@ -44,8 +31,8 @@ struct hts221_sensor { struct hts221_hw { const char *name; struct device *dev; + struct regmap *regmap; - struct mutex lock; struct iio_trigger *trig; int irq; @@ -53,16 +40,12 @@ struct hts221_hw { bool enabled; u8 odr; - - const struct hts221_transfer_function *tf; - struct hts221_transfer_buffer tb; }; extern const struct dev_pm_ops hts221_pm_ops; -int hts221_write_with_mask(struct hts221_hw *hw, u8 addr, u8 mask, u8 val); int hts221_probe(struct device *dev, int irq, const char *name, - const struct hts221_transfer_function *tf_ops); + struct regmap *regmap); int hts221_set_enable(struct hts221_hw *hw, bool enable); int hts221_allocate_buffers(struct hts221_hw *hw); int hts221_allocate_trigger(struct hts221_hw *hw); diff --git a/drivers/iio/humidity/hts221_buffer.c b/drivers/iio/humidity/hts221_buffer.c index f9522ee62170..1a94b0b91721 100644 --- a/drivers/iio/humidity/hts221_buffer.c +++ b/drivers/iio/humidity/hts221_buffer.c @@ -12,6 +12,8 @@ #include #include #include +#include +#include #include #include @@ -38,12 +40,10 @@ 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); - int err; - - err = hts221_write_with_mask(hw, HTS221_REG_DRDY_EN_ADDR, - HTS221_REG_DRDY_EN_MASK, state); - return err < 0 ? err : 0; + return regmap_update_bits(hw->regmap, HTS221_REG_DRDY_EN_ADDR, + HTS221_REG_DRDY_EN_MASK, + FIELD_PREP(HTS221_REG_DRDY_EN_MASK, state)); } static const struct iio_trigger_ops hts221_trigger_ops = { @@ -53,11 +53,9 @@ static const struct iio_trigger_ops hts221_trigger_ops = { static irqreturn_t hts221_trigger_handler_thread(int irq, void *private) { struct hts221_hw *hw = private; - u8 status; - int err; + int err, status; - err = hw->tf->read(hw->dev, HTS221_REG_STATUS_ADDR, sizeof(status), - &status); + err = regmap_read(hw->regmap, HTS221_REG_STATUS_ADDR, &status); if (err < 0) return IRQ_HANDLED; @@ -102,8 +100,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw) break; } - err = hts221_write_with_mask(hw, HTS221_REG_DRDY_HL_ADDR, - HTS221_REG_DRDY_HL_MASK, irq_active_low); + err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_HL_ADDR, + HTS221_REG_DRDY_HL_MASK, + FIELD_PREP(HTS221_REG_DRDY_HL_MASK, + irq_active_low)); if (err < 0) return err; @@ -114,9 +114,10 @@ int hts221_allocate_trigger(struct hts221_hw *hw) open_drain = true; } - err = hts221_write_with_mask(hw, HTS221_REG_DRDY_PP_OD_ADDR, - HTS221_REG_DRDY_PP_OD_MASK, - open_drain); + err = regmap_update_bits(hw->regmap, HTS221_REG_DRDY_PP_OD_ADDR, + HTS221_REG_DRDY_PP_OD_MASK, + FIELD_PREP(HTS221_REG_DRDY_PP_OD_MASK, + open_drain)); if (err < 0) return err; @@ -171,15 +172,15 @@ static irqreturn_t hts221_buffer_handler_thread(int irq, void *p) /* humidity data */ ch = &iio_dev->channels[HTS221_SENSOR_H]; - err = hw->tf->read(hw->dev, ch->address, HTS221_DATA_SIZE, - buffer); + err = regmap_bulk_read(hw->regmap, ch->address, + buffer, HTS221_DATA_SIZE); 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); + err = regmap_bulk_read(hw->regmap, ch->address, + buffer + HTS221_DATA_SIZE, HTS221_DATA_SIZE); if (err < 0) goto out; diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index b662afb2b35c..fb74a22ef3f5 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include #include "hts221.h" @@ -131,38 +133,11 @@ static const struct iio_chan_spec hts221_channels[] = { IIO_CHAN_SOFT_TIMESTAMP(2), }; -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 << __ffs(mask)) & 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; + int err, data; - err = hw->tf->read(hw->dev, HTS221_REG_WHOAMI_ADDR, sizeof(data), - &data); + err = regmap_read(hw->regmap, HTS221_REG_WHOAMI_ADDR, &data); if (err < 0) { dev_err(hw->dev, "failed to read whoami register\n"); return err; @@ -188,8 +163,10 @@ static int hts221_update_odr(struct hts221_hw *hw, u8 odr) if (i == ARRAY_SIZE(hts221_odr_table)) return -EINVAL; - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_ODR_MASK, hts221_odr_table[i].val); + err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR, + HTS221_ODR_MASK, + FIELD_PREP(HTS221_ODR_MASK, + hts221_odr_table[i].val)); if (err < 0) return err; @@ -202,8 +179,8 @@ 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]; + int i, err, data; for (i = 0; i < HTS221_AVG_DEPTH; i++) if (avg->avg_avl[i] == val) @@ -212,7 +189,9 @@ static int hts221_update_avg(struct hts221_hw *hw, if (i == HTS221_AVG_DEPTH) return -EINVAL; - err = hts221_write_with_mask(hw, avg->addr, avg->mask, i); + data = ((i << __ffs(avg->mask)) & avg->mask); + err = regmap_update_bits(hw->regmap, avg->addr, + avg->mask, data); if (err < 0) return err; @@ -274,8 +253,9 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable) { int err; - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_ENABLE_MASK, enable); + err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR, + HTS221_ENABLE_MASK, + FIELD_PREP(HTS221_ENABLE_MASK, enable)); if (err < 0) return err; @@ -286,36 +266,32 @@ int hts221_set_enable(struct hts221_hw *hw, bool enable) static int hts221_parse_temp_caldata(struct hts221_hw *hw) { - int err, *slope, *b_gen; + int err, *slope, *b_gen, cal0, cal1; s16 cal_x0, cal_x1, cal_y0, cal_y1; - u8 cal0, cal1; __le16 val; - err = hw->tf->read(hw->dev, HTS221_REG_0T_CAL_Y_H, - sizeof(cal0), &cal0); + err = regmap_read(hw->regmap, HTS221_REG_0T_CAL_Y_H, &cal0); if (err < 0) return err; - err = hw->tf->read(hw->dev, HTS221_REG_T1_T0_CAL_Y_H, - sizeof(cal1), &cal1); + err = regmap_read(hw->regmap, HTS221_REG_T1_T0_CAL_Y_H, &cal1); if (err < 0) return err; cal_y0 = ((cal1 & 0x3) << 8) | cal0; - err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_Y_H, - sizeof(cal0), &cal0); + err = regmap_read(hw->regmap, HTS221_REG_1T_CAL_Y_H, &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(val), - (u8 *)&val); + err = regmap_bulk_read(hw->regmap, HTS221_REG_0T_CAL_X_L, + &val, sizeof(val)); if (err < 0) return err; cal_x0 = le16_to_cpu(val); - err = hw->tf->read(hw->dev, HTS221_REG_1T_CAL_X_L, sizeof(val), - (u8 *)&val); + err = regmap_bulk_read(hw->regmap, HTS221_REG_1T_CAL_X_L, + &val, sizeof(val)); if (err < 0) return err; cal_x1 = le16_to_cpu(val); @@ -333,31 +309,28 @@ static int hts221_parse_temp_caldata(struct hts221_hw *hw) static int hts221_parse_rh_caldata(struct hts221_hw *hw) { - int err, *slope, *b_gen; + int err, *slope, *b_gen, data; s16 cal_x0, cal_x1, cal_y0, cal_y1; __le16 val; - u8 data; - err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_Y_H, sizeof(data), - &data); + err = regmap_read(hw->regmap, HTS221_REG_0RH_CAL_Y_H, &data); if (err < 0) return err; cal_y0 = data; - err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_Y_H, sizeof(data), - &data); + err = regmap_read(hw->regmap, HTS221_REG_1RH_CAL_Y_H, &data); if (err < 0) return err; cal_y1 = data; - err = hw->tf->read(hw->dev, HTS221_REG_0RH_CAL_X_H, sizeof(val), - (u8 *)&val); + err = regmap_bulk_read(hw->regmap, HTS221_REG_0RH_CAL_X_H, + &val, sizeof(val)); if (err < 0) return err; cal_x0 = le16_to_cpu(val); - err = hw->tf->read(hw->dev, HTS221_REG_1RH_CAL_X_H, sizeof(val), - (u8 *)&val); + err = regmap_bulk_read(hw->regmap, HTS221_REG_1RH_CAL_X_H, + &val, sizeof(val)); if (err < 0) return err; cal_x1 = le16_to_cpu(val); @@ -442,7 +415,7 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val) msleep(50); - err = hw->tf->read(hw->dev, addr, sizeof(data), data); + err = regmap_bulk_read(hw->regmap, addr, data, sizeof(data)); if (err < 0) return err; @@ -584,7 +557,7 @@ static const struct iio_info hts221_info = { static const unsigned long hts221_scan_masks[] = {0x3, 0x0}; int hts221_probe(struct device *dev, int irq, const char *name, - const struct hts221_transfer_function *tf_ops) + struct regmap *regmap) { struct iio_dev *iio_dev; struct hts221_hw *hw; @@ -601,9 +574,7 @@ int hts221_probe(struct device *dev, int irq, const char *name, hw->name = name; hw->dev = dev; hw->irq = irq; - hw->tf = tf_ops; - - mutex_init(&hw->lock); + hw->regmap = regmap; err = hts221_check_whoami(hw); if (err < 0) @@ -618,8 +589,9 @@ int hts221_probe(struct device *dev, int irq, const char *name, iio_dev->info = &hts221_info; /* enable Block Data Update */ - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_BDU_MASK, 1); + err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR, + HTS221_BDU_MASK, + FIELD_PREP(HTS221_BDU_MASK, 1)); if (err < 0) return err; @@ -675,12 +647,10 @@ static int __maybe_unused hts221_suspend(struct device *dev) { struct iio_dev *iio_dev = dev_get_drvdata(dev); struct hts221_hw *hw = iio_priv(iio_dev); - int err; - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_ENABLE_MASK, false); - - return err < 0 ? err : 0; + return regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR, + HTS221_ENABLE_MASK, + FIELD_PREP(HTS221_ENABLE_MASK, false)); } static int __maybe_unused hts221_resume(struct device *dev) @@ -690,9 +660,10 @@ static int __maybe_unused hts221_resume(struct device *dev) int err = 0; if (hw->enabled) - err = hts221_write_with_mask(hw, HTS221_REG_CNTRL1_ADDR, - HTS221_ENABLE_MASK, true); - + err = regmap_update_bits(hw->regmap, HTS221_REG_CNTRL1_ADDR, + HTS221_ENABLE_MASK, + FIELD_PREP(HTS221_ENABLE_MASK, + true)); return err; } diff --git a/drivers/iio/humidity/hts221_i2c.c b/drivers/iio/humidity/hts221_i2c.c index 2c97350a0f76..b5b3f408a658 100644 --- a/drivers/iio/humidity/hts221_i2c.c +++ b/drivers/iio/humidity/hts221_i2c.c @@ -13,61 +13,33 @@ #include #include #include -#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); -} +#include -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; +#include "hts221.h" - return i2c_transfer(client->adapter, &msg, 1); -} +#define HTS221_I2C_AUTO_INCREMENT BIT(7) -static const struct hts221_transfer_function hts221_transfer_fn = { - .read = hts221_i2c_read, - .write = hts221_i2c_write, +static const struct regmap_config hts221_i2c_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .write_flag_mask = HTS221_I2C_AUTO_INCREMENT, + .read_flag_mask = HTS221_I2C_AUTO_INCREMENT, }; static int hts221_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id) { + struct regmap *regmap; + + regmap = devm_regmap_init_i2c(client, &hts221_i2c_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&client->dev, "Failed to register i2c regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + return hts221_probe(&client->dev, client->irq, - client->name, &hts221_transfer_fn); + client->name, regmap); } static const struct acpi_device_id hts221_acpi_match[] = { diff --git a/drivers/iio/humidity/hts221_spi.c b/drivers/iio/humidity/hts221_spi.c index 55b29b53b9d1..9c005f037026 100644 --- a/drivers/iio/humidity/hts221_spi.c +++ b/drivers/iio/humidity/hts221_spi.c @@ -12,76 +12,33 @@ #include #include #include -#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; +#include - if (len > 1) - addr |= SPI_AUTO_INCREMENT; - hw->tb.tx_buf[0] = addr; - memcpy(&hw->tb.tx_buf[1], data, len); +#include "hts221.h" - return spi_sync_transfer(spi, &xfers, 1); -} +#define HTS221_SPI_READ BIT(7) +#define HTS221_SPI_AUTO_INCREMENT BIT(6) -static const struct hts221_transfer_function hts221_transfer_fn = { - .read = hts221_spi_read, - .write = hts221_spi_write, +static const struct regmap_config hts221_spi_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .write_flag_mask = HTS221_SPI_AUTO_INCREMENT, + .read_flag_mask = HTS221_SPI_READ | HTS221_SPI_AUTO_INCREMENT, }; static int hts221_spi_probe(struct spi_device *spi) { + struct regmap *regmap; + + regmap = devm_regmap_init_spi(spi, &hts221_spi_regmap_config); + if (IS_ERR(regmap)) { + dev_err(&spi->dev, "Failed to register spi regmap %d\n", + (int)PTR_ERR(regmap)); + return PTR_ERR(regmap); + } + return hts221_probe(&spi->dev, spi->irq, - spi->modalias, &hts221_transfer_fn); + spi->modalias, regmap); } static const struct of_device_id hts221_spi_of_match[] = { -- cgit v1.2.3 From 56154dac338a96c37713b2320d57b76d624a245c Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Mon, 8 Jan 2018 23:12:31 +0100 Subject: iio: humidity: hts221: remove unnecessary get_unaligned_le16() Remove unnecessary unaligned access routine in hts221_read_oneshot() and the related include Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/hts221_core.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/hts221_core.c b/drivers/iio/humidity/hts221_core.c index fb74a22ef3f5..166946d4978d 100644 --- a/drivers/iio/humidity/hts221_core.c +++ b/drivers/iio/humidity/hts221_core.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include @@ -406,7 +405,7 @@ static int hts221_get_sensor_offset(struct hts221_hw *hw, static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val) { - u8 data[HTS221_DATA_SIZE]; + __le16 data; int err; err = hts221_set_enable(hw, true); @@ -415,13 +414,13 @@ static int hts221_read_oneshot(struct hts221_hw *hw, u8 addr, int *val) msleep(50); - err = regmap_bulk_read(hw->regmap, addr, data, sizeof(data)); + err = regmap_bulk_read(hw->regmap, addr, &data, sizeof(data)); if (err < 0) return err; hts221_set_enable(hw, false); - *val = (s16)get_unaligned_le16(data); + *val = (s16)le16_to_cpu(data); return IIO_VAL_INT; } -- cgit v1.2.3 From 2a7fa90a9aef19ee6a959356be53bbd6ed851304 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Wed, 17 Jan 2018 11:30:00 +0000 Subject: iio: ep93xx: remove redundant return value check of platform_get_resource() Remove unneeded error handling on the result of a call to platform_get_resource() when the value is passed to devm_ioremap_resource(). Signed-off-by: Wei Yongjun Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ep93xx_adc.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ep93xx_adc.c b/drivers/iio/adc/ep93xx_adc.c index 81c901507ad2..5036c392cb20 100644 --- a/drivers/iio/adc/ep93xx_adc.c +++ b/drivers/iio/adc/ep93xx_adc.c @@ -167,10 +167,6 @@ static int ep93xx_adc_probe(struct platform_device *pdev) priv = iio_priv(iiodev); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - dev_err(&pdev->dev, "Cannot obtain memory resource\n"); - return -ENXIO; - } priv->base = devm_ioremap_resource(&pdev->dev, res); if (IS_ERR(priv->base)) { dev_err(&pdev->dev, "Cannot map memory resource\n"); -- cgit v1.2.3 From 0659ecb5dfc10e4bb771d72e8b1b5c429f612082 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 15 Jan 2018 11:33:35 +0100 Subject: iio: adc: axp20x_adc: put ADC rate setting in a per-variant function To prepare for a new comer that set a different register with different values, move rate setting in a function that is specific to each AXP variant. Signed-off-by: Quentin Schulz Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp20x_adc.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index a30a97245e91..3fc1b06ae6b8 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -470,14 +470,18 @@ static const struct iio_info axp22x_adc_iio_info = { .read_raw = axp22x_read_raw, }; -static int axp20x_adc_rate(int rate) +static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate) { - return AXP20X_ADC_RATE_HZ(rate); + return regmap_update_bits(info->regmap, AXP20X_ADC_RATE, + AXP20X_ADC_RATE_MASK, + AXP20X_ADC_RATE_HZ(rate)); } -static int axp22x_adc_rate(int rate) +static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate) { - return AXP22X_ADC_RATE_HZ(rate); + return regmap_update_bits(info->regmap, AXP20X_ADC_RATE, + AXP20X_ADC_RATE_MASK, + AXP22X_ADC_RATE_HZ(rate)); } struct axp_data { @@ -485,7 +489,8 @@ struct axp_data { int num_channels; struct iio_chan_spec const *channels; unsigned long adc_en1_mask; - int (*adc_rate)(int rate); + int (*adc_rate)(struct axp20x_adc_iio *info, + int rate); bool adc_en2; struct iio_map *maps; }; @@ -554,8 +559,7 @@ static int axp20x_probe(struct platform_device *pdev) AXP20X_ADC_EN2_MASK, AXP20X_ADC_EN2_MASK); /* Configure ADCs rate */ - regmap_update_bits(info->regmap, AXP20X_ADC_RATE, AXP20X_ADC_RATE_MASK, - info->data->adc_rate(100)); + info->data->adc_rate(info, 100); ret = iio_map_array_register(indio_dev, info->data->maps); if (ret < 0) { -- cgit v1.2.3 From 359163d786e2b05f3e3bd2db7f1629d7150d6dd2 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 15 Jan 2018 11:33:37 +0100 Subject: iio: adc: axp20x_adc: make it possible to probe from DT To prepare for a future patch that will add a DT node for the ADC, make axp20x_adc able to probe from DT and get the per-variant data from of_device_id.data since platform_device_id.driver_data won't be set when probing by DT. Leave the ability to probe via platform for driver compatibility with old DTs. Signed-off-by: Quentin Schulz Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp20x_adc.c | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 3fc1b06ae6b8..39680539a701 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -515,6 +515,13 @@ static const struct axp_data axp22x_data = { .maps = axp22x_maps, }; +static const struct of_device_id axp20x_adc_of_match[] = { + { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, }, + { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, axp20x_adc_of_match); + static const struct platform_device_id axp20x_adc_id_match[] = { { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, }, { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, }, @@ -543,7 +550,16 @@ static int axp20x_probe(struct platform_device *pdev) indio_dev->dev.of_node = pdev->dev.of_node; indio_dev->modes = INDIO_DIRECT_MODE; - info->data = (struct axp_data *)platform_get_device_id(pdev)->driver_data; + if (!pdev->dev.of_node) { + const struct platform_device_id *id; + + id = platform_get_device_id(pdev); + info->data = (struct axp_data *)id->driver_data; + } else { + struct device *dev = &pdev->dev; + + info->data = (struct axp_data *)of_device_get_match_data(dev); + } indio_dev->name = platform_get_device_id(pdev)->name; indio_dev->info = info->data->iio_info; @@ -606,6 +622,7 @@ static int axp20x_remove(struct platform_device *pdev) static struct platform_driver axp20x_adc_driver = { .driver = { .name = "axp20x-adc", + .of_match_table = of_match_ptr(axp20x_adc_of_match), }, .id_table = axp20x_adc_id_match, .probe = axp20x_probe, -- cgit v1.2.3 From 1a3f6755649dd419d9e01cbc38e116e2c70acb73 Mon Sep 17 00:00:00 2001 From: Quentin Schulz Date: Mon, 15 Jan 2018 11:33:41 +0100 Subject: iio: adc: axp20x_adc: add support for AXP813 ADC The X-Powers AXP813 PMIC is really close to what is already done for AXP20X/AXP22X. There are two pairs of bits to set the rate (one for Voltage and Current measurements and one for TS/GPIO0 voltage measurements) instead of one. The register to set the ADC rates is different from the one for AXP20X/AXP22X. GPIO0 can be used as an ADC (measuring Volts) unlike for AXP22X. The scales to apply to the different inputs are unlike the ones from AXP20X and AXP22X. Signed-off-by: Quentin Schulz Acked-by: Jonathan Cameron Signed-off-by: Jonathan Cameron --- drivers/iio/adc/axp20x_adc.c | 123 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/axp20x.h | 2 + 2 files changed, 125 insertions(+) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/axp20x_adc.c b/drivers/iio/adc/axp20x_adc.c index 39680539a701..7cdb8bc8cde6 100644 --- a/drivers/iio/adc/axp20x_adc.c +++ b/drivers/iio/adc/axp20x_adc.c @@ -35,8 +35,13 @@ #define AXP20X_GPIO10_IN_RANGE_GPIO1_VAL(x) (((x) & BIT(0)) << 1) #define AXP20X_ADC_RATE_MASK GENMASK(7, 6) +#define AXP813_V_I_ADC_RATE_MASK GENMASK(5, 4) +#define AXP813_ADC_RATE_MASK (AXP20X_ADC_RATE_MASK | AXP813_V_I_ADC_RATE_MASK) #define AXP20X_ADC_RATE_HZ(x) ((ilog2((x) / 25) << 6) & AXP20X_ADC_RATE_MASK) #define AXP22X_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 6) & AXP20X_ADC_RATE_MASK) +#define AXP813_TS_GPIO0_ADC_RATE_HZ(x) AXP20X_ADC_RATE_HZ(x) +#define AXP813_V_I_ADC_RATE_HZ(x) ((ilog2((x) / 100) << 4) & AXP813_V_I_ADC_RATE_MASK) +#define AXP813_ADC_RATE_HZ(x) (AXP20X_ADC_RATE_HZ(x) | AXP813_V_I_ADC_RATE_HZ(x)) #define AXP20X_ADC_CHANNEL(_channel, _name, _type, _reg) \ { \ @@ -95,6 +100,12 @@ enum axp22x_adc_channel_i { AXP22X_BATT_DISCHRG_I, }; +enum axp813_adc_channel_v { + AXP813_TS_IN = 0, + AXP813_GPIO0_V, + AXP813_BATT_V, +}; + static struct iio_map axp20x_maps[] = { { .consumer_dev_name = "axp20x-usb-power-supply", @@ -197,6 +208,25 @@ static const struct iio_chan_spec axp22x_adc_channels[] = { AXP20X_BATT_DISCHRG_I_H), }; +static const struct iio_chan_spec axp813_adc_channels[] = { + { + .type = IIO_TEMP, + .address = AXP22X_PMIC_TEMP_H, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_OFFSET), + .datasheet_name = "pmic_temp", + }, + AXP20X_ADC_CHANNEL(AXP813_GPIO0_V, "gpio0_v", IIO_VOLTAGE, + AXP288_GP_ADC_H), + AXP20X_ADC_CHANNEL(AXP813_BATT_V, "batt_v", IIO_VOLTAGE, + AXP20X_BATT_V_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_CHRG_I, "batt_chrg_i", IIO_CURRENT, + AXP20X_BATT_CHRG_I_H), + AXP20X_ADC_CHANNEL(AXP22X_BATT_DISCHRG_I, "batt_dischrg_i", IIO_CURRENT, + AXP20X_BATT_DISCHRG_I_H), +}; + static int axp20x_adc_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int *val) { @@ -243,6 +273,18 @@ static int axp22x_adc_raw(struct iio_dev *indio_dev, return IIO_VAL_INT; } +static int axp813_adc_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val) +{ + struct axp20x_adc_iio *info = iio_priv(indio_dev); + + *val = axp20x_read_variable_width(info->regmap, chan->address, 12); + if (*val < 0) + return *val; + + return IIO_VAL_INT; +} + static int axp20x_adc_scale_voltage(int channel, int *val, int *val2) { switch (channel) { @@ -273,6 +315,24 @@ static int axp20x_adc_scale_voltage(int channel, int *val, int *val2) } } +static int axp813_adc_scale_voltage(int channel, int *val, int *val2) +{ + switch (channel) { + case AXP813_GPIO0_V: + *val = 0; + *val2 = 800000; + return IIO_VAL_INT_PLUS_MICRO; + + case AXP813_BATT_V: + *val = 1; + *val2 = 100000; + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + static int axp20x_adc_scale_current(int channel, int *val, int *val2) { switch (channel) { @@ -342,6 +402,26 @@ static int axp22x_adc_scale(struct iio_chan_spec const *chan, int *val, } } +static int axp813_adc_scale(struct iio_chan_spec const *chan, int *val, + int *val2) +{ + switch (chan->type) { + case IIO_VOLTAGE: + return axp813_adc_scale_voltage(chan->channel, val, val2); + + case IIO_CURRENT: + *val = 1; + return IIO_VAL_INT; + + case IIO_TEMP: + *val = 100; + return IIO_VAL_INT; + + default: + return -EINVAL; + } +} + static int axp20x_adc_offset_voltage(struct iio_dev *indio_dev, int channel, int *val) { @@ -425,6 +505,26 @@ static int axp22x_read_raw(struct iio_dev *indio_dev, } } +static int axp813_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_OFFSET: + *val = -2667; + return IIO_VAL_INT; + + case IIO_CHAN_INFO_SCALE: + return axp813_adc_scale(chan, val, val2); + + case IIO_CHAN_INFO_RAW: + return axp813_adc_raw(indio_dev, chan, val); + + default: + return -EINVAL; + } +} + static int axp20x_write_raw(struct iio_dev *indio_dev, struct iio_chan_spec const *chan, int val, int val2, long mask) @@ -470,6 +570,10 @@ static const struct iio_info axp22x_adc_iio_info = { .read_raw = axp22x_read_raw, }; +static const struct iio_info axp813_adc_iio_info = { + .read_raw = axp813_read_raw, +}; + static int axp20x_adc_rate(struct axp20x_adc_iio *info, int rate) { return regmap_update_bits(info->regmap, AXP20X_ADC_RATE, @@ -484,6 +588,13 @@ static int axp22x_adc_rate(struct axp20x_adc_iio *info, int rate) AXP22X_ADC_RATE_HZ(rate)); } +static int axp813_adc_rate(struct axp20x_adc_iio *info, int rate) +{ + return regmap_update_bits(info->regmap, AXP813_ADC_RATE, + AXP813_ADC_RATE_MASK, + AXP813_ADC_RATE_HZ(rate)); +} + struct axp_data { const struct iio_info *iio_info; int num_channels; @@ -515,9 +626,20 @@ static const struct axp_data axp22x_data = { .maps = axp22x_maps, }; +static const struct axp_data axp813_data = { + .iio_info = &axp813_adc_iio_info, + .num_channels = ARRAY_SIZE(axp813_adc_channels), + .channels = axp813_adc_channels, + .adc_en1_mask = AXP22X_ADC_EN1_MASK, + .adc_rate = axp813_adc_rate, + .adc_en2 = false, + .maps = axp22x_maps, +}; + static const struct of_device_id axp20x_adc_of_match[] = { { .compatible = "x-powers,axp209-adc", .data = (void *)&axp20x_data, }, { .compatible = "x-powers,axp221-adc", .data = (void *)&axp22x_data, }, + { .compatible = "x-powers,axp813-adc", .data = (void *)&axp813_data, }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, axp20x_adc_of_match); @@ -525,6 +647,7 @@ MODULE_DEVICE_TABLE(of, axp20x_adc_of_match); static const struct platform_device_id axp20x_adc_id_match[] = { { .name = "axp20x-adc", .driver_data = (kernel_ulong_t)&axp20x_data, }, { .name = "axp22x-adc", .driver_data = (kernel_ulong_t)&axp22x_data, }, + { .name = "axp813-adc", .driver_data = (kernel_ulong_t)&axp813_data, }, { /* sentinel */ }, }; MODULE_DEVICE_TABLE(platform, axp20x_adc_id_match); diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index 78dc85365c4f..ff95414c8316 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -266,6 +266,8 @@ enum axp20x_variants { #define AXP288_RT_BATT_V_H 0xa0 #define AXP288_RT_BATT_V_L 0xa1 +#define AXP813_ADC_RATE 0x85 + /* Fuel Gauge */ #define AXP288_FG_RDC1_REG 0xba #define AXP288_FG_RDC0_REG 0xbb -- cgit v1.2.3 From ea9170e57689082fabc5787521774533bbd4a5a9 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 14 Jan 2018 21:11:05 -0800 Subject: iio/adc: depend on SYSFS instead of selecting it Drivers should not 'select' a subsystem. Instead they should depend on it. If the subsystem is disabled, the user probably did that for a purpose and one driver shouldn't be changing that. This also makes all IIO drivers consistent w.r.t depending on SYSFS instead of selecting it. Signed-off-by: Randy Dunlap Cc: Jonathan Cameron Cc: linux-iio@vger.kernel.org Signed-off-by: Jonathan Cameron --- drivers/iio/adc/Kconfig | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig index 47492c936747..10f3ede91d3a 100644 --- a/drivers/iio/adc/Kconfig +++ b/drivers/iio/adc/Kconfig @@ -144,10 +144,9 @@ config ASPEED_ADC config AT91_ADC tristate "Atmel AT91 ADC" depends on ARCH_AT91 - depends on INPUT + depends on INPUT && SYSFS select IIO_BUFFER select IIO_TRIGGERED_BUFFER - select SYSFS help Say yes here to build support for Atmel AT91 ADC. -- cgit v1.2.3 From 4d2b6d8c0ae831ba91608f605ce25171bd2d4a4b Mon Sep 17 00:00:00 2001 From: Milan Stevanovic Date: Sun, 14 Jan 2018 21:32:39 +0100 Subject: iio: adc: driver for ti adc081s/adc101s/adc121s Add Linux device driver for TI single-channel CMOS 8/10/12-bit analog-to-digital converter with a high-speed serial interface. Signed-off-by: Milan Stevanovic Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7476.c | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index b7706bf10ffe..0ea0f9066a53 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -1,5 +1,6 @@ /* - * AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver + * Analog Devices AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver + * TI ADC081S/ADC101S/ADC121S 8/10/12-bit SPI ADC driver * * Copyright 2010 Analog Devices Inc. * @@ -56,6 +57,9 @@ enum ad7476_supported_device_ids { ID_AD7468, ID_AD7495, ID_AD7940, + ID_ADC081S, + ID_ADC101S, + ID_ADC121S, }; static irqreturn_t ad7476_trigger_handler(int irq, void *p) @@ -147,6 +151,8 @@ static int ad7476_read_raw(struct iio_dev *indio_dev, }, \ } +#define ADC081S_CHAN(bits) _AD7476_CHAN((bits), 12 - (bits), \ + BIT(IIO_CHAN_INFO_RAW)) #define AD7476_CHAN(bits) _AD7476_CHAN((bits), 13 - (bits), \ BIT(IIO_CHAN_INFO_RAW)) #define AD7940_CHAN(bits) _AD7476_CHAN((bits), 15 - (bits), \ @@ -192,6 +198,18 @@ static const struct ad7476_chip_info ad7476_chip_info_tbl[] = { .channel[0] = AD7940_CHAN(14), .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), }, + [ID_ADC081S] = { + .channel[0] = ADC081S_CHAN(8), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_ADC101S] = { + .channel[0] = ADC081S_CHAN(10), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, + [ID_ADC121S] = { + .channel[0] = ADC081S_CHAN(12), + .channel[1] = IIO_CHAN_SOFT_TIMESTAMP(1), + }, }; static const struct iio_info ad7476_info = { @@ -294,6 +312,9 @@ static const struct spi_device_id ad7476_id[] = { {"ad7910", ID_AD7467}, {"ad7920", ID_AD7466}, {"ad7940", ID_AD7940}, + {"adc081s", ID_ADC081S}, + {"adc101s", ID_ADC101S}, + {"adc121s", ID_ADC121S}, {} }; MODULE_DEVICE_TABLE(spi, ad7476_id); -- cgit v1.2.3 From 54033f19d8f51f75ae263aefb3893f5708cba88b Mon Sep 17 00:00:00 2001 From: Milan Stevanovic Date: Sun, 14 Jan 2018 21:32:40 +0100 Subject: iio: adc: change license description Using an SPDX tag. Remove a license notice to keep the whole purpose of using an SPDx id. Signed-off-by: Milan Stevanovic Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7476.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ad7476.c b/drivers/iio/adc/ad7476.c index 0ea0f9066a53..fbaae47746a8 100644 --- a/drivers/iio/adc/ad7476.c +++ b/drivers/iio/adc/ad7476.c @@ -1,10 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Analog Devices AD7466/7/8 AD7476/5/7/8 (A) SPI ADC driver * TI ADC081S/ADC101S/ADC121S 8/10/12-bit SPI ADC driver * * Copyright 2010 Analog Devices Inc. - * - * Licensed under the GPL-2 or later. */ #include -- cgit v1.2.3 From 213451076bd370e55a70ff07f6575b1451ba1a9f Mon Sep 17 00:00:00 2001 From: Lorenzo Bianconi Date: Sat, 13 Jan 2018 18:57:56 +0100 Subject: iio: imu: st_lsm6dsx: add hw timestamp support Introduce hw timestamp support instead of compute sample timestamps according to interrupt rate and configured watermark. LSM6DSx based devices are able to queue in hw FIFO the time reference of data sampling Signed-off-by: Lorenzo Bianconi Signed-off-by: Jonathan Cameron --- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h | 29 +++-- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c | 161 ++++++++++++++++--------- drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c | 104 +++++++++++++++- 3 files changed, 224 insertions(+), 70 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h index 8fdd723afa05..a3cc7cd97026 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx.h @@ -27,7 +27,7 @@ enum st_lsm6dsx_hw_id { ST_LSM6DSX_MAX_ID, }; -#define ST_LSM6DSX_BUFF_SIZE 256 +#define ST_LSM6DSX_BUFF_SIZE 400 #define ST_LSM6DSX_CHAN_SIZE 2 #define ST_LSM6DSX_SAMPLE_SIZE 6 #define ST_LSM6DSX_MAX_WORD_LEN ((32 / ST_LSM6DSX_SAMPLE_SIZE) * \ @@ -57,6 +57,20 @@ struct st_lsm6dsx_fifo_ops { u8 th_wl; }; +/** + * struct st_lsm6dsx_hw_ts_settings - ST IMU hw timer settings + * @timer_en: Hw timer enable register info (addr + mask). + * @hr_timer: Hw timer resolution register info (addr + mask). + * @fifo_en: Hw timer FIFO enable register info (addr + mask). + * @decimator: Hw timer FIFO decimator register info (addr + mask). + */ +struct st_lsm6dsx_hw_ts_settings { + struct st_lsm6dsx_reg timer_en; + struct st_lsm6dsx_reg hr_timer; + struct st_lsm6dsx_reg fifo_en; + struct st_lsm6dsx_reg decimator; +}; + /** * struct st_lsm6dsx_settings - ST IMU sensor settings * @wai: Sensor WhoAmI default value. @@ -64,6 +78,7 @@ struct st_lsm6dsx_fifo_ops { * @id: List of hw id supported by the driver configuration. * @decimator: List of decimator register info (addr + mask). * @fifo_ops: Sensor hw FIFO parameters. + * @ts_settings: Hw timer related settings. */ struct st_lsm6dsx_settings { u8 wai; @@ -71,6 +86,7 @@ struct st_lsm6dsx_settings { enum st_lsm6dsx_hw_id id[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_reg decimator[ST_LSM6DSX_MAX_ID]; struct st_lsm6dsx_fifo_ops fifo_ops; + struct st_lsm6dsx_hw_ts_settings ts_settings; }; enum st_lsm6dsx_sensor_id { @@ -94,8 +110,7 @@ enum st_lsm6dsx_fifo_mode { * @watermark: Sensor watermark level. * @sip: Number of samples in a given pattern. * @decimator: FIFO decimation factor. - * @delta_ts: Delta time between two consecutive interrupts. - * @ts: Latest timestamp from the interrupt handler. + * @ts_ref: Sensor timestamp reference for hw one. */ struct st_lsm6dsx_sensor { char name[32]; @@ -108,9 +123,7 @@ struct st_lsm6dsx_sensor { u16 watermark; u8 sip; u8 decimator; - - s64 delta_ts; - s64 ts; + s64 ts_ref; }; /** @@ -122,7 +135,8 @@ struct st_lsm6dsx_sensor { * @conf_lock: Mutex to prevent concurrent FIFO configuration update. * @fifo_mode: FIFO operating mode supported by the device. * @enable_mask: Enabled sensor bitmask. - * @sip: Total number of samples (acc/gyro) in a given pattern. + * @ts_sip: Total number of timestamp samples in a given pattern. + * @sip: Total number of samples (acc/gyro/ts) in a given pattern. * @buff: Device read buffer. * @iio_devs: Pointers to acc/gyro iio_dev instances. * @settings: Pointer to the specific sensor settings in use. @@ -137,6 +151,7 @@ struct st_lsm6dsx_hw { enum st_lsm6dsx_fifo_mode fifo_mode; u8 enable_mask; + u8 ts_sip; u8 sip; u8 *buff; diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c index 1d6aa9b1a4cf..1045e025e92b 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_buffer.c @@ -46,9 +46,13 @@ #define ST_LSM6DSX_FIFO_ODR_MASK GENMASK(6, 3) #define ST_LSM6DSX_FIFO_EMPTY_MASK BIT(12) #define ST_LSM6DSX_REG_FIFO_OUTL_ADDR 0x3e +#define ST_LSM6DSX_REG_TS_RESET_ADDR 0x42 #define ST_LSM6DSX_MAX_FIFO_ODR_VAL 0x08 +#define ST_LSM6DSX_TS_SENSITIVITY 25000UL /* 25us */ +#define ST_LSM6DSX_TS_RESET_VAL 0xaa + struct st_lsm6dsx_decimator_entry { u8 decimator; u8 val; @@ -98,9 +102,10 @@ static void st_lsm6dsx_get_max_min_odr(struct st_lsm6dsx_hw *hw, static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) { + u16 max_odr, min_odr, sip = 0, ts_sip = 0; + const struct st_lsm6dsx_reg *ts_dec_reg; struct st_lsm6dsx_sensor *sensor; - u16 max_odr, min_odr, sip = 0; - int err, i; + int err = 0, i; u8 data; st_lsm6dsx_get_max_min_odr(hw, &max_odr, &min_odr); @@ -119,6 +124,7 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) sensor->decimator = 0; data = 0; } + ts_sip = max_t(u16, ts_sip, sensor->sip); dec_reg = &hw->settings->decimator[sensor->id]; if (dec_reg->addr) { @@ -131,9 +137,23 @@ static int st_lsm6dsx_update_decimators(struct st_lsm6dsx_hw *hw) } sip += sensor->sip; } - hw->sip = sip; + hw->sip = sip + ts_sip; + hw->ts_sip = ts_sip; - return 0; + /* + * update hw ts decimator if necessary. Decimator for hw timestamp + * is always 1 or 0 in order to have a ts sample for each data + * sample in FIFO + */ + ts_dec_reg = &hw->settings->ts_settings.decimator; + if (ts_dec_reg->addr) { + int val, ts_dec = !!hw->ts_sip; + + val = ST_LSM6DSX_SHIFT_VAL(ts_dec, ts_dec_reg->mask); + err = regmap_update_bits(hw->regmap, ts_dec_reg->addr, + ts_dec_reg->mask, val); + } + return err; } int st_lsm6dsx_set_fifo_mode(struct st_lsm6dsx_hw *hw, @@ -208,6 +228,28 @@ int st_lsm6dsx_update_watermark(struct st_lsm6dsx_sensor *sensor, u16 watermark) &wdata, sizeof(wdata)); } +static int st_lsm6dsx_reset_hw_ts(struct st_lsm6dsx_hw *hw) +{ + struct st_lsm6dsx_sensor *sensor; + int i, err; + + /* reset hw ts counter */ + err = regmap_write(hw->regmap, ST_LSM6DSX_REG_TS_RESET_ADDR, + ST_LSM6DSX_TS_RESET_VAL); + if (err < 0) + return err; + + for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { + sensor = iio_priv(hw->iio_devs[i]); + /* + * store enable buffer timestamp as reference for + * hw timestamp + */ + sensor->ts_ref = iio_get_time_ns(hw->iio_devs[i]); + } + return 0; +} + /* * Set max bulk read to ST_LSM6DSX_MAX_WORD_LEN in order to avoid * a kmalloc for each bus access @@ -231,6 +273,8 @@ static inline int st_lsm6dsx_read_block(struct st_lsm6dsx_hw *hw, u8 *data, return 0; } +#define ST_LSM6DSX_IIO_BUFF_SIZE (ALIGN(ST_LSM6DSX_SAMPLE_SIZE, \ + sizeof(s64)) + sizeof(s64)) /** * st_lsm6dsx_read_fifo() - LSM6DS3-LSM6DS3H-LSM6DSL-LSM6DSM read FIFO routine * @hw: Pointer to instance of struct st_lsm6dsx_hw. @@ -243,11 +287,13 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) { u16 fifo_len, pattern_len = hw->sip * ST_LSM6DSX_SAMPLE_SIZE; u16 fifo_diff_mask = hw->settings->fifo_ops.fifo_diff.mask; - int err, acc_sip, gyro_sip, read_len, samples, offset; + int err, acc_sip, gyro_sip, ts_sip, read_len, offset; struct st_lsm6dsx_sensor *acc_sensor, *gyro_sensor; - s64 acc_ts, acc_delta_ts, gyro_ts, gyro_delta_ts; - u8 iio_buff[ALIGN(ST_LSM6DSX_SAMPLE_SIZE, sizeof(s64)) + sizeof(s64)]; + u8 gyro_buff[ST_LSM6DSX_IIO_BUFF_SIZE]; + u8 acc_buff[ST_LSM6DSX_IIO_BUFF_SIZE]; + bool reset_ts = false; __le16 fifo_status; + s64 ts = 0; err = regmap_bulk_read(hw->regmap, hw->settings->fifo_ops.fifo_diff.addr, @@ -260,23 +306,10 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) fifo_len = (le16_to_cpu(fifo_status) & fifo_diff_mask) * ST_LSM6DSX_CHAN_SIZE; - samples = fifo_len / ST_LSM6DSX_SAMPLE_SIZE; fifo_len = (fifo_len / pattern_len) * pattern_len; - /* - * compute delta timestamp between two consecutive samples - * in order to estimate queueing time of data generated - * by the sensor - */ acc_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_ACC]); - acc_ts = acc_sensor->ts - acc_sensor->delta_ts; - acc_delta_ts = div_s64(acc_sensor->delta_ts * acc_sensor->decimator, - samples); - gyro_sensor = iio_priv(hw->iio_devs[ST_LSM6DSX_ID_GYRO]); - gyro_ts = gyro_sensor->ts - gyro_sensor->delta_ts; - gyro_delta_ts = div_s64(gyro_sensor->delta_ts * gyro_sensor->decimator, - samples); for (read_len = 0; read_len < fifo_len; read_len += pattern_len) { err = st_lsm6dsx_read_block(hw, hw->buff, pattern_len); @@ -287,7 +320,7 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) * Data are written to the FIFO with a specific pattern * depending on the configured ODRs. The first sequence of data * stored in FIFO contains the data of all enabled sensors - * (e.g. Gx, Gy, Gz, Ax, Ay, Az), then data are repeated + * (e.g. Gx, Gy, Gz, Ax, Ay, Az, Ts), then data are repeated * depending on the value of the decimation factor set for each * sensor. * @@ -296,35 +329,65 @@ static int st_lsm6dsx_read_fifo(struct st_lsm6dsx_hw *hw) * - gyroscope ODR = 208Hz, accelerometer ODR = 104Hz * Since the gyroscope ODR is twice the accelerometer one, the * following pattern is repeated every 9 samples: - * - Gx, Gy, Gz, Ax, Ay, Az, Gx, Gy, Gz + * - Gx, Gy, Gz, Ax, Ay, Az, Ts, Gx, Gy, Gz, Ts, Gx, .. */ gyro_sip = gyro_sensor->sip; acc_sip = acc_sensor->sip; + ts_sip = hw->ts_sip; offset = 0; while (acc_sip > 0 || gyro_sip > 0) { - if (gyro_sip-- > 0) { - memcpy(iio_buff, &hw->buff[offset], + if (gyro_sip > 0) { + memcpy(gyro_buff, &hw->buff[offset], ST_LSM6DSX_SAMPLE_SIZE); - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_GYRO], - iio_buff, gyro_ts); offset += ST_LSM6DSX_SAMPLE_SIZE; - gyro_ts += gyro_delta_ts; } - - if (acc_sip-- > 0) { - memcpy(iio_buff, &hw->buff[offset], + if (acc_sip > 0) { + memcpy(acc_buff, &hw->buff[offset], ST_LSM6DSX_SAMPLE_SIZE); - iio_push_to_buffers_with_timestamp( - hw->iio_devs[ST_LSM6DSX_ID_ACC], - iio_buff, acc_ts); offset += ST_LSM6DSX_SAMPLE_SIZE; - acc_ts += acc_delta_ts; } + + if (ts_sip-- > 0) { + u8 data[ST_LSM6DSX_SAMPLE_SIZE]; + + memcpy(data, &hw->buff[offset], sizeof(data)); + /* + * hw timestamp is 3B long and it is stored + * in FIFO using 6B as 4th FIFO data set + * according to this schema: + * B0 = ts[15:8], B1 = ts[23:16], B3 = ts[7:0] + */ + ts = data[1] << 16 | data[0] << 8 | data[3]; + /* + * check if hw timestamp engine is going to + * reset (the sensor generates an interrupt + * to signal the hw timestamp will reset in + * 1.638s) + */ + if (!reset_ts && ts >= 0xff0000) + reset_ts = true; + ts *= ST_LSM6DSX_TS_SENSITIVITY; + + offset += ST_LSM6DSX_SAMPLE_SIZE; + } + + if (gyro_sip-- > 0) + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_GYRO], + gyro_buff, gyro_sensor->ts_ref + ts); + if (acc_sip-- > 0) + iio_push_to_buffers_with_timestamp( + hw->iio_devs[ST_LSM6DSX_ID_ACC], + acc_buff, acc_sensor->ts_ref + ts); } } + if (unlikely(reset_ts)) { + err = st_lsm6dsx_reset_hw_ts(hw); + if (err < 0) + return err; + } return read_len; } @@ -379,15 +442,12 @@ static int st_lsm6dsx_update_fifo(struct iio_dev *iio_dev, bool enable) goto out; if (hw->enable_mask) { - err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT); + /* reset hw ts counter */ + err = st_lsm6dsx_reset_hw_ts(hw); if (err < 0) goto out; - /* - * store enable buffer timestamp as reference to compute - * first delta timestamp - */ - sensor->ts = iio_get_time_ns(iio_dev); + err = st_lsm6dsx_set_fifo_mode(hw, ST_LSM6DSX_FIFO_CONT); } out: @@ -399,25 +459,8 @@ out: static irqreturn_t st_lsm6dsx_handler_irq(int irq, void *private) { struct st_lsm6dsx_hw *hw = private; - struct st_lsm6dsx_sensor *sensor; - int i; - - if (!hw->sip) - return IRQ_NONE; - - for (i = 0; i < ST_LSM6DSX_ID_MAX; i++) { - sensor = iio_priv(hw->iio_devs[i]); - - if (sensor->sip > 0) { - s64 timestamp; - - timestamp = iio_get_time_ns(hw->iio_devs[i]); - sensor->delta_ts = timestamp - sensor->ts; - sensor->ts = timestamp; - } - } - return IRQ_WAKE_THREAD; + return hw->sip > 0 ? IRQ_WAKE_THREAD : IRQ_NONE; } static irqreturn_t st_lsm6dsx_handler_thread(int irq, void *private) diff --git a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c index c2fa3239b9c6..8656d72ef4ee 100644 --- a/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c +++ b/drivers/iio/imu/st_lsm6dsx/st_lsm6dsx_core.c @@ -181,6 +181,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, .th_wl = 3, /* 1LSB = 2B */ }, + .ts_settings = { + .timer_en = { + .addr = 0x58, + .mask = BIT(7), + }, + .hr_timer = { + .addr = 0x5c, + .mask = BIT(4), + }, + .fifo_en = { + .addr = 0x07, + .mask = BIT(7), + }, + .decimator = { + .addr = 0x09, + .mask = GENMASK(5, 3), + }, + }, }, { .wai = 0x69, @@ -209,6 +227,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, .th_wl = 3, /* 1LSB = 2B */ }, + .ts_settings = { + .timer_en = { + .addr = 0x58, + .mask = BIT(7), + }, + .hr_timer = { + .addr = 0x5c, + .mask = BIT(4), + }, + .fifo_en = { + .addr = 0x07, + .mask = BIT(7), + }, + .decimator = { + .addr = 0x09, + .mask = GENMASK(5, 3), + }, + }, }, { .wai = 0x6a, @@ -238,6 +274,24 @@ static const struct st_lsm6dsx_settings st_lsm6dsx_sensor_settings[] = { }, .th_wl = 3, /* 1LSB = 2B */ }, + .ts_settings = { + .timer_en = { + .addr = 0x19, + .mask = BIT(5), + }, + .hr_timer = { + .addr = 0x5c, + .mask = BIT(4), + }, + .fifo_en = { + .addr = 0x07, + .mask = BIT(7), + }, + .decimator = { + .addr = 0x09, + .mask = GENMASK(5, 3), + }, + }, }, }; @@ -630,6 +684,44 @@ static int st_lsm6dsx_get_drdy_reg(struct st_lsm6dsx_hw *hw, u8 *drdy_reg) return err; } +static int st_lsm6dsx_init_hw_timer(struct st_lsm6dsx_hw *hw) +{ + const struct st_lsm6dsx_hw_ts_settings *ts_settings; + int err, val; + + ts_settings = &hw->settings->ts_settings; + /* enable hw timestamp generation if necessary */ + if (ts_settings->timer_en.addr) { + val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->timer_en.mask); + err = regmap_update_bits(hw->regmap, + ts_settings->timer_en.addr, + ts_settings->timer_en.mask, val); + if (err < 0) + return err; + } + + /* enable high resolution for hw ts timer if necessary */ + if (ts_settings->hr_timer.addr) { + val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->hr_timer.mask); + err = regmap_update_bits(hw->regmap, + ts_settings->hr_timer.addr, + ts_settings->hr_timer.mask, val); + if (err < 0) + return err; + } + + /* enable ts queueing in FIFO if necessary */ + if (ts_settings->fifo_en.addr) { + val = ST_LSM6DSX_SHIFT_VAL(1, ts_settings->fifo_en.mask); + err = regmap_update_bits(hw->regmap, + ts_settings->fifo_en.addr, + ts_settings->fifo_en.mask, val); + if (err < 0) + return err; + } + return 0; +} + static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) { u8 drdy_int_reg; @@ -654,10 +746,14 @@ static int st_lsm6dsx_init_device(struct st_lsm6dsx_hw *hw) if (err < 0) return err; - return regmap_update_bits(hw->regmap, drdy_int_reg, - ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, - FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, - 1)); + err = regmap_update_bits(hw->regmap, drdy_int_reg, + ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, + FIELD_PREP(ST_LSM6DSX_REG_FIFO_FTH_IRQ_MASK, + 1)); + if (err < 0) + return err; + + return st_lsm6dsx_init_hw_timer(hw); } static struct iio_dev *st_lsm6dsx_alloc_iiodev(struct st_lsm6dsx_hw *hw, -- cgit v1.2.3 From c87742abfc80b3f80c4f7a546193ad78e4fed9b6 Mon Sep 17 00:00:00 2001 From: Crt Mori Date: Thu, 11 Jan 2018 11:20:23 +0100 Subject: iio: temperature: Adding support for MLX90632 Melexis has just released Infra Red temperature sensor MLX90632 used for contact-less temperature measurement. Driver provides basic functionality for reporting object (and ambient) temperature with support for object emissivity. Signed-off-by: Crt Mori Signed-off-by: Jonathan Cameron --- MAINTAINERS | 7 + drivers/iio/temperature/Kconfig | 12 + drivers/iio/temperature/Makefile | 1 + drivers/iio/temperature/mlx90632.c | 750 +++++++++++++++++++++++++++++++++++++ 4 files changed, 770 insertions(+) create mode 100644 drivers/iio/temperature/mlx90632.c (limited to 'drivers/iio') diff --git a/MAINTAINERS b/MAINTAINERS index 2722afde692f..228d2daf18ff 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8798,6 +8798,13 @@ W: http://www.melexis.com S: Supported F: drivers/iio/temperature/mlx90614.c +MELEXIS MLX90632 DRIVER +M: Crt Mori +L: linux-iio@vger.kernel.org +W: http://www.melexis.com +S: Supported +F: drivers/iio/temperature/mlx90632.c + MELFAS MIP4 TOUCHSCREEN DRIVER M: Sangwon Jee W: http://www.melfas.com diff --git a/drivers/iio/temperature/Kconfig b/drivers/iio/temperature/Kconfig index 5378976d6d27..82e4a62745e2 100644 --- a/drivers/iio/temperature/Kconfig +++ b/drivers/iio/temperature/Kconfig @@ -43,6 +43,18 @@ config MLX90614 This driver can also be built as a module. If so, the module will be called mlx90614. +config MLX90632 + tristate "MLX90632 contact-less infrared sensor with medical accuracy" + depends on I2C + select REGMAP_I2C + help + If you say yes here you get support for the Melexis + MLX90632 contact-less infrared sensor with medical accuracy + connected with I2C. + + This driver can also be built as a module. If so, the module will + be called mlx90632. + config TMP006 tristate "TMP006 infrared thermopile sensor" depends on I2C diff --git a/drivers/iio/temperature/Makefile b/drivers/iio/temperature/Makefile index 34bd9023727b..34a31db0bb63 100644 --- a/drivers/iio/temperature/Makefile +++ b/drivers/iio/temperature/Makefile @@ -6,6 +6,7 @@ obj-$(CONFIG_HID_SENSOR_TEMP) += hid-sensor-temperature.o obj-$(CONFIG_MAXIM_THERMOCOUPLE) += maxim_thermocouple.o obj-$(CONFIG_MLX90614) += mlx90614.o +obj-$(CONFIG_MLX90632) += mlx90632.o obj-$(CONFIG_TMP006) += tmp006.o obj-$(CONFIG_TMP007) += tmp007.o obj-$(CONFIG_TSYS01) += tsys01.o diff --git a/drivers/iio/temperature/mlx90632.c b/drivers/iio/temperature/mlx90632.c new file mode 100644 index 000000000000..d695ab97d27f --- /dev/null +++ b/drivers/iio/temperature/mlx90632.c @@ -0,0 +1,750 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * mlx90632.c - Melexis MLX90632 contactless IR temperature sensor + * + * Copyright (c) 2017 Melexis + * + * Driver for the Melexis MLX90632 I2C 16-bit IR thermopile sensor + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Memory sections addresses */ +#define MLX90632_ADDR_RAM 0x4000 /* Start address of ram */ +#define MLX90632_ADDR_EEPROM 0x2480 /* Start address of user eeprom */ + +/* EEPROM addresses - used at startup */ +#define MLX90632_EE_CTRL 0x24d4 /* Control register initial value */ +#define MLX90632_EE_I2C_ADDR 0x24d5 /* I2C address register initial value */ +#define MLX90632_EE_VERSION 0x240b /* EEPROM version reg address */ +#define MLX90632_EE_P_R 0x240c /* P_R calibration register 32bit */ +#define MLX90632_EE_P_G 0x240e /* P_G calibration register 32bit */ +#define MLX90632_EE_P_T 0x2410 /* P_T calibration register 32bit */ +#define MLX90632_EE_P_O 0x2412 /* P_O calibration register 32bit */ +#define MLX90632_EE_Aa 0x2414 /* Aa calibration register 32bit */ +#define MLX90632_EE_Ab 0x2416 /* Ab calibration register 32bit */ +#define MLX90632_EE_Ba 0x2418 /* Ba calibration register 32bit */ +#define MLX90632_EE_Bb 0x241a /* Bb calibration register 32bit */ +#define MLX90632_EE_Ca 0x241c /* Ca calibration register 32bit */ +#define MLX90632_EE_Cb 0x241e /* Cb calibration register 32bit */ +#define MLX90632_EE_Da 0x2420 /* Da calibration register 32bit */ +#define MLX90632_EE_Db 0x2422 /* Db calibration register 32bit */ +#define MLX90632_EE_Ea 0x2424 /* Ea calibration register 32bit */ +#define MLX90632_EE_Eb 0x2426 /* Eb calibration register 32bit */ +#define MLX90632_EE_Fa 0x2428 /* Fa calibration register 32bit */ +#define MLX90632_EE_Fb 0x242a /* Fb calibration register 32bit */ +#define MLX90632_EE_Ga 0x242c /* Ga calibration register 32bit */ + +#define MLX90632_EE_Gb 0x242e /* Gb calibration register 16bit */ +#define MLX90632_EE_Ka 0x242f /* Ka calibration register 16bit */ + +#define MLX90632_EE_Ha 0x2481 /* Ha customer calib value reg 16bit */ +#define MLX90632_EE_Hb 0x2482 /* Hb customer calib value reg 16bit */ + +/* Register addresses - volatile */ +#define MLX90632_REG_I2C_ADDR 0x3000 /* Chip I2C address register */ + +/* Control register address - volatile */ +#define MLX90632_REG_CONTROL 0x3001 /* Control Register address */ +#define MLX90632_CFG_PWR_MASK GENMASK(2, 1) /* PowerMode Mask */ +/* PowerModes statuses */ +#define MLX90632_PWR_STATUS(ctrl_val) (ctrl_val << 1) +#define MLX90632_PWR_STATUS_HALT MLX90632_PWR_STATUS(0) /* hold */ +#define MLX90632_PWR_STATUS_SLEEP_STEP MLX90632_PWR_STATUS(1) /* sleep step*/ +#define MLX90632_PWR_STATUS_STEP MLX90632_PWR_STATUS(2) /* step */ +#define MLX90632_PWR_STATUS_CONTINUOUS MLX90632_PWR_STATUS(3) /* continuous*/ + +/* Device status register - volatile */ +#define MLX90632_REG_STATUS 0x3fff /* Device status register */ +#define MLX90632_STAT_BUSY BIT(10) /* Device busy indicator */ +#define MLX90632_STAT_EE_BUSY BIT(9) /* EEPROM busy indicator */ +#define MLX90632_STAT_BRST BIT(8) /* Brown out reset indicator */ +#define MLX90632_STAT_CYCLE_POS GENMASK(6, 2) /* Data position */ +#define MLX90632_STAT_DATA_RDY BIT(0) /* Data ready indicator */ + +/* RAM_MEAS address-es for each channel */ +#define MLX90632_RAM_1(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num) +#define MLX90632_RAM_2(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num + 1) +#define MLX90632_RAM_3(meas_num) (MLX90632_ADDR_RAM + 3 * meas_num + 2) + +/* Magic constants */ +#define MLX90632_ID_MEDICAL 0x0105 /* EEPROM DSPv5 Medical device id */ +#define MLX90632_ID_CONSUMER 0x0205 /* EEPROM DSPv5 Consumer device id */ +#define MLX90632_RESET_CMD 0x0006 /* Reset sensor (address or global) */ +#define MLX90632_REF_12 12LL /**< ResCtrlRef value of Ch 1 or Ch 2 */ +#define MLX90632_REF_3 12LL /**< ResCtrlRef value of Channel 3 */ +#define MLX90632_MAX_MEAS_NUM 31 /**< Maximum measurements in list */ +#define MLX90632_SLEEP_DELAY_MS 3000 /**< Autosleep delay */ + +struct mlx90632_data { + struct i2c_client *client; + struct mutex lock; /* Multiple reads for single measurement */ + struct regmap *regmap; + u16 emissivity; +}; + +static const struct regmap_range mlx90632_volatile_reg_range[] = { + regmap_reg_range(MLX90632_REG_I2C_ADDR, MLX90632_REG_CONTROL), + regmap_reg_range(MLX90632_REG_STATUS, MLX90632_REG_STATUS), + regmap_reg_range(MLX90632_RAM_1(0), + MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)), +}; + +static const struct regmap_access_table mlx90632_volatile_regs_tbl = { + .yes_ranges = mlx90632_volatile_reg_range, + .n_yes_ranges = ARRAY_SIZE(mlx90632_volatile_reg_range), +}; + +static const struct regmap_range mlx90632_read_reg_range[] = { + regmap_reg_range(MLX90632_EE_VERSION, MLX90632_EE_Ka), + regmap_reg_range(MLX90632_EE_CTRL, MLX90632_EE_I2C_ADDR), + regmap_reg_range(MLX90632_EE_Ha, MLX90632_EE_Hb), + regmap_reg_range(MLX90632_REG_I2C_ADDR, MLX90632_REG_CONTROL), + regmap_reg_range(MLX90632_REG_STATUS, MLX90632_REG_STATUS), + regmap_reg_range(MLX90632_RAM_1(0), + MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)), +}; + +static const struct regmap_access_table mlx90632_readable_regs_tbl = { + .yes_ranges = mlx90632_read_reg_range, + .n_yes_ranges = ARRAY_SIZE(mlx90632_read_reg_range), +}; + +static const struct regmap_range mlx90632_no_write_reg_range[] = { + regmap_reg_range(MLX90632_EE_VERSION, MLX90632_EE_Ka), + regmap_reg_range(MLX90632_RAM_1(0), + MLX90632_RAM_3(MLX90632_MAX_MEAS_NUM)), +}; + +static const struct regmap_access_table mlx90632_writeable_regs_tbl = { + .no_ranges = mlx90632_no_write_reg_range, + .n_no_ranges = ARRAY_SIZE(mlx90632_no_write_reg_range), +}; + +static const struct regmap_config mlx90632_regmap = { + .reg_bits = 16, + .val_bits = 16, + + .volatile_table = &mlx90632_volatile_regs_tbl, + .rd_table = &mlx90632_readable_regs_tbl, + .wr_table = &mlx90632_writeable_regs_tbl, + + .use_single_rw = true, + .reg_format_endian = REGMAP_ENDIAN_BIG, + .val_format_endian = REGMAP_ENDIAN_BIG, + .cache_type = REGCACHE_RBTREE, +}; + +static s32 mlx90632_pwr_set_sleep_step(struct regmap *regmap) +{ + return regmap_update_bits(regmap, MLX90632_REG_CONTROL, + MLX90632_CFG_PWR_MASK, + MLX90632_PWR_STATUS_SLEEP_STEP); +} + +static s32 mlx90632_pwr_continuous(struct regmap *regmap) +{ + return regmap_update_bits(regmap, MLX90632_REG_CONTROL, + MLX90632_CFG_PWR_MASK, + MLX90632_PWR_STATUS_CONTINUOUS); +} + +/** + * mlx90632_perform_measurement - Trigger and retrieve current measurement cycle + * @*data: pointer to mlx90632_data object containing regmap information + * + * Perform a measurement and return latest measurement cycle position reported + * by sensor. This is a blocking function for 500ms, as that is default sensor + * refresh rate. + */ +static int mlx90632_perform_measurement(struct mlx90632_data *data) +{ + int ret, tries = 100; + unsigned int reg_status; + + ret = regmap_update_bits(data->regmap, MLX90632_REG_STATUS, + MLX90632_STAT_DATA_RDY, 0); + if (ret < 0) + return ret; + + while (tries-- > 0) { + ret = regmap_read(data->regmap, MLX90632_REG_STATUS, + ®_status); + if (ret < 0) + return ret; + if (reg_status & MLX90632_STAT_DATA_RDY) + break; + usleep_range(10000, 11000); + } + + if (tries < 0) { + dev_err(&data->client->dev, "data not ready"); + return -ETIMEDOUT; + } + + return (reg_status & MLX90632_STAT_CYCLE_POS) >> 2; +} + +static int mlx90632_channel_new_select(int perform_ret, uint8_t *channel_new, + uint8_t *channel_old) +{ + switch (perform_ret) { + case 1: + *channel_new = 1; + *channel_old = 2; + break; + case 2: + *channel_new = 2; + *channel_old = 1; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int mlx90632_read_ambient_raw(struct regmap *regmap, + s16 *ambient_new_raw, s16 *ambient_old_raw) +{ + int ret; + unsigned int read_tmp; + + ret = regmap_read(regmap, MLX90632_RAM_3(1), &read_tmp); + if (ret < 0) + return ret; + *ambient_new_raw = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90632_RAM_3(2), &read_tmp); + if (ret < 0) + return ret; + *ambient_old_raw = (s16)read_tmp; + + return ret; +} + +static int mlx90632_read_object_raw(struct regmap *regmap, + int perform_measurement_ret, + s16 *object_new_raw, s16 *object_old_raw) +{ + int ret; + unsigned int read_tmp; + s16 read; + u8 channel = 0; + u8 channel_old = 0; + + ret = mlx90632_channel_new_select(perform_measurement_ret, &channel, + &channel_old); + if (ret != 0) + return ret; + + ret = regmap_read(regmap, MLX90632_RAM_2(channel), &read_tmp); + if (ret < 0) + return ret; + + read = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90632_RAM_1(channel), &read_tmp); + if (ret < 0) + return ret; + *object_new_raw = (read + (s16)read_tmp) / 2; + + ret = regmap_read(regmap, MLX90632_RAM_2(channel_old), &read_tmp); + if (ret < 0) + return ret; + read = (s16)read_tmp; + + ret = regmap_read(regmap, MLX90632_RAM_1(channel_old), &read_tmp); + if (ret < 0) + return ret; + *object_old_raw = (read + (s16)read_tmp) / 2; + + return ret; +} + +static int mlx90632_read_all_channel(struct mlx90632_data *data, + s16 *ambient_new_raw, s16 *ambient_old_raw, + s16 *object_new_raw, s16 *object_old_raw) +{ + s32 ret, measurement; + + mutex_lock(&data->lock); + measurement = mlx90632_perform_measurement(data); + if (measurement < 0) { + ret = measurement; + goto read_unlock; + } + ret = mlx90632_read_ambient_raw(data->regmap, ambient_new_raw, + ambient_old_raw); + if (ret < 0) + goto read_unlock; + + ret = mlx90632_read_object_raw(data->regmap, measurement, + object_new_raw, object_old_raw); +read_unlock: + mutex_unlock(&data->lock); + return ret; +} + +static int mlx90632_read_ee_register(struct regmap *regmap, u16 reg_lsb, + s32 *reg_value) +{ + s32 ret; + unsigned int read; + u32 value; + + ret = regmap_read(regmap, reg_lsb, &read); + if (ret < 0) + return ret; + + value = read; + + ret = regmap_read(regmap, reg_lsb + 1, &read); + if (ret < 0) + return ret; + + *reg_value = (read << 16) | (value & 0xffff); + + return 0; +} + +static s64 mlx90632_preprocess_temp_amb(s16 ambient_new_raw, + s16 ambient_old_raw, s16 Gb) +{ + s64 VR_Ta, kGb, tmp; + + kGb = ((s64)Gb * 1000LL) >> 10ULL; + VR_Ta = (s64)ambient_old_raw * 1000000LL + + kGb * div64_s64(((s64)ambient_new_raw * 1000LL), + (MLX90632_REF_3)); + tmp = div64_s64( + div64_s64(((s64)ambient_new_raw * 1000000000000LL), + (MLX90632_REF_3)), VR_Ta); + return div64_s64(tmp << 19ULL, 1000LL); +} + +static s64 mlx90632_preprocess_temp_obj(s16 object_new_raw, s16 object_old_raw, + s16 ambient_new_raw, + s16 ambient_old_raw, s16 Ka) +{ + s64 VR_IR, kKa, tmp; + + kKa = ((s64)Ka * 1000LL) >> 10ULL; + VR_IR = (s64)ambient_old_raw * 1000000LL + + kKa * div64_s64(((s64)ambient_new_raw * 1000LL), + (MLX90632_REF_3)); + tmp = div64_s64( + div64_s64(((s64)((object_new_raw + object_old_raw) / 2) + * 1000000000000LL), (MLX90632_REF_12)), + VR_IR); + return div64_s64((tmp << 19ULL), 1000LL); +} + +static s32 mlx90632_calc_temp_ambient(s16 ambient_new_raw, s16 ambient_old_raw, + s32 P_T, s32 P_R, s32 P_G, s32 P_O, + s16 Gb) +{ + s64 Asub, Bsub, Ablock, Bblock, Cblock, AMB, sum; + + AMB = mlx90632_preprocess_temp_amb(ambient_new_raw, ambient_old_raw, + Gb); + Asub = ((s64)P_T * 10000000000LL) >> 44ULL; + Bsub = AMB - (((s64)P_R * 1000LL) >> 8ULL); + Ablock = Asub * (Bsub * Bsub); + Bblock = (div64_s64(Bsub * 10000000LL, P_G)) << 20ULL; + Cblock = ((s64)P_O * 10000000000LL) >> 8ULL; + + sum = div64_s64(Ablock, 1000000LL) + Bblock + Cblock; + + return div64_s64(sum, 10000000LL); +} + +static s32 mlx90632_calc_temp_object_iteration(s32 prev_object_temp, s64 object, + s64 TAdut, s32 Fa, s32 Fb, + s32 Ga, s16 Ha, s16 Hb, + u16 emissivity) +{ + s64 calcedKsTO, calcedKsTA, ir_Alpha, TAdut4, Alpha_corr; + s64 Ha_customer, Hb_customer; + + Ha_customer = ((s64)Ha * 1000000LL) >> 14ULL; + Hb_customer = ((s64)Hb * 100) >> 10ULL; + + calcedKsTO = ((s64)((s64)Ga * (prev_object_temp - 25 * 1000LL) + * 1000LL)) >> 36LL; + calcedKsTA = ((s64)(Fb * (TAdut - 25 * 1000000LL))) >> 36LL; + Alpha_corr = div64_s64((((s64)(Fa * 10000000000LL) >> 46LL) + * Ha_customer), 1000LL); + Alpha_corr *= ((s64)(1 * 1000000LL + calcedKsTO + calcedKsTA)); + Alpha_corr = emissivity * div64_s64(Alpha_corr, 100000LL); + Alpha_corr = div64_s64(Alpha_corr, 1000LL); + ir_Alpha = div64_s64((s64)object * 10000000LL, Alpha_corr); + TAdut4 = (div64_s64(TAdut, 10000LL) + 27315) * + (div64_s64(TAdut, 10000LL) + 27315) * + (div64_s64(TAdut, 10000LL) + 27315) * + (div64_s64(TAdut, 10000LL) + 27315); + + return (int_sqrt64(int_sqrt64(ir_Alpha * 1000000000000LL + TAdut4)) + - 27315 - Hb_customer) * 10; +} + +static s32 mlx90632_calc_temp_object(s64 object, s64 ambient, s32 Ea, s32 Eb, + s32 Fa, s32 Fb, s32 Ga, s16 Ha, s16 Hb, + u16 tmp_emi) +{ + s64 kTA, kTA0, TAdut; + s64 temp = 25000; + s8 i; + + kTA = (Ea * 1000LL) >> 16LL; + kTA0 = (Eb * 1000LL) >> 8LL; + TAdut = div64_s64(((ambient - kTA0) * 1000000LL), kTA) + 25 * 1000000LL; + + /* Iterations of calculation as described in datasheet */ + for (i = 0; i < 5; ++i) { + temp = mlx90632_calc_temp_object_iteration(temp, object, TAdut, + Fa, Fb, Ga, Ha, Hb, + tmp_emi); + } + return temp; +} + +static int mlx90632_calc_object_dsp105(struct mlx90632_data *data, int *val) +{ + s32 ret; + s32 Ea, Eb, Fa, Fb, Ga; + unsigned int read_tmp; + s16 Ha, Hb, Gb, Ka; + s16 ambient_new_raw, ambient_old_raw, object_new_raw, object_old_raw; + s64 object, ambient; + + ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Ea, &Ea); + if (ret < 0) + return ret; + ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Eb, &Eb); + if (ret < 0) + return ret; + ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Fa, &Fa); + if (ret < 0) + return ret; + ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Fb, &Fb); + if (ret < 0) + return ret; + ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_Ga, &Ga); + if (ret < 0) + return ret; + ret = regmap_read(data->regmap, MLX90632_EE_Ha, &read_tmp); + if (ret < 0) + return ret; + Ha = (s16)read_tmp; + ret = regmap_read(data->regmap, MLX90632_EE_Hb, &read_tmp); + if (ret < 0) + return ret; + Hb = (s16)read_tmp; + ret = regmap_read(data->regmap, MLX90632_EE_Gb, &read_tmp); + if (ret < 0) + return ret; + Gb = (s16)read_tmp; + ret = regmap_read(data->regmap, MLX90632_EE_Ka, &read_tmp); + if (ret < 0) + return ret; + Ka = (s16)read_tmp; + + ret = mlx90632_read_all_channel(data, + &ambient_new_raw, &ambient_old_raw, + &object_new_raw, &object_old_raw); + if (ret < 0) + return ret; + + ambient = mlx90632_preprocess_temp_amb(ambient_new_raw, + ambient_old_raw, Gb); + object = mlx90632_preprocess_temp_obj(object_new_raw, + object_old_raw, + ambient_new_raw, + ambient_old_raw, Ka); + + *val = mlx90632_calc_temp_object(object, ambient, Ea, Eb, Fa, Fb, Ga, + Ha, Hb, data->emissivity); + return 0; +} + +static int mlx90632_calc_ambient_dsp105(struct mlx90632_data *data, int *val) +{ + s32 ret; + unsigned int read_tmp; + s32 PT, PR, PG, PO; + s16 Gb; + s16 ambient_new_raw, ambient_old_raw; + + ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_R, &PR); + if (ret < 0) + return ret; + ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_G, &PG); + if (ret < 0) + return ret; + ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_T, &PT); + if (ret < 0) + return ret; + ret = mlx90632_read_ee_register(data->regmap, MLX90632_EE_P_O, &PO); + if (ret < 0) + return ret; + ret = regmap_read(data->regmap, MLX90632_EE_Gb, &read_tmp); + if (ret < 0) + return ret; + Gb = (s16)read_tmp; + + ret = mlx90632_read_ambient_raw(data->regmap, &ambient_new_raw, + &ambient_old_raw); + *val = mlx90632_calc_temp_ambient(ambient_new_raw, ambient_old_raw, + PT, PR, PG, PO, Gb); + return ret; +} + +static int mlx90632_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + struct mlx90632_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (channel->channel2) { + case IIO_MOD_TEMP_AMBIENT: + ret = mlx90632_calc_ambient_dsp105(data, val); + if (ret < 0) + return ret; + return IIO_VAL_INT; + case IIO_MOD_TEMP_OBJECT: + ret = mlx90632_calc_object_dsp105(data, val); + if (ret < 0) + return ret; + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_CALIBEMISSIVITY: + if (data->emissivity == 1000) { + *val = 1; + *val2 = 0; + } else { + *val = 0; + *val2 = data->emissivity * 1000; + } + return IIO_VAL_INT_PLUS_MICRO; + + default: + return -EINVAL; + } +} + +static int mlx90632_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int val, + int val2, long mask) +{ + struct mlx90632_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_CALIBEMISSIVITY: + /* Confirm we are within 0 and 1.0 */ + if (val < 0 || val2 < 0 || val > 1 || + (val == 1 && val2 != 0)) + return -EINVAL; + data->emissivity = val * 1000 + val2 / 1000; + return 0; + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec mlx90632_channels[] = { + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_AMBIENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED), + }, + { + .type = IIO_TEMP, + .modified = 1, + .channel2 = IIO_MOD_TEMP_OBJECT, + .info_mask_separate = BIT(IIO_CHAN_INFO_PROCESSED) | + BIT(IIO_CHAN_INFO_CALIBEMISSIVITY), + }, +}; + +static const struct iio_info mlx90632_info = { + .read_raw = mlx90632_read_raw, + .write_raw = mlx90632_write_raw, +}; + +static int mlx90632_sleep(struct mlx90632_data *data) +{ + regcache_mark_dirty(data->regmap); + + dev_dbg(&data->client->dev, "Requesting sleep"); + return mlx90632_pwr_set_sleep_step(data->regmap); +} + +static int mlx90632_wakeup(struct mlx90632_data *data) +{ + int ret; + + ret = regcache_sync(data->regmap); + if (ret < 0) { + dev_err(&data->client->dev, + "Failed to sync regmap registers: %d\n", ret); + return ret; + } + + dev_dbg(&data->client->dev, "Requesting wake-up\n"); + return mlx90632_pwr_continuous(data->regmap); +} + +static int mlx90632_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct mlx90632_data *mlx90632; + struct regmap *regmap; + int ret; + unsigned int read; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*mlx90632)); + if (!indio_dev) { + dev_err(&client->dev, "Failed to allocate device\n"); + return -ENOMEM; + } + + regmap = devm_regmap_init_i2c(client, &mlx90632_regmap); + if (IS_ERR(regmap)) { + ret = PTR_ERR(regmap); + dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + + mlx90632 = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + mlx90632->client = client; + mlx90632->regmap = regmap; + + mutex_init(&mlx90632->lock); + indio_dev->dev.parent = &client->dev; + indio_dev->name = id->name; + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &mlx90632_info; + indio_dev->channels = mlx90632_channels; + indio_dev->num_channels = ARRAY_SIZE(mlx90632_channels); + + ret = mlx90632_wakeup(mlx90632); + if (ret < 0) { + dev_err(&client->dev, "Wakeup failed: %d\n", ret); + return ret; + } + + ret = regmap_read(mlx90632->regmap, MLX90632_EE_VERSION, &read); + if (ret < 0) { + dev_err(&client->dev, "read of version failed: %d\n", ret); + return ret; + } + if (read == MLX90632_ID_MEDICAL) { + dev_dbg(&client->dev, + "Detected Medical EEPROM calibration %x\n", read); + } else if (read == MLX90632_ID_CONSUMER) { + dev_dbg(&client->dev, + "Detected Consumer EEPROM calibration %x\n", read); + } else { + dev_err(&client->dev, + "EEPROM version mismatch %x (expected %x or %x)\n", + read, MLX90632_ID_CONSUMER, MLX90632_ID_MEDICAL); + return -EPROTONOSUPPORT; + } + + mlx90632->emissivity = 1000; + + pm_runtime_disable(&client->dev); + ret = pm_runtime_set_active(&client->dev); + if (ret < 0) { + mlx90632_sleep(mlx90632); + return ret; + } + pm_runtime_enable(&client->dev); + pm_runtime_set_autosuspend_delay(&client->dev, MLX90632_SLEEP_DELAY_MS); + pm_runtime_use_autosuspend(&client->dev); + + return iio_device_register(indio_dev); +} + +static int mlx90632_remove(struct i2c_client *client) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(client); + struct mlx90632_data *data = iio_priv(indio_dev); + + iio_device_unregister(indio_dev); + + pm_runtime_disable(&client->dev); + pm_runtime_set_suspended(&client->dev); + pm_runtime_put_noidle(&client->dev); + + mlx90632_sleep(data); + + return 0; +} + +static const struct i2c_device_id mlx90632_id[] = { + { "mlx90632", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, mlx90632_id); + +static const struct of_device_id mlx90632_of_match[] = { + { .compatible = "melexis,mlx90632" }, + { } +}; +MODULE_DEVICE_TABLE(of, mlx90632_of_match); + +static int __maybe_unused mlx90632_pm_suspend(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct mlx90632_data *data = iio_priv(indio_dev); + + return mlx90632_sleep(data); +} + +static int __maybe_unused mlx90632_pm_resume(struct device *dev) +{ + struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev)); + struct mlx90632_data *data = iio_priv(indio_dev); + + return mlx90632_wakeup(data); +} + +static UNIVERSAL_DEV_PM_OPS(mlx90632_pm_ops, mlx90632_pm_suspend, + mlx90632_pm_resume, NULL); + +static struct i2c_driver mlx90632_driver = { + .driver = { + .name = "mlx90632", + .of_match_table = mlx90632_of_match, + .pm = &mlx90632_pm_ops, + }, + .probe = mlx90632_probe, + .remove = mlx90632_remove, + .id_table = mlx90632_id, +}; +module_i2c_driver(mlx90632_driver); + +MODULE_AUTHOR("Crt Mori "); +MODULE_DESCRIPTION("Melexis MLX90632 contactless Infra Red temperature sensor driver"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From 79e8a32d2aa9e98b1560cbe107e750f07807d8c7 Mon Sep 17 00:00:00 2001 From: Phil Reid Date: Fri, 9 Feb 2018 08:57:27 +0800 Subject: iio: ad5272: Add support for Analog Devices digital potentiometers Add implementation for Analog Devices AD5272 and AD5274 digital potentiometer devices. Signed-off-by: Phil Reid Signed-off-by: Jonathan Cameron --- drivers/iio/potentiometer/Kconfig | 10 ++ drivers/iio/potentiometer/Makefile | 1 + drivers/iio/potentiometer/ad5272.c | 231 +++++++++++++++++++++++++++++++++++++ 3 files changed, 242 insertions(+) create mode 100644 drivers/iio/potentiometer/ad5272.c (limited to 'drivers/iio') diff --git a/drivers/iio/potentiometer/Kconfig b/drivers/iio/potentiometer/Kconfig index 8bf282510be6..8daf6d1f4e05 100644 --- a/drivers/iio/potentiometer/Kconfig +++ b/drivers/iio/potentiometer/Kconfig @@ -5,6 +5,16 @@ menu "Digital potentiometers" +config AD5272 + tristate "Analog Devices AD5272 and similar Digital Potentiometer driver" + depends on I2C + help + Say yes here to build support for the Analog Devices AD5272 and AD5274 + digital potentiometer chip. + + To compile this driver as a module, choose M here: the + module will be called ad5272. + config DS1803 tristate "Maxim Integrated DS1803 Digital Potentiometer driver" depends on I2C diff --git a/drivers/iio/potentiometer/Makefile b/drivers/iio/potentiometer/Makefile index 1afd1e70f8cc..dbc139cec376 100644 --- a/drivers/iio/potentiometer/Makefile +++ b/drivers/iio/potentiometer/Makefile @@ -4,6 +4,7 @@ # # When adding new entries keep the list in alphabetical order +obj-$(CONFIG_AD5272) += ad5272.o obj-$(CONFIG_DS1803) += ds1803.o obj-$(CONFIG_MAX5481) += max5481.o obj-$(CONFIG_MAX5487) += max5487.o diff --git a/drivers/iio/potentiometer/ad5272.c b/drivers/iio/potentiometer/ad5272.c new file mode 100644 index 000000000000..154f9a5da8bc --- /dev/null +++ b/drivers/iio/potentiometer/ad5272.c @@ -0,0 +1,231 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Analog Devices AD5272 digital potentiometer driver + * Copyright (C) 2018 Phil Reid + * + * Datasheet: http://www.analog.com/media/en/technical-documentation/data-sheets/AD5272_5274.pdf + * + * DEVID #Wipers #Positions Resistor Opts (kOhm) i2c address + * ad5272 1 1024 20, 50, 100 01011xx + * ad5274 1 256 20, 100 01011xx + */ + +#include +#include +#include +#include +#include + +#define AD5272_RDAC_WR 1 +#define AD5272_RDAC_RD 2 +#define AD5272_RESET 4 +#define AD5272_CTL 7 + +#define AD5272_RDAC_WR_EN BIT(1) + +struct ad5272_cfg { + int max_pos; + int kohms; + int shift; +}; + +enum ad5272_type { + AD5272_020, + AD5272_050, + AD5272_100, + AD5274_020, + AD5274_100, +}; + +static const struct ad5272_cfg ad5272_cfg[] = { + [AD5272_020] = { .max_pos = 1024, .kohms = 20 }, + [AD5272_050] = { .max_pos = 1024, .kohms = 50 }, + [AD5272_100] = { .max_pos = 1024, .kohms = 100 }, + [AD5274_020] = { .max_pos = 256, .kohms = 20, .shift = 2 }, + [AD5274_100] = { .max_pos = 256, .kohms = 100, .shift = 2 }, +}; + +struct ad5272_data { + struct i2c_client *client; + struct mutex lock; + const struct ad5272_cfg *cfg; + u8 buf[2] ____cacheline_aligned; +}; + +static const struct iio_chan_spec ad5272_channel = { + .type = IIO_RESISTANCE, + .output = 1, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), +}; + +static int ad5272_write(struct ad5272_data *data, int reg, int val) +{ + int ret; + + data->buf[0] = (reg << 2) | ((val >> 8) & 0x3); + data->buf[1] = (u8)val; + + mutex_lock(&data->lock); + ret = i2c_master_send(data->client, data->buf, sizeof(data->buf)); + mutex_unlock(&data->lock); + return ret < 0 ? ret : 0; +} + +static int ad5272_read(struct ad5272_data *data, int reg, int *val) +{ + int ret; + + data->buf[0] = reg << 2; + data->buf[1] = 0; + + mutex_lock(&data->lock); + ret = i2c_master_send(data->client, data->buf, sizeof(data->buf)); + if (ret < 0) + goto error; + + ret = i2c_master_recv(data->client, data->buf, sizeof(data->buf)); + if (ret < 0) + goto error; + + *val = ((data->buf[0] & 0x3) << 8) | data->buf[1]; + ret = 0; +error: + mutex_unlock(&data->lock); + return ret; +} + +static int ad5272_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int *val, int *val2, long mask) +{ + struct ad5272_data *data = iio_priv(indio_dev); + int ret; + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + ret = ad5272_read(data, AD5272_RDAC_RD, val); + *val = *val >> data->cfg->shift; + return ret ? ret : IIO_VAL_INT; + } + case IIO_CHAN_INFO_SCALE: + *val = 1000 * data->cfg->kohms; + *val2 = data->cfg->max_pos; + return IIO_VAL_FRACTIONAL; + } + + return -EINVAL; +} + +static int ad5272_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ad5272_data *data = iio_priv(indio_dev); + + if (mask != IIO_CHAN_INFO_RAW) + return -EINVAL; + + if (val >= data->cfg->max_pos || val < 0 || val2) + return -EINVAL; + + return ad5272_write(data, AD5272_RDAC_WR, val << data->cfg->shift); +} + +static const struct iio_info ad5272_info = { + .read_raw = ad5272_read_raw, + .write_raw = ad5272_write_raw, +}; + +static int ad5272_reset(struct ad5272_data *data) +{ + struct gpio_desc *reset_gpio; + + reset_gpio = devm_gpiod_get_optional(&data->client->dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(reset_gpio)) + return PTR_ERR(reset_gpio); + + if (reset_gpio) { + udelay(1); + gpiod_set_value(reset_gpio, 1); + } else { + ad5272_write(data, AD5272_RESET, 0); + } + usleep_range(1000, 2000); + + return 0; +} + +static int ad5272_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct device *dev = &client->dev; + struct iio_dev *indio_dev; + struct ad5272_data *data; + int ret; + + indio_dev = devm_iio_device_alloc(dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + i2c_set_clientdata(client, indio_dev); + + data = iio_priv(indio_dev); + data->client = client; + mutex_init(&data->lock); + data->cfg = &ad5272_cfg[id->driver_data]; + + ret = ad5272_reset(data); + if (ret) + return ret; + + ret = ad5272_write(data, AD5272_CTL, AD5272_RDAC_WR_EN); + if (ret < 0) + return -ENODEV; + + indio_dev->dev.parent = dev; + indio_dev->info = &ad5272_info; + indio_dev->channels = &ad5272_channel; + indio_dev->num_channels = 1; + indio_dev->name = client->name; + + return devm_iio_device_register(dev, indio_dev); +} + +#if defined(CONFIG_OF) +static const struct of_device_id ad5272_dt_ids[] = { + { .compatible = "adi,ad5272-020", .data = (void *)AD5272_020 }, + { .compatible = "adi,ad5272-050", .data = (void *)AD5272_050 }, + { .compatible = "adi,ad5272-100", .data = (void *)AD5272_100 }, + { .compatible = "adi,ad5274-020", .data = (void *)AD5274_020 }, + { .compatible = "adi,ad5274-100", .data = (void *)AD5274_100 }, + {} +}; +MODULE_DEVICE_TABLE(of, ad5272_dt_ids); +#endif /* CONFIG_OF */ + +static const struct i2c_device_id ad5272_id[] = { + { "ad5272-020", AD5272_020 }, + { "ad5272-050", AD5272_050 }, + { "ad5272-100", AD5272_100 }, + { "ad5274-020", AD5274_020 }, + { "ad5274-100", AD5274_100 }, + {} +}; +MODULE_DEVICE_TABLE(i2c, ad5272_id); + +static struct i2c_driver ad5272_driver = { + .driver = { + .name = "ad5272", + .of_match_table = of_match_ptr(ad5272_dt_ids), + }, + .probe = ad5272_probe, + .id_table = ad5272_id, +}; + +module_i2c_driver(ad5272_driver); + +MODULE_AUTHOR("Phil Reid "); +MODULE_DESCRIPTION("AD5272 digital potentiometer"); +MODULE_LICENSE("GPL v2"); -- cgit v1.2.3 From a6bffb6920d16fb2ef4ba072fac5b64b36b1c254 Mon Sep 17 00:00:00 2001 From: Harald Geyer Date: Sun, 11 Feb 2018 11:09:40 +0000 Subject: iio: dht11: Improve detection of sensor type The old code was based on a DHT11 datasheet which specifies a measurement range of 20%-90% RH. Turns out the sensor actually reports values outside this range, so we should support it as far as possible. Reported-by: Edward Attfield Signed-off-by: Harald Geyer Signed-off-by: Jonathan Cameron --- drivers/iio/humidity/dht11.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/humidity/dht11.c b/drivers/iio/humidity/dht11.c index df6bab40d6fa..1a9f8f4ffb88 100644 --- a/drivers/iio/humidity/dht11.c +++ b/drivers/iio/humidity/dht11.c @@ -159,7 +159,7 @@ static int dht11_decode(struct dht11 *dht11, int offset) } dht11->timestamp = ktime_get_boot_ns(); - if (hum_int < 20) { /* DHT22 */ + if (hum_int < 4) { /* DHT22: 100000 = (3*256+232)*100 */ dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) * ((temp_int & 0x80) ? -100 : 100); dht11->humidity = ((hum_int << 8) + hum_dec) * 100; -- cgit v1.2.3 From 1482705087b820a3b7019cc9d8a7c760b8999994 Mon Sep 17 00:00:00 2001 From: rodrigosiqueira Date: Fri, 16 Feb 2018 17:44:41 -0200 Subject: iio:pressure:ms5611: Fix coding style in probe function This patch fixes the checkpatch.pl warning and error: iio/pressure/ms5611.h:66: ERROR: code indent should use tabs where possible iio/pressure/ms5611.h:66: WARNING: please, no spaces at the start of a line iio/pressure/ms5611.h:66: ERROR: "foo* bar" should be "foo *bar" Signed-off-by: Rodrigo Siqueira Signed-off-by: Jonathan Cameron --- drivers/iio/pressure/ms5611.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/iio') diff --git a/drivers/iio/pressure/ms5611.h b/drivers/iio/pressure/ms5611.h index ccda63c5b3c3..ead9e9f85894 100644 --- a/drivers/iio/pressure/ms5611.h +++ b/drivers/iio/pressure/ms5611.h @@ -63,7 +63,7 @@ struct ms5611_state { }; int ms5611_probe(struct iio_dev *indio_dev, struct device *dev, - const char* name, int type); + const char *name, int type); int ms5611_remove(struct iio_dev *indio_dev); #endif /* _MS5611_H */ -- cgit v1.2.3 From f0ef941a621797f31a47679f4acaa63f7e1e9a2c Mon Sep 17 00:00:00 2001 From: Richard Lai Date: Tue, 13 Feb 2018 22:36:57 +0000 Subject: iio: chemical: ccs811: Typo correction in HW_ID_VALUE constant define naming This particular constant was named with prefix "CCS881", which should be "CCS811" instead, just like the rest of constant names in the file, as this driver implementation is for AMS CCS811 sensor. "CCS881" could literally be referring to another sensor product unrelated to AMS CCS811 sensor. Signed-off-by: Richard Lai Signed-off-by: Jonathan Cameron --- drivers/iio/chemical/ccs811.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/chemical/ccs811.c b/drivers/iio/chemical/ccs811.c index fbe2431f5b81..cfaf86b248d9 100644 --- a/drivers/iio/chemical/ccs811.c +++ b/drivers/iio/chemical/ccs811.c @@ -32,7 +32,7 @@ #define CCS811_ALG_RESULT_DATA 0x02 #define CCS811_RAW_DATA 0x03 #define CCS811_HW_ID 0x20 -#define CCS881_HW_ID_VALUE 0x81 +#define CCS811_HW_ID_VALUE 0x81 #define CCS811_HW_VERSION 0x21 #define CCS811_HW_VERSION_VALUE 0x10 #define CCS811_HW_VERSION_MASK 0xF0 @@ -353,7 +353,7 @@ static int ccs811_probe(struct i2c_client *client, if (ret < 0) return ret; - if (ret != CCS881_HW_ID_VALUE) { + if (ret != CCS811_HW_ID_VALUE) { dev_err(&client->dev, "hardware id doesn't match CCS81x\n"); return -ENODEV; } -- cgit v1.2.3 From d6ad805844c52d256c2a3ff3d60daa10d27a8b64 Mon Sep 17 00:00:00 2001 From: Matt Ranostay Date: Sat, 17 Feb 2018 21:36:46 -0800 Subject: iio: add SPDX identifier for various drivers Add GPLv2+ SPDX identifier and update email for author's drivers. Signed-off-by: Matt Ranostay Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ti-adc161s626.c | 16 ++++------------ drivers/iio/chemical/ams-iaq-core.c | 17 ++++------------- drivers/iio/chemical/atlas-ph-sensor.c | 16 ++++------------ drivers/iio/chemical/vz89x.c | 17 ++++------------- drivers/iio/health/max30100.c | 16 ++++------------ drivers/iio/humidity/hdc100x.c | 16 ++++------------ drivers/iio/light/apds9960.c | 16 ++++------------ drivers/iio/potentiometer/tpl0102.c | 16 ++++------------ drivers/iio/potentiostat/lmp91000.c | 16 ++++------------ drivers/iio/proximity/as3935.c | 17 ++++------------- drivers/iio/proximity/pulsedlight-lidar-lite-v2.c | 16 ++++------------ drivers/iio/temperature/maxim_thermocouple.c | 16 ++++------------ 12 files changed, 48 insertions(+), 147 deletions(-) (limited to 'drivers/iio') diff --git a/drivers/iio/adc/ti-adc161s626.c b/drivers/iio/adc/ti-adc161s626.c index 10fa7677ac4b..3bbc9b9ddbfe 100644 --- a/drivers/iio/adc/ti-adc161s626.c +++ b/drivers/iio/adc/ti-adc161s626.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ti-adc161s626.c - Texas Instruments ADC161S626 1-channel differential ADC * @@ -5,17 +6,8 @@ * adc141s626 - 14-bit ADC * adc161s626 - 16-bit ADC * - * Copyright (C) 2016 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. + * Copyright (C) 2016-2018 + * Author: Matt Ranostay */ #include @@ -275,6 +267,6 @@ static struct spi_driver ti_adc_driver = { }; module_spi_driver(ti_adc_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("Texas Instruments ADC1x1S 1-channel differential ADC"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/ams-iaq-core.c b/drivers/iio/chemical/ams-iaq-core.c index d9e5950ad24a..a0646ba2ad88 100644 --- a/drivers/iio/chemical/ams-iaq-core.c +++ b/drivers/iio/chemical/ams-iaq-core.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * ams-iaq-core.c - Support for AMS iAQ-Core VOC sensors * - * Copyright (C) 2015 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * + * Copyright (C) 2015, 2018 + * Author: Matt Ranostay */ #include @@ -194,6 +185,6 @@ static struct i2c_driver ams_iaqcore_driver = { }; module_i2c_driver(ams_iaqcore_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("AMS iAQ-Core VOC sensors"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/chemical/atlas-ph-sensor.c b/drivers/iio/chemical/atlas-ph-sensor.c index 8c4e05580091..abfc4bbc4cfc 100644 --- a/drivers/iio/chemical/atlas-ph-sensor.c +++ b/drivers/iio/chemical/atlas-ph-sensor.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * atlas-ph-sensor.c - Support for Atlas Scientific OEM pH-SM sensor * - * Copyright (C) 2015 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. + * Copyright (C) 2015-2018 Matt Ranostay + * Author: Matt Ranostay */ #include @@ -689,6 +681,6 @@ static struct i2c_driver atlas_driver = { }; module_i2c_driver(atlas_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("Atlas Scientific pH-SM sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/chemical/vz89x.c b/drivers/iio/chemical/vz89x.c index 9c9095ba4227..415b39339d4e 100644 --- a/drivers/iio/chemical/vz89x.c +++ b/drivers/iio/chemical/vz89x.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * vz89x.c - Support for SGX Sensortech MiCS VZ89X VOC sensors * - * Copyright (C) 2015 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * + * Copyright (C) 2015-2018 + * Author: Matt Ranostay */ #include @@ -419,6 +410,6 @@ static struct i2c_driver vz89x_driver = { }; module_i2c_driver(vz89x_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("SGX Sensortech MiCS VZ89X VOC sensors"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 91aef5df24a1..84010501762d 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * max30100.c - Support for MAX30100 heart rate and pulse oximeter sensor * - * Copyright (C) 2015 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. + * Copyright (C) 2015, 2018 + * Author: Matt Ranostay * * TODO: enable pulse length controls via device tree properties */ @@ -518,6 +510,6 @@ static struct i2c_driver max30100_driver = { }; module_i2c_driver(max30100_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("MAX30100 heart rate and pulse oximeter sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/humidity/hdc100x.c b/drivers/iio/humidity/hdc100x.c index d8438310b6d4..066e05f92081 100644 --- a/drivers/iio/humidity/hdc100x.c +++ b/drivers/iio/humidity/hdc100x.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * hdc100x.c - Support for the TI HDC100x temperature + humidity sensors * - * Copyright (C) 2015 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. + * Copyright (C) 2015, 2018 + * Author: Matt Ranostay * * Datasheets: * http://www.ti.com/product/HDC1000/datasheet @@ -449,6 +441,6 @@ static struct i2c_driver hdc100x_driver = { }; module_i2c_driver(hdc100x_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/light/apds9960.c b/drivers/iio/light/apds9960.c index a8fa00e31c39..1f112ae15f3c 100644 --- a/drivers/iio/light/apds9960.c +++ b/drivers/iio/light/apds9960.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * apds9960.c - Support for Avago APDS9960 gesture/RGB/ALS/proximity sensor * - * Copyright (C) 2015 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. + * Copyright (C) 2015, 2018 + * Author: Matt Ranostay * * TODO: gesture + proximity calib offsets */ @@ -1141,6 +1133,6 @@ static struct i2c_driver apds9960_driver = { }; module_i2c_driver(apds9960_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("ADPS9960 Gesture/RGB/ALS/Proximity sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/potentiometer/tpl0102.c b/drivers/iio/potentiometer/tpl0102.c index 93f9d4a8c9aa..ca1cce58fe20 100644 --- a/drivers/iio/potentiometer/tpl0102.c +++ b/drivers/iio/potentiometer/tpl0102.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * tpl0102.c - Support for Texas Instruments digital potentiometers * - * Copyright (C) 2016 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. + * Copyright (C) 2016, 2018 + * Author: Matt Ranostay * * TODO: enable/disable hi-z output control */ @@ -156,6 +148,6 @@ static struct i2c_driver tpl0102_driver = { module_i2c_driver(tpl0102_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("TPL0102 digital potentiometer"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/potentiostat/lmp91000.c b/drivers/iio/potentiostat/lmp91000.c index 007710991f15..85714055cc74 100644 --- a/drivers/iio/potentiostat/lmp91000.c +++ b/drivers/iio/potentiostat/lmp91000.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * lmp91000.c - Support for Texas Instruments digital potentiostats * - * Copyright (C) 2016 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. + * Copyright (C) 2016, 2018 + * Author: Matt Ranostay * * TODO: bias voltage + polarity control, and multiple chip support */ @@ -440,6 +432,6 @@ static struct i2c_driver lmp91000_driver = { }; module_i2c_driver(lmp91000_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("LMP91000 digital potentiostat"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/as3935.c b/drivers/iio/proximity/as3935.c index b6249af48014..f130388a16a0 100644 --- a/drivers/iio/proximity/as3935.c +++ b/drivers/iio/proximity/as3935.c @@ -1,18 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * as3935.c - Support for AS3935 Franklin lightning sensor * - * Copyright (C) 2014 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - * + * Copyright (C) 2014, 2017-2018 + * Author: Matt Ranostay */ #include @@ -502,6 +493,6 @@ static struct spi_driver as3935_driver = { }; module_spi_driver(as3935_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("AS3935 lightning sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c index 4d56f67b24c6..47af54f14756 100644 --- a/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c +++ b/drivers/iio/proximity/pulsedlight-lidar-lite-v2.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * pulsedlight-lidar-lite-v2.c - Support for PulsedLight LIDAR sensor * - * Copyright (C) 2015 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. + * Copyright (C) 2015, 2017-2018 + * Author: Matt Ranostay * * TODO: interrupt mode, and signal strength reporting */ @@ -377,6 +369,6 @@ static struct i2c_driver lidar_driver = { }; module_i2c_driver(lidar_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("PulsedLight LIDAR sensor"); MODULE_LICENSE("GPL"); diff --git a/drivers/iio/temperature/maxim_thermocouple.c b/drivers/iio/temperature/maxim_thermocouple.c index e8b7e0b6c8ad..54e383231d1e 100644 --- a/drivers/iio/temperature/maxim_thermocouple.c +++ b/drivers/iio/temperature/maxim_thermocouple.c @@ -1,17 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * maxim_thermocouple.c - Support for Maxim thermocouple chips * - * Copyright (C) 2016 Matt Ranostay - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. + * Copyright (C) 2016-2018 Matt Ranostay + * Author: */ #include @@ -281,6 +273,6 @@ static struct spi_driver maxim_thermocouple_driver = { }; module_spi_driver(maxim_thermocouple_driver); -MODULE_AUTHOR("Matt Ranostay "); +MODULE_AUTHOR("Matt Ranostay "); MODULE_DESCRIPTION("Maxim thermocouple sensors"); MODULE_LICENSE("GPL"); -- cgit v1.2.3