brintos

brintos / linux-shallow public Read only

0
0
Text · 6.2 KiB · 4ed6d13 Raw
289 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2/* Watchdog timer for machines with the CS5535/CS5536 companion chip3 *4 * Copyright (C) 2006-2007, Advanced Micro Devices, Inc.5 * Copyright (C) 2009  Andres Salomon <dilinger@collabora.co.uk>6 */7 8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt9 10#include <linux/module.h>11#include <linux/moduleparam.h>12#include <linux/types.h>13#include <linux/miscdevice.h>14#include <linux/watchdog.h>15#include <linux/fs.h>16#include <linux/platform_device.h>17#include <linux/reboot.h>18#include <linux/uaccess.h>19 20#include <linux/cs5535.h>21 22#define GEODEWDT_HZ 50023#define GEODEWDT_SCALE 624#define GEODEWDT_MAX_SECONDS 13125 26#define WDT_FLAGS_OPEN 127#define WDT_FLAGS_ORPHAN 228 29#define DRV_NAME "geodewdt"30#define WATCHDOG_NAME "Geode GX/LX WDT"31#define WATCHDOG_TIMEOUT 6032 33static int timeout = WATCHDOG_TIMEOUT;34module_param(timeout, int, 0);35MODULE_PARM_DESC(timeout,36	"Watchdog timeout in seconds. 1<= timeout <=131, default="37				__MODULE_STRING(WATCHDOG_TIMEOUT) ".");38 39static bool nowayout = WATCHDOG_NOWAYOUT;40module_param(nowayout, bool, 0);41MODULE_PARM_DESC(nowayout,42	"Watchdog cannot be stopped once started (default="43				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");44 45static struct platform_device *geodewdt_platform_device;46static unsigned long wdt_flags;47static struct cs5535_mfgpt_timer *wdt_timer;48static int safe_close;49 50static void geodewdt_ping(void)51{52	/* Stop the counter */53	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);54 55	/* Reset the counter */56	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);57 58	/* Enable the counter */59	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);60}61 62static void geodewdt_disable(void)63{64	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);65	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);66}67 68static int geodewdt_set_heartbeat(int val)69{70	if (val < 1 || val > GEODEWDT_MAX_SECONDS)71		return -EINVAL;72 73	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, 0);74	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_CMP2, val * GEODEWDT_HZ);75	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_COUNTER, 0);76	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP, MFGPT_SETUP_CNTEN);77 78	timeout = val;79	return 0;80}81 82static int geodewdt_open(struct inode *inode, struct file *file)83{84	if (test_and_set_bit(WDT_FLAGS_OPEN, &wdt_flags))85		return -EBUSY;86 87	if (!test_and_clear_bit(WDT_FLAGS_ORPHAN, &wdt_flags))88		__module_get(THIS_MODULE);89 90	geodewdt_ping();91	return stream_open(inode, file);92}93 94static int geodewdt_release(struct inode *inode, struct file *file)95{96	if (safe_close) {97		geodewdt_disable();98		module_put(THIS_MODULE);99	} else {100		pr_crit("Unexpected close - watchdog is not stopping\n");101		geodewdt_ping();102 103		set_bit(WDT_FLAGS_ORPHAN, &wdt_flags);104	}105 106	clear_bit(WDT_FLAGS_OPEN, &wdt_flags);107	safe_close = 0;108	return 0;109}110 111static ssize_t geodewdt_write(struct file *file, const char __user *data,112				size_t len, loff_t *ppos)113{114	if (len) {115		if (!nowayout) {116			size_t i;117			safe_close = 0;118 119			for (i = 0; i != len; i++) {120				char c;121 122				if (get_user(c, data + i))123					return -EFAULT;124 125				if (c == 'V')126					safe_close = 1;127			}128		}129 130		geodewdt_ping();131	}132	return len;133}134 135static long geodewdt_ioctl(struct file *file, unsigned int cmd,136				unsigned long arg)137{138	void __user *argp = (void __user *)arg;139	int __user *p = argp;140	int interval;141 142	static const struct watchdog_info ident = {143		.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING144		| WDIOF_MAGICCLOSE,145		.firmware_version =     1,146		.identity =             WATCHDOG_NAME,147	};148 149	switch (cmd) {150	case WDIOC_GETSUPPORT:151		return copy_to_user(argp, &ident,152				    sizeof(ident)) ? -EFAULT : 0;153	case WDIOC_GETSTATUS:154	case WDIOC_GETBOOTSTATUS:155		return put_user(0, p);156 157	case WDIOC_SETOPTIONS:158	{159		int options, ret = -EINVAL;160 161		if (get_user(options, p))162			return -EFAULT;163 164		if (options & WDIOS_DISABLECARD) {165			geodewdt_disable();166			ret = 0;167		}168 169		if (options & WDIOS_ENABLECARD) {170			geodewdt_ping();171			ret = 0;172		}173 174		return ret;175	}176	case WDIOC_KEEPALIVE:177		geodewdt_ping();178		return 0;179 180	case WDIOC_SETTIMEOUT:181		if (get_user(interval, p))182			return -EFAULT;183 184		if (geodewdt_set_heartbeat(interval))185			return -EINVAL;186		fallthrough;187	case WDIOC_GETTIMEOUT:188		return put_user(timeout, p);189 190	default:191		return -ENOTTY;192	}193 194	return 0;195}196 197static const struct file_operations geodewdt_fops = {198	.owner          = THIS_MODULE,199	.write          = geodewdt_write,200	.unlocked_ioctl = geodewdt_ioctl,201	.compat_ioctl	= compat_ptr_ioctl,202	.open           = geodewdt_open,203	.release        = geodewdt_release,204};205 206static struct miscdevice geodewdt_miscdev = {207	.minor = WATCHDOG_MINOR,208	.name = "watchdog",209	.fops = &geodewdt_fops,210};211 212static int __init geodewdt_probe(struct platform_device *dev)213{214	int ret;215 216	wdt_timer = cs5535_mfgpt_alloc_timer(MFGPT_TIMER_ANY, MFGPT_DOMAIN_WORKING);217	if (!wdt_timer) {218		pr_err("No timers were available\n");219		return -ENODEV;220	}221 222	/* Set up the timer */223 224	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_SETUP,225			  GEODEWDT_SCALE | (3 << 8));226 227	/* Set up comparator 2 to reset when the event fires */228	cs5535_mfgpt_toggle_event(wdt_timer, MFGPT_CMP2, MFGPT_EVENT_RESET, 1);229 230	/* Set up the initial timeout */231 232	cs5535_mfgpt_write(wdt_timer, MFGPT_REG_CMP2,233		timeout * GEODEWDT_HZ);234 235	ret = misc_register(&geodewdt_miscdev);236 237	return ret;238}239 240static void geodewdt_remove(struct platform_device *dev)241{242	misc_deregister(&geodewdt_miscdev);243}244 245static void geodewdt_shutdown(struct platform_device *dev)246{247	geodewdt_disable();248}249 250static struct platform_driver geodewdt_driver = {251	.remove_new	= geodewdt_remove,252	.shutdown	= geodewdt_shutdown,253	.driver		= {254		.name	= DRV_NAME,255	},256};257 258static int __init geodewdt_init(void)259{260	int ret;261 262	geodewdt_platform_device = platform_device_register_simple(DRV_NAME,263								-1, NULL, 0);264	if (IS_ERR(geodewdt_platform_device))265		return PTR_ERR(geodewdt_platform_device);266 267	ret = platform_driver_probe(&geodewdt_driver, geodewdt_probe);268	if (ret)269		goto err;270 271	return 0;272err:273	platform_device_unregister(geodewdt_platform_device);274	return ret;275}276 277static void __exit geodewdt_exit(void)278{279	platform_device_unregister(geodewdt_platform_device);280	platform_driver_unregister(&geodewdt_driver);281}282 283module_init(geodewdt_init);284module_exit(geodewdt_exit);285 286MODULE_AUTHOR("Advanced Micro Devices, Inc");287MODULE_DESCRIPTION("Geode GX/LX Watchdog Driver");288MODULE_LICENSE("GPL");289