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/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 <asm/prom.h>
  39
  40#include "windfarm.h"
  41
  42#define VERSION "0.2"
  43
  44#undef DEBUG
  45
  46#ifdef DEBUG
  47#define DBG(args...)    printk(args)
  48#else
  49#define DBG(args...)    do { } while(0)
  50#endif
  51
  52static LIST_HEAD(wf_controls);
  53static LIST_HEAD(wf_sensors);
  54static DEFINE_MUTEX(wf_lock);
  55static BLOCKING_NOTIFIER_HEAD(wf_client_list);
  56static int wf_client_count;
  57static unsigned int wf_overtemp;
  58static unsigned int wf_overtemp_counter;
  59struct task_struct *wf_thread;
  60
  61static struct platform_device wf_platform_device = {
  62        .name   = "windfarm",
  63};
  64
  65/*
  66 * Utilities & tick thread
  67 */
  68
  69static inline void wf_notify(int event, void *param)
  70{
  71        blocking_notifier_call_chain(&wf_client_list, event, param);
  72}
  73
  74int wf_critical_overtemp(void)
  75{
  76        static char * critical_overtemp_path = "/sbin/critical_overtemp";
  77        char *argv[] = { critical_overtemp_path, NULL };
  78        static char *envp[] = { "HOME=/",
  79                                "TERM=linux",
  80                                "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
  81                                NULL };
  82
  83        return call_usermodehelper(critical_overtemp_path,
  84                                   argv, envp, UMH_WAIT_EXEC);
  85}
  86EXPORT_SYMBOL_GPL(wf_critical_overtemp);
  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        s32 val = 0;
 167        int err;
 168
 169        err = ctrl->ops->get_value(ctrl, &val);
 170        if (err < 0)
 171                return err;
 172        return sprintf(buf, "%d\n", val);
 173}
 174
 175/* This is really only for debugging... */
 176static ssize_t wf_store_control(struct device *dev,
 177                                struct device_attribute *attr,
 178                                const char *buf, size_t count)
 179{
 180        struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
 181        int val;
 182        int err;
 183        char *endp;
 184
 185        val = simple_strtoul(buf, &endp, 0);
 186        while (endp < buf + count && (*endp == ' ' || *endp == '\n'))
 187                ++endp;
 188        if (endp - buf < count)
 189                return -EINVAL;
 190        err = ctrl->ops->set_value(ctrl, val);
 191        if (err < 0)
 192                return err;
 193        return count;
 194}
 195
 196int wf_register_control(struct wf_control *new_ct)
 197{
 198        struct wf_control *ct;
 199
 200        mutex_lock(&wf_lock);
 201        list_for_each_entry(ct, &wf_controls, link) {
 202                if (!strcmp(ct->name, new_ct->name)) {
 203                        printk(KERN_WARNING "windfarm: trying to register"
 204                               " duplicate control %s\n", ct->name);
 205                        mutex_unlock(&wf_lock);
 206                        return -EEXIST;
 207                }
 208        }
 209        kref_init(&new_ct->ref);
 210        list_add(&new_ct->link, &wf_controls);
 211
 212        new_ct->attr.attr.name = new_ct->name;
 213        new_ct->attr.attr.mode = 0644;
 214        new_ct->attr.show = wf_show_control;
 215        new_ct->attr.store = wf_store_control;
 216        if (device_create_file(&wf_platform_device.dev, &new_ct->attr))
 217                printk(KERN_WARNING "windfarm: device_create_file failed"
 218                        " for %s\n", new_ct->name);
 219                /* the subsystem still does useful work without the file */
 220
 221        DBG("wf: Registered control %s\n", new_ct->name);
 222
 223        wf_notify(WF_EVENT_NEW_CONTROL, new_ct);
 224        mutex_unlock(&wf_lock);
 225
 226        return 0;
 227}
 228EXPORT_SYMBOL_GPL(wf_register_control);
 229
 230void wf_unregister_control(struct wf_control *ct)
 231{
 232        mutex_lock(&wf_lock);
 233        list_del(&ct->link);
 234        mutex_unlock(&wf_lock);
 235
 236        DBG("wf: Unregistered control %s\n", ct->name);
 237
 238        kref_put(&ct->ref, wf_control_release);
 239}
 240EXPORT_SYMBOL_GPL(wf_unregister_control);
 241
 242struct wf_control * wf_find_control(const char *name)
 243{
 244        struct wf_control *ct;
 245
 246        mutex_lock(&wf_lock);
 247        list_for_each_entry(ct, &wf_controls, link) {
 248                if (!strcmp(ct->name, name)) {
 249                        if (wf_get_control(ct))
 250                                ct = NULL;
 251                        mutex_unlock(&wf_lock);
 252                        return ct;
 253                }
 254        }
 255        mutex_unlock(&wf_lock);
 256        return NULL;
 257}
 258EXPORT_SYMBOL_GPL(wf_find_control);
 259
 260int wf_get_control(struct wf_control *ct)
 261{
 262        if (!try_module_get(ct->ops->owner))
 263                return -ENODEV;
 264        kref_get(&ct->ref);
 265        return 0;
 266}
 267EXPORT_SYMBOL_GPL(wf_get_control);
 268
 269void wf_put_control(struct wf_control *ct)
 270{
 271        struct module *mod = ct->ops->owner;
 272        kref_put(&ct->ref, wf_control_release);
 273        module_put(mod);
 274}
 275EXPORT_SYMBOL_GPL(wf_put_control);
 276
 277
 278/*
 279 * Sensors
 280 */
 281
 282
 283static void wf_sensor_release(struct kref *kref)
 284{
 285        struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref);
 286
 287        DBG("wf: Deleting sensor %s\n", sr->name);
 288
 289        if (sr->ops && sr->ops->release)
 290                sr->ops->release(sr);
 291        else
 292                kfree(sr);
 293}
 294
 295static ssize_t wf_show_sensor(struct device *dev,
 296                              struct device_attribute *attr, char *buf)
 297{
 298        struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr);
 299        s32 val = 0;
 300        int err;
 301
 302        err = sens->ops->get_value(sens, &val);
 303        if (err < 0)
 304                return err;
 305        return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val));
 306}
 307
 308int wf_register_sensor(struct wf_sensor *new_sr)
 309{
 310        struct wf_sensor *sr;
 311
 312        mutex_lock(&wf_lock);
 313        list_for_each_entry(sr, &wf_sensors, link) {
 314                if (!strcmp(sr->name, new_sr->name)) {
 315                        printk(KERN_WARNING "windfarm: trying to register"
 316                               " duplicate sensor %s\n", sr->name);
 317                        mutex_unlock(&wf_lock);
 318                        return -EEXIST;
 319                }
 320        }
 321        kref_init(&new_sr->ref);
 322        list_add(&new_sr->link, &wf_sensors);
 323
 324        new_sr->attr.attr.name = new_sr->name;
 325        new_sr->attr.attr.mode = 0444;
 326        new_sr->attr.show = wf_show_sensor;
 327        new_sr->attr.store = NULL;
 328        if (device_create_file(&wf_platform_device.dev, &new_sr->attr))
 329                printk(KERN_WARNING "windfarm: device_create_file failed"
 330                        " for %s\n", new_sr->name);
 331                /* the subsystem still does useful work without the file */
 332
 333        DBG("wf: Registered sensor %s\n", new_sr->name);
 334
 335        wf_notify(WF_EVENT_NEW_SENSOR, new_sr);
 336        mutex_unlock(&wf_lock);
 337
 338        return 0;
 339}
 340EXPORT_SYMBOL_GPL(wf_register_sensor);
 341
 342void wf_unregister_sensor(struct wf_sensor *sr)
 343{
 344        mutex_lock(&wf_lock);
 345        list_del(&sr->link);
 346        mutex_unlock(&wf_lock);
 347
 348        DBG("wf: Unregistered sensor %s\n", sr->name);
 349
 350        wf_put_sensor(sr);
 351}
 352EXPORT_SYMBOL_GPL(wf_unregister_sensor);
 353
 354struct wf_sensor * wf_find_sensor(const char *name)
 355{
 356        struct wf_sensor *sr;
 357
 358        mutex_lock(&wf_lock);
 359        list_for_each_entry(sr, &wf_sensors, link) {
 360                if (!strcmp(sr->name, name)) {
 361                        if (wf_get_sensor(sr))
 362                                sr = NULL;
 363                        mutex_unlock(&wf_lock);
 364                        return sr;
 365                }
 366        }
 367        mutex_unlock(&wf_lock);
 368        return NULL;
 369}
 370EXPORT_SYMBOL_GPL(wf_find_sensor);
 371
 372int wf_get_sensor(struct wf_sensor *sr)
 373{
 374        if (!try_module_get(sr->ops->owner))
 375                return -ENODEV;
 376        kref_get(&sr->ref);
 377        return 0;
 378}
 379EXPORT_SYMBOL_GPL(wf_get_sensor);
 380
 381void wf_put_sensor(struct wf_sensor *sr)
 382{
 383        struct module *mod = sr->ops->owner;
 384        kref_put(&sr->ref, wf_sensor_release);
 385        module_put(mod);
 386}
 387EXPORT_SYMBOL_GPL(wf_put_sensor);
 388
 389
 390/*
 391 * Client & notification
 392 */
 393
 394int wf_register_client(struct notifier_block *nb)
 395{
 396        int rc;
 397        struct wf_control *ct;
 398        struct wf_sensor *sr;
 399
 400        mutex_lock(&wf_lock);
 401        rc = blocking_notifier_chain_register(&wf_client_list, nb);
 402        if (rc != 0)
 403                goto bail;
 404        wf_client_count++;
 405        list_for_each_entry(ct, &wf_controls, link)
 406                wf_notify(WF_EVENT_NEW_CONTROL, ct);
 407        list_for_each_entry(sr, &wf_sensors, link)
 408                wf_notify(WF_EVENT_NEW_SENSOR, sr);
 409        if (wf_client_count == 1)
 410                wf_start_thread();
 411 bail:
 412        mutex_unlock(&wf_lock);
 413        return rc;
 414}
 415EXPORT_SYMBOL_GPL(wf_register_client);
 416
 417int wf_unregister_client(struct notifier_block *nb)
 418{
 419        mutex_lock(&wf_lock);
 420        blocking_notifier_chain_unregister(&wf_client_list, nb);
 421        wf_client_count++;
 422        if (wf_client_count == 0)
 423                wf_stop_thread();
 424        mutex_unlock(&wf_lock);
 425
 426        return 0;
 427}
 428EXPORT_SYMBOL_GPL(wf_unregister_client);
 429
 430void wf_set_overtemp(void)
 431{
 432        mutex_lock(&wf_lock);
 433        wf_overtemp++;
 434        if (wf_overtemp == 1) {
 435                printk(KERN_WARNING "windfarm: Overtemp condition detected !\n");
 436                wf_overtemp_counter = 0;
 437                wf_notify(WF_EVENT_OVERTEMP, NULL);
 438        }
 439        mutex_unlock(&wf_lock);
 440}
 441EXPORT_SYMBOL_GPL(wf_set_overtemp);
 442
 443void wf_clear_overtemp(void)
 444{
 445        mutex_lock(&wf_lock);
 446        WARN_ON(wf_overtemp == 0);
 447        if (wf_overtemp == 0) {
 448                mutex_unlock(&wf_lock);
 449                return;
 450        }
 451        wf_overtemp--;
 452        if (wf_overtemp == 0) {
 453                printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n");
 454                wf_notify(WF_EVENT_NORMALTEMP, NULL);
 455        }
 456        mutex_unlock(&wf_lock);
 457}
 458EXPORT_SYMBOL_GPL(wf_clear_overtemp);
 459
 460int wf_is_overtemp(void)
 461{
 462        return (wf_overtemp != 0);
 463}
 464EXPORT_SYMBOL_GPL(wf_is_overtemp);
 465
 466static int __init windfarm_core_init(void)
 467{
 468        DBG("wf: core loaded\n");
 469
 470        /* Don't register on old machines that use therm_pm72 for now */
 471        if (machine_is_compatible("PowerMac7,2") ||
 472            machine_is_compatible("PowerMac7,3") ||
 473            machine_is_compatible("RackMac3,1"))
 474                return -ENODEV;
 475        platform_device_register(&wf_platform_device);
 476        return 0;
 477}
 478
 479static void __exit windfarm_core_exit(void)
 480{
 481        BUG_ON(wf_client_count != 0);
 482
 483        DBG("wf: core unloaded\n");
 484
 485        platform_device_unregister(&wf_platform_device);
 486}
 487
 488
 489module_init(windfarm_core_init);
 490module_exit(windfarm_core_exit);
 491
 492MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 493MODULE_DESCRIPTION("Core component of PowerMac thermal control");
 494MODULE_LICENSE("GPL");
 495
 496