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_gpio
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/dt-bindings/gpio/nuvoton-npcx-gpio.h>
13 #include <soc.h>
14 
15 #include <zephyr/drivers/gpio/gpio_utils.h>
16 #include "soc_gpio.h"
17 #include "soc_miwu.h"
18 
19 #include <zephyr/logging/log.h>
20 LOG_MODULE_REGISTER(gpio_npcx, LOG_LEVEL_ERR);
21 
22 /* GPIO module instances */
23 #define NPCX_GPIO_DEV(inst) DEVICE_DT_INST_GET(inst),
24 static const struct device *const gpio_devs[] = {
25 	DT_INST_FOREACH_STATUS_OKAY(NPCX_GPIO_DEV)
26 };
27 
28 /* Driver config */
29 struct gpio_npcx_config {
30 	/* gpio_driver_config needs to be first */
31 	struct gpio_driver_config common;
32 	/* GPIO controller base address */
33 	uintptr_t base;
34 	/* IO port */
35 	int port;
36 	/* Mapping table between gpio bits and wui */
37 	struct npcx_wui wui_maps[NPCX_GPIO_PORT_PIN_NUM];
38 	/* Mapping table between gpio bits and lvol */
39 	struct npcx_lvol lvol_maps[NPCX_GPIO_PORT_PIN_NUM];
40 };
41 
42 /* Driver data */
43 struct gpio_npcx_data {
44 	/* gpio_driver_data needs to be first */
45 	struct gpio_driver_data common;
46 };
47 
48 /* Driver convenience defines */
49 #define HAL_INSTANCE(dev)                                                                          \
50 	((struct gpio_reg *)((const struct gpio_npcx_config *)(dev)->config)->base)
51 
52 /* Platform specific GPIO functions */
npcx_get_gpio_dev(int port)53 const struct device *npcx_get_gpio_dev(int port)
54 {
55 	if (port >= ARRAY_SIZE(gpio_devs)) {
56 		return NULL;
57 	}
58 
59 	return gpio_devs[port];
60 }
61 
npcx_gpio_enable_io_pads(const struct device * dev,int pin)62 void npcx_gpio_enable_io_pads(const struct device *dev, int pin)
63 {
64 	const struct gpio_npcx_config *const config = dev->config;
65 	const struct npcx_wui *io_wui = &config->wui_maps[pin];
66 
67 	if (io_wui->table == NPCX_MIWU_TABLE_NONE) {
68 		LOG_ERR("Cannot enable GPIO(%x, %d) pad", config->port, pin);
69 		return;
70 	}
71 
72 	/*
73 	 * If this pin is configured as a GPIO interrupt source, do not
74 	 * implement bypass. Or ec cannot wake up via this event.
75 	 */
76 	if (pin < NPCX_GPIO_PORT_PIN_NUM && !npcx_miwu_irq_get_state(io_wui)) {
77 		npcx_miwu_io_enable(io_wui);
78 	}
79 }
80 
npcx_gpio_disable_io_pads(const struct device * dev,int pin)81 void npcx_gpio_disable_io_pads(const struct device *dev, int pin)
82 {
83 	const struct gpio_npcx_config *const config = dev->config;
84 	const struct npcx_wui *io_wui = &config->wui_maps[pin];
85 
86 	if (io_wui->table == NPCX_MIWU_TABLE_NONE) {
87 		LOG_ERR("Cannot disable GPIO(%x, %d) pad", config->port, pin);
88 		return;
89 	}
90 
91 	/*
92 	 * If this pin is configured as a GPIO interrupt source, do not
93 	 * implement bypass. Or ec cannot wake up via this event.
94 	 */
95 	if (pin < NPCX_GPIO_PORT_PIN_NUM && !npcx_miwu_irq_get_state(io_wui)) {
96 		npcx_miwu_io_disable(io_wui);
97 	}
98 }
99 
100 /* GPIO api functions */
gpio_npcx_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)101 static int gpio_npcx_config(const struct device *dev,
102 			     gpio_pin_t pin, gpio_flags_t flags)
103 {
104 	const struct gpio_npcx_config *const config = dev->config;
105 	const struct npcx_lvol *lvol = &config->lvol_maps[pin];
106 	struct gpio_reg *const inst = HAL_INSTANCE(dev);
107 	uint32_t mask = BIT(pin);
108 
109 	/* Don't support simultaneous in/out mode */
110 	if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) {
111 		return -ENOTSUP;
112 	}
113 
114 	/* Don't support "open source" mode */
115 	if (((flags & GPIO_SINGLE_ENDED) != 0) &&
116 	    ((flags & GPIO_LINE_OPEN_DRAIN) == 0)) {
117 		return -ENOTSUP;
118 	}
119 
120 	/*
121 	 * Configure pin as input, if requested. Output is configured only
122 	 * after setting all other attributes, so as not to create a
123 	 * temporary incorrect logic state 0:input 1:output
124 	 */
125 	if ((flags & GPIO_OUTPUT) == 0) {
126 		inst->PDIR &= ~mask;
127 	}
128 
129 	/* Does this IO pad support low-voltage input (1.8V) detection? */
130 	if (lvol->ctrl != NPCX_DT_LVOL_CTRL_NONE) {
131 		gpio_flags_t volt = flags & NPCX_GPIO_VOLTAGE_MASK;
132 
133 		/*
134 		 * If this IO pad is configured for low-voltage input detection,
135 		 * the related drive type must select to open-drain also.
136 		 */
137 		if (volt == NPCX_GPIO_VOLTAGE_1P8) {
138 			flags |= GPIO_OPEN_DRAIN;
139 			npcx_lvol_set_detect_level(lvol->ctrl, lvol->bit, true);
140 		} else {
141 			npcx_lvol_set_detect_level(lvol->ctrl, lvol->bit, false);
142 		}
143 	}
144 
145 	/* Select open drain 0:push-pull 1:open-drain */
146 	if ((flags & GPIO_OPEN_DRAIN) != 0) {
147 		inst->PTYPE |= mask;
148 	} else {
149 		inst->PTYPE &= ~mask;
150 	}
151 
152 	/* Select pull-up/down of GPIO 0:pull-up 1:pull-down */
153 	if ((flags & GPIO_PULL_UP) != 0) {
154 		inst->PPUD  &= ~mask;
155 		inst->PPULL |= mask;
156 	} else if ((flags & GPIO_PULL_DOWN) != 0) {
157 		inst->PPUD  |= mask;
158 		inst->PPULL |= mask;
159 	} else {
160 		/* disable pull down/up */
161 		inst->PPULL &= ~mask;
162 	}
163 
164 	/* Set level 0:low 1:high */
165 	if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
166 		inst->PDOUT |= mask;
167 	} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
168 		inst->PDOUT &= ~mask;
169 	}
170 
171 	/* Configure pin as output, if requested 0:input 1:output */
172 	if ((flags & GPIO_OUTPUT) != 0) {
173 		inst->PDIR |= mask;
174 	}
175 
176 	return 0;
177 }
178 
179 #ifdef CONFIG_GPIO_GET_CONFIG
gpio_npcx_pin_get_config(const struct device * port,gpio_pin_t pin,gpio_flags_t * out_flags)180 static int gpio_npcx_pin_get_config(const struct device *port, gpio_pin_t pin,
181 				    gpio_flags_t *out_flags)
182 {
183 	const struct gpio_npcx_config *const config = port->config;
184 	const struct npcx_lvol *lvol = &config->lvol_maps[pin];
185 	struct gpio_reg *const inst = HAL_INSTANCE(port);
186 	uint32_t mask = BIT(pin);
187 	gpio_flags_t flags = 0;
188 
189 	/* 0:input 1:output */
190 	if (inst->PDIR & mask) {
191 		flags |= GPIO_OUTPUT;
192 
193 		/* 0:push-pull 1:open-drain */
194 		if (inst->PTYPE & mask) {
195 			flags |= GPIO_OPEN_DRAIN;
196 		}
197 
198 		/* 0:low 1:high */
199 		if (inst->PDOUT & mask) {
200 			flags |= GPIO_OUTPUT_HIGH;
201 		} else {
202 			flags |= GPIO_OUTPUT_LOW;
203 		}
204 	} else {
205 		flags |= GPIO_INPUT;
206 
207 		/* 0:disabled 1:enabled pull */
208 		if (inst->PPULL & mask) {
209 			/* 0:pull-up 1:pull-down */
210 			if (inst->PPUD & mask) {
211 				flags |= GPIO_PULL_DOWN;
212 			} else {
213 				flags |= GPIO_PULL_UP;
214 			}
215 		}
216 	}
217 
218 	/* Enable low-voltage detection? */
219 	if (lvol->ctrl != NPCX_DT_LVOL_CTRL_NONE &&
220 		npcx_lvol_get_detect_level(lvol->ctrl, lvol->bit)) {
221 		flags |= NPCX_GPIO_VOLTAGE_1P8;
222 	};
223 
224 	*out_flags = flags;
225 
226 	return 0;
227 }
228 #endif
229 
gpio_npcx_port_get_raw(const struct device * dev,gpio_port_value_t * value)230 static int gpio_npcx_port_get_raw(const struct device *dev,
231 				  gpio_port_value_t *value)
232 {
233 	struct gpio_reg *const inst = HAL_INSTANCE(dev);
234 
235 	/* Get raw bits of GPIO input registers */
236 	*value = inst->PDIN;
237 
238 	return 0;
239 }
240 
gpio_npcx_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)241 static int gpio_npcx_port_set_masked_raw(const struct device *dev,
242 					  gpio_port_pins_t mask,
243 					  gpio_port_value_t value)
244 {
245 	struct gpio_reg *const inst = HAL_INSTANCE(dev);
246 	uint8_t out = inst->PDOUT;
247 
248 	inst->PDOUT = ((out & ~mask) | (value & mask));
249 
250 	return 0;
251 }
252 
gpio_npcx_port_set_bits_raw(const struct device * dev,gpio_port_pins_t mask)253 static int gpio_npcx_port_set_bits_raw(const struct device *dev,
254 				       gpio_port_pins_t mask)
255 {
256 	struct gpio_reg *const inst = HAL_INSTANCE(dev);
257 
258 	/* Set raw bits of GPIO output registers */
259 	inst->PDOUT |= mask;
260 
261 	return 0;
262 }
263 
gpio_npcx_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t mask)264 static int gpio_npcx_port_clear_bits_raw(const struct device *dev,
265 					 gpio_port_pins_t mask)
266 {
267 	struct gpio_reg *const inst = HAL_INSTANCE(dev);
268 
269 	/* Clear raw bits of GPIO output registers */
270 	inst->PDOUT &= ~mask;
271 
272 	return 0;
273 }
274 
gpio_npcx_port_toggle_bits(const struct device * dev,gpio_port_pins_t mask)275 static int gpio_npcx_port_toggle_bits(const struct device *dev,
276 				      gpio_port_pins_t mask)
277 {
278 	struct gpio_reg *const inst = HAL_INSTANCE(dev);
279 
280 	/* Toggle raw bits of GPIO output registers */
281 	inst->PDOUT ^= mask;
282 
283 	return 0;
284 }
285 
gpio_npcx_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)286 static int gpio_npcx_pin_interrupt_configure(const struct device *dev,
287 					     gpio_pin_t pin,
288 					     enum gpio_int_mode mode,
289 					     enum gpio_int_trig trig)
290 {
291 	const struct gpio_npcx_config *const config = dev->config;
292 
293 	if (config->wui_maps[pin].table == NPCX_MIWU_TABLE_NONE) {
294 		LOG_ERR("Cannot configure GPIO(%x, %d)", config->port, pin);
295 		return -EINVAL;
296 	}
297 
298 	LOG_DBG("pin_int_conf (%d, %d) match (%d, %d, %d)!!!",
299 			config->port, pin, config->wui_maps[pin].table,
300 			config->wui_maps[pin].group,
301 			config->wui_maps[pin].bit);
302 #ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT
303 	if (mode == GPIO_INT_MODE_DISABLE_ONLY) {
304 		npcx_miwu_irq_disable(&config->wui_maps[pin]);
305 		return 0;
306 	} else if (mode == GPIO_INT_MODE_ENABLE_ONLY) {
307 		npcx_miwu_irq_enable(&config->wui_maps[pin]);
308 		return 0;
309 	}
310 #endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */
311 
312 	/* Disable irq of wake-up input io-pads before configuring them */
313 	npcx_miwu_irq_disable(&config->wui_maps[pin]);
314 
315 	/* Configure and enable interrupt? */
316 	if (mode != GPIO_INT_MODE_DISABLED) {
317 		enum miwu_int_mode miwu_mode;
318 		enum miwu_int_trig miwu_trig;
319 		int ret = 0;
320 
321 		/* Determine interrupt is level or edge mode? */
322 		if (mode == GPIO_INT_MODE_EDGE) {
323 			miwu_mode = NPCX_MIWU_MODE_EDGE;
324 		} else {
325 			miwu_mode = NPCX_MIWU_MODE_LEVEL;
326 		}
327 
328 		/* Determine trigger mode is low, high or both? */
329 		if (trig == GPIO_INT_TRIG_LOW) {
330 			miwu_trig = NPCX_MIWU_TRIG_LOW;
331 		} else if (trig == GPIO_INT_TRIG_HIGH) {
332 			miwu_trig = NPCX_MIWU_TRIG_HIGH;
333 		} else if (trig == GPIO_INT_TRIG_BOTH) {
334 			miwu_trig = NPCX_MIWU_TRIG_BOTH;
335 		} else {
336 			LOG_ERR("Invalid interrupt trigger type %d", trig);
337 			return -EINVAL;
338 		}
339 
340 		/* Call MIWU routine to setup interrupt configuration */
341 		ret = npcx_miwu_interrupt_configure(&config->wui_maps[pin],
342 						miwu_mode, miwu_trig);
343 		if (ret != 0) {
344 			LOG_ERR("Configure MIWU interrupt failed");
345 			return ret;
346 		}
347 
348 		/* Enable it after configuration is completed */
349 		npcx_miwu_irq_enable(&config->wui_maps[pin]);
350 	}
351 
352 	return 0;
353 }
354 
gpio_npcx_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)355 static int gpio_npcx_manage_callback(const struct device *dev,
356 				      struct gpio_callback *callback, bool set)
357 {
358 	const struct gpio_npcx_config *const config = dev->config;
359 	struct miwu_callback *miwu_cb = (struct miwu_callback *)callback;
360 	int pin = find_lsb_set(callback->pin_mask) - 1;
361 
362 	/* pin_mask should not be zero */
363 	if (pin < 0) {
364 		return -EINVAL;
365 	}
366 
367 	/* Has the IO pin valid MIWU input source? */
368 	if (config->wui_maps[pin].table == NPCX_MIWU_TABLE_NONE) {
369 		LOG_ERR("Cannot manage GPIO(%x, %d) callback!", config->port,
370 				pin);
371 		return -EINVAL;
372 	}
373 
374 	/* Initialize WUI information in unused bits field */
375 	npcx_miwu_init_gpio_callback(miwu_cb, &config->wui_maps[pin],
376 			config->port);
377 
378 	/* Insert or remove a IO callback which being called in MIWU ISRs */
379 	return npcx_miwu_manage_callback(miwu_cb, set);
380 }
381 
382 /* GPIO driver registration */
383 static const struct gpio_driver_api gpio_npcx_driver = {
384 	.pin_configure = gpio_npcx_config,
385 #ifdef CONFIG_GPIO_GET_CONFIG
386 	.pin_get_config = gpio_npcx_pin_get_config,
387 #endif
388 	.port_get_raw = gpio_npcx_port_get_raw,
389 	.port_set_masked_raw = gpio_npcx_port_set_masked_raw,
390 	.port_set_bits_raw = gpio_npcx_port_set_bits_raw,
391 	.port_clear_bits_raw = gpio_npcx_port_clear_bits_raw,
392 	.port_toggle_bits = gpio_npcx_port_toggle_bits,
393 	.pin_interrupt_configure = gpio_npcx_pin_interrupt_configure,
394 	.manage_callback = gpio_npcx_manage_callback,
395 };
396 
gpio_npcx_init(const struct device * dev)397 int gpio_npcx_init(const struct device *dev)
398 {
399 	ARG_UNUSED(dev);
400 
401 	return 0;
402 }
403 
404 #define NPCX_GPIO_DEVICE_INIT(inst)                                            \
405 	static const struct gpio_npcx_config gpio_npcx_cfg_##inst = {          \
406 		.common = {						       \
407 			.port_pin_mask =                                       \
408 			GPIO_PORT_PIN_MASK_FROM_NGPIOS(NPCX_GPIO_PORT_PIN_NUM),\
409 		},                                                             \
410 		.base = DT_INST_REG_ADDR(inst),                                \
411 		.port = inst,                                                  \
412 		.wui_maps = NPCX_DT_WUI_ITEMS_LIST(inst),                      \
413 		.lvol_maps = NPCX_DT_LVOL_ITEMS_LIST(inst),                    \
414 	};                                                                     \
415 	BUILD_ASSERT(NPCX_DT_WUI_ITEMS_LEN(inst) == NPCX_GPIO_PORT_PIN_NUM,    \
416 			"size of prop. wui-maps must equal to pin number!");   \
417 	BUILD_ASSERT(NPCX_DT_LVOL_ITEMS_LEN(inst) == NPCX_GPIO_PORT_PIN_NUM,   \
418 			"size of prop. lvol-maps must equal to pin number!");  \
419 									       \
420 	static struct gpio_npcx_data gpio_npcx_data_##inst;	               \
421 									       \
422 	DEVICE_DT_INST_DEFINE(inst,					       \
423 			    gpio_npcx_init,                                    \
424 			    NULL,					       \
425 			    &gpio_npcx_data_##inst,                            \
426 			    &gpio_npcx_cfg_##inst,                             \
427 			    PRE_KERNEL_1,                                       \
428 			    CONFIG_GPIO_INIT_PRIORITY,                         \
429 			    &gpio_npcx_driver);
430 
431 DT_INST_FOREACH_STATUS_OKAY(NPCX_GPIO_DEVICE_INIT)
432