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 | ||
