Linux Audio

Check our new training course

Loading...
v3.1
  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);
v3.5.6
  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#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
 26
 27#include <linux/compiler.h>
 28#include <linux/module.h>
 29#include <linux/kernel.h>
 30#include <linux/moduleparam.h>
 31#include <linux/types.h>
 32#include <linux/miscdevice.h>
 33#include <linux/watchdog.h>
 34#include <linux/fs.h>
 35#include <linux/notifier.h>
 36#include <linux/reboot.h>
 37#include <linux/init.h>
 38#include <linux/jiffies.h>
 39#include <linux/uaccess.h>
 40#include <linux/slab.h>
 41#include <linux/io.h>
 42#include <linux/interrupt.h>
 43#include <linux/delay.h>
 44#include <linux/sched.h>
 45#include <linux/signal.h>
 46#include <linux/sfi.h>
 47#include <asm/irq.h>
 48#include <linux/atomic.h>
 49#include <asm/intel_scu_ipc.h>
 50#include <asm/apb_timer.h>
 51#include <asm/mrst.h>
 52
 53#include "intel_scu_watchdog.h"
 54
 55/* Bounds number of times we will retry loading time count */
 56/* This retry is a work around for a silicon bug.	   */
 57#define MAX_RETRY 16
 58
 59#define IPC_SET_WATCHDOG_TIMER	0xF8
 60
 61static int timer_margin = DEFAULT_SOFT_TO_HARD_MARGIN;
 62module_param(timer_margin, int, 0);
 63MODULE_PARM_DESC(timer_margin,
 64		"Watchdog timer margin"
 65		"Time between interrupt and resetting the system"
 66		"The range is from 1 to 160"
 67		"This is the time for all keep alives to arrive");
 68
 69static int timer_set = DEFAULT_TIME;
 70module_param(timer_set, int, 0);
 71MODULE_PARM_DESC(timer_set,
 72		"Default Watchdog timer setting"
 73		"Complete cycle time"
 74		"The range is from 1 to 170"
 75		"This is the time for all keep alives to arrive");
 76
 77/* After watchdog device is closed, check force_boot. If:
 78 * force_boot == 0, then force boot on next watchdog interrupt after close,
 79 * force_boot == 1, then force boot immediately when device is closed.
 80 */
 81static int force_boot;
 82module_param(force_boot, int, 0);
 83MODULE_PARM_DESC(force_boot,
 84		"A value of 1 means that the driver will reboot"
 85		"the system immediately if the /dev/watchdog device is closed"
 86		"A value of 0 means that when /dev/watchdog device is closed"
 87		"the watchdog timer will be refreshed for one more interval"
 88		"of length: timer_set. At the end of this interval, the"
 89		"watchdog timer will reset the system."
 90		);
 91
 92/* there is only one device in the system now; this can be made into
 93 * an array in the future if we have more than one device */
 94
 95static struct intel_scu_watchdog_dev watchdog_device;
 96
 97/* Forces restart, if force_reboot is set */
 98static void watchdog_fire(void)
 99{
100	if (force_boot) {
101		pr_crit("Initiating system reboot\n");
102		emergency_restart();
103		pr_crit("Reboot didn't ?????\n");
104	}
105
106	else {
107		pr_crit("Immediate Reboot Disabled\n");
108		pr_crit("System will reset when watchdog timer times out!\n");
 
109	}
110}
111
112static int check_timer_margin(int new_margin)
113{
114	if ((new_margin < MIN_TIME_CYCLE) ||
115	    (new_margin > MAX_TIME - timer_set)) {
116		pr_debug("value of new_margin %d is out of the range %d to %d\n",
117			 new_margin, MIN_TIME_CYCLE, MAX_TIME - timer_set);
118		return -EINVAL;
119	}
120	return 0;
121}
122
123/*
124 * IPC operations
125 */
126static int watchdog_set_ipc(int soft_threshold, int threshold)
127{
128	u32	*ipc_wbuf;
129	u8	 cbuf[16] = { '\0' };
130	int	 ipc_ret = 0;
131
132	ipc_wbuf = (u32 *)&cbuf;
133	ipc_wbuf[0] = soft_threshold;
134	ipc_wbuf[1] = threshold;
135
136	ipc_ret = intel_scu_ipc_command(
137			IPC_SET_WATCHDOG_TIMER,
138			0,
139			ipc_wbuf,
140			2,
141			NULL,
142			0);
143
144	if (ipc_ret != 0)
145		pr_err("Error setting SCU watchdog timer: %x\n", ipc_ret);
146
147	return ipc_ret;
148};
149
150/*
151 *      Intel_SCU operations
152 */
153
154/* timer interrupt handler */
155static irqreturn_t watchdog_timer_interrupt(int irq, void *dev_id)
156{
157	int int_status;
158	int_status = ioread32(watchdog_device.timer_interrupt_status_addr);
159
160	pr_debug("irq, int_status: %x\n", int_status);
161
162	if (int_status != 0)
163		return IRQ_NONE;
164
165	/* has the timer been started? If not, then this is spurious */
166	if (watchdog_device.timer_started == 0) {
167		pr_debug("spurious interrupt received\n");
168		return IRQ_HANDLED;
169	}
170
171	/* temporarily disable the timer */
172	iowrite32(0x00000002, watchdog_device.timer_control_addr);
173
174	/* set the timer to the threshold */
175	iowrite32(watchdog_device.threshold,
176		  watchdog_device.timer_load_count_addr);
177
178	/* allow the timer to run */
179	iowrite32(0x00000003, watchdog_device.timer_control_addr);
180
181	return IRQ_HANDLED;
182}
183
184static int intel_scu_keepalive(void)
185{
186
187	/* read eoi register - clears interrupt */
188	ioread32(watchdog_device.timer_clear_interrupt_addr);
189
190	/* temporarily disable the timer */
191	iowrite32(0x00000002, watchdog_device.timer_control_addr);
192
193	/* set the timer to the soft_threshold */
194	iowrite32(watchdog_device.soft_threshold,
195		  watchdog_device.timer_load_count_addr);
196
197	/* allow the timer to run */
198	iowrite32(0x00000003, watchdog_device.timer_control_addr);
199
200	return 0;
201}
202
203static int intel_scu_stop(void)
204{
205	iowrite32(0, watchdog_device.timer_control_addr);
206	return 0;
207}
208
209static int intel_scu_set_heartbeat(u32 t)
210{
211	int			 ipc_ret;
212	int			 retry_count;
213	u32			 soft_value;
214	u32			 hw_pre_value;
215	u32			 hw_value;
216
217	watchdog_device.timer_set = t;
218	watchdog_device.threshold =
219		timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
220	watchdog_device.soft_threshold =
221		(watchdog_device.timer_set - timer_margin)
222		* watchdog_device.timer_tbl_ptr->freq_hz;
223
224	pr_debug("set_heartbeat: timer freq is %d\n",
225		 watchdog_device.timer_tbl_ptr->freq_hz);
226	pr_debug("set_heartbeat: timer_set is %x (hex)\n",
227		 watchdog_device.timer_set);
228	pr_debug("set_hearbeat: timer_margin is %x (hex)\n", timer_margin);
229	pr_debug("set_heartbeat: threshold is %x (hex)\n",
230		 watchdog_device.threshold);
231	pr_debug("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("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("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("closed, without starting timer\n");
331		return 0;
332	}
333
334	pr_crit("Unexpected close of /dev/watchdog!\n");
 
335
336	/* Since the timer was started, prevent future reopens */
337	watchdog_device.driver_closed = 1;
338
339	/* Refresh the timer for one more interval */
340	intel_scu_keepalive();
341
342	/* Reboot system (if force_boot is set) */
343	watchdog_fire();
344
345	/* We should only reach this point if force_boot is not set */
346	return 0;
347}
348
349static ssize_t intel_scu_write(struct file *file,
350			      char const *data,
351			      size_t len,
352			      loff_t *ppos)
353{
354
355	if (watchdog_device.timer_started)
356		/* Watchdog already started, keep it alive */
357		intel_scu_keepalive();
358	else
359		/* Start watchdog with timer value set by init */
360		intel_scu_set_heartbeat(watchdog_device.timer_set);
361
362	return len;
363}
364
365static long intel_scu_ioctl(struct file *file,
366			   unsigned int cmd,
367			   unsigned long arg)
368{
369	void __user *argp = (void __user *)arg;
370	u32 __user *p = argp;
371	u32 new_margin;
372
373
374	static const struct watchdog_info ident = {
375		.options =          WDIOF_SETTIMEOUT
376				    | WDIOF_KEEPALIVEPING,
377		.firmware_version = 0,  /* @todo Get from SCU via
378						 ipc_get_scu_fw_version()? */
379		.identity =         "Intel_SCU IOH Watchdog"  /* len < 32 */
380	};
381
382	switch (cmd) {
383	case WDIOC_GETSUPPORT:
384		return copy_to_user(argp,
385				    &ident,
386				    sizeof(ident)) ? -EFAULT : 0;
387	case WDIOC_GETSTATUS:
388	case WDIOC_GETBOOTSTATUS:
389		return put_user(0, p);
390	case WDIOC_KEEPALIVE:
391		intel_scu_keepalive();
392
393		return 0;
394	case WDIOC_SETTIMEOUT:
395		if (get_user(new_margin, p))
396			return -EFAULT;
397
398		if (check_timer_margin(new_margin))
399			return -EINVAL;
400
401		if (intel_scu_set_heartbeat(new_margin))
402			return -EINVAL;
403		return 0;
404	case WDIOC_GETTIMEOUT:
405		return put_user(watchdog_device.soft_threshold, p);
406
407	default:
408		return -ENOTTY;
409	}
410}
411
412/*
413 *      Notifier for system down
414 */
415static int intel_scu_notify_sys(struct notifier_block *this,
416			       unsigned long code,
417			       void *another_unused)
418{
419	if (code == SYS_DOWN || code == SYS_HALT)
420		/* Turn off the watchdog timer. */
421		intel_scu_stop();
422	return NOTIFY_DONE;
423}
424
425/*
426 *      Kernel Interfaces
427 */
428static const struct file_operations intel_scu_fops = {
429	.owner          = THIS_MODULE,
430	.llseek         = no_llseek,
431	.write          = intel_scu_write,
432	.unlocked_ioctl = intel_scu_ioctl,
433	.open           = intel_scu_open,
434	.release        = intel_scu_release,
435};
436
437static int __init intel_scu_watchdog_init(void)
438{
439	int ret;
440	u32 __iomem *tmp_addr;
441
442	/*
443	 * We don't really need to check this as the SFI timer get will fail
444	 * but if we do so we can exit with a clearer reason and no noise.
445	 *
446	 * If it isn't an intel MID device then it doesn't have this watchdog
447	 */
448	if (!mrst_identify_cpu())
449		return -ENODEV;
450
451	/* Check boot parameters to verify that their initial values */
452	/* are in range. */
453	/* Check value of timer_set boot parameter */
454	if ((timer_set < MIN_TIME_CYCLE) ||
455	    (timer_set > MAX_TIME - MIN_TIME_CYCLE)) {
456		pr_err("value of timer_set %x (hex) is out of range from %x to %x (hex)\n",
457		       timer_set, MIN_TIME_CYCLE, MAX_TIME - MIN_TIME_CYCLE);
 
458		return -EINVAL;
459	}
460
461	/* Check value of timer_margin boot parameter */
462	if (check_timer_margin(timer_margin))
463		return -EINVAL;
464
465	watchdog_device.timer_tbl_ptr = sfi_get_mtmr(sfi_mtimer_num-1);
466
467	if (watchdog_device.timer_tbl_ptr == NULL) {
468		pr_debug("timer is not available\n");
469		return -ENODEV;
470	}
471	/* make sure the timer exists */
472	if (watchdog_device.timer_tbl_ptr->phys_addr == 0) {
473		pr_debug("timer %d does not have valid physical memory\n",
474			 sfi_mtimer_num);
475		return -ENODEV;
476	}
477
478	if (watchdog_device.timer_tbl_ptr->irq == 0) {
479		pr_debug("timer %d invalid irq\n", sfi_mtimer_num);
 
480		return -ENODEV;
481	}
482
483	tmp_addr = ioremap_nocache(watchdog_device.timer_tbl_ptr->phys_addr,
484			20);
485
486	if (tmp_addr == NULL) {
487		pr_debug("timer unable to ioremap\n");
488		return -ENOMEM;
489	}
490
491	watchdog_device.timer_load_count_addr = tmp_addr++;
492	watchdog_device.timer_current_value_addr = tmp_addr++;
493	watchdog_device.timer_control_addr = tmp_addr++;
494	watchdog_device.timer_clear_interrupt_addr = tmp_addr++;
495	watchdog_device.timer_interrupt_status_addr = tmp_addr++;
496
497	/* Set the default time values in device structure */
498
499	watchdog_device.timer_set = timer_set;
500	watchdog_device.threshold =
501		timer_margin * watchdog_device.timer_tbl_ptr->freq_hz;
502	watchdog_device.soft_threshold =
503		(watchdog_device.timer_set - timer_margin)
504		* watchdog_device.timer_tbl_ptr->freq_hz;
505
506
507	watchdog_device.intel_scu_notifier.notifier_call =
508		intel_scu_notify_sys;
509
510	ret = register_reboot_notifier(&watchdog_device.intel_scu_notifier);
511	if (ret) {
512		pr_err("cannot register notifier %d)\n", ret);
513		goto register_reboot_error;
514	}
515
516	watchdog_device.miscdev.minor = WATCHDOG_MINOR;
517	watchdog_device.miscdev.name = "watchdog";
518	watchdog_device.miscdev.fops = &intel_scu_fops;
519
520	ret = misc_register(&watchdog_device.miscdev);
521	if (ret) {
522		pr_err("cannot register miscdev %d err =%d\n",
523		       WATCHDOG_MINOR, ret);
524		goto misc_register_error;
525	}
526
527	ret = request_irq((unsigned int)watchdog_device.timer_tbl_ptr->irq,
528		watchdog_timer_interrupt,
529		IRQF_SHARED, "watchdog",
530		&watchdog_device.timer_load_count_addr);
531	if (ret) {
532		pr_err("error requesting irq %d\n", ret);
533		goto request_irq_error;
534	}
535	/* Make sure timer is disabled before returning */
536	intel_scu_stop();
537	return 0;
538
539/* error cleanup */
540
541request_irq_error:
542	misc_deregister(&watchdog_device.miscdev);
543misc_register_error:
544	unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
545register_reboot_error:
546	intel_scu_stop();
547	iounmap(watchdog_device.timer_load_count_addr);
548	return ret;
549}
550
551static void __exit intel_scu_watchdog_exit(void)
552{
553
554	misc_deregister(&watchdog_device.miscdev);
555	unregister_reboot_notifier(&watchdog_device.intel_scu_notifier);
556	/* disable the timer */
557	iowrite32(0x00000002, watchdog_device.timer_control_addr);
558	iounmap(watchdog_device.timer_load_count_addr);
559}
560
561late_initcall(intel_scu_watchdog_init);
562module_exit(intel_scu_watchdog_exit);
563
564MODULE_AUTHOR("Intel Corporation");
565MODULE_DESCRIPTION("Intel SCU Watchdog Device Driver");
566MODULE_LICENSE("GPL");
567MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
568MODULE_VERSION(WDT_VER);