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.9 460 lines 13 kB view raw
1/* 2 * adt7410.c - Part of lm_sensors, Linux kernel modules for hardware 3 * monitoring 4 * This driver handles the ADT7410 and compatible digital temperature sensors. 5 * Hartmut Knaack <knaack.h@gmx.de> 2012-07-22 6 * based on lm75.c by Frodo Looijaard <frodol@dds.nl> 7 * and adt7410.c from iio-staging by Sonic Zhang <sonic.zhang@analog.com> 8 * 9 * This program is free software; you can redistribute it and/or modify 10 * it under the terms of the GNU General Public License as published by 11 * the Free Software Foundation; either version 2 of the License, or 12 * (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 * GNU General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 22 */ 23 24#include <linux/module.h> 25#include <linux/init.h> 26#include <linux/slab.h> 27#include <linux/jiffies.h> 28#include <linux/i2c.h> 29#include <linux/hwmon.h> 30#include <linux/hwmon-sysfs.h> 31#include <linux/err.h> 32#include <linux/mutex.h> 33#include <linux/delay.h> 34 35/* 36 * ADT7410 registers definition 37 */ 38 39#define ADT7410_TEMPERATURE 0 40#define ADT7410_STATUS 2 41#define ADT7410_CONFIG 3 42#define ADT7410_T_ALARM_HIGH 4 43#define ADT7410_T_ALARM_LOW 6 44#define ADT7410_T_CRIT 8 45#define ADT7410_T_HYST 0xA 46 47/* 48 * ADT7410 status 49 */ 50#define ADT7410_STAT_T_LOW (1 << 4) 51#define ADT7410_STAT_T_HIGH (1 << 5) 52#define ADT7410_STAT_T_CRIT (1 << 6) 53#define ADT7410_STAT_NOT_RDY (1 << 7) 54 55/* 56 * ADT7410 config 57 */ 58#define ADT7410_FAULT_QUEUE_MASK (1 << 0 | 1 << 1) 59#define ADT7410_CT_POLARITY (1 << 2) 60#define ADT7410_INT_POLARITY (1 << 3) 61#define ADT7410_EVENT_MODE (1 << 4) 62#define ADT7410_MODE_MASK (1 << 5 | 1 << 6) 63#define ADT7410_FULL (0 << 5 | 0 << 6) 64#define ADT7410_PD (1 << 5 | 1 << 6) 65#define ADT7410_RESOLUTION (1 << 7) 66 67/* 68 * ADT7410 masks 69 */ 70#define ADT7410_T13_VALUE_MASK 0xFFF8 71#define ADT7410_T_HYST_MASK 0xF 72 73/* straight from the datasheet */ 74#define ADT7410_TEMP_MIN (-55000) 75#define ADT7410_TEMP_MAX 150000 76 77enum adt7410_type { /* keep sorted in alphabetical order */ 78 adt7410, 79}; 80 81static const u8 ADT7410_REG_TEMP[4] = { 82 ADT7410_TEMPERATURE, /* input */ 83 ADT7410_T_ALARM_HIGH, /* high */ 84 ADT7410_T_ALARM_LOW, /* low */ 85 ADT7410_T_CRIT, /* critical */ 86}; 87 88/* Each client has this additional data */ 89struct adt7410_data { 90 struct device *hwmon_dev; 91 struct mutex update_lock; 92 u8 config; 93 u8 oldconfig; 94 bool valid; /* true if registers valid */ 95 unsigned long last_updated; /* In jiffies */ 96 s16 temp[4]; /* Register values, 97 0 = input 98 1 = high 99 2 = low 100 3 = critical */ 101 u8 hyst; /* hysteresis offset */ 102}; 103 104/* 105 * adt7410 register access by I2C 106 */ 107static int adt7410_temp_ready(struct i2c_client *client) 108{ 109 int i, status; 110 111 for (i = 0; i < 6; i++) { 112 status = i2c_smbus_read_byte_data(client, ADT7410_STATUS); 113 if (status < 0) 114 return status; 115 if (!(status & ADT7410_STAT_NOT_RDY)) 116 return 0; 117 msleep(60); 118 } 119 return -ETIMEDOUT; 120} 121 122static struct adt7410_data *adt7410_update_device(struct device *dev) 123{ 124 struct i2c_client *client = to_i2c_client(dev); 125 struct adt7410_data *data = i2c_get_clientdata(client); 126 struct adt7410_data *ret = data; 127 mutex_lock(&data->update_lock); 128 129 if (time_after(jiffies, data->last_updated + HZ + HZ / 2) 130 || !data->valid) { 131 int i, status; 132 133 dev_dbg(&client->dev, "Starting update\n"); 134 135 status = adt7410_temp_ready(client); /* check for new value */ 136 if (unlikely(status)) { 137 ret = ERR_PTR(status); 138 goto abort; 139 } 140 for (i = 0; i < ARRAY_SIZE(data->temp); i++) { 141 status = i2c_smbus_read_word_swapped(client, 142 ADT7410_REG_TEMP[i]); 143 if (unlikely(status < 0)) { 144 dev_dbg(dev, 145 "Failed to read value: reg %d, error %d\n", 146 ADT7410_REG_TEMP[i], status); 147 ret = ERR_PTR(status); 148 goto abort; 149 } 150 data->temp[i] = status; 151 } 152 status = i2c_smbus_read_byte_data(client, ADT7410_T_HYST); 153 if (unlikely(status < 0)) { 154 dev_dbg(dev, 155 "Failed to read value: reg %d, error %d\n", 156 ADT7410_T_HYST, status); 157 ret = ERR_PTR(status); 158 goto abort; 159 } 160 data->hyst = status; 161 data->last_updated = jiffies; 162 data->valid = true; 163 } 164 165abort: 166 mutex_unlock(&data->update_lock); 167 return ret; 168} 169 170static s16 ADT7410_TEMP_TO_REG(long temp) 171{ 172 return DIV_ROUND_CLOSEST(clamp_val(temp, ADT7410_TEMP_MIN, 173 ADT7410_TEMP_MAX) * 128, 1000); 174} 175 176static int ADT7410_REG_TO_TEMP(struct adt7410_data *data, s16 reg) 177{ 178 /* in 13 bit mode, bits 0-2 are status flags - mask them out */ 179 if (!(data->config & ADT7410_RESOLUTION)) 180 reg &= ADT7410_T13_VALUE_MASK; 181 /* 182 * temperature is stored in twos complement format, in steps of 183 * 1/128°C 184 */ 185 return DIV_ROUND_CLOSEST(reg * 1000, 128); 186} 187 188/*-----------------------------------------------------------------------*/ 189 190/* sysfs attributes for hwmon */ 191 192static ssize_t adt7410_show_temp(struct device *dev, 193 struct device_attribute *da, char *buf) 194{ 195 struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 196 struct adt7410_data *data = adt7410_update_device(dev); 197 198 if (IS_ERR(data)) 199 return PTR_ERR(data); 200 201 return sprintf(buf, "%d\n", ADT7410_REG_TO_TEMP(data, 202 data->temp[attr->index])); 203} 204 205static ssize_t adt7410_set_temp(struct device *dev, 206 struct device_attribute *da, 207 const char *buf, size_t count) 208{ 209 struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 210 struct i2c_client *client = to_i2c_client(dev); 211 struct adt7410_data *data = i2c_get_clientdata(client); 212 int nr = attr->index; 213 long temp; 214 int ret; 215 216 ret = kstrtol(buf, 10, &temp); 217 if (ret) 218 return ret; 219 220 mutex_lock(&data->update_lock); 221 data->temp[nr] = ADT7410_TEMP_TO_REG(temp); 222 ret = i2c_smbus_write_word_swapped(client, ADT7410_REG_TEMP[nr], 223 data->temp[nr]); 224 if (ret) 225 count = ret; 226 mutex_unlock(&data->update_lock); 227 return count; 228} 229 230static ssize_t adt7410_show_t_hyst(struct device *dev, 231 struct device_attribute *da, 232 char *buf) 233{ 234 struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 235 struct adt7410_data *data; 236 int nr = attr->index; 237 int hyst; 238 239 data = adt7410_update_device(dev); 240 if (IS_ERR(data)) 241 return PTR_ERR(data); 242 hyst = (data->hyst & ADT7410_T_HYST_MASK) * 1000; 243 244 /* 245 * hysteresis is stored as a 4 bit offset in the device, convert it 246 * to an absolute value 247 */ 248 if (nr == 2) /* min has positive offset, others have negative */ 249 hyst = -hyst; 250 return sprintf(buf, "%d\n", 251 ADT7410_REG_TO_TEMP(data, data->temp[nr]) - hyst); 252} 253 254static ssize_t adt7410_set_t_hyst(struct device *dev, 255 struct device_attribute *da, 256 const char *buf, size_t count) 257{ 258 struct i2c_client *client = to_i2c_client(dev); 259 struct adt7410_data *data = i2c_get_clientdata(client); 260 int limit, ret; 261 long hyst; 262 263 ret = kstrtol(buf, 10, &hyst); 264 if (ret) 265 return ret; 266 /* convert absolute hysteresis value to a 4 bit delta value */ 267 limit = ADT7410_REG_TO_TEMP(data, data->temp[1]); 268 hyst = clamp_val(hyst, ADT7410_TEMP_MIN, ADT7410_TEMP_MAX); 269 data->hyst = clamp_val(DIV_ROUND_CLOSEST(limit - hyst, 1000), 0, 270 ADT7410_T_HYST_MASK); 271 ret = i2c_smbus_write_byte_data(client, ADT7410_T_HYST, data->hyst); 272 if (ret) 273 return ret; 274 275 return count; 276} 277 278static ssize_t adt7410_show_alarm(struct device *dev, 279 struct device_attribute *da, 280 char *buf) 281{ 282 struct i2c_client *client = to_i2c_client(dev); 283 struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 284 int ret; 285 286 ret = i2c_smbus_read_byte_data(client, ADT7410_STATUS); 287 if (ret < 0) 288 return ret; 289 290 return sprintf(buf, "%d\n", !!(ret & attr->index)); 291} 292 293static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adt7410_show_temp, NULL, 0); 294static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, 295 adt7410_show_temp, adt7410_set_temp, 1); 296static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, 297 adt7410_show_temp, adt7410_set_temp, 2); 298static SENSOR_DEVICE_ATTR(temp1_crit, S_IWUSR | S_IRUGO, 299 adt7410_show_temp, adt7410_set_temp, 3); 300static SENSOR_DEVICE_ATTR(temp1_max_hyst, S_IWUSR | S_IRUGO, 301 adt7410_show_t_hyst, adt7410_set_t_hyst, 1); 302static SENSOR_DEVICE_ATTR(temp1_min_hyst, S_IRUGO, 303 adt7410_show_t_hyst, NULL, 2); 304static SENSOR_DEVICE_ATTR(temp1_crit_hyst, S_IRUGO, 305 adt7410_show_t_hyst, NULL, 3); 306static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, adt7410_show_alarm, 307 NULL, ADT7410_STAT_T_LOW); 308static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, adt7410_show_alarm, 309 NULL, ADT7410_STAT_T_HIGH); 310static SENSOR_DEVICE_ATTR(temp1_crit_alarm, S_IRUGO, adt7410_show_alarm, 311 NULL, ADT7410_STAT_T_CRIT); 312 313static struct attribute *adt7410_attributes[] = { 314 &sensor_dev_attr_temp1_input.dev_attr.attr, 315 &sensor_dev_attr_temp1_max.dev_attr.attr, 316 &sensor_dev_attr_temp1_min.dev_attr.attr, 317 &sensor_dev_attr_temp1_crit.dev_attr.attr, 318 &sensor_dev_attr_temp1_max_hyst.dev_attr.attr, 319 &sensor_dev_attr_temp1_min_hyst.dev_attr.attr, 320 &sensor_dev_attr_temp1_crit_hyst.dev_attr.attr, 321 &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, 322 &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, 323 &sensor_dev_attr_temp1_crit_alarm.dev_attr.attr, 324 NULL 325}; 326 327static const struct attribute_group adt7410_group = { 328 .attrs = adt7410_attributes, 329}; 330 331/*-----------------------------------------------------------------------*/ 332 333/* device probe and removal */ 334 335static int adt7410_probe(struct i2c_client *client, 336 const struct i2c_device_id *id) 337{ 338 struct adt7410_data *data; 339 int ret; 340 341 if (!i2c_check_functionality(client->adapter, 342 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA)) 343 return -ENODEV; 344 345 data = devm_kzalloc(&client->dev, sizeof(struct adt7410_data), 346 GFP_KERNEL); 347 if (!data) 348 return -ENOMEM; 349 350 i2c_set_clientdata(client, data); 351 mutex_init(&data->update_lock); 352 353 /* configure as specified */ 354 ret = i2c_smbus_read_byte_data(client, ADT7410_CONFIG); 355 if (ret < 0) { 356 dev_dbg(&client->dev, "Can't read config? %d\n", ret); 357 return ret; 358 } 359 data->oldconfig = ret; 360 /* 361 * Set to 16 bit resolution, continous conversion and comparator mode. 362 */ 363 ret &= ~ADT7410_MODE_MASK; 364 data->config = ret | ADT7410_FULL | ADT7410_RESOLUTION | 365 ADT7410_EVENT_MODE; 366 if (data->config != data->oldconfig) { 367 ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, 368 data->config); 369 if (ret) 370 return ret; 371 } 372 dev_dbg(&client->dev, "Config %02x\n", data->config); 373 374 /* Register sysfs hooks */ 375 ret = sysfs_create_group(&client->dev.kobj, &adt7410_group); 376 if (ret) 377 goto exit_restore; 378 379 data->hwmon_dev = hwmon_device_register(&client->dev); 380 if (IS_ERR(data->hwmon_dev)) { 381 ret = PTR_ERR(data->hwmon_dev); 382 goto exit_remove; 383 } 384 385 dev_info(&client->dev, "sensor '%s'\n", client->name); 386 387 return 0; 388 389exit_remove: 390 sysfs_remove_group(&client->dev.kobj, &adt7410_group); 391exit_restore: 392 i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->oldconfig); 393 return ret; 394} 395 396static int adt7410_remove(struct i2c_client *client) 397{ 398 struct adt7410_data *data = i2c_get_clientdata(client); 399 400 hwmon_device_unregister(data->hwmon_dev); 401 sysfs_remove_group(&client->dev.kobj, &adt7410_group); 402 if (data->oldconfig != data->config) 403 i2c_smbus_write_byte_data(client, ADT7410_CONFIG, 404 data->oldconfig); 405 return 0; 406} 407 408static const struct i2c_device_id adt7410_ids[] = { 409 { "adt7410", adt7410, }, 410 { "adt7420", adt7410, }, 411 { /* LIST END */ } 412}; 413MODULE_DEVICE_TABLE(i2c, adt7410_ids); 414 415#ifdef CONFIG_PM_SLEEP 416static int adt7410_suspend(struct device *dev) 417{ 418 int ret; 419 struct i2c_client *client = to_i2c_client(dev); 420 struct adt7410_data *data = i2c_get_clientdata(client); 421 422 ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, 423 data->config | ADT7410_PD); 424 return ret; 425} 426 427static int adt7410_resume(struct device *dev) 428{ 429 int ret; 430 struct i2c_client *client = to_i2c_client(dev); 431 struct adt7410_data *data = i2c_get_clientdata(client); 432 433 ret = i2c_smbus_write_byte_data(client, ADT7410_CONFIG, data->config); 434 return ret; 435} 436 437static SIMPLE_DEV_PM_OPS(adt7410_dev_pm_ops, adt7410_suspend, adt7410_resume); 438 439#define ADT7410_DEV_PM_OPS (&adt7410_dev_pm_ops) 440#else 441#define ADT7410_DEV_PM_OPS NULL 442#endif /* CONFIG_PM */ 443 444static struct i2c_driver adt7410_driver = { 445 .class = I2C_CLASS_HWMON, 446 .driver = { 447 .name = "adt7410", 448 .pm = ADT7410_DEV_PM_OPS, 449 }, 450 .probe = adt7410_probe, 451 .remove = adt7410_remove, 452 .id_table = adt7410_ids, 453 .address_list = I2C_ADDRS(0x48, 0x49, 0x4a, 0x4b), 454}; 455 456module_i2c_driver(adt7410_driver); 457 458MODULE_AUTHOR("Hartmut Knaack"); 459MODULE_DESCRIPTION("ADT7410/ADT7420 driver"); 460MODULE_LICENSE("GPL");