linux/drivers/watchdog/rt2880_wdt.c
<<
>>
Prefs
   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/moduleparam.h>
  20#include <linux/platform_device.h>
  21
  22#include <asm/mach-ralink/ralink_regs.h>
  23
  24#define SYSC_RSTSTAT                    0x38
  25#define WDT_RST_CAUSE                   BIT(1)
  26
  27#define RALINK_WDT_TIMEOUT              30
  28#define RALINK_WDT_PRESCALE             65536
  29
  30#define TIMER_REG_TMR1LOAD              0x00
  31#define TIMER_REG_TMR1CTL               0x08
  32
  33#define TMRSTAT_TMR1RST                 BIT(5)
  34
  35#define TMR1CTL_ENABLE                  BIT(7)
  36#define TMR1CTL_MODE_SHIFT              4
  37#define TMR1CTL_MODE_MASK               0x3
  38#define TMR1CTL_MODE_FREE_RUNNING       0x0
  39#define TMR1CTL_MODE_PERIODIC           0x1
  40#define TMR1CTL_MODE_TIMEOUT            0x2
  41#define TMR1CTL_MODE_WDT                0x3
  42#define TMR1CTL_PRESCALE_MASK           0xf
  43#define TMR1CTL_PRESCALE_65536          0xf
  44
  45static struct clk *rt288x_wdt_clk;
  46static unsigned long rt288x_wdt_freq;
  47static void __iomem *rt288x_wdt_base;
  48static struct reset_control *rt288x_wdt_reset;
  49
  50static bool nowayout = WATCHDOG_NOWAYOUT;
  51module_param(nowayout, bool, 0);
  52MODULE_PARM_DESC(nowayout,
  53                "Watchdog cannot be stopped once started (default="
  54                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  55
  56static inline void rt_wdt_w32(unsigned reg, u32 val)
  57{
  58        iowrite32(val, rt288x_wdt_base + reg);
  59}
  60
  61static inline u32 rt_wdt_r32(unsigned reg)
  62{
  63        return ioread32(rt288x_wdt_base + reg);
  64}
  65
  66static 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
  73static 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
  93static 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
 106static 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
 114static 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
 122static struct watchdog_info rt288x_wdt_info = {
 123        .identity = "Ralink Watchdog",
 124        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 125};
 126
 127static 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
 135static struct watchdog_device rt288x_wdt_dev = {
 136        .info = &rt288x_wdt_info,
 137        .ops = &rt288x_wdt_ops,
 138        .min_timeout = 1,
 139};
 140
 141static 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_ioremap_resource(&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        rt288x_wdt_reset = devm_reset_control_get(&pdev->dev, NULL);
 156        if (!IS_ERR(rt288x_wdt_reset))
 157                reset_control_deassert(rt288x_wdt_reset);
 158
 159        rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE;
 160
 161        rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
 162        rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
 163        rt288x_wdt_dev.parent = &pdev->dev;
 164
 165        watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout,
 166                              &pdev->dev);
 167        watchdog_set_nowayout(&rt288x_wdt_dev, nowayout);
 168
 169        ret = watchdog_register_device(&rt288x_wdt_dev);
 170        if (!ret)
 171                dev_info(&pdev->dev, "Initialized\n");
 172
 173        return 0;
 174}
 175
 176static int rt288x_wdt_remove(struct platform_device *pdev)
 177{
 178        watchdog_unregister_device(&rt288x_wdt_dev);
 179
 180        return 0;
 181}
 182
 183static void rt288x_wdt_shutdown(struct platform_device *pdev)
 184{
 185        rt288x_wdt_stop(&rt288x_wdt_dev);
 186}
 187
 188static const struct of_device_id rt288x_wdt_match[] = {
 189        { .compatible = "ralink,rt2880-wdt" },
 190        {},
 191};
 192MODULE_DEVICE_TABLE(of, rt288x_wdt_match);
 193
 194static struct platform_driver rt288x_wdt_driver = {
 195        .probe          = rt288x_wdt_probe,
 196        .remove         = rt288x_wdt_remove,
 197        .shutdown       = rt288x_wdt_shutdown,
 198        .driver         = {
 199                .name           = KBUILD_MODNAME,
 200                .of_match_table = rt288x_wdt_match,
 201        },
 202};
 203
 204module_platform_driver(rt288x_wdt_driver);
 205
 206MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver");
 207MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
 208MODULE_LICENSE("GPL v2");
 209