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 */
linear_range_values_in_range(const struct linear_range * r)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 */
linear_range_values_in_range_array(const struct linear_range * r,int ranges)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 */
linear_range_get_max_value(const struct linear_range * r)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 */
linear_range_get_value(const struct linear_range * r,unsigned int selector,unsigned int * val)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 */
linear_range_get_value_array(const struct linear_range * r,int ranges,unsigned int selector,unsigned int * val)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 */
linear_range_get_selector_low(const struct linear_range * r,unsigned int val,unsigned int * selector,bool * found)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 */
linear_range_get_selector_low_array(const struct linear_range * r,int ranges,unsigned int val,unsigned int * selector,bool * found)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 */
linear_range_get_selector_high(const struct linear_range * r,unsigned int val,unsigned int * selector,bool * found)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 */
linear_range_get_selector_within(const struct linear_range * r,unsigned int val,unsigned int * selector)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");
277