brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · 61a156b Raw
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							    &current_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