vserver 2.0 rc7
[linux-2.6.git] / drivers / macintosh / therm_pm72.c
index 3c606ef..82336a5 100644 (file)
  *          decisions, like slewing down CPUs
  *       - Deal with fan and i2c failures in a better way
  *       - Maybe do a generic PID based on params used for
- *         U3 and Drives ?
- *        - Add RackMac3,1 support (XServe g5)
+ *         U3 and Drives ? Definitely need to factor code a bit
+ *          bettter... also make sensor detection more robust using
+ *          the device-tree to probe for them
+ *        - Figure out how to get the slots consumption and set the
+ *          slots fan accordingly
  *
  * History:
  *
  *     - Add new CPU cooling algorithm for machines with liquid cooling
  *     - Workaround for some PowerMac7,3 with empty "fan" node in the devtree
  *     - Fix a signed/unsigned compare issue in some PID loops
+ *
+ *  Mar. 10, 2005 : 1.2
+ *     - Add basic support for Xserve G5
+ *     - Retreive pumps min/max from EEPROM image in device-tree (broken)
+ *     - Use min/max macros here or there
+ *     - Latest darwin updated U3H min fan speed to 20% PWM
+ *
  */
 
 #include <linux/config.h>
 
 #include "therm_pm72.h"
 
-#define VERSION "1.1"
+#define VERSION "1.2b2"
 
 #undef DEBUG
 
 static struct of_device *              of_dev;
 static struct i2c_adapter *            u3_0;
 static struct i2c_adapter *            u3_1;
+static struct i2c_adapter *            k2;
 static struct i2c_client *             fcu;
 static struct cpu_pid_state            cpu_state[2];
 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 int                             state;
 static int                             cpu_count;
 static int                             cpu_pid_type;
 static pid_t                           ctrl_task;
 static struct completion               ctrl_complete;
 static int                             critical_state;
+static int                             rackmac;
+static s32                             dimm_output_clamp;
+
 static DECLARE_MUTEX(driver_lock);
 
 /*
- * We have 2 types of CPU PID control. One is "split" old style control
+ * We have 3 types of CPU PID control. One is "split" old style control
  * for intake & exhaust fans, the other is "combined" control for both
  * CPUs that also deals with the pumps when present. To be "compatible"
  * with OS X at this point, we only use "COMBINED" on the machines that
@@ -155,6 +170,7 @@ static DECLARE_MUTEX(driver_lock);
  */
 #define CPU_PID_TYPE_SPLIT     0
 #define CPU_PID_TYPE_COMBINED  1
+#define CPU_PID_TYPE_RACKMAC   2
 
 /*
  * This table describes all fans in the FCU. The "id" and "type" values
@@ -177,7 +193,7 @@ struct fcu_fan_table
 
 struct fcu_fan_table   fcu_fans[] = {
        [BACKSIDE_FAN_PWM_INDEX] = {
-               .loc    = "BACKSIDE",
+               .loc    = "BACKSIDE,SYS CTRLR FAN",
                .type   = FCU_FAN_PWM,
                .id     = BACKSIDE_FAN_PWM_DEFAULT_ID,
        },
@@ -187,7 +203,7 @@ struct fcu_fan_table        fcu_fans[] = {
                .id     = DRIVES_FAN_RPM_DEFAULT_ID,
        },
        [SLOTS_FAN_PWM_INDEX] = {
-               .loc    = "SLOT",
+               .loc    = "SLOT,PCI FAN",
                .type   = FCU_FAN_PWM,
                .id     = SLOTS_FAN_PWM_DEFAULT_ID,
        },
@@ -224,6 +240,37 @@ struct fcu_fan_table       fcu_fans[] = {
                .type   = FCU_FAN_RPM,
                .id     = FCU_FAN_ABSENT_ID,
        },
+       /* Xserve fans */
+       [CPU_A1_FAN_RPM_INDEX] = {
+               .loc    = "CPU A 1",
+               .type   = FCU_FAN_RPM,
+               .id     = FCU_FAN_ABSENT_ID,
+       },
+       [CPU_A2_FAN_RPM_INDEX] = {
+               .loc    = "CPU A 2",
+               .type   = FCU_FAN_RPM,
+               .id     = FCU_FAN_ABSENT_ID,
+       },
+       [CPU_A3_FAN_RPM_INDEX] = {
+               .loc    = "CPU A 3",
+               .type   = FCU_FAN_RPM,
+               .id     = FCU_FAN_ABSENT_ID,
+       },
+       [CPU_B1_FAN_RPM_INDEX] = {
+               .loc    = "CPU B 1",
+               .type   = FCU_FAN_RPM,
+               .id     = FCU_FAN_ABSENT_ID,
+       },
+       [CPU_B2_FAN_RPM_INDEX] = {
+               .loc    = "CPU B 2",
+               .type   = FCU_FAN_RPM,
+               .id     = FCU_FAN_ABSENT_ID,
+       },
+       [CPU_B3_FAN_RPM_INDEX] = {
+               .loc    = "CPU B 3",
+               .type   = FCU_FAN_RPM,
+               .id     = FCU_FAN_ABSENT_ID,
+       },
 };
 
 /*
@@ -251,7 +298,9 @@ static struct i2c_client *attach_i2c_chip(int id, const char *name)
        struct i2c_client *clt;
        struct i2c_adapter *adap;
 
-       if (id & 0x100)
+       if (id & 0x200)
+               adap = k2;
+       else if (id & 0x100)
                adap = u3_1;
        else
                adap = u3_0;
@@ -361,6 +410,31 @@ static int read_smon_adc(struct cpu_pid_state *state, int chan)
        }
 }
 
+static int read_lm87_reg(struct i2c_client * chip, int reg)
+{
+       int rc, tries = 0;
+       u8 buf;
+
+       for (;;) {
+               /* Set address */
+               buf = (u8)reg;
+               rc = i2c_master_send(chip, &buf, 1);
+               if (rc <= 0)
+                       goto error;
+               rc = i2c_master_recv(chip, &buf, 1);
+               if (rc <= 0)
+                       goto error;
+               return (int)buf;
+       error:
+               DBG("Error reading LM87, retrying...\n");
+               if (++tries > 10) {
+                       printk(KERN_ERR "therm_pm72: Error reading LM87 !\n");
+                       return -1;
+               }
+               msleep(10);
+       }
+}
+
 static int fan_read_reg(int reg, unsigned char *buf, int nb)
 {
        int tries, nr, nw;
@@ -570,6 +644,38 @@ static int read_eeprom(int cpu, struct mpu_data *out)
        return 0;
 }
 
+static void fetch_cpu_pumps_minmax(void)
+{
+       struct cpu_pid_state *state0 = &cpu_state[0];
+       struct cpu_pid_state *state1 = &cpu_state[1];
+       u16 pump_min = 0, pump_max = 0xffff;
+       u16 tmp[4];
+
+       /* Try to fetch pumps min/max infos from eeprom */
+
+       memcpy(&tmp, &state0->mpu.processor_part_num, 8);
+       if (tmp[0] != 0xffff && tmp[1] != 0xffff) {
+               pump_min = max(pump_min, tmp[0]);
+               pump_max = min(pump_max, tmp[1]);
+       }
+       if (tmp[2] != 0xffff && tmp[3] != 0xffff) {
+               pump_min = max(pump_min, tmp[2]);
+               pump_max = min(pump_max, tmp[3]);
+       }
+
+       /* Double check the values, this _IS_ needed as the EEPROM on
+        * some dual 2.5Ghz G5s seem, at least, to have both min & max
+        * same to the same value ... (grrrr)
+        */
+       if (pump_min == pump_max || pump_min == 0 || pump_max == 0xffff) {
+               pump_min = CPU_PUMP_OUTPUT_MIN;
+               pump_max = CPU_PUMP_OUTPUT_MAX;
+       }
+
+       state0->pump_min = state1->pump_min = pump_min;
+       state0->pump_max = state1->pump_max = pump_max;
+}
+
 /* 
  * Now, unfortunately, sysfs doesn't give us a nice void * we could
  * pass around to the attribute functions, so we don't really have
@@ -611,6 +717,8 @@ 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(dimms_temperature, dimms_state.last_temp)
+
 static DEVICE_ATTR(cpu0_temperature,S_IRUGO,show_cpu0_temperature,NULL);
 static DEVICE_ATTR(cpu0_voltage,S_IRUGO,show_cpu0_voltage,NULL);
 static DEVICE_ATTR(cpu0_current,S_IRUGO,show_cpu0_current,NULL);
@@ -629,6 +737,8 @@ 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(dimms_temperature,S_IRUGO,show_dimms_temperature,NULL);
+
 /*
  * CPUs fans control loop
  */
@@ -636,17 +746,21 @@ static DEVICE_ATTR(drives_fan_rpm,S_IRUGO,show_drives_fan_rpm,NULL);
 static int do_read_one_cpu_values(struct cpu_pid_state *state, s32 *temp, s32 *power)
 {
        s32 ltemp, volts, amps;
-       int rc = 0;
+       int index, rc = 0;
 
        /* Default (in case of error) */
        *temp = state->cur_temp;
        *power = state->cur_power;
 
-       /* Read current fan status */
-       if (state->index == 0)
-               rc = get_rpm_fan(CPUA_EXHAUST_FAN_RPM_INDEX, !RPM_PID_USE_ACTUAL_SPEED);
+       if (cpu_pid_type == CPU_PID_TYPE_RACKMAC)
+               index = (state->index == 0) ?
+                       CPU_A1_FAN_RPM_INDEX : CPU_B1_FAN_RPM_INDEX;
        else
-               rc = get_rpm_fan(CPUB_EXHAUST_FAN_RPM_INDEX, !RPM_PID_USE_ACTUAL_SPEED);
+               index = (state->index == 0) ?
+                       CPUA_EXHAUST_FAN_RPM_INDEX : CPUB_EXHAUST_FAN_RPM_INDEX;
+
+       /* Read current fan status */
+       rc = get_rpm_fan(index, !RPM_PID_USE_ACTUAL_SPEED);
        if (rc < 0) {
                /* XXX What do we do now ? Nothing for now, keep old value, but
                 * return error upstream
@@ -777,11 +891,6 @@ static void do_cpu_pid(struct cpu_pid_state *state, s32 temp, s32 power)
 
        DBG("   sum: %d\n", (int)sum);
        state->rpm += (s32)sum;
-
-       if (state->rpm < (int)state->mpu.rminn_exhaust_fan)
-               state->rpm = state->mpu.rminn_exhaust_fan;
-       if (state->rpm > (int)state->mpu.rmaxn_exhaust_fan)
-               state->rpm = state->mpu.rmaxn_exhaust_fan;
 }
 
 static void do_monitor_cpu_combined(void)
@@ -823,28 +932,28 @@ static void do_monitor_cpu_combined(void)
        if (state0->overtemp > 0) {
                state0->rpm = state0->mpu.rmaxn_exhaust_fan;
                state0->intake_rpm = intake = state0->mpu.rmaxn_intake_fan;
-               pump = CPU_PUMP_OUTPUT_MAX;
+               pump = state0->pump_min;
                goto do_set_fans;
        }
 
        /* Do the PID */
        do_cpu_pid(state0, temp_combi, power_combi);
 
+       /* Range check */
+       state0->rpm = max(state0->rpm, (int)state0->mpu.rminn_exhaust_fan);
+       state0->rpm = min(state0->rpm, (int)state0->mpu.rmaxn_exhaust_fan);
+
        /* Calculate intake fan speed */
        intake = (state0->rpm * CPU_INTAKE_SCALE) >> 16;
-       if (intake < (int)state0->mpu.rminn_intake_fan)
-               intake = state0->mpu.rminn_intake_fan;
-       if (intake > (int)state0->mpu.rmaxn_intake_fan)
-               intake = state0->mpu.rmaxn_intake_fan;
+       intake = max(intake, (int)state0->mpu.rminn_intake_fan);
+       intake = min(intake, (int)state0->mpu.rmaxn_intake_fan);
        state0->intake_rpm = intake;
 
        /* Calculate pump speed */
-       pump = (state0->rpm * CPU_PUMP_OUTPUT_MAX) /
+       pump = (state0->rpm * state0->pump_max) /
                state0->mpu.rmaxn_exhaust_fan;
-       if (pump > CPU_PUMP_OUTPUT_MAX)
-               pump = CPU_PUMP_OUTPUT_MAX;
-       if (pump < CPU_PUMP_OUTPUT_MIN)
-               pump = CPU_PUMP_OUTPUT_MIN;
+       pump = min(pump, state0->pump_max);
+       pump = max(pump, state0->pump_min);
        
  do_set_fans:
        /* We copy values from state 0 to state 1 for /sysfs */
@@ -904,11 +1013,14 @@ static void do_monitor_cpu_split(struct cpu_pid_state *state)
        /* Do the PID */
        do_cpu_pid(state, temp, power);
 
+       /* Range check */
+       state->rpm = max(state->rpm, (int)state->mpu.rminn_exhaust_fan);
+       state->rpm = min(state->rpm, (int)state->mpu.rmaxn_exhaust_fan);
+
+       /* Calculate intake fan */
        intake = (state->rpm * CPU_INTAKE_SCALE) >> 16;
-       if (intake < (int)state->mpu.rminn_intake_fan)
-               intake = state->mpu.rminn_intake_fan;
-       if (intake > (int)state->mpu.rmaxn_intake_fan)
-               intake = state->mpu.rmaxn_intake_fan;
+       intake = max(intake, (int)state->mpu.rminn_intake_fan);
+       intake = min(intake, (int)state->mpu.rmaxn_intake_fan);
        state->intake_rpm = intake;
 
  do_set_fans:
@@ -929,6 +1041,67 @@ static void do_monitor_cpu_split(struct cpu_pid_state *state)
        }
 }
 
+static void do_monitor_cpu_rack(struct cpu_pid_state *state)
+{
+       s32 temp, power, fan_min;
+       int rc;
+
+       /* Read current fan status */
+       rc = do_read_one_cpu_values(state, &temp, &power);
+       if (rc < 0) {
+               /* XXX What do we do now ? */
+       }
+
+       /* Check tmax, increment overtemp if we are there. At tmax+8, we go
+        * full blown immediately and try to trigger a shutdown
+        */
+       if (temp >= ((state->mpu.tmax + 8) << 16)) {
+               printk(KERN_WARNING "Warning ! CPU %d temperature way above maximum"
+                      " (%d) !\n",
+                      state->index, temp >> 16);
+               state->overtemp = CPU_MAX_OVERTEMP;
+       } else if (temp > (state->mpu.tmax << 16))
+               state->overtemp++;
+       else
+               state->overtemp = 0;
+       if (state->overtemp >= CPU_MAX_OVERTEMP)
+               critical_state = 1;
+       if (state->overtemp > 0) {
+               state->rpm = state->intake_rpm = state->mpu.rmaxn_intake_fan;
+               goto do_set_fans;
+       }
+
+       /* Do the PID */
+       do_cpu_pid(state, temp, power);
+
+       /* Check clamp from dimms */
+       fan_min = dimm_output_clamp;
+       fan_min = max(fan_min, (int)state->mpu.rminn_intake_fan);
+
+       state->rpm = max(state->rpm, (int)fan_min);
+       state->rpm = min(state->rpm, (int)state->mpu.rmaxn_intake_fan);
+       state->intake_rpm = state->rpm;
+
+ do_set_fans:
+       DBG("** CPU %d RPM: %d overtemp: %d\n",
+           state->index, (int)state->rpm, state->overtemp);
+
+       /* We should check for errors, shouldn't we ? But then, what
+        * do we do once the error occurs ? For FCU notified fan
+        * failures (-EFAULT) we probably want to notify userland
+        * some way...
+        */
+       if (state->index == 0) {
+               set_rpm_fan(CPU_A1_FAN_RPM_INDEX, state->rpm);
+               set_rpm_fan(CPU_A2_FAN_RPM_INDEX, state->rpm);
+               set_rpm_fan(CPU_A3_FAN_RPM_INDEX, state->rpm);
+       } else {
+               set_rpm_fan(CPU_B1_FAN_RPM_INDEX, state->rpm);
+               set_rpm_fan(CPU_B2_FAN_RPM_INDEX, state->rpm);
+               set_rpm_fan(CPU_B3_FAN_RPM_INDEX, state->rpm);
+       }
+}
+
 /*
  * Initialize the state structure for one CPU control loop
  */
@@ -936,7 +1109,7 @@ static int init_cpu_state(struct cpu_pid_state *state, int index)
 {
        state->index = index;
        state->first = 1;
-       state->rpm = 1000;
+       state->rpm = (cpu_pid_type == CPU_PID_TYPE_RACKMAC) ? 4000 : 1000;
        state->overtemp = 0;
        state->adc_config = 0x00;
 
@@ -1012,13 +1185,13 @@ static void dispose_cpu_state(struct cpu_pid_state *state)
  */
 static void do_monitor_backside(struct backside_pid_state *state)
 {
-       s32 temp, integral, derivative;
+       s32 temp, integral, derivative, fan_min;
        s64 integ_p, deriv_p, prop_p, sum; 
        int i, rc;
 
        if (--state->ticks != 0)
                return;
-       state->ticks = BACKSIDE_PID_INTERVAL;
+       state->ticks = backside_params.interval;
 
        DBG("backside:\n");
 
@@ -1059,7 +1232,7 @@ static void do_monitor_backside(struct backside_pid_state *state)
        integral = 0;
        for (i = 0; i < BACKSIDE_PID_HISTORY_SIZE; i++)
                integral += state->error_history[i];
-       integral *= BACKSIDE_PID_INTERVAL;
+       integral *= backside_params.interval;
        DBG("  integral: %08x\n", integral);
        integ_p = ((s64)backside_params.G_r) * (s64)integral;
        DBG("   integ_p: %d\n", (int)(integ_p >> 36));
@@ -1069,7 +1242,7 @@ static void do_monitor_backside(struct backside_pid_state *state)
        derivative = state->error_history[state->cur_sample] -
                state->error_history[(state->cur_sample + BACKSIDE_PID_HISTORY_SIZE - 1)
                                    % BACKSIDE_PID_HISTORY_SIZE];
-       derivative /= BACKSIDE_PID_INTERVAL;
+       derivative /= backside_params.interval;
        deriv_p = ((s64)backside_params.G_d) * (s64)derivative;
        DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
        sum += deriv_p;
@@ -1083,11 +1256,17 @@ static void do_monitor_backside(struct backside_pid_state *state)
        sum >>= 36;
 
        DBG("   sum: %d\n", (int)sum);
-       state->pwm += (s32)sum;
-       if (state->pwm < backside_params.output_min)
-               state->pwm = backside_params.output_min;
-       if (state->pwm > backside_params.output_max)
-               state->pwm = backside_params.output_max;
+       if (backside_params.additive)
+               state->pwm += (s32)sum;
+       else
+               state->pwm = sum;
+
+       /* Check for clamp */
+       fan_min = (dimm_output_clamp * 100) / 14000;
+       fan_min = max(fan_min, backside_params.output_min);
+
+       state->pwm = max(state->pwm, fan_min);
+       state->pwm = min(state->pwm, backside_params.output_max);
 
        DBG("** BACKSIDE PWM: %d\n", (int)state->pwm);
        set_pwm_fan(BACKSIDE_FAN_PWM_INDEX, state->pwm);
@@ -1114,17 +1293,33 @@ static int init_backside_state(struct backside_pid_state *state)
                of_node_put(u3);
        }
 
-       backside_params.G_p = BACKSIDE_PID_G_p;
-       backside_params.G_r = BACKSIDE_PID_G_r;
-       backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
-       if (u3h) {
+       if (rackmac) {
+               backside_params.G_d = BACKSIDE_PID_RACK_G_d;
+               backside_params.input_target = BACKSIDE_PID_RACK_INPUT_TARGET;
+               backside_params.output_min = BACKSIDE_PID_U3H_OUTPUT_MIN;
+               backside_params.interval = BACKSIDE_PID_RACK_INTERVAL;
+               backside_params.G_p = BACKSIDE_PID_RACK_G_p;
+               backside_params.G_r = BACKSIDE_PID_G_r;
+               backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
+               backside_params.additive = 0;
+       } else if (u3h) {
                backside_params.G_d = BACKSIDE_PID_U3H_G_d;
                backside_params.input_target = BACKSIDE_PID_U3H_INPUT_TARGET;
                backside_params.output_min = BACKSIDE_PID_U3H_OUTPUT_MIN;
+               backside_params.interval = BACKSIDE_PID_INTERVAL;
+               backside_params.G_p = BACKSIDE_PID_G_p;
+               backside_params.G_r = BACKSIDE_PID_G_r;
+               backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
+               backside_params.additive = 1;
        } else {
                backside_params.G_d = BACKSIDE_PID_U3_G_d;
                backside_params.input_target = BACKSIDE_PID_U3_INPUT_TARGET;
                backside_params.output_min = BACKSIDE_PID_U3_OUTPUT_MIN;
+               backside_params.interval = BACKSIDE_PID_INTERVAL;
+               backside_params.G_p = BACKSIDE_PID_G_p;
+               backside_params.G_r = BACKSIDE_PID_G_r;
+               backside_params.output_max = BACKSIDE_PID_OUTPUT_MAX;
+               backside_params.additive = 1;
        }
 
        state->ticks = 1;
@@ -1233,10 +1428,9 @@ static void do_monitor_drives(struct drives_pid_state *state)
 
        DBG("   sum: %d\n", (int)sum);
        state->rpm += (s32)sum;
-       if (state->rpm < DRIVES_PID_OUTPUT_MIN)
-               state->rpm = DRIVES_PID_OUTPUT_MIN;
-       if (state->rpm > DRIVES_PID_OUTPUT_MAX)
-               state->rpm = DRIVES_PID_OUTPUT_MAX;
+
+       state->rpm = max(state->rpm, DRIVES_PID_OUTPUT_MIN);
+       state->rpm = min(state->rpm, DRIVES_PID_OUTPUT_MAX);
 
        DBG("** DRIVES RPM: %d\n", (int)state->rpm);
        set_rpm_fan(DRIVES_FAN_RPM_INDEX, state->rpm);
@@ -1276,6 +1470,126 @@ static void dispose_drives_state(struct drives_pid_state *state)
        state->monitor = NULL;
 }
 
+/*
+ * DIMMs temp control loop
+ */
+static void do_monitor_dimms(struct dimm_pid_state *state)
+{
+       s32 temp, integral, derivative, fan_min;
+       s64 integ_p, deriv_p, prop_p, sum;
+       int i;
+
+       if (--state->ticks != 0)
+               return;
+       state->ticks = DIMM_PID_INTERVAL;
+
+       DBG("DIMM:\n");
+
+       DBG("  current value: %d\n", state->output);
+
+       temp = read_lm87_reg(state->monitor, LM87_INT_TEMP);
+       if (temp < 0)
+               return;
+       temp <<= 16;
+       state->last_temp = temp;
+       DBG("  temp: %d.%03d, target: %d.%03d\n", FIX32TOPRINT(temp),
+           FIX32TOPRINT(DIMM_PID_INPUT_TARGET));
+
+       /* Store temperature and error in history array */
+       state->cur_sample = (state->cur_sample + 1) % DIMM_PID_HISTORY_SIZE;
+       state->sample_history[state->cur_sample] = temp;
+       state->error_history[state->cur_sample] = temp - DIMM_PID_INPUT_TARGET;
+
+       /* If first loop, fill the history table */
+       if (state->first) {
+               for (i = 0; i < (DIMM_PID_HISTORY_SIZE - 1); i++) {
+                       state->cur_sample = (state->cur_sample + 1) %
+                               DIMM_PID_HISTORY_SIZE;
+                       state->sample_history[state->cur_sample] = temp;
+                       state->error_history[state->cur_sample] =
+                               temp - DIMM_PID_INPUT_TARGET;
+               }
+               state->first = 0;
+       }
+
+       /* Calculate the integral term */
+       sum = 0;
+       integral = 0;
+       for (i = 0; i < DIMM_PID_HISTORY_SIZE; i++)
+               integral += state->error_history[i];
+       integral *= DIMM_PID_INTERVAL;
+       DBG("  integral: %08x\n", integral);
+       integ_p = ((s64)DIMM_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 + DIMM_PID_HISTORY_SIZE - 1)
+                                   % DIMM_PID_HISTORY_SIZE];
+       derivative /= DIMM_PID_INTERVAL;
+       deriv_p = ((s64)DIMM_PID_G_d) * (s64)derivative;
+       DBG("   deriv_p: %d\n", (int)(deriv_p >> 36));
+       sum += deriv_p;
+
+       /* Calculate the proportional term */
+       prop_p = ((s64)DIMM_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->output = (s32)sum;
+       state->output = max(state->output, DIMM_PID_OUTPUT_MIN);
+       state->output = min(state->output, DIMM_PID_OUTPUT_MAX);
+       dimm_output_clamp = state->output;
+
+       DBG("** DIMM clamp value: %d\n", (int)state->output);
+
+       /* Backside PID is only every 5 seconds, force backside fan clamping now */
+       fan_min = (dimm_output_clamp * 100) / 14000;
+       fan_min = max(fan_min, backside_params.output_min);
+       if (backside_state.pwm < fan_min) {
+               backside_state.pwm = fan_min;
+               DBG(" -> applying clamp to backside fan now: %d  !\n", fan_min);
+               set_pwm_fan(BACKSIDE_FAN_PWM_INDEX, fan_min);
+       }
+}
+
+/*
+ * Initialize the state structure for the DIMM temp control loop
+ */
+static int init_dimms_state(struct dimm_pid_state *state)
+{
+       state->ticks = 1;
+       state->first = 1;
+       state->output = 4000;
+
+       state->monitor = attach_i2c_chip(XSERVE_DIMMS_LM87, "dimms_temp");
+       if (state->monitor == NULL)
+               return -ENODEV;
+
+               device_create_file(&of_dev->dev, &dev_attr_dimms_temperature);
+
+       return 0;
+}
+
+/*
+ * Dispose of the state data for the drives control loop
+ */
+static void dispose_dimms_state(struct dimm_pid_state *state)
+{
+       if (state->monitor == NULL)
+               return;
+
+       device_remove_file(&of_dev->dev, &dev_attr_dimms_temperature);
+
+       detach_i2c_chip(state->monitor);
+       state->monitor = NULL;
+}
+
 static int call_critical_overtemp(void)
 {
        char *argv[] = { critical_overtemp_path, NULL };
@@ -1321,15 +1635,29 @@ static int main_control_loop(void *x)
                start = jiffies;
 
                down(&driver_lock);
+
+               /* First, we always calculate the new DIMMs state on an Xserve */
+               if (rackmac)
+                       do_monitor_dimms(&dimms_state);
+
+               /* Then, the CPUs */
                if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
                        do_monitor_cpu_combined();
-               else {
+               else if (cpu_pid_type == CPU_PID_TYPE_RACKMAC) {
+                       do_monitor_cpu_rack(&cpu_state[0]);
+                       if (cpu_state[1].monitor != NULL)
+                               do_monitor_cpu_rack(&cpu_state[1]);
+                       // better deal with UP
+               } else {
                        do_monitor_cpu_split(&cpu_state[0]);
                        if (cpu_state[1].monitor != NULL)
                                do_monitor_cpu_split(&cpu_state[1]);
+                       // better deal with UP
                }
+               /* Then, the rest */
                do_monitor_backside(&backside_state);
-               do_monitor_drives(&drives_state);
+               if (!rackmac)
+                       do_monitor_drives(&drives_state);
                up(&driver_lock);
 
                if (critical_state == 1) {
@@ -1369,9 +1697,9 @@ static void dispose_control_loops(void)
 {
        dispose_cpu_state(&cpu_state[0]);
        dispose_cpu_state(&cpu_state[1]);
-
        dispose_backside_state(&backside_state);
        dispose_drives_state(&drives_state);
+       dispose_dimms_state(&dimms_state);
 }
 
 /*
@@ -1395,7 +1723,9 @@ static int create_control_loops(void)
         * the pumps, though that may not be the best way, that is good enough
         * for now
         */
-       if (machine_is_compatible("PowerMac7,3")
+       if (rackmac)
+               cpu_pid_type = CPU_PID_TYPE_RACKMAC;
+       else if (machine_is_compatible("PowerMac7,3")
            && (cpu_count > 1)
            && fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID
            && fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) {
@@ -1409,11 +1739,16 @@ static int create_control_loops(void)
         */
        if (init_cpu_state(&cpu_state[0], 0))
                goto fail;
+       if (cpu_pid_type == CPU_PID_TYPE_COMBINED)
+               fetch_cpu_pumps_minmax();
+
        if (cpu_count > 1 && init_cpu_state(&cpu_state[1], 1))
                goto fail;
        if (init_backside_state(&backside_state))
                goto fail;
-       if (init_drives_state(&drives_state))
+       if (rackmac && init_dimms_state(&dimms_state))
+               goto fail;
+       if (!rackmac && init_drives_state(&drives_state))
                goto fail;
 
        DBG("all control loops up !\n");
@@ -1492,17 +1827,24 @@ static int therm_pm72_attach(struct i2c_adapter *adapter)
        /* Check if we are looking for one of these */
        if (u3_0 == NULL && !strcmp(adapter->name, "u3 0")) {
                u3_0 = adapter;
-               DBG("found U3-0, creating control loops\n");
-               if (create_control_loops())
-                       u3_0 = NULL;
+               DBG("found U3-0\n");
+               if (k2 || !rackmac)
+                       if (create_control_loops())
+                               u3_0 = NULL;
        } else if (u3_1 == NULL && !strcmp(adapter->name, "u3 1")) {
                u3_1 = adapter;
                DBG("found U3-1, attaching FCU\n");
                if (attach_fcu())
                        u3_1 = NULL;
+       } else if (k2 == NULL && !strcmp(adapter->name, "mac-io 0")) {
+               k2 = adapter;
+               DBG("Found K2\n");
+               if (u3_0 && rackmac)
+                       if (create_control_loops())
+                               k2 = NULL;
        }
        /* We got all we need, start control loops */
-       if (u3_0 != NULL && u3_1 != NULL) {
+       if (u3_0 != NULL && u3_1 != NULL && (k2 || !rackmac)) {
                DBG("everything up, starting control loops\n");
                state = state_attached;
                start_control_loops();
@@ -1548,6 +1890,27 @@ static int therm_pm72_detach(struct i2c_adapter *adapter)
        return 0;
 }
 
+static int fan_check_loc_match(const char *loc, int fan)
+{
+       char    tmp[64];
+       char    *c, *e;
+
+       strlcpy(tmp, fcu_fans[fan].loc, 64);
+
+       c = tmp;
+       for (;;) {
+               e = strchr(c, ',');
+               if (e)
+                       *e = 0;
+               if (strcmp(loc, c) == 0)
+                       return 1;
+               if (e == NULL)
+                       break;
+               c = e + 1;
+       }
+       return 0;
+}
+
 static void fcu_lookup_fans(struct device_node *fcu_node)
 {
        struct device_node *np = NULL;
@@ -1589,7 +1952,7 @@ static void fcu_lookup_fans(struct device_node *fcu_node)
                for (i = 0; i < FCU_FAN_COUNT; i++) {
                        int fan_id;
 
-                       if (strcmp(loc, fcu_fans[i].loc))
+                       if (!fan_check_loc_match(loc, i))
                                continue;
                        DBG(" location match, index: %d\n", i);
                        fcu_fans[i].id = FCU_FAN_ABSENT_ID;
@@ -1671,8 +2034,11 @@ static int __init therm_pm72_init(void)
 {
        struct device_node *np;
 
+       rackmac = machine_is_compatible("RackMac3,1");
+
        if (!machine_is_compatible("PowerMac7,2") &&
-           !machine_is_compatible("PowerMac7,3"))
+           !machine_is_compatible("PowerMac7,3") &&
+           !rackmac)
                return -ENODEV;
 
        printk(KERN_INFO "PowerMac G5 Thermal control driver %s\n", VERSION);
@@ -1709,6 +2075,6 @@ module_init(therm_pm72_init);
 module_exit(therm_pm72_exit);
 
 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
-MODULE_DESCRIPTION("Driver for Apple's PowerMac7,2 G5 thermal control");
+MODULE_DESCRIPTION("Driver for Apple's PowerMac G5 thermal control");
 MODULE_LICENSE("GPL");