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