drivers: gpio: AXP192 GPIO driver

AXP192 is a small power management IC, that also
features 5 GPIOS.
Besides GPIO driver 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 <mrmarteng@teleschirm.org>
This commit is contained in:
Martin Kiepfer 2023-07-19 21:53:55 +02:00 committed by Carles Cufí
commit 74db02bad1
11 changed files with 1055 additions and 6 deletions

View file

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

View file

@ -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"

View file

@ -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
drivers/gpio/gpio_axp192.c Normal file
View file

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

View file

@ -6,7 +6,9 @@
#define DT_DRV_COMPAT x_powers_axp192
#include <errno.h>
#include <stdbool.h>
#include <zephyr/drivers/mfd/axp192.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/sys/util.h>
#include <zephyr/logging/log.h>
@ -19,10 +21,111 @@ LOG_MODULE_REGISTER(mfd_axp192, CONFIG_MFD_LOG_LEVEL);
/* Registers definitions */
#define AXP192_REG_CHIP_ID 0x03U
/* AXP192 GPIO register addresses */
#define AXP192_GPIO0_REG_FUNC 0x90U
#define AXP192_GPIO1_REG_FUNC 0x92U
#define AXP192_GPIO2_REG_FUNC 0x93U
#define AXP192_GPIO34_REG_FUNC 0x95U
#define AXP192_GPIO012_REG_PINVAL 0x94U
#define AXP192_GPIO34_REG_PINVAL 0x96U
#define AXP192_GPIO012_REG_PULLDOWN 0x97U
/* GPIO function control parameters */
#define AXP192_GPIO012_FUNC_VAL_OUTPUT_OD 0x00U
#define AXP192_GPIO012_FUNC_VAL_INPUT 0x01U
#define AXP192_GPIO012_FUNC_VAL_LDO 0x02U /* only applicable for GPIO0 */
#define AXP192_GPIO012_FUNC_VAL_ADC 0x04U
#define AXP192_GPIO012_FUNC_VAL_OUTPUT_LOW 0x05U
#define AXP192_GPIO012_FUNC_VAL_FLOAT 0x06U
#define AXP192_GPIO012_FUNC_MASK \
(AXP192_GPIO012_FUNC_VAL_OUTPUT_OD | AXP192_GPIO012_FUNC_VAL_INPUT | \
AXP192_GPIO012_FUNC_VAL_LDO | AXP192_GPIO012_FUNC_VAL_ADC | \
AXP192_GPIO012_FUNC_VAL_OUTPUT_LOW | AXP192_GPIO012_FUNC_VAL_FLOAT)
#define AXP192_GPIO34_FUNC_ENA 0x80U
#define AXP192_GPIO3_FUNC_VAL_CHARGE_CTL 0x00U
#define AXP192_GPIO3_FUNC_VAL_OUTPUT_OD 0x01U
#define AXP192_GPIO3_FUNC_VAL_INPUT 0x02U
#define AXP192_GPIO3_FUNC_MASK \
(AXP192_GPIO34_FUNC_ENA | AXP192_GPIO3_FUNC_VAL_CHARGE_CTL | \
AXP192_GPIO3_FUNC_VAL_OUTPUT_OD | AXP192_GPIO3_FUNC_VAL_INPUT)
#define AXP192_GPIO4_FUNC_VAL_CHARGE_CTL 0x00U
#define AXP192_GPIO4_FUNC_VAL_OUTPUT_OD 0x04U
#define AXP192_GPIO4_FUNC_VAL_INPUT 0x08U
#define AXP192_GPIO4_FUNC_VAL_ADC 0x0CU
#define AXP192_GPIO4_FUNC_MASK \
(AXP192_GPIO34_FUNC_ENA | AXP192_GPIO4_FUNC_VAL_CHARGE_CTL | \
AXP192_GPIO4_FUNC_VAL_OUTPUT_OD | AXP192_GPIO4_FUNC_VAL_INPUT)
/* Pull-Down enable parameters */
#define AXP192_GPIO0_PULLDOWN_ENABLE 0x01U
#define AXP192_GPIO1_PULLDOWN_ENABLE 0x02U
#define AXP192_GPIO2_PULLDOWN_ENABLE 0x04U
/* GPIO Value parameters */
#define AXP192_GPIO0_INPUT_VAL 0x10U
#define AXP192_GPIO1_INPUT_VAL 0x20U
#define AXP192_GPIO2_INPUT_VAL 0x40U
#define AXP192_GPIO012_INTPUT_SHIFT 4U
#define AXP192_GPIO012_INTPUT_MASK \
(AXP192_GPIO0_INPUT_VAL | AXP192_GPIO1_INPUT_VAL | AXP192_GPIO2_INPUT_VAL)
#define AXP192_GPIO3_INPUT_VAL 0x10U
#define AXP192_GPIO4_INPUT_VAL 0x20U
#define AXP192_GPIO34_INTPUT_SHIFT 4U
#define AXP192_GPIO34_INTPUT_MASK (AXP192_GPIO3_INPUT_VAL | AXP192_GPIO4_INPUT_VAL)
#define AXP192_GPIO0_OUTPUT_VAL 0x01U
#define AXP192_GPIO1_OUTPUT_VAL 0x02U
#define AXP192_GPIO2_OUTPUT_VAL 0x04U
#define AXP192_GPIO012_OUTPUT_MASK \
(AXP192_GPIO0_OUTPUT_VAL | AXP192_GPIO1_OUTPUT_VAL | AXP192_GPIO2_OUTPUT_VAL)
#define AXP192_GPIO3_OUTPUT_VAL 0x01U
#define AXP192_GPIO4_OUTPUT_VAL 0x02U
#define AXP192_GPIO34_OUTPUT_MASK (AXP192_GPIO3_OUTPUT_VAL | AXP192_GPIO4_OUTPUT_VAL)
struct mfd_axp192_config {
struct i2c_dt_spec i2c;
};
struct mfd_axp192_data {
const struct device *gpio_mask_used[AXP192_GPIO_MAX_NUM];
uint8_t gpio_mask_output;
};
struct mfd_axp192_func_reg_desc {
uint8_t reg;
uint8_t mask;
};
const struct mfd_axp192_func_reg_desc gpio_reg_desc[AXP192_GPIO_MAX_NUM] = {
{
/* GPIO0 */
.reg = AXP192_GPIO0_REG_FUNC,
.mask = AXP192_GPIO012_FUNC_MASK,
},
{
/* GPIO1 */
.reg = AXP192_GPIO1_REG_FUNC,
.mask = AXP192_GPIO012_FUNC_MASK,
},
{
/* GPIO2 */
.reg = AXP192_GPIO2_REG_FUNC,
.mask = AXP192_GPIO012_FUNC_MASK,
},
{
/* GPIO3 */
.reg = AXP192_GPIO34_REG_FUNC,
.mask = AXP192_GPIO3_FUNC_MASK,
},
{
/* GPIO4 */
.reg = AXP192_GPIO34_REG_FUNC,
.mask = AXP192_GPIO4_FUNC_MASK,
},
};
static int mfd_axp192_init(const struct device *dev)
{
const struct mfd_axp192_config *config = dev->config;
@ -50,12 +153,405 @@ static int mfd_axp192_init(const struct device *dev)
return 0;
}
int mfd_axp192_gpio_func_get(const struct device *dev, uint8_t gpio, enum axp192_gpio_func *func)
{
const struct mfd_axp192_config *config = dev->config;
int ret;
uint8_t reg_fnc;
if (gpio >= AXP192_GPIO_MAX_NUM) {
LOG_ERR("Invalid gpio (%d)", gpio);
return -EINVAL;
}
ret = i2c_reg_read_byte_dt(&(config->i2c), gpio_reg_desc[gpio].reg, &reg_fnc);
if (ret != 0) {
return ret;
}
switch (gpio) {
case 0U:
__fallthrough;
case 1U:
__fallthrough;
case 2U:
/* GPIO 0-2*/
switch (reg_fnc) {
case AXP192_GPIO012_FUNC_VAL_INPUT:
*func = AXP192_GPIO_FUNC_INPUT;
break;
case AXP192_GPIO012_FUNC_VAL_OUTPUT_OD:
*func = AXP192_GPIO_FUNC_OUTPUT_OD;
break;
case AXP192_GPIO012_FUNC_VAL_OUTPUT_LOW:
*func = AXP192_GPIO_FUNC_OUTPUT_LOW;
break;
case AXP192_GPIO012_FUNC_VAL_LDO:
if (gpio == 0) {
/* LDO is only applicable on GPIO0 */
*func = AXP192_GPIO_FUNC_LDO;
} else {
ret = -ENOTSUP;
}
break;
case AXP192_GPIO012_FUNC_VAL_ADC:
*func = AXP192_GPIO_FUNC_ADC;
break;
case AXP192_GPIO012_FUNC_VAL_FLOAT:
*func = AXP192_GPIO_FUNC_FLOAT;
break;
default:
ret = -ENOTSUP;
break;
}
break;
case 3U:
/* GPIO3 */
switch (reg_fnc) {
case (AXP192_GPIO3_FUNC_VAL_INPUT | AXP192_GPIO34_FUNC_ENA):
*func = AXP192_GPIO_FUNC_INPUT;
break;
case (AXP192_GPIO3_FUNC_VAL_OUTPUT_OD | AXP192_GPIO34_FUNC_ENA):
*func = AXP192_GPIO_FUNC_OUTPUT_OD;
break;
case AXP192_GPIO3_FUNC_VAL_CHARGE_CTL:
*func = AXP192_GPIO_FUNC_CHARGE_CTL;
break;
default:
ret = -ENOTSUP;
break;
}
break;
case 4U:
/* GPIO4 */
switch (reg_fnc) {
case (AXP192_GPIO4_FUNC_VAL_INPUT | AXP192_GPIO34_FUNC_ENA):
*func = AXP192_GPIO_FUNC_INPUT;
break;
case (AXP192_GPIO4_FUNC_VAL_OUTPUT_OD | AXP192_GPIO34_FUNC_ENA):
*func = AXP192_GPIO_FUNC_OUTPUT_OD;
break;
case (AXP192_GPIO4_FUNC_VAL_ADC | AXP192_GPIO34_FUNC_ENA):
*func = AXP192_GPIO_FUNC_ADC;
break;
case AXP192_GPIO4_FUNC_VAL_CHARGE_CTL:
*func = AXP192_GPIO_FUNC_CHARGE_CTL;
break;
default:
ret = -ENOTSUP;
break;
}
break;
default:
ret = -EINVAL;
}
return ret;
}
int mfd_axp192_gpio_func_ctrl(const struct device *dev, const struct device *client_dev,
uint8_t gpio, enum axp192_gpio_func func)
{
const struct mfd_axp192_config *config = dev->config;
struct mfd_axp192_data *data = dev->data;
bool is_output = false;
int ret = 0;
uint8_t reg_cfg;
if (!AXP192_GPIO_FUNC_VALID(func)) {
LOG_ERR("Invalid function");
return -EINVAL;
}
if (gpio >= AXP192_GPIO_MAX_NUM) {
LOG_ERR("Invalid gpio (%d)", gpio);
return -EINVAL;
}
if ((data->gpio_mask_used[gpio] != 0) && (data->gpio_mask_used[gpio] != client_dev)) {
LOG_INF("Warning: Pin already configured. Please check dt configuration");
}
switch (gpio) {
case 0U:
__fallthrough;
case 1U:
__fallthrough;
case 2U:
/* GPIO 0-2*/
switch (func) {
case AXP192_GPIO_FUNC_INPUT:
reg_cfg = AXP192_GPIO012_FUNC_VAL_INPUT;
break;
case AXP192_GPIO_FUNC_OUTPUT_OD:
reg_cfg = AXP192_GPIO012_FUNC_VAL_OUTPUT_OD;
is_output = true;
break;
case AXP192_GPIO_FUNC_OUTPUT_LOW:
reg_cfg = AXP192_GPIO012_FUNC_VAL_OUTPUT_LOW;
is_output = true;
break;
case AXP192_GPIO_FUNC_LDO:
if (gpio == 0) {
/* LDO is only applicable on GPIO0 */
reg_cfg = AXP192_GPIO012_FUNC_VAL_LDO;
} else {
ret = -ENOTSUP;
}
break;
case AXP192_GPIO_FUNC_ADC:
reg_cfg = AXP192_GPIO012_FUNC_VAL_ADC;
break;
case AXP192_GPIO_FUNC_FLOAT:
reg_cfg = AXP192_GPIO012_FUNC_VAL_FLOAT;
break;
default:
ret = -ENOTSUP;
break;
}
break;
case 3U:
/* GPIO3 */
switch (func) {
case AXP192_GPIO_FUNC_INPUT:
reg_cfg = AXP192_GPIO3_FUNC_VAL_INPUT | AXP192_GPIO34_FUNC_ENA;
break;
case AXP192_GPIO_FUNC_OUTPUT_OD:
reg_cfg = AXP192_GPIO3_FUNC_VAL_OUTPUT_OD | AXP192_GPIO34_FUNC_ENA;
is_output = true;
break;
case AXP192_GPIO_FUNC_CHARGE_CTL:
reg_cfg = AXP192_GPIO3_FUNC_VAL_CHARGE_CTL;
break;
default:
ret = -ENOTSUP;
break;
}
break;
case 4U:
/* GPIO4 */
switch (func) {
case AXP192_GPIO_FUNC_INPUT:
reg_cfg = AXP192_GPIO4_FUNC_VAL_INPUT | AXP192_GPIO34_FUNC_ENA;
break;
case AXP192_GPIO_FUNC_OUTPUT_OD:
reg_cfg = AXP192_GPIO4_FUNC_VAL_OUTPUT_OD | AXP192_GPIO34_FUNC_ENA;
is_output = true;
break;
case AXP192_GPIO_FUNC_ADC:
reg_cfg = AXP192_GPIO4_FUNC_VAL_ADC | AXP192_GPIO34_FUNC_ENA;
break;
case AXP192_GPIO_FUNC_CHARGE_CTL:
reg_cfg = AXP192_GPIO4_FUNC_VAL_CHARGE_CTL;
break;
default:
ret = -ENOTSUP;
break;
}
break;
default:
ret = -EINVAL;
}
if (ret != 0) {
LOG_ERR("Invalid function (0x%x) for gpio %d", func, gpio);
return ret;
}
ret = i2c_reg_update_byte_dt(&(config->i2c), gpio_reg_desc[gpio].reg,
gpio_reg_desc[gpio].mask, reg_cfg);
if (ret != 0) {
return ret;
}
/* Save gpio configuration state */
data->gpio_mask_used[gpio] = client_dev;
if (is_output) {
data->gpio_mask_output |= (1u << gpio);
} else {
data->gpio_mask_output &= ~(1u << gpio);
}
LOG_DBG("GPIO %d configured successfully (func=0x%x)", gpio, reg_cfg);
return 0;
}
int mfd_axp192_gpio_pd_get(const struct device *dev, uint8_t gpio, bool *enabled)
{
const struct mfd_axp192_config *config = dev->config;
uint8_t gpio_val;
uint8_t pd_reg_mask = 0;
int ret = 0;
switch (gpio) {
case 0U:
pd_reg_mask = AXP192_GPIO0_PULLDOWN_ENABLE;
break;
case 1U:
pd_reg_mask = AXP192_GPIO1_PULLDOWN_ENABLE;
break;
case 2U:
pd_reg_mask = AXP192_GPIO2_PULLDOWN_ENABLE;
break;
case 3U:
__fallthrough;
case 4U:
__fallthrough;
case 5U:
LOG_DBG("Pull-Down not support on gpio %d", gpio);
return -ENOTSUP;
default:
LOG_ERR("Invalid gpio (%d)", gpio);
return -EINVAL;
}
ret = i2c_reg_read_byte_dt(&(config->i2c), AXP192_GPIO012_REG_PULLDOWN, &gpio_val);
if (ret == 0) {
*enabled = ((gpio_val & pd_reg_mask) != 0);
LOG_DBG("Pull-Down stats of gpio %d: %d", gpio, *enabled);
}
return 0;
}
int mfd_axp192_gpio_pd_ctrl(const struct device *dev, uint8_t gpio, bool enable)
{
uint8_t reg_pd_val = 0;
uint8_t reg_pd_mask = 0;
const struct mfd_axp192_config *config = dev->config;
int ret = 0;
/* Configure pull-down. Pull-down is only supported by GPIO3 and GPIO4 */
switch (gpio) {
case 0U:
reg_pd_mask = AXP192_GPIO0_PULLDOWN_ENABLE;
if (enable) {
reg_pd_val = AXP192_GPIO0_PULLDOWN_ENABLE;
}
break;
case 1U:
reg_pd_mask = AXP192_GPIO1_PULLDOWN_ENABLE;
if (enable) {
reg_pd_val = AXP192_GPIO1_PULLDOWN_ENABLE;
}
break;
case 2U:
reg_pd_mask = AXP192_GPIO2_PULLDOWN_ENABLE;
if (enable) {
reg_pd_val = AXP192_GPIO2_PULLDOWN_ENABLE;
}
break;
case 3U:
__fallthrough;
case 4U:
__fallthrough;
case 5U:
LOG_ERR("Pull-Down not support on gpio %d", gpio);
return -ENOTSUP;
default:
LOG_ERR("Invalid gpio (%d)", gpio);
return -EINVAL;
}
ret = i2c_reg_update_byte_dt(&(config->i2c), AXP192_GPIO012_REG_PULLDOWN, reg_pd_mask,
reg_pd_val);
return ret;
}
int mfd_axp192_gpio_read_port(const struct device *dev, uint8_t *value)
{
const struct mfd_axp192_config *config = dev->config;
const struct mfd_axp192_data *data = dev->data;
int ret;
uint8_t gpio012_val;
uint8_t gpio34_val;
uint8_t gpio_input_val;
uint8_t gpio_output_val;
/* read gpio0-2 */
ret = i2c_reg_read_byte_dt(&(config->i2c), AXP192_GPIO012_REG_PINVAL, &gpio012_val);
if (ret != 0) {
return ret;
}
/* read gpio3-4 */
ret = i2c_reg_read_byte_dt(&(config->i2c), AXP192_GPIO34_REG_PINVAL, &gpio34_val);
if (ret != 0) {
return ret;
}
LOG_DBG("GPIO012 pinval-reg=0x%x", gpio012_val);
LOG_DBG("GPIO34 pinval-reg =0x%x", gpio34_val);
LOG_DBG("Output-Mask =0x%x", data->gpio_mask_output);
gpio_input_val =
((gpio012_val & AXP192_GPIO012_INTPUT_MASK) >> AXP192_GPIO012_INTPUT_SHIFT);
gpio_input_val |=
(((gpio34_val & AXP192_GPIO34_INTPUT_MASK) >> AXP192_GPIO34_INTPUT_SHIFT) << 3u);
gpio_output_val = (gpio012_val & AXP192_GPIO012_OUTPUT_MASK);
gpio_output_val |= ((gpio34_val & AXP192_GPIO34_OUTPUT_MASK) << 3u);
*value = gpio_input_val & ~(data->gpio_mask_output);
*value |= (gpio_output_val & data->gpio_mask_output);
return 0;
}
int mfd_axp192_gpio_write_port(const struct device *dev, uint8_t value, uint8_t mask)
{
const struct mfd_axp192_config *config = dev->config;
int ret;
uint8_t gpio_reg_val;
uint8_t gpio_reg_mask;
/* Write gpio0-2. Mask out other port pins */
gpio_reg_val = (value & AXP192_GPIO012_OUTPUT_MASK);
gpio_reg_mask = mask & AXP192_GPIO012_OUTPUT_MASK;
if (gpio_reg_mask != 0) {
ret = i2c_reg_update_byte_dt(&(config->i2c), AXP192_GPIO012_REG_PINVAL,
gpio_reg_mask, gpio_reg_val);
if (ret != 0) {
return ret;
}
LOG_DBG("GPIO012 pinval-reg=0x%x mask=0x%x", gpio_reg_val, gpio_reg_mask);
}
/* Write gpio3-4. Mask out other port pins */
gpio_reg_val = value >> 3U;
gpio_reg_mask = (mask >> 3U) & AXP192_GPIO34_OUTPUT_MASK;
if (gpio_reg_mask != 0) {
ret = i2c_reg_update_byte_dt(&(config->i2c), AXP192_GPIO34_REG_PINVAL,
gpio_reg_mask, gpio_reg_val);
if (ret != 0) {
return ret;
}
LOG_DBG("GPIO34 pinval-reg =0x%x mask=0x%x", gpio_reg_val, gpio_reg_mask);
}
return 0;
}
#define MFD_AXP192_DEFINE(inst) \
static const struct mfd_axp192_config config##inst = { \
.i2c = I2C_DT_SPEC_INST_GET(inst), \
}; \
\
DEVICE_DT_INST_DEFINE(inst, mfd_axp192_init, NULL, NULL, &config##inst, POST_KERNEL, \
CONFIG_MFD_INIT_PRIORITY, NULL);
static struct mfd_axp192_data data##inst; \
\
DEVICE_DT_INST_DEFINE(inst, mfd_axp192_init, NULL, &data##inst, &config##inst, \
POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL);
DT_INST_FOREACH_STATUS_OKAY(MFD_AXP192_DEFINE)

View file

@ -16,6 +16,7 @@
#include <zephyr/dt-bindings/regulator/axp192.h>
#include <zephyr/logging/log.h>
#include <zephyr/logging/log_instance.h>
#include <zephyr/drivers/mfd/axp192.h>
LOG_MODULE_REGISTER(regulator_axp192, CONFIG_REGULATOR_LOG_LEVEL);
@ -28,6 +29,8 @@ LOG_MODULE_REGISTER(regulator_axp192, CONFIG_REGULATOR_LOG_LEVEL);
#define AXP192_REG_DCDC3_VOLTAGE 0x27U
#define AXP192_REG_LDO23_VOLTAGE 0x28U
#define AXP192_REG_DCDC123_WORKMODE 0x80U
#define AXP192_REG_GPIO0_CONTROL 0x90U
#define AXP192_REG_LDOIO0_VOLTAGE 0x91U
struct regulator_axp192_desc {
const uint8_t enable_reg;
@ -114,6 +117,24 @@ static const struct regulator_axp192_desc dcdc3_desc = {
.num_ranges = ARRAY_SIZE(dcdc3_ranges),
};
static const struct linear_range ldoio0_ranges[] = {
LINEAR_RANGE_INIT(1800000u, 100000u, 0x00u, 0x0Fu),
};
static const struct regulator_axp192_desc ldoio0_desc = {
.enable_reg = AXP192_REG_GPIO0_CONTROL,
.enable_mask = 0x07u,
.enable_val = 0x03u,
.vsel_reg = AXP192_REG_LDOIO0_VOLTAGE,
.vsel_mask = 0xF0u,
.vsel_bitpos = 4u,
.max_ua = 50000u,
.workmode_reg = 0u,
.workmode_mask = 0u,
.ranges = ldoio0_ranges,
.num_ranges = ARRAY_SIZE(ldoio0_ranges),
};
static const struct linear_range ldo2_ranges[] = {
LINEAR_RANGE_INIT(1800000U, 100000U, 0x00U, 0x0FU),
};
@ -159,8 +180,14 @@ static int axp192_enable(const struct device *dev)
LOG_INST_DBG(config->log, "[0x%02x]=0x%02x mask=0x%02x", config->desc->enable_reg,
config->desc->enable_val, config->desc->enable_mask);
ret = i2c_reg_update_byte_dt(&config->i2c, config->desc->enable_reg,
config->desc->enable_mask, config->desc->enable_val);
/* special case for LDOIO0, which is multiplexed with GPIO0 */
if (config->desc->enable_reg == AXP192_REG_GPIO0_CONTROL) {
ret = mfd_axp192_gpio_func_ctrl(config->mfd, dev, 0, AXP192_GPIO_FUNC_LDO);
} else {
ret = i2c_reg_update_byte_dt(&config->i2c, config->desc->enable_reg,
config->desc->enable_mask, config->desc->enable_val);
}
if (ret != 0) {
LOG_INST_ERR(config->log, "Failed to enable regulator");
}
@ -177,8 +204,13 @@ static int axp192_disable(const struct device *dev)
LOG_INST_DBG(config->log, "[0x%02x]=0 mask=0x%x", config->desc->enable_reg,
config->desc->enable_mask);
ret = i2c_reg_update_byte_dt(&config->i2c, config->desc->enable_reg,
config->desc->enable_mask, 0u);
/* special case for LDOIO0, which is multiplexed with GPIO0 */
if (config->desc->enable_reg == AXP192_REG_GPIO0_CONTROL) {
ret = mfd_axp192_gpio_func_ctrl(config->mfd, dev, 0, AXP192_GPIO_FUNC_OUTPUT_LOW);
} else {
ret = i2c_reg_update_byte_dt(&config->i2c, config->desc->enable_reg,
config->desc->enable_mask, 0u);
}
if (ret != 0) {
LOG_INST_ERR(config->log, "Failed to disable regulator");
}
@ -354,6 +386,7 @@ static int regulator_axp192_init(const struct device *dev)
REGULATOR_AXP192_DEFINE_COND(inst, dcdc1) \
REGULATOR_AXP192_DEFINE_COND(inst, dcdc2) \
REGULATOR_AXP192_DEFINE_COND(inst, dcdc3) \
REGULATOR_AXP192_DEFINE_COND(inst, ldoio0) \
REGULATOR_AXP192_DEFINE_COND(inst, ldo2) \
REGULATOR_AXP192_DEFINE_COND(inst, ldo3)

View file

@ -0,0 +1,22 @@
# Copyright (c) 2023 Martin Kiepfer
# SPDX-License-Identifier: Apache-2.0
description: PXA192 GPIO Controller
compatible: "x-powers,axp192-gpio"
include: gpio-controller.yaml
properties:
"#gpio-cells":
const: 2
ngpios:
required: true
const: 5
description: |
Number of GPIOs available on axp192.
gpio-cells:
- pin
- flags

View file

@ -25,6 +25,9 @@ description: |
DCDC3 {
/* all properties for DCDC3 */
};
LDOIO0 {
/* all properties for LDOIO0 */
};
LDO2 {
/* all properties for LDO2 */
};

View file

@ -0,0 +1,137 @@
/*
* Copyright (c) 2023 Martin Kiepfer
*
* SPDX-License-Identifier: Apache-2.0
*/
#ifndef ZEPHYR_INCLUDE_DRIVERS_MFD_AXP192_H_
#define ZEPHYR_INCLUDE_DRIVERS_MFD_AXP192_H_
#include <stddef.h>
#include <stdint.h>
#include <zephyr/device.h>
#include <zephyr/drivers/gpio.h>
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief GPIO function type. Only one function can be configured per GPIO.
*/
enum axp192_gpio_func {
AXP192_GPIO_FUNC_INPUT = BIT(0),
AXP192_GPIO_FUNC_OUTPUT_OD = BIT(1),
AXP192_GPIO_FUNC_OUTPUT_LOW = BIT(2),
AXP192_GPIO_FUNC_LDO = BIT(3),
AXP192_GPIO_FUNC_ADC = BIT(4),
AXP192_GPIO_FUNC_PWM = BIT(5),
AXP192_GPIO_FUNC_FLOAT = BIT(6),
AXP192_GPIO_FUNC_CHARGE_CTL = BIT(7),
AXP192_GPIO_FUNC_INVALID
};
/**
* @brief Check if a given GPIO function value is valid.
*/
#define AXP192_GPIO_FUNC_VALID(func) (func < AXP192_GPIO_FUNC_INVALID)
/**
* @brief Maximum number of GPIOs supported by AXP192 PMIC.
*/
#define AXP192_GPIO_MAX_NUM 5U
/**
* @defgroup mdf_interface_axp192 MFD AXP192 interface
* @ingroup mfd_interfaces
* @{
*/
/**
* @brief Request a GPIO pin to be configured to a specific function. GPIO0..4
* of AXP192 feature various functions (see @ref axp192_gpio_func for details).
* A GPIO can only be used by one driver instance. Subsequential calls on the
* same GPIO will overwrite according function.
*
* @param dev axp192 mfd device
* @param client_dev client device the gpio is used in
* @param gpio GPIO to be configured (0..4)
* @param func Function to be configured (see @ref axp192_gpio_func for details)
* @retval 0 on success
* @retval -EINVAL if an invalid GPIO number is passed
* @retval -ENOTSUP if the requested function is not supported by the given
* @retval -errno in case of any bus error
*/
int mfd_axp192_gpio_func_ctrl(const struct device *dev, const struct device *client_dev,
uint8_t gpio, enum axp192_gpio_func func);
/**
* @brief Read out current configuration of a specific GPIO pin.
*
* @param dev axp192 mfd device
* @param gpio GPIO to read configuration from
* @param func Pointer to store current function configuration in.
* @return 0 on success
* @retval -EINVAL if an invalid GPIO number is passed
* @retval -errno in case of any bus error
*/
int mfd_axp192_gpio_func_get(const struct device *dev, uint8_t gpio, enum axp192_gpio_func *func);
/**
* @brief Enable pull-down on specified GPIO pin. AXP192 only supports
* pull-down on GPIO3..4. Pull-ups are not supprted.
*
* @param dev axp192 mfd device
* @param gpio GPIO to control pull-downs
* @param enable true to enable, false to disable pull-down
* @retval 0 on success
* @retval -EINVAL if an invalid argument is given (e.g. invalid GPIO number)
* @retval -ENOTSUP if pull-down is not supported by the givenn GPIO
* @retval -errno in case of any bus error
*/
int mfd_axp192_gpio_pd_ctrl(const struct device *dev, uint8_t gpio, bool enable);
/**
* @brief Read out the current pull-down configuration of a specific GPIO.
*
* @param dev axp192 mfd device
* @param gpio GPIO to control pull-downs
* @param enabled Pointer to current pull-down configuration (true: pull-down
* enabled/ false: pull-down disabled)
* @retval -EINVAL if an invalid argument is given (e.g. invalid GPIO number)
* @retval -ENOTSUP if pull-down is not supported by the givenn GPIO
* @retval -errno in case of any bus error
*/
int mfd_axp192_gpio_pd_get(const struct device *dev, uint8_t gpio, bool *enabled);
/**
* @brief Read GPIO port.
*
* @param dev axp192 mfd device
* @param value Pointer to port value
* @retval 0 on success
* @retval -errno in case of any bus error
*/
int mfd_axp192_gpio_read_port(const struct device *dev, uint8_t *value);
/**
* @brief Write GPIO port.
*
* @param dev axp192 mfd device
* @param value port value
* @param mask pin mask within the port
* @retval 0 on success
* @retval -errno in case of any bus error
*/
int mfd_axp192_gpio_write_port(const struct device *dev, uint8_t value, uint8_t mask);
/**
* @}
*/
#ifdef __cplusplus
}
#endif
#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_AXP192_H_ */

View file

@ -172,6 +172,18 @@
int-gpios = <&test_gpio 0 0>;
reset-gpios = <&test_gpio 0 0>;
};
test_i2c_axp192: axp192@24 {
compatible = "x-powers,axp192";
reg = <0x24>;
axp192_gpio {
compatible = "x-powers,axp192-gpio";
gpio-controller;
#gpio-cells = <2>;
ngpios = <5>;
};
};
};
nct3807_alert_1 {

View file

@ -69,6 +69,7 @@ axp192@4 {
DCDC1 {};
DCDC2 {};
DCDC3 {};
LDOIO0 {};
LDO1 {};
LDO2 {};
LDO3 {};