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