xref: /OK3568_Linux_fs/kernel/drivers/watchdog/wdrtas.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-or-later
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun  * FIXME: add wdrtas_get_status and wdrtas_get_boot_status as soon as
4*4882a593Smuzhiyun  * RTAS calls are available
5*4882a593Smuzhiyun  */
6*4882a593Smuzhiyun 
7*4882a593Smuzhiyun /*
8*4882a593Smuzhiyun  * RTAS watchdog driver
9*4882a593Smuzhiyun  *
10*4882a593Smuzhiyun  * (C) Copyright IBM Corp. 2005
11*4882a593Smuzhiyun  * device driver to exploit watchdog RTAS functions
12*4882a593Smuzhiyun  *
13*4882a593Smuzhiyun  * Authors : Utz Bacher <utz.bacher@de.ibm.com>
14*4882a593Smuzhiyun  */
15*4882a593Smuzhiyun 
16*4882a593Smuzhiyun #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
17*4882a593Smuzhiyun 
18*4882a593Smuzhiyun #include <linux/fs.h>
19*4882a593Smuzhiyun #include <linux/init.h>
20*4882a593Smuzhiyun #include <linux/kernel.h>
21*4882a593Smuzhiyun #include <linux/miscdevice.h>
22*4882a593Smuzhiyun #include <linux/module.h>
23*4882a593Smuzhiyun #include <linux/notifier.h>
24*4882a593Smuzhiyun #include <linux/reboot.h>
25*4882a593Smuzhiyun #include <linux/types.h>
26*4882a593Smuzhiyun #include <linux/watchdog.h>
27*4882a593Smuzhiyun #include <linux/uaccess.h>
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #include <asm/rtas.h>
30*4882a593Smuzhiyun 
31*4882a593Smuzhiyun #define WDRTAS_MAGIC_CHAR		42
32*4882a593Smuzhiyun #define WDRTAS_SUPPORTED_MASK		(WDIOF_SETTIMEOUT | \
33*4882a593Smuzhiyun 					 WDIOF_MAGICCLOSE)
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun MODULE_AUTHOR("Utz Bacher <utz.bacher@de.ibm.com>");
36*4882a593Smuzhiyun MODULE_DESCRIPTION("RTAS watchdog driver");
37*4882a593Smuzhiyun MODULE_LICENSE("GPL");
38*4882a593Smuzhiyun 
39*4882a593Smuzhiyun static bool wdrtas_nowayout = WATCHDOG_NOWAYOUT;
40*4882a593Smuzhiyun static atomic_t wdrtas_miscdev_open = ATOMIC_INIT(0);
41*4882a593Smuzhiyun static char wdrtas_expect_close;
42*4882a593Smuzhiyun 
43*4882a593Smuzhiyun static int wdrtas_interval;
44*4882a593Smuzhiyun 
45*4882a593Smuzhiyun #define WDRTAS_THERMAL_SENSOR		3
46*4882a593Smuzhiyun static int wdrtas_token_get_sensor_state;
47*4882a593Smuzhiyun #define WDRTAS_SURVEILLANCE_IND		9000
48*4882a593Smuzhiyun static int wdrtas_token_set_indicator;
49*4882a593Smuzhiyun #define WDRTAS_SP_SPI			28
50*4882a593Smuzhiyun static int wdrtas_token_get_sp;
51*4882a593Smuzhiyun static int wdrtas_token_event_scan;
52*4882a593Smuzhiyun 
53*4882a593Smuzhiyun #define WDRTAS_DEFAULT_INTERVAL		300
54*4882a593Smuzhiyun 
55*4882a593Smuzhiyun #define WDRTAS_LOGBUFFER_LEN		128
56*4882a593Smuzhiyun static char wdrtas_logbuffer[WDRTAS_LOGBUFFER_LEN];
57*4882a593Smuzhiyun 
58*4882a593Smuzhiyun 
59*4882a593Smuzhiyun /*** watchdog access functions */
60*4882a593Smuzhiyun 
61*4882a593Smuzhiyun /**
62*4882a593Smuzhiyun  * wdrtas_set_interval - sets the watchdog interval
63*4882a593Smuzhiyun  * @interval: new interval
64*4882a593Smuzhiyun  *
65*4882a593Smuzhiyun  * returns 0 on success, <0 on failures
66*4882a593Smuzhiyun  *
67*4882a593Smuzhiyun  * wdrtas_set_interval sets the watchdog keepalive interval by calling the
68*4882a593Smuzhiyun  * RTAS function set-indicator (surveillance). The unit of interval is
69*4882a593Smuzhiyun  * seconds.
70*4882a593Smuzhiyun  */
71*4882a593Smuzhiyun 
wdrtas_set_interval(int interval)72*4882a593Smuzhiyun static int wdrtas_set_interval(int interval)
73*4882a593Smuzhiyun {
74*4882a593Smuzhiyun 	long result;
75*4882a593Smuzhiyun 	static int print_msg = 10;
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun 	/* rtas uses minutes */
78*4882a593Smuzhiyun 	interval = (interval + 59) / 60;
79*4882a593Smuzhiyun 
80*4882a593Smuzhiyun 	result = rtas_call(wdrtas_token_set_indicator, 3, 1, NULL,
81*4882a593Smuzhiyun 			   WDRTAS_SURVEILLANCE_IND, 0, interval);
82*4882a593Smuzhiyun 	if (result < 0 && print_msg) {
83*4882a593Smuzhiyun 		pr_err("setting the watchdog to %i timeout failed: %li\n",
84*4882a593Smuzhiyun 		       interval, result);
85*4882a593Smuzhiyun 		print_msg--;
86*4882a593Smuzhiyun 	}
87*4882a593Smuzhiyun 
88*4882a593Smuzhiyun 	return result;
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun 
91*4882a593Smuzhiyun #define WDRTAS_SP_SPI_LEN 4
92*4882a593Smuzhiyun 
93*4882a593Smuzhiyun /**
94*4882a593Smuzhiyun  * wdrtas_get_interval - returns the current watchdog interval
95*4882a593Smuzhiyun  * @fallback_value: value (in seconds) to use, if the RTAS call fails
96*4882a593Smuzhiyun  *
97*4882a593Smuzhiyun  * returns the interval
98*4882a593Smuzhiyun  *
99*4882a593Smuzhiyun  * wdrtas_get_interval returns the current watchdog keepalive interval
100*4882a593Smuzhiyun  * as reported by the RTAS function ibm,get-system-parameter. The unit
101*4882a593Smuzhiyun  * of the return value is seconds.
102*4882a593Smuzhiyun  */
wdrtas_get_interval(int fallback_value)103*4882a593Smuzhiyun static int wdrtas_get_interval(int fallback_value)
104*4882a593Smuzhiyun {
105*4882a593Smuzhiyun 	long result;
106*4882a593Smuzhiyun 	char value[WDRTAS_SP_SPI_LEN];
107*4882a593Smuzhiyun 
108*4882a593Smuzhiyun 	spin_lock(&rtas_data_buf_lock);
109*4882a593Smuzhiyun 	memset(rtas_data_buf, 0, WDRTAS_SP_SPI_LEN);
110*4882a593Smuzhiyun 	result = rtas_call(wdrtas_token_get_sp, 3, 1, NULL,
111*4882a593Smuzhiyun 			   WDRTAS_SP_SPI, __pa(rtas_data_buf),
112*4882a593Smuzhiyun 			   WDRTAS_SP_SPI_LEN);
113*4882a593Smuzhiyun 
114*4882a593Smuzhiyun 	memcpy(value, rtas_data_buf, WDRTAS_SP_SPI_LEN);
115*4882a593Smuzhiyun 	spin_unlock(&rtas_data_buf_lock);
116*4882a593Smuzhiyun 
117*4882a593Smuzhiyun 	if (value[0] != 0 || value[1] != 2 || value[3] != 0 || result < 0) {
118*4882a593Smuzhiyun 		pr_warn("could not get sp_spi watchdog timeout (%li). Continuing\n",
119*4882a593Smuzhiyun 			result);
120*4882a593Smuzhiyun 		return fallback_value;
121*4882a593Smuzhiyun 	}
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun 	/* rtas uses minutes */
124*4882a593Smuzhiyun 	return ((int)value[2]) * 60;
125*4882a593Smuzhiyun }
126*4882a593Smuzhiyun 
127*4882a593Smuzhiyun /**
128*4882a593Smuzhiyun  * wdrtas_timer_start - starts watchdog
129*4882a593Smuzhiyun  *
130*4882a593Smuzhiyun  * wdrtas_timer_start starts the watchdog by calling the RTAS function
131*4882a593Smuzhiyun  * set-interval (surveillance)
132*4882a593Smuzhiyun  */
wdrtas_timer_start(void)133*4882a593Smuzhiyun static void wdrtas_timer_start(void)
134*4882a593Smuzhiyun {
135*4882a593Smuzhiyun 	wdrtas_set_interval(wdrtas_interval);
136*4882a593Smuzhiyun }
137*4882a593Smuzhiyun 
138*4882a593Smuzhiyun /**
139*4882a593Smuzhiyun  * wdrtas_timer_stop - stops watchdog
140*4882a593Smuzhiyun  *
141*4882a593Smuzhiyun  * wdrtas_timer_stop stops the watchdog timer by calling the RTAS function
142*4882a593Smuzhiyun  * set-interval (surveillance)
143*4882a593Smuzhiyun  */
wdrtas_timer_stop(void)144*4882a593Smuzhiyun static void wdrtas_timer_stop(void)
145*4882a593Smuzhiyun {
146*4882a593Smuzhiyun 	wdrtas_set_interval(0);
147*4882a593Smuzhiyun }
148*4882a593Smuzhiyun 
149*4882a593Smuzhiyun /**
150*4882a593Smuzhiyun  * wdrtas_timer_keepalive - resets watchdog timer to keep system alive
151*4882a593Smuzhiyun  *
152*4882a593Smuzhiyun  * wdrtas_timer_keepalive restarts the watchdog timer by calling the
153*4882a593Smuzhiyun  * RTAS function event-scan and repeats these calls as long as there are
154*4882a593Smuzhiyun  * events available. All events will be dumped.
155*4882a593Smuzhiyun  */
wdrtas_timer_keepalive(void)156*4882a593Smuzhiyun static void wdrtas_timer_keepalive(void)
157*4882a593Smuzhiyun {
158*4882a593Smuzhiyun 	long result;
159*4882a593Smuzhiyun 
160*4882a593Smuzhiyun 	do {
161*4882a593Smuzhiyun 		result = rtas_call(wdrtas_token_event_scan, 4, 1, NULL,
162*4882a593Smuzhiyun 				   RTAS_EVENT_SCAN_ALL_EVENTS, 0,
163*4882a593Smuzhiyun 				   (void *)__pa(wdrtas_logbuffer),
164*4882a593Smuzhiyun 				   WDRTAS_LOGBUFFER_LEN);
165*4882a593Smuzhiyun 		if (result < 0)
166*4882a593Smuzhiyun 			pr_err("event-scan failed: %li\n", result);
167*4882a593Smuzhiyun 		if (result == 0)
168*4882a593Smuzhiyun 			print_hex_dump(KERN_INFO, "dumping event, data: ",
169*4882a593Smuzhiyun 				DUMP_PREFIX_OFFSET, 16, 1,
170*4882a593Smuzhiyun 				wdrtas_logbuffer, WDRTAS_LOGBUFFER_LEN, false);
171*4882a593Smuzhiyun 	} while (result == 0);
172*4882a593Smuzhiyun }
173*4882a593Smuzhiyun 
174*4882a593Smuzhiyun /**
175*4882a593Smuzhiyun  * wdrtas_get_temperature - returns current temperature
176*4882a593Smuzhiyun  *
177*4882a593Smuzhiyun  * returns temperature or <0 on failures
178*4882a593Smuzhiyun  *
179*4882a593Smuzhiyun  * wdrtas_get_temperature returns the current temperature in Fahrenheit. It
180*4882a593Smuzhiyun  * uses the RTAS call get-sensor-state, token 3 to do so
181*4882a593Smuzhiyun  */
wdrtas_get_temperature(void)182*4882a593Smuzhiyun static int wdrtas_get_temperature(void)
183*4882a593Smuzhiyun {
184*4882a593Smuzhiyun 	int result;
185*4882a593Smuzhiyun 	int temperature = 0;
186*4882a593Smuzhiyun 
187*4882a593Smuzhiyun 	result = rtas_get_sensor(WDRTAS_THERMAL_SENSOR, 0, &temperature);
188*4882a593Smuzhiyun 
189*4882a593Smuzhiyun 	if (result < 0)
190*4882a593Smuzhiyun 		pr_warn("reading the thermal sensor failed: %i\n", result);
191*4882a593Smuzhiyun 	else
192*4882a593Smuzhiyun 		temperature = ((temperature * 9) / 5) + 32; /* fahrenheit */
193*4882a593Smuzhiyun 
194*4882a593Smuzhiyun 	return temperature;
195*4882a593Smuzhiyun }
196*4882a593Smuzhiyun 
197*4882a593Smuzhiyun /**
198*4882a593Smuzhiyun  * wdrtas_get_status - returns the status of the watchdog
199*4882a593Smuzhiyun  *
200*4882a593Smuzhiyun  * returns a bitmask of defines WDIOF_... as defined in
201*4882a593Smuzhiyun  * include/linux/watchdog.h
202*4882a593Smuzhiyun  */
wdrtas_get_status(void)203*4882a593Smuzhiyun static int wdrtas_get_status(void)
204*4882a593Smuzhiyun {
205*4882a593Smuzhiyun 	return 0; /* TODO */
206*4882a593Smuzhiyun }
207*4882a593Smuzhiyun 
208*4882a593Smuzhiyun /**
209*4882a593Smuzhiyun  * wdrtas_get_boot_status - returns the reason for the last boot
210*4882a593Smuzhiyun  *
211*4882a593Smuzhiyun  * returns a bitmask of defines WDIOF_... as defined in
212*4882a593Smuzhiyun  * include/linux/watchdog.h, indicating why the watchdog rebooted the system
213*4882a593Smuzhiyun  */
wdrtas_get_boot_status(void)214*4882a593Smuzhiyun static int wdrtas_get_boot_status(void)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun 	return 0; /* TODO */
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun /*** watchdog API and operations stuff */
220*4882a593Smuzhiyun 
221*4882a593Smuzhiyun /* wdrtas_write - called when watchdog device is written to
222*4882a593Smuzhiyun  * @file: file structure
223*4882a593Smuzhiyun  * @buf: user buffer with data
224*4882a593Smuzhiyun  * @len: amount to data written
225*4882a593Smuzhiyun  * @ppos: position in file
226*4882a593Smuzhiyun  *
227*4882a593Smuzhiyun  * returns the number of successfully processed characters, which is always
228*4882a593Smuzhiyun  * the number of bytes passed to this function
229*4882a593Smuzhiyun  *
230*4882a593Smuzhiyun  * wdrtas_write processes all the data given to it and looks for the magic
231*4882a593Smuzhiyun  * character 'V'. This character allows the watchdog device to be closed
232*4882a593Smuzhiyun  * properly.
233*4882a593Smuzhiyun  */
wdrtas_write(struct file * file,const char __user * buf,size_t len,loff_t * ppos)234*4882a593Smuzhiyun static ssize_t wdrtas_write(struct file *file, const char __user *buf,
235*4882a593Smuzhiyun 	     size_t len, loff_t *ppos)
236*4882a593Smuzhiyun {
237*4882a593Smuzhiyun 	int i;
238*4882a593Smuzhiyun 	char c;
239*4882a593Smuzhiyun 
240*4882a593Smuzhiyun 	if (!len)
241*4882a593Smuzhiyun 		goto out;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun 	if (!wdrtas_nowayout) {
244*4882a593Smuzhiyun 		wdrtas_expect_close = 0;
245*4882a593Smuzhiyun 		/* look for 'V' */
246*4882a593Smuzhiyun 		for (i = 0; i < len; i++) {
247*4882a593Smuzhiyun 			if (get_user(c, buf + i))
248*4882a593Smuzhiyun 				return -EFAULT;
249*4882a593Smuzhiyun 			/* allow to close device */
250*4882a593Smuzhiyun 			if (c == 'V')
251*4882a593Smuzhiyun 				wdrtas_expect_close = WDRTAS_MAGIC_CHAR;
252*4882a593Smuzhiyun 		}
253*4882a593Smuzhiyun 	}
254*4882a593Smuzhiyun 
255*4882a593Smuzhiyun 	wdrtas_timer_keepalive();
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun out:
258*4882a593Smuzhiyun 	return len;
259*4882a593Smuzhiyun }
260*4882a593Smuzhiyun 
261*4882a593Smuzhiyun /**
262*4882a593Smuzhiyun  * wdrtas_ioctl - ioctl function for the watchdog device
263*4882a593Smuzhiyun  * @file: file structure
264*4882a593Smuzhiyun  * @cmd: command for ioctl
265*4882a593Smuzhiyun  * @arg: argument pointer
266*4882a593Smuzhiyun  *
267*4882a593Smuzhiyun  * returns 0 on success, <0 on failure
268*4882a593Smuzhiyun  *
269*4882a593Smuzhiyun  * wdrtas_ioctl implements the watchdog API ioctls
270*4882a593Smuzhiyun  */
271*4882a593Smuzhiyun 
wdrtas_ioctl(struct file * file,unsigned int cmd,unsigned long arg)272*4882a593Smuzhiyun static long wdrtas_ioctl(struct file *file, unsigned int cmd,
273*4882a593Smuzhiyun 							unsigned long arg)
274*4882a593Smuzhiyun {
275*4882a593Smuzhiyun 	int __user *argp = (void __user *)arg;
276*4882a593Smuzhiyun 	int i;
277*4882a593Smuzhiyun 	static const struct watchdog_info wdinfo = {
278*4882a593Smuzhiyun 		.options = WDRTAS_SUPPORTED_MASK,
279*4882a593Smuzhiyun 		.firmware_version = 0,
280*4882a593Smuzhiyun 		.identity = "wdrtas",
281*4882a593Smuzhiyun 	};
282*4882a593Smuzhiyun 
283*4882a593Smuzhiyun 	switch (cmd) {
284*4882a593Smuzhiyun 	case WDIOC_GETSUPPORT:
285*4882a593Smuzhiyun 		if (copy_to_user(argp, &wdinfo, sizeof(wdinfo)))
286*4882a593Smuzhiyun 			return -EFAULT;
287*4882a593Smuzhiyun 		return 0;
288*4882a593Smuzhiyun 
289*4882a593Smuzhiyun 	case WDIOC_GETSTATUS:
290*4882a593Smuzhiyun 		i = wdrtas_get_status();
291*4882a593Smuzhiyun 		return put_user(i, argp);
292*4882a593Smuzhiyun 
293*4882a593Smuzhiyun 	case WDIOC_GETBOOTSTATUS:
294*4882a593Smuzhiyun 		i = wdrtas_get_boot_status();
295*4882a593Smuzhiyun 		return put_user(i, argp);
296*4882a593Smuzhiyun 
297*4882a593Smuzhiyun 	case WDIOC_GETTEMP:
298*4882a593Smuzhiyun 		if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE)
299*4882a593Smuzhiyun 			return -EOPNOTSUPP;
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun 		i = wdrtas_get_temperature();
302*4882a593Smuzhiyun 		return put_user(i, argp);
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun 	case WDIOC_SETOPTIONS:
305*4882a593Smuzhiyun 		if (get_user(i, argp))
306*4882a593Smuzhiyun 			return -EFAULT;
307*4882a593Smuzhiyun 		if (i & WDIOS_DISABLECARD)
308*4882a593Smuzhiyun 			wdrtas_timer_stop();
309*4882a593Smuzhiyun 		if (i & WDIOS_ENABLECARD) {
310*4882a593Smuzhiyun 			wdrtas_timer_keepalive();
311*4882a593Smuzhiyun 			wdrtas_timer_start();
312*4882a593Smuzhiyun 		}
313*4882a593Smuzhiyun 		/* not implemented. Done by H8
314*4882a593Smuzhiyun 		if (i & WDIOS_TEMPPANIC) {
315*4882a593Smuzhiyun 		} */
316*4882a593Smuzhiyun 		return 0;
317*4882a593Smuzhiyun 
318*4882a593Smuzhiyun 	case WDIOC_KEEPALIVE:
319*4882a593Smuzhiyun 		wdrtas_timer_keepalive();
320*4882a593Smuzhiyun 		return 0;
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun 	case WDIOC_SETTIMEOUT:
323*4882a593Smuzhiyun 		if (get_user(i, argp))
324*4882a593Smuzhiyun 			return -EFAULT;
325*4882a593Smuzhiyun 
326*4882a593Smuzhiyun 		if (wdrtas_set_interval(i))
327*4882a593Smuzhiyun 			return -EINVAL;
328*4882a593Smuzhiyun 
329*4882a593Smuzhiyun 		wdrtas_timer_keepalive();
330*4882a593Smuzhiyun 
331*4882a593Smuzhiyun 		if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
332*4882a593Smuzhiyun 			wdrtas_interval = i;
333*4882a593Smuzhiyun 		else
334*4882a593Smuzhiyun 			wdrtas_interval = wdrtas_get_interval(i);
335*4882a593Smuzhiyun 		fallthrough;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun 	case WDIOC_GETTIMEOUT:
338*4882a593Smuzhiyun 		return put_user(wdrtas_interval, argp);
339*4882a593Smuzhiyun 
340*4882a593Smuzhiyun 	default:
341*4882a593Smuzhiyun 		return -ENOTTY;
342*4882a593Smuzhiyun 	}
343*4882a593Smuzhiyun }
344*4882a593Smuzhiyun 
345*4882a593Smuzhiyun /**
346*4882a593Smuzhiyun  * wdrtas_open - open function of watchdog device
347*4882a593Smuzhiyun  * @inode: inode structure
348*4882a593Smuzhiyun  * @file: file structure
349*4882a593Smuzhiyun  *
350*4882a593Smuzhiyun  * returns 0 on success, -EBUSY if the file has been opened already, <0 on
351*4882a593Smuzhiyun  * other failures
352*4882a593Smuzhiyun  *
353*4882a593Smuzhiyun  * function called when watchdog device is opened
354*4882a593Smuzhiyun  */
wdrtas_open(struct inode * inode,struct file * file)355*4882a593Smuzhiyun static int wdrtas_open(struct inode *inode, struct file *file)
356*4882a593Smuzhiyun {
357*4882a593Smuzhiyun 	/* only open once */
358*4882a593Smuzhiyun 	if (atomic_inc_return(&wdrtas_miscdev_open) > 1) {
359*4882a593Smuzhiyun 		atomic_dec(&wdrtas_miscdev_open);
360*4882a593Smuzhiyun 		return -EBUSY;
361*4882a593Smuzhiyun 	}
362*4882a593Smuzhiyun 
363*4882a593Smuzhiyun 	wdrtas_timer_start();
364*4882a593Smuzhiyun 	wdrtas_timer_keepalive();
365*4882a593Smuzhiyun 
366*4882a593Smuzhiyun 	return stream_open(inode, file);
367*4882a593Smuzhiyun }
368*4882a593Smuzhiyun 
369*4882a593Smuzhiyun /**
370*4882a593Smuzhiyun  * wdrtas_close - close function of watchdog device
371*4882a593Smuzhiyun  * @inode: inode structure
372*4882a593Smuzhiyun  * @file: file structure
373*4882a593Smuzhiyun  *
374*4882a593Smuzhiyun  * returns 0 on success
375*4882a593Smuzhiyun  *
376*4882a593Smuzhiyun  * close function. Always succeeds
377*4882a593Smuzhiyun  */
wdrtas_close(struct inode * inode,struct file * file)378*4882a593Smuzhiyun static int wdrtas_close(struct inode *inode, struct file *file)
379*4882a593Smuzhiyun {
380*4882a593Smuzhiyun 	/* only stop watchdog, if this was announced using 'V' before */
381*4882a593Smuzhiyun 	if (wdrtas_expect_close == WDRTAS_MAGIC_CHAR)
382*4882a593Smuzhiyun 		wdrtas_timer_stop();
383*4882a593Smuzhiyun 	else {
384*4882a593Smuzhiyun 		pr_warn("got unexpected close. Watchdog not stopped.\n");
385*4882a593Smuzhiyun 		wdrtas_timer_keepalive();
386*4882a593Smuzhiyun 	}
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun 	wdrtas_expect_close = 0;
389*4882a593Smuzhiyun 	atomic_dec(&wdrtas_miscdev_open);
390*4882a593Smuzhiyun 	return 0;
391*4882a593Smuzhiyun }
392*4882a593Smuzhiyun 
393*4882a593Smuzhiyun /**
394*4882a593Smuzhiyun  * wdrtas_temp_read - gives back the temperature in fahrenheit
395*4882a593Smuzhiyun  * @file: file structure
396*4882a593Smuzhiyun  * @buf: user buffer
397*4882a593Smuzhiyun  * @count: number of bytes to be read
398*4882a593Smuzhiyun  * @ppos: position in file
399*4882a593Smuzhiyun  *
400*4882a593Smuzhiyun  * returns always 1 or -EFAULT in case of user space copy failures, <0 on
401*4882a593Smuzhiyun  * other failures
402*4882a593Smuzhiyun  *
403*4882a593Smuzhiyun  * wdrtas_temp_read gives the temperature to the users by copying this
404*4882a593Smuzhiyun  * value as one byte into the user space buffer. The unit is Fahrenheit...
405*4882a593Smuzhiyun  */
wdrtas_temp_read(struct file * file,char __user * buf,size_t count,loff_t * ppos)406*4882a593Smuzhiyun static ssize_t wdrtas_temp_read(struct file *file, char __user *buf,
407*4882a593Smuzhiyun 		 size_t count, loff_t *ppos)
408*4882a593Smuzhiyun {
409*4882a593Smuzhiyun 	int temperature = 0;
410*4882a593Smuzhiyun 
411*4882a593Smuzhiyun 	temperature = wdrtas_get_temperature();
412*4882a593Smuzhiyun 	if (temperature < 0)
413*4882a593Smuzhiyun 		return temperature;
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun 	if (copy_to_user(buf, &temperature, 1))
416*4882a593Smuzhiyun 		return -EFAULT;
417*4882a593Smuzhiyun 
418*4882a593Smuzhiyun 	return 1;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun /**
422*4882a593Smuzhiyun  * wdrtas_temp_open - open function of temperature device
423*4882a593Smuzhiyun  * @inode: inode structure
424*4882a593Smuzhiyun  * @file: file structure
425*4882a593Smuzhiyun  *
426*4882a593Smuzhiyun  * returns 0 on success, <0 on failure
427*4882a593Smuzhiyun  *
428*4882a593Smuzhiyun  * function called when temperature device is opened
429*4882a593Smuzhiyun  */
wdrtas_temp_open(struct inode * inode,struct file * file)430*4882a593Smuzhiyun static int wdrtas_temp_open(struct inode *inode, struct file *file)
431*4882a593Smuzhiyun {
432*4882a593Smuzhiyun 	return stream_open(inode, file);
433*4882a593Smuzhiyun }
434*4882a593Smuzhiyun 
435*4882a593Smuzhiyun /**
436*4882a593Smuzhiyun  * wdrtas_temp_close - close function of temperature device
437*4882a593Smuzhiyun  * @inode: inode structure
438*4882a593Smuzhiyun  * @file: file structure
439*4882a593Smuzhiyun  *
440*4882a593Smuzhiyun  * returns 0 on success
441*4882a593Smuzhiyun  *
442*4882a593Smuzhiyun  * close function. Always succeeds
443*4882a593Smuzhiyun  */
wdrtas_temp_close(struct inode * inode,struct file * file)444*4882a593Smuzhiyun static int wdrtas_temp_close(struct inode *inode, struct file *file)
445*4882a593Smuzhiyun {
446*4882a593Smuzhiyun 	return 0;
447*4882a593Smuzhiyun }
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun /**
450*4882a593Smuzhiyun  * wdrtas_reboot - reboot notifier function
451*4882a593Smuzhiyun  * @nb: notifier block structure
452*4882a593Smuzhiyun  * @code: reboot code
453*4882a593Smuzhiyun  * @ptr: unused
454*4882a593Smuzhiyun  *
455*4882a593Smuzhiyun  * returns NOTIFY_DONE
456*4882a593Smuzhiyun  *
457*4882a593Smuzhiyun  * wdrtas_reboot stops the watchdog in case of a reboot
458*4882a593Smuzhiyun  */
wdrtas_reboot(struct notifier_block * this,unsigned long code,void * ptr)459*4882a593Smuzhiyun static int wdrtas_reboot(struct notifier_block *this,
460*4882a593Smuzhiyun 					unsigned long code, void *ptr)
461*4882a593Smuzhiyun {
462*4882a593Smuzhiyun 	if (code == SYS_DOWN || code == SYS_HALT)
463*4882a593Smuzhiyun 		wdrtas_timer_stop();
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun 	return NOTIFY_DONE;
466*4882a593Smuzhiyun }
467*4882a593Smuzhiyun 
468*4882a593Smuzhiyun /*** initialization stuff */
469*4882a593Smuzhiyun 
470*4882a593Smuzhiyun static const struct file_operations wdrtas_fops = {
471*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
472*4882a593Smuzhiyun 	.llseek		= no_llseek,
473*4882a593Smuzhiyun 	.write		= wdrtas_write,
474*4882a593Smuzhiyun 	.unlocked_ioctl	= wdrtas_ioctl,
475*4882a593Smuzhiyun 	.compat_ioctl	= compat_ptr_ioctl,
476*4882a593Smuzhiyun 	.open		= wdrtas_open,
477*4882a593Smuzhiyun 	.release	= wdrtas_close,
478*4882a593Smuzhiyun };
479*4882a593Smuzhiyun 
480*4882a593Smuzhiyun static struct miscdevice wdrtas_miscdev = {
481*4882a593Smuzhiyun 	.minor =	WATCHDOG_MINOR,
482*4882a593Smuzhiyun 	.name =		"watchdog",
483*4882a593Smuzhiyun 	.fops =		&wdrtas_fops,
484*4882a593Smuzhiyun };
485*4882a593Smuzhiyun 
486*4882a593Smuzhiyun static const struct file_operations wdrtas_temp_fops = {
487*4882a593Smuzhiyun 	.owner		= THIS_MODULE,
488*4882a593Smuzhiyun 	.llseek		= no_llseek,
489*4882a593Smuzhiyun 	.read		= wdrtas_temp_read,
490*4882a593Smuzhiyun 	.open		= wdrtas_temp_open,
491*4882a593Smuzhiyun 	.release	= wdrtas_temp_close,
492*4882a593Smuzhiyun };
493*4882a593Smuzhiyun 
494*4882a593Smuzhiyun static struct miscdevice wdrtas_tempdev = {
495*4882a593Smuzhiyun 	.minor =	TEMP_MINOR,
496*4882a593Smuzhiyun 	.name =		"temperature",
497*4882a593Smuzhiyun 	.fops =		&wdrtas_temp_fops,
498*4882a593Smuzhiyun };
499*4882a593Smuzhiyun 
500*4882a593Smuzhiyun static struct notifier_block wdrtas_notifier = {
501*4882a593Smuzhiyun 	.notifier_call =	wdrtas_reboot,
502*4882a593Smuzhiyun };
503*4882a593Smuzhiyun 
504*4882a593Smuzhiyun /**
505*4882a593Smuzhiyun  * wdrtas_get_tokens - reads in RTAS tokens
506*4882a593Smuzhiyun  *
507*4882a593Smuzhiyun  * returns 0 on success, <0 on failure
508*4882a593Smuzhiyun  *
509*4882a593Smuzhiyun  * wdrtas_get_tokens reads in the tokens for the RTAS calls used in
510*4882a593Smuzhiyun  * this watchdog driver. It tolerates, if "get-sensor-state" and
511*4882a593Smuzhiyun  * "ibm,get-system-parameter" are not available.
512*4882a593Smuzhiyun  */
wdrtas_get_tokens(void)513*4882a593Smuzhiyun static int wdrtas_get_tokens(void)
514*4882a593Smuzhiyun {
515*4882a593Smuzhiyun 	wdrtas_token_get_sensor_state = rtas_token("get-sensor-state");
516*4882a593Smuzhiyun 	if (wdrtas_token_get_sensor_state == RTAS_UNKNOWN_SERVICE) {
517*4882a593Smuzhiyun 		pr_warn("couldn't get token for get-sensor-state. Trying to continue without temperature support.\n");
518*4882a593Smuzhiyun 	}
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun 	wdrtas_token_get_sp = rtas_token("ibm,get-system-parameter");
521*4882a593Smuzhiyun 	if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE) {
522*4882a593Smuzhiyun 		pr_warn("couldn't get token for ibm,get-system-parameter. Trying to continue with a default timeout value of %i seconds.\n",
523*4882a593Smuzhiyun 			WDRTAS_DEFAULT_INTERVAL);
524*4882a593Smuzhiyun 	}
525*4882a593Smuzhiyun 
526*4882a593Smuzhiyun 	wdrtas_token_set_indicator = rtas_token("set-indicator");
527*4882a593Smuzhiyun 	if (wdrtas_token_set_indicator == RTAS_UNKNOWN_SERVICE) {
528*4882a593Smuzhiyun 		pr_err("couldn't get token for set-indicator. Terminating watchdog code.\n");
529*4882a593Smuzhiyun 		return -EIO;
530*4882a593Smuzhiyun 	}
531*4882a593Smuzhiyun 
532*4882a593Smuzhiyun 	wdrtas_token_event_scan = rtas_token("event-scan");
533*4882a593Smuzhiyun 	if (wdrtas_token_event_scan == RTAS_UNKNOWN_SERVICE) {
534*4882a593Smuzhiyun 		pr_err("couldn't get token for event-scan. Terminating watchdog code.\n");
535*4882a593Smuzhiyun 		return -EIO;
536*4882a593Smuzhiyun 	}
537*4882a593Smuzhiyun 
538*4882a593Smuzhiyun 	return 0;
539*4882a593Smuzhiyun }
540*4882a593Smuzhiyun 
541*4882a593Smuzhiyun /**
542*4882a593Smuzhiyun  * wdrtas_unregister_devs - unregisters the misc dev handlers
543*4882a593Smuzhiyun  *
544*4882a593Smuzhiyun  * wdrtas_register_devs unregisters the watchdog and temperature watchdog
545*4882a593Smuzhiyun  * misc devs
546*4882a593Smuzhiyun  */
wdrtas_unregister_devs(void)547*4882a593Smuzhiyun static void wdrtas_unregister_devs(void)
548*4882a593Smuzhiyun {
549*4882a593Smuzhiyun 	misc_deregister(&wdrtas_miscdev);
550*4882a593Smuzhiyun 	if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE)
551*4882a593Smuzhiyun 		misc_deregister(&wdrtas_tempdev);
552*4882a593Smuzhiyun }
553*4882a593Smuzhiyun 
554*4882a593Smuzhiyun /**
555*4882a593Smuzhiyun  * wdrtas_register_devs - registers the misc dev handlers
556*4882a593Smuzhiyun  *
557*4882a593Smuzhiyun  * returns 0 on success, <0 on failure
558*4882a593Smuzhiyun  *
559*4882a593Smuzhiyun  * wdrtas_register_devs registers the watchdog and temperature watchdog
560*4882a593Smuzhiyun  * misc devs
561*4882a593Smuzhiyun  */
wdrtas_register_devs(void)562*4882a593Smuzhiyun static int wdrtas_register_devs(void)
563*4882a593Smuzhiyun {
564*4882a593Smuzhiyun 	int result;
565*4882a593Smuzhiyun 
566*4882a593Smuzhiyun 	result = misc_register(&wdrtas_miscdev);
567*4882a593Smuzhiyun 	if (result) {
568*4882a593Smuzhiyun 		pr_err("couldn't register watchdog misc device. Terminating watchdog code.\n");
569*4882a593Smuzhiyun 		return result;
570*4882a593Smuzhiyun 	}
571*4882a593Smuzhiyun 
572*4882a593Smuzhiyun 	if (wdrtas_token_get_sensor_state != RTAS_UNKNOWN_SERVICE) {
573*4882a593Smuzhiyun 		result = misc_register(&wdrtas_tempdev);
574*4882a593Smuzhiyun 		if (result) {
575*4882a593Smuzhiyun 			pr_warn("couldn't register watchdog temperature misc device. Continuing without temperature support.\n");
576*4882a593Smuzhiyun 			wdrtas_token_get_sensor_state = RTAS_UNKNOWN_SERVICE;
577*4882a593Smuzhiyun 		}
578*4882a593Smuzhiyun 	}
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun 	return 0;
581*4882a593Smuzhiyun }
582*4882a593Smuzhiyun 
583*4882a593Smuzhiyun /**
584*4882a593Smuzhiyun  * wdrtas_init - init function of the watchdog driver
585*4882a593Smuzhiyun  *
586*4882a593Smuzhiyun  * returns 0 on success, <0 on failure
587*4882a593Smuzhiyun  *
588*4882a593Smuzhiyun  * registers the file handlers and the reboot notifier
589*4882a593Smuzhiyun  */
wdrtas_init(void)590*4882a593Smuzhiyun static int __init wdrtas_init(void)
591*4882a593Smuzhiyun {
592*4882a593Smuzhiyun 	if (wdrtas_get_tokens())
593*4882a593Smuzhiyun 		return -ENODEV;
594*4882a593Smuzhiyun 
595*4882a593Smuzhiyun 	if (wdrtas_register_devs())
596*4882a593Smuzhiyun 		return -ENODEV;
597*4882a593Smuzhiyun 
598*4882a593Smuzhiyun 	if (register_reboot_notifier(&wdrtas_notifier)) {
599*4882a593Smuzhiyun 		pr_err("could not register reboot notifier. Terminating watchdog code.\n");
600*4882a593Smuzhiyun 		wdrtas_unregister_devs();
601*4882a593Smuzhiyun 		return -ENODEV;
602*4882a593Smuzhiyun 	}
603*4882a593Smuzhiyun 
604*4882a593Smuzhiyun 	if (wdrtas_token_get_sp == RTAS_UNKNOWN_SERVICE)
605*4882a593Smuzhiyun 		wdrtas_interval = WDRTAS_DEFAULT_INTERVAL;
606*4882a593Smuzhiyun 	else
607*4882a593Smuzhiyun 		wdrtas_interval = wdrtas_get_interval(WDRTAS_DEFAULT_INTERVAL);
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun 	return 0;
610*4882a593Smuzhiyun }
611*4882a593Smuzhiyun 
612*4882a593Smuzhiyun /**
613*4882a593Smuzhiyun  * wdrtas_exit - exit function of the watchdog driver
614*4882a593Smuzhiyun  *
615*4882a593Smuzhiyun  * unregisters the file handlers and the reboot notifier
616*4882a593Smuzhiyun  */
wdrtas_exit(void)617*4882a593Smuzhiyun static void __exit wdrtas_exit(void)
618*4882a593Smuzhiyun {
619*4882a593Smuzhiyun 	if (!wdrtas_nowayout)
620*4882a593Smuzhiyun 		wdrtas_timer_stop();
621*4882a593Smuzhiyun 
622*4882a593Smuzhiyun 	wdrtas_unregister_devs();
623*4882a593Smuzhiyun 
624*4882a593Smuzhiyun 	unregister_reboot_notifier(&wdrtas_notifier);
625*4882a593Smuzhiyun }
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun module_init(wdrtas_init);
628*4882a593Smuzhiyun module_exit(wdrtas_exit);
629