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