diff options
author | 2016-04-11 10:41:07 +0300 | |
---|---|---|
committer | 2016-04-13 08:17:18 +0300 | |
commit | e09b41010ba33a20a87472ee821fa407a5b8da36 (patch) | |
tree | d10dc367189862e7ca5c592f033dc3726e1df4e3 /kernel/drivers/iio/humidity | |
parent | f93b97fd65072de626c074dbe099a1fff05ce060 (diff) |
These changes are the raw update to linux-4.4.6-rt14. Kernel sources
are taken from kernel.org, and rt patch from the rt wiki download page.
During the rebasing, the following patch collided:
Force tick interrupt and get rid of softirq magic(I70131fb85).
Collisions have been removed because its logic was found on the
source already.
Change-Id: I7f57a4081d9deaa0d9ccfc41a6c8daccdee3b769
Signed-off-by: José Pekkarinen <jose.pekkarinen@nokia.com>
Diffstat (limited to 'kernel/drivers/iio/humidity')
-rw-r--r-- | kernel/drivers/iio/humidity/Kconfig | 25 | ||||
-rw-r--r-- | kernel/drivers/iio/humidity/Makefile | 2 | ||||
-rw-r--r-- | kernel/drivers/iio/humidity/dht11.c | 65 | ||||
-rw-r--r-- | kernel/drivers/iio/humidity/hdc100x.c | 320 | ||||
-rw-r--r-- | kernel/drivers/iio/humidity/htu21.c | 253 | ||||
-rw-r--r-- | kernel/drivers/iio/humidity/si7005.c | 1 | ||||
-rw-r--r-- | kernel/drivers/iio/humidity/si7020.c | 14 |
7 files changed, 643 insertions, 37 deletions
diff --git a/kernel/drivers/iio/humidity/Kconfig b/kernel/drivers/iio/humidity/Kconfig index 4813b793b..6a23698d3 100644 --- a/kernel/drivers/iio/humidity/Kconfig +++ b/kernel/drivers/iio/humidity/Kconfig @@ -5,13 +5,36 @@ menu "Humidity sensors" config DHT11 tristate "DHT11 (and compatible sensors) driver" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST help This driver supports reading data via a single interrupt generating GPIO line. Currently tested are DHT11 and DHT22. Other sensors should work as well as long as they speak the same protocol. +config HDC100X + tristate "TI HDC100x relative humidity and temperature sensor" + depends on I2C + help + Say yes here to build support for the TI HDC100x series of + relative humidity and temperature sensors. + + To compile this driver as a module, choose M here: the module + will be called hdc100x. + +config HTU21 + tristate "Measurement Specialties HTU21 humidity & temperature sensor" + depends on I2C + select IIO_MS_SENSORS_I2C + help + If you say yes here you get support for the Measurement Specialties + HTU21 humidity and temperature sensor. + This driver is also used for MS8607 temperature, pressure & humidity + sensor + + This driver can also be built as a module. If so, the module will + be called htu21. + config SI7005 tristate "SI7005 relative humidity and temperature sensor" depends on I2C diff --git a/kernel/drivers/iio/humidity/Makefile b/kernel/drivers/iio/humidity/Makefile index 86e2d26e9..c9f089a9a 100644 --- a/kernel/drivers/iio/humidity/Makefile +++ b/kernel/drivers/iio/humidity/Makefile @@ -3,5 +3,7 @@ # obj-$(CONFIG_DHT11) += dht11.o +obj-$(CONFIG_HDC100X) += hdc100x.o +obj-$(CONFIG_HTU21) += htu21.o obj-$(CONFIG_SI7005) += si7005.o obj-$(CONFIG_SI7020) += si7020.o diff --git a/kernel/drivers/iio/humidity/dht11.c b/kernel/drivers/iio/humidity/dht11.c index 7d79a1ac5..1165b1c4f 100644 --- a/kernel/drivers/iio/humidity/dht11.c +++ b/kernel/drivers/iio/humidity/dht11.c @@ -33,6 +33,7 @@ #include <linux/delay.h> #include <linux/gpio.h> #include <linux/of_gpio.h> +#include <linux/timekeeping.h> #include <linux/iio/iio.h> @@ -46,7 +47,8 @@ * Note that when reading the sensor actually 84 edges are detected, but * since the last edge is not significant, we only store 83: */ -#define DHT11_EDGES_PER_READ (2*DHT11_BITS_PER_READ + DHT11_EDGES_PREAMBLE + 1) +#define DHT11_EDGES_PER_READ (2 * DHT11_BITS_PER_READ + \ + DHT11_EDGES_PREAMBLE + 1) /* Data transmission timing (nano seconds) */ #define DHT11_START_TRANSMISSION 18 /* ms */ @@ -62,6 +64,7 @@ struct dht11 { int irq; struct completion completion; + /* The iio sysfs interface doesn't prevent concurrent reads: */ struct mutex lock; s64 timestamp; @@ -87,32 +90,20 @@ static unsigned char dht11_decode_byte(int *timing, int threshold) return ret; } -static int dht11_decode(struct dht11 *dht11, int offset) +static int dht11_decode(struct dht11 *dht11, int offset, int timeres) { - int i, t, timing[DHT11_BITS_PER_READ], threshold, - timeres = DHT11_SENSOR_RESPONSE; + int i, t, timing[DHT11_BITS_PER_READ], threshold; unsigned char temp_int, temp_dec, hum_int, hum_dec, checksum; - /* Calculate timestamp resolution */ - for (i = 1; i < dht11->num_edges; ++i) { - t = dht11->edges[i].ts - dht11->edges[i-1].ts; - if (t > 0 && t < timeres) - timeres = t; - } - if (2*timeres > DHT11_DATA_BIT_HIGH) { - pr_err("dht11: timeresolution %d too bad for decoding\n", - timeres); - return -EIO; - } threshold = DHT11_DATA_BIT_HIGH / timeres; - if (DHT11_DATA_BIT_LOW/timeres + 1 >= threshold) + if (DHT11_DATA_BIT_LOW / timeres + 1 >= threshold) pr_err("dht11: WARNING: decoding ambiguous\n"); /* scale down with timeres and check validity */ for (i = 0; i < DHT11_BITS_PER_READ; ++i) { - t = dht11->edges[offset + 2*i + 2].ts - - dht11->edges[offset + 2*i + 1].ts; - if (!dht11->edges[offset + 2*i + 1].value) + t = dht11->edges[offset + 2 * i + 2].ts - + dht11->edges[offset + 2 * i + 1].ts; + if (!dht11->edges[offset + 2 * i + 1].value) return -EIO; /* lost synchronisation */ timing[i] = t / timeres; } @@ -126,7 +117,7 @@ static int dht11_decode(struct dht11 *dht11, int offset) if (((hum_int + hum_dec + temp_int + temp_dec) & 0xff) != checksum) return -EIO; - dht11->timestamp = iio_get_time_ns(); + dht11->timestamp = ktime_get_real_ns(); if (hum_int < 20) { /* DHT22 */ dht11->temperature = (((temp_int & 0x7f) << 8) + temp_dec) * ((temp_int & 0x80) ? -100 : 100); @@ -154,7 +145,7 @@ static irqreturn_t dht11_handle_irq(int irq, void *data) /* TODO: Consider making the handler safe for IRQ sharing */ if (dht11->num_edges < DHT11_EDGES_PER_READ && dht11->num_edges >= 0) { - dht11->edges[dht11->num_edges].ts = iio_get_time_ns(); + dht11->edges[dht11->num_edges].ts = ktime_get_real_ns(); dht11->edges[dht11->num_edges++].value = gpio_get_value(dht11->gpio); @@ -166,14 +157,26 @@ static irqreturn_t dht11_handle_irq(int irq, void *data) } static int dht11_read_raw(struct iio_dev *iio_dev, - const struct iio_chan_spec *chan, + const struct iio_chan_spec *chan, int *val, int *val2, long m) { struct dht11 *dht11 = iio_priv(iio_dev); - int ret; + int ret, timeres; mutex_lock(&dht11->lock); - if (dht11->timestamp + DHT11_DATA_VALID_TIME < iio_get_time_ns()) { + if (dht11->timestamp + DHT11_DATA_VALID_TIME < ktime_get_real_ns()) { + timeres = ktime_get_resolution_ns(); + if (DHT11_DATA_BIT_HIGH < 2 * timeres) { + dev_err(dht11->dev, "timeresolution %dns too low\n", + timeres); + /* In theory a better clock could become available + * at some point ... and there is no error code + * that really fits better. + */ + ret = -EAGAIN; + goto err; + } + reinit_completion(&dht11->completion); dht11->num_edges = 0; @@ -192,13 +195,13 @@ static int dht11_read_raw(struct iio_dev *iio_dev, goto err; ret = wait_for_completion_killable_timeout(&dht11->completion, - HZ); + HZ); free_irq(dht11->irq, iio_dev); if (ret == 0 && dht11->num_edges < DHT11_EDGES_PER_READ - 1) { dev_err(&iio_dev->dev, - "Only %d signal edges detected\n", + "Only %d signal edges detected\n", dht11->num_edges); ret = -ETIMEDOUT; } @@ -206,9 +209,10 @@ static int dht11_read_raw(struct iio_dev *iio_dev, goto err; ret = dht11_decode(dht11, - dht11->num_edges == DHT11_EDGES_PER_READ ? + dht11->num_edges == DHT11_EDGES_PER_READ ? DHT11_EDGES_PREAMBLE : - DHT11_EDGES_PREAMBLE - 2); + DHT11_EDGES_PREAMBLE - 2, + timeres); if (ret) goto err; } @@ -261,9 +265,10 @@ static int dht11_probe(struct platform_device *pdev) dht11 = iio_priv(iio); dht11->dev = dev; - dht11->gpio = ret = of_get_gpio(node, 0); + ret = of_get_gpio(node, 0); if (ret < 0) return ret; + dht11->gpio = ret; ret = devm_gpio_request_one(dev, dht11->gpio, GPIOF_IN, pdev->name); if (ret) return ret; @@ -274,7 +279,7 @@ static int dht11_probe(struct platform_device *pdev) return -EINVAL; } - dht11->timestamp = iio_get_time_ns() - DHT11_DATA_VALID_TIME - 1; + dht11->timestamp = ktime_get_real_ns() - DHT11_DATA_VALID_TIME - 1; dht11->num_edges = -1; platform_set_drvdata(pdev, iio); diff --git a/kernel/drivers/iio/humidity/hdc100x.c b/kernel/drivers/iio/humidity/hdc100x.c new file mode 100644 index 000000000..a7f61e881 --- /dev/null +++ b/kernel/drivers/iio/humidity/hdc100x.c @@ -0,0 +1,320 @@ +/* + * hdc100x.c - Support for the TI HDC100x temperature + humidity sensors + * + * Copyright (C) 2015 Matt Ranostay <mranostay@gmail.com> + * + * 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. + * + */ + +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> + +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#define HDC100X_REG_TEMP 0x00 +#define HDC100X_REG_HUMIDITY 0x01 + +#define HDC100X_REG_CONFIG 0x02 +#define HDC100X_REG_CONFIG_HEATER_EN BIT(13) + +struct hdc100x_data { + struct i2c_client *client; + struct mutex lock; + u16 config; + + /* integration time of the sensor */ + int adc_int_us[2]; +}; + +/* integration time in us */ +static const int hdc100x_int_time[][3] = { + { 6350, 3650, 0 }, /* IIO_TEMP channel*/ + { 6500, 3850, 2500 }, /* IIO_HUMIDITYRELATIVE channel */ +}; + +/* HDC100X_REG_CONFIG shift and mask values */ +static const struct { + int shift; + int mask; +} hdc100x_resolution_shift[2] = { + { /* IIO_TEMP channel */ + .shift = 10, + .mask = 1 + }, + { /* IIO_HUMIDITYRELATIVE channel */ + .shift = 8, + .mask = 2, + }, +}; + +static IIO_CONST_ATTR(temp_integration_time_available, + "0.00365 0.00635"); + +static IIO_CONST_ATTR(humidityrelative_integration_time_available, + "0.0025 0.00385 0.0065"); + +static IIO_CONST_ATTR(out_current_heater_raw_available, + "0 1"); + +static struct attribute *hdc100x_attributes[] = { + &iio_const_attr_temp_integration_time_available.dev_attr.attr, + &iio_const_attr_humidityrelative_integration_time_available.dev_attr.attr, + &iio_const_attr_out_current_heater_raw_available.dev_attr.attr, + NULL +}; + +static struct attribute_group hdc100x_attribute_group = { + .attrs = hdc100x_attributes, +}; + +static const struct iio_chan_spec hdc100x_channels[] = { + { + .type = IIO_TEMP, + .address = HDC100X_REG_TEMP, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_INT_TIME) | + BIT(IIO_CHAN_INFO_OFFSET), + }, + { + .type = IIO_HUMIDITYRELATIVE, + .address = HDC100X_REG_HUMIDITY, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW) | + BIT(IIO_CHAN_INFO_SCALE) | + BIT(IIO_CHAN_INFO_INT_TIME) + }, + { + .type = IIO_CURRENT, + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), + .extend_name = "heater", + .output = 1, + }, +}; + +static int hdc100x_update_config(struct hdc100x_data *data, int mask, int val) +{ + int tmp = (~mask & data->config) | val; + int ret; + + ret = i2c_smbus_write_word_swapped(data->client, + HDC100X_REG_CONFIG, tmp); + if (!ret) + data->config = tmp; + + return ret; +} + +static int hdc100x_set_it_time(struct hdc100x_data *data, int chan, int val2) +{ + int shift = hdc100x_resolution_shift[chan].shift; + int ret = -EINVAL; + int i; + + for (i = 0; i < ARRAY_SIZE(hdc100x_int_time[chan]); i++) { + if (val2 && val2 == hdc100x_int_time[chan][i]) { + ret = hdc100x_update_config(data, + hdc100x_resolution_shift[chan].mask << shift, + i << shift); + if (!ret) + data->adc_int_us[chan] = val2; + break; + } + } + + return ret; +} + +static int hdc100x_get_measurement(struct hdc100x_data *data, + struct iio_chan_spec const *chan) +{ + struct i2c_client *client = data->client; + int delay = data->adc_int_us[chan->address]; + int ret; + int val; + + /* start measurement */ + ret = i2c_smbus_write_byte(client, chan->address); + if (ret < 0) { + dev_err(&client->dev, "cannot start measurement"); + return ret; + } + + /* wait for integration time to pass */ + usleep_range(delay, delay + 1000); + + /* + * i2c_smbus_read_word_data cannot() be used here due to the command + * value not being understood and causes NAKs preventing any reading + * from being accessed. + */ + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "cannot read high byte measurement"); + return ret; + } + val = ret << 6; + + ret = i2c_smbus_read_byte(client); + if (ret < 0) { + dev_err(&client->dev, "cannot read low byte measurement"); + return ret; + } + val |= ret >> 2; + + return val; +} + +static int hdc100x_get_heater_status(struct hdc100x_data *data) +{ + return !!(data->config & HDC100X_REG_CONFIG_HEATER_EN); +} + +static int hdc100x_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, int *val, + int *val2, long mask) +{ + struct hdc100x_data *data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_RAW: { + int ret; + + mutex_lock(&data->lock); + if (chan->type == IIO_CURRENT) { + *val = hdc100x_get_heater_status(data); + ret = IIO_VAL_INT; + } else { + ret = hdc100x_get_measurement(data, chan); + if (ret >= 0) { + *val = ret; + ret = IIO_VAL_INT; + } + } + mutex_unlock(&data->lock); + return ret; + } + case IIO_CHAN_INFO_INT_TIME: + *val = 0; + *val2 = data->adc_int_us[chan->address]; + return IIO_VAL_INT_PLUS_MICRO; + case IIO_CHAN_INFO_SCALE: + if (chan->type == IIO_TEMP) { + *val = 165; + *val2 = 65536 >> 2; + return IIO_VAL_FRACTIONAL; + } else { + *val = 0; + *val2 = 10000; + return IIO_VAL_INT_PLUS_MICRO; + } + break; + case IIO_CHAN_INFO_OFFSET: + *val = -3971; + *val2 = 879096; + return IIO_VAL_INT_PLUS_MICRO; + default: + return -EINVAL; + } +} + +static int hdc100x_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct hdc100x_data *data = iio_priv(indio_dev); + int ret = -EINVAL; + + switch (mask) { + case IIO_CHAN_INFO_INT_TIME: + if (val != 0) + return -EINVAL; + + mutex_lock(&data->lock); + ret = hdc100x_set_it_time(data, chan->address, val2); + mutex_unlock(&data->lock); + return ret; + case IIO_CHAN_INFO_RAW: + if (chan->type != IIO_CURRENT || val2 != 0) + return -EINVAL; + + mutex_lock(&data->lock); + ret = hdc100x_update_config(data, HDC100X_REG_CONFIG_HEATER_EN, + val ? HDC100X_REG_CONFIG_HEATER_EN : 0); + mutex_unlock(&data->lock); + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_info hdc100x_info = { + .read_raw = hdc100x_read_raw, + .write_raw = hdc100x_write_raw, + .attrs = &hdc100x_attribute_group, + .driver_module = THIS_MODULE, +}; + +static int hdc100x_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct iio_dev *indio_dev; + struct hdc100x_data *data; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BYTE)) + return -ENODEV; + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data)); + if (!indio_dev) + return -ENOMEM; + + data = iio_priv(indio_dev); + i2c_set_clientdata(client, indio_dev); + data->client = client; + mutex_init(&data->lock); + + indio_dev->dev.parent = &client->dev; + indio_dev->name = dev_name(&client->dev); + indio_dev->modes = INDIO_DIRECT_MODE; + indio_dev->info = &hdc100x_info; + + indio_dev->channels = hdc100x_channels; + indio_dev->num_channels = ARRAY_SIZE(hdc100x_channels); + + /* be sure we are in a known state */ + hdc100x_set_it_time(data, 0, hdc100x_int_time[0][0]); + hdc100x_set_it_time(data, 1, hdc100x_int_time[1][0]); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id hdc100x_id[] = { + { "hdc100x", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, hdc100x_id); + +static struct i2c_driver hdc100x_driver = { + .driver = { + .name = "hdc100x", + }, + .probe = hdc100x_probe, + .id_table = hdc100x_id, +}; +module_i2c_driver(hdc100x_driver); + +MODULE_AUTHOR("Matt Ranostay <mranostay@gmail.com>"); +MODULE_DESCRIPTION("TI HDC100x humidity and temperature sensor driver"); +MODULE_LICENSE("GPL"); diff --git a/kernel/drivers/iio/humidity/htu21.c b/kernel/drivers/iio/humidity/htu21.c new file mode 100644 index 000000000..d1636a749 --- /dev/null +++ b/kernel/drivers/iio/humidity/htu21.c @@ -0,0 +1,253 @@ +/* + * htu21.c - Support for Measurement-Specialties + * htu21 temperature & humidity sensor + * and humidity part of MS8607 sensor + * + * Copyright (c) 2014 Measurement-Specialties + * + * Licensed under the GPL-2. + * + * (7-bit I2C slave address 0x40) + * + * Datasheet: + * http://www.meas-spec.com/downloads/HTU21D.pdf + * Datasheet: + * http://www.meas-spec.com/downloads/MS8607-02BA01.pdf + */ + +#include <linux/init.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/stat.h> +#include <linux/module.h> +#include <linux/iio/iio.h> +#include <linux/iio/sysfs.h> + +#include "../common/ms_sensors/ms_sensors_i2c.h" + +#define HTU21_RESET 0xFE + +enum { + HTU21, + MS8607 +}; + +static const int htu21_samp_freq[4] = { 20, 40, 70, 120 }; +/* String copy of the above const for readability purpose */ +static const char htu21_show_samp_freq[] = "20 40 70 120"; + +static int htu21_read_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *channel, int *val, + int *val2, long mask) +{ + int ret, temperature; + unsigned int humidity; + struct ms_ht_dev *dev_data = iio_priv(indio_dev); + + switch (mask) { + case IIO_CHAN_INFO_PROCESSED: + switch (channel->type) { + case IIO_TEMP: /* in milli °C */ + ret = ms_sensors_ht_read_temperature(dev_data, + &temperature); + if (ret) + return ret; + *val = temperature; + + return IIO_VAL_INT; + case IIO_HUMIDITYRELATIVE: /* in milli %RH */ + ret = ms_sensors_ht_read_humidity(dev_data, + &humidity); + if (ret) + return ret; + *val = humidity; + + return IIO_VAL_INT; + default: + return -EINVAL; + } + case IIO_CHAN_INFO_SAMP_FREQ: + *val = htu21_samp_freq[dev_data->res_index]; + + return IIO_VAL_INT; + default: + return -EINVAL; + } +} + +static int htu21_write_raw(struct iio_dev *indio_dev, + struct iio_chan_spec const *chan, + int val, int val2, long mask) +{ + struct ms_ht_dev *dev_data = iio_priv(indio_dev); + int i, ret; + + switch (mask) { + case IIO_CHAN_INFO_SAMP_FREQ: + i = ARRAY_SIZE(htu21_samp_freq); + while (i-- > 0) + if (val == htu21_samp_freq[i]) + break; + if (i < 0) + return -EINVAL; + mutex_lock(&dev_data->lock); + dev_data->res_index = i; + ret = ms_sensors_write_resolution(dev_data, i); + mutex_unlock(&dev_data->lock); + + return ret; + default: + return -EINVAL; + } +} + +static const struct iio_chan_spec htu21_channels[] = { + { + .type = IIO_TEMP, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + }, + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + } +}; + +/* + * Meas Spec recommendation is to not read temperature + * on this driver part for MS8607 + */ +static const struct iio_chan_spec ms8607_channels[] = { + { + .type = IIO_HUMIDITYRELATIVE, + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED), + .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ), + } +}; + +static ssize_t htu21_show_battery_low(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ms_ht_dev *dev_data = iio_priv(indio_dev); + + return ms_sensors_show_battery_low(dev_data, buf); +} + +static ssize_t htu21_show_heater(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ms_ht_dev *dev_data = iio_priv(indio_dev); + + return ms_sensors_show_heater(dev_data, buf); +} + +static ssize_t htu21_write_heater(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t len) +{ + struct iio_dev *indio_dev = dev_to_iio_dev(dev); + struct ms_ht_dev *dev_data = iio_priv(indio_dev); + + return ms_sensors_write_heater(dev_data, buf, len); +} + +static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(htu21_show_samp_freq); +static IIO_DEVICE_ATTR(battery_low, S_IRUGO, + htu21_show_battery_low, NULL, 0); +static IIO_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR, + htu21_show_heater, htu21_write_heater, 0); + +static struct attribute *htu21_attributes[] = { + &iio_const_attr_sampling_frequency_available.dev_attr.attr, + &iio_dev_attr_battery_low.dev_attr.attr, + &iio_dev_attr_heater_enable.dev_attr.attr, + NULL, +}; + +static const struct attribute_group htu21_attribute_group = { + .attrs = htu21_attributes, +}; + +static const struct iio_info htu21_info = { + .read_raw = htu21_read_raw, + .write_raw = htu21_write_raw, + .attrs = &htu21_attribute_group, + .driver_module = THIS_MODULE, +}; + +static int htu21_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ms_ht_dev *dev_data; + struct iio_dev *indio_dev; + int ret; + u64 serial_number; + + if (!i2c_check_functionality(client->adapter, + I2C_FUNC_SMBUS_WRITE_BYTE_DATA | + I2C_FUNC_SMBUS_WRITE_BYTE | + I2C_FUNC_SMBUS_READ_I2C_BLOCK)) { + dev_err(&client->dev, + "Adapter does not support some i2c transaction\n"); + return -ENODEV; + } + + indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data)); + if (!indio_dev) + return -ENOMEM; + + dev_data = iio_priv(indio_dev); + dev_data->client = client; + dev_data->res_index = 0; + mutex_init(&dev_data->lock); + + indio_dev->info = &htu21_info; + indio_dev->name = id->name; + indio_dev->dev.parent = &client->dev; + indio_dev->modes = INDIO_DIRECT_MODE; + + if (id->driver_data == MS8607) { + indio_dev->channels = ms8607_channels; + indio_dev->num_channels = ARRAY_SIZE(ms8607_channels); + } else { + indio_dev->channels = htu21_channels; + indio_dev->num_channels = ARRAY_SIZE(htu21_channels); + } + + i2c_set_clientdata(client, indio_dev); + + ret = ms_sensors_reset(client, HTU21_RESET, 15000); + if (ret) + return ret; + + ret = ms_sensors_read_serial(client, &serial_number); + if (ret) + return ret; + dev_info(&client->dev, "Serial number : %llx", serial_number); + + return devm_iio_device_register(&client->dev, indio_dev); +} + +static const struct i2c_device_id htu21_id[] = { + {"htu21", HTU21}, + {"ms8607-humidity", MS8607}, + {} +}; + +static struct i2c_driver htu21_driver = { + .probe = htu21_probe, + .id_table = htu21_id, + .driver = { + .name = "htu21", + }, +}; + +module_i2c_driver(htu21_driver); + +MODULE_DESCRIPTION("Measurement-Specialties htu21 temperature and humidity driver"); +MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); +MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/kernel/drivers/iio/humidity/si7005.c b/kernel/drivers/iio/humidity/si7005.c index bdd586e6d..91972ccd8 100644 --- a/kernel/drivers/iio/humidity/si7005.c +++ b/kernel/drivers/iio/humidity/si7005.c @@ -177,7 +177,6 @@ MODULE_DEVICE_TABLE(i2c, si7005_id); static struct i2c_driver si7005_driver = { .driver = { .name = "si7005", - .owner = THIS_MODULE, }, .probe = si7005_probe, .id_table = si7005_id, diff --git a/kernel/drivers/iio/humidity/si7020.c b/kernel/drivers/iio/humidity/si7020.c index fa3b809af..71991b5c0 100644 --- a/kernel/drivers/iio/humidity/si7020.c +++ b/kernel/drivers/iio/humidity/si7020.c @@ -50,15 +50,19 @@ static int si7020_read_raw(struct iio_dev *indio_dev, switch (mask) { case IIO_CHAN_INFO_RAW: - ret = i2c_smbus_read_word_data(*client, - chan->type == IIO_TEMP ? - SI7020CMD_TEMP_HOLD : - SI7020CMD_RH_HOLD); + ret = i2c_smbus_read_word_swapped(*client, + chan->type == IIO_TEMP ? + SI7020CMD_TEMP_HOLD : + SI7020CMD_RH_HOLD); if (ret < 0) return ret; *val = ret >> 2; + /* + * Humidity values can slightly exceed the 0-100%RH + * range and should be corrected by software + */ if (chan->type == IIO_HUMIDITYRELATIVE) - *val &= GENMASK(11, 0); + *val = clamp_val(*val, 786, 13893); return IIO_VAL_INT; case IIO_CHAN_INFO_SCALE: if (chan->type == IIO_TEMP) |