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, gpio_flags_t flags)
79 {
80 	const struct gpio_axp192_config *config = dev->config;
81 	int ret;
82 	enum axp192_gpio_func func;
83 
84 	if (pin >= config->ngpios) {
85 		LOG_ERR("Invalid gpio pin (%d)", pin);
86 		return -EINVAL;
87 	}
88 
89 	if (k_is_in_isr()) {
90 		return -EWOULDBLOCK;
91 	}
92 
93 	/* Configure pin */
94 	LOG_DBG("Pin: %d / flags=0x%x", pin, flags);
95 	if ((flags & GPIO_OUTPUT) != 0) {
96 
97 		/* Initialize output function */
98 		func = AXP192_GPIO_FUNC_OUTPUT_LOW;
99 		if ((flags & GPIO_OPEN_DRAIN) != 0) {
100 			func = AXP192_GPIO_FUNC_OUTPUT_OD;
101 		}
102 		ret = mfd_axp192_gpio_func_ctrl(config->mfd, dev, pin, func);
103 		if (ret != 0) {
104 			return ret;
105 		}
106 
107 		/* Set init value */
108 		if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
109 			ret = mfd_axp192_gpio_write_port(config->mfd, BIT(pin), 0);
110 		} else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
111 			ret = mfd_axp192_gpio_write_port(config->mfd, BIT(pin), BIT(pin));
112 		}
113 	} else if ((flags & GPIO_INPUT) != 0) {
114 
115 		/* Initialize input function */
116 		func = AXP192_GPIO_FUNC_INPUT;
117 
118 		ret = mfd_axp192_gpio_func_ctrl(config->mfd, dev, pin, func);
119 		if (ret != 0) {
120 			return ret;
121 		}
122 
123 		/* Configure pull-down */
124 		if ((flags & GPIO_PULL_UP) != 0) {
125 			/* not supported */
126 			LOG_ERR("Pull-Up not supported");
127 			ret = -ENOTSUP;
128 		} else if ((flags & GPIO_PULL_DOWN) != 0) {
129 			/* out = 0 means pull-down*/
130 			ret = mfd_axp192_gpio_pd_ctrl(config->mfd, pin, true);
131 		} else {
132 			ret = mfd_axp192_gpio_pd_ctrl(config->mfd, pin, false);
133 		}
134 	} else {
135 		/* Neither input nor output mode is selected */
136 		LOG_INF("No valid gpio mode selected");
137 		ret = -ENOTSUP;
138 	}
139 
140 	return ret;
141 }
142 
gpio_axp192_port_toggle_bits(const struct device * dev,gpio_port_pins_t pins)143 static int gpio_axp192_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
144 {
145 	struct gpio_axp192_data *data = dev->data;
146 	int ret;
147 	uint32_t value;
148 
149 	k_mutex_lock(&data->mutex, K_FOREVER);
150 
151 	ret = gpio_axp192_port_get_raw(dev, &value);
152 	if (ret == 0) {
153 		ret = gpio_axp192_port_set_masked_raw(dev, pins, ~value);
154 	}
155 
156 	k_mutex_unlock(&data->mutex);
157 
158 	return ret;
159 }
160 
gpio_axp192_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)161 static int gpio_axp192_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
162 					       enum gpio_int_mode mode, enum gpio_int_trig trig)
163 {
164 	ARG_UNUSED(dev);
165 	ARG_UNUSED(pin);
166 	ARG_UNUSED(mode);
167 	ARG_UNUSED(trig);
168 
169 	return -ENOTSUP;
170 }
171 
172 #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)173 static int gpio_axp192_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *out_flags)
174 {
175 	const struct gpio_axp192_config *config = dev->config;
176 	enum axp192_gpio_func func;
177 	bool pd_enabled;
178 	int ret;
179 
180 	if (k_is_in_isr()) {
181 		return -EWOULDBLOCK;
182 	}
183 
184 	ret = mfd_axp192_gpio_func_get(config->mfd, pin, &func);
185 	if (ret != 0) {
186 		return ret;
187 	}
188 
189 	/* Set OUTPUT/INPUT flags */
190 	*out_flags = 0;
191 	switch (func) {
192 	case AXP192_GPIO_FUNC_INPUT:
193 		*out_flags |= GPIO_INPUT;
194 		break;
195 	case AXP192_GPIO_FUNC_OUTPUT_OD:
196 		*out_flags |= GPIO_OUTPUT | GPIO_OPEN_DRAIN;
197 		break;
198 	case AXP192_GPIO_FUNC_OUTPUT_LOW:
199 		*out_flags |= GPIO_OUTPUT;
200 		break;
201 
202 	case AXP192_GPIO_FUNC_LDO:
203 		__fallthrough;
204 	case AXP192_GPIO_FUNC_ADC:
205 		__fallthrough;
206 	case AXP192_GPIO_FUNC_FLOAT:
207 		__fallthrough;
208 	default:
209 		LOG_DBG("Pin %d not configured as GPIO", pin);
210 		break;
211 	}
212 
213 	/* Query pull-down config status  */
214 	ret = mfd_axp192_gpio_pd_get(config->mfd, pin, &pd_enabled);
215 	if (ret != 0) {
216 		return ret;
217 	}
218 
219 	if (pd_enabled) {
220 		*out_flags |= GPIO_PULL_DOWN;
221 	}
222 
223 	return 0;
224 }
225 #endif /* CONFIG_GPIO_GET_CONFIG */
226 
227 #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)228 static int gpio_axp192_port_get_direction(const struct device *dev, gpio_port_pins_t map,
229 					  gpio_port_pins_t *inputs, gpio_port_pins_t *outputs)
230 {
231 	const struct gpio_axp192_config *config = dev->config;
232 	gpio_flags_t flags;
233 	int ret;
234 
235 	/* reset output variables */
236 	*inputs = 0;
237 	*outputs = 0;
238 
239 	/* loop through all  */
240 	for (gpio_pin_t gpio = 0; gpio < config->ngpios; gpio++) {
241 
242 		if ((map & (1u << gpio)) != 0) {
243 
244 			/* use internal get_config method to get gpio flags */
245 			ret = gpio_axp192_get_config(dev, gpio, &flags);
246 			if (ret != 0) {
247 				return ret;
248 			}
249 
250 			/* Set output and input flags */
251 			if ((flags & GPIO_OUTPUT) != 0) {
252 				*outputs |= (1u << gpio);
253 			} else if (0 != (flags & GPIO_INPUT)) {
254 				*inputs |= (1u << gpio);
255 			}
256 		}
257 	}
258 
259 	return 0;
260 }
261 #endif /* CONFIG_GPIO_GET_DIRECTION */
262 
gpio_axp192_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)263 static int gpio_axp192_manage_callback(const struct device *dev, struct gpio_callback *callback,
264 				       bool set)
265 {
266 	struct gpio_axp192_data *const data = dev->data;
267 
268 	return gpio_manage_callback(&data->cb_list_gpio, callback, set);
269 }
270 
271 static DEVICE_API(gpio, gpio_axp192_api) = {
272 	.pin_configure = gpio_axp192_configure,
273 	.port_get_raw = gpio_axp192_port_get_raw,
274 	.port_set_masked_raw = gpio_axp192_port_set_masked_raw,
275 	.port_set_bits_raw = gpio_axp192_port_set_bits_raw,
276 	.port_clear_bits_raw = gpio_axp192_port_clear_bits_raw,
277 	.port_toggle_bits = gpio_axp192_port_toggle_bits,
278 	.pin_interrupt_configure = gpio_axp192_pin_interrupt_configure,
279 	.manage_callback = gpio_axp192_manage_callback,
280 #ifdef CONFIG_GPIO_GET_DIRECTION
281 	.port_get_direction = gpio_axp192_port_get_direction,
282 #endif /* CONFIG_GPIO_GET_DIRECTION */
283 #ifdef CONFIG_GPIO_GET_CONFIG
284 	.pin_get_config = gpio_axp192_get_config,
285 #endif
286 };
287 
gpio_axp192_init(const struct device * dev)288 static int gpio_axp192_init(const struct device *dev)
289 {
290 	const struct gpio_axp192_config *config = dev->config;
291 	struct gpio_axp192_data *data = dev->data;
292 
293 	LOG_DBG("Initializing");
294 
295 	if (!i2c_is_ready_dt(&config->i2c)) {
296 		LOG_ERR("device not ready");
297 		return -ENODEV;
298 	}
299 
300 	return k_mutex_init(&data->mutex);
301 }
302 
303 #define GPIO_AXP192_DEFINE(inst)                                                                   \
304 	static const struct gpio_axp192_config gpio_axp192_config##inst = {                        \
305 		.common =                                                                          \
306 			{                                                                          \
307 				.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst),            \
308 			},                                                                         \
309 		.i2c = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)),                                      \
310 		.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)),                                        \
311 		.ngpios = DT_INST_PROP(inst, ngpios),                                              \
312 	};                                                                                         \
313                                                                                                    \
314 	static struct gpio_axp192_data gpio_axp192_data##inst;                                     \
315                                                                                                    \
316 	DEVICE_DT_INST_DEFINE(inst, gpio_axp192_init, NULL, &gpio_axp192_data##inst,               \
317 			      &gpio_axp192_config##inst, POST_KERNEL,                              \
318 			      CONFIG_GPIO_AXP192_INIT_PRIORITY, &gpio_axp192_api);
319 
320 DT_INST_FOREACH_STATUS_OKAY(GPIO_AXP192_DEFINE)
321