|
|
/* |
|
|
* 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; |
|
|
} |
|
|
|
|
|
#if defined(CONFIG_GPIO_GET_CONFIG) || defined(CONFIG_GPIO_GET_DIRECTION) |
|
|
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 DEVICE_API(gpio, 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)
|
|
|
|