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