diff options
Diffstat (limited to 'drivers/macintosh/therm_pm72.c')
-rw-r--r-- | drivers/macintosh/therm_pm72.c | 2278 |
1 files changed, 0 insertions, 2278 deletions
diff --git a/drivers/macintosh/therm_pm72.c b/drivers/macintosh/therm_pm72.c deleted file mode 100644 index 7ed92582d2cf..000000000000 --- a/drivers/macintosh/therm_pm72.c +++ /dev/null @@ -1,2278 +0,0 @@ -/* - * Device driver for the thermostats & fan controller of the - * Apple G5 "PowerMac7,2" desktop machines. - * - * (c) Copyright IBM Corp. 2003-2004 - * - * Maintained by: Benjamin Herrenschmidt - * <benh@kernel.crashing.org> - * - * - * The algorithm used is the PID control algorithm, used the same - * way the published Darwin code does, using the same values that - * are present in the Darwin 7.0 snapshot property lists. - * - * As far as the CPUs control loops are concerned, I use the - * calibration & PID constants provided by the EEPROM, - * I do _not_ embed any value from the property lists, as the ones - * provided by Darwin 7.0 seem to always have an older version that - * what I've seen on the actual computers. - * It would be interesting to verify that though. Darwin has a - * version code of 1.0.0d11 for all control loops it seems, while - * so far, the machines EEPROMs contain a dataset versioned 1.0.0f - * - * Darwin doesn't provide source to all parts, some missing - * bits like the AppleFCU driver or the actual scale of some - * of the values returned by sensors had to be "guessed" some - * way... or based on what Open Firmware does. - * - * I didn't yet figure out how to get the slots power consumption - * out of the FCU, so that part has not been implemented yet and - * the slots fan is set to a fixed 50% PWM, hoping this value is - * safe enough ... - * - * Note: I have observed strange oscillations of the CPU control - * loop on a dual G5 here. When idle, the CPU exhaust fan tend to - * oscillates slowly (over several minutes) between the minimum - * of 300RPMs and approx. 1000 RPMs. I don't know what is causing - * this, it could be some incorrect constant or an error in the - * way I ported the algorithm, or it could be just normal. I - * don't have full understanding on the way Apple tweaked the PID - * algorithm for the CPU control, it is definitely not a standard - * implementation... - * - * TODO: - Check MPU structure version/signature - * - Add things like /sbin/overtemp for non-critical - * overtemp conditions so userland can take some policy - * decisions, like slowing 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 ? Definitely need to factor code a bit - * better... 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: - * - * Nov. 13, 2003 : 0.5 - * - First release - * - * Nov. 14, 2003 : 0.6 - * - Read fan speed from FCU, low level fan routines now deal - * with errors & check fan status, though higher level don't - * do much. - * - Move a bunch of definitions to .h file - * - * Nov. 18, 2003 : 0.7 - * - Fix build on ppc64 kernel - * - Move back statics definitions to .c file - * - Avoid calling schedule_timeout with a negative number - * - * Dec. 18, 2003 : 0.8 - * - Fix typo when reading back fan speed on 2 CPU machines - * - * Mar. 11, 2004 : 0.9 - * - Rework code accessing the ADC chips, make it more robust and - * closer to the chip spec. Also make sure it is configured properly, - * I've seen yet unexplained cases where on startup, I would have stale - * values in the configuration register - * - Switch back to use of target fan speed for PID, thus lowering - * pressure on i2c - * - * Oct. 20, 2004 : 1.1 - * - Add device-tree lookup for fan IDs, should detect liquid cooling - * pumps when present - * - Enable driver for PowerMac7,3 machines - * - Split the U3/Backside cooling on U3 & U3H versions as Darwin does - * - 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 - * - Retrieve 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 - * - * July. 06, 2006 : 1.3 - * - Fix setting of RPM fans on Xserve G5 (they were going too fast) - * - Add missing slots fan control loop for Xserve G5 - * - Lower fixed slots fan speed from 50% to 40% on desktop G5s. We - * still can't properly implement the control loop for these, so let's - * reduce the noise a little bit, it appears that 40% still gives us - * a pretty good air flow - * - Add code to "tickle" the FCU regulary so it doesn't think that - * we are gone while in fact, the machine just didn't need any fan - * speed change lately - * - */ - -#include <linux/types.h> -#include <linux/module.h> -#include <linux/errno.h> -#include <linux/kernel.h> -#include <linux/delay.h> -#include <linux/sched.h> -#include <linux/init.h> -#include <linux/spinlock.h> -#include <linux/wait.h> -#include <linux/reboot.h> -#include <linux/kmod.h> -#include <linux/i2c.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/sections.h> -#include <asm/macio.h> - -#include "therm_pm72.h" - -#define VERSION "1.3" - -#undef DEBUG - -#ifdef DEBUG -#define DBG(args...) printk(args) -#else -#define DBG(args...) do { } while(0) -#endif - - -/* - * Driver statics - */ - -static struct platform_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 processor_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 struct slots_pid_state slots_state; -static int state; -static int cpu_count; -static int cpu_pid_type; -static struct task_struct *ctrl_task; -static struct completion ctrl_complete; -static int critical_state; -static int rackmac; -static s32 dimm_output_clamp; -static int fcu_rpm_shift; -static int fcu_tickle_ticks; -static DEFINE_MUTEX(driver_lock); - -/* - * 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 - * are identified as having the pumps (though that identification is at - * least dodgy). Ultimately, we could probably switch completely to this - * algorithm provided we hack it to deal with the UP case - */ -#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 - * are defaults valid for all earlier machines. Newer machines will - * eventually override the table content based on the device-tree - */ -struct fcu_fan_table -{ - char* loc; /* location code */ - int type; /* 0 = rpm, 1 = pwm, 2 = pump */ - int id; /* id or -1 */ -}; - -#define FCU_FAN_RPM 0 -#define FCU_FAN_PWM 1 - -#define FCU_FAN_ABSENT_ID -1 - -#define FCU_FAN_COUNT ARRAY_SIZE(fcu_fans) - -struct fcu_fan_table fcu_fans[] = { - [BACKSIDE_FAN_PWM_INDEX] = { - .loc = "BACKSIDE,SYS CTRLR FAN", - .type = FCU_FAN_PWM, - .id = BACKSIDE_FAN_PWM_DEFAULT_ID, - }, - [DRIVES_FAN_RPM_INDEX] = { - .loc = "DRIVE BAY", - .type = FCU_FAN_RPM, - .id = DRIVES_FAN_RPM_DEFAULT_ID, - }, - [SLOTS_FAN_PWM_INDEX] = { - .loc = "SLOT,PCI FAN", - .type = FCU_FAN_PWM, - .id = SLOTS_FAN_PWM_DEFAULT_ID, - }, - [CPUA_INTAKE_FAN_RPM_INDEX] = { - .loc = "CPU A INTAKE", - .type = FCU_FAN_RPM, - .id = CPUA_INTAKE_FAN_RPM_DEFAULT_ID, - }, - [CPUA_EXHAUST_FAN_RPM_INDEX] = { - .loc = "CPU A EXHAUST", - .type = FCU_FAN_RPM, - .id = CPUA_EXHAUST_FAN_RPM_DEFAULT_ID, - }, - [CPUB_INTAKE_FAN_RPM_INDEX] = { - .loc = "CPU B INTAKE", - .type = FCU_FAN_RPM, - .id = CPUB_INTAKE_FAN_RPM_DEFAULT_ID, - }, - [CPUB_EXHAUST_FAN_RPM_INDEX] = { - .loc = "CPU B EXHAUST", - .type = FCU_FAN_RPM, - .id = CPUB_EXHAUST_FAN_RPM_DEFAULT_ID, - }, - /* pumps aren't present by default, have to be looked up in the - * device-tree - */ - [CPUA_PUMP_RPM_INDEX] = { - .loc = "CPU A PUMP", - .type = FCU_FAN_RPM, - .id = FCU_FAN_ABSENT_ID, - }, - [CPUB_PUMP_RPM_INDEX] = { - .loc = "CPU B PUMP", - .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, - }, -}; - -static struct i2c_driver therm_pm72_driver; - -/* - * Utility function to create an i2c_client structure and - * attach it to one of u3 adapters - */ -static struct i2c_client *attach_i2c_chip(int id, const char *name) -{ - struct i2c_client *clt; - struct i2c_adapter *adap; - struct i2c_board_info info; - - if (id & 0x200) - adap = k2; - else if (id & 0x100) - adap = u3_1; - else - adap = u3_0; - if (adap == NULL) - return NULL; - - 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); - return NULL; - } - - /* - * 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; -} - -/* - * Here are the i2c chip access wrappers - */ - -static void initialize_adc(struct cpu_pid_state *state) -{ - int rc; - u8 buf[2]; - - /* Read ADC the configuration register and cache it. We - * also make sure Config2 contains proper values, I've seen - * cases where we got stale grabage in there, thus preventing - * proper reading of conv. values - */ - - /* Clear Config2 */ - buf[0] = 5; - buf[1] = 0; - i2c_master_send(state->monitor, buf, 2); - - /* Read & cache Config1 */ - buf[0] = 1; - rc = i2c_master_send(state->monitor, buf, 1); - if (rc > 0) { - rc = i2c_master_recv(state->monitor, buf, 1); - if (rc > 0) { - state->adc_config = buf[0]; - DBG("ADC config reg: %02x\n", state->adc_config); - /* Disable shutdown mode */ - state->adc_config &= 0xfe; - buf[0] = 1; - buf[1] = state->adc_config; - rc = i2c_master_send(state->monitor, buf, 2); - } - } - if (rc <= 0) - printk(KERN_ERR "therm_pm72: Error reading ADC config" - " register !\n"); -} - -static int read_smon_adc(struct cpu_pid_state *state, int chan) -{ - int rc, data, tries = 0; - u8 buf[2]; - - for (;;) { - /* Set channel */ - buf[0] = 1; - buf[1] = (state->adc_config & 0x1f) | (chan << 5); - rc = i2c_master_send(state->monitor, buf, 2); - if (rc <= 0) - goto error; - /* Wait for conversion */ - msleep(1); - /* Switch to data register */ - buf[0] = 4; - rc = i2c_master_send(state->monitor, buf, 1); - if (rc <= 0) - goto error; - /* Read result */ - rc = i2c_master_recv(state->monitor, buf, 2); - if (rc < 0) - goto error; - data = ((u16)buf[0]) << 8 | (u16)buf[1]; - return data >> 6; - error: - DBG("Error reading ADC, retrying...\n"); - if (++tries > 10) { - printk(KERN_ERR "therm_pm72: Error reading ADC !\n"); - return -1; - } - msleep(10); - } -} - -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; - - buf[0] = reg; - tries = 0; - for (;;) { - nw = i2c_master_send(fcu, buf, 1); - if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) - break; - msleep(10); - ++tries; - } - if (nw <= 0) { - printk(KERN_ERR "Failure writing address to FCU: %d", nw); - return -EIO; - } - tries = 0; - for (;;) { - nr = i2c_master_recv(fcu, buf, nb); - if (nr > 0 || (nr < 0 && nr != -ENODEV) || tries >= 100) - break; - msleep(10); - ++tries; - } - if (nr <= 0) - printk(KERN_ERR "Failure reading data from FCU: %d", nw); - return nr; -} - -static int fan_write_reg(int reg, const unsigned char *ptr, int nb) -{ - int tries, nw; - unsigned char buf[16]; - - buf[0] = reg; - memcpy(buf+1, ptr, nb); - ++nb; - tries = 0; - for (;;) { - nw = i2c_master_send(fcu, buf, nb); - if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100) - break; - msleep(10); - ++tries; - } - if (nw < 0) - printk(KERN_ERR "Failure writing to FCU: %d", nw); - return nw; -} - -static int start_fcu(void) -{ - unsigned char buf = 0xff; - int rc; - - rc = fan_write_reg(0xe, &buf, 1); - if (rc < 0) - return -EIO; - 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; - - if (fcu_fans[fan_index].type != FCU_FAN_RPM) - return -EINVAL; - id = fcu_fans[fan_index].id; - 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; - rc = fan_write_reg(0x10 + (id * 2), buf, 2); - if (rc < 0) - return -EIO; - return 0; -} - -static int get_rpm_fan(int fan_index, int programmed) -{ - unsigned char failure; - unsigned char active; - unsigned char buf[2]; - int rc, id, reg_base; - - if (fcu_fans[fan_index].type != FCU_FAN_RPM) - return -EINVAL; - id = fcu_fans[fan_index].id; - if (id == FCU_FAN_ABSENT_ID) - return -EINVAL; - - rc = fan_read_reg(0xb, &failure, 1); - if (rc != 1) - return -EIO; - if ((failure & (1 << id)) != 0) - return -EFAULT; - rc = fan_read_reg(0xd, &active, 1); - if (rc != 1) - return -EIO; - if ((active & (1 << id)) == 0) - return -ENXIO; - - /* Programmed value or real current speed */ - reg_base = programmed ? 0x10 : 0x11; - rc = fan_read_reg(reg_base + (id * 2), buf, 2); - if (rc != 2) - return -EIO; - - return (buf[0] << (8 - fcu_rpm_shift)) | buf[1] >> fcu_rpm_shift; -} - -static int set_pwm_fan(int fan_index, int pwm) -{ - unsigned char buf[2]; - int rc, id; - - if (fcu_fans[fan_index].type != FCU_FAN_PWM) - return -EINVAL; - id = fcu_fans[fan_index].id; - if (id == FCU_FAN_ABSENT_ID) - return -EINVAL; - - if (pwm < 10) - pwm = 10; - else if (pwm > 100) - pwm = 100; - pwm = (pwm * 2559) / 1000; - buf[0] = pwm; - rc = fan_write_reg(0x30 + (id * 2), buf, 1); - if (rc < 0) - return rc; - return 0; -} - -static int get_pwm_fan(int fan_index) -{ - unsigned char failure; - unsigned char active; - unsigned char buf[2]; - int rc, id; - - if (fcu_fans[fan_index].type != FCU_FAN_PWM) - return -EINVAL; - id = fcu_fans[fan_index].id; - if (id == FCU_FAN_ABSENT_ID) - return -EINVAL; - - rc = fan_read_reg(0x2b, &failure, 1); - if (rc != 1) - return -EIO; - if ((failure & (1 << id)) != 0) - return -EFAULT; - rc = fan_read_reg(0x2d, &active, 1); - if (rc != 1) - return -EIO; - if ((active & (1 << id)) == 0) - return -ENXIO; - - /* Programmed value or real current speed */ - rc = fan_read_reg(0x30 + (id * 2), buf, 1); - if (rc != 1) - return -EIO; - - 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 - */ -static int read_eeprom(int cpu, struct mpu_data *out) -{ - struct device_node *np; - char nodename[64]; - const u8 *data; - int len; - - /* prom.c routine for finding a node by path is a bit brain dead - * and requires exact @xxx unit numbers. This is a bit ugly but - * will work for these machines - */ - 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 retrieve cpuid node from device-tree\n"); - return -ENODEV; - } - data = of_get_property(np, "cpuid", &len); - if (data == NULL) { - printk(KERN_ERR "therm_pm72: Failed to retrieve cpuid property from device-tree\n"); - of_node_put(np); - return -ENODEV; - } - memcpy(out, data, sizeof(struct mpu_data)); - of_node_put(np); - - return 0; -} - -static void fetch_cpu_pumps_minmax(void) -{ - struct cpu_pid_state *state0 = &processor_state[0]; - struct cpu_pid_state *state1 = &processor_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 - * choice but implement a bunch of them... - * - * That sucks a bit, we take the lock because FIX32TOPRINT evaluates - * the input twice... I accept patches :) - */ -#define BUILD_SHOW_FUNC_FIX(name, data) \ -static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - ssize_t r; \ - mutex_lock(&driver_lock); \ - r = sprintf(buf, "%d.%03d", FIX32TOPRINT(data)); \ - mutex_unlock(&driver_lock); \ - return r; \ -} -#define BUILD_SHOW_FUNC_INT(name, data) \ -static ssize_t show_##name(struct device *dev, struct device_attribute *attr, char *buf) \ -{ \ - return sprintf(buf, "%d", data); \ -} - -BUILD_SHOW_FUNC_FIX(cpu0_temperature, processor_state[0].last_temp) -BUILD_SHOW_FUNC_FIX(cpu0_voltage, processor_state[0].voltage) -BUILD_SHOW_FUNC_FIX(cpu0_current, processor_state[0].current_a) -BUILD_SHOW_FUNC_INT(cpu0_exhaust_fan_rpm, processor_state[0].rpm) -BUILD_SHOW_FUNC_INT(cpu0_intake_fan_rpm, processor_state[0].intake_rpm) - -BUILD_SHOW_FUNC_FIX(cpu1_temperature, processor_state[1].last_temp) -BUILD_SHOW_FUNC_FIX(cpu1_voltage, processor_state[1].voltage) -BUILD_SHOW_FUNC_FIX(cpu1_current, processor_state[1].current_a) -BUILD_SHOW_FUNC_INT(cpu1_exhaust_fan_rpm, processor_state[1].rpm) -BUILD_SHOW_FUNC_INT(cpu1_intake_fan_rpm, processor_state[1].intake_rpm) - -BUILD_SHOW_FUNC_FIX(backside_temperature, backside_state.last_temp) -BUILD_SHOW_FUNC_INT(backside_fan_pwm, backside_state.pwm) - -BUILD_SHOW_FUNC_FIX(drives_temperature, drives_state.last_temp) -BUILD_SHOW_FUNC_INT(drives_fan_rpm, drives_state.rpm) - -BUILD_SHOW_FUNC_FIX(slots_temperature, slots_state.last_temp) -BUILD_SHOW_FUNC_INT(slots_fan_pwm, slots_state.pwm) - -BUILD_SHOW_FUNC_FIX(dimms_temperature, dimms_state.last_temp) - -static DEVICE_ATTR(cpu0_temperature,S_IRUGO,show_cpu0_temperature,NULL); -static DEVICE_ATTR(cpu0_voltage,S_IRUGO,show_cpu0_voltage,NULL); -static DEVICE_ATTR(cpu0_current,S_IRUGO,show_cpu0_current,NULL); -static DEVICE_ATTR(cpu0_exhaust_fan_rpm,S_IRUGO,show_cpu0_exhaust_fan_rpm,NULL); -static DEVICE_ATTR(cpu0_intake_fan_rpm,S_IRUGO,show_cpu0_intake_fan_rpm,NULL); - -static DEVICE_ATTR(cpu1_temperature,S_IRUGO,show_cpu1_temperature,NULL); -static DEVICE_ATTR(cpu1_voltage,S_IRUGO,show_cpu1_voltage,NULL); -static DEVICE_ATTR(cpu1_current,S_IRUGO,show_cpu1_current,NULL); -static DEVICE_ATTR(cpu1_exhaust_fan_rpm,S_IRUGO,show_cpu1_exhaust_fan_rpm,NULL); -static DEVICE_ATTR(cpu1_intake_fan_rpm,S_IRUGO,show_cpu1_intake_fan_rpm,NULL); - -static DEVICE_ATTR(backside_temperature,S_IRUGO,show_backside_temperature,NULL); -static DEVICE_ATTR(backside_fan_pwm,S_IRUGO,show_backside_fan_pwm,NULL); - -static DEVICE_ATTR(drives_temperature,S_IRUGO,show_drives_temperature,NULL); -static DEVICE_ATTR(drives_fan_rpm,S_IRUGO,show_drives_fan_rpm,NULL); - -static DEVICE_ATTR(slots_temperature,S_IRUGO,show_slots_temperature,NULL); -static DEVICE_ATTR(slots_fan_pwm,S_IRUGO,show_slots_fan_pwm,NULL); - -static DEVICE_ATTR(dimms_temperature,S_IRUGO,show_dimms_temperature,NULL); - -/* - * CPUs fans control loop - */ - -static int do_read_one_cpu_values(struct cpu_pid_state *state, s32 *temp, s32 *power) -{ - s32 ltemp, volts, amps; - int index, rc = 0; - - /* Default (in case of error) */ - *temp = state->cur_temp; - *power = state->cur_power; - - if (cpu_pid_type == CPU_PID_TYPE_RACKMAC) - index = (state->index == 0) ? - CPU_A1_FAN_RPM_INDEX : CPU_B1_FAN_RPM_INDEX; - else - 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 - */ - DBG(" cpu %d, fan reading error !\n", state->index); - } else { - state->rpm = rc; - DBG(" cpu %d, exhaust RPM: %d\n", state->index, state->rpm); - } - - /* Get some sensor readings and scale it */ - ltemp = read_smon_adc(state, 1); - if (ltemp == -1) { - /* XXX What do we do now ? */ - state->overtemp++; - if (rc == 0) - rc = -EIO; - DBG(" cpu %d, temp reading error !\n", state->index); - } else { - /* Fixup temperature according to diode calibration - */ - DBG(" cpu %d, temp raw: %04x, m_diode: %04x, b_diode: %04x\n", - state->index, - ltemp, state->mpu.mdiode, state->mpu.bdiode); - *temp = ((s32)ltemp * (s32)state->mpu.mdiode + ((s32)state->mpu.bdiode << 12)) >> 2; - state->last_temp = *temp; - DBG(" temp: %d.%03d\n", FIX32TOPRINT((*temp))); - } - - /* - * Read voltage & current and calculate power - */ - volts = read_smon_adc(state, 3); - amps = read_smon_adc(state, 4); - - /* Scale voltage and current raw sensor values according to fixed scales - * obtained in Darwin and calculate power from I and V - */ - volts *= ADC_CPU_VOLTAGE_SCALE; - amps *= ADC_CPU_CURRENT_SCALE; - *power = (((u64)volts) * ((u64)amps)) >> 16; - state->voltage = volts; - state->current_a = amps; - state->last_power = *power; - - DBG(" cpu %d, current: %d.%03d, voltage: %d.%03d, power: %d.%03d W\n", - state->index, FIX32TOPRINT(state->current_a), - FIX32TOPRINT(state->voltage), FIX32TOPRINT(*power)); - - return 0; -} - -static void do_cpu_pid(struct cpu_pid_state *state, s32 temp, s32 power) -{ - s32 power_target, integral, derivative, proportional, adj_in_target, sval; - s64 integ_p, deriv_p, prop_p, sum; - int i; - - /* Calculate power target value (could be done once for all) - * and convert to a 16.16 fp number - */ - power_target = ((u32)(state->mpu.pmaxh - state->mpu.padjmax)) << 16; - DBG(" power target: %d.%03d, error: %d.%03d\n", - FIX32TOPRINT(power_target), FIX32TOPRINT(power_target - power)); - - /* Store temperature and power in history array */ - state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE; - state->temp_history[state->cur_temp] = temp; - state->cur_power = (state->cur_power + 1) % state->count_power; - state->power_history[state->cur_power] = power; - state->error_history[state->cur_power] = power_target - power; - - /* If first loop, fill the history table */ - if (state->first) { - for (i = 0; i < (state->count_power - 1); i++) { - state->cur_power = (state->cur_power + 1) % state->count_power; - state->power_history[state->cur_power] = power; - state->error_history[state->cur_power] = power_target - power; - } - for (i = 0; i < (CPU_TEMP_HISTORY_SIZE - 1); i++) { - state->cur_temp = (state->cur_temp + 1) % CPU_TEMP_HISTORY_SIZE; - state->temp_history[state->cur_temp] = temp; - } - state->first = 0; - } - - /* Calculate the integral term normally based on the "power" values */ - sum = 0; - integral = 0; - for (i = 0; i < state->count_power; i++) - integral += state->error_history[i]; - integral *= CPU_PID_INTERVAL; - DBG(" integral: %08x\n", integral); - - /* Calculate the adjusted input (sense value). - * G_r is 12.20 - * integ is 16.16 - * so the result is 28.36 - * - * input target is mpu.ttarget, input max is mpu.tmax - */ - integ_p = ((s64)state->mpu.pid_gr) * (s64)integral; - DBG(" integ_p: %d\n", (int)(integ_p >> 36)); - sval = (state->mpu.tmax << 16) - ((integ_p >> 20) & 0xffffffff); - adj_in_target = (state->mpu.ttarget << 16); - if (adj_in_target > sval) - adj_in_target = sval; - DBG(" adj_in_target: %d.%03d, ttarget: %d\n", FIX32TOPRINT(adj_in_target), - state->mpu.ttarget); - - /* Calculate the derivative term */ - derivative = state->temp_history[state->cur_temp] - - state->temp_history[(state->cur_temp + CPU_TEMP_HISTORY_SIZE - 1) - % CPU_TEMP_HISTORY_SIZE]; - derivative /= CPU_PID_INTERVAL; - deriv_p = ((s64)state->mpu.pid_gd) * (s64)derivative; - DBG(" deriv_p: %d\n", (int)(deriv_p >> 36)); - sum += deriv_p; - - /* Calculate the proportional term */ - proportional = temp - adj_in_target; - prop_p = ((s64)state->mpu.pid_gp) * (s64)proportional; - DBG(" prop_p: %d\n", (int)(prop_p >> 36)); - sum += prop_p; - - /* Scale sum */ - sum >>= 36; - - DBG(" sum: %d\n", (int)sum); - state->rpm += (s32)sum; -} - -static void do_monitor_cpu_combined(void) -{ - struct cpu_pid_state *state0 = &processor_state[0]; - struct cpu_pid_state *state1 = &processor_state[1]; - s32 temp0, power0, temp1, power1; - s32 temp_combi, power_combi; - int rc, intake, pump; - - rc = do_read_one_cpu_values(state0, &temp0, &power0); - if (rc < 0) { - /* XXX What do we do now ? */ - } - state1->overtemp = 0; - rc = do_read_one_cpu_values(state1, &temp1, &power1); - if (rc < 0) { - /* XXX What do we do now ? */ - } - if (state1->overtemp) - state0->overtemp++; - - temp_combi = max(temp0, temp1); - power_combi = max(power0, power1); - - /* Check tmax, increment overtemp if we are there. At tmax+8, we go - * full blown immediately and try to trigger a shutdown - */ - 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 / 4; - } else if (temp_combi > (state0->mpu.tmax << 16)) { - state0->overtemp++; - 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_max; - 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; - 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 * state0->pump_max) / - state0->mpu.rmaxn_exhaust_fan; - 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 */ - state1->rpm = state0->rpm; - state1->intake_rpm = state0->intake_rpm; - - DBG("** CPU %d RPM: %d Ex, %d, Pump: %d, In, overtemp: %d\n", - state1->index, (int)state1->rpm, intake, pump, state1->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... - */ - set_rpm_fan(CPUA_INTAKE_FAN_RPM_INDEX, intake); - set_rpm_fan(CPUA_EXHAUST_FAN_RPM_INDEX, state0->rpm); - set_rpm_fan(CPUB_INTAKE_FAN_RPM_INDEX, intake); - set_rpm_fan(CPUB_EXHAUST_FAN_RPM_INDEX, state0->rpm); - - if (fcu_fans[CPUA_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) - set_rpm_fan(CPUA_PUMP_RPM_INDEX, pump); - if (fcu_fans[CPUB_PUMP_RPM_INDEX].id != FCU_FAN_ABSENT_ID) - set_rpm_fan(CPUB_PUMP_RPM_INDEX, pump); -} - -static void do_monitor_cpu_split(struct cpu_pid_state *state) -{ - s32 temp, power; - int rc, intake; - - /* 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 / 4; - } else if (temp > (state->mpu.tmax << 16)) { - state->overtemp++; - 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) { - state->rpm = state->mpu.rmaxn_exhaust_fan; - state->intake_rpm = intake = state->mpu.rmaxn_intake_fan; - goto do_set_fans; - } - - /* 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; - 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: - DBG("** CPU %d RPM: %d Ex, %d In, overtemp: %d\n", - state->index, (int)state->rpm, intake, 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(CPUA_INTAKE_FAN_RPM_INDEX, intake); - set_rpm_fan(CPUA_EXHAUST_FAN_RPM_INDEX, state->rpm); - } else { - set_rpm_fan(CPUB_INTAKE_FAN_RPM_INDEX, intake); - set_rpm_fan(CPUB_EXHAUST_FAN_RPM_INDEX, state->rpm); - } -} - -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 / 4; - } else if (temp > (state->mpu.tmax << 16)) { - state->overtemp++; - 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-> |