hc
2024-12-19 9370bb92b2d16684ee45cf24e879c93c509162da
kernel/drivers/watchdog/qcom-wdt.c
....@@ -1,17 +1,10 @@
1
+// SPDX-License-Identifier: GPL-2.0-only
12 /* Copyright (c) 2014, The Linux Foundation. All rights reserved.
2
- *
3
- * This program is free software; you can redistribute it and/or modify
4
- * it under the terms of the GNU General Public License version 2 and
5
- * only version 2 as published by the Free Software Foundation.
6
- *
7
- * This program is distributed in the hope that it will be useful,
8
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
- * GNU General Public License for more details.
11
- *
123 */
4
+#include <linux/bits.h>
135 #include <linux/clk.h>
146 #include <linux/delay.h>
7
+#include <linux/interrupt.h>
158 #include <linux/io.h>
169 #include <linux/kernel.h>
1710 #include <linux/module.h>
....@@ -27,6 +20,8 @@
2720 WDT_BARK_TIME,
2821 WDT_BITE_TIME,
2922 };
23
+
24
+#define QCOM_WDT_ENABLE BIT(0)
3025
3126 static const u32 reg_offset_data_apcs_tmr[] = {
3227 [WDT_RST] = 0x38,
....@@ -44,9 +39,13 @@
4439 [WDT_BITE_TIME] = 0x14,
4540 };
4641
42
+struct qcom_wdt_match_data {
43
+ const u32 *offset;
44
+ bool pretimeout;
45
+};
46
+
4747 struct qcom_wdt {
4848 struct watchdog_device wdd;
49
- struct clk *clk;
5049 unsigned long rate;
5150 void __iomem *base;
5251 const u32 *layout;
....@@ -63,15 +62,25 @@
6362 return container_of(wdd, struct qcom_wdt, wdd);
6463 }
6564
65
+static irqreturn_t qcom_wdt_isr(int irq, void *arg)
66
+{
67
+ struct watchdog_device *wdd = arg;
68
+
69
+ watchdog_notify_pretimeout(wdd);
70
+
71
+ return IRQ_HANDLED;
72
+}
73
+
6674 static int qcom_wdt_start(struct watchdog_device *wdd)
6775 {
6876 struct qcom_wdt *wdt = to_qcom_wdt(wdd);
77
+ unsigned int bark = wdd->timeout - wdd->pretimeout;
6978
7079 writel(0, wdt_addr(wdt, WDT_EN));
7180 writel(1, wdt_addr(wdt, WDT_RST));
72
- writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
81
+ writel(bark * wdt->rate, wdt_addr(wdt, WDT_BARK_TIME));
7382 writel(wdd->timeout * wdt->rate, wdt_addr(wdt, WDT_BITE_TIME));
74
- writel(1, wdt_addr(wdt, WDT_EN));
83
+ writel(QCOM_WDT_ENABLE, wdt_addr(wdt, WDT_EN));
7584 return 0;
7685 }
7786
....@@ -98,6 +107,13 @@
98107 return qcom_wdt_start(wdd);
99108 }
100109
110
+static int qcom_wdt_set_pretimeout(struct watchdog_device *wdd,
111
+ unsigned int timeout)
112
+{
113
+ wdd->pretimeout = timeout;
114
+ return qcom_wdt_start(wdd);
115
+}
116
+
101117 static int qcom_wdt_restart(struct watchdog_device *wdd, unsigned long action,
102118 void *data)
103119 {
....@@ -114,7 +130,7 @@
114130 writel(1, wdt_addr(wdt, WDT_RST));
115131 writel(timeout, wdt_addr(wdt, WDT_BARK_TIME));
116132 writel(timeout, wdt_addr(wdt, WDT_BITE_TIME));
117
- writel(1, wdt_addr(wdt, WDT_EN));
133
+ writel(QCOM_WDT_ENABLE, wdt_addr(wdt, WDT_EN));
118134
119135 /*
120136 * Actually make sure the above sequence hits hardware before sleeping.
....@@ -130,6 +146,7 @@
130146 .stop = qcom_wdt_stop,
131147 .ping = qcom_wdt_ping,
132148 .set_timeout = qcom_wdt_set_timeout,
149
+ .set_pretimeout = qcom_wdt_set_pretimeout,
133150 .restart = qcom_wdt_restart,
134151 .owner = THIS_MODULE,
135152 };
....@@ -142,22 +159,48 @@
142159 .identity = KBUILD_MODNAME,
143160 };
144161
162
+static const struct watchdog_info qcom_wdt_pt_info = {
163
+ .options = WDIOF_KEEPALIVEPING
164
+ | WDIOF_MAGICCLOSE
165
+ | WDIOF_SETTIMEOUT
166
+ | WDIOF_PRETIMEOUT
167
+ | WDIOF_CARDRESET,
168
+ .identity = KBUILD_MODNAME,
169
+};
170
+
171
+static void qcom_clk_disable_unprepare(void *data)
172
+{
173
+ clk_disable_unprepare(data);
174
+}
175
+
176
+static const struct qcom_wdt_match_data match_data_apcs_tmr = {
177
+ .offset = reg_offset_data_apcs_tmr,
178
+ .pretimeout = false,
179
+};
180
+
181
+static const struct qcom_wdt_match_data match_data_kpss = {
182
+ .offset = reg_offset_data_kpss,
183
+ .pretimeout = true,
184
+};
185
+
145186 static int qcom_wdt_probe(struct platform_device *pdev)
146187 {
188
+ struct device *dev = &pdev->dev;
147189 struct qcom_wdt *wdt;
148190 struct resource *res;
149
- struct device_node *np = pdev->dev.of_node;
150
- const u32 *regs;
191
+ struct device_node *np = dev->of_node;
192
+ const struct qcom_wdt_match_data *data;
151193 u32 percpu_offset;
152
- int ret;
194
+ int irq, ret;
195
+ struct clk *clk;
153196
154
- regs = of_device_get_match_data(&pdev->dev);
155
- if (!regs) {
156
- dev_err(&pdev->dev, "Unsupported QCOM WDT module\n");
197
+ data = of_device_get_match_data(dev);
198
+ if (!data) {
199
+ dev_err(dev, "Unsupported QCOM WDT module\n");
157200 return -ENODEV;
158201 }
159202
160
- wdt = devm_kzalloc(&pdev->dev, sizeof(*wdt), GFP_KERNEL);
203
+ wdt = devm_kzalloc(dev, sizeof(*wdt), GFP_KERNEL);
161204 if (!wdt)
162205 return -ENOMEM;
163206
....@@ -172,21 +215,24 @@
172215 res->start += percpu_offset;
173216 res->end += percpu_offset;
174217
175
- wdt->base = devm_ioremap_resource(&pdev->dev, res);
218
+ wdt->base = devm_ioremap_resource(dev, res);
176219 if (IS_ERR(wdt->base))
177220 return PTR_ERR(wdt->base);
178221
179
- wdt->clk = devm_clk_get(&pdev->dev, NULL);
180
- if (IS_ERR(wdt->clk)) {
181
- dev_err(&pdev->dev, "failed to get input clock\n");
182
- return PTR_ERR(wdt->clk);
222
+ clk = devm_clk_get(dev, NULL);
223
+ if (IS_ERR(clk)) {
224
+ dev_err(dev, "failed to get input clock\n");
225
+ return PTR_ERR(clk);
183226 }
184227
185
- ret = clk_prepare_enable(wdt->clk);
228
+ ret = clk_prepare_enable(clk);
186229 if (ret) {
187
- dev_err(&pdev->dev, "failed to setup clock\n");
230
+ dev_err(dev, "failed to setup clock\n");
188231 return ret;
189232 }
233
+ ret = devm_add_action_or_reset(dev, qcom_clk_disable_unprepare, clk);
234
+ if (ret)
235
+ return ret;
190236
191237 /*
192238 * We use the clock rate to calculate the max timeout, so ensure it's
....@@ -196,20 +242,35 @@
196242 * that it would bite before a second elapses it's usefulness is
197243 * limited. Bail if this is the case.
198244 */
199
- wdt->rate = clk_get_rate(wdt->clk);
245
+ wdt->rate = clk_get_rate(clk);
200246 if (wdt->rate == 0 ||
201247 wdt->rate > 0x10000000U) {
202
- dev_err(&pdev->dev, "invalid clock rate\n");
203
- ret = -EINVAL;
204
- goto err_clk_unprepare;
248
+ dev_err(dev, "invalid clock rate\n");
249
+ return -EINVAL;
205250 }
206251
207
- wdt->wdd.info = &qcom_wdt_info;
252
+ /* check if there is pretimeout support */
253
+ irq = platform_get_irq_optional(pdev, 0);
254
+ if (data->pretimeout && irq > 0) {
255
+ ret = devm_request_irq(dev, irq, qcom_wdt_isr, 0,
256
+ "wdt_bark", &wdt->wdd);
257
+ if (ret)
258
+ return ret;
259
+
260
+ wdt->wdd.info = &qcom_wdt_pt_info;
261
+ wdt->wdd.pretimeout = 1;
262
+ } else {
263
+ if (irq == -EPROBE_DEFER)
264
+ return -EPROBE_DEFER;
265
+
266
+ wdt->wdd.info = &qcom_wdt_info;
267
+ }
268
+
208269 wdt->wdd.ops = &qcom_wdt_ops;
209270 wdt->wdd.min_timeout = 1;
210271 wdt->wdd.max_timeout = 0x10000000U / wdt->rate;
211
- wdt->wdd.parent = &pdev->dev;
212
- wdt->layout = regs;
272
+ wdt->wdd.parent = dev;
273
+ wdt->layout = data->offset;
213274
214275 if (readl(wdt_addr(wdt, WDT_STS)) & 1)
215276 wdt->wdd.bootstatus = WDIOF_CARDRESET;
....@@ -220,45 +281,52 @@
220281 * the max instead.
221282 */
222283 wdt->wdd.timeout = min(wdt->wdd.max_timeout, 30U);
223
- watchdog_init_timeout(&wdt->wdd, 0, &pdev->dev);
284
+ watchdog_init_timeout(&wdt->wdd, 0, dev);
224285
225
- ret = watchdog_register_device(&wdt->wdd);
226
- if (ret) {
227
- dev_err(&pdev->dev, "failed to register watchdog\n");
228
- goto err_clk_unprepare;
229
- }
286
+ ret = devm_watchdog_register_device(dev, &wdt->wdd);
287
+ if (ret)
288
+ return ret;
230289
231290 platform_set_drvdata(pdev, wdt);
232291 return 0;
233
-
234
-err_clk_unprepare:
235
- clk_disable_unprepare(wdt->clk);
236
- return ret;
237292 }
238293
239
-static int qcom_wdt_remove(struct platform_device *pdev)
294
+static int __maybe_unused qcom_wdt_suspend(struct device *dev)
240295 {
241
- struct qcom_wdt *wdt = platform_get_drvdata(pdev);
296
+ struct qcom_wdt *wdt = dev_get_drvdata(dev);
242297
243
- watchdog_unregister_device(&wdt->wdd);
244
- clk_disable_unprepare(wdt->clk);
298
+ if (watchdog_active(&wdt->wdd))
299
+ qcom_wdt_stop(&wdt->wdd);
300
+
245301 return 0;
246302 }
247303
304
+static int __maybe_unused qcom_wdt_resume(struct device *dev)
305
+{
306
+ struct qcom_wdt *wdt = dev_get_drvdata(dev);
307
+
308
+ if (watchdog_active(&wdt->wdd))
309
+ qcom_wdt_start(&wdt->wdd);
310
+
311
+ return 0;
312
+}
313
+
314
+static SIMPLE_DEV_PM_OPS(qcom_wdt_pm_ops, qcom_wdt_suspend, qcom_wdt_resume);
315
+
248316 static const struct of_device_id qcom_wdt_of_table[] = {
249
- { .compatible = "qcom,kpss-timer", .data = reg_offset_data_apcs_tmr },
250
- { .compatible = "qcom,scss-timer", .data = reg_offset_data_apcs_tmr },
251
- { .compatible = "qcom,kpss-wdt", .data = reg_offset_data_kpss },
317
+ { .compatible = "qcom,kpss-timer", .data = &match_data_apcs_tmr },
318
+ { .compatible = "qcom,scss-timer", .data = &match_data_apcs_tmr },
319
+ { .compatible = "qcom,kpss-wdt", .data = &match_data_kpss },
252320 { },
253321 };
254322 MODULE_DEVICE_TABLE(of, qcom_wdt_of_table);
255323
256324 static struct platform_driver qcom_watchdog_driver = {
257325 .probe = qcom_wdt_probe,
258
- .remove = qcom_wdt_remove,
259326 .driver = {
260327 .name = KBUILD_MODNAME,
261328 .of_match_table = qcom_wdt_of_table,
329
+ .pm = &qcom_wdt_pm_ops,
262330 },
263331 };
264332 module_platform_driver(qcom_watchdog_driver);