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