Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v3.12 199 lines 5.2 kB view raw
1/* 2 * Measurement Specialties HTU21D humidity and temperature sensor driver 3 * 4 * Copyright (C) 2013 William Markezana <william.markezana@meas-spec.com> 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 */ 17 18#include <linux/module.h> 19#include <linux/init.h> 20#include <linux/slab.h> 21#include <linux/i2c.h> 22#include <linux/hwmon.h> 23#include <linux/hwmon-sysfs.h> 24#include <linux/err.h> 25#include <linux/mutex.h> 26#include <linux/device.h> 27#include <linux/jiffies.h> 28 29/* HTU21 Commands */ 30#define HTU21_T_MEASUREMENT_HM 0xE3 31#define HTU21_RH_MEASUREMENT_HM 0xE5 32 33struct htu21 { 34 struct device *hwmon_dev; 35 struct mutex lock; 36 bool valid; 37 unsigned long last_update; 38 int temperature; 39 int humidity; 40}; 41 42static inline int htu21_temp_ticks_to_millicelsius(int ticks) 43{ 44 ticks &= ~0x0003; /* clear status bits */ 45 /* 46 * Formula T = -46.85 + 175.72 * ST / 2^16 from datasheet p14, 47 * optimized for integer fixed point (3 digits) arithmetic 48 */ 49 return ((21965 * ticks) >> 13) - 46850; 50} 51 52static inline int htu21_rh_ticks_to_per_cent_mille(int ticks) 53{ 54 ticks &= ~0x0003; /* clear status bits */ 55 /* 56 * Formula RH = -6 + 125 * SRH / 2^16 from datasheet p14, 57 * optimized for integer fixed point (3 digits) arithmetic 58 */ 59 return ((15625 * ticks) >> 13) - 6000; 60} 61 62static int htu21_update_measurements(struct i2c_client *client) 63{ 64 int ret = 0; 65 struct htu21 *htu21 = i2c_get_clientdata(client); 66 67 mutex_lock(&htu21->lock); 68 69 if (time_after(jiffies, htu21->last_update + HZ / 2) || 70 !htu21->valid) { 71 ret = i2c_smbus_read_word_swapped(client, 72 HTU21_T_MEASUREMENT_HM); 73 if (ret < 0) 74 goto out; 75 htu21->temperature = htu21_temp_ticks_to_millicelsius(ret); 76 ret = i2c_smbus_read_word_swapped(client, 77 HTU21_RH_MEASUREMENT_HM); 78 if (ret < 0) 79 goto out; 80 htu21->humidity = htu21_rh_ticks_to_per_cent_mille(ret); 81 htu21->last_update = jiffies; 82 htu21->valid = true; 83 } 84out: 85 mutex_unlock(&htu21->lock); 86 87 return ret >= 0 ? 0 : ret; 88} 89 90static ssize_t htu21_show_temperature(struct device *dev, 91 struct device_attribute *attr, char *buf) 92{ 93 struct i2c_client *client = to_i2c_client(dev); 94 struct htu21 *htu21 = i2c_get_clientdata(client); 95 int ret = htu21_update_measurements(client); 96 if (ret < 0) 97 return ret; 98 return sprintf(buf, "%d\n", htu21->temperature); 99} 100 101static ssize_t htu21_show_humidity(struct device *dev, 102 struct device_attribute *attr, char *buf) 103{ 104 struct i2c_client *client = to_i2c_client(dev); 105 struct htu21 *htu21 = i2c_get_clientdata(client); 106 int ret = htu21_update_measurements(client); 107 if (ret < 0) 108 return ret; 109 return sprintf(buf, "%d\n", htu21->humidity); 110} 111 112static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, 113 htu21_show_temperature, NULL, 0); 114static SENSOR_DEVICE_ATTR(humidity1_input, S_IRUGO, 115 htu21_show_humidity, NULL, 0); 116 117static struct attribute *htu21_attributes[] = { 118 &sensor_dev_attr_temp1_input.dev_attr.attr, 119 &sensor_dev_attr_humidity1_input.dev_attr.attr, 120 NULL 121}; 122 123static const struct attribute_group htu21_group = { 124 .attrs = htu21_attributes, 125}; 126 127static int htu21_probe(struct i2c_client *client, 128 const struct i2c_device_id *id) 129{ 130 struct htu21 *htu21; 131 int err; 132 133 if (!i2c_check_functionality(client->adapter, 134 I2C_FUNC_SMBUS_READ_WORD_DATA)) { 135 dev_err(&client->dev, 136 "adapter does not support SMBus word transactions\n"); 137 return -ENODEV; 138 } 139 140 htu21 = devm_kzalloc(&client->dev, sizeof(*htu21), GFP_KERNEL); 141 if (!htu21) 142 return -ENOMEM; 143 144 i2c_set_clientdata(client, htu21); 145 146 mutex_init(&htu21->lock); 147 148 err = sysfs_create_group(&client->dev.kobj, &htu21_group); 149 if (err) { 150 dev_dbg(&client->dev, "could not create sysfs files\n"); 151 return err; 152 } 153 htu21->hwmon_dev = hwmon_device_register(&client->dev); 154 if (IS_ERR(htu21->hwmon_dev)) { 155 dev_dbg(&client->dev, "unable to register hwmon device\n"); 156 err = PTR_ERR(htu21->hwmon_dev); 157 goto error; 158 } 159 160 dev_info(&client->dev, "initialized\n"); 161 162 return 0; 163 164error: 165 sysfs_remove_group(&client->dev.kobj, &htu21_group); 166 return err; 167} 168 169static int htu21_remove(struct i2c_client *client) 170{ 171 struct htu21 *htu21 = i2c_get_clientdata(client); 172 173 hwmon_device_unregister(htu21->hwmon_dev); 174 sysfs_remove_group(&client->dev.kobj, &htu21_group); 175 176 return 0; 177} 178 179static const struct i2c_device_id htu21_id[] = { 180 { "htu21", 0 }, 181 { } 182}; 183MODULE_DEVICE_TABLE(i2c, htu21_id); 184 185static struct i2c_driver htu21_driver = { 186 .class = I2C_CLASS_HWMON, 187 .driver = { 188 .name = "htu21", 189 }, 190 .probe = htu21_probe, 191 .remove = htu21_remove, 192 .id_table = htu21_id, 193}; 194 195module_i2c_driver(htu21_driver); 196 197MODULE_AUTHOR("William Markezana <william.markezana@meas-spec.com>"); 198MODULE_DESCRIPTION("MEAS HTU21D humidity and temperature sensor driver"); 199MODULE_LICENSE("GPL");