therm_pm72.c
来自「linux 内核源代码」· C语言 代码 · 共 2,259 行 · 第 1/5 页
C
2,259 行
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 = &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 * 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; \ down(&driver_lock); \ r = sprintf(buf, "%d.%03d", FIX32TOPRINT(data)); \ up(&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, cpu_state[0].last_temp)BUILD_SHOW_FUNC_FIX(cpu0_voltage, cpu_state[0].voltage)BUILD_SHOW_FUNC_FIX(cpu0_current, cpu_state[0].current_a)BUILD_SHOW_FUNC_INT(cpu0_exhaust_fan_rpm, cpu_state[0].rpm)BUILD_SHOW_FUNC_INT(cpu0_intake_fan_rpm, cpu_state[0].intake_rpm)BUILD_SHOW_FUNC_FIX(cpu1_temperature, cpu_state[1].last_temp)BUILD_SHOW_FUNC_FIX(cpu1_voltage, cpu_state[1].voltage)BUILD_SHOW_FUNC_FIX(cpu1_current, cpu_state[1].current_a)BUILD_SHOW_FUNC_INT(cpu1_exhaust_fan_rpm, cpu_state[1].rpm)BUILD_SHOW_FUNC_INT(cpu1_intake_fan_rpm, cpu_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).
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?