Merge tag 'u-boot-amlogic-20210112' of https://gitlab.denx.de/u-boot/custodians/u...
[platform/kernel/u-boot.git] / drivers / watchdog / rti_wdt.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (c) Siemens AG, 2020
4  *
5  * Authors:
6  *   Jan Kiszka <jan.kiszka@siemens.com>
7  *
8  * Derived from linux/drivers/watchdog/rti_wdt.c
9  */
10
11 #include <common.h>
12 #include <clk.h>
13 #include <dm.h>
14 #include <power-domain.h>
15 #include <wdt.h>
16 #include <asm/io.h>
17
18 /* Timer register set definition */
19 #define RTIDWDCTRL              0x90
20 #define RTIDWDPRLD              0x94
21 #define RTIWDSTATUS             0x98
22 #define RTIWDKEY                0x9c
23 #define RTIDWDCNTR              0xa0
24 #define RTIWWDRXCTRL            0xa4
25 #define RTIWWDSIZECTRL          0xa8
26
27 #define RTIWWDRX_NMI            0xa
28
29 #define RTIWWDSIZE_50P          0x50
30
31 #define WDENABLE_KEY            0xa98559da
32
33 #define WDKEY_SEQ0              0xe51a
34 #define WDKEY_SEQ1              0xa35c
35
36 #define WDT_PRELOAD_SHIFT       13
37
38 #define WDT_PRELOAD_MAX         0xfff
39
40 struct rti_wdt_priv {
41         phys_addr_t regs;
42         unsigned int clk_khz;
43 };
44
45 static int rti_wdt_start(struct udevice *dev, u64 timeout_ms, ulong flags)
46 {
47         struct rti_wdt_priv *priv = dev_get_priv(dev);
48         u32 timer_margin;
49         int ret;
50
51         if (readl(priv->regs + RTIDWDCTRL) == WDENABLE_KEY)
52                 return -EBUSY;
53
54         timer_margin = timeout_ms * priv->clk_khz / 1000;
55         timer_margin >>= WDT_PRELOAD_SHIFT;
56         if (timer_margin > WDT_PRELOAD_MAX)
57                 timer_margin = WDT_PRELOAD_MAX;
58
59         writel(timer_margin, priv->regs + RTIDWDPRLD);
60         writel(RTIWWDRX_NMI, priv->regs + RTIWWDRXCTRL);
61         writel(RTIWWDSIZE_50P, priv->regs + RTIWWDSIZECTRL);
62
63         readl(priv->regs + RTIWWDSIZECTRL);
64
65         writel(WDENABLE_KEY, priv->regs + RTIDWDCTRL);
66
67         return 0;
68 }
69
70 static int rti_wdt_reset(struct udevice *dev)
71 {
72         struct rti_wdt_priv *priv = dev_get_priv(dev);
73         u32 prld;
74
75         /* Make sure we do not reset too early */
76         prld = readl(priv->regs + RTIDWDPRLD) << WDT_PRELOAD_SHIFT;
77         if (readl(priv->regs + RTIDWDCNTR) >= prld / 2)
78                 return -EPERM;
79
80         writel(WDKEY_SEQ0, priv->regs + RTIWDKEY);
81         writel(WDKEY_SEQ1, priv->regs + RTIWDKEY);
82
83         return 0;
84 }
85
86 static int rti_wdt_probe(struct udevice *dev)
87 {
88         struct rti_wdt_priv *priv = dev_get_priv(dev);
89         struct clk clk;
90         int ret;
91
92         priv->regs = devfdt_get_addr(dev);
93         if (!priv->regs)
94                 return -EINVAL;
95
96         ret = clk_get_by_index(dev, 0, &clk);
97         if (ret)
98                 return ret;
99
100         priv->clk_khz = clk_get_rate(&clk);
101
102         return 0;
103 }
104
105 static const struct wdt_ops rti_wdt_ops = {
106         .start = rti_wdt_start,
107         .reset = rti_wdt_reset,
108 };
109
110 static const struct udevice_id rti_wdt_ids[] = {
111         { .compatible = "ti,j7-rti-wdt" },
112         { }
113 };
114
115 U_BOOT_DRIVER(rti_wdt) = {
116         .name = "rti_wdt",
117         .id = UCLASS_WDT,
118         .of_match = rti_wdt_ids,
119         .ops = &rti_wdt_ops,
120         .probe = rti_wdt_probe,
121         .priv_auto      = sizeof(struct rti_wdt_priv),
122         .flags = DM_FLAG_REMOVE_WITH_PD_ON,
123 };