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 #include <linux/module.h> 16 17 /** 18 * linear_range_values_in_range - return the amount of values in a range 19 * @r: pointer to linear range where values are counted 20 * 21 * Compute the amount of values in range pointed by @r. Note, values can 22 * be all equal - range with selectors 0,...,2 with step 0 still contains 23 * 3 values even though they are all equal. 24 * 25 * Return: the amount of values in range pointed by @r 26 */ 27 unsigned int linear_range_values_in_range(const struct linear_range *r) 28 { 29 if (!r) 30 return 0; 31 return r->max_sel - r->min_sel + 1; 32 } 33 EXPORT_SYMBOL_GPL(linear_range_values_in_range); 34 35 /** 36 * linear_range_values_in_range_array - return the amount of values in ranges 37 * @r: pointer to array of linear ranges where values are counted 38 * @ranges: amount of ranges we include in computation. 39 * 40 * Compute the amount of values in ranges pointed by @r. Note, values can 41 * be all equal - range with selectors 0,...,2 with step 0 still contains 42 * 3 values even though they are all equal. 43 * 44 * Return: the amount of values in first @ranges ranges pointed by @r 45 */ 46 unsigned int linear_range_values_in_range_array(const struct linear_range *r, 47 int ranges) 48 { 49 int i, values_in_range = 0; 50 51 for (i = 0; i < ranges; i++) { 52 int values; 53 54 values = linear_range_values_in_range(&r[i]); 55 if (!values) 56 return values; 57 58 values_in_range += values; 59 } 60 return values_in_range; 61 } 62 EXPORT_SYMBOL_GPL(linear_range_values_in_range_array); 63 64 /** 65 * linear_range_get_max_value - return the largest value in a range 66 * @r: pointer to linear range where value is looked from 67 * 68 * Return: the largest value in the given range 69 */ 70 unsigned int linear_range_get_max_value(const struct linear_range *r) 71 { 72 return r->min + (r->max_sel - r->min_sel) * r->step; 73 } 74 EXPORT_SYMBOL_GPL(linear_range_get_max_value); 75 76 /** 77 * linear_range_get_value - fetch a value from given range 78 * @r: pointer to linear range where value is looked from 79 * @selector: selector for which the value is searched 80 * @val: address where found value is updated 81 * 82 * Search given ranges for value which matches given selector. 83 * 84 * Return: 0 on success, -EINVAL given selector is not found from any of the 85 * ranges. 86 */ 87 int linear_range_get_value(const struct linear_range *r, unsigned int selector, 88 unsigned int *val) 89 { 90 if (r->min_sel > selector || r->max_sel < selector) 91 return -EINVAL; 92 93 *val = r->min + (selector - r->min_sel) * r->step; 94 95 return 0; 96 } 97 EXPORT_SYMBOL_GPL(linear_range_get_value); 98 99 /** 100 * linear_range_get_value_array - fetch a value from array of ranges 101 * @r: pointer to array of linear ranges where value is looked from 102 * @ranges: amount of ranges in an array 103 * @selector: selector for which the value is searched 104 * @val: address where found value is updated 105 * 106 * Search through an array of ranges for value which matches given selector. 107 * 108 * Return: 0 on success, -EINVAL given selector is not found from any of the 109 * ranges. 110 */ 111 int linear_range_get_value_array(const struct linear_range *r, int ranges, 112 unsigned int selector, unsigned int *val) 113 { 114 int i; 115 116 for (i = 0; i < ranges; i++) 117 if (r[i].min_sel <= selector && r[i].max_sel >= selector) 118 return linear_range_get_value(&r[i], selector, val); 119 120 return -EINVAL; 121 } 122 EXPORT_SYMBOL_GPL(linear_range_get_value_array); 123 124 /** 125 * linear_range_get_selector_low - return linear range selector for value 126 * @r: pointer to linear range where selector is looked from 127 * @val: value for which the selector is searched 128 * @selector: address where found selector value is updated 129 * @found: flag to indicate that given value was in the range 130 * 131 * Return selector for which range value is closest match for given 132 * input value. Value is matching if it is equal or smaller than given 133 * value. If given value is in the range, then @found is set true. 134 * 135 * Return: 0 on success, -EINVAL if range is invalid or does not contain 136 * value smaller or equal to given value 137 */ 138 int linear_range_get_selector_low(const struct linear_range *r, 139 unsigned int val, unsigned int *selector, 140 bool *found) 141 { 142 *found = false; 143 144 if (r->min > val) 145 return -EINVAL; 146 147 if (linear_range_get_max_value(r) < val) { 148 *selector = r->max_sel; 149 return 0; 150 } 151 152 *found = true; 153 154 if (r->step == 0) 155 *selector = r->min_sel; 156 else 157 *selector = (val - r->min) / r->step + r->min_sel; 158 159 return 0; 160 } 161 EXPORT_SYMBOL_GPL(linear_range_get_selector_low); 162 163 /** 164 * linear_range_get_selector_low_array - return linear range selector for value 165 * @r: pointer to array of linear ranges where selector is looked from 166 * @ranges: amount of ranges to scan from array 167 * @val: value for which the selector is searched 168 * @selector: address where found selector value is updated 169 * @found: flag to indicate that given value was in the range 170 * 171 * Scan array of ranges for selector for which range value matches given 172 * input value. Value is matching if it is equal or smaller than given 173 * value. If given value is found to be in a range scanning is stopped and 174 * @found is set true. If a range with values smaller than given value is found 175 * but the range max is being smaller than given value, then the range's 176 * biggest selector is updated to @selector but scanning ranges is continued 177 * and @found is set to false. 178 * 179 * Return: 0 on success, -EINVAL if range array is invalid or does not contain 180 * range with a value smaller or equal to given value 181 */ 182 int linear_range_get_selector_low_array(const struct linear_range *r, 183 int ranges, unsigned int val, 184 unsigned int *selector, bool *found) 185 { 186 int i; 187 int ret = -EINVAL; 188 189 for (i = 0; i < ranges; i++) { 190 int tmpret; 191 192 tmpret = linear_range_get_selector_low(&r[i], val, selector, 193 found); 194 if (!tmpret) 195 ret = 0; 196 197 if (*found) 198 break; 199 } 200 201 return ret; 202 } 203 EXPORT_SYMBOL_GPL(linear_range_get_selector_low_array); 204 205 /** 206 * linear_range_get_selector_high - return linear range selector for value 207 * @r: pointer to linear range where selector is looked from 208 * @val: value for which the selector is searched 209 * @selector: address where found selector value is updated 210 * @found: flag to indicate that given value was in the range 211 * 212 * Return selector for which range value is closest match for given 213 * input value. Value is matching if it is equal or higher than given 214 * value. If given value is in the range, then @found is set true. 215 * 216 * Return: 0 on success, -EINVAL if range is invalid or does not contain 217 * value greater or equal to given value 218 */ 219 int linear_range_get_selector_high(const struct linear_range *r, 220 unsigned int val, unsigned int *selector, 221 bool *found) 222 { 223 *found = false; 224 225 if (linear_range_get_max_value(r) < val) 226 return -EINVAL; 227 228 if (r->min > val) { 229 *selector = r->min_sel; 230 return 0; 231 } 232 233 *found = true; 234 235 if (r->step == 0) 236 *selector = r->max_sel; 237 else 238 *selector = DIV_ROUND_UP(val - r->min, r->step) + r->min_sel; 239 240 return 0; 241 } 242 EXPORT_SYMBOL_GPL(linear_range_get_selector_high); 243 244 MODULE_DESCRIPTION("linear-ranges helper"); 245 MODULE_LICENSE("GPL"); 246