Skip to content

Commit d2218d4

Browse files
M-Vaittinenbroonie
authored andcommitted
lib: add linear ranges helpers
Many devices have control registers which control some measurable property. Often a register contains control field so that change in this field causes linear change in the controlled property. It is not a rare case that user wants to give 'meaningful' control values and driver needs to convert them to register field values. Even more often user wants to 'see' the currently set value - again in meaningful units - and driver needs to convert the values it reads from register to these meaningful units. Examples of this include: - regulators, voltage/current configurations - power, voltage/current configurations - clk(?) NCOs and maybe others I can't think of right now. Provide a linear_range helper which can do conversion from user value to register value 'selector'. The idea here is stolen from regulator framework and patches refactoring the regulator helpers to use this are following. Current implementation does not support inversely proportional ranges but it might be useful if we could support also inversely proportional ranges? Signed-off-by: Matti Vaittinen <[email protected]> Reviewed-by: Mark Brown <[email protected]> Reviewed-by: Linus Walleij <[email protected]> Reviewed-by: Andy Shevchenko <[email protected]> Link: https://lore.kernel.org/r/59259bc475e0c800eb4bb163f02528c7c01f7b3a.1588944082.git.matti.vaittinen@fi.rohmeurope.com Signed-off-by: Mark Brown <[email protected]>
1 parent 0e698df commit d2218d4

File tree

4 files changed

+293
-0
lines changed

4 files changed

+293
-0
lines changed

include/linux/linear_range.h

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/* Copyright (C) 2020 ROHM Semiconductors */
3+
4+
#ifndef LINEAR_RANGE_H
5+
#define LINEAR_RANGE_H
6+
7+
#include <linux/types.h>
8+
9+
/**
10+
* struct linear_range - table of selector - value pairs
11+
*
12+
* Define a lookup-table for range of values. Intended to help when looking
13+
* for a register value matching certaing physical measure (like voltage).
14+
* Usable when increment of one in register always results a constant increment
15+
* of the physical measure (like voltage).
16+
*
17+
* @min: Lowest value in range
18+
* @min_sel: Lowest selector for range
19+
* @max_sel: Highest selector for range
20+
* @step: Value step size
21+
*/
22+
struct linear_range {
23+
unsigned int min;
24+
unsigned int min_sel;
25+
unsigned int max_sel;
26+
unsigned int step;
27+
};
28+
29+
unsigned int linear_range_values_in_range(const struct linear_range *r);
30+
unsigned int linear_range_values_in_range_array(const struct linear_range *r,
31+
int ranges);
32+
unsigned int linear_range_get_max_value(const struct linear_range *r);
33+
34+
int linear_range_get_value(const struct linear_range *r, unsigned int selector,
35+
unsigned int *val);
36+
int linear_range_get_value_array(const struct linear_range *r, int ranges,
37+
unsigned int selector, unsigned int *val);
38+
int linear_range_get_selector_low(const struct linear_range *r,
39+
unsigned int val, unsigned int *selector,
40+
bool *found);
41+
int linear_range_get_selector_high(const struct linear_range *r,
42+
unsigned int val, unsigned int *selector,
43+
bool *found);
44+
int linear_range_get_selector_low_array(const struct linear_range *r,
45+
int ranges, unsigned int val,
46+
unsigned int *selector, bool *found);
47+
48+
#endif

lib/Kconfig

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ config RAID6_PQ_BENCHMARK
1919
Benchmark all available RAID6 PQ functions on init and choose the
2020
fastest one.
2121

22+
config LINEAR_RANGES
23+
tristate
24+
2225
config PACKING
2326
bool "Generic bitfield packing and unpacking"
2427
default n

lib/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,7 @@ obj-$(CONFIG_DEBUG_LIST) += list_debug.o
125125
obj-$(CONFIG_DEBUG_OBJECTS) += debugobjects.o
126126

127127
obj-$(CONFIG_BITREVERSE) += bitrev.o
128+
obj-$(CONFIG_LINEAR_RANGES) += linear_ranges.o
128129
obj-$(CONFIG_PACKING) += packing.o
129130
obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o
130131
obj-$(CONFIG_CRC16) += crc16.o

lib/linear_ranges.c

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
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);

0 commit comments

Comments
 (0)