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