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							    &current_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