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