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