* - 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/config.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/sched.h>
+#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/spinlock.h>
#include <linux/reboot.h>
#include <linux/kmod.h>
#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include "therm_pm72.h"
-#define VERSION "1.3"
+#define VERSION "1.2b2"
#undef DEBUG
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;
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);
/*
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, min, max;
+ int rc, id;
if (fcu_fans[fan_index].type != FCU_FAN_RPM)
return -EINVAL;
if (id == FCU_FAN_ABSENT_ID)
return -EINVAL;
- 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;
+ if (rpm < 300)
+ rpm = 300;
+ else if (rpm > 8191)
+ rpm = 8191;
+ buf[0] = rpm >> 5;
+ buf[1] = rpm << 3;
rc = fan_write_reg(0x10 + (id * 2), buf, 2);
if (rc < 0)
return -EIO;
if (rc != 2)
return -EIO;
- return (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift;
+ return (buf[0] << 5) | buf[1] >> 3;
}
static int set_pwm_fan(int fan_index, int pwm)
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
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);
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);
/*
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;
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));
}
/*
- * Dispose of the state data for the DIMM control loop
+ * Dispose of the state data for the drives control loop
*/
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 };
goto out;
}
- /* Set the PCI fan once for now on non-RackMac */
- if (!rackmac)
- set_pwm_fan(SLOTS_FAN_PWM_INDEX, SLOTS_FAN_DEFAULT_PWM);
+ /* Set the PCI fan once for now */
+ 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) {
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);
}
/* Then, the rest */
do_monitor_backside(&backside_state);
- if (rackmac)
- do_monitor_slots(&slots_state);
- else
+ if (!rackmac)
do_monitor_drives(&drives_state);
up(&driver_lock);
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);
}
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;