1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Windfarm PowerMac thermal control. Core
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
6*4882a593Smuzhiyun * <benh@kernel.crashing.org>
7*4882a593Smuzhiyun *
8*4882a593Smuzhiyun * This core code tracks the list of sensors & controls, register
9*4882a593Smuzhiyun * clients, and holds the kernel thread used for control.
10*4882a593Smuzhiyun *
11*4882a593Smuzhiyun * TODO:
12*4882a593Smuzhiyun *
13*4882a593Smuzhiyun * Add some information about sensor/control type and data format to
14*4882a593Smuzhiyun * sensors/controls, and have the sysfs attribute stuff be moved
15*4882a593Smuzhiyun * generically here instead of hard coded in the platform specific
16*4882a593Smuzhiyun * driver as it us currently
17*4882a593Smuzhiyun *
18*4882a593Smuzhiyun * This however requires solving some annoying lifetime issues with
19*4882a593Smuzhiyun * sysfs which doesn't seem to have lifetime rules for struct attribute,
20*4882a593Smuzhiyun * I may have to create full features kobjects for every sensor/control
21*4882a593Smuzhiyun * instead which is a bit of an overkill imho
22*4882a593Smuzhiyun */
23*4882a593Smuzhiyun
24*4882a593Smuzhiyun #include <linux/types.h>
25*4882a593Smuzhiyun #include <linux/errno.h>
26*4882a593Smuzhiyun #include <linux/kernel.h>
27*4882a593Smuzhiyun #include <linux/slab.h>
28*4882a593Smuzhiyun #include <linux/init.h>
29*4882a593Smuzhiyun #include <linux/spinlock.h>
30*4882a593Smuzhiyun #include <linux/kthread.h>
31*4882a593Smuzhiyun #include <linux/jiffies.h>
32*4882a593Smuzhiyun #include <linux/reboot.h>
33*4882a593Smuzhiyun #include <linux/device.h>
34*4882a593Smuzhiyun #include <linux/platform_device.h>
35*4882a593Smuzhiyun #include <linux/mutex.h>
36*4882a593Smuzhiyun #include <linux/freezer.h>
37*4882a593Smuzhiyun
38*4882a593Smuzhiyun #include <asm/prom.h>
39*4882a593Smuzhiyun
40*4882a593Smuzhiyun #include "windfarm.h"
41*4882a593Smuzhiyun
42*4882a593Smuzhiyun #define VERSION "0.2"
43*4882a593Smuzhiyun
44*4882a593Smuzhiyun #undef DEBUG
45*4882a593Smuzhiyun
46*4882a593Smuzhiyun #ifdef DEBUG
47*4882a593Smuzhiyun #define DBG(args...) printk(args)
48*4882a593Smuzhiyun #else
49*4882a593Smuzhiyun #define DBG(args...) do { } while(0)
50*4882a593Smuzhiyun #endif
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun static LIST_HEAD(wf_controls);
53*4882a593Smuzhiyun static LIST_HEAD(wf_sensors);
54*4882a593Smuzhiyun static DEFINE_MUTEX(wf_lock);
55*4882a593Smuzhiyun static BLOCKING_NOTIFIER_HEAD(wf_client_list);
56*4882a593Smuzhiyun static int wf_client_count;
57*4882a593Smuzhiyun static unsigned int wf_overtemp;
58*4882a593Smuzhiyun static unsigned int wf_overtemp_counter;
59*4882a593Smuzhiyun struct task_struct *wf_thread;
60*4882a593Smuzhiyun
61*4882a593Smuzhiyun static struct platform_device wf_platform_device = {
62*4882a593Smuzhiyun .name = "windfarm",
63*4882a593Smuzhiyun };
64*4882a593Smuzhiyun
65*4882a593Smuzhiyun /*
66*4882a593Smuzhiyun * Utilities & tick thread
67*4882a593Smuzhiyun */
68*4882a593Smuzhiyun
wf_notify(int event,void * param)69*4882a593Smuzhiyun static inline void wf_notify(int event, void *param)
70*4882a593Smuzhiyun {
71*4882a593Smuzhiyun blocking_notifier_call_chain(&wf_client_list, event, param);
72*4882a593Smuzhiyun }
73*4882a593Smuzhiyun
wf_critical_overtemp(void)74*4882a593Smuzhiyun static int wf_critical_overtemp(void)
75*4882a593Smuzhiyun {
76*4882a593Smuzhiyun static char const critical_overtemp_path[] = "/sbin/critical_overtemp";
77*4882a593Smuzhiyun char *argv[] = { (char *)critical_overtemp_path, NULL };
78*4882a593Smuzhiyun static char *envp[] = { "HOME=/",
79*4882a593Smuzhiyun "TERM=linux",
80*4882a593Smuzhiyun "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
81*4882a593Smuzhiyun NULL };
82*4882a593Smuzhiyun
83*4882a593Smuzhiyun return call_usermodehelper(critical_overtemp_path,
84*4882a593Smuzhiyun argv, envp, UMH_WAIT_EXEC);
85*4882a593Smuzhiyun }
86*4882a593Smuzhiyun
wf_thread_func(void * data)87*4882a593Smuzhiyun static int wf_thread_func(void *data)
88*4882a593Smuzhiyun {
89*4882a593Smuzhiyun unsigned long next, delay;
90*4882a593Smuzhiyun
91*4882a593Smuzhiyun next = jiffies;
92*4882a593Smuzhiyun
93*4882a593Smuzhiyun DBG("wf: thread started\n");
94*4882a593Smuzhiyun
95*4882a593Smuzhiyun set_freezable();
96*4882a593Smuzhiyun while (!kthread_should_stop()) {
97*4882a593Smuzhiyun try_to_freeze();
98*4882a593Smuzhiyun
99*4882a593Smuzhiyun if (time_after_eq(jiffies, next)) {
100*4882a593Smuzhiyun wf_notify(WF_EVENT_TICK, NULL);
101*4882a593Smuzhiyun if (wf_overtemp) {
102*4882a593Smuzhiyun wf_overtemp_counter++;
103*4882a593Smuzhiyun /* 10 seconds overtemp, notify userland */
104*4882a593Smuzhiyun if (wf_overtemp_counter > 10)
105*4882a593Smuzhiyun wf_critical_overtemp();
106*4882a593Smuzhiyun /* 30 seconds, shutdown */
107*4882a593Smuzhiyun if (wf_overtemp_counter > 30) {
108*4882a593Smuzhiyun printk(KERN_ERR "windfarm: Overtemp "
109*4882a593Smuzhiyun "for more than 30"
110*4882a593Smuzhiyun " seconds, shutting down\n");
111*4882a593Smuzhiyun machine_power_off();
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun }
114*4882a593Smuzhiyun next += HZ;
115*4882a593Smuzhiyun }
116*4882a593Smuzhiyun
117*4882a593Smuzhiyun delay = next - jiffies;
118*4882a593Smuzhiyun if (delay <= HZ)
119*4882a593Smuzhiyun schedule_timeout_interruptible(delay);
120*4882a593Smuzhiyun }
121*4882a593Smuzhiyun
122*4882a593Smuzhiyun DBG("wf: thread stopped\n");
123*4882a593Smuzhiyun
124*4882a593Smuzhiyun return 0;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun
wf_start_thread(void)127*4882a593Smuzhiyun static void wf_start_thread(void)
128*4882a593Smuzhiyun {
129*4882a593Smuzhiyun wf_thread = kthread_run(wf_thread_func, NULL, "kwindfarm");
130*4882a593Smuzhiyun if (IS_ERR(wf_thread)) {
131*4882a593Smuzhiyun printk(KERN_ERR "windfarm: failed to create thread,err %ld\n",
132*4882a593Smuzhiyun PTR_ERR(wf_thread));
133*4882a593Smuzhiyun wf_thread = NULL;
134*4882a593Smuzhiyun }
135*4882a593Smuzhiyun }
136*4882a593Smuzhiyun
137*4882a593Smuzhiyun
wf_stop_thread(void)138*4882a593Smuzhiyun static void wf_stop_thread(void)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun if (wf_thread)
141*4882a593Smuzhiyun kthread_stop(wf_thread);
142*4882a593Smuzhiyun wf_thread = NULL;
143*4882a593Smuzhiyun }
144*4882a593Smuzhiyun
145*4882a593Smuzhiyun /*
146*4882a593Smuzhiyun * Controls
147*4882a593Smuzhiyun */
148*4882a593Smuzhiyun
wf_control_release(struct kref * kref)149*4882a593Smuzhiyun static void wf_control_release(struct kref *kref)
150*4882a593Smuzhiyun {
151*4882a593Smuzhiyun struct wf_control *ct = container_of(kref, struct wf_control, ref);
152*4882a593Smuzhiyun
153*4882a593Smuzhiyun DBG("wf: Deleting control %s\n", ct->name);
154*4882a593Smuzhiyun
155*4882a593Smuzhiyun if (ct->ops && ct->ops->release)
156*4882a593Smuzhiyun ct->ops->release(ct);
157*4882a593Smuzhiyun else
158*4882a593Smuzhiyun kfree(ct);
159*4882a593Smuzhiyun }
160*4882a593Smuzhiyun
wf_show_control(struct device * dev,struct device_attribute * attr,char * buf)161*4882a593Smuzhiyun static ssize_t wf_show_control(struct device *dev,
162*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
163*4882a593Smuzhiyun {
164*4882a593Smuzhiyun struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
165*4882a593Smuzhiyun const char *typestr;
166*4882a593Smuzhiyun s32 val = 0;
167*4882a593Smuzhiyun int err;
168*4882a593Smuzhiyun
169*4882a593Smuzhiyun err = ctrl->ops->get_value(ctrl, &val);
170*4882a593Smuzhiyun if (err < 0) {
171*4882a593Smuzhiyun if (err == -EFAULT)
172*4882a593Smuzhiyun return sprintf(buf, "<HW FAULT>\n");
173*4882a593Smuzhiyun return err;
174*4882a593Smuzhiyun }
175*4882a593Smuzhiyun switch(ctrl->type) {
176*4882a593Smuzhiyun case WF_CONTROL_RPM_FAN:
177*4882a593Smuzhiyun typestr = " RPM";
178*4882a593Smuzhiyun break;
179*4882a593Smuzhiyun case WF_CONTROL_PWM_FAN:
180*4882a593Smuzhiyun typestr = " %";
181*4882a593Smuzhiyun break;
182*4882a593Smuzhiyun default:
183*4882a593Smuzhiyun typestr = "";
184*4882a593Smuzhiyun }
185*4882a593Smuzhiyun return sprintf(buf, "%d%s\n", val, typestr);
186*4882a593Smuzhiyun }
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun /* This is really only for debugging... */
wf_store_control(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)189*4882a593Smuzhiyun static ssize_t wf_store_control(struct device *dev,
190*4882a593Smuzhiyun struct device_attribute *attr,
191*4882a593Smuzhiyun const char *buf, size_t count)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
194*4882a593Smuzhiyun int val;
195*4882a593Smuzhiyun int err;
196*4882a593Smuzhiyun char *endp;
197*4882a593Smuzhiyun
198*4882a593Smuzhiyun val = simple_strtoul(buf, &endp, 0);
199*4882a593Smuzhiyun while (endp < buf + count && (*endp == ' ' || *endp == '\n'))
200*4882a593Smuzhiyun ++endp;
201*4882a593Smuzhiyun if (endp - buf < count)
202*4882a593Smuzhiyun return -EINVAL;
203*4882a593Smuzhiyun err = ctrl->ops->set_value(ctrl, val);
204*4882a593Smuzhiyun if (err < 0)
205*4882a593Smuzhiyun return err;
206*4882a593Smuzhiyun return count;
207*4882a593Smuzhiyun }
208*4882a593Smuzhiyun
wf_register_control(struct wf_control * new_ct)209*4882a593Smuzhiyun int wf_register_control(struct wf_control *new_ct)
210*4882a593Smuzhiyun {
211*4882a593Smuzhiyun struct wf_control *ct;
212*4882a593Smuzhiyun
213*4882a593Smuzhiyun mutex_lock(&wf_lock);
214*4882a593Smuzhiyun list_for_each_entry(ct, &wf_controls, link) {
215*4882a593Smuzhiyun if (!strcmp(ct->name, new_ct->name)) {
216*4882a593Smuzhiyun printk(KERN_WARNING "windfarm: trying to register"
217*4882a593Smuzhiyun " duplicate control %s\n", ct->name);
218*4882a593Smuzhiyun mutex_unlock(&wf_lock);
219*4882a593Smuzhiyun return -EEXIST;
220*4882a593Smuzhiyun }
221*4882a593Smuzhiyun }
222*4882a593Smuzhiyun kref_init(&new_ct->ref);
223*4882a593Smuzhiyun list_add(&new_ct->link, &wf_controls);
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun sysfs_attr_init(&new_ct->attr.attr);
226*4882a593Smuzhiyun new_ct->attr.attr.name = new_ct->name;
227*4882a593Smuzhiyun new_ct->attr.attr.mode = 0644;
228*4882a593Smuzhiyun new_ct->attr.show = wf_show_control;
229*4882a593Smuzhiyun new_ct->attr.store = wf_store_control;
230*4882a593Smuzhiyun if (device_create_file(&wf_platform_device.dev, &new_ct->attr))
231*4882a593Smuzhiyun printk(KERN_WARNING "windfarm: device_create_file failed"
232*4882a593Smuzhiyun " for %s\n", new_ct->name);
233*4882a593Smuzhiyun /* the subsystem still does useful work without the file */
234*4882a593Smuzhiyun
235*4882a593Smuzhiyun DBG("wf: Registered control %s\n", new_ct->name);
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun wf_notify(WF_EVENT_NEW_CONTROL, new_ct);
238*4882a593Smuzhiyun mutex_unlock(&wf_lock);
239*4882a593Smuzhiyun
240*4882a593Smuzhiyun return 0;
241*4882a593Smuzhiyun }
242*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_register_control);
243*4882a593Smuzhiyun
wf_unregister_control(struct wf_control * ct)244*4882a593Smuzhiyun void wf_unregister_control(struct wf_control *ct)
245*4882a593Smuzhiyun {
246*4882a593Smuzhiyun mutex_lock(&wf_lock);
247*4882a593Smuzhiyun list_del(&ct->link);
248*4882a593Smuzhiyun mutex_unlock(&wf_lock);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun DBG("wf: Unregistered control %s\n", ct->name);
251*4882a593Smuzhiyun
252*4882a593Smuzhiyun kref_put(&ct->ref, wf_control_release);
253*4882a593Smuzhiyun }
254*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_unregister_control);
255*4882a593Smuzhiyun
wf_get_control(struct wf_control * ct)256*4882a593Smuzhiyun int wf_get_control(struct wf_control *ct)
257*4882a593Smuzhiyun {
258*4882a593Smuzhiyun if (!try_module_get(ct->ops->owner))
259*4882a593Smuzhiyun return -ENODEV;
260*4882a593Smuzhiyun kref_get(&ct->ref);
261*4882a593Smuzhiyun return 0;
262*4882a593Smuzhiyun }
263*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_get_control);
264*4882a593Smuzhiyun
wf_put_control(struct wf_control * ct)265*4882a593Smuzhiyun void wf_put_control(struct wf_control *ct)
266*4882a593Smuzhiyun {
267*4882a593Smuzhiyun struct module *mod = ct->ops->owner;
268*4882a593Smuzhiyun kref_put(&ct->ref, wf_control_release);
269*4882a593Smuzhiyun module_put(mod);
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_put_control);
272*4882a593Smuzhiyun
273*4882a593Smuzhiyun
274*4882a593Smuzhiyun /*
275*4882a593Smuzhiyun * Sensors
276*4882a593Smuzhiyun */
277*4882a593Smuzhiyun
278*4882a593Smuzhiyun
wf_sensor_release(struct kref * kref)279*4882a593Smuzhiyun static void wf_sensor_release(struct kref *kref)
280*4882a593Smuzhiyun {
281*4882a593Smuzhiyun struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun DBG("wf: Deleting sensor %s\n", sr->name);
284*4882a593Smuzhiyun
285*4882a593Smuzhiyun if (sr->ops && sr->ops->release)
286*4882a593Smuzhiyun sr->ops->release(sr);
287*4882a593Smuzhiyun else
288*4882a593Smuzhiyun kfree(sr);
289*4882a593Smuzhiyun }
290*4882a593Smuzhiyun
wf_show_sensor(struct device * dev,struct device_attribute * attr,char * buf)291*4882a593Smuzhiyun static ssize_t wf_show_sensor(struct device *dev,
292*4882a593Smuzhiyun struct device_attribute *attr, char *buf)
293*4882a593Smuzhiyun {
294*4882a593Smuzhiyun struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr);
295*4882a593Smuzhiyun s32 val = 0;
296*4882a593Smuzhiyun int err;
297*4882a593Smuzhiyun
298*4882a593Smuzhiyun err = sens->ops->get_value(sens, &val);
299*4882a593Smuzhiyun if (err < 0)
300*4882a593Smuzhiyun return err;
301*4882a593Smuzhiyun return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val));
302*4882a593Smuzhiyun }
303*4882a593Smuzhiyun
wf_register_sensor(struct wf_sensor * new_sr)304*4882a593Smuzhiyun int wf_register_sensor(struct wf_sensor *new_sr)
305*4882a593Smuzhiyun {
306*4882a593Smuzhiyun struct wf_sensor *sr;
307*4882a593Smuzhiyun
308*4882a593Smuzhiyun mutex_lock(&wf_lock);
309*4882a593Smuzhiyun list_for_each_entry(sr, &wf_sensors, link) {
310*4882a593Smuzhiyun if (!strcmp(sr->name, new_sr->name)) {
311*4882a593Smuzhiyun printk(KERN_WARNING "windfarm: trying to register"
312*4882a593Smuzhiyun " duplicate sensor %s\n", sr->name);
313*4882a593Smuzhiyun mutex_unlock(&wf_lock);
314*4882a593Smuzhiyun return -EEXIST;
315*4882a593Smuzhiyun }
316*4882a593Smuzhiyun }
317*4882a593Smuzhiyun kref_init(&new_sr->ref);
318*4882a593Smuzhiyun list_add(&new_sr->link, &wf_sensors);
319*4882a593Smuzhiyun
320*4882a593Smuzhiyun sysfs_attr_init(&new_sr->attr.attr);
321*4882a593Smuzhiyun new_sr->attr.attr.name = new_sr->name;
322*4882a593Smuzhiyun new_sr->attr.attr.mode = 0444;
323*4882a593Smuzhiyun new_sr->attr.show = wf_show_sensor;
324*4882a593Smuzhiyun new_sr->attr.store = NULL;
325*4882a593Smuzhiyun if (device_create_file(&wf_platform_device.dev, &new_sr->attr))
326*4882a593Smuzhiyun printk(KERN_WARNING "windfarm: device_create_file failed"
327*4882a593Smuzhiyun " for %s\n", new_sr->name);
328*4882a593Smuzhiyun /* the subsystem still does useful work without the file */
329*4882a593Smuzhiyun
330*4882a593Smuzhiyun DBG("wf: Registered sensor %s\n", new_sr->name);
331*4882a593Smuzhiyun
332*4882a593Smuzhiyun wf_notify(WF_EVENT_NEW_SENSOR, new_sr);
333*4882a593Smuzhiyun mutex_unlock(&wf_lock);
334*4882a593Smuzhiyun
335*4882a593Smuzhiyun return 0;
336*4882a593Smuzhiyun }
337*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_register_sensor);
338*4882a593Smuzhiyun
wf_unregister_sensor(struct wf_sensor * sr)339*4882a593Smuzhiyun void wf_unregister_sensor(struct wf_sensor *sr)
340*4882a593Smuzhiyun {
341*4882a593Smuzhiyun mutex_lock(&wf_lock);
342*4882a593Smuzhiyun list_del(&sr->link);
343*4882a593Smuzhiyun mutex_unlock(&wf_lock);
344*4882a593Smuzhiyun
345*4882a593Smuzhiyun DBG("wf: Unregistered sensor %s\n", sr->name);
346*4882a593Smuzhiyun
347*4882a593Smuzhiyun wf_put_sensor(sr);
348*4882a593Smuzhiyun }
349*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_unregister_sensor);
350*4882a593Smuzhiyun
wf_get_sensor(struct wf_sensor * sr)351*4882a593Smuzhiyun int wf_get_sensor(struct wf_sensor *sr)
352*4882a593Smuzhiyun {
353*4882a593Smuzhiyun if (!try_module_get(sr->ops->owner))
354*4882a593Smuzhiyun return -ENODEV;
355*4882a593Smuzhiyun kref_get(&sr->ref);
356*4882a593Smuzhiyun return 0;
357*4882a593Smuzhiyun }
358*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_get_sensor);
359*4882a593Smuzhiyun
wf_put_sensor(struct wf_sensor * sr)360*4882a593Smuzhiyun void wf_put_sensor(struct wf_sensor *sr)
361*4882a593Smuzhiyun {
362*4882a593Smuzhiyun struct module *mod = sr->ops->owner;
363*4882a593Smuzhiyun kref_put(&sr->ref, wf_sensor_release);
364*4882a593Smuzhiyun module_put(mod);
365*4882a593Smuzhiyun }
366*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_put_sensor);
367*4882a593Smuzhiyun
368*4882a593Smuzhiyun
369*4882a593Smuzhiyun /*
370*4882a593Smuzhiyun * Client & notification
371*4882a593Smuzhiyun */
372*4882a593Smuzhiyun
wf_register_client(struct notifier_block * nb)373*4882a593Smuzhiyun int wf_register_client(struct notifier_block *nb)
374*4882a593Smuzhiyun {
375*4882a593Smuzhiyun int rc;
376*4882a593Smuzhiyun struct wf_control *ct;
377*4882a593Smuzhiyun struct wf_sensor *sr;
378*4882a593Smuzhiyun
379*4882a593Smuzhiyun mutex_lock(&wf_lock);
380*4882a593Smuzhiyun rc = blocking_notifier_chain_register(&wf_client_list, nb);
381*4882a593Smuzhiyun if (rc != 0)
382*4882a593Smuzhiyun goto bail;
383*4882a593Smuzhiyun wf_client_count++;
384*4882a593Smuzhiyun list_for_each_entry(ct, &wf_controls, link)
385*4882a593Smuzhiyun wf_notify(WF_EVENT_NEW_CONTROL, ct);
386*4882a593Smuzhiyun list_for_each_entry(sr, &wf_sensors, link)
387*4882a593Smuzhiyun wf_notify(WF_EVENT_NEW_SENSOR, sr);
388*4882a593Smuzhiyun if (wf_client_count == 1)
389*4882a593Smuzhiyun wf_start_thread();
390*4882a593Smuzhiyun bail:
391*4882a593Smuzhiyun mutex_unlock(&wf_lock);
392*4882a593Smuzhiyun return rc;
393*4882a593Smuzhiyun }
394*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_register_client);
395*4882a593Smuzhiyun
wf_unregister_client(struct notifier_block * nb)396*4882a593Smuzhiyun int wf_unregister_client(struct notifier_block *nb)
397*4882a593Smuzhiyun {
398*4882a593Smuzhiyun mutex_lock(&wf_lock);
399*4882a593Smuzhiyun blocking_notifier_chain_unregister(&wf_client_list, nb);
400*4882a593Smuzhiyun wf_client_count--;
401*4882a593Smuzhiyun if (wf_client_count == 0)
402*4882a593Smuzhiyun wf_stop_thread();
403*4882a593Smuzhiyun mutex_unlock(&wf_lock);
404*4882a593Smuzhiyun
405*4882a593Smuzhiyun return 0;
406*4882a593Smuzhiyun }
407*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_unregister_client);
408*4882a593Smuzhiyun
wf_set_overtemp(void)409*4882a593Smuzhiyun void wf_set_overtemp(void)
410*4882a593Smuzhiyun {
411*4882a593Smuzhiyun mutex_lock(&wf_lock);
412*4882a593Smuzhiyun wf_overtemp++;
413*4882a593Smuzhiyun if (wf_overtemp == 1) {
414*4882a593Smuzhiyun printk(KERN_WARNING "windfarm: Overtemp condition detected !\n");
415*4882a593Smuzhiyun wf_overtemp_counter = 0;
416*4882a593Smuzhiyun wf_notify(WF_EVENT_OVERTEMP, NULL);
417*4882a593Smuzhiyun }
418*4882a593Smuzhiyun mutex_unlock(&wf_lock);
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_set_overtemp);
421*4882a593Smuzhiyun
wf_clear_overtemp(void)422*4882a593Smuzhiyun void wf_clear_overtemp(void)
423*4882a593Smuzhiyun {
424*4882a593Smuzhiyun mutex_lock(&wf_lock);
425*4882a593Smuzhiyun WARN_ON(wf_overtemp == 0);
426*4882a593Smuzhiyun if (wf_overtemp == 0) {
427*4882a593Smuzhiyun mutex_unlock(&wf_lock);
428*4882a593Smuzhiyun return;
429*4882a593Smuzhiyun }
430*4882a593Smuzhiyun wf_overtemp--;
431*4882a593Smuzhiyun if (wf_overtemp == 0) {
432*4882a593Smuzhiyun printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n");
433*4882a593Smuzhiyun wf_notify(WF_EVENT_NORMALTEMP, NULL);
434*4882a593Smuzhiyun }
435*4882a593Smuzhiyun mutex_unlock(&wf_lock);
436*4882a593Smuzhiyun }
437*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(wf_clear_overtemp);
438*4882a593Smuzhiyun
windfarm_core_init(void)439*4882a593Smuzhiyun static int __init windfarm_core_init(void)
440*4882a593Smuzhiyun {
441*4882a593Smuzhiyun DBG("wf: core loaded\n");
442*4882a593Smuzhiyun
443*4882a593Smuzhiyun platform_device_register(&wf_platform_device);
444*4882a593Smuzhiyun return 0;
445*4882a593Smuzhiyun }
446*4882a593Smuzhiyun
windfarm_core_exit(void)447*4882a593Smuzhiyun static void __exit windfarm_core_exit(void)
448*4882a593Smuzhiyun {
449*4882a593Smuzhiyun BUG_ON(wf_client_count != 0);
450*4882a593Smuzhiyun
451*4882a593Smuzhiyun DBG("wf: core unloaded\n");
452*4882a593Smuzhiyun
453*4882a593Smuzhiyun platform_device_unregister(&wf_platform_device);
454*4882a593Smuzhiyun }
455*4882a593Smuzhiyun
456*4882a593Smuzhiyun
457*4882a593Smuzhiyun module_init(windfarm_core_init);
458*4882a593Smuzhiyun module_exit(windfarm_core_exit);
459*4882a593Smuzhiyun
460*4882a593Smuzhiyun MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
461*4882a593Smuzhiyun MODULE_DESCRIPTION("Core component of PowerMac thermal control");
462*4882a593Smuzhiyun MODULE_LICENSE("GPL");
463*4882a593Smuzhiyun
464