222 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (c) 2020 Samsung Electronics Co., Ltd.4 * http://www.samsung.com/5 * Author: Marek Szyprowski <m.szyprowski@samsung.com>6 *7 * Simplified generic voltage coupler from regulator core.c8 * The main difference is that it keeps current regulator voltage9 * if consumers didn't apply their constraints yet.10 */11 12#include <linux/init.h>13#include <linux/kernel.h>14#include <linux/of.h>15#include <linux/regulator/coupler.h>16#include <linux/regulator/driver.h>17#include <linux/regulator/machine.h>18 19static int regulator_get_optimal_voltage(struct regulator_dev *rdev,20 int *current_uV,21 int *min_uV, int *max_uV,22 suspend_state_t state)23{24 struct coupling_desc *c_desc = &rdev->coupling_desc;25 struct regulator_dev **c_rdevs = c_desc->coupled_rdevs;26 struct regulation_constraints *constraints = rdev->constraints;27 int desired_min_uV = 0, desired_max_uV = INT_MAX;28 int max_current_uV = 0, min_current_uV = INT_MAX;29 int highest_min_uV = 0, target_uV, possible_uV;30 int i, ret, max_spread, n_coupled = c_desc->n_coupled;31 bool done;32 33 *current_uV = -1;34 35 /* Find highest min desired voltage */36 for (i = 0; i < n_coupled; i++) {37 int tmp_min = 0;38 int tmp_max = INT_MAX;39 40 lockdep_assert_held_once(&c_rdevs[i]->mutex.base);41 42 ret = regulator_check_consumers(c_rdevs[i],43 &tmp_min,44 &tmp_max, state);45 if (ret < 0)46 return ret;47 48 if (tmp_min == 0) {49 ret = regulator_get_voltage_rdev(c_rdevs[i]);50 if (ret < 0)51 return ret;52 tmp_min = ret;53 }54 55 /* apply constraints */56 ret = regulator_check_voltage(c_rdevs[i], &tmp_min, &tmp_max);57 if (ret < 0)58 return ret;59 60 highest_min_uV = max(highest_min_uV, tmp_min);61 62 if (i == 0) {63 desired_min_uV = tmp_min;64 desired_max_uV = tmp_max;65 }66 }67 68 max_spread = constraints->max_spread[0];69 70 /*71 * Let target_uV be equal to the desired one if possible.72 * If not, set it to minimum voltage, allowed by other coupled73 * regulators.74 */75 target_uV = max(desired_min_uV, highest_min_uV - max_spread);76 77 /*78 * Find min and max voltages, which currently aren't violating79 * max_spread.80 */81 for (i = 1; i < n_coupled; i++) {82 int tmp_act;83 84 tmp_act = regulator_get_voltage_rdev(c_rdevs[i]);85 if (tmp_act < 0)86 return tmp_act;87 88 min_current_uV = min(tmp_act, min_current_uV);89 max_current_uV = max(tmp_act, max_current_uV);90 }91 92 /*93 * Correct target voltage, so as it currently isn't94 * violating max_spread95 */96 possible_uV = max(target_uV, max_current_uV - max_spread);97 possible_uV = min(possible_uV, min_current_uV + max_spread);98 99 if (possible_uV > desired_max_uV)100 return -EINVAL;101 102 done = (possible_uV == target_uV);103 desired_min_uV = possible_uV;104 105 /* Set current_uV if wasn't done earlier in the code and if necessary */106 if (*current_uV == -1) {107 ret = regulator_get_voltage_rdev(rdev);108 if (ret < 0)109 return ret;110 *current_uV = ret;111 }112 113 *min_uV = desired_min_uV;114 *max_uV = desired_max_uV;115 116 return done;117}118 119static int exynos_coupler_balance_voltage(struct regulator_coupler *coupler,120 struct regulator_dev *rdev,121 suspend_state_t state)122{123 struct regulator_dev **c_rdevs;124 struct regulator_dev *best_rdev;125 struct coupling_desc *c_desc = &rdev->coupling_desc;126 int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev;127 unsigned int delta, best_delta;128 unsigned long c_rdev_done = 0;129 bool best_c_rdev_done;130 131 c_rdevs = c_desc->coupled_rdevs;132 n_coupled = c_desc->n_coupled;133 134 /*135 * Find the best possible voltage change on each loop. Leave the loop136 * if there isn't any possible change.137 */138 do {139 best_c_rdev_done = false;140 best_delta = 0;141 best_min_uV = 0;142 best_max_uV = 0;143 best_c_rdev = 0;144 best_rdev = NULL;145 146 /*147 * Find highest difference between optimal voltage148 * and current voltage.149 */150 for (i = 0; i < n_coupled; i++) {151 /*152 * optimal_uV is the best voltage that can be set for153 * i-th regulator at the moment without violating154 * max_spread constraint in order to balance155 * the coupled voltages.156 */157 int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0;158 159 if (test_bit(i, &c_rdev_done))160 continue;161 162 ret = regulator_get_optimal_voltage(c_rdevs[i],163 ¤t_uV,164 &optimal_uV,165 &optimal_max_uV,166 state);167 if (ret < 0)168 goto out;169 170 delta = abs(optimal_uV - current_uV);171 172 if (delta && best_delta <= delta) {173 best_c_rdev_done = ret;174 best_delta = delta;175 best_rdev = c_rdevs[i];176 best_min_uV = optimal_uV;177 best_max_uV = optimal_max_uV;178 best_c_rdev = i;179 }180 }181 182 /* Nothing to change, return successfully */183 if (!best_rdev) {184 ret = 0;185 goto out;186 }187 188 ret = regulator_set_voltage_rdev(best_rdev, best_min_uV,189 best_max_uV, state);190 191 if (ret < 0)192 goto out;193 194 if (best_c_rdev_done)195 set_bit(best_c_rdev, &c_rdev_done);196 197 } while (n_coupled > 1);198 199out:200 return ret;201}202 203static int exynos_coupler_attach(struct regulator_coupler *coupler,204 struct regulator_dev *rdev)205{206 return 0;207}208 209static struct regulator_coupler exynos_coupler = {210 .attach_regulator = exynos_coupler_attach,211 .balance_voltage = exynos_coupler_balance_voltage,212};213 214static int __init exynos_coupler_init(void)215{216 if (!of_machine_is_compatible("samsung,exynos5800"))217 return 0;218 219 return regulator_coupler_register(&exynos_coupler);220}221arch_initcall(exynos_coupler_init);222