1 /*
2  * Copyright (c) 2022 Whisper.ai
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdio.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/device.h>
10 #include <zephyr/drivers/gpio.h>
11 #include <zephyr/drivers/counter.h>
12 #include <zephyr/pm/pm.h>
13 #include <zephyr/pm/policy.h>
14 
15 #define BUSY_WAIT_S 2U
16 #define SLEEP_S 2U
17 #define SOFT_OFF_S 10U
18 
19 #define SW0_NODE DT_ALIAS(sw0)
20 #if !DT_NODE_HAS_STATUS_OKAY(SW0_NODE)
21 #error "Unsupported board: sw0 devicetree alias is not defined"
22 #endif
23 
24 #define SNVS_RTC_NODE DT_NODELABEL(snvs_rtc)
25 #if !DT_NODE_HAS_STATUS_OKAY(SNVS_RTC_NODE)
26 #error "Unsupported board: snvs_rtc node is not enabled"
27 #endif
28 
29 #define SNVS_LP_RTC_ALARM_ID 1
30 
31 static const struct gpio_dt_spec button = GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, { 0 });
32 static const struct device *const snvs_rtc_dev = DEVICE_DT_GET(SNVS_RTC_NODE);
33 
main(void)34 int main(void)
35 {
36 	printk("\n%s system off demo\n", CONFIG_BOARD);
37 
38 	if (!gpio_is_ready_dt(&button)) {
39 		printk("Error: button device %s is not ready\n", button.port->name);
40 		return 0;
41 	}
42 
43 	/* Configure to generate PORT event (wakeup) on button press. */
44 	/* No external pull-up on the user button, so configure one here */
45 	int ret = gpio_pin_configure_dt(&button, GPIO_INPUT | GPIO_PULL_UP);
46 
47 	if (ret != 0) {
48 		printk("Error %d: failed to configure %s pin %d\n", ret, button.port->name,
49 		       button.pin);
50 		return 0;
51 	}
52 
53 	ret = gpio_pin_interrupt_configure_dt(&button, GPIO_INT_LEVEL_LOW);
54 	if (ret != 0) {
55 		printk("Error %d: failed to configure interrupt on %s pin %d\n", ret,
56 		       button.port->name, button.pin);
57 		return 0;
58 	}
59 
60 	printk("Busy-wait %u s\n", BUSY_WAIT_S);
61 	k_busy_wait(BUSY_WAIT_S * USEC_PER_SEC);
62 
63 	printk("Sleep %u s\n", SLEEP_S);
64 	k_sleep(K_SECONDS(SLEEP_S));
65 
66 	uint32_t sleep_ticks = counter_us_to_ticks(snvs_rtc_dev, SOFT_OFF_S * 1000ULL * 1000ULL);
67 
68 	if (sleep_ticks == 0) {
69 		/* counter_us_to_ticks will round down the number of ticks to the nearest int. */
70 		/* Ensure at least one tick is used in the RTC */
71 		sleep_ticks++;
72 	}
73 	const struct counter_alarm_cfg alarm_cfg = {
74 		.ticks = sleep_ticks,
75 		.flags = 0,
76 	};
77 
78 	ret = counter_set_channel_alarm(snvs_rtc_dev, SNVS_LP_RTC_ALARM_ID, &alarm_cfg);
79 	if (ret != 0) {
80 		printk("Could not rtc alarm.\n");
81 		return 0;
82 	}
83 	printk("RTC Alarm set for %llu seconds to wake from soft-off.\n",
84 	       counter_ticks_to_us(snvs_rtc_dev, alarm_cfg.ticks) / (1000ULL * 1000ULL));
85 	printk("Entering system off; press %s to restart sooner\n", button.port->name);
86 
87 	pm_state_force(0u, &(struct pm_state_info){ PM_STATE_SOFT_OFF, 0, 0 });
88 
89 	/* Now we need to go sleep. This will let the idle thread runs and
90 	 * the pm subsystem will use the forced state. To confirm that the
91 	 * forced state is used, lets set the same timeout used previously.
92 	 */
93 	k_sleep(K_SECONDS(SLEEP_S));
94 
95 	printk("ERROR: System off failed\n");
96 	while (true) {
97 		/* spin to avoid fall-off behavior */
98 	}
99 	return 0;
100 }
101