Skip to content

Commit c32b53f

Browse files
Ye LiWim Van Sebroeck
authored andcommitted
watchdog: imx7ulp_wdt: Handle wdog reconfigure failure
Current driver may meet reconfigure failure caused by below reasons: 1. The wdog on iMX7ULP has different behavior after RCS valid. It needs to wait more than 2.5 wdog clock for clock sync before next reconfiguration, while imx8ulp wdog does not need such delay. 2. After unlock, there is 128 bus clock window opened for reconfiguration, but on iMX8ULP, the HW can't guarantee the latency. So it is possible the window is closed before the writing arrives to wdog. 3. If the PRES is enabled, the RCS valid time becomes x256 to the time of PRES disabled. It is about 1715ms on iMX8ULP. So We have to increase the RCS timeout and can't wait it in IRQ disabled. The patch updates the driver to handle failures 1. Using different wait for unlock and RCS. Unlock valid time is very short and only related to bus clock. It must be in IRQ disabled to avoid being interrupted in 128 clock window. But for RCS time, it is longer and ok for IRQ enabled. 2. Add retry for any reconfigure failure with default 5 times. 3. Add "fsl,imx8ulp-wdt" compatile string for iMX8ULP and afterwards platform which don't need more 2.5 wdog clock after RCS valid. For imx7ulp, add post delay of 2.5 clock after RCS valid. Signed-off-by: Ye Li <[email protected]> Signed-off-by: Alice Guo <[email protected]> Reviewed-by: Jacky Bai <[email protected]> Reviewed-by: Guenter Roeck <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Guenter Roeck <[email protected]> Signed-off-by: Wim Van Sebroeck <[email protected]>
1 parent 52c4d05 commit c32b53f

File tree

1 file changed

+129
-34
lines changed

1 file changed

+129
-34
lines changed

drivers/watchdog/imx7ulp_wdt.c

Lines changed: 129 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
#include <linux/watchdog.h>
1515

1616
#define WDOG_CS 0x0
17+
#define WDOG_CS_FLG BIT(14)
1718
#define WDOG_CS_CMD32EN BIT(13)
19+
#define WDOG_CS_PRES BIT(12)
1820
#define WDOG_CS_ULK BIT(11)
1921
#define WDOG_CS_RCS BIT(10)
2022
#define LPO_CLK 0x1
@@ -39,7 +41,11 @@
3941
#define DEFAULT_TIMEOUT 60
4042
#define MAX_TIMEOUT 128
4143
#define WDOG_CLOCK_RATE 1000
42-
#define WDOG_WAIT_TIMEOUT 10000
44+
#define WDOG_ULK_WAIT_TIMEOUT 1000
45+
#define WDOG_RCS_WAIT_TIMEOUT 10000
46+
#define WDOG_RCS_POST_WAIT 3000
47+
48+
#define RETRY_MAX 5
4349

4450
static bool nowayout = WATCHDOG_NOWAYOUT;
4551
module_param(nowayout, bool, 0000);
@@ -50,40 +56,82 @@ struct imx7ulp_wdt_device {
5056
struct watchdog_device wdd;
5157
void __iomem *base;
5258
struct clk *clk;
59+
bool post_rcs_wait;
5360
};
5461

55-
static int imx7ulp_wdt_wait(void __iomem *base, u32 mask)
62+
static int imx7ulp_wdt_wait_ulk(void __iomem *base)
5663
{
5764
u32 val = readl(base + WDOG_CS);
5865

59-
if (!(val & mask) && readl_poll_timeout_atomic(base + WDOG_CS, val,
60-
val & mask, 0,
61-
WDOG_WAIT_TIMEOUT))
66+
if (!(val & WDOG_CS_ULK) &&
67+
readl_poll_timeout_atomic(base + WDOG_CS, val,
68+
val & WDOG_CS_ULK, 0,
69+
WDOG_ULK_WAIT_TIMEOUT))
6270
return -ETIMEDOUT;
6371

6472
return 0;
6573
}
6674

67-
static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
75+
static int imx7ulp_wdt_wait_rcs(struct imx7ulp_wdt_device *wdt)
6876
{
69-
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
77+
int ret = 0;
78+
u32 val = readl(wdt->base + WDOG_CS);
79+
u64 timeout = (val & WDOG_CS_PRES) ?
80+
WDOG_RCS_WAIT_TIMEOUT * 256 : WDOG_RCS_WAIT_TIMEOUT;
81+
unsigned long wait_min = (val & WDOG_CS_PRES) ?
82+
WDOG_RCS_POST_WAIT * 256 : WDOG_RCS_POST_WAIT;
7083

84+
if (!(val & WDOG_CS_RCS) &&
85+
readl_poll_timeout(wdt->base + WDOG_CS, val, val & WDOG_CS_RCS, 100,
86+
timeout))
87+
ret = -ETIMEDOUT;
88+
89+
/* Wait 2.5 clocks after RCS done */
90+
if (wdt->post_rcs_wait)
91+
usleep_range(wait_min, wait_min + 2000);
92+
93+
return ret;
94+
}
95+
96+
static int _imx7ulp_wdt_enable(struct imx7ulp_wdt_device *wdt, bool enable)
97+
{
7198
u32 val = readl(wdt->base + WDOG_CS);
7299
int ret;
73100

74101
local_irq_disable();
75102
writel(UNLOCK, wdt->base + WDOG_CNT);
76-
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
103+
ret = imx7ulp_wdt_wait_ulk(wdt->base);
77104
if (ret)
78105
goto enable_out;
79106
if (enable)
80107
writel(val | WDOG_CS_EN, wdt->base + WDOG_CS);
81108
else
82109
writel(val & ~WDOG_CS_EN, wdt->base + WDOG_CS);
83-
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
110+
111+
local_irq_enable();
112+
ret = imx7ulp_wdt_wait_rcs(wdt);
113+
114+
return ret;
84115

85116
enable_out:
86117
local_irq_enable();
118+
return ret;
119+
}
120+
121+
static int imx7ulp_wdt_enable(struct watchdog_device *wdog, bool enable)
122+
{
123+
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
124+
int ret;
125+
u32 val;
126+
u32 loop = RETRY_MAX;
127+
128+
do {
129+
ret = _imx7ulp_wdt_enable(wdt, enable);
130+
val = readl(wdt->base + WDOG_CS);
131+
} while (--loop > 0 && ((!!(val & WDOG_CS_EN)) != enable || ret));
132+
133+
if (loop == 0)
134+
return -EBUSY;
87135

88136
return ret;
89137
}
@@ -114,28 +162,44 @@ static int imx7ulp_wdt_stop(struct watchdog_device *wdog)
114162
return imx7ulp_wdt_enable(wdog, false);
115163
}
116164

117-
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
118-
unsigned int timeout)
165+
static int _imx7ulp_wdt_set_timeout(struct imx7ulp_wdt_device *wdt,
166+
unsigned int toval)
119167
{
120-
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
121-
u32 val = WDOG_CLOCK_RATE * timeout;
122168
int ret;
123169

124170
local_irq_disable();
125171
writel(UNLOCK, wdt->base + WDOG_CNT);
126-
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_ULK);
172+
ret = imx7ulp_wdt_wait_ulk(wdt->base);
127173
if (ret)
128174
goto timeout_out;
129-
writel(val, wdt->base + WDOG_TOVAL);
130-
ret = imx7ulp_wdt_wait(wdt->base, WDOG_CS_RCS);
131-
if (ret)
132-
goto timeout_out;
133-
134-
wdog->timeout = timeout;
175+
writel(toval, wdt->base + WDOG_TOVAL);
176+
local_irq_enable();
177+
ret = imx7ulp_wdt_wait_rcs(wdt);
178+
return ret;
135179

136180
timeout_out:
137181
local_irq_enable();
182+
return ret;
183+
}
138184

185+
static int imx7ulp_wdt_set_timeout(struct watchdog_device *wdog,
186+
unsigned int timeout)
187+
{
188+
struct imx7ulp_wdt_device *wdt = watchdog_get_drvdata(wdog);
189+
u32 toval = WDOG_CLOCK_RATE * timeout;
190+
u32 val;
191+
int ret;
192+
u32 loop = RETRY_MAX;
193+
194+
do {
195+
ret = _imx7ulp_wdt_set_timeout(wdt, toval);
196+
val = readl(wdt->base + WDOG_TOVAL);
197+
} while (--loop > 0 && (val != toval || ret));
198+
199+
if (loop == 0)
200+
return -EBUSY;
201+
202+
wdog->timeout = timeout;
139203
return ret;
140204
}
141205

@@ -175,38 +239,59 @@ static const struct watchdog_info imx7ulp_wdt_info = {
175239
WDIOF_MAGICCLOSE,
176240
};
177241

178-
static int imx7ulp_wdt_init(void __iomem *base, unsigned int timeout)
242+
static int _imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout, unsigned int cs)
179243
{
180244
u32 val;
181245
int ret;
182246

183247
local_irq_disable();
184248

185-
val = readl(base + WDOG_CS);
249+
val = readl(wdt->base + WDOG_CS);
186250
if (val & WDOG_CS_CMD32EN) {
187-
writel(UNLOCK, base + WDOG_CNT);
251+
writel(UNLOCK, wdt->base + WDOG_CNT);
188252
} else {
189253
mb();
190254
/* unlock the wdog for reconfiguration */
191-
writel_relaxed(UNLOCK_SEQ0, base + WDOG_CNT);
192-
writel_relaxed(UNLOCK_SEQ1, base + WDOG_CNT);
255+
writel_relaxed(UNLOCK_SEQ0, wdt->base + WDOG_CNT);
256+
writel_relaxed(UNLOCK_SEQ1, wdt->base + WDOG_CNT);
193257
mb();
194258
}
195259

196-
ret = imx7ulp_wdt_wait(base, WDOG_CS_ULK);
260+
ret = imx7ulp_wdt_wait_ulk(wdt->base);
197261
if (ret)
198262
goto init_out;
199263

200264
/* set an initial timeout value in TOVAL */
201-
writel(timeout, base + WDOG_TOVAL);
202-
/* enable 32bit command sequence and reconfigure */
203-
val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
204-
WDOG_CS_WAIT | WDOG_CS_STOP;
205-
writel(val, base + WDOG_CS);
206-
imx7ulp_wdt_wait(base, WDOG_CS_RCS);
265+
writel(timeout, wdt->base + WDOG_TOVAL);
266+
writel(cs, wdt->base + WDOG_CS);
267+
local_irq_enable();
268+
ret = imx7ulp_wdt_wait_rcs(wdt);
269+
270+
return ret;
207271

208272
init_out:
209273
local_irq_enable();
274+
return ret;
275+
}
276+
277+
static int imx7ulp_wdt_init(struct imx7ulp_wdt_device *wdt, unsigned int timeout)
278+
{
279+
/* enable 32bit command sequence and reconfigure */
280+
u32 val = WDOG_CS_CMD32EN | WDOG_CS_CLK | WDOG_CS_UPDATE |
281+
WDOG_CS_WAIT | WDOG_CS_STOP;
282+
u32 cs, toval;
283+
int ret;
284+
u32 loop = RETRY_MAX;
285+
286+
do {
287+
ret = _imx7ulp_wdt_init(wdt, timeout, val);
288+
toval = readl(wdt->base + WDOG_TOVAL);
289+
cs = readl(wdt->base + WDOG_CS);
290+
cs &= ~(WDOG_CS_FLG | WDOG_CS_ULK | WDOG_CS_RCS);
291+
} while (--loop > 0 && (cs != val || toval != timeout || ret));
292+
293+
if (loop == 0)
294+
return -EBUSY;
210295

211296
return ret;
212297
}
@@ -239,6 +324,15 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
239324
return PTR_ERR(imx7ulp_wdt->clk);
240325
}
241326

327+
imx7ulp_wdt->post_rcs_wait = true;
328+
if (of_device_is_compatible(dev->of_node,
329+
"fsl,imx8ulp-wdt")) {
330+
dev_info(dev, "imx8ulp wdt probe\n");
331+
imx7ulp_wdt->post_rcs_wait = false;
332+
} else {
333+
dev_info(dev, "imx7ulp wdt probe\n");
334+
}
335+
242336
ret = clk_prepare_enable(imx7ulp_wdt->clk);
243337
if (ret)
244338
return ret;
@@ -259,7 +353,7 @@ static int imx7ulp_wdt_probe(struct platform_device *pdev)
259353
watchdog_stop_on_reboot(wdog);
260354
watchdog_stop_on_unregister(wdog);
261355
watchdog_set_drvdata(wdog, imx7ulp_wdt);
262-
ret = imx7ulp_wdt_init(imx7ulp_wdt->base, wdog->timeout * WDOG_CLOCK_RATE);
356+
ret = imx7ulp_wdt_init(imx7ulp_wdt, wdog->timeout * WDOG_CLOCK_RATE);
263357
if (ret)
264358
return ret;
265359

@@ -289,7 +383,7 @@ static int __maybe_unused imx7ulp_wdt_resume_noirq(struct device *dev)
289383
return ret;
290384

291385
if (imx7ulp_wdt_is_enabled(imx7ulp_wdt->base))
292-
imx7ulp_wdt_init(imx7ulp_wdt->base, timeout);
386+
imx7ulp_wdt_init(imx7ulp_wdt, timeout);
293387

294388
if (watchdog_active(&imx7ulp_wdt->wdd))
295389
imx7ulp_wdt_start(&imx7ulp_wdt->wdd);
@@ -303,6 +397,7 @@ static const struct dev_pm_ops imx7ulp_wdt_pm_ops = {
303397
};
304398

305399
static const struct of_device_id imx7ulp_wdt_dt_ids[] = {
400+
{ .compatible = "fsl,imx8ulp-wdt", },
306401
{ .compatible = "fsl,imx7ulp-wdt", },
307402
{ /* sentinel */ }
308403
};

0 commit comments

Comments
 (0)