brintos

brintos / linux-shallow public Read only

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