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