// SPDX-License-Identifier: GPL-2.0-only /* * Windfarm PowerMac thermal control. SMU based 1 CPU desktop control loops * * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp. * <benh@kernel.crashing.org> * * The algorithm used is the PID control algorithm, used the same * way the published Darwin code does, using the same values that * are present in the Darwin 8.2 snapshot property lists (note however * that none of the code has been re-used, it's a complete re-implementation * * The various control loops found in Darwin config file are: * * PowerMac9,1 * =========== * * Has 3 control loops: CPU fans is similar to PowerMac8,1 (though it doesn't * try to play with other control loops fans). Drive bay is rather basic PID * with one sensor and one fan. Slots area is a bit different as the Darwin * driver is supposed to be capable of working in a special "AGP" mode which * involves the presence of an AGP sensor and an AGP fan (possibly on the * AGP card itself). I can't deal with that special mode as I don't have * access to those additional sensor/fans for now (though ultimately, it would * be possible to add sensor objects for them) so I'm only implementing the * basic PCI slot control loop
*/
/* Set to kick the control loop into life */ staticint wf_smu_all_controls_ok, wf_smu_all_sensors_ok; staticbool wf_smu_started; staticbool wf_smu_overtemp;
/* Failure handling.. could be nicer */ #define FAILURE_FAN 0x01 #define FAILURE_SENSOR 0x02 #define FAILURE_OVERTEMP 0x04
/* First, locate the PID params in SMU SBD */
hdr = smu_get_sdb_partition(SMU_SDB_CPUPIDDATA_ID, NULL); if (!hdr) {
printk(KERN_WARNING "windfarm: CPU PID fan config not found " "max fan speed\n"); goto fail;
}
piddata = (struct smu_sdbp_cpupiddata *)&hdr[1];
/* Get the FVT params for operating point 0 (the only supported one * for now) in order to get tmax
*/
hdr = smu_get_sdb_partition(SMU_SDB_FVT_ID, NULL); if (hdr) {
fvt = (struct smu_sdbp_fvt *)&hdr[1];
tmax = ((s32)fvt->maxtemp) << 16;
} else
tmax = 0x5e0000; /* 94 degree default */
/* Alloc & initialize state */
wf_smu_cpu_fans = kmalloc(sizeof(struct wf_smu_cpu_fans_state),
GFP_KERNEL); if (wf_smu_cpu_fans == NULL) goto fail;
wf_smu_cpu_fans->ticks = 1;
/* Fill PID params */
pid_param.interval = WF_SMU_CPU_FANS_INTERVAL;
pid_param.history_len = piddata->history_len; if (pid_param.history_len > WF_CPU_PID_MAX_HISTORY) {
printk(KERN_WARNING "windfarm: History size overflow on " "CPU control loop (%d)\n", piddata->history_len);
pid_param.history_len = WF_CPU_PID_MAX_HISTORY;
}
pid_param.gd = piddata->gd;
pid_param.gp = piddata->gp;
pid_param.gr = piddata->gr / pid_param.history_len;
if (!wf_smu_started) {
DBG("wf: creating control loops !\n");
wf_smu_create_drive_fans();
wf_smu_create_slots_fans();
wf_smu_create_cpu_fans();
wf_smu_started = true;
}
/* Skipping ticks */ if (wf_smu_skipping && --wf_smu_skipping) return;
wf_smu_failure_state = 0; if (wf_smu_drive_fans)
wf_smu_drive_fans_tick(wf_smu_drive_fans); if (wf_smu_slots_fans)
wf_smu_slots_fans_tick(wf_smu_slots_fans); if (wf_smu_cpu_fans)
wf_smu_cpu_fans_tick(wf_smu_cpu_fans);
/* If entering failure mode, clamp cpufreq and ramp all * fans to full speed.
*/ if (wf_smu_failure_state && !last_failure) { if (cpufreq_clamp)
wf_control_set_max(cpufreq_clamp); if (fan_cpu_main)
wf_control_set_max(fan_cpu_main); if (fan_cpu_second)
wf_control_set_max(fan_cpu_second); if (fan_cpu_third)
wf_control_set_max(fan_cpu_third); if (fan_hd)
wf_control_set_max(fan_hd); if (fan_slots)
wf_control_set_max(fan_slots);
}
/* If leaving failure mode, unclamp cpufreq and readjust * all fans on next iteration
*/ if (!wf_smu_failure_state && last_failure) { if (cpufreq_clamp)
wf_control_set_min(cpufreq_clamp);
wf_smu_readjust = 1;
}
/* Overtemp condition detected, notify and start skipping a couple * ticks to let the temperature go down
*/ if (new_failure & FAILURE_OVERTEMP) {
wf_set_overtemp();
wf_smu_skipping = 2;
wf_smu_overtemp = true;
}
/* We only clear the overtemp condition if overtemp is cleared * _and_ no other failure is present. Since a sensor error will * clear the overtemp condition (can't measure temperature) at * the control loop levels, but we don't want to keep it clear * here in this case
*/ if (!wf_smu_failure_state && wf_smu_overtemp) {
wf_clear_overtemp();
wf_smu_overtemp = false;
}
}
staticvoid wf_smu_new_control(struct wf_control *ct)
{ if (wf_smu_all_controls_ok) return;
if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-rear-fan-0")) { if (wf_get_control(ct) == 0)
fan_cpu_main = ct;
}
if (fan_cpu_second == NULL && !strcmp(ct->name, "cpu-rear-fan-1")) { if (wf_get_control(ct) == 0)
fan_cpu_second = ct;
}
if (fan_cpu_third == NULL && !strcmp(ct->name, "cpu-front-fan-0")) { if (wf_get_control(ct) == 0)
fan_cpu_third = ct;
}
if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) { if (wf_get_control(ct) == 0)
cpufreq_clamp = ct;
}
if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) { if (wf_get_control(ct) == 0)
fan_hd = ct;
}
if (fan_slots == NULL && !strcmp(ct->name, "slots-fan")) { if (wf_get_control(ct) == 0)
fan_slots = ct;
}
/* XXX We don't have yet a guarantee that our callback isn't * in progress when returning from wf_unregister_client, so * we add an arbitrary delay. I'll have to fix that in the core
*/
msleep(1000);
/* Release all sensors */ /* One more crappy race: I don't think we have any guarantee here * that the attribute callback won't race with the sensor beeing * disposed of, and I'm not 100% certain what best way to deal * with that except by adding locks all over... I'll do that * eventually but heh, who ever rmmod this module anyway ?
*/ if (sensor_cpu_power)
wf_put_sensor(sensor_cpu_power); if (sensor_cpu_temp)
wf_put_sensor(sensor_cpu_temp); if (sensor_hd_temp)
wf_put_sensor(sensor_hd_temp); if (sensor_slots_power)
wf_put_sensor(sensor_slots_power);
/* Release all controls */ if (fan_cpu_main)
wf_put_control(fan_cpu_main); if (fan_cpu_second)
wf_put_control(fan_cpu_second); if (fan_cpu_third)
wf_put_control(fan_cpu_third); if (fan_hd)
wf_put_control(fan_hd); if (fan_slots)
wf_put_control(fan_slots); if (cpufreq_clamp)
wf_put_control(cpufreq_clamp);
/* Destroy control loops state structures */
kfree(wf_smu_slots_fans);
kfree(wf_smu_drive_fans);
kfree(wf_smu_cpu_fans);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.