Merge tag 'drm-misc-fixes-2020-08-12' of git://anongit.freedesktop.org/drm/drm-misc...
[linux-2.6-microblaze.git] / drivers / soc / samsung / exynos-regulator-coupler.c
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);