linux/drivers/watchdog/npcm_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2// Copyright (c) 2018 Nuvoton Technology corporation.
   3// Copyright (c) 2018 IBM Corp.
   4
   5#include <linux/bitops.h>
   6#include <linux/delay.h>
   7#include <linux/interrupt.h>
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/of_irq.h>
  11#include <linux/platform_device.h>
  12#include <linux/slab.h>
  13#include <linux/watchdog.h>
  14
  15#define NPCM_WTCR       0x1C
  16
  17#define NPCM_WTCLK      (BIT(10) | BIT(11))     /* Clock divider */
  18#define NPCM_WTE        BIT(7)                  /* Enable */
  19#define NPCM_WTIE       BIT(6)                  /* Enable irq */
  20#define NPCM_WTIS       (BIT(4) | BIT(5))       /* Interval selection */
  21#define NPCM_WTIF       BIT(3)                  /* Interrupt flag*/
  22#define NPCM_WTRF       BIT(2)                  /* Reset flag */
  23#define NPCM_WTRE       BIT(1)                  /* Reset enable */
  24#define NPCM_WTR        BIT(0)                  /* Reset counter */
  25
  26/*
  27 * Watchdog timeouts
  28 *
  29 * 170     msec:    WTCLK=01 WTIS=00     VAL= 0x400
  30 * 670     msec:    WTCLK=01 WTIS=01     VAL= 0x410
  31 * 1360    msec:    WTCLK=10 WTIS=00     VAL= 0x800
  32 * 2700    msec:    WTCLK=01 WTIS=10     VAL= 0x420
  33 * 5360    msec:    WTCLK=10 WTIS=01     VAL= 0x810
  34 * 10700   msec:    WTCLK=01 WTIS=11     VAL= 0x430
  35 * 21600   msec:    WTCLK=10 WTIS=10     VAL= 0x820
  36 * 43000   msec:    WTCLK=11 WTIS=00     VAL= 0xC00
  37 * 85600   msec:    WTCLK=10 WTIS=11     VAL= 0x830
  38 * 172000  msec:    WTCLK=11 WTIS=01     VAL= 0xC10
  39 * 687000  msec:    WTCLK=11 WTIS=10     VAL= 0xC20
  40 * 2750000 msec:    WTCLK=11 WTIS=11     VAL= 0xC30
  41 */
  42
  43struct npcm_wdt {
  44        struct watchdog_device  wdd;
  45        void __iomem            *reg;
  46};
  47
  48static inline struct npcm_wdt *to_npcm_wdt(struct watchdog_device *wdd)
  49{
  50        return container_of(wdd, struct npcm_wdt, wdd);
  51}
  52
  53static int npcm_wdt_ping(struct watchdog_device *wdd)
  54{
  55        struct npcm_wdt *wdt = to_npcm_wdt(wdd);
  56        u32 val;
  57
  58        val = readl(wdt->reg);
  59        writel(val | NPCM_WTR, wdt->reg);
  60
  61        return 0;
  62}
  63
  64static int npcm_wdt_start(struct watchdog_device *wdd)
  65{
  66        struct npcm_wdt *wdt = to_npcm_wdt(wdd);
  67        u32 val;
  68
  69        if (wdd->timeout < 2)
  70                val = 0x800;
  71        else if (wdd->timeout < 3)
  72                val = 0x420;
  73        else if (wdd->timeout < 6)
  74                val = 0x810;
  75        else if (wdd->timeout < 11)
  76                val = 0x430;
  77        else if (wdd->timeout < 22)
  78                val = 0x820;
  79        else if (wdd->timeout < 44)
  80                val = 0xC00;
  81        else if (wdd->timeout < 87)
  82                val = 0x830;
  83        else if (wdd->timeout < 173)
  84                val = 0xC10;
  85        else if (wdd->timeout < 688)
  86                val = 0xC20;
  87        else
  88                val = 0xC30;
  89
  90        val |= NPCM_WTRE | NPCM_WTE | NPCM_WTR | NPCM_WTIE;
  91
  92        writel(val, wdt->reg);
  93
  94        return 0;
  95}
  96
  97static int npcm_wdt_stop(struct watchdog_device *wdd)
  98{
  99        struct npcm_wdt *wdt = to_npcm_wdt(wdd);
 100
 101        writel(0, wdt->reg);
 102
 103        return 0;
 104}
 105
 106static int npcm_wdt_set_timeout(struct watchdog_device *wdd,
 107                                unsigned int timeout)
 108{
 109        if (timeout < 2)
 110                wdd->timeout = 1;
 111        else if (timeout < 3)
 112                wdd->timeout = 2;
 113        else if (timeout < 6)
 114                wdd->timeout = 5;
 115        else if (timeout < 11)
 116                wdd->timeout = 10;
 117        else if (timeout < 22)
 118                wdd->timeout = 21;
 119        else if (timeout < 44)
 120                wdd->timeout = 43;
 121        else if (timeout < 87)
 122                wdd->timeout = 86;
 123        else if (timeout < 173)
 124                wdd->timeout = 172;
 125        else if (timeout < 688)
 126                wdd->timeout = 687;
 127        else
 128                wdd->timeout = 2750;
 129
 130        if (watchdog_active(wdd))
 131                npcm_wdt_start(wdd);
 132
 133        return 0;
 134}
 135
 136static irqreturn_t npcm_wdt_interrupt(int irq, void *data)
 137{
 138        struct npcm_wdt *wdt = data;
 139
 140        watchdog_notify_pretimeout(&wdt->wdd);
 141
 142        return IRQ_HANDLED;
 143}
 144
 145static int npcm_wdt_restart(struct watchdog_device *wdd,
 146                            unsigned long action, void *data)
 147{
 148        struct npcm_wdt *wdt = to_npcm_wdt(wdd);
 149
 150        writel(NPCM_WTR | NPCM_WTRE | NPCM_WTE, wdt->reg);
 151        udelay(1000);
 152
 153        return 0;
 154}
 155
 156static bool npcm_is_running(struct watchdog_device *wdd)
 157{
 158        struct npcm_wdt *wdt = to_npcm_wdt(wdd);
 159
 160        return readl(wdt->reg) & NPCM_WTE;
 161}
 162
 163static const struct watchdog_info npcm_wdt_info = {
 164        .identity       = KBUILD_MODNAME,
 165        .options        = WDIOF_SETTIMEOUT
 166                        | WDIOF_KEEPALIVEPING
 167                        | WDIOF_MAGICCLOSE,
 168};
 169
 170static const struct watchdog_ops npcm_wdt_ops = {
 171        .owner = THIS_MODULE,
 172        .start = npcm_wdt_start,
 173        .stop = npcm_wdt_stop,
 174        .ping = npcm_wdt_ping,
 175        .set_timeout = npcm_wdt_set_timeout,
 176        .restart = npcm_wdt_restart,
 177};
 178
 179static int npcm_wdt_probe(struct platform_device *pdev)
 180{
 181        struct device *dev = &pdev->dev;
 182        struct npcm_wdt *wdt;
 183        int irq;
 184        int ret;
 185
 186        wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
 187        if (!wdt)
 188                return -ENOMEM;
 189
 190        wdt->reg = devm_platform_ioremap_resource(pdev, 0);
 191        if (IS_ERR(wdt->reg))
 192                return PTR_ERR(wdt->reg);
 193
 194        irq = platform_get_irq(pdev, 0);
 195        if (irq < 0)
 196                return irq;
 197
 198        wdt->wdd.info = &npcm_wdt_info;
 199        wdt->wdd.ops = &npcm_wdt_ops;
 200        wdt->wdd.min_timeout = 1;
 201        wdt->wdd.max_timeout = 2750;
 202        wdt->wdd.parent = dev;
 203
 204        wdt->wdd.timeout = 86;
 205        watchdog_init_timeout(&wdt->wdd, 0, dev);
 206
 207        /* Ensure timeout is able to be represented by the hardware */
 208        npcm_wdt_set_timeout(&wdt->wdd, wdt->wdd.timeout);
 209
 210        if (npcm_is_running(&wdt->wdd)) {
 211                /* Restart with the default or device-tree specified timeout */
 212                npcm_wdt_start(&wdt->wdd);
 213                set_bit(WDOG_HW_RUNNING, &wdt->wdd.status);
 214        }
 215
 216        ret = devm_request_irq(dev, irq, npcm_wdt_interrupt, 0, "watchdog",
 217                               wdt);
 218        if (ret)
 219                return ret;
 220
 221        ret = devm_watchdog_register_device(dev, &wdt->wdd);
 222        if (ret)
 223                return ret;
 224
 225        dev_info(dev, "NPCM watchdog driver enabled\n");
 226
 227        return 0;
 228}
 229
 230#ifdef CONFIG_OF
 231static const struct of_device_id npcm_wdt_match[] = {
 232        {.compatible = "nuvoton,wpcm450-wdt"},
 233        {.compatible = "nuvoton,npcm750-wdt"},
 234        {},
 235};
 236MODULE_DEVICE_TABLE(of, npcm_wdt_match);
 237#endif
 238
 239static struct platform_driver npcm_wdt_driver = {
 240        .probe          = npcm_wdt_probe,
 241        .driver         = {
 242                .name   = "npcm-wdt",
 243                .of_match_table = of_match_ptr(npcm_wdt_match),
 244        },
 245};
 246module_platform_driver(npcm_wdt_driver);
 247
 248MODULE_AUTHOR("Joel Stanley");
 249MODULE_DESCRIPTION("Watchdog driver for NPCM");
 250MODULE_LICENSE("GPL v2");
 251