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