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;
  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 void __iomem *sirfsoc_wdt_base(struct watchdog_device *wdd)
  43{
  44        return (void __iomem __force *)watchdog_get_drvdata(wdd);
  45}
  46
  47static unsigned int sirfsoc_wdt_gettimeleft(struct watchdog_device *wdd)
  48{
  49        u32 counter, match;
  50        void __iomem *wdt_base;
  51        int time_left;
  52
  53        wdt_base = sirfsoc_wdt_base(wdd);
  54        counter = readl(wdt_base + SIRFSOC_TIMER_COUNTER_LO);
  55        match = readl(wdt_base +
  56                SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
  57
  58        time_left = match - counter;
  59
  60        return time_left / CLOCK_FREQ;
  61}
  62
  63static int sirfsoc_wdt_updatetimeout(struct watchdog_device *wdd)
  64{
  65        u32 counter, timeout_ticks;
  66        void __iomem *wdt_base;
  67
  68        timeout_ticks = wdd->timeout * CLOCK_FREQ;
  69        wdt_base = sirfsoc_wdt_base(wdd);
  70
  71        /* Enable the latch before reading the LATCH_LO register */
  72        writel(1, wdt_base + SIRFSOC_TIMER_LATCH);
  73
  74        /* Set the TO value */
  75        counter = readl(wdt_base + SIRFSOC_TIMER_LATCHED_LO);
  76
  77        counter += timeout_ticks;
  78
  79        writel(counter, wdt_base +
  80                SIRFSOC_TIMER_MATCH_0 + (SIRFSOC_TIMER_WDT_INDEX << 2));
  81
  82        return 0;
  83}
  84
  85static int sirfsoc_wdt_enable(struct watchdog_device *wdd)
  86{
  87        void __iomem *wdt_base = sirfsoc_wdt_base(wdd);
  88        sirfsoc_wdt_updatetimeout(wdd);
  89
  90        /*
  91         * NOTE: If interrupt is not enabled
  92         * then WD-Reset doesn't get generated at all.
  93         */
  94        writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
  95                | (1 << SIRFSOC_TIMER_WDT_INDEX),
  96                wdt_base + SIRFSOC_TIMER_INT_EN);
  97        writel(1, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
  98
  99        return 0;
 100}
 101
 102static int sirfsoc_wdt_disable(struct watchdog_device *wdd)
 103{
 104        void __iomem *wdt_base = sirfsoc_wdt_base(wdd);
 105
 106        writel(0, wdt_base + SIRFSOC_TIMER_WATCHDOG_EN);
 107        writel(readl(wdt_base + SIRFSOC_TIMER_INT_EN)
 108                & (~(1 << SIRFSOC_TIMER_WDT_INDEX)),
 109                wdt_base + SIRFSOC_TIMER_INT_EN);
 110
 111        return 0;
 112}
 113
 114static int sirfsoc_wdt_settimeout(struct watchdog_device *wdd, unsigned int to)
 115{
 116        wdd->timeout = to;
 117        sirfsoc_wdt_updatetimeout(wdd);
 118
 119        return 0;
 120}
 121
 122#define OPTIONS (WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE)
 123
 124static const struct watchdog_info sirfsoc_wdt_ident = {
 125        .options          =     OPTIONS,
 126        .firmware_version =     0,
 127        .identity         =     "SiRFSOC Watchdog",
 128};
 129
 130static const struct watchdog_ops sirfsoc_wdt_ops = {
 131        .owner = THIS_MODULE,
 132        .start = sirfsoc_wdt_enable,
 133        .stop = sirfsoc_wdt_disable,
 134        .get_timeleft = sirfsoc_wdt_gettimeleft,
 135        .ping = sirfsoc_wdt_updatetimeout,
 136        .set_timeout = sirfsoc_wdt_settimeout,
 137};
 138
 139static struct watchdog_device sirfsoc_wdd = {
 140        .info = &sirfsoc_wdt_ident,
 141        .ops = &sirfsoc_wdt_ops,
 142        .timeout = SIRFSOC_WDT_DEFAULT_TIMEOUT,
 143        .min_timeout = SIRFSOC_WDT_MIN_TIMEOUT,
 144        .max_timeout = SIRFSOC_WDT_MAX_TIMEOUT,
 145};
 146
 147static int sirfsoc_wdt_probe(struct platform_device *pdev)
 148{
 149        struct resource *res;
 150        int ret;
 151        void __iomem *base;
 152
 153        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 154        base = devm_ioremap_resource(&pdev->dev, res);
 155        if (IS_ERR(base))
 156                return PTR_ERR(base);
 157
 158        watchdog_set_drvdata(&sirfsoc_wdd, (__force void *)base);
 159
 160        watchdog_init_timeout(&sirfsoc_wdd, timeout, &pdev->dev);
 161        watchdog_set_nowayout(&sirfsoc_wdd, nowayout);
 162        sirfsoc_wdd.parent = &pdev->dev;
 163
 164        ret = watchdog_register_device(&sirfsoc_wdd);
 165        if (ret)
 166                return ret;
 167
 168        platform_set_drvdata(pdev, &sirfsoc_wdd);
 169
 170        return 0;
 171}
 172
 173static void sirfsoc_wdt_shutdown(struct platform_device *pdev)
 174{
 175        struct watchdog_device *wdd = platform_get_drvdata(pdev);
 176
 177        sirfsoc_wdt_disable(wdd);
 178}
 179
 180static int sirfsoc_wdt_remove(struct platform_device *pdev)
 181{
 182        sirfsoc_wdt_shutdown(pdev);
 183        return 0;
 184}
 185
 186#ifdef  CONFIG_PM_SLEEP
 187static int sirfsoc_wdt_suspend(struct device *dev)
 188{
 189        return 0;
 190}
 191
 192static int sirfsoc_wdt_resume(struct device *dev)
 193{
 194        struct watchdog_device *wdd = dev_get_drvdata(dev);
 195
 196        /*
 197         * NOTE: Since timer controller registers settings are saved
 198         * and restored back by the timer-prima2.c, so we need not
 199         * update WD settings except refreshing timeout.
 200         */
 201        sirfsoc_wdt_updatetimeout(wdd);
 202
 203        return 0;
 204}
 205#endif
 206
 207static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops,
 208                sirfsoc_wdt_suspend, sirfsoc_wdt_resume);
 209
 210static const struct of_device_id sirfsoc_wdt_of_match[] = {
 211        { .compatible = "sirf,prima2-tick"},
 212        {},
 213};
 214MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match);
 215
 216static struct platform_driver sirfsoc_wdt_driver = {
 217        .driver = {
 218                .name = "sirfsoc-wdt",
 219                .pm = &sirfsoc_wdt_pm_ops,
 220                .of_match_table = sirfsoc_wdt_of_match,
 221        },
 222        .probe = sirfsoc_wdt_probe,
 223        .remove = sirfsoc_wdt_remove,
 224        .shutdown = sirfsoc_wdt_shutdown,
 225};
 226module_platform_driver(sirfsoc_wdt_driver);
 227
 228MODULE_DESCRIPTION("SiRF SoC watchdog driver");
 229MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>");
 230MODULE_LICENSE("GPL v2");
 231MODULE_ALIAS("platform:sirfsoc-wdt");
 232