Commit 22b1c841e31510c3124c88a13b8a7ada14e2e2d1

Authored by Beniamino Galvani
Committed by Wim Van Sebroeck
1 parent 2b9366b669

watchdog: add driver for Ricoh RN5T618 watchdog

This adds a driver for the watchdog timer available in Ricoh RN5T618
PMIC. The device supports a programmable expiration time of 1, 8, 32
or 128 seconds.

Signed-off-by: Beniamino Galvani <b.galvani@gmail.com>
Reviewed-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Wim Van Sebroeck <wim@iguana.be>

Showing 3 changed files with 210 additions and 0 deletions Side-by-side Diff

drivers/watchdog/Kconfig
... ... @@ -327,6 +327,17 @@
327 327 To compile this driver as a module, choose M here: the
328 328 module will be called orion_wdt.
329 329  
  330 +config RN5T618_WATCHDOG
  331 + tristate "Ricoh RN5T618 watchdog"
  332 + depends on MFD_RN5T618
  333 + select WATCHDOG_CORE
  334 + help
  335 + If you say yes here you get support for watchdog on the Ricoh
  336 + RN5T618 PMIC.
  337 +
  338 + This driver can also be built as a module. If so, the module
  339 + will be called rn5t618_wdt.
  340 +
330 341 config SUNXI_WATCHDOG
331 342 tristate "Allwinner SoCs watchdog support"
332 343 depends on ARCH_SUNXI
drivers/watchdog/Makefile
... ... @@ -48,6 +48,7 @@
48 48 obj-$(CONFIG_DAVINCI_WATCHDOG) += davinci_wdt.o
49 49 obj-$(CONFIG_ORION_WATCHDOG) += orion_wdt.o
50 50 obj-$(CONFIG_SUNXI_WATCHDOG) += sunxi_wdt.o
  51 +obj-$(CONFIG_RN5T618_WATCHDOG) += rn5t618_wdt.o
51 52 obj-$(CONFIG_COH901327_WATCHDOG) += coh901327_wdt.o
52 53 obj-$(CONFIG_STMP3XXX_RTC_WATCHDOG) += stmp3xxx_rtc_wdt.o
53 54 obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o
drivers/watchdog/rn5t618_wdt.c
  1 +/*
  2 + * Watchdog driver for Ricoh RN5T618 PMIC
  3 + *
  4 + * Copyright (C) 2014 Beniamino Galvani <b.galvani@gmail.com>
  5 + *
  6 + * This program is free software; you can redistribute it and/or
  7 + * modify it under the terms of the GNU General Public License
  8 + * version 2 as published by the Free Software Foundation.
  9 + *
  10 + * You should have received a copy of the GNU General Public License
  11 + * along with this program. If not, see <http://www.gnu.org/licenses/>.
  12 + */
  13 +
  14 +#include <linux/device.h>
  15 +#include <linux/mfd/rn5t618.h>
  16 +#include <linux/module.h>
  17 +#include <linux/platform_device.h>
  18 +#include <linux/watchdog.h>
  19 +
  20 +#define DRIVER_NAME "rn5t618-wdt"
  21 +
  22 +static bool nowayout = WATCHDOG_NOWAYOUT;
  23 +static unsigned int timeout;
  24 +
  25 +module_param(timeout, uint, 0);
  26 +MODULE_PARM_DESC(timeout, "Initial watchdog timeout in seconds");
  27 +
  28 +module_param(nowayout, bool, 0);
  29 +MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  30 + __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  31 +
  32 +struct rn5t618_wdt {
  33 + struct watchdog_device wdt_dev;
  34 + struct rn5t618 *rn5t618;
  35 +};
  36 +
  37 +/*
  38 + * This array encodes the values of WDOGTIM field for the supported
  39 + * watchdog expiration times. If the watchdog is not accessed before
  40 + * the timer expiration, the PMU generates an interrupt and if the CPU
  41 + * doesn't clear it within one second the system is restarted.
  42 + */
  43 +static const struct {
  44 + u8 reg_val;
  45 + unsigned int time;
  46 +} rn5t618_wdt_map[] = {
  47 + { 0, 1 },
  48 + { 1, 8 },
  49 + { 2, 32 },
  50 + { 3, 128 },
  51 +};
  52 +
  53 +static int rn5t618_wdt_set_timeout(struct watchdog_device *wdt_dev,
  54 + unsigned int t)
  55 +{
  56 + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
  57 + int ret, i;
  58 +
  59 + for (i = 0; i < ARRAY_SIZE(rn5t618_wdt_map); i++) {
  60 + if (rn5t618_wdt_map[i].time + 1 >= t)
  61 + break;
  62 + }
  63 +
  64 + if (i == ARRAY_SIZE(rn5t618_wdt_map))
  65 + return -EINVAL;
  66 +
  67 + ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
  68 + RN5T618_WATCHDOG_WDOGTIM_M,
  69 + rn5t618_wdt_map[i].reg_val);
  70 + if (!ret)
  71 + wdt_dev->timeout = rn5t618_wdt_map[i].time;
  72 +
  73 + return ret;
  74 +}
  75 +
  76 +static int rn5t618_wdt_start(struct watchdog_device *wdt_dev)
  77 +{
  78 + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
  79 + int ret;
  80 +
  81 + ret = rn5t618_wdt_set_timeout(wdt_dev, wdt_dev->timeout);
  82 + if (ret)
  83 + return ret;
  84 +
  85 + /* enable repower-on */
  86 + ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_REPCNT,
  87 + RN5T618_REPCNT_REPWRON,
  88 + RN5T618_REPCNT_REPWRON);
  89 + if (ret)
  90 + return ret;
  91 +
  92 + /* enable watchdog */
  93 + ret = regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
  94 + RN5T618_WATCHDOG_WDOGEN,
  95 + RN5T618_WATCHDOG_WDOGEN);
  96 + if (ret)
  97 + return ret;
  98 +
  99 + /* enable watchdog interrupt */
  100 + return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIREN,
  101 + RN5T618_PWRIRQ_IR_WDOG,
  102 + RN5T618_PWRIRQ_IR_WDOG);
  103 +}
  104 +
  105 +static int rn5t618_wdt_stop(struct watchdog_device *wdt_dev)
  106 +{
  107 + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
  108 +
  109 + return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_WATCHDOG,
  110 + RN5T618_WATCHDOG_WDOGEN, 0);
  111 +}
  112 +
  113 +static int rn5t618_wdt_ping(struct watchdog_device *wdt_dev)
  114 +{
  115 + struct rn5t618_wdt *wdt = watchdog_get_drvdata(wdt_dev);
  116 + unsigned int val;
  117 + int ret;
  118 +
  119 + /* The counter is restarted after a R/W access to watchdog register */
  120 + ret = regmap_read(wdt->rn5t618->regmap, RN5T618_WATCHDOG, &val);
  121 + if (ret)
  122 + return ret;
  123 +
  124 + ret = regmap_write(wdt->rn5t618->regmap, RN5T618_WATCHDOG, val);
  125 + if (ret)
  126 + return ret;
  127 +
  128 + /* Clear pending watchdog interrupt */
  129 + return regmap_update_bits(wdt->rn5t618->regmap, RN5T618_PWRIRQ,
  130 + RN5T618_PWRIRQ_IR_WDOG, 0);
  131 +}
  132 +
  133 +static struct watchdog_info rn5t618_wdt_info = {
  134 + .options = WDIOF_SETTIMEOUT | WDIOF_MAGICCLOSE |
  135 + WDIOF_KEEPALIVEPING,
  136 + .identity = DRIVER_NAME,
  137 +};
  138 +
  139 +static struct watchdog_ops rn5t618_wdt_ops = {
  140 + .owner = THIS_MODULE,
  141 + .start = rn5t618_wdt_start,
  142 + .stop = rn5t618_wdt_stop,
  143 + .ping = rn5t618_wdt_ping,
  144 + .set_timeout = rn5t618_wdt_set_timeout,
  145 +};
  146 +
  147 +static int rn5t618_wdt_probe(struct platform_device *pdev)
  148 +{
  149 + struct rn5t618 *rn5t618 = dev_get_drvdata(pdev->dev.parent);
  150 + struct rn5t618_wdt *wdt;
  151 + int min_timeout, max_timeout;
  152 +
  153 + wdt = devm_kzalloc(&pdev->dev, sizeof(struct rn5t618_wdt), GFP_KERNEL);
  154 + if (!wdt)
  155 + return -ENOMEM;
  156 +
  157 + min_timeout = rn5t618_wdt_map[0].time;
  158 + max_timeout = rn5t618_wdt_map[ARRAY_SIZE(rn5t618_wdt_map) - 1].time;
  159 +
  160 + wdt->rn5t618 = rn5t618;
  161 + wdt->wdt_dev.info = &rn5t618_wdt_info;
  162 + wdt->wdt_dev.ops = &rn5t618_wdt_ops;
  163 + wdt->wdt_dev.min_timeout = min_timeout;
  164 + wdt->wdt_dev.max_timeout = max_timeout;
  165 + wdt->wdt_dev.timeout = max_timeout;
  166 + wdt->wdt_dev.parent = &pdev->dev;
  167 +
  168 + watchdog_set_drvdata(&wdt->wdt_dev, wdt);
  169 + watchdog_init_timeout(&wdt->wdt_dev, timeout, &pdev->dev);
  170 + watchdog_set_nowayout(&wdt->wdt_dev, nowayout);
  171 +
  172 + platform_set_drvdata(pdev, wdt);
  173 +
  174 + return watchdog_register_device(&wdt->wdt_dev);
  175 +}
  176 +
  177 +static int rn5t618_wdt_remove(struct platform_device *pdev)
  178 +{
  179 + struct rn5t618_wdt *wdt = platform_get_drvdata(pdev);
  180 +
  181 + watchdog_unregister_device(&wdt->wdt_dev);
  182 +
  183 + return 0;
  184 +}
  185 +
  186 +static struct platform_driver rn5t618_wdt_driver = {
  187 + .probe = rn5t618_wdt_probe,
  188 + .remove = rn5t618_wdt_remove,
  189 + .driver = {
  190 + .name = DRIVER_NAME,
  191 + },
  192 +};
  193 +
  194 +module_platform_driver(rn5t618_wdt_driver);
  195 +
  196 +MODULE_AUTHOR("Beniamino Galvani <b.galvani@gmail.com>");
  197 +MODULE_DESCRIPTION("RN5T618 watchdog driver");
  198 +MODULE_LICENSE("GPL v2");