of_xilinx_wdt.c revision 4c7fbbc4a57a35ed109f58f52eff1a04660789e9
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;
149e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	u32 *tmptr;
150e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	u32 *pfreq;
151f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek	struct resource *res;
1529066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct xwdt_device *xdev;
153ffb8eee4f140bbfc333381168d6fe1a7e7dc7af7Michal Simek	bool no_timeout = false;
1549066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct watchdog_device *xilinx_wdt_wdd;
1559066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
1569066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xdev = devm_kzalloc(&pdev->dev, sizeof(*xdev), GFP_KERNEL);
1579066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	if (!xdev)
1589066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		return -ENOMEM;
1599066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
1609066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xilinx_wdt_wdd = &xdev->xilinx_wdt_wdd;
1619066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xilinx_wdt_wdd->info = &xilinx_wdt_ident;
1629066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xilinx_wdt_wdd->ops = &xilinx_wdt_ops;
1639066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xilinx_wdt_wdd->parent = &pdev->dev;
164e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
165f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
1669066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	xdev->base = devm_ioremap_resource(&pdev->dev, res);
1679066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	if (IS_ERR(xdev->base))
1689066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		return PTR_ERR(xdev->base);
169f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek
17090fe6c608f8d46e5bb3f31c2d5e1c90475253f79Michal Simek	pfreq = (u32 *)of_get_property(pdev->dev.of_node,
171e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera					"clock-frequency", NULL);
172e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
173e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	if (pfreq == NULL) {
1744c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_warn(&pdev->dev,
1754c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek			 "The watchdog clock frequency cannot be obtained\n");
176ffb8eee4f140bbfc333381168d6fe1a7e7dc7af7Michal Simek		no_timeout = true;
177e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	}
178e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
179e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	tmptr = (u32 *)of_get_property(pdev->dev.of_node,
180e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera					"xlnx,wdt-interval", NULL);
181e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	if (tmptr == NULL) {
1824c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_warn(&pdev->dev,
1834c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek			 "Parameter \"xlnx,wdt-interval\" not found\n");
184ffb8eee4f140bbfc333381168d6fe1a7e7dc7af7Michal Simek		no_timeout = true;
185e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	} else {
1869066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		xdev->wdt_interval = *tmptr;
187e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	}
188e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
189e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	tmptr = (u32 *)of_get_property(pdev->dev.of_node,
190e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera					"xlnx,wdt-enable-once", NULL);
191e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	if (tmptr == NULL) {
1924c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_warn(&pdev->dev,
1934c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek			 "Parameter \"xlnx,wdt-enable-once\" not found\n");
1949066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		watchdog_set_nowayout(xilinx_wdt_wdd, true);
195e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	}
196e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
197e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/*
198e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera *  Twice of the 2^wdt_interval / freq  because the first wdt overflow is
199e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera *  ignored (interrupt), reset is only generated at second wdt overflow
200e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera */
201e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	if (!no_timeout)
2029066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		xilinx_wdt_wdd->timeout = 2 * ((1 << xdev->wdt_interval) /
2039066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek					  *pfreq);
2049066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
2059066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	spin_lock_init(&xdev->spinlock);
2069066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
207e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
2089066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	rc = xwdt_selftest(xdev);
209e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	if (rc == XWT_TIMER_FAILED) {
2104c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_err(&pdev->dev, "SelfTest routine error\n");
211f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek		return rc;
212e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	}
213e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
2149066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	rc = watchdog_register_device(xilinx_wdt_wdd);
215e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	if (rc) {
2164c7fbbc4a57a35ed109f58f52eff1a04660789e9Michal Simek		dev_err(&pdev->dev, "Cannot register watchdog (err=%d)\n", rc);
217f06cdfd184d845e1f01df7f636c0e3b5c5cc8d18Michal Simek		return rc;
218e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	}
219e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
220d14fd9645501444f06034339118de56686e25dfbMichal Simek	dev_info(&pdev->dev, "Xilinx Watchdog Timer at %p with timeout %ds\n",
2219066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek		 xdev->base, xilinx_wdt_wdd->timeout);
2229066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
2239066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	platform_set_drvdata(pdev, xdev);
224e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
225e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	return 0;
226e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}
227e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
2289066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simekstatic int xwdt_remove(struct platform_device *pdev)
229e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera{
2309066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	struct xwdt_device *xdev = platform_get_drvdata(pdev);
2319066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek
2329066317178ca87b9d958f801c8fcf784ec31c6c3Michal Simek	watchdog_unregister_device(&xdev->xilinx_wdt_wdd);
233e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
234e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	return 0;
235e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera}
236e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
237e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera/* Match table for of_platform binding */
2381d1313686422db3bffb2e7bd8eb2ccd9027d3783Bill Pembertonstatic struct of_device_id xwdt_of_match[] = {
2398fce9b367d672332d2d101175b10737ee5c18b59Michal Simek	{ .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
240e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
241e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	{},
242e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera};
243e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_DEVICE_TABLE(of, xwdt_of_match);
244e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
245e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrerastatic struct platform_driver xwdt_driver = {
246e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	.probe       = xwdt_probe,
24782268714bdf06bc06135efb707a9de590ab2d294Bill Pemberton	.remove      = xwdt_remove,
248e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	.driver = {
249e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera		.owner = THIS_MODULE,
250e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera		.name  = WATCHDOG_NAME,
251e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera		.of_match_table = xwdt_of_match,
252e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera	},
253e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera};
254e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
255b8ec61189f3b4cd9d1b2856342f5d7676151d01cAxel Linmodule_platform_driver(xwdt_driver);
256e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro Cabrera
257e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
258e9659e69b0094ea2cc92716e2b1cd6a7db9caf2eAlejandro CabreraMODULE_DESCRIPTION("Xilinx Watchdog driver");
2599419c07ccebf6080159b4440dab9b3e484c96d7aMichal SimekMODULE_LICENSE("GPL v2");
260