Commit 473cf939ff3442dc86d531f3bb152a2f129ea9d1

Authored by John Crispin
Committed by Wim Van Sebroeck
1 parent e14538e0db

watchdog: add ralink watchdog driver

Add a driver for the watchdog timer found on Ralink SoC

Signed-off-by: John Crispin <blogic@openwrt.org>
Signed-off-by: Gabor Juhos <juhosg@openwrt.org>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>
Cc: linux-watchdog@vger.kernel.org
Cc: linux-mips@linux-mips.org
Cc: devicetree-discuss@lists.ozlabs.org

Showing 4 changed files with 235 additions and 0 deletions Side-by-side Diff

Documentation/devicetree/bindings/watchdog/rt2880-wdt.txt
  1 +Ralink Watchdog Timers
  2 +
  3 +Required properties:
  4 +- compatible: must be "ralink,rt2880-wdt"
  5 +- reg: physical base address of the controller and length of the register range
  6 +
  7 +Optional properties:
  8 +- interrupt-parent: phandle to the INTC device node
  9 +- interrupts: Specify the INTC interrupt number
  10 +
  11 +Example:
  12 +
  13 + watchdog@120 {
  14 + compatible = "ralink,rt2880-wdt";
  15 + reg = <0x120 0x10>;
  16 +
  17 + interrupt-parent = <&intc>;
  18 + interrupts = <1>;
  19 + };
drivers/watchdog/Kconfig
... ... @@ -1135,6 +1135,13 @@
1135 1135 help
1136 1136 Hardware driver for the Lantiq SoC Watchdog Timer.
1137 1137  
  1138 +config RALINK_WDT
  1139 + tristate "Ralink SoC watchdog"
  1140 + select WATCHDOG_CORE
  1141 + depends on RALINK
  1142 + help
  1143 + Hardware driver for the Ralink SoC Watchdog Timer.
  1144 +
1138 1145 # PARISC Architecture
1139 1146  
1140 1147 # POWERPC Architecture
drivers/watchdog/Makefile
... ... @@ -135,6 +135,7 @@
135 135 obj-$(CONFIG_OCTEON_WDT) += octeon-wdt.o
136 136 octeon-wdt-y := octeon-wdt-main.o octeon-wdt-nmi.o
137 137 obj-$(CONFIG_LANTIQ_WDT) += lantiq_wdt.o
  138 +obj-$(CONFIG_RALINK_WDT) += rt2880_wdt.o
138 139  
139 140 # PARISC Architecture
140 141  
drivers/watchdog/rt2880_wdt.c
  1 +/*
  2 + * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer
  3 + *
  4 + * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
  5 + * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
  6 + *
  7 + * This driver was based on: drivers/watchdog/softdog.c
  8 + *
  9 + * This program is free software; you can redistribute it and/or modify it
  10 + * under the terms of the GNU General Public License version 2 as published
  11 + * by the Free Software Foundation.
  12 + */
  13 +
  14 +#include <linux/clk.h>
  15 +#include <linux/reset.h>
  16 +#include <linux/module.h>
  17 +#include <linux/kernel.h>
  18 +#include <linux/watchdog.h>
  19 +#include <linux/miscdevice.h>
  20 +#include <linux/moduleparam.h>
  21 +#include <linux/platform_device.h>
  22 +
  23 +#include <asm/mach-ralink/ralink_regs.h>
  24 +
  25 +#define SYSC_RSTSTAT 0x38
  26 +#define WDT_RST_CAUSE BIT(1)
  27 +
  28 +#define RALINK_WDT_TIMEOUT 30
  29 +#define RALINK_WDT_PRESCALE 65536
  30 +
  31 +#define TIMER_REG_TMR1LOAD 0x00
  32 +#define TIMER_REG_TMR1CTL 0x08
  33 +
  34 +#define TMRSTAT_TMR1RST BIT(5)
  35 +
  36 +#define TMR1CTL_ENABLE BIT(7)
  37 +#define TMR1CTL_MODE_SHIFT 4
  38 +#define TMR1CTL_MODE_MASK 0x3
  39 +#define TMR1CTL_MODE_FREE_RUNNING 0x0
  40 +#define TMR1CTL_MODE_PERIODIC 0x1
  41 +#define TMR1CTL_MODE_TIMEOUT 0x2
  42 +#define TMR1CTL_MODE_WDT 0x3
  43 +#define TMR1CTL_PRESCALE_MASK 0xf
  44 +#define TMR1CTL_PRESCALE_65536 0xf
  45 +
  46 +static struct clk *rt288x_wdt_clk;
  47 +static unsigned long rt288x_wdt_freq;
  48 +static void __iomem *rt288x_wdt_base;
  49 +
  50 +static bool nowayout = WATCHDOG_NOWAYOUT;
  51 +module_param(nowayout, bool, 0);
  52 +MODULE_PARM_DESC(nowayout,
  53 + "Watchdog cannot be stopped once started (default="
  54 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  55 +
  56 +static inline void rt_wdt_w32(unsigned reg, u32 val)
  57 +{
  58 + iowrite32(val, rt288x_wdt_base + reg);
  59 +}
  60 +
  61 +static inline u32 rt_wdt_r32(unsigned reg)
  62 +{
  63 + return ioread32(rt288x_wdt_base + reg);
  64 +}
  65 +
  66 +static int rt288x_wdt_ping(struct watchdog_device *w)
  67 +{
  68 + rt_wdt_w32(TIMER_REG_TMR1LOAD, w->timeout * rt288x_wdt_freq);
  69 +
  70 + return 0;
  71 +}
  72 +
  73 +static int rt288x_wdt_start(struct watchdog_device *w)
  74 +{
  75 + u32 t;
  76 +
  77 + t = rt_wdt_r32(TIMER_REG_TMR1CTL);
  78 + t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT |
  79 + TMR1CTL_PRESCALE_MASK);
  80 + t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT |
  81 + TMR1CTL_PRESCALE_65536);
  82 + rt_wdt_w32(TIMER_REG_TMR1CTL, t);
  83 +
  84 + rt288x_wdt_ping(w);
  85 +
  86 + t = rt_wdt_r32(TIMER_REG_TMR1CTL);
  87 + t |= TMR1CTL_ENABLE;
  88 + rt_wdt_w32(TIMER_REG_TMR1CTL, t);
  89 +
  90 + return 0;
  91 +}
  92 +
  93 +static int rt288x_wdt_stop(struct watchdog_device *w)
  94 +{
  95 + u32 t;
  96 +
  97 + rt288x_wdt_ping(w);
  98 +
  99 + t = rt_wdt_r32(TIMER_REG_TMR1CTL);
  100 + t &= ~TMR1CTL_ENABLE;
  101 + rt_wdt_w32(TIMER_REG_TMR1CTL, t);
  102 +
  103 + return 0;
  104 +}
  105 +
  106 +static int rt288x_wdt_set_timeout(struct watchdog_device *w, unsigned int t)
  107 +{
  108 + w->timeout = t;
  109 + rt288x_wdt_ping(w);
  110 +
  111 + return 0;
  112 +}
  113 +
  114 +static int rt288x_wdt_bootcause(void)
  115 +{
  116 + if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE)
  117 + return WDIOF_CARDRESET;
  118 +
  119 + return 0;
  120 +}
  121 +
  122 +static struct watchdog_info rt288x_wdt_info = {
  123 + .identity = "Ralink Watchdog",
  124 + .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
  125 +};
  126 +
  127 +static struct watchdog_ops rt288x_wdt_ops = {
  128 + .owner = THIS_MODULE,
  129 + .start = rt288x_wdt_start,
  130 + .stop = rt288x_wdt_stop,
  131 + .ping = rt288x_wdt_ping,
  132 + .set_timeout = rt288x_wdt_set_timeout,
  133 +};
  134 +
  135 +static struct watchdog_device rt288x_wdt_dev = {
  136 + .info = &rt288x_wdt_info,
  137 + .ops = &rt288x_wdt_ops,
  138 + .min_timeout = 1,
  139 +};
  140 +
  141 +static int rt288x_wdt_probe(struct platform_device *pdev)
  142 +{
  143 + struct resource *res;
  144 + int ret;
  145 +
  146 + res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
  147 + rt288x_wdt_base = devm_request_and_ioremap(&pdev->dev, res);
  148 + if (IS_ERR(rt288x_wdt_base))
  149 + return PTR_ERR(rt288x_wdt_base);
  150 +
  151 + rt288x_wdt_clk = devm_clk_get(&pdev->dev, NULL);
  152 + if (IS_ERR(rt288x_wdt_clk))
  153 + return PTR_ERR(rt288x_wdt_clk);
  154 +
  155 + device_reset(&pdev->dev);
  156 +
  157 + rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE;
  158 +
  159 + rt288x_wdt_dev.dev = &pdev->dev;
  160 + rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
  161 +
  162 + rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
  163 + rt288x_wdt_dev.timeout = rt288x_wdt_dev.max_timeout;
  164 +
  165 + watchdog_set_nowayout(&rt288x_wdt_dev, nowayout);
  166 +
  167 + ret = watchdog_register_device(&rt288x_wdt_dev);
  168 + if (!ret)
  169 + dev_info(&pdev->dev, "Initialized\n");
  170 +
  171 + return 0;
  172 +}
  173 +
  174 +static int rt288x_wdt_remove(struct platform_device *pdev)
  175 +{
  176 + watchdog_unregister_device(&rt288x_wdt_dev);
  177 +
  178 + return 0;
  179 +}
  180 +
  181 +static void rt288x_wdt_shutdown(struct platform_device *pdev)
  182 +{
  183 + rt288x_wdt_stop(&rt288x_wdt_dev);
  184 +}
  185 +
  186 +static const struct of_device_id rt288x_wdt_match[] = {
  187 + { .compatible = "ralink,rt2880-wdt" },
  188 + {},
  189 +};
  190 +MODULE_DEVICE_TABLE(of, rt288x_wdt_match);
  191 +
  192 +static struct platform_driver rt288x_wdt_driver = {
  193 + .probe = rt288x_wdt_probe,
  194 + .remove = rt288x_wdt_remove,
  195 + .shutdown = rt288x_wdt_shutdown,
  196 + .driver = {
  197 + .name = KBUILD_MODNAME,
  198 + .owner = THIS_MODULE,
  199 + .of_match_table = rt288x_wdt_match,
  200 + },
  201 +};
  202 +
  203 +module_platform_driver(rt288x_wdt_driver);
  204 +
  205 +MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver");
  206 +MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
  207 +MODULE_LICENSE("GPL v2");
  208 +MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR);