1 /*
2  * Copyright (c) 2018 Justin Watson
3  * Copyright (c) 2020 ATL Electronics
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #define DT_DRV_COMPAT cypress_psoc6_gpio
9 
10 #include <errno.h>
11 #include <zephyr/kernel.h>
12 #include <zephyr/device.h>
13 #include <zephyr/init.h>
14 #include <soc.h>
15 #include <zephyr/drivers/gpio.h>
16 
17 #include <zephyr/drivers/gpio/gpio_utils.h>
18 #include "cy_gpio.h"
19 #include "cy_sysint.h"
20 
21 #define LOG_LEVEL CONFIG_GPIO_LOG_LEVEL
22 #include <zephyr/logging/log.h>
23 LOG_MODULE_REGISTER(gpio_psoc6);
24 
25 typedef void (*config_func_t)(const struct device *dev);
26 
27 struct gpio_psoc6_config {
28 	/* gpio_driver_config needs to be first */
29 	struct gpio_driver_config common;
30 	GPIO_PRT_Type *regs;
31 	config_func_t config_func;
32 };
33 
34 struct gpio_psoc6_runtime {
35 	/* gpio_driver_data needs to be first */
36 	struct gpio_driver_data common;
37 	sys_slist_t cb;
38 };
39 
gpio_psoc6_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)40 static int gpio_psoc6_config(const struct device *dev,
41 			     gpio_pin_t pin,
42 			     gpio_flags_t flags)
43 {
44 	const struct gpio_psoc6_config * const cfg = dev->config;
45 	GPIO_PRT_Type * const port = cfg->regs;
46 	uint32_t drv_mode;
47 	uint32_t pin_val;
48 
49 	if (flags & GPIO_OUTPUT) {
50 		if (flags & GPIO_SINGLE_ENDED) {
51 			drv_mode = (flags & GPIO_LINE_OPEN_DRAIN) ?
52 				CY_GPIO_DM_OD_DRIVESLOW_IN_OFF :
53 				CY_GPIO_DM_OD_DRIVESHIGH_IN_OFF;
54 
55 			pin_val = (flags & GPIO_LINE_OPEN_DRAIN) ? 1 : 0;
56 		} else {
57 			drv_mode = CY_GPIO_DM_STRONG_IN_OFF;
58 
59 			pin_val = (flags & GPIO_OUTPUT_INIT_HIGH) ? 1 : 0;
60 		}
61 	} else {
62 		if ((flags & GPIO_PULL_UP) && (flags & GPIO_PULL_DOWN)) {
63 			drv_mode = CY_GPIO_DM_PULLUP_DOWN_IN_OFF;
64 		} else if (flags & GPIO_PULL_UP) {
65 			drv_mode = CY_GPIO_DM_PULLUP_IN_OFF;
66 		} else if (flags & GPIO_PULL_DOWN) {
67 			drv_mode = CY_GPIO_DM_PULLDOWN_IN_OFF;
68 		} else {
69 			drv_mode = CY_GPIO_DM_ANALOG;
70 		}
71 
72 		pin_val = (flags & GPIO_PULL_UP) ? 1 : 0;
73 	}
74 
75 	if (flags & GPIO_INPUT) {
76 		/* enable input buffer */
77 		drv_mode |= CY_GPIO_DM_HIGHZ;
78 	}
79 
80 	Cy_GPIO_Pin_FastInit(port, pin, drv_mode, pin_val, HSIOM_SEL_GPIO);
81 	Cy_GPIO_SetVtrip(port, pin, CY_GPIO_VTRIP_CMOS);
82 	Cy_GPIO_SetSlewRate(port, pin, CY_GPIO_SLEW_FAST);
83 	Cy_GPIO_SetDriveSel(port, pin, CY_GPIO_DRIVE_FULL);
84 
85 	LOG_DBG("P: 0x%08x, Pin: %d, Mode: 0x%08x, Val: 0x%02x",
86 			(unsigned int) port, pin, drv_mode, pin_val);
87 
88 	return 0;
89 }
90 
gpio_psoc6_port_get_raw(const struct device * dev,uint32_t * value)91 static int gpio_psoc6_port_get_raw(const struct device *dev,
92 				   uint32_t *value)
93 {
94 	const struct gpio_psoc6_config * const cfg = dev->config;
95 	GPIO_PRT_Type * const port = cfg->regs;
96 
97 	*value = GPIO_PRT_IN(port);
98 
99 	LOG_DBG("P: 0x%08x, V: 0x%08x", (unsigned int) port, *value);
100 
101 	return 0;
102 }
103 
gpio_psoc6_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)104 static int gpio_psoc6_port_set_masked_raw(const struct device *dev,
105 					  uint32_t mask,
106 					  uint32_t value)
107 {
108 	const struct gpio_psoc6_config * const cfg = dev->config;
109 	GPIO_PRT_Type * const port = cfg->regs;
110 
111 	GPIO_PRT_OUT(port) = (GPIO_PRT_IN(port) & ~mask) | (mask & value);
112 
113 	return 0;
114 }
115 
gpio_psoc6_port_set_bits_raw(const struct device * dev,uint32_t mask)116 static int gpio_psoc6_port_set_bits_raw(const struct device *dev,
117 					uint32_t mask)
118 {
119 	const struct gpio_psoc6_config * const cfg = dev->config;
120 	GPIO_PRT_Type * const port = cfg->regs;
121 
122 	GPIO_PRT_OUT_SET(port) = mask;
123 
124 	return 0;
125 }
126 
gpio_psoc6_port_clear_bits_raw(const struct device * dev,uint32_t mask)127 static int gpio_psoc6_port_clear_bits_raw(const struct device *dev,
128 					  uint32_t mask)
129 {
130 	const struct gpio_psoc6_config * const cfg = dev->config;
131 	GPIO_PRT_Type * const port = cfg->regs;
132 
133 	GPIO_PRT_OUT_CLR(port) = mask;
134 
135 	return 0;
136 }
137 
gpio_psoc6_port_toggle_bits(const struct device * dev,uint32_t mask)138 static int gpio_psoc6_port_toggle_bits(const struct device *dev,
139 				       uint32_t mask)
140 {
141 	const struct gpio_psoc6_config * const cfg = dev->config;
142 	GPIO_PRT_Type * const port = cfg->regs;
143 
144 	GPIO_PRT_OUT_INV(port) = mask;
145 
146 	return 0;
147 }
148 
gpio_psoc6_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)149 static int gpio_psoc6_pin_interrupt_configure(const struct device *dev,
150 					    gpio_pin_t pin,
151 					    enum gpio_int_mode mode,
152 					    enum gpio_int_trig trig)
153 {
154 	const struct gpio_psoc6_config * const cfg = dev->config;
155 	GPIO_PRT_Type * const port = cfg->regs;
156 	uint32_t is_enabled = ((mode == GPIO_INT_MODE_DISABLED) ? 0 : 1);
157 	uint32_t lv_trg = CY_GPIO_INTR_DISABLE;
158 
159 	if (mode == GPIO_INT_MODE_LEVEL) {
160 		return -ENOTSUP;
161 	}
162 
163 	if (is_enabled) {
164 		switch (trig) {
165 		case GPIO_INT_TRIG_BOTH:
166 			lv_trg = CY_GPIO_INTR_BOTH;
167 			break;
168 		case GPIO_INT_TRIG_HIGH:
169 			lv_trg = CY_GPIO_INTR_RISING;
170 			break;
171 		case GPIO_INT_TRIG_LOW:
172 			lv_trg = CY_GPIO_INTR_FALLING;
173 			break;
174 		}
175 	}
176 
177 	Cy_GPIO_ClearInterrupt(port, pin);
178 	Cy_GPIO_SetInterruptEdge(port, pin, lv_trg);
179 	Cy_GPIO_SetInterruptMask(port, pin, is_enabled);
180 	/**
181 	 * The driver will set 50ns glitch free filter for any interrupt.
182 	 */
183 	Cy_GPIO_SetFilter(port, pin);
184 
185 	LOG_DBG("config: Pin: %d, Trg: %d", pin, lv_trg);
186 
187 	return 0;
188 }
189 
gpio_psoc6_isr(const struct device * dev)190 static void gpio_psoc6_isr(const struct device *dev)
191 {
192 	const struct gpio_psoc6_config * const cfg = dev->config;
193 	GPIO_PRT_Type * const port = cfg->regs;
194 	struct gpio_psoc6_runtime *context = dev->data;
195 	uint32_t int_stat;
196 
197 	int_stat = GPIO_PRT_INTR_MASKED(port);
198 
199 	/* Any INTR MMIO registers AHB clearing must be preceded with an AHB
200 	 * read access. Taken from Cy_GPIO_ClearInterrupt()
201 	 */
202 	(void)GPIO_PRT_INTR(port);
203 	GPIO_PRT_INTR(port) = int_stat;
204 	/* This read ensures that the initial write has been flushed out to HW
205 	 */
206 	(void)GPIO_PRT_INTR(port);
207 
208 	gpio_fire_callbacks(&context->cb, dev, int_stat);
209 }
210 
gpio_psoc6_manage_callback(const struct device * port,struct gpio_callback * callback,bool set)211 static int gpio_psoc6_manage_callback(const struct device *port,
212 				    struct gpio_callback *callback,
213 				    bool set)
214 {
215 	struct gpio_psoc6_runtime *context = port->data;
216 
217 	return gpio_manage_callback(&context->cb, callback, set);
218 }
219 
gpio_psoc6_get_pending_int(const struct device * dev)220 static uint32_t gpio_psoc6_get_pending_int(const struct device *dev)
221 {
222 	const struct gpio_psoc6_config * const cfg = dev->config;
223 	GPIO_PRT_Type * const port = cfg->regs;
224 
225 	LOG_DBG("Pending: 0x%08x", GPIO_PRT_INTR_MASKED(port));
226 
227 	return GPIO_PRT_INTR_MASKED(port);
228 }
229 
230 static const struct gpio_driver_api gpio_psoc6_api = {
231 	.pin_configure = gpio_psoc6_config,
232 	.port_get_raw = gpio_psoc6_port_get_raw,
233 	.port_set_masked_raw = gpio_psoc6_port_set_masked_raw,
234 	.port_set_bits_raw = gpio_psoc6_port_set_bits_raw,
235 	.port_clear_bits_raw = gpio_psoc6_port_clear_bits_raw,
236 	.port_toggle_bits = gpio_psoc6_port_toggle_bits,
237 	.pin_interrupt_configure = gpio_psoc6_pin_interrupt_configure,
238 	.manage_callback = gpio_psoc6_manage_callback,
239 	.get_pending_int = gpio_psoc6_get_pending_int,
240 };
241 
gpio_psoc6_init(const struct device * dev)242 int gpio_psoc6_init(const struct device *dev)
243 {
244 	const struct gpio_psoc6_config * const cfg = dev->config;
245 
246 	cfg->config_func(dev);
247 
248 	return 0;
249 }
250 
251 #define GPIO_PSOC6_INIT(n)						\
252 	static void port_##n##_psoc6_config_func(const struct device *dev); \
253 									\
254 	static const struct gpio_psoc6_config port_##n##_psoc6_config = { \
255 		.common = {						\
256 			.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(n),\
257 		},							\
258 		.regs = (GPIO_PRT_Type *)DT_INST_REG_ADDR(n),		\
259 		.config_func = port_##n##_psoc6_config_func,		\
260 	};								\
261 									\
262 	static struct gpio_psoc6_runtime port_##n##_psoc6_runtime = { 0 }; \
263 									\
264 	DEVICE_DT_INST_DEFINE(n, gpio_psoc6_init, NULL,			\
265 			    &port_##n##_psoc6_runtime,			\
266 			    &port_##n##_psoc6_config, PRE_KERNEL_1,	\
267 			    CONFIG_GPIO_INIT_PRIORITY,			\
268 			    &gpio_psoc6_api);				\
269 									\
270 	static void port_##n##_psoc6_config_func(const struct device *dev) \
271 	{								\
272 		CY_PSOC6_DT_INST_NVIC_INSTALL(n, gpio_psoc6_isr);	\
273 	};
274 
275 DT_INST_FOREACH_STATUS_OKAY(GPIO_PSOC6_INIT)
276