linux/lib/linear_ranges.c
<<
>>
Prefs
   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 */
  27unsigned 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}
  33EXPORT_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 */
  46unsigned 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}
  62EXPORT_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 */
  70unsigned 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}
  74EXPORT_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 */
  87int 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}
  97EXPORT_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 */
 111int 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}
 122EXPORT_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 */
 138int 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}
 161EXPORT_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 */
 182int 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}
 203EXPORT_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 */
 219int 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}
 242EXPORT_SYMBOL_GPL(linear_range_get_selector_high);
 243
 244/**
 245 * linear_range_get_selector_within - return linear range selector for value
 246 * @r:          pointer to linear range where selector is looked from
 247 * @val:        value for which the selector is searched
 248 * @selector:   address where found selector value is updated
 249 *
 250 * Return selector for which range value is closest match for given
 251 * input value. Value is matching if it is equal or lower than given
 252 * value. But return maximum selector if given value is higher than
 253 * maximum value.
 254 */
 255void linear_range_get_selector_within(const struct linear_range *r,
 256                                      unsigned int val, unsigned int *selector)
 257{
 258        if (r->min > val) {
 259                *selector = r->min_sel;
 260                return;
 261        }
 262
 263        if (linear_range_get_max_value(r) < val) {
 264                *selector = r->max_sel;
 265                return;
 266        }
 267
 268        if (r->step == 0)
 269                *selector = r->min_sel;
 270        else
 271                *selector = (val - r->min) / r->step + r->min_sel;
 272}
 273EXPORT_SYMBOL_GPL(linear_range_get_selector_within);
 274
 275MODULE_DESCRIPTION("linear-ranges helper");
 276MODULE_LICENSE("GPL");
 277