Merge branch 'master' of git://git.denx.de/u-boot-sh
[platform/kernel/u-boot.git] / drivers / watchdog / sp805_wdt.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Watchdog driver for SP805 on some Layerscape SoC
4  *
5  * Copyright 2019 NXP
6  */
7
8 #include <asm/io.h>
9 #include <common.h>
10 #include <dm/device.h>
11 #include <dm/fdtaddr.h>
12 #include <dm/read.h>
13 #include <linux/bitops.h>
14 #include <watchdog.h>
15 #include <wdt.h>
16
17 #define WDTLOAD                 0x000
18 #define WDTCONTROL              0x008
19 #define WDTINTCLR               0x00C
20 #define WDTLOCK                 0xC00
21
22 #define TIME_OUT_MIN_MSECS      1
23 #define TIME_OUT_MAX_MSECS      120000
24 #define SYS_FSL_WDT_CLK_DIV     16
25 #define INT_ENABLE              BIT(0)
26 #define RESET_ENABLE            BIT(1)
27 #define DISABLE                 0
28 #define UNLOCK                  0x1ACCE551
29 #define LOCK                    0x00000001
30 #define INT_MASK                BIT(0)
31
32 DECLARE_GLOBAL_DATA_PTR;
33
34 struct sp805_wdt_priv {
35         void __iomem *reg;
36 };
37
38 static int sp805_wdt_reset(struct udevice *dev)
39 {
40         struct sp805_wdt_priv *priv = dev_get_priv(dev);
41
42         writel(UNLOCK, priv->reg + WDTLOCK);
43         writel(INT_MASK, priv->reg + WDTINTCLR);
44         writel(LOCK, priv->reg + WDTLOCK);
45         readl(priv->reg + WDTLOCK);
46
47         return 0;
48 }
49
50 static int sp805_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
51 {
52         u32 load_value;
53         u32 load_time;
54         struct sp805_wdt_priv *priv = dev_get_priv(dev);
55
56         load_time = (u32)timeout;
57         if (timeout < TIME_OUT_MIN_MSECS)
58                 load_time = TIME_OUT_MIN_MSECS;
59         else if (timeout > TIME_OUT_MAX_MSECS)
60                 load_time = TIME_OUT_MAX_MSECS;
61         /* sp805 runs counter with given value twice, so when the max timeout is
62          * set 120s, the gd->bus_clk is less than 1145MHz, the load_value will
63          * not overflow.
64          */
65         load_value = (gd->bus_clk) /
66                 (2 * 1000 * SYS_FSL_WDT_CLK_DIV) * load_time;
67
68         writel(UNLOCK, priv->reg + WDTLOCK);
69         writel(load_value, priv->reg + WDTLOAD);
70         writel(INT_MASK, priv->reg + WDTINTCLR);
71         writel(INT_ENABLE | RESET_ENABLE, priv->reg + WDTCONTROL);
72         writel(LOCK, priv->reg + WDTLOCK);
73         readl(priv->reg + WDTLOCK);
74
75         return 0;
76 }
77
78 static int sp805_wdt_stop(struct udevice *dev)
79 {
80         struct sp805_wdt_priv *priv = dev_get_priv(dev);
81
82         writel(UNLOCK, priv->reg + WDTLOCK);
83         writel(DISABLE, priv->reg + WDTCONTROL);
84         writel(LOCK, priv->reg + WDTLOCK);
85         readl(priv->reg + WDTLOCK);
86
87         return 0;
88 }
89
90 static int sp805_wdt_probe(struct udevice *dev)
91 {
92         debug("%s: Probing wdt%u\n", __func__, dev->seq);
93
94         return 0;
95 }
96
97 static int sp805_wdt_ofdata_to_platdata(struct udevice *dev)
98 {
99         struct sp805_wdt_priv *priv = dev_get_priv(dev);
100
101         priv->reg = (void __iomem *)dev_read_addr(dev);
102         if (IS_ERR(priv->reg))
103                 return PTR_ERR(priv->reg);
104
105         return 0;
106 }
107
108 static const struct wdt_ops sp805_wdt_ops = {
109         .start = sp805_wdt_start,
110         .reset = sp805_wdt_reset,
111         .stop = sp805_wdt_stop,
112 };
113
114 static const struct udevice_id sp805_wdt_ids[] = {
115         { .compatible = "arm,sp805-wdt" },
116         {}
117 };
118
119 U_BOOT_DRIVER(sp805_wdt) = {
120         .name = "sp805_wdt",
121         .id = UCLASS_WDT,
122         .of_match = sp805_wdt_ids,
123         .probe = sp805_wdt_probe,
124         .priv_auto_alloc_size = sizeof(struct sp805_wdt_priv),
125         .ofdata_to_platdata = sp805_wdt_ofdata_to_platdata,
126         .ops = &sp805_wdt_ops,
127 };