1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * drivers/char/watchdog/davinci_wdt.c |
4 | * |
5 | * Watchdog driver for DaVinci DM644x/DM646x processors |
6 | * |
7 | * Copyright (C) 2006-2013 Texas Instruments. |
8 | * |
9 | * 2007 (c) MontaVista Software, Inc. |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/moduleparam.h> |
14 | #include <linux/mod_devicetable.h> |
15 | #include <linux/types.h> |
16 | #include <linux/kernel.h> |
17 | #include <linux/watchdog.h> |
18 | #include <linux/platform_device.h> |
19 | #include <linux/io.h> |
20 | #include <linux/device.h> |
21 | #include <linux/clk.h> |
22 | #include <linux/err.h> |
23 | |
24 | #define MODULE_NAME "DAVINCI-WDT: " |
25 | |
26 | #define DEFAULT_HEARTBEAT 60 |
27 | #define MAX_HEARTBEAT 600 /* really the max margin is 264/27MHz*/ |
28 | |
29 | /* Timer register set definition */ |
30 | #define PID12 (0x0) |
31 | #define EMUMGT (0x4) |
32 | #define TIM12 (0x10) |
33 | #define TIM34 (0x14) |
34 | #define PRD12 (0x18) |
35 | #define PRD34 (0x1C) |
36 | #define TCR (0x20) |
37 | #define TGCR (0x24) |
38 | #define WDTCR (0x28) |
39 | |
40 | /* TCR bit definitions */ |
41 | #define ENAMODE12_DISABLED (0 << 6) |
42 | #define ENAMODE12_ONESHOT (1 << 6) |
43 | #define ENAMODE12_PERIODIC (2 << 6) |
44 | |
45 | /* TGCR bit definitions */ |
46 | #define TIM12RS_UNRESET (1 << 0) |
47 | #define TIM34RS_UNRESET (1 << 1) |
48 | #define TIMMODE_64BIT_WDOG (2 << 2) |
49 | |
50 | /* WDTCR bit definitions */ |
51 | #define WDEN (1 << 14) |
52 | #define WDFLAG (1 << 15) |
53 | #define WDKEY_SEQ0 (0xa5c6 << 16) |
54 | #define WDKEY_SEQ1 (0xda7e << 16) |
55 | |
56 | static int heartbeat; |
57 | |
58 | /* |
59 | * struct to hold data for each WDT device |
60 | * @base - base io address of WD device |
61 | * @clk - source clock of WDT |
62 | * @wdd - hold watchdog device as is in WDT core |
63 | */ |
64 | struct davinci_wdt_device { |
65 | void __iomem *base; |
66 | struct clk *clk; |
67 | struct watchdog_device wdd; |
68 | }; |
69 | |
70 | static int davinci_wdt_start(struct watchdog_device *wdd) |
71 | { |
72 | u32 tgcr; |
73 | u32 timer_margin; |
74 | unsigned long wdt_freq; |
75 | struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd); |
76 | |
77 | wdt_freq = clk_get_rate(clk: davinci_wdt->clk); |
78 | |
79 | /* disable, internal clock source */ |
80 | iowrite32(0, davinci_wdt->base + TCR); |
81 | /* reset timer, set mode to 64-bit watchdog, and unreset */ |
82 | iowrite32(0, davinci_wdt->base + TGCR); |
83 | tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET; |
84 | iowrite32(tgcr, davinci_wdt->base + TGCR); |
85 | /* clear counter regs */ |
86 | iowrite32(0, davinci_wdt->base + TIM12); |
87 | iowrite32(0, davinci_wdt->base + TIM34); |
88 | /* set timeout period */ |
89 | timer_margin = (((u64)wdd->timeout * wdt_freq) & 0xffffffff); |
90 | iowrite32(timer_margin, davinci_wdt->base + PRD12); |
91 | timer_margin = (((u64)wdd->timeout * wdt_freq) >> 32); |
92 | iowrite32(timer_margin, davinci_wdt->base + PRD34); |
93 | /* enable run continuously */ |
94 | iowrite32(ENAMODE12_PERIODIC, davinci_wdt->base + TCR); |
95 | /* Once the WDT is in pre-active state write to |
96 | * TIM12, TIM34, PRD12, PRD34, TCR, TGCR, WDTCR are |
97 | * write protected (except for the WDKEY field) |
98 | */ |
99 | /* put watchdog in pre-active state */ |
100 | iowrite32(WDKEY_SEQ0 | WDEN, davinci_wdt->base + WDTCR); |
101 | /* put watchdog in active state */ |
102 | iowrite32(WDKEY_SEQ1 | WDEN, davinci_wdt->base + WDTCR); |
103 | return 0; |
104 | } |
105 | |
106 | static int davinci_wdt_ping(struct watchdog_device *wdd) |
107 | { |
108 | struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd); |
109 | |
110 | /* put watchdog in service state */ |
111 | iowrite32(WDKEY_SEQ0, davinci_wdt->base + WDTCR); |
112 | /* put watchdog in active state */ |
113 | iowrite32(WDKEY_SEQ1, davinci_wdt->base + WDTCR); |
114 | return 0; |
115 | } |
116 | |
117 | static unsigned int davinci_wdt_get_timeleft(struct watchdog_device *wdd) |
118 | { |
119 | u64 timer_counter; |
120 | unsigned long freq; |
121 | u32 val; |
122 | struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd); |
123 | |
124 | /* if timeout has occured then return 0 */ |
125 | val = ioread32(davinci_wdt->base + WDTCR); |
126 | if (val & WDFLAG) |
127 | return 0; |
128 | |
129 | freq = clk_get_rate(clk: davinci_wdt->clk); |
130 | |
131 | if (!freq) |
132 | return 0; |
133 | |
134 | timer_counter = ioread32(davinci_wdt->base + TIM12); |
135 | timer_counter |= ((u64)ioread32(davinci_wdt->base + TIM34) << 32); |
136 | |
137 | timer_counter = div64_ul(timer_counter, freq); |
138 | |
139 | return wdd->timeout - timer_counter; |
140 | } |
141 | |
142 | static int davinci_wdt_restart(struct watchdog_device *wdd, |
143 | unsigned long action, void *data) |
144 | { |
145 | struct davinci_wdt_device *davinci_wdt = watchdog_get_drvdata(wdd); |
146 | u32 tgcr, wdtcr; |
147 | |
148 | /* disable, internal clock source */ |
149 | iowrite32(0, davinci_wdt->base + TCR); |
150 | |
151 | /* reset timer, set mode to 64-bit watchdog, and unreset */ |
152 | tgcr = 0; |
153 | iowrite32(tgcr, davinci_wdt->base + TGCR); |
154 | tgcr = TIMMODE_64BIT_WDOG | TIM12RS_UNRESET | TIM34RS_UNRESET; |
155 | iowrite32(tgcr, davinci_wdt->base + TGCR); |
156 | |
157 | /* clear counter and period regs */ |
158 | iowrite32(0, davinci_wdt->base + TIM12); |
159 | iowrite32(0, davinci_wdt->base + TIM34); |
160 | iowrite32(0, davinci_wdt->base + PRD12); |
161 | iowrite32(0, davinci_wdt->base + PRD34); |
162 | |
163 | /* put watchdog in pre-active state */ |
164 | wdtcr = WDKEY_SEQ0 | WDEN; |
165 | iowrite32(wdtcr, davinci_wdt->base + WDTCR); |
166 | |
167 | /* put watchdog in active state */ |
168 | wdtcr = WDKEY_SEQ1 | WDEN; |
169 | iowrite32(wdtcr, davinci_wdt->base + WDTCR); |
170 | |
171 | /* write an invalid value to the WDKEY field to trigger a restart */ |
172 | wdtcr = 0x00004000; |
173 | iowrite32(wdtcr, davinci_wdt->base + WDTCR); |
174 | |
175 | return 0; |
176 | } |
177 | |
178 | static const struct watchdog_info davinci_wdt_info = { |
179 | .options = WDIOF_KEEPALIVEPING, |
180 | .identity = "DaVinci/Keystone Watchdog" , |
181 | }; |
182 | |
183 | static const struct watchdog_ops davinci_wdt_ops = { |
184 | .owner = THIS_MODULE, |
185 | .start = davinci_wdt_start, |
186 | .stop = davinci_wdt_ping, |
187 | .ping = davinci_wdt_ping, |
188 | .get_timeleft = davinci_wdt_get_timeleft, |
189 | .restart = davinci_wdt_restart, |
190 | }; |
191 | |
192 | static int davinci_wdt_probe(struct platform_device *pdev) |
193 | { |
194 | struct device *dev = &pdev->dev; |
195 | struct watchdog_device *wdd; |
196 | struct davinci_wdt_device *davinci_wdt; |
197 | |
198 | davinci_wdt = devm_kzalloc(dev, size: sizeof(*davinci_wdt), GFP_KERNEL); |
199 | if (!davinci_wdt) |
200 | return -ENOMEM; |
201 | |
202 | davinci_wdt->clk = devm_clk_get_enabled(dev, NULL); |
203 | if (IS_ERR(ptr: davinci_wdt->clk)) |
204 | return dev_err_probe(dev, err: PTR_ERR(ptr: davinci_wdt->clk), |
205 | fmt: "failed to get clock node\n" ); |
206 | |
207 | platform_set_drvdata(pdev, data: davinci_wdt); |
208 | |
209 | wdd = &davinci_wdt->wdd; |
210 | wdd->info = &davinci_wdt_info; |
211 | wdd->ops = &davinci_wdt_ops; |
212 | wdd->min_timeout = 1; |
213 | wdd->max_timeout = MAX_HEARTBEAT; |
214 | wdd->timeout = DEFAULT_HEARTBEAT; |
215 | wdd->parent = dev; |
216 | |
217 | watchdog_init_timeout(wdd, timeout_parm: heartbeat, dev); |
218 | |
219 | dev_info(dev, "heartbeat %d sec\n" , wdd->timeout); |
220 | |
221 | watchdog_set_drvdata(wdd, data: davinci_wdt); |
222 | watchdog_set_nowayout(wdd, nowayout: 1); |
223 | watchdog_set_restart_priority(wdd, priority: 128); |
224 | |
225 | davinci_wdt->base = devm_platform_ioremap_resource(pdev, index: 0); |
226 | if (IS_ERR(ptr: davinci_wdt->base)) |
227 | return PTR_ERR(ptr: davinci_wdt->base); |
228 | |
229 | return devm_watchdog_register_device(dev, wdd); |
230 | } |
231 | |
232 | static const struct of_device_id davinci_wdt_of_match[] = { |
233 | { .compatible = "ti,davinci-wdt" , }, |
234 | {}, |
235 | }; |
236 | MODULE_DEVICE_TABLE(of, davinci_wdt_of_match); |
237 | |
238 | static struct platform_driver platform_wdt_driver = { |
239 | .driver = { |
240 | .name = "davinci-wdt" , |
241 | .of_match_table = davinci_wdt_of_match, |
242 | }, |
243 | .probe = davinci_wdt_probe, |
244 | }; |
245 | |
246 | module_platform_driver(platform_wdt_driver); |
247 | |
248 | MODULE_AUTHOR("Texas Instruments" ); |
249 | MODULE_DESCRIPTION("DaVinci Watchdog Driver" ); |
250 | |
251 | module_param(heartbeat, int, 0); |
252 | MODULE_PARM_DESC(heartbeat, |
253 | "Watchdog heartbeat period in seconds from 1 to " |
254 | __MODULE_STRING(MAX_HEARTBEAT) ", default " |
255 | __MODULE_STRING(DEFAULT_HEARTBEAT)); |
256 | |
257 | MODULE_LICENSE("GPL" ); |
258 | MODULE_ALIAS("platform:davinci-wdt" ); |
259 | |