Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * helpers to map values in a linear range to range index
0004  *
0005  * Original idea borrowed from regulator framework
0006  *
0007  * It might be useful if we could support also inversely proportional ranges?
0008  * Copyright 2020 ROHM Semiconductors
0009  */
0010 
0011 #include <linux/errno.h>
0012 #include <linux/export.h>
0013 #include <linux/kernel.h>
0014 #include <linux/linear_range.h>
0015 #include <linux/module.h>
0016 
0017 /**
0018  * linear_range_values_in_range - return the amount of values in a range
0019  * @r:      pointer to linear range where values are counted
0020  *
0021  * Compute the amount of values in range pointed by @r. Note, values can
0022  * be all equal - range with selectors 0,...,2 with step 0 still contains
0023  * 3 values even though they are all equal.
0024  *
0025  * Return: the amount of values in range pointed by @r
0026  */
0027 unsigned int linear_range_values_in_range(const struct linear_range *r)
0028 {
0029     if (!r)
0030         return 0;
0031     return r->max_sel - r->min_sel + 1;
0032 }
0033 EXPORT_SYMBOL_GPL(linear_range_values_in_range);
0034 
0035 /**
0036  * linear_range_values_in_range_array - return the amount of values in ranges
0037  * @r:      pointer to array of linear ranges where values are counted
0038  * @ranges: amount of ranges we include in computation.
0039  *
0040  * Compute the amount of values in ranges pointed by @r. Note, values can
0041  * be all equal - range with selectors 0,...,2 with step 0 still contains
0042  * 3 values even though they are all equal.
0043  *
0044  * Return: the amount of values in first @ranges ranges pointed by @r
0045  */
0046 unsigned int linear_range_values_in_range_array(const struct linear_range *r,
0047                         int ranges)
0048 {
0049     int i, values_in_range = 0;
0050 
0051     for (i = 0; i < ranges; i++) {
0052         int values;
0053 
0054         values = linear_range_values_in_range(&r[i]);
0055         if (!values)
0056             return values;
0057 
0058         values_in_range += values;
0059     }
0060     return values_in_range;
0061 }
0062 EXPORT_SYMBOL_GPL(linear_range_values_in_range_array);
0063 
0064 /**
0065  * linear_range_get_max_value - return the largest value in a range
0066  * @r:      pointer to linear range where value is looked from
0067  *
0068  * Return: the largest value in the given range
0069  */
0070 unsigned int linear_range_get_max_value(const struct linear_range *r)
0071 {
0072     return r->min + (r->max_sel - r->min_sel) * r->step;
0073 }
0074 EXPORT_SYMBOL_GPL(linear_range_get_max_value);
0075 
0076 /**
0077  * linear_range_get_value - fetch a value from given range
0078  * @r:      pointer to linear range where value is looked from
0079  * @selector:   selector for which the value is searched
0080  * @val:    address where found value is updated
0081  *
0082  * Search given ranges for value which matches given selector.
0083  *
0084  * Return: 0 on success, -EINVAL given selector is not found from any of the
0085  * ranges.
0086  */
0087 int linear_range_get_value(const struct linear_range *r, unsigned int selector,
0088                unsigned int *val)
0089 {
0090     if (r->min_sel > selector || r->max_sel < selector)
0091         return -EINVAL;
0092 
0093     *val = r->min + (selector - r->min_sel) * r->step;
0094 
0095     return 0;
0096 }
0097 EXPORT_SYMBOL_GPL(linear_range_get_value);
0098 
0099 /**
0100  * linear_range_get_value_array - fetch a value from array of ranges
0101  * @r:      pointer to array of linear ranges where value is looked from
0102  * @ranges: amount of ranges in an array
0103  * @selector:   selector for which the value is searched
0104  * @val:    address where found value is updated
0105  *
0106  * Search through an array of ranges for value which matches given selector.
0107  *
0108  * Return: 0 on success, -EINVAL given selector is not found from any of the
0109  * ranges.
0110  */
0111 int linear_range_get_value_array(const struct linear_range *r, int ranges,
0112                  unsigned int selector, unsigned int *val)
0113 {
0114     int i;
0115 
0116     for (i = 0; i < ranges; i++)
0117         if (r[i].min_sel <= selector && r[i].max_sel >= selector)
0118             return linear_range_get_value(&r[i], selector, val);
0119 
0120     return -EINVAL;
0121 }
0122 EXPORT_SYMBOL_GPL(linear_range_get_value_array);
0123 
0124 /**
0125  * linear_range_get_selector_low - return linear range selector for value
0126  * @r:      pointer to linear range where selector is looked from
0127  * @val:    value for which the selector is searched
0128  * @selector:   address where found selector value is updated
0129  * @found:  flag to indicate that given value was in the range
0130  *
0131  * Return selector for which range value is closest match for given
0132  * input value. Value is matching if it is equal or smaller than given
0133  * value. If given value is in the range, then @found is set true.
0134  *
0135  * Return: 0 on success, -EINVAL if range is invalid or does not contain
0136  * value smaller or equal to given value
0137  */
0138 int linear_range_get_selector_low(const struct linear_range *r,
0139                   unsigned int val, unsigned int *selector,
0140                   bool *found)
0141 {
0142     *found = false;
0143 
0144     if (r->min > val)
0145         return -EINVAL;
0146 
0147     if (linear_range_get_max_value(r) < val) {
0148         *selector = r->max_sel;
0149         return 0;
0150     }
0151 
0152     *found = true;
0153 
0154     if (r->step == 0)
0155         *selector = r->min_sel;
0156     else
0157         *selector = (val - r->min) / r->step + r->min_sel;
0158 
0159     return 0;
0160 }
0161 EXPORT_SYMBOL_GPL(linear_range_get_selector_low);
0162 
0163 /**
0164  * linear_range_get_selector_low_array - return linear range selector for value
0165  * @r:      pointer to array of linear ranges where selector is looked from
0166  * @ranges: amount of ranges to scan from array
0167  * @val:    value for which the selector is searched
0168  * @selector:   address where found selector value is updated
0169  * @found:  flag to indicate that given value was in the range
0170  *
0171  * Scan array of ranges for selector for which range value matches given
0172  * input value. Value is matching if it is equal or smaller than given
0173  * value. If given value is found to be in a range scanning is stopped and
0174  * @found is set true. If a range with values smaller than given value is found
0175  * but the range max is being smaller than given value, then the range's
0176  * biggest selector is updated to @selector but scanning ranges is continued
0177  * and @found is set to false.
0178  *
0179  * Return: 0 on success, -EINVAL if range array is invalid or does not contain
0180  * range with a value smaller or equal to given value
0181  */
0182 int linear_range_get_selector_low_array(const struct linear_range *r,
0183                     int ranges, unsigned int val,
0184                     unsigned int *selector, bool *found)
0185 {
0186     int i;
0187     int ret = -EINVAL;
0188 
0189     for (i = 0; i < ranges; i++) {
0190         int tmpret;
0191 
0192         tmpret = linear_range_get_selector_low(&r[i], val, selector,
0193                                found);
0194         if (!tmpret)
0195             ret = 0;
0196 
0197         if (*found)
0198             break;
0199     }
0200 
0201     return ret;
0202 }
0203 EXPORT_SYMBOL_GPL(linear_range_get_selector_low_array);
0204 
0205 /**
0206  * linear_range_get_selector_high - return linear range selector for value
0207  * @r:      pointer to linear range where selector is looked from
0208  * @val:    value for which the selector is searched
0209  * @selector:   address where found selector value is updated
0210  * @found:  flag to indicate that given value was in the range
0211  *
0212  * Return selector for which range value is closest match for given
0213  * input value. Value is matching if it is equal or higher than given
0214  * value. If given value is in the range, then @found is set true.
0215  *
0216  * Return: 0 on success, -EINVAL if range is invalid or does not contain
0217  * value greater or equal to given value
0218  */
0219 int linear_range_get_selector_high(const struct linear_range *r,
0220                    unsigned int val, unsigned int *selector,
0221                    bool *found)
0222 {
0223     *found = false;
0224 
0225     if (linear_range_get_max_value(r) < val)
0226         return -EINVAL;
0227 
0228     if (r->min > val) {
0229         *selector = r->min_sel;
0230         return 0;
0231     }
0232 
0233     *found = true;
0234 
0235     if (r->step == 0)
0236         *selector = r->max_sel;
0237     else
0238         *selector = DIV_ROUND_UP(val - r->min, r->step) + r->min_sel;
0239 
0240     return 0;
0241 }
0242 EXPORT_SYMBOL_GPL(linear_range_get_selector_high);
0243 
0244 /**
0245  * linear_range_get_selector_within - return linear range selector for value
0246  * @r:      pointer to linear range where selector is looked from
0247  * @val:    value for which the selector is searched
0248  * @selector:   address where found selector value is updated
0249  *
0250  * Return selector for which range value is closest match for given
0251  * input value. Value is matching if it is equal or lower than given
0252  * value. But return maximum selector if given value is higher than
0253  * maximum value.
0254  */
0255 void linear_range_get_selector_within(const struct linear_range *r,
0256                       unsigned int val, unsigned int *selector)
0257 {
0258     if (r->min > val) {
0259         *selector = r->min_sel;
0260         return;
0261     }
0262 
0263     if (linear_range_get_max_value(r) < val) {
0264         *selector = r->max_sel;
0265         return;
0266     }
0267 
0268     if (r->step == 0)
0269         *selector = r->min_sel;
0270     else
0271         *selector = (val - r->min) / r->step + r->min_sel;
0272 }
0273 EXPORT_SYMBOL_GPL(linear_range_get_selector_within);
0274 
0275 MODULE_DESCRIPTION("linear-ranges helper");
0276 MODULE_LICENSE("GPL");