diff options
Diffstat (limited to 'drivers/macintosh/therm_adt746x.c')
-rw-r--r-- | drivers/macintosh/therm_adt746x.c | 483 |
1 files changed, 278 insertions, 205 deletions
diff --git a/drivers/macintosh/therm_adt746x.c b/drivers/macintosh/therm_adt746x.c index f433521a6f9..02367308ff2 100644 --- a/drivers/macintosh/therm_adt746x.c +++ b/drivers/macintosh/therm_adt746x.c | |||
@@ -29,6 +29,7 @@ | |||
29 | #include <asm/prom.h> | 29 | #include <asm/prom.h> |
30 | #include <asm/machdep.h> | 30 | #include <asm/machdep.h> |
31 | #include <asm/io.h> | 31 | #include <asm/io.h> |
32 | #include <asm/system.h> | ||
32 | #include <asm/sections.h> | 33 | #include <asm/sections.h> |
33 | 34 | ||
34 | #undef DEBUG | 35 | #undef DEBUG |
@@ -47,11 +48,11 @@ static u8 FAN_SPD_SET[2] = {0x30, 0x31}; | |||
47 | 48 | ||
48 | static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ | 49 | static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ |
49 | static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ | 50 | static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ |
50 | static const char *sensor_location[3] = { "?", "?", "?" }; | 51 | static const char *sensor_location[3]; |
51 | 52 | ||
52 | static int limit_adjust; | 53 | static int limit_adjust; |
53 | static int fan_speed = -1; | 54 | static int fan_speed = -1; |
54 | static bool verbose; | 55 | static int verbose; |
55 | 56 | ||
56 | MODULE_AUTHOR("Colin Leroy <colin@colino.net>"); | 57 | MODULE_AUTHOR("Colin Leroy <colin@colino.net>"); |
57 | MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and " | 58 | MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and " |
@@ -79,16 +80,18 @@ struct thermostat { | |||
79 | int last_speed[2]; | 80 | int last_speed[2]; |
80 | int last_var[2]; | 81 | int last_var[2]; |
81 | int pwm_inv[2]; | 82 | int pwm_inv[2]; |
82 | struct task_struct *thread; | ||
83 | struct platform_device *pdev; | ||
84 | enum { | ||
85 | ADT7460, | ||
86 | ADT7467 | ||
87 | } type; | ||
88 | }; | 83 | }; |
89 | 84 | ||
85 | static enum {ADT7460, ADT7467} therm_type; | ||
86 | static int therm_bus, therm_address; | ||
87 | static struct platform_device * of_dev; | ||
88 | static struct thermostat* thermostat; | ||
89 | static struct task_struct *thread_therm = NULL; | ||
90 | |||
90 | static void write_both_fan_speed(struct thermostat *th, int speed); | 91 | static void write_both_fan_speed(struct thermostat *th, int speed); |
91 | static void write_fan_speed(struct thermostat *th, int speed, int fan); | 92 | static void write_fan_speed(struct thermostat *th, int speed, int fan); |
93 | static void thermostat_create_files(void); | ||
94 | static void thermostat_remove_files(void); | ||
92 | 95 | ||
93 | static int | 96 | static int |
94 | write_reg(struct thermostat* th, int reg, u8 data) | 97 | write_reg(struct thermostat* th, int reg, u8 data) |
@@ -124,6 +127,66 @@ read_reg(struct thermostat* th, int reg) | |||
124 | return data; | 127 | return data; |
125 | } | 128 | } |
126 | 129 | ||
130 | static struct i2c_driver thermostat_driver; | ||
131 | |||
132 | static int | ||
133 | attach_thermostat(struct i2c_adapter *adapter) | ||
134 | { | ||
135 | unsigned long bus_no; | ||
136 | struct i2c_board_info info; | ||
137 | struct i2c_client *client; | ||
138 | |||
139 | if (strncmp(adapter->name, "uni-n", 5)) | ||
140 | return -ENODEV; | ||
141 | bus_no = simple_strtoul(adapter->name + 6, NULL, 10); | ||
142 | if (bus_no != therm_bus) | ||
143 | return -ENODEV; | ||
144 | |||
145 | memset(&info, 0, sizeof(struct i2c_board_info)); | ||
146 | strlcpy(info.type, "therm_adt746x", I2C_NAME_SIZE); | ||
147 | info.addr = therm_address; | ||
148 | client = i2c_new_device(adapter, &info); | ||
149 | if (!client) | ||
150 | return -ENODEV; | ||
151 | |||
152 | /* | ||
153 | * Let i2c-core delete that device on driver removal. | ||
154 | * This is safe because i2c-core holds the core_lock mutex for us. | ||
155 | */ | ||
156 | list_add_tail(&client->detected, &thermostat_driver.clients); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | static int | ||
161 | remove_thermostat(struct i2c_client *client) | ||
162 | { | ||
163 | struct thermostat *th = i2c_get_clientdata(client); | ||
164 | int i; | ||
165 | |||
166 | thermostat_remove_files(); | ||
167 | |||
168 | if (thread_therm != NULL) { | ||
169 | kthread_stop(thread_therm); | ||
170 | } | ||
171 | |||
172 | printk(KERN_INFO "adt746x: Putting max temperatures back from " | ||
173 | "%d, %d, %d to %d, %d, %d\n", | ||
174 | th->limits[0], th->limits[1], th->limits[2], | ||
175 | th->initial_limits[0], th->initial_limits[1], | ||
176 | th->initial_limits[2]); | ||
177 | |||
178 | for (i = 0; i < 3; i++) | ||
179 | write_reg(th, LIMIT_REG[i], th->initial_limits[i]); | ||
180 | |||
181 | write_both_fan_speed(th, -1); | ||
182 | |||
183 | thermostat = NULL; | ||
184 | |||
185 | kfree(th); | ||
186 | |||
187 | return 0; | ||
188 | } | ||
189 | |||
127 | static int read_fan_speed(struct thermostat *th, u8 addr) | 190 | static int read_fan_speed(struct thermostat *th, u8 addr) |
128 | { | 191 | { |
129 | u8 tmp[2]; | 192 | u8 tmp[2]; |
@@ -141,7 +204,7 @@ static int read_fan_speed(struct thermostat *th, u8 addr) | |||
141 | static void write_both_fan_speed(struct thermostat *th, int speed) | 204 | static void write_both_fan_speed(struct thermostat *th, int speed) |
142 | { | 205 | { |
143 | write_fan_speed(th, speed, 0); | 206 | write_fan_speed(th, speed, 0); |
144 | if (th->type == ADT7460) | 207 | if (therm_type == ADT7460) |
145 | write_fan_speed(th, speed, 1); | 208 | write_fan_speed(th, speed, 1); |
146 | } | 209 | } |
147 | 210 | ||
@@ -154,7 +217,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) | |||
154 | else if (speed < -1) | 217 | else if (speed < -1) |
155 | speed = 0; | 218 | speed = 0; |
156 | 219 | ||
157 | if (th->type == ADT7467 && fan == 1) | 220 | if (therm_type == ADT7467 && fan == 1) |
158 | return; | 221 | return; |
159 | 222 | ||
160 | if (th->last_speed[fan] != speed) { | 223 | if (th->last_speed[fan] != speed) { |
@@ -177,7 +240,7 @@ static void write_fan_speed(struct thermostat *th, int speed, int fan) | |||
177 | write_reg(th, FAN_SPD_SET[fan], speed); | 240 | write_reg(th, FAN_SPD_SET[fan], speed); |
178 | } else { | 241 | } else { |
179 | /* back to automatic */ | 242 | /* back to automatic */ |
180 | if(th->type == ADT7460) { | 243 | if(therm_type == ADT7460) { |
181 | manual = read_reg(th, | 244 | manual = read_reg(th, |
182 | MANUAL_MODE[fan]) & (~MANUAL_MASK); | 245 | MANUAL_MODE[fan]) & (~MANUAL_MASK); |
183 | manual &= ~INVERT_MASK; | 246 | manual &= ~INVERT_MASK; |
@@ -231,7 +294,7 @@ static void update_fans_speed (struct thermostat *th) | |||
231 | /* we don't care about local sensor, so we start at sensor 1 */ | 294 | /* we don't care about local sensor, so we start at sensor 1 */ |
232 | for (i = 1; i < 3; i++) { | 295 | for (i = 1; i < 3; i++) { |
233 | int started = 0; | 296 | int started = 0; |
234 | int fan_number = (th->type == ADT7460 && i == 2); | 297 | int fan_number = (therm_type == ADT7460 && i == 2); |
235 | int var = th->temps[i] - th->limits[i]; | 298 | int var = th->temps[i] - th->limits[i]; |
236 | 299 | ||
237 | if (var > -1) { | 300 | if (var > -1) { |
@@ -308,22 +371,116 @@ static int monitor_task(void *arg) | |||
308 | 371 | ||
309 | static void set_limit(struct thermostat *th, int i) | 372 | static void set_limit(struct thermostat *th, int i) |
310 | { | 373 | { |
311 | /* Set sensor1 limit higher to avoid powerdowns */ | 374 | /* Set sensor1 limit higher to avoid powerdowns */ |
312 | th->limits[i] = default_limits_chip[i] + limit_adjust; | 375 | th->limits[i] = default_limits_chip[i] + limit_adjust; |
313 | write_reg(th, LIMIT_REG[i], th->limits[i]); | 376 | write_reg(th, LIMIT_REG[i], th->limits[i]); |
314 | 377 | ||
315 | /* set our limits to normal */ | 378 | /* set our limits to normal */ |
316 | th->limits[i] = default_limits_local[i] + limit_adjust; | 379 | th->limits[i] = default_limits_local[i] + limit_adjust; |
317 | } | 380 | } |
318 | 381 | ||
319 | #define BUILD_SHOW_FUNC_INT(name, data) \ | 382 | static int probe_thermostat(struct i2c_client *client, |
320 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ | 383 | const struct i2c_device_id *id) |
321 | { \ | 384 | { |
322 | struct thermostat *th = dev_get_drvdata(dev); \ | 385 | struct thermostat* th; |
323 | return sprintf(buf, "%d\n", data); \ | 386 | int rc; |
387 | int i; | ||
388 | |||
389 | if (thermostat) | ||
390 | return 0; | ||
391 | |||
392 | th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); | ||
393 | if (!th) | ||
394 | return -ENOMEM; | ||
395 | |||
396 | i2c_set_clientdata(client, th); | ||
397 | th->clt = client; | ||
398 | |||
399 | rc = read_reg(th, CONFIG_REG); | ||
400 | if (rc < 0) { | ||
401 | dev_err(&client->dev, "Thermostat failed to read config!\n"); | ||
402 | kfree(th); | ||
403 | return -ENODEV; | ||
404 | } | ||
405 | |||
406 | /* force manual control to start the fan quieter */ | ||
407 | if (fan_speed == -1) | ||
408 | fan_speed = 64; | ||
409 | |||
410 | if(therm_type == ADT7460) { | ||
411 | printk(KERN_INFO "adt746x: ADT7460 initializing\n"); | ||
412 | /* The 7460 needs to be started explicitly */ | ||
413 | write_reg(th, CONFIG_REG, 1); | ||
414 | } else | ||
415 | printk(KERN_INFO "adt746x: ADT7467 initializing\n"); | ||
416 | |||
417 | for (i = 0; i < 3; i++) { | ||
418 | th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); | ||
419 | set_limit(th, i); | ||
420 | } | ||
421 | |||
422 | printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" | ||
423 | " to %d, %d, %d\n", | ||
424 | th->initial_limits[0], th->initial_limits[1], | ||
425 | th->initial_limits[2], th->limits[0], th->limits[1], | ||
426 | th->limits[2]); | ||
427 | |||
428 | thermostat = th; | ||
429 | |||
430 | /* record invert bit status because fw can corrupt it after suspend */ | ||
431 | th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; | ||
432 | th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; | ||
433 | |||
434 | /* be sure to really write fan speed the first time */ | ||
435 | th->last_speed[0] = -2; | ||
436 | th->last_speed[1] = -2; | ||
437 | th->last_var[0] = -80; | ||
438 | th->last_var[1] = -80; | ||
439 | |||
440 | if (fan_speed != -1) { | ||
441 | /* manual mode, stop fans */ | ||
442 | write_both_fan_speed(th, 0); | ||
443 | } else { | ||
444 | /* automatic mode */ | ||
445 | write_both_fan_speed(th, -1); | ||
446 | } | ||
447 | |||
448 | thread_therm = kthread_run(monitor_task, th, "kfand"); | ||
449 | |||
450 | if (thread_therm == ERR_PTR(-ENOMEM)) { | ||
451 | printk(KERN_INFO "adt746x: Kthread creation failed\n"); | ||
452 | thread_therm = NULL; | ||
453 | return -ENOMEM; | ||
454 | } | ||
455 | |||
456 | thermostat_create_files(); | ||
457 | |||
458 | return 0; | ||
324 | } | 459 | } |
325 | 460 | ||
326 | #define BUILD_SHOW_FUNC_INT_LITE(name, data) \ | 461 | static const struct i2c_device_id therm_adt746x_id[] = { |
462 | { "therm_adt746x", 0 }, | ||
463 | { } | ||
464 | }; | ||
465 | |||
466 | static struct i2c_driver thermostat_driver = { | ||
467 | .driver = { | ||
468 | .name = "therm_adt746x", | ||
469 | }, | ||
470 | .attach_adapter = attach_thermostat, | ||
471 | .probe = probe_thermostat, | ||
472 | .remove = remove_thermostat, | ||
473 | .id_table = therm_adt746x_id, | ||
474 | }; | ||
475 | |||
476 | /* | ||
477 | * Now, unfortunately, sysfs doesn't give us a nice void * we could | ||
478 | * pass around to the attribute functions, so we don't really have | ||
479 | * choice but implement a bunch of them... | ||
480 | * | ||
481 | * FIXME, it does now... | ||
482 | */ | ||
483 | #define BUILD_SHOW_FUNC_INT(name, data) \ | ||
327 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ | 484 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ |
328 | { \ | 485 | { \ |
329 | return sprintf(buf, "%d\n", data); \ | 486 | return sprintf(buf, "%d\n", data); \ |
@@ -338,24 +495,22 @@ static ssize_t show_##name(struct device *dev, struct device_attribute *attr, ch | |||
338 | #define BUILD_SHOW_FUNC_FAN(name, data) \ | 495 | #define BUILD_SHOW_FUNC_FAN(name, data) \ |
339 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ | 496 | static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ |
340 | { \ | 497 | { \ |
341 | struct thermostat *th = dev_get_drvdata(dev); \ | ||
342 | return sprintf(buf, "%d (%d rpm)\n", \ | 498 | return sprintf(buf, "%d (%d rpm)\n", \ |
343 | th->last_speed[data], \ | 499 | thermostat->last_speed[data], \ |
344 | read_fan_speed(th, FAN_SPEED[data]) \ | 500 | read_fan_speed(thermostat, FAN_SPEED[data]) \ |
345 | ); \ | 501 | ); \ |
346 | } | 502 | } |
347 | 503 | ||
348 | #define BUILD_STORE_FUNC_DEG(name, data) \ | 504 | #define BUILD_STORE_FUNC_DEG(name, data) \ |
349 | static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ | 505 | static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ |
350 | { \ | 506 | { \ |
351 | struct thermostat *th = dev_get_drvdata(dev); \ | ||
352 | int val; \ | 507 | int val; \ |
353 | int i; \ | 508 | int i; \ |
354 | val = simple_strtol(buf, NULL, 10); \ | 509 | val = simple_strtol(buf, NULL, 10); \ |
355 | printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \ | 510 | printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \ |
356 | limit_adjust = val; \ | 511 | limit_adjust = val; \ |
357 | for (i=0; i < 3; i++) \ | 512 | for (i=0; i < 3; i++) \ |
358 | set_limit(th, i); \ | 513 | set_limit(thermostat, i); \ |
359 | return n; \ | 514 | return n; \ |
360 | } | 515 | } |
361 | 516 | ||
@@ -371,21 +526,20 @@ static ssize_t store_##name(struct device *dev, struct device_attribute *attr, c | |||
371 | return n; \ | 526 | return n; \ |
372 | } | 527 | } |
373 | 528 | ||
374 | BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(th, TEMP_REG[1]))) | 529 | BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(thermostat, TEMP_REG[1]))) |
375 | BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(th, TEMP_REG[2]))) | 530 | BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(thermostat, TEMP_REG[2]))) |
376 | BUILD_SHOW_FUNC_INT(sensor1_limit, th->limits[1]) | 531 | BUILD_SHOW_FUNC_INT(sensor1_limit, thermostat->limits[1]) |
377 | BUILD_SHOW_FUNC_INT(sensor2_limit, th->limits[2]) | 532 | BUILD_SHOW_FUNC_INT(sensor2_limit, thermostat->limits[2]) |
378 | BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1]) | 533 | BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1]) |
379 | BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2]) | 534 | BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2]) |
380 | 535 | ||
381 | BUILD_SHOW_FUNC_INT_LITE(specified_fan_speed, fan_speed) | 536 | BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed) |
382 | BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) | ||
383 | |||
384 | BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0) | 537 | BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0) |
385 | BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1) | 538 | BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1) |
386 | 539 | ||
387 | BUILD_SHOW_FUNC_INT_LITE(limit_adjust, limit_adjust) | 540 | BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) |
388 | BUILD_STORE_FUNC_DEG(limit_adjust, th) | 541 | BUILD_SHOW_FUNC_INT(limit_adjust, limit_adjust) |
542 | BUILD_STORE_FUNC_DEG(limit_adjust, thermostat) | ||
389 | 543 | ||
390 | static DEVICE_ATTR(sensor1_temperature, S_IRUGO, | 544 | static DEVICE_ATTR(sensor1_temperature, S_IRUGO, |
391 | show_sensor1_temperature,NULL); | 545 | show_sensor1_temperature,NULL); |
@@ -411,77 +565,53 @@ static DEVICE_ATTR(sensor2_fan_speed, S_IRUGO, | |||
411 | static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, | 565 | static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, |
412 | show_limit_adjust, store_limit_adjust); | 566 | show_limit_adjust, store_limit_adjust); |
413 | 567 | ||
414 | static void thermostat_create_files(struct thermostat *th) | 568 | |
569 | static int __init | ||
570 | thermostat_init(void) | ||
415 | { | 571 | { |
416 | struct device_node *np = th->clt->dev.of_node; | 572 | struct device_node* np; |
417 | struct device *dev; | 573 | const u32 *prop; |
418 | int err; | 574 | int i = 0, offset = 0; |
419 | 575 | ||
420 | /* To maintain ABI compatibility with userspace, create | 576 | np = of_find_node_by_name(NULL, "fan"); |
421 | * the old style platform driver and attach the attributes | 577 | if (!np) |
422 | * to it here | 578 | return -ENODEV; |
423 | */ | 579 | if (of_device_is_compatible(np, "adt7460")) |
424 | th->pdev = of_platform_device_create(np, "temperatures", NULL); | 580 | therm_type = ADT7460; |
425 | if (!th->pdev) | 581 | else if (of_device_is_compatible(np, "adt7467")) |
426 | return; | 582 | therm_type = ADT7467; |
427 | dev = &th->pdev->dev; | 583 | else { |
428 | dev_set_drvdata(dev, th); | 584 | of_node_put(np); |
429 | err = device_create_file(dev, &dev_attr_sensor1_temperature); | 585 | return -ENODEV; |
430 | err |= device_create_file(dev, &dev_attr_sensor2_temperature); | 586 | } |
431 | err |= device_create_file(dev, &dev_attr_sensor1_limit); | ||
432 | err |= device_create_file(dev, &dev_attr_sensor2_limit); | ||
433 | err |= device_create_file(dev, &dev_attr_sensor1_location); | ||
434 | err |= device_create_file(dev, &dev_attr_sensor2_location); | ||
435 | err |= device_create_file(dev, &dev_attr_limit_adjust); | ||
436 | err |= device_create_file(dev, &dev_attr_specified_fan_speed); | ||
437 | err |= device_create_file(dev, &dev_attr_sensor1_fan_speed); | ||
438 | if(th->type == ADT7460) | ||
439 | err |= device_create_file(dev, &dev_attr_sensor2_fan_speed); | ||
440 | if (err) | ||
441 | printk(KERN_WARNING | ||
442 | "Failed to create temperature attribute file(s).\n"); | ||
443 | } | ||
444 | 587 | ||
445 | static void thermostat_remove_files(struct thermostat *th) | 588 | prop = of_get_property(np, "hwsensor-params-version", NULL); |
446 | { | 589 | printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, |
447 | struct device *dev; | 590 | (*prop == 1)?"":"un"); |
591 | if (*prop != 1) { | ||
592 | of_node_put(np); | ||
593 | return -ENODEV; | ||
594 | } | ||
448 | 595 | ||
449 | if (!th->pdev) | 596 | prop = of_get_property(np, "reg", NULL); |
450 | return; | 597 | if (!prop) { |
451 | dev = &th->pdev->dev; | 598 | of_node_put(np); |
452 | device_remove_file(dev, &dev_attr_sensor1_temperature); | 599 | return -ENODEV; |
453 | device_remove_file(dev, &dev_attr_sensor2_temperature); | 600 | } |
454 | device_remove_file(dev, &dev_attr_sensor1_limit); | ||
455 | device_remove_file(dev, &dev_attr_sensor2_limit); | ||
456 | device_remove_file(dev, &dev_attr_sensor1_location); | ||
457 | device_remove_file(dev, &dev_attr_sensor2_location); | ||
458 | device_remove_file(dev, &dev_attr_limit_adjust); | ||
459 | device_remove_file(dev, &dev_attr_specified_fan_speed); | ||
460 | device_remove_file(dev, &dev_attr_sensor1_fan_speed); | ||
461 | if (th->type == ADT7460) | ||
462 | device_remove_file(dev, &dev_attr_sensor2_fan_speed); | ||
463 | of_device_unregister(th->pdev); | ||
464 | 601 | ||
465 | } | 602 | /* look for bus either by path or using "reg" */ |
603 | if (strstr(np->full_name, "/i2c-bus@") != NULL) { | ||
604 | const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9); | ||
605 | therm_bus = tmp_bus[0]-'0'; | ||
606 | } else { | ||
607 | therm_bus = ((*prop) >> 8) & 0x0f; | ||
608 | } | ||
466 | 609 | ||
467 | static int probe_thermostat(struct i2c_client *client, | 610 | therm_address = ((*prop) & 0xff) >> 1; |
468 | const struct i2c_device_id *id) | ||
469 | { | ||
470 | struct device_node *np = client->dev.of_node; | ||
471 | struct thermostat* th; | ||
472 | const __be32 *prop; | ||
473 | int i, rc, vers, offset = 0; | ||
474 | 611 | ||
475 | if (!np) | 612 | printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, " |
476 | return -ENXIO; | 613 | "limit_adjust: %d, fan_speed: %d\n", |
477 | prop = of_get_property(np, "hwsensor-params-version", NULL); | 614 | therm_bus, therm_address, limit_adjust, fan_speed); |
478 | if (!prop) | ||
479 | return -ENXIO; | ||
480 | vers = be32_to_cpup(prop); | ||
481 | printk(KERN_INFO "adt746x: version %d (%ssupported)\n", | ||
482 | vers, vers == 1 ? "" : "un"); | ||
483 | if (vers != 1) | ||
484 | return -ENXIO; | ||
485 | 615 | ||
486 | if (of_get_property(np, "hwsensor-location", NULL)) { | 616 | if (of_get_property(np, "hwsensor-location", NULL)) { |
487 | for (i = 0; i < 3; i++) { | 617 | for (i = 0; i < 3; i++) { |
@@ -494,129 +624,72 @@ static int probe_thermostat(struct i2c_client *client, | |||
494 | printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]); | 624 | printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]); |
495 | offset += strlen(sensor_location[i]) + 1; | 625 | offset += strlen(sensor_location[i]) + 1; |
496 | } | 626 | } |
627 | } else { | ||
628 | sensor_location[0] = "?"; | ||
629 | sensor_location[1] = "?"; | ||
630 | sensor_location[2] = "?"; | ||
497 | } | 631 | } |
498 | 632 | ||
499 | th = kzalloc(sizeof(struct thermostat), GFP_KERNEL); | 633 | of_dev = of_platform_device_create(np, "temperatures", NULL); |
500 | if (!th) | 634 | of_node_put(np); |
501 | return -ENOMEM; | ||
502 | |||
503 | i2c_set_clientdata(client, th); | ||
504 | th->clt = client; | ||
505 | th->type = id->driver_data; | ||
506 | 635 | ||
507 | rc = read_reg(th, CONFIG_REG); | 636 | if (of_dev == NULL) { |
508 | if (rc < 0) { | 637 | printk(KERN_ERR "Can't register temperatures device !\n"); |
509 | dev_err(&client->dev, "Thermostat failed to read config!\n"); | ||
510 | kfree(th); | ||
511 | return -ENODEV; | 638 | return -ENODEV; |
512 | } | 639 | } |
513 | 640 | ||
514 | /* force manual control to start the fan quieter */ | 641 | #ifndef CONFIG_I2C_POWERMAC |
515 | if (fan_speed == -1) | 642 | request_module("i2c-powermac"); |
516 | fan_speed = 64; | 643 | #endif |
517 | |||
518 | if (th->type == ADT7460) { | ||
519 | printk(KERN_INFO "adt746x: ADT7460 initializing\n"); | ||
520 | /* The 7460 needs to be started explicitly */ | ||
521 | write_reg(th, CONFIG_REG, 1); | ||
522 | } else | ||
523 | printk(KERN_INFO "adt746x: ADT7467 initializing\n"); | ||
524 | |||
525 | for (i = 0; i < 3; i++) { | ||
526 | th->initial_limits[i] = read_reg(th, LIMIT_REG[i]); | ||
527 | set_limit(th, i); | ||
528 | } | ||
529 | |||
530 | printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d" | ||
531 | " to %d, %d, %d\n", | ||
532 | th->initial_limits[0], th->initial_limits[1], | ||
533 | th->initial_limits[2], th->limits[0], th->limits[1], | ||
534 | th->limits[2]); | ||
535 | |||
536 | /* record invert bit status because fw can corrupt it after suspend */ | ||
537 | th->pwm_inv[0] = read_reg(th, MANUAL_MODE[0]) & INVERT_MASK; | ||
538 | th->pwm_inv[1] = read_reg(th, MANUAL_MODE[1]) & INVERT_MASK; | ||
539 | |||
540 | /* be sure to really write fan speed the first time */ | ||
541 | th->last_speed[0] = -2; | ||
542 | th->last_speed[1] = -2; | ||
543 | th->last_var[0] = -80; | ||
544 | th->last_var[1] = -80; | ||
545 | |||
546 | if (fan_speed != -1) { | ||
547 | /* manual mode, stop fans */ | ||
548 | write_both_fan_speed(th, 0); | ||
549 | } else { | ||
550 | /* automatic mode */ | ||
551 | write_both_fan_speed(th, -1); | ||
552 | } | ||
553 | |||
554 | th->thread = kthread_run(monitor_task, th, "kfand"); | ||
555 | if (th->thread == ERR_PTR(-ENOMEM)) { | ||
556 | printk(KERN_INFO "adt746x: Kthread creation failed\n"); | ||
557 | th->thread = NULL; | ||
558 | return -ENOMEM; | ||
559 | } | ||
560 | |||
561 | thermostat_create_files(th); | ||
562 | 644 | ||
563 | return 0; | 645 | return i2c_add_driver(&thermostat_driver); |
564 | } | 646 | } |
565 | 647 | ||
566 | static int remove_thermostat(struct i2c_client *client) | 648 | static void thermostat_create_files(void) |
567 | { | 649 | { |
568 | struct thermostat *th = i2c_get_clientdata(client); | 650 | int err; |
569 | int i; | ||
570 | |||
571 | thermostat_remove_files(th); | ||
572 | |||
573 | if (th->thread != NULL) | ||
574 | kthread_stop(th->thread); | ||
575 | |||
576 | printk(KERN_INFO "adt746x: Putting max temperatures back from " | ||
577 | "%d, %d, %d to %d, %d, %d\n", | ||
578 | th->limits[0], th->limits[1], th->limits[2], | ||
579 | th->initial_limits[0], th->initial_limits[1], | ||
580 | th->initial_limits[2]); | ||
581 | |||
582 | for (i = 0; i < 3; i++) | ||
583 | write_reg(th, LIMIT_REG[i], th->initial_limits[i]); | ||
584 | |||
585 | write_both_fan_speed(th, -1); | ||
586 | |||
587 | kfree(th); | ||
588 | 651 | ||
589 | return 0; | 652 | err = device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature); |
653 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature); | ||
654 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_limit); | ||
655 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_limit); | ||
656 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_location); | ||
657 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_location); | ||
658 | err |= device_create_file(&of_dev->dev, &dev_attr_limit_adjust); | ||
659 | err |= device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed); | ||
660 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); | ||
661 | if(therm_type == ADT7460) | ||
662 | err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_fan_speed); | ||
663 | if (err) | ||
664 | printk(KERN_WARNING | ||
665 | "Failed to create temperature attribute file(s).\n"); | ||
590 | } | 666 | } |
591 | 667 | ||
592 | static const struct i2c_device_id therm_adt746x_id[] = { | 668 | static void thermostat_remove_files(void) |
593 | { "MAC,adt7460", ADT7460 }, | ||
594 | { "MAC,adt7467", ADT7467 }, | ||
595 | { } | ||
596 | }; | ||
597 | MODULE_DEVICE_TABLE(i2c, therm_adt746x_id); | ||
598 | |||
599 | static struct i2c_driver thermostat_driver = { | ||
600 | .driver = { | ||
601 | .name = "therm_adt746x", | ||
602 | }, | ||
603 | .probe = probe_thermostat, | ||
604 | .remove = remove_thermostat, | ||
605 | .id_table = therm_adt746x_id, | ||
606 | }; | ||
607 | |||
608 | static int __init thermostat_init(void) | ||
609 | { | 669 | { |
610 | #ifndef CONFIG_I2C_POWERMAC | 670 | if (of_dev) { |
611 | request_module("i2c-powermac"); | 671 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature); |
612 | #endif | 672 | device_remove_file(&of_dev->dev, &dev_attr_sensor2_temperature); |
673 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_limit); | ||
674 | device_remove_file(&of_dev->dev, &dev_attr_sensor2_limit); | ||
675 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_location); | ||
676 | device_remove_file(&of_dev->dev, &dev_attr_sensor2_location); | ||
677 | device_remove_file(&of_dev->dev, &dev_attr_limit_adjust); | ||
678 | device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed); | ||
679 | device_remove_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); | ||
680 | |||
681 | if(therm_type == ADT7460) | ||
682 | device_remove_file(&of_dev->dev, | ||
683 | &dev_attr_sensor2_fan_speed); | ||
613 | 684 | ||
614 | return i2c_add_driver(&thermostat_driver); | 685 | } |
615 | } | 686 | } |
616 | 687 | ||
617 | static void __exit thermostat_exit(void) | 688 | static void __exit |
689 | thermostat_exit(void) | ||
618 | { | 690 | { |
619 | i2c_del_driver(&thermostat_driver); | 691 | i2c_del_driver(&thermostat_driver); |
692 | of_device_unregister(of_dev); | ||
620 | } | 693 | } |
621 | 694 | ||
622 | module_init(thermostat_init); | 695 | module_init(thermostat_init); |