399 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Watchdog driver for the K3 RTI module4 *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/of.h>18#include <linux/of_address.h>19#include <linux/platform_device.h>20#include <linux/pm_runtime.h>21#include <linux/types.h>22#include <linux/watchdog.h>23 24#define DEFAULT_HEARTBEAT 6025 26/* Max heartbeat is calculated at 32kHz source clock */27#define MAX_HEARTBEAT 100028 29/* Timer register set definition */30#define RTIDWDCTRL 0x9031#define RTIDWDPRLD 0x9432#define RTIWDSTATUS 0x9833#define RTIWDKEY 0x9c34#define RTIDWDCNTR 0xa035#define RTIWWDRXCTRL 0xa436#define RTIWWDSIZECTRL 0xa837 38#define RTIWWDRX_NMI 0xa39 40#define RTIWWDSIZE_50P 0x5041#define RTIWWDSIZE_25P 0x50042#define RTIWWDSIZE_12P5 0x500043#define RTIWWDSIZE_6P25 0x5000044#define RTIWWDSIZE_3P125 0x50000045 46#define WDENABLE_KEY 0xa98559da47 48#define WDKEY_SEQ0 0xe51a49#define WDKEY_SEQ1 0xa35c50 51#define WDT_PRELOAD_SHIFT 1352 53#define WDT_PRELOAD_MAX 0xfff54 55#define DWDST BIT(1)56 57#define PON_REASON_SOF_NUM 0xBBBBCCCC58#define PON_REASON_MAGIC_NUM 0xDDDDDDDD59#define PON_REASON_EOF_NUM 0xCCCCBBBB60#define RESERVED_MEM_MIN_SIZE 1261 62#define MAX_HW_ERROR 25063 64static int heartbeat = DEFAULT_HEARTBEAT;65 66/*67 * struct to hold data for each WDT device68 * @base - base io address of WD device69 * @freq - source clock frequency of WDT70 * @wdd - hold watchdog device as is in WDT core71 */72struct rti_wdt_device {73 void __iomem *base;74 unsigned long freq;75 struct watchdog_device wdd;76};77 78static int rti_wdt_start(struct watchdog_device *wdd)79{80 u32 timer_margin;81 struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);82 int ret;83 84 ret = pm_runtime_resume_and_get(wdd->parent);85 if (ret)86 return ret;87 88 /* set timeout period */89 timer_margin = (u64)wdd->timeout * wdt->freq;90 timer_margin >>= WDT_PRELOAD_SHIFT;91 if (timer_margin > WDT_PRELOAD_MAX)92 timer_margin = WDT_PRELOAD_MAX;93 writel_relaxed(timer_margin, wdt->base + RTIDWDPRLD);94 95 /*96 * RTI only supports a windowed mode, where the watchdog can only97 * be petted during the open window; not too early or not too late.98 * The HW configuration options only allow for the open window size99 * to be 50% or less than that; we obviouly want to configure the open100 * window as large as possible so we select the 50% option.101 */102 wdd->min_hw_heartbeat_ms = 520 * wdd->timeout + MAX_HW_ERROR;103 104 /* Generate NMI when wdt expires */105 writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL);106 107 /* Open window size 50%; this is the largest window size available */108 writel_relaxed(RTIWWDSIZE_50P, wdt->base + RTIWWDSIZECTRL);109 110 readl_relaxed(wdt->base + RTIWWDSIZECTRL);111 112 /* enable watchdog */113 writel_relaxed(WDENABLE_KEY, wdt->base + RTIDWDCTRL);114 return 0;115}116 117static int rti_wdt_ping(struct watchdog_device *wdd)118{119 struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);120 121 /* put watchdog in service state */122 writel_relaxed(WDKEY_SEQ0, wdt->base + RTIWDKEY);123 /* put watchdog in active state */124 writel_relaxed(WDKEY_SEQ1, wdt->base + RTIWDKEY);125 126 return 0;127}128 129static int rti_wdt_setup_hw_hb(struct watchdog_device *wdd, u32 wsize)130{131 /*132 * RTI only supports a windowed mode, where the watchdog can only133 * be petted during the open window; not too early or not too late.134 * The HW configuration options only allow for the open window size135 * to be 50% or less than that.136 * To avoid any glitches, we accommodate 2% + max hardware error137 * safety margin.138 */139 switch (wsize) {140 case RTIWWDSIZE_50P:141 /* 50% open window => 52% min heartbeat */142 wdd->min_hw_heartbeat_ms = 520 * heartbeat + MAX_HW_ERROR;143 break;144 145 case RTIWWDSIZE_25P:146 /* 25% open window => 77% min heartbeat */147 wdd->min_hw_heartbeat_ms = 770 * heartbeat + MAX_HW_ERROR;148 break;149 150 case RTIWWDSIZE_12P5:151 /* 12.5% open window => 89.5% min heartbeat */152 wdd->min_hw_heartbeat_ms = 895 * heartbeat + MAX_HW_ERROR;153 break;154 155 case RTIWWDSIZE_6P25:156 /* 6.5% open window => 95.5% min heartbeat */157 wdd->min_hw_heartbeat_ms = 955 * heartbeat + MAX_HW_ERROR;158 break;159 160 case RTIWWDSIZE_3P125:161 /* 3.125% open window => 98.9% min heartbeat */162 wdd->min_hw_heartbeat_ms = 989 * heartbeat + MAX_HW_ERROR;163 break;164 165 default:166 return -EINVAL;167 }168 169 return 0;170}171 172static unsigned int rti_wdt_get_timeleft_ms(struct watchdog_device *wdd)173{174 u64 timer_counter;175 u32 val;176 struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);177 178 /* if timeout has occurred then return 0 */179 val = readl_relaxed(wdt->base + RTIWDSTATUS);180 if (val & DWDST)181 return 0;182 183 timer_counter = readl_relaxed(wdt->base + RTIDWDCNTR);184 185 timer_counter *= 1000;186 187 do_div(timer_counter, wdt->freq);188 189 return timer_counter;190}191 192static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd)193{194 return rti_wdt_get_timeleft_ms(wdd) / 1000;195}196 197static const struct watchdog_info rti_wdt_info = {198 .options = WDIOF_KEEPALIVEPING,199 .identity = "K3 RTI Watchdog",200};201 202static const struct watchdog_ops rti_wdt_ops = {203 .owner = THIS_MODULE,204 .start = rti_wdt_start,205 .ping = rti_wdt_ping,206 .get_timeleft = rti_wdt_get_timeleft,207};208 209static int rti_wdt_probe(struct platform_device *pdev)210{211 int ret = 0;212 struct device *dev = &pdev->dev;213 struct watchdog_device *wdd;214 struct rti_wdt_device *wdt;215 struct clk *clk;216 u32 last_ping = 0;217 struct device_node *node;218 u32 reserved_mem_size;219 struct resource res;220 u32 *vaddr;221 u64 paddr;222 223 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);224 if (!wdt)225 return -ENOMEM;226 227 clk = clk_get(dev, NULL);228 if (IS_ERR(clk))229 return dev_err_probe(dev, PTR_ERR(clk), "failed to get clock\n");230 231 wdt->freq = clk_get_rate(clk);232 233 clk_put(clk);234 235 if (!wdt->freq) {236 dev_err(dev, "Failed to get fck rate.\n");237 return -EINVAL;238 }239 240 pm_runtime_enable(dev);241 ret = pm_runtime_resume_and_get(dev);242 if (ret < 0) {243 pm_runtime_disable(&pdev->dev);244 return dev_err_probe(dev, ret, "runtime pm failed\n");245 }246 247 platform_set_drvdata(pdev, wdt);248 249 wdd = &wdt->wdd;250 wdd->info = &rti_wdt_info;251 wdd->ops = &rti_wdt_ops;252 wdd->min_timeout = 1;253 wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /254 wdt->freq * 1000;255 wdd->parent = dev;256 257 watchdog_set_drvdata(wdd, wdt);258 watchdog_set_nowayout(wdd, 1);259 watchdog_set_restart_priority(wdd, 128);260 261 wdt->base = devm_platform_ioremap_resource(pdev, 0);262 if (IS_ERR(wdt->base)) {263 ret = PTR_ERR(wdt->base);264 goto err_iomap;265 }266 267 if (readl(wdt->base + RTIDWDCTRL) == WDENABLE_KEY) {268 int preset_heartbeat;269 u32 time_left_ms;270 u64 heartbeat_ms;271 u32 wsize;272 273 set_bit(WDOG_HW_RUNNING, &wdd->status);274 time_left_ms = rti_wdt_get_timeleft_ms(wdd);275 heartbeat_ms = readl(wdt->base + RTIDWDPRLD);276 heartbeat_ms <<= WDT_PRELOAD_SHIFT;277 heartbeat_ms *= 1000;278 do_div(heartbeat_ms, wdt->freq);279 preset_heartbeat = heartbeat_ms + 500;280 preset_heartbeat /= 1000;281 if (preset_heartbeat != heartbeat)282 dev_warn(dev, "watchdog already running, ignoring heartbeat config!\n");283 284 heartbeat = preset_heartbeat;285 286 wsize = readl(wdt->base + RTIWWDSIZECTRL);287 ret = rti_wdt_setup_hw_hb(wdd, wsize);288 if (ret) {289 dev_err(dev, "bad window size.\n");290 goto err_iomap;291 }292 293 last_ping = heartbeat_ms - time_left_ms;294 if (time_left_ms > heartbeat_ms) {295 dev_warn(dev, "time_left > heartbeat? Assuming last ping just before now.\n");296 last_ping = 0;297 }298 }299 300 node = of_parse_phandle(pdev->dev.of_node, "memory-region", 0);301 if (node) {302 ret = of_address_to_resource(node, 0, &res);303 if (ret) {304 dev_err(dev, "No memory address assigned to the region.\n");305 goto err_iomap;306 }307 308 /*309 * If reserved memory is defined for watchdog reset cause.310 * Readout the Power-on(PON) reason and pass to bootstatus.311 */312 paddr = res.start;313 reserved_mem_size = resource_size(&res);314 if (reserved_mem_size < RESERVED_MEM_MIN_SIZE) {315 dev_err(dev, "The size of reserved memory is too small.\n");316 ret = -EINVAL;317 goto err_iomap;318 }319 320 vaddr = memremap(paddr, reserved_mem_size, MEMREMAP_WB);321 if (!vaddr) {322 dev_err(dev, "Failed to map memory-region.\n");323 ret = -ENOMEM;324 goto err_iomap;325 }326 327 if (vaddr[0] == PON_REASON_SOF_NUM &&328 vaddr[1] == PON_REASON_MAGIC_NUM &&329 vaddr[2] == PON_REASON_EOF_NUM) {330 wdd->bootstatus |= WDIOF_CARDRESET;331 }332 memset(vaddr, 0, reserved_mem_size);333 memunmap(vaddr);334 }335 336 watchdog_init_timeout(wdd, heartbeat, dev);337 338 ret = watchdog_register_device(wdd);339 if (ret) {340 dev_err(dev, "cannot register watchdog device\n");341 goto err_iomap;342 }343 344 if (last_ping)345 watchdog_set_last_hw_keepalive(wdd, last_ping);346 347 if (!watchdog_hw_running(wdd))348 pm_runtime_put_sync(&pdev->dev);349 350 return 0;351 352err_iomap:353 pm_runtime_put_sync(&pdev->dev);354 pm_runtime_disable(&pdev->dev);355 356 return ret;357}358 359static void rti_wdt_remove(struct platform_device *pdev)360{361 struct rti_wdt_device *wdt = platform_get_drvdata(pdev);362 363 watchdog_unregister_device(&wdt->wdd);364 365 if (!pm_runtime_suspended(&pdev->dev))366 pm_runtime_put(&pdev->dev);367 368 pm_runtime_disable(&pdev->dev);369}370 371static const struct of_device_id rti_wdt_of_match[] = {372 { .compatible = "ti,j7-rti-wdt", },373 {},374};375MODULE_DEVICE_TABLE(of, rti_wdt_of_match);376 377static struct platform_driver rti_wdt_driver = {378 .driver = {379 .name = "rti-wdt",380 .of_match_table = rti_wdt_of_match,381 },382 .probe = rti_wdt_probe,383 .remove_new = rti_wdt_remove,384};385 386module_platform_driver(rti_wdt_driver);387 388MODULE_AUTHOR("Tero Kristo <t-kristo@ti.com>");389MODULE_DESCRIPTION("K3 RTI Watchdog Driver");390 391module_param(heartbeat, int, 0);392MODULE_PARM_DESC(heartbeat,393 "Watchdog heartbeat period in seconds from 1 to "394 __MODULE_STRING(MAX_HEARTBEAT) ", default "395 __MODULE_STRING(DEFAULT_HEARTBEAT));396 397MODULE_LICENSE("GPL");398MODULE_ALIAS("platform:rti-wdt");399