linux/drivers/watchdog/rt2880_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer
   4 *
   5 * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org>
   6 * Copyright (C) 2013 John Crispin <john@phrozen.org>
   7 *
   8 * This driver was based on: drivers/watchdog/softdog.c
   9 */
  10
  11#include <linux/clk.h>
  12#include <linux/reset.h>
  13#include <linux/module.h>
  14#include <linux/kernel.h>
  15#include <linux/watchdog.h>
  16#include <linux/moduleparam.h>
  17#include <linux/platform_device.h>
  18#include <linux/mod_devicetable.h>
  19
  20#include <asm/mach-ralink/ralink_regs.h>
  21
  22#define SYSC_RSTSTAT                    0x38
  23#define WDT_RST_CAUSE                   BIT(1)
  24
  25#define RALINK_WDT_TIMEOUT              30
  26#define RALINK_WDT_PRESCALE             65536
  27
  28#define TIMER_REG_TMR1LOAD              0x00
  29#define TIMER_REG_TMR1CTL               0x08
  30
  31#define TMRSTAT_TMR1RST                 BIT(5)
  32
  33#define TMR1CTL_ENABLE                  BIT(7)
  34#define TMR1CTL_MODE_SHIFT              4
  35#define TMR1CTL_MODE_MASK               0x3
  36#define TMR1CTL_MODE_FREE_RUNNING       0x0
  37#define TMR1CTL_MODE_PERIODIC           0x1
  38#define TMR1CTL_MODE_TIMEOUT            0x2
  39#define TMR1CTL_MODE_WDT                0x3
  40#define TMR1CTL_PRESCALE_MASK           0xf
  41#define TMR1CTL_PRESCALE_65536          0xf
  42
  43static struct clk *rt288x_wdt_clk;
  44static unsigned long rt288x_wdt_freq;
  45static void __iomem *rt288x_wdt_base;
  46static struct reset_control *rt288x_wdt_reset;
  47
  48static bool nowayout = WATCHDOG_NOWAYOUT;
  49module_param(nowayout, bool, 0);
  50MODULE_PARM_DESC(nowayout,
  51                "Watchdog cannot be stopped once started (default="
  52                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  53
  54static inline void rt_wdt_w32(unsigned reg, u32 val)
  55{
  56        iowrite32(val, rt288x_wdt_base + reg);
  57}
  58
  59static inline u32 rt_wdt_r32(unsigned reg)
  60{
  61        return ioread32(rt288x_wdt_base + reg);
  62}
  63
  64static int rt288x_wdt_ping(struct watchdog_device *w)
  65{
  66        rt_wdt_w32(TIMER_REG_TMR1LOAD, w->timeout * rt288x_wdt_freq);
  67
  68        return 0;
  69}
  70
  71static int rt288x_wdt_start(struct watchdog_device *w)
  72{
  73        u32 t;
  74
  75        t = rt_wdt_r32(TIMER_REG_TMR1CTL);
  76        t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT |
  77                TMR1CTL_PRESCALE_MASK);
  78        t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT |
  79                TMR1CTL_PRESCALE_65536);
  80        rt_wdt_w32(TIMER_REG_TMR1CTL, t);
  81
  82        rt288x_wdt_ping(w);
  83
  84        t = rt_wdt_r32(TIMER_REG_TMR1CTL);
  85        t |= TMR1CTL_ENABLE;
  86        rt_wdt_w32(TIMER_REG_TMR1CTL, t);
  87
  88        return 0;
  89}
  90
  91static int rt288x_wdt_stop(struct watchdog_device *w)
  92{
  93        u32 t;
  94
  95        rt288x_wdt_ping(w);
  96
  97        t = rt_wdt_r32(TIMER_REG_TMR1CTL);
  98        t &= ~TMR1CTL_ENABLE;
  99        rt_wdt_w32(TIMER_REG_TMR1CTL, t);
 100
 101        return 0;
 102}
 103
 104static int rt288x_wdt_set_timeout(struct watchdog_device *w, unsigned int t)
 105{
 106        w->timeout = t;
 107        rt288x_wdt_ping(w);
 108
 109        return 0;
 110}
 111
 112static int rt288x_wdt_bootcause(void)
 113{
 114        if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE)
 115                return WDIOF_CARDRESET;
 116
 117        return 0;
 118}
 119
 120static const struct watchdog_info rt288x_wdt_info = {
 121        .identity = "Ralink Watchdog",
 122        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 123};
 124
 125static const struct watchdog_ops rt288x_wdt_ops = {
 126        .owner = THIS_MODULE,
 127        .start = rt288x_wdt_start,
 128        .stop = rt288x_wdt_stop,
 129        .ping = rt288x_wdt_ping,
 130        .set_timeout = rt288x_wdt_set_timeout,
 131};
 132
 133static struct watchdog_device rt288x_wdt_dev = {
 134        .info = &rt288x_wdt_info,
 135        .ops = &rt288x_wdt_ops,
 136        .min_timeout = 1,
 137};
 138
 139static int rt288x_wdt_probe(struct platform_device *pdev)
 140{
 141        struct device *dev = &pdev->dev;
 142        int ret;
 143
 144        rt288x_wdt_base = devm_platform_ioremap_resource(pdev, 0);
 145        if (IS_ERR(rt288x_wdt_base))
 146                return PTR_ERR(rt288x_wdt_base);
 147
 148        rt288x_wdt_clk = devm_clk_get(dev, NULL);
 149        if (IS_ERR(rt288x_wdt_clk))
 150                return PTR_ERR(rt288x_wdt_clk);
 151
 152        rt288x_wdt_reset = devm_reset_control_get_exclusive(dev, NULL);
 153        if (!IS_ERR(rt288x_wdt_reset))
 154                reset_control_deassert(rt288x_wdt_reset);
 155
 156        rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE;
 157
 158        rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause();
 159        rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq);
 160        rt288x_wdt_dev.parent = dev;
 161
 162        watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout,
 163                              dev);
 164        watchdog_set_nowayout(&rt288x_wdt_dev, nowayout);
 165
 166        watchdog_stop_on_reboot(&rt288x_wdt_dev);
 167        ret = devm_watchdog_register_device(dev, &rt288x_wdt_dev);
 168        if (!ret)
 169                dev_info(dev, "Initialized\n");
 170
 171        return 0;
 172}
 173
 174static const struct of_device_id rt288x_wdt_match[] = {
 175        { .compatible = "ralink,rt2880-wdt" },
 176        {},
 177};
 178MODULE_DEVICE_TABLE(of, rt288x_wdt_match);
 179
 180static struct platform_driver rt288x_wdt_driver = {
 181        .probe          = rt288x_wdt_probe,
 182        .driver         = {
 183                .name           = KBUILD_MODNAME,
 184                .of_match_table = rt288x_wdt_match,
 185        },
 186};
 187
 188module_platform_driver(rt288x_wdt_driver);
 189
 190MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver");
 191MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org");
 192MODULE_LICENSE("GPL v2");
 193