xref: /linux/drivers/watchdog/atcwdt200_wdt.c (revision 9611c0ce215a66770ccbe5c126bf57ba8c31bcad)
1*e4e0848aSCL Wang // SPDX-License-Identifier: GPL-2.0-or-later
2*e4e0848aSCL Wang /*
3*e4e0848aSCL Wang  * Andes ATCWDT200 watchdog timer driver.
4*e4e0848aSCL Wang  *
5*e4e0848aSCL Wang  * Copyright (C) 2025 Andes Technology Corporation
6*e4e0848aSCL Wang  */
7*e4e0848aSCL Wang 
8*e4e0848aSCL Wang #include <linux/bitfield.h>
9*e4e0848aSCL Wang #include <linux/clk.h>
10*e4e0848aSCL Wang #include <linux/device.h>
11*e4e0848aSCL Wang #include <linux/dev_printk.h>
12*e4e0848aSCL Wang #include <linux/math64.h>
13*e4e0848aSCL Wang #include <linux/minmax.h>
14*e4e0848aSCL Wang #include <linux/moduleparam.h>
15*e4e0848aSCL Wang #include <linux/module.h>
16*e4e0848aSCL Wang #include <linux/of.h>
17*e4e0848aSCL Wang #include <linux/of_platform.h>
18*e4e0848aSCL Wang #include <linux/platform_device.h>
19*e4e0848aSCL Wang #include <linux/pm.h>
20*e4e0848aSCL Wang #include <linux/pm_runtime.h>
21*e4e0848aSCL Wang #include <linux/regmap.h>
22*e4e0848aSCL Wang #include <linux/watchdog.h>
23*e4e0848aSCL Wang 
24*e4e0848aSCL Wang /* Register definitions */
25*e4e0848aSCL Wang #define REG_CTRL		0x10
26*e4e0848aSCL Wang #define REG_RESTART		0x14
27*e4e0848aSCL Wang #define REG_WRITE_EN		0x18
28*e4e0848aSCL Wang #define REG_STATUS		0x1C
29*e4e0848aSCL Wang 
30*e4e0848aSCL Wang /* Control Register */
31*e4e0848aSCL Wang #define CTRL_RST_TIME_MSK	GENMASK(10, 8)
32*e4e0848aSCL Wang #define CTRL_RST_TIME_SET(x)	FIELD_PREP(CTRL_RST_TIME_MSK, x)
33*e4e0848aSCL Wang #define CTRL_INT_TIME_MSK	GENMASK(7, 4)
34*e4e0848aSCL Wang #define CTRL_INT_TIME_SET(x)	FIELD_PREP(CTRL_INT_TIME_MSK, x)
35*e4e0848aSCL Wang #define CTRL_INT_TIME_GET(x)	FIELD_GET(CTRL_INT_TIME_MSK, x)
36*e4e0848aSCL Wang #define CTRL_RST_EN		BIT(3)
37*e4e0848aSCL Wang #define CTRL_CLK_SEL		BIT(1)
38*e4e0848aSCL Wang #define CTRL_CLK_SEL_PCLK	1
39*e4e0848aSCL Wang #define CTRL_CLK_SEL_SET(x)	FIELD_PREP(CTRL_CLK_SEL, x)
40*e4e0848aSCL Wang #define CTRL_WDT_EN		BIT(0)
41*e4e0848aSCL Wang 
42*e4e0848aSCL Wang /* Restart Register */
43*e4e0848aSCL Wang #define RESTART_MAGIC		0xCAFE
44*e4e0848aSCL Wang 
45*e4e0848aSCL Wang /* Write Enable Register */
46*e4e0848aSCL Wang #define WRITE_EN_MAGIC		0x5AA5
47*e4e0848aSCL Wang 
48*e4e0848aSCL Wang /* Status Register */
49*e4e0848aSCL Wang #define STATUS_INT_EXPIRED	BIT(1)
50*e4e0848aSCL Wang 
51*e4e0848aSCL Wang /* The default timeout value in seconds */
52*e4e0848aSCL Wang #define ATCWDT_TIMEOUT		4
53*e4e0848aSCL Wang 
54*e4e0848aSCL Wang /* Define the array size for each timer type */
55*e4e0848aSCL Wang #define TMR_SZ_RST		8
56*e4e0848aSCL Wang #define TMR_SZ_INT_16		8
57*e4e0848aSCL Wang #define TMR_SZ_INT_32		16
58*e4e0848aSCL Wang 
59*e4e0848aSCL Wang #define DRV_NAME		"atcwdt200"
60*e4e0848aSCL Wang /**
61*e4e0848aSCL Wang  * enum timer_type - Supported timer types for ATCWDT200 watchdog driver
62*e4e0848aSCL Wang  * @TMR_RST:      Reset timer (non-interrupt).
63*e4e0848aSCL Wang  * @TMR_INT_16:   16-bit interrupt timer supported by hardware.
64*e4e0848aSCL Wang  * @TMR_INT_32:   32-bit interrupt timer supported by hardware.
65*e4e0848aSCL Wang  * @TMR_UNKNOWN:  Timer type cannot be determined.
66*e4e0848aSCL Wang  */
67*e4e0848aSCL Wang enum timer_type {
68*e4e0848aSCL Wang 	TMR_RST,
69*e4e0848aSCL Wang 	TMR_INT_16,
70*e4e0848aSCL Wang 	TMR_INT_32,
71*e4e0848aSCL Wang 	TMR_UNKNOWN
72*e4e0848aSCL Wang };
73*e4e0848aSCL Wang 
74*e4e0848aSCL Wang static unsigned int timeout = ATCWDT_TIMEOUT;
75*e4e0848aSCL Wang static bool nowayout = WATCHDOG_NOWAYOUT;
76*e4e0848aSCL Wang 
77*e4e0848aSCL Wang /**
78*e4e0848aSCL Wang  * struct atcwdt_drv - ATCWDT200 watchdog driver private data
79*e4e0848aSCL Wang  * @wdt_dev:        Watchdog device used by the watchdog framework.
80*e4e0848aSCL Wang  * @regmap:         Register map for accessing hardware registers.
81*e4e0848aSCL Wang  * @clk:            Hardware clock used by the watchdog timer.
82*e4e0848aSCL Wang  * @lock:           Spinlock protecting register accesses and driver state.
83*e4e0848aSCL Wang  * @clk_freq:       Input clock frequency of the ATCWDT200.
84*e4e0848aSCL Wang  * @clk_src:        Selected clock source for the watchdog timer.
85*e4e0848aSCL Wang  * @int_timer_type: Detected interrupt timer type (16-bit, 32-bit, or unknown).
86*e4e0848aSCL Wang  */
87*e4e0848aSCL Wang struct atcwdt_drv {
88*e4e0848aSCL Wang 	struct watchdog_device	wdt_dev;
89*e4e0848aSCL Wang 	struct regmap		*regmap;
90*e4e0848aSCL Wang 	struct clk		*clk;
91*e4e0848aSCL Wang 	spinlock_t		lock;
92*e4e0848aSCL Wang 	unsigned int		clk_freq;
93*e4e0848aSCL Wang 	unsigned char		clk_src;
94*e4e0848aSCL Wang 	unsigned char		int_timer_type;
95*e4e0848aSCL Wang };
96*e4e0848aSCL Wang 
97*e4e0848aSCL Wang static const struct watchdog_info atcwdt_info = {
98*e4e0848aSCL Wang 	.identity = DRV_NAME,
99*e4e0848aSCL Wang 	.options = WDIOF_SETTIMEOUT |
100*e4e0848aSCL Wang 		   WDIOF_KEEPALIVEPING |
101*e4e0848aSCL Wang 		   WDIOF_MAGICCLOSE,
102*e4e0848aSCL Wang };
103*e4e0848aSCL Wang 
104*e4e0848aSCL Wang /**
105*e4e0848aSCL Wang  * atcwdt_get_index - Get the interval value for the specified timer type
106*e4e0848aSCL Wang  * @index: The index of the interval in the array
107*e4e0848aSCL Wang  * @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or
108*e4e0848aSCL Wang  *              TMR_INT_32.
109*e4e0848aSCL Wang  *
110*e4e0848aSCL Wang  * This function retrieves the interval value based on the timer type and
111*e4e0848aSCL Wang  * ensures the index stays within the valid range for the given timer type.
112*e4e0848aSCL Wang  * For TMR_RST:
113*e4e0848aSCL Wang  *  - The maximum array size is 8 (index range: 0-7).
114*e4e0848aSCL Wang  * For TMR_INT_16:
115*e4e0848aSCL Wang  *  - The maximum array size is 8 (index range: 0-7).
116*e4e0848aSCL Wang  * For TMR_INT_32:
117*e4e0848aSCL Wang  *  - The maximum array size is 16 (index range: 0-15).
118*e4e0848aSCL Wang  *
119*e4e0848aSCL Wang  * If the index exceeds the maximum array size, the function will return
120*e4e0848aSCL Wang  * the last element of the respective array.
121*e4e0848aSCL Wang  */
122*e4e0848aSCL Wang static inline unsigned char atcwdt_get_index(unsigned char index,
123*e4e0848aSCL Wang 					     enum timer_type timer_type)
124*e4e0848aSCL Wang {
125*e4e0848aSCL Wang 	static const unsigned char rst_timer_interval[TMR_SZ_RST] = {
126*e4e0848aSCL Wang 		7, 8, 9, 10, 11, 12, 13, 14};
127*e4e0848aSCL Wang 	static const unsigned char int_timer_interval[TMR_SZ_INT_32] = {
128*e4e0848aSCL Wang 		6, 8, 10, 11, 12, 13, 14, 15, 17, 19, 21, 23, 25, 27, 29, 31};
129*e4e0848aSCL Wang 	unsigned char array_index;
130*e4e0848aSCL Wang 
131*e4e0848aSCL Wang 	if (timer_type == TMR_RST) {
132*e4e0848aSCL Wang 		array_index = min(index, TMR_SZ_RST - 1);
133*e4e0848aSCL Wang 		return rst_timer_interval[array_index];
134*e4e0848aSCL Wang 	}
135*e4e0848aSCL Wang 
136*e4e0848aSCL Wang 	if (timer_type == TMR_INT_32)
137*e4e0848aSCL Wang 		array_index = min(index, TMR_SZ_INT_32 - 1);
138*e4e0848aSCL Wang 	else
139*e4e0848aSCL Wang 		array_index = min(index, TMR_SZ_INT_16 - 1);
140*e4e0848aSCL Wang 
141*e4e0848aSCL Wang 	return int_timer_interval[array_index];
142*e4e0848aSCL Wang }
143*e4e0848aSCL Wang 
144*e4e0848aSCL Wang /**
145*e4e0848aSCL Wang  * atcwdt_get_clock_period - Calculate the closest clock period based on a
146*e4e0848aSCL Wang  *                           given tick count
147*e4e0848aSCL Wang  * @tick: The target tick count to match
148*e4e0848aSCL Wang  * @timer_type: The type of timer, which can be TMR_RST, TMR_INT_16, or
149*e4e0848aSCL Wang  *              TMR_INT_32.
150*e4e0848aSCL Wang  * @index: Pointer to store the index of the selected parameter
151*e4e0848aSCL Wang  *
152*e4e0848aSCL Wang  * This function calculates the closest clock period to the given tick count
153*e4e0848aSCL Wang  * by iterating through the timer parameters and selecting the one that
154*e4e0848aSCL Wang  * minimizes the difference between the target tick count and the calculated
155*e4e0848aSCL Wang  * clock period. The function determines the index of the closest parameter
156*e4e0848aSCL Wang  * and returns the difference between the target tick count and the selected
157*e4e0848aSCL Wang  * clock period.
158*e4e0848aSCL Wang  *
159*e4e0848aSCL Wang  * Return: The difference between the target tick count and the selected
160*e4e0848aSCL Wang  * clock period.
161*e4e0848aSCL Wang  */
162*e4e0848aSCL Wang static long long atcwdt_get_clock_period(long long tick,
163*e4e0848aSCL Wang 					 enum timer_type timer_type,
164*e4e0848aSCL Wang 					 unsigned char *index)
165*e4e0848aSCL Wang {
166*e4e0848aSCL Wang 	long long result;
167*e4e0848aSCL Wang 	unsigned char size;
168*e4e0848aSCL Wang 	char i;
169*e4e0848aSCL Wang 
170*e4e0848aSCL Wang 	if (timer_type == TMR_RST)
171*e4e0848aSCL Wang 		size = TMR_SZ_RST;
172*e4e0848aSCL Wang 	else if (timer_type == TMR_INT_32)
173*e4e0848aSCL Wang 		size = TMR_SZ_INT_32;
174*e4e0848aSCL Wang 	else
175*e4e0848aSCL Wang 		size = TMR_SZ_INT_16;
176*e4e0848aSCL Wang 
177*e4e0848aSCL Wang 	*index = size - 1;
178*e4e0848aSCL Wang 	for (i = 0; i < size; i++) {
179*e4e0848aSCL Wang 		result = tick - (1LL << atcwdt_get_index(i, timer_type));
180*e4e0848aSCL Wang 
181*e4e0848aSCL Wang 		if (result <= 1) {
182*e4e0848aSCL Wang 			*index = i;
183*e4e0848aSCL Wang 			break;
184*e4e0848aSCL Wang 		}
185*e4e0848aSCL Wang 	}
186*e4e0848aSCL Wang 
187*e4e0848aSCL Wang 	return result;
188*e4e0848aSCL Wang }
189*e4e0848aSCL Wang 
190*e4e0848aSCL Wang /**
191*e4e0848aSCL Wang  * atcwdt_get_timeout_params - Calculate optimal parameters for Watchdog Timer
192*e4e0848aSCL Wang  * @drv_data: Pointer to the Watchdog driver data structure
193*e4e0848aSCL Wang  * @timeout: Desired timeout value (in seconds)
194*e4e0848aSCL Wang  * @int_timer_params: Pointer to store the calculated interrupt timer
195*e4e0848aSCL Wang  *                    parameter index
196*e4e0848aSCL Wang  * @rst_timer_params: Pointer to store the calculated reset timer parameter
197*e4e0848aSCL Wang  *                    index
198*e4e0848aSCL Wang  *
199*e4e0848aSCL Wang  * This function calculates the optimal parameter combination for the
200*e4e0848aSCL Wang  * interrupt timer and reset timer of the Watchdog Timer to achieve a
201*e4e0848aSCL Wang  * timeout value closest to, but not less than the specified timeout.
202*e4e0848aSCL Wang  *
203*e4e0848aSCL Wang  * Algorithm:
204*e4e0848aSCL Wang  * 1. The parameters for both the interrupt timer and reset timer are
205*e4e0848aSCL Wang  *    predefined as a series of options represented as powers of 2.
206*e4e0848aSCL Wang  * 2. The function first determines the interrupt timer's parameter index
207*e4e0848aSCL Wang  *    that provides a time closest to and not exceeding the desired timeout.
208*e4e0848aSCL Wang  * 3. Based on the selected interrupt timer, it calculates the required
209*e4e0848aSCL Wang  *    reset timer parameter to ensure the total timeout matches the target.
210*e4e0848aSCL Wang  *
211*e4e0848aSCL Wang  * Return: The calculated parameter indices are stored in the provided
212*e4e0848aSCL Wang  *         pointers.
213*e4e0848aSCL Wang  */
214*e4e0848aSCL Wang static void atcwdt_get_timeout_params(struct atcwdt_drv *drv_data,
215*e4e0848aSCL Wang 				      unsigned int timeout,
216*e4e0848aSCL Wang 				      unsigned char *int_timer_params,
217*e4e0848aSCL Wang 				      unsigned char *rst_timer_params)
218*e4e0848aSCL Wang {
219*e4e0848aSCL Wang 	long long rest_time_ms;
220*e4e0848aSCL Wang 	long long result;
221*e4e0848aSCL Wang 	long long tick;
222*e4e0848aSCL Wang 	unsigned char rst_index;
223*e4e0848aSCL Wang 	unsigned char int_index;
224*e4e0848aSCL Wang 	unsigned char above;
225*e4e0848aSCL Wang 	unsigned char below;
226*e4e0848aSCL Wang 
227*e4e0848aSCL Wang 	tick = (long long)timeout * drv_data->clk_freq;
228*e4e0848aSCL Wang 	result = atcwdt_get_clock_period(tick,
229*e4e0848aSCL Wang 					 drv_data->int_timer_type,
230*e4e0848aSCL Wang 					 &above);
231*e4e0848aSCL Wang 	if (result == 0 || above == 0) {
232*e4e0848aSCL Wang 		*int_timer_params = above;
233*e4e0848aSCL Wang 		*rst_timer_params = 0;
234*e4e0848aSCL Wang 		return;
235*e4e0848aSCL Wang 	}
236*e4e0848aSCL Wang 	below = above - 1;
237*e4e0848aSCL Wang 
238*e4e0848aSCL Wang 	int_index = atcwdt_get_index(below, drv_data->int_timer_type);
239*e4e0848aSCL Wang 	rest_time_ms = timeout * 1000LL
240*e4e0848aSCL Wang 		       - div64_s64(1000LL << int_index, drv_data->clk_freq);
241*e4e0848aSCL Wang 
242*e4e0848aSCL Wang 	result = atcwdt_get_clock_period(rest_time_ms * drv_data->clk_freq,
243*e4e0848aSCL Wang 					 TMR_RST,
244*e4e0848aSCL Wang 					 &rst_index);
245*e4e0848aSCL Wang 
246*e4e0848aSCL Wang 	if (result > 1) {
247*e4e0848aSCL Wang 		*int_timer_params = above;
248*e4e0848aSCL Wang 		*rst_timer_params = 0;
249*e4e0848aSCL Wang 	} else {
250*e4e0848aSCL Wang 		*int_timer_params = below;
251*e4e0848aSCL Wang 		*rst_timer_params = rst_index;
252*e4e0848aSCL Wang 	}
253*e4e0848aSCL Wang }
254*e4e0848aSCL Wang 
255*e4e0848aSCL Wang /**
256*e4e0848aSCL Wang  * atcwdt_get_int_timer_type - Get the supported interrupt timer type.
257*e4e0848aSCL Wang  * @drv_data: Pointer to the watchdog driver data structure.
258*e4e0848aSCL Wang  *
259*e4e0848aSCL Wang  * This function tests the writable bits in the IntTime field of the control
260*e4e0848aSCL Wang  * register to determine the interrupt timer type supported by the hardware.
261*e4e0848aSCL Wang  *
262*e4e0848aSCL Wang  * Note: This function must only be called when the ATCWDT200 watchdog is
263*e4e0848aSCL Wang  * disabled. If the watchdog is enabled, this function returns TMR_UNKNOWN.
264*e4e0848aSCL Wang  *
265*e4e0848aSCL Wang  * Returns: The interrupt timer type supported by the hardware.
266*e4e0848aSCL Wang  */
267*e4e0848aSCL Wang static int atcwdt_get_int_timer_type(struct atcwdt_drv *drv_data)
268*e4e0848aSCL Wang {
269*e4e0848aSCL Wang 	struct device *dev = drv_data->wdt_dev.parent;
270*e4e0848aSCL Wang 	unsigned int val;
271*e4e0848aSCL Wang 	int ret  = 0;
272*e4e0848aSCL Wang 
273*e4e0848aSCL Wang 	spin_lock(&drv_data->lock);
274*e4e0848aSCL Wang 	regmap_read(drv_data->regmap, REG_CTRL, &val);
275*e4e0848aSCL Wang 	if (val & CTRL_WDT_EN) {
276*e4e0848aSCL Wang 		spin_unlock(&drv_data->lock);
277*e4e0848aSCL Wang 		return TMR_UNKNOWN;
278*e4e0848aSCL Wang 	}
279*e4e0848aSCL Wang 
280*e4e0848aSCL Wang 	/*
281*e4e0848aSCL Wang 	 * Configures the IntTime field with the maximum mask value
282*e4e0848aSCL Wang 	 * (CTRL_INT_TIME_MSK), reads its value from the control register
283*e4e0848aSCL Wang 	 * to identify the maximum writable bits.
284*e4e0848aSCL Wang 	 */
285*e4e0848aSCL Wang 	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
286*e4e0848aSCL Wang 	regmap_write(drv_data->regmap, REG_CTRL, CTRL_INT_TIME_MSK);
287*e4e0848aSCL Wang 	regmap_read(drv_data->regmap, REG_CTRL, &val);
288*e4e0848aSCL Wang 	spin_unlock(&drv_data->lock);
289*e4e0848aSCL Wang 
290*e4e0848aSCL Wang 	val = CTRL_INT_TIME_GET(val);
291*e4e0848aSCL Wang 	switch (val) {
292*e4e0848aSCL Wang 	case 7:
293*e4e0848aSCL Wang 		drv_data->int_timer_type = TMR_INT_16;
294*e4e0848aSCL Wang 		break;
295*e4e0848aSCL Wang 	case 15:
296*e4e0848aSCL Wang 		drv_data->int_timer_type = TMR_INT_32;
297*e4e0848aSCL Wang 		break;
298*e4e0848aSCL Wang 	default:
299*e4e0848aSCL Wang 		drv_data->int_timer_type = TMR_UNKNOWN;
300*e4e0848aSCL Wang 		ret = dev_err_probe(dev, -ENODEV,
301*e4e0848aSCL Wang 				    "Failed to detect interrupt timer type\n");
302*e4e0848aSCL Wang 	}
303*e4e0848aSCL Wang 
304*e4e0848aSCL Wang 	return ret;
305*e4e0848aSCL Wang }
306*e4e0848aSCL Wang 
307*e4e0848aSCL Wang static int atcwdt_ping(struct watchdog_device *wdt_dev)
308*e4e0848aSCL Wang {
309*e4e0848aSCL Wang 	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
310*e4e0848aSCL Wang 
311*e4e0848aSCL Wang 	spin_lock(&drv_data->lock);
312*e4e0848aSCL Wang 	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
313*e4e0848aSCL Wang 	regmap_write(drv_data->regmap, REG_RESTART, RESTART_MAGIC);
314*e4e0848aSCL Wang 	regmap_update_bits(drv_data->regmap, REG_STATUS, STATUS_INT_EXPIRED,
315*e4e0848aSCL Wang 			   STATUS_INT_EXPIRED);
316*e4e0848aSCL Wang 	spin_unlock(&drv_data->lock);
317*e4e0848aSCL Wang 
318*e4e0848aSCL Wang 	return 0;
319*e4e0848aSCL Wang }
320*e4e0848aSCL Wang 
321*e4e0848aSCL Wang static int atcwdt_set_timeout(struct watchdog_device *wdt_dev,
322*e4e0848aSCL Wang 			      unsigned int timeout)
323*e4e0848aSCL Wang {
324*e4e0848aSCL Wang 	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
325*e4e0848aSCL Wang 	unsigned int value;
326*e4e0848aSCL Wang 	unsigned char  rst_val;
327*e4e0848aSCL Wang 	unsigned char  int_val;
328*e4e0848aSCL Wang 
329*e4e0848aSCL Wang 	wdt_dev->timeout = timeout;
330*e4e0848aSCL Wang 	atcwdt_get_timeout_params(drv_data, timeout, &int_val, &rst_val);
331*e4e0848aSCL Wang 
332*e4e0848aSCL Wang 	spin_lock(&drv_data->lock);
333*e4e0848aSCL Wang 	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
334*e4e0848aSCL Wang 
335*e4e0848aSCL Wang 	value = CTRL_RST_TIME_SET(rst_val) |
336*e4e0848aSCL Wang 		CTRL_INT_TIME_SET(int_val) |
337*e4e0848aSCL Wang 		CTRL_CLK_SEL_SET(drv_data->clk_src);
338*e4e0848aSCL Wang 	regmap_update_bits(drv_data->regmap,
339*e4e0848aSCL Wang 			   REG_CTRL,
340*e4e0848aSCL Wang 			   CTRL_RST_TIME_MSK |
341*e4e0848aSCL Wang 			   CTRL_INT_TIME_MSK |
342*e4e0848aSCL Wang 			   CTRL_CLK_SEL,
343*e4e0848aSCL Wang 			   value);
344*e4e0848aSCL Wang 
345*e4e0848aSCL Wang 	spin_unlock(&drv_data->lock);
346*e4e0848aSCL Wang 	atcwdt_ping(wdt_dev);
347*e4e0848aSCL Wang 
348*e4e0848aSCL Wang 	return 0;
349*e4e0848aSCL Wang }
350*e4e0848aSCL Wang 
351*e4e0848aSCL Wang static int atcwdt_start(struct watchdog_device *wdt_dev)
352*e4e0848aSCL Wang {
353*e4e0848aSCL Wang 	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
354*e4e0848aSCL Wang 
355*e4e0848aSCL Wang 	atcwdt_set_timeout(wdt_dev, wdt_dev->timeout);
356*e4e0848aSCL Wang 
357*e4e0848aSCL Wang 	spin_lock(&drv_data->lock);
358*e4e0848aSCL Wang 	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
359*e4e0848aSCL Wang 	regmap_update_bits(drv_data->regmap,
360*e4e0848aSCL Wang 			   REG_CTRL,
361*e4e0848aSCL Wang 			   CTRL_RST_EN | CTRL_WDT_EN,
362*e4e0848aSCL Wang 			   CTRL_RST_EN | CTRL_WDT_EN);
363*e4e0848aSCL Wang 
364*e4e0848aSCL Wang 	spin_unlock(&drv_data->lock);
365*e4e0848aSCL Wang 
366*e4e0848aSCL Wang 	return 0;
367*e4e0848aSCL Wang }
368*e4e0848aSCL Wang 
369*e4e0848aSCL Wang static int atcwdt_stop(struct watchdog_device *wdt_dev)
370*e4e0848aSCL Wang {
371*e4e0848aSCL Wang 	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
372*e4e0848aSCL Wang 
373*e4e0848aSCL Wang 	spin_lock(&drv_data->lock);
374*e4e0848aSCL Wang 	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
375*e4e0848aSCL Wang 	regmap_update_bits(drv_data->regmap,
376*e4e0848aSCL Wang 			   REG_CTRL,
377*e4e0848aSCL Wang 			   CTRL_RST_EN | CTRL_WDT_EN,
378*e4e0848aSCL Wang 			   0);
379*e4e0848aSCL Wang 	spin_unlock(&drv_data->lock);
380*e4e0848aSCL Wang 
381*e4e0848aSCL Wang 	return 0;
382*e4e0848aSCL Wang }
383*e4e0848aSCL Wang 
384*e4e0848aSCL Wang static int atcwdt_restart(struct watchdog_device *wdt_dev,
385*e4e0848aSCL Wang 			  unsigned long action, void *data)
386*e4e0848aSCL Wang {
387*e4e0848aSCL Wang 	struct atcwdt_drv *drv_data = watchdog_get_drvdata(wdt_dev);
388*e4e0848aSCL Wang 
389*e4e0848aSCL Wang 	atcwdt_set_timeout(wdt_dev, 0);
390*e4e0848aSCL Wang 
391*e4e0848aSCL Wang 	spin_lock(&drv_data->lock);
392*e4e0848aSCL Wang 	regmap_write(drv_data->regmap, REG_WRITE_EN, WRITE_EN_MAGIC);
393*e4e0848aSCL Wang 	regmap_update_bits(drv_data->regmap,
394*e4e0848aSCL Wang 			   REG_CTRL,
395*e4e0848aSCL Wang 			   CTRL_RST_EN | CTRL_WDT_EN,
396*e4e0848aSCL Wang 			   CTRL_RST_EN | CTRL_WDT_EN);
397*e4e0848aSCL Wang 	spin_unlock(&drv_data->lock);
398*e4e0848aSCL Wang 
399*e4e0848aSCL Wang 	return 0;
400*e4e0848aSCL Wang }
401*e4e0848aSCL Wang 
402*e4e0848aSCL Wang static const struct watchdog_ops atcwdt_ops = {
403*e4e0848aSCL Wang 	.owner = THIS_MODULE,
404*e4e0848aSCL Wang 	.start = atcwdt_start,
405*e4e0848aSCL Wang 	.stop = atcwdt_stop,
406*e4e0848aSCL Wang 	.ping = atcwdt_ping,
407*e4e0848aSCL Wang 	.set_timeout = atcwdt_set_timeout,
408*e4e0848aSCL Wang 	.restart = atcwdt_restart,
409*e4e0848aSCL Wang };
410*e4e0848aSCL Wang 
411*e4e0848aSCL Wang static int atcwdt_init_resource(struct platform_device *pdev,
412*e4e0848aSCL Wang 				struct atcwdt_drv *drv_data)
413*e4e0848aSCL Wang {
414*e4e0848aSCL Wang 	struct device *dev = &pdev->dev;
415*e4e0848aSCL Wang 	void __iomem *base;
416*e4e0848aSCL Wang 	const struct regmap_config cfg = {
417*e4e0848aSCL Wang 		.name = "atcwdt",
418*e4e0848aSCL Wang 		.reg_bits = 32,
419*e4e0848aSCL Wang 		.val_bits = 32,
420*e4e0848aSCL Wang 		.cache_type = REGCACHE_NONE,
421*e4e0848aSCL Wang 		.reg_stride = 4,
422*e4e0848aSCL Wang 		.max_register = REG_STATUS,
423*e4e0848aSCL Wang 	};
424*e4e0848aSCL Wang 
425*e4e0848aSCL Wang 	base = devm_platform_ioremap_resource(pdev, 0);
426*e4e0848aSCL Wang 	if (IS_ERR(base))
427*e4e0848aSCL Wang 		return dev_err_probe(dev, PTR_ERR(base),
428*e4e0848aSCL Wang 				     "Failed to ioremap I/O resource\n");
429*e4e0848aSCL Wang 
430*e4e0848aSCL Wang 	drv_data->regmap = devm_regmap_init_mmio(dev, base, &cfg);
431*e4e0848aSCL Wang 	if (IS_ERR(drv_data->regmap))
432*e4e0848aSCL Wang 		return dev_err_probe(dev, PTR_ERR(drv_data->regmap),
433*e4e0848aSCL Wang 				     "Failed to create regmap\n");
434*e4e0848aSCL Wang 
435*e4e0848aSCL Wang 	return 0;
436*e4e0848aSCL Wang }
437*e4e0848aSCL Wang 
438*e4e0848aSCL Wang static int atcwdt_enable_clk(struct atcwdt_drv *drv_data)
439*e4e0848aSCL Wang {
440*e4e0848aSCL Wang 	struct device *dev = drv_data->wdt_dev.parent;
441*e4e0848aSCL Wang 	unsigned int val;
442*e4e0848aSCL Wang 	int clk_src;
443*e4e0848aSCL Wang 
444*e4e0848aSCL Wang 	drv_data->clk = devm_clk_get_enabled(dev, NULL);
445*e4e0848aSCL Wang 	if (IS_ERR(drv_data->clk))
446*e4e0848aSCL Wang 		return dev_err_probe(dev, PTR_ERR(drv_data->clk),
447*e4e0848aSCL Wang 				     "Failed to get watchdog clock\n");
448*e4e0848aSCL Wang 
449*e4e0848aSCL Wang 	drv_data->clk_freq = clk_get_rate(drv_data->clk);
450*e4e0848aSCL Wang 	if (!drv_data->clk_freq)
451*e4e0848aSCL Wang 		return dev_err_probe(dev, -EINVAL,
452*e4e0848aSCL Wang 				     "Failed to get clock rate\n");
453*e4e0848aSCL Wang 
454*e4e0848aSCL Wang 	clk_src = device_property_read_u32(dev, "andestech,clock-source", &val);
455*e4e0848aSCL Wang 	drv_data->clk_src = (!clk_src && val != 0) ? CTRL_CLK_SEL_PCLK : 0;
456*e4e0848aSCL Wang 
457*e4e0848aSCL Wang 	return 0;
458*e4e0848aSCL Wang }
459*e4e0848aSCL Wang 
460*e4e0848aSCL Wang static int atcwdt_init_wdt_device(struct device *dev,
461*e4e0848aSCL Wang 				  struct atcwdt_drv *drv_data)
462*e4e0848aSCL Wang {
463*e4e0848aSCL Wang 	struct watchdog_device *wdd = &drv_data->wdt_dev;
464*e4e0848aSCL Wang 
465*e4e0848aSCL Wang 	wdd->parent = dev;
466*e4e0848aSCL Wang 	wdd->info = &atcwdt_info;
467*e4e0848aSCL Wang 	wdd->ops = &atcwdt_ops;
468*e4e0848aSCL Wang 	wdd->timeout = ATCWDT_TIMEOUT;
469*e4e0848aSCL Wang 	wdd->min_timeout = 1;
470*e4e0848aSCL Wang 
471*e4e0848aSCL Wang 	watchdog_set_nowayout(wdd, nowayout);
472*e4e0848aSCL Wang 	watchdog_set_drvdata(wdd, drv_data);
473*e4e0848aSCL Wang 
474*e4e0848aSCL Wang 	return 0;
475*e4e0848aSCL Wang }
476*e4e0848aSCL Wang 
477*e4e0848aSCL Wang static void atcwdt_calc_max_timeout(struct atcwdt_drv *drv_data)
478*e4e0848aSCL Wang {
479*e4e0848aSCL Wang 	unsigned char rst_idx = atcwdt_get_index(0xFF, TMR_RST);
480*e4e0848aSCL Wang 	unsigned char int_idx = atcwdt_get_index(0xFF,
481*e4e0848aSCL Wang 						 drv_data->int_timer_type);
482*e4e0848aSCL Wang 
483*e4e0848aSCL Wang 	drv_data->wdt_dev.max_timeout =
484*e4e0848aSCL Wang 		((1U << rst_idx) + (1U << int_idx)) / drv_data->clk_freq;
485*e4e0848aSCL Wang }
486*e4e0848aSCL Wang 
487*e4e0848aSCL Wang static int atcwdt_probe(struct platform_device *pdev)
488*e4e0848aSCL Wang {
489*e4e0848aSCL Wang 	struct device *dev = &pdev->dev;
490*e4e0848aSCL Wang 	struct atcwdt_drv *drv_data;
491*e4e0848aSCL Wang 	int ret;
492*e4e0848aSCL Wang 
493*e4e0848aSCL Wang 	drv_data = devm_kzalloc(dev, sizeof(*drv_data), GFP_KERNEL);
494*e4e0848aSCL Wang 	if (!drv_data)
495*e4e0848aSCL Wang 		return -ENOMEM;
496*e4e0848aSCL Wang 
497*e4e0848aSCL Wang 	platform_set_drvdata(pdev, drv_data);
498*e4e0848aSCL Wang 	spin_lock_init(&drv_data->lock);
499*e4e0848aSCL Wang 
500*e4e0848aSCL Wang 	ret = atcwdt_init_wdt_device(dev, drv_data);
501*e4e0848aSCL Wang 	if (ret)
502*e4e0848aSCL Wang 		return ret;
503*e4e0848aSCL Wang 
504*e4e0848aSCL Wang 	ret = atcwdt_init_resource(pdev, drv_data);
505*e4e0848aSCL Wang 	if (ret)
506*e4e0848aSCL Wang 		return ret;
507*e4e0848aSCL Wang 
508*e4e0848aSCL Wang 	ret = atcwdt_enable_clk(drv_data);
509*e4e0848aSCL Wang 	if (ret)
510*e4e0848aSCL Wang 		return ret;
511*e4e0848aSCL Wang 
512*e4e0848aSCL Wang 	ret = atcwdt_get_int_timer_type(drv_data);
513*e4e0848aSCL Wang 	if (ret)
514*e4e0848aSCL Wang 		return ret;
515*e4e0848aSCL Wang 
516*e4e0848aSCL Wang 	atcwdt_calc_max_timeout(drv_data);
517*e4e0848aSCL Wang 
518*e4e0848aSCL Wang 	ret = devm_watchdog_register_device(dev, &drv_data->wdt_dev);
519*e4e0848aSCL Wang 
520*e4e0848aSCL Wang 	return ret;
521*e4e0848aSCL Wang }
522*e4e0848aSCL Wang 
523*e4e0848aSCL Wang static int atcwdt_suspend(struct device *dev)
524*e4e0848aSCL Wang {
525*e4e0848aSCL Wang 	struct atcwdt_drv *drv_data = dev_get_drvdata(dev);
526*e4e0848aSCL Wang 
527*e4e0848aSCL Wang 	if (watchdog_active(&drv_data->wdt_dev)) {
528*e4e0848aSCL Wang 		atcwdt_stop(&drv_data->wdt_dev);
529*e4e0848aSCL Wang 		clk_disable_unprepare(drv_data->clk);
530*e4e0848aSCL Wang 	}
531*e4e0848aSCL Wang 
532*e4e0848aSCL Wang 	return 0;
533*e4e0848aSCL Wang }
534*e4e0848aSCL Wang 
535*e4e0848aSCL Wang static int atcwdt_resume(struct device *dev)
536*e4e0848aSCL Wang {
537*e4e0848aSCL Wang 	struct atcwdt_drv *drv_data = dev_get_drvdata(dev);
538*e4e0848aSCL Wang 	int ret = 0;
539*e4e0848aSCL Wang 
540*e4e0848aSCL Wang 	if (watchdog_active(&drv_data->wdt_dev)) {
541*e4e0848aSCL Wang 		ret = clk_prepare_enable(drv_data->clk);
542*e4e0848aSCL Wang 		if (ret)
543*e4e0848aSCL Wang 			return ret;
544*e4e0848aSCL Wang 		atcwdt_start(&drv_data->wdt_dev);
545*e4e0848aSCL Wang 		atcwdt_ping(&drv_data->wdt_dev);
546*e4e0848aSCL Wang 	}
547*e4e0848aSCL Wang 
548*e4e0848aSCL Wang 	return ret;
549*e4e0848aSCL Wang }
550*e4e0848aSCL Wang 
551*e4e0848aSCL Wang static const struct of_device_id atcwdt_match[] = {
552*e4e0848aSCL Wang 	{ .compatible = "andestech,ae350-wdt" },
553*e4e0848aSCL Wang 	{ /* sentinel */ },
554*e4e0848aSCL Wang };
555*e4e0848aSCL Wang MODULE_DEVICE_TABLE(of, atcwdt_match);
556*e4e0848aSCL Wang 
557*e4e0848aSCL Wang static DEFINE_SIMPLE_DEV_PM_OPS(atcwdt_pm_ops, atcwdt_suspend, atcwdt_resume);
558*e4e0848aSCL Wang 
559*e4e0848aSCL Wang static struct platform_driver atcwdt_driver = {
560*e4e0848aSCL Wang 	.probe = atcwdt_probe,
561*e4e0848aSCL Wang 	.driver = {
562*e4e0848aSCL Wang 		.name = DRV_NAME,
563*e4e0848aSCL Wang 		.of_match_table = atcwdt_match,
564*e4e0848aSCL Wang 		.pm = pm_sleep_ptr(&atcwdt_pm_ops),
565*e4e0848aSCL Wang 	},
566*e4e0848aSCL Wang };
567*e4e0848aSCL Wang 
568*e4e0848aSCL Wang module_platform_driver(atcwdt_driver);
569*e4e0848aSCL Wang 
570*e4e0848aSCL Wang module_param(timeout, uint, 0);
571*e4e0848aSCL Wang MODULE_PARM_DESC(timeout, "Watchdog timeout in seconds (default="
572*e4e0848aSCL Wang 		 __MODULE_STRING(ATCWDT_TIMEOUT) ")");
573*e4e0848aSCL Wang 
574*e4e0848aSCL Wang module_param(nowayout, bool, 0);
575*e4e0848aSCL Wang MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default="
576*e4e0848aSCL Wang 		 __MODULE_STRING(WATCHDOG_NOWAYOUT) ")");
577*e4e0848aSCL Wang 
578*e4e0848aSCL Wang MODULE_LICENSE("GPL");
579*e4e0848aSCL Wang MODULE_AUTHOR("CL Wang <cl634@andestech.com>");
580*e4e0848aSCL Wang MODULE_DESCRIPTION("Andes ATCWDT200 Watchdog timer driver");
581