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