1
2
3
4
5
6#include <linux/kthread.h>
7#include <linux/delay.h>
8#include <linux/kmod.h>
9#include <linux/reboot.h>
10#include <linux/of.h>
11#include <linux/slab.h>
12#include <linux/of_device.h>
13#include <asm/oplib.h>
14
15#include "bbc_i2c.h"
16#include "max1617.h"
17
18#undef ENVCTRL_TRACE
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64struct temp_limits {
65 s8 high_pwroff, high_shutdown, high_warn;
66 s8 low_warn, low_shutdown, low_pwroff;
67};
68
69static struct temp_limits cpu_temp_limits[2] = {
70 { 100, 85, 80, 5, -5, -10 },
71 { 100, 85, 80, 5, -5, -10 },
72};
73
74static struct temp_limits amb_temp_limits[2] = {
75 { 65, 55, 40, 5, -5, -10 },
76 { 65, 55, 40, 5, -5, -10 },
77};
78
79static LIST_HEAD(all_temps);
80static LIST_HEAD(all_fans);
81
82#define CPU_FAN_REG 0xf0
83#define SYS_FAN_REG 0xf2
84#define PSUPPLY_FAN_REG 0xf4
85
86#define FAN_SPEED_MIN 0x0c
87#define FAN_SPEED_MAX 0x3f
88
89#define PSUPPLY_FAN_ON 0x1f
90#define PSUPPLY_FAN_OFF 0x00
91
92static void set_fan_speeds(struct bbc_fan_control *fp)
93{
94
95
96
97 if (fp->cpu_fan_speed < FAN_SPEED_MIN)
98 fp->cpu_fan_speed = FAN_SPEED_MIN;
99 if (fp->cpu_fan_speed > FAN_SPEED_MAX)
100 fp->cpu_fan_speed = FAN_SPEED_MAX;
101 if (fp->system_fan_speed < FAN_SPEED_MIN)
102 fp->system_fan_speed = FAN_SPEED_MIN;
103 if (fp->system_fan_speed > FAN_SPEED_MAX)
104 fp->system_fan_speed = FAN_SPEED_MAX;
105#ifdef ENVCTRL_TRACE
106 printk("fan%d: Changed fan speed to cpu(%02x) sys(%02x)\n",
107 fp->index,
108 fp->cpu_fan_speed, fp->system_fan_speed);
109#endif
110
111 bbc_i2c_writeb(fp->client, fp->cpu_fan_speed, CPU_FAN_REG);
112 bbc_i2c_writeb(fp->client, fp->system_fan_speed, SYS_FAN_REG);
113 bbc_i2c_writeb(fp->client,
114 (fp->psupply_fan_on ?
115 PSUPPLY_FAN_ON : PSUPPLY_FAN_OFF),
116 PSUPPLY_FAN_REG);
117}
118
119static void get_current_temps(struct bbc_cpu_temperature *tp)
120{
121 tp->prev_amb_temp = tp->curr_amb_temp;
122 bbc_i2c_readb(tp->client,
123 (unsigned char *) &tp->curr_amb_temp,
124 MAX1617_AMB_TEMP);
125 tp->prev_cpu_temp = tp->curr_cpu_temp;
126 bbc_i2c_readb(tp->client,
127 (unsigned char *) &tp->curr_cpu_temp,
128 MAX1617_CPU_TEMP);
129#ifdef ENVCTRL_TRACE
130 printk("temp%d: cpu(%d C) amb(%d C)\n",
131 tp->index,
132 (int) tp->curr_cpu_temp, (int) tp->curr_amb_temp);
133#endif
134}
135
136
137static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp)
138{
139 static int shutting_down = 0;
140 char *type = "???";
141 s8 val = -1;
142
143 if (shutting_down != 0)
144 return;
145
146 if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown ||
147 tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) {
148 type = "ambient";
149 val = tp->curr_amb_temp;
150 } else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown ||
151 tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) {
152 type = "CPU";
153 val = tp->curr_cpu_temp;
154 }
155
156 printk(KERN_CRIT "temp%d: Outside of safe %s "
157 "operating temperature, %d C.\n",
158 tp->index, type, val);
159
160 printk(KERN_CRIT "kenvctrld: Shutting down the system now.\n");
161
162 shutting_down = 1;
163 if (orderly_poweroff(true) < 0)
164 printk(KERN_CRIT "envctrl: shutdown execution failed\n");
165}
166
167#define WARN_INTERVAL (30 * HZ)
168
169static void analyze_ambient_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick)
170{
171 int ret = 0;
172
173 if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) {
174 if (tp->curr_amb_temp >=
175 amb_temp_limits[tp->index].high_warn) {
176 printk(KERN_WARNING "temp%d: "
177 "Above safe ambient operating temperature, %d C.\n",
178 tp->index, (int) tp->curr_amb_temp);
179 ret = 1;
180 } else if (tp->curr_amb_temp <
181 amb_temp_limits[tp->index].low_warn) {
182 printk(KERN_WARNING "temp%d: "
183 "Below safe ambient operating temperature, %d C.\n",
184 tp->index, (int) tp->curr_amb_temp);
185 ret = 1;
186 }
187 if (ret)
188 *last_warn = jiffies;
189 } else if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_warn ||
190 tp->curr_amb_temp < amb_temp_limits[tp->index].low_warn)
191 ret = 1;
192
193
194 if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown ||
195 tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) {
196 do_envctrl_shutdown(tp);
197 ret = 1;
198 }
199
200 if (ret) {
201 tp->fan_todo[FAN_AMBIENT] = FAN_FULLBLAST;
202 } else if ((tick & (8 - 1)) == 0) {
203 s8 amb_goal_hi = amb_temp_limits[tp->index].high_warn - 10;
204 s8 amb_goal_lo;
205
206 amb_goal_lo = amb_goal_hi - 3;
207
208
209
210
211 if (tp->avg_amb_temp < amb_goal_hi) {
212 if (tp->avg_amb_temp >= amb_goal_lo)
213 tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
214 else
215 tp->fan_todo[FAN_AMBIENT] = FAN_SLOWER;
216 } else {
217 tp->fan_todo[FAN_AMBIENT] = FAN_FASTER;
218 }
219 } else {
220 tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
221 }
222}
223
224static void analyze_cpu_temp(struct bbc_cpu_temperature *tp, unsigned long *last_warn, int tick)
225{
226 int ret = 0;
227
228 if (time_after(jiffies, (*last_warn + WARN_INTERVAL))) {
229 if (tp->curr_cpu_temp >=
230 cpu_temp_limits[tp->index].high_warn) {
231 printk(KERN_WARNING "temp%d: "
232 "Above safe CPU operating temperature, %d C.\n",
233 tp->index, (int) tp->curr_cpu_temp);
234 ret = 1;
235 } else if (tp->curr_cpu_temp <
236 cpu_temp_limits[tp->index].low_warn) {
237 printk(KERN_WARNING "temp%d: "
238 "Below safe CPU operating temperature, %d C.\n",
239 tp->index, (int) tp->curr_cpu_temp);
240 ret = 1;
241 }
242 if (ret)
243 *last_warn = jiffies;
244 } else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_warn ||
245 tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_warn)
246 ret = 1;
247
248
249 if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown ||
250 tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) {
251 do_envctrl_shutdown(tp);
252 ret = 1;
253 }
254
255 if (ret) {
256 tp->fan_todo[FAN_CPU] = FAN_FULLBLAST;
257 } else if ((tick & (8 - 1)) == 0) {
258 s8 cpu_goal_hi = cpu_temp_limits[tp->index].high_warn - 10;
259 s8 cpu_goal_lo;
260
261 cpu_goal_lo = cpu_goal_hi - 3;
262
263
264
265
266 if (tp->avg_cpu_temp < cpu_goal_hi) {
267 if (tp->avg_cpu_temp >= cpu_goal_lo)
268 tp->fan_todo[FAN_CPU] = FAN_SAME;
269 else
270 tp->fan_todo[FAN_CPU] = FAN_SLOWER;
271 } else {
272 tp->fan_todo[FAN_CPU] = FAN_FASTER;
273 }
274 } else {
275 tp->fan_todo[FAN_CPU] = FAN_SAME;
276 }
277}
278
279static void analyze_temps(struct bbc_cpu_temperature *tp, unsigned long *last_warn)
280{
281 tp->avg_amb_temp = (s8)((int)((int)tp->avg_amb_temp + (int)tp->curr_amb_temp) / 2);
282 tp->avg_cpu_temp = (s8)((int)((int)tp->avg_cpu_temp + (int)tp->curr_cpu_temp) / 2);
283
284 analyze_ambient_temp(tp, last_warn, tp->sample_tick);
285 analyze_cpu_temp(tp, last_warn, tp->sample_tick);
286
287 tp->sample_tick++;
288}
289
290static enum fan_action prioritize_fan_action(int which_fan)
291{
292 struct bbc_cpu_temperature *tp;
293 enum fan_action decision = FAN_STATE_MAX;
294
295
296
297
298
299 list_for_each_entry(tp, &all_temps, glob_list) {
300 if (tp->fan_todo[which_fan] == FAN_FULLBLAST) {
301 decision = FAN_FULLBLAST;
302 break;
303 }
304 if (tp->fan_todo[which_fan] == FAN_SAME &&
305 decision != FAN_FASTER)
306 decision = FAN_SAME;
307 else if (tp->fan_todo[which_fan] == FAN_FASTER)
308 decision = FAN_FASTER;
309 else if (decision != FAN_FASTER &&
310 decision != FAN_SAME &&
311 tp->fan_todo[which_fan] == FAN_SLOWER)
312 decision = FAN_SLOWER;
313 }
314 if (decision == FAN_STATE_MAX)
315 decision = FAN_SAME;
316
317 return decision;
318}
319
320static int maybe_new_ambient_fan_speed(struct bbc_fan_control *fp)
321{
322 enum fan_action decision = prioritize_fan_action(FAN_AMBIENT);
323 int ret;
324
325 if (decision == FAN_SAME)
326 return 0;
327
328 ret = 1;
329 if (decision == FAN_FULLBLAST) {
330 if (fp->system_fan_speed >= FAN_SPEED_MAX)
331 ret = 0;
332 else
333 fp->system_fan_speed = FAN_SPEED_MAX;
334 } else {
335 if (decision == FAN_FASTER) {
336 if (fp->system_fan_speed >= FAN_SPEED_MAX)
337 ret = 0;
338 else
339 fp->system_fan_speed += 2;
340 } else {
341 int orig_speed = fp->system_fan_speed;
342
343 if (orig_speed <= FAN_SPEED_MIN ||
344 orig_speed <= (fp->cpu_fan_speed - 3))
345 ret = 0;
346 else
347 fp->system_fan_speed -= 1;
348 }
349 }
350
351 return ret;
352}
353
354static int maybe_new_cpu_fan_speed(struct bbc_fan_control *fp)
355{
356 enum fan_action decision = prioritize_fan_action(FAN_CPU);
357 int ret;
358
359 if (decision == FAN_SAME)
360 return 0;
361
362 ret = 1;
363 if (decision == FAN_FULLBLAST) {
364 if (fp->cpu_fan_speed >= FAN_SPEED_MAX)
365 ret = 0;
366 else
367 fp->cpu_fan_speed = FAN_SPEED_MAX;
368 } else {
369 if (decision == FAN_FASTER) {
370 if (fp->cpu_fan_speed >= FAN_SPEED_MAX)
371 ret = 0;
372 else {
373 fp->cpu_fan_speed += 2;
374 if (fp->system_fan_speed <
375 (fp->cpu_fan_speed - 3))
376 fp->system_fan_speed =
377 fp->cpu_fan_speed - 3;
378 }
379 } else {
380 if (fp->cpu_fan_speed <= FAN_SPEED_MIN)
381 ret = 0;
382 else
383 fp->cpu_fan_speed -= 1;
384 }
385 }
386
387 return ret;
388}
389
390static void maybe_new_fan_speeds(struct bbc_fan_control *fp)
391{
392 int new;
393
394 new = maybe_new_ambient_fan_speed(fp);
395 new |= maybe_new_cpu_fan_speed(fp);
396
397 if (new)
398 set_fan_speeds(fp);
399}
400
401static void fans_full_blast(void)
402{
403 struct bbc_fan_control *fp;
404
405
406
407
408 list_for_each_entry(fp, &all_fans, glob_list) {
409 fp->cpu_fan_speed = FAN_SPEED_MAX;
410 fp->system_fan_speed = FAN_SPEED_MAX;
411 fp->psupply_fan_on = 1;
412 set_fan_speeds(fp);
413 }
414}
415
416#define POLL_INTERVAL (5 * 1000)
417static unsigned long last_warning_jiffies;
418static struct task_struct *kenvctrld_task;
419
420static int kenvctrld(void *__unused)
421{
422 printk(KERN_INFO "bbc_envctrl: kenvctrld starting...\n");
423 last_warning_jiffies = jiffies - WARN_INTERVAL;
424 for (;;) {
425 struct bbc_cpu_temperature *tp;
426 struct bbc_fan_control *fp;
427
428 msleep_interruptible(POLL_INTERVAL);
429 if (kthread_should_stop())
430 break;
431
432 list_for_each_entry(tp, &all_temps, glob_list) {
433 get_current_temps(tp);
434 analyze_temps(tp, &last_warning_jiffies);
435 }
436 list_for_each_entry(fp, &all_fans, glob_list)
437 maybe_new_fan_speeds(fp);
438 }
439 printk(KERN_INFO "bbc_envctrl: kenvctrld exiting...\n");
440
441 fans_full_blast();
442
443 return 0;
444}
445
446static void attach_one_temp(struct bbc_i2c_bus *bp, struct platform_device *op,
447 int temp_idx)
448{
449 struct bbc_cpu_temperature *tp;
450
451 tp = kzalloc(sizeof(*tp), GFP_KERNEL);
452 if (!tp)
453 return;
454
455 tp->client = bbc_i2c_attach(bp, op);
456 if (!tp->client) {
457 kfree(tp);
458 return;
459 }
460
461
462 tp->index = temp_idx;
463
464 list_add(&tp->glob_list, &all_temps);
465 list_add(&tp->bp_list, &bp->temps);
466
467
468
469
470 bbc_i2c_writeb(tp->client, 0x00, MAX1617_WR_CFG_BYTE);
471 bbc_i2c_writeb(tp->client, 0x02, MAX1617_WR_CVRATE_BYTE);
472
473
474 bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].high_pwroff,
475 MAX1617_WR_AMB_HIGHLIM);
476 bbc_i2c_writeb(tp->client, amb_temp_limits[tp->index].low_pwroff,
477 MAX1617_WR_AMB_LOWLIM);
478 bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].high_pwroff,
479 MAX1617_WR_CPU_HIGHLIM);
480 bbc_i2c_writeb(tp->client, cpu_temp_limits[tp->index].low_pwroff,
481 MAX1617_WR_CPU_LOWLIM);
482
483 get_current_temps(tp);
484 tp->prev_cpu_temp = tp->avg_cpu_temp = tp->curr_cpu_temp;
485 tp->prev_amb_temp = tp->avg_amb_temp = tp->curr_amb_temp;
486
487 tp->fan_todo[FAN_AMBIENT] = FAN_SAME;
488 tp->fan_todo[FAN_CPU] = FAN_SAME;
489}
490
491static void attach_one_fan(struct bbc_i2c_bus *bp, struct platform_device *op,
492 int fan_idx)
493{
494 struct bbc_fan_control *fp;
495
496 fp = kzalloc(sizeof(*fp), GFP_KERNEL);
497 if (!fp)
498 return;
499
500 fp->client = bbc_i2c_attach(bp, op);
501 if (!fp->client) {
502 kfree(fp);
503 return;
504 }
505
506 fp->index = fan_idx;
507
508 list_add(&fp->glob_list, &all_fans);
509 list_add(&fp->bp_list, &bp->fans);
510
511
512
513
514
515
516
517 fp->psupply_fan_on = 1;
518 fp->cpu_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2;
519 fp->cpu_fan_speed += FAN_SPEED_MIN;
520 fp->system_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2;
521 fp->system_fan_speed += FAN_SPEED_MIN;
522
523 set_fan_speeds(fp);
524}
525
526static void destroy_one_temp(struct bbc_cpu_temperature *tp)
527{
528 bbc_i2c_detach(tp->client);
529 kfree(tp);
530}
531
532static void destroy_all_temps(struct bbc_i2c_bus *bp)
533{
534 struct bbc_cpu_temperature *tp, *tpos;
535
536 list_for_each_entry_safe(tp, tpos, &bp->temps, bp_list) {
537 list_del(&tp->bp_list);
538 list_del(&tp->glob_list);
539 destroy_one_temp(tp);
540 }
541}
542
543static void destroy_one_fan(struct bbc_fan_control *fp)
544{
545 bbc_i2c_detach(fp->client);
546 kfree(fp);
547}
548
549static void destroy_all_fans(struct bbc_i2c_bus *bp)
550{
551 struct bbc_fan_control *fp, *fpos;
552
553 list_for_each_entry_safe(fp, fpos, &bp->fans, bp_list) {
554 list_del(&fp->bp_list);
555 list_del(&fp->glob_list);
556 destroy_one_fan(fp);
557 }
558}
559
560int bbc_envctrl_init(struct bbc_i2c_bus *bp)
561{
562 struct platform_device *op;
563 int temp_index = 0;
564 int fan_index = 0;
565 int devidx = 0;
566
567 while ((op = bbc_i2c_getdev(bp, devidx++)) != NULL) {
568 if (!strcmp(op->dev.of_node->name, "temperature"))
569 attach_one_temp(bp, op, temp_index++);
570 if (!strcmp(op->dev.of_node->name, "fan-control"))
571 attach_one_fan(bp, op, fan_index++);
572 }
573 if (temp_index != 0 && fan_index != 0) {
574 kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld");
575 if (IS_ERR(kenvctrld_task)) {
576 int err = PTR_ERR(kenvctrld_task);
577
578 kenvctrld_task = NULL;
579 destroy_all_temps(bp);
580 destroy_all_fans(bp);
581 return err;
582 }
583 }
584
585 return 0;
586}
587
588void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp)
589{
590 if (kenvctrld_task)
591 kthread_stop(kenvctrld_task);
592
593 destroy_all_temps(bp);
594 destroy_all_fans(bp);
595}
596