From b374fa7c2bb50567deb8657d5719bec70b97b476 Mon Sep 17 00:00:00 2001 From: Martin Kiepfer Date: Wed, 19 Jul 2023 21:53:55 +0200 Subject: [PATCH] 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 --- drivers/gpio/CMakeLists.txt | 1 + drivers/gpio/Kconfig | 2 + drivers/gpio/Kconfig.axp192 | 21 + drivers/gpio/gpio_axp192.c | 321 ++++++++++ drivers/mfd/mfd_axp192.c | 558 +++++++++++++++++- drivers/regulator/regulator_axp192.c | 41 +- dts/bindings/gpio/x-powers,axp192-gpio.yaml | 22 + .../regulator/x-powers,axp192-regulator.yaml | 3 + include/zephyr/drivers/mfd/axp192.h | 161 +++++ tests/drivers/build_all/gpio/app.overlay | 12 + tests/drivers/build_all/regulator/i2c.dtsi | 1 + 11 files changed, 1137 insertions(+), 6 deletions(-) create mode 100644 drivers/gpio/Kconfig.axp192 create mode 100644 drivers/gpio/gpio_axp192.c create mode 100644 dts/bindings/gpio/x-powers,axp192-gpio.yaml create mode 100644 include/zephyr/drivers/mfd/axp192.h diff --git a/drivers/gpio/CMakeLists.txt b/drivers/gpio/CMakeLists.txt index 0e583053eb431ce..4787455eb0f953c 100644 --- a/drivers/gpio/CMakeLists.txt +++ b/drivers/gpio/CMakeLists.txt @@ -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) diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 62ff3cb89e095f2..3e73eab3d9f7687 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -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" diff --git a/drivers/gpio/Kconfig.axp192 b/drivers/gpio/Kconfig.axp192 new file mode 100644 index 000000000000000..a9e648309fb89ab --- /dev/null +++ b/drivers/gpio/Kconfig.axp192 @@ -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. diff --git a/drivers/gpio/gpio_axp192.c b/drivers/gpio/gpio_axp192.c new file mode 100644 index 000000000000000..82be69ce97a3716 --- /dev/null +++ b/drivers/gpio/gpio_axp192.c @@ -0,0 +1,321 @@ +/* + * Copyright (c) 2023 Martin Kiepfer + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT x_powers_axp192_gpio + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +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_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 */ + 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) diff --git a/drivers/mfd/mfd_axp192.c b/drivers/mfd/mfd_axp192.c index ee144721db40b5d..c3ff88db31371d7 100644 --- a/drivers/mfd/mfd_axp192.c +++ b/drivers/mfd/mfd_axp192.c @@ -6,7 +6,9 @@ #define DT_DRV_COMPAT x_powers_axp192 #include +#include +#include #include #include #include @@ -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,463 @@ 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, ®_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_read_pin(const struct device *dev, uint8_t pin, bool *value) +{ + uint8_t port_val; + int ret; + + if (pin >= AXP192_GPIO_MAX_NUM) { + LOG_ERR("Invalid gpio (%d)", pin); + return -EINVAL; + } + + ret = mfd_axp192_gpio_read_port(dev, &port_val); + if (ret != 0) { + return ret; + } + + if ((port_val & (1u << pin)) != 0) { + *value = true; + } else { + *value = false; + } + + return 0; +} + +int mfd_axp192_gpio_write_pin(const struct device *dev, uint8_t pin, bool value) +{ + const struct mfd_axp192_config *config = dev->config; + uint8_t reg_val; + uint8_t reg_mask; + int ret; + + if ((pin >= 0) && (pin <= 2U)) { + + reg_mask = (1U << pin) & AXP192_GPIO012_OUTPUT_MASK; + if (value) { + reg_val = (1U << pin); + } else { + reg_val = 0; + } + ret = i2c_reg_update_byte_dt(&(config->i2c), AXP192_GPIO012_REG_PINVAL, reg_mask, + reg_val); + } else if ((pin >= 3U) && (pin <= 4U)) { + + reg_mask = (1U << (pin - 3U)) & AXP192_GPIO34_OUTPUT_MASK; + if (value) { + reg_val = (1U << (pin - 3U)); + } else { + reg_val = 0; + } + ret = i2c_reg_update_byte_dt(&(config->i2c), AXP192_GPIO34_REG_PINVAL, reg_mask, + reg_val); + } else { + LOG_ERR("Invalid gpio (%d)", pin); + ret = -EINVAL; + } + + return ret; +} + +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; + 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; + 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 = { \ + .gpio_mask_used = {0}, \ + .gpio_mask_output = 0, \ + }; \ + \ + 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) diff --git a/drivers/regulator/regulator_axp192.c b/drivers/regulator/regulator_axp192.c index a2513d6e5aef442..56f46cca0db6bda 100644 --- a/drivers/regulator/regulator_axp192.c +++ b/drivers/regulator/regulator_axp192.c @@ -16,6 +16,7 @@ #include #include #include +#include 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) diff --git a/dts/bindings/gpio/x-powers,axp192-gpio.yaml b/dts/bindings/gpio/x-powers,axp192-gpio.yaml new file mode 100644 index 000000000000000..4231777d6e366e1 --- /dev/null +++ b/dts/bindings/gpio/x-powers,axp192-gpio.yaml @@ -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 diff --git a/dts/bindings/regulator/x-powers,axp192-regulator.yaml b/dts/bindings/regulator/x-powers,axp192-regulator.yaml index 6f8d8718357a966..98599731c3b4049 100644 --- a/dts/bindings/regulator/x-powers,axp192-regulator.yaml +++ b/dts/bindings/regulator/x-powers,axp192-regulator.yaml @@ -25,6 +25,9 @@ description: | DCDC3 { /* all properties for DCDC3 */ }; + LDOIO0 { + /* all properties for LDOIO0 */ + }; LDO2 { /* all properties for LDO2 */ }; diff --git a/include/zephyr/drivers/mfd/axp192.h b/include/zephyr/drivers/mfd/axp192.h new file mode 100644 index 000000000000000..0bc60997be27a7e --- /dev/null +++ b/include/zephyr/drivers/mfd/axp192.h @@ -0,0 +1,161 @@ +/* + * 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 +#include + +#include +#include + +#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); + +/** + * @brief Read GPIO pin. + * + * @param dev axp192 mfd device + * @param pin GPIO pin value to be read (0..4) + * @param value Pointer to pin value + * @retval 0 on success + * @retval -EINVAL if the specified GPIO pin is invalid + * @retval -errno in case of any bus error + */ +int mfd_axp192_gpio_read_pin(const struct device *dev, uint8_t pin, bool *value); + +/** + * @brief Write GPIO pin. + * + * @param dev axp102 mfd device + * @param pin GPIO pin value to be written + * @param value Pin value + * @retval 0 on success + * @retval -EINVAL if the specified GPIO pin is invalid + * @retval -errno in case of any bus error + */ +int mfd_axp192_gpio_write_pin(const struct device *dev, uint8_t pin, bool value); + +/** + * @} + */ + +#ifdef __cplusplus +} +#endif + +#endif /* ZEPHYR_INCLUDE_DRIVERS_MFD_AXP192_H_ */ diff --git a/tests/drivers/build_all/gpio/app.overlay b/tests/drivers/build_all/gpio/app.overlay index b0cb9b756f4d60b..64643820d5bbf01 100644 --- a/tests/drivers/build_all/gpio/app.overlay +++ b/tests/drivers/build_all/gpio/app.overlay @@ -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>; + }; + }; }; test_spi: spi@33334444 { diff --git a/tests/drivers/build_all/regulator/i2c.dtsi b/tests/drivers/build_all/regulator/i2c.dtsi index 250c359f9c89bdc..12988303a61fcb1 100644 --- a/tests/drivers/build_all/regulator/i2c.dtsi +++ b/tests/drivers/build_all/regulator/i2c.dtsi @@ -69,6 +69,7 @@ axp192@4 { DCDC1 {}; DCDC2 {}; DCDC3 {}; + LDOIO0 {}; LDO1 {}; LDO2 {}; LDO3 {};