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 + -
显示快捷键?