// SPDX-License-Identifier: GPL-2.0-only /* * Windfarm PowerMac thermal control. iMac G5 * * (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: * * PowerMac8,1 and PowerMac8,2 * =========================== * * System Fans control loop. Different based on models. In addition to the * usual PID algorithm, the control loop gets 2 additional pairs of linear * scaling factors (scale/offsets) expressed as 4.12 fixed point values * signed offset, unsigned scale) * * The targets are modified such as: * - the linked control (second control) gets the target value as-is * (typically the drive fan) * - the main control (first control) gets the target value scaled with * the first pair of factors, and is then modified as below * - the value of the target of the CPU Fan control loop is retrieved, * scaled with the second pair of factors, and the max of that and * the scaled target is applied to the main control. * * # model_id: 2 * controls : system-fan, drive-bay-fan * sensors : hd-temp * PID params : G_d = 0x15400000 * G_p = 0x00200000 * G_r = 0x000002fd * History = 2 entries * Input target = 0x3a0000 * Interval = 5s * linear-factors : offset = 0xff38 scale = 0x0ccd * offset = 0x0208 scale = 0x07ae * * # model_id: 3 * controls : system-fan, drive-bay-fan * sensors : hd-temp * PID params : G_d = 0x08e00000 * G_p = 0x00566666 * G_r = 0x0000072b * History = 2 entries * Input target = 0x350000 * Interval = 5s * linear-factors : offset = 0xff38 scale = 0x0ccd * offset = 0x0000 scale = 0x0000 * * # model_id: 5 * controls : system-fan * sensors : hd-temp * PID params : G_d = 0x15400000 * G_p = 0x00233333 * G_r = 0x000002fd * History = 2 entries * Input target = 0x3a0000 * Interval = 5s * linear-factors : offset = 0x0000 scale = 0x1000 * offset = 0x0091 scale = 0x0bae * * CPU Fan control loop. The loop is identical for all models. it * has an additional pair of scaling factor. This is used to scale the * systems fan control loop target result (the one before it gets scaled * by the System Fans control loop itself). Then, the max value of the * calculated target value and system fan value is sent to the fans * * controls : cpu-fan * sensors : cpu-temp cpu-power * PID params : From SMU sdb partition * linear-factors : offset = 0xfb50 scale = 0x1000 * * CPU Slew control loop. Not implemented. The cpufreq driver in linux is * completely separate for now, though we could find a way to link it, either * as a client reacting to overtemp notifications, or directling monitoring * the CPU temperature * * WARNING ! The CPU control loop requires the CPU tmax for the current * operating point. However, we currently are completely separated from * the cpufreq driver and thus do not know what the current operating * point is. Fortunately, we also do not have any hardware supporting anything * but operating point 0 at the moment, thus we just peek that value directly * from the SDB partition. If we ever end up with actually slewing the system * clock and thus changing operating points, we'll have to find a way to * communicate with the CPU freq driver;
*/
/* Parameters for the System Fans control loop. Parameters * not in this table such as interval, history size, ... * are common to all versions and thus hard coded for now.
*/ struct wf_smu_sys_fans_param { int model_id;
s32 itarget;
s32 gd, gp, gr;
/* State data used by the system fans control loop
*/ struct wf_smu_sys_fans_state { int ticks;
s32 sys_setpoint;
s32 hd_setpoint;
s16 offset0;
u16 scale0;
s16 offset1;
u16 scale1; struct wf_pid_state pid;
};
/* State data used by the cpu fans control loop
*/ struct wf_smu_cpu_fans_state { int ticks;
s32 cpu_setpoint;
s32 scale;
s32 offset; struct wf_cpu_pid_state pid;
};
/* First, locate the params for this model */ for (i = 0; i < WF_SMU_SYS_FANS_NUM_CONFIGS; i++) if (wf_smu_sys_all_params[i].model_id == wf_smu_mach_model) {
param = &wf_smu_sys_all_params[i]; break;
}
/* No params found, put fans to max */ if (param == NULL) {
printk(KERN_WARNING "windfarm: System fan config not found " "for this machine model, max fan speed\n"); goto fail;
}
DBG("wf: System Fan control initialized.\n");
DBG(" itarged=%d.%03d, min=%d RPM, max=%d RPM\n",
FIX32TOPRINT(pid_param.itarget), pid_param.min, pid_param.max); return;
fail:
if (fan_system)
wf_control_set_max(fan_system); if (fan_hd)
wf_control_set_max(fan_hd);
}
/* 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;
/* 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_system)
wf_control_set_max(fan_system); if (fan_cpu_main)
wf_control_set_max(fan_cpu_main); if (fan_hd)
wf_control_set_max(fan_hd);
}
/* 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-fan")) { if (wf_get_control(ct) == 0)
fan_cpu_main = ct;
}
if (fan_system == NULL && !strcmp(ct->name, "system-fan")) { if (wf_get_control(ct) == 0)
fan_system = ct;
}
if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) { if (wf_get_control(ct) == 0)
cpufreq_clamp = ct;
}
/* Darwin property list says the HD fan is only for model ID * 0, 1, 2 and 3
*/
if (wf_smu_mach_model > 3) { if (fan_system && fan_cpu_main && cpufreq_clamp)
wf_smu_all_controls_ok = 1; return;
}
if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) { if (wf_get_control(ct) == 0)
fan_hd = 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);
/* Release all controls */ if (fan_cpu_main)
wf_put_control(fan_cpu_main); if (fan_hd)
wf_put_control(fan_hd); if (fan_system)
wf_put_control(fan_system); if (cpufreq_clamp)
wf_put_control(cpufreq_clamp);
/* Destroy control loops state structures */
kfree(wf_smu_sys_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.