0001
0002
0003
0004
0005
0006
0007
0008
0009 #include <linux/bitops.h>
0010 #include <linux/clk.h>
0011 #include <linux/io.h>
0012 #include <linux/of.h>
0013 #include <linux/of_address.h>
0014 #include <linux/platform_device.h>
0015 #include <linux/watchdog.h>
0016
0017 #define RTD119X_TCWCR 0x0
0018 #define RTD119X_TCWTR 0x4
0019 #define RTD119X_TCWOV 0xc
0020
0021 #define RTD119X_TCWCR_WDEN_DISABLED 0xa5
0022 #define RTD119X_TCWCR_WDEN_ENABLED 0xff
0023 #define RTD119X_TCWCR_WDEN_MASK 0xff
0024
0025 #define RTD119X_TCWTR_WDCLR BIT(0)
0026
0027 struct rtd119x_watchdog_device {
0028 struct watchdog_device wdt_dev;
0029 void __iomem *base;
0030 struct clk *clk;
0031 };
0032
0033 static int rtd119x_wdt_start(struct watchdog_device *wdev)
0034 {
0035 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
0036 u32 val;
0037
0038 val = readl_relaxed(data->base + RTD119X_TCWCR);
0039 val &= ~RTD119X_TCWCR_WDEN_MASK;
0040 val |= RTD119X_TCWCR_WDEN_ENABLED;
0041 writel(val, data->base + RTD119X_TCWCR);
0042
0043 return 0;
0044 }
0045
0046 static int rtd119x_wdt_stop(struct watchdog_device *wdev)
0047 {
0048 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
0049 u32 val;
0050
0051 val = readl_relaxed(data->base + RTD119X_TCWCR);
0052 val &= ~RTD119X_TCWCR_WDEN_MASK;
0053 val |= RTD119X_TCWCR_WDEN_DISABLED;
0054 writel(val, data->base + RTD119X_TCWCR);
0055
0056 return 0;
0057 }
0058
0059 static int rtd119x_wdt_ping(struct watchdog_device *wdev)
0060 {
0061 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
0062
0063 writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
0064
0065 return rtd119x_wdt_start(wdev);
0066 }
0067
0068 static int rtd119x_wdt_set_timeout(struct watchdog_device *wdev, unsigned int val)
0069 {
0070 struct rtd119x_watchdog_device *data = watchdog_get_drvdata(wdev);
0071
0072 writel(val * clk_get_rate(data->clk), data->base + RTD119X_TCWOV);
0073
0074 data->wdt_dev.timeout = val;
0075
0076 return 0;
0077 }
0078
0079 static const struct watchdog_ops rtd119x_wdt_ops = {
0080 .owner = THIS_MODULE,
0081 .start = rtd119x_wdt_start,
0082 .stop = rtd119x_wdt_stop,
0083 .ping = rtd119x_wdt_ping,
0084 .set_timeout = rtd119x_wdt_set_timeout,
0085 };
0086
0087 static const struct watchdog_info rtd119x_wdt_info = {
0088 .identity = "rtd119x-wdt",
0089 .options = 0,
0090 };
0091
0092 static const struct of_device_id rtd119x_wdt_dt_ids[] = {
0093 { .compatible = "realtek,rtd1295-watchdog" },
0094 { }
0095 };
0096
0097 static void rtd119x_clk_disable_unprepare(void *data)
0098 {
0099 clk_disable_unprepare(data);
0100 }
0101
0102 static int rtd119x_wdt_probe(struct platform_device *pdev)
0103 {
0104 struct device *dev = &pdev->dev;
0105 struct rtd119x_watchdog_device *data;
0106 int ret;
0107
0108 data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
0109 if (!data)
0110 return -ENOMEM;
0111
0112 data->base = devm_platform_ioremap_resource(pdev, 0);
0113 if (IS_ERR(data->base))
0114 return PTR_ERR(data->base);
0115
0116 data->clk = devm_clk_get(dev, NULL);
0117 if (IS_ERR(data->clk))
0118 return PTR_ERR(data->clk);
0119
0120 ret = clk_prepare_enable(data->clk);
0121 if (ret)
0122 return ret;
0123 ret = devm_add_action_or_reset(dev, rtd119x_clk_disable_unprepare,
0124 data->clk);
0125 if (ret)
0126 return ret;
0127
0128 data->wdt_dev.info = &rtd119x_wdt_info;
0129 data->wdt_dev.ops = &rtd119x_wdt_ops;
0130 data->wdt_dev.timeout = 120;
0131 data->wdt_dev.max_timeout = 0xffffffff / clk_get_rate(data->clk);
0132 data->wdt_dev.min_timeout = 1;
0133 data->wdt_dev.parent = dev;
0134
0135 watchdog_stop_on_reboot(&data->wdt_dev);
0136 watchdog_set_drvdata(&data->wdt_dev, data);
0137 platform_set_drvdata(pdev, data);
0138
0139 writel_relaxed(RTD119X_TCWTR_WDCLR, data->base + RTD119X_TCWTR);
0140 rtd119x_wdt_set_timeout(&data->wdt_dev, data->wdt_dev.timeout);
0141 rtd119x_wdt_stop(&data->wdt_dev);
0142
0143 return devm_watchdog_register_device(dev, &data->wdt_dev);
0144 }
0145
0146 static struct platform_driver rtd119x_wdt_driver = {
0147 .probe = rtd119x_wdt_probe,
0148 .driver = {
0149 .name = "rtd1295-watchdog",
0150 .of_match_table = rtd119x_wdt_dt_ids,
0151 },
0152 };
0153 builtin_platform_driver(rtd119x_wdt_driver);