blob: c030360c219ca5c99e870be8719e0f303ffa1904 [file] [log] [blame]
Troy Kiskyabbab702012-10-22 15:19:01 +00001/*
2 * watchdog.c - driver for i.mx on-chip watchdog
3 *
4 * Licensed under the GPL-2 or later.
5 */
6
7#include <common.h>
Marek Vasut4b969de2019-06-09 03:46:22 +02008#include <dm.h>
Troy Kiskyabbab702012-10-22 15:19:01 +00009#include <asm/io.h>
Marek Vasut4b969de2019-06-09 03:46:22 +020010#include <wdt.h>
Troy Kiskyabbab702012-10-22 15:19:01 +000011#include <watchdog.h>
12#include <asm/arch/imx-regs.h>
Xiaoliang Yang005c1cf2018-10-18 18:27:45 +080013#ifdef CONFIG_FSL_LSCH2
14#include <asm/arch/immap_lsch2.h>
15#endif
Fabio Estevamf5327272015-10-03 14:20:59 -030016#include <fsl_wdog.h>
Troy Kiskyabbab702012-10-22 15:19:01 +000017
Robert Hancockf2929d12019-08-06 11:05:30 -060018static void imx_watchdog_expire_now(struct watchdog_regs *wdog, bool ext_reset)
Troy Kiskyabbab702012-10-22 15:19:01 +000019{
Robert Hancockf2929d12019-08-06 11:05:30 -060020 u16 wcr = WCR_WDE;
Marek Vasut4b969de2019-06-09 03:46:22 +020021
Robert Hancockf2929d12019-08-06 11:05:30 -060022 if (ext_reset)
23 wcr |= WCR_SRS; /* do not assert internal reset */
24 else
25 wcr |= WCR_WDA; /* do not assert external reset */
26
27 /* Write 3 times to ensure it works, due to IMX6Q errata ERR004346 */
28 writew(wcr, &wdog->wcr);
29 writew(wcr, &wdog->wcr);
30 writew(wcr, &wdog->wcr);
31
Marek Vasut4b969de2019-06-09 03:46:22 +020032 while (1) {
33 /*
Robert Hancockf2929d12019-08-06 11:05:30 -060034 * spin before reset
Marek Vasut4b969de2019-06-09 03:46:22 +020035 */
36 }
37}
38
39#if !defined(CONFIG_IMX_WATCHDOG) || \
40 (defined(CONFIG_IMX_WATCHDOG) && !CONFIG_IS_ENABLED(WDT))
41void __attribute__((weak)) reset_cpu(ulong addr)
42{
Troy Kiskyabbab702012-10-22 15:19:01 +000043 struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
44
Robert Hancockf2929d12019-08-06 11:05:30 -060045 imx_watchdog_expire_now(wdog, true);
Marek Vasut4b969de2019-06-09 03:46:22 +020046}
47#endif
48
49#if defined(CONFIG_IMX_WATCHDOG)
50static void imx_watchdog_reset(struct watchdog_regs *wdog)
51{
52#ifndef CONFIG_WATCHDOG_RESET_DISABLE
Troy Kiskyabbab702012-10-22 15:19:01 +000053 writew(0x5555, &wdog->wsr);
54 writew(0xaaaa, &wdog->wsr);
Xiaoliang Yangda4918a2018-10-18 18:27:46 +080055#endif /* CONFIG_WATCHDOG_RESET_DISABLE*/
Troy Kiskyabbab702012-10-22 15:19:01 +000056}
57
Robert Hancockceea0c12019-08-06 11:05:29 -060058static void imx_watchdog_init(struct watchdog_regs *wdog, bool ext_reset)
Troy Kiskyabbab702012-10-22 15:19:01 +000059{
Troy Kiskyabbab702012-10-22 15:19:01 +000060 u16 timeout;
Robert Hancockceea0c12019-08-06 11:05:29 -060061 u16 wcr;
Troy Kiskyabbab702012-10-22 15:19:01 +000062
63 /*
64 * The timer watchdog can be set between
65 * 0.5 and 128 Seconds. If not defined
66 * in configuration file, sets 128 Seconds
67 */
68#ifndef CONFIG_WATCHDOG_TIMEOUT_MSECS
69#define CONFIG_WATCHDOG_TIMEOUT_MSECS 128000
70#endif
71 timeout = (CONFIG_WATCHDOG_TIMEOUT_MSECS / 500) - 1;
Xiaoliang Yang005c1cf2018-10-18 18:27:45 +080072#ifdef CONFIG_FSL_LSCH2
Robert Hancockceea0c12019-08-06 11:05:29 -060073 wcr = (WCR_WDA | WCR_SRS | WCR_WDE) << 8 | timeout;
Xiaoliang Yang005c1cf2018-10-18 18:27:45 +080074#else
Robert Hancockceea0c12019-08-06 11:05:29 -060075 wcr = WCR_WDZST | WCR_WDBG | WCR_WDE | WCR_SRS |
76 WCR_WDA | SET_WCR_WT(timeout);
77 if (ext_reset)
78 wcr |= WCR_WDT;
Xiaoliang Yang005c1cf2018-10-18 18:27:45 +080079#endif /* CONFIG_FSL_LSCH2*/
Robert Hancockceea0c12019-08-06 11:05:29 -060080 writew(wcr, &wdog->wcr);
Marek Vasut4b969de2019-06-09 03:46:22 +020081 imx_watchdog_reset(wdog);
Troy Kiskyabbab702012-10-22 15:19:01 +000082}
Troy Kiskyabbab702012-10-22 15:19:01 +000083
Marek Vasut4b969de2019-06-09 03:46:22 +020084#if !CONFIG_IS_ENABLED(WDT)
85void hw_watchdog_reset(void)
Troy Kiskyabbab702012-10-22 15:19:01 +000086{
87 struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
88
Marek Vasut4b969de2019-06-09 03:46:22 +020089 imx_watchdog_reset(wdog);
Troy Kiskyabbab702012-10-22 15:19:01 +000090}
Marek Vasut4b969de2019-06-09 03:46:22 +020091
92void hw_watchdog_init(void)
93{
94 struct watchdog_regs *wdog = (struct watchdog_regs *)WDOG1_BASE_ADDR;
95
Robert Hancockceea0c12019-08-06 11:05:29 -060096 imx_watchdog_init(wdog, true);
Marek Vasut4b969de2019-06-09 03:46:22 +020097}
98#else
99struct imx_wdt_priv {
100 void __iomem *base;
Robert Hancockceea0c12019-08-06 11:05:29 -0600101 bool ext_reset;
Marek Vasut4b969de2019-06-09 03:46:22 +0200102};
103
104static int imx_wdt_reset(struct udevice *dev)
105{
106 struct imx_wdt_priv *priv = dev_get_priv(dev);
107
108 imx_watchdog_reset(priv->base);
109
110 return 0;
111}
112
113static int imx_wdt_expire_now(struct udevice *dev, ulong flags)
114{
115 struct imx_wdt_priv *priv = dev_get_priv(dev);
116
Robert Hancockf2929d12019-08-06 11:05:30 -0600117 imx_watchdog_expire_now(priv->base, priv->ext_reset);
Marek Vasut4b969de2019-06-09 03:46:22 +0200118 hang();
119
120 return 0;
121}
122
123static int imx_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
124{
125 struct imx_wdt_priv *priv = dev_get_priv(dev);
126
Robert Hancockceea0c12019-08-06 11:05:29 -0600127 imx_watchdog_init(priv->base, priv->ext_reset);
Marek Vasut4b969de2019-06-09 03:46:22 +0200128
129 return 0;
130}
131
132static int imx_wdt_probe(struct udevice *dev)
133{
134 struct imx_wdt_priv *priv = dev_get_priv(dev);
135
136 priv->base = dev_read_addr_ptr(dev);
137 if (!priv->base)
138 return -ENOENT;
139
Robert Hancockceea0c12019-08-06 11:05:29 -0600140 priv->ext_reset = dev_read_bool(dev, "fsl,ext-reset-output");
141
Marek Vasut4b969de2019-06-09 03:46:22 +0200142 return 0;
143}
144
145static const struct wdt_ops imx_wdt_ops = {
146 .start = imx_wdt_start,
147 .reset = imx_wdt_reset,
148 .expire_now = imx_wdt_expire_now,
149};
150
151static const struct udevice_id imx_wdt_ids[] = {
152 { .compatible = "fsl,imx21-wdt" },
153 {}
154};
155
156U_BOOT_DRIVER(imx_wdt) = {
157 .name = "imx_wdt",
158 .id = UCLASS_WDT,
159 .of_match = imx_wdt_ids,
160 .probe = imx_wdt_probe,
161 .ops = &imx_wdt_ops,
162 .priv_auto_alloc_size = sizeof(struct imx_wdt_priv),
163 .flags = DM_FLAG_PRE_RELOC,
164};
165#endif
166#endif