|
@@ -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>
|
|
@@ -121,7 +132,7 @@
|
|
|
|
|
|
#include "therm_pm72.h"
|
|
|
|
|
|
-#define VERSION "1.2b2"
|
|
|
+#define VERSION "1.3"
|
|
|
|
|
|
#undef DEBUG
|
|
|
|
|
@@ -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;
|
|
@@ -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);
|
|
|
|
|
|
/*
|
|
@@ -495,13 +508,20 @@ 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;
|
|
@@ -509,12 +529,15 @@ static int set_rpm_fan(int fan_index, int rpm)
|
|
|
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;
|
|
@@ -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)
|
|
@@ -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
|
|
@@ -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);
|
|
@@ -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);
|
|
|
|
|
|
/*
|
|
@@ -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;
|
|
@@ -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));
|
|
@@ -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)
|
|
|
{
|
|
@@ -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 };
|
|
@@ -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) {
|
|
@@ -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);
|
|
@@ -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);
|
|
|
|
|
@@ -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);
|
|
|
}
|
|
|
|
|
@@ -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;
|
|
|
|