aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorÉtienne Bersac <bersace@gmail.com>2008-04-29 01:39:55 -0400
committerPaul Mackerras <paulus@samba.org>2008-04-29 01:57:35 -0400
commit80ff974dba8cc432ab81676fc09d3c357cb11276 (patch)
treeb775a81b3ceca1a6df40fec751293d0398c7c36f
parent21e38dfee53a2159d14a24a3d2277ae757599efa (diff)
[POWERPC] windfarm: Add PowerMac 12,1 support
This implements a new driver named windfarm_pm121, which drives the fans on PowerMac 12,1 machines : iMac G5 iSight (rev C) 17" and 20". It's based on the windfarm_pm81 driver from Benjamin Herrenschmidt. This includes fixes from David Woodhouse correcting the names of some of the sensors. Signed-off-by: Étienne Bersac <bersace@gmail.com> Signed-off-by: David Woodhouse <dwmw2@infradead.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org> Signed-off-by: Paul Mackerras <paulus@samba.org>
-rw-r--r--arch/powerpc/configs/g5_defconfig1
-rw-r--r--drivers/macintosh/Kconfig8
-rw-r--r--drivers/macintosh/Makefile5
-rw-r--r--drivers/macintosh/windfarm_lm75_sensor.c6
-rw-r--r--drivers/macintosh/windfarm_max6690_sensor.c20
-rw-r--r--drivers/macintosh/windfarm_pm121.c1040
-rw-r--r--drivers/macintosh/windfarm_smu_controls.c4
7 files changed, 1078 insertions, 6 deletions
diff --git a/arch/powerpc/configs/g5_defconfig b/arch/powerpc/configs/g5_defconfig
index a20501f89474..88338a9f5e95 100644
--- a/arch/powerpc/configs/g5_defconfig
+++ b/arch/powerpc/configs/g5_defconfig
@@ -696,6 +696,7 @@ CONFIG_WINDFARM=y
696CONFIG_WINDFARM_PM81=y 696CONFIG_WINDFARM_PM81=y
697CONFIG_WINDFARM_PM91=y 697CONFIG_WINDFARM_PM91=y
698CONFIG_WINDFARM_PM112=y 698CONFIG_WINDFARM_PM112=y
699CONFIG_WINDFARM_PM121=y
699# CONFIG_PMAC_RACKMETER is not set 700# CONFIG_PMAC_RACKMETER is not set
700CONFIG_NETDEVICES=y 701CONFIG_NETDEVICES=y
701# CONFIG_NETDEVICES_MULTIQUEUE is not set 702# CONFIG_NETDEVICES_MULTIQUEUE is not set
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig
index 77f50b63a970..b52659620d50 100644
--- a/drivers/macintosh/Kconfig
+++ b/drivers/macintosh/Kconfig
@@ -234,6 +234,14 @@ config WINDFARM_PM112
234 which are the recent dual and quad G5 machines using the 234 which are the recent dual and quad G5 machines using the
235 970MP dual-core processor. 235 970MP dual-core processor.
236 236
237config WINDFARM_PM121
238 tristate "Support for thermal management on PowerMac12,1"
239 depends on WINDFARM && I2C && PMAC_SMU
240 select I2C_POWERMAC
241 help
242 This driver provides thermal control for the PowerMac12,1
243 which is the iMac G5 (iSight).
244
237config ANSLCD 245config ANSLCD
238 tristate "Support for ANS LCD display" 246 tristate "Support for ANS LCD display"
239 depends on ADB_CUDA && PPC_PMAC 247 depends on ADB_CUDA && PPC_PMAC
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile
index 2dfc3f4eaf42..e3132efa17c0 100644
--- a/drivers/macintosh/Makefile
+++ b/drivers/macintosh/Makefile
@@ -42,4 +42,9 @@ obj-$(CONFIG_WINDFARM_PM112) += windfarm_pm112.o windfarm_smu_sat.o \
42 windfarm_smu_sensors.o \ 42 windfarm_smu_sensors.o \
43 windfarm_max6690_sensor.o \ 43 windfarm_max6690_sensor.o \
44 windfarm_lm75_sensor.o windfarm_pid.o 44 windfarm_lm75_sensor.o windfarm_pid.o
45obj-$(CONFIG_WINDFARM_PM121) += windfarm_pm121.o windfarm_smu_sat.o \
46 windfarm_smu_controls.o \
47 windfarm_smu_sensors.o \
48 windfarm_max6690_sensor.o \
49 windfarm_lm75_sensor.o windfarm_pid.o
45obj-$(CONFIG_PMAC_RACKMETER) += rack-meter.o 50obj-$(CONFIG_PMAC_RACKMETER) += rack-meter.o
diff --git a/drivers/macintosh/windfarm_lm75_sensor.c b/drivers/macintosh/windfarm_lm75_sensor.c
index 7e10c3ab4d50..b92b959fe16e 100644
--- a/drivers/macintosh/windfarm_lm75_sensor.c
+++ b/drivers/macintosh/windfarm_lm75_sensor.c
@@ -127,6 +127,12 @@ static struct wf_lm75_sensor *wf_lm75_create(struct i2c_adapter *adapter,
127 */ 127 */
128 if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY")) 128 if (!strcmp(loc, "Hard drive") || !strcmp(loc, "DRIVE BAY"))
129 lm->sens.name = "hd-temp"; 129 lm->sens.name = "hd-temp";
130 else if (!strcmp(loc, "Incoming Air Temp"))
131 lm->sens.name = "incoming-air-temp";
132 else if (!strcmp(loc, "ODD Temp"))
133 lm->sens.name = "optical-drive-temp";
134 else if (!strcmp(loc, "HD Temp"))
135 lm->sens.name = "hard-drive-temp";
130 else 136 else
131 goto fail; 137 goto fail;
132 138
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c
index 5f03aab9fb5d..e207a90d6b27 100644
--- a/drivers/macintosh/windfarm_max6690_sensor.c
+++ b/drivers/macintosh/windfarm_max6690_sensor.c
@@ -77,18 +77,28 @@ static struct wf_sensor_ops wf_max6690_ops = {
77 .owner = THIS_MODULE, 77 .owner = THIS_MODULE,
78}; 78};
79 79
80static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr) 80static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr,
81 const char *loc)
81{ 82{
82 struct wf_6690_sensor *max; 83 struct wf_6690_sensor *max;
83 char *name = "backside-temp"; 84 char *name;
84 85
85 max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); 86 max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
86 if (max == NULL) { 87 if (max == NULL) {
87 printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: " 88 printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: "
88 "no memory\n", name); 89 "no memory\n", loc);
89 return; 90 return;
90 } 91 }
91 92
93 if (!strcmp(loc, "BACKSIDE"))
94 name = "backside-temp";
95 else if (!strcmp(loc, "NB Ambient"))
96 name = "north-bridge-temp";
97 else if (!strcmp(loc, "GPU Ambient"))
98 name = "gpu-temp";
99 else
100 goto fail;
101
92 max->sens.ops = &wf_max6690_ops; 102 max->sens.ops = &wf_max6690_ops;
93 max->sens.name = name; 103 max->sens.name = name;
94 max->i2c.addr = addr >> 1; 104 max->i2c.addr = addr >> 1;
@@ -138,9 +148,7 @@ static int wf_max6690_attach(struct i2c_adapter *adapter)
138 if (loc == NULL || addr == 0) 148 if (loc == NULL || addr == 0)
139 continue; 149 continue;
140 printk("found max6690, loc=%s addr=0x%02x\n", loc, addr); 150 printk("found max6690, loc=%s addr=0x%02x\n", loc, addr);
141 if (strcmp(loc, "BACKSIDE")) 151 wf_max6690_create(adapter, addr, loc);
142 continue;
143 wf_max6690_create(adapter, addr);
144 } 152 }
145 153
146 return 0; 154 return 0;
diff --git a/drivers/macintosh/windfarm_pm121.c b/drivers/macintosh/windfarm_pm121.c
new file mode 100644
index 000000000000..66ec4fb115bb
--- /dev/null
+++ b/drivers/macintosh/windfarm_pm121.c
@@ -0,0 +1,1040 @@
1/*
2 * Windfarm PowerMac thermal control. iMac G5 iSight
3 *
4 * (c) Copyright 2007 Étienne Bersac <bersace@gmail.com>
5 *
6 * Bits & pieces from windfarm_pm81.c by (c) Copyright 2005 Benjamin
7 * Herrenschmidt, IBM Corp. <benh@kernel.crashing.org>
8 *
9 * Released under the term of the GNU GPL v2.
10 *
11 *
12 *
13 * PowerMac12,1
14 * ============
15 *
16 *
17 * The algorithm used is the PID control algorithm, used the same way
18 * the published Darwin code does, using the same values that are
19 * present in the Darwin 8.10 snapshot property lists (note however
20 * that none of the code has been re-used, it's a complete
21 * re-implementation
22 *
23 * There is two models using PowerMac12,1. Model 2 is iMac G5 iSight
24 * 17" while Model 3 is iMac G5 20". They do have both the same
25 * controls with a tiny difference. The control-ids of hard-drive-fan
26 * and cpu-fan is swapped.
27 *
28 *
29 * Target Correction :
30 *
31 * controls have a target correction calculated as :
32 *
33 * new_min = ((((average_power * slope) >> 16) + offset) >> 16) + min_value
34 * new_value = max(new_value, max(new_min, 0))
35 *
36 * OD Fan control correction.
37 *
38 * # model_id: 2
39 * offset : -19563152
40 * slope : 1956315
41 *
42 * # model_id: 3
43 * offset : -15650652
44 * slope : 1565065
45 *
46 * HD Fan control correction.
47 *
48 * # model_id: 2
49 * offset : -15650652
50 * slope : 1565065
51 *
52 * # model_id: 3
53 * offset : -19563152
54 * slope : 1956315
55 *
56 * CPU Fan control correction.
57 *
58 * # model_id: 2
59 * offset : -25431900
60 * slope : 2543190
61 *
62 * # model_id: 3
63 * offset : -15650652
64 * slope : 1565065
65 *
66 *
67 * Target rubber-banding :
68 *
69 * Some controls have a target correction which depends on another
70 * control value. The correction is computed in the following way :
71 *
72 * new_min = ref_value * slope + offset
73 *
74 * ref_value is the value of the reference control. If new_min is
75 * greater than 0, then we correct the target value using :
76 *
77 * new_target = max (new_target, new_min >> 16)
78 *
79 *
80 * # model_id : 2
81 * control : cpu-fan
82 * ref : optical-drive-fan
83 * offset : -15650652
84 * slope : 1565065
85 *
86 * # model_id : 3
87 * control : optical-drive-fan
88 * ref : hard-drive-fan
89 * offset : -32768000
90 * slope : 65536
91 *
92 *
93 * In order to have the moste efficient correction with those
94 * dependencies, we must trigger HD loop before OD loop before CPU
95 * loop.
96 *
97 *
98 * The various control loops found in Darwin config file are:
99 *
100 * HD Fan control loop.
101 *
102 * # model_id: 2
103 * control : hard-drive-fan
104 * sensor : hard-drive-temp
105 * PID params : G_d = 0x00000000
106 * G_p = 0x002D70A3
107 * G_r = 0x00019999
108 * History = 2 entries
109 * Input target = 0x370000
110 * Interval = 5s
111 *
112 * # model_id: 3
113 * control : hard-drive-fan
114 * sensor : hard-drive-temp
115 * PID params : G_d = 0x00000000
116 * G_p = 0x002170A3
117 * G_r = 0x00019999
118 * History = 2 entries
119 * Input target = 0x370000
120 * Interval = 5s
121 *
122 * OD Fan control loop.
123 *
124 * # model_id: 2
125 * control : optical-drive-fan
126 * sensor : optical-drive-temp
127 * PID params : G_d = 0x00000000
128 * G_p = 0x001FAE14
129 * G_r = 0x00019999
130 * History = 2 entries
131 * Input target = 0x320000
132 * Interval = 5s
133 *
134 * # model_id: 3
135 * control : optical-drive-fan
136 * sensor : optical-drive-temp
137 * PID params : G_d = 0x00000000
138 * G_p = 0x001FAE14
139 * G_r = 0x00019999
140 * History = 2 entries
141 * Input target = 0x320000
142 * Interval = 5s
143 *
144 * GPU Fan control loop.
145 *
146 * # model_id: 2
147 * control : hard-drive-fan
148 * sensor : gpu-temp
149 * PID params : G_d = 0x00000000
150 * G_p = 0x002A6666
151 * G_r = 0x00019999
152 * History = 2 entries
153 * Input target = 0x5A0000
154 * Interval = 5s
155 *
156 * # model_id: 3
157 * control : cpu-fan
158 * sensor : gpu-temp
159 * PID params : G_d = 0x00000000
160 * G_p = 0x0010CCCC
161 * G_r = 0x00019999
162 * History = 2 entries
163 * Input target = 0x500000
164 * Interval = 5s
165 *
166 * KODIAK (aka northbridge) Fan control loop.
167 *
168 * # model_id: 2
169 * control : optical-drive-fan
170 * sensor : north-bridge-temp
171 * PID params : G_d = 0x00000000
172 * G_p = 0x003BD70A
173 * G_r = 0x00019999
174 * History = 2 entries
175 * Input target = 0x550000
176 * Interval = 5s
177 *
178 * # model_id: 3
179 * control : hard-drive-fan
180 * sensor : north-bridge-temp
181 * PID params : G_d = 0x00000000
182 * G_p = 0x0030F5C2
183 * G_r = 0x00019999
184 * History = 2 entries
185 * Input target = 0x550000
186 * Interval = 5s
187 *
188 * CPU Fan control loop.
189 *
190 * control : cpu-fan
191 * sensors : cpu-temp, cpu-power
192 * PID params : from SDB partition
193 *
194 *
195 * CPU Slew control loop.
196 *
197 * control : cpufreq-clamp
198 * sensor : cpu-temp
199 *
200 */
201
202#undef DEBUG
203
204#include <linux/types.h>
205#include <linux/errno.h>
206#include <linux/kernel.h>
207#include <linux/delay.h>
208#include <linux/slab.h>
209#include <linux/init.h>
210#include <linux/spinlock.h>
211#include <linux/wait.h>
212#include <linux/kmod.h>
213#include <linux/device.h>
214#include <linux/platform_device.h>
215#include <asm/prom.h>
216#include <asm/machdep.h>
217#include <asm/io.h>
218#include <asm/system.h>
219#include <asm/sections.h>
220#include <asm/smu.h>
221
222#include "windfarm.h"
223#include "windfarm_pid.h"
224
225#define VERSION "0.3"
226
227static int pm121_mach_model; /* machine model id */
228
229/* Controls & sensors */
230static struct wf_sensor *sensor_cpu_power;
231static struct wf_sensor *sensor_cpu_temp;
232static struct wf_sensor *sensor_cpu_voltage;
233static struct wf_sensor *sensor_cpu_current;
234static struct wf_sensor *sensor_gpu_temp;
235static struct wf_sensor *sensor_north_bridge_temp;
236static struct wf_sensor *sensor_hard_drive_temp;
237static struct wf_sensor *sensor_optical_drive_temp;
238static struct wf_sensor *sensor_incoming_air_temp; /* unused ! */
239
240enum {
241 FAN_CPU,
242 FAN_HD,
243 FAN_OD,
244 CPUFREQ,
245 N_CONTROLS
246};
247static struct wf_control *controls[N_CONTROLS] = {};
248
249/* Set to kick the control loop into life */
250static int pm121_all_controls_ok, pm121_all_sensors_ok, pm121_started;
251
252enum {
253 FAILURE_FAN = 1 << 0,
254 FAILURE_SENSOR = 1 << 1,
255 FAILURE_OVERTEMP = 1 << 2
256};
257
258/* All sys loops. Note the HD before the OD loop in order to have it
259 run before. */
260enum {
261 LOOP_GPU, /* control = hd or cpu, but luckily,
262 it doesn't matter */
263 LOOP_HD, /* control = hd */
264 LOOP_KODIAK, /* control = hd or od */
265 LOOP_OD, /* control = od */
266 N_LOOPS
267};
268
269static const char *loop_names[N_LOOPS] = {
270 "GPU",
271 "HD",
272 "KODIAK",
273 "OD",
274};
275
276#define PM121_NUM_CONFIGS 2
277
278static unsigned int pm121_failure_state;
279static int pm121_readjust, pm121_skipping;
280static s32 average_power;
281
282struct pm121_correction {
283 int offset;
284 int slope;
285};
286
287static struct pm121_correction corrections[N_CONTROLS][PM121_NUM_CONFIGS] = {
288 /* FAN_OD */
289 {
290 /* MODEL 2 */
291 { .offset = -19563152,
292 .slope = 1956315
293 },
294 /* MODEL 3 */
295 { .offset = -15650652,
296 .slope = 1565065
297 },
298 },
299 /* FAN_HD */
300 {
301 /* MODEL 2 */
302 { .offset = -15650652,
303 .slope = 1565065
304 },
305 /* MODEL 3 */
306 { .offset = -19563152,
307 .slope = 1956315
308 },
309 },
310 /* FAN_CPU */
311 {
312 /* MODEL 2 */
313 { .offset = -25431900,
314 .slope = 2543190
315 },
316 /* MODEL 3 */
317 { .offset = -15650652,
318 .slope = 1565065
319 },
320 },
321 /* CPUFREQ has no correction (and is not implemented at all) */
322};
323
324struct pm121_connection {
325 unsigned int control_id;
326 unsigned int ref_id;
327 struct pm121_correction correction;
328};
329
330static struct pm121_connection pm121_connections[] = {
331 /* MODEL 2 */
332 { .control_id = FAN_CPU,
333 .ref_id = FAN_OD,
334 { .offset = -32768000,
335 .slope = 65536
336 }
337 },
338 /* MODEL 3 */
339 { .control_id = FAN_OD,
340 .ref_id = FAN_HD,
341 { .offset = -32768000,
342 .slope = 65536
343 }
344 },
345};
346
347/* pointer to the current model connection */
348static struct pm121_connection *pm121_connection;
349
350/*
351 * ****** System Fans Control Loop ******
352 *
353 */
354
355/* Since each loop handles only one control and we want to avoid
356 * writing virtual control, we store the control correction with the
357 * loop params. Some data are not set, there are common to all loop
358 * and thus, hardcoded.
359 */
360struct pm121_sys_param {
361 /* purely informative since we use mach_model-2 as index */
362 int model_id;
363 struct wf_sensor **sensor; /* use sensor_id instead ? */
364 s32 gp, itarget;
365 unsigned int control_id;
366};
367
368static struct pm121_sys_param
369pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] = {
370 /* GPU Fan control loop */
371 {
372 { .model_id = 2,
373 .sensor = &sensor_gpu_temp,
374 .gp = 0x002A6666,
375 .itarget = 0x5A0000,
376 .control_id = FAN_HD,
377 },
378 { .model_id = 3,
379 .sensor = &sensor_gpu_temp,
380 .gp = 0x0010CCCC,
381 .itarget = 0x500000,
382 .control_id = FAN_CPU,
383 },
384 },
385 /* HD Fan control loop */
386 {
387 { .model_id = 2,
388 .sensor = &sensor_hard_drive_temp,
389 .gp = 0x002D70A3,
390 .itarget = 0x370000,
391 .control_id = FAN_HD,
392 },
393 { .model_id = 3,
394 .sensor = &sensor_hard_drive_temp,
395 .gp = 0x002170A3,
396 .itarget = 0x370000,
397 .control_id = FAN_HD,
398 },
399 },
400 /* KODIAK Fan control loop */
401 {
402 { .model_id = 2,
403 .sensor = &sensor_north_bridge_temp,
404 .gp = 0x003BD70A,
405 .itarget = 0x550000,
406 .control_id = FAN_OD,
407 },
408 { .model_id = 3,
409 .sensor = &sensor_north_bridge_temp,
410 .gp = 0x0030F5C2,
411 .itarget = 0x550000,
412 .control_id = FAN_HD,
413 },
414 },
415 /* OD Fan control loop */
416 {
417 { .model_id = 2,
418 .sensor = &sensor_optical_drive_temp,
419 .gp = 0x001FAE14,
420 .itarget = 0x320000,
421 .control_id = FAN_OD,
422 },
423 { .model_id = 3,
424 .sensor = &sensor_optical_drive_temp,
425 .gp = 0x001FAE14,
426 .itarget = 0x320000,
427 .control_id = FAN_OD,
428 },
429 },
430};
431
432/* the hardcoded values */
433#define PM121_SYS_GD 0x00000000
434#define PM121_SYS_GR 0x00019999
435#define PM121_SYS_HISTORY_SIZE 2
436#define PM121_SYS_INTERVAL 5
437
438/* State data used by the system fans control loop
439 */
440struct pm121_sys_state {
441 int ticks;
442 s32 setpoint;
443 struct wf_pid_state pid;
444};
445
446struct pm121_sys_state *pm121_sys_state[N_LOOPS] = {};
447
448/*
449 * ****** CPU Fans Control Loop ******
450 *
451 */
452
453#define PM121_CPU_INTERVAL 1
454
455/* State data used by the cpu fans control loop
456 */
457struct pm121_cpu_state {
458 int ticks;
459 s32 setpoint;
460 struct wf_cpu_pid_state pid;
461};
462
463static struct pm121_cpu_state *pm121_cpu_state;
464
465
466
467/*
468 * ***** Implementation *****
469 *
470 */
471
472/* correction the value using the output-low-bound correction algo */
473static s32 pm121_correct(s32 new_setpoint,
474 unsigned int control_id,
475 s32 min)
476{
477 s32 new_min;
478 struct pm121_correction *correction;
479 correction = &corrections[control_id][pm121_mach_model - 2];
480
481 new_min = (average_power * correction->slope) >> 16;
482 new_min += correction->offset;
483 new_min = (new_min >> 16) + min;
484
485 return max(new_setpoint, max(new_min, 0));
486}
487
488static s32 pm121_connect(unsigned int control_id, s32 setpoint)
489{
490 s32 new_min, value, new_setpoint;
491
492 if (pm121_connection->control_id == control_id) {
493 controls[control_id]->ops->get_value(controls[control_id],
494 &value);
495 new_min = value * pm121_connection->correction.slope;
496 new_min += pm121_connection->correction.offset;
497 if (new_min > 0) {
498 new_setpoint = max(setpoint, (new_min >> 16));
499 if (new_setpoint != setpoint) {
500 pr_debug("pm121: %s depending on %s, "
501 "corrected from %d to %d RPM\n",
502 controls[control_id]->name,
503 controls[pm121_connection->ref_id]->name,
504 (int) setpoint, (int) new_setpoint);
505 }
506 } else
507 new_setpoint = setpoint;
508 }
509 /* no connection */
510 else
511 new_setpoint = setpoint;
512
513 return new_setpoint;
514}
515
516/* FAN LOOPS */
517static void pm121_create_sys_fans(int loop_id)
518{
519 struct pm121_sys_param *param = NULL;
520 struct wf_pid_param pid_param;
521 struct wf_control *control = NULL;
522 int i;
523
524 /* First, locate the params for this model */
525 for (i = 0; i < PM121_NUM_CONFIGS; i++) {
526 if (pm121_sys_all_params[loop_id][i].model_id == pm121_mach_model) {
527 param = &(pm121_sys_all_params[loop_id][i]);
528 break;
529 }
530 }
531
532 /* No params found, put fans to max */
533 if (param == NULL) {
534 printk(KERN_WARNING "pm121: %s fan config not found "
535 " for this machine model\n",
536 loop_names[loop_id]);
537 goto fail;
538 }
539
540 control = controls[param->control_id];
541
542 /* Alloc & initialize state */
543 pm121_sys_state[loop_id] = kmalloc(sizeof(struct pm121_sys_state),
544 GFP_KERNEL);
545 if (pm121_sys_state[loop_id] == NULL) {
546 printk(KERN_WARNING "pm121: Memory allocation error\n");
547 goto fail;
548 }
549 pm121_sys_state[loop_id]->ticks = 1;
550
551 /* Fill PID params */
552 pid_param.gd = PM121_SYS_GD;
553 pid_param.gp = param->gp;
554 pid_param.gr = PM121_SYS_GR;
555 pid_param.interval = PM121_SYS_INTERVAL;
556 pid_param.history_len = PM121_SYS_HISTORY_SIZE;
557 pid_param.itarget = param->itarget;
558 pid_param.min = control->ops->get_min(control);
559 pid_param.max = control->ops->get_max(control);
560
561 wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param);
562
563 pr_debug("pm121: %s Fan control loop initialized.\n"
564 " itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
565 loop_names[loop_id], FIX32TOPRINT(pid_param.itarget),
566 pid_param.min, pid_param.max);
567 return;
568
569 fail:
570 /* note that this is not optimal since another loop may still
571 control the same control */
572 printk(KERN_WARNING "pm121: failed to set up %s loop "
573 "setting \"%s\" to max speed.\n",
574 loop_names[loop_id], control->name);
575
576 if (control)
577 wf_control_set_max(control);
578}
579
580static void pm121_sys_fans_tick(int loop_id)
581{
582 struct pm121_sys_param *param;
583 struct pm121_sys_state *st;
584 struct wf_sensor *sensor;
585 struct wf_control *control;
586 s32 temp, new_setpoint;
587 int rc;
588
589 param = &(pm121_sys_all_params[loop_id][pm121_mach_model-2]);
590 st = pm121_sys_state[loop_id];
591 sensor = *(param->sensor);
592 control = controls[param->control_id];
593
594 if (--st->ticks != 0) {
595 if (pm121_readjust)
596 goto readjust;
597 return;
598 }
599 st->ticks = PM121_SYS_INTERVAL;
600
601 rc = sensor->ops->get_value(sensor, &temp);
602 if (rc) {
603 printk(KERN_WARNING "windfarm: %s sensor error %d\n",
604 sensor->name, rc);
605 pm121_failure_state |= FAILURE_SENSOR;
606 return;
607 }
608
609 pr_debug("pm121: %s Fan tick ! %s: %d.%03d\n",
610 loop_names[loop_id], sensor->name,
611 FIX32TOPRINT(temp));
612
613 new_setpoint = wf_pid_run(&st->pid, temp);
614
615 /* correction */
616 new_setpoint = pm121_correct(new_setpoint,
617 param->control_id,
618 st->pid.param.min);
619 /* linked corretion */
620 new_setpoint = pm121_connect(param->control_id, new_setpoint);
621
622 if (new_setpoint == st->setpoint)
623 return;
624 st->setpoint = new_setpoint;
625 pr_debug("pm121: %s corrected setpoint: %d RPM\n",
626 control->name, (int)new_setpoint);
627 readjust:
628 if (control && pm121_failure_state == 0) {
629 rc = control->ops->set_value(control, st->setpoint);
630 if (rc) {
631 printk(KERN_WARNING "windfarm: %s fan error %d\n",
632 control->name, rc);
633 pm121_failure_state |= FAILURE_FAN;
634 }
635 }
636}
637
638
639/* CPU LOOP */
640static void pm121_create_cpu_fans(void)
641{
642 struct wf_cpu_pid_param pid_param;
643 const struct smu_sdbp_header *hdr;
644 struct smu_sdbp_cpupiddata *piddata;
645 struct smu_sdbp_fvt *fvt;
646 struct wf_control *fan_cpu;
647 s32 tmax, tdelta, maxpow, powadj;
648
649 fan_cpu = controls[FAN_CPU];
650
651 /* First, locate the PID params in SMU SBD */
652 hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
653 if (hdr == 0) {
654 printk(KERN_WARNING "pm121: CPU PID fan config not found.\n");
655 goto fail;
656 }
657 piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
658
659 /* Get the FVT params for operating point 0 (the only supported one
660 * for now) in order to get tmax
661 */
662 hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
663 if (hdr) {
664 fvt = (struct smu_sdbp_fvt *)&hdr[1];
665 tmax = ((s32)fvt->maxtemp) << 16;
666 } else
667 tmax = 0x5e0000; /* 94 degree default */
668
669 /* Alloc & initialize state */
670 pm121_cpu_state = kmalloc(sizeof(struct pm121_cpu_state),
671 GFP_KERNEL);
672 if (pm121_cpu_state == NULL)
673 goto fail;
674 pm121_cpu_state->ticks = 1;
675
676 /* Fill PID params */
677 pid_param.interval = PM121_CPU_INTERVAL;
678 pid_param.history_len = piddata->history_len;
679 if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
680 printk(KERN_WARNING "pm121: History size overflow on "
681 "CPU control loop (%d)\n", piddata->history_len);
682 pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
683 }
684 pid_param.gd = piddata->gd;
685 pid_param.gp = piddata->gp;
686 pid_param.gr = piddata->gr / pid_param.history_len;
687
688 tdelta = ((s32)piddata->target_temp_delta) << 16;
689 maxpow = ((s32)piddata->max_power) << 16;
690 powadj = ((s32)piddata->power_adj) << 16;
691
692 pid_param.tmax = tmax;
693 pid_param.ttarget = tmax - tdelta;
694 pid_param.pmaxadj = maxpow - powadj;
695
696 pid_param.min = fan_cpu->ops->get_min(fan_cpu);
697 pid_param.max = fan_cpu->ops->get_max(fan_cpu);
698
699 wf_cpu_pid_init(&pm121_cpu_state->pid, &pid_param);
700
701 pr_debug("pm121: CPU Fan control initialized.\n");
702 pr_debug(" ttarged=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM,\n",
703 FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
704 pid_param.min, pid_param.max);
705
706 return;
707
708 fail:
709 printk(KERN_WARNING "pm121: CPU fan config not found, max fan speed\n");
710
711 if (controls[CPUFREQ])
712 wf_control_set_max(controls[CPUFREQ]);
713 if (fan_cpu)
714 wf_control_set_max(fan_cpu);
715}
716
717
718static void pm121_cpu_fans_tick(struct pm121_cpu_state *st)
719{
720 s32 new_setpoint, temp, power;
721 struct wf_control *fan_cpu = NULL;
722 int rc;
723
724 if (--st->ticks != 0) {
725 if (pm121_readjust)
726 goto readjust;
727 return;
728 }
729 st->ticks = PM121_CPU_INTERVAL;
730
731 fan_cpu = controls[FAN_CPU];
732
733 rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);
734 if (rc) {
735 printk(KERN_WARNING "pm121: CPU temp sensor error %d\n",
736 rc);
737 pm121_failure_state |= FAILURE_SENSOR;
738 return;
739 }
740
741 rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);
742 if (rc) {
743 printk(KERN_WARNING "pm121: CPU power sensor error %d\n",
744 rc);
745 pm121_failure_state |= FAILURE_SENSOR;
746 return;
747 }
748
749 pr_debug("pm121: CPU Fans tick ! CPU temp: %d.%03d°C, power: %d.%03d\n",
750 FIX32TOPRINT(temp), FIX32TOPRINT(power));
751
752 if (temp > st->pid.param.tmax)
753 pm121_failure_state |= FAILURE_OVERTEMP;
754
755 new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
756
757 /* correction */
758 new_setpoint = pm121_correct(new_setpoint,
759 FAN_CPU,
760 st->pid.param.min);
761
762 /* connected correction */
763 new_setpoint = pm121_connect(FAN_CPU, new_setpoint);
764
765 if (st->setpoint == new_setpoint)
766 return;
767 st->setpoint = new_setpoint;
768 pr_debug("pm121: CPU corrected setpoint: %d RPM\n", (int)new_setpoint);
769
770 readjust:
771 if (fan_cpu && pm121_failure_state == 0) {
772 rc = fan_cpu->ops->set_value(fan_cpu, st->setpoint);
773 if (rc) {
774 printk(KERN_WARNING "pm121: %s fan error %d\n",
775 fan_cpu->name, rc);
776 pm121_failure_state |= FAILURE_FAN;
777 }
778 }
779}
780
781/*
782 * ****** Common ******
783 *
784 */
785
786static void pm121_tick(void)
787{
788 unsigned int last_failure = pm121_failure_state;
789 unsigned int new_failure;
790 s32 total_power;
791 int i;
792
793 if (!pm121_started) {
794 pr_debug("pm121: creating control loops !\n");
795 for (i = 0; i < N_LOOPS; i++)
796 pm121_create_sys_fans(i);
797
798 pm121_create_cpu_fans();
799 pm121_started = 1;
800 }
801
802 /* skipping ticks */
803 if (pm121_skipping && --pm121_skipping)
804 return;
805
806 /* compute average power */
807 total_power = 0;
808 for (i = 0; i < pm121_cpu_state->pid.param.history_len; i++)
809 total_power += pm121_cpu_state->pid.powers[i];
810
811 average_power = total_power / pm121_cpu_state->pid.param.history_len;
812
813
814 pm121_failure_state = 0;
815 for (i = 0 ; i < N_LOOPS; i++) {
816 if (pm121_sys_state[i])
817 pm121_sys_fans_tick(i);
818 }
819
820 if (pm121_cpu_state)
821 pm121_cpu_fans_tick(pm121_cpu_state);
822
823 pm121_readjust = 0;
824 new_failure = pm121_failure_state & ~last_failure;
825
826 /* If entering failure mode, clamp cpufreq and ramp all
827 * fans to full speed.
828 */
829 if (pm121_failure_state && !last_failure) {
830 for (i = 0; i < N_CONTROLS; i++) {
831 if (controls[i])
832 wf_control_set_max(controls[i]);
833 }
834 }
835
836 /* If leaving failure mode, unclamp cpufreq and readjust
837 * all fans on next iteration
838 */
839 if (!pm121_failure_state && last_failure) {
840 if (controls[CPUFREQ])
841 wf_control_set_min(controls[CPUFREQ]);
842 pm121_readjust = 1;
843 }
844
845 /* Overtemp condition detected, notify and start skipping a couple
846 * ticks to let the temperature go down
847 */
848 if (new_failure & FAILURE_OVERTEMP) {
849 wf_set_overtemp();
850 pm121_skipping = 2;
851 }
852
853 /* We only clear the overtemp condition if overtemp is cleared
854 * _and_ no other failure is present. Since a sensor error will
855 * clear the overtemp condition (can't measure temperature) at
856 * the control loop levels, but we don't want to keep it clear
857 * here in this case
858 */
859 if (new_failure == 0 && last_failure & FAILURE_OVERTEMP)
860 wf_clear_overtemp();
861}
862
863
864static struct wf_control* pm121_register_control(struct wf_control *ct,
865 const char *match,
866 unsigned int id)
867{
868 if (controls[id] == NULL && !strcmp(ct->name, match)) {
869 if (wf_get_control(ct) == 0)
870 controls[id] = ct;
871 }
872 return controls[id];
873}
874
875static void pm121_new_control(struct wf_control *ct)
876{
877 int all = 1;
878
879 if (pm121_all_controls_ok)
880 return;
881
882 all = pm121_register_control(ct, "optical-drive-fan", FAN_OD) && all;
883 all = pm121_register_control(ct, "hard-drive-fan", FAN_HD) && all;
884 all = pm121_register_control(ct, "cpu-fan", FAN_CPU) && all;
885 all = pm121_register_control(ct, "cpufreq-clamp", CPUFREQ) && all;
886
887 if (all)
888 pm121_all_controls_ok = 1;
889}
890
891
892
893
894static struct wf_sensor* pm121_register_sensor(struct wf_sensor *sensor,
895 const char *match,
896 struct wf_sensor **var)
897{
898 if (*var == NULL && !strcmp(sensor->name, match)) {
899 if (wf_get_sensor(sensor) == 0)
900 *var = sensor;
901 }
902 return *var;
903}
904
905static void pm121_new_sensor(struct wf_sensor *sr)
906{
907 int all = 1;
908
909 if (pm121_all_sensors_ok)
910 return;
911
912 all = pm121_register_sensor(sr, "cpu-temp",
913 &sensor_cpu_temp) && all;
914 all = pm121_register_sensor(sr, "cpu-current",
915 &sensor_cpu_current) && all;
916 all = pm121_register_sensor(sr, "cpu-voltage",
917 &sensor_cpu_voltage) && all;
918 all = pm121_register_sensor(sr, "cpu-power",
919 &sensor_cpu_power) && all;
920 all = pm121_register_sensor(sr, "hard-drive-temp",
921 &sensor_hard_drive_temp) && all;
922 all = pm121_register_sensor(sr, "optical-drive-temp",
923 &sensor_optical_drive_temp) && all;
924 all = pm121_register_sensor(sr, "incoming-air-temp",
925 &sensor_incoming_air_temp) && all;
926 all = pm121_register_sensor(sr, "north-bridge-temp",
927 &sensor_north_bridge_temp) && all;
928 all = pm121_register_sensor(sr, "gpu-temp",
929 &sensor_gpu_temp) && all;
930
931 if (all)
932 pm121_all_sensors_ok = 1;
933}
934
935
936
937static int pm121_notify(struct notifier_block *self,
938 unsigned long event, void *data)
939{
940 switch (event) {
941 case WF_EVENT_NEW_CONTROL:
942 pr_debug("pm121: new control %s detected\n",
943 ((struct wf_control *)data)->name);
944 pm121_new_control(data);
945 break;
946 case WF_EVENT_NEW_SENSOR:
947 pr_debug("pm121: new sensor %s detected\n",
948 ((struct wf_sensor *)data)->name);
949 pm121_new_sensor(data);
950 break;
951 case WF_EVENT_TICK:
952 if (pm121_all_controls_ok && pm121_all_sensors_ok)
953 pm121_tick();
954 break;
955 }
956
957 return 0;
958}
959
960static struct notifier_block pm121_events = {
961 .notifier_call = pm121_notify,
962};
963
964static int pm121_init_pm(void)
965{
966 const struct smu_sdbp_header *hdr;
967
968 hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);
969 if (hdr != 0) {
970 struct smu_sdbp_sensortree *st =
971 (struct smu_sdbp_sensortree *)&hdr[1];
972 pm121_mach_model = st->model_id;
973 }
974
975 pm121_connection = &pm121_connections[pm121_mach_model - 2];
976
977 printk(KERN_INFO "pm121: Initializing for iMac G5 iSight model ID %d\n",
978 pm121_mach_model);
979
980 return 0;
981}
982
983
984static int pm121_probe(struct platform_device *ddev)
985{
986 wf_register_client(&pm121_events);
987
988 return 0;
989}
990
991static int __devexit pm121_remove(struct platform_device *ddev)
992{
993 wf_unregister_client(&pm121_events);
994 return 0;
995}
996
997static struct platform_driver pm121_driver = {
998 .probe = pm121_probe,
999 .remove = __devexit_p(pm121_remove),
1000 .driver = {
1001 .name = "windfarm",
1002 .bus = &platform_bus_type,
1003 },
1004};
1005
1006
1007static int __init pm121_init(void)
1008{
1009 int rc = -ENODEV;
1010
1011 if (machine_is_compatible("PowerMac12,1"))
1012 rc = pm121_init_pm();
1013
1014 if (rc == 0) {
1015 request_module("windfarm_smu_controls");
1016 request_module("windfarm_smu_sensors");
1017 request_module("windfarm_smu_sat");
1018 request_module("windfarm_lm75_sensor");
1019 request_module("windfarm_max6690_sensor");
1020 request_module("windfarm_cpufreq_clamp");
1021 platform_driver_register(&pm121_driver);
1022 }
1023
1024 return rc;
1025}
1026
1027static void __exit pm121_exit(void)
1028{
1029
1030 platform_driver_unregister(&pm121_driver);
1031}
1032
1033
1034module_init(pm121_init);
1035module_exit(pm121_exit);
1036
1037MODULE_AUTHOR("Étienne Bersac <bersace@gmail.com>");
1038MODULE_DESCRIPTION("Thermal control logic for iMac G5 (iSight)");
1039MODULE_LICENSE("GPL");
1040
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c
index 58c2590f05ec..961fa0e7c2cf 100644
--- a/drivers/macintosh/windfarm_smu_controls.c
+++ b/drivers/macintosh/windfarm_smu_controls.c
@@ -218,6 +218,10 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node,
218 fct->ctrl.name = "cpu-fan"; 218 fct->ctrl.name = "cpu-fan";
219 else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive")) 219 else if (!strcmp(l, "Hard Drive") || !strcmp(l, "Hard drive"))
220 fct->ctrl.name = "drive-bay-fan"; 220 fct->ctrl.name = "drive-bay-fan";
221 else if (!strcmp(l, "HDD Fan")) /* seen on iMac G5 iSight */
222 fct->ctrl.name = "hard-drive-fan";
223 else if (!strcmp(l, "ODD Fan")) /* same */
224 fct->ctrl.name = "optical-drive-fan";
221 225
222 /* Unrecognized fan, bail out */ 226 /* Unrecognized fan, bail out */
223 if (fct->ctrl.name == NULL) 227 if (fct->ctrl.name == NULL)