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>
13 #include <linux/of_address.h>
14 #include <linux/platform_device.h>
15 #include <linux/watchdog.h>
17 #define RTD119X_TCWCR 0x0
18 #define RTD119X_TCWTR 0x4
19 #define RTD119X_TCWOV 0xc
21 #define RTD119X_TCWCR_WDEN_DISABLED 0xa5
22 #define RTD119X_TCWCR_WDEN_ENABLED 0xff
23 #define RTD119X_TCWCR_WDEN_MASK 0xff
25 #define RTD119X_TCWTR_WDCLR BIT(0)
27 struct rtd119x_watchdog_device
{
28 struct watchdog_device wdt_dev
;
33 static int rtd119x_wdt_start(struct watchdog_device
*wdev
)
35 struct rtd119x_watchdog_device
*data
= watchdog_get_drvdata(wdev
);
38 val
= readl_relaxed(data
->base
+ RTD119X_TCWCR
);
39 val
&= ~RTD119X_TCWCR_WDEN_MASK
;
40 val
|= RTD119X_TCWCR_WDEN_ENABLED
;
41 writel(val
, data
->base
+ RTD119X_TCWCR
);
46 static int rtd119x_wdt_stop(struct watchdog_device
*wdev
)
48 struct rtd119x_watchdog_device
*data
= watchdog_get_drvdata(wdev
);
51 val
= readl_relaxed(data
->base
+ RTD119X_TCWCR
);
52 val
&= ~RTD119X_TCWCR_WDEN_MASK
;
53 val
|= RTD119X_TCWCR_WDEN_DISABLED
;
54 writel(val
, data
->base
+ RTD119X_TCWCR
);
59 static int rtd119x_wdt_ping(struct watchdog_device
*wdev
)
61 struct rtd119x_watchdog_device
*data
= watchdog_get_drvdata(wdev
);
63 writel_relaxed(RTD119X_TCWTR_WDCLR
, data
->base
+ RTD119X_TCWTR
);
65 return rtd119x_wdt_start(wdev
);
68 static int rtd119x_wdt_set_timeout(struct watchdog_device
*wdev
, unsigned int val
)
70 struct rtd119x_watchdog_device
*data
= watchdog_get_drvdata(wdev
);
72 writel(val
* clk_get_rate(data
->clk
), data
->base
+ RTD119X_TCWOV
);
74 data
->wdt_dev
.timeout
= val
;
79 static const struct watchdog_ops rtd119x_wdt_ops
= {
81 .start
= rtd119x_wdt_start
,
82 .stop
= rtd119x_wdt_stop
,
83 .ping
= rtd119x_wdt_ping
,
84 .set_timeout
= rtd119x_wdt_set_timeout
,
87 static const struct watchdog_info rtd119x_wdt_info
= {
88 .identity
= "rtd119x-wdt",
92 static const struct of_device_id rtd119x_wdt_dt_ids
[] = {
93 { .compatible
= "realtek,rtd1295-watchdog" },
97 static void rtd119x_clk_disable_unprepare(void *data
)
99 clk_disable_unprepare(data
);
102 static int rtd119x_wdt_probe(struct platform_device
*pdev
)
104 struct device
*dev
= &pdev
->dev
;
105 struct rtd119x_watchdog_device
*data
;
108 data
= devm_kzalloc(dev
, sizeof(*data
), GFP_KERNEL
);
112 data
->base
= devm_platform_ioremap_resource(pdev
, 0);
113 if (IS_ERR(data
->base
))
114 return PTR_ERR(data
->base
);
116 data
->clk
= devm_clk_get(dev
, NULL
);
117 if (IS_ERR(data
->clk
))
118 return PTR_ERR(data
->clk
);
120 ret
= clk_prepare_enable(data
->clk
);
123 ret
= devm_add_action_or_reset(dev
, rtd119x_clk_disable_unprepare
,
128 data
->wdt_dev
.info
= &rtd119x_wdt_info
;
129 data
->wdt_dev
.ops
= &rtd119x_wdt_ops
;
130 data
->wdt_dev
.timeout
= 120;
131 data
->wdt_dev
.max_timeout
= 0xffffffff / clk_get_rate(data
->clk
);
132 data
->wdt_dev
.min_timeout
= 1;
133 data
->wdt_dev
.parent
= dev
;
135 watchdog_stop_on_reboot(&data
->wdt_dev
);
136 watchdog_set_drvdata(&data
->wdt_dev
, data
);
137 platform_set_drvdata(pdev
, data
);
139 writel_relaxed(RTD119X_TCWTR_WDCLR
, data
->base
+ RTD119X_TCWTR
);
140 rtd119x_wdt_set_timeout(&data
->wdt_dev
, data
->wdt_dev
.timeout
);
141 rtd119x_wdt_stop(&data
->wdt_dev
);
143 return devm_watchdog_register_device(dev
, &data
->wdt_dev
);
146 static struct platform_driver rtd119x_wdt_driver
= {
147 .probe
= rtd119x_wdt_probe
,
149 .name
= "rtd1295-watchdog",
150 .of_match_table
= rtd119x_wdt_dt_ids
,
153 builtin_platform_driver(rtd119x_wdt_driver
);