linux/drivers/iio/humidity/htu21.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * htu21.c - Support for Measurement-Specialties
   4 *           htu21 temperature & humidity sensor
   5 *           and humidity part of MS8607 sensor
   6 *
   7 * Copyright (c) 2014 Measurement-Specialties
   8 *
   9 * (7-bit I2C slave address 0x40)
  10 *
  11 * Datasheet:
  12 *  http://www.meas-spec.com/downloads/HTU21D.pdf
  13 * Datasheet:
  14 *  http://www.meas-spec.com/downloads/MS8607-02BA01.pdf
  15 */
  16
  17#include <linux/init.h>
  18#include <linux/device.h>
  19#include <linux/kernel.h>
  20#include <linux/stat.h>
  21#include <linux/module.h>
  22#include <linux/mod_devicetable.h>
  23#include <linux/iio/iio.h>
  24#include <linux/iio/sysfs.h>
  25
  26#include "../common/ms_sensors/ms_sensors_i2c.h"
  27
  28#define HTU21_RESET                             0xFE
  29
  30enum {
  31        HTU21,
  32        MS8607
  33};
  34
  35static const int htu21_samp_freq[4] = { 20, 40, 70, 120 };
  36/* String copy of the above const for readability purpose */
  37static const char htu21_show_samp_freq[] = "20 40 70 120";
  38
  39static int htu21_read_raw(struct iio_dev *indio_dev,
  40                          struct iio_chan_spec const *channel, int *val,
  41                          int *val2, long mask)
  42{
  43        int ret, temperature;
  44        unsigned int humidity;
  45        struct ms_ht_dev *dev_data = iio_priv(indio_dev);
  46
  47        switch (mask) {
  48        case IIO_CHAN_INFO_PROCESSED:
  49                switch (channel->type) {
  50                case IIO_TEMP:  /* in milli °C */
  51                        ret = ms_sensors_ht_read_temperature(dev_data,
  52                                                             &temperature);
  53                        if (ret)
  54                                return ret;
  55                        *val = temperature;
  56
  57                        return IIO_VAL_INT;
  58                case IIO_HUMIDITYRELATIVE:      /* in milli %RH */
  59                        ret = ms_sensors_ht_read_humidity(dev_data,
  60                                                          &humidity);
  61                        if (ret)
  62                                return ret;
  63                        *val = humidity;
  64
  65                        return IIO_VAL_INT;
  66                default:
  67                        return -EINVAL;
  68                }
  69        case IIO_CHAN_INFO_SAMP_FREQ:
  70                *val = htu21_samp_freq[dev_data->res_index];
  71
  72                return IIO_VAL_INT;
  73        default:
  74                return -EINVAL;
  75        }
  76}
  77
  78static int htu21_write_raw(struct iio_dev *indio_dev,
  79                           struct iio_chan_spec const *chan,
  80                           int val, int val2, long mask)
  81{
  82        struct ms_ht_dev *dev_data = iio_priv(indio_dev);
  83        int i, ret;
  84
  85        switch (mask) {
  86        case IIO_CHAN_INFO_SAMP_FREQ:
  87                i = ARRAY_SIZE(htu21_samp_freq);
  88                while (i-- > 0)
  89                        if (val == htu21_samp_freq[i])
  90                                break;
  91                if (i < 0)
  92                        return -EINVAL;
  93                mutex_lock(&dev_data->lock);
  94                dev_data->res_index = i;
  95                ret = ms_sensors_write_resolution(dev_data, i);
  96                mutex_unlock(&dev_data->lock);
  97
  98                return ret;
  99        default:
 100                return -EINVAL;
 101        }
 102}
 103
 104static const struct iio_chan_spec htu21_channels[] = {
 105        {
 106                .type = IIO_TEMP,
 107                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED),
 108                .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 109         },
 110        {
 111                .type = IIO_HUMIDITYRELATIVE,
 112                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED),
 113                .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 114         }
 115};
 116
 117/*
 118 * Meas Spec recommendation is to not read temperature
 119 * on this driver part for MS8607
 120 */
 121static const struct iio_chan_spec ms8607_channels[] = {
 122        {
 123                .type = IIO_HUMIDITYRELATIVE,
 124                .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_PROCESSED),
 125                .info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SAMP_FREQ),
 126         }
 127};
 128
 129static ssize_t htu21_show_battery_low(struct device *dev,
 130                                      struct device_attribute *attr, char *buf)
 131{
 132        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 133        struct ms_ht_dev *dev_data = iio_priv(indio_dev);
 134
 135        return ms_sensors_show_battery_low(dev_data, buf);
 136}
 137
 138static ssize_t htu21_show_heater(struct device *dev,
 139                                 struct device_attribute *attr, char *buf)
 140{
 141        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 142        struct ms_ht_dev *dev_data = iio_priv(indio_dev);
 143
 144        return ms_sensors_show_heater(dev_data, buf);
 145}
 146
 147static ssize_t htu21_write_heater(struct device *dev,
 148                                  struct device_attribute *attr,
 149                                  const char *buf, size_t len)
 150{
 151        struct iio_dev *indio_dev = dev_to_iio_dev(dev);
 152        struct ms_ht_dev *dev_data = iio_priv(indio_dev);
 153
 154        return ms_sensors_write_heater(dev_data, buf, len);
 155}
 156
 157static IIO_CONST_ATTR_SAMP_FREQ_AVAIL(htu21_show_samp_freq);
 158static IIO_DEVICE_ATTR(battery_low, S_IRUGO,
 159                       htu21_show_battery_low, NULL, 0);
 160static IIO_DEVICE_ATTR(heater_enable, S_IRUGO | S_IWUSR,
 161                       htu21_show_heater, htu21_write_heater, 0);
 162
 163static struct attribute *htu21_attributes[] = {
 164        &iio_const_attr_sampling_frequency_available.dev_attr.attr,
 165        &iio_dev_attr_battery_low.dev_attr.attr,
 166        &iio_dev_attr_heater_enable.dev_attr.attr,
 167        NULL,
 168};
 169
 170static const struct attribute_group htu21_attribute_group = {
 171        .attrs = htu21_attributes,
 172};
 173
 174static const struct iio_info htu21_info = {
 175        .read_raw = htu21_read_raw,
 176        .write_raw = htu21_write_raw,
 177        .attrs = &htu21_attribute_group,
 178};
 179
 180static int htu21_probe(struct i2c_client *client,
 181                       const struct i2c_device_id *id)
 182{
 183        struct ms_ht_dev *dev_data;
 184        struct iio_dev *indio_dev;
 185        int ret;
 186        u64 serial_number;
 187
 188        if (!i2c_check_functionality(client->adapter,
 189                                     I2C_FUNC_SMBUS_WRITE_BYTE_DATA |
 190                                     I2C_FUNC_SMBUS_WRITE_BYTE |
 191                                     I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
 192                dev_err(&client->dev,
 193                        "Adapter does not support some i2c transaction\n");
 194                return -EOPNOTSUPP;
 195        }
 196
 197        indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*dev_data));
 198        if (!indio_dev)
 199                return -ENOMEM;
 200
 201        dev_data = iio_priv(indio_dev);
 202        dev_data->client = client;
 203        dev_data->res_index = 0;
 204        mutex_init(&dev_data->lock);
 205
 206        indio_dev->info = &htu21_info;
 207        indio_dev->name = id->name;
 208        indio_dev->modes = INDIO_DIRECT_MODE;
 209
 210        if (id->driver_data == MS8607) {
 211                indio_dev->channels = ms8607_channels;
 212                indio_dev->num_channels = ARRAY_SIZE(ms8607_channels);
 213        } else {
 214                indio_dev->channels = htu21_channels;
 215                indio_dev->num_channels = ARRAY_SIZE(htu21_channels);
 216        }
 217
 218        i2c_set_clientdata(client, indio_dev);
 219
 220        ret = ms_sensors_reset(client, HTU21_RESET, 15000);
 221        if (ret)
 222                return ret;
 223
 224        ret = ms_sensors_read_serial(client, &serial_number);
 225        if (ret)
 226                return ret;
 227        dev_info(&client->dev, "Serial number : %llx", serial_number);
 228
 229        return devm_iio_device_register(&client->dev, indio_dev);
 230}
 231
 232static const struct i2c_device_id htu21_id[] = {
 233        {"htu21", HTU21},
 234        {"ms8607-humidity", MS8607},
 235        {}
 236};
 237MODULE_DEVICE_TABLE(i2c, htu21_id);
 238
 239static const struct of_device_id htu21_of_match[] = {
 240        { .compatible = "meas,htu21", },
 241        { .compatible = "meas,ms8607-humidity", },
 242        { },
 243};
 244MODULE_DEVICE_TABLE(of, htu21_of_match);
 245
 246static struct i2c_driver htu21_driver = {
 247        .probe = htu21_probe,
 248        .id_table = htu21_id,
 249        .driver = {
 250                   .name = "htu21",
 251                   .of_match_table = htu21_of_match,
 252                   },
 253};
 254
 255module_i2c_driver(htu21_driver);
 256
 257MODULE_DESCRIPTION("Measurement-Specialties htu21 temperature and humidity driver");
 258MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>");
 259MODULE_AUTHOR("Ludovic Tancerel <ludovic.tancerel@maplehightech.com>");
 260MODULE_LICENSE("GPL v2");
 261