// SPDX-License-Identifier: GPL-2.0
/*
* Copyright ( c ) 2020 Samsung Electronics Co . , Ltd .
* http : //www.samsung.com/
* Author : Marek Szyprowski < m . szyprowski @ samsung . com >
*
* Simplified generic voltage coupler from regulator core . c
* The main difference is that it keeps current regulator voltage
* if consumers didn ' t apply their constraints yet .
*/
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of.h>
#include <linux/regulator/coupler.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
static int regulator_get_optimal_voltage(struct regulator_dev *rdev,
int *current_uV,
int *min_uV, int *max_uV,
suspend_state_t state)
{
struct coupling_desc *c_desc = &rdev->coupling_desc;
struct regulator_dev **c_rdevs = c_desc->coupled_rdevs;
struct regulation_constraints *constraints = rdev->constraints;
int desired_min_uV = 0 , desired_max_uV = INT_MAX;
int max_current_uV = 0 , min_current_uV = INT_MAX;
int highest_min_uV = 0 , target_uV, possible_uV;
int i, ret, max_spread, n_coupled = c_desc->n_coupled;
bool done;
*current_uV = -1 ;
/* Find highest min desired voltage */
for (i = 0 ; i < n_coupled; i++) {
int tmp_min = 0 ;
int tmp_max = INT_MAX;
lockdep_assert_held_once(&c_rdevs[i]->mutex.base);
ret = regulator_check_consumers(c_rdevs[i],
&tmp_min,
&tmp_max, state);
if (ret < 0 )
return ret;
if (tmp_min == 0 ) {
ret = regulator_get_voltage_rdev(c_rdevs[i]);
if (ret < 0 )
return ret;
tmp_min = ret;
}
/* apply constraints */
ret = regulator_check_voltage(c_rdevs[i], &tmp_min, &tmp_max);
if (ret < 0 )
return ret;
highest_min_uV = max(highest_min_uV, tmp_min);
if (i == 0 ) {
desired_min_uV = tmp_min;
desired_max_uV = tmp_max;
}
}
max_spread = constraints->max_spread[0 ];
/*
* Let target_uV be equal to the desired one if possible .
* If not , set it to minimum voltage , allowed by other coupled
* regulators .
*/
target_uV = max(desired_min_uV, highest_min_uV - max_spread);
/*
* Find min and max voltages , which currently aren ' t violating
* max_spread .
*/
for (i = 1 ; i < n_coupled; i++) {
int tmp_act;
tmp_act = regulator_get_voltage_rdev(c_rdevs[i]);
if (tmp_act < 0 )
return tmp_act;
min_current_uV = min(tmp_act, min_current_uV);
max_current_uV = max(tmp_act, max_current_uV);
}
/*
* Correct target voltage , so as it currently isn ' t
* violating max_spread
*/
possible_uV = max(target_uV, max_current_uV - max_spread);
possible_uV = min(possible_uV, min_current_uV + max_spread);
if (possible_uV > desired_max_uV)
return -EINVAL;
done = (possible_uV == target_uV);
desired_min_uV = possible_uV;
/* Set current_uV if wasn't done earlier in the code and if necessary */
if (*current_uV == -1 ) {
ret = regulator_get_voltage_rdev(rdev);
if (ret < 0 )
return ret;
*current_uV = ret;
}
*min_uV = desired_min_uV;
*max_uV = desired_max_uV;
return done;
}
static int exynos_coupler_balance_voltage(struct regulator_coupler *coupler,
struct regulator_dev *rdev,
suspend_state_t state)
{
struct regulator_dev **c_rdevs;
struct regulator_dev *best_rdev;
struct coupling_desc *c_desc = &rdev->coupling_desc;
int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev;
unsigned int delta, best_delta;
unsigned long c_rdev_done = 0 ;
bool best_c_rdev_done;
c_rdevs = c_desc->coupled_rdevs;
n_coupled = c_desc->n_coupled;
/*
* Find the best possible voltage change on each loop . Leave the loop
* if there isn ' t any possible change .
*/
do {
best_c_rdev_done = false ;
best_delta = 0 ;
best_min_uV = 0 ;
best_max_uV = 0 ;
best_c_rdev = 0 ;
best_rdev = NULL;
/*
* Find highest difference between optimal voltage
* and current voltage .
*/
for (i = 0 ; i < n_coupled; i++) {
/*
* optimal_uV is the best voltage that can be set for
* i - th regulator at the moment without violating
* max_spread constraint in order to balance
* the coupled voltages .
*/
int optimal_uV = 0 , optimal_max_uV = 0 , current_uV = 0 ;
if (test_bit(i, &c_rdev_done))
continue ;
ret = regulator_get_optimal_voltage(c_rdevs[i],
¤t_uV,
&optimal_uV,
&optimal_max_uV,
state);
if (ret < 0 )
goto out;
delta = abs(optimal_uV - current_uV);
if (delta && best_delta <= delta) {
best_c_rdev_done = ret;
best_delta = delta;
best_rdev = c_rdevs[i];
best_min_uV = optimal_uV;
best_max_uV = optimal_max_uV;
best_c_rdev = i;
}
}
/* Nothing to change, return successfully */
if (!best_rdev) {
ret = 0 ;
goto out;
}
ret = regulator_set_voltage_rdev(best_rdev, best_min_uV,
best_max_uV, state);
if (ret < 0 )
goto out;
if (best_c_rdev_done)
set_bit(best_c_rdev, &c_rdev_done);
} while (n_coupled > 1 );
out:
return ret;
}
static int exynos_coupler_attach(struct regulator_coupler *coupler,
struct regulator_dev *rdev)
{
return 0 ;
}
static struct regulator_coupler exynos_coupler = {
.attach_regulator = exynos_coupler_attach,
.balance_voltage = exynos_coupler_balance_voltage,
};
static int __init exynos_coupler_init(void )
{
if (!of_machine_is_compatible("samsung,exynos5800" ))
return 0 ;
return regulator_coupler_register(&exynos_coupler);
}
arch_initcall(exynos_coupler_init);
Messung V0.5 in Prozent C=95 H=80 G=87
¤ Dauer der Verarbeitung: 0.18 Sekunden
(vorverarbeitet am 2026-10-11)
¤
*© Formatika GbR, Deutschland