linux/drivers/watchdog/pic32-dmt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 * PIC32 deadman timer driver
   4 *
   5 * Purna Chandra Mandal <purna.mandal@microchip.com>
   6 * Copyright (c) 2016, Microchip Technology Inc.
   7 */
   8#include <linux/clk.h>
   9#include <linux/device.h>
  10#include <linux/err.h>
  11#include <linux/io.h>
  12#include <linux/kernel.h>
  13#include <linux/module.h>
  14#include <linux/of.h>
  15#include <linux/of_device.h>
  16#include <linux/platform_device.h>
  17#include <linux/pm.h>
  18#include <linux/watchdog.h>
  19
  20#include <asm/mach-pic32/pic32.h>
  21
  22/* Deadman Timer Regs */
  23#define DMTCON_REG      0x00
  24#define DMTPRECLR_REG   0x10
  25#define DMTCLR_REG      0x20
  26#define DMTSTAT_REG     0x30
  27#define DMTCNT_REG      0x40
  28#define DMTPSCNT_REG    0x60
  29#define DMTPSINTV_REG   0x70
  30
  31/* Deadman Timer Regs fields */
  32#define DMT_ON                  BIT(15)
  33#define DMT_STEP1_KEY           BIT(6)
  34#define DMT_STEP2_KEY           BIT(3)
  35#define DMTSTAT_WINOPN          BIT(0)
  36#define DMTSTAT_EVENT           BIT(5)
  37#define DMTSTAT_BAD2            BIT(6)
  38#define DMTSTAT_BAD1            BIT(7)
  39
  40/* Reset Control Register fields for watchdog */
  41#define RESETCON_DMT_TIMEOUT    BIT(5)
  42
  43struct pic32_dmt {
  44        void __iomem    *regs;
  45        struct clk      *clk;
  46};
  47
  48static inline void dmt_enable(struct pic32_dmt *dmt)
  49{
  50        writel(DMT_ON, PIC32_SET(dmt->regs + DMTCON_REG));
  51}
  52
  53static inline void dmt_disable(struct pic32_dmt *dmt)
  54{
  55        writel(DMT_ON, PIC32_CLR(dmt->regs + DMTCON_REG));
  56        /*
  57         * Cannot touch registers in the CPU cycle following clearing the
  58         * ON bit.
  59         */
  60        nop();
  61}
  62
  63static inline int dmt_bad_status(struct pic32_dmt *dmt)
  64{
  65        u32 val;
  66
  67        val = readl(dmt->regs + DMTSTAT_REG);
  68        val &= (DMTSTAT_BAD1 | DMTSTAT_BAD2 | DMTSTAT_EVENT);
  69        if (val)
  70                return -EAGAIN;
  71
  72        return 0;
  73}
  74
  75static inline int dmt_keepalive(struct pic32_dmt *dmt)
  76{
  77        u32 v;
  78        u32 timeout = 500;
  79
  80        /* set pre-clear key */
  81        writel(DMT_STEP1_KEY << 8, dmt->regs + DMTPRECLR_REG);
  82
  83        /* wait for DMT window to open */
  84        while (--timeout) {
  85                v = readl(dmt->regs + DMTSTAT_REG) & DMTSTAT_WINOPN;
  86                if (v == DMTSTAT_WINOPN)
  87                        break;
  88        }
  89
  90        /* apply key2 */
  91        writel(DMT_STEP2_KEY, dmt->regs + DMTCLR_REG);
  92
  93        /* check whether keys are latched correctly */
  94        return dmt_bad_status(dmt);
  95}
  96
  97static inline u32 pic32_dmt_get_timeout_secs(struct pic32_dmt *dmt)
  98{
  99        unsigned long rate;
 100
 101        rate = clk_get_rate(dmt->clk);
 102        if (rate)
 103                return readl(dmt->regs + DMTPSCNT_REG) / rate;
 104
 105        return 0;
 106}
 107
 108static inline u32 pic32_dmt_bootstatus(struct pic32_dmt *dmt)
 109{
 110        u32 v;
 111        void __iomem *rst_base;
 112
 113        rst_base = ioremap(PIC32_BASE_RESET, 0x10);
 114        if (!rst_base)
 115                return 0;
 116
 117        v = readl(rst_base);
 118
 119        writel(RESETCON_DMT_TIMEOUT, PIC32_CLR(rst_base));
 120
 121        iounmap(rst_base);
 122        return v & RESETCON_DMT_TIMEOUT;
 123}
 124
 125static int pic32_dmt_start(struct watchdog_device *wdd)
 126{
 127        struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
 128
 129        dmt_enable(dmt);
 130        return dmt_keepalive(dmt);
 131}
 132
 133static int pic32_dmt_stop(struct watchdog_device *wdd)
 134{
 135        struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
 136
 137        dmt_disable(dmt);
 138
 139        return 0;
 140}
 141
 142static int pic32_dmt_ping(struct watchdog_device *wdd)
 143{
 144        struct pic32_dmt *dmt = watchdog_get_drvdata(wdd);
 145
 146        return dmt_keepalive(dmt);
 147}
 148
 149static const struct watchdog_ops pic32_dmt_fops = {
 150        .owner          = THIS_MODULE,
 151        .start          = pic32_dmt_start,
 152        .stop           = pic32_dmt_stop,
 153        .ping           = pic32_dmt_ping,
 154};
 155
 156static const struct watchdog_info pic32_dmt_ident = {
 157        .options        = WDIOF_KEEPALIVEPING |
 158                          WDIOF_MAGICCLOSE,
 159        .identity       = "PIC32 Deadman Timer",
 160};
 161
 162static struct watchdog_device pic32_dmt_wdd = {
 163        .info           = &pic32_dmt_ident,
 164        .ops            = &pic32_dmt_fops,
 165};
 166
 167static void pic32_clk_disable_unprepare(void *data)
 168{
 169        clk_disable_unprepare(data);
 170}
 171
 172static int pic32_dmt_probe(struct platform_device *pdev)
 173{
 174        struct device *dev = &pdev->dev;
 175        int ret;
 176        struct pic32_dmt *dmt;
 177        struct watchdog_device *wdd = &pic32_dmt_wdd;
 178
 179        dmt = devm_kzalloc(dev, sizeof(*dmt), GFP_KERNEL);
 180        if (!dmt)
 181                return -ENOMEM;
 182
 183        dmt->regs = devm_platform_ioremap_resource(pdev, 0);
 184        if (IS_ERR(dmt->regs))
 185                return PTR_ERR(dmt->regs);
 186
 187        dmt->clk = devm_clk_get(dev, NULL);
 188        if (IS_ERR(dmt->clk)) {
 189                dev_err(dev, "clk not found\n");
 190                return PTR_ERR(dmt->clk);
 191        }
 192
 193        ret = clk_prepare_enable(dmt->clk);
 194        if (ret)
 195                return ret;
 196        ret = devm_add_action_or_reset(dev, pic32_clk_disable_unprepare,
 197                                       dmt->clk);
 198        if (ret)
 199                return ret;
 200
 201        wdd->timeout = pic32_dmt_get_timeout_secs(dmt);
 202        if (!wdd->timeout) {
 203                dev_err(dev, "failed to read watchdog register timeout\n");
 204                return -EINVAL;
 205        }
 206
 207        dev_info(dev, "timeout %d\n", wdd->timeout);
 208
 209        wdd->bootstatus = pic32_dmt_bootstatus(dmt) ? WDIOF_CARDRESET : 0;
 210
 211        watchdog_set_nowayout(wdd, WATCHDOG_NOWAYOUT);
 212        watchdog_set_drvdata(wdd, dmt);
 213
 214        ret = devm_watchdog_register_device(dev, wdd);
 215        if (ret)
 216                return ret;
 217
 218        platform_set_drvdata(pdev, wdd);
 219        return 0;
 220}
 221
 222static const struct of_device_id pic32_dmt_of_ids[] = {
 223        { .compatible = "microchip,pic32mzda-dmt",},
 224        { /* sentinel */ }
 225};
 226MODULE_DEVICE_TABLE(of, pic32_dmt_of_ids);
 227
 228static struct platform_driver pic32_dmt_driver = {
 229        .probe          = pic32_dmt_probe,
 230        .driver         = {
 231                .name           = "pic32-dmt",
 232                .of_match_table = of_match_ptr(pic32_dmt_of_ids),
 233        }
 234};
 235
 236module_platform_driver(pic32_dmt_driver);
 237
 238MODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>");
 239MODULE_DESCRIPTION("Microchip PIC32 DMT Driver");
 240MODULE_LICENSE("GPL");
 241