2 * Realtek RTD129x watchdog
4 * Copyright (c) 2017 Andreas Färber
6 * SPDX-License-Identifier: GPL-2.0+
9 #include <linux/bitops.h>
10 #include <linux/clk.h>
12 #include <linux/module.h>
14 #include <linux/of_address.h>
15 #include <linux/platform_device.h>
16 #include <linux/watchdog.h>
18 #define RTD119X_TCWCR 0x0
19 #define RTD119X_TCWTR 0x4
20 #define RTD119X_TCWOV 0xc
22 #define RTD119X_TCWCR_WDEN_DISABLED 0xa5
23 #define RTD119X_TCWCR_WDEN_ENABLED 0xff
24 #define RTD119X_TCWCR_WDEN_MASK 0xff
26 #define RTD119X_TCWTR_WDCLR BIT(0)
28 struct rtd119x_watchdog_device
{
29 struct watchdog_device wdt_dev
;
34 static int rtd119x_wdt_start(struct watchdog_device
*wdev
)
36 struct rtd119x_watchdog_device
*data
= watchdog_get_drvdata(wdev
);
39 val
= readl_relaxed(data
->base
+ RTD119X_TCWCR
);
40 val
&= ~RTD119X_TCWCR_WDEN_MASK
;
41 val
|= RTD119X_TCWCR_WDEN_ENABLED
;
42 writel(val
, data
->base
+ RTD119X_TCWCR
);
47 static int rtd119x_wdt_stop(struct watchdog_device
*wdev
)
49 struct rtd119x_watchdog_device
*data
= watchdog_get_drvdata(wdev
);
52 val
= readl_relaxed(data
->base
+ RTD119X_TCWCR
);
53 val
&= ~RTD119X_TCWCR_WDEN_MASK
;
54 val
|= RTD119X_TCWCR_WDEN_DISABLED
;
55 writel(val
, data
->base
+ RTD119X_TCWCR
);
60 static int rtd119x_wdt_ping(struct watchdog_device
*wdev
)
62 struct rtd119x_watchdog_device
*data
= watchdog_get_drvdata(wdev
);
64 writel_relaxed(RTD119X_TCWTR_WDCLR
, data
->base
+ RTD119X_TCWTR
);
66 return rtd119x_wdt_start(wdev
);
69 static int rtd119x_wdt_set_timeout(struct watchdog_device
*wdev
, unsigned int val
)
71 struct rtd119x_watchdog_device
*data
= watchdog_get_drvdata(wdev
);
73 writel(val
* clk_get_rate(data
->clk
), data
->base
+ RTD119X_TCWOV
);
75 data
->wdt_dev
.timeout
= val
;
80 static const struct watchdog_ops rtd119x_wdt_ops
= {
82 .start
= rtd119x_wdt_start
,
83 .stop
= rtd119x_wdt_stop
,
84 .ping
= rtd119x_wdt_ping
,
85 .set_timeout
= rtd119x_wdt_set_timeout
,
88 static const struct watchdog_info rtd119x_wdt_info
= {
89 .identity
= "rtd119x-wdt",
93 static const struct of_device_id rtd119x_wdt_dt_ids
[] = {
94 { .compatible
= "realtek,rtd1295-watchdog" },
98 static int rtd119x_wdt_probe(struct platform_device
*pdev
)
100 struct rtd119x_watchdog_device
*data
;
101 struct resource
*res
;
104 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
108 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
109 data
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
110 if (IS_ERR(data
->base
))
111 return PTR_ERR(data
->base
);
113 data
->clk
= of_clk_get(pdev
->dev
.of_node
, 0);
114 if (IS_ERR(data
->clk
))
115 return PTR_ERR(data
->clk
);
117 ret
= clk_prepare_enable(data
->clk
);
123 data
->wdt_dev
.info
= &rtd119x_wdt_info
;
124 data
->wdt_dev
.ops
= &rtd119x_wdt_ops
;
125 data
->wdt_dev
.timeout
= 120;
126 data
->wdt_dev
.max_timeout
= 0xffffffff / clk_get_rate(data
->clk
);
127 data
->wdt_dev
.min_timeout
= 1;
128 data
->wdt_dev
.parent
= &pdev
->dev
;
130 watchdog_stop_on_reboot(&data
->wdt_dev
);
131 watchdog_set_drvdata(&data
->wdt_dev
, data
);
132 platform_set_drvdata(pdev
, data
);
134 writel_relaxed(RTD119X_TCWTR_WDCLR
, data
->base
+ RTD119X_TCWTR
);
135 rtd119x_wdt_set_timeout(&data
->wdt_dev
, data
->wdt_dev
.timeout
);
136 rtd119x_wdt_stop(&data
->wdt_dev
);
138 ret
= devm_watchdog_register_device(&pdev
->dev
, &data
->wdt_dev
);
140 clk_disable_unprepare(data
->clk
);
148 static int rtd119x_wdt_remove(struct platform_device
*pdev
)
150 struct rtd119x_watchdog_device
*data
= platform_get_drvdata(pdev
);
152 watchdog_unregister_device(&data
->wdt_dev
);
154 clk_disable_unprepare(data
->clk
);
160 static struct platform_driver rtd119x_wdt_driver
= {
161 .probe
= rtd119x_wdt_probe
,
162 .remove
= rtd119x_wdt_remove
,
164 .name
= "rtd1295-watchdog",
165 .of_match_table
= rtd119x_wdt_dt_ids
,
168 builtin_platform_driver(rtd119x_wdt_driver
);