projects/linux·blob master · refs

162 lines · 4.3 KiB

1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Renesas RZ/N1 Watchdog timer.
4 * This is a 12-bit timer driver from a (62.5/16384) MHz clock. It can't even
5 * cope with 2 seconds.
6 *
7 * Copyright 2018 Renesas Electronics Europe Ltd.
8 *
9 * Derived from Ralink RT288x watchdog timer.
10 */
11
12#include <linux/clk.h>
13#include <linux/interrupt.h>
14#include <linux/kernel.h>
15#include <linux/module.h>
16#include <linux/of_address.h>
17#include <linux/of_irq.h>
18#include <linux/platform_device.h>
19#include <linux/reboot.h>
20#include <linux/watchdog.h>
21
22#define DEFAULT_TIMEOUT 60
23
24#define RZN1_WDT_RETRIGGER 0x0
25#define RZN1_WDT_RETRIGGER_RELOAD_VAL 0
26#define RZN1_WDT_RETRIGGER_RELOAD_VAL_MASK 0xfff
27#define RZN1_WDT_RETRIGGER_PRESCALE BIT(12)
28#define RZN1_WDT_RETRIGGER_ENABLE BIT(13)
29#define RZN1_WDT_RETRIGGER_WDSI (0x2 << 14)
30
31#define RZN1_WDT_PRESCALER 16384
32#define RZN1_WDT_MAX 4095
33
34struct rzn1_watchdog {
35 struct watchdog_device wdtdev;
36 void __iomem *base;
37 unsigned long clk_rate_khz;
38};
39
40static inline uint32_t max_heart_beat_ms(unsigned long clk_rate_khz)
41{
42 return (RZN1_WDT_MAX * RZN1_WDT_PRESCALER) / clk_rate_khz;
43}
44
45static inline uint32_t compute_reload_value(uint32_t tick_ms,
46 unsigned long clk_rate_khz)
47{
48 return (tick_ms * clk_rate_khz) / RZN1_WDT_PRESCALER;
49}
50
51static int rzn1_wdt_ping(struct watchdog_device *w)
52{
53 struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
54
55 /* Any value retriggers the watchdog */
56 writel(0, wdt->base + RZN1_WDT_RETRIGGER);
57
58 return 0;
59}
60
61static int rzn1_wdt_start(struct watchdog_device *w)
62{
63 struct rzn1_watchdog *wdt = watchdog_get_drvdata(w);
64 u32 val;
65
66 /*
67 * The hardware allows you to write to this reg only once.
68 * Since this includes the reload value, there is no way to change the
69 * timeout once started. Also note that the WDT clock is half the bus
70 * fabric clock rate, so if the bus fabric clock rate is changed after
71 * the WDT is started, the WDT interval will be wrong.
72 */
73 val = RZN1_WDT_RETRIGGER_WDSI;
74 val |= RZN1_WDT_RETRIGGER_ENABLE;
75 val |= RZN1_WDT_RETRIGGER_PRESCALE;
76 val |= compute_reload_value(w->max_hw_heartbeat_ms, wdt->clk_rate_khz);
77 writel(val, wdt->base + RZN1_WDT_RETRIGGER);
78
79 return 0;
80}
81
82static struct watchdog_info rzn1_wdt_info = {
83 .identity = "RZ/N1 Watchdog",
84 .options = WDIOF_MAGICCLOSE | WDIOF_SETTIMEOUT | WDIOF_KEEPALIVEPING,
85};
86
87static const struct watchdog_ops rzn1_wdt_ops = {
88 .owner = THIS_MODULE,
89 .start = rzn1_wdt_start,
90 .ping = rzn1_wdt_ping,
91};
92
93static int rzn1_wdt_probe(struct platform_device *pdev)
94{
95 struct device *dev = &pdev->dev;
96 struct rzn1_watchdog *wdt;
97 unsigned long clk_rate;
98 struct clk *clk;
99 int ret;
100
101 wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
102 if (!wdt)
103 return -ENOMEM;
104
105 wdt->base = devm_platform_ioremap_resource(pdev, 0);
106 if (IS_ERR(wdt->base))
107 return PTR_ERR(wdt->base);
108
109 clk = devm_clk_get_enabled(dev, NULL);
110 if (IS_ERR(clk))
111 return dev_err_probe(dev, PTR_ERR(clk), "failed to get the clock\n");
112
113 clk_rate = clk_get_rate(clk);
114 if (!clk_rate)
115 return dev_err_probe(dev, -EINVAL, "failed to get the clock rate\n");
116
117 wdt->clk_rate_khz = clk_rate / 1000;
118 wdt->wdtdev.info = &rzn1_wdt_info;
119 wdt->wdtdev.ops = &rzn1_wdt_ops;
120 wdt->wdtdev.status = WATCHDOG_NOWAYOUT_INIT_STATUS;
121 wdt->wdtdev.parent = dev;
122 /*
123 * The period of the watchdog cannot be changed once set
124 * and is limited to a very short period.
125 * Configure it for a 1s period once and for all, and
126 * rely on the heart-beat provided by the watchdog core
127 * to make this usable by the user-space.
128 */
129 wdt->wdtdev.max_hw_heartbeat_ms = max_heart_beat_ms(wdt->clk_rate_khz);
130 if (wdt->wdtdev.max_hw_heartbeat_ms > 1000)
131 wdt->wdtdev.max_hw_heartbeat_ms = 1000;
132
133 wdt->wdtdev.timeout = DEFAULT_TIMEOUT;
134 ret = watchdog_init_timeout(&wdt->wdtdev, 0, dev);
135 if (ret)
136 return ret;
137
138 watchdog_set_drvdata(&wdt->wdtdev, wdt);
139
140 return devm_watchdog_register_device(dev, &wdt->wdtdev);
141}
142
143
144static const struct of_device_id rzn1_wdt_match[] = {
145 { .compatible = "renesas,rzn1-wdt" },
146 {},
147};
148MODULE_DEVICE_TABLE(of, rzn1_wdt_match);
149
150static struct platform_driver rzn1_wdt_driver = {
151 .probe = rzn1_wdt_probe,
152 .driver = {
153 .name = KBUILD_MODNAME,
154 .of_match_table = rzn1_wdt_match,
155 },
156};
157
158module_platform_driver(rzn1_wdt_driver);
159
160MODULE_DESCRIPTION("Renesas RZ/N1 hardware watchdog");
161MODULE_AUTHOR("Phil Edworthy <[email protected]>");
162MODULE_LICENSE("GPL");