blob: 1cf3f07d6b981bcfc77439a0029acdc962abadc8 [file] [log] [blame]
Mulin Chao84d90e42020-08-13 18:15:25 +08001/*
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 *
Nazar Kazakov9713f0d2022-02-24 12:00:55 +000021 * This driver uses device tree files to present the relationship between
Mulin Chao84d90e42020-08-13 18:15:25 +080022 * 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.
Mulin Chaoa8a217f2021-04-26 01:28:21 -070026 * 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.
Mulin Chao84d90e42020-08-13 18:15:25 +080028 * 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
Gerard Marull-Paretasfb60aab2022-05-06 10:25:46 +020049#include <zephyr/device.h>
50#include <zephyr/kernel.h>
Mulin Chao84d90e42020-08-13 18:15:25 +080051#include <soc.h>
Gerard Marull-Paretasfb60aab2022-05-06 10:25:46 +020052#include <zephyr/sys/__assert.h>
53#include <zephyr/irq_nextlevel.h>
54#include <zephyr/drivers/gpio.h>
Mulin Chao84d90e42020-08-13 18:15:25 +080055
56#include "soc_miwu.h"
Mulin Chao6deb68a2020-08-13 18:53:09 +080057#include "soc_gpio.h"
Mulin Chao84d90e42020-08-13 18:15:25 +080058
Gerard Marull-Paretasfb60aab2022-05-06 10:25:46 +020059#include <zephyr/logging/log.h>
Mulin Chao84d90e42020-08-13 18:15:25 +080060LOG_MODULE_REGISTER(intc_miwu, LOG_LEVEL_ERR);
61
62/* MIWU module instances forward declaration */
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +020063static const struct device *miwu_devs[];
Mulin Chao84d90e42020-08-13 18:15:25 +080064
65/* Driver config */
66struct intc_miwu_config {
67 /* miwu controller base address */
68 uintptr_t base;
69 /* index of miwu controller */
70 uint8_t index;
71};
72
73/* Callback functions list for GPIO wake-up inputs */
74sys_slist_t cb_list_gpio;
75
76/*
77 * Callback functions list for the generic hardware modules wake-up inputs
78 * such as timer, uart, i2c, host interface and so on.
79 */
80sys_slist_t cb_list_generic;
81
Mulin Chao84d90e42020-08-13 18:15:25 +080082BUILD_ASSERT(sizeof(struct miwu_io_callback) == sizeof(struct gpio_callback),
83 "Size of struct miwu_io_callback must equal to struct gpio_callback");
84
85BUILD_ASSERT(sizeof(struct miwu_io_params) == sizeof(gpio_port_pins_t),
86 "Size of struct miwu_io_params must equal to struct gpio_port_pins_t");
87
88/* MIWU local functions */
89static void intc_miwu_dispatch_gpio_isr(uint8_t wui_table,
90 uint8_t wui_group, uint8_t wui_bit)
91{
92 struct miwu_io_callback *cb, *tmp;
93
94 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&cb_list_gpio, cb, tmp, node) {
Mulin Chao6deb68a2020-08-13 18:53:09 +080095 /* Pending bit, group and table match the wui item in list */
96 if (cb->params.wui.table == wui_table
97 && cb->params.wui.group == wui_group
98 && cb->params.wui.bit == wui_bit) {
99 __ASSERT(cb->handler, "No GPIO callback handler!");
100 /*
101 * Execute GPIO callback and the other callback might
102 * match the same wui item.
103 */
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800104 cb->handler(npcx_get_gpio_dev(cb->params.gpio_port),
Mulin Chao6deb68a2020-08-13 18:53:09 +0800105 (struct gpio_callback *)cb,
106 cb->params.pin_mask);
107 }
Mulin Chao84d90e42020-08-13 18:15:25 +0800108 }
109}
110
111static void intc_miwu_dispatch_generic_isr(uint8_t wui_table,
112 uint8_t wui_group, uint8_t wui_bit)
113{
114 struct miwu_dev_callback *cb, *tmp;
115
116 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&cb_list_generic, cb, tmp, node) {
117 /* Pending bit, group and table match the wui item in list */
118 if (cb->wui.table == wui_table
119 && cb->wui.group == wui_group
120 && cb->wui.bit == wui_bit) {
121 __ASSERT(cb->handler, "No Generic callback handler!");
122 /*
123 * Execute generic callback and the other callback might
124 * match the same wui item.
125 */
126 cb->handler(cb->source, &cb->wui);
127 }
128 }
129}
130
131static void intc_miwu_isr_pri(int wui_table, int wui_group)
132{
133 int wui_bit;
Wealian Liao6d6c5e12022-01-21 15:24:08 +0800134 const struct intc_miwu_config *config = miwu_devs[wui_table]->config;
135 const uint32_t base = config->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800136 uint8_t mask = NPCX_WKPND(base, wui_group) & NPCX_WKEN(base, wui_group);
137
138 /* Clear pending bits before dispatch ISR */
Anas Nashif49b36ea2022-07-06 07:34:50 -0400139 if (mask) {
Mulin Chao84d90e42020-08-13 18:15:25 +0800140 NPCX_WKPCL(base, wui_group) = mask;
Anas Nashif49b36ea2022-07-06 07:34:50 -0400141 }
Mulin Chao84d90e42020-08-13 18:15:25 +0800142
143 for (wui_bit = 0; wui_bit < 8; wui_bit++) {
144 if (mask & BIT(wui_bit)) {
145 LOG_DBG("miwu_isr %d %d %d!\n", wui_table,
146 wui_group, wui_bit);
Nazar Kazakov9713f0d2022-02-24 12:00:55 +0000147 /* Dispatch registered gpio and generic isrs */
Mulin Chao84d90e42020-08-13 18:15:25 +0800148 intc_miwu_dispatch_gpio_isr(wui_table,
149 wui_group, wui_bit);
150 intc_miwu_dispatch_generic_isr(wui_table,
151 wui_group, wui_bit);
152 }
153 }
154}
155
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800156/* Platform specific MIWU functions */
157void npcx_miwu_irq_enable(const struct npcx_wui *wui)
Mulin Chao84d90e42020-08-13 18:15:25 +0800158{
Wealian Liao6d6c5e12022-01-21 15:24:08 +0800159 const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
160 const uint32_t base = config->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800161
162 NPCX_WKEN(base, wui->group) |= BIT(wui->bit);
163}
164
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800165void npcx_miwu_irq_disable(const struct npcx_wui *wui)
Mulin Chao84d90e42020-08-13 18:15:25 +0800166{
Wealian Liao6d6c5e12022-01-21 15:24:08 +0800167 const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
168 const uint32_t base = config->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800169
170 NPCX_WKEN(base, wui->group) &= ~BIT(wui->bit);
171}
172
Mulin Chao1cc73072021-04-22 01:45:23 -0700173void npcx_miwu_io_enable(const struct npcx_wui *wui)
174{
Wealian Liao6d6c5e12022-01-21 15:24:08 +0800175 const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
176 const uint32_t base = config->base;
Mulin Chao1cc73072021-04-22 01:45:23 -0700177
178 NPCX_WKINEN(base, wui->group) |= BIT(wui->bit);
179}
180
181void npcx_miwu_io_disable(const struct npcx_wui *wui)
182{
Wealian Liao6d6c5e12022-01-21 15:24:08 +0800183 const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
184 const uint32_t base = config->base;
Mulin Chao1cc73072021-04-22 01:45:23 -0700185
186 NPCX_WKINEN(base, wui->group) &= ~BIT(wui->bit);
187}
188
Mulin Chaoea00ff32021-02-04 18:09:20 -0800189bool npcx_miwu_irq_get_state(const struct npcx_wui *wui)
Mulin Chao84d90e42020-08-13 18:15:25 +0800190{
Wealian Liao6d6c5e12022-01-21 15:24:08 +0800191 const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
192 const uint32_t base = config->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800193
Mulin Chaoea00ff32021-02-04 18:09:20 -0800194 return IS_BIT_SET(NPCX_WKEN(base, wui->group), wui->bit);
Mulin Chao84d90e42020-08-13 18:15:25 +0800195}
196
Wealian Liaoabb94b12021-04-19 15:31:51 +0800197bool npcx_miwu_irq_get_and_clear_pending(const struct npcx_wui *wui)
198{
Wealian Liao6d6c5e12022-01-21 15:24:08 +0800199 const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
200 const uint32_t base = config->base;
Wealian Liaoabb94b12021-04-19 15:31:51 +0800201 bool pending = IS_BIT_SET(NPCX_WKPND(base, wui->group), wui->bit);
202
203 if (pending) {
204 NPCX_WKPCL(base, wui->group) = BIT(wui->bit);
205 }
206
207 return pending;
208}
209
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800210int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
Mulin Chao84d90e42020-08-13 18:15:25 +0800211 enum miwu_int_mode mode, enum miwu_int_trig trig)
212{
Wealian Liao6d6c5e12022-01-21 15:24:08 +0800213 const struct intc_miwu_config *config = miwu_devs[wui->table]->config;
214 const uint32_t base = config->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800215 uint8_t pmask = BIT(wui->bit);
216
Mulin Chaoea00ff32021-02-04 18:09:20 -0800217 /* Disable interrupt of wake-up input source before configuring it */
218 npcx_miwu_irq_disable(wui);
219
220 /* Handle interrupt for level trigger */
221 if (mode == NPCX_MIWU_MODE_LEVEL) {
222 /* Set detection mode to level */
223 NPCX_WKMOD(base, wui->group) |= pmask;
224 switch (trig) {
225 /* Enable interrupting on level high */
226 case NPCX_MIWU_TRIG_HIGH:
227 NPCX_WKEDG(base, wui->group) &= ~pmask;
228 break;
229 /* Enable interrupting on level low */
230 case NPCX_MIWU_TRIG_LOW:
231 NPCX_WKEDG(base, wui->group) |= pmask;
232 break;
233 default:
234 return -EINVAL;
Mulin Chao84d90e42020-08-13 18:15:25 +0800235 }
Mulin Chaoea00ff32021-02-04 18:09:20 -0800236 /* Handle interrupt for edge trigger */
237 } else {
238 /* Set detection mode to edge */
239 NPCX_WKMOD(base, wui->group) &= ~pmask;
240 switch (trig) {
241 /* Handle interrupting on falling edge */
242 case NPCX_MIWU_TRIG_LOW:
243 NPCX_WKAEDG(base, wui->group) &= ~pmask;
244 NPCX_WKEDG(base, wui->group) |= pmask;
245 break;
246 /* Handle interrupting on rising edge */
247 case NPCX_MIWU_TRIG_HIGH:
248 NPCX_WKAEDG(base, wui->group) &= ~pmask;
249 NPCX_WKEDG(base, wui->group) &= ~pmask;
250 break;
251 /* Handle interrupting on both edges */
252 case NPCX_MIWU_TRIG_BOTH:
253 /* Enable any edge */
254 NPCX_WKAEDG(base, wui->group) |= pmask;
255 break;
256 default:
257 return -EINVAL;
258 }
Mulin Chao84d90e42020-08-13 18:15:25 +0800259 }
260
Mulin Chaoea00ff32021-02-04 18:09:20 -0800261 /* Enable wake-up input sources */
262 NPCX_WKINEN(base, wui->group) |= pmask;
263
264 /*
265 * Clear pending bit since it might be set if WKINEN bit is
266 * changed.
267 */
268 NPCX_WKPCL(base, wui->group) |= pmask;
269
Mulin Chao84d90e42020-08-13 18:15:25 +0800270 return 0;
271}
272
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800273void npcx_miwu_init_gpio_callback(struct miwu_io_callback *callback,
Mulin Chao84d90e42020-08-13 18:15:25 +0800274 const struct npcx_wui *io_wui, int port)
275{
276 /* Initialize WUI and GPIO settings in unused bits field */
277 callback->params.wui.table = io_wui->table;
278 callback->params.wui.group = io_wui->group;
279 callback->params.wui.bit = io_wui->bit;
280 callback->params.gpio_port = port;
281}
282
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800283void npcx_miwu_init_dev_callback(struct miwu_dev_callback *callback,
Mulin Chao84d90e42020-08-13 18:15:25 +0800284 const struct npcx_wui *dev_wui,
285 miwu_dev_callback_handler_t handler,
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +0200286 const struct device *source)
Mulin Chao84d90e42020-08-13 18:15:25 +0800287{
288 /* Initialize WUI and input device settings */
289 callback->wui.table = dev_wui->table;
290 callback->wui.group = dev_wui->group;
291 callback->wui.bit = dev_wui->bit;
292 callback->handler = handler;
293 callback->source = source;
294}
295
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800296int npcx_miwu_manage_gpio_callback(struct miwu_io_callback *cb, bool set)
Mulin Chao84d90e42020-08-13 18:15:25 +0800297{
298 if (!sys_slist_is_empty(&cb_list_gpio)) {
299 if (!sys_slist_find_and_remove(&cb_list_gpio, &cb->node)) {
300 if (!set) {
301 return -EINVAL;
302 }
303 }
304 }
305
306 if (set) {
307 sys_slist_prepend(&cb_list_gpio, &cb->node);
308 }
309
310 return 0;
311}
312
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800313int npcx_miwu_manage_dev_callback(struct miwu_dev_callback *cb, bool set)
Mulin Chao84d90e42020-08-13 18:15:25 +0800314{
315 if (!sys_slist_is_empty(&cb_list_generic)) {
316 if (!sys_slist_find_and_remove(&cb_list_generic, &cb->node)) {
317 if (!set) {
318 return -EINVAL;
319 }
320 }
321 }
322
323 if (set) {
324 sys_slist_prepend(&cb_list_generic, &cb->node);
325 }
326
327 return 0;
328}
329
330/* MIWU driver registration */
331#define NPCX_MIWU_ISR_FUNC(index) _CONCAT(intc_miwu_isr, index)
332#define NPCX_MIWU_INIT_FUNC(inst) _CONCAT(intc_miwu_init, inst)
333#define NPCX_MIWU_INIT_FUNC_DECL(inst) \
Peter Bigot478577e2020-09-02 09:47:49 -0500334 static int intc_miwu_init##inst(const struct device *dev)
Mulin Chao84d90e42020-08-13 18:15:25 +0800335
336/* MIWU ISR implementation */
337#define NPCX_MIWU_ISR_FUNC_IMPL(inst) \
338 static void intc_miwu_isr##inst(void *arg) \
339 { \
340 uint8_t grp_mask = (uint32_t)arg; \
341 int group = 0; \
342 \
343 /* Check all MIWU groups belong to the same irq */ \
344 do { \
345 if (grp_mask & 0x01) \
346 intc_miwu_isr_pri(inst, group); \
347 group++; \
348 grp_mask = grp_mask >> 1; \
349 \
350 } while (grp_mask != 0); \
351 }
352
353/* MIWU init function implementation */
354#define NPCX_MIWU_INIT_FUNC_IMPL(inst) \
Peter Bigot478577e2020-09-02 09:47:49 -0500355 static int intc_miwu_init##inst(const struct device *dev) \
Mulin Chao84d90e42020-08-13 18:15:25 +0800356 { \
357 int i; \
Wealian Liao6d6c5e12022-01-21 15:24:08 +0800358 const struct intc_miwu_config *config = dev->config; \
359 const uint32_t base = config->base; \
Mulin Chao84d90e42020-08-13 18:15:25 +0800360 \
361 /* Clear all MIWUs' pending and enable bits of MIWU device */ \
362 for (i = 0; i < NPCX_MIWU_GROUP_COUNT; i++) { \
Mulin Chao84d90e42020-08-13 18:15:25 +0800363 NPCX_WKEN(base, i) = 0; \
Mulin Chaoea00ff32021-02-04 18:09:20 -0800364 NPCX_WKPCL(base, i) = 0xFF; \
Mulin Chao84d90e42020-08-13 18:15:25 +0800365 } \
366 \
367 /* Config IRQ and MWIU group directly */ \
Mulin Chaodaa48da2020-12-06 23:41:07 -0800368 DT_FOREACH_CHILD(NPCX_DT_NODE_FROM_MIWU_MAP(inst), \
369 NPCX_DT_MIWU_IRQ_CONNECT_IMPL_CHILD_FUNC) \
Mulin Chao84d90e42020-08-13 18:15:25 +0800370 return 0; \
371 } \
372
373#define NPCX_MIWU_INIT(inst) \
374 NPCX_MIWU_INIT_FUNC_DECL(inst); \
375 \
376 static const struct intc_miwu_config miwu_config_##inst = { \
377 .base = DT_REG_ADDR(DT_NODELABEL(miwu##inst)), \
378 .index = DT_PROP(DT_NODELABEL(miwu##inst), index), \
379 }; \
380 \
Kumar Gala2d754332020-12-17 11:14:17 -0600381 DEVICE_DT_INST_DEFINE(inst, \
Mulin Chao84d90e42020-08-13 18:15:25 +0800382 NPCX_MIWU_INIT_FUNC(inst), \
Gerard Marull-Paretase6170a42021-04-28 11:06:54 +0200383 NULL, \
Mulin Chao84d90e42020-08-13 18:15:25 +0800384 NULL, &miwu_config_##inst, \
385 PRE_KERNEL_1, \
Maureen Helm41634c82022-03-11 16:25:41 -0600386 CONFIG_INTC_INIT_PRIORITY, NULL); \
Mulin Chao84d90e42020-08-13 18:15:25 +0800387 \
388 NPCX_MIWU_ISR_FUNC_IMPL(inst) \
389 \
390 NPCX_MIWU_INIT_FUNC_IMPL(inst)
391
392DT_INST_FOREACH_STATUS_OKAY(NPCX_MIWU_INIT)
393
394/* MIWU module instances */
Kumar Gala2d754332020-12-17 11:14:17 -0600395#define NPCX_MIWU_DEV(inst) DEVICE_DT_INST_GET(inst),
Mulin Chao84d90e42020-08-13 18:15:25 +0800396
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +0200397static const struct device *miwu_devs[] = {
Mulin Chao84d90e42020-08-13 18:15:25 +0800398 DT_INST_FOREACH_STATUS_OKAY(NPCX_MIWU_DEV)
399};
400
401BUILD_ASSERT(ARRAY_SIZE(miwu_devs) == NPCX_MIWU_TABLE_COUNT,
402 "Size of miwu_devs array must equal to NPCX_MIWU_TABLE_COUNT");