18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Windfarm PowerMac thermal control. Core 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp. 68c2ecf20Sopenharmony_ci * <benh@kernel.crashing.org> 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * This core code tracks the list of sensors & controls, register 98c2ecf20Sopenharmony_ci * clients, and holds the kernel thread used for control. 108c2ecf20Sopenharmony_ci * 118c2ecf20Sopenharmony_ci * TODO: 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Add some information about sensor/control type and data format to 148c2ecf20Sopenharmony_ci * sensors/controls, and have the sysfs attribute stuff be moved 158c2ecf20Sopenharmony_ci * generically here instead of hard coded in the platform specific 168c2ecf20Sopenharmony_ci * driver as it us currently 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * This however requires solving some annoying lifetime issues with 198c2ecf20Sopenharmony_ci * sysfs which doesn't seem to have lifetime rules for struct attribute, 208c2ecf20Sopenharmony_ci * I may have to create full features kobjects for every sensor/control 218c2ecf20Sopenharmony_ci * instead which is a bit of an overkill imho 228c2ecf20Sopenharmony_ci */ 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <linux/types.h> 258c2ecf20Sopenharmony_ci#include <linux/errno.h> 268c2ecf20Sopenharmony_ci#include <linux/kernel.h> 278c2ecf20Sopenharmony_ci#include <linux/slab.h> 288c2ecf20Sopenharmony_ci#include <linux/init.h> 298c2ecf20Sopenharmony_ci#include <linux/spinlock.h> 308c2ecf20Sopenharmony_ci#include <linux/kthread.h> 318c2ecf20Sopenharmony_ci#include <linux/jiffies.h> 328c2ecf20Sopenharmony_ci#include <linux/reboot.h> 338c2ecf20Sopenharmony_ci#include <linux/device.h> 348c2ecf20Sopenharmony_ci#include <linux/platform_device.h> 358c2ecf20Sopenharmony_ci#include <linux/mutex.h> 368c2ecf20Sopenharmony_ci#include <linux/freezer.h> 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci#include <asm/prom.h> 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci#include "windfarm.h" 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci#define VERSION "0.2" 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci#undef DEBUG 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_ci#ifdef DEBUG 478c2ecf20Sopenharmony_ci#define DBG(args...) printk(args) 488c2ecf20Sopenharmony_ci#else 498c2ecf20Sopenharmony_ci#define DBG(args...) do { } while(0) 508c2ecf20Sopenharmony_ci#endif 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cistatic LIST_HEAD(wf_controls); 538c2ecf20Sopenharmony_cistatic LIST_HEAD(wf_sensors); 548c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(wf_lock); 558c2ecf20Sopenharmony_cistatic BLOCKING_NOTIFIER_HEAD(wf_client_list); 568c2ecf20Sopenharmony_cistatic int wf_client_count; 578c2ecf20Sopenharmony_cistatic unsigned int wf_overtemp; 588c2ecf20Sopenharmony_cistatic unsigned int wf_overtemp_counter; 598c2ecf20Sopenharmony_cistruct task_struct *wf_thread; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cistatic struct platform_device wf_platform_device = { 628c2ecf20Sopenharmony_ci .name = "windfarm", 638c2ecf20Sopenharmony_ci}; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* 668c2ecf20Sopenharmony_ci * Utilities & tick thread 678c2ecf20Sopenharmony_ci */ 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic inline void wf_notify(int event, void *param) 708c2ecf20Sopenharmony_ci{ 718c2ecf20Sopenharmony_ci blocking_notifier_call_chain(&wf_client_list, event, param); 728c2ecf20Sopenharmony_ci} 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_cistatic int wf_critical_overtemp(void) 758c2ecf20Sopenharmony_ci{ 768c2ecf20Sopenharmony_ci static char const critical_overtemp_path[] = "/sbin/critical_overtemp"; 778c2ecf20Sopenharmony_ci char *argv[] = { (char *)critical_overtemp_path, NULL }; 788c2ecf20Sopenharmony_ci static char *envp[] = { "HOME=/", 798c2ecf20Sopenharmony_ci "TERM=linux", 808c2ecf20Sopenharmony_ci "PATH=/sbin:/usr/sbin:/bin:/usr/bin", 818c2ecf20Sopenharmony_ci NULL }; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci return call_usermodehelper(critical_overtemp_path, 848c2ecf20Sopenharmony_ci argv, envp, UMH_WAIT_EXEC); 858c2ecf20Sopenharmony_ci} 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_cistatic int wf_thread_func(void *data) 888c2ecf20Sopenharmony_ci{ 898c2ecf20Sopenharmony_ci unsigned long next, delay; 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci next = jiffies; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci DBG("wf: thread started\n"); 948c2ecf20Sopenharmony_ci 958c2ecf20Sopenharmony_ci set_freezable(); 968c2ecf20Sopenharmony_ci while (!kthread_should_stop()) { 978c2ecf20Sopenharmony_ci try_to_freeze(); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (time_after_eq(jiffies, next)) { 1008c2ecf20Sopenharmony_ci wf_notify(WF_EVENT_TICK, NULL); 1018c2ecf20Sopenharmony_ci if (wf_overtemp) { 1028c2ecf20Sopenharmony_ci wf_overtemp_counter++; 1038c2ecf20Sopenharmony_ci /* 10 seconds overtemp, notify userland */ 1048c2ecf20Sopenharmony_ci if (wf_overtemp_counter > 10) 1058c2ecf20Sopenharmony_ci wf_critical_overtemp(); 1068c2ecf20Sopenharmony_ci /* 30 seconds, shutdown */ 1078c2ecf20Sopenharmony_ci if (wf_overtemp_counter > 30) { 1088c2ecf20Sopenharmony_ci printk(KERN_ERR "windfarm: Overtemp " 1098c2ecf20Sopenharmony_ci "for more than 30" 1108c2ecf20Sopenharmony_ci " seconds, shutting down\n"); 1118c2ecf20Sopenharmony_ci machine_power_off(); 1128c2ecf20Sopenharmony_ci } 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci next += HZ; 1158c2ecf20Sopenharmony_ci } 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_ci delay = next - jiffies; 1188c2ecf20Sopenharmony_ci if (delay <= HZ) 1198c2ecf20Sopenharmony_ci schedule_timeout_interruptible(delay); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci DBG("wf: thread stopped\n"); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return 0; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cistatic void wf_start_thread(void) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci wf_thread = kthread_run(wf_thread_func, NULL, "kwindfarm"); 1308c2ecf20Sopenharmony_ci if (IS_ERR(wf_thread)) { 1318c2ecf20Sopenharmony_ci printk(KERN_ERR "windfarm: failed to create thread,err %ld\n", 1328c2ecf20Sopenharmony_ci PTR_ERR(wf_thread)); 1338c2ecf20Sopenharmony_ci wf_thread = NULL; 1348c2ecf20Sopenharmony_ci } 1358c2ecf20Sopenharmony_ci} 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci 1388c2ecf20Sopenharmony_cistatic void wf_stop_thread(void) 1398c2ecf20Sopenharmony_ci{ 1408c2ecf20Sopenharmony_ci if (wf_thread) 1418c2ecf20Sopenharmony_ci kthread_stop(wf_thread); 1428c2ecf20Sopenharmony_ci wf_thread = NULL; 1438c2ecf20Sopenharmony_ci} 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci/* 1468c2ecf20Sopenharmony_ci * Controls 1478c2ecf20Sopenharmony_ci */ 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic void wf_control_release(struct kref *kref) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct wf_control *ct = container_of(kref, struct wf_control, ref); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci DBG("wf: Deleting control %s\n", ct->name); 1548c2ecf20Sopenharmony_ci 1558c2ecf20Sopenharmony_ci if (ct->ops && ct->ops->release) 1568c2ecf20Sopenharmony_ci ct->ops->release(ct); 1578c2ecf20Sopenharmony_ci else 1588c2ecf20Sopenharmony_ci kfree(ct); 1598c2ecf20Sopenharmony_ci} 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_cistatic ssize_t wf_show_control(struct device *dev, 1628c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 1638c2ecf20Sopenharmony_ci{ 1648c2ecf20Sopenharmony_ci struct wf_control *ctrl = container_of(attr, struct wf_control, attr); 1658c2ecf20Sopenharmony_ci const char *typestr; 1668c2ecf20Sopenharmony_ci s32 val = 0; 1678c2ecf20Sopenharmony_ci int err; 1688c2ecf20Sopenharmony_ci 1698c2ecf20Sopenharmony_ci err = ctrl->ops->get_value(ctrl, &val); 1708c2ecf20Sopenharmony_ci if (err < 0) { 1718c2ecf20Sopenharmony_ci if (err == -EFAULT) 1728c2ecf20Sopenharmony_ci return sprintf(buf, "<HW FAULT>\n"); 1738c2ecf20Sopenharmony_ci return err; 1748c2ecf20Sopenharmony_ci } 1758c2ecf20Sopenharmony_ci switch(ctrl->type) { 1768c2ecf20Sopenharmony_ci case WF_CONTROL_RPM_FAN: 1778c2ecf20Sopenharmony_ci typestr = " RPM"; 1788c2ecf20Sopenharmony_ci break; 1798c2ecf20Sopenharmony_ci case WF_CONTROL_PWM_FAN: 1808c2ecf20Sopenharmony_ci typestr = " %"; 1818c2ecf20Sopenharmony_ci break; 1828c2ecf20Sopenharmony_ci default: 1838c2ecf20Sopenharmony_ci typestr = ""; 1848c2ecf20Sopenharmony_ci } 1858c2ecf20Sopenharmony_ci return sprintf(buf, "%d%s\n", val, typestr); 1868c2ecf20Sopenharmony_ci} 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci/* This is really only for debugging... */ 1898c2ecf20Sopenharmony_cistatic ssize_t wf_store_control(struct device *dev, 1908c2ecf20Sopenharmony_ci struct device_attribute *attr, 1918c2ecf20Sopenharmony_ci const char *buf, size_t count) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci struct wf_control *ctrl = container_of(attr, struct wf_control, attr); 1948c2ecf20Sopenharmony_ci int val; 1958c2ecf20Sopenharmony_ci int err; 1968c2ecf20Sopenharmony_ci char *endp; 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci val = simple_strtoul(buf, &endp, 0); 1998c2ecf20Sopenharmony_ci while (endp < buf + count && (*endp == ' ' || *endp == '\n')) 2008c2ecf20Sopenharmony_ci ++endp; 2018c2ecf20Sopenharmony_ci if (endp - buf < count) 2028c2ecf20Sopenharmony_ci return -EINVAL; 2038c2ecf20Sopenharmony_ci err = ctrl->ops->set_value(ctrl, val); 2048c2ecf20Sopenharmony_ci if (err < 0) 2058c2ecf20Sopenharmony_ci return err; 2068c2ecf20Sopenharmony_ci return count; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ciint wf_register_control(struct wf_control *new_ct) 2108c2ecf20Sopenharmony_ci{ 2118c2ecf20Sopenharmony_ci struct wf_control *ct; 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci mutex_lock(&wf_lock); 2148c2ecf20Sopenharmony_ci list_for_each_entry(ct, &wf_controls, link) { 2158c2ecf20Sopenharmony_ci if (!strcmp(ct->name, new_ct->name)) { 2168c2ecf20Sopenharmony_ci printk(KERN_WARNING "windfarm: trying to register" 2178c2ecf20Sopenharmony_ci " duplicate control %s\n", ct->name); 2188c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 2198c2ecf20Sopenharmony_ci return -EEXIST; 2208c2ecf20Sopenharmony_ci } 2218c2ecf20Sopenharmony_ci } 2228c2ecf20Sopenharmony_ci kref_init(&new_ct->ref); 2238c2ecf20Sopenharmony_ci list_add(&new_ct->link, &wf_controls); 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci sysfs_attr_init(&new_ct->attr.attr); 2268c2ecf20Sopenharmony_ci new_ct->attr.attr.name = new_ct->name; 2278c2ecf20Sopenharmony_ci new_ct->attr.attr.mode = 0644; 2288c2ecf20Sopenharmony_ci new_ct->attr.show = wf_show_control; 2298c2ecf20Sopenharmony_ci new_ct->attr.store = wf_store_control; 2308c2ecf20Sopenharmony_ci if (device_create_file(&wf_platform_device.dev, &new_ct->attr)) 2318c2ecf20Sopenharmony_ci printk(KERN_WARNING "windfarm: device_create_file failed" 2328c2ecf20Sopenharmony_ci " for %s\n", new_ct->name); 2338c2ecf20Sopenharmony_ci /* the subsystem still does useful work without the file */ 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci DBG("wf: Registered control %s\n", new_ct->name); 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci wf_notify(WF_EVENT_NEW_CONTROL, new_ct); 2388c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_ci return 0; 2418c2ecf20Sopenharmony_ci} 2428c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_register_control); 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_civoid wf_unregister_control(struct wf_control *ct) 2458c2ecf20Sopenharmony_ci{ 2468c2ecf20Sopenharmony_ci mutex_lock(&wf_lock); 2478c2ecf20Sopenharmony_ci list_del(&ct->link); 2488c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci DBG("wf: Unregistered control %s\n", ct->name); 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci kref_put(&ct->ref, wf_control_release); 2538c2ecf20Sopenharmony_ci} 2548c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_unregister_control); 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ciint wf_get_control(struct wf_control *ct) 2578c2ecf20Sopenharmony_ci{ 2588c2ecf20Sopenharmony_ci if (!try_module_get(ct->ops->owner)) 2598c2ecf20Sopenharmony_ci return -ENODEV; 2608c2ecf20Sopenharmony_ci kref_get(&ct->ref); 2618c2ecf20Sopenharmony_ci return 0; 2628c2ecf20Sopenharmony_ci} 2638c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_get_control); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_civoid wf_put_control(struct wf_control *ct) 2668c2ecf20Sopenharmony_ci{ 2678c2ecf20Sopenharmony_ci struct module *mod = ct->ops->owner; 2688c2ecf20Sopenharmony_ci kref_put(&ct->ref, wf_control_release); 2698c2ecf20Sopenharmony_ci module_put(mod); 2708c2ecf20Sopenharmony_ci} 2718c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_put_control); 2728c2ecf20Sopenharmony_ci 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci/* 2758c2ecf20Sopenharmony_ci * Sensors 2768c2ecf20Sopenharmony_ci */ 2778c2ecf20Sopenharmony_ci 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_cistatic void wf_sensor_release(struct kref *kref) 2808c2ecf20Sopenharmony_ci{ 2818c2ecf20Sopenharmony_ci struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref); 2828c2ecf20Sopenharmony_ci 2838c2ecf20Sopenharmony_ci DBG("wf: Deleting sensor %s\n", sr->name); 2848c2ecf20Sopenharmony_ci 2858c2ecf20Sopenharmony_ci if (sr->ops && sr->ops->release) 2868c2ecf20Sopenharmony_ci sr->ops->release(sr); 2878c2ecf20Sopenharmony_ci else 2888c2ecf20Sopenharmony_ci kfree(sr); 2898c2ecf20Sopenharmony_ci} 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_cistatic ssize_t wf_show_sensor(struct device *dev, 2928c2ecf20Sopenharmony_ci struct device_attribute *attr, char *buf) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr); 2958c2ecf20Sopenharmony_ci s32 val = 0; 2968c2ecf20Sopenharmony_ci int err; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci err = sens->ops->get_value(sens, &val); 2998c2ecf20Sopenharmony_ci if (err < 0) 3008c2ecf20Sopenharmony_ci return err; 3018c2ecf20Sopenharmony_ci return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val)); 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ciint wf_register_sensor(struct wf_sensor *new_sr) 3058c2ecf20Sopenharmony_ci{ 3068c2ecf20Sopenharmony_ci struct wf_sensor *sr; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci mutex_lock(&wf_lock); 3098c2ecf20Sopenharmony_ci list_for_each_entry(sr, &wf_sensors, link) { 3108c2ecf20Sopenharmony_ci if (!strcmp(sr->name, new_sr->name)) { 3118c2ecf20Sopenharmony_ci printk(KERN_WARNING "windfarm: trying to register" 3128c2ecf20Sopenharmony_ci " duplicate sensor %s\n", sr->name); 3138c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 3148c2ecf20Sopenharmony_ci return -EEXIST; 3158c2ecf20Sopenharmony_ci } 3168c2ecf20Sopenharmony_ci } 3178c2ecf20Sopenharmony_ci kref_init(&new_sr->ref); 3188c2ecf20Sopenharmony_ci list_add(&new_sr->link, &wf_sensors); 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci sysfs_attr_init(&new_sr->attr.attr); 3218c2ecf20Sopenharmony_ci new_sr->attr.attr.name = new_sr->name; 3228c2ecf20Sopenharmony_ci new_sr->attr.attr.mode = 0444; 3238c2ecf20Sopenharmony_ci new_sr->attr.show = wf_show_sensor; 3248c2ecf20Sopenharmony_ci new_sr->attr.store = NULL; 3258c2ecf20Sopenharmony_ci if (device_create_file(&wf_platform_device.dev, &new_sr->attr)) 3268c2ecf20Sopenharmony_ci printk(KERN_WARNING "windfarm: device_create_file failed" 3278c2ecf20Sopenharmony_ci " for %s\n", new_sr->name); 3288c2ecf20Sopenharmony_ci /* the subsystem still does useful work without the file */ 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_ci DBG("wf: Registered sensor %s\n", new_sr->name); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci wf_notify(WF_EVENT_NEW_SENSOR, new_sr); 3338c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_register_sensor); 3388c2ecf20Sopenharmony_ci 3398c2ecf20Sopenharmony_civoid wf_unregister_sensor(struct wf_sensor *sr) 3408c2ecf20Sopenharmony_ci{ 3418c2ecf20Sopenharmony_ci mutex_lock(&wf_lock); 3428c2ecf20Sopenharmony_ci list_del(&sr->link); 3438c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci DBG("wf: Unregistered sensor %s\n", sr->name); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci wf_put_sensor(sr); 3488c2ecf20Sopenharmony_ci} 3498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_unregister_sensor); 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_ciint wf_get_sensor(struct wf_sensor *sr) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci if (!try_module_get(sr->ops->owner)) 3548c2ecf20Sopenharmony_ci return -ENODEV; 3558c2ecf20Sopenharmony_ci kref_get(&sr->ref); 3568c2ecf20Sopenharmony_ci return 0; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_get_sensor); 3598c2ecf20Sopenharmony_ci 3608c2ecf20Sopenharmony_civoid wf_put_sensor(struct wf_sensor *sr) 3618c2ecf20Sopenharmony_ci{ 3628c2ecf20Sopenharmony_ci struct module *mod = sr->ops->owner; 3638c2ecf20Sopenharmony_ci kref_put(&sr->ref, wf_sensor_release); 3648c2ecf20Sopenharmony_ci module_put(mod); 3658c2ecf20Sopenharmony_ci} 3668c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_put_sensor); 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* 3708c2ecf20Sopenharmony_ci * Client & notification 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_ci 3738c2ecf20Sopenharmony_ciint wf_register_client(struct notifier_block *nb) 3748c2ecf20Sopenharmony_ci{ 3758c2ecf20Sopenharmony_ci int rc; 3768c2ecf20Sopenharmony_ci struct wf_control *ct; 3778c2ecf20Sopenharmony_ci struct wf_sensor *sr; 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci mutex_lock(&wf_lock); 3808c2ecf20Sopenharmony_ci rc = blocking_notifier_chain_register(&wf_client_list, nb); 3818c2ecf20Sopenharmony_ci if (rc != 0) 3828c2ecf20Sopenharmony_ci goto bail; 3838c2ecf20Sopenharmony_ci wf_client_count++; 3848c2ecf20Sopenharmony_ci list_for_each_entry(ct, &wf_controls, link) 3858c2ecf20Sopenharmony_ci wf_notify(WF_EVENT_NEW_CONTROL, ct); 3868c2ecf20Sopenharmony_ci list_for_each_entry(sr, &wf_sensors, link) 3878c2ecf20Sopenharmony_ci wf_notify(WF_EVENT_NEW_SENSOR, sr); 3888c2ecf20Sopenharmony_ci if (wf_client_count == 1) 3898c2ecf20Sopenharmony_ci wf_start_thread(); 3908c2ecf20Sopenharmony_ci bail: 3918c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 3928c2ecf20Sopenharmony_ci return rc; 3938c2ecf20Sopenharmony_ci} 3948c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_register_client); 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ciint wf_unregister_client(struct notifier_block *nb) 3978c2ecf20Sopenharmony_ci{ 3988c2ecf20Sopenharmony_ci mutex_lock(&wf_lock); 3998c2ecf20Sopenharmony_ci blocking_notifier_chain_unregister(&wf_client_list, nb); 4008c2ecf20Sopenharmony_ci wf_client_count--; 4018c2ecf20Sopenharmony_ci if (wf_client_count == 0) 4028c2ecf20Sopenharmony_ci wf_stop_thread(); 4038c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 4048c2ecf20Sopenharmony_ci 4058c2ecf20Sopenharmony_ci return 0; 4068c2ecf20Sopenharmony_ci} 4078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_unregister_client); 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_civoid wf_set_overtemp(void) 4108c2ecf20Sopenharmony_ci{ 4118c2ecf20Sopenharmony_ci mutex_lock(&wf_lock); 4128c2ecf20Sopenharmony_ci wf_overtemp++; 4138c2ecf20Sopenharmony_ci if (wf_overtemp == 1) { 4148c2ecf20Sopenharmony_ci printk(KERN_WARNING "windfarm: Overtemp condition detected !\n"); 4158c2ecf20Sopenharmony_ci wf_overtemp_counter = 0; 4168c2ecf20Sopenharmony_ci wf_notify(WF_EVENT_OVERTEMP, NULL); 4178c2ecf20Sopenharmony_ci } 4188c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 4198c2ecf20Sopenharmony_ci} 4208c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_set_overtemp); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_civoid wf_clear_overtemp(void) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci mutex_lock(&wf_lock); 4258c2ecf20Sopenharmony_ci WARN_ON(wf_overtemp == 0); 4268c2ecf20Sopenharmony_ci if (wf_overtemp == 0) { 4278c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 4288c2ecf20Sopenharmony_ci return; 4298c2ecf20Sopenharmony_ci } 4308c2ecf20Sopenharmony_ci wf_overtemp--; 4318c2ecf20Sopenharmony_ci if (wf_overtemp == 0) { 4328c2ecf20Sopenharmony_ci printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n"); 4338c2ecf20Sopenharmony_ci wf_notify(WF_EVENT_NORMALTEMP, NULL); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci mutex_unlock(&wf_lock); 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(wf_clear_overtemp); 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_cistatic int __init windfarm_core_init(void) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci DBG("wf: core loaded\n"); 4428c2ecf20Sopenharmony_ci 4438c2ecf20Sopenharmony_ci platform_device_register(&wf_platform_device); 4448c2ecf20Sopenharmony_ci return 0; 4458c2ecf20Sopenharmony_ci} 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_cistatic void __exit windfarm_core_exit(void) 4488c2ecf20Sopenharmony_ci{ 4498c2ecf20Sopenharmony_ci BUG_ON(wf_client_count != 0); 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci DBG("wf: core unloaded\n"); 4528c2ecf20Sopenharmony_ci 4538c2ecf20Sopenharmony_ci platform_device_unregister(&wf_platform_device); 4548c2ecf20Sopenharmony_ci} 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_cimodule_init(windfarm_core_init); 4588c2ecf20Sopenharmony_cimodule_exit(windfarm_core_exit); 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_ciMODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>"); 4618c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Core component of PowerMac thermal control"); 4628c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 4638c2ecf20Sopenharmony_ci 464