linux/drivers/macintosh/therm_windtunnel.c
<<
>>
Prefs
   1/* 
   2 *   Creation Date: <2003/03/14 20:54:13 samuel>
   3 *   Time-stamp: <2004/03/20 14:20:59 samuel>
   4 *   
   5 *      <therm_windtunnel.c>
   6 *      
   7 *      The G4 "windtunnel" has a single fan controlled by an
   8 *      ADM1030 fan controller and a DS1775 thermostat.
   9 *
  10 *      The fan controller is equipped with a temperature sensor
  11 *      which measures the case temperature. The DS1775 sensor
  12 *      measures the CPU temperature. This driver tunes the
  13 *      behavior of the fan. It is based upon empirical observations
  14 *      of the 'AppleFan' driver under Mac OS X.
  15 *
  16 *      WARNING: This driver has only been testen on Apple's
  17 *      1.25 MHz Dual G4 (March 03). It is tuned for a CPU
  18 *      temperatur around 57 C.
  19 *
  20 *   Copyright (C) 2003, 2004 Samuel Rydh (samuel@ibrium.se)
  21 *
  22 *   Loosely based upon 'thermostat.c' written by Benjamin Herrenschmidt
  23 *   
  24 *   This program is free software; you can redistribute it and/or
  25 *   modify it under the terms of the GNU General Public License
  26 *   as published by the Free Software Foundation
  27 *   
  28 */
  29
  30#include <linux/types.h>
  31#include <linux/module.h>
  32#include <linux/errno.h>
  33#include <linux/kernel.h>
  34#include <linux/delay.h>
  35#include <linux/sched.h>
  36#include <linux/i2c.h>
  37#include <linux/slab.h>
  38#include <linux/init.h>
  39#include <linux/kthread.h>
  40#include <linux/of_platform.h>
  41
  42#include <asm/prom.h>
  43#include <asm/machdep.h>
  44#include <asm/io.h>
  45#include <asm/system.h>
  46#include <asm/sections.h>
  47#include <asm/macio.h>
  48
  49#define LOG_TEMP                0                       /* continously log temperature */
  50
  51static int                      do_probe( struct i2c_adapter *adapter, int addr, int kind);
  52
  53/* scan 0x48-0x4f (DS1775) and 0x2c-2x2f (ADM1030) */
  54static const unsigned short     normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b,
  55                                                 0x4c, 0x4d, 0x4e, 0x4f,
  56                                                 0x2c, 0x2d, 0x2e, 0x2f,
  57                                                 I2C_CLIENT_END };
  58
  59I2C_CLIENT_INSMOD;
  60
  61static struct {
  62        volatile int            running;
  63        struct task_struct      *poll_task;
  64        
  65        struct mutex            lock;
  66        struct of_device        *of_dev;
  67        
  68        struct i2c_client       *thermostat;
  69        struct i2c_client       *fan;
  70
  71        int                     overheat_temp;          /* 100% fan at this temp */
  72        int                     overheat_hyst;
  73        int                     temp;
  74        int                     casetemp;
  75        int                     fan_level;              /* active fan_table setting */
  76
  77        int                     downind;
  78        int                     upind;
  79
  80        int                     r0, r1, r20, r23, r25;  /* saved register */
  81} x;
  82
  83#define T(x,y)                  (((x)<<8) | (y)*0x100/10 )
  84
  85static struct {
  86        int                     fan_down_setting;
  87        int                     temp;
  88        int                     fan_up_setting;
  89} fan_table[] = {
  90        { 11, T(0,0),  11 },    /* min fan */
  91        { 11, T(55,0), 11 },
  92        {  6, T(55,3), 11 },
  93        {  7, T(56,0), 11 },
  94        {  8, T(57,0), 8 },
  95        {  7, T(58,3), 7 },
  96        {  6, T(58,8), 6 },
  97        {  5, T(59,2), 5 },
  98        {  4, T(59,6), 4 },
  99        {  3, T(59,9), 3 },
 100        {  2, T(60,1), 2 },
 101        {  1, 0xfffff, 1 }      /* on fire */
 102};
 103
 104static void
 105print_temp( const char *s, int temp )
 106{
 107        printk("%s%d.%d C", s ? s : "", temp>>8, (temp & 255)*10/256 );
 108}
 109
 110static ssize_t
 111show_cpu_temperature( struct device *dev, struct device_attribute *attr, char *buf )
 112{
 113        return sprintf(buf, "%d.%d\n", x.temp>>8, (x.temp & 255)*10/256 );
 114}
 115
 116static ssize_t
 117show_case_temperature( struct device *dev, struct device_attribute *attr, char *buf )
 118{
 119        return sprintf(buf, "%d.%d\n", x.casetemp>>8, (x.casetemp & 255)*10/256 );
 120}
 121
 122static DEVICE_ATTR(cpu_temperature, S_IRUGO, show_cpu_temperature, NULL );
 123static DEVICE_ATTR(case_temperature, S_IRUGO, show_case_temperature, NULL );
 124
 125
 126
 127/************************************************************************/
 128/*      controller thread                                               */
 129/************************************************************************/
 130
 131static int
 132write_reg( struct i2c_client *cl, int reg, int data, int len )
 133{
 134        u8 tmp[3];
 135
 136        if( len < 1 || len > 2 || data < 0 )
 137                return -EINVAL;
 138
 139        tmp[0] = reg;
 140        tmp[1] = (len == 1) ? data : (data >> 8);
 141        tmp[2] = data;
 142        len++;
 143        
 144        if( i2c_master_send(cl, tmp, len) != len )
 145                return -ENODEV;
 146        return 0;
 147}
 148
 149static int
 150read_reg( struct i2c_client *cl, int reg, int len )
 151{
 152        u8 buf[2];
 153
 154        if( len != 1 && len != 2 )
 155                return -EINVAL;
 156        buf[0] = reg;
 157        if( i2c_master_send(cl, buf, 1) != 1 )
 158                return -ENODEV;
 159        if( i2c_master_recv(cl, buf, len) != len )
 160                return -ENODEV;
 161        return (len == 2)? ((unsigned int)buf[0] << 8) | buf[1] : buf[0];
 162}
 163
 164static void
 165tune_fan( int fan_setting )
 166{
 167        int val = (fan_setting << 3) | 7;
 168
 169        /* write_reg( x.fan, 0x24, val, 1 ); */
 170        write_reg( x.fan, 0x25, val, 1 );
 171        write_reg( x.fan, 0x20, 0, 1 );
 172        print_temp("CPU-temp: ", x.temp );
 173        if( x.casetemp )
 174                print_temp(", Case: ", x.casetemp );
 175        printk(",  Fan: %d (tuned %+d)\n", 11-fan_setting, x.fan_level-fan_setting );
 176
 177        x.fan_level = fan_setting;
 178}
 179
 180static void
 181poll_temp( void )
 182{
 183        int temp, i, level, casetemp;
 184
 185        temp = read_reg( x.thermostat, 0, 2 );
 186
 187        /* this actually occurs when the computer is loaded */
 188        if( temp < 0 )
 189                return;
 190
 191        casetemp = read_reg(x.fan, 0x0b, 1) << 8;
 192        casetemp |= (read_reg(x.fan, 0x06, 1) & 0x7) << 5;
 193
 194        if( LOG_TEMP && x.temp != temp ) {
 195                print_temp("CPU-temp: ", temp );
 196                print_temp(", Case: ", casetemp );
 197                printk(",  Fan: %d\n", 11-x.fan_level );
 198        }
 199        x.temp = temp;
 200        x.casetemp = casetemp;
 201
 202        level = -1;
 203        for( i=0; (temp & 0xffff) > fan_table[i].temp ; i++ )
 204                ;
 205        if( i < x.downind )
 206                level = fan_table[i].fan_down_setting;
 207        x.downind = i;
 208
 209        for( i=0; (temp & 0xffff) >= fan_table[i+1].temp ; i++ )
 210                ;
 211        if( x.upind < i )
 212                level = fan_table[i].fan_up_setting;
 213        x.upind = i;
 214
 215        if( level >= 0 )
 216                tune_fan( level );
 217}
 218
 219
 220static void
 221setup_hardware( void )
 222{
 223        int val;
 224        int err;
 225
 226        /* save registers (if we unload the module) */
 227        x.r0 = read_reg( x.fan, 0x00, 1 );
 228        x.r1 = read_reg( x.fan, 0x01, 1 );
 229        x.r20 = read_reg( x.fan, 0x20, 1 );
 230        x.r23 = read_reg( x.fan, 0x23, 1 );
 231        x.r25 = read_reg( x.fan, 0x25, 1 );
 232
 233        /* improve measurement resolution (convergence time 1.5s) */
 234        if( (val=read_reg(x.thermostat, 1, 1)) >= 0 ) {
 235                val |= 0x60;
 236                if( write_reg( x.thermostat, 1, val, 1 ) )
 237                        printk("Failed writing config register\n");
 238        }
 239        /* disable interrupts and TAC input */
 240        write_reg( x.fan, 0x01, 0x01, 1 );
 241        /* enable filter */
 242        write_reg( x.fan, 0x23, 0x91, 1 );
 243        /* remote temp. controls fan */
 244        write_reg( x.fan, 0x00, 0x95, 1 );
 245
 246        /* The thermostat (which besides measureing temperature controls
 247         * has a THERM output which puts the fan on 100%) is usually
 248         * set to kick in at 80 C (chip default). We reduce this a bit
 249         * to be on the safe side (OSX doesn't)...
 250         */
 251        if( x.overheat_temp == (80 << 8) ) {
 252                x.overheat_temp = 65 << 8;
 253                x.overheat_hyst = 60 << 8;
 254                write_reg( x.thermostat, 2, x.overheat_hyst, 2 );
 255                write_reg( x.thermostat, 3, x.overheat_temp, 2 );
 256
 257                print_temp("Reducing overheating limit to ", x.overheat_temp );
 258                print_temp(" (Hyst: ", x.overheat_hyst );
 259                printk(")\n");
 260        }
 261
 262        /* set an initial fan setting */
 263        x.downind = 0xffff;
 264        x.upind = -1;
 265        /* tune_fan( fan_up_table[x.upind].fan_setting ); */
 266
 267        err = device_create_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
 268        err |= device_create_file( &x.of_dev->dev, &dev_attr_case_temperature );
 269        if (err)
 270                printk(KERN_WARNING
 271                        "Failed to create temperature attribute file(s).\n");
 272}
 273
 274static void
 275restore_regs( void )
 276{
 277        device_remove_file( &x.of_dev->dev, &dev_attr_cpu_temperature );
 278        device_remove_file( &x.of_dev->dev, &dev_attr_case_temperature );
 279
 280        write_reg( x.fan, 0x01, x.r1, 1 );
 281        write_reg( x.fan, 0x20, x.r20, 1 );
 282        write_reg( x.fan, 0x23, x.r23, 1 );
 283        write_reg( x.fan, 0x25, x.r25, 1 );
 284        write_reg( x.fan, 0x00, x.r0, 1 );
 285}
 286
 287static int control_loop(void *dummy)
 288{
 289        mutex_lock(&x.lock);
 290        setup_hardware();
 291        mutex_unlock(&x.lock);
 292
 293        for (;;) {
 294                msleep_interruptible(8000);
 295                if (kthread_should_stop())
 296                        break;
 297
 298                mutex_lock(&x.lock);
 299                poll_temp();
 300                mutex_unlock(&x.lock);
 301        }
 302
 303        mutex_lock(&x.lock);
 304        restore_regs();
 305        mutex_unlock(&x.lock);
 306
 307        return 0;
 308}
 309
 310
 311/************************************************************************/
 312/*      i2c probing and setup                                           */
 313/************************************************************************/
 314
 315static int
 316do_attach( struct i2c_adapter *adapter )
 317{
 318        int ret = 0;
 319
 320        if( strncmp(adapter->name, "uni-n", 5) )
 321                return 0;
 322
 323        if( !x.running ) {
 324                ret = i2c_probe( adapter, &addr_data, &do_probe );
 325                if( x.thermostat && x.fan ) {
 326                        x.running = 1;
 327                        x.poll_task = kthread_run(control_loop, NULL, "g4fand");
 328                }
 329        }
 330        return ret;
 331}
 332
 333static int
 334do_detach( struct i2c_client *client )
 335{
 336        int err;
 337
 338        if( (err=i2c_detach_client(client)) )
 339                printk(KERN_ERR "failed to detach thermostat client\n");
 340        else {
 341                if( x.running ) {
 342                        x.running = 0;
 343                        kthread_stop(x.poll_task);
 344                        x.poll_task = NULL;
 345                }
 346                if( client == x.thermostat )
 347                        x.thermostat = NULL;
 348                else if( client == x.fan )
 349                        x.fan = NULL;
 350                else {
 351                        printk(KERN_ERR "g4fan: bad client\n");
 352                }
 353                kfree( client );
 354        }
 355        return err;
 356}
 357
 358static struct i2c_driver g4fan_driver = {  
 359        .driver = {
 360                .name   = "therm_windtunnel",
 361        },
 362        .attach_adapter = do_attach,
 363        .detach_client  = do_detach,
 364};
 365
 366static int
 367attach_fan( struct i2c_client *cl )
 368{
 369        if( x.fan )
 370                goto out;
 371
 372        /* check that this is an ADM1030 */
 373        if( read_reg(cl, 0x3d, 1) != 0x30 || read_reg(cl, 0x3e, 1) != 0x41 )
 374                goto out;
 375        printk("ADM1030 fan controller [@%02x]\n", cl->addr );
 376
 377        strlcpy( cl->name, "ADM1030 fan controller", sizeof(cl->name) );
 378
 379        if( !i2c_attach_client(cl) )
 380                x.fan = cl;
 381 out:
 382        if( cl != x.fan )
 383                kfree( cl );
 384        return 0;
 385}
 386
 387static int
 388attach_thermostat( struct i2c_client *cl ) 
 389{
 390        int hyst_temp, os_temp, temp;
 391
 392        if( x.thermostat )
 393                goto out;
 394
 395        if( (temp=read_reg(cl, 0, 2)) < 0 )
 396                goto out;
 397        
 398        /* temperature sanity check */
 399        if( temp < 0x1600 || temp > 0x3c00 )
 400                goto out;
 401        hyst_temp = read_reg(cl, 2, 2);
 402        os_temp = read_reg(cl, 3, 2);
 403        if( hyst_temp < 0 || os_temp < 0 )
 404                goto out;
 405
 406        printk("DS1775 digital thermometer [@%02x]\n", cl->addr );
 407        print_temp("Temp: ", temp );
 408        print_temp("  Hyst: ", hyst_temp );
 409        print_temp("  OS: ", os_temp );
 410        printk("\n");
 411
 412        x.temp = temp;
 413        x.overheat_temp = os_temp;
 414        x.overheat_hyst = hyst_temp;
 415        
 416        strlcpy( cl->name, "DS1775 thermostat", sizeof(cl->name) );
 417
 418        if( !i2c_attach_client(cl) )
 419                x.thermostat = cl;
 420out:
 421        if( cl != x.thermostat )
 422                kfree( cl );
 423        return 0;
 424}
 425
 426static int
 427do_probe( struct i2c_adapter *adapter, int addr, int kind )
 428{
 429        struct i2c_client *cl;
 430
 431        if( !i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA
 432                                     | I2C_FUNC_SMBUS_WRITE_BYTE) )
 433                return 0;
 434
 435        if( !(cl=kzalloc(sizeof(*cl), GFP_KERNEL)) )
 436                return -ENOMEM;
 437
 438        cl->addr = addr;
 439        cl->adapter = adapter;
 440        cl->driver = &g4fan_driver;
 441        cl->flags = 0;
 442
 443        if( addr < 0x48 )
 444                return attach_fan( cl );
 445        return attach_thermostat( cl );
 446}
 447
 448
 449/************************************************************************/
 450/*      initialization / cleanup                                        */
 451/************************************************************************/
 452
 453static int
 454therm_of_probe( struct of_device *dev, const struct of_device_id *match )
 455{
 456        return i2c_add_driver( &g4fan_driver );
 457}
 458
 459static int
 460therm_of_remove( struct of_device *dev )
 461{
 462        i2c_del_driver( &g4fan_driver );
 463        return 0;
 464}
 465
 466static struct of_device_id therm_of_match[] = {{
 467        .name           = "fan",
 468        .compatible     = "adm1030"
 469    }, {}
 470};
 471
 472static struct of_platform_driver therm_of_driver = {
 473        .name           = "temperature",
 474        .match_table    = therm_of_match,
 475        .probe          = therm_of_probe,
 476        .remove         = therm_of_remove,
 477};
 478
 479struct apple_thermal_info {
 480        u8              id;                     /* implementation ID */
 481        u8              fan_count;              /* number of fans */
 482        u8              thermostat_count;       /* number of thermostats */
 483        u8              unused;
 484};
 485
 486static int __init
 487g4fan_init( void )
 488{
 489        const struct apple_thermal_info *info;
 490        struct device_node *np;
 491
 492        mutex_init(&x.lock);
 493
 494        if( !(np=of_find_node_by_name(NULL, "power-mgt")) )
 495                return -ENODEV;
 496        info = of_get_property(np, "thermal-info", NULL);
 497        of_node_put(np);
 498
 499        if( !info || !machine_is_compatible("PowerMac3,6") )
 500                return -ENODEV;
 501
 502        if( info->id != 3 ) {
 503                printk(KERN_ERR "therm_windtunnel: unsupported thermal design %d\n", info->id );
 504                return -ENODEV;
 505        }
 506        if( !(np=of_find_node_by_name(NULL, "fan")) )
 507                return -ENODEV;
 508        x.of_dev = of_platform_device_create(np, "temperature", NULL);
 509        of_node_put( np );
 510
 511        if( !x.of_dev ) {
 512                printk(KERN_ERR "Can't register fan controller!\n");
 513                return -ENODEV;
 514        }
 515
 516        of_register_platform_driver( &therm_of_driver );
 517        return 0;
 518}
 519
 520static void __exit
 521g4fan_exit( void )
 522{
 523        of_unregister_platform_driver( &therm_of_driver );
 524
 525        if( x.of_dev )
 526                of_device_unregister( x.of_dev );
 527}
 528
 529module_init(g4fan_init);
 530module_exit(g4fan_exit);
 531
 532MODULE_AUTHOR("Samuel Rydh <samuel@ibrium.se>");
 533MODULE_DESCRIPTION("Apple G4 (windtunnel) fan controller");
 534MODULE_LICENSE("GPL");
 535