linux/drivers/macintosh/windfarm_core.c
<<
>>
Prefs
   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 <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
  74static int wf_critical_overtemp(void)
  75{
  76        static char const critical_overtemp_path[] = "/sbin/critical_overtemp";
  77        char *argv[] = { (char *)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}
  86
  87static int wf_thread_func(void *data)
  88{
  89        unsigned long next, delay;
  90
  91        next = jiffies;
  92
  93        DBG("wf: thread started\n");
  94
  95        set_freezable();
  96        while (!kthread_should_stop()) {
  97                try_to_freeze();
  98
  99                if (time_after_eq(jiffies, next)) {
 100                        wf_notify(WF_EVENT_TICK, NULL);
 101                        if (wf_overtemp) {
 102                                wf_overtemp_counter++;
 103                                /* 10 seconds overtemp, notify userland */
 104                                if (wf_overtemp_counter > 10)
 105                                        wf_critical_overtemp();
 106                                /* 30 seconds, shutdown */
 107                                if (wf_overtemp_counter > 30) {
 108                                        printk(KERN_ERR "windfarm: Overtemp "
 109                                               "for more than 30"
 110                                               " seconds, shutting down\n");
 111                                        machine_power_off();
 112                                }
 113                        }
 114                        next += HZ;
 115                }
 116
 117                delay = next - jiffies;
 118                if (delay <= HZ)
 119                        schedule_timeout_interruptible(delay);
 120        }
 121
 122        DBG("wf: thread stopped\n");
 123
 124        return 0;
 125}
 126
 127static void wf_start_thread(void)
 128{
 129        wf_thread = kthread_run(wf_thread_func, NULL, "kwindfarm");
 130        if (IS_ERR(wf_thread)) {
 131                printk(KERN_ERR "windfarm: failed to create thread,err %ld\n",
 132                       PTR_ERR(wf_thread));
 133                wf_thread = NULL;
 134        }
 135}
 136
 137
 138static void wf_stop_thread(void)
 139{
 140        if (wf_thread)
 141                kthread_stop(wf_thread);
 142        wf_thread = NULL;
 143}
 144
 145/*
 146 * Controls
 147 */
 148
 149static void wf_control_release(struct kref *kref)
 150{
 151        struct wf_control *ct = container_of(kref, struct wf_control, ref);
 152
 153        DBG("wf: Deleting control %s\n", ct->name);
 154
 155        if (ct->ops && ct->ops->release)
 156                ct->ops->release(ct);
 157        else
 158                kfree(ct);
 159}
 160
 161static ssize_t wf_show_control(struct device *dev,
 162                               struct device_attribute *attr, char *buf)
 163{
 164        struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
 165        const char *typestr;
 166        s32 val = 0;
 167        int err;
 168
 169        err = ctrl->ops->get_value(ctrl, &val);
 170        if (err < 0) {
 171                if (err == -EFAULT)
 172                        return sprintf(buf, "<HW FAULT>\n");
 173                return err;
 174        }
 175        switch(ctrl->type) {
 176        case WF_CONTROL_RPM_FAN:
 177                typestr = " RPM";
 178                break;
 179        case WF_CONTROL_PWM_FAN:
 180                typestr = " %";
 181                break;
 182        default:
 183                typestr = "";
 184        }
 185        return sprintf(buf, "%d%s\n", val, typestr);
 186}
 187
 188/* This is really only for debugging... */
 189static ssize_t wf_store_control(struct device *dev,
 190                                struct device_attribute *attr,
 191                                const char *buf, size_t count)
 192{
 193        struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
 194        int val;
 195        int err;
 196        char *endp;
 197
 198        val = simple_strtoul(buf, &endp, 0);
 199        while (endp < buf + count && (*endp == ' ' || *endp == '\n'))
 200                ++endp;
 201        if (endp - buf < count)
 202                return -EINVAL;
 203        err = ctrl->ops->set_value(ctrl, val);
 204        if (err < 0)
 205                return err;
 206        return count;
 207}
 208
 209int wf_register_control(struct wf_control *new_ct)
 210{
 211        struct wf_control *ct;
 212
 213        mutex_lock(&wf_lock);
 214        list_for_each_entry(ct, &wf_controls, link) {
 215                if (!strcmp(ct->name, new_ct->name)) {
 216                        printk(KERN_WARNING "windfarm: trying to register"
 217                               " duplicate control %s\n", ct->name);
 218                        mutex_unlock(&wf_lock);
 219                        return -EEXIST;
 220                }
 221        }
 222        kref_init(&new_ct->ref);
 223        list_add(&new_ct->link, &wf_controls);
 224
 225        sysfs_attr_init(&new_ct->attr.attr);
 226        new_ct->attr.attr.name = new_ct->name;
 227        new_ct->attr.attr.mode = 0644;
 228        new_ct->attr.show = wf_show_control;
 229        new_ct->attr.store = wf_store_control;
 230        if (device_create_file(&wf_platform_device.dev, &new_ct->attr))
 231                printk(KERN_WARNING "windfarm: device_create_file failed"
 232                        " for %s\n", new_ct->name);
 233                /* the subsystem still does useful work without the file */
 234
 235        DBG("wf: Registered control %s\n", new_ct->name);
 236
 237        wf_notify(WF_EVENT_NEW_CONTROL, new_ct);
 238        mutex_unlock(&wf_lock);
 239
 240        return 0;
 241}
 242EXPORT_SYMBOL_GPL(wf_register_control);
 243
 244void wf_unregister_control(struct wf_control *ct)
 245{
 246        mutex_lock(&wf_lock);
 247        list_del(&ct->link);
 248        mutex_unlock(&wf_lock);
 249
 250        DBG("wf: Unregistered control %s\n", ct->name);
 251
 252        kref_put(&ct->ref, wf_control_release);
 253}
 254EXPORT_SYMBOL_GPL(wf_unregister_control);
 255
 256int wf_get_control(struct wf_control *ct)
 257{
 258        if (!try_module_get(ct->ops->owner))
 259                return -ENODEV;
 260        kref_get(&ct->ref);
 261        return 0;
 262}
 263EXPORT_SYMBOL_GPL(wf_get_control);
 264
 265void wf_put_control(struct wf_control *ct)
 266{
 267        struct module *mod = ct->ops->owner;
 268        kref_put(&ct->ref, wf_control_release);
 269        module_put(mod);
 270}
 271EXPORT_SYMBOL_GPL(wf_put_control);
 272
 273
 274/*
 275 * Sensors
 276 */
 277
 278
 279static void wf_sensor_release(struct kref *kref)
 280{
 281        struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref);
 282
 283        DBG("wf: Deleting sensor %s\n", sr->name);
 284
 285        if (sr->ops && sr->ops->release)
 286                sr->ops->release(sr);
 287        else
 288                kfree(sr);
 289}
 290
 291static ssize_t wf_show_sensor(struct device *dev,
 292                              struct device_attribute *attr, char *buf)
 293{
 294        struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr);
 295        s32 val = 0;
 296        int err;
 297
 298        err = sens->ops->get_value(sens, &val);
 299        if (err < 0)
 300                return err;
 301        return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val));
 302}
 303
 304int wf_register_sensor(struct wf_sensor *new_sr)
 305{
 306        struct wf_sensor *sr;
 307
 308        mutex_lock(&wf_lock);
 309        list_for_each_entry(sr, &wf_sensors, link) {
 310                if (!strcmp(sr->name, new_sr->name)) {
 311                        printk(KERN_WARNING "windfarm: trying to register"
 312                               " duplicate sensor %s\n", sr->name);
 313                        mutex_unlock(&wf_lock);
 314                        return -EEXIST;
 315                }
 316        }
 317        kref_init(&new_sr->ref);
 318        list_add(&new_sr->link, &wf_sensors);
 319
 320        sysfs_attr_init(&new_sr->attr.attr);
 321        new_sr->attr.attr.name = new_sr->name;
 322        new_sr->attr.attr.mode = 0444;
 323        new_sr->attr.show = wf_show_sensor;
 324        new_sr->attr.store = NULL;
 325        if (device_create_file(&wf_platform_device.dev, &new_sr->attr))
 326                printk(KERN_WARNING "windfarm: device_create_file failed"
 327                        " for %s\n", new_sr->name);
 328                /* the subsystem still does useful work without the file */
 329
 330        DBG("wf: Registered sensor %s\n", new_sr->name);
 331
 332        wf_notify(WF_EVENT_NEW_SENSOR, new_sr);
 333        mutex_unlock(&wf_lock);
 334
 335        return 0;
 336}
 337EXPORT_SYMBOL_GPL(wf_register_sensor);
 338
 339void wf_unregister_sensor(struct wf_sensor *sr)
 340{
 341        mutex_lock(&wf_lock);
 342        list_del(&sr->link);
 343        mutex_unlock(&wf_lock);
 344
 345        DBG("wf: Unregistered sensor %s\n", sr->name);
 346
 347        wf_put_sensor(sr);
 348}
 349EXPORT_SYMBOL_GPL(wf_unregister_sensor);
 350
 351int wf_get_sensor(struct wf_sensor *sr)
 352{
 353        if (!try_module_get(sr->ops->owner))
 354                return -ENODEV;
 355        kref_get(&sr->ref);
 356        return 0;
 357}
 358EXPORT_SYMBOL_GPL(wf_get_sensor);
 359
 360void wf_put_sensor(struct wf_sensor *sr)
 361{
 362        struct module *mod = sr->ops->owner;
 363        kref_put(&sr->ref, wf_sensor_release);
 364        module_put(mod);
 365}
 366EXPORT_SYMBOL_GPL(wf_put_sensor);
 367
 368
 369/*
 370 * Client & notification
 371 */
 372
 373int wf_register_client(struct notifier_block *nb)
 374{
 375        int rc;
 376        struct wf_control *ct;
 377        struct wf_sensor *sr;
 378
 379        mutex_lock(&wf_lock);
 380        rc = blocking_notifier_chain_register(&wf_client_list, nb);
 381        if (rc != 0)
 382                goto bail;
 383        wf_client_count++;
 384        list_for_each_entry(ct, &wf_controls, link)
 385                wf_notify(WF_EVENT_NEW_CONTROL, ct);
 386        list_for_each_entry(sr, &wf_sensors, link)
 387                wf_notify(WF_EVENT_NEW_SENSOR, sr);
 388        if (wf_client_count == 1)
 389                wf_start_thread();
 390 bail:
 391        mutex_unlock(&wf_lock);
 392        return rc;
 393}
 394EXPORT_SYMBOL_GPL(wf_register_client);
 395
 396int wf_unregister_client(struct notifier_block *nb)
 397{
 398        mutex_lock(&wf_lock);
 399        blocking_notifier_chain_unregister(&wf_client_list, nb);
 400        wf_client_count--;
 401        if (wf_client_count == 0)
 402                wf_stop_thread();
 403        mutex_unlock(&wf_lock);
 404
 405        return 0;
 406}
 407EXPORT_SYMBOL_GPL(wf_unregister_client);
 408
 409void wf_set_overtemp(void)
 410{
 411        mutex_lock(&wf_lock);
 412        wf_overtemp++;
 413        if (wf_overtemp == 1) {
 414                printk(KERN_WARNING "windfarm: Overtemp condition detected !\n");
 415                wf_overtemp_counter = 0;
 416                wf_notify(WF_EVENT_OVERTEMP, NULL);
 417        }
 418        mutex_unlock(&wf_lock);
 419}
 420EXPORT_SYMBOL_GPL(wf_set_overtemp);
 421
 422void wf_clear_overtemp(void)
 423{
 424        mutex_lock(&wf_lock);
 425        WARN_ON(wf_overtemp == 0);
 426        if (wf_overtemp == 0) {
 427                mutex_unlock(&wf_lock);
 428                return;
 429        }
 430        wf_overtemp--;
 431        if (wf_overtemp == 0) {
 432                printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n");
 433                wf_notify(WF_EVENT_NORMALTEMP, NULL);
 434        }
 435        mutex_unlock(&wf_lock);
 436}
 437EXPORT_SYMBOL_GPL(wf_clear_overtemp);
 438
 439static int __init windfarm_core_init(void)
 440{
 441        DBG("wf: core loaded\n");
 442
 443        platform_device_register(&wf_platform_device);
 444        return 0;
 445}
 446
 447static void __exit windfarm_core_exit(void)
 448{
 449        BUG_ON(wf_client_count != 0);
 450
 451        DBG("wf: core unloaded\n");
 452
 453        platform_device_unregister(&wf_platform_device);
 454}
 455
 456
 457module_init(windfarm_core_init);
 458module_exit(windfarm_core_exit);
 459
 460MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
 461MODULE_DESCRIPTION("Core component of PowerMac thermal control");
 462MODULE_LICENSE("GPL");
 463
 464