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

hwmon: Support for Dallas Semiconductor DS620

Driver for Dallas Semiconductor DS620 temperature sensor and thermostat

Signed-off-by: Roland Stigge <stigge@antcom.de>
Signed-off-by: Guenter Roeck <guenter.roeck@ericsson.com>

authored by

Roland Stigge and committed by
Guenter Roeck
60994698 430400b8

+403
+34
Documentation/hwmon/ds620
··· 1 + Kernel driver ds620 2 + =================== 3 + 4 + Supported chips: 5 + * Dallas Semiconductor DS620 6 + Prefix: 'ds620' 7 + Datasheet: Publicly available at the Dallas Semiconductor website 8 + http://www.dalsemi.com/ 9 + 10 + Authors: 11 + Roland Stigge <stigge@antcom.de> 12 + based on ds1621.c by 13 + Christian W. Zuckschwerdt <zany@triq.net> 14 + 15 + Description 16 + ----------- 17 + 18 + The DS620 is a (one instance) digital thermometer and thermostat. It has both 19 + high and low temperature limits which can be user defined (i.e. programmed 20 + into non-volatile on-chip registers). Temperature range is -55 degree Celsius 21 + to +125. Between 0 and 70 degree Celsius, accuracy is 0.5 Kelvin. The value 22 + returned via sysfs displays post decimal positions. 23 + 24 + The thermostat function works as follows: When configured via platform_data 25 + (struct ds620_platform_data) .pomode == 0 (default), the thermostat output pin 26 + PO is always low. If .pomode == 1, the thermostat is in PO_LOW mode. I.e., the 27 + output pin PO becomes active when the temperature falls below temp1_min and 28 + stays active until the temperature goes above temp1_max. 29 + 30 + Likewise, with .pomode == 2, the thermostat is in PO_HIGH mode. I.e., the PO 31 + output pin becomes active when the temperature goes above temp1_max and stays 32 + active until the temperature falls below temp1_min. 33 + 34 + The PO output pin of the DS620 operates active-low.
+10
drivers/hwmon/Kconfig
··· 274 274 This driver can also be built as a module. If so, the module 275 275 will be called atxp1. 276 276 277 + config SENSORS_DS620 278 + tristate "Dallas Semiconductor DS620" 279 + depends on I2C 280 + help 281 + If you say yes here you get support for Dallas Semiconductor 282 + DS620 sensor chip. 283 + 284 + This driver can also be built as a module. If so, the module 285 + will be called ds620. 286 + 277 287 config SENSORS_DS1621 278 288 tristate "Dallas Semiconductor DS1621 and DS1625" 279 289 depends on I2C
+1
drivers/hwmon/Makefile
··· 41 41 obj-$(CONFIG_SENSORS_CORETEMP) += coretemp.o 42 42 obj-$(CONFIG_SENSORS_PKGTEMP) += pkgtemp.o 43 43 obj-$(CONFIG_SENSORS_DME1737) += dme1737.o 44 + obj-$(CONFIG_SENSORS_DS620) += ds620.o 44 45 obj-$(CONFIG_SENSORS_DS1621) += ds1621.o 45 46 obj-$(CONFIG_SENSORS_EMC1403) += emc1403.o 46 47 obj-$(CONFIG_SENSORS_EMC2103) += emc2103.o
+337
drivers/hwmon/ds620.c
··· 1 + /* 2 + * ds620.c - Support for temperature sensor and thermostat DS620 3 + * 4 + * Copyright (C) 2010, 2011 Roland Stigge <stigge@antcom.de> 5 + * 6 + * based on ds1621.c by Christian W. Zuckschwerdt <zany@triq.net> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License as published by 10 + * the Free Software Foundation; either version 2 of the License, or 11 + * (at your option) any later version. 12 + * 13 + * This program is distributed in the hope that it will be useful, 14 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 + * GNU General Public License for more details. 17 + * 18 + * You should have received a copy of the GNU General Public License 19 + * along with this program; if not, write to the Free Software 20 + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 21 + */ 22 + 23 + #include <linux/module.h> 24 + #include <linux/init.h> 25 + #include <linux/slab.h> 26 + #include <linux/jiffies.h> 27 + #include <linux/i2c.h> 28 + #include <linux/hwmon.h> 29 + #include <linux/hwmon-sysfs.h> 30 + #include <linux/err.h> 31 + #include <linux/mutex.h> 32 + #include <linux/sysfs.h> 33 + #include <linux/i2c/ds620.h> 34 + 35 + /* 36 + * Many DS620 constants specified below 37 + * 15 14 13 12 11 10 09 08 38 + * |Done|NVB |THF |TLF |R1 |R0 |AUTOC|1SHOT| 39 + * 40 + * 07 06 05 04 03 02 01 00 41 + * |PO2 |PO1 |A2 |A1 |A0 | | | | 42 + */ 43 + #define DS620_REG_CONFIG_DONE 0x8000 44 + #define DS620_REG_CONFIG_NVB 0x4000 45 + #define DS620_REG_CONFIG_THF 0x2000 46 + #define DS620_REG_CONFIG_TLF 0x1000 47 + #define DS620_REG_CONFIG_R1 0x0800 48 + #define DS620_REG_CONFIG_R0 0x0400 49 + #define DS620_REG_CONFIG_AUTOC 0x0200 50 + #define DS620_REG_CONFIG_1SHOT 0x0100 51 + #define DS620_REG_CONFIG_PO2 0x0080 52 + #define DS620_REG_CONFIG_PO1 0x0040 53 + #define DS620_REG_CONFIG_A2 0x0020 54 + #define DS620_REG_CONFIG_A1 0x0010 55 + #define DS620_REG_CONFIG_A0 0x0008 56 + 57 + /* The DS620 registers */ 58 + static const u8 DS620_REG_TEMP[3] = { 59 + 0xAA, /* input, word, RO */ 60 + 0xA2, /* min, word, RW */ 61 + 0xA0, /* max, word, RW */ 62 + }; 63 + 64 + #define DS620_REG_CONF 0xAC /* word, RW */ 65 + #define DS620_COM_START 0x51 /* no data */ 66 + #define DS620_COM_STOP 0x22 /* no data */ 67 + 68 + /* Each client has this additional data */ 69 + struct ds620_data { 70 + struct device *hwmon_dev; 71 + struct mutex update_lock; 72 + char valid; /* !=0 if following fields are valid */ 73 + unsigned long last_updated; /* In jiffies */ 74 + 75 + u16 temp[3]; /* Register values, word */ 76 + }; 77 + 78 + /* 79 + * Temperature registers are word-sized. 80 + * DS620 uses a high-byte first convention, which is exactly opposite to 81 + * the SMBus standard. 82 + */ 83 + static int ds620_read_temp(struct i2c_client *client, u8 reg) 84 + { 85 + int ret; 86 + 87 + ret = i2c_smbus_read_word_data(client, reg); 88 + if (ret < 0) 89 + return ret; 90 + return swab16(ret); 91 + } 92 + 93 + static int ds620_write_temp(struct i2c_client *client, u8 reg, u16 value) 94 + { 95 + return i2c_smbus_write_word_data(client, reg, swab16(value)); 96 + } 97 + 98 + static void ds620_init_client(struct i2c_client *client) 99 + { 100 + struct ds620_platform_data *ds620_info = client->dev.platform_data; 101 + u16 conf, new_conf; 102 + 103 + new_conf = conf = 104 + swab16(i2c_smbus_read_word_data(client, DS620_REG_CONF)); 105 + 106 + /* switch to continuous conversion mode */ 107 + new_conf &= ~DS620_REG_CONFIG_1SHOT; 108 + /* already high at power-on, but don't trust the BIOS! */ 109 + new_conf |= DS620_REG_CONFIG_PO2; 110 + /* thermostat mode according to platform data */ 111 + if (ds620_info && ds620_info->pomode == 1) 112 + new_conf &= ~DS620_REG_CONFIG_PO1; /* PO_LOW */ 113 + else if (ds620_info && ds620_info->pomode == 2) 114 + new_conf |= DS620_REG_CONFIG_PO1; /* PO_HIGH */ 115 + else 116 + new_conf &= ~DS620_REG_CONFIG_PO2; /* always low */ 117 + /* with highest precision */ 118 + new_conf |= DS620_REG_CONFIG_R1 | DS620_REG_CONFIG_R0; 119 + 120 + if (conf != new_conf) 121 + i2c_smbus_write_word_data(client, DS620_REG_CONF, 122 + swab16(new_conf)); 123 + 124 + /* start conversion */ 125 + i2c_smbus_write_byte(client, DS620_COM_START); 126 + } 127 + 128 + static struct ds620_data *ds620_update_client(struct device *dev) 129 + { 130 + struct i2c_client *client = to_i2c_client(dev); 131 + struct ds620_data *data = i2c_get_clientdata(client); 132 + struct ds620_data *ret = data; 133 + 134 + mutex_lock(&data->update_lock); 135 + 136 + if (time_after(jiffies, data->last_updated + HZ + HZ / 2) 137 + || !data->valid) { 138 + int i; 139 + int res; 140 + 141 + dev_dbg(&client->dev, "Starting ds620 update\n"); 142 + 143 + for (i = 0; i < ARRAY_SIZE(data->temp); i++) { 144 + res = ds620_read_temp(client, 145 + DS620_REG_TEMP[i]); 146 + if (res < 0) { 147 + ret = ERR_PTR(res); 148 + goto abort; 149 + } 150 + 151 + data->temp[i] = res; 152 + } 153 + 154 + data->last_updated = jiffies; 155 + data->valid = 1; 156 + } 157 + abort: 158 + mutex_unlock(&data->update_lock); 159 + 160 + return ret; 161 + } 162 + 163 + static ssize_t show_temp(struct device *dev, struct device_attribute *da, 164 + char *buf) 165 + { 166 + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 167 + struct ds620_data *data = ds620_update_client(dev); 168 + 169 + if (IS_ERR(data)) 170 + return PTR_ERR(data); 171 + 172 + return sprintf(buf, "%d\n", ((data->temp[attr->index] / 8) * 625) / 10); 173 + } 174 + 175 + static ssize_t set_temp(struct device *dev, struct device_attribute *da, 176 + const char *buf, size_t count) 177 + { 178 + int res; 179 + long val; 180 + 181 + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 182 + struct i2c_client *client = to_i2c_client(dev); 183 + struct ds620_data *data = i2c_get_clientdata(client); 184 + 185 + res = strict_strtol(buf, 10, &val); 186 + 187 + if (res) 188 + return res; 189 + 190 + val = (val * 10 / 625) * 8; 191 + 192 + mutex_lock(&data->update_lock); 193 + data->temp[attr->index] = val; 194 + ds620_write_temp(client, DS620_REG_TEMP[attr->index], 195 + data->temp[attr->index]); 196 + mutex_unlock(&data->update_lock); 197 + return count; 198 + } 199 + 200 + static ssize_t show_alarm(struct device *dev, struct device_attribute *da, 201 + char *buf) 202 + { 203 + struct sensor_device_attribute *attr = to_sensor_dev_attr(da); 204 + struct ds620_data *data = ds620_update_client(dev); 205 + struct i2c_client *client = to_i2c_client(dev); 206 + u16 conf, new_conf; 207 + int res; 208 + 209 + if (IS_ERR(data)) 210 + return PTR_ERR(data); 211 + 212 + /* reset alarms if necessary */ 213 + res = i2c_smbus_read_word_data(client, DS620_REG_CONF); 214 + if (res < 0) 215 + return res; 216 + 217 + conf = swab16(res); 218 + new_conf = conf; 219 + new_conf &= ~attr->index; 220 + if (conf != new_conf) { 221 + res = i2c_smbus_write_word_data(client, DS620_REG_CONF, 222 + swab16(new_conf)); 223 + if (res < 0) 224 + return res; 225 + } 226 + 227 + return sprintf(buf, "%d\n", !!(conf & attr->index)); 228 + } 229 + 230 + static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); 231 + static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp, set_temp, 1); 232 + static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp, set_temp, 2); 233 + static SENSOR_DEVICE_ATTR(temp1_min_alarm, S_IRUGO, show_alarm, NULL, 234 + DS620_REG_CONFIG_TLF); 235 + static SENSOR_DEVICE_ATTR(temp1_max_alarm, S_IRUGO, show_alarm, NULL, 236 + DS620_REG_CONFIG_THF); 237 + 238 + static struct attribute *ds620_attributes[] = { 239 + &sensor_dev_attr_temp1_input.dev_attr.attr, 240 + &sensor_dev_attr_temp1_min.dev_attr.attr, 241 + &sensor_dev_attr_temp1_max.dev_attr.attr, 242 + &sensor_dev_attr_temp1_min_alarm.dev_attr.attr, 243 + &sensor_dev_attr_temp1_max_alarm.dev_attr.attr, 244 + NULL 245 + }; 246 + 247 + static const struct attribute_group ds620_group = { 248 + .attrs = ds620_attributes, 249 + }; 250 + 251 + static int ds620_probe(struct i2c_client *client, 252 + const struct i2c_device_id *id) 253 + { 254 + struct ds620_data *data; 255 + int err; 256 + 257 + data = kzalloc(sizeof(struct ds620_data), GFP_KERNEL); 258 + if (!data) { 259 + err = -ENOMEM; 260 + goto exit; 261 + } 262 + 263 + i2c_set_clientdata(client, data); 264 + mutex_init(&data->update_lock); 265 + 266 + /* Initialize the DS620 chip */ 267 + ds620_init_client(client); 268 + 269 + /* Register sysfs hooks */ 270 + err = sysfs_create_group(&client->dev.kobj, &ds620_group); 271 + if (err) 272 + goto exit_free; 273 + 274 + data->hwmon_dev = hwmon_device_register(&client->dev); 275 + if (IS_ERR(data->hwmon_dev)) { 276 + err = PTR_ERR(data->hwmon_dev); 277 + goto exit_remove_files; 278 + } 279 + 280 + dev_info(&client->dev, "temperature sensor found\n"); 281 + 282 + return 0; 283 + 284 + exit_remove_files: 285 + sysfs_remove_group(&client->dev.kobj, &ds620_group); 286 + exit_free: 287 + kfree(data); 288 + exit: 289 + return err; 290 + } 291 + 292 + static int ds620_remove(struct i2c_client *client) 293 + { 294 + struct ds620_data *data = i2c_get_clientdata(client); 295 + 296 + hwmon_device_unregister(data->hwmon_dev); 297 + sysfs_remove_group(&client->dev.kobj, &ds620_group); 298 + 299 + kfree(data); 300 + 301 + return 0; 302 + } 303 + 304 + static const struct i2c_device_id ds620_id[] = { 305 + {"ds620", 0}, 306 + {} 307 + }; 308 + 309 + MODULE_DEVICE_TABLE(i2c, ds620_id); 310 + 311 + /* This is the driver that will be inserted */ 312 + static struct i2c_driver ds620_driver = { 313 + .class = I2C_CLASS_HWMON, 314 + .driver = { 315 + .name = "ds620", 316 + }, 317 + .probe = ds620_probe, 318 + .remove = ds620_remove, 319 + .id_table = ds620_id, 320 + }; 321 + 322 + static int __init ds620_init(void) 323 + { 324 + return i2c_add_driver(&ds620_driver); 325 + } 326 + 327 + static void __exit ds620_exit(void) 328 + { 329 + i2c_del_driver(&ds620_driver); 330 + } 331 + 332 + MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>"); 333 + MODULE_DESCRIPTION("DS620 driver"); 334 + MODULE_LICENSE("GPL"); 335 + 336 + module_init(ds620_init); 337 + module_exit(ds620_exit);
+21
include/linux/i2c/ds620.h
··· 1 + #ifndef _LINUX_DS620_H 2 + #define _LINUX_DS620_H 3 + 4 + #include <linux/types.h> 5 + #include <linux/i2c.h> 6 + 7 + /* platform data for the DS620 temperature sensor and thermostat */ 8 + 9 + struct ds620_platform_data { 10 + /* 11 + * Thermostat output pin PO mode: 12 + * 0 = always low (default) 13 + * 1 = PO_LOW 14 + * 2 = PO_HIGH 15 + * 16 + * (see Documentation/hwmon/ds620) 17 + */ 18 + int pomode; 19 + }; 20 + 21 + #endif /* _LINUX_DS620_H */