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