blob: fced8f3686bda743b43734cd68ba6ad0f0eda8fc [file] [log] [blame]
Philippe Boos35de0c52022-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
Philippe Boos35de0c52022-06-13 16:00:56 +020027struct amlogic_wdt_priv {
28 void __iomem *reg_base;
29};
30
31static int amlogic_wdt_set_timeout(struct udevice *dev, u64 timeout_ms)
32{
33 struct amlogic_wdt_priv *data = dev_get_priv(dev);
34
35 if (timeout_ms > GXBB_WDT_TCNT_SETUP_MASK) {
36 dev_warn(dev, "%s: timeout_ms=%llu: maximum watchdog timeout exceeded\n",
37 __func__, timeout_ms);
38 timeout_ms = GXBB_WDT_TCNT_SETUP_MASK;
39 }
40
41 writel(timeout_ms, data->reg_base + GXBB_WDT_TCNT_REG);
42
43 return 0;
44}
45
46static int amlogic_wdt_stop(struct udevice *dev)
47{
48 struct amlogic_wdt_priv *data = dev_get_priv(dev);
49
50 writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) & ~GXBB_WDT_CTRL_EN,
51 data->reg_base + GXBB_WDT_CTRL_REG);
52
53 return 0;
54}
55
56static int amlogic_wdt_start(struct udevice *dev, u64 time_ms, ulong flags)
57{
58 struct amlogic_wdt_priv *data = dev_get_priv(dev);
59
60 writel(readl(data->reg_base + GXBB_WDT_CTRL_REG) | GXBB_WDT_CTRL_EN,
61 data->reg_base + GXBB_WDT_CTRL_REG);
62
63 return amlogic_wdt_set_timeout(dev, time_ms);
64}
65
66static int amlogic_wdt_reset(struct udevice *dev)
67{
68 struct amlogic_wdt_priv *data = dev_get_priv(dev);
69
70 writel(0, data->reg_base + GXBB_WDT_RSET_REG);
71
72 return 0;
73}
74
75static int amlogic_wdt_expire_now(struct udevice *dev, ulong flags)
76{
77 struct amlogic_wdt_priv *data = dev_get_priv(dev);
78
79 writel(0, data->reg_base + GXBB_WDT_CTRL_SYS_RESET_NOW);
80
81 return 0;
82}
83
84static int amlogic_wdt_probe(struct udevice *dev)
85{
86 struct amlogic_wdt_priv *data = dev_get_priv(dev);
87 int ret;
88
89 data->reg_base = dev_remap_addr(dev);
90 if (!data->reg_base)
91 return -EINVAL;
92
93 struct clk clk;
94
95 ret = clk_get_by_index(dev, 0, &clk);
96 if (ret)
97 return ret;
98
99 ret = clk_enable(&clk);
Sean Andersond318eb32023-12-16 14:38:42 -0500100 if (ret)
Philippe Boos35de0c52022-06-13 16:00:56 +0200101 return ret;
Philippe Boos35de0c52022-06-13 16:00:56 +0200102
103 /* Setup with 1ms timebase */
104 writel(((clk_get_rate(&clk) / 1000) & GXBB_WDT_CTRL_DIV_MASK) |
105 GXBB_WDT_CTRL_EE_RESET |
106 GXBB_WDT_CTRL_CLK_EN |
107 GXBB_WDT_CTRL_CLKDIV_EN,
108 data->reg_base + GXBB_WDT_CTRL_REG);
109
110 return 0;
111}
112
113static const struct wdt_ops amlogic_wdt_ops = {
114 .start = amlogic_wdt_start,
115 .reset = amlogic_wdt_reset,
116 .stop = amlogic_wdt_stop,
117 .expire_now = amlogic_wdt_expire_now,
118};
119
120static const struct udevice_id amlogic_wdt_ids[] = {
121 { .compatible = "amlogic,meson-gxbb-wdt" },
122 {}
123};
124
125U_BOOT_DRIVER(amlogic_wdt) = {
126 .name = "amlogic_wdt",
127 .id = UCLASS_WDT,
128 .of_match = amlogic_wdt_ids,
129 .priv_auto = sizeof(struct amlogic_wdt_priv),
130 .probe = amlogic_wdt_probe,
131 .ops = &amlogic_wdt_ops,
132 .flags = DM_FLAG_PRE_RELOC,
133};