Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Windfarm PowerMac thermal control. Core
0004  *
0005  * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
0006  *                    <benh@kernel.crashing.org>
0007  *
0008  * This core code tracks the list of sensors & controls, register
0009  * clients, and holds the kernel thread used for control.
0010  *
0011  * TODO:
0012  *
0013  * Add some information about sensor/control type and data format to
0014  * sensors/controls, and have the sysfs attribute stuff be moved
0015  * generically here instead of hard coded in the platform specific
0016  * driver as it us currently
0017  *
0018  * This however requires solving some annoying lifetime issues with
0019  * sysfs which doesn't seem to have lifetime rules for struct attribute,
0020  * I may have to create full features kobjects for every sensor/control
0021  * instead which is a bit of an overkill imho
0022  */
0023 
0024 #include <linux/types.h>
0025 #include <linux/errno.h>
0026 #include <linux/kernel.h>
0027 #include <linux/slab.h>
0028 #include <linux/init.h>
0029 #include <linux/spinlock.h>
0030 #include <linux/kthread.h>
0031 #include <linux/jiffies.h>
0032 #include <linux/reboot.h>
0033 #include <linux/device.h>
0034 #include <linux/platform_device.h>
0035 #include <linux/mutex.h>
0036 #include <linux/freezer.h>
0037 
0038 #include "windfarm.h"
0039 
0040 #define VERSION "0.2"
0041 
0042 #undef DEBUG
0043 
0044 #ifdef DEBUG
0045 #define DBG(args...)    printk(args)
0046 #else
0047 #define DBG(args...)    do { } while(0)
0048 #endif
0049 
0050 static LIST_HEAD(wf_controls);
0051 static LIST_HEAD(wf_sensors);
0052 static DEFINE_MUTEX(wf_lock);
0053 static BLOCKING_NOTIFIER_HEAD(wf_client_list);
0054 static int wf_client_count;
0055 static unsigned int wf_overtemp;
0056 static unsigned int wf_overtemp_counter;
0057 static struct task_struct *wf_thread;
0058 
0059 static struct platform_device wf_platform_device = {
0060     .name   = "windfarm",
0061 };
0062 
0063 /*
0064  * Utilities & tick thread
0065  */
0066 
0067 static inline void wf_notify(int event, void *param)
0068 {
0069     blocking_notifier_call_chain(&wf_client_list, event, param);
0070 }
0071 
0072 static int wf_critical_overtemp(void)
0073 {
0074     static char const critical_overtemp_path[] = "/sbin/critical_overtemp";
0075     char *argv[] = { (char *)critical_overtemp_path, NULL };
0076     static char *envp[] = { "HOME=/",
0077                 "TERM=linux",
0078                 "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
0079                 NULL };
0080 
0081     return call_usermodehelper(critical_overtemp_path,
0082                    argv, envp, UMH_WAIT_EXEC);
0083 }
0084 
0085 static int wf_thread_func(void *data)
0086 {
0087     unsigned long next, delay;
0088 
0089     next = jiffies;
0090 
0091     DBG("wf: thread started\n");
0092 
0093     set_freezable();
0094     while (!kthread_should_stop()) {
0095         try_to_freeze();
0096 
0097         if (time_after_eq(jiffies, next)) {
0098             wf_notify(WF_EVENT_TICK, NULL);
0099             if (wf_overtemp) {
0100                 wf_overtemp_counter++;
0101                 /* 10 seconds overtemp, notify userland */
0102                 if (wf_overtemp_counter > 10)
0103                     wf_critical_overtemp();
0104                 /* 30 seconds, shutdown */
0105                 if (wf_overtemp_counter > 30) {
0106                     printk(KERN_ERR "windfarm: Overtemp "
0107                            "for more than 30"
0108                            " seconds, shutting down\n");
0109                     machine_power_off();
0110                 }
0111             }
0112             next += HZ;
0113         }
0114 
0115         delay = next - jiffies;
0116         if (delay <= HZ)
0117             schedule_timeout_interruptible(delay);
0118     }
0119 
0120     DBG("wf: thread stopped\n");
0121 
0122     return 0;
0123 }
0124 
0125 static void wf_start_thread(void)
0126 {
0127     wf_thread = kthread_run(wf_thread_func, NULL, "kwindfarm");
0128     if (IS_ERR(wf_thread)) {
0129         printk(KERN_ERR "windfarm: failed to create thread,err %ld\n",
0130                PTR_ERR(wf_thread));
0131         wf_thread = NULL;
0132     }
0133 }
0134 
0135 
0136 static void wf_stop_thread(void)
0137 {
0138     if (wf_thread)
0139         kthread_stop(wf_thread);
0140     wf_thread = NULL;
0141 }
0142 
0143 /*
0144  * Controls
0145  */
0146 
0147 static void wf_control_release(struct kref *kref)
0148 {
0149     struct wf_control *ct = container_of(kref, struct wf_control, ref);
0150 
0151     DBG("wf: Deleting control %s\n", ct->name);
0152 
0153     if (ct->ops && ct->ops->release)
0154         ct->ops->release(ct);
0155     else
0156         kfree(ct);
0157 }
0158 
0159 static ssize_t wf_show_control(struct device *dev,
0160                    struct device_attribute *attr, char *buf)
0161 {
0162     struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
0163     const char *typestr;
0164     s32 val = 0;
0165     int err;
0166 
0167     err = ctrl->ops->get_value(ctrl, &val);
0168     if (err < 0) {
0169         if (err == -EFAULT)
0170             return sprintf(buf, "<HW FAULT>\n");
0171         return err;
0172     }
0173     switch(ctrl->type) {
0174     case WF_CONTROL_RPM_FAN:
0175         typestr = " RPM";
0176         break;
0177     case WF_CONTROL_PWM_FAN:
0178         typestr = " %";
0179         break;
0180     default:
0181         typestr = "";
0182     }
0183     return sprintf(buf, "%d%s\n", val, typestr);
0184 }
0185 
0186 /* This is really only for debugging... */
0187 static ssize_t wf_store_control(struct device *dev,
0188                 struct device_attribute *attr,
0189                 const char *buf, size_t count)
0190 {
0191     struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
0192     int val;
0193     int err;
0194     char *endp;
0195 
0196     val = simple_strtoul(buf, &endp, 0);
0197     while (endp < buf + count && (*endp == ' ' || *endp == '\n'))
0198         ++endp;
0199     if (endp - buf < count)
0200         return -EINVAL;
0201     err = ctrl->ops->set_value(ctrl, val);
0202     if (err < 0)
0203         return err;
0204     return count;
0205 }
0206 
0207 int wf_register_control(struct wf_control *new_ct)
0208 {
0209     struct wf_control *ct;
0210 
0211     mutex_lock(&wf_lock);
0212     list_for_each_entry(ct, &wf_controls, link) {
0213         if (!strcmp(ct->name, new_ct->name)) {
0214             printk(KERN_WARNING "windfarm: trying to register"
0215                    " duplicate control %s\n", ct->name);
0216             mutex_unlock(&wf_lock);
0217             return -EEXIST;
0218         }
0219     }
0220     kref_init(&new_ct->ref);
0221     list_add(&new_ct->link, &wf_controls);
0222 
0223     sysfs_attr_init(&new_ct->attr.attr);
0224     new_ct->attr.attr.name = new_ct->name;
0225     new_ct->attr.attr.mode = 0644;
0226     new_ct->attr.show = wf_show_control;
0227     new_ct->attr.store = wf_store_control;
0228     if (device_create_file(&wf_platform_device.dev, &new_ct->attr))
0229         printk(KERN_WARNING "windfarm: device_create_file failed"
0230             " for %s\n", new_ct->name);
0231         /* the subsystem still does useful work without the file */
0232 
0233     DBG("wf: Registered control %s\n", new_ct->name);
0234 
0235     wf_notify(WF_EVENT_NEW_CONTROL, new_ct);
0236     mutex_unlock(&wf_lock);
0237 
0238     return 0;
0239 }
0240 EXPORT_SYMBOL_GPL(wf_register_control);
0241 
0242 void wf_unregister_control(struct wf_control *ct)
0243 {
0244     mutex_lock(&wf_lock);
0245     list_del(&ct->link);
0246     mutex_unlock(&wf_lock);
0247 
0248     DBG("wf: Unregistered control %s\n", ct->name);
0249 
0250     kref_put(&ct->ref, wf_control_release);
0251 }
0252 EXPORT_SYMBOL_GPL(wf_unregister_control);
0253 
0254 int wf_get_control(struct wf_control *ct)
0255 {
0256     if (!try_module_get(ct->ops->owner))
0257         return -ENODEV;
0258     kref_get(&ct->ref);
0259     return 0;
0260 }
0261 EXPORT_SYMBOL_GPL(wf_get_control);
0262 
0263 void wf_put_control(struct wf_control *ct)
0264 {
0265     struct module *mod = ct->ops->owner;
0266     kref_put(&ct->ref, wf_control_release);
0267     module_put(mod);
0268 }
0269 EXPORT_SYMBOL_GPL(wf_put_control);
0270 
0271 
0272 /*
0273  * Sensors
0274  */
0275 
0276 
0277 static void wf_sensor_release(struct kref *kref)
0278 {
0279     struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref);
0280 
0281     DBG("wf: Deleting sensor %s\n", sr->name);
0282 
0283     if (sr->ops && sr->ops->release)
0284         sr->ops->release(sr);
0285     else
0286         kfree(sr);
0287 }
0288 
0289 static ssize_t wf_show_sensor(struct device *dev,
0290                   struct device_attribute *attr, char *buf)
0291 {
0292     struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr);
0293     s32 val = 0;
0294     int err;
0295 
0296     err = sens->ops->get_value(sens, &val);
0297     if (err < 0)
0298         return err;
0299     return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val));
0300 }
0301 
0302 int wf_register_sensor(struct wf_sensor *new_sr)
0303 {
0304     struct wf_sensor *sr;
0305 
0306     mutex_lock(&wf_lock);
0307     list_for_each_entry(sr, &wf_sensors, link) {
0308         if (!strcmp(sr->name, new_sr->name)) {
0309             printk(KERN_WARNING "windfarm: trying to register"
0310                    " duplicate sensor %s\n", sr->name);
0311             mutex_unlock(&wf_lock);
0312             return -EEXIST;
0313         }
0314     }
0315     kref_init(&new_sr->ref);
0316     list_add(&new_sr->link, &wf_sensors);
0317 
0318     sysfs_attr_init(&new_sr->attr.attr);
0319     new_sr->attr.attr.name = new_sr->name;
0320     new_sr->attr.attr.mode = 0444;
0321     new_sr->attr.show = wf_show_sensor;
0322     new_sr->attr.store = NULL;
0323     if (device_create_file(&wf_platform_device.dev, &new_sr->attr))
0324         printk(KERN_WARNING "windfarm: device_create_file failed"
0325             " for %s\n", new_sr->name);
0326         /* the subsystem still does useful work without the file */
0327 
0328     DBG("wf: Registered sensor %s\n", new_sr->name);
0329 
0330     wf_notify(WF_EVENT_NEW_SENSOR, new_sr);
0331     mutex_unlock(&wf_lock);
0332 
0333     return 0;
0334 }
0335 EXPORT_SYMBOL_GPL(wf_register_sensor);
0336 
0337 void wf_unregister_sensor(struct wf_sensor *sr)
0338 {
0339     mutex_lock(&wf_lock);
0340     list_del(&sr->link);
0341     mutex_unlock(&wf_lock);
0342 
0343     DBG("wf: Unregistered sensor %s\n", sr->name);
0344 
0345     wf_put_sensor(sr);
0346 }
0347 EXPORT_SYMBOL_GPL(wf_unregister_sensor);
0348 
0349 int wf_get_sensor(struct wf_sensor *sr)
0350 {
0351     if (!try_module_get(sr->ops->owner))
0352         return -ENODEV;
0353     kref_get(&sr->ref);
0354     return 0;
0355 }
0356 EXPORT_SYMBOL_GPL(wf_get_sensor);
0357 
0358 void wf_put_sensor(struct wf_sensor *sr)
0359 {
0360     struct module *mod = sr->ops->owner;
0361     kref_put(&sr->ref, wf_sensor_release);
0362     module_put(mod);
0363 }
0364 EXPORT_SYMBOL_GPL(wf_put_sensor);
0365 
0366 
0367 /*
0368  * Client & notification
0369  */
0370 
0371 int wf_register_client(struct notifier_block *nb)
0372 {
0373     int rc;
0374     struct wf_control *ct;
0375     struct wf_sensor *sr;
0376 
0377     mutex_lock(&wf_lock);
0378     rc = blocking_notifier_chain_register(&wf_client_list, nb);
0379     if (rc != 0)
0380         goto bail;
0381     wf_client_count++;
0382     list_for_each_entry(ct, &wf_controls, link)
0383         wf_notify(WF_EVENT_NEW_CONTROL, ct);
0384     list_for_each_entry(sr, &wf_sensors, link)
0385         wf_notify(WF_EVENT_NEW_SENSOR, sr);
0386     if (wf_client_count == 1)
0387         wf_start_thread();
0388  bail:
0389     mutex_unlock(&wf_lock);
0390     return rc;
0391 }
0392 EXPORT_SYMBOL_GPL(wf_register_client);
0393 
0394 int wf_unregister_client(struct notifier_block *nb)
0395 {
0396     mutex_lock(&wf_lock);
0397     blocking_notifier_chain_unregister(&wf_client_list, nb);
0398     wf_client_count--;
0399     if (wf_client_count == 0)
0400         wf_stop_thread();
0401     mutex_unlock(&wf_lock);
0402 
0403     return 0;
0404 }
0405 EXPORT_SYMBOL_GPL(wf_unregister_client);
0406 
0407 void wf_set_overtemp(void)
0408 {
0409     mutex_lock(&wf_lock);
0410     wf_overtemp++;
0411     if (wf_overtemp == 1) {
0412         printk(KERN_WARNING "windfarm: Overtemp condition detected !\n");
0413         wf_overtemp_counter = 0;
0414         wf_notify(WF_EVENT_OVERTEMP, NULL);
0415     }
0416     mutex_unlock(&wf_lock);
0417 }
0418 EXPORT_SYMBOL_GPL(wf_set_overtemp);
0419 
0420 void wf_clear_overtemp(void)
0421 {
0422     mutex_lock(&wf_lock);
0423     WARN_ON(wf_overtemp == 0);
0424     if (wf_overtemp == 0) {
0425         mutex_unlock(&wf_lock);
0426         return;
0427     }
0428     wf_overtemp--;
0429     if (wf_overtemp == 0) {
0430         printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n");
0431         wf_notify(WF_EVENT_NORMALTEMP, NULL);
0432     }
0433     mutex_unlock(&wf_lock);
0434 }
0435 EXPORT_SYMBOL_GPL(wf_clear_overtemp);
0436 
0437 static int __init windfarm_core_init(void)
0438 {
0439     DBG("wf: core loaded\n");
0440 
0441     platform_device_register(&wf_platform_device);
0442     return 0;
0443 }
0444 
0445 static void __exit windfarm_core_exit(void)
0446 {
0447     BUG_ON(wf_client_count != 0);
0448 
0449     DBG("wf: core unloaded\n");
0450 
0451     platform_device_unregister(&wf_platform_device);
0452 }
0453 
0454 
0455 module_init(windfarm_core_init);
0456 module_exit(windfarm_core_exit);
0457 
0458 MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
0459 MODULE_DESCRIPTION("Core component of PowerMac thermal control");
0460 MODULE_LICENSE("GPL");
0461