summaryrefslogtreecommitdiff
path: root/drivers/watchdog/imx_watchdog.c
blob: b90c2daecee6ccf8d0c1727177f91c4f600b19af (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
/*
 * watchdog.c - driver for i.mx on-chip watchdog
 *
 * Licensed under the GPL-2 or later.
 */

#include <common.h>
#include <cpu_func.h>
#include <dm.h>
#include <hang.h>
#include <asm/io.h>
#include <wdt.h>
#include <watchdog.h>
#include <asm/arch/imx-regs.h>
#ifdef CONFIG_FSL_LSCH2
#include <asm/arch/immap_lsch2.h>
#endif
#include <fsl_wdog.h>
#include <div64.h>

#define TIMEOUT_MAX	128000
#define TIMEOUT_MIN	500

static void imx_watchdog_expire_now(struct watchdog_regs *wdog, bool ext_reset)
{
	u16 wcr = WCR_WDE;

	if (ext_reset)
		wcr |= WCR_SRS; /* do not assert internal reset */
	else
		wcr |= WCR_WDA; /* do not assert external reset */

	/* Write 3 times to ensure it works, due to IMX6Q errata ERR004346 */
	writew(wcr, &wdog->wcr);
	writew(wcr, &wdog->wcr);
	writew(wcr, &wdog->wcr);

	while (1) {
		/*
		 * spin before reset
		 */
	}
}

#if !defined(CONFIG_IMX_WATCHDOG) || \
    (defined(CONFIG_IMX_WATCHDOG) && !CONFIG_IS_ENABLED(WDT))
void __attribute__((weak)) reset_cpu(ulong addr)
{
	struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;

	imx_watchdog_expire_now(wdog, true);
}
#endif

#if defined(CONFIG_IMX_WATCHDOG)
static void imx_watchdog_reset(struct watchdog_regs *wdog)
{
#ifndef CONFIG_WATCHDOG_RESET_DISABLE
	writew(0x5555, &wdog->wsr);
	writew(0xaaaa, &wdog->wsr);
#endif /* CONFIG_WATCHDOG_RESET_DISABLE*/
}

static void imx_watchdog_init(struct watchdog_regs *wdog, bool ext_reset,
			      u64 timeout)
{
	u16 wcr;

	/*
	 * The timer watchdog can be set between
	 * 0.5 and 128 Seconds. If not defined
	 * in configuration file, sets 128 Seconds
	 */
#ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS
#define CONFIG_WATCHDOG_TIMEOUT_MSECS 128000
#endif

	timeout = max_t(u64, timeout, TIMEOUT_MIN);
	timeout = min_t(u64, timeout, TIMEOUT_MAX);
	timeout = lldiv(timeout, 500) - 1;

#ifdef CONFIG_FSL_LSCH2
	wcr = (WCR_WDA | WCR_SRS | WCR_WDE) << 8 | timeout;
#else
	wcr = WCR_WDZST | WCR_WDBG | WCR_WDE | WCR_SRS |
		WCR_WDA | SET_WCR_WT(timeout);
	if (ext_reset)
		wcr |= WCR_WDT;
#endif /* CONFIG_FSL_LSCH2*/
	writew(wcr, &wdog->wcr);
	imx_watchdog_reset(wdog);
}

#if !CONFIG_IS_ENABLED(WDT)
void hw_watchdog_reset(void)
{
	struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;

	imx_watchdog_reset(wdog);
}

void hw_watchdog_init(void)
{
	struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;

	imx_watchdog_init(wdog, true, CONFIG_WATCHDOG_TIMEOUT_MSECS);
}
#else
struct imx_wdt_priv {
	void __iomem *base;
	bool ext_reset;
};

static int imx_wdt_reset(struct udevice *dev)
{
	struct imx_wdt_priv *priv = dev_get_priv(dev);

	imx_watchdog_reset(priv->base);

	return 0;
}

static int imx_wdt_expire_now(struct udevice *dev, ulong flags)
{
	struct imx_wdt_priv *priv = dev_get_priv(dev);

	imx_watchdog_expire_now(priv->base, priv->ext_reset);
	hang();

	return 0;
}

static int imx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
	struct imx_wdt_priv *priv = dev_get_priv(dev);

	imx_watchdog_init(priv->base, priv->ext_reset, timeout);

	return 0;
}

static int imx_wdt_probe(struct udevice *dev)
{
	struct imx_wdt_priv *priv = dev_get_priv(dev);

	priv->base = dev_read_addr_ptr(dev);
	if (!priv->base)
		return -ENOENT;

	priv->ext_reset = dev_read_bool(dev, "fsl,ext-reset-output");

	return 0;
}

static const struct wdt_ops imx_wdt_ops = {
	.start		= imx_wdt_start,
	.reset		= imx_wdt_reset,
	.expire_now	= imx_wdt_expire_now,
};

static const struct udevice_id imx_wdt_ids[] = {
	{ .compatible = "fsl,imx21-wdt" },
	{}
};

U_BOOT_DRIVER(imx_wdt) = {
	.name		= "imx_wdt",
	.id		= UCLASS_WDT,
	.of_match	= imx_wdt_ids,
	.probe		= imx_wdt_probe,
	.ops		= &imx_wdt_ops,
	.priv_auto_alloc_size = sizeof(struct imx_wdt_priv),
	.flags		= DM_FLAG_PRE_RELOC,
};
#endif
#endif