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