Lines Matching +full:wdt +full:- +full:timer +full:- +full:index
1 // SPDX-License-Identifier: GPL-2.0-or-later
6 * S3C2410 Watchdog Timer Support
16 #include <linux/timer.h>
90 * DOC: Quirk flags for different Samsung watchdog IP-cores
95 * differences in both watchdog and PMU IP-cores should be accounted for. Quirk
102 * write-only, writing any values to this register clears the interrupt, but
106 * WDT reset request. On old SoCs it's usually called MASK_WDT_RESET_REQUEST,
113 * watchdog timer reset.
116 * register. If 'mask_bit' bit is set, PMU will disable WDT reset when
159 * struct s3c2410_wdt_variant - Per-variant config data
162 * timer reset functionality.
164 * timer reset functionality.
166 * @mask_bit: Bit number for the watchdog timer in the disable register and the
171 * @cnt_en_reg: Offset in pmureg for the register that enables WDT counter.
191 struct clk *src_clk; /* for WDT counter */
369 { .compatible = "google,gs101-wdt",
371 { .compatible = "samsung,s3c2410-wdt",
373 { .compatible = "samsung,s3c6410-wdt",
375 { .compatible = "samsung,exynos5250-wdt",
377 { .compatible = "samsung,exynos5420-wdt",
379 { .compatible = "samsung,exynos7-wdt",
381 { .compatible = "samsung,exynos850-wdt",
383 { .compatible = "samsung,exynos990-wdt",
385 { .compatible = "samsung,exynosautov9-wdt",
387 { .compatible = "samsung,exynosautov920-wdt",
396 .name = "s3c2410-wdt",
405 static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt) in s3c2410wdt_get_freq() argument
407 return clk_get_rate(wdt->src_clk ? wdt->src_clk : wdt->bus_clk); in s3c2410wdt_get_freq()
410 static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt) in s3c2410wdt_max_timeout() argument
412 const unsigned long freq = s3c2410wdt_get_freq(wdt); in s3c2410wdt_max_timeout()
418 static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask) in s3c2410wdt_disable_wdt_reset() argument
420 const u32 mask_val = BIT(wdt->drv_data->mask_bit); in s3c2410wdt_disable_wdt_reset()
424 ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->disable_reg, in s3c2410wdt_disable_wdt_reset()
427 dev_err(wdt->dev, "failed to update reg(%d)\n", ret); in s3c2410wdt_disable_wdt_reset()
432 static int s3c2410wdt_mask_wdt_reset(struct s3c2410_wdt *wdt, bool mask) in s3c2410wdt_mask_wdt_reset() argument
434 const u32 mask_val = BIT(wdt->drv_data->mask_bit); in s3c2410wdt_mask_wdt_reset()
435 const bool val_inv = wdt->drv_data->mask_reset_inv; in s3c2410wdt_mask_wdt_reset()
439 ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->mask_reset_reg, in s3c2410wdt_mask_wdt_reset()
442 dev_err(wdt->dev, "failed to update reg(%d)\n", ret); in s3c2410wdt_mask_wdt_reset()
447 static int s3c2410wdt_enable_counter(struct s3c2410_wdt *wdt, bool en) in s3c2410wdt_enable_counter() argument
449 const u32 mask_val = BIT(wdt->drv_data->cnt_en_bit); in s3c2410wdt_enable_counter()
453 ret = regmap_update_bits(wdt->pmureg, wdt->drv_data->cnt_en_reg, in s3c2410wdt_enable_counter()
456 dev_err(wdt->dev, "failed to update reg(%d)\n", ret); in s3c2410wdt_enable_counter()
461 static int s3c2410wdt_enable(struct s3c2410_wdt *wdt, bool en) in s3c2410wdt_enable() argument
465 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_AUTO_DISABLE) { in s3c2410wdt_enable()
466 ret = s3c2410wdt_disable_wdt_reset(wdt, !en); in s3c2410wdt_enable()
471 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_MASK_RESET) { in s3c2410wdt_enable()
472 ret = s3c2410wdt_mask_wdt_reset(wdt, !en); in s3c2410wdt_enable()
477 if (wdt->drv_data->quirks & QUIRK_HAS_PMU_CNT_EN) { in s3c2410wdt_enable()
478 ret = s3c2410wdt_enable_counter(wdt, en); in s3c2410wdt_enable()
487 static void s3c2410wdt_mask_dbgack(struct s3c2410_wdt *wdt) in s3c2410wdt_mask_dbgack() argument
491 if (!(wdt->drv_data->quirks & QUIRK_HAS_DBGACK_BIT)) in s3c2410wdt_mask_dbgack()
494 wtcon = readl(wdt->reg_base + S3C2410_WTCON); in s3c2410wdt_mask_dbgack()
496 writel(wtcon, wdt->reg_base + S3C2410_WTCON); in s3c2410wdt_mask_dbgack()
501 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); in s3c2410wdt_keepalive() local
504 spin_lock_irqsave(&wdt->lock, flags); in s3c2410wdt_keepalive()
505 writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); in s3c2410wdt_keepalive()
506 spin_unlock_irqrestore(&wdt->lock, flags); in s3c2410wdt_keepalive()
511 static void __s3c2410wdt_stop(struct s3c2410_wdt *wdt) in __s3c2410wdt_stop() argument
515 wtcon = readl(wdt->reg_base + S3C2410_WTCON); in __s3c2410wdt_stop()
517 writel(wtcon, wdt->reg_base + S3C2410_WTCON); in __s3c2410wdt_stop()
522 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); in s3c2410wdt_stop() local
525 spin_lock_irqsave(&wdt->lock, flags); in s3c2410wdt_stop()
526 __s3c2410wdt_stop(wdt); in s3c2410wdt_stop()
527 spin_unlock_irqrestore(&wdt->lock, flags); in s3c2410wdt_stop()
535 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); in s3c2410wdt_start() local
538 spin_lock_irqsave(&wdt->lock, flags); in s3c2410wdt_start()
540 __s3c2410wdt_stop(wdt); in s3c2410wdt_start()
542 wtcon = readl(wdt->reg_base + S3C2410_WTCON); in s3c2410wdt_start()
553 dev_dbg(wdt->dev, "Starting watchdog: count=0x%08x, wtcon=%08lx\n", in s3c2410wdt_start()
554 wdt->count, wtcon); in s3c2410wdt_start()
556 writel(wdt->count, wdt->reg_base + S3C2410_WTDAT); in s3c2410wdt_start()
557 writel(wdt->count, wdt->reg_base + S3C2410_WTCNT); in s3c2410wdt_start()
558 writel(wtcon, wdt->reg_base + S3C2410_WTCON); in s3c2410wdt_start()
559 spin_unlock_irqrestore(&wdt->lock, flags); in s3c2410wdt_start()
567 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); in s3c2410wdt_set_heartbeat() local
568 unsigned long freq = s3c2410wdt_get_freq(wdt); in s3c2410wdt_set_heartbeat()
574 return -EINVAL; in s3c2410wdt_set_heartbeat()
579 dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n", in s3c2410wdt_set_heartbeat()
591 dev_err(wdt->dev, "timeout %d too big\n", timeout); in s3c2410wdt_set_heartbeat()
592 return -EINVAL; in s3c2410wdt_set_heartbeat()
596 dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n", in s3c2410wdt_set_heartbeat()
600 wdt->count = count; in s3c2410wdt_set_heartbeat()
602 /* update the pre-scaler */ in s3c2410wdt_set_heartbeat()
603 wtcon = readl(wdt->reg_base + S3C2410_WTCON); in s3c2410wdt_set_heartbeat()
605 wtcon |= S3C2410_WTCON_PRESCALE(divisor-1); in s3c2410wdt_set_heartbeat()
607 writel(count, wdt->reg_base + S3C2410_WTDAT); in s3c2410wdt_set_heartbeat()
608 writel(wtcon, wdt->reg_base + S3C2410_WTCON); in s3c2410wdt_set_heartbeat()
610 wdd->timeout = (count * divisor) / freq; in s3c2410wdt_set_heartbeat()
618 struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); in s3c2410wdt_restart() local
619 void __iomem *wdt_base = wdt->reg_base; in s3c2410wdt_restart()
666 struct s3c2410_wdt *wdt = platform_get_drvdata(param); in s3c2410wdt_irq() local
668 dev_info(wdt->dev, "watchdog timer expired (irq)\n"); in s3c2410wdt_irq()
670 s3c2410wdt_keepalive(&wdt->wdt_device); in s3c2410wdt_irq()
672 if (wdt->drv_data->quirks & QUIRK_HAS_WTCLRINT_REG) in s3c2410wdt_irq()
673 writel(0x1, wdt->reg_base + S3C2410_WTCLRINT); in s3c2410wdt_irq()
678 static inline unsigned int s3c2410wdt_get_bootstatus(struct s3c2410_wdt *wdt) in s3c2410wdt_get_bootstatus() argument
683 if (!(wdt->drv_data->quirks & QUIRK_HAS_PMU_RST_STAT)) in s3c2410wdt_get_bootstatus()
686 ret = regmap_read(wdt->pmureg, wdt->drv_data->rst_stat_reg, &rst_stat); in s3c2410wdt_get_bootstatus()
688 dev_warn(wdt->dev, "Couldn't get RST_STAT register\n"); in s3c2410wdt_get_bootstatus()
689 else if (rst_stat & BIT(wdt->drv_data->rst_stat_bit)) in s3c2410wdt_get_bootstatus()
696 s3c2410_get_wdt_drv_data(struct platform_device *pdev, struct s3c2410_wdt *wdt) in s3c2410_get_wdt_drv_data() argument
699 struct device *dev = &pdev->dev; in s3c2410_get_wdt_drv_data()
705 platform_get_device_id(pdev)->driver_data; in s3c2410_get_wdt_drv_data()
709 /* Choose Exynos850/ExynosAutov9 driver data w.r.t. cluster index */ in s3c2410_get_wdt_drv_data()
715 u32 index; in s3c2410_get_wdt_drv_data() local
718 err = of_property_read_u32(dev->of_node, in s3c2410_get_wdt_drv_data()
719 "samsung,cluster-index", &index); in s3c2410_get_wdt_drv_data()
721 return dev_err_probe(dev, -EINVAL, "failed to get cluster index\n"); in s3c2410_get_wdt_drv_data()
723 switch (index) { in s3c2410_get_wdt_drv_data()
741 return dev_err_probe(dev, -EINVAL, "wrong cluster index: %u\n", index); in s3c2410_get_wdt_drv_data()
746 wdt->drv_data = variant; in s3c2410_get_wdt_drv_data()
757 struct device *dev = &pdev->dev; in s3c2410wdt_probe()
758 struct s3c2410_wdt *wdt; in s3c2410wdt_probe() local
763 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL); in s3c2410wdt_probe()
764 if (!wdt) in s3c2410wdt_probe()
765 return -ENOMEM; in s3c2410wdt_probe()
767 wdt->dev = dev; in s3c2410wdt_probe()
768 spin_lock_init(&wdt->lock); in s3c2410wdt_probe()
769 wdt->wdt_device = s3c2410_wdd; in s3c2410wdt_probe()
771 ret = s3c2410_get_wdt_drv_data(pdev, wdt); in s3c2410wdt_probe()
775 if (wdt->drv_data->quirks & QUIRKS_HAVE_PMUREG) { in s3c2410wdt_probe()
776 wdt->pmureg = syscon_regmap_lookup_by_phandle(dev->of_node, in s3c2410wdt_probe()
777 "samsung,syscon-phandle"); in s3c2410wdt_probe()
778 if (IS_ERR(wdt->pmureg)) in s3c2410wdt_probe()
779 return dev_err_probe(dev, PTR_ERR(wdt->pmureg), in s3c2410wdt_probe()
787 /* get the memory region for the watchdog timer */ in s3c2410wdt_probe()
788 wdt->reg_base = devm_platform_ioremap_resource(pdev, 0); in s3c2410wdt_probe()
789 if (IS_ERR(wdt->reg_base)) in s3c2410wdt_probe()
790 return PTR_ERR(wdt->reg_base); in s3c2410wdt_probe()
792 wdt->bus_clk = devm_clk_get_enabled(dev, "watchdog"); in s3c2410wdt_probe()
793 if (IS_ERR(wdt->bus_clk)) in s3c2410wdt_probe()
794 return dev_err_probe(dev, PTR_ERR(wdt->bus_clk), "failed to get bus clock\n"); in s3c2410wdt_probe()
797 * "watchdog_src" clock is optional; if it's not present -- just skip it in s3c2410wdt_probe()
800 wdt->src_clk = devm_clk_get_optional_enabled(dev, "watchdog_src"); in s3c2410wdt_probe()
801 if (IS_ERR(wdt->src_clk)) in s3c2410wdt_probe()
802 return dev_err_probe(dev, PTR_ERR(wdt->src_clk), "failed to get source clock\n"); in s3c2410wdt_probe()
804 wdt->wdt_device.min_timeout = 1; in s3c2410wdt_probe()
805 wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt); in s3c2410wdt_probe()
807 watchdog_set_drvdata(&wdt->wdt_device, wdt); in s3c2410wdt_probe()
809 /* see if we can actually set the requested timer margin, and if in s3c2410wdt_probe()
812 watchdog_init_timeout(&wdt->wdt_device, tmr_margin, dev); in s3c2410wdt_probe()
813 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, in s3c2410wdt_probe()
814 wdt->wdt_device.timeout); in s3c2410wdt_probe()
816 ret = s3c2410wdt_set_heartbeat(&wdt->wdt_device, in s3c2410wdt_probe()
826 pdev->name, pdev); in s3c2410wdt_probe()
830 watchdog_set_nowayout(&wdt->wdt_device, nowayout); in s3c2410wdt_probe()
831 watchdog_set_restart_priority(&wdt->wdt_device, 128); in s3c2410wdt_probe()
833 wdt->wdt_device.bootstatus = s3c2410wdt_get_bootstatus(wdt); in s3c2410wdt_probe()
834 wdt->wdt_device.parent = dev; in s3c2410wdt_probe()
836 s3c2410wdt_mask_dbgack(wdt); in s3c2410wdt_probe()
839 * If "tmr_atboot" param is non-zero, start the watchdog right now. Also in s3c2410wdt_probe()
846 dev_info(dev, "starting watchdog timer\n"); in s3c2410wdt_probe()
847 s3c2410wdt_start(&wdt->wdt_device); in s3c2410wdt_probe()
848 set_bit(WDOG_HW_RUNNING, &wdt->wdt_device.status); in s3c2410wdt_probe()
850 s3c2410wdt_stop(&wdt->wdt_device); in s3c2410wdt_probe()
853 ret = devm_watchdog_register_device(dev, &wdt->wdt_device); in s3c2410wdt_probe()
857 ret = s3c2410wdt_enable(wdt, true); in s3c2410wdt_probe()
861 ret = devm_add_action_or_reset(dev, s3c2410wdt_wdt_disable_action, wdt); in s3c2410wdt_probe()
865 platform_set_drvdata(pdev, wdt); in s3c2410wdt_probe()
869 wtcon = readl(wdt->reg_base + S3C2410_WTCON); in s3c2410wdt_probe()
881 struct s3c2410_wdt *wdt = platform_get_drvdata(dev); in s3c2410wdt_shutdown() local
883 s3c2410wdt_enable(wdt, false); in s3c2410wdt_shutdown()
884 s3c2410wdt_stop(&wdt->wdt_device); in s3c2410wdt_shutdown()
890 struct s3c2410_wdt *wdt = dev_get_drvdata(dev); in s3c2410wdt_suspend() local
893 wdt->wtcon_save = readl(wdt->reg_base + S3C2410_WTCON); in s3c2410wdt_suspend()
894 wdt->wtdat_save = readl(wdt->reg_base + S3C2410_WTDAT); in s3c2410wdt_suspend()
896 ret = s3c2410wdt_enable(wdt, false); in s3c2410wdt_suspend()
901 s3c2410wdt_stop(&wdt->wdt_device); in s3c2410wdt_suspend()
909 struct s3c2410_wdt *wdt = dev_get_drvdata(dev); in s3c2410wdt_resume() local
912 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTDAT); in s3c2410wdt_resume()
913 writel(wdt->wtdat_save, wdt->reg_base + S3C2410_WTCNT);/* Reset count */ in s3c2410wdt_resume()
914 writel(wdt->wtcon_save, wdt->reg_base + S3C2410_WTCON); in s3c2410wdt_resume()
916 ret = s3c2410wdt_enable(wdt, true); in s3c2410wdt_resume()
921 (wdt->wtcon_save & S3C2410_WTCON_ENABLE) ? "en" : "dis"); in s3c2410wdt_resume()
934 .name = "s3c2410-wdt",