1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Realtek RTD129x watchdog 4 * 5 * Copyright (c) 2017 Andreas Färber 6 * 7 */ 8 9 #include <linux/bitops.h> 10 #include <linux/clk.h> 11 #include <linux/io.h> 12 #include <linux/of.h> 13 #include <linux/of_address.h> 14 #include <linux/platform_device.h> 15 #include <linux/watchdog.h> 16 17 #define RTD119X_TCWCR 0x0 18 #define RTD119X_TCWTR 0x4 19 #define RTD119X_TCWOV 0xc 20 21 #define RTD119X_TCWCR_WDEN_DISABLED 0xa5 22 #define RTD119X_TCWCR_WDEN_ENABLED 0xff 23 #define RTD119X_TCWCR_WDEN_MASK 0xff 24 25 #define RTD119X_TCWTR_WDCLR BIT(0) 26 27 struct rtd119x_watchdog_device { 28 struct watchdog_device wdt_dev; 29 void __iomem *base; 30 struct clk *clk; 31 }; 32 33 static int rtd119x_wdt_start(struct watchdog_device *wdev) 34 { 35 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 36 u32 val; 37 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); 42 43 return 0; 44 } 45 46 static int rtd119x_wdt_stop(struct watchdog_device *wdev) 47 { 48 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 49 u32 val; 50 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); 55 56 return 0; 57 } 58 59 static int rtd119x_wdt_ping(struct watchdog_device *wdev) 60 { 61 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 62 63 writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR); 64 65 return rtd119x_wdt_start(wdev); 66 } 67 68 static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val) 69 { 70 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 71 72 writel(val * clk_get_rate(data->clk), data->base + RTD119X_TCWOV); 73 74 data->wdt_dev.timeout = val; 75 76 return 0; 77 } 78 79 static const struct watchdog_ops rtd119x_wdt_ops = { 80 .owner = THIS_MODULE, 81 .start = rtd119x_wdt_start, 82 .stop = rtd119x_wdt_stop, 83 .ping = rtd119x_wdt_ping, 84 .set_timeout = rtd119x_wdt_set_timeout, 85 }; 86 87 static const struct watchdog_info rtd119x_wdt_info = { 88 .identity = "rtd119x-wdt", 89 .options = 0, 90 }; 91 92 static const struct of_device_id rtd119x_wdt_dt_ids[] = { 93 { .compatible = "realtek,rtd1295-watchdog" }, 94 { } 95 }; 96 97 static int rtd119x_wdt_probe(struct platform_device *pdev) 98 { 99 struct device *dev = &pdev->dev; 100 struct rtd119x_watchdog_device *data; 101 102 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 103 if (!data) 104 return -ENOMEM; 105 106 data->base = devm_platform_ioremap_resource(pdev, 0); 107 if (IS_ERR(data->base)) 108 return PTR_ERR(data->base); 109 110 data->clk = devm_clk_get_enabled(dev, NULL); 111 if (IS_ERR(data->clk)) 112 return PTR_ERR(data->clk); 113 114 data->wdt_dev.info = &rtd119x_wdt_info; 115 data->wdt_dev.ops = &rtd119x_wdt_ops; 116 data->wdt_dev.timeout = 120; 117 data->wdt_dev.max_timeout = 0xffffffff / clk_get_rate(data->clk); 118 data->wdt_dev.min_timeout = 1; 119 data->wdt_dev.parent = dev; 120 121 watchdog_stop_on_reboot(&data->wdt_dev); 122 watchdog_set_drvdata(&data->wdt_dev, data); 123 platform_set_drvdata(pdev, data); 124 125 writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR); 126 rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout); 127 rtd119x_wdt_stop(&data->wdt_dev); 128 129 return devm_watchdog_register_device(dev, &data->wdt_dev); 130 } 131 132 static struct platform_driver rtd119x_wdt_driver = { 133 .probe = rtd119x_wdt_probe, 134 .driver = { 135 .name = "rtd1295-watchdog", 136 .of_match_table = rtd119x_wdt_dt_ids, 137 }, 138 }; 139 builtin_platform_driver(rtd119x_wdt_driver); 140