xref: /openbmc/linux/drivers/watchdog/st_lpc_wdt.c (revision 2d96b44f)
1 /*
2  * ST's LPC Watchdog
3  *
4  * Copyright (C) 2014 STMicroelectronics -- All Rights Reserved
5  *
6  * Author: David Paris <david.paris@st.com> for STMicroelectronics
7  *         Lee Jones <lee.jones@linaro.org> for STMicroelectronics
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public Licence
11  * as published by the Free Software Foundation; either version
12  * 2 of the Licence, or (at your option) any later version.
13  */
14 
15 #include <linux/clk.h>
16 #include <linux/init.h>
17 #include <linux/io.h>
18 #include <linux/kernel.h>
19 #include <linux/mfd/syscon.h>
20 #include <linux/module.h>
21 #include <linux/of.h>
22 #include <linux/of_platform.h>
23 #include <linux/platform_device.h>
24 #include <linux/regmap.h>
25 #include <linux/watchdog.h>
26 
27 #include <dt-bindings/mfd/st-lpc.h>
28 
29 /* Low Power Alarm */
30 #define LPC_LPA_LSB_OFF			0x410
31 #define LPC_LPA_START_OFF		0x418
32 
33 /* LPC as WDT */
34 #define LPC_WDT_OFF			0x510
35 
36 static struct watchdog_device st_wdog_dev;
37 
38 struct st_wdog_syscfg {
39 	unsigned int reset_type_reg;
40 	unsigned int reset_type_mask;
41 	unsigned int enable_reg;
42 	unsigned int enable_mask;
43 };
44 
45 struct st_wdog {
46 	void __iomem *base;
47 	struct device *dev;
48 	struct regmap *regmap;
49 	struct st_wdog_syscfg *syscfg;
50 	struct clk *clk;
51 	unsigned long clkrate;
52 	bool warm_reset;
53 };
54 
55 static struct st_wdog_syscfg stih407_syscfg = {
56 	.enable_reg		= 0x204,
57 	.enable_mask		= BIT(19),
58 };
59 
60 static const struct of_device_id st_wdog_match[] = {
61 	{
62 		.compatible = "st,stih407-lpc",
63 		.data = &stih407_syscfg,
64 	},
65 	{},
66 };
67 MODULE_DEVICE_TABLE(of, st_wdog_match);
68 
69 static void st_wdog_setup(struct st_wdog *st_wdog, bool enable)
70 {
71 	/* Type of watchdog reset - 0: Cold 1: Warm */
72 	if (st_wdog->syscfg->reset_type_reg)
73 		regmap_update_bits(st_wdog->regmap,
74 				   st_wdog->syscfg->reset_type_reg,
75 				   st_wdog->syscfg->reset_type_mask,
76 				   st_wdog->warm_reset);
77 
78 	/* Mask/unmask watchdog reset */
79 	regmap_update_bits(st_wdog->regmap,
80 			   st_wdog->syscfg->enable_reg,
81 			   st_wdog->syscfg->enable_mask,
82 			   enable ? 0 : st_wdog->syscfg->enable_mask);
83 }
84 
85 static void st_wdog_load_timer(struct st_wdog *st_wdog, unsigned int timeout)
86 {
87 	unsigned long clkrate = st_wdog->clkrate;
88 
89 	writel_relaxed(timeout * clkrate, st_wdog->base + LPC_LPA_LSB_OFF);
90 	writel_relaxed(1, st_wdog->base + LPC_LPA_START_OFF);
91 }
92 
93 static int st_wdog_start(struct watchdog_device *wdd)
94 {
95 	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
96 
97 	writel_relaxed(1, st_wdog->base + LPC_WDT_OFF);
98 
99 	return 0;
100 }
101 
102 static int st_wdog_stop(struct watchdog_device *wdd)
103 {
104 	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
105 
106 	writel_relaxed(0, st_wdog->base + LPC_WDT_OFF);
107 
108 	return 0;
109 }
110 
111 static int st_wdog_set_timeout(struct watchdog_device *wdd,
112 			       unsigned int timeout)
113 {
114 	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
115 
116 	wdd->timeout = timeout;
117 	st_wdog_load_timer(st_wdog, timeout);
118 
119 	return 0;
120 }
121 
122 static int st_wdog_keepalive(struct watchdog_device *wdd)
123 {
124 	struct st_wdog *st_wdog = watchdog_get_drvdata(wdd);
125 
126 	st_wdog_load_timer(st_wdog, wdd->timeout);
127 
128 	return 0;
129 }
130 
131 static const struct watchdog_info st_wdog_info = {
132 	.options = WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING | WDIOF_MAGICCLOSE,
133 	.identity = "ST LPC WDT",
134 };
135 
136 static const struct watchdog_ops st_wdog_ops = {
137 	.owner		= THIS_MODULE,
138 	.start		= st_wdog_start,
139 	.stop		= st_wdog_stop,
140 	.ping		= st_wdog_keepalive,
141 	.set_timeout	= st_wdog_set_timeout,
142 };
143 
144 static struct watchdog_device st_wdog_dev = {
145 	.info		= &st_wdog_info,
146 	.ops		= &st_wdog_ops,
147 };
148 
149 static int st_wdog_probe(struct platform_device *pdev)
150 {
151 	const struct of_device_id *match;
152 	struct device_node *np = pdev->dev.of_node;
153 	struct st_wdog *st_wdog;
154 	struct regmap *regmap;
155 	struct resource *res;
156 	struct clk *clk;
157 	void __iomem *base;
158 	uint32_t mode;
159 	int ret;
160 
161 	ret = of_property_read_u32(np, "st,lpc-mode", &mode);
162 	if (ret) {
163 		dev_err(&pdev->dev, "An LPC mode must be provided\n");
164 		return -EINVAL;
165 	}
166 
167 	/* LPC can either run as a Clocksource or in RTC or WDT mode */
168 	if (mode != ST_LPC_MODE_WDT)
169 		return -ENODEV;
170 
171 	st_wdog = devm_kzalloc(&pdev->dev, sizeof(*st_wdog), GFP_KERNEL);
172 	if (!st_wdog)
173 		return -ENOMEM;
174 
175 	match = of_match_device(st_wdog_match, &pdev->dev);
176 	if (!match) {
177 		dev_err(&pdev->dev, "Couldn't match device\n");
178 		return -ENODEV;
179 	}
180 	st_wdog->syscfg	= (struct st_wdog_syscfg *)match->data;
181 
182 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
183 	base = devm_ioremap_resource(&pdev->dev, res);
184 	if (IS_ERR(base))
185 		return PTR_ERR(base);
186 
187 	regmap = syscon_regmap_lookup_by_phandle(np, "st,syscfg");
188 	if (IS_ERR(regmap)) {
189 		dev_err(&pdev->dev, "No syscfg phandle specified\n");
190 		return PTR_ERR(regmap);
191 	}
192 
193 	clk = devm_clk_get(&pdev->dev, NULL);
194 	if (IS_ERR(clk)) {
195 		dev_err(&pdev->dev, "Unable to request clock\n");
196 		return PTR_ERR(clk);
197 	}
198 
199 	st_wdog->dev		= &pdev->dev;
200 	st_wdog->base		= base;
201 	st_wdog->clk		= clk;
202 	st_wdog->regmap		= regmap;
203 	st_wdog->warm_reset	= of_property_read_bool(np, "st,warm_reset");
204 	st_wdog->clkrate	= clk_get_rate(st_wdog->clk);
205 
206 	if (!st_wdog->clkrate) {
207 		dev_err(&pdev->dev, "Unable to fetch clock rate\n");
208 		return -EINVAL;
209 	}
210 	st_wdog_dev.max_timeout = 0xFFFFFFFF / st_wdog->clkrate;
211 	st_wdog_dev.parent = &pdev->dev;
212 
213 	ret = clk_prepare_enable(clk);
214 	if (ret) {
215 		dev_err(&pdev->dev, "Unable to enable clock\n");
216 		return ret;
217 	}
218 
219 	watchdog_set_drvdata(&st_wdog_dev, st_wdog);
220 	watchdog_set_nowayout(&st_wdog_dev, WATCHDOG_NOWAYOUT);
221 
222 	/* Init Watchdog timeout with value in DT */
223 	ret = watchdog_init_timeout(&st_wdog_dev, 0, &pdev->dev);
224 	if (ret) {
225 		dev_err(&pdev->dev, "Unable to initialise watchdog timeout\n");
226 		clk_disable_unprepare(clk);
227 		return ret;
228 	}
229 
230 	ret = watchdog_register_device(&st_wdog_dev);
231 	if (ret) {
232 		dev_err(&pdev->dev, "Unable to register watchdog\n");
233 		clk_disable_unprepare(clk);
234 		return ret;
235 	}
236 
237 	st_wdog_setup(st_wdog, true);
238 
239 	dev_info(&pdev->dev, "LPC Watchdog driver registered, reset type is %s",
240 		 st_wdog->warm_reset ? "warm" : "cold");
241 
242 	return ret;
243 }
244 
245 static int st_wdog_remove(struct platform_device *pdev)
246 {
247 	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
248 
249 	st_wdog_setup(st_wdog, false);
250 	watchdog_unregister_device(&st_wdog_dev);
251 	clk_disable_unprepare(st_wdog->clk);
252 
253 	return 0;
254 }
255 
256 #ifdef CONFIG_PM_SLEEP
257 static int st_wdog_suspend(struct device *dev)
258 {
259 	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
260 
261 	if (watchdog_active(&st_wdog_dev))
262 		st_wdog_stop(&st_wdog_dev);
263 
264 	st_wdog_setup(st_wdog, false);
265 
266 	clk_disable(st_wdog->clk);
267 
268 	return 0;
269 }
270 
271 static int st_wdog_resume(struct device *dev)
272 {
273 	struct st_wdog *st_wdog = watchdog_get_drvdata(&st_wdog_dev);
274 	int ret;
275 
276 	ret = clk_enable(st_wdog->clk);
277 	if (ret) {
278 		dev_err(dev, "Unable to re-enable clock\n");
279 		watchdog_unregister_device(&st_wdog_dev);
280 		clk_unprepare(st_wdog->clk);
281 		return ret;
282 	}
283 
284 	st_wdog_setup(st_wdog, true);
285 
286 	if (watchdog_active(&st_wdog_dev)) {
287 		st_wdog_load_timer(st_wdog, st_wdog_dev.timeout);
288 		st_wdog_start(&st_wdog_dev);
289 	}
290 
291 	return 0;
292 }
293 #endif
294 
295 static SIMPLE_DEV_PM_OPS(st_wdog_pm_ops,
296 			 st_wdog_suspend,
297 			 st_wdog_resume);
298 
299 static struct platform_driver st_wdog_driver = {
300 	.driver	= {
301 		.name = "st-lpc-wdt",
302 		.pm = &st_wdog_pm_ops,
303 		.of_match_table = st_wdog_match,
304 	},
305 	.probe = st_wdog_probe,
306 	.remove = st_wdog_remove,
307 };
308 module_platform_driver(st_wdog_driver);
309 
310 MODULE_AUTHOR("David Paris <david.paris@st.com>");
311 MODULE_DESCRIPTION("ST LPC Watchdog Driver");
312 MODULE_LICENSE("GPL");
313