1 /*
2 * Copyright 2023 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 /* Based on STM32 EXTI driver, which is (c) 2016 Open-RnD Sp. z o.o. */
8
9 #include <zephyr/device.h>
10 #include <zephyr/irq.h>
11 #include <errno.h>
12 #include <zephyr/drivers/interrupt_controller/nxp_pint.h>
13
14 #include <fsl_inputmux.h>
15
16 #define DT_DRV_COMPAT nxp_pint
17
18 static PINT_Type *pint_base = (PINT_Type *)DT_INST_REG_ADDR(0);
19
20 /* Describes configuration of PINT IRQ slot */
21 struct pint_irq_slot {
22 nxp_pint_cb_t callback;
23 void *user_data;
24 uint8_t pin: 6;
25 uint8_t used: 1;
26 };
27
28 #define NO_PINT_ID 0xFF
29
30 /* Tracks IRQ configuration for each pint interrupt source */
31 static struct pint_irq_slot pint_irq_cfg[DT_INST_PROP(0, num_lines)];
32 /* Tracks pint interrupt source selected for each pin */
33 static uint8_t pin_pint_id[DT_INST_PROP(0, num_inputs)];
34
35 #define PIN_TO_INPUT_MUX_CONNECTION(pin) \
36 ((PINTSEL_PMUX_ID << PMUX_SHIFT) + (pin))
37
38 /* Attaches pin to PINT IRQ slot using INPUTMUX */
attach_pin_to_pint(uint8_t pin,uint8_t pint_slot)39 static void attach_pin_to_pint(uint8_t pin, uint8_t pint_slot)
40 {
41 INPUTMUX_Init(INPUTMUX);
42
43 /* Three parameters here- INPUTMUX base, the ID of the PINT slot,
44 * and a integer describing the GPIO pin.
45 */
46 INPUTMUX_AttachSignal(INPUTMUX, pint_slot,
47 PIN_TO_INPUT_MUX_CONNECTION(pin));
48
49 /* Disable INPUTMUX after making changes, this gates clock and
50 * saves power.
51 */
52 INPUTMUX_Deinit(INPUTMUX);
53 }
54
55 /**
56 * @brief Enable PINT interrupt source.
57 *
58 * @param pin: pin to use as interrupt source
59 * 0-64, corresponding to GPIO0 pin 1 - GPIO1 pin 31)
60 * @param trigger: one of nxp_pint_trigger flags
61 * @return 0 on success, or negative value on error
62 */
nxp_pint_pin_enable(uint8_t pin,enum nxp_pint_trigger trigger)63 int nxp_pint_pin_enable(uint8_t pin, enum nxp_pint_trigger trigger)
64 {
65 uint8_t slot = 0U;
66
67 if (pin > ARRAY_SIZE(pin_pint_id)) {
68 /* Invalid pin ID */
69 return -EINVAL;
70 }
71 /* Find unused IRQ slot */
72 if (pin_pint_id[pin] != NO_PINT_ID) {
73 slot = pin_pint_id[pin];
74 } else {
75 for (slot = 0; slot < ARRAY_SIZE(pint_irq_cfg); slot++) {
76 if (!pint_irq_cfg[slot].used) {
77 break;
78 }
79 }
80 if (slot == ARRAY_SIZE(pint_irq_cfg)) {
81 /* No free IRQ slots */
82 return -EBUSY;
83 }
84 pin_pint_id[pin] = slot;
85 }
86 pint_irq_cfg[slot].used = true;
87 pint_irq_cfg[slot].pin = pin;
88 /* Attach pin to interrupt slot using INPUTMUX */
89 attach_pin_to_pint(pin, slot);
90 /* Now configure the interrupt. No need to install callback, this
91 * driver handles the IRQ
92 */
93 PINT_PinInterruptConfig(pint_base, slot, trigger, NULL);
94 return 0;
95 }
96
97
98 /**
99 * @brief disable PINT interrupt source.
100 *
101 * @param pin: pin interrupt source to disable
102 */
nxp_pint_pin_disable(uint8_t pin)103 void nxp_pint_pin_disable(uint8_t pin)
104 {
105 uint8_t slot;
106
107 if (pin > ARRAY_SIZE(pin_pint_id)) {
108 return;
109 }
110
111 slot = pin_pint_id[pin];
112 if (slot == NO_PINT_ID) {
113 return;
114 }
115 /* Remove this pin from the PINT slot if one was in use */
116 pint_irq_cfg[slot].used = false;
117 PINT_PinInterruptConfig(pint_base, slot, kPINT_PinIntEnableNone, NULL);
118 }
119
120 /**
121 * @brief Install PINT callback
122 *
123 * @param pin: interrupt source to install callback for
124 * @param cb: callback to install
125 * @param data: user data to include in callback
126 * @return 0 on success, or negative value on error
127 */
nxp_pint_pin_set_callback(uint8_t pin,nxp_pint_cb_t cb,void * data)128 int nxp_pint_pin_set_callback(uint8_t pin, nxp_pint_cb_t cb, void *data)
129 {
130 uint8_t slot;
131
132 if (pin > ARRAY_SIZE(pin_pint_id)) {
133 return -EINVAL;
134 }
135
136 slot = pin_pint_id[pin];
137 if (slot == NO_PINT_ID) {
138 return -EINVAL;
139 }
140
141 pint_irq_cfg[slot].callback = cb;
142 pint_irq_cfg[slot].user_data = data;
143 return 0;
144 }
145
146 /**
147 * @brief Remove PINT callback
148 *
149 * @param pin: interrupt source to remove callback for
150 */
nxp_pint_pin_unset_callback(uint8_t pin)151 void nxp_pint_pin_unset_callback(uint8_t pin)
152 {
153 uint8_t slot;
154
155 if (pin > ARRAY_SIZE(pin_pint_id)) {
156 return;
157 }
158
159 slot = pin_pint_id[pin];
160 if (slot == NO_PINT_ID) {
161 return;
162 }
163
164 pint_irq_cfg[slot].callback = NULL;
165 }
166
167 /* NXP PINT ISR handler- called with PINT slot ID */
nxp_pint_isr(uint8_t * slot)168 static void nxp_pint_isr(uint8_t *slot)
169 {
170 PINT_PinInterruptClrStatus(pint_base, *slot);
171 if (pint_irq_cfg[*slot].used && pint_irq_cfg[*slot].callback) {
172 pint_irq_cfg[*slot].callback(pint_irq_cfg[*slot].pin,
173 pint_irq_cfg[*slot].user_data);
174 }
175 }
176
177
178 /* Defines PINT IRQ handler for a given irq index */
179 #define NXP_PINT_IRQ(idx, node_id) \
180 IF_ENABLED(DT_IRQ_HAS_IDX(node_id, idx), \
181 (static uint8_t nxp_pint_idx_##idx = idx; \
182 do { \
183 IRQ_CONNECT(DT_IRQ_BY_IDX(node_id, idx, irq), \
184 DT_IRQ_BY_IDX(node_id, idx, priority), \
185 nxp_pint_isr, &nxp_pint_idx_##idx, 0); \
186 irq_enable(DT_IRQ_BY_IDX(node_id, idx, irq)); \
187 } while (false)))
188
intc_nxp_pint_init(const struct device * dev)189 static int intc_nxp_pint_init(const struct device *dev)
190 {
191 /* First, connect IRQs for each interrupt.
192 * The IRQ handler will receive the PINT slot as a
193 * parameter.
194 */
195 LISTIFY(8, NXP_PINT_IRQ, (;), DT_INST(0, DT_DRV_COMPAT));
196 PINT_Init(pint_base);
197 memset(pin_pint_id, NO_PINT_ID, ARRAY_SIZE(pin_pint_id));
198 return 0;
199 }
200
201 DEVICE_DT_INST_DEFINE(0, intc_nxp_pint_init, NULL, NULL, NULL,
202 PRE_KERNEL_1, CONFIG_INTC_INIT_PRIORITY, NULL);
203