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
 158        ret = watchdog_register_device(&sirfsoc_wdd);
 159        if (ret)
 160                return ret;
 161
 162        platform_set_drvdata(pdev, &sirfsoc_wdd);
 163
 164        return 0;
 165}
 166
 167static void sirfsoc_wdt_shutdown(struct platform_device *pdev)
 168{
 169        struct watchdog_device *wdd = platform_get_drvdata(pdev);
 170
 171        sirfsoc_wdt_disable(wdd);
 172}
 173
 174static int sirfsoc_wdt_remove(struct platform_device *pdev)
 175{
 176        sirfsoc_wdt_shutdown(pdev);
 177        return 0;
 178}
 179
 180#ifdef  CONFIG_PM_SLEEP
 181static int sirfsoc_wdt_suspend(struct device *dev)
 182{
 183        return 0;
 184}
 185
 186static int sirfsoc_wdt_resume(struct device *dev)
 187{
 188        struct watchdog_device *wdd = dev_get_drvdata(dev);
 189
 190        /*
 191         * NOTE: Since timer controller registers settings are saved
 192         * and restored back by the timer-prima2.c, so we need not
 193         * update WD settings except refreshing timeout.
 194         */
 195        sirfsoc_wdt_updatetimeout(wdd);
 196
 197        return 0;
 198}
 199#endif
 200
 201static SIMPLE_DEV_PM_OPS(sirfsoc_wdt_pm_ops,
 202                sirfsoc_wdt_suspend, sirfsoc_wdt_resume);
 203
 204static const struct of_device_id sirfsoc_wdt_of_match[] = {
 205        { .compatible = "sirf,prima2-tick"},
 206        {},
 207};
 208MODULE_DEVICE_TABLE(of, sirfsoc_wdt_of_match);
 209
 210static struct platform_driver sirfsoc_wdt_driver = {
 211        .driver = {
 212                .name = "sirfsoc-wdt",
 213                .pm = &sirfsoc_wdt_pm_ops,
 214                .of_match_table = sirfsoc_wdt_of_match,
 215        },
 216        .probe = sirfsoc_wdt_probe,
 217        .remove = sirfsoc_wdt_remove,
 218        .shutdown = sirfsoc_wdt_shutdown,
 219};
 220module_platform_driver(sirfsoc_wdt_driver);
 221
 222MODULE_DESCRIPTION("SiRF SoC watchdog driver");
 223MODULE_AUTHOR("Xianglong Du <Xianglong.Du@csr.com>");
 224MODULE_LICENSE("GPL v2");
 225MODULE_ALIAS("platform:sirfsoc-wdt");
 226