linux/drivers/watchdog/rti_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Watchdog driver for the K3 RTI module
   4 *
   5 * (c) Copyright 2019-2020 Texas Instruments Inc.
   6 * All rights reserved.
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/device.h>
  11#include <linux/err.h>
  12#include <linux/io.h>
  13#include <linux/kernel.h>
  14#include <linux/mod_devicetable.h>
  15#include <linux/module.h>
  16#include <linux/moduleparam.h>
  17#include <linux/platform_device.h>
  18#include <linux/pm_runtime.h>
  19#include <linux/types.h>
  20#include <linux/watchdog.h>
  21
  22#define DEFAULT_HEARTBEAT 60
  23
  24/* Max heartbeat is calculated at 32kHz source clock */
  25#define MAX_HEARTBEAT   1000
  26
  27/* Timer register set definition */
  28#define RTIDWDCTRL      0x90
  29#define RTIDWDPRLD      0x94
  30#define RTIWDSTATUS     0x98
  31#define RTIWDKEY        0x9c
  32#define RTIDWDCNTR      0xa0
  33#define RTIWWDRXCTRL    0xa4
  34#define RTIWWDSIZECTRL  0xa8
  35
  36#define RTIWWDRX_NMI    0xa
  37
  38#define RTIWWDSIZE_50P          0x50
  39#define RTIWWDSIZE_25P          0x500
  40#define RTIWWDSIZE_12P5         0x5000
  41#define RTIWWDSIZE_6P25         0x50000
  42#define RTIWWDSIZE_3P125        0x500000
  43
  44#define WDENABLE_KEY    0xa98559da
  45
  46#define WDKEY_SEQ0              0xe51a
  47#define WDKEY_SEQ1              0xa35c
  48
  49#define WDT_PRELOAD_SHIFT       13
  50
  51#define WDT_PRELOAD_MAX         0xfff
  52
  53#define DWDST                   BIT(1)
  54
  55static int heartbeat = DEFAULT_HEARTBEAT;
  56
  57/*
  58 * struct to hold data for each WDT device
  59 * @base - base io address of WD device
  60 * @freq - source clock frequency of WDT
  61 * @wdd  - hold watchdog device as is in WDT core
  62 */
  63struct rti_wdt_device {
  64        void __iomem            *base;
  65        unsigned long           freq;
  66        struct watchdog_device  wdd;
  67};
  68
  69static int rti_wdt_start(struct watchdog_device *wdd)
  70{
  71        u32 timer_margin;
  72        struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);
  73
  74        /* set timeout period */
  75        timer_margin = (u64)wdd->timeout * wdt->freq;
  76        timer_margin >>= WDT_PRELOAD_SHIFT;
  77        if (timer_margin > WDT_PRELOAD_MAX)
  78                timer_margin = WDT_PRELOAD_MAX;
  79        writel_relaxed(timer_margin, wdt->base + RTIDWDPRLD);
  80
  81        /*
  82         * RTI only supports a windowed mode, where the watchdog can only
  83         * be petted during the open window; not too early or not too late.
  84         * The HW configuration options only allow for the open window size
  85         * to be 50% or less than that; we obviouly want to configure the open
  86         * window as large as possible so we select the 50% option.
  87         */
  88        wdd->min_hw_heartbeat_ms = 500 * wdd->timeout;
  89
  90        /* Generate NMI when wdt expires */
  91        writel_relaxed(RTIWWDRX_NMI, wdt->base + RTIWWDRXCTRL);
  92
  93        /* Open window size 50%; this is the largest window size available */
  94        writel_relaxed(RTIWWDSIZE_50P, wdt->base + RTIWWDSIZECTRL);
  95
  96        readl_relaxed(wdt->base + RTIWWDSIZECTRL);
  97
  98        /* enable watchdog */
  99        writel_relaxed(WDENABLE_KEY, wdt->base + RTIDWDCTRL);
 100        return 0;
 101}
 102
 103static int rti_wdt_ping(struct watchdog_device *wdd)
 104{
 105        struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);
 106
 107        /* put watchdog in service state */
 108        writel_relaxed(WDKEY_SEQ0, wdt->base + RTIWDKEY);
 109        /* put watchdog in active state */
 110        writel_relaxed(WDKEY_SEQ1, wdt->base + RTIWDKEY);
 111
 112        return 0;
 113}
 114
 115static int rti_wdt_setup_hw_hb(struct watchdog_device *wdd, u32 wsize)
 116{
 117        /*
 118         * RTI only supports a windowed mode, where the watchdog can only
 119         * be petted during the open window; not too early or not too late.
 120         * The HW configuration options only allow for the open window size
 121         * to be 50% or less than that.
 122         */
 123        switch (wsize) {
 124        case RTIWWDSIZE_50P:
 125                /* 50% open window => 50% min heartbeat */
 126                wdd->min_hw_heartbeat_ms = 500 * heartbeat;
 127                break;
 128
 129        case RTIWWDSIZE_25P:
 130                /* 25% open window => 75% min heartbeat */
 131                wdd->min_hw_heartbeat_ms = 750 * heartbeat;
 132                break;
 133
 134        case RTIWWDSIZE_12P5:
 135                /* 12.5% open window => 87.5% min heartbeat */
 136                wdd->min_hw_heartbeat_ms = 875 * heartbeat;
 137                break;
 138
 139        case RTIWWDSIZE_6P25:
 140                /* 6.5% open window => 93.5% min heartbeat */
 141                wdd->min_hw_heartbeat_ms = 935 * heartbeat;
 142                break;
 143
 144        case RTIWWDSIZE_3P125:
 145                /* 3.125% open window => 96.9% min heartbeat */
 146                wdd->min_hw_heartbeat_ms = 969 * heartbeat;
 147                break;
 148
 149        default:
 150                return -EINVAL;
 151        }
 152
 153        return 0;
 154}
 155
 156static unsigned int rti_wdt_get_timeleft_ms(struct watchdog_device *wdd)
 157{
 158        u64 timer_counter;
 159        u32 val;
 160        struct rti_wdt_device *wdt = watchdog_get_drvdata(wdd);
 161
 162        /* if timeout has occurred then return 0 */
 163        val = readl_relaxed(wdt->base + RTIWDSTATUS);
 164        if (val & DWDST)
 165                return 0;
 166
 167        timer_counter = readl_relaxed(wdt->base + RTIDWDCNTR);
 168
 169        timer_counter *= 1000;
 170
 171        do_div(timer_counter, wdt->freq);
 172
 173        return timer_counter;
 174}
 175
 176static unsigned int rti_wdt_get_timeleft(struct watchdog_device *wdd)
 177{
 178        return rti_wdt_get_timeleft_ms(wdd) / 1000;
 179}
 180
 181static const struct watchdog_info rti_wdt_info = {
 182        .options = WDIOF_KEEPALIVEPING,
 183        .identity = "K3 RTI Watchdog",
 184};
 185
 186static const struct watchdog_ops rti_wdt_ops = {
 187        .owner          = THIS_MODULE,
 188        .start          = rti_wdt_start,
 189        .ping           = rti_wdt_ping,
 190        .get_timeleft   = rti_wdt_get_timeleft,
 191};
 192
 193static int rti_wdt_probe(struct platform_device *pdev)
 194{
 195        int ret = 0;
 196        struct device *dev = &pdev->dev;
 197        struct resource *wdt_mem;
 198        struct watchdog_device *wdd;
 199        struct rti_wdt_device *wdt;
 200        struct clk *clk;
 201        u32 last_ping = 0;
 202
 203        wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
 204        if (!wdt)
 205                return -ENOMEM;
 206
 207        clk = clk_get(dev, NULL);
 208        if (IS_ERR(clk)) {
 209                if (PTR_ERR(clk) != -EPROBE_DEFER)
 210                        dev_err(dev, "failed to get clock\n");
 211                return PTR_ERR(clk);
 212        }
 213
 214        wdt->freq = clk_get_rate(clk);
 215
 216        clk_put(clk);
 217
 218        if (!wdt->freq) {
 219                dev_err(dev, "Failed to get fck rate.\n");
 220                return -EINVAL;
 221        }
 222
 223        /*
 224         * If watchdog is running at 32k clock, it is not accurate.
 225         * Adjust frequency down in this case so that we don't pet
 226         * the watchdog too often.
 227         */
 228        if (wdt->freq < 32768)
 229                wdt->freq = wdt->freq * 9 / 10;
 230
 231        pm_runtime_enable(dev);
 232        ret = pm_runtime_get_sync(dev);
 233        if (ret) {
 234                if (ret != -EPROBE_DEFER)
 235                        dev_err(&pdev->dev, "runtime pm failed\n");
 236                return ret;
 237        }
 238
 239        platform_set_drvdata(pdev, wdt);
 240
 241        wdd = &wdt->wdd;
 242        wdd->info = &rti_wdt_info;
 243        wdd->ops = &rti_wdt_ops;
 244        wdd->min_timeout = 1;
 245        wdd->max_hw_heartbeat_ms = (WDT_PRELOAD_MAX << WDT_PRELOAD_SHIFT) /
 246                wdt->freq * 1000;
 247        wdd->parent = dev;
 248
 249        watchdog_set_drvdata(wdd, wdt);
 250        watchdog_set_nowayout(wdd, 1);
 251        watchdog_set_restart_priority(wdd, 128);
 252
 253        wdt_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 254        wdt->base = devm_ioremap_resource(dev, wdt_mem);
 255        if (IS_ERR(wdt->base)) {
 256                ret = PTR_ERR(wdt->base);
 257                goto err_iomap;
 258        }
 259
 260        if (readl(wdt->base + RTIDWDCTRL) == WDENABLE_KEY) {
 261                u32 time_left_ms;
 262                u64 heartbeat_ms;
 263                u32 wsize;
 264
 265                set_bit(WDOG_HW_RUNNING, &wdd->status);
 266                time_left_ms = rti_wdt_get_timeleft_ms(wdd);
 267                heartbeat_ms = readl(wdt->base + RTIDWDPRLD);
 268                heartbeat_ms <<= WDT_PRELOAD_SHIFT;
 269                heartbeat_ms *= 1000;
 270                do_div(heartbeat_ms, wdt->freq);
 271                if (heartbeat_ms != heartbeat * 1000)
 272                        dev_warn(dev, "watchdog already running, ignoring heartbeat config!\n");
 273
 274                heartbeat = heartbeat_ms;
 275                heartbeat /= 1000;
 276
 277                wsize = readl(wdt->base + RTIWWDSIZECTRL);
 278                ret = rti_wdt_setup_hw_hb(wdd, wsize);
 279                if (ret) {
 280                        dev_err(dev, "bad window size.\n");
 281                        goto err_iomap;
 282                }
 283
 284                last_ping = heartbeat_ms - time_left_ms;
 285                if (time_left_ms > heartbeat_ms) {
 286                        dev_warn(dev, "time_left > heartbeat? Assuming last ping just before now.\n");
 287                        last_ping = 0;
 288                }
 289        }
 290
 291        watchdog_init_timeout(wdd, heartbeat, dev);
 292
 293        ret = watchdog_register_device(wdd);
 294        if (ret) {
 295                dev_err(dev, "cannot register watchdog device\n");
 296                goto err_iomap;
 297        }
 298
 299        if (last_ping)
 300                watchdog_set_last_hw_keepalive(wdd, last_ping);
 301
 302        return 0;
 303
 304err_iomap:
 305        pm_runtime_put_sync(&pdev->dev);
 306        pm_runtime_disable(&pdev->dev);
 307
 308        return ret;
 309}
 310
 311static int rti_wdt_remove(struct platform_device *pdev)
 312{
 313        struct rti_wdt_device *wdt = platform_get_drvdata(pdev);
 314
 315        watchdog_unregister_device(&wdt->wdd);
 316        pm_runtime_put(&pdev->dev);
 317        pm_runtime_disable(&pdev->dev);
 318
 319        return 0;
 320}
 321
 322static const struct of_device_id rti_wdt_of_match[] = {
 323        { .compatible = "ti,j7-rti-wdt", },
 324        {},
 325};
 326MODULE_DEVICE_TABLE(of, rti_wdt_of_match);
 327
 328static struct platform_driver rti_wdt_driver = {
 329        .driver = {
 330                .name = "rti-wdt",
 331                .of_match_table = rti_wdt_of_match,
 332        },
 333        .probe = rti_wdt_probe,
 334        .remove = rti_wdt_remove,
 335};
 336
 337module_platform_driver(rti_wdt_driver);
 338
 339MODULE_AUTHOR("Tero Kristo <t-kristo@ti.com>");
 340MODULE_DESCRIPTION("K3 RTI Watchdog Driver");
 341
 342module_param(heartbeat, int, 0);
 343MODULE_PARM_DESC(heartbeat,
 344                 "Watchdog heartbeat period in seconds from 1 to "
 345                 __MODULE_STRING(MAX_HEARTBEAT) ", default "
 346                 __MODULE_STRING(DEFAULT_HEARTBEAT));
 347
 348MODULE_LICENSE("GPL");
 349MODULE_ALIAS("platform:rti-wdt");
 350