xref: /openbmc/linux/drivers/macintosh/windfarm_pm112.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
1ddceed9dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ac171c46SBenjamin Herrenschmidt /*
3ac171c46SBenjamin Herrenschmidt  * Windfarm PowerMac thermal control.
4ac171c46SBenjamin Herrenschmidt  * Control loops for machines with SMU and PPC970MP processors.
5ac171c46SBenjamin Herrenschmidt  *
6ac171c46SBenjamin Herrenschmidt  * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org>
7ac171c46SBenjamin Herrenschmidt  * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp.
8ac171c46SBenjamin Herrenschmidt  */
9ac171c46SBenjamin Herrenschmidt #include <linux/types.h>
10ac171c46SBenjamin Herrenschmidt #include <linux/errno.h>
11ac171c46SBenjamin Herrenschmidt #include <linux/kernel.h>
12ac171c46SBenjamin Herrenschmidt #include <linux/device.h>
13ac171c46SBenjamin Herrenschmidt #include <linux/platform_device.h>
14ac171c46SBenjamin Herrenschmidt #include <linux/reboot.h>
15*a486e512SChristophe Leroy #include <linux/of.h>
16*a486e512SChristophe Leroy #include <linux/slab.h>
17*a486e512SChristophe Leroy 
18ac171c46SBenjamin Herrenschmidt #include <asm/smu.h>
19ac171c46SBenjamin Herrenschmidt 
20ac171c46SBenjamin Herrenschmidt #include "windfarm.h"
21ac171c46SBenjamin Herrenschmidt #include "windfarm_pid.h"
22ac171c46SBenjamin Herrenschmidt 
23ac171c46SBenjamin Herrenschmidt #define VERSION "0.2"
24ac171c46SBenjamin Herrenschmidt 
25ac171c46SBenjamin Herrenschmidt #define DEBUG
26ac171c46SBenjamin Herrenschmidt #undef LOTSA_DEBUG
27ac171c46SBenjamin Herrenschmidt 
28ac171c46SBenjamin Herrenschmidt #ifdef DEBUG
29ac171c46SBenjamin Herrenschmidt #define DBG(args...)	printk(args)
30ac171c46SBenjamin Herrenschmidt #else
31ac171c46SBenjamin Herrenschmidt #define DBG(args...)	do { } while(0)
32ac171c46SBenjamin Herrenschmidt #endif
33ac171c46SBenjamin Herrenschmidt 
34ac171c46SBenjamin Herrenschmidt #ifdef LOTSA_DEBUG
35ac171c46SBenjamin Herrenschmidt #define DBG_LOTS(args...)	printk(args)
36ac171c46SBenjamin Herrenschmidt #else
37ac171c46SBenjamin Herrenschmidt #define DBG_LOTS(args...)	do { } while(0)
38ac171c46SBenjamin Herrenschmidt #endif
39ac171c46SBenjamin Herrenschmidt 
40ac171c46SBenjamin Herrenschmidt /* define this to force CPU overtemp to 60 degree, useful for testing
41ac171c46SBenjamin Herrenschmidt  * the overtemp code
42ac171c46SBenjamin Herrenschmidt  */
43ac171c46SBenjamin Herrenschmidt #undef HACKED_OVERTEMP
44ac171c46SBenjamin Herrenschmidt 
45ac171c46SBenjamin Herrenschmidt /* We currently only handle 2 chips, 4 cores... */
46ac171c46SBenjamin Herrenschmidt #define NR_CHIPS	2
47ac171c46SBenjamin Herrenschmidt #define NR_CORES	4
48ac171c46SBenjamin Herrenschmidt #define NR_CPU_FANS	3 * NR_CHIPS
49ac171c46SBenjamin Herrenschmidt 
50ac171c46SBenjamin Herrenschmidt /* Controls and sensors */
51ac171c46SBenjamin Herrenschmidt static struct wf_sensor *sens_cpu_temp[NR_CORES];
52ac171c46SBenjamin Herrenschmidt static struct wf_sensor *sens_cpu_power[NR_CORES];
53ac171c46SBenjamin Herrenschmidt static struct wf_sensor *hd_temp;
54ac171c46SBenjamin Herrenschmidt static struct wf_sensor *slots_power;
55ac171c46SBenjamin Herrenschmidt static struct wf_sensor *u4_temp;
56ac171c46SBenjamin Herrenschmidt 
57ac171c46SBenjamin Herrenschmidt static struct wf_control *cpu_fans[NR_CPU_FANS];
58ac171c46SBenjamin Herrenschmidt static char *cpu_fan_names[NR_CPU_FANS] = {
59ac171c46SBenjamin Herrenschmidt 	"cpu-rear-fan-0",
60ac171c46SBenjamin Herrenschmidt 	"cpu-rear-fan-1",
61ac171c46SBenjamin Herrenschmidt 	"cpu-front-fan-0",
62ac171c46SBenjamin Herrenschmidt 	"cpu-front-fan-1",
63ac171c46SBenjamin Herrenschmidt 	"cpu-pump-0",
64ac171c46SBenjamin Herrenschmidt 	"cpu-pump-1",
65ac171c46SBenjamin Herrenschmidt };
66ac171c46SBenjamin Herrenschmidt static struct wf_control *cpufreq_clamp;
67ac171c46SBenjamin Herrenschmidt 
68ac171c46SBenjamin Herrenschmidt /* Second pump isn't required (and isn't actually present) */
69ac171c46SBenjamin Herrenschmidt #define CPU_FANS_REQD		(NR_CPU_FANS - 2)
70ac171c46SBenjamin Herrenschmidt #define FIRST_PUMP		4
71ac171c46SBenjamin Herrenschmidt #define LAST_PUMP		5
72ac171c46SBenjamin Herrenschmidt 
73ac171c46SBenjamin Herrenschmidt /* We keep a temperature history for average calculation of 180s */
74ac171c46SBenjamin Herrenschmidt #define CPU_TEMP_HIST_SIZE	180
75ac171c46SBenjamin Herrenschmidt 
76ac171c46SBenjamin Herrenschmidt /* Scale factor for fan speed, *100 */
77ac171c46SBenjamin Herrenschmidt static int cpu_fan_scale[NR_CPU_FANS] = {
78ac171c46SBenjamin Herrenschmidt 	100,
79ac171c46SBenjamin Herrenschmidt 	100,
80ac171c46SBenjamin Herrenschmidt 	97,		/* inlet fans run at 97% of exhaust fan */
81ac171c46SBenjamin Herrenschmidt 	97,
82ac171c46SBenjamin Herrenschmidt 	100,		/* updated later */
83ac171c46SBenjamin Herrenschmidt 	100,		/* updated later */
84ac171c46SBenjamin Herrenschmidt };
85ac171c46SBenjamin Herrenschmidt 
86ac171c46SBenjamin Herrenschmidt static struct wf_control *backside_fan;
87ac171c46SBenjamin Herrenschmidt static struct wf_control *slots_fan;
88ac171c46SBenjamin Herrenschmidt static struct wf_control *drive_bay_fan;
89ac171c46SBenjamin Herrenschmidt 
90ac171c46SBenjamin Herrenschmidt /* PID loop state */
91ac171c46SBenjamin Herrenschmidt static struct wf_cpu_pid_state cpu_pid[NR_CORES];
92ac171c46SBenjamin Herrenschmidt static u32 cpu_thist[CPU_TEMP_HIST_SIZE];
93ac171c46SBenjamin Herrenschmidt static int cpu_thist_pt;
94ac171c46SBenjamin Herrenschmidt static s64 cpu_thist_total;
95ac171c46SBenjamin Herrenschmidt static s32 cpu_all_tmax = 100 << 16;
96ac171c46SBenjamin Herrenschmidt static int cpu_last_target;
97ac171c46SBenjamin Herrenschmidt static struct wf_pid_state backside_pid;
98ac171c46SBenjamin Herrenschmidt static int backside_tick;
99ac171c46SBenjamin Herrenschmidt static struct wf_pid_state slots_pid;
1004f256d56SGustavo A. R. Silva static bool slots_started;
101ac171c46SBenjamin Herrenschmidt static struct wf_pid_state drive_bay_pid;
102ac171c46SBenjamin Herrenschmidt static int drive_bay_tick;
103ac171c46SBenjamin Herrenschmidt 
104ac171c46SBenjamin Herrenschmidt static int nr_cores;
105ac171c46SBenjamin Herrenschmidt static int have_all_controls;
106ac171c46SBenjamin Herrenschmidt static int have_all_sensors;
1074f256d56SGustavo A. R. Silva static bool started;
108ac171c46SBenjamin Herrenschmidt 
109ac171c46SBenjamin Herrenschmidt static int failure_state;
110ac171c46SBenjamin Herrenschmidt #define FAILURE_SENSOR		1
111ac171c46SBenjamin Herrenschmidt #define FAILURE_FAN		2
112ac171c46SBenjamin Herrenschmidt #define FAILURE_PERM		4
113ac171c46SBenjamin Herrenschmidt #define FAILURE_LOW_OVERTEMP	8
114ac171c46SBenjamin Herrenschmidt #define FAILURE_HIGH_OVERTEMP	16
115ac171c46SBenjamin Herrenschmidt 
116ac171c46SBenjamin Herrenschmidt /* Overtemp values */
117ac171c46SBenjamin Herrenschmidt #define LOW_OVER_AVERAGE	0
118ac171c46SBenjamin Herrenschmidt #define LOW_OVER_IMMEDIATE	(10 << 16)
119ac171c46SBenjamin Herrenschmidt #define LOW_OVER_CLEAR		((-10) << 16)
120ac171c46SBenjamin Herrenschmidt #define HIGH_OVER_IMMEDIATE	(14 << 16)
121ac171c46SBenjamin Herrenschmidt #define HIGH_OVER_AVERAGE	(10 << 16)
122ac171c46SBenjamin Herrenschmidt #define HIGH_OVER_IMMEDIATE	(14 << 16)
123ac171c46SBenjamin Herrenschmidt 
124ac171c46SBenjamin Herrenschmidt 
125ac171c46SBenjamin Herrenschmidt /* Implementation... */
create_cpu_loop(int cpu)126ac171c46SBenjamin Herrenschmidt static int create_cpu_loop(int cpu)
127ac171c46SBenjamin Herrenschmidt {
128ac171c46SBenjamin Herrenschmidt 	int chip = cpu / 2;
129ac171c46SBenjamin Herrenschmidt 	int core = cpu & 1;
130ac171c46SBenjamin Herrenschmidt 	struct smu_sdbp_header *hdr;
131ac171c46SBenjamin Herrenschmidt 	struct smu_sdbp_cpupiddata *piddata;
132ac171c46SBenjamin Herrenschmidt 	struct wf_cpu_pid_param pid;
133ac171c46SBenjamin Herrenschmidt 	struct wf_control *main_fan = cpu_fans[0];
134ac171c46SBenjamin Herrenschmidt 	s32 tmax;
135ac171c46SBenjamin Herrenschmidt 	int fmin;
136ac171c46SBenjamin Herrenschmidt 
137ac171c46SBenjamin Herrenschmidt 	/* Get FVT params to get Tmax; if not found, assume default */
138ac171c46SBenjamin Herrenschmidt 	hdr = smu_sat_get_sdb_partition(chip, 0xC4 + core, NULL);
139ac171c46SBenjamin Herrenschmidt 	if (hdr) {
140ac171c46SBenjamin Herrenschmidt 		struct smu_sdbp_fvt *fvt = (struct smu_sdbp_fvt *)&hdr[1];
141ac171c46SBenjamin Herrenschmidt 		tmax = fvt->maxtemp << 16;
142ac171c46SBenjamin Herrenschmidt 	} else
143ac171c46SBenjamin Herrenschmidt 		tmax = 95 << 16;	/* default to 95 degrees C */
144ac171c46SBenjamin Herrenschmidt 
145ac171c46SBenjamin Herrenschmidt 	/* We keep a global tmax for overtemp calculations */
146ac171c46SBenjamin Herrenschmidt 	if (tmax < cpu_all_tmax)
147ac171c46SBenjamin Herrenschmidt 		cpu_all_tmax = tmax;
148ac171c46SBenjamin Herrenschmidt 
14993900337SMichael Ellerman 	kfree(hdr);
15093900337SMichael Ellerman 
15193900337SMichael Ellerman 	/* Get PID params from the appropriate SAT */
15293900337SMichael Ellerman 	hdr = smu_sat_get_sdb_partition(chip, 0xC8 + core, NULL);
15393900337SMichael Ellerman 	if (hdr == NULL) {
15493900337SMichael Ellerman 		printk(KERN_WARNING"windfarm: can't get CPU PID fan config\n");
15593900337SMichael Ellerman 		return -EINVAL;
15693900337SMichael Ellerman 	}
15793900337SMichael Ellerman 	piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
15893900337SMichael Ellerman 
159ac171c46SBenjamin Herrenschmidt 	/*
160ac171c46SBenjamin Herrenschmidt 	 * Darwin has a minimum fan speed of 1000 rpm for the 4-way and
161ac171c46SBenjamin Herrenschmidt 	 * 515 for the 2-way.  That appears to be overkill, so for now,
162ac171c46SBenjamin Herrenschmidt 	 * impose a minimum of 750 or 515.
163ac171c46SBenjamin Herrenschmidt 	 */
164ac171c46SBenjamin Herrenschmidt 	fmin = (nr_cores > 2) ? 750 : 515;
165ac171c46SBenjamin Herrenschmidt 
166ac171c46SBenjamin Herrenschmidt 	/* Initialize PID loop */
167ac171c46SBenjamin Herrenschmidt 	pid.interval = 1;	/* seconds */
168ac171c46SBenjamin Herrenschmidt 	pid.history_len = piddata->history_len;
169ac171c46SBenjamin Herrenschmidt 	pid.gd = piddata->gd;
170ac171c46SBenjamin Herrenschmidt 	pid.gp = piddata->gp;
171ac171c46SBenjamin Herrenschmidt 	pid.gr = piddata->gr / piddata->history_len;
172ac171c46SBenjamin Herrenschmidt 	pid.pmaxadj = (piddata->max_power << 16) - (piddata->power_adj << 8);
173ac171c46SBenjamin Herrenschmidt 	pid.ttarget = tmax - (piddata->target_temp_delta << 16);
174ac171c46SBenjamin Herrenschmidt 	pid.tmax = tmax;
175ac171c46SBenjamin Herrenschmidt 	pid.min = main_fan->ops->get_min(main_fan);
176ac171c46SBenjamin Herrenschmidt 	pid.max = main_fan->ops->get_max(main_fan);
177ac171c46SBenjamin Herrenschmidt 	if (pid.min < fmin)
178ac171c46SBenjamin Herrenschmidt 		pid.min = fmin;
179ac171c46SBenjamin Herrenschmidt 
180ac171c46SBenjamin Herrenschmidt 	wf_cpu_pid_init(&cpu_pid[cpu], &pid);
18193900337SMichael Ellerman 
18293900337SMichael Ellerman 	kfree(hdr);
18393900337SMichael Ellerman 
184ac171c46SBenjamin Herrenschmidt 	return 0;
185ac171c46SBenjamin Herrenschmidt }
186ac171c46SBenjamin Herrenschmidt 
cpu_max_all_fans(void)187ac171c46SBenjamin Herrenschmidt static void cpu_max_all_fans(void)
188ac171c46SBenjamin Herrenschmidt {
189ac171c46SBenjamin Herrenschmidt 	int i;
190ac171c46SBenjamin Herrenschmidt 
191ac171c46SBenjamin Herrenschmidt 	/* We max all CPU fans in case of a sensor error. We also do the
192ac171c46SBenjamin Herrenschmidt 	 * cpufreq clamping now, even if it's supposedly done later by the
193ac171c46SBenjamin Herrenschmidt 	 * generic code anyway, we do it earlier here to react faster
194ac171c46SBenjamin Herrenschmidt 	 */
195ac171c46SBenjamin Herrenschmidt 	if (cpufreq_clamp)
196ac171c46SBenjamin Herrenschmidt 		wf_control_set_max(cpufreq_clamp);
197ac171c46SBenjamin Herrenschmidt 	for (i = 0; i < NR_CPU_FANS; ++i)
198ac171c46SBenjamin Herrenschmidt 		if (cpu_fans[i])
199ac171c46SBenjamin Herrenschmidt 			wf_control_set_max(cpu_fans[i]);
200ac171c46SBenjamin Herrenschmidt }
201ac171c46SBenjamin Herrenschmidt 
cpu_check_overtemp(s32 temp)202ac171c46SBenjamin Herrenschmidt static int cpu_check_overtemp(s32 temp)
203ac171c46SBenjamin Herrenschmidt {
204ac171c46SBenjamin Herrenschmidt 	int new_state = 0;
205ac171c46SBenjamin Herrenschmidt 	s32 t_avg, t_old;
206ac171c46SBenjamin Herrenschmidt 
207ac171c46SBenjamin Herrenschmidt 	/* First check for immediate overtemps */
208ac171c46SBenjamin Herrenschmidt 	if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) {
209ac171c46SBenjamin Herrenschmidt 		new_state |= FAILURE_LOW_OVERTEMP;
210ac171c46SBenjamin Herrenschmidt 		if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
211ac171c46SBenjamin Herrenschmidt 			printk(KERN_ERR "windfarm: Overtemp due to immediate CPU"
212ac171c46SBenjamin Herrenschmidt 			       " temperature !\n");
213ac171c46SBenjamin Herrenschmidt 	}
214ac171c46SBenjamin Herrenschmidt 	if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) {
215ac171c46SBenjamin Herrenschmidt 		new_state |= FAILURE_HIGH_OVERTEMP;
216ac171c46SBenjamin Herrenschmidt 		if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
217ac171c46SBenjamin Herrenschmidt 			printk(KERN_ERR "windfarm: Critical overtemp due to"
218ac171c46SBenjamin Herrenschmidt 			       " immediate CPU temperature !\n");
219ac171c46SBenjamin Herrenschmidt 	}
220ac171c46SBenjamin Herrenschmidt 
221ac171c46SBenjamin Herrenschmidt 	/* We calculate a history of max temperatures and use that for the
222ac171c46SBenjamin Herrenschmidt 	 * overtemp management
223ac171c46SBenjamin Herrenschmidt 	 */
224ac171c46SBenjamin Herrenschmidt 	t_old = cpu_thist[cpu_thist_pt];
225ac171c46SBenjamin Herrenschmidt 	cpu_thist[cpu_thist_pt] = temp;
226ac171c46SBenjamin Herrenschmidt 	cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE;
227ac171c46SBenjamin Herrenschmidt 	cpu_thist_total -= t_old;
228ac171c46SBenjamin Herrenschmidt 	cpu_thist_total += temp;
229ac171c46SBenjamin Herrenschmidt 	t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE;
230ac171c46SBenjamin Herrenschmidt 
231ac171c46SBenjamin Herrenschmidt 	DBG_LOTS("t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n",
232ac171c46SBenjamin Herrenschmidt 		 FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp));
233ac171c46SBenjamin Herrenschmidt 
234ac171c46SBenjamin Herrenschmidt 	/* Now check for average overtemps */
235ac171c46SBenjamin Herrenschmidt 	if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) {
236ac171c46SBenjamin Herrenschmidt 		new_state |= FAILURE_LOW_OVERTEMP;
237ac171c46SBenjamin Herrenschmidt 		if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)
238ac171c46SBenjamin Herrenschmidt 			printk(KERN_ERR "windfarm: Overtemp due to average CPU"
239ac171c46SBenjamin Herrenschmidt 			       " temperature !\n");
240ac171c46SBenjamin Herrenschmidt 	}
241ac171c46SBenjamin Herrenschmidt 	if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) {
242ac171c46SBenjamin Herrenschmidt 		new_state |= FAILURE_HIGH_OVERTEMP;
243ac171c46SBenjamin Herrenschmidt 		if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)
244ac171c46SBenjamin Herrenschmidt 			printk(KERN_ERR "windfarm: Critical overtemp due to"
245ac171c46SBenjamin Herrenschmidt 			       " average CPU temperature !\n");
246ac171c46SBenjamin Herrenschmidt 	}
247ac171c46SBenjamin Herrenschmidt 
248ac171c46SBenjamin Herrenschmidt 	/* Now handle overtemp conditions. We don't currently use the windfarm
249ac171c46SBenjamin Herrenschmidt 	 * overtemp handling core as it's not fully suited to the needs of those
250ac171c46SBenjamin Herrenschmidt 	 * new machine. This will be fixed later.
251ac171c46SBenjamin Herrenschmidt 	 */
252ac171c46SBenjamin Herrenschmidt 	if (new_state) {
253ac171c46SBenjamin Herrenschmidt 		/* High overtemp -> immediate shutdown */
254ac171c46SBenjamin Herrenschmidt 		if (new_state & FAILURE_HIGH_OVERTEMP)
255ac171c46SBenjamin Herrenschmidt 			machine_power_off();
256ac171c46SBenjamin Herrenschmidt 		if ((failure_state & new_state) != new_state)
257ac171c46SBenjamin Herrenschmidt 			cpu_max_all_fans();
258ac171c46SBenjamin Herrenschmidt 		failure_state |= new_state;
259ac171c46SBenjamin Herrenschmidt 	} else if ((failure_state & FAILURE_LOW_OVERTEMP) &&
260ac171c46SBenjamin Herrenschmidt 		   (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) {
261ac171c46SBenjamin Herrenschmidt 		printk(KERN_ERR "windfarm: Overtemp condition cleared !\n");
262ac171c46SBenjamin Herrenschmidt 		failure_state &= ~FAILURE_LOW_OVERTEMP;
263ac171c46SBenjamin Herrenschmidt 	}
264ac171c46SBenjamin Herrenschmidt 
265ac171c46SBenjamin Herrenschmidt 	return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP);
266ac171c46SBenjamin Herrenschmidt }
267ac171c46SBenjamin Herrenschmidt 
cpu_fans_tick(void)268ac171c46SBenjamin Herrenschmidt static void cpu_fans_tick(void)
269ac171c46SBenjamin Herrenschmidt {
270ac171c46SBenjamin Herrenschmidt 	int err, cpu;
271ac171c46SBenjamin Herrenschmidt 	s32 greatest_delta = 0;
272ac171c46SBenjamin Herrenschmidt 	s32 temp, power, t_max = 0;
273ac171c46SBenjamin Herrenschmidt 	int i, t, target = 0;
274ac171c46SBenjamin Herrenschmidt 	struct wf_sensor *sr;
275ac171c46SBenjamin Herrenschmidt 	struct wf_control *ct;
276ac171c46SBenjamin Herrenschmidt 	struct wf_cpu_pid_state *sp;
277ac171c46SBenjamin Herrenschmidt 
278ac171c46SBenjamin Herrenschmidt 	DBG_LOTS(KERN_DEBUG);
279ac171c46SBenjamin Herrenschmidt 	for (cpu = 0; cpu < nr_cores; ++cpu) {
280ac171c46SBenjamin Herrenschmidt 		/* Get CPU core temperature */
281ac171c46SBenjamin Herrenschmidt 		sr = sens_cpu_temp[cpu];
282ac171c46SBenjamin Herrenschmidt 		err = sr->ops->get_value(sr, &temp);
283ac171c46SBenjamin Herrenschmidt 		if (err) {
284ac171c46SBenjamin Herrenschmidt 			DBG("\n");
285ac171c46SBenjamin Herrenschmidt 			printk(KERN_WARNING "windfarm: CPU %d temperature "
286ac171c46SBenjamin Herrenschmidt 			       "sensor error %d\n", cpu, err);
287ac171c46SBenjamin Herrenschmidt 			failure_state |= FAILURE_SENSOR;
288ac171c46SBenjamin Herrenschmidt 			cpu_max_all_fans();
289ac171c46SBenjamin Herrenschmidt 			return;
290ac171c46SBenjamin Herrenschmidt 		}
291ac171c46SBenjamin Herrenschmidt 
292ac171c46SBenjamin Herrenschmidt 		/* Keep track of highest temp */
293ac171c46SBenjamin Herrenschmidt 		t_max = max(t_max, temp);
294ac171c46SBenjamin Herrenschmidt 
295ac171c46SBenjamin Herrenschmidt 		/* Get CPU power */
296ac171c46SBenjamin Herrenschmidt 		sr = sens_cpu_power[cpu];
297ac171c46SBenjamin Herrenschmidt 		err = sr->ops->get_value(sr, &power);
298ac171c46SBenjamin Herrenschmidt 		if (err) {
299ac171c46SBenjamin Herrenschmidt 			DBG("\n");
300ac171c46SBenjamin Herrenschmidt 			printk(KERN_WARNING "windfarm: CPU %d power "
301ac171c46SBenjamin Herrenschmidt 			       "sensor error %d\n", cpu, err);
302ac171c46SBenjamin Herrenschmidt 			failure_state |= FAILURE_SENSOR;
303ac171c46SBenjamin Herrenschmidt 			cpu_max_all_fans();
304ac171c46SBenjamin Herrenschmidt 			return;
305ac171c46SBenjamin Herrenschmidt 		}
306ac171c46SBenjamin Herrenschmidt 
307ac171c46SBenjamin Herrenschmidt 		/* Run PID */
308ac171c46SBenjamin Herrenschmidt 		sp = &cpu_pid[cpu];
309ac171c46SBenjamin Herrenschmidt 		t = wf_cpu_pid_run(sp, power, temp);
310ac171c46SBenjamin Herrenschmidt 
311ac171c46SBenjamin Herrenschmidt 		if (cpu == 0 || sp->last_delta > greatest_delta) {
312ac171c46SBenjamin Herrenschmidt 			greatest_delta = sp->last_delta;
313ac171c46SBenjamin Herrenschmidt 			target = t;
314ac171c46SBenjamin Herrenschmidt 		}
315ac171c46SBenjamin Herrenschmidt 		DBG_LOTS("[%d] P=%d.%.3d T=%d.%.3d ",
316ac171c46SBenjamin Herrenschmidt 		    cpu, FIX32TOPRINT(power), FIX32TOPRINT(temp));
317ac171c46SBenjamin Herrenschmidt 	}
318ac171c46SBenjamin Herrenschmidt 	DBG_LOTS("fans = %d, t_max = %d.%03d\n", target, FIX32TOPRINT(t_max));
319ac171c46SBenjamin Herrenschmidt 
320ac171c46SBenjamin Herrenschmidt 	/* Darwin limits decrease to 20 per iteration */
321ac171c46SBenjamin Herrenschmidt 	if (target < (cpu_last_target - 20))
322ac171c46SBenjamin Herrenschmidt 		target = cpu_last_target - 20;
323ac171c46SBenjamin Herrenschmidt 	cpu_last_target = target;
324ac171c46SBenjamin Herrenschmidt 	for (cpu = 0; cpu < nr_cores; ++cpu)
325ac171c46SBenjamin Herrenschmidt 		cpu_pid[cpu].target = target;
326ac171c46SBenjamin Herrenschmidt 
327ac171c46SBenjamin Herrenschmidt 	/* Handle possible overtemps */
328ac171c46SBenjamin Herrenschmidt 	if (cpu_check_overtemp(t_max))
329ac171c46SBenjamin Herrenschmidt 		return;
330ac171c46SBenjamin Herrenschmidt 
331ac171c46SBenjamin Herrenschmidt 	/* Set fans */
332ac171c46SBenjamin Herrenschmidt 	for (i = 0; i < NR_CPU_FANS; ++i) {
333ac171c46SBenjamin Herrenschmidt 		ct = cpu_fans[i];
334ac171c46SBenjamin Herrenschmidt 		if (ct == NULL)
335ac171c46SBenjamin Herrenschmidt 			continue;
336ac171c46SBenjamin Herrenschmidt 		err = ct->ops->set_value(ct, target * cpu_fan_scale[i] / 100);
337ac171c46SBenjamin Herrenschmidt 		if (err) {
338ac171c46SBenjamin Herrenschmidt 			printk(KERN_WARNING "windfarm: fan %s reports "
339ac171c46SBenjamin Herrenschmidt 			       "error %d\n", ct->name, err);
340ac171c46SBenjamin Herrenschmidt 			failure_state |= FAILURE_FAN;
341ac171c46SBenjamin Herrenschmidt 			break;
342ac171c46SBenjamin Herrenschmidt 		}
343ac171c46SBenjamin Herrenschmidt 	}
344ac171c46SBenjamin Herrenschmidt }
345ac171c46SBenjamin Herrenschmidt 
346ac171c46SBenjamin Herrenschmidt /* Backside/U4 fan */
347ac171c46SBenjamin Herrenschmidt static struct wf_pid_param backside_param = {
348ac171c46SBenjamin Herrenschmidt 	.interval	= 5,
349ac171c46SBenjamin Herrenschmidt 	.history_len	= 2,
350ac171c46SBenjamin Herrenschmidt 	.gd		= 48 << 20,
351ac171c46SBenjamin Herrenschmidt 	.gp		= 5 << 20,
352ac171c46SBenjamin Herrenschmidt 	.gr		= 0,
353ac171c46SBenjamin Herrenschmidt 	.itarget	= 64 << 16,
354ac171c46SBenjamin Herrenschmidt 	.additive	= 1,
355ac171c46SBenjamin Herrenschmidt };
356ac171c46SBenjamin Herrenschmidt 
backside_fan_tick(void)357ac171c46SBenjamin Herrenschmidt static void backside_fan_tick(void)
358ac171c46SBenjamin Herrenschmidt {
359ac171c46SBenjamin Herrenschmidt 	s32 temp;
360ac171c46SBenjamin Herrenschmidt 	int speed;
361ac171c46SBenjamin Herrenschmidt 	int err;
362ac171c46SBenjamin Herrenschmidt 
363ac171c46SBenjamin Herrenschmidt 	if (!backside_fan || !u4_temp)
364ac171c46SBenjamin Herrenschmidt 		return;
365ac171c46SBenjamin Herrenschmidt 	if (!backside_tick) {
366ac171c46SBenjamin Herrenschmidt 		/* first time; initialize things */
367e2a002b9SBenjamin Herrenschmidt 		printk(KERN_INFO "windfarm: Backside control loop started.\n");
368ac171c46SBenjamin Herrenschmidt 		backside_param.min = backside_fan->ops->get_min(backside_fan);
369ac171c46SBenjamin Herrenschmidt 		backside_param.max = backside_fan->ops->get_max(backside_fan);
370ac171c46SBenjamin Herrenschmidt 		wf_pid_init(&backside_pid, &backside_param);
371ac171c46SBenjamin Herrenschmidt 		backside_tick = 1;
372ac171c46SBenjamin Herrenschmidt 	}
373ac171c46SBenjamin Herrenschmidt 	if (--backside_tick > 0)
374ac171c46SBenjamin Herrenschmidt 		return;
375ac171c46SBenjamin Herrenschmidt 	backside_tick = backside_pid.param.interval;
376ac171c46SBenjamin Herrenschmidt 
377ac171c46SBenjamin Herrenschmidt 	err = u4_temp->ops->get_value(u4_temp, &temp);
378ac171c46SBenjamin Herrenschmidt 	if (err) {
379ac171c46SBenjamin Herrenschmidt 		printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n",
380ac171c46SBenjamin Herrenschmidt 		       err);
381ac171c46SBenjamin Herrenschmidt 		failure_state |= FAILURE_SENSOR;
382ac171c46SBenjamin Herrenschmidt 		wf_control_set_max(backside_fan);
383ac171c46SBenjamin Herrenschmidt 		return;
384ac171c46SBenjamin Herrenschmidt 	}
385ac171c46SBenjamin Herrenschmidt 	speed = wf_pid_run(&backside_pid, temp);
386ac171c46SBenjamin Herrenschmidt 	DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n",
387ac171c46SBenjamin Herrenschmidt 		 FIX32TOPRINT(temp), speed);
388ac171c46SBenjamin Herrenschmidt 
389ac171c46SBenjamin Herrenschmidt 	err = backside_fan->ops->set_value(backside_fan, speed);
390ac171c46SBenjamin Herrenschmidt 	if (err) {
391ac171c46SBenjamin Herrenschmidt 		printk(KERN_WARNING "windfarm: backside fan error %d\n", err);
392ac171c46SBenjamin Herrenschmidt 		failure_state |= FAILURE_FAN;
393ac171c46SBenjamin Herrenschmidt 	}
394ac171c46SBenjamin Herrenschmidt }
395ac171c46SBenjamin Herrenschmidt 
396ac171c46SBenjamin Herrenschmidt /* Drive bay fan */
397ac171c46SBenjamin Herrenschmidt static struct wf_pid_param drive_bay_prm = {
398ac171c46SBenjamin Herrenschmidt 	.interval	= 5,
399ac171c46SBenjamin Herrenschmidt 	.history_len	= 2,
400ac171c46SBenjamin Herrenschmidt 	.gd		= 30 << 20,
401ac171c46SBenjamin Herrenschmidt 	.gp		= 5 << 20,
402ac171c46SBenjamin Herrenschmidt 	.gr		= 0,
403ac171c46SBenjamin Herrenschmidt 	.itarget	= 40 << 16,
404ac171c46SBenjamin Herrenschmidt 	.additive	= 1,
405ac171c46SBenjamin Herrenschmidt };
406ac171c46SBenjamin Herrenschmidt 
drive_bay_fan_tick(void)407ac171c46SBenjamin Herrenschmidt static void drive_bay_fan_tick(void)
408ac171c46SBenjamin Herrenschmidt {
409ac171c46SBenjamin Herrenschmidt 	s32 temp;
410ac171c46SBenjamin Herrenschmidt 	int speed;
411ac171c46SBenjamin Herrenschmidt 	int err;
412ac171c46SBenjamin Herrenschmidt 
413ac171c46SBenjamin Herrenschmidt 	if (!drive_bay_fan || !hd_temp)
414ac171c46SBenjamin Herrenschmidt 		return;
415ac171c46SBenjamin Herrenschmidt 	if (!drive_bay_tick) {
416ac171c46SBenjamin Herrenschmidt 		/* first time; initialize things */
417e2a002b9SBenjamin Herrenschmidt 		printk(KERN_INFO "windfarm: Drive bay control loop started.\n");
418ac171c46SBenjamin Herrenschmidt 		drive_bay_prm.min = drive_bay_fan->ops->get_min(drive_bay_fan);
419ac171c46SBenjamin Herrenschmidt 		drive_bay_prm.max = drive_bay_fan->ops->get_max(drive_bay_fan);
420ac171c46SBenjamin Herrenschmidt 		wf_pid_init(&drive_bay_pid, &drive_bay_prm);
421ac171c46SBenjamin Herrenschmidt 		drive_bay_tick = 1;
422ac171c46SBenjamin Herrenschmidt 	}
423ac171c46SBenjamin Herrenschmidt 	if (--drive_bay_tick > 0)
424ac171c46SBenjamin Herrenschmidt 		return;
425ac171c46SBenjamin Herrenschmidt 	drive_bay_tick = drive_bay_pid.param.interval;
426ac171c46SBenjamin Herrenschmidt 
427ac171c46SBenjamin Herrenschmidt 	err = hd_temp->ops->get_value(hd_temp, &temp);
428ac171c46SBenjamin Herrenschmidt 	if (err) {
429ac171c46SBenjamin Herrenschmidt 		printk(KERN_WARNING "windfarm: drive bay temp sensor "
430ac171c46SBenjamin Herrenschmidt 		       "error %d\n", err);
431ac171c46SBenjamin Herrenschmidt 		failure_state |= FAILURE_SENSOR;
432ac171c46SBenjamin Herrenschmidt 		wf_control_set_max(drive_bay_fan);
433ac171c46SBenjamin Herrenschmidt 		return;
434ac171c46SBenjamin Herrenschmidt 	}
435ac171c46SBenjamin Herrenschmidt 	speed = wf_pid_run(&drive_bay_pid, temp);
436ac171c46SBenjamin Herrenschmidt 	DBG_LOTS("drive_bay PID temp=%d.%.3d speed=%d\n",
437ac171c46SBenjamin Herrenschmidt 		 FIX32TOPRINT(temp), speed);
438ac171c46SBenjamin Herrenschmidt 
439ac171c46SBenjamin Herrenschmidt 	err = drive_bay_fan->ops->set_value(drive_bay_fan, speed);
440ac171c46SBenjamin Herrenschmidt 	if (err) {
441ac171c46SBenjamin Herrenschmidt 		printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err);
442ac171c46SBenjamin Herrenschmidt 		failure_state |= FAILURE_FAN;
443ac171c46SBenjamin Herrenschmidt 	}
444ac171c46SBenjamin Herrenschmidt }
445ac171c46SBenjamin Herrenschmidt 
446ac171c46SBenjamin Herrenschmidt /* PCI slots area fan */
447ac171c46SBenjamin Herrenschmidt /* This makes the fan speed proportional to the power consumed */
448ac171c46SBenjamin Herrenschmidt static struct wf_pid_param slots_param = {
449ac171c46SBenjamin Herrenschmidt 	.interval	= 1,
450ac171c46SBenjamin Herrenschmidt 	.history_len	= 2,
451ac171c46SBenjamin Herrenschmidt 	.gd		= 0,
452ac171c46SBenjamin Herrenschmidt 	.gp		= 0,
453ac171c46SBenjamin Herrenschmidt 	.gr		= 0x1277952,
454ac171c46SBenjamin Herrenschmidt 	.itarget	= 0,
455ac171c46SBenjamin Herrenschmidt 	.min		= 1560,
456ac171c46SBenjamin Herrenschmidt 	.max		= 3510,
457ac171c46SBenjamin Herrenschmidt };
458ac171c46SBenjamin Herrenschmidt 
slots_fan_tick(void)459ac171c46SBenjamin Herrenschmidt static void slots_fan_tick(void)
460ac171c46SBenjamin Herrenschmidt {
461ac171c46SBenjamin Herrenschmidt 	s32 power;
462ac171c46SBenjamin Herrenschmidt 	int speed;
463ac171c46SBenjamin Herrenschmidt 	int err;
464ac171c46SBenjamin Herrenschmidt 
465ac171c46SBenjamin Herrenschmidt 	if (!slots_fan || !slots_power)
466ac171c46SBenjamin Herrenschmidt 		return;
467ac171c46SBenjamin Herrenschmidt 	if (!slots_started) {
468ac171c46SBenjamin Herrenschmidt 		/* first time; initialize things */
469e2a002b9SBenjamin Herrenschmidt 		printk(KERN_INFO "windfarm: Slots control loop started.\n");
470ac171c46SBenjamin Herrenschmidt 		wf_pid_init(&slots_pid, &slots_param);
4714f256d56SGustavo A. R. Silva 		slots_started = true;
472ac171c46SBenjamin Herrenschmidt 	}
473ac171c46SBenjamin Herrenschmidt 
474ac171c46SBenjamin Herrenschmidt 	err = slots_power->ops->get_value(slots_power, &power);
475ac171c46SBenjamin Herrenschmidt 	if (err) {
476ac171c46SBenjamin Herrenschmidt 		printk(KERN_WARNING "windfarm: slots power sensor error %d\n",
477ac171c46SBenjamin Herrenschmidt 		       err);
478ac171c46SBenjamin Herrenschmidt 		failure_state |= FAILURE_SENSOR;
479ac171c46SBenjamin Herrenschmidt 		wf_control_set_max(slots_fan);
480ac171c46SBenjamin Herrenschmidt 		return;
481ac171c46SBenjamin Herrenschmidt 	}
482ac171c46SBenjamin Herrenschmidt 	speed = wf_pid_run(&slots_pid, power);
483ac171c46SBenjamin Herrenschmidt 	DBG_LOTS("slots PID power=%d.%.3d speed=%d\n",
484ac171c46SBenjamin Herrenschmidt 		 FIX32TOPRINT(power), speed);
485ac171c46SBenjamin Herrenschmidt 
486ac171c46SBenjamin Herrenschmidt 	err = slots_fan->ops->set_value(slots_fan, speed);
487ac171c46SBenjamin Herrenschmidt 	if (err) {
488ac171c46SBenjamin Herrenschmidt 		printk(KERN_WARNING "windfarm: slots fan error %d\n", err);
489ac171c46SBenjamin Herrenschmidt 		failure_state |= FAILURE_FAN;
490ac171c46SBenjamin Herrenschmidt 	}
491ac171c46SBenjamin Herrenschmidt }
492ac171c46SBenjamin Herrenschmidt 
set_fail_state(void)493ac171c46SBenjamin Herrenschmidt static void set_fail_state(void)
494ac171c46SBenjamin Herrenschmidt {
495ac171c46SBenjamin Herrenschmidt 	int i;
496ac171c46SBenjamin Herrenschmidt 
497ac171c46SBenjamin Herrenschmidt 	if (cpufreq_clamp)
498ac171c46SBenjamin Herrenschmidt 		wf_control_set_max(cpufreq_clamp);
499ac171c46SBenjamin Herrenschmidt 	for (i = 0; i < NR_CPU_FANS; ++i)
500ac171c46SBenjamin Herrenschmidt 		if (cpu_fans[i])
501ac171c46SBenjamin Herrenschmidt 			wf_control_set_max(cpu_fans[i]);
502ac171c46SBenjamin Herrenschmidt 	if (backside_fan)
503ac171c46SBenjamin Herrenschmidt 		wf_control_set_max(backside_fan);
504ac171c46SBenjamin Herrenschmidt 	if (slots_fan)
505ac171c46SBenjamin Herrenschmidt 		wf_control_set_max(slots_fan);
506ac171c46SBenjamin Herrenschmidt 	if (drive_bay_fan)
507ac171c46SBenjamin Herrenschmidt 		wf_control_set_max(drive_bay_fan);
508ac171c46SBenjamin Herrenschmidt }
509ac171c46SBenjamin Herrenschmidt 
pm112_tick(void)510ac171c46SBenjamin Herrenschmidt static void pm112_tick(void)
511ac171c46SBenjamin Herrenschmidt {
512ac171c46SBenjamin Herrenschmidt 	int i, last_failure;
513ac171c46SBenjamin Herrenschmidt 
514ac171c46SBenjamin Herrenschmidt 	if (!started) {
5154f256d56SGustavo A. R. Silva 		started = true;
516e2a002b9SBenjamin Herrenschmidt 		printk(KERN_INFO "windfarm: CPUs control loops started.\n");
517ac171c46SBenjamin Herrenschmidt 		for (i = 0; i < nr_cores; ++i) {
518ac171c46SBenjamin Herrenschmidt 			if (create_cpu_loop(i) < 0) {
519ac171c46SBenjamin Herrenschmidt 				failure_state = FAILURE_PERM;
520ac171c46SBenjamin Herrenschmidt 				set_fail_state();
521ac171c46SBenjamin Herrenschmidt 				break;
522ac171c46SBenjamin Herrenschmidt 			}
523ac171c46SBenjamin Herrenschmidt 		}
524ac171c46SBenjamin Herrenschmidt 		DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax));
525ac171c46SBenjamin Herrenschmidt 
526ac171c46SBenjamin Herrenschmidt #ifdef HACKED_OVERTEMP
527ac171c46SBenjamin Herrenschmidt 		cpu_all_tmax = 60 << 16;
528ac171c46SBenjamin Herrenschmidt #endif
529ac171c46SBenjamin Herrenschmidt 	}
530ac171c46SBenjamin Herrenschmidt 
531ac171c46SBenjamin Herrenschmidt 	/* Permanent failure, bail out */
532ac171c46SBenjamin Herrenschmidt 	if (failure_state & FAILURE_PERM)
533ac171c46SBenjamin Herrenschmidt 		return;
534ac171c46SBenjamin Herrenschmidt 	/* Clear all failure bits except low overtemp which will be eventually
535ac171c46SBenjamin Herrenschmidt 	 * cleared by the control loop itself
536ac171c46SBenjamin Herrenschmidt 	 */
537ac171c46SBenjamin Herrenschmidt 	last_failure = failure_state;
538ac171c46SBenjamin Herrenschmidt 	failure_state &= FAILURE_LOW_OVERTEMP;
539ac171c46SBenjamin Herrenschmidt 	cpu_fans_tick();
540ac171c46SBenjamin Herrenschmidt 	backside_fan_tick();
541ac171c46SBenjamin Herrenschmidt 	slots_fan_tick();
542ac171c46SBenjamin Herrenschmidt 	drive_bay_fan_tick();
543ac171c46SBenjamin Herrenschmidt 
544ac171c46SBenjamin Herrenschmidt 	DBG_LOTS("last_failure: 0x%x, failure_state: %x\n",
545ac171c46SBenjamin Herrenschmidt 		 last_failure, failure_state);
546ac171c46SBenjamin Herrenschmidt 
547ac171c46SBenjamin Herrenschmidt 	/* Check for failures. Any failure causes cpufreq clamping */
548ac171c46SBenjamin Herrenschmidt 	if (failure_state && last_failure == 0 && cpufreq_clamp)
549ac171c46SBenjamin Herrenschmidt 		wf_control_set_max(cpufreq_clamp);
550ac171c46SBenjamin Herrenschmidt 	if (failure_state == 0 && last_failure && cpufreq_clamp)
551ac171c46SBenjamin Herrenschmidt 		wf_control_set_min(cpufreq_clamp);
552ac171c46SBenjamin Herrenschmidt 
553ac171c46SBenjamin Herrenschmidt 	/* That's it for now, we might want to deal with other failures
554ac171c46SBenjamin Herrenschmidt 	 * differently in the future though
555ac171c46SBenjamin Herrenschmidt 	 */
556ac171c46SBenjamin Herrenschmidt }
557ac171c46SBenjamin Herrenschmidt 
pm112_new_control(struct wf_control * ct)558ac171c46SBenjamin Herrenschmidt static void pm112_new_control(struct wf_control *ct)
559ac171c46SBenjamin Herrenschmidt {
560ac171c46SBenjamin Herrenschmidt 	int i, max_exhaust;
561ac171c46SBenjamin Herrenschmidt 
562ac171c46SBenjamin Herrenschmidt 	if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {
563ac171c46SBenjamin Herrenschmidt 		if (wf_get_control(ct) == 0)
564ac171c46SBenjamin Herrenschmidt 			cpufreq_clamp = ct;
565ac171c46SBenjamin Herrenschmidt 	}
566ac171c46SBenjamin Herrenschmidt 
567ac171c46SBenjamin Herrenschmidt 	for (i = 0; i < NR_CPU_FANS; ++i) {
568ac171c46SBenjamin Herrenschmidt 		if (!strcmp(ct->name, cpu_fan_names[i])) {
569ac171c46SBenjamin Herrenschmidt 			if (cpu_fans[i] == NULL && wf_get_control(ct) == 0)
570ac171c46SBenjamin Herrenschmidt 				cpu_fans[i] = ct;
571ac171c46SBenjamin Herrenschmidt 			break;
572ac171c46SBenjamin Herrenschmidt 		}
573ac171c46SBenjamin Herrenschmidt 	}
574ac171c46SBenjamin Herrenschmidt 	if (i >= NR_CPU_FANS) {
575ac171c46SBenjamin Herrenschmidt 		/* not a CPU fan, try the others */
576ac171c46SBenjamin Herrenschmidt 		if (!strcmp(ct->name, "backside-fan")) {
577ac171c46SBenjamin Herrenschmidt 			if (backside_fan == NULL && wf_get_control(ct) == 0)
578ac171c46SBenjamin Herrenschmidt 				backside_fan = ct;
579ac171c46SBenjamin Herrenschmidt 		} else if (!strcmp(ct->name, "slots-fan")) {
580ac171c46SBenjamin Herrenschmidt 			if (slots_fan == NULL && wf_get_control(ct) == 0)
581ac171c46SBenjamin Herrenschmidt 				slots_fan = ct;
582ac171c46SBenjamin Herrenschmidt 		} else if (!strcmp(ct->name, "drive-bay-fan")) {
583ac171c46SBenjamin Herrenschmidt 			if (drive_bay_fan == NULL && wf_get_control(ct) == 0)
584ac171c46SBenjamin Herrenschmidt 				drive_bay_fan = ct;
585ac171c46SBenjamin Herrenschmidt 		}
586ac171c46SBenjamin Herrenschmidt 		return;
587ac171c46SBenjamin Herrenschmidt 	}
588ac171c46SBenjamin Herrenschmidt 
589ac171c46SBenjamin Herrenschmidt 	for (i = 0; i < CPU_FANS_REQD; ++i)
590ac171c46SBenjamin Herrenschmidt 		if (cpu_fans[i] == NULL)
591ac171c46SBenjamin Herrenschmidt 			return;
592ac171c46SBenjamin Herrenschmidt 
593ac171c46SBenjamin Herrenschmidt 	/* work out pump scaling factors */
594ac171c46SBenjamin Herrenschmidt 	max_exhaust = cpu_fans[0]->ops->get_max(cpu_fans[0]);
595ac171c46SBenjamin Herrenschmidt 	for (i = FIRST_PUMP; i <= LAST_PUMP; ++i)
596ac171c46SBenjamin Herrenschmidt 		if ((ct = cpu_fans[i]) != NULL)
597ac171c46SBenjamin Herrenschmidt 			cpu_fan_scale[i] =
598ac171c46SBenjamin Herrenschmidt 				ct->ops->get_max(ct) * 100 / max_exhaust;
599ac171c46SBenjamin Herrenschmidt 
600ac171c46SBenjamin Herrenschmidt 	have_all_controls = 1;
601ac171c46SBenjamin Herrenschmidt }
602ac171c46SBenjamin Herrenschmidt 
pm112_new_sensor(struct wf_sensor * sr)603ac171c46SBenjamin Herrenschmidt static void pm112_new_sensor(struct wf_sensor *sr)
604ac171c46SBenjamin Herrenschmidt {
605ac171c46SBenjamin Herrenschmidt 	unsigned int i;
606ac171c46SBenjamin Herrenschmidt 
607ac171c46SBenjamin Herrenschmidt 	if (!strncmp(sr->name, "cpu-temp-", 9)) {
608ac171c46SBenjamin Herrenschmidt 		i = sr->name[9] - '0';
609ac171c46SBenjamin Herrenschmidt 		if (sr->name[10] == 0 && i < NR_CORES &&
610ac171c46SBenjamin Herrenschmidt 		    sens_cpu_temp[i] == NULL && wf_get_sensor(sr) == 0)
611ac171c46SBenjamin Herrenschmidt 			sens_cpu_temp[i] = sr;
612ac171c46SBenjamin Herrenschmidt 
613ac171c46SBenjamin Herrenschmidt 	} else if (!strncmp(sr->name, "cpu-power-", 10)) {
614ac171c46SBenjamin Herrenschmidt 		i = sr->name[10] - '0';
615ac171c46SBenjamin Herrenschmidt 		if (sr->name[11] == 0 && i < NR_CORES &&
616ac171c46SBenjamin Herrenschmidt 		    sens_cpu_power[i] == NULL && wf_get_sensor(sr) == 0)
617ac171c46SBenjamin Herrenschmidt 			sens_cpu_power[i] = sr;
618ac171c46SBenjamin Herrenschmidt 	} else if (!strcmp(sr->name, "hd-temp")) {
619ac171c46SBenjamin Herrenschmidt 		if (hd_temp == NULL && wf_get_sensor(sr) == 0)
620ac171c46SBenjamin Herrenschmidt 			hd_temp = sr;
621ac171c46SBenjamin Herrenschmidt 	} else if (!strcmp(sr->name, "slots-power")) {
622ac171c46SBenjamin Herrenschmidt 		if (slots_power == NULL && wf_get_sensor(sr) == 0)
623ac171c46SBenjamin Herrenschmidt 			slots_power = sr;
624b55fafc5SBenjamin Herrenschmidt 	} else if (!strcmp(sr->name, "backside-temp")) {
625ac171c46SBenjamin Herrenschmidt 		if (u4_temp == NULL && wf_get_sensor(sr) == 0)
626ac171c46SBenjamin Herrenschmidt 			u4_temp = sr;
627ac171c46SBenjamin Herrenschmidt 	} else
628ac171c46SBenjamin Herrenschmidt 		return;
629ac171c46SBenjamin Herrenschmidt 
630ac171c46SBenjamin Herrenschmidt 	/* check if we have all the sensors we need */
631ac171c46SBenjamin Herrenschmidt 	for (i = 0; i < nr_cores; ++i)
632ac171c46SBenjamin Herrenschmidt 		if (sens_cpu_temp[i] == NULL || sens_cpu_power[i] == NULL)
633ac171c46SBenjamin Herrenschmidt 			return;
634ac171c46SBenjamin Herrenschmidt 
635ac171c46SBenjamin Herrenschmidt 	have_all_sensors = 1;
636ac171c46SBenjamin Herrenschmidt }
637ac171c46SBenjamin Herrenschmidt 
pm112_wf_notify(struct notifier_block * self,unsigned long event,void * data)638ac171c46SBenjamin Herrenschmidt static int pm112_wf_notify(struct notifier_block *self,
639ac171c46SBenjamin Herrenschmidt 			   unsigned long event, void *data)
640ac171c46SBenjamin Herrenschmidt {
641ac171c46SBenjamin Herrenschmidt 	switch (event) {
642ac171c46SBenjamin Herrenschmidt 	case WF_EVENT_NEW_SENSOR:
643ac171c46SBenjamin Herrenschmidt 		pm112_new_sensor(data);
644ac171c46SBenjamin Herrenschmidt 		break;
645ac171c46SBenjamin Herrenschmidt 	case WF_EVENT_NEW_CONTROL:
646ac171c46SBenjamin Herrenschmidt 		pm112_new_control(data);
647ac171c46SBenjamin Herrenschmidt 		break;
648ac171c46SBenjamin Herrenschmidt 	case WF_EVENT_TICK:
649ac171c46SBenjamin Herrenschmidt 		if (have_all_controls && have_all_sensors)
650ac171c46SBenjamin Herrenschmidt 			pm112_tick();
651ac171c46SBenjamin Herrenschmidt 	}
652ac171c46SBenjamin Herrenschmidt 	return 0;
653ac171c46SBenjamin Herrenschmidt }
654ac171c46SBenjamin Herrenschmidt 
655ac171c46SBenjamin Herrenschmidt static struct notifier_block pm112_events = {
656ac171c46SBenjamin Herrenschmidt 	.notifier_call = pm112_wf_notify,
657ac171c46SBenjamin Herrenschmidt };
658ac171c46SBenjamin Herrenschmidt 
wf_pm112_probe(struct platform_device * dev)65910270613SBenjamin Herrenschmidt static int wf_pm112_probe(struct platform_device *dev)
660ac171c46SBenjamin Herrenschmidt {
661ac171c46SBenjamin Herrenschmidt 	wf_register_client(&pm112_events);
662ac171c46SBenjamin Herrenschmidt 	return 0;
663ac171c46SBenjamin Herrenschmidt }
664ac171c46SBenjamin Herrenschmidt 
wf_pm112_remove(struct platform_device * dev)6651da42fb6SGreg Kroah-Hartman static int wf_pm112_remove(struct platform_device *dev)
666ac171c46SBenjamin Herrenschmidt {
667ac171c46SBenjamin Herrenschmidt 	wf_unregister_client(&pm112_events);
668ac171c46SBenjamin Herrenschmidt 	/* should release all sensors and controls */
669ac171c46SBenjamin Herrenschmidt 	return 0;
670ac171c46SBenjamin Herrenschmidt }
671ac171c46SBenjamin Herrenschmidt 
67210270613SBenjamin Herrenschmidt static struct platform_driver wf_pm112_driver = {
67310270613SBenjamin Herrenschmidt 	.probe = wf_pm112_probe,
6741da42fb6SGreg Kroah-Hartman 	.remove = wf_pm112_remove,
67510270613SBenjamin Herrenschmidt 	.driver = {
676ac171c46SBenjamin Herrenschmidt 		.name = "windfarm",
67710270613SBenjamin Herrenschmidt 	},
678ac171c46SBenjamin Herrenschmidt };
679ac171c46SBenjamin Herrenschmidt 
wf_pm112_init(void)680ac171c46SBenjamin Herrenschmidt static int __init wf_pm112_init(void)
681ac171c46SBenjamin Herrenschmidt {
682ac171c46SBenjamin Herrenschmidt 	struct device_node *cpu;
683ac171c46SBenjamin Herrenschmidt 
68471a157e8SGrant Likely 	if (!of_machine_is_compatible("PowerMac11,2"))
685ac171c46SBenjamin Herrenschmidt 		return -ENODEV;
686ac171c46SBenjamin Herrenschmidt 
687ac171c46SBenjamin Herrenschmidt 	/* Count the number of CPU cores */
688ac171c46SBenjamin Herrenschmidt 	nr_cores = 0;
689c7c360eeSWei Yongjun 	for_each_node_by_type(cpu, "cpu")
690ac171c46SBenjamin Herrenschmidt 		++nr_cores;
691ac171c46SBenjamin Herrenschmidt 
692ac171c46SBenjamin Herrenschmidt 	printk(KERN_INFO "windfarm: initializing for dual-core desktop G5\n");
693d31e8171SBenjamin Herrenschmidt 
694d31e8171SBenjamin Herrenschmidt #ifdef MODULE
695d31e8171SBenjamin Herrenschmidt 	request_module("windfarm_smu_controls");
696d31e8171SBenjamin Herrenschmidt 	request_module("windfarm_smu_sensors");
697d31e8171SBenjamin Herrenschmidt 	request_module("windfarm_smu_sat");
698d31e8171SBenjamin Herrenschmidt 	request_module("windfarm_lm75_sensor");
699d31e8171SBenjamin Herrenschmidt 	request_module("windfarm_max6690_sensor");
700d31e8171SBenjamin Herrenschmidt 	request_module("windfarm_cpufreq_clamp");
701d31e8171SBenjamin Herrenschmidt 
702d31e8171SBenjamin Herrenschmidt #endif /* MODULE */
703d31e8171SBenjamin Herrenschmidt 
70410270613SBenjamin Herrenschmidt 	platform_driver_register(&wf_pm112_driver);
705ac171c46SBenjamin Herrenschmidt 	return 0;
706ac171c46SBenjamin Herrenschmidt }
707ac171c46SBenjamin Herrenschmidt 
wf_pm112_exit(void)708ac171c46SBenjamin Herrenschmidt static void __exit wf_pm112_exit(void)
709ac171c46SBenjamin Herrenschmidt {
71010270613SBenjamin Herrenschmidt 	platform_driver_unregister(&wf_pm112_driver);
711ac171c46SBenjamin Herrenschmidt }
712ac171c46SBenjamin Herrenschmidt 
713ac171c46SBenjamin Herrenschmidt module_init(wf_pm112_init);
714ac171c46SBenjamin Herrenschmidt module_exit(wf_pm112_exit);
715ac171c46SBenjamin Herrenschmidt 
716ac171c46SBenjamin Herrenschmidt MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
717ac171c46SBenjamin Herrenschmidt MODULE_DESCRIPTION("Thermal control for PowerMac11,2");
718ac171c46SBenjamin Herrenschmidt MODULE_LICENSE("GPL");
71923386fe5SKay Sievers MODULE_ALIAS("platform:windfarm");
720