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