1 /*
2  * Copyright (c) 2023, Intel Corporation. All rights reserved.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT altr_pio_1_0
8 
9 #include <zephyr/device.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/drivers/gpio/gpio_utils.h>
13 
14 #define ALTERA_AVALON_PIO_DATA_OFFSET		0x00
15 #define ALTERA_AVALON_PIO_DIRECTION_OFFSET	0x04
16 #define ALTERA_AVALON_PIO_IRQ_OFFSET		0x08
17 #define ALTERA_AVALON_PIO_SET_BITS		0x10
18 #define ALTERA_AVALON_PIO_CLEAR_BITS		0x14
19 
20 typedef void (*altera_cfg_func_t)(void);
21 
22 struct gpio_altera_config {
23 	struct gpio_driver_config	common;
24 	uintptr_t			reg_base;
25 	uint32_t			irq_num;
26 	uint8_t				direction;
27 	uint8_t				outset;
28 	uint8_t				outclear;
29 	altera_cfg_func_t		cfg_func;
30 };
31 
32 struct gpio_altera_data {
33 	/* gpio_driver_data needs to be first */
34 	struct gpio_driver_data common;
35 	/* list of callbacks */
36 	sys_slist_t cb;
37 	struct k_spinlock lock;
38 };
39 
gpio_pin_direction(const struct device * dev,uint32_t pin_mask)40 static bool gpio_pin_direction(const struct device *dev, uint32_t pin_mask)
41 {
42 	const struct gpio_altera_config *cfg = dev->config;
43 	const int direction = cfg->direction;
44 	uintptr_t reg_base = cfg->reg_base;
45 	uint32_t addr;
46 	uint32_t pin_direction;
47 
48 	if (pin_mask == 0) {
49 		return -EINVAL;
50 	}
51 
52 	/* Check if the direction is Bidirectional */
53 	if (direction != 0) {
54 		return -EINVAL;
55 	}
56 
57 	addr = reg_base + ALTERA_AVALON_PIO_DIRECTION_OFFSET;
58 
59 	pin_direction = sys_read32(addr);
60 
61 	if (!(pin_direction & pin_mask)) {
62 		return false;
63 	}
64 
65 	return true;
66 }
67 
gpio_altera_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)68 static int gpio_altera_configure(const struct device *dev,
69 				 gpio_pin_t pin, gpio_flags_t flags)
70 {
71 	const struct gpio_altera_config *cfg = dev->config;
72 	struct gpio_altera_data * const data = dev->data;
73 	const int port_pin_mask = cfg->common.port_pin_mask;
74 	const int direction = cfg->direction;
75 	uintptr_t reg_base = cfg->reg_base;
76 	k_spinlock_key_t key;
77 	uint32_t addr;
78 
79 	/* Check if pin number is within range */
80 	if ((port_pin_mask & BIT(pin)) == 0) {
81 		return -EINVAL;
82 	}
83 
84 	/* Check if the direction is Bidirectional */
85 	if (direction != 0) {
86 		return -EINVAL;
87 	}
88 
89 	addr = reg_base + ALTERA_AVALON_PIO_DIRECTION_OFFSET;
90 
91 	key = k_spin_lock(&data->lock);
92 
93 	if (flags == GPIO_INPUT) {
94 		sys_clear_bits(addr, BIT(pin));
95 	} else if (flags == GPIO_OUTPUT) {
96 		sys_set_bits(addr, BIT(pin));
97 	} else {
98 		return -EINVAL;
99 	}
100 
101 	k_spin_unlock(&data->lock, key);
102 
103 	return 0;
104 }
105 
gpio_altera_port_get_raw(const struct device * dev,uint32_t * value)106 static int gpio_altera_port_get_raw(const struct device *dev, uint32_t *value)
107 {
108 	const struct gpio_altera_config *cfg = dev->config;
109 	uintptr_t reg_base = cfg->reg_base;
110 	uint32_t addr;
111 
112 	addr = reg_base + ALTERA_AVALON_PIO_DATA_OFFSET;
113 
114 	if (value == NULL) {
115 		return -EINVAL;
116 	}
117 
118 	*value = sys_read32((addr));
119 
120 	return 0;
121 }
122 
gpio_altera_port_set_bits_raw(const struct device * dev,gpio_port_pins_t mask)123 static int gpio_altera_port_set_bits_raw(const struct device *dev, gpio_port_pins_t mask)
124 {
125 	const struct gpio_altera_config *cfg = dev->config;
126 	struct gpio_altera_data * const data = dev->data;
127 	const uint8_t outset = cfg->outset;
128 	const int port_pin_mask = cfg->common.port_pin_mask;
129 	uintptr_t reg_base = cfg->reg_base;
130 	uint32_t addr;
131 	k_spinlock_key_t key;
132 
133 	if ((port_pin_mask & mask) == 0) {
134 		return -EINVAL;
135 	}
136 
137 	if (!gpio_pin_direction(dev, mask)) {
138 		return -EINVAL;
139 	}
140 
141 	key = k_spin_lock(&data->lock);
142 
143 	if (outset) {
144 		addr = reg_base + ALTERA_AVALON_PIO_SET_BITS;
145 		sys_write32(mask, addr);
146 	} else {
147 		addr = reg_base + ALTERA_AVALON_PIO_DATA_OFFSET;
148 		sys_set_bits(addr, mask);
149 	}
150 
151 	k_spin_unlock(&data->lock, key);
152 
153 	return 0;
154 }
155 
gpio_altera_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t mask)156 static int gpio_altera_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t mask)
157 {
158 	const struct gpio_altera_config *cfg = dev->config;
159 	struct gpio_altera_data * const data = dev->data;
160 	const uint8_t outclear = cfg->outclear;
161 	const int port_pin_mask = cfg->common.port_pin_mask;
162 	uintptr_t reg_base = cfg->reg_base;
163 	uint32_t addr;
164 	k_spinlock_key_t key;
165 
166 	/* Check if mask range within 32 */
167 	if ((port_pin_mask & mask) == 0) {
168 		return -EINVAL;
169 	}
170 
171 	if (!gpio_pin_direction(dev, mask)) {
172 		return -EINVAL;
173 	}
174 
175 	key = k_spin_lock(&data->lock);
176 
177 	if (outclear) {
178 		addr = reg_base + ALTERA_AVALON_PIO_CLEAR_BITS;
179 		sys_write32(mask, addr);
180 	} else {
181 		addr = reg_base + ALTERA_AVALON_PIO_DATA_OFFSET;
182 		sys_clear_bits(addr, mask);
183 	}
184 
185 	k_spin_unlock(&data->lock, key);
186 
187 	return 0;
188 }
189 
gpio_init(const struct device * dev)190 static int gpio_init(const struct device *dev)
191 {
192 	const struct gpio_altera_config *cfg = dev->config;
193 
194 	/* Configure GPIO device */
195 	cfg->cfg_func();
196 
197 	return 0;
198 }
199 
gpio_altera_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)200 static int gpio_altera_pin_interrupt_configure(const struct device *dev,
201 						gpio_pin_t pin,
202 						enum gpio_int_mode mode,
203 						enum gpio_int_trig trig)
204 {
205 	ARG_UNUSED(trig);
206 
207 	const struct gpio_altera_config *cfg = dev->config;
208 	struct gpio_altera_data * const data = dev->data;
209 	uintptr_t reg_base = cfg->reg_base;
210 	const int port_pin_mask = cfg->common.port_pin_mask;
211 	uint32_t addr;
212 	k_spinlock_key_t key;
213 
214 	/* Check if pin number is within range */
215 	if ((port_pin_mask & BIT(pin)) == 0) {
216 		return -EINVAL;
217 	}
218 
219 	if (!gpio_pin_direction(dev, BIT(pin))) {
220 		return -EINVAL;
221 	}
222 
223 	addr = reg_base + ALTERA_AVALON_PIO_IRQ_OFFSET;
224 
225 	key = k_spin_lock(&data->lock);
226 
227 	switch (mode) {
228 	case GPIO_INT_MODE_DISABLED:
229 		/* Disable interrupt of pin */
230 		sys_clear_bits(addr, BIT(pin));
231 		irq_disable(cfg->irq_num);
232 		break;
233 	case GPIO_INT_MODE_LEVEL:
234 	case GPIO_INT_MODE_EDGE:
235 		/* Enable interrupt of pin */
236 		sys_set_bits(addr, BIT(pin));
237 		irq_enable(cfg->irq_num);
238 		break;
239 	default:
240 		return -EINVAL;
241 	}
242 
243 	k_spin_unlock(&data->lock, key);
244 
245 	return 0;
246 }
247 
gpio_altera_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)248 static int gpio_altera_manage_callback(const struct device *dev,
249 					struct gpio_callback *callback,
250 					bool set)
251 {
252 
253 	struct gpio_altera_data * const data = dev->data;
254 
255 	return gpio_manage_callback(&data->cb, callback, set);
256 }
257 
gpio_altera_irq_handler(const struct device * dev)258 static void gpio_altera_irq_handler(const struct device *dev)
259 {
260 	const struct gpio_altera_config *cfg = dev->config;
261 	struct gpio_altera_data *data = dev->data;
262 	uintptr_t reg_base = cfg->reg_base;
263 	uint32_t port_value;
264 	uint32_t addr;
265 	k_spinlock_key_t key;
266 
267 	addr = reg_base + ALTERA_AVALON_PIO_IRQ_OFFSET;
268 
269 	key = k_spin_lock(&data->lock);
270 
271 	port_value = sys_read32(addr);
272 
273 	sys_clear_bits(addr, port_value);
274 
275 	k_spin_unlock(&data->lock, key);
276 
277 	/* Call the corresponding callback registered for the pin */
278 	gpio_fire_callbacks(&data->cb, dev, port_value);
279 }
280 
281 static const struct gpio_driver_api gpio_altera_driver_api = {
282 	.pin_configure		 = gpio_altera_configure,
283 	.port_get_raw		 = gpio_altera_port_get_raw,
284 	.port_set_masked_raw	 = NULL,
285 	.port_set_bits_raw	 = gpio_altera_port_set_bits_raw,
286 	.port_clear_bits_raw	 = gpio_altera_port_clear_bits_raw,
287 	.port_toggle_bits	 = NULL,
288 	.pin_interrupt_configure = gpio_altera_pin_interrupt_configure,
289 	.manage_callback	 = gpio_altera_manage_callback
290 };
291 
292 #define GPIO_CFG_IRQ(idx, n)							\
293 		IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, idx, irq),			\
294 			    COND_CODE_1(DT_INST_IRQ_HAS_CELL(n, priority), \
295 				DT_INST_IRQ(n, priority), (0)), gpio_altera_irq_handler,	\
296 			    DEVICE_DT_INST_GET(n), 0);				\
297 
298 #define CREATE_GPIO_DEVICE(n)						\
299 	static void gpio_altera_cfg_func_##n(void);			\
300 	static struct gpio_altera_data gpio_altera_data_##n;		\
301 	static struct gpio_altera_config gpio_config_##n = {		\
302 		.common		= {					\
303 		.port_pin_mask	=					\
304 				  GPIO_PORT_PIN_MASK_FROM_DT_INST(n),	\
305 		},						        \
306 		.reg_base	= DT_INST_REG_ADDR(n),			\
307 		.direction	= DT_INST_ENUM_IDX(n, direction),	\
308 		.irq_num	= COND_CODE_1(DT_INST_IRQ_HAS_IDX(n, 0), (DT_INST_IRQN(n)), (0)),\
309 		.cfg_func	= gpio_altera_cfg_func_##n,		\
310 		.outset		= DT_INST_PROP(n, outset),	\
311 		.outclear	= DT_INST_PROP(n, outclear),	\
312 	};								\
313 							\
314 	DEVICE_DT_INST_DEFINE(n,			\
315 			      gpio_init,		\
316 			      NULL,			\
317 			      &gpio_altera_data_##n,	\
318 			      &gpio_config_##n,		\
319 			      POST_KERNEL,		\
320 			      CONFIG_GPIO_INIT_PRIORITY,	\
321 			      &gpio_altera_driver_api);		        \
322 									\
323 	static void gpio_altera_cfg_func_##n(void)			\
324 	{								\
325 		LISTIFY(DT_NUM_IRQS(DT_DRV_INST(n)), GPIO_CFG_IRQ, (), n)\
326 	}
327 
328 DT_INST_FOREACH_STATUS_OKAY(CREATE_GPIO_DEVICE)
329