Skip to content

Commit

Permalink
[POWERPC] Xserve G5 thermal control fixes
Browse files Browse the repository at this point in the history
The thermal control for the Xserve G5s had a few issues. For one, the
way to program the RPM fans speeds into the FCU is different between it
and the desktop models, which I didn't figure out until recently, and it
was missing a control loop for the slots fan, running it too fast.  Both
of those problems were causing the machine to be much more noisy than
necessary.  This patch also changes the fixed value of the slots fan for
desktop G5s to 40% instead of 50%.  It seems to still have a pretty good
airflow that way and is much less noisy.

Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
  • Loading branch information
Benjamin Herrenschmidt authored and Paul Mackerras committed Jul 7, 2006
1 parent e7c1f69 commit 861fa77
Show file tree
Hide file tree
Showing 2 changed files with 234 additions and 17 deletions.
218 changes: 203 additions & 15 deletions drivers/macintosh/therm_pm72.c
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,17 @@
* - Use min/max macros here or there
* - Latest darwin updated U3H min fan speed to 20% PWM
*
* July. 06, 2006 : 1.3
* - Fix setting of RPM fans on Xserve G5 (they were going too fast)
* - Add missing slots fan control loop for Xserve G5
* - Lower fixed slots fan speed from 50% to 40% on desktop G5s. We
* still can't properly implement the control loop for these, so let's
* reduce the noise a little bit, it appears that 40% still gives us
* a pretty good air flow
* - Add code to "tickle" the FCU regulary so it doesn't think that
* we are gone while in fact, the machine just didn't need any fan
* speed change lately
*
*/

#include <linux/types.h>
Expand All @@ -121,7 +132,7 @@

#include "therm_pm72.h"

#define VERSION "1.2b2"
#define VERSION "1.3"

#undef DEBUG

Expand All @@ -146,6 +157,7 @@ static struct basckside_pid_params backside_params;
static struct backside_pid_state backside_state;
static struct drives_pid_state drives_state;
static struct dimm_pid_state dimms_state;
static struct slots_pid_state slots_state;
static int state;
static int cpu_count;
static int cpu_pid_type;
Expand All @@ -154,7 +166,8 @@ static struct completion ctrl_complete;
static int critical_state;
static int rackmac;
static s32 dimm_output_clamp;

static int fcu_rpm_shift;
static int fcu_tickle_ticks;
static DECLARE_MUTEX(driver_lock);

/*
Expand Down Expand Up @@ -495,26 +508,36 @@ static int start_fcu(void)
rc = fan_write_reg(0x2e, &buf, 1);
if (rc < 0)
return -EIO;
rc = fan_read_reg(0, &buf, 1);
if (rc < 0)
return -EIO;
fcu_rpm_shift = (buf == 1) ? 2 : 3;
printk(KERN_DEBUG "FCU Initialized, RPM fan shift is %d\n",
fcu_rpm_shift);

return 0;
}

static int set_rpm_fan(int fan_index, int rpm)
{
unsigned char buf[2];
int rc, id;
int rc, id, min, max;

if (fcu_fans[fan_index].type != FCU_FAN_RPM)
return -EINVAL;
id = fcu_fans[fan_index].id;
if (id == FCU_FAN_ABSENT_ID)
return -EINVAL;

if (rpm < 300)
rpm = 300;
else if (rpm > 8191)
rpm = 8191;
buf[0] = rpm >> 5;
buf[1] = rpm << 3;
min = 2400 >> fcu_rpm_shift;
max = 56000 >> fcu_rpm_shift;

if (rpm < min)
rpm = min;
else if (rpm > max)
rpm = max;
buf[0] = rpm >> (8 - fcu_rpm_shift);
buf[1] = rpm << fcu_rpm_shift;
rc = fan_write_reg(0x10 + (id * 2), buf, 2);
if (rc < 0)
return -EIO;
Expand Down Expand Up @@ -551,7 +574,7 @@ static int get_rpm_fan(int fan_index, int programmed)
if (rc != 2)
return -EIO;

return (buf[0] << 5) | buf[1] >> 3;
return (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift;
}

static int set_pwm_fan(int fan_index, int pwm)
Expand Down Expand Up @@ -609,6 +632,26 @@ static int get_pwm_fan(int fan_index)
return (buf[0] * 1000) / 2559;
}

static void tickle_fcu(void)
{
int pwm;

pwm = get_pwm_fan(SLOTS_FAN_PWM_INDEX);

DBG("FCU Tickle, slots fan is: %d\n", pwm);
if (pwm < 0)
pwm = 100;

if (!rackmac) {
pwm = SLOTS_FAN_DEFAULT_PWM;
} else if (pwm < SLOTS_PID_OUTPUT_MIN)
pwm = SLOTS_PID_OUTPUT_MIN;

/* That is hopefully enough to make the FCU happy */
set_pwm_fan(SLOTS_FAN_PWM_INDEX, pwm);
}


/*
* Utility routine to read the CPU calibration EEPROM data
* from the device-tree
Expand Down Expand Up @@ -715,6 +758,9 @@ BUILD_SHOW_FUNC_INT(backside_fan_pwm, backside_state.pwm)
BUILD_SHOW_FUNC_FIX(drives_temperature, drives_state.last_temp)
BUILD_SHOW_FUNC_INT(drives_fan_rpm, drives_state.rpm)

BUILD_SHOW_FUNC_FIX(slots_temperature, slots_state.last_temp)
BUILD_SHOW_FUNC_INT(slots_fan_pwm, slots_state.pwm)

BUILD_SHOW_FUNC_FIX(dimms_temperature, dimms_state.last_temp)

static DEVICE_ATTR(cpu0_temperature,S_IRUGO,show_cpu0_temperature,NULL);
Expand All @@ -735,6 +781,9 @@ static DEVICE_ATTR(backside_fan_pwm,S_IRUGO,show_backside_fan_pwm,NULL);
static DEVICE_ATTR(drives_temperature,S_IRUGO,show_drives_temperature,NULL);
static DEVICE_ATTR(drives_fan_rpm,S_IRUGO,show_drives_fan_rpm,NULL);

static DEVICE_ATTR(slots_temperature,S_IRUGO,show_slots_temperature,NULL);
static DEVICE_ATTR(slots_fan_pwm,S_IRUGO,show_slots_fan_pwm,NULL);

static DEVICE_ATTR(dimms_temperature,S_IRUGO,show_dimms_temperature,NULL);

/*
Expand Down Expand Up @@ -1076,6 +1125,9 @@ static void do_monitor_cpu_rack(struct cpu_pid_state *state)
fan_min = dimm_output_clamp;
fan_min = max(fan_min, (int)state->mpu.rminn_intake_fan);

DBG(" CPU min mpu = %d, min dimm = %d\n",
state->mpu.rminn_intake_fan, dimm_output_clamp);

state->rpm = max(state->rpm, (int)fan_min);
state->rpm = min(state->rpm, (int)state->mpu.rmaxn_intake_fan);
state->intake_rpm = state->rpm;
Expand Down Expand Up @@ -1374,7 +1426,8 @@ static void do_monitor_drives(struct drives_pid_state *state)
DBG(" current rpm: %d\n", state->rpm);

/* Get some sensor readings */
temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor, DS1775_TEMP)) << 8;
temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor,
DS1775_TEMP)) << 8;
state->last_temp = temp;
DBG(" temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
FIX32TOPRINT(DRIVES_PID_INPUT_TARGET));
Expand Down Expand Up @@ -1575,7 +1628,7 @@ static int init_dimms_state(struct dimm_pid_state *state)
}

/*
* Dispose of the state data for the drives control loop
* Dispose of the state data for the DIMM control loop
*/
static void dispose_dimms_state(struct dimm_pid_state *state)
{
Expand All @@ -1588,6 +1641,127 @@ static void dispose_dimms_state(struct dimm_pid_state *state)
state->monitor = NULL;
}

/*
* Slots fan control loop
*/
static void do_monitor_slots(struct slots_pid_state *state)
{
s32 temp, integral, derivative;
s64 integ_p, deriv_p, prop_p, sum;
int i, rc;

if (--state->ticks != 0)
return;
state->ticks = SLOTS_PID_INTERVAL;

DBG("slots:\n");

/* Check fan status */
rc = get_pwm_fan(SLOTS_FAN_PWM_INDEX);
if (rc < 0) {
printk(KERN_WARNING "Error %d reading slots fan !\n", rc);
/* XXX What do we do now ? */
} else
state->pwm = rc;
DBG(" current pwm: %d\n", state->pwm);

/* Get some sensor readings */
temp = le16_to_cpu(i2c_smbus_read_word_data(state->monitor,
DS1775_TEMP)) << 8;
state->last_temp = temp;
DBG(" temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
FIX32TOPRINT(SLOTS_PID_INPUT_TARGET));

/* Store temperature and error in history array */
state->cur_sample = (state->cur_sample + 1) % SLOTS_PID_HISTORY_SIZE;
state->sample_history[state->cur_sample] = temp;
state->error_history[state->cur_sample] = temp - SLOTS_PID_INPUT_TARGET;

/* If first loop, fill the history table */
if (state->first) {
for (i = 0; i < (SLOTS_PID_HISTORY_SIZE - 1); i++) {
state->cur_sample = (state->cur_sample + 1) %
SLOTS_PID_HISTORY_SIZE;
state->sample_history[state->cur_sample] = temp;
state->error_history[state->cur_sample] =
temp - SLOTS_PID_INPUT_TARGET;
}
state->first = 0;
}

/* Calculate the integral term */
sum = 0;
integral = 0;
for (i = 0; i < SLOTS_PID_HISTORY_SIZE; i++)
integral += state->error_history[i];
integral *= SLOTS_PID_INTERVAL;
DBG(" integral: %08x\n", integral);
integ_p = ((s64)SLOTS_PID_G_r) * (s64)integral;
DBG(" integ_p: %d\n", (int)(integ_p >> 36));
sum += integ_p;

/* Calculate the derivative term */
derivative = state->error_history[state->cur_sample] -
state->error_history[(state->cur_sample + SLOTS_PID_HISTORY_SIZE - 1)
% SLOTS_PID_HISTORY_SIZE];
derivative /= SLOTS_PID_INTERVAL;
deriv_p = ((s64)SLOTS_PID_G_d) * (s64)derivative;
DBG(" deriv_p: %d\n", (int)(deriv_p >> 36));
sum += deriv_p;

/* Calculate the proportional term */
prop_p = ((s64)SLOTS_PID_G_p) * (s64)(state->error_history[state->cur_sample]);
DBG(" prop_p: %d\n", (int)(prop_p >> 36));
sum += prop_p;

/* Scale sum */
sum >>= 36;

DBG(" sum: %d\n", (int)sum);
state->pwm = (s32)sum;

state->pwm = max(state->pwm, SLOTS_PID_OUTPUT_MIN);
state->pwm = min(state->pwm, SLOTS_PID_OUTPUT_MAX);

DBG("** DRIVES PWM: %d\n", (int)state->pwm);
set_pwm_fan(SLOTS_FAN_PWM_INDEX, state->pwm);
}

/*
* Initialize the state structure for the slots bay fan control loop
*/
static int init_slots_state(struct slots_pid_state *state)
{
state->ticks = 1;
state->first = 1;
state->pwm = 50;

state->monitor = attach_i2c_chip(XSERVE_SLOTS_LM75, "slots_temp");
if (state->monitor == NULL)
return -ENODEV;

device_create_file(&of_dev->dev, &dev_attr_slots_temperature);
device_create_file(&of_dev->dev, &dev_attr_slots_fan_pwm);

return 0;
}

/*
* Dispose of the state data for the slots control loop
*/
static void dispose_slots_state(struct slots_pid_state *state)
{
if (state->monitor == NULL)
return;

device_remove_file(&of_dev->dev, &dev_attr_slots_temperature);
device_remove_file(&of_dev->dev, &dev_attr_slots_fan_pwm);

detach_i2c_chip(state->monitor);
state->monitor = NULL;
}


static int call_critical_overtemp(void)
{
char *argv[] = { critical_overtemp_path, NULL };
Expand Down Expand Up @@ -1617,14 +1791,17 @@ static int main_control_loop(void *x)
goto out;
}

/* Set the PCI fan once for now */
set_pwm_fan(SLOTS_FAN_PWM_INDEX, SLOTS_FAN_DEFAULT_PWM);
/* Set the PCI fan once for now on non-RackMac */
if (!rackmac)
set_pwm_fan(SLOTS_FAN_PWM_INDEX, SLOTS_FAN_DEFAULT_PWM);

/* Initialize ADCs */
initialize_adc(&cpu_state[0]);
if (cpu_state[1].monitor != NULL)
initialize_adc(&cpu_state[1]);

fcu_tickle_ticks = FCU_TICKLE_TICKS;

up(&driver_lock);

while (state == state_attached) {
Expand All @@ -1634,6 +1811,12 @@ static int main_control_loop(void *x)

down(&driver_lock);

/* Tickle the FCU just in case */
if (--fcu_tickle_ticks < 0) {
fcu_tickle_ticks = FCU_TICKLE_TICKS;
tickle_fcu();
}

/* First, we always calculate the new DIMMs state on an Xserve */
if (rackmac)
do_monitor_dimms(&dimms_state);
Expand All @@ -1654,7 +1837,9 @@ static int main_control_loop(void *x)
}
/* Then, the rest */
do_monitor_backside(&backside_state);
if (!rackmac)
if (rackmac)
do_monitor_slots(&slots_state);
else
do_monitor_drives(&drives_state);
up(&driver_lock);

Expand Down Expand Up @@ -1696,6 +1881,7 @@ static void dispose_control_loops(void)
dispose_cpu_state(&cpu_state[1]);
dispose_backside_state(&backside_state);
dispose_drives_state(&drives_state);
dispose_slots_state(&slots_state);
dispose_dimms_state(&dimms_state);
}

Expand Down Expand Up @@ -1745,6 +1931,8 @@ static int create_control_loops(void)
goto fail;
if (rackmac && init_dimms_state(&dimms_state))
goto fail;
if (rackmac && init_slots_state(&slots_state))
goto fail;
if (!rackmac && init_drives_state(&drives_state))
goto fail;

Expand Down
Loading

0 comments on commit 861fa77

Please sign in to comment.