linux/drivers/watchdog/retu_wdt.c
<<
>>
Prefs
   1/*
   2 * Retu watchdog driver
   3 *
   4 * Copyright (C) 2004, 2005 Nokia Corporation
   5 *
   6 * Based on code written by Amit Kucheria and Michael Buesch.
   7 * Rewritten by Aaro Koskinen.
   8 *
   9 * This file is subject to the terms and conditions of the GNU General
  10 * Public License. See the file "COPYING" in the main directory of this
  11 * archive for more details.
  12 *
  13 * This program is distributed in the hope that it will be useful,
  14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16 * GNU General Public License for more details.
  17 */
  18
  19#include <linux/init.h>
  20#include <linux/slab.h>
  21#include <linux/errno.h>
  22#include <linux/device.h>
  23#include <linux/kernel.h>
  24#include <linux/module.h>
  25#include <linux/mfd/retu.h>
  26#include <linux/watchdog.h>
  27#include <linux/platform_device.h>
  28
  29/* Watchdog timer values in seconds */
  30#define RETU_WDT_MAX_TIMER      63
  31
  32struct retu_wdt_dev {
  33        struct retu_dev         *rdev;
  34        struct device           *dev;
  35        struct delayed_work     ping_work;
  36};
  37
  38/*
  39 * Since Retu watchdog cannot be disabled in hardware, we must kick it
  40 * with a timer until userspace watchdog software takes over. If
  41 * CONFIG_WATCHDOG_NOWAYOUT is set, we never start the feeding.
  42 */
  43static void retu_wdt_ping_enable(struct retu_wdt_dev *wdev)
  44{
  45        retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
  46        schedule_delayed_work(&wdev->ping_work,
  47                        round_jiffies_relative(RETU_WDT_MAX_TIMER * HZ / 2));
  48}
  49
  50static void retu_wdt_ping_disable(struct retu_wdt_dev *wdev)
  51{
  52        retu_write(wdev->rdev, RETU_REG_WATCHDOG, RETU_WDT_MAX_TIMER);
  53        cancel_delayed_work_sync(&wdev->ping_work);
  54}
  55
  56static void retu_wdt_ping_work(struct work_struct *work)
  57{
  58        struct retu_wdt_dev *wdev = container_of(to_delayed_work(work),
  59                                                struct retu_wdt_dev, ping_work);
  60        retu_wdt_ping_enable(wdev);
  61}
  62
  63static int retu_wdt_start(struct watchdog_device *wdog)
  64{
  65        struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  66
  67        retu_wdt_ping_disable(wdev);
  68
  69        return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
  70}
  71
  72static int retu_wdt_stop(struct watchdog_device *wdog)
  73{
  74        struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  75
  76        retu_wdt_ping_enable(wdev);
  77
  78        return 0;
  79}
  80
  81static int retu_wdt_ping(struct watchdog_device *wdog)
  82{
  83        struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  84
  85        return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
  86}
  87
  88static int retu_wdt_set_timeout(struct watchdog_device *wdog,
  89                                unsigned int timeout)
  90{
  91        struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
  92
  93        wdog->timeout = timeout;
  94        return retu_write(wdev->rdev, RETU_REG_WATCHDOG, wdog->timeout);
  95}
  96
  97static const struct watchdog_info retu_wdt_info = {
  98        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
  99        .identity = "Retu watchdog",
 100};
 101
 102static const struct watchdog_ops retu_wdt_ops = {
 103        .owner          = THIS_MODULE,
 104        .start          = retu_wdt_start,
 105        .stop           = retu_wdt_stop,
 106        .ping           = retu_wdt_ping,
 107        .set_timeout    = retu_wdt_set_timeout,
 108};
 109
 110static int retu_wdt_probe(struct platform_device *pdev)
 111{
 112        struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
 113        bool nowayout = WATCHDOG_NOWAYOUT;
 114        struct watchdog_device *retu_wdt;
 115        struct retu_wdt_dev *wdev;
 116        int ret;
 117
 118        retu_wdt = devm_kzalloc(&pdev->dev, sizeof(*retu_wdt), GFP_KERNEL);
 119        if (!retu_wdt)
 120                return -ENOMEM;
 121
 122        wdev = devm_kzalloc(&pdev->dev, sizeof(*wdev), GFP_KERNEL);
 123        if (!wdev)
 124                return -ENOMEM;
 125
 126        retu_wdt->info          = &retu_wdt_info;
 127        retu_wdt->ops           = &retu_wdt_ops;
 128        retu_wdt->timeout       = RETU_WDT_MAX_TIMER;
 129        retu_wdt->min_timeout   = 0;
 130        retu_wdt->max_timeout   = RETU_WDT_MAX_TIMER;
 131
 132        watchdog_set_drvdata(retu_wdt, wdev);
 133        watchdog_set_nowayout(retu_wdt, nowayout);
 134
 135        wdev->rdev              = rdev;
 136        wdev->dev               = &pdev->dev;
 137
 138        INIT_DELAYED_WORK(&wdev->ping_work, retu_wdt_ping_work);
 139
 140        ret = watchdog_register_device(retu_wdt);
 141        if (ret < 0)
 142                return ret;
 143
 144        if (nowayout)
 145                retu_wdt_ping(retu_wdt);
 146        else
 147                retu_wdt_ping_enable(wdev);
 148
 149        platform_set_drvdata(pdev, retu_wdt);
 150
 151        return 0;
 152}
 153
 154static int retu_wdt_remove(struct platform_device *pdev)
 155{
 156        struct watchdog_device *wdog = platform_get_drvdata(pdev);
 157        struct retu_wdt_dev *wdev = watchdog_get_drvdata(wdog);
 158
 159        watchdog_unregister_device(wdog);
 160        cancel_delayed_work_sync(&wdev->ping_work);
 161
 162        return 0;
 163}
 164
 165static struct platform_driver retu_wdt_driver = {
 166        .probe          = retu_wdt_probe,
 167        .remove         = retu_wdt_remove,
 168        .driver         = {
 169                .name   = "retu-wdt",
 170        },
 171};
 172module_platform_driver(retu_wdt_driver);
 173
 174MODULE_ALIAS("platform:retu-wdt");
 175MODULE_DESCRIPTION("Retu watchdog");
 176MODULE_AUTHOR("Amit Kucheria");
 177MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
 178MODULE_LICENSE("GPL");
 179
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.