root/drivers/watchdog/pic32-dmt.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. dmt_enable
  2. dmt_disable
  3. dmt_bad_status
  4. dmt_keepalive
  5. pic32_dmt_get_timeout_secs
  6. pic32_dmt_bootstatus
  7. pic32_dmt_start
  8. pic32_dmt_stop
  9. pic32_dmt_ping
  10. pic32_clk_disable_unprepare
  11. pic32_dmt_probe

   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 
  43 struct pic32_dmt {
  44         void __iomem    *regs;
  45         struct clk      *clk;
  46 };
  47 
  48 static inline void dmt_enable(struct pic32_dmt *dmt)
  49 {
  50         writel(DMT_ON, PIC32_SET(dmt->regs + DMTCON_REG));
  51 }
  52 
  53 static 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 
  63 static 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 
  75 static 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 
  97 static 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 
 108 static 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 
 125 static 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 
 133 static 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 
 142 static 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 
 149 static 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 
 156 static const struct watchdog_info pic32_dmt_ident = {
 157         .options        = WDIOF_KEEPALIVEPING |
 158                           WDIOF_MAGICCLOSE,
 159         .identity       = "PIC32 Deadman Timer",
 160 };
 161 
 162 static struct watchdog_device pic32_dmt_wdd = {
 163         .info           = &pic32_dmt_ident,
 164         .ops            = &pic32_dmt_fops,
 165 };
 166 
 167 static void pic32_clk_disable_unprepare(void *data)
 168 {
 169         clk_disable_unprepare(data);
 170 }
 171 
 172 static 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 
 222 static const struct of_device_id pic32_dmt_of_ids[] = {
 223         { .compatible = "microchip,pic32mzda-dmt",},
 224         { /* sentinel */ }
 225 };
 226 MODULE_DEVICE_TABLE(of, pic32_dmt_of_ids);
 227 
 228 static 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 
 236 module_platform_driver(pic32_dmt_driver);
 237 
 238 MODULE_AUTHOR("Purna Chandra Mandal <purna.mandal@microchip.com>");
 239 MODULE_DESCRIPTION("Microchip PIC32 DMT Driver");
 240 MODULE_LICENSE("GPL");

/* [<][>][^][v][top][bottom][index][help] */