1 /* wdt_xec.c - Microchip XEC watchdog driver */
2 
3 #define DT_DRV_COMPAT microchip_xec_watchdog
4 
5 /*
6  * Copyright (c) 2019 Intel Corporation.
7  *
8  * SPDX-License-Identifier: Apache-2.0
9  */
10 
11 #include <zephyr/irq.h>
12 #define LOG_LEVEL CONFIG_WDT_LOG_LEVEL
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(wdt_mchp_xec);
15 
16 #include <zephyr/drivers/watchdog.h>
17 #include <soc.h>
18 #include <errno.h>
19 
20 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
21 	     "add exactly one wdog node to the devicetree");
22 
23 struct wdt_xec_config {
24 	struct wdt_regs *regs;
25 	uint8_t girq;
26 	uint8_t girq_pos;
27 };
28 
29 struct wdt_xec_data {
30 	wdt_callback_t cb;
31 	bool timeout_installed;
32 };
33 
wdt_xec_setup(const struct device * dev,uint8_t options)34 static int wdt_xec_setup(const struct device *dev, uint8_t options)
35 {
36 	struct wdt_xec_config const *cfg = dev->config;
37 	struct wdt_xec_data *data = dev->data;
38 	struct wdt_regs *regs = cfg->regs;
39 
40 	if (regs->CTRL & MCHP_WDT_CTRL_EN) {
41 		return -EBUSY;
42 	}
43 
44 	if (!data->timeout_installed) {
45 		LOG_ERR("No valid WDT timeout installed");
46 		return -EINVAL;
47 	}
48 
49 	if (options & WDT_OPT_PAUSE_IN_SLEEP) {
50 		LOG_WRN("WDT_OPT_PAUSE_IN_SLEEP is not supported");
51 		return -ENOTSUP;
52 	}
53 
54 	if (options & WDT_OPT_PAUSE_HALTED_BY_DBG) {
55 		regs->CTRL |= MCHP_WDT_CTRL_JTAG_STALL_EN;
56 	} else {
57 		regs->CTRL &= ~MCHP_WDT_CTRL_JTAG_STALL_EN;
58 	}
59 
60 	regs->CTRL |= MCHP_WDT_CTRL_EN;
61 
62 	LOG_DBG("WDT Setup and enabled");
63 
64 	return 0;
65 }
66 
wdt_xec_disable(const struct device * dev)67 static int wdt_xec_disable(const struct device *dev)
68 {
69 	struct wdt_xec_config const *cfg = dev->config;
70 	struct wdt_xec_data *data = dev->data;
71 	struct wdt_regs *regs = cfg->regs;
72 
73 	if (!(regs->CTRL & MCHP_WDT_CTRL_EN)) {
74 		return -EALREADY;
75 	}
76 
77 	regs->CTRL &= ~MCHP_WDT_CTRL_EN;
78 	data->timeout_installed = false;
79 
80 	LOG_DBG("WDT Disabled");
81 
82 	return 0;
83 }
84 
wdt_xec_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * config)85 static int wdt_xec_install_timeout(const struct device *dev,
86 				   const struct wdt_timeout_cfg *config)
87 {
88 	struct wdt_xec_config const *cfg = dev->config;
89 	struct wdt_xec_data *data = dev->data;
90 	struct wdt_regs *regs = cfg->regs;
91 
92 	if (regs->CTRL & MCHP_WDT_CTRL_EN) {
93 		return -EBUSY;
94 	}
95 
96 	if (config->window.min > 0U) {
97 		data->timeout_installed = false;
98 		return -EINVAL;
99 	}
100 
101 	regs->LOAD = 0;
102 
103 	data->cb = config->callback;
104 	if (data->cb) {
105 		regs->CTRL |= MCHP_WDT_CTRL_MODE_IRQ;
106 		regs->IEN |= MCHP_WDT_IEN_EVENT_IRQ_EN;
107 
108 		LOG_DBG("WDT callback enabled");
109 	} else {
110 		/* Setting WDT_FLAG_RESET_SOC or not will have no effect:
111 		 * even after the cb, if anything is done, SoC will reset
112 		 */
113 		regs->CTRL &= ~MCHP_WDT_CTRL_MODE_IRQ;
114 		regs->IEN &= ~MCHP_WDT_IEN_EVENT_IRQ_EN;
115 
116 		LOG_DBG("WDT Reset enabled");
117 	}
118 
119 	/* Since it almost takes 1ms to decrement the load register
120 	 * (See datasheet 18.6.1.4: 33/32.768 KHz = 1.007ms)
121 	 * Let's use the given window directly.
122 	 */
123 	regs->LOAD = config->window.max;
124 
125 	data->timeout_installed = true;
126 
127 	return 0;
128 }
129 
wdt_xec_feed(const struct device * dev,int channel_id)130 static int wdt_xec_feed(const struct device *dev, int channel_id)
131 {
132 	struct wdt_xec_config const *cfg = dev->config;
133 	struct wdt_regs *regs = cfg->regs;
134 
135 	ARG_UNUSED(dev);
136 	ARG_UNUSED(channel_id);
137 
138 	if (!(regs->CTRL & MCHP_WDT_CTRL_EN)) {
139 		return -EINVAL;
140 	}
141 
142 	LOG_DBG("WDT Kicking");
143 
144 	regs->KICK = 1;
145 
146 	return 0;
147 }
148 
wdt_xec_isr(const struct device * dev)149 static void wdt_xec_isr(const struct device *dev)
150 {
151 	struct wdt_xec_config const *cfg = dev->config;
152 	struct wdt_xec_data *data = dev->data;
153 	struct wdt_regs *regs = cfg->regs;
154 
155 	LOG_DBG("WDT ISR");
156 
157 	if (data->cb) {
158 		data->cb(dev, 0);
159 	}
160 
161 #ifdef CONFIG_SOC_SERIES_MEC172X
162 	mchp_soc_ecia_girq_src_clr(cfg->girq, cfg->girq_pos);
163 #else
164 	MCHP_GIRQ_SRC(MCHP_WDT_GIRQ) = BIT(cfg->girq_pos);
165 #endif
166 	regs->IEN &= ~MCHP_WDT_IEN_EVENT_IRQ_EN;
167 }
168 
169 static const struct wdt_driver_api wdt_xec_api = {
170 	.setup = wdt_xec_setup,
171 	.disable = wdt_xec_disable,
172 	.install_timeout = wdt_xec_install_timeout,
173 	.feed = wdt_xec_feed,
174 };
175 
wdt_xec_init(const struct device * dev)176 static int wdt_xec_init(const struct device *dev)
177 {
178 	struct wdt_xec_config const *cfg = dev->config;
179 
180 	if (IS_ENABLED(CONFIG_WDT_DISABLE_AT_BOOT)) {
181 		wdt_xec_disable(dev);
182 	}
183 
184 #ifdef CONFIG_SOC_SERIES_MEC172X
185 	mchp_soc_ecia_girq_src_en(cfg->girq, cfg->girq_pos);
186 #else
187 	MCHP_GIRQ_ENSET(MCHP_WDT_GIRQ) = BIT(cfg->girq_pos);
188 #endif
189 
190 	IRQ_CONNECT(DT_INST_IRQN(0),
191 		    DT_INST_IRQ(0, priority),
192 		    wdt_xec_isr, DEVICE_DT_INST_GET(0), 0);
193 	irq_enable(DT_INST_IRQN(0));
194 
195 	return 0;
196 }
197 
198 static const struct wdt_xec_config wdt_xec_config_0 = {
199 	.regs = (struct wdt_regs *)(DT_INST_REG_ADDR(0)),
200 	.girq = DT_INST_PROP_BY_IDX(0, girqs, 0),
201 	.girq_pos = DT_INST_PROP_BY_IDX(0, girqs, 1),
202 };
203 
204 static struct wdt_xec_data wdt_xec_dev_data;
205 
206 DEVICE_DT_INST_DEFINE(0, wdt_xec_init, NULL,
207 		    &wdt_xec_dev_data, &wdt_xec_config_0,
208 		    PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
209 		    &wdt_xec_api);
210