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" ); |
277 | |