1e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/*
29419c07ccebf6080159b4440dab9b3e484c96d7aMichal Simek * Watchdog Device Driver for Xilinx axi/xps_timebase_wdt
39419c07ccebf6080159b4440dab9b3e484c96d7aMichal Simek *
4d14fd9645501444f06034339118de56686e25dfbMichal Simek * (C) Copyright 2013 - 2014 Xilinx, Inc.
59419c07ccebf6080159b4440dab9b3e484c96d7aMichal Simek * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>)
69419c07ccebf6080159b4440dab9b3e484c96d7aMichal Simek *
79419c07ccebf6080159b4440dab9b3e484c96d7aMichal Simek * This program is free software; you can redistribute it and/or
89419c07ccebf6080159b4440dab9b3e484c96d7aMichal Simek * modify it under the terms of the GNU General Public License
99419c07ccebf6080159b4440dab9b3e484c96d7aMichal Simek * as published by the Free Software Foundation; either version
109419c07ccebf6080159b4440dab9b3e484c96d7aMichal Simek * 2 of the License, or (at your option) any later version.
119419c07ccebf6080159b4440dab9b3e484c96d7aMichal Simek */
12e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
13f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek#include <linux/err.h>
14e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/module.h>
15e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/types.h>
16e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/kernel.h>
17e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/ioport.h>
18e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/watchdog.h>
19e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/io.h>
20e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/of.h>
21e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/of_device.h>
22e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#include <linux/of_address.h>
23e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
24e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* Register offsets for the Wdt device */
25e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_TWCSR0_OFFSET   0x0 /* Control/Status Register0 */
26e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_TWCSR1_OFFSET   0x4 /* Control/Status Register1 */
27e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_TBR_OFFSET      0x8 /* Timebase Register Offset */
28e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
29e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* Control/Status Register Masks  */
30e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_CSR0_WRS_MASK   0x00000008 /* Reset status */
31e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_CSR0_WDS_MASK   0x00000004 /* Timer state  */
32e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */
33e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
34e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* Control/Status Register 0/1 bits  */
35e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */
36e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
37e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* SelfTest constants */
38e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000
39e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define XWT_TIMER_FAILED            0xFFFFFFFF
40e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
41e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera#define WATCHDOG_NAME     "Xilinx Watchdog"
42e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
43e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastruct xwdt_device {
44e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	void __iomem *base;
45e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	u32 wdt_interval;
469066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spinlock_t spinlock;
479066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct watchdog_device xilinx_wdt_wdd;
48e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera};
49e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
50d14fd9645501444f06034339118de56686e25dfbMichal Simekstatic int xilinx_wdt_start(struct watchdog_device *wdd)
51e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{
525cf4e69d39f6a31a5305d01690c1607356b1483bMichal Simek	u32 control_status_reg;
539066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
545cf4e69d39f6a31a5305d01690c1607356b1483bMichal Simek
559066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_lock(&xdev->spinlock);
56e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
57e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	/* Clean previous status and enable the watchdog timer */
589066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
59e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
60e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
61e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK),
629066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		  xdev->base + XWT_TWCSR0_OFFSET);
63e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
649066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	iowrite32(XWT_CSRX_EWDT2_MASK, xdev->base + XWT_TWCSR1_OFFSET);
65e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
669066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_unlock(&xdev->spinlock);
67d14fd9645501444f06034339118de56686e25dfbMichal Simek
68d14fd9645501444f06034339118de56686e25dfbMichal Simek	return 0;
69e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}
70e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
71d14fd9645501444f06034339118de56686e25dfbMichal Simekstatic int xilinx_wdt_stop(struct watchdog_device *wdd)
72e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{
735cf4e69d39f6a31a5305d01690c1607356b1483bMichal Simek	u32 control_status_reg;
749066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
755cf4e69d39f6a31a5305d01690c1607356b1483bMichal Simek
769066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_lock(&xdev->spinlock);
77e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
789066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
79e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
80e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK),
819066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		  xdev->base + XWT_TWCSR0_OFFSET);
82e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
839066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	iowrite32(0, xdev->base + XWT_TWCSR1_OFFSET);
84e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
859066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_unlock(&xdev->spinlock);
8627c766aaacb265d625dc634bf7903f7f9fd0c697Joe Perches	pr_info("Stopped!\n");
87d14fd9645501444f06034339118de56686e25dfbMichal Simek
88d14fd9645501444f06034339118de56686e25dfbMichal Simek	return 0;
89e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}
90e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
91d14fd9645501444f06034339118de56686e25dfbMichal Simekstatic int xilinx_wdt_keepalive(struct watchdog_device *wdd)
92e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{
935cf4e69d39f6a31a5305d01690c1607356b1483bMichal Simek	u32 control_status_reg;
949066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
955cf4e69d39f6a31a5305d01690c1607356b1483bMichal Simek
969066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_lock(&xdev->spinlock);
97e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
989066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
99e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
1009066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	iowrite32(control_status_reg, xdev->base + XWT_TWCSR0_OFFSET);
101e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
1029066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_unlock(&xdev->spinlock);
103e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
104d14fd9645501444f06034339118de56686e25dfbMichal Simek	return 0;
105d14fd9645501444f06034339118de56686e25dfbMichal Simek}
106e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
107d14fd9645501444f06034339118de56686e25dfbMichal Simekstatic const struct watchdog_info xilinx_wdt_ident = {
108d14fd9645501444f06034339118de56686e25dfbMichal Simek	.options =  WDIOF_MAGICCLOSE |
109d14fd9645501444f06034339118de56686e25dfbMichal Simek		    WDIOF_KEEPALIVEPING,
110d14fd9645501444f06034339118de56686e25dfbMichal Simek	.firmware_version =	1,
111d14fd9645501444f06034339118de56686e25dfbMichal Simek	.identity =	WATCHDOG_NAME,
112d14fd9645501444f06034339118de56686e25dfbMichal Simek};
113e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
114d14fd9645501444f06034339118de56686e25dfbMichal Simekstatic const struct watchdog_ops xilinx_wdt_ops = {
115d14fd9645501444f06034339118de56686e25dfbMichal Simek	.owner = THIS_MODULE,
116d14fd9645501444f06034339118de56686e25dfbMichal Simek	.start = xilinx_wdt_start,
117d14fd9645501444f06034339118de56686e25dfbMichal Simek	.stop = xilinx_wdt_stop,
118d14fd9645501444f06034339118de56686e25dfbMichal Simek	.ping = xilinx_wdt_keepalive,
119d14fd9645501444f06034339118de56686e25dfbMichal Simek};
120e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
1219066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simekstatic u32 xwdt_selftest(struct xwdt_device *xdev)
122e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{
123e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	int i;
124e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	u32 timer_value1;
125e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	u32 timer_value2;
126e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
1279066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_lock(&xdev->spinlock);
128e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
1299066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	timer_value1 = ioread32(xdev->base + XWT_TBR_OFFSET);
1309066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
131e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
132e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	for (i = 0;
133e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera		((i <= XWT_MAX_SELFTEST_LOOP_COUNT) &&
134e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera			(timer_value2 == timer_value1)); i++) {
1359066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
136e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	}
137e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
1389066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_unlock(&xdev->spinlock);
139e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
140e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	if (timer_value2 != timer_value1)
141e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera		return ~XWT_TIMER_FAILED;
142e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	else
143e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera		return XWT_TIMER_FAILED;
144e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}
145e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
1462d991a164a61858012651e13c59521975504e260Bill Pembertonstatic int xwdt_probe(struct platform_device *pdev)
147e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{
148e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	int rc;
1498d6a140b5f0def8c34d803474780b7b11fe0fab4Michal Simek	u32 pfreq = 0, enable_once = 0;
150f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek	struct resource *res;
1519066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct xwdt_device *xdev;
1529066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct watchdog_device *xilinx_wdt_wdd;
1539066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
1549066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
1559066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	if (!xdev)
1569066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		return -ENOMEM;
1579066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
1589066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xilinx_wdt_wdd = &xdev->xilinx_wdt_wdd;
1599066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xilinx_wdt_wdd->info = &xilinx_wdt_ident;
1609066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xilinx_wdt_wdd->ops = &xilinx_wdt_ops;
1619066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xilinx_wdt_wdd->parent = &pdev->dev;
162e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
163f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1649066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xdev->base = devm_ioremap_resource(&pdev->dev, res);
1659066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	if (IS_ERR(xdev->base))
1669066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		return PTR_ERR(xdev->base);
167f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek
1682e79a368473d55db3237120dea0f561660dac5bdMichal Simek	rc = of_property_read_u32(pdev->dev.of_node, "clock-frequency", &pfreq);
1698d6a140b5f0def8c34d803474780b7b11fe0fab4Michal Simek	if (rc)
1704c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_warn(&pdev->dev,
1714c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek			 "The watchdog clock frequency cannot be obtained\n");
172e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
1732e79a368473d55db3237120dea0f561660dac5bdMichal Simek	rc = of_property_read_u32(pdev->dev.of_node, "xlnx,wdt-interval",
1742e79a368473d55db3237120dea0f561660dac5bdMichal Simek				  &xdev->wdt_interval);
1758d6a140b5f0def8c34d803474780b7b11fe0fab4Michal Simek	if (rc)
1764c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_warn(&pdev->dev,
1774c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek			 "Parameter \"xlnx,wdt-interval\" not found\n");
178e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
1792e79a368473d55db3237120dea0f561660dac5bdMichal Simek	rc = of_property_read_u32(pdev->dev.of_node, "xlnx,wdt-enable-once",
1802e79a368473d55db3237120dea0f561660dac5bdMichal Simek				  &enable_once);
1812e79a368473d55db3237120dea0f561660dac5bdMichal Simek	if (rc)
1824c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_warn(&pdev->dev,
1834c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek			 "Parameter \"xlnx,wdt-enable-once\" not found\n");
1842e79a368473d55db3237120dea0f561660dac5bdMichal Simek
1852e79a368473d55db3237120dea0f561660dac5bdMichal Simek	watchdog_set_nowayout(xilinx_wdt_wdd, enable_once);
186e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
18775b3c5a827de11620f9bf7cbd6c3e4208bd3ddf5Michal Simek	/*
18875b3c5a827de11620f9bf7cbd6c3e4208bd3ddf5Michal Simek	 * Twice of the 2^wdt_interval / freq  because the first wdt overflow is
18975b3c5a827de11620f9bf7cbd6c3e4208bd3ddf5Michal Simek	 * ignored (interrupt), reset is only generated at second wdt overflow
19075b3c5a827de11620f9bf7cbd6c3e4208bd3ddf5Michal Simek	 */
1918d6a140b5f0def8c34d803474780b7b11fe0fab4Michal Simek	if (pfreq && xdev->wdt_interval)
1929066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		xilinx_wdt_wdd->timeout = 2 * ((1 << xdev->wdt_interval) /
1932e79a368473d55db3237120dea0f561660dac5bdMichal Simek					  pfreq);
1949066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
1959066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_lock_init(&xdev->spinlock);
1969066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
197e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
1989066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	rc = xwdt_selftest(xdev);
199e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	if (rc == XWT_TIMER_FAILED) {
2004c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_err(&pdev->dev, "SelfTest routine error\n");
201f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek		return rc;
202e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	}
203e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
2049066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	rc = watchdog_register_device(xilinx_wdt_wdd);
205e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	if (rc) {
2064c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc);
207f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek		return rc;
208e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	}
209e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
210d14fd9645501444f06034339118de56686e25dfbMichal Simek	dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n",
2119066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		 xdev->base, xilinx_wdt_wdd->timeout);
2129066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
2139066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	platform_set_drvdata(pdev, xdev);
214e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
215e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	return 0;
216e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}
217e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
2189066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simekstatic int xwdt_remove(struct platform_device *pdev)
219e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{
2209066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct xwdt_device *xdev = platform_get_drvdata(pdev);
2219066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
2229066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	watchdog_unregister_device(&xdev->xilinx_wdt_wdd);
223e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
224e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	return 0;
225e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}
226e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
227e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* Match table for of_platform binding */
2289ebf1855dbe0a170e45a2000d6f8e16d88b5fd7cJingoo Hanstatic const struct of_device_id xwdt_of_match[] = {
2298fce9b367d672332d2d101175b10737ee5c18b59Michal Simek	{ .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
230e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
231e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	{},
232e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera};
233e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_DEVICE_TABLE(of, xwdt_of_match);
234e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
235e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic struct platform_driver xwdt_driver = {
236e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	.probe       = xwdt_probe,
23782268714bdf06bc06135efb707a9de590ab2d294Bill Pemberton	.remove      = xwdt_remove,
238e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	.driver = {
239e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera		.name  = WATCHDOG_NAME,
240e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera		.of_match_table = xwdt_of_match,
241e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	},
242e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera};
243e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
244b8ec61189f3b4cd9d1b2856342f5d7676151d01cAxel Linmodule_platform_driver(xwdt_driver);
245e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
246e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
247e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_DESCRIPTION("Xilinx Watchdog driver");
2489419c07ccebf6080159b4440dab9b3e484c96d7aMichal SimekMODULE_LICENSE("GPL v2");
249