Skip to content

Commit

Permalink
[PATCH] powerpc: Thermal control for dual core G5s
Browse files Browse the repository at this point in the history
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: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Linus Torvalds <torvalds@osdl.org>
  • Loading branch information
Benjamin Herrenschmidt authored and Linus Torvalds committed Feb 8, 2006
1 parent 746f956 commit ac171c4
Show file tree
Hide file tree
Showing 14 changed files with 1,479 additions and 199 deletions.
8 changes: 8 additions & 0 deletions drivers/macintosh/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
5 changes: 5 additions & 0 deletions drivers/macintosh/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 3 additions & 0 deletions drivers/macintosh/windfarm.h
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -39,6 +40,7 @@ struct wf_control {
char *name;
int type;
struct kref ref;
struct device_attribute attr;
};

#define WF_CONTROL_TYPE_GENERIC 0
Expand Down Expand Up @@ -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 */
Expand Down
69 changes: 65 additions & 4 deletions drivers/macintosh/windfarm_core.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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");
Expand Down
169 changes: 169 additions & 0 deletions drivers/macintosh/windfarm_max6690_sensor.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
/*
* 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");
8 changes: 4 additions & 4 deletions drivers/macintosh/windfarm_pid.c
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,8 @@ EXPORT_SYMBOL_GPL(wf_cpu_pid_init);

s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp)
{
s64 error, integ, deriv, prop;
s32 target, sval, adj;
s64 integ, deriv, prop;
s32 error, target, sval, adj;
int i, hlen = st->param.history_len;

/* Calculate error term */
Expand Down Expand Up @@ -117,7 +117,7 @@ s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp)
integ += st->errors[(st->index + hlen - i) % hlen];
integ *= st->param.interval;
integ *= st->param.gr;
sval = st->param.tmax - ((integ >> 20) & 0xffffffff);
sval = st->param.tmax - (s32)(integ >> 20);
adj = min(st->param.ttarget, sval);

DBG("integ: %lx, sval: %lx, adj: %lx\n", integ, sval, adj);
Expand All @@ -129,7 +129,7 @@ s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp)
deriv *= st->param.gd;

/* Calculate proportional term */
prop = (new_temp - adj);
prop = st->last_delta = (new_temp - adj);
prop *= st->param.gp;

DBG("deriv: %lx, prop: %lx\n", deriv, prop);
Expand Down
1 change: 1 addition & 0 deletions drivers/macintosh/windfarm_pid.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ struct wf_cpu_pid_state {
int index; /* index of current power */
int tindex; /* index of current temp */
s32 target; /* current target value */
s32 last_delta; /* last Tactual - Ttarget */
s32 powers[WF_PID_MAX_HISTORY]; /* power history buffer */
s32 errors[WF_PID_MAX_HISTORY]; /* error history buffer */
s32 temps[2]; /* temp. history buffer */
Expand Down
Loading

0 comments on commit ac171c4

Please sign in to comment.