[PATCH 1/3] PowerPC4xx/E500 WatchDogTimerDriver(Core and PPC4xx part)

Takeharu KATO kato.takeharu at jp.fujitsu.com
Tue Mar 1 00:18:20 EST 2005


Dear Matt and all:

I finished writing PowerPC4xx/e500 Watch Dog Timer Driver.
This driver consist of three parts of patches:

1) ppc4xx-wdt.patch  ... Driver core and PowerPC4xx relevant setup.
2) e500-wdt.patch    ... PowerPC e500 (MPC85xx) relevant setup.
3) exc-wdt.patch     ... Exception handler fixes.

Please apply these patches.

This driver is tested on following environments:

i)  Ebony evaluation board(CPU:PowerPC440GP)
ii) MPC8560 CDS evaluation board (CPU:MPC8560)

Please contact me via e-mail if there is a person who cooperates in the test.
I can send test-sets for this driver off-list.

Regards,

Signed-off-by: Takeharu KATO <kato.takeharu at jp.fujitsu.com>

--- linux-2.6.11-rc5.orig/arch/ppc/syslib/ppc4xx_setup.c	2005-02-27 15:26:57.000000000 +0900
+++ linux-2.6.11-rc5/arch/ppc/syslib/ppc4xx_setup.c	2005-02-28 20:51:45.000000000 +0900
@@ -48,10 +48,6 @@
  extern void abort(void);
  extern void ppc4xx_find_bridges(void);

-extern void ppc4xx_wdt_heartbeat(void);
-extern int wdt_enable;
-extern unsigned long wdt_period;
-
  /* Global Variables */
  bd_t __res;

@@ -257,22 +253,13 @@ ppc4xx_init(unsigned long r3, unsigned l
  		*(char *) (r7 + KERNELBASE) = 0;
  		strcpy(cmd_line, (char *) (r6 + KERNELBASE));
  	}
-#if defined(CONFIG_PPC405_WDT)
-/* Look for wdt= option on command line */
-	if (strstr(cmd_line, "wdt=")) {
-		int valid_wdt = 0;
-		char *p, *q;
-		for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) {
-			q = p + 4;
-			if (p > cmd_line && p[-1] != ' ')
-				continue;
-			wdt_period = simple_strtoul(q, &q, 0);
-			valid_wdt = 1;
-			++q;
-		}
-		wdt_enable = valid_wdt;
+#ifdef CONFIG_PPC4xx_WATCHDOG
+	{
+	  extern void ppc4xx_wdt_setup_options(char *cmd_line);
+
+	  ppc4xx_wdt_setup_options(cmd_line);
  	}
-#endif
+#endif  /*  CONFIG_PPC4xx_WATCHDOG  */

  	/* Initialize machine-dependent vectors */

@@ -287,9 +274,9 @@ ppc4xx_init(unsigned long r3, unsigned l

  	ppc_md.calibrate_decr = ppc4xx_calibrate_decr;

-#ifdef CONFIG_PPC405_WDT
+#ifdef CONFIG_PPC4xx_WATCHDOG
  	ppc_md.heartbeat = ppc4xx_wdt_heartbeat;
-#endif
+#endif  /*  CONFIG_PPC4xx_WATCHDOG  */
  	ppc_md.heartbeat_count = 0;

  	ppc_md.find_end_of_memory = ppc4xx_find_end_of_memory;
@@ -319,3 +306,5 @@ void platform_machine_check(struct pt_re
  #endif

  }
+
+
--- linux-2.6.11-rc5.orig/drivers/char/watchdog/Kconfig	2005-02-27 15:29:22.000000000 +0900
+++ linux-2.6.11-rc5/drivers/char/watchdog/Kconfig	2005-02-28 19:33:10.000000000 +0900
@@ -346,6 +346,13 @@ config 8xx_WDT
  	tristate "MPC8xx Watchdog Timer"
  	depends on WATCHDOG && 8xx

+config PPC4xx_WATCHDOG
+	bool "Watchdog on PowerPC 4xx/e500"
+	depends on WATCHDOG && ( 4xx || E500 )
+	---help---
+	  This is the driver for the watchdog timers present on
+	  PowerPC 4xx series(PPC405GP/GPr,PPC440GP/GX and so on).
+
  # MIPS Architecture

  config INDYDOG
--- linux-2.6.11-rc5.orig/drivers/char/watchdog/Makefile	2005-02-27 15:29:33.000000000 +0900
+++ linux-2.6.11-rc5/drivers/char/watchdog/Makefile	2005-02-27 21:37:31.000000000 +0900
@@ -39,3 +39,4 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.
  obj-$(CONFIG_IXP4XX_WATCHDOG) += ixp4xx_wdt.o
  obj-$(CONFIG_IXP2000_WATCHDOG) += ixp2000_wdt.o
  obj-$(CONFIG_8xx_WDT) += mpc8xx_wdt.o
+obj-$(CONFIG_PPC4xx_WATCHDOG)   += ppc4xx_wdt.o
\ No newline at end of file
--- linux-2.6.11-rc5.orig/drivers/char/watchdog/ppc4xx_wdt.c	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.11-rc5/drivers/char/watchdog/ppc4xx_wdt.c	2005-02-28 20:51:45.000000000 +0900
@@ -0,0 +1,635 @@
+/*
+ *    Copyright (c) 2005 Fujitsu Limited
+ *
+ *    Module name: ppc4xx_wdt.c
+ *    Author:      Takeharu KATO<kato.takeharu at jp.fujitsu.com>
+ *
+ *    This program is free software; you can redistribute it and/or
+ *    modify it under the terms of the GNU General Public License
+ *    as published by the Free Software Foundation; either version 2
+ *    of the License, or (at your option) any later version.
+ *
+ *    Neither Takeharu KATO nor Fujitsu Ltd. admit liability nor provide
+ *    warranty for any of this software.
+ *
+ *    Description:
+ *     Watchdog driver for PowerPC 4xx-based processors.
+ *     Derived from drivers/char/watchdog/wdt.c by Alan cox
+ *              and  drivers/char/watchdog/ppc405_wdt.c by Armin Kuster.
+ *     PPC4xx WDT operation is driverd from Appendix of
+ *     PowerPC Embedded Processors Application Note
+ *      ``PowerPC 40x Watch Dog Timer'' published from IBM.
+ *     This driver is written according to ``PowerPC e500 Core Complex
+ *     Reference Manual'' for e500 part.
+ */
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/miscdevice.h>
+#include <linux/watchdog.h>
+#include <linux/fs.h>
+#include <linux/reboot.h>
+#include <linux/init.h>
+#include <linux/capability.h>
+#include <linux/string.h>
+#include <asm/reg.h>
+#include <asm/uaccess.h>
+#include <asm/system.h>
+#include "ppc4xx_wdt.h"
+
+/* micro seconds per one milli-second(used to calculatewatchdog
+ * counter to be set). */
+#define US_PER_MS 1000
+/*  Calculate watchdog count   */
+#define calculate_wdt_count(t) ((((unsigned long)(t))*HZ)/1000)
+
+int wdt_enable=0;             /* WDT start on boot  */
+int wdt_period=WDT_TIMO;       /* Time out in ms */
+
+#ifdef CONFIG_WATCHDOG_NOWAYOUT
+static int nowayout = 1;
+#else
+static int nowayout = 0;
+#endif
+
+/*
+ * Global variables
+ */
+static int wdt_count = 0;            /* WDT intrrupt counter to be reloaded */
+static volatile int wdt_heartbeat_count = 0;  /* WDT intrrupt counter(compatible mode)*/
+static unsigned long driver_state; /* Driver status (see: ppc4xx_wdt.h) */
+/*
+ *  Identifier for this watchdog
+ */
+static struct watchdog_info ident = {
+  .options=WDIOF_SETTIMEOUT|WDIOF_KEEPALIVEPING|WDIOF_MAGICCLOSE,
+  .firmware_version =	0, /*  This is filled with PVR in initialization. */
+  .identity =		"PPC4xx WDT",
+};
+
+/*
+ *  External linkage functions
+ */
+void ppc4xx_wdt_heartbeat(void);
+void ppc4xx_wdt_setup_options(char *cmd_line);
+/*
+ * Internal linkage functions
+ */
+static __inline__ void __ppc4xx_wdt_setup_val(int period,int reset);
+static __inline__ void __ppc4xx_wdt_enable(void);
+static __inline__ void __ppc4xx_wdt_disable(void);
+static __inline__ int  __ppc4xx_wdt_is_enabled(void);
+static __inline__ void __ppc4xx_wdt_clear_int_stat(void);
+static __inline__ void __ppc4xx_wdt_set_timeout(int t);
+static __inline__ void ppc4xx_wdt_init_device(void);
+static __inline__ int  ppc4xx_wdt_is_enabled(void);
+static __inline__ int  ppc4xx_wdt_start(void);
+static __inline__ int  ppc4xx_wdt_stop(void);
+static __inline__ int  ppc4xx_wdt_ping(void);
+static __inline__ int  ppc4xx_wdt_set_timeout(int t);
+static __inline__ int  ppc4xx_wdt_get_status(int *status);
+static ssize_t ppc4xx_wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos);
+static int ppc4xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,unsigned long 
arg);
+static int ppc4xx_wdt_open(struct inode *inode, struct file *file);
+static int ppc4xx_wdt_release(struct inode *inode, struct file *file);
+static int ppc4xx_wdt_notify_sys(struct notifier_block *this, unsigned long code,void *unused);
+static int __init ppc4xx_wdt_init(void);
+static void __exit ppc4xx_wdt_exit(void);
+
+/*
+ *	Watchdog operations on PPC4xx MPU
+ */
+
+/**
+ *      __ppc4xx_wdt_setup_val
+ *      Enable 4xx Watchdog, sets up passed in values for TCR[WP],
+ *      TCR[WRC]
+ *
+ *	@period:	Input Watchdog Period - TCR[WP]
+ *                      0 = 2^17 clocks
+ *                      1 = 2^21 clocks
+ *                      2 = 2^25 clocks
+ *                      3 = 2^29 clocks
+ *      @reset:         Watchdog reset control - TCR[WRC]
+ *                      0 = No reset
+ *                      1 = PPC Core reset only
+ *                      2 = PPC Chip reset
+ *                      3 = System reset
+ *     Note: The meaning of period number is differ PPC440GP from PPC440GX.
+ */
+#if defined(CONFIG_4xx)
+static __inline__ void
+__ppc4xx_wdt_setup_val(int period,int reset)
+{
+  unsigned long val;
+
+  /*  Set up TCR  */
+  val=((period)<<WDT_TCR_WP_SHIFT|(reset)<<WDT_TCR_WRC_SHIFT)|mfspr(SPRN_TCR);
+  /*  Disable WDT  */
+  val &= ~(WDT_TCR_WDT_ENABLE);
+
+  mtspr(SPRN_TCR,val);
+}
+#else
+/*  e500  */
+static __inline__ void
+__ppc4xx_wdt_setup_val(int period,int reset)
+{
+  unsigned long val;
+  /*  Set up TCR  */
+
+  val=(((period)&(WDT_TCR_WP_BITMSK)) << WDT_TCR_WP_SHIFT|
+       ( ( (period) >> 2 )&(WDT_TCR_WPEXT_BITMSK)) << WDT_TCR_WPEXT_SHIFT|
+       (reset)<<WDT_TCR_WRC_SHIFT)|mfspr(SPRN_TCR);
+  /*  Disable WDT  */
+  val &= ~(WDT_TCR_WDT_ENABLE);
+
+  mtspr(SPRN_TCR,val);
+}
+#endif  /*  CONFIG_E500 */
+/**
+ *      __ppc4xx_wdt_enable
+ *      Enable 4xx Watchdog
+ */
+static __inline__ void
+__ppc4xx_wdt_enable(void)
+{
+  mtspr(SPRN_TCR,(mfspr(SPRN_TCR)|WDT_TCR_WDT_ENABLE));
+}
+/**
+ *      __ppc4xx_wdt_disable
+ *      Disable 4xx Watchdog
+ */
+static __inline__ void
+__ppc4xx_wdt_disable(void)
+{
+  mtspr(SPRN_TCR,(mfspr(SPRN_TCR)&(~(WDT_TCR_WDT_ENABLE))));
+}
+/**
+ *      __ppc4xx_wdt_is_enabled
+ *      Check whether 4xx Watchdog is enabled.
+ */
+static __inline__ int
+__ppc4xx_wdt_is_enabled(void)
+{
+  return (mfspr(SPRN_TCR) & WDT_TCR_WDT_ENABLE);
+}
+/**
+ *      __ppc4xx_wdt_clear_init_stat
+ *      Clear interrupt status of PPC4xx Watchdog to ping it.
+ */
+static __inline__ void
+__ppc4xx_wdt_clear_int_stat(void)
+{
+  mtspr(SPRN_TSR, (TSR_ENW|TSR_WIS));
+}
+/**
+ *	__ppc4xx_wdt_set_timeout:
+ *	@t:	the new time out value that needs to be set.
+ *
+ *	Set a new time out value for the watchdog device.
+ *
+ */
+static __inline__ void
+__ppc4xx_wdt_set_timeout(int t)
+{
+  wdt_count=calculate_wdt_count(t);
+  return;
+}
+
+/*
+ * Driver specific functions
+ */
+
+/**
+ *   ppc4xx_wdt_setup_options
+ *   @cmd_line : a pointer to kernel command line.
+ *
+ */
+void
+ppc4xx_wdt_setup_options(char *cmd_line)
+{
+/*
+ * Look for wdt= option on command line
+ */
+  if (strstr(cmd_line, "wdt=")) {
+    int valid_wdt = 0;
+    char *p, *q;
+
+    for (q = cmd_line; (p = strstr(q, "wdt=")) != 0;) {
+      q = p + 4;
+      if (p > cmd_line && p[-1] != ' ')
+	continue;
+      wdt_period = simple_strtoul(q, &q, 0);
+      valid_wdt = 1;
+      ++q;
+    }
+    wdt_enable = valid_wdt;
+  }
+  return;
+}
+/**
+ *	ppc4xx_wdt_heartbeat:
+ *      Ping routine called from kernel.
+ */
+void
+ppc4xx_wdt_heartbeat(void)
+{
+  /* Disable watchdog */
+  __ppc4xx_wdt_disable();
+
+  /* Write a watchdog value */
+  __ppc4xx_wdt_clear_int_stat();
+
+  if (!wdt_enable)
+    goto out;
+
+  if  (wdt_heartbeat_count > 0)
+    wdt_heartbeat_count--;
+  else
+    panic(ppc4xx_mkmsg("Initiating system reboot.\n"));
+
+  /* Enable watchdog */
+  __ppc4xx_wdt_enable();
+ out:
+  /*  Reset count  */
+  ppc_md.heartbeat_count = 0;
+}
+
+/*
+ *	Driver Logic functions
+ */
+static __inline__ int
+ppc4xx_wdt_is_enabled(void)
+{
+  return  __ppc4xx_wdt_is_enabled();
+}
+/**
+ *	ppc4xx_wdt_start:
+ *
+ *	Start the watchdog driver.
+ */
+static __inline__ int
+ppc4xx_wdt_start(void)
+{
+  __ppc4xx_wdt_enable();
+  return 0;
+}
+
+/**
+ *	ppc4xx_wdt_stop:
+ *
+ *	Stop the watchdog driver.
+ */
+static __inline__ int
+ppc4xx_wdt_stop (void)
+{
+  __ppc4xx_wdt_disable();
+  return 0;
+}
+/**
+ *	ppc4xx_wdt_ping:
+ *
+ *	Reload counter one with the watchdog heartbeat. We don't bother reloading
+ *	the cascade counter.
+ */
+static __inline__ int
+ppc4xx_wdt_ping(void)
+{
+  /* Disable watchdog */
+  __ppc4xx_wdt_disable();
+  /* Write a watchdog value */
+  __ppc4xx_wdt_clear_int_stat();
+  /*  Reset count  */
+  wdt_heartbeat_count=wdt_count;
+  /* Enable watchdog */
+  __ppc4xx_wdt_enable();
+
+  return 0;
+}
+/**
+ *	ppc4xx_wdt_set_timeout:
+ *	@t:		the new timeout value that needs to be set.
+ *
+ *	Set a new time out value for the watchdog device.
+ *      If the heartbeat value is incorrect we keep the old value
+ *      and return -EINVAL. If successfull we return 0.
+ */
+static __inline__ int
+ppc4xx_wdt_set_timeout(int t)
+{
+  if ((t < WDT_HEARTBEAT_MIN) || (t > WDT_HEARTBEAT_MAX))
+    return -EINVAL;
+
+  wdt_period = t;
+  __ppc4xx_wdt_set_timeout(t);
+  wdt_heartbeat_count=wdt_count;
+  ppc4xx_wdt_dbg("The WDT counter set %d.\n",wdt_count);
+
+  return 0;
+}
+
+/**
+ *	ppc4xx_wdt_get_status:
+ *	@status: the new status.
+ *
+ *	Return the enable/disable card status.
+ */
+static __inline__ int
+ppc4xx_wdt_get_status(int *status)
+{
+  if (wdt_enable)
+	  *status = WDIOS_ENABLECARD;
+  else
+	  *status = WDIOS_DISABLECARD;
+
+  return 0;
+}
+/*
+ *	Kernel Interfaces
+ */
+/**
+ *	ppc4xx_wdt_init_device:
+ *
+ *      Initilize PowerPC 4xx family Watch Dog facility.
+ */
+static void
+ppc4xx_wdt_init_device(void)
+{
+        /* Hardware WDT provided by the processor.
+	 * So, we set firmware version as processor version number.
+	 */
+	ident.firmware_version=mfspr(PVR);
+	__ppc4xx_wdt_setup_val(WDT_WP,WDT_RESET_NONE);
+}
+/**
+ *	ppc4xx_wdt_write:
+ *	@file: file handle to the watchdog
+ *	@buf: buffer to write (unused as data does not matter here
+ *	@count: count of bytes
+ *	@ppos: pointer to the position to write. No seeks allowed
+ *
+ *	A write to a watchdog device is defined as a keepalive signal. Any
+ *	write of data will do, as we we don't define content meaning expept
+ *      'V' character. It is performed as a sign to set stop-on-close mode.
+ */
+
+static ssize_t
+ppc4xx_wdt_write(struct file *file, const char *buf, size_t count, loff_t *ppos)
+{
+  size_t i;
+
+    if (!nowayout) {
+      /* In case it was set long ago */
+      clear_bit(WDT_STATE_STOP_ON_CLOSE, &driver_state);
+
+      for (i = 0; i < count; i++) {
+	char c;
+
+	if (get_user(c, buf + i))
+	  return -EFAULT;
+
+	if (c == 'V') {
+	  set_bit(WDT_STATE_STOP_ON_CLOSE, &driver_state);
+	}
+      }
+    }
+    ppc4xx_wdt_ping();
+
+  return count;
+}
+
+/**
+ *	ppc4xx_wdt_ioctl:
+ *	@inode: inode of the device
+ *	@file: file handle to the device
+ *	@cmd: watchdog command
+ *	@arg: argument pointer
+ *
+ */
+static int
+ppc4xx_wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
+	unsigned long arg)
+{
+	int new_timeout;
+	int status;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;  /*  It may be too strict manner.  */
+	switch(cmd)
+	{
+	default:
+		return -ENOIOCTLCMD;
+	case WDIOC_GETSUPPORT:
+		if (copy_to_user((struct watchdog_info *)arg, &ident, sizeof(struct watchdog_info)))
+			return -EFAULT;
+		else
+			break;
+	case WDIOC_GETSTATUS:
+		ppc4xx_wdt_get_status(&status);
+		return put_user(status,(int *)arg);
+	case WDIOC_KEEPALIVE:
+		ppc4xx_wdt_ping();
+		break;
+	case WDIOC_SETTIMEOUT:
+		if (get_user(new_timeout, (int *)arg))
+			return -EFAULT;
+		if (ppc4xx_wdt_set_timeout(new_timeout))
+			return -EINVAL;
+		ppc4xx_wdt_ping();
+		break;
+	case WDIOC_GETTIMEOUT:
+		return put_user(wdt_period, (int *)arg);
+	case WDIOC_SETOPTIONS:
+		if (get_user(status, (int *)arg))
+			return -EFAULT;
+		/*  Return -EINVAL when the driver can not figure out
+		 *  what it should do. Unknown cases are just ignored.
+		 */
+		if ( (status & (WDIOS_DISABLECARD|WDIOS_ENABLECARD))
+		     == (WDIOS_DISABLECARD|WDIOS_ENABLECARD) )
+			return -EINVAL;
+		if (status & WDIOS_DISABLECARD) {
+			wdt_enable = 0;
+			ppc4xx_wdt_stop();
+			ppc4xx_wdt_note("Watchdog timer is disabled\n");
+		}
+		if (status & WDIOS_ENABLECARD) {
+			wdt_enable = 1;
+			ppc4xx_wdt_start();
+			ppc4xx_wdt_note("Watchdog timer is enabled\n");
+		}
+		break;
+	}
+	return 0;
+}
+/**
+ *	ppc4xx_wdt_open:
+ *	@inode: inode of device
+ *	@file: file handle to device
+ *
+ *	The watchdog device has been opened. The watchdog device is single
+ *	open and start the WDT timer.
+ */
+static int
+ppc4xx_wdt_open(struct inode *inode, struct file *file)
+{
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (test_and_set_bit(WDT_STATE_OPEN, &driver_state))
+		return -EBUSY;
+	/*
+	 * Activate
+	 */
+	ppc4xx_wdt_start();
+	wdt_enable=1;
+
+	if (nowayout)
+	  set_bit(WDT_STATE_STOP_ON_CLOSE, &driver_state);
+
+	return 0;
+}
+
+/**
+ *	ppc4xx_wdt_release:
+ *	@inode: inode to board
+ *	@file: file handle to board
+ *
+ */
+static int
+ppc4xx_wdt_release(struct inode *inode, struct file *file)
+{
+  if (test_bit(WDT_STATE_STOP_ON_CLOSE, &driver_state)) {
+      ppc4xx_wdt_note("WDT device is stopped.\n");
+    ppc4xx_wdt_stop();
+    wdt_enable=0;
+  } else {
+    if ( (ppc4xx_wdt_is_enabled()) && (!nowayout) ) {
+      ppc4xx_wdt_note("WDT device may be closed unexpectedly.  WDT will not stop!\n");
+      ppc4xx_wdt_ping();
+    }
+  }
+  clear_bit(WDT_STATE_OPEN, &driver_state);
+
+  return 0;
+}
+/**
+ *	notify_sys:
+ *	@this: our notifier block
+ *	@code: the event being reported
+ *	@unused: unused
+ *
+ */
+
+static int
+ppc4xx_wdt_notify_sys(struct notifier_block *this, unsigned long code,
+	void *unused)
+{
+	if(code==SYS_DOWN || code==SYS_HALT) {
+	  /* Turn the card off */
+	  ppc4xx_wdt_stop();
+	}
+	return NOTIFY_DONE;
+}
+
+static struct file_operations ppc4xx_wdt_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.write		= ppc4xx_wdt_write,
+	.ioctl		= ppc4xx_wdt_ioctl,
+	.open		= ppc4xx_wdt_open,
+	.release	= ppc4xx_wdt_release,
+};
+
+static struct miscdevice ppc4xx_wdt_miscdev = {
+	.minor	= WATCHDOG_MINOR,
+	.name	= "watchdog",
+	.fops	= &ppc4xx_wdt_fops,
+};
+
+/*
+ *	The WDT card needs to know about shutdowns in order to
+ *	turn WDT off.
+ */
+
+static struct notifier_block ppc4xx_wdt_notifier = {
+	.notifier_call = ppc4xx_wdt_notify_sys,
+};
+
+/**
+ *	cleanup_module:
+ *
+ *	If your watchdog is set to continue ticking on close and you unload
+ *	it, well it keeps ticking.  You just have to load a new
+ *	module in 60 seconds or reboot.
+ *      This behavior(more over the comments as above) is borrowed from
+ *      Alan cox's driver.
+ */
+
+static void __exit
+ppc4xx_wdt_exit(void)
+{
+	misc_deregister(&ppc4xx_wdt_miscdev);
+	unregister_reboot_notifier(&ppc4xx_wdt_notifier);
+}
+
+/**
+ * 	ppc4xx_wdt_init:
+ *
+ *	Set up the WDT relevant timer facility.
+ */
+
+static int __init
+ppc4xx_wdt_init(void)
+{
+	int ret;
+	unsigned long flags;
+
+	ret = register_reboot_notifier(&ppc4xx_wdt_notifier);
+	if(ret) {
+	  ppc4xx_wdt_err("Cannot register reboot notifier (err=%d)\n", ret);
+	  return ret;
+	}
+
+	ret = 0;
+	ppc4xx_wdt_init_device();
+	/* Check that the heartbeat value is within it's range ; if not reset to the default */
+	if (ppc4xx_wdt_set_timeout(wdt_period)) {
+	  if (wdt_period)
+	    ppc4xx_wdt_info("The heartbeat value must be %d < wdt_period < %d, using 
%d\n",WDT_HEARTBEAT_MIN,WDT_HEARTBEAT_MAX,WDT_TIMO);
+	  ppc4xx_wdt_set_timeout(WDT_TIMO);
+	}
+	
+	local_irq_save(flags); /* Prevent timer interrupt */
+	ppc_md.heartbeat_count = 0;	
+	ppc_md.heartbeat=ppc4xx_wdt_heartbeat;
+	local_irq_restore(flags);
+
+	ppc4xx_wdt_info("PowerPC 4xx Watchdog Driver. period=%d ms (nowayout=%d)\n",wdt_period, nowayout);
+
+	ret = misc_register(&ppc4xx_wdt_miscdev);
+	if (ret) {
+	  ppc4xx_wdt_err("Cannot register miscdev on minor=%d (err=%d)\n",
+			WATCHDOG_MINOR, ret);
+		goto outmisc;
+	}
+
+	if (wdt_enable) {
+	  ppc4xx_wdt_info("WDT start on boot.\n");
+	  ppc4xx_wdt_start();
+	}
+out:
+	return ret;
+outmisc:
+	unregister_reboot_notifier(&ppc4xx_wdt_notifier);
+	local_irq_save(flags);
+	ppc_md.heartbeat=NULL;
+	ppc_md.heartbeat_count = 0;
+	local_irq_restore(flags);
+	goto out;
+}
+
+module_init(ppc4xx_wdt_init);
+module_exit(ppc4xx_wdt_exit);
+
--- linux-2.6.11-rc5.orig/drivers/char/watchdog/ppc4xx_wdt.h	1970-01-01 09:00:00.000000000 +0900
+++ linux-2.6.11-rc5/drivers/char/watchdog/ppc4xx_wdt.h	2005-02-28 19:33:10.000000000 +0900
@@ -0,0 +1,125 @@
+/*
+ *
+ *    Copyright (c) 2004 Fujitsu Limited
+ *
+ *    Module name: ppc4xx_wdt.h
+ *    Author:      Takeharu KATO<kato.takeharu at jp.fujitsu.com>
+ *    Description:
+ *      Header file for PPC4xx watchdog driver.
+ *
+ *    This program is free software; you can redistribute it and/or
+ *    modify it under the terms of the GNU General Public License
+ *    as published by the Free Software Foundation; either version 2
+ *    of the License, or (at your option) any later version.
+ *
+ *    Neither Takeharu KATO nor Fujitsu Ltd. admit liability nor provide
+ *    warranty for any of this software.
+ *
+ */
+#ifndef _DRIVERS_CHAR_WATCHDOG_PPC4XX_WDT_H
+#define _DRIVERS_CHAR_WATCHDOG_PPC4XX_WDT_H
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/watchdog.h>
+
+/*
+ *  Driver state flags(bit position)
+ */
+#define WDT_STATE_OPEN                  0   /* driver is opend  */
+#define WDT_STATE_STOP_ON_CLOSE         1   /* Stop with close is expected */
+/*
+ * Configurations
+ */
+#define WDT_TIMO           60000	/* Default timeout = 60000 ms(1min) */
+#define WDT_HEARTBEAT_MIN  100          /* Minimum timeout = 100 ms */
+#define WDT_HEARTBEAT_MAX  600000       /* Maximum timeout = 600000ms(1hour) */
+#ifdef __KERNEL__
+//#define WDT_DEBUG                     /*  Debug switch */
+/*
+ *  Reset type
+ */
+#define WDT_RESET_NONE     0
+#define WDT_RESET_CORE     1
+#define WDT_RESET_CHIP     2
+#define WDT_RESET_SYS      3
+/*
+ *   Bit positions in  TCR register on PPC4xx/e500 series.
+ */
+#define WDT_TCR_WP_BIT     1   /*  WP  bit in TCR (bit[0..1])   */
+#define WDT_TCR_WRC_BIT    3   /*  WRC bit in TCR (bit[2..3])   */
+#define WDT_TCR_WIE_BIT    4   /*  WIE bit in TCR (bit[4])      */
+/*
+ *  TCR[WP] relevant definitions
+ */
+#define WDT_TCR_WP_SHIFT       (31 - WDT_TCR_WP_BIT)
+#define WDT_TCR_WRC_SHIFT      (31 - WDT_TCR_WRC_BIT)
+#define WDT_TCR_WIE_SHIFT      (31 - WDT_TCR_WIE_BIT)
+#define WDT_TCR_WDT_ENABLE     (1<<WDT_TCR_WIE_SHIFT)
+/*  MASK value to obatain TCR[WP]  */
+#define WDT_TCR_WP_MASK        (3<<(WDT_TCR_WP_SHIFT))
+
+/*  Watchdog timer periods can be set on PPC4xx cpus. */
+#if defined(CONFIG_4xx)
+/*
+ *  For PowerPC4xx
+ */
+#define WDT_WP0               0
+#define WDT_WP1               1
+#define WDT_WP2               2
+#define WDT_WP3               3
+#else
+#if defined(CONFIG_E500)
+/*
+ *  For e500 CPU
+ *  Actually, e500 can arbitrary periods can be set,
+ *  But this driver uses fix period value as same as PPC440
+ *  on purpose for simplicity.
+ *  Following values split into WP and WP_EXT parts in ppc4xx_wdt.c.
+ */
+#define WDT_WP0               21
+#define WDT_WP1               25
+#define WDT_WP2               29
+#define WDT_WP3               33
+#define WDT_TCR_WP_BITMSK     0x3  /*  2bit length  */
+#define WDT_TCR_WPEXT_BITMSK  0xf  /*  4bit length  */
+#define WDT_TCR_WPEXT_SHIFT  17
+#else
+#error "PPC4xx WDT Detect invalid configuration(Unknown CPU)"
+#endif  /*  CONFIG_E500  */
+#endif  /*  CONFIG_4xx   */
+/*
+ *  WP relevant values used in our driver.
+ *  Note:WDT period must be more than HZ(Timer ticks)
+ */
+#define WDT_WP                 WDT_WP3
+
+/*
+ *  IOCTL commands for comaptiblity for old driver
+ */
+#define WDIOC_GETPERIOD         WDIOC_GETTIMEOUT
+#define WDIOC_SETPERIOD         WDIOC_SETTIMEOUT
+
+/*
+ *  output messages
+ */
+#define __PPC4xx_WDT_MSG "PPC4xx WDT : "
+#define ppc4xx_mkmsg(str) __PPC4xx_WDT_MSG str
+#define ppc4xx_wdt_info(fmt,arg...) \
+	printk(KERN_INFO __PPC4xx_WDT_MSG fmt,##arg)
+#define ppc4xx_wdt_note(fmt,arg...) \
+	printk(KERN_NOTICE __PPC4xx_WDT_MSG fmt,##arg)
+#define ppc4xx_wdt_err(fmt,arg...) \
+	printk(KERN_ALERT __PPC4xx_WDT_MSG fmt,##arg)
+#define ppc4xx_wdt_crit(fmt,arg...) \
+	printk(KERN_ALERT __PPC4xx_WDT_MSG fmt,##arg)
+#if defined(WDT_DEBUG)
+#define ppc4xx_wdt_dbg(fmt,arg...) \
+	printk(KERN_ALERT __PPC4xx_WDT_MSG fmt,##arg)
+#else
+#define ppc4xx_wdt_dbg(fmt,arg...) \
+        do{}while(0)
+#endif  /*  WDT_DEBUG  */
+
+#endif  /* __KERNEL__  */
+#endif  /*  _DRIVERS_CHAR_WATCHDOG_PPC4XX_WDT_H  */




More information about the Linuxppc-embedded mailing list