linuxppc-dev.lists.ozlabs.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] ugly hack to make therm_pm72 work on XServe G5
@ 2004-08-05 11:43 Harald Welte
  2004-08-06  8:04 ` Harald Welte
  0 siblings, 1 reply; 3+ messages in thread
From: Harald Welte @ 2004-08-05 11:43 UTC (permalink / raw)
  To: linuxppc-dev

[-- Attachment #1: Type: text/plain, Size: 1199 bytes --]

Hi!

I've now figured out most of the details on the Apple FCU setup on my
Dual G5 XServe ClusterNode.  In case anybody is interested, attached is
a ugly hack to add support for RackMac3,1 to therm_pm72.c and make the
machine a bit less noisy.

I'm now working on a completely new FCU driver that just exports the fan
RPM / PWM, voltage/current/... to userspace.  I think some userspace
process can then take care about the policy when and how to blow what
amount of air through the individual fans.

My main problem is that the machine will be installed in some rack 500km
away, so I can't really make sure that any further driver development
will actually work as expected. ;)

Just in case anybody else wants to dive into this issue, please contact
me before, I already discovered quite a lot more about the FCU/i2c/...
setup in the RackMac3,1 than what is visible in this patch.

(attached also a small userspace program to hard-code the fans to some
rpm/pwm)
--
- Harald Welte <laforge@gnumonks.org>               http://www.gnumonks.org/
============================================================================
Programming is like sex: One mistake and you have to support it your lifetime

[-- Attachment #2: therm_pm72-xserve.patch --]
[-- Type: text/plain, Size: 6413 bytes --]

diff -u orig-kernel/therm_pm72.c old-kernel/therm_pm72.c
--- orig-kernel/drivers/macintosh/therm_pm72.c	2004-08-05 14:12:18.000000000 +0200
+++ old-kernel/drivers/macintosh/therm_pm72.c	2004-08-05 15:20:16.000000000 +0200
@@ -73,6 +73,9 @@
  *        values in the configuration register
  *	- Switch back to use of target fan speed for PID, thus lowering
  *        pressure on i2c
+ * Aug. 05, 2004: 0.91
+ *	- Harald Welte <laforge@gnumonks.org>:
+ *		- add minimal support for XServe G5
  */
 
 #include <linux/config.h>
@@ -101,9 +104,9 @@
 
 #include "therm_pm72.h"
 
-#define VERSION "0.9"
+#define VERSION "0.91"
 
-#undef DEBUG
+#define DEBUG
 
 #ifdef DEBUG
 #define DBG(args...)	printk(args)
@@ -509,10 +514,7 @@
 	DBG("cpu %d:\n", state->index);
 
 	/* Read current fan status */
-	if (state->index == 0)
-		rc = get_rpm_fan(CPUA_EXHAUST_FAN_RPM_ID, !RPM_PID_USE_ACTUAL_SPEED);
-	else
-		rc = get_rpm_fan(CPUB_EXHAUST_FAN_RPM_ID, !RPM_PID_USE_ACTUAL_SPEED);
+	rc = get_rpm_fan(state->exhaust_fan_id, !RPM_PID_USE_ACTUAL_SPEED);
 	if (rc < 0) {
 		printk(KERN_WARNING "Error %d reading CPU %d exhaust fan !\n",
 		       rc, state->index);
@@ -662,12 +664,15 @@
 	 * failures (-EFAULT) we probably want to notify userland
 	 * some way...
 	 */
-	if (state->index == 0) {
-		set_rpm_fan(CPUA_INTAKE_FAN_RPM_ID, intake);
-		set_rpm_fan(CPUA_EXHAUST_FAN_RPM_ID, state->rpm);
-	} else {
-		set_rpm_fan(CPUB_INTAKE_FAN_RPM_ID, intake);
-		set_rpm_fan(CPUB_EXHAUST_FAN_RPM_ID, state->rpm);
+	set_rpm_fan(state->intake_fan_id, intake);
+	set_rpm_fan(state->exhaust_fan_id, state->rpm);
+
+	/* ugly hack */
+	if (machine_is_compatible("RackMac3,1")) {
+		if (state->index == 0)
+			set_rpm_fan(2, state->rpm);
+		else
+			set_rpm_fan(5, state->rpm);
 	}
 }
 
@@ -699,6 +704,24 @@
 	}
 	DBG("CPU %d Using %d power history entries\n", index, state->count_power);
 
+	if (machine_is_compatible("PowerMac7,2")) {
+		if (index == 0) {
+			state->intake_fan_id = 3;
+			state->exhaust_fan_id = 4;
+		} else {
+			state->intake_fan_id = 5;
+			state->exhaust_fan_id = 6;
+		}
+	} else {
+		/* RackMac 3,1 */
+		if (index == 0) {
+			state->intake_fan_id = 3;	/* right */
+			state->exhaust_fan_id = 1;	/* left */
+		} else {
+			state->intake_fan_id = 4;	/* left */
+			state->exhaust_fan_id = 6;	/* right */
+		}
+	}
 	if (index == 0) {
 		device_create_file(&of_dev->dev, &dev_attr_cpu0_temperature);
 		device_create_file(&of_dev->dev, &dev_attr_cpu0_voltage);
@@ -772,6 +795,7 @@
 		state->pwm = rc;
 	DBG("  current pwm: %d\n", state->pwm);
 
+	if (machine_is_compatible("PowerMac7,2")) {
 	/* Get some sensor readings */
 	temp = i2c_smbus_read_byte_data(state->monitor, MAX6690_EXT_TEMP) << 16;
 	state->last_temp = temp;
@@ -829,6 +853,8 @@
 		state->pwm = BACKSIDE_PID_OUTPUT_MIN;
 	if (state->pwm > BACKSIDE_PID_OUTPUT_MAX)
 		state->pwm = BACKSIDE_PID_OUTPUT_MAX;
+	} else 
+		state->pwm = 40;
 
 	DBG("** BACKSIDE PWM: %d\n", (int)state->pwm);
 	set_pwm_fan(BACKSIDE_FAN_PWM_ID, state->pwm);
@@ -843,11 +869,16 @@
 	state->first = 1;
 	state->pwm = 50;
 
-	state->monitor = attach_i2c_chip(BACKSIDE_MAX_ID, "backside_temp");
-	if (state->monitor == NULL)
-		return -ENODEV;
+	if (machine_is_compatible("PowerMac7,2")) {
+		state->monitor = attach_i2c_chip(BACKSIDE_MAX_ID, 
+						 "backside_temp");
+		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_temperature);
 	device_create_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
 
 	return 0;
@@ -860,11 +891,13 @@
 {
 	if (state->monitor == NULL)
 		return;
-
-	device_remove_file(&of_dev->dev, &dev_attr_backside_temperature);
+	if (machine_is_compatible("PowerMac7,2")) {
+		device_remove_file(&of_dev->dev, 
+				   &dev_attr_backside_temperature);
+		detach_i2c_chip(state->monitor);
+	}
 	device_remove_file(&of_dev->dev, &dev_attr_backside_fan_pwm);
 
-	detach_i2c_chip(state->monitor);
 	state->monitor = NULL;
 }
  
@@ -1031,7 +1064,9 @@
 		if (cpu_state[1].monitor != NULL)
 			do_monitor_cpu(&cpu_state[1]);
 		do_monitor_backside(&backside_state);
-		do_monitor_drives(&drives_state);
+		if (machine_is_compatible("PowerMac7,2")) {
+			do_monitor_drives(&drives_state);
+		}
 		up(&driver_lock);
 
 		if (critical_state == 1) {
@@ -1070,9 +1105,11 @@
 {
 	dispose_cpu_state(&cpu_state[0]);
 	dispose_cpu_state(&cpu_state[1]);
-
 	dispose_backside_state(&backside_state);
-	dispose_drives_state(&drives_state);
+
+	if (machine_is_compatible("PowerMac7,2")) {
+		dispose_drives_state(&drives_state);
+	}
 }
 
 /*
@@ -1101,8 +1138,11 @@
 		goto fail;
 	if (init_backside_state(&backside_state))
 		goto fail;
-	if (init_drives_state(&drives_state))
-		goto fail;
+
+	if (machine_is_compatible("PowerMac7,2")) {
+		if (init_drives_state(&drives_state))
+			goto fail;
+	}
 
 	DBG("all control loops up !\n");
 
@@ -1280,7 +1320,8 @@
 {
 	struct device_node *np;
 
-	if (!machine_is_compatible("PowerMac7,2"))
+	if (!machine_is_compatible("PowerMac7,2") &&
+	    !machine_is_compatible("RackMac3,1"))
 	    	return -ENODEV;
 
 	printk(KERN_INFO "PowerMac G5 Thermal control driver %s\n", VERSION);
@@ -1313,6 +1354,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 PowerMac7,2 / RackMac3,1  G5 thermal control");
 MODULE_LICENSE("GPL");
 
diff -u orig-kernel/therm_pm72.h old-kernel/therm_pm72.h
--- orig-kernel/drivers/macintosh/therm_pm72.h	2004-08-05 14:12:18.000000000 +0200
+++ old-kernel/drivers/macintosh/therm_pm72.h	2004-08-05 15:20:16.000000000 +0200
@@ -191,11 +191,6 @@
  * CPU B FAKE POWER	49	(I_V_inputs: 18, 19)
  */
 
-#define CPUA_INTAKE_FAN_RPM_ID		3
-#define CPUA_EXHAUST_FAN_RPM_ID		4
-#define CPUB_INTAKE_FAN_RPM_ID		5
-#define CPUB_EXHAUST_FAN_RPM_ID		6
-
 #define CPU_INTAKE_SCALE		0x0000f852
 #define CPU_TEMP_HISTORY_SIZE		2
 #define CPU_POWER_HISTORY_SIZE		10
@@ -221,6 +216,8 @@
 	s32			last_temp;
 	int			first;
 	u8			adc_config;
+	int			exhaust_fan_id;
+	int			intake_fan_id;
 };
 
 /*

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] ugly hack to make therm_pm72 work on XServe G5
  2004-08-05 11:43 [PATCH] ugly hack to make therm_pm72 work on XServe G5 Harald Welte
@ 2004-08-06  8:04 ` Harald Welte
  2004-08-06  8:09   ` Benjamin Herrenschmidt
  0 siblings, 1 reply; 3+ messages in thread
From: Harald Welte @ 2004-08-06  8:04 UTC (permalink / raw)
  To: linuxppc-dev; +Cc: Benjamin Herrenschmidt

[-- Attachment #1: Type: text/plain, Size: 575 bytes --]

I actually forgot that small userspace program ;)

It is a total ripoff of the kernel driver and was implemented because
it's easier to play with i2c from userspace during development.

benh:  Any reason why you implemented your own byte+word read/write
routines?  Apparently the i2c_smbus routines work quite fine, even from
userspace...

--
- Harald Welte <laforge@gnumonks.org>               http://www.gnumonks.org/
============================================================================
Programming is like sex: One mistake and you have to support it your lifetime

[-- Attachment #2: rackmac-fans.c --]
[-- Type: text/x-csrc, Size: 4722 bytes --]

#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>

#include <linux/i2c-dev.h>

static int fd;

#ifdef RAW_I2C
static int fan_read_reg(int reg, unsigned char *buf, int nb)
{
	int tries, nr, nw;

	buf[0] = reg;
	tries = 0;
	for (;;) {
		nw = write(fd, buf, 1);
		if (nw > 0 || (nw < 0 && nw != -EIO) || tries >= 100)
			break;
		usleep(10);
		++tries;
	}
	if (nw < 0) {
		fprintf(stderr, "Failure writing address to FCU: %d\n", nw);
		return -EIO;
	}
	tries = 0;
	for (;;) {
		nr = read(fd, buf, nb);
		if (nr > 0 || (nr < 0 && nr != ENODEV) || tries >= 100)
			break;
		usleep(10);
		++tries;
	}
	if (nr <= 0)
		fprintf(stderr, "Failure reading data from FCU: %d\n", 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 = write(fd, buf, nb);
		if (nw > 0 || (nw < 0 && nw != EIO) || tries >= 100)
			break;
		usleep(1);
		++tries;
	}
	if (nw < 0)
		fprintf(stderr, "Failure writing to FCU: %d", nw);
	return nw;
}
#else
static int fan_read_reg(int reg, unsigned char *ptr, int nb)
{
	int ret;

	if (nb == 1) {
		ret = i2c_smbus_read_byte_data(fd, reg);
		if (ret < 0) {
			fprintf(stderr,"error during read_word_data\n");
			return ret;
		}
		*ptr = (unsigned char) ret;
		return nb;
	} else if (nb == 2) {
		ret = i2c_smbus_read_word_data(fd, reg);
		if (ret < 0) {
			fprintf(stderr,"error during read_word_data\n");
			return ret;
		}
		*ptr = (u_int16_t) ret;
		return nb;
	} else {
		fprintf(stderr, "%u bytes not inplemented\n", nb);
		return -EINVAL;
	}
}
static int fan_write_reg(int reg, const unsigned char *ptr, int nb)
{
	int ret;

	if (nb == 1) {
		ret = i2c_smbus_write_byte_data(fd, reg, *ptr);
		if (ret < 0) {
			fprintf(stderr, "error during write_byte_data\n");
			return ret;
		}
		return nb;
	} else if (nb == 2) {
		ret = i2c_smbus_write_word_data(fd, reg, *ptr);
		if (ret < 0) {
			fprintf(stderr, "error during write_word_data\n");
			return ret;
		}
		return nb;
	} else {
		fprintf(stderr, "%u bytes not implemented\n", nb);
		return -EINVAL;
	}
}
#endif

static int set_rpm_fan(int fan, int rpm)
{
	unsigned char buf[2];
	int rc;

	if (rpm < 300)
		rpm = 300;
	else if (rpm > 8191)
		rpm = 8191;
	buf[0] = rpm >> 5;
	buf[1] = rpm << 3;
	rc = fan_write_reg(0x10 + (fan *2), buf, 2);
	if (rc < 0)
		return -EIO;
	return 0;
}

static int get_rpm_fan(int fan, int programmed)
{
	unsigned char failure;
	unsigned char active;
	unsigned char buf[2];
	int rc, reg_base;

	rc = fan_read_reg(0xb, &failure, 1);
	if (rc != 1) {
		fprintf(stderr, "unable to reada 0xb register\n");
		return -EIO;
	}
	if ((failure & (1 << fan)) != 0)
		return -EFAULT;
	rc = fan_read_reg(0xd, &active, 1);
	if (rc != 1) {
		fprintf(stderr, "unable to read 0xd register\n");
		return -EIO;
	}
	if ((active & (1 << fan)) == 0)
		return -ENXIO;

	reg_base = programmed ? 0x10 : 0x11;
	rc = fan_read_reg(reg_base + (fan * 2), buf, 2);
	if (rc != 2) {
		fprintf(stderr, "unable to read fan register 0x%x\n",=20
			reg_base+(fan*2));
		return -EIO;
	}

	return (buf[0] << 5 | buf[1] >> 3);
}

static int set_pwm_fan(int fan, int pwm)
{
	unsigned char buf[2];
	int rc;

	if (pwm < 10)
		pwm = 10;
	else if (pwm > 100)
		pwm = 100;
	pwm = (pwm * 2559) / 1000;
	buf[0] = pwm;
	rc = fan_write_reg(0x30 + (fan*2), buf, 1);
	if (rc < 0)
		return rc;
	return 0;
}

static int get_pwm_fan(int fan)
{
	unsigned char failure;
	unsigned char active;
	unsigned char buf[2];
	int rc;

	rc = fan_read_reg(0x2b, &failure, 1);
	if (rc != 1)
		return -EIO;
	if ((failure & (1 << fan)) != 0)
		return -EFAULT;
	rc = fan_read_reg(0x2d, &active, 1);
	if (rc != 1)
		return -EIO;
	if ((active & (1 << fan)) == 0)
		return -ENXIO;
=09
	rc = fan_read_reg(0x30 + (fan *2), buf, 1);
	if (rc != 1)
		return -EIO;

	return (buf[0] * 1000) / 2559;
}


int main(int argc, char **argv)
{
	int ret;
	int fan_num;

	fd = open("/dev/i2c-2", O_RDWR);
	if (fd < 0)
		exit(1);

	ret = ioctl(fd, I2C_SLAVE_FORCE, 0x2f);
	if (ret < 0) {
		fprintf(stderr, "Unable to access device 0x2f\n");
		exit(1);
	}

	for (fan_num = 1; fan_num <= 6; fan_num++) {
		int rpm;

		set_rpm_fan(fan_num, 3000);

		rpm = get_rpm_fan(fan_num, 0);
		if (rpm < 0)
			printf("Fan %d: %s\n", fan_num, strerror(-rpm));
		else
			printf("Fan %d: %d rpm\n", fan_num, rpm);
	}
=09
	for (fan_num = 1; fan_num <= 2; fan_num++) {
		int pwm;

		set_pwm_fan(fan_num, 30);
		pwm = get_pwm_fan(fan_num);
		if (pwm < 0)
			printf("PWM-Fan %d: %s\n", fan_num, strerror(-pwm));
		else
			printf("PWM-Fan %d: %d\n", fan_num, pwm);
	}
}

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] ugly hack to make therm_pm72 work on XServe G5
  2004-08-06  8:04 ` Harald Welte
@ 2004-08-06  8:09   ` Benjamin Herrenschmidt
  0 siblings, 0 replies; 3+ messages in thread
From: Benjamin Herrenschmidt @ 2004-08-06  8:09 UTC (permalink / raw)
  To: Harald Welte; +Cc: linuxppc-dev list


On Fri, 2004-08-06 at 18:04, Harald Welte wrote:
> I actually forgot that small userspace program ;)
>
> It is a total ripoff of the kernel driver and was implemented because
> it's easier to play with i2c from userspace during development.
>
> benh:  Any reason why you implemented your own byte+word read/write
> routines?  Apparently the i2c_smbus routines work quite fine, even from
> userspace...

I don't remember the exact reason, I had some issues, either with the
sensors or the FCU, or maybe it was for mimmicing OF code...

Ben.


** Sent via the linuxppc-dev mail list. See http://lists.linuxppc.org/

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2004-08-06  8:09 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-08-05 11:43 [PATCH] ugly hack to make therm_pm72 work on XServe G5 Harald Welte
2004-08-06  8:04 ` Harald Welte
2004-08-06  8:09   ` Benjamin Herrenschmidt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).