1 /*
2 * Copyright 2020 Peter Bigot Consulting, LLC
3 * SPDX-License-Identifier: Apache-2.0
4 */
5
6 #include <zephyr.h>
7 #include <drivers/gpio.h>
8 #include <drivers/regulator.h>
9 #include <ztest.h>
10
11 #define REGULATOR_NODE DT_PATH(regulator)
12 #define CHECK_NODE DT_PATH(resources)
13
14 BUILD_ASSERT(DT_NODE_HAS_COMPAT_STATUS(REGULATOR_NODE, regulator_fixed, okay));
15 BUILD_ASSERT(DT_NODE_HAS_COMPAT_STATUS(CHECK_NODE, test_regulator_fixed, okay));
16
17 #define IS_REGULATOR_SYNC DT_NODE_HAS_COMPAT_STATUS(REGULATOR_NODE, regulator_fixed_sync, okay)
18 #define BOOT_ON DT_PROP(REGULATOR_NODE, regulator_boot_on)
19 #define ALWAYS_ON DT_PROP(REGULATOR_NODE, regulator_always_on)
20 #define STARTUP_DELAY_US DT_PROP(REGULATOR_NODE, startup_delay_us)
21 #define OFF_ON_DELAY_US DT_PROP(REGULATOR_NODE, off_on_delay_us)
22
23 static const struct device *check_gpio;
24 static const struct device *reg_dev;
25 static const gpio_pin_t check_pin = DT_GPIO_PIN(CHECK_NODE, check_gpios);
26 static enum {
27 PC_UNCHECKED,
28 PC_FAIL_REG_INIT,
29 PC_FAIL_DEVICES,
30 PC_FAIL_CFG_OUTPUT,
31 PC_FAIL_CFG_INPUT,
32 PC_FAIL_INACTIVE,
33 PC_FAIL_ACTIVE,
34 PC_FAIL_UNCONFIGURE,
35 PC_OK,
36 } precheck = PC_UNCHECKED;
37 static const char *const pc_errstr[] = {
38 [PC_UNCHECKED] = "precheck not verified",
39 [PC_FAIL_REG_INIT] = "regulator already initialized",
40 [PC_FAIL_DEVICES] = "bad GPIO devices",
41 [PC_FAIL_CFG_OUTPUT] = "failed to configure output",
42 [PC_FAIL_CFG_INPUT] = "failed to configure input",
43 [PC_FAIL_INACTIVE] = "inactive check failed",
44 [PC_FAIL_ACTIVE] = "active check failed",
45 [PC_FAIL_UNCONFIGURE] = "failed to disconnect regulator GPIO",
46 [PC_OK] = "precheck OK",
47 };
48
49 static struct onoff_client cli;
50 static struct onoff_manager *callback_srv;
51 static struct onoff_client *callback_cli;
52 static uint32_t callback_state;
53 static int callback_res;
54 static onoff_client_callback callback_fn;
55
callback(struct onoff_manager * srv,struct onoff_client * cli,uint32_t state,int res)56 static void callback(struct onoff_manager *srv,
57 struct onoff_client *cli,
58 uint32_t state,
59 int res)
60 {
61 onoff_client_callback cb = callback_fn;
62
63 callback_srv = srv;
64 callback_cli = cli;
65 callback_state = state;
66 callback_res = res;
67 callback_fn = NULL;
68
69 if (cb != NULL) {
70 cb(srv, cli, state, res);
71 }
72 }
73
reset_callback(void)74 static void reset_callback(void)
75 {
76 callback_srv = NULL;
77 callback_cli = NULL;
78 callback_state = INT_MIN;
79 callback_res = 0;
80 callback_fn = NULL;
81 }
82
reset_client(void)83 static void reset_client(void)
84 {
85 cli = (struct onoff_client){};
86 reset_callback();
87 sys_notify_init_callback(&cli.notify, callback);
88 }
89
reg_status(void)90 static int reg_status(void)
91 {
92 return gpio_pin_get(check_gpio, check_pin);
93 }
94
setup(const struct device * dev)95 static int setup(const struct device *dev)
96 {
97 #if 0
98 reg_dev = device_get_binding(DT_LABEL(REGULATOR_NODE));
99
100 /* todo: figure out how to verify the device hasn't been initialized. */
101 if (reg_dev != NULL) {
102 precheck = PC_FAIL_REG_INIT;
103 return -EBUSY;
104 }
105 #endif
106
107 /* Configure the regulator GPIO as an output inactive, and the check
108 * GPIO as an input, then start testing whether they track.
109 */
110
111 const char *reg_label = DT_GPIO_LABEL(REGULATOR_NODE, enable_gpios);
112 const struct device *reg_gpio = device_get_binding(reg_label);
113 const char *check_label = DT_GPIO_LABEL(CHECK_NODE, check_gpios);
114 const gpio_pin_t reg_pin = DT_GPIO_PIN(REGULATOR_NODE, enable_gpios);
115
116 check_gpio = device_get_binding(check_label);
117
118 if ((reg_gpio == NULL) || (check_gpio == NULL)) {
119 precheck = PC_FAIL_DEVICES;
120 return -EINVAL;
121 }
122
123 int rc = gpio_pin_configure(reg_gpio, reg_pin,
124 GPIO_OUTPUT_INACTIVE
125 | DT_GPIO_FLAGS(REGULATOR_NODE, enable_gpios));
126 if (rc != 0) {
127 precheck = PC_FAIL_CFG_OUTPUT;
128 return -EIO;
129 }
130
131 rc = gpio_pin_configure(check_gpio, check_pin,
132 GPIO_INPUT
133 | DT_GPIO_FLAGS(CHECK_NODE, check_gpios));
134 if (rc != 0) {
135 precheck = PC_FAIL_CFG_INPUT;
136 return -EIO;
137 }
138
139 rc = reg_status();
140
141 if (rc != 0) { /* should be inactive */
142 precheck = PC_FAIL_INACTIVE;
143 return -EIO;
144 }
145
146 rc = gpio_pin_set(reg_gpio, reg_pin, true);
147
148 if (rc == 0) {
149 rc = reg_status();
150 }
151
152 if (rc != 1) { /* should be active */
153 precheck = PC_FAIL_ACTIVE;
154 return -EIO;
155 }
156
157 rc = gpio_pin_configure(reg_gpio, reg_pin, GPIO_DISCONNECTED);
158 if (rc == -ENOTSUP) {
159 rc = gpio_pin_configure(reg_gpio, reg_pin, GPIO_INPUT);
160 }
161 if (rc == 0) {
162 rc = reg_status();
163 }
164
165 if (rc != 0) { /* should be inactive */
166 precheck = PC_FAIL_UNCONFIGURE;
167 return -EIO;
168 }
169
170 precheck = PC_OK;
171
172 return rc;
173 }
174
175 /* The regulator driver initializes in POST_KERNEL since it has
176 * thread-related stuff in it. We need to verify the shorted signals
177 * required by the test before the driver configures its GPIO. This
178 * should be done late PRE_KERNEL_9, but it can't because Nordic and
179 * possibly other systems initialize GPIO drivers post-kernel. */
180 BUILD_ASSERT(CONFIG_REGULATOR_FIXED_INIT_PRIORITY > 74);
181 SYS_INIT(setup, POST_KERNEL, 74);
182
test_preconditions(void)183 static void test_preconditions(void)
184 {
185 zassert_equal(precheck, PC_OK,
186 "precheck failed: %s",
187 pc_errstr[precheck]);
188
189 zassert_not_equal(reg_dev, NULL,
190 "no regulator device");
191 }
192
test_basic(void)193 static void test_basic(void)
194 {
195 zassert_equal(precheck, PC_OK,
196 "precheck failed: %s",
197 pc_errstr[precheck]);
198
199 zassert_not_equal(reg_dev, NULL,
200 "no regulator device");
201
202 int rs = reg_status();
203
204 /* Initial state: on if and only if it's always on or was enabled at
205 * boot.
206 */
207
208 if (IS_ENABLED(BOOT_ON) || IS_ENABLED(ALWAYS_ON)) {
209 zassert_equal(rs, 1,
210 "not on at boot: %d", rs);
211 } else {
212 zassert_equal(rs, 0,
213 "not off at boot: %d", rs);
214 }
215
216 reset_client();
217
218 /* Turn it on */
219 int rc = regulator_enable(reg_dev, &cli);
220 zassert_true(rc >= 0,
221 "first enable failed: %d", rc);
222
223 if (STARTUP_DELAY_US > 0) {
224 rc = sys_notify_fetch_result(&cli.notify, &rc);
225
226 zassert_equal(rc, -EAGAIN,
227 "startup notify early: %d", rc);
228
229 while (sys_notify_fetch_result(&cli.notify, &rc) == -EAGAIN) {
230 k_yield();
231 }
232 }
233
234 zassert_equal(callback_cli, &cli,
235 "callback not invoked");
236 zassert_equal(callback_res, 0,
237 "callback res: %d", callback_res);
238 zassert_equal(callback_state, ONOFF_STATE_ON,
239 "callback state: 0x%x", callback_res);
240
241 /* Make sure it's on */
242
243 rs = reg_status();
244 zassert_equal(rs, 1,
245 "bad on state: %d", rs);
246
247 /* Turn it on again (another client) */
248
249 reset_client();
250 rc = regulator_enable(reg_dev, &cli);
251 zassert_true(rc >= 0,
252 "second enable failed: %d", rc);
253
254 zassert_equal(callback_cli, &cli,
255 "callback not invoked");
256 zassert_true(callback_res >= 0,
257 "callback res: %d", callback_res);
258 zassert_equal(callback_state, ONOFF_STATE_ON,
259 "callback state: 0x%x", callback_res);
260
261 /* Make sure it's still on */
262
263 rs = reg_status();
264 zassert_equal(rs, 1,
265 "bad 2x on state: %d", rs);
266
267 /* Turn it off once (still has a client) */
268
269 rc = regulator_disable(reg_dev);
270 zassert_true(rc >= 0,
271 "first disable failed: %d", rc);
272
273 /* Make sure it's still on */
274
275 rs = reg_status();
276 zassert_equal(rs, 1,
277 "bad 2x on 1x off state: %d", rs);
278
279 /* Turn it off again (no more clients) */
280
281 rc = regulator_disable(reg_dev);
282 zassert_true(rc >= 0,
283 "first disable failed: %d", rc);
284
285 /* On if and only if it can't be turned off */
286
287 rs = reg_status();
288 zassert_equal(rs, IS_ENABLED(ALWAYS_ON),
289 "bad 2x on 2x off state: %d", rs);
290 }
291
test_main(void)292 void test_main(void)
293 {
294 const char * const compats[] = DT_PROP(REGULATOR_NODE, compatible);
295 reg_dev = device_get_binding(DT_LABEL(REGULATOR_NODE));
296
297 printk("reg %p gpio %p\n", reg_dev, check_gpio);
298 TC_PRINT("Regulator: %s%s%s\n", compats[0],
299 IS_ENABLED(BOOT_ON) ? ", boot-on" : "",
300 IS_ENABLED(ALWAYS_ON) ? ", always-on" : "");
301 TC_PRINT("startup-delay: %u us\n", STARTUP_DELAY_US);
302 TC_PRINT("off-on-delay: %u us\n", OFF_ON_DELAY_US);
303
304 ztest_test_suite(regulator_test,
305 ztest_unit_test(test_preconditions),
306 ztest_unit_test(test_basic));
307 ztest_run_test_suite(regulator_test);
308 }
309