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
|
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (c) 2022 BayLibre, SAS.
*/
#include <clk.h>
#include <dm.h>
#include <dm/device_compat.h>
#include <reset.h>
#include <wdt.h>
#include <asm/io.h>
#include <linux/bitops.h>
#define GXBB_WDT_CTRL_REG 0x0
#define GXBB_WDT_TCNT_REG 0x8
#define GXBB_WDT_RSET_REG 0xc
#define GXBB_WDT_CTRL_SYS_RESET_NOW BIT(26)
#define GXBB_WDT_CTRL_CLKDIV_EN BIT(25)
#define GXBB_WDT_CTRL_CLK_EN BIT(24)
#define GXBB_WDT_CTRL_EE_RESET BIT(21)
#define GXBB_WDT_CTRL_EN BIT(18)
#define GXBB_WDT_CTRL_DIV_MASK GENMASK(17, 0)
#define GXBB_WDT_TCNT_SETUP_MASK GENMASK(15, 0)
struct amlogic_wdt_priv {
void __iomem *reg_base;
};
static int amlogic_wdt_set_timeout(struct udevice *dev, u64 timeout_ms)
{
struct amlogic_wdt_priv *data = dev_get_priv(dev);
if (timeout_ms > GXBB_WDT_TCNT_SETUP_MASK) {
dev_warn(dev, "%s: timeout_ms=%llu: maximum watchdog timeout exceeded\n",
__func__, timeout_ms);
timeout_ms = GXBB_WDT_TCNT_SETUP_MASK;
}
writel(timeout_ms, data->reg_base + GXBB_WDT_TCNT_REG);
return 0;
}
static int amlogic_wdt_stop(struct udevice *dev)
{
struct amlogic_wdt_priv *data = dev_get_priv(dev);
writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) & ~GXBB_WDT_CTRL_EN,
data->reg_base + GXBB_WDT_CTRL_REG);
return 0;
}
static int amlogic_wdt_start(struct udevice *dev, u64 time_ms, ulong flags)
{
struct amlogic_wdt_priv *data = dev_get_priv(dev);
writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) | GXBB_WDT_CTRL_EN,
data->reg_base + GXBB_WDT_CTRL_REG);
return amlogic_wdt_set_timeout(dev, time_ms);
}
static int amlogic_wdt_reset(struct udevice *dev)
{
struct amlogic_wdt_priv *data = dev_get_priv(dev);
writel(0, data->reg_base + GXBB_WDT_RSET_REG);
return 0;
}
static int amlogic_wdt_expire_now(struct udevice *dev, ulong flags)
{
struct amlogic_wdt_priv *data = dev_get_priv(dev);
writel(0, data->reg_base + GXBB_WDT_CTRL_SYS_RESET_NOW);
return 0;
}
static int amlogic_wdt_probe(struct udevice *dev)
{
struct amlogic_wdt_priv *data = dev_get_priv(dev);
int ret;
data->reg_base = dev_remap_addr(dev);
if (!data->reg_base)
return -EINVAL;
struct clk clk;
ret = clk_get_by_index(dev, 0, &clk);
if (ret)
return ret;
ret = clk_enable(&clk);
if (ret)
return ret;
/* Setup with 1ms timebase */
writel(((clk_get_rate(&clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) |
GXBB_WDT_CTRL_EE_RESET |
GXBB_WDT_CTRL_CLK_EN |
GXBB_WDT_CTRL_CLKDIV_EN,
data->reg_base + GXBB_WDT_CTRL_REG);
return 0;
}
static const struct wdt_ops amlogic_wdt_ops = {
.start = amlogic_wdt_start,
.reset = amlogic_wdt_reset,
.stop = amlogic_wdt_stop,
.expire_now = amlogic_wdt_expire_now,
};
static const struct udevice_id amlogic_wdt_ids[] = {
{ .compatible = "amlogic,meson-gxbb-wdt" },
{}
};
U_BOOT_DRIVER(amlogic_wdt) = {
.name = "amlogic_wdt",
.id = UCLASS_WDT,
.of_match = amlogic_wdt_ids,
.priv_auto = sizeof(struct amlogic_wdt_priv),
.probe = amlogic_wdt_probe,
.ops = &amlogic_wdt_ops,
.flags = DM_FLAG_PRE_RELOC,
};
|