linux/drivers/watchdog/davinci_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * drivers/char/watchdog/davinci_wdt.c
   4 *
   5 * Watchdog driver for DaVinci DM644x/DM646x processors
   6 *
   7 * Copyright (C) 2006-2013 Texas Instruments.
   8 *
   9 * 2007 (c) MontaVista Software, Inc.
  10 */
  11
  12#include <linux/module.h>
  13#include <linux/moduleparam.h>
  14#include <linux/mod_devicetable.h>
  15#include <linux/types.h>
  16#include <linux/kernel.h>
  17#include <linux/watchdog.h>
  18#include <linux/platform_device.h>
  19#include <linux/io.h>
  20#include <linux/device.h>
  21#include <linux/clk.h>
  22#include <linux/err.h>
  23
  24#define MODULE_NAME "DAVINCI-WDT: "
  25
  26#define DEFAULT_HEARTBEAT 60
  27#define MAX_HEARTBEAT     600   /* really the max margin is 264/27MHz*/
  28
  29/* Timer register set definition */
  30#define PID12   (0x0)
  31#define EMUMGT  (0x4)
  32#define TIM12   (0x10)
  33#define TIM34   (0x14)
  34#define PRD12   (0x18)
  35#define PRD34   (0x1C)
  36#define TCR     (0x20)
  37#define TGCR    (0x24)
  38#define WDTCR   (0x28)
  39
  40/* TCR bit definitions */
  41#define ENAMODE12_DISABLED      (0 << 6)
  42#define ENAMODE12_ONESHOT       (1 << 6)
  43#define ENAMODE12_PERIODIC      (2 << 6)
  44
  45/* TGCR bit definitions */
  46#define TIM12RS_UNRESET         (1 << 0)
  47#define TIM34RS_UNRESET         (1 << 1)
  48#define TIMMODE_64BIT_WDOG      (2 << 2)
  49
  50/* WDTCR bit definitions */
  51#define WDEN                    (1 << 14)
  52#define WDFLAG                  (1 << 15)
  53#define WDKEY_SEQ0              (0xa5c6 << 16)
  54#define WDKEY_SEQ1              (0xda7e << 16)
  55
  56static int heartbeat;
  57
  58/*
  59 * struct to hold data for each WDT device
  60 * @base - base io address of WD device
  61 * @clk - source clock of WDT
  62 * @wdd - hold watchdog device as is in WDT core
  63 */
  64struct davinci_wdt_device {
  65        void __iomem            *base;
  66        struct clk              *clk;
  67        struct watchdog_device  wdd;
  68};
  69
  70static int davinci_wdt_start(struct watchdog_device *wdd)
  71{
  72        u32 tgcr;
  73        u32 timer_margin;
  74        unsigned long wdt_freq;
  75        struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
  76
  77        wdt_freq = clk_get_rate(davinci_wdt->clk);
  78
  79        /* disable, internal clock source */
  80        iowrite32(0, davinci_wdt->base + TCR);
  81        /* reset timer, set mode to 64-bit watchdog, and unreset */
  82        iowrite32(0, davinci_wdt->base + TGCR);
  83        tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
  84        iowrite32(tgcr, davinci_wdt->base + TGCR);
  85        /* clear counter regs */
  86        iowrite32(0, davinci_wdt->base + TIM12);
  87        iowrite32(0, davinci_wdt->base + TIM34);
  88        /* set timeout period */
  89        timer_margin = (((u64)wdd->timeout * wdt_freq) & 0xffffffff);
  90        iowrite32(timer_margin, davinci_wdt->base + PRD12);
  91        timer_margin = (((u64)wdd->timeout * wdt_freq) >> 32);
  92        iowrite32(timer_margin, davinci_wdt->base + PRD34);
  93        /* enable run continuously */
  94        iowrite32(ENAMODE12_PERIODIC, davinci_wdt->base + TCR);
  95        /* Once the WDT is in pre-active state write to
  96         * TIM12, TIM34, PRD12, PRD34, TCR, TGCR, WDTCR are
  97         * write protected (except for the WDKEY field)
  98         */
  99        /* put watchdog in pre-active state */
 100        iowrite32(WDKEY_SEQ0 | WDEN, davinci_wdt->base + WDTCR);
 101        /* put watchdog in active state */
 102        iowrite32(WDKEY_SEQ1 | WDEN, davinci_wdt->base + WDTCR);
 103        return 0;
 104}
 105
 106static int davinci_wdt_ping(struct watchdog_device *wdd)
 107{
 108        struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
 109
 110        /* put watchdog in service state */
 111        iowrite32(WDKEY_SEQ0, davinci_wdt->base + WDTCR);
 112        /* put watchdog in active state */
 113        iowrite32(WDKEY_SEQ1, davinci_wdt->base + WDTCR);
 114        return 0;
 115}
 116
 117static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd)
 118{
 119        u64 timer_counter;
 120        unsigned long freq;
 121        u32 val;
 122        struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
 123
 124        /* if timeout has occured then return 0 */
 125        val = ioread32(davinci_wdt->base + WDTCR);
 126        if (val & WDFLAG)
 127                return 0;
 128
 129        freq = clk_get_rate(davinci_wdt->clk);
 130
 131        if (!freq)
 132                return 0;
 133
 134        timer_counter = ioread32(davinci_wdt->base + TIM12);
 135        timer_counter |= ((u64)ioread32(davinci_wdt->base + TIM34) << 32);
 136
 137        do_div(timer_counter, freq);
 138
 139        return wdd->timeout - timer_counter;
 140}
 141
 142static int davinci_wdt_restart(struct watchdog_device *wdd,
 143                               unsigned long action, void *data)
 144{
 145        struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd);
 146        u32 tgcr, wdtcr;
 147
 148        /* disable, internal clock source */
 149        iowrite32(0, davinci_wdt->base + TCR);
 150
 151        /* reset timer, set mode to 64-bit watchdog, and unreset */
 152        tgcr = 0;
 153        iowrite32(tgcr, davinci_wdt->base + TGCR);
 154        tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET;
 155        iowrite32(tgcr, davinci_wdt->base + TGCR);
 156
 157        /* clear counter and period regs */
 158        iowrite32(0, davinci_wdt->base + TIM12);
 159        iowrite32(0, davinci_wdt->base + TIM34);
 160        iowrite32(0, davinci_wdt->base + PRD12);
 161        iowrite32(0, davinci_wdt->base + PRD34);
 162
 163        /* put watchdog in pre-active state */
 164        wdtcr = WDKEY_SEQ0 | WDEN;
 165        iowrite32(wdtcr, davinci_wdt->base + WDTCR);
 166
 167        /* put watchdog in active state */
 168        wdtcr = WDKEY_SEQ1 | WDEN;
 169        iowrite32(wdtcr, davinci_wdt->base + WDTCR);
 170
 171        /* write an invalid value to the WDKEY field to trigger a restart */
 172        wdtcr = 0x00004000;
 173        iowrite32(wdtcr, davinci_wdt->base + WDTCR);
 174
 175        return 0;
 176}
 177
 178static const struct watchdog_info davinci_wdt_info = {
 179        .options = WDIOF_KEEPALIVEPING,
 180        .identity = "DaVinci/Keystone Watchdog",
 181};
 182
 183static const struct watchdog_ops davinci_wdt_ops = {
 184        .owner          = THIS_MODULE,
 185        .start          = davinci_wdt_start,
 186        .stop           = davinci_wdt_ping,
 187        .ping           = davinci_wdt_ping,
 188        .get_timeleft   = davinci_wdt_get_timeleft,
 189        .restart        = davinci_wdt_restart,
 190};
 191
 192static void davinci_clk_disable_unprepare(void *data)
 193{
 194        clk_disable_unprepare(data);
 195}
 196
 197static int davinci_wdt_probe(struct platform_device *pdev)
 198{
 199        int ret = 0;
 200        struct device *dev = &pdev->dev;
 201        struct watchdog_device *wdd;
 202        struct davinci_wdt_device *davinci_wdt;
 203
 204        davinci_wdt = devm_kzalloc(dev, sizeof(*davinci_wdt), GFP_KERNEL);
 205        if (!davinci_wdt)
 206                return -ENOMEM;
 207
 208        davinci_wdt->clk = devm_clk_get(dev, NULL);
 209        if (IS_ERR(davinci_wdt->clk))
 210                return dev_err_probe(dev, PTR_ERR(davinci_wdt->clk),
 211                                     "failed to get clock node\n");
 212
 213        ret = clk_prepare_enable(davinci_wdt->clk);
 214        if (ret) {
 215                dev_err(dev, "failed to prepare clock\n");
 216                return ret;
 217        }
 218        ret = devm_add_action_or_reset(dev, davinci_clk_disable_unprepare,
 219                                       davinci_wdt->clk);
 220        if (ret)
 221                return ret;
 222
 223        platform_set_drvdata(pdev, davinci_wdt);
 224
 225        wdd                     = &davinci_wdt->wdd;
 226        wdd->info               = &davinci_wdt_info;
 227        wdd->ops                = &davinci_wdt_ops;
 228        wdd->min_timeout        = 1;
 229        wdd->max_timeout        = MAX_HEARTBEAT;
 230        wdd->timeout            = DEFAULT_HEARTBEAT;
 231        wdd->parent             = dev;
 232
 233        watchdog_init_timeout(wdd, heartbeat, dev);
 234
 235        dev_info(dev, "heartbeat %d sec\n", wdd->timeout);
 236
 237        watchdog_set_drvdata(wdd, davinci_wdt);
 238        watchdog_set_nowayout(wdd, 1);
 239        watchdog_set_restart_priority(wdd, 128);
 240
 241        davinci_wdt->base = devm_platform_ioremap_resource(pdev, 0);
 242        if (IS_ERR(davinci_wdt->base))
 243                return PTR_ERR(davinci_wdt->base);
 244
 245        return devm_watchdog_register_device(dev, wdd);
 246}
 247
 248static const struct of_device_id davinci_wdt_of_match[] = {
 249        { .compatible = "ti,davinci-wdt", },
 250        {},
 251};
 252MODULE_DEVICE_TABLE(of, davinci_wdt_of_match);
 253
 254static struct platform_driver platform_wdt_driver = {
 255        .driver = {
 256                .name = "davinci-wdt",
 257                .of_match_table = davinci_wdt_of_match,
 258        },
 259        .probe = davinci_wdt_probe,
 260};
 261
 262module_platform_driver(platform_wdt_driver);
 263
 264MODULE_AUTHOR("Texas Instruments");
 265MODULE_DESCRIPTION("DaVinci Watchdog Driver");
 266
 267module_param(heartbeat, int, 0);
 268MODULE_PARM_DESC(heartbeat,
 269                 "Watchdog heartbeat period in seconds from 1 to "
 270                 __MODULE_STRING(MAX_HEARTBEAT) ", default "
 271                 __MODULE_STRING(DEFAULT_HEARTBEAT));
 272
 273MODULE_LICENSE("GPL");
 274MODULE_ALIAS("platform:davinci-wdt");
 275
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.