1 /*
2 * Copyright (c) 2022 SEAL AG
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT nuvoton_numicro_gpio
8
9 #include <errno.h>
10 #include <stdint.h>
11 #include <zephyr/device.h>
12 #include <zephyr/devicetree.h>
13 #include <zephyr/irq.h>
14 #include <zephyr/drivers/gpio.h>
15 #include <zephyr/drivers/gpio/gpio_utils.h>
16 #include <zephyr/dt-bindings/gpio/numicro-gpio.h>
17 #include <NuMicro.h>
18
19 #define MODE_PIN_SHIFT(pin) ((pin) * 2)
20 #define MODE_MASK(pin) (3 << MODE_PIN_SHIFT(pin))
21 #define DINOFF_PIN_SHIFT(pin) ((pin) + 16)
22 #define DINOFF_MASK(pin) (1 << DINOFF_PIN_SHIFT(pin))
23 #define PUSEL_PIN_SHIFT(pin) ((pin) * 2)
24 #define PUSEL_MASK(pin) (3 << PUSEL_PIN_SHIFT(pin))
25
26 #define PORT_PIN_MASK 0xFFFF
27
28 struct gpio_numicro_config {
29 /* gpio_driver_config needs to be first */
30 struct gpio_driver_config common;
31 GPIO_T *regs;
32 };
33
34 struct gpio_numicro_data {
35 /* gpio_driver_data needs to be first */
36 struct gpio_driver_data common;
37 /* port ISR callback routine address */
38 sys_slist_t callbacks;
39 #ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT
40 /*
41 * backup of the INTEN register.
42 * The higher half is RHIEN for whether rising trigger is enabled, and
43 * the lower half is FLIEN for whether falling trigger is enabled.
44 */
45 uint32_t interrupt_en_reg_bak;
46 #endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */
47 };
48
gpio_numicro_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)49 static int gpio_numicro_configure(const struct device *dev,
50 gpio_pin_t pin, gpio_flags_t flags)
51 {
52 const struct gpio_numicro_config *cfg = dev->config;
53 GPIO_T * const regs = cfg->regs;
54
55 uint32_t mode;
56 uint32_t debounce_enable = 0;
57 uint32_t schmitt_enable = 0;
58 uint32_t disable_input_path = 0;
59 uint32_t bias = GPIO_PUSEL_DISABLE;
60
61 /* Pin mode */
62 if ((flags & GPIO_OUTPUT) != 0) {
63 /* Output */
64
65 if ((flags & GPIO_SINGLE_ENDED) != 0) {
66 if ((flags & GPIO_LINE_OPEN_DRAIN) != 0) {
67 mode = GPIO_MODE_OPEN_DRAIN;
68 } else {
69 /* Output can't be open source */
70 return -ENOTSUP;
71 }
72 } else {
73 mode = GPIO_MODE_OUTPUT;
74 }
75 } else if ((flags & GPIO_INPUT) != 0) {
76 /* Input */
77
78 mode = GPIO_MODE_INPUT;
79
80 if ((flags & NUMICRO_GPIO_INPUT_DEBOUNCE) != 0) {
81 debounce_enable = 1;
82 }
83
84 if ((flags & NUMICRO_GPIO_INPUT_SCHMITT) != 0) {
85 schmitt_enable = 1;
86 }
87 } else {
88 /* Deactivated: Analog */
89
90 mode = GPIO_MODE_INPUT;
91 disable_input_path = 1;
92 }
93
94 /* Bias */
95 if ((flags & GPIO_OUTPUT) != 0 || (flags & GPIO_INPUT) != 0) {
96 if ((flags & GPIO_PULL_UP) != 0) {
97 bias = GPIO_PUSEL_PULL_UP;
98 } else if ((flags & GPIO_PULL_DOWN) != 0) {
99 bias = GPIO_PUSEL_PULL_DOWN;
100 }
101 }
102
103 regs->MODE = (regs->MODE & ~MODE_MASK(pin)) |
104 (mode << MODE_PIN_SHIFT(pin));
105 regs->DBEN = (regs->DBEN & ~BIT(pin)) | (debounce_enable << pin);
106 regs->SMTEN = (regs->SMTEN & ~BIT(pin)) | (schmitt_enable << pin);
107 regs->DINOFF = (regs->DINOFF & ~DINOFF_MASK(pin)) |
108 (disable_input_path << DINOFF_PIN_SHIFT(pin));
109 regs->PUSEL = (regs->PUSEL & ~PUSEL_MASK(pin)) |
110 (bias << PUSEL_PIN_SHIFT(pin));
111
112 return 0;
113 }
114
gpio_numicro_port_get_raw(const struct device * dev,uint32_t * value)115 static int gpio_numicro_port_get_raw(const struct device *dev, uint32_t *value)
116 {
117 const struct gpio_numicro_config *cfg = dev->config;
118
119 *value = cfg->regs->PIN & PORT_PIN_MASK;
120
121 return 0;
122 }
123
gpio_numicro_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)124 static int gpio_numicro_port_set_masked_raw(const struct device *dev,
125 uint32_t mask,
126 uint32_t value)
127 {
128 const struct gpio_numicro_config *cfg = dev->config;
129
130 cfg->regs->DATMSK = ~mask;
131 cfg->regs->DOUT = value;
132
133 return 0;
134 }
135
gpio_numicro_port_set_bits_raw(const struct device * dev,uint32_t mask)136 static int gpio_numicro_port_set_bits_raw(const struct device *dev,
137 uint32_t mask)
138 {
139 const struct gpio_numicro_config *cfg = dev->config;
140
141 cfg->regs->DATMSK = ~mask;
142 cfg->regs->DOUT = PORT_PIN_MASK;
143
144 return 0;
145 }
146
gpio_numicro_port_clear_bits_raw(const struct device * dev,uint32_t mask)147 static int gpio_numicro_port_clear_bits_raw(const struct device *dev,
148 uint32_t mask)
149 {
150 const struct gpio_numicro_config *cfg = dev->config;
151
152 cfg->regs->DATMSK = ~mask;
153 cfg->regs->DOUT = 0;
154
155 return 0;
156 }
157
gpio_numicro_port_toggle_bits(const struct device * dev,uint32_t mask)158 static int gpio_numicro_port_toggle_bits(const struct device *dev, uint32_t mask)
159 {
160 const struct gpio_numicro_config *cfg = dev->config;
161
162 cfg->regs->DATMSK = 0;
163 cfg->regs->DOUT ^= mask;
164
165 return 0;
166 }
167
gpio_numicro_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)168 static int gpio_numicro_pin_interrupt_configure(const struct device *dev,
169 gpio_pin_t pin, enum gpio_int_mode mode,
170 enum gpio_int_trig trig)
171 {
172 const struct gpio_numicro_config *cfg = dev->config;
173 #ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT
174 struct gpio_numicro_data *data = dev->data;
175 #endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */
176 uint32_t int_type = 0;
177 uint32_t int_level = 0;
178 uint32_t int_level_mask = BIT(pin) | BIT(pin + 16);
179
180 #ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT
181 if (mode == GPIO_INT_MODE_DISABLE_ONLY) {
182 cfg->regs->INTEN &= ~(BIT(pin) | BIT(pin + 16));
183 return 0;
184 } else if (mode == GPIO_INT_MODE_ENABLE_ONLY) {
185 cfg->regs->INTEN |= data->interrupt_en_reg_bak & (BIT(pin) | BIT(pin + 16));
186 return 0;
187 }
188 #endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */
189
190 if (mode != GPIO_INT_MODE_DISABLED) {
191 int_type = (mode == GPIO_INT_MODE_LEVEL) ? 1 : 0;
192
193 switch (trig) {
194 case GPIO_INT_TRIG_LOW:
195 int_level = BIT(pin);
196 break;
197 case GPIO_INT_TRIG_HIGH:
198 int_level = BIT(pin + 16);
199 break;
200 case GPIO_INT_TRIG_BOTH:
201 int_level = BIT(pin) | BIT(pin + 16);
202 break;
203 }
204 }
205
206 cfg->regs->INTTYPE = (cfg->regs->INTTYPE & ~BIT(pin)) | (int_type << pin);
207 cfg->regs->INTEN = (cfg->regs->INTEN & ~int_level_mask) | int_level;
208 #ifdef CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT
209 data->interrupt_en_reg_bak = cfg->regs->INTEN;
210 #endif /* CONFIG_GPIO_ENABLE_DISABLE_INTERRUPT */
211
212 return 0;
213 }
214
gpio_numicro_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)215 static int gpio_numicro_manage_callback(const struct device *dev,
216 struct gpio_callback *callback,
217 bool set)
218 {
219 struct gpio_numicro_data *data = dev->data;
220
221 return gpio_manage_callback(&data->callbacks, callback, set);
222 }
223
gpio_numicro_isr(const struct device * dev)224 static void gpio_numicro_isr(const struct device *dev)
225 {
226 const struct gpio_numicro_config *cfg = dev->config;
227 struct gpio_numicro_data *data = dev->data;
228 uint32_t int_status;
229
230 int_status = cfg->regs->INTSRC;
231
232 /* Clear the port interrupts */
233 cfg->regs->INTSRC = int_status;
234
235 gpio_fire_callbacks(&data->callbacks, dev, int_status);
236 }
237
238 static const struct gpio_driver_api gpio_numicro_driver_api = {
239 .pin_configure = gpio_numicro_configure,
240 .port_get_raw = gpio_numicro_port_get_raw,
241 .port_set_masked_raw = gpio_numicro_port_set_masked_raw,
242 .port_set_bits_raw = gpio_numicro_port_set_bits_raw,
243 .port_clear_bits_raw = gpio_numicro_port_clear_bits_raw,
244 .port_toggle_bits = gpio_numicro_port_toggle_bits,
245 .pin_interrupt_configure = gpio_numicro_pin_interrupt_configure,
246 .manage_callback = gpio_numicro_manage_callback,
247 };
248
249 #define GPIO_NUMICRO_INIT(n) \
250 static int gpio_numicro_port##n##_init(const struct device *dev)\
251 { \
252 IRQ_CONNECT(DT_INST_IRQN(n), \
253 DT_INST_IRQ(n, priority), \
254 gpio_numicro_isr, \
255 DEVICE_DT_INST_GET(n), 0); \
256 irq_enable(DT_INST_IRQN(n)); \
257 return 0; \
258 } \
259 \
260 static struct gpio_numicro_data gpio_numicro_port##n##_data; \
261 \
262 static const struct gpio_numicro_config gpio_numicro_port##n##_config = {\
263 .common = { \
264 .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),\
265 }, \
266 .regs = (GPIO_T *)DT_INST_REG_ADDR(n), \
267 }; \
268 \
269 DEVICE_DT_INST_DEFINE(n, \
270 gpio_numicro_port##n##_init, \
271 NULL, \
272 &gpio_numicro_port##n##_data, \
273 &gpio_numicro_port##n##_config, \
274 PRE_KERNEL_1, \
275 CONFIG_GPIO_INIT_PRIORITY, \
276 &gpio_numicro_driver_api);
277
278 DT_INST_FOREACH_STATUS_OKAY(GPIO_NUMICRO_INIT)
279