linux/drivers/watchdog/sirfsoc_wdt.c
<<
>>
Prefs
   1/*
   2 * Watchdog driver for CSR SiRFprimaII and SiRFatlasVI
   3 *
   4 * Copyright (c) 2013 Cambridge Silicon Radio Limited, a CSR plc group company.
   5 *
   6 * Licensed under GPLv2 or later.
   7 */
   8
   9#include <linux/module.h>
  10#include <linux/watchdog.h>
  11#include <linux/platform_device.h>
  12#include <linux/moduleparam.h>
  13#include <linux/of.h>
  14#include <linux/io.h>
  15#include <linux/uaccess.h>
  16
  17#define CLOCK_FREQ      1000000
  18
  19#define SIRFSOC_TIMER_COUNTER_LO        0x0000
  20#define SIRFSOC_TIMER_MATCH_0           0x0008
  21#define SIRFSOC_TIMER_INT_EN            0x0024
  22#define SIRFSOC_TIMER_WATCHDOG_EN       0x0028
  23#define SIRFSOC_TIMER_LATCH             0x0030
  24#define SIRFSOC_TIMER_LATCHED_LO        0x0034
  25
  26#define SIRFSOC_TIMER_WDT_INDEX         5
  27
  28#define SIRFSOC_WDT_MIN_TIMEOUT         30              /* 30 secs */
  29#define SIRFSOC_WDT_MAX_TIMEOUT         (10 * 60)       /* 10 mins */
  30#define SIRFSOC_WDT_DEFAULT_TIMEOUT     30              /* 30 secs */
  31
  32static unsigned int timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT;
  33static bool nowayout = WATCHDOG_NOWAYOUT;
  34
  35module_param(timeout, uint, 0);
  36module_param(nowayout, bool, 0);
  37
  38MODULE_PARM_DESC(timeout, "Default watchdog timeout (in seconds)");
  39MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
  40                        __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
  41
  42static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd)
  43{
  44        u32 counter, match;
  45        void __iomem *wdt_base;
  46        int time_left;
  47
  48        wdt_base = watchdog_get_drvdata(wdd);
  49        counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO);
  50        match = readl(wdt_base +
  51                SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
  52
  53        time_left = match - counter;
  54
  55        return time_left / CLOCK_FREQ;
  56}
  57
  58static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
  59{
  60        u32 counter, timeout_ticks;
  61        void __iomem *wdt_base;
  62
  63        timeout_ticks = wdd->timeout * CLOCK_FREQ;
  64        wdt_base = watchdog_get_drvdata(wdd);
  65
  66        /* Enable the latch before reading the LATCH_LO register */
  67        writel(1, wdt_base + SIRFSOC_TIMER_LATCH);
  68
  69        /* Set the TO value */
  70        counter = readl(wdt_base + SIRFSOC_TIMER_LATCHED_LO);
  71
  72        counter += timeout_ticks;
  73
  74        writel(counter, wdt_base +
  75                SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
  76
  77        return 0;
  78}
  79
  80static int sirfsoc_wdt_enable(struct watchdog_device *wdd)
  81{
  82        void __iomem *wdt_base = watchdog_get_drvdata(wdd);
  83        sirfsoc_wdt_updatetimeout(wdd);
  84
  85        /*
  86         * NOTE: If interrupt is not enabled
  87         * then WD-Reset doesn't get generated at all.
  88         */
  89        writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
  90                | (1 << SIRFSOC_TIMER_WDT_INDEX),
  91                wdt_base + SIRFSOC_TIMER_INT_EN);
  92        writel(1, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
  93
  94        return 0;
  95}
  96
  97static int sirfsoc_wdt_disable(struct watchdog_device *wdd)
  98{
  99        void __iomem *wdt_base = watchdog_get_drvdata(wdd);
 100
 101        writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
 102        writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
 103                & (~(1 << SIRFSOC_TIMER_WDT_INDEX)),
 104                wdt_base + SIRFSOC_TIMER_INT_EN);
 105
 106        return 0;
 107}
 108
 109static int sirfsoc_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
 110{
 111        wdd->timeout = to;
 112        sirfsoc_wdt_updatetimeout(wdd);
 113
 114        return 0;
 115}
 116
 117#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
 118
 119static const struct watchdog_info sirfsoc_wdt_ident = {
 120        .options          =     OPTIONS,
 121        .firmware_version =     0,
 122        .identity         =     "SiRFSOC Watchdog",
 123};
 124
 125static struct watchdog_ops sirfsoc_wdt_ops = {
 126        .owner = THIS_MODULE,
 127        .start = sirfsoc_wdt_enable,
 128        .stop = sirfsoc_wdt_disable,
 129        .get_timeleft = sirfsoc_wdt_gettimeleft,
 130        .ping = sirfsoc_wdt_updatetimeout,
 131        .set_timeout = sirfsoc_wdt_settimeout,
 132};
 133
 134static struct watchdog_device sirfsoc_wdd = {
 135        .info = &sirfsoc_wdt_ident,
 136        .ops = &sirfsoc_wdt_ops,
 137        .timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT,
 138        .min_timeout = SIRFSOC_WDT_MIN_TIMEOUT,
 139        .max_timeout = SIRFSOC_WDT_MAX_TIMEOUT,
 140};
 141
 142static int sirfsoc_wdt_probe(struct platform_device *pdev)
 143{
 144        struct resource *res;
 145        int ret;
 146        void __iomem *base;
 147
 148        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 149        base = devm_ioremap_resource(&pdev->dev, res);
 150        if (IS_ERR(base))
 151                return PTR_ERR(base);
 152
 153        watchdog_set_drvdata(&sirfsoc_wdd, base);
 154
 155        watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
 156        watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
 157        sirfsoc_wdd.parent = &pdev->dev;
 158
 159        ret = watchdog_register_device(&sirfsoc_wdd);
 160        if (ret)
 161                return ret;
 162
 163        platform_set_drvdata(pdev, &sirfsoc_wdd);
 164
 165        return 0;
 166}
 167
 168static void sirfsoc_wdt_shutdown(struct platform_device *pdev)
 169{
 170        struct watchdog_device *wdd = platform_get_drvdata(pdev);
 171
 172        sirfsoc_wdt_disable(wdd);
 173}
 174
 175static int sirfsoc_wdt_remove(struct platform_device *pdev)
 176{
 177        sirfsoc_wdt_shutdown(pdev);
 178        return 0;
 179}
 180
 181#ifdef  CONFIG_PM_SLEEP
 182static int sirfsoc_wdt_suspend(struct device *dev)
 183{
 184        return 0;
 185}
 186
 187static int sirfsoc_wdt_resume(struct device *dev)
 188{
 189        struct watchdog_device *wdd = dev_get_drvdata(dev);
 190
 191        /*
 192         * NOTE: Since timer controller registers settings are saved
 193         * and restored back by the timer-prima2.c, so we need not
 194         * update WD settings except refreshing timeout.
 195         */
 196        sirfsoc_wdt_updatetimeout(wdd);
 197
 198        return 0;
 199}
 200#endif
 201
 202static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops,
 203                sirfsoc_wdt_suspend, sirfsoc_wdt_resume);
 204
 205static const struct of_device_id sirfsoc_wdt_of_match[] = {
 206        { .compatible = "sirf,prima2-tick"},
 207        {},
 208};
 209MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match);
 210
 211static struct platform_driver sirfsoc_wdt_driver = {
 212        .driver = {
 213                .name = "sirfsoc-wdt",
 214                .pm = &sirfsoc_wdt_pm_ops,
 215                .of_match_table = sirfsoc_wdt_of_match,
 216        },
 217        .probe = sirfsoc_wdt_probe,
 218        .remove = sirfsoc_wdt_remove,
 219        .shutdown = sirfsoc_wdt_shutdown,
 220};
 221module_platform_driver(sirfsoc_wdt_driver);
 222
 223MODULE_DESCRIPTION("SiRF SoC watchdog driver");
 224MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>");
 225MODULE_LICENSE("GPL v2");
 226MODULE_ALIAS("platform:sirfsoc-wdt");
 227