diff options
-rw-r--r-- | drivers/macintosh/Kconfig | 8 | ||||
-rw-r--r-- | drivers/macintosh/Makefile | 5 | ||||
-rw-r--r-- | drivers/macintosh/windfarm.h | 3 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_core.c | 69 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_max6690_sensor.c | 169 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_pid.c | 8 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_pid.h | 1 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_pm112.c | 698 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_pm81.c | 87 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_pm91.c | 95 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_smu_controls.c | 69 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_smu_sat.c | 418 | ||||
-rw-r--r-- | drivers/macintosh/windfarm_smu_sensors.c | 43 | ||||
-rw-r--r-- | include/asm-powerpc/smu.h | 5 |
14 files changed, 1479 insertions, 199 deletions
diff --git a/drivers/macintosh/Kconfig b/drivers/macintosh/Kconfig index 7d4a0ac28c06..b11cd31d8d27 100644 --- a/drivers/macintosh/Kconfig +++ b/drivers/macintosh/Kconfig | |||
@@ -187,6 +187,14 @@ config WINDFARM_PM91 | |||
187 | This driver provides thermal control for the PowerMac9,1 | 187 | This driver provides thermal control for the PowerMac9,1 |
188 | which is the recent (SMU based) single CPU desktop G5 | 188 | which is the recent (SMU based) single CPU desktop G5 |
189 | 189 | ||
190 | config WINDFARM_PM112 | ||
191 | tristate "Support for thermal management on PowerMac11,2" | ||
192 | depends on WINDFARM && I2C && PMAC_SMU | ||
193 | select I2C_PMAC_SMU | ||
194 | help | ||
195 | This driver provides thermal control for the PowerMac11,2 | ||
196 | which are the recent dual and quad G5 machines using the | ||
197 | 970MP dual-core processor. | ||
190 | 198 | ||
191 | config ANSLCD | 199 | config ANSLCD |
192 | tristate "Support for ANS LCD display" | 200 | tristate "Support for ANS LCD display" |
diff --git a/drivers/macintosh/Makefile b/drivers/macintosh/Makefile index f4657aa81fb0..6081acdea404 100644 --- a/drivers/macintosh/Makefile +++ b/drivers/macintosh/Makefile | |||
@@ -35,3 +35,8 @@ obj-$(CONFIG_WINDFARM_PM91) += windfarm_smu_controls.o \ | |||
35 | windfarm_smu_sensors.o \ | 35 | windfarm_smu_sensors.o \ |
36 | windfarm_lm75_sensor.o windfarm_pid.o \ | 36 | windfarm_lm75_sensor.o windfarm_pid.o \ |
37 | windfarm_cpufreq_clamp.o windfarm_pm91.o | 37 | windfarm_cpufreq_clamp.o windfarm_pm91.o |
38 | obj-$(CONFIG_WINDFARM_PM112) += windfarm_pm112.o windfarm_smu_sat.o \ | ||
39 | windfarm_smu_controls.o \ | ||
40 | windfarm_smu_sensors.o \ | ||
41 | windfarm_max6690_sensor.o \ | ||
42 | windfarm_lm75_sensor.o windfarm_pid.o | ||
diff --git a/drivers/macintosh/windfarm.h b/drivers/macintosh/windfarm.h index 3f0cb0312ea3..7a2482cc26a7 100644 --- a/drivers/macintosh/windfarm.h +++ b/drivers/macintosh/windfarm.h | |||
@@ -14,6 +14,7 @@ | |||
14 | #include <linux/list.h> | 14 | #include <linux/list.h> |
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/notifier.h> | 16 | #include <linux/notifier.h> |
17 | #include <linux/device.h> | ||
17 | 18 | ||
18 | /* Display a 16.16 fixed point value */ | 19 | /* Display a 16.16 fixed point value */ |
19 | #define FIX32TOPRINT(f) ((f) >> 16),((((f) & 0xffff) * 1000) >> 16) | 20 | #define FIX32TOPRINT(f) ((f) >> 16),((((f) & 0xffff) * 1000) >> 16) |
@@ -39,6 +40,7 @@ struct wf_control { | |||
39 | char *name; | 40 | char *name; |
40 | int type; | 41 | int type; |
41 | struct kref ref; | 42 | struct kref ref; |
43 | struct device_attribute attr; | ||
42 | }; | 44 | }; |
43 | 45 | ||
44 | #define WF_CONTROL_TYPE_GENERIC 0 | 46 | #define WF_CONTROL_TYPE_GENERIC 0 |
@@ -87,6 +89,7 @@ struct wf_sensor { | |||
87 | struct wf_sensor_ops *ops; | 89 | struct wf_sensor_ops *ops; |
88 | char *name; | 90 | char *name; |
89 | struct kref ref; | 91 | struct kref ref; |
92 | struct device_attribute attr; | ||
90 | }; | 93 | }; |
91 | 94 | ||
92 | /* Same lifetime rules as controls */ | 95 | /* Same lifetime rules as controls */ |
diff --git a/drivers/macintosh/windfarm_core.c b/drivers/macintosh/windfarm_core.c index 32d466441ac2..bb8d5efe19bf 100644 --- a/drivers/macintosh/windfarm_core.c +++ b/drivers/macintosh/windfarm_core.c | |||
@@ -56,6 +56,10 @@ static unsigned int wf_overtemp; | |||
56 | static unsigned int wf_overtemp_counter; | 56 | static unsigned int wf_overtemp_counter; |
57 | struct task_struct *wf_thread; | 57 | struct task_struct *wf_thread; |
58 | 58 | ||
59 | static struct platform_device wf_platform_device = { | ||
60 | .name = "windfarm", | ||
61 | }; | ||
62 | |||
59 | /* | 63 | /* |
60 | * Utilities & tick thread | 64 | * Utilities & tick thread |
61 | */ | 65 | */ |
@@ -157,6 +161,40 @@ static void wf_control_release(struct kref *kref) | |||
157 | kfree(ct); | 161 | kfree(ct); |
158 | } | 162 | } |
159 | 163 | ||
164 | static ssize_t wf_show_control(struct device *dev, | ||
165 | struct device_attribute *attr, char *buf) | ||
166 | { | ||
167 | struct wf_control *ctrl = container_of(attr, struct wf_control, attr); | ||
168 | s32 val = 0; | ||
169 | int err; | ||
170 | |||
171 | err = ctrl->ops->get_value(ctrl, &val); | ||
172 | if (err < 0) | ||
173 | return err; | ||
174 | return sprintf(buf, "%d\n", val); | ||
175 | } | ||
176 | |||
177 | /* This is really only for debugging... */ | ||
178 | static ssize_t wf_store_control(struct device *dev, | ||
179 | struct device_attribute *attr, | ||
180 | const char *buf, size_t count) | ||
181 | { | ||
182 | struct wf_control *ctrl = container_of(attr, struct wf_control, attr); | ||
183 | int val; | ||
184 | int err; | ||
185 | char *endp; | ||
186 | |||
187 | val = simple_strtoul(buf, &endp, 0); | ||
188 | while (endp < buf + count && (*endp == ' ' || *endp == '\n')) | ||
189 | ++endp; | ||
190 | if (endp - buf < count) | ||
191 | return -EINVAL; | ||
192 | err = ctrl->ops->set_value(ctrl, val); | ||
193 | if (err < 0) | ||
194 | return err; | ||
195 | return count; | ||
196 | } | ||
197 | |||
160 | int wf_register_control(struct wf_control *new_ct) | 198 | int wf_register_control(struct wf_control *new_ct) |
161 | { | 199 | { |
162 | struct wf_control *ct; | 200 | struct wf_control *ct; |
@@ -173,6 +211,13 @@ int wf_register_control(struct wf_control *new_ct) | |||
173 | kref_init(&new_ct->ref); | 211 | kref_init(&new_ct->ref); |
174 | list_add(&new_ct->link, &wf_controls); | 212 | list_add(&new_ct->link, &wf_controls); |
175 | 213 | ||
214 | new_ct->attr.attr.name = new_ct->name; | ||
215 | new_ct->attr.attr.owner = THIS_MODULE; | ||
216 | new_ct->attr.attr.mode = 0644; | ||
217 | new_ct->attr.show = wf_show_control; | ||
218 | new_ct->attr.store = wf_store_control; | ||
219 | device_create_file(&wf_platform_device.dev, &new_ct->attr); | ||
220 | |||
176 | DBG("wf: Registered control %s\n", new_ct->name); | 221 | DBG("wf: Registered control %s\n", new_ct->name); |
177 | 222 | ||
178 | wf_notify(WF_EVENT_NEW_CONTROL, new_ct); | 223 | wf_notify(WF_EVENT_NEW_CONTROL, new_ct); |
@@ -247,6 +292,19 @@ static void wf_sensor_release(struct kref *kref) | |||
247 | kfree(sr); | 292 | kfree(sr); |
248 | } | 293 | } |
249 | 294 | ||
295 | static ssize_t wf_show_sensor(struct device *dev, | ||
296 | struct device_attribute *attr, char *buf) | ||
297 | { | ||
298 | struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr); | ||
299 | s32 val = 0; | ||
300 | int err; | ||
301 | |||
302 | err = sens->ops->get_value(sens, &val); | ||
303 | if (err < 0) | ||
304 | return err; | ||
305 | return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val)); | ||
306 | } | ||
307 | |||
250 | int wf_register_sensor(struct wf_sensor *new_sr) | 308 | int wf_register_sensor(struct wf_sensor *new_sr) |
251 | { | 309 | { |
252 | struct wf_sensor *sr; | 310 | struct wf_sensor *sr; |
@@ -263,6 +321,13 @@ int wf_register_sensor(struct wf_sensor *new_sr) | |||
263 | kref_init(&new_sr->ref); | 321 | kref_init(&new_sr->ref); |
264 | list_add(&new_sr->link, &wf_sensors); | 322 | list_add(&new_sr->link, &wf_sensors); |
265 | 323 | ||
324 | new_sr->attr.attr.name = new_sr->name; | ||
325 | new_sr->attr.attr.owner = THIS_MODULE; | ||
326 | new_sr->attr.attr.mode = 0444; | ||
327 | new_sr->attr.show = wf_show_sensor; | ||
328 | new_sr->attr.store = NULL; | ||
329 | device_create_file(&wf_platform_device.dev, &new_sr->attr); | ||
330 | |||
266 | DBG("wf: Registered sensor %s\n", new_sr->name); | 331 | DBG("wf: Registered sensor %s\n", new_sr->name); |
267 | 332 | ||
268 | wf_notify(WF_EVENT_NEW_SENSOR, new_sr); | 333 | wf_notify(WF_EVENT_NEW_SENSOR, new_sr); |
@@ -396,10 +461,6 @@ int wf_is_overtemp(void) | |||
396 | } | 461 | } |
397 | EXPORT_SYMBOL_GPL(wf_is_overtemp); | 462 | EXPORT_SYMBOL_GPL(wf_is_overtemp); |
398 | 463 | ||
399 | static struct platform_device wf_platform_device = { | ||
400 | .name = "windfarm", | ||
401 | }; | ||
402 | |||
403 | static int __init windfarm_core_init(void) | 464 | static int __init windfarm_core_init(void) |
404 | { | 465 | { |
405 | DBG("wf: core loaded\n"); | 466 | DBG("wf: core loaded\n"); |
diff --git a/drivers/macintosh/windfarm_max6690_sensor.c b/drivers/macintosh/windfarm_max6690_sensor.c new file mode 100644 index 000000000000..5b9ad6ca7cba --- /dev/null +++ b/drivers/macintosh/windfarm_max6690_sensor.c | |||
@@ -0,0 +1,169 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. MAX6690 sensor. | ||
3 | * | ||
4 | * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org> | ||
5 | * | ||
6 | * Use and redistribute under the terms of the GNU GPL v2. | ||
7 | */ | ||
8 | #include <linux/types.h> | ||
9 | #include <linux/errno.h> | ||
10 | #include <linux/kernel.h> | ||
11 | #include <linux/init.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/i2c.h> | ||
14 | #include <linux/i2c-dev.h> | ||
15 | #include <asm/prom.h> | ||
16 | #include <asm/pmac_low_i2c.h> | ||
17 | |||
18 | #include "windfarm.h" | ||
19 | |||
20 | #define VERSION "0.1" | ||
21 | |||
22 | /* This currently only exports the external temperature sensor, | ||
23 | since that's all the control loops need. */ | ||
24 | |||
25 | /* Some MAX6690 register numbers */ | ||
26 | #define MAX6690_INTERNAL_TEMP 0 | ||
27 | #define MAX6690_EXTERNAL_TEMP 1 | ||
28 | |||
29 | struct wf_6690_sensor { | ||
30 | struct i2c_client i2c; | ||
31 | struct wf_sensor sens; | ||
32 | }; | ||
33 | |||
34 | #define wf_to_6690(x) container_of((x), struct wf_6690_sensor, sens) | ||
35 | #define i2c_to_6690(x) container_of((x), struct wf_6690_sensor, i2c) | ||
36 | |||
37 | static int wf_max6690_attach(struct i2c_adapter *adapter); | ||
38 | static int wf_max6690_detach(struct i2c_client *client); | ||
39 | |||
40 | static struct i2c_driver wf_max6690_driver = { | ||
41 | .driver = { | ||
42 | .name = "wf_max6690", | ||
43 | }, | ||
44 | .attach_adapter = wf_max6690_attach, | ||
45 | .detach_client = wf_max6690_detach, | ||
46 | }; | ||
47 | |||
48 | static int wf_max6690_get(struct wf_sensor *sr, s32 *value) | ||
49 | { | ||
50 | struct wf_6690_sensor *max = wf_to_6690(sr); | ||
51 | s32 data; | ||
52 | |||
53 | if (max->i2c.adapter == NULL) | ||
54 | return -ENODEV; | ||
55 | |||
56 | /* chip gets initialized by firmware */ | ||
57 | data = i2c_smbus_read_byte_data(&max->i2c, MAX6690_EXTERNAL_TEMP); | ||
58 | if (data < 0) | ||
59 | return data; | ||
60 | *value = data << 16; | ||
61 | return 0; | ||
62 | } | ||
63 | |||
64 | static void wf_max6690_release(struct wf_sensor *sr) | ||
65 | { | ||
66 | struct wf_6690_sensor *max = wf_to_6690(sr); | ||
67 | |||
68 | if (max->i2c.adapter) { | ||
69 | i2c_detach_client(&max->i2c); | ||
70 | max->i2c.adapter = NULL; | ||
71 | } | ||
72 | kfree(max); | ||
73 | } | ||
74 | |||
75 | static struct wf_sensor_ops wf_max6690_ops = { | ||
76 | .get_value = wf_max6690_get, | ||
77 | .release = wf_max6690_release, | ||
78 | .owner = THIS_MODULE, | ||
79 | }; | ||
80 | |||
81 | static void wf_max6690_create(struct i2c_adapter *adapter, u8 addr) | ||
82 | { | ||
83 | struct wf_6690_sensor *max; | ||
84 | char *name = "u4-temp"; | ||
85 | |||
86 | max = kzalloc(sizeof(struct wf_6690_sensor), GFP_KERNEL); | ||
87 | if (max == NULL) { | ||
88 | printk(KERN_ERR "windfarm: Couldn't create MAX6690 sensor %s: " | ||
89 | "no memory\n", name); | ||
90 | return; | ||
91 | } | ||
92 | |||
93 | max->sens.ops = &wf_max6690_ops; | ||
94 | max->sens.name = name; | ||
95 | max->i2c.addr = addr >> 1; | ||
96 | max->i2c.adapter = adapter; | ||
97 | max->i2c.driver = &wf_max6690_driver; | ||
98 | strncpy(max->i2c.name, name, I2C_NAME_SIZE-1); | ||
99 | |||
100 | if (i2c_attach_client(&max->i2c)) { | ||
101 | printk(KERN_ERR "windfarm: failed to attach MAX6690 sensor\n"); | ||
102 | goto fail; | ||
103 | } | ||
104 | |||
105 | if (wf_register_sensor(&max->sens)) { | ||
106 | i2c_detach_client(&max->i2c); | ||
107 | goto fail; | ||
108 | } | ||
109 | |||
110 | return; | ||
111 | |||
112 | fail: | ||
113 | kfree(max); | ||
114 | } | ||
115 | |||
116 | static int wf_max6690_attach(struct i2c_adapter *adapter) | ||
117 | { | ||
118 | struct device_node *busnode, *dev = NULL; | ||
119 | struct pmac_i2c_bus *bus; | ||
120 | const char *loc; | ||
121 | u32 *reg; | ||
122 | |||
123 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
124 | if (bus == NULL) | ||
125 | return -ENODEV; | ||
126 | busnode = pmac_i2c_get_bus_node(bus); | ||
127 | |||
128 | while ((dev = of_get_next_child(busnode, dev)) != NULL) { | ||
129 | if (!device_is_compatible(dev, "max6690")) | ||
130 | continue; | ||
131 | loc = get_property(dev, "hwsensor-location", NULL); | ||
132 | reg = (u32 *) get_property(dev, "reg", NULL); | ||
133 | if (!loc || !reg) | ||
134 | continue; | ||
135 | printk("found max6690, loc=%s reg=%x\n", loc, *reg); | ||
136 | if (strcmp(loc, "BACKSIDE")) | ||
137 | continue; | ||
138 | wf_max6690_create(adapter, *reg); | ||
139 | } | ||
140 | |||
141 | return 0; | ||
142 | } | ||
143 | |||
144 | static int wf_max6690_detach(struct i2c_client *client) | ||
145 | { | ||
146 | struct wf_6690_sensor *max = i2c_to_6690(client); | ||
147 | |||
148 | max->i2c.adapter = NULL; | ||
149 | wf_unregister_sensor(&max->sens); | ||
150 | |||
151 | return 0; | ||
152 | } | ||
153 | |||
154 | static int __init wf_max6690_sensor_init(void) | ||
155 | { | ||
156 | return i2c_add_driver(&wf_max6690_driver); | ||
157 | } | ||
158 | |||
159 | static void __exit wf_max6690_sensor_exit(void) | ||
160 | { | ||
161 | i2c_del_driver(&wf_max6690_driver); | ||
162 | } | ||
163 | |||
164 | module_init(wf_max6690_sensor_init); | ||
165 | module_exit(wf_max6690_sensor_exit); | ||
166 | |||
167 | MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); | ||
168 | MODULE_DESCRIPTION("MAX6690 sensor objects for PowerMac thermal control"); | ||
169 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/macintosh/windfarm_pid.c b/drivers/macintosh/windfarm_pid.c index 2e803b368757..0842432e27ad 100644 --- a/drivers/macintosh/windfarm_pid.c +++ b/drivers/macintosh/windfarm_pid.c | |||
@@ -88,8 +88,8 @@ EXPORT_SYMBOL_GPL(wf_cpu_pid_init); | |||
88 | 88 | ||
89 | s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp) | 89 | s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp) |
90 | { | 90 | { |
91 | s64 error, integ, deriv, prop; | 91 | s64 integ, deriv, prop; |
92 | s32 target, sval, adj; | 92 | s32 error, target, sval, adj; |
93 | int i, hlen = st->param.history_len; | 93 | int i, hlen = st->param.history_len; |
94 | 94 | ||
95 | /* Calculate error term */ | 95 | /* Calculate error term */ |
@@ -117,7 +117,7 @@ s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp) | |||
117 | integ += st->errors[(st->index + hlen - i) % hlen]; | 117 | integ += st->errors[(st->index + hlen - i) % hlen]; |
118 | integ *= st->param.interval; | 118 | integ *= st->param.interval; |
119 | integ *= st->param.gr; | 119 | integ *= st->param.gr; |
120 | sval = st->param.tmax - ((integ >> 20) & 0xffffffff); | 120 | sval = st->param.tmax - (s32)(integ >> 20); |
121 | adj = min(st->param.ttarget, sval); | 121 | adj = min(st->param.ttarget, sval); |
122 | 122 | ||
123 | DBG("integ: %lx, sval: %lx, adj: %lx\n", integ, sval, adj); | 123 | DBG("integ: %lx, sval: %lx, adj: %lx\n", integ, sval, adj); |
@@ -129,7 +129,7 @@ s32 wf_cpu_pid_run(struct wf_cpu_pid_state *st, s32 new_power, s32 new_temp) | |||
129 | deriv *= st->param.gd; | 129 | deriv *= st->param.gd; |
130 | 130 | ||
131 | /* Calculate proportional term */ | 131 | /* Calculate proportional term */ |
132 | prop = (new_temp - adj); | 132 | prop = st->last_delta = (new_temp - adj); |
133 | prop *= st->param.gp; | 133 | prop *= st->param.gp; |
134 | 134 | ||
135 | DBG("deriv: %lx, prop: %lx\n", deriv, prop); | 135 | DBG("deriv: %lx, prop: %lx\n", deriv, prop); |
diff --git a/drivers/macintosh/windfarm_pid.h b/drivers/macintosh/windfarm_pid.h index a364c2a2499c..bbccc22d42b8 100644 --- a/drivers/macintosh/windfarm_pid.h +++ b/drivers/macintosh/windfarm_pid.h | |||
@@ -72,6 +72,7 @@ struct wf_cpu_pid_state { | |||
72 | int index; /* index of current power */ | 72 | int index; /* index of current power */ |
73 | int tindex; /* index of current temp */ | 73 | int tindex; /* index of current temp */ |
74 | s32 target; /* current target value */ | 74 | s32 target; /* current target value */ |
75 | s32 last_delta; /* last Tactual - Ttarget */ | ||
75 | s32 powers[WF_PID_MAX_HISTORY]; /* power history buffer */ | 76 | s32 powers[WF_PID_MAX_HISTORY]; /* power history buffer */ |
76 | s32 errors[WF_PID_MAX_HISTORY]; /* error history buffer */ | 77 | s32 errors[WF_PID_MAX_HISTORY]; /* error history buffer */ |
77 | s32 temps[2]; /* temp. history buffer */ | 78 | s32 temps[2]; /* temp. history buffer */ |
diff --git a/drivers/macintosh/windfarm_pm112.c b/drivers/macintosh/windfarm_pm112.c new file mode 100644 index 000000000000..c2a4e689c784 --- /dev/null +++ b/drivers/macintosh/windfarm_pm112.c | |||
@@ -0,0 +1,698 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. | ||
3 | * Control loops for machines with SMU and PPC970MP processors. | ||
4 | * | ||
5 | * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org> | ||
6 | * Copyright (C) 2006 Benjamin Herrenschmidt, IBM Corp. | ||
7 | * | ||
8 | * Use and redistribute under the terms of the GNU GPL v2. | ||
9 | */ | ||
10 | #include <linux/types.h> | ||
11 | #include <linux/errno.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/device.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/reboot.h> | ||
16 | #include <asm/prom.h> | ||
17 | #include <asm/smu.h> | ||
18 | |||
19 | #include "windfarm.h" | ||
20 | #include "windfarm_pid.h" | ||
21 | |||
22 | #define VERSION "0.2" | ||
23 | |||
24 | #define DEBUG | ||
25 | #undef LOTSA_DEBUG | ||
26 | |||
27 | #ifdef DEBUG | ||
28 | #define DBG(args...) printk(args) | ||
29 | #else | ||
30 | #define DBG(args...) do { } while(0) | ||
31 | #endif | ||
32 | |||
33 | #ifdef LOTSA_DEBUG | ||
34 | #define DBG_LOTS(args...) printk(args) | ||
35 | #else | ||
36 | #define DBG_LOTS(args...) do { } while(0) | ||
37 | #endif | ||
38 | |||
39 | /* define this to force CPU overtemp to 60 degree, useful for testing | ||
40 | * the overtemp code | ||
41 | */ | ||
42 | #undef HACKED_OVERTEMP | ||
43 | |||
44 | /* We currently only handle 2 chips, 4 cores... */ | ||
45 | #define NR_CHIPS 2 | ||
46 | #define NR_CORES 4 | ||
47 | #define NR_CPU_FANS 3 * NR_CHIPS | ||
48 | |||
49 | /* Controls and sensors */ | ||
50 | static struct wf_sensor *sens_cpu_temp[NR_CORES]; | ||
51 | static struct wf_sensor *sens_cpu_power[NR_CORES]; | ||
52 | static struct wf_sensor *hd_temp; | ||
53 | static struct wf_sensor *slots_power; | ||
54 | static struct wf_sensor *u4_temp; | ||
55 | |||
56 | static struct wf_control *cpu_fans[NR_CPU_FANS]; | ||
57 | static char *cpu_fan_names[NR_CPU_FANS] = { | ||
58 | "cpu-rear-fan-0", | ||
59 | "cpu-rear-fan-1", | ||
60 | "cpu-front-fan-0", | ||
61 | "cpu-front-fan-1", | ||
62 | "cpu-pump-0", | ||
63 | "cpu-pump-1", | ||
64 | }; | ||
65 | static struct wf_control *cpufreq_clamp; | ||
66 | |||
67 | /* Second pump isn't required (and isn't actually present) */ | ||
68 | #define CPU_FANS_REQD (NR_CPU_FANS - 2) | ||
69 | #define FIRST_PUMP 4 | ||
70 | #define LAST_PUMP 5 | ||
71 | |||
72 | /* We keep a temperature history for average calculation of 180s */ | ||
73 | #define CPU_TEMP_HIST_SIZE 180 | ||
74 | |||
75 | /* Scale factor for fan speed, *100 */ | ||
76 | static int cpu_fan_scale[NR_CPU_FANS] = { | ||
77 | 100, | ||
78 | 100, | ||
79 | 97, /* inlet fans run at 97% of exhaust fan */ | ||
80 | 97, | ||
81 | 100, /* updated later */ | ||
82 | 100, /* updated later */ | ||
83 | }; | ||
84 | |||
85 | static struct wf_control *backside_fan; | ||
86 | static struct wf_control *slots_fan; | ||
87 | static struct wf_control *drive_bay_fan; | ||
88 | |||
89 | /* PID loop state */ | ||
90 | static struct wf_cpu_pid_state cpu_pid[NR_CORES]; | ||
91 | static u32 cpu_thist[CPU_TEMP_HIST_SIZE]; | ||
92 | static int cpu_thist_pt; | ||
93 | static s64 cpu_thist_total; | ||
94 | static s32 cpu_all_tmax = 100 << 16; | ||
95 | static int cpu_last_target; | ||
96 | static struct wf_pid_state backside_pid; | ||
97 | static int backside_tick; | ||
98 | static struct wf_pid_state slots_pid; | ||
99 | static int slots_started; | ||
100 | static struct wf_pid_state drive_bay_pid; | ||
101 | static int drive_bay_tick; | ||
102 | |||
103 | static int nr_cores; | ||
104 | static int have_all_controls; | ||
105 | static int have_all_sensors; | ||
106 | static int started; | ||
107 | |||
108 | static int failure_state; | ||
109 | #define FAILURE_SENSOR 1 | ||
110 | #define FAILURE_FAN 2 | ||
111 | #define FAILURE_PERM 4 | ||
112 | #define FAILURE_LOW_OVERTEMP 8 | ||
113 | #define FAILURE_HIGH_OVERTEMP 16 | ||
114 | |||
115 | /* Overtemp values */ | ||
116 | #define LOW_OVER_AVERAGE 0 | ||
117 | #define LOW_OVER_IMMEDIATE (10 << 16) | ||
118 | #define LOW_OVER_CLEAR ((-10) << 16) | ||
119 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
120 | #define HIGH_OVER_AVERAGE (10 << 16) | ||
121 | #define HIGH_OVER_IMMEDIATE (14 << 16) | ||
122 | |||
123 | |||
124 | /* Implementation... */ | ||
125 | static int create_cpu_loop(int cpu) | ||
126 | { | ||
127 | int chip = cpu / 2; | ||
128 | int core = cpu & 1; | ||
129 | struct smu_sdbp_header *hdr; | ||
130 | struct smu_sdbp_cpupiddata *piddata; | ||
131 | struct wf_cpu_pid_param pid; | ||
132 | struct wf_control *main_fan = cpu_fans[0]; | ||
133 | s32 tmax; | ||
134 | int fmin; | ||
135 | |||
136 | /* Get PID params from the appropriate SAT */ | ||
137 | hdr = smu_sat_get_sdb_partition(chip, 0xC8 + core, NULL); | ||
138 | if (hdr == NULL) { | ||
139 | printk(KERN_WARNING"windfarm: can't get CPU PID fan config\n"); | ||
140 | return -EINVAL; | ||
141 | } | ||
142 | piddata = (struct smu_sdbp_cpupiddata *)&hdr[1]; | ||
143 | |||
144 | /* Get FVT params to get Tmax; if not found, assume default */ | ||
145 | hdr = smu_sat_get_sdb_partition(chip, 0xC4 + core, NULL); | ||
146 | if (hdr) { | ||
147 | struct smu_sdbp_fvt *fvt = (struct smu_sdbp_fvt *)&hdr[1]; | ||
148 | tmax = fvt->maxtemp << 16; | ||
149 | } else | ||
150 | tmax = 95 << 16; /* default to 95 degrees C */ | ||
151 | |||
152 | /* We keep a global tmax for overtemp calculations */ | ||
153 | if (tmax < cpu_all_tmax) | ||
154 | cpu_all_tmax = tmax; | ||
155 | |||
156 | /* | ||
157 | * Darwin has a minimum fan speed of 1000 rpm for the 4-way and | ||
158 | * 515 for the 2-way. That appears to be overkill, so for now, | ||
159 | * impose a minimum of 750 or 515. | ||
160 | */ | ||
161 | fmin = (nr_cores > 2) ? 750 : 515; | ||
162 | |||
163 | /* Initialize PID loop */ | ||
164 | pid.interval = 1; /* seconds */ | ||
165 | pid.history_len = piddata->history_len; | ||
166 | pid.gd = piddata->gd; | ||
167 | pid.gp = piddata->gp; | ||
168 | pid.gr = piddata->gr / piddata->history_len; | ||
169 | pid.pmaxadj = (piddata->max_power << 16) - (piddata->power_adj << 8); | ||
170 | pid.ttarget = tmax - (piddata->target_temp_delta << 16); | ||
171 | pid.tmax = tmax; | ||
172 | pid.min = main_fan->ops->get_min(main_fan); | ||
173 | pid.max = main_fan->ops->get_max(main_fan); | ||
174 | if (pid.min < fmin) | ||
175 | pid.min = fmin; | ||
176 | |||
177 | wf_cpu_pid_init(&cpu_pid[cpu], &pid); | ||
178 | return 0; | ||
179 | } | ||
180 | |||
181 | static void cpu_max_all_fans(void) | ||
182 | { | ||
183 | int i; | ||
184 | |||
185 | /* We max all CPU fans in case of a sensor error. We also do the | ||
186 | * cpufreq clamping now, even if it's supposedly done later by the | ||
187 | * generic code anyway, we do it earlier here to react faster | ||
188 | */ | ||
189 | if (cpufreq_clamp) | ||
190 | wf_control_set_max(cpufreq_clamp); | ||
191 | for (i = 0; i < NR_CPU_FANS; ++i) | ||
192 | if (cpu_fans[i]) | ||
193 | wf_control_set_max(cpu_fans[i]); | ||
194 | } | ||
195 | |||
196 | static int cpu_check_overtemp(s32 temp) | ||
197 | { | ||
198 | int new_state = 0; | ||
199 | s32 t_avg, t_old; | ||
200 | |||
201 | /* First check for immediate overtemps */ | ||
202 | if (temp >= (cpu_all_tmax + LOW_OVER_IMMEDIATE)) { | ||
203 | new_state |= FAILURE_LOW_OVERTEMP; | ||
204 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
205 | printk(KERN_ERR "windfarm: Overtemp due to immediate CPU" | ||
206 | " temperature !\n"); | ||
207 | } | ||
208 | if (temp >= (cpu_all_tmax + HIGH_OVER_IMMEDIATE)) { | ||
209 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
210 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
211 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
212 | " immediate CPU temperature !\n"); | ||
213 | } | ||
214 | |||
215 | /* We calculate a history of max temperatures and use that for the | ||
216 | * overtemp management | ||
217 | */ | ||
218 | t_old = cpu_thist[cpu_thist_pt]; | ||
219 | cpu_thist[cpu_thist_pt] = temp; | ||
220 | cpu_thist_pt = (cpu_thist_pt + 1) % CPU_TEMP_HIST_SIZE; | ||
221 | cpu_thist_total -= t_old; | ||
222 | cpu_thist_total += temp; | ||
223 | t_avg = cpu_thist_total / CPU_TEMP_HIST_SIZE; | ||
224 | |||
225 | DBG_LOTS("t_avg = %d.%03d (out: %d.%03d, in: %d.%03d)\n", | ||
226 | FIX32TOPRINT(t_avg), FIX32TOPRINT(t_old), FIX32TOPRINT(temp)); | ||
227 | |||
228 | /* Now check for average overtemps */ | ||
229 | if (t_avg >= (cpu_all_tmax + LOW_OVER_AVERAGE)) { | ||
230 | new_state |= FAILURE_LOW_OVERTEMP; | ||
231 | if ((failure_state & FAILURE_LOW_OVERTEMP) == 0) | ||
232 | printk(KERN_ERR "windfarm: Overtemp due to average CPU" | ||
233 | " temperature !\n"); | ||
234 | } | ||
235 | if (t_avg >= (cpu_all_tmax + HIGH_OVER_AVERAGE)) { | ||
236 | new_state |= FAILURE_HIGH_OVERTEMP; | ||
237 | if ((failure_state & FAILURE_HIGH_OVERTEMP) == 0) | ||
238 | printk(KERN_ERR "windfarm: Critical overtemp due to" | ||
239 | " average CPU temperature !\n"); | ||
240 | } | ||
241 | |||
242 | /* Now handle overtemp conditions. We don't currently use the windfarm | ||
243 | * overtemp handling core as it's not fully suited to the needs of those | ||
244 | * new machine. This will be fixed later. | ||
245 | */ | ||
246 | if (new_state) { | ||
247 | /* High overtemp -> immediate shutdown */ | ||
248 | if (new_state & FAILURE_HIGH_OVERTEMP) | ||
249 | machine_power_off(); | ||
250 | if ((failure_state & new_state) != new_state) | ||
251 | cpu_max_all_fans(); | ||
252 | failure_state |= new_state; | ||
253 | } else if ((failure_state & FAILURE_LOW_OVERTEMP) && | ||
254 | (temp < (cpu_all_tmax + LOW_OVER_CLEAR))) { | ||
255 | printk(KERN_ERR "windfarm: Overtemp condition cleared !\n"); | ||
256 | failure_state &= ~FAILURE_LOW_OVERTEMP; | ||
257 | } | ||
258 | |||
259 | return failure_state & (FAILURE_LOW_OVERTEMP | FAILURE_HIGH_OVERTEMP); | ||
260 | } | ||
261 | |||
262 | static void cpu_fans_tick(void) | ||
263 | { | ||
264 | int err, cpu; | ||
265 | s32 greatest_delta = 0; | ||
266 | s32 temp, power, t_max = 0; | ||
267 | int i, t, target = 0; | ||
268 | struct wf_sensor *sr; | ||
269 | struct wf_control *ct; | ||
270 | struct wf_cpu_pid_state *sp; | ||
271 | |||
272 | DBG_LOTS(KERN_DEBUG); | ||
273 | for (cpu = 0; cpu < nr_cores; ++cpu) { | ||
274 | /* Get CPU core temperature */ | ||
275 | sr = sens_cpu_temp[cpu]; | ||
276 | err = sr->ops->get_value(sr, &temp); | ||
277 | if (err) { | ||
278 | DBG("\n"); | ||
279 | printk(KERN_WARNING "windfarm: CPU %d temperature " | ||
280 | "sensor error %d\n", cpu, err); | ||
281 | failure_state |= FAILURE_SENSOR; | ||
282 | cpu_max_all_fans(); | ||
283 | return; | ||
284 | } | ||
285 | |||
286 | /* Keep track of highest temp */ | ||
287 | t_max = max(t_max, temp); | ||
288 | |||
289 | /* Get CPU power */ | ||
290 | sr = sens_cpu_power[cpu]; | ||
291 | err = sr->ops->get_value(sr, &power); | ||
292 | if (err) { | ||
293 | DBG("\n"); | ||
294 | printk(KERN_WARNING "windfarm: CPU %d power " | ||
295 | "sensor error %d\n", cpu, err); | ||
296 | failure_state |= FAILURE_SENSOR; | ||
297 | cpu_max_all_fans(); | ||
298 | return; | ||
299 | } | ||
300 | |||
301 | /* Run PID */ | ||
302 | sp = &cpu_pid[cpu]; | ||
303 | t = wf_cpu_pid_run(sp, power, temp); | ||
304 | |||
305 | if (cpu == 0 || sp->last_delta > greatest_delta) { | ||
306 | greatest_delta = sp->last_delta; | ||
307 | target = t; | ||
308 | } | ||
309 | DBG_LOTS("[%d] P=%d.%.3d T=%d.%.3d ", | ||
310 | cpu, FIX32TOPRINT(power), FIX32TOPRINT(temp)); | ||
311 | } | ||
312 | DBG_LOTS("fans = %d, t_max = %d.%03d\n", target, FIX32TOPRINT(t_max)); | ||
313 | |||
314 | /* Darwin limits decrease to 20 per iteration */ | ||
315 | if (target < (cpu_last_target - 20)) | ||
316 | target = cpu_last_target - 20; | ||
317 | cpu_last_target = target; | ||
318 | for (cpu = 0; cpu < nr_cores; ++cpu) | ||
319 | cpu_pid[cpu].target = target; | ||
320 | |||
321 | /* Handle possible overtemps */ | ||
322 | if (cpu_check_overtemp(t_max)) | ||
323 | return; | ||
324 | |||
325 | /* Set fans */ | ||
326 | for (i = 0; i < NR_CPU_FANS; ++i) { | ||
327 | ct = cpu_fans[i]; | ||
328 | if (ct == NULL) | ||
329 | continue; | ||
330 | err = ct->ops->set_value(ct, target * cpu_fan_scale[i] / 100); | ||
331 | if (err) { | ||
332 | printk(KERN_WARNING "windfarm: fan %s reports " | ||
333 | "error %d\n", ct->name, err); | ||
334 | failure_state |= FAILURE_FAN; | ||
335 | break; | ||
336 | } | ||
337 | } | ||
338 | } | ||
339 | |||
340 | /* Backside/U4 fan */ | ||
341 | static struct wf_pid_param backside_param = { | ||
342 | .interval = 5, | ||
343 | .history_len = 2, | ||
344 | .gd = 48 << 20, | ||
345 | .gp = 5 << 20, | ||
346 | .gr = 0, | ||
347 | .itarget = 64 << 16, | ||
348 | .additive = 1, | ||
349 | }; | ||
350 | |||
351 | static void backside_fan_tick(void) | ||
352 | { | ||
353 | s32 temp; | ||
354 | int speed; | ||
355 | int err; | ||
356 | |||
357 | if (!backside_fan || !u4_temp) | ||
358 | return; | ||
359 | if (!backside_tick) { | ||
360 | /* first time; initialize things */ | ||
361 | backside_param.min = backside_fan->ops->get_min(backside_fan); | ||
362 | backside_param.max = backside_fan->ops->get_max(backside_fan); | ||
363 | wf_pid_init(&backside_pid, &backside_param); | ||
364 | backside_tick = 1; | ||
365 | } | ||
366 | if (--backside_tick > 0) | ||
367 | return; | ||
368 | backside_tick = backside_pid.param.interval; | ||
369 | |||
370 | err = u4_temp->ops->get_value(u4_temp, &temp); | ||
371 | if (err) { | ||
372 | printk(KERN_WARNING "windfarm: U4 temp sensor error %d\n", | ||
373 | err); | ||
374 | failure_state |= FAILURE_SENSOR; | ||
375 | wf_control_set_max(backside_fan); | ||
376 | return; | ||
377 | } | ||
378 | speed = wf_pid_run(&backside_pid, temp); | ||
379 | DBG_LOTS("backside PID temp=%d.%.3d speed=%d\n", | ||
380 | FIX32TOPRINT(temp), speed); | ||
381 | |||
382 | err = backside_fan->ops->set_value(backside_fan, speed); | ||
383 | if (err) { | ||
384 | printk(KERN_WARNING "windfarm: backside fan error %d\n", err); | ||
385 | failure_state |= FAILURE_FAN; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | /* Drive bay fan */ | ||
390 | static struct wf_pid_param drive_bay_prm = { | ||
391 | .interval = 5, | ||
392 | .history_len = 2, | ||
393 | .gd = 30 << 20, | ||
394 | .gp = 5 << 20, | ||
395 | .gr = 0, | ||
396 | .itarget = 40 << 16, | ||
397 | .additive = 1, | ||
398 | }; | ||
399 | |||
400 | static void drive_bay_fan_tick(void) | ||
401 | { | ||
402 | s32 temp; | ||
403 | int speed; | ||
404 | int err; | ||
405 | |||
406 | if (!drive_bay_fan || !hd_temp) | ||
407 | return; | ||
408 | if (!drive_bay_tick) { | ||
409 | /* first time; initialize things */ | ||
410 | drive_bay_prm.min = drive_bay_fan->ops->get_min(drive_bay_fan); | ||
411 | drive_bay_prm.max = drive_bay_fan->ops->get_max(drive_bay_fan); | ||
412 | wf_pid_init(&drive_bay_pid, &drive_bay_prm); | ||
413 | drive_bay_tick = 1; | ||
414 | } | ||
415 | if (--drive_bay_tick > 0) | ||
416 | return; | ||
417 | drive_bay_tick = drive_bay_pid.param.interval; | ||
418 | |||
419 | err = hd_temp->ops->get_value(hd_temp, &temp); | ||
420 | if (err) { | ||
421 | printk(KERN_WARNING "windfarm: drive bay temp sensor " | ||
422 | "error %d\n", err); | ||
423 | failure_state |= FAILURE_SENSOR; | ||
424 | wf_control_set_max(drive_bay_fan); | ||
425 | return; | ||
426 | } | ||
427 | speed = wf_pid_run(&drive_bay_pid, temp); | ||
428 | DBG_LOTS("drive_bay PID temp=%d.%.3d speed=%d\n", | ||
429 | FIX32TOPRINT(temp), speed); | ||
430 | |||
431 | err = drive_bay_fan->ops->set_value(drive_bay_fan, speed); | ||
432 | if (err) { | ||
433 | printk(KERN_WARNING "windfarm: drive bay fan error %d\n", err); | ||
434 | failure_state |= FAILURE_FAN; | ||
435 | } | ||
436 | } | ||
437 | |||
438 | /* PCI slots area fan */ | ||
439 | /* This makes the fan speed proportional to the power consumed */ | ||
440 | static struct wf_pid_param slots_param = { | ||
441 | .interval = 1, | ||
442 | .history_len = 2, | ||
443 | .gd = 0, | ||
444 | .gp = 0, | ||
445 | .gr = 0x1277952, | ||
446 | .itarget = 0, | ||
447 | .min = 1560, | ||
448 | .max = 3510, | ||
449 | }; | ||
450 | |||
451 | static void slots_fan_tick(void) | ||
452 | { | ||
453 | s32 power; | ||
454 | int speed; | ||
455 | int err; | ||
456 | |||
457 | if (!slots_fan || !slots_power) | ||
458 | return; | ||
459 | if (!slots_started) { | ||
460 | /* first time; initialize things */ | ||
461 | wf_pid_init(&slots_pid, &slots_param); | ||
462 | slots_started = 1; | ||
463 | } | ||
464 | |||
465 | err = slots_power->ops->get_value(slots_power, &power); | ||
466 | if (err) { | ||
467 | printk(KERN_WARNING "windfarm: slots power sensor error %d\n", | ||
468 | err); | ||
469 | failure_state |= FAILURE_SENSOR; | ||
470 | wf_control_set_max(slots_fan); | ||
471 | return; | ||
472 | } | ||
473 | speed = wf_pid_run(&slots_pid, power); | ||
474 | DBG_LOTS("slots PID power=%d.%.3d speed=%d\n", | ||
475 | FIX32TOPRINT(power), speed); | ||
476 | |||
477 | err = slots_fan->ops->set_value(slots_fan, speed); | ||
478 | if (err) { | ||
479 | printk(KERN_WARNING "windfarm: slots fan error %d\n", err); | ||
480 | failure_state |= FAILURE_FAN; | ||
481 | } | ||
482 | } | ||
483 | |||
484 | static void set_fail_state(void) | ||
485 | { | ||
486 | int i; | ||
487 | |||
488 | if (cpufreq_clamp) | ||
489 | wf_control_set_max(cpufreq_clamp); | ||
490 | for (i = 0; i < NR_CPU_FANS; ++i) | ||
491 | if (cpu_fans[i]) | ||
492 | wf_control_set_max(cpu_fans[i]); | ||
493 | if (backside_fan) | ||
494 | wf_control_set_max(backside_fan); | ||
495 | if (slots_fan) | ||
496 | wf_control_set_max(slots_fan); | ||
497 | if (drive_bay_fan) | ||
498 | wf_control_set_max(drive_bay_fan); | ||
499 | } | ||
500 | |||
501 | static void pm112_tick(void) | ||
502 | { | ||
503 | int i, last_failure; | ||
504 | |||
505 | if (!started) { | ||
506 | started = 1; | ||
507 | for (i = 0; i < nr_cores; ++i) { | ||
508 | if (create_cpu_loop(i) < 0) { | ||
509 | failure_state = FAILURE_PERM; | ||
510 | set_fail_state(); | ||
511 | break; | ||
512 | } | ||
513 | } | ||
514 | DBG_LOTS("cpu_all_tmax=%d.%03d\n", FIX32TOPRINT(cpu_all_tmax)); | ||
515 | |||
516 | #ifdef HACKED_OVERTEMP | ||
517 | cpu_all_tmax = 60 << 16; | ||
518 | #endif | ||
519 | } | ||
520 | |||
521 | /* Permanent failure, bail out */ | ||
522 | if (failure_state & FAILURE_PERM) | ||
523 | return; | ||
524 | /* Clear all failure bits except low overtemp which will be eventually | ||
525 | * cleared by the control loop itself | ||
526 | */ | ||
527 | last_failure = failure_state; | ||
528 | failure_state &= FAILURE_LOW_OVERTEMP; | ||
529 | cpu_fans_tick(); | ||
530 | backside_fan_tick(); | ||
531 | slots_fan_tick(); | ||
532 | drive_bay_fan_tick(); | ||
533 | |||
534 | DBG_LOTS("last_failure: 0x%x, failure_state: %x\n", | ||
535 | last_failure, failure_state); | ||
536 | |||
537 | /* Check for failures. Any failure causes cpufreq clamping */ | ||
538 | if (failure_state && last_failure == 0 && cpufreq_clamp) | ||
539 | wf_control_set_max(cpufreq_clamp); | ||
540 | if (failure_state == 0 && last_failure && cpufreq_clamp) | ||
541 | wf_control_set_min(cpufreq_clamp); | ||
542 | |||
543 | /* That's it for now, we might want to deal with other failures | ||
544 | * differently in the future though | ||
545 | */ | ||
546 | } | ||
547 | |||
548 | static void pm112_new_control(struct wf_control *ct) | ||
549 | { | ||
550 | int i, max_exhaust; | ||
551 | |||
552 | if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) { | ||
553 | if (wf_get_control(ct) == 0) | ||
554 | cpufreq_clamp = ct; | ||
555 | } | ||
556 | |||
557 | for (i = 0; i < NR_CPU_FANS; ++i) { | ||
558 | if (!strcmp(ct->name, cpu_fan_names[i])) { | ||
559 | if (cpu_fans[i] == NULL && wf_get_control(ct) == 0) | ||
560 | cpu_fans[i] = ct; | ||
561 | break; | ||
562 | } | ||
563 | } | ||
564 | if (i >= NR_CPU_FANS) { | ||
565 | /* not a CPU fan, try the others */ | ||
566 | if (!strcmp(ct->name, "backside-fan")) { | ||
567 | if (backside_fan == NULL && wf_get_control(ct) == 0) | ||
568 | backside_fan = ct; | ||
569 | } else if (!strcmp(ct->name, "slots-fan")) { | ||
570 | if (slots_fan == NULL && wf_get_control(ct) == 0) | ||
571 | slots_fan = ct; | ||
572 | } else if (!strcmp(ct->name, "drive-bay-fan")) { | ||
573 | if (drive_bay_fan == NULL && wf_get_control(ct) == 0) | ||
574 | drive_bay_fan = ct; | ||
575 | } | ||
576 | return; | ||
577 | } | ||
578 | |||
579 | for (i = 0; i < CPU_FANS_REQD; ++i) | ||
580 | if (cpu_fans[i] == NULL) | ||
581 | return; | ||
582 | |||
583 | /* work out pump scaling factors */ | ||
584 | max_exhaust = cpu_fans[0]->ops->get_max(cpu_fans[0]); | ||
585 | for (i = FIRST_PUMP; i <= LAST_PUMP; ++i) | ||
586 | if ((ct = cpu_fans[i]) != NULL) | ||
587 | cpu_fan_scale[i] = | ||
588 | ct->ops->get_max(ct) * 100 / max_exhaust; | ||
589 | |||
590 | have_all_controls = 1; | ||
591 | } | ||
592 | |||
593 | static void pm112_new_sensor(struct wf_sensor *sr) | ||
594 | { | ||
595 | unsigned int i; | ||
596 | |||
597 | if (have_all_sensors) | ||
598 | return; | ||
599 | if (!strncmp(sr->name, "cpu-temp-", 9)) { | ||
600 | i = sr->name[9] - '0'; | ||
601 | if (sr->name[10] == 0 && i < NR_CORES && | ||
602 | sens_cpu_temp[i] == NULL && wf_get_sensor(sr) == 0) | ||
603 | sens_cpu_temp[i] = sr; | ||
604 | |||
605 | } else if (!strncmp(sr->name, "cpu-power-", 10)) { | ||
606 | i = sr->name[10] - '0'; | ||
607 | if (sr->name[11] == 0 && i < NR_CORES && | ||
608 | sens_cpu_power[i] == NULL && wf_get_sensor(sr) == 0) | ||
609 | sens_cpu_power[i] = sr; | ||
610 | } else if (!strcmp(sr->name, "hd-temp")) { | ||
611 | if (hd_temp == NULL && wf_get_sensor(sr) == 0) | ||
612 | hd_temp = sr; | ||
613 | } else if (!strcmp(sr->name, "slots-power")) { | ||
614 | if (slots_power == NULL && wf_get_sensor(sr) == 0) | ||
615 | slots_power = sr; | ||
616 | } else if (!strcmp(sr->name, "u4-temp")) { | ||
617 | if (u4_temp == NULL && wf_get_sensor(sr) == 0) | ||
618 | u4_temp = sr; | ||
619 | } else | ||
620 | return; | ||
621 | |||
622 | /* check if we have all the sensors we need */ | ||
623 | for (i = 0; i < nr_cores; ++i) | ||
624 | if (sens_cpu_temp[i] == NULL || sens_cpu_power[i] == NULL) | ||
625 | return; | ||
626 | |||
627 | have_all_sensors = 1; | ||
628 | } | ||
629 | |||
630 | static int pm112_wf_notify(struct notifier_block *self, | ||
631 | unsigned long event, void *data) | ||
632 | { | ||
633 | switch (event) { | ||
634 | case WF_EVENT_NEW_SENSOR: | ||
635 | pm112_new_sensor(data); | ||
636 | break; | ||
637 | case WF_EVENT_NEW_CONTROL: | ||
638 | pm112_new_control(data); | ||
639 | break; | ||
640 | case WF_EVENT_TICK: | ||
641 | if (have_all_controls && have_all_sensors) | ||
642 | pm112_tick(); | ||
643 | } | ||
644 | return 0; | ||
645 | } | ||
646 | |||
647 | static struct notifier_block pm112_events = { | ||
648 | .notifier_call = pm112_wf_notify, | ||
649 | }; | ||
650 | |||
651 | static int wf_pm112_probe(struct device *dev) | ||
652 | { | ||
653 | wf_register_client(&pm112_events); | ||
654 | return 0; | ||
655 | } | ||
656 | |||
657 | static int wf_pm112_remove(struct device *dev) | ||
658 | { | ||
659 | wf_unregister_client(&pm112_events); | ||
660 | /* should release all sensors and controls */ | ||
661 | return 0; | ||
662 | } | ||
663 | |||
664 | static struct device_driver wf_pm112_driver = { | ||
665 | .name = "windfarm", | ||
666 | .bus = &platform_bus_type, | ||
667 | .probe = wf_pm112_probe, | ||
668 | .remove = wf_pm112_remove, | ||
669 | }; | ||
670 | |||
671 | static int __init wf_pm112_init(void) | ||
672 | { | ||
673 | struct device_node *cpu; | ||
674 | |||
675 | if (!machine_is_compatible("PowerMac11,2")) | ||
676 | return -ENODEV; | ||
677 | |||
678 | /* Count the number of CPU cores */ | ||
679 | nr_cores = 0; | ||
680 | for (cpu = NULL; (cpu = of_find_node_by_type(cpu, "cpu")) != NULL; ) | ||
681 | ++nr_cores; | ||
682 | |||
683 | printk(KERN_INFO "windfarm: initializing for dual-core desktop G5\n"); | ||
684 | driver_register(&wf_pm112_driver); | ||
685 | return 0; | ||
686 | } | ||
687 | |||
688 | static void __exit wf_pm112_exit(void) | ||
689 | { | ||
690 | driver_unregister(&wf_pm112_driver); | ||
691 | } | ||
692 | |||
693 | module_init(wf_pm112_init); | ||
694 | module_exit(wf_pm112_exit); | ||
695 | |||
696 | MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); | ||
697 | MODULE_DESCRIPTION("Thermal control for PowerMac11,2"); | ||
698 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/macintosh/windfarm_pm81.c b/drivers/macintosh/windfarm_pm81.c index eb69a601e765..f1df6efcbe68 100644 --- a/drivers/macintosh/windfarm_pm81.c +++ b/drivers/macintosh/windfarm_pm81.c | |||
@@ -538,45 +538,6 @@ static void wf_smu_cpu_fans_tick(struct wf_smu_cpu_fans_state *st) | |||
538 | } | 538 | } |
539 | } | 539 | } |
540 | 540 | ||
541 | |||
542 | /* | ||
543 | * ****** Attributes ****** | ||
544 | * | ||
545 | */ | ||
546 | |||
547 | #define BUILD_SHOW_FUNC_FIX(name, data) \ | ||
548 | static ssize_t show_##name(struct device *dev, \ | ||
549 | struct device_attribute *attr, \ | ||
550 | char *buf) \ | ||
551 | { \ | ||
552 | ssize_t r; \ | ||
553 | s32 val = 0; \ | ||
554 | data->ops->get_value(data, &val); \ | ||
555 | r = sprintf(buf, "%d.%03d", FIX32TOPRINT(val)); \ | ||
556 | return r; \ | ||
557 | } \ | ||
558 | static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL); | ||
559 | |||
560 | |||
561 | #define BUILD_SHOW_FUNC_INT(name, data) \ | ||
562 | static ssize_t show_##name(struct device *dev, \ | ||
563 | struct device_attribute *attr, \ | ||
564 | char *buf) \ | ||
565 | { \ | ||
566 | s32 val = 0; \ | ||
567 | data->ops->get_value(data, &val); \ | ||
568 | return sprintf(buf, "%d", val); \ | ||
569 | } \ | ||
570 | static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL); | ||
571 | |||
572 | BUILD_SHOW_FUNC_INT(cpu_fan, fan_cpu_main); | ||
573 | BUILD_SHOW_FUNC_INT(sys_fan, fan_system); | ||
574 | BUILD_SHOW_FUNC_INT(hd_fan, fan_hd); | ||
575 | |||
576 | BUILD_SHOW_FUNC_FIX(cpu_temp, sensor_cpu_temp); | ||
577 | BUILD_SHOW_FUNC_FIX(cpu_power, sensor_cpu_power); | ||
578 | BUILD_SHOW_FUNC_FIX(hd_temp, sensor_hd_temp); | ||
579 | |||
580 | /* | 541 | /* |
581 | * ****** Setup / Init / Misc ... ****** | 542 | * ****** Setup / Init / Misc ... ****** |
582 | * | 543 | * |
@@ -654,17 +615,13 @@ static void wf_smu_new_control(struct wf_control *ct) | |||
654 | return; | 615 | return; |
655 | 616 | ||
656 | if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-fan")) { | 617 | if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-fan")) { |
657 | if (wf_get_control(ct) == 0) { | 618 | if (wf_get_control(ct) == 0) |
658 | fan_cpu_main = ct; | 619 | fan_cpu_main = ct; |
659 | device_create_file(wf_smu_dev, &dev_attr_cpu_fan); | ||
660 | } | ||
661 | } | 620 | } |
662 | 621 | ||
663 | if (fan_system == NULL && !strcmp(ct->name, "system-fan")) { | 622 | if (fan_system == NULL && !strcmp(ct->name, "system-fan")) { |
664 | if (wf_get_control(ct) == 0) { | 623 | if (wf_get_control(ct) == 0) |
665 | fan_system = ct; | 624 | fan_system = ct; |
666 | device_create_file(wf_smu_dev, &dev_attr_sys_fan); | ||
667 | } | ||
668 | } | 625 | } |
669 | 626 | ||
670 | if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) { | 627 | if (cpufreq_clamp == NULL && !strcmp(ct->name, "cpufreq-clamp")) { |
@@ -683,10 +640,8 @@ static void wf_smu_new_control(struct wf_control *ct) | |||
683 | } | 640 | } |
684 | 641 | ||
685 | if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) { | 642 | if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) { |
686 | if (wf_get_control(ct) == 0) { | 643 | if (wf_get_control(ct) == 0) |
687 | fan_hd = ct; | 644 | fan_hd = ct; |
688 | device_create_file(wf_smu_dev, &dev_attr_hd_fan); | ||
689 | } | ||
690 | } | 645 | } |
691 | 646 | ||
692 | if (fan_system && fan_hd && fan_cpu_main && cpufreq_clamp) | 647 | if (fan_system && fan_hd && fan_cpu_main && cpufreq_clamp) |
@@ -699,24 +654,18 @@ static void wf_smu_new_sensor(struct wf_sensor *sr) | |||
699 | return; | 654 | return; |
700 | 655 | ||
701 | if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) { | 656 | if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) { |
702 | if (wf_get_sensor(sr) == 0) { | 657 | if (wf_get_sensor(sr) == 0) |
703 | sensor_cpu_power = sr; | 658 | sensor_cpu_power = sr; |
704 | device_create_file(wf_smu_dev, &dev_attr_cpu_power); | ||
705 | } | ||
706 | } | 659 | } |
707 | 660 | ||
708 | if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) { | 661 | if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) { |
709 | if (wf_get_sensor(sr) == 0) { | 662 | if (wf_get_sensor(sr) == 0) |
710 | sensor_cpu_temp = sr; | 663 | sensor_cpu_temp = sr; |
711 | device_create_file(wf_smu_dev, &dev_attr_cpu_temp); | ||
712 | } | ||
713 | } | 664 | } |
714 | 665 | ||
715 | if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) { | 666 | if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) { |
716 | if (wf_get_sensor(sr) == 0) { | 667 | if (wf_get_sensor(sr) == 0) |
717 | sensor_hd_temp = sr; | 668 | sensor_hd_temp = sr; |
718 | device_create_file(wf_smu_dev, &dev_attr_hd_temp); | ||
719 | } | ||
720 | } | 669 | } |
721 | 670 | ||
722 | if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp) | 671 | if (sensor_cpu_power && sensor_cpu_temp && sensor_hd_temp) |
@@ -794,32 +743,20 @@ static int wf_smu_remove(struct device *ddev) | |||
794 | * with that except by adding locks all over... I'll do that | 743 | * with that except by adding locks all over... I'll do that |
795 | * eventually but heh, who ever rmmod this module anyway ? | 744 | * eventually but heh, who ever rmmod this module anyway ? |
796 | */ | 745 | */ |
797 | if (sensor_cpu_power) { | 746 | if (sensor_cpu_power) |
798 | device_remove_file(wf_smu_dev, &dev_attr_cpu_power); | ||
799 | wf_put_sensor(sensor_cpu_power); | 747 | wf_put_sensor(sensor_cpu_power); |
800 | } | 748 | if (sensor_cpu_temp) |
801 | if (sensor_cpu_temp) { | ||
802 | device_remove_file(wf_smu_dev, &dev_attr_cpu_temp); | ||
803 | wf_put_sensor(sensor_cpu_temp); | 749 | wf_put_sensor(sensor_cpu_temp); |
804 | } | 750 | if (sensor_hd_temp) |
805 | if (sensor_hd_temp) { | ||
806 | device_remove_file(wf_smu_dev, &dev_attr_hd_temp); | ||
807 | wf_put_sensor(sensor_hd_temp); | 751 | wf_put_sensor(sensor_hd_temp); |
808 | } | ||
809 | 752 | ||
810 | /* Release all controls */ | 753 | /* Release all controls */ |
811 | if (fan_cpu_main) { | 754 | if (fan_cpu_main) |
812 | device_remove_file(wf_smu_dev, &dev_attr_cpu_fan); | ||
813 | wf_put_control(fan_cpu_main); | 755 | wf_put_control(fan_cpu_main); |
814 | } | 756 | if (fan_hd) |
815 | if (fan_hd) { | ||
816 | device_remove_file(wf_smu_dev, &dev_attr_hd_fan); | ||
817 | wf_put_control(fan_hd); | 757 | wf_put_control(fan_hd); |
818 | } | 758 | if (fan_system) |
819 | if (fan_system) { | ||
820 | device_remove_file(wf_smu_dev, &dev_attr_sys_fan); | ||
821 | wf_put_control(fan_system); | 759 | wf_put_control(fan_system); |
822 | } | ||
823 | if (cpufreq_clamp) | 760 | if (cpufreq_clamp) |
824 | wf_put_control(cpufreq_clamp); | 761 | wf_put_control(cpufreq_clamp); |
825 | 762 | ||
diff --git a/drivers/macintosh/windfarm_pm91.c b/drivers/macintosh/windfarm_pm91.c index 43243cf7410b..0d6372e96d32 100644 --- a/drivers/macintosh/windfarm_pm91.c +++ b/drivers/macintosh/windfarm_pm91.c | |||
@@ -458,45 +458,6 @@ static void wf_smu_slots_fans_tick(struct wf_smu_slots_fans_state *st) | |||
458 | 458 | ||
459 | 459 | ||
460 | /* | 460 | /* |
461 | * ****** Attributes ****** | ||
462 | * | ||
463 | */ | ||
464 | |||
465 | #define BUILD_SHOW_FUNC_FIX(name, data) \ | ||
466 | static ssize_t show_##name(struct device *dev, \ | ||
467 | struct device_attribute *attr, \ | ||
468 | char *buf) \ | ||
469 | { \ | ||
470 | ssize_t r; \ | ||
471 | s32 val = 0; \ | ||
472 | data->ops->get_value(data, &val); \ | ||
473 | r = sprintf(buf, "%d.%03d", FIX32TOPRINT(val)); \ | ||
474 | return r; \ | ||
475 | } \ | ||
476 | static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL); | ||
477 | |||
478 | |||
479 | #define BUILD_SHOW_FUNC_INT(name, data) \ | ||
480 | static ssize_t show_##name(struct device *dev, \ | ||
481 | struct device_attribute *attr, \ | ||
482 | char *buf) \ | ||
483 | { \ | ||
484 | s32 val = 0; \ | ||
485 | data->ops->get_value(data, &val); \ | ||
486 | return sprintf(buf, "%d", val); \ | ||
487 | } \ | ||
488 | static DEVICE_ATTR(name,S_IRUGO,show_##name, NULL); | ||
489 | |||
490 | BUILD_SHOW_FUNC_INT(cpu_fan, fan_cpu_main); | ||
491 | BUILD_SHOW_FUNC_INT(hd_fan, fan_hd); | ||
492 | BUILD_SHOW_FUNC_INT(slots_fan, fan_slots); | ||
493 | |||
494 | BUILD_SHOW_FUNC_FIX(cpu_temp, sensor_cpu_temp); | ||
495 | BUILD_SHOW_FUNC_FIX(cpu_power, sensor_cpu_power); | ||
496 | BUILD_SHOW_FUNC_FIX(hd_temp, sensor_hd_temp); | ||
497 | BUILD_SHOW_FUNC_FIX(slots_power, sensor_slots_power); | ||
498 | |||
499 | /* | ||
500 | * ****** Setup / Init / Misc ... ****** | 461 | * ****** Setup / Init / Misc ... ****** |
501 | * | 462 | * |
502 | */ | 463 | */ |
@@ -581,10 +542,8 @@ static void wf_smu_new_control(struct wf_control *ct) | |||
581 | return; | 542 | return; |
582 | 543 | ||
583 | if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-rear-fan-0")) { | 544 | if (fan_cpu_main == NULL && !strcmp(ct->name, "cpu-rear-fan-0")) { |
584 | if (wf_get_control(ct) == 0) { | 545 | if (wf_get_control(ct) == 0) |
585 | fan_cpu_main = ct; | 546 | fan_cpu_main = ct; |
586 | device_create_file(wf_smu_dev, &dev_attr_cpu_fan); | ||
587 | } | ||
588 | } | 547 | } |
589 | 548 | ||
590 | if (fan_cpu_second == NULL && !strcmp(ct->name, "cpu-rear-fan-1")) { | 549 | if (fan_cpu_second == NULL && !strcmp(ct->name, "cpu-rear-fan-1")) { |
@@ -603,17 +562,13 @@ static void wf_smu_new_control(struct wf_control *ct) | |||
603 | } | 562 | } |
604 | 563 | ||
605 | if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) { | 564 | if (fan_hd == NULL && !strcmp(ct->name, "drive-bay-fan")) { |
606 | if (wf_get_control(ct) == 0) { | 565 | if (wf_get_control(ct) == 0) |
607 | fan_hd = ct; | 566 | fan_hd = ct; |
608 | device_create_file(wf_smu_dev, &dev_attr_hd_fan); | ||
609 | } | ||
610 | } | 567 | } |
611 | 568 | ||
612 | if (fan_slots == NULL && !strcmp(ct->name, "slots-fan")) { | 569 | if (fan_slots == NULL && !strcmp(ct->name, "slots-fan")) { |
613 | if (wf_get_control(ct) == 0) { | 570 | if (wf_get_control(ct) == 0) |
614 | fan_slots = ct; | 571 | fan_slots = ct; |
615 | device_create_file(wf_smu_dev, &dev_attr_slots_fan); | ||
616 | } | ||
617 | } | 572 | } |
618 | 573 | ||
619 | if (fan_cpu_main && (fan_cpu_second || fan_cpu_third) && fan_hd && | 574 | if (fan_cpu_main && (fan_cpu_second || fan_cpu_third) && fan_hd && |
@@ -627,31 +582,23 @@ static void wf_smu_new_sensor(struct wf_sensor *sr) | |||
627 | return; | 582 | return; |
628 | 583 | ||
629 | if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) { | 584 | if (sensor_cpu_power == NULL && !strcmp(sr->name, "cpu-power")) { |
630 | if (wf_get_sensor(sr) == 0) { | 585 | if (wf_get_sensor(sr) == 0) |
631 | sensor_cpu_power = sr; | 586 | sensor_cpu_power = sr; |
632 | device_create_file(wf_smu_dev, &dev_attr_cpu_power); | ||
633 | } | ||
634 | } | 587 | } |
635 | 588 | ||
636 | if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) { | 589 | if (sensor_cpu_temp == NULL && !strcmp(sr->name, "cpu-temp")) { |
637 | if (wf_get_sensor(sr) == 0) { | 590 | if (wf_get_sensor(sr) == 0) |
638 | sensor_cpu_temp = sr; | 591 | sensor_cpu_temp = sr; |
639 | device_create_file(wf_smu_dev, &dev_attr_cpu_temp); | ||
640 | } | ||
641 | } | 592 | } |
642 | 593 | ||
643 | if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) { | 594 | if (sensor_hd_temp == NULL && !strcmp(sr->name, "hd-temp")) { |
644 | if (wf_get_sensor(sr) == 0) { | 595 | if (wf_get_sensor(sr) == 0) |
645 | sensor_hd_temp = sr; | 596 | sensor_hd_temp = sr; |
646 | device_create_file(wf_smu_dev, &dev_attr_hd_temp); | ||
647 | } | ||
648 | } | 597 | } |
649 | 598 | ||
650 | if (sensor_slots_power == NULL && !strcmp(sr->name, "slots-power")) { | 599 | if (sensor_slots_power == NULL && !strcmp(sr->name, "slots-power")) { |
651 | if (wf_get_sensor(sr) == 0) { | 600 | if (wf_get_sensor(sr) == 0) |
652 | sensor_slots_power = sr; | 601 | sensor_slots_power = sr; |
653 | device_create_file(wf_smu_dev, &dev_attr_slots_power); | ||
654 | } | ||
655 | } | 602 | } |
656 | 603 | ||
657 | if (sensor_cpu_power && sensor_cpu_temp && | 604 | if (sensor_cpu_power && sensor_cpu_temp && |
@@ -720,40 +667,26 @@ static int wf_smu_remove(struct device *ddev) | |||
720 | * with that except by adding locks all over... I'll do that | 667 | * with that except by adding locks all over... I'll do that |
721 | * eventually but heh, who ever rmmod this module anyway ? | 668 | * eventually but heh, who ever rmmod this module anyway ? |
722 | */ | 669 | */ |
723 | if (sensor_cpu_power) { | 670 | if (sensor_cpu_power) |
724 | device_remove_file(wf_smu_dev, &dev_attr_cpu_power); | ||
725 | wf_put_sensor(sensor_cpu_power); | 671 | wf_put_sensor(sensor_cpu_power); |
726 | } | 672 | if (sensor_cpu_temp) |
727 | if (sensor_cpu_temp) { | ||
728 | device_remove_file(wf_smu_dev, &dev_attr_cpu_temp); | ||
729 | wf_put_sensor(sensor_cpu_temp); | 673 | wf_put_sensor(sensor_cpu_temp); |
730 | } | 674 | if (sensor_hd_temp) |
731 | if (sensor_hd_temp) { | ||
732 | device_remove_file(wf_smu_dev, &dev_attr_hd_temp); | ||
733 | wf_put_sensor(sensor_hd_temp); | 675 | wf_put_sensor(sensor_hd_temp); |
734 | } | 676 | if (sensor_slots_power) |
735 | if (sensor_slots_power) { | ||
736 | device_remove_file(wf_smu_dev, &dev_attr_slots_power); | ||
737 | wf_put_sensor(sensor_slots_power); | 677 | wf_put_sensor(sensor_slots_power); |
738 | } | ||
739 | 678 | ||
740 | /* Release all controls */ | 679 | /* Release all controls */ |
741 | if (fan_cpu_main) { | 680 | if (fan_cpu_main) |
742 | device_remove_file(wf_smu_dev, &dev_attr_cpu_fan); | ||
743 | wf_put_control(fan_cpu_main); | 681 | wf_put_control(fan_cpu_main); |
744 | } | ||
745 | if (fan_cpu_second) | 682 | if (fan_cpu_second) |
746 | wf_put_control(fan_cpu_second); | 683 | wf_put_control(fan_cpu_second); |
747 | if (fan_cpu_third) | 684 | if (fan_cpu_third) |
748 | wf_put_control(fan_cpu_third); | 685 | wf_put_control(fan_cpu_third); |
749 | if (fan_hd) { | 686 | if (fan_hd) |
750 | device_remove_file(wf_smu_dev, &dev_attr_hd_fan); | ||
751 | wf_put_control(fan_hd); | 687 | wf_put_control(fan_hd); |
752 | } | 688 | if (fan_slots) |
753 | if (fan_slots) { | ||
754 | device_remove_file(wf_smu_dev, &dev_attr_slots_fan); | ||
755 | wf_put_control(fan_slots); | 689 | wf_put_control(fan_slots); |
756 | } | ||
757 | if (cpufreq_clamp) | 690 | if (cpufreq_clamp) |
758 | wf_put_control(cpufreq_clamp); | 691 | wf_put_control(cpufreq_clamp); |
759 | 692 | ||
diff --git a/drivers/macintosh/windfarm_smu_controls.c b/drivers/macintosh/windfarm_smu_controls.c index 4d811600bdab..a9e88edc0c72 100644 --- a/drivers/macintosh/windfarm_smu_controls.c +++ b/drivers/macintosh/windfarm_smu_controls.c | |||
@@ -24,7 +24,7 @@ | |||
24 | 24 | ||
25 | #include "windfarm.h" | 25 | #include "windfarm.h" |
26 | 26 | ||
27 | #define VERSION "0.3" | 27 | #define VERSION "0.4" |
28 | 28 | ||
29 | #undef DEBUG | 29 | #undef DEBUG |
30 | 30 | ||
@@ -34,6 +34,8 @@ | |||
34 | #define DBG(args...) do { } while(0) | 34 | #define DBG(args...) do { } while(0) |
35 | #endif | 35 | #endif |
36 | 36 | ||
37 | static int smu_supports_new_fans_ops = 1; | ||
38 | |||
37 | /* | 39 | /* |
38 | * SMU fans control object | 40 | * SMU fans control object |
39 | */ | 41 | */ |
@@ -59,23 +61,49 @@ static int smu_set_fan(int pwm, u8 id, u16 value) | |||
59 | 61 | ||
60 | /* Fill SMU command structure */ | 62 | /* Fill SMU command structure */ |
61 | cmd.cmd = SMU_CMD_FAN_COMMAND; | 63 | cmd.cmd = SMU_CMD_FAN_COMMAND; |
62 | cmd.data_len = 14; | 64 | |
65 | /* The SMU has an "old" and a "new" way of setting the fan speed | ||
66 | * Unfortunately, I found no reliable way to know which one works | ||
67 | * on a given machine model. After some investigations it appears | ||
68 | * that MacOS X just tries the new one, and if it fails fallbacks | ||
69 | * to the old ones ... Ugh. | ||
70 | */ | ||
71 | retry: | ||
72 | if (smu_supports_new_fans_ops) { | ||
73 | buffer[0] = 0x30; | ||
74 | buffer[1] = id; | ||
75 | *((u16 *)(&buffer[2])) = value; | ||
76 | cmd.data_len = 4; | ||
77 | } else { | ||
78 | if (id > 7) | ||
79 | return -EINVAL; | ||
80 | /* Fill argument buffer */ | ||
81 | memset(buffer, 0, 16); | ||
82 | buffer[0] = pwm ? 0x10 : 0x00; | ||
83 | buffer[1] = 0x01 << id; | ||
84 | *((u16 *)&buffer[2 + id * 2]) = value; | ||
85 | cmd.data_len = 14; | ||
86 | } | ||
87 | |||
63 | cmd.reply_len = 16; | 88 | cmd.reply_len = 16; |
64 | cmd.data_buf = cmd.reply_buf = buffer; | 89 | cmd.data_buf = cmd.reply_buf = buffer; |
65 | cmd.status = 0; | 90 | cmd.status = 0; |
66 | cmd.done = smu_done_complete; | 91 | cmd.done = smu_done_complete; |
67 | cmd.misc = ∁ | 92 | cmd.misc = ∁ |
68 | 93 | ||
69 | /* Fill argument buffer */ | ||
70 | memset(buffer, 0, 16); | ||
71 | buffer[0] = pwm ? 0x10 : 0x00; | ||
72 | buffer[1] = 0x01 << id; | ||
73 | *((u16 *)&buffer[2 + id * 2]) = value; | ||
74 | |||
75 | rc = smu_queue_cmd(&cmd); | 94 | rc = smu_queue_cmd(&cmd); |
76 | if (rc) | 95 | if (rc) |
77 | return rc; | 96 | return rc; |
78 | wait_for_completion(&comp); | 97 | wait_for_completion(&comp); |
98 | |||
99 | /* Handle fallback (see coment above) */ | ||
100 | if (cmd.status != 0 && smu_supports_new_fans_ops) { | ||
101 | printk(KERN_WARNING "windfarm: SMU failed new fan command " | ||
102 | "falling back to old method\n"); | ||
103 | smu_supports_new_fans_ops = 0; | ||
104 | goto retry; | ||
105 | } | ||
106 | |||
79 | return cmd.status; | 107 | return cmd.status; |
80 | } | 108 | } |
81 | 109 | ||
@@ -158,19 +186,29 @@ static struct smu_fan_control *smu_fan_create(struct device_node *node, | |||
158 | 186 | ||
159 | /* Names used on desktop models */ | 187 | /* Names used on desktop models */ |
160 | if (!strcmp(l, "Rear Fan 0") || !strcmp(l, "Rear Fan") || | 188 | if (!strcmp(l, "Rear Fan 0") || !strcmp(l, "Rear Fan") || |
161 | !strcmp(l, "Rear fan 0") || !strcmp(l, "Rear fan")) | 189 | !strcmp(l, "Rear fan 0") || !strcmp(l, "Rear fan") || |
190 | !strcmp(l, "CPU A EXHAUST")) | ||
162 | fct->ctrl.name = "cpu-rear-fan-0"; | 191 | fct->ctrl.name = "cpu-rear-fan-0"; |
163 | else if (!strcmp(l, "Rear Fan 1") || !strcmp(l, "Rear fan 1")) | 192 | else if (!strcmp(l, "Rear Fan 1") || !strcmp(l, "Rear fan 1") || |
193 | !strcmp(l, "CPU B EXHAUST")) | ||
164 | fct->ctrl.name = "cpu-rear-fan-1"; | 194 | fct->ctrl.name = "cpu-rear-fan-1"; |
165 | else if (!strcmp(l, "Front Fan 0") || !strcmp(l, "Front Fan") || | 195 | else if (!strcmp(l, "Front Fan 0") || !strcmp(l, "Front Fan") || |
166 | !strcmp(l, "Front fan 0") || !strcmp(l, "Front fan")) | 196 | !strcmp(l, "Front fan 0") || !strcmp(l, "Front fan") || |
197 | !strcmp(l, "CPU A INTAKE")) | ||
167 | fct->ctrl.name = "cpu-front-fan-0"; | 198 | fct->ctrl.name = "cpu-front-fan-0"; |
168 | else if (!strcmp(l, "Front Fan 1") || !strcmp(l, "Front fan 1")) | 199 | else if (!strcmp(l, "Front Fan 1") || !strcmp(l, "Front fan 1") || |
200 | !strcmp(l, "CPU B INTAKE")) | ||
169 | fct->ctrl.name = "cpu-front-fan-1"; | 201 | fct->ctrl.name = "cpu-front-fan-1"; |
170 | else if (!strcmp(l, "Slots Fan") || !strcmp(l, "Slots fan")) | 202 | else if (!strcmp(l, "CPU A PUMP")) |
203 | fct->ctrl.name = "cpu-pump-0"; | ||
204 | else if (!strcmp(l, "Slots Fan") || !strcmp(l, "Slots fan") || | ||
205 | !strcmp(l, "EXPANSION SLOTS INTAKE")) | ||
171 | fct->ctrl.name = "slots-fan"; | 206 | fct->ctrl.name = "slots-fan"; |
172 | else if (!strcmp(l, "Drive Bay") || !strcmp(l, "Drive bay")) | 207 | else if (!strcmp(l, "Drive Bay") || !strcmp(l, "Drive bay") || |
208 | !strcmp(l, "DRIVE BAY A INTAKE")) | ||
173 | fct->ctrl.name = "drive-bay-fan"; | 209 | fct->ctrl.name = "drive-bay-fan"; |
210 | else if (!strcmp(l, "BACKSIDE")) | ||
211 | fct->ctrl.name = "backside-fan"; | ||
174 | 212 | ||
175 | /* Names used on iMac models */ | 213 | /* Names used on iMac models */ |
176 | if (!strcmp(l, "System Fan") || !strcmp(l, "System fan")) | 214 | if (!strcmp(l, "System Fan") || !strcmp(l, "System fan")) |
@@ -223,7 +261,8 @@ static int __init smu_controls_init(void) | |||
223 | 261 | ||
224 | /* Look for RPM fans */ | 262 | /* Look for RPM fans */ |
225 | for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;) | 263 | for (fans = NULL; (fans = of_get_next_child(smu, fans)) != NULL;) |
226 | if (!strcmp(fans->name, "rpm-fans")) | 264 | if (!strcmp(fans->name, "rpm-fans") || |
265 | device_is_compatible(fans, "smu-rpm-fans")) | ||
227 | break; | 266 | break; |
228 | for (fan = NULL; | 267 | for (fan = NULL; |
229 | fans && (fan = of_get_next_child(fans, fan)) != NULL;) { | 268 | fans && (fan = of_get_next_child(fans, fan)) != NULL;) { |
diff --git a/drivers/macintosh/windfarm_smu_sat.c b/drivers/macintosh/windfarm_smu_sat.c new file mode 100644 index 000000000000..3a32c59494f2 --- /dev/null +++ b/drivers/macintosh/windfarm_smu_sat.c | |||
@@ -0,0 +1,418 @@ | |||
1 | /* | ||
2 | * Windfarm PowerMac thermal control. SMU "satellite" controller sensors. | ||
3 | * | ||
4 | * Copyright (C) 2005 Paul Mackerras, IBM Corp. <paulus@samba.org> | ||
5 | * | ||
6 | * Released under the terms of the GNU GPL v2. | ||
7 | */ | ||
8 | |||
9 | #include <linux/types.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/slab.h> | ||
13 | #include <linux/init.h> | ||
14 | #include <linux/wait.h> | ||
15 | #include <linux/i2c.h> | ||
16 | #include <linux/i2c-dev.h> | ||
17 | #include <asm/semaphore.h> | ||
18 | #include <asm/prom.h> | ||
19 | #include <asm/smu.h> | ||
20 | #include <asm/pmac_low_i2c.h> | ||
21 | |||
22 | #include "windfarm.h" | ||
23 | |||
24 | #define VERSION "0.2" | ||
25 | |||
26 | #define DEBUG | ||
27 | |||
28 | #ifdef DEBUG | ||
29 | #define DBG(args...) printk(args) | ||
30 | #else | ||
31 | #define DBG(args...) do { } while(0) | ||
32 | #endif | ||
33 | |||
34 | /* If the cache is older than 800ms we'll refetch it */ | ||
35 | #define MAX_AGE msecs_to_jiffies(800) | ||
36 | |||
37 | struct wf_sat { | ||
38 | int nr; | ||
39 | atomic_t refcnt; | ||
40 | struct semaphore mutex; | ||
41 | unsigned long last_read; /* jiffies when cache last updated */ | ||
42 | u8 cache[16]; | ||
43 | struct i2c_client i2c; | ||
44 | struct device_node *node; | ||
45 | }; | ||
46 | |||
47 | static struct wf_sat *sats[2]; | ||
48 | |||
49 | struct wf_sat_sensor { | ||
50 | int index; | ||
51 | int index2; /* used for power sensors */ | ||
52 | int shift; | ||
53 | struct wf_sat *sat; | ||
54 | struct wf_sensor sens; | ||
55 | }; | ||
56 | |||
57 | #define wf_to_sat(c) container_of(c, struct wf_sat_sensor, sens) | ||
58 | #define i2c_to_sat(c) container_of(c, struct wf_sat, i2c) | ||
59 | |||
60 | static int wf_sat_attach(struct i2c_adapter *adapter); | ||
61 | static int wf_sat_detach(struct i2c_client *client); | ||
62 | |||
63 | static struct i2c_driver wf_sat_driver = { | ||
64 | .driver = { | ||
65 | .name = "wf_smu_sat", | ||
66 | }, | ||
67 | .attach_adapter = wf_sat_attach, | ||
68 | .detach_client = wf_sat_detach, | ||
69 | }; | ||
70 | |||
71 | /* | ||
72 | * XXX i2c_smbus_read_i2c_block_data doesn't pass the requested | ||
73 | * length down to the low-level driver, so we use this, which | ||
74 | * works well enough with the SMU i2c driver code... | ||
75 | */ | ||
76 | static int sat_read_block(struct i2c_client *client, u8 command, | ||
77 | u8 *values, int len) | ||
78 | { | ||
79 | union i2c_smbus_data data; | ||
80 | int err; | ||
81 | |||
82 | data.block[0] = len; | ||
83 | err = i2c_smbus_xfer(client->adapter, client->addr, client->flags, | ||
84 | I2C_SMBUS_READ, command, I2C_SMBUS_I2C_BLOCK_DATA, | ||
85 | &data); | ||
86 | if (!err) | ||
87 | memcpy(values, data.block, len); | ||
88 | return err; | ||
89 | } | ||
90 | |||
91 | struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, int id, | ||
92 | unsigned int *size) | ||
93 | { | ||
94 | struct wf_sat *sat; | ||
95 | int err; | ||
96 | unsigned int i, len; | ||
97 | u8 *buf; | ||
98 | u8 data[4]; | ||
99 | |||
100 | /* TODO: Add the resulting partition to the device-tree */ | ||
101 | |||
102 | if (sat_id > 1 || (sat = sats[sat_id]) == NULL) | ||
103 | return NULL; | ||
104 | |||
105 | err = i2c_smbus_write_word_data(&sat->i2c, 8, id << 8); | ||
106 | if (err) { | ||
107 | printk(KERN_ERR "smu_sat_get_sdb_part wr error %d\n", err); | ||
108 | return NULL; | ||
109 | } | ||
110 | |||
111 | len = i2c_smbus_read_word_data(&sat->i2c, 9); | ||
112 | if (len < 0) { | ||
113 | printk(KERN_ERR "smu_sat_get_sdb_part rd len error\n"); | ||
114 | return NULL; | ||
115 | } | ||
116 | if (len == 0) { | ||
117 | printk(KERN_ERR "smu_sat_get_sdb_part no partition %x\n", id); | ||
118 | return NULL; | ||
119 | } | ||
120 | |||
121 | len = le16_to_cpu(len); | ||
122 | len = (len + 3) & ~3; | ||
123 | buf = kmalloc(len, GFP_KERNEL); | ||
124 | if (buf == NULL) | ||
125 | return NULL; | ||
126 | |||
127 | for (i = 0; i < len; i += 4) { | ||
128 | err = sat_read_block(&sat->i2c, 0xa, data, 4); | ||
129 | if (err) { | ||
130 | printk(KERN_ERR "smu_sat_get_sdb_part rd err %d\n", | ||
131 | err); | ||
132 | goto fail; | ||
133 | } | ||
134 | buf[i] = data[1]; | ||
135 | buf[i+1] = data[0]; | ||
136 | buf[i+2] = data[3]; | ||
137 | buf[i+3] = data[2]; | ||
138 | } | ||
139 | #ifdef DEBUG | ||
140 | DBG(KERN_DEBUG "sat %d partition %x:", sat_id, id); | ||
141 | for (i = 0; i < len; ++i) | ||
142 | DBG(" %x", buf[i]); | ||
143 | DBG("\n"); | ||
144 | #endif | ||
145 | |||
146 | if (size) | ||
147 | *size = len; | ||
148 | return (struct smu_sdbp_header *) buf; | ||
149 | |||
150 | fail: | ||
151 | kfree(buf); | ||
152 | return NULL; | ||
153 | } | ||
154 | |||
155 | /* refresh the cache */ | ||
156 | static int wf_sat_read_cache(struct wf_sat *sat) | ||
157 | { | ||
158 | int err; | ||
159 | |||
160 | err = sat_read_block(&sat->i2c, 0x3f, sat->cache, 16); | ||
161 | if (err) | ||
162 | return err; | ||
163 | sat->last_read = jiffies; | ||
164 | #ifdef LOTSA_DEBUG | ||
165 | { | ||
166 | int i; | ||
167 | DBG(KERN_DEBUG "wf_sat_get: data is"); | ||
168 | for (i = 0; i < 16; ++i) | ||
169 | DBG(" %.2x", sat->cache[i]); | ||
170 | DBG("\n"); | ||
171 | } | ||
172 | #endif | ||
173 | return 0; | ||
174 | } | ||
175 | |||
176 | static int wf_sat_get(struct wf_sensor *sr, s32 *value) | ||
177 | { | ||
178 | struct wf_sat_sensor *sens = wf_to_sat(sr); | ||
179 | struct wf_sat *sat = sens->sat; | ||
180 | int i, err; | ||
181 | s32 val; | ||
182 | |||
183 | if (sat->i2c.adapter == NULL) | ||
184 | return -ENODEV; | ||
185 | |||
186 | down(&sat->mutex); | ||
187 | if (time_after(jiffies, (sat->last_read + MAX_AGE))) { | ||
188 | err = wf_sat_read_cache(sat); | ||
189 | if (err) | ||
190 | goto fail; | ||
191 | } | ||
192 | |||
193 | i = sens->index * 2; | ||
194 | val = ((sat->cache[i] << 8) + sat->cache[i+1]) << sens->shift; | ||
195 | if (sens->index2 >= 0) { | ||
196 | i = sens->index2 * 2; | ||
197 | /* 4.12 * 8.8 -> 12.20; shift right 4 to get 16.16 */ | ||
198 | val = (val * ((sat->cache[i] << 8) + sat->cache[i+1])) >> 4; | ||
199 | } | ||
200 | |||
201 | *value = val; | ||
202 | err = 0; | ||
203 | |||
204 | fail: | ||
205 | up(&sat->mutex); | ||
206 | return err; | ||
207 | } | ||
208 | |||
209 | static void wf_sat_release(struct wf_sensor *sr) | ||
210 | { | ||
211 | struct wf_sat_sensor *sens = wf_to_sat(sr); | ||
212 | struct wf_sat *sat = sens->sat; | ||
213 | |||
214 | if (atomic_dec_and_test(&sat->refcnt)) { | ||
215 | if (sat->i2c.adapter) { | ||
216 | i2c_detach_client(&sat->i2c); | ||
217 | sat->i2c.adapter = NULL; | ||
218 | } | ||
219 | if (sat->nr >= 0) | ||
220 | sats[sat->nr] = NULL; | ||
221 | kfree(sat); | ||
222 | } | ||
223 | kfree(sens); | ||
224 | } | ||
225 | |||
226 | static struct wf_sensor_ops wf_sat_ops = { | ||
227 | .get_value = wf_sat_get, | ||
228 | .release = wf_sat_release, | ||
229 | .owner = THIS_MODULE, | ||
230 | }; | ||
231 | |||
232 | static void wf_sat_create(struct i2c_adapter *adapter, struct device_node *dev) | ||
233 | { | ||
234 | struct wf_sat *sat; | ||
235 | struct wf_sat_sensor *sens; | ||
236 | u32 *reg; | ||
237 | char *loc, *type; | ||
238 | u8 addr, chip, core; | ||
239 | struct device_node *child; | ||
240 | int shift, cpu, index; | ||
241 | char *name; | ||
242 | int vsens[2], isens[2]; | ||
243 | |||
244 | reg = (u32 *) get_property(dev, "reg", NULL); | ||
245 | if (reg == NULL) | ||
246 | return; | ||
247 | addr = *reg; | ||
248 | DBG(KERN_DEBUG "wf_sat: creating sat at address %x\n", addr); | ||
249 | |||
250 | sat = kzalloc(sizeof(struct wf_sat), GFP_KERNEL); | ||
251 | if (sat == NULL) | ||
252 | return; | ||
253 | sat->nr = -1; | ||
254 | sat->node = of_node_get(dev); | ||
255 | atomic_set(&sat->refcnt, 0); | ||
256 | init_MUTEX(&sat->mutex); | ||
257 | sat->i2c.addr = (addr >> 1) & 0x7f; | ||
258 | sat->i2c.adapter = adapter; | ||
259 | sat->i2c.driver = &wf_sat_driver; | ||
260 | strncpy(sat->i2c.name, "smu-sat", I2C_NAME_SIZE-1); | ||
261 | |||
262 | if (i2c_attach_client(&sat->i2c)) { | ||
263 | printk(KERN_ERR "windfarm: failed to attach smu-sat to i2c\n"); | ||
264 | goto fail; | ||
265 | } | ||
266 | |||
267 | vsens[0] = vsens[1] = -1; | ||
268 | isens[0] = isens[1] = -1; | ||
269 | child = NULL; | ||
270 | while ((child = of_get_next_child(dev, child)) != NULL) { | ||
271 | reg = (u32 *) get_property(child, "reg", NULL); | ||
272 | type = get_property(child, "device_type", NULL); | ||
273 | loc = get_property(child, "location", NULL); | ||
274 | if (reg == NULL || loc == NULL) | ||
275 | continue; | ||
276 | |||
277 | /* the cooked sensors are between 0x30 and 0x37 */ | ||
278 | if (*reg < 0x30 || *reg > 0x37) | ||
279 | continue; | ||
280 | index = *reg - 0x30; | ||
281 | |||
282 | /* expect location to be CPU [AB][01] ... */ | ||
283 | if (strncmp(loc, "CPU ", 4) != 0) | ||
284 | continue; | ||
285 | chip = loc[4] - 'A'; | ||
286 | core = loc[5] - '0'; | ||
287 | if (chip > 1 || core > 1) { | ||
288 | printk(KERN_ERR "wf_sat_create: don't understand " | ||
289 | "location %s for %s\n", loc, child->full_name); | ||
290 | continue; | ||
291 | } | ||
292 | cpu = 2 * chip + core; | ||
293 | if (sat->nr < 0) | ||
294 | sat->nr = chip; | ||
295 | else if (sat->nr != chip) { | ||
296 | printk(KERN_ERR "wf_sat_create: can't cope with " | ||
297 | "multiple CPU chips on one SAT (%s)\n", loc); | ||
298 | continue; | ||
299 | } | ||
300 | |||
301 | if (strcmp(type, "voltage-sensor") == 0) { | ||
302 | name = "cpu-voltage"; | ||
303 | shift = 4; | ||
304 | vsens[core] = index; | ||
305 | } else if (strcmp(type, "current-sensor") == 0) { | ||
306 | name = "cpu-current"; | ||
307 | shift = 8; | ||
308 | isens[core] = index; | ||
309 | } else if (strcmp(type, "temp-sensor") == 0) { | ||
310 | name = "cpu-temp"; | ||
311 | shift = 10; | ||
312 | } else | ||
313 | continue; /* hmmm shouldn't happen */ | ||
314 | |||
315 | /* the +16 is enough for "cpu-voltage-n" */ | ||
316 | sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL); | ||
317 | if (sens == NULL) { | ||
318 | printk(KERN_ERR "wf_sat_create: couldn't create " | ||
319 | "%s sensor %d (no memory)\n", name, cpu); | ||
320 | continue; | ||
321 | } | ||
322 | sens->index = index; | ||
323 | sens->index2 = -1; | ||
324 | sens->shift = shift; | ||
325 | sens->sat = sat; | ||
326 | atomic_inc(&sat->refcnt); | ||
327 | sens->sens.ops = &wf_sat_ops; | ||
328 | sens->sens.name = (char *) (sens + 1); | ||
329 | snprintf(sens->sens.name, 16, "%s-%d", name, cpu); | ||
330 | |||
331 | if (wf_register_sensor(&sens->sens)) { | ||
332 | atomic_dec(&sat->refcnt); | ||
333 | kfree(sens); | ||
334 | } | ||
335 | } | ||
336 | |||
337 | /* make the power sensors */ | ||
338 | for (core = 0; core < 2; ++core) { | ||
339 | if (vsens[core] < 0 || isens[core] < 0) | ||
340 | continue; | ||
341 | cpu = 2 * sat->nr + core; | ||
342 | sens = kzalloc(sizeof(struct wf_sat_sensor) + 16, GFP_KERNEL); | ||
343 | if (sens == NULL) { | ||
344 | printk(KERN_ERR "wf_sat_create: couldn't create power " | ||
345 | "sensor %d (no memory)\n", cpu); | ||
346 | continue; | ||
347 | } | ||
348 | sens->index = vsens[core]; | ||
349 | sens->index2 = isens[core]; | ||
350 | sens->shift = 0; | ||
351 | sens->sat = sat; | ||
352 | atomic_inc(&sat->refcnt); | ||
353 | sens->sens.ops = &wf_sat_ops; | ||
354 | sens->sens.name = (char *) (sens + 1); | ||
355 | snprintf(sens->sens.name, 16, "cpu-power-%d", cpu); | ||
356 | |||
357 | if (wf_register_sensor(&sens->sens)) { | ||
358 | atomic_dec(&sat->refcnt); | ||
359 | kfree(sens); | ||
360 | } | ||
361 | } | ||
362 | |||
363 | if (sat->nr >= 0) | ||
364 | sats[sat->nr] = sat; | ||
365 | |||
366 | return; | ||
367 | |||
368 | fail: | ||
369 | kfree(sat); | ||
370 | } | ||
371 | |||
372 | static int wf_sat_attach(struct i2c_adapter *adapter) | ||
373 | { | ||
374 | struct device_node *busnode, *dev = NULL; | ||
375 | struct pmac_i2c_bus *bus; | ||
376 | |||
377 | bus = pmac_i2c_adapter_to_bus(adapter); | ||
378 | if (bus == NULL) | ||
379 | return -ENODEV; | ||
380 | busnode = pmac_i2c_get_bus_node(bus); | ||
381 | |||
382 | while ((dev = of_get_next_child(busnode, dev)) != NULL) | ||
383 | if (device_is_compatible(dev, "smu-sat")) | ||
384 | wf_sat_create(adapter, dev); | ||
385 | return 0; | ||
386 | } | ||
387 | |||
388 | static int wf_sat_detach(struct i2c_client *client) | ||
389 | { | ||
390 | struct wf_sat *sat = i2c_to_sat(client); | ||
391 | |||
392 | /* XXX TODO */ | ||
393 | |||
394 | sat->i2c.adapter = NULL; | ||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | static int __init sat_sensors_init(void) | ||
399 | { | ||
400 | int err; | ||
401 | |||
402 | err = i2c_add_driver(&wf_sat_driver); | ||
403 | if (err < 0) | ||
404 | return err; | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | static void __exit sat_sensors_exit(void) | ||
409 | { | ||
410 | i2c_del_driver(&wf_sat_driver); | ||
411 | } | ||
412 | |||
413 | module_init(sat_sensors_init); | ||
414 | /*module_exit(sat_sensors_exit); Uncomment when cleanup is implemented */ | ||
415 | |||
416 | MODULE_AUTHOR("Paul Mackerras <paulus@samba.org>"); | ||
417 | MODULE_DESCRIPTION("SMU satellite sensors for PowerMac thermal control"); | ||
418 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/macintosh/windfarm_smu_sensors.c b/drivers/macintosh/windfarm_smu_sensors.c index 1a00d9c75a23..bed25dcf8a1e 100644 --- a/drivers/macintosh/windfarm_smu_sensors.c +++ b/drivers/macintosh/windfarm_smu_sensors.c | |||
@@ -220,14 +220,29 @@ static struct smu_ad_sensor *smu_ads_create(struct device_node *node) | |||
220 | !strcmp(l, "CPU T-Diode")) { | 220 | !strcmp(l, "CPU T-Diode")) { |
221 | ads->sens.ops = &smu_cputemp_ops; | 221 | ads->sens.ops = &smu_cputemp_ops; |
222 | ads->sens.name = "cpu-temp"; | 222 | ads->sens.name = "cpu-temp"; |
223 | if (cpudiode == NULL) { | ||
224 | DBG("wf: cpudiode partition (%02x) not found\n", | ||
225 | SMU_SDB_CPUDIODE_ID); | ||
226 | goto fail; | ||
227 | } | ||
223 | } else if (!strcmp(c, "current-sensor") && | 228 | } else if (!strcmp(c, "current-sensor") && |
224 | !strcmp(l, "CPU Current")) { | 229 | !strcmp(l, "CPU Current")) { |
225 | ads->sens.ops = &smu_cpuamp_ops; | 230 | ads->sens.ops = &smu_cpuamp_ops; |
226 | ads->sens.name = "cpu-current"; | 231 | ads->sens.name = "cpu-current"; |
232 | if (cpuvcp == NULL) { | ||
233 | DBG("wf: cpuvcp partition (%02x) not found\n", | ||
234 | SMU_SDB_CPUVCP_ID); | ||
235 | goto fail; | ||
236 | } | ||
227 | } else if (!strcmp(c, "voltage-sensor") && | 237 | } else if (!strcmp(c, "voltage-sensor") && |
228 | !strcmp(l, "CPU Voltage")) { | 238 | !strcmp(l, "CPU Voltage")) { |
229 | ads->sens.ops = &smu_cpuvolt_ops; | 239 | ads->sens.ops = &smu_cpuvolt_ops; |
230 | ads->sens.name = "cpu-voltage"; | 240 | ads->sens.name = "cpu-voltage"; |
241 | if (cpuvcp == NULL) { | ||
242 | DBG("wf: cpuvcp partition (%02x) not found\n", | ||
243 | SMU_SDB_CPUVCP_ID); | ||
244 | goto fail; | ||
245 | } | ||
231 | } else if (!strcmp(c, "power-sensor") && | 246 | } else if (!strcmp(c, "power-sensor") && |
232 | !strcmp(l, "Slots Power")) { | 247 | !strcmp(l, "Slots Power")) { |
233 | ads->sens.ops = &smu_slotspow_ops; | 248 | ads->sens.ops = &smu_slotspow_ops; |
@@ -365,29 +380,22 @@ smu_cpu_power_create(struct wf_sensor *volts, struct wf_sensor *amps) | |||
365 | return NULL; | 380 | return NULL; |
366 | } | 381 | } |
367 | 382 | ||
368 | static int smu_fetch_param_partitions(void) | 383 | static void smu_fetch_param_partitions(void) |
369 | { | 384 | { |
370 | struct smu_sdbp_header *hdr; | 385 | struct smu_sdbp_header *hdr; |
371 | 386 | ||
372 | /* Get CPU voltage/current/power calibration data */ | 387 | /* Get CPU voltage/current/power calibration data */ |
373 | hdr = smu_get_sdb_partition(SMU_SDB_CPUVCP_ID, NULL); | 388 | hdr = smu_get_sdb_partition(SMU_SDB_CPUVCP_ID, NULL); |
374 | if (hdr == NULL) { | 389 | if (hdr != NULL) { |
375 | DBG("wf: cpuvcp partition (%02x) not found\n", | 390 | cpuvcp = (struct smu_sdbp_cpuvcp *)&hdr[1]; |
376 | SMU_SDB_CPUVCP_ID); | 391 | /* Keep version around */ |
377 | return -ENODEV; | 392 | cpuvcp_version = hdr->version; |
378 | } | 393 | } |
379 | cpuvcp = (struct smu_sdbp_cpuvcp *)&hdr[1]; | ||
380 | /* Keep version around */ | ||
381 | cpuvcp_version = hdr->version; | ||
382 | 394 | ||
383 | /* Get CPU diode calibration data */ | 395 | /* Get CPU diode calibration data */ |
384 | hdr = smu_get_sdb_partition(SMU_SDB_CPUDIODE_ID, NULL); | 396 | hdr = smu_get_sdb_partition(SMU_SDB_CPUDIODE_ID, NULL); |
385 | if (hdr == NULL) { | 397 | if (hdr != NULL) |
386 | DBG("wf: cpudiode partition (%02x) not found\n", | 398 | cpudiode = (struct smu_sdbp_cpudiode *)&hdr[1]; |
387 | SMU_SDB_CPUDIODE_ID); | ||
388 | return -ENODEV; | ||
389 | } | ||
390 | cpudiode = (struct smu_sdbp_cpudiode *)&hdr[1]; | ||
391 | 399 | ||
392 | /* Get slots power calibration data if any */ | 400 | /* Get slots power calibration data if any */ |
393 | hdr = smu_get_sdb_partition(SMU_SDB_SLOTSPOW_ID, NULL); | 401 | hdr = smu_get_sdb_partition(SMU_SDB_SLOTSPOW_ID, NULL); |
@@ -398,23 +406,18 @@ static int smu_fetch_param_partitions(void) | |||
398 | hdr = smu_get_sdb_partition(SMU_SDB_DEBUG_SWITCHES_ID, NULL); | 406 | hdr = smu_get_sdb_partition(SMU_SDB_DEBUG_SWITCHES_ID, NULL); |
399 | if (hdr != NULL) | 407 | if (hdr != NULL) |
400 | debugswitches = (u8 *)&hdr[1]; | 408 | debugswitches = (u8 *)&hdr[1]; |
401 | |||
402 | return 0; | ||
403 | } | 409 | } |
404 | 410 | ||
405 | static int __init smu_sensors_init(void) | 411 | static int __init smu_sensors_init(void) |
406 | { | 412 | { |
407 | struct device_node *smu, *sensors, *s; | 413 | struct device_node *smu, *sensors, *s; |
408 | struct smu_ad_sensor *volt_sensor = NULL, *curr_sensor = NULL; | 414 | struct smu_ad_sensor *volt_sensor = NULL, *curr_sensor = NULL; |
409 | int rc; | ||
410 | 415 | ||
411 | if (!smu_present()) | 416 | if (!smu_present()) |
412 | return -ENODEV; | 417 | return -ENODEV; |
413 | 418 | ||
414 | /* Get parameters partitions */ | 419 | /* Get parameters partitions */ |
415 | rc = smu_fetch_param_partitions(); | 420 | smu_fetch_param_partitions(); |
416 | if (rc) | ||
417 | return rc; | ||
418 | 421 | ||
419 | smu = of_find_node_by_type(NULL, "smu"); | 422 | smu = of_find_node_by_type(NULL, "smu"); |
420 | if (smu == NULL) | 423 | if (smu == NULL) |
diff --git a/include/asm-powerpc/smu.h b/include/asm-powerpc/smu.h index 82ce47607774..2dc93632f210 100644 --- a/include/asm-powerpc/smu.h +++ b/include/asm-powerpc/smu.h | |||
@@ -521,6 +521,11 @@ struct smu_sdbp_cpupiddata { | |||
521 | extern struct smu_sdbp_header *smu_get_sdb_partition(int id, | 521 | extern struct smu_sdbp_header *smu_get_sdb_partition(int id, |
522 | unsigned int *size); | 522 | unsigned int *size); |
523 | 523 | ||
524 | /* Get "sdb" partition data from an SMU satellite */ | ||
525 | extern struct smu_sdbp_header *smu_sat_get_sdb_partition(unsigned int sat_id, | ||
526 | int id, unsigned int *size); | ||
527 | |||
528 | |||
524 | #endif /* __KERNEL__ */ | 529 | #endif /* __KERNEL__ */ |
525 | 530 | ||
526 | 531 | ||