Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ac171c46 authored by Benjamin Herrenschmidt's avatar Benjamin Herrenschmidt Committed by Linus Torvalds
Browse files

[PATCH] powerpc: Thermal control for dual core G5s



This patch adds a windfarm module, windfarm_pm112, for the dual core G5s
(both 2 and 4 core models), keeping the machine from getting into
vacuum-cleaner mode ;) For proper credits, the patch was initially
written by Paul Mackerras, and slightly reworked by me to add overtemp
handling among others. The patch also removes the sysfs attributes from
windfarm_pm81 and windfarm_pm91 and instead adds code to the windfarm
core to automagically expose attributes for sensor & controls.

Signed-off-by: default avatarBenjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@osdl.org>
parent 746f956b
Loading
Loading
Loading
Loading
+8 −0
Original line number Diff line number Diff line
@@ -187,6 +187,14 @@ config WINDFARM_PM91
	  This driver provides thermal control for the PowerMac9,1
          which is the recent (SMU based) single CPU desktop G5

config WINDFARM_PM112
	tristate "Support for thermal management on PowerMac11,2"
	depends on WINDFARM && I2C && PMAC_SMU
	select I2C_PMAC_SMU
	help
	  This driver provides thermal control for the PowerMac11,2
	  which are the recent dual and quad G5 machines using the
	  970MP dual-core processor.

config ANSLCD
	tristate "Support for ANS LCD display"
+5 −0
Original line number Diff line number Diff line
@@ -35,3 +35,8 @@ obj-$(CONFIG_WINDFARM_PM91) += windfarm_smu_controls.o \
				   windfarm_smu_sensors.o \
				   windfarm_lm75_sensor.o windfarm_pid.o \
				   windfarm_cpufreq_clamp.o windfarm_pm91.o
obj-$(CONFIG_WINDFARM_PM112)	+= windfarm_pm112.o windfarm_smu_sat.o \
				   windfarm_smu_controls.o \
				   windfarm_smu_sensors.o \
				   windfarm_max6690_sensor.o \
				   windfarm_lm75_sensor.o windfarm_pid.o
+3 −0
Original line number Diff line number Diff line
@@ -14,6 +14,7 @@
#include <linux/list.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/device.h>

/* Display a 16.16 fixed point value */
#define FIX32TOPRINT(f)	((f) >> 16),((((f) & 0xffff) * 1000) >> 16)
@@ -39,6 +40,7 @@ struct wf_control {
	char			*name;
	int			type;
	struct kref		ref;
	struct device_attribute	attr;
};

#define WF_CONTROL_TYPE_GENERIC		0
@@ -87,6 +89,7 @@ struct wf_sensor {
	struct wf_sensor_ops	*ops;
	char			*name;
	struct kref		ref;
	struct device_attribute	attr;
};

/* Same lifetime rules as controls */
+65 −4
Original line number Diff line number Diff line
@@ -56,6 +56,10 @@ static unsigned int wf_overtemp;
static unsigned int wf_overtemp_counter;
struct task_struct *wf_thread;

static struct platform_device wf_platform_device = {
	.name	= "windfarm",
};

/*
 * Utilities & tick thread
 */
@@ -157,6 +161,40 @@ static void wf_control_release(struct kref *kref)
		kfree(ct);
}

static ssize_t wf_show_control(struct device *dev,
			       struct device_attribute *attr, char *buf)
{
	struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
	s32 val = 0;
	int err;

	err = ctrl->ops->get_value(ctrl, &val);
	if (err < 0)
		return err;
	return sprintf(buf, "%d\n", val);
}

/* This is really only for debugging... */
static ssize_t wf_store_control(struct device *dev,
				struct device_attribute *attr,
				const char *buf, size_t count)
{
	struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
	int val;
	int err;
	char *endp;

	val = simple_strtoul(buf, &endp, 0);
	while (endp < buf + count && (*endp == ' ' || *endp == '\n'))
		++endp;
	if (endp - buf < count)
		return -EINVAL;
	err = ctrl->ops->set_value(ctrl, val);
	if (err < 0)
		return err;
	return count;
}

int wf_register_control(struct wf_control *new_ct)
{
	struct wf_control *ct;
@@ -173,6 +211,13 @@ int wf_register_control(struct wf_control *new_ct)
	kref_init(&new_ct->ref);
	list_add(&new_ct->link, &wf_controls);

	new_ct->attr.attr.name = new_ct->name;
	new_ct->attr.attr.owner = THIS_MODULE;
	new_ct->attr.attr.mode = 0644;
	new_ct->attr.show = wf_show_control;
	new_ct->attr.store = wf_store_control;
	device_create_file(&wf_platform_device.dev, &new_ct->attr);

	DBG("wf: Registered control %s\n", new_ct->name);

	wf_notify(WF_EVENT_NEW_CONTROL, new_ct);
@@ -247,6 +292,19 @@ static void wf_sensor_release(struct kref *kref)
		kfree(sr);
}

static ssize_t wf_show_sensor(struct device *dev,
			      struct device_attribute *attr, char *buf)
{
	struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr);
	s32 val = 0;
	int err;

	err = sens->ops->get_value(sens, &val);
	if (err < 0)
		return err;
	return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val));
}

int wf_register_sensor(struct wf_sensor *new_sr)
{
	struct wf_sensor *sr;
@@ -263,6 +321,13 @@ int wf_register_sensor(struct wf_sensor *new_sr)
	kref_init(&new_sr->ref);
	list_add(&new_sr->link, &wf_sensors);

	new_sr->attr.attr.name = new_sr->name;
	new_sr->attr.attr.owner = THIS_MODULE;
	new_sr->attr.attr.mode = 0444;
	new_sr->attr.show = wf_show_sensor;
	new_sr->attr.store = NULL;
	device_create_file(&wf_platform_device.dev, &new_sr->attr);

	DBG("wf: Registered sensor %s\n", new_sr->name);

	wf_notify(WF_EVENT_NEW_SENSOR, new_sr);
@@ -396,10 +461,6 @@ int wf_is_overtemp(void)
}
EXPORT_SYMBOL_GPL(wf_is_overtemp);

static struct platform_device wf_platform_device = {
	.name	= "windfarm",
};

static int __init windfarm_core_init(void)
{
	DBG("wf: core loaded\n");
+169 −0
Original line number Diff line number Diff line
/*
 * Windfarm PowerMac thermal control.  MAX6690 sensor.
 *
 * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org>
 *
 * Use and redistribute under the terms of the GNU GPL v2.
 */
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <asm/prom.h>
#include <asm/pmac_low_i2c.h>

#include "windfarm.h"

#define VERSION "0.1"

/* This currently only exports the external temperature sensor,
   since that's all the control loops need. */

/* Some MAX6690 register numbers */
#define MAX6690_INTERNAL_TEMP	0
#define MAX6690_EXTERNAL_TEMP	1

struct wf_6690_sensor {
	struct i2c_client	i2c;
	struct wf_sensor	sens;
};

#define wf_to_6690(x)	container_of((x), struct wf_6690_sensor, sens)
#define i2c_to_6690(x)	container_of((x), struct wf_6690_sensor, i2c)

static int wf_max6690_attach(struct i2c_adapter *adapter);
static int wf_max6690_detach(struct i2c_client *client);

static struct i2c_driver wf_max6690_driver = {
	.driver = {
		.name		= "wf_max6690",
	},
	.attach_adapter	= wf_max6690_attach,
	.detach_client	= wf_max6690_detach,
};

static int wf_max6690_get(struct wf_sensor *sr, s32 *value)
{
	struct wf_6690_sensor *max = wf_to_6690(sr);
	s32 data;

	if (max->i2c.adapter == NULL)
		return -ENODEV;

	/* chip gets initialized by firmware */
	data = i2c_smbus_read_byte_data(&max->i2c, MAX6690_EXTERNAL_TEMP);
	if (data < 0)
		return data;
	*value = data << 16;
	return 0;
}

static void wf_max6690_release(struct wf_sensor *sr)
{
	struct wf_6690_sensor *max = wf_to_6690(sr);

	if (max->i2c.adapter) {
		i2c_detach_client(&max->i2c);
		max->i2c.adapter = NULL;
	}
	kfree(max);
}

static struct wf_sensor_ops wf_max6690_ops = {
	.get_value	= wf_max6690_get,
	.release	= wf_max6690_release,
	.owner		= THIS_MODULE,
};

static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr)
{
	struct wf_6690_sensor *max;
	char *name = "u4-temp";

	max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL);
	if (max == NULL) {
		printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: "
		       "no memory\n", name);
		return;
	}

	max->sens.ops = &wf_max6690_ops;
	max->sens.name = name;
	max->i2c.addr = addr >> 1;
	max->i2c.adapter = adapter;
	max->i2c.driver = &wf_max6690_driver;
	strncpy(max->i2c.name, name, I2C_NAME_SIZE-1);

	if (i2c_attach_client(&max->i2c)) {
		printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n");
		goto fail;
	}

	if (wf_register_sensor(&max->sens)) {
		i2c_detach_client(&max->i2c);
		goto fail;
	}

	return;

 fail:
	kfree(max);
}

static int wf_max6690_attach(struct i2c_adapter *adapter)
{
	struct device_node *busnode, *dev = NULL;
	struct pmac_i2c_bus *bus;
	const char *loc;
	u32 *reg;

	bus = pmac_i2c_adapter_to_bus(adapter);
	if (bus == NULL)
		return -ENODEV;
	busnode = pmac_i2c_get_bus_node(bus);

	while ((dev = of_get_next_child(busnode, dev)) != NULL) {
		if (!device_is_compatible(dev, "max6690"))
			continue;
		loc = get_property(dev, "hwsensor-location", NULL);
		reg = (u32 *) get_property(dev, "reg", NULL);
		if (!loc || !reg)
			continue;
		printk("found max6690, loc=%s reg=%x\n", loc, *reg);
		if (strcmp(loc, "BACKSIDE"))
			continue;
		wf_max6690_create(adapter, *reg);
	}

	return 0;
}

static int wf_max6690_detach(struct i2c_client *client)
{
	struct wf_6690_sensor *max = i2c_to_6690(client);

	max->i2c.adapter = NULL;
	wf_unregister_sensor(&max->sens);

	return 0;
}

static int __init wf_max6690_sensor_init(void)
{
	return i2c_add_driver(&wf_max6690_driver);
}

static void __exit wf_max6690_sensor_exit(void)
{
	i2c_del_driver(&wf_max6690_driver);
}

module_init(wf_max6690_sensor_init);
module_exit(wf_max6690_sensor_exit);

MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>");
MODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control");
MODULE_LICENSE("GPL");
Loading