]> Git Repo - J-linux.git/blob - drivers/watchdog/xilinx_wwdt.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / drivers / watchdog / xilinx_wwdt.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Window watchdog device driver for Xilinx Versal WWDT
4  *
5  * Copyright (C) 2022 - 2024, Advanced Micro Devices, Inc.
6  */
7
8 #include <linux/clk.h>
9 #include <linux/interrupt.h>
10 #include <linux/io.h>
11 #include <linux/ioport.h>
12 #include <linux/math64.h>
13 #include <linux/mod_devicetable.h>
14 #include <linux/module.h>
15 #include <linux/platform_device.h>
16 #include <linux/watchdog.h>
17
18 /* Max timeout is calculated at 100MHz source clock */
19 #define XWWDT_DEFAULT_TIMEOUT   42
20 #define XWWDT_MIN_TIMEOUT       1
21
22 /* Register offsets for the WWDT device */
23 #define XWWDT_MWR_OFFSET        0x00
24 #define XWWDT_ESR_OFFSET        0x04
25 #define XWWDT_FCR_OFFSET        0x08
26 #define XWWDT_FWR_OFFSET        0x0c
27 #define XWWDT_SWR_OFFSET        0x10
28
29 /* Master Write Control Register Masks */
30 #define XWWDT_MWR_MASK          BIT(0)
31
32 /* Enable and Status Register Masks */
33 #define XWWDT_ESR_WINT_MASK     BIT(16)
34 #define XWWDT_ESR_WSW_MASK      BIT(8)
35 #define XWWDT_ESR_WEN_MASK      BIT(0)
36
37 #define XWWDT_CLOSE_WINDOW_PERCENT      50
38
39 /* Maximum count value of each 32 bit window */
40 #define XWWDT_MAX_COUNT_WINDOW          GENMASK(31, 0)
41
42 /* Maximum count value of closed and open window combined */
43 #define XWWDT_MAX_COUNT_WINDOW_COMBINED GENMASK_ULL(32, 1)
44
45 static int wwdt_timeout;
46 static int closed_window_percent;
47
48 module_param(wwdt_timeout, int, 0);
49 MODULE_PARM_DESC(wwdt_timeout,
50                  "Watchdog time in seconds. (default="
51                  __MODULE_STRING(XWWDT_DEFAULT_TIMEOUT) ")");
52 module_param(closed_window_percent, int, 0);
53 MODULE_PARM_DESC(closed_window_percent,
54                  "Watchdog closed window percentage. (default="
55                  __MODULE_STRING(XWWDT_CLOSE_WINDOW_PERCENT) ")");
56 /**
57  * struct xwwdt_device - Watchdog device structure
58  * @base: base io address of WDT device
59  * @spinlock: spinlock for IO register access
60  * @xilinx_wwdt_wdd: watchdog device structure
61  * @freq: source clock frequency of WWDT
62  * @close_percent: Closed window percent
63  * @closed_timeout: Closed window timeout in ticks
64  * @open_timeout: Open window timeout in ticks
65  */
66 struct xwwdt_device {
67         void __iomem *base;
68         spinlock_t spinlock; /* spinlock for register handling */
69         struct watchdog_device xilinx_wwdt_wdd;
70         unsigned long freq;
71         u32 close_percent;
72         u64 closed_timeout;
73         u64 open_timeout;
74 };
75
76 static int xilinx_wwdt_start(struct watchdog_device *wdd)
77 {
78         struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
79         struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
80         u32 control_status_reg;
81
82         spin_lock(&xdev->spinlock);
83
84         iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
85         iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET);
86         iowrite32((u32)xdev->closed_timeout, xdev->base + XWWDT_FWR_OFFSET);
87         iowrite32((u32)xdev->open_timeout, xdev->base + XWWDT_SWR_OFFSET);
88
89         /* Enable the window watchdog timer */
90         control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET);
91         control_status_reg |= XWWDT_ESR_WEN_MASK;
92         iowrite32(control_status_reg, xdev->base + XWWDT_ESR_OFFSET);
93
94         spin_unlock(&xdev->spinlock);
95
96         dev_dbg(xilinx_wwdt_wdd->parent, "Watchdog Started!\n");
97
98         return 0;
99 }
100
101 static int xilinx_wwdt_keepalive(struct watchdog_device *wdd)
102 {
103         struct xwwdt_device *xdev = watchdog_get_drvdata(wdd);
104         u32 control_status_reg;
105
106         spin_lock(&xdev->spinlock);
107
108         /* Enable write access control bit for the window watchdog */
109         iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET);
110
111         /* Trigger restart kick to watchdog */
112         control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET);
113         control_status_reg |= XWWDT_ESR_WSW_MASK;
114         iowrite32(control_status_reg, xdev->base + XWWDT_ESR_OFFSET);
115
116         spin_unlock(&xdev->spinlock);
117
118         return 0;
119 }
120
121 static const struct watchdog_info xilinx_wwdt_ident = {
122         .options = WDIOF_KEEPALIVEPING |
123                 WDIOF_SETTIMEOUT,
124         .firmware_version = 1,
125         .identity = "xlnx_window watchdog",
126 };
127
128 static const struct watchdog_ops xilinx_wwdt_ops = {
129         .owner = THIS_MODULE,
130         .start = xilinx_wwdt_start,
131         .ping = xilinx_wwdt_keepalive,
132 };
133
134 static int xwwdt_probe(struct platform_device *pdev)
135 {
136         struct watchdog_device *xilinx_wwdt_wdd;
137         struct device *dev = &pdev->dev;
138         struct xwwdt_device *xdev;
139         u64 max_per_window_ms;
140         u64 min_per_window_ms;
141         u64 timeout_count;
142         struct clk *clk;
143         u32 timeout_ms;
144         u64 ms_count;
145         int ret;
146
147         xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL);
148         if (!xdev)
149                 return -ENOMEM;
150
151         xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd;
152         xilinx_wwdt_wdd->info = &xilinx_wwdt_ident;
153         xilinx_wwdt_wdd->ops = &xilinx_wwdt_ops;
154         xilinx_wwdt_wdd->parent = dev;
155
156         xdev->base = devm_platform_ioremap_resource(pdev, 0);
157         if (IS_ERR(xdev->base))
158                 return PTR_ERR(xdev->base);
159
160         clk = devm_clk_get_enabled(dev, NULL);
161         if (IS_ERR(clk))
162                 return PTR_ERR(clk);
163
164         xdev->freq = clk_get_rate(clk);
165         if (xdev->freq < 1000000)
166                 return -EINVAL;
167
168         xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT;
169         xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT;
170         xilinx_wwdt_wdd->max_hw_heartbeat_ms =
171                 div64_u64(XWWDT_MAX_COUNT_WINDOW_COMBINED, xdev->freq) * 1000;
172
173         if (closed_window_percent == 0 || closed_window_percent >= 100)
174                 xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT;
175         else
176                 xdev->close_percent = closed_window_percent;
177
178         watchdog_init_timeout(xilinx_wwdt_wdd, wwdt_timeout, &pdev->dev);
179
180         /* Calculate ticks for 1 milli-second */
181         ms_count = div_u64(xdev->freq, 1000);
182         timeout_ms = xilinx_wwdt_wdd->timeout * 1000;
183         timeout_count = timeout_ms * ms_count;
184
185         if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms) {
186                 /*
187                  * To avoid ping restrictions until the minimum hardware heartbeat,
188                  * we will solely rely on the open window and
189                  * adjust the minimum hardware heartbeat to 0.
190                  */
191                 xdev->closed_timeout = 0;
192                 xdev->open_timeout = XWWDT_MAX_COUNT_WINDOW;
193                 xilinx_wwdt_wdd->min_hw_heartbeat_ms = 0;
194                 xilinx_wwdt_wdd->max_hw_heartbeat_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
195         } else {
196                 xdev->closed_timeout  = div64_u64(timeout_count * xdev->close_percent, 100);
197                 xilinx_wwdt_wdd->min_hw_heartbeat_ms =
198                         div64_u64(timeout_ms * xdev->close_percent, 100);
199
200                 if (timeout_ms > xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2) {
201                         max_per_window_ms = xilinx_wwdt_wdd->max_hw_heartbeat_ms / 2;
202                         min_per_window_ms = timeout_ms - max_per_window_ms;
203
204                         if (xilinx_wwdt_wdd->min_hw_heartbeat_ms > max_per_window_ms) {
205                                 dev_info(xilinx_wwdt_wdd->parent,
206                                          "Closed window cannot be set to %d%%. Using maximum supported value.\n",
207                                         xdev->close_percent);
208                                 xdev->closed_timeout = max_per_window_ms * ms_count;
209                                 xilinx_wwdt_wdd->min_hw_heartbeat_ms = max_per_window_ms;
210                         } else if (xilinx_wwdt_wdd->min_hw_heartbeat_ms < min_per_window_ms) {
211                                 dev_info(xilinx_wwdt_wdd->parent,
212                                          "Closed window cannot be set to %d%%. Using minimum supported value.\n",
213                                         xdev->close_percent);
214                                 xdev->closed_timeout = min_per_window_ms * ms_count;
215                                 xilinx_wwdt_wdd->min_hw_heartbeat_ms = min_per_window_ms;
216                         }
217                 }
218                 xdev->open_timeout = timeout_count - xdev->closed_timeout;
219         }
220
221         spin_lock_init(&xdev->spinlock);
222         watchdog_set_drvdata(xilinx_wwdt_wdd, xdev);
223         watchdog_set_nowayout(xilinx_wwdt_wdd, 1);
224
225         ret = devm_watchdog_register_device(dev, xilinx_wwdt_wdd);
226         if (ret)
227                 return ret;
228
229         dev_info(dev, "Xilinx window watchdog Timer with timeout %ds\n",
230                  xilinx_wwdt_wdd->timeout);
231
232         return 0;
233 }
234
235 static const struct of_device_id xwwdt_of_match[] = {
236         { .compatible = "xlnx,versal-wwdt", },
237         {},
238 };
239 MODULE_DEVICE_TABLE(of, xwwdt_of_match);
240
241 static struct platform_driver xwwdt_driver = {
242         .probe = xwwdt_probe,
243         .driver = {
244                 .name = "Xilinx window watchdog",
245                 .of_match_table = xwwdt_of_match,
246         },
247 };
248
249 module_platform_driver(xwwdt_driver);
250
251 MODULE_AUTHOR("Neeli Srinivas <[email protected]>");
252 MODULE_DESCRIPTION("Xilinx window watchdog driver");
253 MODULE_LICENSE("GPL");
This page took 0.040265 seconds and 4 git commands to generate.