xref: /linux/drivers/watchdog/rtd119x_wdt.c (revision cdd5b5a9761fd66d17586e4f4ba6588c70e640ea)
1*a0d261ccSBagas Sanjaya // SPDX-License-Identifier: GPL-2.0+
22bdf6acbSAndreas Färber /*
32bdf6acbSAndreas Färber  * Realtek RTD129x watchdog
42bdf6acbSAndreas Färber  *
52bdf6acbSAndreas Färber  * Copyright (c) 2017 Andreas Färber
62bdf6acbSAndreas Färber  *
72bdf6acbSAndreas Färber  */
82bdf6acbSAndreas Färber 
92bdf6acbSAndreas Färber #include <linux/bitops.h>
102bdf6acbSAndreas Färber #include <linux/clk.h>
112bdf6acbSAndreas Färber #include <linux/io.h>
122bdf6acbSAndreas Färber #include <linux/of.h>
132bdf6acbSAndreas Färber #include <linux/of_address.h>
142bdf6acbSAndreas Färber #include <linux/platform_device.h>
152bdf6acbSAndreas Färber #include <linux/watchdog.h>
162bdf6acbSAndreas Färber 
172bdf6acbSAndreas Färber #define RTD119X_TCWCR		0x0
182bdf6acbSAndreas Färber #define RTD119X_TCWTR		0x4
192bdf6acbSAndreas Färber #define RTD119X_TCWOV		0xc
202bdf6acbSAndreas Färber 
212bdf6acbSAndreas Färber #define RTD119X_TCWCR_WDEN_DISABLED		0xa5
222bdf6acbSAndreas Färber #define RTD119X_TCWCR_WDEN_ENABLED		0xff
232bdf6acbSAndreas Färber #define RTD119X_TCWCR_WDEN_MASK			0xff
242bdf6acbSAndreas Färber 
252bdf6acbSAndreas Färber #define RTD119X_TCWTR_WDCLR			BIT(0)
262bdf6acbSAndreas Färber 
272bdf6acbSAndreas Färber struct rtd119x_watchdog_device {
282bdf6acbSAndreas Färber 	struct watchdog_device wdt_dev;
292bdf6acbSAndreas Färber 	void __iomem *base;
302bdf6acbSAndreas Färber 	struct clk *clk;
312bdf6acbSAndreas Färber };
322bdf6acbSAndreas Färber 
rtd119x_wdt_start(struct watchdog_device * wdev)332bdf6acbSAndreas Färber static int rtd119x_wdt_start(struct watchdog_device *wdev)
342bdf6acbSAndreas Färber {
352bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
362bdf6acbSAndreas Färber 	u32 val;
372bdf6acbSAndreas Färber 
382bdf6acbSAndreas Färber 	val = readl_relaxed(data->base + RTD119X_TCWCR);
392bdf6acbSAndreas Färber 	val &= ~RTD119X_TCWCR_WDEN_MASK;
402bdf6acbSAndreas Färber 	val |= RTD119X_TCWCR_WDEN_ENABLED;
412bdf6acbSAndreas Färber 	writel(val, data->base + RTD119X_TCWCR);
422bdf6acbSAndreas Färber 
432bdf6acbSAndreas Färber 	return 0;
442bdf6acbSAndreas Färber }
452bdf6acbSAndreas Färber 
rtd119x_wdt_stop(struct watchdog_device * wdev)462bdf6acbSAndreas Färber static int rtd119x_wdt_stop(struct watchdog_device *wdev)
472bdf6acbSAndreas Färber {
482bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
492bdf6acbSAndreas Färber 	u32 val;
502bdf6acbSAndreas Färber 
512bdf6acbSAndreas Färber 	val = readl_relaxed(data->base + RTD119X_TCWCR);
522bdf6acbSAndreas Färber 	val &= ~RTD119X_TCWCR_WDEN_MASK;
532bdf6acbSAndreas Färber 	val |= RTD119X_TCWCR_WDEN_DISABLED;
542bdf6acbSAndreas Färber 	writel(val, data->base + RTD119X_TCWCR);
552bdf6acbSAndreas Färber 
562bdf6acbSAndreas Färber 	return 0;
572bdf6acbSAndreas Färber }
582bdf6acbSAndreas Färber 
rtd119x_wdt_ping(struct watchdog_device * wdev)592bdf6acbSAndreas Färber static int rtd119x_wdt_ping(struct watchdog_device *wdev)
602bdf6acbSAndreas Färber {
612bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
622bdf6acbSAndreas Färber 
632bdf6acbSAndreas Färber 	writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
642bdf6acbSAndreas Färber 
652bdf6acbSAndreas Färber 	return rtd119x_wdt_start(wdev);
662bdf6acbSAndreas Färber }
672bdf6acbSAndreas Färber 
rtd119x_wdt_set_timeout(struct watchdog_device * wdev,unsigned int val)682bdf6acbSAndreas Färber static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
692bdf6acbSAndreas Färber {
702bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
712bdf6acbSAndreas Färber 
722bdf6acbSAndreas Färber 	writel(val * clk_get_rate(data->clk), data->base + RTD119X_TCWOV);
732bdf6acbSAndreas Färber 
742bdf6acbSAndreas Färber 	data->wdt_dev.timeout = val;
752bdf6acbSAndreas Färber 
762bdf6acbSAndreas Färber 	return 0;
772bdf6acbSAndreas Färber }
782bdf6acbSAndreas Färber 
792bdf6acbSAndreas Färber static const struct watchdog_ops rtd119x_wdt_ops = {
802bdf6acbSAndreas Färber 	.owner = THIS_MODULE,
812bdf6acbSAndreas Färber 	.start		= rtd119x_wdt_start,
822bdf6acbSAndreas Färber 	.stop		= rtd119x_wdt_stop,
832bdf6acbSAndreas Färber 	.ping		= rtd119x_wdt_ping,
842bdf6acbSAndreas Färber 	.set_timeout	= rtd119x_wdt_set_timeout,
852bdf6acbSAndreas Färber };
862bdf6acbSAndreas Färber 
872bdf6acbSAndreas Färber static const struct watchdog_info rtd119x_wdt_info = {
882bdf6acbSAndreas Färber 	.identity = "rtd119x-wdt",
892bdf6acbSAndreas Färber 	.options = 0,
902bdf6acbSAndreas Färber };
912bdf6acbSAndreas Färber 
922bdf6acbSAndreas Färber static const struct of_device_id rtd119x_wdt_dt_ids[] = {
932bdf6acbSAndreas Färber 	 { .compatible = "realtek,rtd1295-watchdog" },
942bdf6acbSAndreas Färber 	 { }
952bdf6acbSAndreas Färber };
962bdf6acbSAndreas Färber 
rtd119x_wdt_probe(struct platform_device * pdev)972bdf6acbSAndreas Färber static int rtd119x_wdt_probe(struct platform_device *pdev)
982bdf6acbSAndreas Färber {
99553140a0SGuenter Roeck 	struct device *dev = &pdev->dev;
1002bdf6acbSAndreas Färber 	struct rtd119x_watchdog_device *data;
1012bdf6acbSAndreas Färber 
102553140a0SGuenter Roeck 	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
1032bdf6acbSAndreas Färber 	if (!data)
1042bdf6acbSAndreas Färber 		return -ENOMEM;
1052bdf6acbSAndreas Färber 
1060f0a6a28SGuenter Roeck 	data->base = devm_platform_ioremap_resource(pdev, 0);
1072bdf6acbSAndreas Färber 	if (IS_ERR(data->base))
1082bdf6acbSAndreas Färber 		return PTR_ERR(data->base);
1092bdf6acbSAndreas Färber 
1106cc0768bSChristophe JAILLET 	data->clk = devm_clk_get_enabled(dev, NULL);
1112bdf6acbSAndreas Färber 	if (IS_ERR(data->clk))
1122bdf6acbSAndreas Färber 		return PTR_ERR(data->clk);
1132bdf6acbSAndreas Färber 
1142bdf6acbSAndreas Färber 	data->wdt_dev.info = &rtd119x_wdt_info;
1152bdf6acbSAndreas Färber 	data->wdt_dev.ops = &rtd119x_wdt_ops;
1162bdf6acbSAndreas Färber 	data->wdt_dev.timeout = 120;
1172bdf6acbSAndreas Färber 	data->wdt_dev.max_timeout = 0xffffffff / clk_get_rate(data->clk);
1182bdf6acbSAndreas Färber 	data->wdt_dev.min_timeout = 1;
119553140a0SGuenter Roeck 	data->wdt_dev.parent = dev;
1202bdf6acbSAndreas Färber 
1212bdf6acbSAndreas Färber 	watchdog_stop_on_reboot(&data->wdt_dev);
1222bdf6acbSAndreas Färber 	watchdog_set_drvdata(&data->wdt_dev, data);
1232bdf6acbSAndreas Färber 	platform_set_drvdata(pdev, data);
1242bdf6acbSAndreas Färber 
1252bdf6acbSAndreas Färber 	writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
1262bdf6acbSAndreas Färber 	rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
1272bdf6acbSAndreas Färber 	rtd119x_wdt_stop(&data->wdt_dev);
1282bdf6acbSAndreas Färber 
129553140a0SGuenter Roeck 	return devm_watchdog_register_device(dev, &data->wdt_dev);
1302bdf6acbSAndreas Färber }
1312bdf6acbSAndreas Färber 
1322bdf6acbSAndreas Färber static struct platform_driver rtd119x_wdt_driver = {
1332bdf6acbSAndreas Färber 	.probe = rtd119x_wdt_probe,
1342bdf6acbSAndreas Färber 	.driver = {
1352bdf6acbSAndreas Färber 		.name = "rtd1295-watchdog",
1362bdf6acbSAndreas Färber 		.of_match_table	= rtd119x_wdt_dt_ids,
1372bdf6acbSAndreas Färber 	},
1382bdf6acbSAndreas Färber };
1392bdf6acbSAndreas Färber builtin_platform_driver(rtd119x_wdt_driver);
140