linux/drivers/watchdog/of_xilinx_wdt.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Watchdog Device Driver for Xilinx axi/xps_timebase_wdt
   4 *
   5 * (C) Copyright 2013 - 2014 Xilinx, Inc.
   6 * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>)
   7 */
   8
   9#include <linux/clk.h>
  10#include <linux/err.h>
  11#include <linux/module.h>
  12#include <linux/types.h>
  13#include <linux/kernel.h>
  14#include <linux/ioport.h>
  15#include <linux/watchdog.h>
  16#include <linux/io.h>
  17#include <linux/of.h>
  18#include <linux/of_device.h>
  19#include <linux/of_address.h>
  20
  21/* Register offsets for the Wdt device */
  22#define XWT_TWCSR0_OFFSET   0x0 /* Control/Status Register0 */
  23#define XWT_TWCSR1_OFFSET   0x4 /* Control/Status Register1 */
  24#define XWT_TBR_OFFSET      0x8 /* Timebase Register Offset */
  25
  26/* Control/Status Register Masks  */
  27#define XWT_CSR0_WRS_MASK   0x00000008 /* Reset status */
  28#define XWT_CSR0_WDS_MASK   0x00000004 /* Timer state  */
  29#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */
  30
  31/* Control/Status Register 0/1 bits  */
  32#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */
  33
  34/* SelfTest constants */
  35#define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000
  36#define XWT_TIMER_FAILED            0xFFFFFFFF
  37
  38#define WATCHDOG_NAME     "Xilinx Watchdog"
  39
  40struct xwdt_device {
  41        void __iomem *base;
  42        u32 wdt_interval;
  43        spinlock_t spinlock;
  44        struct watchdog_device xilinx_wdt_wdd;
  45        struct clk              *clk;
  46};
  47
  48static int xilinx_wdt_start(struct watchdog_device *wdd)
  49{
  50        int ret;
  51        u32 control_status_reg;
  52        struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
  53
  54        ret = clk_enable(xdev->clk);
  55        if (ret) {
  56                dev_err(wdd->parent, "Failed to enable clock\n");
  57                return ret;
  58        }
  59
  60        spin_lock(&xdev->spinlock);
  61
  62        /* Clean previous status and enable the watchdog timer */
  63        control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
  64        control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
  65
  66        iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK),
  67                  xdev->base + XWT_TWCSR0_OFFSET);
  68
  69        iowrite32(XWT_CSRX_EWDT2_MASK, xdev->base + XWT_TWCSR1_OFFSET);
  70
  71        spin_unlock(&xdev->spinlock);
  72
  73        return 0;
  74}
  75
  76static int xilinx_wdt_stop(struct watchdog_device *wdd)
  77{
  78        u32 control_status_reg;
  79        struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
  80
  81        spin_lock(&xdev->spinlock);
  82
  83        control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
  84
  85        iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK),
  86                  xdev->base + XWT_TWCSR0_OFFSET);
  87
  88        iowrite32(0, xdev->base + XWT_TWCSR1_OFFSET);
  89
  90        spin_unlock(&xdev->spinlock);
  91
  92        clk_disable(xdev->clk);
  93
  94        pr_info("Stopped!\n");
  95
  96        return 0;
  97}
  98
  99static int xilinx_wdt_keepalive(struct watchdog_device *wdd)
 100{
 101        u32 control_status_reg;
 102        struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
 103
 104        spin_lock(&xdev->spinlock);
 105
 106        control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
 107        control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
 108        iowrite32(control_status_reg, xdev->base + XWT_TWCSR0_OFFSET);
 109
 110        spin_unlock(&xdev->spinlock);
 111
 112        return 0;
 113}
 114
 115static const struct watchdog_info xilinx_wdt_ident = {
 116        .options =  WDIOF_MAGICCLOSE |
 117                    WDIOF_KEEPALIVEPING,
 118        .firmware_version =     1,
 119        .identity =     WATCHDOG_NAME,
 120};
 121
 122static const struct watchdog_ops xilinx_wdt_ops = {
 123        .owner = THIS_MODULE,
 124        .start = xilinx_wdt_start,
 125        .stop = xilinx_wdt_stop,
 126        .ping = xilinx_wdt_keepalive,
 127};
 128
 129static u32 xwdt_selftest(struct xwdt_device *xdev)
 130{
 131        int i;
 132        u32 timer_value1;
 133        u32 timer_value2;
 134
 135        spin_lock(&xdev->spinlock);
 136
 137        timer_value1 = ioread32(xdev->base + XWT_TBR_OFFSET);
 138        timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
 139
 140        for (i = 0;
 141                ((i <= XWT_MAX_SELFTEST_LOOP_COUNT) &&
 142                        (timer_value2 == timer_value1)); i++) {
 143                timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
 144        }
 145
 146        spin_unlock(&xdev->spinlock);
 147
 148        if (timer_value2 != timer_value1)
 149                return ~XWT_TIMER_FAILED;
 150        else
 151                return XWT_TIMER_FAILED;
 152}
 153
 154static void xwdt_clk_disable_unprepare(void *data)
 155{
 156        clk_disable_unprepare(data);
 157}
 158
 159static int xwdt_probe(struct platform_device *pdev)
 160{
 161        struct device *dev = &pdev->dev;
 162        int rc;
 163        u32 pfreq = 0, enable_once = 0;
 164        struct xwdt_device *xdev;
 165        struct watchdog_device *xilinx_wdt_wdd;
 166
 167        xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
 168        if (!xdev)
 169                return -ENOMEM;
 170
 171        xilinx_wdt_wdd = &xdev->xilinx_wdt_wdd;
 172        xilinx_wdt_wdd->info = &xilinx_wdt_ident;
 173        xilinx_wdt_wdd->ops = &xilinx_wdt_ops;
 174        xilinx_wdt_wdd->parent = dev;
 175
 176        xdev->base = devm_platform_ioremap_resource(pdev, 0);
 177        if (IS_ERR(xdev->base))
 178                return PTR_ERR(xdev->base);
 179
 180        rc = of_property_read_u32(dev->of_node, "xlnx,wdt-interval",
 181                                  &xdev->wdt_interval);
 182        if (rc)
 183                dev_warn(dev, "Parameter \"xlnx,wdt-interval\" not found\n");
 184
 185        rc = of_property_read_u32(dev->of_node, "xlnx,wdt-enable-once",
 186                                  &enable_once);
 187        if (rc)
 188                dev_warn(dev,
 189                         "Parameter \"xlnx,wdt-enable-once\" not found\n");
 190
 191        watchdog_set_nowayout(xilinx_wdt_wdd, enable_once);
 192
 193        xdev->clk = devm_clk_get(dev, NULL);
 194        if (IS_ERR(xdev->clk)) {
 195                if (PTR_ERR(xdev->clk) != -ENOENT)
 196                        return PTR_ERR(xdev->clk);
 197
 198                /*
 199                 * Clock framework support is optional, continue on
 200                 * anyways if we don't find a matching clock.
 201                 */
 202                xdev->clk = NULL;
 203
 204                rc = of_property_read_u32(dev->of_node, "clock-frequency",
 205                                          &pfreq);
 206                if (rc)
 207                        dev_warn(dev,
 208                                 "The watchdog clock freq cannot be obtained\n");
 209        } else {
 210                pfreq = clk_get_rate(xdev->clk);
 211        }
 212
 213        /*
 214         * Twice of the 2^wdt_interval / freq  because the first wdt overflow is
 215         * ignored (interrupt), reset is only generated at second wdt overflow
 216         */
 217        if (pfreq && xdev->wdt_interval)
 218                xilinx_wdt_wdd->timeout = 2 * ((1 << xdev->wdt_interval) /
 219                                          pfreq);
 220
 221        spin_lock_init(&xdev->spinlock);
 222        watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
 223
 224        rc = clk_prepare_enable(xdev->clk);
 225        if (rc) {
 226                dev_err(dev, "unable to enable clock\n");
 227                return rc;
 228        }
 229        rc = devm_add_action_or_reset(dev, xwdt_clk_disable_unprepare,
 230                                      xdev->clk);
 231        if (rc)
 232                return rc;
 233
 234        rc = xwdt_selftest(xdev);
 235        if (rc == XWT_TIMER_FAILED) {
 236                dev_err(dev, "SelfTest routine error\n");
 237                return rc;
 238        }
 239
 240        rc = devm_watchdog_register_device(dev, xilinx_wdt_wdd);
 241        if (rc)
 242                return rc;
 243
 244        clk_disable(xdev->clk);
 245
 246        dev_info(dev, "Xilinx Watchdog Timer at %p with timeout %ds\n",
 247                 xdev->base, xilinx_wdt_wdd->timeout);
 248
 249        platform_set_drvdata(pdev, xdev);
 250
 251        return 0;
 252}
 253
 254/**
 255 * xwdt_suspend - Suspend the device.
 256 *
 257 * @dev: handle to the device structure.
 258 * Return: 0 always.
 259 */
 260static int __maybe_unused xwdt_suspend(struct device *dev)
 261{
 262        struct xwdt_device *xdev = dev_get_drvdata(dev);
 263
 264        if (watchdog_active(&xdev->xilinx_wdt_wdd))
 265                xilinx_wdt_stop(&xdev->xilinx_wdt_wdd);
 266
 267        return 0;
 268}
 269
 270/**
 271 * xwdt_resume - Resume the device.
 272 *
 273 * @dev: handle to the device structure.
 274 * Return: 0 on success, errno otherwise.
 275 */
 276static int __maybe_unused xwdt_resume(struct device *dev)
 277{
 278        struct xwdt_device *xdev = dev_get_drvdata(dev);
 279        int ret = 0;
 280
 281        if (watchdog_active(&xdev->xilinx_wdt_wdd))
 282                ret = xilinx_wdt_start(&xdev->xilinx_wdt_wdd);
 283
 284        return ret;
 285}
 286
 287static SIMPLE_DEV_PM_OPS(xwdt_pm_ops, xwdt_suspend, xwdt_resume);
 288
 289/* Match table for of_platform binding */
 290static const struct of_device_id xwdt_of_match[] = {
 291        { .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
 292        { .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
 293        {},
 294};
 295MODULE_DEVICE_TABLE(of, xwdt_of_match);
 296
 297static struct platform_driver xwdt_driver = {
 298        .probe       = xwdt_probe,
 299        .driver = {
 300                .name  = WATCHDOG_NAME,
 301                .of_match_table = xwdt_of_match,
 302                .pm = &xwdt_pm_ops,
 303        },
 304};
 305
 306module_platform_driver(xwdt_driver);
 307
 308MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
 309MODULE_DESCRIPTION("Xilinx Watchdog driver");
 310MODULE_LICENSE("GPL");
 311