blob: 29ee1a7d1b89617bb82bd3a5d60fba1d31ff35b9 [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 *
21 * This driver uses device tree files to present the relationship bewteen
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. npcx7-miwus-int-map.dtsi: it presents relationship between MIWU group
27 * and NVIC interrupt in npcx7. 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
Mulin Chao84d90e42020-08-13 18:15:25 +080049#include <device.h>
50#include <kernel.h>
51#include <soc.h>
Xavier Chapron824f4232020-09-22 14:42:43 +020052#include <sys/__assert.h>
Mulin Chao84d90e42020-08-13 18:15:25 +080053#include <irq_nextlevel.h>
54#include <drivers/gpio.h>
55
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
59#include <logging/log.h>
60LOG_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
82/* Driver convenience defines */
83#define DRV_CONFIG(dev) \
84 ((const struct intc_miwu_config *)(dev)->config)
85
86BUILD_ASSERT(sizeof(struct miwu_io_callback) == sizeof(struct gpio_callback),
87 "Size of struct miwu_io_callback must equal to struct gpio_callback");
88
89BUILD_ASSERT(sizeof(struct miwu_io_params) == sizeof(gpio_port_pins_t),
90 "Size of struct miwu_io_params must equal to struct gpio_port_pins_t");
91
92/* MIWU local functions */
93static void intc_miwu_dispatch_gpio_isr(uint8_t wui_table,
94 uint8_t wui_group, uint8_t wui_bit)
95{
96 struct miwu_io_callback *cb, *tmp;
97
98 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&cb_list_gpio, cb, tmp, node) {
Mulin Chao6deb68a2020-08-13 18:53:09 +080099 /* Pending bit, group and table match the wui item in list */
100 if (cb->params.wui.table == wui_table
101 && cb->params.wui.group == wui_group
102 && cb->params.wui.bit == wui_bit) {
103 __ASSERT(cb->handler, "No GPIO callback handler!");
104 /*
105 * Execute GPIO callback and the other callback might
106 * match the same wui item.
107 */
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800108 cb->handler(npcx_get_gpio_dev(cb->params.gpio_port),
Mulin Chao6deb68a2020-08-13 18:53:09 +0800109 (struct gpio_callback *)cb,
110 cb->params.pin_mask);
111 }
Mulin Chao84d90e42020-08-13 18:15:25 +0800112 }
113}
114
115static void intc_miwu_dispatch_generic_isr(uint8_t wui_table,
116 uint8_t wui_group, uint8_t wui_bit)
117{
118 struct miwu_dev_callback *cb, *tmp;
119
120 SYS_SLIST_FOR_EACH_CONTAINER_SAFE(&cb_list_generic, cb, tmp, node) {
121 /* Pending bit, group and table match the wui item in list */
122 if (cb->wui.table == wui_table
123 && cb->wui.group == wui_group
124 && cb->wui.bit == wui_bit) {
125 __ASSERT(cb->handler, "No Generic callback handler!");
126 /*
127 * Execute generic callback and the other callback might
128 * match the same wui item.
129 */
130 cb->handler(cb->source, &cb->wui);
131 }
132 }
133}
134
135static void intc_miwu_isr_pri(int wui_table, int wui_group)
136{
137 int wui_bit;
Mulin Chao50753c12020-09-14 10:30:54 +0800138 const uint32_t base = DRV_CONFIG(miwu_devs[wui_table])->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800139 uint8_t mask = NPCX_WKPND(base, wui_group) & NPCX_WKEN(base, wui_group);
140
141 /* Clear pending bits before dispatch ISR */
142 if (mask)
143 NPCX_WKPCL(base, wui_group) = mask;
144
145 for (wui_bit = 0; wui_bit < 8; wui_bit++) {
146 if (mask & BIT(wui_bit)) {
147 LOG_DBG("miwu_isr %d %d %d!\n", wui_table,
148 wui_group, wui_bit);
149 /* Dispatch registed gpio and generic isrs */
150 intc_miwu_dispatch_gpio_isr(wui_table,
151 wui_group, wui_bit);
152 intc_miwu_dispatch_generic_isr(wui_table,
153 wui_group, wui_bit);
154 }
155 }
156}
157
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800158/* Platform specific MIWU functions */
159void npcx_miwu_irq_enable(const struct npcx_wui *wui)
Mulin Chao84d90e42020-08-13 18:15:25 +0800160{
Mulin Chao50753c12020-09-14 10:30:54 +0800161 const uint32_t base = DRV_CONFIG(miwu_devs[wui->table])->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800162
163 NPCX_WKEN(base, wui->group) |= BIT(wui->bit);
164}
165
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800166void npcx_miwu_irq_disable(const struct npcx_wui *wui)
Mulin Chao84d90e42020-08-13 18:15:25 +0800167{
Mulin Chao50753c12020-09-14 10:30:54 +0800168 const uint32_t base = DRV_CONFIG(miwu_devs[wui->table])->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800169
170 NPCX_WKEN(base, wui->group) &= ~BIT(wui->bit);
171}
172
Mulin Chaoea00ff32021-02-04 18:09:20 -0800173bool npcx_miwu_irq_get_state(const struct npcx_wui *wui)
Mulin Chao84d90e42020-08-13 18:15:25 +0800174{
Mulin Chao50753c12020-09-14 10:30:54 +0800175 const uint32_t base = DRV_CONFIG(miwu_devs[wui->table])->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800176
Mulin Chaoea00ff32021-02-04 18:09:20 -0800177 return IS_BIT_SET(NPCX_WKEN(base, wui->group), wui->bit);
Mulin Chao84d90e42020-08-13 18:15:25 +0800178}
179
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800180int npcx_miwu_interrupt_configure(const struct npcx_wui *wui,
Mulin Chao84d90e42020-08-13 18:15:25 +0800181 enum miwu_int_mode mode, enum miwu_int_trig trig)
182{
Mulin Chao50753c12020-09-14 10:30:54 +0800183 const uint32_t base = DRV_CONFIG(miwu_devs[wui->table])->base;
Mulin Chao84d90e42020-08-13 18:15:25 +0800184 uint8_t pmask = BIT(wui->bit);
185
Mulin Chaoea00ff32021-02-04 18:09:20 -0800186 /* Disable interrupt of wake-up input source before configuring it */
187 npcx_miwu_irq_disable(wui);
188
189 /* Handle interrupt for level trigger */
190 if (mode == NPCX_MIWU_MODE_LEVEL) {
191 /* Set detection mode to level */
192 NPCX_WKMOD(base, wui->group) |= pmask;
193 switch (trig) {
194 /* Enable interrupting on level high */
195 case NPCX_MIWU_TRIG_HIGH:
196 NPCX_WKEDG(base, wui->group) &= ~pmask;
197 break;
198 /* Enable interrupting on level low */
199 case NPCX_MIWU_TRIG_LOW:
200 NPCX_WKEDG(base, wui->group) |= pmask;
201 break;
202 default:
203 return -EINVAL;
Mulin Chao84d90e42020-08-13 18:15:25 +0800204 }
Mulin Chaoea00ff32021-02-04 18:09:20 -0800205 /* Handle interrupt for edge trigger */
206 } else {
207 /* Set detection mode to edge */
208 NPCX_WKMOD(base, wui->group) &= ~pmask;
209 switch (trig) {
210 /* Handle interrupting on falling edge */
211 case NPCX_MIWU_TRIG_LOW:
212 NPCX_WKAEDG(base, wui->group) &= ~pmask;
213 NPCX_WKEDG(base, wui->group) |= pmask;
214 break;
215 /* Handle interrupting on rising edge */
216 case NPCX_MIWU_TRIG_HIGH:
217 NPCX_WKAEDG(base, wui->group) &= ~pmask;
218 NPCX_WKEDG(base, wui->group) &= ~pmask;
219 break;
220 /* Handle interrupting on both edges */
221 case NPCX_MIWU_TRIG_BOTH:
222 /* Enable any edge */
223 NPCX_WKAEDG(base, wui->group) |= pmask;
224 break;
225 default:
226 return -EINVAL;
227 }
Mulin Chao84d90e42020-08-13 18:15:25 +0800228 }
229
Mulin Chaoea00ff32021-02-04 18:09:20 -0800230 /* Enable wake-up input sources */
231 NPCX_WKINEN(base, wui->group) |= pmask;
232
233 /*
234 * Clear pending bit since it might be set if WKINEN bit is
235 * changed.
236 */
237 NPCX_WKPCL(base, wui->group) |= pmask;
238
Mulin Chao84d90e42020-08-13 18:15:25 +0800239 return 0;
240}
241
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800242void npcx_miwu_init_gpio_callback(struct miwu_io_callback *callback,
Mulin Chao84d90e42020-08-13 18:15:25 +0800243 const struct npcx_wui *io_wui, int port)
244{
245 /* Initialize WUI and GPIO settings in unused bits field */
246 callback->params.wui.table = io_wui->table;
247 callback->params.wui.group = io_wui->group;
248 callback->params.wui.bit = io_wui->bit;
249 callback->params.gpio_port = port;
250}
251
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800252void npcx_miwu_init_dev_callback(struct miwu_dev_callback *callback,
Mulin Chao84d90e42020-08-13 18:15:25 +0800253 const struct npcx_wui *dev_wui,
254 miwu_dev_callback_handler_t handler,
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +0200255 const struct device *source)
Mulin Chao84d90e42020-08-13 18:15:25 +0800256{
257 /* Initialize WUI and input device settings */
258 callback->wui.table = dev_wui->table;
259 callback->wui.group = dev_wui->group;
260 callback->wui.bit = dev_wui->bit;
261 callback->handler = handler;
262 callback->source = source;
263}
264
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800265int npcx_miwu_manage_gpio_callback(struct miwu_io_callback *cb, bool set)
Mulin Chao84d90e42020-08-13 18:15:25 +0800266{
267 if (!sys_slist_is_empty(&cb_list_gpio)) {
268 if (!sys_slist_find_and_remove(&cb_list_gpio, &cb->node)) {
269 if (!set) {
270 return -EINVAL;
271 }
272 }
273 }
274
275 if (set) {
276 sys_slist_prepend(&cb_list_gpio, &cb->node);
277 }
278
279 return 0;
280}
281
Mulin Chaod55aa5a2020-10-21 14:10:26 +0800282int npcx_miwu_manage_dev_callback(struct miwu_dev_callback *cb, bool set)
Mulin Chao84d90e42020-08-13 18:15:25 +0800283{
284 if (!sys_slist_is_empty(&cb_list_generic)) {
285 if (!sys_slist_find_and_remove(&cb_list_generic, &cb->node)) {
286 if (!set) {
287 return -EINVAL;
288 }
289 }
290 }
291
292 if (set) {
293 sys_slist_prepend(&cb_list_generic, &cb->node);
294 }
295
296 return 0;
297}
298
299/* MIWU driver registration */
300#define NPCX_MIWU_ISR_FUNC(index) _CONCAT(intc_miwu_isr, index)
301#define NPCX_MIWU_INIT_FUNC(inst) _CONCAT(intc_miwu_init, inst)
302#define NPCX_MIWU_INIT_FUNC_DECL(inst) \
Peter Bigot478577e2020-09-02 09:47:49 -0500303 static int intc_miwu_init##inst(const struct device *dev)
Mulin Chao84d90e42020-08-13 18:15:25 +0800304
305/* MIWU ISR implementation */
306#define NPCX_MIWU_ISR_FUNC_IMPL(inst) \
307 static void intc_miwu_isr##inst(void *arg) \
308 { \
309 uint8_t grp_mask = (uint32_t)arg; \
310 int group = 0; \
311 \
312 /* Check all MIWU groups belong to the same irq */ \
313 do { \
314 if (grp_mask & 0x01) \
315 intc_miwu_isr_pri(inst, group); \
316 group++; \
317 grp_mask = grp_mask >> 1; \
318 \
319 } while (grp_mask != 0); \
320 }
321
322/* MIWU init function implementation */
323#define NPCX_MIWU_INIT_FUNC_IMPL(inst) \
Peter Bigot478577e2020-09-02 09:47:49 -0500324 static int intc_miwu_init##inst(const struct device *dev) \
Mulin Chao84d90e42020-08-13 18:15:25 +0800325 { \
326 int i; \
Mulin Chao50753c12020-09-14 10:30:54 +0800327 const uint32_t base = DRV_CONFIG(dev)->base; \
Mulin Chao84d90e42020-08-13 18:15:25 +0800328 \
329 /* Clear all MIWUs' pending and enable bits of MIWU device */ \
330 for (i = 0; i < NPCX_MIWU_GROUP_COUNT; i++) { \
Mulin Chao84d90e42020-08-13 18:15:25 +0800331 NPCX_WKEN(base, i) = 0; \
Mulin Chaoea00ff32021-02-04 18:09:20 -0800332 NPCX_WKPCL(base, i) = 0xFF; \
Mulin Chao84d90e42020-08-13 18:15:25 +0800333 } \
334 \
335 /* Config IRQ and MWIU group directly */ \
Mulin Chaodaa48da2020-12-06 23:41:07 -0800336 DT_FOREACH_CHILD(NPCX_DT_NODE_FROM_MIWU_MAP(inst), \
337 NPCX_DT_MIWU_IRQ_CONNECT_IMPL_CHILD_FUNC) \
Mulin Chao84d90e42020-08-13 18:15:25 +0800338 return 0; \
339 } \
340
341#define NPCX_MIWU_INIT(inst) \
342 NPCX_MIWU_INIT_FUNC_DECL(inst); \
343 \
344 static const struct intc_miwu_config miwu_config_##inst = { \
345 .base = DT_REG_ADDR(DT_NODELABEL(miwu##inst)), \
346 .index = DT_PROP(DT_NODELABEL(miwu##inst), index), \
347 }; \
348 \
Kumar Gala2d754332020-12-17 11:14:17 -0600349 DEVICE_DT_INST_DEFINE(inst, \
Mulin Chao84d90e42020-08-13 18:15:25 +0800350 NPCX_MIWU_INIT_FUNC(inst), \
Kumar Gala2d754332020-12-17 11:14:17 -0600351 device_pm_control_nop, \
Mulin Chao84d90e42020-08-13 18:15:25 +0800352 NULL, &miwu_config_##inst, \
353 PRE_KERNEL_1, \
354 CONFIG_KERNEL_INIT_PRIORITY_OBJECTS, NULL); \
355 \
356 NPCX_MIWU_ISR_FUNC_IMPL(inst) \
357 \
358 NPCX_MIWU_INIT_FUNC_IMPL(inst)
359
360DT_INST_FOREACH_STATUS_OKAY(NPCX_MIWU_INIT)
361
362/* MIWU module instances */
Kumar Gala2d754332020-12-17 11:14:17 -0600363#define NPCX_MIWU_DEV(inst) DEVICE_DT_INST_GET(inst),
Mulin Chao84d90e42020-08-13 18:15:25 +0800364
Tomasz Bursztykae18fcbb2020-04-30 20:33:38 +0200365static const struct device *miwu_devs[] = {
Mulin Chao84d90e42020-08-13 18:15:25 +0800366 DT_INST_FOREACH_STATUS_OKAY(NPCX_MIWU_DEV)
367};
368
369BUILD_ASSERT(ARRAY_SIZE(miwu_devs) == NPCX_MIWU_TABLE_COUNT,
370 "Size of miwu_devs array must equal to NPCX_MIWU_TABLE_COUNT");