diff mbox

[LEDE-DEV] cns3xxx: restore mpcore watchdog

Message ID 1484055297-11313-1-git-send-email-koen.vandeputte@ncentric.com
State Superseded
Headers show

Commit Message

Koen Vandeputte Jan. 10, 2017, 1:34 p.m. UTC
Adds the mpcore watchdog driver for kernel 4.4 as it was originally
present up to kernel 3.12

- Disabled by default for now, but can be simply enabled in kernelconfig
- Currently does not contain the latest patches as mentioned here:

https://git.kernel.org/cgit/linux/kernel/git/next/linux-
next.git/commit/?id=6e63a3a294fdf91eaaac1061a9c7a5f53d16ac25

Tested on cns3xxx (GW2388)

[    0.700000] mpcore_wdt: MPcore Watchdog Timer: 0.1. mpcore_noboot=0
mpcore_margin=60 sec (nowayout= 1)
[    3.490000] mpcore_wdt mpcore_wdt: enabling watchdog
[    3.490000] init: - watchdog -
[    8.750000] procd: - watchdog -

Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
---
 target/linux/cns3xxx/config-4.4                    |   1 +
 .../patches-4.4/300-restore-mpcore-watchdog.patch  | 502 +++++++++++++++++++++
 2 files changed, 503 insertions(+)
 create mode 100644 target/linux/cns3xxx/patches-4.4/300-restore-mpcore-watchdog.patch

Comments

Tim Harvey Jan. 16, 2017, 9:37 p.m. UTC | #1
On Tue, Jan 10, 2017 at 5:34 AM, Koen Vandeputte
<koen.vandeputte@ncentric.com> wrote:
> Adds the mpcore watchdog driver for kernel 4.4 as it was originally
> present up to kernel 3.12
>
> - Disabled by default for now, but can be simply enabled in kernelconfig
> - Currently does not contain the latest patches as mentioned here:

Koen,

Thanks for submitting this - I agree with Felix that we should add
back the functionality if possible.

The issue mentioned in the commit which removed the driver from
mainline linux [1] regarding 'Nothing ensures the userspace ioctl()
will end-up kicking the watchdog on the right CPU.' is addressed by
the fact that the watchdog register base defined in laguna.c [2] as
the register 'CNS3XXX_TC11MP_TWD_BASE + 0x100' always points to the
CPU0 watchdog as opposed to CNS3XXX_TC11MP_TWD_BASE which would be
aliased to the watchdog of whatever CPU core the process is running.

However, I found after a bit of digging I found that this mainline
driver requires other patches to make the watchdog reliable which
apparently never got accepted upstream [3] [4] [5] [6]. While these
patches were apparently never accepted upstream they were in the
OpenWrt git repo for the 3.10 kernel and below and not surprisingly
got removed when the kernel bumped past 3.10. These patches also need
to be re-added (at least definitely [3]) as without them you'll find
that the timer count is too large and the watchdog will unexpectedly
trip typically within a matter of several minutes.

With these additional patches added I would say that we should also
enable the watchdog in the kernel config.

How do you want to proceed? I could ack your patch (as it adds the
base driver back from mainline but doesn't enable it) then followup
with a patch that adds back the additional patches (to
target/platform/cns3xxx unless there are other targets that use the
arm11mpcore processor and want the twd fixes?) and enables the
watchdog kernel driver. Note that the patches affect other files so we
can't simply merge them all into the files directory.

Regards,

Tim

[1] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6e63a3a294fdf91eaaac1061a9c7a5f53d16ac25
[2] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/laguna.c;h=4a596125ab5c8ab1b3a1d31e00d406097b2412d0;hb=HEAD#l636
[3] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/generic/target/linux/generic/target/linux/generic/patches-3.10/target/linux/generic/patches-3.10/992-mpcore_wdt_fix_watchdog_counter_loading.patch;h=ae81f3f384b5805ee64dcbadcaab38aaea1acf1e
[4] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/generic/target/linux/generic/target/linux/generic/patches-3.10/target/linux/generic/patches-3.10/993-mpcore_wdt_fix_wdioc_setoptions_handling.patch;h=c986ac8f216c2a201875b44fc2fe6a8191680c13
[5] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/generic/target/linux/generic/target/linux/generic/patches-3.10/target/linux/generic/patches-3.10/994-mpcore_wdt_fix_timer_mode_setup.patch;h=ecc2452cabe058990d56052644e3d328e549df01
[6] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/generic/target/linux/generic/target/linux/generic/patches-3.10/target/linux/generic/patches-3.10/995-mpcore_twd_calibrate_single_core.patch;h=70411160fac88aeae42a2696039ab9bb5e9f47ba
Koen Vandeputte Jan. 17, 2017, 8:37 a.m. UTC | #2
Hi Tim,

> Koen,
>
> Thanks for submitting this - I agree with Felix that we should add
> back the functionality if possible.
>
> The issue mentioned in the commit which removed the driver from
> mainline linux [1] regarding 'Nothing ensures the userspace ioctl()
> will end-up kicking the watchdog on the right CPU.' is addressed by
> the fact that the watchdog register base defined in laguna.c [2] as
> the register 'CNS3XXX_TC11MP_TWD_BASE + 0x100' always points to the
> CPU0 watchdog as opposed to CNS3XXX_TC11MP_TWD_BASE which would be
> aliased to the watchdog of whatever CPU core the process is running.
> However, I found after a bit of digging I found that this mainline
> driver requires other patches to make the watchdog reliable which
> apparently never got accepted upstream [3] [4] [5] [6]. While these
> patches were apparently never accepted upstream they were in the
> OpenWrt git repo for the 3.10 kernel and below and not surprisingly
> got removed when the kernel bumped past 3.10. These patches also need
> to be re-added (at least definitely [3]) as without them you'll find
> that the timer count is too large and the watchdog will unexpectedly
> trip typically within a matter of several minutes.
Thanks for the detailed info.
>
> With these additional patches added I would say that we should also
> enable the watchdog in the kernel config.
>
> How do you want to proceed? I could ack your patch (as it adds the
> base driver back from mainline but doesn't enable it) then followup
> with a patch that adds back the additional patches (to
> target/platform/cns3xxx unless there are other targets that use the
> arm11mpcore processor and want the twd fixes?) and enables the
> watchdog kernel driver. Note that the patches affect other files so we
> can't simply merge them all into the files directory.

Feel free to Ack.
My plan was to first provide this baseline, and then add the needed 
customization via a second patch.

Like you have noticed, I deliberately submitted as platform specific (& 
disabled) for now until it's fully finished and thoroughly tested.
If another MPcore based platform would like to use it afterwards, it 
should be easy to share it. (Generic or target specific)

The earliest window I see in my calender to continue working on it is in 
about 2 weeks from now, if nothing else pops up ..
If you (or anyone else) would like to further provide fixes it in the 
meantime, please do. :-)

Koen
>
> Regards,
>
> Tim
>
> [1] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/commit/?id=6e63a3a294fdf91eaaac1061a9c7a5f53d16ac25
> [2] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/cns3xxx/files/arch/arm/mach-cns3xxx/laguna.c;h=4a596125ab5c8ab1b3a1d31e00d406097b2412d0;hb=HEAD#l636
> [3] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/generic/target/linux/generic/target/linux/generic/patches-3.10/target/linux/generic/patches-3.10/992-mpcore_wdt_fix_watchdog_counter_loading.patch;h=ae81f3f384b5805ee64dcbadcaab38aaea1acf1e
> [4] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/generic/target/linux/generic/target/linux/generic/patches-3.10/target/linux/generic/patches-3.10/993-mpcore_wdt_fix_wdioc_setoptions_handling.patch;h=c986ac8f216c2a201875b44fc2fe6a8191680c13
> [5] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/generic/target/linux/generic/target/linux/generic/patches-3.10/target/linux/generic/patches-3.10/994-mpcore_wdt_fix_timer_mode_setup.patch;h=ecc2452cabe058990d56052644e3d328e549df01
> [6] https://git.lede-project.org/?p=source.git;a=blob;f=target/linux/generic/target/linux/generic/target/linux/generic/patches-3.10/target/linux/generic/patches-3.10/995-mpcore_twd_calibrate_single_core.patch;h=70411160fac88aeae42a2696039ab9bb5e9f47ba
Tim Harvey Jan. 25, 2017, 4:56 p.m. UTC | #3
On Tue, Jan 10, 2017 at 5:34 AM, Koen Vandeputte
<koen.vandeputte@ncentric.com> wrote:
> Adds the mpcore watchdog driver for kernel 4.4 as it was originally
> present up to kernel 3.12
>
> - Disabled by default for now, but can be simply enabled in kernelconfig
> - Currently does not contain the latest patches as mentioned here:
>
> https://git.kernel.org/cgit/linux/kernel/git/next/linux-
> next.git/commit/?id=6e63a3a294fdf91eaaac1061a9c7a5f53d16ac25
>
> Tested on cns3xxx (GW2388)
>
> [    0.700000] mpcore_wdt: MPcore Watchdog Timer: 0.1. mpcore_noboot=0
> mpcore_margin=60 sec (nowayout= 1)
> [    3.490000] mpcore_wdt mpcore_wdt: enabling watchdog
> [    3.490000] init: - watchdog -
> [    8.750000] procd: - watchdog -
>
> Signed-off-by: Koen Vandeputte <koen.vandeputte@ncentric.com>
> ---
>  target/linux/cns3xxx/config-4.4                    |   1 +
>  .../patches-4.4/300-restore-mpcore-watchdog.patch  | 502 +++++++++++++++++++++
>  2 files changed, 503 insertions(+)
>  create mode 100644 target/linux/cns3xxx/patches-4.4/300-restore-mpcore-watchdog.patch
>
> diff --git a/target/linux/cns3xxx/config-4.4 b/target/linux/cns3xxx/config-4.4
> index 4745f4e..ffc1f02 100644
> --- a/target/linux/cns3xxx/config-4.4
> +++ b/target/linux/cns3xxx/config-4.4
> @@ -182,6 +182,7 @@ CONFIG_MMC_SDHCI_CNS3XXX=y
>  CONFIG_MMC_SDHCI_PLTFM=y
>  # CONFIG_MMC_TIFM_SD is not set
>  CONFIG_MODULES_USE_ELF_REL=y
> +# CONFIG_MPCORE_WATCHDOG is not set
>  CONFIG_MTD_M25P80=y
>  # CONFIG_MTD_OF_PARTS is not set
>  CONFIG_MTD_PHYSMAP=y
> diff --git a/target/linux/cns3xxx/patches-4.4/300-restore-mpcore-watchdog.patch b/target/linux/cns3xxx/patches-4.4/300-restore-mpcore-watchdog.patch
> new file mode 100644
> index 0000000..156dcaa
> --- /dev/null
> +++ b/target/linux/cns3xxx/patches-4.4/300-restore-mpcore-watchdog.patch
> @@ -0,0 +1,502 @@
> +--- a/Documentation/watchdog/watchdog-parameters.txt
> ++++ b/Documentation/watchdog/watchdog-parameters.txt
> +@@ -196,6 +196,14 @@ reset: Watchdog Interrupt/Reset Mode. 0
> + nowayout: Watchdog cannot be stopped once started
> +       (default=kernel config parameter)
> + -------------------------------------------------
> ++mpcore_wdt:
> ++mpcore_margin: MPcore timer margin in seconds.
> ++      (0 < mpcore_margin < 65536, default=60)
> ++nowayout: Watchdog cannot be stopped once started
> ++      (default=kernel config parameter)
> ++mpcore_noboot: MPcore watchdog action, set to 1 to ignore reboots,
> ++      0 to reboot (default=0
> ++--------------------------------------------------
> + mv64x60_wdt:
> + nowayout: Watchdog cannot be stopped once started
> +       (default=kernel config parameter)
> +--- a/drivers/watchdog/Kconfig
> ++++ b/drivers/watchdog/Kconfig
> +@@ -297,6 +297,15 @@ config DW_WATCHDOG
> +         To compile this driver as a module, choose M here: the
> +         module will be called dw_wdt.
> +
> ++config MPCORE_WATCHDOG
> ++      tristate "MPcore watchdog"
> ++      depends on HAVE_ARM_TWD
> ++      help
> ++        Watchdog timer embedded into the MPcore system.
> ++
> ++        To compile this driver as a module, choose M here: the
> ++        module will be called mpcore_wdt.
> ++
> + config EP93XX_WATCHDOG
> +       tristate "EP93xx Watchdog"
> +       depends on ARCH_EP93XX
> +--- a/drivers/watchdog/Makefile
> ++++ b/drivers/watchdog/Makefile
> +@@ -43,6 +43,7 @@ obj-$(CONFIG_S3C2410_WATCHDOG) += s3c241
> + obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
> + obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o
> + obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
> ++obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
> + obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
> + obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
> + obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
> +--- /dev/null
> ++++ b/drivers/watchdog/mpcore_wdt.c
> +@@ -0,0 +1,454 @@
> ++/*
> ++ *    Watchdog driver for the mpcore watchdog timer
> ++ *
> ++ *    (c) Copyright 2004 ARM Limited
> ++ *
> ++ *    Based on the SoftDog driver:
> ++ *    (c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
> ++ *                                            All Rights Reserved.
> ++ *
> ++ *    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 Alan Cox nor CymruNet Ltd. admit liability nor provide
> ++ *    warranty for any of this software. This material is provided
> ++ *    "AS-IS" and at no charge.
> ++ *
> ++ *    (c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
> ++ *
> ++ */
> ++
> ++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> ++
> ++#include <linux/module.h>
> ++#include <linux/moduleparam.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/interrupt.h>
> ++#include <linux/platform_device.h>
> ++#include <linux/uaccess.h>
> ++#include <linux/slab.h>
> ++#include <linux/io.h>
> ++
> ++#include <asm/smp_twd.h>
> ++
> ++struct mpcore_wdt {
> ++      unsigned long   timer_alive;
> ++      struct device   *dev;
> ++      void __iomem    *base;
> ++      int             irq;
> ++      unsigned int    perturb;
> ++      char            expect_close;
> ++};
> ++
> ++static struct platform_device *mpcore_wdt_pdev;
> ++static DEFINE_SPINLOCK(wdt_lock);
> ++
> ++#define TIMER_MARGIN  60
> ++static int mpcore_margin = TIMER_MARGIN;
> ++module_param(mpcore_margin, int, 0);
> ++MODULE_PARM_DESC(mpcore_margin,
> ++      "MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
> ++                              __MODULE_STRING(TIMER_MARGIN) ")");
> ++
> ++static bool nowayout = WATCHDOG_NOWAYOUT;
> ++module_param(nowayout, bool, 0);
> ++MODULE_PARM_DESC(nowayout,
> ++      "Watchdog cannot be stopped once started (default="
> ++                              __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
> ++
> ++#define ONLY_TESTING  0
> ++static int mpcore_noboot = ONLY_TESTING;
> ++module_param(mpcore_noboot, int, 0);
> ++MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
> ++      "set to 1 to ignore reboots, 0 to reboot (default="
> ++                                      __MODULE_STRING(ONLY_TESTING) ")");
> ++
> ++/*
> ++ *    This is the interrupt handler.  Note that we only use this
> ++ *    in testing mode, so don't actually do a reboot here.
> ++ */
> ++static irqreturn_t mpcore_wdt_fire(int irq, void *arg)
> ++{
> ++      struct mpcore_wdt *wdt = arg;
> ++
> ++      /* Check it really was our interrupt */
> ++      if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
> ++              dev_crit(wdt->dev, "Triggered - Reboot ignored\n");
> ++              /* Clear the interrupt on the watchdog */
> ++              writel(1, wdt->base + TWD_WDOG_INTSTAT);
> ++              return IRQ_HANDLED;
> ++      }
> ++      return IRQ_NONE;
> ++}
> ++
> ++/*
> ++ *    mpcore_wdt_keepalive - reload the timer
> ++ *
> ++ *    Note that the spec says a DIFFERENT value must be written to the reload
> ++ *    register each time.  The "perturb" variable deals with this by adding 1
> ++ *    to the count every other time the function is called.
> ++ */
> ++static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
> ++{
> ++      unsigned long count;
> ++
> ++      spin_lock(&wdt_lock);
> ++      /* Assume prescale is set to 256 */
> ++      count =  __raw_readl(wdt->base + TWD_WDOG_COUNTER);
> ++      count = (0xFFFFFFFFU - count) * (HZ / 5);
> ++      count = (count / 256) * mpcore_margin;
> ++
> ++      /* Reload the counter */
> ++      writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
> ++      wdt->perturb = wdt->perturb ? 0 : 1;
> ++      spin_unlock(&wdt_lock);
> ++}
> ++
> ++static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
> ++{
> ++      spin_lock(&wdt_lock);
> ++      writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
> ++      writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
> ++      writel(0x0, wdt->base + TWD_WDOG_CONTROL);
> ++      spin_unlock(&wdt_lock);
> ++}
> ++
> ++static void mpcore_wdt_start(struct mpcore_wdt *wdt)
> ++{
> ++      dev_info(wdt->dev, "enabling watchdog\n");
> ++
> ++      /* This loads the count register but does NOT start the count yet */
> ++      mpcore_wdt_keepalive(wdt);
> ++
> ++      if (mpcore_noboot) {
> ++              /* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
> ++              writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
> ++      } else {
> ++              /* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
> ++              writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
> ++      }
> ++}
> ++
> ++static int mpcore_wdt_set_heartbeat(int t)
> ++{
> ++      if (t < 0x0001 || t > 0xFFFF)
> ++              return -EINVAL;
> ++
> ++      mpcore_margin = t;
> ++      return 0;
> ++}
> ++
> ++/*
> ++ *    /dev/watchdog handling
> ++ */
> ++static int mpcore_wdt_open(struct inode *inode, struct file *file)
> ++{
> ++      struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev);
> ++
> ++      if (test_and_set_bit(0, &wdt->timer_alive))
> ++              return -EBUSY;
> ++
> ++      if (nowayout)
> ++              __module_get(THIS_MODULE);
> ++
> ++      file->private_data = wdt;
> ++
> ++      /*
> ++       *      Activate timer
> ++       */
> ++      mpcore_wdt_start(wdt);
> ++
> ++      return nonseekable_open(inode, file);
> ++}
> ++
> ++static int mpcore_wdt_release(struct inode *inode, struct file *file)
> ++{
> ++      struct mpcore_wdt *wdt = file->private_data;
> ++
> ++      /*
> ++       *      Shut off the timer.
> ++       *      Lock it in if it's a module and we set nowayout
> ++       */
> ++      if (wdt->expect_close == 42)
> ++              mpcore_wdt_stop(wdt);
> ++      else {
> ++              dev_crit(wdt->dev,
> ++                       "unexpected close, not stopping watchdog!\n");
> ++              mpcore_wdt_keepalive(wdt);
> ++      }
> ++      clear_bit(0, &wdt->timer_alive);
> ++      wdt->expect_close = 0;
> ++      return 0;
> ++}
> ++
> ++static ssize_t mpcore_wdt_write(struct file *file, const char *data,
> ++                                              size_t len, loff_t *ppos)
> ++{
> ++      struct mpcore_wdt *wdt = file->private_data;
> ++
> ++      /*
> ++       *      Refresh the timer.
> ++       */
> ++      if (len) {
> ++              if (!nowayout) {
> ++                      size_t i;
> ++
> ++                      /* In case it was set long ago */
> ++                      wdt->expect_close = 0;
> ++
> ++                      for (i = 0; i != len; i++) {
> ++                              char c;
> ++
> ++                              if (get_user(c, data + i))
> ++                                      return -EFAULT;
> ++                              if (c == 'V')
> ++                                      wdt->expect_close = 42;
> ++                      }
> ++              }
> ++              mpcore_wdt_keepalive(wdt);
> ++      }
> ++      return len;
> ++}
> ++
> ++static const struct watchdog_info ident = {
> ++      .options                = WDIOF_SETTIMEOUT |
> ++                                WDIOF_KEEPALIVEPING |
> ++                                WDIOF_MAGICCLOSE,
> ++      .identity               = "MPcore Watchdog",
> ++};
> ++
> ++static long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
> ++                                                      unsigned long arg)
> ++{
> ++      struct mpcore_wdt *wdt = file->private_data;
> ++      int ret;
> ++      union {
> ++              struct watchdog_info ident;
> ++              int i;
> ++      } uarg;
> ++
> ++      if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
> ++              return -ENOTTY;
> ++
> ++      if (_IOC_DIR(cmd) & _IOC_WRITE) {
> ++              ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
> ++              if (ret)
> ++                      return -EFAULT;
> ++      }
> ++
> ++      switch (cmd) {
> ++      case WDIOC_GETSUPPORT:
> ++              uarg.ident = ident;
> ++              ret = 0;
> ++              break;
> ++
> ++      case WDIOC_GETSTATUS:
> ++      case WDIOC_GETBOOTSTATUS:
> ++              uarg.i = 0;
> ++              ret = 0;
> ++              break;
> ++
> ++      case WDIOC_SETOPTIONS:
> ++              ret = -EINVAL;
> ++              if (uarg.i & WDIOS_DISABLECARD) {
> ++                      mpcore_wdt_stop(wdt);
> ++                      ret = 0;
> ++              }
> ++              if (uarg.i & WDIOS_ENABLECARD) {
> ++                      mpcore_wdt_start(wdt);
> ++                      ret = 0;
> ++              }
> ++              break;
> ++
> ++      case WDIOC_KEEPALIVE:
> ++              mpcore_wdt_keepalive(wdt);
> ++              ret = 0;
> ++              break;
> ++
> ++      case WDIOC_SETTIMEOUT:
> ++              ret = mpcore_wdt_set_heartbeat(uarg.i);
> ++              if (ret)
> ++                      break;
> ++
> ++              mpcore_wdt_keepalive(wdt);
> ++              /* Fall */
> ++      case WDIOC_GETTIMEOUT:
> ++              uarg.i = mpcore_margin;
> ++              ret = 0;
> ++              break;
> ++
> ++      default:
> ++              return -ENOTTY;
> ++      }
> ++
> ++      if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
> ++              ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
> ++              if (ret)
> ++                      ret = -EFAULT;
> ++      }
> ++      return ret;
> ++}
> ++
> ++/*
> ++ *    System shutdown handler.  Turn off the watchdog if we're
> ++ *    restarting or halting the system.
> ++ */
> ++static void mpcore_wdt_shutdown(struct platform_device *pdev)
> ++{
> ++      struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
> ++
> ++      if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
> ++              mpcore_wdt_stop(wdt);
> ++}
> ++
> ++/*
> ++ *    Kernel Interfaces
> ++ */
> ++static const struct file_operations mpcore_wdt_fops = {
> ++      .owner          = THIS_MODULE,
> ++      .llseek         = no_llseek,
> ++      .write          = mpcore_wdt_write,
> ++      .unlocked_ioctl = mpcore_wdt_ioctl,
> ++      .open           = mpcore_wdt_open,
> ++      .release        = mpcore_wdt_release,
> ++};
> ++
> ++static struct miscdevice mpcore_wdt_miscdev = {
> ++      .minor          = WATCHDOG_MINOR,
> ++      .name           = "watchdog",
> ++      .fops           = &mpcore_wdt_fops,
> ++};
> ++
> ++static int mpcore_wdt_probe(struct platform_device *pdev)
> ++{
> ++      struct mpcore_wdt *wdt;
> ++      struct resource *res;
> ++      int ret;
> ++
> ++      /* We only accept one device, and it must have an id of -1 */
> ++      if (pdev->id != -1)
> ++              return -ENODEV;
> ++
> ++      res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> ++      if (!res)
> ++              return -ENODEV;
> ++
> ++      wdt = devm_kzalloc(&pdev->dev, sizeof(struct mpcore_wdt), GFP_KERNEL);
> ++      if (!wdt)
> ++              return -ENOMEM;
> ++
> ++      wdt->dev = &pdev->dev;
> ++      wdt->irq = platform_get_irq(pdev, 0);
> ++      if (wdt->irq >= 0) {
> ++              ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0,
> ++                              "mpcore_wdt", wdt);
> ++              if (ret) {
> ++                      dev_err(wdt->dev,
> ++                              "cannot register IRQ%d for watchdog\n",
> ++                              wdt->irq);
> ++                      return ret;
> ++              }
> ++      }
> ++
> ++      wdt->base = devm_ioremap(wdt->dev, res->start, resource_size(res));
> ++      if (!wdt->base)
> ++              return -ENOMEM;
> ++
> ++      mpcore_wdt_miscdev.parent = &pdev->dev;
> ++      ret = misc_register(&mpcore_wdt_miscdev);
> ++      if (ret) {
> ++              dev_err(wdt->dev,
> ++                      "cannot register miscdev on minor=%d (err=%d)\n",
> ++                      WATCHDOG_MINOR, ret);
> ++              return ret;
> ++      }
> ++
> ++      mpcore_wdt_stop(wdt);
> ++      platform_set_drvdata(pdev, wdt);
> ++      mpcore_wdt_pdev = pdev;
> ++
> ++      return 0;
> ++}
> ++
> ++static int mpcore_wdt_remove(struct platform_device *pdev)
> ++{
> ++      misc_deregister(&mpcore_wdt_miscdev);
> ++
> ++      mpcore_wdt_pdev = NULL;
> ++
> ++      return 0;
> ++}
> ++
> ++#ifdef CONFIG_PM
> ++static int mpcore_wdt_suspend(struct platform_device *pdev, pm_message_t msg)
> ++{
> ++      struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
> ++      mpcore_wdt_stop(wdt);           /* Turn the WDT off */
> ++      return 0;
> ++}
> ++
> ++static int mpcore_wdt_resume(struct platform_device *pdev)
> ++{
> ++      struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
> ++      /* re-activate timer */
> ++      if (test_bit(0, &wdt->timer_alive))
> ++              mpcore_wdt_start(wdt);
> ++      return 0;
> ++}
> ++#else
> ++#define mpcore_wdt_suspend    NULL
> ++#define mpcore_wdt_resume     NULL
> ++#endif
> ++
> ++/* work with hotplug and coldplug */
> ++MODULE_ALIAS("platform:mpcore_wdt");
> ++
> ++static struct platform_driver mpcore_wdt_driver = {
> ++      .probe          = mpcore_wdt_probe,
> ++      .remove         = mpcore_wdt_remove,
> ++      .suspend        = mpcore_wdt_suspend,
> ++      .resume         = mpcore_wdt_resume,
> ++      .shutdown       = mpcore_wdt_shutdown,
> ++      .driver         = {
> ++              .owner  = THIS_MODULE,
> ++              .name   = "mpcore_wdt",
> ++      },
> ++};
> ++
> ++static int __init mpcore_wdt_init(void)
> ++{
> ++      /*
> ++       * Check that the margin value is within it's range;
> ++       * if not reset to the default
> ++       */
> ++      if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
> ++              mpcore_wdt_set_heartbeat(TIMER_MARGIN);
> ++              pr_info("mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
> ++                      TIMER_MARGIN);
> ++      }
> ++
> ++      pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n",
> ++              mpcore_noboot, mpcore_margin, nowayout);
> ++
> ++      return platform_driver_register(&mpcore_wdt_driver);
> ++}
> ++
> ++static void __exit mpcore_wdt_exit(void)
> ++{
> ++      platform_driver_unregister(&mpcore_wdt_driver);
> ++}
> ++
> ++module_init(mpcore_wdt_init);
> ++module_exit(mpcore_wdt_exit);
> ++
> ++MODULE_AUTHOR("ARM Limited");
> ++MODULE_DESCRIPTION("MPcore Watchdog Device Driver");
> ++MODULE_LICENSE("GPL");
> ++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);
> --
> 2.7.4

Acked-by: Tim Harvey <tharvey@gateworks.com>

Note that watchdog will not work without additional patches, but as
its disabled it doesn't hurt anything. I will follow-up with
additional patches following this being accepted

Tim
Koen Vandeputte Feb. 1, 2017, 4:34 p.m. UTC | #4
Hi Felix,

> Note that watchdog will not work without additional patches, but as
> its disabled it doesn't hurt anything. I will follow-up with
> additional patches following this being accepted
>
> Tim

How would you like to proceed on this one?
Should Tim first submit his additional patches for review?


(Noticing the big changes in your staging tree, I fully understand if 
this one has currently low priority for you atm. :-) )


Thanks,

Koen
diff mbox

Patch

diff --git a/target/linux/cns3xxx/config-4.4 b/target/linux/cns3xxx/config-4.4
index 4745f4e..ffc1f02 100644
--- a/target/linux/cns3xxx/config-4.4
+++ b/target/linux/cns3xxx/config-4.4
@@ -182,6 +182,7 @@  CONFIG_MMC_SDHCI_CNS3XXX=y
 CONFIG_MMC_SDHCI_PLTFM=y
 # CONFIG_MMC_TIFM_SD is not set
 CONFIG_MODULES_USE_ELF_REL=y
+# CONFIG_MPCORE_WATCHDOG is not set
 CONFIG_MTD_M25P80=y
 # CONFIG_MTD_OF_PARTS is not set
 CONFIG_MTD_PHYSMAP=y
diff --git a/target/linux/cns3xxx/patches-4.4/300-restore-mpcore-watchdog.patch b/target/linux/cns3xxx/patches-4.4/300-restore-mpcore-watchdog.patch
new file mode 100644
index 0000000..156dcaa
--- /dev/null
+++ b/target/linux/cns3xxx/patches-4.4/300-restore-mpcore-watchdog.patch
@@ -0,0 +1,502 @@ 
+--- a/Documentation/watchdog/watchdog-parameters.txt
++++ b/Documentation/watchdog/watchdog-parameters.txt
+@@ -196,6 +196,14 @@ reset: Watchdog Interrupt/Reset Mode. 0
+ nowayout: Watchdog cannot be stopped once started
+ 	(default=kernel config parameter)
+ -------------------------------------------------
++mpcore_wdt:
++mpcore_margin: MPcore timer margin in seconds.
++	(0 < mpcore_margin < 65536, default=60)
++nowayout: Watchdog cannot be stopped once started
++	(default=kernel config parameter)
++mpcore_noboot: MPcore watchdog action, set to 1 to ignore reboots,
++	0 to reboot (default=0
++--------------------------------------------------
+ mv64x60_wdt:
+ nowayout: Watchdog cannot be stopped once started
+ 	(default=kernel config parameter)
+--- a/drivers/watchdog/Kconfig
++++ b/drivers/watchdog/Kconfig
+@@ -297,6 +297,15 @@ config DW_WATCHDOG
+ 	  To compile this driver as a module, choose M here: the
+ 	  module will be called dw_wdt.
+ 
++config MPCORE_WATCHDOG
++	tristate "MPcore watchdog"
++	depends on HAVE_ARM_TWD
++	help
++	  Watchdog timer embedded into the MPcore system.
++
++	  To compile this driver as a module, choose M here: the
++	  module will be called mpcore_wdt.
++
+ config EP93XX_WATCHDOG
+ 	tristate "EP93xx Watchdog"
+ 	depends on ARCH_EP93XX
+--- a/drivers/watchdog/Makefile
++++ b/drivers/watchdog/Makefile
+@@ -43,6 +43,7 @@ obj-$(CONFIG_S3C2410_WATCHDOG) += s3c241
+ obj-$(CONFIG_SA1100_WATCHDOG) += sa1100_wdt.o
+ obj-$(CONFIG_SAMA5D4_WATCHDOG) += sama5d4_wdt.o
+ obj-$(CONFIG_DW_WATCHDOG) += dw_wdt.o
++obj-$(CONFIG_MPCORE_WATCHDOG) += mpcore_wdt.o
+ obj-$(CONFIG_EP93XX_WATCHDOG) += ep93xx_wdt.o
+ obj-$(CONFIG_PNX4008_WATCHDOG) += pnx4008_wdt.o
+ obj-$(CONFIG_IOP_WATCHDOG) += iop_wdt.o
+--- /dev/null
++++ b/drivers/watchdog/mpcore_wdt.c
+@@ -0,0 +1,454 @@
++/*
++ *	Watchdog driver for the mpcore watchdog timer
++ *
++ *	(c) Copyright 2004 ARM Limited
++ *
++ *	Based on the SoftDog driver:
++ *	(c) Copyright 1996 Alan Cox <alan@lxorguk.ukuu.org.uk>,
++ *						All Rights Reserved.
++ *
++ *	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 Alan Cox nor CymruNet Ltd. admit liability nor provide
++ *	warranty for any of this software. This material is provided
++ *	"AS-IS" and at no charge.
++ *
++ *	(c) Copyright 1995    Alan Cox <alan@lxorguk.ukuu.org.uk>
++ *
++ */
++
++#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
++
++#include <linux/module.h>
++#include <linux/moduleparam.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/interrupt.h>
++#include <linux/platform_device.h>
++#include <linux/uaccess.h>
++#include <linux/slab.h>
++#include <linux/io.h>
++
++#include <asm/smp_twd.h>
++
++struct mpcore_wdt {
++	unsigned long	timer_alive;
++	struct device	*dev;
++	void __iomem	*base;
++	int		irq;
++	unsigned int	perturb;
++	char		expect_close;
++};
++
++static struct platform_device *mpcore_wdt_pdev;
++static DEFINE_SPINLOCK(wdt_lock);
++
++#define TIMER_MARGIN	60
++static int mpcore_margin = TIMER_MARGIN;
++module_param(mpcore_margin, int, 0);
++MODULE_PARM_DESC(mpcore_margin,
++	"MPcore timer margin in seconds. (0 < mpcore_margin < 65536, default="
++				__MODULE_STRING(TIMER_MARGIN) ")");
++
++static bool nowayout = WATCHDOG_NOWAYOUT;
++module_param(nowayout, bool, 0);
++MODULE_PARM_DESC(nowayout,
++	"Watchdog cannot be stopped once started (default="
++				__MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
++
++#define ONLY_TESTING	0
++static int mpcore_noboot = ONLY_TESTING;
++module_param(mpcore_noboot, int, 0);
++MODULE_PARM_DESC(mpcore_noboot, "MPcore watchdog action, "
++	"set to 1 to ignore reboots, 0 to reboot (default="
++					__MODULE_STRING(ONLY_TESTING) ")");
++
++/*
++ *	This is the interrupt handler.  Note that we only use this
++ *	in testing mode, so don't actually do a reboot here.
++ */
++static irqreturn_t mpcore_wdt_fire(int irq, void *arg)
++{
++	struct mpcore_wdt *wdt = arg;
++
++	/* Check it really was our interrupt */
++	if (readl(wdt->base + TWD_WDOG_INTSTAT)) {
++		dev_crit(wdt->dev, "Triggered - Reboot ignored\n");
++		/* Clear the interrupt on the watchdog */
++		writel(1, wdt->base + TWD_WDOG_INTSTAT);
++		return IRQ_HANDLED;
++	}
++	return IRQ_NONE;
++}
++
++/*
++ *	mpcore_wdt_keepalive - reload the timer
++ *
++ *	Note that the spec says a DIFFERENT value must be written to the reload
++ *	register each time.  The "perturb" variable deals with this by adding 1
++ *	to the count every other time the function is called.
++ */
++static void mpcore_wdt_keepalive(struct mpcore_wdt *wdt)
++{
++	unsigned long count;
++
++	spin_lock(&wdt_lock);
++	/* Assume prescale is set to 256 */
++	count =  __raw_readl(wdt->base + TWD_WDOG_COUNTER);
++	count = (0xFFFFFFFFU - count) * (HZ / 5);
++	count = (count / 256) * mpcore_margin;
++
++	/* Reload the counter */
++	writel(count + wdt->perturb, wdt->base + TWD_WDOG_LOAD);
++	wdt->perturb = wdt->perturb ? 0 : 1;
++	spin_unlock(&wdt_lock);
++}
++
++static void mpcore_wdt_stop(struct mpcore_wdt *wdt)
++{
++	spin_lock(&wdt_lock);
++	writel(0x12345678, wdt->base + TWD_WDOG_DISABLE);
++	writel(0x87654321, wdt->base + TWD_WDOG_DISABLE);
++	writel(0x0, wdt->base + TWD_WDOG_CONTROL);
++	spin_unlock(&wdt_lock);
++}
++
++static void mpcore_wdt_start(struct mpcore_wdt *wdt)
++{
++	dev_info(wdt->dev, "enabling watchdog\n");
++
++	/* This loads the count register but does NOT start the count yet */
++	mpcore_wdt_keepalive(wdt);
++
++	if (mpcore_noboot) {
++		/* Enable watchdog - prescale=256, watchdog mode=0, enable=1 */
++		writel(0x0000FF01, wdt->base + TWD_WDOG_CONTROL);
++	} else {
++		/* Enable watchdog - prescale=256, watchdog mode=1, enable=1 */
++		writel(0x0000FF09, wdt->base + TWD_WDOG_CONTROL);
++	}
++}
++
++static int mpcore_wdt_set_heartbeat(int t)
++{
++	if (t < 0x0001 || t > 0xFFFF)
++		return -EINVAL;
++
++	mpcore_margin = t;
++	return 0;
++}
++
++/*
++ *	/dev/watchdog handling
++ */
++static int mpcore_wdt_open(struct inode *inode, struct file *file)
++{
++	struct mpcore_wdt *wdt = platform_get_drvdata(mpcore_wdt_pdev);
++
++	if (test_and_set_bit(0, &wdt->timer_alive))
++		return -EBUSY;
++
++	if (nowayout)
++		__module_get(THIS_MODULE);
++
++	file->private_data = wdt;
++
++	/*
++	 *	Activate timer
++	 */
++	mpcore_wdt_start(wdt);
++
++	return nonseekable_open(inode, file);
++}
++
++static int mpcore_wdt_release(struct inode *inode, struct file *file)
++{
++	struct mpcore_wdt *wdt = file->private_data;
++
++	/*
++	 *	Shut off the timer.
++	 *	Lock it in if it's a module and we set nowayout
++	 */
++	if (wdt->expect_close == 42)
++		mpcore_wdt_stop(wdt);
++	else {
++		dev_crit(wdt->dev,
++			 "unexpected close, not stopping watchdog!\n");
++		mpcore_wdt_keepalive(wdt);
++	}
++	clear_bit(0, &wdt->timer_alive);
++	wdt->expect_close = 0;
++	return 0;
++}
++
++static ssize_t mpcore_wdt_write(struct file *file, const char *data,
++						size_t len, loff_t *ppos)
++{
++	struct mpcore_wdt *wdt = file->private_data;
++
++	/*
++	 *	Refresh the timer.
++	 */
++	if (len) {
++		if (!nowayout) {
++			size_t i;
++
++			/* In case it was set long ago */
++			wdt->expect_close = 0;
++
++			for (i = 0; i != len; i++) {
++				char c;
++
++				if (get_user(c, data + i))
++					return -EFAULT;
++				if (c == 'V')
++					wdt->expect_close = 42;
++			}
++		}
++		mpcore_wdt_keepalive(wdt);
++	}
++	return len;
++}
++
++static const struct watchdog_info ident = {
++	.options		= WDIOF_SETTIMEOUT |
++				  WDIOF_KEEPALIVEPING |
++				  WDIOF_MAGICCLOSE,
++	.identity		= "MPcore Watchdog",
++};
++
++static long mpcore_wdt_ioctl(struct file *file, unsigned int cmd,
++							unsigned long arg)
++{
++	struct mpcore_wdt *wdt = file->private_data;
++	int ret;
++	union {
++		struct watchdog_info ident;
++		int i;
++	} uarg;
++
++	if (_IOC_DIR(cmd) && _IOC_SIZE(cmd) > sizeof(uarg))
++		return -ENOTTY;
++
++	if (_IOC_DIR(cmd) & _IOC_WRITE) {
++		ret = copy_from_user(&uarg, (void __user *)arg, _IOC_SIZE(cmd));
++		if (ret)
++			return -EFAULT;
++	}
++
++	switch (cmd) {
++	case WDIOC_GETSUPPORT:
++		uarg.ident = ident;
++		ret = 0;
++		break;
++
++	case WDIOC_GETSTATUS:
++	case WDIOC_GETBOOTSTATUS:
++		uarg.i = 0;
++		ret = 0;
++		break;
++
++	case WDIOC_SETOPTIONS:
++		ret = -EINVAL;
++		if (uarg.i & WDIOS_DISABLECARD) {
++			mpcore_wdt_stop(wdt);
++			ret = 0;
++		}
++		if (uarg.i & WDIOS_ENABLECARD) {
++			mpcore_wdt_start(wdt);
++			ret = 0;
++		}
++		break;
++
++	case WDIOC_KEEPALIVE:
++		mpcore_wdt_keepalive(wdt);
++		ret = 0;
++		break;
++
++	case WDIOC_SETTIMEOUT:
++		ret = mpcore_wdt_set_heartbeat(uarg.i);
++		if (ret)
++			break;
++
++		mpcore_wdt_keepalive(wdt);
++		/* Fall */
++	case WDIOC_GETTIMEOUT:
++		uarg.i = mpcore_margin;
++		ret = 0;
++		break;
++
++	default:
++		return -ENOTTY;
++	}
++
++	if (ret == 0 && _IOC_DIR(cmd) & _IOC_READ) {
++		ret = copy_to_user((void __user *)arg, &uarg, _IOC_SIZE(cmd));
++		if (ret)
++			ret = -EFAULT;
++	}
++	return ret;
++}
++
++/*
++ *	System shutdown handler.  Turn off the watchdog if we're
++ *	restarting or halting the system.
++ */
++static void mpcore_wdt_shutdown(struct platform_device *pdev)
++{
++	struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
++
++	if (system_state == SYSTEM_RESTART || system_state == SYSTEM_HALT)
++		mpcore_wdt_stop(wdt);
++}
++
++/*
++ *	Kernel Interfaces
++ */
++static const struct file_operations mpcore_wdt_fops = {
++	.owner		= THIS_MODULE,
++	.llseek		= no_llseek,
++	.write		= mpcore_wdt_write,
++	.unlocked_ioctl	= mpcore_wdt_ioctl,
++	.open		= mpcore_wdt_open,
++	.release	= mpcore_wdt_release,
++};
++
++static struct miscdevice mpcore_wdt_miscdev = {
++	.minor		= WATCHDOG_MINOR,
++	.name		= "watchdog",
++	.fops		= &mpcore_wdt_fops,
++};
++
++static int mpcore_wdt_probe(struct platform_device *pdev)
++{
++	struct mpcore_wdt *wdt;
++	struct resource *res;
++	int ret;
++
++	/* We only accept one device, and it must have an id of -1 */
++	if (pdev->id != -1)
++		return -ENODEV;
++
++	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++	if (!res)
++		return -ENODEV;
++
++	wdt = devm_kzalloc(&pdev->dev, sizeof(struct mpcore_wdt), GFP_KERNEL);
++	if (!wdt)
++		return -ENOMEM;
++
++	wdt->dev = &pdev->dev;
++	wdt->irq = platform_get_irq(pdev, 0);
++	if (wdt->irq >= 0) {
++		ret = devm_request_irq(wdt->dev, wdt->irq, mpcore_wdt_fire, 0,
++				"mpcore_wdt", wdt);
++		if (ret) {
++			dev_err(wdt->dev,
++				"cannot register IRQ%d for watchdog\n",
++				wdt->irq);
++			return ret;
++		}
++	}
++
++	wdt->base = devm_ioremap(wdt->dev, res->start, resource_size(res));
++	if (!wdt->base)
++		return -ENOMEM;
++
++	mpcore_wdt_miscdev.parent = &pdev->dev;
++	ret = misc_register(&mpcore_wdt_miscdev);
++	if (ret) {
++		dev_err(wdt->dev,
++			"cannot register miscdev on minor=%d (err=%d)\n",
++			WATCHDOG_MINOR, ret);
++		return ret;
++	}
++
++	mpcore_wdt_stop(wdt);
++	platform_set_drvdata(pdev, wdt);
++	mpcore_wdt_pdev = pdev;
++
++	return 0;
++}
++
++static int mpcore_wdt_remove(struct platform_device *pdev)
++{
++	misc_deregister(&mpcore_wdt_miscdev);
++
++	mpcore_wdt_pdev = NULL;
++
++	return 0;
++}
++
++#ifdef CONFIG_PM
++static int mpcore_wdt_suspend(struct platform_device *pdev, pm_message_t msg)
++{
++	struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
++	mpcore_wdt_stop(wdt);		/* Turn the WDT off */
++	return 0;
++}
++
++static int mpcore_wdt_resume(struct platform_device *pdev)
++{
++	struct mpcore_wdt *wdt = platform_get_drvdata(pdev);
++	/* re-activate timer */
++	if (test_bit(0, &wdt->timer_alive))
++		mpcore_wdt_start(wdt);
++	return 0;
++}
++#else
++#define mpcore_wdt_suspend	NULL
++#define mpcore_wdt_resume	NULL
++#endif
++
++/* work with hotplug and coldplug */
++MODULE_ALIAS("platform:mpcore_wdt");
++
++static struct platform_driver mpcore_wdt_driver = {
++	.probe		= mpcore_wdt_probe,
++	.remove		= mpcore_wdt_remove,
++	.suspend	= mpcore_wdt_suspend,
++	.resume		= mpcore_wdt_resume,
++	.shutdown	= mpcore_wdt_shutdown,
++	.driver		= {
++		.owner	= THIS_MODULE,
++		.name	= "mpcore_wdt",
++	},
++};
++
++static int __init mpcore_wdt_init(void)
++{
++	/*
++	 * Check that the margin value is within it's range;
++	 * if not reset to the default
++	 */
++	if (mpcore_wdt_set_heartbeat(mpcore_margin)) {
++		mpcore_wdt_set_heartbeat(TIMER_MARGIN);
++		pr_info("mpcore_margin value must be 0 < mpcore_margin < 65536, using %d\n",
++			TIMER_MARGIN);
++	}
++
++	pr_info("MPcore Watchdog Timer: 0.1. mpcore_noboot=%d mpcore_margin=%d sec (nowayout= %d)\n",
++		mpcore_noboot, mpcore_margin, nowayout);
++
++	return platform_driver_register(&mpcore_wdt_driver);
++}
++
++static void __exit mpcore_wdt_exit(void)
++{
++	platform_driver_unregister(&mpcore_wdt_driver);
++}
++
++module_init(mpcore_wdt_init);
++module_exit(mpcore_wdt_exit);
++
++MODULE_AUTHOR("ARM Limited");
++MODULE_DESCRIPTION("MPcore Watchdog Device Driver");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);