1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Xilinx AXI platforms watchdog timer driver.
4 *
5 * Author(s):	Michal Simek <michal.simek@amd.com>
6 *		Shreenidhi Shedi <yesshedi@gmail.com>
7 *
8 * Copyright (c) 2011-2018 Xilinx Inc.
9 */
10
11#include <dm.h>
12#include <log.h>
13#include <wdt.h>
14#include <linux/err.h>
15#include <linux/io.h>
16
17#define XWT_CSR0_WRS_MASK	0x00000008 /* Reset status Mask */
18#define XWT_CSR0_WDS_MASK	0x00000004 /* Timer state Mask */
19#define XWT_CSR0_EWDT1_MASK	0x00000002 /* Enable bit 1 Mask*/
20#define XWT_CSRX_EWDT2_MASK	0x00000001 /* Enable bit 2 Mask */
21
22struct watchdog_regs {
23	u32 twcsr0; /* 0x0 */
24	u32 twcsr1; /* 0x4 */
25	u32 tbr; /* 0x8 */
26};
27
28struct xlnx_wdt_plat {
29	bool enable_once;
30	struct watchdog_regs *regs;
31};
32
33static int xlnx_wdt_reset(struct udevice *dev)
34{
35	u32 reg;
36	struct xlnx_wdt_plat *plat = dev_get_plat(dev);
37
38	debug("%s ", __func__);
39
40	/* Read the current contents of TCSR0 */
41	reg = readl(&plat->regs->twcsr0);
42
43	/* Clear the watchdog WDS bit */
44	if (reg & (XWT_CSR0_EWDT1_MASK | XWT_CSRX_EWDT2_MASK))
45		writel(reg | XWT_CSR0_WDS_MASK, &plat->regs->twcsr0);
46
47	return 0;
48}
49
50static int xlnx_wdt_stop(struct udevice *dev)
51{
52	u32 reg;
53	struct xlnx_wdt_plat *plat = dev_get_plat(dev);
54
55	if (plat->enable_once) {
56		debug("Can't stop Xilinx watchdog.\n");
57		return -EBUSY;
58	}
59
60	/* Read the current contents of TCSR0 */
61	reg = readl(&plat->regs->twcsr0);
62
63	writel(reg & ~XWT_CSR0_EWDT1_MASK, &plat->regs->twcsr0);
64	writel(~XWT_CSRX_EWDT2_MASK, &plat->regs->twcsr1);
65
66	debug("Watchdog disabled!\n");
67
68	return 0;
69}
70
71static int xlnx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
72{
73	struct xlnx_wdt_plat *plat = dev_get_plat(dev);
74
75	debug("%s:\n", __func__);
76
77	writel((XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK | XWT_CSR0_EWDT1_MASK),
78	       &plat->regs->twcsr0);
79
80	writel(XWT_CSRX_EWDT2_MASK, &plat->regs->twcsr1);
81
82	return 0;
83}
84
85static int xlnx_wdt_probe(struct udevice *dev)
86{
87	debug("%s: Probing wdt%u\n", __func__, dev_seq(dev));
88
89	return 0;
90}
91
92static int xlnx_wdt_of_to_plat(struct udevice *dev)
93{
94	struct xlnx_wdt_plat *plat = dev_get_plat(dev);
95
96	plat->regs = dev_read_addr_ptr(dev);
97	if (!plat->regs)
98		return -EINVAL;
99
100	plat->enable_once = dev_read_u32_default(dev, "xlnx,wdt-enable-once",
101						 0);
102
103	debug("%s: wdt-enable-once %d\n", __func__, plat->enable_once);
104
105	return 0;
106}
107
108static const struct wdt_ops xlnx_wdt_ops = {
109	.start = xlnx_wdt_start,
110	.reset = xlnx_wdt_reset,
111	.stop = xlnx_wdt_stop,
112};
113
114static const struct udevice_id xlnx_wdt_ids[] = {
115	{ .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
116	{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
117	{},
118};
119
120U_BOOT_DRIVER(xlnx_wdt) = {
121	.name = "xlnx_wdt",
122	.id = UCLASS_WDT,
123	.of_match = xlnx_wdt_ids,
124	.probe = xlnx_wdt_probe,
125	.plat_auto	= sizeof(struct xlnx_wdt_plat),
126	.of_to_plat = xlnx_wdt_of_to_plat,
127	.ops = &xlnx_wdt_ops,
128};
129