1/* 2 * Windfarm PowerMac thermal control. Core 3 * 4 * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp. 5 * <benh@kernel.crashing.org> 6 * 7 * Released under the term of the GNU GPL v2. 8 * 9 * This core code tracks the list of sensors & controls, register 10 * clients, and holds the kernel thread used for control. 11 * 12 * TODO: 13 * 14 * Add some information about sensor/control type and data format to 15 * sensors/controls, and have the sysfs attribute stuff be moved 16 * generically here instead of hard coded in the platform specific 17 * driver as it us currently 18 * 19 * This however requires solving some annoying lifetime issues with 20 * sysfs which doesn't seem to have lifetime rules for struct attribute, 21 * I may have to create full features kobjects for every sensor/control 22 * instead which is a bit of an overkill imho 23 */ 24 25#include <linux/types.h> 26#include <linux/errno.h> 27#include <linux/kernel.h> 28#include <linux/slab.h> 29#include <linux/init.h> 30#include <linux/spinlock.h> 31#include <linux/kthread.h> 32#include <linux/jiffies.h> 33#include <linux/reboot.h> 34#include <linux/device.h> 35#include <linux/platform_device.h> 36#include <linux/mutex.h> 37#include <linux/freezer.h> 38 39#include <asm/prom.h> 40 41#include "windfarm.h" 42 43#define VERSION "0.2" 44 45#undef DEBUG 46 47#ifdef DEBUG 48#define DBG(args...) printk(args) 49#else 50#define DBG(args...) do { } while(0) 51#endif 52 53static LIST_HEAD(wf_controls); 54static LIST_HEAD(wf_sensors); 55static DEFINE_MUTEX(wf_lock); 56static BLOCKING_NOTIFIER_HEAD(wf_client_list); 57static int wf_client_count; 58static unsigned int wf_overtemp; 59static unsigned int wf_overtemp_counter; 60struct task_struct *wf_thread; 61 62static struct platform_device wf_platform_device = { 63 .name = "windfarm", 64}; 65 66/* 67 * Utilities & tick thread 68 */ 69 70static inline void wf_notify(int event, void *param) 71{ 72 blocking_notifier_call_chain(&wf_client_list, event, param); 73} 74 75int wf_critical_overtemp(void) 76{ 77 static char * critical_overtemp_path = "/sbin/critical_overtemp"; 78 char *argv[] = { critical_overtemp_path, NULL }; 79 static char *envp[] = { "HOME=/", 80 "TERM=linux", 81 "PATH=/sbin:/usr/sbin:/bin:/usr/bin", 82 NULL }; 83 84 return call_usermodehelper(critical_overtemp_path, 85 argv, envp, UMH_WAIT_EXEC); 86} 87EXPORT_SYMBOL_GPL(wf_critical_overtemp); 88 89static int wf_thread_func(void *data) 90{ 91 unsigned long next, delay; 92 93 next = jiffies; 94 95 DBG("wf: thread started\n"); 96 97 set_freezable(); 98 while (!kthread_should_stop()) { 99 try_to_freeze(); 100 101 if (time_after_eq(jiffies, next)) { 102 wf_notify(WF_EVENT_TICK, NULL); 103 if (wf_overtemp) { 104 wf_overtemp_counter++; 105 /* 10 seconds overtemp, notify userland */ 106 if (wf_overtemp_counter > 10) 107 wf_critical_overtemp(); 108 /* 30 seconds, shutdown */ 109 if (wf_overtemp_counter > 30) { 110 printk(KERN_ERR "windfarm: Overtemp " 111 "for more than 30" 112 " seconds, shutting down\n"); 113 machine_power_off(); 114 } 115 } 116 next += HZ; 117 } 118 119 delay = next - jiffies; 120 if (delay <= HZ) 121 schedule_timeout_interruptible(delay); 122 } 123 124 DBG("wf: thread stopped\n"); 125 126 return 0; 127} 128 129static void wf_start_thread(void) 130{ 131 wf_thread = kthread_run(wf_thread_func, NULL, "kwindfarm"); 132 if (IS_ERR(wf_thread)) { 133 printk(KERN_ERR "windfarm: failed to create thread,err %ld\n", 134 PTR_ERR(wf_thread)); 135 wf_thread = NULL; 136 } 137} 138 139 140static void wf_stop_thread(void) 141{ 142 if (wf_thread) 143 kthread_stop(wf_thread); 144 wf_thread = NULL; 145} 146 147/* 148 * Controls 149 */ 150 151static void wf_control_release(struct kref *kref) 152{ 153 struct wf_control *ct = container_of(kref, struct wf_control, ref); 154 155 DBG("wf: Deleting control %s\n", ct->name); 156 157 if (ct->ops && ct->ops->release) 158 ct->ops->release(ct); 159 else 160 kfree(ct); 161} 162 163static ssize_t wf_show_control(struct device *dev, 164 struct device_attribute *attr, char *buf) 165{ 166 struct wf_control *ctrl = container_of(attr, struct wf_control, attr); 167 s32 val = 0; 168 int err; 169 170 err = ctrl->ops->get_value(ctrl, &val); 171 if (err < 0) 172 return err; 173 return sprintf(buf, "%d\n", val); 174} 175 176/* This is really only for debugging... */ 177static ssize_t wf_store_control(struct device *dev, 178 struct device_attribute *attr, 179 const char *buf, size_t count) 180{ 181 struct wf_control *ctrl = container_of(attr, struct wf_control, attr); 182 int val; 183 int err; 184 char *endp; 185 186 val = simple_strtoul(buf, &endp, 0); 187 while (endp < buf + count && (*endp == ' ' || *endp == '\n')) 188 ++endp; 189 if (endp - buf < count) 190 return -EINVAL; 191 err = ctrl->ops->set_value(ctrl, val); 192 if (err < 0) 193 return err; 194 return count; 195} 196 197int wf_register_control(struct wf_control *new_ct) 198{ 199 struct wf_control *ct; 200 201 mutex_lock(&wf_lock); 202 list_for_each_entry(ct, &wf_controls, link) { 203 if (!strcmp(ct->name, new_ct->name)) { 204 printk(KERN_WARNING "windfarm: trying to register" 205 " duplicate control %s\n", ct->name); 206 mutex_unlock(&wf_lock); 207 return -EEXIST; 208 } 209 } 210 kref_init(&new_ct->ref); 211 list_add(&new_ct->link, &wf_controls); 212 213 sysfs_attr_init(&new_ct->attr.attr); 214 new_ct->attr.attr.name = new_ct->name; 215 new_ct->attr.attr.mode = 0644; 216 new_ct->attr.show = wf_show_control; 217 new_ct->attr.store = wf_store_control; 218 if (device_create_file(&wf_platform_device.dev, &new_ct->attr)) 219 printk(KERN_WARNING "windfarm: device_create_file failed" 220 " for %s\n", new_ct->name); 221 /* the subsystem still does useful work without the file */ 222 223 DBG("wf: Registered control %s\n", new_ct->name); 224 225 wf_notify(WF_EVENT_NEW_CONTROL, new_ct); 226 mutex_unlock(&wf_lock); 227 228 return 0; 229} 230EXPORT_SYMBOL_GPL(wf_register_control); 231 232void wf_unregister_control(struct wf_control *ct) 233{ 234 mutex_lock(&wf_lock); 235 list_del(&ct->link); 236 mutex_unlock(&wf_lock); 237 238 DBG("wf: Unregistered control %s\n", ct->name); 239 240 kref_put(&ct->ref, wf_control_release); 241} 242EXPORT_SYMBOL_GPL(wf_unregister_control); 243 244struct wf_control * wf_find_control(const char *name) 245{ 246 struct wf_control *ct; 247 248 mutex_lock(&wf_lock); 249 list_for_each_entry(ct, &wf_controls, link) { 250 if (!strcmp(ct->name, name)) { 251 if (wf_get_control(ct)) 252 ct = NULL; 253 mutex_unlock(&wf_lock); 254 return ct; 255 } 256 } 257 mutex_unlock(&wf_lock); 258 return NULL; 259} 260EXPORT_SYMBOL_GPL(wf_find_control); 261 262int wf_get_control(struct wf_control *ct) 263{ 264 if (!try_module_get(ct->ops->owner)) 265 return -ENODEV; 266 kref_get(&ct->ref); 267 return 0; 268} 269EXPORT_SYMBOL_GPL(wf_get_control); 270 271void wf_put_control(struct wf_control *ct) 272{ 273 struct module *mod = ct->ops->owner; 274 kref_put(&ct->ref, wf_control_release); 275 module_put(mod); 276} 277EXPORT_SYMBOL_GPL(wf_put_control); 278 279 280/* 281 * Sensors 282 */ 283 284 285static void wf_sensor_release(struct kref *kref) 286{ 287 struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref); 288 289 DBG("wf: Deleting sensor %s\n", sr->name); 290 291 if (sr->ops && sr->ops->release) 292 sr->ops->release(sr); 293 else 294 kfree(sr); 295} 296 297static ssize_t wf_show_sensor(struct device *dev, 298 struct device_attribute *attr, char *buf) 299{ 300 struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr); 301 s32 val = 0; 302 int err; 303 304 err = sens->ops->get_value(sens, &val); 305 if (err < 0) 306 return err; 307 return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val)); 308} 309 310int wf_register_sensor(struct wf_sensor *new_sr) 311{ 312 struct wf_sensor *sr; 313 314 mutex_lock(&wf_lock); 315 list_for_each_entry(sr, &wf_sensors, link) { 316 if (!strcmp(sr->name, new_sr->name)) { 317 printk(KERN_WARNING "windfarm: trying to register" 318 " duplicate sensor %s\n", sr->name); 319 mutex_unlock(&wf_lock); 320 return -EEXIST; 321 } 322 } 323 kref_init(&new_sr->ref); 324 list_add(&new_sr->link, &wf_sensors); 325 326 sysfs_attr_init(&new_sr->attr.attr); 327 new_sr->attr.attr.name = new_sr->name; 328 new_sr->attr.attr.mode = 0444; 329 new_sr->attr.show = wf_show_sensor; 330 new_sr->attr.store = NULL; 331 if (device_create_file(&wf_platform_device.dev, &new_sr->attr)) 332 printk(KERN_WARNING "windfarm: device_create_file failed" 333 " for %s\n", new_sr->name); 334 /* the subsystem still does useful work without the file */ 335 336 DBG("wf: Registered sensor %s\n", new_sr->name); 337 338 wf_notify(WF_EVENT_NEW_SENSOR, new_sr); 339 mutex_unlock(&wf_lock); 340 341 return 0; 342} 343EXPORT_SYMBOL_GPL(wf_register_sensor); 344 345void wf_unregister_sensor(struct wf_sensor *sr) 346{ 347 mutex_lock(&wf_lock); 348 list_del(&sr->link); 349 mutex_unlock(&wf_lock); 350 351 DBG("wf: Unregistered sensor %s\n", sr->name); 352 353 wf_put_sensor(sr); 354} 355EXPORT_SYMBOL_GPL(wf_unregister_sensor); 356 357struct wf_sensor * wf_find_sensor(const char *name) 358{ 359 struct wf_sensor *sr; 360 361 mutex_lock(&wf_lock); 362 list_for_each_entry(sr, &wf_sensors, link) { 363 if (!strcmp(sr->name, name)) { 364 if (wf_get_sensor(sr)) 365 sr = NULL; 366 mutex_unlock(&wf_lock); 367 return sr; 368 } 369 } 370 mutex_unlock(&wf_lock); 371 return NULL; 372} 373EXPORT_SYMBOL_GPL(wf_find_sensor); 374 375int wf_get_sensor(struct wf_sensor *sr) 376{ 377 if (!try_module_get(sr->ops->owner)) 378 return -ENODEV; 379 kref_get(&sr->ref); 380 return 0; 381} 382EXPORT_SYMBOL_GPL(wf_get_sensor); 383 384void wf_put_sensor(struct wf_sensor *sr) 385{ 386 struct module *mod = sr->ops->owner; 387 kref_put(&sr->ref, wf_sensor_release); 388 module_put(mod); 389} 390EXPORT_SYMBOL_GPL(wf_put_sensor); 391 392 393/* 394 * Client & notification 395 */ 396 397int wf_register_client(struct notifier_block *nb) 398{ 399 int rc; 400 struct wf_control *ct; 401 struct wf_sensor *sr; 402 403 mutex_lock(&wf_lock); 404 rc = blocking_notifier_chain_register(&wf_client_list, nb); 405 if (rc != 0) 406 goto bail; 407 wf_client_count++; 408 list_for_each_entry(ct, &wf_controls, link) 409 wf_notify(WF_EVENT_NEW_CONTROL, ct); 410 list_for_each_entry(sr, &wf_sensors, link) 411 wf_notify(WF_EVENT_NEW_SENSOR, sr); 412 if (wf_client_count == 1) 413 wf_start_thread(); 414 bail: 415 mutex_unlock(&wf_lock); 416 return rc; 417} 418EXPORT_SYMBOL_GPL(wf_register_client); 419 420int wf_unregister_client(struct notifier_block *nb) 421{ 422 mutex_lock(&wf_lock); 423 blocking_notifier_chain_unregister(&wf_client_list, nb); 424 wf_client_count++; 425 if (wf_client_count == 0) 426 wf_stop_thread(); 427 mutex_unlock(&wf_lock); 428 429 return 0; 430} 431EXPORT_SYMBOL_GPL(wf_unregister_client); 432 433void wf_set_overtemp(void) 434{ 435 mutex_lock(&wf_lock); 436 wf_overtemp++; 437 if (wf_overtemp == 1) { 438 printk(KERN_WARNING "windfarm: Overtemp condition detected !\n"); 439 wf_overtemp_counter = 0; 440 wf_notify(WF_EVENT_OVERTEMP, NULL); 441 } 442 mutex_unlock(&wf_lock); 443} 444EXPORT_SYMBOL_GPL(wf_set_overtemp); 445 446void wf_clear_overtemp(void) 447{ 448 mutex_lock(&wf_lock); 449 WARN_ON(wf_overtemp == 0); 450 if (wf_overtemp == 0) { 451 mutex_unlock(&wf_lock); 452 return; 453 } 454 wf_overtemp--; 455 if (wf_overtemp == 0) { 456 printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n"); 457 wf_notify(WF_EVENT_NORMALTEMP, NULL); 458 } 459 mutex_unlock(&wf_lock); 460} 461EXPORT_SYMBOL_GPL(wf_clear_overtemp); 462 463int wf_is_overtemp(void) 464{ 465 return (wf_overtemp != 0); 466} 467EXPORT_SYMBOL_GPL(wf_is_overtemp); 468 469static int __init windfarm_core_init(void) 470{ 471 DBG("wf: core loaded\n"); 472 473 /* Don't register on old machines that use therm_pm72 for now */ 474 if (of_machine_is_compatible("PowerMac7,2") || 475 of_machine_is_compatible("PowerMac7,3") || 476 of_machine_is_compatible("RackMac3,1")) 477 return -ENODEV; 478 platform_device_register(&wf_platform_device); 479 return 0; 480} 481 482static void __exit windfarm_core_exit(void) 483{ 484 BUG_ON(wf_client_count != 0); 485 486 DBG("wf: core unloaded\n"); 487 488 platform_device_unregister(&wf_platform_device); 489} 490 491 492module_init(windfarm_core_init); 493module_exit(windfarm_core_exit); 494 495MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 496MODULE_DESCRIPTION("Core component of PowerMac thermal control"); 497MODULE_LICENSE("GPL"); 498