xref: /linux/drivers/watchdog/rtd119x_wdt.c (revision 2bdf6acbfead7e9aa69f36ee5682d1e5c8f70367)
1*2bdf6acbSAndreas Färber /*
2*2bdf6acbSAndreas Färber  * Realtek RTD129x watchdog
3*2bdf6acbSAndreas Färber  *
4*2bdf6acbSAndreas Färber  * Copyright (c) 2017 Andreas Färber
5*2bdf6acbSAndreas Färber  *
6*2bdf6acbSAndreas Färber  * SPDX-License-Identifier: GPL-2.0+
7*2bdf6acbSAndreas Färber  */
8*2bdf6acbSAndreas Färber 
9*2bdf6acbSAndreas Färber #include <linux/bitops.h>
10*2bdf6acbSAndreas Färber #include <linux/clk.h>
11*2bdf6acbSAndreas Färber #include <linux/io.h>
12*2bdf6acbSAndreas Färber #include <linux/module.h>
13*2bdf6acbSAndreas Färber #include <linux/of.h>
14*2bdf6acbSAndreas Färber #include <linux/of_address.h>
15*2bdf6acbSAndreas Färber #include <linux/platform_device.h>
16*2bdf6acbSAndreas Färber #include <linux/watchdog.h>
17*2bdf6acbSAndreas Färber 
18*2bdf6acbSAndreas Färber #define RTD119X_TCWCR		0x0
19*2bdf6acbSAndreas Färber #define RTD119X_TCWTR		0x4
20*2bdf6acbSAndreas Färber #define RTD119X_TCWOV		0xc
21*2bdf6acbSAndreas Färber 
22*2bdf6acbSAndreas Färber #define RTD119X_TCWCR_WDEN_DISABLED		0xa5
23*2bdf6acbSAndreas Färber #define RTD119X_TCWCR_WDEN_ENABLED		0xff
24*2bdf6acbSAndreas Färber #define RTD119X_TCWCR_WDEN_MASK			0xff
25*2bdf6acbSAndreas Färber 
26*2bdf6acbSAndreas Färber #define RTD119X_TCWTR_WDCLR			BIT(0)
27*2bdf6acbSAndreas Färber 
28*2bdf6acbSAndreas Färber struct rtd119x_watchdog_device {
29*2bdf6acbSAndreas Färber 	struct watchdog_device wdt_dev;
30*2bdf6acbSAndreas Färber 	void __iomem *base;
31*2bdf6acbSAndreas Färber 	struct clk *clk;
32*2bdf6acbSAndreas Färber };
33*2bdf6acbSAndreas Färber 
34*2bdf6acbSAndreas Färber static int rtd119x_wdt_start(struct watchdog_device *wdev)
35*2bdf6acbSAndreas Färber {
36*2bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
37*2bdf6acbSAndreas Färber 	u32 val;
38*2bdf6acbSAndreas Färber 
39*2bdf6acbSAndreas Färber 	val = readl_relaxed(data->base + RTD119X_TCWCR);
40*2bdf6acbSAndreas Färber 	val &= ~RTD119X_TCWCR_WDEN_MASK;
41*2bdf6acbSAndreas Färber 	val |= RTD119X_TCWCR_WDEN_ENABLED;
42*2bdf6acbSAndreas Färber 	writel(val, data->base + RTD119X_TCWCR);
43*2bdf6acbSAndreas Färber 
44*2bdf6acbSAndreas Färber 	return 0;
45*2bdf6acbSAndreas Färber }
46*2bdf6acbSAndreas Färber 
47*2bdf6acbSAndreas Färber static int rtd119x_wdt_stop(struct watchdog_device *wdev)
48*2bdf6acbSAndreas Färber {
49*2bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
50*2bdf6acbSAndreas Färber 	u32 val;
51*2bdf6acbSAndreas Färber 
52*2bdf6acbSAndreas Färber 	val = readl_relaxed(data->base + RTD119X_TCWCR);
53*2bdf6acbSAndreas Färber 	val &= ~RTD119X_TCWCR_WDEN_MASK;
54*2bdf6acbSAndreas Färber 	val |= RTD119X_TCWCR_WDEN_DISABLED;
55*2bdf6acbSAndreas Färber 	writel(val, data->base + RTD119X_TCWCR);
56*2bdf6acbSAndreas Färber 
57*2bdf6acbSAndreas Färber 	return 0;
58*2bdf6acbSAndreas Färber }
59*2bdf6acbSAndreas Färber 
60*2bdf6acbSAndreas Färber static int rtd119x_wdt_ping(struct watchdog_device *wdev)
61*2bdf6acbSAndreas Färber {
62*2bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
63*2bdf6acbSAndreas Färber 
64*2bdf6acbSAndreas Färber 	writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
65*2bdf6acbSAndreas Färber 
66*2bdf6acbSAndreas Färber 	return rtd119x_wdt_start(wdev);
67*2bdf6acbSAndreas Färber }
68*2bdf6acbSAndreas Färber 
69*2bdf6acbSAndreas Färber static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
70*2bdf6acbSAndreas Färber {
71*2bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
72*2bdf6acbSAndreas Färber 
73*2bdf6acbSAndreas Färber 	writel(val * clk_get_rate(data->clk), data->base + RTD119X_TCWOV);
74*2bdf6acbSAndreas Färber 
75*2bdf6acbSAndreas Färber 	data->wdt_dev.timeout = val;
76*2bdf6acbSAndreas Färber 
77*2bdf6acbSAndreas Färber 	return 0;
78*2bdf6acbSAndreas Färber }
79*2bdf6acbSAndreas Färber 
80*2bdf6acbSAndreas Färber static const struct watchdog_ops rtd119x_wdt_ops = {
81*2bdf6acbSAndreas Färber 	.owner = THIS_MODULE,
82*2bdf6acbSAndreas Färber 	.start		= rtd119x_wdt_start,
83*2bdf6acbSAndreas Färber 	.stop		= rtd119x_wdt_stop,
84*2bdf6acbSAndreas Färber 	.ping		= rtd119x_wdt_ping,
85*2bdf6acbSAndreas Färber 	.set_timeout	= rtd119x_wdt_set_timeout,
86*2bdf6acbSAndreas Färber };
87*2bdf6acbSAndreas Färber 
88*2bdf6acbSAndreas Färber static const struct watchdog_info rtd119x_wdt_info = {
89*2bdf6acbSAndreas Färber 	.identity = "rtd119x-wdt",
90*2bdf6acbSAndreas Färber 	.options = 0,
91*2bdf6acbSAndreas Färber };
92*2bdf6acbSAndreas Färber 
93*2bdf6acbSAndreas Färber static const struct of_device_id rtd119x_wdt_dt_ids[] = {
94*2bdf6acbSAndreas Färber 	 { .compatible = "realtek,rtd1295-watchdog" },
95*2bdf6acbSAndreas Färber 	 { }
96*2bdf6acbSAndreas Färber };
97*2bdf6acbSAndreas Färber 
98*2bdf6acbSAndreas Färber static int rtd119x_wdt_probe(struct platform_device *pdev)
99*2bdf6acbSAndreas Färber {
100*2bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data;
101*2bdf6acbSAndreas Färber 	struct resource *res;
102*2bdf6acbSAndreas Färber 	int ret;
103*2bdf6acbSAndreas Färber 
104*2bdf6acbSAndreas Färber 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
105*2bdf6acbSAndreas Färber 	if (!data)
106*2bdf6acbSAndreas Färber 		return -ENOMEM;
107*2bdf6acbSAndreas Färber 
108*2bdf6acbSAndreas Färber 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
109*2bdf6acbSAndreas Färber 	data->base = devm_ioremap_resource(&pdev->dev, res);
110*2bdf6acbSAndreas Färber 	if (IS_ERR(data->base))
111*2bdf6acbSAndreas Färber 		return PTR_ERR(data->base);
112*2bdf6acbSAndreas Färber 
113*2bdf6acbSAndreas Färber 	data->clk = of_clk_get(pdev->dev.of_node, 0);
114*2bdf6acbSAndreas Färber 	if (IS_ERR(data->clk))
115*2bdf6acbSAndreas Färber 		return PTR_ERR(data->clk);
116*2bdf6acbSAndreas Färber 
117*2bdf6acbSAndreas Färber 	ret = clk_prepare_enable(data->clk);
118*2bdf6acbSAndreas Färber 	if (ret) {
119*2bdf6acbSAndreas Färber 		clk_put(data->clk);
120*2bdf6acbSAndreas Färber 		return ret;
121*2bdf6acbSAndreas Färber 	}
122*2bdf6acbSAndreas Färber 
123*2bdf6acbSAndreas Färber 	data->wdt_dev.info = &rtd119x_wdt_info;
124*2bdf6acbSAndreas Färber 	data->wdt_dev.ops = &rtd119x_wdt_ops;
125*2bdf6acbSAndreas Färber 	data->wdt_dev.timeout = 120;
126*2bdf6acbSAndreas Färber 	data->wdt_dev.max_timeout = 0xffffffff / clk_get_rate(data->clk);
127*2bdf6acbSAndreas Färber 	data->wdt_dev.min_timeout = 1;
128*2bdf6acbSAndreas Färber 	data->wdt_dev.parent = &pdev->dev;
129*2bdf6acbSAndreas Färber 
130*2bdf6acbSAndreas Färber 	watchdog_stop_on_reboot(&data->wdt_dev);
131*2bdf6acbSAndreas Färber 	watchdog_set_drvdata(&data->wdt_dev, data);
132*2bdf6acbSAndreas Färber 	platform_set_drvdata(pdev, data);
133*2bdf6acbSAndreas Färber 
134*2bdf6acbSAndreas Färber 	writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
135*2bdf6acbSAndreas Färber 	rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
136*2bdf6acbSAndreas Färber 	rtd119x_wdt_stop(&data->wdt_dev);
137*2bdf6acbSAndreas Färber 
138*2bdf6acbSAndreas Färber 	ret = devm_watchdog_register_device(&pdev->dev, &data->wdt_dev);
139*2bdf6acbSAndreas Färber 	if (ret) {
140*2bdf6acbSAndreas Färber 		clk_disable_unprepare(data->clk);
141*2bdf6acbSAndreas Färber 		clk_put(data->clk);
142*2bdf6acbSAndreas Färber 		return ret;
143*2bdf6acbSAndreas Färber 	}
144*2bdf6acbSAndreas Färber 
145*2bdf6acbSAndreas Färber 	return 0;
146*2bdf6acbSAndreas Färber }
147*2bdf6acbSAndreas Färber 
148*2bdf6acbSAndreas Färber static int rtd119x_wdt_remove(struct platform_device *pdev)
149*2bdf6acbSAndreas Färber {
150*2bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data = platform_get_drvdata(pdev);
151*2bdf6acbSAndreas Färber 
152*2bdf6acbSAndreas Färber 	watchdog_unregister_device(&data->wdt_dev);
153*2bdf6acbSAndreas Färber 
154*2bdf6acbSAndreas Färber 	clk_disable_unprepare(data->clk);
155*2bdf6acbSAndreas Färber 	clk_put(data->clk);
156*2bdf6acbSAndreas Färber 
157*2bdf6acbSAndreas Färber 	return 0;
158*2bdf6acbSAndreas Färber }
159*2bdf6acbSAndreas Färber 
160*2bdf6acbSAndreas Färber static struct platform_driver rtd119x_wdt_driver = {
161*2bdf6acbSAndreas Färber 	.probe = rtd119x_wdt_probe,
162*2bdf6acbSAndreas Färber 	.remove = rtd119x_wdt_remove,
163*2bdf6acbSAndreas Färber 	.driver = {
164*2bdf6acbSAndreas Färber 		.name = "rtd1295-watchdog",
165*2bdf6acbSAndreas Färber 		.of_match_table	= rtd119x_wdt_dt_ids,
166*2bdf6acbSAndreas Färber 	},
167*2bdf6acbSAndreas Färber };
168*2bdf6acbSAndreas Färber builtin_platform_driver(rtd119x_wdt_driver);
169