linux/drivers/macintosh/windfarm_core.c
<<
>>
Prefs
   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
 499