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