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>
268 lines
5.8 KiB
C
268 lines
5.8 KiB
C
/*
|
|
* Copyright 2022 Nordic Semiconductor ASA
|
|
* 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) {
|
|
#ifdef CONFIG_MULTITHREADING
|
|
k_sleep(K_USEC(delay_us));
|
|
#else
|
|
k_busy_wait(delay_us);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
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 (config->init_uv > INT32_MIN) {
|
|
ret = regulator_set_voltage(dev, config->init_uv, config->init_uv);
|
|
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++;
|
|
} 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
|
|
|
|
if (data->refcnt == 0) {
|
|
ret = api->enable(dev);
|
|
if (ret == 0) {
|
|
data->refcnt++;
|
|
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
|
|
|
|
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;
|
|
}
|