On Tue, Jan 10, 2017 at 5:34 AM, Koen Vandeputte <koen.vandepu...@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.vandepu...@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 <a...@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 <a...@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 <thar...@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 _______________________________________________ Lede-dev mailing list Lede-dev@lists.infradead.org http://lists.infradead.org/mailman/listinfo/lede-dev