xref: /linux/lib/linear_ranges.c (revision 29e9eff40f5edc2e5de63b28e700e82ed2b6b95c)
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 
16 /**
17  * linear_range_values_in_range - return the amount of values in a range
18  * @r:		pointer to linear range where values are counted
19  *
20  * Compute the amount of values in range pointed by @r. Note, values can
21  * be all equal - range with selectors 0,...,2 with step 0 still contains
22  * 3 values even though they are all equal.
23  *
24  * Return: the amount of values in range pointed by @r
25  */
26 unsigned int linear_range_values_in_range(const struct linear_range *r)
27 {
28 	if (!r)
29 		return 0;
30 	return r->max_sel - r->min_sel + 1;
31 }
32 EXPORT_SYMBOL_GPL(linear_range_values_in_range);
33 
34 /**
35  * linear_range_values_in_range_array - return the amount of values in ranges
36  * @r:		pointer to array of linear ranges where values are counted
37  * @ranges:	amount of ranges we include in computation.
38  *
39  * Compute the amount of values in ranges pointed by @r. Note, values can
40  * be all equal - range with selectors 0,...,2 with step 0 still contains
41  * 3 values even though they are all equal.
42  *
43  * Return: the amount of values in first @ranges ranges pointed by @r
44  */
45 unsigned int linear_range_values_in_range_array(const struct linear_range *r,
46 						int ranges)
47 {
48 	int i, values_in_range = 0;
49 
50 	for (i = 0; i < ranges; i++) {
51 		int values;
52 
53 		values = linear_range_values_in_range(&r[i]);
54 		if (!values)
55 			return values;
56 
57 		values_in_range += values;
58 	}
59 	return values_in_range;
60 }
61 EXPORT_SYMBOL_GPL(linear_range_values_in_range_array);
62 
63 /**
64  * linear_range_get_max_value - return the largest value in a range
65  * @r:		pointer to linear range where value is looked from
66  *
67  * Return: the largest value in the given range
68  */
69 unsigned int linear_range_get_max_value(const struct linear_range *r)
70 {
71 	return r->min + (r->max_sel - r->min_sel) * r->step;
72 }
73 EXPORT_SYMBOL_GPL(linear_range_get_max_value);
74 
75 /**
76  * linear_range_get_value - fetch a value from given range
77  * @r:		pointer to linear range where value is looked from
78  * @selector:	selector for which the value is searched
79  * @val:	address where found value is updated
80  *
81  * Search given ranges for value which matches given selector.
82  *
83  * Return: 0 on success, -EINVAL given selector is not found from any of the
84  * ranges.
85  */
86 int linear_range_get_value(const struct linear_range *r, unsigned int selector,
87 			   unsigned int *val)
88 {
89 	if (r->min_sel > selector || r->max_sel < selector)
90 		return -EINVAL;
91 
92 	*val = r->min + (selector - r->min_sel) * r->step;
93 
94 	return 0;
95 }
96 EXPORT_SYMBOL_GPL(linear_range_get_value);
97 
98 /**
99  * linear_range_get_value_array - fetch a value from array of ranges
100  * @r:		pointer to array of linear ranges where value is looked from
101  * @ranges:	amount of ranges in an array
102  * @selector:	selector for which the value is searched
103  * @val:	address where found value is updated
104  *
105  * Search through an array of ranges for value which matches given selector.
106  *
107  * Return: 0 on success, -EINVAL given selector is not found from any of the
108  * ranges.
109  */
110 int linear_range_get_value_array(const struct linear_range *r, int ranges,
111 				 unsigned int selector, unsigned int *val)
112 {
113 	int i;
114 
115 	for (i = 0; i < ranges; i++)
116 		if (r[i].min_sel <= selector && r[i].max_sel >= selector)
117 			return linear_range_get_value(&r[i], selector, val);
118 
119 	return -EINVAL;
120 }
121 EXPORT_SYMBOL_GPL(linear_range_get_value_array);
122 
123 /**
124  * linear_range_get_selector_low - return linear range selector for value
125  * @r:		pointer to linear range where selector is looked from
126  * @val:	value for which the selector is searched
127  * @selector:	address where found selector value is updated
128  * @found:	flag to indicate that given value was in the range
129  *
130  * Return selector which which range value is closest match for given
131  * input value. Value is matching if it is equal or smaller than given
132  * value. If given value is in the range, then @found is set true.
133  *
134  * Return: 0 on success, -EINVAL if range is invalid or does not contain
135  * value smaller or equal to given value
136  */
137 int linear_range_get_selector_low(const struct linear_range *r,
138 				  unsigned int val, unsigned int *selector,
139 				  bool *found)
140 {
141 	*found = false;
142 
143 	if (r->min > val)
144 		return -EINVAL;
145 
146 	if (linear_range_get_max_value(r) < val) {
147 		*selector = r->max_sel;
148 		return 0;
149 	}
150 
151 	*found = true;
152 
153 	if (r->step == 0)
154 		*selector = r->min_sel;
155 	else
156 		*selector = (val - r->min) / r->step + r->min_sel;
157 
158 	return 0;
159 }
160 EXPORT_SYMBOL_GPL(linear_range_get_selector_low);
161 
162 /**
163  * linear_range_get_selector_low_array - return linear range selector for value
164  * @r:		pointer to array of linear ranges where selector is looked from
165  * @ranges:	amount of ranges to scan from array
166  * @val:	value for which the selector is searched
167  * @selector:	address where found selector value is updated
168  * @found:	flag to indicate that given value was in the range
169  *
170  * Scan array of ranges for selector which which range value matches given
171  * input value. Value is matching if it is equal or smaller than given
172  * value. If given value is found to be in a range scanning is stopped and
173  * @found is set true. If a range with values smaller than given value is found
174  * but the range max is being smaller than given value, then the ranges
175  * biggest selector is updated to @selector but scanning ranges is continued
176  * and @found is set to false.
177  *
178  * Return: 0 on success, -EINVAL if range array is invalid or does not contain
179  * range with a value smaller or equal to given value
180  */
181 int linear_range_get_selector_low_array(const struct linear_range *r,
182 					int ranges, unsigned int val,
183 					unsigned int *selector, bool *found)
184 {
185 	int i;
186 	int ret = -EINVAL;
187 
188 	for (i = 0; i < ranges; i++) {
189 		int tmpret;
190 
191 		tmpret = linear_range_get_selector_low(&r[i], val, selector,
192 						       found);
193 		if (!tmpret)
194 			ret = 0;
195 
196 		if (*found)
197 			break;
198 	}
199 
200 	return ret;
201 }
202 EXPORT_SYMBOL_GPL(linear_range_get_selector_low_array);
203 
204 /**
205  * linear_range_get_selector_high - return linear range selector for value
206  * @r:		pointer to linear range where selector is looked from
207  * @val:	value for which the selector is searched
208  * @selector:	address where found selector value is updated
209  * @found:	flag to indicate that given value was in the range
210  *
211  * Return selector which which range value is closest match for given
212  * input value. Value is matching if it is equal or higher than given
213  * value. If given value is in the range, then @found is set true.
214  *
215  * Return: 0 on success, -EINVAL if range is invalid or does not contain
216  * value greater or equal to given value
217  */
218 int linear_range_get_selector_high(const struct linear_range *r,
219 				   unsigned int val, unsigned int *selector,
220 				   bool *found)
221 {
222 	*found = false;
223 
224 	if (linear_range_get_max_value(r) < val)
225 		return -EINVAL;
226 
227 	if (r->min > val) {
228 		*selector = r->min_sel;
229 		return 0;
230 	}
231 
232 	*found = true;
233 
234 	if (r->step == 0)
235 		*selector = r->max_sel;
236 	else
237 		*selector = DIV_ROUND_UP(val - r->min, r->step) + r->min_sel;
238 
239 	return 0;
240 }
241 EXPORT_SYMBOL_GPL(linear_range_get_selector_high);
242