|  | // SPDX-License-Identifier: GPL-2.0 | 
|  | /* | 
|  | * helpers to map values in a linear range to range index | 
|  | * | 
|  | * Original idea borrowed from regulator framework | 
|  | * | 
|  | * It might be useful if we could support also inversely proportional ranges? | 
|  | * Copyright 2020 ROHM Semiconductors | 
|  | */ | 
|  |  | 
|  | #include <linux/errno.h> | 
|  | #include <linux/export.h> | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/linear_range.h> | 
|  | #include <linux/module.h> | 
|  |  | 
|  | /** | 
|  | * linear_range_values_in_range - return the amount of values in a range | 
|  | * @r:		pointer to linear range where values are counted | 
|  | * | 
|  | * Compute the amount of values in range pointed by @r. Note, values can | 
|  | * be all equal - range with selectors 0,...,2 with step 0 still contains | 
|  | * 3 values even though they are all equal. | 
|  | * | 
|  | * Return: the amount of values in range pointed by @r | 
|  | */ | 
|  | unsigned int linear_range_values_in_range(const struct linear_range *r) | 
|  | { | 
|  | if (!r) | 
|  | return 0; | 
|  | return r->max_sel - r->min_sel + 1; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(linear_range_values_in_range); | 
|  |  | 
|  | /** | 
|  | * linear_range_values_in_range_array - return the amount of values in ranges | 
|  | * @r:		pointer to array of linear ranges where values are counted | 
|  | * @ranges:	amount of ranges we include in computation. | 
|  | * | 
|  | * Compute the amount of values in ranges pointed by @r. Note, values can | 
|  | * be all equal - range with selectors 0,...,2 with step 0 still contains | 
|  | * 3 values even though they are all equal. | 
|  | * | 
|  | * Return: the amount of values in first @ranges ranges pointed by @r | 
|  | */ | 
|  | unsigned int linear_range_values_in_range_array(const struct linear_range *r, | 
|  | int ranges) | 
|  | { | 
|  | int i, values_in_range = 0; | 
|  |  | 
|  | for (i = 0; i < ranges; i++) { | 
|  | int values; | 
|  |  | 
|  | values = linear_range_values_in_range(&r[i]); | 
|  | if (!values) | 
|  | return values; | 
|  |  | 
|  | values_in_range += values; | 
|  | } | 
|  | return values_in_range; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(linear_range_values_in_range_array); | 
|  |  | 
|  | /** | 
|  | * linear_range_get_max_value - return the largest value in a range | 
|  | * @r:		pointer to linear range where value is looked from | 
|  | * | 
|  | * Return: the largest value in the given range | 
|  | */ | 
|  | unsigned int linear_range_get_max_value(const struct linear_range *r) | 
|  | { | 
|  | return r->min + (r->max_sel - r->min_sel) * r->step; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(linear_range_get_max_value); | 
|  |  | 
|  | /** | 
|  | * linear_range_get_value - fetch a value from given range | 
|  | * @r:		pointer to linear range where value is looked from | 
|  | * @selector:	selector for which the value is searched | 
|  | * @val:	address where found value is updated | 
|  | * | 
|  | * Search given ranges for value which matches given selector. | 
|  | * | 
|  | * Return: 0 on success, -EINVAL given selector is not found from any of the | 
|  | * ranges. | 
|  | */ | 
|  | int linear_range_get_value(const struct linear_range *r, unsigned int selector, | 
|  | unsigned int *val) | 
|  | { | 
|  | if (r->min_sel > selector || r->max_sel < selector) | 
|  | return -EINVAL; | 
|  |  | 
|  | *val = r->min + (selector - r->min_sel) * r->step; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(linear_range_get_value); | 
|  |  | 
|  | /** | 
|  | * linear_range_get_value_array - fetch a value from array of ranges | 
|  | * @r:		pointer to array of linear ranges where value is looked from | 
|  | * @ranges:	amount of ranges in an array | 
|  | * @selector:	selector for which the value is searched | 
|  | * @val:	address where found value is updated | 
|  | * | 
|  | * Search through an array of ranges for value which matches given selector. | 
|  | * | 
|  | * Return: 0 on success, -EINVAL given selector is not found from any of the | 
|  | * ranges. | 
|  | */ | 
|  | int linear_range_get_value_array(const struct linear_range *r, int ranges, | 
|  | unsigned int selector, unsigned int *val) | 
|  | { | 
|  | int i; | 
|  |  | 
|  | for (i = 0; i < ranges; i++) | 
|  | if (r[i].min_sel <= selector && r[i].max_sel >= selector) | 
|  | return linear_range_get_value(&r[i], selector, val); | 
|  |  | 
|  | return -EINVAL; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(linear_range_get_value_array); | 
|  |  | 
|  | /** | 
|  | * linear_range_get_selector_low - return linear range selector for value | 
|  | * @r:		pointer to linear range where selector is looked from | 
|  | * @val:	value for which the selector is searched | 
|  | * @selector:	address where found selector value is updated | 
|  | * @found:	flag to indicate that given value was in the range | 
|  | * | 
|  | * Return selector for which range value is closest match for given | 
|  | * input value. Value is matching if it is equal or smaller than given | 
|  | * value. If given value is in the range, then @found is set true. | 
|  | * | 
|  | * Return: 0 on success, -EINVAL if range is invalid or does not contain | 
|  | * value smaller or equal to given value | 
|  | */ | 
|  | int linear_range_get_selector_low(const struct linear_range *r, | 
|  | unsigned int val, unsigned int *selector, | 
|  | bool *found) | 
|  | { | 
|  | *found = false; | 
|  |  | 
|  | if (r->min > val) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (linear_range_get_max_value(r) < val) { | 
|  | *selector = r->max_sel; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | *found = true; | 
|  |  | 
|  | if (r->step == 0) | 
|  | *selector = r->min_sel; | 
|  | else | 
|  | *selector = (val - r->min) / r->step + r->min_sel; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(linear_range_get_selector_low); | 
|  |  | 
|  | /** | 
|  | * linear_range_get_selector_low_array - return linear range selector for value | 
|  | * @r:		pointer to array of linear ranges where selector is looked from | 
|  | * @ranges:	amount of ranges to scan from array | 
|  | * @val:	value for which the selector is searched | 
|  | * @selector:	address where found selector value is updated | 
|  | * @found:	flag to indicate that given value was in the range | 
|  | * | 
|  | * Scan array of ranges for selector for which range value matches given | 
|  | * input value. Value is matching if it is equal or smaller than given | 
|  | * value. If given value is found to be in a range scanning is stopped and | 
|  | * @found is set true. If a range with values smaller than given value is found | 
|  | * but the range max is being smaller than given value, then the range's | 
|  | * biggest selector is updated to @selector but scanning ranges is continued | 
|  | * and @found is set to false. | 
|  | * | 
|  | * Return: 0 on success, -EINVAL if range array is invalid or does not contain | 
|  | * range with a value smaller or equal to given value | 
|  | */ | 
|  | int linear_range_get_selector_low_array(const struct linear_range *r, | 
|  | int ranges, unsigned int val, | 
|  | unsigned int *selector, bool *found) | 
|  | { | 
|  | int i; | 
|  | int ret = -EINVAL; | 
|  |  | 
|  | for (i = 0; i < ranges; i++) { | 
|  | int tmpret; | 
|  |  | 
|  | tmpret = linear_range_get_selector_low(&r[i], val, selector, | 
|  | found); | 
|  | if (!tmpret) | 
|  | ret = 0; | 
|  |  | 
|  | if (*found) | 
|  | break; | 
|  | } | 
|  |  | 
|  | return ret; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(linear_range_get_selector_low_array); | 
|  |  | 
|  | /** | 
|  | * linear_range_get_selector_high - return linear range selector for value | 
|  | * @r:		pointer to linear range where selector is looked from | 
|  | * @val:	value for which the selector is searched | 
|  | * @selector:	address where found selector value is updated | 
|  | * @found:	flag to indicate that given value was in the range | 
|  | * | 
|  | * Return selector for which range value is closest match for given | 
|  | * input value. Value is matching if it is equal or higher than given | 
|  | * value. If given value is in the range, then @found is set true. | 
|  | * | 
|  | * Return: 0 on success, -EINVAL if range is invalid or does not contain | 
|  | * value greater or equal to given value | 
|  | */ | 
|  | int linear_range_get_selector_high(const struct linear_range *r, | 
|  | unsigned int val, unsigned int *selector, | 
|  | bool *found) | 
|  | { | 
|  | *found = false; | 
|  |  | 
|  | if (linear_range_get_max_value(r) < val) | 
|  | return -EINVAL; | 
|  |  | 
|  | if (r->min > val) { | 
|  | *selector = r->min_sel; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | *found = true; | 
|  |  | 
|  | if (r->step == 0) | 
|  | *selector = r->max_sel; | 
|  | else | 
|  | *selector = DIV_ROUND_UP(val - r->min, r->step) + r->min_sel; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(linear_range_get_selector_high); | 
|  |  | 
|  | /** | 
|  | * linear_range_get_selector_within - return linear range selector for value | 
|  | * @r:		pointer to linear range where selector is looked from | 
|  | * @val:	value for which the selector is searched | 
|  | * @selector:	address where found selector value is updated | 
|  | * | 
|  | * Return selector for which range value is closest match for given | 
|  | * input value. Value is matching if it is equal or lower than given | 
|  | * value. But return maximum selector if given value is higher than | 
|  | * maximum value. | 
|  | */ | 
|  | void linear_range_get_selector_within(const struct linear_range *r, | 
|  | unsigned int val, unsigned int *selector) | 
|  | { | 
|  | if (r->min > val) { | 
|  | *selector = r->min_sel; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (linear_range_get_max_value(r) < val) { | 
|  | *selector = r->max_sel; | 
|  | return; | 
|  | } | 
|  |  | 
|  | if (r->step == 0) | 
|  | *selector = r->min_sel; | 
|  | else | 
|  | *selector = (val - r->min) / r->step + r->min_sel; | 
|  | } | 
|  | EXPORT_SYMBOL_GPL(linear_range_get_selector_within); | 
|  |  | 
|  | MODULE_DESCRIPTION("linear-ranges helper"); | 
|  | MODULE_LICENSE("GPL"); |