linux/drivers/watchdog/stmp3xxx_rtc_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Watchdog driver for the RTC based watchdog in STMP3xxx and i.MX23/28
   4 *
   5 * Author: Wolfram Sang <kernel@pengutronix.de>
   6 *
   7 * Copyright (C) 2011-12 Wolfram Sang, Pengutronix
   8 */
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/watchdog.h>
  12#include <linux/platform_device.h>
  13#include <linux/stmp3xxx_rtc_wdt.h>
  14#include <linux/notifier.h>
  15#include <linux/reboot.h>
  16
  17#define WDOG_TICK_RATE 1000 /* 1 kHz clock */
  18#define STMP3XXX_DEFAULT_TIMEOUT 19
  19#define STMP3XXX_MAX_TIMEOUT (UINT_MAX / WDOG_TICK_RATE)
  20
  21static int heartbeat = STMP3XXX_DEFAULT_TIMEOUT;
  22module_param(heartbeat, uint, 0);
  23MODULE_PARM_DESC(heartbeat, "Watchdog heartbeat period in seconds from 1 to "
  24                 __MODULE_STRING(STMP3XXX_MAX_TIMEOUT) ", default "
  25                 __MODULE_STRING(STMP3XXX_DEFAULT_TIMEOUT));
  26
  27static int wdt_start(struct watchdog_device *wdd)
  28{
  29        struct device *dev = watchdog_get_drvdata(wdd);
  30        struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev);
  31
  32        pdata->wdt_set_timeout(dev->parent, wdd->timeout * WDOG_TICK_RATE);
  33        return 0;
  34}
  35
  36static int wdt_stop(struct watchdog_device *wdd)
  37{
  38        struct device *dev = watchdog_get_drvdata(wdd);
  39        struct stmp3xxx_wdt_pdata *pdata = dev_get_platdata(dev);
  40
  41        pdata->wdt_set_timeout(dev->parent, 0);
  42        return 0;
  43}
  44
  45static int wdt_set_timeout(struct watchdog_device *wdd, unsigned new_timeout)
  46{
  47        wdd->timeout = new_timeout;
  48        return wdt_start(wdd);
  49}
  50
  51static const struct watchdog_info stmp3xxx_wdt_ident = {
  52        .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  53        .identity = "STMP3XXX RTC Watchdog",
  54};
  55
  56static const struct watchdog_ops stmp3xxx_wdt_ops = {
  57        .owner = THIS_MODULE,
  58        .start = wdt_start,
  59        .stop = wdt_stop,
  60        .set_timeout = wdt_set_timeout,
  61};
  62
  63static struct watchdog_device stmp3xxx_wdd = {
  64        .info = &stmp3xxx_wdt_ident,
  65        .ops = &stmp3xxx_wdt_ops,
  66        .min_timeout = 1,
  67        .max_timeout = STMP3XXX_MAX_TIMEOUT,
  68        .status = WATCHDOG_NOWAYOUT_INIT_STATUS,
  69};
  70
  71static int wdt_notify_sys(struct notifier_block *nb, unsigned long code,
  72                          void *unused)
  73{
  74        switch (code) {
  75        case SYS_DOWN:  /* keep enabled, system might crash while going down */
  76                break;
  77        case SYS_HALT:  /* allow the system to actually halt */
  78        case SYS_POWER_OFF:
  79                wdt_stop(&stmp3xxx_wdd);
  80                break;
  81        }
  82
  83        return NOTIFY_DONE;
  84}
  85
  86static struct notifier_block wdt_notifier = {
  87        .notifier_call = wdt_notify_sys,
  88};
  89
  90static int stmp3xxx_wdt_probe(struct platform_device *pdev)
  91{
  92        struct device *dev = &pdev->dev;
  93        int ret;
  94
  95        watchdog_set_drvdata(&stmp3xxx_wdd, dev);
  96
  97        stmp3xxx_wdd.timeout = clamp_t(unsigned, heartbeat, 1, STMP3XXX_MAX_TIMEOUT);
  98        stmp3xxx_wdd.parent = dev;
  99
 100        ret = devm_watchdog_register_device(dev, &stmp3xxx_wdd);
 101        if (ret < 0)
 102                return ret;
 103
 104        if (register_reboot_notifier(&wdt_notifier))
 105                dev_warn(dev, "cannot register reboot notifier\n");
 106
 107        dev_info(dev, "initialized watchdog with heartbeat %ds\n",
 108                 stmp3xxx_wdd.timeout);
 109        return 0;
 110}
 111
 112static int stmp3xxx_wdt_remove(struct platform_device *pdev)
 113{
 114        unregister_reboot_notifier(&wdt_notifier);
 115        return 0;
 116}
 117
 118static int __maybe_unused stmp3xxx_wdt_suspend(struct device *dev)
 119{
 120        struct watchdog_device *wdd = &stmp3xxx_wdd;
 121
 122        if (watchdog_active(wdd))
 123                return wdt_stop(wdd);
 124
 125        return 0;
 126}
 127
 128static int __maybe_unused stmp3xxx_wdt_resume(struct device *dev)
 129{
 130        struct watchdog_device *wdd = &stmp3xxx_wdd;
 131
 132        if (watchdog_active(wdd))
 133                return wdt_start(wdd);
 134
 135        return 0;
 136}
 137
 138static SIMPLE_DEV_PM_OPS(stmp3xxx_wdt_pm_ops,
 139                         stmp3xxx_wdt_suspend, stmp3xxx_wdt_resume);
 140
 141static struct platform_driver stmp3xxx_wdt_driver = {
 142        .driver = {
 143                .name = "stmp3xxx_rtc_wdt",
 144                .pm = &stmp3xxx_wdt_pm_ops,
 145        },
 146        .probe = stmp3xxx_wdt_probe,
 147        .remove = stmp3xxx_wdt_remove,
 148};
 149module_platform_driver(stmp3xxx_wdt_driver);
 150
 151MODULE_DESCRIPTION("STMP3XXX RTC Watchdog Driver");
 152MODULE_LICENSE("GPL v2");
 153MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
 154