Merge https://gitlab.denx.de/u-boot/custodians/u-boot-mpc85xx
[oweals/u-boot.git] / drivers / watchdog / xilinx_wwdt.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Xilinx window watchdog timer driver.
4  *
5  * Author(s):   Michal Simek <michal.simek@xilinx.com>
6  *              Ashok Reddy Soma <ashokred@xilinx.com>
7  *
8  * Copyright (c) 2020, Xilinx Inc.
9  */
10
11 #include <clk.h>
12 #include <common.h>
13 #include <dm.h>
14 #include <regmap.h>
15 #include <wdt.h>
16 #include <linux/compat.h>
17 #include <linux/io.h>
18
19 /* Refresh Register Masks */
20 #define XWT_WWREF_GWRR_MASK     BIT(0) /* Refresh and start new period */
21
22 /* Generic Control/Status Register Masks */
23 #define XWT_WWCSR_GWEN_MASK     BIT(0) /* Enable Bit */
24
25 /* Register offsets for the Wdt device */
26 #define XWT_WWREF_OFFSET        0x1000 /* Refresh Register */
27 #define XWT_WWCSR_OFFSET        0x2000 /* Control/Status Register */
28 #define XWT_WWOFF_OFFSET        0x2008 /* Offset Register */
29 #define XWT_WWCMP0_OFFSET       0x2010 /* Compare Value Register0 */
30 #define XWT_WWCMP1_OFFSET       0x2014 /* Compare Value Register1 */
31 #define XWT_WWWRST_OFFSET       0x2FD0 /* Warm Reset Register */
32
33 struct xlnx_wwdt_priv {
34         bool enable_once;
35         struct regmap *regs;
36         struct clk clk;
37 };
38
39 struct xlnx_wwdt_platdata {
40         bool enable_once;
41 };
42
43 static int xlnx_wwdt_reset(struct udevice *dev)
44 {
45         struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
46
47         regmap_write(wdt->regs, XWT_WWREF_OFFSET, XWT_WWREF_GWRR_MASK);
48
49         return 0;
50 }
51
52 static int xlnx_wwdt_stop(struct udevice *dev)
53 {
54         u32 csr;
55         struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
56
57         if (wdt->enable_once) {
58                 dev_warn(dev, "Can't stop Xilinx watchdog.\n");
59                 return -EBUSY;
60         }
61
62         /* Disable the generic watchdog timer */
63         regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
64         csr &= ~(XWT_WWCSR_GWEN_MASK);
65         regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
66
67         clk_disable(&wdt->clk);
68
69         dev_dbg(dev, "Watchdog disabled!\n");
70
71         return 0;
72 }
73
74 static int xlnx_wwdt_start(struct udevice *dev, u64 timeout, ulong flags)
75 {
76         int ret;
77         u32 csr;
78         u64 count;
79         unsigned long clock_f;
80         struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
81
82         clock_f = clk_get_rate(&wdt->clk);
83         if (IS_ERR_VALUE(clock_f)) {
84                 dev_err(dev, "failed to get rate\n");
85                 return clock_f;
86         }
87
88         dev_dbg(dev, "%s: CLK %ld\n", __func__, clock_f);
89
90         /* Calculate timeout count */
91         count = timeout * clock_f;
92
93         /* clk_enable will return -ENOSYS when it is not implemented */
94         ret = clk_enable(&wdt->clk);
95         if (ret && ret != -ENOSYS) {
96                 dev_err(dev, "failed to enable clock\n");
97                 return ret;
98         }
99
100         /*
101          * Timeout count is half as there are two windows
102          * first window overflow is ignored (interrupt),
103          * reset is only generated at second window overflow
104          */
105         count = count >> 1;
106
107         /* Disable the generic watchdog timer */
108         regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
109         csr &= ~(XWT_WWCSR_GWEN_MASK);
110         regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
111
112         /* Set compare and offset registers for generic watchdog timeout */
113         regmap_write(wdt->regs, XWT_WWCMP0_OFFSET, (u32)count);
114         regmap_write(wdt->regs, XWT_WWCMP1_OFFSET, 0);
115         regmap_write(wdt->regs, XWT_WWOFF_OFFSET, (u32)count);
116
117         /* Enable the generic watchdog timer */
118         regmap_read(wdt->regs, XWT_WWCSR_OFFSET, &csr);
119         csr |= (XWT_WWCSR_GWEN_MASK);
120         regmap_write(wdt->regs, XWT_WWCSR_OFFSET, csr);
121
122         return 0;
123 }
124
125 static int xlnx_wwdt_probe(struct udevice *dev)
126 {
127         int ret;
128         struct xlnx_wwdt_platdata *platdata = dev_get_platdata(dev);
129         struct xlnx_wwdt_priv *wdt = dev_get_priv(dev);
130
131         dev_dbg(dev, "%s: Probing wdt%u\n", __func__, dev->seq);
132
133         ret = regmap_init_mem(dev_ofnode(dev), &wdt->regs);
134         if (ret) {
135                 dev_dbg(dev, "failed to get regbase of wwdt\n");
136                 return ret;
137         }
138
139         wdt->enable_once = platdata->enable_once;
140
141         ret = clk_get_by_index(dev, 0, &wdt->clk);
142         if (ret < 0)
143                 dev_err(dev, "failed to get clock\n");
144
145         return ret;
146 }
147
148 static int xlnx_wwdt_ofdata_to_platdata(struct udevice *dev)
149 {
150         struct xlnx_wwdt_platdata *platdata = dev_get_platdata(dev);
151
152         platdata->enable_once = dev_read_u32_default(dev,
153                                                      "xlnx,wdt-enable-once", 0);
154         dev_dbg(dev, "wdt-enable-once %d\n", platdata->enable_once);
155
156         return 0;
157 }
158
159 static const struct wdt_ops xlnx_wwdt_ops = {
160         .start = xlnx_wwdt_start,
161         .reset = xlnx_wwdt_reset,
162         .stop = xlnx_wwdt_stop,
163 };
164
165 static const struct udevice_id xlnx_wwdt_ids[] = {
166         { .compatible = "xlnx,versal-wwdt-1.0", },
167         {},
168 };
169
170 U_BOOT_DRIVER(xlnx_wwdt) = {
171         .name = "xlnx_wwdt",
172         .id = UCLASS_WDT,
173         .of_match = xlnx_wwdt_ids,
174         .probe = xlnx_wwdt_probe,
175         .priv_auto_alloc_size = sizeof(struct xlnx_wwdt_priv),
176         .platdata_auto_alloc_size = sizeof(struct xlnx_wwdt_platdata),
177         .ofdata_to_platdata = xlnx_wwdt_ofdata_to_platdata,
178         .ops = &xlnx_wwdt_ops,
179 };