1 // SPDX-License-Identifier: GPL-2.0-only
3 * Watchdog driver for Intel Keem Bay non-secure watchdog.
5 * Copyright (C) 2020 Intel Corporation
8 #include <linux/arm-smccc.h>
9 #include <linux/bits.h>
10 #include <linux/clk.h>
11 #include <linux/interrupt.h>
13 #include <linux/limits.h>
14 #include <linux/module.h>
15 #include <linux/mod_devicetable.h>
16 #include <linux/platform_device.h>
17 #include <linux/reboot.h>
18 #include <linux/watchdog.h>
20 /* Non-secure watchdog register offsets */
21 #define TIM_WATCHDOG 0x0
22 #define TIM_WATCHDOG_INT_THRES 0x4
23 #define TIM_WDOG_EN 0x8
26 #define WDT_TH_INT_MASK BIT(8)
27 #define WDT_TO_INT_MASK BIT(9)
28 #define WDT_INT_CLEAR_SMC 0x8200ff18
30 #define WDT_UNLOCK 0xf1d0dead
31 #define WDT_DISABLE 0x0
32 #define WDT_ENABLE 0x1
34 #define WDT_LOAD_MAX U32_MAX
35 #define WDT_LOAD_MIN 1
38 #define WDT_PRETIMEOUT 4
40 static unsigned int timeout
= WDT_TIMEOUT
;
41 module_param(timeout
, int, 0);
42 MODULE_PARM_DESC(timeout
, "Watchdog timeout period in seconds (default = "
43 __MODULE_STRING(WDT_TIMEOUT
) ")");
45 static bool nowayout
= WATCHDOG_NOWAYOUT
;
46 module_param(nowayout
, bool, 0);
47 MODULE_PARM_DESC(nowayout
, "Watchdog cannot be stopped once started (default = "
48 __MODULE_STRING(WATCHDOG_NOWAYOUT
) ")");
51 struct watchdog_device wdd
;
59 static inline u32
keembay_wdt_readl(struct keembay_wdt
*wdt
, u32 offset
)
61 return readl(wdt
->base
+ offset
);
64 static inline void keembay_wdt_writel(struct keembay_wdt
*wdt
, u32 offset
, u32 val
)
66 writel(WDT_UNLOCK
, wdt
->base
+ TIM_SAFE
);
67 writel(val
, wdt
->base
+ offset
);
70 static void keembay_wdt_set_timeout_reg(struct watchdog_device
*wdog
)
72 struct keembay_wdt
*wdt
= watchdog_get_drvdata(wdog
);
74 keembay_wdt_writel(wdt
, TIM_WATCHDOG
, wdog
->timeout
* wdt
->rate
);
77 static void keembay_wdt_set_pretimeout_reg(struct watchdog_device
*wdog
)
79 struct keembay_wdt
*wdt
= watchdog_get_drvdata(wdog
);
83 th_val
= wdog
->timeout
- wdog
->pretimeout
;
85 keembay_wdt_writel(wdt
, TIM_WATCHDOG_INT_THRES
, th_val
* wdt
->rate
);
88 static int keembay_wdt_start(struct watchdog_device
*wdog
)
90 struct keembay_wdt
*wdt
= watchdog_get_drvdata(wdog
);
92 keembay_wdt_writel(wdt
, TIM_WDOG_EN
, WDT_ENABLE
);
97 static int keembay_wdt_stop(struct watchdog_device
*wdog
)
99 struct keembay_wdt
*wdt
= watchdog_get_drvdata(wdog
);
101 keembay_wdt_writel(wdt
, TIM_WDOG_EN
, WDT_DISABLE
);
106 static int keembay_wdt_ping(struct watchdog_device
*wdog
)
108 keembay_wdt_set_timeout_reg(wdog
);
113 static int keembay_wdt_set_timeout(struct watchdog_device
*wdog
, u32 t
)
116 keembay_wdt_set_timeout_reg(wdog
);
117 keembay_wdt_set_pretimeout_reg(wdog
);
122 static int keembay_wdt_set_pretimeout(struct watchdog_device
*wdog
, u32 t
)
124 if (t
> wdog
->timeout
)
127 wdog
->pretimeout
= t
;
128 keembay_wdt_set_pretimeout_reg(wdog
);
133 static unsigned int keembay_wdt_get_timeleft(struct watchdog_device
*wdog
)
135 struct keembay_wdt
*wdt
= watchdog_get_drvdata(wdog
);
137 return keembay_wdt_readl(wdt
, TIM_WATCHDOG
) / wdt
->rate
;
141 * SMC call is used to clear the interrupt bits, because the TIM_GEN_CONFIG
142 * register is in the secure bank.
144 static irqreturn_t
keembay_wdt_to_isr(int irq
, void *dev_id
)
146 struct keembay_wdt
*wdt
= dev_id
;
147 struct arm_smccc_res res
;
149 arm_smccc_smc(WDT_INT_CLEAR_SMC
, WDT_TO_INT_MASK
, 0, 0, 0, 0, 0, 0, &res
);
150 dev_crit(wdt
->wdd
.parent
, "Intel Keem Bay non-secure wdt timeout.\n");
156 static irqreturn_t
keembay_wdt_th_isr(int irq
, void *dev_id
)
158 struct keembay_wdt
*wdt
= dev_id
;
159 struct arm_smccc_res res
;
161 keembay_wdt_set_pretimeout(&wdt
->wdd
, 0x0);
163 arm_smccc_smc(WDT_INT_CLEAR_SMC
, WDT_TH_INT_MASK
, 0, 0, 0, 0, 0, 0, &res
);
164 dev_crit(wdt
->wdd
.parent
, "Intel Keem Bay non-secure wdt pre-timeout.\n");
165 watchdog_notify_pretimeout(&wdt
->wdd
);
170 static const struct watchdog_info keembay_wdt_info
= {
171 .identity
= "Intel Keem Bay Watchdog Timer",
172 .options
= WDIOF_SETTIMEOUT
|
178 static const struct watchdog_ops keembay_wdt_ops
= {
179 .owner
= THIS_MODULE
,
180 .start
= keembay_wdt_start
,
181 .stop
= keembay_wdt_stop
,
182 .ping
= keembay_wdt_ping
,
183 .set_timeout
= keembay_wdt_set_timeout
,
184 .set_pretimeout
= keembay_wdt_set_pretimeout
,
185 .get_timeleft
= keembay_wdt_get_timeleft
,
188 static int keembay_wdt_probe(struct platform_device
*pdev
)
190 struct device
*dev
= &pdev
->dev
;
191 struct keembay_wdt
*wdt
;
194 wdt
= devm_kzalloc(dev
, sizeof(*wdt
), GFP_KERNEL
);
198 wdt
->base
= devm_platform_ioremap_resource(pdev
, 0);
199 if (IS_ERR(wdt
->base
))
200 return PTR_ERR(wdt
->base
);
202 /* we do not need to enable the clock as it is enabled by default */
203 wdt
->clk
= devm_clk_get(dev
, NULL
);
204 if (IS_ERR(wdt
->clk
))
205 return dev_err_probe(dev
, PTR_ERR(wdt
->clk
), "Failed to get clock\n");
207 wdt
->rate
= clk_get_rate(wdt
->clk
);
209 return dev_err_probe(dev
, -EINVAL
, "Failed to get clock rate\n");
211 wdt
->th_irq
= platform_get_irq_byname(pdev
, "threshold");
213 return dev_err_probe(dev
, wdt
->th_irq
, "Failed to get IRQ for threshold\n");
215 ret
= devm_request_irq(dev
, wdt
->th_irq
, keembay_wdt_th_isr
, 0,
218 return dev_err_probe(dev
, ret
, "Failed to request IRQ for threshold\n");
220 wdt
->to_irq
= platform_get_irq_byname(pdev
, "timeout");
222 return dev_err_probe(dev
, wdt
->to_irq
, "Failed to get IRQ for timeout\n");
224 ret
= devm_request_irq(dev
, wdt
->to_irq
, keembay_wdt_to_isr
, 0,
227 return dev_err_probe(dev
, ret
, "Failed to request IRQ for timeout\n");
229 wdt
->wdd
.parent
= dev
;
230 wdt
->wdd
.info
= &keembay_wdt_info
;
231 wdt
->wdd
.ops
= &keembay_wdt_ops
;
232 wdt
->wdd
.min_timeout
= WDT_LOAD_MIN
;
233 wdt
->wdd
.max_timeout
= WDT_LOAD_MAX
/ wdt
->rate
;
234 wdt
->wdd
.timeout
= WDT_TIMEOUT
;
235 wdt
->wdd
.pretimeout
= WDT_PRETIMEOUT
;
237 watchdog_set_drvdata(&wdt
->wdd
, wdt
);
238 watchdog_set_nowayout(&wdt
->wdd
, nowayout
);
239 watchdog_init_timeout(&wdt
->wdd
, timeout
, dev
);
240 keembay_wdt_set_timeout(&wdt
->wdd
, wdt
->wdd
.timeout
);
241 keembay_wdt_set_pretimeout(&wdt
->wdd
, wdt
->wdd
.pretimeout
);
243 ret
= devm_watchdog_register_device(dev
, &wdt
->wdd
);
245 return dev_err_probe(dev
, ret
, "Failed to register watchdog device.\n");
247 platform_set_drvdata(pdev
, wdt
);
248 dev_info(dev
, "Initial timeout %d sec%s.\n",
249 wdt
->wdd
.timeout
, nowayout
? ", nowayout" : "");
254 static int __maybe_unused
keembay_wdt_suspend(struct device
*dev
)
256 struct keembay_wdt
*wdt
= dev_get_drvdata(dev
);
258 if (watchdog_active(&wdt
->wdd
))
259 return keembay_wdt_stop(&wdt
->wdd
);
264 static int __maybe_unused
keembay_wdt_resume(struct device
*dev
)
266 struct keembay_wdt
*wdt
= dev_get_drvdata(dev
);
268 if (watchdog_active(&wdt
->wdd
))
269 return keembay_wdt_start(&wdt
->wdd
);
274 static SIMPLE_DEV_PM_OPS(keembay_wdt_pm_ops
, keembay_wdt_suspend
,
277 static const struct of_device_id keembay_wdt_match
[] = {
278 { .compatible
= "intel,keembay-wdt" },
281 MODULE_DEVICE_TABLE(of
, keembay_wdt_match
);
283 static struct platform_driver keembay_wdt_driver
= {
284 .probe
= keembay_wdt_probe
,
286 .name
= "keembay_wdt",
287 .of_match_table
= keembay_wdt_match
,
288 .pm
= &keembay_wdt_pm_ops
,
292 module_platform_driver(keembay_wdt_driver
);
294 MODULE_DESCRIPTION("Intel Keem Bay SoC watchdog driver");
295 MODULE_AUTHOR("Wan Ahmad Zainie <wan.ahmad.zainie.wan.mohamad@intel.com");
296 MODULE_LICENSE("GPL v2");