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); 242