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
  75static int wf_critical_overtemp(void)
  76{
  77        static char const critical_overtemp_path[] = "/sbin/critical_overtemp";
  78        char *argv[] = { (char *)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
  88static 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
 128static 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
 139static 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
 150static 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
 162static 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... */
 190static 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
 210int 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}
 243EXPORT_SYMBOL_GPL(wf_register_control);
 244
 245void 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}
 255EXPORT_SYMBOL_GPL(wf_unregister_control);
 256
 257int 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}
 264EXPORT_SYMBOL_GPL(wf_get_control);
 265
 266void 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}
 272EXPORT_SYMBOL_GPL(wf_put_control);
 273
 274
 275/*
 276 * Sensors
 277 */
 278
 279
 280static 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
 292static 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
 305int 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}
 338EXPORT_SYMBOL_GPL(wf_register_sensor);
 339
 340void 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}
 350EXPORT_SYMBOL_GPL(wf_unregister_sensor);
 351
 352int 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}
 359EXPORT_SYMBOL_GPL(wf_get_sensor);
 360
 361void 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}
 367EXPORT_SYMBOL_GPL(wf_put_sensor);
 368
 369
 370/*
 371 * Client & notification
 372 */
 373
 374int 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}
 395EXPORT_SYMBOL_GPL(wf_register_client);
 396
 397int 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}
 408EXPORT_SYMBOL_GPL(wf_unregister_client);
 409
 410void 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}
 421EXPORT_SYMBOL_GPL(wf_set_overtemp);
 422
 423void 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}
 438EXPORT_SYMBOL_GPL(wf_clear_overtemp);
 439
 440static 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
 448static 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
 458module_init(windfarm_core_init);
 459module_exit(windfarm_core_exit);
 460
 461MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 462MODULE_DESCRIPTION("Core component of PowerMac thermal control");
 463MODULE_LICENSE("GPL");
 464
 465