1 /*
2  * Copyright (c) 2021 Teslabs Engineering S.L.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT gd_gd32_exti
8 
9 #include <errno.h>
10 
11 #include <zephyr/device.h>
12 #include <zephyr/drivers/interrupt_controller/gd32_exti.h>
13 #include <zephyr/sys/__assert.h>
14 #include <zephyr/irq.h>
15 #include <zephyr/sys/util_macro.h>
16 
17 #include <gd32_exti.h>
18 
19 /** Unsupported line indicator */
20 #define EXTI_NOTSUP 0xFFU
21 
22 /** Number of EXTI lines. */
23 #define NUM_EXTI_LINES DT_INST_PROP(0, num_lines)
24 
25 /** @brief EXTI line ranges hold by a single ISR */
26 struct gd32_exti_range {
27 	/** Start of the range */
28 	uint8_t min;
29 	/** End of the range */
30 	uint8_t max;
31 };
32 
33 /** @brief EXTI line interrupt callback. */
34 struct gd32_cb_data {
35 	/** Callback function */
36 	gd32_exti_cb_t cb;
37 	/** User data. */
38 	void *user;
39 };
40 
41 /** EXTI driver data. */
42 struct gd32_exti_data {
43 	/** Array of callbacks. */
44 	struct gd32_cb_data cbs[NUM_EXTI_LINES];
45 };
46 
47 #ifdef CONFIG_GPIO_GD32
48 static const struct gd32_exti_range line0_range = {0U, 0U};
49 static const struct gd32_exti_range line1_range = {1U, 1U};
50 static const struct gd32_exti_range line2_range = {2U, 2U};
51 static const struct gd32_exti_range line3_range = {3U, 3U};
52 static const struct gd32_exti_range line4_range = {4U, 4U};
53 static const struct gd32_exti_range line5_9_range = {5U, 9U};
54 static const struct gd32_exti_range line10_15_range = {10U, 15U};
55 #endif /* CONFIG_GPIO_GD32 */
56 
57 /** @brief Obtain line IRQ number if enabled. */
58 #define EXTI_LINE_IRQ_COND(enabled, line) \
59 	COND_CODE_1(enabled, (DT_INST_IRQ_BY_NAME(0, line, irq)), (EXTI_NOTSUP))
60 
61 static const uint8_t line2irq[NUM_EXTI_LINES] = {
62 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line0),
63 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line1),
64 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line2),
65 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line3),
66 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line4),
67 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9),
68 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9),
69 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9),
70 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9),
71 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line5_9),
72 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15),
73 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15),
74 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15),
75 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15),
76 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15),
77 	EXTI_LINE_IRQ_COND(CONFIG_GPIO_GD32, line10_15),
78 	EXTI_NOTSUP,
79 	EXTI_NOTSUP,
80 	EXTI_NOTSUP,
81 #ifdef CONFIG_SOC_SERIES_GD32F4XX
82 	EXTI_NOTSUP,
83 	EXTI_NOTSUP,
84 	EXTI_NOTSUP,
85 	EXTI_NOTSUP,
86 #endif /* CONFIG_SOC_SERIES_GD32F4XX */
87 };
88 
gd32_exti_isr(const void * isr_data)89 __unused static void gd32_exti_isr(const void *isr_data)
90 {
91 	const struct device *const dev = DEVICE_DT_INST_GET(0);
92 	struct gd32_exti_data *data = dev->data;
93 	const struct gd32_exti_range *range = isr_data;
94 
95 	for (uint8_t i = range->min; i <= range->max; i++) {
96 		if ((EXTI_PD & BIT(i)) != 0U) {
97 			EXTI_PD = BIT(i);
98 
99 			if (data->cbs[i].cb != NULL) {
100 				data->cbs[i].cb(i, data->cbs[i].user);
101 			}
102 		}
103 	}
104 }
105 
gd32_exti_enable(uint8_t line)106 void gd32_exti_enable(uint8_t line)
107 {
108 	__ASSERT_NO_MSG(line < NUM_EXTI_LINES);
109 	__ASSERT_NO_MSG(line2irq[line] != EXTI_NOTSUP);
110 
111 	EXTI_INTEN |= BIT(line);
112 
113 	irq_enable(line2irq[line]);
114 }
115 
gd32_exti_disable(uint8_t line)116 void gd32_exti_disable(uint8_t line)
117 {
118 	__ASSERT_NO_MSG(line < NUM_EXTI_LINES);
119 	__ASSERT_NO_MSG(line2irq[line] != EXTI_NOTSUP);
120 
121 	EXTI_INTEN &= ~BIT(line);
122 }
123 
gd32_exti_trigger(uint8_t line,uint8_t trigger)124 void gd32_exti_trigger(uint8_t line, uint8_t trigger)
125 {
126 	__ASSERT_NO_MSG(line < NUM_EXTI_LINES);
127 	__ASSERT_NO_MSG(line2irq[line] != EXTI_NOTSUP);
128 
129 	if ((trigger & GD32_EXTI_TRIG_RISING) != 0U) {
130 		EXTI_RTEN |= BIT(line);
131 	} else {
132 		EXTI_RTEN &= ~BIT(line);
133 	}
134 
135 	if ((trigger & GD32_EXTI_TRIG_FALLING) != 0U) {
136 		EXTI_FTEN |= BIT(line);
137 	} else {
138 		EXTI_FTEN &= ~BIT(line);
139 	}
140 }
141 
gd32_exti_configure(uint8_t line,gd32_exti_cb_t cb,void * user)142 int gd32_exti_configure(uint8_t line, gd32_exti_cb_t cb, void *user)
143 {
144 	const struct device *const dev = DEVICE_DT_INST_GET(0);
145 	struct gd32_exti_data *data = dev->data;
146 
147 	__ASSERT_NO_MSG(line < NUM_EXTI_LINES);
148 	__ASSERT_NO_MSG(line2irq[line] != EXTI_NOTSUP);
149 
150 	if ((data->cbs[line].cb != NULL) && (cb != NULL)) {
151 		return -EALREADY;
152 	}
153 
154 	data->cbs[line].cb = cb;
155 	data->cbs[line].user = user;
156 
157 	return 0;
158 }
159 
gd32_exti_init(const struct device * dev)160 static int gd32_exti_init(const struct device *dev)
161 {
162 #ifdef CONFIG_GPIO_GD32
163 	IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line0, irq),
164 		    DT_INST_IRQ_BY_NAME(0, line0, priority),
165 		    gd32_exti_isr, &line0_range, 0);
166 
167 	IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line1, irq),
168 		    DT_INST_IRQ_BY_NAME(0, line1, priority),
169 		    gd32_exti_isr, &line1_range, 0);
170 
171 	IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line2, irq),
172 		    DT_INST_IRQ_BY_NAME(0, line2, priority),
173 		    gd32_exti_isr, &line2_range, 0);
174 
175 	IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line3, irq),
176 		    DT_INST_IRQ_BY_NAME(0, line3, priority),
177 		    gd32_exti_isr, &line3_range, 0);
178 
179 	IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line4, irq),
180 		    DT_INST_IRQ_BY_NAME(0, line4, priority),
181 		    gd32_exti_isr, &line4_range, 0);
182 
183 	IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line5_9, irq),
184 		    DT_INST_IRQ_BY_NAME(0, line5_9, priority),
185 		    gd32_exti_isr, &line5_9_range, 0);
186 
187 	IRQ_CONNECT(DT_INST_IRQ_BY_NAME(0, line10_15, irq),
188 		    DT_INST_IRQ_BY_NAME(0, line10_15, priority),
189 		    gd32_exti_isr, &line10_15_range, 0);
190 #endif /* CONFIG_GPIO_GD32 */
191 
192 	return 0;
193 }
194 
195 static struct gd32_exti_data data;
196 
197 DEVICE_DT_INST_DEFINE(0, gd32_exti_init, NULL, &data, NULL, PRE_KERNEL_1,
198 		      CONFIG_INTC_INIT_PRIORITY, NULL);
199