741 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Windfarm PowerMac thermal control. SMU based 1 CPU desktop control loops4 *5 * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.6 * <benh@kernel.crashing.org>7 *8 * The algorithm used is the PID control algorithm, used the same9 * way the published Darwin code does, using the same values that10 * are present in the Darwin 8.2 snapshot property lists (note however11 * that none of the code has been re-used, it's a complete re-implementation12 *13 * The various control loops found in Darwin config file are:14 *15 * PowerMac9,116 * ===========17 *18 * Has 3 control loops: CPU fans is similar to PowerMac8,1 (though it doesn't19 * try to play with other control loops fans). Drive bay is rather basic PID20 * with one sensor and one fan. Slots area is a bit different as the Darwin21 * driver is supposed to be capable of working in a special "AGP" mode which22 * involves the presence of an AGP sensor and an AGP fan (possibly on the23 * AGP card itself). I can't deal with that special mode as I don't have24 * access to those additional sensor/fans for now (though ultimately, it would25 * be possible to add sensor objects for them) so I'm only implementing the26 * basic PCI slot control loop27 */28 29#include <linux/types.h>30#include <linux/errno.h>31#include <linux/kernel.h>32#include <linux/delay.h>33#include <linux/slab.h>34#include <linux/init.h>35#include <linux/spinlock.h>36#include <linux/wait.h>37#include <linux/kmod.h>38#include <linux/device.h>39#include <linux/platform_device.h>40#include <linux/of.h>41 42#include <asm/machdep.h>43#include <asm/io.h>44#include <asm/sections.h>45#include <asm/smu.h>46 47#include "windfarm.h"48#include "windfarm_pid.h"49 50#define VERSION "0.4"51 52#undef DEBUG53 54#ifdef DEBUG55#define DBG(args...) printk(args)56#else57#define DBG(args...) do { } while(0)58#endif59 60/* define this to force CPU overtemp to 74 degree, useful for testing61 * the overtemp code62 */63#undef HACKED_OVERTEMP64 65/* Controls & sensors */66static struct wf_sensor *sensor_cpu_power;67static struct wf_sensor *sensor_cpu_temp;68static struct wf_sensor *sensor_hd_temp;69static struct wf_sensor *sensor_slots_power;70static struct wf_control *fan_cpu_main;71static struct wf_control *fan_cpu_second;72static struct wf_control *fan_cpu_third;73static struct wf_control *fan_hd;74static struct wf_control *fan_slots;75static struct wf_control *cpufreq_clamp;76 77/* Set to kick the control loop into life */78static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok;79static bool wf_smu_started;80static bool wf_smu_overtemp;81 82/* Failure handling.. could be nicer */83#define FAILURE_FAN 0x0184#define FAILURE_SENSOR 0x0285#define FAILURE_OVERTEMP 0x0486 87static unsigned int wf_smu_failure_state;88static int wf_smu_readjust, wf_smu_skipping;89 90/*91 * ****** CPU Fans Control Loop ******92 *93 */94 95 96#define WF_SMU_CPU_FANS_INTERVAL 197#define WF_SMU_CPU_FANS_MAX_HISTORY 1698 99/* State data used by the cpu fans control loop100 */101struct wf_smu_cpu_fans_state {102 int ticks;103 s32 cpu_setpoint;104 struct wf_cpu_pid_state pid;105};106 107static struct wf_smu_cpu_fans_state *wf_smu_cpu_fans;108 109 110 111/*112 * ****** Drive Fan Control Loop ******113 *114 */115 116struct wf_smu_drive_fans_state {117 int ticks;118 s32 setpoint;119 struct wf_pid_state pid;120};121 122static struct wf_smu_drive_fans_state *wf_smu_drive_fans;123 124/*125 * ****** Slots Fan Control Loop ******126 *127 */128 129struct wf_smu_slots_fans_state {130 int ticks;131 s32 setpoint;132 struct wf_pid_state pid;133};134 135static struct wf_smu_slots_fans_state *wf_smu_slots_fans;136 137/*138 * ***** Implementation *****139 *140 */141 142 143static void wf_smu_create_cpu_fans(void)144{145 struct wf_cpu_pid_param pid_param;146 const struct smu_sdbp_header *hdr;147 struct smu_sdbp_cpupiddata *piddata;148 struct smu_sdbp_fvt *fvt;149 s32 tmax, tdelta, maxpow, powadj;150 151 /* First, locate the PID params in SMU SBD */152 hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);153 if (!hdr) {154 printk(KERN_WARNING "windfarm: CPU PID fan config not found "155 "max fan speed\n");156 goto fail;157 }158 piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];159 160 /* Get the FVT params for operating point 0 (the only supported one161 * for now) in order to get tmax162 */163 hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);164 if (hdr) {165 fvt = (struct smu_sdbp_fvt *)&hdr[1];166 tmax = ((s32)fvt->maxtemp) << 16;167 } else168 tmax = 0x5e0000; /* 94 degree default */169 170 /* Alloc & initialize state */171 wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state),172 GFP_KERNEL);173 if (wf_smu_cpu_fans == NULL)174 goto fail;175 wf_smu_cpu_fans->ticks = 1;176 177 /* Fill PID params */178 pid_param.interval = WF_SMU_CPU_FANS_INTERVAL;179 pid_param.history_len = piddata->history_len;180 if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {181 printk(KERN_WARNING "windfarm: History size overflow on "182 "CPU control loop (%d)\n", piddata->history_len);183 pid_param.history_len = WF_CPU_PID_MAX_HISTORY;184 }185 pid_param.gd = piddata->gd;186 pid_param.gp = piddata->gp;187 pid_param.gr = piddata->gr / pid_param.history_len;188 189 tdelta = ((s32)piddata->target_temp_delta) << 16;190 maxpow = ((s32)piddata->max_power) << 16;191 powadj = ((s32)piddata->power_adj) << 16;192 193 pid_param.tmax = tmax;194 pid_param.ttarget = tmax - tdelta;195 pid_param.pmaxadj = maxpow - powadj;196 197 pid_param.min = wf_control_get_min(fan_cpu_main);198 pid_param.max = wf_control_get_max(fan_cpu_main);199 200 wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);201 202 DBG("wf: CPU Fan control initialized.\n");203 DBG(" ttarget=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM\n",204 FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),205 pid_param.min, pid_param.max);206 207 return;208 209 fail:210 printk(KERN_WARNING "windfarm: CPU fan config not found\n"211 "for this machine model, max fan speed\n");212 213 if (cpufreq_clamp)214 wf_control_set_max(cpufreq_clamp);215 if (fan_cpu_main)216 wf_control_set_max(fan_cpu_main);217}218 219static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)220{221 s32 new_setpoint, temp, power;222 int rc;223 224 if (--st->ticks != 0) {225 if (wf_smu_readjust)226 goto readjust;227 return;228 }229 st->ticks = WF_SMU_CPU_FANS_INTERVAL;230 231 rc = wf_sensor_get(sensor_cpu_temp, &temp);232 if (rc) {233 printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",234 rc);235 wf_smu_failure_state |= FAILURE_SENSOR;236 return;237 }238 239 rc = wf_sensor_get(sensor_cpu_power, &power);240 if (rc) {241 printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",242 rc);243 wf_smu_failure_state |= FAILURE_SENSOR;244 return;245 }246 247 DBG("wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d\n",248 FIX32TOPRINT(temp), FIX32TOPRINT(power));249 250#ifdef HACKED_OVERTEMP251 if (temp > 0x4a0000)252 wf_smu_failure_state |= FAILURE_OVERTEMP;253#else254 if (temp > st->pid.param.tmax)255 wf_smu_failure_state |= FAILURE_OVERTEMP;256#endif257 new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);258 259 DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);260 261 if (st->cpu_setpoint == new_setpoint)262 return;263 st->cpu_setpoint = new_setpoint;264 readjust:265 if (fan_cpu_main && wf_smu_failure_state == 0) {266 rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);267 if (rc) {268 printk(KERN_WARNING "windfarm: CPU main fan"269 " error %d\n", rc);270 wf_smu_failure_state |= FAILURE_FAN;271 }272 }273 if (fan_cpu_second && wf_smu_failure_state == 0) {274 rc = wf_control_set(fan_cpu_second, st->cpu_setpoint);275 if (rc) {276 printk(KERN_WARNING "windfarm: CPU second fan"277 " error %d\n", rc);278 wf_smu_failure_state |= FAILURE_FAN;279 }280 }281 if (fan_cpu_third && wf_smu_failure_state == 0) {282 rc = wf_control_set(fan_cpu_third, st->cpu_setpoint);283 if (rc) {284 printk(KERN_WARNING "windfarm: CPU third fan"285 " error %d\n", rc);286 wf_smu_failure_state |= FAILURE_FAN;287 }288 }289}290 291static void wf_smu_create_drive_fans(void)292{293 struct wf_pid_param param = {294 .interval = 5,295 .history_len = 2,296 .gd = 0x01e00000,297 .gp = 0x00500000,298 .gr = 0x00000000,299 .itarget = 0x00200000,300 };301 302 /* Alloc & initialize state */303 wf_smu_drive_fans = kmalloc(sizeof(struct wf_smu_drive_fans_state),304 GFP_KERNEL);305 if (wf_smu_drive_fans == NULL) {306 printk(KERN_WARNING "windfarm: Memory allocation error"307 " max fan speed\n");308 goto fail;309 }310 wf_smu_drive_fans->ticks = 1;311 312 /* Fill PID params */313 param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN);314 param.min = wf_control_get_min(fan_hd);315 param.max = wf_control_get_max(fan_hd);316 wf_pid_init(&wf_smu_drive_fans->pid, ¶m);317 318 DBG("wf: Drive Fan control initialized.\n");319 DBG(" itarged=%d.%03d, min=%d RPM, max=%d RPM\n",320 FIX32TOPRINT(param.itarget), param.min, param.max);321 return;322 323 fail:324 if (fan_hd)325 wf_control_set_max(fan_hd);326}327 328static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st)329{330 s32 new_setpoint, temp;331 int rc;332 333 if (--st->ticks != 0) {334 if (wf_smu_readjust)335 goto readjust;336 return;337 }338 st->ticks = st->pid.param.interval;339 340 rc = wf_sensor_get(sensor_hd_temp, &temp);341 if (rc) {342 printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",343 rc);344 wf_smu_failure_state |= FAILURE_SENSOR;345 return;346 }347 348 DBG("wf_smu: Drive Fans tick ! HD temp: %d.%03d\n",349 FIX32TOPRINT(temp));350 351 if (temp > (st->pid.param.itarget + 0x50000))352 wf_smu_failure_state |= FAILURE_OVERTEMP;353 354 new_setpoint = wf_pid_run(&st->pid, temp);355 356 DBG("wf_smu: new_setpoint: %d\n", (int)new_setpoint);357 358 if (st->setpoint == new_setpoint)359 return;360 st->setpoint = new_setpoint;361 readjust:362 if (fan_hd && wf_smu_failure_state == 0) {363 rc = wf_control_set(fan_hd, st->setpoint);364 if (rc) {365 printk(KERN_WARNING "windfarm: HD fan error %d\n",366 rc);367 wf_smu_failure_state |= FAILURE_FAN;368 }369 }370}371 372static void wf_smu_create_slots_fans(void)373{374 struct wf_pid_param param = {375 .interval = 1,376 .history_len = 8,377 .gd = 0x00000000,378 .gp = 0x00000000,379 .gr = 0x00020000,380 .itarget = 0x00000000381 };382 383 /* Alloc & initialize state */384 wf_smu_slots_fans = kmalloc(sizeof(struct wf_smu_slots_fans_state),385 GFP_KERNEL);386 if (wf_smu_slots_fans == NULL) {387 printk(KERN_WARNING "windfarm: Memory allocation error"388 " max fan speed\n");389 goto fail;390 }391 wf_smu_slots_fans->ticks = 1;392 393 /* Fill PID params */394 param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN);395 param.min = wf_control_get_min(fan_slots);396 param.max = wf_control_get_max(fan_slots);397 wf_pid_init(&wf_smu_slots_fans->pid, ¶m);398 399 DBG("wf: Slots Fan control initialized.\n");400 DBG(" itarged=%d.%03d, min=%d RPM, max=%d RPM\n",401 FIX32TOPRINT(param.itarget), param.min, param.max);402 return;403 404 fail:405 if (fan_slots)406 wf_control_set_max(fan_slots);407}408 409static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st)410{411 s32 new_setpoint, power;412 int rc;413 414 if (--st->ticks != 0) {415 if (wf_smu_readjust)416 goto readjust;417 return;418 }419 st->ticks = st->pid.param.interval;420 421 rc = wf_sensor_get(sensor_slots_power, &power);422 if (rc) {423 printk(KERN_WARNING "windfarm: Slots power sensor error %d\n",424 rc);425 wf_smu_failure_state |= FAILURE_SENSOR;426 return;427 }428 429 DBG("wf_smu: Slots Fans tick ! Slots power: %d.%03d\n",430 FIX32TOPRINT(power));431 432#if 0 /* Check what makes a good overtemp condition */433 if (power > (st->pid.param.itarget + 0x50000))434 wf_smu_failure_state |= FAILURE_OVERTEMP;435#endif436 437 new_setpoint = wf_pid_run(&st->pid, power);438 439 DBG("wf_smu: new_setpoint: %d\n", (int)new_setpoint);440 441 if (st->setpoint == new_setpoint)442 return;443 st->setpoint = new_setpoint;444 readjust:445 if (fan_slots && wf_smu_failure_state == 0) {446 rc = wf_control_set(fan_slots, st->setpoint);447 if (rc) {448 printk(KERN_WARNING "windfarm: Slots fan error %d\n",449 rc);450 wf_smu_failure_state |= FAILURE_FAN;451 }452 }453}454 455 456/*457 * ****** Setup / Init / Misc ... ******458 *459 */460 461static void wf_smu_tick(void)462{463 unsigned int last_failure = wf_smu_failure_state;464 unsigned int new_failure;465 466 if (!wf_smu_started) {467 DBG("wf: creating control loops !\n");468 wf_smu_create_drive_fans();469 wf_smu_create_slots_fans();470 wf_smu_create_cpu_fans();471 wf_smu_started = true;472 }473 474 /* Skipping ticks */475 if (wf_smu_skipping && --wf_smu_skipping)476 return;477 478 wf_smu_failure_state = 0;479 if (wf_smu_drive_fans)480 wf_smu_drive_fans_tick(wf_smu_drive_fans);481 if (wf_smu_slots_fans)482 wf_smu_slots_fans_tick(wf_smu_slots_fans);483 if (wf_smu_cpu_fans)484 wf_smu_cpu_fans_tick(wf_smu_cpu_fans);485 486 wf_smu_readjust = 0;487 new_failure = wf_smu_failure_state & ~last_failure;488 489 /* If entering failure mode, clamp cpufreq and ramp all490 * fans to full speed.491 */492 if (wf_smu_failure_state && !last_failure) {493 if (cpufreq_clamp)494 wf_control_set_max(cpufreq_clamp);495 if (fan_cpu_main)496 wf_control_set_max(fan_cpu_main);497 if (fan_cpu_second)498 wf_control_set_max(fan_cpu_second);499 if (fan_cpu_third)500 wf_control_set_max(fan_cpu_third);501 if (fan_hd)502 wf_control_set_max(fan_hd);503 if (fan_slots)504 wf_control_set_max(fan_slots);505 }506 507 /* If leaving failure mode, unclamp cpufreq and readjust508 * all fans on next iteration509 */510 if (!wf_smu_failure_state && last_failure) {511 if (cpufreq_clamp)512 wf_control_set_min(cpufreq_clamp);513 wf_smu_readjust = 1;514 }515 516 /* Overtemp condition detected, notify and start skipping a couple517 * ticks to let the temperature go down518 */519 if (new_failure & FAILURE_OVERTEMP) {520 wf_set_overtemp();521 wf_smu_skipping = 2;522 wf_smu_overtemp = true;523 }524 525 /* We only clear the overtemp condition if overtemp is cleared526 * _and_ no other failure is present. Since a sensor error will527 * clear the overtemp condition (can't measure temperature) at528 * the control loop levels, but we don't want to keep it clear529 * here in this case530 */531 if (!wf_smu_failure_state && wf_smu_overtemp) {532 wf_clear_overtemp();533 wf_smu_overtemp = false;534 }535}536 537 538static void wf_smu_new_control(struct wf_control *ct)539{540 if (wf_smu_all_controls_ok)541 return;542 543 if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-rear-fan-0")) {544 if (wf_get_control(ct) == 0)545 fan_cpu_main = ct;546 }547 548 if (fan_cpu_second == NULL && !strcmp(ct->name, "cpu-rear-fan-1")) {549 if (wf_get_control(ct) == 0)550 fan_cpu_second = ct;551 }552 553 if (fan_cpu_third == NULL && !strcmp(ct->name, "cpu-front-fan-0")) {554 if (wf_get_control(ct) == 0)555 fan_cpu_third = ct;556 }557 558 if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {559 if (wf_get_control(ct) == 0)560 cpufreq_clamp = ct;561 }562 563 if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) {564 if (wf_get_control(ct) == 0)565 fan_hd = ct;566 }567 568 if (fan_slots == NULL && !strcmp(ct->name, "slots-fan")) {569 if (wf_get_control(ct) == 0)570 fan_slots = ct;571 }572 573 if (fan_cpu_main && (fan_cpu_second || fan_cpu_third) && fan_hd &&574 fan_slots && cpufreq_clamp)575 wf_smu_all_controls_ok = 1;576}577 578static void wf_smu_new_sensor(struct wf_sensor *sr)579{580 if (wf_smu_all_sensors_ok)581 return;582 583 if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) {584 if (wf_get_sensor(sr) == 0)585 sensor_cpu_power = sr;586 }587 588 if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) {589 if (wf_get_sensor(sr) == 0)590 sensor_cpu_temp = sr;591 }592 593 if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) {594 if (wf_get_sensor(sr) == 0)595 sensor_hd_temp = sr;596 }597 598 if (sensor_slots_power == NULL && !strcmp(sr->name, "slots-power")) {599 if (wf_get_sensor(sr) == 0)600 sensor_slots_power = sr;601 }602 603 if (sensor_cpu_power && sensor_cpu_temp &&604 sensor_hd_temp && sensor_slots_power)605 wf_smu_all_sensors_ok = 1;606}607 608 609static int wf_smu_notify(struct notifier_block *self,610 unsigned long event, void *data)611{612 switch(event) {613 case WF_EVENT_NEW_CONTROL:614 DBG("wf: new control %s detected\n",615 ((struct wf_control *)data)->name);616 wf_smu_new_control(data);617 wf_smu_readjust = 1;618 break;619 case WF_EVENT_NEW_SENSOR:620 DBG("wf: new sensor %s detected\n",621 ((struct wf_sensor *)data)->name);622 wf_smu_new_sensor(data);623 break;624 case WF_EVENT_TICK:625 if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok)626 wf_smu_tick();627 }628 629 return 0;630}631 632static struct notifier_block wf_smu_events = {633 .notifier_call = wf_smu_notify,634};635 636static int wf_init_pm(void)637{638 printk(KERN_INFO "windfarm: Initializing for Desktop G5 model\n");639 640 return 0;641}642 643static int wf_smu_probe(struct platform_device *ddev)644{645 wf_register_client(&wf_smu_events);646 647 return 0;648}649 650static void wf_smu_remove(struct platform_device *ddev)651{652 wf_unregister_client(&wf_smu_events);653 654 /* XXX We don't have yet a guarantee that our callback isn't655 * in progress when returning from wf_unregister_client, so656 * we add an arbitrary delay. I'll have to fix that in the core657 */658 msleep(1000);659 660 /* Release all sensors */661 /* One more crappy race: I don't think we have any guarantee here662 * that the attribute callback won't race with the sensor beeing663 * disposed of, and I'm not 100% certain what best way to deal664 * with that except by adding locks all over... I'll do that665 * eventually but heh, who ever rmmod this module anyway ?666 */667 if (sensor_cpu_power)668 wf_put_sensor(sensor_cpu_power);669 if (sensor_cpu_temp)670 wf_put_sensor(sensor_cpu_temp);671 if (sensor_hd_temp)672 wf_put_sensor(sensor_hd_temp);673 if (sensor_slots_power)674 wf_put_sensor(sensor_slots_power);675 676 /* Release all controls */677 if (fan_cpu_main)678 wf_put_control(fan_cpu_main);679 if (fan_cpu_second)680 wf_put_control(fan_cpu_second);681 if (fan_cpu_third)682 wf_put_control(fan_cpu_third);683 if (fan_hd)684 wf_put_control(fan_hd);685 if (fan_slots)686 wf_put_control(fan_slots);687 if (cpufreq_clamp)688 wf_put_control(cpufreq_clamp);689 690 /* Destroy control loops state structures */691 kfree(wf_smu_slots_fans);692 kfree(wf_smu_drive_fans);693 kfree(wf_smu_cpu_fans);694}695 696static struct platform_driver wf_smu_driver = {697 .probe = wf_smu_probe,698 .remove_new = wf_smu_remove,699 .driver = {700 .name = "windfarm",701 },702};703 704 705static int __init wf_smu_init(void)706{707 int rc = -ENODEV;708 709 if (of_machine_is_compatible("PowerMac9,1"))710 rc = wf_init_pm();711 712 if (rc == 0) {713#ifdef MODULE714 request_module("windfarm_smu_controls");715 request_module("windfarm_smu_sensors");716 request_module("windfarm_lm75_sensor");717 request_module("windfarm_cpufreq_clamp");718 719#endif /* MODULE */720 platform_driver_register(&wf_smu_driver);721 }722 723 return rc;724}725 726static void __exit wf_smu_exit(void)727{728 729 platform_driver_unregister(&wf_smu_driver);730}731 732 733module_init(wf_smu_init);734module_exit(wf_smu_exit);735 736MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");737MODULE_DESCRIPTION("Thermal control logic for PowerMac9,1");738MODULE_LICENSE("GPL");739 740MODULE_ALIAS("platform:windfarm");741