Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * SCMI Generic SystemPower Control driver.
0004  *
0005  * Copyright (C) 2020-2022 ARM Ltd.
0006  */
0007 /*
0008  * In order to handle platform originated SCMI SystemPower requests (like
0009  * shutdowns or cold/warm resets) we register an SCMI Notification notifier
0010  * block to react when such SCMI SystemPower events are emitted by platform.
0011  *
0012  * Once such a notification is received we act accordingly to perform the
0013  * required system transition depending on the kind of request.
0014  *
0015  * Graceful requests are routed to userspace through the same API methods
0016  * (orderly_poweroff/reboot()) used by ACPI when handling ACPI Shutdown bus
0017  * events.
0018  *
0019  * Direct forceful requests are not supported since are not meant to be sent
0020  * by the SCMI platform to an OSPM like Linux.
0021  *
0022  * Additionally, graceful request notifications can carry an optional timeout
0023  * field stating the maximum amount of time allowed by the platform for
0024  * completion after which they are converted to forceful ones: the assumption
0025  * here is that even graceful requests can be upper-bound by a maximum final
0026  * timeout strictly enforced by the platform itself which can ultimately cut
0027  * the power off at will anytime; in order to avoid such extreme scenario, we
0028  * track progress of graceful requests through the means of a reboot notifier
0029  * converting timed-out graceful requests to forceful ones, so at least we
0030  * try to perform a clean sync and shutdown/restart before the power is cut.
0031  *
0032  * Given the peculiar nature of SCMI SystemPower protocol, that is being in
0033  * charge of triggering system wide shutdown/reboot events, there should be
0034  * only one SCMI platform actively emitting SystemPower events.
0035  * For this reason the SCMI core takes care to enforce the creation of one
0036  * single unique device associated to the SCMI System Power protocol; no matter
0037  * how many SCMI platforms are defined on the system, only one can be designated
0038  * to support System Power: as a consequence this driver will never be probed
0039  * more than once.
0040  *
0041  * For similar reasons as soon as the first valid SystemPower is received by
0042  * this driver and the shutdown/reboot is started, any further notification
0043  * possibly emitted by the platform will be ignored.
0044  */
0045 
0046 #include <linux/math.h>
0047 #include <linux/module.h>
0048 #include <linux/mutex.h>
0049 #include <linux/printk.h>
0050 #include <linux/reboot.h>
0051 #include <linux/scmi_protocol.h>
0052 #include <linux/slab.h>
0053 #include <linux/time64.h>
0054 #include <linux/timer.h>
0055 #include <linux/types.h>
0056 #include <linux/workqueue.h>
0057 
0058 #ifndef MODULE
0059 #include <linux/fs.h>
0060 #endif
0061 
0062 enum scmi_syspower_state {
0063     SCMI_SYSPOWER_IDLE,
0064     SCMI_SYSPOWER_IN_PROGRESS,
0065     SCMI_SYSPOWER_REBOOTING
0066 };
0067 
0068 /**
0069  * struct scmi_syspower_conf  -  Common configuration
0070  *
0071  * @dev: A reference device
0072  * @state: Current SystemPower state
0073  * @state_mtx: @state related mutex
0074  * @required_transition: The requested transition as decribed in the received
0075  *           SCMI SystemPower notification
0076  * @userspace_nb: The notifier_block registered against the SCMI SystemPower
0077  *        notification to start the needed userspace interactions.
0078  * @reboot_nb: A notifier_block optionally used to track reboot progress
0079  * @forceful_work: A worker used to trigger a forceful transition once a
0080  *         graceful has timed out.
0081  */
0082 struct scmi_syspower_conf {
0083     struct device *dev;
0084     enum scmi_syspower_state state;
0085     /* Protect access to state */
0086     struct mutex state_mtx;
0087     enum scmi_system_events required_transition;
0088 
0089     struct notifier_block userspace_nb;
0090     struct notifier_block reboot_nb;
0091 
0092     struct delayed_work forceful_work;
0093 };
0094 
0095 #define userspace_nb_to_sconf(x)    \
0096     container_of(x, struct scmi_syspower_conf, userspace_nb)
0097 
0098 #define reboot_nb_to_sconf(x)       \
0099     container_of(x, struct scmi_syspower_conf, reboot_nb)
0100 
0101 #define dwork_to_sconf(x)       \
0102     container_of(x, struct scmi_syspower_conf, forceful_work)
0103 
0104 /**
0105  * scmi_reboot_notifier  - A reboot notifier to catch an ongoing successful
0106  * system transition
0107  * @nb: Reference to the related notifier block
0108  * @reason: The reason for the ongoing reboot
0109  * @__unused: The cmd being executed on a restart request (unused)
0110  *
0111  * When an ongoing system transition is detected, compatible with the one
0112  * requested by SCMI, cancel the delayed work.
0113  *
0114  * Return: NOTIFY_OK in any case
0115  */
0116 static int scmi_reboot_notifier(struct notifier_block *nb,
0117                 unsigned long reason, void *__unused)
0118 {
0119     struct scmi_syspower_conf *sc = reboot_nb_to_sconf(nb);
0120 
0121     mutex_lock(&sc->state_mtx);
0122     switch (reason) {
0123     case SYS_HALT:
0124     case SYS_POWER_OFF:
0125         if (sc->required_transition == SCMI_SYSTEM_SHUTDOWN)
0126             sc->state = SCMI_SYSPOWER_REBOOTING;
0127         break;
0128     case SYS_RESTART:
0129         if (sc->required_transition == SCMI_SYSTEM_COLDRESET ||
0130             sc->required_transition == SCMI_SYSTEM_WARMRESET)
0131             sc->state = SCMI_SYSPOWER_REBOOTING;
0132         break;
0133     default:
0134         break;
0135     }
0136 
0137     if (sc->state == SCMI_SYSPOWER_REBOOTING) {
0138         dev_dbg(sc->dev, "Reboot in progress...cancel delayed work.\n");
0139         cancel_delayed_work_sync(&sc->forceful_work);
0140     }
0141     mutex_unlock(&sc->state_mtx);
0142 
0143     return NOTIFY_OK;
0144 }
0145 
0146 /**
0147  * scmi_request_forceful_transition  - Request forceful SystemPower transition
0148  * @sc: A reference to the configuration data
0149  *
0150  * Initiates the required SystemPower transition without involving userspace:
0151  * just trigger the action at the kernel level after issuing an emergency
0152  * sync. (if possible at all)
0153  */
0154 static inline void
0155 scmi_request_forceful_transition(struct scmi_syspower_conf *sc)
0156 {
0157     dev_dbg(sc->dev, "Serving forceful request:%d\n",
0158         sc->required_transition);
0159 
0160 #ifndef MODULE
0161     emergency_sync();
0162 #endif
0163     switch (sc->required_transition) {
0164     case SCMI_SYSTEM_SHUTDOWN:
0165         kernel_power_off();
0166         break;
0167     case SCMI_SYSTEM_COLDRESET:
0168     case SCMI_SYSTEM_WARMRESET:
0169         kernel_restart(NULL);
0170         break;
0171     default:
0172         break;
0173     }
0174 }
0175 
0176 static void scmi_forceful_work_func(struct work_struct *work)
0177 {
0178     struct scmi_syspower_conf *sc;
0179     struct delayed_work *dwork;
0180 
0181     if (system_state > SYSTEM_RUNNING)
0182         return;
0183 
0184     dwork = to_delayed_work(work);
0185     sc = dwork_to_sconf(dwork);
0186 
0187     dev_dbg(sc->dev, "Graceful request timed out...forcing !\n");
0188     mutex_lock(&sc->state_mtx);
0189     /* avoid deadlock by unregistering reboot notifier first */
0190     unregister_reboot_notifier(&sc->reboot_nb);
0191     if (sc->state == SCMI_SYSPOWER_IN_PROGRESS)
0192         scmi_request_forceful_transition(sc);
0193     mutex_unlock(&sc->state_mtx);
0194 }
0195 
0196 /**
0197  * scmi_request_graceful_transition  - Request graceful SystemPower transition
0198  * @sc: A reference to the configuration data
0199  * @timeout_ms: The desired timeout to wait for the shutdown to complete before
0200  *      system is forcibly shutdown.
0201  *
0202  * Initiates the required SystemPower transition, requesting userspace
0203  * co-operation: it uses the same orderly_ methods used by ACPI Shutdown event
0204  * processing.
0205  *
0206  * Takes care also to register a reboot notifier and to schedule a delayed work
0207  * in order to detect if userspace actions are taking too long and in such a
0208  * case to trigger a forceful transition.
0209  */
0210 static void scmi_request_graceful_transition(struct scmi_syspower_conf *sc,
0211                          unsigned int timeout_ms)
0212 {
0213     unsigned int adj_timeout_ms = 0;
0214 
0215     if (timeout_ms) {
0216         int ret;
0217 
0218         sc->reboot_nb.notifier_call = &scmi_reboot_notifier;
0219         ret = register_reboot_notifier(&sc->reboot_nb);
0220         if (!ret) {
0221             /* Wait only up to 75% of the advertised timeout */
0222             adj_timeout_ms = mult_frac(timeout_ms, 3, 4);
0223             INIT_DELAYED_WORK(&sc->forceful_work,
0224                       scmi_forceful_work_func);
0225             schedule_delayed_work(&sc->forceful_work,
0226                           msecs_to_jiffies(adj_timeout_ms));
0227         } else {
0228             /* Carry on best effort even without a reboot notifier */
0229             dev_warn(sc->dev,
0230                  "Cannot register reboot notifier !\n");
0231         }
0232     }
0233 
0234     dev_dbg(sc->dev,
0235         "Serving graceful req:%d (timeout_ms:%u  adj_timeout_ms:%u)\n",
0236         sc->required_transition, timeout_ms, adj_timeout_ms);
0237 
0238     switch (sc->required_transition) {
0239     case SCMI_SYSTEM_SHUTDOWN:
0240         /*
0241          * When triggered early at boot-time the 'orderly' call will
0242          * partially fail due to the lack of userspace itself, but
0243          * the force=true argument will start anyway a successful
0244          * forced shutdown.
0245          */
0246         orderly_poweroff(true);
0247         break;
0248     case SCMI_SYSTEM_COLDRESET:
0249     case SCMI_SYSTEM_WARMRESET:
0250         orderly_reboot();
0251         break;
0252     default:
0253         break;
0254     }
0255 }
0256 
0257 /**
0258  * scmi_userspace_notifier  - Notifier callback to act on SystemPower
0259  * Notifications
0260  * @nb: Reference to the related notifier block
0261  * @event: The SystemPower notification event id
0262  * @data: The SystemPower event report
0263  *
0264  * This callback is in charge of decoding the received SystemPower report
0265  * and act accordingly triggering a graceful or forceful system transition.
0266  *
0267  * Note that once a valid SCMI SystemPower event starts being served, any
0268  * other following SystemPower notification received from the same SCMI
0269  * instance (handle) will be ignored.
0270  *
0271  * Return: NOTIFY_OK once a valid SystemPower event has been successfully
0272  * processed.
0273  */
0274 static int scmi_userspace_notifier(struct notifier_block *nb,
0275                    unsigned long event, void *data)
0276 {
0277     struct scmi_system_power_state_notifier_report *er = data;
0278     struct scmi_syspower_conf *sc = userspace_nb_to_sconf(nb);
0279 
0280     if (er->system_state >= SCMI_SYSTEM_POWERUP) {
0281         dev_err(sc->dev, "Ignoring unsupported system_state: 0x%X\n",
0282             er->system_state);
0283         return NOTIFY_DONE;
0284     }
0285 
0286     if (!SCMI_SYSPOWER_IS_REQUEST_GRACEFUL(er->flags)) {
0287         dev_err(sc->dev, "Ignoring forceful notification.\n");
0288         return NOTIFY_DONE;
0289     }
0290 
0291     /*
0292      * Bail out if system is already shutting down or an SCMI SystemPower
0293      * requested is already being served.
0294      */
0295     if (system_state > SYSTEM_RUNNING)
0296         return NOTIFY_DONE;
0297     mutex_lock(&sc->state_mtx);
0298     if (sc->state != SCMI_SYSPOWER_IDLE) {
0299         dev_dbg(sc->dev,
0300             "Transition already in progress...ignore.\n");
0301         mutex_unlock(&sc->state_mtx);
0302         return NOTIFY_DONE;
0303     }
0304     sc->state = SCMI_SYSPOWER_IN_PROGRESS;
0305     mutex_unlock(&sc->state_mtx);
0306 
0307     sc->required_transition = er->system_state;
0308 
0309     /* Leaving a trace in logs of who triggered the shutdown/reboot. */
0310     dev_info(sc->dev, "Serving shutdown/reboot request: %d\n",
0311          sc->required_transition);
0312 
0313     scmi_request_graceful_transition(sc, er->timeout);
0314 
0315     return NOTIFY_OK;
0316 }
0317 
0318 static int scmi_syspower_probe(struct scmi_device *sdev)
0319 {
0320     int ret;
0321     struct scmi_syspower_conf *sc;
0322     struct scmi_handle *handle = sdev->handle;
0323 
0324     if (!handle)
0325         return -ENODEV;
0326 
0327     ret = handle->devm_protocol_acquire(sdev, SCMI_PROTOCOL_SYSTEM);
0328     if (ret)
0329         return ret;
0330 
0331     sc = devm_kzalloc(&sdev->dev, sizeof(*sc), GFP_KERNEL);
0332     if (!sc)
0333         return -ENOMEM;
0334 
0335     sc->state = SCMI_SYSPOWER_IDLE;
0336     mutex_init(&sc->state_mtx);
0337     sc->required_transition = SCMI_SYSTEM_MAX;
0338     sc->userspace_nb.notifier_call = &scmi_userspace_notifier;
0339     sc->dev = &sdev->dev;
0340 
0341     return handle->notify_ops->devm_event_notifier_register(sdev,
0342                                SCMI_PROTOCOL_SYSTEM,
0343                      SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
0344                                NULL, &sc->userspace_nb);
0345 }
0346 
0347 static const struct scmi_device_id scmi_id_table[] = {
0348     { SCMI_PROTOCOL_SYSTEM, "syspower" },
0349     { },
0350 };
0351 MODULE_DEVICE_TABLE(scmi, scmi_id_table);
0352 
0353 static struct scmi_driver scmi_system_power_driver = {
0354     .name = "scmi-system-power",
0355     .probe = scmi_syspower_probe,
0356     .id_table = scmi_id_table,
0357 };
0358 module_scmi_driver(scmi_system_power_driver);
0359 
0360 MODULE_AUTHOR("Cristian Marussi <cristian.marussi@arm.com>");
0361 MODULE_DESCRIPTION("ARM SCMI SystemPower Control driver");
0362 MODULE_LICENSE("GPL");