1 /*
2  * Copyright 2024 Google LLC
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <stdio.h>
8 #include <zephyr/shell/shell.h>
9 #include <zephyr/pm/device.h>
10 #include <zephyr/pm/device_runtime.h>
11 
pm_device_filter(const struct device * dev)12 static bool pm_device_filter(const struct device *dev)
13 {
14 	return dev->pm != NULL;
15 }
16 
device_name_get(size_t idx,struct shell_static_entry * entry)17 static void device_name_get(size_t idx, struct shell_static_entry *entry)
18 {
19 	const struct device *dev = shell_device_filter(idx, pm_device_filter);
20 
21 	entry->syntax = (dev != NULL) ? dev->name : NULL;
22 	entry->handler = NULL;
23 	entry->help = NULL;
24 	entry->subcmd = NULL;
25 }
26 
27 SHELL_DYNAMIC_CMD_CREATE(dsub_device_name, device_name_get);
28 
pm_cmd_suspend(const struct shell * sh,size_t argc,char * argv[])29 static int pm_cmd_suspend(const struct shell *sh, size_t argc, char *argv[])
30 {
31 	const struct device *dev;
32 	int ret;
33 
34 	dev = shell_device_get_binding(argv[1]);
35 	if (dev == NULL) {
36 		shell_error(sh, "Invalid device: %s", argv[1]);
37 		return -ENODEV;
38 	}
39 
40 	if (pm_device_runtime_is_enabled(dev)) {
41 		shell_error(sh, "Device %s uses runtime PM, use the runtime functions instead",
42 			    dev->name);
43 		return -EINVAL;
44 	}
45 
46 	ret = pm_device_action_run(dev, PM_DEVICE_ACTION_SUSPEND);
47 	if (ret < 0) {
48 		shell_error(sh, "Device %s error: %d", "suspend", ret);
49 		return ret;
50 	}
51 
52 	return 0;
53 }
54 
pm_cmd_resume(const struct shell * sh,size_t argc,char * argv[])55 static int pm_cmd_resume(const struct shell *sh, size_t argc, char *argv[])
56 {
57 	const struct device *dev;
58 	int ret;
59 
60 	dev = shell_device_get_binding(argv[1]);
61 	if (dev == NULL) {
62 		shell_error(sh, "Invalid device: %s", argv[1]);
63 		return -ENODEV;
64 	}
65 
66 	if (pm_device_runtime_is_enabled(dev)) {
67 		shell_error(sh, "Device %s uses runtime PM, use the runtime functions instead",
68 			    dev->name);
69 		return -EINVAL;
70 	}
71 
72 	ret = pm_device_action_run(dev, PM_DEVICE_ACTION_RESUME);
73 	if (ret < 0) {
74 		shell_error(sh, "Device %s error: %d", "resume", ret);
75 		return ret;
76 	}
77 
78 	return 0;
79 }
80 
81 #if defined(CONFIG_PM_DEVICE_RUNTIME)
pm_cmd_runtime_get(const struct shell * sh,size_t argc,char * argv[])82 static int pm_cmd_runtime_get(const struct shell *sh, size_t argc, char *argv[])
83 {
84 	const struct device *dev;
85 	int ret;
86 
87 	dev = shell_device_get_binding(argv[1]);
88 	if (dev == NULL) {
89 		shell_error(sh, "Invalid device: %s", argv[1]);
90 		return -ENODEV;
91 	}
92 
93 	if (!pm_device_runtime_is_enabled(dev)) {
94 		shell_error(sh, "Device %s is not using runtime PM", dev->name);
95 		return -EINVAL;
96 	}
97 
98 	ret = pm_device_runtime_get(dev);
99 	if (ret < 0) {
100 		shell_error(sh, "Device %s error: %d", "runtime get", ret);
101 		return ret;
102 	}
103 
104 	return 0;
105 }
106 
pm_cmd_runtime_put(const struct shell * sh,size_t argc,char * argv[])107 static int pm_cmd_runtime_put(const struct shell *sh, size_t argc, char *argv[])
108 {
109 	const struct device *dev;
110 	int ret;
111 
112 	dev = shell_device_get_binding(argv[1]);
113 	if (dev == NULL) {
114 		shell_error(sh, "Invalid device: %s", argv[1]);
115 		return -ENODEV;
116 	}
117 
118 	if (!pm_device_runtime_is_enabled(dev)) {
119 		shell_error(sh, "Device %s is not using runtime PM", dev->name);
120 		return -EINVAL;
121 	}
122 
123 	ret = pm_device_runtime_put(dev);
124 	if (ret < 0) {
125 		shell_error(sh, "Device %s error: %d", "runtime put", ret);
126 		return ret;
127 	}
128 
129 	return 0;
130 }
131 
pm_cmd_runtime_put_async(const struct shell * sh,size_t argc,char * argv[])132 static int pm_cmd_runtime_put_async(const struct shell *sh, size_t argc, char *argv[])
133 {
134 	const struct device *dev;
135 	int ret;
136 
137 	dev = shell_device_get_binding(argv[1]);
138 	if (dev == NULL) {
139 		shell_error(sh, "Invalid device: %s", argv[1]);
140 		return -ENODEV;
141 	}
142 
143 	if (!pm_device_runtime_is_enabled(dev)) {
144 		shell_error(sh, "Device %s is not using runtime PM", dev->name);
145 		return -EINVAL;
146 	}
147 
148 	ret = pm_device_runtime_put_async(dev, K_NO_WAIT);
149 	if (ret < 0) {
150 		shell_error(sh, "Device %s error: %d", "runtime put async", ret);
151 		return ret;
152 	}
153 
154 	return 0;
155 }
156 #endif /* CONFIG_PM_DEVICE_RUNTIME */
157 
158 SHELL_STATIC_SUBCMD_SET_CREATE(
159 	sub_pm_cmds,
160 	SHELL_CMD_ARG(suspend, &dsub_device_name,
161 		      "Call the PM suspend action on a device",
162 		      pm_cmd_suspend, 2, 0),
163 	SHELL_CMD_ARG(resume, &dsub_device_name,
164 		      "Call the PM resume action on a device",
165 		      pm_cmd_resume, 2, 0),
166 #if defined(CONFIG_PM_DEVICE_RUNTIME)
167 	SHELL_CMD_ARG(runtime-get, &dsub_device_name,
168 		      "Call the PM runtime get on a device",
169 		      pm_cmd_runtime_get, 2, 0),
170 	SHELL_CMD_ARG(runtime-put, &dsub_device_name,
171 		      "Call the PM runtime put on a device",
172 		      pm_cmd_runtime_put, 2, 0),
173 	SHELL_CMD_ARG(runtime-put-async, &dsub_device_name,
174 		      "Call the PM runtime put async on a device",
175 		      pm_cmd_runtime_put_async, 2, 0),
176 #endif /* CONFIG_PM_DEVICE_RUNTIME */
177 	SHELL_SUBCMD_SET_END);
178 
179 SHELL_CMD_REGISTER(pm, &sub_pm_cmds, "PM commands", NULL);
180