Message ID | 1484055297-11313-1-git-send-email-koen.vandeputte@ncentric.com |
---|---|
State | Superseded |
Headers | show |
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
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
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
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 --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);
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