/
wdt_mchp_xec.c
209 lines (160 loc) · 4.52 KB
/
wdt_mchp_xec.c
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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
/* wdt_xec.c - Microchip XEC watchdog driver */
#define DT_DRV_COMPAT microchip_xec_watchdog
/*
* Copyright (c) 2019 Intel Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/irq.h>
#define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(wdt_mchp_xec);
#include <zephyr/drivers/watchdog.h>
#include <soc.h>
#include <errno.h>
BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
"add exactly one wdog node to the devicetree");
struct wdt_xec_config {
struct wdt_regs *regs;
uint8_t girq;
uint8_t girq_pos;
};
struct wdt_xec_data {
wdt_callback_t cb;
bool timeout_installed;
};
static int wdt_xec_setup(const struct device *dev, uint8_t options)
{
struct wdt_xec_config const *cfg = dev->config;
struct wdt_xec_data *data = dev->data;
struct wdt_regs *regs = cfg->regs;
if (regs->CTRL & MCHP_WDT_CTRL_EN) {
return -EBUSY;
}
if (!data->timeout_installed) {
LOG_ERR("No valid WDT timeout installed");
return -EINVAL;
}
if (options & WDT_OPT_PAUSE_IN_SLEEP) {
LOG_WRN("WDT_OPT_PAUSE_IN_SLEEP is not supported");
return -ENOTSUP;
}
if (options & WDT_OPT_PAUSE_HALTED_BY_DBG) {
regs->CTRL |= MCHP_WDT_CTRL_JTAG_STALL_EN;
} else {
regs->CTRL &= ~MCHP_WDT_CTRL_JTAG_STALL_EN;
}
regs->CTRL |= MCHP_WDT_CTRL_EN;
LOG_DBG("WDT Setup and enabled");
return 0;
}
static int wdt_xec_disable(const struct device *dev)
{
struct wdt_xec_config const *cfg = dev->config;
struct wdt_xec_data *data = dev->data;
struct wdt_regs *regs = cfg->regs;
if (!(regs->CTRL & MCHP_WDT_CTRL_EN)) {
return -EALREADY;
}
regs->CTRL &= ~MCHP_WDT_CTRL_EN;
data->timeout_installed = false;
LOG_DBG("WDT Disabled");
return 0;
}
static int wdt_xec_install_timeout(const struct device *dev,
const struct wdt_timeout_cfg *config)
{
struct wdt_xec_config const *cfg = dev->config;
struct wdt_xec_data *data = dev->data;
struct wdt_regs *regs = cfg->regs;
if (regs->CTRL & MCHP_WDT_CTRL_EN) {
return -EBUSY;
}
if (config->window.min > 0U) {
data->timeout_installed = false;
return -EINVAL;
}
regs->LOAD = 0;
data->cb = config->callback;
if (data->cb) {
regs->CTRL |= MCHP_WDT_CTRL_MODE_IRQ;
regs->IEN |= MCHP_WDT_IEN_EVENT_IRQ_EN;
LOG_DBG("WDT callback enabled");
} else {
/* Setting WDT_FLAG_RESET_SOC or not will have no effect:
* even after the cb, if anything is done, SoC will reset
*/
regs->CTRL &= ~MCHP_WDT_CTRL_MODE_IRQ;
regs->IEN &= ~MCHP_WDT_IEN_EVENT_IRQ_EN;
LOG_DBG("WDT Reset enabled");
}
/* Since it almost takes 1ms to decrement the load register
* (See datasheet 18.6.1.4: 33/32.768 KHz = 1.007ms)
* Let's use the given window directly.
*/
regs->LOAD = config->window.max;
data->timeout_installed = true;
return 0;
}
static int wdt_xec_feed(const struct device *dev, int channel_id)
{
struct wdt_xec_config const *cfg = dev->config;
struct wdt_regs *regs = cfg->regs;
ARG_UNUSED(dev);
ARG_UNUSED(channel_id);
if (!(regs->CTRL & MCHP_WDT_CTRL_EN)) {
return -EINVAL;
}
LOG_DBG("WDT Kicking");
regs->KICK = 1;
return 0;
}
static void wdt_xec_isr(const struct device *dev)
{
struct wdt_xec_config const *cfg = dev->config;
struct wdt_xec_data *data = dev->data;
struct wdt_regs *regs = cfg->regs;
LOG_DBG("WDT ISR");
if (data->cb) {
data->cb(dev, 0);
}
#ifdef CONFIG_SOC_SERIES_MEC172X
mchp_soc_ecia_girq_src_clr(cfg->girq, cfg->girq_pos);
#else
MCHP_GIRQ_SRC(MCHP_WDT_GIRQ) = BIT(cfg->girq_pos);
#endif
regs->IEN &= ~MCHP_WDT_IEN_EVENT_IRQ_EN;
}
static const struct wdt_driver_api wdt_xec_api = {
.setup = wdt_xec_setup,
.disable = wdt_xec_disable,
.install_timeout = wdt_xec_install_timeout,
.feed = wdt_xec_feed,
};
static int wdt_xec_init(const struct device *dev)
{
struct wdt_xec_config const *cfg = dev->config;
if (IS_ENABLED(CONFIG_WDT_DISABLE_AT_BOOT)) {
wdt_xec_disable(dev);
}
#ifdef CONFIG_SOC_SERIES_MEC172X
mchp_soc_ecia_girq_src_en(cfg->girq, cfg->girq_pos);
#else
MCHP_GIRQ_ENSET(MCHP_WDT_GIRQ) = BIT(cfg->girq_pos);
#endif
IRQ_CONNECT(DT_INST_IRQN(0),
DT_INST_IRQ(0, priority),
wdt_xec_isr, DEVICE_DT_INST_GET(0), 0);
irq_enable(DT_INST_IRQN(0));
return 0;
}
static const struct wdt_xec_config wdt_xec_config_0 = {
.regs = (struct wdt_regs *)(DT_INST_REG_ADDR(0)),
.girq = DT_INST_PROP_BY_IDX(0, girqs, 0),
.girq_pos = DT_INST_PROP_BY_IDX(0, girqs, 1),
};
static struct wdt_xec_data wdt_xec_dev_data;
DEVICE_DT_INST_DEFINE(0, wdt_xec_init, NULL,
&wdt_xec_dev_data, &wdt_xec_config_0,
PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
&wdt_xec_api);