xref: /linux/drivers/firmware/arm_scmi/scmi_power_control.c (revision 0f46f50845ce75bfaba62df0421084d23bb6a72f)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * SCMI Generic SystemPower Control driver.
4  *
5  * Copyright (C) 2020-2022 ARM Ltd.
6  */
7 /*
8  * In order to handle platform originated SCMI SystemPower requests (like
9  * shutdowns or cold/warm resets) we register an SCMI Notification notifier
10  * block to react when such SCMI SystemPower events are emitted by platform.
11  *
12  * Once such a notification is received we act accordingly to perform the
13  * required system transition depending on the kind of request.
14  *
15  * Graceful requests are routed to userspace through the same API methods
16  * (orderly_poweroff/reboot()) used by ACPI when handling ACPI Shutdown bus
17  * events.
18  *
19  * Direct forceful requests are not supported since are not meant to be sent
20  * by the SCMI platform to an OSPM like Linux.
21  *
22  * Additionally, graceful request notifications can carry an optional timeout
23  * field stating the maximum amount of time allowed by the platform for
24  * completion after which they are converted to forceful ones: the assumption
25  * here is that even graceful requests can be upper-bound by a maximum final
26  * timeout strictly enforced by the platform itself which can ultimately cut
27  * the power off at will anytime; in order to avoid such extreme scenario, we
28  * track progress of graceful requests through the means of a reboot notifier
29  * converting timed-out graceful requests to forceful ones, so at least we
30  * try to perform a clean sync and shutdown/restart before the power is cut.
31  *
32  * Given the peculiar nature of SCMI SystemPower protocol, that is being in
33  * charge of triggering system wide shutdown/reboot events, there should be
34  * only one SCMI platform actively emitting SystemPower events.
35  * For this reason the SCMI core takes care to enforce the creation of one
36  * single unique device associated to the SCMI System Power protocol; no matter
37  * how many SCMI platforms are defined on the system, only one can be designated
38  * to support System Power: as a consequence this driver will never be probed
39  * more than once.
40  *
41  * For similar reasons as soon as the first valid SystemPower is received by
42  * this driver and the shutdown/reboot is started, any further notification
43  * possibly emitted by the platform will be ignored.
44  */
45 
46 #include <linux/math.h>
47 #include <linux/module.h>
48 #include <linux/mutex.h>
49 #include <linux/pm.h>
50 #include <linux/printk.h>
51 #include <linux/reboot.h>
52 #include <linux/scmi_protocol.h>
53 #include <linux/slab.h>
54 #include <linux/suspend.h>
55 #include <linux/time64.h>
56 #include <linux/timer.h>
57 #include <linux/types.h>
58 #include <linux/workqueue.h>
59 
60 #ifndef MODULE
61 #include <linux/fs.h>
62 #endif
63 
64 enum scmi_syspower_state {
65 	SCMI_SYSPOWER_IDLE,
66 	SCMI_SYSPOWER_IN_PROGRESS,
67 	SCMI_SYSPOWER_REBOOTING
68 };
69 
70 /**
71  * struct scmi_syspower_conf  -  Common configuration
72  *
73  * @dev: A reference device
74  * @state: Current SystemPower state
75  * @state_mtx: @state related mutex
76  * @required_transition: The requested transition as decribed in the received
77  *			 SCMI SystemPower notification
78  * @userspace_nb: The notifier_block registered against the SCMI SystemPower
79  *		  notification to start the needed userspace interactions.
80  * @reboot_nb: A notifier_block optionally used to track reboot progress
81  * @forceful_work: A worker used to trigger a forceful transition once a
82  *		   graceful has timed out.
83  * @suspend_work: A worker used to trigger system suspend
84  */
85 struct scmi_syspower_conf {
86 	struct device *dev;
87 	enum scmi_syspower_state state;
88 	/* Protect access to state */
89 	struct mutex state_mtx;
90 	enum scmi_system_events required_transition;
91 
92 	struct notifier_block userspace_nb;
93 	struct notifier_block reboot_nb;
94 
95 	struct delayed_work forceful_work;
96 	struct work_struct suspend_work;
97 };
98 
99 #define userspace_nb_to_sconf(x)	\
100 	container_of(x, struct scmi_syspower_conf, userspace_nb)
101 
102 #define reboot_nb_to_sconf(x)		\
103 	container_of(x, struct scmi_syspower_conf, reboot_nb)
104 
105 #define dwork_to_sconf(x)		\
106 	container_of(x, struct scmi_syspower_conf, forceful_work)
107 
108 /**
109  * scmi_reboot_notifier  - A reboot notifier to catch an ongoing successful
110  * system transition
111  * @nb: Reference to the related notifier block
112  * @reason: The reason for the ongoing reboot
113  * @__unused: The cmd being executed on a restart request (unused)
114  *
115  * When an ongoing system transition is detected, compatible with the one
116  * requested by SCMI, cancel the delayed work.
117  *
118  * Return: NOTIFY_OK in any case
119  */
scmi_reboot_notifier(struct notifier_block * nb,unsigned long reason,void * __unused)120 static int scmi_reboot_notifier(struct notifier_block *nb,
121 				unsigned long reason, void *__unused)
122 {
123 	struct scmi_syspower_conf *sc = reboot_nb_to_sconf(nb);
124 
125 	mutex_lock(&sc->state_mtx);
126 	switch (reason) {
127 	case SYS_HALT:
128 	case SYS_POWER_OFF:
129 		if (sc->required_transition == SCMI_SYSTEM_SHUTDOWN)
130 			sc->state = SCMI_SYSPOWER_REBOOTING;
131 		break;
132 	case SYS_RESTART:
133 		if (sc->required_transition == SCMI_SYSTEM_COLDRESET ||
134 		    sc->required_transition == SCMI_SYSTEM_WARMRESET)
135 			sc->state = SCMI_SYSPOWER_REBOOTING;
136 		break;
137 	default:
138 		break;
139 	}
140 
141 	if (sc->state == SCMI_SYSPOWER_REBOOTING) {
142 		dev_dbg(sc->dev, "Reboot in progress...cancel delayed work.\n");
143 		cancel_delayed_work_sync(&sc->forceful_work);
144 	}
145 	mutex_unlock(&sc->state_mtx);
146 
147 	return NOTIFY_OK;
148 }
149 
150 /**
151  * scmi_request_forceful_transition  - Request forceful SystemPower transition
152  * @sc: A reference to the configuration data
153  *
154  * Initiates the required SystemPower transition without involving userspace:
155  * just trigger the action at the kernel level after issuing an emergency
156  * sync. (if possible at all)
157  */
158 static inline void
scmi_request_forceful_transition(struct scmi_syspower_conf * sc)159 scmi_request_forceful_transition(struct scmi_syspower_conf *sc)
160 {
161 	dev_dbg(sc->dev, "Serving forceful request:%d\n",
162 		sc->required_transition);
163 
164 #ifndef MODULE
165 	emergency_sync();
166 #endif
167 	switch (sc->required_transition) {
168 	case SCMI_SYSTEM_SHUTDOWN:
169 		kernel_power_off();
170 		break;
171 	case SCMI_SYSTEM_COLDRESET:
172 	case SCMI_SYSTEM_WARMRESET:
173 		kernel_restart(NULL);
174 		break;
175 	default:
176 		break;
177 	}
178 }
179 
scmi_forceful_work_func(struct work_struct * work)180 static void scmi_forceful_work_func(struct work_struct *work)
181 {
182 	struct scmi_syspower_conf *sc;
183 	struct delayed_work *dwork;
184 
185 	if (system_state > SYSTEM_RUNNING)
186 		return;
187 
188 	dwork = to_delayed_work(work);
189 	sc = dwork_to_sconf(dwork);
190 
191 	dev_dbg(sc->dev, "Graceful request timed out...forcing !\n");
192 	mutex_lock(&sc->state_mtx);
193 	/* avoid deadlock by unregistering reboot notifier first */
194 	unregister_reboot_notifier(&sc->reboot_nb);
195 	if (sc->state == SCMI_SYSPOWER_IN_PROGRESS)
196 		scmi_request_forceful_transition(sc);
197 	mutex_unlock(&sc->state_mtx);
198 }
199 
200 /**
201  * scmi_request_graceful_transition  - Request graceful SystemPower transition
202  * @sc: A reference to the configuration data
203  * @timeout_ms: The desired timeout to wait for the shutdown to complete before
204  *		system is forcibly shutdown.
205  *
206  * Initiates the required SystemPower transition, requesting userspace
207  * co-operation: it uses the same orderly_ methods used by ACPI Shutdown event
208  * processing.
209  *
210  * Takes care also to register a reboot notifier and to schedule a delayed work
211  * in order to detect if userspace actions are taking too long and in such a
212  * case to trigger a forceful transition.
213  */
scmi_request_graceful_transition(struct scmi_syspower_conf * sc,unsigned int timeout_ms)214 static void scmi_request_graceful_transition(struct scmi_syspower_conf *sc,
215 					     unsigned int timeout_ms)
216 {
217 	unsigned int adj_timeout_ms = 0;
218 
219 	if (timeout_ms) {
220 		int ret;
221 
222 		sc->reboot_nb.notifier_call = &scmi_reboot_notifier;
223 		ret = register_reboot_notifier(&sc->reboot_nb);
224 		if (!ret) {
225 			/* Wait only up to 75% of the advertised timeout */
226 			adj_timeout_ms = mult_frac(timeout_ms, 3, 4);
227 			INIT_DELAYED_WORK(&sc->forceful_work,
228 					  scmi_forceful_work_func);
229 			schedule_delayed_work(&sc->forceful_work,
230 					      msecs_to_jiffies(adj_timeout_ms));
231 		} else {
232 			/* Carry on best effort even without a reboot notifier */
233 			dev_warn(sc->dev,
234 				 "Cannot register reboot notifier !\n");
235 		}
236 	}
237 
238 	dev_dbg(sc->dev,
239 		"Serving graceful req:%d (timeout_ms:%u  adj_timeout_ms:%u)\n",
240 		sc->required_transition, timeout_ms, adj_timeout_ms);
241 
242 	switch (sc->required_transition) {
243 	case SCMI_SYSTEM_SHUTDOWN:
244 		/*
245 		 * When triggered early at boot-time the 'orderly' call will
246 		 * partially fail due to the lack of userspace itself, but
247 		 * the force=true argument will start anyway a successful
248 		 * forced shutdown.
249 		 */
250 		orderly_poweroff(true);
251 		break;
252 	case SCMI_SYSTEM_COLDRESET:
253 	case SCMI_SYSTEM_WARMRESET:
254 		orderly_reboot();
255 		break;
256 	case SCMI_SYSTEM_SUSPEND:
257 		schedule_work(&sc->suspend_work);
258 		break;
259 	default:
260 		break;
261 	}
262 }
263 
264 /**
265  * scmi_userspace_notifier  - Notifier callback to act on SystemPower
266  * Notifications
267  * @nb: Reference to the related notifier block
268  * @event: The SystemPower notification event id
269  * @data: The SystemPower event report
270  *
271  * This callback is in charge of decoding the received SystemPower report
272  * and act accordingly triggering a graceful or forceful system transition.
273  *
274  * Note that once a valid SCMI SystemPower event starts being served, any
275  * other following SystemPower notification received from the same SCMI
276  * instance (handle) will be ignored.
277  *
278  * Return: NOTIFY_OK once a valid SystemPower event has been successfully
279  * processed.
280  */
scmi_userspace_notifier(struct notifier_block * nb,unsigned long event,void * data)281 static int scmi_userspace_notifier(struct notifier_block *nb,
282 				   unsigned long event, void *data)
283 {
284 	struct scmi_system_power_state_notifier_report *er = data;
285 	struct scmi_syspower_conf *sc = userspace_nb_to_sconf(nb);
286 
287 	if (er->system_state >= SCMI_SYSTEM_MAX ||
288 	    er->system_state == SCMI_SYSTEM_POWERUP) {
289 		dev_err(sc->dev, "Ignoring unsupported system_state: 0x%X\n",
290 			er->system_state);
291 		return NOTIFY_DONE;
292 	}
293 
294 	if (!SCMI_SYSPOWER_IS_REQUEST_GRACEFUL(er->flags)) {
295 		dev_err(sc->dev, "Ignoring forceful notification.\n");
296 		return NOTIFY_DONE;
297 	}
298 
299 	/*
300 	 * Bail out if system is already shutting down or an SCMI SystemPower
301 	 * requested is already being served.
302 	 */
303 	if (system_state > SYSTEM_RUNNING)
304 		return NOTIFY_DONE;
305 	mutex_lock(&sc->state_mtx);
306 	if (sc->state != SCMI_SYSPOWER_IDLE) {
307 		dev_dbg(sc->dev,
308 			"Transition already in progress...ignore.\n");
309 		mutex_unlock(&sc->state_mtx);
310 		return NOTIFY_DONE;
311 	}
312 	sc->state = SCMI_SYSPOWER_IN_PROGRESS;
313 	mutex_unlock(&sc->state_mtx);
314 
315 	sc->required_transition = er->system_state;
316 
317 	/* Leaving a trace in logs of who triggered the shutdown/reboot. */
318 	dev_info(sc->dev, "Serving shutdown/reboot request: %d\n",
319 		 sc->required_transition);
320 
321 	scmi_request_graceful_transition(sc, er->timeout);
322 
323 	return NOTIFY_OK;
324 }
325 
scmi_suspend_work_func(struct work_struct * work)326 static void scmi_suspend_work_func(struct work_struct *work)
327 {
328 	pm_suspend(PM_SUSPEND_MEM);
329 }
330 
scmi_syspower_probe(struct scmi_device * sdev)331 static int scmi_syspower_probe(struct scmi_device *sdev)
332 {
333 	int ret;
334 	struct scmi_syspower_conf *sc;
335 	struct scmi_handle *handle = sdev->handle;
336 
337 	if (!handle)
338 		return -ENODEV;
339 
340 	ret = handle->devm_protocol_acquire(sdev, SCMI_PROTOCOL_SYSTEM);
341 	if (ret)
342 		return ret;
343 
344 	sc = devm_kzalloc(&sdev->dev, sizeof(*sc), GFP_KERNEL);
345 	if (!sc)
346 		return -ENOMEM;
347 
348 	sc->state = SCMI_SYSPOWER_IDLE;
349 	mutex_init(&sc->state_mtx);
350 	sc->required_transition = SCMI_SYSTEM_MAX;
351 	sc->userspace_nb.notifier_call = &scmi_userspace_notifier;
352 	sc->dev = &sdev->dev;
353 	dev_set_drvdata(&sdev->dev, sc);
354 
355 	INIT_WORK(&sc->suspend_work, scmi_suspend_work_func);
356 
357 	return handle->notify_ops->devm_event_notifier_register(sdev,
358 							   SCMI_PROTOCOL_SYSTEM,
359 					 SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
360 						       NULL, &sc->userspace_nb);
361 }
362 
scmi_system_power_resume(struct device * dev)363 static int scmi_system_power_resume(struct device *dev)
364 {
365 	struct scmi_syspower_conf *sc = dev_get_drvdata(dev);
366 
367 	sc->state = SCMI_SYSPOWER_IDLE;
368 	return 0;
369 }
370 
371 static const struct dev_pm_ops scmi_system_power_pmops = {
372 	SYSTEM_SLEEP_PM_OPS(NULL, scmi_system_power_resume)
373 };
374 
375 static const struct scmi_device_id scmi_id_table[] = {
376 	{ SCMI_PROTOCOL_SYSTEM, "syspower" },
377 	{ },
378 };
379 MODULE_DEVICE_TABLE(scmi, scmi_id_table);
380 
381 static struct scmi_driver scmi_system_power_driver = {
382 	.driver	= {
383 		.pm = pm_sleep_ptr(&scmi_system_power_pmops),
384 	},
385 	.name = "scmi-system-power",
386 	.probe = scmi_syspower_probe,
387 	.id_table = scmi_id_table,
388 };
389 module_scmi_driver(scmi_system_power_driver);
390 
391 MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
392 MODULE_DESCRIPTION("ARM SCMI SystemPower Control driver");
393 MODULE_LICENSE("GPL");
394