blob: 6ab005813f923d5bd03af8bc794bb8bf6b0ef550 [file] [log] [blame]
Philippe Boos818055f2022-06-13 16:00:56 +02001// 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
28struct amlogic_wdt_priv {
29 void __iomem *reg_base;
30};
31
32static 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
47static 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
57static 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
67static 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
76static 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
85static 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
116static 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
123static const struct udevice_id amlogic_wdt_ids[] = {
124 { .compatible = "amlogic,meson-gxbb-wdt" },
125 {}
126};
127
128U_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};