1 /*
2  * Copyright (c) 2022 Schlumberger
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT infineon_xmc4xxx_gpio
8 
9 #include <errno.h>
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/drivers/interrupt_controller/intc_xmc4xxx.h>
13 #include <zephyr/dt-bindings/gpio/infineon-xmc4xxx-gpio.h>
14 #include <xmc_gpio.h>
15 
16 #include <zephyr/drivers/gpio/gpio_utils.h>
17 
18 #define PORT_TO_PORT_ID(port)      ((int)(port) >> 8 & 0xf)
19 
20 struct gpio_xmc4xxx_config {
21 	/* gpio_driver_config needs to be first, required by Zephyr */
22 	struct gpio_driver_config common;
23 	XMC_GPIO_PORT_t *port;
24 };
25 
26 struct gpio_xmc4xxx_data {
27 	/* gpio_driver_data needs to be first, required by Zephyr */
28 	struct gpio_driver_data common;
29 #if defined(CONFIG_XMC4XXX_INTC)
30 	sys_slist_t callbacks;
31 #endif
32 };
33 
gpio_xmc4xxx_convert_flags(XMC_GPIO_CONFIG_t * pin_config,gpio_flags_t flags)34 static int gpio_xmc4xxx_convert_flags(XMC_GPIO_CONFIG_t *pin_config, gpio_flags_t flags)
35 {
36 	bool is_input  = flags & GPIO_INPUT;
37 	bool is_output = flags & GPIO_OUTPUT;
38 	int ds;
39 
40 	/* GPIO_DISCONNECTED */
41 	if (!is_input && !is_output) {
42 		return -ENOTSUP;
43 	}
44 
45 	if (flags & GPIO_OPEN_SOURCE) {
46 		return -ENOTSUP;
47 	}
48 
49 	if (is_input) {
50 		pin_config->mode = XMC_GPIO_MODE_INPUT_TRISTATE;
51 		if (flags & GPIO_PULL_DOWN) {
52 			pin_config->mode = XMC_GPIO_MODE_INPUT_PULL_DOWN;
53 		}
54 		if (flags & GPIO_PULL_UP) {
55 			pin_config->mode = XMC_GPIO_MODE_INPUT_PULL_UP;
56 		}
57 	}
58 
59 	ds = XMC4XXX_GPIO_GET_DS(flags);
60 	if ((!is_output && ds) || ds > XMC4XXX_GPIO_DS_WEAK) {
61 		return -EINVAL;
62 	}
63 
64 	if (is_output) {
65 		pin_config->mode = XMC_GPIO_MODE_OUTPUT_PUSH_PULL;
66 		if (flags & GPIO_OPEN_DRAIN) {
67 			pin_config->mode = XMC_GPIO_MODE_OUTPUT_OPEN_DRAIN;
68 		}
69 		if (flags & GPIO_OUTPUT_INIT_LOW) {
70 			pin_config->output_level = XMC_GPIO_OUTPUT_LEVEL_LOW;
71 		}
72 		if (flags & GPIO_OUTPUT_INIT_HIGH) {
73 			pin_config->output_level = XMC_GPIO_OUTPUT_LEVEL_HIGH;
74 		}
75 		/* Strong medium edge is default */
76 		pin_config->output_strength = XMC_GPIO_OUTPUT_STRENGTH_STRONG_MEDIUM_EDGE;
77 		if (ds > 0) {
78 			pin_config->output_strength = ds - 1;
79 		}
80 	}
81 
82 	return 0;
83 }
84 
gpio_xmc4xxx_pin_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)85 static int gpio_xmc4xxx_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
86 {
87 	const struct gpio_xmc4xxx_config *config = dev->config;
88 	XMC_GPIO_PORT_t *port = config->port;
89 	XMC_GPIO_CONFIG_t pin_config = {0};
90 	gpio_port_pins_t pin_mask = config->common.port_pin_mask;
91 	int ret;
92 
93 	if ((BIT(pin) & pin_mask) == 0) {
94 		return -EINVAL;
95 	}
96 
97 	if ((port == (XMC_GPIO_PORT_t *)PORT14_BASE || port == (XMC_GPIO_PORT_t *)PORT15_BASE) &&
98 	    (flags & GPIO_OUTPUT)) {
99 		return -EINVAL;
100 	}
101 
102 	ret = gpio_xmc4xxx_convert_flags(&pin_config, flags);
103 	if (ret) {
104 		return ret;
105 	}
106 
107 	XMC_GPIO_Init(port, pin, &pin_config);
108 	return 0;
109 }
110 
111 #if defined(CONFIG_XMC4XXX_INTC)
gpio_xmc4xxx_isr(const struct device * dev,int pin)112 static void gpio_xmc4xxx_isr(const struct device *dev, int pin)
113 {
114 	struct gpio_xmc4xxx_data *data = dev->data;
115 
116 	gpio_fire_callbacks(&data->callbacks, dev, BIT(pin));
117 }
118 #endif
119 
120 #ifdef CONFIG_XMC4XXX_INTC
gpio_xmc4xxx_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)121 static int gpio_xmc4xxx_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
122 						enum gpio_int_mode mode, enum gpio_int_trig trig)
123 {
124 	const struct gpio_xmc4xxx_config *config = dev->config;
125 	int port_id = PORT_TO_PORT_ID(config->port);
126 
127 	if (mode & GPIO_INT_ENABLE) {
128 		return intc_xmc4xxx_gpio_enable_interrupt(port_id, pin, mode, trig,
129 							  gpio_xmc4xxx_isr, (void *)dev);
130 	} else if (mode & GPIO_INT_DISABLE) {
131 		return intc_xmc4xxx_gpio_disable_interrupt(port_id, pin);
132 	} else {
133 		return -EINVAL;
134 	}
135 }
136 #endif
137 
gpio_xmc4xxx_get_raw(const struct device * dev,gpio_port_value_t * value)138 static int gpio_xmc4xxx_get_raw(const struct device *dev, gpio_port_value_t *value)
139 {
140 	const struct gpio_xmc4xxx_config *config = dev->config;
141 	XMC_GPIO_PORT_t *port = config->port;
142 	gpio_port_pins_t pin_mask = config->common.port_pin_mask;
143 
144 	*value = port->IN & pin_mask;
145 	return 0;
146 }
147 
148 #if defined(CONFIG_XMC4XXX_INTC)
gpio_xmc4xxx_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)149 static int gpio_xmc4xxx_manage_callback(const struct device *dev, struct gpio_callback *callback,
150 					bool set)
151 {
152 	struct gpio_xmc4xxx_data *data = dev->data;
153 
154 	return gpio_manage_callback(&data->callbacks, callback, set);
155 }
156 #endif
157 
gpio_xmc4xxx_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)158 static int gpio_xmc4xxx_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
159 				       gpio_port_value_t value)
160 {
161 	const struct gpio_xmc4xxx_config *config = dev->config;
162 	XMC_GPIO_PORT_t *port = config->port;
163 	gpio_port_pins_t pin_mask = config->common.port_pin_mask;
164 
165 	mask &= pin_mask;
166 
167 	/* OMR - output modification register. Upper 16 bits is used to clear pins */
168 	port->OMR = (value & mask) | (~value & mask) << 16;
169 	return 0;
170 }
171 
gpio_xmc4xxx_set_bits_raw(const struct device * dev,gpio_port_pins_t pins)172 static int gpio_xmc4xxx_set_bits_raw(const struct device *dev, gpio_port_pins_t pins)
173 {
174 	const struct gpio_xmc4xxx_config *config = dev->config;
175 	XMC_GPIO_PORT_t *port = config->port;
176 	gpio_port_pins_t pin_mask = config->common.port_pin_mask;
177 
178 	port->OMR = pins & pin_mask;
179 	return 0;
180 }
181 
gpio_xmc4xxx_clear_bits_raw(const struct device * dev,gpio_port_pins_t pins)182 static int gpio_xmc4xxx_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins)
183 {
184 	const struct gpio_xmc4xxx_config *config = dev->config;
185 	XMC_GPIO_PORT_t *port = config->port;
186 
187 	port->OMR = pins << 16;
188 	return 0;
189 }
190 
gpio_xmc4xxx_toggle_bits(const struct device * dev,gpio_port_pins_t pins)191 static int gpio_xmc4xxx_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
192 {
193 	const struct gpio_xmc4xxx_config *config = dev->config;
194 	XMC_GPIO_PORT_t *port = config->port;
195 	gpio_port_pins_t pin_mask = config->common.port_pin_mask;
196 
197 	pins &= pin_mask;
198 	port->OMR = pins | pins << 16;
199 	return 0;
200 }
201 
gpio_xmc4xxx_init(const struct device * dev)202 static int gpio_xmc4xxx_init(const struct device *dev) { return 0; }
203 
204 static const struct gpio_driver_api gpio_xmc4xxx_driver_api = {
205 	.pin_configure = gpio_xmc4xxx_pin_configure,
206 	.port_get_raw = gpio_xmc4xxx_get_raw,
207 	.port_set_masked_raw = gpio_xmc4xxx_set_masked_raw,
208 	.port_set_bits_raw = gpio_xmc4xxx_set_bits_raw,
209 	.port_clear_bits_raw = gpio_xmc4xxx_clear_bits_raw,
210 	.port_toggle_bits = gpio_xmc4xxx_toggle_bits,
211 #ifdef CONFIG_XMC4XXX_INTC
212 	.pin_interrupt_configure = gpio_xmc4xxx_pin_interrupt_configure,
213 	.manage_callback = gpio_xmc4xxx_manage_callback,
214 #endif
215 };
216 
217 #define GPIO_XMC4XXX_INIT(index)                                                                   \
218 	static struct gpio_xmc4xxx_data xmc4xxx_data_##index;                                      \
219                                                                                                    \
220 	static const struct gpio_xmc4xxx_config xmc4xxx_config_##index = {                         \
221 		.port = (XMC_GPIO_PORT_t *)DT_INST_REG_ADDR(index),                                \
222 		.common = {.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(index)}};              \
223                                                                                                    \
224 	DEVICE_DT_INST_DEFINE(index, gpio_xmc4xxx_init, NULL, &xmc4xxx_data_##index,               \
225 			      &xmc4xxx_config_##index, POST_KERNEL, CONFIG_GPIO_INIT_PRIORITY,     \
226 			      &gpio_xmc4xxx_driver_api);
227 
228 DT_INST_FOREACH_STATUS_OKAY(GPIO_XMC4XXX_INIT)
229