322 lines
8.5 KiB
C
322 lines
8.5 KiB
C
/*
|
||
* 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 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)
|