1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* bbc_envctrl.c: UltraSPARC-III environment control driver. |
3 | * |
4 | * Copyright (C) 2001, 2008 David S. Miller (davem@davemloft.net) |
5 | */ |
6 | |
7 | #include <linux/kthread.h> |
8 | #include <linux/delay.h> |
9 | #include <linux/kmod.h> |
10 | #include <linux/reboot.h> |
11 | #include <linux/of.h> |
12 | #include <linux/platform_device.h> |
13 | #include <linux/slab.h> |
14 | #include <asm/oplib.h> |
15 | |
16 | #include "bbc_i2c.h" |
17 | #include "max1617.h" |
18 | |
19 | #undef ENVCTRL_TRACE |
20 | |
21 | /* WARNING: Making changes to this driver is very dangerous. |
22 | * If you misprogram the sensor chips they can |
23 | * cut the power on you instantly. |
24 | */ |
25 | |
26 | /* Two temperature sensors exist in the SunBLADE-1000 enclosure. |
27 | * Both are implemented using max1617 i2c devices. Each max1617 |
28 | * monitors 2 temperatures, one for one of the cpu dies and the other |
29 | * for the ambient temperature. |
30 | * |
31 | * The max1617 is capable of being programmed with power-off |
32 | * temperature values, one low limit and one high limit. These |
33 | * can be controlled independently for the cpu or ambient temperature. |
34 | * If a limit is violated, the power is simply shut off. The frequency |
35 | * with which the max1617 does temperature sampling can be controlled |
36 | * as well. |
37 | * |
38 | * Three fans exist inside the machine, all three are controlled with |
39 | * an i2c digital to analog converter. There is a fan directed at the |
40 | * two processor slots, another for the rest of the enclosure, and the |
41 | * third is for the power supply. The first two fans may be speed |
42 | * controlled by changing the voltage fed to them. The third fan may |
43 | * only be completely off or on. The third fan is meant to only be |
44 | * disabled/enabled when entering/exiting the lowest power-saving |
45 | * mode of the machine. |
46 | * |
47 | * An environmental control kernel thread periodically monitors all |
48 | * temperature sensors. Based upon the samples it will adjust the |
49 | * fan speeds to try and keep the system within a certain temperature |
50 | * range (the goal being to make the fans as quiet as possible without |
51 | * allowing the system to get too hot). |
52 | * |
53 | * If the temperature begins to rise/fall outside of the acceptable |
54 | * operating range, a periodic warning will be sent to the kernel log. |
55 | * The fans will be put on full blast to attempt to deal with this |
56 | * situation. After exceeding the acceptable operating range by a |
57 | * certain threshold, the kernel thread will shut down the system. |
58 | * Here, the thread is attempting to shut the machine down cleanly |
59 | * before the hardware based power-off event is triggered. |
60 | */ |
61 | |
62 | /* These settings are in Celsius. We use these defaults only |
63 | * if we cannot interrogate the cpu-fru SEEPROM. |
64 | */ |
65 | struct temp_limits { |
66 | s8 high_pwroff, high_shutdown, high_warn; |
67 | s8 low_warn, low_shutdown, low_pwroff; |
68 | }; |
69 | |
70 | static struct temp_limits cpu_temp_limits[2] = { |
71 | { 100, 85, 80, 5, -5, -10 }, |
72 | { 100, 85, 80, 5, -5, -10 }, |
73 | }; |
74 | |
75 | static struct temp_limits amb_temp_limits[2] = { |
76 | { 65, 55, 40, 5, -5, -10 }, |
77 | { 65, 55, 40, 5, -5, -10 }, |
78 | }; |
79 | |
80 | static LIST_HEAD(all_temps); |
81 | static LIST_HEAD(all_fans); |
82 | |
83 | #define CPU_FAN_REG 0xf0 |
84 | #define SYS_FAN_REG 0xf2 |
85 | #define PSUPPLY_FAN_REG 0xf4 |
86 | |
87 | #define FAN_SPEED_MIN 0x0c |
88 | #define FAN_SPEED_MAX 0x3f |
89 | |
90 | #define PSUPPLY_FAN_ON 0x1f |
91 | #define PSUPPLY_FAN_OFF 0x00 |
92 | |
93 | static void set_fan_speeds(struct bbc_fan_control *fp) |
94 | { |
95 | /* Put temperatures into range so we don't mis-program |
96 | * the hardware. |
97 | */ |
98 | if (fp->cpu_fan_speed < FAN_SPEED_MIN) |
99 | fp->cpu_fan_speed = FAN_SPEED_MIN; |
100 | if (fp->cpu_fan_speed > FAN_SPEED_MAX) |
101 | fp->cpu_fan_speed = FAN_SPEED_MAX; |
102 | if (fp->system_fan_speed < FAN_SPEED_MIN) |
103 | fp->system_fan_speed = FAN_SPEED_MIN; |
104 | if (fp->system_fan_speed > FAN_SPEED_MAX) |
105 | fp->system_fan_speed = FAN_SPEED_MAX; |
106 | #ifdef ENVCTRL_TRACE |
107 | printk("fan%d: Changed fan speed to cpu(%02x) sys(%02x)\n" , |
108 | fp->index, |
109 | fp->cpu_fan_speed, fp->system_fan_speed); |
110 | #endif |
111 | |
112 | bbc_i2c_writeb(fp->client, val: fp->cpu_fan_speed, CPU_FAN_REG); |
113 | bbc_i2c_writeb(fp->client, val: fp->system_fan_speed, SYS_FAN_REG); |
114 | bbc_i2c_writeb(fp->client, |
115 | val: (fp->psupply_fan_on ? |
116 | PSUPPLY_FAN_ON : PSUPPLY_FAN_OFF), |
117 | PSUPPLY_FAN_REG); |
118 | } |
119 | |
120 | static void get_current_temps(struct bbc_cpu_temperature *tp) |
121 | { |
122 | tp->prev_amb_temp = tp->curr_amb_temp; |
123 | bbc_i2c_readb(tp->client, |
124 | byte: (unsigned char *) &tp->curr_amb_temp, |
125 | MAX1617_AMB_TEMP); |
126 | tp->prev_cpu_temp = tp->curr_cpu_temp; |
127 | bbc_i2c_readb(tp->client, |
128 | byte: (unsigned char *) &tp->curr_cpu_temp, |
129 | MAX1617_CPU_TEMP); |
130 | #ifdef ENVCTRL_TRACE |
131 | printk("temp%d: cpu(%d C) amb(%d C)\n" , |
132 | tp->index, |
133 | (int) tp->curr_cpu_temp, (int) tp->curr_amb_temp); |
134 | #endif |
135 | } |
136 | |
137 | |
138 | static void do_envctrl_shutdown(struct bbc_cpu_temperature *tp) |
139 | { |
140 | static int shutting_down = 0; |
141 | char *type = "???" ; |
142 | s8 val = -1; |
143 | |
144 | if (shutting_down != 0) |
145 | return; |
146 | |
147 | if (tp->curr_amb_temp >= amb_temp_limits[tp->index].high_shutdown || |
148 | tp->curr_amb_temp < amb_temp_limits[tp->index].low_shutdown) { |
149 | type = "ambient" ; |
150 | val = tp->curr_amb_temp; |
151 | } else if (tp->curr_cpu_temp >= cpu_temp_limits[tp->index].high_shutdown || |
152 | tp->curr_cpu_temp < cpu_temp_limits[tp->index].low_shutdown) { |
153 | type = "CPU" ; |
154 | val = tp->curr_cpu_temp; |
155 | } |
156 | |
157 | printk(KERN_CRIT "temp%d: Outside of safe %s " |
158 | "operating temperature, %d C.\n" , |
159 | tp->index, type, val); |
160 | |
161 | printk(KERN_CRIT "kenvctrld: Shutting down the system now.\n" ); |
162 | |
163 | shutting_down = 1; |
164 | orderly_poweroff(force: true); |
165 | } |
166 | |
167 | #define WARN_INTERVAL (30 * HZ) |
168 | |
169 | static 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 | /* Now check the shutdown limits. */ |
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 | /* We do not try to avoid 'too cold' events. Basically we |
209 | * only try to deal with over-heating and fan noise reduction. |
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 | |
224 | static 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 | /* Now check the shutdown limits. */ |
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 | /* We do not try to avoid 'too cold' events. Basically we |
264 | * only try to deal with over-heating and fan noise reduction. |
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 | |
279 | static 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, tick: tp->sample_tick); |
285 | analyze_cpu_temp(tp, last_warn, tick: tp->sample_tick); |
286 | |
287 | tp->sample_tick++; |
288 | } |
289 | |
290 | static 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 | /* Basically, prioritize what the temperature sensors |
296 | * recommend we do, and perform that action on all the |
297 | * fans. |
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 | |
320 | static 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 | |
354 | static 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 | |
390 | static 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 | |
401 | static void fans_full_blast(void) |
402 | { |
403 | struct bbc_fan_control *fp; |
404 | |
405 | /* Since we will not be monitoring things anymore, put |
406 | * the fans on full blast. |
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) |
417 | static unsigned long last_warning_jiffies; |
418 | static struct task_struct *kenvctrld_task; |
419 | |
420 | static 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_warn: &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 | |
446 | static 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(size: sizeof(*tp), GFP_KERNEL); |
452 | if (!tp) |
453 | return; |
454 | |
455 | INIT_LIST_HEAD(list: &tp->bp_list); |
456 | INIT_LIST_HEAD(list: &tp->glob_list); |
457 | |
458 | tp->client = bbc_i2c_attach(bp, op); |
459 | if (!tp->client) { |
460 | kfree(objp: tp); |
461 | return; |
462 | } |
463 | |
464 | |
465 | tp->index = temp_idx; |
466 | |
467 | list_add(new: &tp->glob_list, head: &all_temps); |
468 | list_add(new: &tp->bp_list, head: &bp->temps); |
469 | |
470 | /* Tell it to convert once every 5 seconds, clear all cfg |
471 | * bits. |
472 | */ |
473 | bbc_i2c_writeb(tp->client, val: 0x00, MAX1617_WR_CFG_BYTE); |
474 | bbc_i2c_writeb(tp->client, val: 0x02, MAX1617_WR_CVRATE_BYTE); |
475 | |
476 | /* Program the hard temperature limits into the chip. */ |
477 | bbc_i2c_writeb(tp->client, val: amb_temp_limits[tp->index].high_pwroff, |
478 | MAX1617_WR_AMB_HIGHLIM); |
479 | bbc_i2c_writeb(tp->client, val: amb_temp_limits[tp->index].low_pwroff, |
480 | MAX1617_WR_AMB_LOWLIM); |
481 | bbc_i2c_writeb(tp->client, val: cpu_temp_limits[tp->index].high_pwroff, |
482 | MAX1617_WR_CPU_HIGHLIM); |
483 | bbc_i2c_writeb(tp->client, val: cpu_temp_limits[tp->index].low_pwroff, |
484 | MAX1617_WR_CPU_LOWLIM); |
485 | |
486 | get_current_temps(tp); |
487 | tp->prev_cpu_temp = tp->avg_cpu_temp = tp->curr_cpu_temp; |
488 | tp->prev_amb_temp = tp->avg_amb_temp = tp->curr_amb_temp; |
489 | |
490 | tp->fan_todo[FAN_AMBIENT] = FAN_SAME; |
491 | tp->fan_todo[FAN_CPU] = FAN_SAME; |
492 | } |
493 | |
494 | static void attach_one_fan(struct bbc_i2c_bus *bp, struct platform_device *op, |
495 | int fan_idx) |
496 | { |
497 | struct bbc_fan_control *fp; |
498 | |
499 | fp = kzalloc(size: sizeof(*fp), GFP_KERNEL); |
500 | if (!fp) |
501 | return; |
502 | |
503 | INIT_LIST_HEAD(list: &fp->bp_list); |
504 | INIT_LIST_HEAD(list: &fp->glob_list); |
505 | |
506 | fp->client = bbc_i2c_attach(bp, op); |
507 | if (!fp->client) { |
508 | kfree(objp: fp); |
509 | return; |
510 | } |
511 | |
512 | fp->index = fan_idx; |
513 | |
514 | list_add(new: &fp->glob_list, head: &all_fans); |
515 | list_add(new: &fp->bp_list, head: &bp->fans); |
516 | |
517 | /* The i2c device controlling the fans is write-only. |
518 | * So the only way to keep track of the current power |
519 | * level fed to the fans is via software. Choose half |
520 | * power for cpu/system and 'on' fo the powersupply fan |
521 | * and set it now. |
522 | */ |
523 | fp->psupply_fan_on = 1; |
524 | fp->cpu_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2; |
525 | fp->cpu_fan_speed += FAN_SPEED_MIN; |
526 | fp->system_fan_speed = (FAN_SPEED_MAX - FAN_SPEED_MIN) / 2; |
527 | fp->system_fan_speed += FAN_SPEED_MIN; |
528 | |
529 | set_fan_speeds(fp); |
530 | } |
531 | |
532 | static void destroy_one_temp(struct bbc_cpu_temperature *tp) |
533 | { |
534 | bbc_i2c_detach(tp->client); |
535 | kfree(objp: tp); |
536 | } |
537 | |
538 | static void destroy_all_temps(struct bbc_i2c_bus *bp) |
539 | { |
540 | struct bbc_cpu_temperature *tp, *tpos; |
541 | |
542 | list_for_each_entry_safe(tp, tpos, &bp->temps, bp_list) { |
543 | list_del(entry: &tp->bp_list); |
544 | list_del(entry: &tp->glob_list); |
545 | destroy_one_temp(tp); |
546 | } |
547 | } |
548 | |
549 | static void destroy_one_fan(struct bbc_fan_control *fp) |
550 | { |
551 | bbc_i2c_detach(fp->client); |
552 | kfree(objp: fp); |
553 | } |
554 | |
555 | static void destroy_all_fans(struct bbc_i2c_bus *bp) |
556 | { |
557 | struct bbc_fan_control *fp, *fpos; |
558 | |
559 | list_for_each_entry_safe(fp, fpos, &bp->fans, bp_list) { |
560 | list_del(entry: &fp->bp_list); |
561 | list_del(entry: &fp->glob_list); |
562 | destroy_one_fan(fp); |
563 | } |
564 | } |
565 | |
566 | int bbc_envctrl_init(struct bbc_i2c_bus *bp) |
567 | { |
568 | struct platform_device *op; |
569 | int temp_index = 0; |
570 | int fan_index = 0; |
571 | int devidx = 0; |
572 | |
573 | while ((op = bbc_i2c_getdev(bp, devidx++)) != NULL) { |
574 | if (of_node_name_eq(np: op->dev.of_node, name: "temperature" )) |
575 | attach_one_temp(bp, op, temp_idx: temp_index++); |
576 | if (of_node_name_eq(np: op->dev.of_node, name: "fan-control" )) |
577 | attach_one_fan(bp, op, fan_idx: fan_index++); |
578 | } |
579 | if (temp_index != 0 && fan_index != 0) { |
580 | kenvctrld_task = kthread_run(kenvctrld, NULL, "kenvctrld" ); |
581 | if (IS_ERR(ptr: kenvctrld_task)) { |
582 | int err = PTR_ERR(ptr: kenvctrld_task); |
583 | |
584 | kenvctrld_task = NULL; |
585 | destroy_all_temps(bp); |
586 | destroy_all_fans(bp); |
587 | return err; |
588 | } |
589 | } |
590 | |
591 | return 0; |
592 | } |
593 | |
594 | void bbc_envctrl_cleanup(struct bbc_i2c_bus *bp) |
595 | { |
596 | if (kenvctrld_task) |
597 | kthread_stop(k: kenvctrld_task); |
598 | |
599 | destroy_all_temps(bp); |
600 | destroy_all_fans(bp); |
601 | } |
602 | |