fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / drivers / sbus / char / bbc_envctrl.c
1 /* $Id: bbc_envctrl.c,v 1.4 2001/04/06 16:48:08 davem Exp $
2  * bbc_envctrl.c: UltraSPARC-III environment control driver.
3  *
4  * Copyright (C) 2001 David S. Miller (davem@redhat.com)
5  */
6
7 #include <linux/kthread.h>
8 #include <linux/delay.h>
9 #include <linux/kmod.h>
10 #include <asm/oplib.h>
11 #include <asm/ebus.h>
12
13 #include "bbc_i2c.h"
14 #include "max1617.h"
15
16 #undef ENVCTRL_TRACE
17
18 /* WARNING: Making changes to this driver is very dangerous.
19  *          If you misprogram the sensor chips they can
20  *          cut the power on you instantly.
21  */
22
23 /* Two temperature sensors exist in the SunBLADE-1000 enclosure.
24  * Both are implemented using max1617 i2c devices.  Each max1617
25  * monitors 2 temperatures, one for one of the cpu dies and the other
26  * for the ambient temperature.
27  *
28  * The max1617 is capable of being programmed with power-off
29  * temperature values, one low limit and one high limit.  These
30  * can be controlled independently for the cpu or ambient temperature.
31  * If a limit is violated, the power is simply shut off.  The frequency
32  * with which the max1617 does temperature sampling can be controlled
33  * as well.
34  *
35  * Three fans exist inside the machine, all three are controlled with
36  * an i2c digital to analog converter.  There is a fan directed at the
37  * two processor slots, another for the rest of the enclosure, and the
38  * third is for the power supply.  The first two fans may be speed
39  * controlled by changing the voltage fed to them.  The third fan may
40  * only be completely off or on.  The third fan is meant to only be
41  * disabled/enabled when entering/exiting the lowest power-saving
42  * mode of the machine.
43  *
44  * An environmental control kernel thread periodically monitors all
45  * temperature sensors.  Based upon the samples it will adjust the
46  * fan speeds to try and keep the system within a certain temperature
47  * range (the goal being to make the fans as quiet as possible without
48  * allowing the system to get too hot).
49  *
50  * If the temperature begins to rise/fall outside of the acceptable
51  * operating range, a periodic warning will be sent to the kernel log.
52  * The fans will be put on full blast to attempt to deal with this
53  * situation.  After exceeding the acceptable operating range by a
54  * certain threshold, the kernel thread will shut down the system.
55  * Here, the thread is attempting to shut the machine down cleanly
56  * before the hardware based power-off event is triggered.
57  */
58
59 /* These settings are in Celsius.  We use these defaults only
60  * if we cannot interrogate the cpu-fru SEEPROM.
61  */
62 struct temp_limits {
63         s8 high_pwroff, high_shutdown, high_warn;
64         s8 low_warn, low_shutdown, low_pwroff;
65 };
66
67 static struct temp_limits cpu_temp_limits[2] = {
68         { 100, 85, 80, 5, -5, -10 },
69         { 100, 85, 80, 5, -5, -10 },
70 };
71
72 static struct temp_limits amb_temp_limits[2] = {
73         { 65, 55, 40, 5, -5, -10 },
74         { 65, 55, 40, 5, -5, -10 },
75 };
76
77 enum fan_action { FAN_SLOWER, FAN_SAME, FAN_FASTER, FAN_FULLBLAST, FAN_STATE_MAX };
78
79 struct bbc_cpu_temperature {
80         struct bbc_cpu_temperature      *next;
81
82         struct bbc_i2c_client           *client;
83         int                             index;
84
85         /* Current readings, and history. */
86         s8                              curr_cpu_temp;
87         s8                              curr_amb_temp;
88         s8                              prev_cpu_temp;
89         s8                              prev_amb_temp;
90         s8                              avg_cpu_temp;
91         s8                              avg_amb_temp;
92
93         int                             sample_tick;
94
95         enum fan_action                 fan_todo[2];
96 #define FAN_AMBIENT     0
97 #define FAN_CPU         1
98 };
99
100 struct bbc_cpu_temperature *all_bbc_temps;
101
102 struct bbc_fan_control {
103         struct bbc_fan_control  *next;
104
105         struct bbc_i2c_client   *client;
106         int                     index;
107
108         int                     psupply_fan_on;
109         int                     cpu_fan_speed;
110         int                     system_fan_speed;
111 };
112
113 struct bbc_fan_control *all_bbc_fans;
114
115 #define CPU_FAN_REG     0xf0
116 #define SYS_FAN_REG     0xf2
117 #define PSUPPLY_FAN_REG 0xf4
118
119 #define FAN_SPEED_MIN   0x0c
120 #define FAN_SPEED_MAX   0x3f
121
122 #define PSUPPLY_FAN_ON  0x1f
123 #define PSUPPLY_FAN_OFF 0x00
124
125 static void set_fan_speeds(struct bbc_fan_control *fp)
126 {
127         /* Put temperatures into range so we don't mis-program
128          * the hardware.
129          */
130         if (fp->cpu_fan_speed < FAN_SPEED_MIN)
131                 fp->cpu_fan_speed = FAN_SPEED_MIN;
132         if (fp->cpu_fan_speed > FAN_SPEED_MAX)
133                 fp->cpu_fan_speed = FAN_SPEED_MAX;
134         if (fp->system_fan_speed < FAN_SPEED_MIN)
135                 fp->system_fan_speed = FAN_SPEED_MIN;
136         if (fp->system_fan_speed > FAN_SPEED_MAX)
137                 fp->system_fan_speed = FAN_SPEED_MAX;
138 #ifdef ENVCTRL_TRACE
139         printk("fan%d: Changed fan speed to cpu(%02x) sys(%02x)\n",
140                fp->index,
141                fp->cpu_fan_speed, fp->system_fan_speed);
142 #endif
143
144         bbc_i2c_writeb(fp->client, fp->cpu_fan_speed, CPU_FAN_REG);
145         bbc_i2c_writeb(fp->client, fp->system_fan_speed, SYS_FAN_REG);
146         bbc_i2c_writeb(fp->client,
147                        (fp->psupply_fan_on ?
148                         PSUPPLY_FAN_ON : PSUPPLY_FAN_OFF),
149                        PSUPPLY_FAN_REG);
150 }
151
152 static void get_current_temps(struct bbc_cpu_temperature *tp)
153 {
154         tp->prev_amb_temp = tp->curr_amb_temp;
155         bbc_i2c_readb(tp->client,
156                       (unsigned char *) &tp->curr_amb_temp,
157                       MAX1617_AMB_TEMP);
158         tp->prev_cpu_temp = tp->curr_cpu_temp;
159         bbc_i2c_readb(tp->client,
160                       (unsigned char *) &tp->curr_cpu_temp,
161                       MAX1617_CPU_TEMP);
162 #ifdef ENVCTRL_TRACE
163         printk("temp%d: cpu(%d C) amb(%d C)\n",
164                tp->index,
165                (int) tp->curr_cpu_temp, (int) tp->curr_amb_temp);
166 #endif
167 }
168
169
170 static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp)
171 {
172         static int shutting_down = 0;
173         static char *envp[] = { "HOME=/", "TERM=linux", "PATH=/sbin:/usr/sbin:/bin:/usr/bin", NULL };
174         char *argv[] = { "/sbin/shutdown", "-h", "now", NULL };
175         char *type = "???";
176         s8 val = -1;
177
178         if (shutting_down != 0)
179                 return;
180
181         if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown ||
182             tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) {
183                 type = "ambient";
184                 val = tp->curr_amb_temp;
185         } else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown ||
186                    tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) {
187                 type = "CPU";
188                 val = tp->curr_cpu_temp;
189         }
190
191         printk(KERN_CRIT "temp%d: Outside of safe %s "
192                "operating temperature, %d C.\n",
193                tp->index, type, val);
194
195         printk(KERN_CRIT "kenvctrld: Shutting down the system now.\n");
196
197         shutting_down = 1;
198         if (call_usermodehelper("/sbin/shutdown", argv, envp, 0) < 0)
199                 printk(KERN_CRIT "envctrl: shutdown execution failed\n");
200 }
201
202 #define WARN_INTERVAL   (30 * HZ)
203
204 static void analyze_ambient_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick)
205 {
206         int ret = 0;
207
208         if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) {
209                 if (tp->curr_amb_temp >=
210                     amb_temp_limits[tp->index].high_warn) {
211                         printk(KERN_WARNING "temp%d: "
212                                "Above safe ambient operating temperature, %d C.\n",
213                                tp->index, (int) tp->curr_amb_temp);
214                         ret = 1;
215                 } else if (tp->curr_amb_temp <
216                            amb_temp_limits[tp->index].low_warn) {
217                         printk(KERN_WARNING "temp%d: "
218                                "Below safe ambient operating temperature, %d C.\n",
219                                tp->index, (int) tp->curr_amb_temp);
220                         ret = 1;
221                 }
222                 if (ret)
223                         *last_warn = jiffies;
224         } else if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_warn ||
225                    tp->curr_amb_temp < amb_temp_limits[tp->index].low_warn)
226                 ret = 1;
227
228         /* Now check the shutdown limits. */
229         if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown ||
230             tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) {
231                 do_envctrl_shutdown(tp);
232                 ret = 1;
233         }
234
235         if (ret) {
236                 tp->fan_todo[FAN_AMBIENT] = FAN_FULLBLAST;
237         } else if ((tick & (8 - 1)) == 0) {
238                 s8 amb_goal_hi = amb_temp_limits[tp->index].high_warn - 10;
239                 s8 amb_goal_lo;
240
241                 amb_goal_lo = amb_goal_hi - 3;
242
243                 /* We do not try to avoid 'too cold' events.  Basically we
244                  * only try to deal with over-heating and fan noise reduction.
245                  */
246                 if (tp->avg_amb_temp < amb_goal_hi) {
247                         if (tp->avg_amb_temp >= amb_goal_lo)
248                                 tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
249                         else
250                                 tp->fan_todo[FAN_AMBIENT] = FAN_SLOWER;
251                 } else {
252                         tp->fan_todo[FAN_AMBIENT] = FAN_FASTER;
253                 }
254         } else {
255                 tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
256         }
257 }
258
259 static void analyze_cpu_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick)
260 {
261         int ret = 0;
262
263         if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) {
264                 if (tp->curr_cpu_temp >=
265                     cpu_temp_limits[tp->index].high_warn) {
266                         printk(KERN_WARNING "temp%d: "
267                                "Above safe CPU operating temperature, %d C.\n",
268                                tp->index, (int) tp->curr_cpu_temp);
269                         ret = 1;
270                 } else if (tp->curr_cpu_temp <
271                            cpu_temp_limits[tp->index].low_warn) {
272                         printk(KERN_WARNING "temp%d: "
273                                "Below safe CPU operating temperature, %d C.\n",
274                                tp->index, (int) tp->curr_cpu_temp);
275                         ret = 1;
276                 }
277                 if (ret)
278                         *last_warn = jiffies;
279         } else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_warn ||
280                    tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_warn)
281                 ret = 1;
282
283         /* Now check the shutdown limits. */
284         if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown ||
285             tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) {
286                 do_envctrl_shutdown(tp);
287                 ret = 1;
288         }
289
290         if (ret) {
291                 tp->fan_todo[FAN_CPU] = FAN_FULLBLAST;
292         } else if ((tick & (8 - 1)) == 0) {
293                 s8 cpu_goal_hi = cpu_temp_limits[tp->index].high_warn - 10;
294                 s8 cpu_goal_lo;
295
296                 cpu_goal_lo = cpu_goal_hi - 3;
297
298                 /* We do not try to avoid 'too cold' events.  Basically we
299                  * only try to deal with over-heating and fan noise reduction.
300                  */
301                 if (tp->avg_cpu_temp < cpu_goal_hi) {
302                         if (tp->avg_cpu_temp >= cpu_goal_lo)
303                                 tp->fan_todo[FAN_CPU] = FAN_SAME;
304                         else
305                                 tp->fan_todo[FAN_CPU] = FAN_SLOWER;
306                 } else {
307                         tp->fan_todo[FAN_CPU] = FAN_FASTER;
308                 }
309         } else {
310                 tp->fan_todo[FAN_CPU] = FAN_SAME;
311         }
312 }
313
314 static void analyze_temps(struct bbc_cpu_temperature *tp, unsigned long *last_warn)
315 {
316         tp->avg_amb_temp = (s8)((int)((int)tp->avg_amb_temp + (int)tp->curr_amb_temp) / 2);
317         tp->avg_cpu_temp = (s8)((int)((int)tp->avg_cpu_temp + (int)tp->curr_cpu_temp) / 2);
318
319         analyze_ambient_temp(tp, last_warn, tp->sample_tick);
320         analyze_cpu_temp(tp, last_warn, tp->sample_tick);
321
322         tp->sample_tick++;
323 }
324
325 static enum fan_action prioritize_fan_action(int which_fan)
326 {
327         struct bbc_cpu_temperature *tp;
328         enum fan_action decision = FAN_STATE_MAX;
329
330         /* Basically, prioritize what the temperature sensors
331          * recommend we do, and perform that action on all the
332          * fans.
333          */
334         for (tp = all_bbc_temps; tp; tp = tp->next) {
335                 if (tp->fan_todo[which_fan] == FAN_FULLBLAST) {
336                         decision = FAN_FULLBLAST;
337                         break;
338                 }
339                 if (tp->fan_todo[which_fan] == FAN_SAME &&
340                     decision != FAN_FASTER)
341                         decision = FAN_SAME;
342                 else if (tp->fan_todo[which_fan] == FAN_FASTER)
343                         decision = FAN_FASTER;
344                 else if (decision != FAN_FASTER &&
345                          decision != FAN_SAME &&
346                          tp->fan_todo[which_fan] == FAN_SLOWER)
347                         decision = FAN_SLOWER;
348         }
349         if (decision == FAN_STATE_MAX)
350                 decision = FAN_SAME;
351
352         return decision;
353 }
354
355 static int maybe_new_ambient_fan_speed(struct bbc_fan_control *fp)
356 {
357         enum fan_action decision = prioritize_fan_action(FAN_AMBIENT);
358         int ret;
359
360         if (decision == FAN_SAME)
361                 return 0;
362
363         ret = 1;
364         if (decision == FAN_FULLBLAST) {
365                 if (fp->system_fan_speed >= FAN_SPEED_MAX)
366                         ret = 0;
367                 else
368                         fp->system_fan_speed = FAN_SPEED_MAX;
369         } else {
370                 if (decision == FAN_FASTER) {
371                         if (fp->system_fan_speed >= FAN_SPEED_MAX)
372                                 ret = 0;
373                         else
374                                 fp->system_fan_speed += 2;
375                 } else {
376                         int orig_speed = fp->system_fan_speed;
377
378                         if (orig_speed <= FAN_SPEED_MIN ||
379                             orig_speed <= (fp->cpu_fan_speed - 3))
380                                 ret = 0;
381                         else
382                                 fp->system_fan_speed -= 1;
383                 }
384         }
385
386         return ret;
387 }
388
389 static int maybe_new_cpu_fan_speed(struct bbc_fan_control *fp)
390 {
391         enum fan_action decision = prioritize_fan_action(FAN_CPU);
392         int ret;
393
394         if (decision == FAN_SAME)
395                 return 0;
396
397         ret = 1;
398         if (decision == FAN_FULLBLAST) {
399                 if (fp->cpu_fan_speed >= FAN_SPEED_MAX)
400                         ret = 0;
401                 else
402                         fp->cpu_fan_speed = FAN_SPEED_MAX;
403         } else {
404                 if (decision == FAN_FASTER) {
405                         if (fp->cpu_fan_speed >= FAN_SPEED_MAX)
406                                 ret = 0;
407                         else {
408                                 fp->cpu_fan_speed += 2;
409                                 if (fp->system_fan_speed <
410                                     (fp->cpu_fan_speed - 3))
411                                         fp->system_fan_speed =
412                                                 fp->cpu_fan_speed - 3;
413                         }
414                 } else {
415                         if (fp->cpu_fan_speed <= FAN_SPEED_MIN)
416                                 ret = 0;
417                         else
418                                 fp->cpu_fan_speed -= 1;
419                 }
420         }
421
422         return ret;
423 }
424
425 static void maybe_new_fan_speeds(struct bbc_fan_control *fp)
426 {
427         int new;
428
429         new  = maybe_new_ambient_fan_speed(fp);
430         new |= maybe_new_cpu_fan_speed(fp);
431
432         if (new)
433                 set_fan_speeds(fp);
434 }
435
436 static void fans_full_blast(void)
437 {
438         struct bbc_fan_control *fp;
439
440         /* Since we will not be monitoring things anymore, put
441          * the fans on full blast.
442          */
443         for (fp = all_bbc_fans; fp; fp = fp->next) {
444                 fp->cpu_fan_speed = FAN_SPEED_MAX;
445                 fp->system_fan_speed = FAN_SPEED_MAX;
446                 fp->psupply_fan_on = 1;
447                 set_fan_speeds(fp);
448         }
449 }
450
451 #define POLL_INTERVAL   (5 * 1000)
452 static unsigned long last_warning_jiffies;
453 static struct task_struct *kenvctrld_task;
454
455 static int kenvctrld(void *__unused)
456 {
457         printk(KERN_INFO "bbc_envctrl: kenvctrld starting...\n");
458         last_warning_jiffies = jiffies - WARN_INTERVAL;
459         for (;;) {
460                 struct bbc_cpu_temperature *tp;
461                 struct bbc_fan_control *fp;
462
463                 msleep_interruptible(POLL_INTERVAL);
464                 if (kthread_should_stop())
465                         break;
466
467                 for (tp = all_bbc_temps; tp; tp = tp->next) {
468                         get_current_temps(tp);
469                         analyze_temps(tp, &last_warning_jiffies);
470                 }
471                 for (fp = all_bbc_fans; fp; fp = fp->next)
472                         maybe_new_fan_speeds(fp);
473         }
474         printk(KERN_INFO "bbc_envctrl: kenvctrld exiting...\n");
475
476         fans_full_blast();
477
478         return 0;
479 }
480
481 static void attach_one_temp(struct linux_ebus_child *echild, int temp_idx)
482 {
483         struct bbc_cpu_temperature *tp = kmalloc(sizeof(*tp), GFP_KERNEL);
484
485         if (!tp)
486                 return;
487         memset(tp, 0, sizeof(*tp));
488         tp->client = bbc_i2c_attach(echild);
489         if (!tp->client) {
490                 kfree(tp);
491                 return;
492         }
493
494         tp->index = temp_idx;
495         {
496                 struct bbc_cpu_temperature **tpp = &all_bbc_temps;
497                 while (*tpp)
498                         tpp = &((*tpp)->next);
499                 tp->next = NULL;
500                 *tpp = tp;
501         }
502
503         /* Tell it to convert once every 5 seconds, clear all cfg
504          * bits.
505          */
506         bbc_i2c_writeb(tp->client, 0x00, MAX1617_WR_CFG_BYTE);
507         bbc_i2c_writeb(tp->client, 0x02, MAX1617_WR_CVRATE_BYTE);
508
509         /* Program the hard temperature limits into the chip. */
510         bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].high_pwroff,
511                        MAX1617_WR_AMB_HIGHLIM);
512         bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].low_pwroff,
513                        MAX1617_WR_AMB_LOWLIM);
514         bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].high_pwroff,
515                        MAX1617_WR_CPU_HIGHLIM);
516         bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].low_pwroff,
517                        MAX1617_WR_CPU_LOWLIM);
518
519         get_current_temps(tp);
520         tp->prev_cpu_temp = tp->avg_cpu_temp = tp->curr_cpu_temp;
521         tp->prev_amb_temp = tp->avg_amb_temp = tp->curr_amb_temp;
522
523         tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
524         tp->fan_todo[FAN_CPU] = FAN_SAME;
525 }
526
527 static void attach_one_fan(struct linux_ebus_child *echild, int fan_idx)
528 {
529         struct bbc_fan_control *fp = kmalloc(sizeof(*fp), GFP_KERNEL);
530
531         if (!fp)
532                 return;
533         memset(fp, 0, sizeof(*fp));
534         fp->client = bbc_i2c_attach(echild);
535         if (!fp->client) {
536                 kfree(fp);
537                 return;
538         }
539
540         fp->index = fan_idx;
541
542         {
543                 struct bbc_fan_control **fpp = &all_bbc_fans;
544                 while (*fpp)
545                         fpp = &((*fpp)->next);
546                 fp->next = NULL;
547                 *fpp = fp;
548         }
549
550         /* The i2c device controlling the fans is write-only.
551          * So the only way to keep track of the current power
552          * level fed to the fans is via software.  Choose half
553          * power for cpu/system and 'on' fo the powersupply fan
554          * and set it now.
555          */
556         fp->psupply_fan_on = 1;
557         fp->cpu_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2;
558         fp->cpu_fan_speed += FAN_SPEED_MIN;
559         fp->system_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2;
560         fp->system_fan_speed += FAN_SPEED_MIN;
561
562         set_fan_speeds(fp);
563 }
564
565 int bbc_envctrl_init(void)
566 {
567         struct linux_ebus_child *echild;
568         int temp_index = 0;
569         int fan_index = 0;
570         int devidx = 0;
571
572         while ((echild = bbc_i2c_getdev(devidx++)) != NULL) {
573                 if (!strcmp(echild->prom_node->name, "temperature"))
574                         attach_one_temp(echild, temp_index++);
575                 if (!strcmp(echild->prom_node->name, "fan-control"))
576                         attach_one_fan(echild, fan_index++);
577         }
578         if (temp_index != 0 && fan_index != 0) {
579                 kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld");
580                 if (IS_ERR(kenvctrld_task))
581                         return PTR_ERR(kenvctrld_task);
582         }
583
584         return 0;
585 }
586
587 static void destroy_one_temp(struct bbc_cpu_temperature *tp)
588 {
589         bbc_i2c_detach(tp->client);
590         kfree(tp);
591 }
592
593 static void destroy_one_fan(struct bbc_fan_control *fp)
594 {
595         bbc_i2c_detach(fp->client);
596         kfree(fp);
597 }
598
599 void bbc_envctrl_cleanup(void)
600 {
601         struct bbc_cpu_temperature *tp;
602         struct bbc_fan_control *fp;
603
604         kthread_stop(kenvctrld_task);
605
606         tp = all_bbc_temps;
607         while (tp != NULL) {
608                 struct bbc_cpu_temperature *next = tp->next;
609                 destroy_one_temp(tp);
610                 tp = next;
611         }
612         all_bbc_temps = NULL;
613
614         fp = all_bbc_fans;
615         while (fp != NULL) {
616                 struct bbc_fan_control *next = fp->next;
617                 destroy_one_fan(fp);
618                 fp = next;
619         }
620         all_bbc_fans = NULL;
621 }