Lines Matching +full:watchdog +full:- +full:tops

1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Copyright 2010-2011 Picochip Ltd., Jamie Iles
6 * This file implements a driver for the Synopsys DesignWare watchdog device
7 * in the many subsystems. The watchdog has 16 different timeout periods
10 * The DesignWare watchdog cannot be stopped once it has been started so we
11 * do not implement a stop function. The watchdog core will continue to send
12 * heartbeat requests after the watchdog device has been closed.
30 #include <linux/watchdog.h>
51 /* There are sixteen TOPs (timeout periods) that can be set in the watchdog. */
68 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started "
104 return readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET) & in dw_wdt_is_enabled()
112 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); in dw_wdt_update_mode()
117 writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); in dw_wdt_update_mode()
119 dw_wdt->rmod = rmod; in dw_wdt_update_mode()
133 if (dw_wdt->timeouts[idx].sec >= timeout) in dw_wdt_find_best_top()
138 --idx; in dw_wdt_find_best_top()
140 *top_val = dw_wdt->timeouts[idx].top_val; in dw_wdt_find_best_top()
142 return dw_wdt->timeouts[idx].sec; in dw_wdt_find_best_top()
154 if (dw_wdt->timeouts[idx].sec) in dw_wdt_get_min_timeout()
158 return dw_wdt->timeouts[idx].sec; in dw_wdt_get_min_timeout()
163 struct dw_wdt_timeout *timeout = &dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1]; in dw_wdt_get_max_timeout_ms()
166 msec = (u64)timeout->sec * MSEC_PER_SEC + timeout->msec; in dw_wdt_get_max_timeout_ms()
173 int top_val = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET) & 0xF; in dw_wdt_get_timeout()
177 if (dw_wdt->timeouts[idx].top_val == top_val) in dw_wdt_get_timeout()
185 return dw_wdt->timeouts[idx].sec * dw_wdt->rmod; in dw_wdt_get_timeout()
192 writel(WDOG_COUNTER_RESTART_KICK_VALUE, dw_wdt->regs + in dw_wdt_ping()
205 * Note IRQ mode being enabled means having a non-zero pre-timeout in dw_wdt_set_timeout()
207 * requested timeout as possible since DW Watchdog IRQ mode is designed in dw_wdt_set_timeout()
208 * in two stages way - first timeout rises the pre-timeout interrupt, in dw_wdt_set_timeout()
210 * watchdog-caused reset happens after two watchdog TOPs elapsed. in dw_wdt_set_timeout()
212 timeout = dw_wdt_find_best_top(dw_wdt, DIV_ROUND_UP(top_s, dw_wdt->rmod), in dw_wdt_set_timeout()
214 if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) in dw_wdt_set_timeout()
215 wdd->pretimeout = timeout; in dw_wdt_set_timeout()
217 wdd->pretimeout = 0; in dw_wdt_set_timeout()
220 * Set the new value in the watchdog. Some versions of dw_wdt in dw_wdt_set_timeout()
223 * effectively get a pat of the watchdog right here. in dw_wdt_set_timeout()
226 dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); in dw_wdt_set_timeout()
228 /* Kick new TOP value into the watchdog counter if activated. */ in dw_wdt_set_timeout()
234 * kernel(watchdog_dev.c) helps to feed watchdog before in dw_wdt_set_timeout()
235 * wdd->max_hw_heartbeat_ms in dw_wdt_set_timeout()
237 if (top_s * 1000 <= wdd->max_hw_heartbeat_ms) in dw_wdt_set_timeout()
238 wdd->timeout = timeout * dw_wdt->rmod; in dw_wdt_set_timeout()
240 wdd->timeout = top_s; in dw_wdt_set_timeout()
250 * We ignore actual value of the timeout passed from user-space in dw_wdt_set_pretimeout()
255 dw_wdt_set_timeout(wdd, wdd->timeout); in dw_wdt_set_pretimeout()
262 u32 val = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); in dw_wdt_arm_system_reset()
265 if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) in dw_wdt_arm_system_reset()
269 /* Enable watchdog. */ in dw_wdt_arm_system_reset()
271 writel(val, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); in dw_wdt_arm_system_reset()
278 dw_wdt_set_timeout(wdd, wdd->timeout); in dw_wdt_start()
279 dw_wdt_ping(&dw_wdt->wdd); in dw_wdt_start()
289 if (!dw_wdt->rst) { in dw_wdt_stop()
290 set_bit(WDOG_HW_RUNNING, &wdd->status); in dw_wdt_stop()
294 reset_control_assert(dw_wdt->rst); in dw_wdt_stop()
295 reset_control_deassert(dw_wdt->rst); in dw_wdt_stop()
305 writel(0, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); in dw_wdt_restart()
309 dw_wdt->regs + WDOG_COUNTER_RESTART_REG_OFFSET); in dw_wdt_restart()
325 val = readl(dw_wdt->regs + WDOG_CURRENT_COUNT_REG_OFFSET); in dw_wdt_get_timeleft()
326 sec = val / dw_wdt->rate; in dw_wdt_get_timeleft()
328 if (dw_wdt->rmod == DW_WDT_RMOD_IRQ) { in dw_wdt_get_timeleft()
329 val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET); in dw_wdt_get_timeleft()
331 sec += wdd->pretimeout; in dw_wdt_get_timeleft()
340 .identity = "Synopsys DesignWare Watchdog",
346 .identity = "Synopsys DesignWare Watchdog",
369 val = readl(dw_wdt->regs + WDOG_INTERRUPT_STATUS_REG_OFFSET); in dw_wdt_irq()
373 watchdog_notify_pretimeout(&dw_wdt->wdd); in dw_wdt_irq()
382 dw_wdt->control = readl(dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); in dw_wdt_suspend()
383 dw_wdt->timeout = readl(dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); in dw_wdt_suspend()
385 clk_disable_unprepare(dw_wdt->pclk); in dw_wdt_suspend()
386 clk_disable_unprepare(dw_wdt->clk); in dw_wdt_suspend()
394 int err = clk_prepare_enable(dw_wdt->clk); in dw_wdt_resume()
399 err = clk_prepare_enable(dw_wdt->pclk); in dw_wdt_resume()
401 clk_disable_unprepare(dw_wdt->clk); in dw_wdt_resume()
405 writel(dw_wdt->timeout, dw_wdt->regs + WDOG_TIMEOUT_RANGE_REG_OFFSET); in dw_wdt_resume()
406 writel(dw_wdt->control, dw_wdt->regs + WDOG_CONTROL_REG_OFFSET); in dw_wdt_resume()
408 dw_wdt_ping(&dw_wdt->wdd); in dw_wdt_resume()
417 * TOPs array can be arbitrary ordered with nearly any sixteen uint numbers
419 * passed TOPs array to pre-calculate the effective timeouts and to sort the
423 static void dw_wdt_handle_tops(struct dw_wdt *dw_wdt, const u32 *tops) in dw_wdt_handle_tops() argument
430 * We walk over the passed TOPs array and calculate corresponding in dw_wdt_handle_tops()
432 * is needed to distinguish the TOPs with very close timeouts and to in dw_wdt_handle_tops()
433 * set the watchdog max heartbeat setting further. in dw_wdt_handle_tops()
437 tout.sec = tops[val] / dw_wdt->rate; in dw_wdt_handle_tops()
438 msec = (u64)tops[val] * MSEC_PER_SEC; in dw_wdt_handle_tops()
439 do_div(msec, dw_wdt->rate); in dw_wdt_handle_tops()
440 tout.msec = msec - ((u64)tout.sec * MSEC_PER_SEC); in dw_wdt_handle_tops()
447 dst = &dw_wdt->timeouts[tidx]; in dw_wdt_handle_tops()
448 if (tout.sec > dst->sec || (tout.sec == dst->sec && in dw_wdt_handle_tops()
449 tout.msec >= dst->msec)) in dw_wdt_handle_tops()
455 dw_wdt->timeouts[val] = tout; in dw_wdt_handle_tops()
462 const u32 *tops; in dw_wdt_init_timeouts() local
470 data = readl(dw_wdt->regs + WDOG_COMP_PARAMS_1_REG_OFFSET); in dw_wdt_init_timeouts()
472 tops = dw_wdt_fix_tops; in dw_wdt_init_timeouts()
475 "snps,watchdog-tops", of_tops, DW_WDT_NUM_TOPS, in dw_wdt_init_timeouts()
478 dev_warn(dev, "No valid TOPs array specified\n"); in dw_wdt_init_timeouts()
479 tops = dw_wdt_fix_tops; in dw_wdt_init_timeouts()
481 tops = of_tops; in dw_wdt_init_timeouts()
485 /* Convert the specified TOPs into an array of watchdog timeouts. */ in dw_wdt_init_timeouts()
486 dw_wdt_handle_tops(dw_wdt, tops); in dw_wdt_init_timeouts()
487 if (!dw_wdt->timeouts[DW_WDT_NUM_TOPS - 1].sec) { in dw_wdt_init_timeouts()
489 return -EINVAL; in dw_wdt_init_timeouts()
520 struct device *dev = dw_wdt->wdd.parent; in dw_wdt_dbgfs_init()
527 regset->regs = dw_wdt_dbgfs_regs; in dw_wdt_dbgfs_init()
528 regset->nregs = ARRAY_SIZE(dw_wdt_dbgfs_regs); in dw_wdt_dbgfs_init()
529 regset->base = dw_wdt->regs; in dw_wdt_dbgfs_init()
531 dw_wdt->dbgfs_dir = debugfs_create_dir(dev_name(dev), NULL); in dw_wdt_dbgfs_init()
533 debugfs_create_regset32("registers", 0444, dw_wdt->dbgfs_dir, regset); in dw_wdt_dbgfs_init()
538 debugfs_remove_recursive(dw_wdt->dbgfs_dir); in dw_wdt_dbgfs_clear()
550 struct device *dev = &pdev->dev; in dw_wdt_drv_probe()
557 return -ENOMEM; in dw_wdt_drv_probe()
559 dw_wdt->regs = devm_platform_ioremap_resource(pdev, 0); in dw_wdt_drv_probe()
560 if (IS_ERR(dw_wdt->regs)) in dw_wdt_drv_probe()
561 return PTR_ERR(dw_wdt->regs); in dw_wdt_drv_probe()
564 * Try to request the watchdog dedicated timer clock source. It must in dw_wdt_drv_probe()
569 dw_wdt->clk = devm_clk_get_enabled(dev, "tclk"); in dw_wdt_drv_probe()
570 if (IS_ERR(dw_wdt->clk)) { in dw_wdt_drv_probe()
571 dw_wdt->clk = devm_clk_get_enabled(dev, NULL); in dw_wdt_drv_probe()
572 if (IS_ERR(dw_wdt->clk)) in dw_wdt_drv_probe()
573 return PTR_ERR(dw_wdt->clk); in dw_wdt_drv_probe()
576 dw_wdt->rate = clk_get_rate(dw_wdt->clk); in dw_wdt_drv_probe()
577 if (dw_wdt->rate == 0) in dw_wdt_drv_probe()
578 return -EINVAL; in dw_wdt_drv_probe()
587 dw_wdt->pclk = devm_clk_get_optional_enabled(dev, "pclk"); in dw_wdt_drv_probe()
588 if (IS_ERR(dw_wdt->pclk)) in dw_wdt_drv_probe()
589 return PTR_ERR(dw_wdt->pclk); in dw_wdt_drv_probe()
591 dw_wdt->rst = devm_reset_control_get_optional_shared(&pdev->dev, NULL); in dw_wdt_drv_probe()
592 if (IS_ERR(dw_wdt->rst)) in dw_wdt_drv_probe()
593 return PTR_ERR(dw_wdt->rst); in dw_wdt_drv_probe()
595 /* Enable normal reset without pre-timeout by default. */ in dw_wdt_drv_probe()
599 * Pre-timeout IRQ is optional, since some hardware may lack support in dw_wdt_drv_probe()
600 * of it. Note we must request rising-edge IRQ, since the lane is left in dw_wdt_drv_probe()
601 * pending either until the next watchdog kick event or up to the in dw_wdt_drv_probe()
608 pdev->name, dw_wdt); in dw_wdt_drv_probe()
612 dw_wdt->wdd.info = &dw_wdt_pt_ident; in dw_wdt_drv_probe()
614 if (ret == -EPROBE_DEFER) in dw_wdt_drv_probe()
617 dw_wdt->wdd.info = &dw_wdt_ident; in dw_wdt_drv_probe()
620 reset_control_deassert(dw_wdt->rst); in dw_wdt_drv_probe()
626 wdd = &dw_wdt->wdd; in dw_wdt_drv_probe()
627 wdd->ops = &dw_wdt_ops; in dw_wdt_drv_probe()
628 wdd->min_timeout = dw_wdt_get_min_timeout(dw_wdt); in dw_wdt_drv_probe()
629 wdd->max_hw_heartbeat_ms = dw_wdt_get_max_timeout_ms(dw_wdt); in dw_wdt_drv_probe()
630 wdd->parent = dev; in dw_wdt_drv_probe()
637 * If the watchdog is already running, use its already configured in dw_wdt_drv_probe()
642 wdd->timeout = dw_wdt_get_timeout(dw_wdt); in dw_wdt_drv_probe()
643 set_bit(WDOG_HW_RUNNING, &wdd->status); in dw_wdt_drv_probe()
645 wdd->timeout = DW_WDT_DEFAULT_SECONDS; in dw_wdt_drv_probe()
663 reset_control_assert(dw_wdt->rst); in dw_wdt_drv_probe()
673 watchdog_unregister_device(&dw_wdt->wdd); in dw_wdt_drv_remove()
674 reset_control_assert(dw_wdt->rst); in dw_wdt_drv_remove()
679 { .compatible = "snps,dw-wdt", },
698 MODULE_DESCRIPTION("Synopsys DesignWare Watchdog Driver");