1 /* 2 * Realtek RTD129x watchdog 3 * 4 * Copyright (c) 2017 Andreas Färber 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 */ 8 9 #include <linux/bitops.h> 10 #include <linux/clk.h> 11 #include <linux/io.h> 12 #include <linux/module.h> 13 #include <linux/of.h> 14 #include <linux/of_address.h> 15 #include <linux/platform_device.h> 16 #include <linux/watchdog.h> 17 18 #define RTD119X_TCWCR 0x0 19 #define RTD119X_TCWTR 0x4 20 #define RTD119X_TCWOV 0xc 21 22 #define RTD119X_TCWCR_WDEN_DISABLED 0xa5 23 #define RTD119X_TCWCR_WDEN_ENABLED 0xff 24 #define RTD119X_TCWCR_WDEN_MASK 0xff 25 26 #define RTD119X_TCWTR_WDCLR BIT(0) 27 28 struct rtd119x_watchdog_device { 29 struct watchdog_device wdt_dev; 30 void __iomem *base; 31 struct clk *clk; 32 }; 33 34 static int rtd119x_wdt_start(struct watchdog_device *wdev) 35 { 36 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 37 u32 val; 38 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); 43 44 return 0; 45 } 46 47 static int rtd119x_wdt_stop(struct watchdog_device *wdev) 48 { 49 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 50 u32 val; 51 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); 56 57 return 0; 58 } 59 60 static int rtd119x_wdt_ping(struct watchdog_device *wdev) 61 { 62 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 63 64 writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR); 65 66 return rtd119x_wdt_start(wdev); 67 } 68 69 static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val) 70 { 71 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev); 72 73 writel(val * clk_get_rate(data->clk), data->base + RTD119X_TCWOV); 74 75 data->wdt_dev.timeout = val; 76 77 return 0; 78 } 79 80 static const struct watchdog_ops rtd119x_wdt_ops = { 81 .owner = THIS_MODULE, 82 .start = rtd119x_wdt_start, 83 .stop = rtd119x_wdt_stop, 84 .ping = rtd119x_wdt_ping, 85 .set_timeout = rtd119x_wdt_set_timeout, 86 }; 87 88 static const struct watchdog_info rtd119x_wdt_info = { 89 .identity = "rtd119x-wdt", 90 .options = 0, 91 }; 92 93 static const struct of_device_id rtd119x_wdt_dt_ids[] = { 94 { .compatible = "realtek,rtd1295-watchdog" }, 95 { } 96 }; 97 98 static int rtd119x_wdt_probe(struct platform_device *pdev) 99 { 100 struct rtd119x_watchdog_device *data; 101 struct resource *res; 102 int ret; 103 104 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL); 105 if (!data) 106 return -ENOMEM; 107 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); 112 113 data->clk = of_clk_get(pdev->dev.of_node, 0); 114 if (IS_ERR(data->clk)) 115 return PTR_ERR(data->clk); 116 117 ret = clk_prepare_enable(data->clk); 118 if (ret) { 119 clk_put(data->clk); 120 return ret; 121 } 122 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; 129 130 watchdog_stop_on_reboot(&data->wdt_dev); 131 watchdog_set_drvdata(&data->wdt_dev, data); 132 platform_set_drvdata(pdev, data); 133 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); 137 138 ret = devm_watchdog_register_device(&pdev->dev, &data->wdt_dev); 139 if (ret) { 140 clk_disable_unprepare(data->clk); 141 clk_put(data->clk); 142 return ret; 143 } 144 145 return 0; 146 } 147 148 static int rtd119x_wdt_remove(struct platform_device *pdev) 149 { 150 struct rtd119x_watchdog_device *data = platform_get_drvdata(pdev); 151 152 watchdog_unregister_device(&data->wdt_dev); 153 154 clk_disable_unprepare(data->clk); 155 clk_put(data->clk); 156 157 return 0; 158 } 159 160 static struct platform_driver rtd119x_wdt_driver = { 161 .probe = rtd119x_wdt_probe, 162 .remove = rtd119x_wdt_remove, 163 .driver = { 164 .name = "rtd1295-watchdog", 165 .of_match_table = rtd119x_wdt_dt_ids, 166 }, 167 }; 168 builtin_platform_driver(rtd119x_wdt_driver); 169