linux/drivers/watchdog/orion_wdt.c
<<
>>
Prefs
   1/*
   2 * drivers/watchdog/orion_wdt.c
   3 *
   4 * Watchdog driver for Orion/Kirkwood processors
   5 *
   6 * Author: Sylver Bruneau <sylver.bruneau@googlemail.com>
   7 *
   8 * This file is licensed under  the terms of the GNU General Public
   9 * License version 2. This program is licensed "as is" without any
  10 * warranty of any kind, whether express or implied.
  11 */
  12
  13#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  14
  15#include <linux/module.h>
  16#include <linux/moduleparam.h>
  17#include <linux/types.h>
  18#include <linux/kernel.h>
  19#include <linux/platform_device.h>
  20#include <linux/watchdog.h>
  21#include <linux/interrupt.h>
  22#include <linux/io.h>
  23#include <linux/clk.h>
  24#include <linux/err.h>
  25#include <linux/of.h>
  26#include <linux/of_device.h>
  27
  28/* RSTOUT mask register physical address for Orion5x, Kirkwood and Dove */
  29#define ORION_RSTOUT_MASK_OFFSET        0x20108
  30
  31/* Internal registers can be configured at any 1 MiB aligned address */
  32#define INTERNAL_REGS_MASK              ~(SZ_1M - 1)
  33
  34/*
  35 * Watchdog timer block registers.
  36 */
  37#define TIMER_CTRL              0x0000
  38#define TIMER_A370_STATUS       0x04
  39
  40#define WDT_MAX_CYCLE_COUNT     0xffffffff
  41
  42#define WDT_A370_RATIO_MASK(v)  ((v) << 16)
  43#define WDT_A370_RATIO_SHIFT    5
  44#define WDT_A370_RATIO          (1 << WDT_A370_RATIO_SHIFT)
  45
  46#define WDT_AXP_FIXED_ENABLE_BIT BIT(10)
  47#define WDT_A370_EXPIRED        BIT(31)
  48
  49static bool nowayout = WATCHDOG_NOWAYOUT;
  50static int heartbeat = -1;              /* module parameter (seconds) */
  51
  52struct orion_watchdog;
  53
  54struct orion_watchdog_data {
  55        int wdt_counter_offset;
  56        int wdt_enable_bit;
  57        int rstout_enable_bit;
  58        int rstout_mask_bit;
  59        int (*clock_init)(struct platform_device *,
  60                          struct orion_watchdog *);
  61        int (*enabled)(struct orion_watchdog *);
  62        int (*start)(struct watchdog_device *);
  63        int (*stop)(struct watchdog_device *);
  64};
  65
  66struct orion_watchdog {
  67        struct watchdog_device wdt;
  68        void __iomem *reg;
  69        void __iomem *rstout;
  70        void __iomem *rstout_mask;
  71        unsigned long clk_rate;
  72        struct clk *clk;
  73        const struct orion_watchdog_data *data;
  74};
  75
  76static int orion_wdt_clock_init(struct platform_device *pdev,
  77                                struct orion_watchdog *dev)
  78{
  79        int ret;
  80
  81        dev->clk = clk_get(&pdev->dev, NULL);
  82        if (IS_ERR(dev->clk))
  83                return PTR_ERR(dev->clk);
  84        ret = clk_prepare_enable(dev->clk);
  85        if (ret) {
  86                clk_put(dev->clk);
  87                return ret;
  88        }
  89
  90        dev->clk_rate = clk_get_rate(dev->clk);
  91        return 0;
  92}
  93
  94static int armada370_wdt_clock_init(struct platform_device *pdev,
  95                                    struct orion_watchdog *dev)
  96{
  97        int ret;
  98
  99        dev->clk = clk_get(&pdev->dev, NULL);
 100        if (IS_ERR(dev->clk))
 101                return PTR_ERR(dev->clk);
 102        ret = clk_prepare_enable(dev->clk);
 103        if (ret) {
 104                clk_put(dev->clk);
 105                return ret;
 106        }
 107
 108        /* Setup watchdog input clock */
 109        atomic_io_modify(dev->reg + TIMER_CTRL,
 110                        WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT),
 111                        WDT_A370_RATIO_MASK(WDT_A370_RATIO_SHIFT));
 112
 113        dev->clk_rate = clk_get_rate(dev->clk) / WDT_A370_RATIO;
 114        return 0;
 115}
 116
 117static int armadaxp_wdt_clock_init(struct platform_device *pdev,
 118                                   struct orion_watchdog *dev)
 119{
 120        int ret;
 121
 122        dev->clk = of_clk_get_by_name(pdev->dev.of_node, "fixed");
 123        if (IS_ERR(dev->clk))
 124                return PTR_ERR(dev->clk);
 125        ret = clk_prepare_enable(dev->clk);
 126        if (ret) {
 127                clk_put(dev->clk);
 128                return ret;
 129        }
 130
 131        /* Enable the fixed watchdog clock input */
 132        atomic_io_modify(dev->reg + TIMER_CTRL,
 133                         WDT_AXP_FIXED_ENABLE_BIT,
 134                         WDT_AXP_FIXED_ENABLE_BIT);
 135
 136        dev->clk_rate = clk_get_rate(dev->clk);
 137        return 0;
 138}
 139
 140static int orion_wdt_ping(struct watchdog_device *wdt_dev)
 141{
 142        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 143        /* Reload watchdog duration */
 144        writel(dev->clk_rate * wdt_dev->timeout,
 145               dev->reg + dev->data->wdt_counter_offset);
 146        return 0;
 147}
 148
 149static int armada375_start(struct watchdog_device *wdt_dev)
 150{
 151        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 152        u32 reg;
 153
 154        /* Set watchdog duration */
 155        writel(dev->clk_rate * wdt_dev->timeout,
 156               dev->reg + dev->data->wdt_counter_offset);
 157
 158        /* Clear the watchdog expiration bit */
 159        atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
 160
 161        /* Enable watchdog timer */
 162        atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
 163                                                dev->data->wdt_enable_bit);
 164
 165        /* Enable reset on watchdog */
 166        reg = readl(dev->rstout);
 167        reg |= dev->data->rstout_enable_bit;
 168        writel(reg, dev->rstout);
 169
 170        atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit, 0);
 171        return 0;
 172}
 173
 174static int armada370_start(struct watchdog_device *wdt_dev)
 175{
 176        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 177        u32 reg;
 178
 179        /* Set watchdog duration */
 180        writel(dev->clk_rate * wdt_dev->timeout,
 181               dev->reg + dev->data->wdt_counter_offset);
 182
 183        /* Clear the watchdog expiration bit */
 184        atomic_io_modify(dev->reg + TIMER_A370_STATUS, WDT_A370_EXPIRED, 0);
 185
 186        /* Enable watchdog timer */
 187        atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
 188                                                dev->data->wdt_enable_bit);
 189
 190        /* Enable reset on watchdog */
 191        reg = readl(dev->rstout);
 192        reg |= dev->data->rstout_enable_bit;
 193        writel(reg, dev->rstout);
 194        return 0;
 195}
 196
 197static int orion_start(struct watchdog_device *wdt_dev)
 198{
 199        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 200
 201        /* Set watchdog duration */
 202        writel(dev->clk_rate * wdt_dev->timeout,
 203               dev->reg + dev->data->wdt_counter_offset);
 204
 205        /* Enable watchdog timer */
 206        atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit,
 207                                                dev->data->wdt_enable_bit);
 208
 209        /* Enable reset on watchdog */
 210        atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit,
 211                                      dev->data->rstout_enable_bit);
 212
 213        return 0;
 214}
 215
 216static int orion_wdt_start(struct watchdog_device *wdt_dev)
 217{
 218        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 219
 220        /* There are some per-SoC quirks to handle */
 221        return dev->data->start(wdt_dev);
 222}
 223
 224static int orion_stop(struct watchdog_device *wdt_dev)
 225{
 226        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 227
 228        /* Disable reset on watchdog */
 229        atomic_io_modify(dev->rstout, dev->data->rstout_enable_bit, 0);
 230
 231        /* Disable watchdog timer */
 232        atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
 233
 234        return 0;
 235}
 236
 237static int armada375_stop(struct watchdog_device *wdt_dev)
 238{
 239        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 240        u32 reg;
 241
 242        /* Disable reset on watchdog */
 243        atomic_io_modify(dev->rstout_mask, dev->data->rstout_mask_bit,
 244                                           dev->data->rstout_mask_bit);
 245        reg = readl(dev->rstout);
 246        reg &= ~dev->data->rstout_enable_bit;
 247        writel(reg, dev->rstout);
 248
 249        /* Disable watchdog timer */
 250        atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
 251
 252        return 0;
 253}
 254
 255static int armada370_stop(struct watchdog_device *wdt_dev)
 256{
 257        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 258        u32 reg;
 259
 260        /* Disable reset on watchdog */
 261        reg = readl(dev->rstout);
 262        reg &= ~dev->data->rstout_enable_bit;
 263        writel(reg, dev->rstout);
 264
 265        /* Disable watchdog timer */
 266        atomic_io_modify(dev->reg + TIMER_CTRL, dev->data->wdt_enable_bit, 0);
 267
 268        return 0;
 269}
 270
 271static int orion_wdt_stop(struct watchdog_device *wdt_dev)
 272{
 273        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 274
 275        return dev->data->stop(wdt_dev);
 276}
 277
 278static int orion_enabled(struct orion_watchdog *dev)
 279{
 280        bool enabled, running;
 281
 282        enabled = readl(dev->rstout) & dev->data->rstout_enable_bit;
 283        running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit;
 284
 285        return enabled && running;
 286}
 287
 288static int armada375_enabled(struct orion_watchdog *dev)
 289{
 290        bool masked, enabled, running;
 291
 292        masked = readl(dev->rstout_mask) & dev->data->rstout_mask_bit;
 293        enabled = readl(dev->rstout) & dev->data->rstout_enable_bit;
 294        running = readl(dev->reg + TIMER_CTRL) & dev->data->wdt_enable_bit;
 295
 296        return !masked && enabled && running;
 297}
 298
 299static int orion_wdt_enabled(struct watchdog_device *wdt_dev)
 300{
 301        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 302
 303        return dev->data->enabled(dev);
 304}
 305
 306static unsigned int orion_wdt_get_timeleft(struct watchdog_device *wdt_dev)
 307{
 308        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 309        return readl(dev->reg + dev->data->wdt_counter_offset) / dev->clk_rate;
 310}
 311
 312static int orion_wdt_set_timeout(struct watchdog_device *wdt_dev,
 313                                 unsigned int timeout)
 314{
 315        wdt_dev->timeout = timeout;
 316        return 0;
 317}
 318
 319static const struct watchdog_info orion_wdt_info = {
 320        .options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
 321        .identity = "Orion Watchdog",
 322};
 323
 324static const struct watchdog_ops orion_wdt_ops = {
 325        .owner = THIS_MODULE,
 326        .start = orion_wdt_start,
 327        .stop = orion_wdt_stop,
 328        .ping = orion_wdt_ping,
 329        .set_timeout = orion_wdt_set_timeout,
 330        .get_timeleft = orion_wdt_get_timeleft,
 331};
 332
 333static irqreturn_t orion_wdt_irq(int irq, void *devid)
 334{
 335        panic("Watchdog Timeout");
 336        return IRQ_HANDLED;
 337}
 338
 339/*
 340 * The original devicetree binding for this driver specified only
 341 * one memory resource, so in order to keep DT backwards compatibility
 342 * we try to fallback to a hardcoded register address, if the resource
 343 * is missing from the devicetree.
 344 */
 345static void __iomem *orion_wdt_ioremap_rstout(struct platform_device *pdev,
 346                                              phys_addr_t internal_regs)
 347{
 348        struct resource *res;
 349        phys_addr_t rstout;
 350
 351        res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 352        if (res)
 353                return devm_ioremap(&pdev->dev, res->start,
 354                                    resource_size(res));
 355
 356        rstout = internal_regs + ORION_RSTOUT_MASK_OFFSET;
 357
 358        WARN(1, FW_BUG "falling back to harcoded RSTOUT reg %pa\n", &rstout);
 359        return devm_ioremap(&pdev->dev, rstout, 0x4);
 360}
 361
 362static const struct orion_watchdog_data orion_data = {
 363        .rstout_enable_bit = BIT(1),
 364        .wdt_enable_bit = BIT(4),
 365        .wdt_counter_offset = 0x24,
 366        .clock_init = orion_wdt_clock_init,
 367        .enabled = orion_enabled,
 368        .start = orion_start,
 369        .stop = orion_stop,
 370};
 371
 372static const struct orion_watchdog_data armada370_data = {
 373        .rstout_enable_bit = BIT(8),
 374        .wdt_enable_bit = BIT(8),
 375        .wdt_counter_offset = 0x34,
 376        .clock_init = armada370_wdt_clock_init,
 377        .enabled = orion_enabled,
 378        .start = armada370_start,
 379        .stop = armada370_stop,
 380};
 381
 382static const struct orion_watchdog_data armadaxp_data = {
 383        .rstout_enable_bit = BIT(8),
 384        .wdt_enable_bit = BIT(8),
 385        .wdt_counter_offset = 0x34,
 386        .clock_init = armadaxp_wdt_clock_init,
 387        .enabled = orion_enabled,
 388        .start = armada370_start,
 389        .stop = armada370_stop,
 390};
 391
 392static const struct orion_watchdog_data armada375_data = {
 393        .rstout_enable_bit = BIT(8),
 394        .rstout_mask_bit = BIT(10),
 395        .wdt_enable_bit = BIT(8),
 396        .wdt_counter_offset = 0x34,
 397        .clock_init = armada370_wdt_clock_init,
 398        .enabled = armada375_enabled,
 399        .start = armada375_start,
 400        .stop = armada375_stop,
 401};
 402
 403static const struct orion_watchdog_data armada380_data = {
 404        .rstout_enable_bit = BIT(8),
 405        .rstout_mask_bit = BIT(10),
 406        .wdt_enable_bit = BIT(8),
 407        .wdt_counter_offset = 0x34,
 408        .clock_init = armadaxp_wdt_clock_init,
 409        .enabled = armada375_enabled,
 410        .start = armada375_start,
 411        .stop = armada375_stop,
 412};
 413
 414static const struct of_device_id orion_wdt_of_match_table[] = {
 415        {
 416                .compatible = "marvell,orion-wdt",
 417                .data = &orion_data,
 418        },
 419        {
 420                .compatible = "marvell,armada-370-wdt",
 421                .data = &armada370_data,
 422        },
 423        {
 424                .compatible = "marvell,armada-xp-wdt",
 425                .data = &armadaxp_data,
 426        },
 427        {
 428                .compatible = "marvell,armada-375-wdt",
 429                .data = &armada375_data,
 430        },
 431        {
 432                .compatible = "marvell,armada-380-wdt",
 433                .data = &armada380_data,
 434        },
 435        {},
 436};
 437MODULE_DEVICE_TABLE(of, orion_wdt_of_match_table);
 438
 439static int orion_wdt_get_regs(struct platform_device *pdev,
 440                              struct orion_watchdog *dev)
 441{
 442        struct device_node *node = pdev->dev.of_node;
 443        struct resource *res;
 444
 445        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 446        if (!res)
 447                return -ENODEV;
 448        dev->reg = devm_ioremap(&pdev->dev, res->start,
 449                                resource_size(res));
 450        if (!dev->reg)
 451                return -ENOMEM;
 452
 453        /* Each supported compatible has some RSTOUT register quirk */
 454        if (of_device_is_compatible(node, "marvell,orion-wdt")) {
 455
 456                dev->rstout = orion_wdt_ioremap_rstout(pdev, res->start &
 457                                                       INTERNAL_REGS_MASK);
 458                if (!dev->rstout)
 459                        return -ENODEV;
 460
 461        } else if (of_device_is_compatible(node, "marvell,armada-370-wdt") ||
 462                   of_device_is_compatible(node, "marvell,armada-xp-wdt")) {
 463
 464                /* Dedicated RSTOUT register, can be requested. */
 465                res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 466                dev->rstout = devm_ioremap_resource(&pdev->dev, res);
 467                if (IS_ERR(dev->rstout))
 468                        return PTR_ERR(dev->rstout);
 469
 470        } else if (of_device_is_compatible(node, "marvell,armada-375-wdt") ||
 471                   of_device_is_compatible(node, "marvell,armada-380-wdt")) {
 472
 473                /* Dedicated RSTOUT register, can be requested. */
 474                res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
 475                dev->rstout = devm_ioremap_resource(&pdev->dev, res);
 476                if (IS_ERR(dev->rstout))
 477                        return PTR_ERR(dev->rstout);
 478
 479                res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
 480                if (!res)
 481                        return -ENODEV;
 482                dev->rstout_mask = devm_ioremap(&pdev->dev, res->start,
 483                                                resource_size(res));
 484                if (!dev->rstout_mask)
 485                        return -ENOMEM;
 486
 487        } else {
 488                return -ENODEV;
 489        }
 490
 491        return 0;
 492}
 493
 494static int orion_wdt_probe(struct platform_device *pdev)
 495{
 496        struct orion_watchdog *dev;
 497        const struct of_device_id *match;
 498        unsigned int wdt_max_duration;  /* (seconds) */
 499        int ret, irq;
 500
 501        dev = devm_kzalloc(&pdev->dev, sizeof(struct orion_watchdog),
 502                           GFP_KERNEL);
 503        if (!dev)
 504                return -ENOMEM;
 505
 506        match = of_match_device(orion_wdt_of_match_table, &pdev->dev);
 507        if (!match)
 508                /* Default legacy match */
 509                match = &orion_wdt_of_match_table[0];
 510
 511        dev->wdt.info = &orion_wdt_info;
 512        dev->wdt.ops = &orion_wdt_ops;
 513        dev->wdt.min_timeout = 1;
 514        dev->data = match->data;
 515
 516        ret = orion_wdt_get_regs(pdev, dev);
 517        if (ret)
 518                return ret;
 519
 520        ret = dev->data->clock_init(pdev, dev);
 521        if (ret) {
 522                dev_err(&pdev->dev, "cannot initialize clock\n");
 523                return ret;
 524        }
 525
 526        wdt_max_duration = WDT_MAX_CYCLE_COUNT / dev->clk_rate;
 527
 528        dev->wdt.timeout = wdt_max_duration;
 529        dev->wdt.max_timeout = wdt_max_duration;
 530        watchdog_init_timeout(&dev->wdt, heartbeat, &pdev->dev);
 531
 532        platform_set_drvdata(pdev, &dev->wdt);
 533        watchdog_set_drvdata(&dev->wdt, dev);
 534
 535        /*
 536         * Let's make sure the watchdog is fully stopped, unless it's
 537         * explicitly enabled. This may be the case if the module was
 538         * removed and re-insterted, or if the bootloader explicitly
 539         * set a running watchdog before booting the kernel.
 540         */
 541        if (!orion_wdt_enabled(&dev->wdt))
 542                orion_wdt_stop(&dev->wdt);
 543
 544        /* Request the IRQ only after the watchdog is disabled */
 545        irq = platform_get_irq(pdev, 0);
 546        if (irq > 0) {
 547                /*
 548                 * Not all supported platforms specify an interrupt for the
 549                 * watchdog, so let's make it optional.
 550                 */
 551                ret = devm_request_irq(&pdev->dev, irq, orion_wdt_irq, 0,
 552                                       pdev->name, dev);
 553                if (ret < 0) {
 554                        dev_err(&pdev->dev, "failed to request IRQ\n");
 555                        goto disable_clk;
 556                }
 557        }
 558
 559        watchdog_set_nowayout(&dev->wdt, nowayout);
 560        ret = watchdog_register_device(&dev->wdt);
 561        if (ret)
 562                goto disable_clk;
 563
 564        pr_info("Initial timeout %d sec%s\n",
 565                dev->wdt.timeout, nowayout ? ", nowayout" : "");
 566        return 0;
 567
 568disable_clk:
 569        clk_disable_unprepare(dev->clk);
 570        clk_put(dev->clk);
 571        return ret;
 572}
 573
 574static int orion_wdt_remove(struct platform_device *pdev)
 575{
 576        struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
 577        struct orion_watchdog *dev = watchdog_get_drvdata(wdt_dev);
 578
 579        watchdog_unregister_device(wdt_dev);
 580        clk_disable_unprepare(dev->clk);
 581        clk_put(dev->clk);
 582        return 0;
 583}
 584
 585static void orion_wdt_shutdown(struct platform_device *pdev)
 586{
 587        struct watchdog_device *wdt_dev = platform_get_drvdata(pdev);
 588        orion_wdt_stop(wdt_dev);
 589}
 590
 591static struct platform_driver orion_wdt_driver = {
 592        .probe          = orion_wdt_probe,
 593        .remove         = orion_wdt_remove,
 594        .shutdown       = orion_wdt_shutdown,
 595        .driver         = {
 596                .owner  = THIS_MODULE,
 597                .name   = "orion_wdt",
 598                .of_match_table = orion_wdt_of_match_table,
 599        },
 600};
 601
 602module_platform_driver(orion_wdt_driver);
 603
 604MODULE_AUTHOR("Sylver Bruneau <sylver.bruneau@googlemail.com>");
 605MODULE_DESCRIPTION("Orion Processor Watchdog");
 606
 607module_param(heartbeat, int, 0);
 608MODULE_PARM_DESC(heartbeat, "Initial watchdog heartbeat in seconds");
 609
 610module_param(nowayout, bool, 0);
 611MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
 612                                __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
 613
 614MODULE_LICENSE("GPL");
 615MODULE_ALIAS("platform:orion_wdt");
 616