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