linux/drivers/watchdog/rn5t618_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Watchdog driver for Ricoh RN5T618 PMIC
   4 *
   5 * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
   6 */
   7
   8#include <linux/device.h>
   9#include <linux/mfd/rn5t618.h>
  10#include <linux/module.h>
  11#include <linux/platform_device.h>
  12#include <linux/watchdog.h>
  13
  14#define DRIVER_NAME "rn5t618-wdt"
  15
  16static bool nowayout = WATCHDOG_NOWAYOUT;
  17static unsigned int timeout;
  18
  19module_param(timeout, uint, 0);
  20MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds");
  21
  22module_param(nowayout, bool, 0);
  23MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  24                 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  25
  26struct rn5t618_wdt {
  27        struct watchdog_device wdt_dev;
  28        struct rn5t618 *rn5t618;
  29};
  30
  31/*
  32 * This array encodes the values of WDOGTIM field for the supported
  33 * watchdog expiration times. If the watchdog is not accessed before
  34 * the timer expiration, the PMU generates an interrupt and if the CPU
  35 * doesn't clear it within one second the system is restarted.
  36 */
  37static const struct {
  38        u8 reg_val;
  39        unsigned int time;
  40} rn5t618_wdt_map[] = {
  41        { 0, 1 },
  42        { 1, 8 },
  43        { 2, 32 },
  44        { 3, 128 },
  45};
  46
  47static int rn5t618_wdt_set_timeout(struct watchdog_device *wdt_dev,
  48                                   unsigned int t)
  49{
  50        struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
  51        int ret, i;
  52
  53        for (i = 0; i < ARRAY_SIZE(rn5t618_wdt_map); i++) {
  54                if (rn5t618_wdt_map[i].time + 1 >= t)
  55                        break;
  56        }
  57
  58        if (i == ARRAY_SIZE(rn5t618_wdt_map))
  59                return -EINVAL;
  60
  61        ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
  62                                 RN5T618_WATCHDOG_WDOGTIM_M,
  63                                 rn5t618_wdt_map[i].reg_val);
  64        if (!ret)
  65                wdt_dev->timeout = rn5t618_wdt_map[i].time;
  66
  67        return ret;
  68}
  69
  70static int rn5t618_wdt_start(struct watchdog_device *wdt_dev)
  71{
  72        struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
  73        int ret;
  74
  75        ret = rn5t618_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
  76        if (ret)
  77                return ret;
  78
  79        /* enable repower-on */
  80        ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_REPCNT,
  81                                 RN5T618_REPCNT_REPWRON,
  82                                 RN5T618_REPCNT_REPWRON);
  83        if (ret)
  84                return ret;
  85
  86        /* enable watchdog */
  87        ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
  88                                 RN5T618_WATCHDOG_WDOGEN,
  89                                 RN5T618_WATCHDOG_WDOGEN);
  90        if (ret)
  91                return ret;
  92
  93        /* enable watchdog interrupt */
  94        return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIREN,
  95                                  RN5T618_PWRIRQ_IR_WDOG,
  96                                  RN5T618_PWRIRQ_IR_WDOG);
  97}
  98
  99static int rn5t618_wdt_stop(struct watchdog_device *wdt_dev)
 100{
 101        struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 102
 103        return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
 104                                  RN5T618_WATCHDOG_WDOGEN, 0);
 105}
 106
 107static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev)
 108{
 109        struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
 110        unsigned int val;
 111        int ret;
 112
 113        /* The counter is restarted after a R/W access to watchdog register */
 114        ret = regmap_read(wdt->rn5t618->regmap, RN5T618_WATCHDOG, &val);
 115        if (ret)
 116                return ret;
 117
 118        ret = regmap_write(wdt->rn5t618->regmap, RN5T618_WATCHDOG, val);
 119        if (ret)
 120                return ret;
 121
 122        /* Clear pending watchdog interrupt */
 123        return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIRQ,
 124                                  RN5T618_PWRIRQ_IR_WDOG, 0);
 125}
 126
 127static const struct watchdog_info rn5t618_wdt_info = {
 128        .options        = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
 129                          WDIOF_KEEPALIVEPING,
 130        .identity       = DRIVER_NAME,
 131};
 132
 133static const struct watchdog_ops rn5t618_wdt_ops = {
 134        .owner          = THIS_MODULE,
 135        .start          = rn5t618_wdt_start,
 136        .stop           = rn5t618_wdt_stop,
 137        .ping           = rn5t618_wdt_ping,
 138        .set_timeout    = rn5t618_wdt_set_timeout,
 139};
 140
 141static int rn5t618_wdt_probe(struct platform_device *pdev)
 142{
 143        struct device *dev = &pdev->dev;
 144        struct rn5t618 *rn5t618 = dev_get_drvdata(dev->parent);
 145        struct rn5t618_wdt *wdt;
 146        int min_timeout, max_timeout;
 147
 148        wdt = devm_kzalloc(dev, sizeof(struct rn5t618_wdt), GFP_KERNEL);
 149        if (!wdt)
 150                return -ENOMEM;
 151
 152        min_timeout = rn5t618_wdt_map[0].time;
 153        max_timeout = rn5t618_wdt_map[ARRAY_SIZE(rn5t618_wdt_map) - 1].time;
 154
 155        wdt->rn5t618 = rn5t618;
 156        wdt->wdt_dev.info = &rn5t618_wdt_info;
 157        wdt->wdt_dev.ops = &rn5t618_wdt_ops;
 158        wdt->wdt_dev.min_timeout = min_timeout;
 159        wdt->wdt_dev.max_timeout = max_timeout;
 160        wdt->wdt_dev.timeout = max_timeout;
 161        wdt->wdt_dev.parent = dev;
 162
 163        watchdog_set_drvdata(&wdt->wdt_dev, wdt);
 164        watchdog_init_timeout(&wdt->wdt_dev, timeout, dev);
 165        watchdog_set_nowayout(&wdt->wdt_dev, nowayout);
 166
 167        platform_set_drvdata(pdev, wdt);
 168
 169        return watchdog_register_device(&wdt->wdt_dev);
 170}
 171
 172static int rn5t618_wdt_remove(struct platform_device *pdev)
 173{
 174        struct rn5t618_wdt *wdt = platform_get_drvdata(pdev);
 175
 176        watchdog_unregister_device(&wdt->wdt_dev);
 177
 178        return 0;
 179}
 180
 181static struct platform_driver rn5t618_wdt_driver = {
 182        .probe = rn5t618_wdt_probe,
 183        .remove = rn5t618_wdt_remove,
 184        .driver = {
 185                .name   = DRIVER_NAME,
 186        },
 187};
 188
 189module_platform_driver(rn5t618_wdt_driver);
 190
 191MODULE_ALIAS("platform:rn5t618-wdt");
 192MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
 193MODULE_DESCRIPTION("RN5T618 watchdog driver");
 194MODULE_LICENSE("GPL v2");
 195