zephyr/drivers/regulator/regulator_gpio.c
Andy Sinclair 04e18f093f drivers: regulator: Added startup and off/on delay to common driver
A configurable delay during regulator switch on is currently
only supported by the GPIO and fixed regulator drivers.

This functionality has been moved to the common driver, so it can
be easily added to any regulator driver.

Signed-off-by: Andy Sinclair <andy.sinclair@nordicsemi.no>
2023-11-13 21:30:10 +00:00

232 lines
6.7 KiB
C

/*
* Copyright 2023 EPAM Systems
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT regulator_gpio
#include <stdint.h>
#include <zephyr/drivers/regulator.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(regulator_gpio, CONFIG_REGULATOR_LOG_LEVEL);
struct regulator_gpio_config {
struct regulator_common_config common;
const struct gpio_dt_spec *gpios;
uint8_t num_gpios;
const int32_t *states;
uint8_t states_cnt;
const struct gpio_dt_spec enable;
};
struct regulator_gpio_data {
struct regulator_common_data common;
int32_t current_volt_uv;
};
static int regulator_gpio_apply_state(const struct device *dev, uint32_t state)
{
const struct regulator_gpio_config *cfg = dev->config;
for (unsigned int gpio_idx = 0; gpio_idx < cfg->num_gpios; gpio_idx++) {
int ret;
int new_state_of_gpio = (state >> gpio_idx) & 0x1;
ret = gpio_pin_get_dt(&cfg->gpios[gpio_idx]);
if (ret < 0) {
LOG_ERR("%s: can't get pin state", dev->name);
return ret;
}
if (ret != new_state_of_gpio) {
ret = gpio_pin_set_dt(&cfg->gpios[gpio_idx], new_state_of_gpio);
if (ret < 0) {
LOG_ERR("%s: can't set pin state", dev->name);
return ret;
}
}
}
return 0;
}
static int regulator_gpio_enable(const struct device *dev)
{
const struct regulator_gpio_config *cfg = dev->config;
int ret;
if (cfg->enable.port == NULL) {
return 0;
}
ret = gpio_pin_set_dt(&cfg->enable, 1);
if (ret < 0) {
LOG_ERR("%s: can't enable regulator!", dev->name);
return ret;
}
return 0;
}
static int regulator_gpio_disable(const struct device *dev)
{
const struct regulator_gpio_config *cfg = dev->config;
if (cfg->enable.port == NULL) {
return 0;
}
return gpio_pin_set_dt(&cfg->enable, 0);
}
static unsigned int regulator_gpio_count_voltages(const struct device *dev)
{
const struct regulator_gpio_config *cfg = dev->config;
return cfg->states_cnt;
}
static int regulator_gpio_list_voltage(const struct device *dev, unsigned int idx, int32_t *volt_uv)
{
const struct regulator_gpio_config *cfg = dev->config;
if (idx >= cfg->states_cnt) {
LOG_ERR("%s: can't get list voltage for idx %u", dev->name, idx);
return -EINVAL;
}
*volt_uv = cfg->states[idx * 2];
return 0;
}
static int regulator_gpio_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv)
{
const struct regulator_gpio_config *cfg = dev->config;
struct regulator_gpio_data *data = dev->data;
int32_t best_voltage = INT32_MAX;
unsigned int best_state;
int ret = 0;
/* choose minimum possible voltage in range provided by a caller */
for (unsigned int state_idx = 0; state_idx < cfg->states_cnt; state_idx++) {
if (!IN_RANGE(cfg->states[state_idx * 2], min_uv, max_uv) ||
cfg->states[state_idx * 2] >= best_voltage) {
continue;
}
best_voltage = cfg->states[state_idx * 2];
best_state = cfg->states[state_idx * 2 + 1];
}
if (best_voltage == INT32_MAX) {
LOG_ERR("%s: can't find voltage is states", dev->name);
return -EINVAL;
}
if (best_voltage == data->current_volt_uv) {
return 0;
}
ret = regulator_gpio_apply_state(dev, best_state);
if (ret) {
return ret;
}
data->current_volt_uv = best_voltage;
return 0;
}
static int regulator_gpio_get_voltage(const struct device *dev, int32_t *volt_uv)
{
const struct regulator_gpio_data *data = dev->data;
*volt_uv = data->current_volt_uv;
return 0;
}
static const struct regulator_driver_api regulator_gpio_api = {
.enable = regulator_gpio_enable,
.disable = regulator_gpio_disable,
.set_voltage = regulator_gpio_set_voltage,
.get_voltage = regulator_gpio_get_voltage,
.count_voltages = regulator_gpio_count_voltages,
.list_voltage = regulator_gpio_list_voltage,
};
static int regulator_gpio_init(const struct device *dev)
{
const struct regulator_gpio_config *cfg = dev->config;
int ret;
regulator_common_data_init(dev);
for (unsigned int gpio_idx = 0; gpio_idx < cfg->num_gpios; gpio_idx++) {
int ret;
if (!gpio_is_ready_dt(&cfg->gpios[gpio_idx])) {
LOG_ERR("%s: gpio pin: %s not ready", dev->name,
cfg->gpios[gpio_idx].port ? cfg->gpios[gpio_idx].port->name
: "null");
return -ENODEV;
}
ret = gpio_pin_configure_dt(&cfg->gpios[gpio_idx], GPIO_OUTPUT);
if (ret < 0) {
LOG_ERR("%s: can't configure pin (%d) as output", dev->name,
cfg->gpios[gpio_idx].pin);
return ret;
}
}
if (cfg->enable.port != NULL) {
if (!gpio_is_ready_dt(&cfg->enable)) {
LOG_ERR("%s: gpio pin: %s not ready", dev->name, cfg->enable.port->name);
return -ENODEV;
}
ret = gpio_pin_configure_dt(&cfg->enable, GPIO_OUTPUT | GPIO_OUTPUT_INIT_LOW);
if (ret < 0) {
LOG_ERR("%s: can't configure enable pin (%d) as output", dev->name,
cfg->enable.pin);
return ret;
}
}
return regulator_common_init(dev, false);
}
#define REG_GPIO_CONTEXT_GPIOS_SPEC_ELEM(_node_id, _prop, _idx) \
GPIO_DT_SPEC_GET_BY_IDX(_node_id, _prop, _idx),
#define REG_GPIO_CONTEXT_GPIOS_FOREACH_ELEM(inst) \
DT_FOREACH_PROP_ELEM(DT_DRV_INST(inst), gpios, REG_GPIO_CONTEXT_GPIOS_SPEC_ELEM)
#define REG_GPIO_CONTEXT_GPIOS_INITIALIZE(inst) \
.gpios = (const struct gpio_dt_spec[]){REG_GPIO_CONTEXT_GPIOS_FOREACH_ELEM(inst)}, \
.num_gpios = DT_INST_PROP_LEN(inst, gpios)
#define REGULATOR_GPIO_DEFINE(inst) \
static struct regulator_gpio_data data##inst = { \
.current_volt_uv = INT32_MAX, \
}; \
BUILD_ASSERT(!(DT_INST_PROP_LEN(inst, states) & 0x1), \
"Number of regulator states should be even"); \
static const struct regulator_gpio_config config##inst = { \
.common = REGULATOR_DT_INST_COMMON_CONFIG_INIT(inst), \
REG_GPIO_CONTEXT_GPIOS_INITIALIZE(inst), \
.enable = GPIO_DT_SPEC_INST_GET_OR(inst, enable_gpios, {0}), \
.states = ((const int[])DT_INST_PROP(inst, states)), \
.states_cnt = DT_INST_PROP_LEN(inst, states) / 2, \
}; \
DEVICE_DT_INST_DEFINE(inst, regulator_gpio_init, NULL, &data##inst, &config##inst, \
POST_KERNEL, CONFIG_REGULATOR_GPIO_INIT_PRIORITY, \
&regulator_gpio_api);
DT_INST_FOREACH_STATUS_OKAY(REGULATOR_GPIO_DEFINE)