[PATCH v4 2/2] firmware: arm_scmi: Add SCMI System Power Control driver
Greg KH
gregkh at linuxfoundation.org
Thu Jan 7 11:04:11 EST 2021
On Tue, Jan 05, 2021 at 01:09:45PM +0000, Cristian Marussi wrote:
> diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
> index 3f14dffb9669..2b39453e9dd1 100644
> --- a/drivers/firmware/Kconfig
> +++ b/drivers/firmware/Kconfig
> @@ -40,6 +40,18 @@ config ARM_SCMI_POWER_DOMAIN
> will be called scmi_pm_domain. Note this may needed early in boot
> before rootfs may be available.
>
> +config ARM_SCMI_POWER_CONTROL
> + bool "SCMI system power control driver"
> + depends on ARM_SCMI_PROTOCOL || (COMPILE_TEST && OF)
> + default n
n is always the default, no need to list it here.
> + help
> + This enables System Power control logic which binds system shutdown or
> + reboot actions to SCMI System Power notifications generated by SCP
> + firmware.
> +
> + Graceful requests' methods and timeout and can be configured using
> + a few available module parameters.
> +
> config ARM_SCPI_PROTOCOL
> tristate "ARM System Control and Power Interface (SCPI) Message Protocol"
> depends on ARM || ARM64 || COMPILE_TEST
> diff --git a/drivers/firmware/arm_scmi/Makefile b/drivers/firmware/arm_scmi/Makefile
> index 6a2ef63306d6..ddddb4636ebd 100644
> --- a/drivers/firmware/arm_scmi/Makefile
> +++ b/drivers/firmware/arm_scmi/Makefile
> @@ -9,3 +9,4 @@ scmi-module-objs := $(scmi-bus-y) $(scmi-driver-y) $(scmi-protocols-y) \
> $(scmi-transport-y)
> obj-$(CONFIG_ARM_SCMI_PROTOCOL) += scmi-module.o
> obj-$(CONFIG_ARM_SCMI_POWER_DOMAIN) += scmi_pm_domain.o
> +obj-$(CONFIG_ARM_SCMI_POWER_CONTROL) += scmi_power_control.o
> diff --git a/drivers/firmware/arm_scmi/scmi_power_control.c b/drivers/firmware/arm_scmi/scmi_power_control.c
> new file mode 100644
> index 000000000000..216c2f031111
> --- /dev/null
> +++ b/drivers/firmware/arm_scmi/scmi_power_control.c
> @@ -0,0 +1,359 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * SCMI Generic SystemPower Control driver.
> + *
> + * Copyright (C) 2020-2021 ARM Ltd.
> + */
> +/**
> + * DOC: Theory of operation
What does "DOC:" provide? Did you tie this into the kernel doc build
system? If not, then what is this tag for?
> + *
> + * In order to handle platform originated SCMI SystemPower requests (like
> + * shutdowns or cold/warm resets) we register an SCMI Notification notifier
> + * block to react when such SCMI SystemPower events are emitted.
> + *
> + * Once such a notification is received we act accordingly to perform the
> + * required system transition depending on the kind of request.
> + *
> + * By default graceful requests are routed to userspace through the same
> + * API methods (orderly_poweroff/reboot()) used by ACPI when handling ACPI
> + * Shutdown bus events: alternatively, by properly setting a couple of module
> + * parameters (scmi_graceful_*_signum), it is possible to choose to use signals
> + * to CAD pid as a mean to communicate such graceful requests to userspace.
> + *
> + * Forceful requests, instead, will simply cause an immediate emergency_sync()
> + * and subsequent Kernel-only shutdown/reboot.
> + *
> + * Additionally, setting scmi_graceful_request_tmo_ms to a non-zero value, it
> + * is possible to convert a timed-out graceful request to a forceful one.
> + *
> + * The assumption here is that even graceful requests can be upper-bound by a
> + * maximum final timeout strictly enforced by the platform itself which can
> + * ultimately cut the power off at will anytime: in order to avoid such extreme
> + * scenario, when scmi_graceful_request_tmo_ms is non-zero, we track progress of
> + * graceful requests through the means of a reboot notifier converting timed-out
> + * graceful requests to forceful ones: in such a way at least we perform a
> + * clean sync and shutdown/restart before the power is cut.
> + *
> + * Given that such platform hard-timeout, if present, is anyway highly platform
> + * and event specific and not exposed at run-time by the SCMI SytemPower
> + * protocol, the parameter scmi_graceful_request_tmo_ms defaults to zero.
> + */
> +
> +#define pr_fmt(fmt) "SCMI SystemPower - " fmt
drivers should not need this, and even if they do, that's a HUGE prefix,
be more terse please. Always use dev_*() calls instead of pr_() as you
have access to a device at all times, right? If not, then this isn't a
driver :)
> +enum scmi_syspower_state {
> + SCMI_SYSPOWER_IDLE,
> + SCMI_SYSPOWER_IN_PROGRESS,
> + SCMI_SYSPOWER_REBOOTING
> +};
> +
> +static enum scmi_syspower_state scmi_state;
> +/* Protect access to scmi_state */
> +static DEFINE_MUTEX(scmi_state_mtx);
Why does this only handle "one" state and device? Shouldn't this all be
per-device?
> +
> +static enum scmi_system_events scmi_required_transition = SCMI_SYSTEM_MAX;
> +
> +static unsigned int scmi_graceful_poweroff_signum;
> +static unsigned int scmi_graceful_reboot_signum;
> +static unsigned int scmi_graceful_request_tmo_ms;
why "unsigned int"?
> +
> +static struct timer_list scmi_request_timer;
> +static struct work_struct scmi_forceful_work;
> +
> +/**
> + * scmi_reboot_notifier - A reboot notifier to catch an ongoing successful
> + * system transition
> + * @nb: Reference to the related notifier block
> + * @reason: The reason for the ongoing reboot
> + * @__unused: The cmd being executed on a restart request (unused)
> + *
> + * When an ongoing system transition is detected, compatible with the one
> + * requested by SCMI, cancel the timer work.
> + * This is registered only when a valid SCMI SystemPower transition has been
> + * received and scmi_graceful_request_tmo_ms was non-zero.
> + *
> + * Return: NOTIFY_OK in any case
> + */
> +static int scmi_reboot_notifier(struct notifier_block *nb,
> + unsigned long reason, void *__unused)
> +{
> + mutex_lock(&scmi_state_mtx);
> + switch (reason) {
> + case SYS_HALT:
> + case SYS_POWER_OFF:
> + if (scmi_required_transition == SCMI_SYSTEM_SHUTDOWN)
> + scmi_state = SCMI_SYSPOWER_REBOOTING;
> + break;
> + case SYS_RESTART:
> + if (scmi_required_transition == SCMI_SYSTEM_COLDRESET ||
> + scmi_required_transition == SCMI_SYSTEM_WARMRESET)
> + scmi_state = SCMI_SYSPOWER_REBOOTING;
> + break;
> + default:
> + break;
> + }
> +
> + if (scmi_state == SCMI_SYSPOWER_REBOOTING) {
> + pr_debug("Reboot in progress...cancel timer.\n");
use dev_dbg() and pass it your device pointer please.
Same for all usages of pr_*() in the driver.
> +static int scmi_syspower_probe(struct scmi_device *sdev)
> +{
> + int ret;
> + struct scmi_handle *handle = sdev->handle;
> + struct notifier_block *userspace_nb;
> +
> + if (!handle)
> + return -ENODEV;
> +
> + userspace_nb = devm_kzalloc(&sdev->dev, sizeof(*userspace_nb),
> + GFP_KERNEL);
> + if (!userspace_nb)
> + return -ENOMEM;
> +
> + userspace_nb->notifier_call = &scmi_userspace_notifier;
> + ret = handle->notify_ops->register_event_notifier(handle,
> + SCMI_PROTOCOL_SYSTEM,
> + SCMI_EVENT_SYSTEM_POWER_STATE_NOTIFIER,
> + NULL, userspace_nb);
Crazy indentation, checkpatch was ok with this?
> +module_param(scmi_graceful_request_tmo_ms, uint, 0644);
> +MODULE_PARM_DESC(scmi_graceful_request_tmo_ms,
> + "Maximum time(ms) allowed to userspace for complying to the request. Unlimited if zero.");
> +
> +module_param(scmi_graceful_poweroff_signum, uint, 0644);
> +MODULE_PARM_DESC(scmi_graceful_poweroff_signum,
> + "Signal to request graceful poweroff to CAD process. Ignored if zero.");
> +
> +module_param(scmi_graceful_reboot_signum, uint, 0644);
> +MODULE_PARM_DESC(scmi_graceful_reboot_signum,
> + "Signal to request graceful reboot to CAD process. Ignored if zero.");
This is not the 1990's, please do not add module parameters. Make this
"just work", or worst case, sysfs attributes.
thanks,
greg k-h
More information about the linux-arm-kernel
mailing list