Linux Audio

Check our new training course

Yocto / OpenEmbedded training

Mar 24-27, 2025, special US time zones
Register
Loading...
Note: File does not exist in v6.2.
  1/*
  2 *      Intel_SCU 0.2:  An Intel SCU IOH Based Watchdog Device
  3 *			for Intel part #(s):
  4 *				- AF82MP20 PCH
  5 *
  6 *      Copyright (C) 2009-2010 Intel Corporation. All rights reserved.
  7 *
  8 *      This program is free software; you can redistribute it and/or
  9 *      modify it under the terms of version 2 of the GNU General
 10 *      Public License as published by the Free Software Foundation.
 11 *
 12 *      This program is distributed in the hope that it will be
 13 *      useful, but WITHOUT ANY WARRANTY; without even the implied
 14 *      warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 15 *      PURPOSE.  See the GNU General Public License for more details.
 16 *      You should have received a copy of the GNU General Public
 17 *      License along with this program; if not, write to the Free
 18 *      Software Foundation, Inc., 59 Temple Place - Suite 330,
 19 *      Boston, MA  02111-1307, USA.
 20 *      The full GNU General Public License is included in this
 21 *      distribution in the file called COPYING.
 22 *
 23 */
 24
 25#include <linux/compiler.h>
 26#include <linux/module.h>
 27#include <linux/kernel.h>
 28#include <linux/moduleparam.h>
 29#include <linux/types.h>
 30#include <linux/miscdevice.h>
 31#include <linux/watchdog.h>
 32#include <linux/fs.h>
 33#include <linux/notifier.h>
 34#include <linux/reboot.h>
 35#include <linux/init.h>
 36#include <linux/jiffies.h>
 37#include <linux/uaccess.h>
 38#include <linux/slab.h>
 39#include <linux/io.h>
 40#include <linux/interrupt.h>
 41#include <linux/delay.h>
 42#include <linux/sched.h>
 43#include <linux/signal.h>
 44#include <linux/sfi.h>
 45#include <asm/irq.h>
 46#include <linux/atomic.h>
 47#include <asm/intel_scu_ipc.h>
 48#include <asm/apb_timer.h>
 49#include <asm/mrst.h>
 50
 51#include "intel_scu_watchdog.h"
 52
 53/* Bounds number of times we will retry loading time count */
 54/* This retry is a work around for a silicon bug.	   */
 55#define MAX_RETRY 16
 56
 57#define IPC_SET_WATCHDOG_TIMER	0xF8
 58
 59static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN;
 60module_param(timer_margin, int, 0);
 61MODULE_PARM_DESC(timer_margin,
 62		"Watchdog timer margin"
 63		"Time between interrupt and resetting the system"
 64		"The range is from 1 to 160"
 65		"This is the time for all keep alives to arrive");
 66
 67static int timer_set = DEFAULT_TIME;
 68module_param(timer_set, int, 0);
 69MODULE_PARM_DESC(timer_set,
 70		"Default Watchdog timer setting"
 71		"Complete cycle time"
 72		"The range is from 1 to 170"
 73		"This is the time for all keep alives to arrive");
 74
 75/* After watchdog device is closed, check force_boot. If:
 76 * force_boot == 0, then force boot on next watchdog interrupt after close,
 77 * force_boot == 1, then force boot immediately when device is closed.
 78 */
 79static int force_boot;
 80module_param(force_boot, int, 0);
 81MODULE_PARM_DESC(force_boot,
 82		"A value of 1 means that the driver will reboot"
 83		"the system immediately if the /dev/watchdog device is closed"
 84		"A value of 0 means that when /dev/watchdog device is closed"
 85		"the watchdog timer will be refreshed for one more interval"
 86		"of length: timer_set. At the end of this interval, the"
 87		"watchdog timer will reset the system."
 88		);
 89
 90/* there is only one device in the system now; this can be made into
 91 * an array in the future if we have more than one device */
 92
 93static struct intel_scu_watchdog_dev watchdog_device;
 94
 95/* Forces restart, if force_reboot is set */
 96static void watchdog_fire(void)
 97{
 98	if (force_boot) {
 99		printk(KERN_CRIT PFX "Initiating system reboot.\n");
100		emergency_restart();
101		printk(KERN_CRIT PFX "Reboot didn't ?????\n");
102	}
103
104	else {
105		printk(KERN_CRIT PFX "Immediate Reboot Disabled\n");
106		printk(KERN_CRIT PFX
107			"System will reset when watchdog timer times out!\n");
108	}
109}
110
111static int check_timer_margin(int new_margin)
112{
113	if ((new_margin < MIN_TIME_CYCLE) ||
114	    (new_margin > MAX_TIME - timer_set)) {
115		pr_debug("Watchdog timer: value of new_margin %d is out of the range %d to %d\n",
116			  new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
117		return -EINVAL;
118	}
119	return 0;
120}
121
122/*
123 * IPC operations
124 */
125static int watchdog_set_ipc(int soft_threshold, int threshold)
126{
127	u32	*ipc_wbuf;
128	u8	 cbuf[16] = { '\0' };
129	int	 ipc_ret = 0;
130
131	ipc_wbuf = (u32 *)&cbuf;
132	ipc_wbuf[0] = soft_threshold;
133	ipc_wbuf[1] = threshold;
134
135	ipc_ret = intel_scu_ipc_command(
136			IPC_SET_WATCHDOG_TIMER,
137			0,
138			ipc_wbuf,
139			2,
140			NULL,
141			0);
142
143	if (ipc_ret != 0)
144		pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
145
146	return ipc_ret;
147};
148
149/*
150 *      Intel_SCU operations
151 */
152
153/* timer interrupt handler */
154static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id)
155{
156	int int_status;
157	int_status = ioread32(watchdog_device.timer_interrupt_status_addr);
158
159	pr_debug("Watchdog timer: irq, int_status: %x\n", int_status);
160
161	if (int_status != 0)
162		return IRQ_NONE;
163
164	/* has the timer been started? If not, then this is spurious */
165	if (watchdog_device.timer_started == 0) {
166		pr_debug("Watchdog timer: spurious interrupt received\n");
167		return IRQ_HANDLED;
168	}
169
170	/* temporarily disable the timer */
171	iowrite32(0x00000002, watchdog_device.timer_control_addr);
172
173	/* set the timer to the threshold */
174	iowrite32(watchdog_device.threshold,
175		  watchdog_device.timer_load_count_addr);
176
177	/* allow the timer to run */
178	iowrite32(0x00000003, watchdog_device.timer_control_addr);
179
180	return IRQ_HANDLED;
181}
182
183static int intel_scu_keepalive(void)
184{
185
186	/* read eoi register - clears interrupt */
187	ioread32(watchdog_device.timer_clear_interrupt_addr);
188
189	/* temporarily disable the timer */
190	iowrite32(0x00000002, watchdog_device.timer_control_addr);
191
192	/* set the timer to the soft_threshold */
193	iowrite32(watchdog_device.soft_threshold,
194		  watchdog_device.timer_load_count_addr);
195
196	/* allow the timer to run */
197	iowrite32(0x00000003, watchdog_device.timer_control_addr);
198
199	return 0;
200}
201
202static int intel_scu_stop(void)
203{
204	iowrite32(0, watchdog_device.timer_control_addr);
205	return 0;
206}
207
208static int intel_scu_set_heartbeat(u32 t)
209{
210	int			 ipc_ret;
211	int			 retry_count;
212	u32			 soft_value;
213	u32			 hw_pre_value;
214	u32			 hw_value;
215
216	watchdog_device.timer_set = t;
217	watchdog_device.threshold =
218		timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
219	watchdog_device.soft_threshold =
220		(watchdog_device.timer_set - timer_margin)
221		* watchdog_device.timer_tbl_ptr->freq_hz;
222
223	pr_debug("Watchdog timer: set_heartbeat: timer freq is %d\n",
224		watchdog_device.timer_tbl_ptr->freq_hz);
225	pr_debug("Watchdog timer: set_heartbeat: timer_set is %x (hex)\n",
226		watchdog_device.timer_set);
227	pr_debug("Watchdog timer: set_hearbeat: timer_margin is %x (hex)\n",
228		timer_margin);
229	pr_debug("Watchdog timer: set_heartbeat: threshold is %x (hex)\n",
230		watchdog_device.threshold);
231	pr_debug("Watchdog timer: set_heartbeat: soft_threshold is %x (hex)\n",
232		watchdog_device.soft_threshold);
233
234	/* Adjust thresholds by FREQ_ADJUSTMENT factor, to make the */
235	/* watchdog timing come out right. */
236	watchdog_device.threshold =
237		watchdog_device.threshold / FREQ_ADJUSTMENT;
238	watchdog_device.soft_threshold =
239		watchdog_device.soft_threshold / FREQ_ADJUSTMENT;
240
241	/* temporarily disable the timer */
242	iowrite32(0x00000002, watchdog_device.timer_control_addr);
243
244	/* send the threshold and soft_threshold via IPC to the processor */
245	ipc_ret = watchdog_set_ipc(watchdog_device.soft_threshold,
246				   watchdog_device.threshold);
247
248	if (ipc_ret != 0) {
249		/* Make sure the watchdog timer is stopped */
250		intel_scu_stop();
251		return ipc_ret;
252	}
253
254	/* Soft Threshold set loop. Early versions of silicon did */
255	/* not always set this count correctly.  This loop checks */
256	/* the value and retries if it was not set correctly.     */
257
258	retry_count = 0;
259	soft_value = watchdog_device.soft_threshold & 0xFFFF0000;
260	do {
261
262		/* Make sure timer is stopped */
263		intel_scu_stop();
264
265		if (MAX_RETRY < retry_count++) {
266			/* Unable to set timer value */
267			pr_err("Watchdog timer: Unable to set timer\n");
268			return -ENODEV;
269		}
270
271		/* set the timer to the soft threshold */
272		iowrite32(watchdog_device.soft_threshold,
273			watchdog_device.timer_load_count_addr);
274
275		/* read count value before starting timer */
276		hw_pre_value = ioread32(watchdog_device.timer_load_count_addr);
277		hw_pre_value = hw_pre_value & 0xFFFF0000;
278
279		/* Start the timer */
280		iowrite32(0x00000003, watchdog_device.timer_control_addr);
281
282		/* read the value the time loaded into its count reg */
283		hw_value = ioread32(watchdog_device.timer_load_count_addr);
284		hw_value = hw_value & 0xFFFF0000;
285
286
287	} while (soft_value != hw_value);
288
289	watchdog_device.timer_started = 1;
290
291	return 0;
292}
293
294/*
295 * /dev/watchdog handling
296 */
297
298static int intel_scu_open(struct inode *inode, struct file *file)
299{
300
301	/* Set flag to indicate that watchdog device is open */
302	if (test_and_set_bit(0, &watchdog_device.driver_open))
303		return -EBUSY;
304
305	/* Check for reopen of driver. Reopens are not allowed */
306	if (watchdog_device.driver_closed)
307		return -EPERM;
308
309	return nonseekable_open(inode, file);
310}
311
312static int intel_scu_release(struct inode *inode, struct file *file)
313{
314	/*
315	 * This watchdog should not be closed, after the timer
316	 * is started with the WDIPC_SETTIMEOUT ioctl
317	 * If force_boot is set watchdog_fire() will cause an
318	 * immediate reset. If force_boot is not set, the watchdog
319	 * timer is refreshed for one more interval. At the end
320	 * of that interval, the watchdog timer will reset the system.
321	 */
322
323	if (!test_and_clear_bit(0, &watchdog_device.driver_open)) {
324		pr_debug("Watchdog timer: intel_scu_release, without open\n");
325		return -ENOTTY;
326	}
327
328	if (!watchdog_device.timer_started) {
329		/* Just close, since timer has not been started */
330		pr_debug("Watchdog timer: closed, without starting timer\n");
331		return 0;
332	}
333
334	printk(KERN_CRIT PFX
335	       "Unexpected close of /dev/watchdog!\n");
336
337	/* Since the timer was started, prevent future reopens */
338	watchdog_device.driver_closed = 1;
339
340	/* Refresh the timer for one more interval */
341	intel_scu_keepalive();
342
343	/* Reboot system (if force_boot is set) */
344	watchdog_fire();
345
346	/* We should only reach this point if force_boot is not set */
347	return 0;
348}
349
350static ssize_t intel_scu_write(struct file *file,
351			      char const *data,
352			      size_t len,
353			      loff_t *ppos)
354{
355
356	if (watchdog_device.timer_started)
357		/* Watchdog already started, keep it alive */
358		intel_scu_keepalive();
359	else
360		/* Start watchdog with timer value set by init */
361		intel_scu_set_heartbeat(watchdog_device.timer_set);
362
363	return len;
364}
365
366static long intel_scu_ioctl(struct file *file,
367			   unsigned int cmd,
368			   unsigned long arg)
369{
370	void __user *argp = (void __user *)arg;
371	u32 __user *p = argp;
372	u32 new_margin;
373
374
375	static const struct watchdog_info ident = {
376		.options =          WDIOF_SETTIMEOUT
377				    | WDIOF_KEEPALIVEPING,
378		.firmware_version = 0,  /* @todo Get from SCU via
379						 ipc_get_scu_fw_version()? */
380		.identity =         "Intel_SCU IOH Watchdog"  /* len < 32 */
381	};
382
383	switch (cmd) {
384	case WDIOC_GETSUPPORT:
385		return copy_to_user(argp,
386				    &ident,
387				    sizeof(ident)) ? -EFAULT : 0;
388	case WDIOC_GETSTATUS:
389	case WDIOC_GETBOOTSTATUS:
390		return put_user(0, p);
391	case WDIOC_KEEPALIVE:
392		intel_scu_keepalive();
393
394		return 0;
395	case WDIOC_SETTIMEOUT:
396		if (get_user(new_margin, p))
397			return -EFAULT;
398
399		if (check_timer_margin(new_margin))
400			return -EINVAL;
401
402		if (intel_scu_set_heartbeat(new_margin))
403			return -EINVAL;
404		return 0;
405	case WDIOC_GETTIMEOUT:
406		return put_user(watchdog_device.soft_threshold, p);
407
408	default:
409		return -ENOTTY;
410	}
411}
412
413/*
414 *      Notifier for system down
415 */
416static int intel_scu_notify_sys(struct notifier_block *this,
417			       unsigned long code,
418			       void *another_unused)
419{
420	if (code == SYS_DOWN || code == SYS_HALT)
421		/* Turn off the watchdog timer. */
422		intel_scu_stop();
423	return NOTIFY_DONE;
424}
425
426/*
427 *      Kernel Interfaces
428 */
429static const struct file_operations intel_scu_fops = {
430	.owner          = THIS_MODULE,
431	.llseek         = no_llseek,
432	.write          = intel_scu_write,
433	.unlocked_ioctl = intel_scu_ioctl,
434	.open           = intel_scu_open,
435	.release        = intel_scu_release,
436};
437
438static int __init intel_scu_watchdog_init(void)
439{
440	int ret;
441	u32 __iomem *tmp_addr;
442
443	/*
444	 * We don't really need to check this as the SFI timer get will fail
445	 * but if we do so we can exit with a clearer reason and no noise.
446	 *
447	 * If it isn't an intel MID device then it doesn't have this watchdog
448	 */
449	if (!mrst_identify_cpu())
450		return -ENODEV;
451
452	/* Check boot parameters to verify that their initial values */
453	/* are in range. */
454	/* Check value of timer_set boot parameter */
455	if ((timer_set < MIN_TIME_CYCLE) ||
456	    (timer_set > MAX_TIME - MIN_TIME_CYCLE)) {
457		pr_err("Watchdog timer: value of timer_set %x (hex) "
458		  "is out of range from %x to %x (hex)\n",
459		  timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
460		return -EINVAL;
461	}
462
463	/* Check value of timer_margin boot parameter */
464	if (check_timer_margin(timer_margin))
465		return -EINVAL;
466
467	watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
468
469	if (watchdog_device.timer_tbl_ptr == NULL) {
470		pr_debug("Watchdog timer - Intel SCU watchdog: timer is not available\n");
471		return -ENODEV;
472	}
473	/* make sure the timer exists */
474	if (watchdog_device.timer_tbl_ptr->phys_addr == 0) {
475		pr_debug("Watchdog timer - Intel SCU watchdog - timer %d does not have valid physical memory\n",
476								sfi_mtimer_num);
477		return -ENODEV;
478	}
479
480	if (watchdog_device.timer_tbl_ptr->irq == 0) {
481		pr_debug("Watchdog timer: timer %d invalid irq\n",
482							sfi_mtimer_num);
483		return -ENODEV;
484	}
485
486	tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr,
487			20);
488
489	if (tmp_addr == NULL) {
490		pr_debug("Watchdog timer: timer unable to ioremap\n");
491		return -ENOMEM;
492	}
493
494	watchdog_device.timer_load_count_addr = tmp_addr++;
495	watchdog_device.timer_current_value_addr = tmp_addr++;
496	watchdog_device.timer_control_addr = tmp_addr++;
497	watchdog_device.timer_clear_interrupt_addr = tmp_addr++;
498	watchdog_device.timer_interrupt_status_addr = tmp_addr++;
499
500	/* Set the default time values in device structure */
501
502	watchdog_device.timer_set = timer_set;
503	watchdog_device.threshold =
504		timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
505	watchdog_device.soft_threshold =
506		(watchdog_device.timer_set - timer_margin)
507		* watchdog_device.timer_tbl_ptr->freq_hz;
508
509
510	watchdog_device.intel_scu_notifier.notifier_call =
511		intel_scu_notify_sys;
512
513	ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
514	if (ret) {
515		pr_err("Watchdog timer: cannot register notifier %d)\n", ret);
516		goto register_reboot_error;
517	}
518
519	watchdog_device.miscdev.minor = WATCHDOG_MINOR;
520	watchdog_device.miscdev.name = "watchdog";
521	watchdog_device.miscdev.fops = &intel_scu_fops;
522
523	ret = misc_register(&watchdog_device.miscdev);
524	if (ret) {
525		pr_err("Watchdog timer: cannot register miscdev %d err =%d\n",
526							WATCHDOG_MINOR, ret);
527		goto misc_register_error;
528	}
529
530	ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq,
531		watchdog_timer_interrupt,
532		IRQF_SHARED, "watchdog",
533		&watchdog_device.timer_load_count_addr);
534	if (ret) {
535		pr_err("Watchdog timer: error requesting irq %d\n", ret);
536		goto request_irq_error;
537	}
538	/* Make sure timer is disabled before returning */
539	intel_scu_stop();
540	return 0;
541
542/* error cleanup */
543
544request_irq_error:
545	misc_deregister(&watchdog_device.miscdev);
546misc_register_error:
547	unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
548register_reboot_error:
549	intel_scu_stop();
550	iounmap(watchdog_device.timer_load_count_addr);
551	return ret;
552}
553
554static void __exit intel_scu_watchdog_exit(void)
555{
556
557	misc_deregister(&watchdog_device.miscdev);
558	unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
559	/* disable the timer */
560	iowrite32(0x00000002, watchdog_device.timer_control_addr);
561	iounmap(watchdog_device.timer_load_count_addr);
562}
563
564late_initcall(intel_scu_watchdog_init);
565module_exit(intel_scu_watchdog_exit);
566
567MODULE_AUTHOR("Intel Corporation");
568MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
569MODULE_LICENSE("GPL");
570MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
571MODULE_VERSION(WDT_VER);