xref: /openbmc/linux/drivers/macintosh/windfarm_core.c (revision 4f2c0a4acffbec01079c28f839422e64ddeff004)
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