Blame view

drivers/watchdog/of_xilinx_wdt.c 7.39 KB
2e62c4988   Marcus Folkesson   watchdog: add SPD...
1
  // SPDX-License-Identifier: GPL-2.0+
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
2
  /*
9419c07cc   Michal Simek   watchdog: xilinx:...
3
4
   * Watchdog Device Driver for Xilinx axi/xps_timebase_wdt
   *
d14fd9645   Michal Simek   watchdog: xilinx:...
5
   * (C) Copyright 2013 - 2014 Xilinx, Inc.
9419c07cc   Michal Simek   watchdog: xilinx:...
6
   * (C) Copyright 2011 (Alejandro Cabrera <aldaya@gmail.com>)
9419c07cc   Michal Simek   watchdog: xilinx:...
7
   */
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
8

9d6b4efc1   Shubhrajyoti Datta   watchdog: xilinx:...
9
  #include <linux/clk.h>
f06cdfd18   Michal Simek   watchdog: xilinx:...
10
  #include <linux/err.h>
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
11
12
13
  #include <linux/module.h>
  #include <linux/types.h>
  #include <linux/kernel.h>
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
14
15
16
  #include <linux/ioport.h>
  #include <linux/watchdog.h>
  #include <linux/io.h>
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
  #include <linux/of.h>
  #include <linux/of_device.h>
  #include <linux/of_address.h>
  
  /* Register offsets for the Wdt device */
  #define XWT_TWCSR0_OFFSET   0x0 /* Control/Status Register0 */
  #define XWT_TWCSR1_OFFSET   0x4 /* Control/Status Register1 */
  #define XWT_TBR_OFFSET      0x8 /* Timebase Register Offset */
  
  /* Control/Status Register Masks  */
  #define XWT_CSR0_WRS_MASK   0x00000008 /* Reset status */
  #define XWT_CSR0_WDS_MASK   0x00000004 /* Timer state  */
  #define XWT_CSR0_EWDT1_MASK 0x00000002 /* Enable bit 1 */
  
  /* Control/Status Register 0/1 bits  */
  #define XWT_CSRX_EWDT2_MASK 0x00000001 /* Enable bit 2 */
  
  /* SelfTest constants */
  #define XWT_MAX_SELFTEST_LOOP_COUNT 0x00010000
  #define XWT_TIMER_FAILED            0xFFFFFFFF
  
  #define WATCHDOG_NAME     "Xilinx Watchdog"
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
39
40
  
  struct xwdt_device {
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
41
  	void __iomem *base;
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
42
  	u32 wdt_interval;
906631717   Michal Simek   watchdog: xilinx:...
43
44
  	spinlock_t spinlock;
  	struct watchdog_device xilinx_wdt_wdd;
9d6b4efc1   Shubhrajyoti Datta   watchdog: xilinx:...
45
  	struct clk		*clk;
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
46
  };
d14fd9645   Michal Simek   watchdog: xilinx:...
47
  static int xilinx_wdt_start(struct watchdog_device *wdd)
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
48
  {
b6bc41645   Maulik Jodhani   watchdog: of_xili...
49
  	int ret;
5cf4e69d3   Michal Simek   watchdog: xilinx:...
50
  	u32 control_status_reg;
906631717   Michal Simek   watchdog: xilinx:...
51
  	struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
5cf4e69d3   Michal Simek   watchdog: xilinx:...
52

b6bc41645   Maulik Jodhani   watchdog: of_xili...
53
54
55
56
57
58
  	ret = clk_enable(xdev->clk);
  	if (ret) {
  		dev_err(wdd->parent, "Failed to enable clock
  ");
  		return ret;
  	}
906631717   Michal Simek   watchdog: xilinx:...
59
  	spin_lock(&xdev->spinlock);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
60
61
  
  	/* Clean previous status and enable the watchdog timer */
906631717   Michal Simek   watchdog: xilinx:...
62
  	control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
63
64
65
  	control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
  
  	iowrite32((control_status_reg | XWT_CSR0_EWDT1_MASK),
906631717   Michal Simek   watchdog: xilinx:...
66
  		  xdev->base + XWT_TWCSR0_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
67

906631717   Michal Simek   watchdog: xilinx:...
68
  	iowrite32(XWT_CSRX_EWDT2_MASK, xdev->base + XWT_TWCSR1_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
69

906631717   Michal Simek   watchdog: xilinx:...
70
  	spin_unlock(&xdev->spinlock);
d14fd9645   Michal Simek   watchdog: xilinx:...
71
72
  
  	return 0;
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
73
  }
d14fd9645   Michal Simek   watchdog: xilinx:...
74
  static int xilinx_wdt_stop(struct watchdog_device *wdd)
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
75
  {
5cf4e69d3   Michal Simek   watchdog: xilinx:...
76
  	u32 control_status_reg;
906631717   Michal Simek   watchdog: xilinx:...
77
  	struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
5cf4e69d3   Michal Simek   watchdog: xilinx:...
78

906631717   Michal Simek   watchdog: xilinx:...
79
  	spin_lock(&xdev->spinlock);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
80

906631717   Michal Simek   watchdog: xilinx:...
81
  	control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
82
83
  
  	iowrite32((control_status_reg & ~XWT_CSR0_EWDT1_MASK),
906631717   Michal Simek   watchdog: xilinx:...
84
  		  xdev->base + XWT_TWCSR0_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
85

906631717   Michal Simek   watchdog: xilinx:...
86
  	iowrite32(0, xdev->base + XWT_TWCSR1_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
87

906631717   Michal Simek   watchdog: xilinx:...
88
  	spin_unlock(&xdev->spinlock);
b6bc41645   Maulik Jodhani   watchdog: of_xili...
89
90
  
  	clk_disable(xdev->clk);
27c766aaa   Joe Perches   watchdog: Use pr_...
91
92
  	pr_info("Stopped!
  ");
d14fd9645   Michal Simek   watchdog: xilinx:...
93
94
  
  	return 0;
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
95
  }
d14fd9645   Michal Simek   watchdog: xilinx:...
96
  static int xilinx_wdt_keepalive(struct watchdog_device *wdd)
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
97
  {
5cf4e69d3   Michal Simek   watchdog: xilinx:...
98
  	u32 control_status_reg;
906631717   Michal Simek   watchdog: xilinx:...
99
  	struct xwdt_device *xdev = watchdog_get_drvdata(wdd);
5cf4e69d3   Michal Simek   watchdog: xilinx:...
100

906631717   Michal Simek   watchdog: xilinx:...
101
  	spin_lock(&xdev->spinlock);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
102

906631717   Michal Simek   watchdog: xilinx:...
103
  	control_status_reg = ioread32(xdev->base + XWT_TWCSR0_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
104
  	control_status_reg |= (XWT_CSR0_WRS_MASK | XWT_CSR0_WDS_MASK);
906631717   Michal Simek   watchdog: xilinx:...
105
  	iowrite32(control_status_reg, xdev->base + XWT_TWCSR0_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
106

906631717   Michal Simek   watchdog: xilinx:...
107
  	spin_unlock(&xdev->spinlock);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
108

d14fd9645   Michal Simek   watchdog: xilinx:...
109
110
  	return 0;
  }
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
111

d14fd9645   Michal Simek   watchdog: xilinx:...
112
113
114
115
116
117
  static const struct watchdog_info xilinx_wdt_ident = {
  	.options =  WDIOF_MAGICCLOSE |
  		    WDIOF_KEEPALIVEPING,
  	.firmware_version =	1,
  	.identity =	WATCHDOG_NAME,
  };
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
118

d14fd9645   Michal Simek   watchdog: xilinx:...
119
120
121
122
123
124
  static const struct watchdog_ops xilinx_wdt_ops = {
  	.owner = THIS_MODULE,
  	.start = xilinx_wdt_start,
  	.stop = xilinx_wdt_stop,
  	.ping = xilinx_wdt_keepalive,
  };
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
125

906631717   Michal Simek   watchdog: xilinx:...
126
  static u32 xwdt_selftest(struct xwdt_device *xdev)
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
127
128
129
130
  {
  	int i;
  	u32 timer_value1;
  	u32 timer_value2;
906631717   Michal Simek   watchdog: xilinx:...
131
  	spin_lock(&xdev->spinlock);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
132

906631717   Michal Simek   watchdog: xilinx:...
133
134
  	timer_value1 = ioread32(xdev->base + XWT_TBR_OFFSET);
  	timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
135
136
137
138
  
  	for (i = 0;
  		((i <= XWT_MAX_SELFTEST_LOOP_COUNT) &&
  			(timer_value2 == timer_value1)); i++) {
906631717   Michal Simek   watchdog: xilinx:...
139
  		timer_value2 = ioread32(xdev->base + XWT_TBR_OFFSET);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
140
  	}
906631717   Michal Simek   watchdog: xilinx:...
141
  	spin_unlock(&xdev->spinlock);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
142
143
144
145
146
147
  
  	if (timer_value2 != timer_value1)
  		return ~XWT_TIMER_FAILED;
  	else
  		return XWT_TIMER_FAILED;
  }
801cdffe6   Guenter Roeck   watchdog: of_xili...
148
149
150
151
  static void xwdt_clk_disable_unprepare(void *data)
  {
  	clk_disable_unprepare(data);
  }
2d991a164   Bill Pemberton   watchdog: remove ...
152
  static int xwdt_probe(struct platform_device *pdev)
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
153
  {
801cdffe6   Guenter Roeck   watchdog: of_xili...
154
  	struct device *dev = &pdev->dev;
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
155
  	int rc;
8d6a140b5   Michal Simek   watchdog: xilinx:...
156
  	u32 pfreq = 0, enable_once = 0;
906631717   Michal Simek   watchdog: xilinx:...
157
  	struct xwdt_device *xdev;
906631717   Michal Simek   watchdog: xilinx:...
158
  	struct watchdog_device *xilinx_wdt_wdd;
801cdffe6   Guenter Roeck   watchdog: of_xili...
159
  	xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
906631717   Michal Simek   watchdog: xilinx:...
160
161
162
163
164
165
  	if (!xdev)
  		return -ENOMEM;
  
  	xilinx_wdt_wdd = &xdev->xilinx_wdt_wdd;
  	xilinx_wdt_wdd->info = &xilinx_wdt_ident;
  	xilinx_wdt_wdd->ops = &xilinx_wdt_ops;
801cdffe6   Guenter Roeck   watchdog: of_xili...
166
  	xilinx_wdt_wdd->parent = dev;
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
167

0f0a6a285   Guenter Roeck   watchdog: Convert...
168
  	xdev->base = devm_platform_ioremap_resource(pdev, 0);
906631717   Michal Simek   watchdog: xilinx:...
169
170
  	if (IS_ERR(xdev->base))
  		return PTR_ERR(xdev->base);
f06cdfd18   Michal Simek   watchdog: xilinx:...
171

801cdffe6   Guenter Roeck   watchdog: of_xili...
172
  	rc = of_property_read_u32(dev->of_node, "xlnx,wdt-interval",
2e79a3684   Michal Simek   watchdog: xilinx:...
173
  				  &xdev->wdt_interval);
8d6a140b5   Michal Simek   watchdog: xilinx:...
174
  	if (rc)
801cdffe6   Guenter Roeck   watchdog: of_xili...
175
176
  		dev_warn(dev, "Parameter \"xlnx,wdt-interval\" not found
  ");
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
177

801cdffe6   Guenter Roeck   watchdog: of_xili...
178
  	rc = of_property_read_u32(dev->of_node, "xlnx,wdt-enable-once",
2e79a3684   Michal Simek   watchdog: xilinx:...
179
180
  				  &enable_once);
  	if (rc)
801cdffe6   Guenter Roeck   watchdog: of_xili...
181
  		dev_warn(dev,
4c7fbbc4a   Michal Simek   watchdog: xilinx:...
182
183
  			 "Parameter \"xlnx,wdt-enable-once\" not found
  ");
2e79a3684   Michal Simek   watchdog: xilinx:...
184
185
  
  	watchdog_set_nowayout(xilinx_wdt_wdd, enable_once);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
186

801cdffe6   Guenter Roeck   watchdog: of_xili...
187
  	xdev->clk = devm_clk_get(dev, NULL);
b6bc41645   Maulik Jodhani   watchdog: of_xili...
188
189
190
191
192
193
194
195
196
  	if (IS_ERR(xdev->clk)) {
  		if (PTR_ERR(xdev->clk) != -ENOENT)
  			return PTR_ERR(xdev->clk);
  
  		/*
  		 * Clock framework support is optional, continue on
  		 * anyways if we don't find a matching clock.
  		 */
  		xdev->clk = NULL;
801cdffe6   Guenter Roeck   watchdog: of_xili...
197
  		rc = of_property_read_u32(dev->of_node, "clock-frequency",
b6bc41645   Maulik Jodhani   watchdog: of_xili...
198
199
  					  &pfreq);
  		if (rc)
801cdffe6   Guenter Roeck   watchdog: of_xili...
200
  			dev_warn(dev,
b6bc41645   Maulik Jodhani   watchdog: of_xili...
201
202
203
204
205
  				 "The watchdog clock freq cannot be obtained
  ");
  	} else {
  		pfreq = clk_get_rate(xdev->clk);
  	}
75b3c5a82   Michal Simek   watchdog: xilinx:...
206
207
208
209
  	/*
  	 * Twice of the 2^wdt_interval / freq  because the first wdt overflow is
  	 * ignored (interrupt), reset is only generated at second wdt overflow
  	 */
8d6a140b5   Michal Simek   watchdog: xilinx:...
210
  	if (pfreq && xdev->wdt_interval)
906631717   Michal Simek   watchdog: xilinx:...
211
  		xilinx_wdt_wdd->timeout = 2 * ((1 << xdev->wdt_interval) /
2e79a3684   Michal Simek   watchdog: xilinx:...
212
  					  pfreq);
906631717   Michal Simek   watchdog: xilinx:...
213
214
215
  
  	spin_lock_init(&xdev->spinlock);
  	watchdog_set_drvdata(xilinx_wdt_wdd, xdev);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
216

9d6b4efc1   Shubhrajyoti Datta   watchdog: xilinx:...
217
218
  	rc = clk_prepare_enable(xdev->clk);
  	if (rc) {
801cdffe6   Guenter Roeck   watchdog: of_xili...
219
220
  		dev_err(dev, "unable to enable clock
  ");
9d6b4efc1   Shubhrajyoti Datta   watchdog: xilinx:...
221
222
  		return rc;
  	}
801cdffe6   Guenter Roeck   watchdog: of_xili...
223
224
225
226
  	rc = devm_add_action_or_reset(dev, xwdt_clk_disable_unprepare,
  				      xdev->clk);
  	if (rc)
  		return rc;
9d6b4efc1   Shubhrajyoti Datta   watchdog: xilinx:...
227

906631717   Michal Simek   watchdog: xilinx:...
228
  	rc = xwdt_selftest(xdev);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
229
  	if (rc == XWT_TIMER_FAILED) {
801cdffe6   Guenter Roeck   watchdog: of_xili...
230
231
232
  		dev_err(dev, "SelfTest routine error
  ");
  		return rc;
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
233
  	}
801cdffe6   Guenter Roeck   watchdog: of_xili...
234
  	rc = devm_watchdog_register_device(dev, xilinx_wdt_wdd);
0fa6cf71c   Wolfram Sang   watchdog: of_xili...
235
  	if (rc)
801cdffe6   Guenter Roeck   watchdog: of_xili...
236
  		return rc;
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
237

b6bc41645   Maulik Jodhani   watchdog: of_xili...
238
  	clk_disable(xdev->clk);
801cdffe6   Guenter Roeck   watchdog: of_xili...
239
240
  	dev_info(dev, "Xilinx Watchdog Timer at %p with timeout %ds
  ",
906631717   Michal Simek   watchdog: xilinx:...
241
242
243
  		 xdev->base, xilinx_wdt_wdd->timeout);
  
  	platform_set_drvdata(pdev, xdev);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
244
245
  
  	return 0;
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
246
  }
6f671c6b6   Michal Simek   watchdog: of_xili...
247
248
249
250
251
252
253
254
  /**
   * xwdt_suspend - Suspend the device.
   *
   * @dev: handle to the device structure.
   * Return: 0 always.
   */
  static int __maybe_unused xwdt_suspend(struct device *dev)
  {
20745634c   Wolfram Sang   watchdog: simplif...
255
  	struct xwdt_device *xdev = dev_get_drvdata(dev);
6f671c6b6   Michal Simek   watchdog: of_xili...
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
  
  	if (watchdog_active(&xdev->xilinx_wdt_wdd))
  		xilinx_wdt_stop(&xdev->xilinx_wdt_wdd);
  
  	return 0;
  }
  
  /**
   * xwdt_resume - Resume the device.
   *
   * @dev: handle to the device structure.
   * Return: 0 on success, errno otherwise.
   */
  static int __maybe_unused xwdt_resume(struct device *dev)
  {
20745634c   Wolfram Sang   watchdog: simplif...
271
  	struct xwdt_device *xdev = dev_get_drvdata(dev);
6f671c6b6   Michal Simek   watchdog: of_xili...
272
273
274
275
276
277
278
279
280
  	int ret = 0;
  
  	if (watchdog_active(&xdev->xilinx_wdt_wdd))
  		ret = xilinx_wdt_start(&xdev->xilinx_wdt_wdd);
  
  	return ret;
  }
  
  static SIMPLE_DEV_PM_OPS(xwdt_pm_ops, xwdt_suspend, xwdt_resume);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
281
  /* Match table for of_platform binding */
9ebf1855d   Jingoo Han   watchdog: xilinx:...
282
  static const struct of_device_id xwdt_of_match[] = {
8fce9b367   Michal Simek   watchdog: xilinx:...
283
  	{ .compatible = "xlnx,xps-timebase-wdt-1.00.a", },
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
284
285
286
287
288
289
290
  	{ .compatible = "xlnx,xps-timebase-wdt-1.01.a", },
  	{},
  };
  MODULE_DEVICE_TABLE(of, xwdt_of_match);
  
  static struct platform_driver xwdt_driver = {
  	.probe       = xwdt_probe,
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
291
  	.driver = {
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
292
293
  		.name  = WATCHDOG_NAME,
  		.of_match_table = xwdt_of_match,
6f671c6b6   Michal Simek   watchdog: of_xili...
294
  		.pm = &xwdt_pm_ops,
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
295
296
  	},
  };
b8ec61189   Axel Lin   watchdog: convert...
297
  module_platform_driver(xwdt_driver);
e9659e69b   Alejandro Cabrera   watchdog: Add Xil...
298
299
300
  
  MODULE_AUTHOR("Alejandro Cabrera <aldaya@gmail.com>");
  MODULE_DESCRIPTION("Xilinx Watchdog driver");
2e62c4988   Marcus Folkesson   watchdog: add SPD...
301
  MODULE_LICENSE("GPL");