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