1 /*
2  * Copyright 2017-2020,2022-2023 NXP
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nxp_lpc_gpio_port
8 
9 /** @file
10  * @brief GPIO driver for LPC54XXX family
11  *
12  * Note:
13  * - fsl_pint internally tries to manage interrupts, but this is not used (e.g.
14  *   s_pintCallback), Zephyr's interrupt management system is used in place.
15  */
16 
17 #include <errno.h>
18 #include <zephyr/device.h>
19 #include <zephyr/drivers/gpio.h>
20 #include <zephyr/irq.h>
21 #include <soc.h>
22 #include <fsl_common.h>
23 #include <zephyr/drivers/gpio/gpio_utils.h>
24 #ifdef CONFIG_NXP_PINT
25 #include <zephyr/drivers/interrupt_controller/nxp_pint.h>
26 #endif
27 #include <fsl_gpio.h>
28 #include <fsl_device_registers.h>
29 #ifdef MCI_IO_MUX
30 #include <zephyr/drivers/pinctrl.h>
31 #endif
32 
33 /* Interrupt sources, matching int-source enum in DTS binding definition */
34 #define INT_SOURCE_PINT 0
35 #define INT_SOURCE_INTA 1
36 #define INT_SOURCE_INTB 2
37 #define INT_SOURCE_NONE 3
38 
39 struct gpio_mcux_lpc_config {
40 	/* gpio_driver_config needs to be first */
41 	struct gpio_driver_config common;
42 	GPIO_Type *gpio_base;
43 	uint8_t int_source;
44 #ifdef IOPCTL
45 	IOPCTL_Type *pinmux_base;
46 #endif
47 #ifdef IOCON
48 	IOCON_Type *pinmux_base;
49 #endif
50 #ifdef MCI_IO_MUX
51 	MCI_IO_MUX_Type * pinmux_base;
52 #endif
53 	uint32_t port_no;
54 };
55 
56 struct gpio_mcux_lpc_data {
57 	/* gpio_driver_data needs to be first */
58 	struct gpio_driver_data common;
59 	/* port ISR callback routine address */
60 	sys_slist_t callbacks;
61 };
62 
gpio_mcux_lpc_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)63 static int gpio_mcux_lpc_configure(const struct device *dev, gpio_pin_t pin,
64 				   gpio_flags_t flags)
65 {
66 	const struct gpio_mcux_lpc_config *config = dev->config;
67 	GPIO_Type *gpio_base = config->gpio_base;
68 	uint32_t port = config->port_no;
69 
70 	if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) {
71 		return -ENOTSUP;
72 	}
73 
74 #ifdef IOPCTL /* RT600 and RT500 series */
75 	IOPCTL_Type *pinmux_base = config->pinmux_base;
76 	volatile uint32_t *pinconfig = (volatile uint32_t *)&(pinmux_base->PIO[port][pin]);
77 
78 	/*
79 	 * Enable input buffer for both input and output pins, it costs
80 	 * nothing and allows values to be read back.
81 	 */
82 	*pinconfig |= IOPCTL_PIO_INBUF_EN;
83 
84 	if ((flags & GPIO_SINGLE_ENDED) != 0) {
85 		*pinconfig |= IOPCTL_PIO_PSEDRAIN_EN;
86 	} else {
87 		*pinconfig &= ~IOPCTL_PIO_PSEDRAIN_EN;
88 	}
89 	/* Select GPIO mux for this pin (func 0 is always GPIO) */
90 	*pinconfig &= ~(IOPCTL_PIO_FSEL_MASK);
91 #endif
92 #ifdef IOCON /* LPC SOCs */
93 	volatile uint32_t *pinconfig;
94 	IOCON_Type *pinmux_base;
95 
96 	pinmux_base = config->pinmux_base;
97 	pinconfig = (volatile uint32_t *)&(pinmux_base->PIO[port][pin]);
98 
99 	if ((flags & GPIO_SINGLE_ENDED) != 0) {
100 		/* Set ODE bit. */
101 		*pinconfig |= IOCON_PIO_OD_MASK;
102 	}
103 
104 	if ((flags & GPIO_INPUT) != 0) {
105 		/* Set DIGIMODE bit */
106 		*pinconfig |= IOCON_PIO_DIGIMODE_MASK;
107 	}
108 	/* Select GPIO mux for this pin (func 0 is always GPIO) */
109 	*pinconfig &= ~(IOCON_PIO_FUNC_MASK);
110 #endif
111 #ifdef MCI_IO_MUX /* RW61x SOCs */
112 		/* Construct a pin control state, and apply it directly. */
113 		pinctrl_soc_pin_t pin_cfg;
114 
115 		if (config->port_no == 1) {
116 			pin_cfg = IOMUX_GPIO_IDX(pin + 32) | IOMUX_TYPE(IOMUX_GPIO);
117 		} else {
118 			pin_cfg = IOMUX_GPIO_IDX(pin) | IOMUX_TYPE(IOMUX_GPIO);
119 		}
120 		/* Add pull up flags, if required */
121 		if ((flags & GPIO_PULL_UP) != 0) {
122 			pin_cfg |= IOMUX_PAD_PULL(0x1);
123 		} else if ((flags & GPIO_PULL_DOWN) != 0) {
124 			pin_cfg |= IOMUX_PAD_PULL(0x2);
125 		}
126 		pinctrl_configure_pins(&pin_cfg, 1, 0);
127 #endif
128 
129 	if (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) {
130 #ifdef IOPCTL /* RT600 and RT500 series */
131 		*pinconfig |= IOPCTL_PIO_PUPD_EN;
132 		if ((flags & GPIO_PULL_UP) != 0) {
133 			*pinconfig |= IOPCTL_PIO_PULLUP_EN;
134 		} else if ((flags & GPIO_PULL_DOWN) != 0) {
135 			*pinconfig &= ~(IOPCTL_PIO_PULLUP_EN);
136 		}
137 #endif
138 #ifdef IOCON /* LPC SOCs */
139 
140 		*pinconfig &= ~(IOCON_PIO_MODE_PULLUP|IOCON_PIO_MODE_PULLDOWN);
141 		if ((flags & GPIO_PULL_UP) != 0) {
142 			*pinconfig |= IOCON_PIO_MODE_PULLUP;
143 		} else if ((flags & GPIO_PULL_DOWN) != 0) {
144 			*pinconfig |= IOCON_PIO_MODE_PULLDOWN;
145 		}
146 #endif
147 	} else {
148 #ifdef IOPCTL /* RT600 and RT500 series */
149 		*pinconfig &= ~IOPCTL_PIO_PUPD_EN;
150 #endif
151 #ifdef IOCON /* LPC SOCs */
152 		*pinconfig &= ~(IOCON_PIO_MODE_PULLUP|IOCON_PIO_MODE_PULLDOWN);
153 #endif
154 #ifdef MCI_IO_MUX
155 
156 #endif
157 	}
158 
159 	/* supports access by pin now,you can add access by port when needed */
160 	if (flags & GPIO_OUTPUT_INIT_HIGH) {
161 		gpio_base->SET[port] = BIT(pin);
162 	}
163 
164 	if (flags & GPIO_OUTPUT_INIT_LOW) {
165 		gpio_base->CLR[port] = BIT(pin);
166 	}
167 
168 	/* input-0,output-1 */
169 	WRITE_BIT(gpio_base->DIR[port], pin, flags & GPIO_OUTPUT);
170 
171 	return 0;
172 }
173 
gpio_mcux_lpc_port_get_raw(const struct device * dev,uint32_t * value)174 static int gpio_mcux_lpc_port_get_raw(const struct device *dev,
175 				      uint32_t *value)
176 {
177 	const struct gpio_mcux_lpc_config *config = dev->config;
178 	GPIO_Type *gpio_base = config->gpio_base;
179 
180 	*value = gpio_base->PIN[config->port_no];
181 
182 	return 0;
183 }
184 
gpio_mcux_lpc_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)185 static int gpio_mcux_lpc_port_set_masked_raw(const struct device *dev,
186 					     uint32_t mask,
187 					     uint32_t value)
188 {
189 	const struct gpio_mcux_lpc_config *config = dev->config;
190 	GPIO_Type *gpio_base = config->gpio_base;
191 	uint32_t port = config->port_no;
192 
193 	/* Writing 0 allows R+W, 1 disables the pin */
194 	gpio_base->MASK[port] = ~mask;
195 	gpio_base->MPIN[port] = value;
196 	/* Enable back the pins, user won't assume pins remain masked*/
197 	gpio_base->MASK[port] = 0U;
198 
199 	return 0;
200 }
201 
gpio_mcux_lpc_port_set_bits_raw(const struct device * dev,uint32_t mask)202 static int gpio_mcux_lpc_port_set_bits_raw(const struct device *dev,
203 					   uint32_t mask)
204 {
205 	const struct gpio_mcux_lpc_config *config = dev->config;
206 	GPIO_Type *gpio_base = config->gpio_base;
207 
208 	gpio_base->SET[config->port_no] = mask;
209 
210 	return 0;
211 }
212 
gpio_mcux_lpc_port_clear_bits_raw(const struct device * dev,uint32_t mask)213 static int gpio_mcux_lpc_port_clear_bits_raw(const struct device *dev,
214 					     uint32_t mask)
215 {
216 	const struct gpio_mcux_lpc_config *config = dev->config;
217 	GPIO_Type *gpio_base = config->gpio_base;
218 
219 	gpio_base->CLR[config->port_no] = mask;
220 
221 	return 0;
222 }
223 
gpio_mcux_lpc_port_toggle_bits(const struct device * dev,uint32_t mask)224 static int gpio_mcux_lpc_port_toggle_bits(const struct device *dev,
225 					  uint32_t mask)
226 {
227 	const struct gpio_mcux_lpc_config *config = dev->config;
228 	GPIO_Type *gpio_base = config->gpio_base;
229 
230 	gpio_base->NOT[config->port_no] = mask;
231 
232 	return 0;
233 }
234 
235 
236 #ifdef CONFIG_NXP_PINT
237 /* Called by PINT when pin interrupt fires */
gpio_mcux_lpc_pint_cb(uint8_t pin,void * user)238 static void gpio_mcux_lpc_pint_cb(uint8_t pin, void *user)
239 {
240 	const struct device *dev = user;
241 	const struct gpio_mcux_lpc_config *config = dev->config;
242 	struct gpio_mcux_lpc_data *data = dev->data;
243 	uint32_t gpio_pin;
244 
245 	/* Subtract port number times 32 from pin number to get GPIO API
246 	 * pin number.
247 	 */
248 	gpio_pin = pin - (config->port_no * 32);
249 
250 	gpio_fire_callbacks(&data->callbacks, dev, BIT(gpio_pin));
251 }
252 
253 
254 /* Installs interrupt handler using PINT interrupt controller. */
gpio_mcux_lpc_pint_interrupt_cfg(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)255 static int gpio_mcux_lpc_pint_interrupt_cfg(const struct device *dev,
256 					    gpio_pin_t pin,
257 					    enum gpio_int_mode mode,
258 					    enum gpio_int_trig trig)
259 {
260 	const struct gpio_mcux_lpc_config *config = dev->config;
261 	enum nxp_pint_trigger interrupt_mode = NXP_PINT_NONE;
262 	uint32_t port = config->port_no;
263 	int ret;
264 
265 	switch (mode) {
266 	case GPIO_INT_MODE_DISABLED:
267 		nxp_pint_pin_disable((port * 32) + pin);
268 		return 0;
269 	case GPIO_INT_MODE_LEVEL:
270 		if (trig == GPIO_INT_TRIG_HIGH) {
271 			interrupt_mode = NXP_PINT_HIGH;
272 		} else if (trig == GPIO_INT_TRIG_LOW) {
273 			interrupt_mode = NXP_PINT_LOW;
274 		} else {
275 			return -ENOTSUP;
276 		}
277 		break;
278 	case GPIO_INT_MODE_EDGE:
279 		if (trig == GPIO_INT_TRIG_HIGH) {
280 			interrupt_mode = NXP_PINT_RISING;
281 		} else if (trig == GPIO_INT_TRIG_LOW) {
282 			interrupt_mode = NXP_PINT_FALLING;
283 		} else {
284 			interrupt_mode = NXP_PINT_BOTH;
285 		}
286 		break;
287 	default:
288 		return -ENOTSUP;
289 	}
290 
291 	/* PINT treats GPIO pins as continuous. Each port has 32 pins */
292 	ret = nxp_pint_pin_enable((port * 32) + pin, interrupt_mode, (trig & GPIO_INT_WAKEUP));
293 	if (ret < 0) {
294 		return ret;
295 	}
296 	/* Install callback */
297 	return nxp_pint_pin_set_callback((port * 32) + pin,
298 					  gpio_mcux_lpc_pint_cb,
299 					  (struct device *)dev);
300 }
301 #endif /* CONFIG_NXP_PINT */
302 
303 
304 #if (defined(FSL_FEATURE_GPIO_HAS_INTERRUPT) && FSL_FEATURE_GPIO_HAS_INTERRUPT)
305 
gpio_mcux_lpc_module_interrupt_cfg(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)306 static int gpio_mcux_lpc_module_interrupt_cfg(const struct device *dev,
307 					      gpio_pin_t pin,
308 					      enum gpio_int_mode mode,
309 					      enum gpio_int_trig trig)
310 {
311 	const struct gpio_mcux_lpc_config *config = dev->config;
312 	gpio_interrupt_index_t int_idx;
313 	gpio_interrupt_config_t pin_config;
314 
315 	if (config->int_source == INT_SOURCE_NONE) {
316 		return -ENOTSUP;
317 	}
318 
319 	/* Route interrupt to source A or B based on interrupt source */
320 	int_idx = (config->int_source == INT_SOURCE_INTA) ?
321 			kGPIO_InterruptA : kGPIO_InterruptB;
322 
323 	/* Disable interrupt if requested */
324 	if (mode ==  GPIO_INT_MODE_DISABLED) {
325 		GPIO_PinDisableInterrupt(config->gpio_base, config->port_no, pin, int_idx);
326 		return 0;
327 	}
328 
329 	/* Set pin interrupt level */
330 	if (mode == GPIO_INT_MODE_LEVEL) {
331 		pin_config.mode = kGPIO_PinIntEnableLevel;
332 	} else if (mode == GPIO_INT_MODE_EDGE) {
333 		pin_config.mode = kGPIO_PinIntEnableEdge;
334 	} else {
335 		return -ENOTSUP;
336 	}
337 
338 	/* Set pin interrupt trigger */
339 	if (trig == GPIO_INT_TRIG_HIGH) {
340 		pin_config.polarity = kGPIO_PinIntEnableHighOrRise;
341 	} else if (trig == GPIO_INT_TRIG_LOW) {
342 		pin_config.polarity = kGPIO_PinIntEnableLowOrFall;
343 	} else {
344 		return -ENOTSUP;
345 	}
346 
347 	/* Enable interrupt with new configuration */
348 	GPIO_SetPinInterruptConfig(config->gpio_base, config->port_no, pin, &pin_config);
349 	GPIO_PinEnableInterrupt(config->gpio_base, config->port_no, pin, int_idx);
350 	return 0;
351 }
352 
353 /* GPIO module interrupt handler */
gpio_mcux_lpc_module_isr(const struct device * dev)354 void gpio_mcux_lpc_module_isr(const struct device *dev)
355 {
356 	const struct gpio_mcux_lpc_config *config = dev->config;
357 	struct gpio_mcux_lpc_data *data = dev->data;
358 	uint32_t status;
359 
360 	status = GPIO_PortGetInterruptStatus(config->gpio_base,
361 					config->port_no,
362 					config->int_source == INT_SOURCE_INTA ?
363 					kGPIO_InterruptA : kGPIO_InterruptB);
364 	GPIO_PortClearInterruptFlags(config->gpio_base,
365 					config->port_no,
366 					config->int_source == INT_SOURCE_INTA ?
367 					kGPIO_InterruptA : kGPIO_InterruptB,
368 					status);
369 	gpio_fire_callbacks(&data->callbacks, dev, status);
370 }
371 
372 #endif /* FSL_FEATURE_GPIO_HAS_INTERRUPT */
373 
374 
gpio_mcux_lpc_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)375 static int gpio_mcux_lpc_pin_interrupt_configure(const struct device *dev,
376 						 gpio_pin_t pin,
377 						 enum gpio_int_mode mode,
378 						 enum gpio_int_trig trig)
379 {
380 	const struct gpio_mcux_lpc_config *config = dev->config;
381 	GPIO_Type *gpio_base = config->gpio_base;
382 	uint32_t port = config->port_no;
383 
384 	/* Ensure pin used as interrupt is set as input*/
385 	if ((mode & GPIO_INT_ENABLE) &&
386 		((gpio_base->DIR[port] & BIT(pin)) != 0)) {
387 		return -ENOTSUP;
388 	}
389 #if defined(CONFIG_NXP_PINT)
390 	if (config->int_source == INT_SOURCE_PINT) {
391 		return gpio_mcux_lpc_pint_interrupt_cfg(dev, pin, mode, trig);
392 	}
393 #endif /* CONFIG_NXP_PINT */
394 #if (defined(FSL_FEATURE_GPIO_HAS_INTERRUPT) && FSL_FEATURE_GPIO_HAS_INTERRUPT)
395 	return gpio_mcux_lpc_module_interrupt_cfg(dev, pin, mode, trig);
396 #else
397 	return -ENOTSUP;
398 #endif
399 }
400 
gpio_mcux_lpc_manage_cb(const struct device * port,struct gpio_callback * callback,bool set)401 static int gpio_mcux_lpc_manage_cb(const struct device *port,
402 				   struct gpio_callback *callback, bool set)
403 {
404 	struct gpio_mcux_lpc_data *data = port->data;
405 
406 	return gpio_manage_callback(&data->callbacks, callback, set);
407 }
408 
gpio_mcux_lpc_init(const struct device * dev)409 static int gpio_mcux_lpc_init(const struct device *dev)
410 {
411 	const struct gpio_mcux_lpc_config *config = dev->config;
412 	GPIO_PortInit(config->gpio_base, config->port_no);
413 
414 	return 0;
415 }
416 
417 static const struct gpio_driver_api gpio_mcux_lpc_driver_api = {
418 	.pin_configure = gpio_mcux_lpc_configure,
419 	.port_get_raw = gpio_mcux_lpc_port_get_raw,
420 	.port_set_masked_raw = gpio_mcux_lpc_port_set_masked_raw,
421 	.port_set_bits_raw = gpio_mcux_lpc_port_set_bits_raw,
422 	.port_clear_bits_raw = gpio_mcux_lpc_port_clear_bits_raw,
423 	.port_toggle_bits = gpio_mcux_lpc_port_toggle_bits,
424 	.pin_interrupt_configure = gpio_mcux_lpc_pin_interrupt_configure,
425 	.manage_callback = gpio_mcux_lpc_manage_cb,
426 };
427 
428 
429 
430 #ifdef IOPCTL
431 #define PINMUX_BASE	IOPCTL
432 #endif
433 #ifdef IOCON
434 #define PINMUX_BASE	IOCON
435 #endif
436 #ifdef MCI_IO_MUX
437 #define PINMUX_BASE	MCI_IO_MUX
438 #endif
439 
440 #define GPIO_MCUX_LPC_MODULE_IRQ_CONNECT(inst)						\
441 	do {										\
442 		IRQ_CONNECT(DT_INST_IRQ(inst, irq),					\
443 			DT_INST_IRQ(inst, priority),					\
444 			gpio_mcux_lpc_module_isr, DEVICE_DT_INST_GET(inst), 0);		\
445 		irq_enable(DT_INST_IRQ(inst, irq));					\
446 	} while (false)
447 
448 #define GPIO_MCUX_LPC_MODULE_IRQ(inst)							\
449 	IF_ENABLED(DT_INST_IRQ_HAS_IDX(inst, 0),					\
450 		(GPIO_MCUX_LPC_MODULE_IRQ_CONNECT(inst)))
451 
452 
453 #define GPIO_MCUX_LPC(n)								\
454 	static int lpc_gpio_init_##n(const struct device *dev);				\
455 											\
456 	static const struct gpio_mcux_lpc_config gpio_mcux_lpc_config_##n = {		\
457 		.common = {								\
458 			.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),		\
459 		},									\
460 		.gpio_base = (GPIO_Type *)DT_REG_ADDR(DT_INST_PARENT(n)),		\
461 		.pinmux_base = PINMUX_BASE,						\
462 		.int_source = DT_INST_ENUM_IDX(n, int_source),				\
463 		.port_no = DT_INST_REG_ADDR(n)						\
464 	};										\
465 											\
466 	static struct gpio_mcux_lpc_data gpio_mcux_lpc_data_##n;			\
467 											\
468 	DEVICE_DT_INST_DEFINE(n, lpc_gpio_init_##n, NULL,				\
469 		    &gpio_mcux_lpc_data_##n,						\
470 		    &gpio_mcux_lpc_config_##n, PRE_KERNEL_1,				\
471 		    CONFIG_GPIO_INIT_PRIORITY,						\
472 		    &gpio_mcux_lpc_driver_api);						\
473 											\
474 	static int lpc_gpio_init_##n(const struct device *dev)				\
475 	{										\
476 		gpio_mcux_lpc_init(dev);						\
477 		GPIO_MCUX_LPC_MODULE_IRQ(n);						\
478 											\
479 		return 0;								\
480 	}
481 
482 DT_INST_FOREACH_STATUS_OKAY(GPIO_MCUX_LPC)
483