Skip to content
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 1 commit into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions drivers/gpio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ zephyr_syscall_header(${ZEPHYR_BASE}/include/zephyr/drivers/gpio.h)

zephyr_library()

zephyr_library_sources_ifdef(CONFIG_GPIO_AXP192 gpio_axp192.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_TELINK_B91 gpio_b91.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_INFINEON_CAT1 gpio_ifx_cat1.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_CC13XX_CC26XX gpio_cc13xx_cc26xx.c)
Expand Down
2 changes: 2 additions & 0 deletions drivers/gpio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ config GPIO_ENABLE_DISABLE_INTERRUPT
pending register, etc. The driver must implement it to work.


source "drivers/gpio/Kconfig.axp192"

source "drivers/gpio/Kconfig.b91"

source "drivers/gpio/Kconfig.dw"
Expand Down
21 changes: 21 additions & 0 deletions drivers/gpio/Kconfig.axp192
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.
321 changes: 321 additions & 0 deletions drivers/gpio/gpio_axp192.c
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);
Copy link
Member

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)

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)
Loading
Loading