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 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 static 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, 85 argv, envp, UMH_WAIT_EXEC); 86 } 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 const char *typestr; 167 s32 val = 0; 168 int err; 169 170 err = ctrl->ops->get_value(ctrl, &val); 171 if (err < 0) { 172 if (err == -EFAULT) 173 return sprintf(buf, "<HW FAULT>\n"); 174 return err; 175 } 176 switch(ctrl->type) { 177 case WF_CONTROL_RPM_FAN: 178 typestr = " RPM"; 179 break; 180 case WF_CONTROL_PWM_FAN: 181 typestr = " %"; 182 break; 183 default: 184 typestr = ""; 185 } 186 return sprintf(buf, "%d%s\n", val, typestr); 187 } 188 189 /* This is really only for debugging... */ 190 static ssize_t wf_store_control(struct device *dev, 191 struct device_attribute *attr, 192 const char *buf, size_t count) 193 { 194 struct wf_control *ctrl = container_of(attr, struct wf_control, attr); 195 int val; 196 int err; 197 char *endp; 198 199 val = simple_strtoul(buf, &endp, 0); 200 while (endp < buf + count && (*endp == ' ' || *endp == '\n')) 201 ++endp; 202 if (endp - buf < count) 203 return -EINVAL; 204 err = ctrl->ops->set_value(ctrl, val); 205 if (err < 0) 206 return err; 207 return count; 208 } 209 210 int wf_register_control(struct wf_control *new_ct) 211 { 212 struct wf_control *ct; 213 214 mutex_lock(&wf_lock); 215 list_for_each_entry(ct, &wf_controls, link) { 216 if (!strcmp(ct->name, new_ct->name)) { 217 printk(KERN_WARNING "windfarm: trying to register" 218 " duplicate control %s\n", ct->name); 219 mutex_unlock(&wf_lock); 220 return -EEXIST; 221 } 222 } 223 kref_init(&new_ct->ref); 224 list_add(&new_ct->link, &wf_controls); 225 226 sysfs_attr_init(&new_ct->attr.attr); 227 new_ct->attr.attr.name = new_ct->name; 228 new_ct->attr.attr.mode = 0644; 229 new_ct->attr.show = wf_show_control; 230 new_ct->attr.store = wf_store_control; 231 if (device_create_file(&wf_platform_device.dev, &new_ct->attr)) 232 printk(KERN_WARNING "windfarm: device_create_file failed" 233 " for %s\n", new_ct->name); 234 /* the subsystem still does useful work without the file */ 235 236 DBG("wf: Registered control %s\n", new_ct->name); 237 238 wf_notify(WF_EVENT_NEW_CONTROL, new_ct); 239 mutex_unlock(&wf_lock); 240 241 return 0; 242 } 243 EXPORT_SYMBOL_GPL(wf_register_control); 244 245 void wf_unregister_control(struct wf_control *ct) 246 { 247 mutex_lock(&wf_lock); 248 list_del(&ct->link); 249 mutex_unlock(&wf_lock); 250 251 DBG("wf: Unregistered control %s\n", ct->name); 252 253 kref_put(&ct->ref, wf_control_release); 254 } 255 EXPORT_SYMBOL_GPL(wf_unregister_control); 256 257 int wf_get_control(struct wf_control *ct) 258 { 259 if (!try_module_get(ct->ops->owner)) 260 return -ENODEV; 261 kref_get(&ct->ref); 262 return 0; 263 } 264 EXPORT_SYMBOL_GPL(wf_get_control); 265 266 void wf_put_control(struct wf_control *ct) 267 { 268 struct module *mod = ct->ops->owner; 269 kref_put(&ct->ref, wf_control_release); 270 module_put(mod); 271 } 272 EXPORT_SYMBOL_GPL(wf_put_control); 273 274 275 /* 276 * Sensors 277 */ 278 279 280 static void wf_sensor_release(struct kref *kref) 281 { 282 struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref); 283 284 DBG("wf: Deleting sensor %s\n", sr->name); 285 286 if (sr->ops && sr->ops->release) 287 sr->ops->release(sr); 288 else 289 kfree(sr); 290 } 291 292 static ssize_t wf_show_sensor(struct device *dev, 293 struct device_attribute *attr, char *buf) 294 { 295 struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr); 296 s32 val = 0; 297 int err; 298 299 err = sens->ops->get_value(sens, &val); 300 if (err < 0) 301 return err; 302 return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val)); 303 } 304 305 int wf_register_sensor(struct wf_sensor *new_sr) 306 { 307 struct wf_sensor *sr; 308 309 mutex_lock(&wf_lock); 310 list_for_each_entry(sr, &wf_sensors, link) { 311 if (!strcmp(sr->name, new_sr->name)) { 312 printk(KERN_WARNING "windfarm: trying to register" 313 " duplicate sensor %s\n", sr->name); 314 mutex_unlock(&wf_lock); 315 return -EEXIST; 316 } 317 } 318 kref_init(&new_sr->ref); 319 list_add(&new_sr->link, &wf_sensors); 320 321 sysfs_attr_init(&new_sr->attr.attr); 322 new_sr->attr.attr.name = new_sr->name; 323 new_sr->attr.attr.mode = 0444; 324 new_sr->attr.show = wf_show_sensor; 325 new_sr->attr.store = NULL; 326 if (device_create_file(&wf_platform_device.dev, &new_sr->attr)) 327 printk(KERN_WARNING "windfarm: device_create_file failed" 328 " for %s\n", new_sr->name); 329 /* the subsystem still does useful work without the file */ 330 331 DBG("wf: Registered sensor %s\n", new_sr->name); 332 333 wf_notify(WF_EVENT_NEW_SENSOR, new_sr); 334 mutex_unlock(&wf_lock); 335 336 return 0; 337 } 338 EXPORT_SYMBOL_GPL(wf_register_sensor); 339 340 void wf_unregister_sensor(struct wf_sensor *sr) 341 { 342 mutex_lock(&wf_lock); 343 list_del(&sr->link); 344 mutex_unlock(&wf_lock); 345 346 DBG("wf: Unregistered sensor %s\n", sr->name); 347 348 wf_put_sensor(sr); 349 } 350 EXPORT_SYMBOL_GPL(wf_unregister_sensor); 351 352 int wf_get_sensor(struct wf_sensor *sr) 353 { 354 if (!try_module_get(sr->ops->owner)) 355 return -ENODEV; 356 kref_get(&sr->ref); 357 return 0; 358 } 359 EXPORT_SYMBOL_GPL(wf_get_sensor); 360 361 void wf_put_sensor(struct wf_sensor *sr) 362 { 363 struct module *mod = sr->ops->owner; 364 kref_put(&sr->ref, wf_sensor_release); 365 module_put(mod); 366 } 367 EXPORT_SYMBOL_GPL(wf_put_sensor); 368 369 370 /* 371 * Client & notification 372 */ 373 374 int wf_register_client(struct notifier_block *nb) 375 { 376 int rc; 377 struct wf_control *ct; 378 struct wf_sensor *sr; 379 380 mutex_lock(&wf_lock); 381 rc = blocking_notifier_chain_register(&wf_client_list, nb); 382 if (rc != 0) 383 goto bail; 384 wf_client_count++; 385 list_for_each_entry(ct, &wf_controls, link) 386 wf_notify(WF_EVENT_NEW_CONTROL, ct); 387 list_for_each_entry(sr, &wf_sensors, link) 388 wf_notify(WF_EVENT_NEW_SENSOR, sr); 389 if (wf_client_count == 1) 390 wf_start_thread(); 391 bail: 392 mutex_unlock(&wf_lock); 393 return rc; 394 } 395 EXPORT_SYMBOL_GPL(wf_register_client); 396 397 int wf_unregister_client(struct notifier_block *nb) 398 { 399 mutex_lock(&wf_lock); 400 blocking_notifier_chain_unregister(&wf_client_list, nb); 401 wf_client_count--; 402 if (wf_client_count == 0) 403 wf_stop_thread(); 404 mutex_unlock(&wf_lock); 405 406 return 0; 407 } 408 EXPORT_SYMBOL_GPL(wf_unregister_client); 409 410 void wf_set_overtemp(void) 411 { 412 mutex_lock(&wf_lock); 413 wf_overtemp++; 414 if (wf_overtemp == 1) { 415 printk(KERN_WARNING "windfarm: Overtemp condition detected !\n"); 416 wf_overtemp_counter = 0; 417 wf_notify(WF_EVENT_OVERTEMP, NULL); 418 } 419 mutex_unlock(&wf_lock); 420 } 421 EXPORT_SYMBOL_GPL(wf_set_overtemp); 422 423 void wf_clear_overtemp(void) 424 { 425 mutex_lock(&wf_lock); 426 WARN_ON(wf_overtemp == 0); 427 if (wf_overtemp == 0) { 428 mutex_unlock(&wf_lock); 429 return; 430 } 431 wf_overtemp--; 432 if (wf_overtemp == 0) { 433 printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n"); 434 wf_notify(WF_EVENT_NORMALTEMP, NULL); 435 } 436 mutex_unlock(&wf_lock); 437 } 438 EXPORT_SYMBOL_GPL(wf_clear_overtemp); 439 440 static int __init windfarm_core_init(void) 441 { 442 DBG("wf: core loaded\n"); 443 444 platform_device_register(&wf_platform_device); 445 return 0; 446 } 447 448 static void __exit windfarm_core_exit(void) 449 { 450 BUG_ON(wf_client_count != 0); 451 452 DBG("wf: core unloaded\n"); 453 454 platform_device_unregister(&wf_platform_device); 455 } 456 457 458 module_init(windfarm_core_init); 459 module_exit(windfarm_core_exit); 460 461 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 462 MODULE_DESCRIPTION("Core component of PowerMac thermal control"); 463 MODULE_LICENSE("GPL"); 464 465