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