12c162f9bSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
275722d39SBenjamin Herrenschmidt /*
375722d39SBenjamin Herrenschmidt * Windfarm PowerMac thermal control. Core
475722d39SBenjamin Herrenschmidt *
575722d39SBenjamin Herrenschmidt * (c) Copyright 2005 Benjamin Herrenschmidt, IBM Corp.
675722d39SBenjamin Herrenschmidt * <benh@kernel.crashing.org>
775722d39SBenjamin Herrenschmidt *
875722d39SBenjamin Herrenschmidt * This core code tracks the list of sensors & controls, register
975722d39SBenjamin Herrenschmidt * clients, and holds the kernel thread used for control.
1075722d39SBenjamin Herrenschmidt *
1175722d39SBenjamin Herrenschmidt * TODO:
1275722d39SBenjamin Herrenschmidt *
1375722d39SBenjamin Herrenschmidt * Add some information about sensor/control type and data format to
1475722d39SBenjamin Herrenschmidt * sensors/controls, and have the sysfs attribute stuff be moved
1575722d39SBenjamin Herrenschmidt * generically here instead of hard coded in the platform specific
1675722d39SBenjamin Herrenschmidt * driver as it us currently
1775722d39SBenjamin Herrenschmidt *
1875722d39SBenjamin Herrenschmidt * This however requires solving some annoying lifetime issues with
1975722d39SBenjamin Herrenschmidt * sysfs which doesn't seem to have lifetime rules for struct attribute,
2075722d39SBenjamin Herrenschmidt * I may have to create full features kobjects for every sensor/control
2175722d39SBenjamin Herrenschmidt * instead which is a bit of an overkill imho
2275722d39SBenjamin Herrenschmidt */
2375722d39SBenjamin Herrenschmidt
2475722d39SBenjamin Herrenschmidt #include <linux/types.h>
2575722d39SBenjamin Herrenschmidt #include <linux/errno.h>
2675722d39SBenjamin Herrenschmidt #include <linux/kernel.h>
275a0e3ad6STejun Heo #include <linux/slab.h>
2875722d39SBenjamin Herrenschmidt #include <linux/init.h>
2975722d39SBenjamin Herrenschmidt #include <linux/spinlock.h>
3075722d39SBenjamin Herrenschmidt #include <linux/kthread.h>
3175722d39SBenjamin Herrenschmidt #include <linux/jiffies.h>
3275722d39SBenjamin Herrenschmidt #include <linux/reboot.h>
3375722d39SBenjamin Herrenschmidt #include <linux/device.h>
3475722d39SBenjamin Herrenschmidt #include <linux/platform_device.h>
35837e9594SIngo Molnar #include <linux/mutex.h>
367dfb7103SNigel Cunningham #include <linux/freezer.h>
3775722d39SBenjamin Herrenschmidt
3875722d39SBenjamin Herrenschmidt #include "windfarm.h"
3975722d39SBenjamin Herrenschmidt
4075722d39SBenjamin Herrenschmidt #define VERSION "0.2"
4175722d39SBenjamin Herrenschmidt
4275722d39SBenjamin Herrenschmidt #undef DEBUG
4375722d39SBenjamin Herrenschmidt
4475722d39SBenjamin Herrenschmidt #ifdef DEBUG
4575722d39SBenjamin Herrenschmidt #define DBG(args...) printk(args)
4675722d39SBenjamin Herrenschmidt #else
4775722d39SBenjamin Herrenschmidt #define DBG(args...) do { } while(0)
4875722d39SBenjamin Herrenschmidt #endif
4975722d39SBenjamin Herrenschmidt
5075722d39SBenjamin Herrenschmidt static LIST_HEAD(wf_controls);
5175722d39SBenjamin Herrenschmidt static LIST_HEAD(wf_sensors);
52837e9594SIngo Molnar static DEFINE_MUTEX(wf_lock);
53e041c683SAlan Stern static BLOCKING_NOTIFIER_HEAD(wf_client_list);
5475722d39SBenjamin Herrenschmidt static int wf_client_count;
5575722d39SBenjamin Herrenschmidt static unsigned int wf_overtemp;
5675722d39SBenjamin Herrenschmidt static unsigned int wf_overtemp_counter;
57*4204ecd5SYu Kuai static struct task_struct *wf_thread;
5875722d39SBenjamin Herrenschmidt
59ac171c46SBenjamin Herrenschmidt static struct platform_device wf_platform_device = {
60ac171c46SBenjamin Herrenschmidt .name = "windfarm",
61ac171c46SBenjamin Herrenschmidt };
62ac171c46SBenjamin Herrenschmidt
6375722d39SBenjamin Herrenschmidt /*
6475722d39SBenjamin Herrenschmidt * Utilities & tick thread
6575722d39SBenjamin Herrenschmidt */
6675722d39SBenjamin Herrenschmidt
wf_notify(int event,void * param)6775722d39SBenjamin Herrenschmidt static inline void wf_notify(int event, void *param)
6875722d39SBenjamin Herrenschmidt {
69e041c683SAlan Stern blocking_notifier_call_chain(&wf_client_list, event, param);
7075722d39SBenjamin Herrenschmidt }
7175722d39SBenjamin Herrenschmidt
wf_critical_overtemp(void)72ca94bbabSPaul Bolle static int wf_critical_overtemp(void)
7375722d39SBenjamin Herrenschmidt {
74377e7a27SGreg Kroah-Hartman static char const critical_overtemp_path[] = "/sbin/critical_overtemp";
75377e7a27SGreg Kroah-Hartman char *argv[] = { (char *)critical_overtemp_path, NULL };
7675722d39SBenjamin Herrenschmidt static char *envp[] = { "HOME=/",
7775722d39SBenjamin Herrenschmidt "TERM=linux",
7875722d39SBenjamin Herrenschmidt "PATH=/sbin:/usr/sbin:/bin:/usr/bin",
7975722d39SBenjamin Herrenschmidt NULL };
8075722d39SBenjamin Herrenschmidt
8186313c48SJeremy Fitzhardinge return call_usermodehelper(critical_overtemp_path,
8286313c48SJeremy Fitzhardinge argv, envp, UMH_WAIT_EXEC);
8375722d39SBenjamin Herrenschmidt }
8475722d39SBenjamin Herrenschmidt
wf_thread_func(void * data)8575722d39SBenjamin Herrenschmidt static int wf_thread_func(void *data)
8675722d39SBenjamin Herrenschmidt {
8775722d39SBenjamin Herrenschmidt unsigned long next, delay;
8875722d39SBenjamin Herrenschmidt
8975722d39SBenjamin Herrenschmidt next = jiffies;
9075722d39SBenjamin Herrenschmidt
9175722d39SBenjamin Herrenschmidt DBG("wf: thread started\n");
9275722d39SBenjamin Herrenschmidt
9383144186SRafael J. Wysocki set_freezable();
9475722d39SBenjamin Herrenschmidt while (!kthread_should_stop()) {
9567b60518SJohannes Berg try_to_freeze();
9667b60518SJohannes Berg
9775722d39SBenjamin Herrenschmidt if (time_after_eq(jiffies, next)) {
9875722d39SBenjamin Herrenschmidt wf_notify(WF_EVENT_TICK, NULL);
9975722d39SBenjamin Herrenschmidt if (wf_overtemp) {
10075722d39SBenjamin Herrenschmidt wf_overtemp_counter++;
10175722d39SBenjamin Herrenschmidt /* 10 seconds overtemp, notify userland */
10275722d39SBenjamin Herrenschmidt if (wf_overtemp_counter > 10)
10375722d39SBenjamin Herrenschmidt wf_critical_overtemp();
10475722d39SBenjamin Herrenschmidt /* 30 seconds, shutdown */
10575722d39SBenjamin Herrenschmidt if (wf_overtemp_counter > 30) {
10675722d39SBenjamin Herrenschmidt printk(KERN_ERR "windfarm: Overtemp "
10775722d39SBenjamin Herrenschmidt "for more than 30"
10875722d39SBenjamin Herrenschmidt " seconds, shutting down\n");
10975722d39SBenjamin Herrenschmidt machine_power_off();
11075722d39SBenjamin Herrenschmidt }
11175722d39SBenjamin Herrenschmidt }
11275722d39SBenjamin Herrenschmidt next += HZ;
11375722d39SBenjamin Herrenschmidt }
11475722d39SBenjamin Herrenschmidt
11575722d39SBenjamin Herrenschmidt delay = next - jiffies;
11675722d39SBenjamin Herrenschmidt if (delay <= HZ)
11775722d39SBenjamin Herrenschmidt schedule_timeout_interruptible(delay);
11875722d39SBenjamin Herrenschmidt }
11975722d39SBenjamin Herrenschmidt
12075722d39SBenjamin Herrenschmidt DBG("wf: thread stopped\n");
12175722d39SBenjamin Herrenschmidt
12275722d39SBenjamin Herrenschmidt return 0;
12375722d39SBenjamin Herrenschmidt }
12475722d39SBenjamin Herrenschmidt
wf_start_thread(void)12575722d39SBenjamin Herrenschmidt static void wf_start_thread(void)
12675722d39SBenjamin Herrenschmidt {
12775722d39SBenjamin Herrenschmidt wf_thread = kthread_run(wf_thread_func, NULL, "kwindfarm");
12875722d39SBenjamin Herrenschmidt if (IS_ERR(wf_thread)) {
12975722d39SBenjamin Herrenschmidt printk(KERN_ERR "windfarm: failed to create thread,err %ld\n",
13075722d39SBenjamin Herrenschmidt PTR_ERR(wf_thread));
13175722d39SBenjamin Herrenschmidt wf_thread = NULL;
13275722d39SBenjamin Herrenschmidt }
13375722d39SBenjamin Herrenschmidt }
13475722d39SBenjamin Herrenschmidt
13575722d39SBenjamin Herrenschmidt
wf_stop_thread(void)13675722d39SBenjamin Herrenschmidt static void wf_stop_thread(void)
13775722d39SBenjamin Herrenschmidt {
13875722d39SBenjamin Herrenschmidt if (wf_thread)
13975722d39SBenjamin Herrenschmidt kthread_stop(wf_thread);
14075722d39SBenjamin Herrenschmidt wf_thread = NULL;
14175722d39SBenjamin Herrenschmidt }
14275722d39SBenjamin Herrenschmidt
14375722d39SBenjamin Herrenschmidt /*
14475722d39SBenjamin Herrenschmidt * Controls
14575722d39SBenjamin Herrenschmidt */
14675722d39SBenjamin Herrenschmidt
wf_control_release(struct kref * kref)14775722d39SBenjamin Herrenschmidt static void wf_control_release(struct kref *kref)
14875722d39SBenjamin Herrenschmidt {
14975722d39SBenjamin Herrenschmidt struct wf_control *ct = container_of(kref, struct wf_control, ref);
15075722d39SBenjamin Herrenschmidt
15175722d39SBenjamin Herrenschmidt DBG("wf: Deleting control %s\n", ct->name);
15275722d39SBenjamin Herrenschmidt
15375722d39SBenjamin Herrenschmidt if (ct->ops && ct->ops->release)
15475722d39SBenjamin Herrenschmidt ct->ops->release(ct);
15575722d39SBenjamin Herrenschmidt else
15675722d39SBenjamin Herrenschmidt kfree(ct);
15775722d39SBenjamin Herrenschmidt }
15875722d39SBenjamin Herrenschmidt
wf_show_control(struct device * dev,struct device_attribute * attr,char * buf)159ac171c46SBenjamin Herrenschmidt static ssize_t wf_show_control(struct device *dev,
160ac171c46SBenjamin Herrenschmidt struct device_attribute *attr, char *buf)
161ac171c46SBenjamin Herrenschmidt {
162ac171c46SBenjamin Herrenschmidt struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
163ea5c64b0SBenjamin Herrenschmidt const char *typestr;
164ac171c46SBenjamin Herrenschmidt s32 val = 0;
165ac171c46SBenjamin Herrenschmidt int err;
166ac171c46SBenjamin Herrenschmidt
167ac171c46SBenjamin Herrenschmidt err = ctrl->ops->get_value(ctrl, &val);
1686cd32099SBenjamin Herrenschmidt if (err < 0) {
1696cd32099SBenjamin Herrenschmidt if (err == -EFAULT)
1706cd32099SBenjamin Herrenschmidt return sprintf(buf, "<HW FAULT>\n");
171ac171c46SBenjamin Herrenschmidt return err;
1726cd32099SBenjamin Herrenschmidt }
173ea5c64b0SBenjamin Herrenschmidt switch(ctrl->type) {
174ea5c64b0SBenjamin Herrenschmidt case WF_CONTROL_RPM_FAN:
175ea5c64b0SBenjamin Herrenschmidt typestr = " RPM";
176ea5c64b0SBenjamin Herrenschmidt break;
177ea5c64b0SBenjamin Herrenschmidt case WF_CONTROL_PWM_FAN:
178ea5c64b0SBenjamin Herrenschmidt typestr = " %";
179ea5c64b0SBenjamin Herrenschmidt break;
180ea5c64b0SBenjamin Herrenschmidt default:
181ea5c64b0SBenjamin Herrenschmidt typestr = "";
182ea5c64b0SBenjamin Herrenschmidt }
183ea5c64b0SBenjamin Herrenschmidt return sprintf(buf, "%d%s\n", val, typestr);
184ac171c46SBenjamin Herrenschmidt }
185ac171c46SBenjamin Herrenschmidt
186ac171c46SBenjamin Herrenschmidt /* This is really only for debugging... */
wf_store_control(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)187ac171c46SBenjamin Herrenschmidt static ssize_t wf_store_control(struct device *dev,
188ac171c46SBenjamin Herrenschmidt struct device_attribute *attr,
189ac171c46SBenjamin Herrenschmidt const char *buf, size_t count)
190ac171c46SBenjamin Herrenschmidt {
191ac171c46SBenjamin Herrenschmidt struct wf_control *ctrl = container_of(attr, struct wf_control, attr);
192ac171c46SBenjamin Herrenschmidt int val;
193ac171c46SBenjamin Herrenschmidt int err;
194ac171c46SBenjamin Herrenschmidt char *endp;
195ac171c46SBenjamin Herrenschmidt
196ac171c46SBenjamin Herrenschmidt val = simple_strtoul(buf, &endp, 0);
197ac171c46SBenjamin Herrenschmidt while (endp < buf + count && (*endp == ' ' || *endp == '\n'))
198ac171c46SBenjamin Herrenschmidt ++endp;
199ac171c46SBenjamin Herrenschmidt if (endp - buf < count)
200ac171c46SBenjamin Herrenschmidt return -EINVAL;
201ac171c46SBenjamin Herrenschmidt err = ctrl->ops->set_value(ctrl, val);
202ac171c46SBenjamin Herrenschmidt if (err < 0)
203ac171c46SBenjamin Herrenschmidt return err;
204ac171c46SBenjamin Herrenschmidt return count;
205ac171c46SBenjamin Herrenschmidt }
206ac171c46SBenjamin Herrenschmidt
wf_register_control(struct wf_control * new_ct)20775722d39SBenjamin Herrenschmidt int wf_register_control(struct wf_control *new_ct)
20875722d39SBenjamin Herrenschmidt {
20975722d39SBenjamin Herrenschmidt struct wf_control *ct;
21075722d39SBenjamin Herrenschmidt
211837e9594SIngo Molnar mutex_lock(&wf_lock);
21275722d39SBenjamin Herrenschmidt list_for_each_entry(ct, &wf_controls, link) {
21375722d39SBenjamin Herrenschmidt if (!strcmp(ct->name, new_ct->name)) {
21475722d39SBenjamin Herrenschmidt printk(KERN_WARNING "windfarm: trying to register"
21575722d39SBenjamin Herrenschmidt " duplicate control %s\n", ct->name);
216837e9594SIngo Molnar mutex_unlock(&wf_lock);
21775722d39SBenjamin Herrenschmidt return -EEXIST;
21875722d39SBenjamin Herrenschmidt }
21975722d39SBenjamin Herrenschmidt }
22075722d39SBenjamin Herrenschmidt kref_init(&new_ct->ref);
22175722d39SBenjamin Herrenschmidt list_add(&new_ct->link, &wf_controls);
22275722d39SBenjamin Herrenschmidt
22312765517SWolfram Sang sysfs_attr_init(&new_ct->attr.attr);
224ac171c46SBenjamin Herrenschmidt new_ct->attr.attr.name = new_ct->name;
225ac171c46SBenjamin Herrenschmidt new_ct->attr.attr.mode = 0644;
226ac171c46SBenjamin Herrenschmidt new_ct->attr.show = wf_show_control;
227ac171c46SBenjamin Herrenschmidt new_ct->attr.store = wf_store_control;
228ccd308f0SStephen Rothwell if (device_create_file(&wf_platform_device.dev, &new_ct->attr))
229ccd308f0SStephen Rothwell printk(KERN_WARNING "windfarm: device_create_file failed"
230ccd308f0SStephen Rothwell " for %s\n", new_ct->name);
231ccd308f0SStephen Rothwell /* the subsystem still does useful work without the file */
232ac171c46SBenjamin Herrenschmidt
23375722d39SBenjamin Herrenschmidt DBG("wf: Registered control %s\n", new_ct->name);
23475722d39SBenjamin Herrenschmidt
23575722d39SBenjamin Herrenschmidt wf_notify(WF_EVENT_NEW_CONTROL, new_ct);
236837e9594SIngo Molnar mutex_unlock(&wf_lock);
23775722d39SBenjamin Herrenschmidt
23875722d39SBenjamin Herrenschmidt return 0;
23975722d39SBenjamin Herrenschmidt }
24075722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_register_control);
24175722d39SBenjamin Herrenschmidt
wf_unregister_control(struct wf_control * ct)24275722d39SBenjamin Herrenschmidt void wf_unregister_control(struct wf_control *ct)
24375722d39SBenjamin Herrenschmidt {
244837e9594SIngo Molnar mutex_lock(&wf_lock);
24575722d39SBenjamin Herrenschmidt list_del(&ct->link);
246837e9594SIngo Molnar mutex_unlock(&wf_lock);
24775722d39SBenjamin Herrenschmidt
24875722d39SBenjamin Herrenschmidt DBG("wf: Unregistered control %s\n", ct->name);
24975722d39SBenjamin Herrenschmidt
25075722d39SBenjamin Herrenschmidt kref_put(&ct->ref, wf_control_release);
25175722d39SBenjamin Herrenschmidt }
25275722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_unregister_control);
25375722d39SBenjamin Herrenschmidt
wf_get_control(struct wf_control * ct)25475722d39SBenjamin Herrenschmidt int wf_get_control(struct wf_control *ct)
25575722d39SBenjamin Herrenschmidt {
25675722d39SBenjamin Herrenschmidt if (!try_module_get(ct->ops->owner))
25775722d39SBenjamin Herrenschmidt return -ENODEV;
25875722d39SBenjamin Herrenschmidt kref_get(&ct->ref);
25975722d39SBenjamin Herrenschmidt return 0;
26075722d39SBenjamin Herrenschmidt }
26175722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_get_control);
26275722d39SBenjamin Herrenschmidt
wf_put_control(struct wf_control * ct)26375722d39SBenjamin Herrenschmidt void wf_put_control(struct wf_control *ct)
26475722d39SBenjamin Herrenschmidt {
26575722d39SBenjamin Herrenschmidt struct module *mod = ct->ops->owner;
26675722d39SBenjamin Herrenschmidt kref_put(&ct->ref, wf_control_release);
26775722d39SBenjamin Herrenschmidt module_put(mod);
26875722d39SBenjamin Herrenschmidt }
26975722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_put_control);
27075722d39SBenjamin Herrenschmidt
27175722d39SBenjamin Herrenschmidt
27275722d39SBenjamin Herrenschmidt /*
27375722d39SBenjamin Herrenschmidt * Sensors
27475722d39SBenjamin Herrenschmidt */
27575722d39SBenjamin Herrenschmidt
27675722d39SBenjamin Herrenschmidt
wf_sensor_release(struct kref * kref)27775722d39SBenjamin Herrenschmidt static void wf_sensor_release(struct kref *kref)
27875722d39SBenjamin Herrenschmidt {
27975722d39SBenjamin Herrenschmidt struct wf_sensor *sr = container_of(kref, struct wf_sensor, ref);
28075722d39SBenjamin Herrenschmidt
28175722d39SBenjamin Herrenschmidt DBG("wf: Deleting sensor %s\n", sr->name);
28275722d39SBenjamin Herrenschmidt
28375722d39SBenjamin Herrenschmidt if (sr->ops && sr->ops->release)
28475722d39SBenjamin Herrenschmidt sr->ops->release(sr);
28575722d39SBenjamin Herrenschmidt else
28675722d39SBenjamin Herrenschmidt kfree(sr);
28775722d39SBenjamin Herrenschmidt }
28875722d39SBenjamin Herrenschmidt
wf_show_sensor(struct device * dev,struct device_attribute * attr,char * buf)289ac171c46SBenjamin Herrenschmidt static ssize_t wf_show_sensor(struct device *dev,
290ac171c46SBenjamin Herrenschmidt struct device_attribute *attr, char *buf)
291ac171c46SBenjamin Herrenschmidt {
292ac171c46SBenjamin Herrenschmidt struct wf_sensor *sens = container_of(attr, struct wf_sensor, attr);
293ac171c46SBenjamin Herrenschmidt s32 val = 0;
294ac171c46SBenjamin Herrenschmidt int err;
295ac171c46SBenjamin Herrenschmidt
296ac171c46SBenjamin Herrenschmidt err = sens->ops->get_value(sens, &val);
297ac171c46SBenjamin Herrenschmidt if (err < 0)
298ac171c46SBenjamin Herrenschmidt return err;
299ac171c46SBenjamin Herrenschmidt return sprintf(buf, "%d.%03d\n", FIX32TOPRINT(val));
300ac171c46SBenjamin Herrenschmidt }
301ac171c46SBenjamin Herrenschmidt
wf_register_sensor(struct wf_sensor * new_sr)30275722d39SBenjamin Herrenschmidt int wf_register_sensor(struct wf_sensor *new_sr)
30375722d39SBenjamin Herrenschmidt {
30475722d39SBenjamin Herrenschmidt struct wf_sensor *sr;
30575722d39SBenjamin Herrenschmidt
306837e9594SIngo Molnar mutex_lock(&wf_lock);
30775722d39SBenjamin Herrenschmidt list_for_each_entry(sr, &wf_sensors, link) {
30875722d39SBenjamin Herrenschmidt if (!strcmp(sr->name, new_sr->name)) {
30975722d39SBenjamin Herrenschmidt printk(KERN_WARNING "windfarm: trying to register"
31075722d39SBenjamin Herrenschmidt " duplicate sensor %s\n", sr->name);
311837e9594SIngo Molnar mutex_unlock(&wf_lock);
31275722d39SBenjamin Herrenschmidt return -EEXIST;
31375722d39SBenjamin Herrenschmidt }
31475722d39SBenjamin Herrenschmidt }
31575722d39SBenjamin Herrenschmidt kref_init(&new_sr->ref);
31675722d39SBenjamin Herrenschmidt list_add(&new_sr->link, &wf_sensors);
31775722d39SBenjamin Herrenschmidt
318b35c74daSJohannes Berg sysfs_attr_init(&new_sr->attr.attr);
319ac171c46SBenjamin Herrenschmidt new_sr->attr.attr.name = new_sr->name;
320ac171c46SBenjamin Herrenschmidt new_sr->attr.attr.mode = 0444;
321ac171c46SBenjamin Herrenschmidt new_sr->attr.show = wf_show_sensor;
322ac171c46SBenjamin Herrenschmidt new_sr->attr.store = NULL;
323ccd308f0SStephen Rothwell if (device_create_file(&wf_platform_device.dev, &new_sr->attr))
324ccd308f0SStephen Rothwell printk(KERN_WARNING "windfarm: device_create_file failed"
325ccd308f0SStephen Rothwell " for %s\n", new_sr->name);
326ccd308f0SStephen Rothwell /* the subsystem still does useful work without the file */
327ac171c46SBenjamin Herrenschmidt
32875722d39SBenjamin Herrenschmidt DBG("wf: Registered sensor %s\n", new_sr->name);
32975722d39SBenjamin Herrenschmidt
33075722d39SBenjamin Herrenschmidt wf_notify(WF_EVENT_NEW_SENSOR, new_sr);
331837e9594SIngo Molnar mutex_unlock(&wf_lock);
33275722d39SBenjamin Herrenschmidt
33375722d39SBenjamin Herrenschmidt return 0;
33475722d39SBenjamin Herrenschmidt }
33575722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_register_sensor);
33675722d39SBenjamin Herrenschmidt
wf_unregister_sensor(struct wf_sensor * sr)33775722d39SBenjamin Herrenschmidt void wf_unregister_sensor(struct wf_sensor *sr)
33875722d39SBenjamin Herrenschmidt {
339837e9594SIngo Molnar mutex_lock(&wf_lock);
34075722d39SBenjamin Herrenschmidt list_del(&sr->link);
341837e9594SIngo Molnar mutex_unlock(&wf_lock);
34275722d39SBenjamin Herrenschmidt
34375722d39SBenjamin Herrenschmidt DBG("wf: Unregistered sensor %s\n", sr->name);
34475722d39SBenjamin Herrenschmidt
34575722d39SBenjamin Herrenschmidt wf_put_sensor(sr);
34675722d39SBenjamin Herrenschmidt }
34775722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_unregister_sensor);
34875722d39SBenjamin Herrenschmidt
wf_get_sensor(struct wf_sensor * sr)34975722d39SBenjamin Herrenschmidt int wf_get_sensor(struct wf_sensor *sr)
35075722d39SBenjamin Herrenschmidt {
35175722d39SBenjamin Herrenschmidt if (!try_module_get(sr->ops->owner))
35275722d39SBenjamin Herrenschmidt return -ENODEV;
35375722d39SBenjamin Herrenschmidt kref_get(&sr->ref);
35475722d39SBenjamin Herrenschmidt return 0;
35575722d39SBenjamin Herrenschmidt }
35675722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_get_sensor);
35775722d39SBenjamin Herrenschmidt
wf_put_sensor(struct wf_sensor * sr)35875722d39SBenjamin Herrenschmidt void wf_put_sensor(struct wf_sensor *sr)
35975722d39SBenjamin Herrenschmidt {
36075722d39SBenjamin Herrenschmidt struct module *mod = sr->ops->owner;
36175722d39SBenjamin Herrenschmidt kref_put(&sr->ref, wf_sensor_release);
36275722d39SBenjamin Herrenschmidt module_put(mod);
36375722d39SBenjamin Herrenschmidt }
36475722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_put_sensor);
36575722d39SBenjamin Herrenschmidt
36675722d39SBenjamin Herrenschmidt
36775722d39SBenjamin Herrenschmidt /*
36875722d39SBenjamin Herrenschmidt * Client & notification
36975722d39SBenjamin Herrenschmidt */
37075722d39SBenjamin Herrenschmidt
wf_register_client(struct notifier_block * nb)37175722d39SBenjamin Herrenschmidt int wf_register_client(struct notifier_block *nb)
37275722d39SBenjamin Herrenschmidt {
37375722d39SBenjamin Herrenschmidt int rc;
37475722d39SBenjamin Herrenschmidt struct wf_control *ct;
37575722d39SBenjamin Herrenschmidt struct wf_sensor *sr;
37675722d39SBenjamin Herrenschmidt
377837e9594SIngo Molnar mutex_lock(&wf_lock);
378e041c683SAlan Stern rc = blocking_notifier_chain_register(&wf_client_list, nb);
37975722d39SBenjamin Herrenschmidt if (rc != 0)
38075722d39SBenjamin Herrenschmidt goto bail;
38175722d39SBenjamin Herrenschmidt wf_client_count++;
38275722d39SBenjamin Herrenschmidt list_for_each_entry(ct, &wf_controls, link)
38375722d39SBenjamin Herrenschmidt wf_notify(WF_EVENT_NEW_CONTROL, ct);
38475722d39SBenjamin Herrenschmidt list_for_each_entry(sr, &wf_sensors, link)
38575722d39SBenjamin Herrenschmidt wf_notify(WF_EVENT_NEW_SENSOR, sr);
38675722d39SBenjamin Herrenschmidt if (wf_client_count == 1)
38775722d39SBenjamin Herrenschmidt wf_start_thread();
38875722d39SBenjamin Herrenschmidt bail:
389837e9594SIngo Molnar mutex_unlock(&wf_lock);
39075722d39SBenjamin Herrenschmidt return rc;
39175722d39SBenjamin Herrenschmidt }
39275722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_register_client);
39375722d39SBenjamin Herrenschmidt
wf_unregister_client(struct notifier_block * nb)39475722d39SBenjamin Herrenschmidt int wf_unregister_client(struct notifier_block *nb)
39575722d39SBenjamin Herrenschmidt {
396837e9594SIngo Molnar mutex_lock(&wf_lock);
397e041c683SAlan Stern blocking_notifier_chain_unregister(&wf_client_list, nb);
398fe2b5921SPaul Bolle wf_client_count--;
39975722d39SBenjamin Herrenschmidt if (wf_client_count == 0)
40075722d39SBenjamin Herrenschmidt wf_stop_thread();
401837e9594SIngo Molnar mutex_unlock(&wf_lock);
40275722d39SBenjamin Herrenschmidt
40375722d39SBenjamin Herrenschmidt return 0;
40475722d39SBenjamin Herrenschmidt }
40575722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_unregister_client);
40675722d39SBenjamin Herrenschmidt
wf_set_overtemp(void)40775722d39SBenjamin Herrenschmidt void wf_set_overtemp(void)
40875722d39SBenjamin Herrenschmidt {
409837e9594SIngo Molnar mutex_lock(&wf_lock);
41075722d39SBenjamin Herrenschmidt wf_overtemp++;
41175722d39SBenjamin Herrenschmidt if (wf_overtemp == 1) {
41275722d39SBenjamin Herrenschmidt printk(KERN_WARNING "windfarm: Overtemp condition detected !\n");
41375722d39SBenjamin Herrenschmidt wf_overtemp_counter = 0;
41475722d39SBenjamin Herrenschmidt wf_notify(WF_EVENT_OVERTEMP, NULL);
41575722d39SBenjamin Herrenschmidt }
416837e9594SIngo Molnar mutex_unlock(&wf_lock);
41775722d39SBenjamin Herrenschmidt }
41875722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_set_overtemp);
41975722d39SBenjamin Herrenschmidt
wf_clear_overtemp(void)42075722d39SBenjamin Herrenschmidt void wf_clear_overtemp(void)
42175722d39SBenjamin Herrenschmidt {
422837e9594SIngo Molnar mutex_lock(&wf_lock);
42375722d39SBenjamin Herrenschmidt WARN_ON(wf_overtemp == 0);
42475722d39SBenjamin Herrenschmidt if (wf_overtemp == 0) {
425837e9594SIngo Molnar mutex_unlock(&wf_lock);
42675722d39SBenjamin Herrenschmidt return;
42775722d39SBenjamin Herrenschmidt }
42875722d39SBenjamin Herrenschmidt wf_overtemp--;
42975722d39SBenjamin Herrenschmidt if (wf_overtemp == 0) {
43075722d39SBenjamin Herrenschmidt printk(KERN_WARNING "windfarm: Overtemp condition cleared !\n");
43175722d39SBenjamin Herrenschmidt wf_notify(WF_EVENT_NORMALTEMP, NULL);
43275722d39SBenjamin Herrenschmidt }
433837e9594SIngo Molnar mutex_unlock(&wf_lock);
43475722d39SBenjamin Herrenschmidt }
43575722d39SBenjamin Herrenschmidt EXPORT_SYMBOL_GPL(wf_clear_overtemp);
43675722d39SBenjamin Herrenschmidt
windfarm_core_init(void)43775722d39SBenjamin Herrenschmidt static int __init windfarm_core_init(void)
43875722d39SBenjamin Herrenschmidt {
43975722d39SBenjamin Herrenschmidt DBG("wf: core loaded\n");
44075722d39SBenjamin Herrenschmidt
44175722d39SBenjamin Herrenschmidt platform_device_register(&wf_platform_device);
44275722d39SBenjamin Herrenschmidt return 0;
44375722d39SBenjamin Herrenschmidt }
44475722d39SBenjamin Herrenschmidt
windfarm_core_exit(void)44575722d39SBenjamin Herrenschmidt static void __exit windfarm_core_exit(void)
44675722d39SBenjamin Herrenschmidt {
44775722d39SBenjamin Herrenschmidt BUG_ON(wf_client_count != 0);
44875722d39SBenjamin Herrenschmidt
44975722d39SBenjamin Herrenschmidt DBG("wf: core unloaded\n");
45075722d39SBenjamin Herrenschmidt
45175722d39SBenjamin Herrenschmidt platform_device_unregister(&wf_platform_device);
45275722d39SBenjamin Herrenschmidt }
45375722d39SBenjamin Herrenschmidt
45475722d39SBenjamin Herrenschmidt
45575722d39SBenjamin Herrenschmidt module_init(windfarm_core_init);
45675722d39SBenjamin Herrenschmidt module_exit(windfarm_core_exit);
45775722d39SBenjamin Herrenschmidt
45875722d39SBenjamin Herrenschmidt MODULE_AUTHOR("Benjamin Herrenschmidt <benh@kernel.crashing.org>");
45975722d39SBenjamin Herrenschmidt MODULE_DESCRIPTION("Core component of PowerMac thermal control");
46075722d39SBenjamin Herrenschmidt MODULE_LICENSE("GPL");
46175722d39SBenjamin Herrenschmidt
462