Skip to content

Commit

Permalink
drivers: regulator framework
Browse files Browse the repository at this point in the history
Introduces a voltage regulator driver framework for management
of regulators and supply dependencies. The framework permits 1
regulator supply per regulator.

API function regulator_register() allows a regulator driver to
register a regulator in the regulator framework.

Supported operation here are to enable, disable, get and set
voltage level. They are all optional.

Registered regulators are referenced in a list for initialization
resource release and debug purpose.

Acked-by: Gatien Chevallier <gatien.chevallier@foss.st.com>
Acked-by: Jerome Forissier <jerome.forissier@linaro.org>
Co-developed-by: Pascal Paillet <p.paillet@foss.st.com>
Signed-off-by: Pascal Paillet <p.paillet@foss.st.com>
Signed-off-by: Etienne Carriere <etienne.carriere@foss.st.com>
  • Loading branch information
etienne-lms authored and jforissier committed Oct 2, 2023
1 parent ce56605 commit 1a3d327
Show file tree
Hide file tree
Showing 5 changed files with 467 additions and 0 deletions.
291 changes: 291 additions & 0 deletions core/drivers/regulator/regulator.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,291 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright (c) 2023, STMicroelectronics
*/

#include <assert.h>
#include <compiler.h>
#include <config.h>
#include <drivers/regulator.h>
#include <initcall.h>
#include <keep.h>
#include <kernel/boot.h>
#include <kernel/delay.h>
#include <kernel/mutex.h>
#include <kernel/panic.h>
#include <kernel/pm.h>
#include <kernel/tee_time.h>
#include <kernel/thread.h>
#include <libfdt.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <util.h>

static SLIST_HEAD(, regulator) regulator_device_list =
SLIST_HEAD_INITIALIZER(regulator);

static void lock_regulator(struct regulator *regulator)
{
/*
* Regulator operation may occur at runtime and during specific
* system power transition: power off, PM suspend and resume.
* These operate upon fastcall entries, under PSCI services
* execution, where non-secure world is not operational. In these
* cases we cannot take a mutex and will expect the mutex is
* unlocked.
*/
if (thread_get_id_may_fail() == THREAD_ID_INVALID) {
assert(!regulator->lock.state);
return;
}

mutex_lock(&regulator->lock);
}

static void unlock_regulator(struct regulator *regulator)
{
if (thread_get_id_may_fail() == THREAD_ID_INVALID) {
/* Path for PM sequences when with local Monitor */
return;
}

mutex_unlock(&regulator->lock);
}

static TEE_Result set_state(struct regulator *regulator, bool on_not_off)
{
if (!regulator->ops->set_state)
return TEE_SUCCESS;

return regulator->ops->set_state(regulator, on_not_off);
}

static TEE_Result regulator_refcnt_enable(struct regulator *regulator)
{
TEE_Result res = TEE_ERROR_GENERIC;

FMSG("%s", regulator_name(regulator));

if (regulator->supply) {
res = regulator_enable(regulator->supply);
if (res)
return res;
}

lock_regulator(regulator);

if (!regulator->refcount) {
res = set_state(regulator, true);
if (res) {
EMSG("regul %s set state failed with %#"PRIx32,
regulator_name(regulator), res);

unlock_regulator(regulator);

if (regulator->supply &&
regulator_disable(regulator->supply))
panic();

return res;
}
}

regulator->refcount++;
if (!regulator->refcount)
panic();

FMSG("%s refcount: %u", regulator_name(regulator), regulator->refcount);

unlock_regulator(regulator);

return TEE_SUCCESS;
}

TEE_Result regulator_enable(struct regulator *regulator)
{
assert(regulator);
FMSG("%s", regulator_name(regulator));

if (regulator_is_always_on(regulator))
return TEE_SUCCESS;

return regulator_refcnt_enable(regulator);
}

static TEE_Result regulator_refcnt_disable(struct regulator *regulator)
{
FMSG("%s", regulator_name(regulator));

lock_regulator(regulator);

if (regulator->refcount == 1) {
TEE_Result res = set_state(regulator, false);

if (res) {
EMSG("regul %s set state failed with %#"PRIx32,
regulator_name(regulator), res);
unlock_regulator(regulator);
return res;
}
}

if (!regulator->refcount) {
EMSG("Unbalanced %s", regulator_name(regulator));
panic();
}

regulator->refcount--;

FMSG("%s refcount: %u", regulator_name(regulator), regulator->refcount);

unlock_regulator(regulator);

if (regulator->supply && regulator_disable(regulator->supply)) {
/* We can't leave this unbalanced */
EMSG("Can't disable %s", regulator_name(regulator->supply));
panic();
}

return TEE_SUCCESS;
}

TEE_Result regulator_disable(struct regulator *regulator)
{
assert(regulator);
FMSG("%s", regulator_name(regulator));

if (regulator_is_always_on(regulator))
return TEE_SUCCESS;

return regulator_refcnt_disable(regulator);
}

bool regulator_is_enabled(struct regulator *regulator)
{
TEE_Result res = TEE_SUCCESS;
bool enabled = false;

if (!regulator->ops->get_state)
return true;

lock_regulator(regulator);
res = regulator->ops->get_state(regulator, &enabled);
unlock_regulator(regulator);

if (res)
EMSG("regul %s get state failed with %#"PRIx32,
regulator_name(regulator), res);

return !res && enabled;
}

TEE_Result regulator_set_voltage(struct regulator *regulator, int level_uv)
{
TEE_Result res = TEE_ERROR_GENERIC;

assert(regulator);
FMSG("%s %duV", regulator_name(regulator), level_uv);

if (level_uv < regulator->min_uv || level_uv > regulator->max_uv)
return TEE_ERROR_BAD_PARAMETERS;

if (level_uv == regulator->cur_uv)
return TEE_SUCCESS;

if (!regulator->ops->set_voltage)
return TEE_ERROR_NOT_SUPPORTED;

lock_regulator(regulator);
res = regulator->ops->set_voltage(regulator, level_uv);
unlock_regulator(regulator);

if (res) {
EMSG("regul %s set volt failed with %#"PRIx32,
regulator_name(regulator), res);
return res;
}

regulator->cur_uv = level_uv;

return TEE_SUCCESS;
}

TEE_Result regulator_register(struct regulator *regulator)
{
TEE_Result res = TEE_SUCCESS;
int min_uv = 0;
int max_uv = 0;
int uv = 0;

if (!regulator || !regulator->ops)
return TEE_ERROR_BAD_PARAMETERS;

regulator_get_range(regulator, &min_uv, &max_uv);
if (min_uv > max_uv)
return TEE_ERROR_BAD_PARAMETERS;

/* Sanitize regulator effective level */
if (regulator->ops->get_voltage) {
res = regulator->ops->get_voltage(regulator, &uv);
if (res)
return res;
} else {
uv = min_uv;
}
regulator->cur_uv = uv;

if (uv < min_uv || uv > max_uv) {
res = regulator_set_voltage(regulator, min_uv);
if (res)
return res;
}

SLIST_INSERT_HEAD(&regulator_device_list, regulator, link);

return TEE_SUCCESS;
}

/*
* Log regulators state
*/
void regulator_print_state(const char *message __maybe_unused)
{
struct regulator *regulator = NULL;

DMSG("Regulator state: %s", message);
DMSG("name use\ten\tuV\tmin\tmax\tsupply");

SLIST_FOREACH(regulator, &regulator_device_list, link)
DMSG("%8s %u\t%d\t%d\t%d\t%d\t%#x\t%s\n",
regulator->name, regulator->refcount,
regulator_is_enabled(regulator),
regulator_get_voltage(regulator),
regulator->min_uv, regulator->max_uv,
regulator->supply ? regulator_name(regulator->supply) :
"<none>");
}

/*
* Clean-up regulators that are not used.
*/
static TEE_Result regulator_core_cleanup(void)
{
struct regulator *regulator = NULL;

SLIST_FOREACH(regulator, &regulator_device_list, link) {
if (!regulator->refcount) {
DMSG("disable %s", regulator_name(regulator));
lock_regulator(regulator);
set_state(regulator, false /* disable */);
unlock_regulator(regulator);
}
}

regulator_print_state(__func__);

return TEE_SUCCESS;
}

release_init_resource(regulator_core_cleanup);
1 change: 1 addition & 0 deletions core/drivers/regulator/sub.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
srcs-y += regulator.c
1 change: 1 addition & 0 deletions core/drivers/sub.mk
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ subdirs-$(CFG_DRIVERS_CLK) += clk
subdirs-$(CFG_DRIVERS_GPIO) += gpio
subdirs-$(CFG_DRIVERS_I2C) += i2c
subdirs-$(CFG_DRIVERS_PINCTRL) += pinctrl
subdirs-$(CFG_DRIVERS_REGULATOR) += regulator
subdirs-$(CFG_DRIVERS_RSTCTRL) += rstctrl
subdirs-$(CFG_SCMI_MSG_DRIVERS) += scmi-msg
subdirs-y += imx
Expand Down
Loading

0 comments on commit 1a3d327

Please sign in to comment.