1 /*
2  * Copyright (c) 2023 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_numaker_gpio
8 
9 #include <zephyr/kernel.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/drivers/clock_control.h>
13 #include <zephyr/drivers/clock_control/clock_control_numaker.h>
14 #include <zephyr/drivers/gpio/gpio_utils.h>
15 #include <zephyr/logging/log.h>
16 #include <NuMicro.h>
17 
18 #define NU_MFP_POS(pinindex) ((pinindex % 4) * 8)
19 
20 LOG_MODULE_REGISTER(gpio_numaker, LOG_LEVEL_ERR);
21 
22 struct gpio_numaker_config {
23 	struct gpio_driver_config common;
24 	uint32_t reg;
25 	uint32_t gpa_base;
26 	uint32_t size;
27 	uint32_t clk_modidx;
28 	const struct device *clk_dev;
29 };
30 
31 struct gpio_numaker_data {
32 	struct gpio_driver_data common;
33 	sys_slist_t callbacks;
34 };
35 
gpio_numaker_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)36 static int gpio_numaker_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
37 {
38 	const struct gpio_numaker_config *config = dev->config;
39 	struct gpio_numaker_data *data = dev->data;
40 	GPIO_T *gpio_base = (GPIO_T *)config->reg;
41 	uint32_t pinMfpMask = (0x1f << NU_MFP_POS(pin));
42 	uint32_t pinMask = BIT(pin); /* mask for pin index --> (0x01 << pin) */
43 	uint32_t port_index;
44 	uint32_t *GPx_MFPx;
45 	uint32_t pinMfpGpio;
46 	int err = 0;
47 
48 	ARG_UNUSED(data);
49 
50 	/* Check for an invalid pin number */
51 	if (pin >= 15) {
52 		return -EINVAL;
53 	}
54 
55 	SYS_UnlockReg();
56 
57 	/* Enable GPIO clock */
58 	struct numaker_scc_subsys scc_subsys;
59 
60 	memset(&scc_subsys, 0x00, sizeof(scc_subsys));
61 	scc_subsys.subsys_id = NUMAKER_SCC_SUBSYS_ID_PCC;
62 	scc_subsys.pcc.clk_modidx = config->clk_modidx;
63 
64 	/* Equivalent to CLK_EnableModuleClock(config->clk_modidx) */
65 	err = clock_control_on(config->clk_dev, (clock_control_subsys_t)&scc_subsys);
66 	if (err != 0) {
67 		goto move_exit;
68 	}
69 
70 	/* Configure GPIO direction */
71 	switch (flags & GPIO_DIR_MASK) {
72 	case GPIO_INPUT:
73 		GPIO_SetMode(gpio_base, pinMask, GPIO_MODE_INPUT);
74 		break;
75 	case GPIO_OUTPUT:
76 		GPIO_SetMode(gpio_base, pinMask, GPIO_MODE_OUTPUT);
77 		break;
78 	case (GPIO_INPUT | GPIO_OUTPUT):
79 		GPIO_SetMode(gpio_base, pinMask, GPIO_MODE_QUASI);
80 		break;
81 	default:
82 		err = -ENOTSUP;
83 		goto move_exit;
84 	}
85 
86 	if (flags & GPIO_LINE_OPEN_DRAIN) {
87 		GPIO_SetMode(gpio_base, pinMask, GPIO_MODE_OPEN_DRAIN);
88 	}
89 
90 	/* Set Multi-function, default is GPIO */
91 	port_index = (config->reg - config->gpa_base) / config->size;
92 	GPx_MFPx = ((uint32_t *)&SYS->GPA_MFP0) + port_index * 4 + (pin / 4);
93 	pinMfpGpio = 0x00UL;
94 	/*
95 	 * E.g.: SYS->GPA_MFP0  = (SYS->GPA_MFP0 & (~SYS_GPA_MFP0_PA0MFP_Msk) ) |
96 	 * SYS_GPA_MFP0_PA0MFP_GPIO;
97 	 */
98 	*GPx_MFPx = (*GPx_MFPx & (~pinMfpMask)) | pinMfpGpio;
99 
100 	/* Set pull control as pull-up, pull-down or pull-disable */
101 	if ((flags & GPIO_PULL_UP) != 0) {
102 		GPIO_SetPullCtl(gpio_base, pinMask, GPIO_PUSEL_PULL_UP);
103 	} else if ((flags & GPIO_PULL_DOWN) != 0) {
104 		GPIO_SetPullCtl(gpio_base, pinMask, GPIO_PUSEL_PULL_DOWN);
105 	} else {
106 		GPIO_SetPullCtl(gpio_base, pinMask, GPIO_PUSEL_DISABLE);
107 	}
108 
109 	/* Set Init Level 0:low 1:high */
110 	if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
111 		gpio_base->DOUT |= pinMask;
112 	} else if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
113 		gpio_base->DOUT &= ~pinMask;
114 	}
115 
116 move_exit:
117 	SYS_LockReg();
118 	return err;
119 }
120 
gpio_numaker_port_get_raw(const struct device * dev,uint32_t * value)121 static int gpio_numaker_port_get_raw(const struct device *dev, uint32_t *value)
122 {
123 	const struct gpio_numaker_config *config = dev->config;
124 	GPIO_T *gpio_base = (GPIO_T *)config->reg;
125 
126 	/* Get raw bits of GPIO PIN data */
127 	*value = gpio_base->PIN;
128 
129 	return 0;
130 }
131 
gpio_numaker_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)132 static int gpio_numaker_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value)
133 {
134 	const struct gpio_numaker_config *config = dev->config;
135 	GPIO_T *gpio_base = (GPIO_T *)config->reg;
136 
137 	gpio_base->DOUT = (gpio_base->DOUT & ~mask) | (mask & value);
138 
139 	return 0;
140 }
141 
gpio_numaker_port_set_bits_raw(const struct device * dev,uint32_t mask)142 static int gpio_numaker_port_set_bits_raw(const struct device *dev, uint32_t mask)
143 {
144 	const struct gpio_numaker_config *config = dev->config;
145 	GPIO_T *gpio_base = (GPIO_T *)config->reg;
146 
147 	/* Set raw bits of GPIO output data */
148 	gpio_base->DOUT |= mask;
149 
150 	return 0;
151 }
152 
gpio_numaker_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t mask)153 static int gpio_numaker_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t mask)
154 {
155 	const struct gpio_numaker_config *config = dev->config;
156 	GPIO_T *gpio_base = (GPIO_T *)config->reg;
157 
158 	/* Clear raw bits of GPIO data */
159 	gpio_base->DOUT &= ~mask;
160 
161 	return 0;
162 }
163 
gpio_numaker_port_toggle_bits(const struct device * dev,gpio_port_pins_t mask)164 static int gpio_numaker_port_toggle_bits(const struct device *dev, gpio_port_pins_t mask)
165 {
166 	const struct gpio_numaker_config *config = dev->config;
167 	GPIO_T *gpio_base = (GPIO_T *)config->reg;
168 
169 	/* Toggle raw bits of GPIO data */
170 	gpio_base->DOUT ^= mask;
171 
172 	return 0;
173 }
174 
gpio_numaker_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)175 static int gpio_numaker_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
176 						enum gpio_int_mode mode, enum gpio_int_trig trig)
177 {
178 	const struct gpio_numaker_config *config = dev->config;
179 	GPIO_T *gpio_base = (GPIO_T *)config->reg;
180 	uint32_t intAttr;
181 
182 	if (mode == GPIO_INT_MODE_DISABLED) {
183 		GPIO_DisableInt(gpio_base, pin);
184 		/* Clear the port int status */
185 		gpio_base->INTSRC &= BIT(pin);
186 	} else {
187 		switch (trig) {
188 		case GPIO_INT_TRIG_LOW:
189 			intAttr = ((mode == GPIO_INT_MODE_EDGE) ? GPIO_INT_FALLING : GPIO_INT_LOW);
190 			break;
191 		case GPIO_INT_TRIG_HIGH:
192 			intAttr = ((mode == GPIO_INT_MODE_EDGE) ? GPIO_INT_RISING : GPIO_INT_HIGH);
193 			break;
194 		case GPIO_INT_TRIG_BOTH:
195 			if (mode != GPIO_INT_MODE_EDGE) {
196 				return -ENOTSUP;
197 			}
198 			intAttr = GPIO_INT_BOTH_EDGE;
199 			break;
200 		default:
201 			return -ENOTSUP;
202 		}
203 		GPIO_EnableInt(gpio_base, pin, intAttr);
204 	}
205 
206 	return 0;
207 }
208 
gpio_numaker_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)209 static int gpio_numaker_manage_callback(const struct device *dev, struct gpio_callback *callback,
210 					bool set)
211 {
212 	struct gpio_numaker_data *data = dev->data;
213 
214 	return gpio_manage_callback(&data->callbacks, callback, set);
215 }
216 
217 static const struct gpio_driver_api gpio_numaker_api = {
218 	.pin_configure = gpio_numaker_configure,
219 	.port_get_raw = gpio_numaker_port_get_raw,
220 	.port_set_masked_raw = gpio_numaker_port_set_masked_raw,
221 	.port_set_bits_raw = gpio_numaker_port_set_bits_raw,
222 	.port_clear_bits_raw = gpio_numaker_port_clear_bits_raw,
223 	.port_toggle_bits = gpio_numaker_port_toggle_bits,
224 	.pin_interrupt_configure = gpio_numaker_pin_interrupt_configure,
225 	.manage_callback = gpio_numaker_manage_callback};
226 
gpio_numaker_isr(const struct device * dev)227 static void gpio_numaker_isr(const struct device *dev)
228 {
229 	const struct gpio_numaker_config *config = dev->config;
230 	struct gpio_numaker_data *data = dev->data;
231 	GPIO_T *gpio_base = (GPIO_T *)config->reg;
232 	uint32_t int_status;
233 
234 	/* Get the int status  */
235 	int_status = gpio_base->INTSRC;
236 
237 	/* Clear the port int status */
238 	gpio_base->INTSRC = int_status;
239 
240 	gpio_fire_callbacks(&data->callbacks, dev, int_status);
241 }
242 
243 #define CLOCK_CTRL_INIT(n) .clk_dev = DEVICE_DT_GET(DT_PARENT(DT_INST_CLOCKS_CTLR(n))),
244 
245 #define GPIO_NUMAKER_IRQ_INIT(n)                                                                   \
246 	do {                                                                                       \
247 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), gpio_numaker_isr,           \
248 			    DEVICE_DT_INST_GET(n), 0);                                             \
249                                                                                                    \
250 		irq_enable(DT_INST_IRQN(n));                                                       \
251 	} while (0)
252 
253 #define GPIO_NUMAKER_DEFINE(n)                                                                     \
254 	static const struct gpio_numaker_config gpio_numaker_config##n = {                         \
255 		.common = {                                                                        \
256 				.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),               \
257 			},                                                                         \
258 		.reg = DT_INST_REG_ADDR(n),                                                        \
259 		.gpa_base = DT_REG_ADDR(DT_NODELABEL(gpioa)),                                      \
260 		.size = DT_REG_SIZE(DT_NODELABEL(gpioa)),                                          \
261 		.clk_modidx = DT_INST_CLOCKS_CELL(n, clock_module_index),                          \
262 		CLOCK_CTRL_INIT(n)};                                                               \
263                                                                                                    \
264 	static struct gpio_numaker_data gpio_numaker_data##n;                                      \
265                                                                                                    \
266 	static int gpio_numaker_init##n(const struct device *dev)                                  \
267 	{                                                                                          \
268 		IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 0), (GPIO_NUMAKER_IRQ_INIT(n);))                 \
269 		return 0;                                                                          \
270 	}                                                                                          \
271 	DEVICE_DT_INST_DEFINE(n, &gpio_numaker_init##n, NULL, &gpio_numaker_data##n,               \
272 			      &gpio_numaker_config##n, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY,    \
273 			      &gpio_numaker_api);
274 
275 DT_INST_FOREACH_STATUS_OKAY(GPIO_NUMAKER_DEFINE)
276