1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
|
// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright (C) 2016 Freescale Semiconductor, Inc.
*/
#include <common.h>
#include <init.h>
#include <io.h>
#include <of.h>
#include <errno.h>
#include <malloc.h>
#include <restart.h>
#include <watchdog.h>
#include <reset_source.h>
#include <linux/clk.h>
#include <asm/system.h>
struct imxulp_socdata {
bool prescaler_enable;
unsigned int rate;
};
struct imxulp_wd {
struct watchdog wd;
void __iomem *base;
bool prescaler_enable;
struct device *dev;
const struct imxulp_socdata *socdata;
};
#define REFRESH_WORD0 0xA602 /* 1st refresh word */
#define REFRESH_WORD1 0xB480 /* 2nd refresh word */
#define UNLOCK_WORD0 0xC520 /* 1st unlock word */
#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */
#define UNLOCK_WORD 0xD928C520 /* unlock word */
#define REFRESH_WORD 0xB480A602 /* refresh word */
#define WDOG_CS 0x0
#define WDOG_CS_UPDATE BIT(5)
#define WDOG_CS_EN BIT(7)
#define WDOG_CS_RCS BIT(10)
#define WDOG_CS_ULK BIT(11)
#define WDOG_CS_PRES BIT(12)
#define WDOG_CS_CMD32EN BIT(13)
#define WDOG_CS_FLG BIT(14)
#define WDOG_CS_INT BIT(6)
#define WDOG_CS_LPO_CLK (0x1 << 8)
#define WDOG_CNT 0x4
#define WDOG_TOVAL 0x8
#define CLK_RATE_1KHZ 1000
#define CLK_RATE_32KHZ 128
static int imxulp_watchdog_set_timeout(struct watchdog *wd, unsigned int timeout)
{
struct imxulp_wd *imxwd = container_of(wd, struct imxulp_wd, wd);
u32 cmd32 = 0;
if (timeout == 0)
return -ENOSYS;
if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
writel(UNLOCK_WORD, imxwd->base + WDOG_CNT);
cmd32 = WDOG_CS_CMD32EN;
} else {
writel(UNLOCK_WORD0, imxwd->base + WDOG_CNT);
writel(UNLOCK_WORD1, imxwd->base + WDOG_CNT);
}
/* Wait WDOG Unlock */
while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_ULK))
;
writel(timeout * imxwd->socdata->rate, imxwd->base + WDOG_TOVAL);
if (imxwd->socdata->prescaler_enable)
cmd32 |= WDOG_CS_PRES;
writel(cmd32 | WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_LPO_CLK |
WDOG_CS_FLG | WDOG_CS_INT, imxwd->base + WDOG_CS);
/* Wait WDOG reconfiguration */
while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_RCS))
;
if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
writel(REFRESH_WORD, imxwd->base + WDOG_CNT);
} else {
writel(REFRESH_WORD0, imxwd->base + WDOG_CNT);
writel(REFRESH_WORD1, imxwd->base + WDOG_CNT);
}
return 0;
}
static enum wdog_hw_runnning imxulp_wd_running(struct imxulp_wd *imxwd)
{
if (readl(imxwd->base + WDOG_CS) & WDOG_CS_EN)
return WDOG_HW_RUNNING;
else
return WDOG_HW_NOT_RUNNING;
}
static int imxulp_wd_probe(struct device *dev)
{
struct imxulp_wd *imxwd;
struct resource *iores;
struct imxulp_socdata *socdata;
int ret;
ret = dev_get_drvdata(dev, (const void **)&socdata);
if (ret)
return ret;
imxwd = xzalloc(sizeof(*imxwd));
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores))
return dev_err_probe(dev, PTR_ERR(iores), "could not get memory region\n");
imxwd->socdata = socdata;
imxwd->base = IOMEM(iores->start);
imxwd->wd.set_timeout = imxulp_watchdog_set_timeout;
imxwd->wd.timeout_max = 0xffff / imxwd->socdata->rate;
imxwd->wd.hwdev = dev;
imxwd->wd.running = imxulp_wd_running(imxwd);
ret = watchdog_register(&imxwd->wd);
if (ret)
return dev_err_probe(dev, ret, "Failed to register watchdog device\n");
return 0;
}
static struct imxulp_socdata imx7ulp_wd_socdata = {
.prescaler_enable = false,
.rate = CLK_RATE_1KHZ,
};
static struct imxulp_socdata imx93_wd_socdata = {
.prescaler_enable = true,
.rate = CLK_RATE_32KHZ,
};
static __maybe_unused struct of_device_id imxulp_wdt_dt_ids[] = {
{
.compatible = "fsl,imx7ulp-wdt",
.data = &imx7ulp_wd_socdata,
}, {
.compatible = "fsl,imx8ulp-wdt",
.data = &imx7ulp_wd_socdata,
}, {
.compatible = "fsl,imx93-wdt",
.data = &imx93_wd_socdata,
}, {
/* sentinel */
}
};
MODULE_DEVICE_TABLE(of, imx_wdt_dt_ids);
static struct driver imxulp_wd_driver = {
.name = "imxulp-watchdog",
.probe = imxulp_wd_probe,
.of_compatible = DRV_OF_COMPAT(imxulp_wdt_dt_ids),
};
device_platform_driver(imxulp_wd_driver);
|