Skip to content

Commit

Permalink
drivers: gpio: AXP192 GPIO driver implementation
Browse files Browse the repository at this point in the history
AXP192 is a small power management IC, that also
features 5 GPIOS.
This commit also includes needed modifications in
axp192 regulator and mfd driver as LDOIO0 functioanlity
is multiplexed with GPIO0 pin.

Signed-off-by: Martin Kiepfer <[email protected]>
  • Loading branch information
Martin Kiepfer committed Jul 4, 2023
1 parent c01c85b commit 3c10038
Show file tree
Hide file tree
Showing 12 changed files with 1,102 additions and 6 deletions.
1 change: 1 addition & 0 deletions drivers/gpio/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ zephyr_library_sources_ifdef(CONFIG_GPIO_NUMICRO gpio_numicro.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_HOGS gpio_hogs.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_NUMAKER gpio_numaker.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_EFINIX_SAPPHIRE gpio_efinix_sapphire.c)
zephyr_library_sources_ifdef(CONFIG_GPIO_AXP192 gpio_axp192.c)

if(CONFIG_GPIO_SC18IM704)
zephyr_library_include_directories(${ZEPHYR_BASE}/drivers)
Expand Down
1 change: 1 addition & 0 deletions drivers/gpio/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ source "drivers/gpio/Kconfig.bd8lb600fs"
source "drivers/gpio/Kconfig.sc18im704"

source "drivers/gpio/Kconfig.numaker"
source "drivers/gpio/Kconfig.axp192"

source "drivers/gpio/Kconfig.efinix_sapphire"

Expand Down
20 changes: 20 additions & 0 deletions drivers/gpio/Kconfig.axp192
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# 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.
290 changes: 290 additions & 0 deletions drivers/gpio/gpio_axp192.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
/*
* 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;
};

struct gpio_axp192_data {
struct gpio_driver_data common;
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;

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;

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, 0u);
}

static inline 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 >= AXP192_GPIO_NUM_PINS) {
LOG_ERR("Invalid gpio pin (%d)", pin);
return -EINVAL;
}

/* 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_pin(config->mfd, pin, false);
} else if ((flags & GPIO_OUTPUT_INIT_HIGH) != 0) {
ret = mfd_axp192_gpio_write_pin(config->mfd, pin, true);
}
} 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. Pull-down is only supported by GPIO3 and GPIO4 */
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)
{
int ret;
uint32_t value;

ret = gpio_axp192_port_get_raw(dev, &value);
if (ret < 0) {
return ret;
}

return gpio_axp192_port_set_masked_raw(dev, pins, ~value);
}

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;

ret = mfd_axp192_gpio_func_get(config->mfd, pin, &func);
if (ret == 0) {

/* 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:
case AXP192_GPIO_FUNC_ADC:
case AXP192_GPIO_FUNC_FLOAT:
default:
LOG_DBG("Pin %d not configured as GPIO", pin);
break;
}
}

/* Query pull-down config status  */
if (ret == 0) {
ret = mfd_axp192_gpio_pd_get(config->mfd, pin, &pd_enabled);

if ((ret == 0) && (pd_enabled)) {
*out_flags |= GPIO_PULL_DOWN;
}
}

return ret;
}
#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)
{
int ret = -EINVAL;
gpio_flags_t flags;

/* reset output variables */
*inputs = 0;
*outputs = 0;

/* loop through all */
for (gpio_pin_t gpio = 0; gpio < AXP192_GPIO_NUM_PINS; 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) {

/* Set output and input flags */
if (0 != (flags & GPIO_OUTPUT)) {
*outputs |= (1u << gpio);
} else if (0 != (flags & GPIO_INPUT)) {
*inputs |= (1u << gpio);
}
}
}
}

return ret;
}
#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;

LOG_DBG("Initializing");

if (!i2c_is_ready_dt(&config->i2c)) {
LOG_ERR("device not ready");
return -ENODEV;
}

return 0;
}

#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)), \
}; \
\
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

0 comments on commit 3c10038

Please sign in to comment.