1 /*
2  * Copyright (c) 2023 Martin Kiepfer
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT x_powers_axp192_gpio
8 
9 #include <errno.h>
10 
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/drivers/gpio/gpio_utils.h>
13 #include <zephyr/drivers/i2c.h>
14 #include <zephyr/kernel.h>
15 #include <zephyr/sys/util_macro.h>
16 #include <zephyr/toolchain.h>
17 #include <zephyr/logging/log.h>
18 #include <zephyr/drivers/mfd/axp192.h>
19 
20 LOG_MODULE_REGISTER(gpio_axp192, CONFIG_GPIO_LOG_LEVEL);
21 
22 struct gpio_axp192_config {
23 	struct gpio_driver_config common;
24 	struct i2c_dt_spec i2c;
25 	const struct device *mfd;
26 	uint32_t ngpios;
27 };
28 
29 struct gpio_axp192_data {
30 	struct gpio_driver_data common;
31 	struct k_mutex mutex;
32 	sys_slist_t cb_list_gpio;
33 };
34 
gpio_axp192_port_get_raw(const struct device * dev,uint32_t * value)35 static int gpio_axp192_port_get_raw(const struct device *dev, uint32_t *value)
36 {
37 	int ret;
38 	uint8_t port_val;
39 	const struct gpio_axp192_config *config = dev->config;
40 
41 	if (k_is_in_isr()) {
42 		return -EWOULDBLOCK;
43 	}
44 
45 	ret = mfd_axp192_gpio_read_port(config->mfd, &port_val);
46 	if (ret == 0) {
47 		*value = port_val;
48 	}
49 
50 	return ret;
51 }
52 
gpio_axp192_port_set_masked_raw(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)53 static int gpio_axp192_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask,
54 					   gpio_port_value_t value)
55 {
56 	int ret;
57 	const struct gpio_axp192_config *config = dev->config;
58 
59 	if (k_is_in_isr()) {
60 		return -EWOULDBLOCK;
61 	}
62 
63 	ret = mfd_axp192_gpio_write_port(config->mfd, value, mask);
64 
65 	return ret;
66 }
67 
gpio_axp192_port_set_bits_raw(const struct device * dev,gpio_port_pins_t pins)68 static int gpio_axp192_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins)
69 {
70 	return gpio_axp192_port_set_masked_raw(dev, pins, pins);
71 }
72 
gpio_axp192_port_clear_bits_raw(const struct device * dev,gpio_port_pins_t pins)73 static int gpio_axp192_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins)
74 {
75 	return gpio_axp192_port_set_masked_raw(dev, pins, 0);
76 }
77 
gpio_axp192_configure(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)78 static int gpio_axp192_configure(const struct device *dev, gpio_pin_t pin,
79 					gpio_flags_t flags)
80 {
81 	const struct gpio_axp192_config *config = dev->config;
82 	int ret;
83 	enum axp192_gpio_func func;
84 
85 	if (pin >= config->ngpios) {
86 		LOG_ERR("Invalid gpio pin (%d)", pin);
87 		return -EINVAL;
88 	}
89 
90 	if (k_is_in_isr()) {
91 		return -EWOULDBLOCK;
92 	}
93 
94 	/* Configure pin */
95 	LOG_DBG("Pin: %d / flags=0x%x", pin, flags);
96 	if ((flags & GPIO_OUTPUT) != 0) {
97 
98 		/* Initialize output function */
99 		func = AXP192_GPIO_FUNC_OUTPUT_LOW;
100 		if ((flags & GPIO_OPEN_DRAIN) != 0) {
101 			func = AXP192_GPIO_FUNC_OUTPUT_OD;
102 		}
103 		ret = mfd_axp192_gpio_func_ctrl(config->mfd, dev, pin, func);
104 		if (ret != 0) {
105 			return ret;
106 		}
107 
108 		/* Set init value */
109 		if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
110 			ret = mfd_axp192_gpio_write_port(config->mfd, BIT(pin), 0);
111 		} else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
112 			ret = mfd_axp192_gpio_write_port(config->mfd, BIT(pin), BIT(pin));
113 		}
114 	} else if ((flags & GPIO_INPUT) != 0) {
115 
116 		/* Initialize input function */
117 		func = AXP192_GPIO_FUNC_INPUT;
118 
119 		ret = mfd_axp192_gpio_func_ctrl(config->mfd, dev, pin, func);
120 		if (ret != 0) {
121 			return ret;
122 		}
123 
124 		/* Configure pull-down */
125 		if ((flags & GPIO_PULL_UP) != 0) {
126 			/* not supported */
127 			LOG_ERR("Pull-Up not supported");
128 			ret = -ENOTSUP;
129 		} else if ((flags & GPIO_PULL_DOWN) != 0) {
130 			/* out = 0 means pull-down*/
131 			ret = mfd_axp192_gpio_pd_ctrl(config->mfd, pin, true);
132 		} else {
133 			ret = mfd_axp192_gpio_pd_ctrl(config->mfd, pin, false);
134 		}
135 	} else {
136 		/* Neither input nor output mode is selected */
137 		LOG_INF("No valid gpio mode selected");
138 		ret = -ENOTSUP;
139 	}
140 
141 	return ret;
142 }
143 
gpio_axp192_port_toggle_bits(const struct device * dev,gpio_port_pins_t pins)144 static int gpio_axp192_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
145 {
146 	struct gpio_axp192_data *data = dev->data;
147 	int ret;
148 	uint32_t value;
149 
150 	k_mutex_lock(&data->mutex, K_FOREVER);
151 
152 	ret = gpio_axp192_port_get_raw(dev, &value);
153 	if (ret == 0) {
154 		ret = gpio_axp192_port_set_masked_raw(dev, pins, ~value);
155 	}
156 
157 	k_mutex_unlock(&data->mutex);
158 
159 	return ret;
160 }
161 
gpio_axp192_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)162 static int gpio_axp192_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
163 					       enum gpio_int_mode mode, enum gpio_int_trig trig)
164 {
165 	ARG_UNUSED(dev);
166 	ARG_UNUSED(pin);
167 	ARG_UNUSED(mode);
168 	ARG_UNUSED(trig);
169 
170 	return -ENOTSUP;
171 }
172 
173 #if defined(CONFIG_GPIO_GET_CONFIG) || defined(CONFIG_GPIO_GET_DIRECTION)
gpio_axp192_get_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t * out_flags)174 static int gpio_axp192_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *out_flags)
175 {
176 	const struct gpio_axp192_config *config = dev->config;
177 	enum axp192_gpio_func func;
178 	bool pd_enabled;
179 	int ret;
180 
181 	if (k_is_in_isr()) {
182 		return -EWOULDBLOCK;
183 	}
184 
185 	ret = mfd_axp192_gpio_func_get(config->mfd, pin, &func);
186 	if (ret != 0) {
187 		return ret;
188 	}
189 
190 	/* Set OUTPUT/INPUT flags */
191 	*out_flags = 0;
192 	switch (func) {
193 	case AXP192_GPIO_FUNC_INPUT:
194 		*out_flags |= GPIO_INPUT;
195 		break;
196 	case AXP192_GPIO_FUNC_OUTPUT_OD:
197 		*out_flags |= GPIO_OUTPUT | GPIO_OPEN_DRAIN;
198 		break;
199 	case AXP192_GPIO_FUNC_OUTPUT_LOW:
200 		*out_flags |= GPIO_OUTPUT;
201 		break;
202 
203 	case AXP192_GPIO_FUNC_LDO:
204 		__fallthrough;
205 	case AXP192_GPIO_FUNC_ADC:
206 		__fallthrough;
207 	case AXP192_GPIO_FUNC_FLOAT:
208 		__fallthrough;
209 	default:
210 		LOG_DBG("Pin %d not configured as GPIO", pin);
211 		break;
212 	}
213 
214 	/* Query pull-down config status  */
215 	ret = mfd_axp192_gpio_pd_get(config->mfd, pin, &pd_enabled);
216 	if (ret != 0) {
217 		return ret;
218 	}
219 
220 	if (pd_enabled) {
221 		*out_flags |= GPIO_PULL_DOWN;
222 	}
223 
224 	return 0;
225 }
226 #endif /* CONFIG_GPIO_GET_CONFIG */
227 
228 #ifdef CONFIG_GPIO_GET_DIRECTION
gpio_axp192_port_get_direction(const struct device * dev,gpio_port_pins_t map,gpio_port_pins_t * inputs,gpio_port_pins_t * outputs)229 static int gpio_axp192_port_get_direction(const struct device *dev, gpio_port_pins_t map,
230 					  gpio_port_pins_t *inputs, gpio_port_pins_t *outputs)
231 {
232 	const struct gpio_axp192_config *config = dev->config;
233 	gpio_flags_t flags;
234 	int ret;
235 
236 	/* reset output variables */
237 	*inputs = 0;
238 	*outputs = 0;
239 
240 	/* loop through all  */
241 	for (gpio_pin_t gpio = 0; gpio < config->ngpios; gpio++) {
242 
243 		if ((map & (1u << gpio)) != 0) {
244 
245 			/* use internal get_config method to get gpio flags */
246 			ret = gpio_axp192_get_config(dev, gpio, &flags);
247 			if (ret != 0) {
248 				return ret;
249 			}
250 
251 			/* Set output and input flags */
252 			if ((flags & GPIO_OUTPUT) != 0) {
253 				*outputs |= (1u << gpio);
254 			} else if (0 != (flags & GPIO_INPUT)) {
255 				*inputs |= (1u << gpio);
256 			}
257 		}
258 	}
259 
260 	return 0;
261 }
262 #endif /* CONFIG_GPIO_GET_DIRECTION */
263 
gpio_axp192_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)264 static int gpio_axp192_manage_callback(const struct device *dev, struct gpio_callback *callback,
265 				       bool set)
266 {
267 	struct gpio_axp192_data *const data = dev->data;
268 
269 	return gpio_manage_callback(&data->cb_list_gpio, callback, set);
270 }
271 
272 static const struct gpio_driver_api gpio_axp192_api = {
273 	.pin_configure = gpio_axp192_configure,
274 	.port_get_raw = gpio_axp192_port_get_raw,
275 	.port_set_masked_raw = gpio_axp192_port_set_masked_raw,
276 	.port_set_bits_raw = gpio_axp192_port_set_bits_raw,
277 	.port_clear_bits_raw = gpio_axp192_port_clear_bits_raw,
278 	.port_toggle_bits = gpio_axp192_port_toggle_bits,
279 	.pin_interrupt_configure = gpio_axp192_pin_interrupt_configure,
280 	.manage_callback = gpio_axp192_manage_callback,
281 #ifdef CONFIG_GPIO_GET_DIRECTION
282 	.port_get_direction = gpio_axp192_port_get_direction,
283 #endif /* CONFIG_GPIO_GET_DIRECTION */
284 #ifdef CONFIG_GPIO_GET_CONFIG
285 	.pin_get_config = gpio_axp192_get_config,
286 #endif
287 };
288 
gpio_axp192_init(const struct device * dev)289 static int gpio_axp192_init(const struct device *dev)
290 {
291 	const struct gpio_axp192_config *config = dev->config;
292 	struct gpio_axp192_data *data = dev->data;
293 
294 	LOG_DBG("Initializing");
295 
296 	if (!i2c_is_ready_dt(&config->i2c)) {
297 		LOG_ERR("device not ready");
298 		return -ENODEV;
299 	}
300 
301 	return k_mutex_init(&data->mutex);
302 }
303 
304 #define GPIO_AXP192_DEFINE(inst)                                                                   \
305 	static const struct gpio_axp192_config gpio_axp192_config##inst = {                        \
306 		.common =                                                                          \
307 			{                                                                          \
308 				.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst),            \
309 			},                                                                         \
310 		.i2c = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)),                                      \
311 		.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)),                                        \
312 		.ngpios = DT_INST_PROP(inst, ngpios),                                              \
313 	};                                                                                         \
314                                                                                                    \
315 	static struct gpio_axp192_data gpio_axp192_data##inst;                                     \
316                                                                                                    \
317 	DEVICE_DT_INST_DEFINE(inst, &gpio_axp192_init, NULL, &gpio_axp192_data##inst,              \
318 			      &gpio_axp192_config##inst, POST_KERNEL,                              \
319 			      CONFIG_GPIO_AXP192_INIT_PRIORITY, &gpio_axp192_api);
320 
321 DT_INST_FOREACH_STATUS_OKAY(GPIO_AXP192_DEFINE)
322