rtd119x_wdt.c (3724B)
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/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 27struct rtd119x_watchdog_device { 28 struct watchdog_device wdt_dev; 29 void __iomem *base; 30 struct clk *clk; 31}; 32 33static 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 46static 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 59static 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 68static 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 79static 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 87static const struct watchdog_info rtd119x_wdt_info = { 88 .identity = "rtd119x-wdt", 89 .options = 0, 90}; 91 92static const struct of_device_id rtd119x_wdt_dt_ids[] = { 93 { .compatible = "realtek,rtd1295-watchdog" }, 94 { } 95}; 96 97static void rtd119x_clk_disable_unprepare(void *data) 98{ 99 clk_disable_unprepare(data); 100} 101 102static int rtd119x_wdt_probe(struct platform_device *pdev) 103{ 104 struct device *dev = &pdev->dev; 105 struct rtd119x_watchdog_device *data; 106 int ret; 107 108 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); 109 if (!data) 110 return -ENOMEM; 111 112 data->base = devm_platform_ioremap_resource(pdev, 0); 113 if (IS_ERR(data->base)) 114 return PTR_ERR(data->base); 115 116 data->clk = devm_clk_get(dev, NULL); 117 if (IS_ERR(data->clk)) 118 return PTR_ERR(data->clk); 119 120 ret = clk_prepare_enable(data->clk); 121 if (ret) 122 return ret; 123 ret = devm_add_action_or_reset(dev, rtd119x_clk_disable_unprepare, 124 data->clk); 125 if (ret) 126 return ret; 127 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; 134 135 watchdog_stop_on_reboot(&data->wdt_dev); 136 watchdog_set_drvdata(&data->wdt_dev, data); 137 platform_set_drvdata(pdev, data); 138 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); 142 143 return devm_watchdog_register_device(dev, &data->wdt_dev); 144} 145 146static struct platform_driver rtd119x_wdt_driver = { 147 .probe = rtd119x_wdt_probe, 148 .driver = { 149 .name = "rtd1295-watchdog", 150 .of_match_table = rtd119x_wdt_dt_ids, 151 }, 152}; 153builtin_platform_driver(rtd119x_wdt_driver);