brintos

brintos / linux-shallow public Read only

0
0
Text · 18.0 KiB · fba02a3 Raw
741 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Windfarm PowerMac thermal control. SMU based 1 CPU desktop control loops4 *5 * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.6 *                    <benh@kernel.crashing.org>7 *8 * The algorithm used is the PID control algorithm, used the same9 * way the published Darwin code does, using the same values that10 * are present in the Darwin 8.2 snapshot property lists (note however11 * that none of the code has been re-used, it's a complete re-implementation12 *13 * The various control loops found in Darwin config file are:14 *15 * PowerMac9,116 * ===========17 *18 * Has 3 control loops: CPU fans is similar to PowerMac8,1 (though it doesn't19 * try to play with other control loops fans). Drive bay is rather basic PID20 * with one sensor and one fan. Slots area is a bit different as the Darwin21 * driver is supposed to be capable of working in a special "AGP" mode which22 * involves the presence of an AGP sensor and an AGP fan (possibly on the23 * AGP card itself). I can't deal with that special mode as I don't have24 * access to those additional sensor/fans for now (though ultimately, it would25 * be possible to add sensor objects for them) so I'm only implementing the26 * basic PCI slot control loop27 */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 DEBUG53 54#ifdef DEBUG55#define DBG(args...)	printk(args)56#else57#define DBG(args...)	do { } while(0)58#endif59 60/* define this to force CPU overtemp to 74 degree, useful for testing61 * the overtemp code62 */63#undef HACKED_OVERTEMP64 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		0x0184#define FAILURE_SENSOR		0x0285#define FAILURE_OVERTEMP	0x0486 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	197#define WF_SMU_CPU_FANS_MAX_HISTORY	1698 99/* State data used by the cpu fans control loop100 */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) {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 one161	 * for now) in order to get tmax162	 */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	} else168		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_OVERTEMP251	if (temp > 0x4a0000)252		wf_smu_failure_state |= FAILURE_OVERTEMP;253#else254	if (temp > st->pid.param.tmax)255		wf_smu_failure_state |= FAILURE_OVERTEMP;256#endif257	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	= 0x00000000381	};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#endif436 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 all490	 * 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 readjust508	 * all fans on next iteration509	 */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 couple517	 * ticks to let the temperature go down518	 */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 cleared526	 * _and_ no other failure is present. Since a sensor error will527	 * clear the overtemp condition (can't measure temperature) at528	 * the control loop levels, but we don't want to keep it clear529	 * here in this case530	 */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 void 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't655	 * in progress when returning from wf_unregister_client, so656	 * we add an arbitrary delay. I'll have to fix that in the core657	 */658	msleep(1000);659 660	/* Release all sensors */661	/* One more crappy race: I don't think we have any guarantee here662	 * that the attribute callback won't race with the sensor beeing663	 * disposed of, and I'm not 100% certain what best way to deal664	 * with that except by adding locks all over... I'll do that665	 * 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 696static struct platform_driver wf_smu_driver = {697	.probe = wf_smu_probe,698	.remove_new = wf_smu_remove,699	.driver = {700		.name = "windfarm",701	},702};703 704 705static int __init wf_smu_init(void)706{707	int rc = -ENODEV;708 709	if (of_machine_is_compatible("PowerMac9,1"))710		rc = wf_init_pm();711 712	if (rc == 0) {713#ifdef MODULE714		request_module("windfarm_smu_controls");715		request_module("windfarm_smu_sensors");716		request_module("windfarm_lm75_sensor");717		request_module("windfarm_cpufreq_clamp");718 719#endif /* MODULE */720		platform_driver_register(&wf_smu_driver);721	}722 723	return rc;724}725 726static void __exit wf_smu_exit(void)727{728 729	platform_driver_unregister(&wf_smu_driver);730}731 732 733module_init(wf_smu_init);734module_exit(wf_smu_exit);735 736MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");737MODULE_DESCRIPTION("Thermal control logic for PowerMac9,1");738MODULE_LICENSE("GPL");739 740MODULE_ALIAS("platform:windfarm");741