linux/drivers/watchdog/atlas7_wdt.c
<<
>>
Prefs
   1/*
   2 * Watchdog driver for CSR Atlas7
   3 *
   4 * Copyright (c) 2015 Cambridge Silicon Radio Limited, a CSR plc group company.
   5 *
   6 * Licensed under GPLv2.
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/io.h>
  11#include <linux/module.h>
  12#include <linux/moduleparam.h>
  13#include <linux/of.h>
  14#include <linux/platform_device.h>
  15#include <linux/watchdog.h>
  16
  17#define ATLAS7_TIMER_WDT_INDEX          5
  18#define ATLAS7_WDT_DEFAULT_TIMEOUT      20
  19
  20#define ATLAS7_WDT_CNT_CTRL     (0 + 4 * ATLAS7_TIMER_WDT_INDEX)
  21#define ATLAS7_WDT_CNT_MATCH    (0x18 + 4 * ATLAS7_TIMER_WDT_INDEX)
  22#define ATLAS7_WDT_CNT          (0x48 +  4 * ATLAS7_TIMER_WDT_INDEX)
  23#define ATLAS7_WDT_CNT_EN       (BIT(0) | BIT(1))
  24#define ATLAS7_WDT_EN           0x64
  25
  26static unsigned int timeout = ATLAS7_WDT_DEFAULT_TIMEOUT;
  27static bool nowayout = WATCHDOG_NOWAYOUT;
  28
  29module_param(timeout, uint, 0);
  30module_param(nowayout, bool, 0);
  31
  32MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)");
  33MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  34                        __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  35
  36struct atlas7_wdog {
  37        struct device *dev;
  38        void __iomem *base;
  39        unsigned long tick_rate;
  40        struct clk *clk;
  41};
  42
  43static unsigned int atlas7_wdt_gettimeleft(struct watchdog_device *wdd)
  44{
  45        struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
  46        u32 counter, match, delta;
  47
  48        counter = readl(wdt->base + ATLAS7_WDT_CNT);
  49        match = readl(wdt->base + ATLAS7_WDT_CNT_MATCH);
  50        delta = match - counter;
  51
  52        return  delta / wdt->tick_rate;
  53}
  54
  55static int atlas7_wdt_ping(struct watchdog_device *wdd)
  56{
  57        struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
  58        u32 counter, match, delta;
  59
  60        counter = readl(wdt->base + ATLAS7_WDT_CNT);
  61        delta = wdd->timeout * wdt->tick_rate;
  62        match = counter + delta;
  63
  64        writel(match, wdt->base + ATLAS7_WDT_CNT_MATCH);
  65
  66        return 0;
  67}
  68
  69static int atlas7_wdt_enable(struct watchdog_device *wdd)
  70{
  71        struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
  72
  73        atlas7_wdt_ping(wdd);
  74
  75        writel(readl(wdt->base + ATLAS7_WDT_CNT_CTRL) | ATLAS7_WDT_CNT_EN,
  76              wdt->base + ATLAS7_WDT_CNT_CTRL);
  77        writel(1, wdt->base + ATLAS7_WDT_EN);
  78
  79        return 0;
  80}
  81
  82static int atlas7_wdt_disable(struct watchdog_device *wdd)
  83{
  84        struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
  85
  86        writel(0, wdt->base + ATLAS7_WDT_EN);
  87        writel(readl(wdt->base + ATLAS7_WDT_CNT_CTRL) & ~ATLAS7_WDT_CNT_EN,
  88              wdt->base + ATLAS7_WDT_CNT_CTRL);
  89
  90        return 0;
  91}
  92
  93static int atlas7_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
  94{
  95        wdd->timeout = to;
  96
  97        return 0;
  98}
  99
 100#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
 101
 102static const struct watchdog_info atlas7_wdt_ident = {
 103        .options = OPTIONS,
 104        .firmware_version = 0,
 105        .identity = "atlas7 Watchdog",
 106};
 107
 108static const struct watchdog_ops atlas7_wdt_ops = {
 109        .owner = THIS_MODULE,
 110        .start = atlas7_wdt_enable,
 111        .stop = atlas7_wdt_disable,
 112        .get_timeleft = atlas7_wdt_gettimeleft,
 113        .ping = atlas7_wdt_ping,
 114        .set_timeout = atlas7_wdt_settimeout,
 115};
 116
 117static struct watchdog_device atlas7_wdd = {
 118        .info = &atlas7_wdt_ident,
 119        .ops = &atlas7_wdt_ops,
 120        .timeout = ATLAS7_WDT_DEFAULT_TIMEOUT,
 121};
 122
 123static const struct of_device_id atlas7_wdt_ids[] = {
 124        { .compatible = "sirf,atlas7-tick"},
 125        {}
 126};
 127
 128static int atlas7_wdt_probe(struct platform_device *pdev)
 129{
 130        struct device_node *np = pdev->dev.of_node;
 131        struct atlas7_wdog *wdt;
 132        struct resource *res;
 133        struct clk *clk;
 134        int ret;
 135
 136        wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
 137        if (!wdt)
 138                return -ENOMEM;
 139        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 140        wdt->base = devm_ioremap_resource(&pdev->dev, res);
 141        if (IS_ERR(wdt->base))
 142                return PTR_ERR(wdt->base);
 143
 144        clk = of_clk_get(np, 0);
 145        if (IS_ERR(clk))
 146                return PTR_ERR(clk);
 147        ret = clk_prepare_enable(clk);
 148        if (ret) {
 149                dev_err(&pdev->dev, "clk enable failed\n");
 150                goto err;
 151        }
 152
 153        /* disable watchdog hardware */
 154        writel(0, wdt->base + ATLAS7_WDT_CNT_CTRL);
 155
 156        wdt->tick_rate = clk_get_rate(clk);
 157        if (!wdt->tick_rate) {
 158                ret = -EINVAL;
 159                goto err1;
 160        }
 161
 162        wdt->clk = clk;
 163        atlas7_wdd.min_timeout = 1;
 164        atlas7_wdd.max_timeout = UINT_MAX / wdt->tick_rate;
 165
 166        watchdog_init_timeout(&atlas7_wdd, 0, &pdev->dev);
 167        watchdog_set_nowayout(&atlas7_wdd, nowayout);
 168
 169        watchdog_set_drvdata(&atlas7_wdd, wdt);
 170        platform_set_drvdata(pdev, &atlas7_wdd);
 171
 172        ret = watchdog_register_device(&atlas7_wdd);
 173        if (ret)
 174                goto err1;
 175
 176        return 0;
 177
 178err1:
 179        clk_disable_unprepare(clk);
 180err:
 181        clk_put(clk);
 182        return ret;
 183}
 184
 185static void atlas7_wdt_shutdown(struct platform_device *pdev)
 186{
 187        struct watchdog_device *wdd = platform_get_drvdata(pdev);
 188        struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
 189
 190        atlas7_wdt_disable(wdd);
 191        clk_disable_unprepare(wdt->clk);
 192}
 193
 194static int atlas7_wdt_remove(struct platform_device *pdev)
 195{
 196        struct watchdog_device *wdd = platform_get_drvdata(pdev);
 197        struct atlas7_wdog *wdt = watchdog_get_drvdata(wdd);
 198
 199        atlas7_wdt_shutdown(pdev);
 200        clk_put(wdt->clk);
 201        return 0;
 202}
 203
 204static int __maybe_unused atlas7_wdt_suspend(struct device *dev)
 205{
 206        /*
 207         * NOTE:timer controller registers settings are saved
 208         * and restored back by the timer-atlas7.c
 209         */
 210        return 0;
 211}
 212
 213static int __maybe_unused atlas7_wdt_resume(struct device *dev)
 214{
 215        struct watchdog_device *wdd = dev_get_drvdata(dev);
 216
 217        /*
 218         * NOTE: Since timer controller registers settings are saved
 219         * and restored back by the timer-atlas7.c, so we need not
 220         * update WD settings except refreshing timeout.
 221         */
 222        atlas7_wdt_ping(wdd);
 223
 224        return 0;
 225}
 226
 227static SIMPLE_DEV_PM_OPS(atlas7_wdt_pm_ops,
 228                atlas7_wdt_suspend, atlas7_wdt_resume);
 229
 230MODULE_DEVICE_TABLE(of, atlas7_wdt_ids);
 231
 232static struct platform_driver atlas7_wdt_driver = {
 233        .driver = {
 234                .name = "atlas7-wdt",
 235                .pm = &atlas7_wdt_pm_ops,
 236                .of_match_table = atlas7_wdt_ids,
 237        },
 238        .probe = atlas7_wdt_probe,
 239        .remove = atlas7_wdt_remove,
 240        .shutdown = atlas7_wdt_shutdown,
 241};
 242module_platform_driver(atlas7_wdt_driver);
 243
 244MODULE_DESCRIPTION("CSRatlas7 watchdog driver");
 245MODULE_AUTHOR("Guo Zeng <Guo.Zeng@csr.com>");
 246MODULE_LICENSE("GPL v2");
 247MODULE_ALIAS("platform:atlas7-wdt");
 248