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