Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Windfarm PowerMac thermal control. SMU based 1 CPU desktop control loops
0004  *
0005  * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
0006  *                    <benh@kernel.crashing.org>
0007  *
0008  * The algorithm used is the PID control algorithm, used the same
0009  * way the published Darwin code does, using the same values that
0010  * are present in the Darwin 8.2 snapshot property lists (note however
0011  * that none of the code has been re-used, it's a complete re-implementation
0012  *
0013  * The various control loops found in Darwin config file are:
0014  *
0015  * PowerMac9,1
0016  * ===========
0017  *
0018  * Has 3 control loops: CPU fans is similar to PowerMac8,1 (though it doesn't
0019  * try to play with other control loops fans). Drive bay is rather basic PID
0020  * with one sensor and one fan. Slots area is a bit different as the Darwin
0021  * driver is supposed to be capable of working in a special "AGP" mode which
0022  * involves the presence of an AGP sensor and an AGP fan (possibly on the
0023  * AGP card itself). I can't deal with that special mode as I don't have
0024  * access to those additional sensor/fans for now (though ultimately, it would
0025  * be possible to add sensor objects for them) so I'm only implementing the
0026  * basic PCI slot control loop
0027  */
0028 
0029 #include <linux/types.h>
0030 #include <linux/errno.h>
0031 #include <linux/kernel.h>
0032 #include <linux/delay.h>
0033 #include <linux/slab.h>
0034 #include <linux/init.h>
0035 #include <linux/spinlock.h>
0036 #include <linux/wait.h>
0037 #include <linux/kmod.h>
0038 #include <linux/device.h>
0039 #include <linux/platform_device.h>
0040 #include <linux/of.h>
0041 
0042 #include <asm/machdep.h>
0043 #include <asm/io.h>
0044 #include <asm/sections.h>
0045 #include <asm/smu.h>
0046 
0047 #include "windfarm.h"
0048 #include "windfarm_pid.h"
0049 
0050 #define VERSION "0.4"
0051 
0052 #undef DEBUG
0053 
0054 #ifdef DEBUG
0055 #define DBG(args...)    printk(args)
0056 #else
0057 #define DBG(args...)    do { } while(0)
0058 #endif
0059 
0060 /* define this to force CPU overtemp to 74 degree, useful for testing
0061  * the overtemp code
0062  */
0063 #undef HACKED_OVERTEMP
0064 
0065 /* Controls & sensors */
0066 static struct wf_sensor *sensor_cpu_power;
0067 static struct wf_sensor *sensor_cpu_temp;
0068 static struct wf_sensor *sensor_hd_temp;
0069 static struct wf_sensor *sensor_slots_power;
0070 static struct wf_control *fan_cpu_main;
0071 static struct wf_control *fan_cpu_second;
0072 static struct wf_control *fan_cpu_third;
0073 static struct wf_control *fan_hd;
0074 static struct wf_control *fan_slots;
0075 static struct wf_control *cpufreq_clamp;
0076 
0077 /* Set to kick the control loop into life */
0078 static int wf_smu_all_controls_ok, wf_smu_all_sensors_ok;
0079 static bool wf_smu_started;
0080 static bool wf_smu_overtemp;
0081 
0082 /* Failure handling.. could be nicer */
0083 #define FAILURE_FAN     0x01
0084 #define FAILURE_SENSOR      0x02
0085 #define FAILURE_OVERTEMP    0x04
0086 
0087 static unsigned int wf_smu_failure_state;
0088 static int wf_smu_readjust, wf_smu_skipping;
0089 
0090 /*
0091  * ****** CPU Fans Control Loop ******
0092  *
0093  */
0094 
0095 
0096 #define WF_SMU_CPU_FANS_INTERVAL    1
0097 #define WF_SMU_CPU_FANS_MAX_HISTORY 16
0098 
0099 /* State data used by the cpu fans control loop
0100  */
0101 struct wf_smu_cpu_fans_state {
0102     int         ticks;
0103     s32         cpu_setpoint;
0104     struct wf_cpu_pid_state pid;
0105 };
0106 
0107 static struct wf_smu_cpu_fans_state *wf_smu_cpu_fans;
0108 
0109 
0110 
0111 /*
0112  * ****** Drive Fan Control Loop ******
0113  *
0114  */
0115 
0116 struct wf_smu_drive_fans_state {
0117     int         ticks;
0118     s32         setpoint;
0119     struct wf_pid_state pid;
0120 };
0121 
0122 static struct wf_smu_drive_fans_state *wf_smu_drive_fans;
0123 
0124 /*
0125  * ****** Slots Fan Control Loop ******
0126  *
0127  */
0128 
0129 struct wf_smu_slots_fans_state {
0130     int         ticks;
0131     s32         setpoint;
0132     struct wf_pid_state pid;
0133 };
0134 
0135 static struct wf_smu_slots_fans_state *wf_smu_slots_fans;
0136 
0137 /*
0138  * ***** Implementation *****
0139  *
0140  */
0141 
0142 
0143 static void wf_smu_create_cpu_fans(void)
0144 {
0145     struct wf_cpu_pid_param pid_param;
0146     const struct smu_sdbp_header *hdr;
0147     struct smu_sdbp_cpupiddata *piddata;
0148     struct smu_sdbp_fvt *fvt;
0149     s32 tmax, tdelta, maxpow, powadj;
0150 
0151     /* First, locate the PID params in SMU SBD */
0152     hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL);
0153     if (hdr == 0) {
0154         printk(KERN_WARNING "windfarm: CPU PID fan config not found "
0155                "max fan speed\n");
0156         goto fail;
0157     }
0158     piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
0159 
0160     /* Get the FVT params for operating point 0 (the only supported one
0161      * for now) in order to get tmax
0162      */
0163     hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL);
0164     if (hdr) {
0165         fvt = (struct smu_sdbp_fvt *)&hdr[1];
0166         tmax = ((s32)fvt->maxtemp) << 16;
0167     } else
0168         tmax = 0x5e0000; /* 94 degree default */
0169 
0170     /* Alloc & initialize state */
0171     wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state),
0172                   GFP_KERNEL);
0173     if (wf_smu_cpu_fans == NULL)
0174         goto fail;
0175         wf_smu_cpu_fans->ticks = 1;
0176 
0177     /* Fill PID params */
0178     pid_param.interval = WF_SMU_CPU_FANS_INTERVAL;
0179     pid_param.history_len = piddata->history_len;
0180     if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
0181         printk(KERN_WARNING "windfarm: History size overflow on "
0182                "CPU control loop (%d)\n", piddata->history_len);
0183         pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
0184     }
0185     pid_param.gd = piddata->gd;
0186     pid_param.gp = piddata->gp;
0187     pid_param.gr = piddata->gr / pid_param.history_len;
0188 
0189     tdelta = ((s32)piddata->target_temp_delta) << 16;
0190     maxpow = ((s32)piddata->max_power) << 16;
0191     powadj = ((s32)piddata->power_adj) << 16;
0192 
0193     pid_param.tmax = tmax;
0194     pid_param.ttarget = tmax - tdelta;
0195     pid_param.pmaxadj = maxpow - powadj;
0196 
0197     pid_param.min = wf_control_get_min(fan_cpu_main);
0198     pid_param.max = wf_control_get_max(fan_cpu_main);
0199 
0200     wf_cpu_pid_init(&wf_smu_cpu_fans->pid, &pid_param);
0201 
0202     DBG("wf: CPU Fan control initialized.\n");
0203     DBG("    ttarget=%d.%03d, tmax=%d.%03d, min=%d RPM, max=%d RPM\n",
0204         FIX32TOPRINT(pid_param.ttarget), FIX32TOPRINT(pid_param.tmax),
0205         pid_param.min, pid_param.max);
0206 
0207     return;
0208 
0209  fail:
0210     printk(KERN_WARNING "windfarm: CPU fan config not found\n"
0211            "for this machine model, max fan speed\n");
0212 
0213     if (cpufreq_clamp)
0214         wf_control_set_max(cpufreq_clamp);
0215     if (fan_cpu_main)
0216         wf_control_set_max(fan_cpu_main);
0217 }
0218 
0219 static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st)
0220 {
0221     s32 new_setpoint, temp, power;
0222     int rc;
0223 
0224     if (--st->ticks != 0) {
0225         if (wf_smu_readjust)
0226             goto readjust;
0227         return;
0228     }
0229     st->ticks = WF_SMU_CPU_FANS_INTERVAL;
0230 
0231     rc = wf_sensor_get(sensor_cpu_temp, &temp);
0232     if (rc) {
0233         printk(KERN_WARNING "windfarm: CPU temp sensor error %d\n",
0234                rc);
0235         wf_smu_failure_state |= FAILURE_SENSOR;
0236         return;
0237     }
0238 
0239     rc = wf_sensor_get(sensor_cpu_power, &power);
0240     if (rc) {
0241         printk(KERN_WARNING "windfarm: CPU power sensor error %d\n",
0242                rc);
0243         wf_smu_failure_state |= FAILURE_SENSOR;
0244         return;
0245     }
0246 
0247     DBG("wf_smu: CPU Fans tick ! CPU temp: %d.%03d, power: %d.%03d\n",
0248         FIX32TOPRINT(temp), FIX32TOPRINT(power));
0249 
0250 #ifdef HACKED_OVERTEMP
0251     if (temp > 0x4a0000)
0252         wf_smu_failure_state |= FAILURE_OVERTEMP;
0253 #else
0254     if (temp > st->pid.param.tmax)
0255         wf_smu_failure_state |= FAILURE_OVERTEMP;
0256 #endif
0257     new_setpoint = wf_cpu_pid_run(&st->pid, power, temp);
0258 
0259     DBG("wf_smu: new_setpoint: %d RPM\n", (int)new_setpoint);
0260 
0261     if (st->cpu_setpoint == new_setpoint)
0262         return;
0263     st->cpu_setpoint = new_setpoint;
0264  readjust:
0265     if (fan_cpu_main && wf_smu_failure_state == 0) {
0266         rc = wf_control_set(fan_cpu_main, st->cpu_setpoint);
0267         if (rc) {
0268             printk(KERN_WARNING "windfarm: CPU main fan"
0269                    " error %d\n", rc);
0270             wf_smu_failure_state |= FAILURE_FAN;
0271         }
0272     }
0273     if (fan_cpu_second && wf_smu_failure_state == 0) {
0274         rc = wf_control_set(fan_cpu_second, st->cpu_setpoint);
0275         if (rc) {
0276             printk(KERN_WARNING "windfarm: CPU second fan"
0277                    " error %d\n", rc);
0278             wf_smu_failure_state |= FAILURE_FAN;
0279         }
0280     }
0281     if (fan_cpu_third && wf_smu_failure_state == 0) {
0282         rc = wf_control_set(fan_cpu_third, st->cpu_setpoint);
0283         if (rc) {
0284             printk(KERN_WARNING "windfarm: CPU third fan"
0285                    " error %d\n", rc);
0286             wf_smu_failure_state |= FAILURE_FAN;
0287         }
0288     }
0289 }
0290 
0291 static void wf_smu_create_drive_fans(void)
0292 {
0293     struct wf_pid_param param = {
0294         .interval   = 5,
0295         .history_len    = 2,
0296         .gd     = 0x01e00000,
0297         .gp     = 0x00500000,
0298         .gr     = 0x00000000,
0299         .itarget    = 0x00200000,
0300     };
0301 
0302     /* Alloc & initialize state */
0303     wf_smu_drive_fans = kmalloc(sizeof(struct wf_smu_drive_fans_state),
0304                     GFP_KERNEL);
0305     if (wf_smu_drive_fans == NULL) {
0306         printk(KERN_WARNING "windfarm: Memory allocation error"
0307                " max fan speed\n");
0308         goto fail;
0309     }
0310         wf_smu_drive_fans->ticks = 1;
0311 
0312     /* Fill PID params */
0313     param.additive = (fan_hd->type == WF_CONTROL_RPM_FAN);
0314     param.min = wf_control_get_min(fan_hd);
0315     param.max = wf_control_get_max(fan_hd);
0316     wf_pid_init(&wf_smu_drive_fans->pid, &param);
0317 
0318     DBG("wf: Drive Fan control initialized.\n");
0319     DBG("    itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
0320         FIX32TOPRINT(param.itarget), param.min, param.max);
0321     return;
0322 
0323  fail:
0324     if (fan_hd)
0325         wf_control_set_max(fan_hd);
0326 }
0327 
0328 static void wf_smu_drive_fans_tick(struct wf_smu_drive_fans_state *st)
0329 {
0330     s32 new_setpoint, temp;
0331     int rc;
0332 
0333     if (--st->ticks != 0) {
0334         if (wf_smu_readjust)
0335             goto readjust;
0336         return;
0337     }
0338     st->ticks = st->pid.param.interval;
0339 
0340     rc = wf_sensor_get(sensor_hd_temp, &temp);
0341     if (rc) {
0342         printk(KERN_WARNING "windfarm: HD temp sensor error %d\n",
0343                rc);
0344         wf_smu_failure_state |= FAILURE_SENSOR;
0345         return;
0346     }
0347 
0348     DBG("wf_smu: Drive Fans tick ! HD temp: %d.%03d\n",
0349         FIX32TOPRINT(temp));
0350 
0351     if (temp > (st->pid.param.itarget + 0x50000))
0352         wf_smu_failure_state |= FAILURE_OVERTEMP;
0353 
0354     new_setpoint = wf_pid_run(&st->pid, temp);
0355 
0356     DBG("wf_smu: new_setpoint: %d\n", (int)new_setpoint);
0357 
0358     if (st->setpoint == new_setpoint)
0359         return;
0360     st->setpoint = new_setpoint;
0361  readjust:
0362     if (fan_hd && wf_smu_failure_state == 0) {
0363         rc = wf_control_set(fan_hd, st->setpoint);
0364         if (rc) {
0365             printk(KERN_WARNING "windfarm: HD fan error %d\n",
0366                    rc);
0367             wf_smu_failure_state |= FAILURE_FAN;
0368         }
0369     }
0370 }
0371 
0372 static void wf_smu_create_slots_fans(void)
0373 {
0374     struct wf_pid_param param = {
0375         .interval   = 1,
0376         .history_len    = 8,
0377         .gd     = 0x00000000,
0378         .gp     = 0x00000000,
0379         .gr     = 0x00020000,
0380         .itarget    = 0x00000000
0381     };
0382 
0383     /* Alloc & initialize state */
0384     wf_smu_slots_fans = kmalloc(sizeof(struct wf_smu_slots_fans_state),
0385                     GFP_KERNEL);
0386     if (wf_smu_slots_fans == NULL) {
0387         printk(KERN_WARNING "windfarm: Memory allocation error"
0388                " max fan speed\n");
0389         goto fail;
0390     }
0391         wf_smu_slots_fans->ticks = 1;
0392 
0393     /* Fill PID params */
0394     param.additive = (fan_slots->type == WF_CONTROL_RPM_FAN);
0395     param.min = wf_control_get_min(fan_slots);
0396     param.max = wf_control_get_max(fan_slots);
0397     wf_pid_init(&wf_smu_slots_fans->pid, &param);
0398 
0399     DBG("wf: Slots Fan control initialized.\n");
0400     DBG("    itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
0401         FIX32TOPRINT(param.itarget), param.min, param.max);
0402     return;
0403 
0404  fail:
0405     if (fan_slots)
0406         wf_control_set_max(fan_slots);
0407 }
0408 
0409 static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st)
0410 {
0411     s32 new_setpoint, power;
0412     int rc;
0413 
0414     if (--st->ticks != 0) {
0415         if (wf_smu_readjust)
0416             goto readjust;
0417         return;
0418     }
0419     st->ticks = st->pid.param.interval;
0420 
0421     rc = wf_sensor_get(sensor_slots_power, &power);
0422     if (rc) {
0423         printk(KERN_WARNING "windfarm: Slots power sensor error %d\n",
0424                rc);
0425         wf_smu_failure_state |= FAILURE_SENSOR;
0426         return;
0427     }
0428 
0429     DBG("wf_smu: Slots Fans tick ! Slots power: %d.%03d\n",
0430         FIX32TOPRINT(power));
0431 
0432 #if 0 /* Check what makes a good overtemp condition */
0433     if (power > (st->pid.param.itarget + 0x50000))
0434         wf_smu_failure_state |= FAILURE_OVERTEMP;
0435 #endif
0436 
0437     new_setpoint = wf_pid_run(&st->pid, power);
0438 
0439     DBG("wf_smu: new_setpoint: %d\n", (int)new_setpoint);
0440 
0441     if (st->setpoint == new_setpoint)
0442         return;
0443     st->setpoint = new_setpoint;
0444  readjust:
0445     if (fan_slots && wf_smu_failure_state == 0) {
0446         rc = wf_control_set(fan_slots, st->setpoint);
0447         if (rc) {
0448             printk(KERN_WARNING "windfarm: Slots fan error %d\n",
0449                    rc);
0450             wf_smu_failure_state |= FAILURE_FAN;
0451         }
0452     }
0453 }
0454 
0455 
0456 /*
0457  * ****** Setup / Init / Misc ... ******
0458  *
0459  */
0460 
0461 static void wf_smu_tick(void)
0462 {
0463     unsigned int last_failure = wf_smu_failure_state;
0464     unsigned int new_failure;
0465 
0466     if (!wf_smu_started) {
0467         DBG("wf: creating control loops !\n");
0468         wf_smu_create_drive_fans();
0469         wf_smu_create_slots_fans();
0470         wf_smu_create_cpu_fans();
0471         wf_smu_started = true;
0472     }
0473 
0474     /* Skipping ticks */
0475     if (wf_smu_skipping && --wf_smu_skipping)
0476         return;
0477 
0478     wf_smu_failure_state = 0;
0479     if (wf_smu_drive_fans)
0480         wf_smu_drive_fans_tick(wf_smu_drive_fans);
0481     if (wf_smu_slots_fans)
0482         wf_smu_slots_fans_tick(wf_smu_slots_fans);
0483     if (wf_smu_cpu_fans)
0484         wf_smu_cpu_fans_tick(wf_smu_cpu_fans);
0485 
0486     wf_smu_readjust = 0;
0487     new_failure = wf_smu_failure_state & ~last_failure;
0488 
0489     /* If entering failure mode, clamp cpufreq and ramp all
0490      * fans to full speed.
0491      */
0492     if (wf_smu_failure_state && !last_failure) {
0493         if (cpufreq_clamp)
0494             wf_control_set_max(cpufreq_clamp);
0495         if (fan_cpu_main)
0496             wf_control_set_max(fan_cpu_main);
0497         if (fan_cpu_second)
0498             wf_control_set_max(fan_cpu_second);
0499         if (fan_cpu_third)
0500             wf_control_set_max(fan_cpu_third);
0501         if (fan_hd)
0502             wf_control_set_max(fan_hd);
0503         if (fan_slots)
0504             wf_control_set_max(fan_slots);
0505     }
0506 
0507     /* If leaving failure mode, unclamp cpufreq and readjust
0508      * all fans on next iteration
0509      */
0510     if (!wf_smu_failure_state && last_failure) {
0511         if (cpufreq_clamp)
0512             wf_control_set_min(cpufreq_clamp);
0513         wf_smu_readjust = 1;
0514     }
0515 
0516     /* Overtemp condition detected, notify and start skipping a couple
0517      * ticks to let the temperature go down
0518      */
0519     if (new_failure & FAILURE_OVERTEMP) {
0520         wf_set_overtemp();
0521         wf_smu_skipping = 2;
0522         wf_smu_overtemp = true;
0523     }
0524 
0525     /* We only clear the overtemp condition if overtemp is cleared
0526      * _and_ no other failure is present. Since a sensor error will
0527      * clear the overtemp condition (can't measure temperature) at
0528      * the control loop levels, but we don't want to keep it clear
0529      * here in this case
0530      */
0531     if (!wf_smu_failure_state && wf_smu_overtemp) {
0532         wf_clear_overtemp();
0533         wf_smu_overtemp = false;
0534     }
0535 }
0536 
0537 
0538 static void wf_smu_new_control(struct wf_control *ct)
0539 {
0540     if (wf_smu_all_controls_ok)
0541         return;
0542 
0543     if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-rear-fan-0")) {
0544         if (wf_get_control(ct) == 0)
0545             fan_cpu_main = ct;
0546     }
0547 
0548     if (fan_cpu_second == NULL && !strcmp(ct->name, "cpu-rear-fan-1")) {
0549         if (wf_get_control(ct) == 0)
0550             fan_cpu_second = ct;
0551     }
0552 
0553     if (fan_cpu_third == NULL && !strcmp(ct->name, "cpu-front-fan-0")) {
0554         if (wf_get_control(ct) == 0)
0555             fan_cpu_third = ct;
0556     }
0557 
0558     if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {
0559         if (wf_get_control(ct) == 0)
0560             cpufreq_clamp = ct;
0561     }
0562 
0563     if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) {
0564         if (wf_get_control(ct) == 0)
0565             fan_hd = ct;
0566     }
0567 
0568     if (fan_slots == NULL && !strcmp(ct->name, "slots-fan")) {
0569         if (wf_get_control(ct) == 0)
0570             fan_slots = ct;
0571     }
0572 
0573     if (fan_cpu_main && (fan_cpu_second || fan_cpu_third) && fan_hd &&
0574         fan_slots && cpufreq_clamp)
0575         wf_smu_all_controls_ok = 1;
0576 }
0577 
0578 static void wf_smu_new_sensor(struct wf_sensor *sr)
0579 {
0580     if (wf_smu_all_sensors_ok)
0581         return;
0582 
0583     if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) {
0584         if (wf_get_sensor(sr) == 0)
0585             sensor_cpu_power = sr;
0586     }
0587 
0588     if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) {
0589         if (wf_get_sensor(sr) == 0)
0590             sensor_cpu_temp = sr;
0591     }
0592 
0593     if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) {
0594         if (wf_get_sensor(sr) == 0)
0595             sensor_hd_temp = sr;
0596     }
0597 
0598     if (sensor_slots_power == NULL && !strcmp(sr->name, "slots-power")) {
0599         if (wf_get_sensor(sr) == 0)
0600             sensor_slots_power = sr;
0601     }
0602 
0603     if (sensor_cpu_power && sensor_cpu_temp &&
0604         sensor_hd_temp && sensor_slots_power)
0605         wf_smu_all_sensors_ok = 1;
0606 }
0607 
0608 
0609 static int wf_smu_notify(struct notifier_block *self,
0610                    unsigned long event, void *data)
0611 {
0612     switch(event) {
0613     case WF_EVENT_NEW_CONTROL:
0614         DBG("wf: new control %s detected\n",
0615             ((struct wf_control *)data)->name);
0616         wf_smu_new_control(data);
0617         wf_smu_readjust = 1;
0618         break;
0619     case WF_EVENT_NEW_SENSOR:
0620         DBG("wf: new sensor %s detected\n",
0621             ((struct wf_sensor *)data)->name);
0622         wf_smu_new_sensor(data);
0623         break;
0624     case WF_EVENT_TICK:
0625         if (wf_smu_all_controls_ok && wf_smu_all_sensors_ok)
0626             wf_smu_tick();
0627     }
0628 
0629     return 0;
0630 }
0631 
0632 static struct notifier_block wf_smu_events = {
0633     .notifier_call  = wf_smu_notify,
0634 };
0635 
0636 static int wf_init_pm(void)
0637 {
0638     printk(KERN_INFO "windfarm: Initializing for Desktop G5 model\n");
0639 
0640     return 0;
0641 }
0642 
0643 static int wf_smu_probe(struct platform_device *ddev)
0644 {
0645     wf_register_client(&wf_smu_events);
0646 
0647     return 0;
0648 }
0649 
0650 static int wf_smu_remove(struct platform_device *ddev)
0651 {
0652     wf_unregister_client(&wf_smu_events);
0653 
0654     /* XXX We don't have yet a guarantee that our callback isn't
0655      * in progress when returning from wf_unregister_client, so
0656      * we add an arbitrary delay. I'll have to fix that in the core
0657      */
0658     msleep(1000);
0659 
0660     /* Release all sensors */
0661     /* One more crappy race: I don't think we have any guarantee here
0662      * that the attribute callback won't race with the sensor beeing
0663      * disposed of, and I'm not 100% certain what best way to deal
0664      * with that except by adding locks all over... I'll do that
0665      * eventually but heh, who ever rmmod this module anyway ?
0666      */
0667     if (sensor_cpu_power)
0668         wf_put_sensor(sensor_cpu_power);
0669     if (sensor_cpu_temp)
0670         wf_put_sensor(sensor_cpu_temp);
0671     if (sensor_hd_temp)
0672         wf_put_sensor(sensor_hd_temp);
0673     if (sensor_slots_power)
0674         wf_put_sensor(sensor_slots_power);
0675 
0676     /* Release all controls */
0677     if (fan_cpu_main)
0678         wf_put_control(fan_cpu_main);
0679     if (fan_cpu_second)
0680         wf_put_control(fan_cpu_second);
0681     if (fan_cpu_third)
0682         wf_put_control(fan_cpu_third);
0683     if (fan_hd)
0684         wf_put_control(fan_hd);
0685     if (fan_slots)
0686         wf_put_control(fan_slots);
0687     if (cpufreq_clamp)
0688         wf_put_control(cpufreq_clamp);
0689 
0690     /* Destroy control loops state structures */
0691     kfree(wf_smu_slots_fans);
0692     kfree(wf_smu_drive_fans);
0693     kfree(wf_smu_cpu_fans);
0694 
0695     return 0;
0696 }
0697 
0698 static struct platform_driver wf_smu_driver = {
0699         .probe = wf_smu_probe,
0700         .remove = wf_smu_remove,
0701     .driver = {
0702         .name = "windfarm",
0703     },
0704 };
0705 
0706 
0707 static int __init wf_smu_init(void)
0708 {
0709     int rc = -ENODEV;
0710 
0711     if (of_machine_is_compatible("PowerMac9,1"))
0712         rc = wf_init_pm();
0713 
0714     if (rc == 0) {
0715 #ifdef MODULE
0716         request_module("windfarm_smu_controls");
0717         request_module("windfarm_smu_sensors");
0718         request_module("windfarm_lm75_sensor");
0719         request_module("windfarm_cpufreq_clamp");
0720 
0721 #endif /* MODULE */
0722         platform_driver_register(&wf_smu_driver);
0723     }
0724 
0725     return rc;
0726 }
0727 
0728 static void __exit wf_smu_exit(void)
0729 {
0730 
0731     platform_driver_unregister(&wf_smu_driver);
0732 }
0733 
0734 
0735 module_init(wf_smu_init);
0736 module_exit(wf_smu_exit);
0737 
0738 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
0739 MODULE_DESCRIPTION("Thermal control logic for PowerMac9,1");
0740 MODULE_LICENSE("GPL");
0741 
0742 MODULE_ALIAS("platform:windfarm");