811 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Windfarm PowerMac thermal control. iMac G54 *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 * PowerMac8,1 and PowerMac8,216 * ===========================17 *18 * System Fans control loop. Different based on models. In addition to the19 * usual PID algorithm, the control loop gets 2 additional pairs of linear20 * scaling factors (scale/offsets) expressed as 4.12 fixed point values21 * signed offset, unsigned scale)22 *23 * The targets are modified such as:24 * - the linked control (second control) gets the target value as-is25 * (typically the drive fan)26 * - the main control (first control) gets the target value scaled with27 * the first pair of factors, and is then modified as below28 * - the value of the target of the CPU Fan control loop is retrieved,29 * scaled with the second pair of factors, and the max of that and30 * the scaled target is applied to the main control.31 *32 * # model_id: 233 * controls : system-fan, drive-bay-fan34 * sensors : hd-temp35 * PID params : G_d = 0x1540000036 * G_p = 0x0020000037 * G_r = 0x000002fd38 * History = 2 entries39 * Input target = 0x3a000040 * Interval = 5s41 * linear-factors : offset = 0xff38 scale = 0x0ccd42 * offset = 0x0208 scale = 0x07ae43 *44 * # model_id: 345 * controls : system-fan, drive-bay-fan46 * sensors : hd-temp47 * PID params : G_d = 0x08e0000048 * G_p = 0x0056666649 * G_r = 0x0000072b50 * History = 2 entries51 * Input target = 0x35000052 * Interval = 5s53 * linear-factors : offset = 0xff38 scale = 0x0ccd54 * offset = 0x0000 scale = 0x000055 *56 * # model_id: 557 * controls : system-fan58 * sensors : hd-temp59 * PID params : G_d = 0x1540000060 * G_p = 0x0023333361 * G_r = 0x000002fd62 * History = 2 entries63 * Input target = 0x3a000064 * Interval = 5s65 * linear-factors : offset = 0x0000 scale = 0x100066 * offset = 0x0091 scale = 0x0bae67 *68 * CPU Fan control loop. The loop is identical for all models. it69 * has an additional pair of scaling factor. This is used to scale the70 * systems fan control loop target result (the one before it gets scaled71 * by the System Fans control loop itself). Then, the max value of the72 * calculated target value and system fan value is sent to the fans73 *74 * controls : cpu-fan75 * sensors : cpu-temp cpu-power76 * PID params : From SMU sdb partition77 * linear-factors : offset = 0xfb50 scale = 0x100078 *79 * CPU Slew control loop. Not implemented. The cpufreq driver in linux is80 * completely separate for now, though we could find a way to link it, either81 * as a client reacting to overtemp notifications, or directling monitoring82 * the CPU temperature83 *84 * WARNING ! The CPU control loop requires the CPU tmax for the current85 * operating point. However, we currently are completely separated from86 * the cpufreq driver and thus do not know what the current operating87 * point is. Fortunately, we also do not have any hardware supporting anything88 * but operating point 0 at the moment, thus we just peek that value directly89 * from the SDB partition. If we ever end up with actually slewing the system90 * clock and thus changing operating points, we'll have to find a way to91 * communicate with the CPU freq driver;92 */93 94#include <linux/types.h>95#include <linux/errno.h>96#include <linux/kernel.h>97#include <linux/delay.h>98#include <linux/slab.h>99#include <linux/init.h>100#include <linux/spinlock.h>101#include <linux/wait.h>102#include <linux/kmod.h>103#include <linux/device.h>104#include <linux/platform_device.h>105#include <linux/of.h>106 107#include <asm/machdep.h>108#include <asm/io.h>109#include <asm/sections.h>110#include <asm/smu.h>111 112#include "windfarm.h"113#include "windfarm_pid.h"114 115#define VERSION "0.4"116 117#undef DEBUG118 119#ifdef DEBUG120#define DBG(args...) printk(args)121#else122#define DBG(args...) do { } while(0)123#endif124 125/* define this to force CPU overtemp to 74 degree, useful for testing126 * the overtemp code127 */128#undef HACKED_OVERTEMP129 130static int wf_smu_mach_model; /* machine model id */131 132/* Controls & sensors */133static struct wf_sensor *sensor_cpu_power;134static struct wf_sensor *sensor_cpu_temp;135static struct wf_sensor *sensor_hd_temp;136static struct wf_control *fan_cpu_main;137static struct wf_control *fan_hd;138static struct wf_control *fan_system;139static struct wf_control *cpufreq_clamp;140 141/* Set to kick the control loop into life */142static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok;143static bool wf_smu_started;144 145/* Failure handling.. could be nicer */146#define FAILURE_FAN 0x01147#define FAILURE_SENSOR 0x02148#define FAILURE_OVERTEMP 0x04149 150static unsigned int wf_smu_failure_state;151static int wf_smu_readjust, wf_smu_skipping;152static bool wf_smu_overtemp;153 154/*155 * ****** System Fans Control Loop ******156 *157 */158 159/* Parameters for the System Fans control loop. Parameters160 * not in this table such as interval, history size, ...161 * are common to all versions and thus hard coded for now.162 */163struct wf_smu_sys_fans_param {164 int model_id;165 s32 itarget;166 s32 gd, gp, gr;167 168 s16 offset0;169 u16 scale0;170 s16 offset1;171 u16 scale1;172};173 174#define WF_SMU_SYS_FANS_INTERVAL 5175#define WF_SMU_SYS_FANS_HISTORY_SIZE 2176 177/* State data used by the system fans control loop178 */179struct wf_smu_sys_fans_state {180 int ticks;181 s32 sys_setpoint;182 s32 hd_setpoint;183 s16 offset0;184 u16 scale0;185 s16 offset1;186 u16 scale1;187 struct wf_pid_state pid;188};189 190/*191 * Configs for SMU System Fan control loop192 */193static struct wf_smu_sys_fans_param wf_smu_sys_all_params[] = {194 /* Model ID 2 */195 {196 .model_id = 2,197 .itarget = 0x3a0000,198 .gd = 0x15400000,199 .gp = 0x00200000,200 .gr = 0x000002fd,201 .offset0 = 0xff38,202 .scale0 = 0x0ccd,203 .offset1 = 0x0208,204 .scale1 = 0x07ae,205 },206 /* Model ID 3 */207 {208 .model_id = 3,209 .itarget = 0x350000,210 .gd = 0x08e00000,211 .gp = 0x00566666,212 .gr = 0x0000072b,213 .offset0 = 0xff38,214 .scale0 = 0x0ccd,215 .offset1 = 0x0000,216 .scale1 = 0x0000,217 },218 /* Model ID 5 */219 {220 .model_id = 5,221 .itarget = 0x3a0000,222 .gd = 0x15400000,223 .gp = 0x00233333,224 .gr = 0x000002fd,225 .offset0 = 0x0000,226 .scale0 = 0x1000,227 .offset1 = 0x0091,228 .scale1 = 0x0bae,229 },230};231#define WF_SMU_SYS_FANS_NUM_CONFIGS ARRAY_SIZE(wf_smu_sys_all_params)232 233static struct wf_smu_sys_fans_state *wf_smu_sys_fans;234 235/*236 * ****** CPU Fans Control Loop ******237 *238 */239 240 241#define WF_SMU_CPU_FANS_INTERVAL 1242#define WF_SMU_CPU_FANS_MAX_HISTORY 16243#define WF_SMU_CPU_FANS_SIBLING_SCALE 0x00001000244#define WF_SMU_CPU_FANS_SIBLING_OFFSET 0xfffffb50245 246/* State data used by the cpu fans control loop247 */248struct wf_smu_cpu_fans_state {249 int ticks;250 s32 cpu_setpoint;251 s32 scale;252 s32 offset;253 struct wf_cpu_pid_state pid;254};255 256static struct wf_smu_cpu_fans_state *wf_smu_cpu_fans;257 258 259 260/*261 * ***** Implementation *****262 *263 */264 265static void wf_smu_create_sys_fans(void)266{267 struct wf_smu_sys_fans_param *param = NULL;268 struct wf_pid_param pid_param;269 int i;270 271 /* First, locate the params for this model */272 for (i = 0; i < WF_SMU_SYS_FANS_NUM_CONFIGS; i++)273 if (wf_smu_sys_all_params[i].model_id == wf_smu_mach_model) {274 param = &wf_smu_sys_all_params[i];275 break;276 }277 278 /* No params found, put fans to max */279 if (param == NULL) {280 printk(KERN_WARNING "windfarm: System fan config not found "281 "for this machine model, max fan speed\n");282 goto fail;283 }284 285 /* Alloc & initialize state */286 wf_smu_sys_fans = kmalloc(sizeof(struct wf_smu_sys_fans_state),287 GFP_KERNEL);288 if (wf_smu_sys_fans == NULL) {289 printk(KERN_WARNING "windfarm: Memory allocation error"290 " max fan speed\n");291 goto fail;292 }293 wf_smu_sys_fans->ticks = 1;294 wf_smu_sys_fans->scale0 = param->scale0;295 wf_smu_sys_fans->offset0 = param->offset0;296 wf_smu_sys_fans->scale1 = param->scale1;297 wf_smu_sys_fans->offset1 = param->offset1;298 299 /* Fill PID params */300 pid_param.gd = param->gd;301 pid_param.gp = param->gp;302 pid_param.gr = param->gr;303 pid_param.interval = WF_SMU_SYS_FANS_INTERVAL;304 pid_param.history_len = WF_SMU_SYS_FANS_HISTORY_SIZE;305 pid_param.itarget = param->itarget;306 pid_param.min = wf_control_get_min(fan_system);307 pid_param.max = wf_control_get_max(fan_system);308 if (fan_hd) {309 pid_param.min =310 max(pid_param.min, wf_control_get_min(fan_hd));311 pid_param.max =312 min(pid_param.max, wf_control_get_max(fan_hd));313 }314 wf_pid_init(&wf_smu_sys_fans->pid, &pid_param);315 316 DBG("wf: System Fan control initialized.\n");317 DBG(" itarged=%d.%03d, min=%d RPM, max=%d RPM\n",318 FIX32TOPRINT(pid_param.itarget), pid_param.min, pid_param.max);319 return;320 321 fail:322 323 if (fan_system)324 wf_control_set_max(fan_system);325 if (fan_hd)326 wf_control_set_max(fan_hd);327}328 329static void wf_smu_sys_fans_tick(struct wf_smu_sys_fans_state *st)330{331 s32 new_setpoint, temp, scaled, cputarget;332 int rc;333 334 if (--st->ticks != 0) {335 if (wf_smu_readjust)336 goto readjust;337 return;338 }339 st->ticks = WF_SMU_SYS_FANS_INTERVAL;340 341 rc = wf_sensor_get(sensor_hd_temp, &temp);342 if (rc) {343 printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",344 rc);345 wf_smu_failure_state |= FAILURE_SENSOR;346 return;347 }348 349 DBG("wf_smu: System Fans tick ! HD temp: %d.%03d\n",350 FIX32TOPRINT(temp));351 352 if (temp > (st->pid.param.itarget + 0x50000))353 wf_smu_failure_state |= FAILURE_OVERTEMP;354 355 new_setpoint = wf_pid_run(&st->pid, temp);356 357 DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);358 359 scaled = ((((s64)new_setpoint) * (s64)st->scale0) >> 12) + st->offset0;360 361 DBG("wf_smu: scaled setpoint: %d RPM\n", (int)scaled);362 363 cputarget = wf_smu_cpu_fans ? wf_smu_cpu_fans->pid.target : 0;364 cputarget = ((((s64)cputarget) * (s64)st->scale1) >> 12) + st->offset1;365 scaled = max(scaled, cputarget);366 scaled = max(scaled, st->pid.param.min);367 scaled = min(scaled, st->pid.param.max);368 369 DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)scaled);370 371 if (st->sys_setpoint == scaled && new_setpoint == st->hd_setpoint)372 return;373 st->sys_setpoint = scaled;374 st->hd_setpoint = new_setpoint;375 readjust:376 if (fan_system && wf_smu_failure_state == 0) {377 rc = wf_control_set(fan_system, st->sys_setpoint);378 if (rc) {379 printk(KERN_WARNING "windfarm: Sys fan error %d\n",380 rc);381 wf_smu_failure_state |= FAILURE_FAN;382 }383 }384 if (fan_hd && wf_smu_failure_state == 0) {385 rc = wf_control_set(fan_hd, st->hd_setpoint);386 if (rc) {387 printk(KERN_WARNING "windfarm: HD fan error %d\n",388 rc);389 wf_smu_failure_state |= FAILURE_FAN;390 }391 }392}393 394static void wf_smu_create_cpu_fans(void)395{396 struct wf_cpu_pid_param pid_param;397 const struct smu_sdbp_header *hdr;398 struct smu_sdbp_cpupiddata *piddata;399 struct smu_sdbp_fvt *fvt;400 s32 tmax, tdelta, maxpow, powadj;401 402 /* First, locate the PID params in SMU SBD */403 hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);404 if (!hdr) {405 printk(KERN_WARNING "windfarm: CPU PID fan config not found "406 "max fan speed\n");407 goto fail;408 }409 piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];410 411 /* Get the FVT params for operating point 0 (the only supported one412 * for now) in order to get tmax413 */414 hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);415 if (hdr) {416 fvt = (struct smu_sdbp_fvt *)&hdr[1];417 tmax = ((s32)fvt->maxtemp) << 16;418 } else419 tmax = 0x5e0000; /* 94 degree default */420 421 /* Alloc & initialize state */422 wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state),423 GFP_KERNEL);424 if (wf_smu_cpu_fans == NULL)425 goto fail;426 wf_smu_cpu_fans->ticks = 1;427 428 wf_smu_cpu_fans->scale = WF_SMU_CPU_FANS_SIBLING_SCALE;429 wf_smu_cpu_fans->offset = WF_SMU_CPU_FANS_SIBLING_OFFSET;430 431 /* Fill PID params */432 pid_param.interval = WF_SMU_CPU_FANS_INTERVAL;433 pid_param.history_len = piddata->history_len;434 if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {435 printk(KERN_WARNING "windfarm: History size overflow on "436 "CPU control loop (%d)\n", piddata->history_len);437 pid_param.history_len = WF_CPU_PID_MAX_HISTORY;438 }439 pid_param.gd = piddata->gd;440 pid_param.gp = piddata->gp;441 pid_param.gr = piddata->gr / pid_param.history_len;442 443 tdelta = ((s32)piddata->target_temp_delta) << 16;444 maxpow = ((s32)piddata->max_power) << 16;445 powadj = ((s32)piddata->power_adj) << 16;446 447 pid_param.tmax = tmax;448 pid_param.ttarget = tmax - tdelta;449 pid_param.pmaxadj = maxpow - powadj;450 451 pid_param.min = wf_control_get_min(fan_cpu_main);452 pid_param.max = wf_control_get_max(fan_cpu_main);453 454 wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);455 456 DBG("wf: CPU Fan control initialized.\n");457 DBG(" ttarget=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM\n",458 FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),459 pid_param.min, pid_param.max);460 461 return;462 463 fail:464 printk(KERN_WARNING "windfarm: CPU fan config not found\n"465 "for this machine model, max fan speed\n");466 467 if (cpufreq_clamp)468 wf_control_set_max(cpufreq_clamp);469 if (fan_cpu_main)470 wf_control_set_max(fan_cpu_main);471}472 473static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)474{475 s32 new_setpoint, temp, power, systarget;476 int rc;477 478 if (--st->ticks != 0) {479 if (wf_smu_readjust)480 goto readjust;481 return;482 }483 st->ticks = WF_SMU_CPU_FANS_INTERVAL;484 485 rc = wf_sensor_get(sensor_cpu_temp, &temp);486 if (rc) {487 printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",488 rc);489 wf_smu_failure_state |= FAILURE_SENSOR;490 return;491 }492 493 rc = wf_sensor_get(sensor_cpu_power, &power);494 if (rc) {495 printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",496 rc);497 wf_smu_failure_state |= FAILURE_SENSOR;498 return;499 }500 501 DBG("wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d\n",502 FIX32TOPRINT(temp), FIX32TOPRINT(power));503 504#ifdef HACKED_OVERTEMP505 if (temp > 0x4a0000)506 wf_smu_failure_state |= FAILURE_OVERTEMP;507#else508 if (temp > st->pid.param.tmax)509 wf_smu_failure_state |= FAILURE_OVERTEMP;510#endif511 new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);512 513 DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);514 515 systarget = wf_smu_sys_fans ? wf_smu_sys_fans->pid.target : 0;516 systarget = ((((s64)systarget) * (s64)st->scale) >> 12)517 + st->offset;518 new_setpoint = max(new_setpoint, systarget);519 new_setpoint = max(new_setpoint, st->pid.param.min);520 new_setpoint = min(new_setpoint, st->pid.param.max);521 522 DBG("wf_smu: adjusted setpoint: %d RPM\n", (int)new_setpoint);523 524 if (st->cpu_setpoint == new_setpoint)525 return;526 st->cpu_setpoint = new_setpoint;527 readjust:528 if (fan_cpu_main && wf_smu_failure_state == 0) {529 rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);530 if (rc) {531 printk(KERN_WARNING "windfarm: CPU main fan"532 " error %d\n", rc);533 wf_smu_failure_state |= FAILURE_FAN;534 }535 }536}537 538/*539 * ****** Setup / Init / Misc ... ******540 *541 */542 543static void wf_smu_tick(void)544{545 unsigned int last_failure = wf_smu_failure_state;546 unsigned int new_failure;547 548 if (!wf_smu_started) {549 DBG("wf: creating control loops !\n");550 wf_smu_create_sys_fans();551 wf_smu_create_cpu_fans();552 wf_smu_started = true;553 }554 555 /* Skipping ticks */556 if (wf_smu_skipping && --wf_smu_skipping)557 return;558 559 wf_smu_failure_state = 0;560 if (wf_smu_sys_fans)561 wf_smu_sys_fans_tick(wf_smu_sys_fans);562 if (wf_smu_cpu_fans)563 wf_smu_cpu_fans_tick(wf_smu_cpu_fans);564 565 wf_smu_readjust = 0;566 new_failure = wf_smu_failure_state & ~last_failure;567 568 /* If entering failure mode, clamp cpufreq and ramp all569 * fans to full speed.570 */571 if (wf_smu_failure_state && !last_failure) {572 if (cpufreq_clamp)573 wf_control_set_max(cpufreq_clamp);574 if (fan_system)575 wf_control_set_max(fan_system);576 if (fan_cpu_main)577 wf_control_set_max(fan_cpu_main);578 if (fan_hd)579 wf_control_set_max(fan_hd);580 }581 582 /* If leaving failure mode, unclamp cpufreq and readjust583 * all fans on next iteration584 */585 if (!wf_smu_failure_state && last_failure) {586 if (cpufreq_clamp)587 wf_control_set_min(cpufreq_clamp);588 wf_smu_readjust = 1;589 }590 591 /* Overtemp condition detected, notify and start skipping a couple592 * ticks to let the temperature go down593 */594 if (new_failure & FAILURE_OVERTEMP) {595 wf_set_overtemp();596 wf_smu_skipping = 2;597 wf_smu_overtemp = true;598 }599 600 /* We only clear the overtemp condition if overtemp is cleared601 * _and_ no other failure is present. Since a sensor error will602 * clear the overtemp condition (can't measure temperature) at603 * the control loop levels, but we don't want to keep it clear604 * here in this case605 */606 if (!wf_smu_failure_state && wf_smu_overtemp) {607 wf_clear_overtemp();608 wf_smu_overtemp = false;609 }610}611 612static void wf_smu_new_control(struct wf_control *ct)613{614 if (wf_smu_all_controls_ok)615 return;616 617 if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-fan")) {618 if (wf_get_control(ct) == 0)619 fan_cpu_main = ct;620 }621 622 if (fan_system == NULL && !strcmp(ct->name, "system-fan")) {623 if (wf_get_control(ct) == 0)624 fan_system = ct;625 }626 627 if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {628 if (wf_get_control(ct) == 0)629 cpufreq_clamp = ct;630 }631 632 /* Darwin property list says the HD fan is only for model ID633 * 0, 1, 2 and 3634 */635 636 if (wf_smu_mach_model > 3) {637 if (fan_system && fan_cpu_main && cpufreq_clamp)638 wf_smu_all_controls_ok = 1;639 return;640 }641 642 if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) {643 if (wf_get_control(ct) == 0)644 fan_hd = ct;645 }646 647 if (fan_system && fan_hd && fan_cpu_main && cpufreq_clamp)648 wf_smu_all_controls_ok = 1;649}650 651static void wf_smu_new_sensor(struct wf_sensor *sr)652{653 if (wf_smu_all_sensors_ok)654 return;655 656 if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) {657 if (wf_get_sensor(sr) == 0)658 sensor_cpu_power = sr;659 }660 661 if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) {662 if (wf_get_sensor(sr) == 0)663 sensor_cpu_temp = sr;664 }665 666 if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) {667 if (wf_get_sensor(sr) == 0)668 sensor_hd_temp = sr;669 }670 671 if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp)672 wf_smu_all_sensors_ok = 1;673}674 675 676static int wf_smu_notify(struct notifier_block *self,677 unsigned long event, void *data)678{679 switch(event) {680 case WF_EVENT_NEW_CONTROL:681 DBG("wf: new control %s detected\n",682 ((struct wf_control *)data)->name);683 wf_smu_new_control(data);684 wf_smu_readjust = 1;685 break;686 case WF_EVENT_NEW_SENSOR:687 DBG("wf: new sensor %s detected\n",688 ((struct wf_sensor *)data)->name);689 wf_smu_new_sensor(data);690 break;691 case WF_EVENT_TICK:692 if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok)693 wf_smu_tick();694 }695 696 return 0;697}698 699static struct notifier_block wf_smu_events = {700 .notifier_call = wf_smu_notify,701};702 703static int wf_init_pm(void)704{705 const struct smu_sdbp_header *hdr;706 707 hdr = smu_get_sdb_partition(SMU_SDB_SENSORTREE_ID, NULL);708 if (hdr) {709 struct smu_sdbp_sensortree *st =710 (struct smu_sdbp_sensortree *)&hdr[1];711 wf_smu_mach_model = st->model_id;712 }713 714 printk(KERN_INFO "windfarm: Initializing for iMacG5 model ID %d\n",715 wf_smu_mach_model);716 717 return 0;718}719 720static int wf_smu_probe(struct platform_device *ddev)721{722 wf_register_client(&wf_smu_events);723 724 return 0;725}726 727static void wf_smu_remove(struct platform_device *ddev)728{729 wf_unregister_client(&wf_smu_events);730 731 /* XXX We don't have yet a guarantee that our callback isn't732 * in progress when returning from wf_unregister_client, so733 * we add an arbitrary delay. I'll have to fix that in the core734 */735 msleep(1000);736 737 /* Release all sensors */738 /* One more crappy race: I don't think we have any guarantee here739 * that the attribute callback won't race with the sensor beeing740 * disposed of, and I'm not 100% certain what best way to deal741 * with that except by adding locks all over... I'll do that742 * eventually but heh, who ever rmmod this module anyway ?743 */744 if (sensor_cpu_power)745 wf_put_sensor(sensor_cpu_power);746 if (sensor_cpu_temp)747 wf_put_sensor(sensor_cpu_temp);748 if (sensor_hd_temp)749 wf_put_sensor(sensor_hd_temp);750 751 /* Release all controls */752 if (fan_cpu_main)753 wf_put_control(fan_cpu_main);754 if (fan_hd)755 wf_put_control(fan_hd);756 if (fan_system)757 wf_put_control(fan_system);758 if (cpufreq_clamp)759 wf_put_control(cpufreq_clamp);760 761 /* Destroy control loops state structures */762 kfree(wf_smu_sys_fans);763 kfree(wf_smu_cpu_fans);764}765 766static struct platform_driver wf_smu_driver = {767 .probe = wf_smu_probe,768 .remove_new = wf_smu_remove,769 .driver = {770 .name = "windfarm",771 },772};773 774 775static int __init wf_smu_init(void)776{777 int rc = -ENODEV;778 779 if (of_machine_is_compatible("PowerMac8,1") ||780 of_machine_is_compatible("PowerMac8,2"))781 rc = wf_init_pm();782 783 if (rc == 0) {784#ifdef MODULE785 request_module("windfarm_smu_controls");786 request_module("windfarm_smu_sensors");787 request_module("windfarm_lm75_sensor");788 request_module("windfarm_cpufreq_clamp");789 790#endif /* MODULE */791 platform_driver_register(&wf_smu_driver);792 }793 794 return rc;795}796 797static void __exit wf_smu_exit(void)798{799 800 platform_driver_unregister(&wf_smu_driver);801}802 803 804module_init(wf_smu_init);805module_exit(wf_smu_exit);806 807MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");808MODULE_DESCRIPTION("Thermal control logic for iMac G5");809MODULE_LICENSE("GPL");810MODULE_ALIAS("platform:windfarm");811