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_pm81.c (21694B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * Windfarm PowerMac thermal control. iMac G5
      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 * PowerMac8,1 and PowerMac8,2
     16 * ===========================
     17 *
     18 * System Fans control loop. Different based on models. In addition to the
     19 * usual PID algorithm, the control loop gets 2 additional pairs of linear
     20 * scaling factors (scale/offsets) expressed as 4.12 fixed point values
     21 * signed offset, unsigned scale)
     22 *
     23 * The targets are modified such as:
     24 *  - the linked control (second control) gets the target value as-is
     25 *    (typically the drive fan)
     26 *  - the main control (first control) gets the target value scaled with
     27 *    the first pair of factors, and is then modified as below
     28 *  - 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 and
     30 *    the scaled target is applied to the main control.
     31 *
     32 * # model_id: 2
     33 *   controls       : system-fan, drive-bay-fan
     34 *   sensors        : hd-temp
     35 *   PID params     : G_d = 0x15400000
     36 *                    G_p = 0x00200000
     37 *                    G_r = 0x000002fd
     38 *                    History = 2 entries
     39 *                    Input target = 0x3a0000
     40 *                    Interval = 5s
     41 *   linear-factors : offset = 0xff38 scale  = 0x0ccd
     42 *                    offset = 0x0208 scale  = 0x07ae
     43 *
     44 * # model_id: 3
     45 *   controls       : system-fan, drive-bay-fan
     46 *   sensors        : hd-temp
     47 *   PID params     : G_d = 0x08e00000
     48 *                    G_p = 0x00566666
     49 *                    G_r = 0x0000072b
     50 *                    History = 2 entries
     51 *                    Input target = 0x350000
     52 *                    Interval = 5s
     53 *   linear-factors : offset = 0xff38 scale  = 0x0ccd
     54 *                    offset = 0x0000 scale  = 0x0000
     55 *
     56 * # model_id: 5
     57 *   controls       : system-fan
     58 *   sensors        : hd-temp
     59 *   PID params     : G_d = 0x15400000
     60 *                    G_p = 0x00233333
     61 *                    G_r = 0x000002fd
     62 *                    History = 2 entries
     63 *                    Input target = 0x3a0000
     64 *                    Interval = 5s
     65 *   linear-factors : offset = 0x0000 scale  = 0x1000
     66 *                    offset = 0x0091 scale  = 0x0bae
     67 *
     68 * CPU Fan control loop. The loop is identical for all models. it
     69 * has an additional pair of scaling factor. This is used to scale the
     70 * systems fan control loop target result (the one before it gets scaled
     71 * by the System Fans control loop itself). Then, the max value of the
     72 * calculated target value and system fan value is sent to the fans
     73 *
     74 *   controls       : cpu-fan
     75 *   sensors        : cpu-temp cpu-power
     76 *   PID params     : From SMU sdb partition
     77 *   linear-factors : offset = 0xfb50 scale  = 0x1000
     78 *
     79 * CPU Slew control loop. Not implemented. The cpufreq driver in linux is
     80 * completely separate for now, though we could find a way to link it, either
     81 * as a client reacting to overtemp notifications, or directling monitoring
     82 * the CPU temperature
     83 *
     84 * WARNING ! The CPU control loop requires the CPU tmax for the current
     85 * operating point. However, we currently are completely separated from
     86 * the cpufreq driver and thus do not know what the current operating
     87 * point is. Fortunately, we also do not have any hardware supporting anything
     88 * but operating point 0 at the moment, thus we just peek that value directly
     89 * from the SDB partition. If we ever end up with actually slewing the system
     90 * clock and thus changing operating points, we'll have to find a way to
     91 * 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 DEBUG
    118
    119#ifdef DEBUG
    120#define DBG(args...)	printk(args)
    121#else
    122#define DBG(args...)	do { } while(0)
    123#endif
    124
    125/* define this to force CPU overtemp to 74 degree, useful for testing
    126 * the overtemp code
    127 */
    128#undef HACKED_OVERTEMP
    129
    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		0x01
    147#define FAILURE_SENSOR		0x02
    148#define FAILURE_OVERTEMP	0x04
    149
    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. Parameters
    160 * 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	5
    175#define WF_SMU_SYS_FANS_HISTORY_SIZE	2
    176
    177/* State data used by the system fans control loop
    178 */
    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 loop
    192 */
    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	1
    242#define WF_SMU_CPU_FANS_MAX_HISTORY	16
    243#define WF_SMU_CPU_FANS_SIBLING_SCALE	0x00001000
    244#define WF_SMU_CPU_FANS_SIBLING_OFFSET	0xfffffb50
    245
    246/* State data used by the cpu fans control loop
    247 */
    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 == 0) {
    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 one
    412	 * for now) in order to get tmax
    413	 */
    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	} else
    419		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_OVERTEMP
    505	if (temp > 0x4a0000)
    506		wf_smu_failure_state |= FAILURE_OVERTEMP;
    507#else
    508	if (temp > st->pid.param.tmax)
    509		wf_smu_failure_state |= FAILURE_OVERTEMP;
    510#endif
    511	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 all
    569	 * 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 readjust
    583	 * all fans on next iteration
    584	 */
    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 couple
    592	 * ticks to let the temperature go down
    593	 */
    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 cleared
    601	 * _and_ no other failure is present. Since a sensor error will
    602	 * clear the overtemp condition (can't measure temperature) at
    603	 * the control loop levels, but we don't want to keep it clear
    604	 * here in this case
    605	 */
    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 ID
    633	 * 0, 1, 2 and 3
    634	 */
    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 != 0) {
    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 int 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't
    732	 * in progress when returning from wf_unregister_client, so
    733	 * we add an arbitrary delay. I'll have to fix that in the core
    734	 */
    735	msleep(1000);
    736
    737	/* Release all sensors */
    738	/* One more crappy race: I don't think we have any guarantee here
    739	 * that the attribute callback won't race with the sensor beeing
    740	 * disposed of, and I'm not 100% certain what best way to deal
    741	 * with that except by adding locks all over... I'll do that
    742	 * 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	return 0;
    766}
    767
    768static struct platform_driver wf_smu_driver = {
    769        .probe = wf_smu_probe,
    770        .remove = wf_smu_remove,
    771	.driver = {
    772		.name = "windfarm",
    773	},
    774};
    775
    776
    777static int __init wf_smu_init(void)
    778{
    779	int rc = -ENODEV;
    780
    781	if (of_machine_is_compatible("PowerMac8,1") ||
    782	    of_machine_is_compatible("PowerMac8,2"))
    783		rc = wf_init_pm();
    784
    785	if (rc == 0) {
    786#ifdef MODULE
    787		request_module("windfarm_smu_controls");
    788		request_module("windfarm_smu_sensors");
    789		request_module("windfarm_lm75_sensor");
    790		request_module("windfarm_cpufreq_clamp");
    791
    792#endif /* MODULE */
    793		platform_driver_register(&wf_smu_driver);
    794	}
    795
    796	return rc;
    797}
    798
    799static void __exit wf_smu_exit(void)
    800{
    801
    802	platform_driver_unregister(&wf_smu_driver);
    803}
    804
    805
    806module_init(wf_smu_init);
    807module_exit(wf_smu_exit);
    808
    809MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
    810MODULE_DESCRIPTION("Thermal control logic for iMac G5");
    811MODULE_LICENSE("GPL");
    812MODULE_ALIAS("platform:windfarm");