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