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