1044 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Windfarm PowerMac thermal control. iMac G5 iSight4 *5 * (c) Copyright 2007 Étienne Bersac <bersace@gmail.com>6 *7 * Bits & pieces from windfarm_pm81.c by (c) Copyright 2005 Benjamin8 * Herrenschmidt, IBM Corp. <benh@kernel.crashing.org>9 *10 * PowerMac12,111 * ============12 *13 * The algorithm used is the PID control algorithm, used the same way14 * the published Darwin code does, using the same values that are15 * present in the Darwin 8.10 snapshot property lists (note however16 * that none of the code has been re-used, it's a complete17 * re-implementation18 *19 * There is two models using PowerMac12,1. Model 2 is iMac G5 iSight20 * 17" while Model 3 is iMac G5 20". They do have both the same21 * controls with a tiny difference. The control-ids of hard-drive-fan22 * and cpu-fan is swapped.23 *24 * Target Correction :25 *26 * controls have a target correction calculated as :27 *28 * new_min = ((((average_power * slope) >> 16) + offset) >> 16) + min_value29 * new_value = max(new_value, max(new_min, 0))30 *31 * OD Fan control correction.32 *33 * # model_id: 234 * offset : -1956315235 * slope : 195631536 *37 * # model_id: 338 * offset : -1565065239 * slope : 156506540 *41 * HD Fan control correction.42 *43 * # model_id: 244 * offset : -1565065245 * slope : 156506546 *47 * # model_id: 348 * offset : -1956315249 * slope : 195631550 *51 * CPU Fan control correction.52 *53 * # model_id: 254 * offset : -2543190055 * slope : 254319056 *57 * # model_id: 358 * offset : -1565065259 * slope : 156506560 *61 * Target rubber-banding :62 *63 * Some controls have a target correction which depends on another64 * control value. The correction is computed in the following way :65 *66 * new_min = ref_value * slope + offset67 *68 * ref_value is the value of the reference control. If new_min is69 * greater than 0, then we correct the target value using :70 *71 * new_target = max (new_target, new_min >> 16)72 *73 * # model_id : 274 * control : cpu-fan75 * ref : optical-drive-fan76 * offset : -1565065277 * slope : 156506578 *79 * # model_id : 380 * control : optical-drive-fan81 * ref : hard-drive-fan82 * offset : -3276800083 * slope : 6553684 *85 * In order to have the moste efficient correction with those86 * dependencies, we must trigger HD loop before OD loop before CPU87 * loop.88 *89 * The various control loops found in Darwin config file are:90 *91 * HD Fan control loop.92 *93 * # model_id: 294 * control : hard-drive-fan95 * sensor : hard-drive-temp96 * PID params : G_d = 0x0000000097 * G_p = 0x002D70A398 * G_r = 0x0001999999 * History = 2 entries100 * Input target = 0x370000101 * Interval = 5s102 *103 * # model_id: 3104 * control : hard-drive-fan105 * sensor : hard-drive-temp106 * PID params : G_d = 0x00000000107 * G_p = 0x002170A3108 * G_r = 0x00019999109 * History = 2 entries110 * Input target = 0x370000111 * Interval = 5s112 *113 * OD Fan control loop.114 *115 * # model_id: 2116 * control : optical-drive-fan117 * sensor : optical-drive-temp118 * PID params : G_d = 0x00000000119 * G_p = 0x001FAE14120 * G_r = 0x00019999121 * History = 2 entries122 * Input target = 0x320000123 * Interval = 5s124 *125 * # model_id: 3126 * control : optical-drive-fan127 * sensor : optical-drive-temp128 * PID params : G_d = 0x00000000129 * G_p = 0x001FAE14130 * G_r = 0x00019999131 * History = 2 entries132 * Input target = 0x320000133 * Interval = 5s134 *135 * GPU Fan control loop.136 *137 * # model_id: 2138 * control : hard-drive-fan139 * sensor : gpu-temp140 * PID params : G_d = 0x00000000141 * G_p = 0x002A6666142 * G_r = 0x00019999143 * History = 2 entries144 * Input target = 0x5A0000145 * Interval = 5s146 *147 * # model_id: 3148 * control : cpu-fan149 * sensor : gpu-temp150 * PID params : G_d = 0x00000000151 * G_p = 0x0010CCCC152 * G_r = 0x00019999153 * History = 2 entries154 * Input target = 0x500000155 * Interval = 5s156 *157 * KODIAK (aka northbridge) Fan control loop.158 *159 * # model_id: 2160 * control : optical-drive-fan161 * sensor : north-bridge-temp162 * PID params : G_d = 0x00000000163 * G_p = 0x003BD70A164 * G_r = 0x00019999165 * History = 2 entries166 * Input target = 0x550000167 * Interval = 5s168 *169 * # model_id: 3170 * control : hard-drive-fan171 * sensor : north-bridge-temp172 * PID params : G_d = 0x00000000173 * G_p = 0x0030F5C2174 * G_r = 0x00019999175 * History = 2 entries176 * Input target = 0x550000177 * Interval = 5s178 *179 * CPU Fan control loop.180 *181 * control : cpu-fan182 * sensors : cpu-temp, cpu-power183 * PID params : from SDB partition184 *185 * CPU Slew control loop.186 *187 * control : cpufreq-clamp188 * sensor : cpu-temp189 */190 191#undef DEBUG192 193#include <linux/types.h>194#include <linux/errno.h>195#include <linux/kernel.h>196#include <linux/delay.h>197#include <linux/slab.h>198#include <linux/init.h>199#include <linux/spinlock.h>200#include <linux/wait.h>201#include <linux/kmod.h>202#include <linux/device.h>203#include <linux/platform_device.h>204#include <linux/of.h>205 206#include <asm/machdep.h>207#include <asm/io.h>208#include <asm/sections.h>209#include <asm/smu.h>210 211#include "windfarm.h"212#include "windfarm_pid.h"213 214#define VERSION "0.3"215 216static int pm121_mach_model; /* machine model id */217 218/* Controls & sensors */219static struct wf_sensor *sensor_cpu_power;220static struct wf_sensor *sensor_cpu_temp;221static struct wf_sensor *sensor_cpu_voltage;222static struct wf_sensor *sensor_cpu_current;223static struct wf_sensor *sensor_gpu_temp;224static struct wf_sensor *sensor_north_bridge_temp;225static struct wf_sensor *sensor_hard_drive_temp;226static struct wf_sensor *sensor_optical_drive_temp;227static struct wf_sensor *sensor_incoming_air_temp; /* unused ! */228 229enum {230 FAN_CPU,231 FAN_HD,232 FAN_OD,233 CPUFREQ,234 N_CONTROLS235};236static struct wf_control *controls[N_CONTROLS] = {};237 238/* Set to kick the control loop into life */239static int pm121_all_controls_ok, pm121_all_sensors_ok;240static bool pm121_started;241 242enum {243 FAILURE_FAN = 1 << 0,244 FAILURE_SENSOR = 1 << 1,245 FAILURE_OVERTEMP = 1 << 2246};247 248/* All sys loops. Note the HD before the OD loop in order to have it249 run before. */250enum {251 LOOP_GPU, /* control = hd or cpu, but luckily,252 it doesn't matter */253 LOOP_HD, /* control = hd */254 LOOP_KODIAK, /* control = hd or od */255 LOOP_OD, /* control = od */256 N_LOOPS257};258 259static const char *loop_names[N_LOOPS] = {260 "GPU",261 "HD",262 "KODIAK",263 "OD",264};265 266#define PM121_NUM_CONFIGS 2267 268static unsigned int pm121_failure_state;269static int pm121_readjust, pm121_skipping;270static bool pm121_overtemp;271static s32 average_power;272 273struct pm121_correction {274 int offset;275 int slope;276};277 278static struct pm121_correction corrections[N_CONTROLS][PM121_NUM_CONFIGS] = {279 /* FAN_OD */280 {281 /* MODEL 2 */282 { .offset = -19563152,283 .slope = 1956315284 },285 /* MODEL 3 */286 { .offset = -15650652,287 .slope = 1565065288 },289 },290 /* FAN_HD */291 {292 /* MODEL 2 */293 { .offset = -15650652,294 .slope = 1565065295 },296 /* MODEL 3 */297 { .offset = -19563152,298 .slope = 1956315299 },300 },301 /* FAN_CPU */302 {303 /* MODEL 2 */304 { .offset = -25431900,305 .slope = 2543190306 },307 /* MODEL 3 */308 { .offset = -15650652,309 .slope = 1565065310 },311 },312 /* CPUFREQ has no correction (and is not implemented at all) */313};314 315struct pm121_connection {316 unsigned int control_id;317 unsigned int ref_id;318 struct pm121_correction correction;319};320 321static struct pm121_connection pm121_connections[] = {322 /* MODEL 2 */323 { .control_id = FAN_CPU,324 .ref_id = FAN_OD,325 { .offset = -32768000,326 .slope = 65536327 }328 },329 /* MODEL 3 */330 { .control_id = FAN_OD,331 .ref_id = FAN_HD,332 { .offset = -32768000,333 .slope = 65536334 }335 },336};337 338/* pointer to the current model connection */339static struct pm121_connection *pm121_connection;340 341/*342 * ****** System Fans Control Loop ******343 *344 */345 346/* Since each loop handles only one control and we want to avoid347 * writing virtual control, we store the control correction with the348 * loop params. Some data are not set, there are common to all loop349 * and thus, hardcoded.350 */351struct pm121_sys_param {352 /* purely informative since we use mach_model-2 as index */353 int model_id;354 struct wf_sensor **sensor; /* use sensor_id instead ? */355 s32 gp, itarget;356 unsigned int control_id;357};358 359static struct pm121_sys_param360pm121_sys_all_params[N_LOOPS][PM121_NUM_CONFIGS] = {361 /* GPU Fan control loop */362 {363 { .model_id = 2,364 .sensor = &sensor_gpu_temp,365 .gp = 0x002A6666,366 .itarget = 0x5A0000,367 .control_id = FAN_HD,368 },369 { .model_id = 3,370 .sensor = &sensor_gpu_temp,371 .gp = 0x0010CCCC,372 .itarget = 0x500000,373 .control_id = FAN_CPU,374 },375 },376 /* HD Fan control loop */377 {378 { .model_id = 2,379 .sensor = &sensor_hard_drive_temp,380 .gp = 0x002D70A3,381 .itarget = 0x370000,382 .control_id = FAN_HD,383 },384 { .model_id = 3,385 .sensor = &sensor_hard_drive_temp,386 .gp = 0x002170A3,387 .itarget = 0x370000,388 .control_id = FAN_HD,389 },390 },391 /* KODIAK Fan control loop */392 {393 { .model_id = 2,394 .sensor = &sensor_north_bridge_temp,395 .gp = 0x003BD70A,396 .itarget = 0x550000,397 .control_id = FAN_OD,398 },399 { .model_id = 3,400 .sensor = &sensor_north_bridge_temp,401 .gp = 0x0030F5C2,402 .itarget = 0x550000,403 .control_id = FAN_HD,404 },405 },406 /* OD Fan control loop */407 {408 { .model_id = 2,409 .sensor = &sensor_optical_drive_temp,410 .gp = 0x001FAE14,411 .itarget = 0x320000,412 .control_id = FAN_OD,413 },414 { .model_id = 3,415 .sensor = &sensor_optical_drive_temp,416 .gp = 0x001FAE14,417 .itarget = 0x320000,418 .control_id = FAN_OD,419 },420 },421};422 423/* the hardcoded values */424#define PM121_SYS_GD 0x00000000425#define PM121_SYS_GR 0x00019999426#define PM121_SYS_HISTORY_SIZE 2427#define PM121_SYS_INTERVAL 5428 429/* State data used by the system fans control loop430 */431struct pm121_sys_state {432 int ticks;433 s32 setpoint;434 struct wf_pid_state pid;435};436 437static struct pm121_sys_state *pm121_sys_state[N_LOOPS] = {};438 439/*440 * ****** CPU Fans Control Loop ******441 *442 */443 444#define PM121_CPU_INTERVAL 1445 446/* State data used by the cpu fans control loop447 */448struct pm121_cpu_state {449 int ticks;450 s32 setpoint;451 struct wf_cpu_pid_state pid;452};453 454static struct pm121_cpu_state *pm121_cpu_state;455 456 457 458/*459 * ***** Implementation *****460 *461 */462 463/* correction the value using the output-low-bound correction algo */464static s32 pm121_correct(s32 new_setpoint,465 unsigned int control_id,466 s32 min)467{468 s32 new_min;469 struct pm121_correction *correction;470 correction = &corrections[control_id][pm121_mach_model - 2];471 472 new_min = (average_power * correction->slope) >> 16;473 new_min += correction->offset;474 new_min = (new_min >> 16) + min;475 476 return max3(new_setpoint, new_min, 0);477}478 479static s32 pm121_connect(unsigned int control_id, s32 setpoint)480{481 s32 new_min, value, new_setpoint;482 483 if (pm121_connection->control_id == control_id) {484 controls[control_id]->ops->get_value(controls[control_id],485 &value);486 new_min = value * pm121_connection->correction.slope;487 new_min += pm121_connection->correction.offset;488 if (new_min > 0) {489 new_setpoint = max(setpoint, (new_min >> 16));490 if (new_setpoint != setpoint) {491 pr_debug("pm121: %s depending on %s, "492 "corrected from %d to %d RPM\n",493 controls[control_id]->name,494 controls[pm121_connection->ref_id]->name,495 (int) setpoint, (int) new_setpoint);496 }497 } else498 new_setpoint = setpoint;499 }500 /* no connection */501 else502 new_setpoint = setpoint;503 504 return new_setpoint;505}506 507/* FAN LOOPS */508static void pm121_create_sys_fans(int loop_id)509{510 struct pm121_sys_param *param = NULL;511 struct wf_pid_param pid_param;512 struct wf_control *control = NULL;513 int i;514 515 /* First, locate the params for this model */516 for (i = 0; i < PM121_NUM_CONFIGS; i++) {517 if (pm121_sys_all_params[loop_id][i].model_id == pm121_mach_model) {518 param = &(pm121_sys_all_params[loop_id][i]);519 break;520 }521 }522 523 /* No params found, put fans to max */524 if (param == NULL) {525 printk(KERN_WARNING "pm121: %s fan config not found "526 " for this machine model\n",527 loop_names[loop_id]);528 goto fail;529 }530 531 control = controls[param->control_id];532 533 /* Alloc & initialize state */534 pm121_sys_state[loop_id] = kmalloc(sizeof(struct pm121_sys_state),535 GFP_KERNEL);536 if (pm121_sys_state[loop_id] == NULL) {537 printk(KERN_WARNING "pm121: Memory allocation error\n");538 goto fail;539 }540 pm121_sys_state[loop_id]->ticks = 1;541 542 /* Fill PID params */543 pid_param.gd = PM121_SYS_GD;544 pid_param.gp = param->gp;545 pid_param.gr = PM121_SYS_GR;546 pid_param.interval = PM121_SYS_INTERVAL;547 pid_param.history_len = PM121_SYS_HISTORY_SIZE;548 pid_param.itarget = param->itarget;549 if(control)550 {551 pid_param.min = control->ops->get_min(control);552 pid_param.max = control->ops->get_max(control);553 } else {554 /*555 * This is probably not the right!?556 * Perhaps goto fail if control == NULL above?557 */558 pid_param.min = 0;559 pid_param.max = 0;560 }561 562 wf_pid_init(&pm121_sys_state[loop_id]->pid, &pid_param);563 564 pr_debug("pm121: %s Fan control loop initialized.\n"565 " itarged=%d.%03d, min=%d RPM, max=%d RPM\n",566 loop_names[loop_id], FIX32TOPRINT(pid_param.itarget),567 pid_param.min, pid_param.max);568 return;569 570 fail:571 /* note that this is not optimal since another loop may still572 control the same control */573 printk(KERN_WARNING "pm121: failed to set up %s loop "574 "setting \"%s\" to max speed.\n",575 loop_names[loop_id], control ? control->name : "uninitialized value");576 577 if (control)578 wf_control_set_max(control);579}580 581static void pm121_sys_fans_tick(int loop_id)582{583 struct pm121_sys_param *param;584 struct pm121_sys_state *st;585 struct wf_sensor *sensor;586 struct wf_control *control;587 s32 temp, new_setpoint;588 int rc;589 590 param = &(pm121_sys_all_params[loop_id][pm121_mach_model-2]);591 st = pm121_sys_state[loop_id];592 sensor = *(param->sensor);593 control = controls[param->control_id];594 595 if (--st->ticks != 0) {596 if (pm121_readjust)597 goto readjust;598 return;599 }600 st->ticks = PM121_SYS_INTERVAL;601 602 rc = sensor->ops->get_value(sensor, &temp);603 if (rc) {604 printk(KERN_WARNING "windfarm: %s sensor error %d\n",605 sensor->name, rc);606 pm121_failure_state |= FAILURE_SENSOR;607 return;608 }609 610 pr_debug("pm121: %s Fan tick ! %s: %d.%03d\n",611 loop_names[loop_id], sensor->name,612 FIX32TOPRINT(temp));613 614 new_setpoint = wf_pid_run(&st->pid, temp);615 616 /* correction */617 new_setpoint = pm121_correct(new_setpoint,618 param->control_id,619 st->pid.param.min);620 /* linked corretion */621 new_setpoint = pm121_connect(param->control_id, new_setpoint);622 623 if (new_setpoint == st->setpoint)624 return;625 st->setpoint = new_setpoint;626 pr_debug("pm121: %s corrected setpoint: %d RPM\n",627 control->name, (int)new_setpoint);628 readjust:629 if (control && pm121_failure_state == 0) {630 rc = control->ops->set_value(control, st->setpoint);631 if (rc) {632 printk(KERN_WARNING "windfarm: %s fan error %d\n",633 control->name, rc);634 pm121_failure_state |= FAILURE_FAN;635 }636 }637}638 639 640/* CPU LOOP */641static void pm121_create_cpu_fans(void)642{643 struct wf_cpu_pid_param pid_param;644 const struct smu_sdbp_header *hdr;645 struct smu_sdbp_cpupiddata *piddata;646 struct smu_sdbp_fvt *fvt;647 struct wf_control *fan_cpu;648 s32 tmax, tdelta, maxpow, powadj;649 650 fan_cpu = controls[FAN_CPU];651 652 /* First, locate the PID params in SMU SBD */653 hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);654 if (!hdr) {655 printk(KERN_WARNING "pm121: CPU PID fan config not found.\n");656 goto fail;657 }658 piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];659 660 /* Get the FVT params for operating point 0 (the only supported one661 * for now) in order to get tmax662 */663 hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);664 if (hdr) {665 fvt = (struct smu_sdbp_fvt *)&hdr[1];666 tmax = ((s32)fvt->maxtemp) << 16;667 } else668 tmax = 0x5e0000; /* 94 degree default */669 670 /* Alloc & initialize state */671 pm121_cpu_state = kmalloc(sizeof(struct pm121_cpu_state),672 GFP_KERNEL);673 if (pm121_cpu_state == NULL)674 goto fail;675 pm121_cpu_state->ticks = 1;676 677 /* Fill PID params */678 pid_param.interval = PM121_CPU_INTERVAL;679 pid_param.history_len = piddata->history_len;680 if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {681 printk(KERN_WARNING "pm121: History size overflow on "682 "CPU control loop (%d)\n", piddata->history_len);683 pid_param.history_len = WF_CPU_PID_MAX_HISTORY;684 }685 pid_param.gd = piddata->gd;686 pid_param.gp = piddata->gp;687 pid_param.gr = piddata->gr / pid_param.history_len;688 689 tdelta = ((s32)piddata->target_temp_delta) << 16;690 maxpow = ((s32)piddata->max_power) << 16;691 powadj = ((s32)piddata->power_adj) << 16;692 693 pid_param.tmax = tmax;694 pid_param.ttarget = tmax - tdelta;695 pid_param.pmaxadj = maxpow - powadj;696 697 pid_param.min = fan_cpu->ops->get_min(fan_cpu);698 pid_param.max = fan_cpu->ops->get_max(fan_cpu);699 700 wf_cpu_pid_init(&pm121_cpu_state->pid, &pid_param);701 702 pr_debug("pm121: CPU Fan control initialized.\n");703 pr_debug(" ttarget=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM,\n",704 FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),705 pid_param.min, pid_param.max);706 707 return;708 709 fail:710 printk(KERN_WARNING "pm121: CPU fan config not found, max fan speed\n");711 712 if (controls[CPUFREQ])713 wf_control_set_max(controls[CPUFREQ]);714 if (fan_cpu)715 wf_control_set_max(fan_cpu);716}717 718 719static void pm121_cpu_fans_tick(struct pm121_cpu_state *st)720{721 s32 new_setpoint, temp, power;722 struct wf_control *fan_cpu = NULL;723 int rc;724 725 if (--st->ticks != 0) {726 if (pm121_readjust)727 goto readjust;728 return;729 }730 st->ticks = PM121_CPU_INTERVAL;731 732 fan_cpu = controls[FAN_CPU];733 734 rc = sensor_cpu_temp->ops->get_value(sensor_cpu_temp, &temp);735 if (rc) {736 printk(KERN_WARNING "pm121: CPU temp sensor error %d\n",737 rc);738 pm121_failure_state |= FAILURE_SENSOR;739 return;740 }741 742 rc = sensor_cpu_power->ops->get_value(sensor_cpu_power, &power);743 if (rc) {744 printk(KERN_WARNING "pm121: CPU power sensor error %d\n",745 rc);746 pm121_failure_state |= FAILURE_SENSOR;747 return;748 }749 750 pr_debug("pm121: CPU Fans tick ! CPU temp: %d.%03d°C, power: %d.%03d\n",751 FIX32TOPRINT(temp), FIX32TOPRINT(power));752 753 if (temp > st->pid.param.tmax)754 pm121_failure_state |= FAILURE_OVERTEMP;755 756 new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);757 758 /* correction */759 new_setpoint = pm121_correct(new_setpoint,760 FAN_CPU,761 st->pid.param.min);762 763 /* connected correction */764 new_setpoint = pm121_connect(FAN_CPU, new_setpoint);765 766 if (st->setpoint == new_setpoint)767 return;768 st->setpoint = new_setpoint;769 pr_debug("pm121: CPU corrected setpoint: %d RPM\n", (int)new_setpoint);770 771 readjust:772 if (fan_cpu && pm121_failure_state == 0) {773 rc = fan_cpu->ops->set_value(fan_cpu, st->setpoint);774 if (rc) {775 printk(KERN_WARNING "pm121: %s fan error %d\n",776 fan_cpu->name, rc);777 pm121_failure_state |= FAILURE_FAN;778 }779 }780}781 782/*783 * ****** Common ******784 *785 */786 787static void pm121_tick(void)788{789 unsigned int last_failure = pm121_failure_state;790 unsigned int new_failure;791 s32 total_power;792 int i;793 794 if (!pm121_started) {795 pr_debug("pm121: creating control loops !\n");796 for (i = 0; i < N_LOOPS; i++)797 pm121_create_sys_fans(i);798 799 pm121_create_cpu_fans();800 pm121_started = true;801 }802 803 /* skipping ticks */804 if (pm121_skipping && --pm121_skipping)805 return;806 807 /* compute average power */808 total_power = 0;809 for (i = 0; i < pm121_cpu_state->pid.param.history_len; i++)810 total_power += pm121_cpu_state->pid.powers[i];811 812 average_power = total_power / pm121_cpu_state->pid.param.history_len;813 814 815 pm121_failure_state = 0;816 for (i = 0 ; i < N_LOOPS; i++) {817 if (pm121_sys_state[i])818 pm121_sys_fans_tick(i);819 }820 821 if (pm121_cpu_state)822 pm121_cpu_fans_tick(pm121_cpu_state);823 824 pm121_readjust = 0;825 new_failure = pm121_failure_state & ~last_failure;826 827 /* If entering failure mode, clamp cpufreq and ramp all828 * fans to full speed.829 */830 if (pm121_failure_state && !last_failure) {831 for (i = 0; i < N_CONTROLS; i++) {832 if (controls[i])833 wf_control_set_max(controls[i]);834 }835 }836 837 /* If leaving failure mode, unclamp cpufreq and readjust838 * all fans on next iteration839 */840 if (!pm121_failure_state && last_failure) {841 if (controls[CPUFREQ])842 wf_control_set_min(controls[CPUFREQ]);843 pm121_readjust = 1;844 }845 846 /* Overtemp condition detected, notify and start skipping a couple847 * ticks to let the temperature go down848 */849 if (new_failure & FAILURE_OVERTEMP) {850 wf_set_overtemp();851 pm121_skipping = 2;852 pm121_overtemp = true;853 }854 855 /* We only clear the overtemp condition if overtemp is cleared856 * _and_ no other failure is present. Since a sensor error will857 * clear the overtemp condition (can't measure temperature) at858 * the control loop levels, but we don't want to keep it clear859 * here in this case860 */861 if (!pm121_failure_state && pm121_overtemp) {862 wf_clear_overtemp();863 pm121_overtemp = false;864 }865}866 867 868static struct wf_control* pm121_register_control(struct wf_control *ct,869 const char *match,870 unsigned int id)871{872 if (controls[id] == NULL && !strcmp(ct->name, match)) {873 if (wf_get_control(ct) == 0)874 controls[id] = ct;875 }876 return controls[id];877}878 879static void pm121_new_control(struct wf_control *ct)880{881 int all = 1;882 883 if (pm121_all_controls_ok)884 return;885 886 all = pm121_register_control(ct, "optical-drive-fan", FAN_OD) && all;887 all = pm121_register_control(ct, "hard-drive-fan", FAN_HD) && all;888 all = pm121_register_control(ct, "cpu-fan", FAN_CPU) && all;889 all = pm121_register_control(ct, "cpufreq-clamp", CPUFREQ) && all;890 891 if (all)892 pm121_all_controls_ok = 1;893}894 895 896 897 898static struct wf_sensor* pm121_register_sensor(struct wf_sensor *sensor,899 const char *match,900 struct wf_sensor **var)901{902 if (*var == NULL && !strcmp(sensor->name, match)) {903 if (wf_get_sensor(sensor) == 0)904 *var = sensor;905 }906 return *var;907}908 909static void pm121_new_sensor(struct wf_sensor *sr)910{911 int all = 1;912 913 if (pm121_all_sensors_ok)914 return;915 916 all = pm121_register_sensor(sr, "cpu-temp",917 &sensor_cpu_temp) && all;918 all = pm121_register_sensor(sr, "cpu-current",919 &sensor_cpu_current) && all;920 all = pm121_register_sensor(sr, "cpu-voltage",921 &sensor_cpu_voltage) && all;922 all = pm121_register_sensor(sr, "cpu-power",923 &sensor_cpu_power) && all;924 all = pm121_register_sensor(sr, "hard-drive-temp",925 &sensor_hard_drive_temp) && all;926 all = pm121_register_sensor(sr, "optical-drive-temp",927 &sensor_optical_drive_temp) && all;928 all = pm121_register_sensor(sr, "incoming-air-temp",929 &sensor_incoming_air_temp) && all;930 all = pm121_register_sensor(sr, "north-bridge-temp",931 &sensor_north_bridge_temp) && all;932 all = pm121_register_sensor(sr, "gpu-temp",933 &sensor_gpu_temp) && all;934 935 if (all)936 pm121_all_sensors_ok = 1;937}938 939 940 941static int pm121_notify(struct notifier_block *self,942 unsigned long event, void *data)943{944 switch (event) {945 case WF_EVENT_NEW_CONTROL:946 pr_debug("pm121: new control %s detected\n",947 ((struct wf_control *)data)->name);948 pm121_new_control(data);949 break;950 case WF_EVENT_NEW_SENSOR:951 pr_debug("pm121: new sensor %s detected\n",952 ((struct wf_sensor *)data)->name);953 pm121_new_sensor(data);954 break;955 case WF_EVENT_TICK:956 if (pm121_all_controls_ok && pm121_all_sensors_ok)957 pm121_tick();958 break;959 }960 961 return 0;962}963 964static struct notifier_block pm121_events = {965 .notifier_call = pm121_notify,966};967 968static int pm121_init_pm(void)969{970 const struct smu_sdbp_header *hdr;971 972 hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);973 if (hdr) {974 struct smu_sdbp_sensortree *st =975 (struct smu_sdbp_sensortree *)&hdr[1];976 pm121_mach_model = st->model_id;977 }978 979 pm121_connection = &pm121_connections[pm121_mach_model - 2];980 981 printk(KERN_INFO "pm121: Initializing for iMac G5 iSight model ID %d\n",982 pm121_mach_model);983 984 return 0;985}986 987 988static int pm121_probe(struct platform_device *ddev)989{990 wf_register_client(&pm121_events);991 992 return 0;993}994 995static void pm121_remove(struct platform_device *ddev)996{997 wf_unregister_client(&pm121_events);998}999 1000static struct platform_driver pm121_driver = {1001 .probe = pm121_probe,1002 .remove_new = pm121_remove,1003 .driver = {1004 .name = "windfarm",1005 .bus = &platform_bus_type,1006 },1007};1008 1009 1010static int __init pm121_init(void)1011{1012 int rc = -ENODEV;1013 1014 if (of_machine_is_compatible("PowerMac12,1"))1015 rc = pm121_init_pm();1016 1017 if (rc == 0) {1018 request_module("windfarm_smu_controls");1019 request_module("windfarm_smu_sensors");1020 request_module("windfarm_smu_sat");1021 request_module("windfarm_lm75_sensor");1022 request_module("windfarm_max6690_sensor");1023 request_module("windfarm_cpufreq_clamp");1024 platform_driver_register(&pm121_driver);1025 }1026 1027 return rc;1028}1029 1030static void __exit pm121_exit(void)1031{1032 1033 platform_driver_unregister(&pm121_driver);1034}1035 1036 1037module_init(pm121_init);1038module_exit(pm121_exit);1039 1040MODULE_AUTHOR("Étienne Bersac <bersace@gmail.com>");1041MODULE_DESCRIPTION("Thermal control logic for iMac G5 (iSight)");1042MODULE_LICENSE("GPL");1043 1044