1473cf939SJohn Crispin /* 2473cf939SJohn Crispin * Ralink RT288x/RT3xxx/MT76xx built-in hardware watchdog timer 3473cf939SJohn Crispin * 4473cf939SJohn Crispin * Copyright (C) 2011 Gabor Juhos <juhosg@openwrt.org> 5*f3519a66SJohn Crispin * Copyright (C) 2013 John Crispin <john@phrozen.org> 6473cf939SJohn Crispin * 7473cf939SJohn Crispin * This driver was based on: drivers/watchdog/softdog.c 8473cf939SJohn Crispin * 9473cf939SJohn Crispin * This program is free software; you can redistribute it and/or modify it 10473cf939SJohn Crispin * under the terms of the GNU General Public License version 2 as published 11473cf939SJohn Crispin * by the Free Software Foundation. 12473cf939SJohn Crispin */ 13473cf939SJohn Crispin 14473cf939SJohn Crispin #include <linux/clk.h> 15473cf939SJohn Crispin #include <linux/reset.h> 16473cf939SJohn Crispin #include <linux/module.h> 17473cf939SJohn Crispin #include <linux/kernel.h> 18473cf939SJohn Crispin #include <linux/watchdog.h> 19473cf939SJohn Crispin #include <linux/moduleparam.h> 20473cf939SJohn Crispin #include <linux/platform_device.h> 21473cf939SJohn Crispin 22473cf939SJohn Crispin #include <asm/mach-ralink/ralink_regs.h> 23473cf939SJohn Crispin 24473cf939SJohn Crispin #define SYSC_RSTSTAT 0x38 25473cf939SJohn Crispin #define WDT_RST_CAUSE BIT(1) 26473cf939SJohn Crispin 27473cf939SJohn Crispin #define RALINK_WDT_TIMEOUT 30 28473cf939SJohn Crispin #define RALINK_WDT_PRESCALE 65536 29473cf939SJohn Crispin 30473cf939SJohn Crispin #define TIMER_REG_TMR1LOAD 0x00 31473cf939SJohn Crispin #define TIMER_REG_TMR1CTL 0x08 32473cf939SJohn Crispin 33473cf939SJohn Crispin #define TMRSTAT_TMR1RST BIT(5) 34473cf939SJohn Crispin 35473cf939SJohn Crispin #define TMR1CTL_ENABLE BIT(7) 36473cf939SJohn Crispin #define TMR1CTL_MODE_SHIFT 4 37473cf939SJohn Crispin #define TMR1CTL_MODE_MASK 0x3 38473cf939SJohn Crispin #define TMR1CTL_MODE_FREE_RUNNING 0x0 39473cf939SJohn Crispin #define TMR1CTL_MODE_PERIODIC 0x1 40473cf939SJohn Crispin #define TMR1CTL_MODE_TIMEOUT 0x2 41473cf939SJohn Crispin #define TMR1CTL_MODE_WDT 0x3 42473cf939SJohn Crispin #define TMR1CTL_PRESCALE_MASK 0xf 43473cf939SJohn Crispin #define TMR1CTL_PRESCALE_65536 0xf 44473cf939SJohn Crispin 45473cf939SJohn Crispin static struct clk *rt288x_wdt_clk; 46473cf939SJohn Crispin static unsigned long rt288x_wdt_freq; 47473cf939SJohn Crispin static void __iomem *rt288x_wdt_base; 48a6f8f81eSJohn Crispin static struct reset_control *rt288x_wdt_reset; 49473cf939SJohn Crispin 50473cf939SJohn Crispin static bool nowayout = WATCHDOG_NOWAYOUT; 51473cf939SJohn Crispin module_param(nowayout, bool, 0); 52473cf939SJohn Crispin MODULE_PARM_DESC(nowayout, 53473cf939SJohn Crispin "Watchdog cannot be stopped once started (default=" 54473cf939SJohn Crispin __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); 55473cf939SJohn Crispin 56473cf939SJohn Crispin static inline void rt_wdt_w32(unsigned reg, u32 val) 57473cf939SJohn Crispin { 58473cf939SJohn Crispin iowrite32(val, rt288x_wdt_base + reg); 59473cf939SJohn Crispin } 60473cf939SJohn Crispin 61473cf939SJohn Crispin static inline u32 rt_wdt_r32(unsigned reg) 62473cf939SJohn Crispin { 63473cf939SJohn Crispin return ioread32(rt288x_wdt_base + reg); 64473cf939SJohn Crispin } 65473cf939SJohn Crispin 66473cf939SJohn Crispin static int rt288x_wdt_ping(struct watchdog_device *w) 67473cf939SJohn Crispin { 68473cf939SJohn Crispin rt_wdt_w32(TIMER_REG_TMR1LOAD, w->timeout * rt288x_wdt_freq); 69473cf939SJohn Crispin 70473cf939SJohn Crispin return 0; 71473cf939SJohn Crispin } 72473cf939SJohn Crispin 73473cf939SJohn Crispin static int rt288x_wdt_start(struct watchdog_device *w) 74473cf939SJohn Crispin { 75473cf939SJohn Crispin u32 t; 76473cf939SJohn Crispin 77473cf939SJohn Crispin t = rt_wdt_r32(TIMER_REG_TMR1CTL); 78473cf939SJohn Crispin t &= ~(TMR1CTL_MODE_MASK << TMR1CTL_MODE_SHIFT | 79473cf939SJohn Crispin TMR1CTL_PRESCALE_MASK); 80473cf939SJohn Crispin t |= (TMR1CTL_MODE_WDT << TMR1CTL_MODE_SHIFT | 81473cf939SJohn Crispin TMR1CTL_PRESCALE_65536); 82473cf939SJohn Crispin rt_wdt_w32(TIMER_REG_TMR1CTL, t); 83473cf939SJohn Crispin 84473cf939SJohn Crispin rt288x_wdt_ping(w); 85473cf939SJohn Crispin 86473cf939SJohn Crispin t = rt_wdt_r32(TIMER_REG_TMR1CTL); 87473cf939SJohn Crispin t |= TMR1CTL_ENABLE; 88473cf939SJohn Crispin rt_wdt_w32(TIMER_REG_TMR1CTL, t); 89473cf939SJohn Crispin 90473cf939SJohn Crispin return 0; 91473cf939SJohn Crispin } 92473cf939SJohn Crispin 93473cf939SJohn Crispin static int rt288x_wdt_stop(struct watchdog_device *w) 94473cf939SJohn Crispin { 95473cf939SJohn Crispin u32 t; 96473cf939SJohn Crispin 97473cf939SJohn Crispin rt288x_wdt_ping(w); 98473cf939SJohn Crispin 99473cf939SJohn Crispin t = rt_wdt_r32(TIMER_REG_TMR1CTL); 100473cf939SJohn Crispin t &= ~TMR1CTL_ENABLE; 101473cf939SJohn Crispin rt_wdt_w32(TIMER_REG_TMR1CTL, t); 102473cf939SJohn Crispin 103473cf939SJohn Crispin return 0; 104473cf939SJohn Crispin } 105473cf939SJohn Crispin 106473cf939SJohn Crispin static int rt288x_wdt_set_timeout(struct watchdog_device *w, unsigned int t) 107473cf939SJohn Crispin { 108473cf939SJohn Crispin w->timeout = t; 109473cf939SJohn Crispin rt288x_wdt_ping(w); 110473cf939SJohn Crispin 111473cf939SJohn Crispin return 0; 112473cf939SJohn Crispin } 113473cf939SJohn Crispin 114473cf939SJohn Crispin static int rt288x_wdt_bootcause(void) 115473cf939SJohn Crispin { 116473cf939SJohn Crispin if (rt_sysc_r32(SYSC_RSTSTAT) & WDT_RST_CAUSE) 117473cf939SJohn Crispin return WDIOF_CARDRESET; 118473cf939SJohn Crispin 119473cf939SJohn Crispin return 0; 120473cf939SJohn Crispin } 121473cf939SJohn Crispin 122473cf939SJohn Crispin static struct watchdog_info rt288x_wdt_info = { 123473cf939SJohn Crispin .identity = "Ralink Watchdog", 124473cf939SJohn Crispin .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE, 125473cf939SJohn Crispin }; 126473cf939SJohn Crispin 127473cf939SJohn Crispin static struct watchdog_ops rt288x_wdt_ops = { 128473cf939SJohn Crispin .owner = THIS_MODULE, 129473cf939SJohn Crispin .start = rt288x_wdt_start, 130473cf939SJohn Crispin .stop = rt288x_wdt_stop, 131473cf939SJohn Crispin .ping = rt288x_wdt_ping, 132473cf939SJohn Crispin .set_timeout = rt288x_wdt_set_timeout, 133473cf939SJohn Crispin }; 134473cf939SJohn Crispin 135473cf939SJohn Crispin static struct watchdog_device rt288x_wdt_dev = { 136473cf939SJohn Crispin .info = &rt288x_wdt_info, 137473cf939SJohn Crispin .ops = &rt288x_wdt_ops, 138473cf939SJohn Crispin .min_timeout = 1, 139473cf939SJohn Crispin }; 140473cf939SJohn Crispin 141473cf939SJohn Crispin static int rt288x_wdt_probe(struct platform_device *pdev) 142473cf939SJohn Crispin { 143473cf939SJohn Crispin struct resource *res; 144473cf939SJohn Crispin int ret; 145473cf939SJohn Crispin 146473cf939SJohn Crispin res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1470859ffc3SWei Yongjun rt288x_wdt_base = devm_ioremap_resource(&pdev->dev, res); 148473cf939SJohn Crispin if (IS_ERR(rt288x_wdt_base)) 149473cf939SJohn Crispin return PTR_ERR(rt288x_wdt_base); 150473cf939SJohn Crispin 151473cf939SJohn Crispin rt288x_wdt_clk = devm_clk_get(&pdev->dev, NULL); 152473cf939SJohn Crispin if (IS_ERR(rt288x_wdt_clk)) 153473cf939SJohn Crispin return PTR_ERR(rt288x_wdt_clk); 154473cf939SJohn Crispin 155a6f8f81eSJohn Crispin rt288x_wdt_reset = devm_reset_control_get(&pdev->dev, NULL); 156a6f8f81eSJohn Crispin if (!IS_ERR(rt288x_wdt_reset)) 157a6f8f81eSJohn Crispin reset_control_deassert(rt288x_wdt_reset); 158473cf939SJohn Crispin 159473cf939SJohn Crispin rt288x_wdt_freq = clk_get_rate(rt288x_wdt_clk) / RALINK_WDT_PRESCALE; 160473cf939SJohn Crispin 161473cf939SJohn Crispin rt288x_wdt_dev.bootstatus = rt288x_wdt_bootcause(); 162473cf939SJohn Crispin rt288x_wdt_dev.max_timeout = (0xfffful / rt288x_wdt_freq); 1636551881cSPratyush Anand rt288x_wdt_dev.parent = &pdev->dev; 164473cf939SJohn Crispin 165a6f8f81eSJohn Crispin watchdog_init_timeout(&rt288x_wdt_dev, rt288x_wdt_dev.max_timeout, 166a6f8f81eSJohn Crispin &pdev->dev); 167473cf939SJohn Crispin watchdog_set_nowayout(&rt288x_wdt_dev, nowayout); 168473cf939SJohn Crispin 169473cf939SJohn Crispin ret = watchdog_register_device(&rt288x_wdt_dev); 170473cf939SJohn Crispin if (!ret) 171473cf939SJohn Crispin dev_info(&pdev->dev, "Initialized\n"); 172473cf939SJohn Crispin 173473cf939SJohn Crispin return 0; 174473cf939SJohn Crispin } 175473cf939SJohn Crispin 176473cf939SJohn Crispin static int rt288x_wdt_remove(struct platform_device *pdev) 177473cf939SJohn Crispin { 178473cf939SJohn Crispin watchdog_unregister_device(&rt288x_wdt_dev); 179473cf939SJohn Crispin 180473cf939SJohn Crispin return 0; 181473cf939SJohn Crispin } 182473cf939SJohn Crispin 183473cf939SJohn Crispin static void rt288x_wdt_shutdown(struct platform_device *pdev) 184473cf939SJohn Crispin { 185473cf939SJohn Crispin rt288x_wdt_stop(&rt288x_wdt_dev); 186473cf939SJohn Crispin } 187473cf939SJohn Crispin 188473cf939SJohn Crispin static const struct of_device_id rt288x_wdt_match[] = { 189473cf939SJohn Crispin { .compatible = "ralink,rt2880-wdt" }, 190473cf939SJohn Crispin {}, 191473cf939SJohn Crispin }; 192473cf939SJohn Crispin MODULE_DEVICE_TABLE(of, rt288x_wdt_match); 193473cf939SJohn Crispin 194473cf939SJohn Crispin static struct platform_driver rt288x_wdt_driver = { 195473cf939SJohn Crispin .probe = rt288x_wdt_probe, 196473cf939SJohn Crispin .remove = rt288x_wdt_remove, 197473cf939SJohn Crispin .shutdown = rt288x_wdt_shutdown, 198473cf939SJohn Crispin .driver = { 199473cf939SJohn Crispin .name = KBUILD_MODNAME, 200473cf939SJohn Crispin .of_match_table = rt288x_wdt_match, 201473cf939SJohn Crispin }, 202473cf939SJohn Crispin }; 203473cf939SJohn Crispin 204473cf939SJohn Crispin module_platform_driver(rt288x_wdt_driver); 205473cf939SJohn Crispin 206473cf939SJohn Crispin MODULE_DESCRIPTION("MediaTek/Ralink RT288x/RT3xxx hardware watchdog driver"); 207473cf939SJohn Crispin MODULE_AUTHOR("Gabor Juhos <juhosg@openwrt.org"); 208473cf939SJohn Crispin MODULE_LICENSE("GPL v2"); 209