linux/drivers/watchdog/kempld_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Kontron PLD watchdog driver
   4 *
   5 * Copyright (c) 2010-2013 Kontron Europe GmbH
   6 * Author: Michael Brunner <michael.brunner@kontron.com>
   7 *
   8 * Note: From the PLD watchdog point of view timeout and pretimeout are
   9 *       defined differently than in the kernel.
  10 *       First the pretimeout stage runs out before the timeout stage gets
  11 *       active.
  12 *
  13 * Kernel/API:                     P-----| pretimeout
  14 *               |-----------------------T timeout
  15 * Watchdog:     |-----------------P       pretimeout_stage
  16 *                                 |-----T timeout_stage
  17 */
  18
  19#include <linux/module.h>
  20#include <linux/moduleparam.h>
  21#include <linux/uaccess.h>
  22#include <linux/watchdog.h>
  23#include <linux/platform_device.h>
  24#include <linux/mfd/kempld.h>
  25
  26#define KEMPLD_WDT_STAGE_TIMEOUT(x)     (0x1b + (x) * 4)
  27#define KEMPLD_WDT_STAGE_CFG(x)         (0x18 + (x))
  28#define STAGE_CFG_GET_PRESCALER(x)      (((x) & 0x30) >> 4)
  29#define STAGE_CFG_SET_PRESCALER(x)      (((x) & 0x3) << 4)
  30#define STAGE_CFG_PRESCALER_MASK        0x30
  31#define STAGE_CFG_ACTION_MASK           0x7
  32#define STAGE_CFG_ASSERT                (1 << 3)
  33
  34#define KEMPLD_WDT_MAX_STAGES           2
  35#define KEMPLD_WDT_KICK                 0x16
  36#define KEMPLD_WDT_CFG                  0x17
  37#define KEMPLD_WDT_CFG_ENABLE           0x10
  38#define KEMPLD_WDT_CFG_ENABLE_LOCK      0x8
  39#define KEMPLD_WDT_CFG_GLOBAL_LOCK      0x80
  40
  41enum {
  42        ACTION_NONE = 0,
  43        ACTION_RESET,
  44        ACTION_NMI,
  45        ACTION_SMI,
  46        ACTION_SCI,
  47        ACTION_DELAY,
  48};
  49
  50enum {
  51        STAGE_TIMEOUT = 0,
  52        STAGE_PRETIMEOUT,
  53};
  54
  55enum {
  56        PRESCALER_21 = 0,
  57        PRESCALER_17,
  58        PRESCALER_12,
  59};
  60
  61static const u32 kempld_prescaler[] = {
  62        [PRESCALER_21] = (1 << 21) - 1,
  63        [PRESCALER_17] = (1 << 17) - 1,
  64        [PRESCALER_12] = (1 << 12) - 1,
  65        0,
  66};
  67
  68struct kempld_wdt_stage {
  69        unsigned int    id;
  70        u32             mask;
  71};
  72
  73struct kempld_wdt_data {
  74        struct kempld_device_data       *pld;
  75        struct watchdog_device          wdd;
  76        unsigned int                    pretimeout;
  77        struct kempld_wdt_stage         stage[KEMPLD_WDT_MAX_STAGES];
  78#ifdef CONFIG_PM
  79        u8                              pm_status_store;
  80#endif
  81};
  82
  83#define DEFAULT_TIMEOUT         30 /* seconds */
  84#define DEFAULT_PRETIMEOUT      0
  85
  86static unsigned int timeout = DEFAULT_TIMEOUT;
  87module_param(timeout, uint, 0);
  88MODULE_PARM_DESC(timeout,
  89        "Watchdog timeout in seconds. (>=0, default="
  90        __MODULE_STRING(DEFAULT_TIMEOUT) ")");
  91
  92static unsigned int pretimeout = DEFAULT_PRETIMEOUT;
  93module_param(pretimeout, uint, 0);
  94MODULE_PARM_DESC(pretimeout,
  95        "Watchdog pretimeout in seconds. (>=0, default="
  96        __MODULE_STRING(DEFAULT_PRETIMEOUT) ")");
  97
  98static bool nowayout = WATCHDOG_NOWAYOUT;
  99module_param(nowayout, bool, 0);
 100MODULE_PARM_DESC(nowayout,
 101        "Watchdog cannot be stopped once started (default="
 102        __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 103
 104static int kempld_wdt_set_stage_action(struct kempld_wdt_data *wdt_data,
 105                                        struct kempld_wdt_stage *stage,
 106                                        u8 action)
 107{
 108        struct kempld_device_data *pld = wdt_data->pld;
 109        u8 stage_cfg;
 110
 111        if (!stage || !stage->mask)
 112                return -EINVAL;
 113
 114        kempld_get_mutex(pld);
 115        stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
 116        stage_cfg &= ~STAGE_CFG_ACTION_MASK;
 117        stage_cfg |= (action & STAGE_CFG_ACTION_MASK);
 118
 119        if (action == ACTION_RESET)
 120                stage_cfg |= STAGE_CFG_ASSERT;
 121        else
 122                stage_cfg &= ~STAGE_CFG_ASSERT;
 123
 124        kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
 125        kempld_release_mutex(pld);
 126
 127        return 0;
 128}
 129
 130static int kempld_wdt_set_stage_timeout(struct kempld_wdt_data *wdt_data,
 131                                        struct kempld_wdt_stage *stage,
 132                                        unsigned int timeout)
 133{
 134        struct kempld_device_data *pld = wdt_data->pld;
 135        u32 prescaler;
 136        u64 stage_timeout64;
 137        u32 stage_timeout;
 138        u32 remainder;
 139        u8 stage_cfg;
 140
 141        prescaler = kempld_prescaler[PRESCALER_21];
 142
 143        if (!stage)
 144                return -EINVAL;
 145
 146        stage_timeout64 = (u64)timeout * pld->pld_clock;
 147        remainder = do_div(stage_timeout64, prescaler);
 148        if (remainder)
 149                stage_timeout64++;
 150
 151        if (stage_timeout64 > stage->mask)
 152                return -EINVAL;
 153
 154        stage_timeout = stage_timeout64 & stage->mask;
 155
 156        kempld_get_mutex(pld);
 157        stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
 158        stage_cfg &= ~STAGE_CFG_PRESCALER_MASK;
 159        stage_cfg |= STAGE_CFG_SET_PRESCALER(PRESCALER_21);
 160        kempld_write8(pld, KEMPLD_WDT_STAGE_CFG(stage->id), stage_cfg);
 161        kempld_write32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id),
 162                        stage_timeout);
 163        kempld_release_mutex(pld);
 164
 165        return 0;
 166}
 167
 168/*
 169 * kempld_get_mutex must be called prior to calling this function.
 170 */
 171static unsigned int kempld_wdt_get_timeout(struct kempld_wdt_data *wdt_data,
 172                                                struct kempld_wdt_stage *stage)
 173{
 174        struct kempld_device_data *pld = wdt_data->pld;
 175        unsigned int timeout;
 176        u64 stage_timeout;
 177        u32 prescaler;
 178        u32 remainder;
 179        u8 stage_cfg;
 180
 181        if (!stage->mask)
 182                return 0;
 183
 184        stage_cfg = kempld_read8(pld, KEMPLD_WDT_STAGE_CFG(stage->id));
 185        stage_timeout = kempld_read32(pld, KEMPLD_WDT_STAGE_TIMEOUT(stage->id));
 186        prescaler = kempld_prescaler[STAGE_CFG_GET_PRESCALER(stage_cfg)];
 187
 188        stage_timeout = (stage_timeout & stage->mask) * prescaler;
 189        remainder = do_div(stage_timeout, pld->pld_clock);
 190        if (remainder)
 191                stage_timeout++;
 192
 193        timeout = stage_timeout;
 194        WARN_ON_ONCE(timeout != stage_timeout);
 195
 196        return timeout;
 197}
 198
 199static int kempld_wdt_set_timeout(struct watchdog_device *wdd,
 200                                        unsigned int timeout)
 201{
 202        struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 203        struct kempld_wdt_stage *pretimeout_stage;
 204        struct kempld_wdt_stage *timeout_stage;
 205        int ret;
 206
 207        timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
 208        pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
 209
 210        if (pretimeout_stage->mask && wdt_data->pretimeout > 0)
 211                timeout = wdt_data->pretimeout;
 212
 213        ret = kempld_wdt_set_stage_action(wdt_data, timeout_stage,
 214                                                ACTION_RESET);
 215        if (ret)
 216                return ret;
 217        ret = kempld_wdt_set_stage_timeout(wdt_data, timeout_stage,
 218                                                timeout);
 219        if (ret)
 220                return ret;
 221
 222        wdd->timeout = timeout;
 223        return 0;
 224}
 225
 226static int kempld_wdt_set_pretimeout(struct watchdog_device *wdd,
 227                                        unsigned int pretimeout)
 228{
 229        struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 230        struct kempld_wdt_stage *pretimeout_stage;
 231        u8 action = ACTION_NONE;
 232        int ret;
 233
 234        pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
 235
 236        if (!pretimeout_stage->mask)
 237                return -ENXIO;
 238
 239        if (pretimeout > wdd->timeout)
 240                return -EINVAL;
 241
 242        if (pretimeout > 0)
 243                action = ACTION_NMI;
 244
 245        ret = kempld_wdt_set_stage_action(wdt_data, pretimeout_stage,
 246                                                action);
 247        if (ret)
 248                return ret;
 249        ret = kempld_wdt_set_stage_timeout(wdt_data, pretimeout_stage,
 250                                                wdd->timeout - pretimeout);
 251        if (ret)
 252                return ret;
 253
 254        wdt_data->pretimeout = pretimeout;
 255        return 0;
 256}
 257
 258static void kempld_wdt_update_timeouts(struct kempld_wdt_data *wdt_data)
 259{
 260        struct kempld_device_data *pld = wdt_data->pld;
 261        struct kempld_wdt_stage *pretimeout_stage;
 262        struct kempld_wdt_stage *timeout_stage;
 263        unsigned int pretimeout, timeout;
 264
 265        pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
 266        timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
 267
 268        kempld_get_mutex(pld);
 269        pretimeout = kempld_wdt_get_timeout(wdt_data, pretimeout_stage);
 270        timeout = kempld_wdt_get_timeout(wdt_data, timeout_stage);
 271        kempld_release_mutex(pld);
 272
 273        if (pretimeout)
 274                wdt_data->pretimeout = timeout;
 275        else
 276                wdt_data->pretimeout = 0;
 277
 278        wdt_data->wdd.timeout = pretimeout + timeout;
 279}
 280
 281static int kempld_wdt_start(struct watchdog_device *wdd)
 282{
 283        struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 284        struct kempld_device_data *pld = wdt_data->pld;
 285        u8 status;
 286        int ret;
 287
 288        ret = kempld_wdt_set_timeout(wdd, wdd->timeout);
 289        if (ret)
 290                return ret;
 291
 292        kempld_get_mutex(pld);
 293        status = kempld_read8(pld, KEMPLD_WDT_CFG);
 294        status |= KEMPLD_WDT_CFG_ENABLE;
 295        kempld_write8(pld, KEMPLD_WDT_CFG, status);
 296        status = kempld_read8(pld, KEMPLD_WDT_CFG);
 297        kempld_release_mutex(pld);
 298
 299        /* Check if the watchdog was enabled */
 300        if (!(status & KEMPLD_WDT_CFG_ENABLE))
 301                return -EACCES;
 302
 303        return 0;
 304}
 305
 306static int kempld_wdt_stop(struct watchdog_device *wdd)
 307{
 308        struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 309        struct kempld_device_data *pld = wdt_data->pld;
 310        u8 status;
 311
 312        kempld_get_mutex(pld);
 313        status = kempld_read8(pld, KEMPLD_WDT_CFG);
 314        status &= ~KEMPLD_WDT_CFG_ENABLE;
 315        kempld_write8(pld, KEMPLD_WDT_CFG, status);
 316        status = kempld_read8(pld, KEMPLD_WDT_CFG);
 317        kempld_release_mutex(pld);
 318
 319        /* Check if the watchdog was disabled */
 320        if (status & KEMPLD_WDT_CFG_ENABLE)
 321                return -EACCES;
 322
 323        return 0;
 324}
 325
 326static int kempld_wdt_keepalive(struct watchdog_device *wdd)
 327{
 328        struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 329        struct kempld_device_data *pld = wdt_data->pld;
 330
 331        kempld_get_mutex(pld);
 332        kempld_write8(pld, KEMPLD_WDT_KICK, 'K');
 333        kempld_release_mutex(pld);
 334
 335        return 0;
 336}
 337
 338static long kempld_wdt_ioctl(struct watchdog_device *wdd, unsigned int cmd,
 339                                unsigned long arg)
 340{
 341        struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 342        void __user *argp = (void __user *)arg;
 343        int ret = -ENOIOCTLCMD;
 344        int __user *p = argp;
 345        int new_value;
 346
 347        switch (cmd) {
 348        case WDIOC_SETPRETIMEOUT:
 349                if (get_user(new_value, p))
 350                        return -EFAULT;
 351                ret = kempld_wdt_set_pretimeout(wdd, new_value);
 352                if (ret)
 353                        return ret;
 354                ret = kempld_wdt_keepalive(wdd);
 355                break;
 356        case WDIOC_GETPRETIMEOUT:
 357                ret = put_user(wdt_data->pretimeout, (int __user *)arg);
 358                break;
 359        }
 360
 361        return ret;
 362}
 363
 364static int kempld_wdt_probe_stages(struct watchdog_device *wdd)
 365{
 366        struct kempld_wdt_data *wdt_data = watchdog_get_drvdata(wdd);
 367        struct kempld_device_data *pld = wdt_data->pld;
 368        struct kempld_wdt_stage *pretimeout_stage;
 369        struct kempld_wdt_stage *timeout_stage;
 370        u8 index, data, data_orig;
 371        u32 mask;
 372        int i, j;
 373
 374        pretimeout_stage = &wdt_data->stage[STAGE_PRETIMEOUT];
 375        timeout_stage = &wdt_data->stage[STAGE_TIMEOUT];
 376
 377        pretimeout_stage->mask = 0;
 378        timeout_stage->mask = 0;
 379
 380        for (i = 0; i < 3; i++) {
 381                index = KEMPLD_WDT_STAGE_TIMEOUT(i);
 382                mask = 0;
 383
 384                kempld_get_mutex(pld);
 385                /* Probe each byte individually. */
 386                for (j = 0; j < 4; j++) {
 387                        data_orig = kempld_read8(pld, index + j);
 388                        kempld_write8(pld, index + j, 0x00);
 389                        data = kempld_read8(pld, index + j);
 390                        /* A failed write means this byte is reserved */
 391                        if (data != 0x00)
 392                                break;
 393                        kempld_write8(pld, index + j, data_orig);
 394                        mask |= 0xff << (j * 8);
 395                }
 396                kempld_release_mutex(pld);
 397
 398                /* Assign available stages to timeout and pretimeout */
 399                if (!timeout_stage->mask) {
 400                        timeout_stage->mask = mask;
 401                        timeout_stage->id = i;
 402                } else {
 403                        if (pld->feature_mask & KEMPLD_FEATURE_BIT_NMI) {
 404                                pretimeout_stage->mask = timeout_stage->mask;
 405                                timeout_stage->mask = mask;
 406                                pretimeout_stage->id = timeout_stage->id;
 407                                timeout_stage->id = i;
 408                        }
 409                        break;
 410                }
 411        }
 412
 413        if (!timeout_stage->mask)
 414                return -ENODEV;
 415
 416        return 0;
 417}
 418
 419static const struct watchdog_info kempld_wdt_info = {
 420        .identity       = "KEMPLD Watchdog",
 421        .options        = WDIOF_SETTIMEOUT |
 422                        WDIOF_KEEPALIVEPING |
 423                        WDIOF_MAGICCLOSE |
 424                        WDIOF_PRETIMEOUT
 425};
 426
 427static const struct watchdog_ops kempld_wdt_ops = {
 428        .owner          = THIS_MODULE,
 429        .start          = kempld_wdt_start,
 430        .stop           = kempld_wdt_stop,
 431        .ping           = kempld_wdt_keepalive,
 432        .set_timeout    = kempld_wdt_set_timeout,
 433        .ioctl          = kempld_wdt_ioctl,
 434};
 435
 436static int kempld_wdt_probe(struct platform_device *pdev)
 437{
 438        struct kempld_device_data *pld = dev_get_drvdata(pdev->dev.parent);
 439        struct kempld_wdt_data *wdt_data;
 440        struct device *dev = &pdev->dev;
 441        struct watchdog_device *wdd;
 442        u8 status;
 443        int ret = 0;
 444
 445        wdt_data = devm_kzalloc(dev, sizeof(*wdt_data), GFP_KERNEL);
 446        if (!wdt_data)
 447                return -ENOMEM;
 448
 449        wdt_data->pld = pld;
 450        wdd = &wdt_data->wdd;
 451        wdd->parent = dev;
 452
 453        kempld_get_mutex(pld);
 454        status = kempld_read8(pld, KEMPLD_WDT_CFG);
 455        kempld_release_mutex(pld);
 456
 457        /* Enable nowayout if watchdog is already locked */
 458        if (status & (KEMPLD_WDT_CFG_ENABLE_LOCK |
 459                        KEMPLD_WDT_CFG_GLOBAL_LOCK)) {
 460                if (!nowayout)
 461                        dev_warn(dev,
 462                                 "Forcing nowayout - watchdog lock enabled!\n");
 463                nowayout = true;
 464        }
 465
 466        wdd->info = &kempld_wdt_info;
 467        wdd->ops = &kempld_wdt_ops;
 468
 469        watchdog_set_drvdata(wdd, wdt_data);
 470        watchdog_set_nowayout(wdd, nowayout);
 471
 472        ret = kempld_wdt_probe_stages(wdd);
 473        if (ret)
 474                return ret;
 475
 476        kempld_wdt_set_timeout(wdd, timeout);
 477        kempld_wdt_set_pretimeout(wdd, pretimeout);
 478
 479        /* Check if watchdog is already enabled */
 480        if (status & KEMPLD_WDT_CFG_ENABLE) {
 481                /* Get current watchdog settings */
 482                kempld_wdt_update_timeouts(wdt_data);
 483                dev_info(dev, "Watchdog was already enabled\n");
 484        }
 485
 486        platform_set_drvdata(pdev, wdt_data);
 487        watchdog_stop_on_reboot(wdd);
 488        watchdog_stop_on_unregister(wdd);
 489        ret = devm_watchdog_register_device(dev, wdd);
 490        if (ret)
 491                return ret;
 492
 493        dev_info(dev, "Watchdog registered with %ds timeout\n", wdd->timeout);
 494
 495        return 0;
 496}
 497
 498#ifdef CONFIG_PM
 499/* Disable watchdog if it is active during suspend */
 500static int kempld_wdt_suspend(struct platform_device *pdev,
 501                                pm_message_t message)
 502{
 503        struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
 504        struct kempld_device_data *pld = wdt_data->pld;
 505        struct watchdog_device *wdd = &wdt_data->wdd;
 506
 507        kempld_get_mutex(pld);
 508        wdt_data->pm_status_store = kempld_read8(pld, KEMPLD_WDT_CFG);
 509        kempld_release_mutex(pld);
 510
 511        kempld_wdt_update_timeouts(wdt_data);
 512
 513        if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
 514                return kempld_wdt_stop(wdd);
 515
 516        return 0;
 517}
 518
 519/* Enable watchdog and configure it if necessary */
 520static int kempld_wdt_resume(struct platform_device *pdev)
 521{
 522        struct kempld_wdt_data *wdt_data = platform_get_drvdata(pdev);
 523        struct watchdog_device *wdd = &wdt_data->wdd;
 524
 525        /*
 526         * If watchdog was stopped before suspend be sure it gets disabled
 527         * again, for the case BIOS has enabled it during resume
 528         */
 529        if (wdt_data->pm_status_store & KEMPLD_WDT_CFG_ENABLE)
 530                return kempld_wdt_start(wdd);
 531        else
 532                return kempld_wdt_stop(wdd);
 533}
 534#else
 535#define kempld_wdt_suspend      NULL
 536#define kempld_wdt_resume       NULL
 537#endif
 538
 539static struct platform_driver kempld_wdt_driver = {
 540        .driver         = {
 541                .name   = "kempld-wdt",
 542        },
 543        .probe          = kempld_wdt_probe,
 544        .suspend        = kempld_wdt_suspend,
 545        .resume         = kempld_wdt_resume,
 546};
 547
 548module_platform_driver(kempld_wdt_driver);
 549
 550MODULE_DESCRIPTION("KEM PLD Watchdog Driver");
 551MODULE_AUTHOR("Michael Brunner <michael.brunner@kontron.com>");
 552MODULE_LICENSE("GPL");
 553