1/* 2 * Device driver for the i2c thermostat found on the iBook G4, Albook G4 3 * 4 * Copyright (C) 2003, 2004 Colin Leroy, Rasmus Rohde, Benjamin Herrenschmidt 5 * 6 * Documentation from 7 * http://www.analog.com/UploadedFiles/Data_Sheets/115254175ADT7467_pra.pdf 8 * http://www.analog.com/UploadedFiles/Data_Sheets/3686221171167ADT7460_b.pdf 9 * 10 */ 11 12#include <linux/types.h> 13#include <linux/module.h> 14#include <linux/errno.h> 15#include <linux/kernel.h> 16#include <linux/delay.h> 17#include <linux/sched.h> 18#include <linux/i2c.h> 19#include <linux/slab.h> 20#include <linux/init.h> 21#include <linux/spinlock.h> 22#include <linux/wait.h> 23#include <linux/suspend.h> 24#include <linux/kthread.h> 25#include <linux/moduleparam.h> 26#include <linux/freezer.h> 27#include <linux/of_platform.h> 28 29#include <asm/prom.h> 30#include <asm/machdep.h> 31#include <asm/io.h> 32#include <asm/system.h> 33#include <asm/sections.h> 34 35#undef DEBUG 36 37#define CONFIG_REG 0x40 38#define MANUAL_MASK 0xe0 39#define AUTO_MASK 0x20 40#define INVERT_MASK 0x10 41 42static u8 TEMP_REG[3] = {0x26, 0x25, 0x27}; /* local, sensor1, sensor2 */ 43static u8 LIMIT_REG[3] = {0x6b, 0x6a, 0x6c}; /* local, sensor1, sensor2 */ 44static u8 MANUAL_MODE[2] = {0x5c, 0x5d}; 45static u8 REM_CONTROL[2] = {0x00, 0x40}; 46static u8 FAN_SPEED[2] = {0x28, 0x2a}; 47static u8 FAN_SPD_SET[2] = {0x30, 0x31}; 48 49static u8 default_limits_local[3] = {70, 50, 70}; /* local, sensor1, sensor2 */ 50static u8 default_limits_chip[3] = {80, 65, 80}; /* local, sensor1, sensor2 */ 51static const char *sensor_location[3]; 52 53static int limit_adjust; 54static int fan_speed = -1; 55static int verbose; 56 57MODULE_AUTHOR("Colin Leroy <colin@colino.net>"); 58MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and " 59 "Powerbook G4 Alu"); 60MODULE_LICENSE("GPL"); 61 62module_param(limit_adjust, int, 0644); 63MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50 sensor1, 70 sensor2) " 64 "by N degrees."); 65 66module_param(fan_speed, int, 0644); 67MODULE_PARM_DESC(fan_speed,"Specify starting fan speed (0-255) " 68 "(default 64)"); 69 70module_param(verbose, bool, 0); 71MODULE_PARM_DESC(verbose,"Verbose log operations " 72 "(default 0)"); 73 74struct thermostat { 75 struct i2c_client *clt; 76 u8 temps[3]; 77 u8 cached_temp[3]; 78 u8 initial_limits[3]; 79 u8 limits[3]; 80 int last_speed[2]; 81 int last_var[2]; 82 int pwm_inv[2]; 83}; 84 85static enum {ADT7460, ADT7467} therm_type; 86static int therm_bus, therm_address; 87static struct platform_device * of_dev; 88static struct thermostat* thermostat; 89static struct task_struct *thread_therm = NULL; 90 91static void write_both_fan_speed(struct thermostat *th, int speed); 92static void write_fan_speed(struct thermostat *th, int speed, int fan); 93static void thermostat_create_files(void); 94static void thermostat_remove_files(void); 95 96static int 97write_reg(struct thermostat* th, int reg, u8 data) 98{ 99 u8 tmp[2]; 100 int rc; 101 102 tmp[0] = reg; 103 tmp[1] = data; 104 rc = i2c_master_send(th->clt, (const char *)tmp, 2); 105 if (rc < 0) 106 return rc; 107 if (rc != 2) 108 return -ENODEV; 109 return 0; 110} 111 112static int 113read_reg(struct thermostat* th, int reg) 114{ 115 u8 reg_addr, data; 116 int rc; 117 118 reg_addr = (u8)reg; 119 rc = i2c_master_send(th->clt, ®_addr, 1); 120 if (rc < 0) 121 return rc; 122 if (rc != 1) 123 return -ENODEV; 124 rc = i2c_master_recv(th->clt, (char *)&data, 1); 125 if (rc < 0) 126 return rc; 127 return data; 128} 129 130static struct i2c_driver thermostat_driver; 131 132static int 133attach_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 160static int 161remove_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 190static int read_fan_speed(struct thermostat *th, u8 addr) 191{ 192 u8 tmp[2]; 193 u16 res; 194 195 /* should start with low byte */ 196 tmp[1] = read_reg(th, addr); 197 tmp[0] = read_reg(th, addr + 1); 198 199 res = tmp[1] + (tmp[0] << 8); 200 /* "a value of 0xffff means that the fan has stopped" */ 201 return (res == 0xffff ? 0 : (90000*60)/res); 202} 203 204static void write_both_fan_speed(struct thermostat *th, int speed) 205{ 206 write_fan_speed(th, speed, 0); 207 if (therm_type == ADT7460) 208 write_fan_speed(th, speed, 1); 209} 210 211static void write_fan_speed(struct thermostat *th, int speed, int fan) 212{ 213 u8 manual; 214 215 if (speed > 0xff) 216 speed = 0xff; 217 else if (speed < -1) 218 speed = 0; 219 220 if (therm_type == ADT7467 && fan == 1) 221 return; 222 223 if (th->last_speed[fan] != speed) { 224 if (verbose) { 225 if (speed == -1) 226 printk(KERN_DEBUG "adt746x: Setting speed to automatic " 227 "for %s fan.\n", sensor_location[fan+1]); 228 else 229 printk(KERN_DEBUG "adt746x: Setting speed to %d " 230 "for %s fan.\n", speed, sensor_location[fan+1]); 231 } 232 } else 233 return; 234 235 if (speed >= 0) { 236 manual = read_reg(th, MANUAL_MODE[fan]); 237 manual &= ~INVERT_MASK; 238 write_reg(th, MANUAL_MODE[fan], 239 manual | MANUAL_MASK | th->pwm_inv[fan]); 240 write_reg(th, FAN_SPD_SET[fan], speed); 241 } else { 242 /* back to automatic */ 243 if(therm_type == ADT7460) { 244 manual = read_reg(th, 245 MANUAL_MODE[fan]) & (~MANUAL_MASK); 246 manual &= ~INVERT_MASK; 247 manual |= th->pwm_inv[fan]; 248 write_reg(th, 249 MANUAL_MODE[fan], manual|REM_CONTROL[fan]); 250 } else { 251 manual = read_reg(th, MANUAL_MODE[fan]); 252 manual &= ~INVERT_MASK; 253 manual |= th->pwm_inv[fan]; 254 write_reg(th, MANUAL_MODE[fan], manual&(~AUTO_MASK)); 255 } 256 } 257 258 th->last_speed[fan] = speed; 259} 260 261static void read_sensors(struct thermostat *th) 262{ 263 int i = 0; 264 265 for (i = 0; i < 3; i++) 266 th->temps[i] = read_reg(th, TEMP_REG[i]); 267} 268 269#ifdef DEBUG 270static void display_stats(struct thermostat *th) 271{ 272 if (th->temps[0] != th->cached_temp[0] 273 || th->temps[1] != th->cached_temp[1] 274 || th->temps[2] != th->cached_temp[2]) { 275 printk(KERN_INFO "adt746x: Temperature infos:" 276 " thermostats: %d,%d,%d;" 277 " limits: %d,%d,%d;" 278 " fan speed: %d RPM\n", 279 th->temps[0], th->temps[1], th->temps[2], 280 th->limits[0], th->limits[1], th->limits[2], 281 read_fan_speed(th, FAN_SPEED[0])); 282 } 283 th->cached_temp[0] = th->temps[0]; 284 th->cached_temp[1] = th->temps[1]; 285 th->cached_temp[2] = th->temps[2]; 286} 287#endif 288 289static void update_fans_speed (struct thermostat *th) 290{ 291 int lastvar = 0; /* last variation, for iBook */ 292 int i = 0; 293 294 /* we don't care about local sensor, so we start at sensor 1 */ 295 for (i = 1; i < 3; i++) { 296 int started = 0; 297 int fan_number = (therm_type == ADT7460 && i == 2); 298 int var = th->temps[i] - th->limits[i]; 299 300 if (var > -1) { 301 int step = (255 - fan_speed) / 7; 302 int new_speed = 0; 303 304 /* hysteresis : change fan speed only if variation is 305 * more than two degrees */ 306 if (abs(var - th->last_var[fan_number]) < 2) 307 continue; 308 309 started = 1; 310 new_speed = fan_speed + ((var-1)*step); 311 312 if (new_speed < fan_speed) 313 new_speed = fan_speed; 314 if (new_speed > 255) 315 new_speed = 255; 316 317 if (verbose) 318 printk(KERN_DEBUG "adt746x: Setting fans speed to %d " 319 "(limit exceeded by %d on %s)\n", 320 new_speed, var, 321 sensor_location[fan_number+1]); 322 write_both_fan_speed(th, new_speed); 323 th->last_var[fan_number] = var; 324 } else if (var < -2) { 325 /* don't stop fan if sensor2 is cold and sensor1 is not 326 * so cold (lastvar >= -1) */ 327 if (i == 2 && lastvar < -1) { 328 if (th->last_speed[fan_number] != 0) 329 if (verbose) 330 printk(KERN_DEBUG "adt746x: Stopping " 331 "fans.\n"); 332 write_both_fan_speed(th, 0); 333 } 334 } 335 336 lastvar = var; 337 338 if (started) 339 return; /* we don't want to re-stop the fan 340 * if sensor1 is heating and sensor2 is not */ 341 } 342} 343 344static int monitor_task(void *arg) 345{ 346 struct thermostat* th = arg; 347 348 set_freezable(); 349 while(!kthread_should_stop()) { 350 try_to_freeze(); 351 msleep_interruptible(2000); 352 353#ifndef DEBUG 354 if (fan_speed != -1) 355 read_sensors(th); 356#else 357 read_sensors(th); 358#endif 359 360 if (fan_speed != -1) 361 update_fans_speed(th); 362 363#ifdef DEBUG 364 display_stats(th); 365#endif 366 367 } 368 369 return 0; 370} 371 372static void set_limit(struct thermostat *th, int i) 373{ 374 /* Set sensor1 limit higher to avoid powerdowns */ 375 th->limits[i] = default_limits_chip[i] + limit_adjust; 376 write_reg(th, LIMIT_REG[i], th->limits[i]); 377 378 /* set our limits to normal */ 379 th->limits[i] = default_limits_local[i] + limit_adjust; 380} 381 382static int probe_thermostat(struct i2c_client *client, 383 const struct i2c_device_id *id) 384{ 385 struct thermostat* th; 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; 459} 460 461static const struct i2c_device_id therm_adt746x_id[] = { 462 { "therm_adt746x", 0 }, 463 { } 464}; 465 466static 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#define BUILD_SHOW_FUNC_INT(name, data) \ 477static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ 478{ \ 479 return sprintf(buf, "%d\n", data); \ 480} 481 482#define BUILD_SHOW_FUNC_STR(name, data) \ 483static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ 484{ \ 485 return sprintf(buf, "%s\n", data); \ 486} 487 488#define BUILD_SHOW_FUNC_FAN(name, data) \ 489static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ 490{ \ 491 return sprintf(buf, "%d (%d rpm)\n", \ 492 thermostat->last_speed[data], \ 493 read_fan_speed(thermostat, FAN_SPEED[data]) \ 494 ); \ 495} 496 497#define BUILD_STORE_FUNC_DEG(name, data) \ 498static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ 499{ \ 500 int val; \ 501 int i; \ 502 val = simple_strtol(buf, NULL, 10); \ 503 printk(KERN_INFO "Adjusting limits by %d degrees\n", val); \ 504 limit_adjust = val; \ 505 for (i=0; i < 3; i++) \ 506 set_limit(thermostat, i); \ 507 return n; \ 508} 509 510#define BUILD_STORE_FUNC_INT(name, data) \ 511static ssize_t store_##name(struct device *dev, struct device_attribute *attr, const char *buf, size_t n) \ 512{ \ 513 int val; \ 514 val = simple_strtol(buf, NULL, 10); \ 515 if (val < 0 || val > 255) \ 516 return -EINVAL; \ 517 printk(KERN_INFO "Setting specified fan speed to %d\n", val); \ 518 data = val; \ 519 return n; \ 520} 521 522BUILD_SHOW_FUNC_INT(sensor1_temperature, (read_reg(thermostat, TEMP_REG[1]))) 523BUILD_SHOW_FUNC_INT(sensor2_temperature, (read_reg(thermostat, TEMP_REG[2]))) 524BUILD_SHOW_FUNC_INT(sensor1_limit, thermostat->limits[1]) 525BUILD_SHOW_FUNC_INT(sensor2_limit, thermostat->limits[2]) 526BUILD_SHOW_FUNC_STR(sensor1_location, sensor_location[1]) 527BUILD_SHOW_FUNC_STR(sensor2_location, sensor_location[2]) 528 529BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed) 530BUILD_SHOW_FUNC_FAN(sensor1_fan_speed, 0) 531BUILD_SHOW_FUNC_FAN(sensor2_fan_speed, 1) 532 533BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed) 534BUILD_SHOW_FUNC_INT(limit_adjust, limit_adjust) 535BUILD_STORE_FUNC_DEG(limit_adjust, thermostat) 536 537static DEVICE_ATTR(sensor1_temperature, S_IRUGO, 538 show_sensor1_temperature,NULL); 539static DEVICE_ATTR(sensor2_temperature, S_IRUGO, 540 show_sensor2_temperature,NULL); 541static DEVICE_ATTR(sensor1_limit, S_IRUGO, 542 show_sensor1_limit, NULL); 543static DEVICE_ATTR(sensor2_limit, S_IRUGO, 544 show_sensor2_limit, NULL); 545static DEVICE_ATTR(sensor1_location, S_IRUGO, 546 show_sensor1_location, NULL); 547static DEVICE_ATTR(sensor2_location, S_IRUGO, 548 show_sensor2_location, NULL); 549 550static DEVICE_ATTR(specified_fan_speed, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, 551 show_specified_fan_speed,store_specified_fan_speed); 552 553static DEVICE_ATTR(sensor1_fan_speed, S_IRUGO, 554 show_sensor1_fan_speed, NULL); 555static DEVICE_ATTR(sensor2_fan_speed, S_IRUGO, 556 show_sensor2_fan_speed, NULL); 557 558static DEVICE_ATTR(limit_adjust, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, 559 show_limit_adjust, store_limit_adjust); 560 561 562static int __init 563thermostat_init(void) 564{ 565 struct device_node* np; 566 const u32 *prop; 567 int i = 0, offset = 0; 568 569 np = of_find_node_by_name(NULL, "fan"); 570 if (!np) 571 return -ENODEV; 572 if (of_device_is_compatible(np, "adt7460")) 573 therm_type = ADT7460; 574 else if (of_device_is_compatible(np, "adt7467")) 575 therm_type = ADT7467; 576 else { 577 of_node_put(np); 578 return -ENODEV; 579 } 580 581 prop = of_get_property(np, "hwsensor-params-version", NULL); 582 printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop, 583 (*prop == 1)?"":"un"); 584 if (*prop != 1) { 585 of_node_put(np); 586 return -ENODEV; 587 } 588 589 prop = of_get_property(np, "reg", NULL); 590 if (!prop) { 591 of_node_put(np); 592 return -ENODEV; 593 } 594 595 /* look for bus either by path or using "reg" */ 596 if (strstr(np->full_name, "/i2c-bus@") != NULL) { 597 const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9); 598 therm_bus = tmp_bus[0]-'0'; 599 } else { 600 therm_bus = ((*prop) >> 8) & 0x0f; 601 } 602 603 therm_address = ((*prop) & 0xff) >> 1; 604 605 printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, " 606 "limit_adjust: %d, fan_speed: %d\n", 607 therm_bus, therm_address, limit_adjust, fan_speed); 608 609 if (of_get_property(np, "hwsensor-location", NULL)) { 610 for (i = 0; i < 3; i++) { 611 sensor_location[i] = of_get_property(np, 612 "hwsensor-location", NULL) + offset; 613 614 if (sensor_location[i] == NULL) 615 sensor_location[i] = ""; 616 617 printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]); 618 offset += strlen(sensor_location[i]) + 1; 619 } 620 } else { 621 sensor_location[0] = "?"; 622 sensor_location[1] = "?"; 623 sensor_location[2] = "?"; 624 } 625 626 of_dev = of_platform_device_create(np, "temperatures", NULL); 627 of_node_put(np); 628 629 if (of_dev == NULL) { 630 printk(KERN_ERR "Can't register temperatures device !\n"); 631 return -ENODEV; 632 } 633 634#ifndef CONFIG_I2C_POWERMAC 635 request_module("i2c-powermac"); 636#endif 637 638 return i2c_add_driver(&thermostat_driver); 639} 640 641static void thermostat_create_files(void) 642{ 643 int err; 644 645 err = device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature); 646 err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature); 647 err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_limit); 648 err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_limit); 649 err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_location); 650 err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_location); 651 err |= device_create_file(&of_dev->dev, &dev_attr_limit_adjust); 652 err |= device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed); 653 err |= device_create_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); 654 if(therm_type == ADT7460) 655 err |= device_create_file(&of_dev->dev, &dev_attr_sensor2_fan_speed); 656 if (err) 657 printk(KERN_WARNING 658 "Failed to create tempertaure attribute file(s).\n"); 659} 660 661static void thermostat_remove_files(void) 662{ 663 if (of_dev) { 664 device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature); 665 device_remove_file(&of_dev->dev, &dev_attr_sensor2_temperature); 666 device_remove_file(&of_dev->dev, &dev_attr_sensor1_limit); 667 device_remove_file(&of_dev->dev, &dev_attr_sensor2_limit); 668 device_remove_file(&of_dev->dev, &dev_attr_sensor1_location); 669 device_remove_file(&of_dev->dev, &dev_attr_sensor2_location); 670 device_remove_file(&of_dev->dev, &dev_attr_limit_adjust); 671 device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed); 672 device_remove_file(&of_dev->dev, &dev_attr_sensor1_fan_speed); 673 674 if(therm_type == ADT7460) 675 device_remove_file(&of_dev->dev, 676 &dev_attr_sensor2_fan_speed); 677 678 } 679} 680 681static void __exit 682thermostat_exit(void) 683{ 684 i2c_del_driver(&thermostat_driver); 685 of_device_unregister(of_dev); 686} 687 688module_init(thermostat_init); 689module_exit(thermostat_exit); 690