Merge tag 'for-linus-5.15-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml
[platform/kernel/linux-rpi.git] / lib / linear_ranges.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * helpers to map values in a linear range to range index
4  *
5  * Original idea borrowed from regulator framework
6  *
7  * It might be useful if we could support also inversely proportional ranges?
8  * Copyright 2020 ROHM Semiconductors
9  */
10
11 #include <linux/errno.h>
12 #include <linux/export.h>
13 #include <linux/kernel.h>
14 #include <linux/linear_range.h>
15 #include <linux/module.h>
16
17 /**
18  * linear_range_values_in_range - return the amount of values in a range
19  * @r:          pointer to linear range where values are counted
20  *
21  * Compute the amount of values in range pointed by @r. Note, values can
22  * be all equal - range with selectors 0,...,2 with step 0 still contains
23  * 3 values even though they are all equal.
24  *
25  * Return: the amount of values in range pointed by @r
26  */
27 unsigned int linear_range_values_in_range(const struct linear_range *r)
28 {
29         if (!r)
30                 return 0;
31         return r->max_sel - r->min_sel + 1;
32 }
33 EXPORT_SYMBOL_GPL(linear_range_values_in_range);
34
35 /**
36  * linear_range_values_in_range_array - return the amount of values in ranges
37  * @r:          pointer to array of linear ranges where values are counted
38  * @ranges:     amount of ranges we include in computation.
39  *
40  * Compute the amount of values in ranges pointed by @r. Note, values can
41  * be all equal - range with selectors 0,...,2 with step 0 still contains
42  * 3 values even though they are all equal.
43  *
44  * Return: the amount of values in first @ranges ranges pointed by @r
45  */
46 unsigned int linear_range_values_in_range_array(const struct linear_range *r,
47                                                 int ranges)
48 {
49         int i, values_in_range = 0;
50
51         for (i = 0; i < ranges; i++) {
52                 int values;
53
54                 values = linear_range_values_in_range(&r[i]);
55                 if (!values)
56                         return values;
57
58                 values_in_range += values;
59         }
60         return values_in_range;
61 }
62 EXPORT_SYMBOL_GPL(linear_range_values_in_range_array);
63
64 /**
65  * linear_range_get_max_value - return the largest value in a range
66  * @r:          pointer to linear range where value is looked from
67  *
68  * Return: the largest value in the given range
69  */
70 unsigned int linear_range_get_max_value(const struct linear_range *r)
71 {
72         return r->min + (r->max_sel - r->min_sel) * r->step;
73 }
74 EXPORT_SYMBOL_GPL(linear_range_get_max_value);
75
76 /**
77  * linear_range_get_value - fetch a value from given range
78  * @r:          pointer to linear range where value is looked from
79  * @selector:   selector for which the value is searched
80  * @val:        address where found value is updated
81  *
82  * Search given ranges for value which matches given selector.
83  *
84  * Return: 0 on success, -EINVAL given selector is not found from any of the
85  * ranges.
86  */
87 int linear_range_get_value(const struct linear_range *r, unsigned int selector,
88                            unsigned int *val)
89 {
90         if (r->min_sel > selector || r->max_sel < selector)
91                 return -EINVAL;
92
93         *val = r->min + (selector - r->min_sel) * r->step;
94
95         return 0;
96 }
97 EXPORT_SYMBOL_GPL(linear_range_get_value);
98
99 /**
100  * linear_range_get_value_array - fetch a value from array of ranges
101  * @r:          pointer to array of linear ranges where value is looked from
102  * @ranges:     amount of ranges in an array
103  * @selector:   selector for which the value is searched
104  * @val:        address where found value is updated
105  *
106  * Search through an array of ranges for value which matches given selector.
107  *
108  * Return: 0 on success, -EINVAL given selector is not found from any of the
109  * ranges.
110  */
111 int linear_range_get_value_array(const struct linear_range *r, int ranges,
112                                  unsigned int selector, unsigned int *val)
113 {
114         int i;
115
116         for (i = 0; i < ranges; i++)
117                 if (r[i].min_sel <= selector && r[i].max_sel >= selector)
118                         return linear_range_get_value(&r[i], selector, val);
119
120         return -EINVAL;
121 }
122 EXPORT_SYMBOL_GPL(linear_range_get_value_array);
123
124 /**
125  * linear_range_get_selector_low - return linear range selector for value
126  * @r:          pointer to linear range where selector is looked from
127  * @val:        value for which the selector is searched
128  * @selector:   address where found selector value is updated
129  * @found:      flag to indicate that given value was in the range
130  *
131  * Return selector for which range value is closest match for given
132  * input value. Value is matching if it is equal or smaller than given
133  * value. If given value is in the range, then @found is set true.
134  *
135  * Return: 0 on success, -EINVAL if range is invalid or does not contain
136  * value smaller or equal to given value
137  */
138 int linear_range_get_selector_low(const struct linear_range *r,
139                                   unsigned int val, unsigned int *selector,
140                                   bool *found)
141 {
142         *found = false;
143
144         if (r->min > val)
145                 return -EINVAL;
146
147         if (linear_range_get_max_value(r) < val) {
148                 *selector = r->max_sel;
149                 return 0;
150         }
151
152         *found = true;
153
154         if (r->step == 0)
155                 *selector = r->min_sel;
156         else
157                 *selector = (val - r->min) / r->step + r->min_sel;
158
159         return 0;
160 }
161 EXPORT_SYMBOL_GPL(linear_range_get_selector_low);
162
163 /**
164  * linear_range_get_selector_low_array - return linear range selector for value
165  * @r:          pointer to array of linear ranges where selector is looked from
166  * @ranges:     amount of ranges to scan from array
167  * @val:        value for which the selector is searched
168  * @selector:   address where found selector value is updated
169  * @found:      flag to indicate that given value was in the range
170  *
171  * Scan array of ranges for selector for which range value matches given
172  * input value. Value is matching if it is equal or smaller than given
173  * value. If given value is found to be in a range scanning is stopped and
174  * @found is set true. If a range with values smaller than given value is found
175  * but the range max is being smaller than given value, then the range's
176  * biggest selector is updated to @selector but scanning ranges is continued
177  * and @found is set to false.
178  *
179  * Return: 0 on success, -EINVAL if range array is invalid or does not contain
180  * range with a value smaller or equal to given value
181  */
182 int linear_range_get_selector_low_array(const struct linear_range *r,
183                                         int ranges, unsigned int val,
184                                         unsigned int *selector, bool *found)
185 {
186         int i;
187         int ret = -EINVAL;
188
189         for (i = 0; i < ranges; i++) {
190                 int tmpret;
191
192                 tmpret = linear_range_get_selector_low(&r[i], val, selector,
193                                                        found);
194                 if (!tmpret)
195                         ret = 0;
196
197                 if (*found)
198                         break;
199         }
200
201         return ret;
202 }
203 EXPORT_SYMBOL_GPL(linear_range_get_selector_low_array);
204
205 /**
206  * linear_range_get_selector_high - return linear range selector for value
207  * @r:          pointer to linear range where selector is looked from
208  * @val:        value for which the selector is searched
209  * @selector:   address where found selector value is updated
210  * @found:      flag to indicate that given value was in the range
211  *
212  * Return selector for which range value is closest match for given
213  * input value. Value is matching if it is equal or higher than given
214  * value. If given value is in the range, then @found is set true.
215  *
216  * Return: 0 on success, -EINVAL if range is invalid or does not contain
217  * value greater or equal to given value
218  */
219 int linear_range_get_selector_high(const struct linear_range *r,
220                                    unsigned int val, unsigned int *selector,
221                                    bool *found)
222 {
223         *found = false;
224
225         if (linear_range_get_max_value(r) < val)
226                 return -EINVAL;
227
228         if (r->min > val) {
229                 *selector = r->min_sel;
230                 return 0;
231         }
232
233         *found = true;
234
235         if (r->step == 0)
236                 *selector = r->max_sel;
237         else
238                 *selector = DIV_ROUND_UP(val - r->min, r->step) + r->min_sel;
239
240         return 0;
241 }
242 EXPORT_SYMBOL_GPL(linear_range_get_selector_high);
243
244 /**
245  * linear_range_get_selector_within - return linear range selector for value
246  * @r:          pointer to linear range where selector is looked from
247  * @val:        value for which the selector is searched
248  * @selector:   address where found selector value is updated
249  *
250  * Return selector for which range value is closest match for given
251  * input value. Value is matching if it is equal or lower than given
252  * value. But return maximum selector if given value is higher than
253  * maximum value.
254  */
255 void linear_range_get_selector_within(const struct linear_range *r,
256                                       unsigned int val, unsigned int *selector)
257 {
258         if (r->min > val) {
259                 *selector = r->min_sel;
260                 return;
261         }
262
263         if (linear_range_get_max_value(r) < val) {
264                 *selector = r->max_sel;
265                 return;
266         }
267
268         if (r->step == 0)
269                 *selector = r->min_sel;
270         else
271                 *selector = (val - r->min) / r->step + r->min_sel;
272 }
273 EXPORT_SYMBOL_GPL(linear_range_get_selector_within);
274
275 MODULE_DESCRIPTION("linear-ranges helper");
276 MODULE_LICENSE("GPL");