1*d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2473cf939SJohn Crispin /* 3473cf939SJohn Crispin * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer 4473cf939SJohn Crispin * 5473cf939SJohn Crispin * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org> 6f3519a66SJohn Crispin * Copyright (C) 2013 John Crispin <john@phrozen.org> 7473cf939SJohn Crispin * 8473cf939SJohn Crispin * This driver was based on: drivers/watchdog/softdog.c 9473cf939SJohn Crispin */ 10473cf939SJohn Crispin 11473cf939SJohn Crispin #include <linux/clk.h> 12473cf939SJohn Crispin #include <linux/reset.h> 13473cf939SJohn Crispin #include <linux/module.h> 14473cf939SJohn Crispin #include <linux/kernel.h> 15473cf939SJohn Crispin #include <linux/watchdog.h> 16473cf939SJohn Crispin #include <linux/moduleparam.h> 17473cf939SJohn Crispin #include <linux/platform_device.h> 183aa8b8bbSNeilBrown #include <linux/mod_devicetable.h> 19473cf939SJohn Crispin 20473cf939SJohn Crispin #include <asm/mach-ralink/ralink_regs.h> 21473cf939SJohn Crispin 22473cf939SJohn Crispin #define SYSC_RSTSTAT 0x38 23473cf939SJohn Crispin #define WDT_RST_CAUSE BIT(1) 24473cf939SJohn Crispin 25473cf939SJohn Crispin #define RALINK_WDT_TIMEOUT 30 26473cf939SJohn Crispin #define RALINK_WDT_PRESCALE 65536 27473cf939SJohn Crispin 28473cf939SJohn Crispin #define TIMER_REG_TMR1LOAD 0x00 29473cf939SJohn Crispin #define TIMER_REG_TMR1CTL 0x08 30473cf939SJohn Crispin 31473cf939SJohn Crispin #define TMRSTAT_TMR1RST BIT(5) 32473cf939SJohn Crispin 33473cf939SJohn Crispin #define TMR1CTL_ENABLE BIT(7) 34473cf939SJohn Crispin #define TMR1CTL_MODE_SHIFT 4 35473cf939SJohn Crispin #define TMR1CTL_MODE_MASK 0x3 36473cf939SJohn Crispin #define TMR1CTL_MODE_FREE_RUNNING 0x0 37473cf939SJohn Crispin #define TMR1CTL_MODE_PERIODIC 0x1 38473cf939SJohn Crispin #define TMR1CTL_MODE_TIMEOUT 0x2 39473cf939SJohn Crispin #define TMR1CTL_MODE_WDT 0x3 40473cf939SJohn Crispin #define TMR1CTL_PRESCALE_MASK 0xf 41473cf939SJohn Crispin #define TMR1CTL_PRESCALE_65536 0xf 42473cf939SJohn Crispin 43473cf939SJohn Crispin static struct clk *rt288x_wdt_clk; 44473cf939SJohn Crispin static unsigned long rt288x_wdt_freq; 45473cf939SJohn Crispin static void __iomem *rt288x_wdt_base; 46a6f8f81eSJohn Crispin static struct reset_control *rt288x_wdt_reset; 47473cf939SJohn Crispin 48473cf939SJohn Crispin static bool nowayout = WATCHDOG_NOWAYOUT; 49473cf939SJohn Crispin module_param(nowayout, bool, 0); 50473cf939SJohn Crispin MODULE_PARM_DESC(nowayout, 51473cf939SJohn Crispin "Watchdog cannot be stopped once started (default=" 52473cf939SJohn Crispin __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 53473cf939SJohn Crispin 54473cf939SJohn Crispin static inline void rt_wdt_w32(unsigned reg, u32 val) 55473cf939SJohn Crispin { 56473cf939SJohn Crispin iowrite32(val, rt288x_wdt_base + reg); 57473cf939SJohn Crispin } 58473cf939SJohn Crispin 59473cf939SJohn Crispin static inline u32 rt_wdt_r32(unsigned reg) 60473cf939SJohn Crispin { 61473cf939SJohn Crispin return ioread32(rt288x_wdt_base + reg); 62473cf939SJohn Crispin } 63473cf939SJohn Crispin 64473cf939SJohn Crispin static int rt288x_wdt_ping(struct watchdog_device *w) 65473cf939SJohn Crispin { 66473cf939SJohn Crispin rt_wdt_w32(TIMER_REG_TMR1LOAD, w->timeout * rt288x_wdt_freq); 67473cf939SJohn Crispin 68473cf939SJohn Crispin return 0; 69473cf939SJohn Crispin } 70473cf939SJohn Crispin 71473cf939SJohn Crispin static int rt288x_wdt_start(struct watchdog_device *w) 72473cf939SJohn Crispin { 73473cf939SJohn Crispin u32 t; 74473cf939SJohn Crispin 75473cf939SJohn Crispin t = rt_wdt_r32(TIMER_REG_TMR1CTL); 76473cf939SJohn Crispin t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT | 77473cf939SJohn Crispin TMR1CTL_PRESCALE_MASK); 78473cf939SJohn Crispin t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT | 79473cf939SJohn Crispin TMR1CTL_PRESCALE_65536); 80473cf939SJohn Crispin rt_wdt_w32(TIMER_REG_TMR1CTL, t); 81473cf939SJohn Crispin 82473cf939SJohn Crispin rt288x_wdt_ping(w); 83473cf939SJohn Crispin 84473cf939SJohn Crispin t = rt_wdt_r32(TIMER_REG_TMR1CTL); 85473cf939SJohn Crispin t |= TMR1CTL_ENABLE; 86473cf939SJohn Crispin rt_wdt_w32(TIMER_REG_TMR1CTL, t); 87473cf939SJohn Crispin 88473cf939SJohn Crispin return 0; 89473cf939SJohn Crispin } 90473cf939SJohn Crispin 91473cf939SJohn Crispin static int rt288x_wdt_stop(struct watchdog_device *w) 92473cf939SJohn Crispin { 93473cf939SJohn Crispin u32 t; 94473cf939SJohn Crispin 95473cf939SJohn Crispin rt288x_wdt_ping(w); 96473cf939SJohn Crispin 97473cf939SJohn Crispin t = rt_wdt_r32(TIMER_REG_TMR1CTL); 98473cf939SJohn Crispin t &= ~TMR1CTL_ENABLE; 99473cf939SJohn Crispin rt_wdt_w32(TIMER_REG_TMR1CTL, t); 100473cf939SJohn Crispin 101473cf939SJohn Crispin return 0; 102473cf939SJohn Crispin } 103473cf939SJohn Crispin 104473cf939SJohn Crispin static int rt288x_wdt_set_timeout(struct watchdog_device *w, unsigned int t) 105473cf939SJohn Crispin { 106473cf939SJohn Crispin w->timeout = t; 107473cf939SJohn Crispin rt288x_wdt_ping(w); 108473cf939SJohn Crispin 109473cf939SJohn Crispin return 0; 110473cf939SJohn Crispin } 111473cf939SJohn Crispin 112473cf939SJohn Crispin static int rt288x_wdt_bootcause(void) 113473cf939SJohn Crispin { 114473cf939SJohn Crispin if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE) 115473cf939SJohn Crispin return WDIOF_CARDRESET; 116473cf939SJohn Crispin 117473cf939SJohn Crispin return 0; 118473cf939SJohn Crispin } 119473cf939SJohn Crispin 120323edb2eSJulia Lawall static const struct watchdog_info rt288x_wdt_info = { 121473cf939SJohn Crispin .identity = "Ralink Watchdog", 122473cf939SJohn Crispin .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 123473cf939SJohn Crispin }; 124473cf939SJohn Crispin 125b893e344SBhumika Goyal static const struct watchdog_ops rt288x_wdt_ops = { 126473cf939SJohn Crispin .owner = THIS_MODULE, 127473cf939SJohn Crispin .start = rt288x_wdt_start, 128473cf939SJohn Crispin .stop = rt288x_wdt_stop, 129473cf939SJohn Crispin .ping = rt288x_wdt_ping, 130473cf939SJohn Crispin .set_timeout = rt288x_wdt_set_timeout, 131473cf939SJohn Crispin }; 132473cf939SJohn Crispin 133473cf939SJohn Crispin static struct watchdog_device rt288x_wdt_dev = { 134473cf939SJohn Crispin .info = &rt288x_wdt_info, 135473cf939SJohn Crispin .ops = &rt288x_wdt_ops, 136473cf939SJohn Crispin .min_timeout = 1, 137473cf939SJohn Crispin }; 138473cf939SJohn Crispin 139473cf939SJohn Crispin static int rt288x_wdt_probe(struct platform_device *pdev) 140473cf939SJohn Crispin { 141570927dfSGuenter Roeck struct device *dev = &pdev->dev; 142473cf939SJohn Crispin int ret; 143473cf939SJohn Crispin 1440f0a6a28SGuenter Roeck rt288x_wdt_base = devm_platform_ioremap_resource(pdev, 0); 145473cf939SJohn Crispin if (IS_ERR(rt288x_wdt_base)) 146473cf939SJohn Crispin return PTR_ERR(rt288x_wdt_base); 147473cf939SJohn Crispin 148570927dfSGuenter Roeck rt288x_wdt_clk = devm_clk_get(dev, NULL); 149473cf939SJohn Crispin if (IS_ERR(rt288x_wdt_clk)) 150473cf939SJohn Crispin return PTR_ERR(rt288x_wdt_clk); 151473cf939SJohn Crispin 152570927dfSGuenter Roeck rt288x_wdt_reset = devm_reset_control_get_exclusive(dev, NULL); 153a6f8f81eSJohn Crispin if (!IS_ERR(rt288x_wdt_reset)) 154a6f8f81eSJohn Crispin reset_control_deassert(rt288x_wdt_reset); 155473cf939SJohn Crispin 156473cf939SJohn Crispin rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE; 157473cf939SJohn Crispin 158473cf939SJohn Crispin rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); 159473cf939SJohn Crispin rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); 160570927dfSGuenter Roeck rt288x_wdt_dev.parent = dev; 161473cf939SJohn Crispin 162a6f8f81eSJohn Crispin watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout, 163570927dfSGuenter Roeck dev); 164473cf939SJohn Crispin watchdog_set_nowayout(&rt288x_wdt_dev, nowayout); 165473cf939SJohn Crispin 166570927dfSGuenter Roeck watchdog_stop_on_reboot(&rt288x_wdt_dev); 167570927dfSGuenter Roeck ret = devm_watchdog_register_device(dev, &rt288x_wdt_dev); 168473cf939SJohn Crispin if (!ret) 169570927dfSGuenter Roeck dev_info(dev, "Initialized\n"); 170473cf939SJohn Crispin 171473cf939SJohn Crispin return 0; 172473cf939SJohn Crispin } 173473cf939SJohn Crispin 174473cf939SJohn Crispin static const struct of_device_id rt288x_wdt_match[] = { 175473cf939SJohn Crispin { .compatible = "ralink,rt2880-wdt" }, 176473cf939SJohn Crispin {}, 177473cf939SJohn Crispin }; 178473cf939SJohn Crispin MODULE_DEVICE_TABLE(of, rt288x_wdt_match); 179473cf939SJohn Crispin 180473cf939SJohn Crispin static struct platform_driver rt288x_wdt_driver = { 181473cf939SJohn Crispin .probe = rt288x_wdt_probe, 182473cf939SJohn Crispin .driver = { 183473cf939SJohn Crispin .name = KBUILD_MODNAME, 184473cf939SJohn Crispin .of_match_table = rt288x_wdt_match, 185473cf939SJohn Crispin }, 186473cf939SJohn Crispin }; 187473cf939SJohn Crispin 188473cf939SJohn Crispin module_platform_driver(rt288x_wdt_driver); 189473cf939SJohn Crispin 190473cf939SJohn Crispin MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver"); 191473cf939SJohn Crispin MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org"); 192473cf939SJohn Crispin MODULE_LICENSE("GPL v2"); 193