-
Notifications
You must be signed in to change notification settings - Fork 6.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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 <[email protected]>
- Loading branch information
1 parent
d4169c9
commit 74db02b
Showing
11 changed files
with
1,055 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | ||
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) |
Oops, something went wrong.