1 /*
2 * Copyright 2024 NXP
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/drivers/gpio.h>
9 #include <zephyr/drivers/gpio/gpio_utils.h>
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/logging/log.h>
12 #include <zephyr/sys/byteorder.h>
13
14 #include <zephyr/drivers/mfd/adp5585.h>
15
16 #define DT_DRV_COMPAT adi_adp5585_gpio
17
18 LOG_MODULE_REGISTER(adp5585_gpio, CONFIG_GPIO_LOG_LEVEL);
19
20 #define ADP5585_BANK(offs) (offs >> 3)
21 #define ADP5585_BIT(offs) (offs & GENMASK(2, 0))
22
23 enum adp5585_gpio_pin_direction {
24 adp5585_pin_input = 0U,
25 adp5585_pin_output,
26 };
27
28 enum adp5585_gpio_pin_drive_mode {
29 adp5585_pin_drive_pp = 0U,
30 adp5585_pin_drive_od,
31 };
32
33 enum adp5585_gpio_pull_config {
34 adp5585_pull_up_300k = 0U,
35 adp5585_pull_dn_300k,
36 adp5585_pull_up_100k, /* not used */
37 adp5585_pull_disable,
38 };
39
40 enum adp5585_gpio_int_en {
41 adp5585_int_disable = 0U,
42 adp5585_int_enable,
43 };
44
45 enum adp5585_gpio_int_level {
46 adp5585_int_active_low = 0U,
47 adp5585_int_active_high,
48 };
49
50 /** Configuration data */
51 struct adp5585_gpio_config {
52 /* gpio_driver_config needs to be first */
53 struct gpio_driver_config common;
54 const struct device *mfd_dev;
55 const struct gpio_dt_spec gpio_int;
56 };
57
58 /** Runtime driver data */
59 struct adp5585_gpio_data {
60 /* gpio_driver_data needs to be first */
61 struct gpio_driver_data common;
62 uint16_t output;
63
64 sys_slist_t callbacks;
65 };
66
gpio_adp5585_config(const struct device * dev,gpio_pin_t pin,gpio_flags_t flags)67 static int gpio_adp5585_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags)
68 {
69 const struct adp5585_gpio_config *cfg = dev->config;
70 struct adp5585_gpio_data *data = dev->data;
71 const struct mfd_adp5585_config *parent_cfg =
72 (struct mfd_adp5585_config *)(cfg->mfd_dev->config);
73 struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
74
75 int ret = 0;
76 uint8_t reg_value;
77
78 /* ADP5585 has non-contiguous gpio pin layouts, account for this */
79 if ((pin & cfg->common.port_pin_mask) == 0) {
80 LOG_ERR("pin %d is invalid for this device", pin);
81 return -ENOTSUP;
82 }
83
84 uint8_t bank = ADP5585_BANK(pin);
85 uint8_t bank_pin = ADP5585_BIT(pin);
86
87 /* Can't do I2C bus operations from an ISR */
88 if (k_is_in_isr()) {
89 return -EWOULDBLOCK;
90 }
91
92 /* Simultaneous PU & PD mode not supported */
93 if (((flags & GPIO_PULL_UP) != 0) && ((flags & GPIO_PULL_DOWN) != 0)) {
94 return -ENOTSUP;
95 }
96
97 /* Simultaneous input & output mode not supported */
98 if (((flags & GPIO_INPUT) != 0) && ((flags & GPIO_OUTPUT) != 0)) {
99 return -ENOTSUP;
100 }
101
102 k_sem_take(&parent_data->lock, K_FOREVER);
103
104 if ((flags & GPIO_SINGLE_ENDED) != 0) {
105 reg_value = adp5585_pin_drive_od << bank_pin;
106 } else {
107 reg_value = adp5585_pin_drive_pp << bank_pin;
108 }
109 ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus, ADP5585_GPO_OUT_MODE_A + bank,
110 BIT(bank_pin), reg_value);
111 if (ret != 0) {
112 goto out;
113 }
114
115 uint8_t regaddr = ADP5585_RPULL_CONFIG_A + (bank << 1);
116 uint8_t shift = bank_pin << 1;
117
118 if (bank_pin > 3U) {
119 regaddr += 1U;
120 shift = (bank_pin - 3U) << 1;
121 }
122 if ((flags & GPIO_PULL_UP) != 0) {
123 reg_value = adp5585_pull_up_300k << shift;
124 } else if ((flags & GPIO_PULL_DOWN) != 0) {
125 reg_value = adp5585_pull_dn_300k << shift;
126 } else {
127 reg_value = adp5585_pull_disable << shift;
128 }
129
130 ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus, regaddr,
131 0b11U << shift, reg_value);
132 if (ret != 0) {
133 goto out;
134 }
135
136 /* Ensure either Output or Input is specified */
137 if ((flags & GPIO_OUTPUT) != 0) {
138
139 /* Set Low or High if specified */
140 if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) {
141 data->output &= ~BIT(pin);
142 } else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
143 data->output |= BIT(pin);
144 }
145 if (bank == 0) {
146 /* reg_value for ADP5585_GPO_OUT_MODE */
147 reg_value = (uint8_t)data->output;
148 } else {
149 /* reg_value for ADP5585_GPO_OUT_MODE */
150 reg_value = (uint8_t)(data->output >> 8);
151 }
152 ret = i2c_reg_write_byte_dt(&parent_cfg->i2c_bus,
153 ADP5585_GPO_OUT_MODE_A + bank,
154 reg_value);
155 if (ret != 0) {
156 goto out;
157 }
158 /* reg_value for ADP5585_GPIO_DIRECTION */
159 reg_value = adp5585_pin_output << bank_pin;
160 } else if ((flags & GPIO_INPUT) != 0) {
161 /* reg_value for ADP5585_GPIO_DIRECTION */
162 reg_value = adp5585_pin_output << bank_pin;
163 }
164
165 ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus,
166 ADP5585_GPIO_DIRECTION_A + bank,
167 BIT(bank_pin), reg_value);
168
169 out:
170 k_sem_give(&parent_data->lock);
171 if (ret != 0) {
172 LOG_ERR("pin configure error: %d", ret);
173 }
174 return ret;
175 }
176
gpio_adp5585_port_read(const struct device * dev,gpio_port_value_t * value)177 static int gpio_adp5585_port_read(const struct device *dev, gpio_port_value_t *value)
178 {
179 const struct adp5585_gpio_config *cfg = dev->config;
180 /* struct adp5585_gpio_data *data = dev->data; */
181 const struct mfd_adp5585_config *parent_cfg =
182 (struct mfd_adp5585_config *)(cfg->mfd_dev->config);
183 struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
184
185 uint16_t input_data = 0;
186 int ret = 0;
187
188 /* Can't do I2C bus operations from an ISR */
189 if (k_is_in_isr()) {
190 return -EWOULDBLOCK;
191 }
192
193 k_sem_take(&parent_data->lock, K_FOREVER);
194
195 /** Read Input Register */
196
197 uint8_t gpi_status_reg;
198 uint8_t gpi_status_buf[2];
199
200 ret = i2c_write_read_dt(&parent_cfg->i2c_bus, &gpi_status_reg, 1U,
201 gpi_status_buf, 2U);
202 if (ret) {
203 goto out;
204 }
205 input_data = sys_le16_to_cpu(*((uint16_t *)gpi_status_buf));
206 *value = input_data;
207
208 out:
209 k_sem_give(&parent_data->lock);
210 LOG_DBG("read %x got %d", input_data, ret);
211 return ret;
212 }
213
gpio_adp5585_port_write(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value,gpio_port_value_t toggle)214 static int gpio_adp5585_port_write(const struct device *dev, gpio_port_pins_t mask,
215 gpio_port_value_t value, gpio_port_value_t toggle)
216 {
217 const struct adp5585_gpio_config *cfg = dev->config;
218 struct adp5585_gpio_data *data = dev->data;
219 const struct mfd_adp5585_config *parent_cfg =
220 (struct mfd_adp5585_config *)(cfg->mfd_dev->config);
221 struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
222
223 uint16_t orig_out;
224 uint16_t out;
225 uint8_t reg_value;
226 int ret;
227
228 /* Can't do I2C bus operations from an ISR */
229 if (k_is_in_isr()) {
230 return -EWOULDBLOCK;
231 }
232
233 k_sem_take(&parent_data->lock, K_FOREVER);
234
235 orig_out = data->output;
236 out = ((orig_out & ~mask) | (value & mask)) ^ toggle;
237
238 reg_value = (uint8_t)out;
239 uint8_t gpo_data_out_buf[] = { ADP5585_GPO_DATA_OUT_A,
240 (uint8_t)out, (uint8_t)(out >> 8) };
241
242 ret = i2c_write_dt(&parent_cfg->i2c_bus, gpo_data_out_buf, sizeof(gpo_data_out_buf));
243 if (ret) {
244 goto out;
245 }
246
247 data->output = out;
248
249 out:
250 k_sem_give(&parent_data->lock);
251 LOG_DBG("write %x msk %08x val %08x => %x: %d", orig_out, mask, value, out, ret);
252 return ret;
253 }
254
gpio_adp5585_port_set_masked(const struct device * dev,gpio_port_pins_t mask,gpio_port_value_t value)255 static int gpio_adp5585_port_set_masked(const struct device *dev, gpio_port_pins_t mask,
256 gpio_port_value_t value)
257 {
258 return gpio_adp5585_port_write(dev, mask, value, 0);
259 }
260
gpio_adp5585_port_set_bits(const struct device * dev,gpio_port_pins_t pins)261 static int gpio_adp5585_port_set_bits(const struct device *dev, gpio_port_pins_t pins)
262 {
263 return gpio_adp5585_port_write(dev, pins, pins, 0);
264 }
265
gpio_adp5585_port_clear_bits(const struct device * dev,gpio_port_pins_t pins)266 static int gpio_adp5585_port_clear_bits(const struct device *dev, gpio_port_pins_t pins)
267 {
268 return gpio_adp5585_port_write(dev, pins, 0, 0);
269 }
270
gpio_adp5585_port_toggle_bits(const struct device * dev,gpio_port_pins_t pins)271 static int gpio_adp5585_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins)
272 {
273 return gpio_adp5585_port_write(dev, 0, 0, pins);
274 }
275
gpio_adp5585_pin_interrupt_configure(const struct device * dev,gpio_pin_t pin,enum gpio_int_mode mode,enum gpio_int_trig trig)276 static int gpio_adp5585_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin,
277 enum gpio_int_mode mode, enum gpio_int_trig trig)
278 {
279 const struct adp5585_gpio_config *cfg = dev->config;
280 /* struct adp5585_gpio_data *data = dev->data; */
281 const struct mfd_adp5585_config *parent_cfg =
282 (struct mfd_adp5585_config *)(cfg->mfd_dev->config);
283 struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
284 int ret = 0;
285
286 if (parent_cfg->nint_gpio.port == NULL) {
287 return -ENOTSUP;
288 }
289
290 /* ADP5585 has non-contiguous gpio pin layouts, account for this */
291 if ((pin & cfg->common.port_pin_mask) == 0) {
292 LOG_ERR("pin %d is invalid for this device", pin);
293 return -ENOTSUP;
294 }
295
296 /* This device supports only level-triggered interrupts. */
297 /* This device does NOT support either-level interrupt. */
298 if (mode == GPIO_INT_MODE_EDGE || trig == GPIO_INT_TRIG_BOTH) {
299 return -ENOTSUP;
300 }
301 if (k_is_in_isr()) {
302 return -EWOULDBLOCK;
303 }
304
305 uint8_t bank = ADP5585_BANK(pin);
306 uint8_t bank_pin = ADP5585_BIT(pin);
307
308 k_sem_take(&parent_data->lock, K_FOREVER);
309
310 if (mode == GPIO_INT_MODE_DISABLED) {
311 ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus,
312 ADP5585_GPI_INTERRUPT_EN_A + bank, BIT(bank_pin),
313 (adp5585_int_disable << bank_pin));
314 } else if ((trig & GPIO_INT_TRIG_BOTH) != 0) {
315 if (trig == GPIO_INT_TRIG_LOW) {
316 ret = i2c_reg_update_byte_dt(
317 &parent_cfg->i2c_bus, ADP5585_GPI_INT_LEVEL_A + bank,
318 BIT(bank_pin), (adp5585_int_active_low << bank_pin));
319 } else {
320 ret = i2c_reg_update_byte_dt(
321 &parent_cfg->i2c_bus, ADP5585_GPI_INT_LEVEL_A + bank,
322 BIT(bank_pin), (adp5585_int_active_high << bank_pin));
323 }
324
325 /* make sure GPI_n_EVENT_EN is disabled, otherwise it will generate FIFO event */
326 ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus,
327 ADP5585_GPI_EVENT_EN_A + bank, BIT(bank_pin), 0U);
328 ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus,
329 ADP5585_GPI_INTERRUPT_EN_A + bank,
330 BIT(bank_pin), (adp5585_int_enable << bank_pin));
331 }
332
333 k_sem_give(&parent_data->lock);
334 return ret;
335 }
336
gpio_adp5585_manage_callback(const struct device * dev,struct gpio_callback * callback,bool set)337 static int gpio_adp5585_manage_callback(const struct device *dev, struct gpio_callback *callback,
338 bool set)
339 {
340 struct adp5585_gpio_data *data = dev->data;
341
342 return gpio_manage_callback(&data->callbacks, callback, set);
343 }
344
gpio_adp5585_irq_handler(const struct device * dev)345 void gpio_adp5585_irq_handler(const struct device *dev)
346 {
347 const struct adp5585_gpio_config *cfg = dev->config;
348 struct adp5585_gpio_data *data = dev->data;
349 const struct mfd_adp5585_config *parent_cfg =
350 (struct mfd_adp5585_config *)(cfg->mfd_dev->config);
351 struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
352
353 uint16_t reg_int_status;
354 int ret = 0;
355
356 k_sem_take(&parent_data->lock, K_FOREVER);
357
358 /* Read Input Register */
359 ret = i2c_burst_read_dt(&parent_cfg->i2c_bus, ADP5585_GPI_INT_STAT_A,
360 (uint8_t *)®_int_status, 2U);
361 if (ret != 0) {
362 LOG_WRN("%s failed to read interrupt status %d", dev->name, ret);
363 goto out;
364 }
365
366 out:
367 k_sem_give(&parent_data->lock);
368
369 if (ret == 0 && reg_int_status != 0) {
370 gpio_fire_callbacks(&data->callbacks, dev, reg_int_status);
371 }
372 }
373
374 /**
375 * @brief Initialization function of ADP5585_GPIO
376 *
377 * This sets initial input/ output configuration and output states.
378 * The interrupt is configured if this is enabled.
379 *
380 * @param dev Device struct
381 * @return 0 if successful, failed otherwise.
382 */
gpio_adp5585_init(const struct device * dev)383 static int gpio_adp5585_init(const struct device *dev)
384 {
385 const struct adp5585_gpio_config *cfg = dev->config;
386 struct adp5585_gpio_data *data = dev->data;
387 const struct mfd_adp5585_config *parent_cfg =
388 (struct mfd_adp5585_config *)(cfg->mfd_dev->config);
389 struct mfd_adp5585_data *parent_data = (struct mfd_adp5585_data *)(cfg->mfd_dev->data);
390 int ret = 0;
391
392 if (!device_is_ready(cfg->mfd_dev)) {
393 LOG_ERR("%s: parent dev not ready", dev->name);
394 ret = -ENODEV;
395 goto out;
396 }
397
398 if (!device_is_ready(parent_cfg->i2c_bus.bus)) {
399 LOG_ERR("I2C bus device not found");
400 ret = -EIO;
401 goto out;
402 }
403
404 k_sem_take(&parent_data->lock, K_FOREVER);
405
406 /** Read output register */
407 uint8_t gpo_data_out_buf[] = { ADP5585_GPO_DATA_OUT_A,
408 0x00, 0x00 };
409
410 ret = i2c_write_read_dt(&parent_cfg->i2c_bus, gpo_data_out_buf, 1U,
411 gpo_data_out_buf + 1, 2U);
412 if (ret) {
413 goto out;
414 }
415 data->output = sys_le16_to_cpu(*((uint16_t *)(gpo_data_out_buf + 1)));
416
417 /** Set RPULL to high-z by default */
418 uint8_t rpull_config_buf[] = { ADP5585_RPULL_CONFIG_A,
419 0xffU, 0x03U, 0xffU, 0x03U };
420
421 ret = i2c_write_dt(&parent_cfg->i2c_bus, rpull_config_buf, sizeof(rpull_config_buf));
422 if (ret) {
423 goto out;
424 }
425
426 parent_data->child.gpio_dev = dev;
427
428 /** Enable GPI interrupt */
429 if ((ret == 0) && gpio_is_ready_dt(&parent_cfg->nint_gpio)) {
430 ret = i2c_reg_update_byte_dt(&parent_cfg->i2c_bus, ADP5585_INT_EN, (1U << 1),
431 (1U << 1));
432 }
433
434 out:
435 k_sem_give(&parent_data->lock);
436 if (ret) {
437 LOG_ERR("%s init failed: %d", dev->name, ret);
438 } else {
439 LOG_INF("%s init ok", dev->name);
440 }
441 return ret;
442 }
443
444 static DEVICE_API(gpio, api_table) = {
445 .pin_configure = gpio_adp5585_config,
446 .port_get_raw = gpio_adp5585_port_read,
447 .port_set_masked_raw = gpio_adp5585_port_set_masked,
448 .port_set_bits_raw = gpio_adp5585_port_set_bits,
449 .port_clear_bits_raw = gpio_adp5585_port_clear_bits,
450 .port_toggle_bits = gpio_adp5585_port_toggle_bits,
451 .pin_interrupt_configure = gpio_adp5585_pin_interrupt_configure,
452 .manage_callback = gpio_adp5585_manage_callback,
453 };
454
455 #define GPIO_ADP5585_INIT(inst) \
456 static const struct adp5585_gpio_config adp5585_gpio_cfg_##inst = { \
457 .common = { \
458 .port_pin_mask = GPIO_DT_INST_PORT_PIN_MASK_NGPIOS_EXC( \
459 inst, DT_INST_PROP(inst, ngpios)) \
460 }, \
461 .mfd_dev = DEVICE_DT_GET(DT_INST_PARENT(inst)), \
462 }; \
463 static struct adp5585_gpio_data adp5585_gpio_drvdata_##inst; \
464 DEVICE_DT_INST_DEFINE(inst, gpio_adp5585_init, NULL, \
465 &adp5585_gpio_drvdata_##inst, \
466 &adp5585_gpio_cfg_##inst, POST_KERNEL, \
467 CONFIG_GPIO_ADP5585_INIT_PRIORITY, &api_table);
468
469 DT_INST_FOREACH_STATUS_OKAY(GPIO_ADP5585_INIT)
470