1*2d63908bSTero Kristo // SPDX-License-Identifier: GPL-2.0 2*2d63908bSTero Kristo /* 3*2d63908bSTero Kristo * Watchdog driver for the K3 RTI module 4*2d63908bSTero Kristo * 5*2d63908bSTero Kristo * (c) Copyright 2019-2020 Texas Instruments Inc. 6*2d63908bSTero Kristo * All rights reserved. 7*2d63908bSTero Kristo */ 8*2d63908bSTero Kristo 9*2d63908bSTero Kristo #include <linux/clk.h> 10*2d63908bSTero Kristo #include <linux/device.h> 11*2d63908bSTero Kristo #include <linux/err.h> 12*2d63908bSTero Kristo #include <linux/io.h> 13*2d63908bSTero Kristo #include <linux/kernel.h> 14*2d63908bSTero Kristo #include <linux/mod_devicetable.h> 15*2d63908bSTero Kristo #include <linux/module.h> 16*2d63908bSTero Kristo #include <linux/moduleparam.h> 17*2d63908bSTero Kristo #include <linux/platform_device.h> 18*2d63908bSTero Kristo #include <linux/pm_runtime.h> 19*2d63908bSTero Kristo #include <linux/types.h> 20*2d63908bSTero Kristo #include <linux/watchdog.h> 21*2d63908bSTero Kristo 22*2d63908bSTero Kristo #define DEFAULT_HEARTBEAT 60 23*2d63908bSTero Kristo 24*2d63908bSTero Kristo /* Max heartbeat is calculated at 32kHz source clock */ 25*2d63908bSTero Kristo #define MAX_HEARTBEAT 1000 26*2d63908bSTero Kristo 27*2d63908bSTero Kristo /* Timer register set definition */ 28*2d63908bSTero Kristo #define RTIDWDCTRL 0x90 29*2d63908bSTero Kristo #define RTIDWDPRLD 0x94 30*2d63908bSTero Kristo #define RTIWDSTATUS 0x98 31*2d63908bSTero Kristo #define RTIWDKEY 0x9c 32*2d63908bSTero Kristo #define RTIDWDCNTR 0xa0 33*2d63908bSTero Kristo #define RTIWWDRXCTRL 0xa4 34*2d63908bSTero Kristo #define RTIWWDSIZECTRL 0xa8 35*2d63908bSTero Kristo 36*2d63908bSTero Kristo #define RTIWWDRX_NMI 0xa 37*2d63908bSTero Kristo 38*2d63908bSTero Kristo #define RTIWWDSIZE_50P 0x50 39*2d63908bSTero Kristo 40*2d63908bSTero Kristo #define WDENABLE_KEY 0xa98559da 41*2d63908bSTero Kristo 42*2d63908bSTero Kristo #define WDKEY_SEQ0 0xe51a 43*2d63908bSTero Kristo #define WDKEY_SEQ1 0xa35c 44*2d63908bSTero Kristo 45*2d63908bSTero Kristo #define WDT_PRELOAD_SHIFT 13 46*2d63908bSTero Kristo 47*2d63908bSTero Kristo #define WDT_PRELOAD_MAX 0xfff 48*2d63908bSTero Kristo 49*2d63908bSTero Kristo #define DWDST BIT(1) 50*2d63908bSTero Kristo 51*2d63908bSTero Kristo static int heartbeat; 52*2d63908bSTero Kristo 53*2d63908bSTero Kristo /* 54*2d63908bSTero Kristo * struct to hold data for each WDT device 55*2d63908bSTero Kristo * @base - base io address of WD device 56*2d63908bSTero Kristo * @freq - source clock frequency of WDT 57*2d63908bSTero Kristo * @wdd - hold watchdog device as is in WDT core 58*2d63908bSTero Kristo */ 59*2d63908bSTero Kristo struct rti_wdt_device { 60*2d63908bSTero Kristo void __iomem *base; 61*2d63908bSTero Kristo unsigned long freq; 62*2d63908bSTero Kristo struct watchdog_device wdd; 63*2d63908bSTero Kristo }; 64*2d63908bSTero Kristo 65*2d63908bSTero Kristo static int rti_wdt_start(struct watchdog_device *wdd) 66*2d63908bSTero Kristo { 67*2d63908bSTero Kristo u32 timer_margin; 68*2d63908bSTero Kristo struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd); 69*2d63908bSTero Kristo 70*2d63908bSTero Kristo /* set timeout period */ 71*2d63908bSTero Kristo timer_margin = (u64)wdd->timeout * wdt->freq; 72*2d63908bSTero Kristo timer_margin >>= WDT_PRELOAD_SHIFT; 73*2d63908bSTero Kristo if (timer_margin > WDT_PRELOAD_MAX) 74*2d63908bSTero Kristo timer_margin = WDT_PRELOAD_MAX; 75*2d63908bSTero Kristo writel_relaxed(timer_margin, wdt->base + RTIDWDPRLD); 76*2d63908bSTero Kristo 77*2d63908bSTero Kristo /* 78*2d63908bSTero Kristo * RTI only supports a windowed mode, where the watchdog can only 79*2d63908bSTero Kristo * be petted during the open window; not too early or not too late. 80*2d63908bSTero Kristo * The HW configuration options only allow for the open window size 81*2d63908bSTero Kristo * to be 50% or less than that; we obviouly want to configure the open 82*2d63908bSTero Kristo * window as large as possible so we select the 50% option. To avoid 83*2d63908bSTero Kristo * any glitches, we accommodate 5% safety margin also, so we setup 84*2d63908bSTero Kristo * the min_hw_hearbeat at 55% of the timeout period. 85*2d63908bSTero Kristo */ 86*2d63908bSTero Kristo wdd->min_hw_heartbeat_ms = 11 * wdd->timeout * 1000 / 20; 87*2d63908bSTero Kristo 88*2d63908bSTero Kristo /* Generate NMI when wdt expires */ 89*2d63908bSTero Kristo writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL); 90*2d63908bSTero Kristo 91*2d63908bSTero Kristo /* Open window size 50%; this is the largest window size available */ 92*2d63908bSTero Kristo writel_relaxed(RTIWWDSIZE_50P, wdt->base + RTIWWDSIZECTRL); 93*2d63908bSTero Kristo 94*2d63908bSTero Kristo readl_relaxed(wdt->base + RTIWWDSIZECTRL); 95*2d63908bSTero Kristo 96*2d63908bSTero Kristo /* enable watchdog */ 97*2d63908bSTero Kristo writel_relaxed(WDENABLE_KEY, wdt->base + RTIDWDCTRL); 98*2d63908bSTero Kristo return 0; 99*2d63908bSTero Kristo } 100*2d63908bSTero Kristo 101*2d63908bSTero Kristo static int rti_wdt_ping(struct watchdog_device *wdd) 102*2d63908bSTero Kristo { 103*2d63908bSTero Kristo struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd); 104*2d63908bSTero Kristo 105*2d63908bSTero Kristo /* put watchdog in service state */ 106*2d63908bSTero Kristo writel_relaxed(WDKEY_SEQ0, wdt->base + RTIWDKEY); 107*2d63908bSTero Kristo /* put watchdog in active state */ 108*2d63908bSTero Kristo writel_relaxed(WDKEY_SEQ1, wdt->base + RTIWDKEY); 109*2d63908bSTero Kristo 110*2d63908bSTero Kristo return 0; 111*2d63908bSTero Kristo } 112*2d63908bSTero Kristo 113*2d63908bSTero Kristo static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd) 114*2d63908bSTero Kristo { 115*2d63908bSTero Kristo u64 timer_counter; 116*2d63908bSTero Kristo u32 val; 117*2d63908bSTero Kristo struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd); 118*2d63908bSTero Kristo 119*2d63908bSTero Kristo /* if timeout has occurred then return 0 */ 120*2d63908bSTero Kristo val = readl_relaxed(wdt->base + RTIWDSTATUS); 121*2d63908bSTero Kristo if (val & DWDST) 122*2d63908bSTero Kristo return 0; 123*2d63908bSTero Kristo 124*2d63908bSTero Kristo timer_counter = readl_relaxed(wdt->base + RTIDWDCNTR); 125*2d63908bSTero Kristo 126*2d63908bSTero Kristo do_div(timer_counter, wdt->freq); 127*2d63908bSTero Kristo 128*2d63908bSTero Kristo return timer_counter; 129*2d63908bSTero Kristo } 130*2d63908bSTero Kristo 131*2d63908bSTero Kristo static const struct watchdog_info rti_wdt_info = { 132*2d63908bSTero Kristo .options = WDIOF_KEEPALIVEPING, 133*2d63908bSTero Kristo .identity = "K3 RTI Watchdog", 134*2d63908bSTero Kristo }; 135*2d63908bSTero Kristo 136*2d63908bSTero Kristo static const struct watchdog_ops rti_wdt_ops = { 137*2d63908bSTero Kristo .owner = THIS_MODULE, 138*2d63908bSTero Kristo .start = rti_wdt_start, 139*2d63908bSTero Kristo .ping = rti_wdt_ping, 140*2d63908bSTero Kristo .get_timeleft = rti_wdt_get_timeleft, 141*2d63908bSTero Kristo }; 142*2d63908bSTero Kristo 143*2d63908bSTero Kristo static int rti_wdt_probe(struct platform_device *pdev) 144*2d63908bSTero Kristo { 145*2d63908bSTero Kristo int ret = 0; 146*2d63908bSTero Kristo struct device *dev = &pdev->dev; 147*2d63908bSTero Kristo struct resource *wdt_mem; 148*2d63908bSTero Kristo struct watchdog_device *wdd; 149*2d63908bSTero Kristo struct rti_wdt_device *wdt; 150*2d63908bSTero Kristo struct clk *clk; 151*2d63908bSTero Kristo 152*2d63908bSTero Kristo wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); 153*2d63908bSTero Kristo if (!wdt) 154*2d63908bSTero Kristo return -ENOMEM; 155*2d63908bSTero Kristo 156*2d63908bSTero Kristo clk = clk_get(dev, NULL); 157*2d63908bSTero Kristo if (IS_ERR(clk)) { 158*2d63908bSTero Kristo if (PTR_ERR(clk) != -EPROBE_DEFER) 159*2d63908bSTero Kristo dev_err(dev, "failed to get clock\n"); 160*2d63908bSTero Kristo return PTR_ERR(clk); 161*2d63908bSTero Kristo } 162*2d63908bSTero Kristo 163*2d63908bSTero Kristo wdt->freq = clk_get_rate(clk); 164*2d63908bSTero Kristo 165*2d63908bSTero Kristo clk_put(clk); 166*2d63908bSTero Kristo 167*2d63908bSTero Kristo if (!wdt->freq) { 168*2d63908bSTero Kristo dev_err(dev, "Failed to get fck rate.\n"); 169*2d63908bSTero Kristo return -EINVAL; 170*2d63908bSTero Kristo } 171*2d63908bSTero Kristo 172*2d63908bSTero Kristo pm_runtime_enable(dev); 173*2d63908bSTero Kristo ret = pm_runtime_get_sync(dev); 174*2d63908bSTero Kristo if (ret) { 175*2d63908bSTero Kristo if (ret != -EPROBE_DEFER) 176*2d63908bSTero Kristo dev_err(&pdev->dev, "runtime pm failed\n"); 177*2d63908bSTero Kristo return ret; 178*2d63908bSTero Kristo } 179*2d63908bSTero Kristo 180*2d63908bSTero Kristo platform_set_drvdata(pdev, wdt); 181*2d63908bSTero Kristo 182*2d63908bSTero Kristo wdd = &wdt->wdd; 183*2d63908bSTero Kristo wdd->info = &rti_wdt_info; 184*2d63908bSTero Kristo wdd->ops = &rti_wdt_ops; 185*2d63908bSTero Kristo wdd->min_timeout = 1; 186*2d63908bSTero Kristo wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) / 187*2d63908bSTero Kristo wdt->freq * 1000; 188*2d63908bSTero Kristo wdd->timeout = DEFAULT_HEARTBEAT; 189*2d63908bSTero Kristo wdd->parent = dev; 190*2d63908bSTero Kristo 191*2d63908bSTero Kristo watchdog_init_timeout(wdd, heartbeat, dev); 192*2d63908bSTero Kristo 193*2d63908bSTero Kristo watchdog_set_drvdata(wdd, wdt); 194*2d63908bSTero Kristo watchdog_set_nowayout(wdd, 1); 195*2d63908bSTero Kristo watchdog_set_restart_priority(wdd, 128); 196*2d63908bSTero Kristo 197*2d63908bSTero Kristo wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); 198*2d63908bSTero Kristo wdt->base = devm_ioremap_resource(dev, wdt_mem); 199*2d63908bSTero Kristo if (IS_ERR(wdt->base)) { 200*2d63908bSTero Kristo ret = PTR_ERR(wdt->base); 201*2d63908bSTero Kristo goto err_iomap; 202*2d63908bSTero Kristo } 203*2d63908bSTero Kristo 204*2d63908bSTero Kristo ret = watchdog_register_device(wdd); 205*2d63908bSTero Kristo if (ret) { 206*2d63908bSTero Kristo dev_err(dev, "cannot register watchdog device\n"); 207*2d63908bSTero Kristo goto err_iomap; 208*2d63908bSTero Kristo } 209*2d63908bSTero Kristo 210*2d63908bSTero Kristo return 0; 211*2d63908bSTero Kristo 212*2d63908bSTero Kristo err_iomap: 213*2d63908bSTero Kristo pm_runtime_put_sync(&pdev->dev); 214*2d63908bSTero Kristo 215*2d63908bSTero Kristo return ret; 216*2d63908bSTero Kristo } 217*2d63908bSTero Kristo 218*2d63908bSTero Kristo static int rti_wdt_remove(struct platform_device *pdev) 219*2d63908bSTero Kristo { 220*2d63908bSTero Kristo struct rti_wdt_device *wdt = platform_get_drvdata(pdev); 221*2d63908bSTero Kristo 222*2d63908bSTero Kristo watchdog_unregister_device(&wdt->wdd); 223*2d63908bSTero Kristo pm_runtime_put(&pdev->dev); 224*2d63908bSTero Kristo 225*2d63908bSTero Kristo return 0; 226*2d63908bSTero Kristo } 227*2d63908bSTero Kristo 228*2d63908bSTero Kristo static const struct of_device_id rti_wdt_of_match[] = { 229*2d63908bSTero Kristo { .compatible = "ti,j7-rti-wdt", }, 230*2d63908bSTero Kristo {}, 231*2d63908bSTero Kristo }; 232*2d63908bSTero Kristo MODULE_DEVICE_TABLE(of, rti_wdt_of_match); 233*2d63908bSTero Kristo 234*2d63908bSTero Kristo static struct platform_driver rti_wdt_driver = { 235*2d63908bSTero Kristo .driver = { 236*2d63908bSTero Kristo .name = "rti-wdt", 237*2d63908bSTero Kristo .of_match_table = rti_wdt_of_match, 238*2d63908bSTero Kristo }, 239*2d63908bSTero Kristo .probe = rti_wdt_probe, 240*2d63908bSTero Kristo .remove = rti_wdt_remove, 241*2d63908bSTero Kristo }; 242*2d63908bSTero Kristo 243*2d63908bSTero Kristo module_platform_driver(rti_wdt_driver); 244*2d63908bSTero Kristo 245*2d63908bSTero Kristo MODULE_AUTHOR("Tero Kristo <t-kristo@ti.com>"); 246*2d63908bSTero Kristo MODULE_DESCRIPTION("K3 RTI Watchdog Driver"); 247*2d63908bSTero Kristo 248*2d63908bSTero Kristo module_param(heartbeat, int, 0); 249*2d63908bSTero Kristo MODULE_PARM_DESC(heartbeat, 250*2d63908bSTero Kristo "Watchdog heartbeat period in seconds from 1 to " 251*2d63908bSTero Kristo __MODULE_STRING(MAX_HEARTBEAT) ", default " 252*2d63908bSTero Kristo __MODULE_STRING(DEFAULT_HEARTBEAT)); 253*2d63908bSTero Kristo 254*2d63908bSTero Kristo MODULE_LICENSE("GPL"); 255*2d63908bSTero Kristo MODULE_ALIAS("platform:rti-wdt"); 256