1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xilinx window watchdog timer driver.
4  *
5  * Author(s):	Michal Simek <michal.simek@xilinx.com>
6  *		Ashok Reddy Soma <ashokred@xilinx.com>
7  *
8  * Copyright (c) 2020, Xilinx Inc.
9  */
10 
11 #include <clk.h>
12 #include <common.h>
13 #include <dm.h>
14 #include <regmap.h>
15 #include <wdt.h>
16 #include <linux/compat.h>
17 #include <linux/io.h>
18 
19 /* Refresh Register Masks */
20 #define XWT_WWREF_GWRR_MASK	BIT(0) /* Refresh and start new period */
21 
22 /* Generic Control/Status Register Masks */
23 #define XWT_WWCSR_GWEN_MASK	BIT(0) /* Enable Bit */
24 
25 /* Register offsets for the Wdt device */
26 #define XWT_WWREF_OFFSET	0x1000 /* Refresh Register */
27 #define XWT_WWCSR_OFFSET	0x2000 /* Control/Status Register */
28 #define XWT_WWOFF_OFFSET	0x2008 /* Offset Register */
29 #define XWT_WWCMP0_OFFSET	0x2010 /* Compare Value Register0 */
30 #define XWT_WWCMP1_OFFSET	0x2014 /* Compare Value Register1 */
31 #define XWT_WWWRST_OFFSET	0x2FD0 /* Warm Reset Register */
32 
33 struct xlnx_wwdt_priv {
34 	bool enable_once;
35 	struct regmap *regs;
36 	struct clk clk;
37 };
38 
39 struct xlnx_wwdt_plat {
40 	bool enable_once;
41 };
42 
xlnx_wwdt_reset(struct udevice * dev)43 static int xlnx_wwdt_reset(struct udevice *dev)
44 {
45 	struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
46 
47 	regmap_write(wdt->regs, XWT_WWREF_OFFSET, XWT_WWREF_GWRR_MASK);
48 
49 	return 0;
50 }
51 
xlnx_wwdt_stop(struct udevice * dev)52 static int xlnx_wwdt_stop(struct udevice *dev)
53 {
54 	u32 csr;
55 	struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
56 
57 	if (wdt->enable_once) {
58 		dev_warn(dev, "Can't stop Xilinx watchdog.\n");
59 		return -EBUSY;
60 	}
61 
62 	/* Disable the generic watchdog timer */
63 	regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
64 	csr &= ~(XWT_WWCSR_GWEN_MASK);
65 	regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
66 
67 	clk_disable(&wdt->clk);
68 
69 	dev_dbg(dev, "Watchdog disabled!\n");
70 
71 	return 0;
72 }
73 
xlnx_wwdt_start(struct udevice * dev,u64 timeout,ulong flags)74 static int xlnx_wwdt_start(struct udevice *dev, u64 timeout, ulong flags)
75 {
76 	int ret;
77 	u32 csr;
78 	u64 count;
79 	unsigned long clock_f;
80 	struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
81 
82 	clock_f = clk_get_rate(&wdt->clk);
83 	if (IS_ERR_VALUE(clock_f)) {
84 		dev_err(dev, "failed to get rate\n");
85 		return clock_f;
86 	}
87 
88 	dev_dbg(dev, "%s: CLK %ld\n", __func__, clock_f);
89 
90 	/* Calculate timeout count */
91 	count = timeout * clock_f;
92 
93 	ret = clk_enable(&wdt->clk);
94 	if (ret) {
95 		dev_err(dev, "failed to enable clock\n");
96 		return ret;
97 	}
98 
99 	/*
100 	 * Timeout count is half as there are two windows
101 	 * first window overflow is ignored (interrupt),
102 	 * reset is only generated at second window overflow
103 	 */
104 	count = count >> 1;
105 
106 	/* Disable the generic watchdog timer */
107 	regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
108 	csr &= ~(XWT_WWCSR_GWEN_MASK);
109 	regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
110 
111 	/* Set compare and offset registers for generic watchdog timeout */
112 	regmap_write(wdt->regs, XWT_WWCMP0_OFFSET, (u32)count);
113 	regmap_write(wdt->regs, XWT_WWCMP1_OFFSET, 0);
114 	regmap_write(wdt->regs, XWT_WWOFF_OFFSET, (u32)count);
115 
116 	/* Enable the generic watchdog timer */
117 	regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
118 	csr |= (XWT_WWCSR_GWEN_MASK);
119 	regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
120 
121 	return 0;
122 }
123 
xlnx_wwdt_probe(struct udevice * dev)124 static int xlnx_wwdt_probe(struct udevice *dev)
125 {
126 	int ret;
127 	struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
128 	struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
129 
130 	dev_dbg(dev, "%s: Probing wdt%u\n", __func__, dev_seq(dev));
131 
132 	ret = regmap_init_mem(dev_ofnode(dev), &wdt->regs);
133 	if (ret) {
134 		dev_dbg(dev, "failed to get regbase of wwdt\n");
135 		return ret;
136 	}
137 
138 	wdt->enable_once = plat->enable_once;
139 
140 	ret = clk_get_by_index(dev, 0, &wdt->clk);
141 	if (ret < 0)
142 		dev_err(dev, "failed to get clock\n");
143 
144 	return ret;
145 }
146 
xlnx_wwdt_of_to_plat(struct udevice * dev)147 static int xlnx_wwdt_of_to_plat(struct udevice *dev)
148 {
149 	struct xlnx_wwdt_plat *plat = dev_get_plat(dev);
150 
151 	plat->enable_once = dev_read_u32_default(dev, "xlnx,wdt-enable-once",
152 						 0);
153 	dev_dbg(dev, "wdt-enable-once %d\n", plat->enable_once);
154 
155 	return 0;
156 }
157 
158 static const struct wdt_ops xlnx_wwdt_ops = {
159 	.start = xlnx_wwdt_start,
160 	.reset = xlnx_wwdt_reset,
161 	.stop = xlnx_wwdt_stop,
162 };
163 
164 static const struct udevice_id xlnx_wwdt_ids[] = {
165 	{ .compatible = "xlnx,versal-wwdt-1.0", },
166 	{},
167 };
168 
169 U_BOOT_DRIVER(xlnx_wwdt) = {
170 	.name = "xlnx_wwdt",
171 	.id = UCLASS_WDT,
172 	.of_match = xlnx_wwdt_ids,
173 	.probe = xlnx_wwdt_probe,
174 	.priv_auto	= sizeof(struct xlnx_wwdt_priv),
175 	.plat_auto	= sizeof(struct xlnx_wwdt_plat),
176 	.of_to_plat = xlnx_wwdt_of_to_plat,
177 	.ops = &xlnx_wwdt_ops,
178 };
179