| /* |
| * 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++) { |
| 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, \ |
| ®ulator_gpio_api); |
| |
| DT_INST_FOREACH_STATUS_OKAY(REGULATOR_GPIO_DEFINE) |