Path: blob/master/drivers/macintosh/windfarm_core.c
15112 views
/*1* Windfarm PowerMac thermal control. Core2*3* (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.4* <[email protected]>5*6* Released under the term of the GNU GPL v2.7*8* This core code tracks the list of sensors & controls, register9* clients, and holds the kernel thread used for control.10*11* TODO:12*13* Add some information about sensor/control type and data format to14* sensors/controls, and have the sysfs attribute stuff be moved15* generically here instead of hard coded in the platform specific16* driver as it us currently17*18* This however requires solving some annoying lifetime issues with19* sysfs which doesn't seem to have lifetime rules for struct attribute,20* I may have to create full features kobjects for every sensor/control21* instead which is a bit of an overkill imho22*/2324#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>3738#include <asm/prom.h>3940#include "windfarm.h"4142#define VERSION "0.2"4344#undef DEBUG4546#ifdef DEBUG47#define DBG(args...) printk(args)48#else49#define DBG(args...) do { } while(0)50#endif5152static 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;6061static struct platform_device wf_platform_device = {62.name = "windfarm",63};6465/*66* Utilities & tick thread67*/6869static inline void wf_notify(int event, void *param)70{71blocking_notifier_call_chain(&wf_client_list, event, param);72}7374int wf_critical_overtemp(void)75{76static char * critical_overtemp_path = "/sbin/critical_overtemp";77char *argv[] = { critical_overtemp_path, NULL };78static char *envp[] = { "HOME=/",79"TERM=linux",80"PATH=/sbin:/usr/sbin:/bin:/usr/bin",81NULL };8283return call_usermodehelper(critical_overtemp_path,84argv, envp, UMH_WAIT_EXEC);85}86EXPORT_SYMBOL_GPL(wf_critical_overtemp);8788static int wf_thread_func(void *data)89{90unsigned long next, delay;9192next = jiffies;9394DBG("wf: thread started\n");9596set_freezable();97while (!kthread_should_stop()) {98try_to_freeze();99100if (time_after_eq(jiffies, next)) {101wf_notify(WF_EVENT_TICK, NULL);102if (wf_overtemp) {103wf_overtemp_counter++;104/* 10 seconds overtemp, notify userland */105if (wf_overtemp_counter > 10)106wf_critical_overtemp();107/* 30 seconds, shutdown */108if (wf_overtemp_counter > 30) {109printk(KERN_ERR "windfarm: Overtemp "110"for more than 30"111" seconds, shutting down\n");112machine_power_off();113}114}115next += HZ;116}117118delay = next - jiffies;119if (delay <= HZ)120schedule_timeout_interruptible(delay);121}122123DBG("wf: thread stopped\n");124125return 0;126}127128static void wf_start_thread(void)129{130wf_thread = kthread_run(wf_thread_func, NULL, "kwindfarm");131if (IS_ERR(wf_thread)) {132printk(KERN_ERR "windfarm: failed to create thread,err %ld\n",133PTR_ERR(wf_thread));134wf_thread = NULL;135}136}137138139static void wf_stop_thread(void)140{141if (wf_thread)142kthread_stop(wf_thread);143wf_thread = NULL;144}145146/*147* Controls148*/149150static void wf_control_release(struct kref *kref)151{152struct wf_control *ct = container_of(kref, struct wf_control, ref);153154DBG("wf: Deleting control %s\n", ct->name);155156if (ct->ops && ct->ops->release)157ct->ops->release(ct);158else159kfree(ct);160}161162static ssize_t wf_show_control(struct device *dev,163struct device_attribute *attr, char *buf)164{165struct wf_control *ctrl = container_of(attr, struct wf_control, attr);166s32 val = 0;167int err;168169err = ctrl->ops->get_value(ctrl, &val);170if (err < 0)171return err;172return sprintf(buf, "%d\n", val);173}174175/* This is really only for debugging... */176static ssize_t wf_store_control(struct device *dev,177struct device_attribute *attr,178const char *buf, size_t count)179{180struct wf_control *ctrl = container_of(attr, struct wf_control, attr);181int val;182int err;183char *endp;184185val = simple_strtoul(buf, &endp, 0);186while (endp < buf + count && (*endp == ' ' || *endp == '\n'))187++endp;188if (endp - buf < count)189return -EINVAL;190err = ctrl->ops->set_value(ctrl, val);191if (err < 0)192return err;193return count;194}195196int wf_register_control(struct wf_control *new_ct)197{198struct wf_control *ct;199200mutex_lock(&wf_lock);201list_for_each_entry(ct, &wf_controls, link) {202if (!strcmp(ct->name, new_ct->name)) {203printk(KERN_WARNING "windfarm: trying to register"204" duplicate control %s\n", ct->name);205mutex_unlock(&wf_lock);206return -EEXIST;207}208}209kref_init(&new_ct->ref);210list_add(&new_ct->link, &wf_controls);211212sysfs_attr_init(&new_ct->attr.attr);213new_ct->attr.attr.name = new_ct->name;214new_ct->attr.attr.mode = 0644;215new_ct->attr.show = wf_show_control;216new_ct->attr.store = wf_store_control;217if (device_create_file(&wf_platform_device.dev, &new_ct->attr))218printk(KERN_WARNING "windfarm: device_create_file failed"219" for %s\n", new_ct->name);220/* the subsystem still does useful work without the file */221222DBG("wf: Registered control %s\n", new_ct->name);223224wf_notify(WF_EVENT_NEW_CONTROL, new_ct);225mutex_unlock(&wf_lock);226227return 0;228}229EXPORT_SYMBOL_GPL(wf_register_control);230231void wf_unregister_control(struct wf_control *ct)232{233mutex_lock(&wf_lock);234list_del(&ct->link);235mutex_unlock(&wf_lock);236237DBG("wf: Unregistered control %s\n", ct->name);238239kref_put(&ct->ref, wf_control_release);240}241EXPORT_SYMBOL_GPL(wf_unregister_control);242243struct wf_control * wf_find_control(const char *name)244{245struct wf_control *ct;246247mutex_lock(&wf_lock);248list_for_each_entry(ct, &wf_controls, link) {249if (!strcmp(ct->name, name)) {250if (wf_get_control(ct))251ct = NULL;252mutex_unlock(&wf_lock);253return ct;254}255}256mutex_unlock(&wf_lock);257return NULL;258}259EXPORT_SYMBOL_GPL(wf_find_control);260261int wf_get_control(struct wf_control *ct)262{263if (!try_module_get(ct->ops->owner))264return -ENODEV;265kref_get(&ct->ref);266return 0;267}268EXPORT_SYMBOL_GPL(wf_get_control);269270void wf_put_control(struct wf_control *ct)271{272struct module *mod = ct->ops->owner;273kref_put(&ct->ref, wf_control_release);274module_put(mod);275}276EXPORT_SYMBOL_GPL(wf_put_control);277278279/*280* Sensors281*/282283284static void wf_sensor_release(struct kref *kref)285{286struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref);287288DBG("wf: Deleting sensor %s\n", sr->name);289290if (sr->ops && sr->ops->release)291sr->ops->release(sr);292else293kfree(sr);294}295296static ssize_t wf_show_sensor(struct device *dev,297struct device_attribute *attr, char *buf)298{299struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr);300s32 val = 0;301int err;302303err = sens->ops->get_value(sens, &val);304if (err < 0)305return err;306return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val));307}308309int wf_register_sensor(struct wf_sensor *new_sr)310{311struct wf_sensor *sr;312313mutex_lock(&wf_lock);314list_for_each_entry(sr, &wf_sensors, link) {315if (!strcmp(sr->name, new_sr->name)) {316printk(KERN_WARNING "windfarm: trying to register"317" duplicate sensor %s\n", sr->name);318mutex_unlock(&wf_lock);319return -EEXIST;320}321}322kref_init(&new_sr->ref);323list_add(&new_sr->link, &wf_sensors);324325sysfs_attr_init(&new_sr->attr.attr);326new_sr->attr.attr.name = new_sr->name;327new_sr->attr.attr.mode = 0444;328new_sr->attr.show = wf_show_sensor;329new_sr->attr.store = NULL;330if (device_create_file(&wf_platform_device.dev, &new_sr->attr))331printk(KERN_WARNING "windfarm: device_create_file failed"332" for %s\n", new_sr->name);333/* the subsystem still does useful work without the file */334335DBG("wf: Registered sensor %s\n", new_sr->name);336337wf_notify(WF_EVENT_NEW_SENSOR, new_sr);338mutex_unlock(&wf_lock);339340return 0;341}342EXPORT_SYMBOL_GPL(wf_register_sensor);343344void wf_unregister_sensor(struct wf_sensor *sr)345{346mutex_lock(&wf_lock);347list_del(&sr->link);348mutex_unlock(&wf_lock);349350DBG("wf: Unregistered sensor %s\n", sr->name);351352wf_put_sensor(sr);353}354EXPORT_SYMBOL_GPL(wf_unregister_sensor);355356struct wf_sensor * wf_find_sensor(const char *name)357{358struct wf_sensor *sr;359360mutex_lock(&wf_lock);361list_for_each_entry(sr, &wf_sensors, link) {362if (!strcmp(sr->name, name)) {363if (wf_get_sensor(sr))364sr = NULL;365mutex_unlock(&wf_lock);366return sr;367}368}369mutex_unlock(&wf_lock);370return NULL;371}372EXPORT_SYMBOL_GPL(wf_find_sensor);373374int wf_get_sensor(struct wf_sensor *sr)375{376if (!try_module_get(sr->ops->owner))377return -ENODEV;378kref_get(&sr->ref);379return 0;380}381EXPORT_SYMBOL_GPL(wf_get_sensor);382383void wf_put_sensor(struct wf_sensor *sr)384{385struct module *mod = sr->ops->owner;386kref_put(&sr->ref, wf_sensor_release);387module_put(mod);388}389EXPORT_SYMBOL_GPL(wf_put_sensor);390391392/*393* Client & notification394*/395396int wf_register_client(struct notifier_block *nb)397{398int rc;399struct wf_control *ct;400struct wf_sensor *sr;401402mutex_lock(&wf_lock);403rc = blocking_notifier_chain_register(&wf_client_list, nb);404if (rc != 0)405goto bail;406wf_client_count++;407list_for_each_entry(ct, &wf_controls, link)408wf_notify(WF_EVENT_NEW_CONTROL, ct);409list_for_each_entry(sr, &wf_sensors, link)410wf_notify(WF_EVENT_NEW_SENSOR, sr);411if (wf_client_count == 1)412wf_start_thread();413bail:414mutex_unlock(&wf_lock);415return rc;416}417EXPORT_SYMBOL_GPL(wf_register_client);418419int wf_unregister_client(struct notifier_block *nb)420{421mutex_lock(&wf_lock);422blocking_notifier_chain_unregister(&wf_client_list, nb);423wf_client_count++;424if (wf_client_count == 0)425wf_stop_thread();426mutex_unlock(&wf_lock);427428return 0;429}430EXPORT_SYMBOL_GPL(wf_unregister_client);431432void wf_set_overtemp(void)433{434mutex_lock(&wf_lock);435wf_overtemp++;436if (wf_overtemp == 1) {437printk(KERN_WARNING "windfarm: Overtemp condition detected !\n");438wf_overtemp_counter = 0;439wf_notify(WF_EVENT_OVERTEMP, NULL);440}441mutex_unlock(&wf_lock);442}443EXPORT_SYMBOL_GPL(wf_set_overtemp);444445void wf_clear_overtemp(void)446{447mutex_lock(&wf_lock);448WARN_ON(wf_overtemp == 0);449if (wf_overtemp == 0) {450mutex_unlock(&wf_lock);451return;452}453wf_overtemp--;454if (wf_overtemp == 0) {455printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n");456wf_notify(WF_EVENT_NORMALTEMP, NULL);457}458mutex_unlock(&wf_lock);459}460EXPORT_SYMBOL_GPL(wf_clear_overtemp);461462int wf_is_overtemp(void)463{464return (wf_overtemp != 0);465}466EXPORT_SYMBOL_GPL(wf_is_overtemp);467468static int __init windfarm_core_init(void)469{470DBG("wf: core loaded\n");471472/* Don't register on old machines that use therm_pm72 for now */473if (of_machine_is_compatible("PowerMac7,2") ||474of_machine_is_compatible("PowerMac7,3") ||475of_machine_is_compatible("RackMac3,1"))476return -ENODEV;477platform_device_register(&wf_platform_device);478return 0;479}480481static void __exit windfarm_core_exit(void)482{483BUG_ON(wf_client_count != 0);484485DBG("wf: core unloaded\n");486487platform_device_unregister(&wf_platform_device);488}489490491module_init(windfarm_core_init);492module_exit(windfarm_core_exit);493494MODULE_AUTHOR("Benjamin Herrenschmidt <[email protected]>");495MODULE_DESCRIPTION("Core component of PowerMac thermal control");496MODULE_LICENSE("GPL");497498499500