On Tue, Oct 20, 2015 at 09:49:10AM +0200, Oleksij Rempel wrote:
> Hallo all,
> 
> any feedback here? or it can be merged?
> 
>From my side just too much work and not enough time :-(

Guenter

> Am 18.09.2015 um 11:23 schrieb Oleksij Rempel:
> > Add WD support for Alphascale asm9260 SoC. This driver
> > provide support for different function modes:
> > - HW mode to trigger SoC reset on timeout
> > - SW mode do soft reset if needed
> > - DEBUG mode
> > 
> > Optional support for stopping watchdog. If reset binding are not provided
> > this driver will work in nowayout mode.
> > 
> > Signed-off-by: Oleksij Rempel <[email protected]>
> > ---
> >  .../bindings/watchdog/alphascale-asm9260.txt       |  38 ++
> >  drivers/watchdog/Kconfig                           |   9 +
> >  drivers/watchdog/Makefile                          |   1 +
> >  drivers/watchdog/asm9260_wdt.c                     | 405 
> > +++++++++++++++++++++
> >  4 files changed, 453 insertions(+)
> >  create mode 100644 
> > Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> >  create mode 100644 drivers/watchdog/asm9260_wdt.c
> > 
> > diff --git 
> > a/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt 
> > b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> > new file mode 100644
> > index 0000000..cbe388b
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/watchdog/alphascale-asm9260.txt
> > @@ -0,0 +1,38 @@
> > +Alphascale asm9260 Watchdog timer
> > +
> > +Required properties:
> > +
> > +- compatible : should be "alphascale,asm9260-wdt".
> > +- reg : Specifies base physical address and size of the registers.
> > +- clocks : the clock feeding the watchdog timer.
> > +   Needed if platform uses clocks. See clock-bindings.txt
> > +- clock-names : should be set to
> > +   "mod" - source for tick counter.
> > +   "ahb" - ahb gate.
> > +
> > +Optional properties:
> > +- resets : phandle pointing to the system reset controller with correct
> > +   reset line index for watchdog controller reset. This propertie is
> > +   required if you need to disable "nowayout" and it works only with
> > +   CONFIG_WATCHDOG_NOWAYOUT=n.
> > +- reset-names : should be set to "wdt_rst" if "resets" is used.
> > +- timeout-sec : shall contain the default watchdog timeout in seconds,
> > +   if unset, the default timeout is 30 seconds.
> > +- alphascale,mode : tree modes are supported
> > +   "hw" - hw reset (defaul).
> > +   "sw" - sw reset.
> > +   "debug" - no action is taken.
> > +
> > +Example:
> > +
> > +watchdog0: watchdog@80048000 {
> > +   compatible = "alphascale,asm9260-wdt";
> > +   reg = <0x80048000 0x10>;
> > +   clocks = <&acc CLKID_SYS_WDT>, <&acc CLKID_AHB_WDT>;
> > +   clock-names = "mod", "ahb";
> > +   interrupts = <55>;
> > +   resets = <&rst WDT_RESET>;
> > +   reset-names = "wdt_rst";
> > +   timeout-sec = <30>;
> > +   alphascale,mode = "hw"
> > +};
> > diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig
> > index c68edc1..cc5f675 100644
> > --- a/drivers/watchdog/Kconfig
> > +++ b/drivers/watchdog/Kconfig
> > @@ -173,6 +173,15 @@ config ARM_SP805_WATCHDOG
> >       ARM Primecell SP805 Watchdog timer. This will reboot your system when
> >       the timeout is reached.
> >  
> > +config ASM9260_WATCHDOG
> > +   tristate "Alphascale ASM9260 watchdog"
> > +   depends on MACH_ASM9260
> > +   depends on OF
> > +   select WATCHDOG_CORE
> > +   help
> > +     Watchdog timer embedded into Alphascale asm9260 chips. This will 
> > reboot your
> > +     system when the timeout is reached.
> > +
> >  config AT91RM9200_WATCHDOG
> >     tristate "AT91RM9200 watchdog"
> >     depends on SOC_AT91RM9200 && MFD_SYSCON
> > diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile
> > index 0c616e3..bd7b0cd 100644
> > --- a/drivers/watchdog/Makefile
> > +++ b/drivers/watchdog/Makefile
> > @@ -30,6 +30,7 @@ obj-$(CONFIG_USBPCWATCHDOG) += pcwd_usb.o
> >  
> >  # ARM Architecture
> >  obj-$(CONFIG_ARM_SP805_WATCHDOG) += sp805_wdt.o
> > +obj-$(CONFIG_ASM9260_WATCHDOG) += asm9260_wdt.o
> >  obj-$(CONFIG_AT91RM9200_WATCHDOG) += at91rm9200_wdt.o
> >  obj-$(CONFIG_AT91SAM9X_WATCHDOG) += at91sam9_wdt.o
> >  obj-$(CONFIG_CADENCE_WATCHDOG) += cadence_wdt.o
> > diff --git a/drivers/watchdog/asm9260_wdt.c b/drivers/watchdog/asm9260_wdt.c
> > new file mode 100644
> > index 0000000..e5f859b
> > --- /dev/null
> > +++ b/drivers/watchdog/asm9260_wdt.c
> > @@ -0,0 +1,405 @@
> > +/*
> > + * Watchdog driver for Alphascale ASM9260.
> > + *
> > + * Copyright (c) 2014 Oleksij Rempel <[email protected]>
> > + *
> > + * Licensed under GPLv2 or later.
> > + */
> > +
> > +#include <linux/delay.h>
> > +#include <linux/clk.h>
> > +#include <linux/interrupt.h>
> > +#include <linux/io.h>
> > +#include <linux/module.h>
> > +#include <linux/moduleparam.h>
> > +#include <linux/of.h>
> > +#include <linux/platform_device.h>
> > +#include <linux/reboot.h>
> > +#include <linux/reset.h>
> > +#include <linux/uaccess.h>
> > +#include <linux/watchdog.h>
> > +
> > +#define CLOCK_FREQ 1000000
> > +
> > +/* Watchdog Mode register */
> > +#define HW_WDMOD                   0x00
> > +/* Wake interrupt. Set by HW, can't be cleared. */
> > +#define BM_MOD_WDINT                       BIT(3)
> > +/* This bit set if timeout reached. Cleared by SW. */
> > +#define BM_MOD_WDTOF                       BIT(2)
> > +/* HW Reset on timeout */
> > +#define BM_MOD_WDRESET                     BIT(1)
> > +/* WD enable */
> > +#define BM_MOD_WDEN                        BIT(0)
> > +
> > +/*
> > + * Watchdog Timer Constant register
> > + * Minimal value is 0xff, the meaning of this value
> > + * depends on used clock: T = WDCLK * (0xff + 1) * 4
> > + */
> > +#define HW_WDTC                            0x04
> > +#define BM_WDTC_MAX(freq)          (0x7fffffff / (freq))
> > +
> > +/* Watchdog Feed register */
> > +#define HW_WDFEED                  0x08
> > +
> > +/* Watchdog Timer Value register */
> > +#define HW_WDTV                            0x0c
> > +
> > +#define ASM9260_WDT_DEFAULT_TIMEOUT        30
> > +
> > +enum asm9260_wdt_mode {
> > +   HW_RESET,
> > +   SW_RESET,
> > +   DEBUG,
> > +};
> > +
> > +struct asm9260_wdt_priv {
> > +   struct device           *dev;
> > +   struct watchdog_device  wdd;
> > +   struct clk              *clk;
> > +   struct clk              *clk_ahb;
> > +   struct reset_control    *rst;
> > +   struct notifier_block   restart_handler;
> > +
> > +   void __iomem            *iobase;
> > +   int                     irq;
> > +   unsigned long           wdt_freq;
> > +   enum asm9260_wdt_mode   mode;
> > +};
> > +
> > +static int asm9260_wdt_feed(struct watchdog_device *wdd)
> > +{
> > +   struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +
> > +   iowrite32(0xaa, priv->iobase + HW_WDFEED);
> > +   iowrite32(0x55, priv->iobase + HW_WDFEED);
> > +
> > +   return 0;
> > +}
> > +
> > +static unsigned int asm9260_wdt_gettimeleft(struct watchdog_device *wdd)
> > +{
> > +   struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +   u32 counter;
> > +
> > +   counter = ioread32(priv->iobase + HW_WDTV);
> > +
> > +   return DIV_ROUND_CLOSEST(counter, priv->wdt_freq);
> > +}
> > +
> > +static int asm9260_wdt_updatetimeout(struct watchdog_device *wdd)
> > +{
> > +   struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +   u32 counter;
> > +
> > +   counter = wdd->timeout * priv->wdt_freq;
> > +
> > +   iowrite32(counter, priv->iobase + HW_WDTC);
> > +
> > +   return 0;
> > +}
> > +
> > +static int asm9260_wdt_enable(struct watchdog_device *wdd)
> > +{
> > +   struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +   u32 mode = 0;
> > +
> > +   if (priv->mode == HW_RESET)
> > +           mode = BM_MOD_WDRESET;
> > +
> > +   iowrite32(BM_MOD_WDEN | mode, priv->iobase + HW_WDMOD);
> > +
> > +   asm9260_wdt_updatetimeout(wdd);
> > +
> > +   asm9260_wdt_feed(wdd);
> > +
> > +   return 0;
> > +}
> > +
> > +static int asm9260_wdt_disable(struct watchdog_device *wdd)
> > +{
> > +   struct asm9260_wdt_priv *priv = watchdog_get_drvdata(wdd);
> > +
> > +   /* The only way to disable WD is to reset it. */
> > +   reset_control_assert(priv->rst);
> > +   reset_control_deassert(priv->rst);
> > +
> > +   return 0;
> > +}
> > +
> > +static int asm9260_wdt_settimeout(struct watchdog_device *wdd, unsigned 
> > int to)
> > +{
> > +   wdd->timeout = to;
> > +   asm9260_wdt_updatetimeout(wdd);
> > +
> > +   return 0;
> > +}
> > +
> > +static void asm9260_wdt_sys_reset(struct asm9260_wdt_priv *priv)
> > +{
> > +   /* init WD if it was not started */
> > +   priv->wdd.timeout = 1;
> > +
> > +   iowrite32(BM_MOD_WDEN | BM_MOD_WDRESET, priv->iobase + HW_WDMOD);
> > +
> > +   asm9260_wdt_updatetimeout(&priv->wdd);
> > +   /* first pass correct sequence */
> > +   asm9260_wdt_feed(&priv->wdd);
> > +   /*
> > +    * Then write wrong pattern to the feed to trigger reset
> > +    * ASAP.
> > +    */
> > +   iowrite32(0xff, priv->iobase + HW_WDFEED);
> > +
> > +   mdelay(1000);
> > +}
> > +
> > +static irqreturn_t asm9260_wdt_irq(int irq, void *devid)
> > +{
> > +   struct asm9260_wdt_priv *priv = devid;
> > +   u32 stat;
> > +
> > +   stat = ioread32(priv->iobase + HW_WDMOD);
> > +   if (!(stat & BM_MOD_WDINT))
> > +           return IRQ_NONE;
> > +
> > +   /*
> > +    * BM_MOD_WDINT flag can't be removed by SW. Only way is
> > +    * to reset WD Controller.
> > +    * TODO: add Controller reset if needed.
> > +    */
> > +   if (priv->mode == DEBUG)
> > +           dev_info(priv->dev, "Watchdog Timeout. Do nothing.\n");
> > +   else {
> > +           dev_info(priv->dev, "Watchdog Timeout. Doing SW Reset.\n");
> > +           asm9260_wdt_sys_reset(priv);
> > +   }
> > +
> > +   return IRQ_HANDLED;
> > +}
> > +
> > +static int asm9260_restart_handler(struct notifier_block *this,
> > +                           unsigned long mode, void *cmd)
> > +{
> > +   struct asm9260_wdt_priv *priv =
> > +           container_of(this, struct asm9260_wdt_priv, restart_handler);
> > +
> > +   asm9260_wdt_sys_reset(priv);
> > +
> > +   return NOTIFY_DONE;
> > +}
> > +
> > +static const struct watchdog_info asm9260_wdt_ident = {
> > +   .options          =     WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING
> > +                           | WDIOF_MAGICCLOSE,
> > +   .firmware_version =     0,
> > +   .identity         =     "Alphascale asm9260 Watchdog",
> > +};
> > +
> > +static struct watchdog_ops asm9260_wdt_ops = {
> > +   .owner          = THIS_MODULE,
> > +   .start          = asm9260_wdt_enable,
> > +   .stop           = asm9260_wdt_disable,
> > +   .get_timeleft   = asm9260_wdt_gettimeleft,
> > +   .ping           = asm9260_wdt_feed,
> > +   .set_timeout    = asm9260_wdt_settimeout,
> > +};
> > +
> > +static int __init asm9260_wdt_get_dt_clks(struct asm9260_wdt_priv *priv)
> > +{
> > +   int clk_idx = 0, err;
> > +
> > +   priv->clk = devm_clk_get(priv->dev, "mod");
> > +   if (IS_ERR(priv->clk))
> > +           goto out_err;
> > +
> > +   /* configure AHB clock */
> > +   clk_idx = 1;
> > +   priv->clk_ahb = devm_clk_get(priv->dev, "ahb");
> > +   if (IS_ERR(priv->clk_ahb))
> > +           goto out_err;
> > +
> > +   err = clk_prepare_enable(priv->clk_ahb);
> > +   if (err)
> > +           dev_err(priv->dev, "Failed to enable ahb_clk!\n");
> > +
> > +   err = clk_set_rate(priv->clk, CLOCK_FREQ);
> > +   if (err) {
> > +           clk_disable_unprepare(priv->clk_ahb);
> > +           dev_err(priv->dev, "Failed to set rate!\n");
> > +   }
> > +
> > +   err = clk_prepare_enable(priv->clk);
> > +   if (err) {
> > +           clk_disable_unprepare(priv->clk_ahb);
> > +           dev_err(priv->dev, "Failed to enable clk!\n");
> > +   }
> > +
> > +   /* wdt has internal divider */
> > +   priv->wdt_freq = clk_get_rate(priv->clk) / 2;
> > +
> > +   return 0;
> > +out_err:
> > +   dev_err(priv->dev, "%s: Failed to get clk (%i)\n", __func__, clk_idx);
> > +   return 1;
> > +}
> > +
> > +static void __init asm9260_wdt_get_dt_mode(struct asm9260_wdt_priv *priv)
> > +{
> > +   const char *tmp;
> > +   int ret;
> > +
> > +   /* default mode */
> > +   priv->mode = HW_RESET;
> > +
> > +   ret = of_property_read_string(priv->dev->of_node,
> > +                   "alphascale,mode", &tmp);
> > +   if (ret)
> > +           return;
> > +
> > +   if (!strcmp(tmp, "hw"))
> > +           priv->mode = HW_RESET;
> > +   else if (!strcmp(tmp, "sw"))
> > +           priv->mode = SW_RESET;
> > +   else if (!strcmp(tmp, "debug"))
> > +           priv->mode = DEBUG;
> > +   else {
> > +           dev_warn(priv->dev, "unknown reset-type: %s. Using defaul 
> > \"hw\" mode.",
> > +                   tmp);
> > +           return;
> > +   }
> > +
> > +   dev_info(priv->dev, "using \"%s\" mode.", tmp);
> > +}
> > +
> > +static int __init asm9260_wdt_probe(struct platform_device *pdev)
> > +{
> > +   struct asm9260_wdt_priv *priv;
> > +   struct watchdog_device *wdd;
> > +   struct resource *res;
> > +   bool nowayout = WATCHDOG_NOWAYOUT;
> > +   int ret;
> > +
> > +   priv = devm_kzalloc(&pdev->dev, sizeof(struct asm9260_wdt_priv),
> > +                   GFP_KERNEL);
> > +   if (!priv)
> > +           return -ENOMEM;
> > +
> > +   priv->dev = &pdev->dev;
> > +
> > +   res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> > +   priv->iobase = devm_ioremap_resource(&pdev->dev, res);
> > +   if (IS_ERR(priv->iobase))
> > +           return PTR_ERR(priv->iobase);
> > +
> > +   ret = asm9260_wdt_get_dt_clks(priv);
> > +   if (ret)
> > +           return ret;
> > +
> > +   priv->rst = devm_reset_control_get(&pdev->dev, "wdt_rst");
> > +   if (IS_ERR(priv->rst)) {
> > +           dev_info(&pdev->dev, "No reset control found. Enabling 
> > \"nowayout\" mode\n");
> > +           nowayout = 1;
> > +           priv->rst = NULL;
> > +   }
> > +
> > +   wdd = &priv->wdd;
> > +   wdd->info = &asm9260_wdt_ident;
> > +   wdd->ops = &asm9260_wdt_ops;
> > +   wdd->min_timeout = 1;
> > +   wdd->max_timeout = BM_WDTC_MAX(priv->wdt_freq);
> > +   wdd->parent = &pdev->dev;
> > +
> > +   watchdog_set_drvdata(wdd, priv);
> > +
> > +   /*
> > +    * If 'timeout-sec' unspecified in devicetree, assume a 30 second
> > +    * default, unless the max timeout is less than 30 seconds, then use
> > +    * the max instead.
> > +    */
> > +   wdd->timeout = ASM9260_WDT_DEFAULT_TIMEOUT;
> > +   watchdog_init_timeout(wdd, 0, &pdev->dev);
> > +
> > +   watchdog_set_nowayout(wdd, nowayout);
> > +
> > +   asm9260_wdt_get_dt_mode(priv);
> > +
> > +   if (priv->mode != HW_RESET)
> > +           priv->irq = platform_get_irq(pdev, 0);
> > +
> > +   if (priv->irq > 0) {
> > +           /*
> > +            * Not all supported platforms specify an interrupt for the
> > +            * watchdog, so let's make it optional.
> > +            */
> > +           ret = devm_request_irq(&pdev->dev, priv->irq,
> > +                           asm9260_wdt_irq, IRQF_SHARED,
> > +                           pdev->name, priv);
> > +           if (ret < 0)
> > +                   dev_err(&pdev->dev, "failed to request IRQ\n");
> > +   }
> > +
> > +   ret = watchdog_register_device(wdd);
> > +   if (ret)
> > +           goto clk_off;
> > +
> > +   platform_set_drvdata(pdev, priv);
> > +
> > +   priv->restart_handler.notifier_call = asm9260_restart_handler;
> > +   priv->restart_handler.priority = 128;
> > +   ret = register_restart_handler(&priv->restart_handler);
> > +   if (ret)
> > +           dev_err(&pdev->dev, "cannot register restart handler\n");
> > +
> > +   dev_info(&pdev->dev, "Watchdog enabled (timeout=%d sec, nowayout=%d)",
> > +           wdd->timeout, nowayout);
> > +   return 0;
> > +
> > +clk_off:
> > +   clk_disable_unprepare(priv->clk);
> > +   clk_disable_unprepare(priv->clk_ahb);
> > +   return ret;
> > +}
> > +
> > +static void asm9260_wdt_shutdown(struct platform_device *pdev)
> > +{
> > +   struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> > +
> > +   asm9260_wdt_disable(&priv->wdd);
> > +}
> > +
> > +static int __exit asm9260_wdt_remove(struct platform_device *pdev)
> > +{
> > +   struct asm9260_wdt_priv *priv = platform_get_drvdata(pdev);
> > +
> > +   asm9260_wdt_shutdown(pdev);
> > +
> > +   unregister_restart_handler(&priv->restart_handler);
> > +   clk_disable_unprepare(priv->clk);
> > +   clk_disable_unprepare(priv->clk_ahb);
> > +
> > +   return 0;
> > +}
> > +
> > +static const struct of_device_id asm9260_wdt_of_match[] = {
> > +   { .compatible = "alphascale,asm9260-wdt"},
> > +   {},
> > +};
> > +MODULE_DEVICE_TABLE(of, asm9260_wdt_of_match);
> > +
> > +static struct platform_driver asm9260_wdt_driver = {
> > +   .driver = {
> > +           .name = "asm9260-wdt",
> > +           .owner = THIS_MODULE,
> > +           .of_match_table = asm9260_wdt_of_match,
> > +   },
> > +   .probe = asm9260_wdt_probe,
> > +   .remove = asm9260_wdt_remove,
> > +   .shutdown = asm9260_wdt_shutdown,
> > +};
> > +module_platform_driver(asm9260_wdt_driver);
> > +
> > +MODULE_DESCRIPTION("asm9260 WatchDog Timer Driver");
> > +MODULE_AUTHOR("Oleksij Rempel <[email protected]>");
> > +MODULE_LICENSE("GPL");
> > 
> 
> 
> -- 
> Regards,
> Oleksij
> 


--
To unsubscribe from this list: send the line "unsubscribe linux-watchdog" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to