* - 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/smp_lock.h>
#include <linux/wait.h>
#include <linux/reboot.h>
#include <linux/kmod.h>
#include <linux/i2c.h>
-#include <linux/i2c-dev.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/of_device.h>
+#include <linux/of_platform.h>
#include <asm/prom.h>
#include <asm/machdep.h>
#include <asm/io.h>
#include <asm/system.h>
#include <asm/sections.h>
-#include <asm/of_device.h>
#include <asm/macio.h>
#include "therm_pm72.h"
-#define VERSION "1.2b2"
+#define VERSION "1.3"
#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 pid_t ctrl_task;
+static struct task_struct *ctrl_task;
static struct completion ctrl_complete;
static int critical_state;
static int rackmac;
static s32 dimm_output_clamp;
-
-static DECLARE_MUTEX(driver_lock);
+static int fcu_rpm_shift;
+static int fcu_tickle_ticks;
+static DEFINE_MUTEX(driver_lock);
/*
* We have 3 types of CPU PID control. One is "split" old style control
},
};
-/*
- * i2c_driver structure to attach to the host i2c controller
- */
-
-static int therm_pm72_attach(struct i2c_adapter *adapter);
-static int therm_pm72_detach(struct i2c_adapter *adapter);
-
-static struct i2c_driver therm_pm72_driver =
-{
- .owner = THIS_MODULE,
- .name = "therm_pm72",
- .flags = I2C_DF_NOTIFY,
- .attach_adapter = therm_pm72_attach,
- .detach_adapter = therm_pm72_detach,
-};
+static struct i2c_driver therm_pm72_driver;
/*
* Utility function to create an i2c_client structure and
{
struct i2c_client *clt;
struct i2c_adapter *adap;
+ struct i2c_board_info info;
if (id & 0x200)
adap = k2;
if (adap == NULL)
return NULL;
- clt = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
- if (clt == NULL)
- return NULL;
- memset(clt, 0, sizeof(struct i2c_client));
-
- clt->addr = (id >> 1) & 0x7f;
- clt->adapter = adap;
- clt->driver = &therm_pm72_driver;
- strncpy(clt->name, name, I2C_NAME_SIZE-1);
-
- if (i2c_attach_client(clt)) {
+ memset(&info, 0, sizeof(struct i2c_board_info));
+ info.addr = (id >> 1) & 0x7f;
+ strlcpy(info.type, "therm_pm72", I2C_NAME_SIZE);
+ clt = i2c_new_device(adap, &info);
+ if (!clt) {
printk(KERN_ERR "therm_pm72: Failed to attach to i2c ID 0x%x\n", id);
- kfree(clt);
return NULL;
}
- return clt;
-}
-/*
- * Utility function to get rid of the i2c_client structure
- * (will also detach from the adapter hopepfully)
- */
-static void detach_i2c_chip(struct i2c_client *clt)
-{
- i2c_detach_client(clt);
- kfree(clt);
+ /*
+ * Let i2c-core delete that device on driver removal.
+ * This is safe because i2c-core holds the core_lock mutex for us.
+ */
+ list_add_tail(&clt->detected, &therm_pm72_driver.clients);
+ return clt;
}
/*
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;
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;
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)
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
{
struct device_node *np;
char nodename[64];
- u8 *data;
+ const u8 *data;
int len;
/* prom.c routine for finding a node by path is a bit brain dead
sprintf(nodename, "/u3@0,f8000000/i2c@f8001000/cpuid@a%d", cpu ? 2 : 0);
np = of_find_node_by_path(nodename);
if (np == NULL) {
- printk(KERN_ERR "therm_pm72: Failed to retreive cpuid node from device-tree\n");
+ printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid node from device-tree\n");
return -ENODEV;
}
- data = (u8 *)get_property(np, "cpuid", &len);
+ data = of_get_property(np, "cpuid", &len);
if (data == NULL) {
- printk(KERN_ERR "therm_pm72: Failed to retreive cpuid property from device-tree\n");
+ printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid property from device-tree\n");
of_node_put(np);
return -ENODEV;
}
static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \
{ \
ssize_t r; \
- down(&driver_lock); \
+ mutex_lock(&driver_lock); \
r = sprintf(buf, "%d.%03d", FIX32TOPRINT(data)); \
- up(&driver_lock); \
+ mutex_unlock(&driver_lock); \
return r; \
}
#define BUILD_SHOW_FUNC_INT(name, data) \
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);
/*
if (temp_combi >= ((state0->mpu.tmax + 8) << 16)) {
printk(KERN_WARNING "Warning ! Temperature way above maximum (%d) !\n",
temp_combi >> 16);
- state0->overtemp = CPU_MAX_OVERTEMP;
- } else if (temp_combi > (state0->mpu.tmax << 16))
+ state0->overtemp += CPU_MAX_OVERTEMP / 4;
+ } else if (temp_combi > (state0->mpu.tmax << 16)) {
state0->overtemp++;
- else
+ printk(KERN_WARNING "Temperature %d above max %d. overtemp %d\n",
+ temp_combi >> 16, state0->mpu.tmax, state0->overtemp);
+ } else {
+ if (state0->overtemp)
+ printk(KERN_WARNING "Temperature back down to %d\n",
+ temp_combi >> 16);
state0->overtemp = 0;
+ }
if (state0->overtemp >= CPU_MAX_OVERTEMP)
critical_state = 1;
if (state0->overtemp > 0) {
state0->rpm = state0->mpu.rmaxn_exhaust_fan;
state0->intake_rpm = intake = state0->mpu.rmaxn_intake_fan;
- pump = state0->pump_min;
+ pump = state0->pump_max;
goto do_set_fans;
}
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 += CPU_MAX_OVERTEMP / 4;
+ } else if (temp > (state->mpu.tmax << 16)) {
state->overtemp++;
- else
+ printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
+ state->index, temp >> 16, state->mpu.tmax, state->overtemp);
+ } else {
+ if (state->overtemp)
+ printk(KERN_WARNING "CPU %d temperature back down to %d\n",
+ state->index, temp >> 16);
state->overtemp = 0;
+ }
if (state->overtemp >= CPU_MAX_OVERTEMP)
critical_state = 1;
if (state->overtemp > 0) {
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 = CPU_MAX_OVERTEMP / 4;
+ } else if (temp > (state->mpu.tmax << 16)) {
state->overtemp++;
- else
+ printk(KERN_WARNING "CPU %d temperature %d above max %d. overtemp %d\n",
+ state->index, temp >> 16, state->mpu.tmax, state->overtemp);
+ } else {
+ if (state->overtemp)
+ printk(KERN_WARNING "CPU %d temperature back down to %d\n",
+ state->index, temp >> 16);
state->overtemp = 0;
+ }
if (state->overtemp >= CPU_MAX_OVERTEMP)
critical_state = 1;
if (state->overtemp > 0) {
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;
*/
static int init_cpu_state(struct cpu_pid_state *state, int index)
{
+ int err;
+
state->index = index;
state->first = 1;
state->rpm = (cpu_pid_type == CPU_PID_TYPE_RACKMAC) ? 4000 : 1000;
DBG("CPU %d Using %d power history entries\n", index, state->count_power);
if (index == 0) {
- device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_current);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
- device_create_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
+ err = device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_current);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_exhaust_fan_rpm);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu0_intake_fan_rpm);
} else {
- device_create_file(&of_dev->dev, &dev_attr_cpu1_temperature);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_voltage);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_current);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
- device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
+ err = device_create_file(&of_dev->dev, &dev_attr_cpu1_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_voltage);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_current);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_exhaust_fan_rpm);
+ err |= device_create_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
}
+ if (err)
+ printk(KERN_WARNING "Failed to create some of the atribute"
+ "files for CPU %d\n", index);
return 0;
fail:
- if (state->monitor)
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
return -ENODEV;
device_remove_file(&of_dev->dev, &dev_attr_cpu1_intake_fan_rpm);
}
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
{
struct device_node *u3;
int u3h = 1; /* conservative by default */
+ int err;
/*
* There are different PID params for machines with U3 and machines
*/
u3 = of_find_node_by_path("/u3@0,f8000000");
if (u3 != NULL) {
- u32 *vers = (u32 *)get_property(u3, "device-rev", NULL);
+ const u32 *vers = of_get_property(u3, "device-rev", NULL);
if (vers)
if (((*vers) & 0x3f) < 0x34)
u3h = 0;
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_backside_temperature);
- device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
+ err = device_create_file(&of_dev->dev, &dev_attr_backside_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
+ if (err)
+ printk(KERN_WARNING "Failed to create attribute file(s)"
+ " for backside fan\n");
return 0;
}
device_remove_file(&of_dev->dev, &dev_attr_backside_temperature);
device_remove_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
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));
*/
static int init_drives_state(struct drives_pid_state *state)
{
+ int err;
+
state->ticks = 1;
state->first = 1;
state->rpm = 1000;
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_drives_temperature);
- device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
+ err = device_create_file(&of_dev->dev, &dev_attr_drives_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
+ if (err)
+ printk(KERN_WARNING "Failed to create attribute file(s)"
+ " for drives bay fan\n");
return 0;
}
device_remove_file(&of_dev->dev, &dev_attr_drives_temperature);
device_remove_file(&of_dev->dev, &dev_attr_drives_fan_rpm);
- detach_i2c_chip(state->monitor);
state->monitor = NULL;
}
if (state->monitor == NULL)
return -ENODEV;
- device_create_file(&of_dev->dev, &dev_attr_dimms_temperature);
+ if (device_create_file(&of_dev->dev, &dev_attr_dimms_temperature))
+ printk(KERN_WARNING "Failed to create attribute file"
+ " for DIMM temperature\n");
return 0;
}
/*
- * 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)
{
device_remove_file(&of_dev->dev, &dev_attr_dimms_temperature);
- detach_i2c_chip(state->monitor);
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)
+{
+ int err;
+
+ 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;
+
+ err = device_create_file(&of_dev->dev, &dev_attr_slots_temperature);
+ err |= device_create_file(&of_dev->dev, &dev_attr_slots_fan_pwm);
+ if (err)
+ printk(KERN_WARNING "Failed to create attribute file(s)"
+ " for slots bay fan\n");
+
+ 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);
+
+ state->monitor = NULL;
+}
+
+
static int call_critical_overtemp(void)
{
char *argv[] = { critical_overtemp_path, NULL };
"PATH=/sbin:/usr/sbin:/bin:/usr/bin",
NULL };
- return call_usermodehelper(critical_overtemp_path, argv, envp, 0);
+ return call_usermodehelper(critical_overtemp_path,
+ argv, envp, UMH_WAIT_EXEC);
}
*/
static int main_control_loop(void *x)
{
- daemonize("kfand");
-
DBG("main_control_loop started\n");
- down(&driver_lock);
+ mutex_lock(&driver_lock);
if (start_fcu() < 0) {
printk(KERN_ERR "kfand: failed to start FCU\n");
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
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]);
- up(&driver_lock);
+ fcu_tickle_ticks = FCU_TICKLE_TICKS;
+
+ mutex_unlock(&driver_lock);
while (state == state_attached) {
unsigned long elapsed, start;
start = jiffies;
- down(&driver_lock);
+ mutex_lock(&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)
}
/* 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);
+ mutex_unlock(&driver_lock);
if (critical_state == 1) {
printk(KERN_WARNING "Temperature control detected a critical condition\n");
}
// FIXME: Deal with signals
- set_current_state(TASK_INTERRUPTIBLE);
elapsed = jiffies - start;
if (elapsed < HZ)
- schedule_timeout(HZ - elapsed);
+ schedule_timeout_interruptible(HZ - elapsed);
}
out:
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);
}
*/
if (rackmac)
cpu_pid_type = CPU_PID_TYPE_RACKMAC;
- else if (machine_is_compatible("PowerMac7,3")
+ else if (of_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) {
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;
{
init_completion(&ctrl_complete);
- ctrl_task = kernel_thread(main_control_loop, NULL, SIGCHLD | CLONE_KERNEL);
+ ctrl_task = kthread_run(main_control_loop, NULL, "kfand");
}
/*
*/
static void stop_control_loops(void)
{
- if (ctrl_task != 0)
+ if (ctrl_task)
wait_for_completion(&ctrl_complete);
}
*/
static void detach_fcu(void)
{
- if (fcu)
- detach_i2c_chip(fcu);
fcu = NULL;
}
*/
static int therm_pm72_attach(struct i2c_adapter *adapter)
{
- down(&driver_lock);
+ mutex_lock(&driver_lock);
/* Check state */
if (state == state_detached)
state = state_attaching;
if (state != state_attaching) {
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
return 0;
}
state = state_attached;
start_control_loops();
}
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
return 0;
}
+static int therm_pm72_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ /* Always succeed, the real work was done in therm_pm72_attach() */
+ return 0;
+}
+
/*
- * Called on every adapter when the driver or the i2c controller
+ * Called when any of the devices which participates into thermal management
* is going away.
*/
-static int therm_pm72_detach(struct i2c_adapter *adapter)
+static int therm_pm72_remove(struct i2c_client *client)
{
- down(&driver_lock);
+ struct i2c_adapter *adapter = client->adapter;
+
+ mutex_lock(&driver_lock);
if (state != state_detached)
state = state_detaching;
/* Stop control loops if any */
DBG("stopping control loops\n");
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
stop_control_loops();
- down(&driver_lock);
+ mutex_lock(&driver_lock);
if (u3_0 != NULL && !strcmp(adapter->name, "u3 0")) {
DBG("lost U3-0, disposing control loops\n");
if (u3_0 == NULL && u3_1 == NULL)
state = state_detached;
- up(&driver_lock);
+ mutex_unlock(&driver_lock);
return 0;
}
+/*
+ * i2c_driver structure to attach to the host i2c controller
+ */
+
+static const struct i2c_device_id therm_pm72_id[] = {
+ /*
+ * Fake device name, thermal management is done by several
+ * chips but we don't need to differentiate between them at
+ * this point.
+ */
+ { "therm_pm72", 0 },
+ { }
+};
+
+static struct i2c_driver therm_pm72_driver = {
+ .driver = {
+ .name = "therm_pm72",
+ },
+ .attach_adapter = therm_pm72_attach,
+ .probe = therm_pm72_probe,
+ .remove = therm_pm72_remove,
+ .id_table = therm_pm72_id,
+};
+
static int fan_check_loc_match(const char *loc, int fan)
{
char tmp[64];
while ((np = of_get_next_child(fcu_node, np)) != NULL) {
int type = -1;
- char *loc;
- u32 *reg;
+ const char *loc;
+ const u32 *reg;
DBG(" control: %s, type: %s\n", np->name, np->type);
continue;
/* Lookup for a matching location */
- loc = (char *)get_property(np, "location", NULL);
- reg = (u32 *)get_property(np, "reg", NULL);
+ loc = of_get_property(np, "location", NULL);
+ reg = of_get_property(np, "reg", NULL);
if (loc == NULL || reg == NULL)
continue;
DBG(" matching location: %s, reg: 0x%08x\n", loc, *reg);
static int fcu_of_probe(struct of_device* dev, const struct of_device_id *match)
{
- int rc;
-
state = state_detached;
/* Lookup the fans in the device tree */
fcu_lookup_fans(dev->node);
/* Add the driver */
- rc = i2c_add_driver(&therm_pm72_driver);
- if (rc < 0)
- return rc;
- return 0;
+ return i2c_add_driver(&therm_pm72_driver);
}
static int fcu_of_remove(struct of_device* dev)
return 0;
}
-static struct of_device_id fcu_match[] =
+static const struct of_device_id fcu_match[] =
{
{
.type = "fcu",
{
struct device_node *np;
- rackmac = machine_is_compatible("RackMac3,1");
+ rackmac = of_machine_is_compatible("RackMac3,1");
- if (!machine_is_compatible("PowerMac7,2") &&
- !machine_is_compatible("PowerMac7,3") &&
+ if (!of_machine_is_compatible("PowerMac7,2") &&
+ !of_machine_is_compatible("PowerMac7,3") &&
!rackmac)
return -ENODEV;
return -ENODEV;
}
- of_register_driver(&fcu_of_platform_driver);
+ of_register_platform_driver(&fcu_of_platform_driver);
return 0;
}
static void __exit therm_pm72_exit(void)
{
- of_unregister_driver(&fcu_of_platform_driver);
+ of_unregister_platform_driver(&fcu_of_platform_driver);
if (of_dev)
of_device_unregister(of_dev);