/* * Copyright 2022 Nordic Semiconductor ASA * Copyright 2023 Meta Platforms * SPDX-License-Identifier: Apache-2.0 */ #include #include static void regulator_delay(uint32_t delay_us) { if (delay_us > 0U) { k_sleep(K_USEC(delay_us)); } } void regulator_common_data_init(const struct device *dev) { struct regulator_common_data *data = dev->data; #ifdef CONFIG_REGULATOR_THREAD_SAFE_REFCNT (void)k_mutex_init(&data->lock); #endif data->refcnt = 0; } int regulator_common_init(const struct device *dev, bool is_enabled) { const struct regulator_driver_api *api = dev->api; const struct regulator_common_config *config = dev->config; struct regulator_common_data *data = dev->data; int32_t current_uv; int ret; if (config->initial_mode != REGULATOR_INITIAL_MODE_UNKNOWN) { ret = regulator_set_mode(dev, config->initial_mode); if (ret < 0) { return ret; } } if (REGULATOR_ACTIVE_DISCHARGE_GET_BITS(config->flags) != REGULATOR_ACTIVE_DISCHARGE_DEFAULT) { ret = regulator_set_active_discharge(dev, (bool)REGULATOR_ACTIVE_DISCHARGE_GET_BITS(config->flags)); if (ret < 0) { return ret; } } if (config->init_uv > INT32_MIN) { ret = regulator_set_voltage(dev, config->init_uv, config->init_uv); if (ret < 0) { return ret; } } if (config->init_ua > INT32_MIN) { ret = regulator_set_current_limit(dev, config->init_ua, config->init_ua); if (ret < 0) { return ret; } } /* If we have valid range values, we try to match them before enabling */ if ((config->min_uv > INT32_MIN) || (config->max_uv < INT32_MAX)) { ret = regulator_get_voltage(dev, ¤t_uv); if (ret < 0) { return ret; } /* Snap to closest interval value if out of range */ if (current_uv < config->min_uv) { ret = regulator_set_voltage(dev, config->min_uv, config->min_uv); if (ret < 0) { return ret; } } else if (current_uv > config->max_uv) { ret = regulator_set_voltage(dev, config->max_uv, config->max_uv); if (ret < 0) { return ret; } } } if (is_enabled) { data->refcnt++; if ((config->flags & REGULATOR_BOOT_OFF) != 0U) { return regulator_disable(dev); } } else if ((config->flags & REGULATOR_INIT_ENABLED) != 0U) { ret = api->enable(dev); if (ret < 0) { return ret; } regulator_delay(config->startup_delay_us); data->refcnt++; } return 0; } int regulator_enable(const struct device *dev) { const struct regulator_driver_api *api = dev->api; const struct regulator_common_config *config = dev->config; struct regulator_common_data *data = dev->data; int ret = 0; /* enable not supported (always on) */ if (api->enable == NULL) { return 0; } /* regulator must stay always on */ if ((config->flags & REGULATOR_ALWAYS_ON) != 0U) { return 0; } #ifdef CONFIG_REGULATOR_THREAD_SAFE_REFCNT (void)k_mutex_lock(&data->lock, K_FOREVER); #endif data->refcnt++; if (data->refcnt == 1) { ret = api->enable(dev); if (ret < 0) { data->refcnt--; } else { regulator_delay(config->off_on_delay_us); } } #ifdef CONFIG_REGULATOR_THREAD_SAFE_REFCNT k_mutex_unlock(&data->lock); #endif return ret; } bool regulator_is_enabled(const struct device *dev) { const struct regulator_common_config *config = dev->config; struct regulator_common_data *data = dev->data; bool enabled; if ((config->flags & REGULATOR_ALWAYS_ON) != 0U) { enabled = true; } else { #ifdef CONFIG_REGULATOR_THREAD_SAFE_REFCNT (void)k_mutex_lock(&data->lock, K_FOREVER); #endif enabled = data->refcnt != 0; #ifdef CONFIG_REGULATOR_THREAD_SAFE_REFCNT k_mutex_unlock(&data->lock); #endif } return enabled; } int regulator_disable(const struct device *dev) { const struct regulator_driver_api *api = dev->api; const struct regulator_common_config *config = dev->config; struct regulator_common_data *data = dev->data; int ret = 0; /* disable not supported (always on) */ if (api->disable == NULL) { return 0; } /* regulator must stay always on */ if ((config->flags & REGULATOR_ALWAYS_ON) != 0U) { return 0; } #ifdef CONFIG_REGULATOR_THREAD_SAFE_REFCNT (void)k_mutex_lock(&data->lock, K_FOREVER); #endif if (data->refcnt > 0) { data->refcnt--; if (data->refcnt == 0) { ret = api->disable(dev); if (ret < 0) { data->refcnt++; } } } #ifdef CONFIG_REGULATOR_THREAD_SAFE_REFCNT k_mutex_unlock(&data->lock); #endif return ret; } bool regulator_is_supported_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv) { const struct regulator_common_config *config = dev->config; unsigned int volt_cnt; /* voltage may not be allowed, even if supported */ if ((min_uv > config->max_uv) || (max_uv < config->min_uv)) { return false; } volt_cnt = regulator_count_voltages(dev); for (unsigned int idx = 0U; idx < volt_cnt; idx++) { int32_t volt_uv; (void)regulator_list_voltage(dev, idx, &volt_uv); if ((volt_uv >= min_uv) && (volt_uv <= max_uv)) { return true; } } return false; } int regulator_set_voltage(const struct device *dev, int32_t min_uv, int32_t max_uv) { const struct regulator_common_config *config = dev->config; const struct regulator_driver_api *api = dev->api; if (api->set_voltage == NULL) { return -ENOSYS; } /* voltage may not be allowed, even if supported */ if ((min_uv > config->max_uv) || (max_uv < config->min_uv)) { return -EINVAL; } return api->set_voltage(dev, min_uv, max_uv); } int regulator_set_current_limit(const struct device *dev, int32_t min_ua, int32_t max_ua) { const struct regulator_common_config *config = dev->config; const struct regulator_driver_api *api = dev->api; if (api->set_current_limit == NULL) { return -ENOSYS; } /* current limit may not be allowed, even if supported */ if ((min_ua > config->max_ua) || (max_ua < config->min_ua)) { return -EINVAL; } return api->set_current_limit(dev, min_ua, max_ua); } int regulator_set_mode(const struct device *dev, regulator_mode_t mode) { const struct regulator_common_config *config = dev->config; const struct regulator_driver_api *api = dev->api; if (api->set_mode == NULL) { return -ENOSYS; } /* no mode restrictions */ if (config->allowed_modes_cnt == 0U) { return api->set_mode(dev, mode); } /* check if mode is allowed, apply if it is */ for (uint8_t i = 0U; i < config->allowed_modes_cnt; i++) { if (mode == config->allowed_modes[i]) { return api->set_mode(dev, mode); } } return -ENOTSUP; }