blob: 08630dd8c1de5664877f1947b525164efebdf477 [file] [log] [blame]
/*
* Copyright 2020 Peter Bigot Consulting, LLC
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/drivers/gpio.h>
#include <zephyr/drivers/regulator.h>
#include <zephyr/ztest.h>
#define REGULATOR_NODE DT_PATH(regulator)
#define CHECK_NODE DT_PATH(resources)
BUILD_ASSERT(DT_NODE_HAS_COMPAT_STATUS(REGULATOR_NODE, regulator_fixed, okay));
BUILD_ASSERT(DT_NODE_HAS_COMPAT_STATUS(CHECK_NODE, test_regulator_fixed, okay));
#define IS_REGULATOR_SYNC DT_NODE_HAS_COMPAT_STATUS(REGULATOR_NODE, regulator_fixed_sync, okay)
#define BOOT_ON DT_PROP(REGULATOR_NODE, regulator_boot_on)
#define ALWAYS_ON DT_PROP(REGULATOR_NODE, regulator_always_on)
#define STARTUP_DELAY_US DT_PROP(REGULATOR_NODE, startup_delay_us)
#define OFF_ON_DELAY_US DT_PROP(REGULATOR_NODE, off_on_delay_us)
static const struct gpio_dt_spec reg_gpio = GPIO_DT_SPEC_GET(REGULATOR_NODE, enable_gpios);
static const struct gpio_dt_spec check_gpio = GPIO_DT_SPEC_GET(CHECK_NODE, check_gpios);
static const struct device *const reg_dev = DEVICE_DT_GET(REGULATOR_NODE);
static enum {
PC_UNCHECKED,
PC_FAIL_REG_DEV_READY,
PC_FAIL_DEVICES_READY,
PC_FAIL_CFG_OUTPUT,
PC_FAIL_CFG_INPUT,
PC_FAIL_INACTIVE,
PC_FAIL_ACTIVE,
PC_FAIL_UNCONFIGURE,
PC_OK,
} precheck = PC_UNCHECKED;
static const char *const pc_errstr[] = {
[PC_UNCHECKED] = "precheck not verified",
[PC_FAIL_REG_DEV_READY] = "regulator device not ready",
[PC_FAIL_DEVICES_READY] = "GPIO devices not ready",
[PC_FAIL_CFG_OUTPUT] = "failed to configure output",
[PC_FAIL_CFG_INPUT] = "failed to configure input",
[PC_FAIL_INACTIVE] = "inactive check failed",
[PC_FAIL_ACTIVE] = "active check failed",
[PC_FAIL_UNCONFIGURE] = "failed to disconnect regulator GPIO",
[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);
}
static int setup(const struct device *dev)
{
if (!device_is_ready(reg_dev)) {
precheck = PC_FAIL_REG_DEV_READY;
return -ENODEV;
}
/* Configure the regulator GPIO as an output inactive, and the check
* GPIO as an input, then start testing whether they track.
*/
if (!device_is_ready(reg_gpio.port)) {
precheck = PC_FAIL_DEVICES_READY;
return -ENODEV;
}
if (!device_is_ready(check_gpio.port)) {
precheck = PC_FAIL_DEVICES_READY;
return -ENODEV;
}
int rc = gpio_pin_configure_dt(&reg_gpio, GPIO_OUTPUT_INACTIVE);
if (rc != 0) {
precheck = PC_FAIL_CFG_OUTPUT;
return -EIO;
}
rc = gpio_pin_configure_dt(&check_gpio, GPIO_INPUT);
if (rc != 0) {
precheck = PC_FAIL_CFG_INPUT;
return -EIO;
}
rc = reg_status();
if (rc != 0) { /* should be inactive */
precheck = PC_FAIL_INACTIVE;
return -EIO;
}
rc = gpio_pin_set_dt(&reg_gpio, true);
if (rc == 0) {
rc = reg_status();
}
if (rc != 1) { /* should be active */
precheck = PC_FAIL_ACTIVE;
return -EIO;
}
rc = gpio_pin_configure_dt(&reg_gpio, GPIO_DISCONNECTED);
if (rc == -ENOTSUP) {
rc = gpio_pin_configure_dt(&reg_gpio, GPIO_INPUT);
}
if (rc == 0) {
rc = reg_status();
}
if (rc != 0) { /* should be inactive */
precheck = PC_FAIL_UNCONFIGURE;
return -EIO;
}
precheck = PC_OK;
return rc;
}
/* The regulator driver initializes in POST_KERNEL since it has
* thread-related stuff in it. We need to verify the shorted signals
* required by the test before the driver configures its GPIO. This
* should be done late PRE_KERNEL_9, but it can't because Nordic and
* possibly other systems initialize GPIO drivers post-kernel. */
BUILD_ASSERT(CONFIG_REGULATOR_FIXED_INIT_PRIORITY > 74);
SYS_INIT(setup, POST_KERNEL, 74);
static void test_preconditions(void)
{
zassert_equal(precheck, PC_OK,
"precheck failed: %s",
pc_errstr[precheck]);
}
ZTEST(regulator, test_basic)
{
zassert_equal(precheck, PC_OK,
"precheck failed: %s",
pc_errstr[precheck]);
int rs = reg_status();
/* Initial state: on if and only if it's always on or was enabled at
* boot.
*/
if (IS_ENABLED(BOOT_ON) || IS_ENABLED(ALWAYS_ON)) {
zassert_equal(rs, 1,
"not on at boot: %d", rs);
} else {
zassert_equal(rs, 0,
"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);
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);
/* Make sure it's on */
rs = reg_status();
zassert_equal(rs, 1,
"bad on state: %d", rs);
/* Turn it on again (another client) */
reset_client();
rc = regulator_enable(reg_dev, &cli);
zassert_true(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();
zassert_equal(rs, 1,
"bad 2x on state: %d", rs);
/* Turn it off once (still has a client) */
rc = regulator_disable(reg_dev);
zassert_true(rc >= 0,
"first disable failed: %d", rc);
/* Make sure it's still on */
rs = reg_status();
zassert_equal(rs, 1,
"bad 2x on 1x off state: %d", rs);
/* Turn it off again (no more clients) */
rc = regulator_disable(reg_dev);
zassert_true(rc >= 0,
"first disable failed: %d", rc);
/* On if and only if it can't be turned off */
rs = reg_status();
zassert_equal(rs, IS_ENABLED(ALWAYS_ON),
"bad 2x on 2x off state: %d", rs);
}
void *regulator_setup(void)
{
const char * const compats[] = DT_PROP(REGULATOR_NODE, compatible);
printk("reg %p gpio %p\n", reg_dev, check_gpio.port);
TC_PRINT("Regulator: %s%s%s\n", compats[0],
IS_ENABLED(BOOT_ON) ? ", boot-on" : "",
IS_ENABLED(ALWAYS_ON) ? ", always-on" : "");
TC_PRINT("startup-delay: %u us\n", STARTUP_DELAY_US);
TC_PRINT("off-on-delay: %u us\n", OFF_ON_DELAY_US);
test_preconditions();
return NULL;
}
ZTEST_SUITE(regulator, NULL, regulator_setup, NULL, NULL, NULL);