diff --git a/drivers/regulator/regulator_npm6001.c b/drivers/regulator/regulator_npm6001.c index c1ee79a888b..21086b3ad60 100644 --- a/drivers/regulator/regulator_npm6001.c +++ b/drivers/regulator/regulator_npm6001.c @@ -149,7 +149,7 @@ static int regulator_npm6001_buck012_set_voltage( int ret; ret = linear_range_get_win_index(range, min_uv, max_uv, &idx); - if (ret < 0) { + if (ret == -EINVAL) { return ret; } @@ -205,7 +205,7 @@ static int regulator_npm6001_buck3_set_voltage(const struct device *dev, int ret; ret = linear_range_get_win_index(&buck3_range, min_uv, max_uv, &idx); - if (ret < 0) { + if (ret == -EINVAL) { return ret; } diff --git a/drivers/regulator/regulator_pca9420.c b/drivers/regulator/regulator_pca9420.c index b725b955ae4..630636bccd0 100644 --- a/drivers/regulator/regulator_pca9420.c +++ b/drivers/regulator/regulator_pca9420.c @@ -218,7 +218,7 @@ static int regulator_pca9420_set_voltage(const struct device *dev, ret = linear_range_group_get_win_index(config->desc->ranges, config->desc->num_ranges, min_uv, max_uv, &idx); - if (ret < 0) { + if (ret == -EINVAL) { return ret; } @@ -327,7 +327,7 @@ static int regulator_pca9420_init(const struct device *dev) ret = linear_range_group_get_win_index( config->desc->ranges, config->desc->num_ranges, config->modes_uv[i], config->modes_uv[i], &idx); - if (ret < 0) { + if (ret == -EINVAL) { return ret; } diff --git a/include/zephyr/sys/linear_range.h b/include/zephyr/sys/linear_range.h index f0832a667dc..997efb088d5 100644 --- a/include/zephyr/sys/linear_range.h +++ b/include/zephyr/sys/linear_range.h @@ -177,103 +177,123 @@ static inline int linear_range_group_get_value(const struct linear_range *r, /** * @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 val Value. * @param[out] idx Where index will be stored. * - * @retval 0 If a valid index is found within the linear range. - * @retval -EINVAL If value is out of range. + * @retval 0 If value falls within the range. + * @retval -ERANGE If the value falls out of the range. */ static inline int linear_range_get_index(const struct linear_range *r, int32_t val, uint16_t *idx) { - if ((val < r->min) || (val > linear_range_get_max_value(r))) { - return -EINVAL; + if (val < r->min) { + *idx = r->min_idx; + return -ERANGE; + } + + if (val > linear_range_get_max_value(r)) { + *idx = r->max_idx; + return -ERANGE; } if (r->step == 0U) { - *idx = r->max_idx; - return 0; + *idx = r->min_idx; + } 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; } /** * @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 r_cnt Number of linear range instances. * @param val Value. * @param[out] idx Where index will be stored. * - * @retval 0 If a valid index is found. - * @retval -EINVAL If value is out of range, or if the given window of values is - * too narrow. + * @retval 0 If value falls within the range group. + * @retval -ERANGE If the value falls out of the range group. + * @retval -EINVAL If input is not valid (i.e. zero groups). */ static inline int linear_range_group_get_index(const struct linear_range *r, size_t r_cnt, int32_t val, uint16_t *idx) { - int ret = -EINVAL; + for (size_t i = 0U; i < r_cnt; i++) { + if ((val > linear_range_get_max_value(&r[i])) && + (i < (r_cnt - 1U))) { + continue; + } - for (size_t i = 0U; (ret != 0) && (i < r_cnt); i++) { - ret = linear_range_get_index(&r[i], val, idx); + return linear_range_get_index(&r[i], val, idx); } - return ret; + return -EINVAL; } /** * @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 val_min Minimum window value. * @param val_max Maximum window value. * @param[out] idx Where index will be stored. * - * @retval 0 If a valid index is found within window. - * @retval -EINVAL If value is out of range, or if the given window of values is - * too narrow. + * @retval 0 If a valid index is found within linear range. + * @retval -ERANGE If the given window of values falls partially out of the + * 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, int32_t val_min, int32_t val_max, uint16_t *idx) { - if ((val_min < r->min) || (val_max > linear_range_get_max_value(r))) { - 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); + int ret; + ret = linear_range_get_index(r, val_min, idx); if ((r->min + r->step * (*idx - r->min_idx)) > val_max) { return -EINVAL; } - return 0; + return ret; } /** * @brief Obtain index in a group given a value that must be within a window of * 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 r_cnt Number of linear range instances. * @param val_min Minimum window value. * @param val_max Maximum window value. * @param[out] idx Where index will be stored. * - * @retval 0 If a valid index is found within window. - * @retval -EINVAL If value is out of range, or if the given window of values is - * too narrow. + * @retval 0 If a valid index is found within linear range group. + * @retval -ERANGE If the given window of values falls partially out of the + * 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, 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, 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])) { + continue; + } - for (size_t i = 0U; (ret != 0) && (i < r_cnt); i++) { - 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 linear_range_get_win_index(&r[i], val_min, val_max, idx); } - return ret; + return -EINVAL; } /** @} */ diff --git a/tests/lib/linear_range/src/main.c b/tests/lib/linear_range/src/main.c index f41fa0d1391..033c92bd058 100644 --- a/tests/lib/linear_range/src/main.c +++ b/tests/lib/linear_range/src/main.c @@ -192,10 +192,12 @@ ZTEST(linear_range, test_linear_range_get_index) /* out of range (< min, > max) */ 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); - zassert_equal(ret, -EINVAL); + zassert_equal(ret, -ERANGE); + zassert_equal(idx, 3U); /* range limits */ 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(idx, 5U); - /* always maximum index in constant ranges */ + /* always minimum index in constant ranges */ ret = linear_range_get_index(&r[3], 400, &idx); zassert_equal(ret, 0); - zassert_equal(idx, 12U); + zassert_equal(idx, 11U); /* group */ ret = linear_range_group_get_index(r, r_cnt, -20, &idx); - zassert_equal(ret, -EINVAL); - - ret = linear_range_group_get_index(r, r_cnt, 50, &idx); - zassert_equal(ret, -EINVAL); + zassert_equal(ret, -ERANGE); + zassert_equal(idx, 0U); ret = linear_range_group_get_index(r, r_cnt, -6, &idx); zassert_equal(ret, 0); @@ -231,13 +231,17 @@ ZTEST(linear_range, test_linear_range_get_index) zassert_equal(ret, 0); 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); zassert_equal(ret, 0); zassert_equal(idx, 8U); ret = linear_range_group_get_index(r, r_cnt, 400, &idx); zassert_equal(ret, 0); - zassert_equal(idx, 12U); + zassert_equal(idx, 11U); } 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(idx, 1U); - /* out of range (< min, > max) */ - ret = linear_range_get_win_index(&r[1], -1, 0, &idx); + /* no intersection */ + ret = linear_range_get_win_index(&r[0], -20, -15, &idx); 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); - zassert_equal(ret, -EINVAL); + zassert_equal(ret, -ERANGE); + zassert_equal(idx, 3U); /* min/max equal */ 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); 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); zassert_equal(ret, 0); - zassert_equal(idx, 12U); + zassert_equal(idx, 11U); /* group */ 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(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); zassert_equal(ret, 0); 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); 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);