blob: 421f5d19a37cb011216e526bb762a19aedb1c16d [file] [log] [blame]
/*
* Copyright (c) 2023 Martin Kiepfer <mrmarteng@teleschirm.org>
* SPDX-License-Identifier: Apache-2.0
*/
#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>
LOG_MODULE_REGISTER(mfd_axp192, CONFIG_MFD_LOG_LEVEL);
/* Chip ID value */
#define AXP192_CHIP_ID 0x03U
/* Registers definitions */
#define AXP192_REG_CHIP_ID 0x03U
/* AXP192 GPIO register addresses */
#define AXP192_EXTEN_DCDC2_CONTROL_REG 0x10U
#define AXP192_VBUS_CFG_REG 0x30U
#define AXP192_GPIO0_FUNC_REG 0x90U
#define AXP192_GPIO1_FUNC_REG 0x92U
#define AXP192_GPIO2_FUNC_REG 0x93U
#define AXP192_GPIO34_FUNC_REG 0x95U
#define AXP192_GPIO012_PINVAL_REG 0x94U
#define AXP192_GPIO34_PINVAL_REG 0x96U
#define AXP192_GPIO012_PULLDOWN_REG 0x97U
/* VBUS control reg values */
#define AXP192_VBUS_CFG_VAL_VBUSEN_DISABLE 0x80U
/* 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)
#define AXP192_EXTEN_ENA 0x04U
#define AXP192_EXTEN_MASK 0x04U
/* 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)
#define AXP192_GPIO5_OUTPUT_MASK 0x04U
#define AXP192_GPIO5_OUTPUT_VAL 0x04U
#define AXP192_GPIO5_OUTPUT_SHIFT 3U
struct mfd_axp192_config {
struct i2c_dt_spec i2c;
bool vbusen_disable;
};
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_FUNC_REG,
.mask = AXP192_GPIO012_FUNC_MASK,
},
{
/* GPIO1 */
.reg = AXP192_GPIO1_FUNC_REG,
.mask = AXP192_GPIO012_FUNC_MASK,
},
{
/* GPIO2 */
.reg = AXP192_GPIO2_FUNC_REG,
.mask = AXP192_GPIO012_FUNC_MASK,
},
{
/* GPIO3 */
.reg = AXP192_GPIO34_FUNC_REG,
.mask = AXP192_GPIO3_FUNC_MASK,
},
{
/* GPIO4 */
.reg = AXP192_GPIO34_FUNC_REG,
.mask = AXP192_GPIO4_FUNC_MASK,
},
};
static int mfd_axp192_init(const struct device *dev)
{
const struct mfd_axp192_config *config = dev->config;
uint8_t chip_id;
uint8_t vbus_val;
int ret;
LOG_DBG("Initializing instance");
if (!i2c_is_ready_dt(&config->i2c)) {
LOG_ERR("I2C bus not ready");
return -ENODEV;
}
/* Check if axp192 chip is available */
ret = i2c_reg_read_byte_dt(&config->i2c, AXP192_REG_CHIP_ID, &chip_id);
if (ret < 0) {
return ret;
}
if (chip_id != AXP192_CHIP_ID) {
LOG_ERR("Invalid Chip detected (%d)", chip_id);
return -EINVAL;
}
/* Disable N_VBUSEN */
vbus_val = 0;
if (config->vbusen_disable) {
vbus_val = AXP192_VBUS_CFG_VAL_VBUSEN_DISABLE;
}
ret = i2c_reg_update_byte_dt(&config->i2c, AXP192_VBUS_CFG_REG,
AXP192_VBUS_CFG_VAL_VBUSEN_DISABLE, vbus_val);
if (ret < 0) {
return ret;
}
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;
}
if (gpio < ARRAY_SIZE(gpio_reg_desc)) {
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;
case 5U:
/* EXTEN is an output only pin */
*func = AXP192_GPIO_FUNC_OUTPUT_LOW;
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 = 0;
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;
case 5U:
/* EXTEN is an output only pin */
break;
default:
ret = -EINVAL;
}
if (ret != 0) {
LOG_ERR("Invalid function (0x%x) for gpio %d", func, gpio);
return ret;
}
if (gpio < ARRAY_SIZE(gpio_reg_desc)) {
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_PULLDOWN_REG, &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_PULLDOWN_REG, 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 gpio5_val;
uint8_t gpio_input_val;
uint8_t gpio_output_val;
/* read gpio0-2 */
ret = i2c_reg_read_byte_dt(&(config->i2c), AXP192_GPIO012_PINVAL_REG, &gpio012_val);
if (ret != 0) {
return ret;
}
/* read gpio3-4 */
ret = i2c_reg_read_byte_dt(&(config->i2c), AXP192_GPIO34_PINVAL_REG, &gpio34_val);
if (ret != 0) {
return ret;
}
/* read gpio5 */
ret = i2c_reg_read_byte_dt(&(config->i2c), AXP192_EXTEN_DCDC2_CONTROL_REG, &gpio5_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("GPIO5 pinval-reg =0x%x", gpio5_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);
gpio_output_val |=
(((gpio5_val & AXP192_GPIO5_OUTPUT_MASK) >> AXP192_GPIO5_OUTPUT_SHIFT) << 5u);
*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_PINVAL_REG,
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_PINVAL_REG,
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);
}
/* Write gpio5. Mask out other port pins */
if ((mask & BIT(5)) != 0) {
gpio_reg_mask = AXP192_EXTEN_MASK;
gpio_reg_val = (value & BIT(5)) ? AXP192_EXTEN_ENA : 0U;
ret = i2c_reg_update_byte_dt(&(config->i2c), AXP192_EXTEN_DCDC2_CONTROL_REG,
gpio_reg_mask, gpio_reg_val);
if (ret != 0) {
return ret;
}
LOG_DBG("GPIO5 pinval-reg =0x%x mask=0x%x\n", 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), \
.vbusen_disable = DT_INST_PROP_OR(inst, vbusen_disable, false), \
}; \
\
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);