Revision | 818055fd4e977593197a40bf1fb9b811673c2858 (tree) |
---|---|
Zeit | 2022-07-21 15:09:06 |
Autor | Philippe Boos <pboos@bayl...> |
Commiter | Stefan Roese |
watchdog: add amlogic watchdog support
Add support for hardware watchdog timer for Amlogic SoCs.
This driver has been heavily inspired by his Linux equivalent
(meson_gxbb_wdt.c).
Reviewed-by: Jerome Brunet <jbrunet@baylibre.com>
Reviewed-by: Neil Armstrong <narmstrong@baylibre.com>
Signed-off-by: Philippe Boos <pboos@baylibre.com>
Reviewed-by: Stefan Roese <sr@denx.de>
@@ -161,6 +161,7 @@ F: drivers/spi/meson_spifc.c | ||
161 | 161 | F: drivers/pinctrl/meson/ |
162 | 162 | F: drivers/power/domain/meson-gx-pwrc-vpu.c |
163 | 163 | F: drivers/video/meson/ |
164 | +F: drivers/watchdog/meson_gxbb_wdt.c | |
164 | 165 | F: include/configs/meson64.h |
165 | 166 | F: include/configs/meson64_android.h |
166 | 167 | F: doc/board/amlogic/ |
@@ -73,6 +73,8 @@ This matrix concerns the actual source code version. | ||
73 | 73 | +-------------------------------+-----------+-----------------+--------------+-------------+------------+-------------+--------------+ |
74 | 74 | | PCIe (+NVMe) | *N/A* | *N/A* | *N/A* | **Yes** | **Yes** | **Yes** | **Yes** | |
75 | 75 | +-------------------------------+-----------+-----------------+--------------+-------------+------------+-------------+--------------+ |
76 | +| Watchdog | *N/A* | **Yes** | *N/A* | *N/A* | *N/A* | *N/A* | *N/A* | | |
77 | ++-------------------------------+-----------+-----------------+--------------+-------------+------------+-------------+--------------+ | |
76 | 78 | |
77 | 79 | Boot Documentation |
78 | 80 | ------------------ |
@@ -175,6 +175,13 @@ config WDT_MAX6370 | ||
175 | 175 | help |
176 | 176 | Select this to enable max6370 watchdog timer. |
177 | 177 | |
178 | +config WDT_MESON_GXBB | |
179 | + bool "Amlogic watchdog timer support" | |
180 | + depends on WDT | |
181 | + help | |
182 | + Select this to enable Meson watchdog timer, | |
183 | + which can be found on some Amlogic platforms. | |
184 | + | |
178 | 185 | config WDT_MPC8xx |
179 | 186 | bool "MPC8xx watchdog timer support" |
180 | 187 | depends on WDT && MPC8xx |
@@ -27,6 +27,7 @@ obj-$(CONFIG_WDT_ORION) += orion_wdt.o | ||
27 | 27 | obj-$(CONFIG_WDT_CDNS) += cdns_wdt.o |
28 | 28 | obj-$(CONFIG_WDT_GPIO) += gpio_wdt.o |
29 | 29 | obj-$(CONFIG_WDT_MAX6370) += max6370_wdt.o |
30 | +obj-$(CONFIG_WDT_MESON_GXBB) += meson_gxbb_wdt.o | |
30 | 31 | obj-$(CONFIG_WDT_MPC8xx) += mpc8xx_wdt.o |
31 | 32 | obj-$(CONFIG_WDT_MT7620) += mt7620_wdt.o |
32 | 33 | obj-$(CONFIG_WDT_MT7621) += mt7621_wdt.o |
@@ -0,0 +1,136 @@ | ||
1 | +// SPDX-License-Identifier: GPL-2.0+ | |
2 | +/* | |
3 | + * Copyright (c) 2022 BayLibre, SAS. | |
4 | + */ | |
5 | + | |
6 | +#include <clk.h> | |
7 | +#include <dm.h> | |
8 | +#include <dm/device_compat.h> | |
9 | +#include <reset.h> | |
10 | +#include <wdt.h> | |
11 | +#include <asm/io.h> | |
12 | +#include <linux/bitops.h> | |
13 | + | |
14 | +#define GXBB_WDT_CTRL_REG 0x0 | |
15 | +#define GXBB_WDT_TCNT_REG 0x8 | |
16 | +#define GXBB_WDT_RSET_REG 0xc | |
17 | + | |
18 | +#define GXBB_WDT_CTRL_SYS_RESET_NOW BIT(26) | |
19 | +#define GXBB_WDT_CTRL_CLKDIV_EN BIT(25) | |
20 | +#define GXBB_WDT_CTRL_CLK_EN BIT(24) | |
21 | +#define GXBB_WDT_CTRL_EE_RESET BIT(21) | |
22 | +#define GXBB_WDT_CTRL_EN BIT(18) | |
23 | + | |
24 | +#define GXBB_WDT_CTRL_DIV_MASK GENMASK(17, 0) | |
25 | +#define GXBB_WDT_TCNT_SETUP_MASK GENMASK(15, 0) | |
26 | + | |
27 | + | |
28 | +struct amlogic_wdt_priv { | |
29 | + void __iomem *reg_base; | |
30 | +}; | |
31 | + | |
32 | +static int amlogic_wdt_set_timeout(struct udevice *dev, u64 timeout_ms) | |
33 | +{ | |
34 | + struct amlogic_wdt_priv *data = dev_get_priv(dev); | |
35 | + | |
36 | + if (timeout_ms > GXBB_WDT_TCNT_SETUP_MASK) { | |
37 | + dev_warn(dev, "%s: timeout_ms=%llu: maximum watchdog timeout exceeded\n", | |
38 | + __func__, timeout_ms); | |
39 | + timeout_ms = GXBB_WDT_TCNT_SETUP_MASK; | |
40 | + } | |
41 | + | |
42 | + writel(timeout_ms, data->reg_base + GXBB_WDT_TCNT_REG); | |
43 | + | |
44 | + return 0; | |
45 | +} | |
46 | + | |
47 | +static int amlogic_wdt_stop(struct udevice *dev) | |
48 | +{ | |
49 | + struct amlogic_wdt_priv *data = dev_get_priv(dev); | |
50 | + | |
51 | + writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) & ~GXBB_WDT_CTRL_EN, | |
52 | + data->reg_base + GXBB_WDT_CTRL_REG); | |
53 | + | |
54 | + return 0; | |
55 | +} | |
56 | + | |
57 | +static int amlogic_wdt_start(struct udevice *dev, u64 time_ms, ulong flags) | |
58 | +{ | |
59 | + struct amlogic_wdt_priv *data = dev_get_priv(dev); | |
60 | + | |
61 | + writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) | GXBB_WDT_CTRL_EN, | |
62 | + data->reg_base + GXBB_WDT_CTRL_REG); | |
63 | + | |
64 | + return amlogic_wdt_set_timeout(dev, time_ms); | |
65 | +} | |
66 | + | |
67 | +static int amlogic_wdt_reset(struct udevice *dev) | |
68 | +{ | |
69 | + struct amlogic_wdt_priv *data = dev_get_priv(dev); | |
70 | + | |
71 | + writel(0, data->reg_base + GXBB_WDT_RSET_REG); | |
72 | + | |
73 | + return 0; | |
74 | +} | |
75 | + | |
76 | +static int amlogic_wdt_expire_now(struct udevice *dev, ulong flags) | |
77 | +{ | |
78 | + struct amlogic_wdt_priv *data = dev_get_priv(dev); | |
79 | + | |
80 | + writel(0, data->reg_base + GXBB_WDT_CTRL_SYS_RESET_NOW); | |
81 | + | |
82 | + return 0; | |
83 | +} | |
84 | + | |
85 | +static int amlogic_wdt_probe(struct udevice *dev) | |
86 | +{ | |
87 | + struct amlogic_wdt_priv *data = dev_get_priv(dev); | |
88 | + int ret; | |
89 | + | |
90 | + data->reg_base = dev_remap_addr(dev); | |
91 | + if (!data->reg_base) | |
92 | + return -EINVAL; | |
93 | + | |
94 | + struct clk clk; | |
95 | + | |
96 | + ret = clk_get_by_index(dev, 0, &clk); | |
97 | + if (ret) | |
98 | + return ret; | |
99 | + | |
100 | + ret = clk_enable(&clk); | |
101 | + if (ret) { | |
102 | + clk_free(&clk); | |
103 | + return ret; | |
104 | + } | |
105 | + | |
106 | + /* Setup with 1ms timebase */ | |
107 | + writel(((clk_get_rate(&clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) | | |
108 | + GXBB_WDT_CTRL_EE_RESET | | |
109 | + GXBB_WDT_CTRL_CLK_EN | | |
110 | + GXBB_WDT_CTRL_CLKDIV_EN, | |
111 | + data->reg_base + GXBB_WDT_CTRL_REG); | |
112 | + | |
113 | + return 0; | |
114 | +} | |
115 | + | |
116 | +static const struct wdt_ops amlogic_wdt_ops = { | |
117 | + .start = amlogic_wdt_start, | |
118 | + .reset = amlogic_wdt_reset, | |
119 | + .stop = amlogic_wdt_stop, | |
120 | + .expire_now = amlogic_wdt_expire_now, | |
121 | +}; | |
122 | + | |
123 | +static const struct udevice_id amlogic_wdt_ids[] = { | |
124 | + { .compatible = "amlogic,meson-gxbb-wdt" }, | |
125 | + {} | |
126 | +}; | |
127 | + | |
128 | +U_BOOT_DRIVER(amlogic_wdt) = { | |
129 | + .name = "amlogic_wdt", | |
130 | + .id = UCLASS_WDT, | |
131 | + .of_match = amlogic_wdt_ids, | |
132 | + .priv_auto = sizeof(struct amlogic_wdt_priv), | |
133 | + .probe = amlogic_wdt_probe, | |
134 | + .ops = &amlogic_wdt_ops, | |
135 | + .flags = DM_FLAG_PRE_RELOC, | |
136 | +}; |