brintos

brintos / linux-shallow public Read only

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