linux/drivers/macintosh/therm_adt746x.c
<<
>>
Prefs
   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/config.h>
  13#include <linux/types.h>
  14#include <linux/module.h>
  15#include <linux/errno.h>
  16#include <linux/kernel.h>
  17#include <linux/delay.h>
  18#include <linux/sched.h>
  19#include <linux/i2c.h>
  20#include <linux/slab.h>
  21#include <linux/init.h>
  22#include <linux/spinlock.h>
  23#include <linux/smp_lock.h>
  24#include <linux/wait.h>
  25#include <linux/suspend.h>
  26#include <linux/kthread.h>
  27#include <linux/moduleparam.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#include <asm/of_device.h>
  35
  36#undef DEBUG
  37
  38#define CONFIG_REG   0x40
  39#define MANUAL_MASK  0xe0
  40#define AUTO_MASK    0x20
  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 char *sensor_location[3] = {NULL, NULL, NULL};
  52
  53static int limit_adjust = 0;
  54static int fan_speed = -1;
  55
  56MODULE_AUTHOR("Colin Leroy <colin@colino.net>");
  57MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and "
  58                   "Powerbook G4 Alu");
  59MODULE_LICENSE("GPL");
  60
  61module_param(limit_adjust, int, 0644);
  62MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50 sensor1, 70 sensor2) "
  63                 "by N degrees.");
  64
  65module_param(fan_speed, int, 0644);
  66MODULE_PARM_DESC(fan_speed,"Specify starting fan speed (0-255) "
  67                 "(default 64)");
  68
  69struct thermostat {
  70        struct i2c_client       clt;
  71        u8                      temps[3];
  72        u8                      cached_temp[3];
  73        u8                      initial_limits[3];
  74        u8                      limits[3];
  75        int                     last_speed[2];
  76        int                     last_var[2];
  77};
  78
  79static enum {ADT7460, ADT7467} therm_type;
  80static int therm_bus, therm_address;
  81static struct of_device * of_dev;
  82static struct thermostat* thermostat;
  83static struct task_struct *thread_therm = NULL;
  84
  85static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
  86                                 int busno);
  87
  88static void write_both_fan_speed(struct thermostat *th, int speed);
  89static void write_fan_speed(struct thermostat *th, int speed, int fan);
  90
  91static int
  92write_reg(struct thermostat* th, int reg, u8 data)
  93{
  94        u8 tmp[2];
  95        int rc;
  96        
  97        tmp[0] = reg;
  98        tmp[1] = data;
  99        rc = i2c_master_send(&th->clt, (const char *)tmp, 2);
 100        if (rc < 0)
 101                return rc;
 102        if (rc != 2)
 103                return -ENODEV;
 104        return 0;
 105}
 106
 107static int
 108read_reg(struct thermostat* th, int reg)
 109{
 110        u8 reg_addr, data;
 111        int rc;
 112
 113        reg_addr = (u8)reg;
 114        rc = i2c_master_send(&th->clt, &reg_addr, 1);
 115        if (rc < 0)
 116                return rc;
 117        if (rc != 1)
 118                return -ENODEV;
 119        rc = i2c_master_recv(&th->clt, (char *)&data, 1);
 120        if (rc < 0)
 121                return rc;
 122        return data;
 123}
 124
 125static int
 126attach_thermostat(struct i2c_adapter *adapter)
 127{
 128        unsigned long bus_no;
 129
 130        if (strncmp(adapter->name, "uni-n", 5))
 131                return -ENODEV;
 132        bus_no = simple_strtoul(adapter->name + 6, NULL, 10);
 133        if (bus_no != therm_bus)
 134                return -ENODEV;
 135        return attach_one_thermostat(adapter, therm_address, bus_no);
 136}
 137
 138static int
 139detach_thermostat(struct i2c_adapter *adapter)
 140{
 141        struct thermostat* th;
 142        int i;
 143        
 144        if (thermostat == NULL)
 145                return 0;
 146
 147        th = thermostat;
 148
 149        if (thread_therm != NULL) {
 150                kthread_stop(thread_therm);
 151        }
 152                
 153        printk(KERN_INFO "adt746x: Putting max temperatures back from "
 154                         "%d, %d, %d to %d, %d, %d\n",
 155                th->limits[0], th->limits[1], th->limits[2],
 156                th->initial_limits[0], th->initial_limits[1],
 157                th->initial_limits[2]);
 158        
 159        for (i = 0; i < 3; i++)
 160                write_reg(th, LIMIT_REG[i], th->initial_limits[i]);
 161
 162        write_both_fan_speed(th, -1);
 163
 164        i2c_detach_client(&th->clt);
 165
 166        thermostat = NULL;
 167
 168        kfree(th);
 169
 170        return 0;
 171}
 172
 173static struct i2c_driver thermostat_driver = {  
 174        .owner          = THIS_MODULE,
 175        .name           = "therm_adt746x",
 176        .flags          = I2C_DF_NOTIFY,
 177        .attach_adapter = attach_thermostat,
 178        .detach_adapter = detach_thermostat,
 179};
 180
 181static int read_fan_speed(struct thermostat *th, u8 addr)
 182{
 183        u8 tmp[2];
 184        u16 res;
 185        
 186        /* should start with low byte */
 187        tmp[1] = read_reg(th, addr);
 188        tmp[0] = read_reg(th, addr + 1);
 189        
 190        res = tmp[1] + (tmp[0] << 8);
 191        /* "a value of 0xffff means that the fan has stopped" */
 192        return (res == 0xffff ? 0 : (90000*60)/res);
 193}
 194
 195static void write_both_fan_speed(struct thermostat *th, int speed)
 196{
 197        write_fan_speed(th, speed, 0);
 198        if (therm_type == ADT7460)
 199                write_fan_speed(th, speed, 1);
 200}
 201
 202static void write_fan_speed(struct thermostat *th, int speed, int fan)
 203{
 204        u8 manual;
 205        
 206        if (speed > 0xff) 
 207                speed = 0xff;
 208        else if (speed < -1) 
 209                speed = 0;
 210        
 211        if (therm_type == ADT7467 && fan == 1)
 212                return;
 213        
 214        if (th->last_speed[fan] != speed) {
 215                if (speed == -1)
 216                        printk(KERN_DEBUG "adt746x: Setting speed to automatic "
 217                                "for %s fan.\n", sensor_location[fan+1]);
 218                else
 219                        printk(KERN_DEBUG "adt746x: Setting speed to %d "
 220                                "for %s fan.\n", speed, sensor_location[fan+1]);
 221        } else
 222                return;
 223        
 224        if (speed >= 0) {
 225                manual = read_reg(th, MANUAL_MODE[fan]);
 226                write_reg(th, MANUAL_MODE[fan], manual|MANUAL_MASK);
 227                write_reg(th, FAN_SPD_SET[fan], speed);
 228        } else {
 229                /* back to automatic */
 230                if(therm_type == ADT7460) {
 231                        manual = read_reg(th,
 232                                MANUAL_MODE[fan]) & (~MANUAL_MASK);
 233
 234                        write_reg(th,
 235                                MANUAL_MODE[fan], manual|REM_CONTROL[fan]);
 236                } else {
 237                        manual = read_reg(th, MANUAL_MODE[fan]);
 238                        write_reg(th, MANUAL_MODE[fan], manual&(~AUTO_MASK));
 239                }
 240        }
 241        
 242        th->last_speed[fan] = speed;                    
 243}
 244
 245static void read_sensors(struct thermostat *th)
 246{
 247        int i = 0;
 248
 249        for (i = 0; i < 3; i++)
 250                th->temps[i]  = read_reg(th, TEMP_REG[i]);
 251}
 252
 253#ifdef DEBUG
 254static void display_stats(struct thermostat *th)
 255{
 256        if (th->temps[0] != th->cached_temp[0]
 257        ||  th->temps[1] != th->cached_temp[1]
 258        ||  th->temps[2] != th->cached_temp[2]) {
 259                printk(KERN_INFO "adt746x: Temperature infos:"
 260                                 " thermostats: %d,%d,%d;"
 261                                 " limits: %d,%d,%d;"
 262                                 " fan speed: %d RPM\n",
 263                                 th->temps[0], th->temps[1], th->temps[2],
 264                                 th->limits[0],  th->limits[1],  th->limits[2],
 265                                 read_fan_speed(th, FAN_SPEED[0]));
 266        }
 267        th->cached_temp[0] = th->temps[0];
 268        th->cached_temp[1] = th->temps[1];
 269        th->cached_temp[2] = th->temps[2];
 270}
 271#endif
 272
 273static void update_fans_speed (struct thermostat *th)
 274{
 275        int lastvar = 0; /* last variation, for iBook */
 276        int i = 0;
 277
 278        /* we don't care about local sensor, so we start at sensor 1 */
 279        for (i = 1; i < 3; i++) {
 280                int started = 0;
 281                int fan_number = (therm_type == ADT7460 && i == 2);
 282                int var = th->temps[i] - th->limits[i];
 283
 284                if (var > -1) {
 285                        int step = (255 - fan_speed) / 7;
 286                        int new_speed = 0;
 287
 288                        /* hysteresis : change fan speed only if variation is
 289                         * more than two degrees */
 290                        if (abs(var - th->last_var[fan_number]) < 2)
 291                                continue;
 292
 293                        started = 1;
 294                        new_speed = fan_speed + ((var-1)*step);
 295
 296                        if (new_speed < fan_speed)
 297                                new_speed = fan_speed;
 298                        if (new_speed > 255)
 299                                new_speed = 255;
 300
 301                        printk(KERN_DEBUG "adt746x: setting fans speed to %d "
 302                                         "(limit exceeded by %d on %s) \n",
 303                                        new_speed, var,
 304                                        sensor_location[fan_number+1]);
 305                        write_both_fan_speed(th, new_speed);
 306                        th->last_var[fan_number] = var;
 307                } else if (var < -2) {
 308                        /* don't stop fan if sensor2 is cold and sensor1 is not
 309                         * so cold (lastvar >= -1) */
 310                        if (i == 2 && lastvar < -1) {
 311                                if (th->last_speed[fan_number] != 0)
 312                                        printk(KERN_DEBUG "adt746x: Stopping "
 313                                                "fans.\n");
 314                                write_both_fan_speed(th, 0);
 315                        }
 316                }
 317
 318                lastvar = var;
 319
 320                if (started)
 321                        return; /* we don't want to re-stop the fan
 322                                * if sensor1 is heating and sensor2 is not */
 323        }
 324}
 325
 326static int monitor_task(void *arg)
 327{
 328        struct thermostat* th = arg;
 329
 330        while(!kthread_should_stop()) {
 331                if (current->flags & PF_FREEZE)
 332                        refrigerator(PF_FREEZE);
 333
 334                msleep_interruptible(2000);
 335
 336#ifndef DEBUG
 337                if (fan_speed != -1)
 338                        read_sensors(th);
 339#else
 340                read_sensors(th);
 341#endif          
 342
 343                if (fan_speed != -1)
 344                        update_fans_speed(th);
 345
 346#ifdef DEBUG
 347                display_stats(th);
 348#endif
 349
 350        }
 351
 352        return 0;
 353}
 354
 355static void set_limit(struct thermostat *th, int i)
 356{
 357                /* Set sensor1 limit higher to avoid powerdowns */
 358                th->limits[i] = default_limits_chip[i] + limit_adjust;
 359                write_reg(th, LIMIT_REG[i], th->limits[i]);
 360                
 361                /* set our limits to normal */
 362                th->limits[i] = default_limits_local[i] + limit_adjust;
 363}
 364
 365static int attach_one_thermostat(struct i2c_adapter *adapter, int addr,
 366                                 int busno)
 367{
 368        struct thermostat* th;
 369        int rc;
 370        int i;
 371
 372        if (thermostat)
 373                return 0;
 374
 375        th = (struct thermostat *)
 376                kmalloc(sizeof(struct thermostat), GFP_KERNEL);
 377
 378        if (!th)
 379                return -ENOMEM;
 380
 381        memset(th, 0, sizeof(*th));
 382        th->clt.addr = addr;
 383        th->clt.adapter = adapter;
 384        th->clt.driver = &thermostat_driver;
 385        strcpy(th->clt.name, "thermostat");
 386
 387        rc = read_reg(th, 0);
 388        if (rc < 0) {
 389                printk(KERN_ERR "adt746x: Thermostat failed to read config "
 390                                "from bus %d !\n",
 391                                busno);
 392                kfree(th);
 393                return -ENODEV;
 394        }
 395
 396        /* force manual control to start the fan quieter */
 397        if (fan_speed == -1)
 398                fan_speed = 64;
 399        
 400        if(therm_type == ADT7460) {
 401                printk(KERN_INFO "adt746x: ADT7460 initializing\n");
 402                /* The 7460 needs to be started explicitly */
 403                write_reg(th, CONFIG_REG, 1);
 404        } else
 405                printk(KERN_INFO "adt746x: ADT7467 initializing\n");
 406
 407        for (i = 0; i < 3; i++) {
 408                th->initial_limits[i] = read_reg(th, LIMIT_REG[i]);
 409                set_limit(th, i);
 410        }
 411        
 412        printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"
 413                         " to %d, %d, %d\n",
 414                         th->initial_limits[0], th->initial_limits[1],
 415                         th->initial_limits[2], th->limits[0], th->limits[1],
 416                         th->limits[2]);
 417
 418        thermostat = th;
 419
 420        if (i2c_attach_client(&th->clt)) {
 421                printk(KERN_INFO "adt746x: Thermostat failed to attach "
 422                                 "client !\n");
 423                thermostat = NULL;
 424                kfree(th);
 425                return -ENODEV;
 426        }
 427
 428        /* be sure to really write fan speed the first time */
 429        th->last_speed[0] = -2;
 430        th->last_speed[1] = -2;
 431        th->last_var[0] = -80;
 432        th->last_var[1] = -80;
 433
 434        if (fan_speed != -1) {
 435                /* manual mode, stop fans */
 436                write_both_fan_speed(th, 0);
 437        } else {
 438                /* automatic mode */
 439                write_both_fan_speed(th, -1);
 440        }
 441        
 442        thread_therm = kthread_run(monitor_task, th, "kfand");
 443
 444        if (thread_therm == ERR_PTR(-ENOMEM)) {
 445                printk(KERN_INFO "adt746x: Kthread creation failed\n");
 446                thread_therm = NULL;
 447                return -ENOMEM;
 448        }
 449
 450        return 0;
 451}
 452
 453/* 
 454 * Now, unfortunately, sysfs doesn't give us a nice void * we could
 455 * pass around to the attribute functions, so we don't really have
 456 * choice but implement a bunch of them...
 457 *
 458 */
 459#define BUILD_SHOW_FUNC_INT(name, data)                         \
 460static ssize_t show_##name(struct device *dev, char *buf)       \
 461{                                                               \
 462        return sprintf(buf, "%d\n", data);                      \
 463}
 464
 465#define BUILD_SHOW_FUNC_STR(name, data)                         \
 466static ssize_t show_##name(struct device *dev, char *buf)       \
 467{                                                               \
 468        return sprintf(buf, "%s\n", data);                      \
 469}
 470
 471#define BUILD_SHOW_FUNC_FAN(name, data)                         \
 472static ssize_t show_##name(struct device *dev, char *buf)       \
 473{                                                               \
 474        return sprintf(buf, "%d (%d rpm)\n",                    \
 475                thermostat->last_speed[data],                   \
 476                read_fan_speed(thermostat, FAN_SPEED[data])     \
 477                );                                              \
 478}
 479
 480#define BUILD_STORE_FUNC_DEG(name, data)                        \
 481static ssize_t store_##name(struct device *dev, const char *buf, size_t n) \
 482{                                                               \
 483        int val;                                                \
 484        int i;                                                  \
 485        val = simple_strtol(buf, NULL, 10);                     \
 486        printk(KERN_INFO "Adjusting limits by %d degrees\n", val);      \
 487        limit_adjust = val;                                     \
 488        for (i=0; i < 3; i++)                                   \
 489                set_limit(thermostat, i);                       \
 490        return n;                                               \
 491}
 492
 493#define BUILD_STORE_FUNC_INT(name, data)                        \
 494static ssize_t store_##name(struct device *dev, const char *buf, size_t n) \
 495{                                                               \
 496        u32 val;                                                \
 497        val = simple_strtoul(buf, NULL, 10);                    \
 498        if (val < 0 || val > 255)                               \
 499                return -EINVAL;                                 \
 500        printk(KERN_INFO "Setting specified fan speed to %d\n", val);   \
 501        data = val;                                             \
 502        return n;                                               \
 503}
 504
 505BUILD_SHOW_FUNC_INT(sensor1_temperature,         (read_reg(thermostat, TEMP_REG[1])))
 506BUILD_SHOW_FUNC_INT(sensor2_temperature,         (read_reg(thermostat, TEMP_REG[2])))
 507BUILD_SHOW_FUNC_INT(sensor1_limit,               thermostat->limits[1])
 508BUILD_SHOW_FUNC_INT(sensor2_limit,               thermostat->limits[2])
 509BUILD_SHOW_FUNC_STR(sensor1_location,            sensor_location[1])
 510BUILD_SHOW_FUNC_STR(sensor2_location,            sensor_location[2])
 511
 512BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed)
 513BUILD_SHOW_FUNC_FAN(sensor1_fan_speed,   0)
 514BUILD_SHOW_FUNC_FAN(sensor2_fan_speed,   1)
 515
 516BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed)
 517BUILD_SHOW_FUNC_INT(limit_adjust,        limit_adjust)
 518BUILD_STORE_FUNC_DEG(limit_adjust,       thermostat)
 519                
 520static DEVICE_ATTR(sensor1_temperature, S_IRUGO,
 521                   show_sensor1_temperature,NULL);
 522static DEVICE_ATTR(sensor2_temperature, S_IRUGO,
 523                   show_sensor2_temperature,NULL);
 524static DEVICE_ATTR(sensor1_limit, S_IRUGO,
 525                   show_sensor1_limit,  NULL);
 526static DEVICE_ATTR(sensor2_limit, S_IRUGO,
 527                   show_sensor2_limit,  NULL);
 528static DEVICE_ATTR(sensor1_location, S_IRUGO,
 529                   show_sensor1_location, NULL);
 530static DEVICE_ATTR(sensor2_location, S_IRUGO,
 531                   show_sensor2_location, NULL);
 532
 533static DEVICE_ATTR(specified_fan_speed, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
 534                   show_specified_fan_speed,store_specified_fan_speed);
 535
 536static DEVICE_ATTR(sensor1_fan_speed,   S_IRUGO,
 537                   show_sensor1_fan_speed,      NULL);
 538static DEVICE_ATTR(sensor2_fan_speed,   S_IRUGO,
 539                   show_sensor2_fan_speed,      NULL);
 540
 541static DEVICE_ATTR(limit_adjust,        S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
 542                   show_limit_adjust,   store_limit_adjust);
 543
 544
 545static int __init
 546thermostat_init(void)
 547{
 548        struct device_node* np;
 549        u32 *prop;
 550        int i = 0, offset = 0;
 551        
 552        np = of_find_node_by_name(NULL, "fan");
 553        if (!np)
 554                return -ENODEV;
 555        if (device_is_compatible(np, "adt7460"))
 556                therm_type = ADT7460;
 557        else if (device_is_compatible(np, "adt7467"))
 558                therm_type = ADT7467;
 559        else
 560                return -ENODEV;
 561
 562        prop = (u32 *)get_property(np, "hwsensor-params-version", NULL);
 563        printk(KERN_INFO "adt746x: version %d (%ssupported)\n", *prop,
 564                         (*prop == 1)?"":"un");
 565        if (*prop != 1)
 566                return -ENODEV;
 567
 568        prop = (u32 *)get_property(np, "reg", NULL);
 569        if (!prop)
 570                return -ENODEV;
 571
 572        /* look for bus either by path or using "reg" */
 573        if (strstr(np->full_name, "/i2c-bus@") != NULL) {
 574                const char *tmp_bus = (strstr(np->full_name, "/i2c-bus@") + 9);
 575                therm_bus = tmp_bus[0]-'0';
 576        } else {
 577                therm_bus = ((*prop) >> 8) & 0x0f;
 578        }
 579
 580        therm_address = ((*prop) & 0xff) >> 1;
 581
 582        printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, "
 583                         "limit_adjust: %d, fan_speed: %d\n",
 584                         therm_bus, therm_address, limit_adjust, fan_speed);
 585
 586        if (get_property(np, "hwsensor-location", NULL)) {
 587                for (i = 0; i < 3; i++) {
 588                        sensor_location[i] = get_property(np,
 589                                        "hwsensor-location", NULL) + offset;
 590
 591                        if (sensor_location[i] == NULL)
 592                                sensor_location[i] = "";
 593
 594                        printk(KERN_INFO "sensor %d: %s\n", i, sensor_location[i]);
 595                        offset += strlen(sensor_location[i]) + 1;
 596                }
 597        } else {
 598                sensor_location[0] = "?";
 599                sensor_location[1] = "?";
 600                sensor_location[2] = "?";
 601        }
 602
 603        of_dev = of_platform_device_create(np, "temperatures");
 604        
 605        if (of_dev == NULL) {
 606                printk(KERN_ERR "Can't register temperatures device !\n");
 607                return -ENODEV;
 608        }
 609        
 610        device_create_file(&of_dev->dev, &dev_attr_sensor1_temperature);
 611        device_create_file(&of_dev->dev, &dev_attr_sensor2_temperature);
 612        device_create_file(&of_dev->dev, &dev_attr_sensor1_limit);
 613        device_create_file(&of_dev->dev, &dev_attr_sensor2_limit);
 614        device_create_file(&of_dev->dev, &dev_attr_sensor1_location);
 615        device_create_file(&of_dev->dev, &dev_attr_sensor2_location);
 616        device_create_file(&of_dev->dev, &dev_attr_limit_adjust);
 617        device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed);
 618        device_create_file(&of_dev->dev, &dev_attr_sensor1_fan_speed);
 619        if(therm_type == ADT7460)
 620                device_create_file(&of_dev->dev, &dev_attr_sensor2_fan_speed);
 621
 622#ifndef CONFIG_I2C_KEYWEST
 623        request_module("i2c-keywest");
 624#endif
 625
 626        return i2c_add_driver(&thermostat_driver);
 627}
 628
 629static void __exit
 630thermostat_exit(void)
 631{
 632        if (of_dev) {
 633                device_remove_file(&of_dev->dev, &dev_attr_sensor1_temperature);
 634                device_remove_file(&of_dev->dev, &dev_attr_sensor2_temperature);
 635                device_remove_file(&of_dev->dev, &dev_attr_sensor1_limit);
 636                device_remove_file(&of_dev->dev, &dev_attr_sensor2_limit);
 637                device_remove_file(&of_dev->dev, &dev_attr_sensor1_location);
 638                device_remove_file(&of_dev->dev, &dev_attr_sensor2_location);
 639                device_remove_file(&of_dev->dev, &dev_attr_limit_adjust);
 640                device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed);
 641                device_remove_file(&of_dev->dev, &dev_attr_sensor1_fan_speed);
 642
 643                if(therm_type == ADT7460)
 644                        device_remove_file(&of_dev->dev,
 645                                           &dev_attr_sensor2_fan_speed);
 646
 647                of_device_unregister(of_dev);
 648        }
 649        i2c_del_driver(&thermostat_driver);
 650}
 651
 652module_init(thermostat_init);
 653module_exit(thermostat_exit);
 654
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.