1 /*
2  * Copyright (c) 2023 STMicroelectronics
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/hwinfo.h>
8 #include <zephyr/kernel.h>
9 #include <zephyr/device.h>
10 #include <zephyr/devicetree.h>
11 #include <zephyr/drivers/gpio.h>
12 #include <zephyr/sys/printk.h>
13 #include <zephyr/pm/pm.h>
14 #include <zephyr/sys/poweroff.h>
15 #include <stm32_ll_pwr.h>
16 
17 #if !defined(CONFIG_SOC_SERIES_STM32L4X)
18 #error Not implemented for other series
19 #endif  /* CONFIG_SOC_SERIES_STM32L4X */
20 
21 #define STACKSIZE 1024
22 #define PRIORITY 7
23 #define SLEEP_TIME_MS   3000
24 
25 #define SW0_NODE	DT_ALIAS(sw0)
26 #if !DT_NODE_HAS_STATUS_OKAY(SW0_NODE)
27 #error "Unsupported board: sw0 devicetree alias is not defined"
28 #endif
29 
30 /* Semaphore used to control button pressed value */
31 static struct k_sem button_sem;
32 
33 static int led_is_on;
34 
35 static const struct gpio_dt_spec button =
36 	GPIO_DT_SPEC_GET_OR(SW0_NODE, gpios, {0});
37 
38 static const struct gpio_dt_spec led =
39 	GPIO_DT_SPEC_GET(DT_ALIAS(led0), gpios);
40 
41 static struct gpio_callback button_cb_data;
42 
config_wakeup_features(void)43 void config_wakeup_features(void)
44 {
45 	/* Configure wake-up features */
46 	/* WKUP2(PC13) only , - active low, pull-up */
47 	/* Set pull-ups for standby modes */
48 	LL_PWR_EnableGPIOPullUp(LL_PWR_GPIO_C, LL_PWR_GPIO_BIT_13);
49 	LL_PWR_IsWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN2);
50 	/* Enable pin pull up configurations and wakeup pins */
51 	LL_PWR_EnablePUPDCfg();
52 	LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN2);
53 	/* Clear wakeup flags */
54 	LL_PWR_ClearFlag_WU();
55 }
56 
button_pressed(const struct device * dev,struct gpio_callback * cb,uint32_t pins)57 void button_pressed(const struct device *dev, struct gpio_callback *cb,
58 		    uint32_t pins)
59 {
60 	k_sem_give(&button_sem);
61 }
62 
thread_poweroff_standby_mode(void)63 void thread_poweroff_standby_mode(void)
64 {
65 	k_sem_init(&button_sem, 0, 1);
66 	k_sem_take(&button_sem, K_FOREVER);
67 	gpio_pin_configure(led.port, led.pin, GPIO_DISCONNECTED);
68 	printk("User button pressed\n");
69 	config_wakeup_features();
70 	if (led_is_on == false) {
71 		printk("Powering off\n");
72 		printk("Release the user button to wake-up\n\n");
73 #ifdef CONFIG_LOG
74 		k_msleep(2000);
75 #endif /* CONFIG_LOG */
76 		sys_poweroff();
77 		/* powered off until wakeup line activated */
78 	} else {
79 		printk("Standby Mode requested\n");
80 		printk("Release the user button to exit from Standby Mode\n\n");
81 #ifdef CONFIG_LOG
82 		k_msleep(2000);
83 #endif /* CONFIG_LOG */
84 		pm_state_force(0u, &(struct pm_state_info) {PM_STATE_STANDBY, 0, 0});
85 		/* stay in Standby mode until wakeup line activated */
86 	}
87 }
88 
89 K_THREAD_DEFINE(thread_poweroff_standby_mode_id, STACKSIZE, thread_poweroff_standby_mode,
90 	NULL, NULL, NULL, PRIORITY, 0, 0);
91 
main(void)92 int main(void)
93 {
94 	int ret;
95 	uint32_t cause;
96 
97 	hwinfo_get_reset_cause(&cause);
98 	hwinfo_clear_reset_cause();
99 
100 	if (cause == RESET_LOW_POWER_WAKE)	{
101 		hwinfo_clear_reset_cause();
102 		printk("\nReset cause: Standby mode\n\n");
103 	}
104 
105 	if (cause == (RESET_PIN | RESET_BROWNOUT)) {
106 		printk("\nReset cause: Shutdown mode or power up\n\n");
107 	}
108 
109 	if (cause == RESET_PIN) {
110 		printk("\nReset cause: Reset pin\n\n");
111 	}
112 
113 
114 	__ASSERT_NO_MSG(gpio_is_ready_dt(&led));
115 	if (!gpio_is_ready_dt(&button)) {
116 		printk("Error: button device %s is not ready\n",
117 			button.port->name);
118 		return 0;
119 	}
120 
121 	ret = gpio_pin_configure_dt(&button, GPIO_INPUT);
122 	if (ret != 0) {
123 		printk("Error %d: failed to configure %s pin %d\n",
124 			ret, button.port->name, button.pin);
125 		return 0;
126 	}
127 
128 	ret = gpio_pin_interrupt_configure_dt(&button,
129 					      GPIO_INT_EDGE_TO_ACTIVE);
130 	if (ret != 0) {
131 		printk("Error %d: failed to configure interrupt on %s pin %d\n",
132 			ret, button.port->name, button.pin);
133 		return 0;
134 	}
135 
136 	gpio_init_callback(&button_cb_data, button_pressed, BIT(button.pin));
137 	gpio_add_callback(button.port, &button_cb_data);
138 
139 	printk("Device ready: %s\n\n\n", CONFIG_BOARD);
140 
141 	printk("Press and hold the user button:\n");
142 	printk("  when LED2 is OFF to power off\n");
143 	printk("  when LED2 is ON to enter to Standby Mode\n\n");
144 
145 	led_is_on = true;
146 	while (true) {
147 		gpio_pin_configure_dt(&led, GPIO_OUTPUT_ACTIVE);
148 		gpio_pin_set(led.port, led.pin, (int)led_is_on);
149 		k_msleep(SLEEP_TIME_MS);
150 		led_is_on = !led_is_on;
151 	}
152 
153 	return 0;
154 }
155