linear_ranges.c 7.2 KB

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