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