#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;
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;
}
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];
target_uV = max(desired_min_uV, highest_min_uV - 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);
}
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;
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;
do {
best_c_rdev_done = false;
best_delta = 0;
best_min_uV = 0;
best_max_uV = 0;
best_c_rdev = 0;
best_rdev = NULL;
for (i = 0; i < n_coupled; i++) {
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;
}
}
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