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

hwmon: Add driver for ADT7411 voltage and temperature sensor

Add basic support for the ADT7411. Reads out all conversion results (via I2C,
SPI yet missing) and allows some on-the-fly configuration. Tested with a
custom board.

Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Signed-off-by: Wolfram Sang <w.sang@pengutronix.de>
Signed-off-by: Jean Delvare <khali@linux-fr.org>

authored by

Wolfram Sang and committed by
Jean Delvare
d84ca5b3 5852f960

+412
+42
Documentation/hwmon/adt7411
··· 1 + Kernel driver adt7411 2 + ===================== 3 + 4 + Supported chips: 5 + * Analog Devices ADT7411 6 + Prefix: 'adt7411' 7 + Addresses scanned: 0x48, 0x4a, 0x4b 8 + Datasheet: Publicly available at the Analog Devices website 9 + 10 + Author: Wolfram Sang (based on adt7470 by Darrick J. Wong) 11 + 12 + Description 13 + ----------- 14 + 15 + This driver implements support for the Analog Devices ADT7411 chip. There may 16 + be other chips that implement this interface. 17 + 18 + The ADT7411 can use an I2C/SMBus compatible 2-wire interface or an 19 + SPI-compatible 4-wire interface. It provides a 10-bit analog to digital 20 + converter which measures 1 temperature, vdd and 8 input voltages. It has an 21 + internal temperature sensor, but an external one can also be connected (one 22 + loses 2 inputs then). There are high- and low-limit registers for all inputs. 23 + 24 + Check the datasheet for details. 25 + 26 + sysfs-Interface 27 + --------------- 28 + 29 + in0_input - vdd voltage input 30 + in[1-8]_input - analog 1-8 input 31 + temp1_input - temperature input 32 + 33 + Besides standard interfaces, this driver adds (0 = off, 1 = on): 34 + 35 + adc_ref_vdd - Use vdd as reference instead of 2.25 V 36 + fast_sampling - Sample at 22.5 kHz instead of 1.4 kHz, but drop filters 37 + no_average - Turn off averaging over 16 samples 38 + 39 + Notes 40 + ----- 41 + 42 + SPI, external temperature sensor and limit registers are not supported yet.
+10
drivers/hwmon/Kconfig
··· 170 170 This driver can also be built as a module. If so, the module 171 171 will be called adm9240. 172 172 173 + config SENSORS_ADT7411 174 + tristate "Analog Devices ADT7411" 175 + depends on I2C && EXPERIMENTAL 176 + help 177 + If you say yes here you get support for the Analog Devices 178 + ADT7411 voltage and temperature monitoring chip. 179 + 180 + This driver can also be built as a module. If so, the module 181 + will be called adt7411. 182 + 173 183 config SENSORS_ADT7462 174 184 tristate "Analog Devices ADT7462" 175 185 depends on I2C && EXPERIMENTAL
+1
drivers/hwmon/Makefile
··· 29 29 obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o 30 30 obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o 31 31 obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o 32 + obj-$(CONFIG_SENSORS_ADT7411) += adt7411.o 32 33 obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o 33 34 obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o 34 35 obj-$(CONFIG_SENSORS_ADT7473) += adt7473.o
+359
drivers/hwmon/adt7411.c
··· 1 + /* 2 + * Driver for the ADT7411 (I2C/SPI 8 channel 10 bit ADC & temperature-sensor) 3 + * 4 + * Copyright (C) 2008, 2010 Pengutronix 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 version 2 as 8 + * published by the Free Software Foundation. 9 + * 10 + * TODO: SPI, support for external temperature sensor 11 + * use power-down mode for suspend?, interrupt handling? 12 + */ 13 + 14 + #include <linux/kernel.h> 15 + #include <linux/module.h> 16 + #include <linux/init.h> 17 + #include <linux/err.h> 18 + #include <linux/delay.h> 19 + #include <linux/mutex.h> 20 + #include <linux/jiffies.h> 21 + #include <linux/i2c.h> 22 + #include <linux/hwmon.h> 23 + #include <linux/hwmon-sysfs.h> 24 + 25 + #define ADT7411_REG_INT_TEMP_VDD_LSB 0x03 26 + #define ADT7411_REG_EXT_TEMP_AIN14_LSB 0x04 27 + #define ADT7411_REG_VDD_MSB 0x06 28 + #define ADT7411_REG_INT_TEMP_MSB 0x07 29 + #define ADT7411_REG_EXT_TEMP_AIN1_MSB 0x08 30 + 31 + #define ADT7411_REG_CFG1 0x18 32 + #define ADT7411_CFG1_START_MONITOR (1 << 0) 33 + 34 + #define ADT7411_REG_CFG2 0x19 35 + #define ADT7411_CFG2_DISABLE_AVG (1 << 5) 36 + 37 + #define ADT7411_REG_CFG3 0x1a 38 + #define ADT7411_CFG3_ADC_CLK_225 (1 << 0) 39 + #define ADT7411_CFG3_REF_VDD (1 << 4) 40 + 41 + #define ADT7411_REG_DEVICE_ID 0x4d 42 + #define ADT7411_REG_MANUFACTURER_ID 0x4e 43 + 44 + #define ADT7411_DEVICE_ID 0x2 45 + #define ADT7411_MANUFACTURER_ID 0x41 46 + 47 + static const unsigned short normal_i2c[] = { 0x48, 0x4a, 0x4b, I2C_CLIENT_END }; 48 + 49 + struct adt7411_data { 50 + struct mutex device_lock; /* for "atomic" device accesses */ 51 + unsigned long next_update; 52 + int vref_cached; 53 + bool ref_is_vdd; 54 + struct device *hwmon_dev; 55 + }; 56 + 57 + /* 58 + * When reading a register containing (up to 4) lsb, all associated 59 + * msb-registers get locked by the hardware. After _one_ of those msb is read, 60 + * _all_ are unlocked. In order to use this locking correctly, reading lsb/msb 61 + * is protected here with a mutex, too. 62 + */ 63 + static int adt7411_read_10_bit(struct i2c_client *client, u8 lsb_reg, 64 + u8 msb_reg, u8 lsb_shift) 65 + { 66 + struct adt7411_data *data = i2c_get_clientdata(client); 67 + int val, tmp; 68 + 69 + mutex_lock(&data->device_lock); 70 + 71 + val = i2c_smbus_read_byte_data(client, lsb_reg); 72 + if (val < 0) 73 + goto exit_unlock; 74 + 75 + tmp = (val >> lsb_shift) & 3; 76 + val = i2c_smbus_read_byte_data(client, msb_reg); 77 + 78 + if (val >= 0) 79 + val = (val << 2) | tmp; 80 + 81 + exit_unlock: 82 + mutex_unlock(&data->device_lock); 83 + 84 + return val; 85 + } 86 + 87 + static int adt7411_modify_bit(struct i2c_client *client, u8 reg, u8 bit, 88 + bool flag) 89 + { 90 + struct adt7411_data *data = i2c_get_clientdata(client); 91 + int ret, val; 92 + 93 + mutex_lock(&data->device_lock); 94 + 95 + ret = i2c_smbus_read_byte_data(client, reg); 96 + if (ret < 0) 97 + goto exit_unlock; 98 + 99 + if (flag) 100 + val = ret | bit; 101 + else 102 + val = ret & ~bit; 103 + 104 + ret = i2c_smbus_write_byte_data(client, reg, val); 105 + 106 + exit_unlock: 107 + mutex_unlock(&data->device_lock); 108 + return ret; 109 + } 110 + 111 + static ssize_t adt7411_show_vdd(struct device *dev, 112 + struct device_attribute *attr, char *buf) 113 + { 114 + struct i2c_client *client = to_i2c_client(dev); 115 + int ret = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, 116 + ADT7411_REG_VDD_MSB, 2); 117 + 118 + return ret < 0 ? ret : sprintf(buf, "%u\n", ret * 7000 / 1024); 119 + } 120 + 121 + static ssize_t adt7411_show_temp(struct device *dev, 122 + struct device_attribute *attr, char *buf) 123 + { 124 + struct i2c_client *client = to_i2c_client(dev); 125 + int val = adt7411_read_10_bit(client, ADT7411_REG_INT_TEMP_VDD_LSB, 126 + ADT7411_REG_INT_TEMP_MSB, 0); 127 + 128 + if (val < 0) 129 + return val; 130 + 131 + val = val & 0x200 ? val - 0x400 : val; /* 10 bit signed */ 132 + 133 + return sprintf(buf, "%d\n", val * 250); 134 + } 135 + 136 + static ssize_t adt7411_show_input(struct device *dev, 137 + struct device_attribute *attr, char *buf) 138 + { 139 + int nr = to_sensor_dev_attr(attr)->index; 140 + struct i2c_client *client = to_i2c_client(dev); 141 + struct adt7411_data *data = i2c_get_clientdata(client); 142 + int val; 143 + u8 lsb_reg, lsb_shift; 144 + 145 + if (time_after_eq(jiffies, data->next_update)) { 146 + val = i2c_smbus_read_byte_data(client, ADT7411_REG_CFG3); 147 + if (val < 0) 148 + return val; 149 + data->ref_is_vdd = val & ADT7411_CFG3_REF_VDD; 150 + 151 + if (data->ref_is_vdd) { 152 + val = adt7411_read_10_bit(client, 153 + ADT7411_REG_INT_TEMP_VDD_LSB, 154 + ADT7411_REG_VDD_MSB, 2); 155 + if (val < 0) 156 + return val; 157 + 158 + data->vref_cached = val * 7000 / 1024; 159 + } else { 160 + data->vref_cached = 2250; 161 + } 162 + 163 + data->next_update = jiffies + HZ; 164 + } 165 + 166 + lsb_reg = ADT7411_REG_EXT_TEMP_AIN14_LSB + (nr >> 2); 167 + lsb_shift = 2 * (nr & 0x03); 168 + val = adt7411_read_10_bit(client, lsb_reg, 169 + ADT7411_REG_EXT_TEMP_AIN1_MSB + nr, lsb_shift); 170 + 171 + return val < 0 ? val : 172 + sprintf(buf, "%u\n", val * data->vref_cached / 1024); 173 + } 174 + 175 + static ssize_t adt7411_show_bit(struct device *dev, 176 + struct device_attribute *attr, char *buf) 177 + { 178 + struct sensor_device_attribute_2 *attr2 = to_sensor_dev_attr_2(attr); 179 + struct i2c_client *client = to_i2c_client(dev); 180 + int ret = i2c_smbus_read_byte_data(client, attr2->index); 181 + 182 + return ret < 0 ? ret : sprintf(buf, "%u\n", !!(ret & attr2->nr)); 183 + } 184 + 185 + static ssize_t adt7411_set_bit(struct device *dev, 186 + struct device_attribute *attr, const char *buf, 187 + size_t count) 188 + { 189 + struct sensor_device_attribute_2 *s_attr2 = to_sensor_dev_attr_2(attr); 190 + struct i2c_client *client = to_i2c_client(dev); 191 + struct adt7411_data *data = i2c_get_clientdata(client); 192 + int ret; 193 + unsigned long flag; 194 + 195 + ret = strict_strtoul(buf, 0, &flag); 196 + if (ret || flag > 1) 197 + return -EINVAL; 198 + 199 + ret = adt7411_modify_bit(client, s_attr2->index, s_attr2->nr, flag); 200 + 201 + /* force update */ 202 + data->next_update = jiffies; 203 + 204 + return ret < 0 ? ret : count; 205 + } 206 + 207 + #define ADT7411_BIT_ATTR(__name, __reg, __bit) \ 208 + SENSOR_DEVICE_ATTR_2(__name, S_IRUGO | S_IWUSR, adt7411_show_bit, \ 209 + adt7411_set_bit, __bit, __reg) 210 + 211 + static DEVICE_ATTR(temp1_input, S_IRUGO, adt7411_show_temp, NULL); 212 + static DEVICE_ATTR(in0_input, S_IRUGO, adt7411_show_vdd, NULL); 213 + static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, adt7411_show_input, NULL, 0); 214 + static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, adt7411_show_input, NULL, 1); 215 + static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, adt7411_show_input, NULL, 2); 216 + static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, adt7411_show_input, NULL, 3); 217 + static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, adt7411_show_input, NULL, 4); 218 + static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, adt7411_show_input, NULL, 5); 219 + static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, adt7411_show_input, NULL, 6); 220 + static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, adt7411_show_input, NULL, 7); 221 + static ADT7411_BIT_ATTR(no_average, ADT7411_REG_CFG2, ADT7411_CFG2_DISABLE_AVG); 222 + static ADT7411_BIT_ATTR(fast_sampling, ADT7411_REG_CFG3, ADT7411_CFG3_ADC_CLK_225); 223 + static ADT7411_BIT_ATTR(adc_ref_vdd, ADT7411_REG_CFG3, ADT7411_CFG3_REF_VDD); 224 + 225 + static struct attribute *adt7411_attrs[] = { 226 + &dev_attr_temp1_input.attr, 227 + &dev_attr_in0_input.attr, 228 + &sensor_dev_attr_in1_input.dev_attr.attr, 229 + &sensor_dev_attr_in2_input.dev_attr.attr, 230 + &sensor_dev_attr_in3_input.dev_attr.attr, 231 + &sensor_dev_attr_in4_input.dev_attr.attr, 232 + &sensor_dev_attr_in5_input.dev_attr.attr, 233 + &sensor_dev_attr_in6_input.dev_attr.attr, 234 + &sensor_dev_attr_in7_input.dev_attr.attr, 235 + &sensor_dev_attr_in8_input.dev_attr.attr, 236 + &sensor_dev_attr_no_average.dev_attr.attr, 237 + &sensor_dev_attr_fast_sampling.dev_attr.attr, 238 + &sensor_dev_attr_adc_ref_vdd.dev_attr.attr, 239 + NULL 240 + }; 241 + 242 + static const struct attribute_group adt7411_attr_grp = { 243 + .attrs = adt7411_attrs, 244 + }; 245 + 246 + static int adt7411_detect(struct i2c_client *client, 247 + struct i2c_board_info *info) 248 + { 249 + int val; 250 + 251 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE_DATA)) 252 + return -ENODEV; 253 + 254 + val = i2c_smbus_read_byte_data(client, ADT7411_REG_MANUFACTURER_ID); 255 + if (val < 0 || val != ADT7411_MANUFACTURER_ID) { 256 + dev_dbg(&client->dev, "Wrong manufacturer ID. Got %d, " 257 + "expected %d\n", val, ADT7411_MANUFACTURER_ID); 258 + return -ENODEV; 259 + } 260 + 261 + val = i2c_smbus_read_byte_data(client, ADT7411_REG_DEVICE_ID); 262 + if (val < 0 || val != ADT7411_DEVICE_ID) { 263 + dev_dbg(&client->dev, "Wrong device ID. Got %d, " 264 + "expected %d\n", val, ADT7411_DEVICE_ID); 265 + return -ENODEV; 266 + } 267 + 268 + strlcpy(info->type, "adt7411", I2C_NAME_SIZE); 269 + 270 + return 0; 271 + } 272 + 273 + static int __devinit adt7411_probe(struct i2c_client *client, 274 + const struct i2c_device_id *id) 275 + { 276 + struct adt7411_data *data; 277 + int ret; 278 + 279 + data = kzalloc(sizeof(*data), GFP_KERNEL); 280 + if (!data) 281 + return -ENOMEM; 282 + 283 + i2c_set_clientdata(client, data); 284 + mutex_init(&data->device_lock); 285 + 286 + ret = adt7411_modify_bit(client, ADT7411_REG_CFG1, 287 + ADT7411_CFG1_START_MONITOR, 1); 288 + if (ret < 0) 289 + goto exit_free; 290 + 291 + /* force update on first occasion */ 292 + data->next_update = jiffies; 293 + 294 + ret = sysfs_create_group(&client->dev.kobj, &adt7411_attr_grp); 295 + if (ret) 296 + goto exit_free; 297 + 298 + data->hwmon_dev = hwmon_device_register(&client->dev); 299 + if (IS_ERR(data->hwmon_dev)) { 300 + ret = PTR_ERR(data->hwmon_dev); 301 + goto exit_remove; 302 + } 303 + 304 + dev_info(&client->dev, "successfully registered\n"); 305 + 306 + return 0; 307 + 308 + exit_remove: 309 + sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp); 310 + exit_free: 311 + i2c_set_clientdata(client, NULL); 312 + kfree(data); 313 + return ret; 314 + } 315 + 316 + static int __devexit adt7411_remove(struct i2c_client *client) 317 + { 318 + struct adt7411_data *data = i2c_get_clientdata(client); 319 + 320 + hwmon_device_unregister(data->hwmon_dev); 321 + sysfs_remove_group(&client->dev.kobj, &adt7411_attr_grp); 322 + i2c_set_clientdata(client, NULL); 323 + kfree(data); 324 + return 0; 325 + } 326 + 327 + static const struct i2c_device_id adt7411_id[] = { 328 + { "adt7411", 0 }, 329 + { } 330 + }; 331 + 332 + static struct i2c_driver adt7411_driver = { 333 + .driver = { 334 + .name = "adt7411", 335 + }, 336 + .probe = adt7411_probe, 337 + .remove = __devexit_p(adt7411_remove), 338 + .id_table = adt7411_id, 339 + .detect = adt7411_detect, 340 + .address_list = normal_i2c, 341 + .class = I2C_CLASS_HWMON, 342 + }; 343 + 344 + static int __init sensors_adt7411_init(void) 345 + { 346 + return i2c_add_driver(&adt7411_driver); 347 + } 348 + module_init(sensors_adt7411_init) 349 + 350 + static void __exit sensors_adt7411_exit(void) 351 + { 352 + i2c_del_driver(&adt7411_driver); 353 + } 354 + module_exit(sensors_adt7411_exit) 355 + 356 + MODULE_AUTHOR("Sascha Hauer <s.hauer@pengutronix.de> and " 357 + "Wolfram Sang <w.sang@pengutronix.de>"); 358 + MODULE_DESCRIPTION("ADT7411 driver"); 359 + MODULE_LICENSE("GPL v2");