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