/* * Copyright (c) 2024 Analog Devices Inc. * Copyright (c) 2024 Baylibre SAS * * SPDX-License-Identifier: Apache-2.0 */ #include #include #define DT_DRV_COMPAT adi_max22017_gpio #include LOG_MODULE_REGISTER(gpio_max22017, CONFIG_GPIO_LOG_LEVEL); #include #include struct gpio_adi_max22017_config { /* gpio_driver_config needs to be first */ struct gpio_driver_config common; const struct device *parent; }; struct gpio_adi_max22017_data { /* gpio_driver_data needs to be first */ struct gpio_driver_data common; #ifdef CONFIG_GPIO_MAX22017_INT_QUIRK struct k_timer int_quirk_timer; #endif }; #ifdef CONFIG_GPIO_MAX22017_INT_QUIRK void isr_quirk_handler(struct k_timer *int_quirk_timer) { int ret; struct max22017_data *data = k_timer_user_data_get(int_quirk_timer); k_mutex_lock(&data->lock, K_FOREVER); ret = k_work_submit(&data->int_work); if (ret < 0) { LOG_WRN("Could not submit int work: %d", ret); } k_mutex_unlock(&data->lock); } #endif static int adi_max22017_gpio_set_output(const struct device *dev, uint8_t pin, bool initial_value) { int ret; uint16_t gpio_data, gpio_ctrl; struct max22017_data *data = dev->data; k_mutex_lock(&data->lock, K_FOREVER); ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); if (ret) { goto fail; } ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_CTRL_OFF, &gpio_ctrl); if (ret) { goto fail; } if (initial_value) { gpio_data |= FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, BIT(pin)); } else { gpio_data &= ~FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, BIT(pin)); } gpio_ctrl |= FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_EN, BIT(pin)) | FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_DIR, BIT(pin)); ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_DATA_OFF, gpio_data); if (ret) { goto fail; } ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_CTRL_OFF, gpio_ctrl); fail: k_mutex_unlock(&data->lock); return ret; } static int adi_max22017_gpio_set_input(const struct device *dev, uint8_t pin) { int ret; uint16_t gpio_ctrl; struct max22017_data *data = dev->data; k_mutex_lock(&data->lock, K_FOREVER); ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_CTRL_OFF, &gpio_ctrl); if (ret) { goto fail; } gpio_ctrl |= FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_EN, BIT(pin)); gpio_ctrl &= ~FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_DIR, BIT(pin)); ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_CTRL_OFF, gpio_ctrl); fail: k_mutex_unlock(&data->lock); return ret; } int adi_max22017_gpio_deconfigure(const struct device *dev, uint8_t pin) { int ret; uint16_t gpio_ctrl; struct max22017_data *data = dev->data; k_mutex_lock(&data->lock, K_FOREVER); ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_CTRL_OFF, &gpio_ctrl); if (ret) { goto fail; } gpio_ctrl &= ~FIELD_PREP(MAX22017_GEN_GPIO_CTRL_GPIO_EN, BIT(pin)); ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_CTRL_OFF, gpio_ctrl); fail: k_mutex_unlock(&data->lock); return ret; } int adi_max22017_gpio_set_pin_value(const struct device *dev, uint8_t pin, bool value) { int ret; uint16_t gpio_data; struct max22017_data *data = dev->data; k_mutex_lock(&data->lock, K_FOREVER); ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); if (ret) { goto fail; } if (value) { gpio_data |= FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, BIT(pin)); } else { gpio_data &= ~FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, BIT(pin)); } ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_DATA_OFF, gpio_data); fail: k_mutex_unlock(&data->lock); return ret; } int adi_max22017_gpio_get_pin_value(const struct device *dev, uint8_t pin, bool *value) { int ret; uint16_t gpio_data; struct max22017_data *data = dev->data; k_mutex_lock(&data->lock, K_FOREVER); ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); if (ret) { goto fail; } *value = FIELD_GET(MAX22017_GEN_GPIO_DATA_GPI_DATA, gpio_data) & BIT(pin); fail: k_mutex_unlock(&data->lock); return ret; } static int adi_max22017_gpio_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, gpio_port_value_t value) { int ret; uint16_t gpio_data, tmp_val; struct max22017_data *data = dev->data; k_mutex_lock(&data->lock, K_FOREVER); ret = max22017_reg_read(dev, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); if (ret) { goto fail; } tmp_val = FIELD_GET(MAX22017_GEN_GPIO_DATA_GPO_DATA, gpio_data); tmp_val = (tmp_val & ~mask) | (value & mask); gpio_data = FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, tmp_val) | FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPI_DATA, FIELD_GET(MAX22017_GEN_GPIO_DATA_GPI_DATA, gpio_data)); ret = max22017_reg_write(dev, MAX22017_GEN_GPIO_DATA_OFF, gpio_data); fail: k_mutex_unlock(&data->lock); return ret; } static int gpio_adi_max22017_configure(const struct device *dev, gpio_pin_t pin, gpio_flags_t flags) { const struct gpio_adi_max22017_config *config = dev->config; int err = -EINVAL; if ((flags & (GPIO_INPUT | GPIO_OUTPUT)) == GPIO_DISCONNECTED) { return adi_max22017_gpio_deconfigure(config->parent, pin); } if ((flags & GPIO_SINGLE_ENDED) != 0) { return -ENOTSUP; } if ((flags & (GPIO_PULL_UP | GPIO_PULL_DOWN)) != 0) { return -ENOTSUP; } switch (flags & GPIO_DIR_MASK) { case GPIO_INPUT: err = adi_max22017_gpio_set_input(config->parent, pin); break; case GPIO_OUTPUT: err = adi_max22017_gpio_set_output(config->parent, pin, (flags & GPIO_OUTPUT_INIT_HIGH) != 0); break; default: return -ENOTSUP; } return err; } static int gpio_adi_max22017_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, enum gpio_int_mode mode, enum gpio_int_trig trig) { int ret; uint16_t gpio_int, gen_int_en; const struct gpio_adi_max22017_config *config = dev->config; const struct device *parent = config->parent; struct max22017_data *data = parent->data; k_mutex_lock(&data->lock, K_FOREVER); if (mode == GPIO_INT_MODE_DISABLED) { ret = -ENOTSUP; goto fail; } ret = max22017_reg_read(parent, MAX22017_GEN_GPI_INT_OFF, &gpio_int); if (ret) { goto fail; } if (mode & GPIO_INT_EDGE_RISING) { gpio_int |= FIELD_PREP(MAX22017_GEN_GPI_INT_GPI_POS_EDGE_INT, BIT(pin)); } if (mode & GPIO_INT_EDGE_FALLING) { gpio_int |= FIELD_PREP(MAX22017_GEN_GPI_INT_GPI_NEG_EDGE_INT, BIT(pin)); } ret = max22017_reg_write(parent, MAX22017_GEN_GPI_INT_OFF, gpio_int); if (ret) { goto fail; } ret = max22017_reg_read(parent, MAX22017_GEN_INTEN_OFF, &gen_int_en); if (ret) { goto fail; } ret = max22017_reg_write(parent, MAX22017_GEN_INTEN_OFF, gen_int_en | FIELD_PREP(MAX22017_GEN_INTEN_GPI_INTEN, 1)); fail: k_mutex_unlock(&data->lock); return ret; } static int gpio_adi_max22017_port_get_raw(const struct device *dev, gpio_port_value_t *value) { int ret; const struct gpio_adi_max22017_config *config = dev->config; const struct device *parent = config->parent; struct max22017_data *data = parent->data; k_mutex_lock(&data->lock, K_FOREVER); ret = max22017_reg_read(parent, MAX22017_GEN_GPIO_DATA_OFF, (uint16_t *)value); if (ret) { goto fail; } *value = FIELD_GET(MAX22017_GEN_GPIO_DATA_GPI_DATA, *value); fail: k_mutex_unlock(&data->lock); return ret; } static int gpio_adi_max22017_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, gpio_port_value_t value) { const struct gpio_adi_max22017_config *config = dev->config; return adi_max22017_gpio_port_set_masked_raw(config->parent, mask, value); } static int gpio_adi_max22017_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) { const struct gpio_adi_max22017_config *config = dev->config; return adi_max22017_gpio_port_set_masked_raw(config->parent, pins, pins); } static int gpio_adi_max22017_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) { const struct gpio_adi_max22017_config *config = dev->config; return adi_max22017_gpio_port_set_masked_raw(config->parent, pins, 0); } static int gpio_adi_max22017_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) { int ret; uint16_t gpio_data, tmp_val; const struct gpio_adi_max22017_config *config = dev->config; const struct device *parent = config->parent; struct max22017_data *data = parent->data; k_mutex_lock(&data->lock, K_FOREVER); ret = max22017_reg_read(parent, MAX22017_GEN_GPIO_DATA_OFF, &gpio_data); if (ret) { goto fail; } tmp_val = FIELD_GET(MAX22017_GEN_GPIO_DATA_GPO_DATA, gpio_data); tmp_val = (tmp_val ^ pins); gpio_data = FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPO_DATA, tmp_val) | FIELD_PREP(MAX22017_GEN_GPIO_DATA_GPI_DATA, FIELD_GET(MAX22017_GEN_GPIO_DATA_GPI_DATA, gpio_data)); ret = max22017_reg_write(parent, MAX22017_GEN_GPIO_DATA_OFF, gpio_data); fail: k_mutex_unlock(&data->lock); return ret; } static int gpio_adi_max22017_manage_cb(const struct device *dev, struct gpio_callback *callback, bool set) { int ret; const struct gpio_adi_max22017_config *config = dev->config; struct max22017_data *data = config->parent->data; k_mutex_lock(&data->lock, K_FOREVER); ret = gpio_manage_callback(&data->callbacks_gpi, callback, set); k_mutex_unlock(&data->lock); return ret; } static int gpio_adi_max22017_init(const struct device *dev) { const struct gpio_adi_max22017_config *config = dev->config; const struct device *parent = config->parent; if (!device_is_ready(parent)) { LOG_ERR("parent adi_max22017 MFD device '%s' not ready", config->parent->name); return -EINVAL; } #ifdef CONFIG_GPIO_MAX22017_INT_QUIRK struct gpio_adi_max22017_data *data = dev->data; struct k_timer *t = &data->int_quirk_timer; k_timer_init(t, isr_quirk_handler, NULL); k_timer_user_data_set(t, parent->data); k_timer_start(t, K_MSEC(25), K_MSEC(25)); #endif return 0; } static DEVICE_API(gpio, gpio_adi_max22017_api) = { .pin_configure = gpio_adi_max22017_configure, .port_set_masked_raw = gpio_adi_max22017_port_set_masked_raw, .port_set_bits_raw = gpio_adi_max22017_port_set_bits_raw, .port_clear_bits_raw = gpio_adi_max22017_port_clear_bits_raw, .port_toggle_bits = gpio_adi_max22017_port_toggle_bits, .port_get_raw = gpio_adi_max22017_port_get_raw, .pin_interrupt_configure = gpio_adi_max22017_pin_interrupt_configure, .manage_callback = gpio_adi_max22017_manage_cb, }; #define GPIO_MAX22017_DEVICE(id) \ static const struct gpio_adi_max22017_config gpio_adi_max22017_##id##_cfg = { \ .common = \ { \ .port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(id), \ }, \ .parent = DEVICE_DT_GET(DT_INST_PARENT(id)), \ }; \ static struct gpio_adi_max22017_data gpio_adi_max22017_##id##_data; \ DEVICE_DT_INST_DEFINE(id, gpio_adi_max22017_init, NULL, &gpio_adi_max22017_##id##_data, \ &gpio_adi_max22017_##id##_cfg, POST_KERNEL, \ CONFIG_GPIO_MAX22017_INIT_PRIORITY, &gpio_adi_max22017_api); DT_INST_FOREACH_STATUS_OKAY(GPIO_MAX22017_DEVICE)