All of lore.kernel.org
 help / color / mirror / Atom feed
From: Benjamin Herrenschmidt <benh@kernel.crashing.org>
To: Richard Henderson <rth@twiddle.net>
Cc: linuxppc-dev list <linuxppc-dev@ozlabs.org>
Subject: Re: therm_pm72 changes for xserve
Date: Fri, 11 Feb 2005 09:31:36 +1100	[thread overview]
Message-ID: <1108074696.7686.210.camel@gaston> (raw)
In-Reply-To: <20050210191451.GA6804@twiddle.net>

On Thu, 2005-02-10 at 11:14 -0800, Richard Henderson wrote:
> The machine appears to be stable under load with this patch.
> Which isn't *terribly* surprising since fan speed under darwin
> seems to be universally lower.  (If there's a way to probe the
> actual values under darwin, I don't know it.  This just from
> standing next to the machine and listening.  I suppose this
> could be from failing to lower the rpms of the third cpu fans.)
> 
> I am kinda surprised about the data we pull out the eeprom.
> It seems to believe that 75C is a good target temperature.
> Idle temp appears to be 42C; load temp hovers around 60C.
> The high target temp means that we don't ever increase the
> fan speed from the adversised minimum.
> 
> I got the actual fan ids from a patch that Harald Welte wrote
> once upon a time, someplace googleable.  I'll admit that I have
> no idea how one gets from
> 
>   od /proc/device-tree/sep/fans/cpu-a-1/control-id
>   0000000 000000 040400
> 
> or any other entry in any of that directory, to .id=3.
> 
> Anyway, if this isn't clean enough, give me some direction.

Hi ! You should have asked me :) I did some work on the Xserve. Notably,
it uses a slightly different algorithm.... Here's my current patch.

Index: linux-work/drivers/macintosh/therm_pm72.c
===================================================================
--- linux-work.orig/drivers/macintosh/therm_pm72.c	2005-01-18 18:15:25.000000000 +1100
+++ linux-work/drivers/macintosh/therm_pm72.c	2005-01-18 18:15:33.000000000 +1100
@@ -47,8 +47,11 @@
  *          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:
  *
@@ -85,6 +88,12 @@
  *	- 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
+ *
+ *  Nov. 16, 2004 : 1.2b2
+ *	- 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>
@@ -113,7 +122,7 @@
 
 #include "therm_pm72.h"
 
-#define VERSION "1.1"
+#define VERSION "1.2b2"
 
 #undef DEBUG
 
@@ -131,21 +140,26 @@
 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 +169,7 @@
  */
 #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 +192,7 @@
 
 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 +202,7 @@
 		.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 +239,37 @@
 		.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 +297,9 @@
 	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 +409,31 @@
 	}
 }
 
+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 +643,38 @@
 	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 +716,8 @@
 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 +736,8 @@
 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 +745,21 @@
 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 +890,6 @@
 
 	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 +931,28 @@
 	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 +1012,14 @@
 	/* 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 +1040,67 @@
 	}
 }
 
+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 +1108,7 @@
 {
 	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 +1184,13 @@
  */
 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 +1231,7 @@
 	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 +1241,7 @@
 	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 +1255,17 @@
 	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 +1292,33 @@
 		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 +1427,9 @@
 
 	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 +1469,126 @@
 	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 +1634,29 @@
 		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 +1696,9 @@
 {
 	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 +1722,9 @@
 	 * 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 +1738,16 @@
 	 */
 	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 +1826,24 @@
 	/* 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 +1889,27 @@
 	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 +1951,7 @@
 		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 +2033,11 @@
 {
 	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 +2074,6 @@
 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");
 
Index: linux-work/drivers/macintosh/therm_pm72.h
===================================================================
--- linux-work.orig/drivers/macintosh/therm_pm72.h	2005-01-18 18:15:25.000000000 +1100
+++ linux-work/drivers/macintosh/therm_pm72.h	2005-01-18 18:15:33.000000000 +1100
@@ -52,7 +52,7 @@
 	u16	rmaxn_intake_fan;	/* 0x4e - Intake fan max RPM */
 	u16	rminn_exhaust_fan;	/* 0x50 - Exhaust fan min RPM */
 	u16	rmaxn_exhaust_fan;	/* 0x52 - Exhaust fan max RPM */
-	u8	processor_part_num[8];	/* 0x54 - Processor part number */
+	u8	processor_part_num[8];	/* 0x54 - Processor part number XX pumps min/max */
 	u32	processor_lot_num;	/* 0x5c - Processor lot number */
 	u8	orig_card_sernum[0x10];	/* 0x60 - Card original serial number */
 	u8	curr_card_sernum[0x10];	/* 0x70 - Card current serial number */
@@ -94,19 +94,25 @@
  * of the driver, though I would accept any clean patch
  * doing a better use of the device-tree without turning the
  * while i2c registration mecanism into a racy mess
+ *
+ * Note: Xserve changed this. We have some bits on the K2 bus,
+ * which I arbitrarily set to 0x200. Ultimately, we really want
+ * too lookup these in the device-tree though
  */
 #define FAN_CTRLER_ID		0x15e
 #define SUPPLY_MONITOR_ID      	0x58
 #define SUPPLY_MONITORB_ID     	0x5a
 #define DRIVES_DALLAS_ID	0x94
 #define BACKSIDE_MAX_ID		0x98
+#define XSERVE_DIMMS_LM87	0x25a
 
 /*
- * Some MAX6690 & DS1775 register definitions
+ * Some MAX6690, DS1775, LM87 register definitions
  */
 #define MAX6690_INT_TEMP	0
 #define MAX6690_EXT_TEMP	1
 #define DS1775_TEMP		0
+#define LM87_INT_TEMP		0x27
 
 /*
  * Scaling factors for the AD7417 ADC converters (except
@@ -126,14 +132,18 @@
 #define BACKSIDE_FAN_PWM_INDEX		0
 #define BACKSIDE_PID_U3_G_d		0x02800000
 #define BACKSIDE_PID_U3H_G_d		0x01400000
+#define BACKSIDE_PID_RACK_G_d		0x00500000
 #define BACKSIDE_PID_G_p		0x00500000
+#define BACKSIDE_PID_RACK_G_p		0x0004cccc
 #define BACKSIDE_PID_G_r		0x00000000
 #define BACKSIDE_PID_U3_INPUT_TARGET	0x00410000
 #define BACKSIDE_PID_U3H_INPUT_TARGET	0x004b0000
+#define BACKSIDE_PID_RACK_INPUT_TARGET	0x00460000
 #define BACKSIDE_PID_INTERVAL		5
+#define BACKSIDE_PID_RACK_INTERVAL	1
 #define BACKSIDE_PID_OUTPUT_MAX		100
 #define BACKSIDE_PID_U3_OUTPUT_MIN	20
-#define BACKSIDE_PID_U3H_OUTPUT_MIN	30
+#define BACKSIDE_PID_U3H_OUTPUT_MIN	20
 #define BACKSIDE_PID_HISTORY_SIZE	2
 
 struct basckside_pid_params
@@ -144,6 +154,8 @@
 	s32			input_target;
 	s32			output_min;
 	s32			output_max;
+	s32			interval;
+	int			additive;
 };
 
 struct backside_pid_state
@@ -188,25 +200,34 @@
 #define SLOTS_FAN_PWM_INDEX		2
 #define	SLOTS_FAN_DEFAULT_PWM		50 /* Do better here ! */
 
+
 /*
- * IDs in Darwin for the sensors & fans
- *
- * CPU A AD7417_TEMP	10	(CPU A ambient temperature)
- * CPU A AD7417_AD1	11	(CPU A diode temperature)
- * CPU A AD7417_AD2	12	(CPU A 12V current)
- * CPU A AD7417_AD3	13	(CPU A voltage)
- * CPU A AD7417_AD4	14	(CPU A current)
- *
- * CPU A FAKE POWER	48	(I_V_inputs: 13, 14)
- *
- * CPU B AD7417_TEMP	15	(CPU B ambient temperature)
- * CPU B AD7417_AD1	16	(CPU B diode temperature)
- * CPU B AD7417_AD2	17	(CPU B 12V current)
- * CPU B AD7417_AD3	18	(CPU B voltage)
- * CPU B AD7417_AD4	19	(CPU B current)
- *
- * CPU B FAKE POWER	49	(I_V_inputs: 18, 19)
+ * PID factors for the Xserve DIMM control loop
  */
+#define DIMM_PID_G_d			0
+#define DIMM_PID_G_p			0
+#define DIMM_PID_G_r			0x6553600
+#define DIMM_PID_INPUT_TARGET		3276800
+#define DIMM_PID_INTERVAL    		1
+#define DIMM_PID_OUTPUT_MAX		14000
+#define DIMM_PID_OUTPUT_MIN		4000
+#define DIMM_PID_HISTORY_SIZE		20
+
+struct dimm_pid_state
+{
+	int			ticks;
+	struct i2c_client *	monitor;
+	s32	       		sample_history[DIMM_PID_HISTORY_SIZE];
+	s32			error_history[DIMM_PID_HISTORY_SIZE];
+	int			cur_sample;
+	s32			last_temp;
+	int			first;
+	int			output;
+};
+
+
+
+/* Desktops */
 
 #define CPUA_INTAKE_FAN_RPM_DEFAULT_ID	3
 #define CPUA_EXHAUST_FAN_RPM_DEFAULT_ID	4
@@ -226,8 +247,17 @@
 
 #define CPUA_PUMP_RPM_INDEX		7
 #define CPUB_PUMP_RPM_INDEX		8
-#define CPU_PUMP_OUTPUT_MAX		3700
-#define CPU_PUMP_OUTPUT_MIN		1000
+#define CPU_PUMP_OUTPUT_MAX		3200
+#define CPU_PUMP_OUTPUT_MIN		1250
+
+/* Xserve */
+#define CPU_A1_FAN_RPM_INDEX		9
+#define CPU_A2_FAN_RPM_INDEX		10
+#define CPU_A3_FAN_RPM_INDEX		11
+#define CPU_B1_FAN_RPM_INDEX		12
+#define CPU_B2_FAN_RPM_INDEX		13
+#define CPU_B3_FAN_RPM_INDEX		14
+
 
 struct cpu_pid_state
 {
@@ -249,6 +279,8 @@
 	s32			last_power;
 	int			first;
 	u8			adc_config;
+	s32			pump_min;
+	s32			pump_max;
 };
 
 /*

      reply	other threads:[~2005-02-10 22:32 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2005-02-10 19:14 therm_pm72 changes for xserve Richard Henderson
2005-02-10 22:31 ` Benjamin Herrenschmidt [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1108074696.7686.210.camel@gaston \
    --to=benh@kernel.crashing.org \
    --cc=linuxppc-dev@ozlabs.org \
    --cc=rth@twiddle.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.