[LEDE-DEV] [PATCH] cns3xxx: restore mpcore watchdog

Tim Harvey tharvey at gateworks.com
Wed Jan 25 08:56:45 PST 2017


On Tue, Jan 10, 2017 at 5:34 AM, Koen Vandeputte
<koen.vandeputte at 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 at 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 at 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 at 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 at 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



More information about the Lede-dev mailing list