summaryrefslogtreecommitdiff
path: root/drivers/watchdog/ulp_wdog.c
blob: e0810543048533eda56fc24daa6b0a67727d69e8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2016 Freescale Semiconductor, Inc.
 */

#include <common.h>
#include <cpu_func.h>
#include <asm/io.h>
#include <asm/arch/imx-regs.h>
#include <dm.h>
#include <wdt.h>

/*
 * MX7ULP WDOG Register Map
 */
struct wdog_regs {
	u32 cs;
	u32 cnt;
	u32 toval;
	u32 win;
};

struct ulp_wdt_priv {
	struct wdog_regs *wdog;
	u32 clk_rate;
};

#ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS
#define CONFIG_WATCHDOG_TIMEOUT_MSECS 0x1500
#endif

#define REFRESH_WORD0 0xA602 /* 1st refresh word */
#define REFRESH_WORD1 0xB480 /* 2nd refresh word */

#define UNLOCK_WORD0 0xC520 /* 1st unlock word */
#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */

#define UNLOCK_WORD 0xD928C520 /* unlock word */
#define REFRESH_WORD 0xB480A602 /* refresh word */

#define WDGCS_WDGE                      BIT(7)
#define WDGCS_WDGUPDATE                 BIT(5)

#define WDGCS_RCS                       BIT(10)
#define WDGCS_ULK                       BIT(11)
#define WDOG_CS_PRES                    BIT(12)
#define WDGCS_CMD32EN                   BIT(13)
#define WDGCS_FLG                       BIT(14)
#define WDGCS_INT			BIT(6)

#define WDG_BUS_CLK                      (0x0)
#define WDG_LPO_CLK                      (0x1)
#define WDG_32KHZ_CLK                    (0x2)
#define WDG_EXT_CLK                      (0x3)

#define CLK_RATE_1KHZ			1000
#define CLK_RATE_32KHZ			125

void hw_watchdog_set_timeout(u16 val)
{
	/* setting timeout value */
	struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;

	writel(val, &wdog->toval);
}

void ulp_watchdog_reset(struct wdog_regs *wdog)
{
	if (readl(&wdog->cs) & WDGCS_CMD32EN) {
		writel(REFRESH_WORD, &wdog->cnt);
	} else {
		dmb();
		__raw_writel(REFRESH_WORD0, &wdog->cnt);
		__raw_writel(REFRESH_WORD1, &wdog->cnt);
		dmb();
	}
}

void ulp_watchdog_init(struct wdog_regs *wdog, u16 timeout)
{
	u32 cmd32 = 0;

	if (readl(&wdog->cs) & WDGCS_CMD32EN) {
		writel(UNLOCK_WORD, &wdog->cnt);
		cmd32 = WDGCS_CMD32EN;
	} else {
		dmb();
		__raw_writel(UNLOCK_WORD0, &wdog->cnt);
		__raw_writel(UNLOCK_WORD1, &wdog->cnt);
		dmb();
	}

	/* Wait WDOG Unlock */
	while (!(readl(&wdog->cs) & WDGCS_ULK))
		;

	hw_watchdog_set_timeout(timeout);
	writel(0, &wdog->win);

	/* setting 1-kHz clock source, enable counter running, and clear interrupt */
	if (IS_ENABLED(CONFIG_ARCH_IMX9))
		writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) |
		       WDGCS_FLG | WDOG_CS_PRES | WDGCS_INT), &wdog->cs);
	else
		writel((cmd32 | WDGCS_WDGE | WDGCS_WDGUPDATE | (WDG_LPO_CLK << 8) |
		       WDGCS_FLG), &wdog->cs);

	/* Wait WDOG reconfiguration */
	while (!(readl(&wdog->cs) & WDGCS_RCS))
		;

	ulp_watchdog_reset(wdog);
}

void hw_watchdog_reset(void)
{
	struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;

	ulp_watchdog_reset(wdog);
}

void hw_watchdog_init(void)
{
	struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;

	ulp_watchdog_init(wdog, CONFIG_WATCHDOG_TIMEOUT_MSECS);
}

void reset_cpu(void)
{
	struct wdog_regs *wdog = (struct wdog_regs *)WDOG_BASE_ADDR;
	u32 cmd32 = 0;

	if (readl(&wdog->cs) & WDGCS_CMD32EN) {
		writel(UNLOCK_WORD, &wdog->cnt);
		cmd32 = WDGCS_CMD32EN;
	} else {
		dmb();
		__raw_writel(UNLOCK_WORD0, &wdog->cnt);
		__raw_writel(UNLOCK_WORD1, &wdog->cnt);
		dmb();
	}

	/* Wait WDOG Unlock */
	while (!(readl(&wdog->cs) & WDGCS_ULK))
		;

	hw_watchdog_set_timeout(5); /* 5ms timeout for general; 40ms timeout for imx93 */
	writel(0, &wdog->win);

	/* enable counter running */
	if (IS_ENABLED(CONFIG_ARCH_IMX9))
		writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8) | WDOG_CS_PRES |
		       WDGCS_INT), &wdog->cs);
	else
		writel((cmd32 | WDGCS_WDGE | (WDG_LPO_CLK << 8)), &wdog->cs);

	/* Wait WDOG reconfiguration */
	while (!(readl(&wdog->cs) & WDGCS_RCS))
		;

	hw_watchdog_reset();

	while (1);
}

static int ulp_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
{
	struct ulp_wdt_priv *priv = dev_get_priv(dev);
	u64 timeout = 0;

	timeout = (timeout_ms * priv->clk_rate) / 1000;
	if (timeout > U16_MAX)
		return -EINVAL;

	ulp_watchdog_init(priv->wdog, (u16)timeout);

	return 0;
}

static int ulp_wdt_reset(struct udevice *dev)
{
	struct ulp_wdt_priv *priv = dev_get_priv(dev);

	ulp_watchdog_reset(priv->wdog);

	return 0;
}

static int ulp_wdt_probe(struct udevice *dev)
{
	struct ulp_wdt_priv *priv = dev_get_priv(dev);

	priv->wdog = dev_read_addr_ptr(dev);
	if (!priv->wdog)
		return -EINVAL;

	priv->clk_rate = (u32)dev_get_driver_data(dev);
	if (!priv->clk_rate)
		return -EINVAL;

	return 0;
}

static const struct wdt_ops ulp_wdt_ops = {
	.start = ulp_wdt_start,
	.reset = ulp_wdt_reset,
};

static const struct udevice_id ulp_wdt_ids[] = {
	{ .compatible = "fsl,imx7ulp-wdt", .data = CLK_RATE_1KHZ },
	{ .compatible = "fsl,imx8ulp-wdt", .data = CLK_RATE_1KHZ },
	{ .compatible = "fsl,imx93-wdt", .data = CLK_RATE_32KHZ },
	{}
};

U_BOOT_DRIVER(ulp_wdt) = {
	.name	= "ulp_wdt",
	.id	= UCLASS_WDT,
	.of_match	= ulp_wdt_ids,
	.priv_auto	= sizeof(struct ulp_wdt_priv),
	.probe		= ulp_wdt_probe,
	.ops	= &ulp_wdt_ops,
};