diff --git a/drivers/regulator/CMakeLists.txt b/drivers/regulator/CMakeLists.txt index 14866c3feba..a9ad088728b 100644 --- a/drivers/regulator/CMakeLists.txt +++ b/drivers/regulator/CMakeLists.txt @@ -3,6 +3,7 @@ zephyr_library() +zephyr_library_sources(regulator_common.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_FIXED regulator_fixed.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_PCA9420 regulator_pca9420.c) zephyr_library_sources_ifdef(CONFIG_REGULATOR_SHELL regulator_shell.c) diff --git a/drivers/regulator/regulator_common.c b/drivers/regulator/regulator_common.c new file mode 100644 index 00000000000..be733456b89 --- /dev/null +++ b/drivers/regulator/regulator_common.c @@ -0,0 +1,65 @@ +/* + * Copyright 2022 Nordic Semiconductor ASA + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +void regulator_common_data_init(const struct device *dev) +{ + struct regulator_common_data *data = + (struct regulator_common_data *)dev->data; + + (void)k_mutex_init(&data->lock); + data->refcnt = 0; +} + +int regulator_enable(const struct device *dev) +{ + struct regulator_common_data *data = + (struct regulator_common_data *)dev->data; + int ret = 0; + + (void)k_mutex_lock(&data->lock, K_FOREVER); + + data->refcnt++; + + if (data->refcnt == 1) { + const struct regulator_driver_api *api = + (const struct regulator_driver_api *)dev->api; + + ret = api->enable(dev); + if (ret < 0) { + data->refcnt--; + } + } + + k_mutex_unlock(&data->lock); + + return ret; +} + +int regulator_disable(const struct device *dev) +{ + struct regulator_common_data *data = + (struct regulator_common_data *)dev->data; + int ret = 0; + + (void)k_mutex_lock(&data->lock, K_FOREVER); + + data->refcnt--; + + if (data->refcnt == 0) { + const struct regulator_driver_api *api = + (const struct regulator_driver_api *)dev->api; + + ret = api->disable(dev); + if (ret < 0) { + data->refcnt++; + } + } + + k_mutex_unlock(&data->lock); + + return ret; +} diff --git a/drivers/regulator/regulator_fixed.c b/drivers/regulator/regulator_fixed.c index 9499a6aedc0..ccfb8f9b193 100644 --- a/drivers/regulator/regulator_fixed.c +++ b/drivers/regulator/regulator_fixed.c @@ -28,62 +28,39 @@ struct regulator_fixed_config { }; struct regulator_fixed_data { - struct onoff_sync_service srv; + struct regulator_common_data common; }; -static int regulator_fixed_enable(const struct device *dev, - struct onoff_client *cli) +static int regulator_fixed_enable(const struct device *dev) { const struct regulator_fixed_config *cfg = dev->config; - struct regulator_fixed_data *data = dev->data; - k_spinlock_key_t key; int ret; if ((cfg->options & OPTION_ALWAYS_ON) != 0) { return 0; } - ret = onoff_sync_lock(&data->srv, &key); - if (ret > 0) { - goto finalize; - } - ret = gpio_pin_set_dt(&cfg->enable, 1); if (ret < 0) { - goto finalize; + return ret; } if (cfg->off_on_delay_us > 0U) { k_sleep(K_USEC(cfg->off_on_delay_us)); } -finalize: - return onoff_sync_finalize(&data->srv, key, cli, ret, true); + return 0; } static int regulator_fixed_disable(const struct device *dev) { const struct regulator_fixed_config *cfg = dev->config; - struct regulator_fixed_data *data = dev->data; - k_spinlock_key_t key; - int ret; if ((cfg->options & OPTION_ALWAYS_ON) != 0) { return 0; } - ret = onoff_sync_lock(&data->srv, &key); - if (ret != 1) { - goto finalize; - } - - ret = gpio_pin_set_dt(&cfg->enable, 0); - if (ret < 0) { - return ret; - } - -finalize: - return onoff_sync_finalize(&data->srv, key, NULL, ret, false); + return gpio_pin_set_dt(&cfg->enable, 0); } static const struct regulator_driver_api regulator_fixed_api = { @@ -96,6 +73,8 @@ static int regulator_fixed_init(const struct device *dev) const struct regulator_fixed_config *cfg = dev->config; int ret; + regulator_common_data_init(dev); + if (!device_is_ready(cfg->enable.port)) { LOG_ERR("GPIO port: %s not ready", cfg->enable.port->name); return -ENODEV; @@ -119,6 +98,8 @@ static int regulator_fixed_init(const struct device *dev) } #define REGULATOR_FIXED_DEFINE(inst) \ + static struct regulator_fixed_data data##inst; \ + \ static const struct regulator_fixed_config config##inst = { \ .startup_delay_us = DT_INST_PROP(inst, startup_delay_us), \ .off_on_delay_us = DT_INST_PROP(inst, off_on_delay_us), \ @@ -129,7 +110,7 @@ static int regulator_fixed_init(const struct device *dev) << OPTION_ALWAYS_ON_POS), \ }; \ \ - DEVICE_DT_INST_DEFINE(inst, regulator_fixed_init, NULL, NULL, \ + DEVICE_DT_INST_DEFINE(inst, regulator_fixed_init, NULL, &data##inst, \ &config##inst, POST_KERNEL, \ CONFIG_REGULATOR_FIXED_INIT_PRIORITY, \ ®ulator_fixed_api); diff --git a/drivers/regulator/regulator_pca9420.c b/drivers/regulator/regulator_pca9420.c index 90024f9cc49..814af60579a 100644 --- a/drivers/regulator/regulator_pca9420.c +++ b/drivers/regulator/regulator_pca9420.c @@ -89,10 +89,6 @@ struct regulator_pca9420_desc { const struct linear_range *ranges; }; -struct regulator_pca9420_data { - struct onoff_sync_service srv; -}; - struct regulator_pca9420_common_config { struct i2c_dt_spec i2c; int32_t vin_ilim_ua; @@ -111,6 +107,10 @@ struct regulator_pca9420_config { const struct device *parent; }; +struct regulator_pca9420_data { + struct regulator_common_data data; +}; + static const struct linear_range buck1_ranges[] = { LINEAR_RANGE_INIT(500000, 25000U, 0x0U, 0x28U), LINEAR_RANGE_INIT(1500000, 0U, 0x29U, 0x3E), @@ -558,55 +558,30 @@ static int regulator_pca9420_set_mode(const struct device *dev, uint32_t mode) return rc; } -static int regulator_pca9420_enable(const struct device *dev, - struct onoff_client *cli) +static int regulator_pca9420_enable(const struct device *dev) { - k_spinlock_key_t key; - int rc; - uint8_t en_val; - struct regulator_pca9420_data *data = dev->data; const struct regulator_pca9420_config *config = dev->config; const struct regulator_pca9420_common_config *cconfig = config->parent->config; + uint8_t en_val; - LOG_DBG("Enabling regulator"); - rc = onoff_sync_lock(&data->srv, &key); - if (rc) { - /* Request has already enabled PMIC */ - return onoff_sync_finalize(&data->srv, key, cli, rc, true); - } en_val = config->enable_inverted ? 0 : config->desc->enable_val; - rc = regulator_pca9420_modify_register(&cconfig->i2c, - config->desc->enable_reg, - config->desc->enable_mask, - en_val); - if (rc != 0) { - return onoff_sync_finalize(&data->srv, key, NULL, rc, false); - } - return onoff_sync_finalize(&data->srv, key, cli, rc, true); + return regulator_pca9420_modify_register(&cconfig->i2c, + config->desc->enable_reg, + config->desc->enable_mask, + en_val); } static int regulator_pca9420_disable(const struct device *dev) { - struct regulator_pca9420_data *data = dev->data; const struct regulator_pca9420_config *config = dev->config; const struct regulator_pca9420_common_config *cconfig = config->parent->config; - k_spinlock_key_t key; uint8_t dis_val; - int rc; - LOG_DBG("Disabling regulator"); - rc = onoff_sync_lock(&data->srv, &key); - if (rc == 0) { - rc = -EINVAL; - return onoff_sync_finalize(&data->srv, key, NULL, rc, false); - } else if (rc == 1) { - /* Disable regulator */ - dis_val = config->enable_inverted ? config->desc->enable_val : 0; - rc = regulator_pca9420_modify_register( - &cconfig->i2c, config->desc->enable_reg, - config->desc->enable_mask, dis_val); - } - return onoff_sync_finalize(&data->srv, key, NULL, rc, false); + dis_val = config->enable_inverted ? config->desc->enable_val : 0; + return regulator_pca9420_modify_register(&cconfig->i2c, + config->desc->enable_reg, + config->desc->enable_mask, + dis_val); } static int regulator_pca9420_init(const struct device *dev) @@ -615,12 +590,14 @@ static int regulator_pca9420_init(const struct device *dev) const struct regulator_pca9420_common_config *cconfig = config->parent->config; int rc = 0; + regulator_common_data_init(dev); + if (!device_is_ready(config->parent)) { return -ENODEV; } if (config->boot_on) { - rc = regulator_pca9420_enable(dev, NULL); + rc = regulator_pca9420_enable(dev); } if (cconfig->initial_mode) { rc = regulator_pca9420_set_mode(dev, cconfig->initial_mode); diff --git a/drivers/regulator/regulator_shell.c b/drivers/regulator/regulator_shell.c index a48dee5a830..bbade441d6e 100644 --- a/drivers/regulator/regulator_shell.c +++ b/drivers/regulator/regulator_shell.c @@ -14,7 +14,6 @@ LOG_MODULE_REGISTER(regulator_shell, CONFIG_REGULATOR_LOG_LEVEL); static int cmd_reg_en(const struct shell *sh, size_t argc, char **argv) { - struct onoff_client cli; const struct device *reg_dev; int ret; @@ -24,7 +23,7 @@ static int cmd_reg_en(const struct shell *sh, size_t argc, char **argv) return -ENODEV; } - ret = regulator_enable(reg_dev, &cli); + ret = regulator_enable(reg_dev); if (ret < 0) { shell_error(sh, "failed to enable regulator, error %d", ret); return ret; diff --git a/include/zephyr/drivers/regulator.h b/include/zephyr/drivers/regulator.h index 6e65b07b095..5c52248abce 100644 --- a/include/zephyr/drivers/regulator.h +++ b/include/zephyr/drivers/regulator.h @@ -19,7 +19,7 @@ #include #include -#include +#include #ifdef __cplusplus extern "C" { @@ -29,7 +29,7 @@ extern "C" { /** @brief Driver-specific API functions to support regulator control. */ __subsystem struct regulator_driver_api { - int (*enable)(const struct device *dev, struct onoff_client *cli); + int (*enable)(const struct device *dev); int (*disable)(const struct device *dev); int (*count_voltages)(const struct device *dev); int (*count_modes)(const struct device *dev); @@ -51,35 +51,41 @@ __subsystem struct regulator_driver_api { int (*mode_enable)(const struct device *dev, uint32_t mode); }; +/** + * @brief Common regulator data. + * + * This structure **must** be placed first in the driver's data structure. + */ +struct regulator_common_data { + /** Lock */ + struct k_mutex lock; + /** Reference count */ + int refcnt; +}; + +/** + * @brief Initialize common regulator data. + * + * This function **must** be called when driver is initialized. + * + * @param dev Regulator device instance. + */ +void regulator_common_data_init(const struct device *dev); + /** @endcond */ /** * @brief Enable a regulator. * - * Reference-counted request that a regulator be turned on. This is an - * asynchronous operation; if successfully initiated the result will be - * communicated through the @p cli parameter. - * - * A regulator is considered "on" when it has reached a stable/usable state. - * - * @funcprops \isr_ok \pre_kernel_ok + * Reference-counted request that a regulator be turned on. A regulator is + * considered "on" when it has reached a stable/usable state. * * @param dev Regulator device instance - * @param cli On-off client instance. This is used to notify the caller when the - * attempt to turn on the regulator has completed (can be `NULL`). * - * @retval 0 If enable request has been successfully initiated. - * @retval -errno Negative errno in case of failure (can be from onoff_request() - * or individual regulator drivers). + * @retval 0 If regulator has been successfully enabled. + * @retval -errno Negative errno in case of failure. */ -static inline int regulator_enable(const struct device *dev, - struct onoff_client *cli) -{ - const struct regulator_driver_api *api = - (const struct regulator_driver_api *)dev->api; - - return api->enable(dev, cli); -} +int regulator_enable(const struct device *dev); /** * @brief Disable a regulator. @@ -93,21 +99,12 @@ static inline int regulator_enable(const struct device *dev, * * This must be invoked at most once for each successful regulator_enable(). * - * @funcprops \isr_ok - * * @param dev Regulator device instance. * - * @retval 0 If enable request has been successfully initiated. - * @retval -errno Negative errno in case of failure (can be from onoff_release() - * or individual regulator drivers). + * @retval 0 If regulator has been successfully disabled. + * @retval -errno Negative errno in case of failure. */ -static inline int regulator_disable(const struct device *dev) -{ - const struct regulator_driver_api *api = - (const struct regulator_driver_api *)dev->api; - - return api->disable(dev); -} +int regulator_disable(const struct device *dev); /** * @brief Obtain the number of supported voltage levels. diff --git a/tests/drivers/regulator/fixed/src/main.c b/tests/drivers/regulator/fixed/src/main.c index 37d8dcf6241..78d5bc42c97 100644 --- a/tests/drivers/regulator/fixed/src/main.c +++ b/tests/drivers/regulator/fixed/src/main.c @@ -47,47 +47,6 @@ static const char *const pc_errstr[] = { [PC_OK] = "precheck OK", }; -static struct onoff_client cli; -static struct onoff_manager *callback_srv; -static struct onoff_client *callback_cli; -static uint32_t callback_state; -static int callback_res; -static onoff_client_callback callback_fn; - -static void callback(struct onoff_manager *srv, - struct onoff_client *cli, - uint32_t state, - int res) -{ - onoff_client_callback cb = callback_fn; - - callback_srv = srv; - callback_cli = cli; - callback_state = state; - callback_res = res; - callback_fn = NULL; - - if (cb != NULL) { - cb(srv, cli, state, res); - } -} - -static void reset_callback(void) -{ - callback_srv = NULL; - callback_cli = NULL; - callback_state = INT_MIN; - callback_res = 0; - callback_fn = NULL; -} - -static void reset_client(void) -{ - cli = (struct onoff_client){}; - reset_callback(); - sys_notify_init_callback(&cli.notify, callback); -} - static int reg_status(void) { return gpio_pin_get_dt(&check_gpio); @@ -197,30 +156,11 @@ ZTEST(regulator, test_basic) "not off at boot: %d", rs); } - reset_client(); - /* Turn it on */ - int rc = regulator_enable(reg_dev, &cli); - zassert_true(rc >= 0, - "first enable failed: %d", rc); + int rc = regulator_enable(reg_dev); - if (STARTUP_DELAY_US > 0) { - rc = sys_notify_fetch_result(&cli.notify, &rc); - - zassert_equal(rc, -EAGAIN, - "startup notify early: %d", rc); - - while (sys_notify_fetch_result(&cli.notify, &rc) == -EAGAIN) { - k_yield(); - } - } - - zassert_equal(callback_cli, &cli, - "callback not invoked"); - zassert_equal(callback_res, 0, - "callback res: %d", callback_res); - zassert_equal(callback_state, ONOFF_STATE_ON, - "callback state: 0x%x", callback_res); + zassert_equal(rc, 0, + "first enable failed: %d", rc); /* Make sure it's on */ @@ -230,18 +170,10 @@ ZTEST(regulator, test_basic) /* Turn it on again (another client) */ - reset_client(); - rc = regulator_enable(reg_dev, &cli); - zassert_true(rc >= 0, + rc = regulator_enable(reg_dev); + zassert_equal(rc, 0, "second enable failed: %d", rc); - zassert_equal(callback_cli, &cli, - "callback not invoked"); - zassert_true(callback_res >= 0, - "callback res: %d", callback_res); - zassert_equal(callback_state, ONOFF_STATE_ON, - "callback state: 0x%x", callback_res); - /* Make sure it's still on */ rs = reg_status(); @@ -263,7 +195,7 @@ ZTEST(regulator, test_basic) /* Turn it off again (no more clients) */ rc = regulator_disable(reg_dev); - zassert_true(rc >= 0, + zassert_equal(rc, 0, "first disable failed: %d", rc); /* On if and only if it can't be turned off */