1 /*
2  * Copyright (c) 2024 SILA Embedded Solutions GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT infineon_tle9104_gpio
8 
9 #include <errno.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/device.h>
12 #include <zephyr/init.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/drivers/gpio/gpio_utils.h>
15 #include <zephyr/drivers/mfd/tle9104.h>
16 #include <zephyr/logging/log.h>
17 
18 LOG_MODULE_REGISTER(gpio_tle9104, CONFIG_GPIO_LOG_LEVEL);
19 
20 struct tle9104_gpio_config {
21 	/* gpio_driver_config needs to be first */
22 	struct gpio_driver_config common;
23 	/* parent MFD */
24 	const struct device *parent;
25 	bool parallel_mode_out12;
26 	bool parallel_mode_out34;
27 };
28 
29 struct tle9104_gpio_data {
30 	/* gpio_driver_data needs to be first */
31 	struct gpio_driver_data common;
32 	/* each bit is one output channel, bit 0 = OUT1, ... */
33 	uint8_t state;
34 	/* each bit defines if the output channel is configured, see state */
35 	uint8_t configured;
36 	struct k_mutex lock;
37 };
38 
tle9104_gpio_pin_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)39 static int tle9104_gpio_pin_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
40 {
41 	const struct tle9104_gpio_config *config = dev->config;
42 	struct tle9104_gpio_data *data = dev->data;
43 	int result;
44 
45 	/* cannot execute a bus operation in an ISR context */
46 	if (k_is_in_isr()) {
47 		return -EWOULDBLOCK;
48 	}
49 
50 	if (pin >= TLE9104_GPIO_COUNT) {
51 		LOG_ERR("invalid pin number %i", pin);
52 		return -EINVAL;
53 	}
54 
55 	if ((flags & GPIO_INPUT) != 0) {
56 		LOG_ERR("cannot configure pin as input");
57 		return -ENOTSUP;
58 	}
59 
60 	if ((flags & GPIO_OUTPUT) == 0) {
61 		LOG_ERR("pin must be configured as an output");
62 		return -ENOTSUP;
63 	}
64 
65 	if ((flags & GPIO_SINGLE_ENDED) == 0) {
66 		LOG_ERR("pin must be configured as single ended");
67 		return -ENOTSUP;
68 	}
69 
70 	if ((flags & GPIO_LINE_OPEN_DRAIN) == 0) {
71 		LOG_ERR("pin must be configured as open drain");
72 		return -ENOTSUP;
73 	}
74 
75 	if ((flags & GPIO_PULL_UP) != 0) {
76 		LOG_ERR("pin cannot have a pull up configured");
77 		return -ENOTSUP;
78 	}
79 
80 	if ((flags & GPIO_PULL_DOWN) != 0) {
81 		LOG_ERR("pin cannot have a pull down configured");
82 		return -ENOTSUP;
83 	}
84 
85 	if (config->parallel_mode_out12 && pin == 1) {
86 		LOG_ERR("cannot configure OUT2 if parallel mode is enabled for OUT1 and OUT2");
87 		return -EINVAL;
88 	}
89 
90 	if (config->parallel_mode_out34 && pin == 3) {
91 		LOG_ERR("cannot configure OUT4 if parallel mode is enabled for OUT3 and OUT4");
92 		return -EINVAL;
93 	}
94 
95 	k_mutex_lock(&data->lock, K_FOREVER);
96 
97 	if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
98 		WRITE_BIT(data->state, pin, 0);
99 	} else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
100 		WRITE_BIT(data->state, pin, 1);
101 	}
102 
103 	WRITE_BIT(data->configured, pin, 1);
104 	result = tle9104_write_state(config->parent, data->state);
105 	k_mutex_unlock(&data->lock);
106 
107 	return result;
108 }
109 
tle9104_gpio_port_get_raw(const struct device * dev,uint32_t * value)110 static int tle9104_gpio_port_get_raw(const struct device *dev, uint32_t *value)
111 {
112 	ARG_UNUSED(dev);
113 	ARG_UNUSED(value);
114 
115 	LOG_ERR("input pins are not available");
116 	return -ENOTSUP;
117 }
118 
tle9104_gpio_port_set_masked_raw(const struct device * dev,uint32_t mask,uint32_t value)119 static int tle9104_gpio_port_set_masked_raw(const struct device *dev, uint32_t mask, uint32_t value)
120 {
121 	const struct tle9104_gpio_config *config = dev->config;
122 	struct tle9104_gpio_data *data = dev->data;
123 	int result;
124 
125 	if (config->parallel_mode_out12 && (BIT(1) & mask) != 0) {
126 		LOG_ERR("cannot set OUT2 if parallel mode is enabled for OUT1 and OUT2");
127 		return -EINVAL;
128 	}
129 
130 	if (config->parallel_mode_out34 && (BIT(3) & mask) != 0) {
131 		LOG_ERR("cannot set OUT4 if parallel mode is enabled for OUT3 and OUT4");
132 		return -EINVAL;
133 	}
134 
135 	/* cannot execute a bus operation in an ISR context */
136 	if (k_is_in_isr()) {
137 		return -EWOULDBLOCK;
138 	}
139 
140 	k_mutex_lock(&data->lock, K_FOREVER);
141 	data->state = (data->state & ~mask) | (mask & value);
142 	result = tle9104_write_state(config->parent, data->state);
143 	k_mutex_unlock(&data->lock);
144 
145 	return result;
146 }
147 
tle9104_gpio_port_set_bits_raw(const struct device * dev,uint32_t mask)148 static int tle9104_gpio_port_set_bits_raw(const struct device *dev, uint32_t mask)
149 {
150 	return tle9104_gpio_port_set_masked_raw(dev, mask, mask);
151 }
152 
tle9104_gpio_port_clear_bits_raw(const struct device * dev,uint32_t mask)153 static int tle9104_gpio_port_clear_bits_raw(const struct device *dev, uint32_t mask)
154 {
155 	return tle9104_gpio_port_set_masked_raw(dev, mask, 0);
156 }
157 
tle9104_gpio_port_toggle_bits(const struct device * dev,uint32_t mask)158 static int tle9104_gpio_port_toggle_bits(const struct device *dev, uint32_t mask)
159 {
160 	const struct tle9104_gpio_config *config = dev->config;
161 	struct tle9104_gpio_data *data = dev->data;
162 	int result;
163 
164 	if (config->parallel_mode_out12 && (BIT(1) & mask) != 0) {
165 		LOG_ERR("cannot toggle OUT2 if parallel mode is enabled for OUT1 and OUT2");
166 		return -EINVAL;
167 	}
168 
169 	if (config->parallel_mode_out34 && (BIT(3) & mask) != 0) {
170 		LOG_ERR("cannot toggle OUT4 if parallel mode is enabled for OUT3 and OUT4");
171 		return -EINVAL;
172 	}
173 
174 	/* cannot execute a bus operation in an ISR context */
175 	if (k_is_in_isr()) {
176 		return -EWOULDBLOCK;
177 	}
178 
179 	k_mutex_lock(&data->lock, K_FOREVER);
180 	data->state ^= mask;
181 	result = tle9104_write_state(config->parent, data->state);
182 	k_mutex_unlock(&data->lock);
183 
184 	return result;
185 }
186 
tle9104_gpio_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)187 static int tle9104_gpio_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
188 						enum gpio_int_mode mode, enum gpio_int_trig trig)
189 {
190 	ARG_UNUSED(dev);
191 	ARG_UNUSED(pin);
192 	ARG_UNUSED(mode);
193 	ARG_UNUSED(trig);
194 	return -ENOTSUP;
195 }
196 
197 static DEVICE_API(gpio, api_table) = {
198 	.pin_configure = tle9104_gpio_pin_configure,
199 	.port_get_raw = tle9104_gpio_port_get_raw,
200 	.port_set_masked_raw = tle9104_gpio_port_set_masked_raw,
201 	.port_set_bits_raw = tle9104_gpio_port_set_bits_raw,
202 	.port_clear_bits_raw = tle9104_gpio_port_clear_bits_raw,
203 	.port_toggle_bits = tle9104_gpio_port_toggle_bits,
204 	.pin_interrupt_configure = tle9104_gpio_pin_interrupt_configure,
205 };
206 
tle9104_gpio_init(const struct device * dev)207 static int tle9104_gpio_init(const struct device *dev)
208 {
209 	const struct tle9104_gpio_config *config = dev->config;
210 	struct tle9104_gpio_data *data = dev->data;
211 	int result;
212 
213 	LOG_DBG("initialize TLE9104 GPIO instance %s", dev->name);
214 
215 	if (!device_is_ready(config->parent)) {
216 		LOG_ERR("%s: parent MFD is not ready", dev->name);
217 		return -EINVAL;
218 	}
219 
220 	result = k_mutex_init(&data->lock);
221 	if (result != 0) {
222 		LOG_ERR("unable to initialize mutex");
223 		return result;
224 	}
225 
226 	return 0;
227 }
228 
229 #define TLE9104_GPIO_INIT(inst)                                                                    \
230 	static const struct tle9104_gpio_config tle9104_gpio_##inst##_config = {                   \
231 		.common = {                                                                        \
232 			.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst),                    \
233 		},                                                                                 \
234 		.parent = DEVICE_DT_GET(DT_PARENT(DT_DRV_INST(inst))),                             \
235 		.parallel_mode_out12 = DT_PROP(DT_PARENT(DT_DRV_INST(inst)), parallel_out12),      \
236 		.parallel_mode_out34 = DT_PROP(DT_PARENT(DT_DRV_INST(inst)), parallel_out34),      \
237 	};                                                                                         \
238                                                                                                    \
239 	static struct tle9104_gpio_data tle9104_gpio_##inst##_drvdata;                             \
240                                                                                                    \
241 	/* This has to be initialized after the SPI peripheral. */                                 \
242 	DEVICE_DT_INST_DEFINE(inst, tle9104_gpio_init, NULL, &tle9104_gpio_##inst##_drvdata,       \
243 			      &tle9104_gpio_##inst##_config, POST_KERNEL,                          \
244 			      CONFIG_GPIO_TLE9104_INIT_PRIORITY, &api_table);
245 
246 DT_INST_FOREACH_STATUS_OKAY(TLE9104_GPIO_INIT)
247