windfarm_pm112.c
来自「linux 内核源代码」· C语言 代码 · 共 714 行 · 第 1/2 页
C
714 行
/* * Windfarm PowerMac thermal control. * Control loops for machines with SMU and PPC970MP processors. * * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org> * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp. * * Use and redistribute under the terms of the GNU GPL v2. */#include <linux/types.h>#include <linux/errno.h>#include <linux/kernel.h>#include <linux/device.h>#include <linux/platform_device.h>#include <linux/reboot.h>#include <asm/prom.h>#include <asm/smu.h>#include "windfarm.h"#include "windfarm_pid.h"#define VERSION "0.2"#define DEBUG#undef LOTSA_DEBUG#ifdef DEBUG#define DBG(args...) printk(args)#else#define DBG(args...) do { } while(0)#endif#ifdef LOTSA_DEBUG#define DBG_LOTS(args...) printk(args)#else#define DBG_LOTS(args...) do { } while(0)#endif/* define this to force CPU overtemp to 60 degree, useful for testing * the overtemp code */#undef HACKED_OVERTEMP/* We currently only handle 2 chips, 4 cores... */#define NR_CHIPS 2#define NR_CORES 4#define NR_CPU_FANS 3 * NR_CHIPS/* Controls and sensors */static struct wf_sensor *sens_cpu_temp[NR_CORES];static struct wf_sensor *sens_cpu_power[NR_CORES];static struct wf_sensor *hd_temp;static struct wf_sensor *slots_power;static struct wf_sensor *u4_temp;static struct wf_control *cpu_fans[NR_CPU_FANS];static char *cpu_fan_names[NR_CPU_FANS] = { "cpu-rear-fan-0", "cpu-rear-fan-1", "cpu-front-fan-0", "cpu-front-fan-1", "cpu-pump-0", "cpu-pump-1",};static struct wf_control *cpufreq_clamp;/* Second pump isn't required (and isn't actually present) */#define CPU_FANS_REQD (NR_CPU_FANS - 2)#define FIRST_PUMP 4#define LAST_PUMP 5/* We keep a temperature history for average calculation of 180s */#define CPU_TEMP_HIST_SIZE 180/* Scale factor for fan speed, *100 */static int cpu_fan_scale[NR_CPU_FANS] = { 100, 100, 97, /* inlet fans run at 97% of exhaust fan */ 97, 100, /* updated later */ 100, /* updated later */};static struct wf_control *backside_fan;static struct wf_control *slots_fan;static struct wf_control *drive_bay_fan;/* PID loop state */static struct wf_cpu_pid_state cpu_pid[NR_CORES];static u32 cpu_thist[CPU_TEMP_HIST_SIZE];static int cpu_thist_pt;static s64 cpu_thist_total;static s32 cpu_all_tmax = 100 << 16;static int cpu_last_target;static struct wf_pid_state backside_pid;static int backside_tick;static struct wf_pid_state slots_pid;static int slots_started;static struct wf_pid_state drive_bay_pid;static int drive_bay_tick;static int nr_cores;static int have_all_controls;static int have_all_sensors;static int started;static int failure_state;#define FAILURE_SENSOR 1#define FAILURE_FAN 2#define FAILURE_PERM 4#define FAILURE_LOW_OVERTEMP 8#define FAILURE_HIGH_OVERTEMP 16/* Overtemp values */#define LOW_OVER_AVERAGE 0#define LOW_OVER_IMMEDIATE (10 << 16)#define LOW_OVER_CLEAR ((-10) << 16)#define HIGH_OVER_IMMEDIATE (14 << 16)#define HIGH_OVER_AVERAGE (10 << 16)#define HIGH_OVER_IMMEDIATE (14 << 16)/* Implementation... */static int create_cpu_loop(int cpu){ int chip = cpu / 2; int core = cpu & 1; struct smu_sdbp_header *hdr; struct smu_sdbp_cpupiddata *piddata; struct wf_cpu_pid_param pid; struct wf_control *main_fan = cpu_fans[0]; s32 tmax; int fmin; /* Get PID params from the appropriate SAT */ hdr = smu_sat_get_sdb_partition(chip, 0xC8 + core, NULL); if (hdr == NULL) { printk(KERN_WARNING"windfarm: can't get CPU PID fan config\n"); return -EINVAL; } piddata = (struct smu_sdbp_cpupiddata *)&hdr[1]; /* Get FVT params to get Tmax; if not found, assume default */ hdr = smu_sat_get_sdb_partition(chip, 0xC4 + core, NULL); if (hdr) { struct smu_sdbp_fvt *fvt = (struct smu_sdbp_fvt *)&hdr[1]; tmax = fvt->maxtemp << 16; } else tmax = 95 << 16; /* default to 95 degrees C */ /* We keep a global tmax for overtemp calculations */ if (tmax < cpu_all_tmax) cpu_all_tmax = tmax; /* * Darwin has a minimum fan speed of 1000 rpm for the 4-way and * 515 for the 2-way. That appears to be overkill, so for now, * impose a minimum of 750 or 515. */ fmin = (nr_cores > 2) ? 750 : 515; /* Initialize PID loop */ pid.interval = 1; /* seconds */ pid.history_len = piddata->history_len; pid.gd = piddata->gd; pid.gp = piddata->gp; pid.gr = piddata->gr / piddata->history_len; pid.pmaxadj = (piddata->max_power << 16) - (piddata->power_adj << 8); pid.ttarget = tmax - (piddata->target_temp_delta << 16); pid.tmax = tmax; pid.min = main_fan->ops->get_min(main_fan); pid.max = main_fan->ops->get_max(main_fan); if (pid.min < fmin) pid.min = fmin; wf_cpu_pid_init(&cpu_pid[cpu], &pid); return 0;}static void cpu_max_all_fans(void){ int i; /* We max all CPU fans in case of a sensor error. We also do the * cpufreq clamping now, even if it's supposedly done later by the * generic code anyway, we do it earlier here to react faster */ if (cpufreq_clamp) wf_control_set_max(cpufreq_clamp); for (i = 0; i < NR_CPU_FANS; ++i) if (cpu_fans[i]) wf_control_set_max(cpu_fans[i]);}static int cpu_check_overtemp(s32 temp){ int new_state = 0; s32 t_avg, t_old; /* First check for immediate overtemps */ if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { new_state |= FAILURE_LOW_OVERTEMP; if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" " temperature !\n"); } if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { new_state |= FAILURE_HIGH_OVERTEMP; if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) printk(KERN_ERR "windfarm: Critical overtemp due to" " immediate CPU temperature !\n"); } /* We calculate a history of max temperatures and use that for the * overtemp management */ t_old = cpu_thist[cpu_thist_pt]; cpu_thist[cpu_thist_pt] = temp; cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; cpu_thist_total -= t_old; cpu_thist_total += temp; t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; DBG_LOTS("t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); /* Now check for average overtemps */ if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { new_state |= FAILURE_LOW_OVERTEMP; if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) printk(KERN_ERR "windfarm: Overtemp due to average CPU" " temperature !\n"); } if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { new_state |= FAILURE_HIGH_OVERTEMP; if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) printk(KERN_ERR "windfarm: Critical overtemp due to" " average CPU temperature !\n"); } /* Now handle overtemp conditions. We don't currently use the windfarm * overtemp handling core as it's not fully suited to the needs of those * new machine. This will be fixed later. */ if (new_state) { /* High overtemp -> immediate shutdown */ if (new_state & FAILURE_HIGH_OVERTEMP) machine_power_off(); if ((failure_state & new_state) != new_state) cpu_max_all_fans(); failure_state |= new_state; } else if ((failure_state & FAILURE_LOW_OVERTEMP) && (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); failure_state &= ~FAILURE_LOW_OVERTEMP; } return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP);}static void cpu_fans_tick(void){ int err, cpu; s32 greatest_delta = 0; s32 temp, power, t_max = 0; int i, t, target = 0; struct wf_sensor *sr; struct wf_control *ct; struct wf_cpu_pid_state *sp; DBG_LOTS(KERN_DEBUG); for (cpu = 0; cpu < nr_cores; ++cpu) { /* Get CPU core temperature */ sr = sens_cpu_temp[cpu]; err = sr->ops->get_value(sr, &temp); if (err) { DBG("\n"); printk(KERN_WARNING "windfarm: CPU %d temperature " "sensor error %d\n", cpu, err); failure_state |= FAILURE_SENSOR; cpu_max_all_fans(); return; } /* Keep track of highest temp */ t_max = max(t_max, temp); /* Get CPU power */ sr = sens_cpu_power[cpu]; err = sr->ops->get_value(sr, &power); if (err) { DBG("\n"); printk(KERN_WARNING "windfarm: CPU %d power " "sensor error %d\n", cpu, err); failure_state |= FAILURE_SENSOR; cpu_max_all_fans(); return; } /* Run PID */ sp = &cpu_pid[cpu]; t = wf_cpu_pid_run(sp, power, temp); if (cpu == 0 || sp->last_delta > greatest_delta) { greatest_delta = sp->last_delta; target = t; } DBG_LOTS("[%d] P=%d.%.3d T=%d.%.3d ", cpu, FIX32TOPRINT(power), FIX32TOPRINT(temp)); } DBG_LOTS("fans = %d, t_max = %d.%03d\n", target, FIX32TOPRINT(t_max)); /* Darwin limits decrease to 20 per iteration */ if (target < (cpu_last_target - 20)) target = cpu_last_target - 20; cpu_last_target = target; for (cpu = 0; cpu < nr_cores; ++cpu) cpu_pid[cpu].target = target; /* Handle possible overtemps */ if (cpu_check_overtemp(t_max)) return; /* Set fans */ for (i = 0; i < NR_CPU_FANS; ++i) { ct = cpu_fans[i]; if (ct == NULL) continue; err = ct->ops->set_value(ct, target * cpu_fan_scale[i] / 100); if (err) { printk(KERN_WARNING "windfarm: fan %s reports " "error %d\n", ct->name, err); failure_state |= FAILURE_FAN; break; } }}/* Backside/U4 fan */static struct wf_pid_param backside_param = { .interval = 5, .history_len = 2, .gd = 48 << 20, .gp = 5 << 20, .gr = 0, .itarget = 64 << 16, .additive = 1,};static void backside_fan_tick(void){ s32 temp; int speed; int err; if (!backside_fan || !u4_temp)
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?