sys: linear_range: allow out-of-range values/windows
The existing linear_range API did not allow values or windows outside of the linear range (returned -EINVAL). With this change values are allowed outside of the range, being adjusted to the edge values (min/max) instead. In the case of windows, it is allowed to have partial intersection. In both cases, the API assigns a valid index (nearest) and returns -ERANGE. This change is useful because the main client of the linear range API, regulators, needs such behavior. For example, If an application specifies a voltage range from 1.0V to 1.5V and the regulator supports from 1.2V to 2.7V, the regulator can configure a voltage that satisfies the condition: 1.2V. With the current API, the input would be refused because 1.0V lies outside of the 1.2V-2.7V range. Also, for constant ranges, the minimum index is returned. Tests have been updated/extended accordingly. Signed-off-by: Gerard Marull-Paretas <gerard.marull@nordicsemi.no>
This commit is contained in:
parent
4314480826
commit
e086738b88
4 changed files with 95 additions and 60 deletions
|
@ -149,7 +149,7 @@ static int regulator_npm6001_buck012_set_voltage(
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = linear_range_get_win_index(range, min_uv, max_uv, &idx);
|
ret = linear_range_get_win_index(range, min_uv, max_uv, &idx);
|
||||||
if (ret < 0) {
|
if (ret == -EINVAL) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +205,7 @@ static int regulator_npm6001_buck3_set_voltage(const struct device *dev,
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = linear_range_get_win_index(&buck3_range, min_uv, max_uv, &idx);
|
ret = linear_range_get_win_index(&buck3_range, min_uv, max_uv, &idx);
|
||||||
if (ret < 0) {
|
if (ret == -EINVAL) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -218,7 +218,7 @@ static int regulator_pca9420_set_voltage(const struct device *dev,
|
||||||
ret = linear_range_group_get_win_index(config->desc->ranges,
|
ret = linear_range_group_get_win_index(config->desc->ranges,
|
||||||
config->desc->num_ranges, min_uv,
|
config->desc->num_ranges, min_uv,
|
||||||
max_uv, &idx);
|
max_uv, &idx);
|
||||||
if (ret < 0) {
|
if (ret == -EINVAL) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ static int regulator_pca9420_init(const struct device *dev)
|
||||||
ret = linear_range_group_get_win_index(
|
ret = linear_range_group_get_win_index(
|
||||||
config->desc->ranges, config->desc->num_ranges,
|
config->desc->ranges, config->desc->num_ranges,
|
||||||
config->modes_uv[i], config->modes_uv[i], &idx);
|
config->modes_uv[i], config->modes_uv[i], &idx);
|
||||||
if (ret < 0) {
|
if (ret == -EINVAL) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -177,103 +177,123 @@ static inline int linear_range_group_get_value(const struct linear_range *r,
|
||||||
/**
|
/**
|
||||||
* @brief Obtain index given a value.
|
* @brief Obtain index given a value.
|
||||||
*
|
*
|
||||||
|
* If the value falls outside the range, the nearest index will be stored and
|
||||||
|
* -ERANGE returned. That is, if the value falls below or above the range, the
|
||||||
|
* index will take the minimum or maximum value, respectively. For constant
|
||||||
|
* ranges, the minimum index will be returned.
|
||||||
|
*
|
||||||
* @param[in] r Linear range instance.
|
* @param[in] r Linear range instance.
|
||||||
* @param val Value.
|
* @param val Value.
|
||||||
* @param[out] idx Where index will be stored.
|
* @param[out] idx Where index will be stored.
|
||||||
*
|
*
|
||||||
* @retval 0 If a valid index is found within the linear range.
|
* @retval 0 If value falls within the range.
|
||||||
* @retval -EINVAL If value is out of range.
|
* @retval -ERANGE If the value falls out of the range.
|
||||||
*/
|
*/
|
||||||
static inline int linear_range_get_index(const struct linear_range *r,
|
static inline int linear_range_get_index(const struct linear_range *r,
|
||||||
int32_t val, uint16_t *idx)
|
int32_t val, uint16_t *idx)
|
||||||
{
|
{
|
||||||
if ((val < r->min) || (val > linear_range_get_max_value(r))) {
|
if (val < r->min) {
|
||||||
return -EINVAL;
|
*idx = r->min_idx;
|
||||||
|
return -ERANGE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (val > linear_range_get_max_value(r)) {
|
||||||
|
*idx = r->max_idx;
|
||||||
|
return -ERANGE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r->step == 0U) {
|
if (r->step == 0U) {
|
||||||
*idx = r->max_idx;
|
*idx = r->min_idx;
|
||||||
return 0;
|
} else {
|
||||||
|
*idx = r->min_idx + ceiling_fraction((uint32_t)(val - r->min),
|
||||||
|
r->step);
|
||||||
}
|
}
|
||||||
|
|
||||||
*idx = r->min_idx + ceiling_fraction((uint32_t)(val - r->min), r->step);
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Obtain index in a group given a value.
|
* @brief Obtain index in a group given a value.
|
||||||
*
|
*
|
||||||
|
* This function works the same way as linear_range_get_index(), but considering
|
||||||
|
* all ranges in the group.
|
||||||
|
*
|
||||||
* @param[in] r Linear range instances.
|
* @param[in] r Linear range instances.
|
||||||
* @param r_cnt Number of linear range instances.
|
* @param r_cnt Number of linear range instances.
|
||||||
* @param val Value.
|
* @param val Value.
|
||||||
* @param[out] idx Where index will be stored.
|
* @param[out] idx Where index will be stored.
|
||||||
*
|
*
|
||||||
* @retval 0 If a valid index is found.
|
* @retval 0 If value falls within the range group.
|
||||||
* @retval -EINVAL If value is out of range, or if the given window of values is
|
* @retval -ERANGE If the value falls out of the range group.
|
||||||
* too narrow.
|
* @retval -EINVAL If input is not valid (i.e. zero groups).
|
||||||
*/
|
*/
|
||||||
static inline int linear_range_group_get_index(const struct linear_range *r,
|
static inline int linear_range_group_get_index(const struct linear_range *r,
|
||||||
size_t r_cnt, int32_t val,
|
size_t r_cnt, int32_t val,
|
||||||
uint16_t *idx)
|
uint16_t *idx)
|
||||||
{
|
{
|
||||||
int ret = -EINVAL;
|
for (size_t i = 0U; i < r_cnt; i++) {
|
||||||
|
if ((val > linear_range_get_max_value(&r[i])) &&
|
||||||
for (size_t i = 0U; (ret != 0) && (i < r_cnt); i++) {
|
(i < (r_cnt - 1U))) {
|
||||||
ret = linear_range_get_index(&r[i], val, idx);
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return linear_range_get_index(&r[i], val, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Obtain index given a window of values.
|
* @brief Obtain index given a window of values.
|
||||||
*
|
*
|
||||||
|
* If the window of values does not intersect with the range, -EINVAL will be
|
||||||
|
* returned. If intersection is partial (any of the window egdes does not
|
||||||
|
* intersect), the nearest index will be stored and -ERANGE returned.
|
||||||
|
*
|
||||||
* @param[in] r Linear range instance.
|
* @param[in] r Linear range instance.
|
||||||
* @param val_min Minimum window value.
|
* @param val_min Minimum window value.
|
||||||
* @param val_max Maximum window value.
|
* @param val_max Maximum window value.
|
||||||
* @param[out] idx Where index will be stored.
|
* @param[out] idx Where index will be stored.
|
||||||
*
|
*
|
||||||
* @retval 0 If a valid index is found within window.
|
* @retval 0 If a valid index is found within linear range.
|
||||||
* @retval -EINVAL If value is out of range, or if the given window of values is
|
* @retval -ERANGE If the given window of values falls partially out of the
|
||||||
* too narrow.
|
* linear range.
|
||||||
|
* @retval -EINVAL If the given window of values does not intersect with the
|
||||||
|
* linear range or if they are too narrow.
|
||||||
*/
|
*/
|
||||||
static inline int linear_range_get_win_index(const struct linear_range *r,
|
static inline int linear_range_get_win_index(const struct linear_range *r,
|
||||||
int32_t val_min, int32_t val_max,
|
int32_t val_min, int32_t val_max,
|
||||||
uint16_t *idx)
|
uint16_t *idx)
|
||||||
{
|
{
|
||||||
if ((val_min < r->min) || (val_max > linear_range_get_max_value(r))) {
|
int ret;
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (r->step == 0U) {
|
|
||||||
*idx = r->max_idx;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
*idx = r->min_idx + ceiling_fraction((uint32_t)(val_min - r->min),
|
|
||||||
r->step);
|
|
||||||
|
|
||||||
|
ret = linear_range_get_index(r, val_min, idx);
|
||||||
if ((r->min + r->step * (*idx - r->min_idx)) > val_max) {
|
if ((r->min + r->step * (*idx - r->min_idx)) > val_max) {
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Obtain index in a group given a value that must be within a window of
|
* @brief Obtain index in a group given a value that must be within a window of
|
||||||
* values.
|
* values.
|
||||||
*
|
*
|
||||||
|
* This function works the same way as linear_range_get_win_index(), but
|
||||||
|
* considering all ranges in the group.
|
||||||
|
*
|
||||||
* @param[in] r Linear range instances.
|
* @param[in] r Linear range instances.
|
||||||
* @param r_cnt Number of linear range instances.
|
* @param r_cnt Number of linear range instances.
|
||||||
* @param val_min Minimum window value.
|
* @param val_min Minimum window value.
|
||||||
* @param val_max Maximum window value.
|
* @param val_max Maximum window value.
|
||||||
* @param[out] idx Where index will be stored.
|
* @param[out] idx Where index will be stored.
|
||||||
*
|
*
|
||||||
* @retval 0 If a valid index is found within window.
|
* @retval 0 If a valid index is found within linear range group.
|
||||||
* @retval -EINVAL If value is out of range, or if the given window of values is
|
* @retval -ERANGE If the given window of values falls partially out of the
|
||||||
* too narrow.
|
* linear range group.
|
||||||
|
* @retval -EINVAL If the given window of values does not intersect with the
|
||||||
|
* linear range group, if they are too narrow, or if input is invalid (i.e.
|
||||||
|
* zero groups).
|
||||||
*/
|
*/
|
||||||
static inline int linear_range_group_get_win_index(const struct linear_range *r,
|
static inline int linear_range_group_get_win_index(const struct linear_range *r,
|
||||||
size_t r_cnt,
|
size_t r_cnt,
|
||||||
|
@ -281,17 +301,15 @@ static inline int linear_range_group_get_win_index(const struct linear_range *r,
|
||||||
int32_t val_max,
|
int32_t val_max,
|
||||||
uint16_t *idx)
|
uint16_t *idx)
|
||||||
{
|
{
|
||||||
int ret = -EINVAL;
|
for (size_t i = 0U; i < r_cnt; i++) {
|
||||||
|
if (val_min > linear_range_get_max_value(&r[i])) {
|
||||||
for (size_t i = 0U; (ret != 0) && (i < r_cnt); i++) {
|
continue;
|
||||||
int32_t r_val_max = linear_range_get_max_value(&r[i]);
|
|
||||||
|
|
||||||
ret = linear_range_get_win_index(
|
|
||||||
&r[i], val_min, MAX(val_min, MIN(r_val_max, val_max)),
|
|
||||||
idx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return linear_range_get_win_index(&r[i], val_min, val_max, idx);
|
||||||
|
}
|
||||||
|
|
||||||
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @} */
|
/** @} */
|
||||||
|
|
|
@ -192,10 +192,12 @@ ZTEST(linear_range, test_linear_range_get_index)
|
||||||
|
|
||||||
/* out of range (< min, > max) */
|
/* out of range (< min, > max) */
|
||||||
ret = linear_range_get_index(&r[1], -1, &idx);
|
ret = linear_range_get_index(&r[1], -1, &idx);
|
||||||
zassert_equal(ret, -EINVAL);
|
zassert_equal(ret, -ERANGE);
|
||||||
|
zassert_equal(idx, 2U);
|
||||||
|
|
||||||
ret = linear_range_get_index(&r[1], 2, &idx);
|
ret = linear_range_get_index(&r[1], 2, &idx);
|
||||||
zassert_equal(ret, -EINVAL);
|
zassert_equal(ret, -ERANGE);
|
||||||
|
zassert_equal(idx, 3U);
|
||||||
|
|
||||||
/* range limits */
|
/* range limits */
|
||||||
ret = linear_range_get_index(&r[2], 100, &idx);
|
ret = linear_range_get_index(&r[2], 100, &idx);
|
||||||
|
@ -211,17 +213,15 @@ ZTEST(linear_range, test_linear_range_get_index)
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 5U);
|
zassert_equal(idx, 5U);
|
||||||
|
|
||||||
/* always maximum index in constant ranges */
|
/* always minimum index in constant ranges */
|
||||||
ret = linear_range_get_index(&r[3], 400, &idx);
|
ret = linear_range_get_index(&r[3], 400, &idx);
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 12U);
|
zassert_equal(idx, 11U);
|
||||||
|
|
||||||
/* group */
|
/* group */
|
||||||
ret = linear_range_group_get_index(r, r_cnt, -20, &idx);
|
ret = linear_range_group_get_index(r, r_cnt, -20, &idx);
|
||||||
zassert_equal(ret, -EINVAL);
|
zassert_equal(ret, -ERANGE);
|
||||||
|
zassert_equal(idx, 0U);
|
||||||
ret = linear_range_group_get_index(r, r_cnt, 50, &idx);
|
|
||||||
zassert_equal(ret, -EINVAL);
|
|
||||||
|
|
||||||
ret = linear_range_group_get_index(r, r_cnt, -6, &idx);
|
ret = linear_range_group_get_index(r, r_cnt, -6, &idx);
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
|
@ -231,13 +231,17 @@ ZTEST(linear_range, test_linear_range_get_index)
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 2U);
|
zassert_equal(idx, 2U);
|
||||||
|
|
||||||
|
ret = linear_range_group_get_index(r, r_cnt, 50, &idx);
|
||||||
|
zassert_equal(ret, -ERANGE);
|
||||||
|
zassert_equal(idx, 4U);
|
||||||
|
|
||||||
ret = linear_range_group_get_index(r, r_cnt, 200, &idx);
|
ret = linear_range_group_get_index(r, r_cnt, 200, &idx);
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 8U);
|
zassert_equal(idx, 8U);
|
||||||
|
|
||||||
ret = linear_range_group_get_index(r, r_cnt, 400, &idx);
|
ret = linear_range_group_get_index(r, r_cnt, 400, &idx);
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 12U);
|
zassert_equal(idx, 11U);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZTEST(linear_range, test_linear_range_get_win_index)
|
ZTEST(linear_range, test_linear_range_get_win_index)
|
||||||
|
@ -254,12 +258,18 @@ ZTEST(linear_range, test_linear_range_get_win_index)
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 1U);
|
zassert_equal(idx, 1U);
|
||||||
|
|
||||||
/* out of range (< min, > max) */
|
/* no intersection */
|
||||||
ret = linear_range_get_win_index(&r[1], -1, 0, &idx);
|
ret = linear_range_get_win_index(&r[0], -20, -15, &idx);
|
||||||
zassert_equal(ret, -EINVAL);
|
zassert_equal(ret, -EINVAL);
|
||||||
|
|
||||||
|
/* out of range, partial intersection (< min, > max) */
|
||||||
|
ret = linear_range_get_win_index(&r[1], -1, 0, &idx);
|
||||||
|
zassert_equal(ret, -ERANGE);
|
||||||
|
zassert_equal(idx, 2U);
|
||||||
|
|
||||||
ret = linear_range_get_win_index(&r[1], 2, 3, &idx);
|
ret = linear_range_get_win_index(&r[1], 2, 3, &idx);
|
||||||
zassert_equal(ret, -EINVAL);
|
zassert_equal(ret, -ERANGE);
|
||||||
|
zassert_equal(idx, 3U);
|
||||||
|
|
||||||
/* min/max equal */
|
/* min/max equal */
|
||||||
ret = linear_range_get_win_index(&r[2], 100, 100, &idx);
|
ret = linear_range_get_win_index(&r[2], 100, 100, &idx);
|
||||||
|
@ -283,10 +293,10 @@ ZTEST(linear_range, test_linear_range_get_win_index)
|
||||||
ret = linear_range_group_get_win_index(r, r_cnt, 120, 125, &idx);
|
ret = linear_range_group_get_win_index(r, r_cnt, 120, 125, &idx);
|
||||||
zassert_equal(ret, -EINVAL);
|
zassert_equal(ret, -EINVAL);
|
||||||
|
|
||||||
/* always maximum index in constant ranges */
|
/* always minimum index in constant ranges */
|
||||||
ret = linear_range_get_win_index(&r[3], 400, 400, &idx);
|
ret = linear_range_get_win_index(&r[3], 400, 400, &idx);
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 12U);
|
zassert_equal(idx, 11U);
|
||||||
|
|
||||||
/* group */
|
/* group */
|
||||||
ret = linear_range_group_get_win_index(r, r_cnt, -10, -8, &idx);
|
ret = linear_range_group_get_win_index(r, r_cnt, -10, -8, &idx);
|
||||||
|
@ -297,6 +307,10 @@ ZTEST(linear_range, test_linear_range_get_win_index)
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 2U);
|
zassert_equal(idx, 2U);
|
||||||
|
|
||||||
|
ret = linear_range_group_get_win_index(r, r_cnt, 1, 120, &idx);
|
||||||
|
zassert_equal(ret, 0);
|
||||||
|
zassert_equal(idx, 3U);
|
||||||
|
|
||||||
ret = linear_range_group_get_win_index(r, r_cnt, 120, 140, &idx);
|
ret = linear_range_group_get_win_index(r, r_cnt, 120, 140, &idx);
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 5U);
|
zassert_equal(idx, 5U);
|
||||||
|
@ -307,7 +321,10 @@ ZTEST(linear_range, test_linear_range_get_win_index)
|
||||||
|
|
||||||
ret = linear_range_group_get_win_index(r, r_cnt, 400, 400, &idx);
|
ret = linear_range_group_get_win_index(r, r_cnt, 400, 400, &idx);
|
||||||
zassert_equal(ret, 0);
|
zassert_equal(ret, 0);
|
||||||
zassert_equal(idx, 12U);
|
zassert_equal(idx, 11U);
|
||||||
|
|
||||||
|
ret = linear_range_group_get_win_index(r, r_cnt, 300, 310, &idx);
|
||||||
|
zassert_equal(ret, -EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
ZTEST_SUITE(linear_range, NULL, NULL, NULL, NULL, NULL);
|
ZTEST_SUITE(linear_range, NULL, NULL, NULL, NULL, NULL);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue