Merge tag 'rpi-next-2019.07' of https://github.com/mbgg/u-boot
[platform/kernel/u-boot.git] / drivers / watchdog / ast_wdt.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright 2017 Google, Inc
4  */
5
6 #include <common.h>
7 #include <dm.h>
8 #include <errno.h>
9 #include <wdt.h>
10 #include <asm/io.h>
11 #include <asm/arch/wdt.h>
12
13 #define WDT_AST2500     2500
14 #define WDT_AST2400     2400
15
16 struct ast_wdt_priv {
17         struct ast_wdt *regs;
18 };
19
20 static int ast_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
21 {
22         struct ast_wdt_priv *priv = dev_get_priv(dev);
23         ulong driver_data = dev_get_driver_data(dev);
24         u32 reset_mode = ast_reset_mode_from_flags(flags);
25
26         /* 32 bits at 1MHz is 4294967ms */
27         timeout = min_t(u64, timeout, 4294967);
28
29         /* WDT counts in ticks of 1MHz clock. 1ms / 1e3 * 1e6 */
30         timeout *= 1000;
31
32         clrsetbits_le32(&priv->regs->ctrl,
33                         WDT_CTRL_RESET_MASK << WDT_CTRL_RESET_MODE_SHIFT,
34                         reset_mode << WDT_CTRL_RESET_MODE_SHIFT);
35
36         if (driver_data >= WDT_AST2500 && reset_mode == WDT_CTRL_RESET_SOC)
37                 writel(ast_reset_mask_from_flags(flags),
38                        &priv->regs->reset_mask);
39
40         writel((u32) timeout, &priv->regs->counter_reload_val);
41         writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);
42         /*
43          * Setting CLK1MHZ bit is just for compatibility with ast2400 part.
44          * On ast2500 watchdog timer clock is fixed at 1MHz and the bit is
45          * read-only
46          */
47         setbits_le32(&priv->regs->ctrl,
48                      WDT_CTRL_EN | WDT_CTRL_RESET | WDT_CTRL_CLK1MHZ);
49
50         return 0;
51 }
52
53 static int ast_wdt_stop(struct udevice *dev)
54 {
55         struct ast_wdt_priv *priv = dev_get_priv(dev);
56
57         clrbits_le32(&priv->regs->ctrl, WDT_CTRL_EN);
58
59         writel(WDT_RESET_DEFAULT, &priv->regs->reset_mask);
60         return 0;
61 }
62
63 static int ast_wdt_reset(struct udevice *dev)
64 {
65         struct ast_wdt_priv *priv = dev_get_priv(dev);
66
67         writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);
68
69         return 0;
70 }
71
72 static int ast_wdt_expire_now(struct udevice *dev, ulong flags)
73 {
74         struct ast_wdt_priv *priv = dev_get_priv(dev);
75         int ret;
76
77         ret = ast_wdt_start(dev, 1, flags);
78         if (ret)
79                 return ret;
80
81         while (readl(&priv->regs->ctrl) & WDT_CTRL_EN)
82                 ;
83
84         return ast_wdt_stop(dev);
85 }
86
87 static int ast_wdt_ofdata_to_platdata(struct udevice *dev)
88 {
89         struct ast_wdt_priv *priv = dev_get_priv(dev);
90
91         priv->regs = devfdt_get_addr_ptr(dev);
92         if (IS_ERR(priv->regs))
93                 return PTR_ERR(priv->regs);
94
95         return 0;
96 }
97
98 static const struct wdt_ops ast_wdt_ops = {
99         .start = ast_wdt_start,
100         .reset = ast_wdt_reset,
101         .stop = ast_wdt_stop,
102         .expire_now = ast_wdt_expire_now,
103 };
104
105 static const struct udevice_id ast_wdt_ids[] = {
106         { .compatible = "aspeed,wdt", .data = WDT_AST2500 },
107         { .compatible = "aspeed,ast2500-wdt", .data = WDT_AST2500 },
108         { .compatible = "aspeed,ast2400-wdt", .data = WDT_AST2400 },
109         {}
110 };
111
112 static int ast_wdt_probe(struct udevice *dev)
113 {
114         debug("%s() wdt%u\n", __func__, dev->seq);
115         ast_wdt_stop(dev);
116
117         return 0;
118 }
119
120 U_BOOT_DRIVER(ast_wdt) = {
121         .name = "ast_wdt",
122         .id = UCLASS_WDT,
123         .of_match = ast_wdt_ids,
124         .probe = ast_wdt_probe,
125         .priv_auto_alloc_size = sizeof(struct ast_wdt_priv),
126         .ofdata_to_platdata = ast_wdt_ofdata_to_platdata,
127         .ops = &ast_wdt_ops,
128 };