/* * Copyright 2023 EPAM Systems * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT regulator_gpio #include #include #include #include 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, \ ®ulator_gpio_api); DT_INST_FOREACH_STATUS_OKAY(REGULATOR_GPIO_DEFINE)