]> git.ipfire.org Git - thirdparty/u-boot.git/blob - drivers/watchdog/meson_gxbb_wdt.c
treewide: Remove clk_free
[thirdparty/u-boot.git] / drivers / watchdog / meson_gxbb_wdt.c
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 return ret;
103
104 /* Setup with 1ms timebase */
105 writel(((clk_get_rate(&clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) |
106 GXBB_WDT_CTRL_EE_RESET |
107 GXBB_WDT_CTRL_CLK_EN |
108 GXBB_WDT_CTRL_CLKDIV_EN,
109 data->reg_base + GXBB_WDT_CTRL_REG);
110
111 return 0;
112 }
113
114 static const struct wdt_ops amlogic_wdt_ops = {
115 .start = amlogic_wdt_start,
116 .reset = amlogic_wdt_reset,
117 .stop = amlogic_wdt_stop,
118 .expire_now = amlogic_wdt_expire_now,
119 };
120
121 static const struct udevice_id amlogic_wdt_ids[] = {
122 { .compatible = "amlogic,meson-gxbb-wdt" },
123 {}
124 };
125
126 U_BOOT_DRIVER(amlogic_wdt) = {
127 .name = "amlogic_wdt",
128 .id = UCLASS_WDT,
129 .of_match = amlogic_wdt_ids,
130 .priv_auto = sizeof(struct amlogic_wdt_priv),
131 .probe = amlogic_wdt_probe,
132 .ops = &amlogic_wdt_ops,
133 .flags = DM_FLAG_PRE_RELOC,
134 };