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