linux/drivers/watchdog/retu_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Retu watchdog driver
   4 *
   5 * Copyright (C) 2004, 2005 Nokia Corporation
   6 *
   7 * Based on code written by Amit Kucheria and Michael Buesch.
   8 * Rewritten by Aaro Koskinen.
   9 */
  10
  11#include <linux/devm-helpers.h>
  12#include <linux/slab.h>
  13#include <linux/errno.h>
  14#include <linux/device.h>
  15#include <linux/kernel.h>
  16#include <linux/module.h>
  17#include <linux/mfd/retu.h>
  18#include <linux/watchdog.h>
  19#include <linux/platform_device.h>
  20
  21/* Watchdog timer values in seconds */
  22#define RETU_WDT_MAX_TIMER      63
  23
  24struct retu_wdt_dev {
  25        struct retu_dev         *rdev;
  26        struct device           *dev;
  27        struct delayed_work     ping_work;
  28};
  29
  30/*
  31 * Since Retu watchdog cannot be disabled in hardware, we must kick it
  32 * with a timer until userspace watchdog software takes over. If
  33 * CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding.
  34 */
  35static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev)
  36{
  37        retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
  38        schedule_delayed_work(&wdev->ping_work,
  39                        round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2));
  40}
  41
  42static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev)
  43{
  44        retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
  45        cancel_delayed_work_sync(&wdev->ping_work);
  46}
  47
  48static void retu_wdt_ping_work(struct work_struct *work)
  49{
  50        struct retu_wdt_dev *wdev = container_of(to_delayed_work(work),
  51                                                struct retu_wdt_dev, ping_work);
  52        retu_wdt_ping_enable(wdev);
  53}
  54
  55static int retu_wdt_start(struct watchdog_device *wdog)
  56{
  57        struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  58
  59        retu_wdt_ping_disable(wdev);
  60
  61        return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
  62}
  63
  64static int retu_wdt_stop(struct watchdog_device *wdog)
  65{
  66        struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  67
  68        retu_wdt_ping_enable(wdev);
  69
  70        return 0;
  71}
  72
  73static int retu_wdt_ping(struct watchdog_device *wdog)
  74{
  75        struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  76
  77        return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
  78}
  79
  80static int retu_wdt_set_timeout(struct watchdog_device *wdog,
  81                                unsigned int timeout)
  82{
  83        struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  84
  85        wdog->timeout = timeout;
  86        return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
  87}
  88
  89static const struct watchdog_info retu_wdt_info = {
  90        .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE | WDIOF_KEEPALIVEPING,
  91        .identity = "Retu watchdog",
  92};
  93
  94static const struct watchdog_ops retu_wdt_ops = {
  95        .owner          = THIS_MODULE,
  96        .start          = retu_wdt_start,
  97        .stop           = retu_wdt_stop,
  98        .ping           = retu_wdt_ping,
  99        .set_timeout    = retu_wdt_set_timeout,
 100};
 101
 102static int retu_wdt_probe(struct platform_device *pdev)
 103{
 104        struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
 105        bool nowayout = WATCHDOG_NOWAYOUT;
 106        struct watchdog_device *retu_wdt;
 107        struct retu_wdt_dev *wdev;
 108        int ret;
 109
 110        retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL);
 111        if (!retu_wdt)
 112                return -ENOMEM;
 113
 114        wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
 115        if (!wdev)
 116                return -ENOMEM;
 117
 118        retu_wdt->info          = &retu_wdt_info;
 119        retu_wdt->ops           = &retu_wdt_ops;
 120        retu_wdt->timeout       = RETU_WDT_MAX_TIMER;
 121        retu_wdt->min_timeout   = 0;
 122        retu_wdt->max_timeout   = RETU_WDT_MAX_TIMER;
 123        retu_wdt->parent        = &pdev->dev;
 124
 125        watchdog_set_drvdata(retu_wdt, wdev);
 126        watchdog_set_nowayout(retu_wdt, nowayout);
 127
 128        wdev->rdev              = rdev;
 129        wdev->dev               = &pdev->dev;
 130
 131        ret = devm_delayed_work_autocancel(&pdev->dev, &wdev->ping_work,
 132                                           retu_wdt_ping_work);
 133        if (ret)
 134                return ret;
 135
 136        ret = devm_watchdog_register_device(&pdev->dev, retu_wdt);
 137        if (ret < 0)
 138                return ret;
 139
 140        if (nowayout)
 141                retu_wdt_ping(retu_wdt);
 142        else
 143                retu_wdt_ping_enable(wdev);
 144
 145        return 0;
 146}
 147
 148static struct platform_driver retu_wdt_driver = {
 149        .probe          = retu_wdt_probe,
 150        .driver         = {
 151                .name   = "retu-wdt",
 152        },
 153};
 154module_platform_driver(retu_wdt_driver);
 155
 156MODULE_ALIAS("platform:retu-wdt");
 157MODULE_DESCRIPTION("Retu watchdog");
 158MODULE_AUTHOR("Amit Kucheria");
 159MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
 160MODULE_LICENSE("GPL");
 161