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> 15*35e65600SMatti 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 */ 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 */ 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 */ 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 */ 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 */ 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 * 131d2218d4eSMatti Vaittinen * Return selector which 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 */ 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 * 171d2218d4eSMatti Vaittinen * Scan array of ranges for selector which 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 175d2218d4eSMatti Vaittinen * but the range max is being smaller than given value, then the ranges 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 */ 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 * 212d2218d4eSMatti Vaittinen * Return selector which 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 */ 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); 243*35e65600SMatti Vaittinen 244*35e65600SMatti Vaittinen MODULE_DESCRIPTION("linear-ranges helper"); 245*35e65600SMatti Vaittinen MODULE_LICENSE("GPL"); 246