xref: /linux/drivers/soc/samsung/exynos-regulator-coupler.c (revision 19d0070a2792181f79df01277fe00b83b9f7eda7)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
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.c
8  * The main difference is that it keeps current regulator voltage
9  * 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 
19 static 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 coupled
73 	 * 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 violating
79 	 * 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't
94 	 * violating max_spread
95 	 */
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 
119 static 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 loop
136 	 * 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 voltage
148 		 * and current voltage.
149 		 */
150 		for (i = 0; i < n_coupled; i++) {
151 			/*
152 			 * optimal_uV is the best voltage that can be set for
153 			 * i-th regulator at the moment without violating
154 			 * max_spread constraint in order to balance
155 			 * 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 
199 out:
200 	return ret;
201 }
202 
203 static int exynos_coupler_attach(struct regulator_coupler *coupler,
204 				 struct regulator_dev *rdev)
205 {
206 	return 0;
207 }
208 
209 static struct regulator_coupler exynos_coupler = {
210 	.attach_regulator = exynos_coupler_attach,
211 	.balance_voltage  = exynos_coupler_balance_voltage,
212 };
213 
214 static 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 }
221 arch_initcall(exynos_coupler_init);
222