ftp://ftp.kernel.org/pub/linux/kernel/v2.6/linux-2.6.6.tar.bz2
[linux-2.6.git] / drivers / macintosh / therm_adt746x.c
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 <asm/prom.h>
26 #include <asm/machdep.h>
27 #include <asm/io.h>
28 #include <asm/system.h>
29 #include <asm/sections.h>
30 #include <asm/of_device.h>
31
32 #undef DEBUG
33
34 #define CONFIG_REG   0x40
35 #define MANUAL_MASK  0xe0
36 #define AUTO_MASK    0x20
37
38 static u8 TEMP_REG[3]    = {0x26, 0x25, 0x27}; /* local, cpu, gpu */
39 static u8 LIMIT_REG[3]   = {0x6b, 0x6a, 0x6c}; /* local, cpu, gpu */
40 static u8 MANUAL_MODE[2] = {0x5c, 0x5d};       
41 static u8 REM_CONTROL[2] = {0x00, 0x40};
42 static u8 FAN_SPEED[2]   = {0x28, 0x2a};
43 static u8 FAN_SPD_SET[2] = {0x30, 0x31};
44
45 static u8 default_limits_local[3] = {70, 50, 70};    /* local, cpu, gpu */
46 static u8 default_limits_chip[3] = {80, 65, 80};    /* local, cpu, gpu */
47
48 static int limit_adjust = 0;
49 static int fan_speed = -1;
50
51 MODULE_AUTHOR("Colin Leroy <colin@colino.net>");
52 MODULE_DESCRIPTION("Driver for ADT746x thermostat in iBook G4 and Powerbook G4 Alu");
53 MODULE_LICENSE("GPL");
54
55 MODULE_PARM(limit_adjust,"i");
56 MODULE_PARM_DESC(limit_adjust,"Adjust maximum temperatures (50°C cpu, 70°C gpu) by N °C.");
57 MODULE_PARM(fan_speed,"i");
58 MODULE_PARM_DESC(fan_speed,"Specify fan speed (0-255) when lim < temp < lim+8 (default 128)");
59
60 struct thermostat {
61         struct i2c_client       clt;
62         u8                      cached_temp[3];
63         u8                      initial_limits[3];
64         u8                      limits[3];
65         int                     last_speed[2];
66         int                     overriding[2];
67 };
68
69 static enum {ADT7460, ADT7467} therm_type;
70 static int therm_bus, therm_address;
71 static struct of_device * of_dev;
72 static struct thermostat* thermostat;
73 static pid_t monitor_thread_id;
74 static int monitor_running;
75 static struct completion monitor_task_compl;
76
77 static int attach_one_thermostat(struct i2c_adapter *adapter, int addr, int busno);
78 static void write_both_fan_speed(struct thermostat *th, int speed);
79 static void write_fan_speed(struct thermostat *th, int speed, int fan);
80
81 static int
82 write_reg(struct thermostat* th, int reg, u8 data)
83 {
84         u8 tmp[2];
85         int rc;
86         
87         tmp[0] = reg;
88         tmp[1] = data;
89         rc = i2c_master_send(&th->clt, (const char *)tmp, 2);
90         if (rc < 0)
91                 return rc;
92         if (rc != 2)
93                 return -ENODEV;
94         return 0;
95 }
96
97 static int
98 read_reg(struct thermostat* th, int reg)
99 {
100         u8 reg_addr, data;
101         int rc;
102
103         reg_addr = (u8)reg;
104         rc = i2c_master_send(&th->clt, &reg_addr, 1);
105         if (rc < 0)
106                 return rc;
107         if (rc != 1)
108                 return -ENODEV;
109         rc = i2c_master_recv(&th->clt, (char *)&data, 1);
110         if (rc < 0)
111                 return rc;
112         return data;
113 }
114
115 static int
116 attach_thermostat(struct i2c_adapter *adapter)
117 {
118         unsigned long bus_no;
119
120         if (strncmp(adapter->name, "uni-n", 5))
121                 return -ENODEV;
122         bus_no = simple_strtoul(adapter->name + 6, NULL, 10);
123         if (bus_no != therm_bus)
124                 return -ENODEV;
125         return attach_one_thermostat(adapter, therm_address, bus_no);
126 }
127
128 static int
129 detach_thermostat(struct i2c_adapter *adapter)
130 {
131         struct thermostat* th;
132         int i;
133         
134         if (thermostat == NULL)
135                 return 0;
136
137         th = thermostat;
138
139         if (monitor_running) {
140                 monitor_running = 0;
141                 wait_for_completion(&monitor_task_compl);
142         }
143                 
144         printk(KERN_INFO "adt746x: Putting max temperatures back from %d, %d, %d,"
145                 " to %d, %d, %d, (°C)\n", 
146                 th->limits[0], th->limits[1], th->limits[2],
147                 th->initial_limits[0], th->initial_limits[1], th->initial_limits[2]);
148         
149         for (i = 0; i < 3; i++)
150                 write_reg(th, LIMIT_REG[i], th->initial_limits[i]);
151
152         write_both_fan_speed(th, -1);
153
154         i2c_detach_client(&th->clt);
155
156         thermostat = NULL;
157
158         kfree(th);
159
160         return 0;
161 }
162
163 static struct i2c_driver thermostat_driver = {  
164         .name           ="Apple Thermostat ADT746x",
165         .id             =0xDEAD7467,
166         .flags          =I2C_DF_NOTIFY,
167         .attach_adapter =&attach_thermostat,
168         .detach_adapter =&detach_thermostat,
169 };
170
171 static int read_fan_speed(struct thermostat *th, u8 addr)
172 {
173         u8 tmp[2];
174         u16 res;
175         
176         /* should start with low byte */
177         tmp[1] = read_reg(th, addr);
178         tmp[0] = read_reg(th, addr + 1);
179         
180         res = tmp[1] + (tmp[0] << 8);
181         return (90000*60)/res;
182 }
183
184 static void write_both_fan_speed(struct thermostat *th, int speed)
185 {
186         write_fan_speed(th, speed, 0);
187         if (therm_type == ADT7460)
188                 write_fan_speed(th, speed, 1);
189 }
190
191 static void write_fan_speed(struct thermostat *th, int speed, int fan)
192 {
193         u8 manual;
194         
195         if (speed > 0xff) 
196                 speed = 0xff;
197         else if (speed < -1) 
198                 speed = 0;
199         
200         if (therm_type == ADT7467 && fan == 1)
201                 return;
202         
203         if (th->last_speed[fan] != speed) {
204                 if (speed == -1)
205                         printk(KERN_INFO "adt746x: Setting speed to: automatic for %s fan.\n",
206                                 fan?"GPU":"CPU");
207                 else
208                         printk(KERN_INFO "adt746x: Setting speed to: %d for %s fan.\n",
209                                 speed, fan?"GPU":"CPU");
210         } else
211                 return;
212         
213         if (speed >= 0) {
214                 manual = read_reg(th, MANUAL_MODE[fan]);
215                 write_reg(th, MANUAL_MODE[fan], manual|MANUAL_MASK);
216                 write_reg(th, FAN_SPD_SET[fan], speed);
217         } else {
218                 /* back to automatic */
219                 if(therm_type == ADT7460) {
220                         manual = read_reg(th, MANUAL_MODE[fan]) & (~MANUAL_MASK);
221                         write_reg(th, MANUAL_MODE[fan], manual|REM_CONTROL[fan]);
222                 } else {
223                         manual = read_reg(th, MANUAL_MODE[fan]);
224                         write_reg(th, MANUAL_MODE[fan], manual&(~AUTO_MASK));
225                 }
226         }
227         
228         th->last_speed[fan] = speed;                    
229 }
230
231 static int monitor_task(void *arg)
232 {
233         struct thermostat* th = arg;
234         u8 temps[3];
235         u8 lims[3];
236         int i;
237 #ifdef DEBUG
238         int mfan_speed;
239 #endif
240         
241         lock_kernel();
242         daemonize("kfand");
243         unlock_kernel();
244         strcpy(current->comm, "thermostat");
245         monitor_running = 1;
246
247         while(monitor_running)
248         {
249                 set_task_state(current, TASK_UNINTERRUPTIBLE);
250                 schedule_timeout(2*HZ);
251
252                 /* Check status */
253                 /* local   : chip */
254                 /* remote 1: CPU ?*/
255                 /* remote 2: GPU ?*/
256 #ifndef DEBUG
257                 if (fan_speed != -1) {
258 #endif
259                         for (i = 0; i < 3; i++) {
260                                 temps[i]  = read_reg(th, TEMP_REG[i]);
261                                 lims[i]   = th->limits[i];
262                         }
263 #ifndef DEBUG
264                 }
265 #endif          
266                 if (fan_speed != -1) {
267                         int lastvar = 0;                /* for iBook */
268                         for (i = 1; i < 3; i++) {       /* we don't care about local sensor */
269                                 int started = 0;
270                                 int fan_number = (therm_type == ADT7460 && i == 2);
271                                 int var = temps[i] - lims[i];
272                                 if (var > 8) {
273                                         if (th->overriding[fan_number] == 0)
274                                                 printk(KERN_INFO "adt746x: Limit exceeded by %d°C, overriding specified fan speed for %s.\n",
275                                                         var, fan_number?"GPU":"CPU");
276                                         th->overriding[fan_number] = 1;
277                                         write_fan_speed(th, 255, fan_number);
278                                         started = 1;
279                                 } else if ((!th->overriding[fan_number] || var < 6) && var > 0) {
280                                         if (th->overriding[fan_number] == 1)
281                                                 printk(KERN_INFO "adt746x: Limit exceeded by %d°C, setting speed to specified for %s.\n",
282                                                         var, fan_number?"GPU":"CPU");                                   
283                                         th->overriding[fan_number] = 0;
284                                         write_fan_speed(th, fan_speed, fan_number);
285                                         started = 1;
286                                 } else if (var < -1) {
287                                         /* don't stop iBook fan if GPU is cold and CPU is not
288                                          * so cold (lastvar >= -1) */
289                                         if (therm_type == ADT7460 || lastvar < -1 || i == 1) {
290                                                 if (th->last_speed[fan_number] != 0)
291                                                         printk(KERN_INFO "adt746x: Stopping %s fan.\n",
292                                                                 fan_number?"GPU":"CPU");
293                                                 write_fan_speed(th, 0, fan_number);
294                                         }
295                                 }
296                                 
297                                 lastvar = var;
298                                 
299                                 if (started && therm_type == ADT7467)
300                                         break; /* we don't want to re-stop the fan
301                                                 * if CPU is heating and GPU is not */
302                         }
303                 }
304 #ifdef DEBUG
305                 mfan_speed = read_fan_speed(th, FAN_SPEED[0]);
306                 /* only one fan in the iBook G4 */
307                                 
308                 if (temps[0] != th->cached_temp[0]
309                 ||  temps[1] != th->cached_temp[1]
310                 ||  temps[2] != th->cached_temp[2]) {
311                         printk(KERN_INFO "adt746x: Temperature infos:"
312                                          " thermostats: %d,%d,%d °C;"
313                                          " limits: %d,%d,%d °C;"
314                                          " fan speed: %d RPM\n",
315                                 temps[0], temps[1], temps[2],
316                                 lims[0],  lims[1],  lims[2],
317                                 mfan_speed);
318                 }
319                 th->cached_temp[0] = temps[0];
320                 th->cached_temp[1] = temps[1];
321                 th->cached_temp[2] = temps[2];
322 #endif          
323         }
324
325         complete_and_exit(&monitor_task_compl, 0);
326         return 0;
327 }
328
329 static void
330 set_limit(struct thermostat *th, int i)
331 {
332                 /* Set CPU limit higher to avoid powerdowns */ 
333                 th->limits[i] = default_limits_chip[i] + limit_adjust;
334                 write_reg(th, LIMIT_REG[i], th->limits[i]);
335                 
336                 /* set our limits to normal */
337                 th->limits[i] = default_limits_local[i] + limit_adjust;
338 }
339         
340 static int
341 attach_one_thermostat(struct i2c_adapter *adapter, int addr, int busno)
342 {
343         struct thermostat* th;
344         int rc;
345         int i;
346
347         if (thermostat)
348                 return 0;
349         th = (struct thermostat *)kmalloc(sizeof(struct thermostat), GFP_KERNEL);
350         if (!th)
351                 return -ENOMEM;
352         memset(th, 0, sizeof(*th));
353         th->clt.addr = addr;
354         th->clt.adapter = adapter;
355         th->clt.driver = &thermostat_driver;
356         th->clt.id = 0xDEAD7467;
357         strcpy(th->clt.name, "thermostat");
358
359         rc = read_reg(th, 0);
360         if (rc < 0) {
361                 printk(KERN_ERR "adt746x: Thermostat failed to read config from bus %d !\n",
362                         busno);
363                 kfree(th);
364                 return -ENODEV;
365         }
366         /* force manual control to start the fan quieter */
367         
368         if (fan_speed == -1)
369                 fan_speed=128;
370         
371         if(therm_type == ADT7460) {
372                 printk(KERN_INFO "adt746x: ADT7460 initializing\n");
373                 /* The 7460 needs to be started explicitly */
374                 write_reg(th, CONFIG_REG, 1);
375         } else
376                 printk(KERN_INFO "adt746x: ADT7467 initializing\n");
377
378         for (i = 0; i < 3; i++) {
379                 th->initial_limits[i] = read_reg(th, LIMIT_REG[i]);
380                 set_limit(th, i);
381         }
382         
383         printk(KERN_INFO "adt746x: Lowering max temperatures from %d, %d, %d"
384                 " to %d, %d, %d (°C)\n", 
385                 th->initial_limits[0], th->initial_limits[1], th->initial_limits[2], 
386                 th->limits[0], th->limits[1], th->limits[2]);
387
388         thermostat = th;
389
390         if (i2c_attach_client(&th->clt)) {
391                 printk("adt746x: Thermostat failed to attach client !\n");
392                 thermostat = NULL;
393                 kfree(th);
394                 return -ENODEV;
395         }
396
397         /* be sure to really write fan speed the first time */
398         th->last_speed[0] = -2;
399         th->last_speed[1] = -2;
400         
401         if (fan_speed != -1) {
402                 write_both_fan_speed(th, 0);
403         } else {
404                 write_both_fan_speed(th, -1);
405         }
406         
407         init_completion(&monitor_task_compl);
408         
409         monitor_thread_id = kernel_thread(monitor_task, th,
410                 SIGCHLD | CLONE_KERNEL);
411
412         return 0;
413 }
414
415 /* 
416  * Now, unfortunately, sysfs doesn't give us a nice void * we could
417  * pass around to the attribute functions, so we don't really have
418  * choice but implement a bunch of them...
419  *
420  */
421 #define BUILD_SHOW_FUNC_DEG(name, data)                         \
422 static ssize_t show_##name(struct device *dev, char *buf)       \
423 {                                                               \
424         return sprintf(buf, "%d°C\n", data);                    \
425 }
426 #define BUILD_SHOW_FUNC_INT(name, data)                         \
427 static ssize_t show_##name(struct device *dev, char *buf)       \
428 {                                                               \
429         return sprintf(buf, "%d\n", data);                      \
430 }
431
432 #define BUILD_STORE_FUNC_DEG(name, data)                        \
433 static ssize_t store_##name(struct device *dev, const char *buf, size_t n) \
434 {                                                               \
435         int val;                                                \
436         int i;                                                  \
437         val = simple_strtol(buf, NULL, 10);                     \
438         printk(KERN_INFO "Adjusting limits by %d°C\n", val);    \
439         limit_adjust = val;                                     \
440         for (i=0; i < 3; i++)                                   \
441                 set_limit(thermostat, i);                       \
442         return n;                                               \
443 }
444
445 #define BUILD_STORE_FUNC_INT(name, data)                        \
446 static ssize_t store_##name(struct device *dev, const char *buf, size_t n) \
447 {                                                               \
448         u32 val;                                                \
449         val = simple_strtoul(buf, NULL, 10);                    \
450         if (val < 0 || val > 255)                               \
451                 return -EINVAL;                                 \
452         printk(KERN_INFO "Setting fan speed to %d\n", val);     \
453         data = val;                                             \
454         return n;                                               \
455 }
456
457 BUILD_SHOW_FUNC_DEG(cpu_temperature,     (read_reg(thermostat, TEMP_REG[1])))
458 BUILD_SHOW_FUNC_DEG(gpu_temperature,     (read_reg(thermostat, TEMP_REG[2])))
459 BUILD_SHOW_FUNC_DEG(cpu_limit,           thermostat->limits[1])
460 BUILD_SHOW_FUNC_DEG(gpu_limit,           thermostat->limits[2])
461
462 BUILD_SHOW_FUNC_INT(specified_fan_speed, fan_speed)
463 BUILD_SHOW_FUNC_INT(cpu_fan_speed,       (read_fan_speed(thermostat, FAN_SPEED[0])))
464 BUILD_SHOW_FUNC_INT(gpu_fan_speed,       (read_fan_speed(thermostat, FAN_SPEED[1])))
465
466 BUILD_STORE_FUNC_INT(specified_fan_speed,fan_speed)
467 BUILD_SHOW_FUNC_INT(limit_adjust,        limit_adjust)
468 BUILD_STORE_FUNC_DEG(limit_adjust,       thermostat)
469                 
470 static DEVICE_ATTR(cpu_temperature,     S_IRUGO,
471                    show_cpu_temperature,NULL);
472 static DEVICE_ATTR(gpu_temperature,     S_IRUGO,
473                    show_gpu_temperature,NULL);
474 static DEVICE_ATTR(cpu_limit,           S_IRUGO,
475                    show_cpu_limit,      NULL);
476 static DEVICE_ATTR(gpu_limit,           S_IRUGO,
477                    show_gpu_limit,      NULL);
478
479 static DEVICE_ATTR(specified_fan_speed, S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
480                    show_specified_fan_speed,store_specified_fan_speed);
481
482 static DEVICE_ATTR(cpu_fan_speed,       S_IRUGO,
483                    show_cpu_fan_speed,  NULL);
484 static DEVICE_ATTR(gpu_fan_speed,       S_IRUGO,
485                    show_gpu_fan_speed,  NULL);
486
487 static DEVICE_ATTR(limit_adjust,        S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH,
488                    show_limit_adjust,   store_limit_adjust);
489
490
491 static int __init
492 thermostat_init(void)
493 {
494         struct device_node* np;
495         u32 *prop;
496         
497         np = of_find_node_by_name(NULL, "fan");
498         if (!np)
499                 return -ENODEV;
500         if (device_is_compatible(np, "adt7460"))
501                 therm_type = ADT7460;
502         else if (device_is_compatible(np, "adt7467"))
503                 therm_type = ADT7467;
504         else
505                 return -ENODEV;
506
507         prop = (u32 *)get_property(np, "reg", NULL);
508         if (!prop)
509                 return -ENODEV;
510         therm_bus = ((*prop) >> 8) & 0x0f;
511         therm_address = ((*prop) & 0xff) >> 1;
512
513         printk(KERN_INFO "adt746x: Thermostat bus: %d, address: 0x%02x, limit_adjust: %d, fan_speed: %d\n",
514                 therm_bus, therm_address, limit_adjust, fan_speed);
515
516         of_dev = of_platform_device_create(np, "temperatures");
517         
518         if (of_dev == NULL) {
519                 printk(KERN_ERR "Can't register temperatures device !\n");
520                 return -ENODEV;
521         }
522         
523         device_create_file(&of_dev->dev, &dev_attr_cpu_temperature);
524         device_create_file(&of_dev->dev, &dev_attr_gpu_temperature);
525         device_create_file(&of_dev->dev, &dev_attr_cpu_limit);
526         device_create_file(&of_dev->dev, &dev_attr_gpu_limit);
527         device_create_file(&of_dev->dev, &dev_attr_limit_adjust);
528         device_create_file(&of_dev->dev, &dev_attr_specified_fan_speed);
529         device_create_file(&of_dev->dev, &dev_attr_cpu_fan_speed);
530         if(therm_type == ADT7460)
531                 device_create_file(&of_dev->dev, &dev_attr_gpu_fan_speed);
532
533 #ifndef CONFIG_I2C_KEYWEST
534         request_module("i2c-keywest");
535 #endif
536
537         return i2c_add_driver(&thermostat_driver);
538 }
539
540 static void __exit
541 thermostat_exit(void)
542 {
543         if (of_dev) {
544                 device_remove_file(&of_dev->dev, &dev_attr_cpu_temperature);
545                 device_remove_file(&of_dev->dev, &dev_attr_gpu_temperature);
546                 device_remove_file(&of_dev->dev, &dev_attr_cpu_limit);
547                 device_remove_file(&of_dev->dev, &dev_attr_gpu_limit);
548                 device_remove_file(&of_dev->dev, &dev_attr_limit_adjust);
549                 device_remove_file(&of_dev->dev, &dev_attr_specified_fan_speed);
550                 device_remove_file(&of_dev->dev, &dev_attr_cpu_fan_speed);
551                 if(therm_type == ADT7460)
552                         device_remove_file(&of_dev->dev, &dev_attr_gpu_fan_speed);
553                 of_device_unregister(of_dev);
554         }
555         i2c_del_driver(&thermostat_driver);
556 }
557
558 module_init(thermostat_init);
559 module_exit(thermostat_exit);