linux/drivers/watchdog/da9063_wdt.c
<<
>>
Prefs
   1/*
   2 * Watchdog driver for DA9063 PMICs.
   3 *
   4 * Copyright(c) 2012 Dialog Semiconductor Ltd.
   5 *
   6 * Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 */
  13
  14#include <linux/kernel.h>
  15#include <linux/module.h>
  16#include <linux/watchdog.h>
  17#include <linux/platform_device.h>
  18#include <linux/uaccess.h>
  19#include <linux/slab.h>
  20#include <linux/delay.h>
  21#include <linux/mfd/da9063/registers.h>
  22#include <linux/mfd/da9063/core.h>
  23#include <linux/regmap.h>
  24
  25/*
  26 * Watchdog selector to timeout in seconds.
  27 *   0: WDT disabled;
  28 *   others: timeout = 2048 ms * 2^(TWDSCALE-1).
  29 */
  30static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
  31#define DA9063_TWDSCALE_DISABLE         0
  32#define DA9063_TWDSCALE_MIN             1
  33#define DA9063_TWDSCALE_MAX             (ARRAY_SIZE(wdt_timeout) - 1)
  34#define DA9063_WDT_MIN_TIMEOUT          wdt_timeout[DA9063_TWDSCALE_MIN]
  35#define DA9063_WDT_MAX_TIMEOUT          wdt_timeout[DA9063_TWDSCALE_MAX]
  36#define DA9063_WDG_TIMEOUT              wdt_timeout[3]
  37#define DA9063_RESET_PROTECTION_MS      256
  38
  39struct da9063_watchdog {
  40        struct da9063 *da9063;
  41        struct watchdog_device wdtdev;
  42};
  43
  44static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs)
  45{
  46        unsigned int i;
  47
  48        for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) {
  49                if (wdt_timeout[i] >= secs)
  50                        return i;
  51        }
  52
  53        return DA9063_TWDSCALE_MAX;
  54}
  55
  56static int _da9063_wdt_set_timeout(struct da9063 *da9063, unsigned int regval)
  57{
  58        return regmap_update_bits(da9063->regmap, DA9063_REG_CONTROL_D,
  59                                  DA9063_TWDSCALE_MASK, regval);
  60}
  61
  62static int da9063_wdt_start(struct watchdog_device *wdd)
  63{
  64        struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
  65        unsigned int selector;
  66        int ret;
  67
  68        selector = da9063_wdt_timeout_to_sel(wdt->wdtdev.timeout);
  69        ret = _da9063_wdt_set_timeout(wdt->da9063, selector);
  70        if (ret)
  71                dev_err(wdt->da9063->dev, "Watchdog failed to start (err = %d)\n",
  72                        ret);
  73
  74        return ret;
  75}
  76
  77static int da9063_wdt_stop(struct watchdog_device *wdd)
  78{
  79        struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
  80        int ret;
  81
  82        ret = regmap_update_bits(wdt->da9063->regmap, DA9063_REG_CONTROL_D,
  83                                 DA9063_TWDSCALE_MASK, DA9063_TWDSCALE_DISABLE);
  84        if (ret)
  85                dev_alert(wdt->da9063->dev, "Watchdog failed to stop (err = %d)\n",
  86                          ret);
  87
  88        return ret;
  89}
  90
  91static int da9063_wdt_ping(struct watchdog_device *wdd)
  92{
  93        struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
  94        int ret;
  95
  96        ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F,
  97                           DA9063_WATCHDOG);
  98        if (ret)
  99                dev_alert(wdt->da9063->dev, "Failed to ping the watchdog (err = %d)\n",
 100                          ret);
 101
 102        return ret;
 103}
 104
 105static int da9063_wdt_set_timeout(struct watchdog_device *wdd,
 106                                  unsigned int timeout)
 107{
 108        struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
 109        unsigned int selector;
 110        int ret;
 111
 112        selector = da9063_wdt_timeout_to_sel(timeout);
 113        ret = _da9063_wdt_set_timeout(wdt->da9063, selector);
 114        if (ret)
 115                dev_err(wdt->da9063->dev, "Failed to set watchdog timeout (err = %d)\n",
 116                        ret);
 117        else
 118                wdd->timeout = wdt_timeout[selector];
 119
 120        return ret;
 121}
 122
 123static int da9063_wdt_restart(struct watchdog_device *wdd, unsigned long action,
 124                              void *data)
 125{
 126        struct da9063_watchdog *wdt = watchdog_get_drvdata(wdd);
 127        int ret;
 128
 129        ret = regmap_write(wdt->da9063->regmap, DA9063_REG_CONTROL_F,
 130                           DA9063_SHUTDOWN);
 131        if (ret)
 132                dev_alert(wdt->da9063->dev, "Failed to shutdown (err = %d)\n",
 133                          ret);
 134
 135        return ret;
 136}
 137
 138static const struct watchdog_info da9063_watchdog_info = {
 139        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
 140        .identity = "DA9063 Watchdog",
 141};
 142
 143static const struct watchdog_ops da9063_watchdog_ops = {
 144        .owner = THIS_MODULE,
 145        .start = da9063_wdt_start,
 146        .stop = da9063_wdt_stop,
 147        .ping = da9063_wdt_ping,
 148        .set_timeout = da9063_wdt_set_timeout,
 149        .restart = da9063_wdt_restart,
 150};
 151
 152static int da9063_wdt_probe(struct platform_device *pdev)
 153{
 154        struct da9063 *da9063;
 155        struct da9063_watchdog *wdt;
 156
 157        if (!pdev->dev.parent)
 158                return -EINVAL;
 159
 160        da9063 = dev_get_drvdata(pdev->dev.parent);
 161        if (!da9063)
 162                return -EINVAL;
 163
 164        wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
 165        if (!wdt)
 166                return -ENOMEM;
 167
 168        wdt->da9063 = da9063;
 169
 170        wdt->wdtdev.info = &da9063_watchdog_info;
 171        wdt->wdtdev.ops = &da9063_watchdog_ops;
 172        wdt->wdtdev.min_timeout = DA9063_WDT_MIN_TIMEOUT;
 173        wdt->wdtdev.max_timeout = DA9063_WDT_MAX_TIMEOUT;
 174        wdt->wdtdev.min_hw_heartbeat_ms = DA9063_RESET_PROTECTION_MS;
 175        wdt->wdtdev.timeout = DA9063_WDG_TIMEOUT;
 176        wdt->wdtdev.parent = &pdev->dev;
 177
 178        wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
 179
 180        watchdog_set_restart_priority(&wdt->wdtdev, 128);
 181
 182        watchdog_set_drvdata(&wdt->wdtdev, wdt);
 183
 184        return devm_watchdog_register_device(&pdev->dev, &wdt->wdtdev);
 185}
 186
 187static struct platform_driver da9063_wdt_driver = {
 188        .probe = da9063_wdt_probe,
 189        .driver = {
 190                .name = DA9063_DRVNAME_WATCHDOG,
 191        },
 192};
 193module_platform_driver(da9063_wdt_driver);
 194
 195MODULE_AUTHOR("Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>");
 196MODULE_DESCRIPTION("Watchdog driver for Dialog DA9063");
 197MODULE_LICENSE("GPL");
 198MODULE_ALIAS("platform:" DA9063_DRVNAME_WATCHDOG);
 199