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