12bdf6acbSAndreas Färber /* 22bdf6acbSAndreas Färber * Realtek RTD129x watchdog 32bdf6acbSAndreas Färber * 42bdf6acbSAndreas Färber * Copyright (c) 2017 Andreas Färber 52bdf6acbSAndreas Färber * 62bdf6acbSAndreas Färber * SPDX-License-Identifier: GPL-2.0+ 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 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 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 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 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 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 110*6cc0768bSChristophe 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