1 /*
2  * Copyright (c) 2023 Antmicro <www.antmicro.com>
3  * Copyright (c) 2023 Ambiq Micro Inc. <www.ambiq.com>
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT ambiq_gpio_bank
9 
10 #include <errno.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/kernel.h>
13 #include <zephyr/drivers/gpio/gpio_utils.h>
14 #include <zephyr/irq.h>
15 #include <zephyr/spinlock.h>
16 
17 #include <am_mcu_apollo.h>
18 
19 typedef void (*ambiq_gpio_cfg_func_t)(void);
20 
21 struct ambiq_gpio_config {
22 	struct gpio_driver_config common;
23 	uint32_t base;
24 	uint32_t offset;
25 	uint32_t irq_num;
26 	ambiq_gpio_cfg_func_t cfg_func;
27 	uint8_t ngpios;
28 };
29 
30 struct ambiq_gpio_data {
31 	struct gpio_driver_data common;
32 	sys_slist_t cb;
33 	struct k_spinlock lock;
34 };
35 
ambiq_gpio_pin_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)36 static int ambiq_gpio_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
37 {
38 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
39 
40 	pin += (dev_cfg->offset >> 2);
41 
42 	am_hal_gpio_pincfg_t pincfg = am_hal_gpio_pincfg_default;
43 
44 	if (flags & GPIO_INPUT) {
45 		pincfg = am_hal_gpio_pincfg_input;
46 		if (flags & GPIO_PULL_UP) {
47 			pincfg.GP.cfg_b.ePullup = AM_HAL_GPIO_PIN_PULLUP_50K;
48 		} else if (flags & GPIO_PULL_DOWN) {
49 			pincfg.GP.cfg_b.ePullup = AM_HAL_GPIO_PIN_PULLDOWN_50K;
50 		}
51 	}
52 	if (flags & GPIO_OUTPUT) {
53 		if (flags & GPIO_SINGLE_ENDED) {
54 			if (flags & GPIO_LINE_OPEN_DRAIN) {
55 				pincfg.GP.cfg_b.eGPOutCfg = AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN;
56 			}
57 		} else {
58 			pincfg.GP.cfg_b.eGPOutCfg = AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL;
59 		}
60 	}
61 	if (flags & GPIO_DISCONNECTED) {
62 		pincfg = am_hal_gpio_pincfg_disabled;
63 	}
64 
65 	if (flags & GPIO_OUTPUT_INIT_HIGH) {
66 		pincfg.GP.cfg_b.eCEpol = AM_HAL_GPIO_PIN_CEPOL_ACTIVEHIGH;
67 		am_hal_gpio_state_write(pin, AM_HAL_GPIO_OUTPUT_SET);
68 
69 	} else if (flags & GPIO_OUTPUT_INIT_LOW) {
70 		pincfg.GP.cfg_b.eCEpol = AM_HAL_GPIO_PIN_CEPOL_ACTIVELOW;
71 		am_hal_gpio_state_write(pin, AM_HAL_GPIO_OUTPUT_CLEAR);
72 	}
73 
74 	am_hal_gpio_pinconfig(pin, pincfg);
75 
76 	return 0;
77 }
78 
79 #ifdef CONFIG_GPIO_GET_CONFIG
ambiq_gpio_get_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t * out_flags)80 static int ambiq_gpio_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *out_flags)
81 {
82 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
83 	am_hal_gpio_pincfg_t pincfg;
84 
85 	pin += (dev_cfg->offset >> 2);
86 
87 	am_hal_gpio_pinconfig_get(pin, &pincfg);
88 
89 	if (pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_DISABLE &&
90 	    pincfg.GP.cfg_b.eGPInput == AM_HAL_GPIO_PIN_INPUT_NONE) {
91 		*out_flags = GPIO_DISCONNECTED;
92 	}
93 	if (pincfg.GP.cfg_b.eGPInput == AM_HAL_GPIO_PIN_INPUT_ENABLE) {
94 		*out_flags = GPIO_INPUT;
95 		if (pincfg.GP.cfg_b.ePullup == AM_HAL_GPIO_PIN_PULLUP_50K) {
96 			*out_flags |= GPIO_PULL_UP;
97 		} else if (pincfg.GP.cfg_b.ePullup == AM_HAL_GPIO_PIN_PULLDOWN_50K) {
98 			*out_flags |= GPIO_PULL_DOWN;
99 		}
100 	}
101 	if (pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL) {
102 		*out_flags = GPIO_OUTPUT | GPIO_PUSH_PULL;
103 		if (pincfg.GP.cfg_b.eCEpol == AM_HAL_GPIO_PIN_CEPOL_ACTIVEHIGH) {
104 			*out_flags |= GPIO_OUTPUT_HIGH;
105 		} else if (pincfg.GP.cfg_b.eCEpol == AM_HAL_GPIO_PIN_CEPOL_ACTIVELOW) {
106 			*out_flags |= GPIO_OUTPUT_LOW;
107 		}
108 	}
109 	if (pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN) {
110 		*out_flags = GPIO_OUTPUT | GPIO_OPEN_DRAIN;
111 		if (pincfg.GP.cfg_b.eCEpol == AM_HAL_GPIO_PIN_CEPOL_ACTIVEHIGH) {
112 			*out_flags |= GPIO_OUTPUT_HIGH;
113 		} else if (pincfg.GP.cfg_b.eCEpol == AM_HAL_GPIO_PIN_CEPOL_ACTIVELOW) {
114 			*out_flags |= GPIO_OUTPUT_LOW;
115 		}
116 	}
117 
118 	return 0;
119 }
120 #endif
121 
122 #ifdef CONFIG_GPIO_GET_DIRECTION
ambiq_gpio_port_get_direction(const struct device * dev,gpio_port_pins_t map,gpio_port_pins_t * inputs,gpio_port_pins_t * outputs)123 static int ambiq_gpio_port_get_direction(const struct device *dev, gpio_port_pins_t map,
124 					 gpio_port_pins_t *inputs, gpio_port_pins_t *outputs)
125 {
126 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
127 	am_hal_gpio_pincfg_t pincfg;
128 	gpio_port_pins_t ip = 0;
129 	gpio_port_pins_t op = 0;
130 	uint32_t pin_offset = dev_cfg->offset >> 2;
131 
132 	if (inputs != NULL) {
133 		for (int i = 0; i < dev_cfg->ngpios; i++) {
134 			if ((map >> i) & 1) {
135 				am_hal_gpio_pinconfig_get(i + pin_offset, &pincfg);
136 				if (pincfg.GP.cfg_b.eGPInput == AM_HAL_GPIO_PIN_INPUT_ENABLE) {
137 					ip |= BIT(i);
138 				}
139 			}
140 		}
141 		*inputs = ip;
142 	}
143 	if (outputs != NULL) {
144 		for (int i = 0; i < dev_cfg->ngpios; i++) {
145 			if ((map >> i) & 1) {
146 				am_hal_gpio_pinconfig_get(i + pin_offset, &pincfg);
147 				if (pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_PUSHPULL ||
148 				    pincfg.GP.cfg_b.eGPOutCfg == AM_HAL_GPIO_PIN_OUTCFG_OPENDRAIN) {
149 					op |= BIT(i);
150 				}
151 			}
152 		}
153 		*outputs = op;
154 	}
155 
156 	return 0;
157 }
158 #endif
159 
ambiq_gpio_port_get_raw(const struct device * dev,gpio_port_value_t * value)160 static int ambiq_gpio_port_get_raw(const struct device *dev, gpio_port_value_t *value)
161 {
162 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
163 
164 	*value = (*AM_HAL_GPIO_RDn(dev_cfg->offset >> 2));
165 
166 	return 0;
167 }
168 
ambiq_gpio_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)169 static int ambiq_gpio_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
170 					  gpio_port_value_t value)
171 {
172 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
173 	uint32_t pin_offset = dev_cfg->offset >> 2;
174 
175 	for (int i = 0; i < dev_cfg->ngpios; i++) {
176 		if ((mask >> i) & 1) {
177 			am_hal_gpio_state_write(i + pin_offset, ((value >> i) & 1));
178 		}
179 	}
180 
181 	return 0;
182 }
183 
ambiq_gpio_port_set_bits_raw(const struct device * dev,gpio_port_pins_t pins)184 static int ambiq_gpio_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins)
185 {
186 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
187 	uint32_t pin_offset = dev_cfg->offset >> 2;
188 
189 	for (int i = 0; i < dev_cfg->ngpios; i++) {
190 		if ((pins >> i) & 1) {
191 			am_hal_gpio_state_write(i + pin_offset, AM_HAL_GPIO_OUTPUT_SET);
192 		}
193 	}
194 
195 	return 0;
196 }
197 
ambiq_gpio_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t pins)198 static int ambiq_gpio_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins)
199 {
200 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
201 	uint32_t pin_offset = dev_cfg->offset >> 2;
202 
203 	for (int i = 0; i < dev_cfg->ngpios; i++) {
204 		if ((pins >> i) & 1) {
205 			am_hal_gpio_state_write(i + pin_offset, AM_HAL_GPIO_OUTPUT_CLEAR);
206 		}
207 	}
208 
209 	return 0;
210 }
211 
ambiq_gpio_port_toggle_bits(const struct device * dev,gpio_port_pins_t pins)212 static int ambiq_gpio_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
213 {
214 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
215 	uint32_t pin_offset = dev_cfg->offset >> 2;
216 
217 	for (int i = 0; i < dev_cfg->ngpios; i++) {
218 		if ((pins >> i) & 1) {
219 			am_hal_gpio_state_write(i + pin_offset, AM_HAL_GPIO_OUTPUT_TOGGLE);
220 		}
221 	}
222 
223 	return 0;
224 }
225 
ambiq_gpio_isr(const struct device * dev)226 static void ambiq_gpio_isr(const struct device *dev)
227 {
228 	struct ambiq_gpio_data *const data = dev->data;
229 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
230 
231 	uint32_t int_status;
232 
233 	am_hal_gpio_interrupt_irq_status_get(dev_cfg->irq_num, false, &int_status);
234 	am_hal_gpio_interrupt_irq_clear(dev_cfg->irq_num, int_status);
235 
236 	gpio_fire_callbacks(&data->cb, dev, int_status);
237 }
238 
ambiq_gpio_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)239 static int ambiq_gpio_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
240 					      enum gpio_int_mode mode, enum gpio_int_trig trig)
241 {
242 	const struct ambiq_gpio_config *const dev_cfg = dev->config;
243 	struct ambiq_gpio_data *const data = dev->data;
244 
245 	am_hal_gpio_pincfg_t pincfg;
246 	int gpio_pin = pin + (dev_cfg->offset >> 2);
247 	uint32_t int_status;
248 	int ret;
249 
250 	ret = am_hal_gpio_pinconfig_get(gpio_pin, &pincfg);
251 
252 	if (mode == GPIO_INT_MODE_DISABLED) {
253 		pincfg.GP.cfg_b.eIntDir = AM_HAL_GPIO_PIN_INTDIR_NONE;
254 		ret = am_hal_gpio_pinconfig(gpio_pin, pincfg);
255 
256 		k_spinlock_key_t key = k_spin_lock(&data->lock);
257 
258 		ret = am_hal_gpio_interrupt_irq_status_get(dev_cfg->irq_num, false, &int_status);
259 		ret = am_hal_gpio_interrupt_irq_clear(dev_cfg->irq_num, int_status);
260 		ret = am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0,
261 						    AM_HAL_GPIO_INT_CTRL_INDV_DISABLE,
262 						    (void *)&gpio_pin);
263 		k_spin_unlock(&data->lock, key);
264 
265 	} else {
266 		if (mode == GPIO_INT_MODE_LEVEL) {
267 			return -ENOTSUP;
268 		}
269 		switch (trig) {
270 		case GPIO_INT_TRIG_LOW:
271 			pincfg.GP.cfg_b.eIntDir = AM_HAL_GPIO_PIN_INTDIR_HI2LO;
272 			break;
273 		case GPIO_INT_TRIG_HIGH:
274 			pincfg.GP.cfg_b.eIntDir = AM_HAL_GPIO_PIN_INTDIR_LO2HI;
275 			break;
276 		case GPIO_INT_TRIG_BOTH:
277 			/*
278 			 * GPIO_INT_TRIG_BOTH is not supported on Ambiq Apollo4 Plus Platform
279 			 * ERR008: GPIO: Dual-edge interrupts are not vectoring
280 			 */
281 			return -ENOTSUP;
282 		}
283 		ret = am_hal_gpio_pinconfig(gpio_pin, pincfg);
284 
285 		irq_enable(dev_cfg->irq_num);
286 
287 		k_spinlock_key_t key = k_spin_lock(&data->lock);
288 
289 		ret = am_hal_gpio_interrupt_irq_status_get(dev_cfg->irq_num, false, &int_status);
290 		ret = am_hal_gpio_interrupt_irq_clear(dev_cfg->irq_num, int_status);
291 		ret = am_hal_gpio_interrupt_control(AM_HAL_GPIO_INT_CHANNEL_0,
292 						    AM_HAL_GPIO_INT_CTRL_INDV_ENABLE,
293 						    (void *)&gpio_pin);
294 		k_spin_unlock(&data->lock, key);
295 	}
296 	return ret;
297 }
298 
ambiq_gpio_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)299 static int ambiq_gpio_manage_callback(const struct device *dev, struct gpio_callback *callback,
300 				      bool set)
301 {
302 	struct ambiq_gpio_data *const data = dev->data;
303 
304 	return gpio_manage_callback(&data->cb, callback, set);
305 }
306 
ambiq_gpio_init(const struct device * port)307 static int ambiq_gpio_init(const struct device *port)
308 {
309 	const struct ambiq_gpio_config *const dev_cfg = port->config;
310 
311 	NVIC_ClearPendingIRQ(dev_cfg->irq_num);
312 
313 	dev_cfg->cfg_func();
314 
315 	return 0;
316 }
317 
318 static const struct gpio_driver_api ambiq_gpio_drv_api = {
319 	.pin_configure = ambiq_gpio_pin_configure,
320 #ifdef CONFIG_GPIO_GET_CONFIG
321 	.pin_get_config = ambiq_gpio_get_config,
322 #endif
323 	.port_get_raw = ambiq_gpio_port_get_raw,
324 	.port_set_masked_raw = ambiq_gpio_port_set_masked_raw,
325 	.port_set_bits_raw = ambiq_gpio_port_set_bits_raw,
326 	.port_clear_bits_raw = ambiq_gpio_port_clear_bits_raw,
327 	.port_toggle_bits = ambiq_gpio_port_toggle_bits,
328 	.pin_interrupt_configure = ambiq_gpio_pin_interrupt_configure,
329 	.manage_callback = ambiq_gpio_manage_callback,
330 #ifdef CONFIG_GPIO_GET_DIRECTION
331 	.port_get_direction = ambiq_gpio_port_get_direction,
332 #endif
333 };
334 
335 #define AMBIQ_GPIO_DEFINE(n)                                                                       \
336 	static struct ambiq_gpio_data ambiq_gpio_data_##n;                                         \
337 	static void ambiq_gpio_cfg_func_##n(void);                                                 \
338                                                                                                    \
339 	static const struct ambiq_gpio_config ambiq_gpio_config_##n = {                            \
340 		.common =                                                                          \
341 			{                                                                          \
342 				.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),               \
343 			},                                                                         \
344 		.base = DT_REG_ADDR(DT_INST_PARENT(n)),                                            \
345 		.offset = DT_INST_REG_ADDR(n),                                                     \
346 		.ngpios = DT_INST_PROP(n, ngpios),                                                 \
347 		.irq_num = DT_INST_IRQN(n),                                                        \
348 		.cfg_func = ambiq_gpio_cfg_func_##n};                                              \
349 	static void ambiq_gpio_cfg_func_##n(void)                                                  \
350 	{                                                                                          \
351                                                                                                    \
352 		IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), ambiq_gpio_isr,             \
353 			    DEVICE_DT_INST_GET(n), 0);                                             \
354                                                                                                    \
355 		return;                                                                            \
356 	};                                                                                         \
357                                                                                                    \
358 	DEVICE_DT_INST_DEFINE(n, &ambiq_gpio_init, NULL, &ambiq_gpio_data_##n,                     \
359 			      &ambiq_gpio_config_##n, PRE_KERNEL_1, CONFIG_GPIO_INIT_PRIORITY,     \
360 			      &ambiq_gpio_drv_api);
361 
362 DT_INST_FOREACH_STATUS_OKAY(AMBIQ_GPIO_DEFINE)
363