18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2020 Samsung Electronics Co., Ltd. 48c2ecf20Sopenharmony_ci * http://www.samsung.com/ 58c2ecf20Sopenharmony_ci * Author: Marek Szyprowski <m.szyprowski@samsung.com> 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Simplified generic voltage coupler from regulator core.c 88c2ecf20Sopenharmony_ci * The main difference is that it keeps current regulator voltage 98c2ecf20Sopenharmony_ci * if consumers didn't apply their constraints yet. 108c2ecf20Sopenharmony_ci */ 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/kernel.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/regulator/coupler.h> 168c2ecf20Sopenharmony_ci#include <linux/regulator/driver.h> 178c2ecf20Sopenharmony_ci#include <linux/regulator/machine.h> 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_cistatic int regulator_get_optimal_voltage(struct regulator_dev *rdev, 208c2ecf20Sopenharmony_ci int *current_uV, 218c2ecf20Sopenharmony_ci int *min_uV, int *max_uV, 228c2ecf20Sopenharmony_ci suspend_state_t state) 238c2ecf20Sopenharmony_ci{ 248c2ecf20Sopenharmony_ci struct coupling_desc *c_desc = &rdev->coupling_desc; 258c2ecf20Sopenharmony_ci struct regulator_dev **c_rdevs = c_desc->coupled_rdevs; 268c2ecf20Sopenharmony_ci struct regulation_constraints *constraints = rdev->constraints; 278c2ecf20Sopenharmony_ci int desired_min_uV = 0, desired_max_uV = INT_MAX; 288c2ecf20Sopenharmony_ci int max_current_uV = 0, min_current_uV = INT_MAX; 298c2ecf20Sopenharmony_ci int highest_min_uV = 0, target_uV, possible_uV; 308c2ecf20Sopenharmony_ci int i, ret, max_spread, n_coupled = c_desc->n_coupled; 318c2ecf20Sopenharmony_ci bool done; 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci *current_uV = -1; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci /* Find highest min desired voltage */ 368c2ecf20Sopenharmony_ci for (i = 0; i < n_coupled; i++) { 378c2ecf20Sopenharmony_ci int tmp_min = 0; 388c2ecf20Sopenharmony_ci int tmp_max = INT_MAX; 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_ci lockdep_assert_held_once(&c_rdevs[i]->mutex.base); 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci ret = regulator_check_consumers(c_rdevs[i], 438c2ecf20Sopenharmony_ci &tmp_min, 448c2ecf20Sopenharmony_ci &tmp_max, state); 458c2ecf20Sopenharmony_ci if (ret < 0) 468c2ecf20Sopenharmony_ci return ret; 478c2ecf20Sopenharmony_ci 488c2ecf20Sopenharmony_ci if (tmp_min == 0) { 498c2ecf20Sopenharmony_ci ret = regulator_get_voltage_rdev(c_rdevs[i]); 508c2ecf20Sopenharmony_ci if (ret < 0) 518c2ecf20Sopenharmony_ci return ret; 528c2ecf20Sopenharmony_ci tmp_min = ret; 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci /* apply constraints */ 568c2ecf20Sopenharmony_ci ret = regulator_check_voltage(c_rdevs[i], &tmp_min, &tmp_max); 578c2ecf20Sopenharmony_ci if (ret < 0) 588c2ecf20Sopenharmony_ci return ret; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_ci highest_min_uV = max(highest_min_uV, tmp_min); 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_ci if (i == 0) { 638c2ecf20Sopenharmony_ci desired_min_uV = tmp_min; 648c2ecf20Sopenharmony_ci desired_max_uV = tmp_max; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci max_spread = constraints->max_spread[0]; 698c2ecf20Sopenharmony_ci 708c2ecf20Sopenharmony_ci /* 718c2ecf20Sopenharmony_ci * Let target_uV be equal to the desired one if possible. 728c2ecf20Sopenharmony_ci * If not, set it to minimum voltage, allowed by other coupled 738c2ecf20Sopenharmony_ci * regulators. 748c2ecf20Sopenharmony_ci */ 758c2ecf20Sopenharmony_ci target_uV = max(desired_min_uV, highest_min_uV - max_spread); 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci /* 788c2ecf20Sopenharmony_ci * Find min and max voltages, which currently aren't violating 798c2ecf20Sopenharmony_ci * max_spread. 808c2ecf20Sopenharmony_ci */ 818c2ecf20Sopenharmony_ci for (i = 1; i < n_coupled; i++) { 828c2ecf20Sopenharmony_ci int tmp_act; 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci tmp_act = regulator_get_voltage_rdev(c_rdevs[i]); 858c2ecf20Sopenharmony_ci if (tmp_act < 0) 868c2ecf20Sopenharmony_ci return tmp_act; 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci min_current_uV = min(tmp_act, min_current_uV); 898c2ecf20Sopenharmony_ci max_current_uV = max(tmp_act, max_current_uV); 908c2ecf20Sopenharmony_ci } 918c2ecf20Sopenharmony_ci 928c2ecf20Sopenharmony_ci /* 938c2ecf20Sopenharmony_ci * Correct target voltage, so as it currently isn't 948c2ecf20Sopenharmony_ci * violating max_spread 958c2ecf20Sopenharmony_ci */ 968c2ecf20Sopenharmony_ci possible_uV = max(target_uV, max_current_uV - max_spread); 978c2ecf20Sopenharmony_ci possible_uV = min(possible_uV, min_current_uV + max_spread); 988c2ecf20Sopenharmony_ci 998c2ecf20Sopenharmony_ci if (possible_uV > desired_max_uV) 1008c2ecf20Sopenharmony_ci return -EINVAL; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci done = (possible_uV == target_uV); 1038c2ecf20Sopenharmony_ci desired_min_uV = possible_uV; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci /* Set current_uV if wasn't done earlier in the code and if necessary */ 1068c2ecf20Sopenharmony_ci if (*current_uV == -1) { 1078c2ecf20Sopenharmony_ci ret = regulator_get_voltage_rdev(rdev); 1088c2ecf20Sopenharmony_ci if (ret < 0) 1098c2ecf20Sopenharmony_ci return ret; 1108c2ecf20Sopenharmony_ci *current_uV = ret; 1118c2ecf20Sopenharmony_ci } 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci *min_uV = desired_min_uV; 1148c2ecf20Sopenharmony_ci *max_uV = desired_max_uV; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci return done; 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cistatic int exynos_coupler_balance_voltage(struct regulator_coupler *coupler, 1208c2ecf20Sopenharmony_ci struct regulator_dev *rdev, 1218c2ecf20Sopenharmony_ci suspend_state_t state) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci struct regulator_dev **c_rdevs; 1248c2ecf20Sopenharmony_ci struct regulator_dev *best_rdev; 1258c2ecf20Sopenharmony_ci struct coupling_desc *c_desc = &rdev->coupling_desc; 1268c2ecf20Sopenharmony_ci int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev; 1278c2ecf20Sopenharmony_ci unsigned int delta, best_delta; 1288c2ecf20Sopenharmony_ci unsigned long c_rdev_done = 0; 1298c2ecf20Sopenharmony_ci bool best_c_rdev_done; 1308c2ecf20Sopenharmony_ci 1318c2ecf20Sopenharmony_ci c_rdevs = c_desc->coupled_rdevs; 1328c2ecf20Sopenharmony_ci n_coupled = c_desc->n_coupled; 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci /* 1358c2ecf20Sopenharmony_ci * Find the best possible voltage change on each loop. Leave the loop 1368c2ecf20Sopenharmony_ci * if there isn't any possible change. 1378c2ecf20Sopenharmony_ci */ 1388c2ecf20Sopenharmony_ci do { 1398c2ecf20Sopenharmony_ci best_c_rdev_done = false; 1408c2ecf20Sopenharmony_ci best_delta = 0; 1418c2ecf20Sopenharmony_ci best_min_uV = 0; 1428c2ecf20Sopenharmony_ci best_max_uV = 0; 1438c2ecf20Sopenharmony_ci best_c_rdev = 0; 1448c2ecf20Sopenharmony_ci best_rdev = NULL; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci /* 1478c2ecf20Sopenharmony_ci * Find highest difference between optimal voltage 1488c2ecf20Sopenharmony_ci * and current voltage. 1498c2ecf20Sopenharmony_ci */ 1508c2ecf20Sopenharmony_ci for (i = 0; i < n_coupled; i++) { 1518c2ecf20Sopenharmony_ci /* 1528c2ecf20Sopenharmony_ci * optimal_uV is the best voltage that can be set for 1538c2ecf20Sopenharmony_ci * i-th regulator at the moment without violating 1548c2ecf20Sopenharmony_ci * max_spread constraint in order to balance 1558c2ecf20Sopenharmony_ci * the coupled voltages. 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ci int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci if (test_bit(i, &c_rdev_done)) 1608c2ecf20Sopenharmony_ci continue; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci ret = regulator_get_optimal_voltage(c_rdevs[i], 1638c2ecf20Sopenharmony_ci ¤t_uV, 1648c2ecf20Sopenharmony_ci &optimal_uV, 1658c2ecf20Sopenharmony_ci &optimal_max_uV, 1668c2ecf20Sopenharmony_ci state); 1678c2ecf20Sopenharmony_ci if (ret < 0) 1688c2ecf20Sopenharmony_ci goto out; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci delta = abs(optimal_uV - current_uV); 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_ci if (delta && best_delta <= delta) { 1738c2ecf20Sopenharmony_ci best_c_rdev_done = ret; 1748c2ecf20Sopenharmony_ci best_delta = delta; 1758c2ecf20Sopenharmony_ci best_rdev = c_rdevs[i]; 1768c2ecf20Sopenharmony_ci best_min_uV = optimal_uV; 1778c2ecf20Sopenharmony_ci best_max_uV = optimal_max_uV; 1788c2ecf20Sopenharmony_ci best_c_rdev = i; 1798c2ecf20Sopenharmony_ci } 1808c2ecf20Sopenharmony_ci } 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci /* Nothing to change, return successfully */ 1838c2ecf20Sopenharmony_ci if (!best_rdev) { 1848c2ecf20Sopenharmony_ci ret = 0; 1858c2ecf20Sopenharmony_ci goto out; 1868c2ecf20Sopenharmony_ci } 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci ret = regulator_set_voltage_rdev(best_rdev, best_min_uV, 1898c2ecf20Sopenharmony_ci best_max_uV, state); 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci if (ret < 0) 1928c2ecf20Sopenharmony_ci goto out; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci if (best_c_rdev_done) 1958c2ecf20Sopenharmony_ci set_bit(best_c_rdev, &c_rdev_done); 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci } while (n_coupled > 1); 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ciout: 2008c2ecf20Sopenharmony_ci return ret; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int exynos_coupler_attach(struct regulator_coupler *coupler, 2048c2ecf20Sopenharmony_ci struct regulator_dev *rdev) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci return 0; 2078c2ecf20Sopenharmony_ci} 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_cistatic struct regulator_coupler exynos_coupler = { 2108c2ecf20Sopenharmony_ci .attach_regulator = exynos_coupler_attach, 2118c2ecf20Sopenharmony_ci .balance_voltage = exynos_coupler_balance_voltage, 2128c2ecf20Sopenharmony_ci}; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_cistatic int __init exynos_coupler_init(void) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci if (!of_machine_is_compatible("samsung,exynos5800")) 2178c2ecf20Sopenharmony_ci return 0; 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci return regulator_coupler_register(&exynos_coupler); 2208c2ecf20Sopenharmony_ci} 2218c2ecf20Sopenharmony_ciarch_initcall(exynos_coupler_init); 222