1 /*
2  * Copyright (c) 2020 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_npcx_miwu
8 
9 /**
10  * @file
11  * @brief Nuvoton NPCX MIWU driver
12  *
13  * The device Multi-Input Wake-Up Unit (MIWU) supports the Nuvoton embedded
14  * controller (EC) to exit 'Sleep' or 'Deep Sleep' power state which allows chip
15  * has better power consumption. Also, it provides signal conditioning such as
16  * 'Level' and 'Edge' trigger type and grouping of external interrupt sources
17  * of NVIC. The NPCX series has three identical MIWU modules: MIWU0, MIWU1,
18  * MIWU2. Together, they support a total of over 140 internal and/or external
19  * wake-up input (WUI) sources.
20  *
21  * This driver uses device tree files to present the relationship between
22  * MIWU and the other devices in different npcx series. For npcx7 series,
23  * it include:
24  *  1. npcxn-miwus-wui-map.dtsi: it presents relationship between wake-up inputs
25  *     (WUI) and its source device such as gpio, timer, eSPI VWs and so on.
26  *  2. npcxn-miwus-int-map.dtsi: it presents relationship between MIWU group
27  *     and NVIC interrupt in npcx series. Please notice it isn't 1-to-1 mapping.
28  *     For example, here is the mapping between miwu0's group a & d and IRQ7:
29  *
30  *     map_miwu0_groups: {
31  *         parent = <&miwu0>;
32  *         group_ad0: group_ad0_map {
33  *             irq        = <7>;
34  *             group_mask = <0x09>;
35  *         };
36  *         ...
37  *     };
38  *
39  *     It will connect IRQ 7 and intc_miwu_isr0() with the argument, group_mask,
40  *     by IRQ_CONNECT() during driver initialization function. With group_mask,
41  *     0x09, the driver checks the pending bits of group a and group d in ISR.
42  *     Then it will execute related callback functions if they have been
43  *     registered properly.
44  *
45  * INCLUDE FILES: soc_miwu.h
46  *
47  */
48 
49 #include <zephyr/device.h>
50 #include <zephyr/kernel.h>
51 #include <soc.h>
52 #include <zephyr/sys/__assert.h>
53 #include <zephyr/irq_nextlevel.h>
54 #include <zephyr/drivers/gpio.h>
55 
56 #include "soc_miwu.h"
57 #include "soc_gpio.h"
58 
59 #include <zephyr/logging/log.h>
60 #include <zephyr/irq.h>
61 LOG_MODULE_REGISTER(intc_miwu, LOG_LEVEL_ERR);
62 
63 /* MIWU module instances */
64 #define NPCX_MIWU_DEV(inst) DEVICE_DT_INST_GET(inst),
65 
66 static const struct device *const miwu_devs[] = {
67 	DT_INST_FOREACH_STATUS_OKAY(NPCX_MIWU_DEV)
68 };
69 
70 BUILD_ASSERT(ARRAY_SIZE(miwu_devs) == NPCX_MIWU_TABLE_COUNT,
71 		"Size of miwu_devs array must equal to NPCX_MIWU_TABLE_COUNT");
72 
73 /* Driver config */
74 struct intc_miwu_config {
75 	/* miwu controller base address */
76 	uintptr_t base;
77 	/* index of miwu controller */
78 	uint8_t index;
79 };
80 
81 /* Driver data */
82 struct intc_miwu_data {
83 	/* Callback functions list for each MIWU group */
84 	sys_slist_t cb_list_grp[8];
85 };
86 
87 BUILD_ASSERT(sizeof(struct miwu_io_params) == sizeof(gpio_port_pins_t),
88 	"Size of struct miwu_io_params must equal to struct gpio_port_pins_t");
89 
90 BUILD_ASSERT(offsetof(struct miwu_callback, io_cb.params) +
91 	sizeof(struct miwu_io_params) == sizeof(struct gpio_callback),
92 	"Failed in size check of miwu_callback and gpio_callback structures!");
93 
94 BUILD_ASSERT(offsetof(struct miwu_callback, io_cb.params.cb_type) ==
95 	offsetof(struct miwu_callback, dev_cb.params.cb_type),
96 	"Failed in offset check of cb_type field of miwu_callback structure");
97 
98 /* MIWU local functions */
intc_miwu_dispatch_isr(sys_slist_t * cb_list,uint8_t mask)99 static void intc_miwu_dispatch_isr(sys_slist_t *cb_list, uint8_t mask)
100 {
101 	struct miwu_callback *cb, *tmp;
102 
103 	SYS_SLIST_FOR_EACH_CONTAINER_SAFE(cb_list, cb, tmp, node) {
104 
105 		if (cb->io_cb.params.cb_type == NPCX_MIWU_CALLBACK_GPIO) {
106 			if (BIT(cb->io_cb.params.wui.bit) & mask) {
107 				__ASSERT(cb->io_cb.handler, "No GPIO callback handler!");
108 				cb->io_cb.handler(
109 					npcx_get_gpio_dev(cb->io_cb.params.gpio_port),
110 					(struct gpio_callback *)cb,
111 					cb->io_cb.params.pin_mask);
112 			}
113 		} else {
114 			if (BIT(cb->dev_cb.params.wui.bit) & mask) {
115 				__ASSERT(cb->dev_cb.handler, "No device callback handler!");
116 
117 				cb->dev_cb.handler(cb->dev_cb.params.source,
118 						   &cb->dev_cb.params.wui);
119 			}
120 		}
121 	}
122 }
123 
intc_miwu_isr_pri(int wui_table,int wui_group)124 static void intc_miwu_isr_pri(int wui_table, int wui_group)
125 {
126 	const struct intc_miwu_config *config = miwu_devs[wui_table]->config;
127 	struct intc_miwu_data *data = miwu_devs[wui_table]->data;
128 	const uint32_t base = config->base;
129 	uint8_t mask = NPCX_WKPND(base, wui_group) & NPCX_WKEN(base, wui_group);
130 
131 	/* Clear pending bits before dispatch ISR */
132 	if (mask) {
133 		NPCX_WKPCL(base, wui_group) = mask;
134 	}
135 
136 	/* Dispatch registered gpio isrs */
137 	intc_miwu_dispatch_isr(&data->cb_list_grp[wui_group], mask);
138 }
139 
140 /* Platform specific MIWU functions */
npcx_miwu_irq_enable(const struct npcx_wui * wui)141 void npcx_miwu_irq_enable(const struct npcx_wui *wui)
142 {
143 	const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
144 	const uint32_t base = config->base;
145 
146 	NPCX_WKEN(base, wui->group) |= BIT(wui->bit);
147 }
148 
npcx_miwu_irq_disable(const struct npcx_wui * wui)149 void npcx_miwu_irq_disable(const struct npcx_wui *wui)
150 {
151 	const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
152 	const uint32_t base = config->base;
153 
154 	NPCX_WKEN(base, wui->group) &= ~BIT(wui->bit);
155 }
156 
npcx_miwu_io_enable(const struct npcx_wui * wui)157 void npcx_miwu_io_enable(const struct npcx_wui *wui)
158 {
159 	const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
160 	const uint32_t base = config->base;
161 
162 	NPCX_WKINEN(base, wui->group) |= BIT(wui->bit);
163 }
164 
npcx_miwu_io_disable(const struct npcx_wui * wui)165 void npcx_miwu_io_disable(const struct npcx_wui *wui)
166 {
167 	const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
168 	const uint32_t base = config->base;
169 
170 	NPCX_WKINEN(base, wui->group) &= ~BIT(wui->bit);
171 }
172 
npcx_miwu_irq_get_state(const struct npcx_wui * wui)173 bool npcx_miwu_irq_get_state(const struct npcx_wui *wui)
174 {
175 	const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
176 	const uint32_t base = config->base;
177 
178 	return IS_BIT_SET(NPCX_WKEN(base, wui->group), wui->bit);
179 }
180 
npcx_miwu_irq_get_and_clear_pending(const struct npcx_wui * wui)181 bool npcx_miwu_irq_get_and_clear_pending(const struct npcx_wui *wui)
182 {
183 	const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
184 	const uint32_t base = config->base;
185 	bool pending = IS_BIT_SET(NPCX_WKPND(base, wui->group), wui->bit);
186 
187 	if (pending) {
188 		NPCX_WKPCL(base, wui->group) = BIT(wui->bit);
189 	}
190 
191 	return pending;
192 }
193 
npcx_miwu_interrupt_configure(const struct npcx_wui * wui,enum miwu_int_mode mode,enum miwu_int_trig trig)194 int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
195 		enum miwu_int_mode mode, enum miwu_int_trig trig)
196 {
197 	const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
198 	const uint32_t base = config->base;
199 	uint8_t pmask = BIT(wui->bit);
200 
201 	/* Disable interrupt of wake-up input source before configuring it */
202 	npcx_miwu_irq_disable(wui);
203 
204 	/* Handle interrupt for level trigger */
205 	if (mode == NPCX_MIWU_MODE_LEVEL) {
206 		/* Set detection mode to level */
207 		NPCX_WKMOD(base, wui->group) |= pmask;
208 		switch (trig) {
209 		/* Enable interrupting on level high */
210 		case NPCX_MIWU_TRIG_HIGH:
211 			NPCX_WKEDG(base, wui->group) &= ~pmask;
212 			break;
213 		/* Enable interrupting on level low */
214 		case NPCX_MIWU_TRIG_LOW:
215 			NPCX_WKEDG(base, wui->group) |= pmask;
216 			break;
217 		default:
218 			return -EINVAL;
219 		}
220 	/* Handle interrupt for edge trigger */
221 	} else {
222 		/* Set detection mode to edge */
223 		NPCX_WKMOD(base, wui->group) &= ~pmask;
224 		switch (trig) {
225 		/* Handle interrupting on falling edge */
226 		case NPCX_MIWU_TRIG_LOW:
227 			NPCX_WKAEDG(base, wui->group) &= ~pmask;
228 			NPCX_WKEDG(base, wui->group) |= pmask;
229 			break;
230 		/* Handle interrupting on rising edge */
231 		case NPCX_MIWU_TRIG_HIGH:
232 			NPCX_WKAEDG(base, wui->group) &= ~pmask;
233 			NPCX_WKEDG(base, wui->group) &= ~pmask;
234 			break;
235 		/* Handle interrupting on both edges */
236 		case NPCX_MIWU_TRIG_BOTH:
237 			/* Enable any edge */
238 			NPCX_WKAEDG(base, wui->group) |= pmask;
239 			break;
240 		default:
241 			return -EINVAL;
242 		}
243 	}
244 
245 	/* Enable wake-up input sources */
246 	NPCX_WKINEN(base, wui->group) |= pmask;
247 
248 	/*
249 	 * Clear pending bit since it might be set if WKINEN bit is
250 	 * changed.
251 	 */
252 	NPCX_WKPCL(base, wui->group) |= pmask;
253 
254 	return 0;
255 }
256 
npcx_miwu_init_gpio_callback(struct miwu_callback * callback,const struct npcx_wui * io_wui,int port)257 void npcx_miwu_init_gpio_callback(struct miwu_callback *callback,
258 				const struct npcx_wui *io_wui, int port)
259 {
260 	/* Initialize WUI and GPIO settings in unused bits field */
261 	callback->io_cb.params.wui.table = io_wui->table;
262 	callback->io_cb.params.wui.bit   = io_wui->bit;
263 	callback->io_cb.params.gpio_port = port;
264 	callback->io_cb.params.cb_type = NPCX_MIWU_CALLBACK_GPIO;
265 	callback->io_cb.params.wui.group = io_wui->group;
266 }
267 
npcx_miwu_init_dev_callback(struct miwu_callback * callback,const struct npcx_wui * dev_wui,miwu_dev_callback_handler_t handler,const struct device * source)268 void npcx_miwu_init_dev_callback(struct miwu_callback *callback,
269 				const struct npcx_wui *dev_wui,
270 				miwu_dev_callback_handler_t handler,
271 				const struct device *source)
272 {
273 	/* Initialize WUI and input device settings */
274 	callback->dev_cb.params.wui.table = dev_wui->table;
275 	callback->dev_cb.params.wui.group = dev_wui->group;
276 	callback->dev_cb.params.wui.bit   = dev_wui->bit;
277 	callback->dev_cb.params.source = source;
278 	callback->dev_cb.params.cb_type = NPCX_MIWU_CALLBACK_DEV;
279 	callback->dev_cb.handler = handler;
280 }
281 
npcx_miwu_manage_callback(struct miwu_callback * cb,bool set)282 int npcx_miwu_manage_callback(struct miwu_callback *cb, bool set)
283 {
284 	struct npcx_wui *wui;
285 	struct intc_miwu_data *data;
286 	sys_slist_t *cb_list;
287 
288 	if (cb->io_cb.params.cb_type == NPCX_MIWU_CALLBACK_GPIO) {
289 		wui = &cb->io_cb.params.wui;
290 	} else {
291 		wui = &cb->dev_cb.params.wui;
292 	}
293 
294 	data = miwu_devs[wui->table]->data;
295 	cb_list = &data->cb_list_grp[wui->group];
296 	if (!sys_slist_is_empty(cb_list)) {
297 		if (!sys_slist_find_and_remove(cb_list, &cb->node)) {
298 			if (!set) {
299 				return -EINVAL;
300 			}
301 		}
302 	}
303 
304 	if (set) {
305 		sys_slist_prepend(cb_list, &cb->node);
306 	}
307 
308 	return 0;
309 }
310 
311 /* MIWU driver registration */
312 #define NPCX_MIWU_ISR_FUNC(index) _CONCAT(intc_miwu_isr, index)
313 #define NPCX_MIWU_INIT_FUNC(inst) _CONCAT(intc_miwu_init, inst)
314 #define NPCX_MIWU_INIT_FUNC_DECL(inst) \
315 	static int intc_miwu_init##inst(const struct device *dev)
316 
317 /* MIWU ISR implementation */
318 #define NPCX_MIWU_ISR_FUNC_IMPL(inst)                                          \
319 	static void intc_miwu_isr##inst(void *arg)                             \
320 	{                                                                      \
321 		uint8_t grp_mask = (uint32_t)arg;                              \
322 		int group = 0;                                                 \
323 									       \
324 		/* Check all MIWU groups belong to the same irq */             \
325 		do {                                                           \
326 			if (grp_mask & 0x01)                                   \
327 				intc_miwu_isr_pri(inst, group);                \
328 			group++;                                               \
329 			grp_mask = grp_mask >> 1;                              \
330 									       \
331 		} while (grp_mask != 0);                                       \
332 	}
333 
334 /* MIWU init function implementation */
335 #define NPCX_MIWU_INIT_FUNC_IMPL(inst)                                         \
336 	static int intc_miwu_init##inst(const struct device *dev)              \
337 	{                                                                      \
338 		int i;                                                         \
339 		const struct intc_miwu_config *config = dev->config;           \
340 		const uint32_t base = config->base;                            \
341 									       \
342 		/* Clear all MIWUs' pending and enable bits of MIWU device */  \
343 		for (i = 0; i < NPCX_MIWU_GROUP_COUNT; i++) {                  \
344 			NPCX_WKEN(base, i) = 0;                                \
345 			NPCX_WKPCL(base, i) = 0xFF;                            \
346 		}                                                              \
347 									       \
348 		/* Config IRQ and MWIU group directly */                       \
349 		DT_FOREACH_CHILD(NPCX_DT_NODE_FROM_MIWU_MAP(inst),             \
350 			NPCX_DT_MIWU_IRQ_CONNECT_IMPL_CHILD_FUNC)              \
351 		return 0;                                                      \
352 	}                                                                      \
353 
354 #define NPCX_MIWU_INIT(inst)                                                   \
355 	NPCX_MIWU_INIT_FUNC_DECL(inst);                                        \
356 									       \
357 	static const struct intc_miwu_config miwu_config_##inst = {	       \
358 		.base = DT_REG_ADDR(DT_NODELABEL(miwu##inst)),                 \
359 		.index = DT_PROP(DT_NODELABEL(miwu##inst), index),             \
360 	};                                                                     \
361 	struct intc_miwu_data miwu_data_##inst;				       \
362 									       \
363 	DEVICE_DT_INST_DEFINE(inst,					       \
364 			    NPCX_MIWU_INIT_FUNC(inst),                         \
365 			    NULL,					       \
366 			    &miwu_data_##inst, &miwu_config_##inst,            \
367 			    PRE_KERNEL_1,                                      \
368 			    CONFIG_INTC_INIT_PRIORITY, NULL);                  \
369 									       \
370 	NPCX_MIWU_ISR_FUNC_IMPL(inst)                                          \
371 									       \
372 	NPCX_MIWU_INIT_FUNC_IMPL(inst)
373 
374 DT_INST_FOREACH_STATUS_OKAY(NPCX_MIWU_INIT)
375