brintos

brintos / linux-shallow public Read only

0
0
Text · 18.0 KiB · 876b4d8 Raw
718 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Windfarm PowerMac thermal control.4 * Control loops for machines with SMU and PPC970MP processors.5 *6 * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org>7 * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp.8 */9#include <linux/types.h>10#include <linux/errno.h>11#include <linux/kernel.h>12#include <linux/device.h>13#include <linux/platform_device.h>14#include <linux/reboot.h>15#include <linux/of.h>16#include <linux/slab.h>17 18#include <asm/smu.h>19 20#include "windfarm.h"21#include "windfarm_pid.h"22 23#define VERSION "0.2"24 25#define DEBUG26#undef LOTSA_DEBUG27 28#ifdef DEBUG29#define DBG(args...)	printk(args)30#else31#define DBG(args...)	do { } while(0)32#endif33 34#ifdef LOTSA_DEBUG35#define DBG_LOTS(args...)	printk(args)36#else37#define DBG_LOTS(args...)	do { } while(0)38#endif39 40/* define this to force CPU overtemp to 60 degree, useful for testing41 * the overtemp code42 */43#undef HACKED_OVERTEMP44 45/* We currently only handle 2 chips, 4 cores... */46#define NR_CHIPS	247#define NR_CORES	448#define NR_CPU_FANS	3 * NR_CHIPS49 50/* Controls and sensors */51static struct wf_sensor *sens_cpu_temp[NR_CORES];52static struct wf_sensor *sens_cpu_power[NR_CORES];53static struct wf_sensor *hd_temp;54static struct wf_sensor *slots_power;55static struct wf_sensor *u4_temp;56 57static struct wf_control *cpu_fans[NR_CPU_FANS];58static char *cpu_fan_names[NR_CPU_FANS] = {59	"cpu-rear-fan-0",60	"cpu-rear-fan-1",61	"cpu-front-fan-0",62	"cpu-front-fan-1",63	"cpu-pump-0",64	"cpu-pump-1",65};66static struct wf_control *cpufreq_clamp;67 68/* Second pump isn't required (and isn't actually present) */69#define CPU_FANS_REQD		(NR_CPU_FANS - 2)70#define FIRST_PUMP		471#define LAST_PUMP		572 73/* We keep a temperature history for average calculation of 180s */74#define CPU_TEMP_HIST_SIZE	18075 76/* Scale factor for fan speed, *100 */77static int cpu_fan_scale[NR_CPU_FANS] = {78	100,79	100,80	97,		/* inlet fans run at 97% of exhaust fan */81	97,82	100,		/* updated later */83	100,		/* updated later */84};85 86static struct wf_control *backside_fan;87static struct wf_control *slots_fan;88static struct wf_control *drive_bay_fan;89 90/* PID loop state */91static struct wf_cpu_pid_state cpu_pid[NR_CORES];92static u32 cpu_thist[CPU_TEMP_HIST_SIZE];93static int cpu_thist_pt;94static s64 cpu_thist_total;95static s32 cpu_all_tmax = 100 << 16;96static int cpu_last_target;97static struct wf_pid_state backside_pid;98static int backside_tick;99static struct wf_pid_state slots_pid;100static bool slots_started;101static struct wf_pid_state drive_bay_pid;102static int drive_bay_tick;103 104static int nr_cores;105static int have_all_controls;106static int have_all_sensors;107static bool started;108 109static int failure_state;110#define FAILURE_SENSOR		1111#define FAILURE_FAN		2112#define FAILURE_PERM		4113#define FAILURE_LOW_OVERTEMP	8114#define FAILURE_HIGH_OVERTEMP	16115 116/* Overtemp values */117#define LOW_OVER_AVERAGE	0118#define LOW_OVER_IMMEDIATE	(10 << 16)119#define LOW_OVER_CLEAR		((-10) << 16)120#define HIGH_OVER_IMMEDIATE	(14 << 16)121#define HIGH_OVER_AVERAGE	(10 << 16)122#define HIGH_OVER_IMMEDIATE	(14 << 16)123 124 125/* Implementation... */126static int create_cpu_loop(int cpu)127{128	int chip = cpu / 2;129	int core = cpu & 1;130	struct smu_sdbp_header *hdr;131	struct smu_sdbp_cpupiddata *piddata;132	struct wf_cpu_pid_param pid;133	struct wf_control *main_fan = cpu_fans[0];134	s32 tmax;135	int fmin;136 137	/* Get FVT params to get Tmax; if not found, assume default */138	hdr = smu_sat_get_sdb_partition(chip, 0xC4 + core, NULL);139	if (hdr) {140		struct smu_sdbp_fvt *fvt = (struct smu_sdbp_fvt *)&hdr[1];141		tmax = fvt->maxtemp << 16;142	} else143		tmax = 95 << 16;	/* default to 95 degrees C */144 145	/* We keep a global tmax for overtemp calculations */146	if (tmax < cpu_all_tmax)147		cpu_all_tmax = tmax;148 149	kfree(hdr);150 151	/* Get PID params from the appropriate SAT */152	hdr = smu_sat_get_sdb_partition(chip, 0xC8 + core, NULL);153	if (hdr == NULL) {154		printk(KERN_WARNING"windfarm: can't get CPU PID fan config\n");155		return -EINVAL;156	}157	piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];158 159	/*160	 * Darwin has a minimum fan speed of 1000 rpm for the 4-way and161	 * 515 for the 2-way.  That appears to be overkill, so for now,162	 * impose a minimum of 750 or 515.163	 */164	fmin = (nr_cores > 2) ? 750 : 515;165 166	/* Initialize PID loop */167	pid.interval = 1;	/* seconds */168	pid.history_len = piddata->history_len;169	pid.gd = piddata->gd;170	pid.gp = piddata->gp;171	pid.gr = piddata->gr / piddata->history_len;172	pid.pmaxadj = (piddata->max_power << 16) - (piddata->power_adj << 8);173	pid.ttarget = tmax - (piddata->target_temp_delta << 16);174	pid.tmax = tmax;175	pid.min = main_fan->ops->get_min(main_fan);176	pid.max = main_fan->ops->get_max(main_fan);177	if (pid.min < fmin)178		pid.min = fmin;179 180	wf_cpu_pid_init(&cpu_pid[cpu], &pid);181 182	kfree(hdr);183 184	return 0;185}186 187static void cpu_max_all_fans(void)188{189	int i;190 191	/* We max all CPU fans in case of a sensor error. We also do the192	 * cpufreq clamping now, even if it's supposedly done later by the193	 * generic code anyway, we do it earlier here to react faster194	 */195	if (cpufreq_clamp)196		wf_control_set_max(cpufreq_clamp);197	for (i = 0; i < NR_CPU_FANS; ++i)198		if (cpu_fans[i])199			wf_control_set_max(cpu_fans[i]);200}201 202static int cpu_check_overtemp(s32 temp)203{204	int new_state = 0;205	s32 t_avg, t_old;206 207	/* First check for immediate overtemps */208	if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) {209		new_state |= FAILURE_LOW_OVERTEMP;210		if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)211			printk(KERN_ERR "windfarm: Overtemp due to immediate CPU"212			       " temperature !\n");213	}214	if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) {215		new_state |= FAILURE_HIGH_OVERTEMP;216		if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)217			printk(KERN_ERR "windfarm: Critical overtemp due to"218			       " immediate CPU temperature !\n");219	}220 221	/* We calculate a history of max temperatures and use that for the222	 * overtemp management223	 */224	t_old = cpu_thist[cpu_thist_pt];225	cpu_thist[cpu_thist_pt] = temp;226	cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE;227	cpu_thist_total -= t_old;228	cpu_thist_total += temp;229	t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE;230 231	DBG_LOTS("t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n",232		 FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp));233 234	/* Now check for average overtemps */235	if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) {236		new_state |= FAILURE_LOW_OVERTEMP;237		if ((failure_state & FAILURE_LOW_OVERTEMP) == 0)238			printk(KERN_ERR "windfarm: Overtemp due to average CPU"239			       " temperature !\n");240	}241	if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) {242		new_state |= FAILURE_HIGH_OVERTEMP;243		if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0)244			printk(KERN_ERR "windfarm: Critical overtemp due to"245			       " average CPU temperature !\n");246	}247 248	/* Now handle overtemp conditions. We don't currently use the windfarm249	 * overtemp handling core as it's not fully suited to the needs of those250	 * new machine. This will be fixed later.251	 */252	if (new_state) {253		/* High overtemp -> immediate shutdown */254		if (new_state & FAILURE_HIGH_OVERTEMP)255			machine_power_off();256		if ((failure_state & new_state) != new_state)257			cpu_max_all_fans();258		failure_state |= new_state;259	} else if ((failure_state & FAILURE_LOW_OVERTEMP) &&260		   (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) {261		printk(KERN_ERR "windfarm: Overtemp condition cleared !\n");262		failure_state &= ~FAILURE_LOW_OVERTEMP;263	}264 265	return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP);266}267 268static void cpu_fans_tick(void)269{270	int err, cpu;271	s32 greatest_delta = 0;272	s32 temp, power, t_max = 0;273	int i, t, target = 0;274	struct wf_sensor *sr;275	struct wf_control *ct;276	struct wf_cpu_pid_state *sp;277 278	DBG_LOTS(KERN_DEBUG);279	for (cpu = 0; cpu < nr_cores; ++cpu) {280		/* Get CPU core temperature */281		sr = sens_cpu_temp[cpu];282		err = sr->ops->get_value(sr, &temp);283		if (err) {284			DBG("\n");285			printk(KERN_WARNING "windfarm: CPU %d temperature "286			       "sensor error %d\n", cpu, err);287			failure_state |= FAILURE_SENSOR;288			cpu_max_all_fans();289			return;290		}291 292		/* Keep track of highest temp */293		t_max = max(t_max, temp);294 295		/* Get CPU power */296		sr = sens_cpu_power[cpu];297		err = sr->ops->get_value(sr, &power);298		if (err) {299			DBG("\n");300			printk(KERN_WARNING "windfarm: CPU %d power "301			       "sensor error %d\n", cpu, err);302			failure_state |= FAILURE_SENSOR;303			cpu_max_all_fans();304			return;305		}306 307		/* Run PID */308		sp = &cpu_pid[cpu];309		t = wf_cpu_pid_run(sp, power, temp);310 311		if (cpu == 0 || sp->last_delta > greatest_delta) {312			greatest_delta = sp->last_delta;313			target = t;314		}315		DBG_LOTS("[%d] P=%d.%.3d T=%d.%.3d ",316		    cpu, FIX32TOPRINT(power), FIX32TOPRINT(temp));317	}318	DBG_LOTS("fans = %d, t_max = %d.%03d\n", target, FIX32TOPRINT(t_max));319 320	/* Darwin limits decrease to 20 per iteration */321	if (target < (cpu_last_target - 20))322		target = cpu_last_target - 20;323	cpu_last_target = target;324	for (cpu = 0; cpu < nr_cores; ++cpu)325		cpu_pid[cpu].target = target;326 327	/* Handle possible overtemps */328	if (cpu_check_overtemp(t_max))329		return;330 331	/* Set fans */332	for (i = 0; i < NR_CPU_FANS; ++i) {333		ct = cpu_fans[i];334		if (ct == NULL)335			continue;336		err = ct->ops->set_value(ct, target * cpu_fan_scale[i] / 100);337		if (err) {338			printk(KERN_WARNING "windfarm: fan %s reports "339			       "error %d\n", ct->name, err);340			failure_state |= FAILURE_FAN;341			break;342		}343	}344}345 346/* Backside/U4 fan */347static struct wf_pid_param backside_param = {348	.interval	= 5,349	.history_len	= 2,350	.gd		= 48 << 20,351	.gp		= 5 << 20,352	.gr		= 0,353	.itarget	= 64 << 16,354	.additive	= 1,355};356 357static void backside_fan_tick(void)358{359	s32 temp;360	int speed;361	int err;362 363	if (!backside_fan || !u4_temp)364		return;365	if (!backside_tick) {366		/* first time; initialize things */367		printk(KERN_INFO "windfarm: Backside control loop started.\n");368		backside_param.min = backside_fan->ops->get_min(backside_fan);369		backside_param.max = backside_fan->ops->get_max(backside_fan);370		wf_pid_init(&backside_pid, &backside_param);371		backside_tick = 1;372	}373	if (--backside_tick > 0)374		return;375	backside_tick = backside_pid.param.interval;376 377	err = u4_temp->ops->get_value(u4_temp, &temp);378	if (err) {379		printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n",380		       err);381		failure_state |= FAILURE_SENSOR;382		wf_control_set_max(backside_fan);383		return;384	}385	speed = wf_pid_run(&backside_pid, temp);386	DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n",387		 FIX32TOPRINT(temp), speed);388 389	err = backside_fan->ops->set_value(backside_fan, speed);390	if (err) {391		printk(KERN_WARNING "windfarm: backside fan error %d\n", err);392		failure_state |= FAILURE_FAN;393	}394}395 396/* Drive bay fan */397static struct wf_pid_param drive_bay_prm = {398	.interval	= 5,399	.history_len	= 2,400	.gd		= 30 << 20,401	.gp		= 5 << 20,402	.gr		= 0,403	.itarget	= 40 << 16,404	.additive	= 1,405};406 407static void drive_bay_fan_tick(void)408{409	s32 temp;410	int speed;411	int err;412 413	if (!drive_bay_fan || !hd_temp)414		return;415	if (!drive_bay_tick) {416		/* first time; initialize things */417		printk(KERN_INFO "windfarm: Drive bay control loop started.\n");418		drive_bay_prm.min = drive_bay_fan->ops->get_min(drive_bay_fan);419		drive_bay_prm.max = drive_bay_fan->ops->get_max(drive_bay_fan);420		wf_pid_init(&drive_bay_pid, &drive_bay_prm);421		drive_bay_tick = 1;422	}423	if (--drive_bay_tick > 0)424		return;425	drive_bay_tick = drive_bay_pid.param.interval;426 427	err = hd_temp->ops->get_value(hd_temp, &temp);428	if (err) {429		printk(KERN_WARNING "windfarm: drive bay temp sensor "430		       "error %d\n", err);431		failure_state |= FAILURE_SENSOR;432		wf_control_set_max(drive_bay_fan);433		return;434	}435	speed = wf_pid_run(&drive_bay_pid, temp);436	DBG_LOTS("drive_bay PID temp=%d.%.3d speed=%d\n",437		 FIX32TOPRINT(temp), speed);438 439	err = drive_bay_fan->ops->set_value(drive_bay_fan, speed);440	if (err) {441		printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err);442		failure_state |= FAILURE_FAN;443	}444}445 446/* PCI slots area fan */447/* This makes the fan speed proportional to the power consumed */448static struct wf_pid_param slots_param = {449	.interval	= 1,450	.history_len	= 2,451	.gd		= 0,452	.gp		= 0,453	.gr		= 0x1277952,454	.itarget	= 0,455	.min		= 1560,456	.max		= 3510,457};458 459static void slots_fan_tick(void)460{461	s32 power;462	int speed;463	int err;464 465	if (!slots_fan || !slots_power)466		return;467	if (!slots_started) {468		/* first time; initialize things */469		printk(KERN_INFO "windfarm: Slots control loop started.\n");470		wf_pid_init(&slots_pid, &slots_param);471		slots_started = true;472	}473 474	err = slots_power->ops->get_value(slots_power, &power);475	if (err) {476		printk(KERN_WARNING "windfarm: slots power sensor error %d\n",477		       err);478		failure_state |= FAILURE_SENSOR;479		wf_control_set_max(slots_fan);480		return;481	}482	speed = wf_pid_run(&slots_pid, power);483	DBG_LOTS("slots PID power=%d.%.3d speed=%d\n",484		 FIX32TOPRINT(power), speed);485 486	err = slots_fan->ops->set_value(slots_fan, speed);487	if (err) {488		printk(KERN_WARNING "windfarm: slots fan error %d\n", err);489		failure_state |= FAILURE_FAN;490	}491}492 493static void set_fail_state(void)494{495	int i;496 497	if (cpufreq_clamp)498		wf_control_set_max(cpufreq_clamp);499	for (i = 0; i < NR_CPU_FANS; ++i)500		if (cpu_fans[i])501			wf_control_set_max(cpu_fans[i]);502	if (backside_fan)503		wf_control_set_max(backside_fan);504	if (slots_fan)505		wf_control_set_max(slots_fan);506	if (drive_bay_fan)507		wf_control_set_max(drive_bay_fan);508}509 510static void pm112_tick(void)511{512	int i, last_failure;513 514	if (!started) {515		started = true;516		printk(KERN_INFO "windfarm: CPUs control loops started.\n");517		for (i = 0; i < nr_cores; ++i) {518			if (create_cpu_loop(i) < 0) {519				failure_state = FAILURE_PERM;520				set_fail_state();521				break;522			}523		}524		DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax));525 526#ifdef HACKED_OVERTEMP527		cpu_all_tmax = 60 << 16;528#endif529	}530 531	/* Permanent failure, bail out */532	if (failure_state & FAILURE_PERM)533		return;534	/* Clear all failure bits except low overtemp which will be eventually535	 * cleared by the control loop itself536	 */537	last_failure = failure_state;538	failure_state &= FAILURE_LOW_OVERTEMP;539	cpu_fans_tick();540	backside_fan_tick();541	slots_fan_tick();542	drive_bay_fan_tick();543 544	DBG_LOTS("last_failure: 0x%x, failure_state: %x\n",545		 last_failure, failure_state);546 547	/* Check for failures. Any failure causes cpufreq clamping */548	if (failure_state && last_failure == 0 && cpufreq_clamp)549		wf_control_set_max(cpufreq_clamp);550	if (failure_state == 0 && last_failure && cpufreq_clamp)551		wf_control_set_min(cpufreq_clamp);552 553	/* That's it for now, we might want to deal with other failures554	 * differently in the future though555	 */556}557 558static void pm112_new_control(struct wf_control *ct)559{560	int i, max_exhaust;561 562	if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) {563		if (wf_get_control(ct) == 0)564			cpufreq_clamp = ct;565	}566 567	for (i = 0; i < NR_CPU_FANS; ++i) {568		if (!strcmp(ct->name, cpu_fan_names[i])) {569			if (cpu_fans[i] == NULL && wf_get_control(ct) == 0)570				cpu_fans[i] = ct;571			break;572		}573	}574	if (i >= NR_CPU_FANS) {575		/* not a CPU fan, try the others */576		if (!strcmp(ct->name, "backside-fan")) {577			if (backside_fan == NULL && wf_get_control(ct) == 0)578				backside_fan = ct;579		} else if (!strcmp(ct->name, "slots-fan")) {580			if (slots_fan == NULL && wf_get_control(ct) == 0)581				slots_fan = ct;582		} else if (!strcmp(ct->name, "drive-bay-fan")) {583			if (drive_bay_fan == NULL && wf_get_control(ct) == 0)584				drive_bay_fan = ct;585		}586		return;587	}588 589	for (i = 0; i < CPU_FANS_REQD; ++i)590		if (cpu_fans[i] == NULL)591			return;592 593	/* work out pump scaling factors */594	max_exhaust = cpu_fans[0]->ops->get_max(cpu_fans[0]);595	for (i = FIRST_PUMP; i <= LAST_PUMP; ++i)596		if ((ct = cpu_fans[i]) != NULL)597			cpu_fan_scale[i] =598				ct->ops->get_max(ct) * 100 / max_exhaust;599 600	have_all_controls = 1;601}602 603static void pm112_new_sensor(struct wf_sensor *sr)604{605	unsigned int i;606 607	if (!strncmp(sr->name, "cpu-temp-", 9)) {608		i = sr->name[9] - '0';609		if (sr->name[10] == 0 && i < NR_CORES &&610		    sens_cpu_temp[i] == NULL && wf_get_sensor(sr) == 0)611			sens_cpu_temp[i] = sr;612 613	} else if (!strncmp(sr->name, "cpu-power-", 10)) {614		i = sr->name[10] - '0';615		if (sr->name[11] == 0 && i < NR_CORES &&616		    sens_cpu_power[i] == NULL && wf_get_sensor(sr) == 0)617			sens_cpu_power[i] = sr;618	} else if (!strcmp(sr->name, "hd-temp")) {619		if (hd_temp == NULL && wf_get_sensor(sr) == 0)620			hd_temp = sr;621	} else if (!strcmp(sr->name, "slots-power")) {622		if (slots_power == NULL && wf_get_sensor(sr) == 0)623			slots_power = sr;624	} else if (!strcmp(sr->name, "backside-temp")) {625		if (u4_temp == NULL && wf_get_sensor(sr) == 0)626			u4_temp = sr;627	} else628		return;629 630	/* check if we have all the sensors we need */631	for (i = 0; i < nr_cores; ++i)632		if (sens_cpu_temp[i] == NULL || sens_cpu_power[i] == NULL)633			return;634 635	have_all_sensors = 1;636}637 638static int pm112_wf_notify(struct notifier_block *self,639			   unsigned long event, void *data)640{641	switch (event) {642	case WF_EVENT_NEW_SENSOR:643		pm112_new_sensor(data);644		break;645	case WF_EVENT_NEW_CONTROL:646		pm112_new_control(data);647		break;648	case WF_EVENT_TICK:649		if (have_all_controls && have_all_sensors)650			pm112_tick();651	}652	return 0;653}654 655static struct notifier_block pm112_events = {656	.notifier_call = pm112_wf_notify,657};658 659static int wf_pm112_probe(struct platform_device *dev)660{661	wf_register_client(&pm112_events);662	return 0;663}664 665static void wf_pm112_remove(struct platform_device *dev)666{667	wf_unregister_client(&pm112_events);668}669 670static struct platform_driver wf_pm112_driver = {671	.probe = wf_pm112_probe,672	.remove_new = wf_pm112_remove,673	.driver = {674		.name = "windfarm",675	},676};677 678static int __init wf_pm112_init(void)679{680	struct device_node *cpu;681 682	if (!of_machine_is_compatible("PowerMac11,2"))683		return -ENODEV;684 685	/* Count the number of CPU cores */686	nr_cores = 0;687	for_each_node_by_type(cpu, "cpu")688		++nr_cores;689 690	printk(KERN_INFO "windfarm: initializing for dual-core desktop G5\n");691 692#ifdef MODULE693	request_module("windfarm_smu_controls");694	request_module("windfarm_smu_sensors");695	request_module("windfarm_smu_sat");696	request_module("windfarm_lm75_sensor");697	request_module("windfarm_max6690_sensor");698	request_module("windfarm_cpufreq_clamp");699 700#endif /* MODULE */701 702	platform_driver_register(&wf_pm112_driver);703	return 0;704}705 706static void __exit wf_pm112_exit(void)707{708	platform_driver_unregister(&wf_pm112_driver);709}710 711module_init(wf_pm112_init);712module_exit(wf_pm112_exit);713 714MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");715MODULE_DESCRIPTION("Thermal control for PowerMac11,2");716MODULE_LICENSE("GPL");717MODULE_ALIAS("platform:windfarm");718