1 /*
2 * Copyright (c) 2020 Seagate Technology LLC
3 * Copyright 2022 NXP
4 *
5 * SPDX-License-Identifier: Apache-2.0
6 */
7
8 #define DT_DRV_COMPAT nxp_lpc11u6x_gpio
9
10 /**
11 * @file
12 * @brief GPIO driver for NXP LPC11U6X SoCs
13 *
14 * This driver allows to configure the GPIOs found on the LPC11U6x MCUs.
15 *
16 * @note See the UM10732 LPC11U6x/E6x user manual for register definitions.
17 */
18
19 #include <zephyr/drivers/clock_control.h>
20 #include <zephyr/drivers/gpio.h>
21 #include <zephyr/irq.h>
22
23 #include <soc.h>
24
25 #include <zephyr/drivers/gpio/gpio_utils.h>
26
27 /* Offset from syscon base address. */
28 #define LPC11U6X_PINTSEL_REGS 0x178
29
30 /* Offsets from GPIO base address. */
31 #define LPC11U6X_GPIO_REGS 0x2000
32 #define LPC11U6X_PINT_REGS 0x4000
33
34 /**
35 * @brief Structure mapping the GPIO registers.
36 *
37 * @note The byte and word pin registers are not included because they are
38 * not used by this driver. A 0x2000 offset is applied to skip them.
39 */
40 struct lpc11u6x_gpio_regs {
41 volatile uint32_t dir[3];
42 volatile uint32_t _unused1[29];
43 volatile uint32_t mask[3];
44 volatile uint32_t _unused2[29];
45 volatile uint32_t pin[3];
46 volatile uint32_t _unused3[29];
47 volatile uint32_t mpin[3];
48 volatile uint32_t _unused4[29];
49 volatile uint32_t set[3];
50 volatile uint32_t _unused5[29];
51 volatile uint32_t clr[3];
52 volatile uint32_t _unused6[29];
53 volatile uint32_t not[3];
54 };
55
56 /**
57 * @brief Structure mapping the PINT registers.
58 */
59 struct lpc11u6x_pint_regs {
60 volatile uint32_t isel;
61 volatile uint32_t ienr;
62 volatile uint32_t sienr;
63 volatile uint32_t cienr;
64 volatile uint32_t ienf;
65 volatile uint32_t sienf;
66 volatile uint32_t cienf;
67 volatile uint32_t rise;
68 volatile uint32_t fall;
69 volatile uint32_t ist;
70 volatile uint32_t pmctrl;
71 volatile uint32_t pmsrc;
72 volatile uint32_t pmcfg;
73 };
74
75 /**
76 * @brief Structure for resources and information shared between GPIO ports.
77 *
78 * This structure is included by all the per-port private configuration.
79 * It gathers all the resources and information shared between all the GPIO
80 * ports: GPIO and SYSCON registers base addresses, clock name and subsystem.
81 */
82 struct gpio_lpc11u6x_shared {
83 const struct device *clock_dev;
84 clock_control_subsys_t clock_subsys;
85 uint32_t gpio_base;
86 uint32_t syscon_base;
87 uint8_t nirqs;
88 };
89
90 struct gpio_lpc11u6x_config {
91 /* gpio_driver_config needs to be first */
92 struct gpio_driver_config common;
93 const struct gpio_lpc11u6x_shared *shared;
94 uint8_t port_num;
95 uint8_t ngpios;
96 volatile uint32_t *iocon_base;
97 };
98
99 struct gpio_lpc11u6x_data {
100 /* gpio_driver_data needs to be first. */
101 struct gpio_driver_data common;
102 sys_slist_t cb_list;
103 };
104
gpio_lpc11u6x_pin_configure(const struct device * port,gpio_pin_t pin,gpio_flags_t flags)105 static int gpio_lpc11u6x_pin_configure(const struct device *port,
106 gpio_pin_t pin, gpio_flags_t flags)
107 {
108 const struct gpio_lpc11u6x_config *config = port->config;
109 struct lpc11u6x_gpio_regs *gpio_regs = (struct lpc11u6x_gpio_regs *)
110 (config->shared->gpio_base + LPC11U6X_GPIO_REGS);
111 uint8_t port_num = config->port_num;
112 uint32_t offset;
113 uint32_t func;
114
115 if (pin >= config->ngpios) {
116 return -EINVAL;
117 }
118
119 /*
120 * PIO0_4 and PIO0_5 are "true" open drain pins muxed with the I2C port
121 * 0. They still can be configured as GPIOs but only in open drain mode
122 * and with no pull-down or pull-up resistor enabled.
123 */
124 if (port_num == 0 && (pin == 4 || pin == 5) &&
125 ((flags & GPIO_OPEN_DRAIN) == 0 ||
126 (flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)))) {
127 return -EINVAL;
128 }
129
130 /*
131 * For PIO0_0 and PIO0_[10-15] function 1 enables GPIO mode. For all
132 * the other pins, function 0 must be selected.
133 */
134 if (port_num == 0 && (pin == 0 || (pin >= 10 && pin <= 15))) {
135 func = IOCON_FUNC1;
136 } else {
137 func = IOCON_FUNC0;
138 }
139
140 if (flags & GPIO_SINGLE_ENDED) {
141 /* Open source mode is not supported. */
142 if (flags & GPIO_LINE_OPEN_DRAIN) {
143 func |= IOCON_PIO_OD(1);
144 } else {
145 return -ENOTSUP;
146 }
147 }
148
149 if (flags & GPIO_PULL_UP) {
150 func |= IOCON_PIO_MODE(0x2);
151 } else if (flags & GPIO_PULL_DOWN) {
152 func |= IOCON_PIO_MODE(0x1);
153 } else {
154 func |= IOCON_PIO_MODE(0x0);
155 }
156
157 /* Handle 4 bytes hole between PIO2_1 and PIO2_2. */
158 if (port_num == 2 && pin > 1) {
159 offset = pin + 1;
160 } else {
161 offset = pin;
162 }
163 /* iocon base + offset gives configuration register for this pin */
164 config->iocon_base[offset] = func;
165
166 /* Initial output value. */
167 if (flags & GPIO_OUTPUT_INIT_HIGH) {
168 gpio_regs->set[port_num] |= BIT(pin);
169 }
170
171 if (flags & GPIO_OUTPUT_INIT_LOW) {
172 gpio_regs->clr[port_num] |= BIT(pin);
173 }
174
175 /*
176 * TODO: maybe configure the STARTERP0 register to allow wake-up from
177 * deep-sleep or power-down modes.
178 */
179
180 /* Configure GPIO direction. */
181 WRITE_BIT(gpio_regs->dir[port_num], pin, flags & GPIO_OUTPUT);
182
183 return 0;
184 }
185
gpio_lpc11u6x_port_get_raw(const struct device * port,gpio_port_value_t * value)186 static int gpio_lpc11u6x_port_get_raw(const struct device *port,
187 gpio_port_value_t *value)
188 {
189 const struct gpio_lpc11u6x_config *config = port->config;
190 struct lpc11u6x_gpio_regs *gpio_regs = (struct lpc11u6x_gpio_regs *)
191 (config->shared->gpio_base + LPC11U6X_GPIO_REGS);
192
193 *value = gpio_regs->pin[config->port_num];
194
195 return 0;
196 }
197
gpio_lpc11u6x_port_set_masked_raw(const struct device * port,gpio_port_pins_t mask,gpio_port_value_t value)198 static int gpio_lpc11u6x_port_set_masked_raw(const struct device *port,
199 gpio_port_pins_t mask,
200 gpio_port_value_t value)
201 {
202 const struct gpio_lpc11u6x_config *config = port->config;
203 struct lpc11u6x_gpio_regs *gpio_regs = (struct lpc11u6x_gpio_regs *)
204 (config->shared->gpio_base + LPC11U6X_GPIO_REGS);
205 uint8_t port_num = config->port_num;
206 uint32_t orig_mask;
207
208 orig_mask = gpio_regs->mask[port_num];
209 /* Apply inverted mask (bit set to 1 masks the pin). */
210 gpio_regs->mask[port_num] = ~mask;
211 compiler_barrier();
212 /* Update pins values. */
213 gpio_regs->mpin[port_num] = value;
214 compiler_barrier();
215 /* Restore original mask. */
216 gpio_regs->mask[port_num] = orig_mask;
217 compiler_barrier();
218
219 return 0;
220 }
221
gpio_lpc11u6x_port_set_bits_raw(const struct device * port,gpio_port_pins_t pins)222 static int gpio_lpc11u6x_port_set_bits_raw(const struct device *port,
223 gpio_port_pins_t pins)
224 {
225 const struct gpio_lpc11u6x_config *config = port->config;
226 struct lpc11u6x_gpio_regs *gpio_regs = (struct lpc11u6x_gpio_regs *)
227 (config->shared->gpio_base + LPC11U6X_GPIO_REGS);
228
229 gpio_regs->set[config->port_num] = pins;
230
231 return 0;
232 }
233
gpio_lpc11u6x_port_clear_bits_raw(const struct device * port,gpio_port_pins_t pins)234 static int gpio_lpc11u6x_port_clear_bits_raw(const struct device *port,
235 gpio_port_pins_t pins)
236 {
237 const struct gpio_lpc11u6x_config *config = port->config;
238 struct lpc11u6x_gpio_regs *gpio_regs = (struct lpc11u6x_gpio_regs *)
239 (config->shared->gpio_base + LPC11U6X_GPIO_REGS);
240
241 gpio_regs->clr[config->port_num] = pins;
242
243 return 0;
244 }
245
gpio_lpc11u6x_port_toggle_bits(const struct device * port,gpio_port_pins_t pins)246 static int gpio_lpc11u6x_port_toggle_bits(const struct device *port,
247 gpio_port_pins_t pins)
248 {
249 const struct gpio_lpc11u6x_config *config = port->config;
250 struct lpc11u6x_gpio_regs *gpio_regs = (struct lpc11u6x_gpio_regs *)
251 (config->shared->gpio_base + LPC11U6X_GPIO_REGS);
252
253 gpio_regs->not[config->port_num] = pins;
254
255 return 0;
256 }
257
258 /**
259 * @brief Attach a free interrupt line to a GPIO.
260 *
261 * @param shared Pointer to a structure shared between all the GPIO ports.
262 * @param intpin GPIO port and pin numbers encoded into a value compatible
263 * with the INTPIN register (included in the PINTSEL register).
264 *
265 * @retval >0 Number of the attached interrupt on success.
266 * @retval -EBUSY All the interrupt lines are already attached.
267 */
268 static int
pintsel_attach(const struct gpio_lpc11u6x_shared * shared,uint8_t intpin)269 pintsel_attach(const struct gpio_lpc11u6x_shared *shared, uint8_t intpin)
270 {
271 uint8_t irq;
272 int ret = -EBUSY;
273 uint32_t *pintsel_reg =
274 (uint32_t *) (shared->syscon_base + LPC11U6X_PINTSEL_REGS);
275
276 for (irq = 0; irq < shared->nirqs; irq++) {
277 /* GPIO already attached. */
278 if ((pintsel_reg[irq] & BIT_MASK(5)) == intpin) {
279 return irq;
280 }
281
282 if (ret < 0 && (pintsel_reg[irq] & BIT_MASK(5)) == 0) {
283 ret = irq;
284 }
285 }
286 /* Attach GPIO to the first free interrupt found if any. */
287 if (ret >= 0) {
288 pintsel_reg[ret] = intpin;
289 }
290
291 return ret;
292 }
293
294 /**
295 * @brief Detach an interrupt line from a GPIO.
296 *
297 * @param shared Pointer to a structure shared between all the GPIO ports.
298 * @param intpin GPIO port and pin numbers encoded into a value compatible
299 * with the INTPIN register (included in the PINTSEL register).
300 *
301 * @retval >0 Number of the detached interrupt on success.
302 * @retval -EINVAL No attached interrupt found for the requested GPIO.
303 */
304 static int
pintsel_detach(const struct gpio_lpc11u6x_shared * shared,uint8_t intpin)305 pintsel_detach(const struct gpio_lpc11u6x_shared *shared, uint8_t intpin)
306 {
307 uint8_t irq;
308 uint32_t *pintsel_reg =
309 (uint32_t *) (shared->syscon_base + LPC11U6X_PINTSEL_REGS);
310
311 for (irq = 0; irq < shared->nirqs; irq++) {
312 if ((pintsel_reg[irq] & BIT_MASK(5)) == intpin) {
313 pintsel_reg[irq] = 0;
314 return irq;
315 }
316 }
317 return -EINVAL;
318 }
319
gpio_lpc11u6x_pin_interrupt_configure(const struct device * port,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)320 static int gpio_lpc11u6x_pin_interrupt_configure(const struct device *port,
321 gpio_pin_t pin,
322 enum gpio_int_mode mode,
323 enum gpio_int_trig trig)
324 {
325 const struct gpio_lpc11u6x_config *config = port->config;
326 struct lpc11u6x_pint_regs *pint_regs = (struct lpc11u6x_pint_regs *)
327 (config->shared->gpio_base + LPC11U6X_PINT_REGS);
328 uint8_t intpin;
329 int irq;
330
331 if (pin >= config->ngpios) {
332 return -EINVAL;
333 }
334
335 /*
336 * Because the PINTSEL register only have 6 bits to encode a pin
337 * number, then PIO2_8 to PIO2_23 can't be attached to an interrupt
338 * line.
339 */
340 if (config->port_num == 2 && pin > 7) {
341 return -ENOTSUP;
342 }
343
344 /*
345 * Convert the requested GPIO port and pin numbers into a value
346 * compatible with the INTPIN register (included in the PINTSEL
347 * register).
348 */
349 intpin = pin;
350 if (config->port_num == 1) {
351 intpin += 24;
352 } else if (config->port_num == 2) {
353 intpin += 56;
354 }
355
356 if (mode == GPIO_INT_MODE_DISABLED) {
357 irq = pintsel_detach(config->shared, intpin);
358 } else {
359 irq = pintsel_attach(config->shared, intpin);
360 }
361 if (irq < 0) {
362 return irq;
363 }
364
365 switch (mode) {
366 case GPIO_INT_MODE_DISABLED:
367 pint_regs->isel &= ~BIT(irq);
368 pint_regs->cienr |= BIT(irq);
369 pint_regs->cienf |= BIT(irq);
370 break;
371 case GPIO_INT_MODE_EDGE:
372 /* Select edge interrupt mode. */
373 pint_regs->isel &= ~BIT(irq);
374 /* Enable interrupts on falling and/or rising edges. */
375 if (trig & GPIO_INT_TRIG_LOW) {
376 pint_regs->sienf |= BIT(irq);
377 } else {
378 pint_regs->cienf |= BIT(irq);
379 }
380 if (trig & GPIO_INT_TRIG_HIGH) {
381 pint_regs->sienr |= BIT(irq);
382 } else {
383 pint_regs->cienr |= BIT(irq);
384 }
385 break;
386 case GPIO_INT_MODE_LEVEL:
387 /* Select level interrupt mode. */
388 pint_regs->isel |= BIT(irq);
389 /* Set active level. */
390 if (trig & GPIO_INT_TRIG_LOW) {
391 pint_regs->cienf |= BIT(irq);
392 } else {
393 pint_regs->sienf |= BIT(irq);
394 }
395 /* Enable level interrupt. */
396 pint_regs->sienr |= BIT(irq);
397 break;
398 default:
399 return -ENOTSUP;
400 }
401
402 /* Clear interrupt status. */
403 pint_regs->ist |= BIT(irq);
404
405 return 0;
406 }
407
gpio_lpc11u6x_manage_callback(const struct device * port,struct gpio_callback * cb,bool set)408 static int gpio_lpc11u6x_manage_callback(const struct device *port,
409 struct gpio_callback *cb, bool set)
410 {
411 struct gpio_lpc11u6x_data *data = port->data;
412
413 return gpio_manage_callback(&data->cb_list, cb, set);
414 }
415
gpio_lpc11u6x_get_pending_int(const struct device * dev)416 static uint32_t gpio_lpc11u6x_get_pending_int(const struct device *dev)
417 {
418 ARG_UNUSED(dev);
419
420 return -ENOTSUP;
421 }
422
gpio_lpc11u6x_isr(const void * arg)423 static void gpio_lpc11u6x_isr(const void *arg)
424 {
425 struct gpio_lpc11u6x_shared *shared =
426 (struct gpio_lpc11u6x_shared *)arg;
427 struct lpc11u6x_pint_regs *pint_regs = (struct lpc11u6x_pint_regs *)
428 (shared->gpio_base + LPC11U6X_PINT_REGS);
429 uint32_t *pintsel_reg =
430 (uint32_t *) (shared->syscon_base + LPC11U6X_PINTSEL_REGS);
431 uint8_t irq;
432 uint32_t pins[3] = { 0, 0, 0 };
433 const struct device *port;
434 struct gpio_lpc11u6x_data *data;
435
436 for (irq = 0; irq < shared->nirqs; irq++) {
437 uint32_t intpin;
438
439 if ((pint_regs->ist & BIT(irq)) == 0) {
440 continue;
441 }
442
443 /* Clear interrupt status. */
444 pint_regs->ist |= BIT(irq);
445
446 /*
447 * Look in the PINTSEL register to retrieve the "intpin" value
448 * attached with the requested interrupt. Extract the GPIO
449 * port and pin numbers from this "intpin" value and store them
450 * into an "active pins" mask.
451 */
452 intpin = pintsel_reg[irq] & BIT_MASK(5);
453 if (intpin < 24) {
454 pins[0] |= BIT(intpin);
455 } else if (intpin < 56) {
456 pins[1] |= BIT(intpin - 24);
457 } else {
458 pins[2] |= BIT(intpin - 56);
459 }
460 }
461 /* For each port with active pins, fire the GPIO interrupt callbacks. */
462 #if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio0), okay)
463 if (pins[0]) {
464 port = DEVICE_DT_GET(DT_NODELABEL(gpio0));
465 data = port->data;
466 gpio_fire_callbacks(&data->cb_list, port, pins[0]);
467 }
468 #endif
469 #if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay)
470 if (pins[1]) {
471 port = DEVICE_DT_GET(DT_NODELABEL(gpio1));
472 data = port->data;
473 gpio_fire_callbacks(&data->cb_list, port, pins[1]);
474 }
475 #endif
476 #if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio2), okay)
477 if (pins[2]) {
478 port = DEVICE_DT_GET(DT_NODELABEL(gpio2));
479 data = port->data;
480 gpio_fire_callbacks(&data->cb_list, port, pins[2]);
481 }
482 #endif
483 }
484
485 static const struct gpio_driver_api gpio_lpc11u6x_driver_api = {
486 .pin_configure = gpio_lpc11u6x_pin_configure,
487 .port_get_raw = gpio_lpc11u6x_port_get_raw,
488 .port_set_masked_raw = gpio_lpc11u6x_port_set_masked_raw,
489 .port_set_bits_raw = gpio_lpc11u6x_port_set_bits_raw,
490 .port_clear_bits_raw = gpio_lpc11u6x_port_clear_bits_raw,
491 .port_toggle_bits = gpio_lpc11u6x_port_toggle_bits,
492 .pin_interrupt_configure = gpio_lpc11u6x_pin_interrupt_configure,
493 .manage_callback = gpio_lpc11u6x_manage_callback,
494 .get_pending_int = gpio_lpc11u6x_get_pending_int,
495 };
496
497 /*
498 * Note that the first DT instance is used to initialize the resources
499 * shared between all the ports (IRQ lines, clock).
500 */
501
502 static const struct gpio_lpc11u6x_shared gpio_lpc11u6x_shared = {
503 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(0)),
504 .clock_subsys = (clock_control_subsys_t) DT_INST_PHA(0, clocks, clkid),
505 .gpio_base = DT_INST_REG_ADDR_BY_IDX(0, 0),
506 .syscon_base = DT_INST_REG_ADDR_BY_IDX(0, 1),
507 .nirqs = DT_NUM_IRQS(DT_DRV_INST(0)),
508 };
509
510 #define IRQ_INIT(n) \
511 do { \
512 IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, n, irq), \
513 DT_INST_IRQ_BY_IDX(0, n, priority), \
514 gpio_lpc11u6x_isr, &gpio_lpc11u6x_shared, 0); \
515 irq_enable(DT_INST_IRQ_BY_IDX(0, n, irq)); \
516 } while (false)
517
gpio_lpc11u6x_init(const struct device * dev)518 static int gpio_lpc11u6x_init(const struct device *dev)
519 {
520 const struct gpio_lpc11u6x_config *config = dev->config;
521 int ret;
522 static bool gpio_ready;
523
524 /* Initialize shared resources only once. */
525 if (gpio_ready) {
526 return 0;
527 }
528
529 if (!device_is_ready(config->shared->clock_dev)) {
530 return -ENODEV;
531 }
532
533 /* Enable GPIO and PINT clocks. */
534 ret = clock_control_on(config->shared->clock_dev, config->shared->clock_subsys);
535 if (ret < 0) {
536 return ret;
537 }
538
539 #if DT_INST_IRQ_HAS_IDX(0, 0)
540 IRQ_INIT(0);
541 #endif
542 #if DT_INST_IRQ_HAS_IDX(0, 1)
543 IRQ_INIT(1);
544 #endif
545 #if DT_INST_IRQ_HAS_IDX(0, 2)
546 IRQ_INIT(2);
547 #endif
548 #if DT_INST_IRQ_HAS_IDX(0, 3)
549 IRQ_INIT(3);
550 #endif
551 #if DT_INST_IRQ_HAS_IDX(0, 4)
552 IRQ_INIT(4);
553 #endif
554 #if DT_INST_IRQ_HAS_IDX(0, 5)
555 IRQ_INIT(5);
556 #endif
557 #if DT_INST_IRQ_HAS_IDX(0, 6)
558 IRQ_INIT(6);
559 #endif
560 #if DT_INST_IRQ_HAS_IDX(0, 7)
561 IRQ_INIT(7);
562 #endif
563 gpio_ready = true;
564
565 return 0;
566 }
567
568 #define GPIO_LPC11U6X_INIT(id) \
569 static const struct gpio_lpc11u6x_config \
570 gpio_lpc11u6x_config_##id = { \
571 .common = { \
572 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_NODE( \
573 DT_NODELABEL(gpio##id)), \
574 }, \
575 .shared = &gpio_lpc11u6x_shared, \
576 .port_num = id, \
577 .ngpios = DT_PROP(DT_NODELABEL(gpio##id), ngpios), \
578 .iocon_base = (volatile uint32_t *)DT_REG_ADDR( \
579 DT_INST_PHANDLE(id, iocon)), \
580 }; \
581 \
582 static struct gpio_lpc11u6x_data gpio_lpc11u6x_data_##id; \
583 \
584 DEVICE_DT_DEFINE(DT_NODELABEL(gpio##id), \
585 &gpio_lpc11u6x_init, \
586 NULL, \
587 &gpio_lpc11u6x_data_##id, \
588 &gpio_lpc11u6x_config_##id, \
589 PRE_KERNEL_2, CONFIG_GPIO_INIT_PRIORITY, \
590 &gpio_lpc11u6x_driver_api)
591
592 #if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio0), okay)
593 GPIO_LPC11U6X_INIT(0);
594 #endif
595
596 #if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio1), okay)
597 GPIO_LPC11U6X_INIT(1);
598 #endif
599
600 #if DT_NODE_HAS_STATUS(DT_NODELABEL(gpio2), okay)
601 GPIO_LPC11U6X_INIT(2);
602 #endif
603