cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

windfarm_pm91.c (18472B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Windfarm PowerMac thermal control. SMU based 1 CPU desktop control loops
      4 *
      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 same
      9 * way the published Darwin code does, using the same values that
     10 * are present in the Darwin 8.2 snapshot property lists (note however
     11 * that none of the code has been re-used, it's a complete re-implementation
     12 *
     13 * The various control loops found in Darwin config file are:
     14 *
     15 * PowerMac9,1
     16 * ===========
     17 *
     18 * Has 3 control loops: CPU fans is similar to PowerMac8,1 (though it doesn't
     19 * try to play with other control loops fans). Drive bay is rather basic PID
     20 * with one sensor and one fan. Slots area is a bit different as the Darwin
     21 * driver is supposed to be capable of working in a special "AGP" mode which
     22 * involves the presence of an AGP sensor and an AGP fan (possibly on the
     23 * AGP card itself). I can't deal with that special mode as I don't have
     24 * access to those additional sensor/fans for now (though ultimately, it would
     25 * be possible to add sensor objects for them) so I'm only implementing the
     26 * basic PCI slot control loop
     27 */
     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 DEBUG
     53
     54#ifdef DEBUG
     55#define DBG(args...)	printk(args)
     56#else
     57#define DBG(args...)	do { } while(0)
     58#endif
     59
     60/* define this to force CPU overtemp to 74 degree, useful for testing
     61 * the overtemp code
     62 */
     63#undef HACKED_OVERTEMP
     64
     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		0x01
     84#define FAILURE_SENSOR		0x02
     85#define FAILURE_OVERTEMP	0x04
     86
     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	1
     97#define WF_SMU_CPU_FANS_MAX_HISTORY	16
     98
     99/* State data used by the cpu fans control loop
    100 */
    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 == 0) {
    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 one
    161	 * for now) in order to get tmax
    162	 */
    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	} else
    168		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_OVERTEMP
    251	if (temp > 0x4a0000)
    252		wf_smu_failure_state |= FAILURE_OVERTEMP;
    253#else
    254	if (temp > st->pid.param.tmax)
    255		wf_smu_failure_state |= FAILURE_OVERTEMP;
    256#endif
    257	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, &param);
    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	= 0x00000000
    381	};
    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, &param);
    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#endif
    436
    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 all
    490	 * 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 readjust
    508	 * all fans on next iteration
    509	 */
    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 couple
    517	 * ticks to let the temperature go down
    518	 */
    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 cleared
    526	 * _and_ no other failure is present. Since a sensor error will
    527	 * clear the overtemp condition (can't measure temperature) at
    528	 * the control loop levels, but we don't want to keep it clear
    529	 * here in this case
    530	 */
    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 int 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't
    655	 * in progress when returning from wf_unregister_client, so
    656	 * we add an arbitrary delay. I'll have to fix that in the core
    657	 */
    658	msleep(1000);
    659
    660	/* Release all sensors */
    661	/* One more crappy race: I don't think we have any guarantee here
    662	 * that the attribute callback won't race with the sensor beeing
    663	 * disposed of, and I'm not 100% certain what best way to deal
    664	 * with that except by adding locks all over... I'll do that
    665	 * 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	return 0;
    696}
    697
    698static struct platform_driver wf_smu_driver = {
    699        .probe = wf_smu_probe,
    700        .remove = wf_smu_remove,
    701	.driver = {
    702		.name = "windfarm",
    703	},
    704};
    705
    706
    707static int __init wf_smu_init(void)
    708{
    709	int rc = -ENODEV;
    710
    711	if (of_machine_is_compatible("PowerMac9,1"))
    712		rc = wf_init_pm();
    713
    714	if (rc == 0) {
    715#ifdef MODULE
    716		request_module("windfarm_smu_controls");
    717		request_module("windfarm_smu_sensors");
    718		request_module("windfarm_lm75_sensor");
    719		request_module("windfarm_cpufreq_clamp");
    720
    721#endif /* MODULE */
    722		platform_driver_register(&wf_smu_driver);
    723	}
    724
    725	return rc;
    726}
    727
    728static void __exit wf_smu_exit(void)
    729{
    730
    731	platform_driver_unregister(&wf_smu_driver);
    732}
    733
    734
    735module_init(wf_smu_init);
    736module_exit(wf_smu_exit);
    737
    738MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
    739MODULE_DESCRIPTION("Thermal control logic for PowerMac9,1");
    740MODULE_LICENSE("GPL");
    741
    742MODULE_ALIAS("platform:windfarm");