You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
288 lines
6.4 KiB
288 lines
6.4 KiB
/* |
|
* Copyright 2022 Nordic Semiconductor ASA |
|
* Copyright 2023 Meta Platforms |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#include <zephyr/kernel.h> |
|
#include <zephyr/drivers/regulator.h> |
|
|
|
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; |
|
}
|
|
|