Blame view
drivers/hwmon/adm1029.c
11.3 KB
cae2caae7 hwmon: New driver... |
1 2 3 |
/* * adm1029.c - Part of lm_sensors, Linux kernel modules for hardware monitoring * |
962a75a2d hwmon: (adm1029) ... |
4 |
* Copyright (C) 2006 Corentin LABBE <clabbe.montjoie@gmail.com> |
cae2caae7 hwmon: New driver... |
5 |
* |
7c81c60f3 Update Jean Delva... |
6 |
* Based on LM83 Driver by Jean Delvare <jdelvare@suse.de> |
cae2caae7 hwmon: New driver... |
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
* * Give only processor, motherboard temperatures and fan tachs * Very rare chip please let me know if you use it * * http://www.analog.com/UploadedFiles/Data_Sheets/ADM1029.pdf * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation version 2 of the License * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include <linux/module.h> #include <linux/init.h> #include <linux/slab.h> #include <linux/jiffies.h> #include <linux/i2c.h> #include <linux/hwmon-sysfs.h> #include <linux/hwmon.h> #include <linux/err.h> #include <linux/mutex.h> /* * Addresses to scan */ |
25e9c86d5 hwmon: normal_i2c... |
41 42 |
static const unsigned short normal_i2c[] = { 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, I2C_CLIENT_END |
cae2caae7 hwmon: New driver... |
43 44 45 |
}; /* |
cae2caae7 hwmon: New driver... |
46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
* The ADM1029 registers * Manufacturer ID is 0x41 for Analog Devices */ #define ADM1029_REG_MAN_ID 0x0D #define ADM1029_REG_CHIP_ID 0x0E #define ADM1029_REG_CONFIG 0x01 #define ADM1029_REG_NB_FAN_SUPPORT 0x02 #define ADM1029_REG_TEMP_DEVICES_INSTALLED 0x06 #define ADM1029_REG_LOCAL_TEMP 0xA0 #define ADM1029_REG_REMOTE1_TEMP 0xA1 #define ADM1029_REG_REMOTE2_TEMP 0xA2 #define ADM1029_REG_LOCAL_TEMP_HIGH 0x90 #define ADM1029_REG_REMOTE1_TEMP_HIGH 0x91 #define ADM1029_REG_REMOTE2_TEMP_HIGH 0x92 #define ADM1029_REG_LOCAL_TEMP_LOW 0x98 #define ADM1029_REG_REMOTE1_TEMP_LOW 0x99 #define ADM1029_REG_REMOTE2_TEMP_LOW 0x9A #define ADM1029_REG_FAN1 0x70 #define ADM1029_REG_FAN2 0x71 #define ADM1029_REG_FAN1_MIN 0x78 #define ADM1029_REG_FAN2_MIN 0x79 #define ADM1029_REG_FAN1_CONFIG 0x68 #define ADM1029_REG_FAN2_CONFIG 0x69 #define TEMP_FROM_REG(val) ((val) * 1000) |
08f509077 hwmon: (adm1029) ... |
79 |
#define DIV_FROM_REG(val) (1 << (((val) >> 6) - 1)) |
cae2caae7 hwmon: New driver... |
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
/* Registers to be checked by adm1029_update_device() */ static const u8 ADM1029_REG_TEMP[] = { ADM1029_REG_LOCAL_TEMP, ADM1029_REG_REMOTE1_TEMP, ADM1029_REG_REMOTE2_TEMP, ADM1029_REG_LOCAL_TEMP_HIGH, ADM1029_REG_REMOTE1_TEMP_HIGH, ADM1029_REG_REMOTE2_TEMP_HIGH, ADM1029_REG_LOCAL_TEMP_LOW, ADM1029_REG_REMOTE1_TEMP_LOW, ADM1029_REG_REMOTE2_TEMP_LOW, }; static const u8 ADM1029_REG_FAN[] = { ADM1029_REG_FAN1, ADM1029_REG_FAN2, ADM1029_REG_FAN1_MIN, ADM1029_REG_FAN2_MIN, }; static const u8 ADM1029_REG_FAN_DIV[] = { ADM1029_REG_FAN1_CONFIG, ADM1029_REG_FAN2_CONFIG, }; /* |
cae2caae7 hwmon: New driver... |
107 108 109 110 |
* Client data (each client gets its own) */ struct adm1029_data { |
4fd5233f8 hwmon: (adm1029) ... |
111 |
struct i2c_client *client; |
cae2caae7 hwmon: New driver... |
112 113 114 115 116 117 118 119 120 121 122 |
struct mutex update_lock; char valid; /* zero until following fields are valid */ unsigned long last_updated; /* in jiffies */ /* registers values, signed for temperature, unsigned for other stuff */ s8 temp[ARRAY_SIZE(ADM1029_REG_TEMP)]; u8 fan[ARRAY_SIZE(ADM1029_REG_FAN)]; u8 fan_div[ARRAY_SIZE(ADM1029_REG_FAN_DIV)]; }; /* |
b12e484e8 hwmon: (adm1029) ... |
123 124 125 126 |
* function that update the status of the chips (temperature for example) */ static struct adm1029_data *adm1029_update_device(struct device *dev) { |
4fd5233f8 hwmon: (adm1029) ... |
127 128 |
struct adm1029_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; |
b12e484e8 hwmon: (adm1029) ... |
129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 |
mutex_lock(&data->update_lock); /* * Use the "cache" Luke, don't recheck values * if there are already checked not a long time later */ if (time_after(jiffies, data->last_updated + HZ * 2) || !data->valid) { int nr; dev_dbg(&client->dev, "Updating adm1029 data "); for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_TEMP); nr++) { data->temp[nr] = i2c_smbus_read_byte_data(client, ADM1029_REG_TEMP[nr]); } for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN); nr++) { data->fan[nr] = i2c_smbus_read_byte_data(client, ADM1029_REG_FAN[nr]); } for (nr = 0; nr < ARRAY_SIZE(ADM1029_REG_FAN_DIV); nr++) { data->fan_div[nr] = i2c_smbus_read_byte_data(client, ADM1029_REG_FAN_DIV[nr]); } data->last_updated = jiffies; data->valid = 1; } mutex_unlock(&data->update_lock); return data; } /* |
cae2caae7 hwmon: New driver... |
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
* Sysfs stuff */ static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adm1029_data *data = adm1029_update_device(dev); return sprintf(buf, "%d ", TEMP_FROM_REG(data->temp[attr->index])); } static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adm1029_data *data = adm1029_update_device(dev); u16 val; |
366716e6a hwmon: (adm1029) ... |
186 187 |
if (data->fan[attr->index] == 0 || (data->fan_div[attr->index] & 0xC0) == 0 |
cae2caae7 hwmon: New driver... |
188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
|| data->fan[attr->index] == 255) { return sprintf(buf, "0 "); } val = 1880 * 120 / DIV_FROM_REG(data->fan_div[attr->index]) / data->fan[attr->index]; return sprintf(buf, "%d ", val); } static ssize_t show_fan_div(struct device *dev, struct device_attribute *devattr, char *buf) { struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct adm1029_data *data = adm1029_update_device(dev); |
366716e6a hwmon: (adm1029) ... |
204 |
if ((data->fan_div[attr->index] & 0xC0) == 0) |
cae2caae7 hwmon: New driver... |
205 206 207 208 209 210 211 212 213 |
return sprintf(buf, "0 "); return sprintf(buf, "%d ", DIV_FROM_REG(data->fan_div[attr->index])); } static ssize_t set_fan_div(struct device *dev, struct device_attribute *devattr, const char *buf, size_t count) { |
4fd5233f8 hwmon: (adm1029) ... |
214 215 |
struct adm1029_data *data = dev_get_drvdata(dev); struct i2c_client *client = data->client; |
cae2caae7 hwmon: New driver... |
216 |
struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); |
cae2caae7 hwmon: New driver... |
217 |
u8 reg; |
08f509077 hwmon: (adm1029) ... |
218 219 220 221 |
long val; int ret = kstrtol(buf, 10, &val); if (ret < 0) return ret; |
cae2caae7 hwmon: New driver... |
222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
mutex_lock(&data->update_lock); /*Read actual config */ reg = i2c_smbus_read_byte_data(client, ADM1029_REG_FAN_DIV[attr->index]); switch (val) { case 1: val = 1; break; case 2: val = 2; break; case 4: val = 3; break; default: mutex_unlock(&data->update_lock); |
b55f37572 hwmon: Fix checkp... |
241 242 243 244 |
dev_err(&client->dev, "fan_div value %ld not supported. Choose one of 1, 2 or 4! ", val); |
cae2caae7 hwmon: New driver... |
245 246 247 248 |
return -EINVAL; } /* Update the value */ reg = (reg & 0x3F) | (val << 6); |
1035a9e3e hwmon: (adm1029) ... |
249 250 |
/* Update the cache */ data->fan_div[attr->index] = reg; |
cae2caae7 hwmon: New driver... |
251 252 253 254 255 256 257 258 259 |
/* Write value */ i2c_smbus_write_byte_data(client, ADM1029_REG_FAN_DIV[attr->index], reg); mutex_unlock(&data->update_lock); return count; } /* |
94b991d4c hwmon: (adm1029) ... |
260 261 262 |
* Access rights on sysfs. S_IRUGO: Is Readable by User, Group and Others * S_IWUSR: Is Writable by User. */ |
cae2caae7 hwmon: New driver... |
263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); static SENSOR_DEVICE_ATTR(temp1_max, S_IRUGO, show_temp, NULL, 3); static SENSOR_DEVICE_ATTR(temp2_max, S_IRUGO, show_temp, NULL, 4); static SENSOR_DEVICE_ATTR(temp3_max, S_IRUGO, show_temp, NULL, 5); static SENSOR_DEVICE_ATTR(temp1_min, S_IRUGO, show_temp, NULL, 6); static SENSOR_DEVICE_ATTR(temp2_min, S_IRUGO, show_temp, NULL, 7); static SENSOR_DEVICE_ATTR(temp3_min, S_IRUGO, show_temp, NULL, 8); static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); static SENSOR_DEVICE_ATTR(fan1_min, S_IRUGO, show_fan, NULL, 2); static SENSOR_DEVICE_ATTR(fan2_min, S_IRUGO, show_fan, NULL, 3); static SENSOR_DEVICE_ATTR(fan1_div, S_IRUGO | S_IWUSR, show_fan_div, set_fan_div, 0); static SENSOR_DEVICE_ATTR(fan2_div, S_IRUGO | S_IWUSR, show_fan_div, set_fan_div, 1); |
4fd5233f8 hwmon: (adm1029) ... |
285 |
static struct attribute *adm1029_attrs[] = { |
cae2caae7 hwmon: New driver... |
286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 |
&sensor_dev_attr_temp1_input.dev_attr.attr, &sensor_dev_attr_temp1_min.dev_attr.attr, &sensor_dev_attr_temp1_max.dev_attr.attr, &sensor_dev_attr_temp2_input.dev_attr.attr, &sensor_dev_attr_temp2_min.dev_attr.attr, &sensor_dev_attr_temp2_max.dev_attr.attr, &sensor_dev_attr_temp3_input.dev_attr.attr, &sensor_dev_attr_temp3_min.dev_attr.attr, &sensor_dev_attr_temp3_max.dev_attr.attr, &sensor_dev_attr_fan1_input.dev_attr.attr, &sensor_dev_attr_fan2_input.dev_attr.attr, &sensor_dev_attr_fan1_min.dev_attr.attr, &sensor_dev_attr_fan2_min.dev_attr.attr, &sensor_dev_attr_fan1_div.dev_attr.attr, &sensor_dev_attr_fan2_div.dev_attr.attr, NULL }; |
4fd5233f8 hwmon: (adm1029) ... |
303 |
ATTRIBUTE_GROUPS(adm1029); |
cae2caae7 hwmon: New driver... |
304 305 306 307 |
/* * Real code */ |
9c97fb4d2 hwmon: (adm1029) ... |
308 |
/* Return 0 if detection is successful, -ENODEV otherwise */ |
310ec7921 i2c: Drop the kin... |
309 |
static int adm1029_detect(struct i2c_client *client, |
9c97fb4d2 hwmon: (adm1029) ... |
310 |
struct i2c_board_info *info) |
cae2caae7 hwmon: New driver... |
311 |
{ |
9c97fb4d2 hwmon: (adm1029) ... |
312 |
struct i2c_adapter *adapter = client->adapter; |
52df6440a hwmon: Clean up d... |
313 |
u8 man_id, chip_id, temp_devices_installed, nb_fan_support; |
cae2caae7 hwmon: New driver... |
314 |
|
cae2caae7 hwmon: New driver... |
315 |
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) |
9c97fb4d2 hwmon: (adm1029) ... |
316 |
return -ENODEV; |
cae2caae7 hwmon: New driver... |
317 |
|
94b991d4c hwmon: (adm1029) ... |
318 319 |
/* * ADM1029 doesn't have CHIP ID, check just MAN ID |
cae2caae7 hwmon: New driver... |
320 321 322 323 |
* For better detection we check also ADM1029_TEMP_DEVICES_INSTALLED, * ADM1029_REG_NB_FAN_SUPPORT and compare it with possible values * documented */ |
52df6440a hwmon: Clean up d... |
324 325 326 |
man_id = i2c_smbus_read_byte_data(client, ADM1029_REG_MAN_ID); chip_id = i2c_smbus_read_byte_data(client, ADM1029_REG_CHIP_ID); temp_devices_installed = i2c_smbus_read_byte_data(client, |
cae2caae7 hwmon: New driver... |
327 |
ADM1029_REG_TEMP_DEVICES_INSTALLED); |
52df6440a hwmon: Clean up d... |
328 |
nb_fan_support = i2c_smbus_read_byte_data(client, |
cae2caae7 hwmon: New driver... |
329 |
ADM1029_REG_NB_FAN_SUPPORT); |
52df6440a hwmon: Clean up d... |
330 331 332 333 334 335 |
/* 0x41 is Analog Devices */ if (man_id != 0x41 || (temp_devices_installed & 0xf9) != 0x01 || nb_fan_support != 0x03) return -ENODEV; if ((chip_id & 0xF0) != 0x00) { |
94b991d4c hwmon: (adm1029) ... |
336 337 338 339 |
/* * There are no "official" CHIP ID, so actually * we use Major/Minor revision for that */ |
b55f37572 hwmon: Fix checkp... |
340 341 342 |
pr_info("Unknown major revision %x, please let us know ", chip_id); |
52df6440a hwmon: Clean up d... |
343 |
return -ENODEV; |
cae2caae7 hwmon: New driver... |
344 |
} |
52df6440a hwmon: Clean up d... |
345 |
|
9c97fb4d2 hwmon: (adm1029) ... |
346 |
strlcpy(info->type, "adm1029", I2C_NAME_SIZE); |
cae2caae7 hwmon: New driver... |
347 |
|
9c97fb4d2 hwmon: (adm1029) ... |
348 349 |
return 0; } |
b12e484e8 hwmon: (adm1029) ... |
350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 |
static int adm1029_init_client(struct i2c_client *client) { u8 config; config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG); if ((config & 0x10) == 0) { i2c_smbus_write_byte_data(client, ADM1029_REG_CONFIG, config | 0x10); } /* recheck config */ config = i2c_smbus_read_byte_data(client, ADM1029_REG_CONFIG); if ((config & 0x10) == 0) { dev_err(&client->dev, "Initialization failed! "); return 0; } return 1; } |
9c97fb4d2 hwmon: (adm1029) ... |
368 369 370 |
static int adm1029_probe(struct i2c_client *client, const struct i2c_device_id *id) { |
4fd5233f8 hwmon: (adm1029) ... |
371 |
struct device *dev = &client->dev; |
9c97fb4d2 hwmon: (adm1029) ... |
372 |
struct adm1029_data *data; |
4fd5233f8 hwmon: (adm1029) ... |
373 |
struct device *hwmon_dev; |
9c97fb4d2 hwmon: (adm1029) ... |
374 |
|
4fd5233f8 hwmon: (adm1029) ... |
375 |
data = devm_kzalloc(dev, sizeof(struct adm1029_data), GFP_KERNEL); |
08940b8ab hwmon: (adm1029) ... |
376 377 |
if (!data) return -ENOMEM; |
cae2caae7 hwmon: New driver... |
378 |
|
4fd5233f8 hwmon: (adm1029) ... |
379 |
data->client = client; |
cae2caae7 hwmon: New driver... |
380 |
mutex_init(&data->update_lock); |
cae2caae7 hwmon: New driver... |
381 382 383 384 |
/* * Initialize the ADM1029 chip * Check config register */ |
08940b8ab hwmon: (adm1029) ... |
385 386 |
if (adm1029_init_client(client) == 0) return -ENODEV; |
cae2caae7 hwmon: New driver... |
387 |
|
4fd5233f8 hwmon: (adm1029) ... |
388 389 390 391 |
hwmon_dev = devm_hwmon_device_register_with_groups(dev, client->name, data, adm1029_groups); return PTR_ERR_OR_ZERO(hwmon_dev); |
cae2caae7 hwmon: New driver... |
392 |
} |
b12e484e8 hwmon: (adm1029) ... |
393 394 395 396 397 |
static const struct i2c_device_id adm1029_id[] = { { "adm1029", 0 }, { } }; MODULE_DEVICE_TABLE(i2c, adm1029_id); |
cae2caae7 hwmon: New driver... |
398 |
|
b12e484e8 hwmon: (adm1029) ... |
399 400 401 402 403 404 |
static struct i2c_driver adm1029_driver = { .class = I2C_CLASS_HWMON, .driver = { .name = "adm1029", }, .probe = adm1029_probe, |
b12e484e8 hwmon: (adm1029) ... |
405 406 407 408 |
.id_table = adm1029_id, .detect = adm1029_detect, .address_list = normal_i2c, }; |
cae2caae7 hwmon: New driver... |
409 |
|
f0967eea8 hwmon: convert dr... |
410 |
module_i2c_driver(adm1029_driver); |
cae2caae7 hwmon: New driver... |
411 |
|
962a75a2d hwmon: (adm1029) ... |
412 |
MODULE_AUTHOR("Corentin LABBE <clabbe.montjoie@gmail.com>"); |
cae2caae7 hwmon: New driver... |
413 414 |
MODULE_DESCRIPTION("adm1029 driver"); MODULE_LICENSE("GPL v2"); |