-
Notifications
You must be signed in to change notification settings - Fork 6.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
drivers: gpio: axp192: GPIO driver implementation #59768
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Copyright (c) 2023 Martin Kiepfer | ||
# SPDX-License-Identifier: Apache-2.0 | ||
|
||
config GPIO_AXP192 | ||
bool "AXP192 GPIO driver" | ||
default y | ||
depends on DT_HAS_X_POWERS_AXP192_GPIO_ENABLED | ||
depends on DT_HAS_X_POWERS_AXP192_ENABLED | ||
select I2C | ||
select MFD | ||
help | ||
Enable the AXP192 GPIO driver. | ||
|
||
config GPIO_AXP192_INIT_PRIORITY | ||
int "AXP192 GPIO driver initialization priority" | ||
depends on GPIO_AXP192 | ||
default 80 | ||
help | ||
Initialization priority for the AXP192 GPIO driver. It must be | ||
greater than the I2C controller init priority and the mfd driver | ||
init priority. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,321 @@ | ||
/* | ||
* Copyright (c) 2023 Martin Kiepfer | ||
* | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
#define DT_DRV_COMPAT x_powers_axp192_gpio | ||
|
||
#include <errno.h> | ||
|
||
#include <zephyr/drivers/gpio.h> | ||
#include <zephyr/drivers/gpio/gpio_utils.h> | ||
#include <zephyr/drivers/i2c.h> | ||
#include <zephyr/kernel.h> | ||
#include <zephyr/sys/util_macro.h> | ||
#include <zephyr/toolchain.h> | ||
#include <zephyr/logging/log.h> | ||
#include <zephyr/drivers/mfd/axp192.h> | ||
|
||
LOG_MODULE_REGISTER(gpio_axp192, CONFIG_GPIO_LOG_LEVEL); | ||
|
||
struct gpio_axp192_config { | ||
struct gpio_driver_config common; | ||
struct i2c_dt_spec i2c; | ||
const struct device *mfd; | ||
uint32_t ngpios; | ||
}; | ||
|
||
struct gpio_axp192_data { | ||
struct gpio_driver_data common; | ||
struct k_mutex mutex; | ||
sys_slist_t cb_list_gpio; | ||
}; | ||
|
||
static int gpio_axp192_port_get_raw(const struct device *dev, uint32_t *value) | ||
{ | ||
int ret; | ||
uint8_t port_val; | ||
const struct gpio_axp192_config *config = dev->config; | ||
|
||
if (k_is_in_isr()) { | ||
return -EWOULDBLOCK; | ||
} | ||
|
||
ret = mfd_axp192_gpio_read_port(config->mfd, &port_val); | ||
if (ret == 0) { | ||
*value = port_val; | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
static int gpio_axp192_port_set_masked_raw(const struct device *dev, gpio_port_pins_t mask, | ||
gpio_port_value_t value) | ||
{ | ||
int ret; | ||
const struct gpio_axp192_config *config = dev->config; | ||
|
||
if (k_is_in_isr()) { | ||
return -EWOULDBLOCK; | ||
} | ||
|
||
ret = mfd_axp192_gpio_write_port(config->mfd, value, mask); | ||
|
||
return ret; | ||
} | ||
|
||
static int gpio_axp192_port_set_bits_raw(const struct device *dev, gpio_port_pins_t pins) | ||
{ | ||
return gpio_axp192_port_set_masked_raw(dev, pins, pins); | ||
} | ||
|
||
static int gpio_axp192_port_clear_bits_raw(const struct device *dev, gpio_port_pins_t pins) | ||
{ | ||
return gpio_axp192_port_set_masked_raw(dev, pins, 0); | ||
} | ||
|
||
static int gpio_axp192_configure(const struct device *dev, gpio_pin_t pin, | ||
gpio_flags_t flags) | ||
{ | ||
const struct gpio_axp192_config *config = dev->config; | ||
int ret; | ||
enum axp192_gpio_func func; | ||
|
||
if (pin >= config->ngpios) { | ||
LOG_ERR("Invalid gpio pin (%d)", pin); | ||
return -EINVAL; | ||
} | ||
|
||
if (k_is_in_isr()) { | ||
return -EWOULDBLOCK; | ||
} | ||
|
||
/* Configure pin */ | ||
LOG_DBG("Pin: %d / flags=0x%x", pin, flags); | ||
if ((flags & GPIO_OUTPUT) != 0) { | ||
|
||
/* Initialize output function */ | ||
func = AXP192_GPIO_FUNC_OUTPUT_LOW; | ||
if ((flags & GPIO_OPEN_DRAIN) != 0) { | ||
func = AXP192_GPIO_FUNC_OUTPUT_OD; | ||
} | ||
ret = mfd_axp192_gpio_func_ctrl(config->mfd, dev, pin, func); | ||
if (ret != 0) { | ||
return ret; | ||
} | ||
|
||
/* Set init value */ | ||
if ((flags & GPIO_OUTPUT_INIT_LOW) != 0) { | ||
ret = mfd_axp192_gpio_write_port(config->mfd, BIT(pin), 0); | ||
} else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) { | ||
ret = mfd_axp192_gpio_write_port(config->mfd, BIT(pin), BIT(pin)); | ||
} | ||
} else if ((flags & GPIO_INPUT) != 0) { | ||
|
||
/* Initialize input function */ | ||
func = AXP192_GPIO_FUNC_INPUT; | ||
|
||
ret = mfd_axp192_gpio_func_ctrl(config->mfd, dev, pin, func); | ||
if (ret != 0) { | ||
return ret; | ||
} | ||
|
||
/* Configure pull-down */ | ||
if ((flags & GPIO_PULL_UP) != 0) { | ||
/* not supported */ | ||
LOG_ERR("Pull-Up not supported"); | ||
ret = -ENOTSUP; | ||
} else if ((flags & GPIO_PULL_DOWN) != 0) { | ||
/* out = 0 means pull-down*/ | ||
ret = mfd_axp192_gpio_pd_ctrl(config->mfd, pin, true); | ||
} else { | ||
ret = mfd_axp192_gpio_pd_ctrl(config->mfd, pin, false); | ||
} | ||
} else { | ||
/* Neither input nor output mode is selected */ | ||
LOG_INF("No valid gpio mode selected"); | ||
ret = -ENOTSUP; | ||
} | ||
|
||
return ret; | ||
} | ||
|
||
static int gpio_axp192_port_toggle_bits(const struct device *dev, gpio_port_pins_t pins) | ||
{ | ||
struct gpio_axp192_data *data = dev->data; | ||
int ret; | ||
uint32_t value; | ||
|
||
k_mutex_lock(&data->mutex, K_FOREVER); | ||
|
||
ret = gpio_axp192_port_get_raw(dev, &value); | ||
if (ret == 0) { | ||
ret = gpio_axp192_port_set_masked_raw(dev, pins, ~value); | ||
} | ||
|
||
k_mutex_unlock(&data->mutex); | ||
|
||
return ret; | ||
} | ||
|
||
static int gpio_axp192_pin_interrupt_configure(const struct device *dev, gpio_pin_t pin, | ||
enum gpio_int_mode mode, enum gpio_int_trig trig) | ||
{ | ||
ARG_UNUSED(dev); | ||
ARG_UNUSED(pin); | ||
ARG_UNUSED(mode); | ||
ARG_UNUSED(trig); | ||
|
||
return -ENOTSUP; | ||
} | ||
|
||
#ifdef CONFIG_GPIO_GET_CONFIG | ||
static int gpio_axp192_get_config(const struct device *dev, gpio_pin_t pin, gpio_flags_t *out_flags) | ||
{ | ||
const struct gpio_axp192_config *config = dev->config; | ||
enum axp192_gpio_func func; | ||
bool pd_enabled; | ||
int ret; | ||
|
||
if (k_is_in_isr()) { | ||
return -EWOULDBLOCK; | ||
} | ||
|
||
ret = mfd_axp192_gpio_func_get(config->mfd, pin, &func); | ||
if (ret != 0) { | ||
return ret; | ||
} | ||
|
||
/* Set OUTPUT/INPUT flags */ | ||
*out_flags = 0; | ||
switch (func) { | ||
case AXP192_GPIO_FUNC_INPUT: | ||
*out_flags |= GPIO_INPUT; | ||
break; | ||
case AXP192_GPIO_FUNC_OUTPUT_OD: | ||
*out_flags |= GPIO_OUTPUT | GPIO_OPEN_DRAIN; | ||
break; | ||
case AXP192_GPIO_FUNC_OUTPUT_LOW: | ||
*out_flags |= GPIO_OUTPUT; | ||
break; | ||
|
||
case AXP192_GPIO_FUNC_LDO: | ||
__fallthrough; | ||
case AXP192_GPIO_FUNC_ADC: | ||
__fallthrough; | ||
case AXP192_GPIO_FUNC_FLOAT: | ||
__fallthrough; | ||
default: | ||
LOG_DBG("Pin %d not configured as GPIO", pin); | ||
break; | ||
} | ||
|
||
/* Query pull-down config status */ | ||
ret = mfd_axp192_gpio_pd_get(config->mfd, pin, &pd_enabled); | ||
if (ret != 0) { | ||
return ret; | ||
} | ||
|
||
if (pd_enabled) { | ||
*out_flags |= GPIO_PULL_DOWN; | ||
} | ||
|
||
return 0; | ||
} | ||
#endif /* CONFIG_GPIO_GET_CONFIG */ | ||
|
||
#ifdef CONFIG_GPIO_GET_DIRECTION | ||
static int gpio_axp192_port_get_direction(const struct device *dev, gpio_port_pins_t map, | ||
gpio_port_pins_t *inputs, gpio_port_pins_t *outputs) | ||
{ | ||
const struct gpio_axp192_config *config = dev->config; | ||
gpio_flags_t flags; | ||
int ret; | ||
|
||
/* reset output variables */ | ||
*inputs = 0; | ||
*outputs = 0; | ||
|
||
/* loop through all */ | ||
for (gpio_pin_t gpio = 0; gpio < config->ngpios; gpio++) { | ||
|
||
if ((map & (1u << gpio)) != 0) { | ||
|
||
/* use internal get_config method to get gpio flags */ | ||
ret = gpio_axp192_get_config(dev, gpio, &flags); | ||
if (ret != 0) { | ||
return ret; | ||
} | ||
|
||
/* Set output and input flags */ | ||
if ((flags & GPIO_OUTPUT) != 0) { | ||
*outputs |= (1u << gpio); | ||
} else if (0 != (flags & GPIO_INPUT)) { | ||
*inputs |= (1u << gpio); | ||
} | ||
} | ||
} | ||
|
||
return 0; | ||
} | ||
#endif /* CONFIG_GPIO_GET_DIRECTION */ | ||
|
||
static int gpio_axp192_manage_callback(const struct device *dev, struct gpio_callback *callback, | ||
bool set) | ||
{ | ||
struct gpio_axp192_data *const data = dev->data; | ||
|
||
return gpio_manage_callback(&data->cb_list_gpio, callback, set); | ||
} | ||
|
||
static const struct gpio_driver_api gpio_axp192_api = { | ||
.pin_configure = gpio_axp192_configure, | ||
.port_get_raw = gpio_axp192_port_get_raw, | ||
.port_set_masked_raw = gpio_axp192_port_set_masked_raw, | ||
.port_set_bits_raw = gpio_axp192_port_set_bits_raw, | ||
.port_clear_bits_raw = gpio_axp192_port_clear_bits_raw, | ||
.port_toggle_bits = gpio_axp192_port_toggle_bits, | ||
.pin_interrupt_configure = gpio_axp192_pin_interrupt_configure, | ||
.manage_callback = gpio_axp192_manage_callback, | ||
#ifdef CONFIG_GPIO_GET_DIRECTION | ||
.port_get_direction = gpio_axp192_port_get_direction, | ||
#endif /* CONFIG_GPIO_GET_DIRECTION */ | ||
#ifdef CONFIG_GPIO_GET_CONFIG | ||
.pin_get_config = gpio_axp192_get_config, | ||
#endif | ||
}; | ||
|
||
static int gpio_axp192_init(const struct device *dev) | ||
{ | ||
const struct gpio_axp192_config *config = dev->config; | ||
struct gpio_axp192_data *data = dev->data; | ||
|
||
LOG_DBG("Initializing"); | ||
|
||
if (!i2c_is_ready_dt(&config->i2c)) { | ||
LOG_ERR("device not ready"); | ||
return -ENODEV; | ||
} | ||
|
||
return k_mutex_init(&data->mutex); | ||
} | ||
|
||
#define GPIO_AXP192_DEFINE(inst) \ | ||
static const struct gpio_axp192_config gpio_axp192_config##inst = { \ | ||
.common = \ | ||
{ \ | ||
.port_pin_mask = GPIO_PORT_PIN_MASK_FROM_DT_INST(inst), \ | ||
}, \ | ||
.i2c = I2C_DT_SPEC_GET(DT_INST_PARENT(inst)), \ | ||
.mfd = DEVICE_DT_GET(DT_INST_PARENT(inst)), \ | ||
.ngpios = DT_INST_PROP(inst, ngpios), \ | ||
}; \ | ||
\ | ||
static struct gpio_axp192_data gpio_axp192_data##inst; \ | ||
\ | ||
DEVICE_DT_INST_DEFINE(inst, &gpio_axp192_init, NULL, &gpio_axp192_data##inst, \ | ||
&gpio_axp192_config##inst, POST_KERNEL, \ | ||
CONFIG_GPIO_AXP192_INIT_PRIORITY, &gpio_axp192_api); | ||
|
||
DT_INST_FOREACH_STATUS_OKAY(GPIO_AXP192_DEFINE) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can block, right? If so you need something like https://github.com/zephyrproject-rtos/zephyr/blob/main/drivers/gpio/gpio_npm6001.c#L53 (per API interface specs)